# HG changeset patch # User Thierry Florac # Date 1510312063 -3600 # Node ID 78422a1c4228b2c291dbff87fe74f3a4439cba8f # Parent 0c63253d75bdf2e886f72c725697414f4071602c Create base infrastructure for typed contents management diff -r 0c63253d75bd -r 78422a1c4228 src/pyams_content/shared/common/interfaces/types.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pyams_content/shared/common/interfaces/types.py Fri Nov 10 12:07:43 2017 +0100 @@ -0,0 +1,102 @@ +# +# 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 standard library + +# import interfaces +from pyams_content.shared.common.interfaces import ISharedTool +from zope.container.interfaces import IContainer +from zope.location.interfaces import ILocation + +# import packages +from pyams_i18n.schema import I18nTextLineField, I18nThumbnailImageField +from zope.container.constraints import contains +from zope.interface import Attribute +from zope.schema import TextLine, List, Choice + +from pyams_content import _ + + +class IBaseDataType(ILocation): + """Data interface for data-types and sub-types""" + + name = TextLine(title=_("Name"), + description=_("Name of this data type; must be unique between all data types"), + required=True) + + label = I18nTextLineField(title=_("Label"), + required=True) + + navigation_label = I18nTextLineField(title=_("Navigation label"), + description=_("Label used for navigation entries"), + required=False) + + tabfolder_label = I18nTextLineField(title=_("Tab-folder label"), + description=_("Label used to include into tab folder"), + required=False) + + seealso_label = I18nTextLineField(title=_("'See also' label"), + description=_("This label can be used when contents of this type will be " + "displayed in a 'See also' entries block"), + required=False) + + single_label = I18nTextLineField(title=_("'Single value' label"), + description=_("Label given to this type when a single value is displayed"), + required=False) + + seeall_label = I18nTextLineField(title=_("'Link to list' label"), + description=_("Label used to display a link to a list of items of this type"), + required=False) + + next_label = I18nTextLineField(title=_("Next content label"), + description=_("Label used to announce next date for this type"), + required=False) + + pictogram = I18nThumbnailImageField(title=_("Pictogram"), + description=_("Image associated to this data type"), + required=False) + + +class ISubType(IBaseDataType): + """Data sub-type interface""" + + +class IDataType(IBaseDataType, IContainer): + """Data type interface""" + + contains(ISubType) + + field_names = List(title=_("Field names"), + description=_("List of fields associated with this data type"), + value_type=Choice(vocabulary='PyAMS types interface fields')) + + +# +# Types data manager interfaces +# + +DATA_MANAGER_ANNOTATION_KEY = 'pyams_content.types.manager' + + +class ITypedDataManager(IContainer): + """Typed shared data manager interface""" + + contains(IDataType) + + +class ITypedSharedTool(ISharedTool): + """Shared tool containing typed data""" + + shared_content_types_fields = Attribute("Content fields interface") diff -r 0c63253d75bd -r 78422a1c4228 src/pyams_content/shared/common/types.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pyams_content/shared/common/types.py Fri Nov 10 12:07:43 2017 +0100 @@ -0,0 +1,134 @@ +# +# 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 standard library + +# import interfaces +from pyams_content.component.extfile.interfaces import IExtFileContainerTarget +from pyams_content.component.links.interfaces import ILinkContainerTarget +from pyams_content.component.paragraph.interfaces import IParagraphContainerTarget +from pyams_content.interfaces import MANAGE_TOOL_PERMISSION +from pyams_content.shared.common.interfaces.types import IDataType, ISubType, IBaseDataType, ITypedSharedTool, \ + ITypedDataManager, DATA_MANAGER_ANNOTATION_KEY +from pyams_form.interfaces.form import IFormContextPermissionChecker +from zope.location.interfaces import ISublocations +from zope.traversing.interfaces import ITraversable + +# import packages +from persistent import Persistent +from pyams_content.shared.common.manager import SharedTool +from pyams_i18n.property import I18nFileProperty +from pyams_utils.adapter import adapter_config, ContextAdapter +from pyams_utils.registry import get_global_registry +from pyams_utils.request import check_request +from pyams_utils.traversing import get_parent +from pyams_utils.vocabulary import vocabulary_config +from zope.annotation.interfaces import IAnnotations +from zope.container.contained import Contained +from zope.container.ordered import OrderedContainer +from zope.interface import implementer +from zope.lifecycleevent import ObjectCreatedEvent +from zope.location import locate +from zope.schema import getFieldsInOrder +from zope.schema.fieldproperty import FieldProperty +from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm + + +class BaseDataType(Persistent, Contained): + """Base data type""" + + label = FieldProperty(IBaseDataType['label']) + navigation_label = FieldProperty(IBaseDataType['navigation_label']) + tabfolder_label = FieldProperty(IBaseDataType['tabfolder_label']) + seealso_label = FieldProperty(IBaseDataType['seealso_label']) + single_label = FieldProperty(IBaseDataType['single_label']) + seeall_label = FieldProperty(IBaseDataType['seeall_label']) + next_label = FieldProperty(IBaseDataType['next_label']) + pictogram = I18nFileProperty(IBaseDataType['pictogram']) + + +@implementer(ISubType, IParagraphContainerTarget, IExtFileContainerTarget, ILinkContainerTarget) +class SubType(BaseDataType): + """Data sub-type persistent class""" + + +@implementer(IDataType, IParagraphContainerTarget, IExtFileContainerTarget, ILinkContainerTarget) +class DataType(BaseDataType, OrderedContainer): + """Data type persistent class""" + + field_names = FieldProperty(IDataType['field_names']) + + +@implementer(ITypedDataManager) +class TypedDataManager(OrderedContainer): + """Data types container persistent class""" + + +@adapter_config(context=IBaseDataType, provides=IFormContextPermissionChecker) +class BaseDatatypePermissionChecker(ContextAdapter): + """Base data type permission checker""" + + edit_permission = MANAGE_TOOL_PERMISSION + + +@implementer(ITypedSharedTool) +class TypedSharedTool(SharedTool): + """Typed shared tool""" + + shared_content_types_fields = None + + +@adapter_config(context=ITypedSharedTool, provides=ITypedDataManager) +def TypedSharedToolDataManagerFactory(context): + """Types shared tool data manager factory""" + annotations = IAnnotations(context) + manager = annotations.get(DATA_MANAGER_ANNOTATION_KEY) + if manager is None: + manager = annotations[DATA_MANAGER_ANNOTATION_KEY] = TypedDataManager() + registry = get_global_registry() + registry.notify(ObjectCreatedEvent(manager)) + locate(manager, context, '++types++') + return manager + + +@adapter_config(name='types', context=ITypedSharedTool, provides=ITraversable) +class TypedSharedToolTypesNamespace(ContextAdapter): + """Typed shared tool ++types++ namespace""" + + def traverse(self, name, furtherpath=None): + return ITypedDataManager(self.context) + + +@adapter_config(name='types', context=ITypedSharedTool, provides=ISublocations) +class TypedSharedToolSublocations(ContextAdapter): + """Typed shared tool sublocations adapter""" + + def sublocations(self): + return ITypedDataManager(self.context).values() + + +@vocabulary_config(name='PyAMS types interface fields') +class TypedSharedToolDataTypesFields(SimpleVocabulary): + """Typed shared tool data types fields vocabulary""" + + def __init__(self, context): + terms = [] + parent = get_parent(context, ITypedSharedTool) + if parent is not None: + request = check_request() + translate = request.localizer.translate + terms = [SimpleTerm(name, title=translate(field.title)) + for name, field in getFieldsInOrder(parent.shared_content_types_fields)] + super(TypedSharedToolDataTypesFields, self).__init__(terms) diff -r 0c63253d75bd -r 78422a1c4228 src/pyams_content/shared/common/zmi/preview.py --- a/src/pyams_content/shared/common/zmi/preview.py Fri Nov 10 12:07:09 2017 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,60 +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 standard library - -# import interfaces -from pyams_content.shared.common.interfaces import IWfSharedContent -from pyams_content.shared.common.interfaces.zmi import IPreviewForm -from pyams_skin.interfaces.viewlet import IToolbarViewletManager -from pyams_skin.layer import IPyAMSLayer -from pyams_utils.interfaces import VIEW_SYSTEM_PERMISSION -from pyams_zmi.layer import IAdminLayer - -# import packages -from pyams_pagelet.pagelet import pagelet_config -from pyams_skin.viewlet.toolbar import ToolbarAction -from pyams_viewlet.viewlet import viewlet_config -from pyams_zmi.form import AdminDialogDisplayForm -from z3c.form import field -from zope.interface import implementer, Interface - -from pyams_content import _ - - -@viewlet_config(name='preview.action', context=IWfSharedContent, layer=IAdminLayer, view=Interface, - manager=IToolbarViewletManager, permisison=VIEW_SYSTEM_PERMISSION, weight=50) -class WfSharedContentPreviewAction(ToolbarAction): - """Shared content preview action""" - - label = _("Preview") - - group_css_class = 'btn-group margin-right-10' - label_css_class = 'fa fa-newspaper-o' - css_class = 'btn btn-xs btn-default' - - url = 'preview.html' - modal_target = True - - -@pagelet_config(name='preview.html', context=IWfSharedContent, layer=IPyAMSLayer, permission=VIEW_SYSTEM_PERMISSION) -@implementer(IPreviewForm) -class WfSharedContentPreviewForm(AdminDialogDisplayForm): - """Shared content preview""" - - legend = _("Content preview") - dialog_class = 'modal-max' - - fields = field.Fields(Interface) diff -r 0c63253d75bd -r 78422a1c4228 src/pyams_content/shared/common/zmi/types.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pyams_content/shared/common/zmi/types.py Fri Nov 10 12:07:43 2017 +0100 @@ -0,0 +1,577 @@ +# +# 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 standard library +import json + +# import interfaces +from pyams_content.interfaces import MANAGE_TOOL_PERMISSION +from pyams_content.shared.common.interfaces.types import ITypedSharedTool, ITypedDataManager, \ + IBaseDataType, IDataType, ISubType +from pyams_i18n.interfaces import II18n +from pyams_skin.interfaces.container import ITableElementName +from pyams_skin.interfaces.viewlet import IWidgetTitleViewletManager +from pyams_skin.layer import IPyAMSLayer +from pyams_viewlet.interfaces import IViewletManager +from pyams_zmi.interfaces.menu import IPropertiesMenu +from pyams_zmi.layer import IAdminLayer +from z3c.form.interfaces import DISPLAY_MODE, IDataExtractedEvent +from z3c.table.interfaces import IValues, IColumn + +# import packages +from pyams_content.shared.common.types import DataType, SubType +from pyams_form.form import AJAXAddForm, AJAXEditForm +from pyams_form.security import ProtectedFormObjectMixin +from pyams_pagelet.pagelet import pagelet_config +from pyams_skin.table import BaseTable, SorterColumn, TrashColumn, NameColumn, ActionColumn +from pyams_skin.viewlet.menu import MenuItem +from pyams_skin.viewlet.toolbar import ToolbarAction +from pyams_utils.adapter import adapter_config, ContextRequestViewAdapter, ContextRequestAdapter +from pyams_utils.traversing import get_parent +from pyams_utils.unicode import translate_string +from pyams_utils.url import absolute_url +from pyams_viewlet.viewlet import viewlet_config +from pyams_zmi.form import AdminDialogAddForm, AdminDialogEditForm +from pyams_zmi.view import ContainerAdminView +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 zope.interface import Invalid + +from pyams_content import _ + + +@viewlet_config(name='data-types.menu', context=ITypedSharedTool, layer=IAdminLayer, + manager=IPropertiesMenu, permission=MANAGE_TOOL_PERMISSION, weight=20) +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""" + + id = 'types_list' + 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': + '/--static--/pyams_content/js/pyams_content{MyAMS.devext}.js', + '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""" + + +@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='trash', context=(ITypedSharedTool, IPyAMSLayer, TypedSharedToolTypesTable), + provides=IColumn) +class TypedSharedToolTypesTrashColumn(TrashColumn): + """Typed shared tool types trash column""" + + permission = MANAGE_TOOL_PERMISSION + + +@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 + + +# +# Typed shared data types manager views +# + +@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""" + translate = request.localizer.translate + name = request.params.get('object_name') + if not name: + return {'status': 'message', + 'messagebox': {'status': 'error', + 'content': translate(_("No provided object_name argument!"))}} + if name not in request.context: + return {'status': 'message', + 'messagebox': {'status': 'error', + 'content': translate(_("Given data type doesn't exist!"))}} + del request.context[name] + return {'status': 'success'} + + +@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'} + + +# +# 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) +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' + + fields = field.Fields(IDataType).omit('__parent__', '__name__') + + ajax_handler = 'add-data-type.json' + 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 absolute_url(self.context, self.request, 'admin#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!")),) + + +@view_config(name='add-data-type.json', context=ITypedSharedTool, request_type=IPyAMSLayer, + permission=MANAGE_TOOL_PERMISSION, renderer='json', xhr=True) +class DataTypeAJAXAddForm(AJAXAddForm, DataTypeAddForm): + """Data type add form, JSON renderer""" + + def nextURL(self): + return '#data-types.html' + + +@pagelet_config(name='properties.html', context=IDataType, layer=IPyAMSLayer, permission=MANAGE_TOOL_PERMISSION) +class DataTypeEditForm(AdminDialogEditForm): + """Data type edit form""" + + 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' + + fields = field.Fields(IDataType).omit('__parent__', '__name__') + + ajax_handler = 'properties.json' + 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 + + +@view_config(name='properties.json', context=IDataType, request_type=IPyAMSLayer, + permission=MANAGE_TOOL_PERMISSION, renderer='json', xhr=True) +class DataTypeAJAXEditForm(AJAXEditForm, DataTypeEditForm): + """Data type edit form, JSON renderer""" + + +# +# Subtypes views +# + +class DatatypeSubtypesTable(BaseTable): + """Data type subtypes table""" + + @property + def id(self): + return 'subtypes_{0}_list'.format(self.context.__name__) + + 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-plugins': 'pyams_content', + 'data-ams-plugin-pyams_content-src': + '/--static--/pyams_content/js/pyams_content{MyAMS.devext}.js', + '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', {}).setdefault('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""" + + +@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='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() + + +@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'} + + +@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""" + translate = request.localizer.translate + name = request.params.get('object_name') + if not name: + return {'status': 'message', + 'messagebox': {'status': 'error', + 'content': translate(_("No provided object_name argument!"))}} + if name not in request.context: + return {'status': 'message', + 'messagebox': {'status': 'error', + 'content': translate(_("Given data subtype doesn't exist!"))}} + del request.context[name] + return {'status': 'success'} + + +# +# 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) +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__') + + ajax_handler = 'add-data-subtype.json' + 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') + + +@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!")),) + + +@view_config(name='add-data-subtype.json', context=IDataType, request_type=IPyAMSLayer, + permission=MANAGE_TOOL_PERMISSION, renderer='json', xhr=True) +class DataSubtypeAJAXAddForm(AJAXAddForm, DataSubtypeAddForm): + """Data subtype add form, JSON renderer""" + + def get_ajax_output(self, changes): + subtypes_table = DatatypeSubtypesTable(self.context, self.request) + subtypes_table.update() + return {'status': 'success', + 'message': self.request.localizer.translate(_("Subtype was correctly added.")), + 'events': [{ + 'event': 'PyAMS_content.changed_item', + 'options': { + 'handler': 'PyAMS_content.types.refreshSubtypes', + 'object_name': subtypes_table.id, + 'table': subtypes_table.render() + } + }]} + + +@pagelet_config(name='properties.html', context=ISubType, layer=IPyAMSLayer, permission=MANAGE_TOOL_PERMISSION) +class DataSubtypeEditForm(AdminDialogEditForm): + """Data subtype edit form""" + + 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__') + + ajax_handler = 'properties.json' + 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 + + +@view_config(name='properties.json', context=ISubType, request_type=IPyAMSLayer, + permission=MANAGE_TOOL_PERMISSION, renderer='json', xhr=True) +class DataSubtypeAJAXEditForm(AJAXEditForm, DataSubtypeEditForm): + """Data subtype edit form, JSON renderer""" + + def get_ajax_output(self, changes): + if 'label' in changes.get(IBaseDataType, ()): + target = get_parent(self.context, IDataType) + subtypes_table = DatatypeSubtypesTable(target, self.request) + subtypes_table.update() + return {'status': 'success', + 'message': self.request.localizer.translate(self.successMessage), + 'events': [{ + 'event': 'PyAMS_content.changed_item', + 'options': { + 'handler': 'PyAMS_content.types.refreshSubtypes', + 'object_name': subtypes_table.id, + 'table': subtypes_table.render() + } + }]} + else: + return super(DataSubtypeAJAXEditForm, self).get_ajax_output(changes) diff -r 0c63253d75bd -r 78422a1c4228 src/pyams_content/skin/resources/js/pyams_content.js --- a/src/pyams_content/skin/resources/js/pyams_content.js Fri Nov 10 12:07:09 2017 +0100 +++ b/src/pyams_content/skin/resources/js/pyams_content.js Fri Nov 10 12:07:43 2017 +0100 @@ -214,6 +214,45 @@ /** + * Types management + */ + types: { + + switchSubtypes: function(element) { + var source = $(this); + var switcher = $('i.switch', source); + var td = source.parents('td'); + var subtypes = $('.subtypes', td); + var datatype = source.parents('tr'); + if (switcher.hasClass('fa-plus-square-o')) { + var container = datatype.parents('table'); + subtypes.html('

