make ui configurations selectable (closes #2406609)
authorFlorent Cayré <florent.cayre@logilab.fr>
Mon, 21 Jan 2013 16:34:09 +0100
changeset 8666 1dd655788ece
parent 8665 e65af61bde7d
child 8667 5a394fc419b4
make ui configurations selectable (closes #2406609) * introduce a new 'uicfg' registry (storing instances) * use the relevant new APIs from lgc.registry to manage the new registrable uicfg objects * cw event manager useage is gone; instead thze standard registry reloading mechanism is used * ensure i18n commands still work (devctl) * introduce dynamic uicfgs use whenever possible (various views), even though sometimes the classic 'static' usage remains
cwvreg.py
devtools/devctl.py
rtags.py
server/hook.py
view.py
web/formfields.py
web/test/unittest_formfields.py
web/test/unittest_uicfg.py
web/views/actions.py
web/views/autoform.py
web/views/forms.py
web/views/management.py
web/views/primary.py
web/views/reledit.py
web/views/uicfg.py
--- a/cwvreg.py	Thu Jan 10 18:34:10 2013 +0100
+++ b/cwvreg.py	Mon Jan 21 16:34:09 2013 +0100
@@ -197,12 +197,13 @@
 from os.path import join, dirname, realpath
 from warnings import warn
 from datetime import datetime, date, time, timedelta
+from functools import partial
 
 from logilab.common.decorators import cached, clear_cache
 from logilab.common.deprecation import deprecated, class_deprecated
 from logilab.common.modutils import cleanup_sys_modules
 from logilab.common.registry import (
-    RegistryStore, Registry, classid,
+    RegistryStore, Registry, obj_registries,
     ObjectNotFound, NoSelectableObject, RegistryNotFound)
 
 from rql import RQLHelper
@@ -210,13 +211,15 @@
 
 from cubicweb import (CW_SOFTWARE_ROOT, ETYPE_NAME_MAP, CW_EVENT_MANAGER,
                       Binary, UnknownProperty, UnknownEid)
-from cubicweb.rtags import RTAGS
 from cubicweb.predicates import (implements, appobject_selectable,
                                  _reset_is_instance_cache)
 
-def clear_rtag_objects():
-    for rtag in RTAGS:
-        rtag.clear()
+# backward compat: those modules are now refering to app objects in
+# cw.web.views.uicfg and import * from backward compat. On registry reload, we
+# should pop those modules from the cache so references are properly updated on
+# subsequent reload
+CW_EVENT_MANAGER.bind('before-registry-reload', partial(sys.modules.pop, 'cubicweb.web.uicfg', None))
+CW_EVENT_MANAGER.bind('before-registry-reload', partial(sys.modules.pop, 'cubicweb.web.uihelper', None))
 
 def use_interfaces(obj):
     """return interfaces required by the given object by searching for
@@ -263,6 +266,15 @@
     return getattr(obj, appobjectattr, obj)
 
 
+class InstancesRegistry(CWRegistry):
+
+    def selected(self, winner, args, kwargs):
+        """overriden to avoid the default 'instanciation' behaviour, ie
+        winner(*args, **kwargs)
+        """
+        return winner
+
+
 class ETypeRegistry(CWRegistry):
 
     def clear_caches(self):
@@ -497,6 +509,7 @@
                         'views': ViewsRegistry,
                         'actions': ActionsRegistry,
                         'ctxcomponents': CtxComponentsRegistry,
+                        'uicfg': InstancesRegistry,
                         }
 
     def __init__(self, config, initlog=True):
@@ -517,11 +530,6 @@
             sys.path.remove(CW_SOFTWARE_ROOT)
         self.schema = None
         self.initialized = False
-        # XXX give force_reload (or refactor [re]loading...)
-        if self.config.mode != 'test':
-            # don't clear rtags during test, this may cause breakage with
-            # manually imported appobject modules
-            CW_EVENT_MANAGER.bind('before-registry-reload', clear_rtag_objects)
         self['boxes'] = BwCompatCWRegistry(self, 'boxes', 'ctxcomponents')
         self['contentnavigation'] = BwCompatCWRegistry(self, 'contentnavigation', 'ctxcomponents')
 
@@ -695,8 +703,9 @@
                                    or iface
                                    for iface in ifaces)
                 if not ('Any' in ifaces or ifaces & implemented_interfaces):
+                    reg = self[obj_registries(obj)[0]]
                     self.debug('unregister %s (no implemented '
-                               'interface among %s)', classid(obj), ifaces)
+                               'interface among %s)', reg.objid(obj), ifaces)
                     self.unregister(obj)
             # since 3.9: remove appobjects which depending on other, unexistant
             # appobjects
@@ -704,8 +713,7 @@
                 try:
                     registry = self[regname]
                 except RegistryNotFound:
-                    self.debug('unregister %s (no registry %s)', classid(obj),
-                               regname)
+                    self.debug('unregister %s (no registry %s)', obj, regname)
                     self.unregister(obj)
                     continue
                 for regid in regids:
@@ -713,12 +721,14 @@
                         break
                 else:
                     self.debug('unregister %s (no %s object in registry %s)',
-                               classid(obj), ' or '.join(regids), regname)
+                               registry.objid(obj), ' or '.join(regids), regname)
                     self.unregister(obj)
         super(CWRegistryStore, self).initialization_completed()
-        for rtag in RTAGS:
-            # don't check rtags if we don't want to cleanup_interface_sobjects
-            rtag.init(self.schema, check=self.config.cleanup_interface_sobjects)
+        if 'uicfg' in self: # 'uicfg' is not loaded in a pure repository mode
+            for rtags in self['uicfg'].values():
+                for rtag in rtags:
+                    # don't check rtags if we don't want to cleanup_interface_sobjects
+                    rtag.init(self.schema, check=self.config.cleanup_interface_sobjects)
 
     # rql parsing utilities ####################################################
 
--- a/devtools/devctl.py	Thu Jan 10 18:34:10 2013 +0100
+++ b/devtools/devctl.py	Mon Jan 21 16:34:09 2013 +0100
@@ -95,11 +95,6 @@
             if mod.__file__.startswith(path):
                 del sys.modules[name]
                 break
-    # fresh rtags
-    from cubicweb import rtags
-    from cubicweb.web.views import uicfg
-    rtags.RTAGS[:] = []
-    reload(uicfg)
 
 def generate_schema_pot(w, cubedir=None):
     """generate a pot file with schema specific i18n messages
@@ -129,7 +124,6 @@
 def _generate_schema_pot(w, vreg, schema, libconfig=None):
     from copy import deepcopy
     from cubicweb.i18n import add_msg
-    from cubicweb.web.views import uicfg
     from cubicweb.schema import NO_I18NCONTEXT, CONSTRAINTS
     w('# schema pot file, generated on %s\n'
       % datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
@@ -138,22 +132,21 @@
     w('\n')
     vregdone = set()
     if libconfig is not None:
-        from cubicweb.cwvreg import CWRegistryStore, clear_rtag_objects
+        from cubicweb.cwvreg import CWRegistryStore
         libschema = libconfig.load_schema(remove_unused_rtypes=False)
-        afs = deepcopy(uicfg.autoform_section)
-        appearsin_addmenu = deepcopy(uicfg.actionbox_appearsin_addmenu)
-        clear_rtag_objects()
+        afs = vreg['uicfg'].select('autoform_section')
+        appearsin_addmenu = vreg['uicfg'].select('actionbox_appearsin_addmenu')
         cleanup_sys_modules(libconfig)
         libvreg = CWRegistryStore(libconfig)
         libvreg.set_schema(libschema) # trigger objects registration
-        libafs = uicfg.autoform_section
-        libappearsin_addmenu = uicfg.actionbox_appearsin_addmenu
+        libafs = libvreg['uicfg'].select('autoform_section')
+        libappearsin_addmenu = libvreg['uicfg'].select('actionbox_appearsin_addmenu')
         # prefill vregdone set
         list(_iter_vreg_objids(libvreg, vregdone))
     else:
         libschema = {}
-        afs = uicfg.autoform_section
-        appearsin_addmenu = uicfg.actionbox_appearsin_addmenu
+        afs = vreg['uicfg'].select('autoform_section')
+        appearsin_addmenu = vreg['uicfg'].select('actionbox_appearsin_addmenu')
         for cstrtype in CONSTRAINTS:
             add_msg(w, cstrtype)
     done = set()
--- a/rtags.py	Thu Jan 10 18:34:10 2013 +0100
+++ b/rtags.py	Mon Jan 21 16:34:09 2013 +0100
@@ -38,17 +38,20 @@
 __docformat__ = "restructuredtext en"
 
 import logging
+from warnings import warn
 
 from logilab.common.logging_ext import set_log_methods
-
-RTAGS = []
-def register_rtag(rtag):
-    RTAGS.append(rtag)
+from logilab.common.registry import RegistrableInstance, yes
 
 def _ensure_str_key(key):
     return tuple(str(k) for k in key)
 
-class RelationTags(object):
+class RegistrableRtags(RegistrableInstance):
+    __registry__ = 'uicfg'
+    __select__ = yes()
+
+
+class RelationTags(RegistrableRtags):
     """a tag store for full relation definitions :
 
          (subject type, relation type, object type, tagged)
@@ -58,18 +61,17 @@
     This class associates a single tag to each key.
     """
     _allowed_values = None
-    _initfunc = None
-    def __init__(self, name=None, initfunc=None, allowed_values=None):
-        self._name = name or '<unknown>'
+    # _init expected to be a method (introduced in 3.17), while _initfunc a
+    # function given as __init__ argument and kept for bw compat
+    _init = _initfunc = None
+
+    def __init__(self):
         self._tagdefs = {}
-        if allowed_values is not None:
-            self._allowed_values = allowed_values
-        if initfunc is not None:
-            self._initfunc = initfunc
-        register_rtag(self)
 
     def __repr__(self):
-        return '%s: %s' % (self._name, repr(self._tagdefs))
+        # find a way to have more infos but keep it readable
+        # (in error messages in case of an ambiguity for instance)
+        return '%s (%s): %s' % (id(self), self.__regid__, self.__class__)
 
     # dict compat
     def __getitem__(self, key):
@@ -100,8 +102,8 @@
                                      (stype, rtype, otype, tagged), value, ertype)
                         self.del_rtag(stype, rtype, otype, tagged)
                         break
-        if self._initfunc is not None:
-            self.apply(schema, self._initfunc)
+        if self._init is not None:
+            self.apply(schema, self._init)
 
     def apply(self, schema, func):
         for eschema in schema.entities():
@@ -113,7 +115,7 @@
                         sschema, oschema = eschema, tschema
                     else:
                         sschema, oschema = tschema, eschema
-                    func(self, sschema, rschema, oschema, role)
+                    func(sschema, rschema, oschema, role)
 
     # rtag declaration api ####################################################
 
@@ -250,4 +252,6 @@
                 key = list(key)
             key[0] = '*'
         super(NoTargetRelationTagsDict, self).tag_relation(key, tag)
+
+
 set_log_methods(RelationTags, logging.getLogger('cubicweb.rtags'))
--- a/server/hook.py	Thu Jan 10 18:34:10 2013 +0100
+++ b/server/hook.py	Mon Jan 21 16:34:09 2013 +0100
@@ -258,7 +258,7 @@
 from logilab.common.deprecation import deprecated, class_renamed
 from logilab.common.logging_ext import set_log_methods
 from logilab.common.registry import (Predicate, NotPredicate, OrPredicate,
-                                     classid, objectify_predicate, yes)
+                                     objectify_predicate, yes)
 
 from cubicweb import RegistryNotFound, server
 from cubicweb.cwvreg import CWRegistry, CWRegistryStore
@@ -768,7 +768,7 @@
         """delegate event handling to the opertaion"""
         if event == 'postcommit_event' and hasattr(self, 'commit_event'):
             warn('[3.10] %s: commit_event method has been replaced by postcommit_event'
-                 % classid(self.__class__), DeprecationWarning)
+                 % self.__class__, DeprecationWarning)
             self.commit_event() # pylint: disable=E1101
         getattr(self, event)()
 
