From d687e95a8f30d7e0b88d01ca86fc9bb9bffe9573 Mon Sep 17 00:00:00 2001 From: helmutm Date: Wed, 15 Mar 2006 17:54:31 +0000 Subject: [PATCH] referencing targets from views/nodes on the UI: now fine with intIds; now also with correct access to images and files in the resource space; main control via browser views that now provide a macro for viewing git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@1126 fd906abe-77d9-0310-91a1-e0d9ade77398 --- README.txt | 25 ++++++------ browser/common.py | 5 +++ browser/configure.zcml | 33 ++++++++++------ browser/node.pt | 13 ++----- browser/node.py | 80 ++++++++++++++++++++++---------------- browser/node_macros.pt | 9 ++--- browser/resource.py | 13 +++++++ browser/resource_macros.pt | 27 +++++++++++++ interfaces.py | 47 +--------------------- target.py | 37 +++++++++--------- view.py | 8 ++-- 11 files changed, 159 insertions(+), 138 deletions(-) create mode 100644 browser/resource_macros.pt diff --git a/README.txt b/README.txt index 4c1f43c..df19c21 100755 --- a/README.txt +++ b/README.txt @@ -443,13 +443,13 @@ Node Views >>> view = NodeView(m11, TestRequest()) >>> page = view.page - >>> items = page.textItems() + >>> items = page.textItems >>> for item in items: ... print item.url, item.editable http://127.0.0.1/loops/views/m1/m11/m112 False >>> menu = view.menu - >>> items = menu.menuItems() + >>> items = menu.menuItems >>> for item in items: ... print item.url, view.selected(item) http://127.0.0.1/loops/views/m1/m11 True @@ -458,9 +458,17 @@ A Node and its Target --------------------- When configuring a node you may specify what you want to do with respect -to the node's target: associate an existing one or create a new one. +to the node's target: associate an existing one or create a new one. When +accessing a target via a node view it is usually wrapped in a corresponding +view; these views we have to provide as multi-adapters: >>> from loops.browser.node import ConfigureView + >>> from loops.browser.resource import DocumentView, MediaAssetView + >>> ztapi.provideAdapter(IDocument, Interface, DocumentView, + ... with=(IBrowserRequest,)) + >>> ztapi.provideAdapter(IMediaAsset, Interface, MediaAssetView, + ... with=(IBrowserRequest,)) + >>> form = {'action': 'create', 'create.title': 'New Resource', ... 'create.type': 'loops.resource.MediaAsset',} >>> view = ConfigureView(m111, TestRequest(form = form)) @@ -498,11 +506,6 @@ A node's target is rendered using the NodeView's renderTargetBody() method. This makes use of a browser view registered for the target interface, and of a lot of other stuff needed for the rendering machine. - >>> from zope.app.publisher.interfaces.browser import IBrowserView - >>> from loops.browser.resource import DocumentView - >>> ztapi.provideAdapter(IDocument, Interface, DocumentView, - ... with=(IBrowserRequest,)) - >>> from zope.component.interfaces import IFactory >>> from zope.app.renderer import rest >>> ztapi.provideUtility(IFactory, rest.ReStructuredTextSourceFactory, @@ -513,13 +516,13 @@ and of a lot of other stuff needed for the rendering machine. >>> m112.target = doc1 >>> view = NodeView(m112, TestRequest()) - >>> view.renderTargetBody() + >>> view.renderTarget() u'' >>> doc1.data = u'Test data\n\nAnother paragraph' - >>> view.renderTargetBody() + >>> view.renderTarget() u'Test data\n\nAnother paragraph' >>> doc1.contentType = 'text/restructured' - >>> view.renderTargetBody() + >>> view.renderTarget() u'

Test data

\n

Another paragraph

