src/pyams_content/shared/form/zmi/field.py
changeset 170 26aefef3d0aa
child 228 f70686f44c7b
equal deleted inserted replaced
169:483b0f16e9a6 170:26aefef3d0aa
       
     1 #
       
     2 # Copyright (c) 2008-2015 Thierry Florac <tflorac AT ulthar.net>
       
     3 # All Rights Reserved.
       
     4 #
       
     5 # This software is subject to the provisions of the Zope Public License,
       
     6 # Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
       
     7 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
       
     8 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
       
     9 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
       
    10 # FOR A PARTICULAR PURPOSE.
       
    11 #
       
    12 
       
    13 __docformat__ = 'restructuredtext'
       
    14 
       
    15 
       
    16 # import standard library
       
    17 import json
       
    18 
       
    19 # import interfaces
       
    20 from pyams_content.interfaces import MANAGE_CONTENT_PERMISSION
       
    21 from pyams_content.shared.common.interfaces import IWfSharedContent
       
    22 from pyams_content.shared.form.interfaces import IFormFieldContainerTarget, IFormFieldContainer, IFormField, \
       
    23     IFormFieldFactory
       
    24 from pyams_form.interfaces.form import IFormSecurityContext
       
    25 from pyams_i18n.interfaces import II18n
       
    26 from pyams_skin.interfaces.viewlet import IWidgetTitleViewletManager
       
    27 from pyams_skin.layer import IPyAMSLayer
       
    28 from pyams_utils.interfaces import VIEW_SYSTEM_PERMISSION
       
    29 from pyams_zmi.interfaces.menu import IPropertiesMenu
       
    30 from pyams_zmi.layer import IAdminLayer
       
    31 from z3c.form.interfaces import DISPLAY_MODE, IDataExtractedEvent
       
    32 from z3c.table.interfaces import IColumn, IValues
       
    33 
       
    34 # import packages
       
    35 from pyams_content.shared.common.zmi import WfModifiedContentColumnMixin
       
    36 from pyams_content.shared.form.field import FormField
       
    37 from pyams_form.form import AJAXAddForm, AJAXEditForm
       
    38 from pyams_form.security import ProtectedFormObjectMixin
       
    39 from pyams_pagelet.pagelet import pagelet_config
       
    40 from pyams_skin.table import BaseTable, SorterColumn, JsActionColumn, I18nColumn, TrashColumn, I18nValueColumn
       
    41 from pyams_skin.viewlet.menu import MenuItem
       
    42 from pyams_skin.viewlet.toolbar import ToolbarAction
       
    43 from pyams_utils.adapter import adapter_config, ContextRequestViewAdapter
       
    44 from pyams_utils.traversing import get_parent
       
    45 from pyams_utils.url import absolute_url
       
    46 from pyams_viewlet.viewlet import viewlet_config
       
    47 from pyams_zmi.form import AdminDialogAddForm, AdminDialogEditForm
       
    48 from pyams_zmi.view import ContainerAdminView
       
    49 from pyramid.decorator import reify
       
    50 from pyramid.events import subscriber
       
    51 from pyramid.exceptions import NotFound
       
    52 from pyramid.view import view_config
       
    53 from z3c.form import field
       
    54 from z3c.table.column import GetAttrColumn
       
    55 from zope.interface import Invalid
       
    56 
       
    57 from pyams_content import _
       
    58 
       
    59 
       
    60 @viewlet_config(name='form-fields.menu', context=IFormFieldContainerTarget, layer=IAdminLayer,
       
    61                 manager=IPropertiesMenu, permission=VIEW_SYSTEM_PERMISSION, weight=110)
       
    62 class FormFieldsMenu(MenuItem):
       
    63     """Form fields menu"""
       
    64 
       
    65     label = _("Form fields...")
       
    66     icon_class = 'fa-pencil-square-o'
       
    67     url = '#form-fields.html'
       
    68 
       
    69 
       
    70 #
       
    71 # Form fields container view
       
    72 #
       
    73 
       
    74 class FormFieldsContainerTable(ProtectedFormObjectMixin, BaseTable):
       
    75     """Form fields table"""
       
    76 
       
    77     id = 'form_fields_list'
       
    78     hide_header = True
       
    79     sortOn = None
       
    80 
       
    81     @property
       
    82     def cssClasses(self):
       
    83         classes = ['table', 'table-bordered', 'table-striped', 'table-hover', 'table-tight']
       
    84         permission = self.permission
       
    85         if (not permission) or self.request.has_permission(permission, self.context):
       
    86             classes.append('table-dnd')
       
    87         return {'table': ' '.join(classes)}
       
    88 
       
    89     @property
       
    90     def data_attributes(self):
       
    91         attributes = super(FormFieldsContainerTable, self).data_attributes
       
    92         attributes['table'] = {'id': self.id,
       
    93                                'data-ams-plugins': 'pyams_content',
       
    94                                'data-ams-plugin-pyams_content-src':
       
    95                                    '/--static--/pyams_content/js/pyams_content{MyAMS.devext}.js',
       
    96                                'data-ams-location': absolute_url(IFormFieldContainer(self.context), self.request),
       
    97                                'data-ams-tablednd-drag-handle': 'td.sorter',
       
    98                                'data-ams-tablednd-drop-target': 'set-form-fields-order.json'}
       
    99         return attributes
       
   100 
       
   101     @reify
       
   102     def values(self):
       
   103         return list(super(FormFieldsContainerTable, self).values)
       
   104 
       
   105     def render(self):
       
   106         if not self.values:
       
   107             translate = self.request.localizer.translate
       
   108             return translate(_("No currently defined form field."))
       
   109         return super(FormFieldsContainerTable, self).render()
       
   110 
       
   111 
       
   112 @adapter_config(name='sorter', context=(IFormFieldContainerTarget, IPyAMSLayer, FormFieldsContainerTable),
       
   113                 provides=IColumn)
       
   114 class FormFieldsContainerSorterColumn(ProtectedFormObjectMixin, SorterColumn):
       
   115     """Form fields container sorter column"""
       
   116 
       
   117 
       
   118 @adapter_config(name='show-hide', context=(IFormFieldContainerTarget, IPyAMSLayer, FormFieldsContainerTable),
       
   119                 provides=IColumn)
       
   120 class FormFieldsContainerShowHideColumn(ProtectedFormObjectMixin, JsActionColumn):
       
   121     """Form fields container visibility switcher column"""
       
   122 
       
   123     cssClasses = {'th': 'action',
       
   124                   'td': 'action switcher'}
       
   125 
       
   126     icon_class = 'fa fa-fw fa-eye'
       
   127     icon_hint = _("Switch field visibility")
       
   128 
       
   129     url = 'PyAMS_content.fields.switchVisibility'
       
   130 
       
   131     weight = 5
       
   132 
       
   133     def get_icon(self, item):
       
   134         if item.visible:
       
   135             icon_class = 'fa fa-fw fa-eye'
       
   136         else:
       
   137             icon_class = 'fa fa-fw fa-eye-slash text-danger'
       
   138         return '<i class="{icon_class}"></i>'.format(icon_class=icon_class)
       
   139 
       
   140     def renderCell(self, item):
       
   141         if self.permission and not self.request.has_permission(self.permission, context=item):
       
   142             return self.get_icon(item)
       
   143         else:
       
   144             return super(FormFieldsContainerShowHideColumn, self).renderCell(item)
       
   145 
       
   146 
       
   147 @adapter_config(context=FormFieldsContainerShowHideColumn, provides=IFormSecurityContext)
       
   148 def ShowHideColumnSecurityContextFactory(column):
       
   149     """Show/hide column security context factory"""
       
   150     return column.table.context
       
   151 
       
   152 
       
   153 @adapter_config(name='name', context=(IFormFieldContainerTarget, IPyAMSLayer, FormFieldsContainerTable),
       
   154                 provides=IColumn)
       
   155 class FormFieldsContainerNameColumn(I18nColumn, WfModifiedContentColumnMixin, GetAttrColumn):
       
   156     """Form fields container name column"""
       
   157 
       
   158     _header = _("Name")
       
   159 
       
   160     attrName = 'name'
       
   161     weight = 50
       
   162 
       
   163 
       
   164 @adapter_config(name='label', context=(IFormFieldContainerTarget, IPyAMSLayer, FormFieldsContainerTable),
       
   165                 provides=IColumn)
       
   166 class FormFieldsContainerLabelColumn(I18nColumn, WfModifiedContentColumnMixin, I18nValueColumn):
       
   167     """Form fields container label column"""
       
   168 
       
   169     _header = _("Label")
       
   170 
       
   171     attrName = 'label'
       
   172     weight = 55
       
   173 
       
   174 
       
   175 @adapter_config(name='type', context=(IFormFieldContainerTarget, IPyAMSLayer, FormFieldsContainerTable),
       
   176                 provides=IColumn)
       
   177 class FormFieldsContainerTypeColumn(I18nColumn, WfModifiedContentColumnMixin, GetAttrColumn):
       
   178     """Form fields container label column"""
       
   179 
       
   180     _header = _("Field type")
       
   181 
       
   182     weight = 60
       
   183 
       
   184     def getValue(self, obj):
       
   185         adapter = self.request.registry.queryUtility(IFormFieldFactory, name=obj.field_type)
       
   186         if adapter is not None:
       
   187             label = adapter.label
       
   188         else:
       
   189             label = _("-- unknown field type --")
       
   190         return self.request.localizer.translate(label)
       
   191 
       
   192 
       
   193 @adapter_config(name='trash', context=(IFormFieldContainerTarget, IPyAMSLayer, FormFieldsContainerTable),
       
   194                 provides=IColumn)
       
   195 class FormFieldsContainerTrashColumn(ProtectedFormObjectMixin, TrashColumn):
       
   196     """Form fields container trash column"""
       
   197 
       
   198 
       
   199 @adapter_config(context=(IFormFieldContainerTarget, IPyAMSLayer, FormFieldsContainerTable), provides=IValues)
       
   200 class FormFieldsContainerValues(ContextRequestViewAdapter):
       
   201     """Form fields container values"""
       
   202 
       
   203     @property
       
   204     def values(self):
       
   205         return IFormFieldContainer(self.context).values()
       
   206 
       
   207 
       
   208 @pagelet_config(name='form-fields.html', context=IFormFieldContainerTarget, layer=IPyAMSLayer,
       
   209                 permission=VIEW_SYSTEM_PERMISSION)
       
   210 class FormFieldsContainerView(ContainerAdminView):
       
   211     """Form fields container view"""
       
   212 
       
   213     title = _("Form fields list")
       
   214     table_class = FormFieldsContainerTable
       
   215 
       
   216 
       
   217 @view_config(name='set-form-fields-order.json', context=IFormFieldContainer, request_type=IPyAMSLayer,
       
   218              permission=MANAGE_CONTENT_PERMISSION, renderer='json', xhr=True)
       
   219 def set_form_fields_order(request):
       
   220     """Update form fields order"""
       
   221     order = list(map(str, json.loads(request.params.get('names'))))
       
   222     request.context.updateOrder(order)
       
   223     return {'status': 'success'}
       
   224 
       
   225 
       
   226 @view_config(name='set-form-field-visibility.json', context=IFormFieldContainer, request_type=IPyAMSLayer,
       
   227              permission=MANAGE_CONTENT_PERMISSION, renderer='json', xhr=True)
       
   228 def set_form_field_visibility(request):
       
   229     """Set form field visibility"""
       
   230     container = IFormFieldContainer(request.context)
       
   231     paragraph = container.get(str(request.params.get('object_name')))
       
   232     if paragraph is None:
       
   233         raise NotFound()
       
   234     field = IFormField(paragraph)
       
   235     field.visible = not field.visible
       
   236     return {'visible': field.visible}
       
   237 
       
   238 
       
   239 #
       
   240 # Form field views
       
   241 #
       
   242 
       
   243 @viewlet_config(name='add-form-field.action', context=IFormFieldContainerTarget, layer=IAdminLayer,
       
   244                 view=FormFieldsContainerView, manager=IWidgetTitleViewletManager,
       
   245                 permission=MANAGE_CONTENT_PERMISSION, weight=1)
       
   246 class FormFieldAddAction(ProtectedFormObjectMixin, ToolbarAction):
       
   247     """Form field add action"""
       
   248 
       
   249     label = _("Add form field")
       
   250     label_css_class = 'fa fa-fw fa-plus'
       
   251     url = 'add-form-field.html'
       
   252     modal_target = True
       
   253 
       
   254 
       
   255 @pagelet_config(name='add-form-field.html', context=IFormFieldContainerTarget, layer=IPyAMSLayer,
       
   256                 permission=MANAGE_CONTENT_PERMISSION)
       
   257 class FormFieldAddForm(AdminDialogAddForm):
       
   258     """Form field add form"""
       
   259 
       
   260     legend = _("Add form field")
       
   261     icon_css_class = 'fa fa-fw fa-pencil-square-o'
       
   262 
       
   263     fields = field.Fields(IFormField).omit('__parent__', '__name__', 'visible')
       
   264     ajax_handler = 'add-form-field.json'
       
   265     edit_permission = MANAGE_CONTENT_PERMISSION
       
   266 
       
   267     def updateWidgets(self, prefix=None):
       
   268         super(FormFieldAddForm, self).updateWidgets(prefix)
       
   269         if 'description' in self.widgets:
       
   270             self.widgets['description'].widget_css_class = 'textarea'
       
   271 
       
   272     def create(self, data):
       
   273         return FormField()
       
   274 
       
   275     def add(self, object):
       
   276         IFormFieldContainer(self.context)[object.name] = object
       
   277 
       
   278 
       
   279 @subscriber(IDataExtractedEvent, form_selector=FormFieldAddForm)
       
   280 def handle_new_form_field_data_extraction(event):
       
   281     """Handle new form field form data extraction"""
       
   282     container = IFormFieldContainer(event.form.context)
       
   283     name = event.data.get('name')
       
   284     if name in container:
       
   285         event.form.widgets.errors += (Invalid(_("Specified name is already used!")),)
       
   286 
       
   287 
       
   288 @view_config(name='add-form-field.json', context=IFormFieldContainerTarget, request_type=IPyAMSLayer,
       
   289              permission=MANAGE_CONTENT_PERMISSION, renderer='json', xhr=True)
       
   290 class FormFieldAJAXAddForm(AJAXAddForm, FormFieldAddForm):
       
   291     """Form field add form, JSON renderer"""
       
   292 
       
   293     def nextURL(self):
       
   294         return '#form-fields.html'
       
   295 
       
   296 
       
   297 @pagelet_config(name='properties.html', context=IFormField, layer=IPyAMSLayer, permission=MANAGE_CONTENT_PERMISSION)
       
   298 class FormFieldPropertiesEditForm(AdminDialogEditForm):
       
   299     """Form field properties edit form"""
       
   300 
       
   301     @property
       
   302     def title(self):
       
   303         content = get_parent(self.context, IWfSharedContent)
       
   304         return II18n(content).query_attribute('title', request=self.request)
       
   305 
       
   306     legend = _("Edit form field properties")
       
   307     icon_class = 'fa fa-fw fa-pencil-square-o'
       
   308 
       
   309     fields = field.Fields(IFormField).omit('__parent__', '__name__', 'visible')
       
   310     ajax_handler = 'properties.json'
       
   311     edit_permission = MANAGE_CONTENT_PERMISSION
       
   312 
       
   313     def updateWidgets(self, prefix=None):
       
   314         super(FormFieldPropertiesEditForm, self).updateWidgets(prefix)
       
   315         if 'name' in self.widgets:
       
   316             self.widgets['name'].mode = DISPLAY_MODE
       
   317         if 'description' in self.widgets:
       
   318             self.widgets['description'].widget_css_class = 'textarea'
       
   319 
       
   320 
       
   321 @view_config(name='properties.json', context=IFormField, request_type=IPyAMSLayer,
       
   322              permission=MANAGE_CONTENT_PERMISSION, renderer='json', xhr=True)
       
   323 class FormFieldPropertiesAJAXEditForm(AJAXEditForm, FormFieldPropertiesEditForm):
       
   324     """Form field properties edit form, JSON renderer"""
       
   325 
       
   326     def get_ajax_output(self, changes):
       
   327         output = super(FormFieldPropertiesAJAXEditForm, self).get_ajax_output(changes)
       
   328         if 'label' in changes.get(IFormField, ()):
       
   329             output.setdefault('events', []).append({
       
   330                 'event': 'PyAMS_content.changed_item',
       
   331                 'options': {'object_type': 'form_field',
       
   332                             'object_name': self.context.__name__,
       
   333                             'title': II18n(self.context).query_attribute('label', request=self.request),
       
   334                             'visible': self.context.visible}
       
   335             })
       
   336         return output