[Zope3-checkins]
SVN: Zope3/branches/roger-contentprovider/src/zope/viewlet/
Okay, now all the package tests pass,
including the reworked directive
Stephan Richter
srichter at cosmos.phy.tufts.edu
Wed Oct 12 21:42:21 EDT 2005
Log message for revision 39110:
Okay, now all the package tests pass, including the reworked directive
tests.
Changed:
U Zope3/branches/roger-contentprovider/src/zope/viewlet/README.txt
U Zope3/branches/roger-contentprovider/src/zope/viewlet/directives.txt
U Zope3/branches/roger-contentprovider/src/zope/viewlet/manager.py
U Zope3/branches/roger-contentprovider/src/zope/viewlet/metaconfigure.py
U Zope3/branches/roger-contentprovider/src/zope/viewlet/metadirectives.py
U Zope3/branches/roger-contentprovider/src/zope/viewlet/tests.py
U Zope3/branches/roger-contentprovider/src/zope/viewlet/viewlet.py
-=-
Modified: Zope3/branches/roger-contentprovider/src/zope/viewlet/README.txt
===================================================================
--- Zope3/branches/roger-contentprovider/src/zope/viewlet/README.txt 2005-10-13 01:39:47 UTC (rev 39109)
+++ Zope3/branches/roger-contentprovider/src/zope/viewlet/README.txt 2005-10-13 01:42:20 UTC (rev 39110)
@@ -210,6 +210,7 @@
--------------------
+
A Complex Example
-----------------
Modified: Zope3/branches/roger-contentprovider/src/zope/viewlet/directives.txt
===================================================================
--- Zope3/branches/roger-contentprovider/src/zope/viewlet/directives.txt 2005-10-13 01:39:47 UTC (rev 39109)
+++ Zope3/branches/roger-contentprovider/src/zope/viewlet/directives.txt 2005-10-13 01:42:20 UTC (rev 39110)
@@ -1,17 +1,12 @@
-=================================================
-The ``viewletManager`` and ``viewlet`` Directives
-=================================================
+================================
+The ``viewletManager`` Directive
+================================
-The viewletManager directive allows you to quickly register a new content
-provider that presents viewlets on a web page. You'll find more information
-about viewlets and viewlet managers in the `README.txt` file.
+The ``viewletManager`` directive allows you to quickly register a new viewlet
+manager without worrying about the details of the ``adapter``
+directive. Before we can use the directives, we have to register their
+handlers by executing the package's meta configuration:
-Here is a sample directive::
-
- >>> from zope.app import zapi
- >>> import zope.interface
-
- >>> from zope.viewlet.tests import ILeftViewlet
>>> from zope.configuration import xmlconfig
>>> context = xmlconfig.string('''
... <configure i18n_domain="zope">
@@ -19,40 +14,22 @@
... </configure>
... ''')
- >>> import os, tempfile
- >>> temp_dir = tempfile.mkdtemp()
+Now we can register a viewlet manager:
- >>> testViewletManager = os.path.join(temp_dir, 'testviewletmanager.pt')
- >>> open(testViewletManager, 'w').write('''
- ... <div tal:repeat="viewlet options/viewlets"
- ... <div tal:replace="viewlet"></div>
- ... </div>
- ... ''')
-
>>> context = xmlconfig.string('''
- ... <configure xmlns="http://namespaces.zope.org/browser" i18n_domain="zope"
- ... package="zope.viewlet.tests">
+ ... <configure xmlns="http://namespaces.zope.org/browser" i18n_domain="zope">
... <viewletManager
- ... name="testviewlet"
- ... for="*"
- ... providerType="zope.viewlet.tests.ILeftViewlet"
- ... template="%s"
+ ... name="defaultmanager"
... permission="zope.Public"
... />
... </configure>
- ... ''' % (testViewletManager), context=context)
-
-As you can see, the directive looks very similar to the page directive and you
-are right. The viewlet manager directive does not permit you to specify a
-`menu` and `title`, since it is not sensible to have a menu item for a viewlet.
-However, it does support two more qualifying attributes, `view` and
-`providerType`. While view is nearly never specified (very common default),
-the `providerType` attribute *must* be specified.
+ ... ''', context=context)
-# More (and more precise) information needed here...
+Let's make sure the directive has really issued a sensible adapter
+registration; to do that, we create some dummy content, request and view
+objects:
-If we now look into the adapter registry, we will find the viewlet manager:
-
+ >>> import zope.interface
>>> class Content(object):
... zope.interface.implements(zope.interface.Interface)
>>> content = Content()
@@ -63,181 +40,347 @@
>>> from zope.app.publisher.browser import BrowserView
>>> view = BrowserView(content, request)
- >>> import zope.interface
- >>> from zope.viewlet.interfaces import IViewletManager
-
- >>> manager = zapi.getMultiAdapter(
- ... (content, request, view), IViewletManager, name='testviewlet')
+Now let's lookup the manager. This particular registration is pretty boring:
-As we did not yet associate viewlets with this viewlet manager, calling it
-gives us an empty string:
+ >>> import zope.component
+ >>> from zope.viewlet import interfaces
+ >>> manager = zope.component.getMultiAdapter(
+ ... (content, request, view),
+ ... interfaces.IViewletManager, name='defaultmanager')
- >>> manager().strip()
+ >>> manager
+ <zope.viewlet.manager.<ViewletManager providing IViewletManager> object ...>
+ >>> interfaces.IViewletManager.providedBy(manager)
+ True
+ >>> manager.template is None
+ True
+ >>> manager()
u''
-So let's now create a viewlet for the viewlet manager:
+However, this registration is not very useful, since we did specify a specific
+viewlet manager interface, a specific content interface, specific view or
+specific layer. This means that all viewlets registered will be found.
- >>> testViewlet = os.path.join(temp_dir, 'testviewlet.pt')
- >>> open(testViewlet, 'w').write('''
- ... <div>testviewlet content</div>
+The first step to effectively using the viewlet manager directive is to define
+a special viewlet manager interface:
+
+ >>> class ILeftColumn(interfaces.IViewletManager):
+ ... """Left column of my page."""
+
+Now we can register register a manager providing this interface:
+
+ >>> context = xmlconfig.string('''
+ ... <configure xmlns="http://namespaces.zope.org/browser" i18n_domain="zope">
+ ... <viewletManager
+ ... name="leftcolumn"
+ ... permission="zope.Public"
+ ... provides="zope.viewlet.directives.ILeftColumn"
+ ... />
+ ... </configure>
+ ... ''', context=context)
+
+ >>> manager = zope.component.getMultiAdapter(
+ ... (content, request, view), ILeftColumn, name='leftcolumn')
+
+ >>> manager
+ <zope.viewlet.manager.<ViewletManager providing ILeftColumn> object ...>
+ >>> ILeftColumn.providedBy(manager)
+ True
+ >>> manager.template is None
+ True
+ >>> manager()
+ u''
+
+Next let's see what happens, if we specify a template for the viewlet manager:
+
+ >>> import os, tempfile
+ >>> temp_dir = tempfile.mkdtemp()
+
+ >>> leftColumnTemplate = os.path.join(temp_dir, 'leftcolumn.pt')
+ >>> open(leftColumnTemplate, 'w').write('''
+ ... <div class="column">
+ ... <div class="entry"
+ ... tal:repeat="viewlet options/viewlets"
+ ... tal:content="structure viewlet" />
+ ... </div>
... ''')
-
+
>>> context = xmlconfig.string('''
- ... <configure xmlns="http://namespaces.zope.org/browser" i18n_domain="zope"
- ... package="zope.viewlet.tests">
+ ... <configure xmlns="http://namespaces.zope.org/browser" i18n_domain="zope">
+ ... <viewletManager
+ ... name="leftcolumn"
+ ... permission="zope.Public"
+ ... provides="zope.viewlet.directives.ILeftColumn"
+ ... template="%s"
+ ... />
+ ... </configure>
+ ... ''' %leftColumnTemplate, context=context)
+
+ >>> manager = zope.component.getMultiAdapter(
+ ... (content, request, view), ILeftColumn, name='leftcolumn')
+
+ >>> manager
+ <zope.viewlet.manager.<ViewletManager providing ILeftColumn> object ...>
+ >>> ILeftColumn.providedBy(manager)
+ True
+ >>> manager.template
+ <BoundPageTemplateFile of ...<ViewletManager providing ILeftColumn> ...>>
+ >>> print manager().strip()
+ <div class="column">
+ </div>
+
+Additionally you can specify a class that will serve as a base to the default
+viewlet manager or be a viewlet manager in its own right. In our case we will
+provide a custom implementation of the ``sort()`` method, which will sort by a
+weight attribute in the viewlet:
+
+ >>> class WeightBasedSorting(object):
+ ... def sort(self, viewlets):
+ ... return sorted(viewlets,
+ ... lambda x, y: cmp(x[1].weight, y[1].weight))
+
+ >>> context = xmlconfig.string('''
+ ... <configure xmlns="http://namespaces.zope.org/browser" i18n_domain="zope">
+ ... <viewletManager
+ ... name="leftcolumn"
+ ... permission="zope.Public"
+ ... provides="zope.viewlet.directives.ILeftColumn"
+ ... template="%s"
+ ... class="zope.viewlet.directives.WeightBasedSorting"
+ ... />
+ ... </configure>
+ ... ''' %leftColumnTemplate, context=context)
+
+ >>> manager = zope.component.getMultiAdapter(
+ ... (content, request, view), ILeftColumn, name='leftcolumn')
+
+ >>> manager
+ <zope.viewlet.manager.<ViewletManager providing ILeftColumn> object ...>
+ >>> manager.__class__.__bases__
+ (<class 'zope.viewlet.directives.WeightBasedSorting'>,
+ <class 'zope.viewlet.manager.ViewletManagerBase'>)
+ >>> ILeftColumn.providedBy(manager)
+ True
+ >>> manager.template
+ <BoundPageTemplateFile of ...<ViewletManager providing ILeftColumn> ...>>
+ >>> print manager().strip()
+ <div class="column">
+ </div>
+
+Finally, if a non-existent template is specified, an error is raised:
+
+ >>> context = xmlconfig.string('''
+ ... <configure xmlns="http://namespaces.zope.org/browser" i18n_domain="zope">
+ ... <viewletManager
+ ... name="leftcolumn"
+ ... permission="zope.Public"
+ ... template="foo.pt"
+ ... />
+ ... </configure>
+ ... ''', context=context)
+ Traceback (most recent call last):
+ ...
+ ZopeXMLConfigurationError: File "<string>", line 3.2-7.8
+ ConfigurationError: ('No such file', '...foo.pt')
+
+
+=========================
+The ``viewlet`` Directive
+=========================
+
+Now that we have a viewlet manager, we have to register some viewlets for
+it. The ``viewlet`` directive is similar to the ``viewletManager`` directive,
+except that the viewlet is also registered for a particular manager interface,
+as seen below:
+
+ >>> weatherTemplate = os.path.join(temp_dir, 'weather.pt')
+ >>> open(weatherTemplate, 'w').write('''
+ ... <div>sunny</div>
+ ... ''')
+
+ >>> context = xmlconfig.string('''
+ ... <configure xmlns="http://namespaces.zope.org/browser" i18n_domain="zope">
... <viewlet
- ... name="testviewlet"
- ... for="*"
- ... providerType="zope.viewlet.tests.ILeftViewlet"
+ ... name="weather"
+ ... manager="zope.viewlet.directives.ILeftColumn"
... template="%s"
... permission="zope.Public"
... />
... </configure>
- ... ''' % (testViewlet), context=context)
+ ... ''' % weatherTemplate, context=context)
-The ``viewlet`` directive is similar to the viewletManager directive.
-
If we look into the adapter registry, we will find the viewlet:
- >>> from zope.viewlet.interfaces import IViewlet
-
- >>> viewlet = zapi.getMultiAdapter(
- ... (content, request, view, manager), IViewlet, name='testviewlet')
+ >>> viewlet = zope.component.getMultiAdapter(
+ ... (content, request, view, manager), interfaces.IViewlet,
+ ... name='weather')
>>> viewlet().strip()
- u'<div>testviewlet content</div>'
+ u'<div>sunny</div>'
The manager now also gives us the output of the one and only viewlet:
- >>> manager().strip()
- u'<div>testviewlet content</div>'
+ >>> print manager().strip()
+ <div class="column">
+ <div class="entry">
+ <div>sunny</div>
+ </div>
+ </div>
Let's now ensure that we can also specify a viewlet class:
+ >>> class Weather(object):
+ ... weight = 0
+
>>> context = xmlconfig.string('''
- ... <configure xmlns="http://namespaces.zope.org/browser" i18n_domain="zope"
- ... package="zope.viewlet.tests">
+ ... <configure xmlns="http://namespaces.zope.org/browser" i18n_domain="zope">
... <viewlet
- ... name="testviewlet2"
+ ... name="weather2"
... for="*"
- ... providerType="zope.viewlet.tests.ILeftViewlet"
+ ... manager="zope.viewlet.directives.ILeftColumn"
... template="%s"
- ... class=".TestViewlet"
+ ... class="zope.viewlet.directives.Weather"
... permission="zope.Public"
... />
... </configure>
- ... ''' % testViewlet, context=context)
+ ... ''' % weatherTemplate, context=context)
>>> viewlet = zope.component.getMultiAdapter(
- ... (content, request, view, manager), name='testviewlet2')
+ ... (content, request, view, manager), interfaces.IViewlet,
+ ... name='weather2')
>>> viewlet().strip()
- u'<div>testviewlet content</div>'
+ u'<div>sunny</div>'
-#Okay, so the template-driven cases work. But just specifying a class should
-#also work:
-#
-# >>> context = xmlconfig.string('''
-# ... <configure xmlns="http://namespaces.zope.org/browser" i18n_domain="zope"
-# ... package="zope.viewlet.tests">
-# ... <viewlet
-# ... name="testviewlet3"
-# ... for="*"
-# ... providerType="zope.viewlet.tests.ILeftViewlet"
-# ... class=".tests.TestViewlet2"
-# ... permission="zope.Public"
-# ... />
-# ... </configure>
-# ... ''', context=context)
-#
-# >>> viewlet = zope.component.getMultiAdapter(
-# ... (content, request, view), ILeftViewlet, name='testviewlet3')
-# >>> viewlet()
-# u'called'
-#
-#It should also be possible to specify an alternative attribute of the class to
-#be rendered upon calling the viewlet:
-#
-# >>> context = xmlconfig.string('''
-# ... <configure xmlns="http://namespaces.zope.org/browser" i18n_domain="zope"
-# ... package="zope.viewlet.tests">
-# ... <viewlet
-# ... name="testviewlet4"
-# ... for="*"
-# ... providerType="zope.viewlet.tests.ILeftViewlet"
-# ... class=".tests.TestViewlet"
-# ... attribute="doSomething"
-# ... permission="zope.Public"
-# ... />
-# ... </configure>
-# ... ''', context=context)
-#
-# >>> viewlet = zope.component.getMultiAdapter(
-# ... (content, request, view), ILeftViewlet, name='testviewlet4')
-# >>> viewlet()
-# u'something'
-#
-#
-#Error Scenarios
-#---------------
-#
-#Neither the class or template have been specified:
-#
-# >>> context = xmlconfig.string('''
-# ... <configure xmlns="http://namespaces.zope.org/browser" i18n_domain="zope"
-# ... package="zope.viewlet.tests">
-# ... <viewlet
-# ... name="testviewlet"
-# ... providerType="zope.viewlet.tests.ILeftViewlet"
-# ... permission="zope.Public"
-# ... />
-# ... </configure>
-# ... ''', context=context)
-# Traceback (most recent call last):
-# ...
-# ZopeXMLConfigurationError: File "<string>", line 4.2-8.8
-# ConfigurationError: Must specify a class or template
-#
-#The specified attribute is not ``__call__``, but also a template has been
-#specified:
-#
-# >>> context = xmlconfig.string('''
-# ... <configure xmlns="http://namespaces.zope.org/browser" i18n_domain="zope"
-# ... package="zope.viewlet.tests">
-# ... <viewlet
-# ... name="testviewlet"
-# ... providerType="zope.viewlet.tests.ILeftViewlet"
-# ... template="test_viewlet.pt"
-# ... attribute="faux"
-# ... permission="zope.Public"
-# ... />
-# ... </configure>
-# ... ''', context=context)
-# Traceback (most recent call last):
-# ...
-# ZopeXMLConfigurationError: File "<string>", line 4.2-10.8
-# ConfigurationError: Attribute and template cannot be used together.
-#
-#Now, we are not specifying a template, but a class that does not have the
-#specified attribute:
-#
-# >>> context = xmlconfig.string('''
-# ... <configure xmlns="http://namespaces.zope.org/browser" i18n_domain="zope"
-# ... package="zope.viewlet.tests">
-# ... <viewlet
-# ... name="testviewlet"
-# ... providerType="zope.viewlet.tests.ILeftViewlet"
-# ... class=".TestViewlet"
-# ... attribute="faux"
-# ... permission="zope.Public"
-# ... />
-# ... </configure>
-# ... ''', context=context)
-# Traceback (most recent call last):
-# ...
-# ZopeXMLConfigurationError: File "<string>", line 4.2-10.8
-# ConfigurationError: The provided class doesn't have the specified attribute
+Okay, so the template-driven cases work. But just specifying a class should
+also work:
+ >>> class Sport(object):
+ ... weight = 0
+ ... def __call__(self):
+ ... return u'Red Sox vs. White Sox'
+ >>> context = xmlconfig.string('''
+ ... <configure xmlns="http://namespaces.zope.org/browser" i18n_domain="zope">
+ ... <viewlet
+ ... name="sport"
+ ... for="*"
+ ... manager="zope.viewlet.directives.ILeftColumn"
+ ... class="zope.viewlet.directives.Sport"
+ ... permission="zope.Public"
+ ... />
+ ... </configure>
+ ... ''', context=context)
+ >>> viewlet = zope.component.getMultiAdapter(
+ ... (content, request, view, manager), interfaces.IViewlet, name='sport')
+ >>> viewlet()
+ u'Red Sox vs. White Sox'
+
+It should also be possible to specify an alternative attribute of the class to
+be rendered upon calling the viewlet:
+
+ >>> class Stock(object):
+ ... weight = 0
+ ... def getStockTicker(self):
+ ... return u'SRC $5.19'
+
+ >>> context = xmlconfig.string('''
+ ... <configure xmlns="http://namespaces.zope.org/browser" i18n_domain="zope">
+ ... <viewlet
+ ... name="stock"
+ ... for="*"
+ ... manager="zope.viewlet.directives.ILeftColumn"
+ ... class="zope.viewlet.directives.Stock"
+ ... attribute="getStockTicker"
+ ... permission="zope.Public"
+ ... />
+ ... </configure>
+ ... ''', context=context)
+
+ >>> viewlet = zope.component.getMultiAdapter(
+ ... (content, request, view, manager), interfaces.IViewlet,
+ ... name='stock')
+ >>> viewlet()
+ u'SRC $5.19'
+
+A final feature the ``viewlet`` directive supports is the additional
+specification of any amount keyword arguments:
+
+ >>> context = xmlconfig.string('''
+ ... <configure xmlns="http://namespaces.zope.org/browser" i18n_domain="zope">
+ ... <viewlet
+ ... name="stock2"
+ ... permission="zope.Public"
+ ... class="zope.viewlet.directives.Stock"
+ ... weight="8"
+ ... />
+ ... </configure>
+ ... ''', context=context)
+
+ >>> viewlet = zope.component.getMultiAdapter(
+ ... (content, request, view, manager), interfaces.IViewlet,
+ ... name='stock2')
+ >>> viewlet.weight
+ u'8'
+
+
+Error Scenarios
+---------------
+
+Neither the class or template have been specified:
+
+ >>> context = xmlconfig.string('''
+ ... <configure xmlns="http://namespaces.zope.org/browser" i18n_domain="zope">
+ ... <viewlet
+ ... name="testviewlet"
+ ... manager="zope.viewlet.directives.ILeftColumn"
+ ... permission="zope.Public"
+ ... />
+ ... </configure>
+ ... ''', context=context)
+ Traceback (most recent call last):
+ ...
+ ZopeXMLConfigurationError: File "<string>", line 3.2-7.8
+ ConfigurationError: Must specify a class or template
+
+The specified attribute is not ``__call__``, but also a template has been
+specified:
+
+ >>> context = xmlconfig.string('''
+ ... <configure xmlns="http://namespaces.zope.org/browser" i18n_domain="zope">
+ ... <viewlet
+ ... name="testviewlet"
+ ... manager="zope.viewlet.directives.ILeftColumn"
+ ... template="test_viewlet.pt"
+ ... attribute="faux"
+ ... permission="zope.Public"
+ ... />
+ ... </configure>
+ ... ''', context=context)
+ Traceback (most recent call last):
+ ...
+ ZopeXMLConfigurationError: File "<string>", line 3.2-9.8
+ ConfigurationError: Attribute and template cannot be used together.
+
+Now, we are not specifying a template, but a class that does not have the
+specified attribute:
+
+ >>> context = xmlconfig.string('''
+ ... <configure xmlns="http://namespaces.zope.org/browser" i18n_domain="zope">
+ ... <viewlet
+ ... name="testviewlet"
+ ... manager="zope.viewlet.directives.ILeftColumn"
+ ... class="zope.viewlet.directives.Sport"
+ ... attribute="faux"
+ ... permission="zope.Public"
+ ... />
+ ... </configure>
+ ... ''', context=context)
+ Traceback (most recent call last):
+ ...
+ ZopeXMLConfigurationError: File "<string>", line 3.2-9.8
+ ConfigurationError: The provided class doesn't have the specified attribute
+
+
Cleanup
-------
Modified: Zope3/branches/roger-contentprovider/src/zope/viewlet/manager.py
===================================================================
--- Zope3/branches/roger-contentprovider/src/zope/viewlet/manager.py 2005-10-13 01:39:47 UTC (rev 39109)
+++ Zope3/branches/roger-contentprovider/src/zope/viewlet/manager.py 2005-10-13 01:42:20 UTC (rev 39110)
@@ -112,7 +112,11 @@
template = ViewPageTemplateFile(template)
if ViewletManagerBase not in bases:
- bases = bases + (ViewletManagerBase,)
+ # Make sure that we do not get a default viewlet manager mixin, if the
+ # provided base is already a full viewlet manager implementation.
+ if not (len(bases) == 1 and
+ interfaces.IViewletManager.implementedBy(bases[0])):
+ bases = bases + (ViewletManagerBase,)
ViewletManager = type(
'<ViewletManager providing %s>' % interface.getName(),
Modified: Zope3/branches/roger-contentprovider/src/zope/viewlet/metaconfigure.py
===================================================================
--- Zope3/branches/roger-contentprovider/src/zope/viewlet/metaconfigure.py 2005-10-13 01:39:47 UTC (rev 39109)
+++ Zope3/branches/roger-contentprovider/src/zope/viewlet/metaconfigure.py 2005-10-13 01:42:20 UTC (rev 39110)
@@ -30,27 +30,21 @@
from zope.app.publisher.browser import viewmeta
from zope.app.publisher.interfaces.browser import IBrowserView
-#from zope.contentprovider.interfaces import IWeightSupport
-from zope.viewlet import viewlet
-from zope.viewlet import manager
-from zope.viewlet import interfaces
+from zope.viewlet import viewlet, manager, interfaces
+def viewletManagerDirective(
+ _context, name, permission,
+ for_=Interface, layer=IDefaultBrowserLayer, view=IBrowserView,
+ provides=interfaces.IViewletManager, class_=None, template=None,
+ allowed_interface=None, allowed_attributes=None):
-# TODO: remove weight out of viewlet manager directive
-# TODO: support default class_
-# TODO: do we need a allowed_interface ?
-# TODO:
-def viewletManagerDirective(_context, name, permission, providerType,
- for_=Interface, layer=IDefaultBrowserLayer,
- view=IBrowserView,
- class_=None, template=None, allowed_interface=None):
-
+ # A list of attributes available under the provided permission
required = {}
# Get the permission; mainly to correctly handle CheckerPublic.
permission = viewmeta._handle_permission(_context, permission)
- # If class is not given we use the default viewlet manager.
+ # If class is not given we use the basic viewlet manager.
if class_ is None:
class_ = manager.ViewletManagerBase
@@ -64,50 +58,54 @@
# Create a new class based on the template and class.
new_class = manager.ViewletManager(
- providerType, bases=(class_, ))
+ provides, template=template, bases=(class_, ))
+ else:
+ # Create a new class based on the class.
+ new_class = manager.ViewletManager(provides, bases=(class_, ))
- if hasattr(new_class, '__implements__'):
- classImplements(new_class, IBrowserPublisher)
-
- # set providerType if the class_ defines the global attribute
- if hasattr(class_, 'providerType'):
- classImplements(new_class, providerType)
-
- for attr_name in ('browserDefault', '__call__',
- 'publishTraverse'):
+ # Register some generic attributes with the security dictionary
+ for attr_name in ('browserDefault', '__call__', 'publishTraverse'):
required[attr_name] = permission
+ # Register the ``provides`` interface and register fields in the security
+ # dictionary
viewmeta._handle_allowed_interface(
+ _context, (provides,), permission, required)
+
+ # Register the allowed interface and add the field's security entries
+ viewmeta._handle_allowed_interface(
_context, allowed_interface, permission, required)
+ # Register single allowed attributes in the security dictionary
+ viewmeta._handle_allowed_attributes(
+ _context, allowed_attributes, permission, required)
+
+ # Register interfaces
viewmeta._handle_for(_context, for_)
metaconfigure.interface(_context, view)
+ # Create a checker for the viewlet manager
checker.defineChecker(new_class, checker.Checker(required))
- # register viewlet
+ # register a viewlet manager
_context.action(
discriminator = ('viewletManager', for_, layer, view, name),
callable = metaconfigure.handler,
args = ('provideAdapter',
- (for_, layer, view), interfaces.IViewletManager, name,
+ (for_, layer, view), provides, name,
new_class, _context.info),)
+def viewletDirective(
+ _context, name, permission,
+ for_=Interface, layer=IDefaultBrowserLayer, view=IBrowserView,
+ manager=interfaces.IViewletManager, class_=None, template=None,
+ attribute='__call__', allowed_interface=None, allowed_attributes=None,
+ **kwargs):
-# TODO: support None for weight
-def viewletDirective(_context, name, permission, providerType, for_=Interface,
- layer=IDefaultBrowserLayer, view=IBrowserView,
- class_=None, template=None, attribute='__call__',
- weight=None, allowed_interface=None,
- allowed_attributes=None):
-
+ # Security map dictionary
required = {}
- #if interfaces.IWeightSupport.implementedBy(class_) and weight == None:
- # msg = "Must specify a weight if IWeightSupport is implemented"
- # raise ConfigurationError(msg)
-
# Get the permission; mainly to correctly handle CheckerPublic.
permission = viewmeta._handle_permission(_context, permission)
@@ -144,19 +142,17 @@
if template:
# Create a new class for the viewlet template and class.
new_class = viewlet.SimpleViewletClass(
- template, bases=(class_, ), weight=weight)
+ template, bases=(class_, ), attributes=kwargs)
else:
if not hasattr(class_, 'browserDefault'):
- cdict = {
- 'browserDefault':
- lambda self, request: (getattr(self, attribute), ())
- }
+ cdict = {'browserDefault':
+ lambda self, request: (getattr(self, attribute), ())}
else:
cdict = {}
- cdict['_weight'] = weight
cdict['__name__'] = name
cdict['__page_attribute__'] = attribute
+ cdict.update(kwargs)
new_class = type(class_.__name__,
(class_, viewlet.SimpleAttributeViewlet), cdict)
@@ -165,31 +161,31 @@
else:
# Create a new class for the viewlet template alone.
- new_class = viewlet.SimpleViewletClass(
- template, name=name, weight=weight)
+ new_class = viewlet.SimpleViewletClass(template, name=name)
- # set providerType if the class_ defines the global attribute
- if hasattr(new_class, 'providerType'):
- classImplements(new_class, providerType)
-
- for attr_name in (attribute, 'browserDefault', '__call__',
- 'publishTraverse', 'weight'):
- required[attr_name] = permission
-
+ # Set up permission mapping for various accessible attributes
viewmeta._handle_allowed_interface(
_context, allowed_interface, permission, required)
viewmeta._handle_allowed_attributes(
- _context, allowed_interface, permission, required)
+ _context, allowed_attributes, permission, required)
+ viewmeta._handle_allowed_attributes(
+ _context, kwargs.keys(), permission, required)
+ viewmeta._handle_allowed_attributes(
+ _context,
+ (attribute, 'browserDefault', '__call__', 'publishTraverse'),
+ permission, required)
+ # Register the interfaces.
viewmeta._handle_for(_context, for_)
metaconfigure.interface(_context, view)
+ # Create the security checker for the new class
checker.defineChecker(new_class, checker.Checker(required))
# register viewlet
_context.action(
- discriminator = ('viewlet', for_, layer, view, name, providerType),
+ discriminator = ('viewlet', for_, layer, view, manager, name),
callable = metaconfigure.handler,
args = ('provideAdapter',
- (for_, layer, view, providerType), interfaces.IViewlet,
+ (for_, layer, view, manager), interfaces.IViewlet,
name, new_class, _context.info),)
Modified: Zope3/branches/roger-contentprovider/src/zope/viewlet/metadirectives.py
===================================================================
--- Zope3/branches/roger-contentprovider/src/zope/viewlet/metadirectives.py 2005-10-13 01:39:47 UTC (rev 39109)
+++ Zope3/branches/roger-contentprovider/src/zope/viewlet/metadirectives.py 2005-10-13 01:42:20 UTC (rev 39110)
@@ -20,59 +20,88 @@
import zope.configuration.fields
import zope.schema
+from zope.app.i18n import ZopeMessageIDFactory as _
from zope.app.publisher.browser import metadirectives
+from zope.app.publisher.interfaces import browser
+from zope.viewlet import interfaces
-class IViewletManagerDirective(metadirectives.IPagesDirective):
- """A directive to register a new viewlet manager.
- Viewlet manager registrations are very similar to page registrations,
- except that they are additionally qualified by a type where is used for
- lookup viewlets of this type.
+class IContentProvider(metadirectives.IPagesDirective):
+ """A directive to register a simple content provider.
+
+ Content providers are registered by their context (`for` attribute), the
+ request (`layer` attribute) and the view (`view` attribute). They also
+ must provide a name, so that they can be found using the TALES
+ ``provider`` namespace. Other than that, content providers are just like
+ any other views.
"""
- providerType = zope.configuration.fields.GlobalInterface(
- title=u"Viewlet type",
- description=u"The type interface for viewlets.",
- required=True)
+ view = zope.configuration.fields.GlobalObject(
+ title=_("The view the content provider is registered for."),
+ description=_("The view can either be an interface or a class. By "
+ "default the provider is registered for all views, "
+ "the most common case."),
+ required=False,
+ default=browser.IBrowserView)
name = zope.schema.TextLine(
- title=u"The name of the page (view)",
- description=u"""
- The name shows up in URLs/paths. For example 'foo' or
- 'foo.html'. This attribute is required unless you use the
- subdirective 'page' to create sub views. If you do not have
- sub pages, it is common to use an extension for the view name
- such as '.html'. If you do have sub pages and you want to
- provide a view name, you shouldn't use extensions.""",
+ title=_("The name of the content provider."),
+ description=_("The name of the content provider is used in the TALES "
+ "``provider`` namespace to look up the content "
+ "provider."),
required=True)
+
+class ITemplatedContentProvider(IContentProvider):
+ """A directive for registering a content provider that uses a page
+ template to provide its content."""
+
template = zope.configuration.fields.Path(
- title=u"The name of a template that implements the page.",
- description=u"""
- Refers to a file containing a page template (should end in
- extension '.pt' or '.html').""",
+ title=_("Content-generating template."),
+ description=_("Refers to a file containing a page template (should "
+ "end in extension ``.pt`` or ``.html``)."),
required=False)
-class IViewletDirective(metadirectives.IPagesDirective,
- metadirectives.IViewPageSubdirective,
- IViewletManagerDirective):
+class IViewletManagerDirective(ITemplatedContentProvider):
+ """A directive to register a new viewlet manager.
+
+ Viewlet manager registrations are very similar to content provider
+ registrations, since they are just a simple extension of content
+ providers. However, viewlet managers commonly have a specific provided
+ interface, which is used to discriminate the viewlets they are providing.
+ """
+
+ provides = zope.configuration.fields.GlobalInterface(
+ title=_("The interface this viewlet manager provides."),
+ description=_("A viewlet manager can provide an interface, which "
+ "is used to lookup its contained viewlets."),
+ required=False,
+ default=interfaces.IViewletManager,
+ )
+
+
+class IViewletDirective(ITemplatedContentProvider):
"""A directive to register a new viewlet.
- Viewlet registrations are very similar to page registrations, except that
- they are additionally qualified by the region and view they are used for. An
- additional `weight` attribute is specified that is intended to coarsly
- control the order of the viewlets.
+ Viewlets are content providers that can only be displayed inside a viewlet
+ manager. Thus they are additionally discriminated by the manager. Viewlets
+ can rely on the specified viewlet manager interface to provide their
+ content.
+
+ The viewlet directive also supports an undefined set of keyword arguments
+ that are set as attributes on the viewlet after creation. Those attributes
+ can then be used to implement sorting and filtering, for example.
"""
- view = zope.configuration.fields.GlobalInterface(
- title=u"view",
+ manager = zope.configuration.fields.GlobalObject(
+ title=_("view"),
description=u"The interface of the view this viewlet is for. "
u"(default IBrowserView)""",
- required=False)
+ required=False,
+ default=interfaces.IViewletManager)
- weight = zope.schema.Int(
- title=u"weight",
- description=u"Integer key for sorting viewlets in the same region.",
- required=False)
+
+# Arbitrary keys and values are allowed to be passed to the viewlet.
+IViewletDirective.setTaggedValue('keyword_arguments', True)
Modified: Zope3/branches/roger-contentprovider/src/zope/viewlet/tests.py
===================================================================
--- Zope3/branches/roger-contentprovider/src/zope/viewlet/tests.py 2005-10-13 01:39:47 UTC (rev 39109)
+++ Zope3/branches/roger-contentprovider/src/zope/viewlet/tests.py 2005-10-13 01:42:20 UTC (rev 39110)
@@ -24,30 +24,10 @@
from zope.testing.doctestunit import DocTestSuite, DocFileSuite
from zope.app.testing import setup
-from zope.viewlet import interfaces
-
-
class TestParticipation(object):
principal = 'foobar'
interaction = None
-
-class ILeftViewlet(interfaces.IViewlet):
- """Test viewlet type."""
-
-
-class TestViewlet(object):
-
- def doSomething(self):
- return u'something'
-
-
-class TestViewlet2(object):
-
- def __call__(self):
- return u'called'
-
-
def setUp(test):
setup.placefulSetUp()
@@ -57,11 +37,19 @@
zope.security.management.getInteraction().add(TestParticipation())
+def directivesSetUp(test):
+ setUp(test)
+ setup.setUpTestAsModule(test, 'zope.viewlet.directives')
+
def tearDown(test):
setup.placefulTearDown()
+def directivesTearDown(test):
+ tearDown(test)
+ setup.tearDownTestAsModule(test)
+
def test_suite():
return unittest.TestSuite((
DocFileSuite('README.txt',
@@ -69,7 +57,7 @@
optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
),
DocFileSuite('directives.txt',
- setUp=setUp, tearDown=tearDown,
+ setUp=directivesSetUp, tearDown=directivesTearDown,
optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
),
))
Modified: Zope3/branches/roger-contentprovider/src/zope/viewlet/viewlet.py
===================================================================
--- Zope3/branches/roger-contentprovider/src/zope/viewlet/viewlet.py 2005-10-13 01:39:47 UTC (rev 39109)
+++ Zope3/branches/roger-contentprovider/src/zope/viewlet/viewlet.py 2005-10-13 01:42:20 UTC (rev 39110)
@@ -44,20 +44,11 @@
zope.interface.implements(interfaces.IViewlet)
- _weight = 0
-
def __init__(self, context, request, view, providerType):
super(SimpleViewlet, self).__init__(context, request)
self.view = view
- def _getWeight (self):
- """The weight of the viewlet."""
- return self._weight
- # See zope.app.viewlet.interfaces.IViewlet
- weight = property(_getWeight)
-
-
class SimpleAttributeViewlet(SimpleViewlet):
def publishTraverse(self, request, name):
@@ -75,7 +66,8 @@
return meth(*args, **kw)
-def SimpleViewletClass(template, offering=None, bases=(), name=u'', weight=0):
+def SimpleViewletClass(template, offering=None, bases=(), attributes=None,
+ name=u''):
# Get the current frame
if offering is None:
offering = sys._getframe(1).f_globals
@@ -83,11 +75,13 @@
# Create the base class hierarchy
bases += (SimpleViewlet, simple)
+ attrs = {'index' : ViewletPageTemplateFile(template, offering),
+ '__name__' : name}
+ if attributes:
+ attrs.update(attributes)
+
# Generate a derived view class.
- class_ = type("SimpleViewletClass from %s" % template, bases,
- {'index' : ViewletPageTemplateFile(template, offering),
- '_weight' : weight,
- '__name__' : name})
+ class_ = type("SimpleViewletClass from %s" % template, bases, attrs)
return class_
More information about the Zope3-Checkins
mailing list