'); + MyAMS.ajax.post(container.data('ams-location') + '/get-subtypes-table.json', + {object_name: datatype.data('ams-element-name')}, + function(result) { + subtypes.html(result); + if (result) { + MyAMS.initContent(subtypes); + switcher.removeClass('fa-plus-square-o') + .addClass('fa-minus-square-o'); + } + }); + } else { + MyAMS.skin.cleanContainer(subtypes); + subtypes.empty(); + switcher.removeClass('fa-minus-square-o') + .addClass('fa-plus-square-o'); + } + }, + + refreshSubtypes: function(options) { + // Reload widget + PyAMS_content.refreshTable(options); + } + }, + + + /** * Paragraphs management */ paragraphs: { @@ -247,6 +286,16 @@ }; }, + refreshParagraphs: function(options) { + // Reload widget + var widget = PyAMS_content.refreshTable(options); + // Check fieldset state + var legend = widget.siblings('legend'); + if (legend.parents('fieldset:first').hasClass('switched')) { + legend.click(); + } + }, + refreshParagraph: function(changes) { var container = $('table[id="paragraphs_list"]'); var para = $('tr[data-ams-element-name="' + changes.object_name + '"]', container); diff -r 0c63253d75bd -r 78422a1c4228 src/pyams_content/skin/resources/js/pyams_content.min.js --- a/src/pyams_content/skin/resources/js/pyams_content.min.js Fri Nov 10 12:07:09 2017 +0100 +++ b/src/pyams_content/skin/resources/js/pyams_content.min.js Fri Nov 10 12:07:43 2017 +0100 @@ -1,1 +1,1 @@ -(function(c,b){var e=b.MyAMS;var d={refreshForm:function(g){var f=c("#"+g.object_name);f.replaceWith(c(g.form));f=c("#"+g.object_name);e.initContent(f);return f},refreshTable:function(f){var g=c("#"+f.object_name).parent(".ams-widget");g.replaceWith(c(f.table));g=c("#"+f.object_name).parent(".ams-widget");e.initContent(g);return g},TinyMCE:{initEditor:function(f){f.image_list=d.TinyMCE.getImagesList;f.link_list=d.TinyMCE.getLinksList;return f},getImagesList:function(i){var f=c(document.activeElement).parents("form");if(f.exists()){var g=f.attr("data-ams-form-handler")||f.attr("action");var h=g.substr(0,g.lastIndexOf("/")+1);return e.ajax.post(h+"get-images-list.json",{},i)}},getLinksList:function(i){var f=c(document.activeElement).parents("form");if(f.exists()){var g=f.attr("data-ams-form-handler")||f.attr("action");var h=g.substr(0,g.lastIndexOf("/")+1);return e.ajax.post(h+"get-links-list.json",{},i)}}},profile:{switchFavorite:function(){var g=c(this);var f=g.data("sequence-oid");e.ajax.post("switch-user-favorite.json",{oid:f},function(h,i){if(h.favorite){g.removeClass("fa-star-o").addClass("fa-star")}else{g.removeClass("fa-star").addClass("fa-star-o")}})}},galleries:{updateImageTitle:function(f){c('img[id="'+f.image_id+'"]').attr("original-title",f.title)},switchImageVisibility:function(f){return function(){var h=c(this);var i=h.parents(".image");var g=i.parents(".gallery");e.ajax.post(g.data("ams-location")+"/set-image-visibility.json",{object_name:i.data("ams-element-name")},function(j,k){if(j.visible){c("i",h).attr("class","fa fa-fw fa-eye");h.parents(".btn-group").siblings("a.fancyimg").removeClass("not-visible")}else{c("i",h).attr("class","fa fa-fw fa-eye-slash text-danger");h.parents(".btn-group").siblings("a.fancyimg").addClass("not-visible")}})}},setOrder:function(h,i){if(i&&i.item.hasClass("already-dropped")){return}var f=i.item.parents(".gallery");var g=c(".image",f).listattr("data-ams-element-name");e.ajax.post(f.data("ams-location")+"/set-images-order.json",{images:JSON.stringify(g)})},removeFile:function(f){return function(){var g=c(this);e.skin.bigBox({title:e.i18n.WARNING,content:'  '+e.i18n.DELETE_WARNING,buttons:e.i18n.BTN_OK_CANCEL},function(k){if(k===e.i18n.BTN_OK){var j=g.parents(".gallery");var i=j.data("ams-location");var l=g.parents(".image");var h=l.data("ams-element-name");e.ajax.post(i+"/delete-element.json",{object_name:h},function(m,n){l.remove()})}})}},afterFancyboxLoad:function(h,g){var f=h.element;if(f.hasClass("not-visible")){h.inner.prepend('
')}}},imgmap:{init:function(){var f=c(this);e.ajax.check(c.fn.canvasAreaDraw,"/--static--/pyams_content/js/jquery-canvasAreaDraw"+e.devext+".js",function(){f.canvasAreaDraw({imageUrl:f.data("ams-image-url")})})},initSummary:function(){var f=c(this);e.ajax.check(c.fn.mapster,"/--static--/pyams_content/js/jquery-imagemapster-1.2.10"+e.devext+".js",function(){f.mapster({fillColor:"ff0000",fillOpacity:0.35,selected:true,highlight:true,staticState:true})})}},associations:{refreshAssociations:function(f){var h=d.refreshTable(f);var g=h.siblings("legend");if(g.parents("fieldset:first").hasClass("switched")){g.click()}},switchVisibility:function(f){return function(){var i=c(this);var h=i.parents("tr");var g=h.parents("table");e.ajax.post(g.data("ams-location")+"/set-association-visibility.json",{object_name:h.data("ams-element-name")},function(j,k){if(j.visible){c("i",i).attr("class","fa fa-fw fa-eye")}else{c("i",i).attr("class","fa fa-fw fa-eye-slash text-danger")}})}}},paragraphs:{preReload:function(){d.paragraphs.switched=c("i.switch.fa-minus-square-o","#paragraphs_list").parents("tr").listattr("id")},postReload:function(){c(d.paragraphs.switched).each(function(){c("i.switch.fa-plus-square-o",'[id="'+this+'"]').parents("div:first").click()});delete d.paragraphs.switched},switchVisibility:function(f){return function(){var i=c(this);var g=i.parents("tr");var h=g.parents("table");e.ajax.post(h.data("ams-location")+"/set-paragraph-visibility.json",{object_name:g.data("ams-element-name")},function(j,k){if(j.visible){c("i",i).attr("class","fa fa-fw fa-eye")}else{c("i",i).attr("class","fa fa-fw fa-eye-slash text-danger")}})}},refreshParagraph:function(h){var g=c('table[id="paragraphs_list"]');var f=c('tr[data-ams-element-name="'+h.object_name+'"]',g);c("span.title",f).html(h.title||" - - - - - - - -")},switchEditor:function(h){var k=c(this);var j=c("i.switch",k);var l=k.parents("td");var i=c(".editor",l);var f=k.parents("tr");if(j.hasClass("fa-plus-square-o")){var g=f.parents("table");i.html('

