[Zope-Checkins] SVN: Products.Five/branches/1.4/ * Enabled the viewlet related directives by default.

Alec Mitchell cvs-admin at zope.org
Wed Jun 14 17:50:05 EDT 2006


Log message for revision 68635:
  * Enabled the viewlet related directives by default.
  
  * Added acquisition wrappers to viewlets before updating or rendering.
  
  * Made the provider directive acquisition wrap the resultant content provider so that simple providers that need security declarations (e.g. those that render pagetemplates) can work with the Zope 2 security machinery.
  

Changed:
  U   Products.Five/branches/1.4/CHANGES.txt
  U   Products.Five/branches/1.4/browser/ProviderExpression.py
  U   Products.Five/branches/1.4/browser/tests/provider.txt
  U   Products.Five/branches/1.4/browser/tests/provider.zcml
  A   Products.Five/branches/1.4/browser/tests/provider_template_based.pt
  U   Products.Five/branches/1.4/configure.zcml
  U   Products.Five/branches/1.4/viewlet/configure.zcml
  U   Products.Five/branches/1.4/viewlet/directives.txt
  U   Products.Five/branches/1.4/viewlet/manager.py
  U   Products.Five/branches/1.4/viewlet/tests.py

-=-
Modified: Products.Five/branches/1.4/CHANGES.txt
===================================================================
--- Products.Five/branches/1.4/CHANGES.txt	2006-06-14 18:34:19 UTC (rev 68634)
+++ Products.Five/branches/1.4/CHANGES.txt	2006-06-14 21:50:00 UTC (rev 68635)
@@ -5,6 +5,14 @@
 Five 1.4.1 (unreleased)
 =======================
 
+* Enabled the viewlet related directives by default.
+
+* Added acquisition wrappers to viewlets before updating or rendering.
+
+* Made the provider directive acquisition wrap the resultant content provider
+  so that simple providers that need security declarations (e.g. those that
+  render pagetemplates) can work with the Zope 2 security machinery.
+
 * Added Five.browser.pagetemplatefile.ViewPageTemplateFile as an alias
   to ZopeTwoPageTemplateFile and as a Zope 2 correspondence to
   zope.app.pagetemplate.ViewPageTemplateFile.

Modified: Products.Five/branches/1.4/browser/ProviderExpression.py
===================================================================
--- Products.Five/branches/1.4/browser/ProviderExpression.py	2006-06-14 18:34:19 UTC (rev 68634)
+++ Products.Five/branches/1.4/browser/ProviderExpression.py	2006-06-14 21:50:00 UTC (rev 68635)
@@ -49,6 +49,9 @@
         if provider is None:
             raise interfaces.ContentProviderLookupError(name)
 
+        if getattr(provider, '__of__', None) is not None:
+            provider = provider.__of__(context)
+
         # Insert the data gotten from the context
         addTALNamespaceData(provider, econtext)
 

Modified: Products.Five/branches/1.4/browser/tests/provider.txt
===================================================================
--- Products.Five/branches/1.4/browser/tests/provider.txt	2006-06-14 18:34:19 UTC (rev 68634)
+++ Products.Five/branches/1.4/browser/tests/provider.txt	2006-06-14 21:50:00 UTC (rev 68635)
@@ -189,3 +189,45 @@
       </div>
     </body>
   </html>
+
+Now we test a provider using a PageTemplateFile to render itself.  It must
+inherit from an Acquisition base class so that the template can use Zope 2
+security mechanisms:
+
+  >>> import os, tempfile
+  >>> temp_dir = tempfile.mkdtemp()
+  >>> dynTemplate = os.path.join(temp_dir, 'dynamic_template.pt')
+  >>> open(dynTemplate, 'w').write(
+  ...   'A simple template: <tal:simple replace="python:view.my_property" />')
+  >>> from Acquisition import Explicit
+  >>> from Products.Five.browser.pagetemplatefile import ZopeTwoPageTemplateFile
+  >>> class TemplateProvider(Explicit):
+  ...     zope.component.adapts(zope.interface.Interface,
+  ...                           browser.IDefaultBrowserLayer,
+  ...                           zope.interface.Interface)
+  ...
+  ...     def __init__(self, context, request, view):
+  ...         self.__parent__ = view
+  ...         self.context = context
+  ...         self.request = request
+  ...         self.view = view
+  ...
+  ...     def update(self):
+  ...         pass
+  ...     # Is there a better way to tell it to look in the current dir?
+  ...     render = ZopeTwoPageTemplateFile(dynTemplate, temp_dir)
+  ...     my_property = 'A string for you'
+
+  >>> zope.component.provideAdapter(TemplateProvider, name='mypage.TemplateProvider', provides=interfaces.IContentProvider)
+  >>> print http(r'''
+  ... GET /test_folder_1_/content_obj/template_based.html HTTP/1.1
+  ... ''')
+  HTTP/1.1 200 OK
+  ...
+  A simple template: A string for you
+
+ Cleanup
+ -------
+
+  >>> import shutil
+  >>> shutil.rmtree(temp_dir)

