merge default doc-dc
authorDamien Correia
Fri, 25 May 2018 11:28:17 +0200
branchdoc-dc
changeset 652 b438528e5bb3
parent 651 26a58877d1aa (current diff)
parent 557 a78ade534b97 (diff)
child 653 238b75a21396
merge default
src/pyams_content/component/paragraph/zmi/templates/html-render.pt
src/pyams_content/component/paragraph/zmi/templates/raw-render.pt
src/pyams_content/component/video/zmi/templates/video-render.pt
--- a/src/pyams_content/component/association/interfaces/__init__.py	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/component/association/interfaces/__init__.py	Fri May 25 11:28:17 2018 +0200
@@ -17,7 +17,6 @@
 
 # import interfaces
 from pyams_content.component.paragraph.interfaces import IBaseParagraph
-from pyams_content.features.renderer import IRenderedContent
 from zope.annotation.interfaces import IAttributeAnnotatable
 from zope.container.interfaces import IOrderedContainer
 
@@ -87,7 +86,7 @@
 ASSOCIATION_PARAGRAPH_RENDERERS = 'PyAMS.associations.renderers'
 
 
-class IAssociationParagraph(IRenderedContent, IBaseParagraph):
+class IAssociationParagraph(IBaseParagraph):
     """Associations paragraph interface"""
 
     renderer = Choice(title=_("Associations template"),
--- a/src/pyams_content/component/association/paragraph.py	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/component/association/paragraph.py	Fri May 25 11:28:17 2018 +0200
@@ -22,26 +22,26 @@
 from pyams_content.component.links.interfaces import ILinkContainerTarget
 from pyams_content.component.paragraph.interfaces import IParagraphFactory
 from pyams_content.features.checker.interfaces import IContentChecker, MISSING_VALUE, MISSING_LANG_VALUE
-from pyams_content.features.renderer.interfaces import IContentRenderer
 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 RenderedContentMixin
+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.request import check_request
 from pyams_utils.traversing import get_parent
 from pyams_utils.vocabulary import vocabulary_config
 from zope.interface import implementer
 from zope.schema.fieldproperty import FieldProperty
-from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm
 
 from pyams_content import _
 
 
 @implementer(IAssociationParagraph, IExtFileContainerTarget, ILinkContainerTarget)
-class AssociationParagraph(RenderedContentMixin, BaseParagraph):
+@factory_config(provided=IAssociationParagraph)
+class AssociationParagraph(BaseParagraph):
     """Associations paragraph"""
 
     icon_class = 'fa-link'
@@ -91,16 +91,7 @@
 
 
 @vocabulary_config(name=ASSOCIATION_PARAGRAPH_RENDERERS)
-class AssociationParagraphRendererVocabulary(SimpleVocabulary):
+class AssociationParagraphRendererVocabulary(RenderersVocabulary):
     """Associations paragraph renderers vocabulary"""
 
-    def __init__(self, context=None):
-        request = check_request()
-        translate = request.localizer.translate
-        registry = request.registry
-        if not IAssociationParagraph.providedBy(context):
-            context = AssociationParagraph()
-        terms = [SimpleTerm(name, title=translate(adapter.label))
-                 for name, adapter in sorted(registry.getAdapters((context, request), IContentRenderer),
-                                             key=lambda x: x[1].weight)]
-        super(AssociationParagraphRendererVocabulary, self).__init__(terms)
+    content_interface = IAssociationParagraph
--- a/src/pyams_content/component/association/zmi/paragraph.py	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/component/association/zmi/paragraph.py	Fri May 25 11:28:17 2018 +0200
@@ -18,8 +18,7 @@
 # import interfaces
 from pyams_content.component.association.interfaces import IAssociationParagraph, ASSOCIATION_PARAGRAPH_TYPE
 from pyams_content.component.association.zmi.interfaces import IAssociationsParentForm
-from pyams_content.component.paragraph.interfaces import IParagraphContainerTarget, IParagraphContainer, \
-    IParagraphRenderer
+from pyams_content.component.paragraph.interfaces import IParagraphContainerTarget, IParagraphContainer
 from pyams_content.component.paragraph.zmi.interfaces import IParagraphInnerEditor, IParagraphContainerView
 from pyams_content.interfaces import MANAGE_CONTENT_PERMISSION
 from pyams_content.shared.common.interfaces import IWfSharedContent
@@ -33,7 +32,6 @@
 from pyams_content.component.association.paragraph import AssociationParagraph
 from pyams_content.component.paragraph.zmi import BaseParagraphAJAXAddForm, BaseParagraphAJAXEditForm, \
     BaseParagraphAddMenu, BaseParagraphPropertiesEditForm, IParagraphEditFormButtons
-from pyams_content.features.renderer.zmi import BaseRenderedContentRenderer
 from pyams_content.features.renderer.zmi.widget import RendererFieldWidget
 from pyams_pagelet.pagelet import pagelet_config
 from pyams_skin.event import get_json_widget_refresh_event
@@ -141,12 +139,3 @@
                                                                                  AssociationParagraphInnerEditForm,
                                                                                  'renderer'))
         return output
-
-
-#
-# Association paragraph renderer
-#
-
-@adapter_config(context=(IAssociationParagraph, IPyAMSLayer), provides=IParagraphRenderer)
-class AssociationParagraphRenderer(BaseRenderedContentRenderer):
-    """Association paragraph renderer"""
--- a/src/pyams_content/component/gallery/__init__.py	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/component/gallery/__init__.py	Fri May 25 11:28:17 2018 +0200
@@ -23,7 +23,6 @@
 from pyams_content.shared.common.interfaces import IWfSharedContent
 from pyams_form.interfaces.form import IFormContextPermissionChecker
 from pyams_i18n.interfaces import II18n
-from zope.annotation.interfaces import IAnnotations
 from zope.lifecycleevent.interfaces import IObjectAddedEvent, IObjectModifiedEvent, IObjectRemovedEvent
 from zope.location.interfaces import ISublocations
 from zope.traversing.interfaces import ITraversable
@@ -31,10 +30,10 @@
 # import packages
 from pyams_catalog.utils import index_object
 from pyams_content.features.checker import BaseContentChecker
-from pyams_content.features.renderer import RenderedContentMixin, IContentRenderer
-from pyams_utils.adapter import adapter_config, ContextAdapter
+from pyams_content.features.renderer import RenderedContentMixin, RenderersVocabulary
+from pyams_utils.adapter import adapter_config, ContextAdapter, get_annotation_adapter
+from pyams_utils.factory import factory_config
 from pyams_utils.container import BTreeOrderedContainer
-from pyams_utils.request import check_request
 from pyams_utils.traversing import get_parent
 from pyams_utils.vocabulary import vocabulary_config
 from pyramid.events import subscriber
@@ -43,7 +42,6 @@
 from zope.lifecycleevent import ObjectCreatedEvent, ObjectModifiedEvent
 from zope.location import locate
 from zope.schema.fieldproperty import FieldProperty
-from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm
 
 from pyams_content import _
 
@@ -53,7 +51,8 @@
 #
 
 @implementer(IGallery)
-class Gallery(BTreeOrderedContainer, RenderedContentMixin):
+@factory_config(provided=IBaseGallery)
+class Gallery(RenderedContentMixin, BTreeOrderedContainer):
     """Gallery persistent class"""
 
     title = FieldProperty(IGallery['title'])
@@ -80,13 +79,7 @@
 @adapter_config(context=IGalleryTarget, provides=IGallery)
 def gallery_factory(target):
     """Galleries container factory"""
-    annotations = IAnnotations(target)
-    gallery = annotations.get(GALLERY_CONTAINER_KEY)
-    if gallery is None:
-        gallery = annotations[GALLERY_CONTAINER_KEY] = Gallery()
-        get_current_registry().notify(ObjectCreatedEvent(gallery))
-        locate(gallery, target, '++gallery++')
-    return gallery
+    return get_annotation_adapter(target, GALLERY_CONTAINER_KEY, Gallery, name='++gallery++')
 
 
 @adapter_config(name='gallery', context=IGalleryTarget, provides=ITraversable)
@@ -183,16 +176,7 @@
 
 
 @vocabulary_config(name=GALLERY_RENDERERS)
-class GalleryRendererVocabulary(SimpleVocabulary):
+class GalleryRendererVocabulary(RenderersVocabulary):
     """Gallery renderers vocabulary"""
 
-    def __init__(self, context=None):
-        request = check_request()
-        translate = request.localizer.translate
-        registry = request.registry
-        if not IBaseGallery.providedBy(context):
-            context = Gallery()
-        terms = [SimpleTerm(name, title=translate(adapter.label))
-                 for name, adapter in sorted(registry.getAdapters((context, request), IContentRenderer),
-                                             key=lambda x: x[1].weight)]
-        super(GalleryRendererVocabulary, self).__init__(terms)
+    content_interface = IBaseGallery
--- a/src/pyams_content/component/gallery/zmi/paragraph.py	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/component/gallery/zmi/paragraph.py	Fri May 25 11:28:17 2018 +0200
@@ -18,8 +18,7 @@
 # import interfaces
 from pyams_content.component.gallery.interfaces import IGalleryParagraph, IBaseGallery, GALLERY_PARAGRAPH_TYPE
 from pyams_content.component.gallery.zmi.interfaces import IGalleryContentsView
-from pyams_content.component.paragraph.interfaces import IParagraphContainerTarget, IParagraphContainer, \
-    IParagraphRenderer
+from pyams_content.component.paragraph.interfaces import IParagraphContainerTarget, IParagraphContainer
 from pyams_content.component.paragraph.zmi.interfaces import IParagraphInnerEditor, IParagraphContainerView
 from pyams_content.interfaces import MANAGE_CONTENT_PERMISSION
 from pyams_form.interfaces.form import IInnerForm, IInnerSubForm
@@ -33,7 +32,6 @@
 from pyams_content.component.gallery.paragraph import Gallery
 from pyams_content.component.paragraph.zmi import BaseParagraphAJAXAddForm, BaseParagraphAJAXEditForm, \
     BaseParagraphAddMenu, BaseParagraphPropertiesEditForm, get_json_paragraph_refresh_event, IParagraphEditFormButtons
-from pyams_content.features.renderer.zmi import BaseRenderedContentRenderer
 from pyams_content.features.renderer.zmi.widget import RendererFieldWidget
 from pyams_content.shared.common.zmi import WfSharedContentPermissionMixin
 from pyams_pagelet.pagelet import pagelet_config
@@ -178,12 +176,3 @@
     url = 'add-media.html'
     modal_target = True
     stop_propagation = True
-
-
-#
-# Gallery paragraph renderer
-#
-
-@adapter_config(context=(IGalleryParagraph, IPyAMSLayer), provides=IParagraphRenderer)
-class GalleryParagraphRenderer(BaseRenderedContentRenderer):
-    """Gallery paragraph renderer"""
--- a/src/pyams_content/component/illustration/__init__.py	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/component/illustration/__init__.py	Fri May 25 11:28:17 2018 +0200
@@ -29,9 +29,10 @@
 # import packages
 from persistent import Persistent
 from pyams_content.features.checker import BaseContentChecker
-from pyams_content.features.renderer import RenderedContentMixin, IContentRenderer
+from pyams_content.features.renderer import RenderedContentMixin, RenderersVocabulary
 from pyams_i18n.property import I18nFileProperty
 from pyams_utils.adapter import adapter_config, ContextAdapter
+from pyams_utils.factory import factory_config
 from pyams_utils.registry import query_utility, get_utility
 from pyams_utils.request import check_request
 from pyams_utils.traversing import get_parent
@@ -43,13 +44,13 @@
 from zope.lifecycleevent import ObjectCreatedEvent, ObjectAddedEvent
 from zope.location import locate
 from zope.schema.fieldproperty import FieldProperty
-from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm
 
 from pyams_content import _
 
 
 @implementer(IIllustration)
-class Illustration(Persistent, Contained, RenderedContentMixin):
+@factory_config(provided=IIllustration)
+class Illustration(RenderedContentMixin, Persistent, Contained):
     """Illustration persistent class"""
 
     _data = I18nFileProperty(IIllustration['data'])
