Added raw HTML paragraph
authorThierry Florac <thierry.florac@onf.fr>
Tue, 06 Feb 2018 11:16:23 +0100
changeset 355 5dce53509832
parent 354 871c7cb35fd6
child 356 9922023810d3
Added raw HTML paragraph
src/pyams_content/component/paragraph/__init__.py
src/pyams_content/component/paragraph/header.py
src/pyams_content/component/paragraph/html.py
src/pyams_content/component/paragraph/interfaces/__init__.py
src/pyams_content/component/paragraph/interfaces/html.py
src/pyams_content/component/paragraph/video.py
src/pyams_content/component/paragraph/zmi/html.py
src/pyams_content/component/paragraph/zmi/templates/raw-summary.pt
--- a/src/pyams_content/component/paragraph/__init__.py	Tue Feb 06 11:15:55 2018 +0100
+++ b/src/pyams_content/component/paragraph/__init__.py	Tue Feb 06 11:16:23 2018 +0100
@@ -47,7 +47,12 @@
 
 @subscriber(IObjectAddedEvent, context_selector=IParagraphContainerTarget)
 def handle_new_paragraphs_container(event):
-    """Handle new paragraphs container"""
+    """Handle new paragraphs container
+
+    Subscriber to IObjectAddedEvent for objects implementing IParagraphContainerTarget: when a
+    new paragraphs container is created (in version 1), automatically create new paragraphs matching
+
+    """
     container = IParagraphContainer(event.object)
     if len(container) > 0:
         return
@@ -78,6 +83,15 @@
     title = FieldProperty(IBaseParagraph['title'])
 
 
+@implementer(IParagraphFactory)
+class BaseParagraphFactory(object):
+    """Base paragraph factory class"""
+
+    name = None
+    content_type = None
+    custom_menu = False
+
+
 class BaseParagraphContentChecker(BaseContentChecker):
     """Base paragraph content checker mixin"""
 
--- a/src/pyams_content/component/paragraph/header.py	Tue Feb 06 11:15:55 2018 +0100
+++ b/src/pyams_content/component/paragraph/header.py	Tue Feb 06 11:16:23 2018 +0100
@@ -22,7 +22,7 @@
 from pyams_i18n.interfaces import II18n, II18nManager, INegotiator
 
 # import packages
-from pyams_content.component.paragraph import BaseParagraph, BaseParagraphContentChecker
+from pyams_content.component.paragraph import BaseParagraph, BaseParagraphContentChecker, BaseParagraphFactory
 from pyams_utils.adapter import adapter_config
 from pyams_utils.registry import utility_config, get_utility
 from pyams_utils.text import get_text_start
@@ -49,7 +49,7 @@
 
 
 @utility_config(name='Header', provides=IParagraphFactory)
-class HTMLParagraphFactory(object):
+class HTMLParagraphFactory(BaseParagraphFactory):
     """HTML paragraph factory"""
 
     name = _("Header paragraph")
--- a/src/pyams_content/component/paragraph/html.py	Tue Feb 06 11:15:55 2018 +0100
+++ b/src/pyams_content/component/paragraph/html.py	Tue Feb 06 11:16:23 2018 +0100
@@ -22,14 +22,14 @@
 from pyams_content.component.illustration.interfaces import IIllustrationTarget
 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 IHTMLParagraph
+from pyams_content.component.paragraph.interfaces.html import IRawParagraph, IHTMLParagraph
 from pyams_content.features.checker.interfaces import IContentChecker, MISSING_VALUE, MISSING_LANG_VALUE
 from pyams_i18n.interfaces import II18n, II18nManager, INegotiator
 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
+from pyams_content.component.paragraph import BaseParagraph, BaseParagraphContentChecker, BaseParagraphFactory
 from pyams_utils.adapter import adapter_config
 from pyams_utils.registry import utility_config, get_utility
 from pyams_utils.request import check_request
@@ -46,6 +46,54 @@
 
 
 #
