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
This commit is contained in:
helmutm 2006-03-15 17:54:31 +00:00
parent f15f2e94e3
commit d687e95a8f
11 changed files with 159 additions and 138 deletions

View file

@ -443,13 +443,13 @@ Node Views
>>> view = NodeView(m11, TestRequest()) >>> view = NodeView(m11, TestRequest())
>>> page = view.page >>> page = view.page
>>> items = page.textItems() >>> items = page.textItems
>>> for item in items: >>> for item in items:
... print item.url, item.editable ... print item.url, item.editable
http://127.0.0.1/loops/views/m1/m11/m112 False http://127.0.0.1/loops/views/m1/m11/m112 False
>>> menu = view.menu >>> menu = view.menu
>>> items = menu.menuItems() >>> items = menu.menuItems
>>> for item in items: >>> for item in items:
... print item.url, view.selected(item) ... print item.url, view.selected(item)
http://127.0.0.1/loops/views/m1/m11 True 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 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.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', >>> form = {'action': 'create', 'create.title': 'New Resource',
... 'create.type': 'loops.resource.MediaAsset',} ... 'create.type': 'loops.resource.MediaAsset',}
>>> view = ConfigureView(m111, TestRequest(form = form)) >>> 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, 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. 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.component.interfaces import IFactory
>>> from zope.app.renderer import rest >>> from zope.app.renderer import rest
>>> ztapi.provideUtility(IFactory, rest.ReStructuredTextSourceFactory, >>> ztapi.provideUtility(IFactory, rest.ReStructuredTextSourceFactory,
@ -513,13 +516,13 @@ and of a lot of other stuff needed for the rendering machine.
>>> m112.target = doc1 >>> m112.target = doc1
>>> view = NodeView(m112, TestRequest()) >>> view = NodeView(m112, TestRequest())
>>> view.renderTargetBody() >>> view.renderTarget()
u'' u''
>>> doc1.data = u'Test data\n\nAnother paragraph' >>> doc1.data = u'Test data\n\nAnother paragraph'
>>> view.renderTargetBody() >>> view.renderTarget()
u'Test data\n\nAnother paragraph' u'Test data\n\nAnother paragraph'
>>> doc1.contentType = 'text/restructured' >>> doc1.contentType = 'text/restructured'
>>> view.renderTargetBody() >>> view.renderTarget()
u'<p>Test data</p>\n<p>Another paragraph</p>\n' u'<p>Test data</p>\n<p>Another paragraph</p>\n'
It is possible to edit a target's attributes directly in an It is possible to edit a target's attributes directly in an

View file

@ -25,6 +25,7 @@ $Id$
from zope.app import zapi from zope.app import zapi
from zope.app.dublincore.interfaces import ICMFDublinCore from zope.app.dublincore.interfaces import ICMFDublinCore
from zope.app.form.browser.interfaces import ITerms from zope.app.form.browser.interfaces import ITerms
from zope.app.intid.interfaces import IIntIds
from zope.cachedescriptors.property import Lazy from zope.cachedescriptors.property import Lazy
from zope.interface import implements from zope.interface import implements
from zope.security.proxy import removeSecurityProxy from zope.security.proxy import removeSecurityProxy
@ -86,6 +87,10 @@ class BaseView(object):
for o in objs: for o in objs:
yield BaseView(o, request) yield BaseView(o, request)
@Lazy
def uniqueId(self):
return zapi.getUtility(IIntIds).getId(self.context)
class LoopsTerms(object): class LoopsTerms(object):
""" Provide the ITerms interface, e.g. for usage in selection """ Provide the ITerms interface, e.g. for usage in selection

View file

@ -158,6 +158,14 @@
name="concept.html" name="concept.html"
/> />
<zope:adapter
for="loops.interfaces.IConcept
zope.publisher.interfaces.browser.IBrowserRequest"
provides="zope.interface.Interface"
factory="loops.browser.concept.ConceptView"
permission="zope.View"
/>
<!-- resource manager --> <!-- resource manager -->
<addform <addform
@ -199,15 +207,6 @@
<!-- document --> <!-- document -->
<!--<zope:view
type="zope.publisher.interfaces.browser.IBrowserRequest"
for="zope.schema.interfaces.IBytes"
provides="zope.app.form.interfaces.IInputWidget"
factory=".resource.DataWidget"
permission="zope.Public"
name="loops.resource.DataWidget"
/>-->
<addform <addform
label="Add Document" label="Add Document"
name="AddLoopsDocument.html" name="AddLoopsDocument.html"
@ -278,6 +277,14 @@
filter="nothing" filter="nothing"
/> />
<zope:adapter
for="loops.interfaces.IMediaAsset
zope.publisher.interfaces.browser.IBrowserRequest"
provides="zope.interface.Interface"
factory="loops.browser.resource.MediaAssetView"
permission="zope.View"
/>
<!-- view manager --> <!-- view manager -->
<addform <addform
@ -423,7 +430,7 @@
label="Edit Concept" label="Edit Concept"
name="edit_target.html" name="edit_target.html"
schema="loops.interfaces.IConcept" schema="loops.interfaces.IConcept"
fields="title" fields="title conceptType"
for="loops.interfaces.IConceptView" for="loops.interfaces.IConceptView"
template="edit.pt" template="edit.pt"
permission="zope.ManageContent" permission="zope.ManageContent"
@ -444,11 +451,13 @@
name="node.html" name="node.html"
/> />
<!-- render file or image assigned to a node as target -->
<page <page
name="target" name="view"
for="loops.interfaces.INode" for="loops.interfaces.INode"
class=".node.NodeView" class=".node.NodeView"
attribute="renderTarget" attribute="targetDefaultView"
permission="zope.View" permission="zope.View"
/> />

View file

@ -23,17 +23,12 @@
<metal:body fill-slot="body"> <metal:body fill-slot="body">
<tal:content define="item view/page; <tal:content define="item view/item;
level level|python: 1; level level|python: 1;
target request/annotations/loops.view/target | nothing"> macro item/macro">
<tal:content condition="not:target"> <tal:content>
<metal:block use-macro="views/node_macros/content" /> <metal:block use-macro="macro" />
</tal:content> </tal:content>
<tal:target condition="target">
<div>
Here comes the real target... <span tal:replace="target/title" />
</div>
</tal:target>
</tal:content> </tal:content>
</metal:body> </metal:body>

View file

@ -27,6 +27,8 @@ from zope.app import zapi
from zope.app.catalog.interfaces import ICatalog from zope.app.catalog.interfaces import ICatalog
from zope.app.container.browser.contents import JustContents from zope.app.container.browser.contents import JustContents
from zope.app.event.objectevent import ObjectCreatedEvent 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.dottedname.resolve import resolve
from zope.event import notify from zope.event import notify
from zope.proxy import removeAllProxies from zope.proxy import removeAllProxies
@ -43,6 +45,28 @@ from loops.browser.concept import ConceptView
class NodeView(BaseView): 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 @Lazy
def nodeType(self): def nodeType(self):
return self.context.nodeType return self.context.nodeType
@ -66,33 +90,11 @@ class NodeView(BaseView):
def target(self): def target(self):
obj = self.targetObject obj = self.targetObject
if obj is not None: if obj is not None:
if IConcept.providedBy(obj): return zapi.getMultiAdapter((obj, self.request))
return ConceptView(obj, self.request)
return BaseView(obj, self.request)
def renderTarget(self): def renderTarget(self):
target = self.targetObject target = self.target
if target is not None: return target is not None and target.render() or u''
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)
@Lazy @Lazy
def body(self): def body(self):
@ -103,7 +105,7 @@ class NodeView(BaseView):
target = self.targetObject target = self.targetObject
if target is None or IDocument.providedBy(target): if target is None or IDocument.providedBy(target):
return 'textbody' return 'textbody'
if IConcept.providedBy(target): # TODO... if IConcept.providedBy(target):
return 'conceptbody' return 'conceptbody'
if IMediaAsset.providedBy(target) and target.contentType.startswith('image/'): if IMediaAsset.providedBy(target) and target.contentType.startswith('image/'):
return 'imagebody' return 'imagebody'
@ -118,22 +120,34 @@ class NodeView(BaseView):
menu = self.context.getMenu() menu = self.context.getMenu()
return menu is not None and NodeView(menu, self.request) or None return menu is not None and NodeView(menu, self.request) or None
@Lazy
def menuItems(self): def menuItems(self):
for child in self.context.getMenuItems(): return [NodeView(child, self.request)
yield NodeView(child, self.request) for child in self.context.getMenuItems()]
def selected(self, item): def selected(self, item):
return item.context == self.context if item.context == self.context:
return True
if item.context in zapi.getParents(self.context) and not item.menuItems:
return True
return False
# view @@target - probably obsolete, replace by view.NodeTraverser def targetDefaultView(self):
def renderTarget(self): target = self.request.annotations.get('loops.view', {}).get('target')
target = self.target if target is None:
target = self.targetObject
if target is not None: if target is not None:
targetView = zapi.getMultiAdapter((target, self.request), targetView = zapi.getMultiAdapter((target, self.request),
name=zapi.getDefaultViewName(target, self.request)) name=zapi.getDefaultViewName(target, self.request))
return targetView() return targetView()
return u'' 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): class ConfigureView(NodeView):
""" An editing view for configuring a node, optionally creating """ An editing view for configuring a node, optionally creating

