src/pyams_portal/template.py
changeset 5 670b7956c689
parent 0 6f99128c6d48
child 20 c3f7c8290792
--- a/src/pyams_portal/template.py	Thu Oct 08 12:26:42 2015 +0200
+++ b/src/pyams_portal/template.py	Mon Jan 18 18:09:46 2016 +0100
@@ -16,9 +16,9 @@
 # import standard library
 
 # import interfaces
-from pyams_portal.interfaces import IPortalTemplate, IPortalTemplateConfiguration, IPortalContext, IPortalPage, \
-    IPortletConfiguration, IPortlet, IPortalTemplateContainer, IPortalWfTemplate, IPortalTemplateContainerConfiguration
-from pyams_workflow.interfaces import IWorkflowVersions
+from pyams_portal.interfaces import IPortalTemplateContainer, IPortalTemplateContainerConfiguration, \
+    IPortalTemplate, IPortalTemplateConfiguration, IPortalPortletsConfiguration, IPortlet, IPortletConfiguration, \
+    PORTLETS_CONFIGURATION_KEY, TEMPLATE_CONTAINER_CONFIGURATION_KEY, TEMPLATE_CONFIGURATION_KEY
 from zope.annotation.interfaces import IAnnotations
 from zope.lifecycleevent.interfaces import IObjectAddedEvent, IObjectRemovedEvent
 from zope.traversing.interfaces import ITraversable
@@ -27,9 +27,10 @@
 from persistent import Persistent
 from persistent.list import PersistentList
 from persistent.mapping import PersistentMapping
+from pyams_portal.portlet import PortalPortletsConfiguration
 from pyams_portal.slot import SlotConfiguration
 from pyams_utils.adapter import adapter_config, ContextAdapter
-from pyams_utils.registry import get_local_registry
+from pyams_utils.registry import get_local_registry, get_utility
 from pyams_utils.request import check_request
 from pyramid.events import subscriber
 from pyramid.threadlocal import get_current_registry
@@ -39,79 +40,85 @@
 from zope.copy import clone
 from zope.interface import implementer
 from zope.lifecycleevent import ObjectCreatedEvent
-from zope.location.location import locate
+from zope.location import locate
 from zope.schema.fieldproperty import FieldProperty
 from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm, getVocabularyRegistry
 
 
+#
+# Portal templates container
+#
+
 @implementer(IPortalTemplateContainer)
 class PortalTemplateContainer(Folder):
-    """Portal template container"""
+    """Portal templates container"""
+
+    last_portlet_id = FieldProperty(IPortalTemplateContainer['last_portlet_id'])
+
+    def get_portlet_id(self):
+        self.last_portlet_id += 1
+        return self.last_portlet_id
 
 
 @implementer(IPortalTemplateContainerConfiguration)
 class PortalTemplateContainerConfiguration(Persistent, Contained):
     """Portal template container configuration"""
 
-    selected_portlets = FieldProperty(IPortalTemplateContainerConfiguration['selected_portlets'])
-
-
-PORTAL_TEMPLATE_CONTAINER_CONFIGURATION_KEY = 'pyams_portal.container.configuration'
+    toolbar_portlets = FieldProperty(IPortalTemplateContainerConfiguration['toolbar_portlets'])
 
 
 @adapter_config(context=IPortalTemplateContainer, provides=IPortalTemplateContainerConfiguration)
-def PortalTemplateContainerConfigurationFactory(context):
+def PortalTemplateContainerConfigurationAdapter(context):
     """Portal template container configuration factory"""
     annotations = IAnnotations(context)
-    config = annotations.get(PORTAL_TEMPLATE_CONTAINER_CONFIGURATION_KEY)
+    config = annotations.get(TEMPLATE_CONTAINER_CONFIGURATION_KEY)
     if config is None:
-        config = annotations[PORTAL_TEMPLATE_CONTAINER_CONFIGURATION_KEY] = PortalTemplateContainerConfiguration()
+        config = annotations[TEMPLATE_CONTAINER_CONFIGURATION_KEY] = PortalTemplateContainerConfiguration()
         get_current_registry().notify(ObjectCreatedEvent(config))
         locate(config, context)
     return config
 
 
