[Zope-CVS] CVS: Products/PageDesign - ReferencedElement.py:1.2 utils.py:1.2 IIndexedSlot.py:1.2 IPageElement.py:1.2 ISlotProvider.py:1.2 PageDesign.py:1.3 RawFile.py:1.2 Slot.py:1.2 SlotElementClipboardSource.py:1.2 SlotElementClipboardTarget.py:1.2 SlotElementTraverser.py:1.2 SlotProvider.py:1.2 zopetop_design.zexp:1.2

Shane Hathaway shane@cvs.zope.org
Mon, 12 Aug 2002 10:39:59 -0400


Update of /cvs-repository/Products/PageDesign
In directory cvs.zope.org:/tmp/cvs-serv8780

Modified Files:
	IIndexedSlot.py IPageElement.py ISlotProvider.py PageDesign.py 
	RawFile.py Slot.py SlotElementClipboardSource.py 
	SlotElementClipboardTarget.py SlotElementTraverser.py 
	SlotProvider.py zopetop_design.zexp 
Added Files:
	ReferencedElement.py utils.py 
Log Message:
Merged page-redesign-branch.  This is almost ready to try out.

=== Products/PageDesign/ReferencedElement.py 1.1 => 1.2 ===
--- /dev/null	Mon Aug 12 10:39:59 2002
+++ Products/PageDesign/ReferencedElement.py	Mon Aug 12 10:39:29 2002
@@ -0,0 +1,95 @@
+##############################################################################
+#
+# 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.
+# 
+##############################################################################
+
+import Acquisition
+from Acquisition import aq_base, aq_get, aq_inner, aq_parent
+from Persistence import Persistent
+from OFS.Traversable import Traversable
+from OFS.Application import Application
+
+from IPageElement import IPageElement
+
+
+class ReferencedElement (Persistent, Acquisition.Explicit, Traversable):
+
+    __implements__ = IPageElement
+
+    def __init__(self, ob):
+        self._path = ob.getPhysicalPath()
+
+    def _deref(self):
+        root = self.getPhysicalRoot()
+        return root.restrictedTraverse(self._path)
+
+    def render(self, design, editable, index):
+        """Returns a string.
+        """
+        ob = self._deref()
+        if getattr(aq_base(ob), 'isDocTemp', 0):
+            # DTML
+            s = ob(self, aq_get(self, 'REQUEST'),
+                   editable=editable, index=index)
+        elif hasattr(ob, '__call__'):
+            # Other kinds of callable objects
+            s = ob(editable=editable, index=index)
+        else:
+            s = str(ob)
+        return s
+
+    def getIconURL(self):
+        """Returns an URL to an icon for this element if available.
+        """
+        ob = self._deref()
+        icon = None
+        if hasattr(aq_base(ob), 'getIcon'):
+            icon = ob.getIcon()
+        elif hasattr(aq_base(ob), 'icon'):
+            icon = ob.icon
+        if icon:
+            return '/' + icon
+        else:
+            return ''
+
+    def getTitle(self):
+        """Returns the title of this element.
+        """
+        ob = self._deref()
+        # It's altogether silly to have to look at four attributes. :-)
+        if hasattr(aq_base(ob), 'Title'):
+            s = ob.Title()
+        else:
+            s = getattr(ob, 'title', '')
+        if s:
+            return str(s)
+        if hasattr(aq_base(ob), 'getId'):
+            return ob.getId()
+        return str(ob.id)
+
+    def getEditURL(self):
+        """Returns the URL for editing this element.
+
+        Returns None or a blank string if it is not editable.
+        """
+        if hasattr(Application, 'externalEdit_'):
+            ob = self._deref()
+            folder = aq_parent(aq_inner(ob))
+            if hasattr(aq_base(ob), 'getId'):
+                id = ob.getId()
+            else:
+                id = str(ob.id)
+            
+            url = '%s/externalEdit_/%s' % (folder.absolute_url(), id)
+            return url
+        return None
+


