merge stable
authorAdrien Di Mascio <Adrien.DiMascio@logilab.fr>
Thu, 09 Jul 2009 09:42:44 +0200
branchstable
changeset 2341 6db26d081c37
parent 2336 a15d3f54f0f2 (diff)
parent 2340 65445ae67db0 (current diff)
child 2342 923111e6ab33
merge
--- a/utils.py	Wed Jul 08 16:21:31 2009 +0200
+++ b/utils.py	Thu Jul 09 09:42:44 2009 +0200
@@ -326,4 +326,11 @@
     """
     # XXX deprecated, no more necessary
 
+def compute_cardinality(eschema, rschema, role):
+    if role == 'subject':
+        targetschema = rschema.objects(eschema)[0]
+        return rschema.rproperty(eschema, targetschema, 'cardinality')[0]
+    targetschema = rschema.subjects(eschema)[0]
+    return rschema.rproperty(targetschema, eschema, 'cardinality')[1]
 
+
--- a/web/data/cubicweb.edition.js	Wed Jul 08 16:21:31 2009 +0200
+++ b/web/data/cubicweb.edition.js	Thu Jul 09 09:42:44 2009 +0200
@@ -454,7 +454,8 @@
  * @param eid : the eid of the entity being edited
  * @param reload: boolean to reload page if true (when changing URL dependant data)
  */
-function inlineValidateAttributeForm(formid, rtype, eid, divid, reload, default_value) {
+function inlineValidateAttributeForm(formid, rtype, eid, divid, reload,
+                                     default_value, lzone) {
     try {
 	var form = getNode(formid);
 	if (typeof FCKeditorAPI != "undefined") {
@@ -467,7 +468,7 @@
 	}
 	var zipped = formContents(form);
 	var d = asyncRemoteExec('edit_field', 'apply', zipped[0], zipped[1],
-                                rtype, eid, default_value);
+                                rtype, eid, default_value, lzone);
     } catch (ex) {
 	log('got exception', ex);
 	return false;
@@ -494,30 +495,35 @@
     return false;
 }
 
-function inlineValidateRelationForm(formid, rtype, role, eid, divid, vid, default_value) {
+function inlineValidateRelationForm(formid, rtype, role, eid, divid, reload, vid,
+                                    default_value, escape, lzone) {
     try {
 	var form = getNode(formid);
         var relname = rtype + ':' + eid;
         var newtarget = jQuery('[name=' + relname + ']').val();
 	var zipped = formContents(form);
 	var d = asyncRemoteExec('edit_relation', 'apply', zipped[0], zipped[1], rtype, role,
-                                eid, vid, default_value);
+                                eid, vid, default_value, escape, lzone);
     } catch (ex) {
 	log('got exception', ex);
 	return false;
     }
     d.addCallback(function (result, req) {
-        handleFormValidationResponse(formid, noop, noop, result);
-	var fieldview = getNode(divid);
-        fieldview.innerHTML = result[2];
-	// switch inline form off only if no error
-	if (result[0]) {
-          // hide global error messages
-	  jQuery('div.errorMessage').remove();
-	  jQuery('#appMsg').hide();
-          var inputname = 'edit' + role[0] + '-' + relname;
-          jQuery('input[name=' + inputname + ']').val(newtarget);
-	  cancelInlineEdit(eid, rtype, divid);
+        if (reload) {
+          document.location.href = result[1];
+        } else {
+          handleFormValidationResponse(formid, noop, noop, result);
+	  var fieldview = getNode(divid);
+          fieldview.innerHTML = result[2];
+	  // switch inline form off only if no error
+	  if (result[0]) {
+            // hide global error messages
+	    jQuery('div.errorMessage').remove();
+	    jQuery('#appMsg').hide();
+            var inputname = 'edit' + role[0] + '-' + relname;
+            jQuery('input[name=' + inputname + ']').val(newtarget);
+	    cancelInlineEdit(eid, rtype, divid);
+          }
 	}
         return false;
     });
--- a/web/formfields.py	Wed Jul 08 16:21:31 2009 +0200
+++ b/web/formfields.py	Thu Jul 09 09:42:44 2009 +0200
@@ -14,7 +14,7 @@
 from yams.constraints import SizeConstraint, StaticVocabularyConstraint
 
 from cubicweb.schema import FormatConstraint
-from cubicweb.utils import ustrftime
+from cubicweb.utils import ustrftime, compute_cardinality
 from cubicweb.common import tags, uilib
 from cubicweb.web import INTERNAL_FIELD_VALUE
 from cubicweb.web.formwidgets import (
@@ -460,7 +460,7 @@
     fieldclass = None
     if role == 'subject':
         targetschema = rschema.objects(eschema)[0]
-        card = rschema.rproperty(eschema, targetschema, 'cardinality')[0]
+        card = compute_cardinality(eschema, rschema, role)
         help = rschema.rproperty(eschema, targetschema, 'description')
         if rschema.is_final():
             if rschema.rproperty(eschema, targetschema, 'internationalizable'):
@@ -470,7 +470,7 @@
             kwargs.setdefault('initial', get_default)
     else:
         targetschema = rschema.subjects(eschema)[0]
-        card = rschema.rproperty(targetschema, eschema, 'cardinality')[1]
+        card = compute_cardinality(eschema, rschema, role)
         help = rschema.rproperty(targetschema, eschema, 'description')
     kwargs['required'] = card in '1+'
     kwargs['name'] = rschema.type
--- a/web/views/basecontrollers.py	Wed Jul 08 16:21:31 2009 +0200
+++ b/web/views/basecontrollers.py	Thu Jul 09 09:42:44 2009 +0200
@@ -388,7 +388,7 @@
         return _validate_form(self.req, self.vreg)
 
     @jsonize
-    def js_edit_field(self, action, names, values, rtype, eid, default):
+    def js_edit_field(self, action, names, values, rtype, eid, default, lzone):
         success, args = self.validate_form(action, names, values)
         if success:
             # Any X,N where we don't seem to use N is an optimisation
@@ -396,25 +396,25 @@
             rset = self.req.execute('Any X,N WHERE X eid %%(x)s, X %s N' % rtype,
                                     {'x': eid}, 'x')
             entity = rset.get_entity(0, 0)
-            value = entity.printable_value(rtype)
-            return (success, args, value or default)
+            value = entity.printable_value(rtype) or default
+            return (success, args, lzone + value)
         else:
             return (success, args, None)
 
     @jsonize
-    def js_edit_relation(self, action, names, values,
-                         rtype, role, eid, vid, default):
+    def js_edit_relation(self, action, names, values, rtype,
+                         role, eid, vid, default, escape, lzone):
         success, args = self.validate_form(action, names, values)
         if success:
             entity = self.req.eid_rset(eid).get_entity(0, 0)
             rset = entity.related(rtype, role)
             if rset:
                 output = self.view(vid, rset)
-                if vid == 'textoutofcontext':
+                if escape == 'True':
                     output = xml_escape(output)
             else:
                 output = default
-            return (success, args, output)
+            return (success, args, lzone + output)
         else:
             return (success, args, None)
 
--- a/web/views/editforms.py	Wed Jul 08 16:21:31 2009 +0200
+++ b/web/views/editforms.py	Thu Jul 09 09:42:44 2009 +0200
@@ -17,13 +17,13 @@
 
 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
 from cubicweb.view import EntityView
 from cubicweb.common import tags
 from cubicweb.web import INTERNAL_FIELD_VALUE, stdmsgs, eid_param
 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
 
 
@@ -90,43 +90,58 @@
 
     # 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 = "showInlineEditionForm(%(eid)s, '%(rtype)s', '%(divid)s')"
+    _defaultlandingzone = u'<img title="%s" src="data/file.gif"/>'
+    _landingzonemsg = _('click to edit this field')
+    # default relation vids according to cardinality
+    _one_rvid = 'incontext'
+    _many_rvid = 'csv'
 
-    def cell_call(self, row, col, rtype=None, role='subject', reload=False,
-                  vid='textoutofcontext', default=None):
+    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 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
+                  escape=True,       # depending on the vid, will xml_escape or not
+                  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 double-click"""
-        rschema = self.schema.rschema(rtype)
+        assert rtype
         entity = self.entity(row, col)
