[Zope3-checkins] SVN: Zope3/branches/srichter-viewlet-implementation/src/zope/app/viewlet/ Implemented the features that were previously in the pagelet code as well

Stephan Richter srichter at cosmos.phy.tufts.edu
Sun Sep 18 20:40:51 EDT 2005


Log message for revision 38511:
  Implemented the features that were previously in the pagelet code as well 
  and that I had discussed on Saturday morning on IRC with Roger and 
  Philipp.
  
  

Changed:
  U   Zope3/branches/srichter-viewlet-implementation/src/zope/app/viewlet/README.txt
  U   Zope3/branches/srichter-viewlet-implementation/src/zope/app/viewlet/directives.txt
  U   Zope3/branches/srichter-viewlet-implementation/src/zope/app/viewlet/interfaces.py
  A   Zope3/branches/srichter-viewlet-implementation/src/zope/app/viewlet/manager.py
  U   Zope3/branches/srichter-viewlet-implementation/src/zope/app/viewlet/meta.zcml
  U   Zope3/branches/srichter-viewlet-implementation/src/zope/app/viewlet/metaconfigure.py
  U   Zope3/branches/srichter-viewlet-implementation/src/zope/app/viewlet/tales.py
  U   Zope3/branches/srichter-viewlet-implementation/src/zope/app/viewlet/tests/test_doc.py
  U   Zope3/branches/srichter-viewlet-implementation/src/zope/app/viewlet/viewlet.py