+#
+# Portal template base class
+#
+
 @implementer(IPortalTemplate)
 class PortalTemplate(Persistent, Contained):
-    """Portal template persistent class"""
+    """Portal template class"""
 
     name = FieldProperty(IPortalTemplate['name'])
 
 
-@implementer(IPortalWfTemplate)
-class PortalWfTemplate(Persistent, Contained):
-    """Portal template workflow manager class"""
-
-    content_class = PortalTemplate
-    workflow_name = 'PyAMS portal template workflow'
-    view_permission = None
-
-
-@subscriber(IObjectAddedEvent, context_selector=IPortalWfTemplate)
+@subscriber(IObjectAddedEvent, context_selector=IPortalTemplate)
 def handle_added_template(event):
     """Register shared template"""
     registry = get_local_registry()
     if (registry is not None) and IPortalTemplateContainer.providedBy(event.newParent):
-        registry.registerUtility(event.object, IPortalWfTemplate, name=event.object.__name__)
+        registry.registerUtility(event.object, IPortalTemplate, name=event.object.name)
 
 
-@subscriber(IObjectRemovedEvent, context_selector=IPortalWfTemplate)
+@subscriber(IObjectRemovedEvent, context_selector=IPortalTemplate)
 def handle_removed_template(event):
     """Unregister removed template"""
     registry = get_local_registry()
     if (registry is not None) and IPortalTemplateContainer.providedBy(event.oldParent):
-        registry.unregisterUtility(event.object, IPortalWfTemplate, name=event.object.__name__)
+        registry.unregisterUtility(event.object, IPortalTemplate, name=event.object.name)
 
 
 class PortalTemplatesVocabulary(UtilityVocabulary):
     """Portal templates vocabulary"""
 
-    interface = IPortalWfTemplate
+    interface = IPortalTemplate
     nameOnly = True
 
 getVocabularyRegistry().register('PyAMS portal templates', PortalTemplatesVocabulary)
 
 
+#
+# Portal template configuration
+#
+
 @implementer(IPortalTemplateConfiguration)
 class PortalTemplateConfiguration(Persistent, Contained):
     """Portal template configuration"""
@@ -119,25 +126,21 @@
     rows = FieldProperty(IPortalTemplateConfiguration['rows'])
     _slot_names = FieldProperty(IPortalTemplateConfiguration['slot_names'])
     _slot_order = FieldProperty(IPortalTemplateConfiguration['slot_order'])
-    _slots = FieldProperty(IPortalTemplateConfiguration['slots'])
-    slot_config = FieldProperty(IPortalTemplateConfiguration['slot_config'])
-    portlet_config = FieldProperty(IPortalTemplateConfiguration['portlet_config'])
+    _slot_config = FieldProperty(IPortalTemplateConfiguration['slot_config'])
 
     def __init__(self):
         self._slot_names = PersistentList()
         self._slot_order = PersistentMapping()
         self._slot_order[0] = PersistentList()
-        self._slots = PersistentMapping()
-        self._slots[0] = PersistentMapping()
         self.slot_config = PersistentMapping()
-        self.portlet_config = PersistentMapping()
+
+    # rows management
 
     def add_row(self):
         """Add new row and return last row index (0 based)"""
         self.rows += 1
         last_index = self.rows - 1
         self.slot_order[last_index] = PersistentList()
-        self.slots[last_index] = PersistentMapping()
         return last_index
 
     def set_row_order(self, order):
@@ -145,35 +148,31 @@
         if not isinstance(order, (list, tuple)):
             order = list(order)
         old_slot_order = self.slot_order
-        old_slots = self.slots
         assert len(order) == self.rows
         new_slot_order = PersistentMapping()
-        new_slots = PersistentMapping()
         for index, row_id in enumerate(order):
             new_slot_order[index] = old_slot_order.get(row_id) or PersistentList()
-            new_slots[index] = old_slots.get(row_id) or PersistentMapping()
         if self.slot_order != new_slot_order:
             self.slot_order = new_slot_order
-            self.slots = new_slots
 
     def delete_row(self, row_id):
         """Delete template row"""
