src/pyams_content/component/paragraph/zmi/container.py
changeset 7 cbc55162b64e
parent 0 7c0001cacf8e
child 22 c270ea8f041e
--- a/src/pyams_content/component/paragraph/zmi/container.py	Thu Oct 15 15:42:01 2015 +0200
+++ b/src/pyams_content/component/paragraph/zmi/container.py	Mon Jan 18 16:08:07 2016 +0100
@@ -9,6 +9,7 @@
 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
 # FOR A PARTICULAR PURPOSE.
 #
+from pyams_form.interfaces.form import IFormSecurityContext
 
 __docformat__ = 'restructuredtext'
 
@@ -18,10 +19,11 @@
 
 # import interfaces
 from pyams_content.interfaces import MANAGE_CONTENT_PERMISSION
-from pyams_content.component.extfile.interfaces import IExtFileLinksContainerTarget
-from pyams_content.component.gallery.interfaces import IGalleryLinksContainerTarget
-from pyams_content.component.links.interfaces import ILinkLinksContainerTarget
-from pyams_content.component.paragraph.interfaces import IParagraphContainerTarget, IParagraphContainer
+from pyams_content.component.extfile.interfaces import IExtFileLinksContainerTarget, IExtFileLinksContainer
+from pyams_content.component.gallery.interfaces import IGalleryLinksContainerTarget, IGalleryLinksContainer
+from pyams_content.component.links.interfaces import ILinkLinksContainerTarget, ILinkLinksContainer
+from pyams_content.component.paragraph.interfaces import IParagraphContainerTarget, IParagraphContainer, IBaseParagraph
+from pyams_content.component.paragraph.zmi.interfaces import IParagraphInnerEditor
 from pyams_i18n.interfaces import II18n
 from pyams_skin.interfaces import IInnerPage, IPageHeader
 from pyams_skin.layer import IPyAMSLayer
@@ -35,11 +37,12 @@
 from pyams_form.security import ProtectedFormObjectMixin
 from pyams_pagelet.pagelet import pagelet_config
 from pyams_skin.page import DefaultPageHeaderAdapter
-from pyams_skin.table import BaseTable, I18nColumn, TrashColumn, ActionColumn
+from pyams_skin.table import BaseTable, I18nColumn, TrashColumn, ActionColumn, JsActionColumn
 from pyams_skin.viewlet.menu import MenuItem
 from pyams_template.template import template_config
-from pyams_utils.adapter import adapter_config, ContextRequestViewAdapter
+from pyams_utils.adapter import adapter_config, ContextRequestViewAdapter, ContextAdapter
 from pyams_utils.url import absolute_url
+from pyramid.exceptions import NotFound
 from pyramid.view import view_config
 from pyams_viewlet.viewlet import viewlet_config
 from pyams_zmi.view import AdminView
@@ -64,7 +67,8 @@
 # Paragraphs container view
 #
 
-@pagelet_config(name='paragraphs.html', context=IParagraphContainerTarget, layer=IPyAMSLayer, permission=VIEW_SYSTEM_PERMISSION)
+@pagelet_config(name='paragraphs.html', context=IParagraphContainerTarget, layer=IPyAMSLayer,
+                permission=VIEW_SYSTEM_PERMISSION)
 @template_config(template='templates/container.pt', layer=IPyAMSLayer)
 @implementer(IInnerPage)
 class ParagraphContainerView(AdminView):
@@ -103,10 +107,12 @@
     @property
     def data_attributes(self):
         attributes = super(ParagraphContainerTable, self).data_attributes
-        del attributes['tr']['data-ams-url']
-        del attributes['tr']['data-toggle']
         attributes['table'] = {'id': self.id,
+                               'data-ams-plugins': 'pyams_content',
+                               'data-ams-plugin-pyams_content-src':
+                                   '/--static--/pyams_content/js/pyams_content{MyAMS.devext}.js',
                                'data-ams-location': absolute_url(IParagraphContainer(self.context), self.request),
+                               'data-ams-tablednd-drag-handle': 'td.sorter',
                                'data-ams-tablednd-drop-target': 'set-paragraphs-order.json'}
         return attributes
 
@@ -121,19 +127,58 @@
         return super(ParagraphContainerTable, self).render()
 
 