@@ -178,16 +179,7 @@
 
 
 @vocabulary_config(name=ILLUSTRATION_RENDERERS)
-class IllustrationRendererVocabulary(SimpleVocabulary):
+class IllustrationRendererVocabulary(RenderersVocabulary):
     """Illustration renderers vocabulary"""
 
-    def __init__(self, context=None):
-        request = check_request()
-        translate = request.localizer.translate
-        registry = request.registry
-        if not IIllustration.providedBy(context):
-            context = Illustration()
-        terms = [SimpleTerm(name, title=translate(adapter.label))
-                 for name, adapter in sorted(registry.getAdapters((context, request), IContentRenderer),
-                                             key=lambda x: x[1].weight)]
-        super(IllustrationRendererVocabulary, self).__init__(terms)
+    content_interface = IIllustration
--- a/src/pyams_content/component/illustration/zmi/paragraph.py	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/component/illustration/zmi/paragraph.py	Fri May 25 11:28:17 2018 +0200
@@ -17,7 +17,7 @@
 
 # import interfaces
 from pyams_content.component.paragraph.interfaces import IParagraphContainerTarget, \
-    IParagraphContainer, IParagraphRenderer
+    IParagraphContainer
 from pyams_content.component.illustration.interfaces import IIllustration, IIllustrationParagraph, \
     ILLUSTRATION_PARAGRAPH_TYPE
 from pyams_content.component.paragraph.zmi.interfaces import IParagraphInnerEditor, IParagraphContainerView
@@ -32,7 +32,6 @@
 from pyams_content.component.illustration.paragraph import Illustration
 from pyams_content.component.paragraph.zmi import BaseParagraphAJAXAddForm, BaseParagraphAJAXEditForm, \
     BaseParagraphAddMenu, BaseParagraphPropertiesEditForm, get_json_paragraph_refresh_event, IParagraphEditFormButtons
-from pyams_content.features.renderer.zmi import BaseRenderedContentRenderer
 from pyams_content.features.renderer.zmi.widget import RendererFieldWidget
 from pyams_pagelet.pagelet import pagelet_config
 from pyams_skin.event import get_json_form_refresh_event
@@ -162,12 +161,3 @@
             output.setdefault('events', []).append(get_json_form_refresh_event(self.context, self.request,
                                                                                IllustrationInnerEditForm))
         return output
-
-
-#
-# Illustration renderer
-#
-
-@adapter_config(context=(IIllustrationParagraph, IPyAMSLayer), provides=IParagraphRenderer)
-class IllustrationRenderer(BaseRenderedContentRenderer):
-    """Illustration renderer"""
--- a/src/pyams_content/component/paragraph/__init__.py	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/component/paragraph/__init__.py	Fri May 25 11:28:17 2018 +0200
@@ -18,6 +18,7 @@
 # import interfaces
 from pyams_content.component.paragraph.interfaces import IBaseParagraph, IParagraphFactory, IParagraphContainerTarget, \
     IParagraphContainer, IParagraphFactorySettings
+from pyams_content.features.preview.interfaces import IPreviewTarget
 from pyams_content.shared.common.interfaces import IWfSharedContent
 from pyams_form.interfaces.form import IFormContextPermissionChecker
 from pyams_i18n.interfaces import II18n
@@ -27,6 +28,7 @@
 # import packages
 from persistent import Persistent
 from pyams_content.features.checker import BaseContentChecker
+from pyams_content.features.renderer import RenderedContentMixin
 from pyams_utils.adapter import adapter_config, ContextAdapter
 from pyams_utils.registry import query_utility
 from pyams_utils.request import check_request
@@ -72,8 +74,8 @@
 # Base paragraph classes and subscribers
 #
 
-@implementer(IBaseParagraph)
-class BaseParagraph(Persistent, Contained):
+@implementer(IBaseParagraph, IPreviewTarget)
+class BaseParagraph(RenderedContentMixin, Persistent, Contained):
     """Base paragraph persistent class"""
 
     icon_class = ''
--- a/src/pyams_content/component/paragraph/contact.py	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/component/paragraph/contact.py	Fri May 25 11:28:17 2018 +0200
@@ -25,22 +25,22 @@
 
 # import packages
 from pyams_content.component.paragraph import BaseParagraph, BaseParagraphFactory, BaseParagraphContentChecker
-from pyams_content.features.renderer import RenderedContentMixin, IContentRenderer
+from pyams_content.features.renderer import RenderersVocabulary
 from pyams_file.property import FileProperty
 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.request import check_request
 from pyams_utils.traversing import get_parent
 from pyams_utils.vocabulary import vocabulary_config
 from zope.interface import implementer, alsoProvides
 from zope.schema.fieldproperty import FieldProperty
-from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm
 
 from pyams_content import _
 
 
 @implementer(IContactParagraph)
-class ContactParagraph(RenderedContentMixin, BaseParagraph):
+@factory_config(provided=IContactParagraph)
+class ContactParagraph(BaseParagraph):
     """Contact paragraph"""
 
     icon_class = 'fa-id-card-o'
@@ -112,16 +112,7 @@
 
 
 @vocabulary_config(name=CONTACT_PARAGRAPH_RENDERERS)
-class ContactParagraphRendererVocabulary(SimpleVocabulary):
+class ContactParagraphRendererVocabulary(RenderersVocabulary):
     """Contact paragraph renderers vocabulary"""
 
-    def __init__(self, context=None):
-        request = check_request()
-        translate = request.localizer.translate
-        registry = request.registry
-        if not IContactParagraph.providedBy(context):
-            context = ContactParagraph()
-        terms = [SimpleTerm(name, title=translate(adapter.label))
-                 for name, adapter in sorted(registry.getAdapters((context, request), IContentRenderer),
-                                             key=lambda x: x[1].weight)]
-        super(ContactParagraphRendererVocabulary, self).__init__(terms)
+    content_interface = IContactParagraph
--- a/src/pyams_content/component/paragraph/frame.py	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/component/paragraph/frame.py	Fri May 25 11:28:17 2018 +0200
@@ -27,15 +27,14 @@
 
 # import packages
 from pyams_content.component.paragraph import BaseParagraph, BaseParagraphContentChecker, BaseParagraphFactory
-from pyams_content.features.renderer import RenderedContentMixin, IContentRenderer
+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.request import check_request
 from pyams_utils.traversing import get_parent
 from pyams_utils.vocabulary import vocabulary_config
 from zope.interface import implementer
 from zope.schema.fieldproperty import FieldProperty
-from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm
 
 from pyams_content import _
 
@@ -45,7 +44,8 @@
 #
 
 @implementer(IFrameParagraph, IIllustrationTarget, IExtFileContainerTarget, ILinkContainerTarget)
-class FrameParagraph(RenderedContentMixin, BaseParagraph):
+@factory_config(provided=IFrameParagraph)
+class FrameParagraph(BaseParagraph):
     """Framed text paragraph"""
 
     icon_class = 'fa-list-alt'
@@ -91,16 +91,7 @@
 
 
 @vocabulary_config(name=FRAME_PARAGRAPH_RENDERERS)
-class FrameParagraphRendererVocabulary(SimpleVocabulary):
+class FrameParagraphRendererVocabulary(RenderersVocabulary):
     """Framed text paragraph renderers vocabulary"""
 
-    def __init__(self, context=None):
-        request = check_request()
-        translate = request.localizer.translate
-        registry = request.registry
-        if not IFrameParagraph.providedBy(context):
-            context = FrameParagraph()
-        terms = [SimpleTerm(name, title=translate(adapter.label))
-                 for name, adapter in sorted(registry.getAdapters((context, request), IContentRenderer),
-                                             key=lambda x: x[1].weight)]
-        super(FrameParagraphRendererVocabulary, self).__init__(terms)
+    content_interface = IFrameParagraph
--- a/src/pyams_content/component/paragraph/header.py	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/component/paragraph/header.py	Fri May 25 11:28:17 2018 +0200
@@ -20,27 +20,26 @@
 from pyams_content.component.paragraph.interfaces.header import IHeaderParagraph, HEADER_PARAGRAPH_TYPE, \
     HEADER_PARAGRAPH_RENDERERS
 from pyams_content.features.checker.interfaces import IContentChecker, MISSING_VALUE, MISSING_LANG_VALUE
-from pyams_content.features.renderer.interfaces import IContentRenderer
 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 RenderedContentMixin
+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.request import check_request
 from pyams_utils.text import get_text_start
 from pyams_utils.traversing import get_parent
 from pyams_utils.vocabulary import vocabulary_config
 from zope.interface import implementer
 from zope.schema.fieldproperty import FieldProperty
-from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm
 
 from pyams_content import _
 
 
 @implementer(IHeaderParagraph)
-class HeaderParagraph(RenderedContentMixin, BaseParagraph):
+@factory_config(provided=IHeaderParagraph)
+class HeaderParagraph(BaseParagraph):
     """Header paragraph"""
 
     icon_class = 'fa-download fa-rotate-180'
@@ -89,16 +88,7 @@
 
 
 @vocabulary_config(name=HEADER_PARAGRAPH_RENDERERS)
-class HeaderParagraphRendererVocabulary(SimpleVocabulary):
+class HeaderParagraphRendererVocabulary(RenderersVocabulary):
     """Header paragraph renderers vocabulary"""
 
-    def __init__(self, context=None):
-        request = check_request()
-        translate = request.localizer.translate
-        registry = request.registry
-        if not IHeaderParagraph.providedBy(context):
-            context = HeaderParagraph()
-        terms = [SimpleTerm(name, title=translate(adapter.label))
-                 for name, adapter in sorted(registry.getAdapters((context, request), IContentRenderer),
-                                             key=lambda x: x[1].weight)]
-        super(HeaderParagraphRendererVocabulary, self).__init__(terms)
+    content_interface = IHeaderParagraph
--- a/src/pyams_content/component/paragraph/html.py	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/component/paragraph/html.py	Fri May 25 11:28:17 2018 +0200
@@ -9,7 +9,6 @@
 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
 # FOR A PARTICULAR PURPOSE.
 #
-from pyams_sequence.interfaces import ISequentialIntIds
 
 __docformat__ = 'restructuredtext'
 
@@ -24,19 +23,23 @@
 from pyams_content.component.links.interfaces import ILinkContainerTarget, IInternalLink, IExternalLink, IMailtoLink
 from pyams_content.component.paragraph.interfaces import IParagraphFactory
 from pyams_content.component.paragraph.interfaces.html import RAW_PARAGRAPH_TYPE, IRawParagraph, HTML_PARAGRAPH_TYPE, \
-    IHTMLParagraph
+    IHTMLParagraph, RAW_PARAGRAPH_RENDERERS, HTML_PARAGRAPH_RENDERERS
 from pyams_content.features.checker.interfaces import IContentChecker, MISSING_VALUE, MISSING_LANG_VALUE
 from pyams_i18n.interfaces import II18n, II18nManager, INegotiator
+from pyams_sequence.interfaces import ISequentialIntIds
 from zope.lifecycleevent.interfaces import IObjectAddedEvent, IObjectModifiedEvent
 
 # import packages
 from pyams_content.component.links import InternalLink, ExternalLink, MailtoLink
 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.request import check_request
 from pyams_utils.traversing import get_parent
 from pyams_utils.url import absolute_url
+from pyams_utils.vocabulary import vocabulary_config
 from pyquery import PyQuery
 from pyramid.events import subscriber
 from pyramid.threadlocal import get_current_registry
@@ -52,6 +55,7 @@
 #
 
 @implementer(IRawParagraph)
+@factory_config(provided=IRawParagraph)
 class RawParagraph(BaseParagraph):
     """Raw HTML paragraph"""
 
@@ -59,6 +63,7 @@
     icon_hint = _("Raw HTML ")
 
     body = FieldProperty(IRawParagraph['body'])
+    renderer = FieldProperty(IRawParagraph['renderer'])
 
 
 @utility_config(name=RAW_PARAGRAPH_TYPE, provides=IParagraphFactory)
@@ -94,11 +99,19 @@
         return output
 
 
