--- 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