???
authorThierry Florac <thierry.florac@onf.fr>
Wed, 27 Jun 2018 16:42:01 +0200
changeset 771 d8261b628787
parent 766 37891d35596b (diff)
parent 770 5ee242d90312 (current diff)
child 772 fc8fe2dede63
???
--- a/.hgtags	Wed Jun 27 16:34:12 2018 +0200
+++ b/.hgtags	Wed Jun 27 16:42:01 2018 +0200
@@ -15,3 +15,6 @@
 d73905f6a2eb2949b3979671bf261965473bb7b9 0.1.11
 5d94baef6923642af27a9f30f377d45f67f04bbf 0.1.12
 cf2d19055dd754ce3ed30be24f83dae351b3ae5c 0.1.13
+95026785904d443509d93895ddf761b7967d9d46 0.1.14
+95026785904d443509d93895ddf761b7967d9d46 0.1.14
+1978e4dad1d8f950411807ed2df23fd030a39b60 0.1.14
--- a/buildout.cfg	Wed Jun 27 16:34:12 2018 +0200
+++ b/buildout.cfg	Wed Jun 27 16:42:01 2018 +0200
@@ -7,12 +7,6 @@
 show-picked-versions = true
 newest = false
 
-allow-hosts =
-    bitbucket.org
-    *.python.org
-    *.sourceforge.net
-    github.com
-
 versions = versions
 newest = false
 #allow-picked-versions = false
@@ -92,4 +86,4 @@
 eggs = pyams_content [test]
 
 [versions]
-pyams_content = 0.1.14
+pyams_content = 0.1.15
--- a/docs/HISTORY.txt	Wed Jun 27 16:34:12 2018 +0200
+++ b/docs/HISTORY.txt	Wed Jun 27 16:42:01 2018 +0200
@@ -1,6 +1,24 @@
 History
 =======
 
+0.1.15
+------
+ - added "basic" and "navigation" illustration components
+ - added pictogram selection widget
+ - added optional pictogram to links
+ - added generic menu feature
+ - added key numbers portlet
+ - added site container "rename" view to change object URL
+ - include quick search results table in main dashboard view
+
+0.1.14
+------
+ - added header and footer management features
+ - added renderer for each kind of paragraphs
+ - added preview for a each paragraph one by one
+ - added optional checks before publishing content
+ - updated groups management in forms
+
 0.1.13
 ------
  - renamed factories to use snake_case on functions
--- a/setup.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/setup.py	Wed Jun 27 16:42:01 2018 +0200
@@ -22,7 +22,7 @@
 README = os.path.join(DOCS, 'README.txt')
 HISTORY = os.path.join(DOCS, 'HISTORY.txt')
 
-version = '0.1.14'
+version = '0.1.15'
 long_description = open(README).read() + '\n\n' + open(HISTORY).read()
 
 tests_require = []
--- a/src/pyams_content.egg-info/PKG-INFO	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content.egg-info/PKG-INFO	Wed Jun 27 16:42:01 2018 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: pyams-content
-Version: 0.1.13
+Version: 0.1.15
 Summary: PyAMS base content interfaces and classes
 Home-page: http://hg.ztfy.org/pyams/pyams_content
 Author: Thierry Florac
@@ -73,6 +73,21 @@
         History
         =======
         
+        0.1.15
+        ------
+         - added "basic" illustration component
+         - added pictogram selection widget
+         - added optional pictogram to links
+         - added generic menu feature
+        
+        0.1.14
+        ------
+         - added header and footer management features
+         - added renderer for each kind of paragraphs
+         - added preview for a each paragraph one by one
+         - added optional checks before publishing content
+         - updated groups management in forms
+        
         0.1.13
         ------
          - renamed factories to use snake_case on functions
--- a/src/pyams_content.egg-info/SOURCES.txt	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content.egg-info/SOURCES.txt	Wed Jun 27 16:42:01 2018 +0200
@@ -43,6 +43,13 @@
 src/pyams_content/component/illustration/zmi/paragraph.py
 src/pyams_content/component/illustration/zmi/templates/illustration-thumbnail.pt
 src/pyams_content/component/illustration/zmi/templates/paragraph-illustration-icon.pt
+src/pyams_content/component/keynumber/__init__.py
+src/pyams_content/component/keynumber/interfaces/__init__.py
+src/pyams_content/component/keynumber/portlet/__init__.py
+src/pyams_content/component/keynumber/portlet/interfaces/__init__.py
+src/pyams_content/component/keynumber/portlet/zmi/__init__.py
+src/pyams_content/component/keynumber/portlet/zmi/templates/keynumber-preview.pt
+src/pyams_content/component/keynumber/zmi/__init__.py
 src/pyams_content/component/links/__init__.py
 src/pyams_content/component/links/interfaces/__init__.py
 src/pyams_content/component/links/zmi/__init__.py
@@ -50,6 +57,7 @@
 src/pyams_content/component/links/zmi/reverse.py
 src/pyams_content/component/media/__init__.py
 src/pyams_content/component/paragraph/__init__.py
+src/pyams_content/component/paragraph/audio.py
 src/pyams_content/component/paragraph/contact.py
 src/pyams_content/component/paragraph/container.py
 src/pyams_content/component/paragraph/frame.py
@@ -62,6 +70,7 @@
 src/pyams_content/component/paragraph/verbatim.py
 src/pyams_content/component/paragraph/video.py
 src/pyams_content/component/paragraph/interfaces/__init__.py
+src/pyams_content/component/paragraph/interfaces/audio.py
 src/pyams_content/component/paragraph/interfaces/contact.py
 src/pyams_content/component/paragraph/interfaces/frame.py
 src/pyams_content/component/paragraph/interfaces/header.py
@@ -73,6 +82,7 @@
 src/pyams_content/component/paragraph/interfaces/verbatim.py
 src/pyams_content/component/paragraph/interfaces/video.py
 src/pyams_content/component/paragraph/zmi/__init__.py
+src/pyams_content/component/paragraph/zmi/audio.py
 src/pyams_content/component/paragraph/zmi/contact.py
 src/pyams_content/component/paragraph/zmi/container.py
 src/pyams_content/component/paragraph/zmi/frame.py
@@ -87,10 +97,8 @@
 src/pyams_content/component/paragraph/zmi/verbatim.py
 src/pyams_content/component/paragraph/zmi/video.py
 src/pyams_content/component/paragraph/zmi/templates/associations.pt
-src/pyams_content/component/paragraph/zmi/templates/html-render.pt
 src/pyams_content/component/paragraph/zmi/templates/paragraph-title-icon.pt
 src/pyams_content/component/paragraph/zmi/templates/paragraph-title-toolbar.pt
-src/pyams_content/component/paragraph/zmi/templates/raw-render.pt
 src/pyams_content/component/theme/__init__.py
 src/pyams_content/component/theme/portlet.py
 src/pyams_content/component/theme/interfaces/__init__.py
@@ -113,7 +121,6 @@
 src/pyams_content/component/video/provider/zmi/templates/youtube-render.pt
 src/pyams_content/component/video/zmi/__init__.py
 src/pyams_content/component/video/zmi/paragraph.py
-src/pyams_content/component/video/zmi/templates/video-render.pt
 src/pyams_content/component/video/zmi/templates/video-settings.pt
 src/pyams_content/doctests/README.txt
 src/pyams_content/features/__init__.py
@@ -125,6 +132,33 @@
 src/pyams_content/features/checker/__init__.py
 src/pyams_content/features/checker/interfaces.py
 src/pyams_content/features/checker/zmi/__init__.py
+src/pyams_content/features/footer/__init__.py
+src/pyams_content/features/footer/interfaces/__init__.py
+src/pyams_content/features/footer/skin/__init__.py
+src/pyams_content/features/footer/zmi/__init__.py
+src/pyams_content/features/footer/zmi/__init__.py.orig
+src/pyams_content/features/footer/zmi/templates/renderer-settings.pt
+src/pyams_content/features/header/__init__.py
+src/pyams_content/features/header/interfaces/__init__.py
+src/pyams_content/features/header/skin/__init__.py
+src/pyams_content/features/header/zmi/__init__.py
+src/pyams_content/features/header/zmi/templates/renderer-settings.pt
+src/pyams_content/features/menu/__init__.py
+src/pyams_content/features/menu/interfaces/__init__.py
+src/pyams_content/features/menu/portlet/__init__.py
+src/pyams_content/features/menu/portlet/navigation/__init__.py
+src/pyams_content/features/menu/portlet/navigation/double.py
+src/pyams_content/features/menu/portlet/navigation/simple.py
+src/pyams_content/features/menu/portlet/navigation/interfaces/__init__.py
+src/pyams_content/features/menu/portlet/navigation/interfaces/double.py
+src/pyams_content/features/menu/portlet/navigation/interfaces/simple.py
+src/pyams_content/features/menu/portlet/navigation/zmi/__init__.py
+src/pyams_content/features/menu/portlet/navigation/zmi/double.py
+src/pyams_content/features/menu/portlet/navigation/zmi/simple.py
+src/pyams_content/features/menu/portlet/navigation/zmi/templates/double-preview.pt
+src/pyams_content/features/menu/portlet/navigation/zmi/templates/simple-preview.pt
+src/pyams_content/features/menu/zmi/__init__.py
+src/pyams_content/features/menu/zmi/templates/menu-name-cell.pt
 src/pyams_content/features/preview/__init__.py
 src/pyams_content/features/preview/interfaces.py
 src/pyams_content/features/preview/zmi/__init__.py
@@ -132,6 +166,7 @@
 src/pyams_content/features/preview/zmi/templates/preview.pt
 src/pyams_content/features/renderer/__init__.py
 src/pyams_content/features/renderer/interfaces/__init__.py
+src/pyams_content/features/renderer/skin/__init__.py
 src/pyams_content/features/renderer/zmi/__init__.py
 src/pyams_content/features/renderer/zmi/widget.py
 src/pyams_content/features/renderer/zmi/templates/renderer-input.pt
@@ -159,7 +194,9 @@
 src/pyams_content/reference/pictograms/interfaces/__init__.py
 src/pyams_content/reference/pictograms/zmi/__init__.py
 src/pyams_content/reference/pictograms/zmi/manager.py
+src/pyams_content/reference/pictograms/zmi/widget.py
 src/pyams_content/reference/pictograms/zmi/templates/manager-selection.pt
+src/pyams_content/reference/pictograms/zmi/templates/pictogram-header.pt
 src/pyams_content/reference/zmi/__init__.py
 src/pyams_content/reference/zmi/table.py
 src/pyams_content/root/__init__.py
@@ -179,17 +216,25 @@
 src/pyams_content/shared/blog/zmi/manager.py
 src/pyams_content/shared/common/__init__.py
 src/pyams_content/shared/common/manager.py
+src/pyams_content/shared/common/portal.py
 src/pyams_content/shared/common/security.py
 src/pyams_content/shared/common/types.py
 src/pyams_content/shared/common/interfaces/__init__.py
 src/pyams_content/shared/common/interfaces/types.py
 src/pyams_content/shared/common/interfaces/zmi.py
+src/pyams_content/shared/common/portlet/__init__.py
+src/pyams_content/shared/common/portlet/content/__init__.py
+src/pyams_content/shared/common/portlet/content/interfaces/__init__.py
+src/pyams_content/shared/common/portlet/content/skin/__init__.py
+src/pyams_content/shared/common/portlet/content/zmi/__init__.py
+src/pyams_content/shared/common/portlet/content/zmi/preview.pt
 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/i18n.py
 src/pyams_content/shared/common/zmi/manager.py
 src/pyams_content/shared/common/zmi/owner.py
+src/pyams_content/shared/common/zmi/portal.py
 src/pyams_content/shared/common/zmi/properties.py
 src/pyams_content/shared/common/zmi/search.py
 src/pyams_content/shared/common/zmi/security.py
@@ -198,8 +243,10 @@
 src/pyams_content/shared/common/zmi/types.py
 src/pyams_content/shared/common/zmi/workflow.py
 src/pyams_content/shared/common/zmi/templates/advanced-search.pt
+src/pyams_content/shared/common/zmi/templates/check-input.pt
 src/pyams_content/shared/common/zmi/templates/dashboard.pt
 src/pyams_content/shared/common/zmi/templates/header.pt
+src/pyams_content/shared/common/zmi/templates/preview-input.pt
 src/pyams_content/shared/common/zmi/templates/wf-archive-message.pt
 src/pyams_content/shared/common/zmi/templates/wf-archiving-message.pt
 src/pyams_content/shared/common/zmi/templates/wf-cancel-archiving-message.pt
@@ -236,12 +283,9 @@
 src/pyams_content/shared/imagemap/zmi/container.py
 src/pyams_content/shared/imagemap/zmi/paragraph.py
 src/pyams_content/shared/imagemap/zmi/properties.py
-src/pyams_content/shared/imagemap/zmi/render.py
 src/pyams_content/shared/imagemap/zmi/widget.py
 src/pyams_content/shared/imagemap/zmi/templates/container.pt
 src/pyams_content/shared/imagemap/zmi/templates/imagemap-preview.pt
-src/pyams_content/shared/imagemap/zmi/templates/paragraph-render.pt
-src/pyams_content/shared/imagemap/zmi/templates/render.pt
 src/pyams_content/shared/imagemap/zmi/templates/widget-input.pt
 src/pyams_content/shared/logo/__init__.py
 src/pyams_content/shared/logo/manager.py
@@ -275,16 +319,12 @@
 src/pyams_content/shared/view/interfaces/__init__.py
 src/pyams_content/shared/view/portlet/__init__.py
 src/pyams_content/shared/view/portlet/interfaces.py
-src/pyams_content/shared/view/portlet/templates/view-items-list.pt
-src/pyams_content/shared/view/portlet/templates/view-with-images-list.pt
 src/pyams_content/shared/view/portlet/zmi/__init__.py
 src/pyams_content/shared/view/portlet/zmi/templates/view-items-list-preview.pt
 src/pyams_content/shared/view/zmi/__init__.py
 src/pyams_content/shared/view/zmi/properties.py
 src/pyams_content/shared/view/zmi/reference.py
-src/pyams_content/shared/view/zmi/render.py
 src/pyams_content/shared/view/zmi/theme.py
-src/pyams_content/shared/view/zmi/templates/render.pt
 src/pyams_content/skin/__init__.py
 src/pyams_content/skin/routes.py
 src/pyams_content/skin/resources/css/pyams_content.css
--- a/src/pyams_content.egg-info/requires.txt	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content.egg-info/requires.txt	Wed Jun 27 16:42:01 2018 +0200
@@ -4,7 +4,7 @@
 persistent
 pyams_catalog
 pyams_file
-pyams_form
+pyams_form>=0.1.13
 pyams_i18n
 pyams_mail
 pyams_pagelet
@@ -14,7 +14,7 @@
 pyams_skin
 pyams_template
 pyams_thesaurus
-pyams_utils
+pyams_utils>=0.1.15
 pyams_viewlet
 pyams_workflow
 pyams_zmi
--- a/src/pyams_content/__init__.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -27,7 +27,7 @@
         CREATE_CONTENT_PERMISSION, MANAGE_CONTENT_PERMISSION, COMMENT_CONTENT_PERMISSION, PUBLISH_CONTENT_PERMISSION
     from pyams_content.interfaces import WEBMASTER_ROLE, PILOT_ROLE, MANAGER_ROLE, OWNER_ROLE, CONTRIBUTOR_ROLE, \
         READER_ROLE, OPERATOR_ROLE, GUEST_ROLE
-    from pyams_security.interfaces import ADMIN_USER_ID
+    from pyams_security.interfaces import ADMIN_USER_ID, SYSTEM_ADMIN_ROLE
     from pyams_thesaurus.interfaces import CREATE_THESAURUS_PERMISSION, ADMIN_THESAURUS_PERMISSION
     from pyams_utils.interfaces import PUBLIC_PERMISSION, VIEW_PERMISSION, MANAGE_PERMISSION, \
         VIEW_SYSTEM_PERMISSION, MANAGE_ROLES_PERMISSION, MANAGE_SKIN_PERMISSION
@@ -57,7 +57,9 @@
                                           MANAGE_SITE_ROOT_PERMISSION, MANAGE_SITE_PERMISSION, MANAGE_TOOL_PERMISSION,
                                           CREATE_CONTENT_PERMISSION, MANAGE_CONTENT_PERMISSION, MANAGE_SKIN_PERMISSION,
                                           COMMENT_CONTENT_PERMISSION, PUBLISH_CONTENT_PERMISSION},
-                          'managers': {ADMIN_USER_ID, 'role:system.Manager', 'role:pyams.Webmaster'}})
+                          'managers': {ADMIN_USER_ID,
+                                       'role:{0}'.format(SYSTEM_ADMIN_ROLE),
+                                       'role:{0}'.format(WEBMASTER_ROLE)}})
     config.register_role({'id': PILOT_ROLE,
                           'title': _("Pilot (role)"),
                           'permissions': {PUBLIC_PERMISSION, VIEW_PERMISSION, MANAGE_PERMISSION,
@@ -65,13 +67,17 @@
                                           MANAGE_SITE_PERMISSION, MANAGE_TOOL_PERMISSION,
                                           MANAGE_CONTENT_PERMISSION, COMMENT_CONTENT_PERMISSION,
                                           PUBLISH_CONTENT_PERMISSION},
-                          'managers': {ADMIN_USER_ID, 'role:system.Manager', 'role:pyams.Webmaster'}})
+                          'managers': {ADMIN_USER_ID,
+                                       'role:{0}'.format(SYSTEM_ADMIN_ROLE),
+                                       'role:{0}'.format(WEBMASTER_ROLE)}})
     config.register_role({'id': MANAGER_ROLE,
                           'title': _("Manager (role)"),
                           'permissions': {PUBLIC_PERMISSION, VIEW_PERMISSION, MANAGE_PERMISSION,
                                           VIEW_SYSTEM_PERMISSION, MANAGE_CONTENT_PERMISSION,
                                           COMMENT_CONTENT_PERMISSION, PUBLISH_CONTENT_PERMISSION},
-                          'managers': {ADMIN_USER_ID, 'role:system.Manager', 'role:pyams.Webmaster',
+                          'managers': {ADMIN_USER_ID,
+                                       'role:{0}'.format(SYSTEM_ADMIN_ROLE),
+                                       'role:{0}'.format(WEBMASTER_ROLE),
                                        'role:pyams.Pilot'}})
     config.register_role({'id': OWNER_ROLE,
                           'title': _("Owner (role)"),
@@ -84,20 +90,32 @@
                                           VIEW_SYSTEM_PERMISSION,
                                           CREATE_CONTENT_PERMISSION, MANAGE_CONTENT_PERMISSION,
                                           COMMENT_CONTENT_PERMISSION},
-                          'managers': {ADMIN_USER_ID, 'role:system.Manager', 'role:pyams.Webmaster',
-                                       'role:pyams.Pilot', 'role:pyams.Owner'}})
+                          'managers': {ADMIN_USER_ID,
+                                       'role:{0}'.format(SYSTEM_ADMIN_ROLE),
+                                       'role:{0}'.format(WEBMASTER_ROLE),
+                                       'role:pyams.Pilot',
+                                       'role:pyams.Owner'}})
     config.register_role({'id': READER_ROLE,
                           'title': _("Reader (role)"),
                           'permissions': {PUBLIC_PERMISSION, VIEW_PERMISSION, MANAGE_PERMISSION,
                                           VIEW_SYSTEM_PERMISSION, COMMENT_CONTENT_PERMISSION},
-                          'managers': {ADMIN_USER_ID, 'role:system.Manager', 'role:pyams.Webmaster',
-                                       'role:pyams.Pilot', 'role:pyams.Manager', 'role:pyams.Contributor'}})
+                          'managers': {ADMIN_USER_ID,
+                                       'role:{0}'.format(SYSTEM_ADMIN_ROLE),
+                                       'role:{0}'.format(WEBMASTER_ROLE),
+                                       'role:pyams.Pilot',
+                                       'role:pyams.Manager',
+                                       'role:pyams.Contributor'}})
     config.register_role({'id': OPERATOR_ROLE,
                           'title': _("Operator (role)"),
                           'permissions': {PUBLIC_PERMISSION, VIEW_PERMISSION, VIEW_SYSTEM_PERMISSION},
-                          'managers': {ADMIN_USER_ID, 'role:system.Manager'}})
+                          'managers': {ADMIN_USER_ID,
+                                       'role:{0}'.format(SYSTEM_ADMIN_ROLE)}})
     config.register_role({'id': GUEST_ROLE,
                           'title': _("Guest user (role)"),
                           'permissions': {PUBLIC_PERMISSION, VIEW_PERMISSION},
-                          'managers': {ADMIN_USER_ID, 'role:system.Manager', 'role:pyams.Webmaster',
-                                       'role:pyams.Pilot', 'role:pyams.Manager', 'role:pyams.Contributor'}})
+                          'managers': {ADMIN_USER_ID,
+                                       'role:{0}'.format(SYSTEM_ADMIN_ROLE),
+                                       'role:{0}'.format(WEBMASTER_ROLE),
+                                       'role:pyams.Pilot',
+                                       'role:pyams.Manager',
+                                       'role:pyams.Contributor'}})
--- a/src/pyams_content/component/association/__init__.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/component/association/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -16,7 +16,7 @@
 # import standard library
 
 # import interfaces
-from pyams_content.component.association.interfaces import IAssociationItem, IAssociationTarget
+from pyams_content.component.association.interfaces import IAssociationItem, IAssociationContainerTarget
 from pyams_form.interfaces.form import IFormContextPermissionChecker
 from zope.lifecycleevent.interfaces import IObjectAddedEvent, IObjectModifiedEvent, IObjectRemovedEvent
 
@@ -52,14 +52,14 @@
 
     @property
     def edit_permission(self):
-        content = get_parent(self.context, IAssociationTarget)
+        content = get_parent(self.context, IAssociationContainerTarget)
         return IFormContextPermissionChecker(content).edit_permission
 
 
 @subscriber(IObjectAddedEvent, context_selector=IAssociationItem)
 def handle_added_association(event):
     """Handle added association item"""
-    content = get_parent(event.object, IAssociationTarget)
+    content = get_parent(event.object, IAssociationContainerTarget)
     if content is not None:
         get_current_registry().notify(ObjectModifiedEvent(content))
 
@@ -67,7 +67,7 @@
 @subscriber(IObjectModifiedEvent, context_selector=IAssociationItem)
 def handle_modified_association(event):
     """Handle modified association item"""
-    content = get_parent(event.object, IAssociationTarget)
+    content = get_parent(event.object, IAssociationContainerTarget)
     if content is not None:
         get_current_registry().notify(ObjectModifiedEvent(content))
 
@@ -75,6 +75,6 @@
 @subscriber(IObjectRemovedEvent, context_selector=IAssociationItem)
 def handle_removed_association(event):
     """Handle removed association item"""
-    content = get_parent(event.object, IAssociationTarget)
+    content = get_parent(event.object, IAssociationContainerTarget)
     if content is not None:
         get_current_registry().notify(ObjectModifiedEvent(content))
--- a/src/pyams_content/component/association/container.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/component/association/container.py	Wed Jun 27 16:42:01 2018 +0200
@@ -16,7 +16,7 @@
 # import standard library
 
 # import interfaces
-from pyams_content.component.association.interfaces import IAssociationContainer, IAssociationTarget, \
+from pyams_content.component.association.interfaces import IAssociationContainer, IAssociationContainerTarget, \
     ASSOCIATION_CONTAINER_KEY, IAssociationItem, IAssociationInfo
 from pyams_content.features.checker.interfaces import IContentChecker
 from zope.location.interfaces import ISublocations
@@ -58,13 +58,13 @@
         return filter(lambda x: IAssociationItem(x).visible, self.values())
 
 
-@adapter_config(context=IAssociationTarget, provides=IAssociationContainer)
+@adapter_config(context=IAssociationContainerTarget, provides=IAssociationContainer)
 def association_container_factory(target):
     """Associations container factory"""
     return get_annotation_adapter(target, ASSOCIATION_CONTAINER_KEY, AssociationContainer, name='++ass++')
 
 
-@adapter_config(name='ass', context=IAssociationTarget, provides=ITraversable)
+@adapter_config(name='ass', context=IAssociationContainerTarget, provides=ITraversable)
 class AssociationContainerNamespace(ContextAdapter):
     """Associations container ++ass++ namespace"""
 
@@ -73,7 +73,7 @@
         return registry.queryAdapter(self.context, IAssociationContainer, name=name or '')
 
 
-@adapter_config(name='associations', context=IAssociationTarget, provides=ISublocations)
+@adapter_config(name='associations', context=IAssociationContainerTarget, provides=ISublocations)
 class AssociationContainerSublocations(ContextAdapter):
     """Associations container sub-locations adapter"""
 
@@ -81,7 +81,7 @@
         return IAssociationContainer(self.context).values()
 
 
-@adapter_config(name='associations', context=IAssociationTarget, provides=IContentChecker)
+@adapter_config(name='associations', context=IAssociationContainerTarget, provides=IContentChecker)
 class AssociationsContentChecker(BaseContentChecker):
     """Associations content checker"""
 
@@ -110,7 +110,7 @@
 
     def __init__(self, context=None):
         terms = []
-        target = get_parent(context, IAssociationTarget)
+        target = get_parent(context, IAssociationContainerTarget)
         if target is not None:
             terms = [SimpleTerm(link.__name__, title=IAssociationInfo(link).inner_title)
                      for link in IAssociationContainer(target).values()]
--- a/src/pyams_content/component/association/interfaces/__init__.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/component/association/interfaces/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -74,7 +74,7 @@
         """Get list of visible items"""
 
 
-class IAssociationTarget(IAttributeAnnotatable):
+class IAssociationContainerTarget(IAttributeAnnotatable):
     """Associations container target interface"""
 
 
@@ -82,6 +82,10 @@
     """Association renderer adapter interface"""
 
 
+#
+# Associations paragraph
+#
+
 ASSOCIATION_PARAGRAPH_TYPE = 'Associations'
 ASSOCIATION_PARAGRAPH_NAME = _("Associations")
 ASSOCIATION_PARAGRAPH_RENDERERS = 'PyAMS.associations.renderers'
--- a/src/pyams_content/component/association/zmi/__init__.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/component/association/zmi/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -17,7 +17,8 @@
 import json
 
 # import interfaces
-from pyams_content.component.association.interfaces import IAssociationTarget, IAssociationContainer, IAssociationInfo
+from pyams_content.component.association.interfaces import IAssociationContainerTarget, IAssociationContainer, \
+    IAssociationInfo
 from pyams_content.component.association.zmi.interfaces import IAssociationsParentForm, IAssociationsView
 from pyams_content.interfaces import MANAGE_CONTENT_PERMISSION
 from pyams_form.interfaces.form import IInnerSubForm
@@ -73,7 +74,7 @@
     """Association item properties edit form, JSON renderer"""
 
     def get_associations_table(self):
-        target = get_parent(self.context, IAssociationTarget)
+        target = get_parent(self.context, IAssociationContainerTarget)
         return {
             'status': 'success',
             'message': self.request.localizer.translate(self.successMessage),
@@ -87,7 +88,7 @@
 # Content associations view
 #
 
-@viewlet_config(name='associations.menu', context=IAssociationTarget, layer=IPyAMSLayer,
+@viewlet_config(name='associations.menu', context=IAssociationContainerTarget, layer=IPyAMSLayer,
                 manager=IPropertiesMenu, permission=VIEW_SYSTEM_PERMISSION, weight=60)
 class AssociationsMenu(MenuItem):
     """Associations menu"""
@@ -117,16 +118,15 @@
     @property
     def data_attributes(self):
         registry = get_current_registry()
-        target = get_parent(self.context, IAssociationTarget)
+        target = get_parent(self.context, IAssociationContainerTarget)
         container = registry.getAdapter(target, IAssociationContainer, name=self.associations_name)
         attributes = super(AssociationsTable, self).data_attributes
-        attributes['table'] = {
-            'id': self.id,
+        attributes.setdefault('table', {}).update({
             'data-ams-location': absolute_url(container, self.request),
             'data-ams-tablednd-drag-handle': 'td.sorter',
             'data-ams-tablednd-drop-target': 'set-associations-order.json',
             'data-ams-visibility-switcher': 'switch-association-visibility.json'
-        }
+        })
         return attributes
 
     @reify
@@ -134,7 +134,7 @@
         return list(super(AssociationsTable, self).values)
 
 
-@adapter_config(context=(IAssociationTarget, IPyAMSLayer, AssociationsTable), provides=IValues)
+@adapter_config(context=(IAssociationContainerTarget, IPyAMSLayer, AssociationsTable), provides=IValues)
 class AssociationsTableValuesAdapter(ContextRequestViewAdapter):
     """Associations table values adapter"""
 
@@ -144,7 +144,7 @@
         return registry.getAdapter(self.context, IAssociationContainer, name=self.view.associations_name).values()
 
 
-@adapter_config(name='sorter', context=(IAssociationTarget, IPyAMSLayer, AssociationsTable), provides=IColumn)
+@adapter_config(name='sorter', context=(IAssociationContainerTarget, IPyAMSLayer, AssociationsTable), provides=IColumn)
 @adapter_config(name='sorter', context=(IAssociationContainer, IPyAMSLayer, AssociationsTable), provides=IColumn)
 class AssociationsTableSorterColumn(ProtectedFormObjectMixin, SorterColumn):
     """Associations table sorter column"""
@@ -159,7 +159,7 @@
     return {'status': 'success'}
 
 
-@adapter_config(name='show-hide', context=(IAssociationTarget, IPyAMSLayer, AssociationsTable),
+@adapter_config(name='show-hide', context=(IAssociationContainerTarget, IPyAMSLayer, AssociationsTable),
                 provides=IColumn)
 @adapter_config(name='show-hide', context=(IAssociationContainer, IPyAMSLayer, AssociationsTable),
                 provides=IColumn)
@@ -174,7 +174,7 @@
     return switch_element_visibility(request, IAssociationContainer)
 
 
-@adapter_config(name='pictogram', context=(IAssociationTarget, IPyAMSLayer, AssociationsTable), provides=IColumn)
+@adapter_config(name='pictogram', context=(IAssociationContainerTarget, IPyAMSLayer, AssociationsTable), provides=IColumn)
 @adapter_config(name='pictogram', context=(IAssociationContainer, IPyAMSLayer, AssociationsTable), provides=IColumn)
 class AssociationsTablePictogramColumn(ImageColumn):
     """Associations table pictogram column"""
@@ -190,7 +190,7 @@
         return self.request.localizer.translate(item.icon_hint)
 
 
-@adapter_config(name='name', context=(IAssociationTarget, IPyAMSLayer, AssociationsTable), provides=IColumn)
+@adapter_config(name='name', context=(IAssociationContainerTarget, IPyAMSLayer, AssociationsTable), provides=IColumn)
 @adapter_config(name='name', context=(IAssociationContainer, IPyAMSLayer, AssociationsTable), provides=IColumn)
 class AssociationsTablePublicNameColumn(NameColumn):
     """Associations table name column"""
@@ -208,7 +208,7 @@
         return title
 
 
-@adapter_config(name='inner_name', context=(IAssociationTarget, IPyAMSLayer, AssociationsTable), provides=IColumn)
+@adapter_config(name='inner_name', context=(IAssociationContainerTarget, IPyAMSLayer, AssociationsTable), provides=IColumn)
 @adapter_config(name='inner_name', context=(IAssociationContainer, IPyAMSLayer, AssociationsTable), provides=IColumn)
 class AssociationsTableInnerNameColumn(I18nColumn, GetAttrColumn):
     """Associations table inner name column"""
@@ -224,7 +224,7 @@
             return '--'
 
 
-@adapter_config(name='size', context=(IAssociationTarget, IPyAMSLayer, AssociationsTable), provides=IColumn)
+@adapter_config(name='size', context=(IAssociationContainerTarget, IPyAMSLayer, AssociationsTable), provides=IColumn)
 @adapter_config(name='size', context=(IAssociationContainer, IPyAMSLayer, AssociationsTable), provides=IColumn)
 class AssociationsTableSizeColumn(I18nColumn, GetAttrColumn):
     """Associations table size column"""
@@ -240,7 +240,7 @@
             return '--'
 
 
-@adapter_config(name='trash', context=(IAssociationTarget, IPyAMSLayer, AssociationsTable), provides=IColumn)
+@adapter_config(name='trash', context=(IAssociationContainerTarget, IPyAMSLayer, AssociationsTable), provides=IColumn)
 @adapter_config(name='trash', context=(IAssociationContainer, IPyAMSLayer, AssociationsTable), provides=IColumn)
 class AssociationsTableTrashColumn(ProtectedFormObjectMixin, TrashColumn):
     """Associations table trash column"""
@@ -253,7 +253,7 @@
     output = delete_container_element(request)
     if output.get('status') == 'success':
         from pyams_content.component.paragraph.zmi import get_json_paragraph_toolbar_refresh_event
-        parent = get_parent(request.context, IAssociationTarget)
+        parent = get_parent(request.context, IAssociationContainerTarget)
         output.update({
             'handle_json': True,
             'events': [
@@ -264,7 +264,7 @@
     return output
 
 
-@pagelet_config(name='associations.html', context=IAssociationTarget, layer=IPyAMSLayer,
+@pagelet_config(name='associations.html', context=IAssociationContainerTarget, layer=IPyAMSLayer,
                 permission=VIEW_SYSTEM_PERMISSION)
 @implementer(IAssociationsView)
 class AssociationsContainerView(ContainerAdminView, Pagelet):
@@ -274,7 +274,7 @@
     table_class = AssociationsTable
 
 
-@pagelet_config(name='associations-dialog.html', context=IAssociationTarget, layer=IPyAMSLayer,
+@pagelet_config(name='associations-dialog.html', context=IAssociationContainerTarget, layer=IPyAMSLayer,
                 permission=VIEW_SYSTEM_PERMISSION)
 @implementer(IAssociationsParentForm)
 class AssociationsContainerDialogView(AdminDialogDisplayForm):
@@ -287,7 +287,7 @@
     fields = field.Fields(Interface)
 
 
-@adapter_config(name='associations', context=(IAssociationTarget, IPyAMSLayer, IAssociationsParentForm),
+@adapter_config(name='associations', context=(IAssociationContainerTarget, IPyAMSLayer, IAssociationsParentForm),
                 provides=IInnerSubForm)
 @implementer(IAssociationsView)
 class AssociationsView(InnerTableView):
--- a/src/pyams_content/component/extfile/interfaces/__init__.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/component/extfile/interfaces/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -16,7 +16,7 @@
 # import standard library
 
 # import interfaces
-from pyams_content.component.association.interfaces import IAssociationItem, IAssociationTarget
+from pyams_content.component.association.interfaces import IAssociationItem, IAssociationContainerTarget
 
 # import packages
 from pyams_i18n.schema import I18nTextLineField, I18nTextField, I18nFileField, I18nThumbnailImageField, \
@@ -91,5 +91,5 @@
                           required=True)
 
 
-class IExtFileContainerTarget(IAssociationTarget):
+class IExtFileContainerTarget(IAssociationContainerTarget):
     """External files container marker interface"""
--- a/src/pyams_content/component/extfile/zmi/container.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/component/extfile/zmi/container.py	Wed Jun 27 16:42:01 2018 +0200
@@ -16,7 +16,8 @@
 # import standard library
 
 # import interfaces
-from pyams_content.component.association.interfaces import IAssociationTarget, IAssociationContainer, IAssociationInfo
+from pyams_content.component.association.interfaces import IAssociationContainerTarget, IAssociationContainer, \
+    IAssociationInfo
 from pyams_content.component.extfile.interfaces import IExtFile, IExtImage
 from pyams_i18n.interfaces import II18n
 from pyams_skin.layer import IPyAMSLayer
@@ -37,7 +38,7 @@
 def get_files_list(request):
     """Get container files in JSON format for TinyMCE editor"""
     result = []
-    target = get_parent(request.context, IAssociationTarget)
+    target = get_parent(request.context, IAssociationContainerTarget)
     if target is not None:
         container = IAssociationContainer(target)
         result.extend([{'title': IAssociationInfo(item).user_title,
@@ -52,7 +53,7 @@
 def get_images_list(request):
     """Get container images in JSON format for TinyMCE editor"""
     result = []
-    target = get_parent(request.context, IAssociationTarget)
+    target = get_parent(request.context, IAssociationContainerTarget)
     if target is not None:
         container = IAssociationContainer(target)
         result.extend([{'title': IAssociationInfo(item).user_title,
--- a/src/pyams_content/component/gallery/interfaces/__init__.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/component/gallery/interfaces/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -93,7 +93,7 @@
 
     title = I18nTextLineField(title=_("Title"),
                               description=_("Gallery title, as shown in front-office"),
-                              required=True)
+                              required=False)
 
     description = I18nTextField(title=_("Description"),
                                 description=_("Gallery description displayed by front-office template"),
--- a/src/pyams_content/component/illustration/__init__.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/component/illustration/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -17,7 +17,8 @@
 
 # import interfaces
 from pyams_content.component.illustration.interfaces import IIllustration, IIllustrationTarget, \
-    ILLUSTRATION_KEY, ILLUSTRATION_RENDERERS
+    ILLUSTRATION_KEY, ILLUSTRATION_RENDERERS, IBasicIllustration, IBasicIllustrationTarget, BASIC_ILLUSTRATION_KEY, \
+    ILinkIllustrationTarget, LINK_ILLUSTRATION_KEY, ILinkIllustration
 from pyams_content.features.checker.interfaces import IContentChecker, MISSING_VALUE, MISSING_LANG_VALUE
 from pyams_file.interfaces import IFileInfo, IImage, IResponsiveImage
 from pyams_i18n.interfaces import INegotiator, II18n, II18nManager
@@ -46,17 +47,14 @@
 from pyams_content import _
 
 
-@implementer(IIllustration)
-@factory_config(provided=IIllustration)
-class Illustration(RenderedContentMixin, Persistent, Contained):
+@implementer(IBasicIllustration)
+@factory_config(provided=IBasicIllustration)
+class BasicIllustration(Persistent, Contained):
     """Illustration persistent class"""
 
     _data = I18nFileProperty(IIllustration['data'])
     title = FieldProperty(IIllustration['title'])
     alt_title = FieldProperty(IIllustration['alt_title'])
-    description = FieldProperty(IIllustration['description'])
-    author = FieldProperty(IIllustration['author'])
-    renderer = FieldProperty(IIllustration['renderer'])
 
     @property
     def data(self):
@@ -69,6 +67,36 @@
             if IImage.providedBy(data):
                 alsoProvides(data, IResponsiveImage)
 
+    def has_data(self):
+        if not self._data:
+            return False
+        for data in self._data.values():
+            if bool(data):
+                return True
+        return False
+
+
+@adapter_config(context=IBasicIllustrationTarget, provides=IIllustration)
+def basic_illustration_factory(context):
+    """Basic illustration factory"""
+
+    def illustration_callback(illustration):
+        get_current_registry().notify(ObjectAddedEvent(illustration, context, illustration.__name__))
+
+    return get_annotation_adapter(context, BASIC_ILLUSTRATION_KEY, BasicIllustration,
+                                  name='++illustration++',
+                                  callback=illustration_callback)
+
+
+@implementer(IIllustration)
+@factory_config(provided=IIllustration)
+class Illustration(RenderedContentMixin, BasicIllustration):
+    """Illustration persistent class"""
+
+    description = FieldProperty(IIllustration['description'])
+    author = FieldProperty(IIllustration['author'])
+    renderer = FieldProperty(IIllustration['renderer'])
+
 
 @adapter_config(context=IIllustrationTarget, provides=IIllustration)
 def illustration_factory(context):
@@ -82,6 +110,20 @@
                                   callback=illustration_callback)
 
 
+@adapter_config(context=ILinkIllustrationTarget, provides=ILinkIllustration)
+@adapter_config(name='link', context=ILinkIllustrationTarget, provides=IIllustration)
+def link_illustration_factory(context):
+    """Link illustration factory"""
+
+    def illustration_callback(illustration):
+        get_current_registry().notify(ObjectAddedEvent(illustration, context, illustration.__name__))
+
+    return get_annotation_adapter(context, LINK_ILLUSTRATION_KEY, BasicIllustration,
+                                  markers=ILinkIllustration,
+                                  name='++illustration++link',
+                                  callback=illustration_callback)
+
+
 def update_illustration_properties(illustration):
     """Update missing file properties"""
     request = check_request()
@@ -95,21 +137,21 @@
             info.description = II18n(illustration).get_attribute('alt_title', lang, request)
 
 
-@subscriber(IObjectAddedEvent, context_selector=IIllustration)
+@subscriber(IObjectAddedEvent, context_selector=IBasicIllustration)
 def handle_added_illustration(event):
     """Handle added illustration"""
     illustration = event.object
     update_illustration_properties(illustration)
 
 
-@subscriber(IObjectModifiedEvent, context_selector=IIllustration)
+@subscriber(IObjectModifiedEvent, context_selector=IBasicIllustration)
 def handle_modified_illustration(event):
     """Handle modified illustration"""
     illustration = event.object
     update_illustration_properties(illustration)
 
 
-@adapter_config(name='illustration', context=IIllustrationTarget, provides=ITraversable)
+@adapter_config(name='illustration', context=IBasicIllustrationTarget, provides=ITraversable)
 class IllustrationNamespace(ContextAdapter):
     """++illustration++ namespace adapter"""
 
@@ -118,12 +160,14 @@
         return registry.queryAdapter(self.context, IIllustration, name=name)
 
 
-@adapter_config(name='illustration', context=IIllustrationTarget, provides=ISublocations)
+@adapter_config(name='illustration', context=IBasicIllustrationTarget, provides=ISublocations)
 class IllustrationSublocations(ContextAdapter):
     """Illustration sub-locations adapter"""
 
     def sublocations(self):
-        return IIllustration(self.context),
+        registry = get_global_registry()
+        for name, adapter in registry.getAdapters((self, ), IBasicIllustration):
+            yield adapter
 
 
 @adapter_config(context=IIllustration, provides=IContentChecker)
--- a/src/pyams_content/component/illustration/interfaces/__init__.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/component/illustration/interfaces/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -22,6 +22,7 @@
 from zope.annotation.interfaces import IAttributeAnnotatable
 
 # import packages
+from zope.interface import Interface
 from zope.schema import Choice, TextLine
 
 from pyams_content import _
@@ -31,17 +32,24 @@
 # Illustration
 #
 
+BASIC_ILLUSTRATION_KEY = 'pyams_content.illustration.base'
+
 ILLUSTRATION_KEY = 'pyams_content.illustration'
 ILLUSTRATION_RENDERERS = 'PyAMS.illustration.renderers'
 
+LINK_ILLUSTRATION_KEY = '{0}::link'.format(ILLUSTRATION_KEY)
 
-class IIllustration(IRenderedContent):
-    """Illustration paragraph"""
+
+class IBasicIllustration(Interface):
+    """Basic illustration interface"""
 
     data = I18nThumbnailMediaField(title=_("Image or video data"),
                                    description=_("Image or video content"),
                                    required=False)
 
+    def has_data(self):
+        """Check if data is provided in any language"""
+
     title = I18nTextLineField(title=_("Legend"),
                               required=False)
 
@@ -49,6 +57,10 @@
                                   description=_("Alternate title used to describe image content"),
                                   required=False)
 
+
+class IIllustration(IBasicIllustration, IRenderedContent):
+    """Illustration paragraph"""
+
     description = I18nTextField(title=_("Description"),
                                 description=_(""),
                                 required=False)
@@ -63,9 +75,25 @@
                       default='default')
 
 
-class IIllustrationTarget(IAttributeAnnotatable):
-    """Illustration target marker interface"""
+class ILinkIllustration(IBasicIllustration):
+    """Navigation link illustration interface"""
+
+
+class IBasicIllustrationTarget(IAttributeAnnotatable):
+    """Basic illustration target marker interface"""
+
 
+class IIllustrationTarget(IBasicIllustrationTarget):
+    """Illustration target interface"""
+
+
+class ILinkIllustrationTarget(IBasicIllustrationTarget):
+    """Link illustration target interface"""
+
+
+#
+# Illustration paragraph
+#
 
 ILLUSTRATION_PARAGRAPH_TYPE = 'Illustration'
 ILLUSTRATION_PARAGRAPH_NAME = _("Illustration")
--- a/src/pyams_content/component/illustration/zmi/__init__.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/component/illustration/zmi/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -16,16 +16,16 @@
 # import standard library
 
 # import interfaces
-from pyams_content.component.illustration.interfaces import IIllustration, IIllustrationTarget
+from pyams_content.component.illustration.interfaces import IBasicIllustration, IBasicIllustrationTarget, \
+    IIllustration, IIllustrationTarget, ILinkIllustrationTarget
 from pyams_content.component.paragraph import IBaseParagraph
-from pyams_content.component.paragraph.zmi.interfaces import IParagraphContainerTable, IParagraphTitleToolbar
 from pyams_form.interfaces.form import IInnerSubForm, IWidgetsPrefixViewletsManager
 from pyams_skin.layer import IPyAMSLayer
-from pyams_utils.interfaces import VIEW_SYSTEM_PERMISSION
 from pyams_zmi.interfaces import IPropertiesEditForm
 from transaction.interfaces import ITransactionManager
 
 # import packages
+from pyams_content.component.illustration.zmi.paragraph import ParagraphContainerIllustrationMarker
 from pyams_content.component.paragraph.zmi import get_json_paragraph_markers_refresh_event
 from pyams_content.features.renderer.zmi.widget import RendererFieldWidget
 from pyams_skin.event import get_json_form_refresh_event, get_json_widget_refresh_event
@@ -42,38 +42,57 @@
 # Illustration properties inner edit form
 #
 
-@viewlet_config(name='illustration', context=IIllustrationTarget, layer=IPyAMSLayer, view=IParagraphContainerTable,
-                manager=IParagraphTitleToolbar, permission=VIEW_SYSTEM_PERMISSION, weight=5)
-@template_config(template='templates/paragraph-illustration-icon.pt', layer=IPyAMSLayer)
-class ParagraphContainerIllustrationMarker(Viewlet):
-    """Paragraph container illustration marker column"""
+@adapter_config(name='illustration', context=(IBasicIllustrationTarget, IPyAMSLayer, IPropertiesEditForm),
+                provides=IInnerSubForm)
+class BasicIllustrationPropertiesInnerEditForm(InnerAdminEditForm):
+    """Basic illustration properties inner edit form"""
+
+    prefix = 'basic_illustration_form.'
+
+    css_class = 'form-group'
+    padding_class = ''
+    fieldset_class = 'bordered margin-top-10 padding-y-5'
+
+    legend = _("Illustration")
+    legend_class = 'illustration switcher no-y-padding padding-right-10'
+
+    fields = field.Fields(IBasicIllustration).omit('__parent__', '__name__')
+
+    weight = 10
 
-    weight = 5
-    action_class = 'action illustration nowrap width-40'
-    icon_class = 'fa fa-fw fa-picture-o'
-    icon_hint = _("Illustration")
+    def getContent(self):
+        return IIllustration(self.context)
 
-    marker_type = 'illustration'
+    def check_mode(self):
+        if self.parent_form is not None:
+            self.mode = self.parent_form.mode
 
-    def render(self):
-        illustration = IIllustration(self.context, None)
-        if illustration and illustration.data:
-            for value in illustration.data.values():
-                if value:
-                    return super(ParagraphContainerIllustrationMarker, self).render()
-        return ''
+    @property
+    def switcher_state(self):
+        content = self.getContent()
+        if content.has_data():
+            return 'open'
+
+    def get_ajax_output(self, changes):
+        output = super(BasicIllustrationPropertiesInnerEditForm, self).get_ajax_output(changes)
+        updated = changes.get(IBasicIllustration, ())
+        if 'data' in updated:
+            # we have to commit transaction to be able to handle blobs...
+            ITransactionManager(self.context).get().commit()
+            output.setdefault('events', []).append(
+                get_json_form_refresh_event(self.context, self.request, self.__class__))
+        return output
 
 
 @adapter_config(name='illustration', context=(IIllustrationTarget, IPyAMSLayer, IPropertiesEditForm),
                 provides=IInnerSubForm)
-class IllustrationPropertiesInnerEditForm(InnerAdminEditForm):
+class IllustrationPropertiesInnerEditForm(BasicIllustrationPropertiesInnerEditForm):
     """Illustration properties inner edit form"""
 
     prefix = 'illustration_form.'
 
-    css_class = 'form-group'
-    padding_class = ''
-    fieldset_class = 'margin-top-10 padding-y-5'
+    fields = field.Fields(IIllustration).omit('__parent__', '__name__')
+    fields['renderer'].widgetFactory = RendererFieldWidget
 
     @property
     def legend(self):
@@ -87,20 +106,21 @@
         if IBaseParagraph.providedBy(self.context):
             return 'illustration switcher no-y-padding padding-right-10 pull-left width-auto'
         else:
-            return 'illustration no-y-padding'
+            return 'illustration switcher no-y-padding'
 
-    fields = field.Fields(IIllustration).omit('__parent__', '__name__')
-    fields['renderer'].widgetFactory = RendererFieldWidget
+    @property
+    def fieldset_class(self):
+        result = 'margin-top-10 padding-y-5'
+        if not IBaseParagraph.providedBy(self.context):
+            result += ' bordered'
+        return result
 
     hide_widgets_prefix_div = True
-    weight = 10
 
-    def getContent(self):
-        return IIllustration(self.context)
-
-    def check_mode(self):
-        if self.parent_form is not None:
-            self.mode = self.parent_form.mode
+    @property
+    def switcher_state(self):
+        if not IBaseParagraph.providedBy(self.context):
+            return 'open'
 
     def updateWidgets(self, prefix=None):
         super(IllustrationPropertiesInnerEditForm, self).updateWidgets(prefix)
@@ -109,13 +129,8 @@
 
     def get_ajax_output(self, changes):
         output = super(IllustrationPropertiesInnerEditForm, self).get_ajax_output(changes)
-        updated = changes.get(IIllustration, ())
         events = output.setdefault('events', [])
-        if 'data' in updated:
-            # we have to commit transaction to be able to handle blobs...
-            ITransactionManager(self.context).get().commit()
-            events.append(get_json_form_refresh_event(self.context, self.request,
-                                                      IllustrationPropertiesInnerEditForm))
+        if 'data' in changes.get(IBasicIllustration, ()):
             if IBaseParagraph.providedBy(self.context):
                 if self.getContent().data:
                     events.append(get_json_paragraph_markers_refresh_event(self.context, self.request, self,
@@ -124,14 +139,31 @@
                     events.append(get_json_paragraph_markers_refresh_event(self.context, self.request, self,
                                                                            EmptyViewlet,
                                                                            ParagraphContainerIllustrationMarker.marker_type))
-        elif 'renderer' in updated:
+        elif 'renderer' in changes.get(IIllustration, ()):
             events.append(get_json_widget_refresh_event(self.context, self.request,
                                                         IllustrationPropertiesInnerEditForm, 'renderer'))
         return output
 
 
-@viewlet_config(name='illustration-thumbnail', context=IIllustrationTarget, layer=IPyAMSLayer,
-                view=IllustrationPropertiesInnerEditForm, manager=IWidgetsPrefixViewletsManager)
+@adapter_config(name='link-illustration', context=(ILinkIllustrationTarget, IPyAMSLayer, IPropertiesEditForm),
+                provides=IInnerSubForm)
+class LinkIllustrationPropertiesInnerEditForm(BasicIllustrationPropertiesInnerEditForm):
+    """Link illustration properties inner edit form"""
+
+    prefix = 'link_illustration_form.'
+
+    legend = _("Navigation link illustration")
+    legend_class = 'illustration switcher no-y-padding'
+
+    weight = 11
+
+    def getContent(self):
+        registry = self.request.registry
+        return registry.getAdapter(self.context, IIllustration, name='link')
+
+
+@viewlet_config(name='illustration-thumbnail', context=IBasicIllustrationTarget, layer=IPyAMSLayer,
+                view=BasicIllustrationPropertiesInnerEditForm, manager=IWidgetsPrefixViewletsManager)
 @template_config(template='templates/illustration-thumbnail.pt', layer=IPyAMSLayer)
 class IllustrationThumbnail(Viewlet):
     """Paragraph illustration thumbnail"""
--- a/src/pyams_content/component/illustration/zmi/paragraph.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/component/illustration/zmi/paragraph.py	Wed Jun 27 16:42:01 2018 +0200
@@ -18,13 +18,15 @@
 # import interfaces
 from pyams_content.component.paragraph.interfaces import IParagraphContainerTarget, \
     IParagraphContainer
-from pyams_content.component.illustration.interfaces import IIllustration, IIllustrationParagraph, \
-    ILLUSTRATION_PARAGRAPH_TYPE
-from pyams_content.component.paragraph.zmi.interfaces import IParagraphInnerEditor, IParagraphContainerView
+from pyams_content.component.illustration.interfaces import IIllustrationTarget, IIllustration, \
+    IIllustrationParagraph, ILLUSTRATION_PARAGRAPH_TYPE
+from pyams_content.component.paragraph.zmi.interfaces import IParagraphInnerEditor, IParagraphContainerView, \
+    IParagraphContainerTable, IParagraphTitleToolbar
 from pyams_content.interfaces import MANAGE_CONTENT_PERMISSION
 from pyams_form.interfaces.form import IInnerForm
 from pyams_skin.interfaces.viewlet import IToolbarAddingMenu
 from pyams_skin.layer import IPyAMSLayer
+from pyams_utils.interfaces import VIEW_SYSTEM_PERMISSION
 from transaction.interfaces import ITransactionManager
 from z3c.form.interfaces import INPUT_MODE
 
@@ -36,8 +38,9 @@
 from pyams_form.form import ajax_config
 from pyams_pagelet.pagelet import pagelet_config
 from pyams_skin.event import get_json_form_refresh_event
+from pyams_template.template import template_config
 from pyams_utils.adapter import adapter_config
-from pyams_viewlet.viewlet import viewlet_config
+from pyams_viewlet.viewlet import viewlet_config, Viewlet
 from pyams_zmi.form import AdminDialogAddForm
 from z3c.form import field, button
 from zope.interface import implementer
@@ -147,3 +150,29 @@
             output.setdefault('events', []).append(get_json_form_refresh_event(self.context, self.request,
                                                                                IllustrationInnerEditForm))
         return output
+
+
+#
+# Paragraph container illustration marker
+#
+
+@viewlet_config(name='illustration', context=IIllustrationTarget, layer=IPyAMSLayer, view=IParagraphContainerTable,
+                manager=IParagraphTitleToolbar, permission=VIEW_SYSTEM_PERMISSION, weight=5)
+@template_config(template='templates/paragraph-illustration-icon.pt', layer=IPyAMSLayer)
+class ParagraphContainerIllustrationMarker(Viewlet):
+    """Paragraph container illustration marker column"""
+
+    weight = 5
+    action_class = 'action illustration nowrap width-40'
+    icon_class = 'fa fa-fw fa-picture-o'
+    icon_hint = _("Illustration")
+
+    marker_type = 'illustration'
+
+    def render(self):
+        illustration = IIllustration(self.context, None)
+        if illustration and illustration.data:
+            for value in illustration.data.values():
+                if value:
+                    return super(ParagraphContainerIllustrationMarker, self).render()
+        return ''
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/component/keynumber/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -0,0 +1,204 @@
+#
+# Copyright (c) 2008-2018 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.keynumber.interfaces import IKeyNumber, IKeyNumberContainerTarget, IKeyNumberContainer, \
+    KEYNUMBER_CONTAINER_KEY
+from pyams_content.features.checker.interfaces import IContentChecker, MISSING_VALUE, MISSING_LANG_VALUE
+from pyams_form.interfaces.form import IFormContextPermissionChecker
+from pyams_i18n.interfaces import II18n, II18nManager, INegotiator
+from zope.lifecycleevent.interfaces import IObjectAddedEvent, IObjectModifiedEvent, IObjectRemovedEvent
+from zope.location.interfaces import ISublocations
+from zope.traversing.interfaces import ITraversable
+
+# import packages
+from pyams_catalog.utils import index_object
+from pyams_content.features.checker import BaseContentChecker
+from pyams_utils.adapter import adapter_config, ContextAdapter, get_annotation_adapter
+from pyams_utils.registry import get_current_registry, get_utility
+from pyams_utils.request import check_request
+from pyams_utils.traversing import get_parent
+from persistent import Persistent
+from pyramid.events import subscriber
+from zope.container.contained import Contained
+from zope.container.ordered import OrderedContainer
+from zope.interface import implementer
+from zope.lifecycleevent import ObjectModifiedEvent
+from zope.location.location import locate
+from zope.schema.fieldproperty import FieldProperty
+
+from pyams_content import _
+
+
+#
+# Key number class and adapters
+#
+
+@implementer(IKeyNumber)
+class KeyNumber(Persistent, Contained):
+    """Key number persistent class"""
+
+    visible = FieldProperty(IKeyNumber['visible'])
+    label = FieldProperty(IKeyNumber['label'])
+    number = FieldProperty(IKeyNumber['number'])
+    unit = FieldProperty(IKeyNumber['unit'])
+    text = FieldProperty(IKeyNumber['text'])
+
+
+@adapter_config(context=IKeyNumber, provides=IFormContextPermissionChecker)
+class KeyNumberPermissionChecker(ContextAdapter):
+    """Key number permission checker"""
+
+    @property
+    def edit_permission(self):
+        content = get_parent(self.context, IKeyNumberContainerTarget)
+        return IFormContextPermissionChecker(content).edit_permission
+
+
+@subscriber(IObjectAddedEvent, context_selector=IKeyNumber)
+def handle_added_keynumber(event):
+    """Handle added key number"""
+    content = get_parent(event.object, IKeyNumberContainerTarget)
+    if content is not None:
+        get_current_registry().notify(ObjectModifiedEvent(content))
+
+
+@subscriber(IObjectModifiedEvent, context_selector=IKeyNumber)
+def handle_modified_keynumber(event):
+    """Handle modified key number"""
+    content = get_parent(event.object, IKeyNumberContainerTarget)
+    if content is not None:
+        get_current_registry().notify(ObjectModifiedEvent(content))
+
+
+@subscriber(IObjectRemovedEvent, context_selector=IKeyNumber)
+def handle_removed_keynumber(event):
+    """Handle removed key number"""
+    content = get_parent(event.object, IKeyNumberContainerTarget)
+    if content is not None:
+        get_current_registry().notify(ObjectModifiedEvent(content))
+
+
+@adapter_config(context=IKeyNumber, provides=IContentChecker)
+class KeyNumberContentChecker(BaseContentChecker):
+    """Key number content checker"""
+
+    @property
+    def label(self):
+        request = check_request()
+        return II18n(self.context).query_attribute('title', request=request)
+
+    def inner_check(self, request):
+        output = []
+        translate = request.localizer.translate
+        manager = get_parent(self.context, II18nManager)
+        if manager is not None:
+            langs = manager.get_languages()
+        else:
+            negotiator = get_utility(INegotiator)
+            langs = (negotiator.server_language, )
+        i18n = II18n(self.context)
+        for lang in langs:
+            for attr in ('label', 'text'):
+                value = i18n.get_attribute(attr, lang, request)
+                if not value:
+                    field_title = translate(IKeyNumber[attr].title)
+                    if len(langs) == 1:
+                        output.append(translate(MISSING_VALUE).format(field=field_title))
+                    else:
+                        output.append(translate(MISSING_LANG_VALUE).format(field=field_title, lang=lang))
+        return output
+
+
+#
+# Key numbers container classes and adapters
+#
+
+@implementer(IKeyNumberContainer)
+class KeyNumberContainer(OrderedContainer):
+    """Key numbers container"""
+
+    last_id = 1
+
+    def append(self, value, notify=True):
+        key = str(self.last_id)
+        if not notify:
+            # pre-locate key number item to avoid multiple notifications
+            locate(value, self, key)
+        self[key] = value
+        self.last_id += 1
+        if not notify:
+            # make sure that key number item is correctly indexed
+            index_object(value)
+
+    def get_visible_items(self):
+        return filter(lambda x: IKeyNumber(x).visible, self.values())
+
+
+@adapter_config(context=IKeyNumberContainerTarget, provides=IKeyNumberContainer)
+def keynumber_container_factory(target):
+    """Key number container factory"""
+    return get_annotation_adapter(target, KEYNUMBER_CONTAINER_KEY, KeyNumberContainer,
+                                  name='++keynumbers++')
+
+
+@adapter_config(context=IKeyNumberContainer, provides=IFormContextPermissionChecker)
+class KeyNumberContainerPermissionChecker(ContextAdapter):
+    """Key number container permission checker"""
+
+    @property
+    def edit_permission(self):
+        content = get_parent(self.context, IKeyNumberContainerTarget)
+        return IFormContextPermissionChecker(content).edit_permission
+
+
+@adapter_config(name='keynumbers', context=IKeyNumberContainerTarget, provides=ITraversable)
+class KeyNumberContainerNamespace(ContextAdapter):
+    """Key numbers container ++keynumbers++ namespace"""
+
+    def traverse(self, name, furtherpaath=None):
+        return IKeyNumberContainer(self.context)
+
+
+@adapter_config(name='keynumbers', context=IKeyNumberContainerTarget, provides=ISublocations)
+class KeyNumberContainerSublocations(ContextAdapter):
+    """Key numbers container sub-locations adapter"""
+
+    def sublocations(self):
+        return IKeyNumberContainer(self.context).values()
+
+
+@adapter_config(name='keynumbers', context=IKeyNumberContainerTarget, provides=IContentChecker)
+class KeyNumberContainerContentChecker(BaseContentChecker):
+    """Key numbers container content checker"""
+
+    label = _("Key numbers")
+    sep = '\n'
+    weight = 200
+
+    def inner_check(self, request):
+        output = []
+        registry = request.registry
+        for keynumber in IKeyNumberContainer(self.context).values():
+            if not keynumber.visible:
+                continue
+            for name, checker in sorted(registry.getAdapters((keynumber, ), IContentChecker),
+                                        key=lambda x: x[1].weight):
+                output.append('- {0} ({1}):'.format(keynumber.number,
+                                                    II18n(keynumber).query_attribute('label', request=request) or '--'))
+                output.append(checker.get_check_output(request))
+        return output
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/component/keynumber/interfaces/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -0,0 +1,75 @@
+#
+# Copyright (c) 2008-2018 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.container import IOrderedContainer
+from zope.annotation.interfaces import IAttributeAnnotatable
+
+# import packages
+from pyams_i18n.schema import I18nTextLineField
+from zope.container.constraints import containers, contains
+from zope.interface import Interface
+from zope.schema import Bool, TextLine
+
+from pyams_content import _
+
+
+KEYNUMBER_CONTAINER_KEY = 'pyams_content.keynumbers'
+
+
+class IKeyNumber(IAttributeAnnotatable):
+    """Base key number interface"""
+
+    containers('.IKeyNumberContainer')
+
+    visible = Bool(title=_("Visible?"),
+                   description=_("Is this key number visible in front-office?"),
+                   required=True,
+                   default=True)
+
+    label = I18nTextLineField(title=_('key-number-label', default="Header"),
+                              description=_("Small text to be displayed above number (according to selected "
+                                            "renderer)"),
+                              required=False)
+
+    number = TextLine(title=_("Number"),
+                      description=_("Key number value"),
+                      required=True)
+
+    unit = I18nTextLineField(title=_('key-number-unit', default="Unit"),
+                             description=_("Displayed unit"),
+                             required=False)
+
+    text = I18nTextLineField(title=_("Associated text"),
+                             description=_("The way this text will be rendered depends on presentation template"),
+                             required=False)
+
+
+class IKeyNumberContainer(IOrderedContainer):
+    """Key numbers container interface"""
+
+    contains(IKeyNumber)
+
+    def append(self, value, notify=True):
+        """Append given key number to container"""
+
+    def get_visible_items(self):
+        """Get list of visible key numbers"""
+
+
+class IKeyNumberContainerTarget(Interface):
+    """Key numbers container target interface"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/component/keynumber/portlet/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -0,0 +1,76 @@
+#
+# Copyright (c) 2008-2018 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.association.interfaces import ASSOCIATION_CONTAINER_KEY
+from pyams_content.component.keynumber.interfaces import IKeyNumberContainerTarget, IKeyNumberContainer
+from pyams_content.component.keynumber.portlet.interfaces import IKeyNumberPortletSettings, IKeyNumberPortletMenu
+from pyams_content.features.menu.interfaces import IMenuLinksContainerTarget, IMenuLinksContainer
+from pyams_utils.interfaces import VIEW_PERMISSION
+
+# import packages
+from pyams_content.features.menu import Menu
+from pyams_portal.portlet import PortletSettings, portlet_config, Portlet
+from pyams_utils.adapter import get_annotation_adapter, adapter_config
+from pyams_utils.factory import factory_config
+from zope.interface import implementer
+from zope.schema.fieldproperty import FieldProperty
+
+from pyams_content import _
+
+
+KEYNUMBER_PORTLET_NAME = "pyams_portal.portlet.keynumber"
+
+KEYNUMBER_PORTLET_LINKS_NAME = 'links'
+KEYNUMBER_PORTLET_LINKS_KEY = '{0}::{1}'.format(ASSOCIATION_CONTAINER_KEY, KEYNUMBER_PORTLET_LINKS_NAME)
+
+
+@implementer(IKeyNumberPortletSettings, IKeyNumberContainerTarget, IMenuLinksContainerTarget)
+@factory_config(provided=IKeyNumberPortletSettings)
+class KeyNumberPortletSettings(PortletSettings):
+    """Key Number portlet settings"""
+
+    title = FieldProperty(IKeyNumberPortletSettings['title'])
+    teaser = FieldProperty(IKeyNumberPortletSettings['teaser'])
+
+    @property
+    def keynumbers(self):
+        return IKeyNumberContainer(self)
+
+    @property
+    def links(self):
+        return get_annotation_adapter(self, KEYNUMBER_PORTLET_LINKS_KEY, Menu,
+                                      markers=IKeyNumberPortletMenu,
+                                      name='++ass++' + KEYNUMBER_PORTLET_LINKS_NAME)
+
+
+@adapter_config(name=KEYNUMBER_PORTLET_LINKS_NAME, context=IKeyNumberPortletSettings, provides=IMenuLinksContainer)
+def keynumber_links_adapter(context):
+    """Key number settings links factory"""
+    return context.links
+
+
+@portlet_config(permission=VIEW_PERMISSION)
+class KeyNumberPortlet(Portlet):
+    """Key number portlet"""
+
+    name = KEYNUMBER_PORTLET_NAME
+    label = _("Key Numbers")
+
+    toolbar_image = None
+    toolbar_css_class = 'fa fa-fw fa-2x fa-dashboard'
+
+    settings_factory = IKeyNumberPortletSettings
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/component/keynumber/portlet/interfaces/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -0,0 +1,43 @@
+#
+# 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_portal.interfaces import IPortletSettings
+
+# import packages
+from pyams_i18n.schema import I18nTextLineField, I18nTextField
+from zope.interface import Attribute, Interface
+
+from pyams_content import _
+
+
+class IKeyNumberPortletSettings(IPortletSettings):
+    """Key numbers portlet settings interface"""
+
+    title = I18nTextLineField(title=_("Title"),
+                              description=_("Portlet title"),
+                              required=False)
+
+    teaser = I18nTextField(title=_("Teaser"),
+                           description=_("Short text displayed above key numbers"),
+                           required=False)
+
+    links = Attribute("Navigation links")
+
+
+class IKeyNumberPortletMenu(Interface):
+    """Key numbers portlet menu marker interface"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/component/keynumber/portlet/zmi/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -0,0 +1,126 @@
+#
+# 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.association.interfaces import IAssociationInfo
+from pyams_content.component.keynumber.portlet.interfaces import IKeyNumberPortletSettings, IKeyNumberPortletMenu
+from pyams_content.features.menu.zmi import IMenuLinksView
+from pyams_content.component.links.interfaces import IInternalLink
+from pyams_portal.interfaces import IPortletPreviewer
+from pyams_form.interfaces.form import IInnerSubForm, IInnerTabForm
+from pyams_pagelet.interfaces import IPagelet
+from pyams_skin.layer import IPyAMSLayer
+from pyams_utils.interfaces import VIEW_SYSTEM_PERMISSION
+from pyams_workflow.interfaces import IWorkflowPublicationInfo
+
+# import packages
+from pyams_content.component.keynumber.portlet import KEYNUMBER_PORTLET_LINKS_NAME
+from pyams_content.component.keynumber.zmi import KeyNumbersView
+from pyams_content.features.menu.zmi import MenuLinksView, LinksTable
+from pyams_form.form import AJAXEditForm
+from pyams_pagelet.pagelet import pagelet_config
+from pyams_portal.portlet import PortletPreviewer
+from pyams_portal.zmi.portlet import PortletSettingsEditor, PortletSettingsPropertiesEditor
+from pyams_template.template import template_config
+from pyams_utils.adapter import adapter_config
+from zope.interface import Interface
+
+from pyams_content import _
+
+
+@pagelet_config(name='properties.html', context=IKeyNumberPortletSettings, layer=IPyAMSLayer,
+                permission=VIEW_SYSTEM_PERMISSION)
+class KeyNumberPortletEditor(PortletSettingsEditor):
+    """Key number portlet settings editor"""
+
+    settings = IKeyNumberPortletSettings
+
+
+@adapter_config(name='properties', context=(Interface, IPyAMSLayer, KeyNumberPortletEditor), provides=IInnerTabForm)
+class KeyNumberPortletSettingsPropertiesEditor(PortletSettingsPropertiesEditor):
+    """Key number portlet settings properties editor"""
+
+    def updateWidgets(self, prefix=None):
+        super().updateWidgets(prefix)
+        if 'teaser' in self.widgets:
+            self.widgets['teaser'].widget_css_class = 'textarea'
+
+
+@adapter_config(name='properties.json', context=(IKeyNumberPortletSettings, IPyAMSLayer), provides=IPagelet)
+class KeyNumberPortletAJAXEditor(AJAXEditForm, KeyNumberPortletEditor):
+    """Key number portlet settings editor, JSON renderer"""
+
+
+#
+# Key numbers portlet numbers view
+#
+
+@adapter_config(name='keynumber-portlet-numbers',
+                context=(IKeyNumberPortletSettings, IPyAMSLayer, PortletSettingsPropertiesEditor),
+                provides=IInnerSubForm)
+class KeyNumberPortletNumbersView(KeyNumbersView):
+    """Key number portlet numbers view"""
+
+    title = _("Key numbers")
+    weight = 10
+
+
+#
+# Key numbers portlet links view
+#
+
+class KeyNumberPortletLinksTable(LinksTable):
+    """Key Numbers links associations table"""
+
+    associations_name = KEYNUMBER_PORTLET_LINKS_NAME
+
+
+@adapter_config(name='keynumber-portlet-links',
+                context=(IKeyNumberPortletSettings, IPyAMSLayer, PortletSettingsPropertiesEditor),
+                provides=IInnerSubForm)
+@adapter_config(name='++ass++{0}'.format(KEYNUMBER_PORTLET_LINKS_NAME),
+                context=(IKeyNumberPortletMenu, IPyAMSLayer),
+                provides=IMenuLinksView)
+class KeyNumberPortletLinksView(MenuLinksView):
+    """Key numbers portlet links view"""
+
+    title = _("Associated links")
+    weight = 20
+
+    table_class = KeyNumberPortletLinksTable
+
+
+#
+# Key numbers portlet previewer
+#
+
+@adapter_config(context=(Interface, IPyAMSLayer, Interface, IKeyNumberPortletSettings), provides=IPortletPreviewer)
+@template_config(template='templates/keynumber-preview.pt', layer=IPyAMSLayer)
+class KeyNumberPortletPreviewer(PortletPreviewer):
+    """Key number portlet previewer"""
+
+    @classmethod
+    def get_link_info(cls, link):
+        return IAssociationInfo(link)
+
+    @classmethod
+    def get_link_status(cls, link):
+        if not IInternalLink.providedBy(link):
+            return True
+        target = link.get_target()
+        return (target is not None) and IWorkflowPublicationInfo(target).is_published()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/component/keynumber/portlet/zmi/templates/keynumber-preview.pt	Wed Jun 27 16:42:01 2018 +0200
@@ -0,0 +1,35 @@
+<div tal:define="settings view.settings" i18n:domain="pyams_content">
+	<strong tal:define="title i18n:settings.title"
+			tal:condition="title">
+		<tal:var content="title"/><br /></strong>
+	<div tal:define="teaser i18n:settings.teaser"
+		 tal:content="structure extension:html(teaser)">Teaser</div>
+	<ul>
+		<li tal:repeat="number settings.keynumbers.get_visible_items()">
+			<span tal:define="label i18n:number.label"
+				  tal:condition="label">
+				<tal:var content="label" /> : </span>
+			<span class="badge"
+				  tal:content="i18n:number.number">Number</span>
+			<span tal:define="unit i18n:number.unit"
+				  tal:condition="unit"
+				  tal:content="unit">Unit</span>
+			<span tal:content="i18n:number.text">text</span>
+		</li>
+	</ul>
+	<div tal:define="visible_links list(settings.links.get_visible_items())"
+		 tal:condition="visible_links">
+		<u><i18n:var translate="" >Associated links</i18n:var> :</u>
+		<ul>
+			<li tal:repeat="link visible_links">
+				<i class="fa fa-fw fa-eye-slash text-danger hint opaque align-base"
+				   tal:condition="not:view.get_link_status(link)"
+				   title="Link target is not published!" i18n:attributes="title"></i>
+				<tal:var define="info view.get_link_info(link)">
+					<span tal:content="info.user_title">User title</span>
+					<span tal:content="info.inner_title">Inner title</span>
+				</tal:var>
+			</li>
+		</ul>
+	</div>
+</div>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/component/keynumber/zmi/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -0,0 +1,271 @@
+#
+# Copyright (c) 2008-2018 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 json
+
+# import interfaces
+from pyams_content.component.keynumber.interfaces import IKeyNumberContainer, IKeyNumberContainerTarget, IKeyNumber
+from pyams_content.interfaces import MANAGE_CONTENT_PERMISSION
+from pyams_form.interfaces.form import IInnerSubForm
+from pyams_skin.interfaces.viewlet import IWidgetTitleViewletManager
+from pyams_skin.layer import IPyAMSLayer
+from z3c.table.interfaces import IValues, IColumn
+
+# import packages
+from pyams_content.component.keynumber import KeyNumber
+from pyams_form.form import ajax_config, AJAXAddForm
+from pyams_form.security import ProtectedFormObjectMixin
+from pyams_i18n.column import I18nAttrColumn
+from pyams_pagelet.pagelet import pagelet_config
+from pyams_skin.container import switch_element_visibility
+from pyams_skin.event import get_json_table_row_refresh_event, get_json_switched_table_refresh_event
+from pyams_skin.table import BaseTable, SorterColumn, VisibilitySwitcherColumn, I18nColumn, TrashColumn
+from pyams_skin.viewlet.toolbar import ToolbarAction
+from pyams_utils.adapter import adapter_config, ContextRequestViewAdapter
+from pyams_utils.traversing import get_parent
+from pyams_utils.url import absolute_url
+from pyams_viewlet.viewlet import viewlet_config
+from pyams_zmi.form import AdminDialogAddForm, AdminDialogEditForm
+from pyams_zmi.zmi.table import InnerTableView
+from pyramid.decorator import reify
+from pyramid.view import view_config
+from z3c.form import field
+from z3c.table.column import GetAttrColumn
+from zope.interface import implementer, Interface
+
+from pyams_content import _
+
+
+#
+# Views marker interfaces
+#
+
+class IKeyNumbersView(Interface):
+    """Key numbers view marker interface"""
+
+
+class IKeyNumbersParentForm(Interface):
+    """Key numbers parent form marker interface"""
+
+
+#
+# Key number items table view
+#
+
+class KeyNumbersTable(ProtectedFormObjectMixin, BaseTable):
+    """Key numbers view inner table"""
+
+    prefix = 'keynumbers'
+
+    hide_header = True
+    hide_body_toolbar = True
+    sortOn = None
+
+    @classmethod
+    def get_context(cls, context):
+        return IKeyNumberContainer(context)
+
+    @reify
+    def cssClasses(self):
+        classes = ['table', 'table-bordered', 'table-striped', 'table-hover', 'table-tight']
+        permission = self.permission
+        if (not permission) or self.request.has_permission(permission, self.context):
+            classes.append('table-dnd')
+        return {'table': ' '.join(classes)}
+
+    @reify
+    def data_attributes(self):
+        container = IKeyNumberContainer(self.context)
+        attributes = super(KeyNumbersTable, self).data_attributes
+        attributes.setdefault('table', {}).update({
+            'data-ams-location': absolute_url(container, self.request),
+            'data-ams-tablednd-drag-handle': 'td.sorter',
+            'data-ams-tablednd-drop-target': 'set-keynumbers-order.json',
+            'data-ams-visibility-switcher': 'switch-keynumber-visibility.json'
+        })
+        return attributes
+
+    @reify
+    def values(self):
+        return list(super(KeyNumbersTable, self).values)
+
+
+@adapter_config(context=(IKeyNumberContainer, IPyAMSLayer, KeyNumbersTable), provides=IValues)
+class KeyNumbersTableValuesAdapter(ContextRequestViewAdapter):
+    """Key numbers table values adapter"""
+
+    @property
+    def values(self):
+        return IKeyNumberContainer(self.context).values()
+
+
+@adapter_config(name='sorter', context=(IKeyNumberContainer, IPyAMSLayer, KeyNumbersTable), provides=IColumn)
+class KeyNumbersTableSorterColumn(ProtectedFormObjectMixin, SorterColumn):
+    """Key numbers table sorter column"""
+
+
+@view_config(name='set-keynumbers-order.json', context=IKeyNumberContainer, request_type=IPyAMSLayer,
+             permission=MANAGE_CONTENT_PERMISSION, renderer='json', xhr=True)
+def set_keynumbers_order(request):
+    """Update key numbers order"""
+    order = list(map(str, json.loads(request.params.get('names'))))
+    request.context.updateOrder(order)
+    return {'status': 'success'}
+
+
+@adapter_config(name='show-hide', context=(IKeyNumberContainer, IPyAMSLayer, KeyNumbersTable),
+                provides=IColumn)
+class KeyNumbersTableShowHideColumn(ProtectedFormObjectMixin, VisibilitySwitcherColumn):
+    """Key numbers container visibility switcher column"""
+
+
+@view_config(name='switch-keynumber-visibility.json', context=IKeyNumberContainer, request_type=IPyAMSLayer,
+             permission=MANAGE_CONTENT_PERMISSION, renderer='json', xhr=True)
+def switch_keynumber_visibility(request):
+    """Switch key number visibility"""
+    return switch_element_visibility(request, IKeyNumberContainer)
+
+
+@adapter_config(name='label', context=(IKeyNumberContainer, IPyAMSLayer, KeyNumbersTable), provides=IColumn)
+class KeyNumbersTableLabelColumn(I18nColumn, I18nAttrColumn):
+    """Key numbers table label column"""
+
+    _header = _('key-number-label', default="Header")
+    attrName = 'label'
+    weight = 10
+
+    def getValue(self, obj):
+        return super(KeyNumbersTableLabelColumn, self).getValue(obj) or '--'
+
+
+@adapter_config(name='name', context=(IKeyNumberContainer, IPyAMSLayer, KeyNumbersTable), provides=IColumn)
+class KeyNumbersTableNameColumn(I18nColumn, GetAttrColumn):
+    """Key numbers table number column"""
+
+    _header = _("Number")
+    attrName = 'number'
+    weight = 20
+
+
+@adapter_config(name='unit', context=(IKeyNumberContainer, IPyAMSLayer, KeyNumbersTable), provides=IColumn)
+class KeyNumbersTableUnitColumn(I18nColumn, I18nAttrColumn):
+    """Key numbers table unit column"""
+
+    _header = _('key-number-unit', default="Unit")
+    attrName = 'unit'
+    weight = 30
+
+    def getValue(self, obj):
+        return super(KeyNumbersTableUnitColumn, self).getValue(obj) or '--'
+
+
+@adapter_config(name='text', context=(IKeyNumberContainer, IPyAMSLayer, KeyNumbersTable), provides=IColumn)
+class KeyNumbersTableTextColumn(I18nColumn, I18nAttrColumn):
+    """Key numbers table text column"""
+
+    _header = _("Associated text")
+    attrName = 'text'
+    weight = 40
+
+    def getValue(self, obj):
+        return super(KeyNumbersTableTextColumn, self).getValue(obj) or '--'
+
+
+@adapter_config(name='trash', context=(IKeyNumberContainer, IPyAMSLayer, KeyNumbersTable), provides=IColumn)
+class KeyNumbersTableTrashColumn(ProtectedFormObjectMixin, TrashColumn):
+    """Key numbers table trash column"""
+
+
+@adapter_config(name='keynumbers', context=(IKeyNumberContainerTarget, IPyAMSLayer, IKeyNumbersParentForm),
+                provides=IInnerSubForm)
+@implementer(IKeyNumbersView)
+class KeyNumbersView(InnerTableView):
+    """Key numbers view"""
+
+    title = _("Key numbers")
+
+    table_class = KeyNumbersTable
+    weight = 110
+
+    @property
+    def actions_context(self):  # define context for internal actions
+        return IKeyNumberContainer(self.context)
+
+
+#
+# Key numbers forms
+#
+
+@viewlet_config(name='add-keynumber.action', context=IKeyNumberContainerTarget, layer=IPyAMSLayer, view=IKeyNumbersView,
+                manager=IWidgetTitleViewletManager, weight=1)
+class KeyNumberAddAction(ProtectedFormObjectMixin, ToolbarAction):
+    """Key number add action"""
+
+    label = _("Add keynumber")
+    label_css_class = 'fa fa-fw fa-plus'
+    url = 'add-keynumber.html'
+    modal_target = True
+
+
+@pagelet_config(name='add-keynumber.html', context=IKeyNumberContainer, layer=IPyAMSLayer)
+@ajax_config(name='add-keynumber.json', context=IKeyNumberContainer, layer=IPyAMSLayer,
+             base=AJAXAddForm)
+class KeyNumberAddForm(AdminDialogAddForm):
+    """Key number add form"""
+
+    legend = _("Add new keynumber")
+    icon_css_class = 'fa fa-fw fa-dashboard'
+
+    fields = field.Fields(IKeyNumber).omit('__parent__', '__name__', 'visible')
+    edit_permission = None  # use context permission checker
+
+    def create(self, data):
+        return KeyNumber()
+
+    def add(self, object):
+        IKeyNumberContainer(self.context).append(object)
+
+    def get_ajax_output(self, changes):
+        return {
+            'status': 'success',
+            'message': self.request.localizer.translate(_("Key number was correctly added")),
+            'events': [
+                get_json_switched_table_refresh_event(self.context, self.request, KeyNumbersTable)
+            ]
+        }
+
+
+@pagelet_config(name='properties.html', context=IKeyNumber, layer=IPyAMSLayer)
+@ajax_config(name='properties.json', context=IKeyNumber, layer=IPyAMSLayer)
+class KeyNumberPropertiesEditForm(AdminDialogEditForm):
+    """Key number properties edit form"""
+
+    prefix = 'keynumber_properties.'
+
+    legend = _("Edit keynumber properties")
+    icon_css_class = 'fa fa-fw fa-dashboard'
+
+    fields = field.Fields(IKeyNumber).omit('__parent__', '__name__', 'visible')
+    edit_permission = None  # use context permission checker
+
+    def get_ajax_output(self, changes):
+        output = super(self.__class__, self).get_ajax_output(changes)
+        updated = changes.get(IKeyNumber, ())
+        if updated:
+            target = get_parent(self.context, IKeyNumberContainerTarget)
+            output.setdefault('events', []).append(
+                get_json_table_row_refresh_event(target, self.request, KeyNumbersTable, self.context))
+        return output
--- a/src/pyams_content/component/links/__init__.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/component/links/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -9,6 +9,7 @@
 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
 # FOR A PARTICULAR PURPOSE.
 #
+from pyams_skin.layer import IPyAMSUserLayer
 
 __docformat__ = 'restructuredtext'
 
@@ -16,10 +17,11 @@
 # import standard library
 
 # import interfaces
-from pyams_content.component.association.interfaces import IAssociationInfo, IAssociationTarget, IAssociationContainer
+from pyams_content.component.association.interfaces import IAssociationInfo, IAssociationContainerTarget, IAssociationContainer
 from pyams_content.component.links.interfaces import IBaseLink, IInternalLink, IExternalLink, IMailtoLink
 from pyams_content.features.checker.interfaces import IContentChecker, ERROR_VALUE
 from pyams_content.interfaces import IBaseContent, MANAGE_CONTENT_PERMISSION
+from pyams_content.reference.pictograms.interfaces import IPictogramTable
 from pyams_form.interfaces.form import IFormContextPermissionChecker
 from pyams_i18n.interfaces import II18n
 from pyams_sequence.interfaces import ISequentialIdInfo
@@ -31,10 +33,12 @@
 from pyams_content.workflow import VISIBLE_STATES
 from pyams_sequence.utility import get_reference_target
 from pyams_utils.adapter import adapter_config, ContextAdapter
+from pyams_utils.registry import query_utility
 from pyams_utils.request import check_request
 from pyams_utils.traversing import get_parent
-from pyams_utils.url import absolute_url
+from pyams_utils.url import absolute_url, relative_url
 from pyams_utils.vocabulary import vocabulary_config
+from pyams_utils.zodb import volatile_property
 from zope.interface import implementer
 from zope.schema.fieldproperty import FieldProperty
 from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm
@@ -52,7 +56,7 @@
 
     def __init__(self, context=None):
         terms = []
-        target = get_parent(context, IAssociationTarget)
+        target = get_parent(context, IAssociationContainerTarget)
         if target is not None:
             terms = [SimpleTerm(link.__name__, title=IAssociationInfo(link).inner_title)
                      for link in IAssociationContainer(target).values() if IBaseLink.providedBy(link)]
@@ -69,6 +73,23 @@
 
     title = FieldProperty(IBaseLink['title'])
     description = FieldProperty(IBaseLink['description'])
+    _pictogram_name = FieldProperty(IBaseLink['pictogram_name'])
+
+    @property
+    def pictogram_name(self):
+        return self._pictogram_name
+
+    @pictogram_name.setter
+    def pictogram_name(self, value):
+        if value != self._pictogram_name:
+            self._pictogram_name = value
+            del self.pictogram
+
+    @volatile_property
+    def pictogram(self):
+        table = query_utility(IPictogramTable)
+        if table is not None:
+            return table.get(self._pictogram_name)
 
 
 class BaseLinkInfoAdapter(ContextAdapter):
@@ -112,11 +133,11 @@
         return 'oid://{0}'.format(self.reference)
 
     def get_url(self, request=None, view_name=None):
-        target = self.get_target(state=VISIBLE_STATES)
+        target = self.get_target()
         if target is not None:
             if request is None:
                 request = check_request()
-            return absolute_url(target, request, view_name)
+            return relative_url(target, request, view_name=view_name)
         else:
             return ''
 
--- a/src/pyams_content/component/links/interfaces/__init__.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/component/links/interfaces/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -16,12 +16,14 @@
 # import standard library
 
 # import interfaces
-from pyams_content.component.association.interfaces import IAssociationTarget, IAssociationItem
+from pyams_content.component.association.interfaces import IAssociationContainerTarget, IAssociationItem
+from pyams_content.reference.pictograms.interfaces import SELECTED_PICTOGRAM_VOCABULARY
 from pyams_sequence.interfaces import IInternalReference
 
 # import packages
 from pyams_i18n.schema import I18nTextLineField, I18nTextField
 from pyams_utils.schema import MailAddressField
+from zope.interface import Attribute
 from zope.schema import Choice, TextLine, URI
 
 from pyams_content import _
@@ -38,6 +40,13 @@
                                 description=_("Link description displayed by front-office template"),
                                 required=False)
 
+    pictogram_name = Choice(title=_("Pictogram"),
+                            description=_("Name of the pictogram associated with this link"),
+                            required=False,
+                            vocabulary=SELECTED_PICTOGRAM_VOCABULARY)
+
+    pictogram = Attribute("Selected pictogram object")
+
     def get_editor_url(self):
         """Get URL for use in HTML editor"""
 
@@ -74,5 +83,5 @@
                             required=True)
 
 
-class ILinkContainerTarget(IAssociationTarget):
+class ILinkContainerTarget(IAssociationContainerTarget):
     """Links container marker interface"""
--- a/src/pyams_content/component/links/zmi/__init__.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/component/links/zmi/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -18,19 +18,21 @@
 # import interfaces
 from pyams_content.component.association.interfaces import IAssociationContainer
 from pyams_content.component.association.zmi.interfaces import IAssociationsView
-from pyams_content.component.links.interfaces import ILinkContainerTarget, IInternalLink, IBaseLink, \
+from pyams_content.component.links.interfaces import ILinkContainerTarget, IBaseLink, IInternalLink, \
     IExternalLink, IMailtoLink
 from pyams_content.component.paragraph.zmi.interfaces import IParagraphContainerTable, IParagraphTitleToolbar
 from pyams_content.interfaces import MANAGE_CONTENT_PERMISSION
 from pyams_skin.interfaces.viewlet import IToolbarAddingMenu
 from pyams_skin.layer import IPyAMSLayer
 from pyams_utils.interfaces import VIEW_SYSTEM_PERMISSION
+from pyams_zmi.interfaces import IPropertiesEditForm
 
 # import packages
 from pyams_content.component.association.zmi import AssociationItemAJAXAddForm, AssociationItemAJAXEditForm
 from pyams_content.component.links import InternalLink, ExternalLink, MailtoLink
 from pyams_content.component.paragraph.zmi import get_json_paragraph_markers_refresh_event
 from pyams_content.component.paragraph.zmi.container import ParagraphContainerCounterBase
+from pyams_content.reference.pictograms.zmi.widget import PictogramSelectFieldWidget
 from pyams_form.form import ajax_config
 from pyams_form.security import ProtectedFormObjectMixin
 from pyams_pagelet.pagelet import pagelet_config
@@ -38,6 +40,7 @@
 from pyams_viewlet.viewlet import viewlet_config
 from pyams_zmi.form import AdminDialogAddForm, AdminDialogEditForm
 from z3c.form import field
+from zope.interface import implementer
 
 from pyams_content import _
 
@@ -86,7 +89,9 @@
     legend = _("Add new internal link")
     icon_css_class = 'fa fa-fw fa-external-link-square fa-rotate-90'
 
-    fields = field.Fields(IInternalLink).select('reference', 'title', 'description')
+    fields = field.Fields(IInternalLink).select('reference', 'title', 'description', 'pictogram_name')
+    fields['pictogram_name'].widgetFactory = PictogramSelectFieldWidget
+
     edit_permission = MANAGE_CONTENT_PERMISSION
 
     def updateWidgets(self, prefix=None):
@@ -111,6 +116,7 @@
 @pagelet_config(name='properties.html', context=IInternalLink, layer=IPyAMSLayer, permission=VIEW_SYSTEM_PERMISSION)
 @ajax_config(name='properties.json', context=IInternalLink, layer=IPyAMSLayer,
              permission=MANAGE_CONTENT_PERMISSION, base=AssociationItemAJAXEditForm)
+@implementer(IPropertiesEditForm)
 class InternalLinkPropertiesEditForm(AdminDialogEditForm):
     """Internal link properties edit form"""
 
@@ -118,8 +124,11 @@
 
     legend = _("Edit internal link properties")
     icon_css_class = 'fa fa-fw fa-external-link-square fa-rotate-90'
+    dialog_class = 'modal-large'
 
-    fields = field.Fields(IInternalLink).select('reference', 'title', 'description')
+    fields = field.Fields(IInternalLink).select('reference', 'title', 'description', 'pictogram_name')
+    fields['pictogram_name'].widgetFactory = PictogramSelectFieldWidget
+
     edit_permission = None  # defined by IFormContextPermissionChecker adapter
 
     def updateWidgets(self, prefix=None):
@@ -179,7 +188,9 @@
     legend = _("Add new external link")
     icon_css_class = 'fa fa-fw fa-external-link'
 
-    fields = field.Fields(IExternalLink).select('url', 'title', 'description', 'language')
+    fields = field.Fields(IExternalLink).select('url', 'title', 'description', 'pictogram_name', 'language')
+    fields['pictogram_name'].widgetFactory = PictogramSelectFieldWidget
+
     edit_permission = MANAGE_CONTENT_PERMISSION
 
     def updateWidgets(self, prefix=None):
@@ -204,6 +215,7 @@
 @pagelet_config(name='properties.html', context=IExternalLink, layer=IPyAMSLayer, permission=VIEW_SYSTEM_PERMISSION)
 @ajax_config(name='properties.json', context=IExternalLink, layer=IPyAMSLayer,
              permission=MANAGE_CONTENT_PERMISSION, base=AssociationItemAJAXEditForm)
+@implementer(IPropertiesEditForm)
 class ExternalLinkPropertiesEditForm(AdminDialogEditForm):
     """External link properties edit form"""
 
@@ -211,8 +223,11 @@
 
     legend = _("Edit external link properties")
     icon_css_class = 'fa fa-fw fa-external-link'
+    dialog_class = 'modal-large'
 
-    fields = field.Fields(IExternalLink).select('url', 'title', 'description', 'language')
+    fields = field.Fields(IExternalLink).select('url', 'title', 'description', 'pictogram_name', 'language')
+    fields['pictogram_name'].widgetFactory = PictogramSelectFieldWidget
+
     edit_permission = None  # defined by IFormContextPermissionChecker adapter
 
     def updateWidgets(self, prefix=None):
@@ -272,7 +287,9 @@
     legend = _("Add new mailto link")
     icon_css_class = 'fa fa-fw fa-envelope-o'
 
-    fields = field.Fields(IMailtoLink).select('address', 'address_name', 'title', 'description')
+    fields = field.Fields(IMailtoLink).select('address', 'address_name', 'title', 'description', 'pictogram_name')
+    fields['pictogram_name'].widgetFactory = PictogramSelectFieldWidget
+
     edit_permission = MANAGE_CONTENT_PERMISSION
 
     def updateWidgets(self, prefix=None):
@@ -297,6 +314,7 @@
 @pagelet_config(name='properties.html', context=IMailtoLink, layer=IPyAMSLayer, permission=VIEW_SYSTEM_PERMISSION)
 @ajax_config(name='properties.json', context=IMailtoLink, layer=IPyAMSLayer,
              permission=MANAGE_CONTENT_PERMISSION, base=AssociationItemAJAXEditForm)
+@implementer(IPropertiesEditForm)
 class MailtoLinkPropertiesEditForm(AdminDialogEditForm):
     """Mailto link properties edit form"""
 
@@ -305,7 +323,9 @@
     legend = _("Edit mailto link properties")
     icon_css_class = 'fa fa-fw fa-envelope-o'
 
-    fields = field.Fields(IMailtoLink).select('address', 'address_name', 'title', 'description')
+    fields = field.Fields(IMailtoLink).select('address', 'address_name', 'title', 'description', 'pictogram_name')
+    fields['pictogram_name'].widgetFactory = PictogramSelectFieldWidget
+
     edit_permission = None  # defined by IFormContextPermissionChecker adapter
 
     def updateWidgets(self, prefix=None):
--- a/src/pyams_content/component/links/zmi/container.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/component/links/zmi/container.py	Wed Jun 27 16:42:01 2018 +0200
@@ -16,7 +16,7 @@
 # import standard library
 
 # import interfaces
-from pyams_content.component.association.interfaces import IAssociationContainer, IAssociationTarget, IAssociationInfo
+from pyams_content.component.association.interfaces import IAssociationContainer, IAssociationContainerTarget, IAssociationInfo
 from pyams_content.component.extfile.interfaces import IBaseExtFile
 from pyams_content.component.links.interfaces import IBaseLink
 from pyams_i18n.interfaces import II18n
@@ -40,7 +40,7 @@
     result = []
     key_field_name = request.params.get('keyFieldName', 'title')
     value_field_name = request.params.get('valueFieldName', 'value')
-    target = get_parent(request.context, IAssociationTarget)
+    target = get_parent(request.context, IAssociationContainerTarget)
     if target is not None:
         container = IAssociationContainer(target)
         result.extend([{key_field_name: item.__name__,
@@ -54,7 +54,7 @@
 def get_links_list(request):
     """Get links list in JSON format for TinyMCE editor"""
     result = []
-    target = get_parent(request.context, IAssociationTarget)
+    target = get_parent(request.context, IAssociationContainerTarget)
     if target is not None:
         container = IAssociationContainer(target)
         for item in container.values():
--- a/src/pyams_content/component/links/zmi/reverse.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/component/links/zmi/reverse.py	Wed Jun 27 16:42:01 2018 +0200
@@ -19,6 +19,8 @@
 from hypatia.interfaces import ICatalog
 from pyams_content.shared.common.interfaces import IWfSharedContent
 from pyams_content.shared.common.interfaces.zmi import ISiteRootDashboardTable
+from pyams_content.shared.site.interfaces import ISiteContainer
+from pyams_portal.interfaces import IPortalTemplate
 from pyams_sequence.interfaces import ISequentialIdInfo
 from pyams_skin.interfaces import IInnerPage
 from pyams_skin.layer import IPyAMSLayer
@@ -77,11 +79,23 @@
 
     @property
     def values(self):
+
+        def get_item(result):
+            parent = get_parent(result, IWfSharedContent)
+            if parent is not None:
+                return IWorkflowVersions(parent).get_last_versions(count=1)[0]
+            parent = get_parent(result, IPortalTemplate)
+            if parent is None:
+                parent = get_parent(result, ISiteContainer)
+            if parent is None:
+                parent = self.request.root
+            return parent
+
         catalog = get_utility(ICatalog)
         oid = ISequentialIdInfo(self.context).hex_oid
         params = Or(Eq(catalog['link_reference'], oid),
                     Eq(catalog['link_references'], oid))
-        return unique(map(lambda x: IWorkflowVersions(get_parent(x, IWfSharedContent)).get_last_versions(count=1)[0],
+        return unique(map(get_item,
                           CatalogResultSet(CatalogQuery(catalog).query(params,
                                                                        sort_index='modified_date'))))
 
--- a/src/pyams_content/component/paragraph/audio.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/component/paragraph/audio.py	Wed Jun 27 16:42:01 2018 +0200
@@ -44,10 +44,9 @@
     icon_class = 'fa-volume-up'
     icon_hint = AUDIO_PARAGRAPH_NAME
 
-    body = FieldProperty(IAudioParagraph['body'])
+    data = FileProperty(IAudioParagraph['data'])
     description = FieldProperty(IAudioParagraph['description'])
     author = FieldProperty(IAudioParagraph['author'])
-    data = FileProperty(IAudioParagraph['data'])
     renderer = FieldProperty(IAudioParagraph['renderer'])
 
 
--- a/src/pyams_content/component/paragraph/interfaces/audio.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/component/paragraph/interfaces/audio.py	Wed Jun 27 16:42:01 2018 +0200
@@ -20,7 +20,7 @@
 
 # import packages
 from pyams_file.schema import AudioField
-from pyams_i18n.schema import I18nHTMLField, I18nTextField
+from pyams_i18n.schema import I18nTextField, I18nTextLineField
 from zope.schema import TextLine, Choice
 
 from pyams_content import _
@@ -38,8 +38,12 @@
 class IAudioParagraph(IBaseParagraph):
     """Audio paragraph"""
 
-    body = I18nHTMLField(title=_("Body"),
-                         required=False)
+    data = AudioField(title=_("Audio data"),
+                      description=_("Audio file content"),
+                      required=True)
+
+    title = I18nTextLineField(title=_("Legend"),
+                              required=False)
 
     description = I18nTextField(title=_("Description"),
                                 description=_("File description displayed by front-office template"),
@@ -49,10 +53,6 @@
                       description=_("Name of document's author"),
                       required=False)
 
-    data = AudioField(title=_("Audio data"),
-                      description=_("Audio file content"),
-                      required=True)
-
     renderer = Choice(title=_("Audio template"),
                       description=_("Presentation template used for this audio file"),
                       vocabulary=AUDIO_PARAGRAPH_RENDERERS,
--- a/src/pyams_content/component/paragraph/interfaces/keynumber.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/component/paragraph/interfaces/keynumber.py	Wed Jun 27 16:42:01 2018 +0200
@@ -16,62 +16,15 @@
 # import standard library
 
 # import interfaces
+from pyams_content.component.keynumber.interfaces import IKeyNumberContainerTarget
 from pyams_content.component.paragraph import IBaseParagraph
-from pyams_content.interfaces.container import IOrderedContainer
-from zope.annotation.interfaces import IAttributeAnnotatable
 
 # import packages
-from pyams_i18n.schema import I18nTextLineField
-from zope.container.constraints import containers, contains
-from zope.interface import Interface
 from zope.schema import Bool, Choice, TextLine
 
 from pyams_content import _
 
 
-KEYNUMBER_CONTAINER_KEY = 'pyams_content.keynumbers'
-
-
-class IKeyNumber(IAttributeAnnotatable):
-    """Base key number interface"""
-
-    containers('.IKeyNumberContainer')
-
-    visible = Bool(title=_("Visible?"),
-                   description=_("Is this key number visible in front-office?"),
-                   required=True,
-                   default=True)
-
-    number = TextLine(title=_("Number"),
-                      description=_("Key number value"),
-                      required=True)
-
-    label = I18nTextLineField(title=_('key-number-label', default="Header"),
-                              description=_("Small text to be displayed above number (according to selected "
-                                            "renderer)"),
-                              required=False)
-
-    text = I18nTextLineField(title=_("Associated text"),
-                             description=_("The way this text will be rendered depends on presentation template"),
-                             required=False)
-
-
-class IKeyNumberContainer(IOrderedContainer):
-    """Key numbers container interface"""
-
-    contains(IKeyNumber)
-
-    def append(self, value, notify=True):
-        """Append given key number to container"""
-
-    def get_visible_items(self):
-        """Get list of visible key numbers"""
-
-
-class IKeyNumberContainerTarget(Interface):
-    """Key numbers container target interface"""
-
-
 KEYNUMBER_PARAGRAPH_TYPE = 'KeyNumbers'
 KEYNUMBER_PARAGRAPH_NAME = _("Key numbers")
 KEYNUMBER_PARAGRAPH_RENDERERS = 'PyAMS.keynumbers.renderers'
--- a/src/pyams_content/component/paragraph/interfaces/video.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/component/paragraph/interfaces/video.py	Wed Jun 27 16:42:01 2018 +0200
@@ -20,7 +20,7 @@
 
 # import packages
 from pyams_file.schema import VideoField
-from pyams_i18n.schema import I18nHTMLField, I18nTextField
+from pyams_i18n.schema import I18nTextField, I18nTextLineField
 from zope.schema import TextLine, Choice
 
 from pyams_content import _
@@ -38,8 +38,12 @@
 class IVideoParagraph(IBaseParagraph):
     """Video paragraph"""
 
-    body = I18nHTMLField(title=_("Body"),
-                         required=False)
+    data = VideoField(title=_("Video data"),
+                      description=_("Video file content"),
+                      required=True)
+
+    title = I18nTextLineField(title=_("Legend"),
+                              required=False)
 
     description = I18nTextField(title=_("Description"),
                                 description=_("File description displayed by front-office template"),
@@ -49,10 +53,6 @@
                       description=_("Name of document's author"),
                       required=False)
 
-    data = VideoField(title=_("Video data"),
-                      description=_("Video file content"),
-                      required=True)
-
     renderer = Choice(title=_("Video template"),
                       description=_("Presentation template used for this video"),
                       vocabulary=VIDEO_PARAGRAPH_RENDERERS,
--- a/src/pyams_content/component/paragraph/keynumber.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/component/paragraph/keynumber.py	Wed Jun 27 16:42:01 2018 +0200
@@ -14,188 +14,27 @@
 
 
 # import standard library
-from persistent import Persistent
 
 # import interfaces
 from pyams_content.component.paragraph.interfaces import IParagraphFactory
-from pyams_content.component.paragraph.interfaces.keynumber import IKeyNumber, IKeyNumberContainer, \
-    IKeyNumberContainerTarget, KEYNUMBER_CONTAINER_KEY, IKeyNumberParagraph, KEYNUMBER_PARAGRAPH_TYPE, \
+from pyams_content.component.paragraph.interfaces.keynumber import IKeyNumberParagraph, KEYNUMBER_PARAGRAPH_TYPE, \
     KEYNUMBER_PARAGRAPH_RENDERERS, KEYNUMBER_PARAGRAPH_NAME
 from pyams_content.features.checker.interfaces import IContentChecker, MISSING_VALUE, MISSING_LANG_VALUE
-from pyams_form.interfaces.form import IFormContextPermissionChecker
 from pyams_i18n.interfaces import II18n, II18nManager, INegotiator
-from zope.lifecycleevent.interfaces import IObjectAddedEvent, IObjectModifiedEvent, IObjectRemovedEvent
-from zope.location.interfaces import ISublocations
-from zope.traversing.interfaces import ITraversable
 
 # import packages
-from pyams_catalog.utils import index_object
 from pyams_content.component.paragraph import BaseParagraph, BaseParagraphFactory, BaseParagraphContentChecker
-from pyams_content.features.checker import BaseContentChecker
 from pyams_content.features.renderer import RenderersVocabulary
-from pyams_utils.adapter import adapter_config, ContextAdapter, get_annotation_adapter
+from pyams_utils.adapter import adapter_config
 from pyams_utils.factory import factory_config
-from pyams_utils.registry import get_current_registry, get_utility, utility_config
+from pyams_utils.registry import get_utility, utility_config
 from pyams_utils.request import check_request
 from pyams_utils.traversing import get_parent
 from pyams_utils.vocabulary import vocabulary_config
-from pyramid.events import subscriber
-from zope.container.contained import Contained
-from zope.container.ordered import OrderedContainer
 from zope.interface import implementer
-from zope.lifecycleevent import ObjectCreatedEvent, ObjectModifiedEvent
-from zope.location import locate
 from zope.schema.fieldproperty import FieldProperty
 
 
-#
-# Key number class and adapters
-#
-
-@implementer(IKeyNumber)
-class KeyNumber(Persistent, Contained):
-    """Key number persistent class"""
-
-    visible = FieldProperty(IKeyNumber['visible'])
-    number = FieldProperty(IKeyNumber['number'])
-    label = FieldProperty(IKeyNumber['label'])
-    text = FieldProperty(IKeyNumber['text'])
-
-
-@adapter_config(context=IKeyNumber, provides=IFormContextPermissionChecker)
-class KeyNumberPermissionChecker(ContextAdapter):
-    """Key number permission checker"""
-
-    @property
-    def edit_permission(self):
-        content = get_parent(self.context, IKeyNumberContainerTarget)
-        return IFormContextPermissionChecker(content).edit_permission
-
-
-@subscriber(IObjectAddedEvent, context_selector=IKeyNumber)
-def handle_added_keynumber(event):
-    """Handle added key number"""
-    content = get_parent(event.object, IKeyNumberContainerTarget)
-    if content is not None:
-        get_current_registry().notify(ObjectModifiedEvent(content))
-
-
-@subscriber(IObjectModifiedEvent, context_selector=IKeyNumber)
-def handle_modified_keynumber(event):
-    """Handle modified key number"""
-    content = get_parent(event.object, IKeyNumberContainerTarget)
-    if content is not None:
-        get_current_registry().notify(ObjectModifiedEvent(content))
-
-
-@subscriber(IObjectRemovedEvent, context_selector=IKeyNumber)
-def handle_removed_keynumber(event):
-    """Handle removed key number"""
-    content = get_parent(event.object, IKeyNumberContainerTarget)
-    if content is not None:
-        get_current_registry().notify(ObjectModifiedEvent(content))
-
-
-@adapter_config(context=IKeyNumber, provides=IContentChecker)
-class KeyNumberContentChecker(BaseContentChecker):
-    """Key number content checker"""
-
-    @property
-    def label(self):
-        request = check_request()
-        return II18n(self.context).query_attribute('title', request=request)
-
-    def inner_check(self, request):
-        output = []
-        translate = request.localizer.translate
-        manager = get_parent(self.context, II18nManager)
-        if manager is not None:
-            langs = manager.get_languages()
-        else:
-            negotiator = get_utility(INegotiator)
-            langs = (negotiator.server_language, )
-        i18n = II18n(self.context)
-        for lang in langs:
-            for attr in ('label', 'text'):
-                value = i18n.get_attribute(attr, lang, request)
-                if not value:
-                    field_title = translate(IKeyNumber[attr].title)
-                    if len(langs) == 1:
-                        output.append(translate(MISSING_VALUE).format(field=field_title))
-                    else:
-                        output.append(translate(MISSING_LANG_VALUE).format(field=field_title, lang=lang))
-        return output
-
-
-#
-# Key numbers container classes and adapters
-#
-
-@implementer(IKeyNumberContainer)
-class KeyNumberContainer(OrderedContainer):
-    """Key numbers container"""
-
-    last_id = 1
-
-    def append(self, value, notify=True):
-        key = str(self.last_id)
-        if not notify:
-            # pre-locate key number item to avoid multiple notifications
-            locate(value, self, key)
-        self[key] = value
-        self.last_id += 1
-        if not notify:
-            # make sure that key number item is correctly indexed
-            index_object(value)
-
-    def get_visible_items(self):
-        return filter(lambda x: IKeyNumber(x).visible, self.values())
-
-
-@adapter_config(context=IKeyNumberContainerTarget, provides=IKeyNumberContainer)
-def keynumber_container_factory(target):
-    """Key number container factory"""
-    return get_annotation_adapter(target, KEYNUMBER_CONTAINER_KEY, KeyNumberContainer, name='++keynumbers++')
-
-
-@adapter_config(name='keynumbers', context=IKeyNumberContainerTarget, provides=ITraversable)
-class KeyNumberContainerNamespace(ContextAdapter):
-    """Key numbers container ++keynumbers++ namespace"""
-
-    def traverse(self, name, furtherpaath=None):
-        return IKeyNumberContainer(self.context)
-
-
-@adapter_config(name='keynumbers', context=IKeyNumberContainerTarget, provides=ISublocations)
-class KeyNumberContainerSublocations(ContextAdapter):
-    """Key numbers container sub-locations adapter"""
-
-    def sublocations(self):
-        return IKeyNumberContainer(self.context).values()
-
-
-@adapter_config(name='keynumbers', context=IKeyNumberContainerTarget, provides=IContentChecker)
-class KeyNumberContainerContentChecker(BaseContentChecker):
-    """Key numbers container content checker"""
-
-    label = KEYNUMBER_PARAGRAPH_NAME
-    sep = '\n'
-    weight = 200
-
-    def inner_check(self, request):
-        output = []
-        registry = request.registry
-        for keynumber in IKeyNumberContainer(self.context).values():
-            if not keynumber.visible:
-                continue
-            for name, checker in sorted(registry.getAdapters((keynumber, ), IContentChecker),
-                                        key=lambda x: x[1].weight):
-                output.append('- {0} ({1}):'.format(keynumber.number,
-                                                    II18n(keynumber).query_attribute('label', request=request) or '--'))
-                output.append(checker.get_check_output(request))
-        return output
-
-
 @implementer(IKeyNumberParagraph)
 @factory_config(provided=IKeyNumberParagraph)
 class KeyNumberParagraph(BaseParagraph):
--- a/src/pyams_content/component/paragraph/pictogram.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/component/paragraph/pictogram.py	Wed Jun 27 16:42:01 2018 +0200
@@ -78,7 +78,8 @@
     @volatile_property
     def pictogram(self):
         table = query_utility(IPictogramTable)
-        return table.get(self.pictogram_name)
+        if table is not None:
+            return table.get(self._pictogram_name)
 
 
 @adapter_config(context=IPictogramItem, provides=IFormContextPermissionChecker)
--- a/src/pyams_content/component/paragraph/video.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/component/paragraph/video.py	Wed Jun 27 16:42:01 2018 +0200
@@ -43,10 +43,9 @@
     icon_class = 'fa-film'
     icon_hint = VIDEO_PARAGRAPH_NAME
 
-    body = FieldProperty(IVideoParagraph['body'])
+    data = FileProperty(IVideoParagraph['data'])
     description = FieldProperty(IVideoParagraph['description'])
     author = FieldProperty(IVideoParagraph['author'])
-    data = FileProperty(IVideoParagraph['data'])
     renderer = FieldProperty(IVideoParagraph['renderer'])
 
 
--- a/src/pyams_content/component/paragraph/zmi/audio.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/component/paragraph/zmi/audio.py	Wed Jun 27 16:42:01 2018 +0200
@@ -31,10 +31,9 @@
 # import packages
 from pyams_content.component.paragraph.audio import AudioParagraph
 from pyams_content.component.paragraph.zmi import BaseParagraphAJAXAddForm, BaseParagraphAJAXEditForm, \
-    BaseParagraphAddMenu, BaseParagraphPropertiesEditForm, IParagraphEditFormButtons
+    BaseParagraphAddMenu, BaseParagraphPropertiesEditForm, IParagraphEditFormButtons, get_json_paragraph_refresh_event
 from pyams_content.features.renderer.zmi.widget import RendererFieldWidget
 from pyams_form.form import ajax_config
-from pyams_form.group import NamedWidgetsGroup
 from pyams_pagelet.pagelet import pagelet_config
 from pyams_skin.event import get_json_form_refresh_event, get_json_widget_refresh_event
 from pyams_utils.adapter import adapter_config
@@ -75,20 +74,6 @@
         super(AudioParagraphAddForm, self).updateWidgets(prefix)
         if 'description' in self.widgets:
             self.widgets['description'].widget_css_class = 'textarea'
-        if 'body' in self.widgets:
-            self.widgets['body'].label = ''
-            
-    def updateGroups(self):
-        self.add_group(NamedWidgetsGroup(self, 'body_group', self.widgets, ('body',),
-                                         bordered=False,
-                                         legend=_("HTML content"),
-                                         css_class='inner switcher padding-right-10 no-y-padding pull-left',
-                                         switch=True,
-                                         display_mode='auto'))
-        self.add_group(NamedWidgetsGroup(self, 'data_group', self.widgets,
-                                         ('description', 'author', 'data', 'renderer'),
-                                         bordered=False))
-        super(AudioParagraphAddForm, self).updateGroups()
 
     def create(self, data):
         return AudioParagraph()
@@ -118,21 +103,6 @@
         super(AudioParagraphPropertiesEditForm, self).updateWidgets(prefix)
         if 'description' in self.widgets:
             self.widgets['description'].widget_css_class = 'textarea'
-        if 'body' in self.widgets:
-            self.widgets['body'].label = ''
-            
-    def updateGroups(self):
-        self.add_group(NamedWidgetsGroup(self, 'body_group', self.widgets, ('body',),
-                                         bordered=False,
-                                         fieldset_class='margin-top-10 padding-y-5',
-                                         legend=_("HTML content"),
-                                         css_class='inner switcher padding-right-10 no-y-padding pull-left',
-                                         switch=True,
-                                         display_mode='auto'))
-        self.add_group(NamedWidgetsGroup(self, 'data_group', self.widgets,
-                                         ('description', 'author', 'data', 'renderer'),
-                                         bordered=False))
-        super(AudioParagraphPropertiesEditForm, self).updateGroups()
 
 
 @adapter_config(context=(IAudioParagraph, IPyAMSLayer), provides=IParagraphInnerEditor)
@@ -159,8 +129,12 @@
             ITransactionManager(self.context).get().commit()
             output.setdefault('events', []).append(
                 get_json_form_refresh_event(self.context, self.request, AudioParagraphPropertiesInnerEditForm))
-        elif 'renderer' in updated:
-            output.setdefault('events', []).append(
-                get_json_widget_refresh_event(self.context, self.request,
-                                              AudioParagraphPropertiesInnerEditForm, 'renderer'))
+        else:
+            if 'title' in updated:
+                output.setdefault('events', []).append(
+                    get_json_paragraph_refresh_event(self.context, self.request))
+            if 'renderer' in updated:
+                output.setdefault('events', []).append(
+                    get_json_widget_refresh_event(self.context, self.request,
+                                                  AudioParagraphPropertiesInnerEditForm, 'renderer'))
         return output
--- a/src/pyams_content/component/paragraph/zmi/container.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/component/paragraph/zmi/container.py	Wed Jun 27 16:42:01 2018 +0200
@@ -101,7 +101,6 @@
     def data_attributes(self):
         attributes = super(ParagraphContainerBaseTable, self).data_attributes
         attributes.setdefault('table', {}).update({
-            'id': self.id,
             'data-ams-plugins': 'pyams_content',
             'data-ams-plugin-pyams_content-src': get_resource_path(pyams_content),
             'data-ams-location': absolute_url(IParagraphContainer(self.context), self.request),
@@ -132,7 +131,7 @@
                     message = '{0}<br />{1}'.format(message,
                                                     translate(_("Check allowed paragraph types to be able to create "
                                                                 "new paragraphs.")))
-            return message
+            return self.renderEmptyTable(message)
         return super(ParagraphContainerBaseTable, self).render()
 
 
--- a/src/pyams_content/component/paragraph/zmi/frame.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/component/paragraph/zmi/frame.py	Wed Jun 27 16:42:01 2018 +0200
@@ -16,7 +16,7 @@
 # import standard library
 
 # import interfaces
-from pyams_content.component.association.interfaces import IAssociationTarget
+from pyams_content.component.association.interfaces import IAssociationContainerTarget
 from pyams_content.component.association.zmi.interfaces import IAssociationsParentForm
 from pyams_content.component.paragraph.interfaces import IParagraphContainerTarget, IParagraphContainer
 from pyams_content.component.paragraph.interfaces.frame import IFrameParagraph, FRAME_PARAGRAPH_TYPE
@@ -150,7 +150,7 @@
         output = super(self.__class__, self).get_ajax_output(changes)
         if 'body' in changes.get(IFrameParagraph, ()):
             # refresh associations count markers
-            parent = get_parent(self.context, IAssociationTarget)
+            parent = get_parent(self.context, IAssociationContainerTarget)
             output.setdefault('events', []).append(
                 get_json_paragraph_toolbar_refresh_event(parent, self.request,
                                                          ParagraphContainerTable, ParagraphTitleToolbarViewletManager))
--- a/src/pyams_content/component/paragraph/zmi/html.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/component/paragraph/zmi/html.py	Wed Jun 27 16:42:01 2018 +0200
@@ -16,7 +16,7 @@
 # import standard library
 
 # import interfaces
-from pyams_content.component.association.interfaces import IAssociationTarget
+from pyams_content.component.association.interfaces import IAssociationContainerTarget
 from pyams_content.component.association.zmi.interfaces import IAssociationsParentForm
 from pyams_content.component.paragraph.interfaces import IParagraphContainerTarget, IParagraphFactorySettings, \
     IParagraphContainer
@@ -223,7 +223,7 @@
         output = super(self.__class__, self).get_ajax_output(changes)
         if 'body' in changes.get(IHTMLParagraph, ()):
             # refresh associations count markers
-            parent = get_parent(self.context, IAssociationTarget)
+            parent = get_parent(self.context, IAssociationContainerTarget)
             output.setdefault('events', []).append(
                 get_json_paragraph_toolbar_refresh_event(parent, self.request))
             # refresh associations table
@@ -252,7 +252,7 @@
         output = super(self.__class__, self).get_ajax_output(changes)
         if 'body' in changes.get(IHTMLParagraph, ()):
             # refresh associations count markers
-            parent = get_parent(self.context, IAssociationTarget)
+            parent = get_parent(self.context, IAssociationContainerTarget)
             output.setdefault('events', []).append(
                 get_json_paragraph_toolbar_refresh_event(parent, self.request))
             # refresh associations table
--- a/src/pyams_content/component/paragraph/zmi/keynumber.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/component/paragraph/zmi/keynumber.py	Wed Jun 27 16:42:01 2018 +0200
@@ -14,60 +14,39 @@
 
 
 # import standard library
-import json
 
 # import interfaces
 from pyams_content.component.paragraph.interfaces import IParagraphContainerTarget, IParagraphContainer
-from pyams_content.component.paragraph.interfaces.keynumber import KEYNUMBER_PARAGRAPH_TYPE, IKeyNumberParagraph, \
-    IKeyNumberContainer, IKeyNumberContainerTarget, IKeyNumber
+from pyams_content.component.paragraph.interfaces.keynumber import KEYNUMBER_PARAGRAPH_TYPE, IKeyNumberParagraph
 from pyams_content.component.paragraph.zmi import IParagraphContainerView, IParagraphEditFormButtons
 from pyams_content.component.paragraph.zmi.interfaces import IParagraphInnerEditor
 from pyams_content.interfaces import MANAGE_CONTENT_PERMISSION
 from pyams_content.shared.common import IWfSharedContent
-from pyams_form.interfaces.form import IInnerForm, IInnerSubForm
+from pyams_form.interfaces.form import IInnerForm
 from pyams_i18n.interfaces import II18n
-from pyams_skin.interfaces.viewlet import IToolbarAddingMenu, IWidgetTitleViewletManager
+from pyams_skin.interfaces.viewlet import IToolbarAddingMenu
 from pyams_skin.layer import IPyAMSLayer
 from z3c.form.interfaces import INPUT_MODE
-from z3c.table.interfaces import IValues, IColumn
 
 # import packages
-from pyams_content.component.paragraph.keynumber import KeyNumberParagraph, KeyNumber
+from pyams_content.component.keynumber.zmi import IKeyNumbersParentForm
+from pyams_content.component.paragraph.keynumber import KeyNumberParagraph
 from pyams_content.component.paragraph.zmi import BaseParagraphAddMenu, BaseParagraphAJAXAddForm, \
     BaseParagraphPropertiesEditForm, BaseParagraphAJAXEditForm
 from pyams_content.features.renderer.zmi.widget import RendererFieldWidget
-from pyams_form.form import AJAXAddForm, ajax_config
-from pyams_form.security import ProtectedFormObjectMixin
-from pyams_i18n.column import I18nAttrColumn
+from pyams_form.form import ajax_config
 from pyams_pagelet.pagelet import pagelet_config
-from pyams_skin.container import switch_element_visibility
-from pyams_skin.event import get_json_widget_refresh_event, get_json_switched_table_refresh_event, \
-    get_json_table_row_refresh_event
-from pyams_skin.table import BaseTable, SorterColumn, I18nColumn, VisibilitySwitcherColumn, TrashColumn
-from pyams_skin.viewlet.toolbar import ToolbarAction
-from pyams_utils.adapter import adapter_config, ContextRequestViewAdapter
+from pyams_skin.event import get_json_widget_refresh_event
+from pyams_utils.adapter import adapter_config
 from pyams_utils.traversing import get_parent
-from pyams_utils.url import absolute_url
 from pyams_viewlet.viewlet import viewlet_config
-from pyams_zmi.form import AdminDialogAddForm, AdminDialogEditForm
-from pyams_zmi.zmi.table import InnerTableView
-from pyramid.decorator import reify
-from pyramid.view import view_config
+from pyams_zmi.form import AdminDialogAddForm
 from z3c.form import field, button
-from z3c.table.column import GetAttrColumn
 from zope.interface import implementer, Interface
 
 from pyams_content import _
 
 
-class IKeyNumbersView(Interface):
-    """Key numbers view marker interface"""
-
-
-class IKeyNumbersParentForm(Interface):
-    """Key numbers parent form marker interface"""
-
-
 @viewlet_config(name='add-keynumber-paragraph.menu', context=IParagraphContainerTarget, view=IParagraphContainerView,
                 layer=IPyAMSLayer, manager=IToolbarAddingMenu, weight=600)
 class KeyNumberParagraphAddMenu(BaseParagraphAddMenu):
@@ -103,6 +82,7 @@
                 permission=MANAGE_CONTENT_PERMISSION)
 @ajax_config(name='properties.json', context=IKeyNumberParagraph, layer=IPyAMSLayer,
              base=BaseParagraphAJAXEditForm)
+@implementer(IKeyNumbersParentForm)
 class KeyNumberParagraphPropertiesEditForm(BaseParagraphPropertiesEditForm):
     """Key number paragraph properties edit form"""
 
@@ -145,191 +125,3 @@
             output.setdefault('events', []).append(
                 get_json_widget_refresh_event(self.context, self.request, KeyNumberParagraphInnerEditForm, 'renderer'))
         return output
-
-
-#
-# Key number items table view
-#
-
-class KeyNumbersTable(ProtectedFormObjectMixin, BaseTable):
-    """Key numbers view inner table"""
-
-    prefix = 'keynumbers'
-
-    hide_header = True
-    sortOn = None
-
-    @property
-    def cssClasses(self):
-        classes = ['table', 'table-bordered', 'table-striped', 'table-hover', 'table-tight']
-        permission = self.permission
-        if (not permission) or self.request.has_permission(permission, self.context):
-            classes.append('table-dnd')
-        return {'table': ' '.join(classes)}
-
-    @property
-    def data_attributes(self):
-        attributes = super(KeyNumbersTable, self).data_attributes
-        attributes['table'] = {
-            'id': self.id,
-            'data-ams-location': absolute_url(IKeyNumberContainer(self.context), self.request),
-            'data-ams-tablednd-drag-handle': 'td.sorter',
-            'data-ams-tablednd-drop-target': 'set-keynumbers-order.json',
-            'data-ams-visibility-switcher': 'switch-keynumber-visibility.json'
-        }
-        return attributes
-
-    @reify
-    def values(self):
-        return list(super(KeyNumbersTable, self).values)
-
-
-@adapter_config(context=(IKeyNumberContainerTarget, IPyAMSLayer, KeyNumbersTable), provides=IValues)
-class KeyNumbersTableValuesAdapter(ContextRequestViewAdapter):
-    """Key numbers table values adapter"""
-
-    @property
-    def values(self):
-        return IKeyNumberContainer(self.context).values()
-
-
-@adapter_config(name='sorter', context=(IKeyNumberContainerTarget, IPyAMSLayer, KeyNumbersTable), provides=IColumn)
-class KeyNumbersTableSorterColumn(ProtectedFormObjectMixin, SorterColumn):
-    """Key numbers table sorter column"""
-
-
-@view_config(name='set-keynumbers-order.json', context=IKeyNumberContainer, request_type=IPyAMSLayer,
-             permission=MANAGE_CONTENT_PERMISSION, renderer='json', xhr=True)
-def set_keynumbers_order(request):
-    """Update key numbers order"""
-    order = list(map(str, json.loads(request.params.get('names'))))
-    request.context.updateOrder(order)
-    return {'status': 'success'}
-
-
-@adapter_config(name='show-hide', context=(IKeyNumberContainerTarget, IPyAMSLayer, KeyNumbersTable),
-                provides=IColumn)
-class KeyNumbersTableShowHideColumn(ProtectedFormObjectMixin, VisibilitySwitcherColumn):
-    """Key numbers container visibility switcher column"""
-
-
-@view_config(name='switch-keynumber-visibility.json', context=IKeyNumberContainer, request_type=IPyAMSLayer,
-             permission=MANAGE_CONTENT_PERMISSION, renderer='json', xhr=True)
-def switch_keynumber_visibility(request):
-    """Switch key number visibility"""
-    return switch_element_visibility(request, IKeyNumberContainer)
-
-
-@adapter_config(name='name', context=(IKeyNumberContainerTarget, IPyAMSLayer, KeyNumbersTable), provides=IColumn)
-class KeyNumbersTableNameColumn(I18nColumn, GetAttrColumn):
-    """Key numbers table number column"""
-
-    _header = _("Number")
-    attrName = 'number'
-    weight = 10
-
-
-@adapter_config(name='label', context=(IKeyNumberContainerTarget, IPyAMSLayer, KeyNumbersTable), provides=IColumn)
-class KeyNumbersTableLabelColumn(I18nColumn, I18nAttrColumn):
-    """Key numbers table label column"""
-
-    _header = _('key-number-label', default="Header")
-    attrName = 'label'
-    weight = 20
-
-    def getValue(self, obj):
-        return super(KeyNumbersTableLabelColumn, self).getValue(obj) or '--'
-
-
-@adapter_config(name='text', context=(IKeyNumberContainerTarget, IPyAMSLayer, KeyNumbersTable), provides=IColumn)
-class KeyNumbersTableTextColumn(I18nColumn, I18nAttrColumn):
-    """Key numbers table text column"""
-
-    _header = _("Associated text")
-    attrName = 'text'
-    weight = 30
-
-    def getValue(self, obj):
-        return super(KeyNumbersTableTextColumn, self).getValue(obj) or '--'
-
-
-@adapter_config(name='trash', context=(IKeyNumberContainerTarget, IPyAMSLayer, KeyNumbersTable), provides=IColumn)
-class KeyNumbersTableTrashColumn(ProtectedFormObjectMixin, TrashColumn):
-    """Key numbers table trash column"""
-
-
-@adapter_config(name='keynumbers', context=(IKeyNumberContainerTarget, IPyAMSLayer, IKeyNumbersParentForm),
-                provides=IInnerSubForm)
-@implementer(IKeyNumbersView)
-class KeyNumbersView(InnerTableView):
-    """Key numbers view"""
-
-    title = _("Key numbers")
-
-    table_class = KeyNumbersTable
-    weight = 110
-
-
-#
-# Key numbers forms
-#
-
-@viewlet_config(name='add-keynumber.action', context=IKeyNumberContainerTarget, layer=IPyAMSLayer, view=IKeyNumbersView,
-                manager=IWidgetTitleViewletManager, permission=MANAGE_CONTENT_PERMISSION, weight=1)
-class KeyNumberAddAction(ToolbarAction):
-    """Key number add action"""
-
-    label = _("Add keynumber")
-    label_css_class = 'fa fa-fw fa-plus'
-    url = 'add-keynumber.html'
-    modal_target = True
-
-
-@pagelet_config(name='add-keynumber.html', context=IKeyNumberContainerTarget, layer=IPyAMSLayer,
-                permission=MANAGE_CONTENT_PERMISSION)
-@ajax_config(name='add-keynumber.json', context=IParagraphContainerTarget, layer=IPyAMSLayer,
-             base=AJAXAddForm)
-class KeyNumberAddForm(AdminDialogAddForm):
-    """Key number add form"""
-
-    legend = _("Add new keynumber")
-    icon_css_class = 'fa fa-fw fa-dashboard'
-
-    fields = field.Fields(IKeyNumber).omit('__parent__', '__name__', 'visible')
-    edit_permission = MANAGE_CONTENT_PERMISSION
-
-    def create(self, data):
-        return KeyNumber()
-
-    def add(self, object):
-        IKeyNumberContainer(self.context).append(object)
-
-    def get_ajax_output(self, changes):
-        return {
-            'status': 'success',
-            'message': self.request.localizer.translate(_("Key number was correctly added")),
-            'events': [get_json_switched_table_refresh_event(self.context, self.request, KeyNumbersTable), ]
-        }
-
-
-@pagelet_config(name='properties.html', context=IKeyNumber, layer=IPyAMSLayer, permission=MANAGE_CONTENT_PERMISSION)
-@ajax_config(name='properties.json', context=IKeyNumber, layer=IPyAMSLayer)
-class KeyNumberPropertiesEditForm(AdminDialogEditForm):
-    """Key number properties edit form"""
-
-    prefix = 'keynumber_properties.'
-
-    legend = _("Edit keynumber properties")
-    icon_css_class = 'fa fa-fw fa-dashboard'
-
-    fields = field.Fields(IKeyNumber).omit('__parent__', '__name__', 'visible')
-    edit_permission = MANAGE_CONTENT_PERMISSION
-
-    def get_ajax_output(self, changes):
-        output = super(self.__class__, self).get_ajax_output(changes)
-        updated = changes.get(IKeyNumber, ())
-        if updated:
-            target = get_parent(self.context, IKeyNumberContainerTarget)
-            output.setdefault('events', []).append(
-                get_json_table_row_refresh_event(target, self.request, KeyNumbersTable, self.context))
-        return output
--- a/src/pyams_content/component/paragraph/zmi/milestone.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/component/paragraph/zmi/milestone.py	Wed Jun 27 16:42:01 2018 +0200
@@ -304,7 +304,7 @@
 
 @pagelet_config(name='add-milestone.html', context=IMilestoneContainerTarget, layer=IPyAMSLayer,
                 permission=MANAGE_CONTENT_PERMISSION)
-@ajax_config(name='add-milestone.json', context=IParagraphContainerTarget, layer=IPyAMSLayer,
+@ajax_config(name='add-milestone.json', context=IMilestoneContainerTarget, layer=IPyAMSLayer,
              base=AJAXAddForm)
 class MilestoneAddForm(AdminDialogAddForm):
     """Milestone add form"""
--- a/src/pyams_content/component/paragraph/zmi/pictogram.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/component/paragraph/zmi/pictogram.py	Wed Jun 27 16:42:01 2018 +0200
@@ -23,13 +23,11 @@
 from pyams_content.component.paragraph.zmi import IParagraphContainerView, IParagraphEditFormButtons
 from pyams_content.component.paragraph.zmi.interfaces import IParagraphInnerEditor
 from pyams_content.interfaces import MANAGE_CONTENT_PERMISSION
-from pyams_content.reference.pictograms.interfaces import IPictogramTable
 from pyams_content.shared.common import IWfSharedContent
 from pyams_form.interfaces.form import IInnerForm, IInnerSubForm
 from pyams_i18n.interfaces import II18n
 from pyams_skin.interfaces.viewlet import IToolbarAddingMenu, IWidgetTitleViewletManager
 from pyams_skin.layer import IPyAMSLayer
-from pyams_utils.interfaces.data import IObjectData
 from pyams_zmi.interfaces import IPropertiesEditForm
 from z3c.form.interfaces import INPUT_MODE, IDataExtractedEvent
 from z3c.table.interfaces import IValues, IColumn
@@ -40,6 +38,8 @@
 from pyams_content.component.paragraph.zmi import BaseParagraphAddMenu, BaseParagraphAJAXAddForm, \
     BaseParagraphPropertiesEditForm, BaseParagraphAJAXEditForm
 from pyams_content.features.renderer.zmi.widget import RendererFieldWidget
+from pyams_content.reference.pictograms.zmi.widget import PictogramSelectFieldWidget
+from pyams_file.zmi.image import render_image
 from pyams_form.form import AJAXAddForm, ajax_config
 from pyams_form.security import ProtectedFormObjectMixin
 from pyams_i18n.column import I18nAttrColumn
@@ -50,7 +50,6 @@
 from pyams_skin.table import BaseTable, SorterColumn, I18nColumn, TrashColumn, VisibilitySwitcherColumn
 from pyams_skin.viewlet.toolbar import ToolbarAction
 from pyams_utils.adapter import adapter_config, ContextRequestViewAdapter
-from pyams_utils.registry import query_utility
 from pyams_utils.text import get_text_start
 from pyams_utils.traversing import get_parent
 from pyams_utils.url import absolute_url
@@ -229,7 +228,7 @@
         if pictogram is not None:
             image = II18n(pictogram).query_attribute('image', request=self.request)
             if image:
-                return '<img src="{0}" />'.format(absolute_url(image, self.request, '++thumb++48x48'))
+                return render_image(image, 48, 48, self.request)
         return '--'
 
 
@@ -273,7 +272,7 @@
         value = super(PictogramsTableBodyColumn, self).getValue(obj)
         if not value:
             return BaseParagraph.empty_title
-        return get_text_start(value, 40, 10)
+        return get_text_start(value, 80, 10)
 
 
 @adapter_config(name='trash', context=(IPictogramContainerTarget, IPyAMSLayer, PictogramsTable), provides=IColumn)
@@ -319,26 +318,12 @@
     icon_css_class = 'fa fa-fw fa-arrow-h'
 
     fields = field.Fields(IPictogramItem).omit('__parent__', '__name__', 'visible')
+    fields['pictogram_name'].widgetFactory = PictogramSelectFieldWidget
+
     edit_permission = MANAGE_CONTENT_PERMISSION
 
     def updateWidgets(self, prefix=None):
         super(PictogramAddForm, self).updateWidgets(prefix)
-        if 'pictogram_name' in self.widgets:
-            pictograms = query_utility(IPictogramTable)
-            if pictograms is not None:
-                label_id = '{0}_pictogram_label'.format(self.id)
-                widget = self.widgets['pictogram_name']
-                widget.after_widget_notice = '<span id="{0}" class="text-info">{1}</span>'.format(
-                    label_id,
-                    self.request.localizer.translate(_("Default header: --")))
-                widget.object_data = {
-                    'ams-change-handler': 'MyAMS.helpers.select2ChangeHelper',
-                    'ams-stop-propagation': 'true',
-                    'ams-select2-helper-type': 'html',
-                    'ams-select2-helper-url': absolute_url(pictograms, self.request, 'get-pictogram-header.html'),
-                    'ams-select2-helper-target': '#{0}'.format(label_id)
-                }
-                alsoProvides(widget, IObjectData)
         if 'body' in self.widgets:
             self.widgets['body'].widget_css_class = 'textarea height-100'
 
@@ -375,32 +360,12 @@
     icon_css_class = 'fa fa-fw fa-linode'
 
     fields = field.Fields(IPictogramItem).omit('__parent__', '__name__', 'visible')
+    fields['pictogram_name'].widgetFactory = PictogramSelectFieldWidget
+
     edit_permission = MANAGE_CONTENT_PERMISSION
 
     def updateWidgets(self, prefix=None):
         super(PictogramPropertiesEditForm, self).updateWidgets(prefix)
-        if 'pictogram_name' in self.widgets:
-            pictograms = query_utility(IPictogramTable)
-            if pictograms is not None:
-                pictogram = pictograms.get(self.context.pictogram_name)
-                label_id = '{0}_pictogram_label'.format(self.id)
-                widget = self.widgets['pictogram_name']
-                widget.required = True
-                if pictogram is None:
-                    widget.after_widget_notice = '<span id="{0}" class="text-info">{1}</span>'.format(label_id, '--')
-                else:
-                    widget.after_widget_notice = '<span id="{0}" class="text-info">{1}</span>'.format(
-                        label_id,
-                        self.request.localizer.translate(_("Default header: {0}")).format(
-                            II18n(pictogram).query_attribute('header', request=self.request) or '--'))
-                widget.object_data = {
-                    'ams-change-handler': 'MyAMS.helpers.select2ChangeHelper',
-                    'ams-stop-propagation': 'true',
-                    'ams-select2-helper-type': 'html',
-                    'ams-select2-helper-url': absolute_url(pictograms, self.request, 'get-pictogram-header.html'),
-                    'ams-select2-helper-target': '#{0}'.format(label_id)
-                }
-                alsoProvides(widget, IObjectData)
         if 'body' in self.widgets:
             self.widgets['body'].widget_css_class = 'textarea height-100'
 
--- a/src/pyams_content/component/paragraph/zmi/preview.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/component/paragraph/zmi/preview.py	Wed Jun 27 16:42:01 2018 +0200
@@ -18,7 +18,7 @@
 # import interfaces
 from pyams_content.component.paragraph.interfaces import IParagraphContainerTarget, IParagraphContainer, \
     IParagraphRenderer
-from pyams_content.features.renderer.interfaces import IContentRenderer
+from pyams_content.features.renderer.interfaces import ISharedContentRenderer
 from pyams_skin.layer import IPyAMSLayer
 
 # import packages
@@ -27,7 +27,7 @@
 
 
 @adapter_config(name='paragraphs-render', context=(IParagraphContainerTarget, IPyAMSLayer),
-                provides=IContentRenderer)
+                provides=ISharedContentRenderer)
 class ParagraphsContainerRenderer(BaseContentRenderer):
     """Paragraphs container renderer"""
 
@@ -37,7 +37,8 @@
         super(ParagraphsContainerRenderer, self).__init__(context, request)
         paragraphs = [para for para in IParagraphContainer(self.context).values()
                       if para.visible]
-        self.renderers = [self.request.registry.queryMultiAdapter((paragraph, self.request), IParagraphRenderer)
+        registry = self.request.registry
+        self.renderers = [registry.queryMultiAdapter((paragraph, self.request), IParagraphRenderer)
                           for paragraph in paragraphs]
 
     def update(self):
--- a/src/pyams_content/component/paragraph/zmi/video.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/component/paragraph/zmi/video.py	Wed Jun 27 16:42:01 2018 +0200
@@ -31,16 +31,14 @@
 # import packages
 from pyams_content.component.paragraph.video import VideoParagraph
 from pyams_content.component.paragraph.zmi import BaseParagraphAJAXAddForm, BaseParagraphAJAXEditForm, \
-    BaseParagraphAddMenu, BaseParagraphPropertiesEditForm, IParagraphEditFormButtons
+    BaseParagraphAddMenu, BaseParagraphPropertiesEditForm, IParagraphEditFormButtons, get_json_paragraph_refresh_event
 from pyams_content.features.renderer.zmi.widget import RendererFieldWidget
 from pyams_form.form import ajax_config
-from pyams_form.group import NamedWidgetsGroup
 from pyams_pagelet.pagelet import pagelet_config
 from pyams_skin.event import get_json_form_refresh_event, get_json_widget_refresh_event
 from pyams_utils.adapter import adapter_config
 from pyams_viewlet.viewlet import viewlet_config
 from pyams_zmi.form import AdminDialogAddForm
-from pyramid.view import view_config
 from z3c.form import field, button
 from zope.interface import implementer
 
@@ -76,20 +74,6 @@
         super(VideoParagraphAddForm, self).updateWidgets(prefix)
         if 'description' in self.widgets:
             self.widgets['description'].widget_css_class = 'textarea'
-        if 'body' in self.widgets:
-            self.widgets['body'].label = ''
-            
-    def updateGroups(self):
-        self.add_group(NamedWidgetsGroup(self, 'body_group', self.widgets, ('body',),
-                                         bordered=False,
-                                         legend=_("HTML content"),
-                                         css_class='inner switcher padding-right-10 no-y-padding pull-left',
-                                         switch=True,
-                                         display_mode='auto'))
-        self.add_group(NamedWidgetsGroup(self, 'data_group', self.widgets,
-                                         ('description', 'author', 'data', 'renderer'),
-                                         bordered=False))
-        super(VideoParagraphAddForm, self).updateGroups()
 
     def create(self, data):
         return VideoParagraph()
@@ -120,21 +104,6 @@
         super(VideoParagraphPropertiesEditForm, self).updateWidgets(prefix)
         if 'description' in self.widgets:
             self.widgets['description'].widget_css_class = 'textarea'
-        if 'body' in self.widgets:
-            self.widgets['body'].label = ''
-            
-    def updateGroups(self):
-        self.add_group(NamedWidgetsGroup(self, 'body_group', self.widgets, ('body',),
-                                         bordered=False,
-                                         fieldset_class='margin-top-10 padding-y-5',
-                                         legend=_("HTML content"),
-                                         css_class='inner switcher padding-right-10 no-y-padding pull-left',
-                                         switch=True,
-                                         display_mode='auto'))
-        self.add_group(NamedWidgetsGroup(self, 'data_group', self.widgets,
-                                         ('description', 'author', 'data', 'renderer'),
-                                         bordered=False))
-        super(VideoParagraphPropertiesEditForm, self).updateGroups()
 
 
 @adapter_config(context=(IVideoParagraph, IPyAMSLayer), provides=IParagraphInnerEditor)
@@ -161,8 +130,12 @@
             ITransactionManager(self.context).get().commit()
             output.setdefault('events', []).append(
                 get_json_form_refresh_event(self.context, self.request, VideoParagraphPropertiesInnerEditForm))
-        elif 'renderer' in updated:
-            output.setdefault('events', []).append(
-                get_json_widget_refresh_event(self.context, self.request,
-                                              VideoParagraphPropertiesInnerEditForm, 'renderer'))
+        else:
+            if 'title' in updated:
+                output.setdefault('events', []).append(
+                    get_json_paragraph_refresh_event(self.context, self.request))
+            if 'renderer' in updated:
+                output.setdefault('events', []).append(
+                    get_json_widget_refresh_event(self.context, self.request,
+                                                  VideoParagraphPropertiesInnerEditForm, 'renderer'))
         return output
--- a/src/pyams_content/component/theme/zmi/__init__.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/component/theme/zmi/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -88,7 +88,7 @@
 
     def get_subterms(self, term):
         for subterm in term.specifics:
-            if self.extract_name in subterm.extracts:
+            if (not self.extract_name) or (self.extract_name in subterm.extracts):
                 yield subterm
                 for another in self.get_subterms(subterm):
                     yield another
--- a/src/pyams_content/component/video/interfaces/__init__.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/component/video/interfaces/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -21,7 +21,7 @@
 from zope.contentprovider.interfaces import IContentProvider
 
 # import packages
-from pyams_i18n.schema import I18nHTMLField, I18nTextField
+from pyams_i18n.schema import I18nTextField, I18nTextLineField
 from zope.interface import Interface, Attribute
 from zope.schema import Choice, TextLine
 
@@ -72,8 +72,8 @@
 class IExternalVideoParagraph(IExternalVideo, IBaseParagraph):
     """External video paragraph"""
 
-    body = I18nHTMLField(title=_("Body"),
-                         required=False)
+    title = I18nTextLineField(title=_("Legend"),
+                              required=False)
 
     renderer = Choice(title=_("Video template"),
                       description=_("Presentation template used for this video"),
--- a/src/pyams_content/component/video/paragraph.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/component/video/paragraph.py	Wed Jun 27 16:42:01 2018 +0200
@@ -44,7 +44,6 @@
     icon_class = 'fa-youtube-play'
     icon_hint = EXTERNAL_VIDEO_PARAGRAPH_NAME
 
-    body = FieldProperty(IExternalVideoParagraph['body'])
     renderer = FieldProperty(IExternalVideoParagraph['renderer'])
 
 
--- a/src/pyams_content/component/video/zmi/paragraph.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/component/video/zmi/paragraph.py	Wed Jun 27 16:42:01 2018 +0200
@@ -76,15 +76,13 @@
     dialog_class = 'modal-large'
     icon_css_class = 'fa fa-fw fa-youtube-play'
 
-    fields = field.Fields(IExternalVideoParagraph).omit('__parent__', '__name__', 'visible')
+    fields = field.Fields(IExternalVideoParagraph).select('title', 'description', 'author', 'renderer', 'provider_name')
     edit_permission = MANAGE_CONTENT_PERMISSION
 
     def updateWidgets(self, prefix=None):
         super(ExternalVideoParagraphAddForm, self).updateWidgets(prefix)
         if 'description' in self.widgets:
             self.widgets['description'].widget_css_class = 'textarea'
-        if 'body' in self.widgets:
-            self.widgets['body'].label = ''
         if 'provider_name' in self.widgets:
             widget = self.widgets['provider_name']
             widget.object_data = {
@@ -98,19 +96,6 @@
             }
             alsoProvides(widget, IObjectData)
 
-    def updateGroups(self):
-        if 'body' in self.widgets:
-            self.add_group(NamedWidgetsGroup(self, 'body_group', self.widgets, ('body',),
-                                             bordered=False,
-                                             legend=_("HTML content"),
-                                             css_class='inner switcher padding-right-10 no-y-padding pull-left',
-                                             switch=True,
-                                             display_mode='auto'))
-            self.add_group(NamedWidgetsGroup(self, 'data_group', self.widgets,
-                                             ('description', 'author', 'renderer', 'provider_name'),
-                                             bordered=False))
-        super(ExternalVideoParagraphAddForm, self).updateGroups()
-
     def create(self, data):
         return ExternalVideoParagraph()
 
@@ -211,7 +196,8 @@
 
     @property
     def fields(self):
-        fields = field.Fields(IExternalVideoParagraph).omit('__parent__', '__name__', 'visible')
+        fields = field.Fields(IExternalVideoParagraph).select('title', 'description', 'author', 'renderer',
+                                                              'provider_name')
         fields['renderer'].widgetFactory = RendererFieldWidget
         provider = self.context.get_provider()
         if provider is not None:
@@ -222,23 +208,10 @@
         super(ExternalVideoParagraphPropertiesEditForm, self).updateWidgets(prefix)
         if 'description' in self.widgets:
             self.widgets['description'].widget_css_class = 'textarea'
-        if 'body' in self.widgets:
-            self.widgets['body'].label = ''
         if 'provider_name' in self.widgets:
             self.widgets['provider_name'].mode = DISPLAY_MODE
 
     def updateGroups(self):
-        if 'body' in self.widgets:
-            self.add_group(NamedWidgetsGroup(self, 'body_group', self.widgets, ('body',),
-                                             bordered=False,
-                                             fieldset_class='margin-top-10 padding-y-5',
-                                             legend=_("HTML content"),
-                                             css_class='inner switcher padding-right-10 no-y-padding pull-left',
-                                             switch=True,
-                                             display_mode='auto'))
-            self.add_group(NamedWidgetsGroup(self, 'data_group', self.widgets,
-                                             ('description', 'author', 'renderer', 'provider_name'),
-                                             bordered=False))
         if 'provider_name' in self.widgets:
             provider = self.context.get_provider()
             if provider is not None:
@@ -261,7 +234,7 @@
 
     def get_ajax_output(self, changes):
         output = super(self.__class__, self).get_ajax_output(changes)
-        if 'title' in changes.get(IBaseParagraph, ()):
+        if 'title' in changes.get(IExternalVideoParagraph, ()):
             output.setdefault('events', []).append(
                 get_json_paragraph_refresh_event(self.context, self.request))
         return output
--- a/src/pyams_content/features/alert/zmi/container.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/features/alert/zmi/container.py	Wed Jun 27 16:42:01 2018 +0200
@@ -71,7 +71,6 @@
     def data_attributes(self):
         attributes = super(AlertContainerTable, self).data_attributes
         attributes.setdefault('table', {}).update({
-            'id': self.id,
             'data-ams-plugins': 'pyams_content',
             'data-ams-plugin-pyams_content-src': get_resource_path(pyams_content),
             'data-ams-location': absolute_url(IAlertContainer(self.context), self.request),
--- a/src/pyams_content/features/footer/skin/__init__.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/features/footer/skin/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -19,10 +19,12 @@
 from pyams_content.features.footer.interfaces import IFooterTarget, IFooterRenderer, IFooterSettings
 from pyams_content.features.renderer.interfaces import HIDDEN_RENDERER_NAME
 from pyams_skin.layer import IPyAMSLayer
+from pyams_utils.interfaces.url import DISPLAY_CONTEXT
 
 # import packages
 from pyams_content.features.renderer.skin import BaseContentRenderer
 from pyams_utils.adapter import adapter_config
+from pyams_utils.traversing import get_parent
 from pyramid.decorator import reify
 
 from pyams_content import _
@@ -35,7 +37,11 @@
     def settings(self):
         if self.settings_interface is None:
             return None
-        settings = IFooterSettings(self.context)
+        context = self.request.annotations.get(DISPLAY_CONTEXT)
+        if context is None:
+            context = self.context
+        target = get_parent(context, IFooterTarget)
+        settings = IFooterSettings(target)
         while settings.inherit:
             settings = IFooterSettings(settings.parent)
         return settings.settings
--- a/src/pyams_content/features/footer/zmi/__init__.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/features/footer/zmi/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -19,22 +19,26 @@
 from pyams_content.features.footer.interfaces import IFooterTarget, IFooterSettings, IFooterRenderer, \
     IFooterRendererSettings
 from pyams_form.interfaces.form import IWidgetForm, IUncheckedEditFormButtons, IInnerSubForm, \
-    IWidgetsSuffixViewletsManager
+    IWidgetsSuffixViewletsManager, IFormHelp
 from pyams_portal.interfaces import MANAGE_TEMPLATE_PERMISSION
 from pyams_portal.zmi.interfaces import IPortalContextTemplatePropertiesMenu
 from pyams_skin.interfaces import IInnerPage
 from pyams_skin.layer import IPyAMSLayer
 from pyams_utils.interfaces.data import IObjectData
 from pyams_utils.interfaces.inherit import IInheritInfo
+from pyams_zmi.layer import IAdminLayer
 from z3c.form.interfaces import INPUT_MODE
 
 # import packages
-from pyams_form.form import ajax_config
+from pyams_content.skin import pyams_content
+from pyams_form.form import AJAXEditForm
 from pyams_form.group import NamedWidgetsGroup
+from pyams_form.help import FormHelp
 from pyams_pagelet.pagelet import pagelet_config
 from pyams_skin.viewlet.menu import MenuItem
 from pyams_template.template import template_config
 from pyams_utils.adapter import adapter_config
+from pyams_utils.fanstatic import get_resource_path
 from pyams_utils.url import absolute_url
 from pyams_viewlet.viewlet import viewlet_config, Viewlet
 from pyams_zmi.form import AdminEditForm, InnerAdminEditForm
@@ -63,8 +67,6 @@
 
 @pagelet_config(name='footer-settings.html', context=IFooterTarget, layer=IPyAMSLayer,
                 permission=MANAGE_TEMPLATE_PERMISSION)
-@ajax_config(name='footer-settings.json', context=IFooterTarget, layer=IPyAMSLayer,
-             permission=MANAGE_TEMPLATE_PERMISSION)
 @implementer(IWidgetForm, IInnerPage)
 class FooterSettingsEditForm(AdminEditForm):
     """Footer settings edit form"""
@@ -87,6 +89,8 @@
             fields = field.Fields(Interface)
         return fields
 
+    edit_permission = MANAGE_TEMPLATE_PERMISSION
+
     @property
     def buttons(self):
         if self.mode == INPUT_MODE:
@@ -94,6 +98,8 @@
         else:
             return button.Buttons(Interface)
 
+    ajax_handler = 'footer-settings.json'
+
     def updateGroups(self):
         if self.getContent().can_inherit:
             group = NamedWidgetsGroup(self, 'footer', self.widgets,
@@ -104,19 +110,49 @@
                                       checkbox_switch=True,
                                       checkbox_mode='disable',
                                       checkbox_field=IFooterSettings['no_inherit'])
+            if self.mode == INPUT_MODE:
+                group.object_data = {
+                    'ams-plugins': 'pyams_content',
+                    'ams-plugin-pyams_content-src': get_resource_path(pyams_content),
+                    'ams-plugin-pyams_content-async': False,
+                    'ams-events-handlers': {
+                        'ams.checker.opened': 'PyAMS_content.footer.submitEditForm',
+                        'ams.checker.closed': 'PyAMS_content.footer.submitEditForm'
+                    }
+                }
+                alsoProvides(group, IObjectData)
         else:
             group = NamedWidgetsGroup(self, 'footer', self.widgets, (), css_class='inner')
         alsoProvides(group, IFooterSettingsGroup)
         self.add_group(group)
         super(FooterSettingsEditForm, self).updateGroups()
 
+
+@view_config(name='footer-settings.json', context=IFooterTarget, request_type=IPyAMSLayer,
+             permission=MANAGE_TEMPLATE_PERMISSION, renderer='json', xhr=True)
+class FooterSettingsAJAXEditForm(AJAXEditForm, FooterSettingsEditForm):
+    """Footer settings edit form, JSON renderer"""
+
     def get_ajax_output(self, changes):
-        output = super(self.__class__, self).get_ajax_output(changes) or {}
+        output = super(FooterSettingsAJAXEditForm, self).get_ajax_output(changes) or {}
         if 'no_inherit' in changes.get(IInheritInfo, ()):
             output['status'] = 'reload'
         return output
 
 
+@adapter_config(context=(Interface, IAdminLayer, FooterSettingsEditForm), provides=IFormHelp)
+class FooterSettingsEditFormHelpAdapter(FormHelp):
+    """Footer settings edit form help adapter"""
+
+    def __new__(cls, context, request, view):
+        if (not view.getContent().can_inherit) or (view.mode != INPUT_MODE):
+            return None
+        return FormHelp.__new__(cls)
+
+    message = _("""WARNING: Footer properties are saved automatically when changing inherit mode!!""")
+    message_format = 'rest'
+
+
 @adapter_config(name='renderer', context=(IFooterTarget, IPyAMSLayer, IFooterSettingsGroup), provides=IInnerSubForm)
 class FooterSettingsRendererEditSubform(InnerAdminEditForm):
     """Footer settings renderer edit form"""
@@ -126,10 +162,17 @@
     fields = field.Fields(IFooterSettings).select('renderer')
     weight = 1
 
+    edit_permission = MANAGE_TEMPLATE_PERMISSION
+
+    _changes = None
+
     def __init__(self, context, request, group):
         context = IFooterSettings(context)
         super(FooterSettingsRendererEditSubform, self).__init__(context, request, group)
 
+    def getContent(self):
+        return IFooterSettings(self.context)
+
     def updateWidgets(self, prefix=None):
         super(FooterSettingsRendererEditSubform, self).updateWidgets(prefix)
         if 'renderer' in self.widgets:
@@ -146,19 +189,30 @@
             alsoProvides(widget, IObjectData)
 
     def get_forms(self, include_self=True):
-        if include_self and self.request.method == 'POST':
+        if include_self and (self._changes is None) and (self.request.method == 'POST'):
             data, errors = self.extractData()
             if not errors:
-                self.applyChanges(data)
+                self._changes = self.applyChanges(data)
         for form in super(FooterSettingsRendererEditSubform, self).get_forms(include_self):
             yield form
 
+    def get_ajax_output(self, changes):
+        if not changes:
+            changes = self._changes
+        if changes:
+            return {
+                'status': 'success',
+                'message': self.request.localizer.translate(self.successMessage)
+            }
+        else:
+            return super(FooterSettingsRendererEditSubform, self).get_ajax_output(changes)
+
 
 @adapter_config(name='footer-renderer-settings-form',
                 context=(IFooterRendererSettings, IPyAMSLayer, FooterSettingsRendererEditSubform),
                 provides=IInnerSubForm)
 @adapter_config(name='footer-renderer-settings-form',
-                context=(IFooterTarget, IPyAMSLayer, FooterSettingsEditForm),
+                context=(IFooterTarget, IPyAMSLayer, FooterSettingsAJAXEditForm),
                 provides=IInnerSubForm)
 class FooterSettingsRendererSettingsEditForm(InnerAdminEditForm):
     """Footer settings renderer settings edit form"""
--- a/src/pyams_content/features/header/skin/__init__.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/features/header/skin/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -19,10 +19,12 @@
 from pyams_content.features.header.interfaces import IHeaderTarget, IHeaderRenderer, IHeaderSettings
 from pyams_content.features.renderer.interfaces import HIDDEN_RENDERER_NAME
 from pyams_skin.layer import IPyAMSLayer
+from pyams_utils.interfaces.url import DISPLAY_CONTEXT
 
 # import packages
 from pyams_content.features.renderer.skin import BaseContentRenderer
 from pyams_utils.adapter import adapter_config
+from pyams_utils.traversing import get_parent
 from pyramid.decorator import reify
 
 from pyams_content import _
@@ -35,7 +37,11 @@
     def settings(self):
         if self.settings_interface is None:
             return None
-        settings = IHeaderSettings(self.context)
+        context = self.request.annotations.get(DISPLAY_CONTEXT)
+        if context is None:
+            context = self.context
+        target = get_parent(context, IHeaderTarget)
+        settings = IHeaderSettings(target)
         while settings.inherit:
             settings = IHeaderSettings(settings.parent)
         return settings.settings
--- a/src/pyams_content/features/header/zmi/__init__.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/features/header/zmi/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -19,22 +19,26 @@
 from pyams_content.features.header.interfaces import IHeaderTarget, IHeaderSettings, IHeaderRenderer, \
     IHeaderRendererSettings
 from pyams_form.interfaces.form import IWidgetForm, IUncheckedEditFormButtons, IInnerSubForm, \
-    IWidgetsSuffixViewletsManager
+    IWidgetsSuffixViewletsManager, IFormHelp
 from pyams_portal.interfaces import MANAGE_TEMPLATE_PERMISSION
 from pyams_portal.zmi.interfaces import IPortalContextTemplatePropertiesMenu
 from pyams_skin.interfaces import IInnerPage
 from pyams_skin.layer import IPyAMSLayer
 from pyams_utils.interfaces.data import IObjectData
 from pyams_utils.interfaces.inherit import IInheritInfo
+from pyams_zmi.layer import IAdminLayer
 from z3c.form.interfaces import INPUT_MODE
 
 # import packages
-from pyams_form.form import ajax_config
+from pyams_content.skin import pyams_content
+from pyams_form.form import AJAXEditForm
 from pyams_form.group import NamedWidgetsGroup
+from pyams_form.help import FormHelp
 from pyams_pagelet.pagelet import pagelet_config
 from pyams_skin.viewlet.menu import MenuItem, MenuDivider
 from pyams_template.template import template_config
 from pyams_utils.adapter import adapter_config
+from pyams_utils.fanstatic import get_resource_path
 from pyams_utils.url import absolute_url
 from pyams_viewlet.viewlet import viewlet_config, Viewlet
 from pyams_zmi.form import AdminEditForm, InnerAdminEditForm
@@ -69,8 +73,6 @@
 
 @pagelet_config(name='header-settings.html', context=IHeaderTarget, layer=IPyAMSLayer,
                 permission=MANAGE_TEMPLATE_PERMISSION)
-@ajax_config(name='header-settings.json', context=IHeaderTarget, layer=IPyAMSLayer,
-             permission=MANAGE_TEMPLATE_PERMISSION)
 @implementer(IWidgetForm, IInnerPage)
 class HeaderSettingsEditForm(AdminEditForm):
     """Header settings edit form"""
@@ -93,6 +95,8 @@
             fields = field.Fields(Interface)
         return fields
 
+    edit_permission = MANAGE_TEMPLATE_PERMISSION
+
     @property
     def buttons(self):
         if self.mode == INPUT_MODE:
@@ -100,6 +104,8 @@
         else:
             return button.Buttons(Interface)
 
+    ajax_handler = 'header-settings.json'
+
     def updateGroups(self):
         if self.getContent().can_inherit:
             group = NamedWidgetsGroup(self, 'header', self.widgets,
@@ -110,19 +116,52 @@
                                       checkbox_switch=True,
                                       checkbox_mode='disable',
                                       checkbox_field=IHeaderSettings['no_inherit'])
+            if self.mode == INPUT_MODE:
+                group.object_data = {
+                    'ams-plugins': 'pyams_content',
+                    'ams-plugin-pyams_content-src': get_resource_path(pyams_content),
+                    'ams-plugin-pyams_content-async': False,
+                    'ams-events-handlers': {
+                        'ams.checker.opened': 'PyAMS_content.header.submitEditForm',
+                        'ams.checker.closed': 'PyAMS_content.header.submitEditForm'
+                    }
+                }
+                alsoProvides(group, IObjectData)
         else:
             group = NamedWidgetsGroup(self, 'header', self.widgets, (), css_class='inner')
         alsoProvides(group, IHeaderSettingsGroup)
         self.add_group(group)
         super(HeaderSettingsEditForm, self).updateGroups()
 
+
+@view_config(name='header-settings.json', context=IHeaderTarget, request_type=IPyAMSLayer,
+             permission=MANAGE_TEMPLATE_PERMISSION, renderer='json', xhr=True)
+class HeaderSettingsAJAXEditForm(AJAXEditForm, HeaderSettingsEditForm):
+    """Header settings edit form, JSON renderer"""
+
     def get_ajax_output(self, changes):
-        output = super(self.__class__, self).get_ajax_output(changes) or {}
+        output = super(HeaderSettingsAJAXEditForm, self).get_ajax_output(changes) or {}
         if 'no_inherit' in changes.get(IInheritInfo, ()):
-            output['status'] = 'reload'
+            if 'autosubmit' in self.request.params:
+                output['close_form'] = False
+            else:
+                output['status'] = 'reload'
         return output
 
 
+@adapter_config(context=(Interface, IAdminLayer, HeaderSettingsEditForm), provides=IFormHelp)
+class HeaderSettingsEditFormHelpAdapter(FormHelp):
+    """Header settings edit form help adapter"""
+
+    def __new__(cls, context, request, view):
+        if (not view.getContent().can_inherit) or (view.mode != INPUT_MODE):
+            return None
+        return FormHelp.__new__(cls)
+
+    message = _("""WARNING: Header properties are saved automatically when changing inherit mode!!""")
+    message_format = 'rest'
+
+
 @adapter_config(name='renderer', context=(IHeaderTarget, IPyAMSLayer, IHeaderSettingsGroup), provides=IInnerSubForm)
 class HeaderSettingsRendererEditSubform(InnerAdminEditForm):
     """Header settings renderer edit form"""
@@ -132,10 +171,17 @@
     fields = field.Fields(IHeaderSettings).select('renderer')
     weight = 1
 
+    edit_permission = MANAGE_TEMPLATE_PERMISSION
+
+    _changes = None
+
     def __init__(self, context, request, group):
         context = IHeaderSettings(context)
         super(HeaderSettingsRendererEditSubform, self).__init__(context, request, group)
 
+    def getContent(self):
+        return IHeaderSettings(self.context)
+
     def updateWidgets(self, prefix=None):
         super(HeaderSettingsRendererEditSubform, self).updateWidgets(prefix)
         if 'renderer' in self.widgets:
@@ -152,19 +198,30 @@
             alsoProvides(widget, IObjectData)
 
     def get_forms(self, include_self=True):
-        if include_self and self.request.method == 'POST':
+        if include_self and (self._changes is None) and (self.request.method == 'POST'):
             data, errors = self.extractData()
             if not errors:
-                self.applyChanges(data)
+                self._changes = self.applyChanges(data)
         for form in super(HeaderSettingsRendererEditSubform, self).get_forms(include_self):
             yield form
 
+    def get_ajax_output(self, changes):
+        if not changes:
+            changes = self._changes
+        if changes:
+            return {
+                'status': 'success',
+                'message': self.request.localizer.translate(self.successMessage)
+            }
+        else:
+            return super(HeaderSettingsRendererEditSubform, self).get_ajax_output(changes)
+
 
 @adapter_config(name='header-renderer-settings-form',
                 context=(IHeaderRendererSettings, IPyAMSLayer, HeaderSettingsRendererEditSubform),
                 provides=IInnerSubForm)
 @adapter_config(name='header-renderer-settings-form',
-                context=(IHeaderTarget, IPyAMSLayer, HeaderSettingsEditForm),
+                context=(IHeaderTarget, IPyAMSLayer, HeaderSettingsAJAXEditForm),
                 provides=IInnerSubForm)
 class HeaderSettingsRendererSettingsEditForm(InnerAdminEditForm):
     """Header settings renderer settings edit form"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/features/menu/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -0,0 +1,44 @@
+#
+# Copyright (c) 2008-2018 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.features.menu.interfaces import IMenu, IMenusContainer, IMenuLink
+
+# import packages
+from pyams_content.component.association.container import AssociationContainer
+from zope.interface import implementer
+from zope.schema.fieldproperty import FieldProperty
+
+
+#
+# Menus classes
+#
+
+@implementer(IMenu)
+class Menu(AssociationContainer):
+    """Associations menu"""
+
+    visible = FieldProperty(IMenu['visible'])
+    title = FieldProperty(IMenu['title'])
+
+
+@implementer(IMenusContainer)
+class MenusContainer(AssociationContainer):
+    """Associations menus container"""
+
+    def get_visible_items(self):
+        return filter(lambda x: IMenu(x).visible, self.values())
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/features/menu/interfaces/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -0,0 +1,75 @@
+#
+# Copyright (c) 2008-2018 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.association.interfaces import IAssociationContainer, IAssociationContainerTarget
+from zope.annotation.interfaces import IAttributeAnnotatable
+
+# import packages
+from pyams_i18n.schema import I18nTextLineField
+from zope.container.constraints import containers, contains
+from zope.interface import Interface
+from zope.schema import Bool
+
+from pyams_content import _
+
+
+class IMenuLink(Interface):
+    """Menu link marker interface"""
+
+
+class IMenuInternalLink(IMenuLink):
+    """Menu internal link marker interface"""
+
+
+class IMenuExternalLink(IMenuLink):
+    """Menu external link marker interface"""
+
+
+class IMenuLinksContainer(IAssociationContainer):
+    """Menu links container interface"""
+
+    contains(IMenuLink)
+
+
+class IMenuLinksContainerTarget(IAssociationContainerTarget):
+    """Menu links container marker interface"""
+
+
+class IMenu(IMenuLinksContainer):
+    """Menu container interface"""
+
+    containers('.IMenusContainer')
+
+    visible = Bool(title=_("Visible?"),
+                   description=_("Is this item visible in front-office?"),
+                   required=True,
+                   default=True)
+
+    title = I18nTextLineField(title=_("Menu title"),
+                              description=_("Displayed menu label"),
+                              required=True)
+
+
+class IMenusContainer(IAssociationContainer):
+    """Menus container interface"""
+
+    contains(IMenu)
+
+
+class IMenusContainerTarget(IAssociationContainerTarget):
+    """Menus container target marker interface"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/features/menu/portlet/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -0,0 +1,20 @@
+#
+# Copyright (c) 2008-2018 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/features/menu/portlet/navigation/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -0,0 +1,20 @@
+#
+# Copyright (c) 2008-2018 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/features/menu/portlet/navigation/double.py	Wed Jun 27 16:42:01 2018 +0200
@@ -0,0 +1,84 @@
+#
+# Copyright (c) 2008-2018 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.association.interfaces import ASSOCIATION_CONTAINER_KEY
+from pyams_content.component.illustration.interfaces import IBasicIllustrationTarget
+from pyams_content.features.menu.interfaces import IMenusContainer, IMenusContainerTarget
+from pyams_content.features.menu.portlet.navigation.interfaces.double import IDoubleNavigationPortletSettings, \
+    IDoubleNavigationMenu, IDoubleNavigationMenusContainer
+from pyams_utils.interfaces import VIEW_PERMISSION
+from zope.lifecycleevent.interfaces import IObjectAddedEvent
+
+# import packages
+from pyams_content.features.menu import MenusContainer
+from pyams_portal.portlet import PortletSettings, portlet_config, Portlet
+from pyams_utils.adapter import get_annotation_adapter, adapter_config
+from pyams_utils.factory import factory_config
+from pyramid.events import subscriber
+from zope.interface import implementer, alsoProvides
+from zope.schema.fieldproperty import FieldProperty
+
+from pyams_content import _
+
+
+DOUBLE_NAVIGATION_PORTLET_NAME = 'pyams_content.portlet.navigation.double'
+DOUBLE_NAVIGATION_LINKS_KEY = '{0}::menus'.format(ASSOCIATION_CONTAINER_KEY)
+
+
+@implementer(IDoubleNavigationPortletSettings, IMenusContainerTarget)
+@factory_config(provided=IDoubleNavigationPortletSettings)
+class DoubleNavigationPortletSettings(PortletSettings):
+    """Double navigation portlet settings"""
+
+    title = FieldProperty(IDoubleNavigationPortletSettings['title'])
+    subtitle = FieldProperty(IDoubleNavigationPortletSettings['subtitle'])
+
+    @property
+    def menus(self):
+        return get_annotation_adapter(self, DOUBLE_NAVIGATION_LINKS_KEY, MenusContainer,
+                                      markers=IDoubleNavigationMenusContainer, name='++ass++menus')
+
+
+@adapter_config(name='menus', context=IDoubleNavigationPortletSettings, provides=IMenusContainer)
+def simple_navigation_links_adapter(context):
+    """Double navigation menus factory"""
+    return context.menus
+
+
+@portlet_config(permission=VIEW_PERMISSION)
+class DoubleNavigationPortlet(Portlet):
+    """Double navigation portlet"""
+
+    name = DOUBLE_NAVIGATION_PORTLET_NAME
+    label = _("Double navigation")
+
+    toolbar_css_class = 'fa fa-fw fa-2x fa-list-alt'
+
+    settings_factory = IDoubleNavigationPortletSettings
+
+
+@subscriber(IObjectAddedEvent, parent_selector=IDoubleNavigationMenusContainer)
+def handle_added_navigation_menu(event):
+    """Add marker interface to added menus container"""
+    alsoProvides(event.object, IDoubleNavigationMenu, IBasicIllustrationTarget)
+
+
+@subscriber(IObjectAddedEvent, parent_selector=IDoubleNavigationMenu)
+def handle_added_navigation_menu_link(event):
+    """Add marker interface to added menu link"""
+    alsoProvides(event.object, IBasicIllustrationTarget)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/features/menu/portlet/navigation/interfaces/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -0,0 +1,1 @@
+#
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/features/menu/portlet/navigation/interfaces/double.py	Wed Jun 27 16:42:01 2018 +0200
@@ -0,0 +1,47 @@
+#
+# Copyright (c) 2008-2018 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_portal.interfaces import IPortletSettings
+
+# import packages
+from pyams_i18n.schema import I18nTextLineField
+from zope.interface import Interface, Attribute
+
+from pyams_content import _
+
+
+class IDoubleNavigationPortletSettings(IPortletSettings):
+    """Double navigation portlet settings interface"""
+
+    title = I18nTextLineField(title=_("Title"),
+                              description=_("Portlet main title"),
+                              required=False)
+
+    subtitle = I18nTextLineField(title=_("Subtitle"),
+                                 description=_("Portlet subtitle"),
+                                 required=False)
+
+    menus = Attribute("Navigation menus")
+
+
+class IDoubleNavigationMenusContainer(Interface):
+    """Double navigation menus container marker interface"""
+
+
+class IDoubleNavigationMenu(Interface):
+    """Double navigation menu marker interface"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/features/menu/portlet/navigation/interfaces/simple.py	Wed Jun 27 16:42:01 2018 +0200
@@ -0,0 +1,43 @@
+#
+# Copyright (c) 2008-2018 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_portal.interfaces import IPortletSettings
+
+# import packages
+from pyams_i18n.schema import I18nTextLineField
+from zope.interface import Interface, Attribute
+
+from pyams_content import _
+
+
+class ISimpleNavigationPortletSettings(IPortletSettings):
+    """Simple navigation portlet settings interface"""
+
+    title = I18nTextLineField(title=_("Title"),
+                              description=_("Portlet main title"),
+                              required=False)
+
+    subtitle = I18nTextLineField(title=_("Subtitle"),
+                                 description=_("Portlet subtitle"),
+                                 required=False)
+
+    links = Attribute("Navigation links")
+
+
+class ISimpleNavigationMenu(Interface):
+    """Simple navigation menu marker interface"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/features/menu/portlet/navigation/simple.py	Wed Jun 27 16:42:01 2018 +0200
@@ -0,0 +1,77 @@
+#
+# Copyright (c) 2008-2018 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.association.interfaces import ASSOCIATION_CONTAINER_KEY
+from pyams_content.component.illustration import IBasicIllustrationTarget
+from pyams_content.features.menu.interfaces import IMenuLinksContainer, IMenuLinksContainerTarget
+from pyams_content.features.menu.portlet.navigation.interfaces.simple import ISimpleNavigationPortletSettings, \
+    ISimpleNavigationMenu
+from pyams_utils.interfaces import VIEW_PERMISSION
+from zope.lifecycleevent.interfaces import IObjectAddedEvent
+
+# import packages
+from pyams_content.features.menu import Menu
+from pyams_portal.portlet import PortletSettings, portlet_config, Portlet
+from pyams_utils.adapter import get_annotation_adapter, adapter_config
+from pyams_utils.factory import factory_config
+from pyramid.events import subscriber
+from zope.interface import implementer, alsoProvides
+from zope.schema.fieldproperty import FieldProperty
+
+from pyams_content import _
+
+
+SIMPLE_NAVIGATION_PORTLET_NAME = 'pyams_content.portlet.navigation.simple'
+SIMPLE_NAVIGATION_LINKS_KEY = '{0}::links'.format(ASSOCIATION_CONTAINER_KEY)
+
+
+@implementer(ISimpleNavigationPortletSettings, IMenuLinksContainerTarget)
+@factory_config(provided=ISimpleNavigationPortletSettings)
+class SimpleNavigationPortletSettings(PortletSettings):
+    """Simple navigation portlet settings"""
+
+    title = FieldProperty(ISimpleNavigationPortletSettings['title'])
+    subtitle = FieldProperty(ISimpleNavigationPortletSettings['subtitle'])
+
+    @property
+    def links(self):
+        return get_annotation_adapter(self, SIMPLE_NAVIGATION_LINKS_KEY, Menu,
+                                      markers=ISimpleNavigationMenu, name='++ass++links')
+
+
+@adapter_config(name='links', context=ISimpleNavigationPortletSettings, provides=IMenuLinksContainer)
+def simple_navigation_links_adapter(context):
+    """Simple navigation links factory"""
+    return context.links
+
+
+@portlet_config(permission=VIEW_PERMISSION)
+class SimpleNavigationPortlet(Portlet):
+    """Simple navigation portlet"""
+
+    name = SIMPLE_NAVIGATION_PORTLET_NAME
+    label = _("Simple navigation")
+
+    toolbar_css_class = 'fa fa-fw fa-2x fa-bars'
+
+    settings_factory = ISimpleNavigationPortletSettings
+
+
+@subscriber(IObjectAddedEvent, parent_selector=ISimpleNavigationMenu)
+def handle_added_navigation_link(event):
+    alsoProvides(event.object, IBasicIllustrationTarget)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/features/menu/portlet/navigation/zmi/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -0,0 +1,20 @@
+#
+# Copyright (c) 2008-2018 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/features/menu/portlet/navigation/zmi/double.py	Wed Jun 27 16:42:01 2018 +0200
@@ -0,0 +1,104 @@
+#
+# Copyright (c) 2008-2018 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.illustration.interfaces import IIllustration, ILinkIllustration
+from pyams_content.component.links.interfaces import IInternalLink
+from pyams_content.features.menu.portlet.navigation.interfaces.double import IDoubleNavigationPortletSettings, \
+    IDoubleNavigationMenusContainer
+from pyams_form.interfaces.form import IInnerSubForm
+from pyams_pagelet.interfaces import IPagelet
+from pyams_portal.interfaces import IPortletPreviewer
+from pyams_skin.layer import IPyAMSLayer
+from pyams_utils.interfaces import VIEW_SYSTEM_PERMISSION
+from pyams_workflow.interfaces import IWorkflowPublicationInfo
+
+# import packages
+from pyams_content.component.association.interfaces import IAssociationInfo
+from pyams_content.features.menu.zmi import MenusTable, IMenusView, MenusView
+from pyams_form.form import AJAXEditForm
+from pyams_pagelet.pagelet import pagelet_config
+from pyams_portal.portlet import PortletPreviewer
+from pyams_portal.zmi.portlet import PortletSettingsEditor, PortletSettingsPropertiesEditor
+from pyams_template.template import template_config
+from pyams_utils.adapter import adapter_config
+from zope.interface import alsoProvides, Interface
+
+from pyams_content import _
+
+
+@pagelet_config(name='properties.html', context=IDoubleNavigationPortletSettings, layer=IPyAMSLayer,
+                permission=VIEW_SYSTEM_PERMISSION)
+class DoubleNavigationPortletSettingsEditor(PortletSettingsEditor):
+    """Double navigation portlet settings editor"""
+
+    settings = IDoubleNavigationPortletSettings
+
+
+@adapter_config(name='properties.json', context=(IDoubleNavigationPortletSettings, IPyAMSLayer), provides=IPagelet)
+class DoubleNavigationPortletSettingsAJAXEditor(AJAXEditForm, DoubleNavigationPortletSettingsEditor):
+    """Double navigation portlet settings editor, JSON renderer"""
+
+
+class DoubleNavigationPortletMenusTable(MenusTable):
+    """Double navigation portlet menus table"""
+
+    associations_name = 'menus'
+
+
+@adapter_config(name='double-navigation-menus',
+                context=(IDoubleNavigationPortletSettings, IPyAMSLayer, PortletSettingsPropertiesEditor),
+                provides=IInnerSubForm)
+@adapter_config(name='++ass++menus', context=(IDoubleNavigationMenusContainer, IPyAMSLayer), provides=IMenusView)
+class DoubleNavigationPortletLinksView(MenusView):
+    """Double navigation portlet menus view"""
+
+    title = _("Navigation menus")
+
+    table_class = DoubleNavigationPortletMenusTable
+    weight = 10
+
+
+@adapter_config(context=(Interface, IPyAMSLayer, Interface, IDoubleNavigationPortletSettings),
+                provides=IPortletPreviewer)
+@template_config(template='templates/double-preview.pt', layer=IPyAMSLayer)
+class DoubleNavigationPortletPreviewer(PortletPreviewer):
+    """Double navigation portlet previewer"""
+
+    @classmethod
+    def get_link_info(cls, link):
+        return IAssociationInfo(link)
+
+    @classmethod
+    def get_link_status(cls, link):
+        if not IInternalLink.providedBy(link):
+            return True
+        target = link.get_target()
+        return (target is not None) and IWorkflowPublicationInfo(target).is_published()
+
+    @classmethod
+    def get_link_illustration(cls, link):
+        illustration = IIllustration(link, None)
+        if (illustration is None) or not illustration.has_data():
+            if IInternalLink.providedBy(link):
+                target = link.get_target()
+                if target is None:
+                    return
+                illustration = ILinkIllustration(target, None)
+                if (illustration is None) or not illustration.has_data():
+                    illustration = IIllustration(target, None)
+        return illustration
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/features/menu/portlet/navigation/zmi/simple.py	Wed Jun 27 16:42:01 2018 +0200
@@ -0,0 +1,104 @@
+#
+# Copyright (c) 2008-2018 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.illustration.interfaces import IIllustration, ILinkIllustration
+from pyams_content.component.links.interfaces import IInternalLink
+from pyams_content.features.menu.portlet.navigation.interfaces.simple import ISimpleNavigationPortletSettings, \
+    ISimpleNavigationMenu
+from pyams_form.interfaces.form import IInnerSubForm
+from pyams_pagelet.interfaces import IPagelet
+from pyams_portal.interfaces import IPortletPreviewer
+from pyams_skin.layer import IPyAMSLayer
+from pyams_utils.interfaces import VIEW_SYSTEM_PERMISSION
+from pyams_workflow.interfaces import IWorkflowPublicationInfo
+
+# import packages
+from pyams_content.component.association.interfaces import IAssociationInfo
+from pyams_content.features.menu.zmi import LinksTable, IMenuLinksView, MenuLinksView
+from pyams_form.form import AJAXEditForm
+from pyams_pagelet.pagelet import pagelet_config
+from pyams_portal.portlet import PortletPreviewer
+from pyams_portal.zmi.portlet import PortletSettingsEditor, PortletSettingsPropertiesEditor
+from pyams_template.template import template_config
+from pyams_utils.adapter import adapter_config
+from zope.interface import alsoProvides, Interface
+
+from pyams_content import _
+
+
+@pagelet_config(name='properties.html', context=ISimpleNavigationPortletSettings, layer=IPyAMSLayer,
+                permission=VIEW_SYSTEM_PERMISSION)
+class SimpleNavigationPortletSettingsEditor(PortletSettingsEditor):
+    """Simple navigation portlet settings editor"""
+
+    settings = ISimpleNavigationPortletSettings
+
+
+@adapter_config(name='properties.json', context=(ISimpleNavigationPortletSettings, IPyAMSLayer), provides=IPagelet)
+class SimpleNavigationPortletSettingsAJAXEditor(AJAXEditForm, SimpleNavigationPortletSettingsEditor):
+    """Simple navigation portlet settings editor, JSON renderer"""
+
+
+class SimpleNavigationPortletLinksTable(LinksTable):
+    """Simple navigation portlet links table"""
+
+    associations_name = 'links'
+
+
+@adapter_config(name='simple-navigation-links',
+                context=(ISimpleNavigationPortletSettings, IPyAMSLayer, PortletSettingsPropertiesEditor),
+                provides=IInnerSubForm)
+@adapter_config(name='++ass++links', context=(ISimpleNavigationMenu, IPyAMSLayer), provides=IMenuLinksView)
+class SimpleNavigationPortletLinksView(MenuLinksView):
+    """Simple navigation portlet links view"""
+
+    title = _("Navigation links")
+
+    table_class = SimpleNavigationPortletLinksTable
+    weight = 10
+
+
+@adapter_config(context=(Interface, IPyAMSLayer, Interface, ISimpleNavigationPortletSettings),
+                provides=IPortletPreviewer)
+@template_config(template='templates/simple-preview.pt', layer=IPyAMSLayer)
+class SimpleNavigationPortletPreviewer(PortletPreviewer):
+    """Simple navigation portlet previewer"""
+
+    @classmethod
+    def get_link_info(cls, link):
+        return IAssociationInfo(link)
+
+    @classmethod
+    def get_link_status(cls, link):
+        if not IInternalLink.providedBy(link):
+            return True
+        target = link.get_target()
+        return (target is not None) and IWorkflowPublicationInfo(target).is_published()
+
+    @classmethod
+    def get_link_illustration(cls, link):
+        illustration = IIllustration(link, None)
+        if (illustration is None) or not illustration.has_data():
+            if IInternalLink.providedBy(link):
+                target = link.get_target()
+                if target is None:
+                    return
+                illustration = ILinkIllustration(target, None)
+                if (illustration is None) or not illustration.has_data():
+                    illustration = IIllustration(target, None)
+        return illustration
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/features/menu/portlet/navigation/zmi/templates/double-preview.pt	Wed Jun 27 16:42:01 2018 +0200
@@ -0,0 +1,26 @@
+<div tal:define="settings view.settings">
+	<strong tal:content="i18n:settings.title">Title</strong>
+	<tal:var define="subtitle i18n:settings.subtitle" condition="subtitle" content="subtitle" />
+	<ul>
+		<li tal:repeat="menu settings.menus.get_visible_items()">
+			<span tal:content="i18n:menu.title">Title</span>
+			<ul>
+				<li tal:repeat="link menu.get_visible_items()"
+					tal:attributes="illustration view.get_link_illustration(link);">
+					<i class="fa fa-fw fa-eye-slash text-danger hint opaque align-base"
+					   tal:condition="not:view.get_link_status(link)"
+					   title="Link target is not published!" i18n:attributes="title"></i>
+					<i class="fa fa-fw fa-file-image-o text-danger hint opaque align-base"
+					   tal:define="illustration view.get_link_illustration(link)"
+					   tal:condition="not:illustration and illustration.has_data()"
+					   title="Link has no illustration"></i>
+					<tal:var define="info view.get_link_info(link)">
+						<span tal:content="info.user_title">User title</span>
+						&ndash;
+						<span tal:content="info.inner_title">Inner title</span>
+					</tal:var>
+				</li>
+			</ul>
+		</li>
+	</ul>
+</div>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/features/menu/portlet/navigation/zmi/templates/simple-preview.pt	Wed Jun 27 16:42:01 2018 +0200
@@ -0,0 +1,21 @@
+<div tal:define="settings view.settings" i18n:domain="pyams_content">
+	<strong tal:content="i18n:settings.title">Title</strong>
+	<tal:var define="subtitle i18n:settings.subtitle" condition="subtitle" content="subtitle" />
+	<ul>
+		<li tal:repeat="link settings.links.get_visible_items()"
+			tal:attributes="illustration view.get_link_illustration(link);">
+			<i class="fa fa-fw fa-eye-slash text-danger hint opaque align-base"
+			   tal:condition="not:view.get_link_status(link)"
+			   title="Link target is not published!" i18n:attributes="title"></i>
+			<i class="fa fa-fw fa-file-image-o text-danger hint opaque align-base"
+			   tal:define="illustration view.get_link_illustration(link)"
+			   tal:condition="not:illustration and illustration.has_data()"
+			   title="Link has no illustration" i18n:attributes="title"></i>
+			<tal:var define="info view.get_link_info(link)">
+				<span tal:content="info.user_title">User title</span>
+				&ndash;
+				<span tal:content="info.inner_title">Inner title</span>
+			</tal:var>
+		</li>
+	</ul>
+</div>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/features/menu/zmi/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -0,0 +1,489 @@
+#
+# Copyright (c) 2008-2018 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 json
+
+# import interfaces
+from pyams_content.component.links.zmi import InternalLinkAddMenu, InternalLinkAddForm, InternalLinkPropertiesEditForm, \
+    ExternalLinkAddMenu, ExternalLinkAddForm, ExternalLinkPropertiesEditForm
+from pyams_content.features.menu import IMenusContainer, IMenu, Menu, IMenuLink
+from pyams_content.features.menu.interfaces import IMenusContainerTarget, IMenuLinksContainer, IMenuInternalLink, \
+    IMenuExternalLink, IMenuLinksContainerTarget
+from pyams_form.interfaces.form import IFormContextPermissionChecker
+from pyams_portal.interfaces import MANAGE_TEMPLATE_PERMISSION
+from pyams_skin.interfaces.viewlet import IWidgetTitleViewletManager, IToolbarAddingMenu
+from pyams_skin.layer import IPyAMSLayer
+from pyams_utils.interfaces import VIEW_SYSTEM_PERMISSION
+from pyams_viewlet.interfaces import IViewletManager
+from pyams_zmi.interfaces import IPropertiesEditForm
+from z3c.table.interfaces import IValues, IColumn
+
+# import packages
+from pyams_content.component.association.zmi import AssociationsTable, AssociationsTablePublicNameColumn
+from pyams_form.form import ajax_config, AJAXAddForm, AJAXEditForm
+from pyams_i18n.column import I18nAttrColumn
+from pyams_pagelet.pagelet import pagelet_config
+from pyams_skin.container import switch_element_visibility, delete_container_element
+from pyams_skin.event import get_json_switched_table_refresh_event, get_json_table_row_refresh_event
+from pyams_skin.table import BaseTable, SorterColumn, VisibilitySwitcherColumn, I18nColumn, TrashColumn, NameColumn, \
+    get_table_id
+from pyams_skin.viewlet.toolbar import ToolbarAction
+from pyams_utils.adapter import adapter_config, ContextRequestViewAdapter, ContextAdapter
+from pyams_utils.traversing import get_parent
+from pyams_utils.url import absolute_url
+from pyams_viewlet.viewlet import viewlet_config
+from pyams_zmi.form import AdminDialogAddForm, AdminDialogEditForm
+from pyams_zmi.zmi.table import InnerTableView
+from pyramid.decorator import reify
+from pyramid.exceptions import NotFound
+from pyramid.renderers import render
+from pyramid.view import view_config
+from z3c.form import field
+from zope.interface import implementer, alsoProvides, Interface
+
+from pyams_content import _
+
+
+#
+# Custom marker interfaces
+#
+
+class IMenuLinksView(Interface):
+    """Menu links view marker interface"""
+
+
+class IMenusView(Interface):
+    """Menus view marker interface"""
+
+
+#
+# Menus add and edit forms
+#
+
+@viewlet_config(name='add-menu.action', context=IMenusContainerTarget, layer=IPyAMSLayer,
+                view=IMenusView, manager=IWidgetTitleViewletManager, weight=10)
+class MenuAddAction(ToolbarAction):
+    """Menu add action"""
+
+    label = _("Add menu...")
+    url = 'add-menu.html'
+    modal_target = True
+
+
+@pagelet_config(name='add-menu.html', context=IMenusContainer, layer=IPyAMSLayer,
+                permission=MANAGE_TEMPLATE_PERMISSION)
+@ajax_config(name='add-menu.json', context=IMenusContainer, layer=IPyAMSLayer, base=AJAXAddForm)
+class MenuAddForm(AdminDialogAddForm):
+    """Menu add form"""
+
+    legend = _("Add new menu")
+    icon_css_class = 'fa fa-fw fa-bars'
+
+    fields = field.Fields(IMenu).select('title')
+    edit_permission = MANAGE_TEMPLATE_PERMISSION
+
+    def create(self, data):
+        return Menu()
+
+    def add(self, object):
+        self.context.append(object)
+
+    def get_ajax_output(self, changes):
+        settings = get_parent(self.context, IMenusContainerTarget)
+        view = self.request.registry.getMultiAdapter((self.context, self.request), IMenusView,
+                                                     name=self.context.__name__)
+        return {
+            'status': 'success',
+            'message': self.request.localizer.translate(_("Menu was correctly added.")),
+            'events': [
+                get_json_switched_table_refresh_event(settings, self.request, view.table_class)
+            ]
+        }
+
+
+@pagelet_config(name='properties.html', context=IMenu, layer=IPyAMSLayer, permission=MANAGE_TEMPLATE_PERMISSION)
+@ajax_config(name='properties.json', context=IMenu, layer=IPyAMSLayer)
+@implementer(IPropertiesEditForm)
+class MenuPropertiesEditForm(AdminDialogEditForm):
+    """Menu properties edit form"""
+
+    legend = _("Edit menu properties")
+    icon_css_class = 'fa fa-fw fa-bars'
+
+    dialog_class = 'modal-large'
+
+    fields = field.Fields(IMenu).select('title')
+    edit_permission = MANAGE_TEMPLATE_PERMISSION
+
+    def get_ajax_output(self, changes):
+        output = super(self.__class__, self).get_ajax_output(changes)
+        if changes:
+            settings = get_parent(self.context, IMenusContainerTarget)
+            container = settings.menus
+            view = self.request.registry.getMultiAdapter((container, self.request), IMenusView,
+                                                         name=container.__name__)
+            output.setdefault('events', []).append(
+                get_json_table_row_refresh_event(settings, self.request, view.table_class, self.context))
+        return output
+
+
+#
+# Menus table views
+#
+
+class MenusTable(BaseTable):
+    """Menus table"""
+
+    prefix = 'menus'
+    associations_name = 'menus'
+
+    permission = MANAGE_TEMPLATE_PERMISSION
+    hide_header = True
+    hide_body_toolbar = True
+    sortOn = None
+
+    @property
+    def cssClasses(self):
+        classes = ['table', 'table-bordered', 'table-striped', 'table-hover', 'table-tight']
+        permission = self.permission
+        if (not permission) or self.request.has_permission(permission, self.context):
+            classes.append('table-dnd')
+        return {'table': ' '.join(classes)}
+
+    @property
+    def data_attributes(self):
+        menus = getattr(self.context, self.associations_name)
+        attributes = super(MenusTable, self).data_attributes
+        attributes.setdefault('table', {}).update({
+            'data-ams-location': absolute_url(menus, self.request),
+            'data-ams-tablednd-drag-handle': 'td.sorter',
+            'data-ams-tablednd-drop-target': 'set-menus-order.json',
+            'data-ams-visibility-switcher': 'switch-menu-visibility.json'
+        })
+        return attributes
+
+
+@adapter_config(context=(IMenusContainerTarget, IPyAMSLayer, MenusTable), provides=IValues)
+class MenusTableValuesAdapter(ContextRequestViewAdapter):
+    """Menus table values adapter"""
+
+    @property
+    def values(self):
+        return getattr(self.context, self.view.associations_name).values()
+
+
+@adapter_config(name='sorter', context=(IMenusContainerTarget, IPyAMSLayer, MenusTable),
+                provides=IColumn)
+class MenusTableSorterColumn(SorterColumn):
+    """Menus table sorter column"""
+
+    permission = MANAGE_TEMPLATE_PERMISSION
+
+
+@adapter_config(name='show-hide', context=(IMenusContainerTarget, IPyAMSLayer, MenusTable),
+                provides=IColumn)
+class MenusTableShowHideColumn(VisibilitySwitcherColumn):
+    """Menus table visibility switcher column"""
+
+    permission = MANAGE_TEMPLATE_PERMISSION
+
+
+@adapter_config(name='name', context=(IMenusContainerTarget, IPyAMSLayer, MenusTable), provides=IColumn)
+class MenusTableNameColumn(I18nColumn, I18nAttrColumn):
+    """Menus table name column"""
+
+    _header = _("Label")
+    attrName = 'title'
+    weight = 10
+
+    def renderCell(self, item):
+        return render('templates/menu-name-cell.pt', {'context': item}, request=self.request)
+
+
+@adapter_config(name='trash', context=(IMenusContainerTarget, IPyAMSLayer, MenusTable), provides=IColumn)
+class MenusTableTrashColumn(TrashColumn):
+    """Menus table trash column"""
+
+    permission = MANAGE_TEMPLATE_PERMISSION
+
+
+@implementer(IMenusView)
+class MenusView(InnerTableView):
+    """Menus view"""
+
+    table_class = MenusTable
+
+    @property
+    def actions_context(self):  # define context for internal actions
+        return self.request.registry.getAdapter(self.context, IMenusContainer,
+                                                name=self.table.associations_name)
+
+
+#
+# Menus container views
+#
+
+@view_config(name='set-menus-order.json', context=IMenusContainer, request_type=IPyAMSLayer,
+             permission=MANAGE_TEMPLATE_PERMISSION, renderer='json', xhr=True)
+def set_menus_order(request):
+    """Update menus order"""
+    order = list(map(str, json.loads(request.params.get('names'))))
+    request.context.updateOrder(order)
+    return {'status': 'success'}
+
+
+@view_config(name='switch-menu-visibility.json', context=IMenusContainer, request_type=IPyAMSLayer,
+             permission=MANAGE_TEMPLATE_PERMISSION, renderer='json', xhr=True)
+def set_menu_visibility(request):
+    """Set menu visibility"""
+    return switch_element_visibility(request, IMenusContainer)
+
+
+@view_config(name='delete-element.json', context=IMenusContainer, request_type=IPyAMSLayer,
+             permission=MANAGE_TEMPLATE_PERMISSION, renderer='json', xhr=True)
+def delete_menu(request):
+    """Delete menu"""
+    return delete_container_element(request, ignore_permission=True)
+
+
+@view_config(name='get-menu-items.json', context=IMenusContainer, request_type=IPyAMSLayer,
+             permission=MANAGE_TEMPLATE_PERMISSION, renderer='json', xhr=True)
+def get_menu_items_table(request):
+    """Get menu items table"""
+    menu = request.context.get(str(request.params.get('object_name')))
+    if menu is None:
+        raise NotFound()
+    table = MenuLinksTable(menu, request)
+    table.update()
+    return table.render()
+
+
+#
+# Menu links table
+#
+
+class LinksTable(AssociationsTable):
+    """Links base table class"""
+
+    associations_name = None
+
+    @reify
+    def id(self):
+        if IMenu.providedBy(self.context):
+            context = self.context
+        else:
+            context = self.request.registry.getAdapter(self.context, IMenuLinksContainer,
+                                                       name=self.associations_name)
+        return get_table_id(self, context)
+
+    @property
+    def prefix(self):
+        return '{0}_links'.format(self.associations_name)
+
+    permission = MANAGE_TEMPLATE_PERMISSION
+    hide_header = True
+    hide_body_toolbar = True
+
+
+class MenuLinksTable(LinksTable):
+    """Menu links associations table"""
+
+    prefix = 'menu_links'
+    associations_name = ''
+
+    @property
+    def data_attributes(self):
+        attributes = super(LinksTable, self).data_attributes
+        attributes.setdefault('table', {}).update({
+            'data-ams-location': absolute_url(self.context, self.request),
+        })
+        attributes.setdefault('tr', {}).update({'data-ams-stop-propagation': 'true'})
+        return attributes
+
+
+@adapter_config(name='name', context=(IMenu, IPyAMSLayer, MenuLinksTable), provides=IColumn)
+class MenuLinksTableNameColumn(AssociationsTablePublicNameColumn):
+    """Menu links table name column"""
+
+    def renderHeadCell(self):
+        result = super(MenuLinksTableNameColumn, self).renderHeadCell()
+        registry = self.request.registry
+        viewlet = registry.queryMultiAdapter((self.context, self.request, self.table), IViewletManager,
+                                             name='pyams.widget_title')
+        if viewlet is not None:
+            viewlet.update()
+            result += viewlet.render()
+        return result
+
+
+@adapter_config(context=(IMenu, IPyAMSLayer), provides=IMenuLinksView)
+@implementer(IMenuLinksView)
+class MenuLinksView(InnerTableView):
+    """Links base view"""
+
+    table_class = MenuLinksTable
+
+    @property
+    def actions_context(self):  # define context for internal actions
+        return self.request.registry.getAdapter(self.context, IMenuLinksContainer,
+                                                name=self.table.associations_name)
+
+
+#
+# Menu links container views
+#
+
+@view_config(name='set-associations-order.json', context=IMenuLinksContainer, request_type=IPyAMSLayer,
+             permission=MANAGE_TEMPLATE_PERMISSION, renderer='json', xhr=True)
+def set_associations_order(request):
+    """Update associations order"""
+    order = list(map(str, json.loads(request.params.get('names'))))
+    request.context.updateOrder(order)
+    return {'status': 'success'}
+
+
+@view_config(name='switch-association-visibility.json', context=IMenuLinksContainer, request_type=IPyAMSLayer,
+             permission=MANAGE_TEMPLATE_PERMISSION, renderer='json', xhr=True)
+def set_association_visibility(request):
+    """Set association visibility"""
+    return switch_element_visibility(request, IMenuLinksContainer)
+
+
+#
+# Link add and edit forms
+#
+
+@adapter_config(context=IMenuLink, provides=IFormContextPermissionChecker)
+class MenuLinkPermissionChecker(ContextAdapter):
+    """Menu link permission checker"""
+
+    edit_permission = MANAGE_TEMPLATE_PERMISSION
+
+
+class LinkAJAXAddForm(AJAXAddForm):
+    """Menu link add form, JSON renderer"""
+
+    def get_ajax_output(self, changes):
+        registry = self.request.registry
+        container = get_parent(self.context, IMenuLinksContainer)
+        view = registry.queryMultiAdapter((container, self.request), IMenuLinksView,
+                                          name=container.__name__)
+        if view is None:
+            view = registry.getMultiAdapter((container, self.request), IMenuLinksView)
+        return {
+            'status': 'success',
+            'message': self.request.localizer.translate(_("Link was correctly added.")),
+            'events': [
+                get_json_switched_table_refresh_event(self.context, self.request, view.table_class)
+            ]
+        }
+
+
+class LinkPropertiesAJAXEditForm(AJAXEditForm):
+    """Menu link properties edit form, JSON renderer"""
+
+    def get_ajax_output(self, changes):
+        output = AJAXEditForm.get_ajax_output(self, changes)
+        if changes:
+            registry = self.request.registry
+            container = get_parent(self.context, IMenuLinksContainer)
+            view = registry.queryMultiAdapter((container, self.request), IMenuLinksView,
+                                              name=container.__name__)
+            if view is None:
+                view = registry.getMultiAdapter((container, self.request), IMenuLinksView)
+            output.setdefault('events', []).append(
+                get_json_table_row_refresh_event(container, self.request, view.table_class, self.context))
+        return output
+
+
+#
+# Internal links
+#
+
+@viewlet_config(name='add-internal-link.menu', context=IMenuLinksContainerTarget, layer=IPyAMSLayer,
+                view=IMenuLinksView, manager=IToolbarAddingMenu, weight=50)
+@viewlet_config(name='add-internal-link.menu', context=IMenu, layer=IPyAMSLayer,
+                view=MenuLinksTable, manager=IToolbarAddingMenu, permission=MANAGE_TEMPLATE_PERMISSION, weight=50)
+class MenuInternalLinkAddMenu(InternalLinkAddMenu):
+    """Header internal link add menu"""
+
+
+@pagelet_config(name='add-internal-link.html', context=IMenuLinksContainer, layer=IPyAMSLayer,
+                permission=MANAGE_TEMPLATE_PERMISSION)
+@ajax_config(name='add-internal-link.json', context=IMenuLinksContainer, layer=IPyAMSLayer,
+             base=LinkAJAXAddForm)
+class MenuInternalLinkAddForm(InternalLinkAddForm):
+    """Menu internal link add form"""
+
+    edit_permission = MANAGE_TEMPLATE_PERMISSION
+
+    def create(self, data):
+        result = super(MenuInternalLinkAddForm, self).create(data)
+        alsoProvides(result, IMenuInternalLink)
+        return result
+
+    def add(self, object):
+        self.context.append(object)
+
+
+@pagelet_config(name='properties.html', context=IMenuInternalLink, layer=IPyAMSLayer,
+                permission=VIEW_SYSTEM_PERMISSION)
+@ajax_config(name='properties.json', context=IMenuInternalLink, layer=IPyAMSLayer,
+             base=LinkPropertiesAJAXEditForm)
+class MenuInternalLinkPropertiesEditForm(InternalLinkPropertiesEditForm):
+    """Menu internal link properties edit form"""
+
+    edit_permission = None  # managed by IFormContextPermissionChecker adapter
+
+
+#
+# External links
+#
+
+@viewlet_config(name='add-external-link.menu', context=IMenuLinksContainerTarget, view=IMenuLinksView,
+                layer=IPyAMSLayer, manager=IToolbarAddingMenu, weight=51)
+@viewlet_config(name='add-external-link.menu', context=IMenu, layer=IPyAMSLayer,
+                view=MenuLinksTable, manager=IToolbarAddingMenu, permission=MANAGE_TEMPLATE_PERMISSION, weight=51)
+class MenuExternalLinkAddMenu(ExternalLinkAddMenu):
+    """Menu external link add menu"""
+
+
+@pagelet_config(name='add-external-link.html', context=IMenuLinksContainer, layer=IPyAMSLayer,
+                permission=MANAGE_TEMPLATE_PERMISSION)
+@ajax_config(name='add-external-link.json', context=IMenuLinksContainer, layer=IPyAMSLayer,
+             base=LinkAJAXAddForm)
+class MenuExternalLinkAddForm(ExternalLinkAddForm):
+    """Menu external link add form"""
+
+    edit_permission = MANAGE_TEMPLATE_PERMISSION
+
+    def create(self, data):
+        result = super(MenuExternalLinkAddForm, self).create(data)
+        alsoProvides(result, IMenuExternalLink)
+        return result
+
+    def add(self, object):
+        self.context.append(object)
+
+
+@pagelet_config(name='properties.html', context=IMenuExternalLink, layer=IPyAMSLayer,
+                permission=VIEW_SYSTEM_PERMISSION)
+@ajax_config(name='properties.json', context=IMenuExternalLink, layer=IPyAMSLayer,
+             base=LinkPropertiesAJAXEditForm)
+class MenuExternalLinkPropertiesEditForm(ExternalLinkPropertiesEditForm):
+    """Menu external link properties edit form"""
+
+    edit_permission = None  # managed by IFormContextPermissionChecker adapter
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/features/menu/zmi/templates/menu-name-cell.pt	Wed Jun 27 16:42:01 2018 +0200
@@ -0,0 +1,13 @@
+<i18n:v domain="pyams_content">
+	<span data-ams-stop-propagation="true"
+		  data-ams-click-handler="MyAMS.skin.switchCellContent"
+		  data-ams-switch-handler="get-menu-items.json"
+		  data-ams-switch-target=".menus">
+		<span class="small hint"
+			  title="Click to see menu items" i18n:attributes="title"
+			  data-ams-hint-gravity="e">
+			<i class="fa fa-plus-square-o switch"></i>
+		</span>
+	</span>&nbsp;&nbsp;&nbsp;<span class="title" tal:content="i18n:context.title">title</span>
+	<div class="inner-table-form menus margin-x-10 margin-bottom-0 padding-left-5"></div>
+</i18n:v>
--- a/src/pyams_content/features/preview/zmi/templates/preview.pt	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/features/preview/zmi/templates/preview.pt	Wed Jun 27 16:42:01 2018 +0200
@@ -14,7 +14,7 @@
 				</button>
 				<h3 class="modal-title"
 					tal:define="config extension:configuration;">
-					<span class="title" tal:content="view.title | context.title | config.title">Title</span>
+					<span class="title" tal:content="structure view.title | context.title | config.title">Title</span>
 				</h3>
 				<tal:var replace="structure provider:form_toolbar" />
 			</tal:if>
@@ -36,7 +36,7 @@
 								tal:attributes="class string:small ${active}">
 								<a data-toggle="tab" class="xsmall"
 								   tal:define="url extension:absolute_url(context, 'preview.html')"
-								   tal:attributes="src string:${url}?lang=${lang}">
+								   tal:attributes="href string:#preview-${lang}">
 									<img tal:attributes="src string:/--static--/pyams_i18n/img/flags/${lang}.png" />
 								</a>
 							</li>
--- a/src/pyams_content/features/renderer/__init__.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/features/renderer/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -43,7 +43,11 @@
         """Get rendering adapter based on selected renderer name"""
         if request is None:
             request = check_request()
-        return request.registry.queryMultiAdapter((self, request), self.renderer_interface, name=self.renderer or '')
+        renderer = request.registry.queryMultiAdapter((self, request), self.renderer_interface,
+                                                      name=self.renderer or '')
+        if 'lang' in request.params:
+            renderer.language = request.params['lang']
+        return renderer
 
 
 @adapter_config(context=IRenderedContent, provides=IContentRenderer)
--- a/src/pyams_content/features/renderer/interfaces/__init__.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/features/renderer/interfaces/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -41,6 +41,11 @@
     label = Attribute("Renderer label")
     weight = Attribute("Renderer weight")
     settings_interface = Attribute("Renderer target interface")
+    language = Attribute("Renderer language (if forced)")
+
+
+class ISharedContentRenderer(IContentRenderer):
+    """Shared content renderer interface"""
 
 
 class IRendererSettings(Interface):
--- a/src/pyams_content/features/renderer/skin/__init__.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/features/renderer/skin/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -55,7 +55,7 @@
             i18n = II18n(self.context, None)
             if i18n is not None:
                 for attr in self.i18n_context_attrs:
-                    setattr(self, attr, i18n.get_attribute(attr, lang=self.language, request=self.request))
+                    setattr(self, attr, i18n.query_attribute(attr, lang=self.language, request=self.request))
 
     render = get_view_template()
 
--- a/src/pyams_content/features/renderer/zmi/__init__.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/features/renderer/zmi/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -47,7 +47,6 @@
     def update(self):
         renderer = self.renderer
         if renderer is not None:
-            renderer.language = self.language
             renderer.update()
 
     def render(self):
@@ -57,10 +56,9 @@
 
 
 #
-# Base content renderer
+# Base content renderer edit form
 #
 
-
 @pagelet_config(name='renderer-properties.html', context=IRenderedContent, layer=IPyAMSLayer,
                 permission=MANAGE_CONTENT_PERMISSION)
 @ajax_config(name='renderer-properties.json', context=IRenderedContent, layer=IPyAMSLayer)
--- a/src/pyams_content/features/review/__init__.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/features/review/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -22,7 +22,6 @@
 from pyams_content.interfaces import READER_ROLE
 from pyams_content.features.review.interfaces import IReviewManager, IReviewComment, IReviewComments, \
     REVIEW_COMMENTS_ANNOTATION_KEY, CommentAddedEvent, ICommentAddedEvent, IReviewTarget
-from pyams_content.shared.common.interfaces import IWfSharedContentRoles
 from pyams_i18n.interfaces import II18n
 from pyams_mail.interfaces import IPrincipalMailInfo
 from pyams_security.interfaces import ISecurityManager, IProtectedObject
@@ -124,6 +123,8 @@
 
     def ask_review(self, reviewers, comment, notify_all=True):
         """Ask for content review"""
+        from pyams_content.shared.common.interfaces import IWfSharedContentRoles
+
         roles = IWfSharedContentRoles(self.context, None)
         if roles is None:
             return
--- a/src/pyams_content/generations/__init__.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/generations/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -19,7 +19,9 @@
 
 # import interfaces
 from pyams_catalog.interfaces import MINUTE_RESOLUTION, DATE_RESOLUTION
-from pyams_content.interfaces import IBaseContent
+from pyams_content.component.theme import IThemesInfo
+from pyams_content.interfaces import IBaseContent, WEBMASTER_ROLE, OWNER_ROLE, PILOT_ROLE, MANAGER_ROLE, \
+    CONTRIBUTOR_ROLE
 from pyams_content.root.interfaces import ISiteRootToolsConfiguration
 from pyams_content.shared.common.interfaces import IWfSharedContent
 from pyams_content.shared.form.interfaces import IFormsManagerFactory
@@ -42,6 +44,7 @@
 from pyams_content.shared.common.manager import SharedToolContainer
 from pyams_i18n.index import I18nTextIndexWithInterface
 from pyams_security.index import PrincipalsRoleIndex
+from pyams_thesaurus.index import ThesaurusTermsListFieldIndex
 from pyams_utils.registry import utility_config, get_global_registry
 from pyams_utils.site import check_required_utilities
 from pyramid.path import DottedNameResolver
@@ -53,9 +56,26 @@
 
 
 RENAMED_CLASSES = {
-    'pyams_content.shared.common.review ReviewComment': 'pyams_content.features.review ReviewComment',
+    'pyams_content.shared.common.review ReviewComment':
+        'pyams_content.features.review ReviewComment',
     'pyams_content.shared.common.review ReviewCommentsContainer':
-        'pyams_content.features.review ReviewCommentsContainer'
+        'pyams_content.features.review ReviewCommentsContainer',
+    'pyams_portal.portlets.content ContentPortletSettings':
+        'pyams_content.portlet.content SharedContentPortletSettings',
+    'pyams_content.component.association.menu MenusContainer':
+        'pyams_content.features.menu MenusContainer',
+    'pyams_content.component.association.menu Menu':
+        'pyams_content.features.menu Menu',
+    'pyams_content.component.paragraph.keynumber KeyNumber':
+        'pyams_content.component.keynumber KeyNumber',
+    'pyams_content.component.paragraph.keynumber KeyNumberContainer':
+        'pyams_content.component.keynumber KeyNumberContainer',
+    'pyams_content.portlet.content SharedContentPortletSettings':
+        'pyams_content.shared.common.portlet.content SharedContentPortletSettings',
+    'pyams_content.portlet.navigation SimpleNavigationPortletSettings':
+        'pyams_content.features.menu.portlet.navigation.simple SimpleNavigationPortletSettings',
+    'pyams_content.portlet.navigation.interfaces ISimpleNavigationMenu':
+        'pyams_content.features.menu.portlet.navigation.interfaces.simple ISimpleNavigationMenu'
 }
 
 
@@ -76,11 +96,11 @@
 REQUIRED_INDEXES = [
     ('content_type', FieldIndexWithInterface, {'interface': IBaseContent,
                                                'discriminator': 'content_type'}),
-    ('role:owner', PrincipalsRoleIndex, {'role_id': 'pyams.Owner'}),
-    ('role:pilot', PrincipalsRoleIndex, {'role_id': 'pyams.Pilot'}),
-    ('role:manager', PrincipalsRoleIndex, {'role_id': 'pyams.Manager'}),
-    ('role:contributor', PrincipalsRoleIndex, {'role_id': 'pyams.Contributor'}),
-    ('role:webmaster', PrincipalsRoleIndex, {'role_id': 'pyams.Webmaster'}),
+    ('role:owner', PrincipalsRoleIndex, {'role_id': OWNER_ROLE}),
+    ('role:pilot', PrincipalsRoleIndex, {'role_id': PILOT_ROLE}),
+    ('role:manager', PrincipalsRoleIndex, {'role_id': MANAGER_ROLE}),
+    ('role:contributor', PrincipalsRoleIndex, {'role_id': CONTRIBUTOR_ROLE}),
+    ('role:webmaster', PrincipalsRoleIndex, {'role_id': WEBMASTER_ROLE}),
     ('parents', KeywordIndexWithInterface, {'interface': IPathElements,
                                             'discriminator': 'parents'}),
     ('workflow_state', FieldIndexWithInterface, {'interface': IWorkflowState,
@@ -109,7 +129,19 @@
                                                      'resolution': MINUTE_RESOLUTION}),
     ('first_publication_date', DatetimeIndexWithInterface, {'interface': IWorkflowPublicationInfo,
                                                             'discriminator': 'first_publication_date',
-                                                            'resolution': MINUTE_RESOLUTION})
+                                                            'resolution': MINUTE_RESOLUTION}),
+    ('themes', ThesaurusTermsListFieldIndex, {'interface': IThemesInfo,
+                                              'discriminator': 'themes',
+                                              'include_parents': False,
+                                              'include_synonyms': False}),
+    ('themes_tree', ThesaurusTermsListFieldIndex, {'interface': IThemesInfo,
+                                                   'discriminator': 'themes',
+                                                   'include_parents': True,
+                                                   'include_synonyms': False}),
+    ('themes_all', ThesaurusTermsListFieldIndex, {'interface': IThemesInfo,
+                                                  'discriminator': 'themes',
+                                                  'include_parents': True,
+                                                  'include_synonyms': True})
 ]
 
 
--- a/src/pyams_content/interfaces/__init__.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/interfaces/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -93,7 +93,9 @@
     """Base content interface"""
 
     __name__ = TextLine(title=_("Unique key"),
-                        description=_("WARNING: this key can't be modified after creation!!!"),
+                        description=_("WARNING: this key can't be modified after creation!!! Spaces, "
+                                      "uppercase letters ou accentuated characters will be replaced "
+                                      "automatically."),
                         required=True)
 
     title = I18nTextLineField(title=_("Title"),
Binary file src/pyams_content/locales/fr/LC_MESSAGES/pyams_content.mo has changed
--- a/src/pyams_content/locales/fr/LC_MESSAGES/pyams_content.po	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/locales/fr/LC_MESSAGES/pyams_content.po	Wed Jun 27 16:42:01 2018 +0200
@@ -5,7 +5,7 @@
 msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE 1.0\n"
-"POT-Creation-Date: 2018-06-06 13:20+0200\n"
+"POT-Creation-Date: 2018-06-26 14:52+0200\n"
 "PO-Revision-Date: 2015-09-10 10:42+0200\n"
 "Last-Translator: Thierry Florac <tflorac@ulthar.net>\n"
 "Language-Team: French\n"
@@ -48,31 +48,31 @@
 msgid "Webmaster (role)"
 msgstr "Webmestre (rôle)"
 
-#: src/pyams_content/__init__.py:62
+#: src/pyams_content/__init__.py:64
 msgid "Pilot (role)"
 msgstr "Pilote (rôle)"
 
-#: src/pyams_content/__init__.py:70
+#: src/pyams_content/__init__.py:74
 msgid "Manager (role)"
 msgstr "Responsable (rôle)"
 
-#: src/pyams_content/__init__.py:77
+#: src/pyams_content/__init__.py:83
 msgid "Owner (role)"
 msgstr "Propriétaire (rôle)"
 
-#: src/pyams_content/__init__.py:82
+#: src/pyams_content/__init__.py:88
 msgid "Contributor (role)"
 msgstr "Contributeur (rôle)"
 
-#: src/pyams_content/__init__.py:90
+#: src/pyams_content/__init__.py:99
 msgid "Reader (role)"
 msgstr "Relecteur (rôle)"
 
-#: src/pyams_content/__init__.py:96
+#: src/pyams_content/__init__.py:109
 msgid "Operator (role)"
 msgstr "Opérateur (rôle)"
 
-#: src/pyams_content/__init__.py:100
+#: src/pyams_content/__init__.py:114
 msgid "Guest user (role)"
 msgstr "Invité (rôle)"
 
@@ -80,25 +80,25 @@
 msgid "Gallery"
 msgstr "Galerie de médias"
 
-#: src/pyams_content/component/gallery/zmi/file.py:58
+#: src/pyams_content/component/gallery/zmi/file.py:57
 #: src/pyams_content/component/gallery/zmi/file.py:69
-#: src/pyams_content/component/gallery/zmi/paragraph.py:174
+#: src/pyams_content/component/gallery/zmi/paragraph.py:164
 msgid "Add media(s)"
 msgstr "Ajouter des médias"
 
-#: src/pyams_content/component/gallery/zmi/file.py:189
+#: src/pyams_content/component/gallery/zmi/file.py:184
 msgid "Update media properties"
 msgstr "Propriétés du média"
 
-#: src/pyams_content/component/gallery/zmi/file.py:249
+#: src/pyams_content/component/gallery/zmi/file.py:238
 msgid "Remove media..."
 msgstr "Supprimer le média"
 
-#: src/pyams_content/component/gallery/zmi/file.py:154
+#: src/pyams_content/component/gallery/zmi/file.py:148
 msgid "Show/hide media"
 msgstr "Cliquez pour rendre le média visible ou non"
 
-#: src/pyams_content/component/gallery/zmi/file.py:216
+#: src/pyams_content/component/gallery/zmi/file.py:211
 msgid "Audio content"
 msgstr "Contenu audio associé"
 
@@ -106,25 +106,25 @@
 msgid "Medias gallery..."
 msgstr "Galerie de médias"
 
-#: src/pyams_content/component/gallery/zmi/paragraph.py:67
+#: src/pyams_content/component/gallery/zmi/paragraph.py:69
 msgid "Add new gallery"
 msgstr "Ajout d'une galerie de médias"
 
-#: src/pyams_content/component/gallery/zmi/paragraph.py:99
+#: src/pyams_content/component/gallery/zmi/paragraph.py:96
 msgid "Edit gallery properties"
 msgstr "Propriétés de la galerie de médias"
 
-#: src/pyams_content/component/gallery/zmi/__init__.py:62
+#: src/pyams_content/component/gallery/zmi/__init__.py:63
 msgid "Update gallery properties"
 msgstr "Propriétés de la galerie de médias"
 
-#: src/pyams_content/component/gallery/zmi/__init__.py:101
+#: src/pyams_content/component/gallery/zmi/__init__.py:95
 msgid "Update gallery contents"
 msgstr "Contenu de la galerie de médias"
 
 #: src/pyams_content/component/gallery/zmi/interfaces.py:36
 msgid "Images or videos data"
-msgstr "Fichier (image ou vidéo)"
+msgstr "Fichier"
 
 #: src/pyams_content/component/gallery/zmi/interfaces.py:37
 msgid "You can upload a single file or choose to upload a whole ZIP archive"
@@ -135,8 +135,8 @@
 #: src/pyams_content/component/gallery/zmi/interfaces.py:40
 #: src/pyams_content/component/gallery/interfaces/__init__.py:61
 #: src/pyams_content/component/extfile/interfaces/__init__.py:44
-#: src/pyams_content/component/illustration/interfaces/__init__.py:56
-#: src/pyams_content/component/paragraph/interfaces/video.py:48
+#: src/pyams_content/component/illustration/interfaces/__init__.py:68
+#: src/pyams_content/component/paragraph/interfaces/video.py:52
 #: src/pyams_content/component/paragraph/interfaces/audio.py:48
 #: src/pyams_content/component/paragraph/interfaces/verbatim.py:44
 #: src/pyams_content/component/video/interfaces/__init__.py:52
@@ -146,7 +146,7 @@
 #: src/pyams_content/component/gallery/zmi/interfaces.py:41
 #: src/pyams_content/component/gallery/interfaces/__init__.py:62
 #: src/pyams_content/component/extfile/interfaces/__init__.py:45
-#: src/pyams_content/component/paragraph/interfaces/video.py:49
+#: src/pyams_content/component/paragraph/interfaces/video.py:53
 #: src/pyams_content/component/paragraph/interfaces/audio.py:49
 #: src/pyams_content/component/video/interfaces/__init__.py:53
 msgid "Name of document's author"
@@ -180,25 +180,27 @@
 msgstr "Galerie de médias"
 
 #: src/pyams_content/component/gallery/interfaces/__init__.py:47
-#: src/pyams_content/component/illustration/interfaces/__init__.py:41
-#: src/pyams_content/component/illustration/interfaces/__init__.py:77
+#: src/pyams_content/component/illustration/interfaces/__init__.py:46
+#: src/pyams_content/component/illustration/interfaces/__init__.py:105
 msgid "Image or video data"
-msgstr "Fichier (image ou vidéo)"
+msgstr "Fichier"
 
 #: src/pyams_content/component/gallery/interfaces/__init__.py:48
-#: src/pyams_content/component/illustration/interfaces/__init__.py:42
-#: src/pyams_content/component/illustration/interfaces/__init__.py:78
+#: src/pyams_content/component/illustration/interfaces/__init__.py:47
+#: src/pyams_content/component/illustration/interfaces/__init__.py:106
 msgid "Image or video content"
 msgstr ""
 "Cliquez sur le bouton 'Parcourir...' pour sélectionner un nouveau contenu..."
 
 #: src/pyams_content/component/gallery/interfaces/__init__.py:51
-#: src/pyams_content/component/illustration/interfaces/__init__.py:45
+#: src/pyams_content/component/illustration/interfaces/__init__.py:53
+#: src/pyams_content/component/paragraph/interfaces/video.py:45
+#: src/pyams_content/component/video/interfaces/__init__.py:75
 msgid "Legend"
 msgstr "Légende"
 
 #: src/pyams_content/component/gallery/interfaces/__init__.py:54
-#: src/pyams_content/component/illustration/interfaces/__init__.py:48
+#: src/pyams_content/component/illustration/interfaces/__init__.py:56
 #: src/pyams_content/reference/pictograms/interfaces/__init__.py:48
 msgid "Accessibility title"
 msgstr "Alternative (accessibilité)"
@@ -214,13 +216,13 @@
 #: src/pyams_content/component/gallery/interfaces/__init__.py:58
 #: src/pyams_content/component/gallery/interfaces/__init__.py:98
 #: src/pyams_content/component/extfile/interfaces/__init__.py:40
-#: src/pyams_content/component/illustration/interfaces/__init__.py:52
-#: src/pyams_content/component/paragraph/interfaces/video.py:44
+#: src/pyams_content/component/illustration/interfaces/__init__.py:64
+#: src/pyams_content/component/paragraph/interfaces/video.py:48
 #: src/pyams_content/component/paragraph/interfaces/audio.py:44
-#: src/pyams_content/component/links/interfaces/__init__.py:37
+#: src/pyams_content/component/links/interfaces/__init__.py:39
 #: src/pyams_content/component/video/interfaces/__init__.py:48
-#: src/pyams_content/shared/common/interfaces/__init__.py:145
-#: src/pyams_content/shared/form/interfaces/__init__.py:65
+#: src/pyams_content/shared/common/interfaces/__init__.py:154
+#: src/pyams_content/shared/form/interfaces/__init__.py:66
 msgid "Description"
 msgstr "Description"
 
@@ -273,17 +275,20 @@
 msgstr "Si 'non', ce média ne sera pas présenté aux internautes"
 
 #: src/pyams_content/component/gallery/interfaces/__init__.py:94
-#: src/pyams_content/component/paragraph/zmi/milestone.py:246
-#: src/pyams_content/component/paragraph/zmi/container.py:224
+#: src/pyams_content/component/keynumber/portlet/interfaces/__init__.py:31
+#: src/pyams_content/component/paragraph/zmi/milestone.py:231
+#: src/pyams_content/component/paragraph/zmi/container.py:223
 #: src/pyams_content/component/paragraph/interfaces/milestone.py:45
-#: src/pyams_content/component/links/zmi/reverse.py:71
+#: src/pyams_content/component/links/zmi/reverse.py:73
 #: src/pyams_content/shared/common/zmi/dashboard.py:109
 #: src/pyams_content/shared/common/zmi/templates/advanced-search.pt:188
 #: src/pyams_content/shared/imagemap/zmi/container.py:123
-#: src/pyams_content/shared/site/zmi/folder.py:68
+#: src/pyams_content/shared/site/zmi/folder.py:70
 #: src/pyams_content/root/zmi/templates/advanced-search.pt:188
-#: src/pyams_content/interfaces/__init__.py:99
-#: src/pyams_content/reference/pictograms/zmi/__init__.py:165
+#: src/pyams_content/interfaces/__init__.py:101
+#: src/pyams_content/reference/pictograms/zmi/__init__.py:150
+#: src/pyams_content/features/menu/portlet/navigation/interfaces/simple.py:31
+#: src/pyams_content/features/menu/portlet/navigation/interfaces/double.py:31
 msgid "Title"
 msgstr "Titre"
 
@@ -338,59 +343,59 @@
 msgid "Add external file"
 msgstr "Fichier"
 
-#: src/pyams_content/component/extfile/zmi/__init__.py:101
+#: src/pyams_content/component/extfile/zmi/__init__.py:103
 msgid "Add new external file"
 msgstr "Ajout d'un fichier"
 
-#: src/pyams_content/component/extfile/zmi/__init__.py:141
+#: src/pyams_content/component/extfile/zmi/__init__.py:138
 msgid "Update file properties"
 msgstr "Propriétés du fichier"
 
-#: src/pyams_content/component/extfile/zmi/__init__.py:181
+#: src/pyams_content/component/extfile/zmi/__init__.py:171
 msgid "Images"
 msgstr "Images"
 
-#: src/pyams_content/component/extfile/zmi/__init__.py:196
+#: src/pyams_content/component/extfile/zmi/__init__.py:186
 msgid "Add image"
 msgstr "Image téléchargeable"
 
-#: src/pyams_content/component/extfile/zmi/__init__.py:208
+#: src/pyams_content/component/extfile/zmi/__init__.py:200
 msgid "Add new image"
 msgstr "Ajout d'une image téléchargeable"
 
-#: src/pyams_content/component/extfile/zmi/__init__.py:244
+#: src/pyams_content/component/extfile/zmi/__init__.py:231
 msgid "Update image properties"
 msgstr "Propriétés de l'image téléchargeable"
 
-#: src/pyams_content/component/extfile/zmi/__init__.py:283
+#: src/pyams_content/component/extfile/zmi/__init__.py:264
 msgid "Videos"
 msgstr "Vidéos"
 
-#: src/pyams_content/component/extfile/zmi/__init__.py:298
+#: src/pyams_content/component/extfile/zmi/__init__.py:279
 msgid "Add video"
 msgstr "Vidéo téléchargeable"
 
-#: src/pyams_content/component/extfile/zmi/__init__.py:310
+#: src/pyams_content/component/extfile/zmi/__init__.py:293
 msgid "Add new video"
 msgstr "Ajout d'une vidéo téléchargeable"
 
-#: src/pyams_content/component/extfile/zmi/__init__.py:339
+#: src/pyams_content/component/extfile/zmi/__init__.py:317
 msgid "Update video properties"
 msgstr "Propriétés de la vidéo téléchargeable"
 
-#: src/pyams_content/component/extfile/zmi/__init__.py:371
+#: src/pyams_content/component/extfile/zmi/__init__.py:343
 msgid "Audios files"
 msgstr "Fichiers audios"
 
-#: src/pyams_content/component/extfile/zmi/__init__.py:386
+#: src/pyams_content/component/extfile/zmi/__init__.py:358
 msgid "Add audio file"
 msgstr "Bande son téléchargeable"
 
-#: src/pyams_content/component/extfile/zmi/__init__.py:398
+#: src/pyams_content/component/extfile/zmi/__init__.py:372
 msgid "Add new audio file"
 msgstr "Ajout d'une bande son téléchargeable"
 
-#: src/pyams_content/component/extfile/zmi/__init__.py:427
+#: src/pyams_content/component/extfile/zmi/__init__.py:396
 msgid "Update audio file properties"
 msgstr "Propriétés de la bande son téléchargeable"
 
@@ -399,7 +404,7 @@
 msgstr "Type de fichier joint"
 
 #: src/pyams_content/component/extfile/interfaces/__init__.py:36
-#: src/pyams_content/component/links/interfaces/__init__.py:33
+#: src/pyams_content/component/links/interfaces/__init__.py:35
 #: src/pyams_content/shared/imagemap/interfaces/__init__.py:54
 #: src/pyams_content/shared/site/interfaces/__init__.py:113
 msgid "Alternate title"
@@ -410,14 +415,14 @@
 msgstr "Titre présenté aux internautes"
 
 #: src/pyams_content/component/extfile/interfaces/__init__.py:41
-#: src/pyams_content/component/paragraph/interfaces/video.py:45
+#: src/pyams_content/component/paragraph/interfaces/video.py:49
 #: src/pyams_content/component/paragraph/interfaces/audio.py:45
 #: src/pyams_content/component/video/interfaces/__init__.py:49
 msgid "File description displayed by front-office template"
 msgstr "Description du fichier, présentée aux internautes"
 
 #: src/pyams_content/component/extfile/interfaces/__init__.py:48
-#: src/pyams_content/component/links/interfaces/__init__.py:59
+#: src/pyams_content/component/links/interfaces/__init__.py:68
 msgid "Language"
 msgstr "Langue"
 
@@ -456,7 +461,7 @@
 "Cliquez sur le bouton 'Parcourir...' pour sélectionner un nouveau contenu..."
 
 #: src/pyams_content/component/extfile/interfaces/__init__.py:81
-#: src/pyams_content/component/paragraph/interfaces/video.py:52
+#: src/pyams_content/component/paragraph/interfaces/video.py:41
 msgid "Video data"
 msgstr "Fichier"
 
@@ -471,30 +476,145 @@
 msgstr ""
 "Cliquez sur le bouton 'Parcourir...' pour sélectionner un nouveau contenu"
 
-#: src/pyams_content/component/illustration/__init__.py:132
-#: src/pyams_content/component/illustration/zmi/__init__.py:54
-#: src/pyams_content/component/illustration/zmi/__init__.py:81
-#: src/pyams_content/component/illustration/interfaces/__init__.py:71
+#: src/pyams_content/component/keynumber/__init__.py:189
+#: src/pyams_content/component/keynumber/zmi/__init__.py:198
+#: src/pyams_content/component/keynumber/portlet/zmi/__init__.py:79
+#: src/pyams_content/component/paragraph/interfaces/keynumber.py:29
+msgid "Key numbers"
+msgstr "Chiffres-clés"
+
+#. Default: Header
+#: src/pyams_content/component/keynumber/zmi/__init__.py:146
+#: src/pyams_content/component/keynumber/interfaces/__init__.py:44
+msgid "key-number-label"
+msgstr "En-tête"
+
+#: src/pyams_content/component/keynumber/zmi/__init__.py:158
+#: src/pyams_content/component/keynumber/interfaces/__init__.py:49
+msgid "Number"
+msgstr "Chiffre"
+
+#. Default: Unit
+#: src/pyams_content/component/keynumber/zmi/__init__.py:167
+#: src/pyams_content/component/keynumber/interfaces/__init__.py:53
+msgid "key-number-unit"
+msgstr "Unité"
+
+#: src/pyams_content/component/keynumber/zmi/__init__.py:179
+#: src/pyams_content/component/keynumber/interfaces/__init__.py:57
+#: src/pyams_content/component/paragraph/zmi/pictogram.py:267
+#: src/pyams_content/component/paragraph/interfaces/pictogram.py:58
+msgid "Associated text"
+msgstr "Texte associé"
+
+#: src/pyams_content/component/keynumber/zmi/__init__.py:217
+msgid "Add keynumber"
+msgstr "Ajouter un chiffre-clé"
+
+#: src/pyams_content/component/keynumber/zmi/__init__.py:229
+msgid "Add new keynumber"
+msgstr "Ajout d'un chiffre-clé"
+
+#: src/pyams_content/component/keynumber/zmi/__init__.py:258
+msgid "Edit keynumber properties"
+msgstr "Propriétés du chiffre-clé"
+
+#: src/pyams_content/component/keynumber/zmi/__init__.py:244
+msgid "Key number was correctly added"
+msgstr "Le chiffre-clé a été ajouté."
+
+#: src/pyams_content/component/keynumber/portlet/__init__.py:71
+msgid "Key Numbers"
+msgstr "Chiffres-clés"
+
+#: src/pyams_content/component/keynumber/portlet/zmi/__init__.py:102
+#: src/pyams_content/component/keynumber/portlet/zmi/templates/keynumber-preview.pt:22
+msgid "Associated links"
+msgstr "Liens associés"
+
+#: src/pyams_content/component/keynumber/portlet/zmi/templates/keynumber-preview.pt:27
+#: src/pyams_content/features/menu/portlet/navigation/zmi/templates/double-preview.pt:12
+#: src/pyams_content/features/menu/portlet/navigation/zmi/templates/simple-preview.pt:9
+msgid "Link target is not published!"
+msgstr "Le contenu ciblé n'est pas publié"
+
+#: src/pyams_content/component/keynumber/portlet/interfaces/__init__.py:32
+msgid "Portlet title"
+msgstr "Titre"
+
+#: src/pyams_content/component/keynumber/portlet/interfaces/__init__.py:35
+msgid "Teaser"
+msgstr "Accroche"
+
+#: src/pyams_content/component/keynumber/portlet/interfaces/__init__.py:36
+msgid "Short text displayed above key numbers"
+msgstr "Texte d'introduction des chiffres-clés"
+
+#: src/pyams_content/component/keynumber/interfaces/__init__.py:39
+#: src/pyams_content/component/paragraph/interfaces/milestone.py:40
+#: src/pyams_content/component/paragraph/interfaces/__init__.py:44
+#: src/pyams_content/component/paragraph/interfaces/pictogram.py:41
+#: src/pyams_content/component/association/interfaces/__init__.py:42
+#: src/pyams_content/shared/form/interfaces/__init__.py:87
+#: src/pyams_content/shared/site/interfaces/__init__.py:117
+#: src/pyams_content/features/alert/interfaces.py:54
+#: src/pyams_content/features/menu/interfaces/__init__.py:58
+msgid "Visible?"
+msgstr "Visible ?"
+
+#: src/pyams_content/component/keynumber/interfaces/__init__.py:40
+msgid "Is this key number visible in front-office?"
+msgstr "Si 'non', ce chiffre-clé ne sera pas présenté aux internautes"
+
+#: src/pyams_content/component/keynumber/interfaces/__init__.py:45
+msgid ""
+"Small text to be displayed above number (according to selected renderer)"
+msgstr ""
+"Texte court affiché au-dessus du chiffre (selon le mode de rendu sélectionné)"
+
+#: src/pyams_content/component/keynumber/interfaces/__init__.py:50
+msgid "Key number value"
+msgstr "Chiffre"
+
+#: src/pyams_content/component/keynumber/interfaces/__init__.py:54
+msgid "Displayed unit"
+msgstr "Unité affichée"
+
+#: src/pyams_content/component/keynumber/interfaces/__init__.py:58
+msgid "The way this text will be rendered depends on presentation template"
+msgstr ""
+"La présentation de cette information peut varier en fonction du mode de "
+"rendu choisi"
+
+#: src/pyams_content/component/illustration/__init__.py:177
+#: src/pyams_content/component/illustration/zmi/paragraph.py:168
+#: src/pyams_content/component/illustration/zmi/__init__.py:56
+#: src/pyams_content/component/illustration/zmi/__init__.py:100
+#: src/pyams_content/component/illustration/interfaces/__init__.py:99
 msgid "Illustration"
 msgstr "Illustration"
 
-#: src/pyams_content/component/illustration/zmi/paragraph.py:57
+#: src/pyams_content/component/illustration/zmi/paragraph.py:60
 msgid "Illustration..."
 msgstr "Illustration"
 
-#: src/pyams_content/component/illustration/zmi/paragraph.py:68
+#: src/pyams_content/component/illustration/zmi/paragraph.py:73
 msgid "Add new illustration"
 msgstr "Ajout d'une illustration"
 
-#: src/pyams_content/component/illustration/zmi/paragraph.py:102
+#: src/pyams_content/component/illustration/zmi/paragraph.py:103
 msgid "Edit illustration properties"
 msgstr "Propriétés de l'illustration"
 
-#: src/pyams_content/component/illustration/zmi/__init__.py:83
+#: src/pyams_content/component/illustration/zmi/__init__.py:155
+msgid "Navigation link illustration"
+msgstr "Illustration de navigation"
+
+#: src/pyams_content/component/illustration/zmi/__init__.py:102
 msgid "Header illustration"
 msgstr "Illustration d'en-tête"
 
-#: src/pyams_content/component/illustration/interfaces/__init__.py:49
+#: src/pyams_content/component/illustration/interfaces/__init__.py:57
 #: src/pyams_content/reference/pictograms/interfaces/__init__.py:49
 msgid "Alternate title used to describe image content"
 msgstr ""
@@ -503,15 +623,15 @@
 "déficiences visuelles. Il doit donc décrire le contenu, pour se conformer "
 "aux normes d'accessibilité."
 
-#: src/pyams_content/component/illustration/interfaces/__init__.py:57
+#: src/pyams_content/component/illustration/interfaces/__init__.py:69
 msgid "Name of picture's author"
 msgstr "Sous la forme \"Prénom Nom / Organisme\""
 
-#: src/pyams_content/component/illustration/interfaces/__init__.py:60
+#: src/pyams_content/component/illustration/interfaces/__init__.py:72
 msgid "Illustration template"
 msgstr "Mode de rendu"
 
-#: src/pyams_content/component/illustration/interfaces/__init__.py:61
+#: src/pyams_content/component/illustration/interfaces/__init__.py:73
 msgid "Presentation template used for illustration"
 msgstr ""
 "<span>Modèle de présentation utilisé par cette illustration.<br /"
@@ -534,7 +654,7 @@
 msgid "no visible paragraph"
 msgstr "aucun bloc de contenu visible"
 
-#: src/pyams_content/component/paragraph/pictogram.py:134
+#: src/pyams_content/component/paragraph/pictogram.py:135
 msgid "Selected pictogram is missing"
 msgstr "le pictogramme sélectionné est introuvable"
 
@@ -542,62 +662,62 @@
 msgid "Milestones..."
 msgstr "Chronologie"
 
-#: src/pyams_content/component/paragraph/zmi/milestone.py:88
+#: src/pyams_content/component/paragraph/zmi/milestone.py:90
 msgid "Add new milestone paragraph"
 msgstr "Ajout d'une chronologie"
 
-#: src/pyams_content/component/paragraph/zmi/milestone.py:120
+#: src/pyams_content/component/paragraph/zmi/milestone.py:117
 msgid "Edit milestone paragraph properties"
 msgstr "Propriétés de la chronologie"
 
-#: src/pyams_content/component/paragraph/zmi/milestone.py:258
+#: src/pyams_content/component/paragraph/zmi/milestone.py:243
 #: src/pyams_content/component/paragraph/interfaces/milestone.py:49
 msgid "Associated label"
 msgstr "Information associée"
 
-#: src/pyams_content/component/paragraph/zmi/milestone.py:270
+#: src/pyams_content/component/paragraph/zmi/milestone.py:255
 #: src/pyams_content/component/paragraph/interfaces/milestone.py:53
 msgid "Anchor"
 msgstr "Ancre"
 
-#: src/pyams_content/component/paragraph/zmi/milestone.py:299
+#: src/pyams_content/component/paragraph/zmi/milestone.py:284
 #: src/pyams_content/component/paragraph/interfaces/milestone.py:76
 msgid "Milestones"
 msgstr "Chronologie"
 
-#: src/pyams_content/component/paragraph/zmi/milestone.py:314
+#: src/pyams_content/component/paragraph/zmi/milestone.py:299
 msgid "Add milestone"
 msgstr "Ajouter un jalon"
 
-#: src/pyams_content/component/paragraph/zmi/milestone.py:325
+#: src/pyams_content/component/paragraph/zmi/milestone.py:312
 msgid "Add new milestone"
 msgstr "Ajout d'un jalon"
 
-#: src/pyams_content/component/paragraph/zmi/milestone.py:358
+#: src/pyams_content/component/paragraph/zmi/milestone.py:339
 msgid "Edit milestone properties"
 msgstr "Propriétés du jalon"
 
-#: src/pyams_content/component/paragraph/zmi/milestone.py:347
+#: src/pyams_content/component/paragraph/zmi/milestone.py:327
 msgid "Milestone was correctly added"
 msgstr "Le jalon a été ajouté."
 
-#: src/pyams_content/component/paragraph/zmi/milestone.py:285
+#: src/pyams_content/component/paragraph/zmi/milestone.py:270
 msgid "(missing paragraph)"
 msgstr "(paragraphe supprimé)"
 
-#: src/pyams_content/component/paragraph/zmi/keypoint.py:50
+#: src/pyams_content/component/paragraph/zmi/keypoint.py:51
 msgid "Key points..."
 msgstr "Points clés"
 
-#: src/pyams_content/component/paragraph/zmi/keypoint.py:61
+#: src/pyams_content/component/paragraph/zmi/keypoint.py:64
 msgid "Add new key points paragraph"
 msgstr "Ajout de points clés"
 
-#: src/pyams_content/component/paragraph/zmi/keypoint.py:93
+#: src/pyams_content/component/paragraph/zmi/keypoint.py:91
 msgid "Edit key points paragraph properties"
 msgstr "Propriétés des points clés"
 
-#: src/pyams_content/component/paragraph/zmi/__init__.py:66
+#: src/pyams_content/component/paragraph/zmi/__init__.py:65
 msgid "Content block types..."
 msgstr "Types de blocs de contenu"
 
@@ -605,7 +725,7 @@
 msgid "Content block types"
 msgstr "Types de blocs de contenu"
 
-#: src/pyams_content/component/paragraph/zmi/__init__.py:96
+#: src/pyams_content/component/paragraph/zmi/__init__.py:89
 msgid ""
 "You can define which types of paragraphs are allowed in this container.\n"
 "\n"
@@ -624,230 +744,176 @@
 "REMARQUE : supprimer des types de la liste des types de blocs autorisés sera "
 "sans effet sur les contenus existants."
 
-#: src/pyams_content/component/paragraph/zmi/__init__.py:214
+#: src/pyams_content/component/paragraph/zmi/__init__.py:207
 #: src/pyams_content/shared/common/zmi/templates/preview-input.pt:39
 #: src/pyams_content/features/preview/zmi/__init__.py:45
 msgid "Preview"
 msgstr "Aperçu"
 
-#: src/pyams_content/component/paragraph/zmi/__init__.py:219
-#: src/pyams_content/shared/common/zmi/workflow.py:125
-#: src/pyams_content/shared/common/zmi/workflow.py:217
-#: src/pyams_content/shared/common/zmi/workflow.py:262
-#: src/pyams_content/shared/common/zmi/workflow.py:321
-#: src/pyams_content/shared/common/zmi/workflow.py:415
-#: src/pyams_content/shared/common/zmi/workflow.py:476
-#: src/pyams_content/shared/common/zmi/workflow.py:521
-#: src/pyams_content/shared/common/zmi/workflow.py:567
-#: src/pyams_content/shared/common/zmi/workflow.py:615
-#: src/pyams_content/shared/common/zmi/workflow.py:660
-#: src/pyams_content/shared/common/zmi/workflow.py:706
-#: src/pyams_content/shared/common/zmi/workflow.py:762
-#: src/pyams_content/shared/common/zmi/__init__.py:276
-#: src/pyams_content/shared/common/zmi/owner.py:74
+#: src/pyams_content/component/paragraph/zmi/__init__.py:212
+#: src/pyams_content/shared/common/zmi/workflow.py:122
+#: src/pyams_content/shared/common/zmi/workflow.py:209
+#: src/pyams_content/shared/common/zmi/workflow.py:249
+#: src/pyams_content/shared/common/zmi/workflow.py:303
+#: src/pyams_content/shared/common/zmi/workflow.py:392
+#: src/pyams_content/shared/common/zmi/workflow.py:448
+#: src/pyams_content/shared/common/zmi/workflow.py:488
+#: src/pyams_content/shared/common/zmi/workflow.py:529
+#: src/pyams_content/shared/common/zmi/workflow.py:572
+#: src/pyams_content/shared/common/zmi/workflow.py:612
+#: src/pyams_content/shared/common/zmi/workflow.py:653
+#: src/pyams_content/shared/common/zmi/workflow.py:704
+#: src/pyams_content/shared/common/zmi/__init__.py:275
+#: src/pyams_content/shared/common/zmi/owner.py:73
 #: src/pyams_content/features/review/zmi/__init__.py:90
 msgid "Cancel"
 msgstr "Annuler"
 
-#: src/pyams_content/component/paragraph/zmi/__init__.py:221
+#: src/pyams_content/component/paragraph/zmi/__init__.py:214
 msgid "Submit"
 msgstr "Enregistrer"
 
-#: src/pyams_content/component/paragraph/zmi/__init__.py:202
+#: src/pyams_content/component/paragraph/zmi/__init__.py:195
 msgid "Paragraph was correctly added."
 msgstr "Le bloc a été ajouté."
 
-#: src/pyams_content/component/paragraph/zmi/video.py:54
+#: src/pyams_content/component/paragraph/zmi/video.py:53
 msgid "Video paragraph..."
 msgstr "Vidéo"
 
-#: src/pyams_content/component/paragraph/zmi/video.py:65
+#: src/pyams_content/component/paragraph/zmi/video.py:66
 msgid "Add new video paragraph"
 msgstr "Ajout d'une vidéo"
 
-#: src/pyams_content/component/paragraph/zmi/video.py:112
-#: src/pyams_content/component/video/zmi/paragraph.py:209
+#: src/pyams_content/component/paragraph/zmi/video.py:94
+#: src/pyams_content/component/video/zmi/paragraph.py:192
 msgid "Edit video properties"
 msgstr "Propriétés de la vidéo"
 
-#: src/pyams_content/component/paragraph/zmi/video.py:83
-#: src/pyams_content/component/paragraph/zmi/video.py:133
-#: src/pyams_content/component/paragraph/zmi/audio.py:83
-#: src/pyams_content/component/paragraph/zmi/audio.py:133
-#: src/pyams_content/component/video/zmi/paragraph.py:103
-#: src/pyams_content/component/video/zmi/paragraph.py:238
-msgid "HTML content"
-msgstr "Contenu HTML"
-
 #: src/pyams_content/component/paragraph/zmi/container.py:74
 msgid "Contents..."
 msgstr "Contenu"
 
-#: src/pyams_content/component/paragraph/zmi/container.py:240
+#: src/pyams_content/component/paragraph/zmi/container.py:239
 msgid "Show/hide all paragraphs"
 msgstr "Afficher/masquer tous les blocs"
 
-#: src/pyams_content/component/paragraph/zmi/container.py:288
-#: src/pyams_content/component/paragraph/zmi/container.py:297
-#: src/pyams_content/component/paragraph/zmi/container.py:310
+#: src/pyams_content/component/paragraph/zmi/container.py:287
+#: src/pyams_content/component/paragraph/zmi/container.py:296
+#: src/pyams_content/component/paragraph/zmi/container.py:309
 msgid "Content blocks"
 msgstr "Blocs de contenu"
 
-#: src/pyams_content/component/paragraph/zmi/container.py:367
+#: src/pyams_content/component/paragraph/zmi/container.py:366
 msgid "Links and attachments..."
 msgstr "Récap. liens et PJ"
 
-#: src/pyams_content/component/paragraph/zmi/container.py:379
+#: src/pyams_content/component/paragraph/zmi/container.py:378
 msgid "Content blocks links and attachments"
 msgstr "Récapitulatif des liens et pièces jointes par bloc de contenu"
 
-#: src/pyams_content/component/paragraph/zmi/container.py:127
+#: src/pyams_content/component/paragraph/zmi/container.py:126
 msgid "No currently defined paragraph."
 msgstr "Aucun bloc n'est associé à ce contenu."
 
-#: src/pyams_content/component/paragraph/zmi/container.py:249
+#: src/pyams_content/component/paragraph/zmi/container.py:248
 msgid "Click to open/close all paragraphs editors"
 msgstr "Afficher/masquer tous les blocs"
 
-#: src/pyams_content/component/paragraph/zmi/container.py:262
+#: src/pyams_content/component/paragraph/zmi/container.py:261
 msgid "Click to open/close paragraph editor"
 msgstr "Afficher/masquer ce bloc"
 
-#: src/pyams_content/component/paragraph/zmi/container.py:133
+#: src/pyams_content/component/paragraph/zmi/container.py:132
 msgid "Check allowed paragraph types to be able to create new paragraphs."
 msgstr ""
 "Vérifiez le paramétrage des types de blocs de contenu autorisés pour pouvoir "
 "ajouter de nouveaux blocs."
 
-#: src/pyams_content/component/paragraph/zmi/pictogram.py:83
+#: src/pyams_content/component/paragraph/zmi/pictogram.py:82
 msgid "Pictograms..."
 msgstr "Pictogrammes"
 
-#: src/pyams_content/component/paragraph/zmi/pictogram.py:94
+#: src/pyams_content/component/paragraph/zmi/pictogram.py:95
 msgid "Add new pictogram paragraph"
 msgstr "Ajout de pictogrammes"
 
-#: src/pyams_content/component/paragraph/zmi/pictogram.py:126
+#: src/pyams_content/component/paragraph/zmi/pictogram.py:122
 msgid "Edit pictogram paragraph properties"
 msgstr "Propriétés des pictogrammes"
 
 #. Default: Header
-#: src/pyams_content/component/paragraph/zmi/pictogram.py:268
+#: src/pyams_content/component/paragraph/zmi/pictogram.py:252
 msgid "pictogram-item-header"
 msgstr "En-tête"
 
-#: src/pyams_content/component/paragraph/zmi/pictogram.py:283
-#: src/pyams_content/component/paragraph/zmi/keynumber.py:263
-#: src/pyams_content/component/paragraph/interfaces/pictogram.py:58
-#: src/pyams_content/component/paragraph/interfaces/keynumber.py:54
-msgid "Associated text"
-msgstr "Texte associé"
-
-#: src/pyams_content/component/paragraph/zmi/pictogram.py:305
+#: src/pyams_content/component/paragraph/zmi/pictogram.py:289
 #: src/pyams_content/component/paragraph/interfaces/pictogram.py:80
 msgid "Pictograms"
 msgstr "Pictogrammes"
 
-#: src/pyams_content/component/paragraph/zmi/pictogram.py:320
-#: src/pyams_content/reference/pictograms/zmi/__init__.py:62
+#: src/pyams_content/component/paragraph/zmi/pictogram.py:304
+#: src/pyams_content/reference/pictograms/zmi/__init__.py:59
 msgid "Add pictogram"
 msgstr "Ajouter un pictogramme"
 
-#: src/pyams_content/component/paragraph/zmi/pictogram.py:331
-#: src/pyams_content/reference/pictograms/zmi/__init__.py:73
+#: src/pyams_content/component/paragraph/zmi/pictogram.py:317
+#: src/pyams_content/reference/pictograms/zmi/__init__.py:71
 msgid "Add new pictogram"
 msgstr "Ajout d'un pictogramme"
 
-#: src/pyams_content/component/paragraph/zmi/pictogram.py:393
-#: src/pyams_content/reference/pictograms/zmi/__init__.py:103
+#: src/pyams_content/component/paragraph/zmi/pictogram.py:359
+#: src/pyams_content/reference/pictograms/zmi/__init__.py:95
 msgid "Edit pictogram properties"
 msgstr "Propriétés du pictogramme"
 
-#: src/pyams_content/component/paragraph/zmi/pictogram.py:374
+#: src/pyams_content/component/paragraph/zmi/pictogram.py:339
 msgid "Pictogram was correctly added"
 msgstr "Le pictogramme a été ajouté."
 
-#: src/pyams_content/component/paragraph/zmi/pictogram.py:384
-#: src/pyams_content/component/paragraph/zmi/pictogram.py:448
+#: src/pyams_content/component/paragraph/zmi/pictogram.py:349
+#: src/pyams_content/component/paragraph/zmi/pictogram.py:387
 msgid "You must select a pictogram!"
 msgstr "Vous devez sélectionner un pictogramme !"
 
-#: src/pyams_content/component/paragraph/zmi/pictogram.py:347
-#: src/pyams_content/reference/pictograms/zmi/__init__.py:186
-msgid "Default header: --"
-msgstr "En-tête par défaut : --"
-
-#: src/pyams_content/component/paragraph/zmi/pictogram.py:414
-#: src/pyams_content/reference/pictograms/zmi/__init__.py:190
-#, python-format
-msgid "Default header: {0}"
-msgstr "En-tête par défaut : {0}"
-
 #: src/pyams_content/component/paragraph/zmi/audio.py:54
 msgid "Audio paragraph..."
 msgstr "Bande son"
 
-#: src/pyams_content/component/paragraph/zmi/audio.py:65
+#: src/pyams_content/component/paragraph/zmi/audio.py:67
 msgid "Add new audio paragraph"
 msgstr "Ajout d'une bande son"
 
-#: src/pyams_content/component/paragraph/zmi/audio.py:112
+#: src/pyams_content/component/paragraph/zmi/audio.py:108
 msgid "Edit audio properties"
 msgstr "Propriétés de la bande son"
 
-#: src/pyams_content/component/paragraph/zmi/keynumber.py:76
+#: src/pyams_content/component/paragraph/zmi/audio.py:84
+#: src/pyams_content/component/paragraph/zmi/audio.py:128
+msgid "HTML content"
+msgstr "Contenu HTML"
+
+#: src/pyams_content/component/paragraph/zmi/keynumber.py:55
 msgid "Key numbers..."
 msgstr "Chiffres-clés"
 
-#: src/pyams_content/component/paragraph/zmi/keynumber.py:87
+#: src/pyams_content/component/paragraph/zmi/keynumber.py:68
 msgid "Add new key number paragraph"
 msgstr "Ajout de chiffres-clés"
 
-#: src/pyams_content/component/paragraph/zmi/keynumber.py:119
+#: src/pyams_content/component/paragraph/zmi/keynumber.py:96
 msgid "Edit key number paragraph properties"
 msgstr "Propriétés des chiffres-clés"
 
-#: src/pyams_content/component/paragraph/zmi/keynumber.py:242
-#: src/pyams_content/component/paragraph/interfaces/keynumber.py:45
-msgid "Number"
-msgstr "Chiffre"
-
-#. Default: Header
-#: src/pyams_content/component/paragraph/zmi/keynumber.py:251
-#: src/pyams_content/component/paragraph/interfaces/keynumber.py:49
-msgid "key-number-label"
-msgstr "En-tête"
-
-#: src/pyams_content/component/paragraph/zmi/keynumber.py:282
-#: src/pyams_content/component/paragraph/interfaces/keynumber.py:76
-msgid "Key numbers"
-msgstr "Chiffres-clés"
-
-#: src/pyams_content/component/paragraph/zmi/keynumber.py:297
-msgid "Add keynumber"
-msgstr "Ajouter un chiffre-clé"
-
-#: src/pyams_content/component/paragraph/zmi/keynumber.py:308
-msgid "Add new keynumber"
-msgstr "Ajout d'un chiffre-clé"
-
-#: src/pyams_content/component/paragraph/zmi/keynumber.py:341
-msgid "Edit keynumber properties"
-msgstr "Propriétés du chiffre-clé"
-
-#: src/pyams_content/component/paragraph/zmi/keynumber.py:330
-msgid "Key number was correctly added"
-msgstr "Le chiffre-clé a été ajouté."
-
 #: src/pyams_content/component/paragraph/zmi/frame.py:84
 msgid "Framed text..."
 msgstr "Encadré"
 
-#: src/pyams_content/component/paragraph/zmi/frame.py:96
+#: src/pyams_content/component/paragraph/zmi/frame.py:98
 msgid "Add new framed text paragraph"
 msgstr "Ajout d'un encadré"
 
-#: src/pyams_content/component/paragraph/zmi/frame.py:132
+#: src/pyams_content/component/paragraph/zmi/frame.py:129
 msgid "Edit framed text paragraph properties"
 msgstr "Propriétés de l'encadré"
 
@@ -855,11 +921,11 @@
 msgid "Verbatim..."
 msgstr "Verbatim"
 
-#: src/pyams_content/component/paragraph/zmi/verbatim.py:67
+#: src/pyams_content/component/paragraph/zmi/verbatim.py:69
 msgid "Add new verbatim paragraph"
 msgstr "Ajout d'un verbatim"
 
-#: src/pyams_content/component/paragraph/zmi/verbatim.py:99
+#: src/pyams_content/component/paragraph/zmi/verbatim.py:96
 msgid "Edit verbatim paragraph properties"
 msgstr "Propriétés du verbatim"
 
@@ -867,23 +933,23 @@
 msgid "Raw HTML..."
 msgstr "Code HTML"
 
-#: src/pyams_content/component/paragraph/zmi/html.py:87
+#: src/pyams_content/component/paragraph/zmi/html.py:89
 msgid "Add new raw HTML paragraph"
 msgstr "Ajout d'un bloc de code HTML"
 
-#: src/pyams_content/component/paragraph/zmi/html.py:122
+#: src/pyams_content/component/paragraph/zmi/html.py:119
 msgid "Edit raw HTML paragraph properties"
 msgstr "Propriétés du code HTML"
 
-#: src/pyams_content/component/paragraph/zmi/html.py:174
+#: src/pyams_content/component/paragraph/zmi/html.py:159
 msgid "Rich text..."
 msgstr "Texte enrichi"
 
-#: src/pyams_content/component/paragraph/zmi/html.py:185
+#: src/pyams_content/component/paragraph/zmi/html.py:172
 msgid "Add new rich text paragraph"
 msgstr "Ajout d'un bloc de texte enrichi"
 
-#: src/pyams_content/component/paragraph/zmi/html.py:220
+#: src/pyams_content/component/paragraph/zmi/html.py:202
 msgid "Edit rich text paragraph properties"
 msgstr "Propriétés du texte enrichi"
 
@@ -891,11 +957,11 @@
 msgid "Contact card..."
 msgstr "Fiche contact"
 
-#: src/pyams_content/component/paragraph/zmi/contact.py:64
+#: src/pyams_content/component/paragraph/zmi/contact.py:66
 msgid "Add new contact card"
 msgstr "Ajout d'une fiche contact"
 
-#: src/pyams_content/component/paragraph/zmi/contact.py:97
+#: src/pyams_content/component/paragraph/zmi/contact.py:94
 msgid "Edit contact card properties"
 msgstr "Propriétés de la fiche contact"
 
@@ -903,25 +969,14 @@
 msgid "Header..."
 msgstr "Chapô"
 
-#: src/pyams_content/component/paragraph/zmi/header.py:61
+#: src/pyams_content/component/paragraph/zmi/header.py:63
 msgid "Add new header paragraph"
 msgstr "Ajout d'un chapô"
 
-#: src/pyams_content/component/paragraph/zmi/header.py:93
+#: src/pyams_content/component/paragraph/zmi/header.py:90
 msgid "Edit header paragraph properties"
 msgstr "Propriétés du chapô"
 
-#: src/pyams_content/component/paragraph/interfaces/milestone.py:40
-#: src/pyams_content/component/paragraph/interfaces/__init__.py:44
-#: src/pyams_content/component/paragraph/interfaces/pictogram.py:41
-#: src/pyams_content/component/paragraph/interfaces/keynumber.py:40
-#: src/pyams_content/component/association/interfaces/__init__.py:42
-#: src/pyams_content/shared/form/interfaces/__init__.py:86
-#: src/pyams_content/shared/site/interfaces/__init__.py:117
-#: src/pyams_content/features/alert/interfaces.py:54
-msgid "Visible?"
-msgstr "Visible ?"
-
 #: src/pyams_content/component/paragraph/interfaces/milestone.py:41
 msgid "Is this milestone visible in front-office?"
 msgstr "Si 'non', ce jalon ne sera pas présenté aux internautes"
@@ -990,8 +1045,8 @@
 msgstr "Liste des types de blocs de contenu autorisés pour ce gabarit."
 
 #: src/pyams_content/component/paragraph/interfaces/__init__.py:85
-#: src/pyams_content/shared/common/zmi/types.py:167
-#: src/pyams_content/shared/common/zmi/types.py:395
+#: src/pyams_content/shared/common/zmi/types.py:169
+#: src/pyams_content/shared/common/zmi/types.py:380
 msgid "Default paragraphs"
 msgstr "Types de blocs par défaut"
 
@@ -999,14 +1054,7 @@
 msgid "List of paragraphs automatically added to a new content"
 msgstr "Liste des types de blocs ajoutés automatiquement aux nouveaux contenus"
 
-#: src/pyams_content/component/paragraph/interfaces/video.py:41
-#: src/pyams_content/component/paragraph/interfaces/audio.py:41
-#: src/pyams_content/component/paragraph/interfaces/html.py:63
-#: src/pyams_content/component/video/interfaces/__init__.py:75
-msgid "Body"
-msgstr "Contenu HTML"
-
-#: src/pyams_content/component/paragraph/interfaces/video.py:53
+#: src/pyams_content/component/paragraph/interfaces/video.py:42
 msgid "Video file content"
 msgstr ""
 "Cliquez sur le bouton 'Parcourir...' pour sélectionner un nouveau contenu"
@@ -1026,7 +1074,8 @@
 msgstr "Si 'non', ce pictogramme ne sera pas présenté aux internautes"
 
 #: src/pyams_content/component/paragraph/interfaces/pictogram.py:46
-#: src/pyams_content/shared/common/interfaces/types.py:67
+#: src/pyams_content/component/links/interfaces/__init__.py:43
+#: src/pyams_content/shared/common/interfaces/types.py:68
 #: src/pyams_content/features/alert/interfaces.py:79
 msgid "Pictogram"
 msgstr "Pictogramme"
@@ -1045,7 +1094,7 @@
 "Alternate pictogram label; if not specified, the pictogram header will be "
 "used"
 msgstr ""
-"EN-tête de substitution utilisé par le pictogramme; si rien n'est spécifié, "
+"En-tête de substitution utilisé par le pictogramme; si rien n'est spécifié, "
 "l'en-tête du pictogramme sélectionné sera utilisé."
 
 #: src/pyams_content/component/paragraph/interfaces/pictogram.py:59
@@ -1064,6 +1113,11 @@
 msgid "Audio"
 msgstr "Bande son"
 
+#: src/pyams_content/component/paragraph/interfaces/audio.py:41
+#: src/pyams_content/component/paragraph/interfaces/html.py:63
+msgid "Body"
+msgstr "Contenu HTML"
+
 #: src/pyams_content/component/paragraph/interfaces/audio.py:56
 msgid "Audio template"
 msgstr "Mode de rendu"
@@ -1072,31 +1126,11 @@
 msgid "Presentation template used for this audio file"
 msgstr "Mode de rendu utilisé pour cette bande son"
 
-#: src/pyams_content/component/paragraph/interfaces/keynumber.py:41
-msgid "Is this key number visible in front-office?"
-msgstr "Si 'non', ce chiffre-clé ne sera pas présenté aux internautes"
-
-#: src/pyams_content/component/paragraph/interfaces/keynumber.py:46
-msgid "Key number value"
-msgstr "Chiffre"
-
-#: src/pyams_content/component/paragraph/interfaces/keynumber.py:50
-msgid ""
-"Small text to be displayed above number (according to selected renderer)"
-msgstr ""
-"Texte court affiché au-dessus du chiffre (selon le mode de rendu sélectionné)"
-
-#: src/pyams_content/component/paragraph/interfaces/keynumber.py:55
-msgid "The way this text will be rendered depends on presentation template"
-msgstr ""
-"La présentation de cette information peut varier en fonction du mode de "
-"rendu choisi"
-
-#: src/pyams_content/component/paragraph/interfaces/keynumber.py:83
+#: src/pyams_content/component/paragraph/interfaces/keynumber.py:36
 msgid "Key numbers template"
 msgstr "Mode de rendu"
 
-#: src/pyams_content/component/paragraph/interfaces/keynumber.py:84
+#: src/pyams_content/component/paragraph/interfaces/keynumber.py:37
 msgid "Presentation template used for key numbers"
 msgstr "Modèle de présentation utilisé par ce bloc de contenu"
 
@@ -1254,8 +1288,8 @@
 msgid "no defined theme"
 msgstr "aucun thème défini"
 
-#: src/pyams_content/component/theme/zmi/__init__.py:52
-#: src/pyams_content/shared/view/zmi/theme.py:49
+#: src/pyams_content/component/theme/zmi/__init__.py:51
+#: src/pyams_content/shared/view/zmi/theme.py:48
 msgid "Themes..."
 msgstr "Thèmes"
 
@@ -1263,7 +1297,7 @@
 msgid "Content themes"
 msgstr "Thèmes du contenu"
 
-#: src/pyams_content/component/theme/zmi/manager.py:46
+#: src/pyams_content/component/theme/zmi/manager.py:45
 msgid "Themes settings..."
 msgstr "Paramétrage des thèmes"
 
@@ -1273,20 +1307,20 @@
 
 #: src/pyams_content/component/association/container.py:88
 #: src/pyams_content/component/association/zmi/__init__.py:296
-#: src/pyams_content/component/association/interfaces/__init__.py:86
+#: src/pyams_content/component/association/interfaces/__init__.py:90
 msgid "Associations"
 msgstr "Liens et pièces jointes"
 
 #: src/pyams_content/component/association/zmi/paragraph.py:54
-#: src/pyams_content/component/association/zmi/__init__.py:95
+#: src/pyams_content/component/association/zmi/__init__.py:96
 msgid "Associations..."
 msgstr "Liens et pièces jointes"
 
-#: src/pyams_content/component/association/zmi/paragraph.py:65
+#: src/pyams_content/component/association/zmi/paragraph.py:67
 msgid "Add new association paragraph"
 msgstr "Ajout d'un bloc  « liens et pièces jointes »"
 
-#: src/pyams_content/component/association/zmi/paragraph.py:97
+#: src/pyams_content/component/association/zmi/paragraph.py:93
 msgid "Edit association paragraph properties"
 msgstr "Propriétés du bloc « liens et pièces jointes »"
 
@@ -1307,135 +1341,140 @@
 msgid "Associations list"
 msgstr "Liste des liens et pièces jointes"
 
-#: src/pyams_content/component/association/zmi/__init__.py:65
+#: src/pyams_content/component/association/zmi/__init__.py:66
 msgid "Association was correctly added."
 msgstr "L'association a été ajoutée."
 
 #: src/pyams_content/component/association/interfaces/__init__.py:43
+#: src/pyams_content/features/menu/interfaces/__init__.py:59
 msgid "Is this item visible in front-office?"
 msgstr "Si 'non', ce lien ne sera pas présenté aux internautes"
 
-#: src/pyams_content/component/association/interfaces/__init__.py:93
+#: src/pyams_content/component/association/interfaces/__init__.py:97
 msgid "Associations template"
 msgstr "Mode de rendu"
 
-#: src/pyams_content/component/association/interfaces/__init__.py:94
+#: src/pyams_content/component/association/interfaces/__init__.py:98
 msgid "Presentation template used for associations"
 msgstr "Modèle de présentation utilisé par ce bloc de contenu"
 
-#: src/pyams_content/component/links/__init__.py:104
+#: src/pyams_content/component/links/__init__.py:125
 msgid "Internal link"
 msgstr "Lien interne"
 
-#: src/pyams_content/component/links/__init__.py:186
+#: src/pyams_content/component/links/__init__.py:207
 msgid "External link"
 msgstr "Lien externe"
 
-#: src/pyams_content/component/links/__init__.py:239
+#: src/pyams_content/component/links/__init__.py:260
 msgid "Mailto link"
 msgstr "Lien mailto"
 
-#: src/pyams_content/component/links/__init__.py:173
+#: src/pyams_content/component/links/__init__.py:194
 msgid "target is not published"
 msgstr "le contenu ciblé n'est pas publié"
 
-#: src/pyams_content/component/links/zmi/__init__.py:57
+#: src/pyams_content/component/links/zmi/__init__.py:60
 msgid "Internal links"
 msgstr "Liens internes"
 
-#: src/pyams_content/component/links/zmi/__init__.py:72
+#: src/pyams_content/component/links/zmi/__init__.py:75
 msgid "Add internal link"
 msgstr "Lien interne"
 
-#: src/pyams_content/component/links/zmi/__init__.py:84
+#: src/pyams_content/component/links/zmi/__init__.py:89
 msgid "Add new internal link"
 msgstr "Ajout d'un lien interne"
 
-#: src/pyams_content/component/links/zmi/__init__.py:122
+#: src/pyams_content/component/links/zmi/__init__.py:125
 msgid "Edit internal link properties"
 msgstr "Propriétés du lien interne"
 
-#: src/pyams_content/component/links/zmi/__init__.py:160
+#: src/pyams_content/component/links/zmi/__init__.py:159
 msgid "External links"
 msgstr "Liens externes"
 
-#: src/pyams_content/component/links/zmi/__init__.py:175
+#: src/pyams_content/component/links/zmi/__init__.py:174
 msgid "Add external link"
 msgstr "Lien externe"
 
-#: src/pyams_content/component/links/zmi/__init__.py:187
+#: src/pyams_content/component/links/zmi/__init__.py:188
 msgid "Add new external link"
 msgstr "Ajout d'un lien externe"
 
-#: src/pyams_content/component/links/zmi/__init__.py:225
+#: src/pyams_content/component/links/zmi/__init__.py:224
 msgid "Edit external link properties"
 msgstr "Propriétés du lien externe"
 
-#: src/pyams_content/component/links/zmi/__init__.py:263
+#: src/pyams_content/component/links/zmi/__init__.py:258
 msgid "Mailto links"
 msgstr "Liens mailto"
 
-#: src/pyams_content/component/links/zmi/__init__.py:278
+#: src/pyams_content/component/links/zmi/__init__.py:273
 msgid "Add mailto link"
 msgstr "Lien mailto"
 
-#: src/pyams_content/component/links/zmi/__init__.py:290
+#: src/pyams_content/component/links/zmi/__init__.py:287
 msgid "Add new mailto link"
 msgstr "Ajout d'un lien « mailto »"
 
-#: src/pyams_content/component/links/zmi/__init__.py:328
+#: src/pyams_content/component/links/zmi/__init__.py:323
 msgid "Edit mailto link properties"
 msgstr "Propriétés du lien « mailto »"
 
-#: src/pyams_content/component/links/zmi/reverse.py:55
+#: src/pyams_content/component/links/zmi/reverse.py:57
 msgid "Reverse links"
 msgstr "Liens amont"
 
-#: src/pyams_content/component/links/zmi/reverse.py:64
+#: src/pyams_content/component/links/zmi/reverse.py:66
 msgid "Content's internal links"
 msgstr "Autres contenus qui pointent vers ce contenu"
 
-#: src/pyams_content/component/links/interfaces/__init__.py:34
+#: src/pyams_content/component/links/interfaces/__init__.py:36
 msgid "Link title, as shown in front-office"
 msgstr ""
 "Le contexte d'utilisation de ce lien peut nécessiter de modifier son titre "
 "d'origine. Ce titre de substitution sera alors présenté aux internautes."
 
-#: src/pyams_content/component/links/interfaces/__init__.py:38
+#: src/pyams_content/component/links/interfaces/__init__.py:40
 msgid "Link description displayed by front-office template"
 msgstr "Description du lien, présentée aux internautes"
 
-#: src/pyams_content/component/links/interfaces/__init__.py:55
+#: src/pyams_content/component/links/interfaces/__init__.py:44
+msgid "Name of the pictogram associated with this link"
+msgstr "Pictogramme à associer à ce lien"
+
+#: src/pyams_content/component/links/interfaces/__init__.py:64
 #: src/pyams_content/shared/logo/interfaces/__init__.py:50
 msgid "Target URL"
 msgstr "URL cible"
 
-#: src/pyams_content/component/links/interfaces/__init__.py:56
+#: src/pyams_content/component/links/interfaces/__init__.py:65
 #: src/pyams_content/shared/logo/interfaces/__init__.py:51
 msgid "URL used to access external resource"
 msgstr ""
 "URL utilisée pour accéder à cette ressource externe. Doit comprendre le "
 "protocole d'accès au site, comme « http:// » ou « https:// »."
 
-#: src/pyams_content/component/links/interfaces/__init__.py:60
+#: src/pyams_content/component/links/interfaces/__init__.py:69
 msgid "Language used in this remote resource"
 msgstr ""
 "Langue utilisée par cette ressource extene ; à préciser lorsqu'il ne s'agit "
 "pas de la langue par défaut du site."
 
-#: src/pyams_content/component/links/interfaces/__init__.py:68
+#: src/pyams_content/component/links/interfaces/__init__.py:77
 msgid "Target address"
 msgstr "Adresse mail"
 
-#: src/pyams_content/component/links/interfaces/__init__.py:69
+#: src/pyams_content/component/links/interfaces/__init__.py:78
 msgid "Target email address"
 msgstr "Adresse de messagerie \"stricte\", soit uniquement \"xxx@yyy.com\""
 
-#: src/pyams_content/component/links/interfaces/__init__.py:72
+#: src/pyams_content/component/links/interfaces/__init__.py:81
 msgid "Address name"
 msgstr "Nom de messagerie"
 
-#: src/pyams_content/component/links/interfaces/__init__.py:73
+#: src/pyams_content/component/links/interfaces/__init__.py:82
 msgid "Address as displayed in address book"
 msgstr ""
 "Nom de la boîte aux lettres, tel qu'il sera affiché dans l'application de "
@@ -1704,24 +1743,24 @@
 msgid "Youtube settings"
 msgstr "Paramétres Youtube"
 
-#: src/pyams_content/component/video/zmi/paragraph.py:61
+#: src/pyams_content/component/video/zmi/paragraph.py:62
 msgid "External video..."
 msgstr "Vidéo externe"
 
-#: src/pyams_content/component/video/zmi/paragraph.py:72
+#: src/pyams_content/component/video/zmi/paragraph.py:75
 msgid "Add new external video..."
 msgstr "Ajout d'une vidéo externe"
 
-#: src/pyams_content/component/video/zmi/paragraph.py:145
+#: src/pyams_content/component/video/zmi/paragraph.py:126
 msgid "Video provider is required"
 msgstr "Le nom du fournisseur est obligatoire"
 
-#: src/pyams_content/component/video/zmi/paragraph.py:194
-#: src/pyams_content/component/video/zmi/paragraph.py:259
+#: src/pyams_content/component/video/zmi/paragraph.py:175
+#: src/pyams_content/component/video/zmi/paragraph.py:229
 msgid "Video provider settings"
 msgstr "Paramètres liés au fournisseur"
 
-#: src/pyams_content/component/video/zmi/paragraph.py:174
+#: src/pyams_content/component/video/zmi/paragraph.py:155
 msgid "Other settings"
 msgstr "Autres paramètres"
 
@@ -1733,19 +1772,19 @@
 msgid "Name of external platform providing selected video"
 msgstr "Nom de la plate-forme externe fournissant la vidéo à afficher"
 
-#: src/pyams_content/shared/common/__init__.py:237
-#: src/pyams_content/shared/common/zmi/properties.py:72
-#: src/pyams_content/shared/common/zmi/manager.py:97
+#: src/pyams_content/shared/common/__init__.py:240
+#: src/pyams_content/shared/common/zmi/properties.py:70
+#: src/pyams_content/shared/common/zmi/manager.py:96
 msgid "Properties"
 msgstr "Propriétés"
 
-#: src/pyams_content/shared/common/__init__.py:147
-#: src/pyams_content/shared/common/__init__.py:155
+#: src/pyams_content/shared/common/__init__.py:150
+#: src/pyams_content/shared/common/__init__.py:158
 #, python-format
 msgid "{date} by {principal}"
 msgstr "{date} par {principal}"
 
-#: src/pyams_content/shared/common/__init__.py:260
+#: src/pyams_content/shared/common/__init__.py:263
 #, python-format
 msgid "title length should be between 40 and 66 characters ({length} actually)"
 msgstr ""
@@ -1799,11 +1838,11 @@
 msgid "Modified before..."
 msgstr "et le"
 
-#: src/pyams_content/shared/common/zmi/properties.py:62
+#: src/pyams_content/shared/common/zmi/properties.py:60
 msgid "Composition"
 msgstr "Composition"
 
-#: src/pyams_content/shared/common/zmi/properties.py:83
+#: src/pyams_content/shared/common/zmi/properties.py:82
 msgid "Content properties"
 msgstr "Propriétés élémentaires"
 
@@ -1815,40 +1854,40 @@
 msgid "Data type label"
 msgstr "Libellé du type"
 
-#: src/pyams_content/shared/common/zmi/types.py:183
-#: src/pyams_content/shared/common/zmi/types.py:411
+#: src/pyams_content/shared/common/zmi/types.py:185
+#: src/pyams_content/shared/common/zmi/types.py:396
 msgid "Default associations"
 msgstr "Liens et pièces jointes par défaut"
 
-#: src/pyams_content/shared/common/zmi/types.py:211
+#: src/pyams_content/shared/common/zmi/types.py:213
 msgid "Content data types"
 msgstr "Types de contenus"
 
-#: src/pyams_content/shared/common/zmi/types.py:234
+#: src/pyams_content/shared/common/zmi/types.py:236
 msgid "Add data type"
 msgstr "Ajouter un type"
 
-#: src/pyams_content/shared/common/zmi/types.py:245
+#: src/pyams_content/shared/common/zmi/types.py:248
 msgid "Add new data type"
 msgstr "Ajout d'un type de contenu"
 
-#: src/pyams_content/shared/common/zmi/types.py:291
+#: src/pyams_content/shared/common/zmi/types.py:285
 msgid "Data type properties"
 msgstr "Propriétés du type de contenu"
 
-#: src/pyams_content/shared/common/zmi/types.py:374
+#: src/pyams_content/shared/common/zmi/types.py:359
 msgid "Subtype label"
 msgstr "Libellé du sous-type"
 
-#: src/pyams_content/shared/common/zmi/types.py:455
+#: src/pyams_content/shared/common/zmi/types.py:440
 msgid "Add subtype"
 msgstr "Ajouter un sous-type"
 
-#: src/pyams_content/shared/common/zmi/types.py:466
+#: src/pyams_content/shared/common/zmi/types.py:452
 msgid "Add new subtype"
 msgstr "Ajout d'un sous-type de contenu"
 
-#: src/pyams_content/shared/common/zmi/types.py:518
+#: src/pyams_content/shared/common/zmi/types.py:498
 msgid "Data subtype properties"
 msgstr "Propriétés du fichier standard"
 
@@ -1856,70 +1895,70 @@
 msgid "No currently defined data type."
 msgstr "Aucun type de contenu n'est actuellement défini."
 
-#: src/pyams_content/shared/common/zmi/types.py:273
+#: src/pyams_content/shared/common/zmi/types.py:275
 msgid "Specified type name is already used!"
 msgstr "Le nom indiqué pour ce type de contenu est déjà utilisé !"
 
-#: src/pyams_content/shared/common/zmi/types.py:494
-msgid "Specified subtype name is already used!"
-msgstr "Le nom indiqué pour ce sous-type de contenu est déjà utilisé !"
-
-#: src/pyams_content/shared/common/zmi/types.py:505
+#: src/pyams_content/shared/common/zmi/types.py:474
 msgid "Subtype was correctly added."
 msgstr "Le sous-type a été ajouté."
 
-#: src/pyams_content/shared/common/zmi/types.py:155
+#: src/pyams_content/shared/common/zmi/types.py:488
+msgid "Specified subtype name is already used!"
+msgstr "Le nom indiqué pour ce sous-type de contenu est déjà utilisé !"
+
+#: src/pyams_content/shared/common/zmi/types.py:157
 msgid "Click to see subtypes"
 msgstr "Montrer ou caher les sous-types"
 
-#: src/pyams_content/shared/common/zmi/workflow.py:907
+#: src/pyams_content/shared/common/zmi/workflow.py:844
 msgid "Prior checks"
 msgstr "Contrôles préalables : avez-vous ?"
 
-#: src/pyams_content/shared/common/zmi/workflow.py:126
+#: src/pyams_content/shared/common/zmi/workflow.py:123
 msgid "Request publication"
 msgstr "Demander la publication"
 
-#: src/pyams_content/shared/common/zmi/workflow.py:218
+#: src/pyams_content/shared/common/zmi/workflow.py:210
 #: src/pyams_content/workflow/__init__.py:315
 msgid "Cancel publication request"
 msgstr "Annuler la demande de publication"
 
-#: src/pyams_content/shared/common/zmi/workflow.py:263
+#: src/pyams_content/shared/common/zmi/workflow.py:250
 msgid "Refuse publication request"
 msgstr "Refuser la demande de publication"
 
-#: src/pyams_content/shared/common/zmi/workflow.py:322
+#: src/pyams_content/shared/common/zmi/workflow.py:304
 #: src/pyams_content/workflow/basic.py:196
 msgid "Publish"
 msgstr "Publier"
 
-#: src/pyams_content/shared/common/zmi/workflow.py:416
+#: src/pyams_content/shared/common/zmi/workflow.py:393
 msgid "Request retire"
 msgstr "Demander le retrait"
 
-#: src/pyams_content/shared/common/zmi/workflow.py:477
+#: src/pyams_content/shared/common/zmi/workflow.py:449
 msgid "Cancel retire request"
 msgstr "Annuler la demande de retrait"
 
-#: src/pyams_content/shared/common/zmi/workflow.py:522
+#: src/pyams_content/shared/common/zmi/workflow.py:489
 msgid "Retire"
 msgstr "Retirer"
 
-#: src/pyams_content/shared/common/zmi/workflow.py:568
+#: src/pyams_content/shared/common/zmi/workflow.py:530
 #: src/pyams_content/workflow/__init__.py:436
 msgid "Request archive"
 msgstr "Demander l'archivage"
 
-#: src/pyams_content/shared/common/zmi/workflow.py:616
+#: src/pyams_content/shared/common/zmi/workflow.py:573
 msgid "Cancel archive request"
 msgstr "Annuler la demande d'archivage"
 
-#: src/pyams_content/shared/common/zmi/workflow.py:661
+#: src/pyams_content/shared/common/zmi/workflow.py:613
 msgid "Archive"
 msgstr "Archiver"
 
-#: src/pyams_content/shared/common/zmi/workflow.py:707
+#: src/pyams_content/shared/common/zmi/workflow.py:654
 #: src/pyams_content/workflow/__init__.py:501
 #: src/pyams_content/workflow/__init__.py:513
 #: src/pyams_content/workflow/__init__.py:525
@@ -1930,39 +1969,39 @@
 msgid "Create new version"
 msgstr "Créer une nouvelle version"
 
-#: src/pyams_content/shared/common/zmi/workflow.py:763
+#: src/pyams_content/shared/common/zmi/workflow.py:705
 #: src/pyams_content/workflow/__init__.py:561
 #: src/pyams_content/workflow/basic.py:248
 msgid "Delete version"
 msgstr "Supprimer cette version"
 
-#: src/pyams_content/shared/common/zmi/workflow.py:853
+#: src/pyams_content/shared/common/zmi/workflow.py:790
 msgid "Previewed content?"
 msgstr "Prévisualisé ce contenu ?"
 
-#: src/pyams_content/shared/common/zmi/workflow.py:857
+#: src/pyams_content/shared/common/zmi/workflow.py:794
 msgid "Verified content?"
 msgstr "Audité ce contenu ?"
 
-#: src/pyams_content/shared/common/zmi/workflow.py:188
-#: src/pyams_content/shared/common/zmi/workflow.py:385
+#: src/pyams_content/shared/common/zmi/workflow.py:180
+#: src/pyams_content/shared/common/zmi/workflow.py:362
 msgid "Publication start date is required"
 msgstr "La date de début de publication est obligatoire"
 
-#: src/pyams_content/shared/common/zmi/workflow.py:291
-#: src/pyams_content/shared/common/zmi/workflow.py:447
+#: src/pyams_content/shared/common/zmi/workflow.py:273
+#: src/pyams_content/shared/common/zmi/workflow.py:419
 msgid "A comment is required"
 msgstr "Le commentaire est obligatoire"
 
-#: src/pyams_content/shared/common/zmi/workflow.py:786
+#: src/pyams_content/shared/common/zmi/workflow.py:729
 msgid "Delete content"
 msgstr "Supprimer définitivement ce contenu"
 
-#: src/pyams_content/shared/common/zmi/workflow.py:795
+#: src/pyams_content/shared/common/zmi/workflow.py:738
 msgid "Delete definitively"
 msgstr "Supprimer définitivement"
 
-#: src/pyams_content/shared/common/zmi/workflow.py:923
+#: src/pyams_content/shared/common/zmi/workflow.py:860
 msgid ""
 "You must confirm that you previewed and checked this content before "
 "requesting publication!!"
@@ -1970,12 +2009,12 @@
 "Vous devez avoir prévisualisé et audité ce contenu avant de pouvoir le "
 "publier !!"
 
-#: src/pyams_content/shared/common/zmi/workflow.py:90
+#: src/pyams_content/shared/common/zmi/workflow.py:87
 #, python-format
 msgid "{state} | by {principal}"
 msgstr "{state} | par {principal}"
 
-#: src/pyams_content/shared/common/zmi/workflow.py:86
+#: src/pyams_content/shared/common/zmi/workflow.py:83
 #: src/pyams_content/workflow/__init__.py:648
 #: src/pyams_content/workflow/__init__.py:619
 #: src/pyams_content/workflow/basic.py:315
@@ -1984,7 +2023,7 @@
 msgid "{state} {date}"
 msgstr "{state} {date}"
 
-#: src/pyams_content/shared/common/zmi/__init__.py:266
+#: src/pyams_content/shared/common/zmi/__init__.py:265
 msgid "Duplicate content..."
 msgstr "Dupliquer le contenu"
 
@@ -1992,22 +2031,22 @@
 msgid "Duplicate content"
 msgstr "Dupliquer ce contenu"
 
-#: src/pyams_content/shared/common/zmi/__init__.py:85
+#: src/pyams_content/shared/common/zmi/__init__.py:84
 msgid "This title can be modified afterwards"
 msgstr "Pourra être modifié ultérieurement"
 
-#: src/pyams_content/shared/common/zmi/__init__.py:277
+#: src/pyams_content/shared/common/zmi/__init__.py:276
 msgid "Duplicate this content"
 msgstr "Dupliquer ce contenu"
 
-#: src/pyams_content/shared/common/zmi/__init__.py:338
+#: src/pyams_content/shared/common/zmi/__init__.py:337
 #, python-format
 msgid "Clone created from version {source} of {oid} (in « {state} » state)"
 msgstr ""
 "Duplication de la version {source} du contenu {oid} (alors en statut "
 "« {state} »)"
 
-#: src/pyams_content/shared/common/zmi/__init__.py:389
+#: src/pyams_content/shared/common/zmi/__init__.py:382
 msgid "Created or modified in this version"
 msgstr "Créé ou modifié dans cette version"
 
@@ -2058,11 +2097,11 @@
 msgid "Shared tool properties"
 msgstr "Propriétés de l'outil"
 
-#: src/pyams_content/shared/common/zmi/manager.py:132
+#: src/pyams_content/shared/common/zmi/manager.py:125
 msgid "WARNING"
 msgstr "ATTENTION"
 
-#: src/pyams_content/shared/common/zmi/manager.py:134
+#: src/pyams_content/shared/common/zmi/manager.py:127
 msgid ""
 "Workflow shouldn't be modified if this tool already contains any shared "
 "content!"
@@ -2070,15 +2109,15 @@
 "Le workflow ne doit pas être modifié si cet outil renferme déjà des contenus "
 "partagés !"
 
-#: src/pyams_content/shared/common/zmi/manager.py:157
+#: src/pyams_content/shared/common/zmi/manager.py:150
 msgid "Languages"
 msgstr "Langues"
 
-#: src/pyams_content/shared/common/zmi/manager.py:168
+#: src/pyams_content/shared/common/zmi/manager.py:162
 msgid "Content languages"
 msgstr "Langues pour la traduction de cet outil"
 
-#: src/pyams_content/shared/common/zmi/manager.py:185
+#: src/pyams_content/shared/common/zmi/manager.py:172
 msgid ""
 "Tool languages are used to translate own tool properties, and newly created "
 "contents will propose these languages by default"
@@ -2087,15 +2126,15 @@
 "\n"
 "Les nouveaux contenus proposeront également ces langues par défaut."
 
-#: src/pyams_content/shared/common/zmi/manager.py:81
+#: src/pyams_content/shared/common/zmi/manager.py:80
 msgid "Content management"
 msgstr "Gérer ce gabarit"
 
-#: src/pyams_content/shared/common/zmi/manager.py:83
+#: src/pyams_content/shared/common/zmi/manager.py:82
 msgid "Tool management"
-msgstr "Gérer l'outil partagé"
-
-#: src/pyams_content/shared/common/zmi/owner.py:51
+msgstr "Gérer cet outil"
+
+#: src/pyams_content/shared/common/zmi/owner.py:50
 msgid "Change owner..."
 msgstr "Changer de propriétaire"
 
@@ -2103,7 +2142,7 @@
 msgid "Change content's owner"
 msgstr "Changement de propriétaire"
 
-#: src/pyams_content/shared/common/zmi/owner.py:132
+#: src/pyams_content/shared/common/zmi/owner.py:125
 msgid ""
 "All versions of this content which are not archived will be transferred to "
 "newly selected owner"
@@ -2111,28 +2150,48 @@
 "Toutes les versions non archivées de ce contenu seront transférées au "
 "nouveau propriétaire sélectionné"
 
-#: src/pyams_content/shared/common/zmi/owner.py:61
+#: src/pyams_content/shared/common/zmi/owner.py:60
 msgid "New owner"
 msgstr "Nouveau propriétaire"
 
-#: src/pyams_content/shared/common/zmi/owner.py:62
+#: src/pyams_content/shared/common/zmi/owner.py:61
 msgid "The selected user will become the new content's owner"
 msgstr "L'utilisateur sélectionné deviendra le nouveau propriétaire du contenu"
 
-#: src/pyams_content/shared/common/zmi/owner.py:64
+#: src/pyams_content/shared/common/zmi/owner.py:63
 msgid "Keep previous owner as contributor"
 msgstr "L'ancien propriétaire reste contributeur"
 
-#: src/pyams_content/shared/common/zmi/owner.py:65
+#: src/pyams_content/shared/common/zmi/owner.py:64
 msgid "If 'yes', the previous owner will still be able to modify this content"
 msgstr ""
 "Si 'oui', l'actuel propriétaire du contenu en restera contributeur et pourra "
 "donc continuer à le mettre à jour"
 
-#: src/pyams_content/shared/common/zmi/owner.py:75
+#: src/pyams_content/shared/common/zmi/owner.py:74
 msgid "Change owner"
 msgstr "Changer le propriétaire"
 
+#: src/pyams_content/shared/common/zmi/rename.py:62
+msgid "Change URL..."
+msgstr "Changer d'URL"
+
+#: src/pyams_content/shared/common/zmi/rename.py:79
+msgid "Change item URL"
+msgstr "Modification de l'URL"
+
+#: src/pyams_content/shared/common/zmi/rename.py:86
+msgid "Item URL part"
+msgstr "URL du contenu"
+
+#: src/pyams_content/shared/common/zmi/rename.py:87
+msgid "URL part used to access this content"
+msgstr "Portion de l'URL utilisée pour accéder à ce contenu"
+
+#: src/pyams_content/shared/common/zmi/rename.py:121
+msgid "You must provide an URL for this item!"
+msgstr "Vous devez fournir une URL pour ce contenu !"
+
 #: src/pyams_content/shared/common/zmi/site.py:38
 #, python-format
 msgid ""
@@ -2141,6 +2200,37 @@
 "RECHERCHE - Tous contenus présents dans &laquo;&nbsp;{site}&nbsp;&raquo; "
 "confondus"
 
+#: src/pyams_content/shared/common/zmi/portal.py:46
+msgid "Edit default template properties"
+msgstr "Modèle de présentation par défaut"
+
+#: src/pyams_content/shared/common/zmi/portal.py:56
+msgid ""
+"**This form allows you to select shared content default template.**\n"
+"\n"
+"If you choose to use a shared template, you can only adjust settings of each "
+"portlet individually but can't change portlets list or page configuration.\n"
+"\n"
+"If you use a local template, you can define a whole custom configuration but "
+"the template definition can't be reused anywhere..."
+msgstr ""
+"Vous pouvez modifier le modèle de présentation qui sera appliqué **par "
+"défaut** à tous les contenus de ce gabarit.\n"
+"\n"
+"Si vous choisissez d'utiliser un modèle de présentation partagé, vous "
+"pourrez ajuster les paramètres de chaque composant mais ne pourrez pas "
+"modifier la liste des composants ou leur position et la configuration de la "
+"page.\n"
+"\n"
+"Si vous choisissez d'utiliser un modèle de présentation \"local\", vous "
+"pourrez définir l'ensemble de la configuration mais le modèle de "
+"présentation ne pourra pas être réutilisé ailleurs que dans les contenus de "
+"ce gabarit."
+
+#: src/pyams_content/shared/common/zmi/portal.py:72
+msgid "Override tool default template"
+msgstr "Ne pas utiliser le modèle par défaut de ce gabarit"
+
 #: src/pyams_content/shared/common/zmi/dashboard.py:134
 msgid "Unique ID"
 msgstr "N° IN"
@@ -2158,7 +2248,7 @@
 msgstr "Dernière modification"
 
 #: src/pyams_content/shared/common/zmi/dashboard.py:273
-#: src/pyams_content/root/zmi/__init__.py:91
+#: src/pyams_content/root/zmi/__init__.py:110
 msgid "Dashboard"
 msgstr "Tableau de bord"
 
@@ -2167,55 +2257,55 @@
 msgstr "Mon tableau de bord"
 
 #: src/pyams_content/shared/common/zmi/dashboard.py:329
-#: src/pyams_content/root/zmi/__init__.py:140
+#: src/pyams_content/root/zmi/__init__.py:159
 #, python-format
 msgid "MANAGER - {0} content waiting for your action"
 msgstr "RESPONSABLE - {0} contenu en attente de votre intervention"
 
 #: src/pyams_content/shared/common/zmi/dashboard.py:330
-#: src/pyams_content/root/zmi/__init__.py:141
+#: src/pyams_content/root/zmi/__init__.py:160
 #, python-format
 msgid "MANAGER - {0} contents waiting for your action"
 msgstr "RESPONSABLE - {0} contenus en attente de votre intervention"
 
 #: src/pyams_content/shared/common/zmi/dashboard.py:370
-#: src/pyams_content/root/zmi/__init__.py:184
+#: src/pyams_content/root/zmi/__init__.py:203
 #, python-format
 msgid "CONTRIBUTOR - {0} content waiting for action"
 msgstr "CONTRIBUTEUR - {0} contenu soumis à un responsable"
 
 #: src/pyams_content/shared/common/zmi/dashboard.py:371
-#: src/pyams_content/root/zmi/__init__.py:185
+#: src/pyams_content/root/zmi/__init__.py:204
 #, python-format
 msgid "CONTRIBUTOR - {0} contents waiting for action"
 msgstr "CONTRIBUTEUR - {0} contenus soumis à un responsable"
 
 #: src/pyams_content/shared/common/zmi/dashboard.py:402
-#: src/pyams_content/root/zmi/__init__.py:219
+#: src/pyams_content/root/zmi/__init__.py:238
 #, python-format
 msgid "CONTRIBUTOR - {0} modified content"
 msgstr "CONTRIBUTEUR - {0} contenu modifié"
 
 #: src/pyams_content/shared/common/zmi/dashboard.py:443
-#: src/pyams_content/root/zmi/__init__.py:262
+#: src/pyams_content/root/zmi/__init__.py:281
 msgid "My contents"
 msgstr "Mes contenus"
 
 #: src/pyams_content/shared/common/zmi/dashboard.py:458
 #: src/pyams_content/shared/common/zmi/templates/dashboard.pt:8
-#: src/pyams_content/root/zmi/__init__.py:277
+#: src/pyams_content/root/zmi/__init__.py:296
 #: src/pyams_content/root/zmi/templates/dashboard.pt:8
 msgid "My favorites"
 msgstr "Mes favoris"
 
 #: src/pyams_content/shared/common/zmi/dashboard.py:467
-#: src/pyams_content/root/zmi/__init__.py:286
+#: src/pyams_content/root/zmi/__init__.py:305
 #, python-format
 msgid "CONTRIBUTOR - {0} favorite"
 msgstr "CONTRIBUTEUR - {0} contenu favori"
 
 #: src/pyams_content/shared/common/zmi/dashboard.py:468
-#: src/pyams_content/root/zmi/__init__.py:287
+#: src/pyams_content/root/zmi/__init__.py:306
 #, python-format
 msgid "CONTRIBUTOR - {0} favorites"
 msgstr "CONTRIBUTEUR - {0} contenus favoris"
@@ -2226,188 +2316,188 @@
 msgstr "Ajouter/enlever des favoris"
 
 #: src/pyams_content/shared/common/zmi/dashboard.py:543
-#: src/pyams_content/root/zmi/__init__.py:325
+#: src/pyams_content/root/zmi/__init__.py:344
 msgid "Your favorites"
 msgstr "Mes favoris"
 
 #: src/pyams_content/shared/common/zmi/dashboard.py:556
-#: src/pyams_content/root/zmi/__init__.py:338
+#: src/pyams_content/root/zmi/__init__.py:357
 msgid "My preparations"
 msgstr "Mes préparations"
 
 #: src/pyams_content/shared/common/zmi/dashboard.py:565
-#: src/pyams_content/root/zmi/__init__.py:347
+#: src/pyams_content/root/zmi/__init__.py:366
 #, python-format
 msgid "CONTRIBUTOR - {0} prepared content"
 msgstr "CONTRIBUTEUR - {0} contenu en préparation"
 
 #: src/pyams_content/shared/common/zmi/dashboard.py:566
-#: src/pyams_content/root/zmi/__init__.py:348
+#: src/pyams_content/root/zmi/__init__.py:367
 #, python-format
 msgid "CONTRIBUTOR - {0} prepared contents"
 msgstr "CONTRIBUTEUR - {0} contenus en préparation"
 
 #: src/pyams_content/shared/common/zmi/dashboard.py:605
-#: src/pyams_content/root/zmi/__init__.py:386
+#: src/pyams_content/root/zmi/__init__.py:405
 msgid "Your prepared contents"
 msgstr "Mes contenus en préparation"
 
 #: src/pyams_content/shared/common/zmi/dashboard.py:618
-#: src/pyams_content/root/zmi/__init__.py:399
+#: src/pyams_content/root/zmi/__init__.py:418
 msgid "My submissions"
 msgstr "Mes demandes"
 
 #: src/pyams_content/shared/common/zmi/dashboard.py:627
-#: src/pyams_content/root/zmi/__init__.py:408
+#: src/pyams_content/root/zmi/__init__.py:427
 #, python-format
 msgid "CONTRIBUTOR - {0} submitted content"
 msgstr "CONTRIBUTEUR - {0} contenu soumis à un responsable"
 
 #: src/pyams_content/shared/common/zmi/dashboard.py:628
-#: src/pyams_content/root/zmi/__init__.py:409
+#: src/pyams_content/root/zmi/__init__.py:428
 #, python-format
 msgid "CONTRIBUTOR - {0} submitted contents"
 msgstr "CONTRIBUTEUR - {0} contenus soumis à un responsable"
 
 #: src/pyams_content/shared/common/zmi/dashboard.py:667
-#: src/pyams_content/root/zmi/__init__.py:447
+#: src/pyams_content/root/zmi/__init__.py:466
 msgid "Your submitted contents"
 msgstr "Mes contenus soumis à un responsable"
 
 #: src/pyams_content/shared/common/zmi/dashboard.py:680
-#: src/pyams_content/root/zmi/__init__.py:460
+#: src/pyams_content/root/zmi/__init__.py:479
 msgid "My publications"
 msgstr "Mes publications"
 
 #: src/pyams_content/shared/common/zmi/dashboard.py:689
-#: src/pyams_content/root/zmi/__init__.py:469
+#: src/pyams_content/root/zmi/__init__.py:488
 #, python-format
 msgid "CONTRIBUTOR - {0} published content"
 msgstr "CONTRIBUTEUR - {0} contenu publié"
 
 #: src/pyams_content/shared/common/zmi/dashboard.py:690
-#: src/pyams_content/root/zmi/__init__.py:470
+#: src/pyams_content/root/zmi/__init__.py:489
 #, python-format
 msgid "CONTRIBUTOR - {0} published contents"
 msgstr "CONTRIBUTEUR - {0} contenus publiés"
 
 #: src/pyams_content/shared/common/zmi/dashboard.py:729
-#: src/pyams_content/root/zmi/__init__.py:508
+#: src/pyams_content/root/zmi/__init__.py:527
 msgid "Your published contents"
 msgstr "Mes contenus publiés"
 
 #: src/pyams_content/shared/common/zmi/dashboard.py:742
-#: src/pyams_content/root/zmi/__init__.py:521
+#: src/pyams_content/root/zmi/__init__.py:540
 msgid "My retired contents"
 msgstr "Mes contenus retirés"
 
 #: src/pyams_content/shared/common/zmi/dashboard.py:751
-#: src/pyams_content/root/zmi/__init__.py:530
+#: src/pyams_content/root/zmi/__init__.py:549
 #, python-format
 msgid "CONTRIBUTOR - {0} retired content"
 msgstr "CONTRIBUTEUR - {0} contenu retiré"
 
 #: src/pyams_content/shared/common/zmi/dashboard.py:752
-#: src/pyams_content/root/zmi/__init__.py:531
+#: src/pyams_content/root/zmi/__init__.py:550
 #, python-format
 msgid "CONTRIBUTOR - {0} retired contents"
 msgstr "CONTRIBUTEUR - {0} contenus retirés"
 
 #: src/pyams_content/shared/common/zmi/dashboard.py:792
-#: src/pyams_content/root/zmi/__init__.py:570
+#: src/pyams_content/root/zmi/__init__.py:589
 msgid "Your retired contents"
 msgstr "Mes contenus retirés"
 
 #: src/pyams_content/shared/common/zmi/dashboard.py:805
-#: src/pyams_content/root/zmi/__init__.py:583
+#: src/pyams_content/root/zmi/__init__.py:602
 msgid "My archived contents"
 msgstr "Mes contenus archivés"
 
 #: src/pyams_content/shared/common/zmi/dashboard.py:814
-#: src/pyams_content/root/zmi/__init__.py:592
+#: src/pyams_content/root/zmi/__init__.py:611
 #, python-format
 msgid "CONTRIBUTOR - {0} archived content"
 msgstr "CONTRIBUTEUR - {0} contenu archivé"
 
 #: src/pyams_content/shared/common/zmi/dashboard.py:815
-#: src/pyams_content/root/zmi/__init__.py:593
+#: src/pyams_content/root/zmi/__init__.py:612
 #, python-format
 msgid "CONTRIBUTOR - {0} archived contents"
 msgstr "CONTRIBUTEUR - {0} contenus archivés"
 
 #: src/pyams_content/shared/common/zmi/dashboard.py:861
-#: src/pyams_content/root/zmi/__init__.py:638
+#: src/pyams_content/root/zmi/__init__.py:657
 msgid "Your archived contents"
 msgstr "Mes contenus archivés"
 
 #: src/pyams_content/shared/common/zmi/dashboard.py:875
-#: src/pyams_content/root/zmi/__init__.py:652
+#: src/pyams_content/root/zmi/__init__.py:671
 msgid "Other interventions"
 msgstr "Toutes les interventions"
 
 #: src/pyams_content/shared/common/zmi/dashboard.py:890
-#: src/pyams_content/root/zmi/__init__.py:667
+#: src/pyams_content/root/zmi/__init__.py:686
 msgid "Last publications"
 msgstr "Dernières publications"
 
 #: src/pyams_content/shared/common/zmi/dashboard.py:899
-#: src/pyams_content/root/zmi/__init__.py:676
+#: src/pyams_content/root/zmi/__init__.py:695
 #, python-format
 msgid "CONTRIBUTORS - {0} published content"
 msgstr "TOUS CONTRIBUTEURS - {0} contenu publié"
 
 #: src/pyams_content/shared/common/zmi/dashboard.py:945
-#: src/pyams_content/root/zmi/__init__.py:721
+#: src/pyams_content/root/zmi/__init__.py:740
 msgid "Last published contents"
 msgstr "Derniers contenus publiés"
 
 #: src/pyams_content/shared/common/zmi/dashboard.py:958
-#: src/pyams_content/root/zmi/__init__.py:734
+#: src/pyams_content/root/zmi/__init__.py:753
 msgid "Last updates"
 msgstr "Dernières modifications"
 
 #: src/pyams_content/shared/common/zmi/dashboard.py:967
-#: src/pyams_content/root/zmi/__init__.py:743
+#: src/pyams_content/root/zmi/__init__.py:762
 #, python-format
 msgid "CONTRIBUTORS - {0} updated content"
 msgstr "TOUS CONTRIBUTEURS - {0} contenu modifié"
 
 #: src/pyams_content/shared/common/zmi/dashboard.py:1012
-#: src/pyams_content/root/zmi/__init__.py:787
+#: src/pyams_content/root/zmi/__init__.py:806
 msgid "Last updated contents"
 msgstr "Derniers contenus modifiés"
 
 #: src/pyams_content/shared/common/zmi/dashboard.py:407
-#: src/pyams_content/root/zmi/__init__.py:224
+#: src/pyams_content/root/zmi/__init__.py:243
 #, python-format
 msgid "CONTRIBUTOR - {0} modified contents"
 msgstr "CONTRIBUTEUR - {0} contenus modifiés"
 
 #: src/pyams_content/shared/common/zmi/dashboard.py:409
-#: src/pyams_content/root/zmi/__init__.py:226
+#: src/pyams_content/root/zmi/__init__.py:245
 #, python-format
 msgid "CONTRIBUTOR - Last {0} modified contents"
 msgstr "CONTRIBUTEUR - Les {0} dernières modifications"
 
 #: src/pyams_content/shared/common/zmi/dashboard.py:904
-#: src/pyams_content/root/zmi/__init__.py:681
+#: src/pyams_content/root/zmi/__init__.py:700
 #, python-format
 msgid "CONTRIBUTORS - Last {0} published contents"
 msgstr "TOUS CONTRIBUTEURS - Les {0} dernières publications"
 
 #: src/pyams_content/shared/common/zmi/dashboard.py:906
-#: src/pyams_content/root/zmi/__init__.py:683
+#: src/pyams_content/root/zmi/__init__.py:702
 msgid "CONTRIBUTORS - Last published contents (in the limit of 50)"
 msgstr "TOUS CONTRIBUTEURS - Les {0} dernières publications"
 
 #: src/pyams_content/shared/common/zmi/dashboard.py:972
-#: src/pyams_content/root/zmi/__init__.py:748
+#: src/pyams_content/root/zmi/__init__.py:767
 #, python-format
 msgid "CONTRIBUTORS - Last {0} updated contents"
 msgstr "TOUS CONTRIBUTEURS - Les {0} dernières modifications"
 
 #: src/pyams_content/shared/common/zmi/dashboard.py:974
-#: src/pyams_content/root/zmi/__init__.py:750
+#: src/pyams_content/root/zmi/__init__.py:769
 msgid "CONTRIBUTORS - Last updated contents (in the limit of 50)"
 msgstr "TOUS CONTRIBUTEURS - Les {0} dernières modifications"
 
@@ -2421,64 +2511,62 @@
 msgid "Content publication start date is not passed yet"
 msgstr "La date de début de publication n'est pas encore atteinte"
 
-#: src/pyams_content/shared/common/zmi/security.py:65
+#: src/pyams_content/shared/common/zmi/security.py:64
 msgid "Contributors restrictions"
 msgstr "Paramètres des contributeurs"
 
-#: src/pyams_content/shared/common/zmi/security.py:74
+#: src/pyams_content/shared/common/zmi/security.py:73
 msgid "Content contributors restrictions"
 msgstr "Liste des contributeurs"
 
-#: src/pyams_content/shared/common/zmi/security.py:108
+#: src/pyams_content/shared/common/zmi/security.py:107
 msgid "Contributor name"
 msgstr "Nom du contributeur"
 
-#: src/pyams_content/shared/common/zmi/security.py:119
-#: src/pyams_content/shared/common/zmi/security.py:277
-#: src/pyams_content/shared/common/interfaces/__init__.py:252
-#: src/pyams_content/shared/common/interfaces/__init__.py:277
-msgid "Publication checks"
-msgstr "Activer le tunnel de publication"
-
-#: src/pyams_content/shared/common/zmi/security.py:225
+#: src/pyams_content/shared/common/zmi/security.py:118
+#: src/pyams_content/shared/common/zmi/security.py:270
+msgid "Activated publication checks?"
+msgstr "Tunnel de publication actif ?"
+
+#: src/pyams_content/shared/common/zmi/security.py:218
 msgid "Managers restrictions"
 msgstr "Paramètres des responsables"
 
-#: src/pyams_content/shared/common/zmi/security.py:234
+#: src/pyams_content/shared/common/zmi/security.py:227
 msgid "Content managers restrictions"
 msgstr "Liste des responsables"
 
-#: src/pyams_content/shared/common/zmi/security.py:266
+#: src/pyams_content/shared/common/zmi/security.py:259
 msgid "Manager name"
 msgstr "Nom du responsable"
 
-#: src/pyams_content/shared/common/zmi/security.py:294
+#: src/pyams_content/shared/common/zmi/security.py:287
 msgid "Restricted"
 msgstr "Restrictions"
 
-#: src/pyams_content/shared/common/zmi/security.py:311
+#: src/pyams_content/shared/common/zmi/security.py:304
 msgid "Owners"
 msgstr "Propriétaires"
 
-#: src/pyams_content/shared/common/zmi/security.py:401
+#: src/pyams_content/shared/common/zmi/security.py:394
 msgid "Publication workflow"
 msgstr "Workflow de publication"
 
-#: src/pyams_content/shared/common/zmi/security.py:168
+#: src/pyams_content/shared/common/zmi/security.py:167
 #, python-format
 msgid "Edit contributor restrictions for « {0} »"
 msgstr "Gérer les paramètres d'intervention de « {0} »"
 
-#: src/pyams_content/shared/common/zmi/security.py:360
+#: src/pyams_content/shared/common/zmi/security.py:353
 #, python-format
 msgid "Edit manager restrictions for « {0} »"
 msgstr "Gérer les paramètres d'intervention de « {0} »"
 
-#: src/pyams_content/shared/common/zmi/security.py:407
+#: src/pyams_content/shared/common/zmi/security.py:400
 msgid "Apply contents restrictions"
 msgstr "Appliquer des restrictions d'accès"
 
-#: src/pyams_content/shared/common/zmi/security.py:409
+#: src/pyams_content/shared/common/zmi/security.py:402
 msgid ""
 "You can specify which contents this manager will be able to manage. If you "
 "specify several criteria, the manager will be able to manage contents for "
@@ -2559,9 +2647,10 @@
 
 #: src/pyams_content/shared/common/zmi/templates/check-input.pt:34
 #: src/pyams_content/shared/common/zmi/templates/preview-input.pt:34
-#: src/pyams_content/shared/common/interfaces/types.py:39
+#: src/pyams_content/shared/common/interfaces/types.py:40
 #: src/pyams_content/shared/form/zmi/field.py:159
-#: src/pyams_content/shared/form/interfaces/__init__.py:61
+#: src/pyams_content/shared/form/interfaces/__init__.py:62
+#: src/pyams_content/features/menu/zmi/__init__.py:208
 msgid "Label"
 msgstr "Libellé"
 
@@ -2570,18 +2659,18 @@
 msgid "Audit"
 msgstr "Audit"
 
-#: src/pyams_content/shared/common/zmi/templates/dashboard.pt:28
-#: src/pyams_content/root/zmi/templates/dashboard.pt:28
+#: src/pyams_content/shared/common/zmi/templates/dashboard.pt:29
+#: src/pyams_content/root/zmi/templates/dashboard.pt:29
 msgid "Quick search..."
 msgstr "Recherche rapide..."
 
-#: src/pyams_content/shared/common/zmi/templates/dashboard.pt:33
-#: src/pyams_content/root/zmi/templates/dashboard.pt:33
+#: src/pyams_content/shared/common/zmi/templates/dashboard.pt:35
+#: src/pyams_content/root/zmi/templates/dashboard.pt:35
 msgid "Advanced search..."
 msgstr "Recherche avancée..."
 
-#: src/pyams_content/shared/common/zmi/templates/dashboard.pt:46
-#: src/pyams_content/root/zmi/templates/dashboard.pt:46
+#: src/pyams_content/shared/common/zmi/templates/dashboard.pt:49
+#: src/pyams_content/root/zmi/templates/dashboard.pt:49
 msgid "You are not actually concerned by any content."
 msgstr "Vous n'êtes actuellement concerné par aucun contenu."
 
@@ -2788,36 +2877,48 @@
 msgid "This content is already retired and not visible."
 msgstr "Ce contenu est déjà retiré et n'est plus visible des internautes."
 
-#: src/pyams_content/shared/common/interfaces/types.py:35
+#: src/pyams_content/shared/common/portlet/content/__init__.py:44
+msgid "Context content"
+msgstr "Contenu partagé"
+
+#: src/pyams_content/shared/common/portlet/content/zmi/preview.pt:2
+msgid "This is where the content will be displayed!!"
+msgstr "C'est ici que seront affichés les éléments du contenu."
+
+#: src/pyams_content/shared/common/portlet/content/skin/__init__.py:36
+msgid "Default content renderer"
+msgstr "Par défaut"
+
+#: src/pyams_content/shared/common/interfaces/types.py:36
 #: src/pyams_content/shared/form/zmi/field.py:148
 msgid "Name"
 msgstr "Nom"
 
-#: src/pyams_content/shared/common/interfaces/types.py:36
+#: src/pyams_content/shared/common/interfaces/types.py:37
 msgid "Name of this data type; must be unique between all data types"
 msgstr "Nom de ce type de donnée ; doit être unique entre tous les types"
 
-#: src/pyams_content/shared/common/interfaces/types.py:42
+#: src/pyams_content/shared/common/interfaces/types.py:43
 msgid "Navigation label"
 msgstr "Libellé de navigation"
 
-#: src/pyams_content/shared/common/interfaces/types.py:43
+#: src/pyams_content/shared/common/interfaces/types.py:44
 msgid "Label used for navigation entries"
 msgstr "Libellé utilisé pour les entrées de navigation"
 
-#: src/pyams_content/shared/common/interfaces/types.py:46
+#: src/pyams_content/shared/common/interfaces/types.py:47
 msgid "Tab-folder label"
 msgstr "Libellé d'un l'onglet"
 
-#: src/pyams_content/shared/common/interfaces/types.py:47
+#: src/pyams_content/shared/common/interfaces/types.py:48
 msgid "Label used to include into tab folder"
 msgstr "Libellé utilisé pour l'affichage du type sous la forme d'un onglet"
 
-#: src/pyams_content/shared/common/interfaces/types.py:50
+#: src/pyams_content/shared/common/interfaces/types.py:51
 msgid "'See also' label"
 msgstr "Libellé 'À voir aussi'"
 
-#: src/pyams_content/shared/common/interfaces/types.py:51
+#: src/pyams_content/shared/common/interfaces/types.py:52
 msgid ""
 "This label can be used when contents of this type will be displayed in a "
 "'See also' entries block"
@@ -2825,47 +2926,47 @@
 "Ce libellé peut être utilisé lorsque des contenus de ce type sont affichés "
 "sous la forme d'un bloc 'À voir aussi'"
 
-#: src/pyams_content/shared/common/interfaces/types.py:55
+#: src/pyams_content/shared/common/interfaces/types.py:56
 msgid "'Single value' label"
 msgstr "Libellé 'Valeur unique'"
 
-#: src/pyams_content/shared/common/interfaces/types.py:56
+#: src/pyams_content/shared/common/interfaces/types.py:57
 msgid "Label given to this type when a single value is displayed"
 msgstr "Libellé utilisé pour ce type lorsqu'une seule valeur est affichée"
 
-#: src/pyams_content/shared/common/interfaces/types.py:59
+#: src/pyams_content/shared/common/interfaces/types.py:60
 msgid "'Link to list' label"
 msgstr "Libellé 'Lien vers une liste'"
 
-#: src/pyams_content/shared/common/interfaces/types.py:60
+#: src/pyams_content/shared/common/interfaces/types.py:61
 msgid "Label used to display a link to a list of items of this type"
 msgstr ""
 "Libellé utilisé lorsque l'on crée un lien vers uns liste de contenus de ce "
 "type"
 
-#: src/pyams_content/shared/common/interfaces/types.py:63
+#: src/pyams_content/shared/common/interfaces/types.py:64
 msgid "Next content label"
 msgstr "Libellé du contenu suivant"
 
-#: src/pyams_content/shared/common/interfaces/types.py:64
+#: src/pyams_content/shared/common/interfaces/types.py:65
 msgid "Label used to announce next date for this type"
 msgstr ""
 "Libellé utilisé pour afficher la prochaine date d'un événement pour ce type"
 
-#: src/pyams_content/shared/common/interfaces/types.py:68
+#: src/pyams_content/shared/common/interfaces/types.py:69
 msgid "Image associated to this data type"
 msgstr "Image associée à ce type"
 
-#: src/pyams_content/shared/common/interfaces/types.py:81
+#: src/pyams_content/shared/common/interfaces/types.py:82
 msgid "Field names"
 msgstr "Champs associés"
 
-#: src/pyams_content/shared/common/interfaces/types.py:82
+#: src/pyams_content/shared/common/interfaces/types.py:83
 msgid "List of fields associated with this data type"
 msgstr "Liste des champs de saisie associés à ce type"
 
 #: src/pyams_content/shared/common/interfaces/__init__.py:46
-#: src/pyams_content/root/interfaces/__init__.py:40
+#: src/pyams_content/root/interfaces/__init__.py:43
 msgid "Webmasters"
 msgstr "Webmestres"
 
@@ -2889,12 +2990,12 @@
 "responsables à certains contenus"
 
 #: src/pyams_content/shared/common/interfaces/__init__.py:57
-#: src/pyams_content/shared/common/interfaces/__init__.py:174
+#: src/pyams_content/shared/common/interfaces/__init__.py:187
 msgid "Managers"
 msgstr "Responsables"
 
 #: src/pyams_content/shared/common/interfaces/__init__.py:58
-#: src/pyams_content/shared/common/interfaces/__init__.py:175
+#: src/pyams_content/shared/common/interfaces/__init__.py:188
 msgid ""
 "Managers can handle main operations in tool's workflow, like publish or "
 "retire contents"
@@ -2904,7 +3005,7 @@
 "restrictions qui leur sont imposées"
 
 #: src/pyams_content/shared/common/interfaces/__init__.py:63
-#: src/pyams_content/shared/common/interfaces/__init__.py:180
+#: src/pyams_content/shared/common/interfaces/__init__.py:193
 msgid "Contributors"
 msgstr "Contributeurs"
 
@@ -2912,19 +3013,29 @@
 msgid "Contributors are users which are allowed to create new contents"
 msgstr "Les contributeurs sont autorisés à créer de nouveaux contenus"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:90
+#: src/pyams_content/shared/common/interfaces/__init__.py:68
+#: src/pyams_content/shared/common/interfaces/__init__.py:199
+msgid "Designers"
+msgstr "Designers"
+
+#: src/pyams_content/shared/common/interfaces/__init__.py:69
+#: src/pyams_content/shared/common/interfaces/__init__.py:200
+msgid "Designers are users which are allowed to manage presentation templates"
+msgstr "Les designers sont autorisés à configurer les modèles de présentation"
+
+#: src/pyams_content/shared/common/interfaces/__init__.py:95
 msgid "Workflow name"
 msgstr "Nom du workflow"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:91
+#: src/pyams_content/shared/common/interfaces/__init__.py:96
 msgid "Name of workflow utility used to manage tool contents"
 msgstr "Nom du workflow qui gère le cycle de vie des contenus de cet outil"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:115
+#: src/pyams_content/shared/common/interfaces/__init__.py:124
 msgid "Content URL"
 msgstr "URL du contenu"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:116
+#: src/pyams_content/shared/common/interfaces/__init__.py:125
 msgid ""
 "URL used to access this content; this is important for SEO and should "
 "include most important words describing content; spaces and underscores will "
@@ -2938,11 +3049,11 @@
 "d'union, les lettres accentuées par leur équivalent sans accent, et les mots "
 "de moins de trois lettres sont supprimés."
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:121
+#: src/pyams_content/shared/common/interfaces/__init__.py:130
 msgid "Version creator"
 msgstr "À l'origine de cette version"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:122
+#: src/pyams_content/shared/common/interfaces/__init__.py:131
 msgid ""
 "Name of content's version creator. The creator of the first version is also "
 "it's owner."
@@ -2950,39 +3061,39 @@
 "Nom du créateur de cette version. Le créateur de la première version d'un "
 "contenu est aussi son propriétaire."
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:126
+#: src/pyams_content/shared/common/interfaces/__init__.py:135
 msgid "First owner"
 msgstr "Premier propriétaire"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:127
+#: src/pyams_content/shared/common/interfaces/__init__.py:136
 msgid "Name of content's first version owner"
 msgstr "Nom de l'utilisateur ayant créé la première version"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:131
+#: src/pyams_content/shared/common/interfaces/__init__.py:140
 msgid "Version creation"
 msgstr "Date de création"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:134
+#: src/pyams_content/shared/common/interfaces/__init__.py:143
 msgid "Version modifiers"
 msgstr "Intervenants"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:135
+#: src/pyams_content/shared/common/interfaces/__init__.py:144
 msgid "List of principals who modified this content"
 msgstr "Liste des utilisateurs qui sont intervenus sur cette version"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:138
+#: src/pyams_content/shared/common/interfaces/__init__.py:147
 msgid "Last modifier"
 msgstr "Dernier intervenant"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:139
+#: src/pyams_content/shared/common/interfaces/__init__.py:148
 msgid "Last principal who modified this content"
 msgstr "Dernier utilisateur étant intervenu sur ce contenu"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:142
+#: src/pyams_content/shared/common/interfaces/__init__.py:151
 msgid "Last update"
 msgstr "Dernière modification"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:146
+#: src/pyams_content/shared/common/interfaces/__init__.py:155
 msgid ""
 "The content's description is 'hidden' into HTML's page headers; but it can "
 "be seen, for example, in some search engines results as content's description"
@@ -2991,33 +3102,33 @@
 "mais on peut la retrouver, par exemple, dans les listes de résultats des "
 "moteurs de recherche"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:151
+#: src/pyams_content/shared/common/interfaces/__init__.py:160
 msgid "Keywords"
 msgstr "Mots-clés"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:152
+#: src/pyams_content/shared/common/interfaces/__init__.py:161
 msgid "They will be included into HTML pages metadata"
 msgstr "Ces mots-clés seront intégrés dans les métadonnées des pages HTML"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:155
-#: src/pyams_content/shared/site/zmi/folder.py:76
+#: src/pyams_content/shared/common/interfaces/__init__.py:164
+#: src/pyams_content/shared/site/zmi/folder.py:78
 #: src/pyams_content/shared/site/interfaces/__init__.py:66
 msgid "Notepad"
 msgstr "Bloc-notes"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:156
-#: src/pyams_content/shared/site/zmi/folder.py:77
+#: src/pyams_content/shared/common/interfaces/__init__.py:165
+#: src/pyams_content/shared/site/zmi/folder.py:79
 #: src/pyams_content/shared/site/interfaces/__init__.py:67
 msgid "Internal information to be known about this content"
 msgstr ""
 "Pour prendre note d'informations internes utiles ou importantes à propos de "
 "ce contenu ; ces notes ne seront pas publiées sur internet."
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:167
+#: src/pyams_content/shared/common/interfaces/__init__.py:180
 msgid "Content owner"
 msgstr "Propriétaire"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:168
+#: src/pyams_content/shared/common/interfaces/__init__.py:181
 msgid ""
 "The owner is the creator of content's first version, except if it was "
 "transferred afterwards to another owner"
@@ -3026,7 +3137,7 @@
 "lorsque cette propriété a été transférée à un autre utilisateur après coup. "
 "Les contenus archivés ne sont plus transférables."
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:181
+#: src/pyams_content/shared/common/interfaces/__init__.py:194
 msgid ""
 "Contributors are users which are allowed to update this content in addition "
 "to it's owner"
@@ -3034,11 +3145,11 @@
 "Les contributeurs sont autorisés, en plus du propriétaire, à modifier ce "
 "contenu"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:186
+#: src/pyams_content/shared/common/interfaces/__init__.py:204
 msgid "Readers"
 msgstr "Relecteurs"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:187
+#: src/pyams_content/shared/common/interfaces/__init__.py:205
 msgid ""
 "Readers are users which are asked to verify and comment contents before they "
 "are published"
@@ -3046,22 +3157,27 @@
 "Les relecteurs sont des utilisateurs qui sont sollicités pour vérifier et "
 "commenter un contenu avant sa publication"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:192
+#: src/pyams_content/shared/common/interfaces/__init__.py:210
 msgid "Guests"
 msgstr "Invités"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:193
+#: src/pyams_content/shared/common/interfaces/__init__.py:211
 msgid ""
 "Guests are users which are allowed to view contents with restricted access"
 msgstr ""
 "Les invités sont autorisés à consulter des contenus dont l'accès a été "
 "restreint"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:213
+#: src/pyams_content/shared/common/interfaces/__init__.py:231
 msgid "Principal ID"
 msgstr "ID utilisateur"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:253
+#: src/pyams_content/shared/common/interfaces/__init__.py:270
+#: src/pyams_content/shared/common/interfaces/__init__.py:295
+msgid "Publication checks"
+msgstr "Activer le tunnel de publication"
+
+#: src/pyams_content/shared/common/interfaces/__init__.py:271
 msgid ""
 "If 'yes', this contributor will have to confirm that contents have been "
 "previewed and checked before asking for publication"
@@ -3069,7 +3185,7 @@
 "Si 'oui', ce contributeur devra confirmer qu'il a bien prévisualisé et "
 "audité chaque contenu avant de pouvoir effectuer une demande de publication"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:278
+#: src/pyams_content/shared/common/interfaces/__init__.py:296
 msgid ""
 "If 'yes', this manager will have to confirm that contents have been "
 "previewed and checked before publishing a content"
@@ -3077,11 +3193,11 @@
 "Si 'oui', ce responsable devra confirmer qu'il a bien prévisualisé et audité "
 "chaque contenu avant de pouvoir effectuer une publication"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:283
+#: src/pyams_content/shared/common/interfaces/__init__.py:301
 msgid "Restricted contents"
 msgstr "Accès restreints"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:284
+#: src/pyams_content/shared/common/interfaces/__init__.py:302
 msgid ""
 "If 'yes', this manager will get restricted access to manage contents based "
 "on selected settings"
@@ -3089,11 +3205,11 @@
 "Si 'oui', ce responsable n'aura qu'un accès restreint à certains contenus en "
 "fonction de paramètres spécifiques"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:289
+#: src/pyams_content/shared/common/interfaces/__init__.py:307
 msgid "Selected owners"
 msgstr "Propriétaires"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:290
+#: src/pyams_content/shared/common/interfaces/__init__.py:308
 msgid "Manager will have access to contents owned by these principals"
 msgstr ""
 "Le responsable n'aura accès qu'aux contenus dont ces utilisateurs sont "
@@ -3182,7 +3298,7 @@
 msgstr "Champs de saisie"
 
 #: src/pyams_content/shared/form/zmi/field.py:170
-#: src/pyams_content/shared/form/interfaces/__init__.py:56
+#: src/pyams_content/shared/form/interfaces/__init__.py:57
 msgid "Field type"
 msgstr "Type de champ"
 
@@ -3191,11 +3307,11 @@
 msgstr "Liste des champs du formulaire"
 
 #: src/pyams_content/shared/form/zmi/field.py:226
-#: src/pyams_content/shared/form/zmi/field.py:237
+#: src/pyams_content/shared/form/zmi/field.py:239
 msgid "Add form field"
 msgstr "Ajouter un champ"
 
-#: src/pyams_content/shared/form/zmi/field.py:285
+#: src/pyams_content/shared/form/zmi/field.py:281
 msgid "Edit form field properties"
 msgstr "Propriétés du champ"
 
@@ -3207,153 +3323,153 @@
 msgid "No currently defined form field."
 msgstr "Ce formulaire ne comporte aucun champ."
 
-#: src/pyams_content/shared/form/zmi/field.py:262
+#: src/pyams_content/shared/form/zmi/field.py:266
 msgid "Specified name is already used!"
 msgstr "Le nom indiqué pour ce champ est déjà utilisé !"
 
-#: src/pyams_content/shared/form/interfaces/__init__.py:34
+#: src/pyams_content/shared/form/interfaces/__init__.py:35
 msgid "Form"
 msgstr "Formulaire"
 
-#: src/pyams_content/shared/form/interfaces/__init__.py:52
+#: src/pyams_content/shared/form/interfaces/__init__.py:53
 msgid "Field name"
 msgstr "Nom du champ"
 
-#: src/pyams_content/shared/form/interfaces/__init__.py:53
+#: src/pyams_content/shared/form/interfaces/__init__.py:54
 msgid "Field internal name; must be unique for a given form"
 msgstr ""
 "Nom interne du champ ; ce nom doit être unique pour un formulaire donné"
 
-#: src/pyams_content/shared/form/interfaces/__init__.py:57
+#: src/pyams_content/shared/form/interfaces/__init__.py:58
 msgid "Selected field type"
 msgstr "Type de champ proposé à l'internaute"
 
-#: src/pyams_content/shared/form/interfaces/__init__.py:62
+#: src/pyams_content/shared/form/interfaces/__init__.py:63
 msgid "User field label"
 msgstr "Libellé affiché à l'internaute"
 
-#: src/pyams_content/shared/form/interfaces/__init__.py:66
+#: src/pyams_content/shared/form/interfaces/__init__.py:67
 msgid "Field description can be displayed as hint"
 msgstr ""
 "Description du champ, qui pourra être affichée sous la forme d'une info-bulle"
 
-#: src/pyams_content/shared/form/interfaces/__init__.py:69
+#: src/pyams_content/shared/form/interfaces/__init__.py:70
 msgid "Placeholder"
 msgstr "Espace réservé"
 
-#: src/pyams_content/shared/form/interfaces/__init__.py:70
+#: src/pyams_content/shared/form/interfaces/__init__.py:71
 msgid "Some field types like textline can display a placeholder"
 msgstr ""
 "Certains champs tels que les zones de texte peuvent afficher ce texte tant "
 "qu'aucune valeur n'y a été saisie"
 
-#: src/pyams_content/shared/form/interfaces/__init__.py:73
+#: src/pyams_content/shared/form/interfaces/__init__.py:74
 msgid "Optional values"
 msgstr "Liste de valeurs"
 
-#: src/pyams_content/shared/form/interfaces/__init__.py:74
+#: src/pyams_content/shared/form/interfaces/__init__.py:75
 msgid "List of available values (for 'choice' and 'list' field types)"
 msgstr ""
 "Liste des valeurs disponibles (pour les champs de types 'Sélection simple' "
 "ou 'Sélection multiple')"
 
-#: src/pyams_content/shared/form/interfaces/__init__.py:77
+#: src/pyams_content/shared/form/interfaces/__init__.py:78
 msgid "Default value"
 msgstr "Valeur par défaut"
 
-#: src/pyams_content/shared/form/interfaces/__init__.py:78
+#: src/pyams_content/shared/form/interfaces/__init__.py:79
 msgid "Give default value if field type can use it"
 msgstr ""
 "Donner la valeur par défaut du champ ; attention, tous les types de champs "
 "ne peuvent pas utiliser une valeur par défaut !"
 
-#: src/pyams_content/shared/form/interfaces/__init__.py:81
+#: src/pyams_content/shared/form/interfaces/__init__.py:82
 msgid "Required?"
 msgstr "Obligatoire ?"
 
-#: src/pyams_content/shared/form/interfaces/__init__.py:82
+#: src/pyams_content/shared/form/interfaces/__init__.py:83
 msgid "Select 'yes' to set field as mandatory"
 msgstr "Sélectionnez 'oui' pour que la saisie de ce champ soit obligatoire"
 
-#: src/pyams_content/shared/form/interfaces/__init__.py:87
+#: src/pyams_content/shared/form/interfaces/__init__.py:88
 msgid "Select 'no' to hide given field..."
 msgstr "Sélectionnez 'non' pour masquer ce champ"
 
-#: src/pyams_content/shared/form/interfaces/__init__.py:121
+#: src/pyams_content/shared/form/interfaces/__init__.py:122
 msgid "Form title"
 msgstr "Titre du formulaire"
 
-#: src/pyams_content/shared/form/interfaces/__init__.py:124
+#: src/pyams_content/shared/form/interfaces/__init__.py:125
 msgid "Form header"
 msgstr "En-tête du formulaire"
 
-#: src/pyams_content/shared/form/interfaces/__init__.py:127
+#: src/pyams_content/shared/form/interfaces/__init__.py:128
 msgid "Form handler"
 msgstr "Gestionnaire du formulaire"
 
-#: src/pyams_content/shared/form/interfaces/__init__.py:128
+#: src/pyams_content/shared/form/interfaces/__init__.py:129
 msgid "Select how form data is transmitted"
 msgstr ""
 "Le gestionnaire sélectionné détermine la façon dont les données saisies par "
 "les internautes seront stockées ou envoyées"
 
-#: src/pyams_content/shared/form/interfaces/__init__.py:131
+#: src/pyams_content/shared/form/interfaces/__init__.py:132
 msgid "Authenticated only?"
 msgstr "Authentification requise ?"
 
-#: src/pyams_content/shared/form/interfaces/__init__.py:132
+#: src/pyams_content/shared/form/interfaces/__init__.py:133
 msgid "If 'yes', only authenticated users will be able to see and submit form"
 msgstr ""
 "Si 'oui', seuls les utilisateurs authentifiés pourront saisir des données "
 "dans le formulaire et les soumettre"
 
-#: src/pyams_content/shared/form/interfaces/__init__.py:136
+#: src/pyams_content/shared/form/interfaces/__init__.py:137
 msgid "Use captcha?"
 msgstr "Ajouter un captcha ?"
 
-#: src/pyams_content/shared/form/interfaces/__init__.py:137
+#: src/pyams_content/shared/form/interfaces/__init__.py:138
 msgid "If 'yes', a captcha will be added automatically to the form"
 msgstr "Si 'oui', un captcha sera ajouté automatiquement au formulaire"
 
-#: src/pyams_content/shared/form/interfaces/__init__.py:141
+#: src/pyams_content/shared/form/interfaces/__init__.py:142
 msgid "Submit label"
 msgstr "Libellé de soumission"
 
-#: src/pyams_content/shared/form/interfaces/__init__.py:142
+#: src/pyams_content/shared/form/interfaces/__init__.py:143
 msgid "Label of form submit button"
 msgstr "Libellé du bouton de soumission du formulaire"
 
-#: src/pyams_content/shared/form/interfaces/__init__.py:179
+#: src/pyams_content/shared/form/interfaces/__init__.py:180
 msgid "Source address"
 msgstr "Adresse source"
 
-#: src/pyams_content/shared/form/interfaces/__init__.py:180
+#: src/pyams_content/shared/form/interfaces/__init__.py:181
 msgid "Mail address from which form data is sent"
 msgstr "Adresse de messagerie émettrice des données"
 
-#: src/pyams_content/shared/form/interfaces/__init__.py:183
-msgid "Source name"
-msgstr "Nom de la source"
-
 #: src/pyams_content/shared/form/interfaces/__init__.py:184
+msgid "Source name"
+msgstr "Nom de la source"
+
+#: src/pyams_content/shared/form/interfaces/__init__.py:185
 msgid "Name of mail data sender"
 msgstr "Nom de l'émetteur des données"
 
-#: src/pyams_content/shared/form/interfaces/__init__.py:187
+#: src/pyams_content/shared/form/interfaces/__init__.py:188
 msgid "Recipient address"
 msgstr "Adresse de destination"
 
-#: src/pyams_content/shared/form/interfaces/__init__.py:188
+#: src/pyams_content/shared/form/interfaces/__init__.py:189
 msgid "Mail address to which form data is sent"
 msgstr ""
 "Adresse d'envoi des données; vous pouvez indiquer plusieurs adresses en les "
 "séparant par des point-virgules"
 
-#: src/pyams_content/shared/form/interfaces/__init__.py:191
+#: src/pyams_content/shared/form/interfaces/__init__.py:192
 msgid "Recipient name"
 msgstr "Nom du destinataire"
 
-#: src/pyams_content/shared/form/interfaces/__init__.py:192
+#: src/pyams_content/shared/form/interfaces/__init__.py:193
 msgid "Name of data recipient"
 msgstr "Nom du destinataire des messages"
 
@@ -3371,7 +3487,7 @@
 msgid "News topic « {title} »"
 msgstr "Actualité « {title} »"
 
-#: src/pyams_content/shared/news/interfaces/__init__.py:28
+#: src/pyams_content/shared/news/interfaces/__init__.py:29
 msgid "News topic"
 msgstr "Actualité"
 
@@ -3397,7 +3513,7 @@
 msgid "View themes settings"
 msgstr "Paramétrage des thèmes de la vue"
 
-#: src/pyams_content/shared/view/zmi/reference.py:53
+#: src/pyams_content/shared/view/zmi/reference.py:52
 msgid "References..."
 msgstr "Références"
 
@@ -3405,17 +3521,7 @@
 msgid "View internal references settings"
 msgstr "Références internes de la vue"
 
-#: src/pyams_content/shared/view/zmi/templates/render.pt:2
-msgid "View result items"
-msgstr "Contenus extraits par la vue"
-
-#: src/pyams_content/shared/view/zmi/templates/render.pt:3
-msgid "WARNING: items displayed in this preview are out of context!!"
-msgstr ""
-"ATTENTION : les résultats affichés dans cet aperçu ne tiennent pas compte du "
-"contexte pouvant être paramétré dans la vue !!!"
-
-#: src/pyams_content/shared/view/portlet/__init__.py:56
+#: src/pyams_content/shared/view/portlet/__init__.py:58
 msgid "View items"
 msgstr "Contenu d'une vue"
 
@@ -3432,7 +3538,7 @@
 msgstr "Vue"
 
 #: src/pyams_content/shared/view/interfaces/__init__.py:40
-#: src/pyams_content/interfaces/__init__.py:111
+#: src/pyams_content/interfaces/__init__.py:113
 #: src/pyams_content/features/review/interfaces.py:74
 msgid "Creation date"
 msgstr "Date de création"
@@ -3511,17 +3617,27 @@
 "Indique comment les références internes indiquées seront intégrées à la "
 "liste des résultats"
 
-#: src/pyams_content/shared/view/interfaces/__init__.py:171
+#: src/pyams_content/shared/view/interfaces/__init__.py:164
+msgid "Exclude context?"
+msgstr "Exclure le contexte ?"
+
+#: src/pyams_content/shared/view/interfaces/__init__.py:165
+msgid "If 'yes', context will be excluded from results list"
+msgstr ""
+"Si 'oui', le contexte d'application de la vue sera automatiquement exclus de "
+"la liste des résultats"
+
+#: src/pyams_content/shared/view/interfaces/__init__.py:176
 msgid "Select context themes?"
 msgstr "Thèmes du contexte ?"
 
-#: src/pyams_content/shared/view/interfaces/__init__.py:172
+#: src/pyams_content/shared/view/interfaces/__init__.py:177
 msgid "If 'yes', themes will be extracted from context"
 msgstr ""
 "Si 'oui', les thèmes associés au contexte d'application de la vue seront "
 "automatiquement sélectionnés"
 
-#: src/pyams_content/shared/view/interfaces/__init__.py:176
+#: src/pyams_content/shared/view/interfaces/__init__.py:181
 msgid "Other terms"
 msgstr "Autres thèmes"
 
@@ -3547,12 +3663,12 @@
 msgid "Image map..."
 msgstr "Image cliquable"
 
-#: src/pyams_content/shared/imagemap/zmi/paragraph.py:65
+#: src/pyams_content/shared/imagemap/zmi/paragraph.py:67
 msgid "Add new image map"
 msgstr "Ajout d'une image cliquable"
 
-#: src/pyams_content/shared/imagemap/zmi/paragraph.py:97
-#: src/pyams_content/shared/logo/zmi/paragraph.py:96
+#: src/pyams_content/shared/imagemap/zmi/paragraph.py:94
+#: src/pyams_content/shared/logo/zmi/paragraph.py:93
 msgid "Edit paragraph properties"
 msgstr "Propriétés de l'image cliquable"
 
@@ -3612,15 +3728,15 @@
 msgid "Bad query object_name parameter value!"
 msgstr "Valeur incorrecte du paramètre object_name !"
 
-#: src/pyams_content/shared/imagemap/zmi/area.py:46
+#: src/pyams_content/shared/imagemap/zmi/area.py:47
 msgid "Add image area"
 msgstr "Ajouter une zone"
 
-#: src/pyams_content/shared/imagemap/zmi/area.py:64
+#: src/pyams_content/shared/imagemap/zmi/area.py:66
 msgid "Add new image area"
 msgstr "Ajout d'une zone cliquable"
 
-#: src/pyams_content/shared/imagemap/zmi/area.py:108
+#: src/pyams_content/shared/imagemap/zmi/area.py:107
 msgid "Edit image map properties"
 msgstr "Propriétés de l'image"
 
@@ -3668,7 +3784,7 @@
 msgid "Image map template"
 msgstr "Mode de rendu"
 
-#: src/pyams_content/shared/site/folder.py:58
+#: src/pyams_content/shared/site/folder.py:59
 msgid "Site folder"
 msgstr "Rubrique"
 
@@ -3676,39 +3792,44 @@
 msgid "Content link"
 msgstr "Contenu lié"
 
-#: src/pyams_content/shared/site/zmi/folder.py:59
+#: src/pyams_content/shared/site/manager.py:68
+#: src/pyams_content/shared/site/zmi/manager.py:125
+msgid "Site manager"
+msgstr "Site"
+
+#: src/pyams_content/shared/site/zmi/folder.py:61
 msgid "Add site folder..."
 msgstr "Ajouter une rubrique"
 
-#: src/pyams_content/shared/site/zmi/folder.py:90
+#: src/pyams_content/shared/site/zmi/folder.py:93
 msgid "Add site folder"
 msgstr "Ajout d'une rubrique"
 
-#: src/pyams_content/shared/site/zmi/folder.py:166
+#: src/pyams_content/shared/site/zmi/folder.py:162
 msgid "Site folder management"
 msgstr "Gérer cette rubrique"
 
-#: src/pyams_content/shared/site/zmi/folder.py:177
+#: src/pyams_content/shared/site/zmi/folder.py:190
 msgid "Site folder properties"
 msgstr "Propriétés de la rubrique"
 
-#: src/pyams_content/shared/site/zmi/folder.py:69
-#: src/pyams_content/interfaces/__init__.py:100
+#: src/pyams_content/shared/site/zmi/folder.py:71
+#: src/pyams_content/interfaces/__init__.py:102
 msgid "Visible label used to display content"
 msgstr "Le titre présenté aux internautes"
 
-#: src/pyams_content/shared/site/zmi/folder.py:72
+#: src/pyams_content/shared/site/zmi/folder.py:74
 #: src/pyams_content/shared/site/zmi/__init__.py:72
-#: src/pyams_content/shared/site/zmi/link.py:67
+#: src/pyams_content/shared/site/zmi/link.py:66
 msgid "Parent"
 msgstr "Niveau parent"
 
-#: src/pyams_content/shared/site/zmi/folder.py:73
-#: src/pyams_content/shared/site/zmi/link.py:68
+#: src/pyams_content/shared/site/zmi/folder.py:75
+#: src/pyams_content/shared/site/zmi/link.py:67
 msgid "Folder's parent"
 msgstr "Niveau de rattachement de cette rubrique"
 
-#: src/pyams_content/shared/site/zmi/folder.py:150
+#: src/pyams_content/shared/site/zmi/folder.py:155
 msgid "You must provide a folder name for default server language!"
 msgstr ""
 "Vous devez fournir un nom pour ce dossier pour la langue par défaut du "
@@ -3722,7 +3843,7 @@
 msgid "Add topic..."
 msgstr "Ajouter un article"
 
-#: src/pyams_content/shared/site/zmi/__init__.py:84
+#: src/pyams_content/shared/site/zmi/__init__.py:86
 msgid "Add topic"
 msgstr "Ajout d'un article"
 
@@ -3730,7 +3851,7 @@
 msgid "Topic's parent"
 msgstr "Niveau parent"
 
-#: src/pyams_content/shared/site/zmi/link.py:58
+#: src/pyams_content/shared/site/zmi/link.py:57
 msgid "Rent content..."
 msgstr "Lier un contenu"
 
@@ -3738,87 +3859,83 @@
 msgid "Rent existing content"
 msgstr "Lier un contenu existant"
 
-#: src/pyams_content/shared/site/zmi/link.py:141
+#: src/pyams_content/shared/site/zmi/link.py:135
 msgid "Edit content link properties"
 msgstr "Propriétés du lien"
 
 #: src/pyams_content/shared/site/zmi/container.py:106
 #: src/pyams_content/shared/site/zmi/container.py:118
-#: src/pyams_content/shared/blog/zmi/manager.py:160
-#: src/pyams_content/shared/blog/zmi/manager.py:172
+#: src/pyams_content/shared/blog/zmi/manager.py:155
+#: src/pyams_content/shared/blog/zmi/manager.py:167
 msgid "Publication dates..."
 msgstr "Dates de publication"
 
-#: src/pyams_content/shared/site/zmi/container.py:131
-#: src/pyams_content/shared/blog/zmi/manager.py:185
+#: src/pyams_content/shared/site/zmi/container.py:132
+#: src/pyams_content/shared/blog/zmi/manager.py:181
 msgid "Update publication dates"
 msgstr "Dates de publication"
 
-#: src/pyams_content/shared/site/zmi/container.py:183
-#: src/pyams_content/shared/site/zmi/container.py:193
+#: src/pyams_content/shared/site/zmi/container.py:178
+#: src/pyams_content/shared/site/zmi/container.py:188
 #: src/pyams_content/root/zmi/sites.py:68
 msgid "Site tree"
 msgstr "Arborescence du site"
 
-#: src/pyams_content/shared/site/zmi/container.py:312
-#: src/pyams_content/shared/site/zmi/container.py:162
+#: src/pyams_content/shared/site/zmi/container.py:307
+#: src/pyams_content/shared/site/zmi/container.py:156
 msgid "Visible element?"
 msgstr "Élément visible ?"
 
-#: src/pyams_content/shared/site/zmi/container.py:313
+#: src/pyams_content/shared/site/zmi/container.py:308
 msgid "Switch element visibility"
 msgstr "Cliquez pour rendre l'élément visible ou non"
 
-#: src/pyams_content/shared/site/zmi/container.py:374
+#: src/pyams_content/shared/site/zmi/container.py:369
 msgid "Folders and topics"
 msgstr "Rubriquage"
 
-#: src/pyams_content/shared/site/zmi/container.py:410
-#: src/pyams_content/root/zmi/__init__.py:798
+#: src/pyams_content/shared/site/zmi/container.py:405
+#: src/pyams_content/root/zmi/__init__.py:817
 msgid "Content"
 msgstr "Contenu"
 
-#: src/pyams_content/shared/site/zmi/container.py:506
+#: src/pyams_content/shared/site/zmi/container.py:501
 msgid "Delete site item"
 msgstr "Supprimer ce contenu"
 
-#: src/pyams_content/shared/site/zmi/container.py:383
+#: src/pyams_content/shared/site/zmi/container.py:378
 msgid "Click to open/close all folders"
 msgstr "Afficher/masquer toutes les rubriques"
 
-#: src/pyams_content/shared/site/zmi/container.py:399
+#: src/pyams_content/shared/site/zmi/container.py:394
 msgid "Click to show/hide inner folders"
 msgstr "Cliquer pour afficher ou cache les sous-niveaux"
 
-#: src/pyams_content/shared/site/zmi/container.py:269
+#: src/pyams_content/shared/site/zmi/container.py:264
 msgid "Can't reparent object to one of it's children. Reloading..."
 msgstr ""
 "Impossible de déplacer une rubrique dans l'une de ses sous-rubriques ou "
 "contenus ! Rechargement de la page..."
 
-#: src/pyams_content/shared/site/zmi/manager.py:58
+#: src/pyams_content/shared/site/zmi/manager.py:57
 msgid "Site management"
 msgstr "Gérer ce site"
 
-#: src/pyams_content/shared/site/zmi/manager.py:88
-#: src/pyams_content/shared/site/zmi/manager.py:100
+#: src/pyams_content/shared/site/zmi/manager.py:112
+#: src/pyams_content/shared/site/zmi/manager.py:126
 msgid "Add site manager"
 msgstr "Ajouter un site"
 
-#: src/pyams_content/shared/site/zmi/manager.py:99
-msgid "Site manager"
-msgstr "Site"
-
-#: src/pyams_content/shared/site/zmi/manager.py:131
-#: src/pyams_content/shared/blog/zmi/manager.py:126
+#: src/pyams_content/shared/site/zmi/manager.py:156
+#: src/pyams_content/shared/blog/zmi/manager.py:127
 msgid "You must provide a short name for default server language!"
 msgstr "Vous devez fournir un nom court pour la langue par défaut du serveur !"
 
-#: src/pyams_content/shared/site/zmi/manager.py:135
+#: src/pyams_content/shared/site/zmi/manager.py:160
 msgid "Specified site manager name is already used!"
 msgstr "Le nom indiqué pour ce site existe déjà !"
 
-#: src/pyams_content/shared/site/zmi/manager.py:139
+#: src/pyams_content/shared/site/zmi/manager.py:164
 msgid "A site manager is already registered with this name!!"
 msgstr "Un site est déjà inscrit dans le registre avec ce nom !"
 
@@ -3876,11 +3993,11 @@
 msgid "no URL defined"
 msgstr "aucune URL définie"
 
-#: src/pyams_content/shared/logo/zmi/paragraph.py:53
+#: src/pyams_content/shared/logo/zmi/paragraph.py:54
 msgid "Logos..."
 msgstr "Logos"
 
-#: src/pyams_content/shared/logo/zmi/paragraph.py:64
+#: src/pyams_content/shared/logo/zmi/paragraph.py:67
 msgid "Add new logos paragraph"
 msgstr "Ajout d'une sélection de logos"
 
@@ -3941,19 +4058,19 @@
 msgstr "Gérer ce blog"
 
 #: src/pyams_content/shared/blog/zmi/manager.py:83
-#: src/pyams_content/shared/blog/zmi/manager.py:95
+#: src/pyams_content/shared/blog/zmi/manager.py:97
 msgid "Add blog manager"
 msgstr "Ajouter un blog"
 
-#: src/pyams_content/shared/blog/zmi/manager.py:94
+#: src/pyams_content/shared/blog/zmi/manager.py:96
 msgid "Blog manager"
 msgstr "Blog"
 
-#: src/pyams_content/shared/blog/zmi/manager.py:130
+#: src/pyams_content/shared/blog/zmi/manager.py:131
 msgid "Specified blog manager name is already used!"
 msgstr "Le nom indiqué pour ce blog existe déjà !"
 
-#: src/pyams_content/shared/blog/zmi/manager.py:134
+#: src/pyams_content/shared/blog/zmi/manager.py:135
 msgid "A blog manager is already registered with this name!!"
 msgstr "Un blog est déjà inscrit dans le registre avec ce nom !"
 
@@ -3981,6 +4098,10 @@
 msgid "Default length used for inner tables and dashboards"
 msgstr "Longueur par défaut des tableaux internes et des tableaux de bord"
 
+#: src/pyams_content/root/__init__.py:67
+msgid "Site root"
+msgstr "Racine du site"
+
 #: src/pyams_content/root/zmi/sites.py:78
 msgid "Blogs and shared sites"
 msgstr "Blogs et sites partagés"
@@ -4005,7 +4126,7 @@
 msgid "Content types"
 msgstr "Types de contenus"
 
-#: src/pyams_content/root/zmi/__init__.py:77
+#: src/pyams_content/root/zmi/__init__.py:78
 msgid "Home"
 msgstr "Accueil"
 
@@ -4017,19 +4138,19 @@
 msgid "SEARCH - Between all contents"
 msgstr "RECHERCHE - Tous contenus confondus"
 
-#: src/pyams_content/root/interfaces/__init__.py:36
+#: src/pyams_content/root/interfaces/__init__.py:39
 msgid "Site managers"
 msgstr "Administrateurs"
 
-#: src/pyams_content/root/interfaces/__init__.py:44
+#: src/pyams_content/root/interfaces/__init__.py:47
 msgid "Templates managers"
 msgstr "Designers"
 
-#: src/pyams_content/root/interfaces/__init__.py:48
+#: src/pyams_content/root/interfaces/__init__.py:51
 msgid "Operators group"
 msgstr "Groupe des opérateurs"
 
-#: src/pyams_content/root/interfaces/__init__.py:49
+#: src/pyams_content/root/interfaces/__init__.py:52
 msgid "Name of group containing all roles owners"
 msgstr ""
 "Tous les utilisateurs auxquels sera attribué un rôle seront placés dans ce "
@@ -4355,11 +4476,11 @@
 msgstr ""
 "Retrait automatique des contenus après la date de fin de publication :\n"
 
-#: src/pyams_content/workflow/zmi/task.py:43
+#: src/pyams_content/workflow/zmi/task.py:42
 msgid "Add content archiver task..."
 msgstr "Ajouter une tâche d'archivage automatique"
 
-#: src/pyams_content/workflow/zmi/task.py:54
+#: src/pyams_content/workflow/zmi/task.py:55
 msgid "Add automatic content archiver"
 msgstr "Ajout d'une tâche d'archivage automatique"
 
@@ -4388,18 +4509,23 @@
 msgstr "Clé unique"
 
 #: src/pyams_content/interfaces/__init__.py:96
-msgid "WARNING: this key can't be modified after creation!!!"
-msgstr "ATTENTION : cette clé ne pourra plus être modifiée !!!"
-
-#: src/pyams_content/interfaces/__init__.py:103
+msgid ""
+"WARNING: this key can't be modified after creation!!! Spaces, uppercase "
+"letters ou accentuated characters will be replaced automatically."
+msgstr ""
+"ATTENTION : cette clé ne pourra plus être modifiée après sa création. Les "
+"espaces, les majuscules, les lettres accentuées et les caractères spéciaux "
+"seront remplacées automatiquement."
+
+#: src/pyams_content/interfaces/__init__.py:105
 msgid "Short name"
 msgstr "Fil d'Ariane"
 
-#: src/pyams_content/interfaces/__init__.py:104
+#: src/pyams_content/interfaces/__init__.py:106
 msgid "Short name used in breadcrumbs"
 msgstr "Libellé utilisé dans le fil d'Ariane"
 
-#: src/pyams_content/interfaces/__init__.py:115
+#: src/pyams_content/interfaces/__init__.py:117
 msgid "Modification date"
 msgstr "Dernière modification apportée"
 
@@ -4420,11 +4546,11 @@
 msgid "Properties..."
 msgstr "Propriétés"
 
-#: src/pyams_content/reference/zmi/table.py:156
+#: src/pyams_content/reference/zmi/table.py:157
 msgid "Edit table properties"
 msgstr "Propriétés de la table"
 
-#: src/pyams_content/reference/zmi/table.py:173
+#: src/pyams_content/reference/zmi/table.py:167
 msgid "Table management"
 msgstr "Gérer cette table"
 
@@ -4432,11 +4558,16 @@
 msgid "References"
 msgstr "Tables de réf."
 
+#: src/pyams_content/reference/pictograms/zmi/__init__.py:169
+#: src/pyams_content/reference/pictograms/zmi/widget.py:55
+msgid "Default header: --"
+msgstr "En-tête par défaut : --"
+
 #: src/pyams_content/reference/pictograms/zmi/manager.py:51
 msgid "Pictograms selection..."
 msgstr "Sélection de pictogrammes"
 
-#: src/pyams_content/reference/pictograms/zmi/manager.py:62
+#: src/pyams_content/reference/pictograms/zmi/manager.py:63
 #: src/pyams_content/reference/pictograms/zmi/templates/manager-selection.pt:34
 #: src/pyams_content/reference/pictograms/interfaces/__init__.py:73
 msgid "Selected pictograms"
@@ -4451,6 +4582,10 @@
 msgid "Display pictogram properties"
 msgstr "Propriétés du pictogramme"
 
+#: src/pyams_content/reference/pictograms/zmi/templates/pictogram-header.pt:6
+msgid "Default header: ${header}"
+msgstr "En-tête par défaut : ${header}"
+
 #: src/pyams_content/reference/pictograms/interfaces/__init__.py:45
 msgid "Pictogram content"
 msgstr "Utilisez le bouton \"Parcourir\" pour modifier le contenu de l'image"
@@ -4468,7 +4603,7 @@
 msgid "List of selected pictograms which will be available to shared contents"
 msgstr "Liste des pictogrammes proposés dans les contenus partagés"
 
-#: src/pyams_content/features/renderer/zmi/__init__.py:72
+#: src/pyams_content/features/renderer/zmi/__init__.py:70
 #: src/pyams_content/features/renderer/zmi/templates/renderer-input.pt:4
 msgid "Edit renderer properties"
 msgstr "Propriétés du mode de rendu"
@@ -4548,7 +4683,7 @@
 
 #. Default: Heading
 #: src/pyams_content/features/alert/interfaces.py:65
-#: src/pyams_content/features/alert/zmi/container.py:158
+#: src/pyams_content/features/alert/zmi/container.py:157
 msgid "alert-header"
 msgstr "En-tête"
 
@@ -4557,7 +4692,7 @@
 msgstr "En-tête de l'alerte (« Alerte », « Important », « Prudence »...)"
 
 #: src/pyams_content/features/alert/interfaces.py:69
-#: src/pyams_content/features/alert/zmi/container.py:170
+#: src/pyams_content/features/alert/zmi/container.py:169
 msgid "Message"
 msgstr "Message"
 
@@ -4607,7 +4742,7 @@
 "donné, l'alerte apparaîtra à nouveau. Si aucun intervalle n'est indiqué, "
 "l'alerte s'affichera en permanence."
 
-#: src/pyams_content/features/alert/zmi/__init__.py:46
+#: src/pyams_content/features/alert/zmi/__init__.py:45
 msgid "Add alert"
 msgstr "Ajouter une alerte"
 
@@ -4615,7 +4750,7 @@
 msgid "Add new alert"
 msgstr "Ajout d'une alerte"
 
-#: src/pyams_content/features/alert/zmi/__init__.py:85
+#: src/pyams_content/features/alert/zmi/__init__.py:79
 msgid "Edit alert properties"
 msgstr "Propriétés de l'alerte"
 
@@ -4623,31 +4758,106 @@
 msgid "Alerts"
 msgstr "Alertes"
 
-#: src/pyams_content/features/alert/zmi/container.py:192
+#: src/pyams_content/features/alert/zmi/container.py:191
 msgid "Alert list"
 msgstr "Liste des alertes"
 
-#: src/pyams_content/features/alert/zmi/container.py:91
+#: src/pyams_content/features/alert/zmi/container.py:90
 msgid "No currently defined alert."
 msgstr "Aucune alerte n'est définie actuellement."
 
-#: src/pyams_content/features/footer/zmi/__init__.py:56
+#: src/pyams_content/features/menu/zmi/__init__.py:81
+msgid "Add menu..."
+msgstr "Ajouter un menu"
+
+#: src/pyams_content/features/menu/zmi/__init__.py:92
+msgid "Add new menu"
+msgstr "Ajout d'un menu"
+
+#: src/pyams_content/features/menu/zmi/__init__.py:123
+msgid "Edit menu properties"
+msgstr "Propriétés du menu"
+
+#: src/pyams_content/features/menu/zmi/__init__.py:110
+msgid "Menu was correctly added."
+msgstr "Le menu a été ajouté."
+
+#: src/pyams_content/features/menu/zmi/__init__.py:388
+msgid "Link was correctly added."
+msgstr "Le lien a été ajouté."
+
+#: src/pyams_content/features/menu/zmi/templates/menu-name-cell.pt:7
+msgid "Click to see menu items"
+msgstr "Montrer ou cacher les éléments du menu"
+
+#: src/pyams_content/features/menu/portlet/navigation/simple.py:68
+msgid "Simple navigation"
+msgstr "Navigation à un niveau"
+
+#: src/pyams_content/features/menu/portlet/navigation/double.py:68
+msgid "Double navigation"
+msgstr "Navigation à deux niveaux"
+
+#: src/pyams_content/features/menu/portlet/navigation/zmi/simple.py:70
+msgid "Navigation links"
+msgstr "Liens de navigation"
+
+#: src/pyams_content/features/menu/portlet/navigation/zmi/double.py:70
+msgid "Navigation menus"
+msgstr "Menus de navigation"
+
+#: src/pyams_content/features/menu/portlet/navigation/zmi/templates/simple-preview.pt:13
+msgid "Link has no illustration"
+msgstr "Le lien n'a pas d'illustration"
+
+#: src/pyams_content/features/menu/portlet/navigation/interfaces/simple.py:32
+#: src/pyams_content/features/menu/portlet/navigation/interfaces/double.py:32
+msgid "Portlet main title"
+msgstr "Titre du composant"
+
+#: src/pyams_content/features/menu/portlet/navigation/interfaces/simple.py:35
+#: src/pyams_content/features/menu/portlet/navigation/interfaces/double.py:35
+msgid "Subtitle"
+msgstr "Sous-titre"
+
+#: src/pyams_content/features/menu/portlet/navigation/interfaces/simple.py:36
+#: src/pyams_content/features/menu/portlet/navigation/interfaces/double.py:36
+msgid "Portlet subtitle"
+msgstr "Sous-titre du composant"
+
+#: src/pyams_content/features/menu/interfaces/__init__.py:63
+msgid "Menu title"
+msgstr "Libellé"
+
+#: src/pyams_content/features/menu/interfaces/__init__.py:64
+msgid "Displayed menu label"
+msgstr "Libellé du menu"
+
+#: src/pyams_content/features/footer/zmi/__init__.py:60
 msgid "Page footer"
 msgstr "Pied de pages"
 
-#: src/pyams_content/features/footer/zmi/__init__.py:74
+#: src/pyams_content/features/footer/zmi/__init__.py:78
 msgid "Edit footer settings"
 msgstr "Paramétrage des pieds de pages"
 
-#: src/pyams_content/features/footer/zmi/__init__.py:172
+#: src/pyams_content/features/footer/zmi/__init__.py:152
+msgid ""
+"WARNING: Footer properties are saved automatically when changing inherit "
+"mode!!"
+msgstr ""
+"ATTENTION : les propriétés du pied de page sont enregistrées automatiquement "
+"lorsque vous modifiez le mode d'héritage !!"
+
+#: src/pyams_content/features/footer/zmi/__init__.py:220
 msgid "Footer renderer settings"
 msgstr "Propriétés du mode de rendu"
 
-#: src/pyams_content/features/footer/zmi/__init__.py:101
+#: src/pyams_content/features/footer/zmi/__init__.py:107
 msgid "Don't inherit parent footer"
 msgstr "Ne pas hériter du pied de pages du parent"
 
-#: src/pyams_content/features/footer/skin/__init__.py:49
+#: src/pyams_content/features/footer/skin/__init__.py:53
 msgid "Hidden footer"
 msgstr "Ne pas afficher de pied de pages"
 
@@ -4659,17 +4869,17 @@
 msgid "Presentation template used for this footer"
 msgstr "Mode de rendu utilisé par ce pied de page"
 
-#: src/pyams_content/features/review/__init__.py:180
+#: src/pyams_content/features/review/__init__.py:181
 #, python-format
 msgid "Request comment: {comment}"
 msgstr "Commentaire joint : {comment}"
 
-#: src/pyams_content/features/review/__init__.py:210
+#: src/pyams_content/features/review/__init__.py:211
 #, python-format
 msgid "A new comment was added on content « {0} »"
 msgstr "Un nouveau commentaire a été ajouté pour le contenu « {0} »"
 
-#: src/pyams_content/features/review/__init__.py:167
+#: src/pyams_content/features/review/__init__.py:168
 #, python-format
 msgid "[{service_name}] A content review is requested"
 msgstr "[{service_name}] Demande de relecture"
@@ -4714,15 +4924,15 @@
 msgid "Ask for review..."
 msgstr "Demander une relecture"
 
-#: src/pyams_content/features/review/zmi/__init__.py:99
+#: src/pyams_content/features/review/zmi/__init__.py:100
 msgid "Content review request"
 msgstr "Demande de relecture"
 
-#: src/pyams_content/features/review/zmi/__init__.py:172
+#: src/pyams_content/features/review/zmi/__init__.py:166
 msgid "Comments"
 msgstr "Commentaires"
 
-#: src/pyams_content/features/review/zmi/__init__.py:192
+#: src/pyams_content/features/review/zmi/__init__.py:186
 msgid "Review comments"
 msgstr "Commentaires associés à cette version"
 
@@ -4763,16 +4973,16 @@
 msgid "Ask for content review"
 msgstr "Demander la relecture"
 
-#: src/pyams_content/features/review/zmi/__init__.py:153
+#: src/pyams_content/features/review/zmi/__init__.py:147
 msgid "Request successful. No new notification have been sent"
 msgstr ""
 "Votre demande a été transmise. Aucune nouvelle notification n'a été envoyée."
 
-#: src/pyams_content/features/review/zmi/__init__.py:266
+#: src/pyams_content/features/review/zmi/__init__.py:260
 msgid "Message is mandatory!"
 msgstr "Un commentaire est obligatoire !"
 
-#: src/pyams_content/features/review/zmi/__init__.py:141
+#: src/pyams_content/features/review/zmi/__init__.py:135
 #, python-format
 msgid "Request successful. {count} new notification(s) have been sent"
 msgstr "Votre demande a été transmise. {count} notification(s) envoyée(s)."
@@ -4855,26 +5065,48 @@
 msgid "Thank you."
 msgstr "Merci."
 
-#: src/pyams_content/features/header/zmi/__init__.py:62
+#: src/pyams_content/features/header/zmi/__init__.py:66
 msgid "Page header"
 msgstr "En-tête de pages"
 
-#: src/pyams_content/features/header/zmi/__init__.py:80
+#: src/pyams_content/features/header/zmi/__init__.py:84
 msgid "Edit header settings"
 msgstr "Paramétrage des en-têtes de pages"
 
-#: src/pyams_content/features/header/zmi/__init__.py:178
+#: src/pyams_content/features/header/zmi/__init__.py:161
+msgid ""
+"WARNING: Header properties are saved automatically when changing inherit "
+"mode!!"
+msgstr ""
+"ATTENTION : les propriétés de l'en-tête sont enregistrées automatiquement "
+"lorsque vous modifiez le mode d'héritage !!"
+
+#: src/pyams_content/features/header/zmi/__init__.py:229
 msgid "Header renderer settings"
 msgstr "Propriétés du mode de rendu"
 
-#: src/pyams_content/features/header/zmi/__init__.py:107
+#: src/pyams_content/features/header/zmi/__init__.py:113
 msgid "Don't inherit parent header"
 msgstr "Ne pas hériter de l'en-tête de pages du parent"
 
-#: src/pyams_content/features/header/skin/__init__.py:49
+#: src/pyams_content/features/header/skin/__init__.py:53
 msgid "Hidden header"
 msgstr "Ne pas afficher d'en-tête de pages"
 
+#~ msgid "WARNING: this key can't be modified after creation!!!"
+#~ msgstr "ATTENTION : cette clé ne pourra plus être modifiée !!!"
+
+#~ msgid "Links"
+#~ msgstr "Liens"
+
+#~ msgid "View result items"
+#~ msgstr "Contenus extraits par la vue"
+
+#~ msgid "WARNING: items displayed in this preview are out of context!!"
+#~ msgstr ""
+#~ "ATTENTION : les résultats affichés dans cet aperçu ne tiennent pas compte "
+#~ "du contexte pouvant être paramétré dans la vue !!!"
+
 #~ msgid "Milestones paragraph"
 #~ msgstr "Chronologie"
 
@@ -4908,9 +5140,6 @@
 #~ msgid "Check content..."
 #~ msgstr "Auditer le contenu"
 
-#~ msgid "Simple list view"
-#~ msgstr "Vue simple d'une liste d'éléments"
-
 #~ msgid "Paragraphs types..."
 #~ msgstr "Types de paragraphes"
 
@@ -4978,9 +5207,6 @@
 #~ "Ce formulaire vous permet d'effectuer la sélection des pictogrammes qui "
 #~ "seront disponibles pour être intégrés dans les contenus partagés."
 
-#~ msgid "Default gallery renderer"
-#~ msgstr "Galerie par défaut"
-
 #~ msgid "Add medias gallery..."
 #~ msgstr "Galerie de médias"
 
@@ -5069,9 +5295,6 @@
 #~ msgid "The content « {0} » has been archived"
 #~ msgstr "Le contenu « {0} » a été archivé"
 
-#~ msgid "Add comment..."
-#~ msgstr "Ajouter un commentaire"
-
 #~ msgid "Publication settings"
 #~ msgstr "Dates de publication et de retrait"
 
@@ -5109,9 +5332,6 @@
 #~ msgid "Image title"
 #~ msgstr "Légende de l'image"
 
-#~ msgid "Links"
-#~ msgstr "Liens"
-
 #~ msgid "Paragraph title"
 #~ msgstr "Titre du paragraphe"
 
--- a/src/pyams_content/locales/pyams_content.pot	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/locales/pyams_content.pot	Wed Jun 27 16:42:01 2018 +0200
@@ -6,7 +6,7 @@
 msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE 1.0\n"
-"POT-Creation-Date: 2018-06-06 13:20+0200\n"
+"POT-Creation-Date: 2018-06-26 14:52+0200\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -49,31 +49,31 @@
 msgid "Webmaster (role)"
 msgstr ""
 
-#: ./src/pyams_content/__init__.py:62
+#: ./src/pyams_content/__init__.py:64
 msgid "Pilot (role)"
 msgstr ""
 
-#: ./src/pyams_content/__init__.py:70
+#: ./src/pyams_content/__init__.py:74
 msgid "Manager (role)"
 msgstr ""
 
-#: ./src/pyams_content/__init__.py:77
+#: ./src/pyams_content/__init__.py:83
 msgid "Owner (role)"
 msgstr ""
 
-#: ./src/pyams_content/__init__.py:82
+#: ./src/pyams_content/__init__.py:88
 msgid "Contributor (role)"
 msgstr ""
 
-#: ./src/pyams_content/__init__.py:90
+#: ./src/pyams_content/__init__.py:99
 msgid "Reader (role)"
 msgstr ""
 
-#: ./src/pyams_content/__init__.py:96
+#: ./src/pyams_content/__init__.py:109
 msgid "Operator (role)"
 msgstr ""
 
-#: ./src/pyams_content/__init__.py:100
+#: ./src/pyams_content/__init__.py:114
 msgid "Guest user (role)"
 msgstr ""
 
@@ -81,25 +81,25 @@
 msgid "Gallery"
 msgstr ""
 
-#: ./src/pyams_content/component/gallery/zmi/file.py:58
+#: ./src/pyams_content/component/gallery/zmi/file.py:57
 #: ./src/pyams_content/component/gallery/zmi/file.py:69
-#: ./src/pyams_content/component/gallery/zmi/paragraph.py:174
+#: ./src/pyams_content/component/gallery/zmi/paragraph.py:164
 msgid "Add media(s)"
 msgstr ""
 
-#: ./src/pyams_content/component/gallery/zmi/file.py:189
+#: ./src/pyams_content/component/gallery/zmi/file.py:184
 msgid "Update media properties"
 msgstr ""
 
-#: ./src/pyams_content/component/gallery/zmi/file.py:249
+#: ./src/pyams_content/component/gallery/zmi/file.py:238
 msgid "Remove media..."
 msgstr ""
 
-#: ./src/pyams_content/component/gallery/zmi/file.py:154
+#: ./src/pyams_content/component/gallery/zmi/file.py:148
 msgid "Show/hide media"
 msgstr ""
 
-#: ./src/pyams_content/component/gallery/zmi/file.py:216
+#: ./src/pyams_content/component/gallery/zmi/file.py:211
 msgid "Audio content"
 msgstr ""
 
@@ -107,19 +107,19 @@
 msgid "Medias gallery..."
 msgstr ""
 
-#: ./src/pyams_content/component/gallery/zmi/paragraph.py:67
+#: ./src/pyams_content/component/gallery/zmi/paragraph.py:69
 msgid "Add new gallery"
 msgstr ""
 
-#: ./src/pyams_content/component/gallery/zmi/paragraph.py:99
+#: ./src/pyams_content/component/gallery/zmi/paragraph.py:96
 msgid "Edit gallery properties"
 msgstr ""
 
-#: ./src/pyams_content/component/gallery/zmi/__init__.py:62
+#: ./src/pyams_content/component/gallery/zmi/__init__.py:63
 msgid "Update gallery properties"
 msgstr ""
 
-#: ./src/pyams_content/component/gallery/zmi/__init__.py:101
+#: ./src/pyams_content/component/gallery/zmi/__init__.py:95
 msgid "Update gallery contents"
 msgstr ""
 
@@ -134,8 +134,8 @@
 #: ./src/pyams_content/component/gallery/zmi/interfaces.py:40
 #: ./src/pyams_content/component/gallery/interfaces/__init__.py:61
 #: ./src/pyams_content/component/extfile/interfaces/__init__.py:44
-#: ./src/pyams_content/component/illustration/interfaces/__init__.py:56
-#: ./src/pyams_content/component/paragraph/interfaces/video.py:48
+#: ./src/pyams_content/component/illustration/interfaces/__init__.py:68
+#: ./src/pyams_content/component/paragraph/interfaces/video.py:52
 #: ./src/pyams_content/component/paragraph/interfaces/audio.py:48
 #: ./src/pyams_content/component/paragraph/interfaces/verbatim.py:44
 #: ./src/pyams_content/component/video/interfaces/__init__.py:52
@@ -145,7 +145,7 @@
 #: ./src/pyams_content/component/gallery/zmi/interfaces.py:41
 #: ./src/pyams_content/component/gallery/interfaces/__init__.py:62
 #: ./src/pyams_content/component/extfile/interfaces/__init__.py:45
-#: ./src/pyams_content/component/paragraph/interfaces/video.py:49
+#: ./src/pyams_content/component/paragraph/interfaces/video.py:53
 #: ./src/pyams_content/component/paragraph/interfaces/audio.py:49
 #: ./src/pyams_content/component/video/interfaces/__init__.py:53
 msgid "Name of document's author"
@@ -177,24 +177,26 @@
 msgstr ""
 
 #: ./src/pyams_content/component/gallery/interfaces/__init__.py:47
-#: ./src/pyams_content/component/illustration/interfaces/__init__.py:41
-#: ./src/pyams_content/component/illustration/interfaces/__init__.py:77
+#: ./src/pyams_content/component/illustration/interfaces/__init__.py:46
+#: ./src/pyams_content/component/illustration/interfaces/__init__.py:105
 msgid "Image or video data"
 msgstr ""
 
 #: ./src/pyams_content/component/gallery/interfaces/__init__.py:48
-#: ./src/pyams_content/component/illustration/interfaces/__init__.py:42
-#: ./src/pyams_content/component/illustration/interfaces/__init__.py:78
+#: ./src/pyams_content/component/illustration/interfaces/__init__.py:47
+#: ./src/pyams_content/component/illustration/interfaces/__init__.py:106
 msgid "Image or video content"
 msgstr ""
 
 #: ./src/pyams_content/component/gallery/interfaces/__init__.py:51
-#: ./src/pyams_content/component/illustration/interfaces/__init__.py:45
+#: ./src/pyams_content/component/illustration/interfaces/__init__.py:53
+#: ./src/pyams_content/component/paragraph/interfaces/video.py:45
+#: ./src/pyams_content/component/video/interfaces/__init__.py:75
 msgid "Legend"
 msgstr ""
 
 #: ./src/pyams_content/component/gallery/interfaces/__init__.py:54
-#: ./src/pyams_content/component/illustration/interfaces/__init__.py:48
+#: ./src/pyams_content/component/illustration/interfaces/__init__.py:56
 #: ./src/pyams_content/reference/pictograms/interfaces/__init__.py:48
 msgid "Accessibility title"
 msgstr ""
@@ -206,13 +208,13 @@
 #: ./src/pyams_content/component/gallery/interfaces/__init__.py:58
 #: ./src/pyams_content/component/gallery/interfaces/__init__.py:98
 #: ./src/pyams_content/component/extfile/interfaces/__init__.py:40
-#: ./src/pyams_content/component/illustration/interfaces/__init__.py:52
-#: ./src/pyams_content/component/paragraph/interfaces/video.py:44
+#: ./src/pyams_content/component/illustration/interfaces/__init__.py:64
+#: ./src/pyams_content/component/paragraph/interfaces/video.py:48
 #: ./src/pyams_content/component/paragraph/interfaces/audio.py:44
-#: ./src/pyams_content/component/links/interfaces/__init__.py:37
+#: ./src/pyams_content/component/links/interfaces/__init__.py:39
 #: ./src/pyams_content/component/video/interfaces/__init__.py:48
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:145
-#: ./src/pyams_content/shared/form/interfaces/__init__.py:65
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:154
+#: ./src/pyams_content/shared/form/interfaces/__init__.py:66
 msgid "Description"
 msgstr ""
 
@@ -263,17 +265,20 @@
 msgstr ""
 
 #: ./src/pyams_content/component/gallery/interfaces/__init__.py:94
-#: ./src/pyams_content/component/paragraph/zmi/milestone.py:246
-#: ./src/pyams_content/component/paragraph/zmi/container.py:224
+#: ./src/pyams_content/component/keynumber/portlet/interfaces/__init__.py:31
+#: ./src/pyams_content/component/paragraph/zmi/milestone.py:231
+#: ./src/pyams_content/component/paragraph/zmi/container.py:223
 #: ./src/pyams_content/component/paragraph/interfaces/milestone.py:45
-#: ./src/pyams_content/component/links/zmi/reverse.py:71
+#: ./src/pyams_content/component/links/zmi/reverse.py:73
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:109
 #: ./src/pyams_content/shared/common/zmi/templates/advanced-search.pt:188
 #: ./src/pyams_content/shared/imagemap/zmi/container.py:123
-#: ./src/pyams_content/shared/site/zmi/folder.py:68
+#: ./src/pyams_content/shared/site/zmi/folder.py:70
 #: ./src/pyams_content/root/zmi/templates/advanced-search.pt:188
-#: ./src/pyams_content/interfaces/__init__.py:99
-#: ./src/pyams_content/reference/pictograms/zmi/__init__.py:165
+#: ./src/pyams_content/interfaces/__init__.py:101
+#: ./src/pyams_content/reference/pictograms/zmi/__init__.py:150
+#: ./src/pyams_content/features/menu/portlet/navigation/interfaces/simple.py:31
+#: ./src/pyams_content/features/menu/portlet/navigation/interfaces/double.py:31
 msgid "Title"
 msgstr ""
 
@@ -325,59 +330,59 @@
 msgid "Add external file"
 msgstr ""
 
-#: ./src/pyams_content/component/extfile/zmi/__init__.py:101
+#: ./src/pyams_content/component/extfile/zmi/__init__.py:103
 msgid "Add new external file"
 msgstr ""
 
-#: ./src/pyams_content/component/extfile/zmi/__init__.py:141
+#: ./src/pyams_content/component/extfile/zmi/__init__.py:138
 msgid "Update file properties"
 msgstr ""
 
-#: ./src/pyams_content/component/extfile/zmi/__init__.py:181
+#: ./src/pyams_content/component/extfile/zmi/__init__.py:171
 msgid "Images"
 msgstr ""
 
-#: ./src/pyams_content/component/extfile/zmi/__init__.py:196
+#: ./src/pyams_content/component/extfile/zmi/__init__.py:186
 msgid "Add image"
 msgstr ""
 
-#: ./src/pyams_content/component/extfile/zmi/__init__.py:208
+#: ./src/pyams_content/component/extfile/zmi/__init__.py:200
 msgid "Add new image"
 msgstr ""
 
-#: ./src/pyams_content/component/extfile/zmi/__init__.py:244
+#: ./src/pyams_content/component/extfile/zmi/__init__.py:231
 msgid "Update image properties"
 msgstr ""
 
-#: ./src/pyams_content/component/extfile/zmi/__init__.py:283
+#: ./src/pyams_content/component/extfile/zmi/__init__.py:264
 msgid "Videos"
 msgstr ""
 
-#: ./src/pyams_content/component/extfile/zmi/__init__.py:298
+#: ./src/pyams_content/component/extfile/zmi/__init__.py:279
 msgid "Add video"
 msgstr ""
 
-#: ./src/pyams_content/component/extfile/zmi/__init__.py:310
+#: ./src/pyams_content/component/extfile/zmi/__init__.py:293
 msgid "Add new video"
 msgstr ""
 
-#: ./src/pyams_content/component/extfile/zmi/__init__.py:339
+#: ./src/pyams_content/component/extfile/zmi/__init__.py:317
 msgid "Update video properties"
 msgstr ""
 
-#: ./src/pyams_content/component/extfile/zmi/__init__.py:371
+#: ./src/pyams_content/component/extfile/zmi/__init__.py:343
 msgid "Audios files"
 msgstr ""
 
-#: ./src/pyams_content/component/extfile/zmi/__init__.py:386
+#: ./src/pyams_content/component/extfile/zmi/__init__.py:358
 msgid "Add audio file"
 msgstr ""
 
-#: ./src/pyams_content/component/extfile/zmi/__init__.py:398
+#: ./src/pyams_content/component/extfile/zmi/__init__.py:372
 msgid "Add new audio file"
 msgstr ""
 
-#: ./src/pyams_content/component/extfile/zmi/__init__.py:427
+#: ./src/pyams_content/component/extfile/zmi/__init__.py:396
 msgid "Update audio file properties"
 msgstr ""
 
@@ -386,7 +391,7 @@
 msgstr ""
 
 #: ./src/pyams_content/component/extfile/interfaces/__init__.py:36
-#: ./src/pyams_content/component/links/interfaces/__init__.py:33
+#: ./src/pyams_content/component/links/interfaces/__init__.py:35
 #: ./src/pyams_content/shared/imagemap/interfaces/__init__.py:54
 #: ./src/pyams_content/shared/site/interfaces/__init__.py:113
 msgid "Alternate title"
@@ -397,14 +402,14 @@
 msgstr ""
 
 #: ./src/pyams_content/component/extfile/interfaces/__init__.py:41
-#: ./src/pyams_content/component/paragraph/interfaces/video.py:45
+#: ./src/pyams_content/component/paragraph/interfaces/video.py:49
 #: ./src/pyams_content/component/paragraph/interfaces/audio.py:45
 #: ./src/pyams_content/component/video/interfaces/__init__.py:49
 msgid "File description displayed by front-office template"
 msgstr ""
 
 #: ./src/pyams_content/component/extfile/interfaces/__init__.py:48
-#: ./src/pyams_content/component/links/interfaces/__init__.py:59
+#: ./src/pyams_content/component/links/interfaces/__init__.py:68
 msgid "Language"
 msgstr ""
 
@@ -438,7 +443,7 @@
 msgstr ""
 
 #: ./src/pyams_content/component/extfile/interfaces/__init__.py:81
-#: ./src/pyams_content/component/paragraph/interfaces/video.py:52
+#: ./src/pyams_content/component/paragraph/interfaces/video.py:41
 msgid "Video data"
 msgstr ""
 
@@ -451,43 +456,155 @@
 msgid "Audio file content"
 msgstr ""
 
-#: ./src/pyams_content/component/illustration/__init__.py:132
-#: ./src/pyams_content/component/illustration/zmi/__init__.py:54
-#: ./src/pyams_content/component/illustration/zmi/__init__.py:81
-#: ./src/pyams_content/component/illustration/interfaces/__init__.py:71
+#: ./src/pyams_content/component/keynumber/__init__.py:189
+#: ./src/pyams_content/component/keynumber/zmi/__init__.py:198
+#: ./src/pyams_content/component/keynumber/portlet/zmi/__init__.py:79
+#: ./src/pyams_content/component/paragraph/interfaces/keynumber.py:29
+msgid "Key numbers"
+msgstr ""
+
+#. Default: Header
+#: ./src/pyams_content/component/keynumber/zmi/__init__.py:146
+#: ./src/pyams_content/component/keynumber/interfaces/__init__.py:44
+msgid "key-number-label"
+msgstr ""
+
+#: ./src/pyams_content/component/keynumber/zmi/__init__.py:158
+#: ./src/pyams_content/component/keynumber/interfaces/__init__.py:49
+msgid "Number"
+msgstr ""
+
+#. Default: Unit
+#: ./src/pyams_content/component/keynumber/zmi/__init__.py:167
+#: ./src/pyams_content/component/keynumber/interfaces/__init__.py:53
+msgid "key-number-unit"
+msgstr ""
+
+#: ./src/pyams_content/component/keynumber/zmi/__init__.py:179
+#: ./src/pyams_content/component/keynumber/interfaces/__init__.py:57
+#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:267
+#: ./src/pyams_content/component/paragraph/interfaces/pictogram.py:58
+msgid "Associated text"
+msgstr ""
+
+#: ./src/pyams_content/component/keynumber/zmi/__init__.py:217
+msgid "Add keynumber"
+msgstr ""
+
+#: ./src/pyams_content/component/keynumber/zmi/__init__.py:229
+msgid "Add new keynumber"
+msgstr ""
+
+#: ./src/pyams_content/component/keynumber/zmi/__init__.py:258
+msgid "Edit keynumber properties"
+msgstr ""
+
+#: ./src/pyams_content/component/keynumber/zmi/__init__.py:244
+msgid "Key number was correctly added"
+msgstr ""
+
+#: ./src/pyams_content/component/keynumber/portlet/__init__.py:71
+msgid "Key Numbers"
+msgstr ""
+
+#: ./src/pyams_content/component/keynumber/portlet/zmi/__init__.py:102
+#: ./src/pyams_content/component/keynumber/portlet/zmi/templates/keynumber-preview.pt:22
+msgid "Associated links"
+msgstr ""
+
+#: ./src/pyams_content/component/keynumber/portlet/zmi/templates/keynumber-preview.pt:27
+#: ./src/pyams_content/features/menu/portlet/navigation/zmi/templates/double-preview.pt:12
+#: ./src/pyams_content/features/menu/portlet/navigation/zmi/templates/simple-preview.pt:9
+msgid "Link target is not published!"
+msgstr ""
+
+#: ./src/pyams_content/component/keynumber/portlet/interfaces/__init__.py:32
+msgid "Portlet title"
+msgstr ""
+
+#: ./src/pyams_content/component/keynumber/portlet/interfaces/__init__.py:35
+msgid "Teaser"
+msgstr ""
+
+#: ./src/pyams_content/component/keynumber/portlet/interfaces/__init__.py:36
+msgid "Short text displayed above key numbers"
+msgstr ""
+
+#: ./src/pyams_content/component/keynumber/interfaces/__init__.py:39
+#: ./src/pyams_content/component/paragraph/interfaces/milestone.py:40
+#: ./src/pyams_content/component/paragraph/interfaces/__init__.py:44
+#: ./src/pyams_content/component/paragraph/interfaces/pictogram.py:41
+#: ./src/pyams_content/component/association/interfaces/__init__.py:42
+#: ./src/pyams_content/shared/form/interfaces/__init__.py:87
+#: ./src/pyams_content/shared/site/interfaces/__init__.py:117
+#: ./src/pyams_content/features/alert/interfaces.py:54
+#: ./src/pyams_content/features/menu/interfaces/__init__.py:58
+msgid "Visible?"
+msgstr ""
+
+#: ./src/pyams_content/component/keynumber/interfaces/__init__.py:40
+msgid "Is this key number visible in front-office?"
+msgstr ""
+
+#: ./src/pyams_content/component/keynumber/interfaces/__init__.py:45
+msgid ""
+"Small text to be displayed above number (according to selected renderer)"
+msgstr ""
+
+#: ./src/pyams_content/component/keynumber/interfaces/__init__.py:50
+msgid "Key number value"
+msgstr ""
+
+#: ./src/pyams_content/component/keynumber/interfaces/__init__.py:54
+msgid "Displayed unit"
+msgstr ""
+
+#: ./src/pyams_content/component/keynumber/interfaces/__init__.py:58
+msgid "The way this text will be rendered depends on presentation template"
+msgstr ""
+
+#: ./src/pyams_content/component/illustration/__init__.py:177
+#: ./src/pyams_content/component/illustration/zmi/paragraph.py:168
+#: ./src/pyams_content/component/illustration/zmi/__init__.py:56
+#: ./src/pyams_content/component/illustration/zmi/__init__.py:100
+#: ./src/pyams_content/component/illustration/interfaces/__init__.py:99
 msgid "Illustration"
 msgstr ""
 
-#: ./src/pyams_content/component/illustration/zmi/paragraph.py:57
+#: ./src/pyams_content/component/illustration/zmi/paragraph.py:60
 msgid "Illustration..."
 msgstr ""
 
-#: ./src/pyams_content/component/illustration/zmi/paragraph.py:68
+#: ./src/pyams_content/component/illustration/zmi/paragraph.py:73
 msgid "Add new illustration"
 msgstr ""
 
-#: ./src/pyams_content/component/illustration/zmi/paragraph.py:102
+#: ./src/pyams_content/component/illustration/zmi/paragraph.py:103
 msgid "Edit illustration properties"
 msgstr ""
 
-#: ./src/pyams_content/component/illustration/zmi/__init__.py:83
+#: ./src/pyams_content/component/illustration/zmi/__init__.py:155
+msgid "Navigation link illustration"
+msgstr ""
+
+#: ./src/pyams_content/component/illustration/zmi/__init__.py:102
 msgid "Header illustration"
 msgstr ""
 
-#: ./src/pyams_content/component/illustration/interfaces/__init__.py:49
-#: ./src/pyams_content/reference/pictograms/interfaces/__init__.py:49
-msgid "Alternate title used to describe image content"
-msgstr ""
-
 #: ./src/pyams_content/component/illustration/interfaces/__init__.py:57
+#: ./src/pyams_content/reference/pictograms/interfaces/__init__.py:49
+msgid "Alternate title used to describe image content"
+msgstr ""
+
+#: ./src/pyams_content/component/illustration/interfaces/__init__.py:69
 msgid "Name of picture's author"
 msgstr ""
 
-#: ./src/pyams_content/component/illustration/interfaces/__init__.py:60
+#: ./src/pyams_content/component/illustration/interfaces/__init__.py:72
 msgid "Illustration template"
 msgstr ""
 
-#: ./src/pyams_content/component/illustration/interfaces/__init__.py:61
+#: ./src/pyams_content/component/illustration/interfaces/__init__.py:73
 msgid "Presentation template used for illustration"
 msgstr ""
 
@@ -507,7 +624,7 @@
 msgid "no visible paragraph"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/pictogram.py:134
+#: ./src/pyams_content/component/paragraph/pictogram.py:135
 msgid "Selected pictogram is missing"
 msgstr ""
 
@@ -515,62 +632,62 @@
 msgid "Milestones..."
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/milestone.py:88
+#: ./src/pyams_content/component/paragraph/zmi/milestone.py:90
 msgid "Add new milestone paragraph"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/milestone.py:120
+#: ./src/pyams_content/component/paragraph/zmi/milestone.py:117
 msgid "Edit milestone paragraph properties"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/milestone.py:258
+#: ./src/pyams_content/component/paragraph/zmi/milestone.py:243
 #: ./src/pyams_content/component/paragraph/interfaces/milestone.py:49
 msgid "Associated label"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/milestone.py:270
+#: ./src/pyams_content/component/paragraph/zmi/milestone.py:255
 #: ./src/pyams_content/component/paragraph/interfaces/milestone.py:53
 msgid "Anchor"
 msgstr ""
 
+#: ./src/pyams_content/component/paragraph/zmi/milestone.py:284
+#: ./src/pyams_content/component/paragraph/interfaces/milestone.py:76
+msgid "Milestones"
+msgstr ""
+
 #: ./src/pyams_content/component/paragraph/zmi/milestone.py:299
-#: ./src/pyams_content/component/paragraph/interfaces/milestone.py:76
-msgid "Milestones"
-msgstr ""
-
-#: ./src/pyams_content/component/paragraph/zmi/milestone.py:314
 msgid "Add milestone"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/milestone.py:325
+#: ./src/pyams_content/component/paragraph/zmi/milestone.py:312
 msgid "Add new milestone"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/milestone.py:358
+#: ./src/pyams_content/component/paragraph/zmi/milestone.py:339
 msgid "Edit milestone properties"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/milestone.py:347
+#: ./src/pyams_content/component/paragraph/zmi/milestone.py:327
 msgid "Milestone was correctly added"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/milestone.py:285
+#: ./src/pyams_content/component/paragraph/zmi/milestone.py:270
 msgid "(missing paragraph)"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/keypoint.py:50
+#: ./src/pyams_content/component/paragraph/zmi/keypoint.py:51
 msgid "Key points..."
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/keypoint.py:61
+#: ./src/pyams_content/component/paragraph/zmi/keypoint.py:64
 msgid "Add new key points paragraph"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/keypoint.py:93
+#: ./src/pyams_content/component/paragraph/zmi/keypoint.py:91
 msgid "Edit key points paragraph properties"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/__init__.py:66
+#: ./src/pyams_content/component/paragraph/zmi/__init__.py:65
 msgid "Content block types..."
 msgstr ""
 
@@ -578,7 +695,7 @@
 msgid "Content block types"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/__init__.py:96
+#: ./src/pyams_content/component/paragraph/zmi/__init__.py:89
 msgid ""
 "You can define which types of paragraphs are allowed in this container.\n"
 "\n"
@@ -587,228 +704,174 @@
 "NOTICE: removing types from allowed types list will have no effect on already created contents!"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/__init__.py:214
+#: ./src/pyams_content/component/paragraph/zmi/__init__.py:207
 #: ./src/pyams_content/shared/common/zmi/templates/preview-input.pt:39
 #: ./src/pyams_content/features/preview/zmi/__init__.py:45
 msgid "Preview"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/__init__.py:219
-#: ./src/pyams_content/shared/common/zmi/workflow.py:125
-#: ./src/pyams_content/shared/common/zmi/workflow.py:217
-#: ./src/pyams_content/shared/common/zmi/workflow.py:262
-#: ./src/pyams_content/shared/common/zmi/workflow.py:321
-#: ./src/pyams_content/shared/common/zmi/workflow.py:415
-#: ./src/pyams_content/shared/common/zmi/workflow.py:476
-#: ./src/pyams_content/shared/common/zmi/workflow.py:521
-#: ./src/pyams_content/shared/common/zmi/workflow.py:567
-#: ./src/pyams_content/shared/common/zmi/workflow.py:615
-#: ./src/pyams_content/shared/common/zmi/workflow.py:660
-#: ./src/pyams_content/shared/common/zmi/workflow.py:706
-#: ./src/pyams_content/shared/common/zmi/workflow.py:762
-#: ./src/pyams_content/shared/common/zmi/__init__.py:276
-#: ./src/pyams_content/shared/common/zmi/owner.py:74
+#: ./src/pyams_content/component/paragraph/zmi/__init__.py:212
+#: ./src/pyams_content/shared/common/zmi/workflow.py:122
+#: ./src/pyams_content/shared/common/zmi/workflow.py:209
+#: ./src/pyams_content/shared/common/zmi/workflow.py:249
+#: ./src/pyams_content/shared/common/zmi/workflow.py:303
+#: ./src/pyams_content/shared/common/zmi/workflow.py:392
+#: ./src/pyams_content/shared/common/zmi/workflow.py:448
+#: ./src/pyams_content/shared/common/zmi/workflow.py:488
+#: ./src/pyams_content/shared/common/zmi/workflow.py:529
+#: ./src/pyams_content/shared/common/zmi/workflow.py:572
+#: ./src/pyams_content/shared/common/zmi/workflow.py:612
+#: ./src/pyams_content/shared/common/zmi/workflow.py:653
+#: ./src/pyams_content/shared/common/zmi/workflow.py:704
+#: ./src/pyams_content/shared/common/zmi/__init__.py:275
+#: ./src/pyams_content/shared/common/zmi/owner.py:73
 #: ./src/pyams_content/features/review/zmi/__init__.py:90
 msgid "Cancel"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/__init__.py:221
+#: ./src/pyams_content/component/paragraph/zmi/__init__.py:214
 msgid "Submit"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/__init__.py:202
+#: ./src/pyams_content/component/paragraph/zmi/__init__.py:195
 msgid "Paragraph was correctly added."
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/video.py:54
+#: ./src/pyams_content/component/paragraph/zmi/video.py:53
 msgid "Video paragraph..."
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/video.py:65
+#: ./src/pyams_content/component/paragraph/zmi/video.py:66
 msgid "Add new video paragraph"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/video.py:112
-#: ./src/pyams_content/component/video/zmi/paragraph.py:209
+#: ./src/pyams_content/component/paragraph/zmi/video.py:94
+#: ./src/pyams_content/component/video/zmi/paragraph.py:192
 msgid "Edit video properties"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/video.py:83
-#: ./src/pyams_content/component/paragraph/zmi/video.py:133
-#: ./src/pyams_content/component/paragraph/zmi/audio.py:83
-#: ./src/pyams_content/component/paragraph/zmi/audio.py:133
-#: ./src/pyams_content/component/video/zmi/paragraph.py:103
-#: ./src/pyams_content/component/video/zmi/paragraph.py:238
-msgid "HTML content"
-msgstr ""
-
 #: ./src/pyams_content/component/paragraph/zmi/container.py:74
 msgid "Contents..."
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/container.py:240
+#: ./src/pyams_content/component/paragraph/zmi/container.py:239
 msgid "Show/hide all paragraphs"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/container.py:288
-#: ./src/pyams_content/component/paragraph/zmi/container.py:297
-#: ./src/pyams_content/component/paragraph/zmi/container.py:310
+#: ./src/pyams_content/component/paragraph/zmi/container.py:287
+#: ./src/pyams_content/component/paragraph/zmi/container.py:296
+#: ./src/pyams_content/component/paragraph/zmi/container.py:309
 msgid "Content blocks"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/container.py:367
+#: ./src/pyams_content/component/paragraph/zmi/container.py:366
 msgid "Links and attachments..."
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/container.py:379
+#: ./src/pyams_content/component/paragraph/zmi/container.py:378
 msgid "Content blocks links and attachments"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/container.py:127
+#: ./src/pyams_content/component/paragraph/zmi/container.py:126
 msgid "No currently defined paragraph."
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/container.py:249
+#: ./src/pyams_content/component/paragraph/zmi/container.py:248
 msgid "Click to open/close all paragraphs editors"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/container.py:262
+#: ./src/pyams_content/component/paragraph/zmi/container.py:261
 msgid "Click to open/close paragraph editor"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/container.py:133
+#: ./src/pyams_content/component/paragraph/zmi/container.py:132
 msgid "Check allowed paragraph types to be able to create new paragraphs."
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:83
+#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:82
 msgid "Pictograms..."
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:94
+#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:95
 msgid "Add new pictogram paragraph"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:126
+#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:122
 msgid "Edit pictogram paragraph properties"
 msgstr ""
 
 #. Default: Header
-#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:268
+#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:252
 msgid "pictogram-item-header"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:283
-#: ./src/pyams_content/component/paragraph/zmi/keynumber.py:263
-#: ./src/pyams_content/component/paragraph/interfaces/pictogram.py:58
-#: ./src/pyams_content/component/paragraph/interfaces/keynumber.py:54
-msgid "Associated text"
-msgstr ""
-
-#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:305
+#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:289
 #: ./src/pyams_content/component/paragraph/interfaces/pictogram.py:80
 msgid "Pictograms"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:320
-#: ./src/pyams_content/reference/pictograms/zmi/__init__.py:62
+#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:304
+#: ./src/pyams_content/reference/pictograms/zmi/__init__.py:59
 msgid "Add pictogram"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:331
-#: ./src/pyams_content/reference/pictograms/zmi/__init__.py:73
+#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:317
+#: ./src/pyams_content/reference/pictograms/zmi/__init__.py:71
 msgid "Add new pictogram"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:393
-#: ./src/pyams_content/reference/pictograms/zmi/__init__.py:103
+#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:359
+#: ./src/pyams_content/reference/pictograms/zmi/__init__.py:95
 msgid "Edit pictogram properties"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:374
+#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:339
 msgid "Pictogram was correctly added"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:384
-#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:448
+#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:349
+#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:387
 msgid "You must select a pictogram!"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:347
-#: ./src/pyams_content/reference/pictograms/zmi/__init__.py:186
-msgid "Default header: --"
-msgstr ""
-
-#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:414
-#: ./src/pyams_content/reference/pictograms/zmi/__init__.py:190
-#, python-format
-msgid "Default header: {0}"
-msgstr ""
-
 #: ./src/pyams_content/component/paragraph/zmi/audio.py:54
 msgid "Audio paragraph..."
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/audio.py:65
+#: ./src/pyams_content/component/paragraph/zmi/audio.py:67
 msgid "Add new audio paragraph"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/audio.py:112
+#: ./src/pyams_content/component/paragraph/zmi/audio.py:108
 msgid "Edit audio properties"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/keynumber.py:76
+#: ./src/pyams_content/component/paragraph/zmi/audio.py:84
+#: ./src/pyams_content/component/paragraph/zmi/audio.py:128
+msgid "HTML content"
+msgstr ""
+
+#: ./src/pyams_content/component/paragraph/zmi/keynumber.py:55
 msgid "Key numbers..."
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/keynumber.py:87
+#: ./src/pyams_content/component/paragraph/zmi/keynumber.py:68
 msgid "Add new key number paragraph"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/keynumber.py:119
+#: ./src/pyams_content/component/paragraph/zmi/keynumber.py:96
 msgid "Edit key number paragraph properties"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/keynumber.py:242
-#: ./src/pyams_content/component/paragraph/interfaces/keynumber.py:45
-msgid "Number"
-msgstr ""
-
-#. Default: Header
-#: ./src/pyams_content/component/paragraph/zmi/keynumber.py:251
-#: ./src/pyams_content/component/paragraph/interfaces/keynumber.py:49
-msgid "key-number-label"
-msgstr ""
-
-#: ./src/pyams_content/component/paragraph/zmi/keynumber.py:282
-#: ./src/pyams_content/component/paragraph/interfaces/keynumber.py:76
-msgid "Key numbers"
-msgstr ""
-
-#: ./src/pyams_content/component/paragraph/zmi/keynumber.py:297
-msgid "Add keynumber"
-msgstr ""
-
-#: ./src/pyams_content/component/paragraph/zmi/keynumber.py:308
-msgid "Add new keynumber"
-msgstr ""
-
-#: ./src/pyams_content/component/paragraph/zmi/keynumber.py:341
-msgid "Edit keynumber properties"
-msgstr ""
-
-#: ./src/pyams_content/component/paragraph/zmi/keynumber.py:330
-msgid "Key number was correctly added"
-msgstr ""
-
 #: ./src/pyams_content/component/paragraph/zmi/frame.py:84
 msgid "Framed text..."
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/frame.py:96
+#: ./src/pyams_content/component/paragraph/zmi/frame.py:98
 msgid "Add new framed text paragraph"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/frame.py:132
+#: ./src/pyams_content/component/paragraph/zmi/frame.py:129
 msgid "Edit framed text paragraph properties"
 msgstr ""
 
@@ -816,11 +879,11 @@
 msgid "Verbatim..."
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/verbatim.py:67
+#: ./src/pyams_content/component/paragraph/zmi/verbatim.py:69
 msgid "Add new verbatim paragraph"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/verbatim.py:99
+#: ./src/pyams_content/component/paragraph/zmi/verbatim.py:96
 msgid "Edit verbatim paragraph properties"
 msgstr ""
 
@@ -828,23 +891,23 @@
 msgid "Raw HTML..."
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/html.py:87
+#: ./src/pyams_content/component/paragraph/zmi/html.py:89
 msgid "Add new raw HTML paragraph"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/html.py:122
+#: ./src/pyams_content/component/paragraph/zmi/html.py:119
 msgid "Edit raw HTML paragraph properties"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/html.py:174
+#: ./src/pyams_content/component/paragraph/zmi/html.py:159
 msgid "Rich text..."
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/html.py:185
+#: ./src/pyams_content/component/paragraph/zmi/html.py:172
 msgid "Add new rich text paragraph"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/html.py:220
+#: ./src/pyams_content/component/paragraph/zmi/html.py:202
 msgid "Edit rich text paragraph properties"
 msgstr ""
 
@@ -852,11 +915,11 @@
 msgid "Contact card..."
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/contact.py:64
+#: ./src/pyams_content/component/paragraph/zmi/contact.py:66
 msgid "Add new contact card"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/contact.py:97
+#: ./src/pyams_content/component/paragraph/zmi/contact.py:94
 msgid "Edit contact card properties"
 msgstr ""
 
@@ -864,25 +927,14 @@
 msgid "Header..."
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/header.py:61
+#: ./src/pyams_content/component/paragraph/zmi/header.py:63
 msgid "Add new header paragraph"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/header.py:93
+#: ./src/pyams_content/component/paragraph/zmi/header.py:90
 msgid "Edit header paragraph properties"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/interfaces/milestone.py:40
-#: ./src/pyams_content/component/paragraph/interfaces/__init__.py:44
-#: ./src/pyams_content/component/paragraph/interfaces/pictogram.py:41
-#: ./src/pyams_content/component/paragraph/interfaces/keynumber.py:40
-#: ./src/pyams_content/component/association/interfaces/__init__.py:42
-#: ./src/pyams_content/shared/form/interfaces/__init__.py:86
-#: ./src/pyams_content/shared/site/interfaces/__init__.py:117
-#: ./src/pyams_content/features/alert/interfaces.py:54
-msgid "Visible?"
-msgstr ""
-
 #: ./src/pyams_content/component/paragraph/interfaces/milestone.py:41
 msgid "Is this milestone visible in front-office?"
 msgstr ""
@@ -947,8 +999,8 @@
 msgstr ""
 
 #: ./src/pyams_content/component/paragraph/interfaces/__init__.py:85
-#: ./src/pyams_content/shared/common/zmi/types.py:167
-#: ./src/pyams_content/shared/common/zmi/types.py:395
+#: ./src/pyams_content/shared/common/zmi/types.py:169
+#: ./src/pyams_content/shared/common/zmi/types.py:380
 msgid "Default paragraphs"
 msgstr ""
 
@@ -956,14 +1008,7 @@
 msgid "List of paragraphs automatically added to a new content"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/interfaces/video.py:41
-#: ./src/pyams_content/component/paragraph/interfaces/audio.py:41
-#: ./src/pyams_content/component/paragraph/interfaces/html.py:63
-#: ./src/pyams_content/component/video/interfaces/__init__.py:75
-msgid "Body"
-msgstr ""
-
-#: ./src/pyams_content/component/paragraph/interfaces/video.py:53
+#: ./src/pyams_content/component/paragraph/interfaces/video.py:42
 msgid "Video file content"
 msgstr ""
 
@@ -982,7 +1027,8 @@
 msgstr ""
 
 #: ./src/pyams_content/component/paragraph/interfaces/pictogram.py:46
-#: ./src/pyams_content/shared/common/interfaces/types.py:67
+#: ./src/pyams_content/component/links/interfaces/__init__.py:43
+#: ./src/pyams_content/shared/common/interfaces/types.py:68
 #: ./src/pyams_content/features/alert/interfaces.py:79
 msgid "Pictogram"
 msgstr ""
@@ -1018,6 +1064,11 @@
 msgid "Audio"
 msgstr ""
 
+#: ./src/pyams_content/component/paragraph/interfaces/audio.py:41
+#: ./src/pyams_content/component/paragraph/interfaces/html.py:63
+msgid "Body"
+msgstr ""
+
 #: ./src/pyams_content/component/paragraph/interfaces/audio.py:56
 msgid "Audio template"
 msgstr ""
@@ -1026,28 +1077,11 @@
 msgid "Presentation template used for this audio file"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/interfaces/keynumber.py:41
-msgid "Is this key number visible in front-office?"
-msgstr ""
-
-#: ./src/pyams_content/component/paragraph/interfaces/keynumber.py:46
-msgid "Key number value"
-msgstr ""
-
-#: ./src/pyams_content/component/paragraph/interfaces/keynumber.py:50
-msgid ""
-"Small text to be displayed above number (according to selected renderer)"
-msgstr ""
-
-#: ./src/pyams_content/component/paragraph/interfaces/keynumber.py:55
-msgid "The way this text will be rendered depends on presentation template"
-msgstr ""
-
-#: ./src/pyams_content/component/paragraph/interfaces/keynumber.py:83
+#: ./src/pyams_content/component/paragraph/interfaces/keynumber.py:36
 msgid "Key numbers template"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/interfaces/keynumber.py:84
+#: ./src/pyams_content/component/paragraph/interfaces/keynumber.py:37
 msgid "Presentation template used for key numbers"
 msgstr ""
 
@@ -1203,8 +1237,8 @@
 msgid "no defined theme"
 msgstr ""
 
-#: ./src/pyams_content/component/theme/zmi/__init__.py:52
-#: ./src/pyams_content/shared/view/zmi/theme.py:49
+#: ./src/pyams_content/component/theme/zmi/__init__.py:51
+#: ./src/pyams_content/shared/view/zmi/theme.py:48
 msgid "Themes..."
 msgstr ""
 
@@ -1212,7 +1246,7 @@
 msgid "Content themes"
 msgstr ""
 
-#: ./src/pyams_content/component/theme/zmi/manager.py:46
+#: ./src/pyams_content/component/theme/zmi/manager.py:45
 msgid "Themes settings..."
 msgstr ""
 
@@ -1222,20 +1256,20 @@
 
 #: ./src/pyams_content/component/association/container.py:88
 #: ./src/pyams_content/component/association/zmi/__init__.py:296
-#: ./src/pyams_content/component/association/interfaces/__init__.py:86
+#: ./src/pyams_content/component/association/interfaces/__init__.py:90
 msgid "Associations"
 msgstr ""
 
 #: ./src/pyams_content/component/association/zmi/paragraph.py:54
-#: ./src/pyams_content/component/association/zmi/__init__.py:95
+#: ./src/pyams_content/component/association/zmi/__init__.py:96
 msgid "Associations..."
 msgstr ""
 
-#: ./src/pyams_content/component/association/zmi/paragraph.py:65
+#: ./src/pyams_content/component/association/zmi/paragraph.py:67
 msgid "Add new association paragraph"
 msgstr ""
 
-#: ./src/pyams_content/component/association/zmi/paragraph.py:97
+#: ./src/pyams_content/component/association/zmi/paragraph.py:93
 msgid "Edit association paragraph properties"
 msgstr ""
 
@@ -1256,129 +1290,134 @@
 msgid "Associations list"
 msgstr ""
 
-#: ./src/pyams_content/component/association/zmi/__init__.py:65
+#: ./src/pyams_content/component/association/zmi/__init__.py:66
 msgid "Association was correctly added."
 msgstr ""
 
 #: ./src/pyams_content/component/association/interfaces/__init__.py:43
+#: ./src/pyams_content/features/menu/interfaces/__init__.py:59
 msgid "Is this item visible in front-office?"
 msgstr ""
 
-#: ./src/pyams_content/component/association/interfaces/__init__.py:93
+#: ./src/pyams_content/component/association/interfaces/__init__.py:97
 msgid "Associations template"
 msgstr ""
 
-#: ./src/pyams_content/component/association/interfaces/__init__.py:94
+#: ./src/pyams_content/component/association/interfaces/__init__.py:98
 msgid "Presentation template used for associations"
 msgstr ""
 
-#: ./src/pyams_content/component/links/__init__.py:104
+#: ./src/pyams_content/component/links/__init__.py:125
 msgid "Internal link"
 msgstr ""
 
-#: ./src/pyams_content/component/links/__init__.py:186
+#: ./src/pyams_content/component/links/__init__.py:207
 msgid "External link"
 msgstr ""
 
-#: ./src/pyams_content/component/links/__init__.py:239
+#: ./src/pyams_content/component/links/__init__.py:260
 msgid "Mailto link"
 msgstr ""
 
-#: ./src/pyams_content/component/links/__init__.py:173
+#: ./src/pyams_content/component/links/__init__.py:194
 msgid "target is not published"
 msgstr ""
 
-#: ./src/pyams_content/component/links/zmi/__init__.py:57
+#: ./src/pyams_content/component/links/zmi/__init__.py:60
 msgid "Internal links"
 msgstr ""
 
-#: ./src/pyams_content/component/links/zmi/__init__.py:72
+#: ./src/pyams_content/component/links/zmi/__init__.py:75
 msgid "Add internal link"
 msgstr ""
 
-#: ./src/pyams_content/component/links/zmi/__init__.py:84
+#: ./src/pyams_content/component/links/zmi/__init__.py:89
 msgid "Add new internal link"
 msgstr ""
 
-#: ./src/pyams_content/component/links/zmi/__init__.py:122
+#: ./src/pyams_content/component/links/zmi/__init__.py:125
 msgid "Edit internal link properties"
 msgstr ""
 
-#: ./src/pyams_content/component/links/zmi/__init__.py:160
+#: ./src/pyams_content/component/links/zmi/__init__.py:159
 msgid "External links"
 msgstr ""
 
-#: ./src/pyams_content/component/links/zmi/__init__.py:175
+#: ./src/pyams_content/component/links/zmi/__init__.py:174
 msgid "Add external link"
 msgstr ""
 
-#: ./src/pyams_content/component/links/zmi/__init__.py:187
+#: ./src/pyams_content/component/links/zmi/__init__.py:188
 msgid "Add new external link"
 msgstr ""
 
-#: ./src/pyams_content/component/links/zmi/__init__.py:225
+#: ./src/pyams_content/component/links/zmi/__init__.py:224
 msgid "Edit external link properties"
 msgstr ""
 
-#: ./src/pyams_content/component/links/zmi/__init__.py:263
+#: ./src/pyams_content/component/links/zmi/__init__.py:258
 msgid "Mailto links"
 msgstr ""
 
-#: ./src/pyams_content/component/links/zmi/__init__.py:278
+#: ./src/pyams_content/component/links/zmi/__init__.py:273
 msgid "Add mailto link"
 msgstr ""
 
-#: ./src/pyams_content/component/links/zmi/__init__.py:290
+#: ./src/pyams_content/component/links/zmi/__init__.py:287
 msgid "Add new mailto link"
 msgstr ""
 
-#: ./src/pyams_content/component/links/zmi/__init__.py:328
+#: ./src/pyams_content/component/links/zmi/__init__.py:323
 msgid "Edit mailto link properties"
 msgstr ""
 
-#: ./src/pyams_content/component/links/zmi/reverse.py:55
+#: ./src/pyams_content/component/links/zmi/reverse.py:57
 msgid "Reverse links"
 msgstr ""
 
-#: ./src/pyams_content/component/links/zmi/reverse.py:64
+#: ./src/pyams_content/component/links/zmi/reverse.py:66
 msgid "Content's internal links"
 msgstr ""
 
-#: ./src/pyams_content/component/links/interfaces/__init__.py:34
+#: ./src/pyams_content/component/links/interfaces/__init__.py:36
 msgid "Link title, as shown in front-office"
 msgstr ""
 
-#: ./src/pyams_content/component/links/interfaces/__init__.py:38
+#: ./src/pyams_content/component/links/interfaces/__init__.py:40
 msgid "Link description displayed by front-office template"
 msgstr ""
 
-#: ./src/pyams_content/component/links/interfaces/__init__.py:55
+#: ./src/pyams_content/component/links/interfaces/__init__.py:44
+msgid "Name of the pictogram associated with this link"
+msgstr ""
+
+#: ./src/pyams_content/component/links/interfaces/__init__.py:64
 #: ./src/pyams_content/shared/logo/interfaces/__init__.py:50
 msgid "Target URL"
 msgstr ""
 
-#: ./src/pyams_content/component/links/interfaces/__init__.py:56
+#: ./src/pyams_content/component/links/interfaces/__init__.py:65
 #: ./src/pyams_content/shared/logo/interfaces/__init__.py:51
 msgid "URL used to access external resource"
 msgstr ""
 
-#: ./src/pyams_content/component/links/interfaces/__init__.py:60
-msgid "Language used in this remote resource"
-msgstr ""
-
-#: ./src/pyams_content/component/links/interfaces/__init__.py:68
-msgid "Target address"
-msgstr ""
-
 #: ./src/pyams_content/component/links/interfaces/__init__.py:69
+msgid "Language used in this remote resource"
+msgstr ""
+
+#: ./src/pyams_content/component/links/interfaces/__init__.py:77
+msgid "Target address"
+msgstr ""
+
+#: ./src/pyams_content/component/links/interfaces/__init__.py:78
 msgid "Target email address"
 msgstr ""
 
-#: ./src/pyams_content/component/links/interfaces/__init__.py:72
+#: ./src/pyams_content/component/links/interfaces/__init__.py:81
 msgid "Address name"
 msgstr ""
 
-#: ./src/pyams_content/component/links/interfaces/__init__.py:73
+#: ./src/pyams_content/component/links/interfaces/__init__.py:82
 msgid "Address as displayed in address book"
 msgstr ""
 
@@ -1623,24 +1662,24 @@
 msgid "Youtube settings"
 msgstr ""
 
-#: ./src/pyams_content/component/video/zmi/paragraph.py:61
+#: ./src/pyams_content/component/video/zmi/paragraph.py:62
 msgid "External video..."
 msgstr ""
 
-#: ./src/pyams_content/component/video/zmi/paragraph.py:72
+#: ./src/pyams_content/component/video/zmi/paragraph.py:75
 msgid "Add new external video..."
 msgstr ""
 
-#: ./src/pyams_content/component/video/zmi/paragraph.py:145
+#: ./src/pyams_content/component/video/zmi/paragraph.py:126
 msgid "Video provider is required"
 msgstr ""
 
-#: ./src/pyams_content/component/video/zmi/paragraph.py:194
-#: ./src/pyams_content/component/video/zmi/paragraph.py:259
+#: ./src/pyams_content/component/video/zmi/paragraph.py:175
+#: ./src/pyams_content/component/video/zmi/paragraph.py:229
 msgid "Video provider settings"
 msgstr ""
 
-#: ./src/pyams_content/component/video/zmi/paragraph.py:174
+#: ./src/pyams_content/component/video/zmi/paragraph.py:155
 msgid "Other settings"
 msgstr ""
 
@@ -1652,19 +1691,19 @@
 msgid "Name of external platform providing selected video"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/__init__.py:237
-#: ./src/pyams_content/shared/common/zmi/properties.py:72
-#: ./src/pyams_content/shared/common/zmi/manager.py:97
+#: ./src/pyams_content/shared/common/__init__.py:240
+#: ./src/pyams_content/shared/common/zmi/properties.py:70
+#: ./src/pyams_content/shared/common/zmi/manager.py:96
 msgid "Properties"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/__init__.py:147
-#: ./src/pyams_content/shared/common/__init__.py:155
+#: ./src/pyams_content/shared/common/__init__.py:150
+#: ./src/pyams_content/shared/common/__init__.py:158
 #, python-format
 msgid "{date} by {principal}"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/__init__.py:260
+#: ./src/pyams_content/shared/common/__init__.py:263
 #, python-format
 msgid "title length should be between 40 and 66 characters ({length} actually)"
 msgstr ""
@@ -1717,11 +1756,11 @@
 msgid "Modified before..."
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/properties.py:62
+#: ./src/pyams_content/shared/common/zmi/properties.py:60
 msgid "Composition"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/properties.py:83
+#: ./src/pyams_content/shared/common/zmi/properties.py:82
 msgid "Content properties"
 msgstr ""
 
@@ -1733,40 +1772,40 @@
 msgid "Data type label"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/types.py:183
-#: ./src/pyams_content/shared/common/zmi/types.py:411
+#: ./src/pyams_content/shared/common/zmi/types.py:185
+#: ./src/pyams_content/shared/common/zmi/types.py:396
 msgid "Default associations"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/types.py:211
+#: ./src/pyams_content/shared/common/zmi/types.py:213
 msgid "Content data types"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/types.py:234
+#: ./src/pyams_content/shared/common/zmi/types.py:236
 msgid "Add data type"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/types.py:245
+#: ./src/pyams_content/shared/common/zmi/types.py:248
 msgid "Add new data type"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/types.py:291
+#: ./src/pyams_content/shared/common/zmi/types.py:285
 msgid "Data type properties"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/types.py:374
+#: ./src/pyams_content/shared/common/zmi/types.py:359
 msgid "Subtype label"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/types.py:455
+#: ./src/pyams_content/shared/common/zmi/types.py:440
 msgid "Add subtype"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/types.py:466
+#: ./src/pyams_content/shared/common/zmi/types.py:452
 msgid "Add new subtype"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/types.py:518
+#: ./src/pyams_content/shared/common/zmi/types.py:498
 msgid "Data subtype properties"
 msgstr ""
 
@@ -1774,70 +1813,70 @@
 msgid "No currently defined data type."
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/types.py:273
+#: ./src/pyams_content/shared/common/zmi/types.py:275
 msgid "Specified type name is already used!"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/types.py:494
-msgid "Specified subtype name is already used!"
-msgstr ""
-
-#: ./src/pyams_content/shared/common/zmi/types.py:505
+#: ./src/pyams_content/shared/common/zmi/types.py:474
 msgid "Subtype was correctly added."
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/types.py:155
+#: ./src/pyams_content/shared/common/zmi/types.py:488
+msgid "Specified subtype name is already used!"
+msgstr ""
+
+#: ./src/pyams_content/shared/common/zmi/types.py:157
 msgid "Click to see subtypes"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/workflow.py:907
+#: ./src/pyams_content/shared/common/zmi/workflow.py:844
 msgid "Prior checks"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/workflow.py:126
+#: ./src/pyams_content/shared/common/zmi/workflow.py:123
 msgid "Request publication"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/workflow.py:218
+#: ./src/pyams_content/shared/common/zmi/workflow.py:210
 #: ./src/pyams_content/workflow/__init__.py:315
 msgid "Cancel publication request"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/workflow.py:263
+#: ./src/pyams_content/shared/common/zmi/workflow.py:250
 msgid "Refuse publication request"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/workflow.py:322
+#: ./src/pyams_content/shared/common/zmi/workflow.py:304
 #: ./src/pyams_content/workflow/basic.py:196
 msgid "Publish"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/workflow.py:416
+#: ./src/pyams_content/shared/common/zmi/workflow.py:393
 msgid "Request retire"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/workflow.py:477
+#: ./src/pyams_content/shared/common/zmi/workflow.py:449
 msgid "Cancel retire request"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/workflow.py:522
+#: ./src/pyams_content/shared/common/zmi/workflow.py:489
 msgid "Retire"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/workflow.py:568
+#: ./src/pyams_content/shared/common/zmi/workflow.py:530
 #: ./src/pyams_content/workflow/__init__.py:436
 msgid "Request archive"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/workflow.py:616
+#: ./src/pyams_content/shared/common/zmi/workflow.py:573
 msgid "Cancel archive request"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/workflow.py:661
+#: ./src/pyams_content/shared/common/zmi/workflow.py:613
 msgid "Archive"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/workflow.py:707
+#: ./src/pyams_content/shared/common/zmi/workflow.py:654
 #: ./src/pyams_content/workflow/__init__.py:501
 #: ./src/pyams_content/workflow/__init__.py:513
 #: ./src/pyams_content/workflow/__init__.py:525
@@ -1848,50 +1887,50 @@
 msgid "Create new version"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/workflow.py:763
+#: ./src/pyams_content/shared/common/zmi/workflow.py:705
 #: ./src/pyams_content/workflow/__init__.py:561
 #: ./src/pyams_content/workflow/basic.py:248
 msgid "Delete version"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/workflow.py:853
+#: ./src/pyams_content/shared/common/zmi/workflow.py:790
 msgid "Previewed content?"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/workflow.py:857
+#: ./src/pyams_content/shared/common/zmi/workflow.py:794
 msgid "Verified content?"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/workflow.py:188
-#: ./src/pyams_content/shared/common/zmi/workflow.py:385
+#: ./src/pyams_content/shared/common/zmi/workflow.py:180
+#: ./src/pyams_content/shared/common/zmi/workflow.py:362
 msgid "Publication start date is required"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/workflow.py:291
-#: ./src/pyams_content/shared/common/zmi/workflow.py:447
+#: ./src/pyams_content/shared/common/zmi/workflow.py:273
+#: ./src/pyams_content/shared/common/zmi/workflow.py:419
 msgid "A comment is required"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/workflow.py:786
+#: ./src/pyams_content/shared/common/zmi/workflow.py:729
 msgid "Delete content"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/workflow.py:795
+#: ./src/pyams_content/shared/common/zmi/workflow.py:738
 msgid "Delete definitively"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/workflow.py:923
+#: ./src/pyams_content/shared/common/zmi/workflow.py:860
 msgid ""
 "You must confirm that you previewed and checked this content before "
 "requesting publication!!"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/workflow.py:90
+#: ./src/pyams_content/shared/common/zmi/workflow.py:87
 #, python-format
 msgid "{state} | by {principal}"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/workflow.py:86
+#: ./src/pyams_content/shared/common/zmi/workflow.py:83
 #: ./src/pyams_content/workflow/__init__.py:648
 #: ./src/pyams_content/workflow/__init__.py:619
 #: ./src/pyams_content/workflow/basic.py:315
@@ -1900,7 +1939,7 @@
 msgid "{state} {date}"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/__init__.py:266
+#: ./src/pyams_content/shared/common/zmi/__init__.py:265
 msgid "Duplicate content..."
 msgstr ""
 
@@ -1908,20 +1947,20 @@
 msgid "Duplicate content"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/__init__.py:85
+#: ./src/pyams_content/shared/common/zmi/__init__.py:84
 msgid "This title can be modified afterwards"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/__init__.py:277
+#: ./src/pyams_content/shared/common/zmi/__init__.py:276
 msgid "Duplicate this content"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/__init__.py:338
+#: ./src/pyams_content/shared/common/zmi/__init__.py:337
 #, python-format
 msgid "Clone created from version {source} of {oid} (in « {state} » state)"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/__init__.py:389
+#: ./src/pyams_content/shared/common/zmi/__init__.py:382
 msgid "Created or modified in this version"
 msgstr ""
 
@@ -1972,39 +2011,39 @@
 msgid "Shared tool properties"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/manager.py:132
+#: ./src/pyams_content/shared/common/zmi/manager.py:125
 msgid "WARNING"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/manager.py:134
+#: ./src/pyams_content/shared/common/zmi/manager.py:127
 msgid ""
 "Workflow shouldn't be modified if this tool already contains any shared "
 "content!"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/manager.py:157
+#: ./src/pyams_content/shared/common/zmi/manager.py:150
 msgid "Languages"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/manager.py:168
+#: ./src/pyams_content/shared/common/zmi/manager.py:162
 msgid "Content languages"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/manager.py:185
+#: ./src/pyams_content/shared/common/zmi/manager.py:172
 msgid ""
 "Tool languages are used to translate own tool properties, and newly created "
 "contents will propose these languages by default"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/manager.py:81
+#: ./src/pyams_content/shared/common/zmi/manager.py:80
 msgid "Content management"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/manager.py:83
+#: ./src/pyams_content/shared/common/zmi/manager.py:82
 msgid "Tool management"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/owner.py:51
+#: ./src/pyams_content/shared/common/zmi/owner.py:50
 msgid "Change owner..."
 msgstr ""
 
@@ -2012,38 +2051,75 @@
 msgid "Change content's owner"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/owner.py:132
+#: ./src/pyams_content/shared/common/zmi/owner.py:125
 msgid ""
 "All versions of this content which are not archived will be transferred to "
 "newly selected owner"
 msgstr ""
 
+#: ./src/pyams_content/shared/common/zmi/owner.py:60
+msgid "New owner"
+msgstr ""
+
 #: ./src/pyams_content/shared/common/zmi/owner.py:61
-msgid "New owner"
-msgstr ""
-
-#: ./src/pyams_content/shared/common/zmi/owner.py:62
 msgid "The selected user will become the new content's owner"
 msgstr ""
 
+#: ./src/pyams_content/shared/common/zmi/owner.py:63
+msgid "Keep previous owner as contributor"
+msgstr ""
+
 #: ./src/pyams_content/shared/common/zmi/owner.py:64
-msgid "Keep previous owner as contributor"
-msgstr ""
-
-#: ./src/pyams_content/shared/common/zmi/owner.py:65
 msgid "If 'yes', the previous owner will still be able to modify this content"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/owner.py:75
+#: ./src/pyams_content/shared/common/zmi/owner.py:74
 msgid "Change owner"
 msgstr ""
 
+#: ./src/pyams_content/shared/common/zmi/rename.py:62
+msgid "Change URL..."
+msgstr ""
+
+#: ./src/pyams_content/shared/common/zmi/rename.py:79
+msgid "Change item URL"
+msgstr ""
+
+#: ./src/pyams_content/shared/common/zmi/rename.py:86
+msgid "Item URL part"
+msgstr ""
+
+#: ./src/pyams_content/shared/common/zmi/rename.py:87
+msgid "URL part used to access this content"
+msgstr ""
+
+#: ./src/pyams_content/shared/common/zmi/rename.py:121
+msgid "You must provide an URL for this item!"
+msgstr ""
+
 #: ./src/pyams_content/shared/common/zmi/site.py:38
 #, python-format
 msgid ""
 "SEARCH - Between all contents published into &laquo;&nbsp;{site}&nbsp;&raquo;"
 msgstr ""
 
+#: ./src/pyams_content/shared/common/zmi/portal.py:46
+msgid "Edit default template properties"
+msgstr ""
+
+#: ./src/pyams_content/shared/common/zmi/portal.py:56
+msgid ""
+"**This form allows you to select shared content default template.**\n"
+"\n"
+"If you choose to use a shared template, you can only adjust settings of each portlet individually but can't change portlets list or page configuration.\n"
+"\n"
+"If you use a local template, you can define a whole custom configuration but the template definition can't be reused anywhere..."
+msgstr ""
+
+#: ./src/pyams_content/shared/common/zmi/portal.py:72
+msgid "Override tool default template"
+msgstr ""
+
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:134
 msgid "Unique ID"
 msgstr ""
@@ -2061,7 +2137,7 @@
 msgstr ""
 
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:273
-#: ./src/pyams_content/root/zmi/__init__.py:91
+#: ./src/pyams_content/root/zmi/__init__.py:110
 msgid "Dashboard"
 msgstr ""
 
@@ -2070,55 +2146,55 @@
 msgstr ""
 
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:329
-#: ./src/pyams_content/root/zmi/__init__.py:140
+#: ./src/pyams_content/root/zmi/__init__.py:159
 #, python-format
 msgid "MANAGER - {0} content waiting for your action"
 msgstr ""
 
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:330
-#: ./src/pyams_content/root/zmi/__init__.py:141
+#: ./src/pyams_content/root/zmi/__init__.py:160
 #, python-format
 msgid "MANAGER - {0} contents waiting for your action"
 msgstr ""
 
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:370
-#: ./src/pyams_content/root/zmi/__init__.py:184
+#: ./src/pyams_content/root/zmi/__init__.py:203
 #, python-format
 msgid "CONTRIBUTOR - {0} content waiting for action"
 msgstr ""
 
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:371
-#: ./src/pyams_content/root/zmi/__init__.py:185
+#: ./src/pyams_content/root/zmi/__init__.py:204
 #, python-format
 msgid "CONTRIBUTOR - {0} contents waiting for action"
 msgstr ""
 
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:402
-#: ./src/pyams_content/root/zmi/__init__.py:219
+#: ./src/pyams_content/root/zmi/__init__.py:238
 #, python-format
 msgid "CONTRIBUTOR - {0} modified content"
 msgstr ""
 
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:443
-#: ./src/pyams_content/root/zmi/__init__.py:262
+#: ./src/pyams_content/root/zmi/__init__.py:281
 msgid "My contents"
 msgstr ""
 
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:458
 #: ./src/pyams_content/shared/common/zmi/templates/dashboard.pt:8
-#: ./src/pyams_content/root/zmi/__init__.py:277
+#: ./src/pyams_content/root/zmi/__init__.py:296
 #: ./src/pyams_content/root/zmi/templates/dashboard.pt:8
 msgid "My favorites"
 msgstr ""
 
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:467
-#: ./src/pyams_content/root/zmi/__init__.py:286
+#: ./src/pyams_content/root/zmi/__init__.py:305
 #, python-format
 msgid "CONTRIBUTOR - {0} favorite"
 msgstr ""
 
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:468
-#: ./src/pyams_content/root/zmi/__init__.py:287
+#: ./src/pyams_content/root/zmi/__init__.py:306
 #, python-format
 msgid "CONTRIBUTOR - {0} favorites"
 msgstr ""
@@ -2129,188 +2205,188 @@
 msgstr ""
 
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:543
-#: ./src/pyams_content/root/zmi/__init__.py:325
+#: ./src/pyams_content/root/zmi/__init__.py:344
 msgid "Your favorites"
 msgstr ""
 
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:556
-#: ./src/pyams_content/root/zmi/__init__.py:338
+#: ./src/pyams_content/root/zmi/__init__.py:357
 msgid "My preparations"
 msgstr ""
 
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:565
-#: ./src/pyams_content/root/zmi/__init__.py:347
+#: ./src/pyams_content/root/zmi/__init__.py:366
 #, python-format
 msgid "CONTRIBUTOR - {0} prepared content"
 msgstr ""
 
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:566
-#: ./src/pyams_content/root/zmi/__init__.py:348
+#: ./src/pyams_content/root/zmi/__init__.py:367
 #, python-format
 msgid "CONTRIBUTOR - {0} prepared contents"
 msgstr ""
 
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:605
-#: ./src/pyams_content/root/zmi/__init__.py:386
+#: ./src/pyams_content/root/zmi/__init__.py:405
 msgid "Your prepared contents"
 msgstr ""
 
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:618
-#: ./src/pyams_content/root/zmi/__init__.py:399
+#: ./src/pyams_content/root/zmi/__init__.py:418
 msgid "My submissions"
 msgstr ""
 
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:627
-#: ./src/pyams_content/root/zmi/__init__.py:408
+#: ./src/pyams_content/root/zmi/__init__.py:427
 #, python-format
 msgid "CONTRIBUTOR - {0} submitted content"
 msgstr ""
 
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:628
-#: ./src/pyams_content/root/zmi/__init__.py:409
+#: ./src/pyams_content/root/zmi/__init__.py:428
 #, python-format
 msgid "CONTRIBUTOR - {0} submitted contents"
 msgstr ""
 
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:667
-#: ./src/pyams_content/root/zmi/__init__.py:447
+#: ./src/pyams_content/root/zmi/__init__.py:466
 msgid "Your submitted contents"
 msgstr ""
 
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:680
-#: ./src/pyams_content/root/zmi/__init__.py:460
+#: ./src/pyams_content/root/zmi/__init__.py:479
 msgid "My publications"
 msgstr ""
 
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:689
-#: ./src/pyams_content/root/zmi/__init__.py:469
+#: ./src/pyams_content/root/zmi/__init__.py:488
 #, python-format
 msgid "CONTRIBUTOR - {0} published content"
 msgstr ""
 
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:690
-#: ./src/pyams_content/root/zmi/__init__.py:470
+#: ./src/pyams_content/root/zmi/__init__.py:489
 #, python-format
 msgid "CONTRIBUTOR - {0} published contents"
 msgstr ""
 
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:729
-#: ./src/pyams_content/root/zmi/__init__.py:508
+#: ./src/pyams_content/root/zmi/__init__.py:527
 msgid "Your published contents"
 msgstr ""
 
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:742
-#: ./src/pyams_content/root/zmi/__init__.py:521
+#: ./src/pyams_content/root/zmi/__init__.py:540
 msgid "My retired contents"
 msgstr ""
 
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:751
-#: ./src/pyams_content/root/zmi/__init__.py:530
+#: ./src/pyams_content/root/zmi/__init__.py:549
 #, python-format
 msgid "CONTRIBUTOR - {0} retired content"
 msgstr ""
 
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:752
-#: ./src/pyams_content/root/zmi/__init__.py:531
+#: ./src/pyams_content/root/zmi/__init__.py:550
 #, python-format
 msgid "CONTRIBUTOR - {0} retired contents"
 msgstr ""
 
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:792
-#: ./src/pyams_content/root/zmi/__init__.py:570
+#: ./src/pyams_content/root/zmi/__init__.py:589
 msgid "Your retired contents"
 msgstr ""
 
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:805
-#: ./src/pyams_content/root/zmi/__init__.py:583
+#: ./src/pyams_content/root/zmi/__init__.py:602
 msgid "My archived contents"
 msgstr ""
 
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:814
-#: ./src/pyams_content/root/zmi/__init__.py:592
+#: ./src/pyams_content/root/zmi/__init__.py:611
 #, python-format
 msgid "CONTRIBUTOR - {0} archived content"
 msgstr ""
 
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:815
-#: ./src/pyams_content/root/zmi/__init__.py:593
+#: ./src/pyams_content/root/zmi/__init__.py:612
 #, python-format
 msgid "CONTRIBUTOR - {0} archived contents"
 msgstr ""
 
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:861
-#: ./src/pyams_content/root/zmi/__init__.py:638
+#: ./src/pyams_content/root/zmi/__init__.py:657
 msgid "Your archived contents"
 msgstr ""
 
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:875
-#: ./src/pyams_content/root/zmi/__init__.py:652
+#: ./src/pyams_content/root/zmi/__init__.py:671
 msgid "Other interventions"
 msgstr ""
 
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:890
-#: ./src/pyams_content/root/zmi/__init__.py:667
+#: ./src/pyams_content/root/zmi/__init__.py:686
 msgid "Last publications"
 msgstr ""
 
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:899
-#: ./src/pyams_content/root/zmi/__init__.py:676
+#: ./src/pyams_content/root/zmi/__init__.py:695
 #, python-format
 msgid "CONTRIBUTORS - {0} published content"
 msgstr ""
 
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:945
-#: ./src/pyams_content/root/zmi/__init__.py:721
+#: ./src/pyams_content/root/zmi/__init__.py:740
 msgid "Last published contents"
 msgstr ""
 
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:958
-#: ./src/pyams_content/root/zmi/__init__.py:734
+#: ./src/pyams_content/root/zmi/__init__.py:753
 msgid "Last updates"
 msgstr ""
 
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:967
-#: ./src/pyams_content/root/zmi/__init__.py:743
+#: ./src/pyams_content/root/zmi/__init__.py:762
 #, python-format
 msgid "CONTRIBUTORS - {0} updated content"
 msgstr ""
 
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:1012
-#: ./src/pyams_content/root/zmi/__init__.py:787
+#: ./src/pyams_content/root/zmi/__init__.py:806
 msgid "Last updated contents"
 msgstr ""
 
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:407
-#: ./src/pyams_content/root/zmi/__init__.py:224
+#: ./src/pyams_content/root/zmi/__init__.py:243
 #, python-format
 msgid "CONTRIBUTOR - {0} modified contents"
 msgstr ""
 
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:409
-#: ./src/pyams_content/root/zmi/__init__.py:226
+#: ./src/pyams_content/root/zmi/__init__.py:245
 #, python-format
 msgid "CONTRIBUTOR - Last {0} modified contents"
 msgstr ""
 
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:904
-#: ./src/pyams_content/root/zmi/__init__.py:681
+#: ./src/pyams_content/root/zmi/__init__.py:700
 #, python-format
 msgid "CONTRIBUTORS - Last {0} published contents"
 msgstr ""
 
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:906
-#: ./src/pyams_content/root/zmi/__init__.py:683
+#: ./src/pyams_content/root/zmi/__init__.py:702
 msgid "CONTRIBUTORS - Last published contents (in the limit of 50)"
 msgstr ""
 
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:972
-#: ./src/pyams_content/root/zmi/__init__.py:748
+#: ./src/pyams_content/root/zmi/__init__.py:767
 #, python-format
 msgid "CONTRIBUTORS - Last {0} updated contents"
 msgstr ""
 
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:974
-#: ./src/pyams_content/root/zmi/__init__.py:750
+#: ./src/pyams_content/root/zmi/__init__.py:769
 msgid "CONTRIBUTORS - Last updated contents (in the limit of 50)"
 msgstr ""
 
@@ -2324,64 +2400,62 @@
 msgid "Content publication start date is not passed yet"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/security.py:65
+#: ./src/pyams_content/shared/common/zmi/security.py:64
 msgid "Contributors restrictions"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/security.py:74
+#: ./src/pyams_content/shared/common/zmi/security.py:73
 msgid "Content contributors restrictions"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/security.py:108
+#: ./src/pyams_content/shared/common/zmi/security.py:107
 msgid "Contributor name"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/security.py:119
-#: ./src/pyams_content/shared/common/zmi/security.py:277
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:252
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:277
-msgid "Publication checks"
-msgstr ""
-
-#: ./src/pyams_content/shared/common/zmi/security.py:225
+#: ./src/pyams_content/shared/common/zmi/security.py:118
+#: ./src/pyams_content/shared/common/zmi/security.py:270
+msgid "Activated publication checks?"
+msgstr ""
+
+#: ./src/pyams_content/shared/common/zmi/security.py:218
 msgid "Managers restrictions"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/security.py:234
+#: ./src/pyams_content/shared/common/zmi/security.py:227
 msgid "Content managers restrictions"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/security.py:266
+#: ./src/pyams_content/shared/common/zmi/security.py:259
 msgid "Manager name"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/security.py:294
+#: ./src/pyams_content/shared/common/zmi/security.py:287
 msgid "Restricted"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/security.py:311
+#: ./src/pyams_content/shared/common/zmi/security.py:304
 msgid "Owners"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/security.py:401
+#: ./src/pyams_content/shared/common/zmi/security.py:394
 msgid "Publication workflow"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/security.py:168
+#: ./src/pyams_content/shared/common/zmi/security.py:167
 #, python-format
 msgid "Edit contributor restrictions for « {0} »"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/security.py:360
+#: ./src/pyams_content/shared/common/zmi/security.py:353
 #, python-format
 msgid "Edit manager restrictions for « {0} »"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/security.py:407
+#: ./src/pyams_content/shared/common/zmi/security.py:400
 msgid "Apply contents restrictions"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/security.py:409
+#: ./src/pyams_content/shared/common/zmi/security.py:402
 msgid ""
 "You can specify which contents this manager will be able to manage. If you "
 "specify several criteria, the manager will be able to manage contents for "
@@ -2451,9 +2525,10 @@
 
 #: ./src/pyams_content/shared/common/zmi/templates/check-input.pt:34
 #: ./src/pyams_content/shared/common/zmi/templates/preview-input.pt:34
-#: ./src/pyams_content/shared/common/interfaces/types.py:39
+#: ./src/pyams_content/shared/common/interfaces/types.py:40
 #: ./src/pyams_content/shared/form/zmi/field.py:159
-#: ./src/pyams_content/shared/form/interfaces/__init__.py:61
+#: ./src/pyams_content/shared/form/interfaces/__init__.py:62
+#: ./src/pyams_content/features/menu/zmi/__init__.py:208
 msgid "Label"
 msgstr ""
 
@@ -2462,18 +2537,18 @@
 msgid "Audit"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/templates/dashboard.pt:28
-#: ./src/pyams_content/root/zmi/templates/dashboard.pt:28
+#: ./src/pyams_content/shared/common/zmi/templates/dashboard.pt:29
+#: ./src/pyams_content/root/zmi/templates/dashboard.pt:29
 msgid "Quick search..."
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/templates/dashboard.pt:33
-#: ./src/pyams_content/root/zmi/templates/dashboard.pt:33
+#: ./src/pyams_content/shared/common/zmi/templates/dashboard.pt:35
+#: ./src/pyams_content/root/zmi/templates/dashboard.pt:35
 msgid "Advanced search..."
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/templates/dashboard.pt:46
-#: ./src/pyams_content/root/zmi/templates/dashboard.pt:46
+#: ./src/pyams_content/shared/common/zmi/templates/dashboard.pt:49
+#: ./src/pyams_content/root/zmi/templates/dashboard.pt:49
 msgid "You are not actually concerned by any content."
 msgstr ""
 
@@ -2645,79 +2720,91 @@
 msgid "This content is already retired and not visible."
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/types.py:35
-#: ./src/pyams_content/shared/form/zmi/field.py:148
-msgid "Name"
+#: ./src/pyams_content/shared/common/portlet/content/__init__.py:44
+msgid "Context content"
+msgstr ""
+
+#: ./src/pyams_content/shared/common/portlet/content/zmi/preview.pt:2
+msgid "This is where the content will be displayed!!"
+msgstr ""
+
+#: ./src/pyams_content/shared/common/portlet/content/skin/__init__.py:36
+msgid "Default content renderer"
 msgstr ""
 
 #: ./src/pyams_content/shared/common/interfaces/types.py:36
+#: ./src/pyams_content/shared/form/zmi/field.py:148
+msgid "Name"
+msgstr ""
+
+#: ./src/pyams_content/shared/common/interfaces/types.py:37
 msgid "Name of this data type; must be unique between all data types"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/types.py:42
-msgid "Navigation label"
-msgstr ""
-
 #: ./src/pyams_content/shared/common/interfaces/types.py:43
+msgid "Navigation label"
+msgstr ""
+
+#: ./src/pyams_content/shared/common/interfaces/types.py:44
 msgid "Label used for navigation entries"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/types.py:46
-msgid "Tab-folder label"
-msgstr ""
-
 #: ./src/pyams_content/shared/common/interfaces/types.py:47
+msgid "Tab-folder label"
+msgstr ""
+
+#: ./src/pyams_content/shared/common/interfaces/types.py:48
 msgid "Label used to include into tab folder"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/types.py:50
-msgid "'See also' label"
-msgstr ""
-
 #: ./src/pyams_content/shared/common/interfaces/types.py:51
+msgid "'See also' label"
+msgstr ""
+
+#: ./src/pyams_content/shared/common/interfaces/types.py:52
 msgid ""
 "This label can be used when contents of this type will be displayed in a 'See"
 " also' entries block"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/types.py:55
+#: ./src/pyams_content/shared/common/interfaces/types.py:56
 msgid "'Single value' label"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/types.py:56
+#: ./src/pyams_content/shared/common/interfaces/types.py:57
 msgid "Label given to this type when a single value is displayed"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/types.py:59
-msgid "'Link to list' label"
-msgstr ""
-
 #: ./src/pyams_content/shared/common/interfaces/types.py:60
+msgid "'Link to list' label"
+msgstr ""
+
+#: ./src/pyams_content/shared/common/interfaces/types.py:61
 msgid "Label used to display a link to a list of items of this type"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/types.py:63
-msgid "Next content label"
-msgstr ""
-
 #: ./src/pyams_content/shared/common/interfaces/types.py:64
+msgid "Next content label"
+msgstr ""
+
+#: ./src/pyams_content/shared/common/interfaces/types.py:65
 msgid "Label used to announce next date for this type"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/types.py:68
+#: ./src/pyams_content/shared/common/interfaces/types.py:69
 msgid "Image associated to this data type"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/types.py:81
-msgid "Field names"
-msgstr ""
-
 #: ./src/pyams_content/shared/common/interfaces/types.py:82
+msgid "Field names"
+msgstr ""
+
+#: ./src/pyams_content/shared/common/interfaces/types.py:83
 msgid "List of fields associated with this data type"
 msgstr ""
 
 #: ./src/pyams_content/shared/common/interfaces/__init__.py:46
-#: ./src/pyams_content/root/interfaces/__init__.py:40
+#: ./src/pyams_content/root/interfaces/__init__.py:43
 msgid "Webmasters"
 msgstr ""
 
@@ -2736,19 +2823,19 @@
 msgstr ""
 
 #: ./src/pyams_content/shared/common/interfaces/__init__.py:57
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:174
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:187
 msgid "Managers"
 msgstr ""
 
 #: ./src/pyams_content/shared/common/interfaces/__init__.py:58
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:175
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:188
 msgid ""
 "Managers can handle main operations in tool's workflow, like publish or "
 "retire contents"
 msgstr ""
 
 #: ./src/pyams_content/shared/common/interfaces/__init__.py:63
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:180
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:193
 msgid "Contributors"
 msgstr ""
 
@@ -2756,159 +2843,174 @@
 msgid "Contributors are users which are allowed to create new contents"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:90
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:68
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:199
+msgid "Designers"
+msgstr ""
+
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:69
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:200
+msgid "Designers are users which are allowed to manage presentation templates"
+msgstr ""
+
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:95
 msgid "Workflow name"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:91
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:96
 msgid "Name of workflow utility used to manage tool contents"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:115
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:124
 msgid "Content URL"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:116
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:125
 msgid ""
 "URL used to access this content; this is important for SEO and should include"
 " most important words describing content; spaces and underscores will be "
 "automatically replaced by hyphens"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:121
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:130
 msgid "Version creator"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:122
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:131
 msgid ""
 "Name of content's version creator. The creator of the first version is also "
 "it's owner."
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:126
-msgid "First owner"
-msgstr ""
-
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:127
-msgid "Name of content's first version owner"
-msgstr ""
-
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:131
-msgid "Version creation"
-msgstr ""
-
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:134
-msgid "Version modifiers"
-msgstr ""
-
 #: ./src/pyams_content/shared/common/interfaces/__init__.py:135
+msgid "First owner"
+msgstr ""
+
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:136
+msgid "Name of content's first version owner"
+msgstr ""
+
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:140
+msgid "Version creation"
+msgstr ""
+
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:143
+msgid "Version modifiers"
+msgstr ""
+
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:144
 msgid "List of principals who modified this content"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:138
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:147
 msgid "Last modifier"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:139
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:148
 msgid "Last principal who modified this content"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:142
-msgid "Last update"
-msgstr ""
-
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:146
-msgid ""
-"The content's description is 'hidden' into HTML's page headers; but it can be"
-" seen, for example, in some search engines results as content's description"
-msgstr ""
-
 #: ./src/pyams_content/shared/common/interfaces/__init__.py:151
-msgid "Keywords"
-msgstr ""
-
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:152
-msgid "They will be included into HTML pages metadata"
+msgid "Last update"
 msgstr ""
 
 #: ./src/pyams_content/shared/common/interfaces/__init__.py:155
-#: ./src/pyams_content/shared/site/zmi/folder.py:76
+msgid ""
+"The content's description is 'hidden' into HTML's page headers; but it can be"
+" seen, for example, in some search engines results as content's description"
+msgstr ""
+
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:160
+msgid "Keywords"
+msgstr ""
+
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:161
+msgid "They will be included into HTML pages metadata"
+msgstr ""
+
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:164
+#: ./src/pyams_content/shared/site/zmi/folder.py:78
 #: ./src/pyams_content/shared/site/interfaces/__init__.py:66
 msgid "Notepad"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:156
-#: ./src/pyams_content/shared/site/zmi/folder.py:77
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:165
+#: ./src/pyams_content/shared/site/zmi/folder.py:79
 #: ./src/pyams_content/shared/site/interfaces/__init__.py:67
 msgid "Internal information to be known about this content"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:167
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:180
 msgid "Content owner"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:168
-msgid ""
-"The owner is the creator of content's first version, except if it was "
-"transferred afterwards to another owner"
-msgstr ""
-
 #: ./src/pyams_content/shared/common/interfaces/__init__.py:181
 msgid ""
+"The owner is the creator of content's first version, except if it was "
+"transferred afterwards to another owner"
+msgstr ""
+
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:194
+msgid ""
 "Contributors are users which are allowed to update this content in addition "
 "to it's owner"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:186
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:204
 msgid "Readers"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:187
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:205
 msgid ""
 "Readers are users which are asked to verify and comment contents before they "
 "are published"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:192
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:210
 msgid "Guests"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:193
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:211
 msgid ""
 "Guests are users which are allowed to view contents with restricted access"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:213
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:231
 msgid "Principal ID"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:253
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:270
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:295
+msgid "Publication checks"
+msgstr ""
+
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:271
 msgid ""
 "If 'yes', this contributor will have to confirm that contents have been "
 "previewed and checked before asking for publication"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:278
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:296
 msgid ""
 "If 'yes', this manager will have to confirm that contents have been previewed"
 " and checked before publishing a content"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:283
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:301
 msgid "Restricted contents"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:284
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:302
 msgid ""
 "If 'yes', this manager will get restricted access to manage contents based on"
 " selected settings"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:289
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:307
 msgid "Selected owners"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:290
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:308
 msgid "Manager will have access to contents owned by these principals"
 msgstr ""
 
@@ -2995,7 +3097,7 @@
 msgstr ""
 
 #: ./src/pyams_content/shared/form/zmi/field.py:170
-#: ./src/pyams_content/shared/form/interfaces/__init__.py:56
+#: ./src/pyams_content/shared/form/interfaces/__init__.py:57
 msgid "Field type"
 msgstr ""
 
@@ -3004,11 +3106,11 @@
 msgstr ""
 
 #: ./src/pyams_content/shared/form/zmi/field.py:226
-#: ./src/pyams_content/shared/form/zmi/field.py:237
+#: ./src/pyams_content/shared/form/zmi/field.py:239
 msgid "Add form field"
 msgstr ""
 
-#: ./src/pyams_content/shared/form/zmi/field.py:285
+#: ./src/pyams_content/shared/form/zmi/field.py:281
 msgid "Edit form field properties"
 msgstr ""
 
@@ -3020,139 +3122,139 @@
 msgid "No currently defined form field."
 msgstr ""
 
-#: ./src/pyams_content/shared/form/zmi/field.py:262
+#: ./src/pyams_content/shared/form/zmi/field.py:266
 msgid "Specified name is already used!"
 msgstr ""
 
-#: ./src/pyams_content/shared/form/interfaces/__init__.py:34
+#: ./src/pyams_content/shared/form/interfaces/__init__.py:35
 msgid "Form"
 msgstr ""
 
-#: ./src/pyams_content/shared/form/interfaces/__init__.py:52
-msgid "Field name"
-msgstr ""
-
 #: ./src/pyams_content/shared/form/interfaces/__init__.py:53
+msgid "Field name"
+msgstr ""
+
+#: ./src/pyams_content/shared/form/interfaces/__init__.py:54
 msgid "Field internal name; must be unique for a given form"
 msgstr ""
 
-#: ./src/pyams_content/shared/form/interfaces/__init__.py:57
+#: ./src/pyams_content/shared/form/interfaces/__init__.py:58
 msgid "Selected field type"
 msgstr ""
 
-#: ./src/pyams_content/shared/form/interfaces/__init__.py:62
+#: ./src/pyams_content/shared/form/interfaces/__init__.py:63
 msgid "User field label"
 msgstr ""
 
-#: ./src/pyams_content/shared/form/interfaces/__init__.py:66
+#: ./src/pyams_content/shared/form/interfaces/__init__.py:67
 msgid "Field description can be displayed as hint"
 msgstr ""
 
-#: ./src/pyams_content/shared/form/interfaces/__init__.py:69
-msgid "Placeholder"
-msgstr ""
-
 #: ./src/pyams_content/shared/form/interfaces/__init__.py:70
+msgid "Placeholder"
+msgstr ""
+
+#: ./src/pyams_content/shared/form/interfaces/__init__.py:71
 msgid "Some field types like textline can display a placeholder"
 msgstr ""
 
-#: ./src/pyams_content/shared/form/interfaces/__init__.py:73
-msgid "Optional values"
-msgstr ""
-
 #: ./src/pyams_content/shared/form/interfaces/__init__.py:74
+msgid "Optional values"
+msgstr ""
+
+#: ./src/pyams_content/shared/form/interfaces/__init__.py:75
 msgid "List of available values (for 'choice' and 'list' field types)"
 msgstr ""
 
-#: ./src/pyams_content/shared/form/interfaces/__init__.py:77
-msgid "Default value"
-msgstr ""
-
 #: ./src/pyams_content/shared/form/interfaces/__init__.py:78
+msgid "Default value"
+msgstr ""
+
+#: ./src/pyams_content/shared/form/interfaces/__init__.py:79
 msgid "Give default value if field type can use it"
 msgstr ""
 
-#: ./src/pyams_content/shared/form/interfaces/__init__.py:81
-msgid "Required?"
-msgstr ""
-
 #: ./src/pyams_content/shared/form/interfaces/__init__.py:82
+msgid "Required?"
+msgstr ""
+
+#: ./src/pyams_content/shared/form/interfaces/__init__.py:83
 msgid "Select 'yes' to set field as mandatory"
 msgstr ""
 
-#: ./src/pyams_content/shared/form/interfaces/__init__.py:87
+#: ./src/pyams_content/shared/form/interfaces/__init__.py:88
 msgid "Select 'no' to hide given field..."
 msgstr ""
 
-#: ./src/pyams_content/shared/form/interfaces/__init__.py:121
+#: ./src/pyams_content/shared/form/interfaces/__init__.py:122
 msgid "Form title"
 msgstr ""
 
-#: ./src/pyams_content/shared/form/interfaces/__init__.py:124
+#: ./src/pyams_content/shared/form/interfaces/__init__.py:125
 msgid "Form header"
 msgstr ""
 
-#: ./src/pyams_content/shared/form/interfaces/__init__.py:127
-msgid "Form handler"
-msgstr ""
-
 #: ./src/pyams_content/shared/form/interfaces/__init__.py:128
+msgid "Form handler"
+msgstr ""
+
+#: ./src/pyams_content/shared/form/interfaces/__init__.py:129
 msgid "Select how form data is transmitted"
 msgstr ""
 
-#: ./src/pyams_content/shared/form/interfaces/__init__.py:131
-msgid "Authenticated only?"
-msgstr ""
-
 #: ./src/pyams_content/shared/form/interfaces/__init__.py:132
+msgid "Authenticated only?"
+msgstr ""
+
+#: ./src/pyams_content/shared/form/interfaces/__init__.py:133
 msgid "If 'yes', only authenticated users will be able to see and submit form"
 msgstr ""
 
-#: ./src/pyams_content/shared/form/interfaces/__init__.py:136
-msgid "Use captcha?"
-msgstr ""
-
 #: ./src/pyams_content/shared/form/interfaces/__init__.py:137
+msgid "Use captcha?"
+msgstr ""
+
+#: ./src/pyams_content/shared/form/interfaces/__init__.py:138
 msgid "If 'yes', a captcha will be added automatically to the form"
 msgstr ""
 
-#: ./src/pyams_content/shared/form/interfaces/__init__.py:141
-msgid "Submit label"
-msgstr ""
-
 #: ./src/pyams_content/shared/form/interfaces/__init__.py:142
+msgid "Submit label"
+msgstr ""
+
+#: ./src/pyams_content/shared/form/interfaces/__init__.py:143
 msgid "Label of form submit button"
 msgstr ""
 
-#: ./src/pyams_content/shared/form/interfaces/__init__.py:179
-msgid "Source address"
-msgstr ""
-
 #: ./src/pyams_content/shared/form/interfaces/__init__.py:180
+msgid "Source address"
+msgstr ""
+
+#: ./src/pyams_content/shared/form/interfaces/__init__.py:181
 msgid "Mail address from which form data is sent"
 msgstr ""
 
-#: ./src/pyams_content/shared/form/interfaces/__init__.py:183
-msgid "Source name"
-msgstr ""
-
 #: ./src/pyams_content/shared/form/interfaces/__init__.py:184
+msgid "Source name"
+msgstr ""
+
+#: ./src/pyams_content/shared/form/interfaces/__init__.py:185
 msgid "Name of mail data sender"
 msgstr ""
 
-#: ./src/pyams_content/shared/form/interfaces/__init__.py:187
-msgid "Recipient address"
-msgstr ""
-
 #: ./src/pyams_content/shared/form/interfaces/__init__.py:188
+msgid "Recipient address"
+msgstr ""
+
+#: ./src/pyams_content/shared/form/interfaces/__init__.py:189
 msgid "Mail address to which form data is sent"
 msgstr ""
 
-#: ./src/pyams_content/shared/form/interfaces/__init__.py:191
-msgid "Recipient name"
-msgstr ""
-
 #: ./src/pyams_content/shared/form/interfaces/__init__.py:192
+msgid "Recipient name"
+msgstr ""
+
+#: ./src/pyams_content/shared/form/interfaces/__init__.py:193
 msgid "Name of data recipient"
 msgstr ""
 
@@ -3170,7 +3272,7 @@
 msgid "News topic « {title} »"
 msgstr ""
 
-#: ./src/pyams_content/shared/news/interfaces/__init__.py:28
+#: ./src/pyams_content/shared/news/interfaces/__init__.py:29
 msgid "News topic"
 msgstr ""
 
@@ -3196,7 +3298,7 @@
 msgid "View themes settings"
 msgstr ""
 
-#: ./src/pyams_content/shared/view/zmi/reference.py:53
+#: ./src/pyams_content/shared/view/zmi/reference.py:52
 msgid "References..."
 msgstr ""
 
@@ -3204,15 +3306,7 @@
 msgid "View internal references settings"
 msgstr ""
 
-#: ./src/pyams_content/shared/view/zmi/templates/render.pt:2
-msgid "View result items"
-msgstr ""
-
-#: ./src/pyams_content/shared/view/zmi/templates/render.pt:3
-msgid "WARNING: items displayed in this preview are out of context!!"
-msgstr ""
-
-#: ./src/pyams_content/shared/view/portlet/__init__.py:56
+#: ./src/pyams_content/shared/view/portlet/__init__.py:58
 msgid "View items"
 msgstr ""
 
@@ -3229,7 +3323,7 @@
 msgstr ""
 
 #: ./src/pyams_content/shared/view/interfaces/__init__.py:40
-#: ./src/pyams_content/interfaces/__init__.py:111
+#: ./src/pyams_content/interfaces/__init__.py:113
 #: ./src/pyams_content/features/review/interfaces.py:74
 msgid "Creation date"
 msgstr ""
@@ -3302,15 +3396,23 @@
 msgid "Specify how selected references are included into view results"
 msgstr ""
 
-#: ./src/pyams_content/shared/view/interfaces/__init__.py:171
-msgid "Select context themes?"
-msgstr ""
-
-#: ./src/pyams_content/shared/view/interfaces/__init__.py:172
-msgid "If 'yes', themes will be extracted from context"
+#: ./src/pyams_content/shared/view/interfaces/__init__.py:164
+msgid "Exclude context?"
+msgstr ""
+
+#: ./src/pyams_content/shared/view/interfaces/__init__.py:165
+msgid "If 'yes', context will be excluded from results list"
 msgstr ""
 
 #: ./src/pyams_content/shared/view/interfaces/__init__.py:176
+msgid "Select context themes?"
+msgstr ""
+
+#: ./src/pyams_content/shared/view/interfaces/__init__.py:177
+msgid "If 'yes', themes will be extracted from context"
+msgstr ""
+
+#: ./src/pyams_content/shared/view/interfaces/__init__.py:181
 msgid "Other terms"
 msgstr ""
 
@@ -3336,12 +3438,12 @@
 msgid "Image map..."
 msgstr ""
 
-#: ./src/pyams_content/shared/imagemap/zmi/paragraph.py:65
+#: ./src/pyams_content/shared/imagemap/zmi/paragraph.py:67
 msgid "Add new image map"
 msgstr ""
 
-#: ./src/pyams_content/shared/imagemap/zmi/paragraph.py:97
-#: ./src/pyams_content/shared/logo/zmi/paragraph.py:96
+#: ./src/pyams_content/shared/imagemap/zmi/paragraph.py:94
+#: ./src/pyams_content/shared/logo/zmi/paragraph.py:93
 msgid "Edit paragraph properties"
 msgstr ""
 
@@ -3401,15 +3503,15 @@
 msgid "Bad query object_name parameter value!"
 msgstr ""
 
-#: ./src/pyams_content/shared/imagemap/zmi/area.py:46
+#: ./src/pyams_content/shared/imagemap/zmi/area.py:47
 msgid "Add image area"
 msgstr ""
 
-#: ./src/pyams_content/shared/imagemap/zmi/area.py:64
+#: ./src/pyams_content/shared/imagemap/zmi/area.py:66
 msgid "Add new image area"
 msgstr ""
 
-#: ./src/pyams_content/shared/imagemap/zmi/area.py:108
+#: ./src/pyams_content/shared/imagemap/zmi/area.py:107
 msgid "Edit image map properties"
 msgstr ""
 
@@ -3455,7 +3557,7 @@
 msgid "Image map template"
 msgstr ""
 
-#: ./src/pyams_content/shared/site/folder.py:58
+#: ./src/pyams_content/shared/site/folder.py:59
 msgid "Site folder"
 msgstr ""
 
@@ -3463,39 +3565,44 @@
 msgid "Content link"
 msgstr ""
 
-#: ./src/pyams_content/shared/site/zmi/folder.py:59
+#: ./src/pyams_content/shared/site/manager.py:68
+#: ./src/pyams_content/shared/site/zmi/manager.py:125
+msgid "Site manager"
+msgstr ""
+
+#: ./src/pyams_content/shared/site/zmi/folder.py:61
 msgid "Add site folder..."
 msgstr ""
 
-#: ./src/pyams_content/shared/site/zmi/folder.py:90
+#: ./src/pyams_content/shared/site/zmi/folder.py:93
 msgid "Add site folder"
 msgstr ""
 
-#: ./src/pyams_content/shared/site/zmi/folder.py:166
+#: ./src/pyams_content/shared/site/zmi/folder.py:162
 msgid "Site folder management"
 msgstr ""
 
-#: ./src/pyams_content/shared/site/zmi/folder.py:177
+#: ./src/pyams_content/shared/site/zmi/folder.py:190
 msgid "Site folder properties"
 msgstr ""
 
-#: ./src/pyams_content/shared/site/zmi/folder.py:69
-#: ./src/pyams_content/interfaces/__init__.py:100
+#: ./src/pyams_content/shared/site/zmi/folder.py:71
+#: ./src/pyams_content/interfaces/__init__.py:102
 msgid "Visible label used to display content"
 msgstr ""
 
-#: ./src/pyams_content/shared/site/zmi/folder.py:72
+#: ./src/pyams_content/shared/site/zmi/folder.py:74
 #: ./src/pyams_content/shared/site/zmi/__init__.py:72
-#: ./src/pyams_content/shared/site/zmi/link.py:67
+#: ./src/pyams_content/shared/site/zmi/link.py:66
 msgid "Parent"
 msgstr ""
 
-#: ./src/pyams_content/shared/site/zmi/folder.py:73
-#: ./src/pyams_content/shared/site/zmi/link.py:68
+#: ./src/pyams_content/shared/site/zmi/folder.py:75
+#: ./src/pyams_content/shared/site/zmi/link.py:67
 msgid "Folder's parent"
 msgstr ""
 
-#: ./src/pyams_content/shared/site/zmi/folder.py:150
+#: ./src/pyams_content/shared/site/zmi/folder.py:155
 msgid "You must provide a folder name for default server language!"
 msgstr ""
 
@@ -3507,7 +3614,7 @@
 msgid "Add topic..."
 msgstr ""
 
-#: ./src/pyams_content/shared/site/zmi/__init__.py:84
+#: ./src/pyams_content/shared/site/zmi/__init__.py:86
 msgid "Add topic"
 msgstr ""
 
@@ -3515,7 +3622,7 @@
 msgid "Topic's parent"
 msgstr ""
 
-#: ./src/pyams_content/shared/site/zmi/link.py:58
+#: ./src/pyams_content/shared/site/zmi/link.py:57
 msgid "Rent content..."
 msgstr ""
 
@@ -3523,85 +3630,81 @@
 msgid "Rent existing content"
 msgstr ""
 
-#: ./src/pyams_content/shared/site/zmi/link.py:141
+#: ./src/pyams_content/shared/site/zmi/link.py:135
 msgid "Edit content link properties"
 msgstr ""
 
 #: ./src/pyams_content/shared/site/zmi/container.py:106
 #: ./src/pyams_content/shared/site/zmi/container.py:118
-#: ./src/pyams_content/shared/blog/zmi/manager.py:160
-#: ./src/pyams_content/shared/blog/zmi/manager.py:172
+#: ./src/pyams_content/shared/blog/zmi/manager.py:155
+#: ./src/pyams_content/shared/blog/zmi/manager.py:167
 msgid "Publication dates..."
 msgstr ""
 
-#: ./src/pyams_content/shared/site/zmi/container.py:131
-#: ./src/pyams_content/shared/blog/zmi/manager.py:185
+#: ./src/pyams_content/shared/site/zmi/container.py:132
+#: ./src/pyams_content/shared/blog/zmi/manager.py:181
 msgid "Update publication dates"
 msgstr ""
 
-#: ./src/pyams_content/shared/site/zmi/container.py:183
-#: ./src/pyams_content/shared/site/zmi/container.py:193
+#: ./src/pyams_content/shared/site/zmi/container.py:178
+#: ./src/pyams_content/shared/site/zmi/container.py:188
 #: ./src/pyams_content/root/zmi/sites.py:68
 msgid "Site tree"
 msgstr ""
 
-#: ./src/pyams_content/shared/site/zmi/container.py:312
-#: ./src/pyams_content/shared/site/zmi/container.py:162
+#: ./src/pyams_content/shared/site/zmi/container.py:307
+#: ./src/pyams_content/shared/site/zmi/container.py:156
 msgid "Visible element?"
 msgstr ""
 
-#: ./src/pyams_content/shared/site/zmi/container.py:313
+#: ./src/pyams_content/shared/site/zmi/container.py:308
 msgid "Switch element visibility"
 msgstr ""
 
-#: ./src/pyams_content/shared/site/zmi/container.py:374
+#: ./src/pyams_content/shared/site/zmi/container.py:369
 msgid "Folders and topics"
 msgstr ""
 
-#: ./src/pyams_content/shared/site/zmi/container.py:410
-#: ./src/pyams_content/root/zmi/__init__.py:798
+#: ./src/pyams_content/shared/site/zmi/container.py:405
+#: ./src/pyams_content/root/zmi/__init__.py:817
 msgid "Content"
 msgstr ""
 
-#: ./src/pyams_content/shared/site/zmi/container.py:506
+#: ./src/pyams_content/shared/site/zmi/container.py:501
 msgid "Delete site item"
 msgstr ""
 
-#: ./src/pyams_content/shared/site/zmi/container.py:383
+#: ./src/pyams_content/shared/site/zmi/container.py:378
 msgid "Click to open/close all folders"
 msgstr ""
 
-#: ./src/pyams_content/shared/site/zmi/container.py:399
+#: ./src/pyams_content/shared/site/zmi/container.py:394
 msgid "Click to show/hide inner folders"
 msgstr ""
 
-#: ./src/pyams_content/shared/site/zmi/container.py:269
+#: ./src/pyams_content/shared/site/zmi/container.py:264
 msgid "Can't reparent object to one of it's children. Reloading..."
 msgstr ""
 
-#: ./src/pyams_content/shared/site/zmi/manager.py:58
+#: ./src/pyams_content/shared/site/zmi/manager.py:57
 msgid "Site management"
 msgstr ""
 
-#: ./src/pyams_content/shared/site/zmi/manager.py:88
-#: ./src/pyams_content/shared/site/zmi/manager.py:100
+#: ./src/pyams_content/shared/site/zmi/manager.py:112
+#: ./src/pyams_content/shared/site/zmi/manager.py:126
 msgid "Add site manager"
 msgstr ""
 
-#: ./src/pyams_content/shared/site/zmi/manager.py:99
-msgid "Site manager"
-msgstr ""
-
-#: ./src/pyams_content/shared/site/zmi/manager.py:131
-#: ./src/pyams_content/shared/blog/zmi/manager.py:126
+#: ./src/pyams_content/shared/site/zmi/manager.py:156
+#: ./src/pyams_content/shared/blog/zmi/manager.py:127
 msgid "You must provide a short name for default server language!"
 msgstr ""
 
-#: ./src/pyams_content/shared/site/zmi/manager.py:135
+#: ./src/pyams_content/shared/site/zmi/manager.py:160
 msgid "Specified site manager name is already used!"
 msgstr ""
 
-#: ./src/pyams_content/shared/site/zmi/manager.py:139
+#: ./src/pyams_content/shared/site/zmi/manager.py:164
 msgid "A site manager is already registered with this name!!"
 msgstr ""
 
@@ -3655,11 +3758,11 @@
 msgid "no URL defined"
 msgstr ""
 
-#: ./src/pyams_content/shared/logo/zmi/paragraph.py:53
+#: ./src/pyams_content/shared/logo/zmi/paragraph.py:54
 msgid "Logos..."
 msgstr ""
 
-#: ./src/pyams_content/shared/logo/zmi/paragraph.py:64
+#: ./src/pyams_content/shared/logo/zmi/paragraph.py:67
 msgid "Add new logos paragraph"
 msgstr ""
 
@@ -3720,19 +3823,19 @@
 msgstr ""
 
 #: ./src/pyams_content/shared/blog/zmi/manager.py:83
-#: ./src/pyams_content/shared/blog/zmi/manager.py:95
+#: ./src/pyams_content/shared/blog/zmi/manager.py:97
 msgid "Add blog manager"
 msgstr ""
 
-#: ./src/pyams_content/shared/blog/zmi/manager.py:94
+#: ./src/pyams_content/shared/blog/zmi/manager.py:96
 msgid "Blog manager"
 msgstr ""
 
-#: ./src/pyams_content/shared/blog/zmi/manager.py:130
+#: ./src/pyams_content/shared/blog/zmi/manager.py:131
 msgid "Specified blog manager name is already used!"
 msgstr ""
 
-#: ./src/pyams_content/shared/blog/zmi/manager.py:134
+#: ./src/pyams_content/shared/blog/zmi/manager.py:135
 msgid "A blog manager is already registered with this name!!"
 msgstr ""
 
@@ -3760,6 +3863,10 @@
 msgid "Default length used for inner tables and dashboards"
 msgstr ""
 
+#: ./src/pyams_content/root/__init__.py:67
+msgid "Site root"
+msgstr ""
+
 #: ./src/pyams_content/root/zmi/sites.py:78
 msgid "Blogs and shared sites"
 msgstr ""
@@ -3784,7 +3891,7 @@
 msgid "Content types"
 msgstr ""
 
-#: ./src/pyams_content/root/zmi/__init__.py:77
+#: ./src/pyams_content/root/zmi/__init__.py:78
 msgid "Home"
 msgstr ""
 
@@ -3796,19 +3903,19 @@
 msgid "SEARCH - Between all contents"
 msgstr ""
 
-#: ./src/pyams_content/root/interfaces/__init__.py:36
+#: ./src/pyams_content/root/interfaces/__init__.py:39
 msgid "Site managers"
 msgstr ""
 
-#: ./src/pyams_content/root/interfaces/__init__.py:44
+#: ./src/pyams_content/root/interfaces/__init__.py:47
 msgid "Templates managers"
 msgstr ""
 
-#: ./src/pyams_content/root/interfaces/__init__.py:48
+#: ./src/pyams_content/root/interfaces/__init__.py:51
 msgid "Operators group"
 msgstr ""
 
-#: ./src/pyams_content/root/interfaces/__init__.py:49
+#: ./src/pyams_content/root/interfaces/__init__.py:52
 msgid "Name of group containing all roles owners"
 msgstr ""
 
@@ -4129,11 +4236,11 @@
 msgid "Automatic contents withdrawal:\n"
 msgstr ""
 
-#: ./src/pyams_content/workflow/zmi/task.py:43
+#: ./src/pyams_content/workflow/zmi/task.py:42
 msgid "Add content archiver task..."
 msgstr ""
 
-#: ./src/pyams_content/workflow/zmi/task.py:54
+#: ./src/pyams_content/workflow/zmi/task.py:55
 msgid "Add automatic content archiver"
 msgstr ""
 
@@ -4162,18 +4269,20 @@
 msgstr ""
 
 #: ./src/pyams_content/interfaces/__init__.py:96
-msgid "WARNING: this key can't be modified after creation!!!"
-msgstr ""
-
-#: ./src/pyams_content/interfaces/__init__.py:103
+msgid ""
+"WARNING: this key can't be modified after creation!!! Spaces, uppercase "
+"letters ou accentuated characters will be replaced automatically."
+msgstr ""
+
+#: ./src/pyams_content/interfaces/__init__.py:105
 msgid "Short name"
 msgstr ""
 
-#: ./src/pyams_content/interfaces/__init__.py:104
+#: ./src/pyams_content/interfaces/__init__.py:106
 msgid "Short name used in breadcrumbs"
 msgstr ""
 
-#: ./src/pyams_content/interfaces/__init__.py:115
+#: ./src/pyams_content/interfaces/__init__.py:117
 msgid "Modification date"
 msgstr ""
 
@@ -4194,11 +4303,11 @@
 msgid "Properties..."
 msgstr ""
 
-#: ./src/pyams_content/reference/zmi/table.py:156
+#: ./src/pyams_content/reference/zmi/table.py:157
 msgid "Edit table properties"
 msgstr ""
 
-#: ./src/pyams_content/reference/zmi/table.py:173
+#: ./src/pyams_content/reference/zmi/table.py:167
 msgid "Table management"
 msgstr ""
 
@@ -4206,11 +4315,16 @@
 msgid "References"
 msgstr ""
 
+#: ./src/pyams_content/reference/pictograms/zmi/__init__.py:169
+#: ./src/pyams_content/reference/pictograms/zmi/widget.py:55
+msgid "Default header: --"
+msgstr ""
+
 #: ./src/pyams_content/reference/pictograms/zmi/manager.py:51
 msgid "Pictograms selection..."
 msgstr ""
 
-#: ./src/pyams_content/reference/pictograms/zmi/manager.py:62
+#: ./src/pyams_content/reference/pictograms/zmi/manager.py:63
 #: ./src/pyams_content/reference/pictograms/zmi/templates/manager-selection.pt:34
 #: ./src/pyams_content/reference/pictograms/interfaces/__init__.py:73
 msgid "Selected pictograms"
@@ -4225,6 +4339,10 @@
 msgid "Display pictogram properties"
 msgstr ""
 
+#: ./src/pyams_content/reference/pictograms/zmi/templates/pictogram-header.pt:6
+msgid "Default header: ${header}"
+msgstr ""
+
 #: ./src/pyams_content/reference/pictograms/interfaces/__init__.py:45
 msgid "Pictogram content"
 msgstr ""
@@ -4242,7 +4360,7 @@
 msgid "List of selected pictograms which will be available to shared contents"
 msgstr ""
 
-#: ./src/pyams_content/features/renderer/zmi/__init__.py:72
+#: ./src/pyams_content/features/renderer/zmi/__init__.py:70
 #: ./src/pyams_content/features/renderer/zmi/templates/renderer-input.pt:4
 msgid "Edit renderer properties"
 msgstr ""
@@ -4322,7 +4440,7 @@
 
 #. Default: Heading
 #: ./src/pyams_content/features/alert/interfaces.py:65
-#: ./src/pyams_content/features/alert/zmi/container.py:158
+#: ./src/pyams_content/features/alert/zmi/container.py:157
 msgid "alert-header"
 msgstr ""
 
@@ -4331,7 +4449,7 @@
 msgstr ""
 
 #: ./src/pyams_content/features/alert/interfaces.py:69
-#: ./src/pyams_content/features/alert/zmi/container.py:170
+#: ./src/pyams_content/features/alert/zmi/container.py:169
 msgid "Message"
 msgstr ""
 
@@ -4371,7 +4489,7 @@
 "set to 0 to always display the alert"
 msgstr ""
 
-#: ./src/pyams_content/features/alert/zmi/__init__.py:46
+#: ./src/pyams_content/features/alert/zmi/__init__.py:45
 msgid "Add alert"
 msgstr ""
 
@@ -4379,7 +4497,7 @@
 msgid "Add new alert"
 msgstr ""
 
-#: ./src/pyams_content/features/alert/zmi/__init__.py:85
+#: ./src/pyams_content/features/alert/zmi/__init__.py:79
 msgid "Edit alert properties"
 msgstr ""
 
@@ -4387,31 +4505,104 @@
 msgid "Alerts"
 msgstr ""
 
-#: ./src/pyams_content/features/alert/zmi/container.py:192
+#: ./src/pyams_content/features/alert/zmi/container.py:191
 msgid "Alert list"
 msgstr ""
 
-#: ./src/pyams_content/features/alert/zmi/container.py:91
+#: ./src/pyams_content/features/alert/zmi/container.py:90
 msgid "No currently defined alert."
 msgstr ""
 
-#: ./src/pyams_content/features/footer/zmi/__init__.py:56
+#: ./src/pyams_content/features/menu/zmi/__init__.py:81
+msgid "Add menu..."
+msgstr ""
+
+#: ./src/pyams_content/features/menu/zmi/__init__.py:92
+msgid "Add new menu"
+msgstr ""
+
+#: ./src/pyams_content/features/menu/zmi/__init__.py:123
+msgid "Edit menu properties"
+msgstr ""
+
+#: ./src/pyams_content/features/menu/zmi/__init__.py:110
+msgid "Menu was correctly added."
+msgstr ""
+
+#: ./src/pyams_content/features/menu/zmi/__init__.py:388
+msgid "Link was correctly added."
+msgstr ""
+
+#: ./src/pyams_content/features/menu/zmi/templates/menu-name-cell.pt:7
+msgid "Click to see menu items"
+msgstr ""
+
+#: ./src/pyams_content/features/menu/portlet/navigation/simple.py:68
+msgid "Simple navigation"
+msgstr ""
+
+#: ./src/pyams_content/features/menu/portlet/navigation/double.py:68
+msgid "Double navigation"
+msgstr ""
+
+#: ./src/pyams_content/features/menu/portlet/navigation/zmi/simple.py:70
+msgid "Navigation links"
+msgstr ""
+
+#: ./src/pyams_content/features/menu/portlet/navigation/zmi/double.py:70
+msgid "Navigation menus"
+msgstr ""
+
+#: ./src/pyams_content/features/menu/portlet/navigation/zmi/templates/simple-preview.pt:13
+msgid "Link has no illustration"
+msgstr ""
+
+#: ./src/pyams_content/features/menu/portlet/navigation/interfaces/simple.py:32
+#: ./src/pyams_content/features/menu/portlet/navigation/interfaces/double.py:32
+msgid "Portlet main title"
+msgstr ""
+
+#: ./src/pyams_content/features/menu/portlet/navigation/interfaces/simple.py:35
+#: ./src/pyams_content/features/menu/portlet/navigation/interfaces/double.py:35
+msgid "Subtitle"
+msgstr ""
+
+#: ./src/pyams_content/features/menu/portlet/navigation/interfaces/simple.py:36
+#: ./src/pyams_content/features/menu/portlet/navigation/interfaces/double.py:36
+msgid "Portlet subtitle"
+msgstr ""
+
+#: ./src/pyams_content/features/menu/interfaces/__init__.py:63
+msgid "Menu title"
+msgstr ""
+
+#: ./src/pyams_content/features/menu/interfaces/__init__.py:64
+msgid "Displayed menu label"
+msgstr ""
+
+#: ./src/pyams_content/features/footer/zmi/__init__.py:60
 msgid "Page footer"
 msgstr ""
 
-#: ./src/pyams_content/features/footer/zmi/__init__.py:74
+#: ./src/pyams_content/features/footer/zmi/__init__.py:78
 msgid "Edit footer settings"
 msgstr ""
 
-#: ./src/pyams_content/features/footer/zmi/__init__.py:172
+#: ./src/pyams_content/features/footer/zmi/__init__.py:152
+msgid ""
+"WARNING: Footer properties are saved automatically when changing inherit "
+"mode!!"
+msgstr ""
+
+#: ./src/pyams_content/features/footer/zmi/__init__.py:220
 msgid "Footer renderer settings"
 msgstr ""
 
-#: ./src/pyams_content/features/footer/zmi/__init__.py:101
+#: ./src/pyams_content/features/footer/zmi/__init__.py:107
 msgid "Don't inherit parent footer"
 msgstr ""
 
-#: ./src/pyams_content/features/footer/skin/__init__.py:49
+#: ./src/pyams_content/features/footer/skin/__init__.py:53
 msgid "Hidden footer"
 msgstr ""
 
@@ -4423,17 +4614,17 @@
 msgid "Presentation template used for this footer"
 msgstr ""
 
-#: ./src/pyams_content/features/review/__init__.py:180
+#: ./src/pyams_content/features/review/__init__.py:181
 #, python-format
 msgid "Request comment: {comment}"
 msgstr ""
 
-#: ./src/pyams_content/features/review/__init__.py:210
+#: ./src/pyams_content/features/review/__init__.py:211
 #, python-format
 msgid "A new comment was added on content « {0} »"
 msgstr ""
 
-#: ./src/pyams_content/features/review/__init__.py:167
+#: ./src/pyams_content/features/review/__init__.py:168
 #, python-format
 msgid "[{service_name}] A content review is requested"
 msgstr ""
@@ -4478,15 +4669,15 @@
 msgid "Ask for review..."
 msgstr ""
 
-#: ./src/pyams_content/features/review/zmi/__init__.py:99
+#: ./src/pyams_content/features/review/zmi/__init__.py:100
 msgid "Content review request"
 msgstr ""
 
-#: ./src/pyams_content/features/review/zmi/__init__.py:172
+#: ./src/pyams_content/features/review/zmi/__init__.py:166
 msgid "Comments"
 msgstr ""
 
-#: ./src/pyams_content/features/review/zmi/__init__.py:192
+#: ./src/pyams_content/features/review/zmi/__init__.py:186
 msgid "Review comments"
 msgstr ""
 
@@ -4521,15 +4712,15 @@
 msgid "Ask for content review"
 msgstr ""
 
-#: ./src/pyams_content/features/review/zmi/__init__.py:153
+#: ./src/pyams_content/features/review/zmi/__init__.py:147
 msgid "Request successful. No new notification have been sent"
 msgstr ""
 
-#: ./src/pyams_content/features/review/zmi/__init__.py:266
+#: ./src/pyams_content/features/review/zmi/__init__.py:260
 msgid "Message is mandatory!"
 msgstr ""
 
-#: ./src/pyams_content/features/review/zmi/__init__.py:141
+#: ./src/pyams_content/features/review/zmi/__init__.py:135
 #, python-format
 msgid "Request successful. {count} new notification(s) have been sent"
 msgstr ""
@@ -4605,22 +4796,28 @@
 msgid "Thank you."
 msgstr ""
 
-#: ./src/pyams_content/features/header/zmi/__init__.py:62
+#: ./src/pyams_content/features/header/zmi/__init__.py:66
 msgid "Page header"
 msgstr ""
 
-#: ./src/pyams_content/features/header/zmi/__init__.py:80
+#: ./src/pyams_content/features/header/zmi/__init__.py:84
 msgid "Edit header settings"
 msgstr ""
 
-#: ./src/pyams_content/features/header/zmi/__init__.py:178
+#: ./src/pyams_content/features/header/zmi/__init__.py:161
+msgid ""
+"WARNING: Header properties are saved automatically when changing inherit "
+"mode!!"
+msgstr ""
+
+#: ./src/pyams_content/features/header/zmi/__init__.py:229
 msgid "Header renderer settings"
 msgstr ""
 
-#: ./src/pyams_content/features/header/zmi/__init__.py:107
+#: ./src/pyams_content/features/header/zmi/__init__.py:113
 msgid "Don't inherit parent header"
 msgstr ""
 
-#: ./src/pyams_content/features/header/skin/__init__.py:49
+#: ./src/pyams_content/features/header/skin/__init__.py:53
 msgid "Hidden header"
 msgstr ""
--- a/src/pyams_content/reference/pictograms/manager.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/reference/pictograms/manager.py	Wed Jun 27 16:42:01 2018 +0200
@@ -54,12 +54,16 @@
         terms = []
         table = query_utility(IPictogramTable)
         if table is not None:
+            request = check_request()
             target = get_parent(context, IPictogramManagerTarget)
             if target is not None:
-                request = check_request()
                 manager = IPictogramManager(target)
-                pictograms = [table.get(name) for name in manager.selected_pictograms]
+                pictograms = [table.get(name) for name in manager.selected_pictograms or ()]
                 terms = [SimpleTerm(v.__name__,
                                     title=II18n(v).query_attribute('title', request=request))
                          for v in pictograms if v is not None]
+            else:
+                terms = [SimpleTerm(v.__name__,
+                                    title=II18n(v).query_attribute('title', request=request))
+                         for v in table.values()]
         super(SelectedPictogramsVocabulary, self).__init__(terms)
--- a/src/pyams_content/reference/pictograms/zmi/__init__.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/reference/pictograms/zmi/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -14,9 +14,6 @@
 
 
 # import standard library
-import sys
-
-from random import randint
 from uuid import uuid4
 
 # import interfaces
@@ -32,6 +29,7 @@
 
 # import packages
 from pyams_content.reference.pictograms import Pictogram
+from pyams_file.zmi.image import render_image
 from pyams_form.form import AJAXAddForm, ajax_config
 from pyams_i18n.column import I18nAttrColumn
 from pyams_pagelet.pagelet import pagelet_config
@@ -40,10 +38,10 @@
 from pyams_skin.viewlet.toolbar import ToolbarAction
 from pyams_utils.adapter import adapter_config
 from pyams_utils.traversing import get_parent
-from pyams_utils.url import absolute_url
 from pyams_viewlet.viewlet import viewlet_config
 from pyams_zmi.form import AdminDialogAddForm, AdminDialogEditForm
-from pyramid.httpexceptions import HTTPNotFound, HTTPBadRequest
+from pyramid.httpexceptions import HTTPNotFound
+from pyramid.renderers import render, render_to_response
 from pyramid.response import Response
 from pyramid.view import view_config
 from z3c.form import field
@@ -141,8 +139,8 @@
     def getValue(self, obj):
         image = II18n(obj).query_attribute('image', request=self.request)
         if image:
-            timestamp = randint(0, sys.maxsize)
-            return '<img src="{0}?_={1}" />'.format(absolute_url(image, self.request, '++thumb++32x32'), timestamp)
+            return render_image(image, 32, 32, timestamp=True)
+        return '--'
 
 
 @adapter_config(name='name', context=(IPictogramTable, IAdminLayer, PictogramTableContentsTable), provides=IColumn)
@@ -163,16 +161,22 @@
 
 @view_config(name='get-pictogram-header.html', context=IPictogramTable, request_type=IPyAMSLayer,
              permission=VIEW_SYSTEM_PERMISSION)
-def get_pictogram_label(request):
-    """Get alternate label associated with a given pictogram"""
+def get_pictogram_header_view(request):
+    """View used to get thumbnail and alternate label associated with a given pictogram"""
+    translate = request.localizer.translate
     name = request.params.get('value')
-    if not name:
-        raise HTTPBadRequest()
-    translate = request.localizer.translate
-    if name == '--NOVALUE--':
+    if (not name) or (name == '--NOVALUE--'):
         return Response(translate(_("Default header: --")))
     pictogram = request.context.get(name)
     if pictogram is None:
         raise HTTPNotFound()
-    return Response(translate(_("Default header: {0}")).format(
-        II18n(pictogram).query_attribute('header', request=request) or '--'))
+    return render_to_response('templates/pictogram-header.pt', {
+        'context': pictogram
+    }, request=request)
+
+
+def get_pictogram_header(pictogram, request=None):
+    """Get thumbnail and alternate label associated with a given pictogram"""
+    return render('templates/pictogram-header.pt', {
+        'context': pictogram
+    }, request=request)
--- a/src/pyams_content/reference/pictograms/zmi/templates/manager-selection.pt	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/reference/pictograms/zmi/templates/manager-selection.pt	Wed Jun 27 16:42:01 2018 +0200
@@ -22,9 +22,9 @@
 					</a>
 				</div>
 				<div class="width-50 text-center pull-left margin-right-10">
-					<img tal:define="img i18n:pictogram.image"
-						 tal:condition="img"
-						 tal:attributes="src extension:absolute_url(img, '++thumb++48x48')" />
+					<tal:var define="img i18n:pictogram.image"
+							 condition="img"
+							 content="structure extension:thumbnail(img, 48, 48)" />
 				</div>
 				<span tal:content="i18n:pictogram.title">Title</span>
 			</div>
@@ -49,9 +49,9 @@
 					</a>
 				</div>
 				<div class="width-50 text-center pull-left margin-right-10">
-					<img tal:define="img i18n:pictogram.image"
-						 tal:condition="img"
-						 tal:attributes="src extension:absolute_url(img, '++thumb++48x48')" />
+					<tal:var define="img i18n:pictogram.image"
+							 condition="img"
+							 content="structure extension:thumbnail(img, 48, 48)" />
 				</div>
 				<span tal:content="i18n:pictogram.title">Title</span>
 			</div>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/reference/pictograms/zmi/templates/pictogram-header.pt	Wed Jun 27 16:42:01 2018 +0200
@@ -0,0 +1,9 @@
+<div class="padding-5" i18n:domain="pyams_content">
+	<tal:var define="image i18n:context.image"
+			 condition="image"
+			 content="structure extension:thumbnail(image, 32, 32)" />
+	<span class="padding-left-5" tal:define="header i18n:context.header"
+		  i18n:translate="">Default header:
+		<strong i18n:name="header" tal:content="header or '--'" />
+	</span>
+</div>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/reference/pictograms/zmi/widget.py	Wed Jun 27 16:42:01 2018 +0200
@@ -0,0 +1,69 @@
+#
+# Copyright (c) 2008-2018 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.reference.pictograms import IPictogramTable
+from pyams_utils.interfaces.data import IObjectData
+
+# import packages
+from pyams_content.reference.pictograms.zmi import get_pictogram_header
+from pyams_utils.registry import query_utility
+from pyams_utils.url import absolute_url
+from z3c.form.browser.select import SelectWidget
+from z3c.form.widget import FieldWidget
+from zope.interface import implementer
+
+from pyams_content import _
+
+
+@implementer(IObjectData)
+class PictogramSelectWidget(SelectWidget):
+    """Pictogram selection widget"""
+
+    pictograms = None
+    label_id = None
+    after_widget_notice = None
+
+    def update(self):
+        super(PictogramSelectWidget, self).update()
+        self.label_id = '{0}_header'.format(self.id)
+        self.pictograms = query_utility(IPictogramTable)
+        if self.value and (self.pictograms is not None):
+            pictogram = self.pictograms.get(self.value[0])
+            if pictogram is not None:
+                self.after_widget_notice = '<span id="{0}" class="text-info">{1}</span>'.format(
+                    self.label_id,
+                    get_pictogram_header(pictogram, self.request))
+                return
+        self.after_widget_notice = '<span id="{0}" class="text-info">{1}</span>'.format(
+            self.label_id,
+            self.request.localizer.translate(_("Default header: --")))
+
+    @property
+    def object_data(self):
+        return {
+            'ams-change-handler': 'MyAMS.helpers.select2ChangeHelper',
+            'ams-stop-propagation': 'true',
+            'ams-select2-helper-type': 'html',
+            'ams-select2-helper-url': absolute_url(self.pictograms, self.request, 'get-pictogram-header.html'),
+            'ams-select2-helper-target': '#{0}'.format(self.label_id)
+        }
+
+
+def PictogramSelectFieldWidget(field, request):
+    return FieldWidget(field, PictogramSelectWidget(request))
--- a/src/pyams_content/root/__init__.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/root/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -27,10 +27,11 @@
 from pyams_content.root.interfaces import ISiteRootRoles, ISiteRootConfiguration, ISiteRoot, \
     ISiteRootToolsConfiguration, ISiteRootBackOfficeConfiguration
 from pyams_form.interfaces.form import IFormContextPermissionChecker
-from pyams_portal.interfaces import IPortalContext
-from pyams_security.interfaces import IDefaultProtectionPolicy, IGrantedRoleEvent, ISecurityManager
+from pyams_portal.interfaces import IPortalContext, DESIGNER_ROLE
+from pyams_security.interfaces import IDefaultProtectionPolicy, IGrantedRoleEvent, ISecurityManager, SYSTEM_ADMIN_ROLE
+from pyams_skin.interfaces.configuration import IConfiguration, IBackOfficeConfiguration
 from pyams_utils.interfaces import MANAGE_SYSTEM_PERMISSION
-from pyams_utils.interfaces.site import IConfigurationFactory, IBackOfficeConfigurationFactory, ISiteRootFactory
+from pyams_utils.interfaces.site import ISiteRootFactory
 
 # import packages
 from persistent import Persistent
@@ -39,26 +40,31 @@
 from pyams_skin.configuration import Configuration, BackOfficeConfiguration
 from pyams_skin.skin import UserSkinnableContent
 from pyams_utils.adapter import adapter_config, ContextAdapter, get_annotation_adapter
+from pyams_utils.factory import factory_config
 from pyams_utils.registry import get_utility, utility_config
 from pyams_utils.site import BaseSiteRoot
 from pyams_utils.traversing import get_parent
 from pyramid.events import subscriber
 from zope.interface import implementer
 
+from pyams_content import _
+
 
 @implementer(IDefaultProtectionPolicy, ISiteRoot, ISiteRootRoles, IPortalContext,
              IIllustrationTarget, IHeaderTarget, IFooterTarget, IAlertTarget, IPreviewTarget)
 class SiteRoot(ProtectedObject, BaseSiteRoot, UserSkinnableContent):
     """Main site root"""
 
-    __roles__ = ('system.Manager', WEBMASTER_ROLE, OPERATOR_ROLE, 'pyams.TemplatesManager')
+    __roles__ = (SYSTEM_ADMIN_ROLE, WEBMASTER_ROLE, OPERATOR_ROLE, DESIGNER_ROLE)
 
     roles_interface = ISiteRootRoles
 
     managers = RolePrincipalsFieldProperty(ISiteRootRoles['managers'])
     webmasters = RolePrincipalsFieldProperty(ISiteRootRoles['webmasters'])
     operators = RolePrincipalsFieldProperty(ISiteRootRoles['operators'])
-    templates_managers = RolePrincipalsFieldProperty(ISiteRootRoles['templates_managers'])
+    designers = RolePrincipalsFieldProperty(ISiteRootRoles['designers'])
+
+    content_name = _("Site root")
 
 
 @utility_config(provides=ISiteRootFactory)
@@ -70,30 +76,22 @@
 
 
 @implementer(ISiteRootConfiguration)
+@factory_config(provided=IConfiguration)
 class SiteRootConfiguration(Configuration):
     """Site root configuration"""
 
 
-@adapter_config(context=ISiteRoot, provides=IConfigurationFactory)
-def site_root_configuration_factory(context):
-    return SiteRootConfiguration
-
-
 @implementer(ISiteRootBackOfficeConfiguration)
+@factory_config(provided=IBackOfficeConfiguration)
 class SiteRootBackOfficeConfiguration(BackOfficeConfiguration):
     """Site root back-office configuration"""
 
 
-@adapter_config(context=ISiteRoot, provides=IBackOfficeConfigurationFactory)
-def site_root_back_office_configuration_factory(context):
-    return SiteRootBackOfficeConfiguration
-
-
 @subscriber(IGrantedRoleEvent)
 def handle_granted_role(event):
     """Add principals to operators group when a role is granted"""
     role_id = event.role_id
-    if (role_id == 'pyams.Operator') or (not role_id.startswith('pyams.')):
+    if (role_id == OPERATOR_ROLE) or (not role_id.startswith('pyams.')):
         return
     root = get_parent(event.object, ISiteRoot)
     if not root.operators:
--- a/src/pyams_content/root/interfaces/__init__.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/root/interfaces/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -16,6 +16,9 @@
 # import standard library
 
 # import interfaces
+from pyams_content.interfaces import WEBMASTER_ROLE, OPERATOR_ROLE
+from pyams_portal.interfaces import DESIGNER_ROLE
+from pyams_security.interfaces import SYSTEM_ADMIN_ROLE
 from pyams_skin.interfaces.configuration import IConfiguration, IBackOfficeConfiguration
 from pyams_utils.interfaces.site import ISiteRoot as ISiteRootBase
 
@@ -34,20 +37,20 @@
     """Main site roles"""
 
     managers = PrincipalsSet(title=_("Site managers"),
-                             role_id='system.Manager',
+                             role_id=SYSTEM_ADMIN_ROLE,
                              required=False)
 
     webmasters = PrincipalsSet(title=_("Webmasters"),
-                               role_id='pyams.Webmaster',
+                               role_id=WEBMASTER_ROLE,
                                required=False)
 
-    templates_managers = PrincipalsSet(title=_("Templates managers"),
-                                       role_id='pyams.TemplatesManager',
-                                       required=False)
+    designers = PrincipalsSet(title=_("Templates managers"),
+                              role_id=DESIGNER_ROLE,
+                              required=False)
 
     operators = Principal(title=_("Operators group"),
                           description=_("Name of group containing all roles owners"),
-                          role_id='pyams.Operator',
+                          role_id=OPERATOR_ROLE,
                           required=False)
 
 
--- a/src/pyams_content/root/zmi/__init__.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/root/zmi/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -25,7 +25,8 @@
 from pyams_content.skin.zmi.interfaces import IDashboardMenu, IMyDashboardMenu, IAllContentsMenu
 from pyams_i18n.interfaces import II18n
 from pyams_skin.interfaces import IInnerPage, IPageHeader
-from pyams_skin.interfaces.configuration import IBackOfficeConfiguration
+from pyams_skin.interfaces.configuration import IBackOfficeConfiguration, IConfiguration
+from pyams_skin.interfaces.container import ITableElementName, ITableElementEditor
 from pyams_skin.interfaces.viewlet import IBreadcrumbItem
 from pyams_skin.layer import IPyAMSLayer
 from pyams_utils.interfaces import VIEW_SYSTEM_PERMISSION
@@ -45,11 +46,11 @@
 from pyams_pagelet.pagelet import pagelet_config
 from pyams_skin.container import ContainerView
 from pyams_skin.page import DefaultPageHeaderAdapter
-from pyams_skin.table import I18nColumn
+from pyams_skin.table import I18nColumn, DefaultElementEditorAdapter
 from pyams_skin.viewlet.breadcrumb import BreadcrumbItem
 from pyams_skin.viewlet.menu import MenuItem
 from pyams_template.template import template_config
-from pyams_utils.adapter import adapter_config, ContextRequestViewAdapter
+from pyams_utils.adapter import adapter_config, ContextRequestViewAdapter, ContextRequestAdapter
 from pyams_utils.list import unique
 from pyams_utils.registry import get_utility, get_all_utilities_registered_for
 from pyams_viewlet.manager import viewletmanager_config
@@ -77,6 +78,24 @@
     label = _("Home")
 
 
+@adapter_config(context=(ISiteRoot, IPyAMSLayer), provides=ITableElementName)
+class SiteRootTableElementNameAdapter(ContextRequestAdapter):
+    """Site root table element name adapter"""
+
+    @property
+    def name(self):
+        configuration = IConfiguration(self.context)
+        return II18n(configuration).query_attribute('short_title', request=self.request)
+
+
+@adapter_config(context=(ISiteRoot, IAdminLayer, Interface), provides=ITableElementEditor)
+class SiteRootTableElementEditorAdapter(DefaultElementEditorAdapter):
+    """Site root table element editor adapter"""
+
+    view_name = 'admin#site-tree.html'
+    modal_target = False
+
+
 #
 # Main dashboard menu
 #
@@ -153,6 +172,7 @@
         for tool in get_all_utilities_registered_for(IBaseSharedTool):
             workflow = IWorkflow(tool)
             query = Eq(catalog['parents'], intids.register(tool)) & \
+                    Any(catalog['content_type'], CONTENT_TYPES.keys()) & \
                     Any(catalog['workflow_state'], workflow.waiting_states)
             params = params | query if params else query
         return filter(self.check_access,
@@ -199,6 +219,7 @@
         for tool in get_all_utilities_registered_for(IBaseSharedTool):
             workflow = IWorkflow(tool)
             query = Eq(catalog['parents'], intids.register(tool)) & \
+                    Any(catalog['content_type'], CONTENT_TYPES.keys()) & \
                     Any(catalog['workflow_state'], workflow.waiting_states) & \
                     Eq(catalog['workflow_principal'], self.request.principal.id)
             params = params | query if params else query
@@ -360,6 +381,7 @@
         for tool in get_all_utilities_registered_for(IBaseSharedTool):
             workflow = IWorkflow(tool)
             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)),
                         Eq(catalog['workflow_state'], workflow.initial_state))
@@ -421,6 +443,7 @@
         for tool in get_all_utilities_registered_for(IBaseSharedTool):
             workflow = IWorkflow(tool)
             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)),
                         Any(catalog['workflow_state'], workflow.waiting_states))
@@ -482,6 +505,7 @@
         for tool in get_all_utilities_registered_for(IBaseSharedTool):
             workflow = IWorkflow(tool)
             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)),
                         Any(catalog['workflow_state'], workflow.published_states))
@@ -543,6 +567,7 @@
         for tool in get_all_utilities_registered_for(IBaseSharedTool):
             workflow = IWorkflow(tool)
             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)),
                         Any(catalog['workflow_state'], workflow.retired_states))
@@ -606,6 +631,7 @@
         for tool in get_all_utilities_registered_for(IBaseSharedTool):
             workflow = IWorkflow(tool)
             query = And(Eq(catalog['parents'], intids.register(tool)),
+                        Any(catalog['content_type'], CONTENT_TYPES.keys()) & \
                         Or(Eq(catalog['role:owner'], principal_id),
                            Eq(catalog['role:contributor'], principal_id)),
                         Any(catalog['workflow_state'], workflow.archived_states))
@@ -695,6 +721,7 @@
         for tool in get_all_utilities_registered_for(IBaseSharedTool):
             workflow = IWorkflow(tool)
             query = And(Eq(catalog['parents'], intids.register(tool)),
+                        Any(catalog['content_type'], CONTENT_TYPES.keys()) & \
                         Any(catalog['workflow_state'], workflow.published_states))
             params = params | query if params else query
         return unique(CatalogResultSet(CatalogQuery(catalog).query(params,
@@ -800,4 +827,7 @@
     weight = 1
 
     def getValue(self, obj):
-        return self.request.localizer.translate(obj.content_name)
+        try:
+            return self.request.localizer.translate(obj.content_name)
+        except AttributeError:
+            return '--'
--- a/src/pyams_content/root/zmi/templates/dashboard.pt	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/root/zmi/templates/dashboard.pt	Wed Jun 27 16:42:01 2018 +0200
@@ -25,7 +25,9 @@
 							<label class="input">
 								<button type="submit" class="icon-append fa fa-fw fa-search no-border no-padding"
 										data-ams-form-hide-loading="true"></button>
-								<input type="text" name="query" placeholder="Quick search..." i18n:attributes="placeholder" />
+								<input type="text" name="query"
+									   placeholder="Quick search..." i18n:attributes="placeholder"
+									   data-ams-events-handlers='{"keyup": "MyAMS.helpers.clearSearchTarget"}' />
 							</label>
 						</div>
 						<div class="col-md-6">
@@ -36,7 +38,8 @@
 				</form>
 			</div>
 		</div>
-		<div id="search_results" tal:define="global dashboard_length 0">
+		<div id="search_results"></div>
+		<tal:var define="global dashboard_length 0">
 			<tal:loop repeat="table view.tables">
 				<tal:if condition="table.values">
 					<tal:var content="structure table.render()" />
@@ -46,6 +49,6 @@
 			<div tal:condition="not:dashboard_length" class="alert alert-info" i18n:translate="">
 				You are not actually concerned by any content.
 			</div>
-		</div>
+		</tal:var>
 	</div>
 </div>
--- a/src/pyams_content/shared/blog/__init__.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/shared/blog/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -16,7 +16,7 @@
 # import standard library
 
 # import interfaces
-from pyams_content.component.illustration import IIllustrationTarget
+from pyams_content.component.illustration import IIllustrationTarget, ILinkIllustrationTarget
 from pyams_content.component.paragraph.interfaces import IParagraphContainerTarget
 from pyams_content.component.theme.interfaces import IThemesTarget
 from pyams_content.features.preview.interfaces import IPreviewTarget
@@ -31,8 +31,8 @@
 from zope.interface import implementer, provider
 
 
-@implementer(IWfBlogPost, IParagraphContainerTarget, IThemesTarget,
-             IIllustrationTarget, IPreviewTarget, IReviewTarget)
+@implementer(IWfBlogPost, IParagraphContainerTarget, IThemesTarget, IIllustrationTarget,
+             ILinkIllustrationTarget, IPreviewTarget, IReviewTarget)
 class WfBlogPost(WfSharedContent):
     """Base blog post"""
 
--- a/src/pyams_content/shared/blog/interfaces/__init__.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/shared/blog/interfaces/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -16,8 +16,8 @@
 # import standard library
 
 # import interfaces
-from pyams_content.shared.common.interfaces import ISharedSite, IBaseSharedTool, IWfSharedContent, ISharedContent, \
-    IDeletableElement
+from pyams_content.shared.common.interfaces import ISharedSite, IBaseSharedTool, ISharedContent, \
+    IDeletableElement, IWfSharedContentPortalContext
 from pyams_sequence.interfaces import ISequentialIdTarget
 from pyams_workflow.interfaces import IWorkflowPublicationSupport
 from zope.container.interfaces import IContainer
@@ -33,7 +33,7 @@
 BLOG_CONTENT_NAME = _("Blog post")
 
 
-class IWfBlogPost(IWfSharedContent):
+class IWfBlogPost(IWfSharedContentPortalContext):
     """Blog topic interface"""
 
 
--- a/src/pyams_content/shared/blog/manager.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/shared/blog/manager.py	Wed Jun 27 16:42:01 2018 +0200
@@ -16,7 +16,7 @@
 # import standard library
 
 # import interfaces
-from pyams_content.component.illustration import IIllustrationTarget
+from pyams_content.component.illustration import IIllustrationTarget, ILinkIllustrationTarget
 from pyams_content.component.paragraph.interfaces import IParagraphFactorySettings
 from pyams_content.component.theme.interfaces import IThemesManagerTarget
 from pyams_content.features.footer.interfaces import IFooterTarget
@@ -27,7 +27,6 @@
 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
 from zope.lifecycleevent.interfaces import IObjectAddedEvent, IObjectRemovedEvent
 
@@ -55,7 +54,7 @@
 
 
 @implementer(IBlogManager, IParagraphFactorySettings, IThemesManagerTarget, IPictogramManagerTarget,
-             IIllustrationTarget, IPortalContext, IHeaderTarget, IFooterTarget, IPreviewTarget, IAttributeAnnotatable)
+             IIllustrationTarget, ILinkIllustrationTarget, IHeaderTarget, IFooterTarget, IPortalContext, IPreviewTarget)
 class BlogManager(Folder, BaseSharedTool, UserSkinnableContent):
     """Nlog manager class"""
 
--- a/src/pyams_content/shared/blog/zmi/manager.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/shared/blog/zmi/manager.py	Wed Jun 27 16:42:01 2018 +0200
@@ -186,6 +186,6 @@
     def updateWidgets(self, prefix=None):
         super(BlogManagerWorkflowPublicationEditForm, self).updateWidgets(prefix)
         if 'publication_effective_date' in self.widgets:
-            widget = self.widgets['publication_effective_date']
-            if not widget.value:
-                widget.value = tztime(datetime.utcnow()).strftime('%d/%m/%y %H:%M')
+            pub_info = IWorkflowPublicationInfo(self.context)
+            if pub_info.publication_effective_date is None:
+                self.widgets['publication_effective_date'].value = tztime(datetime.utcnow()).strftime('%d/%m/%y %H:%M')
--- a/src/pyams_content/shared/common/__init__.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/shared/common/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -17,12 +17,14 @@
 
 # import interfaces
 from hypatia.interfaces import ICatalog
-from pyams_content.interfaces import IBaseContentInfo
+from pyams_content.interfaces import IBaseContentInfo, OWNER_ROLE, MANAGER_ROLE, CONTRIBUTOR_ROLE, READER_ROLE, \
+    GUEST_ROLE
 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, IWfSharedContentFactory
 from pyams_i18n.interfaces import II18nManager, II18n
+from pyams_portal.interfaces import DESIGNER_ROLE
 from pyams_security.interfaces import IDefaultProtectionPolicy
 from pyams_sequence.interfaces import ISequentialIdTarget, ISequentialIdInfo
 from pyams_utils.interfaces import VIEW_PERMISSION
@@ -113,12 +115,13 @@
 class WfSharedContent(ProtectedObject, Persistent, Contained, I18nManagerMixin):
     """Shared data content class"""
 
-    __roles__ = ('pyams.Owner', 'pyams.Manager', 'pyams.Contributors', 'pyams.Reader', 'pyams.Guest')
+    __roles__ = (OWNER_ROLE, MANAGER_ROLE, CONTRIBUTOR_ROLE, DESIGNER_ROLE, READER_ROLE, GUEST_ROLE)
     roles_interface = IWfSharedContentRoles
 
     owner = RolePrincipalsFieldProperty(IWfSharedContentRoles['owner'])
     managers = RolePrincipalsFieldProperty(IWfSharedContentRoles['managers'])
     contributors = RolePrincipalsFieldProperty(IWfSharedContentRoles['contributors'])
+    designers = RolePrincipalsFieldProperty(IWfSharedContentRoles['designers'])
     readers = RolePrincipalsFieldProperty(IWfSharedContentRoles['readers'])
     guests = RolePrincipalsFieldProperty(IWfSharedContentRoles['guests'])
 
--- a/src/pyams_content/shared/common/interfaces/__init__.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/shared/common/interfaces/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -18,7 +18,7 @@
 # import interfaces
 from pyams_content.interfaces import IBaseContent, MANAGE_CONTENT_PERMISSION, OWNER_ROLE, MANAGER_ROLE, \
     READER_ROLE, GUEST_ROLE, WEBMASTER_ROLE, PILOT_ROLE, CONTRIBUTOR_ROLE
-from pyams_content.root.interfaces import ISiteRoot
+from pyams_portal.interfaces import IPortalContext, DESIGNER_ROLE
 from pyams_workflow.interfaces import IWorkflowManagedContent
 from zope.container.interfaces import IContainer
 
@@ -65,17 +65,22 @@
                                  role_id=CONTRIBUTOR_ROLE,
                                  required=False)
 
+    designers = PrincipalsSet(title=_("Designers"),
+                              description=_("Designers are users which are allowed to manage presentation templates"),
+                              role_id=DESIGNER_ROLE,
+                              required=False)
+
 
 class ISharedSite(IBaseContent, IDeletableElement):
     """Shared site interface"""
 
-    containers(ISiteRoot)
+    containers('pyams_content.root.interfaces.ISiteRoot')
 
 
 class ISharedToolContainer(IBaseContent, IContainer):
     """Shared tools container"""
 
-    containers(ISiteRoot)
+    containers('pyams_content.root.interfaces.ISiteRoot')
     contains('.ISharedTool')
 
 
@@ -102,6 +107,10 @@
     shared_content_factory = Attribute("Shared data factory")
 
 
+class ISharedToolPortalContext(ISharedTool, IPortalContext):
+    """Shared tool with portal context"""
+
+
 class ISharedToolRoles(IBaseContentManagerRoles):
     """Shared tool roles"""
 
@@ -157,6 +166,10 @@
                    required=False)
 
 
+class IWfSharedContentPortalContext(IWfSharedContent, IPortalContext):
+    """Shared content with portal support"""
+
+
 class IWfSharedContentFactory(Interface):
     """Shared content factory interface"""
 
@@ -180,9 +193,14 @@
     contributors = PrincipalsSet(title=_("Contributors"),
                                  description=_("Contributors are users which are allowed to update this content in "
                                                "addition to it's owner"),
-                                 role_id='pyams.Contributor',
+                                 role_id=CONTRIBUTOR_ROLE,
                                  required=False)
 
+    designers = PrincipalsSet(title=_("Designers"),
+                              description=_("Designers are users which are allowed to manage presentation templates"),
+                              role_id=DESIGNER_ROLE,
+                              required=False)
+
     readers = PrincipalsSet(title=_("Readers"),
                             description=_("Readers are users which are asked to verify and comment contents before "
                                           "they are published"),
--- a/src/pyams_content/shared/common/interfaces/types.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/shared/common/interfaces/types.py	Wed Jun 27 16:42:01 2018 +0200
@@ -17,6 +17,7 @@
 
 # import interfaces
 from pyams_content.shared.common.interfaces import ISharedTool
+from pyams_portal.interfaces import IPortalContext
 from zope.container.interfaces import IContainer
 from zope.location.interfaces import ILocation
 
@@ -100,3 +101,7 @@
     """Shared tool containing typed data"""
 
     shared_content_types_fields = Attribute("Content fields interface")
+
+
+class ITypedSharedToolPortalContext(ITypedSharedTool, IPortalContext):
+    """Typed shared tool with portal context"""
--- a/src/pyams_content/shared/common/manager.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/shared/common/manager.py	Wed Jun 27 16:42:01 2018 +0200
@@ -19,6 +19,7 @@
 from pyams_content.interfaces import WEBMASTER_ROLE, PILOT_ROLE, MANAGER_ROLE, CONTRIBUTOR_ROLE
 from pyams_content.shared.common.interfaces import ISharedToolContainer, IBaseSharedTool, ISharedTool, ISharedToolRoles, \
     ISharedContentFactory
+from pyams_portal.interfaces import DESIGNER_ROLE
 from pyams_security.interfaces import IDefaultProtectionPolicy
 from pyams_workflow.interfaces import IWorkflow
 from zope.annotation.interfaces import IAttributeAnnotatable
@@ -42,11 +43,11 @@
     short_name = FieldProperty(ISharedToolContainer['short_name'])
 
 
-@implementer(IDefaultProtectionPolicy, IBaseSharedTool, ISharedToolRoles, IAttributeAnnotatable)
+@implementer(IDefaultProtectionPolicy, IBaseSharedTool, ISharedToolRoles)
 class BaseSharedTool(ProtectedObject, I18nManagerMixin):
     """Base shared tool class"""
 
-    __roles__ = (WEBMASTER_ROLE, PILOT_ROLE, MANAGER_ROLE, CONTRIBUTOR_ROLE)
+    __roles__ = (WEBMASTER_ROLE, PILOT_ROLE, MANAGER_ROLE, DESIGNER_ROLE, CONTRIBUTOR_ROLE)
 
     roles_interface = ISharedToolRoles
 
@@ -54,6 +55,7 @@
     pilots = RolePrincipalsFieldProperty(ISharedToolRoles['pilots'])
     managers = RolePrincipalsFieldProperty(ISharedToolRoles['managers'])
     contributors = RolePrincipalsFieldProperty(ISharedToolRoles['contributors'])
+    designers = RolePrincipalsFieldProperty(ISharedToolRoles['designers'])
 
     title = FieldProperty(IBaseSharedTool['title'])
     short_name = FieldProperty(IBaseSharedTool['short_name'])
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/shared/common/portal.py	Wed Jun 27 16:42:01 2018 +0200
@@ -0,0 +1,43 @@
+#
+# Copyright (c) 2008-2018 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.shared.common.interfaces import IBaseSharedTool, IWfSharedContentPortalContext
+from pyams_portal.interfaces import IPortalPage, PORTAL_PAGE_KEY
+
+# import packages
+from pyams_portal.page import PortalPage
+from pyams_utils.adapter import adapter_config, get_annotation_adapter
+from pyams_utils.traversing import get_parent
+
+
+class SharedContentPortalPage(PortalPage):
+    """Shared content portal page"""
+
+    @property
+    def can_inherit(self):
+        return IPortalPage(self.parent).template is not None
+
+    @property
+    def parent(self):
+        return get_parent(self, IBaseSharedTool, allow_context=False)
+
+
+@adapter_config(context=IWfSharedContentPortalContext, provides=IPortalPage)
+def shared_content_portal_page_adapter(context):
+    """Shared content portal page adapter"""
+    return get_annotation_adapter(context, PORTAL_PAGE_KEY, SharedContentPortalPage)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/shared/common/portlet/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -0,0 +1,19 @@
+#
+# Copyright (c) 2008-2018 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/shared/common/portlet/content/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -0,0 +1,46 @@
+#
+# Copyright (c) 2008-2018 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.shared.common.portlet.content.interfaces import ISharedContentPortletSettings
+from pyams_utils.interfaces import VIEW_PERMISSION
+
+# import packages
+from pyams_portal.portlet import PortletSettings, portlet_config, Portlet
+from pyams_utils.factory import factory_config
+from zope.interface import implementer
+
+from pyams_content import _
+
+
+SHARED_CONTENT_PORTLET_NAME = 'pyams_content.portlet.content'
+
+
+@implementer(ISharedContentPortletSettings)
+@factory_config(provided=ISharedContentPortletSettings)
+class SharedContentPortletSettings(PortletSettings):
+    """Shared content portlet persistent settings"""
+
+
+@portlet_config(permission=VIEW_PERMISSION)
+class SharedContentPortlet(Portlet):
+    """Shared content portlet"""
+
+    name = SHARED_CONTENT_PORTLET_NAME
+    label = _("Context content")
+
+    settings_factory = ISharedContentPortletSettings
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/shared/common/portlet/content/interfaces/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -0,0 +1,25 @@
+#
+# Copyright (c) 2008-2018 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_portal.interfaces import IPortletSettings
+
+# import packages
+
+
+class ISharedContentPortletSettings(IPortletSettings):
+    """Shared content portlet settings interface"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/shared/common/portlet/content/skin/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -0,0 +1,54 @@
+#
+# Copyright (c) 2008-2018 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.shared.common.portlet.content.interfaces import ISharedContentPortletSettings
+from pyams_content.features.renderer.interfaces import ISharedContentRenderer
+from pyams_portal.interfaces import IPortalContext, IPortletRenderer
+from pyams_skin.layer import IPyAMSLayer
+
+# import packages
+from pyams_portal.portlet import PortletRenderer
+from pyams_utils.adapter import adapter_config
+from zope.interface import Interface
+
+from pyams_content import _
+
+
+@adapter_config(context=(IPortalContext, IPyAMSLayer, Interface, ISharedContentPortletSettings),
+                provides=IPortletRenderer)
+class SharedContentPortletRenderer(PortletRenderer):
+    """Shared content portlet renderer"""
+
+    label = _("Default content renderer")
+
+    def __init__(self, context, request, view, settings):
+        super(SharedContentPortletRenderer, self).__init__(context, request, view, settings)
+        registry = self.request.registry
+        self.renderers = [adapter for name, adapter in sorted(registry.getAdapters((self.context, self.request),
+                                                                                   ISharedContentRenderer),
+                                                              key=lambda x: x[1].weight)]
+
+    def update(self):
+        super(SharedContentPortletRenderer, self).update()
+        [renderer.update() for renderer in self.renderers]
+
+    def render(self):
+        result = ''
+        for renderer in self.renderers:
+            if renderer is not None:
+                result += renderer.render()
+        return result
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/shared/common/portlet/content/zmi/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -0,0 +1,52 @@
+#
+# Copyright (c) 2008-2018 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.shared.common.portlet.content.interfaces import ISharedContentPortletSettings
+from pyams_pagelet.interfaces import IPagelet
+from pyams_portal.interfaces import IPortletPreviewer
+from pyams_skin.layer import IPyAMSLayer
+from pyams_utils.interfaces import VIEW_SYSTEM_PERMISSION
+
+# import packages
+from pyams_form.form import AJAXEditForm
+from pyams_pagelet.pagelet import pagelet_config
+from pyams_portal.portlet import PortletPreviewer
+from pyams_portal.zmi.portlet import PortletSettingsEditor
+from pyams_template.template import template_config
+from pyams_utils.adapter import adapter_config
+from zope.interface import Interface
+
+
+@pagelet_config(name='properties.html', context=ISharedContentPortletSettings, layer=IPyAMSLayer,
+                permission=VIEW_SYSTEM_PERMISSION)
+class SharedContentPortletSettingsEditor(PortletSettingsEditor):
+    """Shared content portlet settings editor"""
+
+    settings = ISharedContentPortletSettings
+
+
+@adapter_config(name='properties.json', context=(ISharedContentPortletSettings, IPyAMSLayer), provides=IPagelet)
+class SharedContentPortletConfigurationAJAXEditor(AJAXEditForm, SharedContentPortletSettingsEditor):
+    """Shared content portlet settings editor, JSON renderer"""
+
+
+@adapter_config(context=(Interface, IPyAMSLayer, Interface, ISharedContentPortletSettings),
+                provides=IPortletPreviewer)
+@template_config(template='preview.pt', layer=IPyAMSLayer)
+class SharedContentPortletPreviewer(PortletPreviewer):
+    """Shared content portlet previewer"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/shared/common/portlet/content/zmi/preview.pt	Wed Jun 27 16:42:01 2018 +0200
@@ -0,0 +1,3 @@
+<tal:var define="settings view.settings" i18n:domain="pyams_content">
+	<span class="padding-5" i18n:translate="">This is where the content will be displayed!!</span>
+</tal:var>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/shared/common/skin/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -0,0 +1,20 @@
+#
+# Copyright (c) 2008-2018 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/shared/common/skin/oid.py	Wed Jun 27 16:42:01 2018 +0200
@@ -0,0 +1,56 @@
+#
+# Copyright (c) 2008-2018 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_sequence.interfaces import ISequentialIntIds
+from pyams_skin.layer import IPyAMSUserLayer
+from pyams_utils.interfaces.url import DISPLAY_CONTEXT
+from pyams_workflow.interfaces import IWorkflow, IWorkflowVersions
+from zope.traversing.interfaces import ITraversable
+
+# import packages
+from pyams_sequence.utility import get_reference_target
+from pyams_utils.adapter import adapter_config, ContextRequestAdapter
+from pyams_utils.registry import get_utility
+from pyramid.exceptions import NotFound
+from zope.interface import Interface
+
+
+@adapter_config(name='oid', context=(Interface, IPyAMSUserLayer), provides=ITraversable)
+class OidTraverser(ContextRequestAdapter):
+    """++oid++ traverser"""
+
+    def traverse(self, name, furtherpath=None):
+        if not name:
+            raise NotFound()
+        if '::' in name:
+            oid, label = name.split('::', 1)
+        else:
+            oid = name
+        sequence = get_utility(ISequentialIntIds)
+        reference = sequence.get_full_oid(oid)
+        target = get_reference_target(reference)
+        if target is not None:
+            workflow = IWorkflow(target, None)
+            if workflow is not None:
+                versions = IWorkflowVersions(target).get_versions(workflow.published_states, sort=True)
+                if versions:
+                    target = versions[-1]
+        if target is not None:
+            self.request.annotations[DISPLAY_CONTEXT] = self.context
+            return target
+        raise NotFound()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/shared/common/skin/url.py	Wed Jun 27 16:42:01 2018 +0200
@@ -0,0 +1,37 @@
+#
+# Copyright (c) 2008-2018 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.shared.common import IWfSharedContent
+from pyams_sequence.interfaces import ISequentialIdInfo
+from pyams_skin.layer import IPyAMSUserLayer
+from pyams_utils.interfaces.url import IRelativeURL
+
+# import packages
+from pyams_utils.adapter import adapter_config, ContextRequestAdapter
+from pyams_utils.url import absolute_url
+
+
+@adapter_config(context=(IWfSharedContent, IPyAMSUserLayer), provides=IRelativeURL)
+class SharedContentRelativeUrlAdapter(ContextRequestAdapter):
+    """Shared content relative URL adapter"""
+
+    def get_url(self, display_context=None, view_name=None, query=None):
+        return absolute_url(display_context, self.request,
+                            '++oid++{0}::{1}.html'.format(
+                                ISequentialIdInfo(self.context).get_base_oid().strip(),
+                                self.context.content_url))
--- a/src/pyams_content/shared/common/zmi/dashboard.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/shared/common/zmi/dashboard.py	Wed Jun 27 16:42:01 2018 +0200
@@ -340,6 +340,7 @@
         catalog = get_utility(ICatalog)
         workflow = get_utility(IWorkflow, name=self.context.shared_content_workflow)
         params = Eq(catalog['parents'], intids.register(self.context)) & \
+                 Any(catalog['content_type'], CONTENT_TYPES.keys()) & \
                  Any(catalog['workflow_state'], workflow.waiting_states)
         return filter(self.check_access,
                       unique(map(lambda x: sorted(IWorkflowVersions(x).get_versions(IWorkflowState(x).state),
@@ -383,6 +384,7 @@
         catalog = get_utility(ICatalog)
         workflow = get_utility(IWorkflow, name=self.context.shared_content_workflow)
         params = Eq(catalog['parents'], intids.register(self.context)) & \
+                 Any(catalog['content_type'], CONTENT_TYPES.keys()) & \
                  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),
@@ -576,6 +578,7 @@
         catalog = get_utility(ICatalog)
         workflow = get_utility(IWorkflow, name=self.context.shared_content_workflow)
         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)),
                      Eq(catalog['workflow_state'], workflow.initial_state))
@@ -638,6 +641,7 @@
         catalog = get_utility(ICatalog)
         workflow = get_utility(IWorkflow, name=self.context.shared_content_workflow)
         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)),
                      Any(catalog['workflow_state'], workflow.waiting_states))
@@ -700,6 +704,7 @@
         catalog = get_utility(ICatalog)
         workflow = get_utility(IWorkflow, name=self.context.shared_content_workflow)
         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)),
                      Any(catalog['workflow_state'], workflow.published_states))
@@ -762,6 +767,7 @@
         catalog = get_utility(ICatalog)
         workflow = get_utility(IWorkflow, name=self.context.shared_content_workflow)
         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)),
                      Any(catalog['workflow_state'], workflow.retired_states))
@@ -826,6 +832,7 @@
         principal_id = self.request.principal.id
         workflow = get_utility(IWorkflow, name=self.context.shared_content_workflow)
         params = And(Eq(catalog['parents'], intids.register(self.context)),
+                     Any(catalog['content_type'], CONTENT_TYPES.keys()) & \
                      Or(Eq(catalog['role:owner'], principal_id),
                         Eq(catalog['role:contributor'], principal_id)),
                      Any(catalog['workflow_state'], workflow.archived_states))
@@ -916,6 +923,7 @@
         catalog = get_utility(ICatalog)
         workflow = get_utility(IWorkflow, name=self.context.shared_content_workflow)
         params = And(Eq(catalog['parents'], intids.register(self.context)),
+                     Any(catalog['content_type'], CONTENT_TYPES.keys()) & \
                      Any(catalog['workflow_state'], workflow.published_states))
         return unique(CatalogResultSet(CatalogQuery(catalog).query(params,
                                                                    limit=50,
--- a/src/pyams_content/shared/common/zmi/manager.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/shared/common/zmi/manager.py	Wed Jun 27 16:42:01 2018 +0200
@@ -120,7 +120,7 @@
 class SharedToolPropertiesHelpAdapter(FormHelp):
     """Shared tool properties help adapter"""
 
-    permission = MANAGE_TOOL_PERMISSION
+    permission = MANAGE_SYSTEM_PERMISSION
 
     header = _("WARNING")
     status = 'danger'
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/shared/common/zmi/portal.py	Wed Jun 27 16:42:01 2018 +0200
@@ -0,0 +1,72 @@
+#
+# Copyright (c) 2008-2018 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.shared.common import IWfSharedContent
+from pyams_content.shared.common.interfaces import IWfSharedContentPortalContext, ISharedToolPortalContext, ISharedTool
+from pyams_content.shared.common.interfaces.types import ITypedSharedToolPortalContext
+from pyams_form.interfaces.form import IFormHelp
+from pyams_portal.interfaces import MANAGE_TEMPLATE_PERMISSION
+from pyams_skin.layer import IPyAMSLayer
+from pyams_zmi.layer import IAdminLayer
+
+# import packages
+from pyams_form.form import ajax_config
+from pyams_form.help import FormHelp
+from pyams_pagelet.pagelet import pagelet_config
+from pyams_portal.zmi.page import PortalContextTemplatePropertiesEditForm
+from pyams_utils.adapter import adapter_config
+
+from pyams_content import _
+
+
+@pagelet_config(name='template-properties.html', context=ISharedToolPortalContext, layer=IPyAMSLayer,
+                permission=MANAGE_TEMPLATE_PERMISSION)
+@pagelet_config(name='template-properties.html', context=ITypedSharedToolPortalContext, layer=IPyAMSLayer,
+                permission=MANAGE_TEMPLATE_PERMISSION)
+@ajax_config(name='template-properties.json', context=ISharedToolPortalContext, layer=IPyAMSLayer)
+@ajax_config(name='template-properties.json', context=ITypedSharedToolPortalContext, layer=IPyAMSLayer)
+class SharedToolTemplatePropertiesEditForm(PortalContextTemplatePropertiesEditForm):
+    """Shared content template properties edit form"""
+
+    legend = _("Edit default template properties")
+
+
+@adapter_config(context=(ISharedToolPortalContext, IAdminLayer, SharedToolTemplatePropertiesEditForm),
+                provides=IFormHelp)
+@adapter_config(context=(ITypedSharedToolPortalContext, IAdminLayer, SharedToolTemplatePropertiesEditForm),
+                provides=IFormHelp)
+class SharedToolPortalContextPropertiesEditFormHelpAdapter(FormHelp):
+    """Shared tool template properties edit form help adapter"""
+
+    message = _("**This form allows you to select shared content default template.**\n"
+                "\n"
+                "If you choose to use a shared template, you can only adjust settings of "
+                "each portlet individually but can't change portlets list or page configuration.\n"
+                "\n"
+                "If you use a local template, you can define a whole custom "
+                "configuration but the template definition can't be reused anywhere...""")
+    message_format = 'rest'
+
+
+@pagelet_config(name='template-properties.html', context=IWfSharedContentPortalContext, layer=IPyAMSLayer,
+                permission=MANAGE_TEMPLATE_PERMISSION)
+@ajax_config(name='template-properties.json', context=IWfSharedContent, layer=IPyAMSLayer)
+class SharedContentTemplatePropertiesEditForm(PortalContextTemplatePropertiesEditForm):
+    """Shared content template properties edit form"""
+
+    override_legend = _("Override tool default template")
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/shared/common/zmi/rename.py	Wed Jun 27 16:42:01 2018 +0200
@@ -0,0 +1,121 @@
+#
+# Copyright (c) 2008-2018 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_PERMISSION
+from pyams_content.shared.common.interfaces import ISharedSite
+from pyams_content.shared.site.interfaces import ISiteFolder
+from pyams_content.skin.zmi.interfaces import ISiteTreeTable
+from pyams_skin.interfaces.viewlet import ITableItemColumnActionsMenu, IContextActions
+from pyams_skin.layer import IPyAMSLayer
+from z3c.form.interfaces import IDataExtractedEvent
+from zope.location.interfaces import ILocation
+
+# import packages
+from pyams_form.form import ajax_config
+from pyams_pagelet.pagelet import pagelet_config
+from pyams_skin.viewlet.toolbar import ToolbarMenuItem
+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 AdminDialogEditForm
+from pyramid.events import subscriber
+from z3c.form import field
+from zope.interface import Interface, Invalid
+
+from pyams_content import _
+
+
+#
+# Site container rename view
+#
+
+@viewlet_config(name='rename-item.menu', context=ISharedSite, layer=IPyAMSLayer,
+                view=ISiteTreeTable, manager=ITableItemColumnActionsMenu,
+                permission=MANAGE_SITE_PERMISSION, weight=900)
+@viewlet_config(name='rename-item.menu', context=ISharedSite, layer=IPyAMSLayer,
+                view=Interface, manager=IContextActions,
+                permission=MANAGE_SITE_PERMISSION, weight=900)
+@viewlet_config(name='rename-item.menu', context=ISiteFolder, layer=IPyAMSLayer,
+                view=ISiteTreeTable, manager=ITableItemColumnActionsMenu,
+                permission=MANAGE_SITE_PERMISSION, weight=900)
+@viewlet_config(name='rename-item.menu', context=ISiteFolder, layer=IPyAMSLayer,
+                view=Interface, manager=IContextActions,
+                permission=MANAGE_SITE_PERMISSION, weight=900)
+class SiteContainerRenameMenu(ToolbarMenuItem):
+    """Site container rename menu"""
+
+    label = _("Change URL...")
+    label_css_class = 'fa fa-fw fa-edit'
+    url = 'rename-item.html'
+    modal_target = True
+
+
+@pagelet_config(name='rename-item.html', context=ISharedSite, layer=IPyAMSLayer,
+                permission=MANAGE_SITE_PERMISSION)
+@ajax_config(name='rename-item.json', context=ISharedSite, layer=IPyAMSLayer)
+@pagelet_config(name='rename-item.html', context=ISiteFolder, layer=IPyAMSLayer,
+                permission=MANAGE_SITE_PERMISSION)
+@ajax_config(name='rename-item.json', context=ISiteFolder, layer=IPyAMSLayer)
+class SiteContainerRenameForm(AdminDialogEditForm):
+    """Site container rename form"""
+
+    prefix = 'rename_item.'
+
+    legend = _("Change item URL")
+
+    fields = field.Fields(ILocation).select('__name__')
+    edit_permission = MANAGE_SITE_PERMISSION
+
+    def updateWidgets(self, prefix=None):
+        super(SiteContainerRenameForm, self).updateWidgets(prefix)
+        self.widgets['__name__'].label = _("Item URL part")
+        self.widgets['__name__'].description = _("URL part used to access this content")
+
+    def update_content(self, content, data):
+        data = data.get(self, data)
+        old_name = content.__name__
+        new_name = data['__name__'] = translate_string(data['__name__'], spaces='-', keep_chars='-')
+        changes = super(SiteContainerRenameForm, self).update_content(content, data)
+        if changes:
+            # revert rename to adjust container properties
+            content.__name__ = old_name
+            parent = content.__parent__
+            parent[new_name] = content
+            del parent[old_name]
+        return changes
+
+    def get_ajax_output(self, changes):
+        if changes:
+            return {
+                'status': 'redirect',
+                'location': absolute_url(self.context, self.request, 'admin'),
+                'smallbox': {
+                    'status': 'success',
+                    'message': self.request.localizer.translate(self.successMessage)
+                }
+            }
+        else:
+            return super(SiteContainerRenameForm, self).get_ajax_output(changes)
+
+
+@subscriber(IDataExtractedEvent, form_selector=SiteContainerRenameForm)
+def handle_rename_form_data_extraction(event):
+    """Handle rename form data extraction"""
+    name = event.data.get('__name__')
+    if not name:
+        event.form.widgets.errors += (Invalid(_("You must provide an URL for this item!")),)
--- a/src/pyams_content/shared/common/zmi/security.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/shared/common/zmi/security.py	Wed Jun 27 16:42:01 2018 +0200
@@ -115,7 +115,7 @@
 class SharedToolContributorsRestrictionsChecksColumn(I18nColumn, GetAttrColumn):
     """Shared tool contributor enabled publication checks column"""
 
-    _header = _("Publication checks")
+    _header = _("Activated publication checks?")
     weight = 20
     cssClasses = {'td': 'center'}
 
@@ -267,7 +267,7 @@
 class SharedToolManagerRestrictionsChecksColumn(I18nColumn, GetAttrColumn):
     """Shared tool manager enabled publication checks column"""
 
-    _header = _("Publication checks")
+    _header = _("Activated publication checks?")
     weight = 40
     cssClasses = {'td': 'center'}
 
--- a/src/pyams_content/shared/common/zmi/templates/dashboard.pt	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/shared/common/zmi/templates/dashboard.pt	Wed Jun 27 16:42:01 2018 +0200
@@ -25,7 +25,9 @@
 							<label class="input">
 								<button type="submit" class="icon-append fa fa-fw fa-search no-border no-padding"
 										data-ams-form-hide-loading="true"></button>
-								<input type="text" name="query" placeholder="Quick search..." i18n:attributes="placeholder" />
+								<input type="text" name="query"
+									   placeholder="Quick search..." i18n:attributes="placeholder"
+									   data-ams-events-handlers='{"keyup": "MyAMS.helpers.clearSearchTarget"}' />
 							</label>
 						</div>
 						<div class="col-md-6">
@@ -36,7 +38,8 @@
 				</form>
 			</div>
 		</div>
-		<div id="search_results" tal:define="global dashboard_length 0">
+		<div id="search_results"></div>
+		<tal:var define="global dashboard_length 0">
 			<tal:loop repeat="table view.tables">
 				<tal:if condition="table.values">
 					<tal:var content="structure table.render()" />
@@ -46,6 +49,6 @@
 			<div tal:condition="not:dashboard_length" class="alert alert-info" i18n:translate="">
 				You are not actually concerned by any content.
 			</div>
-		</div>
+		</tal:var>
 	</div>
 </div>
--- a/src/pyams_content/shared/common/zmi/types.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/shared/common/zmi/types.py	Wed Jun 27 16:42:01 2018 +0200
@@ -146,7 +146,9 @@
 
     def renderCell(self, item):
         return '<span data-ams-stop-propagation="true" ' \
-               '      data-ams-click-handler="PyAMS_content.types.switchSubtypes">' \
+               '      data-ams-click-handler="MyAMS.skin.switchCellContent" ' \
+               '      data-ams-switch-handler="get-subtypes-table.json" ' \
+               '      data-ams-switch-target=".subtypes">' \
                '    <span class="small hint" title="{hint}" data-ams-hint-gravity="e">' \
                '        <i class="fa fa-plus-square-o switch"></i>' \
                '    </span>' \
@@ -315,8 +317,6 @@
         attributes = super(DatatypeSubtypesTable, self).data_attributes
         attributes['table'] = {
             'id': self.id,
-            'data-ams-plugins': 'pyams_content',
-            'data-ams-plugin-pyams_content-src': get_resource_path(pyams_content),
             'data-ams-location': absolute_url(self.context, self.request),
             'data-ams-tablednd-drag-handle': 'td.sorter',
             'data-ams-tablednd-drop-target': 'set-subtypes-order.json'
--- a/src/pyams_content/shared/common/zmi/workflow.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/shared/common/zmi/workflow.py	Wed Jun 27 16:42:01 2018 +0200
@@ -9,12 +9,6 @@
 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
 # FOR A PARTICULAR PURPOSE.
 #
-from z3c.form.browser.radio import RadioWidget
-from z3c.form.widget import FieldWidget
-
-from pyams_form.group import NamedWidgetsGroup
-from pyams_form.interfaces import IFormLayer
-from pyams_form.widget import widgettemplate_config
 
 __docformat__ = 'restructuredtext'
 
@@ -26,6 +20,7 @@
 from pyams_content.interfaces import PUBLISH_CONTENT_PERMISSION, CREATE_CONTENT_PERMISSION, MANAGE_CONTENT_PERMISSION
 from pyams_content.shared.common.interfaces import IWfSharedContent, IBaseSharedTool, ISharedContent, \
     IContributorRestrictions, IManagerRestrictions
+from pyams_form.interfaces import IFormLayer
 from pyams_form.interfaces.form import IWidgetsPrefixViewletsManager, IFormSuffixViewletsManager, IInnerSubForm
 from pyams_security.interfaces import ISecurityManager
 from pyams_skin.layer import IPyAMSLayer
@@ -38,6 +33,7 @@
 from pyams_content.workflow import DRAFT, DELETED
 from pyams_form.form import AJAXAddForm, ajax_config
 from pyams_form.schema import CloseButton
+from pyams_form.widget import widgettemplate_config
 from pyams_pagelet.pagelet import pagelet_config
 from pyams_template.template import template_config
 from pyams_utils.adapter import adapter_config
@@ -48,11 +44,12 @@
 from pyams_utils.traversing import get_parent
 from pyams_utils.url import absolute_url
 from pyams_viewlet.viewlet import viewlet_config, Viewlet
-from pyams_workflow.zmi.transition import WorkflowContentTransitionForm, WorkflowContentTransitionAJAXForm
+from pyams_workflow.zmi.transition import WorkflowContentTransitionForm
 from pyams_zmi.form import InnerAdminAddForm
 from pyramid.events import subscriber
-from pyramid.view import view_config
 from z3c.form import field, button
+from z3c.form.browser.radio import RadioWidget
+from z3c.form.widget import FieldWidget
 from zope.interface import Interface, Invalid
 from zope.schema import Bool
 
@@ -129,7 +126,7 @@
 @pagelet_config(name='wf-propose.html', context=IWfSharedContent, layer=IPyAMSLayer,
                 permission=MANAGE_CONTENT_PERMISSION)
 @ajax_config(name='wf-propose.json', context=IWfSharedContent, layer=IPyAMSLayer,
-             permission=MANAGE_CONTENT_PERMISSION, base=WorkflowContentTransitionAJAXForm)
+             permission=MANAGE_CONTENT_PERMISSION, base=AJAXAddForm)
 class PublicationRequestForm(WorkflowContentTransitionForm):
     """Shared content publication request form"""
 
@@ -216,7 +213,7 @@
 @pagelet_config(name='wf-cancel-propose.html', context=IWfSharedContent, layer=IPyAMSLayer,
                 permission=MANAGE_CONTENT_PERMISSION)
 @ajax_config(name='wf-cancel-propose.json', context=IWfSharedContent, layer=IPyAMSLayer,
-             permission=MANAGE_CONTENT_PERMISSION, base=WorkflowContentTransitionAJAXForm)
+             permission=MANAGE_CONTENT_PERMISSION, base=AJAXAddForm)
 class PublicationRequestCancelForm(WorkflowContentTransitionForm):
     """Shared content publication request cancel form"""
 
@@ -256,7 +253,7 @@
 @pagelet_config(name='wf-refuse.html', context=IWfSharedContent, layer=IPyAMSLayer,
                 permission=PUBLISH_CONTENT_PERMISSION)
 @ajax_config(name='wf-refuse.json', context=IWfSharedContent, layer=IPyAMSLayer,
-             permission=PUBLISH_CONTENT_PERMISSION, base=WorkflowContentTransitionAJAXForm)
+             permission=PUBLISH_CONTENT_PERMISSION, base=AJAXAddForm)
 class PublicationRequestRefuseForm(WorkflowContentTransitionForm):
     """Shared content publication request refuse form"""
 
@@ -310,7 +307,7 @@
 @pagelet_config(name='wf-publish.html', context=IWfSharedContent, layer=IPyAMSLayer,
                 permission=PUBLISH_CONTENT_PERMISSION)
 @ajax_config(name='wf-publish.json', context=IWfSharedContent, layer=IPyAMSLayer,
-             permission=PUBLISH_CONTENT_PERMISSION, base=WorkflowContentTransitionAJAXForm)
+             permission=PUBLISH_CONTENT_PERMISSION, base=AJAXAddForm)
 class PublicationForm(WorkflowContentTransitionForm):
     """Shared content publication form"""
 
@@ -399,7 +396,7 @@
 @pagelet_config(name='wf-retiring.html', context=IWfSharedContent, layer=IPyAMSLayer,
                 permission=MANAGE_CONTENT_PERMISSION)
 @ajax_config(name='wf-retiring.json', context=IWfSharedContent, layer=IPyAMSLayer,
-             permission=MANAGE_CONTENT_PERMISSION, base=WorkflowContentTransitionAJAXForm)
+             permission=MANAGE_CONTENT_PERMISSION, base=AJAXAddForm)
 class PublicationRetireRequestForm(WorkflowContentTransitionForm):
     """Shared content publication request refuse form"""
 
@@ -455,7 +452,7 @@
 @pagelet_config(name='wf-cancel-retiring.html', context=IWfSharedContent, layer=IPyAMSLayer,
                 permission=MANAGE_CONTENT_PERMISSION)
 @ajax_config(name='wf-cancel-retiring.json', context=IWfSharedContent, layer=IPyAMSLayer,
-             permission=MANAGE_CONTENT_PERMISSION, base=WorkflowContentTransitionAJAXForm)
+             permission=MANAGE_CONTENT_PERMISSION, base=AJAXAddForm)
 class PublicationRetireCancelForm(WorkflowContentTransitionForm):
     """Shared content publication retire request cancel form"""
 
@@ -495,7 +492,7 @@
 @pagelet_config(name='wf-retire.html', context=IWfSharedContent, layer=IPyAMSLayer,
                 permission=PUBLISH_CONTENT_PERMISSION)
 @ajax_config(name='wf-retire.json', context=IWfSharedContent, layer=IPyAMSLayer,
-             permission=PUBLISH_CONTENT_PERMISSION, base=WorkflowContentTransitionAJAXForm)
+             permission=PUBLISH_CONTENT_PERMISSION, base=AJAXAddForm)
 class PublicationRetireForm(WorkflowContentTransitionForm):
     """Shared content publication retire form"""
 
@@ -536,7 +533,7 @@
 @pagelet_config(name='wf-archiving.html', context=IWfSharedContent, layer=IPyAMSLayer,
                 permission=MANAGE_CONTENT_PERMISSION)
 @ajax_config(name='wf-archiving.json', context=IWfSharedContent, layer=IPyAMSLayer,
-             permission=MANAGE_CONTENT_PERMISSION, base=WorkflowContentTransitionAJAXForm)
+             permission=MANAGE_CONTENT_PERMISSION, base=AJAXAddForm)
 class PublicationArchiveRequestForm(WorkflowContentTransitionForm):
     """Shared content publication request archive form"""
 
@@ -579,7 +576,7 @@
 @pagelet_config(name='wf-cancel-archiving.html', context=IWfSharedContent, layer=IPyAMSLayer,
                 permission=MANAGE_CONTENT_PERMISSION)
 @ajax_config(name='wf-cancel-archiving.json', context=IWfSharedContent, layer=IPyAMSLayer,
-             permission=MANAGE_CONTENT_PERMISSION, base=WorkflowContentTransitionAJAXForm)
+             permission=MANAGE_CONTENT_PERMISSION, base=AJAXAddForm)
 class PublicationArchiveCancelForm(WorkflowContentTransitionForm):
     """Shared content publication archive request cancel form"""
 
@@ -619,7 +616,7 @@
 @pagelet_config(name='wf-archive.html', context=IWfSharedContent, layer=IPyAMSLayer,
                 permission=PUBLISH_CONTENT_PERMISSION)
 @ajax_config(name='wf-archive.json', context=IWfSharedContent, layer=IPyAMSLayer,
-             permission=PUBLISH_CONTENT_PERMISSION, base=WorkflowContentTransitionAJAXForm)
+             permission=PUBLISH_CONTENT_PERMISSION, base=AJAXAddForm)
 class PublicationArchiveForm(WorkflowContentTransitionForm):
     """Shared content publication archive form"""
 
@@ -711,7 +708,7 @@
 @pagelet_config(name='wf-delete.html', context=IWfSharedContent, layer=IPyAMSLayer,
                 permission=MANAGE_CONTENT_PERMISSION)
 @ajax_config(name='wf-delete.json', context=IWfSharedContent, layer=IPyAMSLayer,
-             permission=MANAGE_CONTENT_PERMISSION, base=WorkflowContentTransitionAJAXForm)
+             permission=MANAGE_CONTENT_PERMISSION, base=AJAXAddForm)
 class SharedContentDeleteForm(WorkflowContentTransitionForm):
     """Shared content delete form"""
 
@@ -835,7 +832,7 @@
                (manager_restrictions.check_access(context, request=request)) and \
                (not manager_restrictions.publication_checks):
                 return None
-        if isinstance(form, PublicationForm):
+        if isinstance(form, PublicationRequestForm):
             restrictions = IContributorRestrictions(context, None)
             if restrictions is not None:
                 contributor_restrictions = restrictions.get_restrictions(principal_id)
--- a/src/pyams_content/shared/form/interfaces/__init__.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/shared/form/interfaces/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -16,7 +16,8 @@
 # import standard library
 
 # import interfaces
-from pyams_content.shared.common.interfaces import ISharedTool, IWfSharedContent, ISharedContent
+from pyams_content.shared.common.interfaces import ISharedContent, IWfSharedContentPortalContext, \
+    ISharedToolPortalContext
 from zope.annotation.interfaces import IAttributeAnnotatable
 from zope.container.interfaces import IContainer, IContained
 
@@ -36,7 +37,7 @@
 FORM_FIELD_CONTAINER_KEY = 'pyams_content.shared.form_fields'
 
 
-class IFormsManager(ISharedTool):
+class IFormsManager(ISharedToolPortalContext):
     """Forms manager interface"""
 
 
@@ -115,7 +116,7 @@
     """Form fields container target marker interface"""
 
 
-class IWfForm(IWfSharedContent):
+class IWfForm(IWfSharedContentPortalContext):
     """Form interface"""
 
     user_title = I18nTextLineField(title=_("Form title"),
--- a/src/pyams_content/shared/form/manager.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/shared/form/manager.py	Wed Jun 27 16:42:01 2018 +0200
@@ -18,7 +18,7 @@
 # import interfaces
 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 pyams_portal.interfaces import IPortalContext
 from zope.component.interfaces import ISite
 from zope.lifecycleevent.interfaces import IObjectAddedEvent
 
@@ -32,7 +32,7 @@
 from zope.interface import implementer
 
 
-@implementer(IFormsManager, IAttributeAnnotatable)
+@implementer(IFormsManager)
 class FormsManager(SharedTool):
     """Forms manager class"""
 
--- a/src/pyams_content/shared/form/zmi/render.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/shared/form/zmi/render.py	Wed Jun 27 16:42:01 2018 +0200
@@ -16,7 +16,7 @@
 # import standard library
 
 # import interfaces
-from pyams_content.features.renderer.interfaces import IContentRenderer
+from pyams_content.features.renderer.interfaces import ISharedContentRenderer
 from pyams_content.shared.form.interfaces import IFormFieldContainerTarget, IFormFieldContainer
 from pyams_form.interfaces.form import IFormHelp
 from pyams_i18n.interfaces import II18n
@@ -77,7 +77,7 @@
 
 
 @adapter_config(name='form-render', context=(IFormFieldContainerTarget, IPyAMSLayer),
-                provides=IContentRenderer)
+                provides=ISharedContentRenderer)
 class FormFieldContainerRenderer(BaseContentRenderer):
     """Form field container renderer"""
 
--- a/src/pyams_content/shared/imagemap/zmi/area.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/shared/imagemap/zmi/area.py	Wed Jun 27 16:42:01 2018 +0200
@@ -9,6 +9,8 @@
 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
 # FOR A PARTICULAR PURPOSE.
 #
+from pyams_skin.event import get_json_table_row_refresh_event
+from pyams_utils.traversing import get_parent
 
 __docformat__ = 'restructuredtext'
 
@@ -24,7 +26,7 @@
 
 # import packages
 from pyams_content.shared.imagemap import ImageMapArea
-from pyams_content.shared.imagemap.zmi.container import ImagemapAreasContainerView
+from pyams_content.shared.imagemap.zmi.container import ImagemapAreasContainerView, ImagemapAreasTable
 from pyams_content.shared.imagemap.zmi.widget import ImgareaInputFieldWidget
 from pyams_form.form import AJAXAddForm, ajax_config
 from pyams_pagelet.pagelet import pagelet_config
@@ -63,6 +65,9 @@
 
     legend = _("Add new image area")
 
+    label_css_class = 'control-label col-md-2'
+    input_css_class = 'col-md-10'
+
     @property
     def dialog_class(self):
         image = II18n(self.context).query_attribute('image', request=self.request)
@@ -101,6 +106,9 @@
 
     legend = _("Edit image map properties")
 
+    label_css_class = 'control-label col-md-2'
+    input_css_class = 'col-md-10'
+
     @property
     def dialog_class(self):
         image = II18n(self.context.__parent__).query_attribute('image', request=self.request)
@@ -119,3 +127,11 @@
     fields['area'].widgetFactory = ImgareaInputFieldWidget
 
     edit_permission = MANAGE_CONTENT_PERMISSION
+
+    def get_ajax_output(self, changes):
+        output = super(self.__class__, self).get_ajax_output(changes)
+        if changes:
+            container = get_parent(self.context, IWfImageMap)
+            output.setdefault('events', []).append(
+                get_json_table_row_refresh_event(container, self.request, ImagemapAreasTable, self.context))
+        return output
--- a/src/pyams_content/shared/imagemap/zmi/container.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/shared/imagemap/zmi/container.py	Wed Jun 27 16:42:01 2018 +0200
@@ -89,11 +89,11 @@
     @property
     def data_attributes(self):
         attributes = super(ImagemapAreasTable, self).data_attributes
-        attributes['table'] = {
+        attributes.setdefault('table', {}).update({
             'data-ams-location': absolute_url(self.context, self.request),
             'data-ams-datatable-sort': 'false',
             'data-ams-datatable-pagination': 'false'
-        }
+        })
         return attributes
 
     @reify
--- a/src/pyams_content/shared/imagemap/zmi/templates/paragraph-render.pt	Wed Jun 27 16:34:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-<h3 tal:content="view.title">title</h3>
-<div class="no-padding" i18n:domain="pyams_content"
-	 data-ams-plugins="pyams_content"
-	 tal:define="imagemap context.get_target()"
-	 tal:attributes="data-ams-plugin-pyams_content-src extension:resource_path('pyams_content.skin:pyams_content')">
-	<img usemap="#imagemap_preview"
-		 tal:define="image i18n:imagemap.image"
-		 tal:attributes="src extension:absolute_url(image);
-						 usemap string:#imagemap_preview_${context.__name__}"
-		 data-ams-callback="PyAMS_content.imgmap.initPreview" />
-	<map name="imagemap_preview"
-		 tal:attributes="name string:imagemap_preview_${context.__name__}">
-		<tal:loop repeat="area imagemap.areas.values()">
-			<tal:var define="item imagemap.get_association(area)"
-					 condition="item and item.visible">
-				<area shape="poly"
-					  tal:define="info view.get_item_info(item);
-								  area_title i18n:area.title;
-								  item_title i18n:info.user_title;
-								  title area_title or item_title;"
-					  tal:attributes="coords area.area;
-									  href item.get_url(request);
-									  title title;" />
-			</tal:var>
-		</tal:loop>
-	</map>
-</div>
--- a/src/pyams_content/shared/imagemap/zmi/templates/render.pt	Wed Jun 27 16:34:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,23 +0,0 @@
-<div class="padding-y-5" i18n:domain="pyams_content"
-	 data-ams-plugins="pyams_content"
-	 tal:attributes="data-ams-plugin-pyams_content-src extension:resource_path('pyams_content.skin:pyams_content')">
-	<img usemap="#imagemap_preview"
-		 tal:define="image i18n:context.image"
-		 tal:attributes="src extension:absolute_url(image);"
-		 data-ams-callback="PyAMS_content.imgmap.initSummary" />
-	<map name="imagemap_preview">
-		<tal:loop repeat="area context.areas.values()">
-			<tal:var define="item context.get_association(area)"
-					 condition="item and item.visible">
-				<area shape="poly"
-					  tal:define="info view.get_item_info(item);
-								  area_title i18n:area.title;
-								  item_title i18n:info.user_title;
-								  title area_title or item_title;"
-					  tal:attributes="coords area.area;
-									  href item.get_url(request);
-									  title title;" />
-			</tal:var>
-		</tal:loop>
-	</map>
-</div>
--- a/src/pyams_content/shared/news/__init__.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/shared/news/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -16,7 +16,7 @@
 # import standard library
 
 # import interfaces
-from pyams_content.component.illustration import IIllustrationTarget
+from pyams_content.component.illustration import IIllustrationTarget, ILinkIllustrationTarget
 from pyams_content.component.paragraph.interfaces import IParagraphContainerTarget
 from pyams_content.component.theme.interfaces import IThemesTarget
 from pyams_content.features.preview.interfaces import IPreviewTarget
@@ -30,7 +30,7 @@
 from zope.interface import implementer, provider
 
 
-@implementer(IWfNewsEvent, IIllustrationTarget, IParagraphContainerTarget, IThemesTarget,
+@implementer(IWfNewsEvent, IIllustrationTarget, ILinkIllustrationTarget, IParagraphContainerTarget, IThemesTarget,
              IPreviewTarget, IReviewTarget)
 class WfNewsEvent(WfSharedContent):
     """Base news event"""
--- a/src/pyams_content/shared/news/interfaces/__init__.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/shared/news/interfaces/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -16,7 +16,8 @@
 # import standard library
 
 # import interfaces
-from pyams_content.shared.common.interfaces import ISharedTool, IWfSharedContent, ISharedContent
+from pyams_content.shared.common.interfaces import ISharedContent, \
+    IWfSharedContentPortalContext, ISharedToolPortalContext
 
 # import packages
 from zope.interface import Interface
@@ -28,7 +29,7 @@
 NEWS_CONTENT_NAME = _("News topic")
 
 
-class INewsManager(ISharedTool):
+class INewsManager(ISharedToolPortalContext):
     """News manager interface"""
 
 
@@ -36,7 +37,7 @@
     """News manager factory interface"""
 
 
-class IWfNewsEvent(IWfSharedContent):
+class IWfNewsEvent(IWfSharedContentPortalContext):
     """News event interface"""
 
 
--- a/src/pyams_content/shared/site/__init__.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/shared/site/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -16,7 +16,7 @@
 # import standard library
 
 # import interfaces
-from pyams_content.component.illustration import IIllustrationTarget
+from pyams_content.component.illustration import IIllustrationTarget, ILinkIllustrationTarget
 from pyams_content.component.paragraph.interfaces import IParagraphContainerTarget
 from pyams_content.component.theme.interfaces import IThemesTarget
 from pyams_content.features.preview.interfaces import IPreviewTarget
@@ -31,8 +31,8 @@
 from zope.interface import implementer, provider
 
 
-@implementer(IWfTopic, IParagraphContainerTarget, IThemesTarget,
-             IIllustrationTarget, IPreviewTarget, IReviewTarget)
+@implementer(IWfTopic, IIllustrationTarget, ILinkIllustrationTarget, IParagraphContainerTarget,
+             IThemesTarget, IPreviewTarget, IReviewTarget)
 class WfTopic(WfSharedContent):
     """Base topic"""
 
--- a/src/pyams_content/shared/site/folder.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/shared/site/folder.py	Wed Jun 27 16:42:01 2018 +0200
@@ -16,7 +16,7 @@
 # import standard library
 
 # import interfaces
-from pyams_content.component.illustration import IIllustrationTarget
+from pyams_content.component.illustration import IIllustrationTarget, ILinkIllustrationTarget
 from pyams_content.features.preview.interfaces import IPreviewTarget
 from pyams_content.interfaces import MANAGE_SITE_PERMISSION
 from pyams_content.shared.site.interfaces import ISiteFolder, ISiteManager, ISiteFolderRoles
@@ -24,7 +24,6 @@
 from pyams_i18n.interfaces import II18n
 from pyams_portal.interfaces import IPortalContext
 from pyams_security.interfaces import IDefaultProtectionPolicy
-from zope.annotation.interfaces import IAttributeAnnotatable
 from zope.intid.interfaces import IIntIds
 
 # import packages
@@ -43,9 +42,11 @@
 
 from pyams_content import _
 
+from pyams_content import _
+
 
 @implementer(IDefaultProtectionPolicy, ISiteFolder, ISiteFolderRoles,
-             IIllustrationTarget, IPortalContext, IPreviewTarget, IAttributeAnnotatable)
+             IIllustrationTarget, ILinkIllustrationTarget, IPortalContext, IPreviewTarget)
 class SiteFolder(SiteContainerMixin, OrderedContainer, BaseSharedTool):
     """Site folder persistent class"""
 
--- a/src/pyams_content/shared/site/interfaces/__init__.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/shared/site/interfaces/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -19,11 +19,12 @@
 from pyams_content.interfaces import IBaseContent
 from pyams_sequence.interfaces import ISequentialIdTarget, IInternalReference
 from pyams_workflow.interfaces import IWorkflowPublicationSupport
+from zope.annotation.interfaces import IAttributeAnnotatable
 from zope.container.interfaces import IContainer, IContained
 
 # import packages
-from pyams_content.shared.common.interfaces import ISharedSite, IWfSharedContent, ISharedContent, \
-    IBaseContentManagerRoles, IBaseSharedTool, IDeletableElement
+from pyams_content.shared.common.interfaces import ISharedSite, ISharedContent, \
+    IBaseContentManagerRoles, IBaseSharedTool, IDeletableElement, IWfSharedContentPortalContext
 from pyams_i18n.schema import I18nTextLineField, I18nTextField
 from zope.container.constraints import containers, contains
 from zope.interface import Interface, Attribute
@@ -95,7 +96,7 @@
 TOPIC_CONTENT_NAME = _("Topic")
 
 
-class IWfTopic(IWfSharedContent):
+class IWfTopic(IWfSharedContentPortalContext):
     """Topic interface"""
 
 
@@ -107,7 +108,7 @@
     """Workflow managed topic interface"""
 
 
-class IContentLink(ISiteElement, IInternalReference):
+class IContentLink(ISiteElement, IInternalReference, IAttributeAnnotatable):
     """Rented content interface"""
 
     alt_title = I18nTextLineField(title=_("Alternate title"),
--- a/src/pyams_content/shared/site/link.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/shared/site/link.py	Wed Jun 27 16:42:01 2018 +0200
@@ -17,12 +17,14 @@
 
 # import interfaces
 from pyams_content.shared.site.interfaces import IContentLink
+from pyams_utils.interfaces.url import IRelativeURL
 from pyams_workflow.interfaces import IWorkflow, IWorkflowVersion, IWorkflowVersions, IWorkflowPublicationInfo, \
     IWorkflowState
 
 # import packages
 from persistent import Persistent
 from pyams_sequence.utility import get_reference_target
+from pyams_skin.layer import IPyAMSUserLayer
 from pyams_utils.adapter import adapter_config
 from zope.container.contained import Contained
 from zope.interface import implementer
@@ -83,3 +85,11 @@
     target = context.get_target()
     if target is not None:
         return IWorkflowPublicationInfo(target, None)
+
+
+@adapter_config(context=(IContentLink, IPyAMSUserLayer), provides=IRelativeURL)
+def content_link_relative_url(context):
+    """Content link relative URL"""
+    target = context.get_target()
+    if target is not None:
+        return IRelativeURL(target, None)
--- a/src/pyams_content/shared/site/manager.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/shared/site/manager.py	Wed Jun 27 16:42:01 2018 +0200
@@ -16,7 +16,7 @@
 # import standard library
 
 # import interfaces
-from pyams_content.component.illustration import IIllustrationTarget
+from pyams_content.component.illustration import IIllustrationTarget, ILinkIllustrationTarget
 from pyams_content.component.paragraph.interfaces import IParagraphFactorySettings
 from pyams_content.component.theme.interfaces import IThemesManagerTarget
 from pyams_content.features.footer.interfaces import IFooterTarget
@@ -30,7 +30,6 @@
 from pyams_form.interfaces.form import IFormContextPermissionChecker
 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
 
@@ -51,9 +50,12 @@
 from zope.schema.fieldproperty import FieldProperty
 from zope.schema.vocabulary import SimpleTerm, SimpleVocabulary
 
+from pyams_content import _
+
 
 @implementer(ISiteManager, IParagraphFactorySettings, IThemesManagerTarget, IPictogramManagerTarget,
-             IIllustrationTarget, IPortalContext, IHeaderTarget, IFooterTarget, IPreviewTarget, IAttributeAnnotatable)
+             IIllustrationTarget, ILinkIllustrationTarget, IPortalContext, IHeaderTarget,
+             IFooterTarget, IPreviewTarget)
 class SiteManager(SiteContainerMixin, OrderedContainer, BaseSharedTool, UserSkinnableContent):
     """Site manager persistent class"""
 
@@ -63,6 +65,8 @@
     sequence_name = ''  # use default sequence generator
     sequence_prefix = ''
 
+    content_name = _("Site manager")
+
     @property
     def folder_factory(self):
         return ISiteFolderFactory(self, SiteFolder)
--- a/src/pyams_content/shared/site/zmi/container.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/shared/site/zmi/container.py	Wed Jun 27 16:42:01 2018 +0200
@@ -68,7 +68,7 @@
 from pyramid.view import view_config
 from z3c.form import field
 from z3c.table.column import GetAttrColumn
-from zope.interface import implementer
+from zope.interface import implementer, Interface
 from zope.lifecycleevent import ObjectMovedEvent
 
 from pyams_content import _
@@ -137,9 +137,9 @@
     def updateWidgets(self, prefix=None):
         super(SiteContainerWorkflowPublicationEditForm, self).updateWidgets(prefix)
         if 'publication_effective_date' in self.widgets:
-            widget = self.widgets['publication_effective_date']
-            if not widget.value:
-                widget.value = tztime(datetime.utcnow()).strftime('%d/%m/%y %H:%M')
+            pub_info = IWorkflowPublicationInfo(self.context)
+            if pub_info.publication_effective_date is None:
+                self.widgets['publication_effective_date'].value = tztime(datetime.utcnow()).strftime('%d/%m/%y %H:%M')
 
     def get_ajax_output(self, changes):
         output = super(self.__class__, self).get_ajax_output(changes)
@@ -203,7 +203,10 @@
         permission = self.permission
         if self.can_sort and ((not permission) or self.request.has_permission(permission, self.context)):
             classes.append('table-dnd')
-        return {'table': ' '.join(classes)}
+        return {
+            'table': ' '.join(classes),
+            'tr.selected': lambda item, col, row: 'current' if item is self.context else ''
+        }
 
     @property
     def data_attributes(self):
--- a/src/pyams_content/shared/site/zmi/folder.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/shared/site/zmi/folder.py	Wed Jun 27 16:42:01 2018 +0200
@@ -20,6 +20,7 @@
 from pyams_content.shared.common.interfaces import IBaseSharedTool
 from pyams_content.shared.site.interfaces import ISiteContainer, ISiteManager, ISiteFolder
 from pyams_i18n.interfaces import INegotiator, II18n
+from pyams_skin.interfaces.container import ITableElementName, ITableElementEditor
 from pyams_skin.interfaces.viewlet import IToolbarAddingMenu, IMenuHeader
 from pyams_skin.layer import IPyAMSLayer
 from pyams_zmi.interfaces.menu import ISiteManagementMenu
@@ -33,6 +34,7 @@
 from pyams_form.form import AJAXAddForm, AJAXEditForm, ajax_config
 from pyams_i18n.schema import I18nTextLineField
 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 get_utility
@@ -164,6 +166,23 @@
 # Site folder edit form
 #
 
+@adapter_config(context=(ISiteFolder, IPyAMSLayer), provides=ITableElementName)
+class SiteFolderTableElementNameAdapter(ContextRequestAdapter):
+    """Site folder table element name adapter"""
+
+    @property
+    def name(self):
+        return II18n(self.context).query_attribute('short_name', request=self.request)
+
+
+@adapter_config(context=(ISiteFolder, IAdminLayer, Interface), provides=ITableElementEditor)
+class SiteFolderTableElementEditorAdapter(DefaultElementEditorAdapter):
+    """Site folder table element editor adapter"""
+
+    view_name = 'admin#site-tree.html'
+    modal_target = False
+
+
 @pagelet_config(name='properties.html', context=ISiteFolder, layer=IPyAMSLayer, permission=MANAGE_TOOL_PERMISSION)
 class SiteFolderPropertiesEditForm(SharedToolPropertiesEditForm):
     """Site folder properties edit form"""
--- a/src/pyams_content/shared/site/zmi/link.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/shared/site/zmi/link.py	Wed Jun 27 16:42:01 2018 +0200
@@ -9,6 +9,7 @@
 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
 # FOR A PARTICULAR PURPOSE.
 #
+from pyams_skin.event import get_json_table_row_refresh_event
 
 __docformat__ = 'restructuredtext'
 
@@ -21,7 +22,6 @@
 from pyams_content.shared.site.interfaces import ISiteContainer, IContentLink
 from pyams_content.skin.zmi.interfaces import ISiteTreeTable
 from pyams_i18n.interfaces import II18n
-from pyams_sequence.interfaces import IInternalReference
 from pyams_skin.interfaces.container import ITableElementName
 from pyams_skin.interfaces.viewlet import IToolbarAddingMenu
 from pyams_skin.layer import IPyAMSLayer
@@ -31,7 +31,7 @@
 
 # import packages
 from pyams_content.shared.site.link import ContentLink
-from pyams_content.shared.site.zmi.container import SiteContainerTreeTable, SiteContainerTreeNameColumn
+from pyams_content.shared.site.zmi.container import SiteContainerTreeTable
 from pyams_content.shared.site.zmi.widget import SiteManagerFoldersSelectorFieldWidget
 from pyams_form.form import AJAXAddForm, ajax_config
 from pyams_pagelet.pagelet import pagelet_config
@@ -140,7 +140,7 @@
     def get_ajax_output(self, changes):
         output = super(self.__class__, self).get_ajax_output(changes)
         intids = get_utility(IIntIds)
-        if 'reference' in changes.get(IInternalReference, ()):
+        if changes:
             table = SiteContainerTreeTable(self.context.__parent__, self.request)
             table.update()
             row = table.setUpRow(self.context)
@@ -152,16 +152,4 @@
                     'row': table.renderRow(row)
                 }
             })
-        elif 'alt_title' in changes.get(IContentLink, ()):
-            adapter = ContentLinkTableElementName(self.context, self.request, None)
-            column = SiteContainerTreeNameColumn(self.context, self.request, None)
-            output.setdefault('events', []).append({
-                'event': 'myams.refresh',
-                'options': {
-                    'handler': 'MyAMS.skin.refreshRowCell',
-                    'object_id': '{0}::{1}'.format(SiteContainerTreeTable.id, intids.queryId(self.context)),
-                    'col_name': 'name',
-                    'cell': column.renderCell(self.context, name=adapter.name)
-                }
-            })
         return output
--- a/src/pyams_content/shared/site/zmi/manager.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/shared/site/zmi/manager.py	Wed Jun 27 16:42:01 2018 +0200
@@ -21,7 +21,7 @@
 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
+from pyams_skin.interfaces.container import ITableElementEditor, ITableElementName
 from pyams_skin.interfaces.viewlet import IToolbarAddingMenu, IBreadcrumbItem, IMenuHeader
 from pyams_skin.layer import IPyAMSLayer
 from pyams_zmi.interfaces.menu import ISiteManagementMenu
@@ -45,7 +45,7 @@
 from pyramid.events import subscriber
 from pyramid.path import DottedNameResolver
 from z3c.form import field
-from zope.interface import Invalid
+from zope.interface import Invalid, Interface
 
 from pyams_content import _
 
@@ -68,6 +68,31 @@
     css_class = 'strong'
 
 
+@adapter_config(context=(ISiteManager, IPyAMSLayer), provides=ITableElementName)
+class SiteManagerTableElementNameAdapter(ContextRequestAdapter):
+    """Site manager table element name adapter"""
+
+    @property
+    def name(self):
+        return II18n(self.context).query_attribute('short_name', request=self.request)
+
+
+@adapter_config(context=(ISiteManager, IAdminLayer, Interface), provides=ITableElementEditor)
+class SiteManagerTableElementEditorAdapter(DefaultElementEditorAdapter):
+    """Site manager table element editor adapter"""
+
+    view_name = 'admin#site-tree.html'
+    modal_target = False
+
+
+@adapter_config(context=(ISiteManager, IAdminLayer, ISiteTreeTable), provides=ITableElementEditor)
+class SiteManagerTableElementEditor(DefaultElementEditorAdapter):
+    """Site tree table element editor"""
+
+    view_name = 'admin#site-tree.html'
+    modal_target = False
+
+
 @adapter_config(context=(ISiteManager, IAdminLayer), provides=IUserAddingsMenuLabel)
 class SiteManagerUserAddingsMenuLabelAdapter(ContextRequestAdapter):
     """Site manager user addings menu label adapter"""
@@ -137,11 +162,3 @@
     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!!")),)
-
-
-@adapter_config(context=(ISiteManager, IAdminLayer, ISiteTreeTable), provides=ITableElementEditor)
-class SiteManagerTableElementEditor(DefaultElementEditorAdapter):
-    """Site tree table element editor"""
-
-    view_name = 'admin#site-tree.html'
-    modal_target = False
--- a/src/pyams_content/shared/view/__init__.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/shared/view/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -25,6 +25,7 @@
 from pyams_content.features.review.interfaces import IReviewTarget
 from pyams_content.shared.view.interfaces import IView, IWfView, IWfViewFactory, IViewQuery, \
     IViewQueryParamsExtension, IViewQueryFilterExtension, VIEW_CONTENT_TYPE, VIEW_CONTENT_NAME, IViewSettings
+from pyams_utils.interfaces import ICacheKeyValue
 from zope.intid.interfaces import IIntIds
 from zope.lifecycleevent.interfaces import IObjectModifiedEvent
 
@@ -86,13 +87,12 @@
         return list(content_types)
 
     def get_results(self, context):
-        intids = get_utility(IIntIds)
         views_cache = get_cache(VIEWS_CACHE_REGION, VIEWS_CACHE_NAME)
         if self.is_using_context:
-            cache_key = VIEW_CONTEXT_CACHE_KEY.format(view=intids.queryId(self),
-                                                      context=intids.queryId(context))
+            cache_key = VIEW_CONTEXT_CACHE_KEY.format(view=ICacheKeyValue(self),
+                                                      context=ICacheKeyValue(context))
         else:
-            cache_key = VIEW_CACHE_KEY.format(view=intids.queryId(self))
+            cache_key = VIEW_CACHE_KEY.format(ICacheKeyValue(self))
         try:
             results = views_cache.get_value(cache_key)
         except KeyError:
@@ -101,6 +101,7 @@
             if adapter is None:
                 adapter = registry.getAdapter(self, IViewQuery)
             results = adapter.get_results(context, self.limit)
+            intids = get_utility(IIntIds)
             views_cache.set_value(cache_key, [intids.queryId(item) for item in results])
             logger.debug("Storing view items to cache key {0}".format(cache_key))
         else:
--- a/src/pyams_content/shared/view/interfaces/__init__.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/shared/view/interfaces/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -161,6 +161,11 @@
                              required=True,
                              default=ALWAYS_REFERENCE_MODE)
 
+    exclude_context = Bool(title=_("Exclude context?"),
+                           description=_("If 'yes', context will be excluded from results list"),
+                           required=True,
+                           default=True)
+
 
 VIEW_THEMES_SETTINGS_KEY = 'pyams_content.view.themes'
 
--- a/src/pyams_content/shared/view/portlet/__init__.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/shared/view/portlet/__init__.py	Wed Jun 27 16:42:01 2018 +0200
@@ -23,6 +23,7 @@
 from pyams_content.workflow import PUBLISHED_STATES
 from pyams_portal.portlet import PortletSettings, portlet_config, Portlet
 from pyams_sequence.utility import get_sequence_target
+from pyams_utils.factory import factory_config
 from zope.interface import implementer
 from zope.schema.fieldproperty import FieldProperty
 
@@ -33,6 +34,7 @@
 
 
 @implementer(IViewItemsPortletSettings)
+@factory_config(provided=IViewItemsPortletSettings)
 class ViewItemsPortletSettings(PortletSettings):
     """View items portlet settings"""
 
@@ -58,4 +60,4 @@
     toolbar_image = None
     toolbar_css_class = 'fa fa-fw fa-2x fa-th-list'
 
-    settings_class = ViewItemsPortletSettings
+    settings_factory = IViewItemsPortletSettings
--- a/src/pyams_content/shared/view/portlet/templates/view-items-list.pt	Wed Jun 27 16:34:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,3 +0,0 @@
-<div tal:repeat="item view.settings.get_items(context)">
-	<tal:var content="i18n:item.title" />
-</div>
--- a/src/pyams_content/shared/view/portlet/zmi/templates/view-items-list-preview.pt	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/shared/view/portlet/zmi/templates/view-items-list-preview.pt	Wed Jun 27 16:42:01 2018 +0200
@@ -1,22 +1,12 @@
 <tal:var define="settings view.settings">
-	<tal:if condition="settings.visible">
-		<tal:var define="items settings.get_items(context)">
-			<tal:if condition="items">
-				<div tal:repeat="item items">
-					<tal:var content="i18n:item.title" />
-				</div>
-			</tal:if>
-			<tal:if condition="not:items" i18n:translate="">
-				No result found
-			</tal:if>
-		</tal:var>
-	</tal:if>
-	<tal:if condition="not settings.visible">
-		<div class="text-center padding-y-5">
-			<span class="fa-stack fa-lg">
-				<i class="fa fa-eye fa-stack-1x"></i>
-				<i class="fa fa-ban fa-stack-2x text-danger"></i>
-			</span>
-		</div>
-	</tal:if>
+	<tal:var define="items settings.get_items(context)">
+		<tal:if condition="items">
+			<div tal:repeat="item items">
+				<tal:var content="i18n:item.title" />
+			</div>
+		</tal:if>
+		<tal:if condition="not:items" i18n:translate="">
+			No result found
+		</tal:if>
+	</tal:var>
 </tal:var>
--- a/src/pyams_content/shared/view/reference.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/shared/view/reference.py	Wed Jun 27 16:42:01 2018 +0200
@@ -9,6 +9,7 @@
 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
 # FOR A PARTICULAR PURPOSE.
 #
+from pyams_sequence.interfaces import ISequentialIdInfo
 
 __docformat__ = 'restructuredtext'
 
@@ -19,12 +20,12 @@
 # import interfaces
 from hypatia.interfaces import ICatalog
 from pyams_content.shared.view.interfaces import IWfView, IViewSettings, IViewInternalReferencesSettings, \
-    IViewQueryFilterExtension, VIEW_REFERENCES_SETTINGS_KEY, ALWAYS_REFERENCE_MODE
+    IViewQueryParamsExtension, IViewQueryFilterExtension, VIEW_REFERENCES_SETTINGS_KEY, ALWAYS_REFERENCE_MODE
 
 # import packages
 from hypatia.catalog import CatalogQuery
-from hypatia.query import Any
-from pyams_catalog.query import CatalogResultSet
+from hypatia.query import Any, Not, NotEq
+from pyams_catalog.query import CatalogResultSet, and_
 from pyams_content.workflow import VISIBLE_STATES
 from pyams_utils.adapter import adapter_config, ContextAdapter, get_annotation_adapter
 from pyams_utils.registry import get_utility
@@ -39,6 +40,7 @@
 
     references = FieldProperty(IViewInternalReferencesSettings['references'])
     references_mode = FieldProperty(IViewInternalReferencesSettings['references_mode'])
+    exclude_context = FieldProperty(IViewInternalReferencesSettings['exclude_context'])
 
     @property
     def is_using_context(self):
@@ -53,6 +55,23 @@
                                   name='++view:references++')
 
 
+@adapter_config(name='references', context=IWfView, provides=IViewQueryParamsExtension)
+class ViewThemesQueryParamsExtension(ContextAdapter):
+    """View internal references query params extension"""
+
+    weight = 50
+
+    def get_params(self, context):
+        catalog = get_utility(ICatalog)
+        settings = IViewInternalReferencesSettings(self.context)
+        params = None
+        # check themes
+        if settings.exclude_context:
+            oid = ISequentialIdInfo(context).hex_oid
+            params = and_(params, NotEq(catalog['oid'], oid))
+        return params
+
+
 @adapter_config(name='references', context=IWfView, provides=IViewQueryFilterExtension)
 class ViewInternalReferencesQueryFilterExtension(ContextAdapter):
     """View internal references filter extension"""
@@ -65,6 +84,7 @@
             return items
         if (not items) or (settings.references_mode == ALWAYS_REFERENCE_MODE):
             catalog = get_utility(ICatalog)
-            params = Any(catalog['oid'], settings.references) & Any(catalog['workflow_state'], VISIBLE_STATES)
+            params = Any(catalog['oid'], settings.references) & \
+                     Any(catalog['workflow_state'], VISIBLE_STATES)
             items.prepend(CatalogResultSet(CatalogQuery(catalog).query(params)))
         return items
--- a/src/pyams_content/shared/view/theme.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/shared/view/theme.py	Wed Jun 27 16:42:01 2018 +0200
@@ -16,12 +16,18 @@
 # import standard library
 
 # import interfaces
+from hypatia.interfaces import ICatalog
 from pyams_content.component.theme.interfaces import IThemesInfo
-from pyams_content.shared.view.interfaces import IWfView, IViewSettings, IViewThemesSettings, VIEW_THEMES_SETTINGS_KEY
+from pyams_content.shared.view.interfaces import IWfView, IViewSettings, IViewThemesSettings, \
+    IViewQueryParamsExtension, VIEW_THEMES_SETTINGS_KEY
+from zope.intid.interfaces import IIntIds
 
 # import packages
+from hypatia.query import Any
 from persistent import Persistent
-from pyams_utils.adapter import adapter_config, get_annotation_adapter
+from pyams_catalog.query import and_
+from pyams_utils.adapter import adapter_config, get_annotation_adapter, ContextAdapter
+from pyams_utils.registry import get_utility
 from zope.container.contained import Contained
 from zope.interface import implementer
 from zope.schema.fieldproperty import FieldProperty
@@ -49,7 +55,8 @@
         return themes
 
     def get_themes_index(self, context):
-        return [theme.label for theme in self.get_themes(context)]
+        intids = get_utility(IIntIds)
+        return [intids.register(term) for term in self.get_themes(context)]
 
 
 @adapter_config(context=IWfView, provides=IViewThemesSettings)
@@ -58,3 +65,20 @@
     """View themes settings factory"""
     return get_annotation_adapter(view, VIEW_THEMES_SETTINGS_KEY, ViewThemesSettings,
                                   name='++view:themes++')
+
+
+@adapter_config(name='themes', context=IWfView, provides=IViewQueryParamsExtension)
+class ViewThemesQueryParamsExtension(ContextAdapter):
+    """View themes query params extension"""
+
+    weight = 50
+
+    def get_params(self, context):
+        catalog = get_utility(ICatalog)
+        settings = IViewThemesSettings(self.context)
+        params = None
+        # check themes
+        themes = settings.get_themes_index(context)
+        if themes:
+            params = and_(params, Any(catalog['themes'], themes))
+        return params
--- a/src/pyams_content/shared/view/zmi/render.py	Wed Jun 27 16:34:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,38 +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
-from pyams_content.features.renderer.interfaces import IContentRenderer
-from pyams_content.shared.view.interfaces import IWfView
-from pyams_skin.layer import IPyAMSLayer
-
-# import packages
-from pyams_content.features.renderer.skin import BaseContentRenderer
-from pyams_template.template import template_config
-from pyams_utils.adapter import adapter_config
-
-
-@adapter_config(name='view-render', context=(IWfView, IPyAMSLayer), provides=IContentRenderer)
-@template_config(template='templates/render.pt', layer=IPyAMSLayer)
-class ViewRenderer(BaseContentRenderer):
-    """Shared view renderer"""
-
-    weight = 20
-
-    @property
-    def items(self):
-        return self.context.get_results(self.context)
--- a/src/pyams_content/shared/view/zmi/templates/render.pt	Wed Jun 27 16:34:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,9 +0,0 @@
-<fieldset i18n:domain="pyams_content">
-	<legend class="inner" i18n:translate="">View result items</legend>
-	<div class="alert alert-info padding-5" i18n:translate="">
-		WARNING: items displayed in this preview are out of context!!
-	</div>
-	<tal:loop repeat="item view.items">
-		<span tal:content="i18n:item.title" /><br />
-	</tal:loop>
-</fieldset>
--- a/src/pyams_content/site.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/site.py	Wed Jun 27 16:42:01 2018 +0200
@@ -19,6 +19,7 @@
 # import interfaces
 from hypatia.interfaces import ICatalog
 from pyams_content.shared.common.interfaces import IWfSharedContent
+from pyams_content.shared.site.interfaces import IContentLink
 from zope.intid.interfaces import IIntIds
 
 # import packages
@@ -38,6 +39,9 @@
             for document in find_objects_providing(application, IWfSharedContent):
                 print("Indexing: {0!r}".format(document))
                 catalog.reindex_doc(intids.register(document), document)
+            for document in find_objects_providing(application, IContentLink):
+                print("Indexing: {0!r}".format(document))
+                catalog.reindex_doc(intids.register(document), document)
         finally:
             set_local_registry(None)
         transaction.commit()
--- a/src/pyams_content/skin/resources/js/pyams_content.js	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/skin/resources/js/pyams_content.js	Wed Jun 27 16:42:01 2018 +0200
@@ -337,7 +337,6 @@
 			},
 
 			updateMarkers: function(settings) {
-				debugger;
 				var para = $('tr[id="' + settings.object_id + '"]');
 				var toolbar = $('.title-toolbar', para);
 				var marker = $('DIV.action.' + settings.marker_type, toolbar);
@@ -468,39 +467,6 @@
 		},
 
 		/**
-		 * Types management
-		 */
-		types: {
-
-			switchSubtypes: function(element) {
-				var source = $(this);
-				var switcher = $('i.switch', source);
-				var td = source.parents('td');
-				var subtypes = $('.subtypes', td);
-				var datatype = source.parents('tr');
-				if (switcher.hasClass('fa-plus-square-o')) {
-					var container = datatype.parents('table');
-					subtypes.html('<h1 class="loading"><i class="fa fa-2x fa-gear fa-spin"></i></h1>');
-					MyAMS.ajax.post(container.data('ams-location') + '/get-subtypes-table.json',
-									{object_name: datatype.data('ams-element-name')},
-									function(result) {
-										subtypes.html(result);
-										if (result) {
-											MyAMS.initContent(subtypes);
-											switcher.removeClass('fa-plus-square-o')
-													.addClass('fa-minus-square-o');
-										}
-									});
-				} else {
-					MyAMS.skin.cleanContainer(subtypes);
-					subtypes.empty();
-					switcher.removeClass('fa-minus-square-o')
-							.addClass('fa-plus-square-o');
-				}
-			}
-		},
-
-		/**
 		 * Site management
 		 */
 		site: {
@@ -607,6 +573,28 @@
 		},
 
 		/**
+		 * Header management
+		 */
+		header: {
+
+			submitEditForm: function() {
+				var form = $(this).parents('form').first();
+				MyAMS.form.submit(form, {form_data: {'autosubmit': true}});
+			}
+		},
+
+		/**
+		 * Footer management
+		 */
+		footer: {
+
+			submitEditForm: function() {
+				var form = $(this).parents('form').first();
+				MyAMS.form.submit(form, {form_data: {'autosubmit': true}});
+			}
+		},
+
+		/**
 		 * User profile management
 		 */
 		profile: {
--- a/src/pyams_content/skin/resources/js/pyams_content.min.js	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/skin/resources/js/pyams_content.min.js	Wed Jun 27 16:42:01 2018 +0200
@@ -1,1 +1,1 @@
-!function(t,e){"use strict";var a=e.MyAMS,i={widget:{treeview:{selectFolder:function(e,a){t(e.target).siblings('input[type="hidden"]').val(a.id)},unselectFolder:function(e,a){t(e.target).siblings('input[type="hidden"]').val(null)}}},TinyMCE:{initEditor:function(t){return tinyMCE.addI18n("fr",{"Link list":"Liste de liens","Toggle h3 header":"En-tête H3","Toggle h4 header":"En-tête H4","Insert internal link":"Insérer un lien interne","Link title":"Texte à afficher","Internal number":"N° interne"}),tinymce.PluginManager.add("internal_links",function(t,e){t.addButton("internal_links",{icon:"cloud-check",tooltip:"Insert internal link",image:"/--static--/pyams_content/img/internal-link.png",onclick:function(){t.windowManager.open({title:"Insert internal link",body:[{type:"textbox",name:"oid",label:"Internal number"},{type:"textbox",name:"title",label:"Link title",value:t.selection.getContent()}],onsubmit:function(e){t.insertContent('<a href="oid://'+e.data.oid+'">'+e.data.title+"</a>")}})}})}),tinyMCE.PluginManager.add("headers",function(t,e){["h3","h4"].forEach(function(e){t.addButton("header-"+e,{tooltip:"Toggle "+e+" header",text:e.toUpperCase(),onClick:function(){t.execCommand("mceToggleFormat",!1,e)},onPostRender:function(){var a=this,i=function(){t.formatter.formatChanged(e,function(t){a.active(t)})};t.formatter?i():t.on("init",i)}})})}),t.image_list=i.TinyMCE.getImagesList,t.link_list=i.TinyMCE.getLinksList,t.style_formats=[{title:"Inline",items:[{title:"Bold",icon:"bold",format:"bold"},{title:"Italic",icon:"italic",format:"italic"},{title:"Underline",icon:"underline",format:"underline"},{title:"Strikethrough",icon:"strikethrough",format:"strikethrough"},{title:"Superscript",icon:"superscript",format:"superscript"},{title:"Subscript",icon:"subscript",format:"subscript"},{title:"Code",icon:"code",format:"code"}]},{title:"Blocks",items:[{title:"Paragraph",format:"p"},{title:"Blockquote",format:"blockquote"},{title:"Div",format:"div"},{title:"Pre",format:"pre"}]},{title:"Alignment",items:[{title:"Left",icon:"alignleft",format:"alignleft"},{title:"Center",icon:"aligncenter",format:"aligncenter"},{title:"Right",icon:"alignright",format:"alignright"},{title:"Justify",icon:"alignjustify",format:"alignjustify"}]}],t.plugins+=" internal_links headers",t.toolbar1&&(t.toolbar1="undo redo | header-h3 header-h4 styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent"),t.toolbar2&&(t.toolbar2="forecolor backcolor | charmap internal_links link | fullscreen preview print | code"),t},getImagesList:function(e){var i=t(document.activeElement).parents("form");if(i.exists()){var n=i.attr("data-ams-form-handler")||i.attr("action"),s=n.substr(0,n.lastIndexOf("/")+1);return a.ajax.post(s+"get-images-list.json",{},e)}},getLinksList:function(e){var i=t(document.activeElement).parents("form");if(i.exists()){var n=i.attr("data-ams-form-handler")||i.attr("action"),s=n.substr(0,n.lastIndexOf("/")+1);return a.ajax.post(s+"get-links-list.json",{},e)}}},galleries:{updateMediaTitle:function(e){t('img[id="'+e.media_id+'"]').attr("original-title",e.title)},switchMediaVisibility:function(e){return function(){var e=t(this),i=e.parents(".media"),n=i.parents(".gallery");a.ajax.post(n.data("ams-location")+"/set-media-visibility.json",{object_name:i.data("ams-element-name")},function(a,i){a.visible?(t("i",e).attr("class","fa fa-fw fa-eye"),e.parents(".btn-group").siblings("a.fancyimg").removeClass("not-visible")):(t("i",e).attr("class","fa fa-fw fa-eye-slash text-danger"),e.parents(".btn-group").siblings("a.fancyimg").addClass("not-visible"))})}},setOrder:function(e,i){if(!i||!i.item.hasClass("already-dropped")){var n=i.item.parents(".gallery"),s=t(".media",n).listattr("data-ams-element-name");a.ajax.post(n.data("ams-location")+"/set-medias-order.json",{medias:JSON.stringify(s)})}},removeMedia:function(e){return function(){var e=t(this);a.skin.bigBox({title:a.i18n.WARNING,content:'<i class="text-danger fa fa-2x fa-bell shake animated"></i>&nbsp; '+a.i18n.DELETE_WARNING,buttons:a.i18n.BTN_OK_CANCEL},function(t){if(t===a.i18n.BTN_OK){var i=e.parents(".gallery").data("ams-location"),n=e.parents(".media"),s=n.data("ams-element-name");a.ajax.post(i+"/delete-element.json",{object_name:s},function(t,e){n.remove()})}})}},afterFancyboxLoad:function(t,e){t.element.hasClass("not-visible")&&t.inner.prepend('<div class="hidden-mask"></div>')}},paragraphs:{preReload:function(){i.paragraphs.switched=t("i.switch.fa-minus-square-o","#paragraphs_list").parents("tr").listattr("id")},postReload:function(){t(i.paragraphs.switched).each(function(){t("i.switch.fa-plus-square-o",'[id="'+this+'"]').parents("div").first().click()}),delete i.paragraphs.switched},refreshParagraph:function(e){var a=t('tr[id="'+e.object_id+'"]');t("span.title",a).html(e.title||" - - - - - - - -")},switchEditor:function(e){var i=t(this),n=t("i.switch",i),s=i.parents("td"),r=t(".editor",s),o=i.parents("tr");if(n.hasClass("fa-plus-square-o")){var l=o.parents("table");r.html('<h1 class="loading"><i class="fa fa-2x fa-gear fa-spin"></i></h1>'),a.ajax.post(l.data("ams-location")+"/get-paragraph-editor.json",{object_name:o.data("ams-element-name")},function(t){r.html(t),t&&(a.initContent(r),n.removeClass("fa-plus-square-o").addClass("fa-minus-square-o"),o.data("ams-disabled-handlers",!0),a.skin.scrollTo(r,{offset:r.height()-o.height()}))})}else a.skin.cleanContainer(r),r.empty(),n.removeClass("fa-minus-square-o").addClass("fa-plus-square-o"),o.removeData("ams-disabled-handlers")},switchLastEditor:function(e){var a=t('table[id="'+e+'"]'),i=t("tr:last",a);t('[data-ams-click-handler="PyAMS_content.paragraphs.switchEditor"]',i).click()},switchAllEditors:function(e){var i=t(this),n=t("i",i),s=i.parents("table");n.hasClass("fa-plus-square-o")?(n.removeClass("fa-plus-square-o").addClass("fa-cog fa-spin"),a.ajax.post(s.data("ams-location")+"/get-paragraphs-editors.json",{},function(e){for(var i in e)if(e.hasOwnProperty(i)){var r=t('tr[data-ams-element-name="'+i+'"]',s),o=t(".editor",r);o.is(":empty")&&o.html(e[i]),t(".fa-plus-square-o",r).removeClass("fa-plus-square-o").addClass("fa-minus-square-o"),r.data("ams-disabled-handlers",!0)}t("i.fa-plus-square-o",t("tbody",s)).exists()||n.removeClass("fa-cog fa-spin").addClass("fa-minus-square-o"),a.initContent(s)})):(t(".editor",s).each(function(){a.skin.cleanContainer(t(this)),t(this).empty()}),t(".fa-minus-square-o",s).removeClass("fa-minus-square-o").addClass("fa-plus-square-o"),t("tr",s).removeData("ams-disabled-handlers"))},updateToolbar:function(e){var i=t('tr[id="'+e.object_id+'"]'),n=t(".title-toolbar",i);n.replaceWith(e.toolbar_tag),n=t(".title-toolbar",i),a.initContent(n)},updateMarkers:function(e){var i=t('tr[id="'+e.object_id+'"]'),n=t(".title-toolbar",i),s=t("DIV.action."+e.marker_type,n);s.exists()?s.replaceWith(e.marker_tag):t(e.marker_tag).appendTo(n),e.marker_tag&&(s=t("DIV.action."+e.marker_type,n),a.initContent(s)),a.helpers.sort(n,"weight")}},pictograms:{initManagerSelection:function(){var e=t(this),a=t('input[type="hidden"]',t(".selected-pictograms",e)).listattr("value");return{selected:JSON.stringify(a)}},switchPictogram:function(){var e=t(this),a=e.parents(".pictograms"),i=a.parents(".pictograms-manager");a.hasClass("available-pictograms")?t(".selected-pictograms",i).append(e):t(".available-pictograms",i).append(e)}},themes:{initExtracts:function(e){var i=t('select[name="form.widgets.thesaurus_name:list"]',e).val(),n=t('select[name="form.widgets.extract_name:list"]',e),s=n.val();i&&a.jsonrpc.post("getExtracts",{thesaurus_name:i},{url:"/api/thesaurus/json"},function(e){n.empty(),t(e.result).each(function(){t("<option></option>").attr("value",this.id).attr("selected",this.id===s).text(this.text).appendTo(n)})}),n.attr("data-ams-events-handlers",'{"select2-open": "PyAMS_content.themes.getExtracts"}')},getExtracts:function(e){var i=t(e.currentTarget).parents("form"),n=t('select[name="form.widgets.thesaurus_name:list"]',i).val();n&&a.jsonrpc.post("getExtracts",{thesaurus_name:n},{url:"/api/thesaurus/json"},function(e){var a=t('select[name="form.widgets.extract_name:list"]',i).data("select2");a.results.empty(),a.opts.populateResults.call(a,a.results,e.result,{term:""})})}},fields:{refreshField:function(e){var a=t('table[id="form_fields_list"]'),i=t('tr[data-ams-element-name="'+e.object_name+'"]',a);t("td:nth-child(4)",i).html(e.title)}},imgmap:{init:function(){var e=t(this);a.ajax.check(t.fn.canvasAreaDraw,"/--static--/pyams_content/js/jquery-canvasAreaDraw"+a.devext+".js",function(){e.canvasAreaDraw({imageUrl:e.data("ams-image-url")})})},initPreview:function(){var e=t(this);a.ajax.check(t.fn.mapster,"/--static--/pyams_content/js/jquery-imagemapster-1.2.10"+a.devext+".js",function(){e.mapster({fillColor:"ff0000",fillOpacity:.35,selected:!0,highlight:!0,staticState:!0})})}},types:{switchSubtypes:function(e){var i=t(this),n=t("i.switch",i),s=i.parents("td"),r=t(".subtypes",s),o=i.parents("tr");if(n.hasClass("fa-plus-square-o")){var l=o.parents("table");r.html('<h1 class="loading"><i class="fa fa-2x fa-gear fa-spin"></i></h1>'),a.ajax.post(l.data("ams-location")+"/get-subtypes-table.json",{object_name:o.data("ams-element-name")},function(t){r.html(t),t&&(a.initContent(r),n.removeClass("fa-plus-square-o").addClass("fa-minus-square-o"))})}else a.skin.cleanContainer(r),r.empty(),n.removeClass("fa-minus-square-o").addClass("fa-plus-square-o")}},site:{switchVisibility:function(){return function(){var e=t(this),i=e.parents("tr").first();a.ajax.post(i.data("ams-location")+"/switch-content-visibility.json",{object_name:i.data("ams-element-name")},function(a,i){var n="fa-eye";a.visible||(n+="-slash"),a.published||(n+=" text-danger"),t("i",e).attr("class","fa fa-fw "+n)})}}},review:{timer:null,timer_duration:{general:3e4,chat:5e3},initComments:function(e){var n=t(".chat-body",e);n.animate({scrollTop:n[0].scrollHeight},1e3),clearInterval(i.review.timer),i.review.timer=setInterval(i.review.updateComments,i.review.timer_duration.chat),a.skin.registerCleanCallback(i.review.cleanCommentsCallback)},cleanCommentsCallback:function(){clearInterval(i.review.timer),i.review.timer=setInterval(i.review.updateComments,i.review.timer_duration.general)},updateComments:function(){var e,i=t(".badge",'nav a[href="#review-comments.html"]'),n=t(".chat-body",".widget-body");e=n.exists()?t(".message",n).length:parseInt(i.text()),a.ajax.post("get-last-review-comments.json",{count:e},function(a){n.exists()&&i.removeClass("bg-color-danger").addClass("bg-color-info"),e!==a.count&&(i.text(a.count).removeClass("hidden"),n.exists()&&(t(".messages",n).append(a.content),n.animate({scrollTop:n[0].scrollHeight},1e3)),n.exists()||i.removeClass("bg-color-info").addClass("bg-color-danger").animate({padding:"3px 12px 2px","margin-right":"9px"},"slow",function(){t(this).animate({padding:"3px 6px 2px","margin-right":"15px"},"slow")}))})},initCommentData:function(e){var a=t(".chat-body",".widget-body");return{count:t(".message",a).length}},addCommentAction:function(){return function(){t('textarea[name="comment"]').focus()}},addCommentCallback:function(e){var a=t(this),i=a.parents(".widget-body");t(".messages",i).append(e.content),t('textarea[name="comment"]',a).val("");var n=t(".chat-body",i);n.animate({scrollTop:n[0].scrollHeight},1e3),t(".badge",'nav a[href="#review-comments.html"]').text(e.count).removeClass("hidden")}},profile:{switchFavorite:function(){var e=t(this),i=e.data("sequence-oid");a.ajax.post("switch-user-favorite.json",{oid:i},function(t,a){t.favorite?e.removeClass("fa-star-o").addClass("fa-star"):e.removeClass("fa-star").addClass("fa-star-o")})}}};t(".badge",'nav a[href="#review-comments.html"]').exists()&&(i.review.timer=setInterval(i.review.updateComments,i.review.timer_duration.general)),e.PyAMS_content=i}(jQuery,this);
+!function(o,t){"use strict";var l=t.MyAMS,a={widget:{treeview:{selectFolder:function(t,e){o(t.target).siblings('input[type="hidden"]').val(e.id)},unselectFolder:function(t,e){o(t.target).siblings('input[type="hidden"]').val(null)}}},TinyMCE:{initEditor:function(t){return tinyMCE.addI18n("fr",{"Link list":"Liste de liens","Toggle h3 header":"En-tête H3","Toggle h4 header":"En-tête H4","Insert internal link":"Insérer un lien interne","Link title":"Texte à afficher","Internal number":"N° interne"}),tinymce.PluginManager.add("internal_links",function(e,t){e.addButton("internal_links",{icon:"cloud-check",tooltip:"Insert internal link",image:"/--static--/pyams_content/img/internal-link.png",onclick:function(){e.windowManager.open({title:"Insert internal link",body:[{type:"textbox",name:"oid",label:"Internal number"},{type:"textbox",name:"title",label:"Link title",value:e.selection.getContent()}],onsubmit:function(t){e.insertContent('<a href="oid://'+t.data.oid+'">'+t.data.title+"</a>")}})}})}),tinyMCE.PluginManager.add("headers",function(i,t){["h3","h4"].forEach(function(a){i.addButton("header-"+a,{tooltip:"Toggle "+a+" header",text:a.toUpperCase(),onClick:function(){i.execCommand("mceToggleFormat",!1,a)},onPostRender:function(){var e=this,t=function(){i.formatter.formatChanged(a,function(t){e.active(t)})};i.formatter?t():i.on("init",t)}})})}),t.image_list=a.TinyMCE.getImagesList,t.link_list=a.TinyMCE.getLinksList,t.style_formats=[{title:"Inline",items:[{title:"Bold",icon:"bold",format:"bold"},{title:"Italic",icon:"italic",format:"italic"},{title:"Underline",icon:"underline",format:"underline"},{title:"Strikethrough",icon:"strikethrough",format:"strikethrough"},{title:"Superscript",icon:"superscript",format:"superscript"},{title:"Subscript",icon:"subscript",format:"subscript"},{title:"Code",icon:"code",format:"code"}]},{title:"Blocks",items:[{title:"Paragraph",format:"p"},{title:"Blockquote",format:"blockquote"},{title:"Div",format:"div"},{title:"Pre",format:"pre"}]},{title:"Alignment",items:[{title:"Left",icon:"alignleft",format:"alignleft"},{title:"Center",icon:"aligncenter",format:"aligncenter"},{title:"Right",icon:"alignright",format:"alignright"},{title:"Justify",icon:"alignjustify",format:"alignjustify"}]}],t.plugins+=" internal_links headers",t.toolbar1&&(t.toolbar1="undo redo | header-h3 header-h4 styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent"),t.toolbar2&&(t.toolbar2="forecolor backcolor | charmap internal_links link | fullscreen preview print | code"),t},getImagesList:function(t){var e=o(document.activeElement).parents("form");if(e.exists()){var a=e.attr("data-ams-form-handler")||e.attr("action"),i=a.substr(0,a.lastIndexOf("/")+1);return l.ajax.post(i+"get-images-list.json",{},t)}},getLinksList:function(t){var e=o(document.activeElement).parents("form");if(e.exists()){var a=e.attr("data-ams-form-handler")||e.attr("action"),i=a.substr(0,a.lastIndexOf("/")+1);return l.ajax.post(i+"get-links-list.json",{},t)}}},galleries:{updateMediaTitle:function(t){o('img[id="'+t.media_id+'"]').attr("original-title",t.title)},switchMediaVisibility:function(t){return function(){var a=o(this),t=a.parents(".media"),e=t.parents(".gallery");l.ajax.post(e.data("ams-location")+"/set-media-visibility.json",{object_name:t.data("ams-element-name")},function(t,e){t.visible?(o("i",a).attr("class","fa fa-fw fa-eye"),a.parents(".btn-group").siblings("a.fancyimg").removeClass("not-visible")):(o("i",a).attr("class","fa fa-fw fa-eye-slash text-danger"),a.parents(".btn-group").siblings("a.fancyimg").addClass("not-visible"))})}},setOrder:function(t,e){if(!e||!e.item.hasClass("already-dropped")){var a=e.item.parents(".gallery"),i=o(".media",a).listattr("data-ams-element-name");l.ajax.post(a.data("ams-location")+"/set-medias-order.json",{medias:JSON.stringify(i)})}},removeMedia:function(t){return function(){var n=o(this);l.skin.bigBox({title:l.i18n.WARNING,content:'<i class="text-danger fa fa-2x fa-bell shake animated"></i>&nbsp; '+l.i18n.DELETE_WARNING,buttons:l.i18n.BTN_OK_CANCEL},function(t){if(t===l.i18n.BTN_OK){var e=n.parents(".gallery").data("ams-location"),a=n.parents(".media"),i=a.data("ams-element-name");l.ajax.post(e+"/delete-element.json",{object_name:i},function(t,e){a.remove()})}})}},afterFancyboxLoad:function(t,e){t.element.hasClass("not-visible")&&t.inner.prepend('<div class="hidden-mask"></div>')}},paragraphs:{preReload:function(){a.paragraphs.switched=o("i.switch.fa-minus-square-o","#paragraphs_list").parents("tr").listattr("id")},postReload:function(){o(a.paragraphs.switched).each(function(){o("i.switch.fa-plus-square-o",'[id="'+this+'"]').parents("div").first().click()}),delete a.paragraphs.switched},refreshParagraph:function(t){var e=o('tr[id="'+t.object_id+'"]');o("span.title",e).html(t.title||" - - - - - - - -")},switchEditor:function(t){var e=o(this),a=o("i.switch",e),i=e.parents("td"),n=o(".editor",i),s=e.parents("tr");if(a.hasClass("fa-plus-square-o")){var r=s.parents("table");n.html('<h1 class="loading"><i class="fa fa-2x fa-gear fa-spin"></i></h1>'),l.ajax.post(r.data("ams-location")+"/get-paragraph-editor.json",{object_name:s.data("ams-element-name")},function(t){n.html(t),t&&(l.initContent(n),a.removeClass("fa-plus-square-o").addClass("fa-minus-square-o"),s.data("ams-disabled-handlers",!0),l.skin.scrollTo(n,{offset:n.height()-s.height()}))})}else l.skin.cleanContainer(n),n.empty(),a.removeClass("fa-minus-square-o").addClass("fa-plus-square-o"),s.removeData("ams-disabled-handlers")},switchLastEditor:function(t){var e=o('table[id="'+t+'"]'),a=o("tr:last",e);o('[data-ams-click-handler="PyAMS_content.paragraphs.switchEditor"]',a).click()},switchAllEditors:function(t){var e=o(this),n=o("i",e),s=e.parents("table");n.hasClass("fa-plus-square-o")?(n.removeClass("fa-plus-square-o").addClass("fa-cog fa-spin"),l.ajax.post(s.data("ams-location")+"/get-paragraphs-editors.json",{},function(t){for(var e in t)if(t.hasOwnProperty(e)){var a=o('tr[data-ams-element-name="'+e+'"]',s),i=o(".editor",a);i.is(":empty")&&i.html(t[e]),o(".fa-plus-square-o",a).removeClass("fa-plus-square-o").addClass("fa-minus-square-o"),a.data("ams-disabled-handlers",!0)}o("i.fa-plus-square-o",o("tbody",s)).exists()||n.removeClass("fa-cog fa-spin").addClass("fa-minus-square-o"),l.initContent(s)})):(o(".editor",s).each(function(){l.skin.cleanContainer(o(this)),o(this).empty()}),o(".fa-minus-square-o",s).removeClass("fa-minus-square-o").addClass("fa-plus-square-o"),o("tr",s).removeData("ams-disabled-handlers"))},updateToolbar:function(t){var e=o('tr[id="'+t.object_id+'"]'),a=o(".title-toolbar",e);a.replaceWith(t.toolbar_tag),a=o(".title-toolbar",e),l.initContent(a)},updateMarkers:function(t){var e=o('tr[id="'+t.object_id+'"]'),a=o(".title-toolbar",e),i=o("DIV.action."+t.marker_type,a);i.exists()?i.replaceWith(t.marker_tag):o(t.marker_tag).appendTo(a),t.marker_tag&&(i=o("DIV.action."+t.marker_type,a),l.initContent(i)),l.helpers.sort(a,"weight")}},pictograms:{initManagerSelection:function(){var t=o(this),e=o('input[type="hidden"]',o(".selected-pictograms",t)).listattr("value");return{selected:JSON.stringify(e)}},switchPictogram:function(){var t=o(this),e=t.parents(".pictograms"),a=e.parents(".pictograms-manager");e.hasClass("available-pictograms")?o(".selected-pictograms",a).append(t):o(".available-pictograms",a).append(t)}},themes:{initExtracts:function(t){var e=o('select[name="form.widgets.thesaurus_name:list"]',t).val(),a=o('select[name="form.widgets.extract_name:list"]',t),i=a.val();e&&l.jsonrpc.post("getExtracts",{thesaurus_name:e},{url:"/api/thesaurus/json"},function(t){a.empty(),o(t.result).each(function(){o("<option></option>").attr("value",this.id).attr("selected",this.id===i).text(this.text).appendTo(a)})}),a.attr("data-ams-events-handlers",'{"select2-open": "PyAMS_content.themes.getExtracts"}')},getExtracts:function(t){var a=o(t.currentTarget).parents("form"),e=o('select[name="form.widgets.thesaurus_name:list"]',a).val();e&&l.jsonrpc.post("getExtracts",{thesaurus_name:e},{url:"/api/thesaurus/json"},function(t){var e=o('select[name="form.widgets.extract_name:list"]',a).data("select2");e.results.empty(),e.opts.populateResults.call(e,e.results,t.result,{term:""})})}},fields:{refreshField:function(t){var e=o('table[id="form_fields_list"]'),a=o('tr[data-ams-element-name="'+t.object_name+'"]',e);o("td:nth-child(4)",a).html(t.title)}},imgmap:{init:function(){var t=o(this);l.ajax.check(o.fn.canvasAreaDraw,"/--static--/pyams_content/js/jquery-canvasAreaDraw"+l.devext+".js",function(){t.canvasAreaDraw({imageUrl:t.data("ams-image-url")})})},initPreview:function(){var t=o(this);l.ajax.check(o.fn.mapster,"/--static--/pyams_content/js/jquery-imagemapster-1.2.10"+l.devext+".js",function(){t.mapster({fillColor:"ff0000",fillOpacity:.35,selected:!0,highlight:!0,staticState:!0})})}},site:{switchVisibility:function(){return function(){var i=o(this),t=i.parents("tr").first();l.ajax.post(t.data("ams-location")+"/switch-content-visibility.json",{object_name:t.data("ams-element-name")},function(t,e){var a="fa-eye";t.visible||(a+="-slash"),t.published||(a+=" text-danger"),o("i",i).attr("class","fa fa-fw "+a)})}}},review:{timer:null,timer_duration:{general:3e4,chat:5e3},initComments:function(t){var e=o(".chat-body",t);e.animate({scrollTop:e[0].scrollHeight},1e3),clearInterval(a.review.timer),a.review.timer=setInterval(a.review.updateComments,a.review.timer_duration.chat),l.skin.registerCleanCallback(a.review.cleanCommentsCallback)},cleanCommentsCallback:function(){clearInterval(a.review.timer),a.review.timer=setInterval(a.review.updateComments,a.review.timer_duration.general)},updateComments:function(){var e,a=o(".badge",'nav a[href="#review-comments.html"]'),i=o(".chat-body",".widget-body");e=i.exists()?o(".message",i).length:parseInt(a.text()),l.ajax.post("get-last-review-comments.json",{count:e},function(t){i.exists()&&a.removeClass("bg-color-danger").addClass("bg-color-info"),e!==t.count&&(a.text(t.count).removeClass("hidden"),i.exists()&&(o(".messages",i).append(t.content),i.animate({scrollTop:i[0].scrollHeight},1e3)),i.exists()||a.removeClass("bg-color-info").addClass("bg-color-danger").animate({padding:"3px 12px 2px","margin-right":"9px"},"slow",function(){o(this).animate({padding:"3px 6px 2px","margin-right":"15px"},"slow")}))})},initCommentData:function(t){var e=o(".chat-body",".widget-body");return{count:o(".message",e).length}},addCommentAction:function(){return function(){o('textarea[name="comment"]').focus()}},addCommentCallback:function(t){var e=o(this),a=e.parents(".widget-body");o(".messages",a).append(t.content),o('textarea[name="comment"]',e).val("");var i=o(".chat-body",a);i.animate({scrollTop:i[0].scrollHeight},1e3),o(".badge",'nav a[href="#review-comments.html"]').text(t.count).removeClass("hidden")}},header:{submitEditForm:function(){var t=o(this).parents("form").first();l.form.submit(t,{form_data:{autosubmit:!0}})}},footer:{submitEditForm:function(){var t=o(this).parents("form").first();l.form.submit(t,{form_data:{autosubmit:!0}})}},profile:{switchFavorite:function(){var a=o(this),t=a.data("sequence-oid");l.ajax.post("switch-user-favorite.json",{oid:t},function(t,e){t.favorite?a.removeClass("fa-star-o").addClass("fa-star"):a.removeClass("fa-star").addClass("fa-star-o")})}}};o(".badge",'nav a[href="#review-comments.html"]').exists()&&(a.review.timer=setInterval(a.review.updateComments,a.review.timer_duration.general)),t.PyAMS_content=a}(jQuery,this);
--- a/src/pyams_content/skin/routes.py	Wed Jun 27 16:34:12 2018 +0200
+++ b/src/pyams_content/skin/routes.py	Wed Jun 27 16:42:01 2018 +0200
@@ -26,7 +26,7 @@
 from pyams_catalog.query import CatalogResultSet
 from pyams_content.workflow import VISIBLE_STATES
 from pyams_utils.registry import get_utility
-from pyams_utils.url import absolute_url
+from pyams_utils.url import absolute_url, canonical_url
 from pyramid.exceptions import NotFound
 from pyramid.response import Response
 from pyramid.view import view_config
@@ -51,12 +51,13 @@
             params &= Any(catalog['workflow_state'], VISIBLE_STATES)
         results = list(CatalogResultSet(CatalogQuery(catalog).query(params)))
         if results:
+            response = Response()
+            response.status_code = 302
             if view_name:  # back-office access => last version
                 version = IWorkflowVersions(results[0]).get_last_versions()[0]
+                response.location = absolute_url(version, request, '/'.join(view_name))
             else:
                 version = results[0]
-            response = Response()
-            response.status_code = 302
-            response.location = absolute_url(version, request, '/'.join(view_name))
+                response.location = canonical_url(version, request)
             return response
     raise NotFound()