\n' It is possible to edit a target's attributes directly in an diff --git a/browser/common.py b/browser/common.py index 9572993..2ef9181 100644 --- a/browser/common.py +++ b/browser/common.py @@ -25,6 +25,7 @@ $Id$ from zope.app import zapi from zope.app.dublincore.interfaces import ICMFDublinCore from zope.app.form.browser.interfaces import ITerms +from zope.app.intid.interfaces import IIntIds from zope.cachedescriptors.property import Lazy from zope.interface import implements from zope.security.proxy import removeSecurityProxy @@ -86,6 +87,10 @@ class BaseView(object): for o in objs: yield BaseView(o, request) + @Lazy + def uniqueId(self): + return zapi.getUtility(IIntIds).getId(self.context) + class LoopsTerms(object): """ Provide the ITerms interface, e.g. for usage in selection diff --git a/browser/configure.zcml b/browser/configure.zcml index e4b86e7..764572a 100644 --- a/browser/configure.zcml +++ b/browser/configure.zcml @@ -158,6 +158,14 @@ name="concept.html" /> + + - - + + + + diff --git a/browser/node.pt b/browser/node.pt index 3f5e71d..519b5f5 100644 --- a/browser/node.pt +++ b/browser/node.pt @@ -23,17 +23,12 @@ - - - + macro item/macro"> + + - -
- Here comes the real target... -
-
diff --git a/browser/node.py b/browser/node.py index a895efe..43711b1 100644 --- a/browser/node.py +++ b/browser/node.py @@ -27,6 +27,8 @@ from zope.app import zapi from zope.app.catalog.interfaces import ICatalog from zope.app.container.browser.contents import JustContents from zope.app.event.objectevent import ObjectCreatedEvent +from zope.app.pagetemplate import ViewPageTemplateFile +from zope.app.intid.interfaces import IIntIds from zope.dottedname.resolve import resolve from zope.event import notify from zope.proxy import removeAllProxies @@ -43,6 +45,28 @@ from loops.browser.concept import ConceptView class NodeView(BaseView): + template = ViewPageTemplateFile('node_macros.pt') + macro = template.macros['content'] + + @Lazy + def item(self): + target = self.request.annotations.get('loops.view', {}).get('target') + if target is not None: + # .target.... traversal magic + return zapi.getMultiAdapter((target, self.request)) + return self.page + + @Lazy + def page(self): + page = self.context.getPage() + return page is not None and NodeView(page, self.request) or None + + @Lazy + def textItems(self): + return [NodeView(child, self.request) + for child in self.context.getTextItems()] + + @Lazy def nodeType(self): return self.context.nodeType @@ -66,33 +90,11 @@ class NodeView(BaseView): def target(self): obj = self.targetObject if obj is not None: - if IConcept.providedBy(obj): - return ConceptView(obj, self.request) - return BaseView(obj, self.request) + return zapi.getMultiAdapter((obj, self.request)) def renderTarget(self): - target = self.targetObject - if target is not None: - targetView = zapi.getMultiAdapter((target, self.request), - name=zapi.getDefaultViewName(target, self.request)) - return targetView() - return u'' - - def renderTargetBody(self): - target = self.targetObject - if target is not None: - targetView = zapi.getMultiAdapter((target, self.request)) - return targetView.render() - return u'' - - @Lazy - def page(self): - page = self.context.getPage() - return page is not None and NodeView(page, self.request) or None - - def textItems(self): - for child in self.context.getTextItems(): - yield NodeView(child, self.request) + target = self.target + return target is not None and target.render() or u'' @Lazy def body(self): @@ -103,7 +105,7 @@ class NodeView(BaseView): target = self.targetObject if target is None or IDocument.providedBy(target): return 'textbody' - if IConcept.providedBy(target): # TODO... + if IConcept.providedBy(target): return 'conceptbody' if IMediaAsset.providedBy(target) and target.contentType.startswith('image/'): return 'imagebody' @@ -118,22 +120,34 @@ class NodeView(BaseView): menu = self.context.getMenu() return menu is not None and NodeView(menu, self.request) or None + @Lazy def menuItems(self): - for child in self.context.getMenuItems(): - yield NodeView(child, self.request) + return [NodeView(child, self.request) + for child in self.context.getMenuItems()] def selected(self, item): - return item.context == self.context - - # view @@target - probably obsolete, replace by view.NodeTraverser - def renderTarget(self): - target = self.target + if item.context == self.context: + return True + if item.context in zapi.getParents(self.context) and not item.menuItems: + return True + return False + + def targetDefaultView(self): + target = self.request.annotations.get('loops.view', {}).get('target') + if target is None: + target = self.targetObject if target is not None: targetView = zapi.getMultiAdapter((target, self.request), name=zapi.getDefaultViewName(target, self.request)) return targetView() return u'' + def targetId(self): + target = self.request.annotations.get('loops.view', {}).get('target') + if target is None: + target = self.targetObject + if target is not None: + return zapi.getUtility(IIntIds).getId(target) class ConfigureView(NodeView): """ An editing view for configuring a node, optionally creating diff --git a/browser/node_macros.pt b/browser/node_macros.pt index 1f73843..aca1286 100644 --- a/browser/node_macros.pt +++ b/browser/node_macros.pt @@ -27,7 +27,7 @@ tal:condition="target" tal:attributes="class string:content-$level; ondblclick python: item.editable and onclick or ''" - tal:content="structure item/renderTargetBody"> + tal:content="structure item/renderTarget"> The body @@ -45,8 +45,7 @@ @@ -60,7 +59,7 @@ tal:attributes="class string:content-$level; ondblclick python: item.editable and onclick or ''"> The body @@ -74,7 +73,7 @@ tal:attributes="class string:content-$level; ondblclick python: item.editable and onclick or ''"> + tal:attributes="src string:${item/url}/.target/view" />
+
+ Here comes a document... + +
+ + + + +
+ Here comes an image... + +
+
+ + + +
+ Here comes a file... + + Download '' + +
+
+ diff --git a/interfaces.py b/interfaces.py index 7c01066..ea8c52b 100644 --- a/interfaces.py +++ b/interfaces.py @@ -53,7 +53,7 @@ class IPotentialTarget(Interface): proxyInterface = Attribute('An interface allowing an object to be ' 'used as a target for a view/node (and ' - 'typically specifying the corresponding schema') + 'typically specifying the corresponding schema)') # concept interfaces @@ -399,39 +399,8 @@ class INodeContained(Interface): containers(INode, IViewManager) -# schemas to be used by forms on view/node objects - -class ITargetProperties(Interface): - """ Fields used for specifying a view's or node's target. - """ - - targetType = schema.Choice( - title=_(u'Target Type'), - description=_(u'Type of the target'), - values=('loops.resource.Document', 'loops.resource.MediaAsset', - 'loops.concept.Concept'), - default=None, - required=False) - - targetUri = schema.TextLine( - title=_(u'Target URI'), - description=_(u'An URI being a unique reference to the target'), - required=False) - - -class INodeConfigSchema(INode, ITargetProperties): - """ All fields that may be shown in the node config form. - """ - - createTarget = schema.Bool( - title=_(u'Create Target'), - description=_(u'Should a new target object be created?'), - required=False) - - # the loops top-level container -#class ILoops(ILoopsObject, IFolder): class ILoops(ILoopsObject): """ The top-level object of a loops site. """ @@ -476,20 +445,6 @@ class IConceptRelation(IRelation): """ -# type and type manager interfaces - probably obsolete - -# class ILoopsType(IType): -# """ Each loops object is of a certain type providing this interface. -# Usually implemented as an adapter. -# """ - - -# class ILoopsTypeManager(ITypeManager): -# """ The loops type manager, probably implemented by an adapter to -# the loops root object or the loops root object itself. -# """ - - # interfaces for catalog indexes class IIndexAttributes(Interface): diff --git a/target.py b/target.py index d2b4fb0..66639cc 100644 --- a/target.py +++ b/target.py @@ -42,19 +42,32 @@ _ = MessageFactory('loops') # proxies for accessing target objects from views/nodes -class ConceptProxy(object): - implements(IConcept) - adapts(IConceptView) +class TargetProxy(object): def __init__(self, context): #self.context = context self.context = removeSecurityProxy(context) - def getTitle(self): return self.target.title + @Lazy + def target(self): + return self.context.target + + def getTitle(self): + return self.target.title def setTitle(self, title): self.target.title = title title = property(getTitle, setTitle) + +class ConceptProxy(TargetProxy): + + implements(IConcept) + adapts(IConceptView) + + def getConceptType(self): return self.target.conceptType + def setConceptType(self, conceptType): self.target.conceptType = conceptType + conceptType = property(getConceptType, setConceptType) + def getChildren(self, predicates=None): return self.target.getChildren(predicates) @@ -65,27 +78,13 @@ class ConceptProxy(object): return self.target.getResources(predicates) -class ResourceProxy(object): - - adapts(IView) - - def __init__(self, context): - #self.context = context - self.context = removeSecurityProxy(context) - - def getTitle(self): return self.target.title - def setTitle(self, title): self.target.title = title - title = property(getTitle, setTitle) +class ResourceProxy(TargetProxy): def setContentType(self, contentType): self.target.contentType = contentType def getContentType(self): return self.target.contentType contentType = property(getContentType, setContentType) - @Lazy - def target(self): - return self.context.target - class DocumentProxy(ResourceProxy): diff --git a/view.py b/view.py index bf9c0a4..720bc6c 100644 --- a/view.py +++ b/view.py @@ -28,6 +28,7 @@ from zope.app.container.contained import Contained from zope.app.container.ordered import OrderedContainer from zope.app.container.traversal import ContainerTraverser, ItemTraverser from zope.app.container.traversal import ContainerTraversable +from zope.app.intid.interfaces import IIntIds from zope.cachedescriptors.property import Lazy, readproperty from zope.component import adapts from zope.interface import implements @@ -38,7 +39,7 @@ from cybertools.relation import DyadicRelation from cybertools.relation.registry import getRelations from cybertools.relation.interfaces import IRelationRegistry, IRelatable -from interfaces import IView, INode, INodeConfigSchema +from interfaces import IView, INode from interfaces import IViewManager, INodeContained from interfaces import ILoopsContained from interfaces import ITargetRelation @@ -171,8 +172,9 @@ class NodeTraverser(ItemTraverser): if name.startswith('.target'): target = self.context.target if len(name) > len('.target') and IConcept.providedBy(target): - idx = int(name[len('.target'):]) - 1 - target = target.getResources()[idx] + idx = int(name[len('.target'):]) + target = zapi.getUtility(IIntIds).getObject(idx) + #target = target.getResources()[idx] viewAnnotations = request.annotations.get('loops.view', {}) viewAnnotations['target'] = target request.annotations['loops.view'] = viewAnnotations