+# Raw HTML paragraph
+#
+
+@implementer(IRawParagraph)
+class RawParagraph(BaseParagraph):
+    """Raw HTML paragraph"""
+
+    icon_class = 'fa-code'
+    icon_hint = _("Raw HTML ")
+
+    body = FieldProperty(IRawParagraph['body'])
+
+
+@utility_config(name='raw', provides=IParagraphFactory)
+class RawParagraphFactory(BaseParagraphFactory):
+    """Raw paragraph factory"""
+
+    name = _("Raw HTML paragraph")
+    content_type = RawParagraph
+    custom_menu = True
+
+
+@adapter_config(context=IRawParagraph, provides=IContentChecker)
+class RawParagraphContentChecker(BaseParagraphContentChecker):
+    """Raw HTML paragraph content checker"""
+
+    def inner_check(self, request):
+        output = []
+        translate = request.localizer.translate
+        manager = get_parent(self.context, II18nManager)
+        if manager is not None:
+            langs = manager.get_languages()
+        else:
+            negotiator = get_utility(INegotiator)
+            langs = (negotiator.server_language, )
+        i18n = II18n(self.context)
+        for lang in langs:
+            value = i18n.get_attribute('body', lang, request)
+            if not value:
+                field_title = translate(IRawParagraph['body'].title)
+                if len(langs) == 1:
+                    output.append(translate(MISSING_VALUE).format(field=field_title))
+                else:
+                    output.append(translate(MISSING_LANG_VALUE).format(field=field_title, lang=lang))
+        return output
+
+
+#
 # HTML paragraph
 #
 
@@ -54,16 +102,16 @@
     """HTML paragraph"""
 
     icon_class = 'fa-html5'
-    icon_hint = _("HTML paragraph")
+    icon_hint = _("Rich text")
 
     body = FieldProperty(IHTMLParagraph['body'])
 
 
 @utility_config(name='HTML', provides=IParagraphFactory)
-class HTMLParagraphFactory(object):
+class HTMLParagraphFactory(BaseParagraphFactory):
     """HTML paragraph factory"""
 
-    name = _("HTML paragraph")
+    name = _("Rich text paragraph")
     content_type = HTMLParagraph
 
 
--- a/src/pyams_content/component/paragraph/interfaces/__init__.py	Tue Feb 06 11:15:55 2018 +0100
+++ b/src/pyams_content/component/paragraph/interfaces/__init__.py	Tue Feb 06 11:16:23 2018 +0100
@@ -67,6 +67,7 @@
 
     name = Attribute("Factory name")
     content_type = Attribute("Factory content type")
+    custom_menu = Attribute("Display factory in 'custom' paragraphs menu")
 
 
 class IParagraphFactorySettings(Interface):
--- a/src/pyams_content/component/paragraph/interfaces/html.py	Tue Feb 06 11:15:55 2018 +0100
+++ b/src/pyams_content/component/paragraph/interfaces/html.py	Tue Feb 06 11:16:23 2018 +0100
@@ -19,17 +19,30 @@
 from pyams_content.component.paragraph.interfaces import IBaseParagraph
 
 # import packages
-from pyams_i18n.schema import I18nHTMLField
+from pyams_i18n.schema import I18nHTMLField, I18nTextField
 
 from pyams_content import _
 
 
 #
+# Raw HTML paragraph
+#
+
+class IRawParagraph(IBaseParagraph):
+    """Raw HTML paragraph interface"""
+
+    body = I18nTextField(title=_("Raw HTML code"),
+                         description=_("This HTML code will be used 'as is', without any transformation. Use with "
+                                       "care!!"),
+                         required=False)
+
+
+#
 # HTML paragraph
 #
 
 class IHTMLParagraph(IBaseParagraph):
-    """HTML body paragraph"""
+    """Rich text paragraph interface"""
 
     body = I18nHTMLField(title=_("Body"),
                          required=False)
--- a/src/pyams_content/component/paragraph/video.py	Tue Feb 06 11:15:55 2018 +0100
+++ b/src/pyams_content/component/paragraph/video.py	Tue Feb 06 11:16:23 2018 +0100
@@ -25,7 +25,7 @@
 from zope.lifecycleevent.interfaces import IObjectAddedEvent, IObjectModifiedEvent
 
 # import packages