+@vocabulary_config(name=RAW_PARAGRAPH_RENDERERS)
+class RawParagraphRendererVocabulary(RenderersVocabulary):
+    """Raw HTML paragraph renderers vocabulary"""
+
+    content_interface = IRawParagraph
+
+
 #
 # HTML paragraph
 #
 
 @implementer(IHTMLParagraph, IIllustrationTarget, IExtFileContainerTarget, ILinkContainerTarget)
+@factory_config(provided=IHTMLParagraph)
 class HTMLParagraph(BaseParagraph):
     """HTML paragraph"""
 
@@ -106,6 +119,7 @@
     icon_hint = _("Rich text")
 
     body = FieldProperty(IHTMLParagraph['body'])
+    renderer = FieldProperty(IHTMLParagraph['renderer'])
 
 
 @utility_config(name=HTML_PARAGRAPH_TYPE, provides=IParagraphFactory)
@@ -223,3 +237,10 @@
                 else:
                     output.append(translate(MISSING_LANG_VALUE).format(field=field_title, lang=lang))
         return output
+
+
+@vocabulary_config(name=HTML_PARAGRAPH_RENDERERS)
+class HTMLParagraphRendererVocabulary(RenderersVocabulary):
+    """HTML paragraph renderers vocabulary"""
+
+    content_interface = IHTMLParagraph
--- a/src/pyams_content/component/paragraph/interfaces/__init__.py	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/component/paragraph/interfaces/__init__.py	Fri May 25 11:28:17 2018 +0200
@@ -16,6 +16,7 @@
 # import standard library
 
 # import interfaces
+from pyams_content.features.renderer import IRenderedContent
 from zope.annotation.interfaces import IAttributeAnnotatable
 from zope.container.interfaces import IOrderedContainer
 from zope.contentprovider.interfaces import IContentProvider
@@ -32,7 +33,7 @@
 PARAGRAPH_CONTAINER_KEY = 'pyams_content.paragraph'
 
 
-class IBaseParagraph(IAttributeAnnotatable):
+class IBaseParagraph(IRenderedContent, IAttributeAnnotatable):
     """Base paragraph interface"""
 
     containers('.IParagraphContainer')
--- a/src/pyams_content/component/paragraph/interfaces/contact.py	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/component/paragraph/interfaces/contact.py	Fri May 25 11:28:17 2018 +0200
@@ -17,7 +17,6 @@
 
 # import interfaces
 from pyams_content.component.paragraph import IBaseParagraph
-from pyams_content.features.renderer import IRenderedContent
 from pyams_content.shared.form.interfaces import FORM_CONTENT_TYPE
 
 # import packages
@@ -44,7 +43,7 @@
 CONTACT_PARAGRAPH_RENDERERS = 'PyAMS.paragraph.contact.renderers'
 
 
