[Zope-CVS] CVS: Products/PageDesign - IClipboardSource.py:1.1 IClipboardTarget.py:1.1 IIndexedSlot.py:1.1 IPageElement.py:1.1 ISlot.py:1.1 ISlotProvider.py:1.1 Slot.py:1.1 SlotElementClipboardSource.py:1.1 SlotElementClipboardTarget.py:1.1 SlotElementTraverser.py:1.1 SlotNameCollector.py:1.1 SlotProvider.py:1.1 newnotes.txt:1.1 PageDesign.py:1.2.2.1

Shane Hathaway shane@cvs.zope.org
Sat, 3 Aug 2002 15:46:10 -0400


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

Modified Files:
      Tag: page-redesign-branch
	PageDesign.py 
Added Files:
	IClipboardSource.py IClipboardTarget.py IIndexedSlot.py 
	IPageElement.py ISlot.py ISlotProvider.py Slot.py 
	SlotElementClipboardSource.py SlotElementClipboardTarget.py 
	SlotElementTraverser.py SlotNameCollector.py SlotProvider.py 
	newnotes.txt 
Log Message:
Designed some interfaces to break up responsibilities.

Made it possible to use both single-item and multiple-item slots.

Started thinking about integrating External Editor.

(Note that this is just a checkpoint.)


=== Added File Products/PageDesign/IClipboardSource.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.
# 
##############################################################################


from Interface import Interface

class IClipboardSource(Interface):

    def cut():
        """Removes this item from its container and returns the element."""

    #def copy():
    #    """Belongs here too but I don't need it yet. ;-)"""



=== Added File Products/PageDesign/IClipboardTarget.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.
# 
##############################################################################


from Interface import Interface

class IClipboardTarget(Interface):

    def paste(elements):
        """Adds the given elements to the container."""



=== Added File Products/PageDesign/IIndexedSlot.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.
# 
##############################################################################


from ISlot import ISlot

class IIndexedSlot(ISlot):

    def getElementCount():
        """
        """

    def getElement(index):
        """
        """

    def insertBefore(before_element, elements):
        """
        """

    def remove(element):
        """Removes an element and returns the element removed.
        """


=== Added File Products/PageDesign/IPageElement.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.
# 
##############################################################################

from Interface import Interface

class IPageElement (Interface):

    def render(design, editable, index):
        """Returns a string.
        """

    def getTitle():
        """Returns the title of this element.
        """

    def getEditURL():
        """Returns the URL for editing this element.

        Returns None or a blank string if it is not editable.
        """


=== Added File Products/PageDesign/ISlot.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.
# 
##############################################################################


from Interface import Interface


class ISlot (Interface):

    def single():
        """Renders as a single-element slot."""

    def multiple():
        """Renders as a multiple-element slot."""



=== Added File Products/PageDesign/ISlotProvider.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.
# 
##############################################################################

from Interface import Interface