=== Products/PageDesign/utils.py 1.1 => 1.2 ===
--- /dev/null	Mon Aug 12 10:39:59 2002
+++ Products/PageDesign/utils.py	Mon Aug 12 10:39:29 2002
@@ -0,0 +1,28 @@
+##############################################################################
+#
+# 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.
+# 
+##############################################################################
+
+import os
+
+import Globals
+import OFS
+
+def registerIcon(product_id, icon_filepath):
+    dir, name = os.path.split(icon_filepath)
+    icon = Globals.ImageFile(name, dir)
+    icon.__roles__ = None  # Public.
+    if not hasattr(OFS.misc_.misc_, product_id):
+        setattr(OFS.misc_.misc_, product_id, OFS.misc_.Misc_(product_id, {}))
+    getattr(OFS.misc_.misc_, product_id)[name] = icon
+    return 'misc_/%s/%s' % (product_id, name)
+


=== Products/PageDesign/IIndexedSlot.py 1.1 => 1.2 ===
--- Products/PageDesign/IIndexedSlot.py:1.1	Sat Aug  3 15:46:10 2002
+++ Products/PageDesign/IIndexedSlot.py	Mon Aug 12 10:39:29 2002
@@ -18,15 +18,33 @@
 class IIndexedSlot(ISlot):
 
     def getElementCount():
-        """
+        """Returns the number of elements currently in the slot.
         """
 
     def getElement(index):
+        """Returns the IPageElement at the specified index.
         """
+
+    def track(index):
+        """Returns an insertion point tracker.
+
+        The insertion point will be updated as items are added or
+        removed from this slot, ensuring that elements are later inserted
+        where the user expected.
         """
 
-    def insertBefore(before_element, elements):
+    def untrack(ipt):
+        """Disconnects an insertion point tracker, returning its index.
         """
+
+    def insertElements(index, elements):
+        """Inserts IPageElements at the specified index.
+
+        If index is an insertion point tracker (as returned by track()),
+        the tracker will be disconnected and its current value will be
+        used as the index.
+
+        If index is None, the elements will be appended to the end.
         """
 
     def remove(element):


=== Products/PageDesign/IPageElement.py 1.1 => 1.2 ===
--- Products/PageDesign/IPageElement.py:1.1	Sat Aug  3 15:46:10 2002
+++ Products/PageDesign/IPageElement.py	Mon Aug 12 10:39:29 2002
@@ -24,6 +24,10 @@
         """Returns the title of this element.
         """
 
+    def getIconURL():
+        """Returns the URL to an icon for this element if any.
+        """
+
     def getEditURL():
         """Returns the URL for editing this element.
 


=== Products/PageDesign/ISlotProvider.py 1.1 => 1.2 ===
--- Products/PageDesign/ISlotProvider.py:1.1	Sat Aug  3 15:46:10 2002
+++ Products/PageDesign/ISlotProvider.py	Mon Aug 12 10:39:29 2002
@@ -23,10 +23,8 @@
         The slot might be created on the fly and not stored.
         """
 
-    def _makeSlot(name):
-        """Creates a slot if it doesn't exist.
-
-        Returns the slot object.
+    def _persistSlot(slot):
+        """Ensures the slot is linked into the database.
         """
 
     def _isEditable():


=== Products/PageDesign/PageDesign.py 1.2 => 1.3 === (550/650 lines abridged)
--- Products/PageDesign/PageDesign.py:1.2	Mon Jul  1 14:27:31 2002
+++ Products/PageDesign/PageDesign.py	Mon Aug 12 10:39:29 2002
@@ -24,23 +24,27 @@
 import Globals
 import Acquisition
 from Acquisition import aq_base, aq_inner, aq_parent
-from AccessControl import ClassSecurityInfo
+from AccessControl import ClassSecurityInfo, getSecurityManager
 from AccessControl.Permissions import view