');e.ajax.post(g.data("ams-location")+"/get-paragraph-editor.json",{object_name:f.data("ams-element-name")},function(m){i.html(m);if(m){e.initContent(i);j.removeClass("fa-plus-square-o").addClass("fa-minus-square-o");f.data("ams-disabled-handlers",true)}})}else{e.skin.cleanContainer(i);i.empty();j.removeClass("fa-minus-square-o").addClass("fa-plus-square-o");f.removeData("ams-disabled-handlers")}},switchAllEditors:function(g){var i=c(this);var h=c("i",i);var f=i.parents("table");if(h.hasClass("fa-plus-square-o")){h.removeClass("fa-plus-square-o").addClass("fa-cog fa-spin");e.ajax.post(f.data("ams-location")+"/get-paragraphs-editors.json",{},function(k){for(var l in k){if(!k.hasOwnProperty(l)){continue}var j=c('tr[data-ams-element-name="'+l+'"]',f);var m=c(".editor",j);if(m.is(":empty")){m.html(k[l]);e.initContent(m)}c(".fa-plus-square-o",j).removeClass("fa-plus-square-o").addClass("fa-minus-square-o");j.data("ams-disabled-handlers",true)}if(!c("i.fa-plus-square-o",c("tbody",f)).exists()){h.removeClass("fa-cog fa-spin").addClass("fa-minus-square-o")}})}else{c(".editor",f).each(function(){e.skin.cleanContainer(c(this));c(this).empty()});c(".fa-minus-square-o",f).removeClass("fa-minus-square-o").addClass("fa-plus-square-o");c("tr",f).removeData("ams-disabled-handlers")}},updateToolbar:function(h){var g=c('table[id="paragraphs_list"]');var f=c('tr[data-ams-element-name="'+h.object_name+'"]',g);var i=c(".title-toolbar",f);i.replaceWith(h.toolbar_tag);i=c(".title-toolbar",f);e.initContent(i)},updateMarkers:function(i){var h=c('table[id="paragraphs_list"]');var f=c('tr[data-ams-element-name="'+i.object_name+'"]',h);var j=c(".title-toolbar",f);var g=c("DIV.action."+i.marker_type,j);if(g.exists()){g.replaceWith(i.marker_tag)}else{c(i.marker_tag).appendTo(j)}if(i.marker_tag){g=c("DIV.action."+i.marker_type,j);e.initContent(g)}e.helpers.sort(j,"weight")}},illustration:{addIllustration:function(){var g=c(this);var h=g.parents(".btn-group");var f=h.siblings("legend.switcher");c("i.fa-plus",f).click();g.hide()}},fields:{refreshField:function(h){var g=c('table[id="form_fields_list"]');var f=c('tr[data-ams-element-name="'+h.object_name+'"]',g);c("td:nth-child(4)",f).html(h.title)},switchVisibility:function(f){return function(){var i=c(this);var h=i.parents("tr");var g=h.parents("table");e.ajax.post(g.data("ams-location")+"/set-form-field-visibility.json",{object_name:h.data("ams-element-name")},function(j,k){if(j.visible){c("i",i).attr("class","fa fa-fw fa-eye")}else{c("i",i).attr("class","fa fa-fw fa-eye-slash text-danger")}})}}},themes:{initExtracts:function(h){var g=c('select[name="form.widgets.thesaurus_name:list"]',h);var f=g.val();var j=c('select[name="form.widgets.extract_name:list"]',h);var i=j.val();if(f){e.jsonrpc.post("getExtracts",{thesaurus_name:f},{url:"/api/thesaurus/json"},function(k){j.empty();c(k.result).each(function(){c("").attr("value",this.id).attr("selected",this.id===i).text(this.text).appendTo(j)})})}j.attr("data-ams-events-handlers",'{"select2-open": "PyAMS_content.themes.getExtracts"}')},getExtracts:function(i){var f=c(i.currentTarget);var h=f.parents("form");var g=c('select[name="form.widgets.thesaurus_name:list"]',h).val();if(g){e.jsonrpc.post("getExtracts",{thesaurus_name:g},{url:"/api/thesaurus/json"},function(l){var k=c('select[name="form.widgets.extract_name:list"]',h);var j=k.data("select2");j.results.empty();j.opts.populateResults.call(j,j.results,l.result,{term:""})})}}},review:{timer:null,timer_duration:{general:30000,chat:5000},initComments:function(g){var f=c(".chat-body",g);f.animate({scrollTop:f[0].scrollHeight},1000);clearInterval(d.review.timer);d.review.timer=setInterval(d.review.updateComments,d.review.timer_duration.chat);e.skin.registerCleanCallback(d.review.cleanCommentsCallback)},cleanCommentsCallback:function(){clearInterval(d.review.timer);d.review.timer=setInterval(d.review.updateComments,d.review.timer_duration.general)},updateComments:function(){var f=c(".badge",'nav a[href="#review-comments.html"]'),h;var g=c(".chat-body",".widget-body");if(g.exists()){h=c(".message",g).length}else{h=parseInt(f.text())}e.ajax.post("get-last-review-comments.json",{count:h},function(i){if(g.exists()){f.removeClass("bg-color-danger").addClass("bg-color-info")}if(h!==i.count){f.text(i.count).removeClass("hidden");if(g.exists()){c(".messages",g).append(i.content);g.animate({scrollTop:g[0].scrollHeight},1000)}if(!g.exists()){f.removeClass("bg-color-info").addClass("bg-color-danger").animate({padding:"3px 12px 2px","margin-right":"9px"},"slow",function(){c(this).animate({padding:"3px 6px 2px","margin-right":"15px"},"slow")})}}})},initCommentData:function(f){var g=c(".chat-body",".widget-body");return{count:c(".message",g).length}},addCommentAction:function(){return function(){c('textarea[name="comment"]').focus()}},addCommentCallback:function(g){var h=c(this);var i=h.parents(".widget-body");c(".messages",i).append(g.content);c('textarea[name="comment"]',h).val("");var f=c(".chat-body",i);f.animate({scrollTop:f[0].scrollHeight},1000);c(".badge",'nav a[href="#review-comments.html"]').text(g.count).removeClass("hidden")}}};b.PyAMS_content=d;c(b.document).on("PyAMS_content.changed_item",function(g,f){e.executeFunctionByName(f.handler,document,f)});var a=c(".badge",'nav a[href="#review-comments.html"]');if(a.exists()){d.review.timer=setInterval(d.review.updateComments,d.review.timer_duration.general)}})(jQuery,this); \ No newline at end of file +!function(a,e){"use strict";var t=e.MyAMS,s={refreshForm:function(e){var s=a("#"+e.object_name);return s.replaceWith(a(e.form)),s=a("#"+e.object_name),t.initContent(s),s},refreshTable:function(e){var s=a("#"+e.object_name).parent(".ams-widget");return s.replaceWith(a(e.table)),s=a("#"+e.object_name).parent(".ams-widget"),t.initContent(s),s},TinyMCE:{initEditor:function(a){return a.image_list=s.TinyMCE.getImagesList,a.link_list=s.TinyMCE.getLinksList,a},getImagesList:function(e){var s=a(document.activeElement).parents("form");if(s.exists()){var n=s.attr("data-ams-form-handler")||s.attr("action"),i=n.substr(0,n.lastIndexOf("/")+1);return t.ajax.post(i+"get-images-list.json",{},e)}},getLinksList:function(e){var s=a(document.activeElement).parents("form");if(s.exists()){var n=s.attr("data-ams-form-handler")||s.attr("action"),i=n.substr(0,n.lastIndexOf("/")+1);return t.ajax.post(i+"get-links-list.json",{},e)}}},profile:{switchFavorite:function(){var e=a(this),s=e.data("sequence-oid");t.ajax.post("switch-user-favorite.json",{oid:s},function(a,t){a.favorite?e.removeClass("fa-star-o").addClass("fa-star"):e.removeClass("fa-star").addClass("fa-star-o")})}},galleries:{updateImageTitle:function(e){a('img[id="'+e.image_id+'"]').attr("original-title",e.title)},switchImageVisibility:function(e){return function(){var e=a(this),s=e.parents(".image"),n=s.parents(".gallery");t.ajax.post(n.data("ams-location")+"/set-image-visibility.json",{object_name:s.data("ams-element-name")},function(t,s){t.visible?(a("i",e).attr("class","fa fa-fw fa-eye"),e.parents(".btn-group").siblings("a.fancyimg").removeClass("not-visible")):(a("i",e).attr("class","fa fa-fw fa-eye-slash text-danger"),e.parents(".btn-group").siblings("a.fancyimg").addClass("not-visible"))})}},setOrder:function(e,s){if(!s||!s.item.hasClass("already-dropped")){var n=s.item.parents(".gallery"),i=a(".image",n).listattr("data-ams-element-name");t.ajax.post(n.data("ams-location")+"/set-images-order.json",{images:JSON.stringify(i)})}},removeFile:function(e){return function(){var e=a(this);t.skin.bigBox({title:t.i18n.WARNING,content:'  '+t.i18n.DELETE_WARNING,buttons:t.i18n.BTN_OK_CANCEL},function(a){if(a===t.i18n.BTN_OK){var s=e.parents(".gallery").data("ams-location"),n=e.parents(".image"),i=n.data("ams-element-name");t.ajax.post(s+"/delete-element.json",{object_name:i},function(a,e){n.remove()})}})}},afterFancyboxLoad:function(a,e){a.element.hasClass("not-visible")&&a.inner.prepend('
')}},imgmap:{init:function(){var e=a(this);t.ajax.check(a.fn.canvasAreaDraw,"/--static--/pyams_content/js/jquery-canvasAreaDraw"+t.devext+".js",function(){e.canvasAreaDraw({imageUrl:e.data("ams-image-url")})})},initSummary:function(){var e=a(this);t.ajax.check(a.fn.mapster,"/--static--/pyams_content/js/jquery-imagemapster-1.2.10"+t.devext+".js",function(){e.mapster({fillColor:"ff0000",fillOpacity:.35,selected:!0,highlight:!0,staticState:!0})})}},associations:{refreshAssociations:function(a){var e=s.refreshTable(a).siblings("legend");e.parents("fieldset:first").hasClass("switched")&&e.click()},switchVisibility:function(e){return function(){var e=a(this),s=e.parents("tr"),n=s.parents("table");t.ajax.post(n.data("ams-location")+"/set-association-visibility.json",{object_name:s.data("ams-element-name")},function(t,s){t.visible?a("i",e).attr("class","fa fa-fw fa-eye"):a("i",e).attr("class","fa fa-fw fa-eye-slash text-danger")})}}},types:{switchSubtypes:function(e){var s=a(this),n=a("i.switch",s),i=s.parents("td"),r=a(".subtypes",i),o=s.parents("tr");if(n.hasClass("fa-plus-square-o")){var l=o.parents("table");r.html('