-from pyams_content.component.paragraph import BaseParagraph, BaseParagraphContentChecker
+from pyams_content.component.paragraph import BaseParagraph, BaseParagraphContentChecker, BaseParagraphFactory
 from pyams_content.component.paragraph.html import check_associations
 from pyams_file.property import FileProperty
 from pyams_utils.adapter import adapter_config
@@ -52,7 +52,7 @@
 
 
 @utility_config(name='Video', provides=IParagraphFactory)
-class VideoParagraphFactory(object):
+class VideoParagraphFactory(BaseParagraphFactory):
     """Video paragraph factory"""
 
     name = _("Video")
--- a/src/pyams_content/component/paragraph/zmi/html.py	Tue Feb 06 11:15:55 2018 +0100
+++ b/src/pyams_content/component/paragraph/zmi/html.py	Tue Feb 06 11:16:23 2018 +0100
@@ -21,10 +21,9 @@
 from pyams_content.component.illustration.interfaces import IIllustration, IIllustrationRenderer
 from pyams_content.component.paragraph.interfaces import IParagraphContainerTarget, IParagraphContainer, \
     IParagraphSummary
-from pyams_content.component.paragraph.interfaces.html import IHTMLParagraph
+from pyams_content.component.paragraph.interfaces.html import IHTMLParagraph, IRawParagraph
 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
 from pyams_form.interfaces.form import IInnerForm, IEditFormButtons
 from pyams_i18n.interfaces import II18n
 from pyams_skin.interfaces.viewlet import IToolbarAddingMenu
@@ -34,14 +33,13 @@
 
 # import packages
 from pyams_content.component.association.zmi import AssociationsTable
-from pyams_content.component.paragraph.html import HTMLParagraph
+from pyams_content.component.paragraph.html import HTMLParagraph, RawParagraph
 from pyams_content.component.paragraph.zmi import BaseParagraphAJAXAddForm, BaseParagraphAJAXEditForm, \
     BaseParagraphAddMenu, BaseParagraphPropertiesEditForm
 from pyams_content.component.paragraph.zmi.container import ParagraphContainerTable, \
     ParagraphTitleToolbarViewletManager
 from pyams_pagelet.pagelet import pagelet_config
 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
@@ -55,15 +53,130 @@
 
 
 #
