src/pyams_content/component/association/zmi/__init__.py
changeset 140 67bad9f880ee
child 165 2912fbd45f62
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/component/association/zmi/__init__.py	Mon Sep 11 14:54:30 2017 +0200
@@ -0,0 +1,299 @@
+#
+# Copyright (c) 2008-2015 Thierry Florac <tflorac AT ulthar.net>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+
+__docformat__ = 'restructuredtext'
+
+
+# import standard library
+import json
+
+# import interfaces
+from pyams_content.component.association.interfaces import IAssociationTarget, IAssociationContainer, IAssociationInfo
+from pyams_content.component.association.zmi.interfaces import IAssociationsParentForm, IAssociationsView
+from pyams_content.interfaces import MANAGE_CONTENT_PERMISSION
+from pyams_form.interfaces.form import IInnerSubForm
+from pyams_skin.interfaces import IInnerPage
+from pyams_skin.layer import IPyAMSLayer
+from pyams_utils.interfaces import VIEW_SYSTEM_PERMISSION
+from pyams_zmi.interfaces.menu import IPropertiesMenu
+from z3c.table.interfaces import IValues, IColumn
+
+# import packages
+from pyams_form.form import AJAXAddForm, AJAXEditForm
+from pyams_form.security import ProtectedFormObjectMixin
+from pyams_pagelet.pagelet import pagelet_config, Pagelet
+from pyams_skin.table import BaseTable, SorterColumn, JsActionColumn, NameColumn, ImageColumn, I18nColumn, TrashColumn
+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.traversing import get_parent
+from pyams_utils.url import absolute_url
+from pyams_viewlet.viewlet import viewlet_config
+from pyams_zmi.form import InnerAdminDisplayForm
+from pyams_zmi.view import AdminView
+from pyramid.decorator import reify
+from pyramid.exceptions import NotFound
+from pyramid.view import view_config
+from z3c.form import field
+from z3c.table.column import GetAttrColumn
+from zope.interface import implementer, Interface
+
+from pyams_content import _
+
+
+#
+# Association item base forms
+#
+
+class AssociationItemAJAXAddForm(AJAXAddForm):
+    """Association item add form, JSON renderer"""
+
+    def get_ajax_output(self, changes):
+        associations_table = AssociationsTable(self.context, self.request, None)
+        associations_table.update()
+        return {'status': 'success',
+                'message': self.request.localizer.translate(_("Association was correctly added.")),
+                'callback': 'PyAMS_content.associations.afterUpdateCallback',
+                'options': {'parent': associations_table.id,
+                            'table': associations_table.render()}}
+
+
+class AssociationItemAJAXEditForm(AJAXEditForm):
+    """Association item properties edit form, JSON renderer"""
+
+    def get_associations_table(self):
+        target = get_parent(self.context, IAssociationTarget)
+        associations_table = AssociationsTable(target, self.request, None)
+        associations_table.update()
+        return {'status': 'success',
+                'message': self.request.localizer.translate(self.successMessage),
+                'callback': 'PyAMS_content.associations.afterUpdateCallback',
+                'options': {'parent': associations_table.id,
+                            'table': associations_table.render()}}
+
+
+#
+# Content associations view
+#
+
+@viewlet_config(name='associations.menu', context=IAssociationTarget, layer=IPyAMSLayer,
+                manager=IPropertiesMenu, permission=VIEW_SYSTEM_PERMISSION, weight=20)
+class AssociationsMenu(MenuItem):
+    """Associations menu"""
+
+    label = _("Associations...")
+    icon_class = 'fa-link'
+    url = '#associations.html'
+
+
+@pagelet_config(name='associations.html', context=IAssociationTarget, layer=IPyAMSLayer,
+                permission=VIEW_SYSTEM_PERMISSION)
+@template_config(template='templates/associations-view.pt', layer=IPyAMSLayer)
+@implementer(IInnerPage, IAssociationsView)
+class AssociationsContainerView(AdminView, Pagelet):
+    """Associations container view"""
+
+    title = _("Associations list")
+
+    def __init__(self, context, request):
+        super(AssociationsContainerView, self).__init__(context, request)
+        self.table = AssociationsTable(context, request, self)
+
+    def update(self):
+        super(AssociationsContainerView, self).update()
+        self.table.update()
+
+
+@adapter_config(name='associations', context=(IAssociationTarget, IPyAMSLayer, IAssociationsParentForm),
+                provides=IInnerSubForm)
+@template_config(template='templates/associations.pt', layer=IPyAMSLayer)
+@implementer(IAssociationsView)
+class AssociationsView(InnerAdminDisplayForm):
+    """Associations view"""
+
+    fields = field.Fields(Interface)
+    weight = 90
+
+    def __init__(self, context, request, view):
+        super(AssociationsView, self).__init__(context, request, view)
+        self.table = AssociationsTable(context, request, self)
+
+    def update(self):
+        super(AssociationsView, self).update()
+        self.table.update()
+
+
+class AssociationsTable(ProtectedFormObjectMixin, BaseTable):
+    """Associations view inner table"""
+
+    @property
+    def id(self):
+        return 'associations_{0}_list'.format(self.context.__name__)
+
+    hide_header = True
+    sortOn = None
+
+    def __init__(self, context, request, view):
+        super(AssociationsTable, self).__init__(context, request)
+        self.view = view
+
+    @property
+    def cssClasses(self):
+        classes = ['table', 'table-bordered', 'table-striped', 'table-hover', 'table-tight']
+        permission = self.permission
+        if (not permission) or self.request.has_permission(permission, self.context):
+            classes.append('table-dnd')
+        return {'table': ' '.join(classes)}
+
+    @property
+    def data_attributes(self):
+        attributes = super(AssociationsTable, self).data_attributes
+        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(IAssociationContainer(self.context), self.request),
+                               'data-ams-tablednd-drag-handle': 'td.sorter',
+                               'data-ams-tablednd-drop-target': 'set-associations-order.json'}
+        return attributes
+
+    @reify
+    def values(self):
+        return list(super(AssociationsTable, self).values)
+
+
+@adapter_config(context=(IAssociationTarget, IPyAMSLayer, AssociationsTable), provides=IValues)
+class AssociationsTableValuesAdapter(ContextRequestViewAdapter):
+    """Associations table values adapter"""
+
+    @property
+    def values(self):
+        return IAssociationContainer(self.context).values()
+
+
+@adapter_config(name='sorter', context=(IAssociationTarget, IPyAMSLayer, AssociationsTable), provides=IColumn)
+class AssociationsTableSorterColumn(ProtectedFormObjectMixin, SorterColumn):
+    """Associations table sorter column"""
+
+
+@view_config(name='set-associations-order.json', context=IAssociationContainer, request_type=IPyAMSLayer,
+             permission=MANAGE_CONTENT_PERMISSION, renderer='json', xhr=True)
+def set_associations_order(request):
+    """Update asociations order"""
+    order = list(map(str, json.loads(request.params.get('names'))))
+    request.context.updateOrder(order)
+    return {'status': 'success'}
+
+
+@adapter_config(name='show-hide', context=(IAssociationTarget, IPyAMSLayer, AssociationsTable),
+                provides=IColumn)
+class AssociationsTableShowHideColumn(ProtectedFormObjectMixin, JsActionColumn):
+    """Associations container visibility switcher column"""
+
+    cssClasses = {'th': 'action',
+                  'td': 'action switcher'}
+
+    icon_class = 'fa fa-fw fa-eye'
+    icon_hint = _("Switch association visibility")
+
+    url = 'PyAMS_content.associations.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(AssociationsTableShowHideColumn, self).renderCell(item)
+
+
+@view_config(name='set-association-visibility.json', context=IAssociationContainer, request_type=IPyAMSLayer,
+             permission=MANAGE_CONTENT_PERMISSION, renderer='json', xhr=True)
+def set_paragraph_visibility(request):
+    """Set paragraph visibility"""
+    container = IAssociationContainer(request.context)
+    association = container.get(str(request.params.get('object_name')))
+    if association is None:
+        raise NotFound()
+    association.visible = not association.visible
+    return {'visible': association.visible}
+
+
+@adapter_config(name='pictogram', context=(IAssociationTarget, IPyAMSLayer, AssociationsTable), provides=IColumn)
+class AssociationsTablePictogramColumn(ImageColumn):
+    """Associations table pictogram column"""
+
+    weight = 8
+
+    def get_icon_class(self, item):
+        info = IAssociationInfo(item, None)
+        if info is not None:
+            return info.pictogram
+
+    def get_icon_hint(self, item):
+        return self.request.localizer.translate(item.icon_hint)
+
+
+@adapter_config(name='name', context=(IAssociationTarget, IPyAMSLayer, AssociationsTable), provides=IColumn)
+class AssociationsTablePublicNameColumn(NameColumn):
+    """Associations table name column"""
+
+    _header = _("Public title")
+
+    def getValue(self, obj):
+        info = IAssociationInfo(obj, None)
+        if info is not None:
+            return info.user_title
+        else:
+            return '--'
+
+
+@adapter_config(name='inner_name', context=(IAssociationTarget, IPyAMSLayer, AssociationsTable), provides=IColumn)
+class AssociationsTableInnerNameColumn(I18nColumn, GetAttrColumn):
+    """Associations table inner name column"""
+
+    _header = _("Inner title")
+    weight = 20
+
+    def getValue(self, obj):
+        info = IAssociationInfo(obj, None)
+        if info is not None:
+            return info.inner_title
+        else:
+            return '--'
+
+
+@adapter_config(name='size', context=(IAssociationTarget, IPyAMSLayer, AssociationsTable), provides=IColumn)
+class AssociationsTableSizeColumn(I18nColumn, GetAttrColumn):
+    """Associations table size column"""
+
+    _header = _("Size")
+    weight = 30
+
+    def getValue(self, obj):
+        info = IAssociationInfo(obj, None)
+        if info is not None:
+            return info.human_size
+        else:
+            return '--'
+
+
+@adapter_config(name='trash', context=(IAssociationTarget, IPyAMSLayer, AssociationsTable), provides=IColumn)
+class AssociationsTableTrashColumn(ProtectedFormObjectMixin, TrashColumn):
+    """Associations table trash column"""