[Zope3-checkins] SVN: Zope3/branches/roger-contentprovider/src/zope/contentprovider/ Initial stab at the final rewrite of content providers. I think we really

Stephan Richter srichter at cosmos.phy.tufts.edu
Sat Oct 8 06:33:52 EDT 2005


Log message for revision 38932:
  Initial stab at the final rewrite of content providers. I think we really 
  got it right this time. (Thanks to a late night session with Roger after 
  some good German food and beer -- not for me of course.)
  
  

Changed:
  U   Zope3/branches/roger-contentprovider/src/zope/contentprovider/README.txt
  U   Zope3/branches/roger-contentprovider/src/zope/contentprovider/interfaces.py
  D   Zope3/branches/roger-contentprovider/src/zope/contentprovider/manager.py
  U   Zope3/branches/roger-contentprovider/src/zope/contentprovider/tales.py
  D   Zope3/branches/roger-contentprovider/src/zope/contentprovider/tests/
  A   Zope3/branches/roger-contentprovider/src/zope/contentprovider/tests.py

-=-
Modified: Zope3/branches/roger-contentprovider/src/zope/contentprovider/README.txt
===================================================================
--- Zope3/branches/roger-contentprovider/src/zope/contentprovider/README.txt	2005-10-08 10:27:45 UTC (rev 38931)
+++ Zope3/branches/roger-contentprovider/src/zope/contentprovider/README.txt	2005-10-08 10:33:52 UTC (rev 38932)
@@ -4,87 +4,77 @@
 
 This package provides a framework to develop componentized Web GUI
 applications. Instead of describing the content of a page using a single
-template or static system of templates and METAL macros, page regions can be
-defined and are filled dynamically with content based on the setup
-of the application.
+template or static system of templates and METAL macros, content provider
+objects can be created that are dynamically looked up based on the
+setup/configuration of the application.
 
+  >>> from zope.contentprovider import interfaces
 
-Getting Started
----------------
 
-Let's say we have simple two-column page. In the smaller left column, there
-are boxes filled with various pieces of information, such as news, latest
-software releases, etc. This content, however, generally changes depending on
-the view and the object being viewed.
+Content Providers
+-----------------
 
+Content Providers is a term from the Java world that refers to components that
+can provide HTML content. It means nothing more! How the content is found and
+returned is totally up to the implementation. The Zope 3 touch to the concept
+is that content providers are multi-adapters that are looked up by the
+context, request (and thus the layer/skin), and view they are displayed in.
 
-Regions
-~~~~~~~
+So let's create a simple content provider:
 
-Instead of hard-coding the pieces of content in the left column in the page
-template or using macros, we simply define a region for it. Regions are
-interfaces that act as content placeholders. Here is a common setup:
-
   >>> import zope.interface
-  >>> class ILeftColumn(zope.interface.Interface):
-  ...     '''The left column of a Web site.'''
-
-  >>> from zope.contentprovider import interfaces
-  >>> zope.interface.directlyProvides(ILeftColumn, interfaces.IRegion)
-
   >>> import zope.component
-  >>> zope.component.provideUtility(ILeftColumn, interfaces.IRegion,
-  ...                               'webpage.LeftColumn')
+  >>> from zope.publisher.interfaces import browser
 
-It is important that the region provides the ``IRegion``
-interface and that it is registered as a utility providing
-``IRegion``. If omitted, the framework will be unable to find the
-region later.
+  >>> class MessageBox(object):
+  ...     zope.interface.implements(interfaces.IContentProvider)
+  ...     zope.component.adapts=(zope.interface.Interface,
+  ...                            browser.IDefaultBrowserLayer,
+  ...                            zope.interface.Interface)
+  ...     message = u'My Message'
+  ...
+  ...     def __init__(self, context, request, view):
+  ...         pass
+  ...
+  ...     def __call__(self):
+  ...         return u'<div class="box">%s</div>' %self.message
+  ...
+  ...     def __repr__(self):
+  ...         return 'MessageBox(%s)' %self.message.encode('utf-8')
 
+The interface requires us to make the content provider callable. We can now
+instantiate the content provider (manually) and render it:
 
-Content Providers
-~~~~~~~~~~~~~~~~~
+  >>> box = MessageBox(None, None, None)
+  >>> box()
+  u'<div class="box">My Message</div>'
 