-# HTML paragraph
+# Raw HTML paragraph
+#
+
+@viewlet_config(name='add-raw-paragraph.menu', context=IParagraphContainerTarget, view=IParagraphContainerView,
+                layer=IPyAMSLayer, manager=IToolbarAddingMenu, weight=999)
+class RawParagraphAddMenu(BaseParagraphAddMenu):
+    """Raw HTML paragraph add menu"""
+
+    label = _("Add raw HTML paragraph...")
+    label_css_class = 'fa fa-fw fa-code'
+    url = 'add-raw-paragraph.html'
+    paragraph_type = 'raw'
+
+
+@pagelet_config(name='add-raw-paragraph.html', context=IParagraphContainerTarget, layer=IPyAMSLayer,
+                permission=MANAGE_CONTENT_PERMISSION)
+class RawParagraphAddForm(AdminDialogAddForm):
+    """Raw HTML paragraph add form"""
+
+    legend = _("Add new raw HTML paragraph")
+    dialog_class = 'modal-large'
+    icon_css_class = 'fa fa-fw fa-code'
+    label_css_class = 'control-label col-md-2'
+    input_css_class = 'col-md-10'
+
+    fields = field.Fields(IRawParagraph).omit('__parent__', '__name__', 'visible')
+    ajax_handler = 'add-raw-paragraph.json'
+    edit_permission = MANAGE_CONTENT_PERMISSION
+
+    def updateWidgets(self, prefix=None):
+        super(RawParagraphAddForm, self).updateWidgets(prefix)
+        if 'body' in self.widgets:
+            self.widgets['body'].widget_css_class = 'textarea height-100'
+
+    def create(self, data):
+        return RawParagraph()
+
+    def add(self, object):
+        IParagraphContainer(self.context).append(object)
+
+
+@view_config(name='add-raw-paragraph.json', context=IParagraphContainerTarget, request_type=IPyAMSLayer,
+             permission=MANAGE_CONTENT_PERMISSION, renderer='json', xhr=True)
+class RawParagraphAJAXAddForm(BaseParagraphAJAXAddForm, RawParagraphAddForm):
+    """Raw HTML paragraph add form, JSON renderer"""
+
+
+@pagelet_config(name='properties.html', context=IRawParagraph, layer=IPyAMSLayer,
+                permission=MANAGE_CONTENT_PERMISSION)
+class RawParagraphPropertiesEditForm(BaseParagraphPropertiesEditForm):
+    """Raw HTML paragraph properties edit form"""
+
+    legend = _("Edit raw HTML paragraph properties")
+    icon_css_class = 'fa fa-fw fa-code'
+
+    fields = field.Fields(IRawParagraph).omit('__parent__', '__name__', 'visible')
+    ajax_handler = 'properties.json'
+    edit_permission = MANAGE_CONTENT_PERMISSION
+
+    def updateWidgets(self, prefix=None):
+        super(RawParagraphPropertiesEditForm, self).updateWidgets(prefix)
+        if 'body' in self.widgets:
+            self.widgets['body'].widget_css_class = 'textarea height-100'
+
+
+@view_config(name='properties.json', context=IRawParagraph, request_type=IPyAMSLayer,
+             permission=MANAGE_CONTENT_PERMISSION, renderer='json', xhr=True)
+class RawParagraphPropertiesAJAXEditForm(BaseParagraphAJAXEditForm, RawParagraphPropertiesEditForm):
+    """Raw HTML paragraph properties edit form, JSON renderer"""
+
+
+@adapter_config(context=(IRawParagraph, IPyAMSLayer), provides=IParagraphInnerEditor)
+@implementer(IInnerForm)
+class RawParagraphInnerEditForm(RawParagraphPropertiesEditForm):
+    """Raw HTML paragraph inner edit form"""
+
+    legend = None
+    ajax_handler = 'inner-properties.json'
+
+    @property
+    def buttons(self):
+        if self.mode == INPUT_MODE:
+            return button.Buttons(IEditFormButtons)
+        else:
+            return button.Buttons()
+
+
+@view_config(name='inner-properties.json', context=IRawParagraph, request_type=IPyAMSLayer,
+             permission=MANAGE_CONTENT_PERMISSION, renderer='json', xhr=True)
+class RawParagraphInnerAJAXEditForm(BaseParagraphAJAXEditForm, RawParagraphInnerEditForm):
+    """Raw HTML paragraph inner edit form, JSON renderer"""
+
+
+#
+# Raw HTML paragraph summary
+#
+
+@adapter_config(context=(IRawParagraph, IPyAMSLayer), provides=IParagraphSummary)
+@template_config(template='templates/raw-summary.pt', layer=IPyAMSLayer)
+class RawParagraphSummary(BaseContentProvider):
+    """Raw HTML paragraph summary"""
+
+    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
 #
 
 @viewlet_config(name='add-html-paragraph.menu', context=IParagraphContainerTarget, view=IParagraphContainerView,
                 layer=IPyAMSLayer, manager=IToolbarAddingMenu, weight=50)
 class HTMLParagraphAddMenu(BaseParagraphAddMenu):
-    """HTML paragraph add menu"""
+    """Rich text paragraph add menu"""
 
-    label = _("Add HTML paragraph...")
+    label = _("Add rich text paragraph...")
     label_css_class = 'fa fa-fw fa-html5'
     url = 'add-html-paragraph.html'
     paragraph_type = 'HTML'
@@ -72,9 +185,9 @@
 @pagelet_config(name='add-html-paragraph.html', context=IParagraphContainerTarget, layer=IPyAMSLayer,
                 permission=MANAGE_CONTENT_PERMISSION)
 class HTMLParagraphAddForm(AdminDialogAddForm):
-    """HTML paragraph add form"""
+    """Rich text paragraph add form"""
 