-=-
Modified: Zope3/branches/srichter-viewlet-implementation/src/zope/app/viewlet/README.txt
===================================================================
--- Zope3/branches/srichter-viewlet-implementation/src/zope/app/viewlet/README.txt	2005-09-18 11:11:10 UTC (rev 38510)
+++ Zope3/branches/srichter-viewlet-implementation/src/zope/app/viewlet/README.txt	2005-09-19 00:40:49 UTC (rev 38511)
@@ -4,302 +4,268 @@
 
 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, regions (called
-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, page regions can be
+defined and are filled dynamically with content (viewlets) based on the setup
+of the application.
 
-The Design
-----------
 
-UML Diagram
-~~~~~~~~~~~
-                      _________
-                     |         |
-                     | Context |
-                     |_________|
-                          ^
-                          |
-                          |*
-                      ____|____
-                     |         |
-                     |   View  |
-                     |_________|
-                          |
-                          |
-                          |* a view is composed of regions
-                      ____v____
-                     |          |
-                     | ViewRegion |
-                     |__________|
-                          |
-                          |
-                          |* a region contains a list of viewlets
-                      ____v____        _____________
-                     |         |      |             |
-                     | Viewlet |------| ViewletType |
-                     |_________|*     |_____________|
-                          ^
-                         /_\
-              ____________|____________
-             |                         |
-         ____|____                 ____|____
-        |         |               |         |
-        | Viewlet |               | Portlet |
-        |_________|               |_________|
+Getting Started
+---------------
 
-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.
+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.
 
 
-The Difference betwen a Viewlet and a Portlet
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Regions
+~~~~~~~
 
-Let's start with the properties the two share. Viewlets and portlets are
-designed to be parts of a view; they are specialized viewlets. In the Zope
-world the difference is commonly seen as follows:
+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:
 
-  * A viewlet of a view displays the underlying context.
+  >>> import zope.interface
+  >>> class ILeftColumn(zope.interface.Interface):
+  ...     '''The left column of a Web site.'''
 
-  * A portlet of a view displays data from different contexts.
+  >>> from zope.app.viewlet import interfaces
+  >>> zope.interface.directlyProvides(ILeftColumn, interfaces.IRegion)
 
-For example, the metadata viewlet displays the metadata of the underlying
-context. A metadata portlet, on the other hand, could display the metadata of
-all of the context's children (assuming the context is a container). In a
-second example, a calendar viewlet displays the calendar data of a content
-object that implements its own calendar, while a calendar portlet displays
-global calendar data on different objects that may come from an utility.
+  >>> import zope.component
+  >>> zope.component.provideUtility(ILeftColumn, interfaces.IRegion,
+  ...                               'webpage.LeftColumn')
 
-The above definitions need to be altered slightly when talking in terms of
-Zope 3, since the adaption mechanism used for looking up views hides the data
-retrieval from the user. Thus, let's slightly reword the definitions:
+It is important that the region interface 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.
 
-  * A viewlet of a view operates on the underlying context.
 
-  * A portlet of a view operates on the underlying or on a different context.
+Viewlet
+~~~~~~~
 
-Rephrasing our examples, we have: The metadata viewlet displays the metadata
-of the view context by adapting it to ``IMetadata``. The portlet, on the other
-hand, adapts to a context independent of the underlying view context.
+Viewlets are snippets of content that can be placed into a region, such as the
+one defined above. As the name suggests, viewlets are views, but they are
+qualified not only by the context object and the request, but also the view
+they appear in. Also, the viewlet must *provide* the region interface it is
+filling; we will demonstrate a more advanced example later, where the purpose
+of this requirement becomes clear.
 
-Therefore, a set of viewlets of the same type inside a particular views must
-always display similar content, while a set of portlets of the same type can
-provide a wide range of contents from very different parts of the site.
+Like regular views, viewlets can either use page templates to provide content
+or provide a simple ``__call__`` method. For our first viewlet, let's develop
+a more commonly used page-template-driven viewlet:
 
+  >>> import os, tempfile
+  >>> temp_dir = tempfile.mkdtemp()
 
-Usage
------
+  >>> viewletFileName = os.path.join(temp_dir, 'viewlet.pt')
+  >>> open(viewletFileName, 'w').write('''
+  ...         <div class="box">
+  ...           <tal:block replace="viewlet/title" />
+  ...         </div>
+  ... ''')
 
-This viewlet implementation supports viewlets of the first and second
-definition, but portlets only of the first. In the following text we use the
-term "viewlet" as defined in the second definition.
+  >>> class ViewletBase(object):
+  ...     def title(self):
+  ...         return 'Viewlet Title'
 
-Viewlets are responsible for a piece of content in a view. They can be used to
-provide additionally information about an object that is not fully relevant
-for the view's functionality, but provides useful information and/or links to
-the user. Viewlets are small, view-like components that are identified by the
-following set of interfaces they are registered for:
+As you can see, the viewlet Python class is known as ``viewlet`` inside the
+template, while the view class is still available as ``view``. Next we build
+and register the viewlet using a special helper function:
 
-  * Layer: The layer in which the viewlet will be used.
+  # Create the viewlet class
+  >>> from zope.app.viewlet import viewlet
+  >>> Viewlet = viewlet.SimpleViewletClass(
+  ...     viewletFileName, bases=(ViewletBase,), name='viewlet')
 
-  * Content Type: The interface the context of the the view must provide. This
-    is the ``for`` attribute of the view and viewlet directive.
+  # Generate a viewlet checker
+  >>> from zope.security.checker import NamesChecker, defineChecker
+  >>> viewletChecker = NamesChecker(('__call__', 'weight', 'title',))
+  >>> defineChecker(Viewlet, viewletChecker)
 
-  * View: The interface the view must provide. By default this is
-    ``IBrowserView`` and the default is commonly not changed.
+  # 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')
 
-  * Region: The instance of the region in which this viewlet can be placed.
+As you can see from the security checker registration, a viewlet provides also
+a weight, which acts as a hint to determine the order in which the viewlets of
+a region should be displayed. The view the viewlet is used in can also be
+accessed via the ``view`` attribute of the viewlet class.
 
-Inside a pagetemplate the viewlets of a particular region can be retrieved using
-the ``viewlets`` TALES namespace. The return value is a sequence of viewlet
-objects that can simply be called. The viewlets are selected by the four
-above-mentioned parameters and sorted by the weight of the viewlets::
 
-  <div class="row">
-    <tal:repeat="viewlet viewlets:path.to.Region">
-      <tal:block replace="structure viewlet" />
-    </tal:repeat>
-  </div>
+Creating the View
+~~~~~~~~~~~~~~~~~
 
+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:
 
-An Example
-----------
-
-Before we even start demonstrating the template, we need to register the
-`viewlets` TALES namespace:
-
-  >>> from zope.app.pagetemplate import metaconfigure
-  >>> from zope.app.viewlet import tales
-  >>> metaconfigure.registerType('viewlets', tales.TALESViewletsExpression)
-
-The first task will be to create a region that we can use in a pagetemplate. A
-region is simply an interface that simply needs to provide ``IViewletRegion``. The
-interface is then registered as a utility providing the interface.
-
-  >>> import zope.interface
-  >>> class IDemoRegion(zope.interface.Interface):
-  ...     '''A region for demonstration purposes.'''
-
-  >>> from zope.app.viewlet import interfaces
-  >>> zope.interface.directlyProvides(IDemoRegion, interfaces.IViewletRegion)
-
-  >>> import zope.component
-  >>> zope.component.provideUtility(IDemoRegion, interfaces.IViewletRegion,
-  ...                               'DemoRegion')
-
-The argument to the region class is commonly used for documentations. Next we
-can create viewlets for this Now we can create a page template that uses this
-region object to define a region in the template:
-
-  >>> import os, tempfile
-  >>> temp_dir = tempfile.mkdtemp()
-
-  >>> zpt_filename = os.path.join(temp_dir, 'template.pt')
-  >>> open(zpt_filename, 'w').write('''
+  >>> templateFileName = os.path.join(temp_dir, 'template.pt')
+  >>> open(templateFileName, 'w').write('''
   ... <html>
   ...   <body>
-  ...     <h1>Viewlet Demo</h1>
+  ...     <h1>My Web Page</h1>
   ...     <div class="left-column">
   ...       <div class="column-item"
-  ...            tal:repeat="viewlet viewlets:DemoRegion">
+  ...            tal:repeat="viewlet viewlets:webpage.LeftColumn">
   ...         <tal:block replace="structure viewlet" />
   ...       </div>
   ...     </div>
+  ...     <div class="main">
+  ...       Content here
+  ...     </div>
   ...   </body>
   ... </html>
   ... ''')
 
-Now that the template is created, we register the template as a browser page
-view:
+and registering it as a view (browser page) for all objects:
 
   >>> from zope.app.pagetemplate.simpleviewclass import SimpleViewClass
-  >>> DemoPage = SimpleViewClass(zpt_filename, name='demo.html')
+  >>> FrontPage = SimpleViewClass(templateFileName, name='main.html')
 
-  >>> from zope.publisher.interfaces.browser import IDefaultBrowserLayer
   >>> zope.component.provideAdapter(
-  ...     DemoPage,
+  ...     FrontPage,
   ...     (zope.interface.Interface, IDefaultBrowserLayer),
   ...     zope.interface.Interface,
-  ...     name='demo.html')
+  ...     name='main.html')
 
-In the following step we will create a couple of viewlets that are used in the
-demo page. Viewlets are really views, except that they additionally adapt
-their view and region. The first viewlet is a minimalistic implementation:
+That is all of the setup. Let's now render the view.
 
-  >>> from zope.app.publisher.browser import BrowserView
-  >>> class Viewlet1(BrowserView):
-  ...     weight = 0
-  ...
-  ...     def __init__(self, context, request, view, region):
-  ...         super(Viewlet1, self).__init__(context, request)
-  ...
-  ...     def __call__(self):
-  ...         return u'<h3>Viewlet 1 Content</h3>'
 
-  >>> from zope.security.checker import NamesChecker, defineChecker
-  >>> viewletChecker = NamesChecker(('__call__', 'weight'))
-  >>> defineChecker(Viewlet1, viewletChecker)
+Using the View
+~~~~~~~~~~~~~~
 
-  >>> from zope.interface import Interface, providedBy
-  >>> from zope.app.publisher.interfaces.browser import IBrowserView
-  >>> zope.component.provideAdapter(
-  ...     Viewlet1,
-  ...     (Interface, IDefaultBrowserLayer, IBrowserView, IDemoRegion),
-  ...     interfaces.IViewlet,
-  ...     name='viewlet1')
+Let's create a content object that can be viewed:
 
-Let's now register a more typical viewlet. We first create a template:
-
-  >>> plt_filename = os.path.join(temp_dir, 'viewlet2.pt')
-  >>> open(plt_filename, 'w').write('''
-  ...         <div class="box">
-  ...           <tal:block replace="viewlet/title" />
-  ...         </div>
-  ... ''')
-
-  >>> class Viewlet2Base(object):
-  ...     def title(self):
-  ...         return 'Viewlet 2 Content'
-
-As you can see, the viewlet Python class is known as ``viewlet``, while the
-view class is still available as ``view``. Next we build and register the
-viewlet using a special helper function:
-
-  >>> from zope.app.viewlet import viewlet
-  >>> Viewlet2 = viewlet.SimpleViewletClass(
-  ...     plt_filename, bases=(Viewlet2Base,), name='viewlet2', weight=1)
-
-  >>> defineChecker(Viewlet2, viewletChecker)
-
-  >>> zope.component.provideAdapter(
-  ...     Viewlet2,
-  ...     (Interface, IDefaultBrowserLayer, IBrowserView, IDemoRegion),
-  ...     interfaces.IViewlet,
-  ...     name='viewlet2')
-
-Now all the setup is completed. Let's create a content object:
-
   >>> class Content(object):
   ...     zope.interface.implements(zope.interface.Interface)
 
   >>> content = Content()
 
-and finally, we look up the view and render it:
+Finally we look up the view and render it:
 
   >>> from zope.publisher.browser import TestRequest
   >>> request = TestRequest()
 
   >>> view = zope.component.getMultiAdapter((content, request),
-  ...                                       name='demo.html')
+  ...                                       name='main.html')
   >>> print view().strip()
   <html>
     <body>
-      <h1>Viewlet Demo</h1>
+      <h1>My Web Page</h1>
       <div class="left-column">
         <div class="column-item">
-          <h3>Viewlet 1 Content</h3>
-        </div>
-        <div class="column-item">
   <BLANKLINE>
           <div class="box">
-            Viewlet 2 Content
+            Viewlet Title
           </div>
   <BLANKLINE>
         </div>
       </div>
+      <div class="main">
+        Content here
+      </div>
     </body>
   </html>
 
-Note that if we turn the weight around,
 
-  >>> Viewlet1.weight = 1
-  >>> Viewlet2._weight = 0
+Class-driven Viewlets
+~~~~~~~~~~~~~~~~~~~~~
 
-the order of the left column in the page template shoudl change:
+Let's now have a look into the steps involved to create a viewlet class from
+scratch. We also want to ensure that this viewlet always displays second and
+not before the first one. Here is a most simple implementation:
 
+  >>> from zope.app.publisher.browser import BrowserView
+  >>> class InfoViewlet(BrowserView):
+  ...     zope.interface.implements(interfaces.IViewlet, ILeftColumn)
+  ...     weight = 1
+  ...
+  ...     def __init__(self, context, request, view):
+  ...         super(InfoViewlet, self).__init__(context, request)
+  ...         self.view = view
+  ...
+  ...     def __call__(self):
+  ...         return u'<h3>Some Information.</h3>'
+
+  >>> defineChecker(InfoViewlet, viewletChecker)
+
+  >>> zope.component.provideAdapter(
+  ...     InfoViewlet,
+  ...     (zope.interface.Interface, IDefaultBrowserLayer, IBrowserView),
+  ...     ILeftColumn,
+  ...     name='infoViewlet')
+
+
+Note that you would commonly not state in the class that itself that it
+implements a particular region, since it is usually done by the ZCML
+directive, which is introduced in `directives.zcml`.
+
+When we now render the view, the content of our info viewlet appears as well:
+
   >>> print view().strip()
   <html>
     <body>
-      <h1>Viewlet Demo</h1>
+      <h1>My Web Page</h1>
       <div class="left-column">
         <div class="column-item">
   <BLANKLINE>
           <div class="box">
-            Viewlet 2 Content
+            Viewlet Title
           </div>
   <BLANKLINE>
         </div>
         <div class="column-item">
-          <h3>Viewlet 1 Content</h3>
+          <h3>Some Information.</h3>
         </div>
       </div>
+      <div class="main">
+        Content here
+      </div>
     </body>
   </html>
 
 
-Looking up a viewlet by name
+Changing the Weight
+~~~~~~~~~~~~~~~~~~~
+
+Let's ensure that the weight really affects the order of the viewlets. 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">
+          <h3>Some Information.</h3>
+        </div>
+        <div class="column-item">
+  <BLANKLINE>
+          <div class="box">
+            Viewlet Title
+          </div>
+  <BLANKLINE>
+        </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,
@@ -307,40 +273,43 @@
 TALES namespace called ``viewlet`` that selects the viewlet using the
 expression ``<path to region>/<viewlet name>``.
 
-  >>> metaconfigure.registerType('viewlet', tales.TALESViewletExpression)
-
 Since everything else is already setup, we can simply register a new view:
 
-  >>> zpt_filename2 = os.path.join(temp_dir, 'template2.pt')
-  >>> open(zpt_filename2, 'w').write('''
+  >>> template2FileName = os.path.join(temp_dir, 'template2.pt')
+  >>> open(template2FileName, 'w').write('''
   ... <html>
   ...   <body>
-  ...     <h1>Viewlet Demo</h1>
+  ...     <h1>My Web Page - Take 2</h1>
   ...     <div class="left-column">
   ...       <div class="column-item">
-  ...         <tal:block replace="structure viewlet:DemoRegion/viewlet1" />
+  ...         <tal:block
+  ...           replace="structure viewlet:webpage.LeftColumn/viewlet" />
   ...       </div>
   ...     </div>
   ...   </body>
   ... </html>
   ... ''')
 
-  >>> DemoPage2 = SimpleViewClass(zpt_filename2, name='demo2.html')
+  >>> SecondPage = SimpleViewClass(template2FileName, name='second.html')
   >>> zope.component.provideAdapter(
-  ...     DemoPage2,
+  ...     SecondPage,
   ...     (zope.interface.Interface, IDefaultBrowserLayer),
-  ...     zope.interface.Interface,
-  ...     name='demo2.html')
+  ...     ILeftColumn,
+  ...     name='second.html')
 
   >>> view = zope.component.getMultiAdapter((content, request),
-  ...                                       name='demo2.html')
+  ...                                       name='second.html')
   >>> print view().strip()
   <html>
     <body>
