[Zope3-checkins] CVS: Zope3/src/zope/app/publisher/browser - configure.zcml:1.3 meta.zcml:1.5 viewmeta.py:1.4

Jim Fulton jim@zope.com
Mon, 30 Dec 2002 18:34:17 -0500


Update of /cvs-repository/Zope3/src/zope/app/publisher/browser
In directory cvs.zope.org:/tmp/cvs-serv16088

Modified Files:
	configure.zcml meta.zcml viewmeta.py 
Log Message:
Added a new directive, page, to be udes when creating simple one-page
views. This directive uses:

- Uses a class attribute rather than a factory

- requires that a permission be specified, but allows the permission
  to be "*", to apply to all objects.

- Alloes a menu id and title to be specified so that you don't need to
  use a separate menuItem directive.





=== Zope3/src/zope/app/publisher/browser/configure.zcml 1.2 => 1.3 ===
--- Zope3/src/zope/app/publisher/browser/configure.zcml:1.2	Wed Dec 25 09:13:09 2002
+++ Zope3/src/zope/app/publisher/browser/configure.zcml	Mon Dec 30 18:33:46 2002
@@ -29,11 +29,12 @@
 </content>
 
 
-<browser:view name=""
-              factory="zope.app.publisher.browser.resources.Resources"
-              for="zope.app.interfaces.services.service.IServiceManagerContainer"
-              permission="zope.Public"
-              allowed_interface="zope.publisher.interfaces.browser.IBrowserPublisher"
-              />
+<browser:page 
+    name=""
+    class="zope.app.publisher.browser.resources.Resources"
+    for="zope.app.interfaces.services.service.IServiceManagerContainer"
+    permission="zope.Public"
+    allowed_interface="zope.publisher.interfaces.browser.IBrowserPublisher"
+    />
 
 </zopeConfigure>


=== Zope3/src/zope/app/publisher/browser/meta.zcml 1.4 => 1.5 ===
--- Zope3/src/zope/app/publisher/browser/meta.zcml:1.4	Sat Dec 28 11:14:00 2002
+++ Zope3/src/zope/app/publisher/browser/meta.zcml	Mon Dec 30 18:33:46 2002
@@ -2,6 +2,134 @@
   
   <directives namespace="http://namespaces.zope.org/browser">
 
+    <directive name="page" 
+               handler="zope.app.publisher.browser.viewmeta.page" 
+               >
+
+      <attribute name="name" required="yes">
+        <description>
+              The name of the view defined by the page. 
+ 
+              The name shows up in URLs/paths. For example 'foo' or 
+              'foo.html'. This attribute is required unless you use the
+              subdirective 'page' to create sub views. If you do not have
+              sub pages, it is common to use an extension for the view name 
+              such as '.html'. If you do have sub pages and you want to
+              provide a view name, you shouldn't use
+              extensions. 
+          </description>
+         </attribute>
+
+      <attribute name="for" required="yes">
+        <description>
+              The interface this page (view) applies to. 
+
+              The view will be for all objects that implement this interface.
+
+              To provide a page for all components, use
+              "zope.interface.Interface".  To provide a page for all
+              objects, use "*".
+          </description>
+        </attribute>
+
+      <attribute name="permission" required="yes">
+        <description>
+              The permission needed to use the view. 
+
+              This attribute is required. 
+          
+          </description>
+        </attribute>
+
+      <attribute name="template">
+        <description>
+              The name of a page template.
+
+              Refers to a file containing a page template (must end in
+              extension '.pt'). You must specify either 'template' or
+              'factory'. If you supply a template, you must
+              also supply a name. You can also optionally supply a
+              'class' attribute which contains a view class that has
+              methods that can be used by the template.
+
+              You cannot have sub pages if you use
+              'template'. 
+          </description>
+        </attribute>
+
+      <attribute name="attribute" required="no">
+        <description>
+           If a class is used, this is the name of the attribute to be used
+
+           This is the attribute, usually a method, to be published as
+           the page (view).  The fault is "__call__".
+          </description>
+        </attribute>
+
+      <attribute name="class">
+        <description>
+           A class to use with a template, or to provide an attribute
+           to publish.
+
+           It's common to provide a class with methods to be used by
+           the template to prevent including Python code in the template.
+          </description>
+        </attribute>
+
+      <attribute name="layer" required="no">
+        <description>
+              The layer the view is in. 
+
+              A skin is composed of layers. It is common to put skin specific
+              views in a layer named after the skin. If the 'layer' attribute
+              is not supplied, it defaults to
+              'default'. 
+          </description>
+        </attribute>
+
+      <attribute name="allowed_interface" required="no">
+        <description>
+              Interface that is also allowed if user has permission.
+
+              By default, 'permission' only applies to viewing the view and 
+              any possible sub views. By specifying this attribute, you can
+              make the permission also apply to everything described in the 
+              supplied interface. 
+          </description>
+        </attribute>
+
+      <attribute name="allowed_attributes" required="no">
+        <description>
+              View attributes that are also allowed if user has permission.
+
+              By default, 'permission' only applies to viewing the view and any
+              possible sub views. By specifying 'allowed_attributes', you can
+              make the permission also apply to the extra attributes on the 
+              view object. 
+          </description>
+        </attribute>
+
+      <attribute name="menu" required="no">
+        <description>
+          The browser menu to include the page (view) in.
+
+          Many views are included in menus. It's convenient to name
+          the menu in the page directive, rather than having to give a
+          separate menuItem directive.          
+          </description>
+        </attribute>
+
+      <attribute name="title" required="no">
+        <description>
+          The browser menu label for the page (view)
+
+          This attribute must be supplied if a menu attribute is
+          supplied. 
+          </description>
+        </attribute>
+
+      </directive>
+
     <directive name="view" 
                handler="zope.app.publisher.browser.metaconfigure.view" 
                >