View file

@ -27,7 +27,7 @@
tal:condition="target" tal:condition="target"
tal:attributes="class string:content-$level; tal:attributes="class string:content-$level;
ondblclick python: item.editable and onclick or ''" ondblclick python: item.editable and onclick or ''"
tal:content="structure item/renderTargetBody"> tal:content="structure item/renderTarget">
The body The body
</div> </div>
</tal:body> </tal:body>
@ -45,8 +45,7 @@
</div> </div>
<div tal:repeat="resource target/resources"> <div tal:repeat="resource target/resources">
<a href="#" <a href="#"
tal:attributes="href tal:attributes="href string:${item/url}/.target${resource/uniqueId}"
string:${item/url}/.target${repeat/resource/number}/@@node.html"
tal:content="resource/title">Resource Title</a> tal:content="resource/title">Resource Title</a>
</div> </div>
</tal:body> </tal:body>
@ -60,7 +59,7 @@
tal:attributes="class string:content-$level; tal:attributes="class string:content-$level;
ondblclick python: item.editable and onclick or ''"> ondblclick python: item.editable and onclick or ''">
<a href="#" <a href="#"
tal:attributes="href string:${item/url}/@@target" tal:attributes="href string:${item/url}/.target/view"
tal:content="structure body">The body</a> tal:content="structure body">The body</a>
</div> </div>
</tal:body> </tal:body>
@ -74,7 +73,7 @@
tal:attributes="class string:content-$level; tal:attributes="class string:content-$level;
ondblclick python: item.editable and onclick or ''"> ondblclick python: item.editable and onclick or ''">
<img src="target" <img src="target"
tal:attributes="src string:${item/url}/@@target" /> tal:attributes="src string:${item/url}/.target/view" />
</div> </div>
<div class="content-1" <div class="content-1"
tal:condition="body" tal:condition="body"