'),t.ajax.post(l.data("ams-location")+"/get-subtypes-table.json",{object_name:o.data("ams-element-name")},function(a){r.html(a),a&&(t.initContent(r),n.removeClass("fa-plus-square-o").addClass("fa-minus-square-o"))})}else t.skin.cleanContainer(r),r.empty(),n.removeClass("fa-minus-square-o").addClass("fa-plus-square-o")},refreshSubtypes:function(a){s.refreshTable(a)}},paragraphs:{preReload:function(){s.paragraphs.switched=a("i.switch.fa-minus-square-o","#paragraphs_list").parents("tr").listattr("id")},postReload:function(){a(s.paragraphs.switched).each(function(){a("i.switch.fa-plus-square-o",'[id="'+this+'"]').parents("div:first").click()}),delete s.paragraphs.switched},switchVisibility:function(e){return function(){var e=a(this),s=e.parents("tr"),n=s.parents("table");t.ajax.post(n.data("ams-location")+"/set-paragraph-visibility.json",{object_name:s.data("ams-element-name")},function(t,s){t.visible?a("i",e).attr("class","fa fa-fw fa-eye"):a("i",e).attr("class","fa fa-fw fa-eye-slash text-danger")})}},refreshParagraphs:function(a){var e=s.refreshTable(a).siblings("legend");e.parents("fieldset:first").hasClass("switched")&&e.click()},refreshParagraph:function(e){var t=a('table[id="paragraphs_list"]'),s=a('tr[data-ams-element-name="'+e.object_name+'"]',t);a("span.title",s).html(e.title||" - - - - - - - -")},switchEditor:function(e){var s=a(this),n=a("i.switch",s),i=s.parents("td"),r=a(".editor",i),o=s.parents("tr");if(n.hasClass("fa-plus-square-o")){var l=o.parents("table");r.html('