-class IContactParagraph(IRenderedContent, IBaseParagraph):
+class IContactParagraph(IBaseParagraph):
     """Contact paragraph interface"""
 
     name = TextLine(title=_("Contact identity"),
--- a/src/pyams_content/component/paragraph/interfaces/frame.py	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/component/paragraph/interfaces/frame.py	Fri May 25 11:28:17 2018 +0200
@@ -17,7 +17,6 @@
 
 # import interfaces
 from pyams_content.component.paragraph.interfaces import IBaseParagraph
-from pyams_content.features.renderer import IRenderedContent
 
 # import packages
 from pyams_i18n.schema import I18nHTMLField
@@ -34,7 +33,7 @@
 FRAME_PARAGRAPH_RENDERERS = 'PyAMS.paragraph.text.renderers'
 
 
-class IFrameParagraph(IRenderedContent, IBaseParagraph):
+class IFrameParagraph(IBaseParagraph):
     """Framed text paragraph interface"""
 
     body = I18nHTMLField(title=_("Frame body"),
--- a/src/pyams_content/component/paragraph/interfaces/header.py	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/component/paragraph/interfaces/header.py	Fri May 25 11:28:17 2018 +0200
@@ -17,7 +17,6 @@
 
 # import interfaces
 from pyams_content.component.paragraph.interfaces import IBaseParagraph
-from pyams_content.features.renderer import IRenderedContent
 
 # import packages
 from pyams_i18n.schema import I18nTextField
@@ -34,7 +33,7 @@
 HEADER_PARAGRAPH_RENDERERS = 'PyAMS.paragraph.header.renderers'
 
 
-class IHeaderParagraph(IRenderedContent, IBaseParagraph):
+class IHeaderParagraph(IBaseParagraph):
     """Header paragraph"""
 
     header = I18nTextField(title=_("Header"),
--- a/src/pyams_content/component/paragraph/interfaces/html.py	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/component/paragraph/interfaces/html.py	Fri May 25 11:28:17 2018 +0200
@@ -20,6 +20,7 @@
 
 # import packages
 from pyams_i18n.schema import I18nHTMLField, I18nTextField
+from zope.schema import Choice
 
 from pyams_content import _
 
@@ -29,6 +30,7 @@
 #
 
 RAW_PARAGRAPH_TYPE = 'raw'
+RAW_PARAGRAPH_RENDERERS = 'PyAMS.paragraph.raw.renderers'
 
 
 class IRawParagraph(IBaseParagraph):
@@ -39,12 +41,18 @@
                                        "care!!"),
                          required=False)
 
+    renderer = Choice(title=_("Raw HTML code template"),
+                      description=_("Presentation template used for this paragraph"),
+                      vocabulary=RAW_PARAGRAPH_RENDERERS,
+                      default='default')
+
 
 #
 # HTML paragraph
 #
 
 HTML_PARAGRAPH_TYPE = 'HTML'
+HTML_PARAGRAPH_RENDERERS = 'PyAMS.paragraph.html.renderers'
 
 
 class IHTMLParagraph(IBaseParagraph):
@@ -52,3 +60,8 @@
 
     body = I18nHTMLField(title=_("Body"),
                          required=False)
+
+    renderer = Choice(title=_("Body template"),
+                      description=_("Presentation template used for this paragraph"),
+                      vocabulary=HTML_PARAGRAPH_RENDERERS,
+                      default='default')
--- a/src/pyams_content/component/paragraph/interfaces/keynumber.py	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/component/paragraph/interfaces/keynumber.py	Fri May 25 11:28:17 2018 +0200
@@ -17,7 +17,6 @@
 
 # import interfaces
 from pyams_content.component.paragraph import IBaseParagraph
-from pyams_content.features.renderer import IRenderedContent
 from pyams_content.interfaces.container import IOrderedContainer
 from zope.annotation.interfaces import IAttributeAnnotatable
 
@@ -77,7 +76,7 @@
 KEYNUMBER_PARAGRAPH_RENDERERS = 'PyAMS.keynumbers.renderers'
 
 
-class IKeyNumberParagraph(IKeyNumberContainerTarget, IRenderedContent, IBaseParagraph):
+class IKeyNumberParagraph(IKeyNumberContainerTarget, IBaseParagraph):
     """Key numbers paragraph interface"""
 
     renderer = Choice(title=_("Key numbers template"),
--- a/src/pyams_content/component/paragraph/interfaces/keypoint.py	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/component/paragraph/interfaces/keypoint.py	Fri May 25 11:28:17 2018 +0200
@@ -17,7 +17,6 @@
 
 # import interfaces
 from pyams_content.component.paragraph.interfaces import IBaseParagraph
-from pyams_content.features.renderer import IRenderedContent
 
 # import packages
 from pyams_i18n.schema import I18nTextField
@@ -34,7 +33,7 @@
 KEYPOINTS_PARAGRAPH_RENDERERS = 'PyAMS.paragraph.keypoint.renderers'
 
 
-class IKeypointsParagraph(IRenderedContent, IBaseParagraph):
+class IKeypointsParagraph(IBaseParagraph):
     """Key points paragraph"""
 
     body = I18nTextField(title=_("Key points"),
--- a/src/pyams_content/component/paragraph/interfaces/milestone.py	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/component/paragraph/interfaces/milestone.py	Fri May 25 11:28:17 2018 +0200
@@ -17,7 +17,6 @@
 
 # import interfaces
 from pyams_content.component.paragraph import IBaseParagraph
-from pyams_content.features.renderer import IRenderedContent
 from pyams_content.interfaces.container import IOrderedContainer
 from zope.annotation.interfaces import IAttributeAnnotatable
 
@@ -77,7 +76,7 @@
 MILESTONE_PARAGRAPH_RENDERERS = 'PyAMS.milestones.renderers'
 
 
-class IMilestoneParagraph(IMilestoneContainerTarget, IRenderedContent, IBaseParagraph):
+class IMilestoneParagraph(IMilestoneContainerTarget, IBaseParagraph):
     """Milestones paragraph interface"""
 
     renderer = Choice(title=_("Milestones template"),
--- a/src/pyams_content/component/paragraph/interfaces/pictogram.py	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/component/paragraph/interfaces/pictogram.py	Fri May 25 11:28:17 2018 +0200
@@ -17,7 +17,6 @@
 
 # import interfaces
 from pyams_content.component.paragraph.interfaces import IBaseParagraph
-from pyams_content.features.renderer.interfaces import IRenderedContent
 from pyams_content.interfaces.container import IOrderedContainer
 from pyams_content.reference.pictograms.interfaces import SELECTED_PICTOGRAM_VOCABULARY
 from zope.annotation import IAttributeAnnotatable
@@ -81,7 +80,7 @@
 PICTOGRAM_PARAGRAPH_RENDERERS = 'MyAMS.pictograms.renderers'
 
 
-class IPictogramParagraph(IPictogramContainerTarget, IRenderedContent, IBaseParagraph):
+class IPictogramParagraph(IPictogramContainerTarget, IBaseParagraph):
     """Pictograms paragraph interface"""
 
     renderer = Choice(title=_("Pictograms template"),
--- a/src/pyams_content/component/paragraph/interfaces/verbatim.py	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/component/paragraph/interfaces/verbatim.py	Fri May 25 11:28:17 2018 +0200
@@ -17,7 +17,6 @@
 
 # import interfaces
 from pyams_content.component.paragraph.interfaces import IBaseParagraph
-from pyams_content.features.renderer import IRenderedContent
 
 # import packages
 from pyams_i18n.schema import I18nTextLineField, I18nTextField
@@ -34,7 +33,7 @@
 VERBATIM_PARAGRAPH_RENDERERS = 'PyAMS.paragraph.verbatim.renderers'
 
 
-class IVerbatimParagraph(IRenderedContent, IBaseParagraph):
+class IVerbatimParagraph(IBaseParagraph):
     """Verbatim paragraph interface"""
 
     quote = I18nTextField(title=_("Quoted text"),
--- a/src/pyams_content/component/paragraph/interfaces/video.py	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/component/paragraph/interfaces/video.py	Fri May 25 11:28:17 2018 +0200
@@ -17,7 +17,6 @@
 
 # import interfaces
 from pyams_content.component.paragraph.interfaces import IBaseParagraph
-from pyams_content.features.renderer.interfaces import IRenderedContent
 
 # import packages
 from pyams_file.schema import VideoField
@@ -35,7 +34,7 @@
 VIDEO_PARAGRAPH_RENDERERS = 'PyAMS.paragraph.video.renderers'
 
 
-class IVideoParagraph(IRenderedContent, IBaseParagraph):
+class IVideoParagraph(IBaseParagraph):
     """Video paragraph"""
 
     body = I18nHTMLField(title=_("Body"),
--- a/src/pyams_content/component/paragraph/keynumber.py	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/component/paragraph/keynumber.py	Fri May 25 11:28:17 2018 +0200
@@ -33,8 +33,9 @@
 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 RenderedContentMixin, IContentRenderer
+from pyams_content.features.renderer import RenderersVocabulary
 from pyams_utils.adapter import adapter_config, ContextAdapter
+from pyams_utils.factory import factory_config
 from pyams_utils.registry import get_current_registry, get_utility, utility_config
 from pyams_utils.request import check_request
 from pyams_utils.traversing import get_parent
@@ -46,7 +47,6 @@
 from zope.lifecycleevent import ObjectCreatedEvent, ObjectModifiedEvent
 from zope.location import locate
 from zope.schema.fieldproperty import FieldProperty
-from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm
 
 from pyams_content import _
 
@@ -206,7 +206,8 @@
 
 
 @implementer(IKeyNumberParagraph)
-class KeyNumberParagraph(RenderedContentMixin, BaseParagraph):
+@factory_config(provided=IKeyNumberParagraph)
+class KeyNumberParagraph(BaseParagraph):
     """Key numbers paragraph"""
 
     icon_class = 'fa-list-ol'
@@ -256,16 +257,7 @@
 
 
 @vocabulary_config(name=KEYNUMBER_PARAGRAPH_RENDERERS)
-class KeyNumberParagraphRendererVocabulary(SimpleVocabulary):
+class KeyNumberParagraphRendererVocabulary(RenderersVocabulary):
     """Key numbers paragraph renderers vocabulary"""
 
-    def __init__(self, context=None):
-        request = check_request()
-        translate = request.localizer.translate
-        registry = request.registry
-        if not IKeyNumberParagraph.providedBy(context):
-            context = KeyNumberParagraph()
-        terms = [SimpleTerm(name, title=translate(adapter.label))
-                 for name, adapter in sorted(registry.getAdapters((context, request), IContentRenderer),
-                                             key=lambda x: x[1].weight)]
-        super(KeyNumberParagraphRendererVocabulary, self).__init__(terms)
+    content_interface = IKeyNumberParagraph
--- a/src/pyams_content/component/paragraph/keypoint.py	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/component/paragraph/keypoint.py	Fri May 25 11:28:17 2018 +0200
@@ -20,27 +20,26 @@
 from pyams_content.component.paragraph.interfaces.keypoint import IKeypointsParagraph, KEYPOINTS_PARAGRAPH_TYPE, \
     KEYPOINTS_PARAGRAPH_RENDERERS
 from pyams_content.features.checker.interfaces import IContentChecker, MISSING_VALUE, MISSING_LANG_VALUE
-from pyams_content.features.renderer.interfaces import IContentRenderer
 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 RenderedContentMixin
+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.request import check_request
 from pyams_utils.text import get_text_start
 from pyams_utils.traversing import get_parent
 from pyams_utils.vocabulary import vocabulary_config
 from zope.interface import implementer
 from zope.schema.fieldproperty import FieldProperty
-from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm
 
 from pyams_content import _
 
 
 @implementer(IKeypointsParagraph)
-class KeypointsParagraph(RenderedContentMixin, BaseParagraph):
+@factory_config(provided=IKeypointsParagraph)
+class KeypointsParagraph(BaseParagraph):
     """Key points paragraph"""
 
     icon_class = 'fa-key'
@@ -90,16 +89,7 @@
 
 
 @vocabulary_config(name=KEYPOINTS_PARAGRAPH_RENDERERS)
-class KeypointsParagraphRendererVocabulary(SimpleVocabulary):
+class KeypointsParagraphRendererVocabulary(RenderersVocabulary):
     """Key points paragraph renderers vocabulary"""
 
-    def __init__(self, context=None):
-        request = check_request()
-        translate = request.localizer.translate
-        registry = request.registry
-        if not IKeypointsParagraph.providedBy(context):
-            context = KeypointsParagraph()
-        terms = [SimpleTerm(name, title=translate(adapter.label))
-                 for name, adapter in sorted(registry.getAdapters((context, request), IContentRenderer),
-                                             key=lambda x: x[1].weight)]
-        super(KeypointsParagraphRendererVocabulary, self).__init__(terms)
+    content_interface = IKeypointsParagraph
--- a/src/pyams_content/component/paragraph/milestone.py	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/component/paragraph/milestone.py	Fri May 25 11:28:17 2018 +0200
@@ -34,8 +34,9 @@
 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 RenderedContentMixin, IContentRenderer
+from pyams_content.features.renderer import RenderersVocabulary
 from pyams_utils.adapter import adapter_config, ContextAdapter
+from pyams_utils.factory import factory_config
 from pyams_utils.registry import get_current_registry, get_utility, utility_config
 from pyams_utils.request import check_request
 from pyams_utils.traversing import get_parent
@@ -47,7 +48,6 @@
 from zope.lifecycleevent import ObjectCreatedEvent, ObjectModifiedEvent
 from zope.location import locate
 from zope.schema.fieldproperty import FieldProperty
-from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm
 
 from pyams_content import _
 
@@ -221,7 +221,8 @@
 
 
 @implementer(IMilestoneParagraph)
-class MilestoneParagraph(RenderedContentMixin, BaseParagraph):
+@factory_config(provided=IMilestoneParagraph)
+class MilestoneParagraph(BaseParagraph):
     """Milestones paragraph"""
 
     icon_class = 'fa-arrows-h'
@@ -271,16 +272,7 @@
 
 
 @vocabulary_config(name=MILESTONE_PARAGRAPH_RENDERERS)
-class MilestoneParagraphRendererVocabulary(SimpleVocabulary):
+class MilestoneParagraphRendererVocabulary(RenderersVocabulary):
     """Milestones paragraph renderers vocabulary"""
 
-    def __init__(self, context=None):
-        request = check_request()
-        translate = request.localizer.translate
-        registry = request.registry
-        if not IMilestoneParagraph.providedBy(context):
-            context = MilestoneParagraph()
-        terms = [SimpleTerm(name, title=translate(adapter.label))
-                 for name, adapter in sorted(registry.getAdapters((context, request), IContentRenderer),
-                                             key=lambda x: x[1].weight)]
-        super(MilestoneParagraphRendererVocabulary, self).__init__(terms)
+    content_interface = IMilestoneParagraph
--- a/src/pyams_content/component/paragraph/pictogram.py	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/component/paragraph/pictogram.py	Fri May 25 11:28:17 2018 +0200
@@ -23,7 +23,6 @@
     IPictogramContainer, PICTOGRAM_CONTAINER_KEY, IPictogramParagraph, PICTOGRAM_PARAGRAPH_TYPE, \
     PICTOGRAM_PARAGRAPH_RENDERERS
 from pyams_content.features.checker.interfaces import IContentChecker, MISSING_VALUE, MISSING_LANG_VALUE, ERROR_VALUE
-from pyams_content.features.renderer.interfaces import IContentRenderer
 from pyams_content.reference.pictograms.interfaces import IPictogramTable
 from pyams_form.interfaces.form import IFormContextPermissionChecker
 from pyams_i18n.interfaces import II18n, II18nManager, INegotiator
@@ -36,8 +35,9 @@
 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 RenderedContentMixin
+from pyams_content.features.renderer import RenderersVocabulary
 from pyams_utils.adapter import adapter_config, ContextAdapter
+from pyams_utils.factory import factory_config
 from pyams_utils.registry import query_utility, get_current_registry, get_utility, utility_config
 from pyams_utils.request import check_request
 from pyams_utils.traversing import get_parent
@@ -49,7 +49,6 @@
 from zope.interface import implementer
 from zope.location import locate
 from zope.schema.fieldproperty import FieldProperty
-from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm
 
 from pyams_content import _
 
@@ -212,7 +211,8 @@
 
 
 @implementer(IPictogramParagraph, IIllustrationTarget)
-class PictogramParagraph(RenderedContentMixin, BaseParagraph):
+@factory_config(provided=IPictogramParagraph)
+class PictogramParagraph(BaseParagraph):
     """Pictograms paragraph"""
 
     icon_class = 'fa-linode'
@@ -262,16 +262,7 @@
 
 
 @vocabulary_config(name=PICTOGRAM_PARAGRAPH_RENDERERS)
-class PictogramParagraphRendererVocabulary(SimpleVocabulary):
+class PictogramParagraphRendererVocabulary(RenderersVocabulary):
     """Pictograms paragraph renderers vocabulary"""
 
-    def __init__(self, context=None):
-        request = check_request()
-        translate = request.localizer.translate
-        registry = request.registry
-        if not IPictogramParagraph.providedBy(context):
-            context = PictogramParagraph()
-        terms = [SimpleTerm(name, title=translate(adapter.label))
-                 for name, adapter in sorted(registry.getAdapters((context, request), IContentRenderer),
-                                             key=lambda x: x[1].weight)]
-        super(PictogramParagraphRendererVocabulary, self).__init__(terms)
+    content_interface = IPictogramParagraph
--- a/src/pyams_content/component/paragraph/verbatim.py	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/component/paragraph/verbatim.py	Fri May 25 11:28:17 2018 +0200
@@ -25,15 +25,14 @@
 
 # import packages
 from pyams_content.component.paragraph import BaseParagraph, BaseParagraphContentChecker, BaseParagraphFactory
-from pyams_content.features.renderer import RenderedContentMixin, IContentRenderer
+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.request import check_request
 from pyams_utils.traversing import get_parent
 from pyams_utils.vocabulary import vocabulary_config
 from zope.interface import implementer
 from zope.schema.fieldproperty import FieldProperty
-from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm
 
 from pyams_content import _
 
@@ -43,7 +42,8 @@
 #
 
 @implementer(IVerbatimParagraph, IIllustrationTarget)
-class VerbatimParagraph(RenderedContentMixin, BaseParagraph):
+@factory_config(provided=IVerbatimParagraph)
+class VerbatimParagraph(BaseParagraph):
     """Verbatim paragraph"""
 
     icon_class = 'fa-quote-right'
@@ -96,16 +96,7 @@
 
 
 @vocabulary_config(name=VERBATIM_PARAGRAPH_RENDERERS)
-class VerbatimParagraphRendererVocabulary(SimpleVocabulary):
+class VerbatimParagraphRendererVocabulary(RenderersVocabulary):
     """Verbatim paragraph renderers vocabulary"""
 
-    def __init__(self, context=None):
-        request = check_request()
-        translate = request.localizer.translate
-        registry = request.registry
-        if not IVerbatimParagraph.providedBy(context):
-            context = VerbatimParagraph()
-        terms = [SimpleTerm(name, title=translate(adapter.label))
-                 for name, adapter in sorted(registry.getAdapters((context, request), IContentRenderer),
-                                             key=lambda x: x[1].weight)]
-        super(VerbatimParagraphRendererVocabulary, self).__init__(terms)
+    content_interface = IVerbatimParagraph
--- a/src/pyams_content/component/paragraph/video.py	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/component/paragraph/video.py	Fri May 25 11:28:17 2018 +0200
@@ -28,23 +28,23 @@
 # import packages
 from pyams_content.component.paragraph import BaseParagraph, BaseParagraphContentChecker, BaseParagraphFactory
 from pyams_content.component.paragraph.html import check_associations
-from pyams_content.features.renderer import RenderedContentMixin, IContentRenderer
+from pyams_content.features.renderer import RenderersVocabulary
 from pyams_file.property import FileProperty
 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.request import check_request
 from pyams_utils.traversing import get_parent
 from pyams_utils.vocabulary import vocabulary_config
 from pyramid.events import subscriber
 from zope.interface import implementer
 from zope.schema.fieldproperty import FieldProperty
-from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm
 
 from pyams_content import _
 
 
 @implementer(IVideoParagraph, IExtFileContainerTarget, ILinkContainerTarget)
-class VideoParagraph(RenderedContentMixin, BaseParagraph):
+@factory_config(provided=IVideoParagraph)
+class VideoParagraph(BaseParagraph):
     """Video paragraph class"""
 
     icon_class = 'fa-film'
@@ -111,16 +111,7 @@
 
 
 @vocabulary_config(name=VIDEO_PARAGRAPH_RENDERERS)
-class VideoParagraphRendererVocabulary(SimpleVocabulary):
+class VideoParagraphRendererVocabulary(RenderersVocabulary):
     """Video paragraph renderers vocabulary"""
 
-    def __init__(self, context=None):
-        request = check_request()
-        translate = request.localizer.translate
-        registry = request.registry
-        if not IVideoParagraph.providedBy(context):
-            context = VideoParagraph()
-        terms = [SimpleTerm(name, title=translate(adapter.label))
-                 for name, adapter in sorted(registry.getAdapters((context, request), IContentRenderer),
-                                             key=lambda x: x[1].weight)]
-        super(VideoParagraphRendererVocabulary, self).__init__(terms)
+    content_interface = IVideoParagraph
--- a/src/pyams_content/component/paragraph/zmi/__init__.py	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/component/paragraph/zmi/__init__.py	Fri May 25 11:28:17 2018 +0200
@@ -17,7 +17,7 @@
 
 # import interfaces
 from pyams_content.component.paragraph.interfaces import IParagraphContainerTarget, IBaseParagraph, IParagraphFactory, \
-    IParagraphFactorySettings
+    IParagraphFactorySettings, IParagraphRenderer
 from pyams_content.component.paragraph.zmi.interfaces import IParagraphContainerView
 from pyams_content.interfaces import MANAGE_TOOL_PERMISSION
 from pyams_content.shared.common.interfaces import IWfSharedContent
@@ -31,6 +31,7 @@
 
 # import packages
 from pyams_content.component.paragraph.zmi.container import ParagraphContainerTable, ParagraphContainerBaseTable
+from pyams_content.features.renderer.zmi import BaseRenderedContentRenderer
 from pyams_form.form import AJAXEditForm, AJAXAddForm
 from pyams_form.help import FormHelp
 from pyams_form.schema import ActionButton, CloseButton
@@ -205,8 +206,8 @@
     """Paragraph edit form buttons"""
 
     preview = ActionButton(name='preview', title=_("Preview"),
-                           label_css_class='fa fa-fw fa-eye',
-                           url='preview.html',
+                           label_css_class='fa fa-fw fa-binoculars',
+                           url='content-preview.html',
                            modal_target=True)
 
     close = CloseButton(name='close', title=_("Cancel"))
@@ -242,3 +243,12 @@
             output.setdefault('events', []).append(
                 get_json_paragraph_refresh_event(self.context, self.request))
         return output
+
+
+#
+# Base paragraph renderer
+#
+
+@adapter_config(context=(IBaseParagraph, IPyAMSLayer), provides=IParagraphRenderer)
+class BaseParagraphRenderer(BaseRenderedContentRenderer):
+    """Base paragraph renderer"""
--- a/src/pyams_content/component/paragraph/zmi/contact.py	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/component/paragraph/zmi/contact.py	Fri May 25 11:28:17 2018 +0200
@@ -17,7 +17,7 @@
 
 # import interfaces
 from pyams_content.component.paragraph.interfaces import IParagraphContainerTarget, IParagraphContainer, \
-    IBaseParagraph, IParagraphRenderer
+    IBaseParagraph
 from pyams_content.component.paragraph.interfaces.contact import CONTACT_PARAGRAPH_TYPE, IContactParagraph
 from pyams_content.component.paragraph.zmi.interfaces import IParagraphContainerView, IParagraphInnerEditor
 from pyams_content.interfaces import MANAGE_CONTENT_PERMISSION
@@ -32,7 +32,6 @@
 from pyams_content.component.paragraph.zmi import BaseParagraphAddMenu, BaseParagraphAJAXAddForm, \
     BaseParagraphPropertiesEditForm, BaseParagraphAJAXEditForm, get_json_paragraph_refresh_event, \
     IParagraphEditFormButtons
-from pyams_content.features.renderer.zmi import BaseRenderedContentRenderer
 from pyams_content.features.renderer.zmi.widget import RendererFieldWidget
 from pyams_pagelet.pagelet import pagelet_config
 from pyams_skin.event import get_json_form_refresh_event
@@ -150,12 +149,3 @@
             output.setdefault('events', []).append(get_json_form_refresh_event(self.context, self.request,
                                                                                ContactParagraphInnerEditForm))
         return output
-
-
-#
-# Contact paragraph renderer
-#
-
-@adapter_config(context=(IContactParagraph, IPyAMSLayer), provides=IParagraphRenderer)
-class ContactParagraphRenderer(BaseRenderedContentRenderer):
-    """Contact paragraph renderer"""
--- a/src/pyams_content/component/paragraph/zmi/frame.py	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/component/paragraph/zmi/frame.py	Fri May 25 11:28:17 2018 +0200
@@ -18,8 +18,7 @@
 # import interfaces
 from pyams_content.component.association.interfaces import IAssociationTarget
 from pyams_content.component.association.zmi.interfaces import IAssociationsParentForm
-from pyams_content.component.paragraph.interfaces import IParagraphContainerTarget, IParagraphContainer, \
-    IParagraphRenderer
+from pyams_content.component.paragraph.interfaces import IParagraphContainerTarget, IParagraphContainer
 from pyams_content.component.paragraph.interfaces.frame import IFrameParagraph, FRAME_PARAGRAPH_TYPE
 from pyams_content.component.paragraph.zmi.interfaces import IParagraphInnerEditor, IParagraphContainerView
 from pyams_content.interfaces import MANAGE_CONTENT_PERMISSION
@@ -38,7 +37,6 @@
     IParagraphEditFormButtons
 from pyams_content.component.paragraph.zmi.container import ParagraphContainerTable, \
     ParagraphTitleToolbarViewletManager
-from pyams_content.features.renderer.zmi import BaseRenderedContentRenderer
 from pyams_content.features.renderer.zmi.widget import RendererFieldWidget
 from pyams_pagelet.pagelet import pagelet_config
 from pyams_skin.event import get_json_switched_table_refresh_event, get_json_widget_refresh_event
@@ -200,12 +198,3 @@
             output.setdefault('events', []).append(
                 get_json_widget_refresh_event(self.context, self.request, FrameParagraphInnerEditForm, 'renderer'))
         return output
-
-
-#
-# Framed text paragraph renderer
-#
-
-@adapter_config(context=(IFrameParagraph, IPyAMSLayer), provides=IParagraphRenderer)
-class FrameParagraphRenderer(BaseRenderedContentRenderer):
-    """Framed text paragraph renderer"""
--- a/src/pyams_content/component/paragraph/zmi/header.py	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/component/paragraph/zmi/header.py	Fri May 25 11:28:17 2018 +0200
@@ -16,8 +16,7 @@
 # import standard library
 
 # import interfaces
-from pyams_content.component.paragraph.interfaces import IParagraphContainerTarget, IParagraphContainer, \
-    IParagraphRenderer
+from pyams_content.component.paragraph.interfaces import IParagraphContainerTarget, IParagraphContainer
 from pyams_content.component.paragraph.interfaces.header import IHeaderParagraph, HEADER_PARAGRAPH_TYPE
 from pyams_content.component.paragraph.zmi.interfaces import IParagraphInnerEditor, IParagraphContainerView
 from pyams_content.interfaces import MANAGE_CONTENT_PERMISSION
@@ -30,7 +29,6 @@
 from pyams_content.component.paragraph.header import HeaderParagraph
 from pyams_content.component.paragraph.zmi import BaseParagraphAJAXAddForm, BaseParagraphAJAXEditForm, \
     BaseParagraphAddMenu, BaseParagraphPropertiesEditForm, get_json_paragraph_refresh_event, IParagraphEditFormButtons
-from pyams_content.features.renderer.zmi import BaseRenderedContentRenderer
 from pyams_content.features.renderer.zmi.widget import RendererFieldWidget
 from pyams_pagelet.pagelet import pagelet_config
 from pyams_skin.event import get_json_widget_refresh_event
@@ -152,12 +150,3 @@
             output.setdefault('events', []).append(get_json_widget_refresh_event(self.context, self.request,
                                                                                  HeaderParagraphInnerEditForm, 'renderer'))
         return output
-
-
-#
-# Header paragraph renderer
-#
-
-@adapter_config(context=(IHeaderParagraph, IPyAMSLayer), provides=IParagraphRenderer)
-class HeaderParagraphRenderer(BaseRenderedContentRenderer):
-    """Header paragraph renderer"""
--- a/src/pyams_content/component/paragraph/zmi/html.py	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/component/paragraph/zmi/html.py	Fri May 25 11:28:17 2018 +0200
@@ -18,15 +18,13 @@
 # import interfaces
 from pyams_content.component.association.interfaces import IAssociationTarget
 from pyams_content.component.association.zmi.interfaces import IAssociationsParentForm
-from pyams_content.component.illustration.interfaces import IIllustration
 from pyams_content.component.paragraph.interfaces import IParagraphContainerTarget, IParagraphFactorySettings, \
-    IParagraphContainer, IParagraphRenderer
+    IParagraphContainer
 from pyams_content.component.paragraph.interfaces.html import IHTMLParagraph, IRawParagraph, RAW_PARAGRAPH_TYPE, \
     HTML_PARAGRAPH_TYPE
 from pyams_content.component.paragraph.zmi.interfaces import IParagraphInnerEditor, IParagraphContainerView
 from pyams_content.interfaces import MANAGE_CONTENT_PERMISSION
 from pyams_form.interfaces.form import IInnerForm
-from pyams_i18n.interfaces import II18n
 from pyams_skin.interfaces.viewlet import IToolbarAddingMenu
 from pyams_skin.layer import IPyAMSLayer
 from pyams_zmi.interfaces import IPropertiesEditForm
@@ -38,14 +36,14 @@
 from pyams_content.component.paragraph.zmi import BaseParagraphAJAXAddForm, BaseParagraphAJAXEditForm, \
     BaseParagraphAddMenu, BaseParagraphPropertiesEditForm, get_json_paragraph_toolbar_refresh_event, \
     IParagraphEditFormButtons
+from pyams_content.features.renderer.zmi.widget import RendererFieldWidget
 from pyams_form.security import ProtectedFormObjectMixin
 from pyams_pagelet.pagelet import pagelet_config
 from pyams_skin.event import get_json_switched_table_refresh_event
 from pyams_skin.viewlet.menu import MenuDivider
-from pyams_template.template import template_config
 from pyams_utils.adapter import adapter_config
 from pyams_utils.traversing import get_parent
-from pyams_viewlet.viewlet import viewlet_config, BaseContentProvider
+from pyams_viewlet.viewlet import viewlet_config
 from pyams_zmi.form import AdminDialogAddForm
 from pyramid.view import view_config
 from z3c.form import field, button
@@ -125,6 +123,8 @@
     icon_css_class = 'fa fa-fw fa-code'
 
     fields = field.Fields(IRawParagraph).omit('__parent__', '__name__', 'visible')
+    fields['renderer'].widgetFactory = RendererFieldWidget
+
     ajax_handler = 'properties.json'
     edit_permission = MANAGE_CONTENT_PERMISSION
 
@@ -163,27 +163,6 @@
 
 
 #
-# Raw HTML paragraph renderer
-#
-
-@adapter_config(context=(IRawParagraph, IPyAMSLayer), provides=IParagraphRenderer)
-@template_config(template='templates/raw-render.pt', layer=IPyAMSLayer)
-class RawParagraphRenderer(BaseContentProvider):
-    """Raw HTML paragraph renderer"""
-
-    language = None
-
-    def update(self):
-        i18n = II18n(self.context)
-        if self.language:
-            for attr in ('title', 'body'):
-                setattr(self, attr, i18n.get_attribute(attr, self.language, request=self.request))
-        else:
-            for attr in ('title', 'body'):
-                setattr(self, attr, i18n.query_attribute(attr, request=self.request))
-
-
-#
 # Rich text paragraph
 #
 
@@ -245,6 +224,8 @@
     input_css_class = 'col-md-10'
 
     fields = field.Fields(IHTMLParagraph).omit('__parent__', '__name__', 'visible')
+    fields['renderer'].widgetFactory = RendererFieldWidget
+
     ajax_handler = 'properties.json'
     edit_permission = MANAGE_CONTENT_PERMISSION
 
@@ -308,36 +289,3 @@
             output.setdefault('events', []).append(
                 get_json_switched_table_refresh_event(self.context, self.request, AssociationsTable))
         return output
-
-
-#
-# Rich text paragraph renderer
-#
-
-@adapter_config(context=(IHTMLParagraph, IPyAMSLayer), provides=IParagraphRenderer)
-@template_config(template='templates/html-render.pt', layer=IPyAMSLayer)
-class HTMLParagraphRenderer(BaseContentProvider):
-    """Rich text paragraph renderer"""
-
-    illustration = None
-    illustration_renderer = None
-    language = None
-
-    def update(self):
-        i18n = II18n(self.context)
-        if self.language:
-            for attr in ('title', 'body'):
-                setattr(self, attr, i18n.get_attribute(attr, self.language, request=self.request))
-        else:
-            for attr in ('title', 'body'):
-                setattr(self, attr, i18n.query_attribute(attr, request=self.request))
-        self.illustration = IIllustration(self.context)
-        if self.illustration.data:
-            renderer = self.illustration_renderer = self.illustration.get_renderer()
-            if renderer is not None:
-                renderer.update()
-
-    def render_illustration(self):
-        if not self.illustration_renderer:
-            return ''
-        return self.illustration_renderer.render()
--- a/src/pyams_content/component/paragraph/zmi/keynumber.py	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/component/paragraph/zmi/keynumber.py	Fri May 25 11:28:17 2018 +0200
@@ -17,8 +17,7 @@
 import json
 
 # import interfaces
-from pyams_content.component.paragraph.interfaces import IParagraphContainerTarget, IParagraphContainer, \
-    IParagraphRenderer
+from pyams_content.component.paragraph.interfaces import IParagraphContainerTarget, IParagraphContainer
 from pyams_content.component.paragraph.interfaces.keynumber import KEYNUMBER_PARAGRAPH_TYPE, IKeyNumberParagraph, \
     IKeyNumberContainer, IKeyNumberContainerTarget, IKeyNumber
 from pyams_content.component.paragraph.zmi import IParagraphContainerView, IParagraphEditFormButtons
@@ -36,7 +35,6 @@
 from pyams_content.component.paragraph.keynumber import KeyNumberParagraph, KeyNumber
 from pyams_content.component.paragraph.zmi import BaseParagraphAddMenu, BaseParagraphAJAXAddForm, \
     BaseParagraphPropertiesEditForm, BaseParagraphAJAXEditForm
-from pyams_content.features.renderer.zmi import BaseRenderedContentRenderer
 from pyams_content.features.renderer.zmi.widget import RendererFieldWidget
 from pyams_form.form import AJAXAddForm, AJAXEditForm
 from pyams_form.security import ProtectedFormObjectMixin
@@ -165,15 +163,6 @@
 
 
 #
-# Key number paragraph renderer
-#
-
-@adapter_config(context=(IKeyNumberParagraph, IPyAMSLayer), provides=IParagraphRenderer)
-class KeyNumberParagraphRenderer(BaseRenderedContentRenderer):
-    """Key number paragraph renderer"""
-
-
-#
 # Key number items table view
 #
 
--- a/src/pyams_content/component/paragraph/zmi/keypoint.py	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/component/paragraph/zmi/keypoint.py	Fri May 25 11:28:17 2018 +0200
@@ -16,8 +16,7 @@
 # import standard library
 
 # import interfaces
-from pyams_content.component.paragraph.interfaces import IParagraphContainerTarget, IParagraphContainer, \
-    IParagraphRenderer
+from pyams_content.component.paragraph.interfaces import IParagraphContainerTarget, IParagraphContainer
 from pyams_content.component.paragraph.interfaces.keypoint import IKeypointsParagraph, KEYPOINTS_PARAGRAPH_TYPE
 from pyams_content.component.paragraph.zmi.interfaces import IParagraphInnerEditor, IParagraphContainerView
 from pyams_content.interfaces import MANAGE_CONTENT_PERMISSION
@@ -30,7 +29,6 @@
 from pyams_content.component.paragraph.keypoint import KeypointsParagraph
 from pyams_content.component.paragraph.zmi import BaseParagraphAJAXAddForm, BaseParagraphAJAXEditForm, \
     BaseParagraphAddMenu, BaseParagraphPropertiesEditForm, get_json_paragraph_refresh_event, IParagraphEditFormButtons
-from pyams_content.features.renderer.zmi import BaseRenderedContentRenderer
 from pyams_content.features.renderer.zmi.widget import RendererFieldWidget
 from pyams_pagelet.pagelet import pagelet_config
 from pyams_skin.event import get_json_widget_refresh_event
@@ -153,12 +151,3 @@
             output.setdefault('events', []).append(
                 get_json_widget_refresh_event(self.context, self.request, KeypointsParagraphInnerEditForm, 'renderer'))
         return output
-
-
-#
-# Key points paragraph renderer
-#
-
-@adapter_config(context=(IKeypointsParagraph, IPyAMSLayer), provides=IParagraphRenderer)
-class KeypointsParagraphRenderer(BaseRenderedContentRenderer):
-    """Key points paragraph renderer"""
--- a/src/pyams_content/component/paragraph/zmi/milestone.py	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/component/paragraph/zmi/milestone.py	Fri May 25 11:28:17 2018 +0200
@@ -17,8 +17,7 @@
 import json
 
 # import interfaces
-from pyams_content.component.paragraph.interfaces import IParagraphContainerTarget, IParagraphContainer, \
-    IParagraphRenderer
+from pyams_content.component.paragraph.interfaces import IParagraphContainerTarget, IParagraphContainer
 from pyams_content.component.paragraph.interfaces.milestone import MILESTONE_PARAGRAPH_TYPE, IMilestoneParagraph, \
     IMilestoneContainer, IMilestoneContainerTarget, IMilestone
 from pyams_content.component.paragraph.zmi import IParagraphContainerView, IParagraphEditFormButtons
@@ -36,7 +35,6 @@
 from pyams_content.component.paragraph.milestone import MilestoneParagraph, Milestone
 from pyams_content.component.paragraph.zmi import BaseParagraphAddMenu, BaseParagraphAJAXAddForm, \
     BaseParagraphPropertiesEditForm, BaseParagraphAJAXEditForm
-from pyams_content.features.renderer.zmi import BaseRenderedContentRenderer
 from pyams_content.features.renderer.zmi.widget import RendererFieldWidget
 from pyams_form.form import AJAXAddForm, AJAXEditForm
 from pyams_form.security import ProtectedFormObjectMixin
@@ -169,15 +167,6 @@
 
 
 #
-# Milestone paragraph renderer
-#
-
-@adapter_config(context=(IMilestoneParagraph, IPyAMSLayer), provides=IParagraphRenderer)
-class MilestoneParagraphRenderer(BaseRenderedContentRenderer):
-    """Milestone paragraph renderer"""
-
-
-#
 # Milestone items table view
 #
 
--- a/src/pyams_content/component/paragraph/zmi/pictogram.py	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/component/paragraph/zmi/pictogram.py	Fri May 25 11:28:17 2018 +0200
@@ -17,8 +17,7 @@
 import json
 
 # import interfaces
-from pyams_content.component.paragraph.interfaces import IParagraphContainerTarget, IParagraphContainer, \
-    IParagraphRenderer
+from pyams_content.component.paragraph.interfaces import IParagraphContainerTarget, IParagraphContainer
 from pyams_content.component.paragraph.interfaces.pictogram import PICTOGRAM_PARAGRAPH_TYPE, IPictogramParagraph, \
     IPictogramContainer, IPictogramContainerTarget, IPictogramItem
 from pyams_content.component.paragraph.zmi import IParagraphContainerView, IParagraphEditFormButtons
@@ -38,7 +37,6 @@
 from pyams_content.component.paragraph.pictogram import PictogramParagraph, PictogramItem
 from pyams_content.component.paragraph.zmi import BaseParagraphAddMenu, BaseParagraphAJAXAddForm, \
     BaseParagraphPropertiesEditForm, BaseParagraphAJAXEditForm
-from pyams_content.features.renderer.zmi import BaseRenderedContentRenderer
 from pyams_content.features.renderer.zmi.widget import RendererFieldWidget
 from pyams_form.form import AJAXAddForm, AJAXEditForm
 from pyams_form.security import ProtectedFormObjectMixin
@@ -168,15 +166,6 @@
 
 
 #
-# Pictogram paragraph renderer
-#
-
-@adapter_config(context=(IPictogramParagraph, IPyAMSLayer), provides=IParagraphRenderer)
-class PictogramParagraphRenderer(BaseRenderedContentRenderer):
-    """Pictogram paragraph renderer"""
-
-
-#
 # Pictogram items table view
 #
 
--- a/src/pyams_content/component/paragraph/zmi/templates/html-render.pt	Wed May 23 15:30:41 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,3 +0,0 @@
-<h3 tal:content="view.title">title</h3>
-<div tal:content="structure view.body">body</div>
-<tal:var content="structure view.render_illustration()">Illustration</tal:var>
--- a/src/pyams_content/component/paragraph/zmi/templates/raw-render.pt	Wed May 23 15:30:41 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,2 +0,0 @@
-<h3 tal:content="view.title">title</h3>
-<div tal:content="structure view.body">body</div>
--- a/src/pyams_content/component/paragraph/zmi/verbatim.py	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/component/paragraph/zmi/verbatim.py	Fri May 25 11:28:17 2018 +0200
@@ -17,8 +17,7 @@
 
 # import interfaces
 from pyams_content.component.association.zmi.interfaces import IAssociationsParentForm
-from pyams_content.component.paragraph.interfaces import IParagraphContainerTarget, IParagraphContainer, \
-    IParagraphRenderer
+from pyams_content.component.paragraph.interfaces import IParagraphContainerTarget, IParagraphContainer
 from pyams_content.component.paragraph.interfaces.verbatim import IVerbatimParagraph, VERBATIM_PARAGRAPH_TYPE
 from pyams_content.component.paragraph.zmi.interfaces import IParagraphInnerEditor, IParagraphContainerView
 from pyams_content.interfaces import MANAGE_CONTENT_PERMISSION
@@ -32,7 +31,6 @@
 from pyams_content.component.paragraph.verbatim import VerbatimParagraph
 from pyams_content.component.paragraph.zmi import BaseParagraphAJAXAddForm, BaseParagraphAJAXEditForm, \
     BaseParagraphAddMenu, BaseParagraphPropertiesEditForm, IParagraphEditFormButtons
-from pyams_content.features.renderer.zmi import BaseRenderedContentRenderer
 from pyams_content.features.renderer.zmi.widget import RendererFieldWidget
 from pyams_pagelet.pagelet import pagelet_config
 from pyams_skin.event import get_json_widget_refresh_event
@@ -147,12 +145,3 @@
             output.setdefault('events', []).append(
                 get_json_widget_refresh_event(self.context, self.request, VerbatimParagraphInnerEditForm, 'renderer'))
         return output
-
-
-#
-# Verbatim paragraph renderer
-#
-
-@adapter_config(context=(IVerbatimParagraph, IPyAMSLayer), provides=IParagraphRenderer)
-class VerbatimParagraphRenderer(BaseRenderedContentRenderer):
-    """Verbatim paragraph renderer"""
--- a/src/pyams_content/component/paragraph/zmi/video.py	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/component/paragraph/zmi/video.py	Fri May 25 11:28:17 2018 +0200
@@ -17,8 +17,7 @@
 
 # import interfaces
 from pyams_content.component.association.zmi.interfaces import IAssociationsParentForm
-from pyams_content.component.paragraph.interfaces import IParagraphContainerTarget, IParagraphContainer, \
-    IParagraphRenderer
+from pyams_content.component.paragraph.interfaces import IParagraphContainerTarget, IParagraphContainer
 from pyams_content.component.paragraph.interfaces.video import IVideoParagraph, VIDEO_PARAGRAPH_TYPE
 from pyams_content.component.paragraph.zmi.interfaces import IParagraphInnerEditor, IParagraphContainerView
 from pyams_content.interfaces import MANAGE_CONTENT_PERMISSION
@@ -34,7 +33,6 @@
 from pyams_content.component.paragraph.video import VideoParagraph
 from pyams_content.component.paragraph.zmi import BaseParagraphAJAXAddForm, BaseParagraphAJAXEditForm, \
     BaseParagraphAddMenu, BaseParagraphPropertiesEditForm, IParagraphEditFormButtons
-from pyams_content.features.renderer.zmi import BaseRenderedContentRenderer
 from pyams_content.features.renderer.zmi.widget import RendererFieldWidget
 from pyams_form.group import NamedWidgetsGroup
 from pyams_pagelet.pagelet import pagelet_config
@@ -204,12 +202,3 @@
                 get_json_widget_refresh_event(self.context, self.request,
                                               VideoParagraphPropertiesInnerEditForm, 'renderer'))
         return output
-
-
-#
-# Video paragraph renderer
-#
-
-@adapter_config(context=(IVideoParagraph, IPyAMSLayer), provides=IParagraphRenderer)
-class VideoParagraphRenderer(BaseRenderedContentRenderer):
-    """Video paragraph renderer"""
--- a/src/pyams_content/component/video/__init__.py	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/component/video/__init__.py	Fri May 25 11:28:17 2018 +0200
@@ -44,7 +44,7 @@
     provider_name = FieldProperty(IExternalVideo['provider_name'])
 
     def get_provider(self):
-        return query_utility(IExternalVideoProvider, name=self.provider_name)
+        return query_utility(IExternalVideoProvider, name=self.provider_name or '')
 
     @property
     def settings(self):
--- a/src/pyams_content/component/video/interfaces/__init__.py	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/component/video/interfaces/__init__.py	Fri May 25 11:28:17 2018 +0200
@@ -65,6 +65,7 @@
 
 
 EXTERNAL_VIDEO_PARAGRAPH_TYPE = 'External video'
+EXTERNAL_VIDEO_PARAGRAPH_RENDERERS = 'PyAMS.paragraph.video.renderers'
 
 
 class IExternalVideoParagraph(IExternalVideo, IBaseParagraph):
@@ -73,6 +74,11 @@
     body = I18nHTMLField(title=_("Body"),
                          required=False)
 
+    renderer = Choice(title=_("Video template"),
+                      description=_("Presentation template used for this video"),
+                      vocabulary=EXTERNAL_VIDEO_PARAGRAPH_RENDERERS,
+                      default='default')
+
 
 class IExternalVideoRenderer(IContentProvider):
     """External video renderer"""
--- a/src/pyams_content/component/video/paragraph.py	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/component/video/paragraph.py	Fri May 25 11:28:17 2018 +0200
@@ -17,17 +17,21 @@
 
 # import interfaces
 from pyams_content.component.paragraph.interfaces import IParagraphFactory
-from pyams_content.component.video.interfaces import IExternalVideoParagraph, EXTERNAL_VIDEO_PARAGRAPH_TYPE
+from pyams_content.component.video.interfaces import IExternalVideoParagraph, EXTERNAL_VIDEO_PARAGRAPH_TYPE, \
+    EXTERNAL_VIDEO_PARAGRAPH_RENDERERS
 from pyams_content.features.checker.interfaces import IContentChecker, MISSING_VALUE, MISSING_LANG_VALUE
 from pyams_i18n.interfaces import II18n, II18nManager, INegotiator
 
 # import packages
 from pyams_content.component.paragraph import BaseParagraph, BaseParagraphFactory
 from pyams_content.component.video import ExternalVideo, ExternalVideoContentChecker
+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.request import check_request
 from pyams_utils.traversing import get_parent
+from pyams_utils.vocabulary import vocabulary_config
 from zope.interface import implementer
 from zope.schema.fieldproperty import FieldProperty
 
@@ -35,6 +39,7 @@
 
 
 @implementer(IExternalVideoParagraph)
+@factory_config(provided=IExternalVideoParagraph)
 class ExternalVideoParagraph(ExternalVideo, BaseParagraph):
     """External video paragraph"""
 
@@ -42,6 +47,7 @@
     icon_hint = _("External video")
 
     body = FieldProperty(IExternalVideoParagraph['body'])
+    renderer = FieldProperty(IExternalVideoParagraph['renderer'])
 
 
 @utility_config(name=EXTERNAL_VIDEO_PARAGRAPH_TYPE, provides=IParagraphFactory)
@@ -85,3 +91,10 @@
                         output.insert(0, missing_lang_value.format(field=translate(IExternalVideoParagraph[attr].title),
                                                                    lang=lang))
         return output
+
+
+@vocabulary_config(name=EXTERNAL_VIDEO_PARAGRAPH_RENDERERS)
+class ExternalVideoParagraphRendererVocabulary(RenderersVocabulary):
+    """External video paragraph renderers vocabulary"""
+
+    content_interface = IExternalVideoParagraph
--- a/src/pyams_content/component/video/provider/zmi/__init__.py	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/component/video/provider/zmi/__init__.py	Fri May 25 11:28:17 2018 +0200
@@ -9,7 +9,6 @@
 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
 # FOR A PARTICULAR PURPOSE.
 #
-from pyams_content.component.video.provider import CustomVideoSettings
 
 __docformat__ = 'restructuredtext'
 
@@ -21,6 +20,7 @@
 from pyams_skin.layer import IPyAMSLayer
 
 # import packages
+from pyams_content.component.video.provider import CustomVideoSettings
 from pyams_content.component.video.provider.dailymotion import DailymotionVideoSettings
 from pyams_content.component.video.provider.vimeo import VimeoVideoSettings
 from pyams_content.component.video.provider.youtube import YoutubeVideoSettings
--- a/src/pyams_content/component/video/zmi/paragraph.py	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/component/video/zmi/paragraph.py	Fri May 25 11:28:17 2018 +0200
@@ -17,13 +17,12 @@
 
 # import interfaces
 from pyams_content.component.paragraph.interfaces import IParagraphContainerTarget, IParagraphContainer, \
-    IBaseParagraph, IParagraphRenderer
+    IBaseParagraph
 from pyams_content.component.paragraph.zmi.interfaces import IParagraphContainerView, IParagraphInnerEditor
 from pyams_content.component.video.interfaces import IExternalVideoProvider, IExternalVideoSettings, \
-    IExternalVideoParagraph, IExternalVideoRenderer, EXTERNAL_VIDEO_PARAGRAPH_TYPE
+    IExternalVideoParagraph, EXTERNAL_VIDEO_PARAGRAPH_TYPE
 from pyams_content.interfaces import MANAGE_CONTENT_PERMISSION
 from pyams_form.interfaces.form import IWidgetsSuffixViewletsManager, IInnerForm
-from pyams_i18n.interfaces import II18n
 from pyams_skin.interfaces.viewlet import IToolbarAddingMenu
 from pyams_skin.layer import IPyAMSLayer
 from pyams_utils.interfaces.data import IObjectData
@@ -34,13 +33,14 @@
     BaseParagraphPropertiesEditForm, BaseParagraphAJAXEditForm, get_json_paragraph_refresh_event, \
     IParagraphEditFormButtons
 from pyams_content.component.video.paragraph import ExternalVideoParagraph
+from pyams_content.features.renderer.zmi.widget import RendererFieldWidget
 from pyams_form.group import NamedWidgetsGroup
 from pyams_pagelet.pagelet import pagelet_config
 from pyams_template.template import template_config
 from pyams_utils.adapter import adapter_config
-from pyams_utils.registry import get_utility, get_current_registry
+from pyams_utils.registry import get_utility
 from pyams_utils.url import absolute_url
-from pyams_viewlet.viewlet import viewlet_config, Viewlet, BaseContentProvider
+from pyams_viewlet.viewlet import viewlet_config, Viewlet
 from pyams_zmi.form import AdminDialogAddForm, InnerAdminAddForm, InnerAdminEditForm
 from pyramid.events import subscriber
 from pyramid.exceptions import NotFound
@@ -105,7 +105,7 @@
                                              switch=True,
                                              hide_if_empty=True))
             self.add_group(NamedWidgetsGroup(self, 'data_group', self.widgets,
-                                             ('description', 'author', 'provider_name'),
+                                             ('description', 'author', 'renderer', 'provider_name'),
                                              bordered=False))
         super(ExternalVideoParagraphAddForm, self).updateGroups()
 
@@ -215,6 +215,7 @@
     @property
     def fields(self):
         fields = field.Fields(IExternalVideoParagraph).omit('__parent__', '__name__', 'visible')
+        fields['renderer'].widgetFactory = RendererFieldWidget
         provider = self.context.get_provider()
         if provider is not None:
             fields += field.Fields(provider.settings_interface)
@@ -239,7 +240,7 @@
                                              switch=True,
                                              hide_if_empty=True))
             self.add_group(NamedWidgetsGroup(self, 'data_group', self.widgets,
-                                             ('description', 'author', 'provider_name'),
+                                             ('description', 'author', 'renderer', 'provider_name'),
                                              bordered=False))
         if 'provider_name' in self.widgets:
             provider = self.context.get_provider()
@@ -295,40 +296,3 @@
              permission=MANAGE_CONTENT_PERMISSION, renderer='json', xhr=True)
 class ExternalVideoParagraphInnerAJAXEditForm(BaseParagraphAJAXEditForm, ExternalVideoParagraphInnerEditForm):
     """External video paragraph inner edit form, JSON renderer"""
-
-
-#
-# Video paragraph renderer
-#
-
-@adapter_config(context=(IExternalVideoParagraph, IPyAMSLayer), provides=IParagraphRenderer)
-@template_config(template='templates/video-render.pt', layer=IPyAMSLayer)
-class ExternalVideoParagraphRenderer(BaseContentProvider):
-    """External video paragraph renderer"""
-
-    video_renderer = None
-
-    def __init__(self, context, request):
-        super(ExternalVideoParagraphRenderer, self).__init__(context, request)
-        provider = context.get_provider()
-        if provider is not None:
-            registry = get_current_registry()
-            self.video_renderer = registry.queryMultiAdapter((context.settings, request), IExternalVideoRenderer)
-
-    def update(self):
-        i18n = II18n(self.context)
-        if self.language:
-            for attr in ('title', 'body', 'description'):
-                setattr(self, attr, i18n.get_attribute(attr, self.language, request=self.request))
-        else:
-            for attr in ('title', 'body', 'description'):
-                setattr(self, attr, i18n.query_attribute(attr, request=self.request))
-        renderer = self.video_renderer
-        if renderer is not None:
-            renderer.update()
-
-    def render_video(self):
-        renderer = self.video_renderer
-        if not renderer:
-            return ''
-        return renderer.render()
--- a/src/pyams_content/component/video/zmi/templates/video-render.pt	Wed May 23 15:30:41 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,7 +0,0 @@
-<h3 tal:condition="view.title"
-	tal:content="view.title">title</h3>
-<div tal:condition="view.body"
-	 tal:content="structure view.body">body</div>
-<div tal:condition="view.description"
-	 tal:content="structure extension:html(view.description)">Description</div>
-<tal:var replace="structure view.render_video()" />
--- a/src/pyams_content/features/preview/zmi/templates/preview.pt	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/features/preview/zmi/templates/preview.pt	Fri May 25 11:28:17 2018 +0200
@@ -24,7 +24,8 @@
 				<tal:if condition="len(langs) == 1">
 					<div class="margin-top-10">
 						<iframe src="preview.html" frameborder="0" style="width: 100%;"
-								tal:attributes="src string:preview.html?lang=${langs[0]}"
+								tal:define="url extension:absolute_url(context, 'preview.html')"
+								tal:attributes="src string:${url}?lang=${langs[0]}"
 								onload="this.style.height = (jQuery(parent.window).height() - 150) + 'px'"></iframe>
 					</div>
 				</tal:if>
@@ -34,7 +35,8 @@
 							<li tal:define="active python:'active' if repeat['lang'].start() else ''"
 								tal:attributes="class string:small ${active}">
 								<a data-toggle="tab" class="xsmall"
-								   tal:attributes="href string:#preview-${lang}">
+								   tal:define="url extension:absolute_url(context, 'preview.html')"
+								   tal:attributes="src string:${url}?lang=${lang}">
 									<img tal:attributes="src string:/--static--/pyams_i18n/img/flags/${lang}.png" />
 								</a>
 							</li>
@@ -46,7 +48,8 @@
 								 tal:attributes="class string:clearfix tab-pane ${active} fade in padding-5;
 												 id string:preview-${lang};">
 								<iframe src="preview.html" frameborder="0" style="width: 100%;"
-										tal:attributes="src string:preview.html?lang=${lang}"
+										tal:define="url extension:absolute_url(context, 'preview.html')"
+										tal:attributes="src string:${url}?lang=${lang}"
 										onload="this.style.height = (jQuery(parent.window).height() - 150) + 'px'"></iframe>
 							</div>
 						</tal:loop>
--- a/src/pyams_content/features/renderer/__init__.py	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/features/renderer/__init__.py	Fri May 25 11:28:17 2018 +0200
@@ -20,8 +20,10 @@
 
 # import packages
 from pyams_utils.adapter import adapter_config
+from pyams_utils.factory import get_object_factory
 from pyams_utils.request import check_request
 from zope.interface import implementer
+from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm
 
 
 @implementer(IRenderedContent)
@@ -29,12 +31,19 @@
     """Renderer mixin interface"""
 
     renderer = None
+    """Attribute used to store selected content renderer.    
+    Subclasses should generally override this attribute to define a "Choice" field property based
+    on a given renderers vocabulary. 
+    """
+
     renderer_interface = IContentRenderer
+    """Content renderer interface"""
 
     def get_renderer(self, request=None):
+        """Get rendering adapter based on selected renderer name"""
         if request is None:
             request = check_request()
-        return request.registry.queryMultiAdapter((self, request), self.renderer_interface, name=self.renderer)
+        return request.registry.queryMultiAdapter((self, request), self.renderer_interface, name=self.renderer or '')
 
 
 @adapter_config(context=IRenderedContent, provides=IContentRenderer)
@@ -50,3 +59,30 @@
     if renderer.settings_interface is None:
         return None
     return renderer.settings_interface(context)
+
+
+class RenderersVocabulary(SimpleVocabulary):
+    """Renderers vocabulary base class"""
+
+    content_interface = IRenderedContent
+    """Interface used to check current context"""
+
+    content_factory = None
+    """Factory used to create a new context if current context doesn't implements required interface.
+    If no factory is given, vocabulary is looking for default object factory for given interface.
+    """
+
+    def __init__(self, context=None):
+        request = check_request()
+        translate = request.localizer.translate
+        registry = request.registry
+        if not self.content_interface.providedBy(context):
+            factory = self.content_factory
+            if factory is None:
+                factory = get_object_factory(self.content_interface)
+            if factory is not None:
+                context = factory()
+        terms = [SimpleTerm(name, title=translate(adapter.label))
+                 for name, adapter in sorted(registry.getAdapters((context, request), IContentRenderer),
+                                             key=lambda x: x[1].weight)]
+        super(RenderersVocabulary, self).__init__(terms)
--- a/src/pyams_content/shared/imagemap/interfaces/__init__.py	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/shared/imagemap/interfaces/__init__.py	Fri May 25 11:28:17 2018 +0200
@@ -85,6 +85,7 @@
 
 
 IMAGEMAP_PARAGRAPH_TYPE = 'ImageMap'
+IMAGEMAP_PARAGRAPH_RENDERERS = 'PyAMS.paragraph.imagemap.renderers'
 
 
 class IImageMapParagraph(IBaseParagraph):
@@ -94,5 +95,10 @@
                                   description=_("Reference to image map object"),
                                   content_type=IMAGEMAP_CONTENT_TYPE)
 