@@ -73,7 +201,7 @@
           </description>
         </attribute>
 
-       <attribute name="permission" >
+       <attribute name="permission" required="yes">
         <description>
               The permission needed to use the view. 
 


=== Zope3/src/zope/app/publisher/browser/viewmeta.py 1.3 => 1.4 ===
--- Zope3/src/zope/app/publisher/browser/viewmeta.py:1.3	Sat Dec 28 09:14:09 2002
+++ Zope3/src/zope/app/publisher/browser/viewmeta.py	Mon Dec 30 18:33:46 2002
@@ -20,6 +20,7 @@
 
 from zope.security.proxy import Proxy
 from zope.security.checker import CheckerPublic, NamesChecker, Checker
+from zope.security.checker import defineChecker
 
 from zope.interfaces.configuration import INonEmptyDirective
 from zope.interfaces.configuration import ISubdirectiveHandler
@@ -29,13 +30,194 @@
 from zope.publisher.interfaces.browser import IBrowserPresentation
 from zope.publisher.interfaces.browser import IBrowserPublisher
 
+from zope.publisher.browser import BrowserView
+
 from zope.app.component.metaconfigure import handler
 
 from zope.app.pagetemplate.simpleviewclass import SimpleViewClass
 from zope.app.pagetemplate.viewpagetemplatefile import ViewPageTemplateFile
 
+from zope.app.security.permission import checkPermission
+
 from zope.proxy.context import ContextMethod
 
