Added default factory adapters for shared tools and shared contents
authorThierry Florac <thierry.florac@onf.fr>
Thu, 22 Mar 2018 14:52:22 +0100
changeset 492 04503227569d
parent 491 828f9c9303cf
child 493 5d94baef6923
Added default factory adapters for shared tools and shared contents
src/pyams_content/shared/blog/__init__.py
src/pyams_content/shared/blog/interfaces/__init__.py
src/pyams_content/shared/blog/manager.py
src/pyams_content/shared/blog/zmi/__init__.py
src/pyams_content/shared/blog/zmi/manager.py
src/pyams_content/shared/common/__init__.py
src/pyams_content/shared/common/interfaces/__init__.py
src/pyams_content/shared/common/manager.py
src/pyams_content/shared/common/review.py
src/pyams_content/shared/form/__init__.py
src/pyams_content/shared/form/interfaces/__init__.py
src/pyams_content/shared/form/manager.py
src/pyams_content/shared/imagemap/__init__.py
src/pyams_content/shared/imagemap/interfaces/__init__.py
src/pyams_content/shared/imagemap/manager.py
src/pyams_content/shared/logo/__init__.py
src/pyams_content/shared/logo/interfaces/__init__.py
src/pyams_content/shared/logo/manager.py
src/pyams_content/shared/news/__init__.py
src/pyams_content/shared/news/interfaces/__init__.py
src/pyams_content/shared/news/manager.py
src/pyams_content/shared/site/__init__.py
src/pyams_content/shared/site/container.py
src/pyams_content/shared/site/interfaces/__init__.py
src/pyams_content/shared/site/manager.py
src/pyams_content/shared/site/zmi/__init__.py
src/pyams_content/shared/site/zmi/folder.py
src/pyams_content/shared/site/zmi/manager.py
src/pyams_content/shared/view/__init__.py
src/pyams_content/shared/view/interfaces/__init__.py
src/pyams_content/shared/view/manager.py
--- a/src/pyams_content/shared/blog/__init__.py	Thu Mar 22 14:50:32 2018 +0100
+++ b/src/pyams_content/shared/blog/__init__.py	Thu Mar 22 14:52:22 2018 +0100
@@ -21,12 +21,14 @@
 from pyams_content.component.theme.interfaces import IThemesTarget
 from pyams_content.features.preview.interfaces import IPreviewTarget
 from pyams_content.features.review.interfaces import IReviewTarget
-from pyams_content.shared.blog.interfaces import IWfBlogPost, BLOG_CONTENT_TYPE, BLOG_CONTENT_NAME, IBlogPost
+from pyams_content.shared.blog.interfaces import IWfBlogPost, BLOG_CONTENT_TYPE, BLOG_CONTENT_NAME, IBlogPost, \
+    IWfBlogPostFactory
 from pyams_workflow.interfaces import IWorkflow, IWorkflowVersions, IWorkflowState
 
 # import packages
