[uicfg] Fix autoform_section rtags wrt derivation 3.25
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Fri, 06 Oct 2017 15:14:18 +0200
branch3.25
changeset 12213 1d7e4f98f902
parent 12212 f2e6fb5ddf68
child 12214 d238badfc268
[uicfg] Fix autoform_section rtags wrt derivation cw 3.25 introduced the 'derived rtags' feature, but missed that `autoform_section` implementation was overriding several methods where this was implemented in the base class. The following problems are tested and fixed: * during init, we should not attempt to call `_initfunc_step2` if there is some parent, * but we should handle expansion of wildcard ('*' used as subject or object of a tag) since this is handled during init step for this class, unlike others which handle this in `get` method (i.e. at lookup time), * fix overrided `get` method to consider parent rtag if any. Closes #17107020
cubicweb/web/test/unittest_uicfg.py
cubicweb/web/views/uicfg.py
--- a/cubicweb/web/test/unittest_uicfg.py	Fri Oct 06 10:00:36 2017 +0200
+++ b/cubicweb/web/test/unittest_uicfg.py	Fri Oct 06 15:14:18 2017 +0200
@@ -19,7 +19,10 @@
 import copy
 import warnings
 
-from cubicweb.devtools.testlib import CubicWebTC
+from yams.buildobjs import RelationDefinition, EntityType
+
+from cubicweb.devtools.testlib import CubicWebTC, BaseTestCase
+from cubicweb.schema import build_schema_from_namespace
 from cubicweb.web import uihelper, formwidgets as fwdgs
 from cubicweb.web.views import uicfg
 
@@ -144,6 +147,55 @@
         self.assertTrue(obj is custom_afs)
 
 
+def _schema():
+
+    class Personne(EntityType):
+        pass
+
+    class Societe(EntityType):
+        pass
+
+    class Tag(EntityType):
+        pass
+
+    class travaille(RelationDefinition):
+        subject = 'Personne'
+        object = 'Societe'
+
+    class tags(RelationDefinition):
+        subject = 'Tag'
+        object = ('Personne', 'Societe', 'Tag')
+
+    return build_schema_from_namespace(locals().items())
+
+
+class AutoformSectionTC(BaseTestCase):
+
+    def test_derivation(self):
+        schema = _schema()
+        afs = uicfg.AutoformSectionRelationTags()
+        afs.tag_subject_of(('Personne', 'travaille', '*'), 'main', 'relations')
+        afs.tag_object_of(('*', 'travaille', 'Societe'), 'main', 'relations')
+        afs.tag_subject_of(('Tag', 'tags', '*'), 'main', 'relations')
+
+        afs2 = afs.derive(__name__, afs.__select__)
+        afs2.tag_subject_of(('Personne', 'travaille', '*'), 'main', 'attributes')
+        afs2.tag_object_of(('*', 'travaille', 'Societe'), 'main', 'attributes')
+        afs2.tag_subject_of(('Tag', 'tags', 'Societe'), 'main', 'attributes')
+
+        afs.init(schema)
+        afs2.init(schema)
+
+        self.assertEqual(afs2.etype_get('Tag', 'tags', 'subject', 'Personne'),
+                         set(('main_relations', 'muledit_hidden', 'inlined_relations')))
+        self.assertEqual(afs2.etype_get('Tag', 'tags', 'subject', 'Societe'),
+                         set(('main_attributes', 'muledit_hidden', 'inlined_attributes')))
+        self.assertEqual(afs2.etype_get('Personne', 'travaille', 'subject', 'Societe'),
+                         set(('main_attributes', 'muledit_hidden', 'inlined_attributes')))
+        self.assertEqual(afs2.etype_get('Societe', 'travaille', 'object', 'Personne'),
+                         set(('main_attributes', 'muledit_hidden', 'inlined_attributes')))
+
+
 if __name__ == '__main__':
     import unittest
     unittest.main()
--- a/cubicweb/web/views/uicfg.py	Fri Oct 06 10:00:36 2017 +0200
+++ b/cubicweb/web/views/uicfg.py	Fri Oct 06 15:14:18 2017 +0200
@@ -54,12 +54,14 @@
    uicfg.actionbox_appearsin_addmenu.tag_object_of(('*', 'entry_of', 'Blog'), True)
 """
 
+from itertools import repeat
+
 from six import string_types
 
 from cubicweb import neg_role
 from cubicweb.rtags import (RelationTags, RelationTagsBool, RelationTagsSet,
                             RelationTagsDict, NoTargetRelationTagsDict,
-                            _ensure_str_key)
+                            rtags_chain, _ensure_str_key)
 from cubicweb.schema import META_RTYPES, INTERNAL_TYPES, WORKFLOW_TYPES
 
 
@@ -203,7 +205,16 @@
 
 
 class AutoformSectionRelationTags(RelationTagsSet):
-    """autoform relations'section"""
+    """autoform relations'section
+
+    Notice that unlike other rtags where wildcard handling is done when
+    retrieving some value, all values are expanded here during initialization
+    step.
+
+    For derived rtags, values specified for the 'main' form type are propagated
+    to the 'inlined' form type if unspecified. Others are fetched back from the
+    parent.
+    """
     __regid__ = 'autoform_section'
 
     _allowed_form_types = ('main', 'inlined', 'muledit')
@@ -215,7 +226,34 @@
 
     def init(self, schema, check=True):
         super(AutoformSectionRelationTags, self).init(schema, check)
-        self.apply(schema, self._initfunc_step2)
+        if self._parent is None:
+            self.apply(schema, self._initfunc_step2)
+        else:
+            # we still need to expand wildcard in defined keys
+            for key in list(self._tagdefs):
+                stype, rtype, otype, role = key
+                rschema = schema.rschema(rtype)
+                if stype == '*' and stype == '*':
+                    concrete_rdefs = rschema.rdefs.keys()
+                elif stype == '*':
+                    concrete_rdefs = zip(rschema.subjects(otype), repeat(otype))
+                elif otype == '*':
+                    concrete_rdefs = zip(repeat(stype), rschema.objects(stype))
+                else:
+                    concrete_rdefs = [(stype, otype)]
+                for sschema, oschema in concrete_rdefs:
+                    self._init(sschema, rschema, oschema, role)
+                    # also, we have to copy values from 'main' to 'inlined' and
+                    # for other undefined sections from the parent's rtag
+                    formsections = self.get(sschema, rschema, oschema, role)
+                    sectdict = _formsections_as_dict(formsections)
+                    parent_formsections = self._parent.get(sschema, rschema, oschema, role)
+                    parent_sectdict = _formsections_as_dict(parent_formsections)
+                    for formtype, section in parent_sectdict.items():
+                        if formtype not in sectdict:
+                            if formtype == 'inlined':
+                                section = sectdict.get('main', section)
+                            formsections.add('%s_%s' % (formtype, section))
 
     def _init(self, sschema, rschema, oschema, role):
         formsections = self.init_get(sschema, rschema, oschema, role)
@@ -300,7 +338,12 @@
 
     def get(self, *key):
         # overriden to avoid recomputing done in parent classes
-        return self._tagdefs.get(key, ())
+        for rtag in rtags_chain(self):
+            try:
+                return rtag._tagdefs[key]
+            except KeyError:
+                continue
+        return ()
 
     def relations_by_section(self, entity, formtype, section, permission,
                              strict=False):