+    renderer = Choice(title=_("Image map template"),
+                      description=_("Presentation template used for this paragraph"),
+                      vocabulary=IMAGEMAP_PARAGRAPH_RENDERERS,
+                      default='default')
+
     def get_target(self, state=None):
         """Get reference target"""
--- a/src/pyams_content/shared/imagemap/paragraph.py	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/shared/imagemap/paragraph.py	Fri May 25 11:28:17 2018 +0200
@@ -18,16 +18,20 @@
 # import interfaces
 from pyams_content.component.paragraph.interfaces import IParagraphFactory
 from pyams_content.features.checker.interfaces import IContentChecker, ERROR_VALUE, MISSING_VALUE, MISSING_LANG_VALUE
-from pyams_content.shared.imagemap.interfaces import IImageMapParagraph, IMAGEMAP_PARAGRAPH_TYPE
+from pyams_content.shared.imagemap.interfaces import IImageMapParagraph, IMAGEMAP_PARAGRAPH_TYPE, \
+    IMAGEMAP_PARAGRAPH_RENDERERS
 from pyams_i18n.interfaces import II18nManager, INegotiator, II18n
 from pyams_workflow.interfaces import IWorkflow, IWorkflowState
 
 # import packages
 from pyams_content.component.paragraph import BaseParagraph, BaseParagraphContentChecker, BaseParagraphFactory
