[Zope-CVS] CVS: Products/CompositePage - slotexpr.py:1.1 __init__.py:1.7 composite.py:1.15 designuis.py:1.8 interfaces.py:1.10 macro.py:1.6

Shane Hathaway shane at zope.com
Fri Mar 5 16:41:35 EST 2004


Update of /cvs-repository/Products/CompositePage
In directory cvs.zope.org:/tmp/cvs-serv9514

Modified Files:
	__init__.py composite.py designuis.py interfaces.py macro.py 
Added Files:
	slotexpr.py 
Log Message:
Added a new page template expression type, "slot:".

"slot:" provides a concise way to declare composite slots in page templates.
It is designed to replace metal:define-slot.

Also augmented interfaces and fixed a race condition in the slot
generator.  If two people used the _v_used_names mechanism
simultaneously, the results would be unpredictable.  Now, the tracking
is done on the Composite instead.



=== Added File Products/CompositePage/slotexpr.py ===
##############################################################################
#
# Copyright (c) 2003 Zope Corporation. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Visible Source 
# License, Version 1.0 (ZVSL).  A copy of the ZVSL 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
#
##############################################################################
"""Support for 'slot:' expression type in ZPT.

$Id: slotexpr.py,v 1.1 2004/03/05 21:41:04 shane Exp $
"""

import re

from Products.PageTemplates.TALES import CompilerError, _valid_name

from interfaces import IComposite


# Match a slot_name, (class_name), or 'title'.
_token_re = re.compile("([a-zA-Z][a-zA-Z0-9_]*)|[(]([^)]+)[)]|[']([^']+)[']")


class SlotExpr:
    """Slot expression type.

    Provides a concise syntax for specifying composite slots in
    ZPT.  An example slot expression, in context of ZPT:

    <div tal:replace="slot: slot_name (class_name) 'Title'" />
    """

    def __init__(self, name, expr, engine):
        self._s = expr
        # pattern we are aiming for is 
        # id 'Title' (my_slot_class_id) 
        # or any variation.  first id, title, and slot class are used without
        # throwing an error if there are any more, but slot class and id must
        # be valid names
        self._name = None
        self._class_name = None
        self._title = None
        for n, c, t in _token_re.findall(expr):
            if n:
                if not _valid_name(n):
                    raise CompilerError('Invalid slot name "%s"' % n)
                if self._name:
                    raise CompilerError('Multiple slot names')
                self._name = n
            if c:
                if not _valid_name(c):
                    raise CompilerError('Invalid slot class "%s"' % c)
                if self._class_name:
                    raise CompilerError('Multiple class names')
                self._class_name = c
            if t:
                if self._title:
                    raise CompilerError('Multiple titles')
                self._title = t
    
    def __call__(self, econtext):
        context = econtext.contexts.get('here')
        if context is None:
            context = econtext.context.get('context')
            if context is None:
                raise RuntimeError("Could not find context")
        if IComposite.isImplementedBy(context):
            slot = context.slots.get(self._name, self._class_name, self._title)
            # Render the slot
            return str(slot)
        else:
            # Show the slot expression
            return repr(self)

    def __repr__(self):
        return 'slot:%s' % self._s


def registerSlotExprType():
    # Register the 'slot:' expression type.
    from Products.PageTemplates.Expressions import getEngine
    getEngine().registerType("slot", SlotExpr)


=== Products/CompositePage/__init__.py 1.6 => 1.7 ===
--- Products/CompositePage/__init__.py:1.6	Tue Mar  2 15:41:44 2004
+++ Products/CompositePage/__init__.py	Fri Mar  5 16:41:04 2004
@@ -16,6 +16,9 @@
 """
 
 import tool, element, composite, slot, slotclass, designuis, interfaces
+import slotexpr
+
+slotexpr.registerSlotExprType()
 
 tool.registerUI("common", designuis.CommonUI())
 tool.registerUI("zmi", designuis.ZMIUI())


=== Products/CompositePage/composite.py 1.14 => 1.15 ===
--- Products/CompositePage/composite.py:1.14	Wed Mar  3 11:00:16 2004
+++ Products/CompositePage/composite.py	Fri Mar  5 16:41:04 2004
@@ -28,7 +28,7 @@
 from AccessControl import ClassSecurityInfo
 from AccessControl.ZopeGuards import guarded_getattr
 
-from interfaces import ISlot, CompositeError
+from interfaces import IComposite, ISlot, ISlotGenerator, CompositeError
 from slot import Slot, getIconURL, formatException
 from macro import renderMacro, getRootMacro
 import perm_names
@@ -38,15 +38,27 @@
 
 class SlotGenerator (Acquisition.Explicit):
     """Automatically makes slots available to the template.
