web/uihelper.py
changeset 8030 552d85fcb587
child 8033 2a9764674a74
equal deleted inserted replaced
8029:805d4e121b65 8030:552d85fcb587
       
     1 # copyright 2011 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 provide highlevel helpers to avoid uicfg boilerplate
       
    19 for most common tasks such as fields ordering, widget customization, etc.
       
    20 
       
    21 
       
    22 Here are a few helpers to customize *action box* rendering:
       
    23 
       
    24 .. autofunction:: cubicweb.web.uihelper.append_to_addmenu
       
    25 .. autofunction:: cubicweb.web.uihelper.remove_from_addmenu
       
    26 
       
    27 
       
    28 and a few other ones for *form configuration*:
       
    29 
       
    30 .. autofunction:: cubicweb.web.uihelper.set_fields_order
       
    31 .. autofunction:: cubicweb.web.uihelper.hide_field
       
    32 .. autofunction:: cubicweb.web.uihelper.hide_fields
       
    33 .. autofunction:: cubicweb.web.uihelper.set_field_kwargs
       
    34 .. autofunction:: cubicweb.web.uihelper.set_field
       
    35 .. autofunction:: cubicweb.web.uihelper.edit_inline
       
    36 .. autofunction:: cubicweb.web.uihelper.edit_as_attr
       
    37 .. autofunction:: cubicweb.web.uihelper.set_muledit_editable
       
    38 
       
    39 The module also provides a :class:`FormConfig` base class that lets you gather
       
    40 uicfg declaration in the scope of a single class, which can sometimes
       
    41 be clearer to read than a bunch of sequential function calls.
       
    42 
       
    43 .. autoclass:: cubicweb.web.uihelper.FormConfig
       
    44 
       
    45 """
       
    46 __docformat__ = "restructuredtext en"
       
    47 
       
    48 from cubicweb.web import uicfg
       
    49 from functools import partial
       
    50 
       
    51 def _tag_rel(rtag, etype, attr, desttype='*', *args, **kwargs):
       
    52     if isinstance(attr, basestring):
       
    53         attr, role = attr, 'subject'
       
    54     else:
       
    55         attr, role = attr
       
    56     if role == 'subject':
       
    57         rtag.tag_subject_of((etype, attr, desttype), *args, **kwargs)
       
    58     else:
       
    59         rtag.tag_object_of((desttype, attr, etype), *args, **kwargs)
       
    60 
       
    61 
       
    62 ## generic uicfg helpers ######################################################
       
    63 def append_to_addmenu(etype, attr, createdtype='*'):
       
    64     """adds `attr` in the actions box *addrelated* submenu of `etype`.
       
    65 
       
    66     :param etype: the entity type as a string
       
    67     :param attr: the name of the attribute or relation to hide
       
    68 
       
    69     `attr` can be a string or 2-tuple (relname, role_of_etype_in_the_relation)
       
    70 
       
    71     """
       
    72     _tag_rel(uicfg.actionbox_appearsin_addmenu, etype, attr, createdtype, True)
       
    73 
       
    74 def remove_from_addmenu(etype, attr, createdtype='*'):
       
    75     """removes `attr` from the actions box *addrelated* submenu of `etype`.
       
    76 
       
    77     :param etype: the entity type as a string
       
    78     :param attr: the name of the attribute or relation to hide
       
    79 
       
    80     `attr` can be a string or 2-tuple (relname, role_of_etype_in_the_relation)
       
    81     """
       
    82     _tag_rel(uicfg.actionbox_appearsin_addmenu, etype, attr, createdtype, False)
       
    83 
       
    84 
       
    85 ## form uicfg helpers ##########################################################
       
    86 def set_fields_order(etype, attrs):
       
    87     """specify the field order in `etype` main edition form.
       
    88 
       
    89     :param etype: the entity type as a string
       
    90     :param attrs: the ordered list of attribute names (or relations)
       
    91 
       
    92     `attrs` can be strings or 2-tuples (relname, role_of_etype_in_the_relation)
       
    93 
       
    94     Unspecified fields will be displayed after specified ones, their
       
    95     order being consistent with the schema definition.
       
    96 
       
    97     Examples:
       
    98 
       
    99 .. sourcecode:: python
       
   100 
       
   101   from cubicweb.web import uihelper
       
   102   uihelper.set_fields_order('CWUser', ('firstname', 'surname', 'login'))
       
   103   uihelper.set_fields_order('CWUser', ('firstname', ('in_group', 'subject'), 'surname', 'login'))
       
   104 
       
   105     """
       
   106     afk = uicfg.autoform_field_kwargs
       
   107     for index, attr in enumerate(attrs):
       
   108         _tag_rel(afk, etype, attr, '*', {'order': index})
       
   109 
       
   110 
       
   111 def hide_field(etype, attr, desttype='*', formtype='main'):
       
   112     """hide `attr` in `etype` forms.
       
   113 
       
   114     :param etype: the entity type as a string
       
   115     :param attr: the name of the attribute or relation to hide
       
   116     :param formtype: which form will be affected ('main', 'inlined', etc.), *main* by default.
       
   117 
       
   118     `attr` can be a string or 2-tuple (relname, role_of_etype_in_the_relation)
       
   119 
       
   120     Examples:
       
   121 
       
   122 .. sourcecode:: python
       
   123 
       
   124   from cubicweb.web import uihelper
       
   125   uihelper.hide_field('CWUser', 'login')
       
   126   uihelper.hide_field('*', 'name')
       
   127   uihelper.hide_field('CWUser', 'use_email', formtype='inlined')
       
   128 
       
   129     """
       
   130     _tag_rel(uicfg.autoform_section, etype, attr, desttype,
       
   131              formtype=formtype, section='hidden')
       
   132 
       
   133 
       
   134 def hide_fields(etype, attrs, formtype='main'):
       
   135     """simple for-loop wrapper around :func:`hide_field`.
       
   136 
       
   137     :param etype: the entity type as a string
       
   138     :param attrs: the ordered list of attribute names (or relations)
       
   139     :param formtype: which form will be affected ('main', 'inlined', etc.), *main* by default.
       
   140 
       
   141     `attrs` can be strings or 2-tuples (relname, role_of_etype_in_the_relation)
       
   142 
       
   143     Examples:
       
   144 
       
   145 .. sourcecode:: python
       
   146 
       
   147   from cubicweb.web import uihelper
       
   148   uihelper.hide_fields('CWUser', ('login', ('use_email', 'subject')), formtype='inlined')
       
   149     """
       
   150     for field in fields:
       
   151         hide_field(etype, field, formtype=formtype)
       
   152 
       
   153 
       
   154 def set_field_kwargs(etype, attr, **kwargs):
       
   155     """tag `attr` field of `etype` with additional named paremeters.
       
   156 
       
   157     :param etype: the entity type as a string
       
   158     :param attr: the name of the attribute or relation
       
   159 
       
   160     `attr` can be a string or 2-tuple (relname, role_of_etype_in_the_relation)
       
   161 
       
   162     Examples:
       
   163 
       
   164 .. sourcecode:: python
       
   165 
       
   166   from cubicweb.web import uihelper, formwidgets as fwdgs
       
   167 
       
   168   uihelper.set_field_kwargs('Person', 'works_for', widget=fwdgs.AutoCompletionWidget())
       
   169   uihelper.set_field_kwargs('CWUser', 'login', label=_('login or email address'),
       
   170                             widget=fwdgs.TextInput(attrs={'size': 30}))
       
   171     """
       
   172     _tag_rel(uicfg.autoform_field_kwargs, etype, attr, '*', kwargs)
       
   173 
       
   174 
       
   175 def set_field(etype, attr, field):
       
   176     """sets the `attr` field of `etype`.
       
   177 
       
   178     :param etype: the entity type as a string
       
   179     :param attr: the name of the attribute or relation
       
   180 
       
   181     `attr` can be a string or 2-tuple (relname, role_of_etype_in_the_relation)
       
   182 
       
   183     """
       
   184     _tag_rel(uicfg.autoform_field, etype, attr, '*', field)
       
   185 
       
   186 
       
   187 def edit_inline(etype, attr, desttype='*', formtype=('main', 'inlined')):
       
   188     """edit `attr` with and inlined form.
       
   189 
       
   190     :param etype: the entity type as a string
       
   191     :param attr: the name of the attribute or relation
       
   192     :param desttype: the destination type(s) concerned, default is everything
       
   193     :param formtype: which form will be affected ('main', 'inlined', etc.), *main* and *inlined* by default.
       
   194 
       
   195     `attr` can be a string or 2-tuple (relname, role_of_etype_in_the_relation)
       
   196 
       
   197     Examples:
       
   198 
       
   199 .. sourcecode:: python
       
   200 
       
   201   from cubicweb.web import uihelper
       
   202 
       
   203   uihelper.edit_inline('*', 'use_email')
       
   204   """
       
   205     _tag_rel(uicfg.autoform_section, etype, attr, desttype,
       
   206              formtype=formtype, section='inlined')
       
   207 
       
   208 
       
   209 def edit_as_attr(etype, attr, desttype='*', formtype=('main', 'muledit')):
       
   210     """make `attr` appear in the *attributes* section of `etype` form.
       
   211 
       
   212     :param etype: the entity type as a string
       
   213     :param attr: the name of the attribute or relation
       
   214     :param desttype: the destination type(s) concerned, default is everything
       
   215     :param formtype: which form will be affected ('main', 'inlined', etc.), *main* and *muledit* by default.
       
   216 
       
   217     `attr` can be a string or 2-tuple (relname, role_of_etype_in_the_relation)
       
   218 
       
   219     Examples:
       
   220 
       
   221 .. sourcecode:: python
       
   222 
       
   223   from cubicweb.web import uihelper
       
   224 
       
   225   uihelper.edit_as_attr('CWUser', 'in_group')
       
   226     """
       
   227     _tag_rel(uicfg.autoform_section, etype, attr, desttype,
       
   228              formtype=formtype, section='attributes')
       
   229 
       
   230 
       
   231 def set_muledit_editable(etype, attrs):
       
   232     """make `attrs` appear in muledit form of `etype`.
       
   233 
       
   234     :param etype: the entity type as a string
       
   235     :param attrs: the ordered list of attribute names (or relations)
       
   236 
       
   237     `attrs` can be strings or 2-tuples (relname, role_of_etype_in_the_relation)
       
   238 
       
   239     Examples:
       
   240 
       
   241 .. sourcecode:: python
       
   242 
       
   243   from cubicweb.web import uihelper
       
   244 
       
   245   uihelper.set_muledit_editable('CWUser', ('firstname', 'surname', 'in_group'))
       
   246     """
       
   247     for attr in attrs:
       
   248         edit_as_attr(etype, attr, formtype='muledit')
       
   249 
       
   250 
       
   251 class meta_formconfig(type):
       
   252     """metaclass of FormConfig classes, only for easier declaration purpose"""
       
   253     def __init__(cls, name, bases, classdict):
       
   254         if cls.etype is None:
       
   255             return
       
   256         for attr_role in cls.hidden:
       
   257             hide_field(cls.etype, attr_role, formtype=cls.formtype)
       
   258         for attr_role in cls.rels_as_attrs:
       
   259             edit_as_attr(cls.etype, attr_role, formtype=cls.formtype)
       
   260         for attr_role in cls.inlined:
       
   261             edit_inline(cls.etype, attr_role, formtype=cls.formtype)
       
   262         for rtype, widget in cls.widgets.items():
       
   263             set_field_kwargs(cls.etype, rtype, widget=widget)
       
   264         for rtype, field in cls.fields.items():
       
   265             set_field(cls.etype, rtype, field)
       
   266         set_fields_order(cls.etype, cls.fields_order)
       
   267         super(meta_formconfig, cls).__init__(name, bases, classdict)
       
   268 
       
   269 
       
   270 class FormConfig:
       
   271     """helper base class to define uicfg rules on a given entity type.
       
   272 
       
   273     In all descriptions below, attributes list can either be a list of
       
   274     attribute names of a list of 2-tuples (relation name, role of
       
   275     the edited entity in the relation).
       
   276 
       
   277     **Attributes**
       
   278 
       
   279     :attr:`etype`
       
   280       which entity type the form config is for. This attribute is **mandatory**
       
   281 
       
   282     :attr:`formtype`
       
   283       the formtype the class tries toc customize (i.e. *main*, *inlined*, or *muledit*),
       
   284       default is *main*.
       
   285 
       
   286     :attr:`hidden`
       
   287       the list of attributes or relations to hide.
       
   288 
       
   289     :attr:`rels_as_attrs`
       
   290       the list of attributes to edit in the *attributes* section.
       
   291 
       
   292     :attr:`inlined`
       
   293       the list of attributes to edit in the *inlined* section.
       
   294 
       
   295     :attr:`fields_order`
       
   296       the list of attributes to edit, in the desired order. Unspecified
       
   297       fields will be displayed after specified ones, their order
       
   298       being consistent with the schema definition.
       
   299 
       
   300     :attr:`widgets`
       
   301       a dictionnary mapping attribute names to widget instances.
       
   302 
       
   303     :attr:`fields`
       
   304       a dictionnary mapping attribute names to field instances.
       
   305 
       
   306     Examples:
       
   307 
       
   308 .. sourcecode:: python
       
   309 
       
   310   from cubicweb.web import uihelper, formwidgets as fwdgs
       
   311 
       
   312   class LinkFormConfig(uihelper.FormConfig):
       
   313       etype = 'Link'
       
   314       hidden = ('title', 'description', 'embed')
       
   315       widgets = dict(
       
   316           url=fwdgs.TextInput(attrs={'size':40}),
       
   317           )
       
   318 
       
   319   class UserFormConfig(uihelper.FormConfig):
       
   320       etype = 'CWUser'
       
   321       hidden = ('login',)
       
   322       rels_as_attrs = ('in_group',)
       
   323       fields_order = ('firstname', 'surname', 'in_group', 'use_email')
       
   324       inlined = ('use_email',)
       
   325 
       
   326     """
       
   327     __metaclass__ = meta_formconfig
       
   328     formtype = 'main'
       
   329     etype = None # must be defined in concrete subclasses
       
   330     hidden = ()
       
   331     rels_as_attrs = ()
       
   332     inlined = ()
       
   333     fields_order = ()
       
   334     widgets = {}
       
   335     fields = {}