Modified: Products.Five/branches/1.4/browser/tests/provider.zcml
===================================================================
--- Products.Five/branches/1.4/browser/tests/provider.zcml	2006-06-14 18:34:19 UTC (rev 68634)
+++ Products.Five/branches/1.4/browser/tests/provider.zcml	2006-06-14 21:50:00 UTC (rev 68635)
@@ -29,5 +29,11 @@
       name="namespace2.html"
       permission="zope2.Public"
       />
+  <browser:page
+      for="Products.Five.tests.testing.simplecontent.ISimpleContent"
+      template="provider_template_based.pt"
+      name="template_based.html"
+      permission="zope2.View"
+      />
 
 </configure>

Added: Products.Five/branches/1.4/browser/tests/provider_template_based.pt
===================================================================
--- Products.Five/branches/1.4/browser/tests/provider_template_based.pt	2006-06-14 18:34:19 UTC (rev 68634)
+++ Products.Five/branches/1.4/browser/tests/provider_template_based.pt	2006-06-14 21:50:00 UTC (rev 68635)
@@ -0,0 +1 @@
+<tal:block replace="structure provider:mypage.TemplateProvider" />

Modified: Products.Five/branches/1.4/configure.zcml
===================================================================
--- Products.Five/branches/1.4/configure.zcml	2006-06-14 18:34:19 UTC (rev 68634)
+++ Products.Five/branches/1.4/configure.zcml	2006-06-14 21:50:00 UTC (rev 68635)
@@ -12,6 +12,7 @@
   <include package=".formlib" />
   <include package=".skin" />
   <include package=".utilities" />
+  <include package=".viewlet" />
 
   <include package="zope.app.event" />
   <include package="zope.app.traversing" />

Modified: Products.Five/branches/1.4/viewlet/configure.zcml
===================================================================
--- Products.Five/branches/1.4/viewlet/configure.zcml	2006-06-14 18:34:19 UTC (rev 68634)
+++ Products.Five/branches/1.4/viewlet/configure.zcml	2006-06-14 21:50:00 UTC (rev 68635)
@@ -1,6 +1,8 @@
 <configure xmlns="http://namespaces.zope.org/zope"
            xmlns:browser="http://namespaces.zope.org/browser">
 
+  <include file="meta.zcml" />
+
   <interface
       interface="zope.viewlet.interfaces.IViewletManager"
       />

Modified: Products.Five/branches/1.4/viewlet/directives.txt
===================================================================
--- Products.Five/branches/1.4/viewlet/directives.txt	2006-06-14 18:34:19 UTC (rev 68634)
+++ Products.Five/branches/1.4/viewlet/directives.txt	2006-06-14 21:50:00 UTC (rev 68635)
@@ -408,7 +408,7 @@
   ... <configure xmlns="http://namespaces.zope.org/browser" i18n_domain="zope">
   ...   <viewletManager
   ...       name="newcolumn"
-  ...       permission="zope.Public"
+  ...       permission="zope2.View"
   ...       provides="Products.Five.viewlet.tests.INewColumn"
   ...       />
   ... </configure>
@@ -451,7 +451,6 @@
   ...       manager="Products.Five.viewlet.tests.INewColumn"
   ...       template="%s"
   ...       permission="zope.Public"
