Plone视图和其他可视部件

Plone视图和其他可视部件
A Zope 3 view is simply a component that can be found during URL traversal,and that can (usually) render itself. When Zope traverses a URL such ashttp://myserver.com/guitars/@@list_guitars it will first find the guitars object, using the rules outlned in the description of object publishing at the beginning of this chapter, and then do something akin to:
view = getMultiAdapter((guitars, request), name="list_guitars")
That is, a view is simply a named multi-adapter of some context and the current request. The name is usually disambiguated from content objects and attributes by prefixing it with @@, although this is optional. To render the view object, Zope will call it. This normally results in a page template being invoked, although some views will simply construct and return a string.
In Zope 2, a view should inherit from Products.Five.browser.BrowserView in order to function properly with Zope 2 security. Note that unlike page templates in skin layers, views (including their templates) execute in unprotected file system code and are not subject to additional "through-the-web" security restrictions.
Plone used to have its own base class for views in Products.CMFPlone.utils, which was introduced to deal with the kind of acquisition-related problems for self.context described in this chapter. It did so not by using aq_inner, but by keeping the object in a list (which saves it from being acquisition-wrapped on retrieval).
However, this base class is now deprecated, because it breaks the convention that self.context should be the context object itself.
In general, the aq_inner approach is safer, and avoids a somewhat awkward dependency on Products.CMFPlone.
By convention, views are located in a browser module. For larger packages, browser is often a package with its own configure.zcml file, included from the main ZCML file using:

<include package=".browser" />
Here is an example using a page template file in the current directory. The base class __init__() method (acting as the multi-adapter's factory) will assign self.context and self.request appropriately.
from Products.Five.browser import BrowserView
from Products.Five.browser.pagetemplatefile import
ViewPageTemplateFile
class GuitarsListing(BrowserView):
    """List guitars found in the current context
    """
    __call__ = ViewPageTemplateFile('listguitars.pt')
    def list_guitars(self):
        ...
The examples in this section are not found in the optilux.codeexamples package, because views do not lend themselves to being described in doctests. However, the next chapter will contain several examples of views.
The assignment of __call__ works because ViewPageTemplateFile is callable. Inside the page template, the implicit variables context, request, and view will refer to the context object (guitars), the request, and the view instance itself,respectively. This allows us to put all the display logic inside the view class,exemplified by list_guitars(), and use simple TAL constructs like view/list_guitars in the template. To register views, we use the <browser:page /> ZCML directive:
<browser:page
    name="list_guitars"
    class=".browser.GuitarsListing"
    for=".interfaces.IGuitarsFolder"
    permission="zope2.View"
    />
Here, the for attribute refers to the type of context for which the view is available (the request part of the multi-adapter is implied). The permission attribute is required. "zope.Public" can be used to define a view that is available to all. Other standard permissions, including the CMF core permissions described in Chapter 6,are defined in permissions.zcml in Products.Five, which is normally installed in the $SOFTWARE_HOME. In the buildout, this would be parts/zope2/lib/python/Products/Five.
We used the __call__() method to reference a template. You will sometimes see a template attribute in the <browser:page /> directive referencing a template, which is equivalent but somewhat more magical. Conversely, the class attribute could be omitted if we had no need for a class to manage the display logic. In this case,template is mandatory.
Sometimes, we have nothing to render at all. This can be the case if we are defining a view for some shared utility functions looked up by other views or templates, but never rendered on their own. In this case, we omit both the __call__() method and the template ZCML attribute. If we had a view called @@guitar_utils, we could look it up in a page template with:
<tal:block define="utils_view context/@@guitar_utils">
Or, in Python from another view class:
context = aq_inner(self.context)
utils_view = getMultiAdapter((context, self.request,), name="guitar_
utils")
See the section on Acquisition Chains in this chapter for an explanation of the use of aq_inner().
As we saw in Chapter 8, it is possible to register a view for a particular browser layer. During traversal, Zope can set a marker interface on the request to indicate the current skin. The view is a multi-adapter on the context and the request. Hence,a view for a particular browser layer is a more specific adapter on the request,compared to a view not registered for a layer. The default layer is zope.publisher.interfaces.browser.IDefaultBrowserLayer.
<browser:page
    name="list_guitars"
    class=".browser.GuitarsListing"
    for=".interfaces.IGuitarsFolder"
    layer=".interfaces.IThemeSpecific"
    permission="zope2.View"
    />
In the next chapter, we will see several examples of views used to render custom content types, and in Chapter 11, we will cover standalone views and auto-generated forms.

Content Providers and Viewlets
When building pages with page templates, we can use METAL macros to include other pages. However, you may also see statements such as:
<div tal:replace="structure provider:guitars.header" />
See the Zope Book on http://zope.org or the ZPT tutorial athttp://plone.org/documentation/tutorial/zpt for more information about TAL and METAL.
The provider: expression type comes from zope.contentprovider (with some overrides in Products.Five.viewlet to support Zope 2). It will perform an operation analogous to:
provider = getMultiAdapter((context, request, view,), name="guitars.
header")
return provider()
That is, the content provider is a named multi-adapter of the context, request, and the current view instance. For skin layer templates (which are not Zope 3 views), Plone's main_template will define the default view to be the @@plone view, which is described in the interface Products.CMFPlone.browser.interfaces.IPlone.
Content providers are rarely used directly, but they are the building blocks for viewlets. A viewlet manager is a content provider, which when rendered will locate any number of viewlets appropriate for the current context, request, and view registered to the particular viewlet manager. As you may have guessed, there are some named multi-adapters involved under the hood. However, unlike raw content providers, there are ZCML directives to make things easier.
To create a new viewlet manager, we must first create a marker interface for it:
from zope.viewlet.interfaces import IViewletManager
class IGuitarsHeader(IViewletManager):
    """A viewlet manager that is put at the head of a guitar listing
    """
Then we register it in ZCML with:
<browser:viewletManager
    name="guitars.header"
    provides=".interfaces.IGuitarsHeader"
    permission="zope2.View"
    />
The name should be unique. A dotted name prefixed with the package name is the convention.It is possible to specify a custom implementation of the viewlet manager, which allows us to control the rendering of viewlets more precisely. In Plone, we normally use the implementation from plone.app.viewletmanager, in order to gain support for the @@manage-viewlets screen and viewlet re-ordering described in Chapter 8:
<browser:viewletManager
    name="guitars.header"
    provides=".interfaces.IGuitarsHeader"
    permission="zope2.View"
    class="plone.app.viewletmanager.manager.OrderedViewletManager"
    />
We can now register any number of viewlets for this viewlet manager.
<browser:viewlet
    name="guitars.headers.adbanner"
    manager=".interfaces.IGuitarsHeader"
    class=".ads.GuitarAds"
    permission="zope2.View"
    />
We can also use the attributes layer and view to reference specific interfaces for the request (browser layer) and view. The latter allows us to have a viewlet in a general viewlet manager (say, one defined in main_template) that is shown on one particular view but not all views.
For example, here is a viewlet that is only shown on the main view of an object,but not on any other tabs or templates. This works because IViewView is a marker interface applied to the view instance during page construction:
<browser:viewlet
    name="guitars.headers.adbanner"
    manager="plone.app.layout.viewlets.interfaces.IAboveContent"
    view="plone.app.layout.globals.interfaces.IViewView"
    class=".ads.GuitarAds"
    permission="zope2.View"
    />
Because viewlets are multi-adapters, the usual rules about overrides apply. We could have a general viewlet for all views, and a more specific one with the same name for a particular type of context, request, and/or view.
Unlike views, viewlets (actually content providers) are not rendered by being called.Instead, they must provide update() and render() methods. During rendering,update() is called on all the viewlets in a viewlet manager, and then render() is called on each viewlet in turn. The results of the render() calls are concatenated and then inserted into the output stream. The update() method should be used to
update state from the request, potentially allowing viewlets to communicate with each other prior to final rendering.
Here is an example, again using a page template for rendering. As with views,the implicit view variable can be used in the template to reference methods on the viewlet class:
from zope.interface import implements
from zope.viewlet.interfaces import IViewlet
from Products.Five.browser import BrowserView
from Products.Five.browser.pagetemplatefile import
ViewPageTemplateFile
class GuitarAds(BrowserView):
    """Rotating ads for new guitars
    """
    implements(IViewlet)
    def __init__(self, context, request, view, manager):
        self.context = context
        self.request = request
        self.__parent__ = view # from IContentProvider
        self.manager = manager # from IViewlet (child of
        IContentProvider)
    def update(self):
        pass
    render = ViewPageTemplateFile("rotating_ads.pt")
Plone inserts a number of viewlet managers in main_template and the standard content type views, offering third-party components various places to plug into the general user interface. These are defined in plone.app.layout.viewlets and use the viewlet manager implementation from plone.app.viewletmanager.
设置