-Content providers provide snippets of content that can be placed into a region,
-such as the one defined above. Content providers are qualified not only by
-the context object and the request, but also the view they appear in. Also,
-the content provider  must *provide* the region interface
-it is filling; we will demonstrate a more advanced example later, where the
-purpose of this requirement becomes clear.
+Since our content provider did not require the context, request or view to
+create its HTML content, we were able to pass trivial dummy values into the
+constructor.
 
-A typical kind of a content provider is a viewlet, so we'll use simple
-viewlets for the following examples.
+I agree, this functionally does not seem very useful now. The constructor
+seems useless and the returned content is totally static. However, we
+implemented a contract for content providers that other code can rely
+on. Content providers are (commonly) instantiated using the context, request
+and view they appear in and are required to always generate its HTML using
+those three components.
 
-  >>> class Viewlet(object):
-  ...     zope.interface.implements(interfaces.IContentProvider)
-  ...     def __init__(self, context, request,view):
-  ...          pass
-  ...     title = 'Demo Viewlet'
-  ...     weight = 1
-  ...     def __call__(self, *args, **kw):
-  ...         return 'viewlet content'
 
-  # Generate a viewlet checker
-  >>> from zope.security.checker import NamesChecker, defineChecker
-  >>> viewletChecker = NamesChecker(('__call__', 'weight', 'title',))
-  >>> defineChecker(Viewlet, viewletChecker)
-  
-  # Register the viewlet with component architecture
-  >>> from zope.publisher.interfaces.browser import IDefaultBrowserLayer
-  >>> from zope.app.publisher.interfaces.browser import IBrowserView
-  >>> zope.component.provideAdapter(
-  ...     Viewlet,
-  ...     (zope.interface.Interface, IDefaultBrowserLayer, IBrowserView),
-  ...     ILeftColumn,
-  ...     name='viewlet')
+The TALES ``provider`` Expression
+---------------------------------
 
+The ``provider`` expression will look up the name of the content provider,
+call it and return the HTML content. The first step, however, will be to
+register our content provider with the component architecture:
 
-Creating the View
-~~~~~~~~~~~~~~~~~
+  >>> zope.component.provideAdapter(MessageBox, name='mypage.MessageBox')
 
-Now that we have a region with a viewlet registered for it, let's use it by
-creating the front page of our Web Site:
+The content provider must be registered by name, since the TALES expression
+uses the name to look up the provider at run time.
 
+Let's now create a view using a page template:
+
   >>> import os, tempfile
   >>> temp_dir = tempfile.mkdtemp()
   >>> templateFileName = os.path.join(temp_dir, 'template.pt')
@@ -93,10 +83,7 @@
   ...   <body>
   ...     <h1>My Web Page</h1>
   ...     <div class="left-column">
-  ...       <div class="column-item"
-  ...            tal:repeat="viewlet providers:webpage.LeftColumn">
-  ...         <tal:block replace="structure viewlet" />
-  ...       </div>
+  ...       <tal:block replace="structure provider:mypage.MessageBox">
   ...     </div>
   ...     <div class="main">
   ...       Content here
