web/views/editforms.py
changeset 2381 caad2367d940
parent 2293 7ded2a1416e4
parent 2380 5d980ba57632
child 2389 c399bd6a9c38
--- a/web/views/editforms.py	Wed Jul 15 09:45:13 2009 +0200
+++ b/web/views/editforms.py	Thu Jul 16 13:30:13 2009 +0200
@@ -13,17 +13,17 @@
 
 from simplejson import dumps
 
-from logilab.mtconverter import html_escape
+from logilab.mtconverter import xml_escape
 
 from cubicweb.selectors import (match_kwargs, one_line_rset, non_final_entity,
                                 specified_etype_implements, yes)
-from cubicweb.utils import make_uid
+from cubicweb.utils import make_uid, compute_cardinality, get_schema_property
 from cubicweb.view import EntityView
 from cubicweb.common import tags
-from cubicweb.web import INTERNAL_FIELD_VALUE, stdmsgs, eid_param
+from cubicweb.web import INTERNAL_FIELD_VALUE, stdmsgs, eid_param, uicfg
 from cubicweb.web.form import FormViewMixIn
-from cubicweb.web.formfields import RelationField
-from cubicweb.web.formwidgets import Button, SubmitButton, ResetButton, Select
+from cubicweb.web.formfields import guess_field
+from cubicweb.web.formwidgets import Button, SubmitButton, ResetButton
 from cubicweb.web.views import forms
 
 
@@ -38,7 +38,7 @@
     entities
     """
     js = u"javascript: togglePendingDelete('%s', %s);" % (
-        nodeid, html_escape(dumps(eid)))
+        nodeid, xml_escape(dumps(eid)))
     return u'[<a class="handle" href="%s" id="handle%s">%s</a>]' % (
         js, nodeid, label)
 
@@ -90,91 +90,127 @@
 
     # FIXME editableField class could be toggleable from userprefs
 
-    onsubmit = ("return inlineValidateAttributeForm('%(divid)s-form', '%(rtype)s', "
-                "'%(eid)s', '%(divid)s', %(reload)s, '%(default)s');")
-    ondblclick = "showInlineEditionForm(%(eid)s, '%(rtype)s', '%(divid)s')"
+    _onclick = u"showInlineEditionForm(%(eid)s, '%(rtype)s', '%(divid)s')"
+    _defaultlandingzone = u'<img title="%(msg)s" src="data/file.gif" alt="%(msg)s"/>'
+    _landingzonemsg = _('click to edit this field')
+    # default relation vids according to cardinality
+    _one_rvid = 'incontext'
+    _many_rvid = 'csv'
+
+    def _compute_best_vid(self, entity, rtype, role):
+        if compute_cardinality(entity.e_schema,
+                               entity.schema.rschema(rtype),
+                               role) in '+*':
+            return self._many_rvid
+        return self._one_rvid
+
+    def _build_landing_zone(self, lzone):
+        return lzone or self._defaultlandingzone % {'msg' : self.req._(self._landingzonemsg)}
 
-    def cell_call(self, row, col, rtype=None, role='subject', reload=False,
-                  vid='textoutofcontext', default=None):
-        """display field to edit entity's `rtype` relation on double-click"""
-        rschema = self.schema.rschema(rtype)
+    def _build_renderer(self, entity, rtype, role):
+        return self.vreg.select_object('formrenderers', 'base', self.req,
+                                       entity=entity,
+                                       display_label=False, display_help=False,
+                                       display_fields=[(rtype, role)],
+                                       table_class='', button_bar_class='buttonbar',
+                                       display_progress_div=False)
+
+    def cell_call(self, row, col, rtype=None, role='subject',
+                  reload=False,      # controls reloading the whole page after change
+                  rvid=None,         # vid to be applied to other side of rtype
+                  default=None,      # default value
+                  landing_zone=None  # prepend value with a separate html element to click onto
+                                     # (esp. needed when values are links)
+                  ):
+        """display field to edit entity's `rtype` relation on click"""
+        assert rtype
+        assert role in ('subject', 'object')
+        if default is None:
+            default = xml_escape(self.req._('<no value>'))
         entity = self.entity(row, col)
