web/views/uicfg.py
changeset 8666 1dd655788ece
parent 8665 e65af61bde7d
child 8667 5a394fc419b4
equal deleted inserted replaced
8665:e65af61bde7d 8666:1dd655788ece
     1 # copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
     1 # copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
     2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
     2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
     3 #
     3 #
     4 # This file is part of CubicWeb.
     4 # This file is part of CubicWeb.
     5 #
     5 #
     6 # CubicWeb is free software: you can redistribute it and/or modify it under the
     6 # CubicWeb is free software: you can redistribute it and/or modify it under the
    13 # FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
    13 # FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
    14 # details.
    14 # details.
    15 #
    15 #
    16 # You should have received a copy of the GNU Lesser General Public License along
    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/>.
    17 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
    18 """This module (``cubicweb.web.uicfg``) regroups a set of structures that may be
    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.
    19 used to configure various options of the generated web interface.
    20 
    20 
    21 To configure the interface generation, we use ``RelationTag`` objects.
    21 To configure the interface generation, we use ``RelationTag`` objects.
    22 
    22 
    23 Index view configuration
    23 Index view configuration
    32 
    32 
    33    By default only entities on the ``application`` category are shown.
    33    By default only entities on the ``application`` category are shown.
    34 
    34 
    35 .. sourcecode:: python
    35 .. sourcecode:: python
    36 
    36 
    37     from cubicweb.web import uicfg
    37     from cubicweb.web.views import uicfg
    38     # force hiding
    38     # force hiding
    39     uicfg.indexview_etype_section['HideMe'] = 'subobject'
    39     uicfg.indexview_etype_section['HideMe'] = 'subobject'
    40     # force display
    40     # force display
    41     uicfg.indexview_etype_section['ShowMe'] = 'application'
    41     uicfg.indexview_etype_section['ShowMe'] = 'application'
    42 
    42 
    60 from logilab.common.compat import any
    60 from logilab.common.compat import any
    61 
    61 
    62 from cubicweb import neg_role
    62 from cubicweb import neg_role
    63 from cubicweb.rtags import (RelationTags, RelationTagsBool, RelationTagsSet,
    63 from cubicweb.rtags import (RelationTags, RelationTagsBool, RelationTagsSet,
    64                             RelationTagsDict, NoTargetRelationTagsDict,
    64                             RelationTagsDict, NoTargetRelationTagsDict,
    65                             register_rtag, _ensure_str_key)
    65                             _ensure_str_key)
    66 from cubicweb.schema import META_RTYPES, INTERNAL_TYPES, WORKFLOW_TYPES
    66 from cubicweb.schema import META_RTYPES, INTERNAL_TYPES, WORKFLOW_TYPES
    67 
    67 
    68 
    68 
    69 # primary view configuration ##################################################
    69 # primary view configuration ##################################################
    70 
    70 
    71 def init_primaryview_section(rtag, sschema, rschema, oschema, role):
    71 class PrimaryViewSectionRelationTags(RelationTags):
    72     if rtag.get(sschema, rschema, oschema, role) is None:
    72     """primary view section configuration"""
    73         rdef = rschema.rdef(sschema, oschema)
    73     __regid__ = 'primaryview_section'
    74         if rschema.final:
    74 
    75             if rschema.meta or sschema.is_metadata(rschema) \
    75     _allowed_values = frozenset(('attributes', 'relations',
    76                     or oschema.type in ('Password', 'Bytes'):
    76                                  'sideboxes', 'hidden'))
    77                 section = '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'
    78             else:
    87             else:
    79                 section = 'attributes'
    88                 if rdef.role_cardinality(role) in '1+':
    80         else:
    89                     section = 'attributes'
    81             if rdef.role_cardinality(role) in '1+':
    90                 elif rdef.composite == neg_role(role):
    82                 section = 'attributes'
    91                     section = 'relations'
    83             elif rdef.composite == neg_role(role):
    92                 else:
    84                 section = 'relations'
    93                     section = 'sideboxes'
    85             else:
    94             self.tag_relation((sschema, rschema, oschema, role), section)
    86                 section = 'sideboxes'
    95 
    87         rtag.tag_relation((sschema, rschema, oschema, role), section)
    96 primaryview_section = PrimaryViewSectionRelationTags()
    88 
       
    89 primaryview_section = RelationTags('primaryview_section',
       
    90                                    init_primaryview_section,
       
    91                                    frozenset(('attributes', 'relations',
       
    92                                               'sideboxes', 'hidden')))
       
    93 
    97 
    94 
    98 
    95 class DisplayCtrlRelationTags(NoTargetRelationTagsDict):
    99 class DisplayCtrlRelationTags(NoTargetRelationTagsDict):
       
   100     """primary view display controller configuration"""
       
   101     __regid__ = 'primaryview_display_ctrl'
       
   102 
    96     def __init__(self, *args, **kwargs):
   103     def __init__(self, *args, **kwargs):
    97         super(DisplayCtrlRelationTags, self).__init__(*args, **kwargs)
   104         super(DisplayCtrlRelationTags, self).__init__(*args, **kwargs)
    98         self.counter = 0
   105         self.counter = 0
    99 
   106 
   100 def init_primaryview_display_ctrl(rtag, sschema, rschema, oschema, role):
   107     def _init(self, sschema, rschema, oschema, role):
   101     if role == 'subject':
   108         if role == 'subject':
   102         oschema = '*'
   109             oschema = '*'
   103     else:
   110         else:
   104         sschema = '*'
   111             sschema = '*'
   105     rtag.counter += 1
   112         self.counter += 1
   106     rtag.setdefault((sschema, rschema, oschema, role), 'order', rtag.counter)
   113         self.setdefault((sschema, rschema, oschema, role),
   107 
   114                         'order',
   108 primaryview_display_ctrl = DisplayCtrlRelationTags('primaryview_display_ctrl',
   115                         self.counter)
   109                                                    init_primaryview_display_ctrl)
   116 
       
   117 primaryview_display_ctrl = DisplayCtrlRelationTags()
   110 
   118 
   111 
   119 
   112 # index view configuration ####################################################
   120 # index view configuration ####################################################
   113 # entity type section in the index/manage page. May be one of
   121 # entity type section in the index/manage page. May be one of
   114 # * 'application'
   122 # * 'application'
   115 # * 'system'
   123 # * 'system'
   116 # * 'schema'
   124 # * 'schema'
   117 # * 'hidden'
   125 # * 'hidden'
   118 # * 'subobject' (not displayed by default)
   126 # * 'subobject' (not displayed by default)
   119 
   127 
   120 class InitializableDict(dict):
   128 class InitializableDict(dict): # XXX not a rtag. Turn into an appobject?
   121     def __init__(self, *args, **kwargs):
   129     def __init__(self, *args, **kwargs):
   122         super(InitializableDict, self).__init__(*args, **kwargs)
   130         super(InitializableDict, self).__init__(*args, **kwargs)
   123         register_rtag(self)
       
   124         self.__defaults = dict(self)
   131         self.__defaults = dict(self)
   125 
   132 
   126     def init(self, schema, check=True):
   133     def init(self, schema, check=True):
   127         self.update(self.__defaults)
   134         self.update(self.__defaults)
   128         for eschema in schema.entities():
   135         for eschema in schema.entities():
   142     Bookmark='system',
   149     Bookmark='system',
   143     # entity types in the 'system' table by default (managers only)
   150     # entity types in the 'system' table by default (managers only)
   144     CWUser='system', CWGroup='system',
   151     CWUser='system', CWGroup='system',
   145     )
   152     )
   146 
   153 
       
   154 
   147 # autoform.AutomaticEntityForm configuration ##################################
   155 # autoform.AutomaticEntityForm configuration ##################################
   148 
   156 
   149 def _formsections_as_dict(formsections):
   157 def _formsections_as_dict(formsections):
   150     result = {}
   158     result = {}
   151     for formsection in formsections:
   159     for formsection in formsections:
   163         composed = not rschema.final and rdef.composite == 'subject'
   171         composed = not rschema.final and rdef.composite == 'subject'
   164     return card, composed
   172     return card, composed
   165 
   173 
   166 class AutoformSectionRelationTags(RelationTagsSet):
   174 class AutoformSectionRelationTags(RelationTagsSet):
   167     """autoform relations'section"""
   175     """autoform relations'section"""
       
   176     __regid__ = 'autoform_section'
   168 
   177 
   169     _allowed_form_types = ('main', 'inlined', 'muledit')
   178     _allowed_form_types = ('main', 'inlined', 'muledit')
   170     _allowed_values = {'main': ('attributes', 'inlined', 'relations',
   179     _allowed_values = {'main': ('attributes', 'inlined', 'relations',
   171                                 'metadata', 'hidden'),
   180                                 'metadata', 'hidden'),
   172                        'inlined': ('attributes', 'inlined', 'hidden'),
   181                        'inlined': ('attributes', 'inlined', 'hidden'),
   175 
   184 
   176     def init(self, schema, check=True):
   185     def init(self, schema, check=True):
   177         super(AutoformSectionRelationTags, self).init(schema, check)
   186         super(AutoformSectionRelationTags, self).init(schema, check)
   178         self.apply(schema, self._initfunc_step2)
   187         self.apply(schema, self._initfunc_step2)
   179 
   188 
   180     @staticmethod
   189     def _init(self, sschema, rschema, oschema, role):
   181     def _initfunc(self, sschema, rschema, oschema, role):
       
   182         formsections = self.init_get(sschema, rschema, oschema, role)
   190         formsections = self.init_get(sschema, rschema, oschema, role)
   183         if formsections is None:
   191         if formsections is None:
   184             formsections = self.tag_container_cls()
   192             formsections = self.tag_container_cls()
   185         if not any(tag.startswith('inlined') for tag in formsections):
   193         if not any(tag.startswith('inlined') for tag in formsections):
   186             if not rschema.final:
   194             if not rschema.final:
   188                 if 'main_inlined' in negsects:
   196                 if 'main_inlined' in negsects:
   189                     formsections.add('inlined_hidden')
   197                     formsections.add('inlined_hidden')
   190         key = _ensure_str_key( (sschema, rschema, oschema, role) )
   198         key = _ensure_str_key( (sschema, rschema, oschema, role) )
   191         self._tagdefs[key] = formsections
   199         self._tagdefs[key] = formsections
   192 
   200 
   193     @staticmethod
       
   194     def _initfunc_step2(self, sschema, rschema, oschema, role):
   201     def _initfunc_step2(self, sschema, rschema, oschema, role):
   195         formsections = self.get(sschema, rschema, oschema, role)
   202         formsections = self.get(sschema, rschema, oschema, role)
   196         sectdict = _formsections_as_dict(formsections)
   203         sectdict = _formsections_as_dict(formsections)
   197         if rschema in META_RTYPES:
   204         if rschema in META_RTYPES:
   198             sectdict.setdefault('main', 'hidden')
   205             sectdict.setdefault('main', 'hidden')
   264             for tag in tags:
   271             for tag in tags:
   265                 assert '_' in tag, (tag, tags)
   272                 assert '_' in tag, (tag, tags)
   266                 section, value = tag.split('_', 1)
   273                 section, value = tag.split('_', 1)
   267                 rtags[section] = value
   274                 rtags[section] = value
   268         cls = self.tag_container_cls
   275         cls = self.tag_container_cls
   269         rtags = cls('_'.join([section,value]) for section,value in rtags.iteritems())
   276         rtags = cls('_'.join([section,value])
       
   277                     for section,value in rtags.iteritems())
   270         return rtags
   278         return rtags
   271 
       
   272 
   279 
   273     def get(self, *key):
   280     def get(self, *key):
   274         # overriden to avoid recomputing done in parent classes
   281         # overriden to avoid recomputing done in parent classes
   275         return self._tagdefs.get(key, ())
   282         return self._tagdefs.get(key, ())
   276 
   283 
   282         `strict`:
   289         `strict`:
   283           bool telling if having local role is enough (strict = False) or not
   290           bool telling if having local role is enough (strict = False) or not
   284         """
   291         """
   285         tag = '%s_%s' % (formtype, section)
   292         tag = '%s_%s' % (formtype, section)
   286         eschema  = entity.e_schema
   293         eschema  = entity.e_schema
   287         permsoverrides = autoform_permissions_overrides
   294         cw = entity._cw
       
   295         permsoverrides = cw.vreg['uicfg'].select('autoform_permissions_overrides', cw, entity=entity)
   288         if entity.has_eid():
   296         if entity.has_eid():
   289             eid = entity.eid
   297             eid = entity.eid
   290         else:
   298         else:
   291             eid = None
   299             eid = None
   292             strict = False
   300             strict = False
   294             assert section in ('attributes', 'metadata', 'hidden')
   302             assert section in ('attributes', 'metadata', 'hidden')
   295             relpermission = 'add'
   303             relpermission = 'add'
   296         else:
   304         else:
   297             assert section not in ('attributes', 'metadata', 'hidden')
   305             assert section not in ('attributes', 'metadata', 'hidden')
   298             relpermission = permission
   306             relpermission = permission
   299         cw = entity._cw
       
   300         for rschema, targetschemas, role in eschema.relation_definitions(True):
   307         for rschema, targetschemas, role in eschema.relation_definitions(True):
   301             _targetschemas = []
   308             _targetschemas = []
   302             for tschema in targetschemas:
   309             for tschema in targetschemas:
   303                 # check section's tag first, potentially lower cost than
   310                 # check section's tag first, potentially lower cost than
   304                 # checking permission which may imply rql queries
   311                 # checking permission which may imply rql queries
   345                     and not rdef.has_perm(cw, 'delete', toeid=eid,
   352                     and not rdef.has_perm(cw, 'delete', toeid=eid,
   346                                           fromeid=entity.related(rschema.type, role)[0][0])):
   353                                           fromeid=entity.related(rschema.type, role)[0][0])):
   347                     continue
   354                     continue
   348             yield (rschema, targetschemas, role)
   355             yield (rschema, targetschemas, role)
   349 
   356 
   350 autoform_section = AutoformSectionRelationTags('autoform_section')
   357 autoform_section = AutoformSectionRelationTags()
   351 
   358 
   352 # relations'field class
   359 # relations'field class
   353 autoform_field = RelationTags('autoform_field')
   360 class AutoformFieldTags(RelationTags):
       
   361     __regid__ = 'autoform_field'
       
   362 
       
   363 autoform_field = AutoformFieldTags()
   354 
   364 
   355 # relations'field explicit kwargs (given to field's __init__)
   365 # relations'field explicit kwargs (given to field's __init__)
   356 autoform_field_kwargs = RelationTagsDict('autoform_field_kwargs')
   366 class AutoformFieldKwargsTags(RelationTagsDict):
       
   367     __regid__ = 'autoform_field_kwargs'
       
   368 
       
   369 autoform_field_kwargs = AutoformFieldKwargsTags()
   357 
   370 
   358 
   371 
   359 # set of tags of the form <action>_on_new on relations. <action> is a
   372 # set of tags of the form <action>_on_new on relations. <action> is a
   360 # schema action (add/update/delete/read), and when such a tag is found
   373 # schema action (add/update/delete/read), and when such a tag is found
   361 # permissions checking is by-passed and supposed to be ok
   374 # permissions checking is by-passed and supposed to be ok
   362 autoform_permissions_overrides = RelationTagsSet('autoform_permissions_overrides')
   375 class AutoFormPermissionsOverrides(RelationTagsSet):
       
   376     __regid__ = 'autoform_permissions_overrides'
       
   377 
       
   378 autoform_permissions_overrides = AutoFormPermissionsOverrides()
       
   379 
   363 
   380 
   364 class ReleditTags(NoTargetRelationTagsDict):
   381 class ReleditTags(NoTargetRelationTagsDict):
   365     """Associate to relation a dictionary to control `reledit` (e.g. edition of
   382     """Associate to relation a dictionary to control `reledit` (e.g. edition of
   366     attributes / relations from within views).
   383     attributes / relations from within views).
   367 
   384 
   386       (to edit the related entity).  This controls whether to edit the relation
   403       (to edit the related entity).  This controls whether to edit the relation
   387       or the target entity of the relation.  Currently only one-to-one relations
   404       or the target entity of the relation.  Currently only one-to-one relations
   388       support target entity edition. By default, the 'related' option is taken
   405       support target entity edition. By default, the 'related' option is taken
   389       whenever the relation is composite.
   406       whenever the relation is composite.
   390     """
   407     """
       
   408     __regid__ = 'reledit'
   391     _keys = frozenset('novalue_label novalue_include_rtype reload rvid edit_target'.split())
   409     _keys = frozenset('novalue_label novalue_include_rtype reload rvid edit_target'.split())
   392 
   410 
   393     def tag_relation(self, key, tag):
   411     def tag_relation(self, key, tag):
   394         for tagkey in tag.iterkeys():
   412         for tagkey in tag.iterkeys():
   395             assert tagkey in self._keys, 'tag %r not in accepted tags: %r' % (tag, self._keys)
   413             assert tagkey in self._keys, 'tag %r not in accepted tags: %r' % (tag, self._keys)
   396         return super(ReleditTags, self).tag_relation(key, tag)
   414         return super(ReleditTags, self).tag_relation(key, tag)
   397 
   415 
   398 def init_reledit_ctrl(rtag, sschema, rschema, oschema, role):
   416     def _init(self, sschema, rschema, oschema, role):
   399     values = rtag.get(sschema, rschema, oschema, role)
   417         values = self.get(sschema, rschema, oschema, role)
   400     if not rschema.final:
   418         if not rschema.final:
   401         composite = rschema.rdef(sschema, oschema).composite == role
   419             composite = rschema.rdef(sschema, oschema).composite == role
   402         if role == 'subject':
   420             if role == 'subject':
   403             oschema = '*'
   421                 oschema = '*'
   404         else:
   422             else:
   405             sschema = '*'
   423                 sschema = '*'
   406         edittarget = values.get('edit_target')
   424             edittarget = values.get('edit_target')
   407         if edittarget not in (None, 'rtype', 'related'):
   425             if edittarget not in (None, 'rtype', 'related'):
   408             rtag.warning('reledit: wrong value for edit_target on relation %s: %s',
   426                 self.warning('reledit: wrong value for edit_target on relation %s: %s',
   409                          rschema, edittarget)
   427                              rschema, edittarget)
   410             edittarget = None
   428                 edittarget = None
   411         if not edittarget:
   429             if not edittarget:
   412             edittarget = 'related' if composite else 'rtype'
   430                 edittarget = 'related' if composite else 'rtype'
   413             rtag.tag_relation((sschema, rschema, oschema, role),
   431                 self.tag_relation((sschema, rschema, oschema, role),
   414                               {'edit_target': edittarget})
   432                                   {'edit_target': edittarget})
   415     if not 'novalue_include_rtype' in values:
   433         if not 'novalue_include_rtype' in values:
   416         showlabel = primaryview_display_ctrl.get(
   434             showlabel = primaryview_display_ctrl.get(
   417             sschema, rschema, oschema, role).get('showlabel', True)
   435                 sschema, rschema, oschema, role).get('showlabel', True)
   418         rtag.tag_relation((sschema, rschema, oschema, role),
   436             self.tag_relation((sschema, rschema, oschema, role),
   419                           {'novalue_include_rtype': not showlabel})
   437                               {'novalue_include_rtype': not showlabel})
   420 
   438 
   421 reledit_ctrl = ReleditTags('reledit', init_reledit_ctrl)
   439 reledit_ctrl = ReleditTags()
       
   440 
   422 
   441 
   423 # boxes.EditBox configuration #################################################
   442 # boxes.EditBox configuration #################################################
   424 
   443 
   425 # 'link' / 'create' relation tags, used to control the "add entity" submenu
   444 # 'link' / 'create' relation tags, used to control the "add entity" submenu
   426 def init_actionbox_appearsin_addmenu(rtag, sschema, rschema, oschema, role):
   445 class ActionBoxUicfg(RelationTagsBool):
   427     if rtag.get(sschema, rschema, oschema, role) is None:
   446     __regid__ = 'actionbox_appearsin_addmenu'
   428         if rschema in META_RTYPES:
   447 
   429             rtag.tag_relation((sschema, rschema, oschema, role), False)
   448     def _init(self, sschema, rschema, oschema, role):
   430             return
   449         if self.get(sschema, rschema, oschema, role) is None:
   431         rdef = rschema.rdef(sschema, oschema)
   450             if rschema in META_RTYPES:
   432         if not rdef.role_cardinality(role) in '?1' and rdef.composite == role:
   451                 self.tag_relation((sschema, rschema, oschema, role), False)
   433             rtag.tag_relation((sschema, rschema, oschema, role), True)
   452                 return
   434 
   453             rdef = rschema.rdef(sschema, oschema)
   435 actionbox_appearsin_addmenu = RelationTagsBool('actionbox_appearsin_addmenu',
   454             if not rdef.role_cardinality(role) in '?1' and rdef.composite == role:
   436                                                init_actionbox_appearsin_addmenu)
   455                 self.tag_relation((sschema, rschema, oschema, role), True)
       
   456 
       
   457 actionbox_appearsin_addmenu = ActionBoxUicfg()
       
   458 
       
   459 
       
   460 
       
   461 def registration_callback(vreg):
       
   462     vreg.register_all(globals().values(), __name__)
       
   463     indexview_etype_section.init(vreg.schema)