# HG changeset patch # User Thierry Florac # Date 1558627302 -7200 # Node ID c32aea65edf3ea21cb48b2f4014030ae0ecbd13f # Parent 2ef1161388a955234b33b4de0e2444e8a777907a Module refactoring diff -r 2ef1161388a9 -r c32aea65edf3 src/pyams_content/shared/common/zmi/types.py --- a/src/pyams_content/shared/common/zmi/types.py Thu May 23 17:16:27 2019 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,620 +0,0 @@ -# -# Copyright (c) 2008-2015 Thierry Florac -# 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 json - -from pyramid.decorator import reify -from pyramid.events import subscriber -from pyramid.exceptions import NotFound -from pyramid.view import view_config -from z3c.form import field -from z3c.form.interfaces import DISPLAY_MODE, IDataExtractedEvent -from z3c.table.interfaces import IColumn, IValues -from zope.interface import Invalid, implementer - -from pyams_content import _ -from pyams_content.interfaces import MANAGE_CONTENT_PERMISSION, MANAGE_TOOL_PERMISSION -from pyams_content.reference.pictograms.zmi.widget import PictogramSelectFieldWidget -from pyams_content.shared.common.interfaces.types import IBaseDataType, IDataType, ISubType, ITypedDataManager, \ - ITypedSharedTool, IWfTypedSharedContent -from pyams_content.shared.common.types import DataType, SubType -from pyams_content.shared.common.zmi import SharedContentAddForm -from pyams_content.shared.common.zmi.properties import SharedContentPropertiesEditForm -from pyams_content.zmi import pyams_content -from pyams_form.form import AJAXAddForm, ajax_config -from pyams_form.interfaces.form import IWidgetForm -from pyams_form.security import ProtectedFormObjectMixin -from pyams_i18n.interfaces import II18n -from pyams_i18n.widget import I18nSEOTextLineFieldWidget -from pyams_pagelet.pagelet import pagelet_config -from pyams_skin.container import delete_container_element -from pyams_skin.event import get_json_table_refresh_event -from pyams_skin.interfaces import IInnerPage -from pyams_skin.interfaces.container import ITableElementName -from pyams_skin.interfaces.viewlet import IWidgetTitleViewletManager -from pyams_skin.layer import IPyAMSLayer -from pyams_skin.table import ActionColumn, BaseTable, NameColumn, SorterColumn, TrashColumn -from pyams_skin.viewlet.menu import MenuItem -from pyams_skin.viewlet.toolbar import ToolbarAction -from pyams_utils.adapter import ContextRequestAdapter, ContextRequestViewAdapter, adapter_config -from pyams_utils.fanstatic import get_resource_path -from pyams_utils.traversing import get_parent -from pyams_utils.unicode import translate_string -from pyams_utils.url import absolute_url -from pyams_viewlet.interfaces import IViewletManager -from pyams_viewlet.viewlet import viewlet_config -from pyams_zmi.form import AdminDialogAddForm, AdminDialogEditForm, AdminEditForm -from pyams_zmi.interfaces.menu import IPropertiesMenu -from pyams_zmi.layer import IAdminLayer -from pyams_zmi.view import ContainerAdminView - - -@viewlet_config(name='data-types.menu', context=ITypedSharedTool, layer=IAdminLayer, - manager=IPropertiesMenu, permission=MANAGE_TOOL_PERMISSION, weight=5) -class TypedSharedToolTypesMenu(MenuItem): - """Typed shared tool types menu""" - - label = _("Data types") - icon_class = 'fa-folder-o' - url = '#data-types.html' - - -# -# Typed shared data types manager target views -# - -class TypedSharedToolTypesTable(ProtectedFormObjectMixin, BaseTable): - """Typed shared tool types table""" - - prefix = 'types' - - hide_header = 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): - attributes = super(TypedSharedToolTypesTable, self).data_attributes - attributes['table'] = { - 'id': self.id, - 'data-ams-plugins': 'pyams_content', - 'data-ams-plugin-pyams_content-src': get_resource_path(pyams_content), - 'data-ams-location': absolute_url(ITypedDataManager(self.context), self.request), - 'data-ams-tablednd-drag-handle': 'td.sorter', - 'data-ams-tablednd-drop-target': 'set-types-order.json' - } - return attributes - - @reify - def values(self): - return list(super(TypedSharedToolTypesTable, self).values) - - def render(self): - if not self.values: - translate = self.request.localizer.translate - return translate(_("No currently defined data type.")) - return super(TypedSharedToolTypesTable, self).render() - - -@adapter_config(context=(ITypedSharedTool, IPyAMSLayer, TypedSharedToolTypesTable), provides=IValues) -class TypedSharedToolTypesValues(ContextRequestViewAdapter): - """Typed shared tool types table values adapter""" - - @property - def values(self): - return ITypedDataManager(self.context).values() - - -@adapter_config(name='sorter', context=(ITypedSharedTool, IPyAMSLayer, TypedSharedToolTypesTable), - provides=IColumn) -class TypedSharedToolTypesSorterColumn(ProtectedFormObjectMixin, SorterColumn): - """Typed shared tool types sorter column""" - - -@view_config(name='set-types-order.json', context=ITypedDataManager, request_type=IPyAMSLayer, - permission=MANAGE_TOOL_PERMISSION, renderer='json', xhr=True) -def set_data_types_order(request): - """Update data types order""" - order = list(map(str, json.loads(request.params.get('names')))) - request.context.updateOrder(order) - return {'status': 'success'} - - -@adapter_config(name='name', context=(ITypedSharedTool, IPyAMSLayer, TypedSharedToolTypesTable), - provides=IColumn) -class TypedSharedToolTypesNameColumn(NameColumn): - """Typed shared tool types name column""" - - _header = _("Data type label") - - def renderCell(self, item): - return '' \ - ' ' \ - ' ' \ - ' ' \ - '   {title}' \ - '
'.format( - hint=self.request.localizer.translate(_("Click to see subtypes")), - title=super(TypedSharedToolTypesNameColumn, self).renderCell(item)) - - -@adapter_config(name='paragraphs', context=(ITypedSharedTool, IPyAMSLayer, TypedSharedToolTypesTable), - provides=IColumn) -class TypedSharedToolTypesParagraphsColumn(ActionColumn): - """Typed shared tool types paragraphs column""" - - weight = 100 - - icon_class = 'fa fa-fw fa-paragraph' - icon_hint = _("Default paragraphs") - - url = 'paragraphs-dialog.html' - modal_target = True - - permission = MANAGE_TOOL_PERMISSION - - -@adapter_config(name='associations', context=(ITypedSharedTool, IPyAMSLayer, TypedSharedToolTypesTable), - provides=IColumn) -class TypedSharedToolTypesAssociationsColumn(ActionColumn): - """Typed shared tool types associations column""" - - weight = 110 - - icon_class = 'fa fa-fw fa-link' - icon_hint = _("Default associations") - - url = 'associations-dialog.html' - modal_target = True - - permission = MANAGE_TOOL_PERMISSION - - -@adapter_config(name='themes', context=(ITypedSharedTool, IPyAMSLayer, TypedSharedToolTypesTable), - provides=IColumn) -class TypedSharedToolTypesThemesColumn(ActionColumn): - """Typed shared tool types themes column""" - - weight = 120 - - icon_class = 'fa fa-fw fa-tags' - icon_hint = _("Default themes") - - url = 'themes-dialog.html' - modal_target = True - - permission = MANAGE_TOOL_PERMISSION - - -@adapter_config(name='trash', context=(ITypedSharedTool, IPyAMSLayer, TypedSharedToolTypesTable), - provides=IColumn) -class TypedSharedToolTypesTrashColumn(TrashColumn): - """Typed shared tool types trash column""" - - permission = MANAGE_TOOL_PERMISSION - - -@view_config(name='delete-element.json', context=ITypedDataManager, request_type=IPyAMSLayer, - permission=MANAGE_TOOL_PERMISSION, renderer='json', xhr=True) -def delete_data_type(request): - """Data type delete view""" - return delete_container_element(request, ignore_permission=True) - - -@pagelet_config(name='data-types.html', context=ITypedSharedTool, layer=IPyAMSLayer, - permission=MANAGE_TOOL_PERMISSION) -class TypedSharedToolTypesView(ContainerAdminView): - """Typed shared tool types view""" - - title = _("Content data types") - table_class = TypedSharedToolTypesTable - - -# -# Data type views -# - -@adapter_config(context=(IBaseDataType, IPyAMSLayer), provides=ITableElementName) -class DataTypeElementNameAdapter(ContextRequestAdapter): - """Types shared tool types name adapter""" - - @property - def name(self): - return II18n(self.context).query_attribute('label', request=self.request) - - -@viewlet_config(name='add-data-type.action', context=ITypedSharedTool, layer=IAdminLayer, - view=TypedSharedToolTypesView, manager=IWidgetTitleViewletManager, - permission=MANAGE_TOOL_PERMISSION, weight=1) -class DataTypeAddAction(ToolbarAction): - """Data type adding action""" - - label = _("Add data type") - label_css_class = 'fa fa-fw fa-plus' - url = 'add-data-type.html' - modal_target = True - - -@pagelet_config(name='add-data-type.html', context=ITypedSharedTool, layer=IPyAMSLayer, - permission=MANAGE_TOOL_PERMISSION) -@ajax_config(name='add-data-type.json', context=ITypedSharedTool, layer=IPyAMSLayer, base=AJAXAddForm) -class DataTypeAddForm(AdminDialogAddForm): - """Data type add form""" - - legend = _("Add new data type") - icon_css_class = 'fa fa-fw fa-folder-o' - label_css_class = 'control-label col-md-4' - input_css_class = 'col-md-8' - - @property - def fields(self): - fields = field.Fields(IDataType).omit('__parent__', '__name__') - fields['pictogram'].widgetFactory = PictogramSelectFieldWidget - if not self.context.shared_content_types_fields: - fields = fields.omit('field_names') - return fields - - edit_permission = MANAGE_TOOL_PERMISSION - - def create(self, data): - return DataType() - - def add(self, object): - name = translate_string(object.name, spaces='-') - ITypedDataManager(self.context)[name] = object - - def nextURL(self): - return '#data-types.html' - - -@subscriber(IDataExtractedEvent, form_selector=DataTypeAddForm) -def handle_datatype_add_form_data_extraction(event): - """Check new data type for existing name""" - context = event.form.context - manager = ITypedDataManager(context) - name = event.data.get('name') - if translate_string(name, spaces='-') in manager: - event.form.widgets.errors += (Invalid(_("Specified type name is already used!")),) - - -@pagelet_config(name='properties.html', context=IDataType, layer=IPyAMSLayer, permission=MANAGE_TOOL_PERMISSION) -@ajax_config(name='properties.json', context=IDataType, layer=IPyAMSLayer) -class DataTypeEditForm(AdminDialogEditForm): - """Data type edit form""" - - prefix = 'datatype_properties.' - - legend = _("Data type properties") - icon_css_class = 'fa fa-fw fa-folder-o' - label_css_class = 'control-label col-md-4' - input_css_class = 'col-md-8' - - @property - def fields(self): - fields = field.Fields(IDataType).omit('__parent__', '__name__') - fields['pictogram'].widgetFactory = PictogramSelectFieldWidget - tool = get_parent(self.context, ITypedSharedTool) - if not tool.shared_content_types_fields: - fields = fields.omit('field_names') - return fields - - edit_permission = MANAGE_TOOL_PERMISSION - - def updateWidgets(self, prefix=None): - super(DataTypeEditForm, self).updateWidgets(prefix) - if 'name' in self.widgets: - self.widgets['name'].mode = DISPLAY_MODE - - -# -# Subtypes views -# - -class DatatypeSubtypesTable(BaseTable): - """Data type subtypes table""" - - prefix = 'subtypes' - - hide_header = True - sortOn = None - - widget_class = 'ams-widget margin-top-5' - cssClasses = {'table': 'table table-bordered table-striped table-hover table-tight table-dnd'} - - @property - def data_attributes(self): - attributes = super(DatatypeSubtypesTable, self).data_attributes - attributes['table'] = { - 'id': self.id, - 'data-ams-location': absolute_url(self.context, self.request), - 'data-ams-tablednd-drag-handle': 'td.sorter', - 'data-ams-tablednd-drop-target': 'set-subtypes-order.json' - } - attributes.setdefault('tr', {}).update({'data-ams-stop-propagation': 'true'}) - return attributes - - @reify - def values(self): - return list(super(DatatypeSubtypesTable, self).values) - - -@adapter_config(context=(IDataType, IPyAMSLayer, DatatypeSubtypesTable), provides=IValues) -class DatatypeSubtypesTableValues(ContextRequestViewAdapter): - """Data type subtypes table values adapter""" - - @property - def values(self): - return self.context.values() - - -@adapter_config(name='sorter', context=(IDataType, IPyAMSLayer, DatatypeSubtypesTable), provides=IColumn) -class DatatypeSubtypesTableSorterColumn(SorterColumn): - """Data type subtypes table sorter column""" - - -@view_config(name='set-subtypes-order.json', context=IDataType, request_type=IPyAMSLayer, - permission=MANAGE_TOOL_PERMISSION, renderer='json', xhr=True) -def set_subtypes_order(request): - """Update subtypes order""" - order = list(map(str, json.loads(request.params.get('names')))) - request.context.updateOrder(order) - return {'status': 'success'} - - -@adapter_config(name='name', context=(IDataType, IPyAMSLayer, DatatypeSubtypesTable), provides=IColumn) -class DatatypeSubtypesTableNameColumn(NameColumn): - """Data type subtypes table name column""" - - _header = _("Subtype label") - - def renderHeadCell(self): - result = super(DatatypeSubtypesTableNameColumn, 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(name='paragraphs', context=(IDataType, IPyAMSLayer, DatatypeSubtypesTable), - provides=IColumn) -class DatatypeSubtypesTableParagraphsColumn(ActionColumn): - """Data type subtypes paragraphs column""" - - weight = 100 - - icon_class = 'fa fa-fw fa-paragraph' - icon_hint = _("Default paragraphs") - - url = 'paragraphs-dialog.html' - modal_target = True - - permission = MANAGE_TOOL_PERMISSION - - -@adapter_config(name='associations', context=(IDataType, IPyAMSLayer, DatatypeSubtypesTable), - provides=IColumn) -class DatatypeSubtypesTableAssociationsColumn(ActionColumn): - """Data type subtypes associations column""" - - weight = 110 - - icon_class = 'fa fa-fw fa-link' - icon_hint = _("Default associations") - - url = 'associations-dialog.html' - modal_target = True - - permission = MANAGE_TOOL_PERMISSION - - -@adapter_config(name='trash', context=(IDataType, IPyAMSLayer, DatatypeSubtypesTable), provides=IColumn) -class DatatypeSubtypesTableTrashColumn(TrashColumn): - """Data type subtypes table trash column""" - - permission = MANAGE_TOOL_PERMISSION - - -@view_config(name='delete-element.json', context=IDataType, request_type=IPyAMSLayer, - permission=MANAGE_TOOL_PERMISSION, renderer='json', xhr=True) -def delete_subtype(request): - """Data subtype delete view""" - return delete_container_element(request, ignore_permission=True) - - -@view_config(name='get-subtypes-table.json', context=ITypedDataManager, request_type=IPyAMSLayer, - permission=MANAGE_TOOL_PERMISSION, renderer='json', xhr=True) -def get_subtypes_table(request): - """Get subtypes table""" - datatype = request.context.get(str(request.params.get('object_name'))) - if datatype is None: - raise NotFound() - table = DatatypeSubtypesTable(datatype, request) - table.update() - return table.render() - - -# -# Data sub-types views -# - -@viewlet_config(name='add-data-subtype.action', context=IDataType, layer=IPyAMSLayer, - view=DatatypeSubtypesTable, manager=IWidgetTitleViewletManager, - permission=MANAGE_TOOL_PERMISSION, weight=1) -class DataSubtypeAddAction(ToolbarAction): - """Data subtype adding action""" - - label = _("Add subtype") - label_css_class = 'fa fa-fw fa-plus' - url = 'add-data-subtype.html' - modal_target = True - - -@pagelet_config(name='add-data-subtype.html', context=IDataType, layer=IPyAMSLayer, - permission=MANAGE_TOOL_PERMISSION) -@ajax_config(name='add-data-subtype.json', context=IDataType, layer=IPyAMSLayer, base=AJAXAddForm) -class DataSubtypeAddForm(AdminDialogAddForm): - """Data subtype add form""" - - legend = _("Add new subtype") - icon_css_class = 'fa fa-fw fa-folder-o' - label_css_class = 'control-label col-md-4' - input_css_class = 'col-md-8' - - fields = field.Fields(ISubType).omit('__parent__', '__name__') - fields['pictogram'].widgetFactory = PictogramSelectFieldWidget - - edit_permission = MANAGE_TOOL_PERMISSION - - def create(self, data): - return SubType() - - def add(self, object): - name = translate_string(object.name, spaces='-') - IDataType(self.context)[name] = object - - def nextURL(self): - return absolute_url(self.context, self.request, 'admin#data-types.html') - - def get_ajax_output(self, changes): - return { - 'status': 'success', - 'message': self.request.localizer.translate(_("Subtype was correctly added.")), - 'events': [ - get_json_table_refresh_event(self.context, self.request, DatatypeSubtypesTable) - ] - } - - -@subscriber(IDataExtractedEvent, form_selector=DataSubtypeAddForm) -def handle_subtype_add_form_data_extraction(event): - """Check new data subtype for existing name""" - context = event.form.context - manager = IDataType(context) - name = event.data.get('name') - if translate_string(name, spaces='-') in manager: - event.form.widgets.errors += (Invalid(_("Specified subtype name is already used!")),) - - -@pagelet_config(name='properties.html', context=ISubType, layer=IPyAMSLayer, permission=MANAGE_TOOL_PERMISSION) -@ajax_config(name='properties.json', context=ISubType, layer=IPyAMSLayer) -class DataSubtypeEditForm(AdminDialogEditForm): - """Data subtype edit form""" - - prefix = 'subtype_properties.' - - legend = _("Data subtype properties") - icon_css_class = 'fa fa-fw fa-folder-o' - label_css_class = 'control-label col-md-4' - input_css_class = 'col-md-8' - - fields = field.Fields(ISubType).omit('__parent__', '__name__') - fields['pictogram'].widgetFactory = PictogramSelectFieldWidget - - edit_permission = MANAGE_TOOL_PERMISSION - - def updateWidgets(self, prefix=None): - super(DataSubtypeEditForm, self).updateWidgets(prefix) - if 'name' in self.widgets: - self.widgets['name'].mode = DISPLAY_MODE - - def get_ajax_output(self, changes): - if 'label' in changes.get(IBaseDataType, ()): - target = get_parent(self.context, IDataType) - return { - 'status': 'success', - 'message': self.request.localizer.translate(self.successMessage), - 'events': [ - get_json_table_refresh_event(target, self.request, DatatypeSubtypesTable) - ] - } - else: - return super(self.__class__, self).get_ajax_output(changes) - - -# -# Typed shared content views -# - -class TypedSharedContentAddForm(SharedContentAddForm): - """Typed shared content add form""" - - fields = field.Fields(IWfTypedSharedContent).select('title', 'data_type', 'notepad') - - def updateWidgets(self, prefix=None): - super(TypedSharedContentAddForm, self).updateWidgets(prefix) - if 'data_type' in self.widgets: - self.widgets['data_type'].prompt = True - self.widgets['data_type'].promptMessage = _("Select content type...") - - -@pagelet_config(name='properties.html', context=IWfTypedSharedContent, layer=IPyAMSLayer, - permission=MANAGE_CONTENT_PERMISSION) -@ajax_config(name='properties.json', context=IWfTypedSharedContent, layer=IPyAMSLayer, - permission=MANAGE_CONTENT_PERMISSION) -class TypedSharedContentPropertiesEditForm(SharedContentPropertiesEditForm): - """Typed shared content properties edit form""" - - interface = IWfTypedSharedContent - - @property - def fields(self): - fields = field.Fields(self.interface).select('title', 'short_name', 'content_url', - 'data_type', 'header', 'description', 'notepad') - fields['title'].widgetFactory = I18nSEOTextLineFieldWidget - if not self.context.handle_content_url: - fields = fields.omit('content_url') - if not self.context.handle_header: - fields = fields.omit('header') - if not self.context.handle_description: - fields = fields.omit('description') - return fields - - -# -# Typed shared content custom fields edit form -# - -@implementer(IWidgetForm, IInnerPage) -class TypedSharedContentTypeFieldsEditForm(AdminEditForm): - """Typed shared content type fields properties edit form""" - - prefix = 'type_properties.' - - @property - def legend(self): - translate = self.request.localizer.translate - data_type = self.context.get_data_type() - return translate(_("Custom properties for type « {0} »")).format( - II18n(data_type).query_attribute('label', request=self.request)) - - @property - def fields(self): - data_type = self.context.get_data_type() - if data_type is not None: - manager = get_parent(self.context, ITypedSharedTool) - return field.Fields(manager.shared_content_types_fields).select(*data_type.field_names) diff -r 2ef1161388a9 -r c32aea65edf3 src/pyams_content/shared/common/zmi/types/__init__.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pyams_content/shared/common/zmi/types/__init__.py Thu May 23 18:01:42 2019 +0200 @@ -0,0 +1,444 @@ +# +# Copyright (c) 2008-2015 Thierry Florac +# 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 json + +from pyramid.decorator import reify +from pyramid.events import subscriber +from pyramid.exceptions import NotFound +from pyramid.view import view_config +from z3c.form import field +from z3c.form.interfaces import DISPLAY_MODE, IDataExtractedEvent +from z3c.table.interfaces import IColumn, IValues +from zope.interface import Invalid, implementer + +from pyams_content.interfaces import MANAGE_CONTENT_PERMISSION, MANAGE_TOOL_PERMISSION +from pyams_content.reference.pictograms.zmi.widget import PictogramSelectFieldWidget +from pyams_content.shared.common.interfaces.types import IBaseDataType, IDataType, ISubType, \ + ITypedDataManager, ITypedSharedTool, IWfTypedSharedContent +from pyams_content.shared.common.types import DataType, SubType +from pyams_content.shared.common.zmi import SharedContentAddForm +from pyams_content.shared.common.zmi.properties import SharedContentPropertiesEditForm +from pyams_content.shared.common.zmi.types.manager import TypedSharedToolTypesView +from pyams_form.form import AJAXAddForm, ajax_config +from pyams_form.interfaces.form import IWidgetForm +from pyams_i18n.interfaces import II18n +from pyams_i18n.widget import I18nSEOTextLineFieldWidget +from pyams_pagelet.pagelet import pagelet_config +from pyams_skin.container import delete_container_element +from pyams_skin.event import get_json_table_refresh_event +from pyams_skin.interfaces import IInnerPage +from pyams_skin.interfaces.container import ITableElementName +from pyams_skin.interfaces.viewlet import IWidgetTitleViewletManager +from pyams_skin.layer import IPyAMSLayer +from pyams_skin.table import ActionColumn, BaseTable, NameColumn, SorterColumn, TrashColumn +from pyams_skin.viewlet.toolbar import ToolbarAction +from pyams_utils.adapter import ContextRequestAdapter, ContextRequestViewAdapter, adapter_config +from pyams_utils.traversing import get_parent +from pyams_utils.unicode import translate_string +from pyams_utils.url import absolute_url +from pyams_viewlet.interfaces import IViewletManager +from pyams_viewlet.viewlet import viewlet_config +from pyams_zmi.form import AdminDialogAddForm, AdminDialogEditForm, AdminEditForm +from pyams_zmi.layer import IAdminLayer + +from pyams_content import _ + + +# +# Data type views +# + +@adapter_config(context=(IBaseDataType, IPyAMSLayer), provides=ITableElementName) +class DataTypeElementNameAdapter(ContextRequestAdapter): + """Types shared tool types name adapter""" + + @property + def name(self): + return II18n(self.context).query_attribute('label', request=self.request) + + +@viewlet_config(name='add-data-type.action', context=ITypedSharedTool, layer=IAdminLayer, + view=TypedSharedToolTypesView, manager=IWidgetTitleViewletManager, + permission=MANAGE_TOOL_PERMISSION, weight=1) +class DataTypeAddAction(ToolbarAction): + """Data type adding action""" + + label = _("Add data type") + label_css_class = 'fa fa-fw fa-plus' + url = 'add-data-type.html' + modal_target = True + + +@pagelet_config(name='add-data-type.html', context=ITypedSharedTool, layer=IPyAMSLayer, + permission=MANAGE_TOOL_PERMISSION) +@ajax_config(name='add-data-type.json', context=ITypedSharedTool, layer=IPyAMSLayer, base=AJAXAddForm) +class DataTypeAddForm(AdminDialogAddForm): + """Data type add form""" + + legend = _("Add new data type") + icon_css_class = 'fa fa-fw fa-folder-o' + label_css_class = 'control-label col-md-4' + input_css_class = 'col-md-8' + + @property + def fields(self): + fields = field.Fields(IDataType).omit('__parent__', '__name__') + fields['pictogram'].widgetFactory = PictogramSelectFieldWidget + if not self.context.shared_content_types_fields: + fields = fields.omit('field_names') + return fields + + edit_permission = MANAGE_TOOL_PERMISSION + + def create(self, data): + return DataType() + + def add(self, object): + name = translate_string(object.name, spaces='-') + ITypedDataManager(self.context)[name] = object + + def nextURL(self): + return '#data-types.html' + + +@subscriber(IDataExtractedEvent, form_selector=DataTypeAddForm) +def handle_datatype_add_form_data_extraction(event): + """Check new data type for existing name""" + context = event.form.context + manager = ITypedDataManager(context) + name = event.data.get('name') + if translate_string(name, spaces='-') in manager: + event.form.widgets.errors += (Invalid(_("Specified type name is already used!")),) + + +@pagelet_config(name='properties.html', context=IDataType, layer=IPyAMSLayer, permission=MANAGE_TOOL_PERMISSION) +@ajax_config(name='properties.json', context=IDataType, layer=IPyAMSLayer) +class DataTypeEditForm(AdminDialogEditForm): + """Data type edit form""" + + prefix = 'datatype_properties.' + + legend = _("Data type properties") + icon_css_class = 'fa fa-fw fa-folder-o' + label_css_class = 'control-label col-md-4' + input_css_class = 'col-md-8' + + @property + def fields(self): + fields = field.Fields(IDataType).omit('__parent__', '__name__') + fields['pictogram'].widgetFactory = PictogramSelectFieldWidget + tool = get_parent(self.context, ITypedSharedTool) + if not tool.shared_content_types_fields: + fields = fields.omit('field_names') + return fields + + edit_permission = MANAGE_TOOL_PERMISSION + + def updateWidgets(self, prefix=None): + super(DataTypeEditForm, self).updateWidgets(prefix) + if 'name' in self.widgets: + self.widgets['name'].mode = DISPLAY_MODE + + +# +# Subtypes views +# + +class DatatypeSubtypesTable(BaseTable): + """Data type subtypes table""" + + prefix = 'subtypes' + + hide_header = True + sortOn = None + + widget_class = 'ams-widget margin-top-5' + cssClasses = {'table': 'table table-bordered table-striped table-hover table-tight table-dnd'} + + @property + def data_attributes(self): + attributes = super(DatatypeSubtypesTable, self).data_attributes + attributes['table'] = { + 'id': self.id, + 'data-ams-location': absolute_url(self.context, self.request), + 'data-ams-tablednd-drag-handle': 'td.sorter', + 'data-ams-tablednd-drop-target': 'set-subtypes-order.json' + } + attributes.setdefault('tr', {}).update({'data-ams-stop-propagation': 'true'}) + return attributes + + @reify + def values(self): + return list(super(DatatypeSubtypesTable, self).values) + + +@adapter_config(context=(IDataType, IPyAMSLayer, DatatypeSubtypesTable), provides=IValues) +class DatatypeSubtypesTableValues(ContextRequestViewAdapter): + """Data type subtypes table values adapter""" + + @property + def values(self): + return self.context.values() + + +@adapter_config(name='sorter', context=(IDataType, IPyAMSLayer, DatatypeSubtypesTable), provides=IColumn) +class DatatypeSubtypesTableSorterColumn(SorterColumn): + """Data type subtypes table sorter column""" + + +@view_config(name='set-subtypes-order.json', context=IDataType, request_type=IPyAMSLayer, + permission=MANAGE_TOOL_PERMISSION, renderer='json', xhr=True) +def set_subtypes_order(request): + """Update subtypes order""" + order = list(map(str, json.loads(request.params.get('names')))) + request.context.updateOrder(order) + return {'status': 'success'} + + +@adapter_config(name='name', context=(IDataType, IPyAMSLayer, DatatypeSubtypesTable), provides=IColumn) +class DatatypeSubtypesTableNameColumn(NameColumn): + """Data type subtypes table name column""" + + _header = _("Subtype label") + + def renderHeadCell(self): + result = super(DatatypeSubtypesTableNameColumn, 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(name='paragraphs', context=(IDataType, IPyAMSLayer, DatatypeSubtypesTable), + provides=IColumn) +class DatatypeSubtypesTableParagraphsColumn(ActionColumn): + """Data type subtypes paragraphs column""" + + weight = 100 + + icon_class = 'fa fa-fw fa-paragraph' + icon_hint = _("Default paragraphs") + + url = 'paragraphs-dialog.html' + modal_target = True + + permission = MANAGE_TOOL_PERMISSION + + +@adapter_config(name='associations', context=(IDataType, IPyAMSLayer, DatatypeSubtypesTable), + provides=IColumn) +class DatatypeSubtypesTableAssociationsColumn(ActionColumn): + """Data type subtypes associations column""" + + weight = 110 + + icon_class = 'fa fa-fw fa-link' + icon_hint = _("Default associations") + + url = 'associations-dialog.html' + modal_target = True + + permission = MANAGE_TOOL_PERMISSION + + +@adapter_config(name='trash', context=(IDataType, IPyAMSLayer, DatatypeSubtypesTable), provides=IColumn) +class DatatypeSubtypesTableTrashColumn(TrashColumn): + """Data type subtypes table trash column""" + + permission = MANAGE_TOOL_PERMISSION + + +@view_config(name='delete-element.json', context=IDataType, request_type=IPyAMSLayer, + permission=MANAGE_TOOL_PERMISSION, renderer='json', xhr=True) +def delete_subtype(request): + """Data subtype delete view""" + return delete_container_element(request, ignore_permission=True) + + +@view_config(name='get-subtypes-table.json', context=ITypedDataManager, request_type=IPyAMSLayer, + permission=MANAGE_TOOL_PERMISSION, renderer='json', xhr=True) +def get_subtypes_table(request): + """Get subtypes table""" + datatype = request.context.get(str(request.params.get('object_name'))) + if datatype is None: + raise NotFound() + table = DatatypeSubtypesTable(datatype, request) + table.update() + return table.render() + + +# +# Data sub-types views +# + +@viewlet_config(name='add-data-subtype.action', context=IDataType, layer=IPyAMSLayer, + view=DatatypeSubtypesTable, manager=IWidgetTitleViewletManager, + permission=MANAGE_TOOL_PERMISSION, weight=1) +class DataSubtypeAddAction(ToolbarAction): + """Data subtype adding action""" + + label = _("Add subtype") + label_css_class = 'fa fa-fw fa-plus' + url = 'add-data-subtype.html' + modal_target = True + + +@pagelet_config(name='add-data-subtype.html', context=IDataType, layer=IPyAMSLayer, + permission=MANAGE_TOOL_PERMISSION) +@ajax_config(name='add-data-subtype.json', context=IDataType, layer=IPyAMSLayer, base=AJAXAddForm) +class DataSubtypeAddForm(AdminDialogAddForm): + """Data subtype add form""" + + legend = _("Add new subtype") + icon_css_class = 'fa fa-fw fa-folder-o' + label_css_class = 'control-label col-md-4' + input_css_class = 'col-md-8' + + fields = field.Fields(ISubType).omit('__parent__', '__name__') + fields['pictogram'].widgetFactory = PictogramSelectFieldWidget + + edit_permission = MANAGE_TOOL_PERMISSION + + def create(self, data): + return SubType() + + def add(self, object): + name = translate_string(object.name, spaces='-') + IDataType(self.context)[name] = object + + def nextURL(self): + return absolute_url(self.context, self.request, 'admin#data-types.html') + + def get_ajax_output(self, changes): + return { + 'status': 'success', + 'message': self.request.localizer.translate(_("Subtype was correctly added.")), + 'events': [ + get_json_table_refresh_event(self.context, self.request, DatatypeSubtypesTable) + ] + } + + +@subscriber(IDataExtractedEvent, form_selector=DataSubtypeAddForm) +def handle_subtype_add_form_data_extraction(event): + """Check new data subtype for existing name""" + context = event.form.context + manager = IDataType(context) + name = event.data.get('name') + if translate_string(name, spaces='-') in manager: + event.form.widgets.errors += (Invalid(_("Specified subtype name is already used!")),) + + +@pagelet_config(name='properties.html', context=ISubType, layer=IPyAMSLayer, permission=MANAGE_TOOL_PERMISSION) +@ajax_config(name='properties.json', context=ISubType, layer=IPyAMSLayer) +class DataSubtypeEditForm(AdminDialogEditForm): + """Data subtype edit form""" + + prefix = 'subtype_properties.' + + legend = _("Data subtype properties") + icon_css_class = 'fa fa-fw fa-folder-o' + label_css_class = 'control-label col-md-4' + input_css_class = 'col-md-8' + + fields = field.Fields(ISubType).omit('__parent__', '__name__') + fields['pictogram'].widgetFactory = PictogramSelectFieldWidget + + edit_permission = MANAGE_TOOL_PERMISSION + + def updateWidgets(self, prefix=None): + super(DataSubtypeEditForm, self).updateWidgets(prefix) + if 'name' in self.widgets: + self.widgets['name'].mode = DISPLAY_MODE + + def get_ajax_output(self, changes): + if 'label' in changes.get(IBaseDataType, ()): + target = get_parent(self.context, IDataType) + return { + 'status': 'success', + 'message': self.request.localizer.translate(self.successMessage), + 'events': [ + get_json_table_refresh_event(target, self.request, DatatypeSubtypesTable) + ] + } + else: + return super(self.__class__, self).get_ajax_output(changes) + + +# +# Typed shared content views +# + +class TypedSharedContentAddForm(SharedContentAddForm): + """Typed shared content add form""" + + fields = field.Fields(IWfTypedSharedContent).select('title', 'data_type', 'notepad') + + def updateWidgets(self, prefix=None): + super(TypedSharedContentAddForm, self).updateWidgets(prefix) + if 'data_type' in self.widgets: + self.widgets['data_type'].prompt = True + self.widgets['data_type'].promptMessage = _("Select content type...") + + +@pagelet_config(name='properties.html', context=IWfTypedSharedContent, layer=IPyAMSLayer, + permission=MANAGE_CONTENT_PERMISSION) +@ajax_config(name='properties.json', context=IWfTypedSharedContent, layer=IPyAMSLayer, + permission=MANAGE_CONTENT_PERMISSION) +class TypedSharedContentPropertiesEditForm(SharedContentPropertiesEditForm): + """Typed shared content properties edit form""" + + interface = IWfTypedSharedContent + + @property + def fields(self): + fields = field.Fields(self.interface).select('title', 'short_name', 'content_url', + 'data_type', 'header', 'description', 'notepad') + fields['title'].widgetFactory = I18nSEOTextLineFieldWidget + if not self.context.handle_content_url: + fields = fields.omit('content_url') + if not self.context.handle_header: + fields = fields.omit('header') + if not self.context.handle_description: + fields = fields.omit('description') + return fields + + +# +# Typed shared content custom fields edit form +# + +@implementer(IWidgetForm, IInnerPage) +class TypedSharedContentTypeFieldsEditForm(AdminEditForm): + """Typed shared content type fields properties edit form""" + + prefix = 'type_properties.' + + @property + def legend(self): + translate = self.request.localizer.translate + data_type = self.context.get_data_type() + return translate(_("Custom properties for type « {0} »")).format( + II18n(data_type).query_attribute('label', request=self.request)) + + @property + def fields(self): + data_type = self.context.get_data_type() + if data_type is not None: + manager = get_parent(self.context, ITypedSharedTool) + return field.Fields(manager.shared_content_types_fields).select(*data_type.field_names) diff -r 2ef1161388a9 -r c32aea65edf3 src/pyams_content/shared/common/zmi/types/manager.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pyams_content/shared/common/zmi/types/manager.py Thu May 23 18:01:42 2019 +0200 @@ -0,0 +1,209 @@ +# +# Copyright (c) 2008-2019 Thierry Florac +# 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 json + +from pyramid.decorator import reify +from pyramid.view import view_config +from z3c.table.interfaces import IColumn, IValues + +from pyams_content.interfaces import MANAGE_TOOL_PERMISSION +from pyams_content.shared.common.interfaces.types import ITypedDataManager, ITypedSharedTool +from pyams_content.zmi import pyams_content +from pyams_form.security import ProtectedFormObjectMixin +from pyams_pagelet.pagelet import pagelet_config +from pyams_skin.container import delete_container_element +from pyams_skin.layer import IPyAMSLayer +from pyams_skin.table import ActionColumn, BaseTable, NameColumn, SorterColumn, TrashColumn +from pyams_skin.viewlet.menu import MenuItem +from pyams_utils.adapter import ContextRequestViewAdapter, adapter_config +from pyams_utils.fanstatic import get_resource_path +from pyams_utils.url import absolute_url +from pyams_viewlet.viewlet import viewlet_config +from pyams_zmi.interfaces.menu import IPropertiesMenu +from pyams_zmi.layer import IAdminLayer +from pyams_zmi.view import ContainerAdminView + +from pyams_content import _ + + +@viewlet_config(name='data-types.menu', context=ITypedSharedTool, layer=IAdminLayer, + manager=IPropertiesMenu, permission=MANAGE_TOOL_PERMISSION, weight=5) +class TypedSharedToolTypesMenu(MenuItem): + """Typed shared tool types menu""" + + label = _("Data types") + icon_class = 'fa-folder-o' + url = '#data-types.html' + + +# +# Typed shared data types manager target views +# + +class TypedSharedToolTypesTable(ProtectedFormObjectMixin, BaseTable): + """Typed shared tool types table""" + + prefix = 'types' + + hide_header = 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): + attributes = super(TypedSharedToolTypesTable, self).data_attributes + attributes['table'] = { + 'id': self.id, + 'data-ams-plugins': 'pyams_content', + 'data-ams-plugin-pyams_content-src': get_resource_path(pyams_content), + 'data-ams-location': absolute_url(ITypedDataManager(self.context), self.request), + 'data-ams-tablednd-drag-handle': 'td.sorter', + 'data-ams-tablednd-drop-target': 'set-types-order.json' + } + return attributes + + @reify + def values(self): + return list(super(TypedSharedToolTypesTable, self).values) + + def render(self): + if not self.values: + translate = self.request.localizer.translate + return translate(_("No currently defined data type.")) + return super(TypedSharedToolTypesTable, self).render() + + +@adapter_config(context=(ITypedSharedTool, IPyAMSLayer, TypedSharedToolTypesTable), provides=IValues) +class TypedSharedToolTypesValues(ContextRequestViewAdapter): + """Typed shared tool types table values adapter""" + + @property + def values(self): + return ITypedDataManager(self.context).values() + + +@adapter_config(name='sorter', context=(ITypedSharedTool, IPyAMSLayer, TypedSharedToolTypesTable), + provides=IColumn) +class TypedSharedToolTypesSorterColumn(ProtectedFormObjectMixin, SorterColumn): + """Typed shared tool types sorter column""" + + +@view_config(name='set-types-order.json', context=ITypedDataManager, request_type=IPyAMSLayer, + permission=MANAGE_TOOL_PERMISSION, renderer='json', xhr=True) +def set_data_types_order(request): + """Update data types order""" + order = list(map(str, json.loads(request.params.get('names')))) + request.context.updateOrder(order) + return {'status': 'success'} + + +@adapter_config(name='name', context=(ITypedSharedTool, IPyAMSLayer, TypedSharedToolTypesTable), + provides=IColumn) +class TypedSharedToolTypesNameColumn(NameColumn): + """Typed shared tool types name column""" + + _header = _("Data type label") + + def renderCell(self, item): + return '' \ + ' ' \ + ' ' \ + ' ' \ + '   {title}' \ + '
'.format( + hint=self.request.localizer.translate(_("Click to see subtypes")), + title=super(TypedSharedToolTypesNameColumn, self).renderCell(item)) + + +@adapter_config(name='paragraphs', context=(ITypedSharedTool, IPyAMSLayer, TypedSharedToolTypesTable), + provides=IColumn) +class TypedSharedToolTypesParagraphsColumn(ActionColumn): + """Typed shared tool types paragraphs column""" + + weight = 100 + + icon_class = 'fa fa-fw fa-paragraph' + icon_hint = _("Default paragraphs") + + url = 'paragraphs-dialog.html' + modal_target = True + + permission = MANAGE_TOOL_PERMISSION + + +@adapter_config(name='associations', context=(ITypedSharedTool, IPyAMSLayer, TypedSharedToolTypesTable), + provides=IColumn) +class TypedSharedToolTypesAssociationsColumn(ActionColumn): + """Typed shared tool types associations column""" + + weight = 110 + + icon_class = 'fa fa-fw fa-link' + icon_hint = _("Default associations") + + url = 'associations-dialog.html' + modal_target = True + + permission = MANAGE_TOOL_PERMISSION + + +@adapter_config(name='themes', context=(ITypedSharedTool, IPyAMSLayer, TypedSharedToolTypesTable), + provides=IColumn) +class TypedSharedToolTypesThemesColumn(ActionColumn): + """Typed shared tool types themes column""" + + weight = 120 + + icon_class = 'fa fa-fw fa-tags' + icon_hint = _("Default themes") + + url = 'themes-dialog.html' + modal_target = True + + permission = MANAGE_TOOL_PERMISSION + + +@adapter_config(name='trash', context=(ITypedSharedTool, IPyAMSLayer, TypedSharedToolTypesTable), + provides=IColumn) +class TypedSharedToolTypesTrashColumn(TrashColumn): + """Typed shared tool types trash column""" + + permission = MANAGE_TOOL_PERMISSION + + +@view_config(name='delete-element.json', context=ITypedDataManager, request_type=IPyAMSLayer, + permission=MANAGE_TOOL_PERMISSION, renderer='json', xhr=True) +def delete_data_type(request): + """Data type delete view""" + return delete_container_element(request, ignore_permission=True) + + +@pagelet_config(name='data-types.html', context=ITypedSharedTool, layer=IPyAMSLayer, + permission=MANAGE_TOOL_PERMISSION) +class TypedSharedToolTypesView(ContainerAdminView): + """Typed shared tool types view""" + + title = _("Content data types") + table_class = TypedSharedToolTypesTable