'),t.ajax.post(l.data("ams-location")+"/get-paragraph-editor.json",{object_name:o.data("ams-element-name")},function(a){r.html(a),a&&(t.initContent(r),n.removeClass("fa-plus-square-o").addClass("fa-minus-square-o"),o.data("ams-disabled-handlers",!0))})}else t.skin.cleanContainer(r),r.empty(),n.removeClass("fa-minus-square-o").addClass("fa-plus-square-o"),o.removeData("ams-disabled-handlers")},switchAllEditors:function(e){var s=a(this),n=a("i",s),i=s.parents("table");n.hasClass("fa-plus-square-o")?(n.removeClass("fa-plus-square-o").addClass("fa-cog fa-spin"),t.ajax.post(i.data("ams-location")+"/get-paragraphs-editors.json",{},function(e){for(var s in e)if(e.hasOwnProperty(s)){var r=a('tr[data-ams-element-name="'+s+'"]',i),o=a(".editor",r);o.is(":empty")&&(o.html(e[s]),t.initContent(o)),a(".fa-plus-square-o",r).removeClass("fa-plus-square-o").addClass("fa-minus-square-o"),r.data("ams-disabled-handlers",!0)}a("i.fa-plus-square-o",a("tbody",i)).exists()||n.removeClass("fa-cog fa-spin").addClass("fa-minus-square-o")})):(a(".editor",i).each(function(){t.skin.cleanContainer(a(this)),a(this).empty()}),a(".fa-minus-square-o",i).removeClass("fa-minus-square-o").addClass("fa-plus-square-o"),a("tr",i).removeData("ams-disabled-handlers"))},updateToolbar:function(e){var s=a('table[id="paragraphs_list"]'),n=a('tr[data-ams-element-name="'+e.object_name+'"]',s),i=a(".title-toolbar",n);i.replaceWith(e.toolbar_tag),i=a(".title-toolbar",n),t.initContent(i)},updateMarkers:function(e){var s=a('table[id="paragraphs_list"]'),n=a('tr[data-ams-element-name="'+e.object_name+'"]',s),i=a(".title-toolbar",n),r=a("DIV.action."+e.marker_type,i);r.exists()?r.replaceWith(e.marker_tag):a(e.marker_tag).appendTo(i),e.marker_tag&&(r=a("DIV.action."+e.marker_type,i),t.initContent(r)),t.helpers.sort(i,"weight")}},illustration:{addIllustration:function(){var e=a(this),t=e.parents(".btn-group").siblings("legend.switcher");a("i.fa-plus",t).click(),e.hide()}},fields:{refreshField:function(e){var t=a('table[id="form_fields_list"]'),s=a('tr[data-ams-element-name="'+e.object_name+'"]',t);a("td:nth-child(4)",s).html(e.title)},switchVisibility:function(e){return function(){var e=a(this),s=e.parents("tr"),n=s.parents("table");t.ajax.post(n.data("ams-location")+"/set-form-field-visibility.json",{object_name:s.data("ams-element-name")},function(t,s){t.visible?a("i",e).attr("class","fa fa-fw fa-eye"):a("i",e).attr("class","fa fa-fw fa-eye-slash text-danger")})}}},themes:{initExtracts:function(e){var s=a('select[name="form.widgets.thesaurus_name:list"]',e).val(),n=a('select[name="form.widgets.extract_name:list"]',e),i=n.val();s&&t.jsonrpc.post("getExtracts",{thesaurus_name:s},{url:"/api/thesaurus/json"},function(e){n.empty(),a(e.result).each(function(){a("").attr("value",this.id).attr("selected",this.id===i).text(this.text).appendTo(n)})}),n.attr("data-ams-events-handlers",'{"select2-open": "PyAMS_content.themes.getExtracts"}')},getExtracts:function(e){var s=a(e.currentTarget).parents("form"),n=a('select[name="form.widgets.thesaurus_name:list"]',s).val();n&&t.jsonrpc.post("getExtracts",{thesaurus_name:n},{url:"/api/thesaurus/json"},function(e){var t=a('select[name="form.widgets.extract_name:list"]',s).data("select2");t.results.empty(),t.opts.populateResults.call(t,t.results,e.result,{term:""})})}},review:{timer:null,timer_duration:{general:3e4,chat:5e3},initComments:function(e){var n=a(".chat-body",e);n.animate({scrollTop:n[0].scrollHeight},1e3),clearInterval(s.review.timer),s.review.timer=setInterval(s.review.updateComments,s.review.timer_duration.chat),t.skin.registerCleanCallback(s.review.cleanCommentsCallback)},cleanCommentsCallback:function(){clearInterval(s.review.timer),s.review.timer=setInterval(s.review.updateComments,s.review.timer_duration.general)},updateComments:function(){var e,s=a(".badge",'nav a[href="#review-comments.html"]'),n=a(".chat-body",".widget-body");e=n.exists()?a(".message",n).length:parseInt(s.text()),t.ajax.post("get-last-review-comments.json",{count:e},function(t){n.exists()&&s.removeClass("bg-color-danger").addClass("bg-color-info"),e!==t.count&&(s.text(t.count).removeClass("hidden"),n.exists()&&(a(".messages",n).append(t.content),n.animate({scrollTop:n[0].scrollHeight},1e3)),n.exists()||s.removeClass("bg-color-info").addClass("bg-color-danger").animate({padding:"3px 12px 2px","margin-right":"9px"},"slow",function(){a(this).animate({padding:"3px 6px 2px","margin-right":"15px"},"slow")}))})},initCommentData:function(e){var t=a(".chat-body",".widget-body");return{count:a(".message",t).length}},addCommentAction:function(){return function(){a('textarea[name="comment"]').focus()}},addCommentCallback:function(e){var t=a(this),s=t.parents(".widget-body");a(".messages",s).append(e.content),a('textarea[name="comment"]',t).val("");var n=a(".chat-body",s);n.animate({scrollTop:n[0].scrollHeight},1e3),a(".badge",'nav a[href="#review-comments.html"]').text(e.count).removeClass("hidden")}}};e.PyAMS_content=s,a(e.document).on("PyAMS_content.changed_item",function(a,e){t.executeFunctionByName(e.handler,document,e)}),a(".badge",'nav a[href="#review-comments.html"]').exists()&&(s.review.timer=setInterval(s.review.updateComments,s.review.timer_duration.general))}(jQuery,this);