@@ -105,8 +92,11 @@
   ... </html>
   ... ''')
 
-and registering it as a view (browser page) for all objects:
+As you can see, we exprect the ``provider`` expression to simply look up the
+content provider and insert the HTML content at this place.
 
+Next we register the template as a view (browser page) for all objects:
+
   >>> from zope.app.pagetemplate.simpleviewclass import SimpleViewClass
   >>> FrontPage = SimpleViewClass(templateFileName, name='main.html')
 
@@ -116,12 +106,6 @@
   ...     zope.interface.Interface,
   ...     name='main.html')
 
-That is all of the setup. Let's now render the view.
-
-
-Using the View
-~~~~~~~~~~~~~~
-
 Let's create a content object that can be viewed:
 
   >>> class Content(object):
@@ -142,7 +126,7 @@
       <h1>My Web Page</h1>
       <div class="left-column">
         <div class="column-item">
-          viewlet content
+          <div class="box">My Message</div>
         </div>
       </div>
       <div class="main">
@@ -152,464 +136,13 @@
   </html>
 
 
-More than one View
-~~~~~~~~~~~~~~~~~~
-
-  >>> class InfoViewlet(object):
-  ...     zope.interface.implements(interfaces.IContentProvider)
-  ...     def __init__(self, context, request,view):
-  ...         pass
-  ...     title = 'Info Viewlet'
-  ...     weight = 3
-  ...     def __call__(self, *args, **kw):
-  ...         return 'viewlet information'
-
-  >>> defineChecker(InfoViewlet, viewletChecker)
-
-  >>> zope.component.provideAdapter(
-  ...     InfoViewlet,
-  ...     (zope.interface.Interface, IDefaultBrowserLayer, IBrowserView),
-  ...     ILeftColumn,
-  ...     name='infoViewlet')
-
-When we now render the view, the content of our info viewlet appears as well:
-
-  >>> print view().strip()
-  <html>
-    <body>
-      <h1>My Web Page</h1>
-      <div class="left-column">
-        <div class="column-item">
-            viewlet content
-        </div>
-        <div class="column-item">
-            viewlet information
-        </div>
-      </div>
-      <div class="main">
-        Content here
-      </div>
-    </body>
-  </html>
-
-
-Changing the Weight
-~~~~~~~~~~~~~~~~~~~
-
-Let's ensure that the weight really affects the order of the content providers.
-If we change the weights around,
-
-  >>> InfoViewlet.weight = 0
-  >>> Viewlet.weight = 1
-
-the order of the left column in the page template should change:
-
-  >>> print view().strip()
-  <html>
-    <body>
-      <h1>My Web Page</h1>
-      <div class="left-column">
-        <div class="column-item">
-            viewlet information
-        </div>
-        <div class="column-item">
-            viewlet content
-        </div>
-      </div>
-      <div class="main">
-        Content here
-      </div>
-    </body>
-  </html>
-
-
-Looking Up a Viewlet by Name
-----------------------------
-
-In some cases you want to be able to look up a particular viewlet for a region,
-given a context and a view. For this use case, you can simply use a second
-TALES namespace called ``viewlet`` that selects the viewlet using the
-expression ``<path to region>/<viewlet name>``.
-
-Since everything else is already set up, we can simply register a new view:
-
-  >>> template2FileName = os.path.join(temp_dir, 'template2.pt')
-  >>> open(template2FileName, 'w').write('''
-  ... <html>
-  ...   <body>
-  ...     <h1>My Web Page - Take 2</h1>
-  ...     <div class="left-column">
-  ...       <div class="column-item">
-  ...         <tal:block
-  ...           replace="structure provider:webpage.LeftColumn/viewlet" />
-  ...       </div>
-  ...     </div>
-  ...   </body>
-  ... </html>
-  ... ''')
-
-  >>> SecondPage = SimpleViewClass(template2FileName, name='second.html')
-  >>> zope.component.provideAdapter(
-  ...     SecondPage,
-  ...     (zope.interface.Interface, IDefaultBrowserLayer),
-  ...     ILeftColumn,
-  ...     name='second.html')
-
-  >>> view = zope.component.getMultiAdapter((content, request),
-  ...                                       name='second.html')
-  >>> print view().strip()
-  <html>
-    <body>
-      <h1>My Web Page - Take 2</h1>
-      <div class="left-column">
-        <div class="column-item">
-            viewlet content
-        </div>
-      </div>
-    </body>
-  </html>
-
-Note that this namespace returns the rendered viewlet and not the viewlet
-view, like the ``viewlets`` TALES namespace.
-
-
-Region Schemas
---------------
-
-In some use cases you want to be able to provide variables to a viewlet that
-cannot be accessed via the view class or the context object. They are usually
-variables that are defined by the view template. Since we do not just want all
-of the view's template variables to be available (because it would be implicit
-and not all viewlets must be called from within page templates), we must
-specify the variables that the environment of the viewlet provides in the slot
-interface as fields.
-
-Let's say in your view you want to display a list of objects and you would
-like to allow various columns that are controlled by viewlets:
-
-  >>> class ObjectItems(object):
-  ...
-  ...     def objectInfo(self):
-  ...         return [{'name': 'README.txt', 'size': '1.2kB'},
-  ...                 {'name': 'logo.png', 'size': '100 x 100'}]
-
-  >>> contentsFileName = os.path.join(temp_dir, 'items.pt')
-  >>> open(contentsFileName, 'w').write('''
-  ... <html>
-  ...   <body>
-  ...     <h1>Contents</h1>
-  ...     <table>
-  ...       <tr tal:repeat="item view/objectInfo">
-  ...         <td tal:repeat="column providers:webpage.ObjectInfoColumn"
-  ...             tal:content="structure column" />
-  ...       </tr>
-  ...     </table>
-  ...   </body>
-  ... </html>
-  ... ''')
-
-  >>> Contents = SimpleViewClass(contentsFileName, bases=(ObjectItems,),
-  ...                            name='contents.html')
-
-  >>> zope.component.provideAdapter(
-  ...     Contents,
-  ...     (zope.interface.Interface, IDefaultBrowserLayer),
-  ...     zope.interface.Interface,
-  ...     name='contents.html')
-
-As you can see from the page template code, in order for the viewlets to be
-of any use, they need access to the ``item`` variable as defined in the page
-template. Thus, the region definition will state that the viewlet must have
-access to a variable called ``item`` that contains the value of ``item`` in
-the page template:
-
-  >>> import zope.schema
-  >>> class IObjectInfoColumn(zope.interface.Interface):
-  ...     '''Place holder for the columns of a contents view.'''
-  ...
-  ...     item = zope.schema.Dict(
-  ...         title=u'Object info dictionary',
-  ...         required=True)
-
-  >>> zope.interface.directlyProvides(
-  ...     IObjectInfoColumn, interfaces.IRegion)
-
-  >>> zope.component.provideUtility(
-  ...     IObjectInfoColumn, interfaces.IRegion,
-  ...     'webpage.ObjectInfoColumn')
-
-
-Next we implement two very simple viewlets, one displaying the name
-
-  >>> from zope.app.publisher.browser import BrowserView
-  
-  >>> class NameColumnViewlet(object):
-  ...     zope.interface.implements(IObjectInfoColumn)
-  ...     weight = 0
-  ...
-  ...     def __init__(self, context, request, view):
-  ...         self.view = view
-  ...
-  ...     def __call__(self):
-  ...         return '<b>' + self.item['name'] + '</b>'
-
-  >>> defineChecker(NameColumnViewlet, viewletChecker)
-
-  >>> zope.component.provideAdapter(
-  ...     NameColumnViewlet,
-  ...     (zope.interface.Interface, IDefaultBrowserLayer, IBrowserView),
-  ...     IObjectInfoColumn,
-  ...     name='name')
-
-... and the other displaying the size of the of objects in the list:
-
-  >>> class SizeColumnViewlet(BrowserView):
-  ...     zope.interface.implements(IObjectInfoColumn)
-  ...     weight = 1
-  ...
-  ...     def __init__(self, context, request, view):
-  ...         self.view = view
-  ...
-  ...     def __call__(self):
-  ...         return self.item['size']
-
-  >>> defineChecker(SizeColumnViewlet, viewletChecker)
-
-  >>> zope.component.provideAdapter(
-  ...     SizeColumnViewlet,
-  ...     (zope.interface.Interface, IDefaultBrowserLayer, IBrowserView),
-  ...     IObjectInfoColumn,
-  ...     name='size')
-
-
-Now let's have a look at the resulting view:
-
-  >>> view = zope.component.getMultiAdapter(
-  ...     (content, request), name='contents.html')
-  >>> print view().strip()
-  <html>
-    <body>
-      <h1>Contents</h1>
-      <table>
-        <tr>
-          <td><b>README.txt</b></td>
-          <td>1.2kB</td>
-        </tr>
-        <tr>
-          <td><b>logo.png</b></td>
-          <td>100 x 100</td>
-        </tr>
-      </table>
-    </body>
-  </html>
-
-
-Content Provider Managers
--------------------------
-
-Until now we have always asserted that the viewlets returned by the TALES
-namespaces ``providers`` and ``provider`` always find the viewlets in the
-component architecture and then return them ordered by weight. This, however,
-is just the default policy. We could also register an alternative policy that
-has different rules on looking up, filtering and sorting the viewlets. The
-objects that implement those policies are known as viewlet managers.
-
-Content provider managers are usually implemented as adapters from the context,
-request, view and region to the ``IContentProviderManager`` interface. They
-must implement two methods. The first one is ``values()``, which returns a list
-of viewlets for the specified region. The region argument is the region
-interface. The second method is ``__getitem__(name)``, which allows you
-to look up a specific viewlet by name and region.
-
-
-The Default Content Provider Manager
+Failure to lookup a Content Provider
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-Let's first have a close look at the default content provider manager, whose
-functionality we took for granted until now. Initializing the manager
 
-  >>> from zope.contentprovider import manager
-  >>> defaultManager = manager.DefaultContentProviderManager(
-  ...     content, request, FrontPage(content, request), ILeftColumn)
+Additional Data from TAL
+~~~~~~~~~~~~~~~~~~~~~~~~
 
-we can now get a list of viewlets:
 
-  >>> defaultManager.values()
-  [<InfoViewlet object at ...>,
-   <Viewlet object at ...>]
-
-The default manager also filters out all viewlets for which the current user
-is not authorized. So, if I create a viewlet that has no security
-declarations, then it is ignored:
-
-  >>> class UnauthorizedViewlet(Viewlet):
-  ...     pass
-
-  # Register the access to a permission that does not exist.
-  >>> unauthorizedChecker = NamesChecker(('__call__', 'weight', 'title',),
-  ...                                    permission_id='Unauthorized')
-  >>> defineChecker(UnauthorizedViewlet, unauthorizedChecker)
-
-  >>> zope.component.provideAdapter(
-  ...     UnauthorizedViewlet,
-  ...     (zope.interface.Interface, IDefaultBrowserLayer, IBrowserView),
-  ...     ILeftColumn,
-  ...     name='unauthorized')
-
-  >>> len(defaultManager.values())
-  2
-
-Also, when you try to look up the unauthorized viewlet by name you will get an
-exception telling you that you have insufficient priviledges to access the
-viewlet:
-
-  >>> defaultManager.__getitem__('unauthorized')
-  Traceback (most recent call last):
-  ...
-  Unauthorized: You are not authorized to access the provider
-                called `unauthorized`.
-
-When looking for a particular viewlet, you also get an exception, if none is
-found:
-
-  >>> defaultManager.__getitem__('unknown')
-  Traceback (most recent call last):
-  ...
-  ComponentLookupError: 'No provider with name `unknown` found.'
-
-
-An Alternative Content Provider Manager
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Let's now imagine that we would like to allow the user to choose the columns
-for the contents view. Here it would not be enough to implement a condition as
-part of the viewlet class, since the TD tag appearance is not controlled by
-the viewlet itself. In those cases it is best to implement a custom viewlet
-manager that only returns the viewlets that are specified in an option:
-
-  >>> showColumns = ['name', 'size']
-
-So our custom viewlet manager could look something like this:
-
-  >>> class ContentsContentProviderManager(manager.DefaultContentProviderManager):
-  ...
-  ...     def values(self):
-  ...         viewlets = zope.component.getAdapters(
-  ...             (self.context, self.request, self.view), self.region)
-  ...         viewlets = [(name, viewlet) for name, viewlet in viewlets
-  ...                     if name in showColumns]
-  ...         viewlets.sort(lambda x, y: cmp(showColumns.index(x[0]),
-  ...                                        showColumns.index(y[0])))
-  ...         return [viewlet for name, viewlet in viewlets]
-
-We just have to register it as an adapter:
-
-  >>> zope.component.provideAdapter(
-  ...     ContentsContentProviderManager,
-  ...     (zope.interface.Interface, IDefaultBrowserLayer, IBrowserView,
-  ...      IObjectInfoColumn),
-  ...     interfaces.IContentProviderManager)
-
-  >>> view = zope.component.getMultiAdapter(
-  ...     (content, request), name='contents.html')
-  >>> print view().strip()
-  <html>
-    <body>
-      <h1>Contents</h1>
-      <table>
-        <tr>
-          <td><b>README.txt</b></td>
-          <td>1.2kB</td>
-        </tr>
-        <tr>
-          <td><b>logo.png</b></td>
-          <td>100 x 100</td>
-        </tr>
-      </table>
-    </body>
-  </html>
-
-But if I turn the order around,
-
-  >>> showColumns = ['size', 'name']
-
-it will provide the columns in a different order as well:
-
-  >>> print view().strip()
-  <html>
-    <body>
-      <h1>Contents</h1>
-      <table>
-        <tr>
-          <td>1.2kB</td>
-          <td><b>README.txt</b></td>
-        </tr>
-        <tr>
-          <td>100 x 100</td>
-          <td><b>logo.png</b></td>
-        </tr>
-      </table>
-    </body>
-  </html>
-
-On the other hand, it is as easy to remove a column:
-
-  >>> showColumns = ['name']
-  >>> print view().strip()
-  <html>
-    <body>
-      <h1>Contents</h1>
-      <table>
-        <tr>
-          <td><b>README.txt</b></td>
-        </tr>
-        <tr>
-          <td><b>logo.png</b></td>
-        </tr>
-      </table>
-    </body>
-  </html>
-
-
-UML Diagram
------------
-
-                      _________
-                     |         |
-                     | Context |
-                     |_________|
-                          ^
-                          |
-                          |*
-                      ____|____
-                     |         |
-                     |   View  |
-                     |_________|
-                          |
-                          |
-                          |* a view is composed of regions
-                      ____v____
-                     |         |
-                     |  Region |
-                     |_________|
-                          |
-                          |
-                          |* a region contains a list of viewlets
-                      ____v____
-                     |         |
-                     | Viewlet |
-                     |_________|
-
-Natively, Zope 3 allows us to associate one or more views to a given
-object. Those views are either registered for the provided interfaces of the
-object or the object itself. In a view, usually a template, one can define
-zero or more view regions. Upon rendering time, those view regions are populated
-with the viewlets that have been assigned to the region.
-
-
-Cleanup
--------
-
-  >>> import shutil
-  >>> shutil.rmtree(temp_dir)
-
+You might also want to look at the ``zope.viewlet`` package for a more
+featureful API.
\ No newline at end of file

Modified: Zope3/branches/roger-contentprovider/src/zope/contentprovider/interfaces.py
===================================================================
--- Zope3/branches/roger-contentprovider/src/zope/contentprovider/interfaces.py	2005-10-08 10:27:45 UTC (rev 38931)
+++ Zope3/branches/roger-contentprovider/src/zope/contentprovider/interfaces.py	2005-10-08 10:33:52 UTC (rev 38932)
@@ -11,7 +11,7 @@
 # FOR A PARTICULAR PURPOSE.
 #
 ##############################################################################
-"""content provider interfaces
+"""Content provider interfaces
 
 $Id$
 """
@@ -19,103 +19,53 @@
 
 import zope.component
 import zope.interface
-import zope.schema
 from zope.tales import interfaces
+from zope.app.publisher.interfaces import browser
 
-from zope.app.i18n import ZopeMessageIDFactory as _
-from zope.app.publisher.interfaces.browser import IBrowserView
 
+class IContentProvider(browser.IBrowserView):
+    """A piece of content to be shown on a page.
 
-class RegionLookupError(zope.component.ComponentLookupError):
-    """Region object not found."""
+    Objects implementing this interface are providing HTML content when they
+    are called. It is up to the implementation to decide how to lookup
+    necessary data to complete the job.
 
-
-class IRegion(zope.interface.interfaces.IInterface):
-    """Type interface for content provider regions.
-
-    Region interfaces specify the environment variables that are available to
-    the IContentProvider. How those variables are provided is up to the 
-    implementation.
+    Content Providers are discriminated by three components: the context, the
+    request and the view. This allows great control over the selection of the
+    provider.
     """
 
+    view = zope.interface.Attribute(
+        """The View
 
-class IContentProvider(zope.interface.Interface):
-    """A piece of content to be shown on a page.
+        The view is the third discriminator of the content provider. It allows
+        that the content can be controlled for different views.
+        """)
 
-    Content provider are objects that can fill the region specified in a 
-    page, most often page templates. They are selected by the context, request 
-    and view. All content providers of a particular region must also provide
-    the corresponding region interface.
-    """
-
-    weight = zope.schema.Int(
-        title=_(u'weight'),
-        description=_(u"""
-            Key for sorting content providers if the content provider
-            manager is supporting this sort mechanism."""),
-        required=False,
-        default=0)
-
     def __call__(*args, **kw):
         """ Return the content provided by this content provider.
         """
 
+class ITALNamespaceData(zope.interface.interfaces.IInterface):
+    """A type interface that marks an interface as a TAL data specification.
 
-class IContentProviderManager(zope.interface.Interface):
-    """An object that provides access to the content providers.
-
-    The content providers are of a particular context, request and view configuration
-    are accessible via a particular manager instance. content providers are looked up
-    by the region they appear in and the name of the content provider.
+    All fields specified in an interface that provides `ITALNamespaceData`
+    will be looked up in the TAL context and stored on the content provider. A
+    content provider can have multiple interfaces that are of this type.
     """
 
-    context = zope.interface.Attribute(
-        'The context of the view the provider manager adapts to.')
+class ContentProviderLookupError(zope.component.ComponentLookupError):
+    """No content provider was found."""
 
-    view = zope.interface.Attribute(
-        'The view the provider manager adapts to.')
 
-    request = zope.interface.Attribute(
-        'The request the provider manager adapts to.')
+class ITALESProviderExpression(interfaces.ITALESExpression):
+    """Return the HTML content of the named provider.
 
-    region = zope.interface.Attribute(
-        'An interface providing IRegion that specifies the region the '
-        'provider manager is responsible for.')
-
-    def values():
-        """Get all available content providers for this manager.
-
-        This method is responsible for sorting the providers as well.
-        """
-
-    def __getitem__(self, name):
-        """Get a particular content provider of a region selected by name."""
-
-
-class ITALESProvidersExpression(interfaces.ITALESExpression):
-    """TAL namespace for getting a list of content providers.
-
-    To call content providers in a view use the the following syntax in a page
+    To call a content provider in a view use the the following syntax in a page
     template::
 
-      <tal:block repeat="content providers:path.to.my.IRegion">
-        <tal:block replace="structure content" />
-      </tal:block>
+      <tal:block replace="structure provider:provider.name">
 
-    where ``path.to.my.IRegion`` is a region object that provides
-    ``contentprovider.interfaces.IRegion``.
+    The content provider is looked up by the (context, request, view) objects
+    and the name (`provider.name`).
     """
-
-
-class ITALESProviderExpression(interfaces.ITALESExpression):
-    """TAL namespace for getting a single content provider.
-
-    To call a named content provider in a view use the the following syntax
-    in a page template::
-
-      <tal:block replace="structure provider:path.to.my.IRegion/name" />
-
-    where ``path.to.my.IRegion`` is a region object that provides
-    ``contentprovider.interfaces.IRegion`` and ``name`` is the name of the page
-    template.
-    """

Deleted: Zope3/branches/roger-contentprovider/src/zope/contentprovider/manager.py
===================================================================
--- Zope3/branches/roger-contentprovider/src/zope/contentprovider/manager.py	2005-10-08 10:27:45 UTC (rev 38931)
+++ Zope3/branches/roger-contentprovider/src/zope/contentprovider/manager.py	2005-10-08 10:33:52 UTC (rev 38932)
@@ -1,77 +0,0 @@
-##############################################################################
-#
-# 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 implementation
-
-$Id$
-"""
-__docformat__ = 'restructuredtext'
-
-import zope.component
-import zope.interface
-import zope.security
-
-from zope.contentprovider import interfaces
-
-
-class DefaultContentProviderManager(object):
-    """The Default ContentProvider Manager
-
-    This implementation looks up all viewlets from the adapter registry and
-    sorts the viewlet list by weight. Viewlets that are not accessible in the
-    context of this request will also be filtered.
-    """
-    zope.interface.implements(interfaces.IContentProviderManager)
-
-    def __init__(self, context, request, view, region):
-        self.context = context
-        self.request = request
-        self.view = view
-        self.region = region
-
-
-    def values(self):
-        """See zope.app.viewlet.interfaces.IViewletManager"""
-        # Find all viewlets for this region
-        region = self.region
-        viewlets = zope.component.getAdapters(
-            (self.context, self.request, self.view), region)
-        # Sort out all viewlets that cannot be accessed by the principal
-        viewlets = [viewlet for name, viewlet in viewlets
-                    if zope.security.canAccess(viewlet, '__call__')]
-        # Sort the viewlets by weight.
-        viewlets.sort(lambda x, y: cmp(x.weight, y.weight))
-
-        return viewlets
-
-
-    def __getitem__(self, name):
-        """See zope.app.viewlet.interfaces.IViewletManager"""
-        # Find the viewlet
-        region = self.region
-        viewlet = zope.component.queryMultiAdapter(
-            (self.context, self.request, self.view), region, name=name)
-
-        # If the viewlet was not found, then raise a lookup error
-        if viewlet is None:
-            raise zope.component.interfaces.ComponentLookupError(
-                'No provider with name `%s` found.' %name)
-
-        # If the viewlet cannot be accessed, then raise an unauthorized error
-        if not zope.security.canAccess(viewlet, '__call__'):
-            raise zope.security.interfaces.Unauthorized(
-                'You are not authorized to access the provider '
-                'called `%s`.' %name)
-
-        # Return the rendered viewlet.
-        return viewlet

Modified: Zope3/branches/roger-contentprovider/src/zope/contentprovider/tales.py
===================================================================
--- Zope3/branches/roger-contentprovider/src/zope/contentprovider/tales.py	2005-10-08 10:27:45 UTC (rev 38931)
+++ Zope3/branches/roger-contentprovider/src/zope/contentprovider/tales.py	2005-10-08 10:33:52 UTC (rev 38932)
@@ -16,119 +16,47 @@
 $Id$
 """
 __docformat__ = 'restructuredtext'
-import zope.interface
+
 import zope.component
+import zope.interface
+import zope.schema
 from zope.tales import expressions
 
-from zope.interface.declarations import providedBy
-from zope.app.component.hooks import siteinfo
-from zope.contentprovider import interfaces, manager
+from zope.contentprovider import interfaces
 
 
-def getRegion(str):
-    """Get a region from the string.
-
-    This function will create the dummy region implementation as well.
-    """
-    region = zope.component.queryUtility(interfaces.IRegion, name=str)
-    if region is None:
-        raise interfaces.RegionLookupError(
-            'Provider region interface not found.', str)
-    return region
-
-
-def getRegionFieldData(region, context):
-    """Get a dictionary of values for the region fields."""
+def addTALNamespaceData(provider, context):
+    """Add the requested TAL attributes to the provider"""
     data = {}
-    for name, field in zope.schema.getFields(region).items():
-        data[name] = context.vars.get(name, field.default)
-    return data
 
+    for interface in zope.interface.providedBy(provider):
+        if interfaces.ITALNamespaceData.providedBy(interface):
+            for name, field in zope.schema.getFields(interface).items():
+                data[name] = context.vars.get(name, field.default)
 
-class TALESProvidersExpression(expressions.StringExpr):
-    """Collect content provider via a TAL namespace."""
+    provider.__dict__.update(data)
 
-    zope.interface.implements(interfaces.ITALESProvidersExpression)
 
-    def __call__(self, econtext):
-        context = econtext.vars['context']
-        request = econtext.vars['request']
-        view = econtext.vars['view']
-
-        # get the region from the expression
-        region = getRegion(self._s)
-
-        cpManager = None
-        res = []
-        iface = interfaces.IContentProviderManager
-        objs = (context, request, view)
-        # we have to use the lookup method because region is an interface!
-        lookup = siteinfo.sm.adapters.lookup
-        cpManagerClass = lookup(map(providedBy, objs)+[region], iface, name='')
-        if cpManagerClass is not None:
-            cpManager = cpManagerClass(context, request, view, region)
-            
-        if cpManager is None:
-            cpManager = manager.DefaultContentProviderManager(
-                context, request, view, region)
-
-        providers = cpManager.values()
-        #providers = cpManager.values()
-
-        # Insert the data gotten from the context
-        data = getRegionFieldData(region, econtext)
-        for provider in providers:
-            provider.__dict__.update(data)
-
-        return providers
-
-
 class TALESProviderExpression(expressions.StringExpr):
-    """Collects a single content provider via a TAL namespace."""
+    """Collect content provider via a TAL namespace."""
 
     zope.interface.implements(interfaces.ITALESProviderExpression)
 
     def __call__(self, econtext):
-        expr = super(TALESProviderExpression, self).__call__(econtext)
-        if not '/' in expr:
-            raise KeyError('Use `iface/key` for defining the provider.')
-
-        parts = expr.split('/')
-        if len(parts) > 2:
-            msg = "Do not use more then one '/' for defining iface/key."
-            raise KeyError(msg)
-
-        # get interface from key
-        self._iface = parts[0]
-        self._name = parts[1]
-
+        name = super(TALESProviderExpression, self).__call__(econtext)
         context = econtext.vars['context']
         request = econtext.vars['request']
         view = econtext.vars['view']
 
-        # get the region from the expression
-        region = getRegion(self._iface)
+        # Try to look up the provider.
+        provider = zope.component.queryMultiAdapter(
+            (context, request, view), interfaces.IContentProviderManager, name)
 
-        # Find the content provider
-        cpManager = None
-        res = []
-        iface = interfaces.IContentProviderManager
-        objs = (context, request, view)
-        # we have to use the lookup method because region is an interface!
-        lookup = siteinfo.sm.adapters.lookup
-        cpManagerClass = lookup(map(providedBy, objs)+[region], iface, name='')
-        if cpManagerClass is not None:
-            cpManager = cpManagerClass(context, request, view, region)
-            
-        if cpManager is None:
-            cpManager = manager.DefaultContentProviderManager(
-                context, request, view, region)
+        # Provide a useful error message, if the provider was not found.
+        if provider is None:
+            raise interfaces.ContentProviderLookupError(name)
 
-        provider = cpManager.__getitem__(self._name)
-        #provider = cpManager[self._name]
-
         # Insert the data gotten from the context
-        data = getRegionFieldData(region, econtext)
-        provider.__dict__.update(data)
+        addTALNamespaceData(provider, econtext)
 
-        return provider()
+        return provider

Copied: Zope3/branches/roger-contentprovider/src/zope/contentprovider/tests.py (from rev 38927, Zope3/branches/roger-contentprovider/src/zope/contentprovider/tests/test_doc.py)



More information about the Zope3-Checkins mailing list