Updated JSON output of AJAX forms
authorThierry Florac <thierry.florac@onf.fr>
Tue, 09 Oct 2018 15:06:10 +0200
changeset 1006 cbb65b4b8742
parent 1005 107406cb705c
child 1007 0ca2b4efb158
Updated JSON output of AJAX forms
src/pyams_content/component/association/zmi/paragraph.py
src/pyams_content/component/gallery/paragraph.py
src/pyams_content/component/gallery/zmi/paragraph.py
src/pyams_content/component/keynumber/zmi/paragraph.py
src/pyams_content/component/paragraph/frame.py
src/pyams_content/component/paragraph/pictogram.py
src/pyams_content/component/paragraph/verbatim.py
src/pyams_content/component/paragraph/zmi/__init__.py
src/pyams_content/component/paragraph/zmi/audio.py
src/pyams_content/component/paragraph/zmi/contact.py
src/pyams_content/component/paragraph/zmi/container.py
src/pyams_content/component/paragraph/zmi/frame.py
src/pyams_content/component/paragraph/zmi/header.py
src/pyams_content/component/paragraph/zmi/html.py
src/pyams_content/component/paragraph/zmi/interfaces.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/verbatim.py
src/pyams_content/component/paragraph/zmi/video.py
src/pyams_content/locales/fr/LC_MESSAGES/pyams_content.mo
src/pyams_content/locales/fr/LC_MESSAGES/pyams_content.po
src/pyams_content/locales/pyams_content.pot
src/pyams_content/shared/imagemap/zmi/paragraph.py
src/pyams_content/shared/logo/zmi/paragraph.py
--- a/src/pyams_content/component/association/zmi/paragraph.py	Tue Oct 09 15:05:38 2018 +0200
+++ b/src/pyams_content/component/association/zmi/paragraph.py	Tue Oct 09 15:06:10 2018 +0200
@@ -98,6 +98,15 @@
 
     edit_permission = MANAGE_CONTENT_PERMISSION
 