View file

@ -26,6 +26,7 @@ from zope.cachedescriptors.property import Lazy
from zope.app import zapi from zope.app import zapi
from zope.app.catalog.interfaces import ICatalog from zope.app.catalog.interfaces import ICatalog
from zope.app.dublincore.interfaces import ICMFDublinCore from zope.app.dublincore.interfaces import ICMFDublinCore
from zope.app.pagetemplate import ViewPageTemplateFile
from zope.proxy import removeAllProxies from zope.proxy import removeAllProxies
from zope.security import canAccess, canWrite from zope.security import canAccess, canWrite
from zope.security.proxy import removeSecurityProxy from zope.security.proxy import removeSecurityProxy
@ -46,6 +47,8 @@ renderingFactories = {
class ResourceView(BaseView): class ResourceView(BaseView):
template = ViewPageTemplateFile('resource_macros.pt')
def concepts(self): def concepts(self):
for r in self.context.getConceptRelations(): for r in self.context.getConceptRelations():
yield ConceptRelationView(r, self.request) yield ConceptRelationView(r, self.request)
@ -112,6 +115,8 @@ class ResourceConfigureView(ResourceView, ConceptConfigureView):
class DocumentView(ResourceView): class DocumentView(ResourceView):
macro = ResourceView.template.macros['render']
def render(self): def render(self):
""" Return the rendered content (data) of the context object. """ Return the rendered content (data) of the context object.
""" """
@ -124,3 +129,11 @@ class DocumentView(ResourceView):
return view.render() return view.render()
class MediaAssetView(ResourceView):
@property
def macro(self):
if 'image/' in self.context.contentType:
return self.template.macros['image']
else:
return self.template.macros['download']

View file

@ -0,0 +1,27 @@
<metal:block define-macro="render">
<div>
Here comes a document...
<span tal:replace="structure item/render" />
</div>
</metal:block>
<metal:block define-macro="image">
<div>
Here comes an image...
<img src="#"
tal:attributes="src string:${view/url}/.target${view/targetId}/view" />
</div>
</metal:block>
<metal:block define-macro="download">
<div>
Here comes a file...
<a href="#"
tal:attributes="href string:${view/url}/.target${view/targetId}/view">
Download '<span tal:replace="item/title" />'
</a>
</div>
</metal:block>

View file

@ -53,7 +53,7 @@ class IPotentialTarget(Interface):
proxyInterface = Attribute('An interface allowing an object to be ' proxyInterface = Attribute('An interface allowing an object to be '
'used as a target for a view/node (and ' 'used as a target for a view/node (and '
'typically specifying the corresponding schema') 'typically specifying the corresponding schema)')
# concept interfaces # concept interfaces
@ -399,39 +399,8 @@ class INodeContained(Interface):
containers(INode, IViewManager) 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 # the loops top-level container
#class ILoops(ILoopsObject, IFolder):
class ILoops(ILoopsObject): class ILoops(ILoopsObject):
""" The top-level object of a loops site. """ 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 # interfaces for catalog indexes
class IIndexAttributes(Interface): class IIndexAttributes(Interface):

View file

@ -42,19 +42,32 @@ _ = MessageFactory('loops')
# proxies for accessing target objects from views/nodes # proxies for accessing target objects from views/nodes
class ConceptProxy(object):
implements(IConcept) class TargetProxy(object):
adapts(IConceptView)
def __init__(self, context): def __init__(self, context):
#self.context = context #self.context = context
self.context = removeSecurityProxy(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 def setTitle(self, title): self.target.title = title
title = property(getTitle, setTitle) 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): def getChildren(self, predicates=None):
return self.target.getChildren(predicates) return self.target.getChildren(predicates)
@ -65,27 +78,13 @@ class ConceptProxy(object):
return self.target.getResources(predicates) return self.target.getResources(predicates)
class ResourceProxy(object): class ResourceProxy(TargetProxy):
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)
def setContentType(self, contentType): def setContentType(self, contentType):
self.target.contentType = contentType self.target.contentType = contentType
def getContentType(self): return self.target.contentType def getContentType(self): return self.target.contentType
contentType = property(getContentType, setContentType) contentType = property(getContentType, setContentType)
@Lazy
def target(self):
return self.context.target
class DocumentProxy(ResourceProxy): class DocumentProxy(ResourceProxy):

