[Checkins] SVN: CompositePage/trunk/ Moved the code into Products/CompositePage so we can turn this into an egg.
Shane Hathaway
shane at hathawaymix.org
Sat Apr 30 15:15:39 EDT 2011
Log message for revision 121490:
Moved the code into Products/CompositePage so we can turn this into an egg.
Changed:
D CompositePage/trunk/CHANGES.txt
D CompositePage/trunk/COPYRIGHT.txt
D CompositePage/trunk/LICENSE.txt
A CompositePage/trunk/Products/CompositePage/CHANGES.txt
A CompositePage/trunk/Products/CompositePage/COPYRIGHT.txt
A CompositePage/trunk/Products/CompositePage/LICENSE.txt
A CompositePage/trunk/Products/CompositePage/README.txt
A CompositePage/trunk/Products/CompositePage/__init__.py
A CompositePage/trunk/Products/CompositePage/cmf/
A CompositePage/trunk/Products/CompositePage/common/
A CompositePage/trunk/Products/CompositePage/composite.py
A CompositePage/trunk/Products/CompositePage/designuis.py
A CompositePage/trunk/Products/CompositePage/element.py
A CompositePage/trunk/Products/CompositePage/interfaces.py
A CompositePage/trunk/Products/CompositePage/manual/
A CompositePage/trunk/Products/CompositePage/perm_names.py
A CompositePage/trunk/Products/CompositePage/rawfile.py
A CompositePage/trunk/Products/CompositePage/slot.py
A CompositePage/trunk/Products/CompositePage/slotclass.py
A CompositePage/trunk/Products/CompositePage/slotexpr.py
A CompositePage/trunk/Products/CompositePage/tests/
A CompositePage/trunk/Products/CompositePage/tool.py
A CompositePage/trunk/Products/CompositePage/utils.py
A CompositePage/trunk/Products/CompositePage/version.txt
A CompositePage/trunk/Products/CompositePage/www/
A CompositePage/trunk/Products/CompositePage/zmi/
D CompositePage/trunk/README.txt
D CompositePage/trunk/__init__.py
D CompositePage/trunk/cmf/
D CompositePage/trunk/common/
D CompositePage/trunk/composite.py
D CompositePage/trunk/designuis.py
D CompositePage/trunk/element.py
D CompositePage/trunk/interfaces.py
D CompositePage/trunk/manual/
D CompositePage/trunk/perm_names.py
D CompositePage/trunk/rawfile.py
D CompositePage/trunk/slot.py
D CompositePage/trunk/slotclass.py
D CompositePage/trunk/slotexpr.py
D CompositePage/trunk/tests/
D CompositePage/trunk/tool.py
D CompositePage/trunk/utils.py
D CompositePage/trunk/version.txt
D CompositePage/trunk/www/
D CompositePage/trunk/zmi/
-=-
Deleted: CompositePage/trunk/CHANGES.txt
===================================================================
--- CompositePage/trunk/CHANGES.txt 2011-04-30 19:14:59 UTC (rev 121489)
+++ CompositePage/trunk/CHANGES.txt 2011-04-30 19:15:39 UTC (rev 121490)
@@ -1,40 +0,0 @@
-Next Release
-------------
-
-- Fixed test failures caused by 7 years of changes to Zope. This mostly
- involved simple modernization:
-
- - Use zope.interface, zope.tales, and zope.pagetemplate rather than
- their predecessors.
-
- - Use absolute imports.
-
- - Use Unicode where possible.
-
-- Let's stop swallowing errors that occur when rendering the template
- to get the list of slots. Error messages are friendlier than silent
- breakage.
-
-- Slots now show the add element interface only at the top and bottom
- of the slot, not between elements. (Hmm, should this be configurable?)
-
-
-0.2 (2004-06-16)
-----------------
-
-- Changed the UI to use images for elements and targets.
-
-- Added inline views. You can now select templates to render objects.
-
-- Context menus now have headers.
-
-- The clipboard now works.
-
-- You can now define slots in a template using "slot:" expressions.
- This should make it easier to write templates.
-
-- The composite tool now checks copy/paste permissions before making
- any changes.
-
-- Slots now have a __str__ method, making it easy to render them
- directly.
Deleted: CompositePage/trunk/COPYRIGHT.txt
===================================================================
--- CompositePage/trunk/COPYRIGHT.txt 2011-04-30 19:14:59 UTC (rev 121489)
+++ CompositePage/trunk/COPYRIGHT.txt 2011-04-30 19:15:39 UTC (rev 121490)
@@ -1 +0,0 @@
-Zope Foundation and Contributors
\ No newline at end of file
Deleted: CompositePage/trunk/LICENSE.txt
===================================================================
--- CompositePage/trunk/LICENSE.txt 2011-04-30 19:14:59 UTC (rev 121489)
+++ CompositePage/trunk/LICENSE.txt 2011-04-30 19:15:39 UTC (rev 121490)
@@ -1,44 +0,0 @@
-Zope Public License (ZPL) Version 2.1
-
-A copyright notice accompanies this license document that identifies the
-copyright holders.
-
-This license has been certified as open source. It has also been designated as
-GPL compatible by the Free Software Foundation (FSF).
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
-1. Redistributions in source code must retain the accompanying copyright
-notice, this list of conditions, and the following disclaimer.
-
-2. Redistributions in binary form must reproduce the accompanying copyright
-notice, this list of conditions, and the following disclaimer in the
-documentation and/or other materials provided with the distribution.
-
-3. Names of the copyright holders must not be used to endorse or promote
-products derived from this software without prior written permission from the
-copyright holders.
-
-4. The right to distribute this software or to use it for any purpose does not
-give you the right to use Servicemarks (sm) or Trademarks (tm) of the
-copyright
-holders. Use of them is covered by separate agreement with the copyright
-holders.
-
-5. If any files are modified, you must cause the modified files to carry
-prominent notices stating that you changed the files and the date of any
-change.
-
-Disclaimer
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESSED
-OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
-EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
-EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Copied: CompositePage/trunk/Products/CompositePage/CHANGES.txt (from rev 121488, CompositePage/trunk/CHANGES.txt)
===================================================================
--- CompositePage/trunk/Products/CompositePage/CHANGES.txt (rev 0)
+++ CompositePage/trunk/Products/CompositePage/CHANGES.txt 2011-04-30 19:15:39 UTC (rev 121490)
@@ -0,0 +1,40 @@
+Next Release
+------------
+
+- Fixed test failures caused by 7 years of changes to Zope. This mostly
+ involved simple modernization:
+
+ - Use zope.interface, zope.tales, and zope.pagetemplate rather than
+ their predecessors.
+
+ - Use absolute imports.
+
+ - Use Unicode where possible.
+
+- Let's stop swallowing errors that occur when rendering the template
+ to get the list of slots. Error messages are friendlier than silent
+ breakage.
+
+- Slots now show the add element interface only at the top and bottom
+ of the slot, not between elements. (Hmm, should this be configurable?)
+
+
+0.2 (2004-06-16)
+----------------
+
+- Changed the UI to use images for elements and targets.
+
+- Added inline views. You can now select templates to render objects.
+
+- Context menus now have headers.
+
+- The clipboard now works.
+
+- You can now define slots in a template using "slot:" expressions.
+ This should make it easier to write templates.
+
+- The composite tool now checks copy/paste permissions before making
+ any changes.
+
+- Slots now have a __str__ method, making it easy to render them
+ directly.
Copied: CompositePage/trunk/Products/CompositePage/COPYRIGHT.txt (from rev 121488, CompositePage/trunk/COPYRIGHT.txt)
===================================================================
--- CompositePage/trunk/Products/CompositePage/COPYRIGHT.txt (rev 0)
+++ CompositePage/trunk/Products/CompositePage/COPYRIGHT.txt 2011-04-30 19:15:39 UTC (rev 121490)
@@ -0,0 +1 @@
+Zope Foundation and Contributors
\ No newline at end of file
Copied: CompositePage/trunk/Products/CompositePage/LICENSE.txt (from rev 121488, CompositePage/trunk/LICENSE.txt)
===================================================================
--- CompositePage/trunk/Products/CompositePage/LICENSE.txt (rev 0)
+++ CompositePage/trunk/Products/CompositePage/LICENSE.txt 2011-04-30 19:15:39 UTC (rev 121490)
@@ -0,0 +1,44 @@
+Zope Public License (ZPL) Version 2.1
+
+A copyright notice accompanies this license document that identifies the
+copyright holders.
+
+This license has been certified as open source. It has also been designated as
+GPL compatible by the Free Software Foundation (FSF).
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions in source code must retain the accompanying copyright
+notice, this list of conditions, and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the accompanying copyright
+notice, this list of conditions, and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+3. Names of the copyright holders must not be used to endorse or promote
+products derived from this software without prior written permission from the
+copyright holders.
+
+4. The right to distribute this software or to use it for any purpose does not
+give you the right to use Servicemarks (sm) or Trademarks (tm) of the
+copyright
+holders. Use of them is covered by separate agreement with the copyright
+holders.
+
+5. If any files are modified, you must cause the modified files to carry
+prominent notices stating that you changed the files and the date of any
+change.
+
+Disclaimer
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESSED
+OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Copied: CompositePage/trunk/Products/CompositePage/README.txt (from rev 121488, CompositePage/trunk/README.txt)
===================================================================
--- CompositePage/trunk/Products/CompositePage/README.txt (rev 0)
+++ CompositePage/trunk/Products/CompositePage/README.txt 2011-04-30 19:15:39 UTC (rev 121490)
@@ -0,0 +1,205 @@
+
+
+Contents
+========
+
+ - Introduction
+
+ - How to use CompositePage
+
+ - How to write a template
+
+ - How it works
+
+ - Adapting CompositePage to other applications
+
+ - Near-term directions
+
+
+
+Introduction
+============
+
+CompositePage is a new way to assemble pages for the World Wide Web.
+Through the use of Zope technology, browser-based drag and drop, and
+custom context menus, CompositePage makes it easy to visually combine
+page fragments into complete pages.
+
+CompositePage supercedes the PageDesign product and makes use of
+PDLib, a Javascript library. CompositePage is designed for browsers
+that support the DOM (Document Object Model) and CSS (Cascading Style
+Sheets) level 2: Mozilla, Internet Explorer 5+, Opera, Konqueror, etc.
+Not all of these browsers have been tested, but it should be possible
+to solve most problems that occur.
+
+
+
+How to use CompositePage
+========================
+
+Follow these steps:
+
+- Install the CompositePage product in Zope by unpacking the archive
+into your Products directory. I've tested only with a current Zope
+checkout, which is something like Zope 2.7.
+
+- Create a Composite Tool instance in a central location, possibly the
+root folder.
+
+- Create a Composite object. On the creation form, there is a
+checkbox for creating a sample template. Leave the checkbox checked.
+
+- Visit the Composite object and select the "Design" tab. You should
+see a three-column layout with blue dotted lines in the places where
+you are allowed to insert content.
+
+- Click just beneath one of the blue lines. A context menu will pop
+up. Select "Add...".
+
+- You will be directed to a slot (a folderish object.) In slots, you
+can add composite elements. Add a composite element that points to a
+script.
+
+- Find the composite created earlier and select the "Design" tab
+again. Your new object should now show up in the slot.
+
+- Move the object to a different slot using drag and drop. When the
+mouse cursor is hovering over a permitted target (the blue dotted
+lines are targets), the target will be highlighted. Let go and watch
+your object appear in the new place.
+
+- Right-click over your object and select "Delete" from the context
+menu.
+
+
+How to write a template
+=======================
+
+Templates can be any Zope object, but ZPTs (Zope Page Templates) are
+the most common. A template designed for use with composites uses the
+'slots' attribute of the composite. The 'slots' attribute is a
+mapping-like object.
+
+Here is a simple composite-aware page template::
+
+ <html>
+ <head>
+ </head>
+ <body>
+ <div tal:content="structure here/slots/center/single">
+ This will be replaced with one element from one slot.
+ </div>
+ </body>
+ </html>
+
+The expression 'here/slots/center/single' gets the 'slots' attribute
+of the composite, finds a slot named 'center', and calls the single()
+method of the slot, returning a string containing an HTML structure.
+
+The only place you have to name a slot is in the template. If the
+template refers to a slot that does not yet exist, the composite will
+create and return an empty slot. If you place something in that slot
+using the drag and drop interface, the composite will transparently
+add a new slot to the 'filled_slots' folder. Note that Zope prevents
+you from storing slots with names that start with an underscore or
+that clash existing folder attributes.
+
+Templates use either the single() or the multiple() method of a slot.
+single() returns a string, while multiple() returns a list of strings.
+Use single() when you expect the slot to never contain more than one
+element. Use multiple() to allow more than one element. In either
+case, don't forget to use the ZPT 'structure' keyword, since the
+returned strings contain HTML that should not be escaped.
+
+
+
+How it works
+============
+
+Rendering:
+
+When you render (view) a composite, it calls its template. When the
+template refers to a slot, the composite looks for the named slot in
+the filled_slots folder. If it finds the slot, it returns it; if it
+doesn't find it, the composite creates a temporary empty slot. Then
+the template calls either the single() or multiple() method and the
+slot renders and returns its contents.
+
+
+Rendering in edit mode:
+
+When requested, the composite calls upon a "UI" object to render its
+template and slots with edit mode turned on. In edit mode, slots add
+'class', 'source_path', 'target_path', and 'target_index' attributes
+to HTML tags to mark movable objects and available drop targets.
+Slots add HTML markup for drop targets automatically. When rendering
+using the single() method, slots provide a drop target only if the
+slot is empty. When rendering using the multiple() method, slots
+insert drop targets between the elements and to the beginning and end
+of the slot.
+
+The UI object can use various mechanisms to make the page editable.
+Most UI objects use regular expressions to find the 'head' and 'body'
+tags. Then the UI object inserts scripts, styles, and HTML elements.
+The result of the transformation is sent back to the browser.
+
+
+Drag and drop:
+
+At the bottom of a page rendered in edit mode is a call to the
+pd_setupPage() Javascript function. pd_setupPage() searches all of
+the elements on the page, looking for elements with particular 'class'
+attributes. When it finds a 'slot_element', a handler adds event
+listeners to that element that react when the user presses the mouse
+button in that element. When pd_setupPage() finds a 'slot_target',
+another handler adds event listeners that react when the user drags
+into that element.
+
+If the user releases the mouse while dragging into a target, the
+Javascript code puts the appropriate source paths, target paths, and
+target indexes into a hidden form and submits that form to the
+composite tool in Zope. The composite tool moves the elements then
+redirects the browser to the original page. The browser loads the
+page in edit mode again and the moved element gets rendered in its new
+spot.
+
+
+Context menus:
+
+Like drag and drop, context menus depend on pd_setupPage(). When
+pd_setupPage() finds a 'slot_element', a handler adds a context menu
+listener to that element. The context menu listener, when activated,
+positions and displays an otherwise invisible HTML element that looks
+just like a context menu. Once displayed, the user is expected to
+either select an item from the context menu or click outside the
+context menu to make it disappear. A similar process exists for
+'slot_target' elements, but a different invisible HTML element is
+used.
+
+Just before popping up a context menu, its contents are filtered using
+Javascript expressions. Some actions are valid only when the user has
+selected at least one item, and other actions are valid only when
+exactly one item item is selected. Filter expressions provide a way
+to express these constraints.
+
+
+
+Adapting CompositePage to other applications
+============================================
+
+CompositePage provides a default user interface that integrates with
+the Zope management interface, but mechanisms are provided for
+integrating with any user interface. Look at design.py, the 'common'
+subdirectory, and the 'zmi' subdirectory for guidance. Simple
+customizations probably do not require more code than ZMIUI.
+
+
+
+Near-term directions
+====================
+
+I would like CompositePage to work reliably with as many browsers as
+possible, but Mozilla 1.4 is the current reference browser. Try it in
+your preferred browser. If it acts strangely, try the same thing in
+Mozilla 1.4 and send an email describing the differences.
+
Copied: CompositePage/trunk/Products/CompositePage/__init__.py (from rev 121488, CompositePage/trunk/__init__.py)
===================================================================
--- CompositePage/trunk/Products/CompositePage/__init__.py (rev 0)
+++ CompositePage/trunk/Products/CompositePage/__init__.py 2011-04-30 19:15:39 UTC (rev 121490)
@@ -0,0 +1,72 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Foundation and Contributors.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL). A copy of the ZPL 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.
+#
+##############################################################################
+"""CompositePage product initialization.
+
+$Id: __init__.py,v 1.7 2004/03/05 21:41:04 shane Exp $
+"""
+
+import tool, element, composite, slot, slotclass, designuis, interfaces
+import slotexpr
+
+slotexpr.registerSlotExprType()
+
+tool.registerUI("common", designuis.CommonUI())
+tool.registerUI("zmi", designuis.ZMIUI())
+tool.registerUI("cmf", designuis.CMFUI())
+tool.registerUI("manual", designuis.ManualUI())
+
+
+def initialize(context):
+
+ context.registerClass(
+ tool.CompositeTool,
+ constructors=(tool.manage_addCompositeTool,),
+ icon="www/comptool.gif",
+ )
+
+ context.registerClass(
+ element.CompositeElement,
+ constructors=(element.addElementForm,
+ element.manage_addElement,
+ ),
+ visibility=None,
+ icon="www/element.gif",
+ )
+
+ context.registerClass(
+ slotclass.SlotClass,
+ constructors=(slotclass.addSlotClassForm,
+ slotclass.manage_addSlotClass,
+ ),
+ interfaces=(interfaces.ISlotClass,),
+ visibility=None,
+ icon="www/slot.gif",
+ )
+
+ context.registerClass(
+ composite.Composite,
+ constructors=(composite.addCompositeForm,
+ composite.manage_addComposite,
+ ),
+ icon="www/composite.gif",
+ )
+
+ context.registerClass(
+ slot.Slot,
+ constructors=(slot.addSlotForm,
+ slot.manage_addSlot,
+ slot.manage_generateSlots,
+ ),
+ visibility=None,
+ icon="www/slot.gif",
+ )
Copied: CompositePage/trunk/Products/CompositePage/composite.py (from rev 121488, CompositePage/trunk/composite.py)
===================================================================
--- CompositePage/trunk/Products/CompositePage/composite.py (rev 0)
+++ CompositePage/trunk/Products/CompositePage/composite.py 2011-04-30 19:15:39 UTC (rev 121490)
@@ -0,0 +1,367 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Foundation and Contributors.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL). A copy of the ZPL 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.
+#
+##############################################################################
+"""Composite class and supporting code.
+
+$Id: composite.py,v 1.24 2004/04/15 22:13:44 shane Exp $
+"""
+
+import os
+import re
+
+import Globals
+import Acquisition
+from Acquisition import aq_base
+from Acquisition import aq_inner
+from Acquisition import aq_parent
+from Acquisition import aq_get
+from OFS.Folder import Folder
+from OFS.SimpleItem import SimpleItem
+from Products.PageTemplates.PageTemplateFile import PageTemplateFile
+from Products.PageTemplates.ZopePageTemplate import ZopePageTemplate
+from AccessControl import ClassSecurityInfo
+from AccessControl.ZopeGuards import guarded_getattr
+from zope.interface import implements
+
+from Products.CompositePage.interfaces import IComposite
+from Products.CompositePage.interfaces import ISlot
+from Products.CompositePage.interfaces import ISlotGenerator
+from Products.CompositePage.interfaces import CompositeError
+from Products.CompositePage.slot import Slot
+from Products.CompositePage.slot import getIconURL
+from Products.CompositePage.slot import formatException
+from Products.CompositePage.perm_names import view_perm
+from Products.CompositePage.perm_names import change_composites_perm
+
+_www = os.path.join(os.path.dirname(__file__), "www")
+
+
+class SlotGenerator(Acquisition.Explicit):
+ """Automatically makes slots available to the template.
+
+ Note: instances of this class are shared across threads.
+ """
+ _slot_class = Slot
+
+ 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.
+ """
+ name = str(name)
+ composite = aq_parent(aq_inner(self))
+ composite._usingSlot(name, class_name, title)
+ slots = composite.filled_slots
+ if slots.hasObject(name):
+ return slots[name]
+ else:
+ # Generate a new slot.
+ s = self._slot_class(name)
+ if composite.isEditing():
+ # Persist the slot.
+ slots._setObject(s.getId(), s)
+ # else don't persist the slot.
+ return s.__of__(slots)
+
+ __getitem__ = get
+
+
+class CompositeMixin:
+ """Base class for editable composite pages.
+
+ Composite pages are assemblies of templates and composite
+ elements. This base class provides the nuts and bolts of a
+ composite editing interface.
+ """
+ implements(IComposite)
+ meta_type = "Composite"
+
+ security = ClassSecurityInfo()
+
+ manage_options = (
+ {"label": "Design", "action": "manage_designForm",},
+ {"label": "View", "action": "view",},
+ )
+
+ default_ui = "common"
+ template_path = "template"
+ _v_editing = 0
+ _v_rendering = 0
+ _v_slot_specs = None # [{'name', 'class', 'title'}]
+
+ security.declarePublic("slots")
+ slots = SlotGenerator()
+
+ _properties = (
+ {"id": "template_path", "mode": "w", "type": "string",
+ "label": "Path to template"},
+ )
+
+ security.declareProtected(view_perm, "hasTemplate")
+ def hasTemplate(self):
+ if self.template_path:
+ return 1
+ return 0
+
+ security.declareProtected(view_perm, "getTemplate")
+ def getTemplate(self):
+ if not self.template_path:
+ raise CompositeError("No template set")
+ return self.restrictedTraverse(str(self.template_path))
+
+ security.declareProtected(change_composites_perm, "generateSlots")
+ def generateSlots(self):
+ """Creates the slots defined by the template.
+ """
+ self._v_editing = 1
+ try:
+ self()
+ finally:
+ self._v_editing = 0
+
+ security.declareProtected(view_perm, "__call__")
+ def __call__(self):
+ """Renders the composite.
+ """
+ if self._v_rendering:
+ raise CompositeError("Circular composite reference")
+ self._v_rendering = 1
+ try:
+ template = self.getTemplate()
+ return template(composite=self)
+ finally:
+ self._v_rendering = 0
+
+ view = __call__
+
+ index_html = None
+
+ security.declareProtected(change_composites_perm, "design")
+ def design(self, ui=None):
+ """Renders the composite with editing features.
+ """
+ # Never cache a design view.
+ req = getattr(self, "REQUEST", None)
+ if req is not None:
+ req["RESPONSE"].setHeader("Cache-Control", "no-cache")
+ ui_obj = self.getUI(ui)
+ self._v_editing = 1
+ try:
+ return ui_obj.render(self)
+ finally:
+ self._v_editing = 0
+
+ security.declareProtected(change_composites_perm, "manage_designForm")
+ def manage_designForm(self):
+ """Renders the composite with editing and ZMI features.
+ """
+ return self.design("zmi")
+
+ security.declareProtected(change_composites_perm, "getUI")
+ def getUI(self, ui=None):
+ """Returns a UI object.
+ """
+ if not ui:
+ ui = self.default_ui
+ tool = aq_get(self, "composite_tool", None, 1)
+ if tool is None:
+ raise CompositeError("No composite_tool found")
+ return guarded_getattr(tool.uis, ui)
+
+ def _usingSlot(self, name, class_name, title):
+ """Receives notification that the template is using a slot.
+
+ Even though slots are persisted, only the template knows
+ exactly what slots are in use. This callback gives the
+ composite a chance to learn about the slots in use. This is
+ designed for editing purposes, not rendering.
+ """
+ if self._v_slot_specs is not None:
+ # Record the slot spec.
+ self._v_slot_specs.append({
+ 'name': name,
+ 'class_name': class_name,
+ 'title': title,
+ })
+
+ security.declareProtected(change_composites_perm, "getSlotSpecs")
+ def getSlotSpecs(self):
+ """Returns the slot specs within the template.
+
+ Returns [{'name', 'class_name', 'title'}]. May return duplicates.
+ """
+ self._v_editing = 1
+ self._v_slot_specs = []
+ try:
+ self()
+ slots = self._v_slot_specs
+ return slots
+ finally:
+ self._v_editing = 0
+ self._v_slot_specs = None
+
+ security.declareProtected(change_composites_perm, "getSlotClassName")
+ def getSlotClassName(self, slot_name):
+ """Returns the class_name of a slot.
+
+ Returns None if no class is defined for the slot. Raises
+ KeyError if no such slot exists.
+ """
+ specs = self.getSlotSpecs()
+ for spec in specs:
+ if spec['name'] == slot_name:
+ return spec['class_name']
+ raise KeyError(slot_name)
+
+ security.declareProtected(change_composites_perm, "getManifest")
+ def getManifest(self):
+ """Returns a manifest of slot contents.
+
+ Designed for use by page templates that implement a manual
+ slotting user interface.
+ """
+ contents = [] # [{name, slot_info}]
+ seen = {}
+ specs = self.getSlotSpecs()
+ if hasattr(self, 'portal_url'):
+ icon_base_url = self.portal_url()
+ else:
+ request = getattr(self, 'REQUEST', None)
+ if request is not None:
+ icon_base_url = 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
+ seen[name] = 1
+ slot = self.slots[name]
+ elements = []
+ index = 0
+ slot_values = slot.objectValues()
+ for element in slot_values:
+ error = None
+ template = None
+ templates = ()
+ try:
+ ob = element.dereference()
+ template = element.queryInlineTemplate(spec['class_name'])
+ templates = element.listAllowableInlineTemplates(
+ spec['class_name'])
+ except:
+ error = formatException(self, editing=1)
+ ob = FailedElement().__of__(self)
+ icon = getIconURL(ob, icon_base_url)
+ available_templates = []
+ for name, t in templates:
+ if hasattr(aq_base(t), 'title_or_id'):
+ title = t.title_or_id()
+ else:
+ title = name
+ available_templates.append({'id': name, 'title': title})
+ element_info = {
+ 'title': ob.title_or_id(),
+ 'icon': icon,
+ 'error': error,
+ 'source_path': '/'.join(element.getPhysicalPath()),
+ 'index': index,
+ 'next_index': index + 1,
+ 'can_move_up': (index > 0),
+ 'can_move_down': (index < len(slot_values) - 1),
+ 'template': template,
+ 'available_templates': available_templates,
+ }
+ elements.append(element_info)
+ index += 1
+ slot_info = {
+ 'name': name,
+ 'title': spec['title'] or name,
+ 'class_name': spec['class_name'],
+ 'target_path': '/'.join(slot.getPhysicalPath()),
+ 'elements': elements,
+ }
+ contents.append(slot_info)
+ return contents
+
+ security.declareProtected(view_perm, "isEditing")
+ def isEditing(self):
+ """Returns true if currently rendering in design mode.
+ """
+ return self._v_editing
+
+Globals.InitializeClass(CompositeMixin)
+
+
+class SlotCollection(Folder):
+ """Stored collection of composite slots.
+ """
+ meta_type = "Slot Collection"
+
+ def all_meta_types(self):
+ return Folder.all_meta_types(self, interfaces=(ISlot,))
+
+
+class Composite(CompositeMixin, Folder):
+ """An HTML fragment composed from a template and fragments.
+
+ Fragments are stored on a container called 'filled_slots'.
+ """
+
+ manage_options = (
+ Folder.manage_options[:1]
+ + CompositeMixin.manage_options
+ + Folder.manage_options[2:]
+ )
+
+ _properties = Folder._properties + CompositeMixin._properties
+
+ def __init__(self):
+ f = SlotCollection()
+ f._setId("filled_slots")
+ self._setObject(f.getId(), f)
+
+Globals.InitializeClass(Composite)
+
+
+class FailedElement(SimpleItem):
+ meta_type = "Failed Element"
+ icon = 'p_/broken'
+ id = 'error'
+ title = 'Error'
+
+
+addCompositeForm = PageTemplateFile("addCompositeForm.zpt", _www)
+
+def manage_addComposite(dispatcher, id, title="", create_sample="",
+ REQUEST=None):
+ """Adds a composite to a folder.
+ """
+ ob = Composite()
+ ob._setId(id)
+ ob.title = unicode(title)
+ dispatcher._setObject(ob.getId(), ob)
+ if create_sample:
+ ob = dispatcher.this()._getOb(ob.getId())
+ f = open(os.path.join(_www, 'sample_template.zpt'), "rt")
+ try:
+ text = f.read()
+ finally:
+ f.close()
+ pt = ZopePageTemplate(
+ id="template", text=text, content_type="text/html")
+ ob._setObject(pt.getId(), pt)
+ if REQUEST is not None:
+ return dispatcher.manage_main(dispatcher, REQUEST)
Copied: CompositePage/trunk/Products/CompositePage/designuis.py (from rev 121488, CompositePage/trunk/designuis.py)
===================================================================
--- CompositePage/trunk/Products/CompositePage/designuis.py (rev 0)
+++ CompositePage/trunk/Products/CompositePage/designuis.py 2011-04-30 19:15:39 UTC (rev 121490)
@@ -0,0 +1,345 @@
+##############################################################################
+#
+# Copyright (c) 2002 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL). A copy of the ZPL 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.
+#
+##############################################################################
+"""Page design UI classes.
+
+$Id: designuis.py,v 1.10 2004/04/06 16:50:25 shane Exp $
+"""
+
+import os
+import re
+
+import Globals
+from Acquisition import aq_base, aq_inner, aq_parent
+from OFS.SimpleItem import SimpleItem
+from Products.PageTemplates.PageTemplateFile import PageTemplateFile
+from AccessControl import ClassSecurityInfo
+from AccessControl.ZopeGuards import guarded_getattr
+
+from Products.CompositePage.rawfile import RawFile
+from Products.CompositePage.rawfile import InterpolatedFile
+from Products.CompositePage.interfaces import ICompositeElement
+
+
+_common = os.path.join(os.path.dirname(__file__), "common")
+_zmi = os.path.join(os.path.dirname(__file__), "zmi")
+_cmf = os.path.join(os.path.dirname(__file__), "cmf")
+_manual = os.path.join(os.path.dirname(__file__), "manual")
+
+start_of_head_search = re.compile("(<head[^>]*>)", re.IGNORECASE).search
+start_of_body_search = re.compile("(<body[^>]*>)", re.IGNORECASE).search
+end_of_body_search = re.compile("(</body[^>]*>)", re.IGNORECASE).search
+
+default_html_page = """<html>
+<head>
+<title>Composite Page</title>
+</head>
+<body>
+%s
+</body>
+</html>
+"""
+
+close_dialog_html = '''<html>
+<script type="text/javascript">
+if (window.opener)
+ window.opener.location.reload();
+window.close();
+</script>
+</html>
+'''
+
+class CommonUI(SimpleItem):
+ """Basic page design UI.
+
+ Adds editing features to a rendered composite.
+ """
+
+ security = ClassSecurityInfo()
+
+ security.declarePublic(
+ "pdlib_js", "design_js", "pdstyles_css", "designstyles_css")
+ pdlib_js = RawFile("pdlib.js", "text/javascript", _common)
+ edit_js = RawFile("edit.js", "text/javascript", _common)
+ pdstyles_css = RawFile("pdstyles.css", "text/css", _common)
+ editstyles_css = InterpolatedFile("editstyles.css", "text/css", _common)
+ target_image = RawFile("target.gif", "image/gif", _common)
+ target_image_hover = RawFile("target_hover.gif", "image/gif", _common)
+ target_image_active = RawFile("target_active.gif", "image/gif", _common)
+ element_image = RawFile("element.gif", "image/gif", _common)
+
+ header_templates = (PageTemplateFile("header.pt", _common),)
+ top_templates = ()
+ bottom_templates = (PageTemplateFile("bottom.pt", _common),)
+
+ changeTemplateForm = PageTemplateFile("changeTemplateForm.pt", _common)
+
+ workspace_view_name = "view" # To be overridden
+
+ security.declarePublic("getFragments")
+ def getFragments(self, composite):
+ """Returns the fragments to be inserted in design mode.
+ """
+ params = {
+ "tool": aq_parent(aq_inner(aq_parent(aq_inner(self)))),
+ "ui": self,
+ "composite": composite,
+ }
+ header = ""
+ top = ""
+ bottom = ""
+ for t in self.header_templates:
+ header += t.__of__(self)(**params)
+ for t in self.top_templates:
+ top += t.__of__(self)(**params)
+ for t in self.bottom_templates:
+ bottom += t.__of__(self)(**params)
+ return {"header": header, "top": top, "bottom": bottom}
+
+
+ security.declarePrivate("render")
+ def render(self, composite):
+ """Renders a composite, adding scripts and styles.
+ """
+ text = composite()
+ fragments = self.getFragments(composite)
+ match = start_of_head_search(text)
+ if match is None:
+ # Turn it into a page.
+ text = default_html_page % text
+ match = start_of_head_search(text)
+ if match is None:
+ raise CompositeError("Could not find header")
+ if fragments['header']:
+ index = match.end(0)
+ text = "%s%s%s" % (text[:index], fragments['header'], text[index:])
+ if fragments['top']:
+ match = start_of_body_search(text)
+ if match is None:
+ raise CompositeError("No 'body' tag found")
+ index = match.end(0)
+ text = "%s%s%s" % (text[:index], fragments['top'], text[index:])
+ if fragments['bottom']:
+ match = end_of_body_search(text)
+ if match is None:
+ raise CompositeError("No 'body' end tag found")
+ m = match
+ while m is not None:
+ # Find the *last* occurrence of "</body>".
+ match = m
+ m = end_of_body_search(text, match.end(0))
+ index = match.start(0)
+ text = "%s%s%s" % (text[:index], fragments['bottom'], text[index:])
+ return text
+
+
+ security.declarePublic("showElement")
+ def showElement(self, path, RESPONSE):
+ """Redirects to the workspace for an element.
+ """
+ root = self.getPhysicalRoot()
+ obj = root.restrictedTraverse(path)
+ if ICompositeElement.providedBy(obj):
+ obj = obj.dereference()
+ RESPONSE.redirect("%s/%s" % (
+ obj.absolute_url(), self.workspace_view_name))
+
+
+ security.declarePublic("previewElement")
+ def previewElement(self, path, RESPONSE):
+ """Redirects to the preview for an element.
+ """
+ root = self.getPhysicalRoot()
+ obj = root.restrictedTraverse(path)
+ if ICompositeElement.providedBy(obj):
+ obj = obj.dereference()
+ RESPONSE.redirect(obj.absolute_url())
+
+
+ security.declarePublic("showSlot")
+ def showSlot(self, path, RESPONSE):
+ """Redirects to (and possibly creates) the workspace for a slot.
+ """
+ from composite import Composite
+
+ obj = self.getPhysicalRoot()
+ parts = str(path).split('/')
+ for name in parts:
+ obj = obj.restrictedTraverse(name)
+ if IComposite.providedBy(obj):
+ gen = guarded_getattr(obj, "generateSlots")
+ gen()
+ RESPONSE.redirect("%s/%s" % (
+ obj.absolute_url(), self.workspace_view_name))
+
+
+ security.declarePublic("getTemplateChangeInfo")
+ def getTemplateChangeInfo(self, paths):
+ """Returns information for changing the template applied to objects.
+ """
+ root = self.getPhysicalRoot()
+ tool = aq_parent(aq_inner(self))
+ obs = []
+ all_choices = None # {template -> 1}
+ current = None
+ for path in str(paths).split(':'):
+ ob = root.unrestrictedTraverse(path)
+ obs.append(ob)
+ if not ICompositeElement.providedBy(ob):
+ raise ValueError("Not a composite element: %s" % path)
+ m = guarded_getattr(ob, "queryInlineTemplate")
+ template = m()
+ if current is None:
+ current = template
+ elif current and current != template:
+ # The current template isn't the same for all of the elements,
+ # so there is no common current template. Spell this condition
+ # using a non-string value.
+ current = 0
+ m = guarded_getattr(ob, "listAllowableInlineTemplates")
+ templates = m()
+ d = {}
+ for name, template in templates:
+ d[name] = template
+ if all_choices is None:
+ all_choices = d
+ else:
+ for template in all_choices.keys():
+ if not d.has_key(template):
+ del all_choices[template]
+ return {
+ "obs": obs,
+ "templates": all_choices,
+ "current_template": current,
+ }
+
+
+ security.declarePublic("changeTemplate")
+ def changeTemplate(self, paths, template, reload=0, close=1, REQUEST=None):
+ """Changes the template for objects.
+ """
+ info = self.getTemplateChangeInfo(paths)
+ if template not in info["templates"]:
+ raise KeyError("Template %s is not among the choices" % template)
+ tool = aq_parent(aq_inner(self))
+ for ob in info["obs"]:
+ assert ICompositeElement.providedBy(ob)
+ m = guarded_getattr(ob, "setInlineTemplate")
+ m(template)
+ if REQUEST is not None:
+ if reload:
+ REQUEST["RESPONSE"].redirect(REQUEST["HTTP_REFERER"])
+ elif close:
+ return close_dialog_html
+
+Globals.InitializeClass(CommonUI)
+
+
+
+class ZMIUI (CommonUI):
+ """Page design UI meant to fit the Zope management interface.
+
+ Adds editing features to a rendered composite.
+ """
+ security = ClassSecurityInfo()
+
+ workspace_view_name = "manage_workspace"
+
+ security.declarePublic("zmi_edit_js")
+ zmi_edit_js = RawFile("zmi_edit.js", "text/javascript", _zmi)
+
+ header_templates = CommonUI.header_templates + (
+ PageTemplateFile("header.pt", _zmi),)
+ top_templates = CommonUI.top_templates + (
+ PageTemplateFile("top.pt", _zmi),)
+ bottom_templates = (PageTemplateFile("bottom.pt", _zmi),
+ ) + CommonUI.bottom_templates
+
+Globals.InitializeClass(ZMIUI)
+
+
+
+class CMFUI (CommonUI):
+ """Page design UI meant to fit CMF.
+
+ Adds CMF-specific scripts and styles to a page.
+ """
+ security = ClassSecurityInfo()
+
+ workspace_view_name = "view"
+
+ security.declarePublic("cmf_edit_js")
+ cmf_edit_js = RawFile("cmf_edit.js", "text/javascript", _cmf)
+
+ header_templates = CommonUI.header_templates + (
+ PageTemplateFile("header.pt", _cmf),)
+ bottom_templates = (PageTemplateFile("bottom.pt", _cmf),
+ ) + CommonUI.bottom_templates
+
+Globals.InitializeClass(CMFUI)
+
+
+
+class ManualUI (CommonUI):
+ """Non-WYSIWYG page design UI.
+ """
+ security = ClassSecurityInfo()
+
+ body = PageTemplateFile("body.pt", _manual)
+ manual_styles_css = InterpolatedFile(
+ "manual_styles.css", "text/css", _manual)
+ header_templates = (PageTemplateFile("header.pt", _manual),)
+ bottom_templates = CommonUI.bottom_templates + (
+ PageTemplateFile("bottom.pt", _manual),)
+ manual_js = RawFile("manual.js", "text/javascript", _manual)
+ add_icon = RawFile("add.gif", "image/gif", _manual)
+ add_rollover = RawFile("add_rollover.gif", "image/gif", _manual)
+ remove_icon = RawFile("remove.gif", "image/gif", _manual)
+ remove_rollover = RawFile("remove_rollover.gif", "image/gif", _manual)
+ cut_icon = RawFile("cut.gif", "image/gif", _manual)
+ cut_rollover = RawFile("cut_rollover.gif", "image/gif", _manual)
+ copy_icon = RawFile("copy.gif", "image/gif", _manual)
+ copy_rollover = RawFile("copy_rollover.gif", "image/gif", _manual)
+ paste_icon = RawFile("paste.gif", "image/gif", _manual)
+ paste_rollover = RawFile("paste_rollover.gif", "image/gif", _manual)
+
+ security.declarePublic("renderBody")
+ def renderBody(self, composite):
+ """Renders the slotting interface for a composite.
+
+ Returns an HTML fragment without the required scripts and
+ styles.
+ """
+ manifest = composite.getManifest()
+ pt = self.body.__of__(composite)
+ return pt(ui=self, manifest=manifest)
+
+ security.declarePublic("render")
+ def render(self, composite):
+ """Renders a ZMI slotting interface for a composite.
+
+ Returns a full HTML page with scripts and styles.
+ """
+ fragments = self.getFragments(composite)
+ body = self.renderBody(composite)
+ res = []
+ res.append(composite.manage_page_header())
+ res.append(composite.manage_tabs())
+ res.append(fragments["header"])
+ res.append(fragments["top"])
+ res.append(body)
+ res.append(fragments["bottom"])
+ res.append(composite.manage_page_footer())
+ return '\n'.join(res)
+
+Globals.InitializeClass(ManualUI)
Copied: CompositePage/trunk/Products/CompositePage/element.py (from rev 121488, CompositePage/trunk/element.py)
===================================================================
--- CompositePage/trunk/Products/CompositePage/element.py (rev 0)
+++ CompositePage/trunk/Products/CompositePage/element.py 2011-04-30 19:15:39 UTC (rev 121490)
@@ -0,0 +1,116 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Foundation and Contributors.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL). A copy of the ZPL 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.
+#
+##############################################################################
+"""Composite element.
+
+$Id: element.py,v 1.5 2004/04/14 16:15:29 sidnei Exp $
+"""
+
+import os
+
+import Globals
+from AccessControl.SecurityInfo import ClassSecurityInfo
+from Acquisition import aq_get
+from OFS.SimpleItem import SimpleItem
+from OFS.PropertyManager import PropertyManager
+from Products.PageTemplates.PageTemplateFile import PageTemplateFile
+from DocumentTemplate.DT_Util import safe_callable
+
+from zope.interface import implements
+
+from Products.CompositePage.interfaces import ICompositeElement
+
+_www = os.path.join(os.path.dirname(__file__), "www")
+
+
+class CompositeElement(SimpleItem, PropertyManager):
+ """A simple path-based reference to an object and a template.
+
+ You can render it and choose which template to apply for rendering.
+ """
+ implements(ICompositeElement)
+ meta_type = "Composite Element"
+ security = ClassSecurityInfo()
+ manage_options = PropertyManager.manage_options + SimpleItem.manage_options
+
+ _properties = (
+ {'id': 'path', 'type': 'string', 'mode': 'w',},
+ {'id': 'template_name', 'type': 'string', 'mode': 'w',},
+ )
+
+ template_name = ''
+
+ def __init__(self, id, obj):
+ self.id = id
+ self.path = '/'.join(obj.getPhysicalPath())
+
+ def dereference(self):
+ """Returns the object referenced by this composite element.
+ """
+ return self.restrictedTraverse(self.path)
+
+ def renderInline(self):
+ """Returns a representation of this object as a string.
+ """
+ obj = self.dereference()
+ name = self.template_name
+ if not name:
+ # Default to the first allowable inline template.
+ names = self.listAllowableInlineTemplates()
+ if names:
+ name = names[0]
+ if name and name != "call":
+ template = obj.restrictedTraverse(str(name))
+ return template()
+ # Special template name "call" means to call the object.
+ if safe_callable(obj):
+ return obj()
+ return unicode(obj)
+
+ def queryInlineTemplate(self, slot_class_name=None):
+ """Returns the name of the inline template this object uses.
+ """
+ return self.template_name
+
+ def setInlineTemplate(self, template):
+ """Sets the inline template for this object.
+ """
+ self.template_name = str(template)
+
+ def listAllowableInlineTemplates(self, slot_class_name=None):
+ """Returns a list of inline template names allowable for this object.
+ """
+ tool = aq_get(self, "composite_tool", None, 1)
+ if tool is not None:
+ res = []
+ for name in tool.default_inline_templates:
+ template = obj.restrictedTraverse(str(name))
+ res.append((name, template))
+ return res
+ # No tool found, so no inline templates are known.
+ return ()
+
+Globals.InitializeClass(CompositeElement)
+
+
+addElementForm = PageTemplateFile("addElementForm.zpt", _www)
+
+def manage_addElement(dispatcher, id, path, template_name=None, REQUEST=None):
+ """Adds an element to a slot.
+ """
+ target = dispatcher.restrictedTraverse(path)
+ ob = CompositeElement(str(id), target)
+ if template_name:
+ ob.template_name = str(template_name)
+ dispatcher._setObject(ob.getId(), ob)
+ if REQUEST is not None:
+ return dispatcher.manage_main(dispatcher, REQUEST)
Copied: CompositePage/trunk/Products/CompositePage/interfaces.py (from rev 121488, CompositePage/trunk/interfaces.py)
===================================================================
--- CompositePage/trunk/Products/CompositePage/interfaces.py (rev 0)
+++ CompositePage/trunk/Products/CompositePage/interfaces.py 2011-04-30 19:15:39 UTC (rev 121490)
@@ -0,0 +1,127 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Foundation and Contributors.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL). A copy of the ZPL 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.
+#
+##############################################################################
+"""Interfaces and exceptions in the CompositePage product.
+
+$Id: interfaces.py,v 1.14 2004/04/15 22:13:44 shane Exp $
+"""
+
+from zope.interface import Attribute
+from zope.interface import Interface
+
+
+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.
+ """
+
+ def single():
+ """Renders to a string as a single-element slot.
+ """
+
+ def multiple():
+ """Renders to a sequence of strings as a multiple-element slot.
+ """
+
+ def reorder(name, new_index):
+ """Moves an item to a new index.
+ """
+
+ def nullify(name):
+ """Removes an item from the slot, returning the old item.
+
+ Leaves a null element in its place. The null element ensures
+ that other items temporarily keep their index within the slot.
+ """
+
+ def pack():
+ """Removes all null elements from the slot.
+ """
+
+
+class ISlotClass(Interface):
+ """Parameters and constraints for a slot.
+ """
+
+ # inline_templates is an attribute listing allowed template names. If
+ # this list is not empty, it is intersected with the inline templates
+ # provided by the object type to determine what templates are
+ # available. If this list is empty, all inline templates provided by
+ # the object type are available.
+
+ def findAvailableElements():
+ """Returns a list of elements available for this slot.
+
+ Generally returns catalog results.
+ """
+
+
+class ICompositeElement(Interface):
+ """Interface of objects that can be part of a composite.
+ """
+
+ def renderInline():
+ """Returns a representation of this object as a string.
+ """
+
+ def queryInlineTemplate(slot_class_name=None):
+ """Returns the name of the inline template this object uses.
+
+ Returns None if none has been chosen and there is no default.
+
+ The slot_class_name may be provided as an optimization.
+ """
+
+ def setInlineTemplate(template):
+ """Sets the inline template for this object.
+ """
+
+ def listAllowableInlineTemplates(slot_class_name=None):
+ """Returns a list of templates allowable for this object.
+
+ Returns a list of (template_name, template_object).
+
+ The slot_class_name may be provided as an optimization.
+ """
+
+ def dereference():
+ """Returns the object to be rendered.
+ """
Copied: CompositePage/trunk/Products/CompositePage/perm_names.py (from rev 121488, CompositePage/trunk/perm_names.py)
===================================================================
--- CompositePage/trunk/Products/CompositePage/perm_names.py (rev 0)
+++ CompositePage/trunk/Products/CompositePage/perm_names.py 2011-04-30 19:15:39 UTC (rev 121490)
@@ -0,0 +1,21 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL). A copy of the ZPL 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.
+#
+##############################################################################
+"""Names of permissions used by CompositePage.
+
+$Id: perm_names.py,v 1.1 2003/10/01 18:59:31 shane Exp $
+"""
+
+change_composites_perm = "Change Composites"
+view_perm = "View"
+
Copied: CompositePage/trunk/Products/CompositePage/rawfile.py (from rev 121488, CompositePage/trunk/rawfile.py)
===================================================================
--- CompositePage/trunk/Products/CompositePage/rawfile.py (rev 0)
+++ CompositePage/trunk/Products/CompositePage/rawfile.py 2011-04-30 19:15:39 UTC (rev 121490)
@@ -0,0 +1,115 @@
+##############################################################################
+#
+# Copyright (c) 2002 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL). A copy of the ZPL 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.
+#
+##############################################################################
+"""Binary data that is stored in a file.
+
+$Id: rawfile.py,v 1.3 2004/04/03 16:35:32 shane Exp $
+"""
+
+import os
+from os import stat
+from time import time
+
+import Acquisition
+from Acquisition import aq_inner, aq_parent
+import Globals
+from Globals import package_home
+from App.Common import rfc1123_date
+from DateTime import DateTime
+
+
+class RawFile(Acquisition.Explicit):
+ """Binary data stored in external files."""
+
+ def __init__(self, path, content_type, _prefix=None):
+ if _prefix is None:
+ _prefix = SOFTWARE_HOME
+ elif type(_prefix) is not type(''):
+ _prefix = package_home(_prefix)
+ path = os.path.join(_prefix, path)
+ self.path = path
+ self.cch = 'public, max-age=3600' # One hour
+
+ file = open(path, 'rb')
+ data = file.read()
+ file.close()
+ self.content_type = content_type
+ self.__name__ = path.split('/')[-1]
+ self.lmt = float(stat(path)[8]) or time()
+ self.lmh = rfc1123_date(self.lmt)
+
+
+ def __call__(self, REQUEST=None, RESPONSE=None):
+ """Default rendering"""
+ # HTTP If-Modified-Since header handling. This is duplicated
+ # from OFS.Image.Image - it really should be consolidated
+ # somewhere...
+ if RESPONSE is not None:
+ RESPONSE.setHeader('Content-Type', self.content_type)
+ RESPONSE.setHeader('Last-Modified', self.lmh)
+ RESPONSE.setHeader('Cache-Control', self.cch)
+ if REQUEST is not None:
+ header = REQUEST.get_header('If-Modified-Since', None)
+ if header is not None:
+ header = header.split(';')[0]
+ # Some proxies seem to send invalid date strings for this
+ # header. If the date string is not valid, we ignore it
+ # rather than raise an error to be generally consistent
+ # with common servers such as Apache (which can usually
+ # understand the screwy date string as a lucky side effect
+ # of the way they parse it).
+ try:
+ mod_since = long(DateTime(header).timeTime())
+ except:
+ mod_since = None
+ if mod_since is not None:
+ if getattr(self, 'lmt', None):
+ last_mod = long(self.lmt)
+ else:
+ last_mod = long(0)
+ if last_mod > 0 and last_mod <= mod_since:
+ RESPONSE.setStatus(304)
+ return ''
+
+ f = open(self.path, 'rb')
+ data = f.read()
+ f.close()
+ data = self.interp(data)
+ return data
+
+ def interp(self, data):
+ """Hook point for subclasses that modify the file content.
+ """
+ return data
+
+ index_html = None # Tells ZPublisher to use __call__
+
+ HEAD__roles__ = None
+ def HEAD(self, REQUEST, RESPONSE):
+ """ """
+ RESPONSE.setHeader('Content-Type', self.content_type)
+ RESPONSE.setHeader('Last-Modified', self.lmh)
+ return ''
+
+
+class InterpolatedFile(RawFile):
+ """Text data, stored in a file, with %(xxx)s interpolation.
+ """
+
+ def interp(self, data):
+ parent_url = aq_parent(aq_inner(self)).absolute_url()
+ d = {
+ "parent_url": parent_url,
+ }
+ return data % d
+
Copied: CompositePage/trunk/Products/CompositePage/slot.py (from rev 121488, CompositePage/trunk/slot.py)
===================================================================
--- CompositePage/trunk/Products/CompositePage/slot.py (rev 0)
+++ CompositePage/trunk/Products/CompositePage/slot.py 2011-04-30 19:15:39 UTC (rev 121490)
@@ -0,0 +1,271 @@
+##############################################################################
+#
+# Copyright (c) 2002 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL). A copy of the ZPL 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.
+#
+##############################################################################
+"""Slot class and supporting code.
+
+$Id: slot.py,v 1.21 2004/04/26 09:30:25 gotcha Exp $
+"""
+
+import os
+import sys
+from cgi import escape
+
+import Globals
+from Acquisition import aq_base
+from Acquisition import aq_inner
+from Acquisition import aq_parent
+from Acquisition import aq_get
+from ZODB.POSException import ConflictError
+from OFS.SimpleItem import SimpleItem
+from Products.PageTemplates.PageTemplateFile import PageTemplateFile
+from AccessControl import ClassSecurityInfo
+from zLOG import LOG, ERROR
+from zope.interface import implements
+
+from Products.CompositePage.interfaces import ICompositeElement
+from Products.CompositePage.interfaces import ISlot
+from Products.CompositePage.perm_names import view_perm
+from Products.CompositePage.perm_names import change_composites_perm
+
+
+try:
+ # Use OrderedFolder if it's available.
+ from OFS.OrderedFolder import OrderedFolder
+except ImportError:
+ # Fall back to normal folders, which happen to retain order anyway.
+ from OFS.Folder import Folder as OrderedFolder
+
+_www = os.path.join(os.path.dirname(__file__), "www")
+
+target_tag = '''<div class="slot_target" title="Slot: %s [%d]"
+target_path="%s" target_index="%d"></div>'''
+
+edit_tag = '''<div class="slot_element" source_path="%s" icon="%s" title="%s">
+<div class="slot_element_body">%s</div>
+</div>'''
+
+# view_tag includes a <div> just to ensure that the element is
+# rendered as an HTML block in both editing mode and view mode.
+view_tag = '''<div>
+%s
+</div>'''
+
+# error_tag lets the user click on the 'log' link even if the
+# container normally stops clicks.
+error_tag = '''<span class="slot_error">%s
+(<a href="%s" onmousedown="document.location=this.href">log</a>)</span>'''
+
+
+class NullElement(SimpleItem):
+ """Empty placeholder for slot content
+ """
+ meta_type = "Temporary Empty Slot Content"
+
+ def __init__(self, id):
+ self.id = id
+
+
+class Slot(OrderedFolder):
+ """A slot in a composite.
+ """
+ implements(ISlot)
+ meta_type = "Composite Slot"
+
+ security = ClassSecurityInfo()
+
+ null_element = NullElement("null_element")
+
+
+ def __init__(self, id):
+ self.id = id
+
+ def all_meta_types(self):
+ return OrderedFolder.all_meta_types(
+ self, interfaces=(ICompositeElement,))
+
+ security.declareProtected(view_perm, "single")
+ def single(self):
+ """Renders as a single-element slot.
+
+ Attempts to prevent the user from adding multiple elements
+ by not providing insertion points when the slot already
+ contains elements.
+ """
+ allow_add = (not self._objects)
+ return "".join(self.renderToList(allow_add))
+
+ security.declareProtected(view_perm, "multiple")
+ def multiple(self):
+ """Renders as a list containing multiple elements.
+ """
+ return self.renderToList(1)
+
+ def __str__(self):
+ """Renders as a string containing multiple elements.
+ """
+ return "".join(self.renderToList(1))
+
+ __unicode__ = __str__
+
+ security.declareProtected(change_composites_perm, "reorder")
+ def reorder(self, name, new_index):
+ if name not in self.objectIds():
+ raise KeyError, name
+ objs = [info for info in self._objects if info['id'] != name]
+ objs.insert(new_index,
+ {'id': name, 'meta_type': getattr(self, name).meta_type})
+ self._objects = tuple(objs)
+
+ security.declareProtected(change_composites_perm, "nullify")
+ def nullify(self, name):
+ res = self[name]
+ objs = list(self._objects)
+ # Replace the item with a pointer to the null element.
+ for info in objs:
+ if info["id"] == name:
+ info["id"] = "null_element"
+ delattr(self, name)
+ return res
+
+ security.declareProtected(change_composites_perm, "nullify")
+ def pack(self):
+ objs = [info for info in self._objects if info["id"] != "null_element"]
+ self._objects = tuple(objs)
+
+ security.declareProtected(view_perm, "renderToList")
+ def renderToList(self, allow_add):
+ """Renders the items to a list.
+ """
+ res = ['<div class="slot_header"></div>']
+ composite = aq_parent(aq_inner(aq_parent(aq_inner(self))))
+ editing = composite.isEditing()
+ items = self.objectItems()
+ if editing:
+ mypath = escape('/'.join(self.getPhysicalPath()))
+ myid = self.getId()
+ if hasattr(self, 'portal_url'):
+ icon_base_url = self.portal_url()
+ else:
+ request = getattr(self, 'REQUEST', None)
+ if request is not None:
+ icon_base_url = request['BASEPATH1']
+ else:
+ icon_base_url = '/'
+
+ if editing and allow_add:
+ res.append(self._render_add_target(myid, 0, mypath))
+
+ for index in range(len(items)):
+ name, obj = items[index]
+
+ try:
+ assert ICompositeElement.providedBy(obj), (
+ "Not a composite element: %s" % repr(obj))
+ text = obj.renderInline()
+ except ConflictError:
+ # Ugly ZODB requirement: don't catch ConflictErrors
+ raise
+ except:
+ text = formatException(self, editing)
+
+
+ if editing:
+ res.append(self._render_editing(obj, text, icon_base_url))
+ else:
+ res.append(view_tag % text)
+
+ if editing and allow_add:
+ res.append(self._render_add_target(myid, index+1, mypath, obj.getId()))
+
+ return res
+
+ def _render_editing(self, obj, text, icon_base_url):
+ o2 = obj.dereference()
+ icon = getIconURL(o2, icon_base_url)
+ title = o2.title_and_id()
+ path = escape('/'.join(obj.getPhysicalPath()))
+ return edit_tag % (path,
+ escape(icon), escape(title), text)
+
+ def _render_add_target(self, slot_id, index, path, obj_id=''):
+ return target_tag % (slot_id, index, path, index)
+
+Globals.InitializeClass(Slot)
+
+
+def getIconURL(obj, icon_base_url):
+ base = aq_base(obj)
+ if hasattr(base, 'getIcon'):
+ icon = obj.getIcon()
+ elif hasattr(base, 'icon'):
+ icon = obj.icon
+ else:
+ icon = ""
+ if icon and '://' not in icon:
+ if not icon.startswith('/'):
+ icon = '/' + icon
+ icon = icon_base_url + icon
+ return icon
+
+
+def formatException(context, editing):
+ """Returns an HTML-ified error message.
+
+ If not editing, the message includes no details.
+ """
+ exc_info = sys.exc_info()
+ try:
+ if editing:
+ # Show editors the real error
+ t, v = exc_info[:2]
+ t = getattr(t, '__name__', t)
+ msg = "An error occurred. %s" % (
+ escape(('%s: %s' % (t, v))[:80]))
+ else:
+ # Show viewers a simplified error.
+ msg = ("An error occurred while generating "
+ "this part of the page.")
+ try:
+ log = aq_get(context, '__error_log__', None, 1)
+ raising = getattr(log, 'raising', None)
+ except AttributeError:
+ raising = None
+
+ if raising is not None:
+ error_log_url = raising(exc_info)
+ return error_tag % (msg, error_log_url)
+ else:
+ LOG("Composite", ERROR, "Error in a page element",
+ error=exc_info)
+ return msg
+ finally:
+ del exc_info
+
+
+addSlotForm = PageTemplateFile("addSlotForm.zpt", _www)
+
+def manage_addSlot(dispatcher, id, REQUEST=None):
+ """Adds a slot to a composite.
+ """
+ ob = Slot(id)
+ dispatcher._setObject(ob.getId(), ob)
+ if REQUEST is not None:
+ return dispatcher.manage_main(dispatcher, REQUEST)
+
+
+def manage_generateSlots(dispatcher, REQUEST=None):
+ """Adds all slots requested by a template to a composite.
+ """
+ dispatcher.this().generateSlots()
+ if REQUEST is not None:
+ return dispatcher.manage_main(dispatcher, REQUEST)
Copied: CompositePage/trunk/Products/CompositePage/slotclass.py (from rev 121488, CompositePage/trunk/slotclass.py)
===================================================================
--- CompositePage/trunk/Products/CompositePage/slotclass.py (rev 0)
+++ CompositePage/trunk/Products/CompositePage/slotclass.py 2011-04-30 19:15:39 UTC (rev 121490)
@@ -0,0 +1,66 @@
+##############################################################################
+#
+# Copyright (c) 2002 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL). A copy of the ZPL 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.
+#
+##############################################################################
+"""Slot classes.
+
+$Id: slotclass.py,v 1.1 2004/03/02 20:41:44 shane Exp $
+"""
+
+import os
+
+from Acquisition import aq_inner, aq_parent
+from OFS.SimpleItem import SimpleItem
+from OFS.PropertyManager import PropertyManager
+from Products.PageTemplates.PageTemplateFile import PageTemplateFile
+from zope.interface import implements
+
+from Products.CompositePage.interfaces import ISlotClass
+
+
+_www = os.path.join(os.path.dirname(__file__), "www")
+
+
+class SlotClass(SimpleItem, PropertyManager):
+ """Parameters and constraints for a slot.
+ """
+ implements(ISlotClass)
+ meta_type = "Composite Slot Class"
+ find_script = ""
+
+ manage_options = (PropertyManager.manage_options
+ + SimpleItem.manage_options)
+
+ _properties = (
+ {'id': 'find_script', 'mode': 'w', 'type': 'string',
+ 'label': 'Script that finds available elements',},
+ )
+
+ def findAvailableElements(self, slot):
+ if not self.find_script:
+ return None
+ tool = aq_parent(aq_inner(aq_parent(aq_inner(self))))
+ s = tool.restrictedTraverse(self.find_script)
+ return s(slot)
+
+
+addSlotClassForm = PageTemplateFile("addSlotClassForm.zpt", _www)
+
+def manage_addSlotClass(dispatcher, id, REQUEST=None):
+ """Adds a slot class to a composite tool.
+ """
+ ob = SlotClass()
+ ob._setId(id)
+ dispatcher._setObject(ob.getId(), ob)
+ if REQUEST is not None:
+ return dispatcher.manage_main(dispatcher, REQUEST)
+
Copied: CompositePage/trunk/Products/CompositePage/slotexpr.py (from rev 121488, CompositePage/trunk/slotexpr.py)
===================================================================
--- CompositePage/trunk/Products/CompositePage/slotexpr.py (rev 0)
+++ CompositePage/trunk/Products/CompositePage/slotexpr.py 2011-04-30 19:15:39 UTC (rev 121490)
@@ -0,0 +1,105 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Foundation and Contributors.
+#
+# 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.5 2004/05/03 16:02:40 sidnei Exp $
+"""
+
+import logging
+import re
+
+from zope.tales.tales import CompilerError
+
+from Products.CompositePage.interfaces import IComposite
+
+name_re = re.compile("\s*([a-zA-Z][a-zA-Z0-9_]*)")
+class_name_re = re.compile("\s*[(]([a-zA-Z][a-zA-Z0-9_]*)[)]")
+title_re = re.compile("\s*[']([^']+)[']")
+
+log = logging.getLogger(__name__)
+
+
+class SlotExpr(object):
+ """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 = s = expr.strip()
+ mo = name_re.match(s)
+ if mo is None:
+ raise CompilerError('Invalid slot expression "%s"' % s)
+ self._name = mo.group(1)
+ s = s[mo.end():]
+ mo = class_name_re.match(s)
+ if mo is not None:
+ self._class_name = mo.group(1)
+ s = s[mo.end():]
+ else:
+ self._class_name = None
+ mo = title_re.match(s)
+ if mo is not None:
+ self._title = mo.group(1)
+ s = s[mo.end():]
+ else:
+ self._title = None
+ if s.strip():
+ # Can't interpret some of the expression
+ raise CompilerError(
+ 'Slot expression syntax error near %s' % repr(s))
+
+ def __call__(self, econtext):
+ context = econtext.contexts.get('options')
+ if context is None:
+ raise RuntimeError("Could not find options")
+ composite = context.get('composite')
+ if IComposite.providedBy(composite):
+ slot = composite.slots.get(
+ self._name, self._class_name, self._title)
+ # Render the slot
+ return unicode(slot)
+ else:
+ # Show the default content
+ return econtext.getDefault()
+
+ def __repr__(self):
+ return '<SlotExpr %s>' % repr(self._s)
+
+
+def registerSlotExprType():
+ # Register the 'slot:' expression type.
+
+ # Register with Products.PageTemplates.
+ try:
+ from Products.PageTemplates.Expressions import getEngine
+ except ImportError:
+ log.exception("Unable to register the slot expression type")
+ else:
+ engine = getEngine()
+ if not engine.getTypes().has_key('slot'):
+ engine.registerType('slot', SlotExpr)
+
+ # Register with zope.tales.
+ try:
+ from zope.tales.engine import Engine
+ except ImportError:
+ log.exception("Unable to register the slot expression type")
+ else:
+ if not Engine.getTypes().has_key('slot'):
+ Engine.registerType('slot', SlotExpr)
Copied: CompositePage/trunk/Products/CompositePage/tool.py (from rev 121488, CompositePage/trunk/tool.py)
===================================================================
--- CompositePage/trunk/Products/CompositePage/tool.py (rev 0)
+++ CompositePage/trunk/Products/CompositePage/tool.py 2011-04-30 19:15:39 UTC (rev 121490)
@@ -0,0 +1,260 @@
+##############################################################################
+#
+# Copyright (c) 2002 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL). A copy of the ZPL 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.
+#
+##############################################################################
+"""Composite tool.
+
+$Id: tool.py,v 1.11 2004/03/02 20:41:44 shane Exp $
+"""
+
+import Globals
+from Acquisition import aq_base, aq_parent, aq_inner
+from OFS.SimpleItem import SimpleItem
+from OFS.Folder import Folder
+from OFS.CopySupport import _cb_encode, _cb_decode, cookie_path
+from AccessControl import ClassSecurityInfo
+from AccessControl.ZopeGuards import guarded_getattr
+
+from Products.CompositePage.interfaces import ICompositeElement
+from Products.CompositePage.interfaces import ISlot
+from Products.CompositePage.interfaces import ISlotClass
+from Products.CompositePage.interfaces import CompositeError
+from Products.CompositePage.element import CompositeElement
+from Products.CompositePage.utils import copyOf
+
+
+_uis = {}
+
+def registerUI(name, obj):
+ """Registers a page design UI for use with the composite tool.
+ """
+ if _uis.has_key(name):
+ raise RuntimeError("There is already a UI named %s" % name)
+ obj._setId(name)
+ _uis[name] = obj
+
+
+
+class DesignUIs(SimpleItem):
+ """The container of design user interface objects.
+
+ Makes page design UIs accessible through URL traversal.
+ """
+
+ def __init__(self, id):
+ self._setId(id)
+
+ def __getattr__(self, name):
+ try:
+ return _uis[name]
+ except KeyError:
+ raise AttributeError, name
+
+
+class SlotClassFolder(Folder):
+ """Container of slot classes.
+ """
+ meta_type = "Slot Class Folder"
+
+ def all_meta_types(self):
+ return Folder.all_meta_types(self, interfaces=(ISlotClass,))
+
+
+class CompositeTool(Folder):
+ """Page composition helper tool.
+ """
+ meta_type = "Composite Tool"
+ id = "composite_tool"
+
+ security = ClassSecurityInfo()
+
+ security.declarePublic("uis")
+ uis = DesignUIs("uis")
+
+ _properties = Folder._properties + (
+ {'id': 'default_inline_templates', 'mode': 'w', 'type': 'lines',
+ 'label': 'Default inline template names',},
+ )
+
+ default_inline_templates = ()
+
+ _check_security = 1 # Turned off in unit tests
+
+ def __init__(self):
+ scf = SlotClassFolder()
+ scf._setId("slot_classes")
+ self._setObject(scf.id, scf)
+ self._reserved_names = ('slot_classes',)
+
+ security.declarePublic("moveElements")
+ def moveElements(self, source_paths, target_path, target_index, copy=0):
+ """Moves or copies elements to a slot.
+ """
+ target_index = int(target_index)
+ # Coerce the paths to sequences of path elements.
+ if hasattr(target_path, "split"):
+ target_path = target_path.split('/')
+ sources = []
+ for p in source_paths:
+ if hasattr(p, "split"):
+ p = p.split('/')
+ if p:
+ sources.append(p)
+
+ # Ignore descendants when an ancestor is already listed.
+ i = 1
+ sources.sort()
+ while i < len(sources):
+ prev = sources[i - 1]
+ if sources[i][:len(prev)] == prev:
+ del sources[i]
+ else:
+ i = i + 1
+
+ # Prevent parents from becoming their own descendants.
+ for source in sources:
+ if target_path[:len(source)] == source:
+ raise CompositeError(
+ "Can't make an object a descendant of itself")
+
+ # Gather the sources, checking interfaces and security before
+ # making any changes.
+ root = self.getPhysicalRoot()
+ elements = []
+ target = root.restrictedTraverse(target_path)
+ assert ISlot.providedBy(target), repr(target)
+ for source in sources:
+ slot = root.restrictedTraverse(source[:-1])
+ assert ISlot.providedBy(slot), repr(slot)
+ element = slot.restrictedTraverse(source[-1])
+ elements.append(element)
+ if self._check_security:
+ target._verifyObjectPaste(element)
+
+ changed_slots = {} # id(aq_base(slot)) -> slot
+ try:
+ if not copy:
+ # Replace items with nulls to avoid changing indexes
+ # while moving.
+ for source in sources:
+ slot = root.restrictedTraverse(source[:-1])
+ slot_id = id(aq_base(slot))
+ if not changed_slots.has_key(slot_id):
+ changed_slots[slot_id] = slot
+ # Check security
+ nullify = guarded_getattr(slot, "nullify")
+ nullify(source[-1])
+
+ # Add the elements and reorder.
+ for element in elements:
+
+ if not ICompositeElement.providedBy(element):
+ # Make a composite element wrapper.
+ element = CompositeElement(element.getId(), element)
+
+ element = aq_base(element)
+ new_id = target._get_id(element.getId())
+ if copy:
+ element = copyOf(element)
+ element._setId(new_id)
+ target._setObject(new_id, element)
+ # Check security
+ reorder = guarded_getattr(target, "reorder")
+ reorder(new_id, target_index)
+ target_index += 1
+ finally:
+ # Clear the nulls just added.
+ for slot in changed_slots.values():
+ slot.pack()
+
+
+ security.declarePublic("deleteElements")
+ def deleteElements(self, source_paths):
+ sources = []
+ for p in source_paths:
+ if hasattr(p, "split"):
+ p = p.split('/')
+ if p:
+ sources.append(p)
+
+ # Replace with nulls to avoid changing indexes while deleting.
+ orig_slots = {}
+ try:
+ for source in sources:
+ slot = self.restrictedTraverse(source[:-1])
+ assert ISlot.providedBy(slot), repr(slot)
+ slot_id = id(aq_base(slot))
+ if not orig_slots.has_key(slot_id):
+ orig_slots[slot_id] = slot
+ nullify = guarded_getattr(slot, "nullify") # Check security
+ nullify(source[-1])
+ finally:
+ # Clear the nulls just added.
+ for slot in orig_slots.values():
+ slot.pack()
+
+
+ security.declarePublic("moveAndDelete")
+ def moveAndDelete(self, move_source_paths="", move_target_path="",
+ move_target_index="", delete_source_paths="",
+ REQUEST=None):
+ """Move and/or delete elements.
+ """
+ if move_source_paths:
+ p = move_source_paths.split(':')
+ self.moveElements(p, move_target_path, int(move_target_index))
+ if delete_source_paths:
+ p = delete_source_paths.split(':')
+ self.deleteElements(p)
+ if REQUEST is not None:
+ # Return to the page the user was looking at.
+ REQUEST["RESPONSE"].redirect(REQUEST["HTTP_REFERER"])
+
+
+ security.declarePublic("useClipboard")
+ def useClipboard(self, func, REQUEST,
+ source_paths=None, target_path=None, target_index=None):
+ """Clipboard interaction.
+ """
+ resp = REQUEST['RESPONSE']
+ if func in ("cut", "copy"):
+ assert source_paths
+ items = [] # list of path tuples
+ cut = (func == 'cut')
+ for p in str(source_paths).split(':'):
+ items.append(p.split('/'))
+ data = _cb_encode((cut, items))
+ resp.setCookie('__cp', data, path=cookie_path(REQUEST))
+ elif func == 'paste':
+ assert target_path
+ assert target_index
+ assert REQUEST is not None
+ data = REQUEST['__cp']
+ cut, items = _cb_decode(data)
+ self.moveElements(
+ items, target_path, int(target_index), not cut)
+ resp.expireCookie('__cp', path=cookie_path(REQUEST))
+ else:
+ raise ValueError("Clipboard function %s unknown" % func)
+ resp.redirect(REQUEST["HTTP_REFERER"])
+
+Globals.InitializeClass(CompositeTool)
+
+
+def manage_addCompositeTool(dispatcher, REQUEST=None):
+ """Adds a composite tool to a folder.
+ """
+ ob = CompositeTool()
+ dispatcher._setObject(ob.getId(), ob)
+ if REQUEST is not None:
+ return dispatcher.manage_main(dispatcher, REQUEST)
+
Copied: CompositePage/trunk/Products/CompositePage/utils.py (from rev 121488, CompositePage/trunk/utils.py)
===================================================================
--- CompositePage/trunk/Products/CompositePage/utils.py (rev 0)
+++ CompositePage/trunk/Products/CompositePage/utils.py 2011-04-30 19:15:39 UTC (rev 121490)
@@ -0,0 +1,70 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL). A copy of the ZPL 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.
+#
+##############################################################################
+"""Utilities for handling ZODB objects.
+
+(Copied from the Ape product.)
+
+$Id: utils.py,v 1.1 2003/12/28 04:32:47 shane Exp $
+"""
+
+from cStringIO import StringIO
+from cPickle import Pickler, Unpickler
+
+
+def copyOf(source):
+ """Copies a ZODB object, loading subobjects as needed.
+
+ Re-ghostifies objects along the way to save memory.
+ """
+ former_ghosts = []
+ zclass_refs = {}
+
+ def persistent_id(ob, former_ghosts=former_ghosts,
+ zclass_refs=zclass_refs):
+ if getattr(ob, '_p_changed', 0) is None:
+ # Load temporarily.
+ former_ghosts.append(ob)
+ ob._p_changed = 0
+ if hasattr(ob, '__bases__'):
+ m = getattr(ob, '__module__', None)
+ if (m is not None
+ and isinstance(m, basestring)
+ and m.startswith('*')):
+ n = getattr(ob, '__name__', None)
+ if n is not None:
+ # Pickling a ZClass instance. Store the reference to
+ # the ZClass class separately, so that the pickler
+ # and unpickler don't trip over the apparently
+ # missing module.
+ ref = (m, n)
+ zclass_refs[ref] = ob
+ return ref
+ return None
+
+ def persistent_load(ref, zclass_refs=zclass_refs):
+ return zclass_refs[ref]
+
+ stream = StringIO()
+ p = Pickler(stream, 1)
+ p.persistent_id = persistent_id
+ p.dump(source)
+ if former_ghosts:
+ for g in former_ghosts:
+ del g._p_changed
+ del former_ghosts[:]
+ stream.seek(0)
+ u = Unpickler(stream)
+ u.persistent_load = persistent_load
+ return u.load()
+
Copied: CompositePage/trunk/Products/CompositePage/version.txt (from rev 121488, CompositePage/trunk/version.txt)
===================================================================
--- CompositePage/trunk/Products/CompositePage/version.txt (rev 0)
+++ CompositePage/trunk/Products/CompositePage/version.txt 2011-04-30 19:15:39 UTC (rev 121490)
@@ -0,0 +1 @@
+CompositePage-0.2
Deleted: CompositePage/trunk/README.txt
===================================================================
--- CompositePage/trunk/README.txt 2011-04-30 19:14:59 UTC (rev 121489)
+++ CompositePage/trunk/README.txt 2011-04-30 19:15:39 UTC (rev 121490)
@@ -1,205 +0,0 @@
-
-
-Contents
-========
-
- - Introduction
-
- - How to use CompositePage
-
- - How to write a template
-
- - How it works
-
- - Adapting CompositePage to other applications
-
- - Near-term directions
-
-
-
-Introduction
-============
-
-CompositePage is a new way to assemble pages for the World Wide Web.
-Through the use of Zope technology, browser-based drag and drop, and
-custom context menus, CompositePage makes it easy to visually combine
-page fragments into complete pages.
-
-CompositePage supercedes the PageDesign product and makes use of
-PDLib, a Javascript library. CompositePage is designed for browsers
-that support the DOM (Document Object Model) and CSS (Cascading Style
-Sheets) level 2: Mozilla, Internet Explorer 5+, Opera, Konqueror, etc.
-Not all of these browsers have been tested, but it should be possible
-to solve most problems that occur.
-
-
-
-How to use CompositePage
-========================
-
-Follow these steps:
-
-- Install the CompositePage product in Zope by unpacking the archive
-into your Products directory. I've tested only with a current Zope
-checkout, which is something like Zope 2.7.
-
-- Create a Composite Tool instance in a central location, possibly the
-root folder.
-
-- Create a Composite object. On the creation form, there is a
-checkbox for creating a sample template. Leave the checkbox checked.
-
-- Visit the Composite object and select the "Design" tab. You should
-see a three-column layout with blue dotted lines in the places where
-you are allowed to insert content.
-
-- Click just beneath one of the blue lines. A context menu will pop
-up. Select "Add...".
-
-- You will be directed to a slot (a folderish object.) In slots, you
-can add composite elements. Add a composite element that points to a
-script.
-
-- Find the composite created earlier and select the "Design" tab
-again. Your new object should now show up in the slot.
-
-- Move the object to a different slot using drag and drop. When the
-mouse cursor is hovering over a permitted target (the blue dotted
-lines are targets), the target will be highlighted. Let go and watch
-your object appear in the new place.
-
-- Right-click over your object and select "Delete" from the context
-menu.
-
-
-How to write a template
-=======================
-
-Templates can be any Zope object, but ZPTs (Zope Page Templates) are
-the most common. A template designed for use with composites uses the
-'slots' attribute of the composite. The 'slots' attribute is a
-mapping-like object.
-
-Here is a simple composite-aware page template::
-
- <html>
- <head>
- </head>
- <body>
- <div tal:content="structure here/slots/center/single">
- This will be replaced with one element from one slot.
- </div>
- </body>
- </html>
-
-The expression 'here/slots/center/single' gets the 'slots' attribute
-of the composite, finds a slot named 'center', and calls the single()
-method of the slot, returning a string containing an HTML structure.
-
-The only place you have to name a slot is in the template. If the
-template refers to a slot that does not yet exist, the composite will
-create and return an empty slot. If you place something in that slot
-using the drag and drop interface, the composite will transparently
-add a new slot to the 'filled_slots' folder. Note that Zope prevents
-you from storing slots with names that start with an underscore or
-that clash existing folder attributes.
-
-Templates use either the single() or the multiple() method of a slot.
-single() returns a string, while multiple() returns a list of strings.
-Use single() when you expect the slot to never contain more than one
-element. Use multiple() to allow more than one element. In either
-case, don't forget to use the ZPT 'structure' keyword, since the
-returned strings contain HTML that should not be escaped.
-
-
-
-How it works
-============
-
-Rendering:
-
-When you render (view) a composite, it calls its template. When the
-template refers to a slot, the composite looks for the named slot in
-the filled_slots folder. If it finds the slot, it returns it; if it
-doesn't find it, the composite creates a temporary empty slot. Then
-the template calls either the single() or multiple() method and the
-slot renders and returns its contents.
-
-
-Rendering in edit mode:
-
-When requested, the composite calls upon a "UI" object to render its
-template and slots with edit mode turned on. In edit mode, slots add
-'class', 'source_path', 'target_path', and 'target_index' attributes
-to HTML tags to mark movable objects and available drop targets.
-Slots add HTML markup for drop targets automatically. When rendering
-using the single() method, slots provide a drop target only if the
-slot is empty. When rendering using the multiple() method, slots
-insert drop targets between the elements and to the beginning and end
-of the slot.
-
-The UI object can use various mechanisms to make the page editable.
-Most UI objects use regular expressions to find the 'head' and 'body'
-tags. Then the UI object inserts scripts, styles, and HTML elements.
-The result of the transformation is sent back to the browser.
-
-
-Drag and drop:
-
-At the bottom of a page rendered in edit mode is a call to the
-pd_setupPage() Javascript function. pd_setupPage() searches all of
-the elements on the page, looking for elements with particular 'class'
-attributes. When it finds a 'slot_element', a handler adds event
-listeners to that element that react when the user presses the mouse
-button in that element. When pd_setupPage() finds a 'slot_target',
-another handler adds event listeners that react when the user drags
-into that element.
-
-If the user releases the mouse while dragging into a target, the
-Javascript code puts the appropriate source paths, target paths, and
-target indexes into a hidden form and submits that form to the
-composite tool in Zope. The composite tool moves the elements then
-redirects the browser to the original page. The browser loads the
-page in edit mode again and the moved element gets rendered in its new
-spot.
-
-
-Context menus:
-
-Like drag and drop, context menus depend on pd_setupPage(). When
-pd_setupPage() finds a 'slot_element', a handler adds a context menu
-listener to that element. The context menu listener, when activated,
-positions and displays an otherwise invisible HTML element that looks
-just like a context menu. Once displayed, the user is expected to
-either select an item from the context menu or click outside the
-context menu to make it disappear. A similar process exists for
-'slot_target' elements, but a different invisible HTML element is
-used.
-
-Just before popping up a context menu, its contents are filtered using
-Javascript expressions. Some actions are valid only when the user has
-selected at least one item, and other actions are valid only when
-exactly one item item is selected. Filter expressions provide a way
-to express these constraints.
-
-
-
-Adapting CompositePage to other applications
-============================================
-
-CompositePage provides a default user interface that integrates with
-the Zope management interface, but mechanisms are provided for
-integrating with any user interface. Look at design.py, the 'common'
-subdirectory, and the 'zmi' subdirectory for guidance. Simple
-customizations probably do not require more code than ZMIUI.
-
-
-
-Near-term directions
-====================
-
-I would like CompositePage to work reliably with as many browsers as
-possible, but Mozilla 1.4 is the current reference browser. Try it in
-your preferred browser. If it acts strangely, try the same thing in
-Mozilla 1.4 and send an email describing the differences.
-
Deleted: CompositePage/trunk/__init__.py
===================================================================
--- CompositePage/trunk/__init__.py 2011-04-30 19:14:59 UTC (rev 121489)
+++ CompositePage/trunk/__init__.py 2011-04-30 19:15:39 UTC (rev 121490)
@@ -1,72 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2003 Zope Foundation and Contributors.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.0 (ZPL). A copy of the ZPL 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.
-#
-##############################################################################
-"""CompositePage product initialization.
-
-$Id: __init__.py,v 1.7 2004/03/05 21:41:04 shane Exp $
-"""
-
-import tool, element, composite, slot, slotclass, designuis, interfaces
-import slotexpr
-
-slotexpr.registerSlotExprType()
-
-tool.registerUI("common", designuis.CommonUI())
-tool.registerUI("zmi", designuis.ZMIUI())
-tool.registerUI("cmf", designuis.CMFUI())
-tool.registerUI("manual", designuis.ManualUI())
-
-
-def initialize(context):
-
- context.registerClass(
- tool.CompositeTool,
- constructors=(tool.manage_addCompositeTool,),
- icon="www/comptool.gif",
- )
-
- context.registerClass(
- element.CompositeElement,
- constructors=(element.addElementForm,
- element.manage_addElement,
- ),
- visibility=None,
- icon="www/element.gif",
- )
-
- context.registerClass(
- slotclass.SlotClass,
- constructors=(slotclass.addSlotClassForm,
- slotclass.manage_addSlotClass,
- ),
- interfaces=(interfaces.ISlotClass,),
- visibility=None,
- icon="www/slot.gif",
- )
-
- context.registerClass(
- composite.Composite,
- constructors=(composite.addCompositeForm,
- composite.manage_addComposite,
- ),
- icon="www/composite.gif",
- )
-
- context.registerClass(
- slot.Slot,
- constructors=(slot.addSlotForm,
- slot.manage_addSlot,
- slot.manage_generateSlots,
- ),
- visibility=None,
- icon="www/slot.gif",
- )
Deleted: CompositePage/trunk/composite.py
===================================================================
--- CompositePage/trunk/composite.py 2011-04-30 19:14:59 UTC (rev 121489)
+++ CompositePage/trunk/composite.py 2011-04-30 19:15:39 UTC (rev 121490)
@@ -1,367 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2003 Zope Foundation and Contributors.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.0 (ZPL). A copy of the ZPL 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.
-#
-##############################################################################
-"""Composite class and supporting code.
-
-$Id: composite.py,v 1.24 2004/04/15 22:13:44 shane Exp $
-"""
-
-import os
-import re
-
-import Globals
-import Acquisition
-from Acquisition import aq_base
-from Acquisition import aq_inner
-from Acquisition import aq_parent
-from Acquisition import aq_get
-from OFS.Folder import Folder
-from OFS.SimpleItem import SimpleItem
-from Products.PageTemplates.PageTemplateFile import PageTemplateFile
-from Products.PageTemplates.ZopePageTemplate import ZopePageTemplate
-from AccessControl import ClassSecurityInfo
-from AccessControl.ZopeGuards import guarded_getattr
-from zope.interface import implements
-
-from Products.CompositePage.interfaces import IComposite
-from Products.CompositePage.interfaces import ISlot
-from Products.CompositePage.interfaces import ISlotGenerator
-from Products.CompositePage.interfaces import CompositeError
-from Products.CompositePage.slot import Slot
-from Products.CompositePage.slot import getIconURL
-from Products.CompositePage.slot import formatException
-from Products.CompositePage.perm_names import view_perm
-from Products.CompositePage.perm_names import change_composites_perm
-
-_www = os.path.join(os.path.dirname(__file__), "www")
-
-
-class SlotGenerator(Acquisition.Explicit):
- """Automatically makes slots available to the template.
-
- Note: instances of this class are shared across threads.
- """
- _slot_class = Slot
-
- 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.
- """
- name = str(name)
- composite = aq_parent(aq_inner(self))
- composite._usingSlot(name, class_name, title)
- slots = composite.filled_slots
- if slots.hasObject(name):
- return slots[name]
- else:
- # Generate a new slot.
- s = self._slot_class(name)
- if composite.isEditing():
- # Persist the slot.
- slots._setObject(s.getId(), s)
- # else don't persist the slot.
- return s.__of__(slots)
-
- __getitem__ = get
-
-
-class CompositeMixin:
- """Base class for editable composite pages.
-
- Composite pages are assemblies of templates and composite
- elements. This base class provides the nuts and bolts of a
- composite editing interface.
- """
- implements(IComposite)
- meta_type = "Composite"
-
- security = ClassSecurityInfo()
-
- manage_options = (
- {"label": "Design", "action": "manage_designForm",},
- {"label": "View", "action": "view",},
- )
-
- default_ui = "common"
- template_path = "template"
- _v_editing = 0
- _v_rendering = 0
- _v_slot_specs = None # [{'name', 'class', 'title'}]
-
- security.declarePublic("slots")
- slots = SlotGenerator()
-
- _properties = (
- {"id": "template_path", "mode": "w", "type": "string",
- "label": "Path to template"},
- )
-
- security.declareProtected(view_perm, "hasTemplate")
- def hasTemplate(self):
- if self.template_path:
- return 1
- return 0
-
- security.declareProtected(view_perm, "getTemplate")
- def getTemplate(self):
- if not self.template_path:
- raise CompositeError("No template set")
- return self.restrictedTraverse(str(self.template_path))
-
- security.declareProtected(change_composites_perm, "generateSlots")
- def generateSlots(self):
- """Creates the slots defined by the template.
- """
- self._v_editing = 1
- try:
- self()
- finally:
- self._v_editing = 0
-
- security.declareProtected(view_perm, "__call__")
- def __call__(self):
- """Renders the composite.
- """
- if self._v_rendering:
- raise CompositeError("Circular composite reference")
- self._v_rendering = 1
- try:
- template = self.getTemplate()
- return template(composite=self)
- finally:
- self._v_rendering = 0
-
- view = __call__
-
- index_html = None
-
- security.declareProtected(change_composites_perm, "design")
- def design(self, ui=None):
- """Renders the composite with editing features.
- """
- # Never cache a design view.
- req = getattr(self, "REQUEST", None)
- if req is not None:
- req["RESPONSE"].setHeader("Cache-Control", "no-cache")
- ui_obj = self.getUI(ui)
- self._v_editing = 1
- try:
- return ui_obj.render(self)
- finally:
- self._v_editing = 0
-
- security.declareProtected(change_composites_perm, "manage_designForm")
- def manage_designForm(self):
- """Renders the composite with editing and ZMI features.
- """
- return self.design("zmi")
-
- security.declareProtected(change_composites_perm, "getUI")
- def getUI(self, ui=None):
- """Returns a UI object.
- """
- if not ui:
- ui = self.default_ui
- tool = aq_get(self, "composite_tool", None, 1)
- if tool is None:
- raise CompositeError("No composite_tool found")
- return guarded_getattr(tool.uis, ui)
-
- def _usingSlot(self, name, class_name, title):
- """Receives notification that the template is using a slot.
-
- Even though slots are persisted, only the template knows
- exactly what slots are in use. This callback gives the
- composite a chance to learn about the slots in use. This is
- designed for editing purposes, not rendering.
- """
- if self._v_slot_specs is not None:
- # Record the slot spec.
- self._v_slot_specs.append({
- 'name': name,
- 'class_name': class_name,
- 'title': title,
- })
-
- security.declareProtected(change_composites_perm, "getSlotSpecs")
- def getSlotSpecs(self):
- """Returns the slot specs within the template.
-
- Returns [{'name', 'class_name', 'title'}]. May return duplicates.
- """
- self._v_editing = 1
- self._v_slot_specs = []
- try:
- self()
- slots = self._v_slot_specs
- return slots
- finally:
- self._v_editing = 0
- self._v_slot_specs = None
-
- security.declareProtected(change_composites_perm, "getSlotClassName")
- def getSlotClassName(self, slot_name):
- """Returns the class_name of a slot.
-
- Returns None if no class is defined for the slot. Raises
- KeyError if no such slot exists.
- """
- specs = self.getSlotSpecs()
- for spec in specs:
- if spec['name'] == slot_name:
- return spec['class_name']
- raise KeyError(slot_name)
-
- security.declareProtected(change_composites_perm, "getManifest")
- def getManifest(self):
- """Returns a manifest of slot contents.
-
- Designed for use by page templates that implement a manual
- slotting user interface.
- """
- contents = [] # [{name, slot_info}]
- seen = {}
- specs = self.getSlotSpecs()
- if hasattr(self, 'portal_url'):
- icon_base_url = self.portal_url()
- else:
- request = getattr(self, 'REQUEST', None)
- if request is not None:
- icon_base_url = 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
- seen[name] = 1
- slot = self.slots[name]
- elements = []
- index = 0
- slot_values = slot.objectValues()
- for element in slot_values:
- error = None
- template = None
- templates = ()
- try:
- ob = element.dereference()
- template = element.queryInlineTemplate(spec['class_name'])
- templates = element.listAllowableInlineTemplates(
- spec['class_name'])
- except:
- error = formatException(self, editing=1)
- ob = FailedElement().__of__(self)
- icon = getIconURL(ob, icon_base_url)
- available_templates = []
- for name, t in templates:
- if hasattr(aq_base(t), 'title_or_id'):
- title = t.title_or_id()
- else:
- title = name
- available_templates.append({'id': name, 'title': title})
- element_info = {
- 'title': ob.title_or_id(),
- 'icon': icon,
- 'error': error,
- 'source_path': '/'.join(element.getPhysicalPath()),
- 'index': index,
- 'next_index': index + 1,
- 'can_move_up': (index > 0),
- 'can_move_down': (index < len(slot_values) - 1),
- 'template': template,
- 'available_templates': available_templates,
- }
- elements.append(element_info)
- index += 1
- slot_info = {
- 'name': name,
- 'title': spec['title'] or name,
- 'class_name': spec['class_name'],
- 'target_path': '/'.join(slot.getPhysicalPath()),
- 'elements': elements,
- }
- contents.append(slot_info)
- return contents
-
- security.declareProtected(view_perm, "isEditing")
- def isEditing(self):
- """Returns true if currently rendering in design mode.
- """
- return self._v_editing
-
-Globals.InitializeClass(CompositeMixin)
-
-
-class SlotCollection(Folder):
- """Stored collection of composite slots.
- """
- meta_type = "Slot Collection"
-
- def all_meta_types(self):
- return Folder.all_meta_types(self, interfaces=(ISlot,))
-
-
-class Composite(CompositeMixin, Folder):
- """An HTML fragment composed from a template and fragments.
-
- Fragments are stored on a container called 'filled_slots'.
- """
-
- manage_options = (
- Folder.manage_options[:1]
- + CompositeMixin.manage_options
- + Folder.manage_options[2:]
- )
-
- _properties = Folder._properties + CompositeMixin._properties
-
- def __init__(self):
- f = SlotCollection()
- f._setId("filled_slots")
- self._setObject(f.getId(), f)
-
-Globals.InitializeClass(Composite)
-
-
-class FailedElement(SimpleItem):
- meta_type = "Failed Element"
- icon = 'p_/broken'
- id = 'error'
- title = 'Error'
-
-
-addCompositeForm = PageTemplateFile("addCompositeForm.zpt", _www)
-
-def manage_addComposite(dispatcher, id, title="", create_sample="",
- REQUEST=None):
- """Adds a composite to a folder.
- """
- ob = Composite()
- ob._setId(id)
- ob.title = unicode(title)
- dispatcher._setObject(ob.getId(), ob)
- if create_sample:
- ob = dispatcher.this()._getOb(ob.getId())
- f = open(os.path.join(_www, 'sample_template.zpt'), "rt")
- try:
- text = f.read()
- finally:
- f.close()
- pt = ZopePageTemplate(
- id="template", text=text, content_type="text/html")
- ob._setObject(pt.getId(), pt)
- if REQUEST is not None:
- return dispatcher.manage_main(dispatcher, REQUEST)
Deleted: CompositePage/trunk/designuis.py
===================================================================
--- CompositePage/trunk/designuis.py 2011-04-30 19:14:59 UTC (rev 121489)
+++ CompositePage/trunk/designuis.py 2011-04-30 19:15:39 UTC (rev 121490)
@@ -1,345 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2002 Zope Foundation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.0 (ZPL). A copy of the ZPL 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.
-#
-##############################################################################
-"""Page design UI classes.
-
-$Id: designuis.py,v 1.10 2004/04/06 16:50:25 shane Exp $
-"""
-
-import os
-import re
-
-import Globals
-from Acquisition import aq_base, aq_inner, aq_parent
-from OFS.SimpleItem import SimpleItem
-from Products.PageTemplates.PageTemplateFile import PageTemplateFile
-from AccessControl import ClassSecurityInfo
-from AccessControl.ZopeGuards import guarded_getattr
-
-from Products.CompositePage.rawfile import RawFile
-from Products.CompositePage.rawfile import InterpolatedFile
-from Products.CompositePage.interfaces import ICompositeElement
-
-
-_common = os.path.join(os.path.dirname(__file__), "common")
-_zmi = os.path.join(os.path.dirname(__file__), "zmi")
-_cmf = os.path.join(os.path.dirname(__file__), "cmf")
-_manual = os.path.join(os.path.dirname(__file__), "manual")
-
-start_of_head_search = re.compile("(<head[^>]*>)", re.IGNORECASE).search
-start_of_body_search = re.compile("(<body[^>]*>)", re.IGNORECASE).search
-end_of_body_search = re.compile("(</body[^>]*>)", re.IGNORECASE).search
-
-default_html_page = """<html>
-<head>
-<title>Composite Page</title>
-</head>
-<body>
-%s
-</body>
-</html>
-"""
-
-close_dialog_html = '''<html>
-<script type="text/javascript">
-if (window.opener)
- window.opener.location.reload();
-window.close();
-</script>
-</html>
-'''
-
-class CommonUI(SimpleItem):
- """Basic page design UI.
-
- Adds editing features to a rendered composite.
- """
-
- security = ClassSecurityInfo()
-
- security.declarePublic(
- "pdlib_js", "design_js", "pdstyles_css", "designstyles_css")
- pdlib_js = RawFile("pdlib.js", "text/javascript", _common)
- edit_js = RawFile("edit.js", "text/javascript", _common)
- pdstyles_css = RawFile("pdstyles.css", "text/css", _common)
- editstyles_css = InterpolatedFile("editstyles.css", "text/css", _common)
- target_image = RawFile("target.gif", "image/gif", _common)
- target_image_hover = RawFile("target_hover.gif", "image/gif", _common)
- target_image_active = RawFile("target_active.gif", "image/gif", _common)
- element_image = RawFile("element.gif", "image/gif", _common)
-
- header_templates = (PageTemplateFile("header.pt", _common),)
- top_templates = ()
- bottom_templates = (PageTemplateFile("bottom.pt", _common),)
-
- changeTemplateForm = PageTemplateFile("changeTemplateForm.pt", _common)
-
- workspace_view_name = "view" # To be overridden
-
- security.declarePublic("getFragments")
- def getFragments(self, composite):
- """Returns the fragments to be inserted in design mode.
- """
- params = {
- "tool": aq_parent(aq_inner(aq_parent(aq_inner(self)))),
- "ui": self,
- "composite": composite,
- }
- header = ""
- top = ""
- bottom = ""
- for t in self.header_templates:
- header += t.__of__(self)(**params)
- for t in self.top_templates:
- top += t.__of__(self)(**params)
- for t in self.bottom_templates:
- bottom += t.__of__(self)(**params)
- return {"header": header, "top": top, "bottom": bottom}
-
-
- security.declarePrivate("render")
- def render(self, composite):
- """Renders a composite, adding scripts and styles.
- """
- text = composite()
- fragments = self.getFragments(composite)
- match = start_of_head_search(text)
- if match is None:
- # Turn it into a page.
- text = default_html_page % text
- match = start_of_head_search(text)
- if match is None:
- raise CompositeError("Could not find header")
- if fragments['header']:
- index = match.end(0)
- text = "%s%s%s" % (text[:index], fragments['header'], text[index:])
- if fragments['top']:
- match = start_of_body_search(text)
- if match is None:
- raise CompositeError("No 'body' tag found")
- index = match.end(0)
- text = "%s%s%s" % (text[:index], fragments['top'], text[index:])
- if fragments['bottom']:
- match = end_of_body_search(text)
- if match is None:
- raise CompositeError("No 'body' end tag found")
- m = match
- while m is not None:
- # Find the *last* occurrence of "</body>".
- match = m
- m = end_of_body_search(text, match.end(0))
- index = match.start(0)
- text = "%s%s%s" % (text[:index], fragments['bottom'], text[index:])
- return text
-
-
- security.declarePublic("showElement")
- def showElement(self, path, RESPONSE):
- """Redirects to the workspace for an element.
- """
- root = self.getPhysicalRoot()
- obj = root.restrictedTraverse(path)
- if ICompositeElement.providedBy(obj):
- obj = obj.dereference()
- RESPONSE.redirect("%s/%s" % (
- obj.absolute_url(), self.workspace_view_name))
-
-
- security.declarePublic("previewElement")
- def previewElement(self, path, RESPONSE):
- """Redirects to the preview for an element.
- """
- root = self.getPhysicalRoot()
- obj = root.restrictedTraverse(path)
- if ICompositeElement.providedBy(obj):
- obj = obj.dereference()
- RESPONSE.redirect(obj.absolute_url())
-
-
- security.declarePublic("showSlot")
- def showSlot(self, path, RESPONSE):
- """Redirects to (and possibly creates) the workspace for a slot.
- """
- from composite import Composite
-
- obj = self.getPhysicalRoot()
- parts = str(path).split('/')
- for name in parts:
- obj = obj.restrictedTraverse(name)
- if IComposite.providedBy(obj):
- gen = guarded_getattr(obj, "generateSlots")
- gen()
- RESPONSE.redirect("%s/%s" % (
- obj.absolute_url(), self.workspace_view_name))
-
-
- security.declarePublic("getTemplateChangeInfo")
- def getTemplateChangeInfo(self, paths):
- """Returns information for changing the template applied to objects.
- """
- root = self.getPhysicalRoot()
- tool = aq_parent(aq_inner(self))
- obs = []
- all_choices = None # {template -> 1}
- current = None
- for path in str(paths).split(':'):
- ob = root.unrestrictedTraverse(path)
- obs.append(ob)
- if not ICompositeElement.providedBy(ob):
- raise ValueError("Not a composite element: %s" % path)
- m = guarded_getattr(ob, "queryInlineTemplate")
- template = m()
- if current is None:
- current = template
- elif current and current != template:
- # The current template isn't the same for all of the elements,
- # so there is no common current template. Spell this condition
- # using a non-string value.
- current = 0
- m = guarded_getattr(ob, "listAllowableInlineTemplates")
- templates = m()
- d = {}
- for name, template in templates:
- d[name] = template
- if all_choices is None:
- all_choices = d
- else:
- for template in all_choices.keys():
- if not d.has_key(template):
- del all_choices[template]
- return {
- "obs": obs,
- "templates": all_choices,
- "current_template": current,
- }
-
-
- security.declarePublic("changeTemplate")
- def changeTemplate(self, paths, template, reload=0, close=1, REQUEST=None):
- """Changes the template for objects.
- """
- info = self.getTemplateChangeInfo(paths)
- if template not in info["templates"]:
- raise KeyError("Template %s is not among the choices" % template)
- tool = aq_parent(aq_inner(self))
- for ob in info["obs"]:
- assert ICompositeElement.providedBy(ob)
- m = guarded_getattr(ob, "setInlineTemplate")
- m(template)
- if REQUEST is not None:
- if reload:
- REQUEST["RESPONSE"].redirect(REQUEST["HTTP_REFERER"])
- elif close:
- return close_dialog_html
-
-Globals.InitializeClass(CommonUI)
-
-
-
-class ZMIUI (CommonUI):
- """Page design UI meant to fit the Zope management interface.
-
- Adds editing features to a rendered composite.
- """
- security = ClassSecurityInfo()
-
- workspace_view_name = "manage_workspace"
-
- security.declarePublic("zmi_edit_js")
- zmi_edit_js = RawFile("zmi_edit.js", "text/javascript", _zmi)
-
- header_templates = CommonUI.header_templates + (
- PageTemplateFile("header.pt", _zmi),)
- top_templates = CommonUI.top_templates + (
- PageTemplateFile("top.pt", _zmi),)
- bottom_templates = (PageTemplateFile("bottom.pt", _zmi),
- ) + CommonUI.bottom_templates
-
-Globals.InitializeClass(ZMIUI)
-
-
-
-class CMFUI (CommonUI):
- """Page design UI meant to fit CMF.
-
- Adds CMF-specific scripts and styles to a page.
- """
- security = ClassSecurityInfo()
-
- workspace_view_name = "view"
-
- security.declarePublic("cmf_edit_js")
- cmf_edit_js = RawFile("cmf_edit.js", "text/javascript", _cmf)
-
- header_templates = CommonUI.header_templates + (
- PageTemplateFile("header.pt", _cmf),)
- bottom_templates = (PageTemplateFile("bottom.pt", _cmf),
- ) + CommonUI.bottom_templates
-
-Globals.InitializeClass(CMFUI)
-
-
-
-class ManualUI (CommonUI):
- """Non-WYSIWYG page design UI.
- """
- security = ClassSecurityInfo()
-
- body = PageTemplateFile("body.pt", _manual)
- manual_styles_css = InterpolatedFile(
- "manual_styles.css", "text/css", _manual)
- header_templates = (PageTemplateFile("header.pt", _manual),)
- bottom_templates = CommonUI.bottom_templates + (
- PageTemplateFile("bottom.pt", _manual),)
- manual_js = RawFile("manual.js", "text/javascript", _manual)
- add_icon = RawFile("add.gif", "image/gif", _manual)
- add_rollover = RawFile("add_rollover.gif", "image/gif", _manual)
- remove_icon = RawFile("remove.gif", "image/gif", _manual)
- remove_rollover = RawFile("remove_rollover.gif", "image/gif", _manual)
- cut_icon = RawFile("cut.gif", "image/gif", _manual)
- cut_rollover = RawFile("cut_rollover.gif", "image/gif", _manual)
- copy_icon = RawFile("copy.gif", "image/gif", _manual)
- copy_rollover = RawFile("copy_rollover.gif", "image/gif", _manual)
- paste_icon = RawFile("paste.gif", "image/gif", _manual)
- paste_rollover = RawFile("paste_rollover.gif", "image/gif", _manual)
-
- security.declarePublic("renderBody")
- def renderBody(self, composite):
- """Renders the slotting interface for a composite.
-
- Returns an HTML fragment without the required scripts and
- styles.
- """
- manifest = composite.getManifest()
- pt = self.body.__of__(composite)
- return pt(ui=self, manifest=manifest)
-
- security.declarePublic("render")
- def render(self, composite):
- """Renders a ZMI slotting interface for a composite.
-
- Returns a full HTML page with scripts and styles.
- """
- fragments = self.getFragments(composite)
- body = self.renderBody(composite)
- res = []
- res.append(composite.manage_page_header())
- res.append(composite.manage_tabs())
- res.append(fragments["header"])
- res.append(fragments["top"])
- res.append(body)
- res.append(fragments["bottom"])
- res.append(composite.manage_page_footer())
- return '\n'.join(res)
-
-Globals.InitializeClass(ManualUI)
Deleted: CompositePage/trunk/element.py
===================================================================
--- CompositePage/trunk/element.py 2011-04-30 19:14:59 UTC (rev 121489)
+++ CompositePage/trunk/element.py 2011-04-30 19:15:39 UTC (rev 121490)
@@ -1,116 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2003 Zope Foundation and Contributors.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.0 (ZPL). A copy of the ZPL 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.
-#
-##############################################################################
-"""Composite element.
-
-$Id: element.py,v 1.5 2004/04/14 16:15:29 sidnei Exp $
-"""
-
-import os
-
-import Globals
-from AccessControl.SecurityInfo import ClassSecurityInfo
-from Acquisition import aq_get
-from OFS.SimpleItem import SimpleItem
-from OFS.PropertyManager import PropertyManager
-from Products.PageTemplates.PageTemplateFile import PageTemplateFile
-from DocumentTemplate.DT_Util import safe_callable
-
-from zope.interface import implements
-
-from Products.CompositePage.interfaces import ICompositeElement
-
-_www = os.path.join(os.path.dirname(__file__), "www")
-
-
-class CompositeElement(SimpleItem, PropertyManager):
- """A simple path-based reference to an object and a template.
-
- You can render it and choose which template to apply for rendering.
- """
- implements(ICompositeElement)
- meta_type = "Composite Element"
- security = ClassSecurityInfo()
- manage_options = PropertyManager.manage_options + SimpleItem.manage_options
-
- _properties = (
- {'id': 'path', 'type': 'string', 'mode': 'w',},
- {'id': 'template_name', 'type': 'string', 'mode': 'w',},
- )
-
- template_name = ''
-
- def __init__(self, id, obj):
- self.id = id
- self.path = '/'.join(obj.getPhysicalPath())
-
- def dereference(self):
- """Returns the object referenced by this composite element.
- """
- return self.restrictedTraverse(self.path)
-
- def renderInline(self):
- """Returns a representation of this object as a string.
- """
- obj = self.dereference()
- name = self.template_name
- if not name:
- # Default to the first allowable inline template.
- names = self.listAllowableInlineTemplates()
- if names:
- name = names[0]
- if name and name != "call":
- template = obj.restrictedTraverse(str(name))
- return template()
- # Special template name "call" means to call the object.
- if safe_callable(obj):
- return obj()
- return unicode(obj)
-
- def queryInlineTemplate(self, slot_class_name=None):
- """Returns the name of the inline template this object uses.
- """
- return self.template_name
-
- def setInlineTemplate(self, template):
- """Sets the inline template for this object.
- """
- self.template_name = str(template)
-
- def listAllowableInlineTemplates(self, slot_class_name=None):
- """Returns a list of inline template names allowable for this object.
- """
- tool = aq_get(self, "composite_tool", None, 1)
- if tool is not None:
- res = []
- for name in tool.default_inline_templates:
- template = obj.restrictedTraverse(str(name))
- res.append((name, template))
- return res
- # No tool found, so no inline templates are known.
- return ()
-
-Globals.InitializeClass(CompositeElement)
-
-
-addElementForm = PageTemplateFile("addElementForm.zpt", _www)
-
-def manage_addElement(dispatcher, id, path, template_name=None, REQUEST=None):
- """Adds an element to a slot.
- """
- target = dispatcher.restrictedTraverse(path)
- ob = CompositeElement(str(id), target)
- if template_name:
- ob.template_name = str(template_name)
- dispatcher._setObject(ob.getId(), ob)
- if REQUEST is not None:
- return dispatcher.manage_main(dispatcher, REQUEST)
Deleted: CompositePage/trunk/interfaces.py
===================================================================
--- CompositePage/trunk/interfaces.py 2011-04-30 19:14:59 UTC (rev 121489)
+++ CompositePage/trunk/interfaces.py 2011-04-30 19:15:39 UTC (rev 121490)
@@ -1,127 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2003 Zope Foundation and Contributors.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.0 (ZPL). A copy of the ZPL 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.
-#
-##############################################################################
-"""Interfaces and exceptions in the CompositePage product.
-
-$Id: interfaces.py,v 1.14 2004/04/15 22:13:44 shane Exp $
-"""
-
-from zope.interface import Attribute
-from zope.interface import Interface
-
-
-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.
- """
-
- def single():
- """Renders to a string as a single-element slot.
- """
-
- def multiple():
- """Renders to a sequence of strings as a multiple-element slot.
- """
-
- def reorder(name, new_index):
- """Moves an item to a new index.
- """
-
- def nullify(name):
- """Removes an item from the slot, returning the old item.
-
- Leaves a null element in its place. The null element ensures
- that other items temporarily keep their index within the slot.
- """
-
- def pack():
- """Removes all null elements from the slot.
- """
-
-
-class ISlotClass(Interface):
- """Parameters and constraints for a slot.
- """
-
- # inline_templates is an attribute listing allowed template names. If
- # this list is not empty, it is intersected with the inline templates
- # provided by the object type to determine what templates are
- # available. If this list is empty, all inline templates provided by
- # the object type are available.
-
- def findAvailableElements():
- """Returns a list of elements available for this slot.
-
- Generally returns catalog results.
- """
-
-
-class ICompositeElement(Interface):
- """Interface of objects that can be part of a composite.
- """
-
- def renderInline():
- """Returns a representation of this object as a string.
- """
-
- def queryInlineTemplate(slot_class_name=None):
- """Returns the name of the inline template this object uses.
-
- Returns None if none has been chosen and there is no default.
-
- The slot_class_name may be provided as an optimization.
- """
-
- def setInlineTemplate(template):
- """Sets the inline template for this object.
- """
-
- def listAllowableInlineTemplates(slot_class_name=None):
- """Returns a list of templates allowable for this object.
-
- Returns a list of (template_name, template_object).
-
- The slot_class_name may be provided as an optimization.
- """
-
- def dereference():
- """Returns the object to be rendered.
- """
Deleted: CompositePage/trunk/perm_names.py
===================================================================
--- CompositePage/trunk/perm_names.py 2011-04-30 19:14:59 UTC (rev 121489)
+++ CompositePage/trunk/perm_names.py 2011-04-30 19:15:39 UTC (rev 121490)
@@ -1,21 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2003 Zope Foundation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.0 (ZPL). A copy of the ZPL 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.
-#
-##############################################################################
-"""Names of permissions used by CompositePage.
-
-$Id: perm_names.py,v 1.1 2003/10/01 18:59:31 shane Exp $
-"""
-
-change_composites_perm = "Change Composites"
-view_perm = "View"
-
Deleted: CompositePage/trunk/rawfile.py
===================================================================
--- CompositePage/trunk/rawfile.py 2011-04-30 19:14:59 UTC (rev 121489)
+++ CompositePage/trunk/rawfile.py 2011-04-30 19:15:39 UTC (rev 121490)
@@ -1,115 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2002 Zope Foundation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.0 (ZPL). A copy of the ZPL 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.
-#
-##############################################################################
-"""Binary data that is stored in a file.
-
-$Id: rawfile.py,v 1.3 2004/04/03 16:35:32 shane Exp $
-"""
-
-import os
-from os import stat
-from time import time
-
-import Acquisition
-from Acquisition import aq_inner, aq_parent
-import Globals
-from Globals import package_home
-from App.Common import rfc1123_date
-from DateTime import DateTime
-
-
-class RawFile(Acquisition.Explicit):
- """Binary data stored in external files."""
-
- def __init__(self, path, content_type, _prefix=None):
- if _prefix is None:
- _prefix = SOFTWARE_HOME
- elif type(_prefix) is not type(''):
- _prefix = package_home(_prefix)
- path = os.path.join(_prefix, path)
- self.path = path
- self.cch = 'public, max-age=3600' # One hour
-
- file = open(path, 'rb')
- data = file.read()
- file.close()
- self.content_type = content_type
- self.__name__ = path.split('/')[-1]
- self.lmt = float(stat(path)[8]) or time()
- self.lmh = rfc1123_date(self.lmt)
-
-
- def __call__(self, REQUEST=None, RESPONSE=None):
- """Default rendering"""
- # HTTP If-Modified-Since header handling. This is duplicated
- # from OFS.Image.Image - it really should be consolidated
- # somewhere...
- if RESPONSE is not None:
- RESPONSE.setHeader('Content-Type', self.content_type)
- RESPONSE.setHeader('Last-Modified', self.lmh)
- RESPONSE.setHeader('Cache-Control', self.cch)
- if REQUEST is not None:
- header = REQUEST.get_header('If-Modified-Since', None)
- if header is not None:
- header = header.split(';')[0]
- # Some proxies seem to send invalid date strings for this
- # header. If the date string is not valid, we ignore it
- # rather than raise an error to be generally consistent
- # with common servers such as Apache (which can usually
- # understand the screwy date string as a lucky side effect
- # of the way they parse it).
- try:
- mod_since = long(DateTime(header).timeTime())
- except:
- mod_since = None
- if mod_since is not None:
- if getattr(self, 'lmt', None):
- last_mod = long(self.lmt)
- else:
- last_mod = long(0)
- if last_mod > 0 and last_mod <= mod_since:
- RESPONSE.setStatus(304)
- return ''
-
- f = open(self.path, 'rb')
- data = f.read()
- f.close()
- data = self.interp(data)
- return data
-
- def interp(self, data):
- """Hook point for subclasses that modify the file content.
- """
- return data
-
- index_html = None # Tells ZPublisher to use __call__
-
- HEAD__roles__ = None
- def HEAD(self, REQUEST, RESPONSE):
- """ """
- RESPONSE.setHeader('Content-Type', self.content_type)
- RESPONSE.setHeader('Last-Modified', self.lmh)
- return ''
-
-
-class InterpolatedFile(RawFile):
- """Text data, stored in a file, with %(xxx)s interpolation.
- """
-
- def interp(self, data):
- parent_url = aq_parent(aq_inner(self)).absolute_url()
- d = {
- "parent_url": parent_url,
- }
- return data % d
-
Deleted: CompositePage/trunk/slot.py
===================================================================
--- CompositePage/trunk/slot.py 2011-04-30 19:14:59 UTC (rev 121489)
+++ CompositePage/trunk/slot.py 2011-04-30 19:15:39 UTC (rev 121490)
@@ -1,271 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2002 Zope Foundation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.0 (ZPL). A copy of the ZPL 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.
-#
-##############################################################################
-"""Slot class and supporting code.
-
-$Id: slot.py,v 1.21 2004/04/26 09:30:25 gotcha Exp $
-"""
-
-import os
-import sys
-from cgi import escape
-
-import Globals
-from Acquisition import aq_base
-from Acquisition import aq_inner
-from Acquisition import aq_parent
-from Acquisition import aq_get
-from ZODB.POSException import ConflictError
-from OFS.SimpleItem import SimpleItem
-from Products.PageTemplates.PageTemplateFile import PageTemplateFile
-from AccessControl import ClassSecurityInfo
-from zLOG import LOG, ERROR
-from zope.interface import implements
-
-from Products.CompositePage.interfaces import ICompositeElement
-from Products.CompositePage.interfaces import ISlot
-from Products.CompositePage.perm_names import view_perm
-from Products.CompositePage.perm_names import change_composites_perm
-
-
-try:
- # Use OrderedFolder if it's available.
- from OFS.OrderedFolder import OrderedFolder
-except ImportError:
- # Fall back to normal folders, which happen to retain order anyway.
- from OFS.Folder import Folder as OrderedFolder
-
-_www = os.path.join(os.path.dirname(__file__), "www")
-
-target_tag = '''<div class="slot_target" title="Slot: %s [%d]"
-target_path="%s" target_index="%d"></div>'''
-
-edit_tag = '''<div class="slot_element" source_path="%s" icon="%s" title="%s">
-<div class="slot_element_body">%s</div>
-</div>'''
-
-# view_tag includes a <div> just to ensure that the element is
-# rendered as an HTML block in both editing mode and view mode.
-view_tag = '''<div>
-%s
-</div>'''
-
-# error_tag lets the user click on the 'log' link even if the
-# container normally stops clicks.
-error_tag = '''<span class="slot_error">%s
-(<a href="%s" onmousedown="document.location=this.href">log</a>)</span>'''
-
-
-class NullElement(SimpleItem):
- """Empty placeholder for slot content
- """
- meta_type = "Temporary Empty Slot Content"
-
- def __init__(self, id):
- self.id = id
-
-
-class Slot(OrderedFolder):
- """A slot in a composite.
- """
- implements(ISlot)
- meta_type = "Composite Slot"
-
- security = ClassSecurityInfo()
-
- null_element = NullElement("null_element")
-
-
- def __init__(self, id):
- self.id = id
-
- def all_meta_types(self):
- return OrderedFolder.all_meta_types(
- self, interfaces=(ICompositeElement,))
-
- security.declareProtected(view_perm, "single")
- def single(self):
- """Renders as a single-element slot.
-
- Attempts to prevent the user from adding multiple elements
- by not providing insertion points when the slot already
- contains elements.
- """
- allow_add = (not self._objects)
- return "".join(self.renderToList(allow_add))
-
- security.declareProtected(view_perm, "multiple")
- def multiple(self):
- """Renders as a list containing multiple elements.
- """
- return self.renderToList(1)
-
- def __str__(self):
- """Renders as a string containing multiple elements.
- """
- return "".join(self.renderToList(1))
-
- __unicode__ = __str__
-
- security.declareProtected(change_composites_perm, "reorder")
- def reorder(self, name, new_index):
- if name not in self.objectIds():
- raise KeyError, name
- objs = [info for info in self._objects if info['id'] != name]
- objs.insert(new_index,
- {'id': name, 'meta_type': getattr(self, name).meta_type})
- self._objects = tuple(objs)
-
- security.declareProtected(change_composites_perm, "nullify")
- def nullify(self, name):
- res = self[name]
- objs = list(self._objects)
- # Replace the item with a pointer to the null element.
- for info in objs:
- if info["id"] == name:
- info["id"] = "null_element"
- delattr(self, name)
- return res
-
- security.declareProtected(change_composites_perm, "nullify")
- def pack(self):
- objs = [info for info in self._objects if info["id"] != "null_element"]
- self._objects = tuple(objs)
-
- security.declareProtected(view_perm, "renderToList")
- def renderToList(self, allow_add):
- """Renders the items to a list.
- """
- res = ['<div class="slot_header"></div>']
- composite = aq_parent(aq_inner(aq_parent(aq_inner(self))))
- editing = composite.isEditing()
- items = self.objectItems()
- if editing:
- mypath = escape('/'.join(self.getPhysicalPath()))
- myid = self.getId()
- if hasattr(self, 'portal_url'):
- icon_base_url = self.portal_url()
- else:
- request = getattr(self, 'REQUEST', None)
- if request is not None:
- icon_base_url = request['BASEPATH1']
- else:
- icon_base_url = '/'
-
- if editing and allow_add:
- res.append(self._render_add_target(myid, 0, mypath))
-
- for index in range(len(items)):
- name, obj = items[index]
-
- try:
- assert ICompositeElement.providedBy(obj), (
- "Not a composite element: %s" % repr(obj))
- text = obj.renderInline()
- except ConflictError:
- # Ugly ZODB requirement: don't catch ConflictErrors
- raise
- except:
- text = formatException(self, editing)
-
-
- if editing:
- res.append(self._render_editing(obj, text, icon_base_url))
- else:
- res.append(view_tag % text)
-
- if editing and allow_add:
- res.append(self._render_add_target(myid, index+1, mypath, obj.getId()))
-
- return res
-
- def _render_editing(self, obj, text, icon_base_url):
- o2 = obj.dereference()
- icon = getIconURL(o2, icon_base_url)
- title = o2.title_and_id()
- path = escape('/'.join(obj.getPhysicalPath()))
- return edit_tag % (path,
- escape(icon), escape(title), text)
-
- def _render_add_target(self, slot_id, index, path, obj_id=''):
- return target_tag % (slot_id, index, path, index)
-
-Globals.InitializeClass(Slot)
-
-
-def getIconURL(obj, icon_base_url):
- base = aq_base(obj)
- if hasattr(base, 'getIcon'):
- icon = obj.getIcon()
- elif hasattr(base, 'icon'):
- icon = obj.icon
- else:
- icon = ""
- if icon and '://' not in icon:
- if not icon.startswith('/'):
- icon = '/' + icon
- icon = icon_base_url + icon
- return icon
-
-
-def formatException(context, editing):
- """Returns an HTML-ified error message.
-
- If not editing, the message includes no details.
- """
- exc_info = sys.exc_info()
- try:
- if editing:
- # Show editors the real error
- t, v = exc_info[:2]
- t = getattr(t, '__name__', t)
- msg = "An error occurred. %s" % (
- escape(('%s: %s' % (t, v))[:80]))
- else:
- # Show viewers a simplified error.
- msg = ("An error occurred while generating "
- "this part of the page.")
- try:
- log = aq_get(context, '__error_log__', None, 1)
- raising = getattr(log, 'raising', None)
- except AttributeError:
- raising = None
-
- if raising is not None:
- error_log_url = raising(exc_info)
- return error_tag % (msg, error_log_url)
- else:
- LOG("Composite", ERROR, "Error in a page element",
- error=exc_info)
- return msg
- finally:
- del exc_info
-
-
-addSlotForm = PageTemplateFile("addSlotForm.zpt", _www)
-
-def manage_addSlot(dispatcher, id, REQUEST=None):
- """Adds a slot to a composite.
- """
- ob = Slot(id)
- dispatcher._setObject(ob.getId(), ob)
- if REQUEST is not None:
- return dispatcher.manage_main(dispatcher, REQUEST)
-
-
-def manage_generateSlots(dispatcher, REQUEST=None):
- """Adds all slots requested by a template to a composite.
- """
- dispatcher.this().generateSlots()
- if REQUEST is not None:
- return dispatcher.manage_main(dispatcher, REQUEST)
Deleted: CompositePage/trunk/slotclass.py
===================================================================
--- CompositePage/trunk/slotclass.py 2011-04-30 19:14:59 UTC (rev 121489)
+++ CompositePage/trunk/slotclass.py 2011-04-30 19:15:39 UTC (rev 121490)
@@ -1,66 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2002 Zope Foundation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.0 (ZPL). A copy of the ZPL 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.
-#
-##############################################################################
-"""Slot classes.
-
-$Id: slotclass.py,v 1.1 2004/03/02 20:41:44 shane Exp $
-"""
-
-import os
-
-from Acquisition import aq_inner, aq_parent
-from OFS.SimpleItem import SimpleItem
-from OFS.PropertyManager import PropertyManager
-from Products.PageTemplates.PageTemplateFile import PageTemplateFile
-from zope.interface import implements
-
-from Products.CompositePage.interfaces import ISlotClass
-
-
-_www = os.path.join(os.path.dirname(__file__), "www")
-
-
-class SlotClass(SimpleItem, PropertyManager):
- """Parameters and constraints for a slot.
- """
- implements(ISlotClass)
- meta_type = "Composite Slot Class"
- find_script = ""
-
- manage_options = (PropertyManager.manage_options
- + SimpleItem.manage_options)
-
- _properties = (
- {'id': 'find_script', 'mode': 'w', 'type': 'string',
- 'label': 'Script that finds available elements',},
- )
-
- def findAvailableElements(self, slot):
- if not self.find_script:
- return None
- tool = aq_parent(aq_inner(aq_parent(aq_inner(self))))
- s = tool.restrictedTraverse(self.find_script)
- return s(slot)
-
-
-addSlotClassForm = PageTemplateFile("addSlotClassForm.zpt", _www)
-
-def manage_addSlotClass(dispatcher, id, REQUEST=None):
- """Adds a slot class to a composite tool.
- """
- ob = SlotClass()
- ob._setId(id)
- dispatcher._setObject(ob.getId(), ob)
- if REQUEST is not None:
- return dispatcher.manage_main(dispatcher, REQUEST)
-
Deleted: CompositePage/trunk/slotexpr.py
===================================================================
--- CompositePage/trunk/slotexpr.py 2011-04-30 19:14:59 UTC (rev 121489)
+++ CompositePage/trunk/slotexpr.py 2011-04-30 19:15:39 UTC (rev 121490)
@@ -1,105 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2003 Zope Foundation and Contributors.
-#
-# 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.5 2004/05/03 16:02:40 sidnei Exp $
-"""
-
-import logging
-import re
-
-from zope.tales.tales import CompilerError
-
-from Products.CompositePage.interfaces import IComposite
-
-name_re = re.compile("\s*([a-zA-Z][a-zA-Z0-9_]*)")
-class_name_re = re.compile("\s*[(]([a-zA-Z][a-zA-Z0-9_]*)[)]")
-title_re = re.compile("\s*[']([^']+)[']")
-
-log = logging.getLogger(__name__)
-
-
-class SlotExpr(object):
- """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 = s = expr.strip()
- mo = name_re.match(s)
- if mo is None:
- raise CompilerError('Invalid slot expression "%s"' % s)
- self._name = mo.group(1)
- s = s[mo.end():]
- mo = class_name_re.match(s)
- if mo is not None:
- self._class_name = mo.group(1)
- s = s[mo.end():]
- else:
- self._class_name = None
- mo = title_re.match(s)
- if mo is not None:
- self._title = mo.group(1)
- s = s[mo.end():]
- else:
- self._title = None
- if s.strip():
- # Can't interpret some of the expression
- raise CompilerError(
- 'Slot expression syntax error near %s' % repr(s))
-
- def __call__(self, econtext):
- context = econtext.contexts.get('options')
- if context is None:
- raise RuntimeError("Could not find options")
- composite = context.get('composite')
- if IComposite.providedBy(composite):
- slot = composite.slots.get(
- self._name, self._class_name, self._title)
- # Render the slot
- return unicode(slot)
- else:
- # Show the default content
- return econtext.getDefault()
-
- def __repr__(self):
- return '<SlotExpr %s>' % repr(self._s)
-
-
-def registerSlotExprType():
- # Register the 'slot:' expression type.
-
- # Register with Products.PageTemplates.
- try:
- from Products.PageTemplates.Expressions import getEngine
- except ImportError:
- log.exception("Unable to register the slot expression type")
- else:
- engine = getEngine()
- if not engine.getTypes().has_key('slot'):
- engine.registerType('slot', SlotExpr)
-
- # Register with zope.tales.
- try:
- from zope.tales.engine import Engine
- except ImportError:
- log.exception("Unable to register the slot expression type")
- else:
- if not Engine.getTypes().has_key('slot'):
- Engine.registerType('slot', SlotExpr)
Deleted: CompositePage/trunk/tool.py
===================================================================
--- CompositePage/trunk/tool.py 2011-04-30 19:14:59 UTC (rev 121489)
+++ CompositePage/trunk/tool.py 2011-04-30 19:15:39 UTC (rev 121490)
@@ -1,260 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2002 Zope Foundation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.0 (ZPL). A copy of the ZPL 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.
-#
-##############################################################################
-"""Composite tool.
-
-$Id: tool.py,v 1.11 2004/03/02 20:41:44 shane Exp $
-"""
-
-import Globals
-from Acquisition import aq_base, aq_parent, aq_inner
-from OFS.SimpleItem import SimpleItem
-from OFS.Folder import Folder
-from OFS.CopySupport import _cb_encode, _cb_decode, cookie_path
-from AccessControl import ClassSecurityInfo
-from AccessControl.ZopeGuards import guarded_getattr
-
-from Products.CompositePage.interfaces import ICompositeElement
-from Products.CompositePage.interfaces import ISlot
-from Products.CompositePage.interfaces import ISlotClass
-from Products.CompositePage.interfaces import CompositeError
-from Products.CompositePage.element import CompositeElement
-from Products.CompositePage.utils import copyOf
-
-
-_uis = {}
-
-def registerUI(name, obj):
- """Registers a page design UI for use with the composite tool.
- """
- if _uis.has_key(name):
- raise RuntimeError("There is already a UI named %s" % name)
- obj._setId(name)
- _uis[name] = obj
-
-
-
-class DesignUIs(SimpleItem):
- """The container of design user interface objects.
-
- Makes page design UIs accessible through URL traversal.
- """
-
- def __init__(self, id):
- self._setId(id)
-
- def __getattr__(self, name):
- try:
- return _uis[name]
- except KeyError:
- raise AttributeError, name
-
-
-class SlotClassFolder(Folder):
- """Container of slot classes.
- """
- meta_type = "Slot Class Folder"
-
- def all_meta_types(self):
- return Folder.all_meta_types(self, interfaces=(ISlotClass,))
-
-
-class CompositeTool(Folder):
- """Page composition helper tool.
- """
- meta_type = "Composite Tool"
- id = "composite_tool"
-
- security = ClassSecurityInfo()
-
- security.declarePublic("uis")
- uis = DesignUIs("uis")
-
- _properties = Folder._properties + (
- {'id': 'default_inline_templates', 'mode': 'w', 'type': 'lines',
- 'label': 'Default inline template names',},
- )
-
- default_inline_templates = ()
-
- _check_security = 1 # Turned off in unit tests
-
- def __init__(self):
- scf = SlotClassFolder()
- scf._setId("slot_classes")
- self._setObject(scf.id, scf)
- self._reserved_names = ('slot_classes',)
-
- security.declarePublic("moveElements")
- def moveElements(self, source_paths, target_path, target_index, copy=0):
- """Moves or copies elements to a slot.
- """
- target_index = int(target_index)
- # Coerce the paths to sequences of path elements.
- if hasattr(target_path, "split"):
- target_path = target_path.split('/')
- sources = []
- for p in source_paths:
- if hasattr(p, "split"):
- p = p.split('/')
- if p:
- sources.append(p)
-
- # Ignore descendants when an ancestor is already listed.
- i = 1
- sources.sort()
- while i < len(sources):
- prev = sources[i - 1]
- if sources[i][:len(prev)] == prev:
- del sources[i]
- else:
- i = i + 1
-
- # Prevent parents from becoming their own descendants.
- for source in sources:
- if target_path[:len(source)] == source:
- raise CompositeError(
- "Can't make an object a descendant of itself")
-
- # Gather the sources, checking interfaces and security before
- # making any changes.
- root = self.getPhysicalRoot()
- elements = []
- target = root.restrictedTraverse(target_path)
- assert ISlot.providedBy(target), repr(target)
- for source in sources:
- slot = root.restrictedTraverse(source[:-1])
- assert ISlot.providedBy(slot), repr(slot)
- element = slot.restrictedTraverse(source[-1])
- elements.append(element)
- if self._check_security:
- target._verifyObjectPaste(element)
-
- changed_slots = {} # id(aq_base(slot)) -> slot
- try:
- if not copy:
- # Replace items with nulls to avoid changing indexes
- # while moving.
- for source in sources:
- slot = root.restrictedTraverse(source[:-1])
- slot_id = id(aq_base(slot))
- if not changed_slots.has_key(slot_id):
- changed_slots[slot_id] = slot
- # Check security
- nullify = guarded_getattr(slot, "nullify")
- nullify(source[-1])
-
- # Add the elements and reorder.
- for element in elements:
-
- if not ICompositeElement.providedBy(element):
- # Make a composite element wrapper.
- element = CompositeElement(element.getId(), element)
-
- element = aq_base(element)
- new_id = target._get_id(element.getId())
- if copy:
- element = copyOf(element)
- element._setId(new_id)
- target._setObject(new_id, element)
- # Check security
- reorder = guarded_getattr(target, "reorder")
- reorder(new_id, target_index)
- target_index += 1
- finally:
- # Clear the nulls just added.
- for slot in changed_slots.values():
- slot.pack()
-
-
- security.declarePublic("deleteElements")
- def deleteElements(self, source_paths):
- sources = []
- for p in source_paths:
- if hasattr(p, "split"):
- p = p.split('/')
- if p:
- sources.append(p)
-
- # Replace with nulls to avoid changing indexes while deleting.
- orig_slots = {}
- try:
- for source in sources:
- slot = self.restrictedTraverse(source[:-1])
- assert ISlot.providedBy(slot), repr(slot)
- slot_id = id(aq_base(slot))
- if not orig_slots.has_key(slot_id):
- orig_slots[slot_id] = slot
- nullify = guarded_getattr(slot, "nullify") # Check security
- nullify(source[-1])
- finally:
- # Clear the nulls just added.
- for slot in orig_slots.values():
- slot.pack()
-
-
- security.declarePublic("moveAndDelete")
- def moveAndDelete(self, move_source_paths="", move_target_path="",
- move_target_index="", delete_source_paths="",
- REQUEST=None):
- """Move and/or delete elements.
- """
- if move_source_paths:
- p = move_source_paths.split(':')
- self.moveElements(p, move_target_path, int(move_target_index))
- if delete_source_paths:
- p = delete_source_paths.split(':')
- self.deleteElements(p)
- if REQUEST is not None:
- # Return to the page the user was looking at.
- REQUEST["RESPONSE"].redirect(REQUEST["HTTP_REFERER"])
-
-
- security.declarePublic("useClipboard")
- def useClipboard(self, func, REQUEST,
- source_paths=None, target_path=None, target_index=None):
- """Clipboard interaction.
- """
- resp = REQUEST['RESPONSE']
- if func in ("cut", "copy"):
- assert source_paths
- items = [] # list of path tuples
- cut = (func == 'cut')
- for p in str(source_paths).split(':'):
- items.append(p.split('/'))
- data = _cb_encode((cut, items))
- resp.setCookie('__cp', data, path=cookie_path(REQUEST))
- elif func == 'paste':
- assert target_path
- assert target_index
- assert REQUEST is not None
- data = REQUEST['__cp']
- cut, items = _cb_decode(data)
- self.moveElements(
- items, target_path, int(target_index), not cut)
- resp.expireCookie('__cp', path=cookie_path(REQUEST))
- else:
- raise ValueError("Clipboard function %s unknown" % func)
- resp.redirect(REQUEST["HTTP_REFERER"])
-
-Globals.InitializeClass(CompositeTool)
-
-
-def manage_addCompositeTool(dispatcher, REQUEST=None):
- """Adds a composite tool to a folder.
- """
- ob = CompositeTool()
- dispatcher._setObject(ob.getId(), ob)
- if REQUEST is not None:
- return dispatcher.manage_main(dispatcher, REQUEST)
-
Deleted: CompositePage/trunk/utils.py
===================================================================
--- CompositePage/trunk/utils.py 2011-04-30 19:14:59 UTC (rev 121489)
+++ CompositePage/trunk/utils.py 2011-04-30 19:15:39 UTC (rev 121490)
@@ -1,70 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2003 Zope Foundation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.0 (ZPL). A copy of the ZPL 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.
-#
-##############################################################################
-"""Utilities for handling ZODB objects.
-
-(Copied from the Ape product.)
-
-$Id: utils.py,v 1.1 2003/12/28 04:32:47 shane Exp $
-"""
-
-from cStringIO import StringIO
-from cPickle import Pickler, Unpickler
-
-
-def copyOf(source):
- """Copies a ZODB object, loading subobjects as needed.
-
- Re-ghostifies objects along the way to save memory.
- """
- former_ghosts = []
- zclass_refs = {}
-
- def persistent_id(ob, former_ghosts=former_ghosts,
- zclass_refs=zclass_refs):
- if getattr(ob, '_p_changed', 0) is None:
- # Load temporarily.
- former_ghosts.append(ob)
- ob._p_changed = 0
- if hasattr(ob, '__bases__'):
- m = getattr(ob, '__module__', None)
- if (m is not None
- and isinstance(m, basestring)
- and m.startswith('*')):
- n = getattr(ob, '__name__', None)
- if n is not None:
- # Pickling a ZClass instance. Store the reference to
- # the ZClass class separately, so that the pickler
- # and unpickler don't trip over the apparently
- # missing module.
- ref = (m, n)
- zclass_refs[ref] = ob
- return ref
- return None
-
- def persistent_load(ref, zclass_refs=zclass_refs):
- return zclass_refs[ref]
-
- stream = StringIO()
- p = Pickler(stream, 1)
- p.persistent_id = persistent_id
- p.dump(source)
- if former_ghosts:
- for g in former_ghosts:
- del g._p_changed
- del former_ghosts[:]
- stream.seek(0)
- u = Unpickler(stream)
- u.persistent_load = persistent_load
- return u.load()
-
Deleted: CompositePage/trunk/version.txt
===================================================================
--- CompositePage/trunk/version.txt 2011-04-30 19:14:59 UTC (rev 121489)
+++ CompositePage/trunk/version.txt 2011-04-30 19:15:39 UTC (rev 121490)
@@ -1 +0,0 @@
-CompositePage-0.2
More information about the checkins
mailing list