Corrected portal management
authorThierry Florac <thierry.florac@onf.fr>
Wed, 12 Jul 2017 12:23:32 +0200
changeset 39 f0ef959855e7
parent 38 e994aa2ae16e
child 40 71ff4ade6995
Corrected portal management
src/pyams_portal/page.py
src/pyams_portal/portlet.py
src/pyams_portal/template.py
--- a/src/pyams_portal/page.py	Wed Jul 12 12:21:40 2017 +0200
+++ b/src/pyams_portal/page.py	Wed Jul 12 12:23:32 2017 +0200
@@ -18,12 +18,13 @@
 # import interfaces
 from pyams_portal.interfaces import IPortalPage, IPortalContext, IPortalTemplateConfiguration, \
     IPortalTemplate, PORTAL_PAGE_KEY, IPortalPortletsConfiguration, PORTLETS_CONFIGURATION_KEY, \
-    ILocalTemplateHandler
+    ILocalTemplateHandler, LOCAL_TEMPLATE_NAME
 from zope.annotation.interfaces import IAnnotations
 from zope.traversing.interfaces import ITraversable
 
 # import packages
 from persistent import Persistent
+from pyams_portal.portlet import PortalPortletsConfiguration
 from pyams_portal.template import PortalTemplate
 from pyams_utils.adapter import adapter_config, ContextAdapter
 from pyams_utils.registry import query_utility
@@ -38,12 +39,17 @@
 
 @implementer(IPortalPage)
 class PortalPage(Persistent, Contained):
-    """Portal page persistent class"""
+    """Portal page persistent class
+
+    The page is the highest configuration level.
+    It defines which template is used (a shared or local one), which gives
+    the slot and portlet lists.
+    """
 
     _inherit_parent = FieldProperty(IPortalPage['inherit_parent'])
     _use_local_template = FieldProperty(IPortalPage['use_local_template'])
+    _local_template = FieldProperty(IPortalPage['local_template'])
     _shared_template = FieldProperty(IPortalPage['shared_template'])
-    _local_template = FieldProperty(IPortalPage['local_template'])
 
     @property
     def can_inherit(self):
@@ -55,8 +61,7 @@
 
     @inherit_parent.setter
     def inherit_parent(self, value):
-        if (not value) or self.can_inherit:
-            self._inherit_parent = value
+        self._inherit_parent = value if self.can_inherit else False
 
     @property
     def parent(self):
@@ -69,7 +74,7 @@
 
     @property
     def use_local_template(self):
-        return self._use_local_template if not self.inherit_parent else False
+        return False if self.inherit_parent else self._use_local_template
 
     @use_local_template.setter
     def use_local_template(self, value):
@@ -77,7 +82,7 @@
         if value and (self._local_template is None) and not self.inherit_parent:
             registry = get_current_registry()
             template = PortalTemplate()
-            template.name = "local"
+            template.name = LOCAL_TEMPLATE_NAME
             self._local_template = template
             registry.notify(ObjectCreatedEvent(template))
             locate(template, self, '++template++')
@@ -87,8 +92,12 @@
             noLongerProvides(self, ILocalTemplateHandler)
 
     @property
+    def local_template(self):
+        return self._local_template
+
+    @property
     def use_shared_template(self):
-        return not self.use_local_template
+        return IPortalPage(self.parent).use_shared_template if self.inherit_parent else not self._use_local_template
 
     @use_shared_template.setter
     def use_shared_template(self, value):
@@ -106,20 +115,14 @@
             self._shared_template = value
 
     @property
-    def local_template(self):
-        return IPortalPage(self.parent).local_template if self.inherit_parent else self._local_template
-
-    @local_template.setter
-    def local_template(self, value):
-        if not self.inherit_parent:
-            self._local_template = value
-
-    @property
     def template(self):
-        if self.use_local_template:
-            template = self.local_template
+        if self.inherit_parent:
+            template = IPortalPage(self.parent).template
         else:
-            template = self.shared_template
+            if self.use_local_template:
+                template = self.local_template
+            else:
+                template = self.shared_template
         if isinstance(template, str):
             template = query_utility(IPortalTemplate, name=template)
         return template
@@ -142,9 +145,7 @@
     """++template++ portal context traverser"""
 
     def traverse(self, name, furtherpath=None):