View file

@ -28,6 +28,7 @@ from zope.app.container.contained import Contained
from zope.app.container.ordered import OrderedContainer from zope.app.container.ordered import OrderedContainer
from zope.app.container.traversal import ContainerTraverser, ItemTraverser from zope.app.container.traversal import ContainerTraverser, ItemTraverser
from zope.app.container.traversal import ContainerTraversable from zope.app.container.traversal import ContainerTraversable
from zope.app.intid.interfaces import IIntIds
from zope.cachedescriptors.property import Lazy, readproperty from zope.cachedescriptors.property import Lazy, readproperty
from zope.component import adapts from zope.component import adapts
from zope.interface import implements from zope.interface import implements
@ -38,7 +39,7 @@ from cybertools.relation import DyadicRelation
from cybertools.relation.registry import getRelations from cybertools.relation.registry import getRelations
from cybertools.relation.interfaces import IRelationRegistry, IRelatable 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 IViewManager, INodeContained
from interfaces import ILoopsContained from interfaces import ILoopsContained
from interfaces import ITargetRelation from interfaces import ITargetRelation
@ -171,8 +172,9 @@ class NodeTraverser(ItemTraverser):
if name.startswith('.target'): if name.startswith('.target'):
target = self.context.target target = self.context.target
if len(name) > len('.target') and IConcept.providedBy(target): if len(name) > len('.target') and IConcept.providedBy(target):
idx = int(name[len('.target'):]) - 1 idx = int(name[len('.target'):])
target = target.getResources()[idx] target = zapi.getUtility(IIntIds).getObject(idx)
#target = target.getResources()[idx]
viewAnnotations = request.annotations.get('loops.view', {}) viewAnnotations = request.annotations.get('loops.view', {})
viewAnnotations['target'] = target viewAnnotations['target'] = target
request.annotations['loops.view'] = viewAnnotations request.annotations['loops.view'] = viewAnnotations