-@adapter_config(name='properties', context=(IParagraphContainerTarget, IPyAMSLayer, ParagraphContainerTable),
+@adapter_config(name='sorter', context=(IParagraphContainerTarget, IPyAMSLayer, ParagraphContainerTable),
                 provides=IColumn)
-class ParagraphContainerPropertiesColumn(ActionColumn):
-    """Paragraphs container properties column"""
+class ParagraphContainerSorterColumn(ProtectedFormObjectMixin, ActionColumn):
+    """Paragraphs container sorter column"""
+
+    cssClasses = {'th': 'action',
+                  'td': 'action sorter'}
+
+    icon_class = 'fa fa-fw fa-sort'
+    icon_hint = _("Click and drag to sort paragraphs...")
+
+    url = '#'
+    weight = 1
 
-    icon_class = 'fa fa-fw fa-edit'
-    icon_hint = _("Paragraph properties")
+    def get_url(self, item):
+        return '#'
+
 
-    url = 'properties.html'
-    modal_target = True
+@adapter_config(name='show-hide', context=(IParagraphContainerTarget, IPyAMSLayer, ParagraphContainerTable),
+                provides=IColumn)
+class ParagraphContainerShowHideColumn(ProtectedFormObjectMixin, JsActionColumn):
+    """Paragraphs container visibility switcher column"""
+
+    cssClasses = {'th': 'action',
+                  'td': 'action switcher'}
+
+    icon_class = 'fa fa-fw fa-eye'
+    icon_hint = _("Switch paragraph visibility")
+
+    url = 'PyAMS_content.paragraphs.switchVisibility'
 
     weight = 5
 
+    def get_icon(self, item):
+        if item.visible:
+            icon_class = 'fa fa-fw fa-eye'
+        else:
+            icon_class = 'fa fa-fw fa-eye-slash text-danger'
+        return '<i class="{icon_class}"></i>'.format(icon_class=icon_class)
+
+    def renderCell(self, item):
+        if self.permission and not self.request.has_permission(self.permission, context=item):
+            return self.get_icon(item)
+        else:
+            return super(ParagraphContainerShowHideColumn, self).renderCell(item)
+
+
+@adapter_config(context=ParagraphContainerShowHideColumn, provides=IFormSecurityContext)
+def ShowHideColumnSecurityContextFactory(column):
+    """Show/hide column security context factory"""
+    return column.table.context
+
 
 @adapter_config(name='files', context=(IParagraphContainerTarget, IPyAMSLayer, ParagraphContainerTable),
                 provides=IColumn)
@@ -143,6 +188,8 @@
     icon_class = 'fa fa-fw fa-file-text-o'
     icon_hint = _("External files")
 
+    cssClasses = {'td': 'action extfiles nowrap'}
+
     url = 'extfile-links.html'
     modal_target = True
 
@@ -151,7 +198,14 @@
     def renderCell(self, item):
         if not IExtFileLinksContainerTarget.providedBy(item):
             return ''
-        return super(ParagraphContainerExtFileLinksColumn, self).renderCell(item)
+        action = '{action} <span class="count">{{count}}</span>'.format(
+            action=super(ParagraphContainerExtFileLinksColumn, self).renderCell(item))
+        length = len(IExtFileLinksContainer(item).files or ())
+        if length:
+            action = action.format(count='({0})'.format(length))
+        else:
+            action = action.format(count='')
+        return action
 
 
 @adapter_config(name='links', context=(IParagraphContainerTarget, IPyAMSLayer, ParagraphContainerTable),
@@ -162,6 +216,8 @@
     icon_class = 'fa fa-fw fa-link'
     icon_hint = _("Useful links")
 
+    cssClasses = {'td': 'action links nowrap'}
+
     url = 'link-links.html'
     modal_target = True
 
@@ -170,7 +226,14 @@
     def renderCell(self, item):
         if not ILinkLinksContainerTarget.providedBy(item):
             return ''
-        return super(ParagraphContainerLinkLinksColumn, self).renderCell(item)
+        action = '{action} <span class="count">{{count}}</span>'.format(
+            action=super(ParagraphContainerLinkLinksColumn, self).renderCell(item))
+        length = len(ILinkLinksContainer(item).links or ())
+        if length:
+            action = action.format(count='({0})'.format(length))
+        else:
+            action = action.format(count='')
+        return action
 
 
 @adapter_config(name='gallery', context=(IParagraphContainerTarget, IPyAMSLayer, ParagraphContainerTable),
@@ -181,6 +244,8 @@
     icon_class = 'fa fa-fw fa-picture-o'
     icon_hint = _("Images galleries")
 
+    cssClasses = {'td': 'action galleries nowrap'}
+
     url = 'gallery-links.html'
     modal_target = True
 
@@ -189,7 +254,14 @@
     def renderCell(self, item):
         if not IGalleryLinksContainerTarget.providedBy(item):
             return ''
-        return super(ParagraphContainerGalleryLinksColumn, self).renderCell(item)
+        action = '{action} <span class="count">{{count}}</span>'.format(
+            action=super(ParagraphContainerGalleryLinksColumn, self).renderCell(item))
+        length = len(IGalleryLinksContainer(item).galleries or ())
+        if length:
+            action = action.format(count='({0})'.format(length))
+        else:
+            action = action.format(count='')
+        return action
 
 
 @adapter_config(name='name', context=(IParagraphContainerTarget, IPyAMSLayer, ParagraphContainerTable),
@@ -201,6 +273,25 @@
 
     weight = 50
 
+    def renderHeadCell(self):
+        return '<span class="small hint" title="{title}" data-ams-hint-gravity="e"' \
+               '      data-ams-stop-propagation="true"' \
+               '      data-ams-click-handler="PyAMS_content.paragraphs.switchAllEditors">' \
+               '    <i class="fa fa-plus-square-o"></i>' \
+               '</span> '.format(
+                    title=self.request.localizer.translate(_("Click to open/close all paragraphs editors"))) + \
+               super(ParagraphContainerTitleColumn, self).renderHeadCell()
+
+    def renderCell(self, item):
+        return '<div><span class="small hint" title="{title}" data-ams-hint-gravity="e"' \
+               '      data-ams-stop-propagation="true" ' \
+               '      data-ams-click-handler="PyAMS_content.paragraphs.switchEditor">' \
+               '    <i class="fa fa-plus-square-o"></i>' \
+               '</span> '.format(
+                    title=self.request.localizer.translate(_("Click to open/close paragraph editor"))) + \
+               '<span class="title">{0}</span>'.format(super(ParagraphContainerTitleColumn, self).renderCell(item)) + \
+               '</div><div class="inner-table-form editor margin-x-10 margin-bottom-0"></div>'
+
     def getValue(self, obj):
         return II18n(obj).query_attribute('title', request=self.request) or '--'
 
@@ -237,3 +328,50 @@
     order = list(map(str, json.loads(request.params.get('names'))))
     container.updateOrder(order)
     return {'status': 'success'}
+
+
+@view_config(name='set-paragraph-visibility.json', context=IParagraphContainer, request_type=IPyAMSLayer,
+             permission=MANAGE_CONTENT_PERMISSION, renderer='json', xhr=True)
+def set_paragraph_visibility(request):
+    """Set paragraph visibility"""
+    container = IParagraphContainer(request.context)
+    paragraph = container.get(str(request.params.get('object_name')))
+    if paragraph is None:
+        raise NotFound()
+    paragraph = IBaseParagraph(paragraph)
+    paragraph.visible = not paragraph.visible
+    return {'visible': paragraph.visible}
+
+
+@view_config(name='get-paragraph-editor.json', context=IParagraphContainer, request_type=IPyAMSLayer,
+             permission=VIEW_SYSTEM_PERMISSION, renderer='json', xhr=True)
+def get_paragraph_editor(request):
+    """Get paragraph editor"""
+    container = IParagraphContainer(request.context)
+    paragraph = container.get(str(request.params.get('object_name')))
+    if paragraph is None:
+        raise NotFound()
+    registry = request.registry
+    editor = registry.queryMultiAdapter((paragraph, request), IParagraphInnerEditor)
+    if editor is None:
+        editor = registry.queryAdapter(paragraph, IParagraphInnerEditor)
+    if editor is not None:
+        editor.update()
+        return editor.render()
+
+
+@view_config(name='get-paragraphs-editors.json', context=IParagraphContainer, request_type=IPyAMSLayer,
+             permission=VIEW_SYSTEM_PERMISSION, renderer='json', xhr=True)
+def get_paragraphs_editors(request):
+    """Get all paragraphs inner editors"""
+    container = IParagraphContainer(request.context)
+    registry = request.registry
+    result = {}
+    for key, paragraph in container.items():
+        editor = registry.queryMultiAdapter((paragraph, request), IParagraphInnerEditor)
+        if editor is None:
+            editor = registry.queryAdapter(paragraph, IParagraphInnerEditor)
+        if editor is not None:
+            editor.update()
+            result[key] = editor.render()
+    return result