-from pyams_content.shared.common import WfSharedContent, register_content_type, SharedContent
-from zope.interface import implementer
+from pyams_content.shared.common import WfSharedContent, register_content_type, SharedContent, IWfSharedContentFactory
+from pyams_utils.adapter import adapter_config
+from zope.interface import implementer, provider
 
 
 @implementer(IWfBlogPost, IParagraphContainerTarget, IThemesTarget,
@@ -40,15 +42,19 @@
 register_content_type(WfBlogPost)
 
 
+@provider(IWfBlogPostFactory)
 @implementer(IBlogPost)
 class BlogPost(SharedContent):
     """Worfklow managed blog post class"""
 
-    content_class = WfBlogPost
-
     def is_deletable(self):
         workflow = IWorkflow(self)
         for version in IWorkflowVersions(self).get_versions():
             if IWorkflowState(version).state != workflow.initial_state:
                 return False
         return True
+
+
+@adapter_config(context=IWfBlogPostFactory, provides=IWfSharedContentFactory)
+def BlogPostContentFactory(context):
+    return WfBlogPost
--- a/src/pyams_content/shared/blog/interfaces/__init__.py	Thu Mar 22 14:50:32 2018 +0100
+++ b/src/pyams_content/shared/blog/interfaces/__init__.py	Thu Mar 22 14:52:22 2018 +0100
@@ -16,7 +16,7 @@
 # import standard library
 
 # import interfaces
-from pyams_content.shared.common.interfaces import ISharedSite, ISharedTool, IWfSharedContent, ISharedContent, \
+from pyams_content.shared.common.interfaces import ISharedSite, IBaseSharedTool, IWfSharedContent, ISharedContent, \
     IDeletableElement
 from pyams_sequence.interfaces import ISequentialIdTarget
 from pyams_workflow.interfaces import IWorkflowPublicationSupport
@@ -24,7 +24,7 @@
 
 # import packages
 from zope.container.constraints import containers, contains
-from zope.interface import Attribute
+from zope.interface import Attribute, Interface
 
 from pyams_content import _
 
@@ -37,6 +37,10 @@
     """Blog topic interface"""
 
 
+class IWfBlogPostFactory(Interface):
+    """Blog post factory interface"""
+
+
 class IBlogPost(ISharedContent, IDeletableElement):
     """Workflow managed blog post interface"""
 
@@ -50,9 +54,20 @@
     contains('.IBlogFolder', '.IBlogPost')
 
 
-class IBlogManager(ISharedSite, ISharedTool, IWorkflowPublicationSupport, IDeletableElement, ISequentialIdTarget):
+class IBlogFolderFactory(Interface):
+    """Blog folder factory interface"""
+
+
+class IBlogManager(ISharedSite, IBaseSharedTool, IWorkflowPublicationSupport, IDeletableElement, ISequentialIdTarget):
     """Blog manager interface"""
 
     contains(IBlogFolder)
 
     folder_factory = Attribute("Blog folder factory")
+
+    topic_content_type = Attribute("Topic content type")
+    topic_content_factory = Attribute("Topic content factory")
+
+
+class IBlogManagerFactory(Interface):
+    """Blog manager factory interface"""
--- a/src/pyams_content/shared/blog/manager.py	Thu Mar 22 14:50:32 2018 +0100
+++ b/src/pyams_content/shared/blog/manager.py	Thu Mar 22 14:52:22 2018 +0100
@@ -21,7 +21,9 @@
 from pyams_content.component.theme.interfaces import IThemesManagerTarget
 from pyams_content.features.preview.interfaces import IPreviewTarget
 from pyams_content.reference.pictograms.interfaces import IPictogramManagerTarget
-from pyams_content.shared.blog.interfaces import IBlogManager, BLOG_CONTENT_TYPE, IBlogFolder
+from pyams_content.root.interfaces import ISiteRoot
+from pyams_content.shared.blog.interfaces import IBlogManager, IBlogFolder, IBlogFolderFactory, IBlogManagerFactory
+from pyams_content.shared.common.interfaces import ISharedContentFactory
 from pyams_portal.interfaces import IPortalContext
 from zope.annotation.interfaces import IAttributeAnnotatable
 from zope.component.interfaces import ISite
@@ -29,8 +31,9 @@
 
 # import packages
 from pyams_content.shared.blog import BlogPost
-from pyams_content.shared.common.manager import SharedTool
+from pyams_content.shared.common.manager import BaseSharedTool
 from pyams_skin.skin import UserSkinnableContent
+from pyams_utils.adapter import adapter_config
 from pyams_utils.traversing import get_parent
 from pyramid.events import subscriber
 from zope.container.folder import Folder
@@ -51,20 +54,29 @@
 
 @implementer(IBlogManager, IParagraphFactorySettings, IThemesManagerTarget, IPictogramManagerTarget,
              IIllustrationTarget, IPortalContext, IPreviewTarget, IAttributeAnnotatable)
-class BlogManager(SharedTool, UserSkinnableContent):
+class BlogManager(Folder, BaseSharedTool, UserSkinnableContent):
     """Nlog manager class"""
 
-    folder_factory = BlogFolder
-
-    shared_content_type = BLOG_CONTENT_TYPE
-    shared_content_factory = BlogPost
-
     allowed_paragraphs = FieldProperty(IParagraphFactorySettings['allowed_paragraphs'])
     auto_created_paragraphs = FieldProperty(IParagraphFactorySettings['auto_created_paragraphs'])
 
     sequence_name = ''  # use default sequence generator
     sequence_prefix = ''
 
+    @property
+    def folder_factory(self):
+        return IBlogFolderFactory(self, BlogFolder)
+
+    @property
+    def topic_content_factory(self):
+        return ISharedContentFactory(self, None)
+
+    @property
+    def topic_content_type(self):
+        factory = self.topic_content_factory
+        if factory is not None:
+            return factory.content_class.content_type
+
     def is_deletable(self):
         for element in self.values():
             if not element.is_deletable():
@@ -72,6 +84,21 @@
         return True
 
 
+@adapter_config(context=ISiteRoot, provides=IBlogManagerFactory)
+def SiteRootBlogManagerFactory(context):
+    return BlogManager
+
+
+@adapter_config(context=IBlogManager, provides=IBlogFolderFactory)
+def BlogManagerFolderFactory(context):
+    return BlogFolder
+
+
+@adapter_config(context=IBlogManager, provides=ISharedContentFactory)
+def BlogManagerTopicFactory(context):
+    return BlogPost
+
+
 @subscriber(IObjectAddedEvent, context_selector=IBlogManager)
 def handle_added_blog_manager(event):
     """Register blog manager when added"""
--- a/src/pyams_content/shared/blog/zmi/__init__.py	Thu Mar 22 14:50:32 2018 +0100
+++ b/src/pyams_content/shared/blog/zmi/__init__.py	Thu Mar 22 14:52:22 2018 +0100
@@ -80,7 +80,7 @@
 
     def add(self, wf_content):
         # create shared content
-        content = self.context.shared_content_factory()
+        content = self.context.topic_content_factory()
         self.request.registry.notify(ObjectCreatedEvent(content))
         # check blog folders
         now = datetime.utcnow()
--- a/src/pyams_content/shared/blog/zmi/manager.py	Thu Mar 22 14:50:32 2018 +0100
+++ b/src/pyams_content/shared/blog/zmi/manager.py	Thu Mar 22 14:52:22 2018 +0100
@@ -16,11 +16,11 @@
 # import standard library
 from pyams_content.interfaces import MANAGE_SITE_ROOT_PERMISSION, MANAGE_SITE_PERMISSION
 from pyams_content.root.interfaces import ISiteRoot
-from pyams_content.shared.blog.interfaces import IBlogManager
+from pyams_content.shared.blog.interfaces import IBlogManager, IBlogManagerFactory
 from pyams_content.skin.zmi.interfaces import ISiteTreeTable, IUserAddingsMenuLabel
 from pyams_i18n.interfaces import II18n, INegotiator
 from pyams_skin.interfaces.container import ITableElementEditor
-from pyams_skin.interfaces.viewlet import IToolbarAddingMenu
+from pyams_skin.interfaces.viewlet import IToolbarAddingMenu, ITableItemColumnActionsMenu
 from pyams_skin.layer import IPyAMSLayer
 from pyams_workflow.interfaces import IWorkflowPublicationInfo
 from pyams_zmi.interfaces.menu import IPropertiesMenu
@@ -30,6 +30,7 @@
 from z3c.form.interfaces import IDataExtractedEvent
 
 # import packages
+from pyams_content.shared.blog.manager import BlogManager
 from pyams_form.form import AJAXAddForm, AJAXEditForm
 from pyams_pagelet.pagelet import pagelet_config
 from pyams_skin.table import DefaultElementEditorAdapter
@@ -91,9 +92,10 @@
 
     def create(self, data):
         factory = self.request.registry.settings.get('pyams_content.config.blog_factory')
-        if factory is None:
-            factory = 'pyams_content.shared.blog.manager.BlogManager'
-        factory = DottedNameResolver().resolve(factory)
+        if factory:
+            factory = DottedNameResolver().resolve(factory)
+        else:
+            factory = IBlogManagerFactory(self.context, BlogManager)
         return factory()
 
     def add(self, object):
@@ -141,8 +143,20 @@
 # Blog manager publication views
 #
 
+@viewlet_config(name='workflow-publication.menu', context=IBlogManager, layer=IPyAMSLayer, view=ISiteTreeTable,
+                manager=ITableItemColumnActionsMenu, permission=MANAGE_SITE_PERMISSION, weight=210)
+class BlogManagerTableItemWorkflowPublicationMenu(ToolbarMenuItem):
+    """Blog manager tree item workflow publication menu"""
+
+    label = _("Publication dates...")
+    label_css_class = 'fa fa-fw fa-eye'
+    url = 'workflow-publication.html'
+    modal_target = True
+    stop_propagation = True
+
+
 @viewlet_config(name='workflow-publication.menu', context=IBlogManager, layer=IAdminLayer, manager=IPropertiesMenu,
-                permission=MANAGE_SITE_PERMISSION, weight=2)
+                permission=MANAGE_SITE_PERMISSION, weight=210)
 class BlogManagerWorkflowPublicationMenu(MenuItem):
     """Blog manager workflow publication menu"""
 