+from pyams_content.features.renderer import RenderersVocabulary
 from pyams_sequence.utility import get_reference_target
 from pyams_utils.adapter import adapter_config
+from pyams_utils.factory import factory_config
 from pyams_utils.registry import utility_config, get_utility
 from pyams_utils.traversing import get_parent
+from pyams_utils.vocabulary import vocabulary_config
 from zope.interface import implementer
 from zope.schema.fieldproperty import FieldProperty
 
@@ -35,6 +39,7 @@
 
 
 @implementer(IImageMapParagraph)
+@factory_config(provided=IImageMapParagraph)
 class ImageMapParagraph(BaseParagraph):
     """Image map paragraph"""
 
@@ -42,6 +47,7 @@
     icon_hint = _("Image map")
 
     reference = FieldProperty(IImageMapParagraph['reference'])
+    renderer = FieldProperty(IImageMapParagraph['renderer'])
 
     def get_target(self, state=None):
         return get_reference_target(self.reference, state)
@@ -99,3 +105,10 @@
                             message=translate(_("image map '{0}' is not published")).format(
                                 II18n(target).query_attribute('title', request=request))))
         return output
+
+
+@vocabulary_config(name=IMAGEMAP_PARAGRAPH_RENDERERS)
+class ImageMapParagraphRendererVocabulary(RenderersVocabulary):
+    """Image map paragraph renderers vocabulary"""
+
+    content_interface = IImageMapParagraph
--- a/src/pyams_content/shared/imagemap/zmi/paragraph.py	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/shared/imagemap/zmi/paragraph.py	Fri May 25 11:28:17 2018 +0200
@@ -21,7 +21,7 @@
 from pyams_content.interfaces import MANAGE_CONTENT_PERMISSION
 from pyams_content.shared.common.interfaces import IWfSharedContent
 from pyams_content.shared.imagemap.interfaces import IImageMapParagraph, IMAGEMAP_PARAGRAPH_TYPE