class ISlotProvider (Interface):
    """The aq_parent should be the page design object."""

    def __getitem__(name):
        """Returns an object that implements ISlot.

        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 _isEditable():
        """
        """

    def _getLayeredProvider():
        """
        """


=== Added File Products/PageDesign/Slot.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.
# 
##############################################################################


from Persistence import Persistent
import Acquisition
from Acquisition import aq_inner, aq_parent
from OFS.Traversable import Traversable

from IIndexedSlot import IIndexedSlot
from IPageElement import IPageElement
from SlotElementClipboardTarget \
     import SlotElementClipboardTarget, SlotElementClipboardLastTarget


class Slot (Persistent, Acquisition.Explicit, Traversable):
    """Slot in a page design.
    """

    __implements__ = IIndexedSlot

    # Traversal helpers.
    elementSources = SlotElementTraverser('elementSources',
                                          SlotElementClipboardSource)
    elementTargets = SlotElementTraverser('elementTargets',
                                          SlotElementClipboardTarget)
    lastElementTarget = SlotElementClipboardLastTarget


    def __init__(self, id):
        self.id = id
        self.contents = ()


    def getId(self):
        return self.id


    def single(self):
        """Renders as a single-element slot."""
        allow_add = (not self.contents)
        res = self.renderToList(allow_add)
        if not res:
            layered_provider = aq_parent(aq_inner(self))._getLayeredProvider()
            if layered_provider is not None:
                # A higher layer is using the design holding this slot
                # as a template.
                # Since the slot is empty, let the higher layer supply it.
                return layered_provider[self.id].single()
        return ''.join(res)
            

    def multiple(self):
        """Renders as a multiple-element slot."""
        res = self.renderToList(1)
        layered_provider = aq_parent(aq_inner(self))._getLayeredProvider()
        if layered_provider is not None:
            # A higher layer is using the design holding this slot
            # as a template.
            # Allow the higher layer to supply text before and after.
            before = layered_provider[self.id + '_before'].multiple()
            after = layered_provider[self.id + '_after'].multiple()
            res = before + res + after
        return res


    def renderToList(self, allow_add):
        """Renders the items to a list."""
        sp = self.aq_parent
        design = sp.aq_parent
        editable = sp._isEditable()
        res = []
        phys_path = '/'.join(self.getPhysicalPath())

        for index in range(len(self.contents)):
            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,
                    })

            text = e.render(design=design, editable=editable, index=index)
            if editable:
                path = '%s/elementSources/%d' % (phys_path, index)
                text = self.element_html % {
                    'title': e.getTitle(),
                    'path': path,
                    'edit_url': e.getEditURL(),
                    'text': text,
                    }
            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,
                })

        return res


    def getElementCount(self):
        return len(self.contents)

    def getElement(self, index):
        return self.contents[index]

    def insertBefore(self, before_element, elements):
        # Verify none of the elements are already inserted.
        # An element can only be in one place at a time.
        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:
            # 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:])

    def remove(self, element):
        lst = list(self.contents)
        lst.remove(element)
        self.contents = tuple(lst)



=== Added File Products/PageDesign/SlotElementClipboardSource.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.
# 
##############################################################################


import Acquisition
from Acquisition import aq_base, aq_inner, aq_parent
from IClipboardSource import IClipboardSource
from IIndexedSlot import IIndexedSlot


class SlotElementClipboardSource (Acquisition.Explicit):

    __implements__ = IClipboardSource

    def __init__(self, index):
        index = int(index)
        slot = aq_parent(aq_inner(self))
        assert IIndexedSlot.isImplementedBy(slot)
        self.element = slot.getElement(index)

    def cut(self):
        slot = aq_parent(aq_inner(self))
        assert IIndexedSlot.isImplementedBy(slot)
        element = aq_base(self.element)
        slot.remove(element)
        return element



=== Added File Products/PageDesign/SlotElementClipboardTarget.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.
# 
##############################################################################

import Acquisition
from Acquisition import aq_base, aq_inner, aq_parent
from IClipboardTarget import IClipboardTarget


class SlotElementClipboardTarget (Acquisition.Explicit):

    __implements__ = IClipboardTarget

    def __init__(self, index):
        index = int(index)
        slot = aq_parent(aq_inner(self))
        self.before_element = slot.getElement(index)

    def paste(self, elements):
        slot = aq_parent(aq_inner(self))
        slot.insertBefore(aq_base(self.before_element), elements)


class SlotElementClipboardLastTarget (Acquisition.Explicit):

    __implements__ = IClipboardTarget

    def paste(self, elements):
        slot = aq_parent(aq_inner(self))
        slot.insertBefore(None, elements)



=== Added File Products/PageDesign/SlotElementTraverser.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.
# 
##############################################################################

from OFS.Traversable import Traversable


class SlotElementTraverser (Acquisition.Explicit, Traversable):

    def __init__(self, id, item_factory):
        self.id = id
        self.item_factory = item_factory

    def getId(self):
        return self.id

    def __getitem__(self, name):
        return self.item_factory(name).__of__(self)



=== Added File Products/PageDesign/SlotNameCollector.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.
# 
##############################################################################


import Acquisition
from AccessControl import ClassSecurityInfo


class MockSlot:

    __implements__ = ISlot

    def single(self):
        return ''

    def multiple(self):
        return ()

mock_slot = MockSlot()


class SlotNameCollector (Acquisition.Explicit):

    __implements__ = ISlotProvider

    security = ClassSecurityInfo()
    security.setDefaultAccess('allow')

    def __init__(self):
        self._names = {}

    def __getitem__(self, name):
        self._names[name] = 1
        return mock_slot

    def _makeSlot(self, name):
        return self[name]

    def _getNames(self):
        return self._names

    def _isEditable(self):
        return 0

    def _getLayeredProvider():
        return None



=== Added File Products/PageDesign/SlotProvider.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.
# 
##############################################################################

import Acquisition
from AccessControl import ClassSecurityInfo
from OFS.Traversable import Traversable


class SlotProvider (Acquisition.Explicit, Traversable):

    __implements__ = ISlotProvider

    security = ClassSecurityInfo()
    security.setDefaultAccess('allow')

    id = 'slots'

    def __init__(self, editable=0, layered_provider=None):
        self._editable = editable
        self._layered_provider = layered_provider

    def getId(self):
        return self.id

    def _makeSlot(self, name):
        self.__getitem__(name, 1)

    def __getitem__(self, name, add=0):
        slots = self.aq_parent._slots
        slot = slots.get(name)
        if slot is None:
            slot = Slot()
            if add:
                slots[name] = slot
        return slot.__of__(self)

    def _isEditable(self):
        return self._editable

    def _getLayeredProvider():
        return self._layered_provider



=== Added File Products/PageDesign/newnotes.txt ===


element id:
  /folder/design/slots/top/elementSources/0
  /folder/palette/foo

target id:
  /folder/design/slots/bottom/elementTargets/2
  /folder/design/slots/bottom/elementTargets/3  (last)



Palettes let you add either references to existing items or new items
  (storing a reference to the local item).


You can use a page design as a page template macro:

  metal:use-macro="here/design/macro"

"macro" returns a tuple (generated on the fly) with pre-rendered content.


The bar above each page element contains:

Title
Pencil (for external editor)
Move up
Move down
Close (X)

Show the "+" sign for empty slots so it works in NS4.



Verify ahead of time:

- Does Konqueror support external JS?


Change UI:

- Don't make outlines when floating over an item.



Default palette lets you browse the tree?



Management Tabs:

  Design
  Local page elements
  (Folder stuff)




=== Products/PageDesign/PageDesign.py 1.2 => 1.2.2.1 ===
 # 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,71 +49,20 @@
     """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
