Use object factory in annotations adapters
authorThierry Florac <thierry.florac@onf.fr>
Thu, 17 Jan 2019 10:45:18 +0100
changeset 252 6cd660d0ac45
parent 251 3c9a3e1d00cc
child 253 7f51b37f1d4f
Use object factory in annotations adapters
src/pyams_portal/interfaces.py
src/pyams_portal/interfaces/__init__.py
src/pyams_portal/page.py
src/pyams_portal/portlet.py
src/pyams_portal/template.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_portal/interfaces.py	Thu Jan 17 10:45:18 2019 +0100
@@ -0,0 +1,453 @@
+#
+# Copyright (c) 2008-2015 Thierry Florac <tflorac AT ulthar.net>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+
+__docformat__ = 'restructuredtext'
+
+from zope.annotation.interfaces import IAttributeAnnotatable
+from zope.container.constraints import contains
+from zope.container.interfaces import IContainer
+from zope.contentprovider.interfaces import IContentProvider
+from zope.interface import Attribute, Interface
+from zope.location.interfaces import IContained, ILocation
+from zope.schema import Bool, Choice, Int, List, Object, TextLine
+
+from pyams_portal import _
+from pyams_security.schema import PermissionField
+from pyams_utils.schema import PersistentDict, PersistentList
+
+
+MANAGE_TEMPLATE_PERMISSION = 'pyams_portal.manage_template'
+
+DESIGNER_ROLE = 'pyams.TemplatesManager'
+'''Designer role is allowed to manage presentation templates'''
+
+
+#
+# Portlet interfaces
+#
+
+class IPortlet(Interface):
+    """Portlet utility interface
+
+    Portlets are registered utilities providing IPortlet
+    """
+
+    name = Attribute("Portlet internal name")
+
+    label = Attribute("Portlet visible name")
+
+    permission = PermissionField(title="Portlet permission",
+                                 description="Permission required to display portlet",
+                                 required=False)
+
+    toolbar_image = Attribute("Portlet toolbar image")
+
+    toolbar_css_class = Attribute("Portlet toolbar CSS class")
+
+    settings_factory = Attribute("Portlet settings factory")
+
+
+class IPortletCSSClass(Interface):
+    """Portlet CSS class interface"""
+
+
+class IPortletAddingInfo(Interface):
+    """Portlet adding info interface"""
+
+    portlet_name = Choice(title=_("Portlet"),
+                          vocabulary="PyAMS portal portlets")
+
+    slot_name = Choice(title=_("Slot name"),
+                       description=_("Slot name to which this configuration applies"),
+                       required=True,
+                       vocabulary='PyAMS template slots')
+
+
+class IPortletSettings(ILocation, IAttributeAnnotatable):
+    """Portlet settings interface
+
+    Portlet settings is parented to it's configuration
+    """
+
+    configuration = Attribute("Settings parent configuration")
+
+    renderer = Choice(title=_("Portlet renderer"),
+                      description=_("Name of renderer used to render this portlet"),
+                      vocabulary='PyAMS portlet renderers',
+                      required=False,
+                      default='')
+
+    def get_renderer(self, request=None):
+        """Get renderer utility"""
+
+
+PORTLETS_CONFIGURATION_KEY = 'pyams_portal.portlets'
+
+
+class IPortletConfiguration(ILocation):
+    """Portlet common configuration interface
+
+    This is generic configuration settings common to all portlets.
+    Portlet configuration is parented to:
+     - it's template if parent is the template
+     - it's context if parent is a portal context
+    """
+
+    portlet_id = Int(title="Portlet ID",
+                     required=True)
+
+    portlet_name = Attribute("Portlet name")
+
+    def get_portlet(self):
+        """Return portlet utility matching current portlet name"""
+
+    can_inherit = Attribute("Can inherit parent configuration?")
+
+    parent = Attribute("Portlet configuration parent")
+
+    inherit_parent = Bool(title=_("Inherit parent configuration?"),
+                          description=_("This option is only available if context's parent is using the same "
+                                        "template..."),
+                          required=True,
+                          default=True)
+
+    settings = Object(title="Portlet local settings",
+                      schema=IPortletSettings,
+                      readonly=True)
+
+    editor_settings = Attribute("Editor settings")
+
+    def get_settings(self, allow_inherit=True):
+        """Get configuration settings, with or without inheritance"""
+
+
+class IPortletContentProvider(IContentProvider):
+    """Portlet content provider"""
+
+    portlet = Object(title="Portlet utility",
+                     schema=IPortlet)
+
+    configuration = Object(title="Portlet renderer configuration",
+                           schema=IPortletConfiguration)
+
+
+class IPortletPreviewer(IPortletContentProvider):
+    """Portlet previewer interface
+
+    A portlet previewer should be defined as an adapter for a context,
+    a request, a view and a portlet
+    """
+
+
+class IPortletRenderer(IPortletContentProvider):
+    """Portlet renderer interface
+
+    A portlet renderer should be defined as an adapter for a context,
+    a request, a view and a portlet
+    """
+
+    label = Attribute("Renderer name")
+
+    settings_interface = Attribute("Settings interface defined for this renderer")
+    settings_key = Attribute("Annotations key used to store renderer settings")
+
+    target_interface = Attribute("Target interface provided by this renderer")
+
+    use_portlets_cache = Attribute("Can renderer use rendering cache?")
+    use_authentication = Attribute("If 'True', portlet cache entry key is based on current authentication")
+
+    resources = Attribute("Tuple of Fanstatic resources needed by this renderer")
+
+
+PORTLET_RENDERER_SETTINGS_KEY = 'pyams_portal.renderer.settings::{0}'
+
+
+class IPortletRendererSettings(Interface):
+    """Portlet renderer settings interface"""
+
+
+class IPortalPortletsConfiguration(IContained):
+    """Portal template portlet configuration interface"""
+
+    def get_portlet_configuration(self, portlet_id):
+        """Get portlet configuration for given slot"""
+
+    def set_portlet_configuration(self, portlet_id, config):
+        """Set portlet configuration"""
+
+    def delete_portlet_configuration(self, portlet_id):
+        """Delete portlet configuration"""
+
+
+#
+# Slot interfaces
+#
+
+PORTAL_SLOTS_KEY = 'pyams_portal.slots'
+
+
+class ISlot(Interface):
+    """Portal template slot interface"""
+
+    name = TextLine(title=_("Slot name"),
+                    description=_("This name must be unique in a given template"),
+                    required=True)
+
+    row_id = Int(title=_("Row ID"),
+                 required=False)
+
+
+class ISlotConfiguration(Interface):
+    """Portal slot configuration"""
+
+    template = Attribute("Slot template")
+
+    slot_name = TextLine(title="Slot name")
+
+    portlet_ids = PersistentList(title="Portlet IDs",
+                                 value_type=Int())
+
+    visible = Bool(title=_("Visible slot?"),
+                   description=_("Select 'no' to hide this slot..."),
+                   required=True,
+                   default=True)
+
+    xs_width = Int(title=_("Extra small device width"),
+                   description=_("Slot width, in columns count, on extra small devices (phones...); "
+                                 "set to 0 to hide the portlet"),
+                   required=False,
+                   min=0,
+                   max=12)
+
+    sm_width = Int(title=_("Small device width"),
+                   description=_("Slot width, in columns count, on small devices (tablets...); "
+                                 "set to 0 to hide the portlet"),
+                   required=False,
+                   min=0,
+                   max=12)
+
+    md_width = Int(title=_("Medium devices width"),
+                   description=_("Slot width, in columns count, on medium desktop devices (>= 992 pixels); "
+                                 "set to 0 to hide the portlet"),
+                   required=False,
+                   min=0,
+                   max=12)
+
+    lg_width = Int(title=_("Large devices width"),
+                   description=_("Slot width, in columns count, on large desktop devices (>= 1200 pixels); "
+                                 "set to 0 to hide the portlet"),
+                   required=False,
+                   min=0,
+                   max=12)
+
+    css_class = TextLine(title=_("CSS class"),
+                         description=_("CSS class applied to this slot"),
+                         required=False)
+
+    def get_css_class(self, device=None):
+        """Get current CSS class"""
+
+    def get_width(self, device=None):
+        """Get slot width for each or given device"""
+
+    def set_width(self, width, device=None):
+        """Set width in columns count for given device"""
+
+
+class ISlotRenderer(IContentProvider):
+    """Slot renderer"""
+
+
+#
+# Template configuration interfaces
+#
+
+class IPortalTemplateConfiguration(IContained):
+    """Portal template configuration interface"""
+
+    # Rows configuration
+    rows = Int(title="Rows count",
+               required=True,
+               default=1,
+               min=0)
+
+    def add_row(self):
+        """Add new row"""
+
+    def set_row_order(self, order):
+        """Change template rows order"""
+
+    def delete_row(self, row_id):
+        """Delete template row"""
+
+    # Slots configuration
+    slot_names = PersistentList(title="Slot names",
+                                value_type=TextLine())
+
+    slot_order = PersistentDict(title="Solts order",
+                                key_type=Int(),  # row index
+                                value_type=PersistentList(value_type=TextLine()))  # slot name
+
+    slot_config = PersistentDict(title="Slots configuration",
+                                 key_type=TextLine(),  # slot name
+                                 value_type=Object(schema=ISlotConfiguration),
+                                 required=False)
+
+    def add_slot(self, slot_name, row_id=None):
+        """Add slot with given name"""
+
+    def set_slot_order(self, order):
+        """Change template slots order"""
+
+    def get_slot_row(self, slot_name):
+        """Get row containing given slot"""
+
+    def get_slots(self, row_id):
+        """Get ordered list of slots for given row ID"""
+
+    def get_slots_width(self, device=None):
+        """Get slots width for given or all device(s)"""
+
+    def set_slot_width(self, slot_name, device, width):
+        """Set slot width for given device"""
+
+    def get_slot_configuration(self, slot_name):
+        """Get slot configuration for given slot"""
+
+    def delete_slot(self, slot_name):
+        """Delete template slot"""
+
+    # Portlets configuration
+    def add_portlet(self, portlet_name, slot_name):
+        """Add portlet to givben slot"""
+
+    def get_portlet_slot(self, portlet_id):
+        """Get row ID and slot for given portlet"""
+
+    def set_portlet_order(self, slot_name, order):
+        """Set template portlets order"""
+
+    def delete_portlet(self, slot_name, position):
+        """Delete template portlet"""
+
+
+#
+# Portal templates interfaces
+#
+
+LOCAL_TEMPLATE_NAME = '__local__'
+
+TEMPLATE_CONFIGURATION_KEY = 'pyams_portal.template'
+
+TEMPLATE_CONTAINER_CONFIGURATION_KEY = 'pyams_portal.container.configuration'
+
+PORTAL_PAGE_KEY = 'pyams_portal.page'
+
+
+class ILocalTemplateHandler(IAttributeAnnotatable):
+    """Base interface for local template handler"""
+
+
+class IPortalTemplate(ILocalTemplateHandler):
+    """Portal template interface
+
+    A portal template is a named utility providing a name and a set of slots containing portlets
+    """
+
+    name = TextLine(title=_("Template name"),
+                    description=_("Two registered templates can't share the same name..."),
+                    required=True)
+
+
+class IPortalTemplateContainer(IContainer, IAttributeAnnotatable):
+    """Portal template container interface"""
+
+    contains(IPortalTemplate)
+
+    last_portlet_id = Int(title="Last portlet ID",
+                          required=True,
+                          default=1,
+                          min=0)
+
+    def get_portlet_id(self):
+        """Get new portlet ID"""
+
+
+class IPortalTemplateContainerConfiguration(Interface):
+    """Portal templates container configuration"""
+
+    toolbar_portlets = List(title=_("Toolbar portlets"),
+                            description=_("These portlets will be directly available in templates configuration "
+                                          "page toolbar"),
+                            value_type=Choice(vocabulary="PyAMS portal portlets"),
+                            required=False)
+
+
+class IPortalTemplateRenderer(IContentProvider):
+    """Portal template renderer
+
+    A portal template renderer should be implemented as an adapter for a context, a request
+    and a template
+    """
+
+
+PREVIEW_MODE = 'PREVIEW_MODE'
+
+
+class IPortalPage(Interface):
+    """Portal page interface
+
+    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.
+    """
+
+    can_inherit = Attribute("Can inherit parent template?")
+
+    parent = Attribute("Parent from which to inherit, the real parent or the source template")
+
+    inherit_parent = Bool(title=_("Inherit parent template?"),
+                          description=_("Should we reuse parent template?"),
+                          required=True,
+                          default=True)
+
+    override_parent = Bool(title=_("Override parent template?"),
+                           description=_("Should we override parent template?"),
+                           required=True,
+                           default=False)
+
+    use_local_template = Bool(title=_("Use local template?"),
+                              description=_("If 'yes', you can define a custom local template instead of "
+                                            "a shared template"),
+                              required=True,
+                              default=False)
+
+    local_template = Object(title=_("Local template"),
+                            schema=IPortalTemplate,
+                            required=False,
+                            readonly=True)
+
+    use_shared_template = Bool(title=_("Use shared template?"),
+                               description=_("If 'yes', you can select a shared template"),
+                               required=True,
+                               default=True)
+
+    shared_template = Choice(title=_("Page template"),
+                             description=_("Template used for this page"),
+                             vocabulary='PyAMS portal templates',
+                             required=False)
+
+    template = Attribute("Used template")
+
+
+class IPortalContext(IAttributeAnnotatable):
+    """Portal context marker interface"""
--- a/src/pyams_portal/interfaces/__init__.py	Thu Jan 03 17:50:58 2019 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,453 +0,0 @@
-#
-# Copyright (c) 2008-2015 Thierry Florac <tflorac AT ulthar.net>
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
-# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
-# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
-# FOR A PARTICULAR PURPOSE.
-#
-
-__docformat__ = 'restructuredtext'
-
-from zope.annotation.interfaces import IAttributeAnnotatable
-from zope.container.constraints import contains
-from zope.container.interfaces import IContainer
-from zope.contentprovider.interfaces import IContentProvider
-from zope.interface import Attribute, Interface
-from zope.location.interfaces import IContained, ILocation
-from zope.schema import Bool, Choice, Int, List, Object, TextLine
-
-from pyams_portal import _
-from pyams_security.schema import PermissionField
-from pyams_utils.schema import PersistentDict, PersistentList
-
-
-MANAGE_TEMPLATE_PERMISSION = 'pyams_portal.manage_template'
-
-DESIGNER_ROLE = 'pyams.TemplatesManager'
-'''Designer role is allowed to manage presentation templates'''
-
-
-#
-# Portlet interfaces
-#
-
-class IPortlet(Interface):
-    """Portlet utility interface
-
-    Portlets are registered utilities providing IPortlet
-    """
-
-    name = Attribute("Portlet internal name")
-
-    label = Attribute("Portlet visible name")
-
-    permission = PermissionField(title="Portlet permission",
-                                 description="Permission required to display portlet",
-                                 required=False)
-
-    toolbar_image = Attribute("Portlet toolbar image")
-
-    toolbar_css_class = Attribute("Portlet toolbar CSS class")
-
-    settings_factory = Attribute("Portlet settings factory")
-
-
-class IPortletCSSClass(Interface):
-    """Portlet CSS class interface"""
-
-
-class IPortletAddingInfo(Interface):
-    """Portlet adding info interface"""
-
-    portlet_name = Choice(title=_("Portlet"),
-                          vocabulary="PyAMS portal portlets")
-
-    slot_name = Choice(title=_("Slot name"),
-                       description=_("Slot name to which this configuration applies"),
-                       required=True,
-                       vocabulary='PyAMS template slots')
-
-
-class IPortletSettings(ILocation, IAttributeAnnotatable):
-    """Portlet settings interface
-
-    Portlet settings is parented to it's configuration
-    """
-
-    configuration = Attribute("Settings parent configuration")
-
-    renderer = Choice(title=_("Portlet renderer"),
-                      description=_("Name of renderer used to render this portlet"),
-                      vocabulary='PyAMS portlet renderers',
-                      required=False,
-                      default='')
-
-    def get_renderer(self, request=None):
-        """Get renderer utility"""
-
-
-PORTLETS_CONFIGURATION_KEY = 'pyams_portal.portlets'
-
-
-class IPortletConfiguration(ILocation):
-    """Portlet common configuration interface
-
-    This is generic configuration settings common to all portlets.
-    Portlet configuration is parented to:
-     - it's template if parent is the template
-     - it's context if parent is a portal context
-    """
-
-    portlet_id = Int(title="Portlet ID",
-                     required=True)
-
-    portlet_name = Attribute("Portlet name")
-
-    def get_portlet(self):
-        """Return portlet utility matching current portlet name"""
-
-    can_inherit = Attribute("Can inherit parent configuration?")
-
-    parent = Attribute("Portlet configuration parent")
-
-    inherit_parent = Bool(title=_("Inherit parent configuration?"),
-                          description=_("This option is only available if context's parent is using the same "
-                                        "template..."),
-                          required=True,
-                          default=True)
-
-    settings = Object(title="Portlet local settings",
-                      schema=IPortletSettings,
-                      readonly=True)
-
-    editor_settings = Attribute("Editor settings")
-
-    def get_settings(self, allow_inherit=True):
-        """Get configuration settings, with or without inheritance"""
-
-
-class IPortletContentProvider(IContentProvider):
-    """Portlet content provider"""
-
-    portlet = Object(title="Portlet utility",
-                     schema=IPortlet)
-
-    configuration = Object(title="Portlet renderer configuration",
-                           schema=IPortletConfiguration)
-
-
-class IPortletPreviewer(IPortletContentProvider):
-    """Portlet previewer interface
-
-    A portlet previewer should be defined as an adapter for a context,
-    a request, a view and a portlet
-    """
-
-
-class IPortletRenderer(IPortletContentProvider):
-    """Portlet renderer interface
-
-    A portlet renderer should be defined as an adapter for a context,
-    a request, a view and a portlet
-    """
-
-    label = Attribute("Renderer name")
-
-    settings_interface = Attribute("Settings interface defined for this renderer")
-    settings_key = Attribute("Annotations key used to store renderer settings")
-
-    target_interface = Attribute("Target interface provided by this renderer")
-
-    use_portlets_cache = Attribute("Can renderer use rendering cache?")
-    use_authentication = Attribute("If 'True', portlet cache entry key is based on current authentication")
-
-    resources = Attribute("Tuple of Fanstatic resources needed by this renderer")
-
-
-PORTLET_RENDERER_SETTINGS_KEY = 'pyams_portal.renderer.settings::{0}'
-
-
-class IPortletRendererSettings(Interface):
-    """Portlet renderer settings interface"""
-
-
-class IPortalPortletsConfiguration(IContained):
-    """Portal template portlet configuration interface"""
-
-    def get_portlet_configuration(self, portlet_id):
-        """Get portlet configuration for given slot"""
-
-    def set_portlet_configuration(self, portlet_id, config):
-        """Set portlet configuration"""
-
-    def delete_portlet_configuration(self, portlet_id):
-        """Delete portlet configuration"""
-
-
-#
-# Slot interfaces
-#
-
-PORTAL_SLOTS_KEY = 'pyams_portal.slots'
-
-
-class ISlot(Interface):
-    """Portal template slot interface"""
-
-    name = TextLine(title=_("Slot name"),
-                    description=_("This name must be unique in a given template"),
-                    required=True)
-
-    row_id = Int(title=_("Row ID"),
-                 required=False)
-
-
-class ISlotConfiguration(Interface):
-    """Portal slot configuration"""
-
-    template = Attribute("Slot template")
-
-    slot_name = TextLine(title="Slot name")
-
-    portlet_ids = PersistentList(title="Portlet IDs",
-                                 value_type=Int())
-
-    visible = Bool(title=_("Visible slot?"),
-                   description=_("Select 'no' to hide this slot..."),
-                   required=True,
-                   default=True)
-
-    xs_width = Int(title=_("Extra small device width"),
-                   description=_("Slot width, in columns count, on extra small devices (phones...); "
-                                 "set to 0 to hide the portlet"),
-                   required=False,
-                   min=0,
-                   max=12)
-
-    sm_width = Int(title=_("Small device width"),
-                   description=_("Slot width, in columns count, on small devices (tablets...); "
-                                 "set to 0 to hide the portlet"),
-                   required=False,
-                   min=0,
-                   max=12)
-
-    md_width = Int(title=_("Medium devices width"),
-                   description=_("Slot width, in columns count, on medium desktop devices (>= 992 pixels); "
-                                 "set to 0 to hide the portlet"),
-                   required=False,
-                   min=0,
-                   max=12)
-
-    lg_width = Int(title=_("Large devices width"),
-                   description=_("Slot width, in columns count, on large desktop devices (>= 1200 pixels); "
-                                 "set to 0 to hide the portlet"),
-                   required=False,
-                   min=0,
-                   max=12)
-
-    css_class = TextLine(title=_("CSS class"),
-                         description=_("CSS class applied to this slot"),
-                         required=False)
-
-    def get_css_class(self, device=None):
-        """Get current CSS class"""
-
-    def get_width(self, device=None):
-        """Get slot width for each or given device"""
-
-    def set_width(self, width, device=None):
-        """Set width in columns count for given device"""
-
-
-class ISlotRenderer(IContentProvider):
-    """Slot renderer"""
-
-
-#
-# Template configuration interfaces
-#
-
-class IPortalTemplateConfiguration(IContained):
-    """Portal template configuration interface"""
-
-    # Rows configuration
-    rows = Int(title="Rows count",
-               required=True,
-               default=1,
-               min=0)
-
-    def add_row(self):
-        """Add new row"""
-
-    def set_row_order(self, order):
-        """Change template rows order"""
-
-    def delete_row(self, row_id):
-        """Delete template row"""
-
-    # Slots configuration
-    slot_names = PersistentList(title="Slot names",
-                                value_type=TextLine())
-
-    slot_order = PersistentDict(title="Solts order",
-                                key_type=Int(),  # row index
-                                value_type=PersistentList(value_type=TextLine()))  # slot name
-
-    slot_config = PersistentDict(title="Slots configuration",
-                                 key_type=TextLine(),  # slot name
-                                 value_type=Object(schema=ISlotConfiguration),
-                                 required=False)
-
-    def add_slot(self, slot_name, row_id=None):
-        """Add slot with given name"""
-
-    def set_slot_order(self, order):
-        """Change template slots order"""
-
-    def get_slot_row(self, slot_name):
-        """Get row containing given slot"""
-
-    def get_slots(self, row_id):
-        """Get ordered list of slots for given row ID"""
-
-    def get_slots_width(self, device=None):
-        """Get slots width for given or all device(s)"""
-
-    def set_slot_width(self, slot_name, device, width):
-        """Set slot width for given device"""
-
-    def get_slot_configuration(self, slot_name):
-        """Get slot configuration for given slot"""
-
-    def delete_slot(self, slot_name):
-        """Delete template slot"""
-
-    # Portlets configuration
-    def add_portlet(self, portlet_name, slot_name):
-        """Add portlet to givben slot"""
-
-    def get_portlet_slot(self, portlet_id):
-        """Get row ID and slot for given portlet"""
-
-    def set_portlet_order(self, slot_name, order):
-        """Set template portlets order"""
-
-    def delete_portlet(self, slot_name, position):
-        """Delete template portlet"""
-
-
-#
-# Portal templates interfaces
-#
-
-LOCAL_TEMPLATE_NAME = '__local__'
-
-TEMPLATE_CONFIGURATION_KEY = 'pyams_portal.template'
-
-TEMPLATE_CONTAINER_CONFIGURATION_KEY = 'pyams_portal.container.configuration'
-
-PORTAL_PAGE_KEY = 'pyams_portal.page'
-
-
-class ILocalTemplateHandler(IAttributeAnnotatable):
-    """Base interface for local template handler"""
-
-
-class IPortalTemplate(ILocalTemplateHandler):
-    """Portal template interface
-
-    A portal template is a named utility providing a name and a set of slots containing portlets
-    """
-
-    name = TextLine(title=_("Template name"),
-                    description=_("Two registered templates can't share the same name..."),
-                    required=True)
-
-
-class IPortalTemplateContainer(IContainer, IAttributeAnnotatable):
-    """Portal template container interface"""
-
-    contains(IPortalTemplate)
-
-    last_portlet_id = Int(title="Last portlet ID",
-                          required=True,
-                          default=1,
-                          min=0)
-
-    def get_portlet_id(self):
-        """Get new portlet ID"""
-
-
-class IPortalTemplateContainerConfiguration(Interface):
-    """Portal templates container configuration"""
-
-    toolbar_portlets = List(title=_("Toolbar portlets"),
-                            description=_("These portlets will be directly available in templates configuration "
-                                          "page toolbar"),
-                            value_type=Choice(vocabulary="PyAMS portal portlets"),
-                            required=False)
-
-
-class IPortalTemplateRenderer(IContentProvider):
-    """Portal template renderer
-
-    A portal template renderer should be implemented as an adapter for a context, a request
-    and a template
-    """
-
-
-PREVIEW_MODE = 'PREVIEW_MODE'
-
-
-class IPortalPage(Interface):
-    """Portal page interface
-
-    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.
-    """
-
-    can_inherit = Attribute("Can inherit parent template?")
-
-    parent = Attribute("Parent from which to inherit, the real parent or the source template")
-
-    inherit_parent = Bool(title=_("Inherit parent template?"),
-                          description=_("Should we reuse parent template?"),
-                          required=True,
-                          default=True)
-
-    override_parent = Bool(title=_("Override parent template?"),
-                           description=_("Should we override parent template?"),
-                           required=True,
-                           default=False)
-
-    use_local_template = Bool(title=_("Use local template?"),
-                              description=_("If 'yes', you can define a custom local template instead of "
-                                            "a shared template"),
-                              required=True,
-                              default=False)
-
-    local_template = Object(title=_("Local template"),
-                            schema=IPortalTemplate,
-                            required=False,
-                            readonly=True)
-
-    use_shared_template = Bool(title=_("Use shared template?"),
-                               description=_("If 'yes', you can select a shared template"),
-                               required=True,
-                               default=True)
-
-    shared_template = Choice(title=_("Page template"),
-                             description=_("Template used for this page"),
-                             vocabulary='PyAMS portal templates',
-                             required=False)
-
-    template = Attribute("Used template")
-
-
-class IPortalContext(IAttributeAnnotatable):
-    """Portal context marker interface"""
--- a/src/pyams_portal/page.py	Thu Jan 03 17:50:58 2019 +0100
+++ b/src/pyams_portal/page.py	Thu Jan 17 10:45:18 2019 +0100
@@ -12,31 +12,27 @@
 
 __docformat__ = 'restructuredtext'
 
