src/pyams_portal/portlet.py
changeset 5 670b7956c689
parent 0 6f99128c6d48
child 16 ea990f1f72d2
--- a/src/pyams_portal/portlet.py	Thu Oct 08 12:26:42 2015 +0200
+++ b/src/pyams_portal/portlet.py	Mon Jan 18 18:09:46 2016 +0100
@@ -20,97 +20,42 @@
 import venusian
 
 # import interfaces
-from pyams_portal.interfaces import IPortlet, IPortletRenderer, IPortletConfiguration, \
-    IPortalPage, IPortletPreviewer
+from pyams_portal.interfaces import IPortlet, IPortletSettings, IPortletConfiguration, IPortletPreviewer, \
+    IPortletRenderer, IPortalPortletsConfiguration, IPortalTemplate, IPortalContext, IPortalPage
 from zope.schema.interfaces import IVocabularyFactory
+from zope.traversing.interfaces import ITraversable
 
 # import packages
 from persistent import Persistent
+from persistent.mapping import PersistentMapping
+from pyams_utils.adapter import adapter_config, ContextAdapter
 from pyams_utils.request import check_request
 from pyams_viewlet.viewlet import ContentProvider
 from pyramid.exceptions import ConfigurationError
+from pyramid.threadlocal import get_current_registry
 from zope.container.contained import Contained
+from zope.copy import clone
 from zope.interface import implementer, provider
+from zope.lifecycleevent import ObjectCreatedEvent
+from zope.location import locate
 from zope.schema.fieldproperty import FieldProperty
-from zope.schema.vocabulary import getVocabularyRegistry, SimpleVocabulary, SimpleTerm
+from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm, getVocabularyRegistry
 
 
-@implementer(IPortletConfiguration)
-class PortletConfiguration(Persistent, Contained):
-    """Portlet configuration"""
-
-    template = None
-    portlet_name = None
-    slot_name = FieldProperty(IPortletConfiguration['slot_name'])
-    position = FieldProperty(IPortletConfiguration['position'])
-    visible = FieldProperty(IPortletConfiguration['visible'])
-    _inherit_parent = FieldProperty(IPortletConfiguration['inherit_parent'])
-
-    def __init__(self, portlet):
-        self.portlet_name = portlet.name
-
-    @property
-    def can_inherit(self):
-        return IPortalPage.providedBy(self.__parent__)
-
-    @property
-    def inherit_parent(self):
-        return self._inherit_parent if self.can_inherit else False
-
-    @inherit_parent.setter
-    def inherit_parent(self, value):
-        self._inherit_parent = value
-
+#
+# Portlets utilities
+#
 
 @implementer(IPortlet)
 class Portlet(object):
-    """Base portlet content provider"""
+    """Base portlet utility"""
 
     permission = FieldProperty(IPortlet['permission'])
 
     toolbar_image = None
     toolbar_css_class = 'fa fa-fw fa-2x fa-edit'
 
-
-@provider(IVocabularyFactory)
-class PortletVocabulary(SimpleVocabulary):
-    """Portlet vocabulary"""
-
-    def __init__(self, context):
-        request = check_request()
-        translate = request.localizer.translate
-        utils = request.registry.getUtilitiesFor(IPortlet)
-        terms = [SimpleTerm(name, title=translate(util.label))
-                 for name, util in sorted(utils, key=lambda x: translate(x[1].label))]
-        super(PortletVocabulary, self).__init__(terms)
-
-getVocabularyRegistry().register('PyAMS portal portlets', PortletVocabulary)
-
-
-class PortletContentProvider(ContentProvider):
-    """Bae portlet content provider"""
-
-    def __init__(self, context, request, view, portlet_config):
-        super(PortletContentProvider, self).__init__(context, request, view)
-        self.__parent__ = view
-        self.configuration = portlet_config
-        self.portlet = self.request.registry.getUtility(IPortlet, name=portlet_config.portlet_name)
-
-    def __call__(self):
-        if self.portlet.permission and not self.request.has_permission(self.portlet.permission):
-            return ''
-        self.update()
-        return self.render()
-
-
-@implementer(IPortletPreviewer)
-class PortletPreviewer(PortletContentProvider):
-    """Portlet previewer adapter"""
-
-
-@implementer(IPortletRenderer)
-class PortletRenderer(PortletContentProvider):
-    """Portlet renderer adapter"""
+    settings_class = None
 
 
 class portlet_config(object):
@@ -158,3 +103,176 @@
 
         settings['_info'] = info.codeinfo  # fbo "action_method"
         return wrapped