--- a/src/pyams_content/shared/common/__init__.py	Thu Mar 22 14:50:32 2018 +0100
+++ b/src/pyams_content/shared/common/__init__.py	Thu Mar 22 14:52:22 2018 +0100
@@ -21,7 +21,7 @@
 from pyams_content.features.checker.interfaces import IContentChecker, MISSING_VALUE, MISSING_LANG_VALUE, ERROR_VALUE
 from pyams_content.features.review.interfaces import IReviewComments
 from pyams_content.shared.common.interfaces import IWfSharedContent, IWfSharedContentRoles, ISharedContent, \
-    IBaseSharedTool, ISharedSite
+    IBaseSharedTool, ISharedSite, IWfSharedContentFactory
 from pyams_i18n.interfaces import II18nManager, II18n
 from pyams_security.interfaces import IDefaultProtectionPolicy
 from pyams_sequence.interfaces import ISequentialIdTarget, ISequentialIdInfo
@@ -41,6 +41,7 @@
 from pyams_security.utility import get_principal
 from pyams_utils.adapter import adapter_config, ContextAdapter
 from pyams_utils.date import format_datetime
+from pyams_utils.property import classproperty, classproperty_support
 from pyams_utils.registry import query_utility, get_utilities_for
 from pyams_utils.request import query_request, check_request
 from pyams_utils.timezone import tztime
@@ -266,6 +267,7 @@
 # Main shared content class and adapters
 #
 
+@classproperty_support
 @implementer(ISharedContent, ISequentialIdTarget)
 class SharedContent(Persistent, Contained):
     """Workflow managed shared data"""
@@ -275,6 +277,10 @@
     sequence_name = ''  # use default sequence generator
     sequence_prefix = ''
 
+    @classproperty
+    def content_class(cls):
+        return IWfSharedContentFactory(cls, None)
+
     @property
     def workflow_name(self):
         return get_parent(self, IBaseSharedTool).shared_content_workflow
--- a/src/pyams_content/shared/common/interfaces/__init__.py	Thu Mar 22 14:50:32 2018 +0100
+++ b/src/pyams_content/shared/common/interfaces/__init__.py	Thu Mar 22 14:52:22 2018 +0100
@@ -148,6 +148,10 @@
                    required=False)
 
 
+class IWfSharedContentFactory(Interface):
+    """Shared content factory interface"""
+
+
 class IWfSharedContentRoles(Interface):
     """Shared content roles"""
 
@@ -186,6 +190,10 @@
     """Workflow managed shared content interface"""
 
 
+class ISharedContentFactory(Interface):
+    """Workflow managed shared content factory interface"""
+
+
 #
 # Shared tool manager security restrictions
 #
--- a/src/pyams_content/shared/common/manager.py	Thu Mar 22 14:50:32 2018 +0100
+++ b/src/pyams_content/shared/common/manager.py	Thu Mar 22 14:52:22 2018 +0100
@@ -17,7 +17,8 @@
 
 # import interfaces
 from pyams_content.interfaces import WEBMASTER_ROLE, PILOT_ROLE, MANAGER_ROLE, CONTRIBUTOR_ROLE
-from pyams_content.shared.common.interfaces import ISharedToolContainer, IBaseSharedTool, ISharedTool, ISharedToolRoles
+from pyams_content.shared.common.interfaces import ISharedToolContainer, IBaseSharedTool, ISharedTool, ISharedToolRoles, \
+    ISharedContentFactory
 from pyams_security.interfaces import IDefaultProtectionPolicy
 from pyams_workflow.interfaces import IWorkflow
 from zope.annotation.interfaces import IAttributeAnnotatable
@@ -64,8 +65,15 @@
 class SharedTool(Folder, BaseSharedTool):
     """Shared tool class"""
 
-    shared_content_type = None
-    shared_content_factory = None
+    @property
+    def shared_content_factory(self):
+        return ISharedContentFactory(self, None)
+
+    @property
+    def shared_content_type(self):
+        factory = self.shared_content_factory
+        if factory is not None:
+            return factory.content_class.content_type
 
 
 @adapter_config(context=IBaseSharedTool, provides=IWorkflow)
--- a/src/pyams_content/shared/common/review.py	Thu Mar 22 14:50:32 2018 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,23 +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'
-
-
-# import standard library
-
-# import interfaces
-
-# import packages
-
-# imports for backward compatibility: module moved to pyams_content.features.review !!
-from pyams_content.features.review import ReviewComment, ReviewCommentsContainer
--- a/src/pyams_content/shared/form/__init__.py	Thu Mar 22 14:50:32 2018 +0100
+++ b/src/pyams_content/shared/form/__init__.py	Thu Mar 22 14:52:22 2018 +0100
@@ -20,13 +20,14 @@
 from pyams_content.features.preview.interfaces import IPreviewTarget
 from pyams_content.features.review.interfaces import IReviewTarget
 from pyams_content.shared.form.interfaces import IWfForm, IForm, FORM_CONTENT_TYPE, FORM_CONTENT_NAME, \
-    IFormFieldContainerTarget, IFormHandler, IFormFieldContainer
+    IFormFieldContainerTarget, IFormHandler, IFormFieldContainer, IWfFormFactory
 
 # import packages
-from pyams_content.shared.common import WfSharedContent, SharedContent, register_content_type, WfSharedContentChecker
+from pyams_content.shared.common import WfSharedContent, SharedContent, register_content_type, WfSharedContentChecker, \
+    IWfSharedContentFactory
 from pyams_utils.adapter import adapter_config
 from pyams_utils.registry import get_global_registry
-from zope.interface import implementer, alsoProvides, noLongerProvides
+from zope.interface import implementer, provider, alsoProvides, noLongerProvides
 from zope.schema.fieldproperty import FieldProperty
 
 from pyams_content import _
@@ -76,11 +77,15 @@
 register_content_type(WfForm)
 
 
+@provider(IWfFormFactory)
 @implementer(IForm)
 class Form(SharedContent):
     """Workflow managed form class"""
 
-    content_class = WfForm
+
+@adapter_config(context=IWfFormFactory, provides=IWfSharedContentFactory)
+def FormContentFactory(context):
+    return WfForm
 
 
 @adapter_config(name='properties', context=IWfForm, provides=IContentChecker)
--- a/src/pyams_content/shared/form/interfaces/__init__.py	Thu Mar 22 14:50:32 2018 +0100
+++ b/src/pyams_content/shared/form/interfaces/__init__.py	Thu Mar 22 14:52:22 2018 +0100
@@ -37,7 +37,11 @@
 
 
 class IFormsManager(ISharedTool):
-    """Formq manager interface"""
+    """Forms manager interface"""
+
+
+class IFormsManagerFactory(Interface):
+    """Forms manager factory interface"""
 
 
 class IFormField(IContained):
@@ -142,6 +146,10 @@
         """Get form handler utility"""
 
 
