[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