[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