+class IWfFormFactory(Interface):
+    """Form factory interface"""
+
+
 class IForm(ISharedContent):
     """Workflow managed form interface"""
 
--- a/src/pyams_content/shared/form/manager.py	Thu Mar 22 14:50:32 2018 +0100
+++ b/src/pyams_content/shared/form/manager.py	Thu Mar 22 14:52:22 2018 +0100
@@ -16,7 +16,8 @@
 # import standard library
 
 # import interfaces
-from pyams_content.shared.form.interfaces import IFormsManager, FORM_CONTENT_TYPE
+from pyams_content.shared.common.interfaces import ISharedContentFactory
+from pyams_content.shared.form.interfaces import IFormsManager, FORM_CONTENT_TYPE, IFormsManagerFactory
 from zope.annotation.interfaces import IAttributeAnnotatable
 from zope.component.interfaces import ISite
 from zope.lifecycleevent.interfaces import IObjectAddedEvent
@@ -24,6 +25,8 @@
 # import packages
 from pyams_content.shared.common.manager import SharedTool
 from pyams_content.shared.form import Form
+from pyams_utils.adapter import adapter_config
+from pyams_utils.registry import utility_config
 from pyams_utils.traversing import get_parent
 from pyramid.events import subscriber
 from zope.interface import implementer
@@ -34,7 +37,19 @@
     """Forms manager class"""
 
     shared_content_type = FORM_CONTENT_TYPE
-    shared_content_factory = Form
+
+
+@utility_config(provides=IFormsManagerFactory)
+class FormsManagerFactory(object):
+    """Defautl forms manager factory"""
+
+    def __new__(cls):
+        return FormsManager
+
+
+@adapter_config(context=IFormsManager, provides=ISharedContentFactory)
+def FormsManagerContentFactory(context):
+    return Form
 
 
 @subscriber(IObjectAddedEvent, context_selector=IFormsManager)
--- a/src/pyams_content/shared/imagemap/__init__.py	Thu Mar 22 14:50:32 2018 +0100
+++ b/src/pyams_content/shared/imagemap/__init__.py	Thu Mar 22 14:52:22 2018 +0100
@@ -25,19 +25,20 @@
 from pyams_content.features.preview.interfaces import IPreviewTarget
 from pyams_content.features.review.interfaces import IReviewTarget
 from pyams_content.shared.imagemap.interfaces import IMAGEMAP_CONTENT_TYPE, IMAGEMAP_CONTENT_NAME, \
-    IWfImageMap, IImageMap, IImageMapArea
+    IWfImageMap, IImageMap, IImageMapArea, IWfImageMapFactory
 from pyams_i18n.interfaces import II18n, II18nManager
 from z3c.form.interfaces import NOT_CHANGED
 from zope.location.interfaces import ISublocations
 from zope.traversing.interfaces import ITraversable
 
 # import packages
-from pyams_content.shared.common import WfSharedContent, register_content_type, SharedContent, WfSharedContentChecker
+from pyams_content.shared.common import WfSharedContent, register_content_type, SharedContent, WfSharedContentChecker, \
+    IWfSharedContentFactory
 from pyams_i18n.property import I18nFileProperty
 from pyams_utils.adapter import adapter_config, ContextAdapter
 from pyramid.threadlocal import get_current_registry
 from zope.container.contained import Contained
-from zope.interface import implementer
+from zope.interface import implementer, provider
 from zope.lifecycleevent import ObjectModifiedEvent
 from zope.location import locate
 from zope.schema.fieldproperty import FieldProperty
@@ -102,11 +103,15 @@
 register_content_type(WfImageMap)
 
 
+@provider(IWfImageMapFactory)
 @implementer(IImageMap)
 class ImageMap(SharedContent):
     """Workflow managed image map class"""
 
-    content_class = WfImageMap
+
+@adapter_config(context=IWfImageMapFactory, provides=IWfSharedContentFactory)
+def ImageMapContentFactory(context):
+    return WfImageMap
 
 
 @adapter_config(name='areas', context=IWfImageMap, provides=ITraversable)
--- a/src/pyams_content/shared/imagemap/interfaces/__init__.py	Thu Mar 22 14:50:32 2018 +0100
+++ b/src/pyams_content/shared/imagemap/interfaces/__init__.py	Thu Mar 22 14:52:22 2018 +0100
@@ -25,6 +25,7 @@
 from pyams_i18n.schema import I18nTextLineField, I18nImageField
 from pyams_sequence.schema import InternalReference
 from pyams_utils.schema import PersistentDict
+from zope.interface import Interface
 from zope.schema import Object, Choice
 
 from pyams_content import _
@@ -38,6 +39,10 @@
     """Image maps manager interface"""
 
 
+class IImageMapManagerFactory(Interface):
+    """Image maps manager factory interface"""
+
+
 class IImageMapArea(IAttributeAnnotatable):
     """Image map area interface"""
 
@@ -71,6 +76,10 @@
         """Get association for given area"""
 
 
+class IWfImageMapFactory(Interface):
+    """Image map factory interface"""
+
+
 class IImageMap(ISharedContent):
     """Workflow managed image map interface"""
 
--- a/src/pyams_content/shared/imagemap/manager.py	Thu Mar 22 14:50:32 2018 +0100
+++ b/src/pyams_content/shared/imagemap/manager.py	Thu Mar 22 14:52:22 2018 +0100
@@ -16,13 +16,16 @@
 # import standard library
 
 # import interfaces
-from pyams_content.shared.imagemap.interfaces import IImageMapManager, IMAGEMAP_CONTENT_TYPE
+from pyams_content.shared.common.interfaces import ISharedContentFactory
+from pyams_content.shared.imagemap.interfaces import IImageMapManager, IMAGEMAP_CONTENT_TYPE, IImageMapManagerFactory
 from zope.annotation.interfaces import IAttributeAnnotatable
 from zope.lifecycleevent.interfaces import IObjectAddedEvent
 
 # import packages
 from pyams_content.shared.common.manager import SharedTool
 from pyams_content.shared.imagemap import ImageMap
+from pyams_utils.adapter import adapter_config
+from pyams_utils.registry import utility_config
 from pyams_utils.traversing import get_parent
 from pyramid.events import subscriber
 from zope.component.interfaces import ISite
@@ -34,7 +37,19 @@
     """Image maps manager class"""
 
     shared_content_type = IMAGEMAP_CONTENT_TYPE
-    shared_content_factory = ImageMap
+
+
+@utility_config(provides=IImageMapManagerFactory)
+class ImageMapsManagerFactory(object):
+    """Default image maps manager factory"""
+
+    def __new__(cls):
+        return ImageMapsManager
+
+
+@adapter_config(context=IImageMapManager, provides=ISharedContentFactory)
+def ImageMapsManagerContentFactory(context):
+    return ImageMap
 
 
 @subscriber(IObjectAddedEvent, context_selector=IImageMapManager)