-        assert row_id in self.slots
-        for slot_name in self.slots.get(row_id, {}).keys():
+        assert row_id in self.slot_order
+        for slot_name in self.slot_order.get(row_id, ()):
+            config = IPortalPortletsConfiguration(self.__parent__)
+            config.delete_portlet_configuration(self.slot_config[slot_name].portlet_ids)
             if slot_name in self.slot_names:
                 self.slot_names.remove(slot_name)
             if slot_name in self.slot_config:
                 del self.slot_config[slot_name]
-            if slot_name in self.portlet_config:
-                del self.portlet_config[slot_name]
         for index in range(row_id, self.rows-1):
             self.slot_order[index] = self.slot_order[index+1]
-            self.slots[index] = self.slots[index+1]
         if self.rows > 0:
             del self.slot_order[self.rows-1]
-            del self.slots[self.rows-1]
         self.rows -= 1
 
+    # slots management
+
     @property
     def slot_names(self):
         if IPortalTemplate.providedBy(self.__parent__):
@@ -197,15 +196,15 @@
         self._slot_order = value
 
     @property
-    def slots(self):
+    def slot_config(self):
         if IPortalTemplate.providedBy(self.__parent__):
-            return self._slots
+            return self._slot_config
         else:
-            return IPortalTemplateConfiguration(self.__parent__).slots
+            return IPortalTemplateConfiguration(self.__parent__).slot_config
 
-    @slots.setter
-    def slots(self, value):
-        self._slots = value
+    @slot_config.setter
+    def slot_config(self, value):
+        self._slot_config = value
 
     def add_slot(self, slot_name, row_id=None):
         assert slot_name not in self.slot_names
@@ -216,10 +215,6 @@
         if row_id not in self.slot_order:
             self.slot_order[row_id] = PersistentList()
         self.slot_order[row_id].append(slot_name)
-        # init slots portlets
-        if row_id not in self.slots:
-            self.slots[row_id] = PersistentMapping()
-        self.slots[row_id][slot_name] = PersistentList()
         # init slots configuration
         slot = self.slot_config[slot_name] = SlotConfiguration(slot_name)
         locate(slot, self.__parent__)
@@ -228,18 +223,11 @@
     def set_slot_order(self, order):
         """Set slots order"""
         old_slot_order = self.slot_order
-        old_slots = self.slots
         new_slot_order = PersistentMapping()
-        new_slots = PersistentMapping()
         for row_id in sorted(map(int, order.keys())):
             new_slot_order[row_id] = PersistentList(order[row_id])
-            new_slots[row_id] = PersistentMapping()
-            for slot_name in order[row_id]:
-                old_row_id = self.get_slot_row(slot_name)
-                new_slots[row_id][slot_name] = old_slots[old_row_id][slot_name]
         if new_slot_order != old_slot_order:
             self.slot_order = new_slot_order
-            self.slots = new_slots
 
     def get_slot_row(self, slot_name):
         for row_id in self.slot_order:
@@ -280,102 +268,58 @@
         """Delete slot and associated portlets"""
         assert slot_name in self.slot_names
         row_id = self.get_slot_row(slot_name)
-        del self.portlet_config[slot_name]
+        # delete portlet configuration
+        config = IPortalPortletsConfiguration(self.__parent__)
+        config.delete_portlet_configuration(self.slot_config[slot_name].portlet_ids)
+        # delete slot configuration
         del self.slot_config[slot_name]
-        del self.slots[row_id][slot_name]
         self.slot_order[row_id].remove(slot_name)
         self.slot_names.remove(slot_name)
 
+    # portlets management
+
     def add_portlet(self, portlet_name, slot_name):
         """Add portlet to given slot"""
         assert slot_name in self.slot_names
-        row_id = self.get_slot_row(slot_name)
-        if slot_name not in self.slots.get(row_id):
-            self.slots[row_id][slot_name] = PersistentList()
-        self.slots[row_id][slot_name].append(portlet_name)
-        if slot_name not in self.portlet_config:
-            self.portlet_config[slot_name] = PersistentMapping()
-        position = len(self.slots[row_id][slot_name]) - 1
+        # get new portlet configuration
         portlet = get_current_registry().getUtility(IPortlet, name=portlet_name)
         config = IPortletConfiguration(portlet)