+from AccessControl.ZopeGuards import guarded_getattr
 from Interface import Base as Interface
 from ZODB import Persistent
 from ZODB.PersistentMapping import PersistentMapping
 from OFS.SimpleItem import SimpleItem
 from OFS.PropertyManager import PropertyManager
 from Products.PageTemplates.PageTemplateFile import PageTemplateFile
+from Globals import ImageFile
 
 from RawFile import RawFile
+from SlotProvider import SlotProvider
+from ReferencedElement import ReferencedElement
+from ISlotProvider import ISlotProvider
+from IClipboardSource import IClipboardSource
+from IClipboardTarget import IClipboardTarget
 
 # Permission name
 change_page_designs = 'Change Page Designs'
 
-# Constant until the right policy can be chosen.
-INDEPENDENT_TARGETS = 1
-
 
 _www = os.path.join(os.path.dirname(__file__), 'www')
 
@@ -52,308 +56,259 @@
     """Error in page design"""
 
 
-class AreaRenderer:
-
-    security = ClassSecurityInfo()
-    security.setDefaultAccess('allow')
-
-    def __init__(self, design, editable, layered_areas):
-        self._design = design
-        self._editable = editable
-        # layered_areas is not None when a page design uses another page
-        # design as its template.
-        self._layered_areas = layered_areas
-

[-=- -=- -=- 550 lines omitted -=- -=- -=-]

+    setattr(PageDesignBase, img_name + '_icon', im)
+
+
 
 class PageDesign(PageDesignBase, SimpleItem, PropertyManager):
     meta_type = 'Page Design'
@@ -422,33 +394,20 @@
     manage_options = (PageDesignBase.manage_options +
                       SimpleItem.manage_options)
 
-    _properties = ({'id': 'title', 'type': 'string', 'mode': 'w'},
-                   {'id': 'template_id', 'type': 'string', 'mode': 'w'},)
-    template_id = None
-    title = ''
-
-    security.declareProtected(change_page_designs, 'listAvailableElementIds')
-    def listAvailableElementIds(self):
-        # This implementation uses a simple ID.
-        return aq_parent(aq_inner(self)).objectIds()
-
-    security.declareProtected(change_page_designs, 'getElement')
-    def getElement(self, element_id):
-        return aq_parent(aq_inner(self))[element_id]
-
-    security.declareProtected(change_page_designs, 'getTemplate')
-    def getTemplate(self):
-        id = self.template_id
-        if not id:
-            raise DesignError, 'Template not set'
-        return getattr(aq_parent(aq_inner(self)), id)
+    _properties = (
+        {'id': 'title', 'type': 'string', 'mode': 'w'},
+        {'id': 'template_id', 'type': 'string', 'mode': 'w'},
+        )
 
     security.declareProtected(change_page_designs, 'manage_propertiesForm')
-    def manage_propertiesForm(self, *args, **kw):
+    def manage_propertiesForm(self, client=None, REQUEST=None,
+                              manage_tabs_message=None):
         """Object properties form
         """
-        return self.manage_areas(*args, **kw)
-        
+        if REQUEST is not None:
+            REQUEST['RESPONSE'].redirect(
+                self.absolute_url() + '/manage_main?manage_tabs_message=' +
+                manage_tabs_message)
 
 Globals.InitializeClass(PageDesign)
 


=== Products/PageDesign/RawFile.py 1.1.1.1 => 1.2 ===
--- Products/PageDesign/RawFile.py:1.1.1.1	Sat Jun  8 12:09:34 2002
+++ Products/PageDesign/RawFile.py	Mon Aug 12 10:39:29 2002
@@ -51,48 +51,48 @@
         self.lmh = rfc1123_date(self.lmt)
 
 
-    def index_html(self, REQUEST, RESPONSE):
+    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...
-        RESPONSE.setHeader('Content-Type', self.content_type)
-        RESPONSE.setHeader('Last-Modified', self.lmh)
-        RESPONSE.setHeader('Cache-Control', self.cch)
-        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 ''
+        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()
         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 ''
-
-    def __len__(self):
-        # This is bogus and needed because of the way Python tests truth.
-        return 1 


