# HG changeset patch # User Florent Cayré # Date 1358782449 -3600 # Node ID 1dd655788ece6018e3bf7d5486d3802e5a7b14d5 # Parent e65af61bde7d8056fa184693791bfd75be72cbca 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 diff -r e65af61bde7d -r 1dd655788ece cwvreg.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 #################################################### diff -r e65af61bde7d -r 1dd655788ece devtools/devctl.py --- 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() diff -r e65af61bde7d -r 1dd655788ece rtags.py --- 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 '' + # _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')) diff -r e65af61bde7d -r 1dd655788ece server/hook.py --- 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)() diff -r e65af61bde7d -r 1dd655788ece view.py --- 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): diff -r e65af61bde7d -r 1dd655788ece web/formfields.py --- 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) diff -r e65af61bde7d -r 1dd655788ece web/test/unittest_formfields.py --- 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', '')]) diff -r e65af61bde7d -r 1dd655788ece web/test/unittest_uicfg.py --- 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 diff -r e65af61bde7d -r 1dd655788ece web/views/actions.py --- 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()), diff -r e65af61bde7d -r 1dd655788ece web/views/autoform.py --- 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', '*'), diff -r e65af61bde7d -r 1dd655788ece web/views/forms.py --- 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 diff -r e65af61bde7d -r 1dd655788ece web/views/management.py --- 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) diff -r e65af61bde7d -r 1dd655788ece web/views/primary.py --- 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): diff -r e65af61bde7d -r 1dd655788ece web/views/reledit.py --- 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, ' diff -r e65af61bde7d -r 1dd655788ece web/views/uicfg.py --- 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 . -"""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 _on_new on relations. 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)