-        config.slot_name = slot_name
-        config.position = position
-        locate(config, self.__parent__, '++portlet++{0}::{1}'.format(slot_name, position))
-        self.portlet_config[slot_name][position] = config
+        # store portlet configuration
+        manager = get_utility(IPortalTemplateContainer)
+        IPortalPortletsConfiguration(self.__parent__).set_portlet_configuration(manager.get_portlet_id(), config)
+        # update slots configuration
+        self.slot_config[slot_name].portlet_ids.append(config.portlet_id)
         return {'portlet_name': portlet_name,
+                'portlet_id': config.portlet_id,
                 'slot_name': slot_name,
-                'position': position,
+                'position': len(self.slot_config[slot_name].portlet_ids) - 1,
                 'label': check_request().localizer.translate(portlet.label)}
 
+    def get_portlet_slot(self, portlet_id):
+        """Get portlet slot"""
+        for slot_name, config in self.slot_config.items():
+            if portlet_id in config.portlet_ids:
+                return self.get_slot_row(slot_name), slot_name
+        return None, None
+
     def set_portlet_order(self, order):
         """Set portlet order"""
-        source = order['from']
-        source_slot = source['slot']
-        source_row = self.get_slot_row(source_slot)
-        target = order['to']
-        target_slot = target['slot']
+        from_row, from_slot = self.get_portlet_slot(order['from'])
+        if from_slot is None:
+            return
+        target_slot = order['to']['slot']
         target_row = self.get_slot_row(target_slot)
-        portlet_config = self.portlet_config
-        old_config = portlet_config[source_slot].pop(source['position'])
-        target_config = PersistentMapping()
-        for index, (slot_name, portlet_name, position) in enumerate(zip(target['slots'], target['names'],
-                                                                        target['positions'])):
-            if (slot_name == source_slot) and (position == source['position']):
-                target_config[index] = old_config
-            else:
-                target_config[index] = portlet_config[slot_name][position]
-            target_config[index].slot_name = target_slot
-            target_config[index].position = index
-            locate(target_config[index], self.__parent__, '++portlet++{0}::{1}'.format(slot_name, index))
-        portlet_config[target_slot] = target_config
-        # re-order source portlets
-        config = portlet_config[source_slot]
-        for index, key in enumerate(sorted(config)):
-            if index != key:
-                config[index] = config.pop(key)
-                config[index].position = index
-                locate(config[index], self.__parent__, '++portlet++{0}::{1}'.format(source_slot, index))
-        # re-order target portlets
-        if target_slot != source_slot:
-            config = portlet_config[target_slot]
-            for index, key in enumerate(sorted(config)):
-                config[index] = config.pop(key)
-                config[index].position = index
-                locate(config[index], self.__parent__, '++portlet++{0}::{1}'.format(target_slot, index))
-        self.portlet_config = portlet_config
-        del self.slots[source_row][source_slot][source['position']]
-        self.slots[target_row][target_slot] = PersistentList(target['names'])
+        if target_row is None:
+            return
+        self.slot_config[from_slot].portlet_ids.remove(order['from'])
+        self.slot_config[target_slot].portlet_ids = PersistentList(order['to']['portlet_ids'])
 
-    def get_portlet_configuration(self, slot_name, position):
-        """Get portlet configuration"""
-        if slot_name not in self.slot_names:
-            return None
-        config = self.portlet_config.get(slot_name, {}).get(position)
-        if config is None:
-            if IPortalTemplate.providedBy(self.__parent__):
-                portlet_name = self.slots[slot_name][position]
-                portlet = get_current_registry().queryUtility(IPortlet, name=portlet_name)
-                config = IPortletConfiguration(portlet)
-            else:
-                config = clone(IPortalTemplateConfiguration(self.__parent__).get_portlet_configuration(slot_name,
-                                                                                                       position))
-                config.inherit_parent = True
-            self.portlet_config[slot_name][position] = config
-            locate(config, self.__parent__)
-        return config
-
-    def delete_portlet(self, slot_name, position):
+    def delete_portlet(self, portlet_id):
         """Delete portlet"""
