Module refactoring
authorThierry Florac <thierry.florac@onf.fr>
Thu, 23 May 2019 18:01:42 +0200
changeset 1317 c32aea65edf3
parent 1316 2ef1161388a9
child 1318 bab66a5a7d2f
Module refactoring
src/pyams_content/shared/common/zmi/types.py
src/pyams_content/shared/common/zmi/types/__init__.py
src/pyams_content/shared/common/zmi/types/manager.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 <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 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 '<span data-ams-stop-propagation="true" ' \
-               '      data-ams-click-handler="MyAMS.skin.switchCellContent" ' \
-               '      data-ams-switch-handler="get-subtypes-table.json" ' \
-               '      data-ams-switch-target=".subtypes">' \
-               '    <span class="small hint" title="{hint}" data-ams-hint-gravity="e">' \
-               '        <i class="fa fa-plus-square-o switch"></i>' \
-               '    </span>' \
-               '</span>&nbsp;&nbsp;&nbsp;<span class="title">{title}</span>' \
-               '<div class="inner-table-form subtypes margin-x-10 margin-bottom-0 padding-left-5"></div>'.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)
--- /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 <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 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)
--- /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 <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 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 '<span data-ams-stop-propagation="true" ' \
+               '      data-ams-click-handler="MyAMS.skin.switchCellContent" ' \
+               '      data-ams-switch-handler="get-subtypes-table.json" ' \
+               '      data-ams-switch-target=".subtypes">' \
+               '    <span class="small hint" title="{hint}" data-ams-hint-gravity="e">' \
+               '        <i class="fa fa-plus-square-o switch"></i>' \
+               '    </span>' \
+               '</span>&nbsp;&nbsp;&nbsp;<span class="title">{title}</span>' \
+               '<div class="inner-table-form subtypes margin-x-10 margin-bottom-0 padding-left-5"></div>'.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