[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