--- a/src/pyams_content/shared/logo/__init__.py	Thu Mar 22 14:50:32 2018 +0100
+++ b/src/pyams_content/shared/logo/__init__.py	Thu Mar 22 14:52:22 2018 +0100
@@ -17,14 +17,15 @@
 
 # import interfaces
 from pyams_content.features.checker.interfaces import IContentChecker, MISSING_VALUE
-from pyams_content.shared.logo.interfaces import IWfLogo, LOGO_CONTENT_TYPE, LOGO_CONTENT_NAME, ILogo
+from pyams_content.shared.logo.interfaces import IWfLogo, LOGO_CONTENT_TYPE, LOGO_CONTENT_NAME, ILogo, IWfLogoFactory
 from pyams_content.features.review import IReviewTarget
 
 # import packages
-from pyams_content.shared.common import WfSharedContent, register_content_type, SharedContent, WfSharedContentChecker
+from pyams_content.shared.common import WfSharedContent, register_content_type, SharedContent, WfSharedContentChecker, \
+    IWfSharedContentFactory
 from pyams_file.property import FileProperty
 from pyams_utils.adapter import adapter_config
-from zope.interface import implementer
+from zope.interface import implementer, provider
 from zope.schema.fieldproperty import FieldProperty
 
 from pyams_content import _
@@ -40,15 +41,18 @@
     image = FileProperty(IWfLogo['image'])
     url = FieldProperty(IWfLogo['url'])
 
-
 register_content_type(WfLogo)
 
 
+@provider(IWfLogoFactory)
 @implementer(ILogo)
 class Logo(SharedContent):
     """WOrkflow managed logo persistent class"""
 
-    content_class = WfLogo
+
+@adapter_config(context=IWfLogoFactory, provides=IWfSharedContentFactory)
+def LogoContentFactory(context):
+    return WfLogo
 
 
 @adapter_config(name='properties', context=IWfLogo, provides=IContentChecker)
--- a/src/pyams_content/shared/logo/interfaces/__init__.py	Thu Mar 22 14:50:32 2018 +0100
+++ b/src/pyams_content/shared/logo/interfaces/__init__.py	Thu Mar 22 14:52:22 2018 +0100
@@ -23,6 +23,7 @@
 # import packages
 from pyams_file.schema import ImageField
 from pyams_sequence.schema import InternalReferencesList
+from zope.interface import Interface
 from zope.schema import URI, Choice
 
 from pyams_content import _
@@ -36,6 +37,10 @@
     """Logos manager interface"""
 
 
+class ILogosManagerFactory(Interface):
+    """Logos manager factory interface"""
+
+
 class IWfLogo(IWfSharedContent):
     """Logo interface"""
 
@@ -48,6 +53,10 @@
               required=False)
 
 
+class IWfLogoFactory(Interface):
+    """Logo factory interface"""
+
+
 class ILogo(ISharedContent):
     """Workflow managed logo interface"""
 
--- a/src/pyams_content/shared/logo/manager.py	Thu Mar 22 14:50:32 2018 +0100
+++ b/src/pyams_content/shared/logo/manager.py	Thu Mar 22 14:52:22 2018 +0100
@@ -16,7 +16,8 @@
 # import standard library
 
 # import interfaces
-from pyams_content.shared.logo.interfaces import ILogosManager
+from pyams_content.shared.common.interfaces import ISharedContentFactory
+from pyams_content.shared.logo.interfaces import ILogosManager, ILogosManagerFactory
 from zope.annotation import IAttributeAnnotatable
 from zope.component.interfaces import ISite
 from zope.lifecycleevent import IObjectAddedEvent
@@ -24,6 +25,8 @@
 # import packages
 from pyams_content.shared.common.manager import SharedTool
 from pyams_content.shared.logo import LOGO_CONTENT_TYPE, Logo
+from pyams_utils.adapter import adapter_config
+from pyams_utils.registry import utility_config
 from pyams_utils.traversing import get_parent
 from pyramid.events import subscriber
 from zope.interface import implementer
@@ -34,7 +37,20 @@
     """Logos manager class"""
 
     shared_content_type = LOGO_CONTENT_TYPE
-    shared_content_factory = Logo
+
+
+@utility_config(provides=ILogosManagerFactory)
+class LogosManagerFactory(object):
+    """Default logos manager factory"""
+
+    def __new__(cls):
+        return LogosManager
+
+
+@adapter_config(context=ILogosManager, provides=ISharedContentFactory)
+def LogosManagerContentFactory(context):
+    """Logos manager content factory"""
+    return Logo
 
 
 @subscriber(IObjectAddedEvent, context_selector=ILogosManager)
--- a/src/pyams_content/shared/news/__init__.py	Thu Mar 22 14:50:32 2018 +0100
+++ b/src/pyams_content/shared/news/__init__.py	Thu Mar 22 14:52:22 2018 +0100
@@ -21,11 +21,13 @@
 from pyams_content.component.theme.interfaces import IThemesTarget
 from pyams_content.features.preview.interfaces import IPreviewTarget
 from pyams_content.features.review.interfaces import IReviewTarget
-from pyams_content.shared.news.interfaces import INewsEvent, IWfNewsEvent, NEWS_CONTENT_TYPE, NEWS_CONTENT_NAME
+from pyams_content.shared.news.interfaces import INewsEvent, IWfNewsEvent, NEWS_CONTENT_TYPE, NEWS_CONTENT_NAME, \
+    IWfNewsEventFactory
 
 # import packages