-        if not default:
-            default = self.req._('not specified')
+        rschema = entity.schema.rschema(rtype)
+        # compute value, checking perms, build form
         if rschema.is_final():
-            value = entity.printable_value(rtype)
+            value = entity.printable_value(rtype) or default
             if not entity.has_perm('update'):
                 self.w(value)
                 return
-        else:
-            rset = entity.related(rtype, role)
-            # XXX html_escape but that depends of the actual vid
-            value = html_escape(self.view(vid, rset, 'null') or default)
-        # XXX consider local roles ?
-        if role == 'subject'and not rschema.has_perm(self.req, 'add',
-                                                    fromeid=entity.eid):
-            self.w(value)
-            return
-        elif role == 'object'and not rschema.has_perm(self.req, 'add',
-                                                      toeid=entity.eid):
-            self.w(value)
-            return
-        if not value.strip():
-            value = default
-        if rschema.is_final():
-            form = self._build_attribute_form(entity, value, rtype, role,
-                                              reload, row, col, default)
+            self._attribute_form(entity, value, rtype, role, reload,
+                                 row, col, default, landing_zone)
         else:
-            form = self._build_relation_form(entity, value, rtype, role,
-                                             row, col, vid, default)
-        renderer = self.vreg.select('formrenderers', 'base', self.req,
-                                    entity=entity,
-                                    display_label=False, display_help=False,
-                                    display_fields=[(rtype, role)],
-                                    table_class='', button_bar_class='buttonbar',
-                                    display_progress_div=False)
-        self.w(form.form_render(renderer=renderer))
+            dispctrl = uicfg.primaryview_display_ctrl.etype_get(entity.e_schema,
+                                                                rtype, role)
+            vid = dispctrl.get('vid', 'reledit')
+            if vid != 'reledit': # reledit explicitly disabled
+                self.wview(vid, entity.related(rtype, role))
+                return
+            if rvid is None:
+                rvid = self._compute_best_vid(entity, rtype, role)
+            rset = entity.related(rtype, role)
+            candidate = self.view(rvid, rset, 'null')
+            value = candidate or default
+            if role == 'subject' and not rschema.has_perm(self.req, 'add',
+                                                          fromeid=entity.eid):
+                return self.w(value)
+            elif role == 'object' and not rschema.has_perm(self.req, 'add',
+                                                           toeid=entity.eid):
+                return self.w(value)
+            elif get_schema_property(entity.e_schema, rschema,
+                                     role, 'composite') == role:
+                self.warning('reledit cannot be applied : (... %s %s [composite])'
+                             % (rtype, entity.e_schema))
+                return self.w(value)
+            self._relation_form(entity, value, rtype, role, reload, row, col,
+                                rvid, default, landing_zone)
 
-    def _build_relation_form(self, entity, value, rtype, role, row, col, vid, default):
-        entity = self.entity(row, col)
+
+    def _relation_form(self, entity, value, rtype, role, row, col, reload, rvid, default, lzone):
+        lzone = self._build_landing_zone(lzone)
+        value = lzone + value
         divid = 'd%s' % make_uid('%s-%s' % (rtype, entity.eid))