--- a/view.py	Thu Jan 10 18:34:10 2013 +0100
+++ b/view.py	Mon Jan 21 16:34:09 2013 +0100
@@ -26,7 +26,7 @@
 from functools import partial
 
 from logilab.common.deprecation import deprecated
-from logilab.common.registry import classid, yes
+from logilab.common.registry import yes
 from logilab.mtconverter import xml_escape
 
 from rql import nodes
@@ -608,7 +608,7 @@
             if hasattr(entity, func.__name__):
                 warn('[3.9] %s method is deprecated, define it on a custom '
                      '%s for %s instead' % (func.__name__, iface,
-                                            classid(entity.__class__)),
+                                            entity.__class__),
                      DeprecationWarning)
                 member = getattr(entity, func.__name__)
                 if callable(member):
--- a/web/formfields.py	Thu Jan 10 18:34:10 2013 +0100
+++ b/web/formfields.py	Mon Jan 21 16:34:09 2013 +0100
@@ -82,7 +82,6 @@
      formwidgets as fw
 from cubicweb.web.views import uicfg
 
-
 class UnmodifiedField(Exception):
     """raise this when a field has not actually been edited and you want to skip
     it
@@ -466,8 +465,6 @@
             # attribute or relation
             return True
         # if it's a non final relation, we need the eids
-        # XXX underlying regression: getattr(ent, 'foo') used to return
-        #     a tuple, now we get a list
         if isinstance(previous_value, (list, tuple)):
             # widget should return a set of untyped eids
             previous_value = set(e.eid for e in previous_value)
@@ -1164,7 +1161,7 @@
 
 _AFF_KWARGS = uicfg.autoform_field_kwargs
 
-def guess_field(eschema, rschema, role='subject', **kwargs):
+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`).
@@ -1212,12 +1209,16 @@
                     kwargs['max_length'] = cstr.max
             return StringField(**kwargs)
         if fieldclass is FileField:
+            if req:
+                aff_kwargs = req.vreg['uicfg'].select('autoform_field_kwargs', req)
+            else:
+                aff_kwargs = _AFF_KWARGS
             for metadata in KNOWN_METAATTRIBUTES:
                 metaschema = eschema.has_metadata(rschema, metadata)
                 if metaschema is not None:
-                    metakwargs = _AFF_KWARGS.etype_get(eschema, metaschema, 'subject')
+                    metakwargs = aff_kwargs.etype_get(eschema, metaschema, 'subject')
                     kwargs['%s_field' % metadata] = guess_field(eschema, metaschema,
-                                                                **metakwargs)
+                                                                req=req, **metakwargs)
         return fieldclass(**kwargs)
     return RelationField.fromcardinality(card, **kwargs)
 
--- a/web/test/unittest_formfields.py	Thu Jan 10 18:34:10 2013 +0100
+++ b/web/test/unittest_formfields.py	Mon Jan 21 16:34:09 2013 +0100
@@ -35,10 +35,14 @@
     config.bootstrap_cubes()
     schema = config.load_schema()
 
-class GuessFieldTC(TestCase):
+class GuessFieldTC(CubicWebTC):
+
+    def setUp(self):
+        super(GuessFieldTC, self).setUp()
+        self.req = self.request()
 
     def test_state_fields(self):
-        title_field = guess_field(schema['State'], schema['name'])
+        title_field = guess_field(schema['State'], schema['name'], req=self.req)
         self.assertIsInstance(title_field, StringField)
         self.assertEqual(title_field.required, True)
 
@@ -48,7 +52,7 @@
 #         self.assertEqual(synopsis_field.required, False)
 #         self.assertEqual(synopsis_field.help, 'an abstract for this state')
 
-        description_field = guess_field(schema['State'], schema['description'])
+        description_field = guess_field(schema['State'], schema['description'], req=self.req)
         self.assertIsInstance(description_field, RichTextField)
         self.assertEqual(description_field.required, False)
         self.assertEqual(description_field.format_field, None)
@@ -56,7 +60,8 @@
         # description_format_field = guess_field(schema['State'], schema['description_format'])
         # self.assertEqual(description_format_field, None)
 
-        description_format_field = guess_field(schema['State'], schema['description_format'])
+        description_format_field = guess_field(schema['State'], schema['description_format'],
+                                               req=self.req)
         self.assertEqual(description_format_field.internationalizable, True)
         self.assertEqual(description_format_field.sort, True)
 
@@ -66,22 +71,22 @@
 
 
     def test_cwuser_fields(self):
-        upassword_field = guess_field(schema['CWUser'], schema['upassword'])
+        upassword_field = guess_field(schema['CWUser'], schema['upassword'], req=self.req)
         self.assertIsInstance(upassword_field, StringField)
         self.assertIsInstance(upassword_field.widget, PasswordInput)
         self.assertEqual(upassword_field.required, True)
 
-        last_login_time_field = guess_field(schema['CWUser'], schema['last_login_time'])
+        last_login_time_field = guess_field(schema['CWUser'], schema['last_login_time'], req=self.req)
         self.assertIsInstance(last_login_time_field, DateTimeField)
         self.assertEqual(last_login_time_field.required, False)
 
-        in_group_field = guess_field(schema['CWUser'], schema['in_group'])
+        in_group_field = guess_field(schema['CWUser'], schema['in_group'], req=self.req)
         self.assertIsInstance(in_group_field, RelationField)
         self.assertEqual(in_group_field.required, True)
         self.assertEqual(in_group_field.role, 'subject')
         self.assertEqual(in_group_field.help, 'groups grant permissions to the user')
 
-        owned_by_field = guess_field(schema['CWUser'], schema['owned_by'], 'object')
+        owned_by_field = guess_field(schema['CWUser'], schema['owned_by'], 'object', req=self.req)
         self.assertIsInstance(owned_by_field, RelationField)
         self.assertEqual(owned_by_field.required, False)
         self.assertEqual(owned_by_field.role, 'object')
@@ -95,7 +100,7 @@
         # data_name_field = guess_field(schema['File'], schema['data_name'])
         # self.assertEqual(data_name_field, None)
 
-        data_field = guess_field(schema['File'], schema['data'])
+        data_field = guess_field(schema['File'], schema['data'], req=self.req)
         self.assertIsInstance(data_field, FileField)
         self.assertEqual(data_field.required, True)
         self.assertIsInstance(data_field.format_field, StringField)
@@ -103,7 +108,7 @@
         self.assertIsInstance(data_field.name_field, StringField)
 
     def test_constraints_priority(self):
-        salesterm_field = guess_field(schema['Salesterm'], schema['reason'])
+        salesterm_field = guess_field(schema['Salesterm'], schema['reason'], req=self.req)
         constraints = schema['reason'].rdef('Salesterm', 'String').constraints
         self.assertEqual([c.__class__ for c in constraints],
                           [SizeConstraint, StaticVocabularyConstraint])
@@ -112,7 +117,7 @@
 
 
     def test_bool_field_base(self):
-        field = guess_field(schema['CWAttribute'], schema['indexed'])
+        field = guess_field(schema['CWAttribute'], schema['indexed'], req=self.req)
         self.assertIsInstance(field, BooleanField)
         self.assertEqual(field.required, False)
         self.assertIsInstance(field.widget, Radio)
@@ -121,7 +126,7 @@
 
     def test_bool_field_explicit_choices(self):
         field = guess_field(schema['CWAttribute'], schema['indexed'],
-                            choices=[(u'maybe', '1'), (u'no', '')])
+                            choices=[(u'maybe', '1'), (u'no', '')], req=self.req)
         self.assertIsInstance(field.widget, Radio)
         self.assertEqual(field.vocabulary(mock(req=mock(_=unicode))),
                           [(u'maybe', '1'), (u'no', '')])
--- a/web/test/unittest_uicfg.py	Thu Jan 10 18:34:10 2013 +0100
+++ b/web/test/unittest_uicfg.py	Mon Jan 21 16:34:09 2013 +0100
@@ -107,6 +107,24 @@
         self.assertEqual(afk_get('CWUser', 'firstname', 'String', 'subject'), {'order': 1})
 
 
+class UicfgRegistryTC(CubicWebTC):
+
+    def test_default_uicfg_object(self):
+        'CW default ui config objects must be registered in uicfg registry'
+        onames = ('autoform_field', 'autoform_section', 'autoform_field_kwargs')
+        for oname in onames:
+            obj = self.vreg['uicfg'].select_or_none(oname)
+            self.assertTrue(obj is not None, '%s not found in uicfg registry'
+                            % oname)
+
+    def test_custom_uicfg(self):
+        ASRT = uicfg.AutoformSectionRelationTags
+        custom_afs = ASRT()
+        custom_afs.__select__ = ASRT.__select__ & ASRT.__select__
+        self.vreg['uicfg'].register(custom_afs)
+        obj = self.vreg['uicfg'].select_or_none('autoform_section')
+        self.assertTrue(obj is custom_afs)
+
 
 if __name__ == '__main__':
     from logilab.common.testlib import unittest_main
--- a/web/views/actions.py	Thu Jan 10 18:34:10 2013 +0100
+++ b/web/views/actions.py	Mon Jan 21 16:34:09 2013 +0100
@@ -291,7 +291,8 @@
         method to return an empty list. If you only want some, you can configure
         them by using uicfg.actionbox_appearsin_addmenu
         """
