src/pyams_content/shared/form/field.py
changeset 170 26aefef3d0aa
child 372 719c3edee5c1
--- /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