-from pyams_content.shared.common import SharedContent, WfSharedContent, register_content_type
-from zope.interface import implementer
+from pyams_content.shared.common import SharedContent, WfSharedContent, register_content_type, IWfSharedContentFactory
+from pyams_utils.adapter import adapter_config
+from zope.interface import implementer, provider
 
 
 @implementer(IWfNewsEvent, IIllustrationTarget, IParagraphContainerTarget, IThemesTarget,
@@ -39,8 +41,12 @@
 register_content_type(WfNewsEvent)
 
 
+@provider(IWfNewsEventFactory)
 @implementer(INewsEvent)
 class NewsEvent(SharedContent):
     """Workflow managed news event class"""
 
-    content_class = WfNewsEvent
+
+@adapter_config(context=IWfNewsEventFactory, provides=IWfSharedContentFactory)
+def NewsEventContentFactory(context):
+    return WfNewsEvent
--- a/src/pyams_content/shared/news/interfaces/__init__.py	Thu Mar 22 14:50:32 2018 +0100
+++ b/src/pyams_content/shared/news/interfaces/__init__.py	Thu Mar 22 14:52:22 2018 +0100
@@ -19,6 +19,7 @@
 from pyams_content.shared.common.interfaces import ISharedTool, IWfSharedContent, ISharedContent
 
 # import packages
+from zope.interface import Interface
 
 from pyams_content import _
 
@@ -31,9 +32,17 @@
     """News manager interface"""
 
 
+class INewsManagerFactory(Interface):
+    """News manager factory interface"""
+
+
 class IWfNewsEvent(IWfSharedContent):
     """News event interface"""
 
 
+class IWfNewsEventFactory(Interface):
+    """News event parent interface"""
+
+
 class INewsEvent(ISharedContent):
     """Workflow managed news event interface"""
--- a/src/pyams_content/shared/news/manager.py	Thu Mar 22 14:50:32 2018 +0100
+++ b/src/pyams_content/shared/news/manager.py	Thu Mar 22 14:52:22 2018 +0100
@@ -19,13 +19,16 @@
 from pyams_content.component.paragraph.interfaces import IParagraphFactorySettings
 from pyams_content.component.theme.interfaces import IThemesManagerTarget
 from pyams_content.reference.pictograms.interfaces import IPictogramManagerTarget
-from pyams_content.shared.news.interfaces import INewsManager, NEWS_CONTENT_TYPE
+from pyams_content.shared.common.interfaces import ISharedContentFactory
+from pyams_content.shared.news.interfaces import INewsManager, NEWS_CONTENT_TYPE, INewsManagerFactory
 from zope.component.interfaces import ISite
 from zope.lifecycleevent.interfaces import IObjectAddedEvent
 
 # import packages
 from pyams_content.shared.common.manager import SharedTool
 from pyams_content.shared.news import NewsEvent
+from pyams_utils.adapter import adapter_config
+from pyams_utils.registry import utility_config
 from pyams_utils.traversing import get_parent
 from pyramid.events import subscriber
 from zope.interface import implementer
@@ -37,12 +40,24 @@
     """News manager class"""
 
     shared_content_type = NEWS_CONTENT_TYPE
-    shared_content_factory = NewsEvent
 
     allowed_paragraphs = FieldProperty(IParagraphFactorySettings['allowed_paragraphs'])
     auto_created_paragraphs = FieldProperty(IParagraphFactorySettings['auto_created_paragraphs'])
 
 
+@utility_config(provides=INewsManagerFactory)
+class NewsManagerFactory(object):
+    """Default news manager factory"""
+
+    def __new__(cls):
+        return NewsManager
+
+
+@adapter_config(context=INewsManager, provides=ISharedContentFactory)
+def NewsManagerContentFactory(context):
+    return NewsEvent
+
+
 @subscriber(IObjectAddedEvent, context_selector=INewsManager)
 def handle_added_news_manager(event):
     """Register news manager when added"""
--- a/src/pyams_content/shared/site/__init__.py	Thu Mar 22 14:50:32 2018 +0100
+++ b/src/pyams_content/shared/site/__init__.py	Thu Mar 22 14:52:22 2018 +0100
@@ -24,10 +24,11 @@
 from pyams_workflow.interfaces import IWorkflow, IWorkflowVersions, IWorkflowState
 
 # import packages
-from pyams_content.shared.common import SharedContent, WfSharedContent, register_content_type
+from pyams_content.shared.common import SharedContent, WfSharedContent, register_content_type, IWfSharedContentFactory
 from pyams_content.shared.site.interfaces import ISiteContainer, ISiteFolder, ITopic, IWfTopic, TOPIC_CONTENT_NAME, \
-    TOPIC_CONTENT_TYPE
-from zope.interface import implementer
+    TOPIC_CONTENT_TYPE, IWfTopicFactory
+from pyams_utils.adapter import adapter_config
+from zope.interface import implementer, provider
 
 
 @implementer(IWfTopic, IParagraphContainerTarget, IThemesTarget,
@@ -41,15 +42,19 @@
 register_content_type(WfTopic)
 
 
+@provider(IWfTopicFactory)
 @implementer(ITopic)
 class Topic(SharedContent):
     """WOrkflow managed topic class"""
 
-    content_class = WfTopic
-
     def is_deletable(self):
         workflow = IWorkflow(self)
         for version in IWorkflowVersions(self).get_versions():
             if IWorkflowState(version).state != workflow.initial_state:
                 return False
         return True
+
+
+@adapter_config(context=IWfTopicFactory, provides=IWfSharedContentFactory)
+def TopicContentFactory(context):
+    return WfTopic
--- a/src/pyams_content/shared/site/container.py	Thu Mar 22 14:50:32 2018 +0100
+++ b/src/pyams_content/shared/site/container.py	Thu Mar 22 14:52:22 2018 +0100
@@ -17,11 +17,14 @@
 import json
 
 # import interfaces
+from pyams_content.shared.common.interfaces import ISharedContentFactory
 from pyams_content.shared.site.interfaces import ISiteContainer, ISiteFolder
 from pyams_i18n.interfaces import II18n
 from zope.intid.interfaces import IIntIds
 
 # import packages
+from pyams_content.shared.site import Topic
+from pyams_utils.adapter import adapter_config
 from pyams_utils.registry import get_utility
 from pyams_utils.request import query_request
 from pyramid.location import lineage
@@ -82,3 +85,8 @@
             container = container.__parent__
 
         return json.dumps(items)
+
+
+@adapter_config(context=ISiteContainer, provides=ISharedContentFactory)
+def SiteContainerTopicFactory(context):
+    return Topic
--- a/src/pyams_content/shared/site/interfaces/__init__.py	Thu Mar 22 14:50:32 2018 +0100
+++ b/src/pyams_content/shared/site/interfaces/__init__.py	Thu Mar 22 14:52:22 2018 +0100
@@ -26,7 +26,7 @@
     IBaseContentManagerRoles, IBaseSharedTool, IDeletableElement
 from pyams_i18n.schema import I18nTextLineField
 from zope.container.constraints import containers, contains
-from zope.interface import Attribute
+from zope.interface import Interface, Attribute
 from zope.schema import Text, Bool
 
 from pyams_content import _
@@ -58,6 +58,10 @@
                    required=False)
 
 
+class ISiteFolderFactory(Interface):
+    """Site folder factory interface"""
+
+
 class ISiteFolderRoles(IBaseContentManagerRoles):
     """Site folder roles interface"""
 
@@ -73,6 +77,10 @@
     topic_content_factory = Attribute("Topic content factory")
 
 
+class ISiteManagerFactory(Interface):
+    """Site manager factory interface"""
+
+
 TOPIC_CONTENT_TYPE = 'topic'
 TOPIC_CONTENT_NAME = _("Topic")
 
@@ -81,6 +89,10 @@
     """Topic interface"""
 
 
+class IWfTopicFactory(Interface):
+    """Topic factory interface"""
+
+
 class ITopic(ISharedContent, ISiteElement):
     """Workflow managed topic interface"""
 
--- a/src/pyams_content/shared/site/manager.py	Thu Mar 22 14:50:32 2018 +0100
+++ b/src/pyams_content/shared/site/manager.py	Thu Mar 22 14:52:22 2018 +0100
@@ -22,7 +22,9 @@
 from pyams_content.features.preview.interfaces import IPreviewTarget
 from pyams_content.interfaces import MANAGE_SITE_PERMISSION
 from pyams_content.reference.pictograms.interfaces import IPictogramManagerTarget
-from pyams_content.shared.site.interfaces import ISiteManager, TOPIC_CONTENT_TYPE
+from pyams_content.root.interfaces import ISiteRoot
+from pyams_content.shared.common.interfaces import ISharedContentFactory
+from pyams_content.shared.site.interfaces import ISiteManager, ISiteManagerFactory, ISiteFolderFactory
 from pyams_form.interfaces.form import IFormContextPermissionChecker
 from pyams_i18n.interfaces import II18n
 from pyams_portal.interfaces import IPortalContext
@@ -32,7 +34,7 @@
 
 # import packages
 from pyams_content.shared.common.manager import BaseSharedTool
-from pyams_content.shared.site import Topic
+from pyams_content.shared.site import Topic, ISiteContainer
 from pyams_content.shared.site.container import SiteContainerMixin
 from pyams_content.shared.site.folder import SiteFolder
 from pyams_skin.skin import UserSkinnableContent
@@ -53,17 +55,26 @@
 class SiteManager(SiteContainerMixin, OrderedContainer, BaseSharedTool, UserSkinnableContent):
     """Site manager persistent class"""
 
-    folder_factory = SiteFolder
-
-    topic_content_type = TOPIC_CONTENT_TYPE
-    topic_content_factory = Topic
-
     allowed_paragraphs = FieldProperty(IParagraphFactorySettings['allowed_paragraphs'])
     auto_created_paragraphs = FieldProperty(IParagraphFactorySettings['auto_created_paragraphs'])
 
     sequence_name = ''  # use default sequence generator
     sequence_prefix = ''
 
+    @property
+    def folder_factory(self):
+        return ISiteFolderFactory(self, SiteFolder)
+
+    @property
+    def topic_content_factory(self):
+        return ISharedContentFactory(self, None)
+
+    @property
+    def topic_content_type(self):
+        factory = self.topic_content_factory
+        if factory is not None:
+            return factory.content_class.content_type
+
     def is_deletable(self):
         for element in self.values():
             if not element.is_deletable():
@@ -71,6 +82,21 @@
         return True
 
 
+@adapter_config(context=ISiteRoot, provides=ISiteManagerFactory)
+def SiteRootSiteManagerFactory(context):
+    return SiteManager
+
+
+@adapter_config(context=ISiteManager, provides=ISiteFolderFactory)
+def SiteManagerFolderFactory(context):
+    return SiteFolder
+
+
+@adapter_config(context=ISiteContainer, provides=ISharedContentFactory)
+def SiteManagerSharedContentFactory(context):
+    return Topic
+
+
 @subscriber(IObjectAddedEvent, context_selector=ISiteManager)
 def handle_added_site_manager(event):
     """Register site manager when added"""
--- a/src/pyams_content/shared/site/zmi/__init__.py	Thu Mar 22 14:50:32 2018 +0100
+++ b/src/pyams_content/shared/site/zmi/__init__.py	Thu Mar 22 14:52:22 2018 +0100
@@ -36,6 +36,8 @@
 from pyams_utils.traversing import get_parent
 from pyams_utils.url import absolute_url
 from pyams_viewlet.viewlet import viewlet_config
+from pyramid.decorator import reify
+from pyramid.path import DottedNameResolver
 from pyramid.view import view_config
 from z3c.form import field
 from zope.interface import Interface
@@ -85,11 +87,22 @@
         if 'parent' in self.widgets:
             self.widgets['parent'].permission = CREATE_CONTENT_PERMISSION
 
+    @reify
+    def content_factory(self):
+        registry = self.request.registry
+        factory = registry.settings.get('pyams_content.config.topic_factory')
+        if factory:
+            factory = DottedNameResolver().resolve(factory)
+        else:
+            manager = get_parent(self.context, ISiteManager)
+            factory = manager.topic_content_factory
+        return factory
+
     def create(self, data):
-        manager = get_parent(self.context, ISiteManager)
-        return manager.topic_content_factory.content_class()
+        return self.content_factory.content_class()
 
     def update_content(self, content, data):
+        data = data.get(self, data)
         # initialize content fields
         content.title = data['title']
         content.short_name = content.title.copy()
@@ -103,8 +116,7 @@
             languages = II18nManager(parent).languages
             if languages:
                 II18nManager(content).languages = languages.copy()
-            manager = get_parent(parent, ISiteManager)
-            wf_parent = manager.topic_content_factory()
+            wf_parent = self.content_factory()
             self.request.registry.notify(ObjectCreatedEvent(wf_parent))
             uuid = str(uuid4())
             parent[uuid] = wf_parent
--- a/src/pyams_content/shared/site/zmi/folder.py	Thu Mar 22 14:50:32 2018 +0100
+++ b/src/pyams_content/shared/site/zmi/folder.py	Thu Mar 22 14:52:22 2018 +0100
@@ -38,6 +38,7 @@
 from pyams_viewlet.viewlet import viewlet_config
 from pyams_zmi.form import AdminDialogAddForm
 from pyramid.events import subscriber
+from pyramid.path import DottedNameResolver
 from pyramid.view import view_config
 from z3c.form import field
 from zope.interface import Interface, Invalid
@@ -99,10 +100,18 @@
             self.widgets['notepad'].widget_css_class = 'textarea'
 
     def create(self, data):
-        manager = get_parent(self.context, ISiteManager)
-        return manager.folder_factory()
+        registry = self.request.registry
+        factory = registry.settings.get('pyams_content.config.folder_factory')
+        if factory:
+            factory = DottedNameResolver().resolve(factory)
+        else:
+            manager = get_parent(self.context, ISiteManager)
+            factory = manager.folder_factory
+        return factory()
 
     def update_content(self, content, data):
+        data = data.get(self, data)
+        # initialize
         content.title = data['title']
         content.short_name = data['title']
         content.notepad = data['notepad']
--- a/src/pyams_content/shared/site/zmi/manager.py	Thu Mar 22 14:50:32 2018 +0100
+++ b/src/pyams_content/shared/site/zmi/manager.py	Thu Mar 22 14:52:22 2018 +0100
@@ -18,7 +18,7 @@
 # import interfaces
 from pyams_content.interfaces import MANAGE_SITE_ROOT_PERMISSION
 from pyams_content.root.interfaces import ISiteRoot
-from pyams_content.shared.site.interfaces import ISiteManager
+from pyams_content.shared.site.interfaces import ISiteManager, ISiteManagerFactory
 from pyams_content.skin.zmi.interfaces import ISiteTreeTable, IUserAddingsMenuLabel
 from pyams_i18n.interfaces import II18n, INegotiator
 from pyams_skin.interfaces.container import ITableElementEditor
@@ -29,6 +29,7 @@
 
 # import packages
 from pyams_content.shared.site import WfTopic
+from pyams_content.shared.site.manager import SiteManager
 from pyams_form.form import AJAXAddForm
 from pyams_pagelet.pagelet import pagelet_config
 from pyams_skin.table import DefaultElementEditorAdapter
@@ -66,9 +67,9 @@
 
     @property
     def label(self):
-        return '{content} ({blog})'.format(
+        return '{content} ({site})'.format(
             content=self.request.localizer.translate(WfTopic.content_name),
-            blog=II18n(self.context).query_attribute('title', request=self.request))
+            site=II18n(self.context).query_attribute('title', request=self.request))
 
 
 @viewlet_config(name='add-site-manager.menu', context=ISiteRoot, layer=IAdminLayer,
@@ -97,9 +98,10 @@
 
     def create(self, data):
         factory = self.request.registry.settings.get('pyams_content.config.site_factory')
-        if factory is None:
-            factory = 'pyams_content.shared.site.manager.SiteManager'
-        factory = DottedNameResolver().resolve(factory)
+        if factory:
+            factory = DottedNameResolver().resolve(factory)
+        else:
+            factory = ISiteManagerFactory(self.context, SiteManager)
         return factory()
 
     def add(self, object):
--- a/src/pyams_content/shared/view/__init__.py	Thu Mar 22 14:50:32 2018 +0100
+++ b/src/pyams_content/shared/view/__init__.py	Thu Mar 22 14:52:22 2018 +0100
@@ -23,8 +23,8 @@
 from hypatia.interfaces import ICatalog
 from pyams_content.features.preview.interfaces import IPreviewTarget
 from pyams_content.features.review.interfaces import IReviewTarget
-from pyams_content.shared.view.interfaces import IView, IWfView, IViewQuery, IViewQueryParamsExtension, \
-    IViewQueryFilterExtension, VIEW_CONTENT_TYPE, VIEW_CONTENT_NAME, IViewSettings
+from pyams_content.shared.view.interfaces import IView, IWfView, IWfViewFactory, IViewQuery, \
+    IViewQueryParamsExtension, IViewQueryFilterExtension, VIEW_CONTENT_TYPE, VIEW_CONTENT_NAME, IViewSettings
 from zope.intid.interfaces import IIntIds
 from zope.lifecycleevent.interfaces import IObjectModifiedEvent
 
@@ -33,7 +33,7 @@
 from hypatia.query import Any, Gt, Lt
 from pyams_cache.beaker import get_cache
 from pyams_catalog.query import CatalogResultSet, or_
-from pyams_content.shared.common import WfSharedContent, register_content_type, SharedContent
+from pyams_content.shared.common import WfSharedContent, register_content_type, SharedContent, IWfSharedContentFactory
 from pyams_utils.adapter import adapter_config, ContextAdapter
 from pyams_utils.list import unique
 from pyams_utils.registry import get_utility, get_global_registry
@@ -41,7 +41,7 @@
 from pyams_workflow.interfaces import IWorkflow
 from pyramid.events import subscriber
 from pyramid.threadlocal import get_current_registry
-from zope.interface import implementer
+from zope.interface import implementer, provider
 from zope.schema.fieldproperty import FieldProperty
 
 
@@ -111,11 +111,15 @@
 register_content_type(WfView)
 
 
+@provider(IWfViewFactory)
 @implementer(IView)
 class View(SharedContent):
     """Workflow managed view class"""
 
-    content_class = WfView
+
+@adapter_config(context=IWfViewFactory, provides=IWfSharedContentFactory)
+def ViewContentFactory(context):
+    return WfView
 
 
 @adapter_config(context=IWfView, provides=IViewQuery)
--- a/src/pyams_content/shared/view/interfaces/__init__.py	Thu Mar 22 14:50:32 2018 +0100
+++ b/src/pyams_content/shared/view/interfaces/__init__.py	Thu Mar 22 14:52:22 2018 +0100
@@ -50,6 +50,10 @@
     """Views manager interface"""
 
 
+class IViewsManagerFactory(Interface):
+    """Views manager factory interface"""
+
+
 class IWfView(IWfSharedContent):
     """View interface"""
 
@@ -87,6 +91,10 @@
         """Get results of catalog query"""
 
 
+class IWfViewFactory(Interface):
+    """View factory interface"""
+
+
 class IView(ISharedContent):
     """Workflow managed view interface"""
 
--- a/src/pyams_content/shared/view/manager.py	Thu Mar 22 14:50:32 2018 +0100
+++ b/src/pyams_content/shared/view/manager.py	Thu Mar 22 14:52:22 2018 +0100
@@ -17,7 +17,8 @@
 
 # import interfaces
 from pyams_content.component.theme.interfaces import IThemesManagerTarget
-from pyams_content.shared.view.interfaces import IViewsManager, VIEW_CONTENT_TYPE
+from pyams_content.shared.common.interfaces import ISharedContentFactory
+from pyams_content.shared.view.interfaces import IViewsManager, VIEW_CONTENT_TYPE, IViewsManagerFactory
 from zope.annotation.interfaces import IAttributeAnnotatable
 from zope.component.interfaces import ISite
 from zope.lifecycleevent.interfaces import IObjectAddedEvent
@@ -25,6 +26,8 @@
 # import packages
 from pyams_content.shared.common.manager import SharedTool
 from pyams_content.shared.view import View
+from pyams_utils.adapter import adapter_config
+from pyams_utils.registry import utility_config
 from pyams_utils.traversing import get_parent
 from pyramid.events import subscriber
 from zope.interface import implementer
@@ -35,7 +38,20 @@
     """Views manager class"""
 
     shared_content_type = VIEW_CONTENT_TYPE
-    shared_content_factory = View
+
+
+@utility_config(provides=IViewsManagerFactory)
+class ViewsManagerFactory(object):
+    """Default views manager factory"""
+
+    def __new__(cls):
+        return ViewsManager
+
+
+@adapter_config(context=IViewsManager, provides=ISharedContentFactory)
+def ViewsManagerContentFactory(context):
+    """Views manager content factory"""
+    return View
 
 
 @subscriber(IObjectAddedEvent, context_selector=IViewsManager)