-from pyams_form.interfaces.form import IInnerForm, IEditFormButtons, IWidgetsSuffixViewletsManager
+from pyams_form.interfaces.form import IInnerForm, IWidgetsSuffixViewletsManager
 from pyams_i18n.interfaces import II18n
 from pyams_skin.interfaces.viewlet import IToolbarAddingMenu
 from pyams_skin.layer import IPyAMSLayer
@@ -29,7 +29,8 @@
 
 # import packages
 from pyams_content.component.paragraph.zmi import BaseParagraphAJAXAddForm, BaseParagraphAJAXEditForm, \
-    BaseParagraphAddMenu, BaseParagraphPropertiesEditForm
+    BaseParagraphAddMenu, BaseParagraphPropertiesEditForm, IParagraphEditFormButtons
+from pyams_content.features.renderer.zmi.widget import RendererFieldWidget
 from pyams_content.shared.imagemap.paragraph import ImageMapParagraph
 from pyams_pagelet.pagelet import pagelet_config
 from pyams_skin.event import get_json_form_refresh_event
@@ -97,6 +98,8 @@
     icon_css_class = 'fa fa-fw fa-location-arrow'
 
     fields = field.Fields(IImageMapParagraph).omit('__parent__', '__name__', 'visible')
+    fields['renderer'].widgetFactory = RendererFieldWidget
+
     ajax_handler = 'properties.json'
     edit_permission = MANAGE_CONTENT_PERMISSION
 