-
-    def __getitem__(self, name):
-        res = ()
-        try:
-            res = self._design.renderArea(name, self._editable)
-            if not res:
-                # If the area is empty, allow it to be filled by the
-                # page design that's using this design as a template.
-                # This is just one way to use page designs as templates.
-                la = self._layered_areas
-                if la is not None:
-                    res = la[name]
-        except:
-            if self._editable:
-                t, v = sys.exc_info()[:2]
-                res = [escape('Unable to render area "%s". %s: %s' % (
-                    name, getattr(t, '__name__', t), v))]
-        return res
-
-Globals.InitializeClass(AreaRenderer)
-
-
-class AreaNameCollector:
-
-    security = ClassSecurityInfo()
-    security.setDefaultAccess('allow')
-
-    def __init__(self):
-        self._names = {}
-
-    def __getitem__(self, name):
-        self._names[name] = 1
-        return ()
-
-    def _getNames(self):
-        return self._names
-
-Globals.InitializeClass(AreaNameCollector)
-
-
 class PageDesignBase (Persistent):
     """Base class for page designs.
     """
     # The idea is that the contents of this
-    # folder is the "palette".  _placement defines where the items go.
+    # folder is the "palette".  _slots defines where the items go.
     # Every item must be callable, accepting the "editable" and
     # "index" keyword arguments.
 
     security = ClassSecurityInfo()
 
