web/views/editforms.py
changeset 2381 caad2367d940
parent 2293 7ded2a1416e4
parent 2380 5d980ba57632
child 2389 c399bd6a9c38
equal deleted inserted replaced
2374:ea1a44e4ad62 2381:caad2367d940
    11 
    11 
    12 from copy import copy
    12 from copy import copy
    13 
    13 
    14 from simplejson import dumps
    14 from simplejson import dumps
    15 
    15 
    16 from logilab.mtconverter import html_escape
    16 from logilab.mtconverter import xml_escape
    17 
    17 
    18 from cubicweb.selectors import (match_kwargs, one_line_rset, non_final_entity,
    18 from cubicweb.selectors import (match_kwargs, one_line_rset, non_final_entity,
    19                                 specified_etype_implements, yes)
    19                                 specified_etype_implements, yes)
    20 from cubicweb.utils import make_uid
    20 from cubicweb.utils import make_uid, compute_cardinality, get_schema_property
    21 from cubicweb.view import EntityView
    21 from cubicweb.view import EntityView
    22 from cubicweb.common import tags
    22 from cubicweb.common import tags
    23 from cubicweb.web import INTERNAL_FIELD_VALUE, stdmsgs, eid_param
    23 from cubicweb.web import INTERNAL_FIELD_VALUE, stdmsgs, eid_param, uicfg
    24 from cubicweb.web.form import FormViewMixIn
    24 from cubicweb.web.form import FormViewMixIn
    25 from cubicweb.web.formfields import RelationField
    25 from cubicweb.web.formfields import guess_field
    26 from cubicweb.web.formwidgets import Button, SubmitButton, ResetButton, Select
    26 from cubicweb.web.formwidgets import Button, SubmitButton, ResetButton
    27 from cubicweb.web.views import forms
    27 from cubicweb.web.views import forms
    28 
    28 
    29 
    29 
    30 def relation_id(eid, rtype, role, reid):
    30 def relation_id(eid, rtype, role, reid):
    31     """return an identifier for a relation between two entities"""
    31     """return an identifier for a relation between two entities"""
    36 def toggleable_relation_link(eid, nodeid, label='x'):
    36 def toggleable_relation_link(eid, nodeid, label='x'):
    37     """return javascript snippet to delete/undelete a relation between two
    37     """return javascript snippet to delete/undelete a relation between two
    38     entities
    38     entities
    39     """
    39     """
    40     js = u"javascript: togglePendingDelete('%s', %s);" % (
    40     js = u"javascript: togglePendingDelete('%s', %s);" % (
    41         nodeid, html_escape(dumps(eid)))
    41         nodeid, xml_escape(dumps(eid)))
    42     return u'[<a class="handle" href="%s" id="handle%s">%s</a>]' % (
    42     return u'[<a class="handle" href="%s" id="handle%s">%s</a>]' % (
    43         js, nodeid, label)
    43         js, nodeid, label)
    44 
    44 
    45 
    45 
    46 class DeleteConfForm(FormViewMixIn, EntityView):
    46 class DeleteConfForm(FormViewMixIn, EntityView):
    88     id = 'reledit'
    88     id = 'reledit'
    89     __select__ = non_final_entity() & match_kwargs('rtype')
    89     __select__ = non_final_entity() & match_kwargs('rtype')
    90 
    90 
    91     # FIXME editableField class could be toggleable from userprefs
    91     # FIXME editableField class could be toggleable from userprefs
    92 
    92 
    93     onsubmit = ("return inlineValidateAttributeForm('%(divid)s-form', '%(rtype)s', "
    93     _onclick = u"showInlineEditionForm(%(eid)s, '%(rtype)s', '%(divid)s')"
    94                 "'%(eid)s', '%(divid)s', %(reload)s, '%(default)s');")
    94     _defaultlandingzone = u'<img title="%(msg)s" src="data/file.gif" alt="%(msg)s"/>'
    95     ondblclick = "showInlineEditionForm(%(eid)s, '%(rtype)s', '%(divid)s')"
    95     _landingzonemsg = _('click to edit this field')
    96 
    96     # default relation vids according to cardinality
    97     def cell_call(self, row, col, rtype=None, role='subject', reload=False,
    97     _one_rvid = 'incontext'
    98                   vid='textoutofcontext', default=None):
    98     _many_rvid = 'csv'
    99         """display field to edit entity's `rtype` relation on double-click"""
    99 
   100         rschema = self.schema.rschema(rtype)
   100     def _compute_best_vid(self, entity, rtype, role):
       
   101         if compute_cardinality(entity.e_schema,
       
   102                                entity.schema.rschema(rtype),
       
   103                                role) in '+*':
       
   104             return self._many_rvid
       
   105         return self._one_rvid
       
   106 
       
   107     def _build_landing_zone(self, lzone):
       
   108         return lzone or self._defaultlandingzone % {'msg' : self.req._(self._landingzonemsg)}
       
   109 
       
   110     def _build_renderer(self, entity, rtype, role):
       
   111         return self.vreg.select_object('formrenderers', 'base', self.req,
       
   112                                        entity=entity,
       
   113                                        display_label=False, display_help=False,
       
   114                                        display_fields=[(rtype, role)],
       
   115                                        table_class='', button_bar_class='buttonbar',
       
   116                                        display_progress_div=False)
       
   117 
       
   118     def cell_call(self, row, col, rtype=None, role='subject',
       
   119                   reload=False,      # controls reloading the whole page after change
       
   120                   rvid=None,         # vid to be applied to other side of rtype
       
   121                   default=None,      # default value
       
   122                   landing_zone=None  # prepend value with a separate html element to click onto
       
   123                                      # (esp. needed when values are links)
       
   124                   ):
       
   125         """display field to edit entity's `rtype` relation on click"""
       
   126         assert rtype
       
   127         assert role in ('subject', 'object')
       
   128         if default is None:
       
   129             default = xml_escape(self.req._('<no value>'))
   101         entity = self.entity(row, col)
   130         entity = self.entity(row, col)
   102         if not default:
   131         rschema = entity.schema.rschema(rtype)
   103             default = self.req._('not specified')
   132         # compute value, checking perms, build form
   104         if rschema.is_final():
   133         if rschema.is_final():
   105             value = entity.printable_value(rtype)
   134             value = entity.printable_value(rtype) or default
   106             if not entity.has_perm('update'):
   135             if not entity.has_perm('update'):
   107                 self.w(value)
   136                 self.w(value)
   108                 return
   137                 return
       
   138             self._attribute_form(entity, value, rtype, role, reload,
       
   139                                  row, col, default, landing_zone)
   109         else:
   140         else:
       
   141             dispctrl = uicfg.primaryview_display_ctrl.etype_get(entity.e_schema,
       
   142                                                                 rtype, role)
       
   143             vid = dispctrl.get('vid', 'reledit')
       
   144             if vid != 'reledit': # reledit explicitly disabled
       
   145                 self.wview(vid, entity.related(rtype, role))
       
   146                 return
       
   147             if rvid is None:
       
   148                 rvid = self._compute_best_vid(entity, rtype, role)
   110             rset = entity.related(rtype, role)
   149             rset = entity.related(rtype, role)
   111             # XXX html_escape but that depends of the actual vid
   150             candidate = self.view(rvid, rset, 'null')
   112             value = html_escape(self.view(vid, rset, 'null') or default)
   151             value = candidate or default
   113         # XXX consider local roles ?
   152             if role == 'subject' and not rschema.has_perm(self.req, 'add',
   114         if role == 'subject'and not rschema.has_perm(self.req, 'add',
   153                                                           fromeid=entity.eid):
   115                                                     fromeid=entity.eid):
   154                 return self.w(value)
   116             self.w(value)
   155             elif role == 'object' and not rschema.has_perm(self.req, 'add',
   117             return
   156                                                            toeid=entity.eid):
   118         elif role == 'object'and not rschema.has_perm(self.req, 'add',
   157                 return self.w(value)
   119                                                       toeid=entity.eid):
   158             elif get_schema_property(entity.e_schema, rschema,
   120             self.w(value)
   159                                      role, 'composite') == role:
   121             return
   160                 self.warning('reledit cannot be applied : (... %s %s [composite])'
   122         if not value.strip():
   161                              % (rtype, entity.e_schema))
   123             value = default
   162                 return self.w(value)
   124         if rschema.is_final():
   163             self._relation_form(entity, value, rtype, role, reload, row, col,
   125             form = self._build_attribute_form(entity, value, rtype, role,
   164                                 rvid, default, landing_zone)
   126                                               reload, row, col, default)
   165 
   127         else:
   166 
   128             form = self._build_relation_form(entity, value, rtype, role,
   167     def _relation_form(self, entity, value, rtype, role, row, col, reload, rvid, default, lzone):
   129                                              row, col, vid, default)
   168         lzone = self._build_landing_zone(lzone)
   130         renderer = self.vreg.select('formrenderers', 'base', self.req,
   169         value = lzone + value
   131                                     entity=entity,
   170         divid = 'd%s' % make_uid('%s-%s' % (rtype, entity.eid))
   132                                     display_label=False, display_help=False,
   171         event_data = {'divid' : divid, 'eid' : entity.eid, 'rtype' : rtype, 'vid' : rvid,
   133                                     display_fields=[(rtype, role)],
   172                       'reload' : reload, 'default' : default, 'role' : role,
   134                                     table_class='', button_bar_class='buttonbar',
   173                       'lzone' : lzone}
   135                                     display_progress_div=False)
   174         onsubmit = ("return inlineValidateRelationForm('%(rtype)s', '%(role)s', '%(eid)s', "
       
   175                     "'%(divid)s', %(reload)s, '%(vid)s', '%(default)s', '%(lzone)s');"
       
   176                     % event_data)
       
   177         cancelclick = "hideInlineEdit(%s,\'%s\',\'%s\')" % (
       
   178             entity.eid, rtype, divid)
       
   179         form = self.vreg.select_object('forms', 'base', self.req, entity=entity,
       
   180                                        domid='%s-form' % divid, cssstyle='display: none',
       
   181                                        onsubmit=onsubmit, action='#',
       
   182                                        form_buttons=[SubmitButton(),
       
   183                                                      Button(stdmsgs.BUTTON_CANCEL,
       
   184                                                             onclick=cancelclick)])
       
   185         field = guess_field(entity.e_schema, entity.schema.rschema(rtype), role)
       
   186         form.append_field(field)
       
   187         self.w(u'<div id="%s-reledit" class="field">' % divid)
       
   188         self.w(tags.div(value, klass='editableField', id=divid,
       
   189                         onclick=self._onclick % event_data))
       
   190         renderer = self._build_renderer(entity, rtype, role)
   136         self.w(form.form_render(renderer=renderer))
   191         self.w(form.form_render(renderer=renderer))
   137 
   192         self.w(u'</div>')
   138     def _build_relation_form(self, entity, value, rtype, role, row, col, vid, default):
   193 
   139         entity = self.entity(row, col)
   194     def _attribute_form(self, entity, value, rtype, role, reload, row, col, default, lzone):
   140         divid = 'd%s' % make_uid('%s-%s' % (rtype, entity.eid))
       
   141         event_data = {'divid' : divid, 'eid' : entity.eid, 'rtype' : rtype, 'vid' : vid,
       
   142                       'default' : default, 'role' : role}
       
   143         onsubmit = ("return inlineValidateRelationForm('%(divid)s-form', '%(rtype)s', "
       
   144                     "'%(role)s', '%(eid)s', '%(divid)s', '%(vid)s', '%(default)s');"
       
   145                     % event_data)
       
   146         cancelclick = "cancelInlineEdit(%s,\'%s\',\'%s\')" % (
       
   147             entity.eid, rtype, divid)
       
   148         form = self.vreg.select('forms', 'base', self.req, entity=entity,
       
   149                                 domid='%s-form' % divid, cssstyle='display: none',
       
   150                                 onsubmit=onsubmit, action='#',
       
   151                                 form_buttons=[SubmitButton(),
       
   152                                               Button(stdmsgs.BUTTON_CANCEL,
       
   153                                                      onclick=cancelclick)])
       
   154         form.append_field(RelationField(name=rtype, role=role, sort=True,
       
   155                                         widget=Select(),
       
   156                                         label=u' '))
       
   157         self.w(tags.div(value, klass='editableField', id=divid,
       
   158                         ondblclick=self.ondblclick % event_data))
       
   159         return form
       
   160 
       
   161     def _build_attribute_form(self, entity, value, rtype, role, reload, row, col, default):
       
   162         eid = entity.eid
   195         eid = entity.eid
   163         divid = 'd%s' % make_uid('%s-%s' % (rtype, eid))
   196         divid = 'd%s' % make_uid('%s-%s' % (rtype, eid))
   164         event_data = {'divid' : divid, 'eid' : eid, 'rtype' : rtype,
   197         event_data = {'divid' : divid, 'eid' : eid, 'rtype' : rtype,
   165                       'reload' : dumps(reload), 'default' : default}
   198                       'reload' : dumps(reload), 'default' : default, 'lzone' : lzone}
       
   199         onsubmit = ("return inlineValidateAttributeForm('%(rtype)s', '%(eid)s', '%(divid)s', "
       
   200                     "%(reload)s, '%(default)s', '%(lzone)s');")
   166         buttons = [SubmitButton(stdmsgs.BUTTON_OK),
   201         buttons = [SubmitButton(stdmsgs.BUTTON_OK),
   167                    Button(stdmsgs.BUTTON_CANCEL,
   202                    Button(stdmsgs.BUTTON_CANCEL,
   168                           onclick="cancelInlineEdit(%s,\'%s\',\'%s\')" % (
   203                           onclick="hideInlineEdit(%s,\'%s\',\'%s\')" % (
   169                               eid, rtype, divid))]
   204                               eid, rtype, divid))]
   170         form = self.vreg.select('forms', 'edition', self.req, rset=self.rset,
   205         form = self.vreg.select_object('forms', 'edition', self.req, self.rset,
   171                                 row=row, col=col, form_buttons=buttons,
   206                                        row=row, col=col, form_buttons=buttons,
   172                                 domid='%s-form' % divid, action='#',
   207                                        domid='%s-form' % divid, action='#',
   173                                 cssstyle='display: none',
   208                                        cssstyle='display: none',
   174                                 onsubmit=self.onsubmit % event_data)
   209                                        onsubmit=onsubmit % event_data)
   175         self.w(tags.div(value, klass='editableField', id=divid,
   210         self.w(tags.div(value, klass='editableField', id=divid,
   176                         ondblclick=self.ondblclick % event_data))
   211                         onclick=self._onclick % event_data))
   177         return form
   212         renderer = self._build_renderer(entity, rtype, role)
       
   213         self.w(form.form_render(renderer=renderer))
   178 
   214 
   179 
   215 
   180 class EditionFormView(FormViewMixIn, EntityView):
   216 class EditionFormView(FormViewMixIn, EntityView):
   181     """display primary entity edition form"""
   217     """display primary entity edition form"""
   182     id = 'edition'
   218     id = 'edition'