[ui] Provide monkey-patches for cw's #14474840
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Fri, 22 Jul 2016 11:44:28 +0200
changeset 1494 5b2cd16061b3
parent 1493 b43b95943a80
child 1495 cc21266f79c3
[ui] Provide monkey-patches for cw's #14474840
site_cubicweb.py
views/patches.py
--- a/site_cubicweb.py	Fri Jul 22 11:24:41 2016 +0200
+++ b/site_cubicweb.py	Fri Jul 22 11:44:28 2016 +0200
@@ -45,3 +45,76 @@
 def __init__(self, **kwargs):
     super(rtags.RelationTags, self).__init__(**kwargs)
     self._tagdefs = {}
+
+
+# auto-configuration of custom fields ##############################################################
+# (https://www.cubicweb.org/ticket/14474840)
+# other part in views/patches
+
+from cubicweb.web import formfields  # noqa
+
+
+@monkeypatch(formfields)
+def guess_field(eschema, rschema, role='subject', req=None, **kwargs):
+    """This function return the most adapted field to edit the given relation
+    (`rschema`) where the given entity type (`eschema`) is the subject or object
+    (`role`).
+
+    The field is initialized according to information found in the schema,
+    though any value can be explicitly specified using `kwargs`.
+    """
+    fieldclass = None
+    rdef = eschema.rdef(rschema, role)
+    if role == 'subject':
+        targetschema = rdef.object
+        if rschema.final:
+            if rdef.get('internationalizable'):
+                kwargs.setdefault('internationalizable', True)
+    else:
+        targetschema = rdef.subject
+    card = rdef.role_cardinality(role)
+    composite = getattr(rdef, 'composite', None)
+    kwargs['name'] = rschema.type
+    kwargs['role'] = role
+    kwargs['eidparam'] = True
+    # don't mark composite relation as required, we want the composite element
+    # to be removed when not linked to its parent
+    kwargs.setdefault('required', card in '1+' and composite != formfields.neg_role(role))
+    if role == 'object':
+        kwargs.setdefault('label', (eschema.type, rschema.type + '_object'))
+    else:
+        kwargs.setdefault('label', (eschema.type, rschema.type))
+    kwargs.setdefault('help', rdef.description)
+    if rschema.final:
+        fieldclass = kwargs.pop('fieldclass', formfields.FIELDS[targetschema])
+        if issubclass(fieldclass, formfields.StringField):
+            if eschema.has_metadata(rschema, 'format'):
+                # use RichTextField instead of StringField if the attribute has
+                # a "format" metadata. But getting information from constraints
+                # may be useful anyway...
+                for cstr in rdef.constraints:
+                    if isinstance(cstr, formfields.StaticVocabularyConstraint):
+                        raise Exception('rich text field with static vocabulary')
+                return formfields.RichTextField(**kwargs)
+            # init StringField parameters according to constraints
+            for cstr in rdef.constraints:
+                if isinstance(cstr, formfields.StaticVocabularyConstraint):
+                    kwargs.setdefault('choices', cstr.vocabulary)
+                    break
+            for cstr in rdef.constraints:
+                if isinstance(cstr, formfields.SizeConstraint) and cstr.max is not None:
+                    kwargs['max_length'] = cstr.max
+            return fieldclass(**kwargs)
+        if issubclass(fieldclass, formfields.FileField):
+            if req:
+                aff_kwargs = req.vreg['uicfg'].select('autoform_field_kwargs', req)
+            else:
+                aff_kwargs = formfields._AFF_KWARGS
+            for metadata in formfields.KNOWN_METAATTRIBUTES:
+                metaschema = eschema.has_metadata(rschema, metadata)
+                if metaschema is not None:
+                    metakwargs = aff_kwargs.etype_get(eschema, metaschema, 'subject')
+                    kwargs['%s_field' % metadata] = formfields.guess_field(eschema, metaschema,
+                                                                           req=req, **metakwargs)
+        return fieldclass(**kwargs)
+    return formfields.RelationField.fromcardinality(card, **kwargs)
--- a/views/patches.py	Fri Jul 22 11:24:41 2016 +0200
+++ b/views/patches.py	Fri Jul 22 11:44:28 2016 +0200
@@ -32,3 +32,45 @@
         if button.label.endswith('cancel') and 'class' in button.attrs:
             button.attrs['class'] = button.attrs['class'].replace('cwjs-edition-cancel', '')
     return form, renderer
+
+
+# auto-configuration of custom fields ##############################################################
+# (https://www.cubicweb.org/ticket/14474840)
+# other part in site_cubicweb
+
+from cubicweb.web.views import forms  # noqa
+
+
+@monkeypatch(forms.EntityFieldsForm, methodname='field_by_name')
+@forms.iclassmethod
+def field_by_name(cls_or_self, name, role=None, eschema=None):
+    """return field with the given name and role. If field is not explicitly
+    defined for the form but `eclass` is specified, guess_field will be
+    called.
+    """
+    try:
+        return super(forms.EntityFieldsForm, cls_or_self).field_by_name(name, role)
+    except forms.form.FieldNotFound:
+        if eschema is None or role is None or name not in eschema.schema:
+            raise
+        rschema = eschema.schema.rschema(name)
+        # XXX use a sample target type. Document this.
+        tschemas = rschema.targets(eschema, role)
+        fieldclass = cls_or_self.uicfg_aff.etype_get(
+            eschema, rschema, role, tschemas[0])
+        kwargs = cls_or_self.uicfg_affk.etype_get(
+            eschema, rschema, role, tschemas[0])
+        if kwargs is None:
+            kwargs = {}
+        if fieldclass:
+            if not isinstance(fieldclass, type):
+                return fieldclass  # already an instance
+            kwargs['fieldclass'] = fieldclass
+        if isinstance(cls_or_self, type):
+            req = None
+        else:
+            req = cls_or_self._cw
+        field = forms.guess_field(eschema, rschema, role, req=req, eidparam=True, **kwargs)
+        if field is None:
+            raise
+        return field