-    legend = _("Add new HTML paragraph")
+    legend = _("Add new rich text paragraph")
     dialog_class = 'modal-max'
     icon_css_class = 'fa fa-fw fa-html5'
     label_css_class = 'control-label col-md-2'
@@ -99,15 +212,15 @@
 @view_config(name='add-html-paragraph.json', context=IParagraphContainerTarget, request_type=IPyAMSLayer,
              permission=MANAGE_CONTENT_PERMISSION, renderer='json', xhr=True)
 class HTMLParagraphAJAXAddForm(BaseParagraphAJAXAddForm, HTMLParagraphAddForm):
-    """HTML paragraph add form, JSON renderer"""
+    """Rich text paragraph add form, JSON renderer"""
 
 
 @pagelet_config(name='properties.html', context=IHTMLParagraph, layer=IPyAMSLayer,
                 permission=MANAGE_CONTENT_PERMISSION)
 class HTMLParagraphPropertiesEditForm(BaseParagraphPropertiesEditForm):
-    """HTML paragraph properties edit form"""
+    """Rich text paragraph properties edit form"""
 
-    legend = _("Edit HTML paragraph properties")
+    legend = _("Edit rich text paragraph properties")
     dialog_class = 'modal-max'
     icon_css_class = 'fa fa-fw fa-html5'
     label_css_class = 'control-label col-md-2'
@@ -128,13 +241,13 @@
 
 
 class IHTMLParagraphInnerEditForm(Interface):
-    """Marker interface for HTML paragraph inner form"""
+    """Marker interface for rich text paragraph inner form"""
 
 
 @view_config(name='properties.json', context=IHTMLParagraph, request_type=IPyAMSLayer,
              permission=MANAGE_CONTENT_PERMISSION, renderer='json', xhr=True)
 class HTMLParagraphPropertiesAJAXEditForm(BaseParagraphAJAXEditForm, HTMLParagraphPropertiesEditForm):
-    """HTML paragraph properties edit form, JSON renderer"""
+    """Rich text paragraph properties edit form, JSON renderer"""
 
     def get_ajax_output(self, changes):
         output = super(HTMLParagraphPropertiesAJAXEditForm, self).get_ajax_output(changes)
@@ -169,7 +282,7 @@
 @adapter_config(context=(IHTMLParagraph, IPyAMSLayer), provides=IParagraphInnerEditor)
 @implementer(IInnerForm, IPropertiesEditForm, IAssociationsParentForm, IHTMLParagraphInnerEditForm)
 class HTMLParagraphInnerEditForm(HTMLParagraphPropertiesEditForm):
-    """HTML paragraph inner edit form"""
+    """Rich text paragraph inner edit form"""
 
     legend = None
     ajax_handler = 'inner-properties.json'
@@ -185,7 +298,7 @@
 @view_config(name='inner-properties.json', context=IHTMLParagraph, request_type=IPyAMSLayer,
              permission=MANAGE_CONTENT_PERMISSION, renderer='json', xhr=True)
 class HTMLParagraphInnerAJAXEditForm(BaseParagraphAJAXEditForm, HTMLParagraphInnerEditForm):
-    """HTML paragraph inner edit form, JSON renderer"""
+    """Rich text paragraph inner edit form, JSON renderer"""
 
     def get_ajax_output(self, changes):
         output = super(HTMLParagraphInnerAJAXEditForm, self).get_ajax_output(changes)
@@ -218,13 +331,13 @@
 
 
 #
-# HTML paragraph summary
+# rich text paragraph summary
 #
 
 @adapter_config(context=(IHTMLParagraph, IPyAMSLayer), provides=IParagraphSummary)
 @template_config(template='templates/html-summary.pt', layer=IPyAMSLayer)
 class HTMLParagraphSummary(BaseContentProvider):
-    """HTML paragraph summary"""
+    """Rich text paragraph summary"""
 
     illustration = None
     illustration_renderer = None
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/component/paragraph/zmi/templates/raw-summary.pt	Tue Feb 06 11:16:23 2018 +0100
@@ -0,0 +1,2 @@
+<h3 tal:content="view.title">title</h3>
+<div tal:content="structure view.body">body</div>