@@ -118,7 +121,7 @@
     @property
     def buttons(self):
         if self.mode == INPUT_MODE:
-            return button.Buttons(IEditFormButtons)
+            return button.Buttons(IParagraphEditFormButtons)
         else:
             return button.Buttons()
 
--- a/src/pyams_content/shared/logo/interfaces/__init__.py	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/shared/logo/interfaces/__init__.py	Fri May 25 11:28:17 2018 +0200
@@ -17,7 +17,6 @@
 
 # import interfaces
 from pyams_content.component.paragraph import IBaseParagraph
-from pyams_content.features.renderer.interfaces import IRenderedContent
 from pyams_content.shared.common.interfaces import ISharedTool, IWfSharedContent, ISharedContent
 
 # import packages
@@ -65,7 +64,7 @@
 LOGOS_PARAGRAPH_RENDERERS = 'PyAMS.shared.logos.renderers'
 
 
-class ILogosParagraph(IRenderedContent, IBaseParagraph):
+class ILogosParagraph(IBaseParagraph):
     """Logos paragraph"""
 
     references = InternalReferencesList(title=_("Logos references"),
--- a/src/pyams_content/shared/logo/paragraph.py	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/shared/logo/paragraph.py	Fri May 25 11:28:17 2018 +0200
@@ -17,6 +17,7 @@
 
 # import interfaces
 from pyams_content.features.checker.interfaces import IContentChecker, MISSING_VALUE, MISSING_LANG_VALUE, ERROR_VALUE
+from pyams_content.features.renderer.interfaces import IContentRenderer
 from pyams_content.shared.logo.interfaces import ILogosParagraph, LOGOS_PARAGRAPH_TYPE, LOGOS_PARAGRAPH_RENDERERS
 from pyams_i18n.interfaces import II18nManager, INegotiator, II18n
 from pyams_workflow.interfaces import IWorkflow, IWorkflowState
@@ -24,7 +25,6 @@
 # import packages
 from pyams_content.component.paragraph import BaseParagraph, IParagraphFactory, BaseParagraphFactory, \
     BaseParagraphContentChecker
-from pyams_content.features.renderer import RenderedContentMixin, IContentRenderer
 from pyams_sequence.utility import get_reference_target
 from pyams_utils.adapter import adapter_config
 from pyams_utils.registry import utility_config, get_utility
@@ -39,7 +39,7 @@
 
 
 @implementer(ILogosParagraph)
-class LogosParagraph(RenderedContentMixin, BaseParagraph):
+class LogosParagraph(BaseParagraph):
     """Logos paragraph"""
 
     icon_class = 'fa-th-large'
--- a/src/pyams_content/shared/logo/zmi/paragraph.py	Wed May 23 15:30:41 2018 +0200
+++ b/src/pyams_content/shared/logo/zmi/paragraph.py	Fri May 25 11:28:17 2018 +0200
@@ -16,8 +16,7 @@
 # import standard library
 
 # import interfaces
-from pyams_content.component.paragraph.interfaces import IParagraphContainerTarget, IParagraphContainer, \
-    IParagraphRenderer
+from pyams_content.component.paragraph.interfaces import IParagraphContainerTarget, IParagraphContainer
 from pyams_content.component.paragraph.zmi.interfaces import IParagraphInnerEditor
 from pyams_content.interfaces import MANAGE_CONTENT_PERMISSION
 from pyams_content.shared.common import IWfSharedContent
@@ -31,7 +30,6 @@
 # import packages
 from pyams_content.component.paragraph.zmi import IParagraphContainerView, BaseParagraphAddMenu, \
     BaseParagraphAJAXAddForm, BaseParagraphPropertiesEditForm, BaseParagraphAJAXEditForm, IParagraphEditFormButtons
-from pyams_content.features.renderer.zmi import BaseRenderedContentRenderer
 from pyams_content.features.renderer.zmi.widget import RendererFieldWidget
 from pyams_content.shared.logo.paragraph import LogosParagraph
 from pyams_pagelet.pagelet import pagelet_config
@@ -140,12 +138,3 @@
                 get_json_widget_refresh_event(self.context, self.request,
                                               LogosParagraphInnerEditForm, 'renderer'))
         return output
-
-
-#
-# Logos paragraph renderer
-#
-
-@adapter_config(context=(ILogosParagraph, IPyAMSLayer), provides=IParagraphRenderer)
-class LogosParagraphRenderer(BaseRenderedContentRenderer):
-    """Logos paragraph renderer"""