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())
>>> 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'<p>Test data</p>\n<p>Another paragraph</p>\n'
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.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

View file

@ -158,6 +158,14 @@
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 -->
<addform
@ -199,15 +207,6 @@
<!-- 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
label="Add Document"
name="AddLoopsDocument.html"
@ -278,6 +277,14 @@
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 -->
<addform
@ -423,7 +430,7 @@
label="Edit Concept"
name="edit_target.html"
schema="loops.interfaces.IConcept"
fields="title"
fields="title conceptType"
for="loops.interfaces.IConceptView"
template="edit.pt"
permission="zope.ManageContent"
@ -444,11 +451,13 @@
name="node.html"
/>
<!-- render file or image assigned to a node as target -->
<page
name="target"
name="view"
for="loops.interfaces.INode"
class=".node.NodeView"
attribute="renderTarget"
attribute="targetDefaultView"
permission="zope.View"
/>

View file

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

View file

@ -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

View file

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

View file

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

View file

@ -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):

View file

@ -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