diff --git a/browser/common.py b/browser/common.py
index a476e5a..9b05eb6 100644
--- a/browser/common.py
+++ b/browser/common.py
@@ -52,7 +52,7 @@ class BaseView(object):
skin = None
if skinName and IView.providedBy(self.context):
skin = zapi.queryUtility(ISkin, skinName)
- if skin is not None:
+ if skin:
applySkin(self.request, skin)
self.skin = skin
diff --git a/browser/configure.zcml b/browser/configure.zcml
index ad1711d..1490ccb 100644
--- a/browser/configure.zcml
+++ b/browser/configure.zcml
@@ -565,4 +565,32 @@
allowed_interface="zope.publisher.interfaces.browser.IBrowserPublisher"
permission="zope.Public" />
+
+
+
+
+
+
+
+
+
+
diff --git a/browser/node.py b/browser/node.py
index 4b00e4f..c2745b5 100644
--- a/browser/node.py
+++ b/browser/node.py
@@ -22,8 +22,10 @@ View class for Node objects.
$Id$
"""
+from zope import component, interface
from zope.cachedescriptors.property import Lazy
from zope.app import zapi
+from zope.app.annotation.interfaces import IAnnotations
from zope.app.catalog.interfaces import ICatalog
from zope.app.container.browser.contents import JustContents
from zope.app.container.browser.adding import ContentAdding
@@ -36,8 +38,10 @@ from zope.proxy import removeAllProxies
from zope.security import canAccess, canWrite
from zope.security.proxy import removeSecurityProxy
+from cybertools.browser import configurator
from cybertools.typology.interfaces import ITypeManager
from loops.interfaces import IConcept, IResource, IDocument, IMediaAsset, INode
+from loops.interfaces import IViewConfiguratorSchema
from loops.resource import MediaAsset
from loops import util
from loops.browser.common import BaseView
@@ -314,3 +318,43 @@ class NodeAdding(ContentAdding):
# 'has_custom_add_view': True,
# 'description': 'This creates a node with an associated document'})
return info
+
+
+class ViewPropertiesConfigurator(object):
+
+ interface.implements(IViewConfiguratorSchema)
+ component.adapts(INode)
+
+ def __init__(self, context):
+ self.context = removeSecurityProxy(context)
+
+ def setSkinName(self, skinName):
+ ann = IAnnotations(self.context)
+ setting = ann.get(configurator.ANNOTATION_KEY, {})
+ setting['skinName'] = {'value': skinName}
+ ann[configurator.ANNOTATION_KEY] = setting
+ def getSkinName(self):
+ ann = IAnnotations(self.context)
+ setting = ann.get(configurator.ANNOTATION_KEY, {})
+ return setting.get('skinName', {}).get('value', '')
+ skinName = property(getSkinName, setSkinName)
+
+
+class NodeViewConfigurator(configurator.ViewConfigurator):
+ """ Take properties from next menu item...
+ """
+
+ @property
+ def viewProperties(self):
+ result = []
+ for p in list(reversed(zapi.getParents(self.context))) + [self.context]:
+ if not INode.providedBy(p) or p.nodeType != 'menu':
+ continue
+ ann = IAnnotations(p)
+ propDefs = ann.get(configurator.ANNOTATION_KEY, {})
+ if propDefs:
+ result.extend([self.setupViewProperty(prop, propDef)
+ for prop, propDef in propDefs.items() if propDef])
+ return result
+
+
diff --git a/helpers.txt b/helpers.txt
index 2a2dfc5..7b9cce4 100755
--- a/helpers.txt
+++ b/helpers.txt
@@ -13,6 +13,7 @@ Let's first do some basic imports
>>> from zope.app.testing.setup import placefulSetUp, placefulTearDown
>>> site = placefulSetUp(True)
+ >>> from zope import interface, component
>>> from zope.app import zapi
>>> from zope.app.tests import ztapi
>>> from zope.interface import Interface
@@ -32,6 +33,10 @@ and setup a simple loops site with its manager objects,
>>> loopsRoot['resources'] = ResourceManager()
>>> resources = loopsRoot['resources']
+ >>> from loops.view import ViewManager, Node
+ >>> loopsRoot['views'] = ViewManager()
+ >>> views = loopsRoot['views']
+
some concepts,
>>> cc1 = Concept(u'Zope')
@@ -44,7 +49,7 @@ some concepts,
>>> cc2.title
u'Zope 3'
-and resources:
+resources,
>>> doc1 = Document(u'Zope Info')
>>> resources['doc1'] = doc1
@@ -54,6 +59,16 @@ and resources:
>>> img1 = MediaAsset(u'An Image')
>>> resources['img1'] = img1
+and nodes (in view space):
+
+ >>> m1 = Node(u'Home')
+ >>> views['m1'] = m1
+ >>> m1.nodeType = 'menu'
+
+ >>> m1p1 = Node(u'Page')
+ >>> m1['p1'] = m1p1
+ >>> m1p1.nodeType = 'page'
+
Finally, we also need a relation registry:
>>> from cybertools.relation.interfaces import IRelationRegistry
@@ -229,3 +244,55 @@ i.e. the 'topic' concept, via an adapter:
>>> cc1Adapter = cc1_type.typeInterface(cc1)
>>> ITopic.providedBy(cc1Adapter)
True
+
+
+Controlling presentation using view properties
+----------------------------------------------
+
+ >>> from zope.app.annotation.interfaces import IAttributeAnnotatable, IAnnotations
+ >>> from zope.app.annotation.attribute import AttributeAnnotations
+ >>> from loops.interfaces import INode
+
+First we have to make sure we can use attribute annotations with our nodes,
+and we also have to register an IViewConfigurator adapter for them:
+
+ >>> component.provideAdapter(AttributeAnnotations, (INode,), IAnnotations)
+
+ >>> from cybertools.browser.configurator import IViewConfigurator
+ >>> from loops.browser.node import NodeViewConfigurator
+ >>> from zope.publisher.interfaces.browser import IBrowserRequest
+ >>> component.provideAdapter(NodeViewConfigurator, (INode, IBrowserRequest),
+ ... IViewConfigurator)
+
+Now we are ready to set up a view on our page node:
+
+ >>> from loops.browser.node import NodeView
+ >>> request = TestRequest()
+ >>> view = NodeView(m1p1, request)
+
+The elements responsible for presentation are controlled by a controller
+object:
+
+ >>> from cybertools.browser.controller import Controller
+ >>> controller = Controller(view, request)
+ >>> getattr(controller, 'skinName', None) is None
+ True
+
+There is no `skinName` setting in the controller as we did not set any.
+The configurator (IViewConfigurator adapter, see above) takes the
+view properties from the attribute annotations. We set these properties
+using an adapter to the config schema; the configurator will only use
+settings on menu nodes (possibly above the node to be viewed in the
+browser).
+
+ >>> from loops.interfaces import IViewConfiguratorSchema
+ >>> from loops.browser.node import ViewPropertiesConfigurator
+ >>> component.provideAdapter(ViewPropertiesConfigurator, (INode,),
+ ... IViewConfiguratorSchema)
+
+ >>> pageConfigurator = IViewConfiguratorSchema(m1)
+ >>> pageConfigurator.skinName = 'SuperSkin'
+
+ >>> controller = Controller(view, request)
+ >>> controller.skinName.value
+ 'SuperSkin'
diff --git a/interfaces.py b/interfaces.py
index c9b8029..da05358 100644
--- a/interfaces.py
+++ b/interfaces.py
@@ -497,3 +497,14 @@ class ITypeConcept(Interface):
source="loops.TypeInterfaceSource",
required=False)
+
+# view configurator stuff
+
+class IViewConfiguratorSchema(Interface):
+
+ skinName = schema.TextLine(
+ title=_(u'Skin Name'),
+ description=_(u'Name of the skin to use for this part of the site'),
+ default=u'',
+ required=False)
+