+    def get_ajax_output(self, changes):
+        output = super(self.__class__, self).get_ajax_output(changes)
+        updated = changes.get(IAssociationParagraph, ())
+        if 'renderer' in updated:
+            output.setdefault('events', []).append(get_json_widget_refresh_event(self.context, self.request,
+                                                                                 AssociationParagraphInnerEditForm,
+                                                                                 'renderer'))
+        return output
+
 
 @adapter_config(context=(IAssociationParagraph, IPyAMSLayer), provides=IParagraphInnerEditor)
 @ajax_config(name='inner-properties.json', context=IAssociationParagraph, layer=IPyAMSLayer,
@@ -114,12 +123,3 @@
             return button.Buttons(IParagraphInnerEditFormButtons)
         else:
             return button.Buttons()
-
-    def get_ajax_output(self, changes):
-        output = super(self.__class__, self).get_ajax_output(changes)
-        updated = changes.get(IAssociationParagraph, ())
-        if 'renderer' in updated:
-            output.setdefault('events', []).append(get_json_widget_refresh_event(self.context, self.request,
-                                                                                 AssociationParagraphInnerEditForm,
-                                                                                 'renderer'))
-        return output
--- a/src/pyams_content/component/gallery/paragraph.py	Tue Oct 09 15:05:38 2018 +0200
+++ b/src/pyams_content/component/gallery/paragraph.py	Tue Oct 09 15:06:10 2018 +0200
@@ -14,7 +14,6 @@
 
 from zope.interface import implementer
 
-from pyams_content import _
 from pyams_content.component.gallery import BaseGallery
 from pyams_content.component.gallery.interfaces import GALLERY_PARAGRAPH_NAME, GALLERY_PARAGRAPH_TYPE, IGalleryParagraph
 from pyams_content.component.paragraph import BaseParagraph, BaseParagraphContentChecker, BaseParagraphFactory
@@ -23,7 +22,6 @@
 from pyams_i18n.interfaces import II18n, II18nManager, INegotiator
 from pyams_utils.adapter import adapter_config
 from pyams_utils.registry import get_utility, utility_config
-from pyams_utils.request import check_request
 from pyams_utils.traversing import get_parent
 
 
@@ -34,18 +32,6 @@
     icon_class = 'fa-picture-o'
     icon_hint = GALLERY_PARAGRAPH_NAME
 
-    @property
-    def title(self):
-        request = check_request()
-        translate = request.localizer.translate
-        nb_medias = len(self)
-        if nb_medias > 1:
-            return translate(_("(gallery contains {0} medias)")).format(nb_medias)
-        elif nb_medias == 1:
-            return translate(_("(gallery contains 1 media)"))
-        else:
-            return translate(_("(empty gallery)"))
-
 
 @utility_config(name=GALLERY_PARAGRAPH_TYPE, provides=IParagraphFactory)
 class GalleryFactory(BaseParagraphFactory):
--- a/src/pyams_content/component/gallery/zmi/paragraph.py	Tue Oct 09 15:05:38 2018 +0200
+++ b/src/pyams_content/component/gallery/zmi/paragraph.py	Tue Oct 09 15:06:10 2018 +0200
@@ -28,7 +28,8 @@
 from pyams_content.component.paragraph.zmi import BaseParagraphAJAXAddForm, BaseParagraphAJAXEditForm, \
     BaseParagraphAddMenu, BaseParagraphPropertiesEditForm, IParagraphInnerEditFormButtons, ParagraphContainerTable, \
     get_json_paragraph_refresh_event
-from pyams_content.component.paragraph.zmi.interfaces import IParagraphContainerView, IParagraphInnerEditor
+from pyams_content.component.paragraph.zmi.interfaces import IParagraphContainerView, IParagraphInnerEditor, \
+    IParagraphTitleValue
 from pyams_content.features.renderer.zmi.widget import RendererFieldWidget
 from pyams_content.interfaces import MANAGE_CONTENT_PERMISSION
 from pyams_content.shared.common.zmi import WfSharedContentPermissionMixin
@@ -83,6 +84,20 @@
         IParagraphContainer(self.context).append(object)
 
 
+@adapter_config(context=(IGalleryParagraph, IPyAMSLayer), provides=IParagraphTitleValue)
+def gallery_paragraph_title_adapter(context, request):
+    """Gallery paragraph title adapter"""
+    translate = request.localizer.translate
+    nb_medias = len(context)
+    if nb_medias > 1:
+        title = translate(_("(gallery contains {0} medias)")).format(nb_medias)
+    elif nb_medias == 1:
+        title = translate(_("(gallery contains 1 media)"))
+    else:
+        title = translate(_("(empty gallery)"))
+    return '<i>{0}</i>'.format(title)
+
+
 @pagelet_config(name='properties.html', context=IGalleryParagraph, layer=IPyAMSLayer,
                 permission=MANAGE_CONTENT_PERMISSION)
 @ajax_config(name='properties.json', context=IGalleryParagraph, layer=IPyAMSLayer,
--- a/src/pyams_content/component/keynumber/zmi/paragraph.py	Tue Oct 09 15:05:38 2018 +0200
+++ b/src/pyams_content/component/keynumber/zmi/paragraph.py	Tue Oct 09 15:06:10 2018 +0200
@@ -95,6 +95,14 @@
 
     edit_permission = MANAGE_CONTENT_PERMISSION
 
+    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
+
 
 @adapter_config(context=(IKeyNumberParagraph, IPyAMSLayer), provides=IParagraphInnerEditor)
 @ajax_config(name='inner-properties.json', context=IKeyNumberParagraph, layer=IPyAMSLayer,
@@ -111,11 +119,3 @@
             return button.Buttons(IParagraphInnerEditFormButtons)
         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/paragraph/frame.py	Tue Oct 09 15:05:38 2018 +0200
+++ b/src/pyams_content/component/paragraph/frame.py	Tue Oct 09 15:06:10 2018 +0200
@@ -12,36 +12,31 @@
 
 __docformat__ = 'restructuredtext'
 
-
-# import standard library
+from zope.interface import implementer
+from zope.schema.fieldproperty import FieldProperty
 
-# import interfaces
 from pyams_content.component.extfile.interfaces import IExtFileContainerTarget
-from pyams_content.component.illustration.interfaces import IIllustrationTarget
+from pyams_content.component.illustration.interfaces import IBasicIllustrationTarget
 from pyams_content.component.links.interfaces import ILinkContainerTarget
+from pyams_content.component.paragraph import BaseParagraph, BaseParagraphContentChecker, BaseParagraphFactory
 from pyams_content.component.paragraph.interfaces import IParagraphFactory
-from pyams_content.component.paragraph.interfaces.frame import IFrameParagraph, FRAME_PARAGRAPH_TYPE, \
-    FRAME_PARAGRAPH_RENDERERS, FRAME_PARAGRAPH_NAME
-from pyams_content.features.checker.interfaces import IContentChecker, MISSING_VALUE, MISSING_LANG_VALUE
+from pyams_content.component.paragraph.interfaces.frame import FRAME_PARAGRAPH_NAME, FRAME_PARAGRAPH_RENDERERS, \
+    FRAME_PARAGRAPH_TYPE, IFrameParagraph
+from pyams_content.features.checker.interfaces import IContentChecker, MISSING_LANG_VALUE, MISSING_VALUE
+from pyams_content.features.renderer import RenderersVocabulary
 from pyams_i18n.interfaces import II18n, II18nManager, INegotiator
-
-# import packages
-from pyams_content.component.paragraph import BaseParagraph, BaseParagraphContentChecker, BaseParagraphFactory
-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.registry import get_utility, utility_config
 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
 
 
 #
 # Frame paragraph
 #
 
-@implementer(IFrameParagraph, IIllustrationTarget, IExtFileContainerTarget, ILinkContainerTarget)
+@implementer(IFrameParagraph, IBasicIllustrationTarget, IExtFileContainerTarget, ILinkContainerTarget)
 @factory_config(provided=IFrameParagraph)
 class FrameParagraph(BaseParagraph):
     """Framed text paragraph"""
@@ -74,7 +69,7 @@
             langs = manager.get_languages()
         else:
             negotiator = get_utility(INegotiator)
-            langs = (negotiator.server_language, )
+            langs = (negotiator.server_language,)
         i18n = II18n(self.context)
         for lang in langs:
             for attr in ('title', 'body'):
--- a/src/pyams_content/component/paragraph/pictogram.py	Tue Oct 09 15:05:38 2018 +0200
+++ b/src/pyams_content/component/paragraph/pictogram.py	Tue Oct 09 15:06:10 2018 +0200
@@ -12,42 +12,37 @@
 
 __docformat__ = 'restructuredtext'
 
-
-# import standard library
 from persistent import Persistent
+from pyramid.events import subscriber
+from zope.container.contained import Contained
+from zope.container.ordered import OrderedContainer
+from zope.interface import implementer
+from zope.lifecycleevent import IObjectAddedEvent, ObjectModifiedEvent
+from zope.location import locate
+from zope.location.interfaces import ISublocations
+from zope.schema.fieldproperty import FieldProperty
+from zope.traversing.interfaces import ITraversable
 
-# import interfaces
-from pyams_content.component.illustration import IIllustrationTarget
+from pyams_catalog.utils import index_object
+from pyams_content.component.illustration import IBasicIllustrationTarget
+from pyams_content.component.paragraph import BaseParagraph, BaseParagraphContentChecker, BaseParagraphFactory
 from pyams_content.component.paragraph import IParagraphFactory
-from pyams_content.component.paragraph.interfaces.pictogram import IPictogramItem, IPictogramContainerTarget, \
-    IPictogramContainer, PICTOGRAM_CONTAINER_KEY, IPictogramParagraph, PICTOGRAM_PARAGRAPH_TYPE, \
-    PICTOGRAM_PARAGRAPH_RENDERERS, PICTOGRAM_PARAGRAPH_NAME
-from pyams_content.features.checker.interfaces import IContentChecker, MISSING_VALUE, MISSING_LANG_VALUE, ERROR_VALUE
+from pyams_content.component.paragraph.interfaces.pictogram import IPictogramContainer, IPictogramContainerTarget, \
+    IPictogramItem, IPictogramParagraph, PICTOGRAM_CONTAINER_KEY, PICTOGRAM_PARAGRAPH_NAME, \
+    PICTOGRAM_PARAGRAPH_RENDERERS, PICTOGRAM_PARAGRAPH_TYPE
+from pyams_content.features.checker import BaseContentChecker
+from pyams_content.features.checker.interfaces import ERROR_VALUE, IContentChecker, MISSING_LANG_VALUE, MISSING_VALUE
+from pyams_content.features.renderer import RenderersVocabulary
 from pyams_content.reference.pictograms.interfaces import IPictogramTable
 from pyams_form.interfaces.form import IFormContextPermissionChecker
 from pyams_i18n.interfaces import II18n, II18nManager, INegotiator
-from zope.lifecycleevent import IObjectAddedEvent, ObjectModifiedEvent
-from zope.location.interfaces import ISublocations
-from zope.traversing.interfaces import ITraversable
-
-# import packages
-from pyams_catalog.utils import index_object
-from pyams_content.component.paragraph import BaseParagraph, BaseParagraphFactory, BaseParagraphContentChecker
-from pyams_content.features.checker import BaseContentChecker
-from pyams_content.features.renderer import RenderersVocabulary
-from pyams_utils.adapter import adapter_config, ContextAdapter, get_annotation_adapter
+from pyams_utils.adapter import ContextAdapter, adapter_config, get_annotation_adapter
 from pyams_utils.factory import factory_config
-from pyams_utils.registry import query_utility, get_current_registry, get_utility, utility_config
+from pyams_utils.registry import get_current_registry, get_utility, query_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 pyams_utils.zodb import volatile_property
-from pyramid.events import subscriber
-from zope.container.contained import Contained
-from zope.container.ordered import OrderedContainer
-from zope.interface import implementer
-from zope.location import locate
-from zope.schema.fieldproperty import FieldProperty
 
 from pyams_content import _
 
@@ -114,7 +109,7 @@
             langs = manager.get_languages()
         else:
             negotiator = get_utility(INegotiator)
-            langs = (negotiator.server_language, )
+            langs = (negotiator.server_language,)
         i18n = II18n(self.context)
         for lang in langs:
             for attr in ('label', 'body'):
@@ -197,14 +192,14 @@
         for pictogram in IPictogramContainer(self.context).values():
             if not pictogram.visible:
                 continue
-            for name, checker in sorted(registry.getAdapters((pictogram, ), IContentChecker),
+            for name, checker in sorted(registry.getAdapters((pictogram,), IContentChecker),
                                         key=lambda x: x[1].weight):
                 output.append('- {0} :'.format(II18n(pictogram).query_attribute('label', request=request)))
                 output.append(checker.get_check_output(request))
         return output
 
 
-@implementer(IPictogramParagraph, IIllustrationTarget)
+@implementer(IPictogramParagraph, IBasicIllustrationTarget)
 @factory_config(provided=IPictogramParagraph)
 class PictogramParagraph(BaseParagraph):
     """Pictograms paragraph"""
@@ -232,7 +227,7 @@
         request = check_request()
         translate = request.localizer.translate
         return II18n(self.context).query_attribute('title', request) or \
-            '({0})'.format(translate(self.context.icon_hint).lower())
+               '({0})'.format(translate(self.context.icon_hint).lower())
 
     def inner_check(self, request):
         output = []
@@ -242,7 +237,7 @@
             langs = manager.get_languages()
         else:
             negotiator = get_utility(INegotiator)
-            langs = (negotiator.server_language, )
+            langs = (negotiator.server_language,)
         i18n = II18n(self.context)
         for lang in langs:
             value = i18n.get_attribute('title', lang, request)
--- a/src/pyams_content/component/paragraph/verbatim.py	Tue Oct 09 15:05:38 2018 +0200
+++ b/src/pyams_content/component/paragraph/verbatim.py	Tue Oct 09 15:06:10 2018 +0200
@@ -12,25 +12,20 @@
 
 __docformat__ = 'restructuredtext'
 
-
-# import standard library
-
 from zope.interface import implementer
 from zope.schema.fieldproperty import FieldProperty
 
-# import interfaces
 from pyams_content.component.illustration.interfaces import IBasicIllustrationTarget
-# import packages
 from pyams_content.component.paragraph import BaseParagraph, BaseParagraphContentChecker, BaseParagraphFactory
 from pyams_content.component.paragraph.interfaces import IParagraphFactory
-from pyams_content.component.paragraph.interfaces.verbatim import IVerbatimParagraph, VERBATIM_PARAGRAPH_TYPE, \
-    VERBATIM_PARAGRAPH_RENDERERS, VERBATIM_PARAGRAPH_NAME
-from pyams_content.features.checker.interfaces import IContentChecker, MISSING_VALUE, MISSING_LANG_VALUE
+from pyams_content.component.paragraph.interfaces.verbatim import IVerbatimParagraph, VERBATIM_PARAGRAPH_NAME, \
+    VERBATIM_PARAGRAPH_RENDERERS, VERBATIM_PARAGRAPH_TYPE
+from pyams_content.features.checker.interfaces import IContentChecker, MISSING_LANG_VALUE, MISSING_VALUE
 from pyams_content.features.renderer import RenderersVocabulary
 from pyams_i18n.interfaces import II18n, II18nManager, INegotiator
 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.registry import get_utility, utility_config
 from pyams_utils.traversing import get_parent
 from pyams_utils.vocabulary import vocabulary_config
 
@@ -74,7 +69,7 @@
             langs = manager.get_languages()
         else:
             negotiator = get_utility(INegotiator)
-            langs = (negotiator.server_language, )
+            langs = (negotiator.server_language,)
         i18n = II18n(self.context)
         for lang in langs:
             for attr in ('title', 'quote', 'charge'):
@@ -85,7 +80,7 @@
                         output.append(translate(MISSING_VALUE).format(field=field_title))
                     else:
                         output.append(translate(MISSING_LANG_VALUE).format(field=field_title, lang=lang))
-        for attr in ('author', ):
+        for attr in ('author',):
             value = getattr(self.context, attr, None)
             if not value:
                 field_title = translate(IVerbatimParagraph[attr].title)
--- a/src/pyams_content/component/paragraph/zmi/__init__.py	Tue Oct 09 15:05:38 2018 +0200
+++ b/src/pyams_content/component/paragraph/zmi/__init__.py	Tue Oct 09 15:06:10 2018 +0200
@@ -19,7 +19,7 @@
 from pyams_content.component.paragraph.interfaces import IBaseParagraph, IParagraphContainerTarget, IParagraphFactory, \
     IParagraphFactorySettings, IParagraphRenderer
 from pyams_content.component.paragraph.zmi.container import ParagraphContainerBaseTable, ParagraphContainerTable
-from pyams_content.component.paragraph.zmi.interfaces import IParagraphContainerView
+from pyams_content.component.paragraph.zmi.interfaces import IParagraphContainerView, IParagraphTitleValue
 from pyams_content.features.renderer.zmi import BaseRenderedContentRenderer
 from pyams_content.interfaces import MANAGE_TOOL_PERMISSION
 from pyams_content.shared.common.interfaces import IWfSharedContent
@@ -95,12 +95,17 @@
     """Get JSON response value for paragraph refresh event"""
     parent = get_parent(context, IParagraphContainerTarget)
     if parent is not None:
+        adapter = request.registry.queryMultiAdapter((context, request), IParagraphTitleValue)
+        if adapter is not None:
+            title = adapter
+        else:
+            title = II18n(context).query_attribute('title', request=request)
         return {
             'event': 'myams.refresh',
             'options': {
                 'handler': 'PyAMS_content.paragraphs.refreshParagraph',
                 'object_id': get_element_id(ParagraphContainerBaseTable, context, parent),
-                'title': II18n(context).query_attribute('title', request=request),
+                'title': title,
                 'visible': context.visible
             }
         }
--- a/src/pyams_content/component/paragraph/zmi/audio.py	Tue Oct 09 15:05:38 2018 +0200
+++ b/src/pyams_content/component/paragraph/zmi/audio.py	Tue Oct 09 15:06:10 2018 +0200
@@ -91,23 +91,6 @@
 
     edit_permission = MANAGE_CONTENT_PERMISSION
 
-
-@adapter_config(context=(IAudioParagraph, IPyAMSLayer), provides=IParagraphInnerEditor)
-@ajax_config(name='inner-properties.json', context=IAudioParagraph, layer=IPyAMSLayer,
-             base=BaseParagraphAJAXEditForm)
-@implementer(IInnerForm, IPropertiesEditForm, IAssociationsParentForm)
-class AudioParagraphPropertiesInnerEditForm(AudioParagraphPropertiesEditForm):
-    """Audio paragraph properties inner edit form"""
-
-    legend = None
-
-    @property
-    def buttons(self):
-        if self.mode == INPUT_MODE:
-            return button.Buttons(IParagraphInnerEditFormButtons)
-        else:
-            return button.Buttons()
-
     def get_ajax_output(self, changes):
         output = super(self.__class__, self).get_ajax_output(changes)
         updated = changes.get(IAudioParagraph, ())
@@ -125,3 +108,20 @@
                     get_json_widget_refresh_event(self.context, self.request,
                                                   AudioParagraphPropertiesInnerEditForm, 'renderer'))
         return output
+
+
+@adapter_config(context=(IAudioParagraph, IPyAMSLayer), provides=IParagraphInnerEditor)
+@ajax_config(name='inner-properties.json', context=IAudioParagraph, layer=IPyAMSLayer,
+             base=BaseParagraphAJAXEditForm)
+@implementer(IInnerForm, IPropertiesEditForm, IAssociationsParentForm)
+class AudioParagraphPropertiesInnerEditForm(AudioParagraphPropertiesEditForm):
+    """Audio paragraph properties inner edit form"""
+
+    legend = None
+
+    @property
+    def buttons(self):
+        if self.mode == INPUT_MODE:
+            return button.Buttons(IParagraphInnerEditFormButtons)
+        else:
+            return button.Buttons()
--- a/src/pyams_content/component/paragraph/zmi/contact.py	Tue Oct 09 15:05:38 2018 +0200
+++ b/src/pyams_content/component/paragraph/zmi/contact.py	Tue Oct 09 15:06:10 2018 +0200
@@ -89,6 +89,20 @@
 
     edit_permission = MANAGE_CONTENT_PERMISSION
 
+    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(IContactParagraph, ())
+        if ('photo' in updated) or ('renderer' in updated):
+            # we have to commit transaction to be able to handle blobs...
+            if 'photo' in updated:
+                ITransactionManager(self.context).get().commit()
+            output.setdefault('events', []).append(get_json_form_refresh_event(self.context, self.request,
+                                                                               ContactParagraphInnerEditForm))
+        return output
+
 
 @adapter_config(context=(IContactParagraph, IPyAMSLayer), provides=IParagraphInnerEditor)
 @ajax_config(name='inner-properties.json', context=IContactParagraph, layer=IPyAMSLayer,
@@ -105,17 +119,3 @@
             return button.Buttons(IParagraphInnerEditFormButtons)
         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(IContactParagraph, ())
-        if ('photo' in updated) or ('renderer' in updated):
-            # we have to commit transaction to be able to handle blobs...
-            if 'photo' in updated:
-                ITransactionManager(self.context).get().commit()
-            output.setdefault('events', []).append(get_json_form_refresh_event(self.context, self.request,
-                                                                               ContactParagraphInnerEditForm))
-        return output
--- a/src/pyams_content/component/paragraph/zmi/container.py	Tue Oct 09 15:05:38 2018 +0200
+++ b/src/pyams_content/component/paragraph/zmi/container.py	Tue Oct 09 15:06:10 2018 +0200
@@ -12,56 +12,52 @@
 
 __docformat__ = 'restructuredtext'
 
-
-# import standard library
 import json
 
-# import interfaces
-from pyams_content.interfaces import MANAGE_CONTENT_PERMISSION
-from pyams_content.component.association.interfaces import IAssociationContainer
-from pyams_content.component.paragraph.interfaces import IParagraphContainerTarget, IParagraphContainer, \
-    IBaseParagraph, IParagraphFactorySettings
-from pyams_content.component.paragraph.zmi.interfaces import IParagraphInnerEditor, IParagraphTitleToolbar, \
-    IParagraphContainerTable, IParagraphContainerView
-from pyams_form.interfaces.form import IFormSecurityContext, IInnerSubForm
-from pyams_i18n.interfaces import II18n
-from pyams_skin.interfaces import IInnerPage, IPageHeader
-from pyams_skin.interfaces.container import ITableElementEditor
-from pyams_skin.layer import IPyAMSLayer
-from pyams_utils.interfaces import VIEW_SYSTEM_PERMISSION
-from pyams_zmi.interfaces.menu import IPropertiesMenu
-from pyams_zmi.layer import IAdminLayer
-from z3c.table.interfaces import IColumn, IValues
-from zope.contentprovider.interfaces import IContentProvider
-
-# import packages
-from pyams_content.component.association.zmi import AssociationsContainerView
-from pyams_content.component.paragraph import BaseParagraph
-from pyams_content.shared.common.zmi import WfModifiedContentColumnMixin
-from pyams_content.skin import pyams_content
-from pyams_form.security import ProtectedFormObjectMixin
-from pyams_pagelet.pagelet import pagelet_config
-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, AttributeSwitcherColumn
-from pyams_skin.viewlet.menu import MenuItem
-from pyams_template.template import template_config
-from pyams_utils.adapter import adapter_config, ContextRequestViewAdapter, NullAdapter
-from pyams_utils.fanstatic import get_resource_path
-from pyams_utils.traversing import get_parent
-from pyams_utils.url import absolute_url
-from pyams_viewlet.manager import viewletmanager_config, WeightOrderedViewletManager, TemplateBasedViewletManager
-from pyams_viewlet.viewlet import viewlet_config, Viewlet
-from pyams_zmi.form import AdminDialogDisplayForm
-from pyams_zmi.view import AdminView, ContainerAdminView
-from pyams_zmi.zmi.table import InnerTableView
 from pyramid.decorator import reify
 from pyramid.exceptions import NotFound
 from pyramid.view import view_config
 from z3c.form import field
 from z3c.table.column import GetAttrColumn
-from zope.interface import implementer, Interface
+from z3c.table.interfaces import IColumn, IValues
+from zope.contentprovider.interfaces import IContentProvider
+from zope.interface import Interface, implementer
+
+from pyams_content.component.association.interfaces import IAssociationContainer
+from pyams_content.component.association.zmi import AssociationsContainerView
+from pyams_content.component.paragraph import BaseParagraph
+from pyams_content.component.paragraph.interfaces import IBaseParagraph, IParagraphContainer, IParagraphContainerTarget, \
+    IParagraphFactorySettings
+from pyams_content.component.paragraph.zmi.interfaces import IParagraphContainerTable, IParagraphContainerView, \
+    IParagraphInnerEditor, IParagraphTitleToolbar, IParagraphTitleValue
+from pyams_content.interfaces import MANAGE_CONTENT_PERMISSION
+from pyams_content.shared.common.zmi import WfModifiedContentColumnMixin
+from pyams_content.skin import pyams_content
+from pyams_form.interfaces.form import IFormSecurityContext, IInnerSubForm
+from pyams_form.security import ProtectedFormObjectMixin
+from pyams_i18n.interfaces import II18n
+from pyams_pagelet.pagelet import pagelet_config
+from pyams_skin.container import switch_element_attribute, switch_element_visibility
+from pyams_skin.interfaces import IInnerPage, IPageHeader
+from pyams_skin.interfaces.container import ITableElementEditor
+from pyams_skin.layer import IPyAMSLayer
+from pyams_skin.page import DefaultPageHeaderAdapter
+from pyams_skin.table import AttributeSwitcherColumn, BaseTable, I18nColumn, ImageColumn, SorterColumn, TrashColumn, \
+    VisibilitySwitcherColumn
+from pyams_skin.viewlet.menu import MenuItem
+from pyams_template.template import template_config
+from pyams_utils.adapter import ContextRequestViewAdapter, NullAdapter, adapter_config
+from pyams_utils.fanstatic import get_resource_path
+from pyams_utils.interfaces import VIEW_SYSTEM_PERMISSION
+from pyams_utils.traversing import get_parent
+from pyams_utils.url import absolute_url
+from pyams_viewlet.manager import TemplateBasedViewletManager, WeightOrderedViewletManager, viewletmanager_config
+from pyams_viewlet.viewlet import Viewlet, viewlet_config
+from pyams_zmi.form import AdminDialogDisplayForm
+from pyams_zmi.interfaces.menu import IPropertiesMenu
+from pyams_zmi.layer import IAdminLayer
+from pyams_zmi.view import AdminView, ContainerAdminView
+from pyams_zmi.zmi.table import InnerTableView
 
 from pyams_content import _
 
@@ -252,7 +248,7 @@
     """Paragraph title toolbar viewlet manager"""
 
 
-def getParagraphTitleHints(item, request, table):
+def get_paragraph_title_hints(item, request, table):
     """Get paragraphs column title hints"""
     registry = request.registry
     provider = registry.queryMultiAdapter((item, request, table), IContentProvider,
@@ -275,6 +271,9 @@
         return '<span class="title">{0}</span>'.format(super(ParagraphContainerBaseTitleColumn, self).renderCell(item))
 
     def getValue(self, obj):
+        adapter = self.request.registry.queryMultiAdapter((obj, self.request), IParagraphTitleValue)
+        if adapter is not None:
+            return adapter
         return II18n(obj).query_attribute('title', request=self.request) or BaseParagraph.empty_title
 
 
@@ -292,11 +291,11 @@
                '        <i class="fa fa-plus-square-o switch"></i>' \
                '    </span>&nbsp;&nbsp;&nbsp;{title}' \
                '</span>'.format(
-                    hint=self.request.localizer.translate(_("Click to open/close all paragraphs editors")),
-                    title=super(ParagraphContainerTitleColumn, self).renderHeadCell())
+            hint=self.request.localizer.translate(_("Click to open/close all paragraphs editors")),
+            title=super(ParagraphContainerTitleColumn, self).renderHeadCell())
 
     def renderCell(self, item):
-        provider = getParagraphTitleHints(item, self.request, self.table) or ''
+        provider = get_paragraph_title_hints(item, self.request, self.table) or ''
         return '<div data-ams-stop-propagation="true" ' \
                '     data-ams-click-handler="PyAMS_content.paragraphs.switchEditor">{provider}' \
                '    <span class="small hint" title="{hint}" data-ams-hint-gravity="e">' \
@@ -304,9 +303,9 @@
                '    </span>&nbsp;&nbsp;&nbsp;<span class="title">{title}</span>' \
                '</div>' \
                '<div class="inner-table-form editor margin-x-10 margin-bottom-0"></div>'.format(
-                    provider=provider,
-                    hint=self.request.localizer.translate(_("Click to open/close paragraph editor")),
-                    title=super(ParagraphContainerTitleColumn, self).renderCell(item))
+            provider=provider,
+            hint=self.request.localizer.translate(_("Click to open/close paragraph editor")),
+            title=super(ParagraphContainerTitleColumn, self).renderCell(item))
 
 
 @template_config(template='templates/paragraph-title-icon.pt', layer=IPyAMSLayer)
@@ -433,7 +432,7 @@
                 view = AssociationsContainerView(paragraph, self.request)
                 view.widget_icon_class = 'fa fa-fw {0}'.format(paragraph.icon_class)
                 view.title = II18n(paragraph).query_attribute('title', request=self.request) or \
-                    BaseParagraph.empty_title
+                             BaseParagraph.empty_title
                 result.append(view)
         return result
 
--- a/src/pyams_content/component/paragraph/zmi/frame.py	Tue Oct 09 15:05:38 2018 +0200
+++ b/src/pyams_content/component/paragraph/zmi/frame.py	Tue Oct 09 15:06:10 2018 +0200
@@ -9,6 +9,8 @@
 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
 # FOR A PARTICULAR PURPOSE.
 #
+from pyams_content.component.paragraph import IBaseParagraph
+
 
 __docformat__ = 'restructuredtext'
 
@@ -24,8 +26,8 @@
     PARAGRAPH_HIDDEN_FIELDS
 from pyams_content.component.paragraph.interfaces.frame import FRAME_PARAGRAPH_TYPE, IFrameParagraph
 from pyams_content.component.paragraph.zmi import BaseParagraphAJAXAddForm, BaseParagraphAJAXEditForm, \
-    BaseParagraphAddMenu, BaseParagraphPropertiesEditForm, IParagraphInnerEditFormButtons, \
-    get_json_paragraph_toolbar_refresh_event
+    BaseParagraphAddMenu, BaseParagraphPropertiesEditForm, IParagraphInnerEditFormButtons, IParagraphTitleValue, \
+    get_json_paragraph_toolbar_refresh_event, get_json_paragraph_refresh_event
 from pyams_content.component.paragraph.zmi.container import ParagraphContainerTable, \
     ParagraphTitleToolbarViewletManager
 from pyams_content.component.paragraph.zmi.interfaces import IParagraphContainerView, IParagraphInnerEditor
@@ -33,12 +35,15 @@
 from pyams_content.interfaces import MANAGE_CONTENT_PERMISSION
 from pyams_form.form import ajax_config
 from pyams_form.interfaces.form import IInnerForm
+from pyams_i18n.interfaces import II18n
 from pyams_pagelet.pagelet import pagelet_config
 from pyams_skin.event import get_json_switched_table_refresh_event, get_json_widget_refresh_event
 from pyams_skin.interfaces.tinymce import ITinyMCEConfiguration
 from pyams_skin.interfaces.viewlet import IToolbarAddingMenu
 from pyams_skin.layer import IPyAMSLayer
 from pyams_utils.adapter import ContextRequestAdapter, adapter_config
+from pyams_utils.html import html_to_text
+from pyams_utils.text import get_text_start
 from pyams_utils.traversing import get_parent
 from pyams_viewlet.viewlet import viewlet_config
 from pyams_zmi.form import AdminDialogAddForm
@@ -73,6 +78,18 @@
 # Framed text paragraph
 #
 
+@adapter_config(context=(IFrameParagraph, IPyAMSLayer), provides=IParagraphTitleValue)
+def frame_paragraph_title_adapter(context, request):
+    """Frame paragraph title adapter"""
+    i18n = II18n(context)
+    title = i18n.query_attribute('title', request=request)
+    if not title:
+        body = i18n.query_attribute('body', request=request)
+        if body:
+            title = '<i>{0}</i>'.format(get_text_start(html_to_text(body), 50, 10))
+    return title
+
+
 @viewlet_config(name='add-frame-paragraph.menu', context=IParagraphContainerTarget, view=IParagraphContainerView,
                 layer=IPyAMSLayer, manager=IToolbarAddingMenu, weight=600)
 class FrameParagraphAddMenu(BaseParagraphAddMenu):
@@ -140,6 +157,9 @@
     def get_ajax_output(self, changes):
         output = super(self.__class__, self).get_ajax_output(changes)
         if 'body' in changes.get(IFrameParagraph, ()):
+            if 'title' not in changes.get(IBaseParagraph, ()):
+                output.setdefault('events', []).append(
+                    get_json_paragraph_refresh_event(self.context, self.request))
             # refresh associations count markers
             parent = get_parent(self.context, IAssociationContainerTarget)
             output.setdefault('events', []).append(
@@ -166,11 +186,3 @@
             return button.Buttons(IParagraphInnerEditFormButtons)
         else:
             return button.Buttons()
-
-    def get_ajax_output(self, changes):
-        output = super(self.__class__, self).get_ajax_output(changes)
-        updated = changes.get(IFrameParagraph, ())
-        if 'renderer' in updated:
-            output.setdefault('events', []).append(
-                get_json_widget_refresh_event(self.context, self.request, FrameParagraphInnerEditForm, 'renderer'))
-        return output
--- a/src/pyams_content/component/paragraph/zmi/header.py	Tue Oct 09 15:05:38 2018 +0200
+++ b/src/pyams_content/component/paragraph/zmi/header.py	Tue Oct 09 15:06:10 2018 +0200
@@ -56,8 +56,13 @@
 
     def get_ajax_output(self, changes):
         output = super(self.__class__, self).get_ajax_output(changes)
-        if 'header' in changes.get(IHeaderParagraph, ()):
+        updated = changes.get(IHeaderParagraph, ())
+        if 'header' in updated:
             output.setdefault('events', []).append(get_json_paragraph_refresh_event(self.context, self.request))
+        if 'renderer' in updated:
+            output.setdefault('events', []).append(get_json_widget_refresh_event(self.context, self.request,
+                                                                                 HeaderParagraphInnerEditForm,
+                                                                                 'renderer'))
         return output
 
 
@@ -78,14 +83,3 @@
             return button.Buttons(IParagraphInnerEditFormButtons)
         else:
             return button.Buttons()
-
-    def get_ajax_output(self, changes):
-        output = super(self.__class__, self).get_ajax_output(changes)
-        updated = changes.get(IHeaderParagraph, ())
-        if 'header' in updated:
-            output.setdefault('events', []).append(get_json_paragraph_refresh_event(self.context, self.request))
-        if 'renderer' in updated:
-            output.setdefault('events', []).append(get_json_widget_refresh_event(self.context, self.request,
-                                                                                 HeaderParagraphInnerEditForm,
-                                                                                 'renderer'))
-        return output
--- a/src/pyams_content/component/paragraph/zmi/html.py	Tue Oct 09 15:05:38 2018 +0200
+++ b/src/pyams_content/component/paragraph/zmi/html.py	Tue Oct 09 15:06:10 2018 +0200
@@ -20,25 +20,28 @@
 from pyams_content.component.association.zmi import AssociationsTable
 from pyams_content.component.association.zmi.interfaces import IAssociationsParentForm
 from pyams_content.component.paragraph.html import HTMLParagraph, RawParagraph
-from pyams_content.component.paragraph.interfaces import IParagraphContainer, IParagraphContainerTarget, \
-    IParagraphFactorySettings, PARAGRAPH_HIDDEN_FIELDS
+from pyams_content.component.paragraph.interfaces import IBaseParagraph, IParagraphContainer, \
+    IParagraphContainerTarget, IParagraphFactorySettings, PARAGRAPH_HIDDEN_FIELDS
 from pyams_content.component.paragraph.interfaces.html import HTML_PARAGRAPH_TYPE, IHTMLParagraph, IRawParagraph, \
     RAW_PARAGRAPH_TYPE
 from pyams_content.component.paragraph.zmi import BaseParagraphAJAXAddForm, BaseParagraphAJAXEditForm, \
-    BaseParagraphAddMenu, BaseParagraphPropertiesEditForm, IParagraphInnerEditFormButtons, \
-    get_json_paragraph_toolbar_refresh_event
+    BaseParagraphAddMenu, BaseParagraphPropertiesEditForm, IParagraphInnerEditFormButtons, IParagraphTitleValue, \
+    get_json_paragraph_refresh_event, get_json_paragraph_toolbar_refresh_event
 from pyams_content.component.paragraph.zmi.interfaces import IParagraphContainerView, IParagraphInnerEditor
 from pyams_content.features.renderer.zmi.widget import RendererFieldWidget
 from pyams_content.interfaces import MANAGE_CONTENT_PERMISSION
 from pyams_form.form import ajax_config
 from pyams_form.interfaces.form import IInnerForm
 from pyams_form.security import ProtectedFormObjectMixin
+from pyams_i18n.interfaces import II18n
 from pyams_pagelet.pagelet import pagelet_config
 from pyams_skin.event import get_json_switched_table_refresh_event
 from pyams_skin.interfaces.viewlet import IToolbarAddingMenu
 from pyams_skin.layer import IPyAMSLayer
 from pyams_skin.viewlet.menu import MenuDivider
 from pyams_utils.adapter import adapter_config
+from pyams_utils.html import html_to_text
+from pyams_utils.text import get_text_start
 from pyams_utils.traversing import get_parent
 from pyams_viewlet.viewlet import viewlet_config
 from pyams_zmi.form import AdminDialogAddForm
@@ -180,6 +183,18 @@
         IParagraphContainer(self.context).append(object)
 
 
+@adapter_config(context=(IHTMLParagraph, IPyAMSLayer), provides=IParagraphTitleValue)
+def html_paragraph_title_adapter(context, request):
+    """HTML paragraph title adapter"""
+    i18n = II18n(context)
+    title = i18n.query_attribute('title', request=request)
+    if not title:
+        body = i18n.query_attribute('body', request=request)
+        if body:
+            title = '<i>{0}</i>'.format(get_text_start(html_to_text(body), 50, 10))
+    return title
+
+
 @pagelet_config(name='properties.html', context=IHTMLParagraph, layer=IPyAMSLayer,
                 permission=MANAGE_CONTENT_PERMISSION)
 @ajax_config(name='properties.json', context=IHTMLParagraph, layer=IPyAMSLayer,
@@ -211,6 +226,10 @@
     def get_ajax_output(self, changes):
         output = super(self.__class__, self).get_ajax_output(changes)
         if 'body' in changes.get(IHTMLParagraph, ()):
+            # refresh paragraph title
+            if 'title' not in changes.get(IBaseParagraph, ()):
+                output.setdefault('events', []).append(
+                    get_json_paragraph_refresh_event(self.context, self.request))
             # refresh associations count markers
             parent = get_parent(self.context, IAssociationContainerTarget)
             output.setdefault('events', []).append(
@@ -236,15 +255,3 @@
             return button.Buttons(IParagraphInnerEditFormButtons)
         else:
             return button.Buttons()
-
-    def get_ajax_output(self, changes):
-        output = super(self.__class__, self).get_ajax_output(changes)
-        if 'body' in changes.get(IHTMLParagraph, ()):
-            # refresh associations count markers
-            parent = get_parent(self.context, IAssociationContainerTarget)
-            output.setdefault('events', []).append(
-                get_json_paragraph_toolbar_refresh_event(parent, self.request))
-            # refresh associations table
-            output.setdefault('events', []).append(
-                get_json_switched_table_refresh_event(self.context, self.request, AssociationsTable))
-        return output
--- a/src/pyams_content/component/paragraph/zmi/interfaces.py	Tue Oct 09 15:05:38 2018 +0200
+++ b/src/pyams_content/component/paragraph/zmi/interfaces.py	Tue Oct 09 15:06:10 2018 +0200
@@ -35,6 +35,10 @@
     """Paragraphs view parent form marker interface"""
 
 
+class IParagraphTitleValue(Interface):
+    """Paragraph title column value adapter"""
+
+
 class IParagraphTitleToolbar(IViewletManager):
     """Paragraph title toolbar viewlet manager"""
 
--- a/src/pyams_content/component/paragraph/zmi/keypoint.py	Tue Oct 09 15:05:38 2018 +0200
+++ b/src/pyams_content/component/paragraph/zmi/keypoint.py	Tue Oct 09 15:06:10 2018 +0200
@@ -96,6 +96,16 @@
         if 'body' in self.widgets:
             self.widgets['body'].widget_css_class = 'input height-100'
 
+    def get_ajax_output(self, changes):
+        output = super(self.__class__, self).get_ajax_output(changes)
+        updated = changes.get(IKeypointsParagraph, ())
+        if 'renderer' in updated:
+            form = KeypointsParagraphInnerEditForm(self.context, self.request)
+            form.update()
+            output.setdefault('events', []).append(
+                get_json_widget_refresh_event(self.context, self.request, KeypointsParagraphInnerEditForm, 'renderer'))
+        return output
+
 
 @adapter_config(context=(IKeypointsParagraph, IPyAMSLayer), provides=IParagraphInnerEditor)
 @ajax_config(name='inner-properties.json', context=IKeypointsParagraph, layer=IPyAMSLayer,
@@ -114,13 +124,3 @@
             return button.Buttons(IParagraphInnerEditFormButtons)
         else:
             return button.Buttons()
-
-    def get_ajax_output(self, changes):
-        output = super(self.__class__, self).get_ajax_output(changes)
-        updated = changes.get(IKeypointsParagraph, ())
-        if 'renderer' in updated:
-            form = KeypointsParagraphInnerEditForm(self.context, self.request)
-            form.update()
-            output.setdefault('events', []).append(
-                get_json_widget_refresh_event(self.context, self.request, KeypointsParagraphInnerEditForm, 'renderer'))
-        return output
--- a/src/pyams_content/component/paragraph/zmi/map.py	Tue Oct 09 15:05:38 2018 +0200
+++ b/src/pyams_content/component/paragraph/zmi/map.py	Tue Oct 09 15:06:10 2018 +0200
@@ -91,6 +91,17 @@
 
         edit_permission = MANAGE_CONTENT_PERMISSION
 
+        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
+
 
     @adapter_config(context=(IMapParagraph, IPyAMSLayer), provides=IParagraphInnerEditor)
     @ajax_config(name='inner-properties.json', context=IMapParagraph, layer=IPyAMSLayer,
@@ -107,14 +118,3 @@
                 return button.Buttons(IParagraphInnerEditFormButtons)
             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 Oct 09 15:05:38 2018 +0200
+++ b/src/pyams_content/component/paragraph/zmi/milestone.py	Tue Oct 09 15:06:10 2018 +0200
@@ -119,6 +119,17 @@
 
     edit_permission = MANAGE_CONTENT_PERMISSION
 
+    def get_ajax_output(self, changes):
+        output = super(self.__class__, self).get_ajax_output(changes)
+        updated = changes.get(IMilestoneParagraph, ())
+        if 'renderer' in updated:
+            form = MilestoneParagraphInnerEditForm(self.context, self.request)
+            form.update()
+            output.setdefault('events', []) \
+                .append(get_json_widget_refresh_event(self.context, self.request,
+                                                      MilestoneParagraphInnerEditForm, 'renderer'))
+        return output
+
 
 @adapter_config(context=(IMilestoneParagraph, IPyAMSLayer), provides=IParagraphInnerEditor)
 @ajax_config(name='inner-properties.json', context=IMilestoneParagraph, layer=IPyAMSLayer,
@@ -136,17 +147,6 @@
         else:
             return button.Buttons()
 
-    def get_ajax_output(self, changes):
-        output = super(self.__class__, self).get_ajax_output(changes)
-        updated = changes.get(IMilestoneParagraph, ())
-        if 'renderer' in updated:
-            form = MilestoneParagraphInnerEditForm(self.context, self.request)
-            form.update()
-            output.setdefault('events', []) \
-                .append(get_json_widget_refresh_event(self.context, self.request,
-                                                      MilestoneParagraphInnerEditForm, 'renderer'))
-        return output
-
 
 #
 # Milestone items table view
--- a/src/pyams_content/component/paragraph/zmi/pictogram.py	Tue Oct 09 15:05:38 2018 +0200
+++ b/src/pyams_content/component/paragraph/zmi/pictogram.py	Tue Oct 09 15:06:10 2018 +0200
@@ -124,6 +124,14 @@
 
     edit_permission = MANAGE_CONTENT_PERMISSION
 
+    def get_ajax_output(self, changes):
+        output = super(self.__class__, self).get_ajax_output(changes)
+        updated = changes.get(IPictogramParagraph, ())
+        if 'renderer' in updated:
+            output.setdefault('events', []).append(
+                get_json_widget_refresh_event(self.context, self.request, PictogramParagraphInnerEditForm, 'renderer'))
+        return output
+
 
 @adapter_config(context=(IPictogramParagraph, IPyAMSLayer), provides=IParagraphInnerEditor)
 @ajax_config(name='inner-properties.json', context=IPictogramParagraph, layer=IPyAMSLayer,
@@ -141,14 +149,6 @@
         else:
             return button.Buttons()
 
-    def get_ajax_output(self, changes):
-        output = super(self.__class__, self).get_ajax_output(changes)
-        updated = changes.get(IPictogramParagraph, ())
-        if 'renderer' in updated:
-            output.setdefault('events', []).append(
-                get_json_widget_refresh_event(self.context, self.request, PictogramParagraphInnerEditForm, 'renderer'))
-        return output
-
 
 #
 # Pictogram items table view
--- a/src/pyams_content/component/paragraph/zmi/verbatim.py	Tue Oct 09 15:05:38 2018 +0200
+++ b/src/pyams_content/component/paragraph/zmi/verbatim.py	Tue Oct 09 15:06:10 2018 +0200
@@ -9,6 +9,8 @@
 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
 # FOR A PARTICULAR PURPOSE.
 #
+from pyams_content.component.paragraph import IBaseParagraph
+
 
 __docformat__ = 'restructuredtext'
 
@@ -22,17 +24,20 @@
 from pyams_content.component.paragraph.interfaces.verbatim import IVerbatimParagraph, VERBATIM_PARAGRAPH_TYPE
 from pyams_content.component.paragraph.verbatim import VerbatimParagraph
 from pyams_content.component.paragraph.zmi import BaseParagraphAJAXAddForm, BaseParagraphAJAXEditForm, \
-    BaseParagraphAddMenu, BaseParagraphPropertiesEditForm, IParagraphInnerEditFormButtons
+    BaseParagraphAddMenu, BaseParagraphPropertiesEditForm, IParagraphInnerEditFormButtons, IParagraphTitleValue, \
+    get_json_paragraph_refresh_event
 from pyams_content.component.paragraph.zmi.interfaces import IParagraphContainerView, IParagraphInnerEditor
 from pyams_content.features.renderer.zmi.widget import RendererFieldWidget
 from pyams_content.interfaces import MANAGE_CONTENT_PERMISSION
 from pyams_form.form import ajax_config
 from pyams_form.interfaces.form import IInnerForm
+from pyams_i18n.interfaces import II18n
 from pyams_pagelet.pagelet import pagelet_config
 from pyams_skin.event import get_json_widget_refresh_event
 from pyams_skin.interfaces.viewlet import IToolbarAddingMenu
 from pyams_skin.layer import IPyAMSLayer
 from pyams_utils.adapter import adapter_config
+from pyams_utils.text import get_text_start
 from pyams_viewlet.viewlet import viewlet_config
 from pyams_zmi.form import AdminDialogAddForm
 from pyams_zmi.interfaces import IPropertiesEditForm
@@ -75,6 +80,18 @@
         IParagraphContainer(self.context).append(object)
 
 
+@adapter_config(context=(IVerbatimParagraph, IPyAMSLayer), provides=IParagraphTitleValue)
+def verbatim_paragraph_title_adapter(context, request):
+    """Verbatim paragraph title adapter"""
+    i18n = II18n(context)
+    title = i18n.query_attribute('title', request=request)
+    if not title:
+        quote = i18n.query_attribute('quote', request=request)
+        if quote:
+            title = '<i>{0}</i>'.format(get_text_start(quote, 50, 10))
+    return title
+
+
 @pagelet_config(name='properties.html', context=IVerbatimParagraph, layer=IPyAMSLayer,
                 permission=MANAGE_CONTENT_PERMISSION)
 @ajax_config(name='properties.json', context=IVerbatimParagraph, layer=IPyAMSLayer,
@@ -92,6 +109,14 @@
 
     edit_permission = MANAGE_CONTENT_PERMISSION
 
+    def get_ajax_output(self, changes):
+        output = super(self.__class__, self).get_ajax_output(changes)
+        if 'quote' in changes.get(IVerbatimParagraph, ()):
+            if 'title' not in changes.get(IBaseParagraph, ()):
+                output.setdefault('events', []).append(
+                    get_json_paragraph_refresh_event(self.context, self.request))
+        return output
+
 
 @adapter_config(context=(IVerbatimParagraph, IPyAMSLayer), provides=IParagraphInnerEditor)
 @ajax_config(name='inner-properties.json', context=IVerbatimParagraph, layer=IPyAMSLayer,
@@ -108,11 +133,3 @@
             return button.Buttons(IParagraphInnerEditFormButtons)
         else:
             return button.Buttons()
-
-    def get_ajax_output(self, changes):
-        output = super(self.__class__, self).get_ajax_output(changes)
-        updated = changes.get(IVerbatimParagraph, ())
-        if 'renderer' in updated:
-            output.setdefault('events', []).append(
-                get_json_widget_refresh_event(self.context, self.request, VerbatimParagraphInnerEditForm, 'renderer'))
-        return output
--- a/src/pyams_content/component/paragraph/zmi/video.py	Tue Oct 09 15:05:38 2018 +0200
+++ b/src/pyams_content/component/paragraph/zmi/video.py	Tue Oct 09 15:06:10 2018 +0200
@@ -92,23 +92,6 @@
 
     edit_permission = MANAGE_CONTENT_PERMISSION
 
-
-@adapter_config(context=(IVideoParagraph, IPyAMSLayer), provides=IParagraphInnerEditor)
-@ajax_config(name='inner-properties.json', context=IVideoParagraph, layer=IPyAMSLayer,
-             base=BaseParagraphAJAXEditForm)
-@implementer(IInnerForm, IPropertiesEditForm, IAssociationsParentForm)
-class VideoParagraphPropertiesInnerEditForm(VideoParagraphPropertiesEditForm):
-    """Video paragraph properties inner edit form"""
-
-    legend = None
-
-    @property
-    def buttons(self):
-        if self.mode == INPUT_MODE:
-            return button.Buttons(IParagraphInnerEditFormButtons)
-        else:
-            return button.Buttons()
-
     def get_ajax_output(self, changes):
         output = super(self.__class__, self).get_ajax_output(changes)
         updated = changes.get(IVideoParagraph, ())
@@ -126,3 +109,20 @@
                     get_json_widget_refresh_event(self.context, self.request,
                                                   VideoParagraphPropertiesInnerEditForm, 'renderer'))
         return output
+
+
+@adapter_config(context=(IVideoParagraph, IPyAMSLayer), provides=IParagraphInnerEditor)
+@ajax_config(name='inner-properties.json', context=IVideoParagraph, layer=IPyAMSLayer,
+             base=BaseParagraphAJAXEditForm)
+@implementer(IInnerForm, IPropertiesEditForm, IAssociationsParentForm)
+class VideoParagraphPropertiesInnerEditForm(VideoParagraphPropertiesEditForm):
+    """Video paragraph properties inner edit form"""
+
+    legend = None
+
+    @property
+    def buttons(self):
+        if self.mode == INPUT_MODE:
+            return button.Buttons(IParagraphInnerEditFormButtons)
+        else:
+            return button.Buttons()
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 Oct 09 15:05:38 2018 +0200
+++ b/src/pyams_content/locales/fr/LC_MESSAGES/pyams_content.po	Tue Oct 09 15:06:10 2018 +0200
@@ -5,7 +5,7 @@
 msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE 1.0\n"
-"POT-Creation-Date: 2018-10-08 09:36+0200\n"
+"POT-Creation-Date: 2018-10-09 13:56+0200\n"
 "PO-Revision-Date: 2015-09-10 10:42+0200\n"
 "Last-Translator: Thierry Florac <tflorac@ulthar.net>\n"
 "Language-Team: French\n"
@@ -80,26 +80,13 @@
 msgid "Guest user (role)"
 msgstr "Invité (rôle)"
 
-#: src/pyams_content/component/gallery/paragraph.py:45
-msgid "(gallery contains 1 media)"
-msgstr "(1 média dans la galerie)"
-
-#: src/pyams_content/component/gallery/paragraph.py:47
-msgid "(empty gallery)"
-msgstr "(aucun média dans la galerie)"
-
-#: src/pyams_content/component/gallery/paragraph.py:43
-#, python-format
-msgid "(gallery contains {0} medias)"
-msgstr "({0} medias dans la galerie)"
-
 #: src/pyams_content/component/gallery/__init__.py:155
 msgid "Gallery"
 msgstr "Galerie de médias"
 
 #: src/pyams_content/component/gallery/zmi/file.py:57
 #: src/pyams_content/component/gallery/zmi/file.py:69
-#: src/pyams_content/component/gallery/zmi/paragraph.py:148
+#: src/pyams_content/component/gallery/zmi/paragraph.py:163
 msgid "Add media(s)"
 msgstr "Ajouter un média / un groupe de médias (zip)"
 
@@ -119,22 +106,35 @@
 msgid "Audio content"
 msgstr "Contenu audio associé"
 
-#: src/pyams_content/component/gallery/zmi/paragraph.py:60
+#: src/pyams_content/component/gallery/zmi/paragraph.py:61
 msgid "Medias gallery..."
 msgstr "Galerie de médias"
 
-#: src/pyams_content/component/gallery/zmi/paragraph.py:73
+#: src/pyams_content/component/gallery/zmi/paragraph.py:74
 msgid "Add new gallery"
 msgstr "Ajout d'une galerie de médias"
 
-#: src/pyams_content/component/gallery/zmi/paragraph.py:95
+#: src/pyams_content/component/gallery/zmi/paragraph.py:110
 msgid "Edit gallery properties"
 msgstr "Propriétés de la galerie de médias"
 
-#: src/pyams_content/component/gallery/zmi/paragraph.py:169
+#: src/pyams_content/component/gallery/zmi/paragraph.py:95
+msgid "(gallery contains 1 media)"
+msgstr "(1 média dans la galerie)"
+
+#: src/pyams_content/component/gallery/zmi/paragraph.py:97
+msgid "(empty gallery)"
+msgstr "(aucun média dans la galerie)"
+
+#: src/pyams_content/component/gallery/zmi/paragraph.py:184
 msgid "Media(s) successfully added"
 msgstr "Les médias ont été ajoutés dans la galerie."
 
+#: src/pyams_content/component/gallery/zmi/paragraph.py:93
+#, python-format
+msgid "(gallery contains {0} medias)"
+msgstr "({0} medias dans la galerie)"
+
 #: src/pyams_content/component/gallery/zmi/__init__.py:57
 msgid "Update gallery properties"
 msgstr "Propriétés de la galerie de médias"
@@ -281,8 +281,8 @@
 
 #: src/pyams_content/component/gallery/interfaces/__init__.py:106
 #: src/pyams_content/component/keynumber/portlet/interfaces/__init__.py:31
-#: src/pyams_content/component/paragraph/zmi/milestone.py:238
-#: src/pyams_content/component/paragraph/zmi/container.py:270
+#: src/pyams_content/component/paragraph/zmi/milestone.py:235
+#: src/pyams_content/component/paragraph/zmi/container.py:266
 #: src/pyams_content/component/paragraph/interfaces/milestone.py:43
 #: src/pyams_content/shared/common/zmi/reverse.py:73
 #: src/pyams_content/shared/common/zmi/dashboard.py:103
@@ -475,15 +475,15 @@
 msgid "Key numbers"
 msgstr "Chiffres-clés"
 
-#: src/pyams_content/component/keynumber/zmi/paragraph.py:53
+#: src/pyams_content/component/keynumber/zmi/paragraph.py:49
 msgid "Key numbers..."
 msgstr "Chiffres-clés"
 
-#: src/pyams_content/component/keynumber/zmi/paragraph.py:66
+#: src/pyams_content/component/keynumber/zmi/paragraph.py:62
 msgid "Add new key number paragraph"
 msgstr "Ajout de chiffres-clés"
 
-#: src/pyams_content/component/keynumber/zmi/paragraph.py:94
+#: src/pyams_content/component/keynumber/zmi/paragraph.py:90
 msgid "Edit key number paragraph properties"
 msgstr "Propriétés des chiffres-clés"
 
@@ -507,7 +507,7 @@
 #: 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:62
-#: src/pyams_content/component/paragraph/zmi/pictogram.py:276
+#: src/pyams_content/component/paragraph/zmi/pictogram.py:273
 #: src/pyams_content/component/paragraph/interfaces/milestone.py:81
 #: src/pyams_content/component/paragraph/interfaces/video.py:50
 #: src/pyams_content/component/paragraph/interfaces/pictogram.py:58
@@ -605,8 +605,8 @@
 #: src/pyams_content/component/illustration/__init__.py:171
 #: src/pyams_content/component/illustration/thesaurus.py:32
 #: src/pyams_content/component/illustration/zmi/paragraph.py:153
-#: src/pyams_content/component/illustration/zmi/__init__.py:51
-#: src/pyams_content/component/illustration/zmi/__init__.py:112
+#: src/pyams_content/component/illustration/zmi/__init__.py:57
+#: src/pyams_content/component/illustration/zmi/__init__.py:118
 #: src/pyams_content/component/illustration/interfaces/__init__.py:97
 msgid "Illustration"
 msgstr "Illustration"
@@ -624,11 +624,15 @@
 msgid "Edit illustration properties"
 msgstr "Propriétés de l'illustration"
 
-#: src/pyams_content/component/illustration/zmi/__init__.py:146
+#: src/pyams_content/component/illustration/zmi/__init__.py:152
 msgid "Navigation link illustration"
 msgstr "Illustration de navigation"
 
-#: src/pyams_content/component/illustration/zmi/__init__.py:114
+#: src/pyams_content/component/illustration/zmi/__init__.py:169
+msgid "Add illustration"
+msgstr "Ajouter une illustration"
+
+#: src/pyams_content/component/illustration/zmi/__init__.py:120
 msgid "Header illustration"
 msgstr "Illustration d'en-tête"
 
@@ -678,7 +682,7 @@
 msgid "no visible paragraph"
 msgstr "aucun bloc de contenu visible"
 
-#: src/pyams_content/component/paragraph/pictogram.py:135
+#: src/pyams_content/component/paragraph/pictogram.py:130
 msgid "Selected pictogram is missing"
 msgstr "le pictogramme sélectionné est introuvable"
 
@@ -686,74 +690,74 @@
 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:77
+#: src/pyams_content/component/paragraph/zmi/milestone.py:74
 msgid "Milestones..."
 msgstr "Chronologie"
 
-#: src/pyams_content/component/paragraph/zmi/milestone.py:90
+#: src/pyams_content/component/paragraph/zmi/milestone.py:87
 msgid "Add new milestone paragraph"
 msgstr "Ajout d'une chronologie"
 
-#: src/pyams_content/component/paragraph/zmi/milestone.py:117
+#: src/pyams_content/component/paragraph/zmi/milestone.py:114
 msgid "Edit milestone paragraph properties"
 msgstr "Propriétés de la chronologie"
 
-#: src/pyams_content/component/paragraph/zmi/milestone.py:250
+#: src/pyams_content/component/paragraph/zmi/milestone.py:247
 #: src/pyams_content/component/paragraph/interfaces/milestone.py:47
 msgid "Associated label"
 msgstr "Information associée"
 
-#: src/pyams_content/component/paragraph/zmi/milestone.py:262
+#: src/pyams_content/component/paragraph/zmi/milestone.py:259
 #: src/pyams_content/component/paragraph/interfaces/milestone.py:51
 msgid "Anchor"
 msgstr "Ancre"
 
-#: src/pyams_content/component/paragraph/zmi/milestone.py:291
+#: src/pyams_content/component/paragraph/zmi/milestone.py:288
 #: src/pyams_content/component/paragraph/interfaces/milestone.py:74
 msgid "Milestones"
 msgstr "Chronologie"
 
-#: src/pyams_content/component/paragraph/zmi/milestone.py:306
+#: src/pyams_content/component/paragraph/zmi/milestone.py:303
 msgid "Add milestone"
 msgstr "Ajouter un jalon"
 
-#: src/pyams_content/component/paragraph/zmi/milestone.py:319
+#: src/pyams_content/component/paragraph/zmi/milestone.py:316
 msgid "Add new milestone"
 msgstr "Ajout d'un jalon"
 
-#: src/pyams_content/component/paragraph/zmi/milestone.py:346
+#: src/pyams_content/component/paragraph/zmi/milestone.py:343
 msgid "Edit milestone properties"
 msgstr "Propriétés du jalon"
 
-#: src/pyams_content/component/paragraph/zmi/milestone.py:334
+#: src/pyams_content/component/paragraph/zmi/milestone.py:331
 msgid "Milestone was correctly added"
 msgstr "Le jalon a été ajouté."
 
-#: src/pyams_content/component/paragraph/zmi/milestone.py:277
+#: src/pyams_content/component/paragraph/zmi/milestone.py:274
 msgid "(missing paragraph)"
 msgstr "(paragraphe supprimé)"
 
-#: src/pyams_content/component/paragraph/zmi/keypoint.py:51
+#: src/pyams_content/component/paragraph/zmi/keypoint.py:46
 msgid "Key points..."
 msgstr "Points clés"
 
-#: src/pyams_content/component/paragraph/zmi/keypoint.py:64
+#: src/pyams_content/component/paragraph/zmi/keypoint.py:59
 msgid "Add new key points paragraph"
 msgstr "Ajout de points clés"
 
-#: src/pyams_content/component/paragraph/zmi/keypoint.py:91
+#: src/pyams_content/component/paragraph/zmi/keypoint.py:86
 msgid "Edit key points paragraph properties"
 msgstr "Propriétés des points clés"
 
-#: src/pyams_content/component/paragraph/zmi/__init__.py:64
+#: src/pyams_content/component/paragraph/zmi/__init__.py:60
 msgid "Content block types..."
 msgstr "Types de blocs de contenu"
 
-#: src/pyams_content/component/paragraph/zmi/__init__.py:78
+#: src/pyams_content/component/paragraph/zmi/__init__.py:74
 msgid "Content block types"
 msgstr "Types de blocs de contenu"
 
-#: src/pyams_content/component/paragraph/zmi/__init__.py:88
+#: src/pyams_content/component/paragraph/zmi/__init__.py:84
 msgid ""
 "You can define which types of paragraphs are allowed in this container.\n"
 "\n"
@@ -772,13 +776,15 @@
 "REMARQUE : supprimer des types de la liste des types de blocs autorisés sera "
 "sans effet sur les contenus existants."
 
-#: src/pyams_content/component/paragraph/zmi/__init__.py:208
+#: src/pyams_content/component/paragraph/zmi/__init__.py:209
+#: src/pyams_content/component/paragraph/zmi/__init__.py:222
 #: src/pyams_content/shared/common/zmi/templates/preview-input.pt:39
 #: src/pyams_content/features/preview/zmi/__init__.py:45
 msgid "Preview"
 msgstr "Aperçu"
 
-#: src/pyams_content/component/paragraph/zmi/__init__.py:213
+#: src/pyams_content/component/paragraph/zmi/__init__.py:214
+#: src/pyams_content/component/paragraph/zmi/__init__.py:227
 #: src/pyams_content/shared/common/zmi/workflow.py:123
 #: src/pyams_content/shared/common/zmi/workflow.py:210
 #: src/pyams_content/shared/common/zmi/workflow.py:250
@@ -797,15 +803,16 @@
 msgid "Cancel"
 msgstr "Annuler"
 
-#: src/pyams_content/component/paragraph/zmi/__init__.py:215
+#: src/pyams_content/component/paragraph/zmi/__init__.py:216
+#: src/pyams_content/component/paragraph/zmi/__init__.py:229
 msgid "Submit"
 msgstr "Enregistrer"
 
-#: src/pyams_content/component/paragraph/zmi/__init__.py:196
+#: src/pyams_content/component/paragraph/zmi/__init__.py:197
 msgid "Paragraph was correctly added."
 msgstr "Le bloc a été ajouté."
 
-#: src/pyams_content/component/paragraph/zmi/__init__.py:255
+#: src/pyams_content/component/paragraph/zmi/__init__.py:269
 msgid ""
 "You changed renderer selection. Don't omit to update new renderer "
 "properties..."
@@ -813,142 +820,142 @@
 "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
+#: src/pyams_content/component/paragraph/zmi/map.py:50
 msgid "Location map..."
 msgstr "Carte de situation"
 
-#: src/pyams_content/component/paragraph/zmi/map.py:68
+#: src/pyams_content/component/paragraph/zmi/map.py:63
 msgid "Add new location map"
 msgstr "Ajout d'une carte de situation"
 
-#: src/pyams_content/component/paragraph/zmi/map.py:91
+#: src/pyams_content/component/paragraph/zmi/map.py:86
 msgid "Edit location map properties"
 msgstr "Propriétés de la carte"
 
-#: src/pyams_content/component/paragraph/zmi/video.py:54
+#: src/pyams_content/component/paragraph/zmi/video.py:50
 msgid "Video paragraph..."
 msgstr "Vidéo"
 
-#: src/pyams_content/component/paragraph/zmi/video.py:67
+#: src/pyams_content/component/paragraph/zmi/video.py:63
 msgid "Add new video paragraph"
 msgstr "Ajout d'une vidéo"
 
-#: src/pyams_content/component/paragraph/zmi/video.py:90
-#: src/pyams_content/component/video/zmi/paragraph.py:182
+#: src/pyams_content/component/paragraph/zmi/video.py:86
+#: src/pyams_content/component/video/zmi/paragraph.py:183
 msgid "Edit video properties"
 msgstr "Propriétés de la vidéo"
 
-#: src/pyams_content/component/paragraph/zmi/container.py:74
+#: src/pyams_content/component/paragraph/zmi/container.py:70
 msgid "Contents..."
 msgstr "Blocs de contenu"
 
-#: src/pyams_content/component/paragraph/zmi/container.py:215
+#: src/pyams_content/component/paragraph/zmi/container.py:211
 msgid "Set navigation anchor"
 msgstr "Ancre de navigation"
 
-#: src/pyams_content/component/paragraph/zmi/container.py:286
+#: src/pyams_content/component/paragraph/zmi/container.py:285
 msgid "Show/hide all paragraphs"
 msgstr "Afficher/masquer tous les blocs"
 
-#: 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
+#: src/pyams_content/component/paragraph/zmi/container.py:333
+#: src/pyams_content/component/paragraph/zmi/container.py:342
+#: src/pyams_content/component/paragraph/zmi/container.py:355
 msgid "Content blocks"
 msgstr "Blocs de contenu"
 
-#: src/pyams_content/component/paragraph/zmi/container.py:413
+#: src/pyams_content/component/paragraph/zmi/container.py:412
 msgid "Links and attachments..."
 msgstr "Récap. liens et PJ"
 
-#: src/pyams_content/component/paragraph/zmi/container.py:425
+#: src/pyams_content/component/paragraph/zmi/container.py:424
 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:145
+#: src/pyams_content/component/paragraph/zmi/container.py:141
 msgid "No currently defined paragraph."
 msgstr "Aucun bloc n'est associé à ce contenu."
 
-#: src/pyams_content/component/paragraph/zmi/container.py:295
+#: src/pyams_content/component/paragraph/zmi/container.py:294
 msgid "Click to open/close all paragraphs editors"
 msgstr "Afficher/masquer tous les blocs"
 
-#: src/pyams_content/component/paragraph/zmi/container.py:308
+#: src/pyams_content/component/paragraph/zmi/container.py:307
 msgid "Click to open/close paragraph editor"
 msgstr "Afficher/masquer ce bloc"
 
-#: src/pyams_content/component/paragraph/zmi/container.py:151
+#: src/pyams_content/component/paragraph/zmi/container.py:147
 msgid "Check allowed paragraph types to be able to create new paragraphs."
 msgstr ""
 "Vérifiez le paramétrage des types de blocs de contenu autorisés pour pouvoir "
 "ajouter de nouveaux blocs."
 
-#: src/pyams_content/component/paragraph/zmi/pictogram.py:82
+#: src/pyams_content/component/paragraph/zmi/pictogram.py:79
 msgid "Pictograms..."
 msgstr "Pictogrammes"
 
-#: src/pyams_content/component/paragraph/zmi/pictogram.py:95
+#: src/pyams_content/component/paragraph/zmi/pictogram.py:92
 msgid "Add new pictogram paragraph"
 msgstr "Ajout de pictogrammes"
 
-#: src/pyams_content/component/paragraph/zmi/pictogram.py:122
+#: src/pyams_content/component/paragraph/zmi/pictogram.py:119
 msgid "Edit pictogram paragraph properties"
 msgstr "Propriétés des pictogrammes"
 
 #. Default: Header
-#: src/pyams_content/component/paragraph/zmi/pictogram.py:259
+#: src/pyams_content/component/paragraph/zmi/pictogram.py:256
 msgid "pictogram-item-header"
 msgstr "En-tête"
 
-#: src/pyams_content/component/paragraph/zmi/pictogram.py:298
+#: src/pyams_content/component/paragraph/zmi/pictogram.py:295
 #: src/pyams_content/component/paragraph/interfaces/pictogram.py:80
 msgid "Pictograms"
 msgstr "Pictogrammes"
 
-#: src/pyams_content/component/paragraph/zmi/pictogram.py:313
+#: src/pyams_content/component/paragraph/zmi/pictogram.py:310
 #: src/pyams_content/reference/pictograms/zmi/__init__.py:59
 msgid "Add pictogram"
 msgstr "Ajouter un pictogramme"
 
-#: src/pyams_content/component/paragraph/zmi/pictogram.py:326
+#: src/pyams_content/component/paragraph/zmi/pictogram.py:323
 #: 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:368
+#: src/pyams_content/component/paragraph/zmi/pictogram.py:365
 #: 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:348
+#: src/pyams_content/component/paragraph/zmi/pictogram.py:345
 msgid "Pictogram was correctly added"
 msgstr "Le pictogramme a été ajouté."
 
-#: src/pyams_content/component/paragraph/zmi/pictogram.py:358
-#: src/pyams_content/component/paragraph/zmi/pictogram.py:396
+#: src/pyams_content/component/paragraph/zmi/pictogram.py:355
+#: src/pyams_content/component/paragraph/zmi/pictogram.py:393
 msgid "You must select a pictogram!"
 msgstr "Vous devez sélectionner un pictogramme !"
 
-#: src/pyams_content/component/paragraph/zmi/audio.py:54
+#: src/pyams_content/component/paragraph/zmi/audio.py:50
 msgid "Audio paragraph..."
 msgstr "Bande son"
 
-#: src/pyams_content/component/paragraph/zmi/audio.py:67
+#: src/pyams_content/component/paragraph/zmi/audio.py:63
 msgid "Add new audio paragraph"
 msgstr "Ajout d'une bande son"
 
-#: src/pyams_content/component/paragraph/zmi/audio.py:89
+#: src/pyams_content/component/paragraph/zmi/audio.py:85
 msgid "Edit audio properties"
 msgstr "Propriétés de la bande son"
 
-#: src/pyams_content/component/paragraph/zmi/frame.py:86
+#: src/pyams_content/component/paragraph/zmi/frame.py:98
 msgid "Framed text..."
 msgstr "Encadré"
 
-#: src/pyams_content/component/paragraph/zmi/frame.py:100
+#: src/pyams_content/component/paragraph/zmi/frame.py:112
 msgid "Add new framed text paragraph"
 msgstr "Ajout d'un encadré"
 
-#: src/pyams_content/component/paragraph/zmi/frame.py:126
+#: src/pyams_content/component/paragraph/zmi/frame.py:138
 msgid "Edit framed text paragraph properties"
 msgstr "Propriétés de l'encadré"
 
@@ -960,47 +967,47 @@
 msgid "Add new verbatim paragraph"
 msgstr "Ajout d'un verbatim"
 
-#: src/pyams_content/component/paragraph/zmi/verbatim.py:92
+#: src/pyams_content/component/paragraph/zmi/verbatim.py:104
 msgid "Edit verbatim paragraph properties"
 msgstr "Propriétés du verbatim"
 
-#: src/pyams_content/component/paragraph/zmi/html.py:76
+#: src/pyams_content/component/paragraph/zmi/html.py:74
 msgid "Raw HTML..."
 msgstr "Code HTML"
 
-#: src/pyams_content/component/paragraph/zmi/html.py:89
+#: src/pyams_content/component/paragraph/zmi/html.py:87
 msgid "Add new raw HTML paragraph"
 msgstr "Ajout d'un bloc de code HTML"
 
-#: src/pyams_content/component/paragraph/zmi/html.py:119
+#: src/pyams_content/component/paragraph/zmi/html.py:117
 msgid "Edit raw HTML paragraph properties"
 msgstr "Propriétés du code HTML"
 
-#: src/pyams_content/component/paragraph/zmi/html.py:159
+#: src/pyams_content/component/paragraph/zmi/html.py:157
 msgid "Rich text..."
 msgstr "Texte enrichi"
 
-#: src/pyams_content/component/paragraph/zmi/html.py:172
+#: src/pyams_content/component/paragraph/zmi/html.py:170
 msgid "Add new rich text paragraph"
 msgstr "Ajout d'un bloc de texte enrichi"
 
-#: src/pyams_content/component/paragraph/zmi/html.py:197
+#: src/pyams_content/component/paragraph/zmi/html.py:207
 msgid "Edit rich text paragraph properties"
 msgstr "Propriétés du texte enrichi"
 
-#: src/pyams_content/component/paragraph/zmi/contact.py:53
+#: src/pyams_content/component/paragraph/zmi/contact.py:48
 msgid "Contact card..."
 msgstr "Fiche contact"
 
-#: src/pyams_content/component/paragraph/zmi/contact.py:66
+#: src/pyams_content/component/paragraph/zmi/contact.py:61
 msgid "Add new contact card"
 msgstr "Ajout d'une fiche contact"
 
-#: src/pyams_content/component/paragraph/zmi/contact.py:89
+#: src/pyams_content/component/paragraph/zmi/contact.py:84
 msgid "Edit contact card properties"
 msgstr "Propriétés de la fiche contact"
 
-#: src/pyams_content/component/paragraph/zmi/header.py:49
+#: src/pyams_content/component/paragraph/zmi/header.py:44
 msgid "Edit header paragraph properties"
 msgstr "Propriétés du chapô"
 
@@ -1993,24 +2000,24 @@
 msgid "Youtube settings"
 msgstr "Paramétres Youtube"
 
-#: src/pyams_content/component/video/zmi/paragraph.py:55
+#: src/pyams_content/component/video/zmi/paragraph.py:56
 msgid "External video..."
 msgstr "Vidéo externe"
 
-#: src/pyams_content/component/video/zmi/paragraph.py:68
+#: src/pyams_content/component/video/zmi/paragraph.py:69
 msgid "Add new external video..."
 msgstr "Ajout d'une vidéo externe"
 
-#: src/pyams_content/component/video/zmi/paragraph.py:117
+#: src/pyams_content/component/video/zmi/paragraph.py:118
 msgid "Video provider is required"
 msgstr "Le nom du fournisseur est obligatoire"
 
-#: src/pyams_content/component/video/zmi/paragraph.py:165
-#: src/pyams_content/component/video/zmi/paragraph.py:216
+#: src/pyams_content/component/video/zmi/paragraph.py:166
+#: src/pyams_content/component/video/zmi/paragraph.py:217
 msgid "Video provider settings"
 msgstr "Paramètres liés au fournisseur"
 
-#: src/pyams_content/component/video/zmi/paragraph.py:145
+#: src/pyams_content/component/video/zmi/paragraph.py:146
 msgid "Other settings"
 msgstr "Autres paramètres"
 
@@ -4191,16 +4198,16 @@
 msgid "no area defined"
 msgstr "aucune zone définie"
 
-#: src/pyams_content/shared/imagemap/zmi/paragraph.py:55
+#: src/pyams_content/shared/imagemap/zmi/paragraph.py:50
 msgid "Image map..."
 msgstr "Image cliquable"
 
-#: src/pyams_content/shared/imagemap/zmi/paragraph.py:68
+#: src/pyams_content/shared/imagemap/zmi/paragraph.py:63
 msgid "Add new image map"
 msgstr "Ajout d'une image cliquable"
 
-#: src/pyams_content/shared/imagemap/zmi/paragraph.py:95
-#: src/pyams_content/shared/logo/zmi/paragraph.py:94
+#: src/pyams_content/shared/imagemap/zmi/paragraph.py:90
+#: src/pyams_content/shared/logo/zmi/paragraph.py:89
 msgid "Edit paragraph properties"
 msgstr "Propriétés de l'image cliquable"
 
@@ -4599,11 +4606,11 @@
 msgid "no URL defined"
 msgstr "aucune URL définie"
 
-#: src/pyams_content/shared/logo/zmi/paragraph.py:55
+#: src/pyams_content/shared/logo/zmi/paragraph.py:50
 msgid "Logos..."
 msgstr "Logos"
 
-#: src/pyams_content/shared/logo/zmi/paragraph.py:68
+#: src/pyams_content/shared/logo/zmi/paragraph.py:63
 msgid "Add new logos paragraph"
 msgstr "Ajout d'une sélection de logos"
 
@@ -6153,9 +6160,6 @@
 #~ msgid "Paragraphs associations"
 #~ msgstr "Associations par paragraphe"
 
-#~ msgid "Add illustration"
-#~ msgstr "Ajouter une illustration"
-
 #~ msgid "Given media name doesn't exist!"
 #~ msgstr "Le nom du média indiqué n'existe pas !"
 
--- a/src/pyams_content/locales/pyams_content.pot	Tue Oct 09 15:05:38 2018 +0200
+++ b/src/pyams_content/locales/pyams_content.pot	Tue Oct 09 15:06:10 2018 +0200
@@ -6,7 +6,7 @@
 msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE 1.0\n"
-"POT-Creation-Date: 2018-10-08 09:36+0200\n"
+"POT-Creation-Date: 2018-10-09 13:56+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"
@@ -81,26 +81,13 @@
 msgid "Guest user (role)"
 msgstr ""
 
-#: ./src/pyams_content/component/gallery/paragraph.py:45
-msgid "(gallery contains 1 media)"
-msgstr ""
-
-#: ./src/pyams_content/component/gallery/paragraph.py:47
-msgid "(empty gallery)"
-msgstr ""
-
-#: ./src/pyams_content/component/gallery/paragraph.py:43
-#, python-format
-msgid "(gallery contains {0} medias)"
-msgstr ""
-
 #: ./src/pyams_content/component/gallery/__init__.py:155
 msgid "Gallery"
 msgstr ""
 
 #: ./src/pyams_content/component/gallery/zmi/file.py:57
 #: ./src/pyams_content/component/gallery/zmi/file.py:69
-#: ./src/pyams_content/component/gallery/zmi/paragraph.py:148
+#: ./src/pyams_content/component/gallery/zmi/paragraph.py:163
 msgid "Add media(s)"
 msgstr ""
 
@@ -120,22 +107,35 @@
 msgid "Audio content"
 msgstr ""
 
-#: ./src/pyams_content/component/gallery/zmi/paragraph.py:60
+#: ./src/pyams_content/component/gallery/zmi/paragraph.py:61
 msgid "Medias gallery..."
 msgstr ""
 
-#: ./src/pyams_content/component/gallery/zmi/paragraph.py:73
+#: ./src/pyams_content/component/gallery/zmi/paragraph.py:74
 msgid "Add new gallery"
 msgstr ""
 
+#: ./src/pyams_content/component/gallery/zmi/paragraph.py:110
+msgid "Edit gallery properties"
+msgstr ""
+
 #: ./src/pyams_content/component/gallery/zmi/paragraph.py:95
-msgid "Edit gallery properties"
-msgstr ""
-
-#: ./src/pyams_content/component/gallery/zmi/paragraph.py:169
+msgid "(gallery contains 1 media)"
+msgstr ""
+
+#: ./src/pyams_content/component/gallery/zmi/paragraph.py:97
+msgid "(empty gallery)"
+msgstr ""
+
+#: ./src/pyams_content/component/gallery/zmi/paragraph.py:184
 msgid "Media(s) successfully added"
 msgstr ""
 
+#: ./src/pyams_content/component/gallery/zmi/paragraph.py:93
+#, python-format
+msgid "(gallery contains {0} medias)"
+msgstr ""
+
 #: ./src/pyams_content/component/gallery/zmi/__init__.py:57
 msgid "Update gallery properties"
 msgstr ""
@@ -272,8 +272,8 @@
 
 #: ./src/pyams_content/component/gallery/interfaces/__init__.py:106
 #: ./src/pyams_content/component/keynumber/portlet/interfaces/__init__.py:31
-#: ./src/pyams_content/component/paragraph/zmi/milestone.py:238
-#: ./src/pyams_content/component/paragraph/zmi/container.py:270
+#: ./src/pyams_content/component/paragraph/zmi/milestone.py:235
+#: ./src/pyams_content/component/paragraph/zmi/container.py:266
 #: ./src/pyams_content/component/paragraph/interfaces/milestone.py:43
 #: ./src/pyams_content/shared/common/zmi/reverse.py:73
 #: ./src/pyams_content/shared/common/zmi/dashboard.py:103
@@ -459,15 +459,15 @@
 msgid "Key numbers"
 msgstr ""
 
-#: ./src/pyams_content/component/keynumber/zmi/paragraph.py:53
+#: ./src/pyams_content/component/keynumber/zmi/paragraph.py:49
 msgid "Key numbers..."
 msgstr ""
 
-#: ./src/pyams_content/component/keynumber/zmi/paragraph.py:66
+#: ./src/pyams_content/component/keynumber/zmi/paragraph.py:62
 msgid "Add new key number paragraph"
 msgstr ""
 
-#: ./src/pyams_content/component/keynumber/zmi/paragraph.py:94
+#: ./src/pyams_content/component/keynumber/zmi/paragraph.py:90
 msgid "Edit key number paragraph properties"
 msgstr ""
 
@@ -491,7 +491,7 @@
 #: ./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:62
-#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:276
+#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:273
 #: ./src/pyams_content/component/paragraph/interfaces/milestone.py:81
 #: ./src/pyams_content/component/paragraph/interfaces/video.py:50
 #: ./src/pyams_content/component/paragraph/interfaces/pictogram.py:58
@@ -586,8 +586,8 @@
 #: ./src/pyams_content/component/illustration/__init__.py:171
 #: ./src/pyams_content/component/illustration/thesaurus.py:32
 #: ./src/pyams_content/component/illustration/zmi/paragraph.py:153
-#: ./src/pyams_content/component/illustration/zmi/__init__.py:51
-#: ./src/pyams_content/component/illustration/zmi/__init__.py:112
+#: ./src/pyams_content/component/illustration/zmi/__init__.py:57
+#: ./src/pyams_content/component/illustration/zmi/__init__.py:118
 #: ./src/pyams_content/component/illustration/interfaces/__init__.py:97
 msgid "Illustration"
 msgstr ""
@@ -605,11 +605,15 @@
 msgid "Edit illustration properties"
 msgstr ""
 
-#: ./src/pyams_content/component/illustration/zmi/__init__.py:146
+#: ./src/pyams_content/component/illustration/zmi/__init__.py:152
 msgid "Navigation link illustration"
 msgstr ""
 
-#: ./src/pyams_content/component/illustration/zmi/__init__.py:114
+#: ./src/pyams_content/component/illustration/zmi/__init__.py:169
+msgid "Add illustration"
+msgstr ""
+
+#: ./src/pyams_content/component/illustration/zmi/__init__.py:120
 msgid "Header illustration"
 msgstr ""
 
@@ -650,7 +654,7 @@
 msgid "no visible paragraph"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/pictogram.py:135
+#: ./src/pyams_content/component/paragraph/pictogram.py:130
 msgid "Selected pictogram is missing"
 msgstr ""
 
@@ -658,74 +662,74 @@
 msgid "This paragraph type is deprecated and should be removed!"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/milestone.py:77
+#: ./src/pyams_content/component/paragraph/zmi/milestone.py:74
 msgid "Milestones..."
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/milestone.py:90
+#: ./src/pyams_content/component/paragraph/zmi/milestone.py:87
 msgid "Add new milestone paragraph"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/milestone.py:117
+#: ./src/pyams_content/component/paragraph/zmi/milestone.py:114
 msgid "Edit milestone paragraph properties"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/milestone.py:250
+#: ./src/pyams_content/component/paragraph/zmi/milestone.py:247
 #: ./src/pyams_content/component/paragraph/interfaces/milestone.py:47
 msgid "Associated label"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/milestone.py:262
+#: ./src/pyams_content/component/paragraph/zmi/milestone.py:259
 #: ./src/pyams_content/component/paragraph/interfaces/milestone.py:51
 msgid "Anchor"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/milestone.py:291
+#: ./src/pyams_content/component/paragraph/zmi/milestone.py:288
 #: ./src/pyams_content/component/paragraph/interfaces/milestone.py:74
 msgid "Milestones"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/milestone.py:306
+#: ./src/pyams_content/component/paragraph/zmi/milestone.py:303
 msgid "Add milestone"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/milestone.py:319
+#: ./src/pyams_content/component/paragraph/zmi/milestone.py:316
 msgid "Add new milestone"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/milestone.py:346
+#: ./src/pyams_content/component/paragraph/zmi/milestone.py:343
 msgid "Edit milestone properties"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/milestone.py:334
+#: ./src/pyams_content/component/paragraph/zmi/milestone.py:331
 msgid "Milestone was correctly added"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/milestone.py:277
+#: ./src/pyams_content/component/paragraph/zmi/milestone.py:274
 msgid "(missing paragraph)"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/keypoint.py:51
+#: ./src/pyams_content/component/paragraph/zmi/keypoint.py:46
 msgid "Key points..."
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/keypoint.py:64
+#: ./src/pyams_content/component/paragraph/zmi/keypoint.py:59
 msgid "Add new key points paragraph"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/keypoint.py:91
+#: ./src/pyams_content/component/paragraph/zmi/keypoint.py:86
 msgid "Edit key points paragraph properties"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/__init__.py:64
+#: ./src/pyams_content/component/paragraph/zmi/__init__.py:60
 msgid "Content block types..."
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/__init__.py:78
+#: ./src/pyams_content/component/paragraph/zmi/__init__.py:74
 msgid "Content block types"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/__init__.py:88
+#: ./src/pyams_content/component/paragraph/zmi/__init__.py:84
 msgid ""
 "You can define which types of paragraphs are allowed in this container.\n"
 "\n"
@@ -734,13 +738,15 @@
 "NOTICE: removing types from allowed types list will have no effect on already created contents!"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/__init__.py:208
+#: ./src/pyams_content/component/paragraph/zmi/__init__.py:209
+#: ./src/pyams_content/component/paragraph/zmi/__init__.py:222
 #: ./src/pyams_content/shared/common/zmi/templates/preview-input.pt:39
 #: ./src/pyams_content/features/preview/zmi/__init__.py:45
 msgid "Preview"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/__init__.py:213
+#: ./src/pyams_content/component/paragraph/zmi/__init__.py:214
+#: ./src/pyams_content/component/paragraph/zmi/__init__.py:227
 #: ./src/pyams_content/shared/common/zmi/workflow.py:123
 #: ./src/pyams_content/shared/common/zmi/workflow.py:210
 #: ./src/pyams_content/shared/common/zmi/workflow.py:250
@@ -759,154 +765,155 @@
 msgid "Cancel"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/__init__.py:215
+#: ./src/pyams_content/component/paragraph/zmi/__init__.py:216
+#: ./src/pyams_content/component/paragraph/zmi/__init__.py:229
 msgid "Submit"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/__init__.py:196
+#: ./src/pyams_content/component/paragraph/zmi/__init__.py:197
 msgid "Paragraph was correctly added."
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/__init__.py:255
+#: ./src/pyams_content/component/paragraph/zmi/__init__.py:269
 msgid ""
 "You changed renderer selection. Don't omit to update new renderer "
 "properties..."
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/map.py:55
+#: ./src/pyams_content/component/paragraph/zmi/map.py:50
 msgid "Location map..."
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/map.py:68
+#: ./src/pyams_content/component/paragraph/zmi/map.py:63
 msgid "Add new location map"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/map.py:91
+#: ./src/pyams_content/component/paragraph/zmi/map.py:86
 msgid "Edit location map properties"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/video.py:54
+#: ./src/pyams_content/component/paragraph/zmi/video.py:50
 msgid "Video paragraph..."
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/video.py:67
+#: ./src/pyams_content/component/paragraph/zmi/video.py:63
 msgid "Add new video paragraph"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/video.py:90
-#: ./src/pyams_content/component/video/zmi/paragraph.py:182
+#: ./src/pyams_content/component/paragraph/zmi/video.py:86
+#: ./src/pyams_content/component/video/zmi/paragraph.py:183
 msgid "Edit video properties"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/container.py:74
+#: ./src/pyams_content/component/paragraph/zmi/container.py:70
 msgid "Contents..."
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/container.py:215
+#: ./src/pyams_content/component/paragraph/zmi/container.py:211
 msgid "Set navigation anchor"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/container.py:286
+#: ./src/pyams_content/component/paragraph/zmi/container.py:285
 msgid "Show/hide all paragraphs"
 msgstr ""
 
-#: ./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
+#: ./src/pyams_content/component/paragraph/zmi/container.py:333
+#: ./src/pyams_content/component/paragraph/zmi/container.py:342
+#: ./src/pyams_content/component/paragraph/zmi/container.py:355
 msgid "Content blocks"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/container.py:413
+#: ./src/pyams_content/component/paragraph/zmi/container.py:412
 msgid "Links and attachments..."
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/container.py:425
+#: ./src/pyams_content/component/paragraph/zmi/container.py:424
 msgid "Content blocks links and attachments"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/container.py:145
+#: ./src/pyams_content/component/paragraph/zmi/container.py:141
 msgid "No currently defined paragraph."
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/container.py:295
+#: ./src/pyams_content/component/paragraph/zmi/container.py:294
 msgid "Click to open/close all paragraphs editors"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/container.py:308
+#: ./src/pyams_content/component/paragraph/zmi/container.py:307
 msgid "Click to open/close paragraph editor"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/container.py:151
+#: ./src/pyams_content/component/paragraph/zmi/container.py:147
 msgid "Check allowed paragraph types to be able to create new paragraphs."
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:82
+#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:79
 msgid "Pictograms..."
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:95
+#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:92
 msgid "Add new pictogram paragraph"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:122
+#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:119
 msgid "Edit pictogram paragraph properties"
 msgstr ""
 
 #. Default: Header
-#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:259
+#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:256
 msgid "pictogram-item-header"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:298
+#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:295
 #: ./src/pyams_content/component/paragraph/interfaces/pictogram.py:80
 msgid "Pictograms"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:313
+#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:310
 #: ./src/pyams_content/reference/pictograms/zmi/__init__.py:59
 msgid "Add pictogram"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:326
+#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:323
 #: ./src/pyams_content/reference/pictograms/zmi/__init__.py:71
 msgid "Add new pictogram"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:368
+#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:365
 #: ./src/pyams_content/reference/pictograms/zmi/__init__.py:95
 msgid "Edit pictogram properties"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:348
+#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:345
 msgid "Pictogram was correctly added"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:358
-#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:396
+#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:355
+#: ./src/pyams_content/component/paragraph/zmi/pictogram.py:393
 msgid "You must select a pictogram!"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/audio.py:54
+#: ./src/pyams_content/component/paragraph/zmi/audio.py:50
 msgid "Audio paragraph..."
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/audio.py:67
+#: ./src/pyams_content/component/paragraph/zmi/audio.py:63
 msgid "Add new audio paragraph"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/audio.py:89
+#: ./src/pyams_content/component/paragraph/zmi/audio.py:85
 msgid "Edit audio properties"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/frame.py:86
+#: ./src/pyams_content/component/paragraph/zmi/frame.py:98
 msgid "Framed text..."
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/frame.py:100
+#: ./src/pyams_content/component/paragraph/zmi/frame.py:112
 msgid "Add new framed text paragraph"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/frame.py:126
+#: ./src/pyams_content/component/paragraph/zmi/frame.py:138
 msgid "Edit framed text paragraph properties"
 msgstr ""
 
@@ -918,47 +925,47 @@
 msgid "Add new verbatim paragraph"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/verbatim.py:92
+#: ./src/pyams_content/component/paragraph/zmi/verbatim.py:104
 msgid "Edit verbatim paragraph properties"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/html.py:76
+#: ./src/pyams_content/component/paragraph/zmi/html.py:74
 msgid "Raw HTML..."
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/html.py:89
+#: ./src/pyams_content/component/paragraph/zmi/html.py:87
 msgid "Add new raw HTML paragraph"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/html.py:119
+#: ./src/pyams_content/component/paragraph/zmi/html.py:117
 msgid "Edit raw HTML paragraph properties"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/html.py:159
+#: ./src/pyams_content/component/paragraph/zmi/html.py:157
 msgid "Rich text..."
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/html.py:172
+#: ./src/pyams_content/component/paragraph/zmi/html.py:170
 msgid "Add new rich text paragraph"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/html.py:197
+#: ./src/pyams_content/component/paragraph/zmi/html.py:207
 msgid "Edit rich text paragraph properties"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/contact.py:53
+#: ./src/pyams_content/component/paragraph/zmi/contact.py:48
 msgid "Contact card..."
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/contact.py:66
+#: ./src/pyams_content/component/paragraph/zmi/contact.py:61
 msgid "Add new contact card"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/contact.py:89
+#: ./src/pyams_content/component/paragraph/zmi/contact.py:84
 msgid "Edit contact card properties"
 msgstr ""
 
-#: ./src/pyams_content/component/paragraph/zmi/header.py:49
+#: ./src/pyams_content/component/paragraph/zmi/header.py:44
 msgid "Edit header paragraph properties"
 msgstr ""
 
@@ -1886,24 +1893,24 @@
 msgid "Youtube settings"
 msgstr ""
 
-#: ./src/pyams_content/component/video/zmi/paragraph.py:55
+#: ./src/pyams_content/component/video/zmi/paragraph.py:56
 msgid "External video..."
 msgstr ""
 
-#: ./src/pyams_content/component/video/zmi/paragraph.py:68
+#: ./src/pyams_content/component/video/zmi/paragraph.py:69
 msgid "Add new external video..."
 msgstr ""
 
-#: ./src/pyams_content/component/video/zmi/paragraph.py:117
+#: ./src/pyams_content/component/video/zmi/paragraph.py:118
 msgid "Video provider is required"
 msgstr ""
 
-#: ./src/pyams_content/component/video/zmi/paragraph.py:165
-#: ./src/pyams_content/component/video/zmi/paragraph.py:216
+#: ./src/pyams_content/component/video/zmi/paragraph.py:166
+#: ./src/pyams_content/component/video/zmi/paragraph.py:217
 msgid "Video provider settings"
 msgstr ""
 
-#: ./src/pyams_content/component/video/zmi/paragraph.py:145
+#: ./src/pyams_content/component/video/zmi/paragraph.py:146
 msgid "Other settings"
 msgstr ""
 
@@ -3895,16 +3902,16 @@
 msgid "no area defined"
 msgstr ""
 
-#: ./src/pyams_content/shared/imagemap/zmi/paragraph.py:55
+#: ./src/pyams_content/shared/imagemap/zmi/paragraph.py:50
 msgid "Image map..."
 msgstr ""
 
-#: ./src/pyams_content/shared/imagemap/zmi/paragraph.py:68
+#: ./src/pyams_content/shared/imagemap/zmi/paragraph.py:63
 msgid "Add new image map"
 msgstr ""
 
-#: ./src/pyams_content/shared/imagemap/zmi/paragraph.py:95
-#: ./src/pyams_content/shared/logo/zmi/paragraph.py:94
+#: ./src/pyams_content/shared/imagemap/zmi/paragraph.py:90
+#: ./src/pyams_content/shared/logo/zmi/paragraph.py:89
 msgid "Edit paragraph properties"
 msgstr ""
 
@@ -4279,11 +4286,11 @@
 msgid "no URL defined"
 msgstr ""
 
-#: ./src/pyams_content/shared/logo/zmi/paragraph.py:55
+#: ./src/pyams_content/shared/logo/zmi/paragraph.py:50
 msgid "Logos..."
 msgstr ""
 
-#: ./src/pyams_content/shared/logo/zmi/paragraph.py:68
+#: ./src/pyams_content/shared/logo/zmi/paragraph.py:63
 msgid "Add new logos paragraph"
 msgstr ""
 
--- a/src/pyams_content/shared/imagemap/zmi/paragraph.py	Tue Oct 09 15:05:38 2018 +0200
+++ b/src/pyams_content/shared/imagemap/zmi/paragraph.py	Tue Oct 09 15:06:10 2018 +0200
@@ -95,6 +95,13 @@
 
     edit_permission = MANAGE_CONTENT_PERMISSION
 
+    def get_ajax_output(self, changes):
+        output = super(self.__class__, self).get_ajax_output(changes)
+        if 'reference' in changes.get(IImageMapParagraph, ()):
+            output.setdefault('events', []).append(
+                get_json_form_refresh_event(self.context, self.request, ImagemapParagraphInnerEditForm))
+        return output
+
 
 @adapter_config(context=(IImageMapParagraph, IPyAMSLayer), provides=IParagraphInnerEditor)
 @ajax_config(name='inner-properties.json', context=IImageMapParagraph, layer=IPyAMSLayer,
@@ -112,13 +119,6 @@
         else:
             return button.Buttons()
 
-    def get_ajax_output(self, changes):
-        output = super(self.__class__, self).get_ajax_output(changes)
-        if 'reference' in changes.get(IImageMapParagraph, ()):
-            output.setdefault('events', []).append(
-                get_json_form_refresh_event(self.context, self.request, ImagemapParagraphInnerEditForm))
-        return output
-
 
 @viewlet_config(name='imagemap-preview', context=IImageMapParagraph, layer=IPyAMSLayer,
                 view=ImagemapParagraphInnerEditForm, manager=IWidgetsSuffixViewletsManager, weight=1)
--- a/src/pyams_content/shared/logo/zmi/paragraph.py	Tue Oct 09 15:05:38 2018 +0200
+++ b/src/pyams_content/shared/logo/zmi/paragraph.py	Tue Oct 09 15:06:10 2018 +0200
@@ -94,6 +94,14 @@
 
     edit_permission = MANAGE_CONTENT_PERMISSION
 
+    def get_ajax_output(self, changes):
+        output = super(self.__class__, self).get_ajax_output(changes)
+        if 'renderer' in changes.get(ILogosParagraph, ()):
+            output.setdefault('events', []).append(
+                get_json_widget_refresh_event(self.context, self.request,
+                                              LogosParagraphInnerEditForm, 'renderer'))
+        return output
+
 
 @adapter_config(context=(ILogosParagraph, IPyAMSLayer), provides=IParagraphInnerEditor)
 @ajax_config(name='inner-properties.json', context=ILogosParagraph, layer=IPyAMSLayer,
@@ -110,18 +118,3 @@
             return button.Buttons(IParagraphInnerEditFormButtons)
         else:
             return button.Buttons()
-
-
-@view_config(name='inner-properties.json', context=ILogosParagraph, request_type=IPyAMSLayer,
-             permission=MANAGE_CONTENT_PERMISSION, renderer='json', xhr=True)
-class LogosParagraphInnerAJAXEditForm(BaseParagraphAJAXEditForm, LogosParagraphInnerEditForm):
-    """Logos paragraph properteis inner edit form, JSON renderer"""
-
-    def get_ajax_output(self, changes):
-        output = super(LogosParagraphInnerAJAXEditForm, self).get_ajax_output(changes)
-        updated = changes.get(ILogosParagraph, ())
-        if 'renderer' in updated:
-            output.setdefault('events', []).append(
-                get_json_widget_refresh_event(self.context, self.request,
-                                              LogosParagraphInnerEditForm, 'renderer'))
-        return output