--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_content/features/menu/zmi/__init__.py Wed Jun 13 11:30:18 2018 +0200
@@ -0,0 +1,495 @@
+#
+# Copyright (c) 2008-2018 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.links.zmi import InternalLinkAddMenu, InternalLinkAddForm, InternalLinkPropertiesEditForm, \
+ ExternalLinkAddMenu, ExternalLinkAddForm, ExternalLinkPropertiesEditForm
+from pyams_content.features.menu import IMenusContainer, IMenu, Menu, IMenuLink
+from pyams_content.features.menu.interfaces import IMenusContainerTarget, IMenuLinksContainer, IMenuInternalLink, \
+ IMenuExternalLink, IMenuLinksContainerTarget
+from pyams_form.interfaces.form import IFormContextPermissionChecker
+from pyams_portal.interfaces import MANAGE_TEMPLATE_PERMISSION
+from pyams_skin.interfaces.viewlet import IWidgetTitleViewletManager, IToolbarAddingMenu
+from pyams_skin.layer import IPyAMSLayer
+from pyams_utils.interfaces import VIEW_SYSTEM_PERMISSION
+from pyams_viewlet.interfaces import IViewletManager
+from z3c.table.interfaces import IValues, IColumn
+
+# import packages
+from pyams_content.component.association.zmi import AssociationsTable, AssociationsTablePublicNameColumn
+from pyams_form.form import ajax_config, AJAXAddForm, AJAXEditForm
+from pyams_i18n.column import I18nAttrColumn
+from pyams_pagelet.pagelet import pagelet_config
+from pyams_skin.container import switch_element_visibility, delete_container_element
+from pyams_skin.event import get_json_switched_table_refresh_event, get_json_table_row_refresh_event
+from pyams_skin.table import BaseTable, SorterColumn, VisibilitySwitcherColumn, I18nColumn, TrashColumn, NameColumn, \
+ get_table_id
+from pyams_skin.viewlet.toolbar import ToolbarAction
+from pyams_utils.adapter import adapter_config, ContextRequestViewAdapter, ContextAdapter
+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 AdminDialogAddForm, AdminDialogEditForm
+from pyams_zmi.zmi.table import InnerTableView
+from pyramid.decorator import reify
+from pyramid.exceptions import NotFound
+from pyramid.view import view_config
+from z3c.form import field
+from zope.interface import implementer, alsoProvides, Interface
+
+from pyams_content import _
+
+
+#
+# Custom marker interfaces
+#
+
+class IMenuLinksView(Interface):
+ """Menu links view marker interface"""
+
+
+class IMenusView(Interface):
+ """Menus view marker interface"""
+
+
+#
+# Menus add and edit forms
+#
+
+@viewlet_config(name='add-menu.action', context=IMenusContainerTarget, layer=IPyAMSLayer,
+ view=IMenusView, manager=IWidgetTitleViewletManager, weight=10)
+class MenuAddAction(ToolbarAction):
+ """Menu add action"""
+
+ label = _("Add menu...")
+ url = 'add-menu.html'
+ modal_target = True
+
+
+@pagelet_config(name='add-menu.html', context=IMenusContainer, layer=IPyAMSLayer,
+ permission=MANAGE_TEMPLATE_PERMISSION)
+@ajax_config(name='add-menu.json', context=IMenusContainer, layer=IPyAMSLayer, base=AJAXAddForm)
+class MenuAddForm(AdminDialogAddForm):
+ """Menu add form"""
+
+ legend = _("Add new menu")
+ icon_css_class = 'fa fa-fw fa-bars'
+
+ fields = field.Fields(IMenu).select('title')
+ edit_permission = MANAGE_TEMPLATE_PERMISSION
+
+ def create(self, data):
+ return Menu()
+
+ def add(self, object):
+ self.context.append(object)
+
+ def get_ajax_output(self, changes):
+ settings = get_parent(self.context, IMenusContainerTarget)
+ view = self.request.registry.getMultiAdapter((self.context, self.request), IMenusView,
+ name=self.context.__name__)
+ return {
+ 'status': 'success',
+ 'message': self.request.localizer.translate(_("Menu was correctly added.")),
+ 'events': [
+ get_json_switched_table_refresh_event(settings, self.request, view.table_class)
+ ]
+ }
+
+
+@pagelet_config(name='properties.html', context=IMenu, layer=IPyAMSLayer, permission=MANAGE_TEMPLATE_PERMISSION)
+@ajax_config(name='properties.json', context=IMenu, layer=IPyAMSLayer)
+class MenuPropertiesEditForm(AdminDialogEditForm):
+ """Menu properties edit form"""
+
+ legend = _("Edit menu properties")
+ icon_css_class = 'fa fa-fw fa-bars'
+
+ fields = field.Fields(IMenu).select('title')
+ edit_permission = MANAGE_TEMPLATE_PERMISSION
+
+ def get_ajax_output(self, changes):
+ output = super(self.__class__, self).get_ajax_output(changes)
+ if changes:
+ settings = get_parent(self.context, IMenusContainerTarget)
+ container = settings.menus
+ view = self.request.registry.getMultiAdapter((container, self.request), IMenusView,
+ name=container.__name__)
+ output.setdefault('events', []).append(
+ get_json_table_row_refresh_event(settings, self.request, view.table_class, self.context))
+ return output
+
+
+#
+# Menus table views
+#
+
+class MenusTable(BaseTable):
+ """Menus table"""
+
+ prefix = 'menus'
+ associations_name = 'menus'
+
+ permission = MANAGE_TEMPLATE_PERMISSION
+ hide_header = True
+ hide_body_toolbar = True
+ sortOn = None
+
+ @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):
+ menus = getattr(self.context, self.associations_name)
+ attributes = super(MenusTable, self).data_attributes
+ attributes.setdefault('table', {}).update({
+ 'id': self.id,
+ 'data-ams-location': absolute_url(menus, self.request),
+ 'data-ams-tablednd-drag-handle': 'td.sorter',
+ 'data-ams-tablednd-drop-target': 'set-menus-order.json',
+ 'data-ams-visibility-switcher': 'switch-menu-visibility.json'
+ })
+ return attributes
+
+
+@adapter_config(context=(IMenusContainerTarget, IPyAMSLayer, MenusTable), provides=IValues)
+class MenusTableValuesAdapter(ContextRequestViewAdapter):
+ """Menus table values adapter"""
+
+ @property
+ def values(self):
+ return getattr(self.context, self.view.associations_name).values()
+
+
+@adapter_config(name='sorter', context=(IMenusContainerTarget, IPyAMSLayer, MenusTable),
+ provides=IColumn)
+class MenusTableSorterColumn(SorterColumn):
+ """Menus table sorter column"""
+
+ permission = MANAGE_TEMPLATE_PERMISSION
+
+
+@adapter_config(name='show-hide', context=(IMenusContainerTarget, IPyAMSLayer, MenusTable),
+ provides=IColumn)
+class MenusTableShowHideColumn(VisibilitySwitcherColumn):
+ """Menus table visibility switcher column"""
+
+ permission = MANAGE_TEMPLATE_PERMISSION
+
+
+@adapter_config(name='name', context=(IMenusContainerTarget, IPyAMSLayer, MenusTable), provides=IColumn)
+class MenusTableNameColumn(I18nColumn, I18nAttrColumn):
+ """Menus table name column"""
+
+ _header = _("Label")
+ attrName = 'title'
+ weight = 10
+
+ def renderCell(self, item):
+ return '<span data-ams-stop-propagation="true" ' \
+ ' data-ams-click-handler="MyAMS.skin.switchCellContent" ' \
+ ' data-ams-switch-handler="get-menu-items.json" ' \
+ ' data-ams-switch-target=".menus">' \
+ ' <span class="small hint" title="{hint}" data-ams-hint-gravity="e">' \
+ ' <i class="fa fa-plus-square-o switch"></i>' \
+ ' </span>' \
+ '</span> <span class="title">{title}</span>' \
+ '<div class="inner-table-form menus margin-x-10 margin-bottom-0 padding-left-5"></div>'.format(
+ hint=self.request.localizer.translate(_("Click to see menu items")),
+ title=super(MenusTableNameColumn, self).renderCell(item))
+
+
+@adapter_config(name='trash', context=(IMenusContainerTarget, IPyAMSLayer, MenusTable), provides=IColumn)
+class MenusTableTrashColumn(TrashColumn):
+ """Menus table trash column"""
+
+ permission = MANAGE_TEMPLATE_PERMISSION
+
+
+@implementer(IMenusView)
+class MenusView(InnerTableView):
+ """Menus view"""
+
+ table_class = MenusTable
+
+ @property
+ def actions_context(self): # define context for internal actions
+ return self.request.registry.getAdapter(self.context, IMenusContainer,
+ name=self.table.associations_name)
+
+
+#
+# Menus container views
+#
+
+@view_config(name='set-menus-order.json', context=IMenusContainer, request_type=IPyAMSLayer,
+ permission=MANAGE_TEMPLATE_PERMISSION, renderer='json', xhr=True)
+def set_menus_order(request):
+ """Update menus order"""
+ order = list(map(str, json.loads(request.params.get('names'))))
+ request.context.updateOrder(order)
+ return {'status': 'success'}
+
+
+@view_config(name='switch-menu-visibility.json', context=IMenusContainer, request_type=IPyAMSLayer,
+ permission=MANAGE_TEMPLATE_PERMISSION, renderer='json', xhr=True)
+def set_menu_visibility(request):
+ """Set menu visibility"""
+ return switch_element_visibility(request, IMenusContainer)
+
+
+@view_config(name='delete-element.json', context=IMenusContainer, request_type=IPyAMSLayer,
+ permission=MANAGE_TEMPLATE_PERMISSION, renderer='json', xhr=True)
+def delete_menu(request):
+ """Delete menu"""
+ return delete_container_element(request, ignore_permission=True)
+
+
+@view_config(name='get-menu-items.json', context=IMenusContainer, request_type=IPyAMSLayer,
+ permission=MANAGE_TEMPLATE_PERMISSION, renderer='json', xhr=True)
+def get_menu_items_table(request):
+ """Get menu items table"""
+ menu = request.context.get(str(request.params.get('object_name')))
+ if menu is None:
+ raise NotFound()
+ table = MenuLinksTable(menu, request)
+ table.update()
+ return table.render()
+
+
+#
+# Menu links table
+#
+
+class LinksTable(AssociationsTable):
+ """Links base table class"""
+
+ associations_name = None
+
+ @reify
+ def id(self):
+ if IMenu.providedBy(self.context):
+ context = self.context
+ else:
+ context = self.request.registry.getAdapter(self.context, IMenuLinksContainer,
+ name=self.associations_name)
+ return get_table_id(self, context)
+
+ @property
+ def prefix(self):
+ return '{0}_links'.format(self.associations_name)
+
+ permission = MANAGE_TEMPLATE_PERMISSION
+ hide_header = True
+ hide_body_toolbar = True
+
+
+class MenuLinksTable(LinksTable):
+ """Menu links associations table"""
+
+ prefix = 'menu_links'
+ associations_name = ''
+
+ @property
+ def data_attributes(self):
+ attributes = super(LinksTable, self).data_attributes
+ attributes.setdefault('table', {}).update({
+ 'data-ams-location': absolute_url(self.context, self.request),
+ })
+ attributes.setdefault('tr', {}).update({'data-ams-stop-propagation': 'true'})
+ return attributes
+
+
+@adapter_config(name='name', context=(IMenu, IPyAMSLayer, MenuLinksTable), provides=IColumn)
+class MenuLinksTableNameColumn(AssociationsTablePublicNameColumn):
+ """Menu links table name column"""
+
+ def renderHeadCell(self):
+ result = super(MenuLinksTableNameColumn, self).renderHeadCell()
+ registry = self.request.registry
+ viewlet = registry.queryMultiAdapter((self.context, self.request, self.table), IViewletManager,
+ name='pyams.widget_title')
+ if viewlet is not None:
+ viewlet.update()
+ result += viewlet.render()
+ return result
+
+
+@adapter_config(context=(IMenu, IPyAMSLayer), provides=IMenuLinksView)
+@implementer(IMenuLinksView)
+class MenuLinksView(InnerTableView):
+ """Links base view"""
+
+ table_class = MenuLinksTable
+
+ @property
+ def actions_context(self): # define context for internal actions
+ return self.request.registry.getAdapter(self.context, IMenuLinksContainer,
+ name=self.table.associations_name)
+
+
+#
+# Menu links container views
+#
+
+@view_config(name='set-associations-order.json', context=IMenuLinksContainer, request_type=IPyAMSLayer,
+ permission=MANAGE_TEMPLATE_PERMISSION, renderer='json', xhr=True)
+def set_associations_order(request):
+ """Update associations order"""
+ order = list(map(str, json.loads(request.params.get('names'))))
+ request.context.updateOrder(order)
+ return {'status': 'success'}
+
+
+@view_config(name='switch-association-visibility.json', context=IMenuLinksContainer, request_type=IPyAMSLayer,
+ permission=MANAGE_TEMPLATE_PERMISSION, renderer='json', xhr=True)
+def set_association_visibility(request):
+ """Set association visibility"""
+ return switch_element_visibility(request, IMenuLinksContainer)
+
+
+#
+# Link add and edit forms
+#
+
+@adapter_config(context=IMenuLink, provides=IFormContextPermissionChecker)
+class MenuLinkPermissionChecker(ContextAdapter):
+ """Menu link permission checker"""
+
+ edit_permission = MANAGE_TEMPLATE_PERMISSION
+
+
+class LinkAJAXAddForm(AJAXAddForm):
+ """Menu link add form, JSON renderer"""
+
+ def get_ajax_output(self, changes):
+ registry = self.request.registry
+ container = get_parent(self.context, IMenuLinksContainer)
+ view = registry.queryMultiAdapter((container, self.request), IMenuLinksView,
+ name=container.__name__)
+ if view is None:
+ view = registry.getMultiAdapter((container, self.request), IMenuLinksView)
+ return {
+ 'status': 'success',
+ 'message': self.request.localizer.translate(_("Link was correctly added.")),
+ 'events': [
+ get_json_switched_table_refresh_event(self.context, self.request, view.table_class)
+ ]
+ }
+
+
+class LinkPropertiesAJAXEditForm(AJAXEditForm):
+ """Menu link properties edit form, JSON renderer"""
+
+ def get_ajax_output(self, changes):
+ output = AJAXEditForm.get_ajax_output(self, changes)
+ if changes:
+ registry = self.request.registry
+ container = get_parent(self.context, IMenuLinksContainer)
+ view = registry.queryMultiAdapter((container, self.request), IMenuLinksView,
+ name=container.__name__)
+ if view is None:
+ view = registry.getMultiAdapter((container, self.request), IMenuLinksView)
+ output.setdefault('events', []).append(
+ get_json_table_row_refresh_event(container, self.request, view.table_class, self.context))
+ return output
+
+
+#
+# Internal links
+#
+
+@viewlet_config(name='add-internal-link.menu', context=IMenuLinksContainerTarget, layer=IPyAMSLayer,
+ view=IMenuLinksView, manager=IToolbarAddingMenu, weight=50)
+@viewlet_config(name='add-internal-link.menu', context=IMenu, layer=IPyAMSLayer,
+ view=MenuLinksTable, manager=IToolbarAddingMenu, permission=MANAGE_TEMPLATE_PERMISSION, weight=50)
+class MenuInternalLinkAddMenu(InternalLinkAddMenu):
+ """Header internal link add menu"""
+
+
+@pagelet_config(name='add-internal-link.html', context=IMenuLinksContainer, layer=IPyAMSLayer,
+ permission=MANAGE_TEMPLATE_PERMISSION)
+@ajax_config(name='add-internal-link.json', context=IMenuLinksContainer, layer=IPyAMSLayer,
+ base=LinkAJAXAddForm)
+class MenuInternalLinkAddForm(InternalLinkAddForm):
+ """Menu internal link add form"""
+
+ edit_permission = MANAGE_TEMPLATE_PERMISSION
+
+ def create(self, data):
+ result = super(MenuInternalLinkAddForm, self).create(data)
+ alsoProvides(result, IMenuInternalLink)
+ return result
+
+ def add(self, object):
+ self.context.append(object)
+
+
+@pagelet_config(name='properties.html', context=IMenuInternalLink, layer=IPyAMSLayer,
+ permission=VIEW_SYSTEM_PERMISSION)
+@ajax_config(name='properties.json', context=IMenuInternalLink, layer=IPyAMSLayer,
+ base=LinkPropertiesAJAXEditForm)
+class MenuInternalLinkPropertiesEditForm(InternalLinkPropertiesEditForm):
+ """Menu internal link properties edit form"""
+
+ edit_permission = None # managed by IFormContextPermissionChecker adapter
+
+
+#
+# External links
+#
+
+@viewlet_config(name='add-external-link.menu', context=IMenuLinksContainerTarget, view=IMenuLinksView,
+ layer=IPyAMSLayer, manager=IToolbarAddingMenu, weight=51)
+@viewlet_config(name='add-external-link.menu', context=IMenu, layer=IPyAMSLayer,
+ view=MenuLinksTable, manager=IToolbarAddingMenu, permission=MANAGE_TEMPLATE_PERMISSION, weight=51)
+class MenuExternalLinkAddMenu(ExternalLinkAddMenu):
+ """Menu external link add menu"""
+
+
+@pagelet_config(name='add-external-link.html', context=IMenuLinksContainer, layer=IPyAMSLayer,
+ permission=MANAGE_TEMPLATE_PERMISSION)
+@ajax_config(name='add-external-link.json', context=IMenuLinksContainer, layer=IPyAMSLayer,
+ base=LinkAJAXAddForm)
+class MenuExternalLinkAddForm(ExternalLinkAddForm):
+ """Menu external link add form"""
+
+ edit_permission = MANAGE_TEMPLATE_PERMISSION
+
+ def create(self, data):
+ result = super(MenuExternalLinkAddForm, self).create(data)
+ alsoProvides(result, IMenuExternalLink)
+ return result
+
+ def add(self, object):
+ self.context.append(object)
+
+
+@pagelet_config(name='properties.html', context=IMenuExternalLink, layer=IPyAMSLayer,
+ permission=VIEW_SYSTEM_PERMISSION)
+@ajax_config(name='properties.json', context=IMenuExternalLink, layer=IPyAMSLayer,
+ base=LinkPropertiesAJAXEditForm)
+class MenuExternalLinkPropertiesEditForm(ExternalLinkPropertiesEditForm):
+ """Menu external link properties edit form"""
+
+ edit_permission = None # managed by IFormContextPermissionChecker adapter