-
-# import standard library
-
-# import packages
 from persistent import Persistent
 from pyramid.threadlocal import get_current_registry
 from zope.container.contained import Contained
 from zope.copy import clone
-from zope.interface import alsoProvides, implementer, noLongerProvides
+from zope.interface import alsoProvides, noLongerProvides
 from zope.lifecycleevent import ObjectCreatedEvent
 from zope.location import locate
 from zope.schema.fieldproperty import FieldProperty
 from zope.traversing.interfaces import ITraversable
 
-# import interfaces
 from pyams_portal.interfaces import ILocalTemplateHandler, IPortalContext, IPortalPage, IPortalPortletsConfiguration, \
     IPortalTemplate, IPortalTemplateConfiguration, LOCAL_TEMPLATE_NAME, PORTAL_PAGE_KEY, PORTLETS_CONFIGURATION_KEY
 from pyams_portal.portlet import PortalPortletsConfiguration
 from pyams_portal.template import PortalTemplate
 from pyams_utils.adapter import ContextAdapter, adapter_config, get_annotation_adapter
+from pyams_utils.factory import factory_config
 from pyams_utils.registry import query_utility
 from pyams_utils.zodb import volatile_property
 
 
-@implementer(IPortalPage)
+@factory_config(IPortalPage)
 class PortalPage(Persistent, Contained):
     """Portal page persistent class
 
@@ -138,7 +134,7 @@
 @adapter_config(context=IPortalContext, provides=IPortalPage)
 def portal_context_page_adapter(context):
     """Portal context page factory"""
-    return get_annotation_adapter(context, PORTAL_PAGE_KEY, PortalPage)
+    return get_annotation_adapter(context, PORTAL_PAGE_KEY, IPortalPage)
 
 
 @adapter_config(name='template', context=IPortalContext, provides=ITraversable)
--- a/src/pyams_portal/portlet.py	Thu Jan 03 17:50:58 2019 +0100
+++ b/src/pyams_portal/portlet.py	Thu Jan 17 10:45:18 2019 +0100
@@ -36,7 +36,7 @@
     IPortalTemplateConfiguration, IPortlet, IPortletConfiguration, IPortletPreviewer, IPortletRenderer, \
     IPortletRendererSettings, IPortletSettings, MANAGE_TEMPLATE_PERMISSION, PORTLET_RENDERER_SETTINGS_KEY, PREVIEW_MODE
 from pyams_utils.adapter import ContextAdapter, adapter_config, get_adapter_weight, get_annotation_adapter
-from pyams_utils.factory import get_object_factory, is_interface
+from pyams_utils.factory import get_object_factory, is_interface, factory_config
 from pyams_utils.interfaces import ICacheKeyValue
 from pyams_utils.interfaces.url import DISPLAY_CONTEXT
 from pyams_utils.registry import get_global_registry
@@ -445,7 +445,7 @@
 # Template portlets configuration
 #
 
-@implementer(IPortalPortletsConfiguration)
+@factory_config(IPortalPortletsConfiguration)
 class PortalPortletsConfiguration(PersistentMapping, Contained):
     """Portal portlets configuration"""
 
--- a/src/pyams_portal/template.py	Thu Jan 03 17:50:58 2019 +0100
+++ b/src/pyams_portal/template.py	Thu Jan 17 10:45:18 2019 +0100
@@ -27,17 +27,18 @@
 from zope.schema.vocabulary import SimpleTerm, SimpleVocabulary
 from zope.traversing.interfaces import ITraversable
 
-from pyams_portal import _
 from pyams_portal.interfaces import IPortalPortletsConfiguration, IPortalTemplate, IPortalTemplateConfiguration, \
     IPortalTemplateContainer, IPortalTemplateContainerConfiguration, IPortlet, IPortletConfiguration, \
     PORTLETS_CONFIGURATION_KEY, TEMPLATE_CONFIGURATION_KEY, TEMPLATE_CONTAINER_CONFIGURATION_KEY
-from pyams_portal.portlet import PortalPortletsConfiguration
 from pyams_portal.slot import SlotConfiguration
 from pyams_utils.adapter import ContextAdapter, adapter_config, get_annotation_adapter
+from pyams_utils.factory import factory_config
 from pyams_utils.registry import get_local_registry, get_utility
 from pyams_utils.request import check_request
 from pyams_utils.vocabulary import vocabulary_config
 
+from pyams_portal import _
+
 
 #
 # Portal templates container
@@ -54,7 +55,7 @@
         return self.last_portlet_id
 
 
-@implementer(IPortalTemplateContainerConfiguration)
+@factory_config(IPortalTemplateContainerConfiguration)
 class PortalTemplateContainerConfiguration(Persistent, Contained):
     """Portal template container configuration"""
 
@@ -64,7 +65,7 @@
 @adapter_config(context=IPortalTemplateContainer, provides=IPortalTemplateContainerConfiguration)
 def portal_template_container_configuration_adapter(context):
     """Portal template container configuration factory"""
-    return get_annotation_adapter(context, TEMPLATE_CONTAINER_CONFIGURATION_KEY, PortalTemplateContainerConfiguration)
+    return get_annotation_adapter(context, TEMPLATE_CONTAINER_CONFIGURATION_KEY, IPortalTemplateContainerConfiguration)
 
 
 #
@@ -108,7 +109,7 @@
 # Portal template configuration
 #
 
-@implementer(IPortalTemplateConfiguration)
+@factory_config(IPortalTemplateConfiguration)
 class PortalTemplateConfiguration(Persistent, Contained):
     """Portal template configuration"""
 
@@ -298,6 +299,12 @@
             IPortalPortletsConfiguration(self.__parent__).delete_portlet_configuration(portlet_id)
 
 
+@adapter_config(context=IPortalTemplate, provides=IPortalTemplateConfiguration)
+def portal_template_configuration_factory(context):
+    """Portal template configuration adapter"""
+    return get_annotation_adapter(context, TEMPLATE_CONFIGURATION_KEY, IPortalTemplateConfiguration)
+
+
 @vocabulary_config(name='PyAMS template slots')
 class PortalTemplateSlotsVocabulary(SimpleVocabulary):
     """Portal template slots vocabulary"""
@@ -308,12 +315,6 @@
         super(PortalTemplateSlotsVocabulary, self).__init__(terms)
 
 
-@adapter_config(context=IPortalTemplate, provides=IPortalTemplateConfiguration)
-def portal_template_configuration_factory(context):
-    """Portal template configuration adapter"""
-    return get_annotation_adapter(context, TEMPLATE_CONFIGURATION_KEY, PortalTemplateConfiguration)
-
-
 @adapter_config(name='portlet', context=IPortalTemplate, provides=ITraversable)
 class PortalTemplatePortletTraverser(ContextAdapter):
     """++portlet++ template traverser"""
@@ -333,4 +334,4 @@
 @adapter_config(context=IPortalTemplate, provides=IPortalPortletsConfiguration)
 def portal_template_portlets_configuration_adapter(template):
     """Portal template portlets configuration adapter"""
-    return get_annotation_adapter(template, PORTLETS_CONFIGURATION_KEY, PortalPortletsConfiguration)
+    return get_annotation_adapter(template, PORTLETS_CONFIGURATION_KEY, IPortalPortletsConfiguration)