[Zope-CVS] CVS: Products/CompositePage - designuis.py:1.1.2.1
README.txt:1.5.2.1 __init__.py:1.4.2.1 composite.py:1.9.2.1
tool.py:1.8.2.1
Shane Hathaway
shane at zope.com
Fri Feb 20 12:00:17 EST 2004
Update of /cvs-repository/Products/CompositePage
In directory cvs.zope.org:/tmp/cvs-serv23783
Modified Files:
Tag: composite-flat-ui-branch
README.txt __init__.py composite.py tool.py
Added Files:
Tag: composite-flat-ui-branch
designuis.py
Log Message:
Started a branch for the work on a manual page composition UI.
Replaced the concept of a transformer with a "design UI". Also
added functionality for listing what slots a template uses.
=== Added File Products/CompositePage/designuis.py ===
##############################################################################
#
# Copyright (c) 2002 Zope Corporation 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.1.2.1 2004/02/20 16:59:46 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 rawfile import RawFile, InterpolatedFile
_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")
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),)
changeViewForm = PageTemplateFile("changeViewForm.pt", _common)
workspace_view_name = "" # To be overridden
security.declarePrivate("render")
def render(self, composite):
"""Renders a composite and adds scripts.
"""
text = composite()
params = {
"tool": 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)
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 header:
index = match.end(0)
text = "%s%s%s" % (text[:index], header, text[index:])
if 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], top, text[index:])
if 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], 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)
RESPONSE.redirect("%s/%s" % (
obj.absolute_url(), self.workspace_view_name))
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)
try:
is_comp = isinstance(obj, Composite)
except TypeError:
is_comp = 0 # Python 2.1 bug
if is_comp:
gen = guarded_getattr(obj, "generateSlots")
gen()
RESPONSE.redirect("%s/%s" % (
obj.absolute_url(), self.workspace_view_name))
security.declarePublic("getViewChangeInfo")
def getViewChangeInfo(self, paths):
"""Returns information for changing the view applied to objects.
"""
root = self.getPhysicalRoot()
tool = aq_parent(aq_inner(self))
obs = []
all_choices = None # {view -> 1}
current = None
for path in str(paths).split(':'):
ob = root.restrictedTraverse(path)
obs.append(ob)
renderer = tool.getRendererFor(ob)
m = guarded_getattr(renderer, "getInlineView")
view = m()
if current is None:
current = view
elif current and current != view:
# The current view isn't the same for all of the elements,
# so there is no common current view. Spell this condition
# using a non-string value.
current = 0
m = guarded_getattr(renderer, "listAllowableInlineViews")
views = m()
d = {}
for view in views:
d[view] = 1
if all_choices is None:
all_choices = d
else:
for view in all_choices.keys():
if not d.has_key(view):
del all_choices[view]
views = all_choices.keys()
views.sort()
return {"obs": obs, "views": views, "current_view": current}
security.declarePublic("changeView")
def changeView(self, paths, view, REQUEST=None):
"""Changes the view for objects.
"""
info = self.getViewChangeInfo(paths)
if view not in info["views"]:
raise KeyError("View %s is not among the choices" % view)
tool = aq_parent(aq_inner(self))
for ob in info["obs"]:
renderer = tool.getRendererFor(ob)
m = guarded_getattr(renderer, "setInlineView")
m(view)
if REQUEST is not None:
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 (SimpleItem):
"""Page design UI based on a simple list of slots.
Does not display the actual template.
"""
security = ClassSecurityInfo()
security.declarePrivate("render")
def render(self, composite):
return 'foo'
Globals.InitializeClass(ManualUI)
=== Products/CompositePage/README.txt 1.5 => 1.5.2.1 ===
--- Products/CompositePage/README.txt:1.5 Mon Oct 13 13:21:47 2003
+++ Products/CompositePage/README.txt Fri Feb 20 11:59:46 2004
@@ -131,20 +131,20 @@
Rendering in edit mode:
-When requested, the composite renders 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.
-
-After the composite is rendered, the rendered HTML is passed through a
-transformer. The transformer uses regular expressions to find the
-'head' and 'body' tags. Then the transformer inserts scripts, styles,
-and HTML elements. The result of the transformation is sent back to
-the browser.
+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:
@@ -192,10 +192,9 @@
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 transformers.py, the
-'common' subdirectory, and the 'zmi' subdirectory for guidance.
-Simple customizations probably do not require more code than the 'zmi'
-transformer.
+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.
=== Products/CompositePage/__init__.py 1.4 => 1.4.2.1 ===
--- Products/CompositePage/__init__.py:1.4 Fri Dec 26 15:43:30 2003
+++ Products/CompositePage/__init__.py Fri Feb 20 11:59:46 2004
@@ -15,11 +15,12 @@
$Id$
"""
-import tool, composite, slot, slotdef, transformers, interfaces
+import tool, composite, slot, slotdef, designuis, interfaces
-tool.registerTransformer("common", transformers.CommonTransformer())
-tool.registerTransformer("zmi", transformers.ZMITransformer())
-tool.registerTransformer("cmf", transformers.CMFTransformer())
+tool.registerUI("common", designuis.CommonUI())
+tool.registerUI("zmi", designuis.ZMIUI())
+tool.registerUI("cmf", designuis.CMFUI())
+tool.registerUI("manual", designuis.ManualUI())
def initialize(context):
=== Products/CompositePage/composite.py 1.9 => 1.9.2.1 ===
--- Products/CompositePage/composite.py:1.9 Wed Dec 31 12:32:14 2003
+++ Products/CompositePage/composite.py Fri Feb 20 11:59:46 2004
@@ -39,10 +39,13 @@
"""Automatically makes slots available to the template.
"""
_slot_class = Slot
+ _v_used_slots = None
def __getitem__(self, name):
composite = aq_parent(aq_inner(self))
slots = composite.filled_slots
+ if self._v_used_slots is not None:
+ self._v_used_slots.append(name)
try:
return slots[name]
except (KeyError, AttributeError):
@@ -57,6 +60,17 @@
s._p_jar = jar
return s.__of__(slots)
+ def _startCollection(self):
+ """Starts collecting the names of slots used.
+ """
+ self._v_used_slots = []
+
+ def _endCollection(self):
+ """Stops collecting slot names and returns the names in order of use.
+ """
+ res = self._v_used_slots
+ self._v_used_slots = None
+ return res
class Composite(Folder):
@@ -138,7 +152,7 @@
index_html = None
security.declareProtected(perm_names.change_composites, "design")
- def design(self, transformer="common"):
+ def design(self, ui="common"):
"""Renders the composite with editing features.
"""
tool = aq_get(self, "composite_tool", None, 1)
@@ -150,13 +164,12 @@
if req is not None:
req["RESPONSE"].setHeader("Cache-Control", "no-cache")
+ ui_obj = guarded_getattr(tool.uis, ui)
self._v_editing = 1
try:
- text = self()
+ return ui_obj.render(self)
finally:
self._v_editing = 0
- tf = guarded_getattr(tool.transformers, transformer)
- return tf.transform(self, text)
security.declareProtected(perm_names.change_composites,
"manage_designForm")
@@ -167,7 +180,22 @@
security.declareProtected(perm_names.view, "isEditing")
def isEditing(self):
+ """Returns true if currently rendering in design mode.
+ """
return self._v_editing
+
+ security.declareProtected(perm_names.view, "getSlotNames")
+ def getSlotNames(self):
+ """Returns the names of the slots in order of use.
+
+ May return duplicates.
+ """
+ self.slots._beginCollection()
+ try:
+ self()
+ finally:
+ names = self.slots._endCollection()
+ return names
Globals.InitializeClass(Composite)
=== Products/CompositePage/tool.py 1.8 => 1.8.2.1 ===
--- Products/CompositePage/tool.py:1.8 Sat Dec 27 23:32:47 2003
+++ Products/CompositePage/tool.py Fri Feb 20 11:59:46 2004
@@ -30,22 +30,22 @@
from utils import copyOf
-_transformers = {}
+_uis = {}
-def registerTransformer(name, obj):
- """Registers a transformer for use with the composite tool.
+def registerUI(name, obj):
+ """Registers a page design UI for use with the composite tool.
"""
- if _transformers.has_key(name):
- raise RuntimeError("There is already a transformer named %s" % name)
+ if _uis.has_key(name):
+ raise RuntimeError("There is already a UI named %s" % name)
obj._setId(name)
- _transformers[name] = obj
+ _uis[name] = obj
-class Transformers(SimpleItem):
- """The container of transformer objects.
+class DesignUIs(SimpleItem):
+ """The container of design user interface objects.
- Makes page transformers accessible through URL traversal.
+ Makes page design UIs accessible through URL traversal.
"""
def __init__(self, id):
@@ -53,7 +53,7 @@
def __getattr__(self, name):
try:
- return _transformers[name]
+ return _uis[name]
except KeyError:
raise AttributeError, name
@@ -75,8 +75,8 @@
security = ClassSecurityInfo()
- security.declarePublic("transformers")
- transformers = Transformers("transformers")
+ security.declarePublic("uis")
+ uis = DesignUIs("uis")
_properties = Folder._properties + (
{'id': 'default_inline_views', 'mode': 'w', 'type': 'lines',
More information about the Zope-CVS
mailing list