[Zope3-checkins]
SVN: Zope3/branches/roger-contentprovider/src/zope/portlet/
Added initial portlet meta directive
Roger Ineichen
roger at projekt01.ch
Sat Oct 8 07:13:17 EDT 2005
Log message for revision 38936:
Added initial portlet meta directive
--> work in progress
Changed:
U Zope3/branches/roger-contentprovider/src/zope/portlet/__init__.py
A Zope3/branches/roger-contentprovider/src/zope/portlet/browser/
A Zope3/branches/roger-contentprovider/src/zope/portlet/browser/__init__.py
A Zope3/branches/roger-contentprovider/src/zope/portlet/directives.txt
A Zope3/branches/roger-contentprovider/src/zope/portlet/meta.zcml
A Zope3/branches/roger-contentprovider/src/zope/portlet/metaconfigure.py
A Zope3/branches/roger-contentprovider/src/zope/portlet/metadirectives.py
U Zope3/branches/roger-contentprovider/src/zope/portlet/portlet.py
U Zope3/branches/roger-contentprovider/src/zope/portlet/tests/test_doc.py
A Zope3/branches/roger-contentprovider/src/zope/portlet/tests/test_portlet.pt
A Zope3/branches/roger-contentprovider/src/zope/portlet/zope.portlet-meta.zcml
-=-
Modified: Zope3/branches/roger-contentprovider/src/zope/portlet/__init__.py
===================================================================
--- Zope3/branches/roger-contentprovider/src/zope/portlet/__init__.py 2005-10-08 11:06:04 UTC (rev 38935)
+++ Zope3/branches/roger-contentprovider/src/zope/portlet/__init__.py 2005-10-08 11:13:16 UTC (rev 38936)
@@ -11,7 +11,7 @@
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
-"""Viewlet exceptions
+"""Portlet
$Id$
"""
Added: Zope3/branches/roger-contentprovider/src/zope/portlet/browser/__init__.py
===================================================================
--- Zope3/branches/roger-contentprovider/src/zope/portlet/browser/__init__.py 2005-10-08 11:06:04 UTC (rev 38935)
+++ Zope3/branches/roger-contentprovider/src/zope/portlet/browser/__init__.py 2005-10-08 11:13:16 UTC (rev 38936)
@@ -0,0 +1,17 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Protlet views
+
+$Id:$
+"""
Property changes on: Zope3/branches/roger-contentprovider/src/zope/portlet/browser/__init__.py
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Added: Zope3/branches/roger-contentprovider/src/zope/portlet/directives.txt
===================================================================
--- Zope3/branches/roger-contentprovider/src/zope/portlet/directives.txt 2005-10-08 11:06:04 UTC (rev 38935)
+++ Zope3/branches/roger-contentprovider/src/zope/portlet/directives.txt 2005-10-08 11:13:16 UTC (rev 38936)
@@ -0,0 +1,183 @@
+=========================
+The ``portlet`` Directive
+=========================
+
+The portlet directive allows you to quickly register a new portlet without much
+hassle, like it was shown in the `README.txt` file. Here is a sample
+directive::
+
+ >>> from zope.configuration import xmlconfig
+ >>> context = xmlconfig.string('''
+ ... <configure i18n_domain="zope">
+ ... <include package="zope.portlet" file="meta.zcml" />
+ ... </configure>
+ ... ''')
+
+ >>> context = xmlconfig.string('''
+ ... <configure xmlns="http://namespaces.zope.org/browser" i18n_domain="zope"
+ ... package="zope.portlet.tests">
+ ... <portlet
+ ... name="testportlet"
+ ... for="*"
+ ... viewletType=".test_doc.ITestRegion"
+ ... template="test_portlet.pt"
+ ... permission="zope.Public"
+ ... />
+ ... </configure>
+ ... ''', context=context)
+
+As you can see, the directive looks very similar to the page directive and you
+are right. The portlet directive does not permit you to specify a `menu` and
+`title`, since it is not sensible to have a menu item for a portlet. However,
+it does support two more qualifying attributes, `view` and `region`. While view
+is nearly never specified (very common default), the `region` attribute *must*
+be specified. An optional `weight` attribute (not shown above) allows you to
+change the position of a particular portlet relative to the others. The
+default value is zero.
+
+If we now look into the adapter registry, we will find the portlet:
+
+ >>> class Content(object):
+ ... pass
+ >>> content = Content()
+
+ >>> from zope.publisher.browser import TestRequest
+ >>> request = TestRequest()
+
+ >>> from zope.app.publisher.browser import BrowserView
+ >>> view = BrowserView(content, request)
+
+ >>> import zope.interface
+ >>> from zope.portlet.tests.test_doc import ITestRegion
+
+ >>> import zope.component
+ >>> from zope.portlet.interfaces import IPortlet
+ >>> portlet = zope.component.getMultiAdapter(
+ ... (content, request, view), ITestRegion, name='testportlet')
+ >>> portlet()
+ u'<div>testportlet macro content</div>\n'
+
+Let's now ensure that we can also specify a portlet class:
+
+ >>> context = xmlconfig.string('''
+ ... <configure xmlns="http://namespaces.zope.org/browser" i18n_domain="zope"
+ ... package="zope.portlet.tests">
+ ... <portlet
+ ... name="testportlet2"
+ ... for="*"
+ ... viewletType=".test_doc.ITestRegion"
+ ... template="test_portlet.pt"
+ ... class=".test_doc.TestPortlet"
+ ... permission="zope.Public"
+ ... />
+ ... </configure>
+ ... ''', context=context)
+
+ >>> portlet = zope.component.getMultiAdapter(
+ ... (content, request, view), ITestRegion, name='testportlet2')
+ >>> portlet()
+ u'<div>testportlet macro content</div>\n'
+
+Okay, so the template-driven cases wrok. But just specifying a class should
+also work:
+
+ >>> context = xmlconfig.string('''
+ ... <configure xmlns="http://namespaces.zope.org/browser" i18n_domain="zope"
+ ... package="zope.portlet.tests">
+ ... <portlet
+ ... name="testportlet3"
+ ... for="*"
+ ... viewletType=".test_doc.ITestRegion"
+ ... class=".test_doc.TestPortlet2"
+ ... permission="zope.Public"
+ ... />
+ ... </configure>
+ ... ''', context=context)
+
+ >>> portlet = zope.component.getMultiAdapter(
+ ... (content, request, view), ITestRegion, name='testportlet3')
+ >>> portlet()
+ u'called'
+
+It should also be possible to specify an alternative attribute of the class to
+be rendered upon calling the portlet:
+
+ >>> context = xmlconfig.string('''
+ ... <configure xmlns="http://namespaces.zope.org/browser" i18n_domain="zope"
+ ... package="zope.portlet.tests">
+ ... <portlet
+ ... name="testportlet4"
+ ... for="*"
+ ... viewletType=".test_doc.ITestRegion"
+ ... class=".test_doc.TestPortlet"
+ ... attribute="doSomething"
+ ... permission="zope.Public"
+ ... />
+ ... </configure>
+ ... ''', context=context)
+
+ >>> portlet = zope.component.getMultiAdapter(
+ ... (content, request, view), ITestRegion, name='testportlet4')
+ >>> portlet()
+ 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.portlet.tests">
+ ... <portlet
+ ... name="testportlet"
+ ... viewletType=".test_doc.ITestRegion"
+ ... 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.portlet.tests">
+ ... <portlet
+ ... name="testportlet"
+ ... viewletType=".test_doc.ITestRegion"
+ ... template="test_portlet.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.portlet.tests">
+ ... <portlet
+ ... name="testportlet"
+ ... viewletType=".test_doc.ITestRegion"
+ ... class=".test_doc.TestPortlet"
+ ... 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
Property changes on: Zope3/branches/roger-contentprovider/src/zope/portlet/directives.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: Zope3/branches/roger-contentprovider/src/zope/portlet/meta.zcml
===================================================================
--- Zope3/branches/roger-contentprovider/src/zope/portlet/meta.zcml 2005-10-08 11:06:04 UTC (rev 38935)
+++ Zope3/branches/roger-contentprovider/src/zope/portlet/meta.zcml 2005-10-08 11:13:16 UTC (rev 38936)
@@ -0,0 +1,14 @@
+<configure
+ xmlns:meta="http://namespaces.zope.org/meta">
+
+ <meta:directives namespace="http://namespaces.zope.org/browser">
+
+ <meta:directive
+ name="portlet"
+ schema=".metadirectives.IPortletDirective"
+ handler=".metaconfigure.portletDirective"
+ />
+
+ </meta:directives>
+
+</configure>
Property changes on: Zope3/branches/roger-contentprovider/src/zope/portlet/meta.zcml
___________________________________________________________________
Name: svn:eol-style
+ native
Added: Zope3/branches/roger-contentprovider/src/zope/portlet/metaconfigure.py
===================================================================
--- Zope3/branches/roger-contentprovider/src/zope/portlet/metaconfigure.py 2005-10-08 11:06:04 UTC (rev 38935)
+++ Zope3/branches/roger-contentprovider/src/zope/portlet/metaconfigure.py 2005-10-08 11:13:16 UTC (rev 38936)
@@ -0,0 +1,130 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Viewlet metadconfigure
+
+$Id$
+"""
+__docformat__ = 'restructuredtext'
+
+import os
+
+from zope.security import checker
+
+from zope.configuration.exceptions import ConfigurationError
+from zope.interface import Interface, classImplements
+from zope.publisher.interfaces.browser import IDefaultBrowserLayer
+
+from zope.app.component.interface import provideInterface
+from zope.app.component import metaconfigure
+from zope.app.publisher.browser import viewmeta
+from zope.app.publisher.interfaces.browser import IBrowserView
+
+#from zope.contentprovider.interfaces import IRegion
+from zope.portlet import portlet
+from zope.portlet import interfaces
+
+
+def portletDirective(_context, name, permission, viewletType,
+ for_=Interface, layer=IDefaultBrowserLayer,
+ view=IBrowserView,
+ class_=None, template=None, attribute='__call__', weight=0,
+ allowed_interface=None, allowed_attributes=None):
+
+ required = {}
+
+ # Get the permission; mainly to correctly handle CheckerPublic.
+ permission = viewmeta._handle_permission(_context, permission)
+
+ # Either the class or template must be specified.
+ if not (class_ or template):
+ raise ConfigurationError("Must specify a class or template")
+
+ # Make sure that all the non-default attribute specifications are correct.
+ if attribute != '__call__':
+ if template:
+ raise ConfigurationError(
+ "Attribute and template cannot be used together.")
+
+ # Note: The previous logic forbids this condition to evere occur.
+ if not class_:
+ raise ConfigurationError(
+ "A class must be provided if attribute is used")
+
+ # Make sure that the template exists and that all low-level API methods
+ # have the right permission.
+ if template:
+ template = os.path.abspath(str(_context.path(template)))
+ if not os.path.isfile(template):
+ raise ConfigurationError("No such file", template)
+ required['__getitem__'] = permission
+
+ # Make sure the has the right form, if specified.
+ if class_:
+ if attribute != '__call__':
+ if not hasattr(class_, attribute):
+ raise ConfigurationError(
+ "The provided class doesn't have the specified attribute "
+ )
+ if template:
+ # Create a new class for the portlet template and class.
+ new_class = portlet.SimplePortletClass(
+ template, bases=(class_, ), weight=weight)
+ else:
+ if not hasattr(class_, 'browserDefault'):
+ cdict = {
+ 'browserDefault':
+ lambda self, request: (getattr(self, attribute), ())
+ }
+ else:
+ cdict = {}
+
+ cdict['_weight'] = weight
+ cdict['__name__'] = name
+ cdict['__page_attribute__'] = attribute
+ new_class = type(class_.__name__,
+ (class_, portlet.SimpleAttributePortlet), cdict)
+
+ if hasattr(class_, '__implements__'):
+ classImplements(new_class, IBrowserPublisher)
+
+ else:
+ # Create a new class for the portlet template alone.
+ new_class = portlet.SimplePortletClass(
+ template, name=name, weight=weight)
+
+ # Make sure the new class implements the type
+ classImplements(new_class, viewletType)
+
+ for attr_name in (attribute, 'browserDefault', '__call__',
+ 'publishTraverse', 'weight'):
+ required[attr_name] = permission
+
+ viewmeta._handle_allowed_interface(
+ _context, allowed_interface, permission, required)
+ viewmeta._handle_allowed_attributes(
+ _context, allowed_interface, permission, required)
+
+ viewmeta._handle_for(_context, for_)
+ metaconfigure.interface(_context, view)
+# metaconfigure.interface(_context, type, IRegion)
+
+ checker.defineChecker(new_class, checker.Checker(required))
+
+ # register portlet
+ _context.action(
+ discriminator = ('portlet', for_, layer, view, viewletType, name),
+ callable = metaconfigure.handler,
+ args = ('provideAdapter',
+ (for_, layer, view), viewletType, name, new_class,
+ _context.info),)
Property changes on: Zope3/branches/roger-contentprovider/src/zope/portlet/metaconfigure.py
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Added: Zope3/branches/roger-contentprovider/src/zope/portlet/metadirectives.py
===================================================================
--- Zope3/branches/roger-contentprovider/src/zope/portlet/metadirectives.py 2005-10-08 11:06:04 UTC (rev 38935)
+++ Zope3/branches/roger-contentprovider/src/zope/portlet/metadirectives.py 2005-10-08 11:13:16 UTC (rev 38936)
@@ -0,0 +1,50 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Viewlet metadirective
+
+$Id$
+"""
+__docformat__ = 'restructuredtext'
+
+from zope.configuration.fields import GlobalInterface
+from zope.schema import Int
+
+from zope.app.publisher.browser import metadirectives
+
+
+class IPortletDirective(metadirectives.IPagesDirective,
+ metadirectives.IViewPageSubdirective):
+ """A directive to register a new portlet.
+
+ Portlet registrations are very similar to page registrations, except that
+ they are additionally qualified by the type and view they are used for. An
+ additional `weight` attribute is specified that is intended to coarsly
+ control the order of the portlets.
+ """
+
+ viewletType = GlobalInterface(
+ title=u"type",
+ description=u"The type interface of this portlet.",
+ required=True)
+
+ view = GlobalInterface(
+ title=u"view",
+ description=u"The interface of the view this portlet is for. "
+ u"(default IBrowserView)""",
+ required=False)
+
+ weight = Int(
+ title=u"weight",
+ description=u"Integer key for sorting portlet in the same region.",
+ required=False)
Property changes on: Zope3/branches/roger-contentprovider/src/zope/portlet/metadirectives.py
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Modified: Zope3/branches/roger-contentprovider/src/zope/portlet/portlet.py
===================================================================
--- Zope3/branches/roger-contentprovider/src/zope/portlet/portlet.py 2005-10-08 11:06:04 UTC (rev 38935)
+++ Zope3/branches/roger-contentprovider/src/zope/portlet/portlet.py 2005-10-08 11:13:16 UTC (rev 38936)
@@ -17,14 +17,48 @@
"""
__docformat__ = 'restructuredtext'
+import sys
import zope.interface
-#from zope.viewlet.viewlet import ViewletManager
-from zope.portlet.interfaces import IPortletManager
+from zope.viewlet.viewlet import ViewletPageTemplateFile
+from zope.viewlet.viewlet import SimpleAttributeViewlet
+from zope.viewlet.viewlet import SimpleViewlet
+from zope.portlet import interfaces
+from zope.app.pagetemplate.simpleviewclass import simple
-#class DefaultPortletManager(ViewletManager):
+
+
class DefaultPortletManager(object):
"""Default portlet manager."""
- zope.interface.implements(IPortletManager)
+ zope.interface.implements(interfaces.IPortletManager)
+
+class SimplePortlet(SimpleViewlet):
+ """Portlet adapter class used in meta directive as a mixin class."""
+
+ zope.interface.implements(interfaces.IPortlet)
+
+ def __init__(self, context, request, view):
+ super(SimplePortlet, self).__init__(context, request, view)
+
+
+class SimpleAttributePortlet(SimpleAttributeViewlet):
+ """Simple attribute based portlet."""
+
+
+def SimplePortletClass(template, offering=None, bases=(), name=u'', weight=0):
+ # Get the current frame
+ if offering is None:
+ offering = sys._getframe(1).f_globals
+
+ # Create the base class hierarchy
+ bases += (SimplePortlet, simple)
+
+ # Generate a derived view class.
+ class_ = type("SimplePortletClass from %s" % template, bases,
+ {'index' : ViewletPageTemplateFile(template, offering),
+ '_weight' : weight,
+ '__name__' : name})
+
+ return class_
Modified: Zope3/branches/roger-contentprovider/src/zope/portlet/tests/test_doc.py
===================================================================
--- Zope3/branches/roger-contentprovider/src/zope/portlet/tests/test_doc.py 2005-10-08 11:06:04 UTC (rev 38935)
+++ Zope3/branches/roger-contentprovider/src/zope/portlet/tests/test_doc.py 2005-10-08 11:13:16 UTC (rev 38936)
@@ -24,7 +24,26 @@
from zope.testing.doctestunit import DocTestSuite, DocFileSuite
from zope.app.testing import setup
+from zope.contentprovider.interfaces import IRegion
+
+class TestPortlet(object):
+
+ def doSomething(self):
+ return u'something'
+
+
+class TestPortlet2(object):
+
+ def __call__(self):
+ return u'called'
+
+
+class ITestRegion(zope.interface.Interface):
+ """A region for testing purposes."""
+zope.interface.directlyProvides(ITestRegion, IRegion)
+
+
class TestParticipation(object):
principal = 'foobar'
interaction = None
@@ -50,10 +69,10 @@
setUp=setUp, tearDown=tearDown,
optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
),
-# DocFileSuite('../directives.txt',
-# setUp=setUp, tearDown=tearDown,
-# optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
-# ),
+ DocFileSuite('../directives.txt',
+ setUp=setUp, tearDown=tearDown,
+ optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+ ),
))
if __name__ == '__main__':
Added: Zope3/branches/roger-contentprovider/src/zope/portlet/tests/test_portlet.pt
===================================================================
--- Zope3/branches/roger-contentprovider/src/zope/portlet/tests/test_portlet.pt 2005-10-08 11:06:04 UTC (rev 38935)
+++ Zope3/branches/roger-contentprovider/src/zope/portlet/tests/test_portlet.pt 2005-10-08 11:13:16 UTC (rev 38936)
@@ -0,0 +1 @@
+<div>testportlet macro content</div>
Property changes on: Zope3/branches/roger-contentprovider/src/zope/portlet/tests/test_portlet.pt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: Zope3/branches/roger-contentprovider/src/zope/portlet/zope.portlet-meta.zcml
===================================================================
--- Zope3/branches/roger-contentprovider/src/zope/portlet/zope.portlet-meta.zcml 2005-10-08 11:06:04 UTC (rev 38935)
+++ Zope3/branches/roger-contentprovider/src/zope/portlet/zope.portlet-meta.zcml 2005-10-08 11:13:16 UTC (rev 38936)
@@ -0,0 +1 @@
+<include package="zope.viewlet" file="meta.zcml" />
Property changes on: Zope3/branches/roger-contentprovider/src/zope/portlet/zope.portlet-meta.zcml
___________________________________________________________________
Name: svn:eol-style
+ native
More information about the Zope3-Checkins
mailing list