-    _placement = None  # { area_name -> (element_id,) }
+    _slots = None  # { slot_name -> (element_id,) }
 
     manage_options = (
-        {'label': 'Design', 'action': 'manage_areas'},
+        {'label': 'Design', 'action': 'manage_slots'},
         )
 
     security.declareProtected(change_page_designs, 'scripts_js')
@@ -125,8 +71,8 @@
     security.declareProtected(change_page_designs, 'pagedesign_css')
     pagedesign_css = RawFile('pagedesign.css', 'text/css', _www)
 
-    security.declareProtected(change_page_designs, 'manage_areas')
-    manage_areas = PageTemplateFile('areas.pt', _www)
+    security.declareProtected(change_page_designs, 'manage_slots')
+    manage_slots = PageTemplateFile('slots.pt', _www)
 
     security.declareProtected(change_page_designs, 'manage_designFrames')
     manage_designFrames = PageTemplateFile('designFrames.pt', _www)
@@ -179,175 +125,142 @@
 
     _v_rendering = 0
 
+    slots = SlotProvider()
 
     def __init__(self, id, title=''):
-        self._placement = PersistentMapping()
+        self._slots = PersistentMapping()
         self.id = id
         if title:
             self.title = title
 
-
-    security.declarePrivate('listAvailableElementIds')
-    def listAvailableElementIds(self):
-        # To be overridden
-        raise NotImplementedError
-
-
-    security.declarePrivate('getElement')
-    def getElement(self, element_id):
-        # To be overridden
-        raise NotImplementedError
-
-
     security.declareProtected(change_page_designs, 'getTemplate')
     def getTemplate(self):
         # To be overridden
         raise NotImplementedError
 
 
-    security.declareProtected(change_page_designs, 'addArea')
-    def addArea(self, name):
-        """Creates an area if it doesn't already exist.
+    security.declareProtected(change_page_designs, 'addSlot')
+    def addSlot(self, name):
+        """Creates a slot if it doesn't already exist.
 
-        If the area already exists, does nothing.
+        If the slot already exists, does nothing.
         """
-        assert name
-        name = str(name)
-        if not self._placement.has_key(name):
-            self._placement[name] = ()
+        self.slots._makeSlot(name)
 
 
-    security.declareProtected(change_page_designs, 'delArea')
-    def delArea(self, name):
-        """Removes an area.
+    security.declareProtected(change_page_designs, 'delSlot')
+    def delSlot(self, name):
+        """Removes a slot.
         """
-        del self._placement[name]
+        del self._slots[name]
 
 
     security.declareProtected(change_page_designs, 'moveElement')
-    def moveElement(self, source_info, target_info, RESPONSE=None):
-        """Changes an element's placement.
+    def moveElement(self, source_path, target_path, RESPONSE=None):
+        """Adds or moves an element to a slot.
         """
-        s_area, s_data = str(source_info).split('/', 1)
-        dummy, t_area, t_index = str(target_info).split('/', 2)
-        assert dummy == 'targets'
-
-        self._placement._p_changed = 1
-        if t_area:
-            t_index = int(t_index)
-        if s_area:
-            s_index = int(s_data)
-            if t_area == s_area:
-                # Adjust index if necessary.
-                if s_index <= t_index:
-                    t_index -= 1
-            element_id = self._placement[s_area][s_index]
-            del self._placement[s_area][s_index]
-        else:
-            # The user selected something from the palette.
-            element_id = s_data
-        if t_area:
-            lst = self._placement.get(t_area)
-            if not lst:
-                lst = []
-                self._placement[t_area] = lst
-            lst.insert(t_index, element_id)
-        # else the user moved the object to the palette.
+        if source_path:
+            source = self.getPhysicalRoot().restrictedTraverse(source_path)
+            if not IClipboardSource.isImplementedBy(source):
+                raise DesignError, 'Not a source'
+            getSecurityManager().validate(source, source,
+                                          'cut', source.cut)
+        if target_path:
+            target = self.getPhysicalRoot().restrictedTraverse(target_path)
+            if not IClipboardTarget.isImplementedBy(target):
+                raise DesignError, 'Not a target'
+            getSecurityManager().validate(target, target,
+                                          'paste', target.paste)
+
+        if source is not None:
+            element = source.cut()
+            if target is not None:
+                target.paste([element])
+
         if RESPONSE is not None:
             RESPONSE.redirect(self.absolute_url() + '/manage_editForm')
 
 
-    security.declareProtected(change_page_designs, 'getAreaNames')
-    def getAreaNames(self):
-        """Returns the names of all areas filled and declared by the template.
+    security.declareProtected(change_page_designs, 'getSlotNames')
+    def getSlotNames(self):
+        """Returns the names of all slots filled and declared by the template.
         """
         names = {}
         template = self.getTemplate()
         if template is not None:
-            areas = AreaNameCollector()
-            try:
-                template(areas=areas)
-            except:
-                # Hopefully, this exception gets reported elsewhere.
-                pass
-            else:
-                names = areas._getNames()
-        for k in self._placement.keys():
+            slots = SlotNameCollector().__of__(self)
+            template(slots=slots)
+            names = slots._getNames()
+        for k in self._slots.keys():
             names[k] = 1
         res = names.keys()
         res.sort()
         return res
 
 
-    security.declarePrivate('renderArea')
-    def renderArea(self, name, editable=0):
-        """Returns a sequence of rendered items."""
-        element_ids = self._placement.get(name, ())
-        res = []
-        for index in range(len(element_ids)):
-            s = None
-            element_id = element_ids[index]
-            try:
-                item = self.getElement(element_id)
-                # Render the item as a string.
-                if getattr(aq_base(item), 'isDocTemp', 0):
-                    # DTML
-                    s = item(self, self.REQUEST,
-                             editable=editable, index=index)
-                else:
-                    # Other kinds of callable objects
-                    s = item(editable=editable, index=index)
-            except:
-                if editable:
-                    t, v = sys.exc_info()[:2]
-                    s = escape('Unable to render "%s". %s: %s' % (
-                        element_id, getattr(t, '__name__', t), v))
-            if s is not None:
-                # Make sure it's a string or unicode.
-                s = '%s' % s
-                if editable:
-                    # Wrap the element.
-                    s = self.element_html % (name, index, s)
-                    if INDEPENDENT_TARGETS:
-                        # Make targets independent blocks.
-                        res.append(self.target_html % (name, index))
-                    else:
-                        # Targets are mixed into elements.
-                        s = (self.target_html % (name, index)) + s
-                res.append(s)
-        if editable:
-            # Add a final insertion point.
-            res.append(self.target_html % (name, len(element_ids)))
-        return res
+##    security.declarePrivate('renderSlot')
+##    def renderSlot(self, name, editable=0):
+##        """Returns a sequence of rendered items."""
+##        element_ids = self._slots.get(name, ())
+##        res = []
+##        for index in range(len(element_ids)):
+##            s = None
+##            element_id = element_ids[index]
+##            try:
+##                item = self.getElement(element_id)
+##                # Render the item as a string.
+##                if getattr(aq_base(item), 'isDocTemp', 0):
+##                    # DTML
+##                    s = item(self, self.REQUEST,
+##                             editable=editable, index=index)
+##                else:
+##                    # Other kinds of callable objects
+##                    s = item(editable=editable, index=index)
+##            except:
+##                if editable:
+##                    t, v = sys.exc_info()[:2]
+##                    s = escape('Unable to render "%s". %s: %s' % (
+##                        element_id, getattr(t, '__name__', t), v))
+##            if s is not None:
+##                # Make sure it's a string or unicode.
+##                s = '%s' % s
+##                if editable:
+##                    s = (self.target_html % (name, index)) + (
+##                        self.element_html % (name, index, s))
+##                res.append(s)
+##        if editable:
+##            # Add a final insertion point.
+##            res.append(self.target_html % (name, len(element_ids)))
+##        return res
+
+
+##    security.declarePrivate('getPaletteHTML')
+##    def getPaletteHTML(self):
+##        unplaced = {}
+##        for element_id in self.listAvailableElementIds():
+##            unplaced[element_id] = 1
+##        for slot in self._slots.values():
+##            for element_id in slot:
+##                if unplaced.has_key(element_id):
+##                    del unplaced[element_id]
+##        ids = unplaced.keys()
+##        ids.sort()
+##        items_html = []
+##        for element_id in ids:
+##            element = self.getElement(element_id)
+##            if hasattr(element, 'getIcon'):
+##                icon = element.getIcon()
+##            else:
+##                icon = element.icon
+##            items_html.append(self.palette_element_html % (
+##                element_id, icon, element_id))
 