-        appearsin_addmenu = uicfg.actionbox_appearsin_addmenu
+        appearsin_addmenu = self._cw.vreg['uicfg'].select(
+            'actionbox_appearsin_addmenu', self._cw, entity=entity)
         req = self._cw
         eschema = entity.e_schema
         for role, rschemas in (('subject', eschema.subject_relations()),
--- a/web/views/autoform.py	Thu Jan 10 18:34:10 2013 +0100
+++ b/web/views/autoform.py	Mon Jan 21 16:34:09 2013 +0100
@@ -1,4 +1,4 @@
-# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -126,7 +126,6 @@
 from logilab.mtconverter import xml_escape
 from logilab.common.decorators import iclassmethod, cached
 from logilab.common.deprecation import deprecated
-from logilab.common.registry import classid
 
 from cubicweb import typed_eid, neg_role, uilib
 from cubicweb.schema import display_name
@@ -140,9 +139,6 @@
 from cubicweb.web.views import uicfg, forms
 from cubicweb.web.views.ajaxcontroller import ajaxfunc
 
-_AFS = uicfg.autoform_section
-_AFFK = uicfg.autoform_field_kwargs
-
 
 # inlined form handling ########################################################
 
@@ -755,6 +751,8 @@
 
     def __init__(self, *args, **kwargs):
         super(AutomaticEntityForm, self).__init__(*args, **kwargs)
+        self.uicfg_afs = self._cw.vreg['uicfg'].select(
+            'autoform_section', self._cw, entity=self.edited_entity)
         entity = self.edited_entity
         if entity.has_eid():
             entity.complete()
@@ -820,8 +818,8 @@
 
     def _inlined_form_view_field(self, view):
         # XXX allow more customization
-        kwargs = _AFFK.etype_get(self.edited_entity.e_schema, view.rtype,
-                                 view.role, view.etype)
+        kwargs = self.uicfg_affk.etype_get(self.edited_entity.e_schema,
+                                           view.rtype, view.role, view.etype)
         if kwargs is None:
             kwargs = {}
         return InlinedFormField(view=view, **kwargs)
@@ -832,7 +830,7 @@
         """return a list of (relation schema, target schemas, role) matching
         given category(ies) and permission
         """
-        return _AFS.relations_by_section(
+        return self.uicfg_afs.relations_by_section(
             self.edited_entity, self.formtype, section, permission, strict)
 
     def editable_attributes(self, strict=False):
@@ -963,6 +961,7 @@
 
 ## default form ui configuration ##############################################
 
+_AFS = uicfg.autoform_section
 # use primary and not generated for eid since it has to be an hidden
 _AFS.tag_attribute(('*', 'eid'), 'main', 'attributes')
 _AFS.tag_attribute(('*', 'eid'), 'muledit', 'attributes')
@@ -994,6 +993,7 @@
 _AFS.tag_subject_of(('CWRelation', 'from_entity', '*'), 'main', 'inlined')
 _AFS.tag_subject_of(('CWRelation', 'to_entity', '*'), 'main', 'inlined')
 
+_AFFK = uicfg.autoform_field_kwargs
 _AFFK.tag_attribute(('RQLExpression', 'expression'),
                     {'widget': fw.TextInput})
 _AFFK.tag_subject_of(('TrInfo', 'wf_info_for', '*'),
--- a/web/views/forms.py	Thu Jan 10 18:34:10 2013 +0100
+++ b/web/views/forms.py	Mon Jan 21 16:34:09 2013 +0100
@@ -302,9 +302,6 @@
             return processed
 
 
-_AFF = uicfg.autoform_field
-_AFF_KWARGS = uicfg.autoform_field_kwargs
-
 class EntityFieldsForm(FieldsForm):
     """This class is designed for forms used to edit some entities. It should
     handle for you all the underlying stuff necessary to properly work with the
@@ -315,6 +312,8 @@
     __select__ = (match_kwargs('entity')
                   | (one_line_rset() & non_final_entity()))
     domid = 'entityForm'
+    uicfg_aff = uicfg.autoform_field
+    uicfg_affk = uicfg.autoform_field_kwargs
 
     @iclassmethod
     def field_by_name(cls_or_self, name, role=None, eschema=None):
@@ -330,15 +329,21 @@
             rschema = eschema.schema.rschema(name)
             # XXX use a sample target type. Document this.
             tschemas = rschema.targets(eschema, role)
-            fieldcls = _AFF.etype_get(eschema, rschema, role, tschemas[0])
-            kwargs = _AFF_KWARGS.etype_get(eschema, rschema, role, tschemas[0])
+            fieldcls = 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 fieldcls:
                 if not isinstance(fieldcls, type):
                     return fieldcls # already and instance
                 return fieldcls(name=name, role=role, eidparam=True, **kwargs)
-            field = guess_field(eschema, rschema, role, eidparam=True, **kwargs)
+            if isinstance(cls_or_self, type):
+                req = None
+            else:
+                req = cls_or_self._cw
+            field = guess_field(eschema, rschema, role, req=req, eidparam=True, **kwargs)
             if field is None:
                 raise
             return field
@@ -350,6 +355,10 @@
             self.edited_entity = rset.complete_entity(row or 0, col or 0)
         msg = kwargs.pop('submitmsg', None)
         super(EntityFieldsForm, self).__init__(_cw, rset, row, col, **kwargs)
+        self.uicfg_aff = self._cw.vreg['uicfg'].select(
+            'autoform_field', self._cw, entity=self.edited_entity)
+        self.uicfg_affk = self._cw.vreg['uicfg'].select(
+            'autoform_field_kwargs', self._cw, entity=self.edited_entity)
         self.add_hidden('__type', self.edited_entity.__regid__, eidparam=True)
         self.add_hidden('eid', self.edited_entity.eid)
         # mainform default to true in parent, hence default to True
--- a/web/views/management.py	Thu Jan 10 18:34:10 2013 +0100
+++ b/web/views/management.py	Mon Jan 21 16:34:09 2013 +0100
@@ -76,7 +76,9 @@
                                          domid='ownership%s' % entity.eid,
                                          __redirectvid='security',
                                          __redirectpath=entity.rest_path())
-        field = guess_field(entity.e_schema, self._cw.vreg.schema.rschema('owned_by'))
+        field = guess_field(entity.e_schema,
+                            self._cw.vreg.schema['owned_by'],
+                            req=self._cw)
         form.append_field(field)
         form.render(w=self.w, display_progress_div=False)
 
--- a/web/views/primary.py	Thu Jan 10 18:34:10 2013 +0100
+++ b/web/views/primary.py	Mon Jan 21 16:34:09 2013 +0100
@@ -1,4 +1,4 @@
-# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -97,8 +97,8 @@
     title = _('primary')
     show_attr_label = True
     show_rel_label = True
-    rsection = uicfg.primaryview_section
-    display_ctrl = uicfg.primaryview_display_ctrl
+    rsection = None
+    display_ctrl = None
     main_related_section = True
 
     def html_headers(self):
@@ -111,6 +111,13 @@
 
     def entity_call(self, entity):
         entity.complete()
+        uicfg_reg = self._cw.vreg['uicfg']
+        if self.rsection is None:
+            self.rsection = uicfg_reg.select('primaryview_section',
+                                             self._cw, entity=entity)
+        if self.display_ctrl is None:
+            self.display_ctrl = uicfg_reg.select('primaryview_display_ctrl',
+                                                 self._cw, entity=entity)
         self.render_entity(entity)
 
     def render_entity(self, entity):
--- a/web/views/reledit.py	Thu Jan 10 18:34:10 2013 +0100
+++ b/web/views/reledit.py	Mon Jan 21 16:34:09 2013 +0100
@@ -1,4 +1,4 @@
-# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -51,8 +51,6 @@
     def add_hidden(self, *args):
         pass
 
-rctrl = uicfg.reledit_ctrl
-
 class AutoClickAndEditFormView(EntityView):
     __regid__ = 'reledit'
     __select__ = non_final_entity() & match_kwargs('rtype')
@@ -91,6 +89,7 @@
         self._cw.add_js(('cubicweb.reledit.js', 'cubicweb.edition.js', 'cubicweb.ajax.js'))
         self.entity = entity
         rschema = self._cw.vreg.schema[rtype]
+        rctrl = self._cw.vreg['uicfg'].select('reledit', self._cw, entity=entity)
         self._rules = rctrl.etype_get(self.entity.e_schema.type, rschema.type, role, '*')
         if rvid is not None or default_value is not None:
             warn('[3.9] specifying rvid/default_value on select is deprecated, '
--- a/web/views/uicfg.py	Thu Jan 10 18:34:10 2013 +0100
+++ b/web/views/uicfg.py	Mon Jan 21 16:34:09 2013 +0100
@@ -1,4 +1,4 @@
-# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -15,7 +15,7 @@
 #
 # You should have received a copy of the GNU Lesser General Public License along
 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
-"""This module (``cubicweb.web.uicfg``) regroups a set of structures that may be
+"""This module (``cubicweb.web.views.uicfg``) regroups a set of structures that may be
 used to configure various options of the generated web interface.
 
 To configure the interface generation, we use ``RelationTag`` objects.
@@ -34,7 +34,7 @@
 
 .. sourcecode:: python
 
-    from cubicweb.web import uicfg
+    from cubicweb.web.views import uicfg
     # force hiding
     uicfg.indexview_etype_section['HideMe'] = 'subobject'
     # force display
@@ -62,51 +62,59 @@
 from cubicweb import neg_role
 from cubicweb.rtags import (RelationTags, RelationTagsBool, RelationTagsSet,
                             RelationTagsDict, NoTargetRelationTagsDict,
-                            register_rtag, _ensure_str_key)
+                            _ensure_str_key)
 from cubicweb.schema import META_RTYPES, INTERNAL_TYPES, WORKFLOW_TYPES
 
 
 # primary view configuration ##################################################
 
-def init_primaryview_section(rtag, sschema, rschema, oschema, role):
-    if rtag.get(sschema, rschema, oschema, role) is None:
-        rdef = rschema.rdef(sschema, oschema)
-        if rschema.final:
-            if rschema.meta or sschema.is_metadata(rschema) \
-                    or oschema.type in ('Password', 'Bytes'):
-                section = 'hidden'
+class PrimaryViewSectionRelationTags(RelationTags):
+    """primary view section configuration"""
+    __regid__ = 'primaryview_section'
+
+    _allowed_values = frozenset(('attributes', 'relations',
+                                 'sideboxes', 'hidden'))
+
+    def _init(self, sschema, rschema, oschema, role):
+        if self.get(sschema, rschema, oschema, role) is None:
+            rdef = rschema.rdef(sschema, oschema)
+            if rschema.final:
+                if rschema.meta or sschema.is_metadata(rschema) \
+                        or oschema.type in ('Password', 'Bytes'):
+                    section = 'hidden'
+                else:
+                    section = 'attributes'
             else:
-                section = 'attributes'
-        else:
-            if rdef.role_cardinality(role) in '1+':
-                section = 'attributes'
-            elif rdef.composite == neg_role(role):
-                section = 'relations'
-            else:
-                section = 'sideboxes'
-        rtag.tag_relation((sschema, rschema, oschema, role), section)
+                if rdef.role_cardinality(role) in '1+':
+                    section = 'attributes'
+                elif rdef.composite == neg_role(role):
+                    section = 'relations'
+                else:
+                    section = 'sideboxes'
+            self.tag_relation((sschema, rschema, oschema, role), section)
 
-primaryview_section = RelationTags('primaryview_section',
-                                   init_primaryview_section,
-                                   frozenset(('attributes', 'relations',
-                                              'sideboxes', 'hidden')))
+primaryview_section = PrimaryViewSectionRelationTags()
 
 
 class DisplayCtrlRelationTags(NoTargetRelationTagsDict):
+    """primary view display controller configuration"""
+    __regid__ = 'primaryview_display_ctrl'
+
     def __init__(self, *args, **kwargs):
         super(DisplayCtrlRelationTags, self).__init__(*args, **kwargs)
         self.counter = 0
 
-def init_primaryview_display_ctrl(rtag, sschema, rschema, oschema, role):
-    if role == 'subject':
-        oschema = '*'
-    else:
-        sschema = '*'
-    rtag.counter += 1
-    rtag.setdefault((sschema, rschema, oschema, role), 'order', rtag.counter)
+    def _init(self, sschema, rschema, oschema, role):
+        if role == 'subject':
+            oschema = '*'
+        else:
+            sschema = '*'
+        self.counter += 1
+        self.setdefault((sschema, rschema, oschema, role),
+                        'order',
+                        self.counter)
 
-primaryview_display_ctrl = DisplayCtrlRelationTags('primaryview_display_ctrl',
-                                                   init_primaryview_display_ctrl)
+primaryview_display_ctrl = DisplayCtrlRelationTags()
 
 
 # index view configuration ####################################################
@@ -117,10 +125,9 @@
 # * 'hidden'
 # * 'subobject' (not displayed by default)
 
-class InitializableDict(dict):
+class InitializableDict(dict): # XXX not a rtag. Turn into an appobject?
     def __init__(self, *args, **kwargs):
         super(InitializableDict, self).__init__(*args, **kwargs)
-        register_rtag(self)
         self.__defaults = dict(self)
 
     def init(self, schema, check=True):
@@ -144,6 +151,7 @@
     CWUser='system', CWGroup='system',
     )
 
+
 # autoform.AutomaticEntityForm configuration ##################################
 
 def _formsections_as_dict(formsections):
@@ -165,6 +173,7 @@
 
 class AutoformSectionRelationTags(RelationTagsSet):
     """autoform relations'section"""
+    __regid__ = 'autoform_section'
 
     _allowed_form_types = ('main', 'inlined', 'muledit')
     _allowed_values = {'main': ('attributes', 'inlined', 'relations',
@@ -177,8 +186,7 @@
         super(AutoformSectionRelationTags, self).init(schema, check)
         self.apply(schema, self._initfunc_step2)
 
-    @staticmethod
-    def _initfunc(self, sschema, rschema, oschema, role):
+    def _init(self, sschema, rschema, oschema, role):
         formsections = self.init_get(sschema, rschema, oschema, role)
         if formsections is None:
             formsections = self.tag_container_cls()
@@ -190,7 +198,6 @@
         key = _ensure_str_key( (sschema, rschema, oschema, role) )
         self._tagdefs[key] = formsections
 
-    @staticmethod
     def _initfunc_step2(self, sschema, rschema, oschema, role):
         formsections = self.get(sschema, rschema, oschema, role)
         sectdict = _formsections_as_dict(formsections)
@@ -266,10 +273,10 @@
                 section, value = tag.split('_', 1)
                 rtags[section] = value
         cls = self.tag_container_cls
-        rtags = cls('_'.join([section,value]) for section,value in rtags.iteritems())
+        rtags = cls('_'.join([section,value])
+                    for section,value in rtags.iteritems())
         return rtags
 
-
     def get(self, *key):
         # overriden to avoid recomputing done in parent classes
         return self._tagdefs.get(key, ())
@@ -284,7 +291,8 @@
         """
         tag = '%s_%s' % (formtype, section)
         eschema  = entity.e_schema
-        permsoverrides = autoform_permissions_overrides
+        cw = entity._cw
+        permsoverrides = cw.vreg['uicfg'].select('autoform_permissions_overrides', cw, entity=entity)
         if entity.has_eid():
             eid = entity.eid
         else:
@@ -296,7 +304,6 @@
         else:
             assert section not in ('attributes', 'metadata', 'hidden')
             relpermission = permission
-        cw = entity._cw
         for rschema, targetschemas, role in eschema.relation_definitions(True):
             _targetschemas = []
             for tschema in targetschemas:
@@ -347,19 +354,29 @@
                     continue
             yield (rschema, targetschemas, role)
 
-autoform_section = AutoformSectionRelationTags('autoform_section')
+autoform_section = AutoformSectionRelationTags()
 
 # relations'field class
-autoform_field = RelationTags('autoform_field')
+class AutoformFieldTags(RelationTags):
+    __regid__ = 'autoform_field'
+
+autoform_field = AutoformFieldTags()
 
 # relations'field explicit kwargs (given to field's __init__)
-autoform_field_kwargs = RelationTagsDict('autoform_field_kwargs')
+class AutoformFieldKwargsTags(RelationTagsDict):
+    __regid__ = 'autoform_field_kwargs'
+
+autoform_field_kwargs = AutoformFieldKwargsTags()
 
 
 # set of tags of the form <action>_on_new on relations. <action> is a
 # schema action (add/update/delete/read), and when such a tag is found
 # permissions checking is by-passed and supposed to be ok
-autoform_permissions_overrides = RelationTagsSet('autoform_permissions_overrides')
+class AutoFormPermissionsOverrides(RelationTagsSet):
+    __regid__ = 'autoform_permissions_overrides'
+
+autoform_permissions_overrides = AutoFormPermissionsOverrides()
+
 
 class ReleditTags(NoTargetRelationTagsDict):
     """Associate to relation a dictionary to control `reledit` (e.g. edition of
@@ -388,6 +405,7 @@
       support target entity edition. By default, the 'related' option is taken
       whenever the relation is composite.
     """
+    __regid__ = 'reledit'
     _keys = frozenset('novalue_label novalue_include_rtype reload rvid edit_target'.split())
 
     def tag_relation(self, key, tag):
@@ -395,42 +413,51 @@
             assert tagkey in self._keys, 'tag %r not in accepted tags: %r' % (tag, self._keys)
         return super(ReleditTags, self).tag_relation(key, tag)
 
-def init_reledit_ctrl(rtag, sschema, rschema, oschema, role):
-    values = rtag.get(sschema, rschema, oschema, role)
-    if not rschema.final:
-        composite = rschema.rdef(sschema, oschema).composite == role
-        if role == 'subject':
-            oschema = '*'
-        else:
-            sschema = '*'
-        edittarget = values.get('edit_target')
-        if edittarget not in (None, 'rtype', 'related'):
-            rtag.warning('reledit: wrong value for edit_target on relation %s: %s',
-                         rschema, edittarget)
-            edittarget = None
-        if not edittarget:
-            edittarget = 'related' if composite else 'rtype'
-            rtag.tag_relation((sschema, rschema, oschema, role),
-                              {'edit_target': edittarget})
-    if not 'novalue_include_rtype' in values:
-        showlabel = primaryview_display_ctrl.get(
-            sschema, rschema, oschema, role).get('showlabel', True)
-        rtag.tag_relation((sschema, rschema, oschema, role),
-                          {'novalue_include_rtype': not showlabel})
+    def _init(self, sschema, rschema, oschema, role):
+        values = self.get(sschema, rschema, oschema, role)
+        if not rschema.final:
+            composite = rschema.rdef(sschema, oschema).composite == role
+            if role == 'subject':
+                oschema = '*'
+            else:
+                sschema = '*'
+            edittarget = values.get('edit_target')
+            if edittarget not in (None, 'rtype', 'related'):
+                self.warning('reledit: wrong value for edit_target on relation %s: %s',
+                             rschema, edittarget)
+                edittarget = None
+            if not edittarget:
+                edittarget = 'related' if composite else 'rtype'
+                self.tag_relation((sschema, rschema, oschema, role),
+                                  {'edit_target': edittarget})
+        if not 'novalue_include_rtype' in values:
+            showlabel = primaryview_display_ctrl.get(
+                sschema, rschema, oschema, role).get('showlabel', True)
+            self.tag_relation((sschema, rschema, oschema, role),
+                              {'novalue_include_rtype': not showlabel})
 
-reledit_ctrl = ReleditTags('reledit', init_reledit_ctrl)
+reledit_ctrl = ReleditTags()
+
 
 # boxes.EditBox configuration #################################################
 
 # 'link' / 'create' relation tags, used to control the "add entity" submenu
-def init_actionbox_appearsin_addmenu(rtag, sschema, rschema, oschema, role):
-    if rtag.get(sschema, rschema, oschema, role) is None:
-        if rschema in META_RTYPES:
-            rtag.tag_relation((sschema, rschema, oschema, role), False)
-            return
-        rdef = rschema.rdef(sschema, oschema)
-        if not rdef.role_cardinality(role) in '?1' and rdef.composite == role:
-            rtag.tag_relation((sschema, rschema, oschema, role), True)
+class ActionBoxUicfg(RelationTagsBool):
+    __regid__ = 'actionbox_appearsin_addmenu'
 
-actionbox_appearsin_addmenu = RelationTagsBool('actionbox_appearsin_addmenu',
-                                               init_actionbox_appearsin_addmenu)
+    def _init(self, sschema, rschema, oschema, role):
+        if self.get(sschema, rschema, oschema, role) is None:
+            if rschema in META_RTYPES:
+                self.tag_relation((sschema, rschema, oschema, role), False)
+                return
+            rdef = rschema.rdef(sschema, oschema)
+            if not rdef.role_cardinality(role) in '?1' and rdef.composite == role:
+                self.tag_relation((sschema, rschema, oschema, role), True)
+
+actionbox_appearsin_addmenu = ActionBoxUicfg()
+
+
+
+def registration_callback(vreg):
+    vreg.register_all(globals().values(), __name__)
+    indexview_etype_section.init(vreg.schema)