Author: jmorliaguet Date: Thu Jun 15 23:10:24 2006 New Revision: 3420 Modified: cpsskins/branches/paris-sprint-2006/browser/tree/slot.py cpsskins/branches/paris-sprint-2006/doc/portlets.txt cpsskins/branches/paris-sprint-2006/elements/configure.zcml cpsskins/branches/paris-sprint-2006/setup/io/README.txt cpsskins/branches/paris-sprint-2006/setup/io/utils.py cpsskins/branches/paris-sprint-2006/standard/displays/boxgroup.py cpsskins/branches/paris-sprint-2006/standard/formats/configure.zcml
Log: - added an 'order' format to the slot's display that stores information about how portlets are ordered inside the slot. The information used to be stored inside the display element itself which meant that a new display had to be created to store the portlets of a same slot in different orders. - test / doc updates Modified: cpsskins/branches/paris-sprint-2006/browser/tree/slot.py ============================================================================== --- cpsskins/branches/paris-sprint-2006/browser/tree/slot.py (original) +++ cpsskins/branches/paris-sprint-2006/browser/tree/slot.py Thu Jun 15 23:10:24 2006 @@ -29,7 +29,7 @@ from cpsskins.browser.tree.interfaces import INodeDuplicating, INodeTraversing from cpsskins.elements.display import IGNORE_PERSPECTIVES from cpsskins.elements.display import USE_CURRENT_PERSPECTIVE -from cpsskins.elements.interfaces import IPortlet, IDisplayable +from cpsskins.elements.interfaces import IPortlet, IDisplayable, IFormattable from cpsskins.ontology import hasPortlet, hasPortletFromPerspective from cpsskins.relations import DyadicRelation, TriadicRelation from cpsskins.utils import getThemeManager, getRelationStorage @@ -46,22 +46,22 @@ context = self.context relations = getRelationStorage(context) + # TODO: refactor using relations if perspective is None or display.mode == IGNORE_PERSPECTIVES: portlets = relations.getSeconds(predicate=hasPortlet, first=context) elif display.mode == USE_CURRENT_PERSPECTIVE: portlets = relations.getSeconds(predicate=hasPortletFromPerspective, first=context, third=perspective) - # the portlet order is obtained from the display, if the display is - # iterable otherwise the portlets are sorted by their identifier. - def cmp_by_order(a, b): - if a in display and b in display: - index = display.index - return cmp(index(a), index(b)) - return cmp(a.identifier, b.identifier) + display = IDisplayable(context).getEffectiveDisplay(perspective) + order = IFormattable(display).getFormat(u'order') - # sort the portlets - #portlets.sort(cmp_by_order) + if order is not None: + def cmp_by_order(a, b): + index = order.index + return cmp(index(a.identifier), index(b.identifier)) + + portlets.sort(cmp_by_order) return portlets class SlotAdding(Adding): @@ -86,18 +86,12 @@ portlets.add(content) if perspective is None: - relation = DyadicRelation( - predicate=hasPortlet, - first=container, - second=content, - ) + relation = DyadicRelation(predicate=hasPortlet, first=container, + second=content) else: - relation = TriadicRelation( - predicate=hasPortletFromPerspective, - first=container, - second=content, - third=perspective, - ) + relation = TriadicRelation(predicate=hasPortletFromPerspective, + first=container, second=content, + third=perspective) relations = getThemeManager(container).getRelationStorage() relations.add(relation) @@ -106,7 +100,9 @@ # store the portlet's order in the slot's "BoxGroup" display display = IDisplayable(container).getEffectiveDisplay(perspective) - display.append(content) + order = IFormattable(display).getFormat(u'order') + if order is not None: + order.append(content.identifier) return content @@ -116,9 +112,9 @@ implements(INodeMoving) def move(self, content): + request = self.request src_container = getParent(content) dest_container = self.context - request = self.request if dest_container == src_container: return content @@ -180,20 +176,21 @@ ids = relations.search(predicate=hasPortletFromPerspective, first=container, second=content, third=perspective) - relations.remove(ids) del portlets[getName(content)] # store the portlet's order in the slot's "BoxGroup" display display = IDisplayable(container).getEffectiveDisplay(perspective) - display.remove(content) + order = IFormattable(display).getFormat(u'order') + if order is not None: + order.remove(content.identifier) class SlotOrdering(BrowserView): """A view for reordering elements in slots """ implements(INodeOrdering) - def setOrder(self, content, order): + def setOrder(self, content, position): """Set the element's order in the container. """ container = self.context @@ -208,10 +205,12 @@ ).getPerspective() display = IDisplayable(container).getEffectiveDisplay(perspective) - - if content in display: - display.remove(content) - display.insert(order, content) + order = IFormattable(display).getFormat(u'order') + if order is not None: + id = content.identifier + if id in order: + order.remove(id) + order.insert(position, id) def getOrder(self, content): """Get the element's order in the container. @@ -226,7 +225,9 @@ ).getPerspective() display = IDisplayable(container).getEffectiveDisplay(perspective) - return display.index(content) + order = IFormattable(display).getFormat(u'order') + if order is not None: + return order.index(content.identifier) class SlotDuplicating(BrowserView): """A view for duplicating elements in nodes @@ -256,7 +257,10 @@ perspective = len(rel) == 3 and rel.third or None display = IDisplayable(container).getEffectiveDisplay(perspective) - display.insert(0, duplicated) + order = IFormattable(display).getFormat(u'order') + if order is not None: + position = order.index(content.identifier) + order.insert(position, duplicated.identifier) # set the dest_slot -> portlet relation if perspective is None: @@ -268,5 +272,5 @@ third=perspective) relations.add(relation) duplicated.__parent__ = container - return duplicated.identifier + return duplicated Modified: cpsskins/branches/paris-sprint-2006/doc/portlets.txt ============================================================================== --- cpsskins/branches/paris-sprint-2006/doc/portlets.txt (original) +++ cpsskins/branches/paris-sprint-2006/doc/portlets.txt Thu Jun 15 23:10:24 2006 @@ -438,24 +438,23 @@ [] - Finally we clear the storage: >>> relations.clear() -PORTLET ORDERING -================ +PORTLET MANIPULATION +==================== -The portlets displayed in slots need to be ordered. The order information is -stored in the slot's display. +Portlets can be manipulated, i.e added, duplicated, removed, reordered inside a +same slot or be moved between different slots. -We set up some test environment: +We set up a test environment: >>> from cpsskins.tests.setup import makeSite >>> from cpsskins.tests.setup import addThemeManager, addThemeSkeleton - >>> root = getRootFolder() + >>> tmutil = addThemeManager(root, makeSite(root)) Let us create a theme with two slots ('slot_A', 'slot_B'): @@ -469,7 +468,7 @@ >>> theme[u'page'][u'block'][u'cell'][u'slot_a'] = slot_A >>> theme[u'page'][u'block'][u'cell'][u'slot_b'] = slot_B -The slots A and B have their own display +The slots A and B have their own display: >>> from cpsskins.elements.interfaces import IDisplayable >>> from cpsskins.standard.displays.boxgroup import BoxGroup @@ -486,6 +485,102 @@ >>> display_slot_A is not display_slot_B True -[...] +Adding +------ +Portlets are added into the slots with the INodeAdding view: + + >>> from cpsskins.browser.tree.slot import INodeAdding + >>> adding = getMultiAdapter((slot_A, request), INodeAdding) + + >>> portlet1 = Portlet(u'Portlet 1') + >>> added = adding.add(portlet1) + +Traversing +---------- +to get the list of portlets inside a slot we use the INodeTraversing view: + + >>> from cpsskins.browser.tree.slot import INodeTraversing + >>> traversing = getMultiAdapter((slot_A, request), INodeTraversing) + >>> traversing.getChildNodes() == [portlet1] + True + +Duplicating +----------- +to duplicate portlets inside a slot we use INodeDuplicating: + + >>> from cpsskins.browser.tree.slot import INodeDuplicating + >>> duplicating = getMultiAdapter((slot_A, request), INodeDuplicating) + + >>> duplicated = duplicating.duplicate(portlet1) + +we now have two identical portlets inside the slot: + + >>> traversing.getChildNodes() == [duplicated, added] + True + +Removing +-------- +to remove a portlet we use INodeRemoving: + + >>> from cpsskins.browser.tree.slot import INodeRemoving + >>> removing = getMultiAdapter((slot_A, request), INodeRemoving) + >>> removing.remove(duplicated) + +the second portlet is removed: + + >>> traversing.getChildNodes() == [added] + True + +we also remove the first portlet: + + >>> removing.remove(added) + >>> traversing.getChildNodes() == [] + True + +Reordering +---------- +portlets are ordered in slots if the slot's display has an 'order' format. +let us add a few portlets: + + >>> portlets = [] + >>> for i in range(4): + ... portlets.append(adding.add(Portlet(str(i)))) + + >>> traversing.getChildNodes() + [Portlet('0'), Portlet('1'), Portlet('2'), Portlet('3')] + + >>> from cpsskins.browser.tree.slot import INodeOrdering + >>> ordering = getMultiAdapter((slot_A, request), INodeOrdering) + + >>> ordering.getOrder(portlets[1]) + 1 + + >>> ordering.getOrder(portlets[2]) + 2 + + >>> ordering.setOrder(portlets[2], 3) + >>> ordering.getOrder(portlets[2]) + 3 + + >>> traversing.getChildNodes() + [Portlet('0'), Portlet('1'), Portlet('3'), Portlet('2')] + +Moving +------ +portlets are moved between slots using INodeMoving: + + >>> from cpsskins.browser.tree.slot import INodeMoving + >>> moving = getMultiAdapter((slot_B, request), INodeMoving) + >>> moving.move(portlets[1]) + +now Slot B contains one more portlet: + + >>> traversing_B = getMultiAdapter((slot_B, request), INodeTraversing) + >>> traversing_B.getChildNodes() + [Portlet('1')] + +and Slot A contains one portlet less: + >>> traversing.getChildNodes() + [Portlet('0'), Portlet('3'), Portlet('2')] Modified: cpsskins/branches/paris-sprint-2006/elements/configure.zcml ============================================================================== --- cpsskins/branches/paris-sprint-2006/elements/configure.zcml (original) +++ cpsskins/branches/paris-sprint-2006/elements/configure.zcml Thu Jun 15 23:10:24 2006 @@ -430,6 +430,10 @@ /> <cpsskins:format + name="order" + /> + + <cpsskins:format name="effect" types="scale" /> Modified: cpsskins/branches/paris-sprint-2006/setup/io/README.txt ============================================================================== --- cpsskins/branches/paris-sprint-2006/setup/io/README.txt (original) +++ cpsskins/branches/paris-sprint-2006/setup/io/README.txt Thu Jun 15 23:10:24 2006 @@ -178,7 +178,7 @@ >>> pageblock[u'cell1'][u'slot'] = slot >>> print toXML(pageblock, u'elements', (u'title', u'description'), - ... (u'slot',)) + ... (u'name',)) <?xml version="1.0" encoding="utf-8"?> <elements> <pageblock id="..." title="Some page block"> Modified: cpsskins/branches/paris-sprint-2006/setup/io/utils.py ============================================================================== --- cpsskins/branches/paris-sprint-2006/setup/io/utils.py (original) +++ cpsskins/branches/paris-sprint-2006/setup/io/utils.py Thu Jun 15 23:10:24 2006 @@ -131,7 +131,7 @@ exporter.archive = archive exporter.document = document exporter.fields_as_attributes = u'title', u'description' - exporter.ignored_fields = u'slot', + exporter.ignored_fields = u'name', exporter.save() archive[u'themes.xml'] = document.toprettyxml(indent=u' ', Modified: cpsskins/branches/paris-sprint-2006/standard/displays/boxgroup.py ============================================================================== --- cpsskins/branches/paris-sprint-2006/standard/displays/boxgroup.py (original) +++ cpsskins/branches/paris-sprint-2006/standard/displays/boxgroup.py Thu Jun 15 23:10:24 2006 @@ -42,106 +42,18 @@ vocabulary="Display Modes", required=True) - def insert(i, item): - """ """ - - def remove(item): - """ """ - - def __contains__(value): - """Return whether the value is contained in the box group""" - - def index(value): - """Return the position of a value in the box group""" - class BoxGroup(Persistent, Display): """A Box Group is used to display a group of portlets. - - >>> from zope.component import getGlobalSiteManager - >>> from cpsskins.thememanager import ThemeManagementFolder - >>> from cpsskins.thememanager import IThemeManagementFolder - - >>> gsm = getGlobalSiteManager() - >>> gsm.registerUtility(ThemeManagementFolder(), IThemeManagementFolder, - ... name=u'themes') - - >>> from cpsskins.elements.portlet import TestPortlet as Portlet - >>> portlet1, portlet2 = (Portlet('portlet1'), Portlet('portlet2')) - >>> portlet1.identifier = 1 - >>> portlet2.identifier = 2 - - >>> boxgroup = BoxGroup() - >>> boxgroup.identifier = 12345 - >>> boxgroup - <BoxGroup: 12345> - - >>> boxgroup.insert(0, portlet1) - >>> boxgroup.insert(1, portlet2) - >>> portlet1 in boxgroup and portlet2 in boxgroup - True - - >>> list(boxgroup) - [1, 2] - - >>> boxgroup.remove(portlet1) - >>> list(boxgroup) - [2] - - >>> portlet1 in boxgroup - False - - >>> portlet2 in boxgroup - True - - >>> boxgroup.append(portlet1) - >>> list(boxgroup) - [2, 1] - """ implements(IBoxGroup) def __init__(self, **kw): super(BoxGroup, self).__init__(**kw) - self._order = PersistentList() self.mode = USE_CURRENT_PERSPECTIVE def __repr__(self): return "<BoxGroup: %s>" % self.identifier - def insert(self, order, item): - """Insert an element at the specified 'order' position - """ - self._order.insert(order, item.identifier) - self._p_changed = True - logger.debug("Inserted %s in %s at position %s", repr(item), repr(self), - order) - - def remove(self, item): - """Remove an element - """ - self._order.remove(item.identifier) - self._p_changed = True - logger.debug("Removed %s from %s", repr(item), repr(self)) - - def append(self, item): - """Add an element in the last position. - """ - self._order.append(item.identifier) - self._p_changed = True - logger.debug("Appended %s to %s", repr(item), repr(self)) - - def __len__(self): - return len(self._order) - - def index(self, item): - return self._order.index(item.identifier) - - def __contains__(self, item): - return item.identifier in self._order - - def __iter__(self): - return iter(self._order) - # List of display modes def DisplayModes(context): Modified: cpsskins/branches/paris-sprint-2006/standard/formats/configure.zcml ============================================================================== --- cpsskins/branches/paris-sprint-2006/standard/formats/configure.zcml (original) +++ cpsskins/branches/paris-sprint-2006/standard/formats/configure.zcml Thu Jun 15 23:10:24 2006 @@ -41,4 +41,12 @@ predicate=".effect.hasEffect" /> + <!-- Order --> + <cpsskins:format + name="order" + schema=".order.IOrder" + class=".order.Order" + predicate=".order.hasOrder" + /> + </configure> -- http://lists.nuxeo.com/mailman/listinfo/z3lab-checkins