-      <h1>Viewlet Demo</h1>
+      <h1>My Web Page - Take 2</h1>
       <div class="left-column">
         <div class="column-item">
-          <h3>Viewlet 1 Content</h3>
+  <BLANKLINE>
+          <div class="box">
+            Viewlet Title
+          </div>
+  <BLANKLINE>
         </div>
       </div>
     </body>
@@ -348,3 +317,336 @@
 
 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 viewlets: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
+any useful, they need access the 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
+
+  >>> class NameColumnViewlet(BrowserView):
+  ...     zope.interface.implements(interfaces.IViewlet, IObjectInfoColumn)
+  ...     weight = 0
+  ...
+  ...     def __init__(self, context, request, view):
+  ...         super(NameColumnViewlet, self).__init__(context, request)
+  ...         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 the size of the of objects in the list:
+
+  >>> class SizeColumnViewlet(BrowserView):
+  ...     zope.interface.implements(interfaces.IViewlet, IObjectInfoColumn)
+  ...     weight = 1
+  ...
+  ...     def __init__(self, context, request, view):
+  ...         super(SizeColumnViewlet, self).__init__(context, request)
+  ...         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>
+
+
+Viewlet Managers
+----------------
+
+Until now we have always asserted that the viewlets returned by the TALES
+namespaces ``viewlets`` and ``viewlet`` 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.
+
+Viewlet managers are usually implemented as adapters from the context, request
+and view to the ``IViewletManager`` interface. They must implement two
+methods. The first one is ``getViewlets(region)``, which returns a list of
+viewlets for the specified region. The region argument is the region
+interface. The second method is ``getViewlet(name, region)``, which allows you
+to look up a specific viewlet by name ans region.
+
+
+The Default Viewlet Manager
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Let's first have a close look at the default view manager, whose functionality
+we took for granted until now. Initializing the manager
+
+  >>> from zope.app.viewlet import manager
+  >>> defaultManager = manager.DefaultViewletManager(
+  ...     content, request, FrontPage(content, request))
+
+we can now get a list of viewlets:
+
+  >>> defaultManager.getViewlets(ILeftColumn)
+  [<InfoViewlet object at ...>,
+   <zope.app.viewlet.viewlet.SimpleViewletClass from ...viewlet.pt object ...>]
+
+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.getViewlets(ILeftColumn))
+  2
+
+Also, when you try to look up the unauthorized viewlet by name you will get an
+exception telling you that you have insufficient priveledges to access the
+viewlet:
+
+  >>> defaultManager.getViewlet('unauthorized', ILeftColumn)
+  Traceback (most recent call last):
+  ...
+  Unauthorized: You are not authorized to access the viewlet
+                called `unauthorized`.
+
+When looking for a particular viewlet, you also get an exception, if none is
+found:
+
+  >>> defaultManager.getViewlet('unknown', ILeftColumn)
+  Traceback (most recent call last):
+  ...
+  ComponentLookupError: 'No viewlet with name `unknown` found.'
+
+
+An Alternative Viewlet 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 ContentsViewletManager(manager.DefaultViewletManager):
+  ...
+  ...     def getViewlets(self, region):
+  ...         viewlets = zope.component.getAdapters(
+  ...             (self.context, self.request, self.view), 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(
+  ...     ContentsViewletManager,
+  ...     (zope.interface.Interface, IDefaultBrowserLayer, IBrowserView),
+  ...     interfaces.IViewletManager)
+
+  >>> 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.

Modified: Zope3/branches/srichter-viewlet-implementation/src/zope/app/viewlet/directives.txt
===================================================================
--- Zope3/branches/srichter-viewlet-implementation/src/zope/app/viewlet/directives.txt	2005-09-18 11:11:10 UTC (rev 38510)
+++ Zope3/branches/srichter-viewlet-implementation/src/zope/app/viewlet/directives.txt	2005-09-19 00:40:49 UTC (rev 38511)
@@ -49,15 +49,11 @@
 
   >>> import zope.interface
   >>> from zope.app.viewlet.tests.test_doc import ITestRegion
-  >>> class Region(object):
-  ...     pass
-  >>> region = Region()
-  >>> zope.interface.directlyProvides(region, ITestRegion)
 
   >>> import zope.component
   >>> from zope.app.viewlet.interfaces import IViewlet
   >>> viewlet = zope.component.getMultiAdapter(
-  ...     (content, request, view, region), IViewlet, name='testviewlet')
+  ...     (content, request, view), ITestRegion, name='testviewlet')
   >>> viewlet()
   u'<div>testviewlet macro content</div>\n'
 
