Initialized sites management
authorThierry Florac <thierry.florac@onf.fr>
Tue, 14 Nov 2017 14:05:30 +0100
changeset 277 9649f8ce3b1c
parent 276 78422a1c4228
child 278 f78a3411bf62
Initialized sites management
src/pyams_content/root/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/security.py
src/pyams_content/shared/common/zmi/__init__.py
src/pyams_content/shared/common/zmi/dashboard.py
src/pyams_content/shared/common/zmi/header.py
src/pyams_content/shared/common/zmi/manager.py
src/pyams_content/shared/common/zmi/search.py
src/pyams_content/shared/common/zmi/security.py
src/pyams_content/shared/common/zmi/summary.py
src/pyams_content/shared/common/zmi/workflow.py
src/pyams_content/shared/site/__init__.py
src/pyams_content/shared/site/interfaces/__init__.py
src/pyams_content/shared/site/manager.py
src/pyams_content/shared/site/zmi/manager.py
src/pyams_content/shared/zmi/sites.py
src/pyams_content/workflow/task.py
src/pyams_content/zmi/viewlet/toplinks/__init__.py
--- a/src/pyams_content/root/zmi/__init__.py	Fri Nov 10 12:07:43 2017 +0100
+++ b/src/pyams_content/root/zmi/__init__.py	Tue Nov 14 14:05:30 2017 +0100
@@ -20,7 +20,7 @@
 from pyams_content.interfaces import PUBLISH_CONTENT_PERMISSION, MANAGE_SITE_ROOT_PERMISSION
 from pyams_content.profile.interfaces import IAdminProfile
 from pyams_content.root.interfaces import ISiteRoot
-from pyams_content.shared.common.interfaces import ISharedTool, IManagerRestrictions
+from pyams_content.shared.common.interfaces import IBaseSharedTool, IManagerRestrictions
 from pyams_content.shared.common.interfaces.zmi import ISiteRootDashboardTable
 from pyams_content.zmi.interfaces import IDashboardMenu, IMyDashboardMenu, IAllContentsMenu
 from pyams_i18n.interfaces import II18n
@@ -34,11 +34,13 @@
 from pyams_zmi.layer import IAdminLayer
 from z3c.table.interfaces import IValues, IColumn
 from zope.dublincore.interfaces import IZopeDublinCore
+from zope.intid.interfaces import IIntIds
 
 # import packages
 from hypatia.catalog import CatalogQuery
-from hypatia.query import And, Or, Any, Eq
+from hypatia.query import And, Or, Any, Eq, Any
 from pyams_catalog.query import CatalogResultSet
+from pyams_content.shared.common import CONTENT_TYPES
 from pyams_content.shared.common.zmi.dashboard import BaseDashboardTable as BaseDashboardTableBase
 from pyams_pagelet.pagelet import pagelet_config
 from pyams_skin.container import ContainerView
@@ -145,11 +147,12 @@
 
     @property
     def values(self):
+        intids = get_utility(IIntIds)
         catalog = get_utility(ICatalog)
         params = None
-        for tool in get_all_utilities_registered_for(ISharedTool):
+        for tool in get_all_utilities_registered_for(IBaseSharedTool):
             workflow = IWorkflow(tool)