+
+    Note: instances of this class are shared across threads.
     """
     _slot_class = Slot
-    _v_used_slots = None
 
-    def __getitem__(self, name):
+    def get(self, name, class_name=None, title=None):
+        """Returns a slot by name.
+
+        This is designed to be called in the middle of a page
+        template.  Assigns attributes (class_name and title) to the
+        slot at the same time.
+        """
         composite = aq_parent(aq_inner(self))
         slots = composite.filled_slots
-        if self._v_used_slots is not None:
-            self._v_used_slots.append(name)
+        if composite._v_slot_specs is not None:
+            # Send the slot specs to the composite.
+            composite._v_slot_specs.append({
+                'name': name,
+                'class_name': class_name,
+                'title': title,
+                })
         try:
             return slots[name]
         except (KeyError, AttributeError):
@@ -61,23 +73,15 @@
                 s._p_jar = jar
             return s.__of__(slots)
 
-    def _beginCollection(self):
-        """Starts collecting the names of slots used.
-        """
-        self._v_used_slots = []
+    __getitem__ = get
 
-    def _endCollection(self):
-        """Stops collecting slot names and returns the names in order of use.
-        """
-        res = self._v_used_slots
-        self._v_used_slots = None
-        return res
 
 
 class Composite(Folder):
     """An HTML fragment composed from a template and fragments.
     """
     meta_type = "Composite"
+    __implements__ = IComposite
 
     security = ClassSecurityInfo()
 
@@ -93,6 +97,7 @@
     _v_editing = 0
     _v_rendering = 0
     _v_generating = 0
+    _v_slot_specs = None  # [{'name', 'class', 'title'}]
 
     security.declarePublic("slots")
     slots = SlotGenerator()
@@ -186,31 +191,40 @@
             raise CompositeError("No composite_tool found")
         return guarded_getattr(tool.uis, ui)
 
-    security.declareProtected(perm_names.change_composites, "getSlotNames")
-    def getSlotNames(self):
-        """Returns the names of the slots in order of use.
+    security.declareProtected(perm_names.change_composites, "getSlotSpecs")
+    def getSlotSpecs(self):
+        """Returns the slot specs within the template.
 
-        May return duplicates.
+        Returns [{'name', 'class', 'title'}].  May return duplicates.
         """
-        self.slots._beginCollection()
+        self._v_slot_specs = []
         try:
             self()
         finally:
-            names = self.slots._endCollection()
-            return names
+            slots = self._v_slot_specs
+            self._v_slot_specs = None
+            return slots
+
+    security.declareProtected(perm_names.change_composites, "getManifest")
+    def getManifest(self):
+        """Returns a manifest of slot contents.
 
-    security.declareProtected(perm_names.change_composites, "getSlotData")
-    def getSlotData(self):
-        """Prepares information about slot contents for presentation.
+        Designed for use by page templates that implement a manual
+        slotting user interface.
         """
         contents = []  # [{name, slot_info}]
         seen = {}
-        names = self.getSlotNames()
+        specs = self.getSlotSpecs()
         if hasattr(self, 'portal_url'):
             icon_base_url = self.portal_url()
         else:
-            icon_base_url = self.REQUEST['BASEPATH1']
-        for name in names:
+            REQUEST = getattr(self, 'REQUEST', None)
+            if REQUEST is not None:
+                icon_base_url = self.REQUEST['BASEPATH1']
+            else:
+                icon_base_url = ''
+        for spec in specs:
+            name = spec['name']
             if seen.has_key(name):
                 # Don't show duplicate uses of a slot.
                 continue
@@ -253,8 +267,9 @@
                 elements.append(element_info)
                 index += 1
             slot_info = {
-                'title': name,  # XXX need to get a real slot title somehow.
-                'slot': slot,
+                'name': name,
+                'title': spec['title'] or name,
+                'class_name': spec['class_name'],
                 'target_path': '/'.join(slot.getPhysicalPath()),
                 'elements': elements,
                 }


=== Products/CompositePage/designuis.py 1.7 => 1.8 ===
--- Products/CompositePage/designuis.py:1.7	Wed Mar  3 11:00:16 2004
+++ Products/CompositePage/designuis.py	Fri Mar  5 16:41:04 2004
@@ -315,9 +315,9 @@
         Returns an HTML fragment without the required scripts and
         styles.
         """
-        slot_data = composite.getSlotData()
+        manifest = composite.getManifest()
         pt = self.body.__of__(composite)
-        return pt(ui=self, slot_data=slot_data)
+        return pt(ui=self, manifest=manifest)
 
     security.declarePublic("render")
     def render(self, composite):


=== Products/CompositePage/interfaces.py 1.9 => 1.10 ===
--- Products/CompositePage/interfaces.py:1.9	Wed Mar  3 11:00:16 2004
+++ Products/CompositePage/interfaces.py	Fri Mar  5 16:41:04 2004
@@ -16,10 +16,37 @@
 """
 
 from Interface import Interface
+from Interface.Attribute import Attribute
 
 class CompositeError(Exception):
     """An error in constructing a composite
     """
+
+
+class IComposite(Interface):
+    """An object whose rendering is composed of a layout and elements.
+    """
+    slots = Attribute("An ISlotGenerator.")
+
+    def __call__():
+        """Renders the composite as a string.
+        """
+
+
+class ISlotGenerator(Interface):
+
+    def get(name, class_name=None, title=None):
+        """Returns a slot, creating it if it does not yet exist.
+
+        The 'class_name' and 'title' arguments allow the caller to
+        specify a slot class and title.  Both are used for composite
+        design purposes, not rendering.
+        """
+
+    def __getitem__(name):
+        """Returns a slot, creating it if it does not yet exist.
+        """
+
 
 class ISlot(Interface):
     """A slot in a composite.


=== Products/CompositePage/macro.py 1.5 => 1.6 ===
--- Products/CompositePage/macro.py:1.5	Wed Dec 31 12:32:14 2003
+++ Products/CompositePage/macro.py	Fri Mar  5 16:41:04 2004
@@ -89,7 +89,7 @@
     c = makeContext(template, composite, options)
     TALInterpreter(program, {}, c, output, tal=1, strictinsert=0)()
     return output.getvalue()
-    
+
 
 def getRootMacro(template):
     """If the template defines a macro at the root, returns it.




More information about the Zope-CVS mailing list