=== Products/PageDesign/Slot.py 1.1 => 1.2 ===
--- Products/PageDesign/Slot.py:1.1	Sat Aug  3 15:46:10 2002
+++ Products/PageDesign/Slot.py	Mon Aug 12 10:39:29 2002
@@ -11,32 +11,80 @@
 # FOR A PARTICULAR PURPOSE.
 # 
 ##############################################################################
+"""Slots containing one or more page elements.
 
+$Id$
+"""
+
+import os
+import sys
+from cgi import escape
 
 from Persistence import Persistent
 import Acquisition
 from Acquisition import aq_inner, aq_parent
+import Globals
+from AccessControl import ClassSecurityInfo
 from OFS.Traversable import Traversable
 
 from IIndexedSlot import IIndexedSlot
 from IPageElement import IPageElement
+from SlotElementClipboardSource import SlotElementClipboardSource
 from SlotElementClipboardTarget \
      import SlotElementClipboardTarget, SlotElementClipboardLastTarget
+from SlotElementTraverser import SlotElementTraverser
+import utils
+
+_www = os.path.join(os.path.dirname(__file__), 'www')
+
+
+target_icon = '/' + utils.registerIcon(
+    'PageDesign', os.path.join(_www, 'element_add.gif'))
 
 
 class Slot (Persistent, Acquisition.Explicit, Traversable):
