--- /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 <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 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