[Checkins] SVN: five.customerize/trunk/src/five/customerize/ * rename TTWTemplate to TTWViewTemplate

Philipp von Weitershausen philikon at philikon.de
Mon Jan 8 17:33:54 EST 2007


Log message for revision 71835:
  * rename TTWTemplate to TTWViewTemplate
  * provide a registrations.html view for the template that tells you where and how
    this template is registered as a browser view, and it lets you register it again.
  * various fixes
  

Changed:
  U   five.customerize/trunk/src/five/customerize/browser.py
  U   five.customerize/trunk/src/five/customerize/browser.txt
  U   five.customerize/trunk/src/five/customerize/configure.zcml
  U   five.customerize/trunk/src/five/customerize/customerize.txt
  A   five.customerize/trunk/src/five/customerize/registrations.pt
  U   five.customerize/trunk/src/five/customerize/tests.py
  U   five.customerize/trunk/src/five/customerize/zpt.py
  U   five.customerize/trunk/src/five/customerize/zpt.txt

-=-
Modified: five.customerize/trunk/src/five/customerize/browser.py
===================================================================
--- five.customerize/trunk/src/five/customerize/browser.py	2007-01-08 22:28:11 UTC (rev 71834)
+++ five.customerize/trunk/src/five/customerize/browser.py	2007-01-08 22:33:52 UTC (rev 71835)
@@ -8,11 +8,13 @@
 import zope.interface
 import zope.component
 import zope.dottedname.resolve
+from zope.interface.interfaces import IInterface
+from zope.schema.interfaces import IVocabularyFactory
 from zope.publisher.interfaces.browser import IBrowserRequest
 from zope.traversing.browser import absoluteURL
 from zope.app.apidoc.presentation import getViews
 