-    """Slot in a page design.
+    """Slot in a page design that can contain either 1 element or a sequence.
+
+    When a template uses the 'single()' method, which returns a string,
+    the slot provides a UI for adding only one page element.
+
+    When a template uses the 'multiple()' method, which returns a list,
+    the slot provides a UI for adding multiple page elements.  This allows
+    the template to have some control over the layout of multiple elements.
+
+    Slots designed using multiple() but later rendered with single() will
+    get all elements concatenated together in a string.
     """
 
     __implements__ = IIndexedSlot
 
-    # Traversal helpers.
-    elementSources = SlotElementTraverser('elementSources',
-                                          SlotElementClipboardSource)
-    elementTargets = SlotElementTraverser('elementTargets',
-                                          SlotElementClipboardTarget)
-    lastElementTarget = SlotElementClipboardLastTarget
+    security = ClassSecurityInfo()
+
+    # Clipboard traversal helpers.
+    security.declarePublic('elementSources', 'elementTargets',
+                           'lastElementTarget')
+    elementSources = SlotElementTraverser(
+        'elementSources', SlotElementClipboardSource)
+    elementTargets = SlotElementTraverser(
+        'elementTargets', SlotElementClipboardTarget)
+    lastElementTarget = SlotElementClipboardLastTarget()
+
+
+    target_html = '''<div class="design-target" align="center"
+    id="%(clipboard_path)s"><a href="#"
+    onclick="addElementDialog('%(clipboard_path)s'); return false;"><img
+    src="%(target_icon)s" border="0"
+    alt="Add element to slot: %(slot_name)s"
+    title="Add element to slot: %(slot_name)s" /></a></div>'''
+
+    element_html = '''<div class="design-element" id="%(clipboard_path)s">
+    <table class="design-element-titlebar" width="100%%"><tr>
+    <td align="left">%(titlebar)s</td>
+    <td align="right">%(controls)s</td>
+    </tr></table>
+    <div class="design-element-body">%(text)s</div></div>'''
 
+    _trackers = ()
 
     def __init__(self, id):
         self.id = id
@@ -47,6 +95,34 @@
         return self.id
 
 
+    def renderTargetHTML(self, **kw):
+        kw['target_icon'] = target_icon
+        return self.target_html % kw
+
+
+    def renderSourceHTML(self, element, **kw):
+        c = []
+        for name in ('moveup', 'movedown', 'remove'):
+            url = kw.get(name + '_url')
+            if url:
+                # TODO: use server-wide icons
+                c.append('<a href="%s"><img src="%s_icon" border="0" /></a>'
+                         % (url, name))
+        icon_tag = edit_tag = ''
+        icon_url = element.getIconURL()
+        if icon_url:
+            icon_tag = '<img border="0" src="%s" /> ' % icon_url
+        edit_url = element.getEditURL()
+        if edit_url:
+            edit_tag = (' <a href="%s"><img border="0" src="edit_icon" /></a>'
+                        % edit_url)
+        
+        kw['titlebar'] = icon_tag + escape(kw['title']) + edit_tag
+        kw['controls'] = '\n'.join(c)
+        return self.element_html % kw
+
+
+    security.declarePublic('single')
     def single(self):
         """Renders as a single-element slot."""
         allow_add = (not self.contents)
@@ -61,6 +137,7 @@
         return ''.join(res)
             
 
+    security.declarePublic('multiple')
     def multiple(self):
         """Renders as a multiple-element slot."""
         res = self.renderToList(1)
@@ -83,34 +160,61 @@
         res = []
         phys_path = '/'.join(self.getPhysicalPath())
 
-        for index in range(len(self.contents)):
+        clen = len(self.contents)
+        for index in range(clen):
             e = self.contents[index].__of__(self)
             if editable and allow_add:
                 path = '%s/elementTargets/%d' % (phys_path, index)
-                res.append(self.target_html % {
-                    'slot_name': self.id,
-                    'slot_index': index,
-                    'path': path,
-                    })
+                res.append(self.renderTargetHTML(
+                    clipboard_path=path,
+                    slot_name=self.id,
+                    slot_index=index,
+                    ))
+
+            try:
+                text = e.render(design, editable, index)
+            except:
+                # Show the exception.
+                text = "<i>%s</i>" % (
+                    escape(('%s: %s' % (sys.exc_info()[:2]))[:80]))
 
-            text = e.render(design=design, editable=editable, index=index)
             if editable:
+                design_url = design.absolute_url()
                 path = '%s/elementSources/%d' % (phys_path, index)
-                text = self.element_html % {
-                    'title': e.getTitle(),
-                    'path': path,
-                    'edit_url': e.getEditURL(),
-                    'text': text,
-                    }
+                move_up_url = move_down_url = ''
+                if index > 0:
+                    # Can move up.
+                    move_up_url = (
+                        '%s/moveElement?source_path=%s'
+                        '&target_path=%s/elementTargets/%d' % (
+                        design_url, path, phys_path, index - 1))
+                if index < (clen - 1):
+                    # Can move down.
+                    move_down_url = (
+                        '%s/moveElement?source_path=%s'
+                        '&target_path=%s/elementTargets/%d' % (
+                        design_url, path, phys_path, index + 2))
+                remove_url = '%s/removeElement?source_path=%s' % (
+                    design_url, path)
+
+                text = self.renderSourceHTML(
+                    element=e,
+                    clipboard_path=path,
+                    title=e.getTitle(),
+                    text=text,
+                    moveup_url=move_up_url,
+                    movedown_url=move_down_url,
+                    remove_url=remove_url,
+                    )
             res.append(text)
 
         if editable and allow_add:
             path = '%s/lastElementTarget' % phys_path
-            res.append(self.target_html % {
-                'slot_name': self.id,
-                'slot_index': -1,
-                'path': path,
-                })
+            res.append(self.renderTargetHTML(
+                clipboard_path=path,
+                slot_name=self.id,
+                slot_index=-1,
+                ))
 
         return res
 
@@ -121,24 +225,65 @@
     def getElement(self, index):
         return self.contents[index]
 
-    def insertBefore(self, before_element, elements):
+    def beforeChange(self):
+        aq_parent(aq_inner(self))._persistSlot(self)
+
+    def track(self, index):
+        self.beforeChange()
+        ipt = InsertionPointTracker(index)
+        self._trackers = self._trackers + (ipt,)
+        return ipt
+
+    def untrack(self, ipt):
+        self.beforeChange()
+        ipts = list(self._trackers)
+        ipts.remove(ipt)
+        self._trackers = tuple(ipts)
+        return ipt.index
+
+    def insertElements(self, index, elements):
         # Verify none of the elements are already inserted.
         # An element can only be in one place at a time.
+        if isinstance(index, InsertionPointTracker):
+            index = self.untrack(index)
         for e in elements:
             if not IPageElement.isImplementedBy(e):
                 raise ValueError, 'Not a page element'
             if e in self.contents:
                 raise ValueError, 'Already inserted'
-        if before_element is None:
+        self.beforeChange()
+        if index is None:
             # append.
             self.contents = self.contents + tuple(elements)
         else:
-            index = list(self.contents).index(before_element)
             self.contents = (self.contents[:index] + tuple(elements)
                              + self.contents[index:])
+        for ipt in self._trackers:
+            ipt.afterInsert(index, len(elements))
 
     def remove(self, element):
+        self.beforeChange()
         lst = list(self.contents)
-        lst.remove(element)
+        index = lst.index(element)
+        del lst[index]
         self.contents = tuple(lst)
+        for ipt in self._trackers:
+            ipt.afterRemove(index, 1)
+
+Globals.InitializeClass(Slot)
+
+
+
+class InsertionPointTracker:
+
+    def __init__(self, index):
+        self.index = index
+
+    def afterInsert(self, index, count):
+        if self.index >= index:
+            self.index += count
+
+    def afterRemove(self, index, count):
+        if self.index > index:
+            self.index -= count
 


=== Products/PageDesign/SlotElementClipboardSource.py 1.1 => 1.2 ===
--- Products/PageDesign/SlotElementClipboardSource.py:1.1	Sat Aug  3 15:46:10 2002
+++ Products/PageDesign/SlotElementClipboardSource.py	Mon Aug 12 10:39:29 2002
@@ -14,25 +14,37 @@
 
 
 import Acquisition
+import Globals
 from Acquisition import aq_base, aq_inner, aq_parent
+from AccessControl import ClassSecurityInfo
 from IClipboardSource import IClipboardSource
 from IIndexedSlot import IIndexedSlot
+from IPageElement import IPageElement
+
+# Permission name
+change_page_designs = 'Change Page Designs'
 
 
 class SlotElementClipboardSource (Acquisition.Explicit):
 
     __implements__ = IClipboardSource
 
-    def __init__(self, index):
+    security = ClassSecurityInfo()
+
+    def init(self, index):
         index = int(index)
         slot = aq_parent(aq_inner(self))
-        assert IIndexedSlot.isImplementedBy(slot)
-        self.element = slot.getElement(index)
+        assert IIndexedSlot.isImplementedBy(slot), `slot`
+        element = aq_base(slot.getElement(index))
+        assert IPageElement.isImplementedBy(element), `element`
+        self.element = aq_base(element)
+        return self
 
+    security.declareProtected(change_page_designs, 'cut')
     def cut(self):
         slot = aq_parent(aq_inner(self))
-        assert IIndexedSlot.isImplementedBy(slot)
         element = aq_base(self.element)
         slot.remove(element)
         return element
 
+Globals.InitializeClass(SlotElementClipboardSource)


=== Products/PageDesign/SlotElementClipboardTarget.py 1.1 => 1.2 ===
--- Products/PageDesign/SlotElementClipboardTarget.py:1.1	Sat Aug  3 15:46:10 2002
+++ Products/PageDesign/SlotElementClipboardTarget.py	Mon Aug 12 10:39:29 2002
@@ -13,29 +13,52 @@
 ##############################################################################
 
 import Acquisition
+import Globals
 from Acquisition import aq_base, aq_inner, aq_parent
+from AccessControl import ClassSecurityInfo
 from IClipboardTarget import IClipboardTarget
+from IIndexedSlot import IIndexedSlot
+
+# Permission name
+change_page_designs = 'Change Page Designs'
 
 
 class SlotElementClipboardTarget (Acquisition.Explicit):
 
     __implements__ = IClipboardTarget
 
-    def __init__(self, index):
+    __roles__ = None  # ugh
+
+    security = ClassSecurityInfo()
+
+    def init(self, index):
         index = int(index)
         slot = aq_parent(aq_inner(self))
-        self.before_element = slot.getElement(index)
+        assert IIndexedSlot.isImplementedBy(slot), `slot`
+        self.ipt = slot.track(index)
+        return self
 
+    security.declareProtected(change_page_designs, 'paste')
     def paste(self, elements):
         slot = aq_parent(aq_inner(self))
-        slot.insertBefore(aq_base(self.before_element), elements)
+        slot.insertElements(self.ipt, elements)
+
+Globals.InitializeClass(SlotElementClipboardTarget)
 
 
 class SlotElementClipboardLastTarget (Acquisition.Explicit):
 
     __implements__ = IClipboardTarget
 
+    __roles__ = None  # ugh
+
+    security = ClassSecurityInfo()
+
+    security.declareProtected(change_page_designs, 'paste')
     def paste(self, elements):
         slot = aq_parent(aq_inner(self))
-        slot.insertBefore(None, elements)
+        assert IIndexedSlot.isImplementedBy(slot), `slot`
+        slot.insertElements(None, elements)
+
+Globals.InitializeClass(SlotElementClipboardLastTarget)
 


=== Products/PageDesign/SlotElementTraverser.py 1.1 => 1.2 ===
--- Products/PageDesign/SlotElementTraverser.py:1.1	Sat Aug  3 15:46:10 2002
+++ Products/PageDesign/SlotElementTraverser.py	Mon Aug 12 10:39:29 2002
@@ -12,18 +12,30 @@
 # 
 ##############################################################################
 
+import Acquisition
+from Acquisition import aq_inner, aq_parent
+import Globals
+from AccessControl import ClassSecurityInfo
 from OFS.Traversable import Traversable
 
 
 class SlotElementTraverser (Acquisition.Explicit, Traversable):
 
+    security = ClassSecurityInfo()
+    security.setDefaultAccess('allow')
+
+    __roles__ = None
+
     def __init__(self, id, item_factory):
         self.id = id
-        self.item_factory = item_factory
+        self._item_factory = item_factory
 
     def getId(self):
         return self.id
 
     def __getitem__(self, name):
-        return self.item_factory(name).__of__(self)
+        slot = aq_parent(aq_inner(self))
+        return self._item_factory().__of__(slot).init(name)
+
+Globals.InitializeClass(SlotElementTraverser)
 


=== Products/PageDesign/SlotProvider.py 1.1 => 1.2 ===
--- Products/PageDesign/SlotProvider.py:1.1	Sat Aug  3 15:46:10 2002
+++ Products/PageDesign/SlotProvider.py	Mon Aug 12 10:39:29 2002
@@ -13,9 +13,14 @@
 ##############################################################################
 
 import Acquisition
+from Acquisition import aq_base
+import Globals
 from AccessControl import ClassSecurityInfo
 from OFS.Traversable import Traversable
 
+from ISlotProvider import ISlotProvider
+from Slot import Slot
+
 
 class SlotProvider (Acquisition.Explicit, Traversable):
 
@@ -33,21 +38,23 @@
     def getId(self):
         return self.id
 
-    def _makeSlot(self, name):
-        self.__getitem__(name, 1)
+    def _persistSlot(self, slot):
+        slots = self.aq_parent._slots
+        name = slot.getId()
+        if not slots.has_key(name):
+            slots[name] = aq_base(slot)
 
-    def __getitem__(self, name, add=0):
+    def __getitem__(self, name):
         slots = self.aq_parent._slots
         slot = slots.get(name)
         if slot is None:
-            slot = Slot()
-            if add:
-                slots[name] = slot
+            slot = Slot(name)
         return slot.__of__(self)
 
     def _isEditable(self):
         return self._editable
 
-    def _getLayeredProvider():
+    def _getLayeredProvider(self):
         return self._layered_provider
 
+Globals.InitializeClass(SlotProvider)


=== Products/PageDesign/zopetop_design.zexp 1.1 => 1.2 ===