-        page = IPortalPage(self.context)
-        if page.use_local_template:
-            return page.local_template
+        return IPortalPage(self.context).local_template
 
 
 @adapter_config(context=IPortalContext, provides=IPortalTemplateConfiguration)
@@ -157,20 +158,25 @@
 @adapter_config(context=IPortalContext, provides=IPortalPortletsConfiguration)
 def PortalContextPortletsConfigurationAdapter(context):
     """Portal context portlets configuration adapter"""
+    # get page and template
     page = IPortalPage(context)
-    if page.inherit_parent:
-        return IPortalPortletsConfiguration(page.parent)
+    if page.use_local_template:
+        template = page.local_template
     else:
-        annotations = IAnnotations(context)
-        config = annotations.get(PORTLETS_CONFIGURATION_KEY)
-        if config is None:
-            config = annotations[PORTLETS_CONFIGURATION_KEY] = clone(IPortalPortletsConfiguration(page.template))
-            get_current_registry().notify(ObjectCreatedEvent(config))
-            locate(config, context)
-            for portlet_id, portlet_config in config.items():
-                portlet_cloned_config = clone(portlet_config)
-                config.set_portlet_configuration(portlet_id, portlet_cloned_config)
-        return config
+        template = page.template
+    portlets_config = IPortalPortletsConfiguration(template)
+    if page.use_local_template:
+        context = template
+    # get current configuration
+    annotations = IAnnotations(context)
+    config = annotations.get(PORTLETS_CONFIGURATION_KEY)
+    if config is None:
+        config = annotations[PORTLETS_CONFIGURATION_KEY] = PortalPortletsConfiguration.clone(portlets_config, context)
+    # check for missing portlets configuration
+    for portlet_id, portlet_config in portlets_config.items():
+        if portlet_id not in config:
+            config.set_portlet_configuration(portlet_id, clone(portlet_config))
+    return config
 
 
 @adapter_config(name='portlet', context=IPortalContext, provides=ITraversable)
--- a/src/pyams_portal/portlet.py	Wed Jul 12 12:21:40 2017 +0200
+++ b/src/pyams_portal/portlet.py	Wed Jul 12 12:23:32 2017 +0200
@@ -20,9 +20,10 @@
 import venusian
 
 # import interfaces
+from pyams_form.interfaces.form import IFormContextPermissionChecker
 from pyams_portal.interfaces import IPortlet, IPortletSettings, IPortletConfiguration, IPortletPreviewer, \
     IPortletRenderer, IPortalPortletsConfiguration, IPortalTemplate, IPortalContext, IPortalPage, \
-    IPortalTemplateConfiguration
+    IPortalTemplateConfiguration, MANAGE_TEMPLATE_PERMISSION
 from zope.traversing.interfaces import ITraversable
 
 # import packages
@@ -189,7 +190,12 @@
 
 @implementer(IPortletSettings)
 class PortletSettings(Persistent, Contained):
-    """Portlet settings persistent class"""
+    """Portlet settings persistent class
+
+    This class is supposed to be sub-classed by all custom portlet subclasses to
+    store their configuration settings.
+    Each portlet sub-class must define it's settings class in it's "settings_class" attribute.
+    """
 
     visible = FieldProperty(IPortletSettings['visible'])
     _renderer = FieldProperty(IPortletSettings['renderer'])
@@ -228,8 +234,12 @@
 class PortletConfiguration(Persistent, Contained):
     """Portlet configuration persistent class
 
-    PortletConfiguration.__parent__ points to context where configuration is applied
-    PortletConfiguration.parent points to context from where configuration is inherited
+    This class is a generic persistent class which is used to store all portlet
+    configuration and is *not* supposed to be sub-classed.
+
+    PortletConfiguration.__parent__ points to context where configuration is applied (each context or
+    local template).
+    PortletConfiguration.parent points to context from where configuration is inherited.
     """
 
     portlet_id = FieldProperty(IPortletConfiguration['portlet_id'])
@@ -263,7 +273,8 @@
             configuration = IPortalPortletsConfiguration(parent).get_portlet_configuration(self.portlet_id)
             if not configuration.inherit_parent:
                 return parent