-  ...       extra_string_attributes="can be specified"
   ...       />
   ... </configure>
   ... ''' % weatherTemplate)
@@ -495,6 +494,45 @@
        </div>
   ...
 
+A Dynamic Viewlet
+-----------------
+
+A viewlet template can of course contain some dynamic code, let's see how
+that works:
+
+  >>> dynWeatherTemplate = os.path.join(temp_dir, 'dynamic_weather.pt')
+  >>> open(dynWeatherTemplate, 'w').write(u'''
+  ... <div tal:define="city view/city;"><span tal:replace="string:${city/name}: ${city/temp} F" /></div>'''
+  ... )
+
+  >>> context = zcml.load_string('''
+  ... <configure xmlns="http://namespaces.zope.org/browser" i18n_domain="zope">
+  ...   <viewlet
+  ...       name="dynweather"
+  ...       for="*"
+  ...       manager="Products.Five.viewlet.tests.INewColumn"
+  ...       class="Products.Five.viewlet.tests.DynamicTempBox"
+  ...       template="%s"
+  ...       permission="zope2.View"
+  ...       />
+  ... </configure>
+  ... ''' % dynWeatherTemplate)
+
+Now we request the view to ensure that we can see the dynamic template
+rendered as expected:
+
+  >>> print http(r"""
+  ... GET /test_folder_1_/ftf/@@securitytest_view HTTP/1.1
+  ... """, handle_errors=False)
+  HTTP/1.1 200 OK
+  ...
+       <h1>Weather</h1>
+       <div>
+       <div>Los Angeles, CA: 78 F</div>
+       <div>sunny</div>
+       </div>
+  ...
+
 Cleanup
 -------
 

Modified: Products.Five/branches/1.4/viewlet/manager.py
===================================================================
--- Products.Five/branches/1.4/viewlet/manager.py	2006-06-14 18:34:19 UTC (rev 68634)
+++ Products.Five/branches/1.4/viewlet/manager.py	2006-06-14 21:50:00 UTC (rev 68635)
@@ -7,6 +7,7 @@
 
 from Products.Five.browser.pagetemplatefile import ZopeTwoPageTemplateFile
 
+aq_base = Acquisition.aq_base
 
 class ViewletManagerBase(origManagerBase, Acquisition.Explicit):
     """A base class for Viewlet managers to work in Zope2"""
@@ -23,9 +24,12 @@
             raise zope.component.interfaces.ComponentLookupError(
                 'No provider with name `%s` found.' %name)
 
+        # Wrap the viewlet for security lookups
+        viewlet = viewlet.__of__(viewlet.context)
+
         # If the viewlet cannot be accessed, then raise an
         # unauthorized error
-        if not guarded_hasattr(viewlet.__of__(viewlet.context), 'render'):
+        if not guarded_hasattr(viewlet, 'render'):
             raise zope.security.interfaces.Unauthorized(
                 'You are not authorized to access the provider '
                 'called `%s`.' %name)
@@ -38,13 +42,27 @@
 
         ``viewlets`` is a list of tuples of the form (name, viewlet).
         """
+        results = []
         # Only return viewlets accessible to the principal
         # We need to wrap each viewlet in its context to make sure that
         # the object has a real context from which to determine owner
         # security.
-        return [(name, viewlet) for name, viewlet in viewlets if
-                guarded_hasattr(viewlet.__of__(viewlet.context), 'render')]
+        for name, viewlet in viewlets:
+            viewlet = viewlet.__of__(viewlet.context)
+            if guarded_hasattr(viewlet, 'render'):
+                results.append((name, viewlet))
+        return results
 
+    def sort(self, viewlets):
+        """Sort the viewlets.
+
+        ``viewlets`` is a list of tuples of the form (name, viewlet).
+        """
+        # By default, use the standard Python way of doing sorting. Unwrap the
+        # objects first so that they are sorted as expected.  This is dumb
+        # but it allows the tests to have deterministic results.
+        return sorted(viewlets, lambda x, y: cmp(aq_base(x[1]), aq_base(y[1])))
+
 def ViewletManager(name, interface, template=None, bases=()):
 
     if template is not None:

Modified: Products.Five/branches/1.4/viewlet/tests.py
===================================================================
--- Products.Five/branches/1.4/viewlet/tests.py	2006-06-14 18:34:19 UTC (rev 68634)
+++ Products.Five/branches/1.4/viewlet/tests.py	2006-06-14 21:50:00 UTC (rev 68635)
@@ -73,6 +73,10 @@
     def __call__(self):
         return u'Red Sox vs. White Sox'
 
+class DynamicTempBox(object):
+    weight = 0
+    city = {'name': 'Los Angeles, CA', 'temp': 78}
+
 def setUp(test):
     setup.placefulSetUp()
 



More information about the Zope-Checkins mailing list