-        assert slot_name in self.slot_names
-        row_id = self.get_slot_row(slot_name)
-        config = self.portlet_config[slot_name]
-        del config[position]
-        if len(config) and (position < max(tuple(config.keys()))):
-            for index, key in enumerate(sorted(config)):
-                config[index] = config.pop(key)
-                config[index].position = index
-                locate(config[index], self.__parent__, '++portlet++{0}::{1}'.format(slot_name, index))
-        del self.slots[row_id][slot_name][position]
+        row_id, slot_name = self.get_portlet_slot(portlet_id)
+        if slot_name is not None:
+            self.slot_config[slot_name].portlet_ids.remove(portlet_id)
+            IPortalPortletsConfiguration(self.__parent__).delete_portlet_configuration(portlet_id)
 
 
 class PortalTemplateSlotsVocabulary(SimpleVocabulary):
@@ -389,25 +333,9 @@
 getVocabularyRegistry().register('PyAMS template slots', PortalTemplateSlotsVocabulary)
 
 
-@adapter_config(name='portlet', context=IPortalTemplate, provides=ITraversable)
-class PortalTemplatePortletTraverser(ContextAdapter):
-    """++portlet++ namespace traverser"""
-
-    def traverse(self, name, furtherpath=None):
-        config = IPortalTemplateConfiguration(self.context)
-        if name:
-            slot_name, position = name.split('::')
-            return config.get_portlet_configuration(slot_name, int(position))
-        else:
-            return config
-
-
-TEMPLATE_CONFIGURATION_KEY = 'pyams_portal.template'
-
-
 @adapter_config(context=IPortalTemplate, provides=IPortalTemplateConfiguration)
 def PortalTemplateConfigurationFactory(context):
-    """Portal template configuration factory"""
+    """Portal template configuration adapter"""
     annotations = IAnnotations(context)
     config = annotations.get(TEMPLATE_CONFIGURATION_KEY)
     if config is None:
@@ -417,25 +345,29 @@
     return config
 
 
-@adapter_config(context=IPortalContext, provides=IPortalTemplateConfiguration)
-def PortalContextConfigurationFactory(context):
-    """Portal context configuration factory"""
-    page = IPortalPage(context)
-    if page.use_local_template:
-        template = IWorkflowVersions(page.template).get_last_versions()[0]
-        config = IPortalTemplateConfiguration(template)
-    else:
-        annotations = IAnnotations(context)
-        config = annotations.get(TEMPLATE_CONFIGURATION_KEY)
-        if config is None:
-            # we clone template configuration
-            config = annotations[TEMPLATE_CONFIGURATION_KEY] = clone(IPortalTemplateConfiguration(page.template))
-            get_current_registry().notify(ObjectCreatedEvent(config))
-            locate(config, context)
-    return config
+@adapter_config(name='portlet', context=IPortalTemplate, provides=ITraversable)
+class PortalTemplatePortletTraverser(ContextAdapter):
+    """++portlet++ template traverser"""
+
+    def traverse(self, name, furtherpath=None):
+        config = IPortalPortletsConfiguration(self.context)
+        if name:
+            return config.get_portlet_configuration(int(name))
+        else:
+            return config
 
 
-@adapter_config(context=IPortletConfiguration, provides=IPortalTemplateConfiguration)
-def PortalPortletConfigurationFactory(context):
-    """Portal portlet configuration factory"""
-    return IPortalTemplateConfiguration(context.__parent__)
+#
+# Template portlets configuration
+#
+
+@adapter_config(context=IPortalTemplate, provides=IPortalPortletsConfiguration)
+def PortalTemplatePortletsConfigurationAdapter(template):
+    """Portal template portlets configuration adapter"""
+    annotations = IAnnotations(template)
+    config = annotations.get(PORTLETS_CONFIGURATION_KEY)
+    if config is None:
+        config = annotations[PORTLETS_CONFIGURATION_KEY] = PortalPortletsConfiguration()
+        get_current_registry().notify(ObjectCreatedEvent(config))
+        locate(config, template)
+    return config