+##        block = self.palette_elements_block % ''.join(items_html)
+##        return block
 
-    security.declarePrivate('getPaletteHTML')
-    def getPaletteHTML(self):
-        unplaced = {}
-        for element_id in self.listAvailableElementIds():
-            unplaced[element_id] = 1
-        for area in self._placement.values():
-            for element_id in area:
-                if unplaced.has_key(element_id):
-                    del unplaced[element_id]
-        ids = unplaced.keys()
-        ids.sort()
-        items_html = []
-        for element_id in ids:
-            element = self.getElement(element_id)
-            if hasattr(element, 'getIcon'):
-                icon = element.getIcon()
-            else:
-                icon = element.icon
-            items_html.append(self.palette_element_html % (
-                element_id, icon, element_id))
 
-        block = self.palette_elements_block % ''.join(items_html)
-        return block
-
-
-    security.declarePrivate('insertScripts')
-    def insertScripts(self, s):
+    security.declarePrivate('insertEditScripts')
+    def insertEditScripts(self, s):
         match = start_of_head_search(s)
         if match is None:
             raise DesignError, 'The template needs a HEAD element'
@@ -371,7 +284,7 @@
 
 
     security.declareProtected(change_page_designs, 'render')
-    def render(self, editable=0, **kw):
+    def render(self, **kw):
         """Renders the design."""
         if self._v_rendering:
             raise DesignError, "Recursive page design rendering"
@@ -381,34 +294,38 @@
             if template is None:
                 raise DesignError, (
                     'Template not set or not available')
-            if kw.has_key('areas'):
+            ls = kw.get('slots')
+            if ls is not None and ISlotProvider.isImplementedBy(ls):
                 # This page design is being used as the template for another
-                # page design.  Use the layered areas.
-                layered_areas = kw['areas']
+                # page design.  Use the layered slots.
+                layered_slots = ls
             else:
-                layered_areas = None
-            kw['areas'] = AreaRenderer(self, editable, layered_areas)
+                layered_slots = None
+
+            editable = kw.get('editable', 0)
+            sp = SlotProvider(editable, layered_slots).__of__(self)
+            kw['slots'] = sp
             res = template(**kw)
             if editable:
-                res = self.insertScripts(res)
+                res = self.insertEditScripts(res)
             return res
         finally:
             self._v_rendering = 0
 
 
-    index_html = None  # Use __call__
+    index_html = None  # Instruct ZPublisher to use __call__
 
     security.declareProtected(view, '__call__')
-    def __call__(self, editable=0, **kw):
+    def __call__(self, **kw):
         """Renders the design in non-edit mode."""
-        # Note that the editable argument is ignored on purpose.
-        return self.render(0, **kw)
+        kw['editable'] = 0
+        return self.render(**kw)
 
-    security.declareProtected(change_page_designs, 'manage_editForm')
-    def manage_editForm(self, editable=1, **kw):
+    security.declareProtected(change_page_designs, 'editForm')
+    def editForm(self, **kw):
         """Renders the design in edit mode."""
-        # Note that the editable argument is ignored on purpose.
-        return self.render(1, **kw)
+        kw['editable'] = 1
+        return self.render(**kw)
 
 
 Globals.InitializeClass(PageDesignBase)
@@ -447,8 +364,8 @@
     def manage_propertiesForm(self, *args, **kw):
         """Object properties form
         """
-        return self.manage_areas(*args, **kw)
-        
+        return self.manage_slots(*args, **kw)
+
 
 Globals.InitializeClass(PageDesign)