+from zope.app.publisher.browser.globalbrowsermenuservice \
+     import menuItemDirective
+
+# There are three cases we want to suport:
+#
+# Named view without pages (single-page view)
+#
+#     <browser:page
+#         for=".IContact.IContactInfo."
+#         name="info.html" 
+#         template="info.pt"
+#         class=".ContactInfoView."
+#         permission="Zope.View"
+#         />
+#
+# Unamed view with pages (multi-page view)
+#
+#     <browser:pages
+#         for=".IContact."
+#         class=".ContactEditView."
+#         permission="ZopeProducts.Contact.ManageContacts"
+#         >
+# 
+#       <browser:page name="edit.html"       template="edit.pt" />
+#       <browser:page name="editAction.html" attribute="action" />
+#       </browser:pages>
+#
+# Named view with pages (add view is a special case of this)
+#
+#        <browser:view
+#            name="ZopeProducts.Contact"
+#            for="Zope.App.OFS.Container.IAdding."
+#            class=".ContactAddView."
+#            permission="ZopeProducts.Contact.ManageContacts"
+#            >
+#
+#          <browser:page name="add.html"    template="add.pt" />
+#          <browser:page name="action.html" attribute="action" />
+#          </browser:view>
+
+# We'll also provide a convenience directive for add views:
+#
+#        <browser:add
+#            name="ZopeProducts.Contact"
+#            class=".ContactAddView."
+#            permission="ZopeProducts.Contact.ManageContacts"
+#            >
+#
+#          <browser:page name="add.html"    template="add.pt" />
+#          <browser:page name="action.html" attribute="action" />
+#          </browser:view>
+
+# page
+
+def _handle_permission(_context, permission, actions):
+    if permission == 'zope.Public':
+        permission = CheckerPublic
+    else:
+        actions.append(Action(discriminator = None, callable = checkPermission,
+                              args = (None, permission)))
+
+    return permission
+
+def _handle_allowed_interface(_context, allowed_interface, permission,
+                              required, actions):
+    # Allow access for all names defined by named interfaces
+    if allowed_interface.strip():
+        for i in allowed_interface.strip().split():
+            i = _context.resolve(i)
+            actions .append(
+                Action(discriminator = None, callable = handler,
+                       args = ('Interfaces', 'provideInterface', None, i)
+                       ))
+            for name in i:
+                required[name] = permission
+
+def _handle_allowed_attributes(_context, allowed_attributes, permission,
+                               required):
+    # Allow access for all named attributes
+    if allowed_attributes.strip():
+        for name in allowed_attributes.strip().split():
+            required[name] = permission
+
+def _handle_for(_context, for_, actions):
+    if for_ == '*':
+        for_ = None
+        
+    if for_ is not None:
+        for_ = _context.resolve(for_)
+        
+        actions .append(
+            Action(discriminator = None, callable = handler,
+                   args = ('Interfaces', 'provideInterface', None, for_)
+            ))
+
+    return for_
+
+class simple(BrowserView):
+
+    __implements__ = IBrowserPublisher, BrowserView.__implements__
+
+    def publishTraverse(self, request, name):
+        raise NotFound(self, name, request)
+
+def page(_context, name, permission, for_,
+         layer='default', template=None, class_=None,
+         allowed_interface='', allowed_attributes='',
+         attribute='__call__', menu=None, title=None
+         ):
+
+    actions = []
+    required = {}
+
+    if menu or title:
+        if not (menu and title):
+            raise ConfigurationError(
+                "If either menu or title are specified, they must "
+                "both be specified.")
+
+        actions = menuItemDirective(_context, menu, for_, '@@' + name, title,
+                                    permission=permission)
+
+    permission = _handle_permission(_context, permission, actions)
+            
+    if not (class_ or template):
+        raise ConfigurationError("Must specify a class or template")
+
+    if attribute != '__call__' and template:
+        raise ConfigurationError(
+            "Attribute and template cannot be used together.")
+
+    if template:
+        template = str(_context.path(template))
+        required['__getitem__'] = permission
+
+    if class_:
+        class_ = _context.resolve(class_)
+        if template:
+            template = str(_context.path(template))
+
+            class_ = SimpleViewClass(template, bases=(class_, ))
+
+        else:
+            if not hasattr(class_, 'browserDefault'):
+                cdict = { 'browserDefault':
+                          lambda self, request:
+                          (getattr(self, attribute), ())
+                          }
+            else:
+                cdict = {}
+                
+            class_ = type(class_.__name__, (class_, simple,), cdict)
+            
+    else:
+        class_ = SimpleViewClass(template)
+        
+    for n in (attribute, 'browserDefault'):
+        required[n] = permission
+
+    _handle_allowed_interface(_context, allowed_interface, permission,
+                              required, actions)
+    _handle_allowed_attributes(_context, allowed_interface, permission,
+                               required)
+    for_ = _handle_for(_context, for_, actions)
+
+    defineChecker(class_, Checker(required))
+
+    actions.append(
+        Action(
+          discriminator = ('view', for_, name, IBrowserPresentation, layer),
+          callable = handler,
+          args = ('Views', 'provideView',
+                  for_, name, IBrowserPresentation, [class_], layer),
+          )
+        )
+
+    return actions
 
 class view:
 
@@ -120,8 +302,6 @@
             self.__pages[name] = attribute, permission, template
             if self.__default is None:
                 self.__default = name
-
-
 
             return ()