-from five.customerize.zpt import TTWTemplate
+from five.customerize.zpt import TTWViewTemplate
 
 def mangleAbsoluteFilename(filename):
     """
@@ -90,7 +92,7 @@
         # (generally object or BrowserView) we return the full class
         # and hope that it can be pickled
         klass = view.__class__
-        base =klass.__bases__[0]
+        base = klass.__bases__[0]
         if base is BrowserView or base is object:
             return klass
         return base
@@ -102,7 +104,9 @@
 
     def templateCodeFromViewName(self, viewname):
         template = self.templateFromViewName(viewname)
-        return open(template.filename, 'rb').read() #XXX: bad zope
+        #XXX: we can't do template.read() here because of a bug in
+        # Zope 3's ZPT implementation.
+        return open(template.filename, 'rb').read()
 
     def permissionFromViewName(self, viewname):
         view = zope.component.getMultiAdapter((self.context, self.request),
@@ -127,8 +131,8 @@
         template_file = self.templateCodeFromViewName(viewname)
         viewclass = self.viewClassFromViewName(viewname)
         permission = self.permissionFromViewName(viewname)
-        viewzpt = TTWTemplate(zpt_id, template_file, view=viewclass,
-                              permission=permission)
+        viewzpt = TTWViewTemplate(zpt_id, template_file, view=viewclass,
+                                  permission=permission)
         site._setObject(zpt_id, viewzpt) #XXXthere could be a naming conflict
         components = site.getSiteManager()
 
@@ -140,7 +144,8 @@
                 break
 
         components.registerAdapter(viewzpt, required=reg.required,
-                                   provided=reg.provided, name=viewname) #XXX info?
+                                   provided=reg.provided, name=viewname
+                                   ) #XXX info?
 
         viewzpt = getattr(site, zpt_id)
         return viewzpt
@@ -150,4 +155,55 @@
         # to get a "direct" URL we use aq_inner for a straight
         # acquisition chain
         url = absoluteURL(aq_inner(viewzpt), self.request) + "/manage_workspace"
-        self.request.RESPONSE.redirect(url)
+        self.request.response.redirect(url)
+
+class RegistrationsView(BrowserView):
+
+    def viewRegistrations(self):
+        regs = []
+        components = zope.component.getSiteManager(self.context)
+        for reg in components.registeredAdapters():
+            if (len(reg.required) == 2 and
+                reg.required[1].isOrExtends(IBrowserRequest) and
+                reg.factory == self.context):
+                regs.append(reg)
+        def regkey(reg):
+            return (reg.name, reg.required)
+        return sorted(regs, key=regkey)
+
+    def getAllInterfaceNames(self):
+        factory = zope.component.getUtility(IVocabularyFactory, 'Interfaces')
+        vocab = factory(self.context)
+        return (term.token for term in vocab)
+
+    def getRequestInterfaceNames(self):
+        factory = zope.component.getUtility(IVocabularyFactory, 'Interfaces')
+        vocab = factory(self.context)
+        return (term.token for term in vocab
+                if term.value.isOrExtends(IBrowserRequest))
+
+    # TODO needs tests
+    def unregister(self):
+        index = self.request.form.get('index')
+        try:
+            index = int(index)
+        except (TypeError, ValueError):
+            index = None
+        if index is None:
+            #XXX: find right exception type
+            raise ValueError("Missing or invalid 'index' parameter.")
+        regs = self.viewRegistrations()
+        reg = regs[index]
+        components = zope.component.getSiteManager(self.context)
+        components.unregisterAdapter(reg.factory, reg.required, reg.provided,
+                                     reg.name)
+        self.request.response.redirect('registrations.html')
+
+    # TODO needs tests
+    def register(self, for_name, type_name, name='', comment=''):
+        for_ = zope.component.getUtility(IInterface, for_name)
+        type = zope.component.getUtility(IInterface, type_name)
+        components = zope.component.getSiteManager(self.context)
+        components.registerAdapter(self.context, (for_, type),
+                                   zope.interface.Interface, name, comment)
+        self.request.response.redirect('registrations.html')

Modified: five.customerize/trunk/src/five/customerize/browser.txt
===================================================================
--- five.customerize/trunk/src/five/customerize/browser.txt	2007-01-08 22:28:11 UTC (rev 71834)
+++ five.customerize/trunk/src/five/customerize/browser.txt	2007-01-08 22:33:52 UTC (rev 71835)
@@ -1,5 +1,5 @@
-Viewing TTWTemplates TTW
-========================
+Viewing TTWViewTemplates through-the-web
+========================================
 
 Set Up
 ------
@@ -7,8 +7,8 @@
 Make this test available as a module so that stuff defined in here can
 be pickled properly:
 
-    >>> from zope.testing.module import setUp, tearDown
-    >>> setUp(test, name='five.customerize.browsertest')
+    >>> from zope.testing import module
+    >>> module.setUp(test, name='five.customerize.browsertest')
 
 Load all of Five's configuration (this is a functional test):
 
@@ -48,14 +48,14 @@
     >>> browser.open('http://localhost/folder/components.html')
     >>> browser.getControl('Make site').click()
 
-Create and a TTWTemplate instance as a view in our site manager:
+Create and a TTWViewTemplate instance as a view in our site manager:
 XXX: We should be able to do this TTW
 
     >>> from zope.interface import Interface
     >>> from OFS.interfaces import IObjectManager
     >>> from zope.publisher.interfaces.browser import IDefaultBrowserLayer
-    >>> from five.customerize.zpt import TTWTemplate
-    >>> template = TTWTemplate('ttwtemplate', 'hello')
+    >>> from five.customerize.zpt import TTWViewTemplate
+    >>> template = TTWViewTemplate('ttwtemplate', 'hello')
     >>> t_id = app.folder._setObject('ttwtemplate', template)
     >>> sm = app.folder.getSiteManager()
     >>> sm.registerAdapter(template, (IObjectManager, IDefaultBrowserLayer),
@@ -63,7 +63,7 @@
     >>> import transaction
     >>> transaction.commit()
 
-Let's see if we can view it
+Let's see if we can view it:
 
     >>> browser.handleErrors = False
     >>> browser.open('http://localhost/folder/myttwtemplate.html')
@@ -84,7 +84,7 @@
     bar
     None
 
-Make and register a view that we can customize with a TTWTemplate:
+Make and register a view that we can customize with a TTWViewTemplate:
 
     >>> from Products.Five.browser import BrowserView
     >>> class TestView(BrowserView):
@@ -103,10 +103,11 @@
     >>> print browser.contents
     Original View
 
-Pass that view to the constructor for a new TTWTemplate, and register
+Pass that view to the constructor for a new TTWViewTemplate, and register
 it locally to override the static view:
 
-    >>> template = TTWTemplate('ttwtemplate2', 'Not so static', view=TestView)
+    >>> template = TTWViewTemplate('ttwtemplate2', 'Not so static',
+    ...                             view=TestView)
     >>> t_id = app.folder._setObject('ttwtemplate2', template)
     >>> sm = app.folder.getSiteManager()
     >>> sm.registerAdapter(template, (IObjectManager, IDefaultBrowserLayer),
@@ -134,6 +135,6 @@
 Clean up:
 ---------
 
+    >>> module.tearDown(test, name='five.customerize.browsertest')
     >>> from zope.testing.cleanup import cleanUp
     >>> cleanUp()
-    >>> tearDown(test, name='five.customerize.browsertest')

Modified: five.customerize/trunk/src/five/customerize/configure.zcml
===================================================================
--- five.customerize/trunk/src/five/customerize/configure.zcml	2007-01-08 22:28:11 UTC (rev 71834)
+++ five.customerize/trunk/src/five/customerize/configure.zcml	2007-01-08 22:33:52 UTC (rev 71835)
@@ -3,18 +3,27 @@
            xmlns:browser="http://namespaces.zope.org/browser"
            i18n_domain="five.customerize">
 
-  <permission id="five.AddTTWTemplate"
-              title="Five: Add TTW Template"
-              />
+  <permission
+      id="five.AddTTWViewTemplate"
+      title="Five: Add TTW View Template"
+      />
   
-  <five:registerClass class=".zpt.TTWTemplate"
-                      meta_type="TTW Template"
-                      permission="five.AddTTWTemplate"
-                      />
+  <five:registerClass
+      class=".zpt.TTWViewTemplate"
+      meta_type="TTW View Template"
+      permission="five.AddTTWViewTemplate"
+      />
 
-  <browser:pages for="*"
-                 class=".browser.CustomizationView"
-                 permission="five.ManageSite">
+  <utility
+      component="zope.app.component.vocabulary.InterfacesVocabulary"
+      name="Interfaces"
+      />
+
+  <browser:pages
+      for="*"
+      class=".browser.CustomizationView"
+      permission="five.ManageSite"
+      >
     <browser:page
        name="zptviews.html"
        template="zptviews.pt"
@@ -30,6 +39,23 @@
   </browser:pages>
 
   <subscriber handler=".zpt.unregisterViewWhenZPTIsDeleted"/>
-                
-  
+
+  <browser:pages
+      for=".zpt.TTWViewTemplate"
+      class=".browser.RegistrationsView"
+      permission="five.ManageSite">
+    <browser:page
+        name="registrations.html"
+        template="registrations.pt"
+        />
+    <browser:page
+        name="unregister"
+        attribute="unregister"
+        />
+    <browser:page
+        name="register"
+        attribute="register"
+        />
+  </browser:pages>
+
 </configure>
\ No newline at end of file

Modified: five.customerize/trunk/src/five/customerize/customerize.txt
===================================================================
--- five.customerize/trunk/src/five/customerize/customerize.txt	2007-01-08 22:28:11 UTC (rev 71834)
+++ five.customerize/trunk/src/five/customerize/customerize.txt	2007-01-08 22:33:52 UTC (rev 71835)
@@ -31,8 +31,8 @@
 
 Make this test a usable module
 
-  >>> from zope.testing.module import setUp, tearDown
-  >>> setUp(test, name='five.customerize.testcustomerize')
+  >>> from zope.testing import module
+  >>> module.setUp(test, name='five.customerize.testcustomerize')
 
 XXX: we are using root as the app name
   >>> root = self.folder
@@ -128,7 +128,7 @@
 
   >>> zpt = view.doCustomizeTemplate(u'customizezpt.html')
 
-That actually creates a TTWTemplate object in the nearest site
+That actually creates a TTWViewTemplate object in the nearest site
 (perhaps later we'd like to have the option to pick which of the sites
 above us should be targeted)
 
@@ -153,7 +153,7 @@
   ... modules:   <tal:var replace="structure modules" />
   ... options:   <tal:var replace="structure options" />
   ... nothing:   <tal:var replace="structure nothing" />
-  ... """, content_type=None)
+  ... """, 'text/html')
 
 In order to be able to look up the customized view now, we need to
 make the site the current site:
@@ -179,7 +179,7 @@
   >>> view = view.__of__(item)
   >>> print view() #doctest: +ELLIPSIS
   context:   <SimpleContent at item>
-  template:  <TTWTemplate at customize.pt>
+  template:  <TTWViewTemplate at customize.pt>
   request:   <HTTPRequest, ...>
   view:      <five.customerize.zpt.TTWView ...>
   modules:   <Products.PageTemplates.ZRPythonExpr._SecureModuleImporter instance at ...>
@@ -255,7 +255,7 @@
   >>> template = getattr(site, 'testviewtemplate.pt')
   >>> template.pt_edit('''\
   ... A customized view
-  ... <span tal:replace="view/foo_method" />''', content_type=None)
+  ... <span tal:replace="view/foo_method" />''', 'text/html')
 
 And render it again:
 
@@ -268,5 +268,6 @@
 Clean up:
 ---------
 
-  >>> from zope.app.testing.placelesssetup import tearDown
-  >>> tearDown()
+  >>> module.tearDown(test, name='five.customerize.testcustomerize')
+  >>> from zope.testing.cleanup import cleanUp
+  >>> cleanUp()

Added: five.customerize/trunk/src/five/customerize/registrations.pt
===================================================================
--- five.customerize/trunk/src/five/customerize/registrations.pt	2007-01-08 22:28:11 UTC (rev 71834)
+++ five.customerize/trunk/src/five/customerize/registrations.pt	2007-01-08 22:33:52 UTC (rev 71835)
@@ -0,0 +1,87 @@
+<h1 tal:replace="structure context/manage_page_header" />
+<h1 tal:replace="structure context/manage_tabs" />
+
+<tal:var define="global registrations view/viewRegistrations" />
+
+<tal:if condition="registrations">
+<p class="form-help">This view template has been registered as a view
+in the following ways:</p>
+
+<form action="unregister"
+      tal:attributes="action string:${context/absolute_url}/unregister">
+<table cellspacing="0" cellpadding="2" border="0">
+  <tr class="list-header">
+    <td align="left" valign="top" width="16">&nbsp;</td>
+    <td><div class="form-label">For</div></td>
+    <td><div class="form-label">Request Type</div></td>
+    <td><div class="form-label">Name</div></td>
+  </tr>
+
+  <tr tal:repeat="reg registrations">
+    <td><input type="checkbox" name="index" value=""
+               tal:attributes="value repeat/reg/index" /></td>
+    <td><div class="list-item"
+             tal:content="python:reg.required[0].__identifier__">for</div></td>
+    <td><div class="list-item"
+             tal:content="python:reg.required[1].__identifier__">type</div></td>
+    <td><div class="list-item"
+             tal:content="reg/name">...</div></td>
+  </tr>
+
+  <tr>
+    <td></td>
+    <td colspan="3"><input type="submit" value="Unregister" /></td>
+  </tr>
+</table>
+</form>
+
+<p></p>
+
+</tal:if>
+<tal:else condition="not:registrations">
+<p class="form-help">This template is not registered as a view.</p>
+</tal:else>
+
+<p class="form-help">Register this template as a view:</p>
+
+<form action="register"
+      tal:attributes="action string:${context/absolute_url}/register">
+
+<table>
+  <tr>
+    <td><div class="form-label">For</div></td>
+    <td><select name="for_name" size="1">
+          <option tal:repeat="name view/getAllInterfaceNames"
+                  tal:content="name"
+                  tal:attributes="value name" />
+        </select></td>
+  </tr>
+
+  <tr>
+    <td><div class="form-label">Request Type</div></td>
+    <td><select name="type_name" size="1">
+          <option tal:repeat="name view/getRequestInterfaceNames"
+                  tal:content="name"
+                  tal:attributes="value name" />
+        </select></td>
+  </tr>
+
+  <tr>
+    <td><div class="form-label">Name</div></td>
+    <td><input type="text" name="name" size="30" /></td>
+  </tr>
+
+  <tr>
+    <td><div class="form-label">Comment</div></td>
+    <td><textarea name="comment" cols="30" rows="5"></textarea></td>
+  </tr>
+
+  <tr>
+    <td></td>
+    <td><input type="submit" value="Register" /></td>
+  </tr>
+</table>
+
+</form>
+
+<h1 tal:replace="structure context/manage_page_footer" />


Property changes on: five.customerize/trunk/src/five/customerize/registrations.pt
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: five.customerize/trunk/src/five/customerize/tests.py
===================================================================
--- five.customerize/trunk/src/five/customerize/tests.py	2007-01-08 22:28:11 UTC (rev 71834)
+++ five.customerize/trunk/src/five/customerize/tests.py	2007-01-08 22:33:52 UTC (rev 71835)
@@ -1,17 +1,24 @@
 import unittest
+from zope.testing.doctest import DocTestSuite
 from Testing.ZopeTestCase import ZopeDocFileSuite
 from Testing.ZopeTestCase import FunctionalDocFileSuite
+
+import zope.component.testing
 from zope.traversing.adapters import DefaultTraversable
-import zope.component.testing
+from zope.publisher.browser import BrowserLanguages
+from zope.publisher.http import HTTPCharsets
 
 __docformat__ = "reStructuredText"
 
 def setUp(test):
     zope.component.testing.setUp(test)
     zope.component.provideAdapter(DefaultTraversable, (None,))
+    zope.component.provideAdapter(BrowserLanguages)
+    zope.component.provideAdapter(HTTPCharsets)
 
 def test_suite():
     return unittest.TestSuite([
+        #DocTestSuite('five.customerize.browser'),
         ZopeDocFileSuite('zpt.txt', package="five.customerize",
                          setUp=setUp, tearDown=zope.component.testing.tearDown),
         ZopeDocFileSuite('customerize.txt', package="five.customerize"),

Modified: five.customerize/trunk/src/five/customerize/zpt.py
===================================================================
--- five.customerize/trunk/src/five/customerize/zpt.py	2007-01-08 22:28:11 UTC (rev 71834)
+++ five.customerize/trunk/src/five/customerize/zpt.py	2007-01-08 22:33:52 UTC (rev 71835)
@@ -5,27 +5,48 @@
 from Products.PageTemplates.ZopePageTemplate import ZopePageTemplate
 from zope.app.container.interfaces import IObjectRemovedEvent
 
-class TTWTemplate(ZopePageTemplate):
+class TTWViewTemplate(ZopePageTemplate):
     """A template class used to generate Zope 3 views TTW"""
 
-    def __init__(self, id, text=None, content_type=None, encoding='utf-8',
-                 strict=False, view=None, permission=None):
+    manage_options = (
+        ZopePageTemplate.manage_options[0],
+        dict(label='Registrations', action='registrations.html'),
+        ) + ZopePageTemplate.manage_options[2:]
+
+    def __init__(self, id, text=None, content_type='text/html', strict=True,
+                 encoding='utf-8', view=None, permission=None):
         self.view = view
         self.permission = permission
-        super(TTWTemplate, self).__init__(id, text, content_type, encoding,
-                                          strict)
+        super(TTWViewTemplate, self).__init__(id, text, content_type, encoding,
+                                              strict)
 
     def __call__(self, context, request):
+        #XXX raise a sensible exception if context and request are
+        # omitted, IOW, if someone tries to render the template not as
+        # a view.
         sm = getSecurityManager()
         if self.permission:
-            allowed = sm.checkPermission(self.permission, context)
-            if not allowed:
-                raise Unauthorized, 'The current user does not have the '\
-                      'required "%s" permission'%self.permission
-        return TTWTemplateRenderer(context, request, self, self.view)
+            if not sm.checkPermission(self.permission, context):
+                raise Unauthorized('The current user does not have the '
+                                   'required "%s" permission'
+                                   % self.permission)
+        return TTWViewTemplateRenderer(context, request, self, self.view)
 
+    # overwrite Shared.DC.Scripts.Binding.Binding's before traversal
+    # hook that would prevent to look up views for instances of this
+    # class.
+    def __before_publishing_traverse__(self, self2, request):
+        pass
 
-class TTWTemplateRenderer(object):
+class TTWViewTemplateRenderer(object):
+    """The view object for the TTW View Template.
+
+    When a TTWViewTemplate-based view is looked up, an object of this
+    class is instantiated.  It holds a reference to the
+    TTWViewTemplate object which it will use in the render process
+    (__call__).
+    """
+
     def __init__(self, context, request, template, view):
         self.context = context
         self.request = request
@@ -33,31 +54,44 @@
         self.view = view
 
     def __call__(self, *args, **kwargs):
-        """Add the zope user to the security context, as done in
-        PageTemplateFile"""
+        """Render the TTWViewTemplate-based view.
+        """
         view = self._getView()
-        bound_names = {'view': view,
+        # we need to override the template's context and request as
+        # they generally point to the wrong objects (a template's
+        # context usually is what it was acquired from, which isn't
+        # what the context is for a view template).
+        bound_names = {'context': self.context,
                        'request': self.request,
-                       'context': self.context}
+                       'view': view}
         template = self.template.__of__(self.context)
         return template._exec(bound_names, args, kwargs)
 
     def _getView(self):
         view = self.view
         if view is not None:
+            # Filesystem-based view templates are trusted code and
+            # have unrestricted access to the view class.  We simulate
+            # that for TTW templates (which are trusted code) by
+            # creating a subclass with unrestricted access to all
+            # subobjects.
             class TTWView(view):
                 __allow_access_to_unprotected_subobjects__ = 1
             view = TTWView(self.context, self.request)
         return view
 
+    # Zope 2 wants to acquisition-wrap every view object (via __of__).
+    # We don't need this as the TTWViewTemplate object is already
+    # properly acquisition-wrapped in __call__.  Nevertheless we need
+    # to support the __of__ method as a no-op.
     def __of__(self, obj):
         return self
 
- at zope.component.adapter(TTWTemplate, IObjectRemovedEvent)
+ at zope.component.adapter(TTWViewTemplate, IObjectRemovedEvent)
 def unregisterViewWhenZPTIsDeleted(zpt, event):
     components = zope.component.getSiteManager(zpt)
     for reg in components.registeredAdapters():
         if reg.factory == zpt:
+            components.unregisterAdapter(reg.factory, reg.required,
+                                         reg.provided, reg.name)
             break
-    components.unregisterAdapter(reg.factory, reg.required, reg.provided,
-                                 reg.name)

Modified: five.customerize/trunk/src/five/customerize/zpt.txt
===================================================================
--- five.customerize/trunk/src/five/customerize/zpt.txt	2007-01-08 22:28:11 UTC (rev 71834)
+++ five.customerize/trunk/src/five/customerize/zpt.txt	2007-01-08 22:33:52 UTC (rev 71835)
@@ -1,11 +1,11 @@
 TTW Template Tests
-----------------
+------------------
 
-First we create a simple TTWTemplate object and then we obtain a
+First we create a simple TTWViewTemplate object and then we obtain a
 renderer by calling as if it were a view factory:
 
-    >>> from five.customerize.zpt import TTWTemplate
-    >>> template = TTWTemplate('test_template', '<html></html>')
+    >>> from five.customerize.zpt import TTWViewTemplate
+    >>> template = TTWViewTemplate('test_template', '<html></html>')
     >>> template = template.__of__(app)
     >>> renderer = template(self.folder, None)
     >>> print renderer()
@@ -18,7 +18,9 @@
     ... <span tal:replace="context/getId"/>
     ... <span tal:replace="request/foo"/>
     ... <span tal:replace="python:repr(view)"/>''', 'text/html')
-    >>> request = {'foo': 'bar'}
+
+    >>> from zope.publisher.browser import TestRequest
+    >>> request = TestRequest(environ={'foo': 'bar'})
     >>> renderer = template(self.folder, request)
     >>> print renderer()
     test_folder_1_



More information about the Checkins mailing list