diff -r 483b0f16e9a6 -r 26aefef3d0aa src/pyams_content/shared/form/field.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pyams_content/shared/form/field.py Tue Sep 19 11:11:30 2017 +0200 @@ -0,0 +1,265 @@ +# +# 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 +from collections import OrderedDict + +# import interfaces +from pyams_content.shared.form.interfaces import IFormFieldFactory, IFormField, IFormFieldContainer, \ + IFormFieldContainerTarget, FORM_FIELD_CONTAINER_KEY +from pyams_i18n.interfaces import II18n +from zope.annotation.interfaces import IAnnotations +from zope.location.interfaces import ISublocations +from zope.traversing.interfaces import ITraversable + +# import packages +from persistent import Persistent +from pyams_utils.adapter import adapter_config, ContextAdapter +from pyams_utils.registry import utility_config +from pyams_utils.request import check_request +from pyams_utils.schema import MailAddressField +from pyams_utils.vocabulary import vocabulary_config +from pyramid.threadlocal import get_current_registry +from zope.component.globalregistry import getGlobalSiteManager +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 TextLine, Text, Bool, Int, Decimal, URI, Date, Choice, List +from zope.schema.fieldproperty import FieldProperty +from zope.componentvocabulary.vocabulary import UtilityVocabulary, UtilityTerm + +from pyams_content import _ + + +@implementer(IFormField) +class FormField(Persistent, Contained): + """Form field definition persistent class""" + + name = FieldProperty(IFormField['name']) + field_type = FieldProperty(IFormField['field_type']) + label = FieldProperty(IFormField['label']) + description = FieldProperty(IFormField['description']) + placeholder = FieldProperty(IFormField['placeholder']) + values = FieldProperty(IFormField['values']) + default = FieldProperty(IFormField['default']) + required = FieldProperty(IFormField['required']) + visible = FieldProperty(IFormField['visible']) + + +@implementer(IFormFieldContainer) +class FormFieldContainer(OrderedContainer): + """Form fields container persistent class""" + + def get_fields(self): + registry = getGlobalSiteManager() + fields = [] + for field in self.values(): + if field.visible: + factory = registry.queryUtility(IFormFieldFactory, name=field.field_type) + if factory is not None: + fields.append(factory.get_schema_field(field)) + return fields + + +@adapter_config(context=IFormFieldContainerTarget, provides=IFormFieldContainer) +def FormFieldContainerFactory(context): + """Form fields container factory""" + annotations = IAnnotations(context) + container = annotations.get(FORM_FIELD_CONTAINER_KEY) + if container is None: + container = annotations[FORM_FIELD_CONTAINER_KEY] = FormFieldContainer() + get_current_registry().notify(ObjectCreatedEvent(container)) + locate(container, context, '++fields++') + return container + + +@adapter_config(name='fields', context=IFormFieldContainerTarget, provides=ITraversable) +class FormFieldContainerNamespace(ContextAdapter): + """Form fields container ++fields++ namespace""" + + def traverse(self, name, firtherpath=None): + return IFormFieldContainer(self.context) + + +@adapter_config(name='fields', context=IFormFieldContainerTarget, provides=ISublocations) +class FormFieldsContainerSublocations(ContextAdapter): + """Form fields container sub-locations adapter""" + + def sublocations(self): + return IFormFieldContainer(self.context).values() + + +# +# Form fields factories +# + +@vocabulary_config(name='PyAMS form field types') +class FormFieldTypesVocabulary(UtilityVocabulary): + """Form field types vocabulary""" + + interface = IFormFieldFactory + + def __init__(self, context, **kw): + request = check_request() + registry = request.registry + translate = request.localizer.translate + utils = [(name, translate(util.label)) + for (name, util) in sorted(registry.getUtilitiesFor(self.interface), + key=lambda x: x[1].weight)] + self._terms = OrderedDict((title, UtilityTerm(name, title)) for name, title in utils) + + def __iter__(self): + return iter(self._terms.values()) + + +class BaseFormFieldFactory(object): + """Base form field factory""" + + field_factory = None + + def get_schema_field(self, field): + i18n = II18n(field) + result = self.field_factory(title=i18n.query_attribute('label'), + description=i18n.query_attribute('description'), + required=field.required, + default=i18n.query_attribute('default')) + result.__name__ = field.name + return result + + +@utility_config(name='textline', provides=IFormFieldFactory) +class TextLineFieldFactory(BaseFormFieldFactory): + """Textline field factory""" + + label = _("Text") + weight = 1 + + field_factory = TextLine + + +@utility_config(name='text', provides=IFormFieldFactory) +class TextFieldFactory(BaseFormFieldFactory): + """Text field factory""" + + label = _("Multi-lines text") + weight = 2 + + field_factory = Text + + +@utility_config(name='bool', provides=IFormFieldFactory) +class BooleanFieldFactory(BaseFormFieldFactory): + """Boolean field factory""" + + label = _("Boolean") + weight = 3 + + field_factory = Bool + + +@utility_config(name='integer', provides=IFormFieldFactory) +class IntegerFieldFactory(BaseFormFieldFactory): + """Integer field factory""" + + label = _("Integer") + weight = 4 + + field_factory = Int + + +@utility_config(name='decimal', provides=IFormFieldFactory) +class DecimalFieldFactory(BaseFormFieldFactory): + """Decimal field factory""" + + label = _("Decimal") + weight = 5 + + field_factory = Decimal + + +@utility_config(name='mail', provides=IFormFieldFactory) +class MailFieldFactory(BaseFormFieldFactory): + """Mail field factory""" + + label = _("E-mail address") + weight = 10 + + field_factory = MailAddressField + + +@utility_config(name='uri', provides=IFormFieldFactory) +class URIFieldFactory(BaseFormFieldFactory): + """URI field factory""" + + label = _("URI") + weight = 11 + + field_factory = URI + + +@utility_config(name='date', provides=IFormFieldFactory) +class DateFieldFactory(BaseFormFieldFactory): + """Date field factory""" + + label = _("Date") + weight = 15 + + field_factory = Date + + +class ValuesFieldFactory(BaseFormFieldFactory): + """Values-based field factory""" + + +@utility_config(name='choice', provides=IFormFieldFactory) +class ChoiceFieldFactory(ValuesFieldFactory): + """Choice field factory""" + + label = _("Choice") + weight = 20 + + field_factory = Choice + + def get_schema_field(self, field): + i18n = II18n(field) + result = self.field_factory(title=i18n.query_attribute('label'), + description=i18n.query_attribute('description'), + required=field.required, + default=i18n.query_attribute('default'), + values=field.values) + result.__name__ = field.name + return result + + +@utility_config(name='list', provides=IFormFieldFactory) +class ListFieldFactory(ValuesFieldFactory): + """List field factory""" + + label = _("List") + weight = 51 + + field_factory = List + + def get_schema_field(self, field): + i18n = II18n(field) + result = self.field_factory(title=i18n.query_attribute('label'), + description=i18n.query_attribute('description'), + required=field.required, + default=[i18n.query_attribute('default')], + value_type=Choice(values=field.values)) + result.__name__ = field.name + return result