+        rschema = entity.schema.rschema(rtype)
         if not default:
-            default = self.req._('not specified')
+            default = xml_escape(self.req._('<no value>'))
+        # 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
+            form = self._build_attribute_form(entity, value, rtype, role, reload,
+                                              row, col, default, landing_zone)
         else:
+            if rvid is None:
+                rvid = self._compute_best_vid(entity, rtype, role)
             rset = entity.related(rtype, role)
-            # XXX xml_escape but that depends of the actual vid
-            value = xml_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)
-        else:
-            form = self._build_relation_form(entity, value, rtype, role,
-                                             row, col, vid, default)
+            candidate = self.view(rvid, rset, 'null')
+            if candidate and escape:
+                value = xml_escape(candidate)
+            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)
+            form = self._build_relation_form(entity, value, rtype, role, reload, row, col,
+                                             rvid, default, escape, landing_zone)
         renderer = self.vreg.select_object('formrenderers', 'base', self.req,
                                       entity=entity,
                                       display_label=False, display_help=False,
@@ -135,13 +150,17 @@
                                       display_progress_div=False)
         self.w(form.form_render(renderer=renderer))
 
-    def _build_relation_form(self, entity, value, rtype, role, row, col, vid, default):
-        entity = self.entity(row, col)
+    def _build_relation_form(self, entity, value, rtype, role, row, col, reload, rvid,
+                             default, escape, lzone):
+        lzone = lzone or self._defaultlandingzone % self.req._(self._landingzonemsg)
+        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}
+        event_data = {'divid' : divid, 'eid' : entity.eid, 'rtype' : rtype, 'vid' : rvid,
+                      'reload' : reload, 'default' : default, 'role' : role,
+                      'escape' : escape, 'lzone' : lzone}
         onsubmit = ("return inlineValidateRelationForm('%(divid)s-form', '%(rtype)s', "
-                    "'%(role)s', '%(eid)s', '%(divid)s', '%(vid)s', '%(default)s');"
+                    "'%(role)s', '%(eid)s', '%(divid)s', %(reload)s, '%(vid)s', "
+                    "'%(default)s', '%(escape)s', '%(lzone)s');"
                     % event_data)
         cancelclick = "cancelInlineEdit(%s,\'%s\',\'%s\')" % (
             entity.eid, rtype, divid)
@@ -151,18 +170,19 @@
                                        form_buttons=[SubmitButton(),
                                                      Button(stdmsgs.BUTTON_CANCEL,
                                                        onclick=cancelclick)])
-        form.append_field(RelationField(name=rtype, role=role, sort=True,
-                                        widget=Select(),
-                                        label=u' '))
+        field = guess_field(entity.e_schema, entity.schema.rschema(rtype), role)
+        form.append_field(field)
         self.w(tags.div(value, klass='editableField', id=divid,
-                        ondblclick=self.ondblclick % event_data))
+                        onclick=self._onclick % event_data))
         return form
 
-    def _build_attribute_form(self, entity, value, rtype, role, reload, row, col, default):
+    def _build_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('%(divid)s-form', '%(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\')" % (
@@ -171,9 +191,9 @@
                                        row=row, col=col, form_buttons=buttons,
                                        domid='%s-form' % divid, action='#',
                                        cssstyle='display: none',
-                                       onsubmit=self.onsubmit % event_data)
+                                       onsubmit=onsubmit % event_data)
         self.w(tags.div(value, klass='editableField', id=divid,
-                        ondblclick=self.ondblclick % event_data))
+                        onclick=self._onclick % event_data))
         return form