-            if not IPortalContext.providedBy(parent.__parent__):
+            page = IPortalPage(parent)
+            if not page.inherit_parent:
                 break
             parent = parent.__parent__
         page = IPortalPage(parent, None)
@@ -302,6 +313,15 @@
         return self.context.settings
 
 
+@adapter_config(context=IPortletConfiguration, provides=IFormContextPermissionChecker)
+class PortletConfigurationPermissionChecker(ContextAdapter):
+    """Portlet configuration permission checker"""
+
+    @property
+    def edit_permission(self):
+        return MANAGE_TEMPLATE_PERMISSION
+
+
 #
 # Template portlets configuration
 #
@@ -310,11 +330,29 @@
 class PortalPortletsConfiguration(PersistentMapping, Contained):
     """Portal portlets configuration"""
 
+    @classmethod
+    def clone(cls, source_config, new_parent):
+        """Clone source configuration"""
+        configuration = source_config.__class__()
+        get_current_registry().notify(ObjectCreatedEvent(configuration))
+        locate(configuration, new_parent)
+        for config_id, config_portlet in source_config.items():
+            config = clone(config_portlet)
+            configuration[config_id] = config
+        return configuration
+
+    def __setitem__(self, key, value):
+        super(PortalPortletsConfiguration, self).__setitem__(key, value)
+        locate(value, self.__parent__, '++portlet++{0}'.format(key))
+
     def get_portlet_configuration(self, portlet_id):
         configuration = self.get(portlet_id)
-        if (configuration is None) and not IPortalTemplate.providedBy(self.__parent__):
-            template = IPortalPage(self.__parent__).template
-            portlets = IPortalPortletsConfiguration(template)
+        if configuration is None:
+            if IPortalTemplate.providedBy(self.__parent__):
+                portlets = IPortalPortletsConfiguration(self.__parent__)
+            else:
+                template = IPortalPage(self.__parent__).template
+                portlets = IPortalPortletsConfiguration(template)
             configuration = clone(portlets.get_portlet_configuration(portlet_id))
             get_current_registry().notify(ObjectCreatedEvent(configuration))
             self.set_portlet_configuration(portlet_id, configuration)
@@ -323,7 +361,6 @@
     def set_portlet_configuration(self, portlet_id, config):
         config.portlet_id = portlet_id
         self[portlet_id] = config
-        locate(config, self.__parent__, '++portlet++{0}'.format(portlet_id))
 
     def delete_portlet_configuration(self, portlet_id):
         if isinstance(portlet_id, int):
--- a/src/pyams_portal/template.py	Wed Jul 12 12:21:40 2017 +0200
+++ b/src/pyams_portal/template.py	Wed Jul 12 12:23:32 2017 +0200
@@ -175,10 +175,7 @@
 
     @property
     def slot_names(self):
-        if IPortalTemplate.providedBy(self.__parent__):
-            return self._slot_names
-        else:
-            return IPortalTemplateConfiguration(self.__parent__).slot_names
+        return self._slot_names
 
     @slot_names.setter
     def slot_names(self, value):
@@ -186,10 +183,7 @@
 
     @property
     def slot_order(self):
-        if IPortalTemplate.providedBy(self.__parent__):
-            return self._slot_order
-        else:
-            return IPortalTemplateConfiguration(self.__parent__).slot_order
+        return self._slot_order
 
     @slot_order.setter
     def slot_order(self, value):
@@ -197,10 +191,7 @@
 
     @property
     def slot_config(self):
-        if IPortalTemplate.providedBy(self.__parent__):
-            return self._slot_config
-        else:
-            return IPortalTemplateConfiguration(self.__parent__).slot_config
+        return self._slot_config
 
     @slot_config.setter
     def slot_config(self, value):
@@ -255,12 +246,7 @@
             return None
         config = self.slot_config.get(slot_name)
         if config is None:
-            if IPortalTemplate.providedBy(self.__parent__):
-                config = SlotConfiguration()
-            else:
-                config = clone(IPortalTemplateConfiguration(self.__parent__).get_slot_configuration(slot_name))
-                config.inherit_parent = True
-            self.slot_config[slot_name] = config
+            self.slot_config[slot_name] = config = SlotConfiguration()
             locate(config, self.__parent__)
         return config