Merge default dev-dc
authorDamien Correia
Thu, 06 Sep 2018 11:27:55 +0200
branchdev-dc
changeset 931 a2f3b82f93c3
parent 858 1afd36ed6947 (current diff)
parent 926 9f9ffb0be25d (diff)
child 932 8e9a1496c2ba
Merge default
src/pyams_content/component/links/zmi/reverse.py
src/pyams_content/component/paragraph/interfaces/keynumber.py
src/pyams_content/component/paragraph/keynumber.py
src/pyams_content/component/paragraph/zmi/keynumber.py
--- a/.hgtags	Tue Jul 17 15:12:43 2018 +0200
+++ b/.hgtags	Thu Sep 06 11:27:55 2018 +0200
@@ -20,3 +20,4 @@
 1978e4dad1d8f950411807ed2df23fd030a39b60 0.1.14
 fc8fe2dede6309db4a8cfc5b53eb894cca2f6970 0.1.15
 9cc7207c1399658ef821a1ecdde86df0b5a1298d 0.1.15.1
+f687b90488819f2dba88ed44a254d7cba1f8c652 0.1.16
--- a/buildout.cfg	Tue Jul 17 15:12:43 2018 +0200
+++ b/buildout.cfg	Thu Sep 06 11:27:55 2018 +0200
@@ -86,4 +86,4 @@
 eggs = pyams_content [test]
 
 [versions]
-pyams_content = 0.1.16
+pyams_content = 0.1.17
--- a/docs/HISTORY.txt	Tue Jul 17 15:12:43 2018 +0200
+++ b/docs/HISTORY.txt	Thu Sep 06 11:27:55 2018 +0200
@@ -1,6 +1,24 @@
 History
 =======
 
+0.1.16
+------
+ - use iterators in all dashboards
+ - updated view content portlet to select and merge several views
+ - handle cache for header and footer
+ - added custom file view to check publication status before giving access
+ - added arguments to avoid using cache when getting view results (for example in preview mode)
+ - added "oid_to_urls" HTML renderer to convert internal "oid://" links to URLs
+ - added Opengraph metas for shared contents
+ - added support for illustration to thesaurus terms
+ - added support for tags and collections, which are specific kinds of themes
+ - added location map paragraph
+ - added redirections manager
+ - added renderer resources management
+ - use locale name in header and footer cache key
+ - updated system manager permissions
+ - updated internal links management
+
 0.1.15.1
 --------
  - added request argument when rendering pictogram image
--- a/setup.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/setup.py	Thu Sep 06 11:27:55 2018 +0200
@@ -22,7 +22,7 @@
 README = os.path.join(DOCS, 'README.txt')
 HISTORY = os.path.join(DOCS, 'HISTORY.txt')
 
-version = '0.1.16'
+version = '0.1.17'
 long_description = open(README).read() + '\n\n' + open(HISTORY).read()
 
 tests_require = []
--- a/src/pyams_content.egg-info/PKG-INFO	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content.egg-info/PKG-INFO	Thu Sep 06 11:27:55 2018 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: pyams-content
-Version: 0.1.15.1
+Version: 0.1.16
 Summary: PyAMS base content interfaces and classes
 Home-page: http://hg.ztfy.org/pyams/pyams_content
 Author: Thierry Florac
@@ -73,6 +73,24 @@
         History
         =======
         
+        0.1.16
+        ------
+         - use iterators in all dashboards
+         - updated view content portlet to select and merge several views
+         - handle cache for header and footer
+         - added custom file view to check publication status before giving access
+         - added arguments to avoid using cache when getting view results (for example in preview mode)
+         - added "oid_to_urls" HTML renderer to convert internal "oid://" links to URLs
+         - added Opengraph metas for shared contents
+         - added support for illustration to thesaurus terms
+         - added support for tags and collections, which are specific kinds of themes
+         - added location map paragraph
+         - added redirections manager
+         - added renderer resources management
+         - use locale name in header and footer cache key
+         - updated system manager permissions
+         - updated internal links management
+        
         0.1.15.1
         --------
          - added request argument when rendering pictogram image
--- a/src/pyams_content.egg-info/SOURCES.txt	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content.egg-info/SOURCES.txt	Thu Sep 06 11:27:55 2018 +0200
@@ -27,6 +27,7 @@
 src/pyams_content/component/extfile/interfaces/__init__.py
 src/pyams_content/component/extfile/zmi/__init__.py
 src/pyams_content/component/extfile/zmi/container.py
+src/pyams_content/component/file/__init__.py
 src/pyams_content/component/gallery/__init__.py
 src/pyams_content/component/gallery/file.py
 src/pyams_content/component/gallery/paragraph.py
@@ -35,12 +36,15 @@
 src/pyams_content/component/gallery/zmi/file.py
 src/pyams_content/component/gallery/zmi/interfaces.py
 src/pyams_content/component/gallery/zmi/paragraph.py
+src/pyams_content/component/gallery/zmi/templates/gallery-media-thumbnail.pt
 src/pyams_content/component/gallery/zmi/templates/gallery-medias.pt
 src/pyams_content/component/illustration/__init__.py
 src/pyams_content/component/illustration/paragraph.py
+src/pyams_content/component/illustration/thesaurus.py
 src/pyams_content/component/illustration/interfaces/__init__.py
 src/pyams_content/component/illustration/zmi/__init__.py
 src/pyams_content/component/illustration/zmi/paragraph.py
+src/pyams_content/component/illustration/zmi/thesaurus.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
@@ -54,7 +58,6 @@
 src/pyams_content/component/links/interfaces/__init__.py
 src/pyams_content/component/links/zmi/__init__.py
 src/pyams_content/component/links/zmi/container.py
-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
@@ -65,6 +68,7 @@
 src/pyams_content/component/paragraph/html.py
 src/pyams_content/component/paragraph/keynumber.py
 src/pyams_content/component/paragraph/keypoint.py
+src/pyams_content/component/paragraph/map.py
 src/pyams_content/component/paragraph/milestone.py
 src/pyams_content/component/paragraph/pictogram.py
 src/pyams_content/component/paragraph/verbatim.py
@@ -77,6 +81,7 @@
 src/pyams_content/component/paragraph/interfaces/html.py
 src/pyams_content/component/paragraph/interfaces/keynumber.py
 src/pyams_content/component/paragraph/interfaces/keypoint.py
+src/pyams_content/component/paragraph/interfaces/map.py
 src/pyams_content/component/paragraph/interfaces/milestone.py
 src/pyams_content/component/paragraph/interfaces/pictogram.py
 src/pyams_content/component/paragraph/interfaces/verbatim.py
@@ -91,6 +96,7 @@
 src/pyams_content/component/paragraph/zmi/interfaces.py
 src/pyams_content/component/paragraph/zmi/keynumber.py
 src/pyams_content/component/paragraph/zmi/keypoint.py
+src/pyams_content/component/paragraph/zmi/map.py
 src/pyams_content/component/paragraph/zmi/milestone.py
 src/pyams_content/component/paragraph/zmi/pictogram.py
 src/pyams_content/component/paragraph/zmi/preview.py
@@ -105,7 +111,6 @@
 src/pyams_content/component/theme/zmi/__init__.py
 src/pyams_content/component/theme/zmi/manager.py
 src/pyams_content/component/theme/zmi/portlet.py
-src/pyams_content/component/theme/zmi/templates/themes-info.pt
 src/pyams_content/component/video/__init__.py
 src/pyams_content/component/video/paragraph.py
 src/pyams_content/component/video/interfaces/__init__.py
@@ -141,8 +146,10 @@
 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/skin/interfaces.py
 src/pyams_content/features/header/zmi/__init__.py
 src/pyams_content/features/header/zmi/templates/renderer-settings.pt
+src/pyams_content/features/html/__init__.py
 src/pyams_content/features/menu/__init__.py
 src/pyams_content/features/menu/interfaces/__init__.py
 src/pyams_content/features/menu/portlet/__init__.py
@@ -164,6 +171,13 @@
 src/pyams_content/features/preview/zmi/__init__.py
 src/pyams_content/features/preview/zmi/interfaces.py
 src/pyams_content/features/preview/zmi/templates/preview.pt
+src/pyams_content/features/redirect/__init__.py
+src/pyams_content/features/redirect/container.py
+src/pyams_content/features/redirect/tween.py
+src/pyams_content/features/redirect/interfaces/__init__.py
+src/pyams_content/features/redirect/zmi/__init__.py
+src/pyams_content/features/redirect/zmi/container.py
+src/pyams_content/features/redirect/zmi/templates/manager-test.pt
 src/pyams_content/features/renderer/__init__.py
 src/pyams_content/features/renderer/interfaces/__init__.py
 src/pyams_content/features/renderer/skin/__init__.py
@@ -233,6 +247,7 @@
 src/pyams_content/shared/common/portlet/content/zmi/preview.pt
 src/pyams_content/shared/common/skin/__init__.py
 src/pyams_content/shared/common/skin/oid.py
+src/pyams_content/shared/common/skin/opengraph.py
 src/pyams_content/shared/common/skin/url.py
 src/pyams_content/shared/common/zmi/__init__.py
 src/pyams_content/shared/common/zmi/dashboard.py
@@ -243,6 +258,7 @@
 src/pyams_content/shared/common/zmi/portal.py
 src/pyams_content/shared/common/zmi/properties.py
 src/pyams_content/shared/common/zmi/rename.py
+src/pyams_content/shared/common/zmi/reverse.py
 src/pyams_content/shared/common/zmi/search.py
 src/pyams_content/shared/common/zmi/security.py
 src/pyams_content/shared/common/zmi/site.py
@@ -321,6 +337,7 @@
 src/pyams_content/shared/site/zmi/widget/templates/folders-input.pt
 src/pyams_content/shared/view/__init__.py
 src/pyams_content/shared/view/manager.py
+src/pyams_content/shared/view/merge.py
 src/pyams_content/shared/view/reference.py
 src/pyams_content/shared/view/theme.py
 src/pyams_content/shared/view/interfaces/__init__.py
--- a/src/pyams_content/component/association/zmi/__init__.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/component/association/zmi/__init__.py	Thu Sep 06 11:27:55 2018 +0200
@@ -124,11 +124,18 @@
         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'
+            'data-ams-tablednd-drop-target': 'set-associations-order.json'
+        })
+        attributes.setdefault('td', {}).update({
+            'data-ams-attribute-switcher': self.get_switcher_target
         })
         return attributes
 
+    @staticmethod
+    def get_switcher_target(element, column):
+        if column.__name__ == 'show-hide':
+            return 'switch-association-visibility.json'
+
     @reify
     def values(self):
         return list(super(AssociationsTable, self).values)
--- a/src/pyams_content/component/gallery/zmi/interfaces.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/component/gallery/zmi/interfaces.py	Thu Sep 06 11:27:55 2018 +0200
@@ -39,7 +39,7 @@
 
     author = TextLine(title=_("Author"),
                       description=_("Name of document's author"),
-                      required=False)
+                      required=True)
 
     author_comments = I18nTextField(title=_("Author comments"),
                                     description=_("Comments relatives to author's rights management"),
--- a/src/pyams_content/component/gallery/zmi/templates/gallery-medias.pt	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/component/gallery/zmi/templates/gallery-medias.pt	Thu Sep 06 11:27:55 2018 +0200
@@ -2,6 +2,7 @@
 	 data-ams-plugins="pyams_content"
 	 tal:define="gallery_images context.values()"
 	 tal:attributes="data-ams-plugin-pyams_content-src tales:resource_path('pyams_content.skin:pyams_content');
+					 data-ams-plugin-pyams_content-css tales:resource_path('pyams_content.skin:pyams_content_css');
 					 id string:gallery_medias_${context.__name__};"
 	 data-ams-plugin-pyams_content-async="false">
 	<fieldset class="margin-top-10 padding-top-5 padding-bottom-0">
--- a/src/pyams_content/component/illustration/interfaces/__init__.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/component/illustration/interfaces/__init__.py	Thu Sep 06 11:27:55 2018 +0200
@@ -61,8 +61,8 @@
 class IIllustration(IBasicIllustration, IRenderedContent):
     """Illustration paragraph"""
 
-    description = I18nTextField(title=_("Description"),
-                                description=_(""),
+    description = I18nTextField(title=_("Associated text"),
+                                description=_("Illustration description displayed in front-office templates"),
                                 required=False)
 
     author = TextLine(title=_("Author"),
--- a/src/pyams_content/component/illustration/zmi/__init__.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/component/illustration/zmi/__init__.py	Thu Sep 06 11:27:55 2018 +0200
@@ -51,15 +51,31 @@
 
     css_class = 'form-group'
     padding_class = ''
-    fieldset_class = 'bordered margin-top-10 padding-y-5'
+    # fieldset_class = 'bordered margin-top-10 padding-y-5'
 
     legend = _("Illustration")
-    legend_class = 'illustration switcher no-y-padding padding-right-10'
+    # legend_class = 'illustration switcher no-y-padding padding-right-10'
 
     fields = field.Fields(IBasicIllustration).omit('__parent__', '__name__')
 
     weight = 10
 
+    @property
+    def legend_class(self):
+        if IBaseParagraph.providedBy(self.context):
+            return 'illustration switcher no-y-padding padding-right-10 pull-left width-auto'
+        else:
+            return 'illustration switcher no-y-padding'
+
+    @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
+
     def getContent(self):
         return IIllustration(self.context)
 
@@ -69,9 +85,10 @@
 
     @property
     def switcher_state(self):
-        content = self.getContent()
-        if content.has_data():
-            return 'open'
+        if not IBaseParagraph.providedBy(self.context):
+            content = self.getContent()
+            if content.has_data():
+                return 'open'
 
     def get_ajax_output(self, changes):
         output = super(BasicIllustrationPropertiesInnerEditForm, self).get_ajax_output(changes)
@@ -102,22 +119,6 @@
             return _("Header illustration")
 
     @property
-    def legend_class(self):
-        if IBaseParagraph.providedBy(self.context):
-            return 'illustration switcher no-y-padding padding-right-10 pull-left width-auto'
-        else:
-            return 'illustration switcher no-y-padding'
-
-    @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
-
-    @property
     def switcher_state(self):
         if not IBaseParagraph.providedBy(self.context):
             return 'open'
--- a/src/pyams_content/component/keynumber/interfaces/__init__.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/component/keynumber/interfaces/__init__.py	Thu Sep 06 11:27:55 2018 +0200
@@ -16,6 +16,7 @@
 # import standard library
 
 # import interfaces
+from pyams_content.component.paragraph.interfaces import IBaseParagraph
 from pyams_content.interfaces.container import IOrderedContainer
 from zope.annotation.interfaces import IAttributeAnnotatable
 
@@ -23,7 +24,7 @@
 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 zope.schema import Bool, TextLine, Choice
 
 from pyams_content import _
 
@@ -76,3 +77,21 @@
 
 class IKeyNumberContainerTarget(Interface):
     """Key numbers container target interface"""
+
+
+KEYNUMBER_PARAGRAPH_TYPE = 'KeyNumbers'
+KEYNUMBER_PARAGRAPH_NAME = _("Key numbers")
+KEYNUMBER_PARAGRAPH_RENDERERS = 'PyAMS.keynumbers.renderers'
+
+
+#
+# KeyNumber paragraph
+#
+
+class IKeyNumberParagraph(IKeyNumberContainerTarget, IBaseParagraph):
+    """Key numbers paragraph interface"""
+
+    renderer = Choice(title=_("Key numbers template"),
+                      description=_("Presentation template used for key numbers"),
+                      vocabulary=KEYNUMBER_PARAGRAPH_RENDERERS,
+                      default='default')
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/component/keynumber/paragraph.py	Thu Sep 06 11:27:55 2018 +0200
@@ -0,0 +1,93 @@
+#
+# Copyright (c) 2008-2015 Thierry Florac <tflorac AT ulthar.net>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+
+__docformat__ = 'restructuredtext'
+
+
+# import standard library
+
+# import interfaces
+from pyams_content.component.paragraph.interfaces import IParagraphFactory
+from pyams_content.component.keynumber.interfaces import KEYNUMBER_PARAGRAPH_TYPE, KEYNUMBER_PARAGRAPH_NAME, \
+    KEYNUMBER_PARAGRAPH_RENDERERS, IKeyNumberParagraph
+from pyams_content.features.checker.interfaces import IContentChecker, MISSING_VALUE, MISSING_LANG_VALUE
+from pyams_i18n.interfaces import II18n, II18nManager, INegotiator
+
+# import packages
+from pyams_content.component.paragraph import BaseParagraph, BaseParagraphFactory, BaseParagraphContentChecker
+from pyams_content.features.renderer import RenderersVocabulary
+from pyams_utils.adapter import adapter_config
+from pyams_utils.factory import factory_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 zope.interface import implementer
+from zope.schema.fieldproperty import FieldProperty
+
+
+@implementer(IKeyNumberParagraph)
+@factory_config(provided=IKeyNumberParagraph)
+class KeyNumberParagraph(BaseParagraph):
+    """Key numbers paragraph"""
+
+    icon_class = 'fa-dashboard'
+    icon_hint = KEYNUMBER_PARAGRAPH_NAME
+
+    renderer = FieldProperty(IKeyNumberParagraph['renderer'])
+
+
+@utility_config(name=KEYNUMBER_PARAGRAPH_TYPE, provides=IParagraphFactory)
+class KeyNumberParagraphFactory(BaseParagraphFactory):
+    """Key numbers paragraph factory"""
+
+    name = KEYNUMBER_PARAGRAPH_NAME
+    content_type = KeyNumberParagraph
+
+
+@adapter_config(context=IKeyNumberParagraph, provides=IContentChecker)
+class KeyNumberParagraphContentChecker(BaseParagraphContentChecker):
+    """Key numbers paragraph content checker"""
+
+    @property
+    def label(self):
+        request = check_request()
+        translate = request.localizer.translate
+        return II18n(self.context).query_attribute('title', request) or \
+            '({0})'.format(translate(self.context.icon_hint).lower())
+
+    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:
+            value = i18n.get_attribute('title', lang, request)
+            if not value:
+                field_title = translate(IKeyNumberParagraph['title'].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
+
+
+@vocabulary_config(name=KEYNUMBER_PARAGRAPH_RENDERERS)
+class KeyNumberParagraphRendererVocabulary(RenderersVocabulary):
+    """Key numbers paragraph renderers vocabulary"""
+
+    content_interface = IKeyNumberParagraph
--- a/src/pyams_content/component/keynumber/zmi/__init__.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/component/keynumber/zmi/__init__.py	Thu Sep 06 11:27:55 2018 +0200
@@ -94,11 +94,24 @@
         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'
+            'data-ams-tablednd-drop-target': 'set-keynumbers-order.json'
+        })
+        attributes.setdefault('td', {}).update({
+            'data-ams-attribute-switcher': self.get_switcher_target,
+            'data-ams-switcher-attribute-name': self.get_switcher_attribute
         })
         return attributes
 
+    @staticmethod
+    def get_switcher_target(element, column):
+        if column.__name__ == 'show-hide':
+            return 'switch-keynumber-visibility.json'
+
+    @staticmethod
+    def get_switcher_attribute(element, column):
+        if column.__name__ == 'show-hide':
+            return 'visible'
+
     @reify
     def values(self):
         return list(super(KeyNumbersTable, self).values)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/component/keynumber/zmi/paragraph.py	Thu Sep 06 11:27:55 2018 +0200
@@ -0,0 +1,125 @@
+#
+# Copyright (c) 2008-2015 Thierry Florac <tflorac AT ulthar.net>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+
+__docformat__ = 'restructuredtext'
+
+# import standard library
+
+# import interfaces
+from pyams_content.component.paragraph.interfaces import IParagraphContainerTarget, IParagraphContainer
+from pyams_content.component.keynumber.interfaces import KEYNUMBER_PARAGRAPH_TYPE, IKeyNumberParagraph
+from pyams_content.component.paragraph.zmi.interfaces import IParagraphInnerEditor, IParagraphContainerView
+from pyams_content.interfaces import MANAGE_CONTENT_PERMISSION
+from pyams_content.shared.common import IWfSharedContent
+from pyams_form.interfaces.form import IInnerForm
+from pyams_i18n.interfaces import II18n
+from pyams_skin.interfaces.viewlet import IToolbarAddingMenu
+from pyams_skin.layer import IPyAMSLayer
+from z3c.form.interfaces import INPUT_MODE
+
+# import packages
+from pyams_content.component.keynumber.zmi import IKeyNumbersParentForm
+from pyams_content.component.keynumber.paragraph import KeyNumberParagraph
+from pyams_content.component.paragraph.zmi import BaseParagraphAddMenu, BaseParagraphAJAXAddForm, \
+    BaseParagraphPropertiesEditForm, BaseParagraphAJAXEditForm, IParagraphEditFormButtons
+from pyams_content.features.renderer.zmi.widget import RendererFieldWidget
+from pyams_form.form import ajax_config
+from pyams_pagelet.pagelet import pagelet_config
+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_viewlet.viewlet import viewlet_config
+from pyams_zmi.form import AdminDialogAddForm
+from z3c.form import field, button
+from zope.interface import implementer, Interface
+
+from pyams_content import _
+
+
+@viewlet_config(name='add-keynumber-paragraph.menu', context=IParagraphContainerTarget, view=IParagraphContainerView,
+                layer=IPyAMSLayer, manager=IToolbarAddingMenu, weight=600)
+class KeyNumberParagraphAddMenu(BaseParagraphAddMenu):
+    """Key number paragraph add menu"""
+
+    label = _("Key numbers...")
+    label_css_class = 'fa fa-fw fa-dashboard'
+    url = 'add-keynumber-paragraph.html'
+    paragraph_type = KEYNUMBER_PARAGRAPH_TYPE
+
+
+@pagelet_config(name='add-keynumber-paragraph.html', context=IParagraphContainerTarget, layer=IPyAMSLayer,
+                permission=MANAGE_CONTENT_PERMISSION)
+@ajax_config(name='add-keynumber-paragraph.json', context=IParagraphContainerTarget, layer=IPyAMSLayer,
+             base=BaseParagraphAJAXAddForm)
+class KeyNumberParagraphAddForm(AdminDialogAddForm):
+    """Key number paragraph add form"""
+
+    legend = _("Add new key number paragraph")
+    icon_css_class = 'fa fa-fw fa-dashboard'
+
+    fields = field.Fields(IKeyNumberParagraph).select('title', 'renderer')
+    edit_permission = MANAGE_CONTENT_PERMISSION
+
+    def create(self, data):
+        return KeyNumberParagraph()
+
+    def add(self, object):
+        IParagraphContainer(self.context).append(object)
+
+
+@pagelet_config(name='properties.html', context=IKeyNumberParagraph, layer=IPyAMSLayer,
+                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"""
+
+    prefix = 'keynumbers_properties.'
+
+    @property
+    def title(self):
+        content = get_parent(self.context, IWfSharedContent)
+        return II18n(content).query_attribute('title', request=self.request)
+
+    legend = _("Edit key number paragraph properties")
+    icon_css_class = 'fa fa-fw fa-dashboard'
+
+    fields = field.Fields(IKeyNumberParagraph).select('title', 'renderer')
+    fields['renderer'].widgetFactory = RendererFieldWidget
+
+    edit_permission = MANAGE_CONTENT_PERMISSION
+
+
+@adapter_config(context=(IKeyNumberParagraph, IPyAMSLayer), provides=IParagraphInnerEditor)
+@ajax_config(name='inner-properties.json', context=IKeyNumberParagraph, layer=IPyAMSLayer,
+             base=BaseParagraphAJAXEditForm)
+@implementer(IInnerForm, IKeyNumbersParentForm)
+class KeyNumberParagraphInnerEditForm(KeyNumberParagraphPropertiesEditForm):
+    """Key number paragraph inner edit form"""
+
+    legend = None
+
+    @property
+    def buttons(self):
+        if self.mode == INPUT_MODE:
+            return button.Buttons(IParagraphEditFormButtons)
+        else:
+            return button.Buttons()
+
+    def get_ajax_output(self, changes):
+        output = super(self.__class__, self).get_ajax_output(changes)
+        updated = changes.get(IKeyNumberParagraph, ())
+        if 'renderer' in updated:
+            output.setdefault('events', []).append(
+                get_json_widget_refresh_event(self.context, self.request, KeyNumberParagraphInnerEditForm, 'renderer'))
+        return output
--- a/src/pyams_content/component/links/__init__.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/component/links/__init__.py	Thu Sep 06 11:27:55 2018 +0200
@@ -16,14 +16,16 @@
 # import standard library
 
 # import interfaces
-from pyams_content.component.association.interfaces import IAssociationInfo, IAssociationContainerTarget, 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
+from pyams_sequence.interfaces import ISequentialIdInfo, IInternalReference
+from pyams_skin.layer import IPyAMSUserLayer
 from pyams_workflow.interfaces import IWorkflow, IWorkflowPublicationInfo
 
 # import packages
@@ -115,8 +117,27 @@
 # Internal links
 #
 
+@implementer(IInternalReference)
+class InternalReferenceMixin(object):
+    """Internal reference mixin class"""
+
+    reference = None
+
+    @volatile_property
+    def target(self):
+        return get_reference_target(self.reference)
+
+    def get_target(self, state=None, request=None):
+        if request is None:
+            request = check_request()
+        if (not state) and not IPyAMSUserLayer.providedBy(request):
+            return self.target
+        else:
+            return get_reference_target(self.reference, state, request)
+
+
 @implementer(IInternalLink)
-class InternalLink(BaseLink):
+class InternalLink(BaseLink, InternalReferenceMixin):
     """Internal link persistent class"""
 
     icon_class = 'fa-external-link-square fa-rotate-90'
@@ -124,16 +145,6 @@
 
     reference = FieldProperty(IInternalLink['reference'])
 
-    @volatile_property
-    def target(self):
-        return get_reference_target(self.reference)
-
-    def get_target(self, state=None):
-        if not state:
-            return self.target
-        else:
-            return get_reference_target(self.reference, state)
-
     def is_visible(self, request=None):
         target = self.get_target()
         if target is not None:
--- a/src/pyams_content/component/links/zmi/reverse.py	Tue Jul 17 15:12:43 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,109 +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 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
-from pyams_utils.interfaces import VIEW_SYSTEM_PERMISSION
-from pyams_workflow.interfaces import IWorkflowVersions
-from pyams_zmi.interfaces.menu import IContentManagementMenu
-from pyams_zmi.layer import IAdminLayer
-from z3c.table.interfaces import IValues, IColumn
-
-# import packages
-from hypatia.catalog import CatalogQuery
-from hypatia.query import Eq, Or
-from pyams_catalog.query import CatalogResultSet
-from pyams_pagelet.pagelet import pagelet_config
-from pyams_skin.container import ContainerView
-from pyams_skin.table import BaseTable, NameColumn
-from pyams_skin.viewlet.menu import MenuItem
-from pyams_utils.adapter import adapter_config, ContextRequestViewAdapter
-from pyams_utils.list import unique
-from pyams_utils.registry import get_utility
-from pyams_utils.traversing import get_parent
-from pyams_viewlet.viewlet import viewlet_config
-from pyams_zmi.view import AdminView
-from zope.interface import implementer, Interface
-
-from pyams_content import _
-
-
-@viewlet_config(name='reverse-links.menu', context=IWfSharedContent, layer=IAdminLayer,
-                manager=IContentManagementMenu, permission=VIEW_SYSTEM_PERMISSION, weight=40)
-class SequentialITargetReverseLinksMenu(MenuItem):
-    """Sequential ID target reverse links menu"""
-
-    label = _("Reverse links")
-    icon_class = 'fa-anchor'
-    url = '#reverse-links.html'
-
-
-@implementer(ISiteRootDashboardTable)
-class SequentialIdTargetReverseLinkTable(BaseTable):
-    """Sequential ID target reverse links table"""
-
-    title = _("Content's internal links")
-
-
-@adapter_config(name='name', context=(Interface, IPyAMSLayer, SequentialIdTargetReverseLinkTable), provides=IColumn)
-class ReverseLinkNameColumn(NameColumn):
-    """Reverse link name column"""
-
-    _header = _("Title")
-
-
-@adapter_config(context=(IWfSharedContent, IPyAMSLayer, SequentialIdTargetReverseLinkTable), provides=IValues)
-class SequentialIdTargetReverseLinkValues(ContextRequestViewAdapter):
-    """Sequential ID target reverse links values"""
-
-    @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(get_item,
-                          CatalogResultSet(CatalogQuery(catalog).query(params,
-                                                                       sort_index='modified_date'))))
-
-
-@pagelet_config(name='reverse-links.html', context=IWfSharedContent, layer=IPyAMSLayer,
-                permission=VIEW_SYSTEM_PERMISSION)
-@implementer(IInnerPage)
-class SequentialIdTargetReverseLinkView(AdminView, ContainerView):
-    """Sequential ID target reverse links view"""
-
-    table_class = SequentialIdTargetReverseLinkTable
--- a/src/pyams_content/component/paragraph/audio.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/component/paragraph/audio.py	Thu Sep 06 11:27:55 2018 +0200
@@ -16,7 +16,7 @@
 # import standard library
 
 # import interfaces
-from pyams_content.component.illustration.interfaces import IIllustrationTarget
+from pyams_content.component.illustration.interfaces import IBasicIllustrationTarget
 from pyams_content.component.paragraph.interfaces import IParagraphFactory
 from pyams_content.component.paragraph.interfaces.audio import IAudioParagraph, AUDIO_PARAGRAPH_TYPE, \
     AUDIO_PARAGRAPH_RENDERERS, AUDIO_PARAGRAPH_NAME
@@ -36,7 +36,7 @@
 from zope.schema.fieldproperty import FieldProperty
 
 
-@implementer(IAudioParagraph, IIllustrationTarget)
+@implementer(IAudioParagraph, IBasicIllustrationTarget)
 @factory_config(provided=IAudioParagraph)
 class AudioParagraph(BaseParagraph):
     """Audio paragraph class"""
--- a/src/pyams_content/component/paragraph/container.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/component/paragraph/container.py	Thu Sep 06 11:27:55 2018 +0200
@@ -16,8 +16,8 @@
 # import standard library
 
 # import interfaces
-from pyams_content.component.paragraph.interfaces import IParagraphContainer, IParagraphContainerTarget, \
-    PARAGRAPH_CONTAINER_KEY
+from pyams_content.component.paragraph.interfaces import IParagraphFactory, IParagraphContainer, \
+    IParagraphContainerTarget, PARAGRAPH_CONTAINER_KEY
 from pyams_content.features.checker.interfaces import IContentChecker
 from zope.location.interfaces import ISublocations
 from zope.traversing.interfaces import ITraversable
@@ -26,6 +26,7 @@
 from pyams_content.features.checker import BaseContentChecker
 from pyams_utils.adapter import adapter_config, ContextAdapter, get_annotation_adapter
 from pyams_utils.container import BTreeOrderedContainer
+from pyams_utils.registry import get_global_registry
 from zope.interface import implementer
 
 from pyams_content import _
@@ -42,6 +43,22 @@
         self[key] = value
         self.last_id += 1
 
+    def get_visible_paragraphs(self, anchors_only=False, factories=None):
+        for paragraph in self.values():
+            if not paragraph.visible:
+                continue
+            if anchors_only and not paragraph.anchor:
+                continue
+            if factories:
+                registry = get_global_registry()
+                has_factory = False
+                for factory_name in factories:
+                    factory = registry.queryUtility(IParagraphFactory, name=factory_name)
+                    has_factory = (factory is not None) and (factory.content_type == paragraph.__class__)
+                if not has_factory:
+                    continue
+            yield paragraph
+
 
 @adapter_config(context=IParagraphContainerTarget, provides=IParagraphContainer)
 def paragraph_container_factory(target):
--- a/src/pyams_content/component/paragraph/frame.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/component/paragraph/frame.py	Thu Sep 06 11:27:55 2018 +0200
@@ -46,7 +46,7 @@
 class FrameParagraph(BaseParagraph):
     """Framed text paragraph"""
 
-    icon_class = 'fa-list-alt'
+    icon_class = 'fa-window-maximize'
     icon_hint = FRAME_PARAGRAPH_NAME
 
     body = FieldProperty(IFrameParagraph['body'])
--- a/src/pyams_content/component/paragraph/header.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/component/paragraph/header.py	Thu Sep 06 11:27:55 2018 +0200
@@ -16,24 +16,23 @@
 # import standard library
 
 # import interfaces
-from pyams_content.component.paragraph.interfaces import IParagraphFactory
-from pyams_content.component.paragraph.interfaces.header import IHeaderParagraph, HEADER_PARAGRAPH_TYPE, \
-    HEADER_PARAGRAPH_RENDERERS, HEADER_PARAGRAPH_NAME
-from pyams_content.features.checker.interfaces import IContentChecker, MISSING_VALUE, MISSING_LANG_VALUE
-from pyams_i18n.interfaces import II18n, II18nManager, INegotiator
+from pyams_content.component.paragraph.interfaces.header import IHeaderParagraph, HEADER_PARAGRAPH_RENDERERS, \
+    HEADER_PARAGRAPH_NAME
+from pyams_content.features.checker.interfaces import IContentChecker, ERROR_VALUE
+from pyams_i18n.interfaces import II18n
 
 # import packages
-from pyams_content.component.paragraph import BaseParagraph, BaseParagraphContentChecker, BaseParagraphFactory
+from pyams_content.component.paragraph import BaseParagraph, BaseParagraphContentChecker
 from pyams_content.features.renderer import RenderersVocabulary
 from pyams_utils.adapter import adapter_config
 from pyams_utils.factory import factory_config
-from pyams_utils.registry import utility_config, get_utility
 from pyams_utils.text import get_text_start
-from pyams_utils.traversing import get_parent
 from pyams_utils.vocabulary import vocabulary_config
 from zope.interface import implementer
 from zope.schema.fieldproperty import FieldProperty
 
+from pyams_content import _
+
 
 @implementer(IHeaderParagraph)
 @factory_config(provided=IHeaderParagraph)
@@ -52,37 +51,15 @@
     renderer = FieldProperty(IHeaderParagraph['renderer'])
 
 
-@utility_config(name=HEADER_PARAGRAPH_TYPE, provides=IParagraphFactory)
-class HeaderParagraphFactory(BaseParagraphFactory):
-    """Header paragraph factory"""
-
-    name = HEADER_PARAGRAPH_NAME
-    content_type = HeaderParagraph
-
-
 @adapter_config(context=IHeaderParagraph, provides=IContentChecker)
 class HeaderParagraphContentChecker(BaseParagraphContentChecker):
     """Header paragraph content checker"""
 
     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:
-            value = i18n.get_attribute('header', lang, request)
-            if not value:
-                field_title = translate(IHeaderParagraph['header'].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
+        field_title = translate(IHeaderParagraph['header'].title)
+        return [translate(ERROR_VALUE).format(field=field_title,
+                                              message=_("This paragraph type is deprecated and should be removed!")), ]
 
 
 @vocabulary_config(name=HEADER_PARAGRAPH_RENDERERS)
--- a/src/pyams_content/component/paragraph/interfaces/__init__.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/component/paragraph/interfaces/__init__.py	Thu Sep 06 11:27:55 2018 +0200
@@ -66,11 +66,17 @@
     def append(self, value):
         """Add given value to container"""
 
+    def get_visible_paragraphs(self, anchors_only=False, factories=None):
+        """Get visible paragraphs matching given arguments"""
+
 
 class IParagraphContainerTarget(IAttributeAnnotatable):
     """Paragraphs container marker interface"""
 
 
+PARAGRAPH_FACTORIES_VOCABULARY = 'PyAMS paragraph factories'
+
+
 class IParagraphFactory(Interface):
     """Paragraph factory utility interface"""
 
@@ -93,7 +99,7 @@
     auto_created_paragraphs = List(title=_("Default paragraphs"),
                                    description=_("List of paragraphs automatically added to a new content"),
                                    required=False,
-                                   value_type=Choice(vocabulary='PyAMS paragraph factories'))
+                                   value_type=Choice(vocabulary=PARAGRAPH_FACTORIES_VOCABULARY))
 
 
 class IParagraphRenderer(IContentProvider):
--- a/src/pyams_content/component/paragraph/interfaces/audio.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/component/paragraph/interfaces/audio.py	Thu Sep 06 11:27:55 2018 +0200
@@ -51,7 +51,7 @@
 
     author = TextLine(title=_("Author"),
                       description=_("Name of document's author"),
-                      required=True)
+                      required=False)
 
     renderer = Choice(title=_("Audio template"),
                       description=_("Presentation template used for this audio file"),
--- a/src/pyams_content/component/paragraph/interfaces/keynumber.py	Tue Jul 17 15:12:43 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +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.component.keynumber.interfaces import IKeyNumberContainerTarget
-from pyams_content.component.paragraph import IBaseParagraph
-
-# import packages
-from zope.schema import Bool, Choice, TextLine
-
-from pyams_content import _
-
-
-KEYNUMBER_PARAGRAPH_TYPE = 'KeyNumbers'
-KEYNUMBER_PARAGRAPH_NAME = _("Key numbers")
-KEYNUMBER_PARAGRAPH_RENDERERS = 'PyAMS.keynumbers.renderers'
-
-
-class IKeyNumberParagraph(IKeyNumberContainerTarget, IBaseParagraph):
-    """Key numbers paragraph interface"""
-
-    renderer = Choice(title=_("Key numbers template"),
-                      description=_("Presentation template used for key numbers"),
-                      vocabulary=KEYNUMBER_PARAGRAPH_RENDERERS,
-                      default='default')
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/component/paragraph/interfaces/map.py	Thu Sep 06 11:27:55 2018 +0200
@@ -0,0 +1,60 @@
+#
+# 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'
+
+
+try:
+    from pyams_gis.schema import GeoPointField
+except ImportError:
+    have_gis = False
+else:
+    have_gis = True
+
+if have_gis:
+
+    # import standard library
+
+    # import interfaces
+    from pyams_content.component.paragraph.interfaces import IBaseParagraph
+
+    # import packages
+    from zope.schema import Choice, Bool
+
+    from pyams_content import _
+
+
+    #
+    # Map paragraph
+    #
+
+    MAP_PARAGRAPH_TYPE = 'Map'
+    MAP_PARAGRAPH_NAME = _("Location map")
+    MAP_PARAGRAPH_RENDERERS = 'PyAMS.paragraph.map.renderers'
+
+
+    class IMapParagraph(IBaseParagraph):
+        """Map paragraph interface"""
+
+        gps_location = GeoPointField(title=_("GPS location"),
+                                     description=_("GPS coordinates used to locate map"),
+                                     required=False)
+
+        display_marker = Bool(title=_("Display location mark?"),
+                              description=_("If 'yes', a location marker will be displayed on map"),
+                              required=True,
+                              default=True)
+
+        renderer = Choice(title=_("Map template"),
+                          description=_("Presentation template used for this map"),
+                          vocabulary=MAP_PARAGRAPH_RENDERERS,
+                          default='default')
--- a/src/pyams_content/component/paragraph/interfaces/video.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/component/paragraph/interfaces/video.py	Thu Sep 06 11:27:55 2018 +0200
@@ -45,8 +45,8 @@
     title = I18nTextLineField(title=_("Legend"),
                               required=False)
 
-    description = I18nTextField(title=_("Description"),
-                                description=_("File description displayed by front-office template"),
+    description = I18nTextField(title=_("Associated text"),
+                                description=_("Video description displayed by front-office template"),
                                 required=False)
 
     author = TextLine(title=_("Author"),
--- a/src/pyams_content/component/paragraph/keynumber.py	Tue Jul 17 15:12:43 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,93 +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.component.paragraph.interfaces import IParagraphFactory
-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_i18n.interfaces import II18n, II18nManager, INegotiator
-
-# import packages
-from pyams_content.component.paragraph import BaseParagraph, BaseParagraphFactory, BaseParagraphContentChecker
-from pyams_content.features.renderer import RenderersVocabulary
-from pyams_utils.adapter import adapter_config
-from pyams_utils.factory import factory_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 zope.interface import implementer
-from zope.schema.fieldproperty import FieldProperty
-
-
-@implementer(IKeyNumberParagraph)
-@factory_config(provided=IKeyNumberParagraph)
-class KeyNumberParagraph(BaseParagraph):
-    """Key numbers paragraph"""
-
-    icon_class = 'fa-dashboard'
-    icon_hint = KEYNUMBER_PARAGRAPH_NAME
-
-    renderer = FieldProperty(IKeyNumberParagraph['renderer'])
-
-
-@utility_config(name=KEYNUMBER_PARAGRAPH_TYPE, provides=IParagraphFactory)
-class KeyNumberParagraphFactory(BaseParagraphFactory):
-    """Key numbers paragraph factory"""
-
-    name = KEYNUMBER_PARAGRAPH_NAME
-    content_type = KeyNumberParagraph
-
-
-@adapter_config(context=IKeyNumberParagraph, provides=IContentChecker)
-class KeyNumberParagraphContentChecker(BaseParagraphContentChecker):
-    """Key numbers paragraph content checker"""
-
-    @property
-    def label(self):
-        request = check_request()
-        translate = request.localizer.translate
-        return II18n(self.context).query_attribute('title', request) or \
-            '({0})'.format(translate(self.context.icon_hint).lower())
-
-    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:
-            value = i18n.get_attribute('title', lang, request)
-            if not value:
-                field_title = translate(IKeyNumberParagraph['title'].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
-
-
-@vocabulary_config(name=KEYNUMBER_PARAGRAPH_RENDERERS)
-class KeyNumberParagraphRendererVocabulary(RenderersVocabulary):
-    """Key numbers paragraph renderers vocabulary"""
-
-    content_interface = IKeyNumberParagraph
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/component/paragraph/map.py	Thu Sep 06 11:27:55 2018 +0200
@@ -0,0 +1,64 @@
+#
+# 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'
+
+
+from pyams_content.component.paragraph.interfaces.map import have_gis
+if have_gis:
+
+    # import standard library
+
+    # import interfaces
+    from pyams_content.component.paragraph.interfaces import IParagraphFactory
+    from pyams_content.component.paragraph.interfaces.map import IMapParagraph, MAP_PARAGRAPH_NAME, have_gis, \
+        MAP_PARAGRAPH_TYPE, MAP_PARAGRAPH_RENDERERS
+
+    # import packages
+    from pyams_content.component.paragraph import BaseParagraph, BaseParagraphFactory
+    from pyams_content.features.renderer import RenderersVocabulary
+    from pyams_utils.factory import factory_config
+    from pyams_utils.registry import utility_config
+    from pyams_utils.vocabulary import vocabulary_config
+    from zope.interface import implementer
+    from zope.schema.fieldproperty import FieldProperty
+
+
+    @implementer(IMapParagraph)
+    @factory_config(provided=IMapParagraph)
+    class MapParagraph(BaseParagraph):
+        """Map paragraph"""
+
+        icon_class = 'fa-map-marker'
+        icon_hint = MAP_PARAGRAPH_NAME
+
+        if have_gis:
+            gps_location = FieldProperty(IMapParagraph['gps_location'])
+            display_marker = FieldProperty(IMapParagraph['display_marker'])
+
+        renderer = FieldProperty(IMapParagraph['renderer'])
+
+
+    @utility_config(name=MAP_PARAGRAPH_TYPE, provides=IParagraphFactory)
+    class MapParagraphFactory(BaseParagraphFactory):
+        """Map paragraph factory"""
+
+        name = MAP_PARAGRAPH_NAME
+        content_type = MapParagraph
+        secondary_menu = True
+
+
+    @vocabulary_config(name=MAP_PARAGRAPH_RENDERERS)
+    class MapParagraphRenderersVocabulary(RenderersVocabulary):
+        """Map paragraph renderers vocabulary"""
+
+        content_interface = IMapParagraph
--- a/src/pyams_content/component/paragraph/zmi/__init__.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/component/paragraph/zmi/__init__.py	Thu Sep 06 11:27:55 2018 +0200
@@ -37,7 +37,7 @@
 from pyams_form.schema import ActionButton, CloseButton
 from pyams_form.security import ProtectedFormObjectMixin
 from pyams_pagelet.pagelet import pagelet_config
-from pyams_skin.event import get_json_switched_table_refresh_event
+from pyams_skin.event import get_json_switched_table_refresh_event, get_json_widget_refresh_event
 from pyams_skin.table import get_element_id
 from pyams_skin.viewlet.menu import MenuItem, MenuDivider
 from pyams_skin.viewlet.toolbar import ToolbarMenuItem
@@ -175,7 +175,7 @@
             return MenuDivider.__new__(cls)
         for factory_name in settings.allowed_paragraphs or ():
             factory = query_utility(IParagraphFactory, name=factory_name)
-            if factory.secondary_menu:
+            if (factory is not None) and factory.secondary_menu:
                 return MenuDivider.__new__(cls)
         return None
 
@@ -241,6 +241,20 @@
         if 'title' in changes.get(IBaseParagraph, ()):
             output.setdefault('events', []).append(
                 get_json_paragraph_refresh_event(self.context, self.request))
+        elif 'renderer' in self.widgets:
+            renderer_interface = self.widgets['renderer'].field.interface
+            if 'renderer' in changes.get(renderer_interface):
+                output.setdefault('events', []).append(
+                    get_json_widget_refresh_event(self.context, self.request, self.__class__, 'renderer'))
+                renderer = self.context.get_renderer()
+                if (renderer is not None) and \
+                   (renderer.settings_interface is not None):
+                    output['smallbox'] = {
+                        'status': 'info',
+                        'message': self.request.localizer.translate(_("You changed renderer selection. Don't omit to "
+                                                                      "update new renderer properties...")),
+                        'timeout': 5000
+                    }
         return output
 
 
--- a/src/pyams_content/component/paragraph/zmi/container.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/component/paragraph/zmi/container.py	Thu Sep 06 11:27:55 2018 +0200
@@ -44,7 +44,7 @@
 from pyams_skin.container import switch_element_visibility, switch_element_attribute
 from pyams_skin.page import DefaultPageHeaderAdapter
 from pyams_skin.table import BaseTable, I18nColumn, TrashColumn, SorterColumn, ImageColumn, \
-    VisibilitySwitcherColumn
+    VisibilitySwitcherColumn, AttributeSwitcherColumn
 from pyams_skin.viewlet.menu import MenuItem
 from pyams_template.template import template_config
 from pyams_utils.adapter import adapter_config, ContextRequestViewAdapter, NullAdapter
@@ -108,11 +108,29 @@
             'data-ams-post-reload': 'PyAMS_content.paragraphs.postReload',
             'data-ams-tablednd-drag-handle': 'td.sorter',
             'data-ams-tablednd-drop-target': 'set-paragraphs-order.json',
-            'data-ams-visibility-switcher': 'switch-paragraph-visibility.json',
-            'data-ams-anchor-switcher': 'switch-paragraph-anchor.json'
+            'data-ams-anchor-icon-on': 'fa fa-fw fa-anchor',
+            'data-ams-anchor-icon-off': 'fa fa-fw fa-anchor txt-color-silver opacity-50'
+        })
+        attributes.setdefault('td', {}).update({
+            'data-ams-attribute-switcher': self.get_switcher_target,
+            'data-ams-switcher-attribute-name': self.get_switcher_attribute
         })
         return attributes
 
+    @staticmethod
+    def get_switcher_target(element, column):
+        if column.__name__ == 'show-hide':
+            return 'switch-paragraph-visibility.json'
+        elif column.__name__ == 'anchor':
+            return 'switch-paragraph-anchor.json'
+
+    @staticmethod
+    def get_switcher_attribute(element, column):
+        if column.__name__ == 'show-hide':
+            return 'visible'
+        elif column.__name__ == 'anchor':
+            return 'anchor'
+
 
 class ParagraphContainerTable(ParagraphContainerBaseTable):
     """Paragraph container base table"""
@@ -186,16 +204,16 @@
 
 @adapter_config(name='anchor', context=(IParagraphContainerTarget, IPyAMSLayer, ParagraphContainerBaseTable),
                 provides=IColumn)
-class ParagraphContainerAnchorColumn(ProtectedFormObjectMixin, VisibilitySwitcherColumn):
+class ParagraphContainerAnchorColumn(ProtectedFormObjectMixin, AttributeSwitcherColumn):
     """Paragraphs container anchor switcher column"""
 
     switch_attribute = 'anchor'
-    visible_icon_class = 'fa fa-fw fa-anchor'
-    hidden_icon_class = 'fa fa-fw fa-anchor txt-color-silver opacity-50'
+
+    on_icon_class = 'fa fa-fw fa-anchor'
+    off_icon_class = 'fa fa-fw fa-anchor txt-color-silver opacity-50'
 
     icon_hint = _("Set navigation anchor")
 
-    url = 'PyAMS_content.paragraphs.switchAnchor'
     weight = 6
 
 
--- a/src/pyams_content/component/paragraph/zmi/frame.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/component/paragraph/zmi/frame.py	Thu Sep 06 11:27:55 2018 +0200
@@ -69,7 +69,8 @@
         'ams-tinymce-plugins': ['lists', ],
         'ams-tinymce-toolbar': 'undo redo | bold italic | bullist numlist',
         'ams-tinymce-toolbar1': False,
-        'ams-tinymce-toolbar2': False
+        'ams-tinymce-toolbar2': False,
+        'ams-tinymce-height': 150
     }
 
 
@@ -83,7 +84,7 @@
     """Framed text paragraph add menu"""
 
     label = _("Framed text...")
-    label_css_class = 'fa fa-fw fa-list-alt'
+    label_css_class = 'fa fa-fw fa-window-maximize'
     url = 'add-frame-paragraph.html'
     paragraph_type = FRAME_PARAGRAPH_TYPE
 
@@ -98,7 +99,7 @@
 
     legend = _("Add new framed text paragraph")
     dialog_class = 'modal-large'
-    icon_css_class = 'fa fa-fw fa-list-alt'
+    icon_css_class = 'fa fa-fw fa-window-maximize'
     label_css_class = 'control-label col-md-2'
     input_css_class = 'col-md-10'
 
@@ -124,7 +125,7 @@
 
     legend = _("Edit framed text paragraph properties")
     dialog_class = 'modal-large'
-    icon_css_class = 'fa fa-fw fa-list-alt'
+    icon_css_class = 'fa fa-fw fa-window-maximize'
     label_css_class = 'control-label col-md-2'
     input_css_class = 'col-md-10'
 
--- a/src/pyams_content/component/paragraph/zmi/header.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/component/paragraph/zmi/header.py	Thu Sep 06 11:27:55 2018 +0200
@@ -16,68 +16,27 @@
 # import standard library
 
 # import interfaces
-from pyams_content.component.paragraph.interfaces import IParagraphContainerTarget, IParagraphContainer
-from pyams_content.component.paragraph.interfaces.header import IHeaderParagraph, HEADER_PARAGRAPH_TYPE
-from pyams_content.component.paragraph.zmi.interfaces import IParagraphInnerEditor, IParagraphContainerView
+from pyams_content.component.paragraph.interfaces.header import IHeaderParagraph
+from pyams_content.component.paragraph.zmi.interfaces import IParagraphInnerEditor
 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 z3c.form.interfaces import INPUT_MODE
 
 # import packages
-from pyams_content.component.paragraph.header import HeaderParagraph
-from pyams_content.component.paragraph.zmi import BaseParagraphAJAXAddForm, BaseParagraphAJAXEditForm, \
-    BaseParagraphAddMenu, BaseParagraphPropertiesEditForm, get_json_paragraph_refresh_event, IParagraphEditFormButtons
+from pyams_content.component.paragraph.zmi import BaseParagraphAJAXEditForm, \
+    BaseParagraphPropertiesEditForm, get_json_paragraph_refresh_event, IParagraphEditFormButtons
 from pyams_content.features.renderer.zmi.widget import RendererFieldWidget
 from pyams_form.form import ajax_config
 from pyams_pagelet.pagelet import pagelet_config
 from pyams_skin.event import 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 z3c.form import field, button
 from zope.interface import implementer
 
 from pyams_content import _
 
 
-@viewlet_config(name='add-header-paragraph.menu', context=IParagraphContainerTarget, view=IParagraphContainerView,
-                layer=IPyAMSLayer, manager=IToolbarAddingMenu, weight=40)
-class HeaderParagraphAddMenu(BaseParagraphAddMenu):
-    """Header paragraph add menu"""
-
-    label = _("Header...")
-    label_css_class = 'fa fa-fw fa-download fa-rotate-180'
-    url = 'add-header-paragraph.html'
-    paragraph_type = HEADER_PARAGRAPH_TYPE
-
-
-@pagelet_config(name='add-header-paragraph.html', context=IParagraphContainerTarget, layer=IPyAMSLayer,
-                permission=MANAGE_CONTENT_PERMISSION)
-@ajax_config(name='add-header-paragraph.json', context=IParagraphContainerTarget, layer=IPyAMSLayer,
-             base=BaseParagraphAJAXAddForm)
-class HeaderParagraphAddForm(AdminDialogAddForm):
-    """Header paragraph add form"""
-
-    legend = _("Add new header paragraph")
-    icon_css_class = 'fa fa-fw fa-download fa-rotate-180'
-
-    fields = field.Fields(IHeaderParagraph).select('header', 'renderer')
-    edit_permission = MANAGE_CONTENT_PERMISSION
-
-    def updateWidgets(self, prefix=None):
-        super(HeaderParagraphAddForm, self).updateWidgets(prefix)
-        if 'header' in self.widgets:
-            self.widgets['header'].widget_css_class = 'input height-100'
-
-    def create(self, data):
-        return HeaderParagraph()
-
-    def add(self, object):
-        IParagraphContainer(self.context).append(object)
-
-
 @pagelet_config(name='properties.html', context=IHeaderParagraph, layer=IPyAMSLayer,
                 permission=MANAGE_CONTENT_PERMISSION)
 @ajax_config(name='properties.json', context=IHeaderParagraph, layer=IPyAMSLayer,
--- a/src/pyams_content/component/paragraph/zmi/keynumber.py	Tue Jul 17 15:12:43 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,127 +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.component.paragraph.interfaces import IParagraphContainerTarget, IParagraphContainer
-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
-from pyams_i18n.interfaces import II18n
-from pyams_skin.interfaces.viewlet import IToolbarAddingMenu
-from pyams_skin.layer import IPyAMSLayer
-from z3c.form.interfaces import INPUT_MODE
-
-# import packages
-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 ajax_config
-from pyams_pagelet.pagelet import pagelet_config
-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_viewlet.viewlet import viewlet_config
-from pyams_zmi.form import AdminDialogAddForm
-from z3c.form import field, button
-from zope.interface import implementer, Interface
-
-from pyams_content import _
-
-
-@viewlet_config(name='add-keynumber-paragraph.menu', context=IParagraphContainerTarget, view=IParagraphContainerView,
-                layer=IPyAMSLayer, manager=IToolbarAddingMenu, weight=600)
-class KeyNumberParagraphAddMenu(BaseParagraphAddMenu):
-    """Key number paragraph add menu"""
-    
-    label = _("Key numbers...")
-    label_css_class = 'fa fa-fw fa-dashboard'
-    url = 'add-keynumber-paragraph.html'
-    paragraph_type = KEYNUMBER_PARAGRAPH_TYPE
-    
-    
-@pagelet_config(name='add-keynumber-paragraph.html', context=IParagraphContainerTarget, layer=IPyAMSLayer,
-                permission=MANAGE_CONTENT_PERMISSION)
-@ajax_config(name='add-keynumber-paragraph.json', context=IParagraphContainerTarget, layer=IPyAMSLayer,
-             base=BaseParagraphAJAXAddForm)
-class KeyNumberParagraphAddForm(AdminDialogAddForm):
-    """Key number paragraph add form"""
-    
-    legend = _("Add new key number paragraph")
-    icon_css_class = 'fa fa-fw fa-dashboard'
-    
-    fields = field.Fields(IKeyNumberParagraph).select('title', 'renderer')
-    edit_permission = MANAGE_CONTENT_PERMISSION
-    
-    def create(self, data):
-        return KeyNumberParagraph()
-    
-    def add(self, object):
-        IParagraphContainer(self.context).append(object)
-
-    
-@pagelet_config(name='properties.html', context=IKeyNumberParagraph, layer=IPyAMSLayer,
-                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"""
-
-    prefix = 'keynumbers_properties.'
-
-    @property
-    def title(self):
-        content = get_parent(self.context, IWfSharedContent)
-        return II18n(content).query_attribute('title', request=self.request)
-
-    legend = _("Edit key number paragraph properties")
-    icon_css_class = 'fa fa-fw fa-dashboard'
-
-    fields = field.Fields(IKeyNumberParagraph).select('title', 'renderer')
-    fields['renderer'].widgetFactory = RendererFieldWidget
-
-    edit_permission = MANAGE_CONTENT_PERMISSION
-
-
-@adapter_config(context=(IKeyNumberParagraph, IPyAMSLayer), provides=IParagraphInnerEditor)
-@ajax_config(name='inner-properties.json', context=IKeyNumberParagraph, layer=IPyAMSLayer,
-             base=BaseParagraphAJAXEditForm)
-@implementer(IInnerForm, IKeyNumbersParentForm)
-class KeyNumberParagraphInnerEditForm(KeyNumberParagraphPropertiesEditForm):
-    """Key number paragraph inner edit form"""
-
-    legend = None
-
-    @property
-    def buttons(self):
-        if self.mode == INPUT_MODE:
-            return button.Buttons(IParagraphEditFormButtons)
-        else:
-            return button.Buttons()
-
-    def get_ajax_output(self, changes):
-        output = super(self.__class__, self).get_ajax_output(changes)
-        updated = changes.get(IKeyNumberParagraph, ())
-        if 'renderer' in updated:
-            output.setdefault('events', []).append(
-                get_json_widget_refresh_event(self.context, self.request, KeyNumberParagraphInnerEditForm, 'renderer'))
-        return output
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/component/paragraph/zmi/map.py	Thu Sep 06 11:27:55 2018 +0200
@@ -0,0 +1,125 @@
+#
+# 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'
+
+
+from pyams_content.component.paragraph.interfaces.map import have_gis
+if have_gis:
+
+    # import standard library
+
+    # import interfaces
+    from pyams_content.component.paragraph.interfaces import IParagraphContainerTarget, IParagraphContainer, \
+        IBaseParagraph, PARAGRAPH_HIDDEN_FIELDS
+    from pyams_content.component.paragraph.interfaces.map import MAP_PARAGRAPH_TYPE, IMapParagraph
+    from pyams_content.component.paragraph.zmi.interfaces import IParagraphInnerEditor
+    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 z3c.form.interfaces import INPUT_MODE
+
+    # import packages
+    from pyams_content.component.paragraph.map import MapParagraph
+    from pyams_content.component.paragraph.zmi import IParagraphContainerView, BaseParagraphAddMenu, \
+        BaseParagraphAJAXAddForm, BaseParagraphAJAXEditForm, 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_pagelet.pagelet import pagelet_config
+    from pyams_skin.event import get_json_form_refresh_event
+    from pyams_utils.adapter import adapter_config
+    from pyams_viewlet.viewlet import viewlet_config
+    from pyams_zmi.form import AdminDialogAddForm
+    from z3c.form import field, button
+    from zope.interface import implementer
+
+    from pyams_content import _
+
+
+    @viewlet_config(name='add-map-paragraph.menu', context=IParagraphContainerTarget, view=IParagraphContainerView,
+                    layer=IPyAMSLayer, manager=IToolbarAddingMenu, weight=600)
+    class MapParagraphAddMenu(BaseParagraphAddMenu):
+        """Map paragraph add menu"""
+
+        label = _("Location map...")
+        label_css_class = 'fa fa-fw fa-map-marker'
+        url = 'add-map-paragraph.html'
+        paragraph_type = MAP_PARAGRAPH_TYPE
+
+
+    @pagelet_config(name='add-map-paragraph.html', context=IParagraphContainerTarget, layer=IPyAMSLayer,
+                    permission=MANAGE_CONTENT_PERMISSION)
+    @ajax_config(name='add-map-paragraph.json', context=IParagraphContainerTarget, layer=IPyAMSLayer,
+                 base=BaseParagraphAJAXAddForm)
+    class MapParagraphAddForm(AdminDialogAddForm):
+        """Map paragraph add form"""
+
+        legend = _("Add new location map")
+        dialog_class = 'modal-large'
+        icon_css_class = 'fa fa-fw fa-map-marker'
+
+        fields = field.Fields(IMapParagraph).omit(*PARAGRAPH_HIDDEN_FIELDS)
+        edit_permission = MANAGE_CONTENT_PERMISSION
+
+        def create(self, data):
+            return MapParagraph()
+
+        def add(self, object):
+            IParagraphContainer(self.context).append(object)
+
+
+    @pagelet_config(name='properties.html', context=IMapParagraph, layer=IPyAMSLayer,
+                    permission=MANAGE_CONTENT_PERMISSION)
+    @ajax_config(name='properties.json', context=IMapParagraph, request_type=IPyAMSLayer,
+                 base=BaseParagraphAJAXEditForm)
+    class MapParagraphPropertiesEditForm(BaseParagraphPropertiesEditForm):
+        """Map paragraph properties edit form"""
+
+        prefix = 'map_properties.'
+
+        legend = _("Edit location map properties")
+        icon_css_class = 'fa fa-fw fa-map-marker'
+
+        fields = field.Fields(IMapParagraph).omit(*PARAGRAPH_HIDDEN_FIELDS)
+        fields['renderer'].widgetFactory = RendererFieldWidget
+
+        edit_permission = MANAGE_CONTENT_PERMISSION
+
+
+    @adapter_config(context=(IMapParagraph, IPyAMSLayer), provides=IParagraphInnerEditor)
+    @ajax_config(name='inner-properties.json', context=IMapParagraph, layer=IPyAMSLayer,
+                 base=BaseParagraphAJAXEditForm)
+    @implementer(IInnerForm)
+    class MapParagraphInnerEditForm(MapParagraphPropertiesEditForm):
+        """Map paragraph inner edit form"""
+
+        legend = None
+
+        @property
+        def buttons(self):
+            if self.mode == INPUT_MODE:
+                return button.Buttons(IParagraphEditFormButtons)
+            else:
+                return button.Buttons()
+
+        def get_ajax_output(self, changes):
+            output = super(self.__class__, self).get_ajax_output(changes)
+            updated = changes.get(IBaseParagraph, ())
+            if 'title' in updated:
+                output.setdefault('events', []).append(get_json_paragraph_refresh_event(self.context, self.request))
+            updated = changes.get(IMapParagraph, ())
+            if 'renderer' in updated:
+                output.setdefault('events', []).append(get_json_form_refresh_event(self.context, self.request,
+                                                                                   MapParagraphInnerEditForm))
+            return output
--- a/src/pyams_content/component/paragraph/zmi/milestone.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/component/paragraph/zmi/milestone.py	Thu Sep 06 11:27:55 2018 +0200
@@ -179,11 +179,18 @@
             'id': self.id,
             'data-ams-location': absolute_url(IMilestoneContainer(self.context), self.request),
             'data-ams-tablednd-drag-handle': 'td.sorter',
-            'data-ams-tablednd-drop-target': 'set-milestones-order.json',
-            'data-ams-visibility-switcher': 'switch-milestone-visibility.json'
+            'data-ams-tablednd-drop-target': 'set-milestones-order.json'
         }
+        attributes.setdefault('td', {}).update({
+            'data-ams-attribute-switcher': self.get_switcher_target
+        })
         return attributes
 
+    @staticmethod
+    def get_switcher_target(element, column):
+        if column.__name__ == 'show-hide':
+            return 'switch-milestone-visibility.json'
+
     @reify
     def values(self):
         return list(super(MilestonesTable, self).values)
--- a/src/pyams_content/component/paragraph/zmi/pictogram.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/component/paragraph/zmi/pictogram.py	Thu Sep 06 11:27:55 2018 +0200
@@ -181,11 +181,18 @@
             'id': self.id,
             'data-ams-location': absolute_url(IPictogramContainer(self.context), self.request),
             'data-ams-tablednd-drag-handle': 'td.sorter',
-            'data-ams-tablednd-drop-target': 'set-pictograms-order.json',
-            'data-ams-visibility-switcher': 'switch-pictogram-visibility.json'
+            'data-ams-tablednd-drop-target': 'set-pictograms-order.json'
         }
+        attributes.setdefault('td', {}).update({
+            'data-ams-attribute-switcher': self.get_switcher_target
+        })
         return attributes
 
+    @staticmethod
+    def get_switcher_target(element, column):
+        if column.__name__ == 'show-hide':
+            return 'switch-pictogram-visibility.json'
+
     @reify
     def values(self):
         return list(super(PictogramsTable, self).values)
@@ -214,6 +221,19 @@
     return {'status': 'success'}
 
 
+@adapter_config(name='show-hide', context=(IPictogramContainerTarget, IPyAMSLayer, PictogramsTable),
+                provides=IColumn)
+class PictogramsTableShowHideColumn(ProtectedFormObjectMixin, VisibilitySwitcherColumn):
+    """Pictograms container visibility switcher column"""
+
+
+@view_config(name='switch-pictogram-visibility.json', context=IPictogramContainer, request_type=IPyAMSLayer,
+             permission=MANAGE_CONTENT_PERMISSION, renderer='json', xhr=True)
+def switch_pictogram_visibility(request):
+    """Set pictogram visibility"""
+    return switch_element_visibility(request, IPictogramContainer)
+
+
 @adapter_config(name='image', context=(IPictogramContainerTarget, IPyAMSLayer, PictogramsTable), provides=IColumn)
 class PictogramsTableImageColumn(GetAttrColumn):
     """Pictogram image column"""
@@ -233,19 +253,6 @@
         return '--'
 
 
-@adapter_config(name='show-hide', context=(IPictogramContainerTarget, IPyAMSLayer, PictogramsTable),
-                provides=IColumn)
-class PictogramsTableShowHideColumn(ProtectedFormObjectMixin, VisibilitySwitcherColumn):
-    """Pictograms container visibility switcher column"""
-
-
-@view_config(name='switch-pictogram-visibility.json', context=IPictogramContainer, request_type=IPyAMSLayer,
-             permission=MANAGE_CONTENT_PERMISSION, renderer='json', xhr=True)
-def switch_pictogram_visibility(request):
-    """Set pictogram visibility"""
-    return switch_element_visibility(request, IPictogramContainer)
-
-
 @adapter_config(name='name', context=(IPictogramContainerTarget, IPyAMSLayer, PictogramsTable), provides=IColumn)
 class PictogramsTableNameColumn(I18nColumn, I18nAttrColumn):
     """Pictograms table name column"""
@@ -257,7 +264,9 @@
     def getValue(self, obj):
         value = super(PictogramsTableNameColumn, self).getValue(obj)
         if not value:
-            value = II18n(obj.pictogram).query_attribute('header', request=self.request)
+            pictogram = obj.pictogram
+            if pictogram is not None:
+                value = II18n(pictogram).query_attribute('header', request=self.request)
         return value or BaseParagraph.empty_title
 
 
--- a/src/pyams_content/component/theme/zmi/manager.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/component/theme/zmi/manager.py	Thu Sep 06 11:27:55 2018 +0200
@@ -9,7 +9,6 @@
 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
 # FOR A PARTICULAR PURPOSE.
 #
-from pyams_content.component.theme import ICollectionsManagerTarget, ICollectionsManager
 
 __docformat__ = 'restructuredtext'
 
@@ -17,8 +16,10 @@
 # import standard library
 
 # import interfaces
-from pyams_content.component.theme.interfaces import ITagsManagerTarget, ITagsManager, IThemesManagerTarget, \
-    IThemesManager
+from pyams_content.component.theme.interfaces import \
+    ITagsManagerTarget, ITagsManager, \
+    IThemesManagerTarget, IThemesManager, \
+    ICollectionsManagerTarget, ICollectionsManager
 from pyams_content.interfaces import MANAGE_TOOL_PERMISSION, MANAGE_SITE_ROOT_PERMISSION
 from pyams_skin.layer import IPyAMSLayer
 from pyams_utils.interfaces.data import IObjectData
@@ -29,7 +30,7 @@
 from pyams_content.skin import pyams_content
 from pyams_form.form import ajax_config
 from pyams_pagelet.pagelet import pagelet_config
-from pyams_skin.viewlet.menu import MenuItem
+from pyams_skin.viewlet.menu import MenuItem, MenuDivider
 from pyams_utils.fanstatic import get_resource_path
 from pyams_viewlet.viewlet import viewlet_config
 from pyams_zmi.form import AdminDialogEditForm
@@ -43,6 +44,12 @@
 # Tags management view
 #
 
+@viewlet_config(name='tags.divider', context=ITagsManagerTarget, layer=IAdminLayer,
+                manager=IPropertiesMenu, permission=MANAGE_SITE_ROOT_PERMISSION, weight=39)
+class TagsManagerMenuDivider(MenuDivider):
+    """Tags manager menu divider"""
+
+
 @viewlet_config(name='tags-manager.menu', context=ITagsManagerTarget, layer=IAdminLayer,
                 manager=IPropertiesMenu, permission=MANAGE_SITE_ROOT_PERMISSION, weight=40)
 class TagsManagerMenu(MenuItem):
--- a/src/pyams_content/component/video/interfaces/__init__.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/component/video/interfaces/__init__.py	Thu Sep 06 11:27:55 2018 +0200
@@ -45,8 +45,8 @@
 class IExternalVideo(IAttributeAnnotatable):
     """Base interface for external video integration"""
 
-    description = I18nTextField(title=_("Description"),
-                                description=_("File description displayed by front-office template"),
+    description = I18nTextField(title=_("Associated text"),
+                                description=_("Video description displayed by front-office template"),
                                 required=False)
 
     author = TextLine(title=_("Author"),
--- a/src/pyams_content/features/alert/__init__.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/features/alert/__init__.py	Thu Sep 06 11:27:55 2018 +0200
@@ -19,58 +19,28 @@
 # import interfaces
 from pyams_content.features.alert.interfaces import IAlertItem
 from pyams_content.interfaces import MANAGE_SITE_ROOT_PERMISSION
-from pyams_content.reference.pictograms import IPictogramTable
 from pyams_form.interfaces.form import IFormContextPermissionChecker
 
 # import packages
-from pyams_sequence.reference import get_reference_target
+from pyams_content.component.links import InternalReferenceMixin
 from pyams_utils.adapter import adapter_config, ContextAdapter
-from pyams_utils.registry import query_utility
-from pyams_utils.zodb import volatile_property
 from zope.container.contained import Contained
 from zope.interface import implementer
 from zope.schema.fieldproperty import FieldProperty
 
 
 @implementer(IAlertItem)
-class AlertItem(Persistent, Contained):
+class AlertItem(Persistent, Contained, InternalReferenceMixin):
     """Alert item persistent class"""
 
     visible = FieldProperty(IAlertItem['visible'])
     gravity = FieldProperty(IAlertItem['gravity'])
-    header = FieldProperty(IAlertItem['header'])
     message = FieldProperty(IAlertItem['message'])
     reference = FieldProperty(IAlertItem['reference'])
-    _pictogram_name = FieldProperty(IAlertItem['pictogram_name'])
     start_date = FieldProperty(IAlertItem['start_date'])
     end_date = FieldProperty(IAlertItem['end_date'])
     maximum_interval = FieldProperty(IAlertItem['maximum_interval'])
 
-    @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)
-        return table.get(self.pictogram_name)
-
-    @volatile_property
-    def target(self):
-        return get_reference_target(self.reference)
-
-    def get_target(self, state=None):
-        if not state:
-            return self.target
-        else:
-            return get_reference_target(self.reference)
-
 
 @adapter_config(context=IAlertItem, provides=IFormContextPermissionChecker)
 class AlertitemPermissionChecker(ContextAdapter):
--- a/src/pyams_content/features/alert/interfaces.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/features/alert/interfaces.py	Thu Sep 06 11:27:55 2018 +0200
@@ -18,7 +18,6 @@
 
 # import interfaces
 from pyams_content.interfaces.container import IOrderedContainer
-from pyams_content.reference.pictograms.interfaces import PICTOGRAM_VOCABULARY
 from pyams_sequence.interfaces import IInternalReference
 from zope.annotation import IAttributeAnnotatable
 
@@ -37,10 +36,11 @@
 
 
 ALERT_GRAVITY_NAMES = OrderedDict((
-    ('success', _("Success")),
+    ('alert', _("Alert")),
+    ('alert_end', _("End of alert")),
     ('info', _("Information")),
     ('warning', _("Warning")),
-    ('danger', _("Danger"))
+    ('recommend', _("Recommendation"))
 ))
 
 ALERT_GRAVITY_VOCABULARY = SimpleVocabulary([SimpleTerm(v, title=t) for v, t in ALERT_GRAVITY_NAMES.items()])
@@ -62,27 +62,16 @@
                      default='info',
                      vocabulary=ALERT_GRAVITY_VOCABULARY)
 
-    header = I18nTextLineField(title=_('alert-header', default="Heading"),
-                               description=_("Short alert header (Alert, Important...)"),
-                               required=False)
-
     message = I18nTextLineField(title=_("Message"),
                                 description=_("Alert message"),
                                 required=True)
 
     reference = InternalReferenceField(title=_("Internal reference"),
                                        description=_("Internal link target reference. You can search a reference using "
-                                                "'+' followed by internal number, of by entering text matching "
-                                                "content title."),
+                                                     "'+' followed by internal number, of by entering text matching "
+                                                     "content title."),
                                        required=False)
 
-    pictogram_name = Choice(title=_("Pictogram"),
-                            description=_("Name of the pictogram to select"),
-                            required=False,
-                            vocabulary=PICTOGRAM_VOCABULARY)
-
-    pictogram = Attribute("Selected pictogram object")
-
     start_date = Datetime(title=_("Display start date"),
                           description=_("First date at which alert should be displayed"),
                           required=False)
--- a/src/pyams_content/features/alert/zmi/container.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/features/alert/zmi/container.py	Thu Sep 06 11:27:55 2018 +0200
@@ -75,11 +75,18 @@
             'data-ams-plugin-pyams_content-src': get_resource_path(pyams_content),
             'data-ams-location': absolute_url(IAlertContainer(self.context), self.request),
             'data-ams-tablednd-drag-handle': 'td.sorter',
-            'data-ams-tablednd-drop-target': 'set-alerts-order.json',
-            'data-ams-visibility-switcher': 'switch-alert-visibility.json'
+            'data-ams-tablednd-drop-target': 'set-alerts-order.json'
+        })
+        attributes.setdefault('td', {}).update({
+            'data-ams-attribute-switcher': self.get_switcher_target
         })
         return attributes
 
+    @staticmethod
+    def get_switcher_target(element, column):
+        if column.__name__ == 'show-hide':
+            return 'switch-alert-visibility.json'
+
     @reify
     def values(self):
         return list(super(AlertContainerTable, self).values)
@@ -131,44 +138,13 @@
     return {'visible': alert.visible}
 
 
-@adapter_config(name='pictogram', context=(IAlertTarget, IPyAMSLayer, AlertContainerTable), provides=IColumn)
-class AlertContainerPictogramColumn(GetAttrColumn):
-    """Alert container pictogram image column"""
-
-    header = ''
-    weight = 10
-
-    cssClasses = {'td': 'text-center width-50'}
-    dt_sortable = 'false'
-
-    def getValue(self, obj):
-        pictogram = obj.pictogram
-        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++32x32'))
-        return '--'
-
-
-@adapter_config(name='header', context=(IAlertTarget, IPyAMSLayer, AlertContainerTable), provides=IColumn)
-class AlertContainerHeaderColumn(I18nColumn, I18nAttrColumn):
-    """Alert container header column"""
-
-    _header = _('alert-header', default="Heading")
-    attrName = 'header'
-    weight = 20
-
-    def getValue(self, obj):
-        return super(AlertContainerHeaderColumn, self).getValue(obj) or '--'
-
-
 @adapter_config(name='name', context=(IAlertTarget, IPyAMSLayer, AlertContainerTable), provides=IColumn)
 class AlertContainerNameColumn(I18nColumn, I18nAttrColumn):
     """Alert container message column"""
 
     _header = _("Message")
     attrName = 'message'
-    weight = 30
+    weight = 10
 
     def getValue(self, obj):
         value = super(AlertContainerNameColumn, self).getValue(obj)
--- a/src/pyams_content/features/footer/__init__.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/features/footer/__init__.py	Thu Sep 06 11:27:55 2018 +0200
@@ -18,16 +18,20 @@
 # import interfaces
 from pyams_content.features.footer.interfaces import FOOTER_RENDERERS, IFooterRenderer, IFooterSettings, IFooterTarget, \
     FOOTER_SETTINGS_KEY, IFooterRendererSettings, FOOTER_RENDERER_SETTINGS_KEY
+from zope.lifecycleevent.interfaces import IObjectModifiedEvent
 from zope.traversing.interfaces import ITraversable
 
 # import packages
 from persistent import Persistent
+from pyams_cache.beaker import get_cache
 from pyams_content.features.renderer import RenderedContentMixin
+from pyams_portal.portlet import PORTLETS_CACHE_REGION, PORTLETS_CACHE_NAME
 from pyams_utils.adapter import adapter_config, ContextAdapter, get_annotation_adapter
 from pyams_utils.inherit import BaseInheritInfo, InheritedFieldProperty
 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.interface import implementer, noLongerProvides, alsoProvides
 from zope.location import Location, locate
 from zope.schema.fieldproperty import FieldProperty
@@ -77,6 +81,14 @@
     return get_annotation_adapter(context, FOOTER_SETTINGS_KEY, FooterSettings, name='++footer++')
 
 
+@subscriber(IObjectModifiedEvent, context_selector=IFooterSettings)
+def handle_modified_footer_settings(event):
+    """Clear cache if modified footer settings"""
+    renderer = event.object.get_renderer()
+    portlets_cache = get_cache(PORTLETS_CACHE_REGION, PORTLETS_CACHE_NAME)
+    portlets_cache.remove(renderer.cache_key)
+
+
 @adapter_config(name='footer', context=IFooterTarget, provides=ITraversable)
 class FooterTargetNamespace(ContextAdapter):
     """Footer target '++footer++' namespace traverser"""
--- a/src/pyams_content/features/footer/interfaces/__init__.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/features/footer/interfaces/__init__.py	Thu Sep 06 11:27:55 2018 +0200
@@ -54,6 +54,7 @@
 
     name = Attribute("Renderer name")
     settings_key = Attribute("Renderer settings key")
+    cache_key = Attribute("Renderer cache key")
 
 
 class IFooterRendererSettings(IRendererSettings):
--- a/src/pyams_content/features/footer/skin/__init__.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/features/footer/skin/__init__.py	Thu Sep 06 11:27:55 2018 +0200
@@ -32,13 +32,18 @@
 from pyams_utils.adapter import adapter_config
 from pyams_utils.traversing import get_parent
 from pyramid.decorator import reify
+from zope.interface import implementer
 
 from pyams_content import _
 
 
+@implementer(IFooterRenderer)
 class BaseFooterRenderer(BaseContentRenderer):
     """Base footer renderer"""
 
+    name = None
+    settings_key = None
+
     @reify
     def settings_target(self):
         context = self.request.annotations.get(DISPLAY_CONTEXT)
@@ -56,23 +61,28 @@
             settings = IFooterSettings(settings.parent)
         return settings.settings
 
+    @reify
+    def cache_key(self):
+        return PORTLETS_CACHE_KEY.format(portlet=self.name,
+                                         context=ICacheKeyValue(self.settings_target),
+                                         lang=self.request.locale_name)
+
     def render(self):
         preview_mode = self.request.annotations.get(PREVIEW_MODE, False)
         if preview_mode:
             return super(BaseFooterRenderer, self).render()
         else:
             portlets_cache = get_cache(PORTLETS_CACHE_REGION, PORTLETS_CACHE_NAME)
-            cache_key = PORTLETS_CACHE_KEY.format(portlet=self.name,
-                                                  context=ICacheKeyValue(self.settings_target))
+            cache_key = self.cache_key
             if self.context is not self.request.context:  # display shared content
                 cache_key = '{0}::shared'.format(cache_key)
             try:
                 result = portlets_cache.get_value(cache_key)
-                logger.debug("Retrieving header content from cache key {0}".format(cache_key))
+                logger.debug("Retrieving footer content from cache key {0}".format(cache_key))
             except KeyError:
                 result = super(BaseFooterRenderer, self).render()
                 portlets_cache.set_value(cache_key, result)
-                logger.debug("Storing header content for cache key {0}".format(cache_key))
+                logger.debug("Storing footer content to cache key {0}".format(cache_key))
             return result
 
 
--- a/src/pyams_content/features/header/__init__.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/features/header/__init__.py	Thu Sep 06 11:27:55 2018 +0200
@@ -18,16 +18,20 @@
 # import interfaces
 from pyams_content.features.header.interfaces import HEADER_RENDERERS, IHeaderRenderer, IHeaderSettings, IHeaderTarget, \
     HEADER_SETTINGS_KEY, IHeaderRendererSettings, HEADER_RENDERER_SETTINGS_KEY
+from zope.lifecycleevent.interfaces import IObjectModifiedEvent
 from zope.traversing.interfaces import ITraversable
 
 # import packages
 from persistent import Persistent
+from pyams_cache.beaker import get_cache
 from pyams_content.features.renderer import RenderedContentMixin
+from pyams_portal.portlet import PORTLETS_CACHE_REGION, PORTLETS_CACHE_NAME
 from pyams_utils.adapter import adapter_config, ContextAdapter, get_annotation_adapter
 from pyams_utils.inherit import BaseInheritInfo, InheritedFieldProperty
 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.interface import implementer, noLongerProvides, alsoProvides
 from zope.location import Location, locate
 from zope.schema.fieldproperty import FieldProperty
@@ -77,6 +81,14 @@
     return get_annotation_adapter(context, HEADER_SETTINGS_KEY, HeaderSettings, name='++header++')
 
 
+@subscriber(IObjectModifiedEvent, context_selector=IHeaderSettings)
+def handle_modified_header_settings(event):
+    """Clear cache if modified header settings"""
+    renderer = event.object.get_renderer()
+    portlets_cache = get_cache(PORTLETS_CACHE_REGION, PORTLETS_CACHE_NAME)
+    portlets_cache.remove(renderer.cache_key)
+
+
 @adapter_config(name='header', context=IHeaderTarget, provides=ITraversable)
 class HeaderTargetNamespace(ContextAdapter):
     """Header target '++header++' namespace traverser"""
--- a/src/pyams_content/features/header/interfaces/__init__.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/features/header/interfaces/__init__.py	Thu Sep 06 11:27:55 2018 +0200
@@ -54,6 +54,8 @@
 
     name = Attribute("Renderer name")
     settings_key = Attribute("Renderer settings key")
+    cache_key = Attribute("Renderer cache key")
+
 
 
 class IHeaderRendererSettings(IRendererSettings):
--- a/src/pyams_content/features/header/skin/__init__.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/features/header/skin/__init__.py	Thu Sep 06 11:27:55 2018 +0200
@@ -33,13 +33,18 @@
 from pyams_utils.adapter import adapter_config
 from pyams_utils.traversing import get_parent
 from pyramid.decorator import reify
+from zope.interface import implementer
 
 from pyams_content import _
 
 
+@implementer(IHeaderRenderer)
 class BaseHeaderRenderer(BaseContentRenderer):
     """Base header renderer"""
 
+    name = None
+    settings_key = None
+
     @reify
     def settings_target(self):
         context = self.request.annotations.get(DISPLAY_CONTEXT)
@@ -57,6 +62,12 @@
             settings = IHeaderSettings(settings.parent)
         return settings.settings
 
+    @reify
+    def cache_key(self):
+        return PORTLETS_CACHE_KEY.format(portlet=self.name,
+                                         context=ICacheKeyValue(self.settings_target),
+                                         lang=self.request.locale_name)
+
     @property
     def main_header_class(self):
         request = self.request
@@ -68,8 +79,7 @@
             return super(BaseHeaderRenderer, self).render()
         else:
             portlets_cache = get_cache(PORTLETS_CACHE_REGION, PORTLETS_CACHE_NAME)
-            cache_key = PORTLETS_CACHE_KEY.format(portlet=self.name,
-                                                  context=ICacheKeyValue(self.settings_target))
+            cache_key = self.cache_key
             if self.context is not self.request.context:  # display shared content
                 cache_key = '{0}::shared'.format(cache_key)
             try:
@@ -78,7 +88,7 @@
             except KeyError:
                 result = super(BaseHeaderRenderer, self).render()
                 portlets_cache.set_value(cache_key, result)
-                logger.debug("Storing header content for cache key {0}".format(cache_key))
+                logger.debug("Storing header content to cache key {0}".format(cache_key))
             return result
 
 
--- a/src/pyams_content/features/menu/__init__.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/features/menu/__init__.py	Thu Sep 06 11:27:55 2018 +0200
@@ -20,8 +20,7 @@
 
 # import packages
 from pyams_content.component.association.container import AssociationContainer
-from pyams_sequence.reference import get_reference_target
-from pyams_utils.zodb import volatile_property
+from pyams_content.component.links import InternalReferenceMixin
 from zope.interface import implementer
 from zope.schema.fieldproperty import FieldProperty
 
@@ -31,23 +30,13 @@
 #
 
 @implementer(IMenu)
-class Menu(AssociationContainer):
+class Menu(AssociationContainer, InternalReferenceMixin):
     """Associations menu"""
 
     visible = FieldProperty(IMenu['visible'])
     title = FieldProperty(IMenu['title'])
     reference = FieldProperty(IMenu['reference'])
 
-    @volatile_property
-    def target(self):
-        return get_reference_target(self.reference)
-
-    def get_target(self, state=None):
-        if not state:
-            return self.target
-        else:
-            return get_reference_target(self.reference)
-
 
 @implementer(IMenusContainer)
 class MenusContainer(AssociationContainer):
--- a/src/pyams_content/features/menu/portlet/navigation/double.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/features/menu/portlet/navigation/double.py	Thu Sep 06 11:27:55 2018 +0200
@@ -46,7 +46,6 @@
     """Double navigation portlet settings"""
 
     title = FieldProperty(IDoubleNavigationPortletSettings['title'])
-    subtitle = FieldProperty(IDoubleNavigationPortletSettings['subtitle'])
 
     @property
     def menus(self):
--- a/src/pyams_content/features/menu/portlet/navigation/interfaces/double.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/features/menu/portlet/navigation/interfaces/double.py	Thu Sep 06 11:27:55 2018 +0200
@@ -32,10 +32,6 @@
                               description=_("Portlet main title"),
                               required=False)
 
-    subtitle = I18nTextLineField(title=_("Subtitle"),
-                                 description=_("Portlet subtitle"),
-                                 required=False)
-
     menus = Attribute("Navigation menus")
 
 
--- a/src/pyams_content/features/menu/portlet/navigation/zmi/simple.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/features/menu/portlet/navigation/zmi/simple.py	Thu Sep 06 11:27:55 2018 +0200
@@ -35,7 +35,7 @@
 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 zope.interface import Interface
 
 from pyams_content import _
 
@@ -87,4 +87,7 @@
         if not IInternalLink.providedBy(link):
             return True
         target = link.get_target()
-        return (target is not None) and IWorkflowPublicationInfo(target).is_published()
+        if target is not None:
+            publication_info = IWorkflowPublicationInfo(target, None)
+            if publication_info is not None:
+                return publication_info.is_published()
--- a/src/pyams_content/features/menu/zmi/__init__.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/features/menu/zmi/__init__.py	Thu Sep 06 11:27:55 2018 +0200
@@ -17,8 +17,6 @@
 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
@@ -33,15 +31,17 @@
 
 # import packages
 from pyams_content.component.association.zmi import AssociationsTable, AssociationsTablePublicNameColumn
+from pyams_content.component.links.zmi import \
+    InternalLinkAddMenu, InternalLinkAddForm, InternalLinkPropertiesEditForm, \
+    ExternalLinkAddMenu, ExternalLinkAddForm, ExternalLinkPropertiesEditForm
 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.table import BaseTable, SorterColumn, VisibilitySwitcherColumn, I18nColumn, TrashColumn, get_table_id
 from pyams_skin.viewlet.toolbar import ToolbarAction
-from pyams_utils.adapter import adapter_config, ContextRequestViewAdapter, ContextAdapter, NullAdapter
+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
@@ -170,11 +170,18 @@
         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'
+            'data-ams-tablednd-drop-target': 'set-menus-order.json'
+        })
+        attributes.setdefault('td', {}).update({
+            'data-ams-attribute-switcher': self.get_switcher_target
         })
         return attributes
 
+    @staticmethod
+    def get_switcher_target(element, column):
+        if column.__name__ == 'show-hide':
+            return 'switch-menu-visibility.json'
+
 
 @adapter_config(context=(IMenusContainerTarget, IPyAMSLayer, MenusTable), provides=IValues)
 class MenusTableValuesAdapter(ContextRequestViewAdapter):
@@ -487,13 +494,3 @@
     """Menu external link properties edit form"""
 
     edit_permission = None  # managed by IFormContextPermissionChecker adapter
-
-
-#
-# Custom adapters
-#
-
-@adapter_config(name='size', context=(IMenu, IPyAMSLayer, MenuLinksTable), provides=IColumn)
-@adapter_config(name='size', context=(IMenuLinksContainerTarget, IPyAMSLayer, LinksTable), provides=IColumn)
-class MenuLinksTableSizeColumn(NullAdapter):
-    """Menu links table size column"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/features/redirect/__init__.py	Thu Sep 06 11:27:55 2018 +0200
@@ -0,0 +1,78 @@
+#
+# 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 re
+
+# import interfaces
+from pyams_content.features.redirect.interfaces import IRedirectionRule
+from pyams_content.interfaces import MANAGE_SITE_ROOT_PERMISSION
+from pyams_form.interfaces.form import IFormContextPermissionChecker
+
+# import packages
+from persistent import Persistent
+from pyams_content.component.links import InternalReferenceMixin
+from pyams_utils.adapter import adapter_config, ContextAdapter
+from pyams_utils.url import canonical_url
+from pyams_utils.zodb import volatile_property
+from zope.container.contained import Contained
+from zope.interface import implementer
+from zope.schema.fieldproperty import FieldProperty
+
+
+@implementer(IRedirectionRule)
+class RedirectionRule(Persistent, Contained, InternalReferenceMixin):
+    """Redirection rule persistent class"""
+
+    active = FieldProperty(IRedirectionRule['active'])
+    chained = FieldProperty(IRedirectionRule['chained'])
+    permanent = FieldProperty(IRedirectionRule['permanent'])
+    _url_pattern = FieldProperty(IRedirectionRule['url_pattern'])
+    reference = FieldProperty(IRedirectionRule['reference'])
+    target_url = FieldProperty(IRedirectionRule['target_url'])
+
+    @property
+    def url_pattern(self):
+        return self._url_pattern
+
+    @url_pattern.setter
+    def url_pattern(self, value):
+        if value != self._url_pattern:
+            self._url_pattern = value
+            del self.pattern
+
+    @volatile_property
+    def pattern(self):
+        return re.compile(self.url_pattern)
+
+    def match(self, source_url):
+        return self.pattern.match(source_url)
+
+    def rewrite(self, source_url, request):
+        target_url = None
+        if self.reference:
+            target = self.target
+            if target is not None:
+                target_url = canonical_url(target, request)
+        else:
+            target_url = self.pattern.sub(self.target_url, source_url)
+        return target_url
+
+
+@adapter_config(context=IRedirectionRule, provides=IFormContextPermissionChecker)
+class RedirectionRulePermissionChecker(ContextAdapter):
+    """Redirection rule permission checker"""
+
+    edit_permission = MANAGE_SITE_ROOT_PERMISSION
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/features/redirect/container.py	Thu Sep 06 11:27:55 2018 +0200
@@ -0,0 +1,103 @@
+#
+# 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.redirect.interfaces import IRedirectionManager, IRedirectionRule, IRedirectionManagerTarget, \
+    REDIRECT_MANAGER_KEY
+from zope.location.interfaces import ISublocations
+from zope.traversing.interfaces import ITraversable
+
+# import packages
+from pyams_catalog.utils import index_object
+from pyams_utils.adapter import adapter_config, get_annotation_adapter, ContextAdapter
+from pyramid.response import Response
+from zope.container.ordered import OrderedContainer
+from zope.interface import implementer
+from zope.location.location import locate
+
+from pyams_content import _
+
+
+@implementer(IRedirectionManager)
+class RedirectManager(OrderedContainer):
+    """Redirect manager"""
+
+    last_id = 1
+
+    def append(self, value, notify=True):
+        key = str(self.last_id)
+        if not notify:
+            # pre-locate item to avoid multiple notifications
+            locate(value, self, key)
+        self[key] = value
+        self.last_id += 1
+        if not notify:
+            # make sure that item is correctly indexed
+            index_object(value)
+
+    def get_active_items(self):
+        yield from filter(lambda x: IRedirectionRule(x).active, self.values())
+
+    def get_response(self, request):
+        target_url = request.path_qs
+        for rule in self.get_active_items():
+            match = rule.match(target_url)
+            if match:
+                target_url = rule.rewrite(target_url, request)
+                if not rule.chained:
+                    response = Response()
+                    response.status_code = 301 if rule.permanent else 302
+                    response.location = target_url
+                    return response
+
+    def test_rules(self, source_url, request, check_inactive_rules=False):
+        if check_inactive_rules:
+            rules = self.values()
+        else:
+            rules = self.get_active_items()
+        for rule in rules:
+            match = rule.match(source_url)
+            if match:
+                target_url = rule.rewrite(source_url, request)
+                yield rule, source_url, target_url
+                if not rule.chained:
+                    raise StopIteration
+                source_url = target_url
+            else:
+                yield rule, source_url, request.localizer.translate(_("not matching"))
+
+
+@adapter_config(context=IRedirectionManagerTarget, provides=IRedirectionManager)
+def redirection_manager_factory(context):
+    """Redirection manager factory"""
+    return get_annotation_adapter(context, REDIRECT_MANAGER_KEY, RedirectManager, name='++redirect++')
+
+
+@adapter_config(name='redirect', context=IRedirectionManagerTarget, provides=ITraversable)
+class RedirectionManagerNamespace(ContextAdapter):
+    """Redirection manager ++redirect++ namespace"""
+
+    def traverse(self, name, furtherpath=None):
+        return IRedirectionManager(self.context)
+
+
+@adapter_config(name='redirect', context=IRedirectionManagerTarget, provides=ISublocations)
+class RedirectManagerSublocations(ContextAdapter):
+    """redirection manager sub-locations adapter"""
+
+    def sublocations(self):
+        return IRedirectionManager(self.context).values()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/features/redirect/interfaces/__init__.py	Thu Sep 06 11:27:55 2018 +0200
@@ -0,0 +1,102 @@
+#
+# 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 pyams_sequence.interfaces import IInternalReference
+
+# import packages
+from pyams_sequence.schema import InternalReferenceField
+from zope.container.constraints import contains, containers
+from zope.interface import Interface, Attribute, invariant, Invalid
+from zope.schema import Bool, TextLine, Choice
+
+from pyams_content import _
+
+
+REDIRECT_MANAGER_KEY = 'pyams_content.redirect'
+
+
+class IRedirectionRule(IInternalReference):
+    """Redirection rule interface"""
+
+    containers('.IRedirectManager')
+
+    active = Bool(title=_("Active rule?"),
+                  description=_("If 'no', selected rule is inactive"),
+                  required=True,
+                  default=False)
+
+    chained = Bool(title=_("Chained rule?"),
+                   description=_("If 'no', and if this rule is matching received request URL, the rule "
+                                 "returns a redirection response; otherwise, the rule just rewrites the "
+                                 "input URL which is forwarded to the next rule"),
+                   required=True,
+                   default=False)
+
+    permanent = Bool(title=_("Permanent redirect?"),
+                     description=_("Define if this redirection should be permanent or temporary"),
+                     required=True,
+                     default=True)
+
+    url_pattern = TextLine(title=_("URL pattern"),
+                           description=_("Regexp pattern of matching URLs for this redirection rule"),
+                           required=True)
+
+    pattern = Attribute("Compiled URL pattern")
+
+    reference = InternalReferenceField(title=_("Internal redirection target"),
+                                       description=_("Internal redirection reference. You can search a reference using "
+                                                     "'+' followed by internal number, of by entering text matching "
+                                                     "content title."),
+                                       required=False)
+
+    target_url = TextLine(title=_("Target URL"),
+                          description=_("URL to which source URL should be redirected"),
+                          required=False)
+
+    @invariant
+    def check_reference_and_target(self):
+        if self.reference and self.target_url:
+            raise Invalid(_("You can only provide an internal reference OR a target URL"))
+        elif not (self.reference or self.target_url):
+            raise Invalid(_("You must provide an internal reference OR a target URL"))
+
+    def match(self, source_url):
+        """Return regexp URL match on given URL"""
+
+    def rewrite(self, source_url, request):
+        """Rewrite given source URL"""
+
+
+class IRedirectionManager(IOrderedContainer):
+    """Redirection manager"""
+
+    contains(IRedirectionRule)
+
+    def get_active_items(self):
+        """Get iterator over active items"""
+
+    def get_response(self, request):
+        """Get new response for given request"""
+
+    def test_rules(self, source_url, request, check_inactive_rules=False):
+        """Test rules against given URL"""
+
+
+class IRedirectionManagerTarget(Interface):
+    """Redirection manager target marker interface"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/features/redirect/tween.py	Thu Sep 06 11:27:55 2018 +0200
@@ -0,0 +1,49 @@
+#
+# 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.redirect.interfaces import IRedirectionManager
+
+# import packages
+from pyramid.exceptions import NotFound
+from pyramid.httpexceptions import HTTPNotFound
+
+
+def redirect_tween_factory(handler, registry):
+    """Redirect tween factory
+
+    This tween is used to handle NotFound errors: when a request which raises
+    a NotFound error is served, we look info redirects configuration to check if
+    given URL is matching any defined redirection, in which case another HTTPRedirect
+    response is returned with a new location; another content using a proxy request
+    can also be returned.
+    """
+
+    def redirect_tween(request):
+        try:
+            response = handler(request)
+        except (NotFound, HTTPNotFound):
+            manager = IRedirectionManager(request.root, None)
+            if manager is not None:
+                response = manager.get_response(request)
+                if response is not None:
+                    return response
+            raise
+        else:
+            return response
+
+    return redirect_tween
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/features/redirect/zmi/__init__.py	Thu Sep 06 11:27:55 2018 +0200
@@ -0,0 +1,126 @@
+#
+# 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.
+#
+from pyams_form.help import FormHelp
+from pyams_form.interfaces.form import IFormHelp
+from pyams_utils.adapter import adapter_config
+from pyams_zmi.layer import IAdminLayer
+
+__docformat__ = 'restructuredtext'
+
+
+# import standard library
+
+# import interfaces
+from pyams_content.features.redirect.interfaces import IRedirectionRule, IRedirectionManagerTarget, IRedirectionManager
+from pyams_content.interfaces import MANAGE_SITE_ROOT_PERMISSION
+from pyams_skin.interfaces.viewlet import IWidgetTitleViewletManager
+from pyams_skin.layer import IPyAMSLayer
+
+# import packages
+from pyams_content.features.redirect import RedirectionRule
+from pyams_content.features.redirect.zmi.container import RedirectionsContainerView, RedirectionsContainerTable
+from pyams_form.form import ajax_config, AJAXAddForm
+from pyams_pagelet.pagelet import pagelet_config
+from pyams_skin.event import get_json_table_row_refresh_event
+from pyams_skin.viewlet.toolbar import ToolbarAction
+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 z3c.form import field
+
+from pyams_content import _
+
+
+@viewlet_config(name='add-rule.action', context=IRedirectionManagerTarget, layer=IPyAMSLayer,
+                view=RedirectionsContainerView, manager=IWidgetTitleViewletManager,
+                permission=MANAGE_SITE_ROOT_PERMISSION, weight=1)
+class RedirectionRuleAddAction(ToolbarAction):
+    """Redirection rule add action"""
+
+    label = _("Add rule")
+    label_css_class = 'fa fa-fw fa-plus'
+    url = 'add-rule.html'
+    modal_target = True
+
+
+@pagelet_config(name='add-rule.html', context=IRedirectionManagerTarget, layer=IPyAMSLayer,
+                permission=MANAGE_SITE_ROOT_PERMISSION)
+@ajax_config(name='add-rule.json', context=IRedirectionManagerTarget, layer=IPyAMSLayer, base=AJAXAddForm)
+class RedirectionRuleAddForm(AdminDialogAddForm):
+    """Redirection rule add form"""
+
+    dialog_class = 'modal-large'
+    legend = _("Add new redirection rule")
+    icon_css_class = 'fa fa-fw fa-map-signs'
+
+    fields = field.Fields(IRedirectionRule).omit('__parent__', '__name__', 'active', 'chained')
+    edit_permission = MANAGE_SITE_ROOT_PERMISSION
+
+    def create(self, data):
+        return RedirectionRule()
+
+    def add(self, object):
+        IRedirectionManager(self.context).append(object)
+
+    def nextURL(self):
+        return absolute_url(self.context, self.request, 'redirections.html')
+
+
+@pagelet_config(name='properties.html', context=IRedirectionRule, layer=IPyAMSLayer,
+                permission=MANAGE_SITE_ROOT_PERMISSION)
+@ajax_config(name='properties.json', context=IRedirectionRule, layer=IPyAMSLayer)
+class RedirectionRulePropertiesEditForm(AdminDialogEditForm):
+    """Redirection rule properties edit form"""
+
+    dialog_class = 'modal-large'
+    prefix = 'rule_properties.'
+
+    legend = _("Edit redirection rule properties")
+    icon_css_class = 'fa fa-fw fa-map-signs'
+
+    fields = field.Fields(IRedirectionRule).omit('__parent__', '__name__', 'active', 'chained')
+    edit_permission = MANAGE_SITE_ROOT_PERMISSION
+
+    def get_ajax_output(self, changes):
+        output = super(self.__class__, self).get_ajax_output(changes)
+        updated = changes.get(IRedirectionRule, ())
+        if updated:
+            target = get_parent(self.context, IRedirectionManagerTarget)
+            output.setdefault('events', []).append(
+                get_json_table_row_refresh_event(target, self.request, RedirectionsContainerTable, self.context))
+        return output
+
+
+@adapter_config(context=(IRedirectionManagerTarget, IAdminLayer, RedirectionRuleAddForm), provides=IFormHelp)
+@adapter_config(context=(IRedirectionRule, IAdminLayer, RedirectionRulePropertiesEditForm), provides=IFormHelp)
+class RedirectionRuleFormHelp(FormHelp):
+    """Redirection rule form help"""
+
+    message = _("""URL pattern and target URL are defined by *regular expressions* (see |regexp|).
+    
+In URL pattern, you can use any valid regular expression element, notably:
+
+- « .* » to match any list of characters 
+
+- « ( ) » to "memorize" parts of the URL which can be replaced into target URL
+
+- special characters (like "+") must be escaped with an « \\\\ ».
+
+In target URL, memorized parts can be reused using « \\\\1 », « \\\\2 » and so on, where given number is
+the order of the matching pattern element.
+
+.. |regexp| raw:: html
+
+    <a href="https://docs.python.org/3/library/re.html" target="_blank">Python Regular Expressions</a>
+""")
+    message_format = 'rest'
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/features/redirect/zmi/container.py	Thu Sep 06 11:27:55 2018 +0200
@@ -0,0 +1,376 @@
+#
+# 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.features.redirect.interfaces import IRedirectionManagerTarget, IRedirectionManager
+from pyams_content.interfaces import MANAGE_SITE_ROOT_PERMISSION
+from pyams_form.interfaces.form import IWidgetsSuffixViewletsManager
+from pyams_i18n.interfaces import II18n
+from pyams_sequence.interfaces import ISequentialIdInfo
+from pyams_skin.interfaces import IPageHeader, IUserSkinnable, IContentHelp
+from pyams_skin.interfaces.viewlet import IToolbarViewletManager
+from pyams_skin.layer import IPyAMSLayer
+from pyams_zmi.interfaces.menu import ISiteManagementMenu
+from pyams_zmi.layer import IAdminLayer
+from z3c.table.interfaces import IValues, IColumn
+
+# import packages
+from pyams_content.skin import pyams_content
+from pyams_form.form import AJAXAddForm
+from pyams_form.schema import CloseButton
+from pyams_pagelet.pagelet import pagelet_config
+from pyams_skin.help import ContentHelp
+from pyams_skin.page import DefaultPageHeaderAdapter
+from pyams_skin.skin import apply_skin
+from pyams_skin.table import BaseTable, SorterColumn, TrashColumn, I18nColumn, AttributeSwitcherColumn
+from pyams_skin.viewlet.menu import MenuItem
+from pyams_skin.viewlet.toolbar import ToolbarAction
+from pyams_template.template import template_config
+from pyams_utils.adapter import adapter_config, ContextRequestViewAdapter
+from pyams_utils.fanstatic import get_resource_path
+from pyams_utils.request import copy_request
+from pyams_utils.url import absolute_url
+from pyams_viewlet.viewlet import viewlet_config, Viewlet
+from pyams_zmi.form import AdminDialogAddForm
+from pyams_zmi.view import ContainerAdminView
+from pyramid.decorator import reify
+from pyramid.exceptions import NotFound
+from pyramid.view import view_config
+from z3c.form import field, button
+from z3c.table.column import GetAttrColumn
+from zope.interface import Interface
+from zope.schema import TextLine, Bool
+
+from pyams_content import _
+
+
+@viewlet_config(name='redirections.menu', context=IRedirectionManagerTarget, layer=IPyAMSLayer,
+                manager=ISiteManagementMenu, permission=MANAGE_SITE_ROOT_PERMISSION, weight=35)
+class RedirectionMenu(MenuItem):
+    """Redirection manager menu"""
+
+    label = _("Redirections")
+    icon_class = 'fa-map-signs'
+    url = '#redirections.html'
+
+
+class RedirectionsContainerTable(BaseTable):
+    """Redirections container table"""
+
+    prefix = 'redirections'
+
+    hide_header = True
+    sortOn = None
+
+    cssClasses = {'table': 'table table-bordered table-striped table-hover table-tight table-dnd'}
+
+    @property
+    def data_attributes(self):
+        attributes = super(RedirectionsContainerTable, self).data_attributes
+        attributes.setdefault('table', {}).update({
+            'data-ams-plugins': 'pyams_content',
+            'data-ams-plugin-pyams_content-src': get_resource_path(pyams_content),
+            'data-ams-location': absolute_url(IRedirectionManager(self.context), self.request),
+            'data-ams-tablednd-drag-handle': 'td.sorter',
+            'data-ams-tablednd-drop-target': 'set-rules-order.json',
+            'data-ams-active-icon-on': 'fa fa-fw fa-check-square-o',
+            'data-ams-active-icon-off': 'fa fa-fw fa-square-o txt-color-silver opacity-75',
+            'data-ams-chained-icon-on': 'fa fa-fw fa-chain',
+            'data-ams-chained-icon-off': 'fa fa-fw fa-chain txt-color-silver opacity-50'
+        })
+        attributes.setdefault('td', {}).update({
+            'data-ams-attribute-switcher': self.get_switcher_target,
+            'data-ams-switcher-attribute-name': self.get_switcher_attribute
+        })
+        return attributes
+
+    @staticmethod
+    def get_switcher_target(element, column):
+        if column.__name__ == 'enable-disable':
+            return 'switch-rule-activity.json'
+        elif column.__name__ == 'chain-unchain':
+            return 'switch-rule-chain.json'
+
+    @staticmethod
+    def get_switcher_attribute(element, column):
+        if column.__name__ == 'enable-disable':
+            return 'active'
+        elif column.__name__ == 'chain-unchain':
+            return 'chained'
+
+    @reify
+    def values(self):
+        return list(super(RedirectionsContainerTable, self).values)
+
+    def render(self):
+        if not self.values:
+            translate = self.request.localizer.translate
+            return translate(_("No currently defined redirection rule."))
+        return super(RedirectionsContainerTable, self).render()
+
+
+@adapter_config(context=(IRedirectionManagerTarget, IPyAMSLayer, RedirectionsContainerTable), provides=IValues)
+class RedirectionsContainerValues(ContextRequestViewAdapter):
+    """Redirections container values"""
+
+    @property
+    def values(self):
+        return IRedirectionManager(self.context).values()
+
+
+@adapter_config(name='sorter', context=(IRedirectionManagerTarget, IPyAMSLayer, RedirectionsContainerTable),
+                provides=IColumn)
+class RedirectionsContainerSorterColumn(SorterColumn):
+    """Redirections container sorter column"""
+
+
+@view_config(name='set-rules-order.json', context=IRedirectionManager, request_type=IPyAMSLayer,
+             permission=MANAGE_SITE_ROOT_PERMISSION, renderer='json', xhr=True)
+def set_rules_order(request):
+    """Update redirection rules order"""
+    order = list(map(str, json.loads(request.params.get('names'))))
+    request.context.updateOrder(order)
+    return {'status': 'success'}
+
+
+@adapter_config(name='enable-disable', context=(IRedirectionManagerTarget, IPyAMSLayer, RedirectionsContainerTable),
+                provides=IColumn)
+class RedirectionsContainerEnablerColumn(AttributeSwitcherColumn):
+    """Redirections container enabler switcher column"""
+
+    switch_attribute = 'active'
+
+    on_icon_class = 'fa fa-fw fa-check-square-o'
+    off_icon_class = 'fa fa-fw fa-square-o txt-color-silver opacity-75'
+
+    icon_hint = _("Enable/disable rule")
+
+    weight = 6
+
+
+@view_config(name='switch-rule-activity.json', context=IRedirectionManager, request_type=IPyAMSLayer,
+             permission=MANAGE_SITE_ROOT_PERMISSION, renderer='json', xhr=True)
+def switch_rule_activity(request):
+    """Switch rule activity"""
+    container = IRedirectionManager(request.context)
+    rule = container.get(str(request.params.get('object_name')))
+    if rule is None:
+        raise NotFound()
+    rule.active = not rule.active
+    return {'on': rule.active}
+
+
+@adapter_config(name='chain-unchain', context=(IRedirectionManagerTarget, IPyAMSLayer, RedirectionsContainerTable),
+                provides=IColumn)
+class RedirectionsContainerChainedColumn(AttributeSwitcherColumn):
+    """Redirections container chained switcher column"""
+
+    switch_attribute = 'chained'
+
+    on_icon_class = 'fa fa-fw fa-chain'
+    off_icon_class = 'fa fa-fw fa-chain txt-color-silver opacity-50'
+
+    icon_hint = _("Chain/unchain rule")
+
+    weight = 7
+
+
+@view_config(name='switch-rule-chain.json', context=IRedirectionManager, request_type=IPyAMSLayer,
+             permission=MANAGE_SITE_ROOT_PERMISSION, renderer='json', xhr=True)
+def switch_rule_chain(request):
+    """Switch rule chain"""
+    container = IRedirectionManager(request.context)
+    rule = container.get(str(request.params.get('object_name')))
+    if rule is None:
+        raise NotFound()
+    rule.chained = not rule.chained
+    return {'chained': rule.chained}
+
+
+@adapter_config(name='name', context=(IRedirectionManagerTarget, IPyAMSLayer, RedirectionsContainerTable),
+                provides=IColumn)
+class RedirectionsContainerNameColumn(I18nColumn, GetAttrColumn):
+    """Redirections container name column"""
+
+    _header = _("URL pattern")
+    attrName = 'url_pattern'
+    weight = 10
+
+
+@adapter_config(name='target', context=(IRedirectionManagerTarget, IPyAMSLayer, RedirectionsContainerTable),
+                provides=IColumn)
+class RedirectionsContainerTargetColumn(I18nColumn, GetAttrColumn):
+    """Redirections container target column"""
+
+    _header = _("Target")
+    attrName = 'target_url'
+    weight = 20
+
+    def getValue(self, obj):
+        if obj.reference:
+            target = obj.target
+            return '{0} ({1})'.format(II18n(target).query_attribute('title', request=self.request),
+                                      ISequentialIdInfo(target).get_short_oid())
+        else:
+            return super(RedirectionsContainerTargetColumn, self).getValue(obj)
+
+
+@adapter_config(name='trash', context=(IRedirectionManagerTarget, IPyAMSLayer, RedirectionsContainerTable),
+                provides=IColumn)
+class RedirectionsContainerTrashColumn(TrashColumn):
+    """Redirections container trash column"""
+
+    permission = MANAGE_SITE_ROOT_PERMISSION
+
+
+@pagelet_config(name='redirections.html', context=IRedirectionManagerTarget, layer=IPyAMSLayer,
+                permission=MANAGE_SITE_ROOT_PERMISSION)
+class RedirectionsContainerView(ContainerAdminView):
+    """Redirections container view"""
+
+    title = _("Redirections list")
+    table_class = RedirectionsContainerTable
+
+
+@adapter_config(context=(IRedirectionManagerTarget, IAdminLayer, RedirectionsContainerView), provides=IPageHeader)
+class RedirectionsContainerViewHeaderAdapter(DefaultPageHeaderAdapter):
+    """Redirections container view header adapter"""
+
+    icon_class = 'fa fa-fw fa-map-signs'
+
+
+@adapter_config(context=(IRedirectionManagerTarget, IAdminLayer, RedirectionsContainerView), provides=IContentHelp)
+class RedirectionsContainerHelpAdapter(ContentHelp):
+    """Redirections container help adapter"""
+
+    header = _("Redirection rules")
+    message = _("""Redirection rules are use to handle redirections responses when a request generates 
+a famous « 404 NotFound » error.
+
+Redirections are particularly useful when you are migrating from a previous site and don't want to lose 
+your SEO.
+
+You can define a set of rules which will be applied to every \"NotFound\" request; rules are based on 
+regular expressions which are applied to input URL: if the rule is \"matching\", the target URL is rewritten
+and a \"Redirect\" response is send.
+
+You can chain rules together: when a rule is chained, it's rewritten URL is passed as input URL to the 
+next rule, until a matching rule is found.
+""")
+    message_format = 'rest'
+
+
+#
+# Redirections container test form
+#
+
+@viewlet_config(name='test.action', context=IRedirectionManagerTarget, layer=IAdminLayer,
+                view=RedirectionsContainerView, manager=IToolbarViewletManager,
+                permission=MANAGE_SITE_ROOT_PERMISSION, weight=75)
+class RedirectionsContainerTestAction(ToolbarAction):
+    """redirections container test action"""
+
+    label = _("Test")
+
+    group_css_class = 'btn-group margin-left-5'
+    label_css_class = 'fa fa-fw fa-magic'
+    css_class = 'btn btn-xs btn-default'
+
+    url = 'test-redirection-rules.html'
+    modal_target = True
+
+
+class IRedirectionsContainerTestFields(Interface):
+    """Redirections container test fields"""
+
+    source_url = TextLine(title=_("Test URL"),
+                          required=True)
+
+    check_inactive_rules = Bool(title=_("Check inactive rules?"),
+                                description=_("If 'yes', inactive rules will also be tested"),
+                                required=True,
+                                default=False)
+
+
+class IRedirectionsContainerTestButtons(Interface):
+    """Redirections container test form buttons"""
+
+    close = CloseButton(name='close', title=_("Close"))
+    test = button.Button(name='test', title=_("Test rules"))
+
+
+@pagelet_config(name='test-redirection-rules.html', context=IRedirectionManagerTarget, layer=IPyAMSLayer,
+                permission=MANAGE_SITE_ROOT_PERMISSION)
+class RedirectionsContainerTestForm(AdminDialogAddForm):
+    """Redirections container test form"""
+
+    dialog_class = 'modal-max'
+    legend = _("Test redirection rules")
+    icon_css_class = 'fa fa-fw fa-magic'
+
+    prefix = 'rules_test_form.'
+    fields = field.Fields(IRedirectionsContainerTestFields)
+    buttons = button.Buttons(IRedirectionsContainerTestButtons)
+    ajax_handler = 'test-redirection-rules.json'
+    edit_permission = MANAGE_SITE_ROOT_PERMISSION
+
+    @property
+    def form_target(self):
+        return '#{0}_test_result'.format(self.id)
+
+    def updateActions(self):
+        super(RedirectionsContainerTestForm, self).updateActions()
+        if 'test' in self.actions:
+            self.actions['test'].addClass('btn-primary')
+
+    def createAndAdd(self, data):
+        data = data.get(self, data)
+        request = copy_request(self.request)
+        apply_skin(request, IUserSkinnable(self.context).get_skin())
+        return IRedirectionManager(self.context).test_rules(data['source_url'], request, data['check_inactive_rules'])
+
+
+@viewlet_config(name='test-indexer-process.suffix', layer=IAdminLayer, manager=IWidgetsSuffixViewletsManager,
+                view=RedirectionsContainerTestForm, weight=50)
+@template_config(template='templates/manager-test.pt')
+class RedirectionsContainerTestSuffix(Viewlet):
+    """Redirections container test form suffix"""
+
+
+@view_config(name='test-redirection-rules.json', context=IRedirectionManagerTarget, request_type=IPyAMSLayer,
+             permission=MANAGE_SITE_ROOT_PERMISSION, renderer='json', xhr=True)
+class RedirectionsContainerAJAXTestForm(AJAXAddForm, RedirectionsContainerTestForm):
+    """Redirections container test form, JSON renderer"""
+
+    def get_ajax_output(self, changes):
+        message = []
+        translate = self.request.localizer.translate
+        for rule, source_url, target_url in changes:
+            if not message:
+                message.append('{:<40} | {:<40} | {:<40}'.format(translate(_("Input URL")),
+                                                                 translate(_("URL pattern")),
+                                                                 translate(_("Output URL"))))
+                message.append('{:<40}-|-{:<40}-|-{:<40}'.format('-' * 40, '-' * 40, '-' * 40))
+            message.append('{:<40} | {:<40} | {:<40}'.format(source_url, rule.url_pattern, target_url))
+        if not message:
+            message.append(translate(_("No matching rule!")))
+        return {
+            'status': 'success',
+            'content': {'html': '\n'.join(message)},
+            'close_form': False
+        }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/features/redirect/zmi/templates/manager-test.pt	Thu Sep 06 11:27:55 2018 +0200
@@ -0,0 +1,4 @@
+<div class="no-widget-toolbar">
+	<pre class="height-min-200"
+		 tal:attributes="id string:${view.__parent__.id}_test_result"></pre>
+</div>
--- a/src/pyams_content/features/renderer/__init__.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/features/renderer/__init__.py	Thu Sep 06 11:27:55 2018 +0200
@@ -45,7 +45,7 @@
             request = check_request()
         renderer = request.registry.queryMultiAdapter((self, request), self.renderer_interface,
                                                       name=self.renderer or '')
-        if 'lang' in request.params:
+        if (renderer is not None) and ('lang' in request.params):
             renderer.language = request.params['lang']
         return renderer
 
--- a/src/pyams_content/features/renderer/interfaces/__init__.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/features/renderer/interfaces/__init__.py	Thu Sep 06 11:27:55 2018 +0200
@@ -39,9 +39,14 @@
     """Content renderer interface"""
 
     label = Attribute("Renderer label")
-    weight = Attribute("Renderer weight")
-    settings_interface = Attribute("Renderer target interface")
+    weight = Attribute("Renderer weight, used for ordering")
+
+    settings_interface = Attribute("Renderer settings interface")
+    resources = Attribute("Iterable of needed Fanstatic resources")
+
     language = Attribute("Renderer language (if forced)")
+    context_attrs = Attribute("Context attributes defined into renderer")
+    i18n_context_attrs = Attribute("I18n context attributes defined into renderer")
 
 
 class ISharedContentRenderer(IContentRenderer):
--- a/src/pyams_content/features/renderer/skin/__init__.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/features/renderer/skin/__init__.py	Thu Sep 06 11:27:55 2018 +0200
@@ -36,7 +36,9 @@
 
     label = None
     weight = 0
+
     settings_interface = None
+    resources = ()
 
     language = None
     context_attrs = ()
@@ -49,6 +51,8 @@
         return IRendererSettings(self.context)
 
     def update(self):
+        for resource in self.resources:
+            resource.need()
         for attr in self.context_attrs:
             setattr(self, attr, getattr(self.context, attr, None))
         if self.i18n_context_attrs:
--- a/src/pyams_content/features/renderer/zmi/__init__.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/features/renderer/zmi/__init__.py	Thu Sep 06 11:27:55 2018 +0200
@@ -18,6 +18,7 @@
 # import interfaces
 from pyams_content.features.renderer.interfaces import IRenderedContent, IContentRenderer, IRendererSettings
 from pyams_content.interfaces import MANAGE_CONTENT_PERMISSION
+from pyams_form.interfaces.form import IFormManager
 from pyams_skin.layer import IPyAMSLayer
 
 # import packages
@@ -25,6 +26,7 @@
 from pyams_pagelet.pagelet import pagelet_config
 from pyams_viewlet.viewlet import BaseContentProvider
 from pyams_zmi.form import AdminDialogEditForm
+from pyramid.decorator import reify
 from z3c.form import field
 from zope.interface import Interface
 
@@ -70,12 +72,45 @@
     legend = _("Edit renderer properties")
     icon_css_class = 'fa fa-fw fa-pencil-square-o'
 
-    @property
+    @reify
+    def manager(self):
+        content = self.getContent()
+        return self.request.registry.queryMultiAdapter((content, self.request, self), IFormManager)
+
+    @reify
     def fields(self):
-        renderer = IContentRenderer(self.context)
-        return field.Fields(renderer.settings_interface or Interface)
+        if self.manager is not None:
+            return self.manager.getFields()
+        else:
+            renderer = IContentRenderer(self.context)
+            return field.Fields(renderer.settings_interface or Interface)
 
     edit_permission = MANAGE_CONTENT_PERMISSION
 
     def getContent(self):
         return IRendererSettings(self.context)
+
+    def update(self):
+        if self.manager is not None:
+            self.manager.update()
+        else:
+            super(RendererPropertiesEditForm, self).update()
+
+    def updateWidgets(self, prefix=None):
+        if self.manager is not None:
+            self.manager.updateWidgets(prefix)
+        else:
+            super(RendererPropertiesEditForm, self).updateWidgets(prefix)
+
+    def updateActions(self):
+        if self.manager is not None:
+            self.manager.updateActions()
+        else:
+            super(RendererPropertiesEditForm, self).updateActions()
+
+    def updateGroups(self):
+        if self.manager is not None:
+            self.manager.updateGroups()
+        else:
+            super(RendererPropertiesEditForm, self).updateGroups()
+
--- a/src/pyams_content/features/renderer/zmi/templates/renderer-input.pt	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/features/renderer/zmi/templates/renderer-input.pt	Thu Sep 06 11:27:55 2018 +0200
@@ -1,6 +1,6 @@
 <label class="input bordered with-icon" i18n:domain="pyams_content">
 	<tal:var define="render view/show_renderer_properties">
-		<i class="icon-append fa fa-fw fa-pencil-square-o text-primary hint align-base opaque"
+		<i class="icon-append fa fa-fw fa-pencil-square-o text-primary inverted hint align-base opaque"
 			title="Edit renderer properties" i18n:attributes="title"
 			data-ams-hint-gravity="se" data-toggle="modal"
 			tal:condition="render"
--- a/src/pyams_content/features/renderer/zmi/widget.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/features/renderer/zmi/widget.py	Thu Sep 06 11:27:55 2018 +0200
@@ -31,7 +31,7 @@
 
     @property
     def show_renderer_properties(self):
-        renderer = IContentRenderer(self.context)
+        renderer = IContentRenderer(self.context, None)
         return (renderer is not None) and (renderer.settings_interface is not None)
 
 
--- a/src/pyams_content/generations/__init__.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/generations/__init__.py	Thu Sep 06 11:27:55 2018 +0200
@@ -71,6 +71,8 @@
         'pyams_content.component.keynumber KeyNumber',
     'pyams_content.component.paragraph.keynumber KeyNumberContainer':
         'pyams_content.component.keynumber KeyNumberContainer',
+    'pyams_content.component.paragraph.keynumber KeyNumberParagraph':
+        'pyams_content.component.keynumber.paragraph KeyNumberParagraph',
     'pyams_content.portlet.content SharedContentPortletSettings':
         'pyams_content.shared.common.portlet.content SharedContentPortletSettings',
     'pyams_content.portlet.navigation SimpleNavigationPortletSettings':
--- a/src/pyams_content/include.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/include.py	Thu Sep 06 11:27:55 2018 +0200
@@ -18,6 +18,7 @@
 # import interfaces
 
 # import packages
+from pyramid.tweens import MAIN
 
 
 def include_package(config):
@@ -26,6 +27,9 @@
     # add translations
     config.add_translation_dirs('pyams_content:locales')
 
+    # add custom twwen
+    config.add_tween('pyams_content.features.redirect.tween.redirect_tween_factory', over=MAIN)
+
     # add custom routes
     config.add_route('oid_access', '/+/{oid}*view')
 
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	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/locales/fr/LC_MESSAGES/pyams_content.po	Thu Sep 06 11:27:55 2018 +0200
@@ -5,7 +5,7 @@
 msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE 1.0\n"
-"POT-Creation-Date: 2018-07-17 14:54+0200\n"
+"POT-Creation-Date: 2018-09-06 08:55+0200\n"
 "PO-Revision-Date: 2015-09-10 10:42+0200\n"
 "Last-Translator: Thierry Florac <tflorac@ulthar.net>\n"
 "Language-Team: French\n"
@@ -167,15 +167,15 @@
 "Commentaires (non publiés mais à conserver) relatifs à l'auteur et à la "
 "gestion de ses droits"
 
-#: src/pyams_content/component/gallery/zmi/templates/gallery-medias.pt:11
+#: src/pyams_content/component/gallery/zmi/templates/gallery-medias.pt:12
 msgid "Gallery medias"
 msgstr "Contenu de la galerie"
 
-#: src/pyams_content/component/gallery/zmi/templates/gallery-medias.pt:17
+#: src/pyams_content/component/gallery/zmi/templates/gallery-medias.pt:18
 msgid "Download medias"
 msgstr "Télécharger tous les médias"
 
-#: src/pyams_content/component/gallery/zmi/templates/gallery-medias.pt:41
+#: src/pyams_content/component/gallery/zmi/templates/gallery-medias.pt:42
 msgid "Zoom image"
 msgstr "Agrandir l'image"
 
@@ -221,13 +221,9 @@
 #: src/pyams_content/component/gallery/interfaces/__init__.py:58
 #: src/pyams_content/component/gallery/interfaces/__init__.py:101
 #: src/pyams_content/component/extfile/interfaces/__init__.py:40
-#: 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:48
 #: 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:154
-#: src/pyams_content/shared/form/interfaces/__init__.py:66
+#: src/pyams_content/shared/form/interfaces/__init__.py:64
 msgid "Description"
 msgstr "Description"
 
@@ -281,16 +277,16 @@
 
 #: src/pyams_content/component/gallery/interfaces/__init__.py:97
 #: src/pyams_content/component/keynumber/portlet/interfaces/__init__.py:31
-#: src/pyams_content/component/paragraph/zmi/milestone.py:232
-#: src/pyams_content/component/paragraph/zmi/container.py:252
+#: src/pyams_content/component/paragraph/zmi/milestone.py:239
+#: src/pyams_content/component/paragraph/zmi/container.py:270
 #: src/pyams_content/component/paragraph/interfaces/milestone.py:45
-#: src/pyams_content/component/links/zmi/reverse.py:73
+#: src/pyams_content/shared/common/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/common/zmi/templates/advanced-search.pt:200
 #: src/pyams_content/shared/view/portlet/interfaces.py:56
 #: src/pyams_content/shared/imagemap/zmi/container.py:123
-#: src/pyams_content/shared/site/zmi/folder.py:70
-#: src/pyams_content/root/zmi/templates/advanced-search.pt:188
+#: src/pyams_content/shared/site/zmi/folder.py:71
+#: src/pyams_content/root/zmi/templates/advanced-search.pt:197
 #: 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
@@ -411,7 +407,6 @@
 #: src/pyams_content/component/extfile/interfaces/__init__.py:36
 #: src/pyams_content/component/links/interfaces/__init__.py:35
 #: src/pyams_content/shared/imagemap/interfaces/__init__.py:55
-#: src/pyams_content/shared/site/interfaces/__init__.py:114
 msgid "Alternate title"
 msgstr "Titre de substitution"
 
@@ -420,9 +415,7 @@
 msgstr "Titre présenté aux internautes"
 
 #: src/pyams_content/component/extfile/interfaces/__init__.py:41
-#: src/pyams_content/component/paragraph/interfaces/video.py:49
 #: src/pyams_content/component/paragraph/interfaces/audio.py:49
-#: 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"
 
@@ -482,49 +475,64 @@
 "Cliquez sur le bouton 'Parcourir...' pour sélectionner un nouveau contenu"
 
 #: src/pyams_content/component/keynumber/__init__.py:189
-#: src/pyams_content/component/keynumber/zmi/__init__.py:199
+#: src/pyams_content/component/keynumber/zmi/__init__.py:212
 #: src/pyams_content/component/keynumber/portlet/zmi/__init__.py:74
-#: src/pyams_content/component/paragraph/interfaces/keynumber.py:29
+#: src/pyams_content/component/keynumber/interfaces/__init__.py:83
 msgid "Key numbers"
 msgstr "Chiffres-clés"
 
+#: src/pyams_content/component/keynumber/zmi/paragraph.py:53
+msgid "Key numbers..."
+msgstr "Chiffres-clés"
+
+#: src/pyams_content/component/keynumber/zmi/paragraph.py:66
+msgid "Add new key number paragraph"
+msgstr "Ajout de chiffres-clés"
+
+#: src/pyams_content/component/keynumber/zmi/paragraph.py:94
+msgid "Edit key number paragraph properties"
+msgstr "Propriétés des chiffres-clés"
+
 #. Default: Header
-#: src/pyams_content/component/keynumber/zmi/__init__.py:147
-#: src/pyams_content/component/keynumber/interfaces/__init__.py:44
+#: src/pyams_content/component/keynumber/zmi/__init__.py:160
+#: src/pyams_content/component/keynumber/interfaces/__init__.py:45
 msgid "key-number-label"
 msgstr "En-tête"
 
-#: src/pyams_content/component/keynumber/zmi/__init__.py:159
-#: src/pyams_content/component/keynumber/interfaces/__init__.py:49
+#: src/pyams_content/component/keynumber/zmi/__init__.py:172
+#: src/pyams_content/component/keynumber/interfaces/__init__.py:50
 msgid "Number"
 msgstr "Chiffre"
 
 #. Default: Unit
-#: src/pyams_content/component/keynumber/zmi/__init__.py:168
-#: src/pyams_content/component/keynumber/interfaces/__init__.py:53
+#: src/pyams_content/component/keynumber/zmi/__init__.py:181
+#: src/pyams_content/component/keynumber/interfaces/__init__.py:54
 msgid "key-number-unit"
 msgstr "Unité"
 
-#: src/pyams_content/component/keynumber/zmi/__init__.py:180
-#: src/pyams_content/component/keynumber/interfaces/__init__.py:57
-#: src/pyams_content/component/paragraph/zmi/pictogram.py:268
+#: src/pyams_content/component/keynumber/zmi/__init__.py:193
+#: src/pyams_content/component/keynumber/interfaces/__init__.py:58
+#: src/pyams_content/component/illustration/interfaces/__init__.py:64
+#: src/pyams_content/component/paragraph/zmi/pictogram.py:277
+#: src/pyams_content/component/paragraph/interfaces/video.py:48
 #: src/pyams_content/component/paragraph/interfaces/pictogram.py:58
+#: src/pyams_content/component/video/interfaces/__init__.py:48
 msgid "Associated text"
 msgstr "Texte associé"
 
-#: src/pyams_content/component/keynumber/zmi/__init__.py:218
+#: src/pyams_content/component/keynumber/zmi/__init__.py:231
 msgid "Add keynumber"
 msgstr "Ajouter un chiffre-clé"
 
-#: src/pyams_content/component/keynumber/zmi/__init__.py:230
+#: src/pyams_content/component/keynumber/zmi/__init__.py:243
 msgid "Add new keynumber"
 msgstr "Ajout d'un chiffre-clé"
 
-#: src/pyams_content/component/keynumber/zmi/__init__.py:259
+#: src/pyams_content/component/keynumber/zmi/__init__.py:272
 msgid "Edit keynumber properties"
 msgstr "Propriétés du chiffre-clé"
 
-#: src/pyams_content/component/keynumber/zmi/__init__.py:245
+#: src/pyams_content/component/keynumber/zmi/__init__.py:258
 msgid "Key number was correctly added"
 msgstr "Le chiffre-clé a été ajouté."
 
@@ -538,8 +546,8 @@
 msgstr "Liens associés"
 
 #: src/pyams_content/component/keynumber/portlet/zmi/templates/keynumber-preview.pt:31
-#: src/pyams_content/features/menu/portlet/navigation/zmi/templates/double-preview.pt:11
-#: src/pyams_content/features/menu/portlet/navigation/zmi/templates/simple-preview.pt:8
+#: src/pyams_content/features/menu/portlet/navigation/zmi/templates/double-preview.pt:10
+#: src/pyams_content/features/menu/portlet/navigation/zmi/templates/simple-preview.pt:7
 msgid "Link target is not published!"
 msgstr "Le contenu ciblé n'est pas publié"
 
@@ -555,47 +563,55 @@
 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/keynumber/interfaces/__init__.py:40
 #: 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:118
+#: src/pyams_content/shared/form/interfaces/__init__.py:85
+#: src/pyams_content/shared/site/interfaces/__init__.py:144
 #: src/pyams_content/features/alert/interfaces.py:54
 #: src/pyams_content/features/menu/interfaces/__init__.py:59
 msgid "Visible?"
 msgstr "Visible ?"
 
-#: src/pyams_content/component/keynumber/interfaces/__init__.py:40
+#: src/pyams_content/component/keynumber/interfaces/__init__.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/keynumber/interfaces/__init__.py:45
+#: src/pyams_content/component/keynumber/interfaces/__init__.py:46
 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
+#: src/pyams_content/component/keynumber/interfaces/__init__.py:51
 msgid "Key number value"
 msgstr "Chiffre"
 
-#: src/pyams_content/component/keynumber/interfaces/__init__.py:54
+#: src/pyams_content/component/keynumber/interfaces/__init__.py:55
 msgid "Displayed unit"
 msgstr "Unité affichée"
 
-#: src/pyams_content/component/keynumber/interfaces/__init__.py:58
+#: src/pyams_content/component/keynumber/interfaces/__init__.py:59
 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/keynumber/interfaces/__init__.py:94
+msgid "Key numbers template"
+msgstr "Mode de rendu"
+
+#: src/pyams_content/component/keynumber/interfaces/__init__.py:95
+msgid "Presentation template used for key numbers"
+msgstr "Modèle de présentation utilisé par ce bloc de contenu"
+
 #: src/pyams_content/component/illustration/__init__.py:177
 #: src/pyams_content/component/illustration/thesaurus.py:32
 #: src/pyams_content/component/illustration/zmi/paragraph.py:158
 #: src/pyams_content/component/illustration/zmi/__init__.py:56
-#: src/pyams_content/component/illustration/zmi/__init__.py:100
+#: src/pyams_content/component/illustration/zmi/__init__.py:117
 #: src/pyams_content/component/illustration/interfaces/__init__.py:99
 msgid "Illustration"
 msgstr "Illustration"
@@ -613,11 +629,11 @@
 msgid "Edit illustration properties"
 msgstr "Propriétés de l'illustration"
 
-#: src/pyams_content/component/illustration/zmi/__init__.py:150
+#: src/pyams_content/component/illustration/zmi/__init__.py:151
 msgid "Navigation link illustration"
 msgstr "Illustration de navigation"
 
-#: src/pyams_content/component/illustration/zmi/__init__.py:102
+#: src/pyams_content/component/illustration/zmi/__init__.py:119
 msgid "Header illustration"
 msgstr "Illustration d'en-tête"
 
@@ -630,6 +646,12 @@
 "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:65
+msgid "Illustration description displayed in front-office templates"
+msgstr ""
+"Le texte accompagne l'illustration, en complément de la légende. Attention : "
+"sa présence et sa mise en forme dépendent du mode de rendu choisi."
+
 #: src/pyams_content/component/illustration/interfaces/__init__.py:69
 msgid "Name of picture's author"
 msgstr "Sous la forme \"Prénom Nom / Organisme\""
@@ -653,11 +675,11 @@
 msgid "Selected paragraph is not visible"
 msgstr "le bloc sélectionné n'est pas visible"
 
-#: src/pyams_content/component/paragraph/container.py:73
+#: src/pyams_content/component/paragraph/container.py:90
 msgid "Paragraphs"
 msgstr "Blocs de contenu"
 
-#: src/pyams_content/component/paragraph/container.py:95
+#: src/pyams_content/component/paragraph/container.py:112
 msgid "no visible paragraph"
 msgstr "aucun bloc de contenu visible"
 
@@ -665,6 +687,10 @@
 msgid "Selected pictogram is missing"
 msgstr "le pictogramme sélectionné est introuvable"
 
+#: src/pyams_content/component/paragraph/header.py:62
+msgid "This paragraph type is deprecated and should be removed!"
+msgstr "Ce type de paragraphe a été supprimé et ne doit plus être utilisé !"
+
 #: src/pyams_content/component/paragraph/zmi/milestone.py:78
 msgid "Milestones..."
 msgstr "Chronologie"
@@ -677,38 +703,38 @@
 msgid "Edit milestone paragraph properties"
 msgstr "Propriétés de la chronologie"
 
-#: src/pyams_content/component/paragraph/zmi/milestone.py:244
+#: src/pyams_content/component/paragraph/zmi/milestone.py:251
 #: src/pyams_content/component/paragraph/interfaces/milestone.py:49
 msgid "Associated label"
 msgstr "Information associée"
 
-#: src/pyams_content/component/paragraph/zmi/milestone.py:256
+#: src/pyams_content/component/paragraph/zmi/milestone.py:263
 #: src/pyams_content/component/paragraph/interfaces/milestone.py:53
 msgid "Anchor"
 msgstr "Ancre"
 
-#: src/pyams_content/component/paragraph/zmi/milestone.py:285
+#: src/pyams_content/component/paragraph/zmi/milestone.py:292
 #: src/pyams_content/component/paragraph/interfaces/milestone.py:76
 msgid "Milestones"
 msgstr "Chronologie"
 
-#: src/pyams_content/component/paragraph/zmi/milestone.py:300
+#: src/pyams_content/component/paragraph/zmi/milestone.py:307
 msgid "Add milestone"
 msgstr "Ajouter un jalon"
 
-#: src/pyams_content/component/paragraph/zmi/milestone.py:313
+#: src/pyams_content/component/paragraph/zmi/milestone.py:320
 msgid "Add new milestone"
 msgstr "Ajout d'un jalon"
 
-#: src/pyams_content/component/paragraph/zmi/milestone.py:340
+#: src/pyams_content/component/paragraph/zmi/milestone.py:347
 msgid "Edit milestone properties"
 msgstr "Propriétés du jalon"
 
-#: src/pyams_content/component/paragraph/zmi/milestone.py:328
+#: src/pyams_content/component/paragraph/zmi/milestone.py:335
 msgid "Milestone was correctly added"
 msgstr "Le jalon a été ajouté."
 
-#: src/pyams_content/component/paragraph/zmi/milestone.py:271
+#: src/pyams_content/component/paragraph/zmi/milestone.py:278
 msgid "(missing paragraph)"
 msgstr "(paragraphe supprimé)"
 
@@ -784,6 +810,26 @@
 msgid "Paragraph was correctly added."
 msgstr "Le bloc a été ajouté."
 
+#: src/pyams_content/component/paragraph/zmi/__init__.py:254
+msgid ""
+"You changed renderer selection. Don't omit to update new renderer "
+"properties..."
+msgstr ""
+"Vous avez changé de mode de rendu. N'oubliez pas de vérifier les propriétés "
+"du nouveau mode de rendu sélectionné..."
+
+#: src/pyams_content/component/paragraph/zmi/map.py:55
+msgid "Location map..."
+msgstr "Carte de situation"
+
+#: src/pyams_content/component/paragraph/zmi/map.py:68
+msgid "Add new location map"
+msgstr "Ajout d'une carte de situation"
+
+#: src/pyams_content/component/paragraph/zmi/map.py:91
+msgid "Edit location map properties"
+msgstr "Propriétés de la carte"
+
 #: src/pyams_content/component/paragraph/zmi/video.py:54
 msgid "Video paragraph..."
 msgstr "Vidéo"
@@ -799,43 +845,43 @@
 
 #: src/pyams_content/component/paragraph/zmi/container.py:74
 msgid "Contents..."
-msgstr "Contenu"
-
-#: src/pyams_content/component/paragraph/zmi/container.py:196
+msgstr "Blocs de contenu"
+
+#: src/pyams_content/component/paragraph/zmi/container.py:215
 msgid "Set navigation anchor"
 msgstr "Ancre de navigation"
 
-#: src/pyams_content/component/paragraph/zmi/container.py:268
+#: src/pyams_content/component/paragraph/zmi/container.py:286
 msgid "Show/hide all paragraphs"
 msgstr "Afficher/masquer tous les blocs"
 
-#: src/pyams_content/component/paragraph/zmi/container.py:316
-#: src/pyams_content/component/paragraph/zmi/container.py:325
-#: src/pyams_content/component/paragraph/zmi/container.py:338
+#: src/pyams_content/component/paragraph/zmi/container.py:334
+#: src/pyams_content/component/paragraph/zmi/container.py:343
+#: src/pyams_content/component/paragraph/zmi/container.py:356
 msgid "Content blocks"
 msgstr "Blocs de contenu"
 
-#: src/pyams_content/component/paragraph/zmi/container.py:395
+#: src/pyams_content/component/paragraph/zmi/container.py:413
 msgid "Links and attachments..."
 msgstr "Récap. liens et PJ"
 
-#: src/pyams_content/component/paragraph/zmi/container.py:407
+#: src/pyams_content/component/paragraph/zmi/container.py:425
 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:145
 msgid "No currently defined paragraph."
 msgstr "Aucun bloc n'est associé à ce contenu."
 
-#: src/pyams_content/component/paragraph/zmi/container.py:277
+#: src/pyams_content/component/paragraph/zmi/container.py:295
 msgid "Click to open/close all paragraphs editors"
 msgstr "Afficher/masquer tous les blocs"
 
-#: src/pyams_content/component/paragraph/zmi/container.py:290
+#: src/pyams_content/component/paragraph/zmi/container.py:308
 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:151
 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 "
@@ -854,36 +900,36 @@
 msgstr "Propriétés des pictogrammes"
 
 #. Default: Header
-#: src/pyams_content/component/paragraph/zmi/pictogram.py:253
+#: src/pyams_content/component/paragraph/zmi/pictogram.py:260
 msgid "pictogram-item-header"
 msgstr "En-tête"
 
-#: src/pyams_content/component/paragraph/zmi/pictogram.py:290
+#: src/pyams_content/component/paragraph/zmi/pictogram.py:299
 #: src/pyams_content/component/paragraph/interfaces/pictogram.py:80
 msgid "Pictograms"
 msgstr "Pictogrammes"
 
-#: src/pyams_content/component/paragraph/zmi/pictogram.py:305
+#: src/pyams_content/component/paragraph/zmi/pictogram.py:314
 #: src/pyams_content/reference/pictograms/zmi/__init__.py:59
 msgid "Add pictogram"
 msgstr "Ajouter un pictogramme"
 
-#: src/pyams_content/component/paragraph/zmi/pictogram.py:318
+#: src/pyams_content/component/paragraph/zmi/pictogram.py:327
 #: 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:360
+#: src/pyams_content/component/paragraph/zmi/pictogram.py:369
 #: 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:340
+#: src/pyams_content/component/paragraph/zmi/pictogram.py:349
 msgid "Pictogram was correctly added"
 msgstr "Le pictogramme a été ajouté."
 
-#: src/pyams_content/component/paragraph/zmi/pictogram.py:350
-#: src/pyams_content/component/paragraph/zmi/pictogram.py:388
+#: src/pyams_content/component/paragraph/zmi/pictogram.py:359
+#: src/pyams_content/component/paragraph/zmi/pictogram.py:397
 msgid "You must select a pictogram!"
 msgstr "Vous devez sélectionner un pictogramme !"
 
@@ -899,27 +945,15 @@
 msgid "Edit audio properties"
 msgstr "Propriétés de la bande son"
 
-#: src/pyams_content/component/paragraph/zmi/keynumber.py:55
-msgid "Key numbers..."
-msgstr "Chiffres-clés"
-
-#: 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:96
-msgid "Edit key number paragraph properties"
-msgstr "Propriétés des chiffres-clés"
-
-#: src/pyams_content/component/paragraph/zmi/frame.py:85
+#: src/pyams_content/component/paragraph/zmi/frame.py:86
 msgid "Framed text..."
 msgstr "Encadré"
 
-#: src/pyams_content/component/paragraph/zmi/frame.py:99
+#: src/pyams_content/component/paragraph/zmi/frame.py:100
 msgid "Add new framed text paragraph"
 msgstr "Ajout d'un encadré"
 
-#: src/pyams_content/component/paragraph/zmi/frame.py:125
+#: src/pyams_content/component/paragraph/zmi/frame.py:126
 msgid "Edit framed text paragraph properties"
 msgstr "Propriétés de l'encadré"
 
@@ -971,15 +1005,7 @@
 msgid "Edit contact card properties"
 msgstr "Propriétés de la fiche contact"
 
-#: src/pyams_content/component/paragraph/zmi/header.py:50
-msgid "Header..."
-msgstr "Chapô"
-
-#: 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:90
+#: src/pyams_content/component/paragraph/zmi/header.py:49
 msgid "Edit header paragraph properties"
 msgstr "Propriétés du chapô"
 
@@ -1051,29 +1077,65 @@
 msgid "§ Title"
 msgstr "Titre §"
 
-#: src/pyams_content/component/paragraph/interfaces/__init__.py:88
+#: src/pyams_content/component/paragraph/interfaces/__init__.py:94
 msgid "Allowed paragraphs"
 msgstr "Types de blocs autorisés"
 
-#: src/pyams_content/component/paragraph/interfaces/__init__.py:89
+#: src/pyams_content/component/paragraph/interfaces/__init__.py:95
 msgid "List of paragraphs allowed for this content type"
 msgstr "Liste des types de blocs de contenu autorisés pour ce gabarit."
 
-#: src/pyams_content/component/paragraph/interfaces/__init__.py:93
-#: src/pyams_content/shared/common/zmi/types.py:173
-#: src/pyams_content/shared/common/zmi/types.py:413
+#: src/pyams_content/component/paragraph/interfaces/__init__.py:99
+#: src/pyams_content/shared/common/zmi/types.py:172
+#: src/pyams_content/shared/common/zmi/types.py:412
 msgid "Default paragraphs"
 msgstr "Types de blocs par défaut"
 
-#: src/pyams_content/component/paragraph/interfaces/__init__.py:94
+#: src/pyams_content/component/paragraph/interfaces/__init__.py:100
 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/map.py:41
+msgid "Location map"
+msgstr "Carte"
+
+#: src/pyams_content/component/paragraph/interfaces/map.py:48
+#: src/pyams_content/component/paragraph/interfaces/contact.py:72
+msgid "GPS location"
+msgstr "Position GPS"
+
+#: src/pyams_content/component/paragraph/interfaces/map.py:49
+msgid "GPS coordinates used to locate map"
+msgstr "Coordonnées GPS de situation de la carte"
+
+#: src/pyams_content/component/paragraph/interfaces/map.py:52
+msgid "Display location mark?"
+msgstr "Marqueur de position ?"
+
+#: src/pyams_content/component/paragraph/interfaces/map.py:53
+msgid "If 'yes', a location marker will be displayed on map"
+msgstr "Si 'oui', un marqueur de position sera placé sur la carte"
+
+#: src/pyams_content/component/paragraph/interfaces/map.py:57
+msgid "Map template"
+msgstr "Mode de rendu"
+
+#: src/pyams_content/component/paragraph/interfaces/map.py:58
+msgid "Presentation template used for this map"
+msgstr "Mode de rendu utilisé par cette carte"
+
 #: 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"
 
+#: src/pyams_content/component/paragraph/interfaces/video.py:49
+#: src/pyams_content/component/video/interfaces/__init__.py:49
+msgid "Video description displayed by front-office template"
+msgstr ""
+"Le texte accompagne la vidéo, en complément de la légende. Attention : sa "
+"présence et sa mise en forme dépendent du mode de rendu choisi."
+
 #: src/pyams_content/component/paragraph/interfaces/video.py:56
 #: src/pyams_content/component/video/interfaces/__init__.py:78
 msgid "Video template"
@@ -1091,12 +1153,10 @@
 #: src/pyams_content/component/paragraph/interfaces/pictogram.py:46
 #: src/pyams_content/component/links/interfaces/__init__.py:43
 #: src/pyams_content/shared/common/interfaces/types.py:75
-#: src/pyams_content/features/alert/interfaces.py:79
 msgid "Pictogram"
 msgstr "Pictogramme"
 
 #: src/pyams_content/component/paragraph/interfaces/pictogram.py:47
-#: src/pyams_content/features/alert/interfaces.py:80
 msgid "Name of the pictogram to select"
 msgstr "Sélection du pictogramme à afficher"
 
@@ -1136,14 +1196,6 @@
 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:36
-msgid "Key numbers template"
-msgstr "Mode de rendu"
-
-#: 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"
-
 #: src/pyams_content/component/paragraph/interfaces/frame.py:33
 msgid "Framed text"
 msgstr "Encadré"
@@ -1269,16 +1321,13 @@
 msgid "Presentation template used for this contact"
 msgstr "Modèle de présentation utilisé pour ce contact"
 
-#: src/pyams_content/component/paragraph/interfaces/contact.py:72
-msgid "GPS location"
-msgstr "Position GPS"
-
 #: src/pyams_content/component/paragraph/interfaces/contact.py:73
 msgid "GPS coordinates used to locate contact"
 msgstr "Coordonnées GPS de situation du contact"
 
 #: src/pyams_content/component/paragraph/interfaces/header.py:33
 #: src/pyams_content/component/paragraph/interfaces/header.py:40
+#: src/pyams_content/shared/common/interfaces/__init__.py:153
 msgid "Header"
 msgstr "Chapô"
 
@@ -1296,6 +1345,9 @@
 #: src/pyams_content/component/theme/zmi/portlet.py:40
 #: src/pyams_content/component/theme/interfaces/__init__.py:47
 #: src/pyams_content/component/theme/interfaces/__init__.py:61
+#: src/pyams_content/shared/common/zmi/search.py:189
+#: src/pyams_content/root/zmi/search.py:179
+#: src/pyams_content/root/zmi/templates/advanced-search.pt:181
 msgid "Tags"
 msgstr "Tags"
 
@@ -1303,6 +1355,7 @@
 #: src/pyams_content/component/theme/zmi/portlet.py:55
 #: src/pyams_content/component/theme/interfaces/__init__.py:88
 #: src/pyams_content/component/theme/interfaces/__init__.py:102
+#: src/pyams_content/shared/common/zmi/search.py:192
 msgid "Themes"
 msgstr "Thèmes"
 
@@ -1310,6 +1363,7 @@
 #: src/pyams_content/component/theme/zmi/portlet.py:70
 #: src/pyams_content/component/theme/interfaces/__init__.py:129
 #: src/pyams_content/component/theme/interfaces/__init__.py:143
+#: src/pyams_content/shared/common/zmi/search.py:195
 msgid "Collections"
 msgstr "Collections"
 
@@ -1352,32 +1406,32 @@
 msgid "Content collections"
 msgstr "Collections associées au contenu"
 
-#: src/pyams_content/component/theme/zmi/manager.py:51
+#: src/pyams_content/component/theme/zmi/manager.py:58
 msgid "Tags settings..."
 msgstr "Paramétrage des tags"
 
-#: src/pyams_content/component/theme/zmi/manager.py:65
+#: src/pyams_content/component/theme/zmi/manager.py:72
 msgid "Selected tags"
 msgstr "Tags sélectionnés"
 
-#: src/pyams_content/component/theme/zmi/manager.py:101
+#: src/pyams_content/component/theme/zmi/manager.py:108
 msgid "Themes settings..."
 msgstr "Paramétrage des thèmes"
 
-#: src/pyams_content/component/theme/zmi/manager.py:115
+#: src/pyams_content/component/theme/zmi/manager.py:122
 msgid "Selected themes"
 msgstr "Thèmes sélectionnés"
 
-#: src/pyams_content/component/theme/zmi/manager.py:151
+#: src/pyams_content/component/theme/zmi/manager.py:158
 msgid "Collections settings..."
 msgstr "Paramétrage des collections"
 
-#: src/pyams_content/component/theme/zmi/manager.py:165
+#: src/pyams_content/component/theme/zmi/manager.py:172
 msgid "Selected collections"
 msgstr "Collections sélectionnées"
 
 #: src/pyams_content/component/association/container.py:91
-#: src/pyams_content/component/association/zmi/__init__.py:296
+#: src/pyams_content/component/association/zmi/__init__.py:303
 #: src/pyams_content/component/association/interfaces/__init__.py:93
 msgid "Associations"
 msgstr "Liens et pièces jointes"
@@ -1395,20 +1449,20 @@
 msgid "Edit association paragraph properties"
 msgstr "Propriétés du bloc « liens et pièces jointes »"
 
-#: src/pyams_content/component/association/zmi/__init__.py:198
+#: src/pyams_content/component/association/zmi/__init__.py:205
 msgid "Public title"
 msgstr "Libellé public"
 
-#: src/pyams_content/component/association/zmi/__init__.py:216
+#: src/pyams_content/component/association/zmi/__init__.py:223
 msgid "Inner title"
 msgstr "Contenu interne"
 
-#: src/pyams_content/component/association/zmi/__init__.py:232
+#: src/pyams_content/component/association/zmi/__init__.py:239
 msgid "Size"
 msgstr "Taille"
 
-#: src/pyams_content/component/association/zmi/__init__.py:273
-#: src/pyams_content/component/association/zmi/__init__.py:283
+#: src/pyams_content/component/association/zmi/__init__.py:280
+#: src/pyams_content/component/association/zmi/__init__.py:290
 msgid "Associations list"
 msgstr "Liste des liens et pièces jointes"
 
@@ -1429,19 +1483,19 @@
 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:123
+#: src/pyams_content/component/links/__init__.py:144
 msgid "Internal link"
 msgstr "Lien interne"
 
-#: src/pyams_content/component/links/__init__.py:219
+#: src/pyams_content/component/links/__init__.py:230
 msgid "External link"
 msgstr "Lien externe"
 
-#: src/pyams_content/component/links/__init__.py:272
+#: src/pyams_content/component/links/__init__.py:283
 msgid "Mailto link"
 msgstr "Lien mailto"
 
-#: src/pyams_content/component/links/__init__.py:206
+#: src/pyams_content/component/links/__init__.py:217
 msgid "target is not published"
 msgstr "le contenu ciblé n'est pas publié"
 
@@ -1493,14 +1547,6 @@
 msgid "Edit mailto link properties"
 msgstr "Propriétés du lien « mailto »"
 
-#: src/pyams_content/component/links/zmi/reverse.py:57
-msgid "Reverse links"
-msgstr "Liens amont"
-
-#: 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:36
 msgid "Link title, as shown in front-office"
 msgstr ""
@@ -1519,6 +1565,7 @@
 
 #: src/pyams_content/component/links/interfaces/__init__.py:61
 #: src/pyams_content/shared/logo/interfaces/__init__.py:56
+#: src/pyams_content/features/redirect/interfaces/__init__.py:68
 msgid "Target URL"
 msgstr "URL cible"
 
@@ -1845,146 +1892,150 @@
 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:240
-#: src/pyams_content/shared/common/zmi/properties.py:70
+#: src/pyams_content/shared/common/__init__.py:242
+#: src/pyams_content/shared/common/zmi/properties.py:69
 #: src/pyams_content/shared/common/zmi/manager.py:96
 msgid "Properties"
 msgstr "Propriétés"
 
-#: src/pyams_content/shared/common/__init__.py:150
-#: src/pyams_content/shared/common/__init__.py:158
+#: src/pyams_content/shared/common/__init__.py:152
+#: src/pyams_content/shared/common/__init__.py:160
 #, python-format
 msgid "{date} by {principal}"
 msgstr "{date} par {principal}"
 
-#: src/pyams_content/shared/common/__init__.py:263
+#: src/pyams_content/shared/common/__init__.py:265
 #, python-format
 msgid "title length should be between 40 and 66 characters ({length} actually)"
 msgstr ""
 "Le titre devrait être composé de 40 à 66 caractères ({length} actuellement)"
 
-#: src/pyams_content/shared/common/zmi/search.py:92
-#: src/pyams_content/root/zmi/search.py:91
+#: src/pyams_content/shared/common/zmi/search.py:96
+#: src/pyams_content/root/zmi/search.py:95
 msgid "Quick search results"
 msgstr "Résultats de la recherche rapide"
 
-#: src/pyams_content/shared/common/zmi/search.py:157
-#: src/pyams_content/shared/common/zmi/search.py:190
-#: src/pyams_content/root/zmi/search.py:147
-#: src/pyams_content/root/zmi/search.py:180
+#: src/pyams_content/shared/common/zmi/search.py:162
+#: src/pyams_content/shared/common/zmi/search.py:204
+#: src/pyams_content/root/zmi/search.py:152
+#: src/pyams_content/root/zmi/search.py:188
 msgid "Advanced search"
 msgstr "Recherche avancée"
 
-#: src/pyams_content/shared/common/zmi/search.py:282
-#: src/pyams_content/root/zmi/search.py:260
+#: src/pyams_content/shared/common/zmi/search.py:332
+#: src/pyams_content/root/zmi/search.py:280
 msgid "Advanced search results"
 msgstr "Résultats de la recherche avancée"
 
-#: src/pyams_content/shared/common/zmi/search.py:165
+#: src/pyams_content/shared/common/zmi/search.py:170
 #: src/pyams_content/shared/common/zmi/dashboard.py:231
-#: src/pyams_content/root/zmi/search.py:159
+#: src/pyams_content/root/zmi/search.py:164
 msgid "Owner"
 msgstr "Propriétaire"
 
-#: src/pyams_content/shared/common/zmi/search.py:168
+#: src/pyams_content/shared/common/zmi/search.py:173
 #: src/pyams_content/shared/common/zmi/dashboard.py:154
 msgid "Status"
 msgstr "Statut"
 
-#: src/pyams_content/shared/common/zmi/search.py:172
-#: src/pyams_content/root/zmi/search.py:162
+#: src/pyams_content/shared/common/zmi/search.py:177
+#: src/pyams_content/root/zmi/search.py:167
 msgid "Created after..."
 msgstr "Créé entre le"
 
-#: src/pyams_content/shared/common/zmi/search.py:175
-#: src/pyams_content/root/zmi/search.py:165
+#: src/pyams_content/shared/common/zmi/search.py:180
+#: src/pyams_content/root/zmi/search.py:170
 msgid "Created before..."
 msgstr "et le"
 
-#: src/pyams_content/shared/common/zmi/search.py:178
-#: src/pyams_content/root/zmi/search.py:168
+#: src/pyams_content/shared/common/zmi/search.py:183
+#: src/pyams_content/root/zmi/search.py:173
 msgid "Modified after..."
 msgstr "Modifié entre le"
 
-#: src/pyams_content/shared/common/zmi/search.py:181
-#: src/pyams_content/root/zmi/search.py:171
+#: src/pyams_content/shared/common/zmi/search.py:186
+#: src/pyams_content/root/zmi/search.py:176
 msgid "Modified before..."
 msgstr "et le"
 
-#: src/pyams_content/shared/common/zmi/properties.py:60
+#: src/pyams_content/shared/common/zmi/properties.py:59
 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"
 
-#: src/pyams_content/shared/common/zmi/types.py:71
+#: src/pyams_content/shared/common/zmi/types.py:70
 msgid "Data types"
 msgstr "Types de contenus"
 
-#: src/pyams_content/shared/common/zmi/types.py:149
+#: src/pyams_content/shared/common/zmi/types.py:148
 msgid "Data type label"
 msgstr "Libellé du type"
 
-#: src/pyams_content/shared/common/zmi/types.py:189
-#: src/pyams_content/shared/common/zmi/types.py:429
+#: src/pyams_content/shared/common/zmi/types.py:188
+#: src/pyams_content/shared/common/zmi/types.py:428
 msgid "Default associations"
 msgstr "Liens et pièces jointes par défaut"
 
-#: src/pyams_content/shared/common/zmi/types.py:205
+#: src/pyams_content/shared/common/zmi/types.py:204
 msgid "Default themes"
 msgstr "Thèmes par défaut"
 
-#: src/pyams_content/shared/common/zmi/types.py:233
+#: src/pyams_content/shared/common/zmi/types.py:232
 msgid "Content data types"
 msgstr "Types de contenus"
 
-#: src/pyams_content/shared/common/zmi/types.py:256
+#: src/pyams_content/shared/common/zmi/types.py:255
 msgid "Add data type"
 msgstr "Ajouter un type"
 
-#: src/pyams_content/shared/common/zmi/types.py:268
+#: src/pyams_content/shared/common/zmi/types.py:267
 msgid "Add new data type"
 msgstr "Ajout d'un type de contenu"
 
-#: src/pyams_content/shared/common/zmi/types.py:311
+#: src/pyams_content/shared/common/zmi/types.py:310
 msgid "Data type properties"
 msgstr "Propriétés du type de contenu"
 
-#: src/pyams_content/shared/common/zmi/types.py:392
+#: src/pyams_content/shared/common/zmi/types.py:391
 msgid "Subtype label"
 msgstr "Libellé du sous-type"
 
-#: src/pyams_content/shared/common/zmi/types.py:473
+#: src/pyams_content/shared/common/zmi/types.py:472
 msgid "Add subtype"
 msgstr "Ajouter un sous-type"
 
-#: src/pyams_content/shared/common/zmi/types.py:485
+#: src/pyams_content/shared/common/zmi/types.py:484
 msgid "Add new subtype"
 msgstr "Ajout d'un sous-type de contenu"
 
-#: src/pyams_content/shared/common/zmi/types.py:532
+#: src/pyams_content/shared/common/zmi/types.py:531
 msgid "Data subtype properties"
 msgstr "Propriétés du fichier standard"
 
-#: src/pyams_content/shared/common/zmi/types.py:116
+#: src/pyams_content/shared/common/zmi/types.py:573
+msgid "Select content type..."
+msgstr "Sélectionnez un type de contenu..."
+
+#: src/pyams_content/shared/common/zmi/types.py:115
 msgid "No currently defined data type."
 msgstr "Aucun type de contenu n'est actuellement défini."
 
-#: src/pyams_content/shared/common/zmi/types.py:301
+#: src/pyams_content/shared/common/zmi/types.py:300
 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:508
+#: src/pyams_content/shared/common/zmi/types.py:507
 msgid "Subtype was correctly added."
 msgstr "Le sous-type a été ajouté."
 
-#: src/pyams_content/shared/common/zmi/types.py:522
+#: src/pyams_content/shared/common/zmi/types.py:521
 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:161
+#: src/pyams_content/shared/common/zmi/types.py:160
 msgid "Click to see subtypes"
 msgstr "Montrer ou caher les sous-types"
 
@@ -2095,8 +2146,7 @@
 msgid ""
 "You must confirm that you checked this content before requesting "
 "publication!!"
-msgstr ""
-"Vous devez avoir audité ce contenu avant de pouvoir le publier !!"
+msgstr "Vous devez avoir audité ce contenu avant de pouvoir le publier !!"
 
 #: src/pyams_content/shared/common/zmi/workflow.py:84
 #: src/pyams_content/workflow/__init__.py:648
@@ -2134,45 +2184,45 @@
 msgid "Created or modified in this version"
 msgstr "Créé ou modifié dans cette version"
 
-#: src/pyams_content/shared/common/zmi/summary.py:50
+#: src/pyams_content/shared/common/zmi/summary.py:52
 msgid "Display content summary"
 msgstr "Récapitulatif pour ce contenu"
 
-#: src/pyams_content/shared/common/zmi/summary.py:74
+#: src/pyams_content/shared/common/zmi/summary.py:76
 msgid "Identity card"
 msgstr "Carte d'identité"
 
-#: src/pyams_content/shared/common/zmi/summary.py:86
+#: src/pyams_content/shared/common/zmi/summary.py:94
 msgid "Requested action"
 msgstr "Évolution demandée"
 
-#: src/pyams_content/shared/common/zmi/summary.py:127
+#: src/pyams_content/shared/common/zmi/summary.py:135
 msgid "Publication and retire dates"
 msgstr "Dates de publication et de retrait planifiées"
 
-#: src/pyams_content/shared/common/zmi/summary.py:146
+#: src/pyams_content/shared/common/zmi/summary.py:154
 msgid "Current version"
 msgstr "À propos de cette version"
 
-#: src/pyams_content/shared/common/zmi/summary.py:176
+#: src/pyams_content/shared/common/zmi/summary.py:184
 msgid "Content history"
 msgstr "Pour mémoire"
 
-#: src/pyams_content/shared/common/zmi/summary.py:117
+#: src/pyams_content/shared/common/zmi/summary.py:125
 msgid "Associated comment"
 msgstr "Commentaire associé"
 
-#: src/pyams_content/shared/common/zmi/summary.py:158
+#: src/pyams_content/shared/common/zmi/summary.py:166
 #: src/pyams_content/shared/common/zmi/dashboard.py:198
 msgid "Version"
 msgstr "Version"
 
-#: src/pyams_content/shared/common/zmi/summary.py:107
+#: src/pyams_content/shared/common/zmi/summary.py:115
 #, python-format
 msgid "{state} {date} by {principal}"
 msgstr "{state} {date} par {principal}"
 
-#: src/pyams_content/shared/common/zmi/summary.py:164
+#: src/pyams_content/shared/common/zmi/summary.py:172
 #, python-format
 msgid "{state} since {date}, by {principal}"
 msgstr "{state} depuis {date} par {principal}"
@@ -2276,6 +2326,14 @@
 msgid "You must provide an URL for this item!"
 msgstr "Vous devez fournir une URL pour ce contenu !"
 
+#: src/pyams_content/shared/common/zmi/reverse.py:57
+msgid "Reverse links"
+msgstr "Liens amont"
+
+#: src/pyams_content/shared/common/zmi/reverse.py:66
+msgid "Content's internal links"
+msgstr "Autres contenus qui pointent vers ce contenu"
+
 #: src/pyams_content/shared/common/zmi/site.py:38
 #, python-format
 msgid ""
@@ -2284,11 +2342,11 @@
 "RECHERCHE - Tous contenus présents dans &laquo;&nbsp;{site}&nbsp;&raquo; "
 "confondus"
 
-#: src/pyams_content/shared/common/zmi/portal.py:46
+#: src/pyams_content/shared/common/zmi/portal.py:44
 msgid "Edit default template properties"
 msgstr "Modèle de présentation par défaut"
 
-#: src/pyams_content/shared/common/zmi/portal.py:56
+#: src/pyams_content/shared/common/zmi/portal.py:54
 msgid ""
 "**This form allows you to select shared content default template.**\n"
 "\n"
@@ -2311,9 +2369,9 @@
 "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/portal.py:70
+msgid "Use tool default template"
+msgstr "Utiliser le modèle de présentation par défaut de ce gabarit"
 
 #: src/pyams_content/shared/common/zmi/dashboard.py:134
 msgid "Unique ID"
@@ -2401,8 +2459,8 @@
 
 #: src/pyams_content/shared/common/zmi/dashboard.py:545
 #: src/pyams_content/root/zmi/__init__.py:346
-msgid "Your favorites"
-msgstr "Mes favoris"
+msgid "Your favorite contents"
+msgstr "Mes contenus favoris"
 
 #: src/pyams_content/shared/common/zmi/dashboard.py:558
 #: src/pyams_content/root/zmi/__init__.py:359
@@ -2610,7 +2668,7 @@
 #: 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 ?"
+msgstr "Tunnel de publication activé"
 
 #: src/pyams_content/shared/common/zmi/security.py:218
 msgid "Managers restrictions"
@@ -2732,9 +2790,9 @@
 #: 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:47
-#: src/pyams_content/shared/form/zmi/field.py:160
-#: src/pyams_content/shared/form/interfaces/__init__.py:62
-#: src/pyams_content/features/menu/zmi/__init__.py:208
+#: src/pyams_content/shared/form/zmi/field.py:167
+#: src/pyams_content/shared/form/interfaces/__init__.py:60
+#: src/pyams_content/features/menu/zmi/__init__.py:215
 msgid "Label"
 msgstr "Libellé"
 
@@ -2846,25 +2904,25 @@
 "Un numéro unique lui sera également attribué ; ce numéro sera conservé "
 "pendant toute la vie du contenu, quelle que soit la version."
 
-#: src/pyams_content/shared/common/zmi/templates/advanced-search.pt:128
-#: src/pyams_content/root/zmi/templates/advanced-search.pt:128
+#: src/pyams_content/shared/common/zmi/templates/advanced-search.pt:130
+#: src/pyams_content/root/zmi/templates/advanced-search.pt:130
 msgid "Created between"
 msgstr "Créé entre le"
 
-#: src/pyams_content/shared/common/zmi/templates/advanced-search.pt:140
-#: src/pyams_content/shared/common/zmi/templates/advanced-search.pt:166
-#: src/pyams_content/root/zmi/templates/advanced-search.pt:140
-#: src/pyams_content/root/zmi/templates/advanced-search.pt:166
+#: src/pyams_content/shared/common/zmi/templates/advanced-search.pt:142
+#: src/pyams_content/shared/common/zmi/templates/advanced-search.pt:168
+#: src/pyams_content/root/zmi/templates/advanced-search.pt:142
+#: src/pyams_content/root/zmi/templates/advanced-search.pt:168
 msgid "and"
 msgstr "et le"
 
-#: src/pyams_content/shared/common/zmi/templates/advanced-search.pt:154
-#: src/pyams_content/root/zmi/templates/advanced-search.pt:154
+#: src/pyams_content/shared/common/zmi/templates/advanced-search.pt:156
+#: src/pyams_content/root/zmi/templates/advanced-search.pt:156
 msgid "Modified between"
 msgstr "Modifié entre le"
 
-#: src/pyams_content/shared/common/zmi/templates/advanced-search.pt:202
-#: src/pyams_content/root/zmi/templates/advanced-search.pt:202
+#: src/pyams_content/shared/common/zmi/templates/advanced-search.pt:214
+#: src/pyams_content/root/zmi/templates/advanced-search.pt:211
 msgid "Tab label"
 msgstr "Libellé de l'onglet"
 
@@ -2969,12 +3027,12 @@
 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
+#: src/pyams_content/shared/common/portlet/content/skin/__init__.py:39
 msgid "Default content renderer"
 msgstr "Par défaut"
 
 #: src/pyams_content/shared/common/interfaces/types.py:43
-#: src/pyams_content/shared/form/zmi/field.py:149
+#: src/pyams_content/shared/form/zmi/field.py:156
 msgid "Name"
 msgstr "Nom"
 
@@ -3057,22 +3115,22 @@
 msgid "Type of content data"
 msgstr "Type du contenu associé à ce gabarit"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:46
+#: src/pyams_content/shared/common/interfaces/__init__.py:45
 #: src/pyams_content/root/interfaces/__init__.py:43
 msgid "Webmasters"
 msgstr "Webmestres"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:47
+#: src/pyams_content/shared/common/interfaces/__init__.py:46
 msgid "Webmasters can handle all contents, including published ones"
 msgstr ""
 "Les webmestres peuvent modifier et gérer tous les contenus, y compris ceux "
 "qui sont publiés et hormis ceux qui sont archivés"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:51
+#: src/pyams_content/shared/common/interfaces/__init__.py:50
 msgid "Pilots"
 msgstr "Pilotes"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:52
+#: src/pyams_content/shared/common/interfaces/__init__.py:51
 msgid ""
 "Pilots can handle tool configuration, manage access rules, grant users roles "
 "and manage managers restrictions"
@@ -3081,13 +3139,13 @@
 "et les contributeurs, et limitent si nécessaire l'intervention des "
 "responsables à certains contenus"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:57
-#: src/pyams_content/shared/common/interfaces/__init__.py:191
+#: src/pyams_content/shared/common/interfaces/__init__.py:56
+#: src/pyams_content/shared/common/interfaces/__init__.py:196
 msgid "Managers"
 msgstr "Responsables"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:58
-#: src/pyams_content/shared/common/interfaces/__init__.py:192
+#: src/pyams_content/shared/common/interfaces/__init__.py:57
+#: src/pyams_content/shared/common/interfaces/__init__.py:197
 msgid ""
 "Managers can handle main operations in tool's workflow, like publish or "
 "retire contents"
@@ -3096,38 +3154,38 @@
 "(comme la publication ou le retrait des contenus), dans la limite des "
 "restrictions qui leur sont imposées"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:63
-#: src/pyams_content/shared/common/interfaces/__init__.py:197
+#: src/pyams_content/shared/common/interfaces/__init__.py:62
+#: src/pyams_content/shared/common/interfaces/__init__.py:202
 msgid "Contributors"
 msgstr "Contributeurs"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:64
+#: src/pyams_content/shared/common/interfaces/__init__.py:63
 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:68
-#: src/pyams_content/shared/common/interfaces/__init__.py:203
+#: src/pyams_content/shared/common/interfaces/__init__.py:67
+#: src/pyams_content/shared/common/interfaces/__init__.py:208
 msgid "Designers"
 msgstr "Designers"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:69
-#: src/pyams_content/shared/common/interfaces/__init__.py:204
+#: src/pyams_content/shared/common/interfaces/__init__.py:68
+#: src/pyams_content/shared/common/interfaces/__init__.py:209
 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
+#: src/pyams_content/shared/common/interfaces/__init__.py:94
 msgid "Workflow name"
 msgstr "Nom du workflow"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:96
+#: src/pyams_content/shared/common/interfaces/__init__.py:95
 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:123
+msgid "Content URL"
+msgstr "URL du contenu"
+
 #: src/pyams_content/shared/common/interfaces/__init__.py:124
-msgid "Content URL"
-msgstr "URL du contenu"
-
-#: 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 "
@@ -3141,11 +3199,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:130
+#: src/pyams_content/shared/common/interfaces/__init__.py:129
 msgid "Version creator"
 msgstr "À l'origine de cette version"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:131
+#: src/pyams_content/shared/common/interfaces/__init__.py:130
 msgid ""
 "Name of content's version creator. The creator of the first version is also "
 "it's owner."
@@ -3153,74 +3211,84 @@
 "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:135
+#: src/pyams_content/shared/common/interfaces/__init__.py:134
 msgid "First owner"
 msgstr "Premier propriétaire"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:136
+#: src/pyams_content/shared/common/interfaces/__init__.py:135
 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:140
+#: src/pyams_content/shared/common/interfaces/__init__.py:139
 msgid "Version creation"
 msgstr "Date de création"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:143
+#: src/pyams_content/shared/common/interfaces/__init__.py:142
 msgid "Version modifiers"
 msgstr "Intervenants"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:144
+#: src/pyams_content/shared/common/interfaces/__init__.py:143
 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:147
+#: src/pyams_content/shared/common/interfaces/__init__.py:146
 msgid "Last modifier"
 msgstr "Dernier intervenant"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:148
+#: src/pyams_content/shared/common/interfaces/__init__.py:147
 msgid "Last principal who modified this content"
 msgstr "Dernier utilisateur étant intervenu sur ce contenu"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:151
+#: src/pyams_content/shared/common/interfaces/__init__.py:150
 msgid "Last update"
 msgstr "Dernière modification"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:155
+#: src/pyams_content/shared/common/interfaces/__init__.py:154
+msgid "Content's header is generally displayed in page header"
+msgstr "Le chapô du contenu est généralement affiché en tête de page"
+
+#: src/pyams_content/shared/common/interfaces/__init__.py:159
+msgid "Meta-description"
+msgstr "Méta-description"
+
+#: src/pyams_content/shared/common/interfaces/__init__.py:160
 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"
+"be seen, for example, in some search engines results as content's "
+"description; if description is empty, content's header will be used."
 msgstr ""
 "La description du contenu est 'masquée' dans les en-têtes des pages HTML ; "
 "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:160
+"moteurs de recherche ; si la description n'est pas renseignée, le chapô "
+"(s'il existe pour ce contenu) sera utilisé."
+
+#: src/pyams_content/shared/common/interfaces/__init__.py:165
 msgid "Keywords"
 msgstr "Mots-clés"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:161
+#: src/pyams_content/shared/common/interfaces/__init__.py:166
 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:164
-#: src/pyams_content/shared/site/zmi/folder.py:78
-#: src/pyams_content/shared/site/interfaces/__init__.py:67
+#: src/pyams_content/shared/common/interfaces/__init__.py:169
+#: src/pyams_content/shared/site/zmi/folder.py:79
+#: src/pyams_content/shared/site/interfaces/__init__.py:76
 msgid "Notepad"
 msgstr "Bloc-notes"
 
-#: 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:68
+#: src/pyams_content/shared/common/interfaces/__init__.py:170
+#: src/pyams_content/shared/site/zmi/folder.py:80
+#: src/pyams_content/shared/site/interfaces/__init__.py:77
 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:184
+#: src/pyams_content/shared/common/interfaces/__init__.py:189
 msgid "Content owner"
 msgstr "Propriétaire"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:185
+#: src/pyams_content/shared/common/interfaces/__init__.py:190
 msgid ""
 "The owner is the creator of content's first version, except if it was "
 "transferred afterwards to another owner"
@@ -3229,7 +3297,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:198
+#: src/pyams_content/shared/common/interfaces/__init__.py:203
 msgid ""
 "Contributors are users which are allowed to update this content in addition "
 "to it's owner"
@@ -3237,11 +3305,11 @@
 "Les contributeurs sont autorisés, en plus du propriétaire, à modifier ce "
 "contenu"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:208
+#: src/pyams_content/shared/common/interfaces/__init__.py:213
 msgid "Readers"
 msgstr "Relecteurs"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:209
+#: src/pyams_content/shared/common/interfaces/__init__.py:214
 msgid ""
 "Readers are users which are asked to verify and comment contents before they "
 "are published"
@@ -3249,27 +3317,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:214
+#: src/pyams_content/shared/common/interfaces/__init__.py:219
 msgid "Guests"
 msgstr "Invités"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:215
+#: src/pyams_content/shared/common/interfaces/__init__.py:220
 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:235
+#: src/pyams_content/shared/common/interfaces/__init__.py:243
 msgid "Principal ID"
 msgstr "ID utilisateur"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:274
-#: src/pyams_content/shared/common/interfaces/__init__.py:299
+#: src/pyams_content/shared/common/interfaces/__init__.py:282
+#: src/pyams_content/shared/common/interfaces/__init__.py:307
 msgid "Publication checks"
 msgstr "Activer le tunnel de publication"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:275
+#: src/pyams_content/shared/common/interfaces/__init__.py:283
 msgid ""
 "If 'yes', this contributor will have to confirm that contents have been "
 "previewed and checked before asking for publication"
@@ -3277,7 +3345,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:300
+#: src/pyams_content/shared/common/interfaces/__init__.py:308
 msgid ""
 "If 'yes', this manager will have to confirm that contents have been "
 "previewed and checked before publishing a content"
@@ -3285,11 +3353,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:305
+#: src/pyams_content/shared/common/interfaces/__init__.py:313
 msgid "Restricted contents"
 msgstr "Accès restreints"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:306
+#: src/pyams_content/shared/common/interfaces/__init__.py:314
 msgid ""
 "If 'yes', this manager will get restricted access to manage contents based "
 "on selected settings"
@@ -3297,21 +3365,21 @@
 "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:311
+#: src/pyams_content/shared/common/interfaces/__init__.py:319
 msgid "Selected owners"
 msgstr "Propriétaires"
 
-#: src/pyams_content/shared/common/interfaces/__init__.py:312
+#: src/pyams_content/shared/common/interfaces/__init__.py:320
 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 "
 "propriétaires"
 
-#: src/pyams_content/shared/form/__init__.py:99
+#: src/pyams_content/shared/form/__init__.py:97
 msgid "Form fields"
 msgstr "Champs de saisie"
 
-#: src/pyams_content/shared/form/__init__.py:100
+#: src/pyams_content/shared/form/__init__.py:98
 msgid "no field defined"
 msgstr "aucun champ défini"
 
@@ -3389,179 +3457,175 @@
 msgid "Form fields..."
 msgstr "Champs de saisie"
 
-#: src/pyams_content/shared/form/zmi/field.py:171
-#: src/pyams_content/shared/form/interfaces/__init__.py:57
+#: src/pyams_content/shared/form/zmi/field.py:178
+#: src/pyams_content/shared/form/interfaces/__init__.py:55
 msgid "Field type"
 msgstr "Type de champ"
 
-#: src/pyams_content/shared/form/zmi/field.py:204
+#: src/pyams_content/shared/form/zmi/field.py:211
 msgid "Form fields list"
 msgstr "Liste des champs du formulaire"
 
-#: src/pyams_content/shared/form/zmi/field.py:227
-#: src/pyams_content/shared/form/zmi/field.py:240
+#: src/pyams_content/shared/form/zmi/field.py:234
+#: src/pyams_content/shared/form/zmi/field.py:247
 msgid "Add form field"
 msgstr "Ajouter un champ"
 
-#: src/pyams_content/shared/form/zmi/field.py:278
+#: src/pyams_content/shared/form/zmi/field.py:285
 msgid "Edit form field properties"
 msgstr "Propriétés du champ"
 
-#: src/pyams_content/shared/form/zmi/field.py:180
+#: src/pyams_content/shared/form/zmi/field.py:187
 msgid "-- unknown field type --"
 msgstr "-- type de champ inconnu --"
 
-#: src/pyams_content/shared/form/zmi/field.py:115
+#: src/pyams_content/shared/form/zmi/field.py:122
 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:269
 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:35
+#: src/pyams_content/shared/form/interfaces/__init__.py:33
 msgid "Form"
 msgstr "Formulaire"
 
-#: src/pyams_content/shared/form/interfaces/__init__.py:53
+#: src/pyams_content/shared/form/interfaces/__init__.py:51
 msgid "Field name"
 msgstr "Nom du champ"
 
-#: src/pyams_content/shared/form/interfaces/__init__.py:54
+#: src/pyams_content/shared/form/interfaces/__init__.py:52
 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:58
+#: src/pyams_content/shared/form/interfaces/__init__.py:56
 msgid "Selected field type"
 msgstr "Type de champ proposé à l'internaute"
 
-#: src/pyams_content/shared/form/interfaces/__init__.py:63
+#: src/pyams_content/shared/form/interfaces/__init__.py:61
 msgid "User field label"
 msgstr "Libellé affiché à l'internaute"
 
-#: src/pyams_content/shared/form/interfaces/__init__.py:67
+#: src/pyams_content/shared/form/interfaces/__init__.py:65
 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:70
+#: src/pyams_content/shared/form/interfaces/__init__.py:68
 msgid "Placeholder"
 msgstr "Espace réservé"
 
-#: src/pyams_content/shared/form/interfaces/__init__.py:71
+#: src/pyams_content/shared/form/interfaces/__init__.py:69
 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:74
+#: src/pyams_content/shared/form/interfaces/__init__.py:72
 msgid "Optional values"
 msgstr "Liste de valeurs"
 
-#: src/pyams_content/shared/form/interfaces/__init__.py:75
+#: src/pyams_content/shared/form/interfaces/__init__.py:73
 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:78
+#: src/pyams_content/shared/form/interfaces/__init__.py:76
 msgid "Default value"
 msgstr "Valeur par défaut"
 
-#: src/pyams_content/shared/form/interfaces/__init__.py:79
+#: src/pyams_content/shared/form/interfaces/__init__.py:77
 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:82
+#: src/pyams_content/shared/form/interfaces/__init__.py:80
 msgid "Required?"
 msgstr "Obligatoire ?"
 
-#: src/pyams_content/shared/form/interfaces/__init__.py:83
+#: src/pyams_content/shared/form/interfaces/__init__.py:81
 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:88
+#: src/pyams_content/shared/form/interfaces/__init__.py:86
 msgid "Select 'no' to hide given field..."
 msgstr "Sélectionnez 'non' pour masquer ce champ"
 
-#: src/pyams_content/shared/form/interfaces/__init__.py:122
+#: src/pyams_content/shared/form/interfaces/__init__.py:120
 msgid "Form title"
 msgstr "Titre du formulaire"
 
-#: 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:128
+#: src/pyams_content/shared/form/interfaces/__init__.py:123
 msgid "Form handler"
 msgstr "Gestionnaire du formulaire"
 
-#: src/pyams_content/shared/form/interfaces/__init__.py:129
+#: src/pyams_content/shared/form/interfaces/__init__.py:124
 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:132
+#: src/pyams_content/shared/form/interfaces/__init__.py:127
 msgid "Authenticated only?"
 msgstr "Authentification requise ?"
 
-#: src/pyams_content/shared/form/interfaces/__init__.py:133
+#: src/pyams_content/shared/form/interfaces/__init__.py:128
 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:137
+#: src/pyams_content/shared/form/interfaces/__init__.py:132
 msgid "Use captcha?"
 msgstr "Ajouter un captcha ?"
 
-#: src/pyams_content/shared/form/interfaces/__init__.py:138
+#: src/pyams_content/shared/form/interfaces/__init__.py:133
 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:142
+#: src/pyams_content/shared/form/interfaces/__init__.py:137
 msgid "Submit label"
 msgstr "Libellé de soumission"
 
-#: src/pyams_content/shared/form/interfaces/__init__.py:143
+#: src/pyams_content/shared/form/interfaces/__init__.py:138
 msgid "Label of form submit button"
 msgstr "Libellé du bouton de soumission du formulaire"
 
-#: src/pyams_content/shared/form/interfaces/__init__.py:180
+#: src/pyams_content/shared/form/interfaces/__init__.py:175
 msgid "Source address"
 msgstr "Adresse source"
 
-#: src/pyams_content/shared/form/interfaces/__init__.py:181
+#: src/pyams_content/shared/form/interfaces/__init__.py:176
 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:184
+#: src/pyams_content/shared/form/interfaces/__init__.py:179
 msgid "Source name"
 msgstr "Nom de la source"
 
-#: src/pyams_content/shared/form/interfaces/__init__.py:185
+#: src/pyams_content/shared/form/interfaces/__init__.py:180
 msgid "Name of mail data sender"
 msgstr "Nom de l'émetteur des données"
 
-#: src/pyams_content/shared/form/interfaces/__init__.py:188
+#: src/pyams_content/shared/form/interfaces/__init__.py:183
 msgid "Recipient address"
 msgstr "Adresse de destination"
 
-#: src/pyams_content/shared/form/interfaces/__init__.py:189
+#: src/pyams_content/shared/form/interfaces/__init__.py:184
 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:192
+#: src/pyams_content/shared/form/interfaces/__init__.py:187
 msgid "Recipient name"
 msgstr "Nom du destinataire"
 
-#: src/pyams_content/shared/form/interfaces/__init__.py:193
+#: src/pyams_content/shared/form/interfaces/__init__.py:188
 msgid "Name of data recipient"
 msgstr "Nom du destinataire des messages"
 
@@ -3625,7 +3689,7 @@
 "Trier tous les résultats sur la date de première publication (du plus récent "
 "au plus ancien)"
 
-#: src/pyams_content/shared/view/zmi/properties.py:40
+#: src/pyams_content/shared/view/zmi/properties.py:45
 msgid "Main view settings"
 msgstr "Paramètres de la vue"
 
@@ -3726,7 +3790,7 @@
 "uniquement sur les paramètres de la vue."
 
 #: src/pyams_content/shared/view/portlet/interfaces.py:91
-#: src/pyams_content/shared/view/interfaces/__init__.py:86
+#: src/pyams_content/shared/view/interfaces/__init__.py:102
 msgid "Results count limit"
 msgstr "Limite de résultats"
 
@@ -3745,152 +3809,174 @@
 msgid "No selected view"
 msgstr "Aucune vue sélectionnée."
 
-#: src/pyams_content/shared/view/interfaces/__init__.py:32
+#: src/pyams_content/shared/view/interfaces/__init__.py:34
 msgid "View"
 msgstr "Vue"
 
-#: src/pyams_content/shared/view/interfaces/__init__.py:150
+#: src/pyams_content/shared/view/interfaces/__init__.py:166
 msgid "Always include selected internal references"
 msgstr "Toujours inclure toutes les références internes"
 
-#: src/pyams_content/shared/view/interfaces/__init__.py:151
+#: src/pyams_content/shared/view/interfaces/__init__.py:167
 msgid "Include selected internal references only if empty"
 msgstr "Inclure les références internes seulement lorsque la vue est vide"
 
-#: src/pyams_content/shared/view/interfaces/__init__.py:41
+#: src/pyams_content/shared/view/interfaces/__init__.py:43
 #: src/pyams_content/interfaces/__init__.py:113
 #: src/pyams_content/features/review/interfaces.py:74
 msgid "Creation date"
 msgstr "Date de création"
 
-#: src/pyams_content/shared/view/interfaces/__init__.py:42
+#: src/pyams_content/shared/view/interfaces/__init__.py:44
 msgid "Last update date"
 msgstr "Date de dernière modification"
 
-#: src/pyams_content/shared/view/interfaces/__init__.py:43
+#: src/pyams_content/shared/view/interfaces/__init__.py:45
 msgid "Current publication date"
 msgstr "Date de publication de la version actuelle"
 
-#: src/pyams_content/shared/view/interfaces/__init__.py:44
+#: src/pyams_content/shared/view/interfaces/__init__.py:46
 msgid "First publication date"
 msgstr "Date de première publication"
 
-#: src/pyams_content/shared/view/interfaces/__init__.py:62
+#: src/pyams_content/shared/view/interfaces/__init__.py:64
 msgid "Select context type?"
-msgstr "Type du contexte ?"
-
-#: src/pyams_content/shared/view/interfaces/__init__.py:63
+msgstr "Gabarit du contexte ?"
+
+#: src/pyams_content/shared/view/interfaces/__init__.py:65
 msgid "If 'yes', content type will be extracted from context"
 msgstr ""
-"Si 'oui', seuls des contenus du même type que le contexte seront "
+"Si 'oui', seuls des contenus du même gabarit que le contexte seront "
 "automatiquement sélectionnés"
 
-#: src/pyams_content/shared/view/interfaces/__init__.py:67
+#: src/pyams_content/shared/view/interfaces/__init__.py:69
 msgid "Other content types"
-msgstr "Autres types de contenus"
-
-#: src/pyams_content/shared/view/interfaces/__init__.py:68
+msgstr "Autres gabarits"
+
+#: src/pyams_content/shared/view/interfaces/__init__.py:70
 msgid "Selected content types; leave empty for all"
 msgstr ""
+"Autres gabarits sélectionnés ; si l'on n'extrait pas le gabarit du contexte "
+"et si cette sélection est vide, tous les gabarits seront pris en charge"
+
+#: src/pyams_content/shared/view/interfaces/__init__.py:77
+msgid "Select context data type?"
+msgstr "Type du contexte ?"
+
+#: src/pyams_content/shared/view/interfaces/__init__.py:78
+msgid ""
+"If 'yes', content data type (if available) will be extracted from context"
+msgstr ""
+"Si 'oui', et si le contexte de la vue est \"typé\", seuls des contenus du "
+"même type que le contexte seront automatiquement sélectionnés"
+
+#: src/pyams_content/shared/view/interfaces/__init__.py:83
+msgid "Other data types"
+msgstr "Autres types de contenus"
+
+#: src/pyams_content/shared/view/interfaces/__init__.py:84
+msgid "Selected data types; leave empty for all"
+msgstr ""
 "Autres types de contenus sélectionnés ; si l'on n'extrait pas le type du "
-"contexte et si cette sélection est vide, tous les types seront pris en charge"
-
-#: src/pyams_content/shared/view/interfaces/__init__.py:75
+"contexte et si cette sélection est vide, tous les contenus (typés ou non) "
+"seront pris en charge"
+
+#: src/pyams_content/shared/view/interfaces/__init__.py:91
 msgid "Order by"
 msgstr "Ordre de tri"
 
-#: src/pyams_content/shared/view/interfaces/__init__.py:76
+#: src/pyams_content/shared/view/interfaces/__init__.py:92
 msgid "Property to use to sort results"
 msgstr ""
 "Propriété utilisée pour trier les résultats ; par défaut, le tri se fait en "
 "ordre inverse, donc du plus récent au plus ancien"
 
-#: src/pyams_content/shared/view/interfaces/__init__.py:81
+#: src/pyams_content/shared/view/interfaces/__init__.py:97
 msgid "Reversed order?"
 msgstr "Ordre inverse ?"
 
-#: src/pyams_content/shared/view/interfaces/__init__.py:82
+#: src/pyams_content/shared/view/interfaces/__init__.py:98
 msgid "If 'yes', items order will be reversed"
 msgstr ""
 "Si 'non', le tri se fera en ordre \"naturel\", donc du plus ancien au plus "
 "récent"
 
-#: src/pyams_content/shared/view/interfaces/__init__.py:87
+#: src/pyams_content/shared/view/interfaces/__init__.py:103
 msgid "Maximum number of results that the view may retrieve"
 msgstr "Nombre maximal de résultats que la vue doit renvoyer"
 
-#: src/pyams_content/shared/view/interfaces/__init__.py:160
+#: src/pyams_content/shared/view/interfaces/__init__.py:176
 msgid "Internal references usage"
 msgstr "Utilisation des références internes"
 
-#: src/pyams_content/shared/view/interfaces/__init__.py:161
+#: src/pyams_content/shared/view/interfaces/__init__.py:177
 msgid "Specify how selected references are included into view results"
 msgstr ""
 "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:166
+#: src/pyams_content/shared/view/interfaces/__init__.py:182
 msgid "Exclude context?"
 msgstr "Exclure le contexte ?"
 
-#: src/pyams_content/shared/view/interfaces/__init__.py:167
+#: src/pyams_content/shared/view/interfaces/__init__.py:183
 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:178
+#: src/pyams_content/shared/view/interfaces/__init__.py:194
 msgid "Select context tags?"
 msgstr "Tags du contexte ?"
 
-#: src/pyams_content/shared/view/interfaces/__init__.py:179
+#: src/pyams_content/shared/view/interfaces/__init__.py:195
 msgid "If 'yes', tags will be extracted from context"
 msgstr ""
 "Si 'oui', les tags associés au contexte d'application de la vue seront "
 "automatiquement sélectionnés"
 
-#: src/pyams_content/shared/view/interfaces/__init__.py:183
+#: src/pyams_content/shared/view/interfaces/__init__.py:199
 msgid "Other tags"
 msgstr "Autres tags"
 
-#: src/pyams_content/shared/view/interfaces/__init__.py:199
+#: src/pyams_content/shared/view/interfaces/__init__.py:215
 msgid "Select context themes?"
 msgstr "Thèmes du contexte ?"
 
-#: src/pyams_content/shared/view/interfaces/__init__.py:200
+#: src/pyams_content/shared/view/interfaces/__init__.py:216
 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:204
+#: src/pyams_content/shared/view/interfaces/__init__.py:220
 msgid "Other themes"
 msgstr "Autres thèmes"
 
-#: src/pyams_content/shared/view/interfaces/__init__.py:220
+#: src/pyams_content/shared/view/interfaces/__init__.py:236
 msgid "Select context collections?"
 msgstr "Collections du contexte ?"
 
-#: src/pyams_content/shared/view/interfaces/__init__.py:221
+#: src/pyams_content/shared/view/interfaces/__init__.py:237
 msgid "If 'yes', collections will be extracted from context"
 msgstr ""
 "Si 'oui', les collections associées au contexte d'application de la vue "
 "seront automatiquement sélectionnés"
 
-#: src/pyams_content/shared/view/interfaces/__init__.py:225
+#: src/pyams_content/shared/view/interfaces/__init__.py:241
 msgid "Other collections"
 msgstr "Autres collections"
 
-#: src/pyams_content/shared/imagemap/paragraph.py:99
+#: src/pyams_content/shared/imagemap/paragraph.py:88
 msgid "no selected image map"
 msgstr "aucune image cliquable sélectionnée"
 
-#: src/pyams_content/shared/imagemap/paragraph.py:105
+#: src/pyams_content/shared/imagemap/paragraph.py:94
 #, python-format
 msgid "image map '{0}' can't be found"
 msgstr "l'image cliquable '{0}' est introuvable"
 
-#: src/pyams_content/shared/imagemap/paragraph.py:113
+#: src/pyams_content/shared/imagemap/paragraph.py:102
 #, python-format
 msgid "image map '{0}' is not published"
 msgstr "l'image cliquable '{0}' n'est pas publiée"
@@ -3940,7 +4026,7 @@
 #: src/pyams_content/shared/imagemap/zmi/container.py:65
 #: src/pyams_content/shared/imagemap/interfaces/__init__.py:71
 msgid "Image map areas"
-msgstr "Zones cliquables de l'images"
+msgstr "Zones cliquables de l'image"
 
 #: src/pyams_content/shared/imagemap/zmi/container.py:140
 #: src/pyams_content/shared/imagemap/interfaces/__init__.py:50
@@ -4010,7 +4096,7 @@
 msgstr "Liste des zones cliquables définies sur l'image"
 
 #: src/pyams_content/shared/imagemap/interfaces/__init__.py:96
-#: src/pyams_content/features/alert/interfaces.py:73
+#: src/pyams_content/features/alert/interfaces.py:69
 #: src/pyams_content/features/menu/interfaces/__init__.py:68
 msgid "Internal reference"
 msgstr "Référence interne"
@@ -4025,7 +4111,7 @@
 msgid "Image map template"
 msgstr "Mode de rendu"
 
-#: src/pyams_content/shared/site/folder.py:59
+#: src/pyams_content/shared/site/folder.py:62
 msgid "Site folder"
 msgstr "Rubrique"
 
@@ -4038,39 +4124,43 @@
 msgid "Site manager"
 msgstr "Site"
 
-#: src/pyams_content/shared/site/zmi/folder.py:61
+#: src/pyams_content/shared/site/zmi/folder.py:62
 msgid "Add site folder..."
 msgstr "Ajouter une rubrique"
 
-#: src/pyams_content/shared/site/zmi/folder.py:93
+#: src/pyams_content/shared/site/zmi/folder.py:94
 msgid "Add site folder"
 msgstr "Ajout d'une rubrique"
 
-#: src/pyams_content/shared/site/zmi/folder.py:160
+#: src/pyams_content/shared/site/zmi/folder.py:161
 msgid "Site folder management"
 msgstr "Gérer cette rubrique"
 
-#: src/pyams_content/shared/site/zmi/folder.py:188
+#: 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:71
+#: src/pyams_content/shared/site/zmi/folder.py:208
+msgid "Navigation properties"
+msgstr "Propriétés de navigation"
+
+#: src/pyams_content/shared/site/zmi/folder.py:72
 #: 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:74
+#: src/pyams_content/shared/site/zmi/folder.py:75
 #: src/pyams_content/shared/site/zmi/__init__.py:72
-#: src/pyams_content/shared/site/zmi/link.py:66
+#: src/pyams_content/shared/site/zmi/link.py:65
 msgid "Parent"
 msgstr "Niveau parent"
 
-#: src/pyams_content/shared/site/zmi/folder.py:75
-#: src/pyams_content/shared/site/zmi/link.py:67
+#: src/pyams_content/shared/site/zmi/folder.py:76
+#: src/pyams_content/shared/site/zmi/link.py:66
 msgid "Folder's parent"
 msgstr "Niveau de rattachement de cette rubrique"
 
-#: src/pyams_content/shared/site/zmi/folder.py:153
+#: src/pyams_content/shared/site/zmi/folder.py:154
 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 "
@@ -4092,15 +4182,15 @@
 msgid "Topic's parent"
 msgstr "Niveau parent"
 
-#: src/pyams_content/shared/site/zmi/link.py:57
+#: src/pyams_content/shared/site/zmi/link.py:56
 msgid "Rent content..."
 msgstr "Lier un contenu"
 
-#: src/pyams_content/shared/site/zmi/link.py:77
+#: src/pyams_content/shared/site/zmi/link.py:76
 msgid "Rent existing content"
 msgstr "Lier un contenu existant"
 
-#: src/pyams_content/shared/site/zmi/link.py:135
+#: src/pyams_content/shared/site/zmi/link.py:134
 msgid "Edit content link properties"
 msgstr "Propriétés du lien"
 
@@ -4180,57 +4270,91 @@
 msgid "A site manager is already registered with this name!!"
 msgstr "Un site est déjà inscrit dans le registre avec ce nom !"
 
-#: src/pyams_content/shared/site/interfaces/__init__.py:96
+#: src/pyams_content/shared/site/interfaces/__init__.py:121
 msgid "Topic"
 msgstr "Article"
 
-#: src/pyams_content/shared/site/interfaces/__init__.py:59
+#: src/pyams_content/shared/site/interfaces/__init__.py:42
+msgid "Redirect to first visible sub-folder or content"
+msgstr "Re-diriger vers le premier contenu publié"
+
+#: src/pyams_content/shared/site/interfaces/__init__.py:43
+msgid "Use presentation template"
+msgstr "Afficher le modèle de présentation de la rubrique"
+
+#: src/pyams_content/shared/site/interfaces/__init__.py:72
 msgid "Heading"
 msgstr "Chapô"
 
-#: src/pyams_content/shared/site/interfaces/__init__.py:60
+#: src/pyams_content/shared/site/interfaces/__init__.py:73
 msgid "Heading displayed according to presentation template"
 msgstr ""
 "Ce chapô pourra être affiché ou non en fonction du modèle de présentation "
 "retenu"
 
-#: src/pyams_content/shared/site/interfaces/__init__.py:63
+#: src/pyams_content/shared/site/interfaces/__init__.py:80
+msgid "Visible in folders list"
+msgstr "Visible dans la liste des rubriques ?"
+
+#: src/pyams_content/shared/site/interfaces/__init__.py:81
+msgid "If 'no', folder will not be displayed into folders list"
+msgstr ""
+"Si 'non', cette rubrique ne sera pas affichée dans la liste des rubriques "
+"affichée par un composant de navigation"
+
+#: src/pyams_content/shared/site/interfaces/__init__.py:85
+#: src/pyams_content/shared/site/interfaces/__init__.py:139
 msgid "Navigation title"
 msgstr "Titre de navigation"
 
-#: src/pyams_content/shared/site/interfaces/__init__.py:64
-msgid "Title displayed in navigation items"
+#: src/pyams_content/shared/site/interfaces/__init__.py:86
+msgid ""
+"Folder's title displayed in navigation pages; original title will be used if "
+"none is specified"
 msgstr ""
-"Libellé utilisé en lieu et place du titre dans les blocs de navigation, "
-"notamment dans les pages carrefours"
-
-#: src/pyams_content/shared/site/interfaces/__init__.py:115
-msgid "Content title, as shown in front-office"
-msgstr "Titre présenté aux internautes"
-
-#: src/pyams_content/shared/site/interfaces/__init__.py:119
+"Titre de substitution affiché dans les composants de navigation ; si rien "
+"n'est indiqué, le titre original de la rubrique sera utilisé"
+
+#: src/pyams_content/shared/site/interfaces/__init__.py:90
+msgid "Navigation mode"
+msgstr "Mode de navigation"
+
+#: src/pyams_content/shared/site/interfaces/__init__.py:91
+msgid "Folder behaviour when navigating to folder URL"
+msgstr "Comportement à adopter lorsqu'un internaute accède à cette rubrique"
+
+#: src/pyams_content/shared/site/interfaces/__init__.py:140
+msgid ""
+"Alternate content's title displayed in navigation pages; original title will "
+"be used if none is specified"
+msgstr ""
+"Titre de substitution affiché dans les composants de navigation ; si rien "
+"n'est indiqué, le titre original du contenu référencé sera utilisé"
+
+#: src/pyams_content/shared/site/interfaces/__init__.py:145
 msgid "If 'no', link is not visible"
-msgstr "Si 'non', le lien ne sera pas visible"
-
-#: src/pyams_content/shared/logo/paragraph.py:96
+msgstr ""
+"Si 'non', le lien ne sera pas visible même si le contenu référencé est publié"
+
+#: src/pyams_content/shared/logo/paragraph.py:95
 msgid "no selected logo"
 msgstr "aucun logo sélectionné"
 
-#: src/pyams_content/shared/logo/paragraph.py:102
+#: src/pyams_content/shared/logo/paragraph.py:101
 #, python-format
 msgid "logo '{0}' can't be found"
 msgstr "le logo '{0}' est introuvable"
 
-#: src/pyams_content/shared/logo/paragraph.py:110
+#: src/pyams_content/shared/logo/paragraph.py:109
 #, python-format
 msgid "logo '{0}' is not published"
 msgstr "le logo '{0}' n'est pas publié"
 
-#: src/pyams_content/shared/logo/__init__.py:68
+#: src/pyams_content/shared/logo/__init__.py:69
 msgid "no image defined"
 msgstr "aucune image définie"
 
-#: src/pyams_content/shared/logo/__init__.py:71
+#: src/pyams_content/shared/logo/__init__.py:72
 msgid "no URL defined"
 msgstr "aucune URL définie"
 
@@ -4294,16 +4418,16 @@
 msgid "Logos template"
 msgstr "Mode de rendu"
 
-#: src/pyams_content/shared/blog/zmi/__init__.py:52
+#: src/pyams_content/shared/blog/zmi/__init__.py:53
 msgid "This blog post"
 msgstr "Cet article"
 
-#: src/pyams_content/shared/blog/zmi/__init__.py:71
-#: src/pyams_content/shared/blog/zmi/__init__.py:81
+#: src/pyams_content/shared/blog/zmi/__init__.py:72
+#: src/pyams_content/shared/blog/zmi/__init__.py:82
 msgid "Add blog post"
 msgstr "Ajouter un article"
 
-#: src/pyams_content/shared/blog/zmi/__init__.py:62
+#: src/pyams_content/shared/blog/zmi/__init__.py:63
 #, python-format
 msgid "Blog post « {title} »"
 msgstr "Article de blog « {title} »"
@@ -4353,7 +4477,7 @@
 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:68
+#: src/pyams_content/root/__init__.py:69
 msgid "Site root"
 msgstr "Racine du site"
 
@@ -4377,7 +4501,7 @@
 msgid "Given element name doesn't exist!"
 msgstr "Le nom de l'élément indiqué n'existe pas !"
 
-#: src/pyams_content/root/zmi/search.py:155
+#: src/pyams_content/root/zmi/search.py:160
 msgid "Content types"
 msgstr "Types de contenus"
 
@@ -4858,14 +4982,14 @@
 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:70
+#: src/pyams_content/features/renderer/zmi/__init__.py:72
 #: src/pyams_content/features/renderer/zmi/templates/renderer-input.pt:4
 msgid "Edit renderer properties"
-msgstr "Propriétés du mode de rendu"
-
-#: src/pyams_content/features/renderer/skin/__init__.py:67
+msgstr "Propriétés de ce mode de rendu"
+
+#: src/pyams_content/features/renderer/skin/__init__.py:71
 msgid "Hidden content"
-msgstr "Contenu non affiché"
+msgstr "NON affiché"
 
 #: src/pyams_content/features/checker/interfaces.py:27
 #, python-format
@@ -4888,7 +5012,7 @@
 
 #: src/pyams_content/features/checker/zmi/__init__.py:82
 msgid "No checker available. This content is clean!"
-msgstr "Pas de vérificateur disponible. Ce contenu est propre !"
+msgstr "Pas de vérificateur disponible pour ce contenu."
 
 #: src/pyams_content/features/checker/zmi/__init__.py:78
 #, python-format
@@ -4908,8 +5032,12 @@
 msgid "preview"
 msgstr "aperçu"
 
+#: src/pyams_content/features/alert/interfaces.py:39
+msgid "Alert"
+msgstr "Alerte"
+
 #: src/pyams_content/features/alert/interfaces.py:40
-msgid "Success"
+msgid "End of alert"
 msgstr "Levée d'alerte"
 
 #: src/pyams_content/features/alert/interfaces.py:41
@@ -4921,8 +5049,8 @@
 msgstr "Avertissement"
 
 #: src/pyams_content/features/alert/interfaces.py:43
-msgid "Danger"
-msgstr "Danger !"
+msgid "Recommendation"
+msgstr "Recommandation"
 
 #: src/pyams_content/features/alert/interfaces.py:55
 msgid "Is this alert visible in front-office?"
@@ -4936,26 +5064,16 @@
 msgid "Alert gravity will affect rendered alert style"
 msgstr "Le niveau de gravité choisi affectera le style de rendu de l'alerte"
 
-#. Default: Heading
 #: src/pyams_content/features/alert/interfaces.py:65
-#: src/pyams_content/features/alert/zmi/container.py:157
-msgid "alert-header"
-msgstr "En-tête"
+#: src/pyams_content/features/alert/zmi/container.py:145
+msgid "Message"
+msgstr "Message"
 
 #: src/pyams_content/features/alert/interfaces.py:66
-msgid "Short alert header (Alert, Important...)"
-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:169
-msgid "Message"
-msgstr "Message"
+msgid "Alert message"
+msgstr "Le message d'alerte doit être assez court et explicite"
 
 #: src/pyams_content/features/alert/interfaces.py:70
-msgid "Alert message"
-msgstr "Le message d'alerte doit être assez court et explicite"
-
-#: src/pyams_content/features/alert/interfaces.py:74
 msgid ""
 "Internal link target reference. You can search a reference using '+' "
 "followed by internal number, of by entering text matching content title."
@@ -4964,31 +5082,31 @@
 "mots de son titre, ou par son numéro interne (précédé d'un '+') ; le titre "
 "d'origine peut être modifié en utilisant le titre de substitution."
 
-#: src/pyams_content/features/alert/interfaces.py:86
+#: src/pyams_content/features/alert/interfaces.py:75
 msgid "Display start date"
 msgstr "Date d'affichage"
 
-#: src/pyams_content/features/alert/interfaces.py:87
+#: src/pyams_content/features/alert/interfaces.py:76
 msgid "First date at which alert should be displayed"
 msgstr ""
 "Première date à laquelle l'alerte sera affichée. Laissez la zone vide pour "
 "qu'elle soit affichée immédiatement."
 
-#: src/pyams_content/features/alert/interfaces.py:90
+#: src/pyams_content/features/alert/interfaces.py:79
 msgid "Display end date"
 msgstr "Date de retrait"
 
-#: src/pyams_content/features/alert/interfaces.py:91
+#: src/pyams_content/features/alert/interfaces.py:80
 msgid "Last date at which alert should be displayed"
 msgstr ""
 "Dernière date à laquelle l'alerte sera affichée. Laissez la zone vide pour "
 "qu'elle ne soit pas retirée."
 
-#: src/pyams_content/features/alert/interfaces.py:94
+#: src/pyams_content/features/alert/interfaces.py:83
 msgid "Maximum interval"
 msgstr "Intervalle d'affichage"
 
-#: src/pyams_content/features/alert/interfaces.py:95
+#: src/pyams_content/features/alert/interfaces.py:84
 msgid ""
 "Maximum interval between alert displays on a given device, given in hours; "
 "set to 0 to always display the alert"
@@ -5013,14 +5131,256 @@
 msgid "Alerts"
 msgstr "Alertes"
 
-#: src/pyams_content/features/alert/zmi/container.py:191
+#: src/pyams_content/features/alert/zmi/container.py:167
 msgid "Alert list"
 msgstr "Liste des alertes"
 
-#: src/pyams_content/features/alert/zmi/container.py:90
+#: src/pyams_content/features/alert/zmi/container.py:97
 msgid "No currently defined alert."
 msgstr "Aucune alerte n'est définie actuellement."
 
+#: src/pyams_content/features/redirect/container.py:81
+msgid "not matching"
+msgstr "pas de correspondance"
+
+#: src/pyams_content/features/redirect/zmi/__init__.py:50
+msgid "Add rule"
+msgstr "Ajouter une règle"
+
+#: src/pyams_content/features/redirect/zmi/__init__.py:63
+msgid "Add new redirection rule"
+msgstr "Ajout d'une règle de redirection"
+
+#: src/pyams_content/features/redirect/zmi/__init__.py:88
+msgid "Edit redirection rule properties"
+msgstr "Propriétés de la règle de redirection"
+
+#: src/pyams_content/features/redirect/zmi/__init__.py:109
+msgid ""
+"URL pattern and target URL are defined by *regular expressions* (see |"
+"regexp|).\n"
+"    \n"
+"In URL pattern, you can use any valid regular expression element, notably:\n"
+"\n"
+"- « .* » to match any list of characters \n"
+"\n"
+"- « ( ) » to \"memorize\" parts of the URL which can be replaced into target "
+"URL\n"
+"\n"
+"- special characters (like \"+\") must be escaped with an « \\\\ ».\n"
+"\n"
+"In target URL, memorized parts can be reused using « \\\\1 », « \\\\2 » and "
+"so on, where given number is\n"
+"the order of the matching pattern element.\n"
+"\n"
+".. |regexp| raw:: html\n"
+"\n"
+"    <a href=\"https://docs.python.org/3/library/re.html\" target=\"_blank"
+"\">Python Regular Expressions</a>\n"
+msgstr ""
+"Le schéma d'URL et l'URL cible sont définis en tant que « expressions "
+"rationelles » (voir |regexp|).\n"
+"\n"
+"Dans le schéma d'URL utilisé pour identifier les requêtes en entrée, vous "
+"pouvez utiliser tout élément d'une expression rationnelle valide, "
+"notamment :\n"
+"\n"
+"- « .* » pour rechercher n'importe quelle suite de caractères\n"
+"\n"
+"- « ^ » et « $ » pour identifier le début ou la fin de l'URL\n"
+"\n"
+"- « ( ) » pour \"mémoriser\" certains éléments de l'URL qui pourront être "
+"repris dans l'URL cible\n"
+"\n"
+"- les caractères spéciaux (comme les \"+\") doivent être protégés par un "
+"caractère « \\\\ ».\n"
+"\n"
+"For exemple : dans le schéma « ^/.*?oid=([a-z0-9]+)$ », toute URL contenant "
+"un paramètre \"oid\" composé de minuscules et/ou de chiffres sera mémorisé "
+"pour pouvoir être réutilisé dans l'URL cible.\n"
+"\n"
+"Dans l'URL cible, les éléments mémorisés peuvent être réutilisés en "
+"utilisant une expression comme « \\\\1 », « \\\\2 » (et ainsi de suite), le "
+"chiffre indiquant la position de l'élément dans la liste des éléments "
+"mémorisés.\n"
+"\n"
+".. |regexp| raw:: html\n"
+"\n"
+"    <a href=\"https://docs.python.org/fr/3/library/re.html\" target=\"_blank"
+"\">Expressions rationnelles en Python</a>\n"
+
+#: src/pyams_content/features/redirect/zmi/container.py:67
+msgid "Redirections"
+msgstr "Redirections"
+
+#: src/pyams_content/features/redirect/zmi/container.py:161
+msgid "Enable/disable rule"
+msgstr "Activer/désactiver la règle"
+
+#: src/pyams_content/features/redirect/zmi/container.py:188
+msgid "Chain/unchain rule"
+msgstr "Enchaîner la règle avec la suivante"
+
+#: src/pyams_content/features/redirect/zmi/container.py:210
+#: src/pyams_content/features/redirect/zmi/container.py:366
+#: src/pyams_content/features/redirect/interfaces/__init__.py:56
+msgid "URL pattern"
+msgstr "Schéma d'URL"
+
+#: src/pyams_content/features/redirect/zmi/container.py:220
+msgid "Target"
+msgstr "Cible"
+
+#: src/pyams_content/features/redirect/zmi/container.py:246
+msgid "Redirections list"
+msgstr "Liste des règles de redirection"
+
+#: src/pyams_content/features/redirect/zmi/container.py:261
+msgid "Redirection rules"
+msgstr "Règles de redirection"
+
+#: src/pyams_content/features/redirect/zmi/container.py:262
+msgid ""
+"Redirection rules are use to handle redirections responses when a request "
+"generates \n"
+"a famous « 404 NotFound » error.\n"
+"\n"
+"Redirections are particularly useful when you are migrating from a previous "
+"site and don't want to lose \n"
+"your SEO.\n"
+"\n"
+"You can define a set of rules which will be applied to every \"NotFound\" "
+"request; rules are based on \n"
+"regular expressions which are applied to input URL: if the rule is \"matching"
+"\", the target URL is rewritten\n"
+"and a \"Redirect\" response is send.\n"
+"\n"
+"You can chain rules together: when a rule is chained, it's rewritten URL is "
+"passed as input URL to the \n"
+"next rule, until a matching rule is found.\n"
+msgstr ""
+"Les règles de redirection sont utilisées pour transmettre des réponses de "
+"redirection au lieu de la fameuse erreur « 404 - Page non trouvée ».\n"
+"\n"
+"La gestion des redirections est particulièrement importante en phase de "
+"migration d'un site web, pour éviter les liens cassés, ne pas perdre votre "
+"référencement et faciliter la mise à jour des moteurs de recherche.\n"
+"\n"
+"Vous pouvez définir un ensemble de règles qui seront appliquées dès lors "
+"qu'une requête adressée au serveur génère une erreur de page non trouvée ; "
+"les règles sont basées sur des expressions rationnelles que l'on applique à "
+"l'URL de la requête reçue : si la règle correspond, l'URL est réécrite et "
+"une réponse de redirection vers cette nouvelle URL est renvoyée.\n"
+"\n"
+"Vous pouvez également enchaîner les règles : lorsqu'une règle est \"chaînée"
+"\", la nouvelle URL qu'elle génère est passée aux règles suivantes, jusqu'à "
+"ce qu'une règle s'applique à cette nouvelle URL.\n"
+
+#: src/pyams_content/features/redirect/zmi/container.py:288
+msgid "Test"
+msgstr "Tester !"
+
+#: src/pyams_content/features/redirect/zmi/container.py:323
+msgid "Test redirection rules"
+msgstr "Test des règles de redirection"
+
+#: src/pyams_content/features/redirect/zmi/container.py:301
+msgid "Test URL"
+msgstr "URL à tester"
+
+#: src/pyams_content/features/redirect/zmi/container.py:304
+msgid "Check inactive rules?"
+msgstr "Tester les règles inactive ?"
+
+#: src/pyams_content/features/redirect/zmi/container.py:305
+msgid "If 'yes', inactive rules will also be tested"
+msgstr "Si 'oui', les règles inactives seront également testées"
+
+#: src/pyams_content/features/redirect/zmi/container.py:313
+msgid "Close"
+msgstr "Fermer"
+
+#: src/pyams_content/features/redirect/zmi/container.py:314
+msgid "Test rules"
+msgstr "Tester cette URL"
+
+#: src/pyams_content/features/redirect/zmi/container.py:123
+msgid "No currently defined redirection rule."
+msgstr "Aucune règle de redirection n'est définie actuellement."
+
+#: src/pyams_content/features/redirect/zmi/container.py:371
+msgid "No matching rule!"
+msgstr "Aucune règle ne correspond !"
+
+#: src/pyams_content/features/redirect/zmi/container.py:365
+msgid "Input URL"
+msgstr "URL en entrée"
+
+#: src/pyams_content/features/redirect/zmi/container.py:367
+msgid "Output URL"
+msgstr "URL générée"
+
+#: src/pyams_content/features/redirect/interfaces/__init__.py:39
+msgid "Active rule?"
+msgstr "Règle active ?"
+
+#: src/pyams_content/features/redirect/interfaces/__init__.py:40
+msgid "If 'no', selected rule is inactive"
+msgstr "Si 'non', la règle est inactive"
+
+#: src/pyams_content/features/redirect/interfaces/__init__.py:44
+msgid "Chained rule?"
+msgstr "Règle chaînée ?"
+
+#: src/pyams_content/features/redirect/interfaces/__init__.py:45
+msgid ""
+"If 'no', and if this rule is matching received request URL, the rule returns "
+"a redirection response; otherwise, the rule just rewrites the input URL "
+"which is forwarded to the next rule"
+msgstr ""
+"Si 'non', et si cette règle correspond à l'URL reçue en entrée, une réponde "
+"de redirection est renvoyée directement ; dans le cas contraire, l'URL "
+"générée par cette règle est passée en entrée de la règle suivante"
+
+#: src/pyams_content/features/redirect/interfaces/__init__.py:51
+msgid "Permanent redirect?"
+msgstr "Redirection permanente ?"
+
+#: src/pyams_content/features/redirect/interfaces/__init__.py:52
+msgid "Define if this redirection should be permanent or temporary"
+msgstr ""
+"Indique si cette redirection doit être considérée comme permanente ou "
+"temporaire"
+
+#: src/pyams_content/features/redirect/interfaces/__init__.py:57
+msgid "Regexp pattern of matching URLs for this redirection rule"
+msgstr "Modèle de l'URL d'origine de cette règle de redirection"
+
+#: src/pyams_content/features/redirect/interfaces/__init__.py:62
+msgid "Internal redirection target"
+msgstr "Redirection interne"
+
+#: src/pyams_content/features/redirect/interfaces/__init__.py:63
+msgid ""
+"Internal redirection reference. You can search a reference using '+' "
+"followed by internal number, of by entering text matching content title."
+msgstr ""
+"Référence interne vers une cible de redirection. Vous pouvez la rechercher "
+"par des mots de son titre, ou par son numéro interne (précédé d'un '+')"
+
+#: src/pyams_content/features/redirect/interfaces/__init__.py:69
+msgid "URL to which source URL should be redirected"
+msgstr "URL vers laquelle l'URL d'origine doit être redirigée"
+
+#: src/pyams_content/features/redirect/interfaces/__init__.py:75
+msgid "You can only provide an internal reference OR a target URL"
+msgstr ""
+"Vous ne pouvez fournir qu'une référence interne OU une URL de redirection !"
+
+#: src/pyams_content/features/redirect/interfaces/__init__.py:77
+msgid "You must provide an internal reference OR a target URL"
+msgstr "Vous devez fournir une référence interne OU une URL de redirection !"
+
 #: src/pyams_content/features/menu/zmi/__init__.py:81
 msgid "Add menu..."
 msgstr "Ajouter un menu"
@@ -5037,7 +5397,7 @@
 msgid "Menu was correctly added."
 msgstr "Le menu a été ajouté."
 
-#: src/pyams_content/features/menu/zmi/__init__.py:388
+#: src/pyams_content/features/menu/zmi/__init__.py:395
 msgid "Link was correctly added."
 msgstr "Le lien a été ajouté."
 
@@ -5045,11 +5405,11 @@
 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
+#: src/pyams_content/features/menu/portlet/navigation/simple.py:67
 msgid "Simple navigation"
 msgstr "Navigation à un niveau"
 
-#: src/pyams_content/features/menu/portlet/navigation/double.py:68
+#: src/pyams_content/features/menu/portlet/navigation/double.py:67
 msgid "Double navigation"
 msgstr "Navigation à deux niveaux"
 
@@ -5061,8 +5421,8 @@
 msgid "Navigation menus"
 msgstr "Menus de navigation"
 
-#: src/pyams_content/features/menu/portlet/navigation/zmi/templates/double-preview.pt:15
-#: src/pyams_content/features/menu/portlet/navigation/zmi/templates/simple-preview.pt:12
+#: src/pyams_content/features/menu/portlet/navigation/zmi/templates/double-preview.pt:14
+#: src/pyams_content/features/menu/portlet/navigation/zmi/templates/simple-preview.pt:11
 msgid "Link has no illustration"
 msgstr "Le lien n'a pas d'illustration"
 
@@ -5071,16 +5431,6 @@
 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:64
 msgid "Menu title"
 msgstr "Libellé"
@@ -5115,13 +5465,13 @@
 
 #: src/pyams_content/features/footer/zmi/__init__.py:220
 msgid "Footer renderer settings"
-msgstr "Propriétés du mode de rendu"
+msgstr "Propriétés de ce mode de rendu"
 
 #: 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:84
+#: src/pyams_content/features/footer/skin/__init__.py:94
 msgid "Hidden footer"
 msgstr "Ne pas afficher de pied de pages"
 
@@ -5347,16 +5697,69 @@
 
 #: src/pyams_content/features/header/zmi/__init__.py:229
 msgid "Header renderer settings"
-msgstr "Propriétés du mode de rendu"
+msgstr "Propriétés de ce mode de rendu"
 
 #: 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:90
+#: src/pyams_content/features/header/skin/__init__.py:100
 msgid "Hidden header"
 msgstr "Ne pas afficher d'en-tête de pages"
 
+#~ msgid "Form header"
+#~ msgstr "En-tête du formulaire"
+
+#~ msgid "Title displayed in navigation items"
+#~ msgstr ""
+#~ "Libellé utilisé en lieu et place du titre dans les blocs de navigation, "
+#~ "notamment dans les pages carrefours"
+
+#~ msgid "Content title, as shown in front-office"
+#~ msgstr "Titre présenté aux internautes"
+
+#~ msgid "Success"
+#~ msgstr "Levée d'alerte"
+
+#~ msgid "Danger"
+#~ msgstr "Danger !"
+
+#~ msgid "alert-header"
+#~ msgstr "En-tête"
+
+#~ msgid "Short alert header (Alert, Important...)"
+#~ msgstr "En-tête de l'alerte (« Alerte », « Important », « Prudence »...)"
+
+#~ msgid "Header..."
+#~ msgstr "Chapô"
+
+#~ msgid "Add new header paragraph"
+#~ msgstr "Ajout d'un chapô"
+
+#~ msgid "Your favorites"
+#~ msgstr "Mes favoris"
+
+#~ msgid "Rewrite to another internal reference or URL"
+#~ msgstr "Rediriger vers une autre référence ou URL interne"
+
+#~ msgid "Redirect to another external URL"
+#~ msgstr "Rediriger vers une URL externe"
+
+#~ msgid "Return content in proxy mode without redirection"
+#~ msgstr "Charger le contenu ciblé sans redirection"
+
+#~ msgid "Redirect mode"
+#~ msgstr "Mode de redirection"
+
+#~ msgid "Mode of redirection for this URL pattern"
+#~ msgstr "Mode de redirection utilisé par cette règle"
+
+#~ msgid "Subtitle"
+#~ msgstr "Sous-titre"
+
+#~ msgid "Portlet subtitle"
+#~ msgstr "Sous-titre du composant"
+
 #~ msgid "Image associated to this data type"
 #~ msgstr "Image associée à ce type"
 
@@ -5651,9 +6054,6 @@
 #~ msgid "Visible gallery?"
 #~ msgstr "Galerie visible ?"
 
-#~ msgid "If 'no', this gallery won't be displayed in front office"
-#~ msgstr "Si 'non', cette galerie ne sera pas affichée en front-office"
-
 #~ msgid "Contained galleries"
 #~ msgstr "Galeries d'images"
 
--- a/src/pyams_content/locales/pyams_content.pot	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/locales/pyams_content.pot	Thu Sep 06 11:27:55 2018 +0200
@@ -6,7 +6,7 @@
 msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE 1.0\n"
-"POT-Creation-Date: 2018-07-17 14:54+0200\n"
+"POT-Creation-Date: 2018-09-06 08:55+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"
@@ -164,15 +164,15 @@
 msgid "Comments relatives to author's rights management"
 msgstr ""
 
-#: ./src/pyams_content/component/gallery/zmi/templates/gallery-medias.pt:11
+#: ./src/pyams_content/component/gallery/zmi/templates/gallery-medias.pt:12
 msgid "Gallery medias"
 msgstr ""
 
-#: ./src/pyams_content/component/gallery/zmi/templates/gallery-medias.pt:17
+#: ./src/pyams_content/component/gallery/zmi/templates/gallery-medias.pt:18
 msgid "Download medias"
 msgstr ""
 
-#: ./src/pyams_content/component/gallery/zmi/templates/gallery-medias.pt:41
+#: ./src/pyams_content/component/gallery/zmi/templates/gallery-medias.pt:42
 msgid "Zoom image"
 msgstr ""
 
@@ -213,13 +213,9 @@
 #: ./src/pyams_content/component/gallery/interfaces/__init__.py:58
 #: ./src/pyams_content/component/gallery/interfaces/__init__.py:101
 #: ./src/pyams_content/component/extfile/interfaces/__init__.py:40
-#: ./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:48
 #: ./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:154
-#: ./src/pyams_content/shared/form/interfaces/__init__.py:66
+#: ./src/pyams_content/shared/form/interfaces/__init__.py:64
 msgid "Description"
 msgstr ""
 
@@ -271,16 +267,16 @@
 
 #: ./src/pyams_content/component/gallery/interfaces/__init__.py:97
 #: ./src/pyams_content/component/keynumber/portlet/interfaces/__init__.py:31
-#: ./src/pyams_content/component/paragraph/zmi/milestone.py:232
-#: ./src/pyams_content/component/paragraph/zmi/container.py:252
+#: ./src/pyams_content/component/paragraph/zmi/milestone.py:239
+#: ./src/pyams_content/component/paragraph/zmi/container.py:270
 #: ./src/pyams_content/component/paragraph/interfaces/milestone.py:45
-#: ./src/pyams_content/component/links/zmi/reverse.py:73
+#: ./src/pyams_content/shared/common/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/common/zmi/templates/advanced-search.pt:200
 #: ./src/pyams_content/shared/view/portlet/interfaces.py:56
 #: ./src/pyams_content/shared/imagemap/zmi/container.py:123
-#: ./src/pyams_content/shared/site/zmi/folder.py:70
-#: ./src/pyams_content/root/zmi/templates/advanced-search.pt:188
+#: ./src/pyams_content/shared/site/zmi/folder.py:71
+#: ./src/pyams_content/root/zmi/templates/advanced-search.pt:197
 #: ./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
@@ -398,7 +394,6 @@
 #: ./src/pyams_content/component/extfile/interfaces/__init__.py:36
 #: ./src/pyams_content/component/links/interfaces/__init__.py:35
 #: ./src/pyams_content/shared/imagemap/interfaces/__init__.py:55
-#: ./src/pyams_content/shared/site/interfaces/__init__.py:114
 msgid "Alternate title"
 msgstr ""
 
@@ -407,9 +402,7 @@
 msgstr ""
 
 #: ./src/pyams_content/component/extfile/interfaces/__init__.py:41
-#: ./src/pyams_content/component/paragraph/interfaces/video.py:49
 #: ./src/pyams_content/component/paragraph/interfaces/audio.py:49
-#: ./src/pyams_content/component/video/interfaces/__init__.py:49
 msgid "File description displayed by front-office template"
 msgstr ""
 
@@ -462,49 +455,64 @@
 msgstr ""
 
 #: ./src/pyams_content/component/keynumber/__init__.py:189
-#: ./src/pyams_content/component/keynumber/zmi/__init__.py:199
+#: ./src/pyams_content/component/keynumber/zmi/__init__.py:212
 #: ./src/pyams_content/component/keynumber/portlet/zmi/__init__.py:74
-#: ./src/pyams_content/component/paragraph/interfaces/keynumber.py:29
+#: ./src/pyams_content/component/keynumber/interfaces/__init__.py:83
 msgid "Key numbers"
 msgstr ""
 
+#: ./src/pyams_content/component/keynumber/zmi/paragraph.py:53
+msgid "Key numbers..."
+msgstr ""
+
+#: ./src/pyams_content/component/keynumber/zmi/paragraph.py:66
+msgid "Add new key number paragraph"
+msgstr ""
+
+#: ./src/pyams_content/component/keynumber/zmi/paragraph.py:94
+msgid "Edit key number paragraph properties"
+msgstr ""
+
 #. Default: Header
-#: ./src/pyams_content/component/keynumber/zmi/__init__.py:147
-#: ./src/pyams_content/component/keynumber/interfaces/__init__.py:44
+#: ./src/pyams_content/component/keynumber/zmi/__init__.py:160
+#: ./src/pyams_content/component/keynumber/interfaces/__init__.py:45
 msgid "key-number-label"
 msgstr ""
 
-#: ./src/pyams_content/component/keynumber/zmi/__init__.py:159
-#: ./src/pyams_content/component/keynumber/interfaces/__init__.py:49
+#: ./src/pyams_content/component/keynumber/zmi/__init__.py:172
+#: ./src/pyams_content/component/keynumber/interfaces/__init__.py:50
 msgid "Number"
 msgstr ""
 
 #. Default: Unit
-#: ./src/pyams_content/component/keynumber/zmi/__init__.py:168
-#: ./src/pyams_content/component/keynumber/interfaces/__init__.py:53
+#: ./src/pyams_content/component/keynumber/zmi/__init__.py:181
+#: ./src/pyams_content/component/keynumber/interfaces/__init__.py:54
 msgid "key-number-unit"
 msgstr ""
 
-#: ./src/pyams_content/component/keynumber/zmi/__init__.py:180
-#: ./src/pyams_content/component/keynumber/interfaces/__init__.py:57
-#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:268
+#: ./src/pyams_content/component/keynumber/zmi/__init__.py:193
+#: ./src/pyams_content/component/keynumber/interfaces/__init__.py:58
+#: ./src/pyams_content/component/illustration/interfaces/__init__.py:64
+#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:277
+#: ./src/pyams_content/component/paragraph/interfaces/video.py:48
 #: ./src/pyams_content/component/paragraph/interfaces/pictogram.py:58
+#: ./src/pyams_content/component/video/interfaces/__init__.py:48
 msgid "Associated text"
 msgstr ""
 
-#: ./src/pyams_content/component/keynumber/zmi/__init__.py:218
+#: ./src/pyams_content/component/keynumber/zmi/__init__.py:231
 msgid "Add keynumber"
 msgstr ""
 
-#: ./src/pyams_content/component/keynumber/zmi/__init__.py:230
+#: ./src/pyams_content/component/keynumber/zmi/__init__.py:243
 msgid "Add new keynumber"
 msgstr ""
 
-#: ./src/pyams_content/component/keynumber/zmi/__init__.py:259
+#: ./src/pyams_content/component/keynumber/zmi/__init__.py:272
 msgid "Edit keynumber properties"
 msgstr ""
 
-#: ./src/pyams_content/component/keynumber/zmi/__init__.py:245
+#: ./src/pyams_content/component/keynumber/zmi/__init__.py:258
 msgid "Key number was correctly added"
 msgstr ""
 
@@ -518,8 +526,8 @@
 msgstr ""
 
 #: ./src/pyams_content/component/keynumber/portlet/zmi/templates/keynumber-preview.pt:31
-#: ./src/pyams_content/features/menu/portlet/navigation/zmi/templates/double-preview.pt:11
-#: ./src/pyams_content/features/menu/portlet/navigation/zmi/templates/simple-preview.pt:8
+#: ./src/pyams_content/features/menu/portlet/navigation/zmi/templates/double-preview.pt:10
+#: ./src/pyams_content/features/menu/portlet/navigation/zmi/templates/simple-preview.pt:7
 msgid "Link target is not published!"
 msgstr ""
 
@@ -535,44 +543,52 @@
 msgid "Short text displayed above key numbers"
 msgstr ""
 
-#: ./src/pyams_content/component/keynumber/interfaces/__init__.py:39
+#: ./src/pyams_content/component/keynumber/interfaces/__init__.py:40
 #: ./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:118
+#: ./src/pyams_content/shared/form/interfaces/__init__.py:85
+#: ./src/pyams_content/shared/site/interfaces/__init__.py:144
 #: ./src/pyams_content/features/alert/interfaces.py:54
 #: ./src/pyams_content/features/menu/interfaces/__init__.py:59
 msgid "Visible?"
 msgstr ""
 
-#: ./src/pyams_content/component/keynumber/interfaces/__init__.py:40
+#: ./src/pyams_content/component/keynumber/interfaces/__init__.py:41
 msgid "Is this key number visible in front-office?"
 msgstr ""
 
-#: ./src/pyams_content/component/keynumber/interfaces/__init__.py:45
+#: ./src/pyams_content/component/keynumber/interfaces/__init__.py:46
 msgid ""
 "Small text to be displayed above number (according to selected renderer)"
 msgstr ""
 
-#: ./src/pyams_content/component/keynumber/interfaces/__init__.py:50
+#: ./src/pyams_content/component/keynumber/interfaces/__init__.py:51
 msgid "Key number value"
 msgstr ""
 
-#: ./src/pyams_content/component/keynumber/interfaces/__init__.py:54
+#: ./src/pyams_content/component/keynumber/interfaces/__init__.py:55
 msgid "Displayed unit"
 msgstr ""
 
-#: ./src/pyams_content/component/keynumber/interfaces/__init__.py:58
+#: ./src/pyams_content/component/keynumber/interfaces/__init__.py:59
 msgid "The way this text will be rendered depends on presentation template"
 msgstr ""
 
+#: ./src/pyams_content/component/keynumber/interfaces/__init__.py:94
+msgid "Key numbers template"
+msgstr ""
+
+#: ./src/pyams_content/component/keynumber/interfaces/__init__.py:95
+msgid "Presentation template used for key numbers"
+msgstr ""
+
 #: ./src/pyams_content/component/illustration/__init__.py:177
 #: ./src/pyams_content/component/illustration/thesaurus.py:32
 #: ./src/pyams_content/component/illustration/zmi/paragraph.py:158
 #: ./src/pyams_content/component/illustration/zmi/__init__.py:56
-#: ./src/pyams_content/component/illustration/zmi/__init__.py:100
+#: ./src/pyams_content/component/illustration/zmi/__init__.py:117
 #: ./src/pyams_content/component/illustration/interfaces/__init__.py:99
 msgid "Illustration"
 msgstr ""
@@ -590,11 +606,11 @@
 msgid "Edit illustration properties"
 msgstr ""
 
-#: ./src/pyams_content/component/illustration/zmi/__init__.py:150
+#: ./src/pyams_content/component/illustration/zmi/__init__.py:151
 msgid "Navigation link illustration"
 msgstr ""
 
-#: ./src/pyams_content/component/illustration/zmi/__init__.py:102
+#: ./src/pyams_content/component/illustration/zmi/__init__.py:119
 msgid "Header illustration"
 msgstr ""
 
@@ -603,6 +619,10 @@
 msgid "Alternate title used to describe image content"
 msgstr ""
 
+#: ./src/pyams_content/component/illustration/interfaces/__init__.py:65
+msgid "Illustration description displayed in front-office templates"
+msgstr ""
+
 #: ./src/pyams_content/component/illustration/interfaces/__init__.py:69
 msgid "Name of picture's author"
 msgstr ""
@@ -623,11 +643,11 @@
 msgid "Selected paragraph is not visible"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/container.py:73
+#: ./src/pyams_content/component/paragraph/container.py:90
 msgid "Paragraphs"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/container.py:95
+#: ./src/pyams_content/component/paragraph/container.py:112
 msgid "no visible paragraph"
 msgstr ""
 
@@ -635,6 +655,10 @@
 msgid "Selected pictogram is missing"
 msgstr ""
 
+#: ./src/pyams_content/component/paragraph/header.py:62
+msgid "This paragraph type is deprecated and should be removed!"
+msgstr ""
+
 #: ./src/pyams_content/component/paragraph/zmi/milestone.py:78
 msgid "Milestones..."
 msgstr ""
@@ -647,38 +671,38 @@
 msgid "Edit milestone paragraph properties"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/milestone.py:244
+#: ./src/pyams_content/component/paragraph/zmi/milestone.py:251
 #: ./src/pyams_content/component/paragraph/interfaces/milestone.py:49
 msgid "Associated label"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/milestone.py:256
+#: ./src/pyams_content/component/paragraph/zmi/milestone.py:263
 #: ./src/pyams_content/component/paragraph/interfaces/milestone.py:53
 msgid "Anchor"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/milestone.py:285
+#: ./src/pyams_content/component/paragraph/zmi/milestone.py:292
 #: ./src/pyams_content/component/paragraph/interfaces/milestone.py:76
 msgid "Milestones"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/milestone.py:300
+#: ./src/pyams_content/component/paragraph/zmi/milestone.py:307
 msgid "Add milestone"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/milestone.py:313
+#: ./src/pyams_content/component/paragraph/zmi/milestone.py:320
 msgid "Add new milestone"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/milestone.py:340
+#: ./src/pyams_content/component/paragraph/zmi/milestone.py:347
 msgid "Edit milestone properties"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/milestone.py:328
+#: ./src/pyams_content/component/paragraph/zmi/milestone.py:335
 msgid "Milestone was correctly added"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/milestone.py:271
+#: ./src/pyams_content/component/paragraph/zmi/milestone.py:278
 msgid "(missing paragraph)"
 msgstr ""
 
@@ -744,6 +768,24 @@
 msgid "Paragraph was correctly added."
 msgstr ""
 
+#: ./src/pyams_content/component/paragraph/zmi/__init__.py:254
+msgid ""
+"You changed renderer selection. Don't omit to update new renderer "
+"properties..."
+msgstr ""
+
+#: ./src/pyams_content/component/paragraph/zmi/map.py:55
+msgid "Location map..."
+msgstr ""
+
+#: ./src/pyams_content/component/paragraph/zmi/map.py:68
+msgid "Add new location map"
+msgstr ""
+
+#: ./src/pyams_content/component/paragraph/zmi/map.py:91
+msgid "Edit location map properties"
+msgstr ""
+
 #: ./src/pyams_content/component/paragraph/zmi/video.py:54
 msgid "Video paragraph..."
 msgstr ""
@@ -761,41 +803,41 @@
 msgid "Contents..."
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/container.py:196
+#: ./src/pyams_content/component/paragraph/zmi/container.py:215
 msgid "Set navigation anchor"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/container.py:268
+#: ./src/pyams_content/component/paragraph/zmi/container.py:286
 msgid "Show/hide all paragraphs"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/container.py:316
-#: ./src/pyams_content/component/paragraph/zmi/container.py:325
-#: ./src/pyams_content/component/paragraph/zmi/container.py:338
+#: ./src/pyams_content/component/paragraph/zmi/container.py:334
+#: ./src/pyams_content/component/paragraph/zmi/container.py:343
+#: ./src/pyams_content/component/paragraph/zmi/container.py:356
 msgid "Content blocks"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/container.py:395
+#: ./src/pyams_content/component/paragraph/zmi/container.py:413
 msgid "Links and attachments..."
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/container.py:407
+#: ./src/pyams_content/component/paragraph/zmi/container.py:425
 msgid "Content blocks links and attachments"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/container.py:127
+#: ./src/pyams_content/component/paragraph/zmi/container.py:145
 msgid "No currently defined paragraph."
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/container.py:277
+#: ./src/pyams_content/component/paragraph/zmi/container.py:295
 msgid "Click to open/close all paragraphs editors"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/container.py:290
+#: ./src/pyams_content/component/paragraph/zmi/container.py:308
 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:151
 msgid "Check allowed paragraph types to be able to create new paragraphs."
 msgstr ""
 
@@ -812,36 +854,36 @@
 msgstr ""
 
 #. Default: Header
-#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:253
+#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:260
 msgid "pictogram-item-header"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:290
+#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:299
 #: ./src/pyams_content/component/paragraph/interfaces/pictogram.py:80
 msgid "Pictograms"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:305
+#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:314
 #: ./src/pyams_content/reference/pictograms/zmi/__init__.py:59
 msgid "Add pictogram"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:318
+#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:327
 #: ./src/pyams_content/reference/pictograms/zmi/__init__.py:71
 msgid "Add new pictogram"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:360
+#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:369
 #: ./src/pyams_content/reference/pictograms/zmi/__init__.py:95
 msgid "Edit pictogram properties"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:340
+#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:349
 msgid "Pictogram was correctly added"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:350
-#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:388
+#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:359
+#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:397
 msgid "You must select a pictogram!"
 msgstr ""
 
@@ -857,27 +899,15 @@
 msgid "Edit audio properties"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/keynumber.py:55
-msgid "Key numbers..."
-msgstr ""
-
-#: ./src/pyams_content/component/paragraph/zmi/keynumber.py:68
-msgid "Add new key number paragraph"
-msgstr ""
-
-#: ./src/pyams_content/component/paragraph/zmi/keynumber.py:96
-msgid "Edit key number paragraph properties"
-msgstr ""
-
-#: ./src/pyams_content/component/paragraph/zmi/frame.py:85
+#: ./src/pyams_content/component/paragraph/zmi/frame.py:86
 msgid "Framed text..."
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/frame.py:99
+#: ./src/pyams_content/component/paragraph/zmi/frame.py:100
 msgid "Add new framed text paragraph"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/frame.py:125
+#: ./src/pyams_content/component/paragraph/zmi/frame.py:126
 msgid "Edit framed text paragraph properties"
 msgstr ""
 
@@ -929,15 +959,7 @@
 msgid "Edit contact card properties"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/header.py:50
-msgid "Header..."
-msgstr ""
-
-#: ./src/pyams_content/component/paragraph/zmi/header.py:63
-msgid "Add new header paragraph"
-msgstr ""
-
-#: ./src/pyams_content/component/paragraph/zmi/header.py:90
+#: ./src/pyams_content/component/paragraph/zmi/header.py:49
 msgid "Edit header paragraph properties"
 msgstr ""
 
@@ -1004,28 +1026,62 @@
 msgid "§ Title"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/interfaces/__init__.py:88
+#: ./src/pyams_content/component/paragraph/interfaces/__init__.py:94
 msgid "Allowed paragraphs"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/interfaces/__init__.py:89
+#: ./src/pyams_content/component/paragraph/interfaces/__init__.py:95
 msgid "List of paragraphs allowed for this content type"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/interfaces/__init__.py:93
-#: ./src/pyams_content/shared/common/zmi/types.py:173
-#: ./src/pyams_content/shared/common/zmi/types.py:413
+#: ./src/pyams_content/component/paragraph/interfaces/__init__.py:99
+#: ./src/pyams_content/shared/common/zmi/types.py:172
+#: ./src/pyams_content/shared/common/zmi/types.py:412
 msgid "Default paragraphs"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/interfaces/__init__.py:94
+#: ./src/pyams_content/component/paragraph/interfaces/__init__.py:100
 msgid "List of paragraphs automatically added to a new content"
 msgstr ""
 
+#: ./src/pyams_content/component/paragraph/interfaces/map.py:41
+msgid "Location map"
+msgstr ""
+
+#: ./src/pyams_content/component/paragraph/interfaces/map.py:48
+#: ./src/pyams_content/component/paragraph/interfaces/contact.py:72
+msgid "GPS location"
+msgstr ""
+
+#: ./src/pyams_content/component/paragraph/interfaces/map.py:49
+msgid "GPS coordinates used to locate map"
+msgstr ""
+
+#: ./src/pyams_content/component/paragraph/interfaces/map.py:52
+msgid "Display location mark?"
+msgstr ""
+
+#: ./src/pyams_content/component/paragraph/interfaces/map.py:53
+msgid "If 'yes', a location marker will be displayed on map"
+msgstr ""
+
+#: ./src/pyams_content/component/paragraph/interfaces/map.py:57
+msgid "Map template"
+msgstr ""
+
+#: ./src/pyams_content/component/paragraph/interfaces/map.py:58
+msgid "Presentation template used for this map"
+msgstr ""
+
 #: ./src/pyams_content/component/paragraph/interfaces/video.py:42
 msgid "Video file content"
 msgstr ""
 
+#: ./src/pyams_content/component/paragraph/interfaces/video.py:49
+#: ./src/pyams_content/component/video/interfaces/__init__.py:49
+msgid "Video description displayed by front-office template"
+msgstr ""
+
 #: ./src/pyams_content/component/paragraph/interfaces/video.py:56
 #: ./src/pyams_content/component/video/interfaces/__init__.py:78
 msgid "Video template"
@@ -1043,12 +1099,10 @@
 #: ./src/pyams_content/component/paragraph/interfaces/pictogram.py:46
 #: ./src/pyams_content/component/links/interfaces/__init__.py:43
 #: ./src/pyams_content/shared/common/interfaces/types.py:75
-#: ./src/pyams_content/features/alert/interfaces.py:79
 msgid "Pictogram"
 msgstr ""
 
 #: ./src/pyams_content/component/paragraph/interfaces/pictogram.py:47
-#: ./src/pyams_content/features/alert/interfaces.py:80
 msgid "Name of the pictogram to select"
 msgstr ""
 
@@ -1086,14 +1140,6 @@
 msgid "Presentation template used for this audio file"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/interfaces/keynumber.py:36
-msgid "Key numbers template"
-msgstr ""
-
-#: ./src/pyams_content/component/paragraph/interfaces/keynumber.py:37
-msgid "Presentation template used for key numbers"
-msgstr ""
-
 #: ./src/pyams_content/component/paragraph/interfaces/frame.py:33
 msgid "Framed text"
 msgstr ""
@@ -1217,16 +1263,13 @@
 msgid "Presentation template used for this contact"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/interfaces/contact.py:72
-msgid "GPS location"
-msgstr ""
-
 #: ./src/pyams_content/component/paragraph/interfaces/contact.py:73
 msgid "GPS coordinates used to locate contact"
 msgstr ""
 
 #: ./src/pyams_content/component/paragraph/interfaces/header.py:33
 #: ./src/pyams_content/component/paragraph/interfaces/header.py:40
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:153
 msgid "Header"
 msgstr ""
 
@@ -1244,6 +1287,9 @@
 #: ./src/pyams_content/component/theme/zmi/portlet.py:40
 #: ./src/pyams_content/component/theme/interfaces/__init__.py:47
 #: ./src/pyams_content/component/theme/interfaces/__init__.py:61
+#: ./src/pyams_content/shared/common/zmi/search.py:189
+#: ./src/pyams_content/root/zmi/search.py:179
+#: ./src/pyams_content/root/zmi/templates/advanced-search.pt:181
 msgid "Tags"
 msgstr ""
 
@@ -1251,6 +1297,7 @@
 #: ./src/pyams_content/component/theme/zmi/portlet.py:55
 #: ./src/pyams_content/component/theme/interfaces/__init__.py:88
 #: ./src/pyams_content/component/theme/interfaces/__init__.py:102
+#: ./src/pyams_content/shared/common/zmi/search.py:192
 msgid "Themes"
 msgstr ""
 
@@ -1258,6 +1305,7 @@
 #: ./src/pyams_content/component/theme/zmi/portlet.py:70
 #: ./src/pyams_content/component/theme/interfaces/__init__.py:129
 #: ./src/pyams_content/component/theme/interfaces/__init__.py:143
+#: ./src/pyams_content/shared/common/zmi/search.py:195
 msgid "Collections"
 msgstr ""
 
@@ -1300,32 +1348,32 @@
 msgid "Content collections"
 msgstr ""
 
-#: ./src/pyams_content/component/theme/zmi/manager.py:51
+#: ./src/pyams_content/component/theme/zmi/manager.py:58
 msgid "Tags settings..."
 msgstr ""
 
-#: ./src/pyams_content/component/theme/zmi/manager.py:65
+#: ./src/pyams_content/component/theme/zmi/manager.py:72
 msgid "Selected tags"
 msgstr ""
 
-#: ./src/pyams_content/component/theme/zmi/manager.py:101
+#: ./src/pyams_content/component/theme/zmi/manager.py:108
 msgid "Themes settings..."
 msgstr ""
 
-#: ./src/pyams_content/component/theme/zmi/manager.py:115
+#: ./src/pyams_content/component/theme/zmi/manager.py:122
 msgid "Selected themes"
 msgstr ""
 
-#: ./src/pyams_content/component/theme/zmi/manager.py:151
+#: ./src/pyams_content/component/theme/zmi/manager.py:158
 msgid "Collections settings..."
 msgstr ""
 
-#: ./src/pyams_content/component/theme/zmi/manager.py:165
+#: ./src/pyams_content/component/theme/zmi/manager.py:172
 msgid "Selected collections"
 msgstr ""
 
 #: ./src/pyams_content/component/association/container.py:91
-#: ./src/pyams_content/component/association/zmi/__init__.py:296
+#: ./src/pyams_content/component/association/zmi/__init__.py:303
 #: ./src/pyams_content/component/association/interfaces/__init__.py:93
 msgid "Associations"
 msgstr ""
@@ -1343,20 +1391,20 @@
 msgid "Edit association paragraph properties"
 msgstr ""
 
-#: ./src/pyams_content/component/association/zmi/__init__.py:198
+#: ./src/pyams_content/component/association/zmi/__init__.py:205
 msgid "Public title"
 msgstr ""
 
-#: ./src/pyams_content/component/association/zmi/__init__.py:216
+#: ./src/pyams_content/component/association/zmi/__init__.py:223
 msgid "Inner title"
 msgstr ""
 
-#: ./src/pyams_content/component/association/zmi/__init__.py:232
+#: ./src/pyams_content/component/association/zmi/__init__.py:239
 msgid "Size"
 msgstr ""
 
-#: ./src/pyams_content/component/association/zmi/__init__.py:273
-#: ./src/pyams_content/component/association/zmi/__init__.py:283
+#: ./src/pyams_content/component/association/zmi/__init__.py:280
+#: ./src/pyams_content/component/association/zmi/__init__.py:290
 msgid "Associations list"
 msgstr ""
 
@@ -1377,19 +1425,19 @@
 msgid "Presentation template used for associations"
 msgstr ""
 
-#: ./src/pyams_content/component/links/__init__.py:123
+#: ./src/pyams_content/component/links/__init__.py:144
 msgid "Internal link"
 msgstr ""
 
-#: ./src/pyams_content/component/links/__init__.py:219
+#: ./src/pyams_content/component/links/__init__.py:230
 msgid "External link"
 msgstr ""
 
-#: ./src/pyams_content/component/links/__init__.py:272
+#: ./src/pyams_content/component/links/__init__.py:283
 msgid "Mailto link"
 msgstr ""
 
-#: ./src/pyams_content/component/links/__init__.py:206
+#: ./src/pyams_content/component/links/__init__.py:217
 msgid "target is not published"
 msgstr ""
 
@@ -1441,14 +1489,6 @@
 msgid "Edit mailto link properties"
 msgstr ""
 
-#: ./src/pyams_content/component/links/zmi/reverse.py:57
-msgid "Reverse links"
-msgstr ""
-
-#: ./src/pyams_content/component/links/zmi/reverse.py:66
-msgid "Content's internal links"
-msgstr ""
-
 #: ./src/pyams_content/component/links/interfaces/__init__.py:36
 msgid "Link title, as shown in front-office"
 msgstr ""
@@ -1463,6 +1503,7 @@
 
 #: ./src/pyams_content/component/links/interfaces/__init__.py:61
 #: ./src/pyams_content/shared/logo/interfaces/__init__.py:56
+#: ./src/pyams_content/features/redirect/interfaces/__init__.py:68
 msgid "Target URL"
 msgstr ""
 
@@ -1761,145 +1802,149 @@
 msgid "Name of external platform providing selected video"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/__init__.py:240
-#: ./src/pyams_content/shared/common/zmi/properties.py:70
+#: ./src/pyams_content/shared/common/__init__.py:242
+#: ./src/pyams_content/shared/common/zmi/properties.py:69
 #: ./src/pyams_content/shared/common/zmi/manager.py:96
 msgid "Properties"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/__init__.py:150
-#: ./src/pyams_content/shared/common/__init__.py:158
+#: ./src/pyams_content/shared/common/__init__.py:152
+#: ./src/pyams_content/shared/common/__init__.py:160
 #, python-format
 msgid "{date} by {principal}"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/__init__.py:263
+#: ./src/pyams_content/shared/common/__init__.py:265
 #, python-format
 msgid "title length should be between 40 and 66 characters ({length} actually)"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/search.py:92
-#: ./src/pyams_content/root/zmi/search.py:91
+#: ./src/pyams_content/shared/common/zmi/search.py:96
+#: ./src/pyams_content/root/zmi/search.py:95
 msgid "Quick search results"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/search.py:157
-#: ./src/pyams_content/shared/common/zmi/search.py:190
-#: ./src/pyams_content/root/zmi/search.py:147
-#: ./src/pyams_content/root/zmi/search.py:180
+#: ./src/pyams_content/shared/common/zmi/search.py:162
+#: ./src/pyams_content/shared/common/zmi/search.py:204
+#: ./src/pyams_content/root/zmi/search.py:152
+#: ./src/pyams_content/root/zmi/search.py:188
 msgid "Advanced search"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/search.py:282
-#: ./src/pyams_content/root/zmi/search.py:260
+#: ./src/pyams_content/shared/common/zmi/search.py:332
+#: ./src/pyams_content/root/zmi/search.py:280
 msgid "Advanced search results"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/search.py:165
+#: ./src/pyams_content/shared/common/zmi/search.py:170
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:231
-#: ./src/pyams_content/root/zmi/search.py:159
+#: ./src/pyams_content/root/zmi/search.py:164
 msgid "Owner"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/search.py:168
+#: ./src/pyams_content/shared/common/zmi/search.py:173
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:154
 msgid "Status"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/search.py:172
-#: ./src/pyams_content/root/zmi/search.py:162
+#: ./src/pyams_content/shared/common/zmi/search.py:177
+#: ./src/pyams_content/root/zmi/search.py:167
 msgid "Created after..."
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/search.py:175
-#: ./src/pyams_content/root/zmi/search.py:165
+#: ./src/pyams_content/shared/common/zmi/search.py:180
+#: ./src/pyams_content/root/zmi/search.py:170
 msgid "Created before..."
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/search.py:178
-#: ./src/pyams_content/root/zmi/search.py:168
+#: ./src/pyams_content/shared/common/zmi/search.py:183
+#: ./src/pyams_content/root/zmi/search.py:173
 msgid "Modified after..."
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/search.py:181
-#: ./src/pyams_content/root/zmi/search.py:171
+#: ./src/pyams_content/shared/common/zmi/search.py:186
+#: ./src/pyams_content/root/zmi/search.py:176
 msgid "Modified before..."
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/properties.py:60
+#: ./src/pyams_content/shared/common/zmi/properties.py:59
 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 ""
 
-#: ./src/pyams_content/shared/common/zmi/types.py:71
+#: ./src/pyams_content/shared/common/zmi/types.py:70
 msgid "Data types"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/types.py:149
+#: ./src/pyams_content/shared/common/zmi/types.py:148
 msgid "Data type label"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/types.py:189
-#: ./src/pyams_content/shared/common/zmi/types.py:429
+#: ./src/pyams_content/shared/common/zmi/types.py:188
+#: ./src/pyams_content/shared/common/zmi/types.py:428
 msgid "Default associations"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/types.py:205
+#: ./src/pyams_content/shared/common/zmi/types.py:204
 msgid "Default themes"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/types.py:233
+#: ./src/pyams_content/shared/common/zmi/types.py:232
 msgid "Content data types"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/types.py:256
+#: ./src/pyams_content/shared/common/zmi/types.py:255
 msgid "Add data type"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/types.py:268
+#: ./src/pyams_content/shared/common/zmi/types.py:267
 msgid "Add new data type"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/types.py:311
+#: ./src/pyams_content/shared/common/zmi/types.py:310
 msgid "Data type properties"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/types.py:392
+#: ./src/pyams_content/shared/common/zmi/types.py:391
 msgid "Subtype label"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/types.py:473
+#: ./src/pyams_content/shared/common/zmi/types.py:472
 msgid "Add subtype"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/types.py:485
+#: ./src/pyams_content/shared/common/zmi/types.py:484
 msgid "Add new subtype"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/types.py:532
+#: ./src/pyams_content/shared/common/zmi/types.py:531
 msgid "Data subtype properties"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/types.py:116
+#: ./src/pyams_content/shared/common/zmi/types.py:573
+msgid "Select content type..."
+msgstr ""
+
+#: ./src/pyams_content/shared/common/zmi/types.py:115
 msgid "No currently defined data type."
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/types.py:301
+#: ./src/pyams_content/shared/common/zmi/types.py:300
 msgid "Specified type name is already used!"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/types.py:508
+#: ./src/pyams_content/shared/common/zmi/types.py:507
 msgid "Subtype was correctly added."
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/types.py:522
+#: ./src/pyams_content/shared/common/zmi/types.py:521
 msgid "Specified subtype name is already used!"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/types.py:161
+#: ./src/pyams_content/shared/common/zmi/types.py:160
 msgid "Click to see subtypes"
 msgstr ""
 
@@ -2044,45 +2089,45 @@
 msgid "Created or modified in this version"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/summary.py:50
+#: ./src/pyams_content/shared/common/zmi/summary.py:52
 msgid "Display content summary"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/summary.py:74
+#: ./src/pyams_content/shared/common/zmi/summary.py:76
 msgid "Identity card"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/summary.py:86
+#: ./src/pyams_content/shared/common/zmi/summary.py:94
 msgid "Requested action"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/summary.py:127
+#: ./src/pyams_content/shared/common/zmi/summary.py:135
 msgid "Publication and retire dates"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/summary.py:146
+#: ./src/pyams_content/shared/common/zmi/summary.py:154
 msgid "Current version"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/summary.py:176
+#: ./src/pyams_content/shared/common/zmi/summary.py:184
 msgid "Content history"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/summary.py:117
+#: ./src/pyams_content/shared/common/zmi/summary.py:125
 msgid "Associated comment"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/summary.py:158
+#: ./src/pyams_content/shared/common/zmi/summary.py:166
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:198
 msgid "Version"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/summary.py:107
+#: ./src/pyams_content/shared/common/zmi/summary.py:115
 #, python-format
 msgid "{state} {date} by {principal}"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/summary.py:164
+#: ./src/pyams_content/shared/common/zmi/summary.py:172
 #, python-format
 msgid "{state} since {date}, by {principal}"
 msgstr ""
@@ -2177,17 +2222,25 @@
 msgid "You must provide an URL for this item!"
 msgstr ""
 
+#: ./src/pyams_content/shared/common/zmi/reverse.py:57
+msgid "Reverse links"
+msgstr ""
+
+#: ./src/pyams_content/shared/common/zmi/reverse.py:66
+msgid "Content's internal links"
+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
+#: ./src/pyams_content/shared/common/zmi/portal.py:44
 msgid "Edit default template properties"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/portal.py:56
+#: ./src/pyams_content/shared/common/zmi/portal.py:54
 msgid ""
 "**This form allows you to select shared content default template.**\n"
 "\n"
@@ -2196,8 +2249,8 @@
 "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"
+#: ./src/pyams_content/shared/common/zmi/portal.py:70
+msgid "Use tool default template"
 msgstr ""
 
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:134
@@ -2286,7 +2339,7 @@
 
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:545
 #: ./src/pyams_content/root/zmi/__init__.py:346
-msgid "Your favorites"
+msgid "Your favorite contents"
 msgstr ""
 
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:558
@@ -2606,9 +2659,9 @@
 #: ./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:47
-#: ./src/pyams_content/shared/form/zmi/field.py:160
-#: ./src/pyams_content/shared/form/interfaces/__init__.py:62
-#: ./src/pyams_content/features/menu/zmi/__init__.py:208
+#: ./src/pyams_content/shared/form/zmi/field.py:167
+#: ./src/pyams_content/shared/form/interfaces/__init__.py:60
+#: ./src/pyams_content/features/menu/zmi/__init__.py:215
 msgid "Label"
 msgstr ""
 
@@ -2700,25 +2753,25 @@
 "shared by all content's versions."
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/templates/advanced-search.pt:128
-#: ./src/pyams_content/root/zmi/templates/advanced-search.pt:128
+#: ./src/pyams_content/shared/common/zmi/templates/advanced-search.pt:130
+#: ./src/pyams_content/root/zmi/templates/advanced-search.pt:130
 msgid "Created between"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/templates/advanced-search.pt:140
-#: ./src/pyams_content/shared/common/zmi/templates/advanced-search.pt:166
-#: ./src/pyams_content/root/zmi/templates/advanced-search.pt:140
-#: ./src/pyams_content/root/zmi/templates/advanced-search.pt:166
+#: ./src/pyams_content/shared/common/zmi/templates/advanced-search.pt:142
+#: ./src/pyams_content/shared/common/zmi/templates/advanced-search.pt:168
+#: ./src/pyams_content/root/zmi/templates/advanced-search.pt:142
+#: ./src/pyams_content/root/zmi/templates/advanced-search.pt:168
 msgid "and"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/templates/advanced-search.pt:154
-#: ./src/pyams_content/root/zmi/templates/advanced-search.pt:154
+#: ./src/pyams_content/shared/common/zmi/templates/advanced-search.pt:156
+#: ./src/pyams_content/root/zmi/templates/advanced-search.pt:156
 msgid "Modified between"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/zmi/templates/advanced-search.pt:202
-#: ./src/pyams_content/root/zmi/templates/advanced-search.pt:202
+#: ./src/pyams_content/shared/common/zmi/templates/advanced-search.pt:214
+#: ./src/pyams_content/root/zmi/templates/advanced-search.pt:211
 msgid "Tab label"
 msgstr ""
 
@@ -2808,12 +2861,12 @@
 msgid "This is where the content will be displayed!!"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/portlet/content/skin/__init__.py:36
+#: ./src/pyams_content/shared/common/portlet/content/skin/__init__.py:39
 msgid "Default content renderer"
 msgstr ""
 
 #: ./src/pyams_content/shared/common/interfaces/types.py:43
-#: ./src/pyams_content/shared/form/zmi/field.py:149
+#: ./src/pyams_content/shared/form/zmi/field.py:156
 msgid "Name"
 msgstr ""
 
@@ -2891,222 +2944,231 @@
 msgid "Type of content data"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:46
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:45
 #: ./src/pyams_content/root/interfaces/__init__.py:43
 msgid "Webmasters"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:47
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:46
 msgid "Webmasters can handle all contents, including published ones"
 msgstr ""
 
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:50
+msgid "Pilots"
+msgstr ""
+
 #: ./src/pyams_content/shared/common/interfaces/__init__.py:51
-msgid "Pilots"
-msgstr ""
-
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:52
 msgid ""
 "Pilots can handle tool configuration, manage access rules, grant users roles "
 "and manage managers restrictions"
 msgstr ""
 
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:56
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:196
+msgid "Managers"
+msgstr ""
+
 #: ./src/pyams_content/shared/common/interfaces/__init__.py:57
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:191
-msgid "Managers"
-msgstr ""
-
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:58
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:192
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:197
 msgid ""
 "Managers can handle main operations in tool's workflow, like publish or "
 "retire contents"
 msgstr ""
 
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:62
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:202
+msgid "Contributors"
+msgstr ""
+
 #: ./src/pyams_content/shared/common/interfaces/__init__.py:63
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:197
-msgid "Contributors"
-msgstr ""
-
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:64
 msgid "Contributors are users which are allowed to create new contents"
 msgstr ""
 
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:67
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:208
+msgid "Designers"
+msgstr ""
+
 #: ./src/pyams_content/shared/common/interfaces/__init__.py:68
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:203
-msgid "Designers"
-msgstr ""
-
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:69
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:204
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:209
 msgid "Designers are users which are allowed to manage presentation templates"
 msgstr ""
 
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:94
+msgid "Workflow name"
+msgstr ""
+
 #: ./src/pyams_content/shared/common/interfaces/__init__.py:95
-msgid "Workflow name"
-msgstr ""
-
-#: ./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:123
+msgid "Content URL"
+msgstr ""
+
 #: ./src/pyams_content/shared/common/interfaces/__init__.py:124
-msgid "Content URL"
-msgstr ""
-
-#: ./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:130
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:129
 msgid "Version creator"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:131
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:130
 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:135
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:134
 msgid "First owner"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:136
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:135
 msgid "Name of content's first version owner"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:140
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:139
 msgid "Version creation"
 msgstr ""
 
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:142
+msgid "Version modifiers"
+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:146
+msgid "Last modifier"
+msgstr ""
+
 #: ./src/pyams_content/shared/common/interfaces/__init__.py:147
-msgid "Last modifier"
-msgstr ""
-
-#: ./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:151
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:150
 msgid "Last update"
 msgstr ""
 
-#: ./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"
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:154
+msgid "Content's header is generally displayed in page header"
+msgstr ""
+
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:159
+msgid "Meta-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:67
-msgid "Notepad"
+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; "
+"if description is empty, content's header will be used."
 msgstr ""
 
 #: ./src/pyams_content/shared/common/interfaces/__init__.py:165
+msgid "Keywords"
+msgstr ""
+
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:166
+msgid "They will be included into HTML pages metadata"
+msgstr ""
+
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:169
 #: ./src/pyams_content/shared/site/zmi/folder.py:79
-#: ./src/pyams_content/shared/site/interfaces/__init__.py:68
+#: ./src/pyams_content/shared/site/interfaces/__init__.py:76
+msgid "Notepad"
+msgstr ""
+
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:170
+#: ./src/pyams_content/shared/site/zmi/folder.py:80
+#: ./src/pyams_content/shared/site/interfaces/__init__.py:77
 msgid "Internal information to be known about this content"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:184
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:189
 msgid "Content owner"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:185
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:190
 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:198
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:203
 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:208
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:213
 msgid "Readers"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:209
-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:214
+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:219
 msgid "Guests"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:215
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:220
 msgid ""
 "Guests are users which are allowed to view contents with restricted access"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:235
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:243
 msgid "Principal ID"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:274
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:299
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:282
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:307
 msgid "Publication checks"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:275
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:283
 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:300
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:308
 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:305
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:313
 msgid "Restricted contents"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:306
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:314
 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:311
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:319
 msgid "Selected owners"
 msgstr ""
 
-#: ./src/pyams_content/shared/common/interfaces/__init__.py:312
+#: ./src/pyams_content/shared/common/interfaces/__init__.py:320
 msgid "Manager will have access to contents owned by these principals"
 msgstr ""
 
-#: ./src/pyams_content/shared/form/__init__.py:99
+#: ./src/pyams_content/shared/form/__init__.py:97
 msgid "Form fields"
 msgstr ""
 
-#: ./src/pyams_content/shared/form/__init__.py:100
+#: ./src/pyams_content/shared/form/__init__.py:98
 msgid "no field defined"
 msgstr ""
 
@@ -3184,165 +3246,161 @@
 msgid "Form fields..."
 msgstr ""
 
-#: ./src/pyams_content/shared/form/zmi/field.py:171
-#: ./src/pyams_content/shared/form/interfaces/__init__.py:57
+#: ./src/pyams_content/shared/form/zmi/field.py:178
+#: ./src/pyams_content/shared/form/interfaces/__init__.py:55
 msgid "Field type"
 msgstr ""
 
-#: ./src/pyams_content/shared/form/zmi/field.py:204
+#: ./src/pyams_content/shared/form/zmi/field.py:211
 msgid "Form fields list"
 msgstr ""
 
-#: ./src/pyams_content/shared/form/zmi/field.py:227
-#: ./src/pyams_content/shared/form/zmi/field.py:240
+#: ./src/pyams_content/shared/form/zmi/field.py:234
+#: ./src/pyams_content/shared/form/zmi/field.py:247
 msgid "Add form field"
 msgstr ""
 
-#: ./src/pyams_content/shared/form/zmi/field.py:278
+#: ./src/pyams_content/shared/form/zmi/field.py:285
 msgid "Edit form field properties"
 msgstr ""
 
-#: ./src/pyams_content/shared/form/zmi/field.py:180
+#: ./src/pyams_content/shared/form/zmi/field.py:187
 msgid "-- unknown field type --"
 msgstr ""
 
-#: ./src/pyams_content/shared/form/zmi/field.py:115
+#: ./src/pyams_content/shared/form/zmi/field.py:122
 msgid "No currently defined form field."
 msgstr ""
 
-#: ./src/pyams_content/shared/form/zmi/field.py:262
+#: ./src/pyams_content/shared/form/zmi/field.py:269
 msgid "Specified name is already used!"
 msgstr ""
 
-#: ./src/pyams_content/shared/form/interfaces/__init__.py:35
+#: ./src/pyams_content/shared/form/interfaces/__init__.py:33
 msgid "Form"
 msgstr ""
 
-#: ./src/pyams_content/shared/form/interfaces/__init__.py:53
+#: ./src/pyams_content/shared/form/interfaces/__init__.py:51
 msgid "Field name"
 msgstr ""
 
-#: ./src/pyams_content/shared/form/interfaces/__init__.py:54
+#: ./src/pyams_content/shared/form/interfaces/__init__.py:52
 msgid "Field internal name; must be unique for a given form"
 msgstr ""
 
-#: ./src/pyams_content/shared/form/interfaces/__init__.py:58
+#: ./src/pyams_content/shared/form/interfaces/__init__.py:56
 msgid "Selected field type"
 msgstr ""
 
-#: ./src/pyams_content/shared/form/interfaces/__init__.py:63
+#: ./src/pyams_content/shared/form/interfaces/__init__.py:61
 msgid "User field label"
 msgstr ""
 
-#: ./src/pyams_content/shared/form/interfaces/__init__.py:67
+#: ./src/pyams_content/shared/form/interfaces/__init__.py:65
 msgid "Field description can be displayed as hint"
 msgstr ""
 
-#: ./src/pyams_content/shared/form/interfaces/__init__.py:70
+#: ./src/pyams_content/shared/form/interfaces/__init__.py:68
 msgid "Placeholder"
 msgstr ""
 
-#: ./src/pyams_content/shared/form/interfaces/__init__.py:71
+#: ./src/pyams_content/shared/form/interfaces/__init__.py:69
 msgid "Some field types like textline can display a placeholder"
 msgstr ""
 
-#: ./src/pyams_content/shared/form/interfaces/__init__.py:74
+#: ./src/pyams_content/shared/form/interfaces/__init__.py:72
 msgid "Optional values"
 msgstr ""
 
-#: ./src/pyams_content/shared/form/interfaces/__init__.py:75
+#: ./src/pyams_content/shared/form/interfaces/__init__.py:73
 msgid "List of available values (for 'choice' and 'list' field types)"
 msgstr ""
 
-#: ./src/pyams_content/shared/form/interfaces/__init__.py:78
+#: ./src/pyams_content/shared/form/interfaces/__init__.py:76
 msgid "Default value"
 msgstr ""
 
-#: ./src/pyams_content/shared/form/interfaces/__init__.py:79
+#: ./src/pyams_content/shared/form/interfaces/__init__.py:77
 msgid "Give default value if field type can use it"
 msgstr ""
 
-#: ./src/pyams_content/shared/form/interfaces/__init__.py:82
+#: ./src/pyams_content/shared/form/interfaces/__init__.py:80
 msgid "Required?"
 msgstr ""
 
-#: ./src/pyams_content/shared/form/interfaces/__init__.py:83
+#: ./src/pyams_content/shared/form/interfaces/__init__.py:81
 msgid "Select 'yes' to set field as mandatory"
 msgstr ""
 
-#: ./src/pyams_content/shared/form/interfaces/__init__.py:88
+#: ./src/pyams_content/shared/form/interfaces/__init__.py:86
 msgid "Select 'no' to hide given field..."
 msgstr ""
 
-#: ./src/pyams_content/shared/form/interfaces/__init__.py:122
+#: ./src/pyams_content/shared/form/interfaces/__init__.py:120
 msgid "Form title"
 msgstr ""
 
-#: ./src/pyams_content/shared/form/interfaces/__init__.py:125
-msgid "Form header"
+#: ./src/pyams_content/shared/form/interfaces/__init__.py:123
+msgid "Form handler"
+msgstr ""
+
+#: ./src/pyams_content/shared/form/interfaces/__init__.py:124
+msgid "Select how form data is transmitted"
+msgstr ""
+
+#: ./src/pyams_content/shared/form/interfaces/__init__.py:127
+msgid "Authenticated only?"
 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"
+msgid "If 'yes', only authenticated users will be able to see and submit form"
 msgstr ""
 
 #: ./src/pyams_content/shared/form/interfaces/__init__.py:132
-msgid "Authenticated only?"
+msgid "Use captcha?"
 msgstr ""
 
 #: ./src/pyams_content/shared/form/interfaces/__init__.py:133
-msgid "If 'yes', only authenticated users will be able to see and submit form"
+msgid "If 'yes', a captcha will be added automatically to the form"
 msgstr ""
 
 #: ./src/pyams_content/shared/form/interfaces/__init__.py:137
-msgid "Use captcha?"
+msgid "Submit label"
 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: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:175
+msgid "Source address"
+msgstr ""
+
+#: ./src/pyams_content/shared/form/interfaces/__init__.py:176
+msgid "Mail address from which form data is sent"
+msgstr ""
+
+#: ./src/pyams_content/shared/form/interfaces/__init__.py:179
+msgid "Source name"
+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"
+msgid "Name of mail data sender"
+msgstr ""
+
+#: ./src/pyams_content/shared/form/interfaces/__init__.py:183
+msgid "Recipient address"
 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"
+msgid "Mail address to which form data is sent"
+msgstr ""
+
+#: ./src/pyams_content/shared/form/interfaces/__init__.py:187
+msgid "Recipient name"
 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:192
-msgid "Recipient name"
-msgstr ""
-
-#: ./src/pyams_content/shared/form/interfaces/__init__.py:193
 msgid "Name of data recipient"
 msgstr ""
 
@@ -3396,7 +3454,7 @@
 msgid "Sort all results by first publication date"
 msgstr ""
 
-#: ./src/pyams_content/shared/view/zmi/properties.py:40
+#: ./src/pyams_content/shared/view/zmi/properties.py:45
 msgid "Main view settings"
 msgstr ""
 
@@ -3482,7 +3540,7 @@
 msgstr ""
 
 #: ./src/pyams_content/shared/view/portlet/interfaces.py:91
-#: ./src/pyams_content/shared/view/interfaces/__init__.py:86
+#: ./src/pyams_content/shared/view/interfaces/__init__.py:102
 msgid "Results count limit"
 msgstr ""
 
@@ -3499,134 +3557,151 @@
 msgid "No selected view"
 msgstr ""
 
-#: ./src/pyams_content/shared/view/interfaces/__init__.py:32
+#: ./src/pyams_content/shared/view/interfaces/__init__.py:34
 msgid "View"
 msgstr ""
 
-#: ./src/pyams_content/shared/view/interfaces/__init__.py:150
+#: ./src/pyams_content/shared/view/interfaces/__init__.py:166
 msgid "Always include selected internal references"
 msgstr ""
 
-#: ./src/pyams_content/shared/view/interfaces/__init__.py:151
+#: ./src/pyams_content/shared/view/interfaces/__init__.py:167
 msgid "Include selected internal references only if empty"
 msgstr ""
 
-#: ./src/pyams_content/shared/view/interfaces/__init__.py:41
+#: ./src/pyams_content/shared/view/interfaces/__init__.py:43
 #: ./src/pyams_content/interfaces/__init__.py:113
 #: ./src/pyams_content/features/review/interfaces.py:74
 msgid "Creation date"
 msgstr ""
 
-#: ./src/pyams_content/shared/view/interfaces/__init__.py:42
-msgid "Last update date"
-msgstr ""
-
-#: ./src/pyams_content/shared/view/interfaces/__init__.py:43
-msgid "Current publication date"
-msgstr ""
-
 #: ./src/pyams_content/shared/view/interfaces/__init__.py:44
+msgid "Last update date"
+msgstr ""
+
+#: ./src/pyams_content/shared/view/interfaces/__init__.py:45
+msgid "Current publication date"
+msgstr ""
+
+#: ./src/pyams_content/shared/view/interfaces/__init__.py:46
 msgid "First publication date"
 msgstr ""
 
-#: ./src/pyams_content/shared/view/interfaces/__init__.py:62
+#: ./src/pyams_content/shared/view/interfaces/__init__.py:64
 msgid "Select context type?"
 msgstr ""
 
-#: ./src/pyams_content/shared/view/interfaces/__init__.py:63
+#: ./src/pyams_content/shared/view/interfaces/__init__.py:65
 msgid "If 'yes', content type will be extracted from context"
 msgstr ""
 
-#: ./src/pyams_content/shared/view/interfaces/__init__.py:67
+#: ./src/pyams_content/shared/view/interfaces/__init__.py:69
 msgid "Other content types"
 msgstr ""
 
-#: ./src/pyams_content/shared/view/interfaces/__init__.py:68
+#: ./src/pyams_content/shared/view/interfaces/__init__.py:70
 msgid "Selected content types; leave empty for all"
 msgstr ""
 
-#: ./src/pyams_content/shared/view/interfaces/__init__.py:75
+#: ./src/pyams_content/shared/view/interfaces/__init__.py:77
+msgid "Select context data type?"
+msgstr ""
+
+#: ./src/pyams_content/shared/view/interfaces/__init__.py:78
+msgid ""
+"If 'yes', content data type (if available) will be extracted from context"
+msgstr ""
+
+#: ./src/pyams_content/shared/view/interfaces/__init__.py:83
+msgid "Other data types"
+msgstr ""
+
+#: ./src/pyams_content/shared/view/interfaces/__init__.py:84
+msgid "Selected data types; leave empty for all"
+msgstr ""
+
+#: ./src/pyams_content/shared/view/interfaces/__init__.py:91
 msgid "Order by"
 msgstr ""
 
-#: ./src/pyams_content/shared/view/interfaces/__init__.py:76
+#: ./src/pyams_content/shared/view/interfaces/__init__.py:92
 msgid "Property to use to sort results"
 msgstr ""
 
-#: ./src/pyams_content/shared/view/interfaces/__init__.py:81
+#: ./src/pyams_content/shared/view/interfaces/__init__.py:97
 msgid "Reversed order?"
 msgstr ""
 
-#: ./src/pyams_content/shared/view/interfaces/__init__.py:82
+#: ./src/pyams_content/shared/view/interfaces/__init__.py:98
 msgid "If 'yes', items order will be reversed"
 msgstr ""
 
-#: ./src/pyams_content/shared/view/interfaces/__init__.py:87
+#: ./src/pyams_content/shared/view/interfaces/__init__.py:103
 msgid "Maximum number of results that the view may retrieve"
 msgstr ""
 
-#: ./src/pyams_content/shared/view/interfaces/__init__.py:160
+#: ./src/pyams_content/shared/view/interfaces/__init__.py:176
 msgid "Internal references usage"
 msgstr ""
 
-#: ./src/pyams_content/shared/view/interfaces/__init__.py:161
+#: ./src/pyams_content/shared/view/interfaces/__init__.py:177
 msgid "Specify how selected references are included into view results"
 msgstr ""
 
-#: ./src/pyams_content/shared/view/interfaces/__init__.py:166
+#: ./src/pyams_content/shared/view/interfaces/__init__.py:182
 msgid "Exclude context?"
 msgstr ""
 
-#: ./src/pyams_content/shared/view/interfaces/__init__.py:167
-msgid "If 'yes', context will be excluded from results list"
-msgstr ""
-
-#: ./src/pyams_content/shared/view/interfaces/__init__.py:178
-msgid "Select context tags?"
-msgstr ""
-
-#: ./src/pyams_content/shared/view/interfaces/__init__.py:179
-msgid "If 'yes', tags will be extracted from context"
-msgstr ""
-
 #: ./src/pyams_content/shared/view/interfaces/__init__.py:183
-msgid "Other tags"
+msgid "If 'yes', context will be excluded from results list"
+msgstr ""
+
+#: ./src/pyams_content/shared/view/interfaces/__init__.py:194
+msgid "Select context tags?"
+msgstr ""
+
+#: ./src/pyams_content/shared/view/interfaces/__init__.py:195
+msgid "If 'yes', tags will be extracted from context"
 msgstr ""
 
 #: ./src/pyams_content/shared/view/interfaces/__init__.py:199
+msgid "Other tags"
+msgstr ""
+
+#: ./src/pyams_content/shared/view/interfaces/__init__.py:215
 msgid "Select context themes?"
 msgstr ""
 
-#: ./src/pyams_content/shared/view/interfaces/__init__.py:200
+#: ./src/pyams_content/shared/view/interfaces/__init__.py:216
 msgid "If 'yes', themes will be extracted from context"
 msgstr ""
 
-#: ./src/pyams_content/shared/view/interfaces/__init__.py:204
-msgid "Other themes"
-msgstr ""
-
 #: ./src/pyams_content/shared/view/interfaces/__init__.py:220
+msgid "Other themes"
+msgstr ""
+
+#: ./src/pyams_content/shared/view/interfaces/__init__.py:236
 msgid "Select context collections?"
 msgstr ""
 
-#: ./src/pyams_content/shared/view/interfaces/__init__.py:221
+#: ./src/pyams_content/shared/view/interfaces/__init__.py:237
 msgid "If 'yes', collections will be extracted from context"
 msgstr ""
 
-#: ./src/pyams_content/shared/view/interfaces/__init__.py:225
+#: ./src/pyams_content/shared/view/interfaces/__init__.py:241
 msgid "Other collections"
 msgstr ""
 
-#: ./src/pyams_content/shared/imagemap/paragraph.py:99
+#: ./src/pyams_content/shared/imagemap/paragraph.py:88
 msgid "no selected image map"
 msgstr ""
 
-#: ./src/pyams_content/shared/imagemap/paragraph.py:105
+#: ./src/pyams_content/shared/imagemap/paragraph.py:94
 #, python-format
 msgid "image map '{0}' can't be found"
 msgstr ""
 
-#: ./src/pyams_content/shared/imagemap/paragraph.py:113
+#: ./src/pyams_content/shared/imagemap/paragraph.py:102
 #, python-format
 msgid "image map '{0}' is not published"
 msgstr ""
@@ -3746,7 +3821,7 @@
 msgstr ""
 
 #: ./src/pyams_content/shared/imagemap/interfaces/__init__.py:96
-#: ./src/pyams_content/features/alert/interfaces.py:73
+#: ./src/pyams_content/features/alert/interfaces.py:69
 #: ./src/pyams_content/features/menu/interfaces/__init__.py:68
 msgid "Internal reference"
 msgstr ""
@@ -3759,7 +3834,7 @@
 msgid "Image map template"
 msgstr ""
 
-#: ./src/pyams_content/shared/site/folder.py:59
+#: ./src/pyams_content/shared/site/folder.py:62
 msgid "Site folder"
 msgstr ""
 
@@ -3772,39 +3847,43 @@
 msgid "Site manager"
 msgstr ""
 
-#: ./src/pyams_content/shared/site/zmi/folder.py:61
+#: ./src/pyams_content/shared/site/zmi/folder.py:62
 msgid "Add site folder..."
 msgstr ""
 
-#: ./src/pyams_content/shared/site/zmi/folder.py:93
+#: ./src/pyams_content/shared/site/zmi/folder.py:94
 msgid "Add site folder"
 msgstr ""
 
-#: ./src/pyams_content/shared/site/zmi/folder.py:160
+#: ./src/pyams_content/shared/site/zmi/folder.py:161
 msgid "Site folder management"
 msgstr ""
 
-#: ./src/pyams_content/shared/site/zmi/folder.py:188
+#: ./src/pyams_content/shared/site/zmi/folder.py:190
 msgid "Site folder properties"
 msgstr ""
 
-#: ./src/pyams_content/shared/site/zmi/folder.py:71
+#: ./src/pyams_content/shared/site/zmi/folder.py:208
+msgid "Navigation properties"
+msgstr ""
+
+#: ./src/pyams_content/shared/site/zmi/folder.py:72
 #: ./src/pyams_content/interfaces/__init__.py:102
 msgid "Visible label used to display content"
 msgstr ""
 
-#: ./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:66
-msgid "Parent"
-msgstr ""
-
 #: ./src/pyams_content/shared/site/zmi/folder.py:75
-#: ./src/pyams_content/shared/site/zmi/link.py:67
+#: ./src/pyams_content/shared/site/zmi/__init__.py:72
+#: ./src/pyams_content/shared/site/zmi/link.py:65
+msgid "Parent"
+msgstr ""
+
+#: ./src/pyams_content/shared/site/zmi/folder.py:76
+#: ./src/pyams_content/shared/site/zmi/link.py:66
 msgid "Folder's parent"
 msgstr ""
 
-#: ./src/pyams_content/shared/site/zmi/folder.py:153
+#: ./src/pyams_content/shared/site/zmi/folder.py:154
 msgid "You must provide a folder name for default server language!"
 msgstr ""
 
@@ -3824,15 +3903,15 @@
 msgid "Topic's parent"
 msgstr ""
 
-#: ./src/pyams_content/shared/site/zmi/link.py:57
+#: ./src/pyams_content/shared/site/zmi/link.py:56
 msgid "Rent content..."
 msgstr ""
 
-#: ./src/pyams_content/shared/site/zmi/link.py:77
+#: ./src/pyams_content/shared/site/zmi/link.py:76
 msgid "Rent existing content"
 msgstr ""
 
-#: ./src/pyams_content/shared/site/zmi/link.py:135
+#: ./src/pyams_content/shared/site/zmi/link.py:134
 msgid "Edit content link properties"
 msgstr ""
 
@@ -3910,53 +3989,82 @@
 msgid "A site manager is already registered with this name!!"
 msgstr ""
 
-#: ./src/pyams_content/shared/site/interfaces/__init__.py:96
+#: ./src/pyams_content/shared/site/interfaces/__init__.py:121
 msgid "Topic"
 msgstr ""
 
-#: ./src/pyams_content/shared/site/interfaces/__init__.py:59
+#: ./src/pyams_content/shared/site/interfaces/__init__.py:42
+msgid "Redirect to first visible sub-folder or content"
+msgstr ""
+
+#: ./src/pyams_content/shared/site/interfaces/__init__.py:43
+msgid "Use presentation template"
+msgstr ""
+
+#: ./src/pyams_content/shared/site/interfaces/__init__.py:72
 msgid "Heading"
 msgstr ""
 
-#: ./src/pyams_content/shared/site/interfaces/__init__.py:60
+#: ./src/pyams_content/shared/site/interfaces/__init__.py:73
 msgid "Heading displayed according to presentation template"
 msgstr ""
 
-#: ./src/pyams_content/shared/site/interfaces/__init__.py:63
+#: ./src/pyams_content/shared/site/interfaces/__init__.py:80
+msgid "Visible in folders list"
+msgstr ""
+
+#: ./src/pyams_content/shared/site/interfaces/__init__.py:81
+msgid "If 'no', folder will not be displayed into folders list"
+msgstr ""
+
+#: ./src/pyams_content/shared/site/interfaces/__init__.py:85
+#: ./src/pyams_content/shared/site/interfaces/__init__.py:139
 msgid "Navigation title"
 msgstr ""
 
-#: ./src/pyams_content/shared/site/interfaces/__init__.py:64
-msgid "Title displayed in navigation items"
-msgstr ""
-
-#: ./src/pyams_content/shared/site/interfaces/__init__.py:115
-msgid "Content title, as shown in front-office"
-msgstr ""
-
-#: ./src/pyams_content/shared/site/interfaces/__init__.py:119
+#: ./src/pyams_content/shared/site/interfaces/__init__.py:86
+msgid ""
+"Folder's title displayed in navigation pages; original title will be used if "
+"none is specified"
+msgstr ""
+
+#: ./src/pyams_content/shared/site/interfaces/__init__.py:90
+msgid "Navigation mode"
+msgstr ""
+
+#: ./src/pyams_content/shared/site/interfaces/__init__.py:91
+msgid "Folder behaviour when navigating to folder URL"
+msgstr ""
+
+#: ./src/pyams_content/shared/site/interfaces/__init__.py:140
+msgid ""
+"Alternate content's title displayed in navigation pages; original title will "
+"be used if none is specified"
+msgstr ""
+
+#: ./src/pyams_content/shared/site/interfaces/__init__.py:145
 msgid "If 'no', link is not visible"
 msgstr ""
 
-#: ./src/pyams_content/shared/logo/paragraph.py:96
+#: ./src/pyams_content/shared/logo/paragraph.py:95
 msgid "no selected logo"
 msgstr ""
 
-#: ./src/pyams_content/shared/logo/paragraph.py:102
+#: ./src/pyams_content/shared/logo/paragraph.py:101
 #, python-format
 msgid "logo '{0}' can't be found"
 msgstr ""
 
-#: ./src/pyams_content/shared/logo/paragraph.py:110
+#: ./src/pyams_content/shared/logo/paragraph.py:109
 #, python-format
 msgid "logo '{0}' is not published"
 msgstr ""
 
-#: ./src/pyams_content/shared/logo/__init__.py:68
+#: ./src/pyams_content/shared/logo/__init__.py:69
 msgid "no image defined"
 msgstr ""
 
-#: ./src/pyams_content/shared/logo/__init__.py:71
+#: ./src/pyams_content/shared/logo/__init__.py:72
 msgid "no URL defined"
 msgstr ""
 
@@ -4018,16 +4126,16 @@
 msgid "Logos template"
 msgstr ""
 
-#: ./src/pyams_content/shared/blog/zmi/__init__.py:52
+#: ./src/pyams_content/shared/blog/zmi/__init__.py:53
 msgid "This blog post"
 msgstr ""
 
-#: ./src/pyams_content/shared/blog/zmi/__init__.py:71
-#: ./src/pyams_content/shared/blog/zmi/__init__.py:81
+#: ./src/pyams_content/shared/blog/zmi/__init__.py:72
+#: ./src/pyams_content/shared/blog/zmi/__init__.py:82
 msgid "Add blog post"
 msgstr ""
 
-#: ./src/pyams_content/shared/blog/zmi/__init__.py:62
+#: ./src/pyams_content/shared/blog/zmi/__init__.py:63
 #, python-format
 msgid "Blog post « {title} »"
 msgstr ""
@@ -4077,7 +4185,7 @@
 msgid "Default length used for inner tables and dashboards"
 msgstr ""
 
-#: ./src/pyams_content/root/__init__.py:68
+#: ./src/pyams_content/root/__init__.py:69
 msgid "Site root"
 msgstr ""
 
@@ -4101,7 +4209,7 @@
 msgid "Given element name doesn't exist!"
 msgstr ""
 
-#: ./src/pyams_content/root/zmi/search.py:155
+#: ./src/pyams_content/root/zmi/search.py:160
 msgid "Content types"
 msgstr ""
 
@@ -4574,12 +4682,12 @@
 msgid "List of selected pictograms which will be available to shared contents"
 msgstr ""
 
-#: ./src/pyams_content/features/renderer/zmi/__init__.py:70
+#: ./src/pyams_content/features/renderer/zmi/__init__.py:72
 #: ./src/pyams_content/features/renderer/zmi/templates/renderer-input.pt:4
 msgid "Edit renderer properties"
 msgstr ""
 
-#: ./src/pyams_content/features/renderer/skin/__init__.py:67
+#: ./src/pyams_content/features/renderer/skin/__init__.py:71
 msgid "Hidden content"
 msgstr ""
 
@@ -4624,8 +4732,12 @@
 msgid "preview"
 msgstr ""
 
+#: ./src/pyams_content/features/alert/interfaces.py:39
+msgid "Alert"
+msgstr ""
+
 #: ./src/pyams_content/features/alert/interfaces.py:40
-msgid "Success"
+msgid "End of alert"
 msgstr ""
 
 #: ./src/pyams_content/features/alert/interfaces.py:41
@@ -4637,7 +4749,7 @@
 msgstr ""
 
 #: ./src/pyams_content/features/alert/interfaces.py:43
-msgid "Danger"
+msgid "Recommendation"
 msgstr ""
 
 #: ./src/pyams_content/features/alert/interfaces.py:55
@@ -4652,52 +4764,42 @@
 msgid "Alert gravity will affect rendered alert style"
 msgstr ""
 
-#. Default: Heading
 #: ./src/pyams_content/features/alert/interfaces.py:65
-#: ./src/pyams_content/features/alert/zmi/container.py:157
-msgid "alert-header"
+#: ./src/pyams_content/features/alert/zmi/container.py:145
+msgid "Message"
 msgstr ""
 
 #: ./src/pyams_content/features/alert/interfaces.py:66
-msgid "Short alert header (Alert, Important...)"
-msgstr ""
-
-#: ./src/pyams_content/features/alert/interfaces.py:69
-#: ./src/pyams_content/features/alert/zmi/container.py:169
-msgid "Message"
+msgid "Alert message"
 msgstr ""
 
 #: ./src/pyams_content/features/alert/interfaces.py:70
-msgid "Alert message"
-msgstr ""
-
-#: ./src/pyams_content/features/alert/interfaces.py:74
 msgid ""
 "Internal link target reference. You can search a reference using '+' followed"
 " by internal number, of by entering text matching content title."
 msgstr ""
 
-#: ./src/pyams_content/features/alert/interfaces.py:86
+#: ./src/pyams_content/features/alert/interfaces.py:75
 msgid "Display start date"
 msgstr ""
 
-#: ./src/pyams_content/features/alert/interfaces.py:87
+#: ./src/pyams_content/features/alert/interfaces.py:76
 msgid "First date at which alert should be displayed"
 msgstr ""
 
-#: ./src/pyams_content/features/alert/interfaces.py:90
+#: ./src/pyams_content/features/alert/interfaces.py:79
 msgid "Display end date"
 msgstr ""
 
-#: ./src/pyams_content/features/alert/interfaces.py:91
+#: ./src/pyams_content/features/alert/interfaces.py:80
 msgid "Last date at which alert should be displayed"
 msgstr ""
 
-#: ./src/pyams_content/features/alert/interfaces.py:94
+#: ./src/pyams_content/features/alert/interfaces.py:83
 msgid "Maximum interval"
 msgstr ""
 
-#: ./src/pyams_content/features/alert/interfaces.py:95
+#: ./src/pyams_content/features/alert/interfaces.py:84
 msgid ""
 "Maximum interval between alert displays on a given device, given in hours; "
 "set to 0 to always display the alert"
@@ -4719,14 +4821,193 @@
 msgid "Alerts"
 msgstr ""
 
-#: ./src/pyams_content/features/alert/zmi/container.py:191
+#: ./src/pyams_content/features/alert/zmi/container.py:167
 msgid "Alert list"
 msgstr ""
 
-#: ./src/pyams_content/features/alert/zmi/container.py:90
+#: ./src/pyams_content/features/alert/zmi/container.py:97
 msgid "No currently defined alert."
 msgstr ""
 
+#: ./src/pyams_content/features/redirect/container.py:81
+msgid "not matching"
+msgstr ""
+
+#: ./src/pyams_content/features/redirect/zmi/__init__.py:50
+msgid "Add rule"
+msgstr ""
+
+#: ./src/pyams_content/features/redirect/zmi/__init__.py:63
+msgid "Add new redirection rule"
+msgstr ""
+
+#: ./src/pyams_content/features/redirect/zmi/__init__.py:88
+msgid "Edit redirection rule properties"
+msgstr ""
+
+#: ./src/pyams_content/features/redirect/zmi/__init__.py:109
+msgid ""
+"URL pattern and target URL are defined by *regular expressions* (see |regexp|).\n"
+"    \n"
+"In URL pattern, you can use any valid regular expression element, notably:\n"
+"\n"
+"- « .* » to match any list of characters \n"
+"\n"
+"- « ( ) » to \"memorize\" parts of the URL which can be replaced into target URL\n"
+"\n"
+"- special characters (like \"+\") must be escaped with an « \\\\ ».\n"
+"\n"
+"In target URL, memorized parts can be reused using « \\\\1 », « \\\\2 » and so on, where given number is\n"
+"the order of the matching pattern element.\n"
+"\n"
+".. |regexp| raw:: html\n"
+"\n"
+"    <a href=\"https://docs.python.org/3/library/re.html\" target=\"_blank\">Python Regular Expressions</a>\n"
+msgstr ""
+
+#: ./src/pyams_content/features/redirect/zmi/container.py:67
+msgid "Redirections"
+msgstr ""
+
+#: ./src/pyams_content/features/redirect/zmi/container.py:161
+msgid "Enable/disable rule"
+msgstr ""
+
+#: ./src/pyams_content/features/redirect/zmi/container.py:188
+msgid "Chain/unchain rule"
+msgstr ""
+
+#: ./src/pyams_content/features/redirect/zmi/container.py:210
+#: ./src/pyams_content/features/redirect/zmi/container.py:366
+#: ./src/pyams_content/features/redirect/interfaces/__init__.py:56
+msgid "URL pattern"
+msgstr ""
+
+#: ./src/pyams_content/features/redirect/zmi/container.py:220
+msgid "Target"
+msgstr ""
+
+#: ./src/pyams_content/features/redirect/zmi/container.py:246
+msgid "Redirections list"
+msgstr ""
+
+#: ./src/pyams_content/features/redirect/zmi/container.py:261
+msgid "Redirection rules"
+msgstr ""
+
+#: ./src/pyams_content/features/redirect/zmi/container.py:262
+msgid ""
+"Redirection rules are use to handle redirections responses when a request generates \n"
+"a famous « 404 NotFound » error.\n"
+"\n"
+"Redirections are particularly useful when you are migrating from a previous site and don't want to lose \n"
+"your SEO.\n"
+"\n"
+"You can define a set of rules which will be applied to every \"NotFound\" request; rules are based on \n"
+"regular expressions which are applied to input URL: if the rule is \"matching\", the target URL is rewritten\n"
+"and a \"Redirect\" response is send.\n"
+"\n"
+"You can chain rules together: when a rule is chained, it's rewritten URL is passed as input URL to the \n"
+"next rule, until a matching rule is found.\n"
+msgstr ""
+
+#: ./src/pyams_content/features/redirect/zmi/container.py:288
+msgid "Test"
+msgstr ""
+
+#: ./src/pyams_content/features/redirect/zmi/container.py:323
+msgid "Test redirection rules"
+msgstr ""
+
+#: ./src/pyams_content/features/redirect/zmi/container.py:301
+msgid "Test URL"
+msgstr ""
+
+#: ./src/pyams_content/features/redirect/zmi/container.py:304
+msgid "Check inactive rules?"
+msgstr ""
+
+#: ./src/pyams_content/features/redirect/zmi/container.py:305
+msgid "If 'yes', inactive rules will also be tested"
+msgstr ""
+
+#: ./src/pyams_content/features/redirect/zmi/container.py:313
+msgid "Close"
+msgstr ""
+
+#: ./src/pyams_content/features/redirect/zmi/container.py:314
+msgid "Test rules"
+msgstr ""
+
+#: ./src/pyams_content/features/redirect/zmi/container.py:123
+msgid "No currently defined redirection rule."
+msgstr ""
+
+#: ./src/pyams_content/features/redirect/zmi/container.py:371
+msgid "No matching rule!"
+msgstr ""
+
+#: ./src/pyams_content/features/redirect/zmi/container.py:365
+msgid "Input URL"
+msgstr ""
+
+#: ./src/pyams_content/features/redirect/zmi/container.py:367
+msgid "Output URL"
+msgstr ""
+
+#: ./src/pyams_content/features/redirect/interfaces/__init__.py:39
+msgid "Active rule?"
+msgstr ""
+
+#: ./src/pyams_content/features/redirect/interfaces/__init__.py:40
+msgid "If 'no', selected rule is inactive"
+msgstr ""
+
+#: ./src/pyams_content/features/redirect/interfaces/__init__.py:44
+msgid "Chained rule?"
+msgstr ""
+
+#: ./src/pyams_content/features/redirect/interfaces/__init__.py:45
+msgid ""
+"If 'no', and if this rule is matching received request URL, the rule returns "
+"a redirection response; otherwise, the rule just rewrites the input URL which"
+" is forwarded to the next rule"
+msgstr ""
+
+#: ./src/pyams_content/features/redirect/interfaces/__init__.py:51
+msgid "Permanent redirect?"
+msgstr ""
+
+#: ./src/pyams_content/features/redirect/interfaces/__init__.py:52
+msgid "Define if this redirection should be permanent or temporary"
+msgstr ""
+
+#: ./src/pyams_content/features/redirect/interfaces/__init__.py:57
+msgid "Regexp pattern of matching URLs for this redirection rule"
+msgstr ""
+
+#: ./src/pyams_content/features/redirect/interfaces/__init__.py:62
+msgid "Internal redirection target"
+msgstr ""
+
+#: ./src/pyams_content/features/redirect/interfaces/__init__.py:63
+msgid ""
+"Internal redirection reference. You can search a reference using '+' followed"
+" by internal number, of by entering text matching content title."
+msgstr ""
+
+#: ./src/pyams_content/features/redirect/interfaces/__init__.py:69
+msgid "URL to which source URL should be redirected"
+msgstr ""
+
+#: ./src/pyams_content/features/redirect/interfaces/__init__.py:75
+msgid "You can only provide an internal reference OR a target URL"
+msgstr ""
+
+#: ./src/pyams_content/features/redirect/interfaces/__init__.py:77
+msgid "You must provide an internal reference OR a target URL"
+msgstr ""
+
 #: ./src/pyams_content/features/menu/zmi/__init__.py:81
 msgid "Add menu..."
 msgstr ""
@@ -4743,7 +5024,7 @@
 msgid "Menu was correctly added."
 msgstr ""
 
-#: ./src/pyams_content/features/menu/zmi/__init__.py:388
+#: ./src/pyams_content/features/menu/zmi/__init__.py:395
 msgid "Link was correctly added."
 msgstr ""
 
@@ -4751,11 +5032,11 @@
 msgid "Click to see menu items"
 msgstr ""
 
-#: ./src/pyams_content/features/menu/portlet/navigation/simple.py:68
+#: ./src/pyams_content/features/menu/portlet/navigation/simple.py:67
 msgid "Simple navigation"
 msgstr ""
 
-#: ./src/pyams_content/features/menu/portlet/navigation/double.py:68
+#: ./src/pyams_content/features/menu/portlet/navigation/double.py:67
 msgid "Double navigation"
 msgstr ""
 
@@ -4767,8 +5048,8 @@
 msgid "Navigation menus"
 msgstr ""
 
-#: ./src/pyams_content/features/menu/portlet/navigation/zmi/templates/double-preview.pt:15
-#: ./src/pyams_content/features/menu/portlet/navigation/zmi/templates/simple-preview.pt:12
+#: ./src/pyams_content/features/menu/portlet/navigation/zmi/templates/double-preview.pt:14
+#: ./src/pyams_content/features/menu/portlet/navigation/zmi/templates/simple-preview.pt:11
 msgid "Link has no illustration"
 msgstr ""
 
@@ -4777,16 +5058,6 @@
 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:64
 msgid "Menu title"
 msgstr ""
@@ -4821,7 +5092,7 @@
 msgid "Don't inherit parent footer"
 msgstr ""
 
-#: ./src/pyams_content/features/footer/skin/__init__.py:84
+#: ./src/pyams_content/features/footer/skin/__init__.py:94
 msgid "Hidden footer"
 msgstr ""
 
@@ -5037,6 +5308,6 @@
 msgid "Don't inherit parent header"
 msgstr ""
 
-#: ./src/pyams_content/features/header/skin/__init__.py:90
+#: ./src/pyams_content/features/header/skin/__init__.py:100
 msgid "Hidden header"
 msgstr ""
--- a/src/pyams_content/reference/zmi/table.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/reference/zmi/table.py	Thu Sep 06 11:27:55 2018 +0200
@@ -58,7 +58,7 @@
 
     @property
     def label(self):
-        return II18n(self.context).query_attribute('short_name', request=self.request)
+        return II18n(self.context).query_attribute('title', request=self.request)
 
     css_class = 'strong'
 
--- a/src/pyams_content/root/__init__.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/root/__init__.py	Thu Sep 06 11:27:55 2018 +0200
@@ -24,14 +24,14 @@
 from pyams_content.features.footer.interfaces import IFooterTarget
 from pyams_content.features.header.interfaces import IHeaderTarget
 from pyams_content.features.preview.interfaces import IPreviewTarget
-from pyams_content.interfaces import WEBMASTER_ROLE, OPERATOR_ROLE
+from pyams_content.features.redirect.interfaces import IRedirectionManagerTarget
+from pyams_content.interfaces import WEBMASTER_ROLE, OPERATOR_ROLE, MANAGE_SITE_ROOT_PERMISSION
 from pyams_content.root.interfaces import ISiteRootRoles, ISiteRootConfiguration, ISiteRoot, \
     ISiteRootToolsConfiguration, ISiteRootBackOfficeConfiguration
 from pyams_form.interfaces.form import IFormContextPermissionChecker
 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 ISiteRootFactory
 
 # import packages
@@ -52,7 +52,8 @@
 
 
 @implementer(IDefaultProtectionPolicy, ISiteRoot, ISiteRootRoles, IPortalContext, ITagsManagerTarget,
-             IIllustrationTarget, IHeaderTarget, IFooterTarget, IAlertTarget, IPreviewTarget)
+             IIllustrationTarget, IHeaderTarget, IFooterTarget, IAlertTarget, IRedirectionManagerTarget,
+             IPreviewTarget)
 class SiteRoot(ProtectedObject, BaseSiteRoot, UserSkinnableContent):
     """Main site root"""
 
@@ -110,9 +111,7 @@
 class SiteRootPermissionChecker(ContextAdapter):
     """Site root permission checker"""
 
-    @property
-    def edit_permission(self):
-        return MANAGE_SYSTEM_PERMISSION
+    edit_permission = MANAGE_SITE_ROOT_PERMISSION
 
 
 #
--- a/src/pyams_content/root/zmi/__init__.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/root/zmi/__init__.py	Thu Sep 06 11:27:55 2018 +0200
@@ -343,7 +343,7 @@
 
     icon_class = 'fa fa-fw fa-star'
 
-    title = _("Your favorites")
+    title = _("Your favorite contents")
 
 
 #
--- a/src/pyams_content/root/zmi/search.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/root/zmi/search.py	Thu Sep 06 11:27:55 2018 +0200
@@ -17,8 +17,10 @@
 
 # import interfaces
 from hypatia.interfaces import ICatalog
+from pyams_content.component.theme.interfaces import ITagsManager
 from pyams_content.profile.interfaces import IAdminProfile
 from pyams_content.root import ISiteRoot
+from pyams_content.shared.common.interfaces import CONTENT_TYPES_VOCABULARY
 from pyams_content.shared.common.interfaces.zmi import ISiteRootDashboardTable
 from pyams_content.skin.zmi.interfaces import IAllContentsMenu
 from pyams_form.search import ISearchFields, SearchForm, SearchView, SearchResultsView
@@ -45,6 +47,8 @@
 from pyams_skin.table import BaseTable
 from pyams_skin.viewlet.menu import MenuItem
 from pyams_template.template import template_config
+from pyams_thesaurus.schema import ThesaurusTermsListField
+from pyams_thesaurus.zmi.widget import ThesaurusTermsTreeFieldWidget
 from pyams_utils.adapter import adapter_config, ContextRequestViewAdapter
 from pyams_utils.list import unique
 from pyams_utils.registry import get_utility
@@ -91,12 +95,13 @@
     title = _("Quick search results")
 
     sortOn = None
+    dt_sort_order = 'desc'
 
     @property
     def data_attributes(self):
         attributes = super(SiteRootQuickSearchResults, self).data_attributes
         attributes['table'] = {
-            'data-ams-datatable-sorting': '[]',
+            'data-ams-datatable-sorting': '{0},{1}'.format(len(self.columns) - 1, self.dt_sort_order),
             'data-ams-datatable-display-length': IAdminProfile(self.request.principal).table_page_length
         }
         return attributes
@@ -153,7 +158,7 @@
     """Site root advanced search fields"""
 
     content_type = List(title=_("Content types"),
-                        value_type=Choice(vocabulary='PyAMS content types'),
+                        value_type=Choice(vocabulary=CONTENT_TYPES_VOCABULARY),
                         required=False)
 
     owner = Principal(title=_("Owner"),
@@ -171,6 +176,9 @@
     modified_before = Datetime(title=_("Modified before..."),
                                required=False)
 
+    tags = ThesaurusTermsListField(title=_("Tags"),
+                                   required=False)
+
 
 @template_config(template='templates/advanced-search.pt', layer=IPyAMSLayer)
 @implementer(IInnerPage)
@@ -185,8 +193,17 @@
         apply_skin(self.request, 'PyAMS admin skin')
 
     fields = field.Fields(ISiteRootAdvancedSearchFields)
+    fields['tags'].widgetFactory = ThesaurusTermsTreeFieldWidget
     ajax_handler = 'advanced-search-results.html'
 
+    def updateWidgets(self, prefix=None):
+        super(SiteRootAdvancedSearchForm, self).updateWidgets(prefix)
+        if 'tags' in self.widgets:
+            widget = self.widgets['tags']
+            manager = ITagsManager(self.request.root)
+            widget.thesaurus_name = manager.thesaurus_name
+            widget.extract_name = manager.extract_name
+
 
 @adapter_config(context=(ISiteRoot, IPyAMSLayer, SiteRootAdvancedSearchForm), provides=IContentSearch)
 class SiteRootAdvancedSearchFormSearchAdapter(ContextRequestViewAdapter):
@@ -227,6 +244,9 @@
             params &= Ge(catalog['modified_date'], data['modified_after'])
         if data.get('modified_before'):
             params &= Le(catalog['modified_date'], data['modified_before'])
+        if data.get('tags'):
+            tags = [intids.register(term) for term in data['tags']]
+            params &= Any(catalog['tags'], tags)
         return unique(map(lambda x: IWorkflowVersions(x).get_last_versions()[0],
                           CatalogResultSet(CatalogQuery(catalog).query(params,
                                                                        sort_index='modified_date',
--- a/src/pyams_content/root/zmi/templates/advanced-search.pt	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/root/zmi/templates/advanced-search.pt	Thu Sep 06 11:27:55 2018 +0200
@@ -73,12 +73,12 @@
 									</span>
 								</label>
 								<div class="col-md-9">
-									<label class="input"
-										   tal:attributes="class widget.widget_css_class | default;
-														   data-ams-data tales:object_data(widget);
-														   data-ams-form-validator view.get_widget_callback(widget.field.getName())">
+									<div class="input"
+										 tal:attributes="class widget.widget_css_class | default;
+														 data-ams-data tales:object_data(widget);
+														 data-ams-form-validator view.get_widget_callback(widget.field.getName())">
 										<input tal:replace="structure widget.render()" />
-									</label>
+									</div>
 								</div>
 							</div>
 							<div class="form-group">
@@ -94,16 +94,18 @@
 										</span>
 									</label>
 									<div class="col-md-4">
-										<label class="input"
-											   tal:attributes="class widget.widget_css_class | default;
-															   data-ams-data tales:object_data(widget);
-															   data-ams-form-validator view.get_widget_callback(widget.field.getName())">
+										<div class="input"
+											 tal:attributes="class widget.widget_css_class | default;
+															 data-ams-data tales:object_data(widget);
+															 data-ams-form-validator view.get_widget_callback(widget.field.getName())">
 											<input tal:replace="structure widget.render()" />
-										</label>
+										</div>
 									</div>
 								</tal:var>
+							</div>
+							<div class="form-group">
 								<tal:var define="widget view.widgets['content_type']">
-									<label class="control-label col-md-2">
+									<label class="control-label col-md-3">
 										<span>
 											<tal:var content="widget.label" />
 											<i class="fa fa-question-circle hint" title="Input hint"
@@ -113,13 +115,13 @@
 															   data-ams-hint-html '<' in description;"></i>
 										</span>
 									</label>
-									<div class="col-md-3">
-										<label class="input"
-											   tal:attributes="class widget.widget_css_class | default;
-															   data-ams-data tales:object_data(widget);
-															   data-ams-form-validator view.get_widget_callback(widget.field.getName())">
+									<div class="col-md-4">
+										<div class="input"
+											 tal:attributes="class widget.widget_css_class | default;
+															 data-ams-data tales:object_data(widget);
+															 data-ams-form-validator view.get_widget_callback(widget.field.getName())">
 											<input tal:replace="structure widget.render()" />
-										</label>
+										</div>
 									</div>
 								</tal:var>
 							</div>
@@ -128,25 +130,25 @@
 									<span i18n:translate="">Created between</span>
 								</label>
 								<div class="col-md-4">
-									<label class="input"
-											tal:define="widget view.widgets['created_after']"
-										   tal:attributes="class widget.widget_css_class | default;
-														   data-ams-data tales:object_data(widget);
-														   data-ams-form-validator view.get_widget_callback(widget.field.getName())">
+									<div class="input"
+										 tal:define="widget view.widgets['created_after']"
+										 tal:attributes="class widget.widget_css_class | default;
+														 data-ams-data tales:object_data(widget);
+														 data-ams-form-validator view.get_widget_callback(widget.field.getName())">
 										<input tal:replace="structure widget.render()" />
-									</label>
+									</div>
 								</div>
 								<div class="control-label col-md-1 text-align-center">
 									<i18n:var translate=""> and </i18n:var>
 								</div>
 								<div class="col-md-4">
-									<label class="input"
-											tal:define="widget view.widgets['created_before']"
-										   tal:attributes="class widget.widget_css_class | default;
-														   data-ams-data tales:object_data(widget);
-														   data-ams-form-validator view.get_widget_callback(widget.field.getName())">
+									<div class="input"
+										 tal:define="widget view.widgets['created_before']"
+										 tal:attributes="class widget.widget_css_class | default;
+														 data-ams-data tales:object_data(widget);
+														 data-ams-form-validator view.get_widget_callback(widget.field.getName())">
 										<input tal:replace="structure widget.render()" />
-									</label>
+									</div>
 								</div>
 							</div>
 							<div class="form-group">
@@ -154,27 +156,34 @@
 									<span i18n:translate="">Modified between</span>
 								</label>
 								<div class="col-md-4">
-									<label class="input"
-											tal:define="widget view.widgets['modified_after']"
-										   tal:attributes="class widget.widget_css_class | default;
-														   data-ams-data tales:object_data(widget);
-														   data-ams-form-validator view.get_widget_callback(widget.field.getName())">
+									<div class="input"
+										 tal:define="widget view.widgets['modified_after']"
+										 tal:attributes="class widget.widget_css_class | default;
+														 data-ams-data tales:object_data(widget);
+														 data-ams-form-validator view.get_widget_callback(widget.field.getName())">
 										<input tal:replace="structure widget.render()" />
-									</label>
+									</div>
 								</div>
 								<div class="control-label col-md-1 text-align-center">
 									<i18n:var translate=""> and </i18n:var>
 								</div>
 								<div class="col-md-4">
-									<label class="input"
-											tal:define="widget view.widgets['modified_before']"
-										   tal:attributes="class widget.widget_css_class | default;
-														   data-ams-data tales:object_data(widget);
-														   data-ams-form-validator view.get_widget_callback(widget.field.getName())">
+									<div class="input"
+										 tal:define="widget view.widgets['modified_before']"
+										 tal:attributes="class widget.widget_css_class | default;
+														 data-ams-data tales:object_data(widget);
+														 data-ams-form-validator view.get_widget_callback(widget.field.getName())">
 										<input tal:replace="structure widget.render()" />
-									</label>
+									</div>
 								</div>
 							</div>
+							<fieldset tal:condition="'tags' in view.widgets">
+								<legend class="switcher" i18n:translate="">Tags</legend>
+								<div class="input"
+									 tal:define="widget view.widgets['tags']">
+									<input tal:replace="structure widget.render()" />
+								</div>
+							</fieldset>
 						</fieldset>
 					</tal:loop>
 					<div class="widgets-suffix"
--- a/src/pyams_content/root/zmi/templates/dashboard.pt	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/root/zmi/templates/dashboard.pt	Thu Sep 06 11:27:55 2018 +0200
@@ -22,13 +22,13 @@
 					  data-async data-ams-form-submit-target="#search_results">
 					<div class="form-group">
 						<div class="col-md-6">
-							<label class="input">
+							<div 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"
 									   data-ams-events-handlers='{"keyup": "MyAMS.helpers.clearSearchTarget"}' />
-							</label>
+							</div>
 						</div>
 						<div class="col-md-6">
 							<a class="nowrap btn-sm col-md-2" href="#advanced-search.html"
--- a/src/pyams_content/shared/blog/zmi/__init__.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/shared/blog/zmi/__init__.py	Thu Sep 06 11:27:55 2018 +0200
@@ -19,7 +19,7 @@
 # import interfaces
 from pyams_content.interfaces import CREATE_CONTENT_PERMISSION
 from pyams_content.shared.blog.interfaces import IWfBlogPost, IBlogManager
-from pyams_i18n.interfaces import II18n, II18nManager
+from pyams_i18n.interfaces import II18n, II18nManager, INegotiator
 from pyams_skin.interfaces.viewlet import IMenuHeader, IWidgetTitleViewletManager
 from pyams_skin.layer import IPyAMSLayer
 from pyams_workflow.interfaces import IWorkflowVersions, IWorkflowInfo
@@ -32,9 +32,10 @@
 from pyams_skin.interfaces import IContentTitle
 from pyams_skin.viewlet.toolbar import ToolbarAction
 from pyams_utils.adapter import adapter_config, ContextRequestViewAdapter, ContextRequestAdapter
+from pyams_utils.registry import get_utility
 from pyams_utils.traversing import get_parent
 from pyams_utils.unicode import translate_string
-from pyams_utils.url import absolute_url
+from pyams_utils.url import absolute_url, generate_url
 from pyams_viewlet.viewlet import viewlet_config
 from pyramid.decorator import reify
 from pyramid.path import DottedNameResolver
@@ -98,11 +99,13 @@
     def update_content(self, content, data):
         data = data.get(self, data)
         # initialize content fields
+        lang = get_utility(INegotiator).server_language
+        content.creator = self.request.principal.id
+        content.owner = self.request.principal.id
         content.title = data['title']
         content.short_name = content.title.copy()
+        content.content_url = generate_url(content.title.get(lang, ''))
         content.notepad = data.get('notepad')
-        content.creator = self.request.principal.id
-        content.owner = self.request.principal.id
         # check blog folders
         now = datetime.utcnow()
         year, month = now.strftime('%Y:%m').split(':')
--- a/src/pyams_content/shared/common/__init__.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/shared/common/__init__.py	Thu Sep 06 11:27:55 2018 +0200
@@ -17,47 +17,46 @@
 
 # import interfaces
 from hypatia.interfaces import ICatalog
+# import packages
+from persistent import Persistent
+from pyramid.events import subscriber
+from pyramid.interfaces import IWSGIApplicationCreatedEvent
+from pyramid.settings import asbool
+from pyramid.threadlocal import get_current_registry
+from zope.container.contained import Contained
+from zope.dublincore.interfaces import IZopeDublinCore
+from zope.interface import implementer
+from zope.intid.interfaces import IIntIds
+from zope.lifecycleevent.interfaces import IObjectModifiedEvent
+from zope.schema.fieldproperty import FieldProperty
+from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm
+
+from pyams_content import _
+from pyams_content.features.checker import BaseContentChecker
+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.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
+    IBaseSharedTool, ISharedSite, IWfSharedContentFactory, CONTENT_TYPES_VOCABULARY
+from pyams_i18n.content import I18nManagerMixin
 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
-from pyams_workflow.interfaces import IWorkflowPublicationSupport, IWorkflow, IObjectClonedEvent, IWorkflowVersions
-from pyramid.interfaces import IWSGIApplicationCreatedEvent
-from zope.dublincore.interfaces import IZopeDublinCore
-from zope.intid.interfaces import IIntIds
-from zope.lifecycleevent.interfaces import IObjectModifiedEvent
-
-# import packages
-from persistent import Persistent
-from pyams_content.features.checker import BaseContentChecker
-from pyams_i18n.content import I18nManagerMixin
 from pyams_security.property import RolePrincipalsFieldProperty
 from pyams_security.security import ProtectedObject
 from pyams_security.utility import get_principal
+from pyams_sequence.interfaces import ISequentialIdTarget, ISequentialIdInfo
 from pyams_utils.adapter import adapter_config, ContextAdapter
 from pyams_utils.date import format_datetime
+from pyams_utils.interfaces import VIEW_PERMISSION
 from pyams_utils.property import classproperty, classproperty_support
 from pyams_utils.registry import query_utility, get_utilities_for
 from pyams_utils.request import query_request, check_request
 from pyams_utils.timezone import tztime
 from pyams_utils.traversing import get_parent
 from pyams_utils.vocabulary import vocabulary_config
-from pyramid.events import subscriber
-from pyramid.settings import asbool
-from pyramid.threadlocal import get_current_registry
-from zope.container.contained import Contained
-from zope.interface import implementer
-from zope.schema.fieldproperty import FieldProperty
-from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm
-
-from pyams_content import _
+from pyams_workflow.interfaces import IWorkflowPublicationSupport, IWorkflow, IObjectClonedEvent, IWorkflowVersions
 
 
 @vocabulary_config(name='PyAMS shared sites')
@@ -94,7 +93,7 @@
             del CONTENT_TYPES[key]
 
 
-@vocabulary_config(name='PyAMS content types')
+@vocabulary_config(name=CONTENT_TYPES_VOCABULARY)
 class ContentTypesVocabulary(SimpleVocabulary):
     """Content types vocabulary"""
 
@@ -128,12 +127,15 @@
     content_type = None
     content_name = None
 
+    handle_header = True
+
     title = FieldProperty(IWfSharedContent['title'])
     short_name = FieldProperty(IWfSharedContent['short_name'])
     content_url = FieldProperty(IWfSharedContent['content_url'])
     creator = FieldProperty(IWfSharedContent['creator'])
     modifiers = FieldProperty(IWfSharedContent['modifiers'])
     last_modifier = FieldProperty(IWfSharedContent['last_modifier'])
+    header = FieldProperty(IWfSharedContent['header'])
     description = FieldProperty(IWfSharedContent['description'])
     keywords = FieldProperty(IWfSharedContent['keywords'])
     notepad = FieldProperty(IWfSharedContent['notepad'])
--- a/src/pyams_content/shared/common/interfaces/__init__.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/shared/common/interfaces/__init__.py	Thu Sep 06 11:27:55 2018 +0200
@@ -15,22 +15,21 @@
 
 # import standard library
 
-# 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_portal.interfaces import IPortalContext, DESIGNER_ROLE
-from pyams_workflow.interfaces import IWorkflowManagedContent
+from zope.container.constraints import containers, contains
 from zope.container.interfaces import IContainer
-
-# import packages
-from pyams_i18n.schema import I18nTextField
-from pyams_security.schema import Principal, PrincipalsSet
-from pyams_utils.schema import TextLineListField
-from zope.container.constraints import containers, contains
 from zope.interface import Interface, Attribute
 from zope.schema import Choice, Bool, Text, TextLine
 
 from pyams_content import _
+# 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
+# import packages
+from pyams_i18n.schema import I18nTextField
+from pyams_portal.interfaces import IPortalContext, DESIGNER_ROLE
+from pyams_security.schema import Principal, PrincipalsSet
+from pyams_utils.schema import TextLineListField
+from pyams_workflow.interfaces import IWorkflowManagedContent
 
 
 class IDeletableElement(Interface):
@@ -151,10 +150,16 @@
     last_update_label = TextLine(title=_("Last update"),
                                  readonly=True)
 
-    description = I18nTextField(title=_("Description"),
+    header = I18nTextField(title=_("Header"),
+                           description=_("Content's header is generally displayed in page header"),
+                           required=False)
+
+    handle_header = Attribute("Static boolean value to specify if header is supported by this content type")
+
+    description = I18nTextField(title=_("Meta-description"),
                                 description=_("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"),
+                                              "description; if description is empty, content's header will be used."),
                                 required=False)
 
     keywords = TextLineListField(title=_("Keywords"),
@@ -225,6 +230,9 @@
     """Workflow managed shared content factory interface"""
 
 
+CONTENT_TYPES_VOCABULARY = 'PyAMS content types'
+
+
 #
 # Generic restrictions interfaces
 #
--- a/src/pyams_content/shared/common/portlet/content/skin/__init__.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/shared/common/portlet/content/skin/__init__.py	Thu Sep 06 11:27:55 2018 +0200
@@ -12,6 +12,7 @@
 
 __docformat__ = 'restructuredtext'
 
+
 # import standard library
 
 # import interfaces
@@ -22,6 +23,7 @@
 
 # import packages
 from pyams_portal.portlet import PortletRenderer
+from pyams_template.template import template_config
 from pyams_utils.adapter import adapter_config
 from zope.interface import Interface
 
@@ -30,6 +32,7 @@
 
 @adapter_config(context=(IPortalContext, IPyAMSLayer, Interface, ISharedContentPortletSettings),
                 provides=IPortletRenderer)
+@template_config(template='templates/content.pt', layer=IPyAMSLayer)
 class SharedContentPortletRenderer(PortletRenderer):
     """Shared content portlet renderer"""
 
@@ -45,10 +48,3 @@
     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/skin/templates/content.pt	Thu Sep 06 11:27:55 2018 +0200
@@ -0,0 +1,7 @@
+<div class="edito"
+	 tal:condition="view.renderers">
+	<tal:loop repeat="renderer view.renderers">
+		<tal:if condition="renderer"
+				content="structure renderer.render()">Renderer</tal:if>
+	</tal:loop>
+</div>
--- a/src/pyams_content/shared/common/types.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/shared/common/types.py	Thu Sep 06 11:27:55 2018 +0200
@@ -21,9 +21,10 @@
 from pyams_content.component.paragraph.interfaces import IParagraphContainerTarget
 from pyams_content.component.theme.interfaces import IThemesTarget, IThemesInfo
 from pyams_content.interfaces import MANAGE_TOOL_PERMISSION
+from pyams_content.shared.common.interfaces import ISharedContentFactory
 from pyams_content.shared.common.interfaces.types import IDataType, ISubType, IBaseDataType, ITypedSharedTool, \
     ITypedDataManager, DATA_MANAGER_ANNOTATION_KEY, DATA_TYPES_VOCABULARY, DATA_TYPE_FIELDS_VOCABULARY, \
-    IWfTypedSharedContent, DATA_SUBTYPES_VOCABULARY
+    IWfTypedSharedContent, DATA_SUBTYPES_VOCABULARY, ALL_DATA_TYPES_VOCABULARY
 from pyams_form.interfaces.form import IFormContextPermissionChecker
 from pyams_i18n.interfaces import II18n
 from zope.lifecycleevent.interfaces import IObjectAddedEvent
@@ -32,9 +33,10 @@
 
 # import packages
 from persistent import Persistent
-from pyams_content.shared.common import WfSharedContent
+from pyams_content.shared.common import WfSharedContent, IWfSharedContentFactory
 from pyams_content.shared.common.manager import SharedTool
 from pyams_utils.adapter import adapter_config, ContextAdapter, get_annotation_adapter
+from pyams_utils.registry import get_local_registry
 from pyams_utils.request import check_request
 from pyams_utils.traversing import get_parent
 from pyams_utils.vocabulary import vocabulary_config
@@ -147,6 +149,43 @@
 # Data types vocabularies
 #
 
+@vocabulary_config(name=ALL_DATA_TYPES_VOCABULARY)
+class AllTypedSharedToolDataTypesVocabulary(SimpleVocabulary):
+    """Vocabulary consolidating all data types"""
+
+    def __init__(self, context):
+        terms = []
+        request = check_request()
+        registry = get_local_registry()
+        for tool in registry.getAllUtilitiesRegisteredFor(ITypedSharedTool):
+            manager = ITypedDataManager(tool)
+            terms.extend([SimpleTerm(datatype.__name__,
+                                     title=II18n(datatype).query_attribute('label', request=request))
+                          for datatype in manager.values()])
+        terms.sort(key=lambda x: x.title)
+        super(AllTypedSharedToolDataTypesVocabulary, self).__init__(terms)
+
+
+def get_all_data_types(request):
+    """Get list of all registered data types as JSON object"""
+    results = []
+    registry = get_local_registry()
+    for tool in sorted(registry.getAllUtilitiesRegisteredFor(ITypedSharedTool),
+                       key=lambda x: II18n(x).query_attribute('title', request=request)):
+        manager = ITypedDataManager(tool)
+        terms = [{
+            'id': datatype.__name__,
+            'text': II18n(datatype).query_attribute('label', request=request)
+        } for datatype in manager.values()]
+        content_factory = IWfSharedContentFactory(ISharedContentFactory(tool))
+        results.append({
+            'text': request.localizer.translate(content_factory.content_name),
+            'disabled': True,
+            'children': terms
+        })
+    return results
+
+
 @vocabulary_config(name=DATA_TYPES_VOCABULARY)
 class TypedSharedToolDataTypesVocabulary(SimpleVocabulary):
     """Typed shared tool data types vocabulary"""
--- a/src/pyams_content/shared/common/zmi/__init__.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/shared/common/zmi/__init__.py	Thu Sep 06 11:27:55 2018 +0200
@@ -87,12 +87,12 @@
         return self.context.shared_content_factory.content_class()
 
     def update_content(self, content, data):
-        # generic content update
         changes = super(SharedContentAddForm, self).update_content(content, data)
+        # initialize content fields
+        lang = get_utility(INegotiator).server_language
         content.creator = self.request.principal.id
         content.owner = self.request.principal.id
         content.short_name = content.title.copy()
-        lang = get_utility(INegotiator).server_language
         content.content_url = generate_url(content.title.get(lang, ''))
         # init content languages
         languages = II18nManager(self.context).languages
--- a/src/pyams_content/shared/common/zmi/dashboard.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/shared/common/zmi/dashboard.py	Thu Sep 06 11:27:55 2018 +0200
@@ -542,7 +542,7 @@
     def title(self):
         return II18n(self.context).query_attribute('title', request=self.request)
 
-    subtitle = _("Your favorites")
+    subtitle = _("Your favorite contents")
 
 
 #
--- a/src/pyams_content/shared/common/zmi/manager.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/shared/common/zmi/manager.py	Thu Sep 06 11:27:55 2018 +0200
@@ -56,7 +56,7 @@
 
     @property
     def label(self):
-        return II18n(self.context).query_attribute('short_name', request=self.request)
+        return II18n(self.context).query_attribute('title', request=self.request)
 
     css_class = 'strong'
 
@@ -87,7 +87,7 @@
 #
 
 @viewlet_config(name='properties.menu', context=IBaseSharedTool, layer=IAdminLayer,
-                manager=ISiteManagementMenu, permission=MANAGE_TOOL_PERMISSION, weight=40)
+                manager=ISiteManagementMenu, permission=MANAGE_TOOL_PERMISSION, weight=15)
 @viewletmanager_config(name='properties.menu', layer=IAdminLayer, provides=IPropertiesMenu)
 @implementer(IPropertiesMenu)
 class SharedToolPropertiesMenu(MenuItem):
--- a/src/pyams_content/shared/common/zmi/portal.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/shared/common/zmi/portal.py	Thu Sep 06 11:27:55 2018 +0200
@@ -15,23 +15,21 @@
 
 # import standard library
 
+from pyams_content import _
 # 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 import IWfSharedContentPortalContext, ISharedToolPortalContext
 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_form.interfaces.form import IFormHelp
 from pyams_pagelet.pagelet import pagelet_config
+from pyams_portal.interfaces import MANAGE_TEMPLATE_PERMISSION
 from pyams_portal.zmi.page import PortalContextTemplatePropertiesEditForm
+from pyams_skin.layer import IPyAMSLayer
 from pyams_utils.adapter import adapter_config
-
-from pyams_content import _
+from pyams_zmi.layer import IAdminLayer
 
 
 @pagelet_config(name='template-properties.html', context=ISharedToolPortalContext, layer=IPyAMSLayer,
@@ -69,4 +67,4 @@
 class SharedContentTemplatePropertiesEditForm(PortalContextTemplatePropertiesEditForm):
     """Shared content template properties edit form"""
 
-    override_legend = _("Override tool default template")
+    inherit_legend = _("Use tool default template")
--- a/src/pyams_content/shared/common/zmi/properties.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/shared/common/zmi/properties.py	Thu Sep 06 11:27:55 2018 +0200
@@ -15,35 +15,34 @@
 
 # import standard library
 
+from pyramid.events import subscriber
+from z3c.form import field
+from z3c.form.interfaces import IDataExtractedEvent, HIDDEN_MODE
+from zope.interface import implementer
+
+from pyams_content import _
 # import interfaces
 from pyams_content.interfaces import MANAGE_CONTENT_PERMISSION, IBaseContent
 from pyams_content.shared.common.interfaces import IWfSharedContent
-from pyams_form.interfaces.form import IWidgetForm
-from pyams_skin.interfaces import IInnerPage, IPageHeader
-from pyams_skin.layer import IPyAMSLayer
-from pyams_utils.interfaces import VIEW_SYSTEM_PERMISSION
-from pyams_zmi.interfaces import IPropertiesEditForm
-from pyams_zmi.interfaces.menu import IContentManagementMenu, IPropertiesMenu
-from pyams_zmi.layer import IAdminLayer
-from z3c.form.interfaces import IDataExtractedEvent, HIDDEN_MODE
-
 # import packages
 from pyams_content.shared.common.zmi import WfSharedContentHeaderAdapter
 from pyams_form.form import ajax_config
+from pyams_form.interfaces.form import IWidgetForm
 from pyams_i18n.widget import I18nSEOTextLineFieldWidget
 from pyams_pagelet.pagelet import pagelet_config
 from pyams_skin.event import get_json_widget_refresh_event
+from pyams_skin.interfaces import IInnerPage, IPageHeader
+from pyams_skin.layer import IPyAMSLayer
 from pyams_skin.viewlet.menu import MenuItem
 from pyams_utils.adapter import adapter_config
+from pyams_utils.interfaces import VIEW_SYSTEM_PERMISSION
 from pyams_utils.url import generate_url
 from pyams_viewlet.manager import viewletmanager_config
 from pyams_viewlet.viewlet import viewlet_config
 from pyams_zmi.form import AdminEditForm
-from pyramid.events import subscriber
-from z3c.form import field
-from zope.interface import implementer
-
-from pyams_content import _
+from pyams_zmi.interfaces import IPropertiesEditForm
+from pyams_zmi.interfaces.menu import IContentManagementMenu, IPropertiesMenu
+from pyams_zmi.layer import IAdminLayer
 
 
 #
@@ -82,9 +81,14 @@
 
     legend = _("Content properties")
 
-    fields = field.Fields(IWfSharedContent).select('title', 'short_name', 'content_url',
-                                                   'description', 'notepad')
-    fields['title'].widgetFactory = I18nSEOTextLineFieldWidget
+    @property
+    def fields(self):
+        fields = field.Fields(IWfSharedContent).select('title', 'short_name', 'content_url',
+                                                       'header', 'description', 'notepad')
+        fields['title'].widgetFactory = I18nSEOTextLineFieldWidget
+        if not self.context.handle_header:
+            fields = fields.omit('header')
+        return fields
 
     def updateWidgets(self, prefix=None):
         super(SharedContentPropertiesEditForm, self).updateWidgets(prefix)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/shared/common/zmi/reverse.py	Thu Sep 06 11:27:55 2018 +0200
@@ -0,0 +1,109 @@
+#
+# 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 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
+from pyams_utils.interfaces import VIEW_SYSTEM_PERMISSION
+from pyams_workflow.interfaces import IWorkflowVersions
+from pyams_zmi.interfaces.menu import IContentManagementMenu
+from pyams_zmi.layer import IAdminLayer
+from z3c.table.interfaces import IValues, IColumn
+
+# import packages
+from hypatia.catalog import CatalogQuery
+from hypatia.query import Eq, Or, Any
+from pyams_catalog.query import CatalogResultSet
+from pyams_pagelet.pagelet import pagelet_config
+from pyams_skin.container import ContainerView
+from pyams_skin.table import BaseTable, NameColumn
+from pyams_skin.viewlet.menu import MenuItem
+from pyams_utils.adapter import adapter_config, ContextRequestViewAdapter
+from pyams_utils.list import unique_iter
+from pyams_utils.registry import get_utility
+from pyams_utils.traversing import get_parent
+from pyams_viewlet.viewlet import viewlet_config
+from pyams_zmi.view import AdminView
+from zope.interface import implementer, Interface
+
+from pyams_content import _
+
+
+@viewlet_config(name='reverse-links.menu', context=IWfSharedContent, layer=IAdminLayer,
+                manager=IContentManagementMenu, permission=VIEW_SYSTEM_PERMISSION, weight=40)
+class SequentialITargetReverseLinksMenu(MenuItem):
+    """Sequential ID target reverse links menu"""
+
+    label = _("Reverse links")
+    icon_class = 'fa-anchor'
+    url = '#reverse-links.html'
+
+
+@implementer(ISiteRootDashboardTable)
+class SequentialIdTargetReverseLinkTable(BaseTable):
+    """Sequential ID target reverse links table"""
+
+    title = _("Content's internal links")
+
+
+@adapter_config(name='name', context=(Interface, IPyAMSLayer, SequentialIdTargetReverseLinkTable), provides=IColumn)
+class ReverseLinkNameColumn(NameColumn):
+    """Reverse link name column"""
+
+    _header = _("Title")
+
+
+@adapter_config(context=(IWfSharedContent, IPyAMSLayer, SequentialIdTargetReverseLinkTable), provides=IValues)
+class SequentialIdTargetReverseLinkValues(ContextRequestViewAdapter):
+    """Sequential ID target reverse links values"""
+
+    @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),
+                    Any(catalog['link_references'], {oid}))
+        return unique_iter(map(get_item,
+                               CatalogResultSet(CatalogQuery(catalog).query(params,
+                                                                            sort_index='modified_date'))))
+
+
+@pagelet_config(name='reverse-links.html', context=IWfSharedContent, layer=IPyAMSLayer,
+                permission=VIEW_SYSTEM_PERMISSION)
+@implementer(IInnerPage)
+class SequentialIdTargetReverseLinkView(AdminView, ContainerView):
+    """Sequential ID target reverse links view"""
+
+    table_class = SequentialIdTargetReverseLinkTable
--- a/src/pyams_content/shared/common/zmi/search.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/shared/common/zmi/search.py	Thu Sep 06 11:27:55 2018 +0200
@@ -17,6 +17,8 @@
 
 # import interfaces
 from hypatia.interfaces import ICatalog
+from pyams_content.component.theme.interfaces import ITagsManager, IThemesManagerTarget, IThemesManager, \
+    ICollectionsManagerTarget, ICollectionsManager
 from pyams_content.profile.interfaces import IAdminProfile
 from pyams_content.shared.common.interfaces import IBaseSharedTool
 from pyams_content.shared.common.interfaces.zmi import ISharedToolDashboardTable
@@ -46,6 +48,8 @@
 from pyams_skin.table import BaseTable
 from pyams_skin.viewlet.menu import MenuItem
 from pyams_template.template import template_config
+from pyams_thesaurus.schema import ThesaurusTermsListField
+from pyams_thesaurus.zmi.widget import ThesaurusTermsTreeFieldWidget
 from pyams_utils.adapter import adapter_config, ContextRequestViewAdapter
 from pyams_utils.list import unique
 from pyams_utils.registry import get_utility
@@ -92,12 +96,13 @@
     title = _("Quick search results")
 
     sortOn = None
+    dt_sort_order = 'desc'
 
     @property
     def data_attributes(self):
         attributes = super(SharedToolQuickSearchResults, self).data_attributes
         attributes['table'] = {
-            'data-ams-datatable-sorting': '[]',
+            'data-ams-datatable-sorting': '{0},{1}'.format(len(self.columns) - 1, self.dt_sort_order),
             'data-ams-datatable-display-length': IAdminProfile(self.request.principal).table_page_length
         }
         return attributes
@@ -181,6 +186,15 @@
     modified_before = Datetime(title=_("Modified before..."),
                                required=False)
 
+    tags = ThesaurusTermsListField(title=_("Tags"),
+                                   required=False)
+
+    themes = ThesaurusTermsListField(title=_("Themes"),
+                                     required=False)
+
+    collections = ThesaurusTermsListField(title=_("Collections"),
+                                          required=False)
+
 
 @template_config(template='templates/advanced-search.pt', layer=IPyAMSLayer)
 @implementer(IInnerPage)
@@ -201,8 +215,35 @@
         workflow = IWorkflow(self.context)
         fields = field.Fields(ISharedToolAdvancedSearchFields)
         fields['status'].vocabulary = workflow.states
+        fields['tags'].widgetFactory = ThesaurusTermsTreeFieldWidget
+        if IThemesManagerTarget.providedBy(self.context):
+            fields['themes'].widgetFactory = ThesaurusTermsTreeFieldWidget
+        else:
+            fields = fields.omit('themes')
+        if ICollectionsManagerTarget.providedBy(self.context):
+            fields['collections'].widgetFactory = ThesaurusTermsTreeFieldWidget
+        else:
+            fields = fields.omit('collections')
         return fields
 
+    def updateWidgets(self, prefix=None):
+        super(SharedToolAdvancedSearchForm, self).updateWidgets(prefix)
+        if 'tags' in self.widgets:
+            widget = self.widgets['tags']
+            manager = ITagsManager(self.request.root)
+            widget.thesaurus_name = manager.thesaurus_name
+            widget.extract_name = manager.extract_name
+        if 'themes' in self.widgets:
+            widget = self.widgets['themes']
+            manager = IThemesManager(self.context)
+            widget.thesaurus_name = manager.thesaurus_name
+            widget.extract_name = manager.extract_name
+        if 'collections' in self.widgets:
+            widget = self.widgets['collections']
+            manager = ICollectionsManager(self.context)
+            widget.thesaurus_name = manager.thesaurus_name
+            widget.extract_name = manager.extract_name
+
 
 @adapter_config(context=(IBaseSharedTool, IPyAMSLayer, SharedToolAdvancedSearchForm), provides=IContentSearch)
 class SharedToolAdvancedSearchFormSearchAdapter(ContextRequestViewAdapter):
@@ -242,6 +283,15 @@
             params &= Ge(catalog['modified_date'], data['modified_after'])
         if data.get('modified_before'):
             params &= Le(catalog['modified_date'], data['modified_before'])
+        if data.get('tags'):
+            tags = [intids.register(term) for term in data['tags']]
+            params &= Any(catalog['tags'], tags)
+        if data.get('themes'):
+            tags = [intids.register(term) for term in data['themes']]
+            params &= Any(catalog['themes'], tags)
+        if data.get('collections'):
+            tags = [intids.register(term) for term in data['collections']]
+            params &= Any(catalog['collections'], tags)
         if data.get('status'):
             return unique(map(lambda x: sorted(IWorkflowVersions(x).get_versions(data['status']),
                                                key=lambda y: IZopeDublinCore(y).modified)[0],
--- a/src/pyams_content/shared/common/zmi/summary.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/shared/common/zmi/summary.py	Thu Sep 06 11:27:55 2018 +0200
@@ -9,6 +9,8 @@
 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
 # FOR A PARTICULAR PURPOSE.
 #
+from pyams_content.shared.common.interfaces.types import IWfTypedSharedContent
+from pyams_sequence.interfaces import ISequentialIdInfo
 
 __docformat__ = 'restructuredtext'
 
@@ -74,7 +76,13 @@
     tab_label = _("Identity card")
     css_class = 'form-tight'
 
-    fields = field.Fields(Interface)
+    @property
+    def fields(self):
+        fields = field.Fields(IWfSharedContent).select('title')
+        if IWfTypedSharedContent.providedBy(self.context):
+            fields += field.Fields(IWfTypedSharedContent).select('data_type')
+        fields += field.Fields(ISequentialIdInfo).select('public_oid')
+        return fields
 
 
 @adapter_config(name='workflow-waiting-state',
--- a/src/pyams_content/shared/common/zmi/templates/advanced-search.pt	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/shared/common/zmi/templates/advanced-search.pt	Thu Sep 06 11:27:55 2018 +0200
@@ -73,12 +73,12 @@
 									</span>
 								</label>
 								<div class="col-md-9">
-									<label class="input"
-										   tal:attributes="class widget.widget_css_class | default;
-														   data-ams-data tales:object_data(widget);
-														   data-ams-form-validator view.get_widget_callback(widget.field.getName())">
+									<div class="input"
+										 tal:attributes="class widget.widget_css_class | default;
+														 data-ams-data tales:object_data(widget);
+														 data-ams-form-validator view.get_widget_callback(widget.field.getName())">
 										<input tal:replace="structure widget.render()" />
-									</label>
+									</div>
 								</div>
 							</div>
 							<div class="form-group">
@@ -94,16 +94,18 @@
 										</span>
 									</label>
 									<div class="col-md-4">
-										<label class="input"
-											   tal:attributes="class widget.widget_css_class | default;
-															   data-ams-data tales:object_data(widget);
-															   data-ams-form-validator view.get_widget_callback(widget.field.getName())">
+										<div class="input"
+											 tal:attributes="class widget.widget_css_class | default;
+															 data-ams-data tales:object_data(widget);
+															 data-ams-form-validator view.get_widget_callback(widget.field.getName())">
 											<input tal:replace="structure widget.render()" />
-										</label>
+										</div>
 									</div>
 								</tal:var>
+							</div>
+							<div class="form-group">
 								<tal:var define="widget view.widgets['status']">
-									<label class="control-label col-md-1">
+									<label class="control-label col-md-3">
 										<span>
 											<tal:var content="widget.label" />
 											<i class="fa fa-question-circle hint" title="Input hint"
@@ -114,12 +116,12 @@
 										</span>
 									</label>
 									<div class="col-md-4">
-										<label class="input"
-											   tal:attributes="class widget.widget_css_class | default;
-															   data-ams-data tales:object_data(widget);
-															   data-ams-form-validator view.get_widget_callback(widget.field.getName())">
+										<div class="input"
+											 tal:attributes="class widget.widget_css_class | default;
+															 data-ams-data tales:object_data(widget);
+															 data-ams-form-validator view.get_widget_callback(widget.field.getName())">
 											<input tal:replace="structure widget.render()" />
-										</label>
+										</div>
 									</div>
 								</tal:var>
 							</div>
@@ -128,25 +130,25 @@
 									<span i18n:translate="">Created between</span>
 								</label>
 								<div class="col-md-4">
-									<label class="input"
-											tal:define="widget view.widgets['created_after']"
-										   tal:attributes="class widget.widget_css_class | default;
-														   data-ams-data tales:object_data(widget);
-														   data-ams-form-validator view.get_widget_callback(widget.field.getName())">
+									<div class="input"
+										 tal:define="widget view.widgets['created_after']"
+										 tal:attributes="class widget.widget_css_class | default;
+														 data-ams-data tales:object_data(widget);
+														 data-ams-form-validator view.get_widget_callback(widget.field.getName())">
 										<input tal:replace="structure widget.render()" />
-									</label>
+									</div>
 								</div>
 								<div class="control-label col-md-1 text-align-center">
 									<i18n:var translate=""> and </i18n:var>
 								</div>
 								<div class="col-md-4">
-									<label class="input"
-											tal:define="widget view.widgets['created_before']"
-										   tal:attributes="class widget.widget_css_class | default;
-														   data-ams-data tales:object_data(widget);
-														   data-ams-form-validator view.get_widget_callback(widget.field.getName())">
+									<div class="input"
+										 tal:define="widget view.widgets['created_before']"
+										 tal:attributes="class widget.widget_css_class | default;
+														 data-ams-data tales:object_data(widget);
+														 data-ams-form-validator view.get_widget_callback(widget.field.getName())">
 										<input tal:replace="structure widget.render()" />
-									</label>
+									</div>
 								</div>
 							</div>
 							<div class="form-group">
@@ -154,27 +156,37 @@
 									<span i18n:translate="">Modified between</span>
 								</label>
 								<div class="col-md-4">
-									<label class="input"
-											tal:define="widget view.widgets['modified_after']"
-										   tal:attributes="class widget.widget_css_class | default;
-														   data-ams-data tales:object_data(widget);
-														   data-ams-form-validator view.get_widget_callback(widget.field.getName())">
+									<div class="input"
+										 tal:define="widget view.widgets['modified_after']"
+										 tal:attributes="class widget.widget_css_class | default;
+														 data-ams-data tales:object_data(widget);
+														 data-ams-form-validator view.get_widget_callback(widget.field.getName())">
 										<input tal:replace="structure widget.render()" />
-									</label>
+									</div>
 								</div>
 								<div class="control-label col-md-1 text-align-center">
 									<i18n:var translate=""> and </i18n:var>
 								</div>
 								<div class="col-md-4">
-									<label class="input"
-											tal:define="widget view.widgets['modified_before']"
-										   tal:attributes="class widget.widget_css_class | default;
-														   data-ams-data tales:object_data(widget);
-														   data-ams-form-validator view.get_widget_callback(widget.field.getName())">
+									<div class="input"
+										 tal:define="widget view.widgets['modified_before']"
+										 tal:attributes="class widget.widget_css_class | default;
+														 data-ams-data tales:object_data(widget);
+														 data-ams-form-validator view.get_widget_callback(widget.field.getName())">
 										<input tal:replace="structure widget.render()" />
-									</label>
+									</div>
 								</div>
 							</div>
+							<tal:loop repeat="fieldname ('tags', 'themes', 'collections')">
+								<fieldset tal:condition="fieldname in view.widgets">
+									<tal:var define="widget view.widgets[fieldname]">
+										<legend class="switcher">${widget.label}</legend>
+										<div class="input">
+											<input tal:replace="structure widget.render()" />
+										</div>
+									</tal:var>
+								</fieldset>
+							</tal:loop>
 						</fieldset>
 					</tal:loop>
 					<div class="widgets-suffix"
--- a/src/pyams_content/shared/common/zmi/templates/dashboard.pt	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/shared/common/zmi/templates/dashboard.pt	Thu Sep 06 11:27:55 2018 +0200
@@ -22,13 +22,13 @@
 					  data-async data-ams-form-submit-target="#search_results">
 					<div class="form-group">
 						<div class="col-md-6">
-							<label class="input">
+							<div 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"
 									   data-ams-events-handlers='{"keyup": "MyAMS.helpers.clearSearchTarget"}' />
-							</label>
+							</div>
 						</div>
 						<div class="col-md-6">
 							<a class="nowrap btn-sm col-md-2" href="#advanced-search.html"
--- a/src/pyams_content/shared/common/zmi/types.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/shared/common/zmi/types.py	Thu Sep 06 11:27:55 2018 +0200
@@ -16,32 +16,36 @@
 # import standard library
 import json
 
+from pyramid.decorator import reify
+from pyramid.events import subscriber
+from pyramid.exceptions import NotFound
+from pyramid.view import view_config
+from z3c.form import field
+from z3c.form.interfaces import DISPLAY_MODE, IDataExtractedEvent
+from z3c.table.interfaces import IValues, IColumn
+from zope.interface import Invalid
+
+from pyams_content import _
 # import interfaces
 from pyams_content.interfaces import MANAGE_TOOL_PERMISSION, MANAGE_CONTENT_PERMISSION
+# import packages
+from pyams_content.reference.pictograms.zmi.widget import PictogramSelectFieldWidget
 from pyams_content.shared.common.interfaces.types import ITypedSharedTool, ITypedDataManager, \
     IBaseDataType, IDataType, ISubType, IWfTypedSharedContent
-from pyams_content.skin import pyams_content
-from pyams_i18n.interfaces import II18n
-from pyams_skin.interfaces.container import ITableElementName
-from pyams_skin.interfaces.viewlet import IWidgetTitleViewletManager
-from pyams_skin.layer import IPyAMSLayer
-from pyams_viewlet.interfaces import IViewletManager
-from pyams_zmi.interfaces.menu import IPropertiesMenu
-from pyams_zmi.layer import IAdminLayer
-from z3c.form.interfaces import DISPLAY_MODE, IDataExtractedEvent
-from z3c.table.interfaces import IValues, IColumn
-
-# import packages
-from pyams_content.reference.pictograms.zmi.widget import PictogramSelectFieldWidget
 from pyams_content.shared.common.types import DataType, SubType
 from pyams_content.shared.common.zmi import SharedContentAddForm
 from pyams_content.shared.common.zmi.properties import SharedContentPropertiesEditForm
+from pyams_content.skin import pyams_content
 from pyams_form.form import AJAXAddForm, ajax_config
 from pyams_form.security import ProtectedFormObjectMixin
+from pyams_i18n.interfaces import II18n
 from pyams_i18n.widget import I18nSEOTextLineFieldWidget
 from pyams_pagelet.pagelet import pagelet_config
 from pyams_skin.container import delete_container_element
 from pyams_skin.event import get_json_table_refresh_event
+from pyams_skin.interfaces.container import ITableElementName
+from pyams_skin.interfaces.viewlet import IWidgetTitleViewletManager
+from pyams_skin.layer import IPyAMSLayer
 from pyams_skin.table import BaseTable, SorterColumn, TrashColumn, NameColumn, ActionColumn
 from pyams_skin.viewlet.menu import MenuItem
 from pyams_skin.viewlet.toolbar import ToolbarAction
@@ -50,17 +54,12 @@
 from pyams_utils.traversing import get_parent
 from pyams_utils.unicode import translate_string
 from pyams_utils.url import absolute_url
+from pyams_viewlet.interfaces import IViewletManager
 from pyams_viewlet.viewlet import viewlet_config
 from pyams_zmi.form import AdminDialogAddForm, AdminDialogEditForm
+from pyams_zmi.interfaces.menu import IPropertiesMenu
+from pyams_zmi.layer import IAdminLayer
 from pyams_zmi.view import ContainerAdminView
-from pyramid.decorator import reify
-from pyramid.events import subscriber
-from pyramid.exceptions import NotFound
-from pyramid.view import view_config
-from z3c.form import field
-from zope.interface import Invalid
-
-from pyams_content import _
 
 
 @viewlet_config(name='data-types.menu', context=ITypedSharedTool, layer=IAdminLayer,
@@ -567,6 +566,12 @@
 
     fields = field.Fields(IWfTypedSharedContent).select('title', 'data_type', 'notepad')
 
+    def updateWidgets(self, prefix=None):
+        super(TypedSharedContentAddForm, self).updateWidgets(prefix)
+        if 'data_type' in self.widgets:
+            self.widgets['data_type'].prompt = True
+            self.widgets['data_type'].promptMessage = _("Select content type...")
+
 
 @pagelet_config(name='properties.html', context=IWfTypedSharedContent, layer=IPyAMSLayer,
                 permission=MANAGE_CONTENT_PERMISSION)
--- a/src/pyams_content/shared/form/__init__.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/shared/form/__init__.py	Thu Sep 06 11:27:55 2018 +0200
@@ -15,22 +15,21 @@
 
 # import standard library
 
+from zope.interface import implementer, provider, alsoProvides, noLongerProvides
+from zope.schema.fieldproperty import FieldProperty
+
+from pyams_content import _
 # import interfaces
 from pyams_content.features.checker.interfaces import IContentChecker, ERROR_VALUE
 from pyams_content.features.preview.interfaces import IPreviewTarget
 from pyams_content.features.review.interfaces import IReviewTarget
-from pyams_content.shared.form.interfaces import IWfForm, IForm, FORM_CONTENT_TYPE, FORM_CONTENT_NAME, \
-    IFormFieldContainerTarget, IFormHandler, IFormFieldContainer, IWfFormFactory
-
 # import packages
 from pyams_content.shared.common import WfSharedContent, SharedContent, register_content_type, WfSharedContentChecker, \
     IWfSharedContentFactory
+from pyams_content.shared.form.interfaces import IWfForm, IForm, FORM_CONTENT_TYPE, FORM_CONTENT_NAME, \
+    IFormFieldContainerTarget, IFormHandler, IFormFieldContainer, IWfFormFactory
 from pyams_utils.adapter import adapter_config
 from pyams_utils.registry import get_global_registry
-from zope.interface import implementer, provider, alsoProvides, noLongerProvides
-from zope.schema.fieldproperty import FieldProperty
-
-from pyams_content import _
 
 
 @implementer(IWfForm, IFormFieldContainerTarget,
@@ -42,7 +41,6 @@
     content_name = FORM_CONTENT_NAME
 
     user_title = FieldProperty(IWfForm['user_title'])
-    header = FieldProperty(IWfForm['header'])
     _handler = FieldProperty(IWfForm['handler'])
     auth_only = FieldProperty(IWfForm['auth_only'])
     use_captcha = FieldProperty(IWfForm['use_captcha'])
--- a/src/pyams_content/shared/form/interfaces/__init__.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/shared/form/interfaces/__init__.py	Thu Sep 06 11:27:55 2018 +0200
@@ -15,21 +15,19 @@
 
 # import standard library
 
-# import interfaces
-from pyams_content.shared.common.interfaces import ISharedContent, IWfSharedContentPortalContext, \
-    ISharedToolPortalContext
 from zope.annotation.interfaces import IAttributeAnnotatable
+from zope.container.constraints import containers, contains
 from zope.container.interfaces import IContainer, IContained
-
-# import packages
-from pyams_i18n.schema import I18nTextLineField, I18nTextField
-from pyams_utils.schema import MailAddressField, TextLineListField
-from zope.container.constraints import containers, contains
 from zope.interface import Interface, Attribute
 from zope.schema import TextLine, Choice, Bool
 
 from pyams_content import _
-
+# import interfaces
+from pyams_content.shared.common.interfaces import ISharedContent, IWfSharedContentPortalContext, \
+    ISharedToolPortalContext
+# import packages
+from pyams_i18n.schema import I18nTextLineField, I18nTextField
+from pyams_utils.schema import MailAddressField, TextLineListField
 
 FORM_CONTENT_TYPE = 'form'
 FORM_CONTENT_NAME = _('Form')
@@ -122,9 +120,6 @@
     user_title = I18nTextLineField(title=_("Form title"),
                                    required=True)
 
-    header = I18nTextField(title=_("Form header"),
-                           required=False)
-
     handler = Choice(title=_("Form handler"),
                      description=_("Select how form data is transmitted"),
                      vocabulary='PyAMS form handlers')
--- a/src/pyams_content/shared/form/zmi/field.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/shared/form/zmi/field.py	Thu Sep 06 11:27:55 2018 +0200
@@ -100,11 +100,18 @@
             'data-ams-plugin-pyams_content-src': get_resource_path(pyams_content),
             'data-ams-location': absolute_url(IFormFieldContainer(self.context), self.request),
             'data-ams-tablednd-drag-handle': 'td.sorter',
-            'data-ams-tablednd-drop-target': 'set-form-fields-order.json',
-            'data-ams-visibility-switcher': 'switch-form-field-visibility.json'
+            'data-ams-tablednd-drop-target': 'set-form-fields-order.json'
         }
+        attributes.setdefault('td', {}).update({
+            'data-ams-attribute-switcher': self.get_switcher_target
+        })
         return attributes
 
+    @staticmethod
+    def get_switcher_target(element, column):
+        if column.__name__ == 'show-hide':
+            return 'switch-form-field-visibility.json'
+
     @reify
     def values(self):
         return list(super(FormFieldsContainerTable, self).values)
--- a/src/pyams_content/shared/imagemap/__init__.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/shared/imagemap/__init__.py	Thu Sep 06 11:27:55 2018 +0200
@@ -16,7 +16,17 @@
 # import standard library
 from persistent import Persistent
 from persistent.mapping import PersistentMapping
+from pyramid.threadlocal import get_current_registry
+from z3c.form.interfaces import NOT_CHANGED
+from zope.container.contained import Contained
+from zope.interface import implementer, provider
+from zope.lifecycleevent import ObjectModifiedEvent
+from zope.location import locate
+from zope.location.interfaces import ISublocations
+from zope.schema.fieldproperty import FieldProperty
+from zope.traversing.interfaces import ITraversable
 
+from pyams_content import _
 # import interfaces
 from pyams_content.component.association.interfaces import IAssociationContainer
 from pyams_content.component.extfile.interfaces import IExtFileContainerTarget
@@ -24,26 +34,14 @@
 from pyams_content.features.checker.interfaces import IContentChecker, MISSING_VALUE, MISSING_LANG_VALUE, ERROR_VALUE
 from pyams_content.features.preview.interfaces import IPreviewTarget
 from pyams_content.features.review.interfaces import IReviewTarget
-from pyams_content.shared.imagemap.interfaces import IMAGEMAP_CONTENT_TYPE, IMAGEMAP_CONTENT_NAME, \
-    IWfImageMap, IImageMap, IImageMapArea, IWfImageMapFactory
-from pyams_i18n.interfaces import II18n, II18nManager
-from z3c.form.interfaces import NOT_CHANGED
-from zope.location.interfaces import ISublocations
-from zope.traversing.interfaces import ITraversable
-
 # import packages
 from pyams_content.shared.common import WfSharedContent, register_content_type, SharedContent, WfSharedContentChecker, \
     IWfSharedContentFactory
+from pyams_content.shared.imagemap.interfaces import IMAGEMAP_CONTENT_TYPE, IMAGEMAP_CONTENT_NAME, \
+    IWfImageMap, IImageMap, IImageMapArea, IWfImageMapFactory
+from pyams_i18n.interfaces import II18n, II18nManager
 from pyams_i18n.property import I18nFileProperty
 from pyams_utils.adapter import adapter_config, ContextAdapter
-from pyramid.threadlocal import get_current_registry
-from zope.container.contained import Contained
-from zope.interface import implementer, provider
-from zope.lifecycleevent import ObjectModifiedEvent
-from zope.location import locate
-from zope.schema.fieldproperty import FieldProperty
-
-from pyams_content import _
 
 
 @implementer(IImageMapArea)
@@ -63,6 +61,8 @@
     content_type = IMAGEMAP_CONTENT_TYPE
     content_name = IMAGEMAP_CONTENT_NAME
 
+    handle_header = False
+
     _image = I18nFileProperty(IWfImageMap['image'])
     areas = FieldProperty(IWfImageMap['areas'])
 
--- a/src/pyams_content/shared/imagemap/paragraph.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/shared/imagemap/paragraph.py	Thu Sep 06 11:27:55 2018 +0200
@@ -24,15 +24,14 @@
 from pyams_workflow.interfaces import IWorkflow, IWorkflowState
 
 # import packages
+from pyams_content.component.links import InternalReferenceMixin
 from pyams_content.component.paragraph import BaseParagraph, BaseParagraphContentChecker, BaseParagraphFactory
 from pyams_content.features.renderer import RenderersVocabulary
-from pyams_sequence.reference import get_reference_target
 from pyams_utils.adapter import adapter_config
 from pyams_utils.factory import factory_config
 from pyams_utils.registry import utility_config, get_utility
 from pyams_utils.traversing import get_parent
 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
 
@@ -41,7 +40,7 @@
 
 @implementer(IImageMapParagraph)
 @factory_config(provided=IImageMapParagraph)
-class ImageMapParagraph(BaseParagraph):
+class ImageMapParagraph(BaseParagraph, InternalReferenceMixin):
     """Image map paragraph"""
 
     icon_class = 'fa-location-arrow'
@@ -50,16 +49,6 @@
     reference = FieldProperty(IImageMapParagraph['reference'])
     renderer = FieldProperty(IImageMapParagraph['renderer'])
 
-    @volatile_property
-    def target(self):
-        return get_reference_target(self.reference)
-
-    def get_target(self, state=None):
-        if not state:
-            return self.target
-        else:
-            return get_reference_target(self.reference, state)
-
 
 @utility_config(name=IMAGEMAP_PARAGRAPH_TYPE, provides=IParagraphFactory)
 class ImageMapParagraphFactory(BaseParagraphFactory):
--- a/src/pyams_content/shared/imagemap/zmi/templates/imagemap-preview.pt	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/shared/imagemap/zmi/templates/imagemap-preview.pt	Thu Sep 06 11:27:55 2018 +0200
@@ -1,8 +1,11 @@
 <div class="form-group" i18n:domain="pyams_content">
 	<div class="col-md-3"><!-- empty marker --></div>
 	<div class="col-md-9">
-		<img tal:define="map context.get_target();
-						 image i18n:map.image;"
-			 tal:attributes="src tales:absolute_url(image, '++thumb++600x480')" />
+		<tal:if define="map context.get_target()"
+				condition="map is not None">
+			<img tal:define="image i18n:map.image;"
+				 tal:condition="image"
+				 tal:attributes="src tales:absolute_url(image, '++thumb++600x480')" />
+		</tal:if>
 	</div>
 </div>
--- a/src/pyams_content/shared/logo/__init__.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/shared/logo/__init__.py	Thu Sep 06 11:27:55 2018 +0200
@@ -15,20 +15,19 @@
 
 # import standard library
 
-# import interfaces
-from pyams_content.features.checker.interfaces import IContentChecker, MISSING_VALUE
-from pyams_content.shared.logo.interfaces import IWfLogo, LOGO_CONTENT_TYPE, LOGO_CONTENT_NAME, ILogo, IWfLogoFactory
-from pyams_content.features.review import IReviewTarget
-
-# import packages
-from pyams_content.shared.common import WfSharedContent, register_content_type, SharedContent, WfSharedContentChecker, \
-    IWfSharedContentFactory
-from pyams_file.property import FileProperty
-from pyams_utils.adapter import adapter_config
 from zope.interface import implementer, provider
 from zope.schema.fieldproperty import FieldProperty
 
 from pyams_content import _
+# import interfaces
+from pyams_content.features.checker.interfaces import IContentChecker, MISSING_VALUE
+from pyams_content.features.review import IReviewTarget
+# import packages
+from pyams_content.shared.common import WfSharedContent, register_content_type, SharedContent, WfSharedContentChecker, \
+    IWfSharedContentFactory
+from pyams_content.shared.logo.interfaces import IWfLogo, LOGO_CONTENT_TYPE, LOGO_CONTENT_NAME, ILogo, IWfLogoFactory
+from pyams_file.property import FileProperty
+from pyams_utils.adapter import adapter_config
 
 
 @implementer(IWfLogo, IReviewTarget)
@@ -38,6 +37,8 @@
     content_type = LOGO_CONTENT_TYPE
     content_name = LOGO_CONTENT_NAME
 
+    handle_header = False
+
     image = FileProperty(IWfLogo['image'])
     monochrome_image = FileProperty(IWfLogo['monochrome_image'])
     url = FieldProperty(IWfLogo['url'])
--- a/src/pyams_content/shared/logo/paragraph.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/shared/logo/paragraph.py	Thu Sep 06 11:27:55 2018 +0200
@@ -9,7 +9,6 @@
 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
 # FOR A PARTICULAR PURPOSE.
 #
-from pyams_utils.zodb import volatile_property
 
 __docformat__ = 'restructuredtext'
 
--- a/src/pyams_content/shared/site/folder.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/shared/site/folder.py	Thu Sep 06 11:27:55 2018 +0200
@@ -53,8 +53,11 @@
     roles_interface = ISiteFolderRoles
 
     heading = FieldProperty(ISiteFolder['heading'])
+    notepad = FieldProperty(ISiteFolder['notepad'])
+
+    visible_in_list = FieldProperty(ISiteFolder['visible_in_list'])
     navigation_title = FieldProperty(ISiteFolder['navigation_title'])
-    notepad = FieldProperty(ISiteFolder['notepad'])
+    navigation_mode = FieldProperty(ISiteFolder['navigation_mode'])
 
     content_name = _("Site folder")
 
--- a/src/pyams_content/shared/site/interfaces/__init__.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/shared/site/interfaces/__init__.py	Thu Sep 06 11:27:55 2018 +0200
@@ -14,6 +14,7 @@
 
 
 # import standard library
+from collections import OrderedDict
 
 # import interfaces
 from pyams_content.interfaces import IBaseContent
@@ -28,11 +29,23 @@
 from pyams_i18n.schema import I18nTextLineField, I18nTextField
 from zope.container.constraints import containers, contains
 from zope.interface import Interface, Attribute
-from zope.schema import Text, Bool
+from zope.schema import Text, Bool, Choice
+from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm
 
 from pyams_content import _
 
 
+FOLDER_REDIRECT_DISPLAY_MODE = 'redirect'
+FOLDER_TEMPLATE_DISPLAY_MODE = 'template'
+
+FOLDER_DISPLAY_MODES = OrderedDict((
+    (FOLDER_REDIRECT_DISPLAY_MODE, _("Redirect to first visible sub-folder or content")),
+    (FOLDER_TEMPLATE_DISPLAY_MODE, _("Use presentation template"))
+))
+
+FOLDER_DISPLAY_MODE_VOCABULARY = SimpleVocabulary([SimpleTerm(v, title=t) for v, t in FOLDER_DISPLAY_MODES.items()])
+
+
 class ISiteElement(IContained, IDeletableElement):
     """Base site element interface"""
 
@@ -60,14 +73,26 @@
                             description=_("Heading displayed according to presentation template"),
                             required=False)
 
-    navigation_title = I18nTextLineField(title=_("Navigation title"),
-                                         description=_("Title displayed in navigation items"),
-                                         required=False)
-
     notepad = Text(title=_("Notepad"),
                    description=_("Internal information to be known about this content"),
                    required=False)
 
+    visible_in_list = Bool(title=_("Visible in folders list"),
+                           description=_("If 'no', folder will not be displayed into folders list"),
+                           required=True,
+                           default=True)
+
+    navigation_title = I18nTextLineField(title=_("Navigation title"),
+                                         description=_("Folder's title displayed in navigation pages; "
+                                                       "original title will be used if none is specified"),
+                                         required=False)
+
+    navigation_mode = Choice(title=_("Navigation mode"),
+                             description=_("Folder behaviour when navigating to folder URL"),
+                             required=True,
+                             vocabulary=FOLDER_DISPLAY_MODE_VOCABULARY,
+                             default=FOLDER_REDIRECT_DISPLAY_MODE)
+
 
 class ISiteFolderFactory(Interface):
     """Site folder factory interface"""
@@ -111,9 +136,10 @@
 class IContentLink(ISiteElement, IInternalReference, IAttributeAnnotatable):
     """Rented content interface"""
 
-    alt_title = I18nTextLineField(title=_("Alternate title"),
-                                  description=_("Content title, as shown in front-office"),
-                                  required=False)
+    navigation_title = I18nTextLineField(title=_("Navigation title"),
+                                         description=_("Alternate content's title displayed in navigation pages; "
+                                                       "original title will be used if none is specified"),
+                                         required=False)
 
     visible = Bool(title=_("Visible?"),
                    description=_("If 'no', link is not visible"),
--- a/src/pyams_content/shared/site/link.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/shared/site/link.py	Thu Sep 06 11:27:55 2018 +0200
@@ -43,7 +43,7 @@
     """
 
     reference = FieldProperty(IContentLink['reference'])
-    alt_title = FieldProperty(IContentLink['alt_title'])
+    navigation_title = FieldProperty(IContentLink['navigation_title'])
     visible = FieldProperty(IContentLink['visible'])
 
     content_name = _("Content link")
--- a/src/pyams_content/shared/site/zmi/__init__.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/shared/site/zmi/__init__.py	Thu Sep 06 11:27:55 2018 +0200
@@ -20,7 +20,7 @@
 from pyams_content.interfaces import CREATE_CONTENT_PERMISSION
 from pyams_content.shared.common.interfaces import IWfSharedContent
 from pyams_content.shared.site.interfaces import ISiteContainer, ISiteManager, IWfTopic
-from pyams_i18n.interfaces import II18nManager
+from pyams_i18n.interfaces import II18nManager, INegotiator
 from pyams_skin.interfaces.viewlet import IToolbarAddingMenu, IMenuHeader
 from pyams_skin.layer import IPyAMSLayer
 from pyams_workflow.interfaces import IWorkflowInfo, IWorkflowVersions
@@ -37,7 +37,7 @@
 from pyams_utils.adapter import adapter_config, ContextRequestAdapter
 from pyams_utils.registry import get_utility
 from pyams_utils.traversing import get_parent
-from pyams_utils.url import absolute_url
+from pyams_utils.url import absolute_url, generate_url
 from pyams_viewlet.viewlet import viewlet_config
 from pyramid.decorator import reify
 from pyramid.path import DottedNameResolver
@@ -114,11 +114,13 @@
     def update_content(self, content, data):
         data = data.get(self, data)
         # initialize content fields
+        lang = get_utility(INegotiator).server_language
+        content.creator = self.request.principal.id
+        content.owner = self.request.principal.id
         content.title = data['title']
         content.short_name = content.title.copy()
+        content.content_url = generate_url(content.title.get(lang, ''))
         content.notepad = data.get('notepad')
-        content.creator = self.request.principal.id
-        content.owner = self.request.principal.id
         # get parent
         intids = get_utility(IIntIds)
         parent = intids.queryObject(data.get('parent'))
--- a/src/pyams_content/shared/site/zmi/container.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/shared/site/zmi/container.py	Thu Sep 06 11:27:55 2018 +0200
@@ -80,7 +80,7 @@
 
     @property
     def label(self):
-        return II18n(self.context).query_attribute('short_name', request=self.request)
+        return II18n(self.context).query_attribute('title', request=self.request)
 
 
 @adapter_config(context=(ISiteContainer, IAdminLayer), provides=IUserAddingsMenuLabel)
@@ -334,7 +334,7 @@
                     icon_class = 'fa-eye-slash text-danger opaque'
             return '<i class="fa fa-fw {icon_class} hint align-base" title="{title}" data-ams-hint-gravity="e"></i>'.format(
                 icon_class=icon_class,
-                title=self.request.localizer.translate(self.icon_hint))
+                title=self.request.localizer.translate(self.get_icon_hint(item)))
 
     def get_icon_hint(self, item):
         translate = self.request.localizer.translate
--- a/src/pyams_content/shared/site/zmi/folder.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/shared/site/zmi/folder.py	Thu Sep 06 11:27:55 2018 +0200
@@ -9,6 +9,8 @@
 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
 # FOR A PARTICULAR PURPOSE.
 #
+from pyams_form.group import NamedWidgetsGroup
+from pyams_form.interfaces.form import IInnerSubForm
 
 __docformat__ = 'restructuredtext'
 
@@ -31,7 +33,7 @@
 # import packages
 from pyams_content.shared.common.zmi.manager import SharedToolPropertiesEditForm
 from pyams_content.shared.site.zmi.widget import SiteManagerFoldersSelectorFieldWidget
-from pyams_form.form import AJAXAddForm, AJAXEditForm, ajax_config
+from pyams_form.form import AJAXAddForm, ajax_config
 from pyams_i18n.schema import I18nTextLineField
 from pyams_pagelet.pagelet import pagelet_config
 from pyams_skin.table import DefaultElementEditorAdapter
@@ -42,10 +44,9 @@
 from pyams_utils.unicode import translate_string
 from pyams_utils.url import absolute_url
 from pyams_viewlet.viewlet import viewlet_config
-from pyams_zmi.form import AdminDialogAddForm
+from pyams_zmi.form import AdminDialogAddForm, InnerAdminEditForm
 from pyramid.events import subscriber
 from pyramid.path import DottedNameResolver
-from pyramid.view import view_config
 from z3c.form import field
 from zope.interface import Interface, Invalid
 from zope.schema import Text, Int
@@ -182,16 +183,45 @@
 
 
 @pagelet_config(name='properties.html', context=ISiteFolder, layer=IPyAMSLayer, permission=MANAGE_TOOL_PERMISSION)
+@ajax_config(name='properties.json', context=ISiteFolder, layer=IPyAMSLayer)
 class SiteFolderPropertiesEditForm(SharedToolPropertiesEditForm):
     """Site folder properties edit form"""
 
     legend = _("Site folder properties")
 
-    fields = field.Fields(ISiteFolder).select('title', 'short_name', 'heading', 'navigation_title', 'notepad') + \
+    fields = field.Fields(ISiteFolder).select('title', 'short_name', 'heading', 'notepad') + \
              field.Fields(IBaseSharedTool).select('shared_content_workflow')
 
 
-@view_config(name='properties.json', context=ISiteFolder, request_type=IPyAMSLayer,
-             permission=MANAGE_TOOL_PERMISSION, renderer='json', xhr=True)
-class SiteFolderPropertiesAJAXEditForm(AJAXEditForm, SiteFolderPropertiesEditForm):
-    """Site folder properties edit form, JSON renderer"""
+@adapter_config(name='navigation', context=(ISiteFolder, IPyAMSLayer, SiteFolderPropertiesEditForm),
+                provides=IInnerSubForm)
+class SiteFolderNavigationPropertiesEditForm(InnerAdminEditForm):
+    """Site folder navigation properties edit form"""
+
+    prefix = 'navigation_form.'
+
+    css_class = 'form-group'
+    padding_class = ''
+    fieldset_class = 'bordered margin-top-10 padding-y-5'
+
+    legend = None
+    main_group_legend = _("Navigation properties")
+    main_group_class = 'inner switcher no-y-padding'
+
+    fields = field.Fields(ISiteFolder).select('visible_in_list', 'navigation_title', 'navigation_mode')
+
+    weight = 5
+
+    def check_mode(self):
+        if self.parent_form is not None:
+            self.mode = self.parent_form.mode
+
+    def updateGroups(self):
+        self.add_group(NamedWidgetsGroup(self, 'navigation', self.widgets,
+                                         ('visible_in_list', 'navigation_title', 'navigation_mode'),
+                                         fieldset_class=self.fieldset_class,
+                                         legend=self.main_group_legend,
+                                         css_class=self.main_group_class,
+                                         switch=True,
+                                         display_mode='auto'))
+        super(SiteFolderNavigationPropertiesEditForm, self).updateGroups()
--- a/src/pyams_content/shared/site/zmi/link.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/shared/site/zmi/link.py	Thu Sep 06 11:27:55 2018 +0200
@@ -9,7 +9,6 @@
 # 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'
 
@@ -76,7 +75,7 @@
 
     legend = _("Rent existing content")
 
-    fields = field.Fields(IContentLinkAddFormFields).select('reference', 'alt_title', 'parent')
+    fields = field.Fields(IContentLinkAddFormFields).select('reference', 'navigation_title', 'parent')
     fields['parent'].widgetFactory = SiteManagerFoldersSelectorFieldWidget
 
     edit_permission = CREATE_CONTENT_PERMISSION
@@ -94,7 +93,7 @@
     def update_content(self, content, data):
         data = data.get(self, data)
         content.reference = data.get('reference')
-        content.alt_title = data['alt_title']
+        content.navigation_title = data['navigation_title']
         intids = get_utility(IIntIds)
         parent = intids.queryObject(data.get('parent'))
         if parent is not None:
@@ -115,7 +114,7 @@
 
     @property
     def name(self):
-        title = II18n(self.context).query_attribute('alt_title', request=self.request)
+        title = II18n(self.context).query_attribute('navigation_title', request=self.request)
         if not title:
             target = self.context.get_target()
             if target is not None:
@@ -134,7 +133,7 @@
 
     legend = _("Edit content link properties")
 
-    fields = field.Fields(IContentLink).omit('__parent__', '__name__')
+    fields = field.Fields(IContentLink).omit('__parent__', '__name__', 'visible')
     edit_permission = MANAGE_CONTENT_PERMISSION
 
     def get_ajax_output(self, changes):
--- a/src/pyams_content/shared/site/zmi/manager.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/shared/site/zmi/manager.py	Thu Sep 06 11:27:55 2018 +0200
@@ -63,7 +63,7 @@
 
     @property
     def label(self):
-        return II18n(self.context).query_attribute('short_name', request=self.request)
+        return II18n(self.context).query_attribute('title', request=self.request)
 
     css_class = 'strong'
 
--- a/src/pyams_content/shared/view/__init__.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/shared/view/__init__.py	Thu Sep 06 11:27:55 2018 +0200
@@ -24,6 +24,7 @@
 from hypatia.interfaces import ICatalog
 from pyams_content.features.preview.interfaces import IPreviewTarget
 from pyams_content.features.review.interfaces import IReviewTarget
+from pyams_content.shared.common.interfaces.types import IWfTypedSharedContent
 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
@@ -63,8 +64,12 @@
     content_type = VIEW_CONTENT_TYPE
     content_name = VIEW_CONTENT_NAME
 
+    handle_header = False
+
     select_context_type = FieldProperty(IWfView['select_context_type'])
     selected_content_types = FieldProperty(IWfView['selected_content_types'])
+    select_context_datatype = FieldProperty(IWfView['select_context_datatype'])
+    selected_datatypes = FieldProperty(IWfView['selected_datatypes'])
     order_by = FieldProperty(IWfView['order_by'])
     reversed_order = FieldProperty(IWfView['reversed_order'])
     limit = FieldProperty(IWfView['limit'])
@@ -89,6 +94,16 @@
             content_types |= set(self.selected_content_types)
         return list(content_types)
 
+    def get_data_types(self, context):
+        data_types = set()
+        if self.select_context_datatype:
+            content = IWfTypedSharedContent(context, None)
+            if content is not None:
+                data_types.add(content.data_type)
+        if self.selected_datatypes:
+            data_types |= set(self.selected_datatypes)
+        return list(data_types)
+
     def get_results(self, context, sort_index=None, reverse=None, limit=None, ignore_cache=False):
         results = _MARKER
         if not ignore_cache:
@@ -153,6 +168,10 @@
         content_types = view.get_content_types(context)
         if content_types:
             params &= Any(catalog['content_type'], content_types)
+        # check data types
+        data_types = view.get_data_types(context)
+        if data_types:
+            params &= Any(catalog['data_type'], data_types)
         # check workflow states
         wf_params = None
         for workflow in registry.getAllUtilitiesRegisteredFor(IWorkflow):
--- a/src/pyams_content/shared/view/interfaces/__init__.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/shared/view/interfaces/__init__.py	Thu Sep 06 11:27:55 2018 +0200
@@ -16,13 +16,15 @@
 # import standard library
 
 # import interfaces
-from pyams_content.shared.common.interfaces import ISharedContent, IWfSharedContent, ISharedTool
+from pyams_content.shared.common.interfaces import ISharedContent, IWfSharedContent, ISharedTool, \
+    CONTENT_TYPES_VOCABULARY
+from pyams_content.shared.common.interfaces.types import ALL_DATA_TYPES_VOCABULARY
 from pyams_sequence.interfaces import IInternalReferencesList
 
 # import packages
 from pyams_thesaurus.schema import ThesaurusTermsListField
 from zope.interface import Interface, Attribute
-from zope.schema import List, Choice, Bool, Int
+from zope.schema import List, Set, Choice, Bool, Int
 from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm
 
 from pyams_content import _
@@ -64,14 +66,28 @@
                                required=True,
                                default=False)
 
-    selected_content_types = List(title=_("Other content types"),
-                                  description=_("Selected content types; leave empty for all"),
-                                  value_type=Choice(vocabulary='PyAMS content types'),
-                                  required=False)
+    selected_content_types = Set(title=_("Other content types"),
+                                 description=_("Selected content types; leave empty for all"),
+                                 value_type=Choice(vocabulary=CONTENT_TYPES_VOCABULARY),
+                                 required=False)
 
     def get_content_types(self, context):
         """Get content types for given context"""
 
+    select_context_datatype = Bool(title=_("Select context data type?"),
+                                   description=_("If 'yes', content data type (if available) will be extracted from "
+                                                 "context"),
+                                   required=True,
+                                   default=False)
+
+    selected_datatypes = Set(title=_("Other data types"),
+                             description=_("Selected data types; leave empty for all"),
+                             value_type=Choice(vocabulary=ALL_DATA_TYPES_VOCABULARY),
+                             required=False)
+
+    def get_data_types(self, context):
+        """Get data types for given context"""
+
     order_by = Choice(title=_("Order by"),
                       description=_("Property to use to sort results"),
                       vocabulary=VIEW_ORDER_VOCABULARY,
--- a/src/pyams_content/shared/view/zmi/properties.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/shared/view/zmi/properties.py	Thu Sep 06 11:27:55 2018 +0200
@@ -14,17 +14,22 @@
 
 
 # import standard library
+import json
 
 # import interfaces
 from pyams_content.shared.view.interfaces import IWfView
 from pyams_form.interfaces.form import IInnerSubForm
 from pyams_skin.layer import IPyAMSLayer
+from pyams_utils.interfaces.data import IObjectData
 
 # import packages
+from pyams_content.shared.common.types import get_all_data_types
 from pyams_content.shared.common.zmi.properties import SharedContentPropertiesEditForm
+from pyams_form.widget import HiddenSelect2FieldWidget
 from pyams_utils.adapter import adapter_config
 from pyams_zmi.form import InnerAdminEditForm
 from z3c.form import field
+from zope.interface import alsoProvides
 
 from pyams_content import _
 
@@ -41,5 +46,19 @@
     fieldset_class = 'bordered no-x-margin margin-y-10'
 
     fields = field.Fields(IWfView).select('select_context_type', 'selected_content_types',
+                                          'select_context_datatype', 'selected_datatypes',
                                           'order_by', 'reversed_order', 'limit')
+    fields['selected_datatypes'].widgetFactory = HiddenSelect2FieldWidget
+
     weight = 1
+
+    def updateWidgets(self, prefix=None):
+        super(ViewPropertiesEditForm, self).updateWidgets(prefix)
+        if 'selected_datatypes' in self.widgets:
+            widget = self.widgets['selected_datatypes']
+            # widget.multiple = True
+            widget.object_data = {
+                'ams-select2-multiple': True,
+                'ams-select2-data': json.dumps(get_all_data_types(self.request))
+            }
+            alsoProvides(widget, IObjectData)
--- a/src/pyams_content/site.py	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/site.py	Thu Sep 06 11:27:55 2018 +0200
@@ -18,14 +18,13 @@
 
 # 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
 from pyams_utils.container import find_objects_providing
 from pyams_utils.registry import set_local_registry, get_utility
 from pyams_utils.site import site_factory
+from zope.interface import Interface
 
 
 def site_index(request):
@@ -35,13 +34,14 @@
         try:
             set_local_registry(application.getSiteManager())
             catalog = get_utility(ICatalog)
+            catalog.reset()
+            transaction.savepoint()
             intids = get_utility(IIntIds)
-            for document in find_objects_providing(application, IWfSharedContent):
+            for index, document in enumerate(find_objects_providing(application, Interface)):
                 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)
+                if not index % 100:
+                    transaction.savepoint()
         finally:
             set_local_registry(None)
         transaction.commit()
--- a/src/pyams_content/skin/resources/css/pyams_content.css	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/skin/resources/css/pyams_content.css	Thu Sep 06 11:27:55 2018 +0200
@@ -46,3 +46,6 @@
 .pictograms-manager .pictogram:last-child {
   border-bottom: none;
 }
+.sortable.gallery {
+  max-height: 550px;
+}
--- a/src/pyams_content/skin/resources/css/pyams_content.min.css	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/skin/resources/css/pyams_content.min.css	Thu Sep 06 11:27:55 2018 +0200
@@ -1,1 +1,1 @@
-.ams-widget.comments .widget-body{position:fixed;height:calc(100% - 337px)}.ams-widget.comments .widget-body .chat-body{position:relative;height:100%}.ams-widget.comments .widget-body .chat-footer{position:fixed;bottom:10px}.ams-widget.comments .widget-body,.ams-widget.comments .widget-body .chat-footer{width:calc(100% - 240px)}@media (max-width:767px){.ams-widget.comments .widget-body,.ams-widget.comments .widget-body .chat-footer{width:calc(100% - 10px)}}@media (min-width:768px) and (max-width:979px){.ams-widget.comments .widget-body,.ams-widget.comments .widget-body .chat-footer{width:calc(100% - 20px)}}.minified .ams-widget.comments .widget-body,.minified .ams-widget.comments .widget-body .chat-footer{width:calc(100% - 65px)}@media (max-width:767px){.minified .ams-widget.comments .widget-body,.minified .ams-widget.comments .widget-body .chat-footer{width:calc(100% - 55px)}}.hidden-menu .ams-widget.comments .widget-body,.hidden-menu .ams-widget.comments .widget-body .chat-footer{width:calc(100% - 30px)}.pictograms-manager .pictogram{border-bottom:1px solid silver}.pictograms-manager .pictogram:last-child{border-bottom:none}
+.ams-widget.comments .widget-body{position:fixed;height:calc(100% - 337px)}.ams-widget.comments .widget-body .chat-body{position:relative;height:100%}.ams-widget.comments .widget-body .chat-footer{position:fixed;bottom:10px}.ams-widget.comments .widget-body,.ams-widget.comments .widget-body .chat-footer{width:calc(100% - 240px)}@media (max-width:767px){.ams-widget.comments .widget-body,.ams-widget.comments .widget-body .chat-footer{width:calc(100% - 10px)}}@media (min-width:768px) and (max-width:979px){.ams-widget.comments .widget-body,.ams-widget.comments .widget-body .chat-footer{width:calc(100% - 20px)}}.minified .ams-widget.comments .widget-body,.minified .ams-widget.comments .widget-body .chat-footer{width:calc(100% - 65px)}@media (max-width:767px){.minified .ams-widget.comments .widget-body,.minified .ams-widget.comments .widget-body .chat-footer{width:calc(100% - 55px)}}.hidden-menu .ams-widget.comments .widget-body,.hidden-menu .ams-widget.comments .widget-body .chat-footer{width:calc(100% - 30px)}.pictograms-manager .pictogram{border-bottom:1px solid silver}.pictograms-manager .pictogram:last-child{border-bottom:none}.sortable.gallery{max-height:550px}
--- a/src/pyams_content/skin/resources/js/pyams_content.js	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/skin/resources/js/pyams_content.js	Thu Sep 06 11:27:55 2018 +0200
@@ -173,6 +173,7 @@
 					var source = $(this);
 					var media = source.parents('.media');
 					var gallery = media.parents('.gallery');
+					$('i', source).attr('class', 'fa fa-fw fa-spinner fa-pulse');
 					MyAMS.ajax.post(gallery.data('ams-location') + '/set-media-visibility.json',
 									{object_name: media.data('ams-element-name')},
 									function(result, status) {
@@ -350,24 +351,6 @@
 					MyAMS.initContent(marker);
 				}
 				MyAMS.helpers.sort(toolbar, 'weight');
-			},
-
-			switchAnchor: function() {
-				return function () {
-					var source = $(this);
-					var element = source.parents('tr').first();
-					var container = element.parents('table');
-					MyAMS.ajax.post(container.data('ams-location') + '/' +
-									container.data('ams-anchor-switcher'),
-						{object_name: element.data('ams-element-name')},
-						function (result, status) {
-							if (result.anchor) {
-								$('i', source).attr('class', 'fa fa-fw fa-anchor');
-							} else {
-								$('i', source).attr('class', 'fa fa-fw fa-anchor txt-color-silver opacity-50');
-							}
-						});
-				}
 			}
 		},
 
--- a/src/pyams_content/skin/resources/js/pyams_content.min.js	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/skin/resources/js/pyams_content.min.js	Thu Sep 06 11:27:55 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")},switchAnchor:function(){return function(){var e=t(this),i=e.parents("tr").first(),n=i.parents("table");a.ajax.post(n.data("ams-location")+"/"+n.data("ams-anchor-switcher"),{object_name:i.data("ams-element-name")},function(a,i){a.anchor?t("i",e).attr("class","fa fa-fw fa-anchor"):t("i",e).attr("class","fa fa-fw fa-anchor txt-color-silver opacity-50")})}}},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="manager_themes.widgets.thesaurus_name:list"]',e).val(),n=t('select[name="manager_themes.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)})})},getExtracts:function(e){var i=t(e.currentTarget).parents("form"),n=t('select[name="manager_themes.widgets.thesaurus_name:list"]',i).val(),s=t('select[name="manager_themes.widgets.extract_name:list"]',i),r=s.data("select2");n&&"--NOVALUE--"!==n?a.jsonrpc.post("getExtracts",{thesaurus_name:n},{url:"/api/thesaurus/json"},function(t){r.results.empty(),r.opts.populateResults.call(r,r.results,t.result,{term:""})}):(s.select2("data",null),r.results.empty(),r.opts.populateResults.call(r,r.results,[],{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})})}},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")}},header:{submitEditForm:function(){var e=t(this).parents("form").first();a.form.submit(e,{form_data:{autosubmit:!0}})}},footer:{submitEditForm:function(){var e=t(this).parents("form").first();a.form.submit(e,{form_data:{autosubmit:!0}})}},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(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");t("i",e).attr("class","fa fa-fw fa-spinner fa-pulse"),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="manager_themes.widgets.thesaurus_name:list"]',e).val(),n=t('select[name="manager_themes.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)})})},getExtracts:function(e){var i=t(e.currentTarget).parents("form"),n=t('select[name="manager_themes.widgets.thesaurus_name:list"]',i).val(),s=t('select[name="manager_themes.widgets.extract_name:list"]',i),r=s.data("select2");n&&"--NOVALUE--"!==n?a.jsonrpc.post("getExtracts",{thesaurus_name:n},{url:"/api/thesaurus/json"},function(t){r.results.empty(),r.opts.populateResults.call(r,r.results,t.result,{term:""})}):(s.select2("data",null),r.results.empty(),r.opts.populateResults.call(r,r.results,[],{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})})}},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")}},header:{submitEditForm:function(){var e=t(this).parents("form").first();a.form.submit(e,{form_data:{autosubmit:!0}})}},footer:{submitEditForm:function(){var e=t(this).parents("form").first();a.form.submit(e,{form_data:{autosubmit:!0}})}},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);
--- a/src/pyams_content/skin/resources/less/pyams_content.less	Tue Jul 17 15:12:43 2018 +0200
+++ b/src/pyams_content/skin/resources/less/pyams_content.less	Thu Sep 06 11:27:55 2018 +0200
@@ -50,3 +50,10 @@
 		border-bottom: none;
 	}
 }
+
+
+.sortable {
+	&.gallery {
+		max-height: 550px;
+	}
+}
\ No newline at end of file