-            query = Eq(catalog['content_type'], tool.shared_content_type) & \
+            query = Eq(catalog['parents'], intids.register(tool)) & \
                     Any(catalog['workflow_state'], workflow.waiting_states)
             params = params | query if params else query
         return filter(self.check_access,
@@ -190,11 +193,12 @@
 
     @property
     def values(self):
+        intids = get_utility(IIntIds)
         catalog = get_utility(ICatalog)
         params = None
-        for tool in get_all_utilities_registered_for(ISharedTool):
+        for tool in get_all_utilities_registered_for(IBaseSharedTool):
             workflow = IWorkflow(tool)
-            query = Eq(catalog['content_type'], tool.shared_content_type) & \
+            query = Eq(catalog['parents'], intids.register(tool)) & \
                     Any(catalog['workflow_state'], workflow.waiting_states) & \
                     Eq(catalog['workflow_principal'], self.request.principal.id)
             params = params | query if params else query
@@ -228,10 +232,12 @@
 
     @property
     def values(self):
+        intids = get_utility(IIntIds)
         catalog = get_utility(ICatalog)
         params = None
-        for tool in get_all_utilities_registered_for(ISharedTool):
-            query = And(Eq(catalog['content_type'], tool.shared_content_type),
+        for tool in get_all_utilities_registered_for(IBaseSharedTool):
+            query = And(Eq(catalog['parents'], intids.register(tool)),
+                        Any(catalog['content_type'], CONTENT_TYPES.keys()),
                         Or(Eq(catalog['role:owner'], self.request.principal.id),
                            Eq(catalog['role:contributor'], self.request.principal.id)))
             params = params | query if params else query
@@ -287,11 +293,13 @@
 
     @property
     def values(self):
+        intids = get_utility(IIntIds)
         catalog = get_utility(ICatalog)
         profile = IAdminProfile(self.request.principal)
         params = None
-        for tool in get_all_utilities_registered_for(ISharedTool):
-            query = And(Eq(catalog['content_type'], tool.shared_content_type),
+        for tool in get_all_utilities_registered_for(IBaseSharedTool):
+            query = And(Eq(catalog['parents'], intids.register(tool)),
+                        Any(catalog['content_type'], CONTENT_TYPES.keys()),
                         Any(catalog['oid'], profile.favorites or ()))
             params = params | query if params else query
         return unique(map(lambda x: IWorkflowVersions(x).get_last_versions()[0],
@@ -346,11 +354,12 @@
 
     @property
     def values(self):
+        intids = get_utility(IIntIds)
         catalog = get_utility(ICatalog)
         params = None
-        for tool in get_all_utilities_registered_for(ISharedTool):
+        for tool in get_all_utilities_registered_for(IBaseSharedTool):
             workflow = IWorkflow(tool)
-            query = And(Eq(catalog['content_type'], tool.shared_content_type),
+            query = And(Eq(catalog['parents'], intids.register(tool)),
                         Or(Eq(catalog['role:owner'], self.request.principal.id),
                            Eq(catalog['role:contributor'], self.request.principal.id)),
                         Eq(catalog['workflow_state'], workflow.initial_state))
@@ -406,11 +415,12 @@
 
     @property
     def values(self):
+        intids = get_utility(IIntIds)
         catalog = get_utility(ICatalog)
         params = None
-        for tool in get_all_utilities_registered_for(ISharedTool):
+        for tool in get_all_utilities_registered_for(IBaseSharedTool):
             workflow = IWorkflow(tool)
-            query = And(Eq(catalog['content_type'], tool.shared_content_type),
+            query = And(Eq(catalog['parents'], intids.register(tool)),
                         Or(Eq(catalog['role:owner'], self.request.principal.id),
                            Eq(catalog['role:contributor'], self.request.principal.id)),
                         Any(catalog['workflow_state'], workflow.waiting_states))
@@ -466,11 +476,12 @@
 
     @property
     def values(self):
+        intids = get_utility(IIntIds)
         catalog = get_utility(ICatalog)
         params = None
-        for tool in get_all_utilities_registered_for(ISharedTool):
+        for tool in get_all_utilities_registered_for(IBaseSharedTool):
             workflow = IWorkflow(tool)
-            query = And(Eq(catalog['content_type'], tool.shared_content_type),
+            query = And(Eq(catalog['parents'], intids.register(tool)),
                         Or(Eq(catalog['role:owner'], self.request.principal.id),
                            Eq(catalog['role:contributor'], self.request.principal.id)),
                         Any(catalog['workflow_state'], workflow.published_states))
@@ -526,11 +537,12 @@
 
     @property
     def values(self):
+        intids = get_utility(IIntIds)
         catalog = get_utility(ICatalog)
         params = None
-        for tool in get_all_utilities_registered_for(ISharedTool):
+        for tool in get_all_utilities_registered_for(IBaseSharedTool):
             workflow = IWorkflow(tool)
-            query = And(Eq(catalog['content_type'], tool.shared_content_type),
+            query = And(Eq(catalog['parents'], intids.register(tool)),
                         Or(Eq(catalog['role:owner'], self.request.principal.id),
                            Eq(catalog['role:contributor'], self.request.principal.id)),
                         Any(catalog['workflow_state'], workflow.retired_states))
@@ -587,12 +599,13 @@
 
     @property
     def values(self):
+        intids = get_utility(IIntIds)
         catalog = get_utility(ICatalog)
         params = None
         principal_id = self.request.principal.id
-        for tool in get_all_utilities_registered_for(ISharedTool):
+        for tool in get_all_utilities_registered_for(IBaseSharedTool):
             workflow = IWorkflow(tool)
-            query = And(Eq(catalog['content_type'], tool.shared_content_type),
+            query = And(Eq(catalog['parents'], intids.register(tool)),
                         Or(Eq(catalog['role:owner'], principal_id),
                            Eq(catalog['role:contributor'], principal_id)),
                         Any(catalog['workflow_state'], workflow.archived_states))
@@ -676,11 +689,12 @@
 
     @property
     def values(self):
+        intids = get_utility(IIntIds)
         catalog = get_utility(ICatalog)
         params = None
-        for tool in get_all_utilities_registered_for(ISharedTool):
+        for tool in get_all_utilities_registered_for(IBaseSharedTool):
             workflow = IWorkflow(tool)
-            query = And(Eq(catalog['content_type'], tool.shared_content_type),
+            query = And(Eq(catalog['parents'], intids.register(tool)),
                         Any(catalog['workflow_state'], workflow.published_states))
             params = params | query if params else query
         return unique(CatalogResultSet(CatalogQuery(catalog).query(params,
@@ -742,10 +756,12 @@
 
     @property
     def values(self):
+        intids = get_utility(IIntIds)
         catalog = get_utility(ICatalog)
         params = None
-        for tool in get_all_utilities_registered_for(ISharedTool):
-            query = Eq(catalog['content_type'], tool.shared_content_type)
+        for tool in get_all_utilities_registered_for(IBaseSharedTool):
+            query = Eq(catalog['parents'], intids.register(tool)) & \
+                    Any(catalog['content_type'], CONTENT_TYPES.keys())
             params = params | query if params else query
         return unique(CatalogResultSet(CatalogQuery(catalog).query(params,
                                                                    limit=50,
--- a/src/pyams_content/shared/blog/zmi/manager.py	Fri Nov 10 12:07:43 2017 +0100
+++ b/src/pyams_content/shared/blog/zmi/manager.py	Tue Nov 14 14:05:30 2017 +0100
@@ -120,8 +120,8 @@
 
 
 @adapter_config(context=(IBlogManager, IAdminLayer, SiteTreeTable), provides=ITableElementEditor)
-class SiteTreeTableElementEditor(DefaultElementEditorAdapter):
-    """Site tree table element editor"""
+class BlogManagerTableElementEditor(DefaultElementEditorAdapter):
+    """Blog manager table element editor"""
 
     view_name = 'admin'
     modal_target = False
--- a/src/pyams_content/shared/common/__init__.py	Fri Nov 10 12:07:43 2017 +0100
+++ b/src/pyams_content/shared/common/__init__.py	Tue Nov 14 14:05:30 2017 +0100
@@ -20,7 +20,8 @@
 from pyams_content.interfaces import IBaseContentInfo
 from pyams_content.features.checker.interfaces import IContentChecker, MISSING_VALUE, MISSING_LANG_VALUE
 from pyams_content.features.review.interfaces import IReviewComments
-from pyams_content.shared.common.interfaces import IWfSharedContent, IWfSharedContentRoles, ISharedContent, ISharedTool
+from pyams_content.shared.common.interfaces import IWfSharedContent, IWfSharedContentRoles, ISharedContent, \
+    IBaseSharedTool, ISharedSite
 from pyams_i18n.interfaces import II18nManager, II18n
 from pyams_security.interfaces import IDefaultProtectionPolicy
 from pyams_sequence.interfaces import ISequentialIdTarget, ISequentialIdInfo
@@ -40,7 +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.registry import query_utility
+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
 from pyams_utils.traversing import get_parent
@@ -56,6 +57,19 @@
 from pyams_content import _
 
 
+@vocabulary_config(name='PyAMS shared sites')
+class SharedSiteVocabulary(SimpleVocabulary):
+    """Shared sites vocabulary"""
+
+    interface = ISharedSite
+
+    def __init__(self, context):
+        request = query_request()
+        super(SharedSiteVocabulary, self).__init__([SimpleTerm(v, title=II18n(t).query_attribute('title',
+                                                                                                 request=request))
+                                                    for v, t in get_utilities_for(self.interface)])
+
+
 #
 # Content types management
 #
@@ -205,7 +219,7 @@
 @adapter_config(context=IWfSharedContent, provides=IWorkflow)
 def WfSharedContentWorkflowAdapter(context):
     """Shared content workflow adapter"""
-    parent = get_parent(context, ISharedTool)
+    parent = get_parent(context, IBaseSharedTool)
     return query_utility(IWorkflow, name=parent.shared_content_workflow)
 
 
@@ -251,7 +265,7 @@
 
     @property
     def workflow_name(self):
-        return get_parent(self, ISharedTool).shared_content_workflow
+        return get_parent(self, IBaseSharedTool).shared_content_workflow
 
 
 @adapter_config(context=ISharedContent, provides=IBaseContentInfo)
@@ -270,5 +284,5 @@
 @adapter_config(context=ISharedContent, provides=IWorkflow)
 def SharedContentWorkflowAdapter(context):
     """Shared content workflow adapter"""
-    parent = get_parent(context, ISharedTool)
+    parent = get_parent(context, IBaseSharedTool)
     return query_utility(IWorkflow, name=parent.shared_content_workflow)
--- a/src/pyams_content/shared/common/interfaces/__init__.py	Fri Nov 10 12:07:43 2017 +0100
+++ b/src/pyams_content/shared/common/interfaces/__init__.py	Tue Nov 14 14:05:30 2017 +0100
@@ -46,14 +46,10 @@
     contains('.ISharedTool')
 
 
-class ISharedTool(IBaseContent, IContainer):
-    """Shared tool interface"""
+class IBaseSharedTool(IBaseContent, IContainer):
+    """Base shared tool interface"""
 
     containers(ISharedToolContainer)
-    contains('.ISharedData')
-
-    shared_content_type = Attribute("Shared data content type")
-    shared_content_factory = Attribute("Shared data factory")
 
     shared_content_workflow = Choice(title=_("Workflow name"),
                                      description=_("Name of workflow utility used to manage tool contents"),
@@ -61,6 +57,15 @@
                                      default="PyAMS default workflow")
 
 
+class ISharedTool(IBaseSharedTool):
+    """Shared tool interface"""
+
+    contains('.ISharedContent')
+
+    shared_content_type = Attribute("Shared data content type")
+    shared_content_factory = Attribute("Shared data factory")
+
+
 class ISharedToolRoles(Interface):
     """Shared tool roles interface"""
 
--- a/src/pyams_content/shared/common/manager.py	Fri Nov 10 12:07:43 2017 +0100
+++ b/src/pyams_content/shared/common/manager.py	Tue Nov 14 14:05:30 2017 +0100
@@ -16,7 +16,8 @@
 # import standard library
 
 # import interfaces
-from pyams_content.shared.common.interfaces import ISharedToolContainer, ISharedTool, ISharedToolRoles
+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_security.interfaces import IDefaultProtectionPolicy
 from pyams_workflow.interfaces import IWorkflow
 from zope.annotation.interfaces import IAttributeAnnotatable
@@ -40,11 +41,11 @@
     short_name = FieldProperty(ISharedToolContainer['short_name'])
 
 
-@implementer(IDefaultProtectionPolicy, ISharedTool, ISharedToolRoles, IAttributeAnnotatable)
-class SharedTool(ProtectedObject, Folder, I18nManagerMixin):
-    """Shared tool"""
+@implementer(IDefaultProtectionPolicy, IBaseSharedTool, ISharedToolRoles, IAttributeAnnotatable)
+class BaseSharedTool(ProtectedObject, I18nManagerMixin):
+    """Base shared tool class"""
 
-    __roles__ = ('pyams.Webmaster', 'pyams.Pilot', 'pyams.Manager', 'pyams.Contributor')
+    __roles__ = (WEBMASTER_ROLE, PILOT_ROLE, MANAGER_ROLE, CONTRIBUTOR_ROLE)
 
     roles_interface = ISharedToolRoles
 
@@ -53,15 +54,21 @@
     managers = RolePrincipalsFieldProperty(ISharedToolRoles['managers'])
     contributors = RolePrincipalsFieldProperty(ISharedToolRoles['contributors'])
 
-    title = FieldProperty(ISharedTool['title'])
-    short_name = FieldProperty(ISharedTool['short_name'])
+    title = FieldProperty(IBaseSharedTool['title'])
+    short_name = FieldProperty(IBaseSharedTool['short_name'])
+
+    shared_content_workflow = FieldProperty(IBaseSharedTool['shared_content_workflow'])
+
+
+@implementer(ISharedTool)
+class SharedTool(Folder, BaseSharedTool):
+    """Shared tool class"""
 
     shared_content_type = None
     shared_content_factory = None
-    shared_content_workflow = FieldProperty(ISharedTool['shared_content_workflow'])
 
 
-@adapter_config(context=ISharedTool, provides=IWorkflow)
+@adapter_config(context=IBaseSharedTool, provides=IWorkflow)
 def SharedToolWorkflowAdapter(context):
     """Shared tool workflow adapter"""
     return query_utility(IWorkflow, name=context.shared_content_workflow)
--- a/src/pyams_content/shared/common/security.py	Fri Nov 10 12:07:43 2017 +0100
+++ b/src/pyams_content/shared/common/security.py	Tue Nov 14 14:05:30 2017 +0100
@@ -18,7 +18,7 @@
 # import interfaces
 from pyams_content.interfaces import MANAGE_CONTENT_PERMISSION
 from pyams_content.shared.common.interfaces import IWfSharedContent, IManagerRestrictions, MANAGER_RESTRICTIONS_KEY, \
-    IManagerRestrictionsFactory, ISharedTool, IManagerRestrictionInfo
+    IManagerRestrictionsFactory, IBaseSharedTool, IManagerRestrictionInfo
 
 # import packages
 from persistent import Persistent
@@ -58,7 +58,7 @@
         return False
 
 
-@adapter_config(context=ISharedTool, provides=IManagerRestrictions)
+@adapter_config(context=IBaseSharedTool, provides=IManagerRestrictions)
 class SharedToolManagerRestrictions(ContextAdapter):
     """Shared tool manager restrictions"""
 
@@ -86,12 +86,12 @@
 @adapter_config(context=IWfSharedContent, provides=IManagerRestrictions)
 def SharedContentManagerRestrictions(context):
     """Shared tool manager restrictions"""
-    tool = get_parent(context, ISharedTool)
+    tool = get_parent(context, IBaseSharedTool)
     if tool is not None:
         return IManagerRestrictions(tool)
 
 
-@adapter_config(context=ISharedTool, provides=IManagerRestrictionsFactory)
+@adapter_config(context=IBaseSharedTool, provides=IManagerRestrictionsFactory)
 def SharedToolManagerRestrictionsFactory(context):
     """Default shared tool manager restrictions factory"""
     return SharedToolManagerRestrictionInfo
--- a/src/pyams_content/shared/common/zmi/__init__.py	Fri Nov 10 12:07:43 2017 +0100
+++ b/src/pyams_content/shared/common/zmi/__init__.py	Tue Nov 14 14:05:30 2017 +0100
@@ -21,7 +21,8 @@
 from pyams_content.interfaces import MANAGE_SITE_ROOT_PERMISSION, MANAGE_CONTENT_PERMISSION, CREATE_CONTENT_PERMISSION, \
     PUBLISH_CONTENT_PERMISSION
 from pyams_content.features.review.interfaces import IReviewComments
-from pyams_content.shared.common.interfaces import IWfSharedContent, ISharedContent, ISharedTool, IManagerRestrictions
+from pyams_content.shared.common.interfaces import IWfSharedContent, ISharedContent, IBaseSharedTool, \
+    IManagerRestrictions
 from pyams_form.interfaces.form import IFormContextPermissionChecker, IWidgetsPrefixViewletsManager
 from pyams_i18n.interfaces import II18n, II18nManager
 from pyams_sequence.interfaces import ISequentialIdInfo
@@ -207,7 +208,7 @@
 
     @property
     def back_url(self):
-        shared_tool = get_parent(self.context, ISharedTool)
+        shared_tool = get_parent(self.context, IBaseSharedTool)
         return absolute_url(shared_tool, self.request, 'admin#dashboard.html')
 
     back_target = None
@@ -281,7 +282,7 @@
         content = get_parent(self.context, ISharedContent)
         new_content = content.__class__()
         registry.notify(ObjectCreatedEvent(new_content))
-        container = get_parent(content, ISharedTool)
+        container = get_parent(content, IBaseSharedTool)
         container[str(uuid4())] = new_content
         # initialize new version
         new_version = copy(self.context)
--- a/src/pyams_content/shared/common/zmi/dashboard.py	Fri Nov 10 12:07:43 2017 +0100
+++ b/src/pyams_content/shared/common/zmi/dashboard.py	Tue Nov 14 14:05:30 2017 +0100
@@ -19,7 +19,7 @@
 from hypatia.interfaces import ICatalog
 from pyams_content.interfaces import MANAGE_SITE_ROOT_PERMISSION, PUBLISH_CONTENT_PERMISSION
 from pyams_content.profile.interfaces import IAdminProfile
-from pyams_content.shared.common.interfaces import ISharedTool, IWfSharedContent, IManagerRestrictions
+from pyams_content.shared.common.interfaces import IBaseSharedTool, IWfSharedContent, IManagerRestrictions
 from pyams_content.shared.common.interfaces.zmi import IDashboardTable, ISharedToolDashboardTable
 from pyams_content.zmi.interfaces import IDashboardMenu, IMyDashboardMenu, IAllContentsMenu
 from pyams_i18n.interfaces import II18n
@@ -34,11 +34,13 @@
 from pyams_zmi.layer import IAdminLayer
 from z3c.table.interfaces import IValues, IColumn
 from zope.dublincore.interfaces import IZopeDublinCore
+from zope.intid.interfaces import IIntIds
 
 # import packages
 from hypatia.catalog import CatalogQuery
 from hypatia.query import And, Or, Eq, Any
 from pyams_catalog.query import CatalogResultSet
+from pyams_content.shared.common import CONTENT_TYPES
 from pyams_content.skin import pyams_content
 from pyams_pagelet.pagelet import pagelet_config
 from pyams_skin.container import ContainerView
@@ -106,7 +108,7 @@
     _header = _("Title")
 
 
-@adapter_config(context=(ISharedTool, IPyAMSLayer, IDashboardTable), provides=ITableElementName)
+@adapter_config(context=(IBaseSharedTool, IPyAMSLayer, IDashboardTable), provides=ITableElementName)
 class SharedToolDashboardNameAdapter(ContextRequestViewAdapter):
     """Shared tool dashboard name adapter"""
 
@@ -133,8 +135,11 @@
 
     def getValue(self, obj):
         target = get_parent(obj, ISequentialIdTarget)
-        sequence = get_utility(ISequentialIntIds, name=target.sequence_name)
-        return sequence.get_base_oid(ISequentialIdInfo(obj).oid, target.sequence_prefix)
+        if target is not None:
+            sequence = get_utility(ISequentialIntIds, name=target.sequence_name)
+            return sequence.get_base_oid(ISequentialIdInfo(obj).oid, target.sequence_prefix)
+        else:
+            return '--'
 
 
 @adapter_config(name='status', context=(Interface, IPyAMSLayer, ISharedToolDashboardTable), provides=IColumn)
@@ -145,19 +150,22 @@
     weight = 20
 
     def getValue(self, obj):
-        workflow = IWorkflow(obj)
-        state = IWorkflowState(obj)
-        result = self.request.localizer.translate(workflow.get_state_label(state.state))
-        if state.state_urgency:
-            result += ' <i class="fa fa-fw fa-exclamation-triangle txt-color-red"></i>'
-        elif state.state in workflow.published_states:
-            pub_info = IWorkflowPublicationInfo(obj, None)
-            if (pub_info is not None) and not pub_info.is_published():
-                translate = self.request.localizer.translate
-                result += ' <i class="fa fa-fw fa-hourglass-half font-xs opacity-75 hint align-base" ' \
-                          'data-ams-hint-offset="5" title="{0}"></i>'.format(
-                    translate(_("Content publication start date is not passed yet")))
-        return result
+        state = IWorkflowState(obj, None)
+        if state is not None:
+            workflow = IWorkflow(obj)
+            result = self.request.localizer.translate(workflow.get_state_label(state.state))
+            if state.state_urgency:
+                result += ' <i class="fa fa-fw fa-exclamation-triangle txt-color-red"></i>'
+            elif state.state in workflow.published_states:
+                pub_info = IWorkflowPublicationInfo(obj, None)
+                if (pub_info is not None) and not pub_info.is_published():
+                    translate = self.request.localizer.translate
+                    result += ' <i class="fa fa-fw fa-hourglass-half font-xs opacity-75 hint align-base" ' \
+                              'data-ams-hint-offset="5" title="{0}"></i>'.format(
+                        translate(_("Content publication start date is not passed yet")))
+            return result
+        else:
+            return '--'
 
 
 @adapter_config(name='status_date', context=(Interface, IPyAMSLayer, ISharedToolDashboardTable), provides=IColumn)
@@ -170,8 +178,11 @@
     weight = 21
 
     def getValue(self, obj):
-        state = IWorkflowState(obj)
-        return format_datetime(state.state_date, SH_DATETIME_FORMAT, request=self.request)
+        state = IWorkflowState(obj, None)
+        if state is not None:
+            return format_datetime(state.state_date, SH_DATETIME_FORMAT, request=self.request)
+        else:
+            return '--'
 
 
 @adapter_config(name='version', context=(Interface, IPyAMSLayer, ISharedToolDashboardTable), provides=IColumn)
@@ -182,7 +193,11 @@
     weight = 25
 
     def getValue(self, obj):
-        return str(IWorkflowState(obj).version_id)
+        state = IWorkflowState(obj, None)
+        if state is not None:
+            return str(state.version_id)
+        else:
+            return '--'
 
 
 @adapter_config(name='status_principal', context=(Interface, IPyAMSLayer, ISharedToolDashboardTable), provides=IColumn)
@@ -195,9 +210,12 @@
     weight = 30
 
     def getValue(self, obj):
-        state = IWorkflowState(obj)
-        manager = get_utility(ISecurityManager)
-        return manager.get_principal(state.state_principal).title
+        state = IWorkflowState(obj, None)
+        if state is not None:
+            manager = get_utility(ISecurityManager)
+            return manager.get_principal(state.state_principal).title
+        else:
+            return '--'
 
 
 @adapter_config(name='owner', context=(Interface, IPyAMSLayer, ISharedToolDashboardTable), provides=IColumn)
@@ -210,8 +228,13 @@
     weight = 35
 
     def getValue(self, obj):
-        manager = get_utility(ISecurityManager)
-        return manager.get_principal(next(iter(obj.owner))).title
+        try:
+            owner = obj.owner
+        except AttributeError:
+            return '--'
+        else:
+            manager = get_utility(ISecurityManager)
+            return manager.get_principal(next(iter(owner))).title
 
 
 @adapter_config(name='modified', context=(Interface, IPyAMSLayer, ISharedToolDashboardTable), provides=IColumn)
@@ -229,7 +252,7 @@
 # Shared tool control panel
 #
 
-@viewlet_config(name='dashboard.menu', context=ISharedTool, layer=IAdminLayer,
+@viewlet_config(name='dashboard.menu', context=IBaseSharedTool, layer=IAdminLayer,
                 manager=IContentManagementMenu, permission=VIEW_SYSTEM_PERMISSION, weight=1)
 @viewletmanager_config(name='dashboard.menu', layer=IAdminLayer, provides=IDashboardMenu)
 @implementer(IDashboardMenu)
@@ -241,7 +264,7 @@
     url = '#dashboard.html'
 
 
-@pagelet_config(name='dashboard.html', context=ISharedTool, layer=IPyAMSLayer, permission=VIEW_SYSTEM_PERMISSION)
+@pagelet_config(name='dashboard.html', context=IBaseSharedTool, layer=IPyAMSLayer, permission=VIEW_SYSTEM_PERMISSION)
 @template_config(template='templates/dashboard.pt', layer=IAdminLayer)
 class SharedToolDashboardView(InnerAdminView):
     """Shared tool dashboard view"""
@@ -262,7 +285,7 @@
         [table.update() for table in self.tables]
 
 
-@adapter_config(context=(ISharedTool, IPyAMSLayer, SharedToolDashboardView), provides=IPageHeader)
+@adapter_config(context=(IBaseSharedTool, IPyAMSLayer, SharedToolDashboardView), provides=IPageHeader)
 class SharedToolDashboardHeaderAdapter(DefaultPageHeaderAdapter):
     """Shared tool properties header adapter"""
 
@@ -284,15 +307,16 @@
     _plural_title = _("MANAGER - {0} contents waiting for your action")
 
 
-@adapter_config(context=(ISharedTool, IPyAMSLayer, SharedToolDashboardManagerWaitingTable), provides=IValues)
+@adapter_config(context=(IBaseSharedTool, IPyAMSLayer, SharedToolDashboardManagerWaitingTable), provides=IValues)
 class SharedToolDashboardManagerWaitingValues(ContextRequestViewAdapter):
     """Shared tool dashboard waiting values adapter"""
 
     @property
     def values(self):
+        intids = get_utility(IIntIds)
         catalog = get_utility(ICatalog)
         workflow = get_utility(IWorkflow, name=self.context.shared_content_workflow)
-        params = Eq(catalog['content_type'], self.context.shared_content_type) & \
+        params = Eq(catalog['parents'], intids.register(self.context)) & \
                  Any(catalog['workflow_state'], workflow.waiting_states)
         return filter(self.check_access,
                       unique(map(lambda x: sorted(IWorkflowVersions(x).get_versions(IWorkflowState(x).state),
@@ -326,15 +350,16 @@
     dt_sort_order = 'asc'
 
 
-@adapter_config(context=(ISharedTool, IPyAMSLayer, SharedToolDashboardOwnerWaitingTable), provides=IValues)
+@adapter_config(context=(IBaseSharedTool, IPyAMSLayer, SharedToolDashboardOwnerWaitingTable), provides=IValues)
 class SharedToolDashboardOwnerWaitingValues(ContextRequestViewAdapter):
     """Shared tool dashboard waiting owned contents values adapter"""
 
     @property
     def values(self):
+        intids = get_utility(IIntIds)
         catalog = get_utility(ICatalog)
         workflow = get_utility(IWorkflow, name=self.context.shared_content_workflow)
-        params = Eq(catalog['content_type'], self.context.shared_content_type) & \
+        params = Eq(catalog['parents'], intids.register(self.context)) & \
                  Any(catalog['workflow_state'], workflow.waiting_states) & \
                  Eq(catalog['workflow_principal'], self.request.principal.id)
         return unique(map(lambda x: sorted(IWorkflowVersions(x).get_versions(IWorkflowState(x).state),
@@ -361,16 +386,18 @@
             return _("CONTRIBUTOR - Last {0} modified contents")
 
 
-@adapter_config(context=(ISharedTool, IPyAMSLayer, SharedToolDashboardOwnerModifiedTable), provides=IValues)
+@adapter_config(context=(IBaseSharedTool, IPyAMSLayer, SharedToolDashboardOwnerModifiedTable), provides=IValues)
 class SharedToolDashboardOwnerModifiedValues(ContextRequestViewAdapter):
     """Shared tool dashboard waiting owned contents values adapter"""
 
     @property
     def values(self):
+        intids = get_utility(IIntIds)
         catalog = get_utility(ICatalog)
-        params = And(Eq(catalog['content_type'], self.context.shared_content_type),
-                        Or(Eq(catalog['role:owner'], self.request.principal.id),
-                           Eq(catalog['role:contributor'], self.request.principal.id)))
+        params = And(Eq(catalog['parents'], intids.register(self.context)),
+                     Any(catalog['content_type'], CONTENT_TYPES.keys()),
+                     Or(Eq(catalog['role:owner'], self.request.principal.id),
+                        Eq(catalog['role:contributor'], self.request.principal.id)))
         return unique(map(lambda x: sorted(IWorkflowVersions(x).get_versions(IWorkflowState(x).state),
                                            key=lambda y: IZopeDublinCore(y).modified, reverse=True)[0],
                           CatalogResultSet(CatalogQuery(catalog).query(params,
@@ -383,7 +410,7 @@
 # All my contents menu
 #
 
-@viewlet_config(name='my-contents.menu', context=ISharedTool, layer=IAdminLayer,
+@viewlet_config(name='my-contents.menu', context=IBaseSharedTool, layer=IAdminLayer,
                 manager=IContentManagementMenu, permission=VIEW_SYSTEM_PERMISSION, weight=5)
 @viewletmanager_config(name='my-contents.menu', layer=IAdminLayer, provides=IMyDashboardMenu)
 @implementer(IMyDashboardMenu)
@@ -400,7 +427,7 @@
 # Dashboard of favorites contents
 #
 
-@viewlet_config(name='my-favorites.menu', context=ISharedTool, layer=IAdminLayer,
+@viewlet_config(name='my-favorites.menu', context=IBaseSharedTool, layer=IAdminLayer,
                 manager=IMyDashboardMenu, permission=VIEW_SYSTEM_PERMISSION, weight=3)
 class SharedToolFavoritesMenu(MenuItem):
     """Shared tool favorites dashboard menu"""
@@ -454,15 +481,17 @@
         return ''
 
 
-@adapter_config(context=(ISharedTool, IPyAMSLayer, SharedToolFavoritesTable), provides=IValues)
+@adapter_config(context=(IBaseSharedTool, IPyAMSLayer, SharedToolFavoritesTable), provides=IValues)
 class SharedToolFavoritesValues(ContextRequestViewAdapter):
     """Shared tool favorites values adapter"""
 
     @property
     def values(self):
+        intids = get_utility(IIntIds)
         catalog = get_utility(ICatalog)
         profile = IAdminProfile(self.request.principal)
-        params = And(Eq(catalog['content_type'], self.context.shared_content_type),
+        params = And(Eq(catalog['parents'], intids.register(self.context)),
+                     Any(catalog['content_type'], CONTENT_TYPES.keys()),
                      Any(catalog['oid'], profile.favorites or ()))
         return unique(map(lambda x: IWorkflowVersions(x).get_last_versions()[0],
                       CatalogResultSet(CatalogQuery(catalog).query(params,
@@ -470,14 +499,14 @@
                                                                    reverse=True))))
 
 
-@pagelet_config(name='my-favorites.html', context=ISharedTool, layer=IPyAMSLayer, permission=VIEW_SYSTEM_PERMISSION)
+@pagelet_config(name='my-favorites.html', context=IBaseSharedTool, layer=IPyAMSLayer, permission=VIEW_SYSTEM_PERMISSION)
 class SharedToolFavoritesView(InnerAdminView, ContainerView):
     """Shared tool favorites view"""
 
     table_class = SharedToolFavoritesTable
 
 
-@adapter_config(context=(ISharedTool, IPyAMSLayer, SharedToolFavoritesView), provides=IPageHeader)
+@adapter_config(context=(IBaseSharedTool, IPyAMSLayer, SharedToolFavoritesView), provides=IPageHeader)
 class SharedToolFavoritesHeaderAdapter(DefaultPageHeaderAdapter):
     """Shared tool favorites header adapter"""
 
@@ -496,7 +525,7 @@
 # Dashboard of owned and modified contents which can be updated
 #
 
-@viewlet_config(name='my-preparations.menu', context=ISharedTool, layer=IAdminLayer,
+@viewlet_config(name='my-preparations.menu', context=IBaseSharedTool, layer=IAdminLayer,
                 manager=IMyDashboardMenu, permission=VIEW_SYSTEM_PERMISSION, weight=5)
 class SharedToolPreparationsMenu(MenuItem):
     """Shared tool preparations dashboard menu"""
@@ -514,15 +543,16 @@
     _plural_title = _("CONTRIBUTOR - {0} prepared contents")
 
 
-@adapter_config(context=(ISharedTool, IPyAMSLayer, SharedToolPreparationsTable), provides=IValues)
+@adapter_config(context=(IBaseSharedTool, IPyAMSLayer, SharedToolPreparationsTable), provides=IValues)
 class SharedToolPreparationsValues(ContextRequestViewAdapter):
     """Shared tool preparations values adapter"""
 
     @property
     def values(self):
+        intids = get_utility(IIntIds)
         catalog = get_utility(ICatalog)
         workflow = get_utility(IWorkflow, name=self.context.shared_content_workflow)
-        params = And(Eq(catalog['content_type'], self.context.shared_content_type),
+        params = And(Eq(catalog['parents'], intids.register(self.context)),
                      Or(Eq(catalog['role:owner'], self.request.principal.id),
                         Eq(catalog['role:contributor'], self.request.principal.id)),
                      Eq(catalog['workflow_state'], workflow.initial_state))
@@ -531,14 +561,14 @@
                                                                    reverse=True)))
 
 
-@pagelet_config(name='my-preparations.html', context=ISharedTool, layer=IPyAMSLayer, permission=VIEW_SYSTEM_PERMISSION)
+@pagelet_config(name='my-preparations.html', context=IBaseSharedTool, layer=IPyAMSLayer, permission=VIEW_SYSTEM_PERMISSION)
 class SharedToolPreparationsView(InnerAdminView, ContainerView):
     """Shared tool preparations view"""
 
     table_class = SharedToolPreparationsTable
 
 
-@adapter_config(context=(ISharedTool, IPyAMSLayer, SharedToolPreparationsView), provides=IPageHeader)
+@adapter_config(context=(IBaseSharedTool, IPyAMSLayer, SharedToolPreparationsView), provides=IPageHeader)
 class SharedToolPreparationsHeaderAdapter(DefaultPageHeaderAdapter):
     """Shared tool preparations header adapter"""
 
@@ -557,7 +587,7 @@
 # Dashboard of owned and modified contents which are waiting for manager action
 #
 
-@viewlet_config(name='my-submissions.menu', context=ISharedTool, layer=IAdminLayer,
+@viewlet_config(name='my-submissions.menu', context=IBaseSharedTool, layer=IAdminLayer,
                 manager=IMyDashboardMenu, permission=VIEW_SYSTEM_PERMISSION, weight=7)
 class SharedToolSubmissionsMenu(MenuItem):
     """Shared tool submissions dashboard menu"""
@@ -575,15 +605,16 @@
     _plural_title = _("CONTRIBUTOR - {0} submitted contents")
 
 
-@adapter_config(context=(ISharedTool, IPyAMSLayer, SharedToolSubmissionsTable), provides=IValues)
+@adapter_config(context=(IBaseSharedTool, IPyAMSLayer, SharedToolSubmissionsTable), provides=IValues)
 class SharedToolSubmissionsValues(ContextRequestViewAdapter):
     """Shared tool submissions values adapter"""
 
     @property
     def values(self):
+        intids = get_utility(IIntIds)
         catalog = get_utility(ICatalog)
         workflow = get_utility(IWorkflow, name=self.context.shared_content_workflow)
-        params = And(Eq(catalog['content_type'], self.context.shared_content_type),
+        params = And(Eq(catalog['parents'], intids.register(self.context)),
                      Or(Eq(catalog['role:owner'], self.request.principal.id),
                         Eq(catalog['role:contributor'], self.request.principal.id)),
                      Any(catalog['workflow_state'], workflow.waiting_states))
@@ -592,14 +623,14 @@
                                                                    reverse=True)))
 
 
-@pagelet_config(name='my-submissions.html', context=ISharedTool, layer=IPyAMSLayer, permission=VIEW_SYSTEM_PERMISSION)
+@pagelet_config(name='my-submissions.html', context=IBaseSharedTool, layer=IPyAMSLayer, permission=VIEW_SYSTEM_PERMISSION)
 class SharedToolSubmissionsView(InnerAdminView, ContainerView):
     """Shared tool submissions view"""
 
     table_class = SharedToolSubmissionsTable
 
 
-@adapter_config(context=(ISharedTool, IPyAMSLayer, SharedToolSubmissionsView), provides=IPageHeader)
+@adapter_config(context=(IBaseSharedTool, IPyAMSLayer, SharedToolSubmissionsView), provides=IPageHeader)
 class SharedToolSubmissionsHeaderAdapter(DefaultPageHeaderAdapter):
     """Shared tool submissions header adapter"""
 
@@ -618,7 +649,7 @@
 # Dashboard of owned and modified contents which are published
 #
 
-@viewlet_config(name='my-publications.menu', context=ISharedTool, layer=IAdminLayer,
+@viewlet_config(name='my-publications.menu', context=IBaseSharedTool, layer=IAdminLayer,
                 manager=IMyDashboardMenu, permission=VIEW_SYSTEM_PERMISSION, weight=10)
 class SharedToolPublicationsMenu(MenuItem):
     """Shared tool publications dashboard menu"""
@@ -636,15 +667,16 @@
     _plural_title = _("CONTRIBUTOR - {0} published contents")
 
 
-@adapter_config(context=(ISharedTool, IPyAMSLayer, SharedToolPublicationsTable), provides=IValues)
+@adapter_config(context=(IBaseSharedTool, IPyAMSLayer, SharedToolPublicationsTable), provides=IValues)
 class SharedToolPublicationsValues(ContextRequestViewAdapter):
     """Shared tool publications values adapter"""
 
     @property
     def values(self):
+        intids = get_utility(IIntIds)
         catalog = get_utility(ICatalog)
         workflow = get_utility(IWorkflow, name=self.context.shared_content_workflow)
-        params = And(Eq(catalog['content_type'], self.context.shared_content_type),
+        params = And(Eq(catalog['parents'], intids.register(self.context)),
                      Or(Eq(catalog['role:owner'], self.request.principal.id),
                         Eq(catalog['role:contributor'], self.request.principal.id)),
                      Any(catalog['workflow_state'], workflow.published_states))
@@ -653,14 +685,14 @@
                                                                    reverse=True)))
 
 
-@pagelet_config(name='my-publications.html', context=ISharedTool, layer=IPyAMSLayer, permission=VIEW_SYSTEM_PERMISSION)
+@pagelet_config(name='my-publications.html', context=IBaseSharedTool, layer=IPyAMSLayer, permission=VIEW_SYSTEM_PERMISSION)
 class SharedToolPublicationsView(InnerAdminView, ContainerView):
     """Shared tool publications view"""
 
     table_class = SharedToolPublicationsTable
 
 
-@adapter_config(context=(ISharedTool, IPyAMSLayer, SharedToolPublicationsView), provides=IPageHeader)
+@adapter_config(context=(IBaseSharedTool, IPyAMSLayer, SharedToolPublicationsView), provides=IPageHeader)
 class SharedToolPublicationsHeaderAdapter(DefaultPageHeaderAdapter):
     """Shared tool publications header adapter"""
 
@@ -679,7 +711,7 @@
 # Dashboard of owned and modified contents which are retired
 #
 
-@viewlet_config(name='my-retired-contents.menu', context=ISharedTool, layer=IAdminLayer,
+@viewlet_config(name='my-retired-contents.menu', context=IBaseSharedTool, layer=IAdminLayer,
                 manager=IMyDashboardMenu, permission=VIEW_SYSTEM_PERMISSION, weight=15)
 class SharedToolRetiredMenu(MenuItem):
     """Shared tool retired contents dashboard menu"""
@@ -697,15 +729,16 @@
     _plural_title = _("CONTRIBUTOR - {0} retired contents")
 
 
-@adapter_config(context=(ISharedTool, IPyAMSLayer, SharedToolRetiredContentsTable), provides=IValues)
+@adapter_config(context=(IBaseSharedTool, IPyAMSLayer, SharedToolRetiredContentsTable), provides=IValues)
 class SharedToolRetiredContentsValues(ContextRequestViewAdapter):
     """Shared tool retired contents values adapter"""
 
     @property
     def values(self):
+        intids = get_utility(IIntIds)
         catalog = get_utility(ICatalog)
         workflow = get_utility(IWorkflow, name=self.context.shared_content_workflow)
-        params = And(Eq(catalog['content_type'], self.context.shared_content_type),
+        params = And(Eq(catalog['parents'], intids.register(self.context)),
                      Or(Eq(catalog['role:owner'], self.request.principal.id),
                         Eq(catalog['role:contributor'], self.request.principal.id)),
                      Any(catalog['workflow_state'], workflow.retired_states))
@@ -714,7 +747,7 @@
                                                                    reverse=True)))
 
 
-@pagelet_config(name='my-retired-contents.html', context=ISharedTool, layer=IPyAMSLayer,
+@pagelet_config(name='my-retired-contents.html', context=IBaseSharedTool, layer=IPyAMSLayer,
                 permission=VIEW_SYSTEM_PERMISSION)
 class SharedToolRetiredContentsView(InnerAdminView, ContainerView):
     """Shared tool retired contents view"""
@@ -722,7 +755,7 @@
     table_class = SharedToolRetiredContentsTable
 
 
-@adapter_config(context=(ISharedTool, IPyAMSLayer, SharedToolRetiredContentsView), provides=IPageHeader)
+@adapter_config(context=(IBaseSharedTool, IPyAMSLayer, SharedToolRetiredContentsView), provides=IPageHeader)
 class SharedToolRetiredContentsHeaderAdapter(DefaultPageHeaderAdapter):
     """Shared tool retired contents header adapter"""
 
@@ -741,7 +774,7 @@
 # Dashboard of owned and modified contents which are archived
 #
 
-@viewlet_config(name='my-archived-contents.menu', context=ISharedTool, layer=IAdminLayer,
+@viewlet_config(name='my-archived-contents.menu', context=IBaseSharedTool, layer=IAdminLayer,
                 manager=IMyDashboardMenu, permission=VIEW_SYSTEM_PERMISSION, weight=20)
 class SharedToolArchivedMenu(MenuItem):
     """Shared tool archived contents dashboard menu"""
@@ -759,16 +792,17 @@
     _plural_title = _("CONTRIBUTOR - {0} archived contents")
 
 
-@adapter_config(context=(ISharedTool, IPyAMSLayer, SharedToolArchivedContentsTable), provides=IValues)
+@adapter_config(context=(IBaseSharedTool, IPyAMSLayer, SharedToolArchivedContentsTable), provides=IValues)
 class SharedToolArchivedContentsValues(ContextRequestViewAdapter):
     """Shared tool archived contents values adapter"""
 
     @property
     def values(self):
+        intids = get_utility(IIntIds)
         catalog = get_utility(ICatalog)
         principal_id = self.request.principal.id
         workflow = get_utility(IWorkflow, name=self.context.shared_content_workflow)
-        params = And(Eq(catalog['content_type'], self.context.shared_content_type),
+        params = And(Eq(catalog['parents'], intids.register(self.context)),
                      Or(Eq(catalog['role:owner'], principal_id),
                         Eq(catalog['role:contributor'], principal_id)),
                      Any(catalog['workflow_state'], workflow.archived_states))
@@ -782,7 +816,7 @@
                                                                        reverse=True))))
 
 
-@pagelet_config(name='my-archived-contents.html', context=ISharedTool, layer=IPyAMSLayer,
+@pagelet_config(name='my-archived-contents.html', context=IBaseSharedTool, layer=IPyAMSLayer,
                 permission=VIEW_SYSTEM_PERMISSION)
 class SharedToolArchivedContentsView(InnerAdminView, ContainerView):
     """Shared tool archived contents view"""
@@ -790,7 +824,7 @@
     table_class = SharedToolArchivedContentsTable
 
 
-@adapter_config(context=(ISharedTool, IPyAMSLayer, SharedToolArchivedContentsView), provides=IPageHeader)
+@adapter_config(context=(IBaseSharedTool, IPyAMSLayer, SharedToolArchivedContentsView), provides=IPageHeader)
 class SharedToolArchivedContentsHeaderAdapter(DefaultPageHeaderAdapter):
     """Shared tool archived contents header adapter"""
 
@@ -808,7 +842,7 @@
 # All interventions
 #
 
-@viewlet_config(name='all-interventions.menu', context=ISharedTool, layer=IAdminLayer,
+@viewlet_config(name='all-interventions.menu', context=IBaseSharedTool, layer=IAdminLayer,
                 manager=IContentManagementMenu, permission=VIEW_SYSTEM_PERMISSION, weight=10)
 @viewletmanager_config(name='all-interventions.menu', layer=IAdminLayer, provides=IAllContentsMenu)
 @implementer(IAllContentsMenu)
@@ -825,7 +859,7 @@
 # Dashboard of all published contents
 #
 
-@viewlet_config(name='all-publications.menu', context=ISharedTool, layer=IAdminLayer,
+@viewlet_config(name='all-publications.menu', context=IBaseSharedTool, layer=IAdminLayer,
                 manager=IAllContentsMenu, permission=VIEW_SYSTEM_PERMISSION, weight=10)
 class SharedToolAllPublicationsMenu(MenuItem):
     """Shared tool published contents dashboard menu"""
@@ -849,15 +883,16 @@
             return _("CONTRIBUTORS - Last published contents (in the limit of 50)")
 
 
-@adapter_config(context=(ISharedTool, IPyAMSLayer, SharedToolAllPublicationsTable), provides=IValues)
+@adapter_config(context=(IBaseSharedTool, IPyAMSLayer, SharedToolAllPublicationsTable), provides=IValues)
 class SharedToolAllPublicationsValues(ContextRequestViewAdapter):
     """Shared tool published contents values adapter"""
 
     @property
     def values(self):
+        intids = get_utility(IIntIds)
         catalog = get_utility(ICatalog)
         workflow = get_utility(IWorkflow, name=self.context.shared_content_workflow)
-        params = And(Eq(catalog['content_type'], self.context.shared_content_type),
+        params = And(Eq(catalog['parents'], intids.register(self.context)),
                      Any(catalog['workflow_state'], workflow.published_states))
         return unique(CatalogResultSet(CatalogQuery(catalog).query(params,
                                                                    limit=50,
@@ -865,7 +900,7 @@
                                                                    reverse=True)))
 
 
-@pagelet_config(name='all-publications.html', context=ISharedTool, layer=IPyAMSLayer,
+@pagelet_config(name='all-publications.html', context=IBaseSharedTool, layer=IPyAMSLayer,
                 permission=VIEW_SYSTEM_PERMISSION)
 class SharedToolAllPublicationsView(InnerAdminView, ContainerView):
     """Shared tool published contents view"""
@@ -873,7 +908,7 @@
     table_class = SharedToolAllPublicationsTable
 
 
-@adapter_config(context=(ISharedTool, IPyAMSLayer, SharedToolAllPublicationsView), provides=IPageHeader)
+@adapter_config(context=(IBaseSharedTool, IPyAMSLayer, SharedToolAllPublicationsView), provides=IPageHeader)
 class SharedToolAllPublicationsHeaderAdapter(DefaultPageHeaderAdapter):
     """Shared tool published contents header adapter"""
 
@@ -892,7 +927,7 @@
 # Dashboard of all updated contents
 #
 
-@viewlet_config(name='all-updates.menu', context=ISharedTool, layer=IAdminLayer,
+@viewlet_config(name='all-updates.menu', context=IBaseSharedTool, layer=IAdminLayer,
                 manager=IAllContentsMenu, permission=VIEW_SYSTEM_PERMISSION, weight=20)
 class SharedToolAllUpdatesMenu(MenuItem):
     """Shared tool updated contents dashboard menu"""
@@ -916,21 +951,23 @@
             return _("CONTRIBUTORS - Last updated contents (in the limit of 50)")
 
 
-@adapter_config(context=(ISharedTool, IPyAMSLayer, SharedToolAllUpdatesTable), provides=IValues)
+@adapter_config(context=(IBaseSharedTool, IPyAMSLayer, SharedToolAllUpdatesTable), provides=IValues)
 class SharedToolAllUpdatesValues(ContextRequestViewAdapter):
     """Shared tool updated contents values adapter"""
 
     @property
     def values(self):
+        intids = get_utility(IIntIds)
         catalog = get_utility(ICatalog)
-        params = Eq(catalog['content_type'], self.context.shared_content_type)
+        params = Eq(catalog['parents'], intids.register(self.context)) & \
+                 Any(catalog['content_type'], CONTENT_TYPES.keys())
         return unique(CatalogResultSet(CatalogQuery(catalog).query(params,
                                                                    limit=50,
                                                                    sort_index='modified_date',
                                                                    reverse=True)))
 
 
-@pagelet_config(name='all-updates.html', context=ISharedTool, layer=IPyAMSLayer,
+@pagelet_config(name='all-updates.html', context=IBaseSharedTool, layer=IPyAMSLayer,
                 permission=VIEW_SYSTEM_PERMISSION)
 class SharedToolAllUpdatesView(InnerAdminView, ContainerView):
     """Shared tool updated contents view"""
@@ -938,7 +975,7 @@
     table_class = SharedToolAllUpdatesTable
 
 
-@adapter_config(context=(ISharedTool, IPyAMSLayer, SharedToolAllUpdatesView), provides=IPageHeader)
+@adapter_config(context=(IBaseSharedTool, IPyAMSLayer, SharedToolAllUpdatesView), provides=IPageHeader)
 class SharedToolAllUpdatesHeaderAdapter(DefaultPageHeaderAdapter):
     """Shared tool updated contents header adapter"""
 
@@ -956,7 +993,7 @@
 # Advanced search access menu
 #
 
-@viewlet_config(name='advanced-search.menu', context=ISharedTool, layer=IAdminLayer,
+@viewlet_config(name='advanced-search.menu', context=IBaseSharedTool, layer=IAdminLayer,
                 manager=IAllContentsMenu, permission=VIEW_SYSTEM_PERMISSION, weight=90)
 class SharedToolAdvancedSearchMenu(MenuItem):
     """Shared tool advanced search menu"""
--- a/src/pyams_content/shared/common/zmi/header.py	Fri Nov 10 12:07:43 2017 +0100
+++ b/src/pyams_content/shared/common/zmi/header.py	Tue Nov 14 14:05:30 2017 +0100
@@ -17,7 +17,7 @@
 
 # import interfaces
 from pyams_content.profile.interfaces import IAdminProfile
-from pyams_content.shared.common.interfaces import IWfSharedContent, ISharedTool
+from pyams_content.shared.common.interfaces import IWfSharedContent, IBaseSharedTool
 from pyams_form.interfaces.form import IInnerTabForm
 from pyams_i18n.interfaces import II18n
 from pyams_security.interfaces import ISecurityManager
@@ -163,7 +163,7 @@
 
     @property
     def title(self):
-        tool = get_parent(self.context, ISharedTool)
+        tool = get_parent(self.context, IBaseSharedTool)
         return II18n(tool).query_attribute('title', request=self.request)
 
     @reify
--- a/src/pyams_content/shared/common/zmi/manager.py	Fri Nov 10 12:07:43 2017 +0100
+++ b/src/pyams_content/shared/common/zmi/manager.py	Tue Nov 14 14:05:30 2017 +0100
@@ -17,13 +17,12 @@
 
 # import interfaces
 from pyams_content.interfaces import MANAGE_TOOL_PERMISSION
-from pyams_content.shared.common.interfaces import ISharedTool
+from pyams_content.shared.common.interfaces import IBaseSharedTool
 from pyams_form.interfaces.form import IWidgetForm, IFormHelp
 from pyams_i18n.interfaces import II18n, II18nManager
 from pyams_skin.interfaces import IInnerPage, IPageHeader, IContentTitle
 from pyams_skin.interfaces.viewlet import IMenuHeader, IBreadcrumbItem
 from pyams_skin.layer import IPyAMSLayer
-from pyams_utils.interfaces import VIEW_SYSTEM_PERMISSION
 from pyams_zmi.interfaces.menu import IPropertiesMenu, ISiteManagementMenu
 from pyams_zmi.layer import IAdminLayer
 
@@ -49,7 +48,7 @@
 # Shared tools common adapters
 #
 
-@adapter_config(context=(ISharedTool, IPyAMSLayer), provides=IBreadcrumbItem)
+@adapter_config(context=(IBaseSharedTool, IPyAMSLayer), provides=IBreadcrumbItem)
 class SharedToolBreadcrumbAdapter(BreadcrumbItem):
     """Shared tool breadcrumb adapter"""
 
@@ -60,7 +59,7 @@
     css_class = 'strong'
 
 
-@adapter_config(context=(ISharedTool, IPyAMSLayer, Interface), provides=IContentTitle)
+@adapter_config(context=(IBaseSharedTool, IPyAMSLayer, Interface), provides=IContentTitle)
 class SharedToolTitleAdapter(ContextRequestViewAdapter):
     """Shared tool title adapter"""
 
@@ -69,7 +68,7 @@
         return II18n(self.context).query_attribute('title', request=self.request)
 
 
-@adapter_config(context=(ISharedTool, ISiteManagementMenu), provides=IMenuHeader)
+@adapter_config(context=(IBaseSharedTool, ISiteManagementMenu), provides=IMenuHeader)
 class SharedToolSiteManagementMenuHeader(ContextRequestAdapter):
     """Shared tool site management menu header adapter"""
 
@@ -80,7 +79,7 @@
 # Shared tool properties
 #
 
-@viewlet_config(name='properties.menu', context=ISharedTool, layer=IAdminLayer,
+@viewlet_config(name='properties.menu', context=IBaseSharedTool, layer=IAdminLayer,
                 manager=ISiteManagementMenu, permission=MANAGE_TOOL_PERMISSION, weight=1)
 @viewletmanager_config(name='properties.menu', layer=IAdminLayer, provides=IPropertiesMenu)
 @implementer(IPropertiesMenu)
@@ -92,26 +91,26 @@
     url = '#properties.html'
 
 
-@pagelet_config(name='properties.html', context=ISharedTool, layer=IPyAMSLayer, permission=MANAGE_TOOL_PERMISSION)
+@pagelet_config(name='properties.html', context=IBaseSharedTool, layer=IPyAMSLayer, permission=MANAGE_TOOL_PERMISSION)
 @implementer(IWidgetForm, IInnerPage)
 class SharedToolPropertiesEditForm(AdminEditForm):
     """Shared tool properties edit form"""
 
     legend = _("Shared tool properties")
 
-    fields = field.Fields(ISharedTool).omit('__parent__', '__name__')
+    fields = field.Fields(IBaseSharedTool).omit('__parent__', '__name__')
 
     ajax_handler = 'properties.json'
     edit_permission = MANAGE_TOOL_PERMISSION
 
 
-@view_config(name='properties.json', context=ISharedTool, request_type=IPyAMSLayer,
+@view_config(name='properties.json', context=IBaseSharedTool, request_type=IPyAMSLayer,
              permission=MANAGE_TOOL_PERMISSION, renderer='json', xhr=True)
 class SharedToolPropertiesAJAXEditForm(AJAXEditForm, SharedToolPropertiesEditForm):
     """Shared tool properties edit form, JSON renderer"""
 
 
-@adapter_config(context=(ISharedTool, IPyAMSLayer, SharedToolPropertiesEditForm), provides=IFormHelp)
+@adapter_config(context=(IBaseSharedTool, IPyAMSLayer, SharedToolPropertiesEditForm), provides=IFormHelp)
 class SharedToolPropertiesHelpAdapter(FormHelp):
     """Shared tool properties help adapter"""
 
@@ -123,7 +122,7 @@
     message_format = 'rest'
 
 
-@adapter_config(context=(ISharedTool, IPyAMSLayer, Interface), provides=IPageHeader)
+@adapter_config(context=(IBaseSharedTool, IPyAMSLayer, Interface), provides=IPageHeader)
 class SharedToolPropertiesHeaderAdapter(DefaultPageHeaderAdapter):
     """Shared tool properties header adapter"""
 
@@ -137,7 +136,7 @@
 # Shared tool languages
 #
 
-@viewlet_config(name='languages.menu', context=ISharedTool, layer=IAdminLayer,
+@viewlet_config(name='languages.menu', context=IBaseSharedTool, layer=IAdminLayer,
                 manager=ISiteManagementMenu, permission=MANAGE_TOOL_PERMISSION, weight=10)
 class SharedToolLanguagesMenu(MenuItem):
     """Shared tool languages menu"""
@@ -148,7 +147,7 @@
     url = '#languages.html'
 
 
-@pagelet_config(name='languages.html', context=ISharedTool, layer=IPyAMSLayer, permission=MANAGE_TOOL_PERMISSION)
+@pagelet_config(name='languages.html', context=IBaseSharedTool, layer=IPyAMSLayer, permission=MANAGE_TOOL_PERMISSION)
 @implementer(IInnerPage, IWidgetForm)
 class SharedToolLanguagesEditForm(AdminEditForm):
     """Shared tool languages edit form"""
@@ -160,13 +159,13 @@
     edit_permission = MANAGE_TOOL_PERMISSION
 
 
-@view_config(name='languages.json', context=ISharedTool, request_type=IPyAMSLayer,
+@view_config(name='languages.json', context=IBaseSharedTool, request_type=IPyAMSLayer,
              permission=MANAGE_TOOL_PERMISSION, renderer='json', xhr=True)
 class SharedToolLanguagesAJAXEditForm(AJAXEditForm, SharedToolLanguagesEditForm):
     """Shared tool languages edit form, JSON renderer"""
 
 
-@adapter_config(context=(ISharedTool, IPyAMSLayer, SharedToolLanguagesEditForm), provides=IFormHelp)
+@adapter_config(context=(IBaseSharedTool, IPyAMSLayer, SharedToolLanguagesEditForm), provides=IFormHelp)
 class SharedToolLanguagesEditFormHelp(FormHelp):
     """Shared tool languages edit form help"""
 
--- a/src/pyams_content/shared/common/zmi/search.py	Fri Nov 10 12:07:43 2017 +0100
+++ b/src/pyams_content/shared/common/zmi/search.py	Tue Nov 14 14:05:30 2017 +0100
@@ -18,7 +18,7 @@
 # import interfaces
 from hypatia.interfaces import ICatalog
 from pyams_content.profile.interfaces import IAdminProfile
-from pyams_content.shared.common.interfaces import ISharedTool
+from pyams_content.shared.common.interfaces import IBaseSharedTool
 from pyams_content.shared.common.interfaces.zmi import ISharedToolDashboardTable
 from pyams_i18n.interfaces import INegotiator
 from pyams_pagelet.interfaces import PageletCreatedEvent
@@ -29,10 +29,12 @@
 from pyams_workflow.interfaces import IWorkflowVersions, IWorkflow
 from z3c.table.interfaces import IValues
 from zope.dublincore.interfaces import IZopeDublinCore
+from zope.intid.interfaces import IIntIds
 
 # import packages
 from hypatia.catalog import CatalogQuery
-from hypatia.query import Eq, Contains, Ge, Le
+from hypatia.query import Eq, Contains, Ge, Le, Any
+from pyams_content.shared.common import CONTENT_TYPES
 from pyams_content.workflow import STATES_VOCABULARY
 from pyams_catalog.query import CatalogResultSet
 from pyams_form.search import SearchView, SearchForm, SearchResultsView, ISearchFields
@@ -47,8 +49,6 @@
 from pyams_utils.registry import get_utility
 from pyams_utils.url import absolute_url
 from pyams_zmi.view import AdminView
-from pyramid.decorator import reify
-from pyramid.response import Response
 from pyramid.view import view_config
 from z3c.form import field
 from zope.interface import implementer
@@ -61,7 +61,7 @@
 # Quick search adapters
 #
 
-@view_config(name='quick-search.html', context=ISharedTool, request_type=IPyAMSLayer,
+@view_config(name='quick-search.html', context=IBaseSharedTool, request_type=IPyAMSLayer,
              permission=VIEW_SYSTEM_PERMISSION, renderer='json', xhr=True)
 def shared_tool_quick_search_view(request):
     """Shared tool quick search view"""
@@ -93,14 +93,16 @@
         return attributes
 
 
-@adapter_config(context=(ISharedTool, IPyAMSLayer, SharedToolQuickSearchResults), provides=IValues)
+@adapter_config(context=(IBaseSharedTool, IPyAMSLayer, SharedToolQuickSearchResults), provides=IValues)
 class SharedToolQuickSearchValues(ContextRequestViewAdapter):
     """Shared tool quick search results view values adapter"""
 
     @property
     def values(self):
+        intids = get_utility(IIntIds)
         catalog = get_utility(ICatalog)
-        params = Eq(catalog['content_type'], self.context.shared_content_type)
+        params = Eq(catalog['parents'], intids.register(self.context)) & \
+                 Any(catalog['content_type'], CONTENT_TYPES.keys())
         query = self.request.params.get('query')
         if query:
             sequence = get_utility(ISequentialIntIds)
@@ -173,13 +175,15 @@
         return fields
 
 
-@adapter_config(context=(ISharedTool, IPyAMSLayer, SharedToolAdvancedSearchForm), provides=IContentSearch)
+@adapter_config(context=(IBaseSharedTool, IPyAMSLayer, SharedToolAdvancedSearchForm), provides=IContentSearch)
 class SharedToolAdvancedSearchFormSearchAdapter(ContextRequestViewAdapter):
     """Shared tool adavanced search form search adapter"""
 
     def get_search_results(self, data):
+        intids = get_utility(IIntIds)
         catalog = get_utility(ICatalog)
-        params = Eq(catalog['content_type'], self.context.shared_content_type)
+        params = Eq(catalog['parents'], intids.register(self.context)) & \
+                 Any(catalog['content_type'], CONTENT_TYPES.keys())
         query = data.get('query')
         if query:
             sequence = get_utility(ISequentialIntIds)
@@ -222,14 +226,15 @@
                                                                            reverse=True))))
 
 
-@pagelet_config(name='advanced-search.html', context=ISharedTool, layer=IPyAMSLayer, permission=VIEW_SYSTEM_PERMISSION)
+@pagelet_config(name='advanced-search.html', context=IBaseSharedTool, layer=IPyAMSLayer,
+                permission=VIEW_SYSTEM_PERMISSION)
 class SharedToolAdvancedSearchView(SearchView):
     """Shared tool advanced search view"""
 
     search_form_factory = SharedToolAdvancedSearchForm
 
 
-@adapter_config(context=(ISharedTool, IPyAMSLayer, SharedToolAdvancedSearchView), provides=IPageHeader)
+@adapter_config(context=(IBaseSharedTool, IPyAMSLayer, SharedToolAdvancedSearchView), provides=IPageHeader)
 class SharedToolAdvancedSearchHeaderAdapter(DefaultPageHeaderAdapter):
     """Shared tool advanced search header adapter"""
 
@@ -239,7 +244,7 @@
     icon_class = 'fa fa-fw fa-search'
 
 
-@view_config(name='advanced-search-results.html', context=ISharedTool, request_type=IPyAMSLayer,
+@view_config(name='advanced-search-results.html', context=IBaseSharedTool, request_type=IPyAMSLayer,
              permission=VIEW_SYSTEM_PERMISSION)
 @implementer(ISharedToolDashboardTable)
 class SharedToolAdvancedSearchResultsView(AdminView, SearchResultsView):
@@ -263,7 +268,7 @@
         return attributes
 
 
-@adapter_config(context=(ISharedTool, IPyAMSLayer, SharedToolAdvancedSearchResultsView), provides=IValues)
+@adapter_config(context=(IBaseSharedTool, IPyAMSLayer, SharedToolAdvancedSearchResultsView), provides=IValues)
 class SearchResultsViewValuesAdapter(ContextRequestViewAdapter):
     """Search results view values adapter"""
 
--- a/src/pyams_content/shared/common/zmi/security.py	Fri Nov 10 12:07:43 2017 +0100
+++ b/src/pyams_content/shared/common/zmi/security.py	Tue Nov 14 14:05:30 2017 +0100
@@ -17,7 +17,7 @@
 
 # import interfaces
 from pyams_content.interfaces import MANAGE_TOOL_PERMISSION
-from pyams_content.shared.common.interfaces import ISharedTool, IManagerRestrictions, IManagerRestrictionsFactory
+from pyams_content.shared.common.interfaces import IBaseSharedTool, IManagerRestrictions, IManagerRestrictionsFactory
 from pyams_security.interfaces import ISecurityManager, IPrincipalInfo
 from pyams_security.zmi.interfaces import IObjectSecurityMenu
 from pyams_skin.interfaces import IInnerPage, IPageHeader
@@ -53,7 +53,7 @@
 from pyams_content import _
 
 
-@viewlet_config(name='managers-restrictions.menu', context=ISharedTool, layer=IAdminLayer,
+@viewlet_config(name='managers-restrictions.menu', context=IBaseSharedTool, layer=IAdminLayer,
                 manager=IObjectSecurityMenu, permission=MANAGE_TOOL_PERMISSION, weight=910)
 class SharedToolManagersRestrictionsMenu(MenuItem):
     """Shared tool managers restrictions menu"""
@@ -70,7 +70,7 @@
     title = _("Content managers restrictions")
 
 
-@adapter_config(context=(ISharedTool, IAdminLayer, SharedToolManagersRestrictionsTable), provides=IValues)
+@adapter_config(context=(IBaseSharedTool, IAdminLayer, SharedToolManagersRestrictionsTable), provides=IValues)
 class SharedToolManagerRestrictionsValuesAdapter(ContextRequestViewAdapter):
     """Shared tool manager restrictions values adapter"""
 
@@ -95,7 +95,7 @@
     modal_target = True
 
 
-@adapter_config(name='name', context=(ISharedTool, IAdminLayer, SharedToolManagersRestrictionsTable), provides=IColumn)
+@adapter_config(name='name', context=(IBaseSharedTool, IAdminLayer, SharedToolManagersRestrictionsTable), provides=IColumn)
 class SharedToolManagerRestrictionsNameColumn(I18nColumn, GetAttrColumn):
     """Shared tool manager restrictions name column"""
 
@@ -104,7 +104,7 @@
     weight = 10
 
 
-@adapter_config(name='restricted', context=(ISharedTool, IAdminLayer, SharedToolManagersRestrictionsTable),
+@adapter_config(name='restricted', context=(IBaseSharedTool, IAdminLayer, SharedToolManagersRestrictionsTable),
                 provides=IColumn)
 class SharedToolManagerRestrictionsEnabledColumn(I18nColumn, GetAttrColumn):
     """Shared tool manager enabled restrictions column"""
@@ -121,7 +121,7 @@
             return '--'
 
 
-@adapter_config(name='owners', context=(ISharedTool, IAdminLayer, SharedToolManagersRestrictionsTable),
+@adapter_config(name='owners', context=(IBaseSharedTool, IAdminLayer, SharedToolManagersRestrictionsTable),
                 provides=IColumn)
 class SharedToolManagerRestrictionsOwnersColumn(I18nColumn, GetAttrColumn):
     """Shared tool manager owners restrictions column"""
@@ -138,7 +138,7 @@
             return '--'
 
 
-@pagelet_config(name='managers-restrictions.html', context=ISharedTool, layer=IPyAMSLayer,
+@pagelet_config(name='managers-restrictions.html', context=IBaseSharedTool, layer=IPyAMSLayer,
                 permission=MANAGE_TOOL_PERMISSION)
 @implementer(IInnerPage)
 class SharedToolManagersRestrictionsView(AdminView, ContainerView):
@@ -147,7 +147,7 @@
     table_class = SharedToolManagersRestrictionsTable
 
 
-@adapter_config(context=(ISharedTool, IAdminLayer, SharedToolManagersRestrictionsView), provides=IPageHeader)
+@adapter_config(context=(IBaseSharedTool, IAdminLayer, SharedToolManagersRestrictionsView), provides=IPageHeader)
 class SharedToolManagersRestrictionsHeaderAdapter(DefaultPageHeaderAdapter):
     """Shared tool managers restrictions header adapter"""
 
@@ -161,7 +161,7 @@
 # Manager restrictions edit form
 #
 
-@pagelet_config(name='manager-restrictions.html', context=ISharedTool, layer=IPyAMSLayer,
+@pagelet_config(name='manager-restrictions.html', context=IBaseSharedTool, layer=IPyAMSLayer,
                 permission=MANAGE_TOOL_PERMISSION)
 class SharedToolManagerRestrictionsEditForm(AdminDialogEditForm):
     """Shared tool manager restrictions edit form"""
@@ -225,7 +225,7 @@
         self.widgets['principal_id'].mode = HIDDEN_MODE
 
 
-@view_config(name='manager-restrictions.json', context=ISharedTool, request_type=IPyAMSLayer,
+@view_config(name='manager-restrictions.json', context=IBaseSharedTool, request_type=IPyAMSLayer,
              permission=MANAGE_TOOL_PERMISSION, renderer='json', xhr=True)
 class SharedToolManagerRestrictionsAJAXEditForm(AJAXEditForm, SharedToolManagerRestrictionsEditForm):
     """Shared tool manager restrictions edit form, JSON renderer"""
--- a/src/pyams_content/shared/common/zmi/summary.py	Fri Nov 10 12:07:43 2017 +0100
+++ b/src/pyams_content/shared/common/zmi/summary.py	Tue Nov 14 14:05:30 2017 +0100
@@ -17,7 +17,7 @@
 from datetime import datetime
 
 # import interfaces
-from pyams_content.shared.common.interfaces import IWfSharedContent, IWfSharedContentRoles, ISharedTool
+from pyams_content.shared.common.interfaces import IWfSharedContent, IWfSharedContentRoles, IBaseSharedTool
 from pyams_form.interfaces.form import IWidgetForm, IInnerTabForm, IInnerSubForm
 from pyams_skin.interfaces import IInnerPage
 from pyams_skin.layer import IPyAMSLayer
@@ -37,7 +37,6 @@
 from pyams_viewlet.viewlet import contentprovider_config
 from pyams_zmi.form import AdminDisplayForm, InnerAdminDisplayForm, InnerAdminAddForm
 from z3c.form import field
-from z3c.form.browser.checkbox import SingleCheckBoxFieldWidget
 from zope.interface import implementer, Interface
 
 from pyams_content import _
@@ -61,7 +60,7 @@
 
     @property
     def back_url(self):
-        tool = get_parent(self.context, ISharedTool)
+        tool = get_parent(self.context, IBaseSharedTool)
         return absolute_url(tool, self.request, 'admin')
 
 
--- a/src/pyams_content/shared/common/zmi/workflow.py	Fri Nov 10 12:07:43 2017 +0100
+++ b/src/pyams_content/shared/common/zmi/workflow.py	Tue Nov 14 14:05:30 2017 +0100
@@ -18,7 +18,7 @@
 
 # import interfaces
 from pyams_content.interfaces import PUBLISH_CONTENT_PERMISSION, CREATE_CONTENT_PERMISSION, MANAGE_CONTENT_PERMISSION
-from pyams_content.shared.common.interfaces import IWfSharedContent, ISharedTool, ISharedContent
+from pyams_content.shared.common.interfaces import IWfSharedContent, IBaseSharedTool, ISharedContent
 from pyams_form.interfaces.form import IWidgetsPrefixViewletsManager, IFormSuffixViewletsManager
 from pyams_security.interfaces import ISecurityManager
 from pyams_skin.layer import IPyAMSLayer
@@ -762,7 +762,7 @@
         state = IWorkflowState(self.context)
         if state.version_id == 1:  # remove the first and only version => remove all
             content = get_parent(self.context, ISharedContent)
-            self.__target = get_parent(content, ISharedTool)
+            self.__target = get_parent(content, IBaseSharedTool)
             del content.__parent__[content.__name__]
         else:
             versions = IWorkflowVersions(self.context)
--- a/src/pyams_content/shared/site/__init__.py	Fri Nov 10 12:07:43 2017 +0100
+++ b/src/pyams_content/shared/site/__init__.py	Tue Nov 14 14:05:30 2017 +0100
@@ -16,31 +16,30 @@
 # import standard library
 
 # import interfaces
-from pyams_content.shared.common.interfaces import ISharedSite
-from pyams_i18n.interfaces import II18n
+from pyams_content.component.paragraph.interfaces import IParagraphContainerTarget
+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.site.interfaces import IWfTopic, TOPIC_CONTENT_TYPE, TOPIC_CONTENT_NAME, ITopic
 
 # import packages
-from pyams_utils.container import BTreeOrderedContainer
-from pyams_utils.registry import get_utilities_for
-from pyams_utils.request import query_request
-from pyams_utils.vocabulary import vocabulary_config
+from pyams_content.shared.common import register_content_type, WfSharedContent, SharedContent
 from zope.interface import implementer
-from zope.schema.vocabulary import SimpleTerm, SimpleVocabulary
 
 
-@implementer(ISharedSite)
-class SharedSite(BTreeOrderedContainer):
-    """Shared site persistent class"""
+@implementer(IWfTopic, IParagraphContainerTarget, IThemesTarget,
+             IPreviewTarget, IReviewTarget)
+class WfTopic(WfSharedContent):
+    """Base topic"""
+
+    content_type = TOPIC_CONTENT_TYPE
+    content_name = TOPIC_CONTENT_NAME
+
+register_content_type(WfTopic)
 
 
-@vocabulary_config(name='PyAMS shared sites')
-class SharedSiteVocabulary(SimpleVocabulary):
-    """Shared sites vocabulary"""
-
-    interface = ISharedSite
+@implementer(ITopic)
+class Topic(SharedContent):
+    """WOrkflow managed topic class"""
 
-    def __init__(self, context):
-        request = query_request()
-        super(SharedSiteVocabulary, self).__init__([SimpleTerm(v, title=II18n(t).query_attribute('title',
-                                                                                                 request=request))
-                                                    for v, t in get_utilities_for(self.interface)])
+    content_class = WfTopic
--- a/src/pyams_content/shared/site/interfaces/__init__.py	Fri Nov 10 12:07:43 2017 +0100
+++ b/src/pyams_content/shared/site/interfaces/__init__.py	Tue Nov 14 14:05:30 2017 +0100
@@ -16,5 +16,48 @@
 # import standard library
 
 # import interfaces
+from pyams_content.interfaces import IBaseContent
+from zope.container.interfaces import IContainer
 
 # import packages
+from pyams_content.shared.common.interfaces import ISharedSite, IWfSharedContent, ISharedContent
+from zope.container.constraints import containers, contains
+
+from pyams_content import _
+
+
+class ISiteElement(IBaseContent):
+    """Base site element interface"""
+
+    containers('.ISiteContainer')
+
+
+class ISiteContainer(IBaseContent, IContainer):
+    """Base site container interface"""
+
+    contains(ISiteElement)
+
+
+class ISiteFolder(ISiteElement, ISiteContainer):
+    """Site folder interface
+
+    A site folder is made to contain sub-folders and topics
+    """
+
+
+class ISiteManager(ISharedSite, ISiteContainer):
+    """Site manager interface"""
+
+    contains(ISiteElement)
+
+
+TOPIC_CONTENT_TYPE = 'topic'
+TOPIC_CONTENT_NAME = _("Topic")
+
+
+class IWfTopic(IWfSharedContent):
+    """Topic interface"""
+
+
+class ITopic(ISharedContent, ISiteElement):
+    """Workflow managed topic interface"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/shared/site/manager.py	Tue Nov 14 14:05:30 2017 +0100
@@ -0,0 +1,78 @@
+#
+# 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
+from pyams_content.component.paragraph.interfaces import IParagraphFactorySettings
+from pyams_content.component.theme.interfaces import IThemesManagerTarget
+from pyams_content.shared.site.interfaces import ISiteManager
+from pyams_i18n.interfaces import II18n
+from pyams_portal.interfaces import IPortalContext
+from zope.annotation.interfaces import IAttributeAnnotatable
+from zope.component.interfaces import ISite
+from zope.lifecycleevent.interfaces import IObjectAddedEvent, IObjectRemovedEvent
+
+# import packages
+from pyams_content.shared.common.manager import BaseSharedTool
+from pyams_skin.skin import UserSkinnableContent
+from pyams_utils.registry import get_utilities_for
+from pyams_utils.request import query_request
+from pyams_utils.traversing import get_parent
+from pyams_utils.vocabulary import vocabulary_config
+from pyramid.events import subscriber
+from zope.container.ordered import OrderedContainer
+from zope.interface import implementer
+from zope.schema.fieldproperty import FieldProperty
+from zope.schema.vocabulary import SimpleTerm, SimpleVocabulary
+
+
+@implementer(ISiteManager, IParagraphFactorySettings, IThemesManagerTarget, IAttributeAnnotatable, IPortalContext)
+class SiteManager(OrderedContainer, BaseSharedTool, UserSkinnableContent):
+    """Site manager persistent class"""
+
+    allowed_paragraphs = FieldProperty(IParagraphFactorySettings['allowed_paragraphs'])
+    auto_created_paragraphs = FieldProperty(IParagraphFactorySettings['auto_created_paragraphs'])
+
+
+@subscriber(IObjectAddedEvent, context_selector=ISiteManager)
+def handle_added_site_manager(event):
+    """Register site manager when added"""
+    site = get_parent(event.newParent, ISite)
+    registry = site.getSiteManager()
+    if registry is not None:
+        registry.registerUtility(event.object, ISiteManager, name=event.object.__name__)
+
+
+@subscriber(IObjectRemovedEvent, context_selector=ISiteManager)
+def handle_deleted_site_manager(event):
+    """Un-register site manager when deleted"""
+    site = get_parent(event.oldParent, ISite)
+    registry = site.getSiteManager()
+    if registry is not None:
+        registry.unregisterUtility(event.object, ISiteManager, name=event.object.__name__)
+
+
+@vocabulary_config(name='PyAMS site managers')
+class SiteManagerVocabulary(SimpleVocabulary):
+    """Site manager vocabulary"""
+
+    interface = ISiteManager
+
+    def __init__(self, context):
+        request = query_request()
+        super(SiteManagerVocabulary, self).__init__([SimpleTerm(v, title=II18n(t).query_attribute('title',
+                                                                                                  request=request))
+                                                    for v, t in get_utilities_for(self.interface)])
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/shared/site/zmi/manager.py	Tue Nov 14 14:05:30 2017 +0100
@@ -0,0 +1,127 @@
+#
+# 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
+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.zmi.interfaces import 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.layer import IPyAMSLayer
+from pyams_zmi.layer import IAdminLayer
+from z3c.form.interfaces import IDataExtractedEvent
+
+# import packages
+from pyams_content.shared.site.manager import SiteManager
+from pyams_content.shared.zmi.sites import SiteTreeTable
+from pyams_form.form import AJAXAddForm
+from pyams_pagelet.pagelet import pagelet_config
+from pyams_skin.table import DefaultElementEditorAdapter
+from pyams_skin.viewlet.toolbar import ToolbarMenuItem
+from pyams_utils.adapter import adapter_config, ContextRequestAdapter
+from pyams_utils.registry import query_utility
+from pyams_utils.unicode import translate_string
+from pyams_utils.url import absolute_url
+from pyams_viewlet.viewlet import viewlet_config
+from pyams_zmi.form import AdminDialogAddForm
+from pyramid.events import subscriber
+from pyramid.view import view_config
+from z3c.form import field
+from zope.interface import Invalid
+
+from pyams_content import _
+
+
+@adapter_config(context=(ISiteManager, IAdminLayer), provides=IUserAddingsMenuLabel)
+class SiteManageruserAddingsMenuLabelAdapter(ContextRequestAdapter):
+    """Site manager user addings menu label adapter"""
+
+    @property
+    def label(self):
+        return '{content} ({blog})'.format(
+            content=self.request.localizer.translate(self.context.shared_content_factory.content_class.content_name),
+            blog=II18n(self.context).query_attribute('title', request=self.request))
+
+
+@viewlet_config(name='add-site-manager.menu', context=ISiteRoot, layer=IAdminLayer,
+                view=SiteTreeTable, manager=IToolbarAddingMenu, permission=MANAGE_SITE_ROOT_PERMISSION)
+class SiteManagerAddMenu(ToolbarMenuItem):
+    """Site manager add menu"""
+
+    label = _("Add site manager")
+    label_css_class = 'fa fa-fw fa-sitemap'
+    url = 'add-site-manager.html'
+    modal_target = True
+
+
+@pagelet_config(name='add-site-manager.html', context=ISiteRoot, layer=IPyAMSLayer,
+                permission=MANAGE_SITE_ROOT_PERMISSION)
+class SiteManagerAddForm(AdminDialogAddForm):
+    """Site manager add form"""
+
+    title = _("Site manager")
+    legend = _("Add site manager")
+    icon_css_class = 'fa fa-fw fa-sitemap'
+
+    fields = field.Fields(ISiteManager).select('title', 'short_name')
+    ajax_handler = 'add-site-manager.json'
+    edit_permission = None
+
+    def create(self, data):
+        return SiteManager()
+
+    def add(self, object):
+        short_name = II18n(object).query_attribute('short_name', request=self.request)
+        name = translate_string(short_name, force_lower=True, spaces='-')
+        self.context[name] = object
+
+    def nextURL(self):
+        return absolute_url(self.context, self.request, 'site-tree.html')
+
+
+@subscriber(IDataExtractedEvent, form_selector=SiteManagerAddForm)
+def handle_new_site_manager_data_extraction(event):
+    """Handle new site manager data extraction"""
+    container = event.form.context
+    negotiator = query_utility(INegotiator)
+    short_name = event.data['short_name'].get(negotiator.server_language)
+    if not short_name:
+        event.form.widgets.errors += (Invalid(_("You must provide a short name for default server language!")),)
+        return
+    name = translate_string(short_name, force_lower=True, spaces='-')
+    if name in container:
+        event.form.widgets.errors += (Invalid(_("Specified site manager name is already used!")),)
+        return
+    site = query_utility(ISiteManager, name=short_name)
+    if site is not None:
+        event.form.widgets.errors += (Invalid(_("A site manager is already registered with this name!!")),)
+
+
+@view_config(name='add-site-manager.json', context=ISiteRoot, request_type=IPyAMSLayer,
+             permission=MANAGE_SITE_ROOT_PERMISSION, renderer='json', xhr=True)
+class SiteManagerAJAXAddForm(AJAXAddForm, SiteManagerAddForm):
+    """Site manager add form, JSOn renderer"""
+
+
+@adapter_config(context=(ISiteManager, IAdminLayer, SiteTreeTable), provides=ITableElementEditor)
+class SiteManagerTableElementEditor(DefaultElementEditorAdapter):
+    """Site tree table element editor"""
+
+    view_name = 'admin'
+    modal_target = False
--- a/src/pyams_content/shared/zmi/sites.py	Fri Nov 10 12:07:43 2017 +0100
+++ b/src/pyams_content/shared/zmi/sites.py	Tue Nov 14 14:05:30 2017 +0100
@@ -16,7 +16,7 @@
 # import standard library
 
 # import interfaces
-from pyams_content.shared.common.interfaces import ISharedSite, ISharedTool
+from pyams_content.shared.common.interfaces import ISharedSite, IBaseSharedTool
 from pyams_content.shared.common.interfaces.zmi import IDashboardTable
 from pyams_content.shared.zmi.interfaces import ISiteTreeMenu
 from pyams_skin.interfaces import IInnerPage, IPageHeader
@@ -91,7 +91,7 @@
     @property
     def values(self):
         return [value for value in self.context.values()
-                if ISharedTool.providedBy(value) or ISharedSite.providedBy(value)]
+                if IBaseSharedTool.providedBy(value) or ISharedSite.providedBy(value)]
 
 
 @pagelet_config(name='site-tree.html', context=ISite, layer=IPyAMSLayer, permission=VIEW_SYSTEM_PERMISSION)
--- a/src/pyams_content/workflow/task.py	Fri Nov 10 12:07:43 2017 +0100
+++ b/src/pyams_content/workflow/task.py	Tue Nov 14 14:05:30 2017 +0100
@@ -18,11 +18,12 @@
 
 # import interfaces
 from hypatia.interfaces import ICatalog
-from pyams_content.shared.common.interfaces import ISharedTool
+from pyams_content.shared.common.interfaces import IBaseSharedTool
 from pyams_content.workflow.interfaces import IContentArchiverTask
 from pyams_i18n.interfaces import II18n
 from pyams_sequence.interfaces import ISequentialIdInfo
 from pyams_workflow.interfaces import IWorkflow, IWorkflowInfo
+from zope.intid.interfaces import IIntIds
 
 # import packages
 from hypatia.catalog import CatalogQuery
@@ -46,12 +47,13 @@
     def run(self, report):
         request = check_request()
         translate = request.localizer.translate
+        intids = get_utility(IIntIds)
         catalog = get_utility(ICatalog)
         now = gmtime(datetime.utcnow())
         has_retired = False
-        for tool in get_all_utilities_registered_for(ISharedTool):
+        for tool in get_all_utilities_registered_for(IBaseSharedTool):
             workflow = IWorkflow(tool)
-            params = Eq(catalog['content_type'], tool.shared_content_type) & \
+            params = Eq(catalog['parents'], intids.register(tool)) & \
                      Any(catalog['workflow_state'], workflow.published_states) & \
                      Lt(catalog['expiration_date'], now)
             for content in CatalogResultSet(CatalogQuery(catalog).query(params)):
--- a/src/pyams_content/zmi/viewlet/toplinks/__init__.py	Fri Nov 10 12:07:43 2017 +0100
+++ b/src/pyams_content/zmi/viewlet/toplinks/__init__.py	Tue Nov 14 14:05:30 2017 +0100
@@ -17,7 +17,7 @@
 
 # import interfaces
 from hypatia.interfaces import ICatalog
-from pyams_content.shared.common.interfaces import ISharedTool, ISharedSite
+from pyams_content.shared.common.interfaces import IBaseSharedTool, ISharedSite
 from pyams_content.zmi.interfaces import IUserAddingsMenuLabel
 from pyams_i18n.interfaces import II18n
 from pyams_skin.interfaces.viewlet import ITopLinksViewletManager
@@ -68,7 +68,7 @@
     def update(self):
         super(SharedToolsMenu, self).update()
         registry = get_local_registry()
-        for tool in sorted(registry.getAllUtilitiesRegisteredFor(ISharedTool),
+        for tool in sorted(registry.getAllUtilitiesRegisteredFor(IBaseSharedTool),
                            key=lambda x: II18n(x).query_attribute('title', request=self.request) or ''):
             if ISharedSite.providedBy(tool):
                 continue