[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