cubicweb/web/views/uicfg.py
changeset 11057 0b59724cb3f2
parent 10663 54b8a1f249fb
child 11335 507ff9e71269
equal deleted inserted replaced
11052:058bb3dc685f 11057:0b59724cb3f2
       
     1 # copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
       
     2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
       
     3 #
       
     4 # This file is part of CubicWeb.
       
     5 #
       
     6 # CubicWeb is free software: you can redistribute it and/or modify it under the
       
     7 # terms of the GNU Lesser General Public License as published by the Free
       
     8 # Software Foundation, either version 2.1 of the License, or (at your option)
       
     9 # any later version.
       
    10 #
       
    11 # CubicWeb is distributed in the hope that it will be useful, but WITHOUT
       
    12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
       
    13 # FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
       
    14 # details.
       
    15 #
       
    16 # You should have received a copy of the GNU Lesser General Public License along
       
    17 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
       
    18 """This module (``cubicweb.web.views.uicfg``) regroups a set of structures that may be
       
    19 used to configure various options of the generated web interface.
       
    20 
       
    21 To configure the interface generation, we use ``RelationTag`` objects.
       
    22 
       
    23 Index view configuration
       
    24 ````````````````````````
       
    25 :indexview_etype_section:
       
    26    entity type category in the index/manage page. May be one of:
       
    27 
       
    28       * ``application``
       
    29       * ``system``
       
    30       * ``schema``
       
    31       * ``subobject`` (not displayed by default)
       
    32 
       
    33    By default only entities on the ``application`` category are shown.
       
    34 
       
    35 .. sourcecode:: python
       
    36 
       
    37     from cubicweb.web.views import uicfg
       
    38     # force hiding
       
    39     uicfg.indexview_etype_section['HideMe'] = 'subobject'
       
    40     # force display
       
    41     uicfg.indexview_etype_section['ShowMe'] = 'application'
       
    42 
       
    43 
       
    44 Actions box configuration
       
    45 `````````````````````````
       
    46 :actionbox_appearsin_addmenu:
       
    47   simple boolean relation tags used to control the "add entity" submenu.
       
    48   Relations whose rtag is True will appears, other won't.
       
    49 
       
    50 .. sourcecode:: python
       
    51 
       
    52    # Adds all subjects of the entry_of relation in the add menu of the ``Blog``
       
    53    # primary view
       
    54    uicfg.actionbox_appearsin_addmenu.tag_object_of(('*', 'entry_of', 'Blog'), True)
       
    55 """
       
    56 __docformat__ = "restructuredtext en"
       
    57 
       
    58 from warnings import warn
       
    59 
       
    60 from six import string_types
       
    61 
       
    62 from cubicweb import neg_role
       
    63 from cubicweb.rtags import (RelationTags, RelationTagsBool, RelationTagsSet,
       
    64                             RelationTagsDict, NoTargetRelationTagsDict,
       
    65                             _ensure_str_key)
       
    66 from cubicweb.schema import META_RTYPES, INTERNAL_TYPES, WORKFLOW_TYPES
       
    67 
       
    68 
       
    69 # primary view configuration ##################################################
       
    70 
       
    71 class PrimaryViewSectionRelationTags(RelationTags):
       
    72     """primary view section configuration"""
       
    73     __regid__ = 'primaryview_section'
       
    74 
       
    75     _allowed_values = frozenset(('attributes', 'relations',
       
    76                                  'sideboxes', 'hidden'))
       
    77 
       
    78     def _init(self, sschema, rschema, oschema, role):
       
    79         if self.get(sschema, rschema, oschema, role) is None:
       
    80             rdef = rschema.rdef(sschema, oschema)
       
    81             if rschema.final:
       
    82                 if rschema.meta or sschema.is_metadata(rschema) \
       
    83                         or oschema.type in ('Password', 'Bytes'):
       
    84                     section = 'hidden'
       
    85                 else:
       
    86                     section = 'attributes'
       
    87             else:
       
    88                 if rdef.role_cardinality(role) in '1+':
       
    89                     section = 'attributes'
       
    90                 elif rdef.composite == neg_role(role):
       
    91                     section = 'relations'
       
    92                 else:
       
    93                     section = 'sideboxes'
       
    94             self.tag_relation((sschema, rschema, oschema, role), section)
       
    95 
       
    96 primaryview_section = PrimaryViewSectionRelationTags()
       
    97 
       
    98 
       
    99 class DisplayCtrlRelationTags(NoTargetRelationTagsDict):
       
   100     """primary view display controller configuration"""
       
   101     __regid__ = 'primaryview_display_ctrl'
       
   102 
       
   103     def __init__(self, *args, **kwargs):
       
   104         super(DisplayCtrlRelationTags, self).__init__(*args, **kwargs)
       
   105         self.counter = 0
       
   106 
       
   107     def _init(self, sschema, rschema, oschema, role):
       
   108         if role == 'subject':
       
   109             oschema = '*'
       
   110         else:
       
   111             sschema = '*'
       
   112         self.counter += 1
       
   113         self.setdefault((sschema, rschema, oschema, role),
       
   114                         'order',
       
   115                         self.counter)
       
   116 
       
   117     def set_fields_order(self, etype, relations):
       
   118         """specify the field order in `etype` primary view.
       
   119 
       
   120         :param etype: the entity type as a string
       
   121         :param attrs: the ordered list of attribute names (or relations)
       
   122 
       
   123         `attrs` can be strings or 2-tuples (relname, role_of_etype_in_the_rel)
       
   124 
       
   125         Unspecified fields will be displayed after specified ones, their
       
   126         order being consistent with the schema definition.
       
   127 
       
   128         Examples:
       
   129 
       
   130         .. sourcecode:: python
       
   131 
       
   132           from cubicweb.web.views.uicfg import primaryview_display_ctrl as pvdc
       
   133           pvdc.set_fields_order('CWUser', ('firstname', ('in_group', 'subject'),
       
   134                                            'surname', 'login'))
       
   135 
       
   136         """
       
   137         for index, relation in enumerate(relations):
       
   138             if not isinstance(relation, tuple):
       
   139                 relation = (relation, 'subject')
       
   140             rtype, role = relation
       
   141             if role == 'subject':
       
   142                 self.tag_subject_of((etype, rtype, '*'), {'order': index})
       
   143             else:
       
   144                 self.tag_object_of((etype, rtype, '*'), {'order': index})
       
   145 
       
   146 
       
   147 primaryview_display_ctrl = DisplayCtrlRelationTags()
       
   148 
       
   149 
       
   150 # index view configuration ####################################################
       
   151 # entity type section in the index/manage page. May be one of
       
   152 # * 'application'
       
   153 # * 'system'
       
   154 # * 'schema'
       
   155 # * 'hidden'
       
   156 # * 'subobject' (not displayed by default)
       
   157 
       
   158 class InitializableDict(dict): # XXX not a rtag. Turn into an appobject?
       
   159     def __init__(self, *args, **kwargs):
       
   160         super(InitializableDict, self).__init__(*args, **kwargs)
       
   161         self.__defaults = dict(self)
       
   162 
       
   163     def init(self, schema, check=True):
       
   164         self.update(self.__defaults)
       
   165         for eschema in schema.entities():
       
   166             if eschema.final:
       
   167                 continue
       
   168             if eschema.schema_entity():
       
   169                 self.setdefault(eschema, 'schema')
       
   170             elif eschema in INTERNAL_TYPES or eschema in WORKFLOW_TYPES:
       
   171                 self.setdefault(eschema, 'system')
       
   172             elif eschema.is_subobject(strict=True):
       
   173                 self.setdefault(eschema, 'subobject')
       
   174             else:
       
   175                 self.setdefault(eschema, 'application')
       
   176 
       
   177 indexview_etype_section = InitializableDict(
       
   178     EmailAddress='subobject',
       
   179     Bookmark='system',
       
   180     # entity types in the 'system' table by default (managers only)
       
   181     CWUser='system', CWGroup='system',
       
   182     )
       
   183 
       
   184 
       
   185 # autoform.AutomaticEntityForm configuration ##################################
       
   186 
       
   187 def _formsections_as_dict(formsections):
       
   188     result = {}
       
   189     for formsection in formsections:
       
   190         formtype, section = formsection.split('_', 1)
       
   191         result[formtype] = section
       
   192     return result
       
   193 
       
   194 def _card_and_comp(sschema, rschema, oschema, role):
       
   195     rdef = rschema.rdef(sschema, oschema)
       
   196     if role == 'subject':
       
   197         card = rdef.cardinality[0]
       
   198         composed = not rschema.final and rdef.composite == 'object'
       
   199     else:
       
   200         card = rdef.cardinality[1]
       
   201         composed = not rschema.final and rdef.composite == 'subject'
       
   202     return card, composed
       
   203 
       
   204 class AutoformSectionRelationTags(RelationTagsSet):
       
   205     """autoform relations'section"""
       
   206     __regid__ = 'autoform_section'
       
   207 
       
   208     _allowed_form_types = ('main', 'inlined', 'muledit')
       
   209     _allowed_values = {'main': ('attributes', 'inlined', 'relations',
       
   210                                 'metadata', 'hidden'),
       
   211                        'inlined': ('attributes', 'inlined', 'hidden'),
       
   212                        'muledit': ('attributes', 'hidden'),
       
   213                        }
       
   214 
       
   215     def init(self, schema, check=True):
       
   216         super(AutoformSectionRelationTags, self).init(schema, check)
       
   217         self.apply(schema, self._initfunc_step2)
       
   218 
       
   219     def _init(self, sschema, rschema, oschema, role):
       
   220         formsections = self.init_get(sschema, rschema, oschema, role)
       
   221         if formsections is None:
       
   222             formsections = self.tag_container_cls()
       
   223         if not any(tag.startswith('inlined') for tag in formsections):
       
   224             if not rschema.final:
       
   225                 negsects = self.init_get(sschema, rschema, oschema, neg_role(role))
       
   226                 if 'main_inlined' in negsects:
       
   227                     formsections.add('inlined_hidden')
       
   228         key = _ensure_str_key( (sschema, rschema, oschema, role) )
       
   229         self._tagdefs[key] = formsections
       
   230 
       
   231     def _initfunc_step2(self, sschema, rschema, oschema, role):
       
   232         formsections = self.get(sschema, rschema, oschema, role)
       
   233         sectdict = _formsections_as_dict(formsections)
       
   234         if rschema in META_RTYPES:
       
   235             sectdict.setdefault('main', 'hidden')
       
   236             sectdict.setdefault('muledit', 'hidden')
       
   237             sectdict.setdefault('inlined', 'hidden')
       
   238         elif role == 'subject' and rschema in sschema.meta_attributes():
       
   239             # meta attribute, usually embeded by the described attribute's field
       
   240             # (eg RichTextField, FileField...)
       
   241             sectdict.setdefault('main', 'hidden')
       
   242             sectdict.setdefault('muledit', 'hidden')
       
   243             sectdict.setdefault('inlined', 'hidden')
       
   244         # ensure we have a tag for each form type
       
   245         if not 'main' in sectdict:
       
   246             if not rschema.final and (
       
   247                 sectdict.get('inlined') == 'attributes' or
       
   248                 'inlined_attributes' in self.init_get(sschema, rschema, oschema,
       
   249                                                       neg_role(role))):
       
   250                 sectdict['main'] = 'hidden'
       
   251             elif sschema.is_metadata(rschema):
       
   252                 sectdict['main'] = 'metadata'
       
   253             else:
       
   254                 card, composed = _card_and_comp(sschema, rschema, oschema, role)
       
   255                 if card in '1+':
       
   256                     sectdict['main'] = 'attributes'
       
   257                     if not 'muledit' in sectdict:
       
   258                         sectdict['muledit'] = 'attributes'
       
   259                 elif rschema.final:
       
   260                     sectdict['main'] = 'attributes'
       
   261                 else:
       
   262                     sectdict['main'] = 'relations'
       
   263         if not 'muledit' in sectdict:
       
   264             sectdict['muledit'] = 'hidden'
       
   265             if sectdict['main'] == 'attributes':
       
   266                 card, composed = _card_and_comp(sschema, rschema, oschema, role)
       
   267                 if card in '1+' and not composed:
       
   268                     sectdict['muledit'] = 'attributes'
       
   269         if not 'inlined' in sectdict:
       
   270             sectdict['inlined'] = sectdict['main']
       
   271         # recompute formsections and set it to avoid recomputing
       
   272         for formtype, section in sectdict.items():
       
   273             formsections.add('%s_%s' % (formtype, section))
       
   274 
       
   275     def tag_relation(self, key, formtype, section):
       
   276         if isinstance(formtype, tuple):
       
   277             for ftype in formtype:
       
   278                 self.tag_relation(key, ftype, section)
       
   279             return
       
   280         assert formtype in self._allowed_form_types, \
       
   281                'formtype should be in (%s), not %s' % (
       
   282             ','.join(self._allowed_form_types), formtype)
       
   283         assert section in self._allowed_values[formtype], \
       
   284                'section for %s should be in (%s), not %s' % (
       
   285             formtype, ','.join(self._allowed_values[formtype]), section)
       
   286         rtags = self._tagdefs.setdefault(_ensure_str_key(key),
       
   287                                          self.tag_container_cls())
       
   288         # remove previous section for this form type if any
       
   289         if rtags:
       
   290             for tag in rtags.copy():
       
   291                 if tag.startswith(formtype):
       
   292                     rtags.remove(tag)
       
   293         rtags.add('%s_%s' % (formtype, section))
       
   294         return rtags
       
   295 
       
   296     def init_get(self, stype, rtype, otype, tagged):
       
   297         key = (stype, rtype, otype, tagged)
       
   298         rtags = {}
       
   299         for key in self._get_keys(stype, rtype, otype, tagged):
       
   300             tags = self._tagdefs.get(key, ())
       
   301             for tag in tags:
       
   302                 assert '_' in tag, (tag, tags)
       
   303                 section, value = tag.split('_', 1)
       
   304                 rtags[section] = value
       
   305         cls = self.tag_container_cls
       
   306         rtags = cls('_'.join([section,value])
       
   307                     for section,value in rtags.items())
       
   308         return rtags
       
   309 
       
   310     def get(self, *key):
       
   311         # overriden to avoid recomputing done in parent classes
       
   312         return self._tagdefs.get(key, ())
       
   313 
       
   314     def relations_by_section(self, entity, formtype, section, permission,
       
   315                              strict=False):
       
   316         """return a list of (relation schema, target schemas, role) for the
       
   317         given entity matching categories and permission.
       
   318 
       
   319         `strict`:
       
   320           bool telling if having local role is enough (strict = False) or not
       
   321         """
       
   322         tag = '%s_%s' % (formtype, section)
       
   323         eschema  = entity.e_schema
       
   324         cw = entity._cw
       
   325         permsoverrides = cw.vreg['uicfg'].select('autoform_permissions_overrides', cw, entity=entity)
       
   326         if entity.has_eid():
       
   327             eid = entity.eid
       
   328         else:
       
   329             eid = None
       
   330             strict = False
       
   331         if permission == 'update':
       
   332             assert section in ('attributes', 'metadata', 'hidden')
       
   333             relpermission = 'add'
       
   334         else:
       
   335             assert section not in ('metadata', 'hidden')
       
   336             relpermission = permission
       
   337         for rschema, targetschemas, role in eschema.relation_definitions(True):
       
   338             _targetschemas = []
       
   339             for tschema in targetschemas:
       
   340                 # check section's tag first, potentially lower cost than
       
   341                 # checking permission which may imply rql queries
       
   342                 if not tag in self.etype_get(eschema, rschema, role, tschema):
       
   343                     continue
       
   344                 rdef = rschema.role_rdef(eschema, tschema, role)
       
   345                 if rschema.final:
       
   346                     if not rdef.has_perm(cw, permission, eid=eid,
       
   347                                          creating=eid is None):
       
   348                         continue
       
   349                 elif strict or not rdef.has_local_role(relpermission):
       
   350                     if role == 'subject':
       
   351                         if not rdef.has_perm(cw, relpermission, fromeid=eid):
       
   352                             continue
       
   353                     elif role == 'object':
       
   354                         if not rdef.has_perm(cw, relpermission, toeid=eid):
       
   355                             continue
       
   356                 _targetschemas.append(tschema)
       
   357             if not _targetschemas:
       
   358                 continue
       
   359             targetschemas = _targetschemas
       
   360             rdef = eschema.rdef(rschema, role=role, targettype=targetschemas[0])
       
   361             # XXX tag allowing to hijack the permission machinery when
       
   362             # permission is not verifiable until the entity is actually
       
   363             # created...
       
   364             if eid is None and '%s_on_new' % permission in permsoverrides.etype_get(eschema, rschema, role):
       
   365                 yield (rschema, targetschemas, role)
       
   366                 continue
       
   367             if not rschema.final and role == 'subject':
       
   368                 # on relation with cardinality 1 or ?, we need delete perm as well
       
   369                 # if the relation is already set
       
   370                 if (relpermission == 'add'
       
   371                     and rdef.role_cardinality(role) in '1?'
       
   372                     and eid and entity.related(rschema.type, role)
       
   373                     and not rdef.has_perm(cw, 'delete', fromeid=eid,
       
   374                                           toeid=entity.related(rschema.type, role)[0][0])):
       
   375                     continue
       
   376             elif role == 'object':
       
   377                 # on relation with cardinality 1 or ?, we need delete perm as well
       
   378                 # if the relation is already set
       
   379                 if (relpermission == 'add'
       
   380                     and rdef.role_cardinality(role) in '1?'
       
   381                     and eid and entity.related(rschema.type, role)
       
   382                     and not rdef.has_perm(cw, 'delete', toeid=eid,
       
   383                                           fromeid=entity.related(rschema.type, role)[0][0])):
       
   384                     continue
       
   385             yield (rschema, targetschemas, role)
       
   386 
       
   387     def hide_field(self, etype, attr, desttype='*', formtype='main'):
       
   388         """hide `attr` in `etype` forms.
       
   389 
       
   390         :param etype: the entity type as a string
       
   391         :param attr: the name of the attribute or relation to hide
       
   392         :param formtype: which form will be affected ('main', 'inlined', etc.),
       
   393          *main* by default.
       
   394 
       
   395         `attr` can be a string or 2-tuple (relname, role_of_etype_in_the_rel)
       
   396 
       
   397         Examples:
       
   398 
       
   399         .. sourcecode:: python
       
   400 
       
   401           from cubicweb.web.views.uicfg import autoform_section as afs
       
   402           afs.hide_field('CWUser', 'login')
       
   403           afs.hide_field('*', 'name')
       
   404           afs.hide_field('CWUser', 'use_email', formtype='inlined')
       
   405 
       
   406         """
       
   407         self._tag_etype_attr(etype, attr, desttype,
       
   408                              formtype=formtype, section='hidden')
       
   409 
       
   410     def hide_fields(self, etype, attrs, formtype='main'):
       
   411         """simple for-loop wrapper around :func:`hide_field`.
       
   412 
       
   413         :param etype: the entity type as a string
       
   414         :param attrs: the ordered list of attribute names (or relations)
       
   415         :param formtype: which form will be affected ('main', 'inlined', etc.),
       
   416          *main* by default.
       
   417 
       
   418         `attrs` can be strings or 2-tuples (relname, role_of_etype_in_the_rel)
       
   419 
       
   420         Examples:
       
   421 
       
   422         .. sourcecode:: python
       
   423 
       
   424           from cubicweb.web.views.uicfg import autoform_section as afs
       
   425           afs.hide_fields('CWUser', ('login', ('use_email', 'subject')),
       
   426                           formtype='inlined')
       
   427         """
       
   428         for attr in attrs:
       
   429             self.hide_field(etype, attr, formtype=formtype)
       
   430 
       
   431     def edit_inline(self, etype, attr, desttype='*', formtype=('main', 'inlined')):
       
   432         """edit `attr` with and inlined form.
       
   433 
       
   434         :param etype: the entity type as a string
       
   435         :param attr: the name of the attribute or relation
       
   436         :param desttype: the destination type(s) concerned, default is everything
       
   437         :param formtype: which form will be affected ('main', 'inlined', etc.),
       
   438           *main* and *inlined* by default.
       
   439 
       
   440         `attr` can be a string or 2-tuple (relname, role_of_etype_in_the_relation)
       
   441 
       
   442         Examples:
       
   443 
       
   444         .. sourcecode:: python
       
   445 
       
   446           from cubicweb.web.views.uicfg import autoform_section as afs
       
   447 
       
   448           afs.edit_inline('*', 'use_email')
       
   449       """
       
   450         self._tag_etype_attr(etype, attr, desttype, formtype=formtype,
       
   451                              section='inlined')
       
   452 
       
   453     def edit_as_attr(self, etype, attr, desttype='*', formtype=('main', 'muledit')):
       
   454         """make `attr` appear in the *attributes* section of `etype` form.
       
   455 
       
   456         :param etype: the entity type as a string
       
   457         :param attr: the name of the attribute or relation
       
   458         :param desttype: the destination type(s) concerned, default is everything
       
   459         :param formtype: which form will be affected ('main', 'inlined', etc.),
       
   460           *main* and *muledit* by default.
       
   461 
       
   462         `attr` can be a string or 2-tuple (relname, role_of_etype_in_the_relation)
       
   463 
       
   464         Examples:
       
   465 
       
   466         .. sourcecode:: python
       
   467 
       
   468           from cubicweb.web.views.uicfg import autoform_section as afs
       
   469 
       
   470           afs.edit_as_attr('CWUser', 'in_group')
       
   471         """
       
   472         self._tag_etype_attr(etype, attr, desttype,
       
   473                              formtype=formtype, section='attributes')
       
   474 
       
   475     def set_muledit_editable(self, etype, attrs):
       
   476         """make `attrs` appear in muledit form of `etype`.
       
   477 
       
   478         :param etype: the entity type as a string
       
   479         :param attrs: the ordered list of attribute names (or relations)
       
   480 
       
   481         `attrs` can be strings or 2-tuples (relname, role_of_etype_in_the_relation)
       
   482 
       
   483         Examples:
       
   484 
       
   485         .. sourcecode:: python
       
   486 
       
   487           from cubicweb.web.views.uicfg import autoform_section as afs
       
   488 
       
   489           afs.set_muledit_editable('CWUser', ('firstname', 'surname', 'in_group'))
       
   490         """
       
   491         for attr in attrs:
       
   492             self.edit_as_attr(self, etype, attr, formtype='muledit')
       
   493 
       
   494 autoform_section = AutoformSectionRelationTags()
       
   495 
       
   496 
       
   497 # relations'field class
       
   498 
       
   499 class AutoformFieldTags(RelationTags):
       
   500     __regid__ = 'autoform_field'
       
   501 
       
   502     def set_field(self, etype, attr, field):
       
   503         """sets the `attr` field of `etype`.
       
   504 
       
   505         :param etype: the entity type as a string
       
   506         :param attr: the name of the attribute or relation
       
   507 
       
   508         `attr` can be a string or 2-tuple (relname, role_of_etype_in_the_relation)
       
   509 
       
   510         """
       
   511         self._tag_etype_attr(etype, attr, '*', field)
       
   512 
       
   513 autoform_field = AutoformFieldTags()
       
   514 
       
   515 
       
   516 # relations'field explicit kwargs (given to field's __init__)
       
   517 
       
   518 class AutoformFieldKwargsTags(RelationTagsDict):
       
   519     __regid__ = 'autoform_field_kwargs'
       
   520 
       
   521     def set_fields_order(self, etype, attrs):
       
   522         """specify the field order in `etype` main edition form.
       
   523 
       
   524         :param etype: the entity type as a string
       
   525         :param attrs: the ordered list of attribute names (or relations)
       
   526 
       
   527         `attrs` can be strings or 2-tuples (relname, role_of_etype_in_the_rel)
       
   528 
       
   529         Unspecified fields will be displayed after specified ones, their
       
   530         order being consistent with the schema definition.
       
   531 
       
   532         Examples:
       
   533 
       
   534         .. sourcecode:: python
       
   535 
       
   536           from cubicweb.web.views.uicfg import autoform_field_kwargs as affk
       
   537           affk.set_fields_order('CWUser', ('firstname', 'surname', 'login'))
       
   538           affk.set_fields_order('CWUser', ('firstname', ('in_group', 'subject'),
       
   539                                 'surname', 'login'))
       
   540 
       
   541         """
       
   542         for index, attr in enumerate(attrs):
       
   543             self._tag_etype_attr(etype, attr, '*', {'order': index})
       
   544 
       
   545     def set_field_kwargs(self, etype, attr, **kwargs):
       
   546         """tag `attr` field of `etype` with additional named paremeters.
       
   547 
       
   548         :param etype: the entity type as a string
       
   549         :param attr: the name of the attribute or relation
       
   550 
       
   551         `attr` can be a string or 2-tuple (relname, role_of_etype_in_the_relation)
       
   552 
       
   553         Examples:
       
   554 
       
   555         .. sourcecode:: python
       
   556 
       
   557           from cubicweb.web.views.uicfg import autoform_field_kwargs as affk
       
   558           affk.set_field_kwargs('Person', 'works_for', widget=fwdgs.AutoCompletionWidget())
       
   559           affk.set_field_kwargs('CWUser', 'login', label=_('login or email address'),
       
   560                                 widget=fwdgs.TextInput(attrs={'size': 30}))
       
   561         """
       
   562         self._tag_etype_attr(etype, attr, '*', kwargs)
       
   563 
       
   564 
       
   565 autoform_field_kwargs = AutoformFieldKwargsTags()
       
   566 
       
   567 
       
   568 # set of tags of the form <action>_on_new on relations. <action> is a
       
   569 # schema action (add/update/delete/read), and when such a tag is found
       
   570 # permissions checking is by-passed and supposed to be ok
       
   571 class AutoFormPermissionsOverrides(RelationTagsSet):
       
   572     __regid__ = 'autoform_permissions_overrides'
       
   573 
       
   574 autoform_permissions_overrides = AutoFormPermissionsOverrides()
       
   575 
       
   576 
       
   577 class ReleditTags(NoTargetRelationTagsDict):
       
   578     """Associate to relation a dictionary to control `reledit` (e.g. edition of
       
   579     attributes / relations from within views).
       
   580 
       
   581     Possible keys and associated values are:
       
   582 
       
   583     * `novalue_label`, alternative default value (shown when there is no value).
       
   584 
       
   585     * `novalue_include_rtype`, when `novalue_label` is not specified, this boolean
       
   586       flag control wether the generated default value should contains the
       
   587       relation label or not. Will be the opposite of the `showlabel` value found
       
   588       in the `primaryview_display_ctrl` rtag by default.
       
   589 
       
   590     * `reload`, boolean, eid (to reload to) or function taking subject and
       
   591       returning bool/eid. This is useful when editing a relation (or attribute)
       
   592       that impacts the url or another parts of the current displayed
       
   593       page. Defaults to False.
       
   594 
       
   595     * `rvid`, alternative view id (as str) for relation or composite edition.
       
   596       Default is 'autolimited'.
       
   597 
       
   598     * `edit_target`, may be either 'rtype' (to edit the relation) or 'related'
       
   599       (to edit the related entity).  This controls whether to edit the relation
       
   600       or the target entity of the relation.  Currently only one-to-one relations
       
   601       support target entity edition. By default, the 'related' option is taken
       
   602       whenever the relation is composite.
       
   603     """
       
   604     __regid__ = 'reledit'
       
   605     _keys = frozenset('novalue_label novalue_include_rtype reload rvid edit_target'.split())
       
   606 
       
   607     def tag_relation(self, key, tag):
       
   608         for tagkey in tag:
       
   609             assert tagkey in self._keys, 'tag %r not in accepted tags: %r' % (tag, self._keys)
       
   610         return super(ReleditTags, self).tag_relation(key, tag)
       
   611 
       
   612     def _init(self, sschema, rschema, oschema, role):
       
   613         values = self.get(sschema, rschema, oschema, role)
       
   614         if not rschema.final:
       
   615             composite = rschema.rdef(sschema, oschema).composite == role
       
   616             if role == 'subject':
       
   617                 oschema = '*'
       
   618             else:
       
   619                 sschema = '*'
       
   620             edittarget = values.get('edit_target')
       
   621             if edittarget not in (None, 'rtype', 'related'):
       
   622                 self.warning('reledit: wrong value for edit_target on relation %s: %s',
       
   623                              rschema, edittarget)
       
   624                 edittarget = None
       
   625             if not edittarget:
       
   626                 edittarget = 'related' if composite else 'rtype'
       
   627                 self.tag_relation((sschema, rschema, oschema, role),
       
   628                                   {'edit_target': edittarget})
       
   629         if not 'novalue_include_rtype' in values:
       
   630             showlabel = primaryview_display_ctrl.get(
       
   631                 sschema, rschema, oschema, role).get('showlabel', True)
       
   632             self.tag_relation((sschema, rschema, oschema, role),
       
   633                               {'novalue_include_rtype': not showlabel})
       
   634 
       
   635 reledit_ctrl = ReleditTags()
       
   636 
       
   637 
       
   638 # boxes.EditBox configuration #################################################
       
   639 
       
   640 # 'link' / 'create' relation tags, used to control the "add entity" submenu
       
   641 
       
   642 class ActionBoxUicfg(RelationTagsBool):
       
   643     __regid__ = 'actionbox_appearsin_addmenu'
       
   644 
       
   645     def _init(self, sschema, rschema, oschema, role):
       
   646         if self.get(sschema, rschema, oschema, role) is None:
       
   647             if rschema in META_RTYPES:
       
   648                 self.tag_relation((sschema, rschema, oschema, role), False)
       
   649                 return
       
   650             rdef = rschema.rdef(sschema, oschema)
       
   651             if not rdef.role_cardinality(role) in '?1' and rdef.composite == role:
       
   652                 self.tag_relation((sschema, rschema, oschema, role), True)
       
   653 
       
   654     def _tag_etype_attr(self, etype, attr, desttype='*', *args, **kwargs):
       
   655         if isinstance(attr, string_types):
       
   656             attr, role = attr, 'subject'
       
   657         else:
       
   658             attr, role = attr
       
   659         if role == 'subject':
       
   660             self.tag_subject_of((etype, attr, desttype), *args, **kwargs)
       
   661         else:
       
   662             self.tag_object_of((desttype, attr, etype), *args, **kwargs)
       
   663 
       
   664     def append_to_addmenu(self, etype, attr, createdtype='*'):
       
   665         """adds `attr` in the actions box *addrelated* submenu of `etype`.
       
   666 
       
   667         :param etype: the entity type as a string
       
   668         :param attr: the name of the attribute or relation to hide
       
   669         :param createdtype: the target type of the relation (optional, defaults to '*' (all possible types))
       
   670 
       
   671         `attr` can be a string or 2-tuple (relname, role_of_etype_in_the_relation)
       
   672 
       
   673         """
       
   674         self._tag_etype_attr(etype, attr, createdtype, True)
       
   675 
       
   676     def remove_from_addmenu(self, etype, attr, createdtype='*'):
       
   677         """removes `attr` from the actions box *addrelated* submenu of `etype`.
       
   678 
       
   679         :param etype: the entity type as a string
       
   680         :param attr: the name of the attribute or relation to hide
       
   681         :param createdtype: the target type of the relation (optional, defaults to '*' (all possible types))
       
   682 
       
   683         `attr` can be a string or 2-tuple (relname, role_of_etype_in_the_relation)
       
   684         """
       
   685         self._tag_etype_attr(etype, attr, createdtype, False)
       
   686 
       
   687 actionbox_appearsin_addmenu = ActionBoxUicfg()
       
   688 
       
   689 
       
   690 
       
   691 def registration_callback(vreg):
       
   692     vreg.register_all(globals().values(), __name__)
       
   693     indexview_etype_section.init(vreg.schema)