-        event_data = {'divid' : divid, 'eid' : entity.eid, 'rtype' : rtype, 'vid' : vid,
-                      'default' : default, 'role' : role}
-        onsubmit = ("return inlineValidateRelationForm('%(divid)s-form', '%(rtype)s', "
-                    "'%(role)s', '%(eid)s', '%(divid)s', '%(vid)s', '%(default)s');"
+        event_data = {'divid' : divid, 'eid' : entity.eid, 'rtype' : rtype, 'vid' : rvid,
+                      'reload' : reload, 'default' : default, 'role' : role,
+                      'lzone' : lzone}
+        onsubmit = ("return inlineValidateRelationForm('%(rtype)s', '%(role)s', '%(eid)s', "
+                    "'%(divid)s', %(reload)s, '%(vid)s', '%(default)s', '%(lzone)s');"
                     % event_data)
-        cancelclick = "cancelInlineEdit(%s,\'%s\',\'%s\')" % (
+        cancelclick = "hideInlineEdit(%s,\'%s\',\'%s\')" % (
             entity.eid, rtype, divid)
-        form = self.vreg.select('forms', 'base', self.req, entity=entity,
-                                domid='%s-form' % divid, cssstyle='display: none',
-                                onsubmit=onsubmit, action='#',
-                                form_buttons=[SubmitButton(),
-                                              Button(stdmsgs.BUTTON_CANCEL,
-                                                     onclick=cancelclick)])
-        form.append_field(RelationField(name=rtype, role=role, sort=True,
-                                        widget=Select(),
-                                        label=u' '))
+        form = self.vreg.select_object('forms', 'base', self.req, entity=entity,
+                                       domid='%s-form' % divid, cssstyle='display: none',
+                                       onsubmit=onsubmit, action='#',
+                                       form_buttons=[SubmitButton(),
+                                                     Button(stdmsgs.BUTTON_CANCEL,
+                                                            onclick=cancelclick)])
+        field = guess_field(entity.e_schema, entity.schema.rschema(rtype), role)
+        form.append_field(field)
+        self.w(u'<div id="%s-reledit" class="field">' % divid)
         self.w(tags.div(value, klass='editableField', id=divid,
-                        ondblclick=self.ondblclick % event_data))
-        return form
+                        onclick=self._onclick % event_data))
+        renderer = self._build_renderer(entity, rtype, role)
+        self.w(form.form_render(renderer=renderer))
+        self.w(u'</div>')
 
-    def _build_attribute_form(self, entity, value, rtype, role, reload, row, col, default):
+    def _attribute_form(self, entity, value, rtype, role, reload, row, col, default, lzone):
         eid = entity.eid
         divid = 'd%s' % make_uid('%s-%s' % (rtype, eid))
         event_data = {'divid' : divid, 'eid' : eid, 'rtype' : rtype,
-                      'reload' : dumps(reload), 'default' : default}
+                      'reload' : dumps(reload), 'default' : default, 'lzone' : lzone}
+        onsubmit = ("return inlineValidateAttributeForm('%(rtype)s', '%(eid)s', '%(divid)s', "
+                    "%(reload)s, '%(default)s', '%(lzone)s');")
         buttons = [SubmitButton(stdmsgs.BUTTON_OK),
                    Button(stdmsgs.BUTTON_CANCEL,
-                          onclick="cancelInlineEdit(%s,\'%s\',\'%s\')" % (
+                          onclick="hideInlineEdit(%s,\'%s\',\'%s\')" % (
                               eid, rtype, divid))]
-        form = self.vreg.select('forms', 'edition', self.req, rset=self.rset,
-                                row=row, col=col, form_buttons=buttons,
-                                domid='%s-form' % divid, action='#',
-                                cssstyle='display: none',
-                                onsubmit=self.onsubmit % event_data)
+        form = self.vreg.select_object('forms', 'edition', self.req, self.rset,
+                                       row=row, col=col, form_buttons=buttons,
+                                       domid='%s-form' % divid, action='#',
+                                       cssstyle='display: none',
+                                       onsubmit=onsubmit % event_data)
         self.w(tags.div(value, klass='editableField', id=divid,
-                        ondblclick=self.ondblclick % event_data))
-        return form
+                        onclick=self._onclick % event_data))
+        renderer = self._build_renderer(entity, rtype, role)
+        self.w(form.form_render(renderer=renderer))
 
 
 class EditionFormView(FormViewMixIn, EntityView):