+
+
+@provider(IVocabularyFactory)
+class PortletVocabulary(SimpleVocabulary):
+    """Portlet vocabulary"""
+
+    def __init__(self, context):
+        request = check_request()
+        translate = request.localizer.translate
+        utils = request.registry.getUtilitiesFor(IPortlet)
+        terms = [SimpleTerm(name, title=translate(util.label))
+                 for name, util in sorted(utils, key=lambda x: translate(x[1].label))]
+        super(PortletVocabulary, self).__init__(terms)
+
+getVocabularyRegistry().register('PyAMS portal portlets', PortletVocabulary)
+
+
+#
+# Portlets renderers
+#
+
+class PortletContentProvider(ContentProvider):
+    """Base portlet content provider"""
+
+    def __init__(self, context, request, view, settings):
+        super(PortletContentProvider, self).__init__(context, request, view)
+        self.__parent__ = view
+        self.settings = settings
+        self.portlet = self.request.registry.getUtility(IPortlet, name=settings.configuration.portlet_name)
+
+    def __call__(self):
+        if self.portlet.permission and not self.request.has_permission(self.portlet.permission):
+            return ''
+        self.update()
+        return self.render()
+
+
+@implementer(IPortletPreviewer)
+class PortletPreviewer(PortletContentProvider):
+    """Portlet previewer adapter"""
+
+
+@implementer(IPortletRenderer)
+class PortletRenderer(PortletContentProvider):
+    """Portlet renderer adapter"""
+
+
+#
+# Portlet configuration
+#
+
+@implementer(IPortletSettings)
+class PortletSettings(Persistent, Contained):
+    """Portlet settings persistent class"""
+
+    visible = FieldProperty(IPortletSettings['visible'])
+
+    __name__ = '++settings++'
+
+    def __init__(self, configuration):
+        self.__parent__ = configuration
+
+    @property
+    def configuration(self):
+        return self.__parent__
+
+
+@implementer(IPortletConfiguration)
+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
+    """
+
+    portlet_id = FieldProperty(IPortletConfiguration['portlet_id'])
+    portlet_name = None
+    _inherit_parent = FieldProperty(IPortletConfiguration['inherit_parent'])
+    _settings = FieldProperty(IPortletConfiguration['settings'])
+
+    def __init__(self, portlet):
+        self.portlet_name = portlet.name
+        self._settings = portlet.settings_class(self)
+
+    @property
+    def can_inherit(self):
+        return not IPortalTemplate.providedBy(self.__parent__)
+
+    @property
+    def inherit_parent(self):
+        return self._inherit_parent if self.can_inherit else False
+
+    @inherit_parent.setter
+    def inherit_parent(self, value):
+        if (not value) or self.can_inherit:
+            self._inherit_parent = value
+
+    @property
+    def parent(self):
+        parent = self.__parent__
+        if IPortalTemplate.providedBy(parent):
+            return parent
+        while IPortalContext.providedBy(parent):
+            configuration = IPortalPortletsConfiguration(parent).get_portlet_configuration(self.portlet_id)
+            if not configuration.inherit_parent:
+                return parent
+            if not IPortalContext.providedBy(parent.__parent__):
+                break
+            parent = parent.__parent__
+        page = IPortalPage(parent, None)
+        if page is not None:
+            return page.template
+
+    @property
+    def settings(self):
+        if self.inherit_parent:
+            return IPortalPortletsConfiguration(self.parent).get_portlet_configuration(self.portlet_id).settings
+        else:
+            return self._settings
+
+    @property
+    def editor_settings(self):
+        return self._settings
+
+
+@adapter_config(context=IPortlet, provides=IPortletConfiguration)
+def PortletConfigurationAdapter(portlet):
+    """Portlet configuration factory"""
+    return PortletConfiguration(portlet)
+
+
+@adapter_config(context=IPortletConfiguration, provides=IPortletSettings)
+def PortletConfigurationSettingsAdapter(configuration):
+    """Portlet configuration settings adapter"""
+    return configuration.settings
+
+
+@adapter_config(name='settings', context=IPortletConfiguration, provides=ITraversable)
+class PortletConfigurationSettingsTraverser(ContextAdapter):
+    """++settings++ portlet configuration traverser"""
+
+    def traverse(self, name, furtherpath=None):
+        return self.context.settings
+
+
+#
+# Template portlets configuration
+#
+
+@implementer(IPortalPortletsConfiguration)
+class PortalPortletsConfiguration(PersistentMapping, Contained):
+    """Portal portlets configuration"""
+
+    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)
+            configuration = clone(portlets.get_portlet_configuration(portlet_id))
+            get_current_registry().notify(ObjectCreatedEvent(configuration))
+            self.set_portlet_configuration(portlet_id, configuration)
+        return configuration
+
+    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):
+            portlet_id = (portlet_id,)
+        for p_id in portlet_id:
+            del self[p_id]