@@ -78,7 +74,7 @@
   ... ''', context=context)
 
   >>> viewlet = zope.component.getMultiAdapter(
-  ...     (content, request, view, region), IViewlet, name='testviewlet2')
+  ...     (content, request, view), ITestRegion, name='testviewlet2')
   >>> viewlet()
   u'<div>testviewlet macro content</div>\n'
 
@@ -99,7 +95,7 @@
   ... ''', context=context)
 
   >>> viewlet = zope.component.getMultiAdapter(
-  ...     (content, request, view, region), IViewlet, name='testviewlet3')
+  ...     (content, request, view), ITestRegion, name='testviewlet3')
   >>> viewlet()
   u'called'
 
@@ -121,7 +117,7 @@
   ... ''', context=context)
 
   >>> viewlet = zope.component.getMultiAdapter(
-  ...     (content, request, view, region), IViewlet, name='testviewlet4')
+  ...     (content, request, view), ITestRegion, name='testviewlet4')
   >>> viewlet()
   u'something'
 

Modified: Zope3/branches/srichter-viewlet-implementation/src/zope/app/viewlet/interfaces.py
===================================================================
--- Zope3/branches/srichter-viewlet-implementation/src/zope/app/viewlet/interfaces.py	2005-09-18 11:11:10 UTC (rev 38510)
+++ Zope3/branches/srichter-viewlet-implementation/src/zope/app/viewlet/interfaces.py	2005-09-19 00:40:49 UTC (rev 38511)
@@ -17,52 +17,40 @@
 """
 __docformat__ = 'restructuredtext'
 
-from zope.component import ComponentLookupError
-from zope.interface import Attribute
-from zope.interface import Interface
-from zope.interface.interfaces import IInterface
-from zope.schema import Int
-from zope.tales.interfaces import ITALESExpression
+import zope.component
+import zope.interface
+import zope.schema
+from zope.tales import interfaces
 
 from zope.app.i18n import ZopeMessageIDFactory as _
-from zope.app.location import ILocation
 from zope.app.publisher.interfaces.browser import IBrowserView
 
 
-class ViewletRegionLookupError(ComponentLookupError):
+class ViewletRegionLookupError(zope.component.ComponentLookupError):
     """Viewlet region object not found."""
 
 
-class IViewletRegion(IInterface):
-    """Marker interface for viewlet regions.
+class IRegion(zope.interface.interfaces.IInterface):
+    """Type interface for viewlet regions.
 
-    The viewlet region is used as a part ot the key for to register and
-    collect viewlets.
+    Region interfaces specify the environment variables that are available to
+    the viewlet. How those variables are provided is up to the implementation.
     """
 
 
 class IViewlet(IBrowserView):
-    """Interface for custom viewlet adapters.
+    """A piece of content of a page.
 
-    Viewlets can be used in a page template as a piece of content rendered
-    with it's own python view class. Yes with viewlets you can use more then
-    one views in a pageltemplate. This let's viewlets act as portlets. The
-    viewlet view can support content independent information where you can
-    access in every page template on which the viewlet is registered.
-
-    The meta directive set the 'weight' attribute to the class attribute
-    '_weight'. If you whould like to use the settings from the meta directive
-    point the attribute 'weight' to this default attribute.
-
-    If you use a 'template', the meta directive sets the 'template' to the
-    class attribute '_template'.
+    Viewlets 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 viewlets of a particular region must also provide the region
+    interface.
     """
 
-    view = Attribute('The view the viewlet is used in.')
+    view = zope.interface.Attribute(
+        'The view the viewlet is used in.')
 
-    region = Attribute('The region in which the viewlet is placed.')
-
-    weight = Int(
+    weight = zope.schema.Int(
         title=_(u'weight'),
         description=_(u"""
             Key for sorting viewlets if the viewlet collector is supporting
@@ -71,7 +59,34 @@
         default=0)
 
 
-class ITALESViewletsExpression(ITALESExpression):
+class IViewletManager(zope.interface.Interface):
+    """An object that provides access to the viewlets.
+
+    The viewlets are of a particular context, request and view configuration
+    are accessible via a particular manager instance. Viewlets are looked up
+    by the region they appear in and the name of the viewlet.
+    """
+
+    context = zope.interface.Attribute(
+        'The context of the view the viewlet appears in.')
+
+    view = zope.interface.Attribute(
+        'The view the viewlet is used in.')
+
+    request = zope.interface.Attribute(
+        'The request of the view the viewlet is used in.')
+
+    def getViewlets(region):
+        """Get all available viewlets of the given region.
+
+        This method is responsible for sorting the viewlets as well.
+        """
+
+    def getViewlet(self, name, region):
+        """Get a particular viewlet of a region selected by name."""
+
+
+class ITALESViewletsExpression(interfaces.ITALESExpression):
     """TAL namespace for getting a list of viewlets.
 
     To call viewlets in a view use the the following syntax in a page
@@ -82,11 +97,11 @@
       </tal:block>
 
     where ``path.to.my.IRegion`` is a region object that provides
-    ``viewlet.interfaces.IViewletRegion``.
+    ``viewlet.interfaces.IRegion``.
     """
 
 
-class ITALESViewletExpression(ITALESExpression):
+class ITALESViewletExpression(interfaces.ITALESExpression):
     """TAL namespace for getting a single viewlet.
 
     To call a named viewlet in a view use the the following syntax in a page
@@ -95,6 +110,6 @@
       <tal:block replace="structure viewlet:path.to.my.IRegion/name" />
 
     where ``path.to.my.IRegion`` is a region object that provides
-    ``viewlet.interfaces.IViewletRegion`` and ``name`` is the name of the page
-    template .
+    ``viewlet.interfaces.IRegion`` and ``name`` is the name of the page
+    template.
     """

Added: Zope3/branches/srichter-viewlet-implementation/src/zope/app/viewlet/manager.py
===================================================================
--- Zope3/branches/srichter-viewlet-implementation/src/zope/app/viewlet/manager.py	2005-09-18 11:11:10 UTC (rev 38510)
+++ Zope3/branches/srichter-viewlet-implementation/src/zope/app/viewlet/manager.py	2005-09-19 00:40:49 UTC (rev 38511)
@@ -0,0 +1,74 @@
+##############################################################################
+#
+# 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: metaconfigure.py 38437 2005-09-10 01:59:07Z rogerineichen $
+"""
+__docformat__ = 'restructuredtext'
+
+import zope.component
+import zope.interface
+import zope.security
+
+from zope.app.viewlet import interfaces
+
+
+class DefaultViewletManager(object):
+    """The Default Viewlet 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.IViewletManager)
+
+    def __init__(self, context, request, view):
+        self.context = context
+        self.request = request
+        self.view = view
+
+
+    def getViewlets(self, region):
+        """See zope.app.viewlet.interfaces.IViewletManager"""
+        # Find all viewlets for this 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 getViewlet(self, name, region):
+        """See zope.app.viewlet.interfaces.IViewletManager"""
+        # Find the viewlet
+        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 viewlet 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 viewlet '
+                'called `%s`.' %name)
+
+        # Return the rendered viewlet.
+        return viewlet


Property changes on: Zope3/branches/srichter-viewlet-implementation/src/zope/app/viewlet/manager.py
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: Zope3/branches/srichter-viewlet-implementation/src/zope/app/viewlet/meta.zcml
===================================================================
--- Zope3/branches/srichter-viewlet-implementation/src/zope/app/viewlet/meta.zcml	2005-09-18 11:11:10 UTC (rev 38510)
+++ Zope3/branches/srichter-viewlet-implementation/src/zope/app/viewlet/meta.zcml	2005-09-19 00:40:49 UTC (rev 38511)
@@ -6,7 +6,7 @@
     <meta:directive
         name="viewlet"
         schema=".metadirectives.IViewletDirective"
-        handler=".metaconfigure.viewlet"
+        handler=".metaconfigure.viewletDirective"
         />
 
   </meta:directives>

Modified: Zope3/branches/srichter-viewlet-implementation/src/zope/app/viewlet/metaconfigure.py
===================================================================
--- Zope3/branches/srichter-viewlet-implementation/src/zope/app/viewlet/metaconfigure.py	2005-09-18 11:11:10 UTC (rev 38510)
+++ Zope3/branches/srichter-viewlet-implementation/src/zope/app/viewlet/metaconfigure.py	2005-09-19 00:40:49 UTC (rev 38511)
@@ -19,25 +19,25 @@
 
 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.security.checker import defineChecker, Checker
 
 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.app.viewlet.interfaces import IViewletRegion, IViewlet
-from zope.app.viewlet.viewlet import SimpleViewletClass
-from zope.app.viewlet.viewlet import SimpleAttributeViewlet
+from zope.app.viewlet import viewlet, interfaces
 
 
-def viewlet(_context, name, permission,
-            region, for_=Interface, layer=IDefaultBrowserLayer, view=IBrowserView,
-            class_=None, template=None, attribute='__call__', weight=0,
-            allowed_interface=None, allowed_attributes=None):
+def viewletDirective(_context, name, permission, region,
+                     for_=Interface, layer=IDefaultBrowserLayer,
+                     view=IBrowserView,
+                     class_=None, template=None, attribute='__call__', weight=0,
+                     allowed_interface=None, allowed_attributes=None):
 
     required = {}
 
@@ -76,7 +76,7 @@
                     )
         if template:
             # Create a new class for the viewlet template and class.
-            new_class = SimpleViewletClass(
+            new_class = viewlet.SimpleViewletClass(
                 template, bases=(class_, ), weight=weight)
         else:
             if not hasattr(class_, 'browserDefault'):
@@ -90,15 +90,19 @@
             cdict['__name__'] = name
             cdict['__page_attribute__'] = attribute
             new_class = type(class_.__name__,
-                             (class_, SimpleAttributeViewlet), cdict)
+                             (class_, viewlet.SimpleAttributeViewlet), cdict)
 
         if hasattr(class_, '__implements__'):
             classImplements(new_class, IBrowserPublisher)
 
     else:
         # Create a new class for the viewlet template alone.
-        new_class = SimpleViewletClass(template, name=name, weight=weight)
+        new_class = viewlet.SimpleViewletClass(
+            template, name=name, weight=weight)
 
+    # Make sure the new class implements the region
+    classImplements(new_class, region)
+
     for attr_name in (attribute, 'browserDefault', '__call__',
                       'publishTraverse', 'weight'):
         required[attr_name] = permission
@@ -110,14 +114,14 @@
 
     viewmeta._handle_for(_context, for_)
     metaconfigure.interface(_context, view)
-    metaconfigure.interface(_context, region, IViewletRegion)
+    metaconfigure.interface(_context, region, interfaces.IRegion)
 
-    defineChecker(new_class, Checker(required))
+    checker.defineChecker(new_class, checker.Checker(required))
 
     # register viewlet
     _context.action(
         discriminator = ('viewlet', for_, layer, view, region, name),
         callable = metaconfigure.handler,
         args = ('provideAdapter',
-                (for_, layer, view, region), IViewlet, name, new_class,
+                (for_, layer, view), region, name, new_class,
                  _context.info),)

Modified: Zope3/branches/srichter-viewlet-implementation/src/zope/app/viewlet/tales.py
===================================================================
--- Zope3/branches/srichter-viewlet-implementation/src/zope/app/viewlet/tales.py	2005-09-18 11:11:10 UTC (rev 38510)
+++ Zope3/branches/srichter-viewlet-implementation/src/zope/app/viewlet/tales.py	2005-09-19 00:40:49 UTC (rev 38511)
@@ -16,46 +16,37 @@
 $Id$
 """
 __docformat__ = 'restructuredtext'
-import sys
+import zope.interface
+import zope.component
+from zope.tales import expressions
 
-from zope.component.interfaces import ComponentLookupError
-from zope.interface import implements, directlyProvides
-from zope.security import canAccess
-from zope.security.interfaces import Unauthorized
+from zope.app.viewlet import interfaces, manager
 
-from zope.tales.expressions import StringExpr
 
-from zope.app import zapi
-
-from zope.app.viewlet.interfaces import ITALESViewletExpression
-from zope.app.viewlet.interfaces import ITALESViewletsExpression
-from zope.app.viewlet.interfaces import IViewletRegion
-from zope.app.viewlet.interfaces import IViewlet
-from zope.app.viewlet.interfaces import ViewletRegionLookupError
-
-
 def getRegion(str):
     """Get a region from the string.
 
     This function will create the dummy region implementation as well.
     """
-    region = zapi.queryUtility(IViewletRegion, name=str)
+    region = zope.component.queryUtility(interfaces.IRegion, name=str)
     if region is None:
-        raise ViewletRegionLookupError(
+        raise interfaces.ViewletRegionLookupError(
             'Viewlet region interface not found.', str)
+    return region
 
-    # Create a dummy region instance for adapter lookup. This is not ultra
-    # clean but puts the burden of filtering by region on the adapter
-    # registry.
-    class DummyRegion(object):
-        implements(region)
-    return DummyRegion()
 
+def getRegionFieldData(region, context):
+    """Get a dictionary of values for the region fields."""
+    data = {}
+    for name, field in zope.schema.getFields(region).items():
+        data[name] = context.vars.get(name, field.default)
+    return data
 
-class TALESViewletsExpression(StringExpr):
+
+class TALESViewletsExpression(expressions.StringExpr):
     """Collect viewlets via a TAL namespace called `viewlets`."""
 
-    implements(ITALESViewletsExpression)
+    zope.interface.implements(interfaces.ITALESViewletsExpression)
 
     def __call__(self, econtext):
         context = econtext.vars['context']
@@ -66,18 +57,26 @@
         region = getRegion(self._s)
 
         # Find the viewlets
-        viewlets = zapi.getAdapters((context, request, view, region), IViewlet)
-        viewlets = [viewlet for name, viewlet in viewlets
-                    if canAccess(viewlet, '__call__')]
-        viewlets.sort(lambda x, y: cmp(x.weight, y.weight))
+        viewletManager = zope.component.queryMultiAdapter(
+            (context, request, view), interfaces.IViewletManager)
+        if viewletManager is None:
+            viewletManager = manager.DefaultViewletManager(
+                context, request, view)
 
+        viewlets = viewletManager.getViewlets(region)
+
+        # Insert the data gotten from the context
+        data = getRegionFieldData(region, econtext)
+        for viewlet in viewlets:
+            viewlet.__dict__.update(data)
+
         return viewlets
 
 
-class TALESViewletExpression(StringExpr):
+class TALESViewletExpression(expressions.StringExpr):
     """Collects a single viewlet via a TAL namespace called viewlet."""
 
-    implements(ITALESViewletExpression)
+    zope.interface.implements(interfaces.ITALESViewletExpression)
 
     def __init__(self, name, expr, engine):
         if not '/' in expr:
@@ -100,16 +99,16 @@
         region = getRegion(self._iface)
 
         # Find the viewlets
-        viewlet = zapi.queryMultiAdapter(
-            (context, request, view, region), IViewlet, name=self._name)
+        viewletManager = zope.component.queryMultiAdapter(
+            (context, request, view), interfaces.IViewletManager)
+        if viewletManager is None:
+            viewletManager = manager.DefaultViewletManager(
+                context, request, view)
 
-        if viewlet is None:
-            raise ComponentLookupError(
-                'No viewlet with name `%s` found.' %self._name)
+        viewlet = viewletManager.getViewlet(self._name, region)
 
-        if not canAccess(viewlet, '__call__'):
-            raise Unauthorized(
-                'You are not authorized to access the viewlet '
-                'called `%s`.' %self._name)
+        # Insert the data gotten from the context
+        data = getRegionFieldData(region, econtext)
+        viewlet.__dict__.update(data)
 
         return viewlet()

Modified: Zope3/branches/srichter-viewlet-implementation/src/zope/app/viewlet/tests/test_doc.py
===================================================================
--- Zope3/branches/srichter-viewlet-implementation/src/zope/app/viewlet/tests/test_doc.py	2005-09-18 11:11:10 UTC (rev 38510)
+++ Zope3/branches/srichter-viewlet-implementation/src/zope/app/viewlet/tests/test_doc.py	2005-09-19 00:40:49 UTC (rev 38511)
@@ -19,6 +19,7 @@
 
 import unittest
 import zope.interface
+import zope.security
 from zope.testing import doctest
 from zope.testing.doctestunit import DocTestSuite, DocFileSuite
 from zope.app.testing import setup
@@ -40,19 +41,38 @@
 
 class ITestRegion(zope.interface.Interface):
     '''A region for testing purposes.'''
-zope.interface.directlyProvides(ITestRegion, interfaces.IViewletRegion)
+zope.interface.directlyProvides(ITestRegion, interfaces.IRegion)
 
 
+class TestParticipation(object):
+    principal = 'foobar'
+    interaction = None
+
+
+def setUp(test):
+    setup.placefulSetUp()
+
+    from zope.app.pagetemplate import metaconfigure
+    from zope.app.viewlet import tales
+    metaconfigure.registerType('viewlets', tales.TALESViewletsExpression)
+    metaconfigure.registerType('viewlet', tales.TALESViewletExpression)
+
+    zope.security.management.getInteraction().add(TestParticipation())
+
+
+def tearDown(test):
+    setup.placefulTearDown()
+
+
 def test_suite():
     return unittest.TestSuite((
         DocTestSuite('zope.app.viewlet.tales'),
         DocFileSuite('../README.txt',
-                     setUp=setup.placefulSetUp,
-                     tearDown=setup.placefulTearDown(),
+                     setUp=setUp, tearDown=tearDown,
+                     optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
                      ),
         DocFileSuite('../directives.txt',
-                     setUp=setup.placefulSetUp,
-                     tearDown=setup.placefulTearDown(),
+                     setUp=setUp, tearDown=tearDown,
                      optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
                      ),
         ))

Modified: Zope3/branches/srichter-viewlet-implementation/src/zope/app/viewlet/viewlet.py
===================================================================
--- Zope3/branches/srichter-viewlet-implementation/src/zope/app/viewlet/viewlet.py	2005-09-18 11:11:10 UTC (rev 38510)
+++ Zope3/branches/srichter-viewlet-implementation/src/zope/app/viewlet/viewlet.py	2005-09-19 00:40:49 UTC (rev 38511)
@@ -43,10 +43,9 @@
 
     _weight = 0
 
-    def __init__(self, context, request, view, region):
+    def __init__(self, context, request, view):
         super(SimpleViewlet, self).__init__(context, request)
         self.view = view
-        self.region = region
 
     def _getWeight (self):
         """The weight of the viewlet."""



More information about the Zope3-Checkins mailing list