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