# HG changeset patch # User Thierry Florac # Date 1499855012 -7200 # Node ID f0ef959855e7649f508eeecb022a00528f4a9cc6 # Parent e994aa2ae16e5ea2691e81b4e063a1ddfc23450e Corrected portal management diff -r e994aa2ae16e -r f0ef959855e7 src/pyams_portal/page.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) diff -r e994aa2ae16e -r f0ef959855e7 src/pyams_portal/portlet.py --- 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): diff -r e994aa2ae16e -r f0ef959855e7 src/pyams_portal/template.py --- 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