[FIX] attempt to make editability handling more logical and simpler to manage.
authorXavier Morel <xmo@openerp.com>
Tue, 24 Jul 2012 17:05:50 +0000 (19:05 +0200)
committerXavier Morel <xmo@openerp.com>
Tue, 24 Jul 2012 17:05:50 +0000 (19:05 +0200)
Also less buggy, with a bit o' luck

bzr revid: xmo@openerp.com-20120724170550-150vimuk6bvzh8y8

addons/web/static/src/js/view_form.js
addons/web/static/src/js/view_list.js
addons/web/static/src/js/view_list_editable.js
doc/list-view.rst

index 7a0482a..835527b 100644 (file)
@@ -3027,8 +3027,11 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({
         this.viewmanager.on_controller_inited.add_last(function(view_type, controller) {
             controller.o2m = self;
             if (view_type == "list") {
-                if (self.get("effective_readonly"))
-                    controller.set_editable(false);
+                if (self.get("effective_readonly")) {
+                    controller.on('edit:before', self, function (e) {
+                        e.cancel = true;
+                    });
+                }
             } else if (view_type === "form") {
                 if (self.get("effective_readonly")) {
                     $(".oe_form_buttons", controller.$element).children().remove();
@@ -3301,7 +3304,7 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({
             .value();
     },
     do_add_record: function () {
-        if (this.options.editable) {
+        if (this.editable()) {
             this._super.apply(this, arguments);
         } else {
             var self = this;
@@ -4108,10 +4111,12 @@ instance.web.form.SelectCreatePopup = instance.web.form.AbstractFormPopup.extend
                     self.dataset, false,
                     _.extend({'deletable': false,
                         'selectable': !self.options.disable_multiple_selection,
-                        'read_only': true,
                         'import_enabled': false,
                         '$buttons': self.$buttonpane,
                     }, self.options.list_view_options || {}));
+            self.view_list.on('edit:before', self, function (e) {
+                e.cancel = true;
+            });
             self.view_list.popup = self;
             self.view_list.appendTo($(".oe_popup_list", self.$element)).pipe(function() {
                 self.view_list.do_show();
index f68f135..53a8c01 100644 (file)
@@ -21,9 +21,6 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
         // whether the view rows can be reordered (via vertical drag & drop)
         'reorderable': true,
         'action_buttons': true,
-        // if true, the view can't be editable, ignoring the view's and the context's
-        // instructions
-        'read_only': false,
         // if true, the 'Import', 'Export', etc... buttons will be shown
         'import_enabled': true,
     },
index f2ce08f..697715f 100644 (file)
@@ -12,6 +12,8 @@ openerp.web.list_editable = function (instance) {
             var self = this;
             this._super.apply(this, arguments);
 
+            this._force_editability = null;
+            this._context_editable = false;
             this.editor = this.make_editor();
             // Stores records of {field, cell}, allows for re-rendering fields
             // depending on cell state during and after resize events
@@ -37,6 +39,11 @@ openerp.web.list_editable = function (instance) {
                 }
             });
 
+            this.on('edit:before', this, function (event) {
+                if (!self.editable() || self.editor.is_editing()) {
+                    event.cancel = true;
+                }
+            });
             this.on('edit:after', this, function () {
                 self.$element.add(self.$buttons).addClass('oe_editing');
             });
@@ -62,26 +69,18 @@ openerp.web.list_editable = function (instance) {
         do_edit: function (index, id, dataset) {
             _.extend(this.dataset, dataset);
         },
-        /**
-         * Sets editability status for the list, based on defaults, view
-         * architecture and the provided flag, if any.
-         *
-         * @param {Boolean} [force] forces the list to editability. Sets new row edition status to "bottom".
-         */
-        set_editable: function (force) {
-            // TODO: fix handling of editability status to be simpler & clearer & more coherent
-            // If ``force``, set editability to bottom
-            // otherwise rely on view default
-            // view' @editable is handled separately as we have not yet
-            // fetched and processed the view at this point.
-            this.options.editable = (
-                    ! this.options.read_only && ((force && "bottom") || this.defaults.editable));
+        editable: function () {
+            if (this.fields_view.arch.attrs.editable || this._context_editable) {
+                return true;
+            }
+
+            return this.options.editable;
         },
         /**
          * Replace do_search to handle editability process
          */
         do_search: function(domain, context, group_by) {
-            this.set_editable(context['set_editable']);
+            this._context_editable = !!context.set_editable;
             this._super.apply(this, arguments);
         },
         /**
@@ -89,7 +88,7 @@ openerp.web.list_editable = function (instance) {
          * as an editable row at the top or bottom of the list)
          */
         do_add_record: function () {
-            if (this.options.editable) {
+            if (this.editable()) {
                 this.$element.find('table:first').show();
                 this.$element.find('.oe_view_nocontent').remove();
                 this.start_edition();
@@ -103,9 +102,8 @@ openerp.web.list_editable = function (instance) {
                 this.editor.destroy();
             }
             // tree/@editable takes priority on everything else if present.
-            this.options.editable = ! this.options.read_only && (data.arch.attrs.editable || this.options.editable);
             var result = this._super(data, grouped);
-            if (this.options.editable) {
+            if (this.editable()) {
                 // FIXME: any hook available to ensure this is only done once?
                 this.$buttons
                     .off('click', '.oe_list_save')
@@ -210,6 +208,12 @@ openerp.web.list_editable = function (instance) {
                         self.resize_fields();
                         return record.attributes;
                     });
+                }).fail(function () {
+                    // if the start_edition event is cancelled and it was a
+                    // creation, remove the newly-created empty record
+                    if (!record.get('id')) {
+                        self.records.remove(record);
+                    }
                 });
             });
         },
@@ -379,7 +383,7 @@ openerp.web.list_editable = function (instance) {
             return this.reload_record(record);
         },
         prepends_on_create: function () {
-            return this.options.editable === 'top';
+            return this.editable() === 'top';
         },
         setup_events: function () {
             var self = this;
@@ -701,7 +705,7 @@ openerp.web.list_editable = function (instance) {
 
     instance.web.ListView.List.include(/** @lends instance.web.ListView.List# */{
         row_clicked: function (event) {
-            if (!this.options.editable) {
+            if (!this.view.editable()) {
                 return this._super.apply(this, arguments);
             }
             var record_id = $(event.currentTarget).data('id');
index 0d67c27..83925d8 100644 (file)
@@ -87,34 +87,37 @@ List view edition is an extension to the base listview providing the
 capability of inline record edition by delegating to an embedded form
 view.
 
-.. todo::
-
-    cleanup options and settings for editability configuration. Right
-    now there are:
-
-    ``defaults.editable``
+Editability status
+++++++++++++++++++
 
-        ``null``, ``"top"`` or ``"bottom"``, generally broken and
-        useless
+The editability status of a list view can be queried through the
+:js:func:`~openerp.web.ListView.editable` method, will return a falsy
+value if the listview is not currently editable.
 
-    ``context.set_editable``
+The editability status is based on three flags:
 
-        forces ``options.editable`` to ``"bottom"``
+``tree/@editable``
 
-    ``view.arch.attrs.editable``
+    If present, can be either ``"top"`` or ``"bottom"``. Either will
+    make the list view editable, with new records being respectively
+    created at the top or at the bottom of the view.
 
-        same as ``defaults.editable``, but applied separately (after
-        reloading the view), if absent delegates to
-        ``options.editable`` which may have been set previously.
+``context.set_editable``
 
-    ``options.read_only``
+    Boolean flag extracted from a search context (during the
+    :js:func:`~openerp.web.ListView.do_search`` handler), ``true``
+    will make the view editable (from the top), ``false`` or the
+    absence of the flag is a noop.
 
-        force options.editable to false, or something?
+``defaults.editable``
 
-        .. note:: can probably be replaced by cancelling ``edit:before``
+    Like ``tree/@editable``, one of absent (``null``)), ``"top"`` or
+    ``"bottom"``, fallback for the list view if none of the previous
+    two flags are set.
 
-    and :js:func:`~openerp.web.ListView.set_editable` which
-    ultimately behaves weird-as-fuck-ly.
+These three flags can only *make* a listview editable, they can *not*
+override a previously set flag. To do that, a listview user should
+instead cancel :ref:`the edit:before event <listview-edit-before>`.
 
 The editable list view module adds a number of methods to the list
 view, on top of implementing the :js:class:`EditorDelegate` protocol:
@@ -219,6 +222,14 @@ view provides a number of dedicated events to its lifecycle.
           abort its current behavior as soon as possible, and rollback
           any state modification.
 
+          Generally speaking, an event should only be cancelled (by
+          setting the ``cancel`` flag to ``true``), uncancelling an
+          event is undefined as event handlers are executed on a
+          first-come-first-serve basis and later handlers may
+          re-cancel an uncancelled event.
+
+.. _listview-edit-before:
+
 ``edit:before`` *cancellable*
 
     Invoked before the list view starts editing a record.