[FIX] attempt to make editability handling more logical and simpler to manage.

Also less buggy, with a bit o' luck

bzr revid: xmo@openerp.com-20120724170550-150vimuk6bvzh8y8
This commit is contained in:
Xavier Morel 2012-07-24 19:05:50 +02:00
parent da361d1042
commit 38cb3de518
4 changed files with 63 additions and 46 deletions

View 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) { this.viewmanager.on_controller_inited.add_last(function(view_type, controller) {
controller.o2m = self; controller.o2m = self;
if (view_type == "list") { if (view_type == "list") {
if (self.get("effective_readonly")) if (self.get("effective_readonly")) {
controller.set_editable(false); controller.on('edit:before', self, function (e) {
e.cancel = true;
});
}
} else if (view_type === "form") { } else if (view_type === "form") {
if (self.get("effective_readonly")) { if (self.get("effective_readonly")) {
$(".oe_form_buttons", controller.$element).children().remove(); $(".oe_form_buttons", controller.$element).children().remove();
@ -3301,7 +3304,7 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({
.value(); .value();
}, },
do_add_record: function () { do_add_record: function () {
if (this.options.editable) { if (this.editable()) {
this._super.apply(this, arguments); this._super.apply(this, arguments);
} else { } else {
var self = this; var self = this;
@ -4108,10 +4111,12 @@ instance.web.form.SelectCreatePopup = instance.web.form.AbstractFormPopup.extend
self.dataset, false, self.dataset, false,
_.extend({'deletable': false, _.extend({'deletable': false,
'selectable': !self.options.disable_multiple_selection, 'selectable': !self.options.disable_multiple_selection,
'read_only': true,
'import_enabled': false, 'import_enabled': false,
'$buttons': self.$buttonpane, '$buttons': self.$buttonpane,
}, self.options.list_view_options || {})); }, 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.popup = self;
self.view_list.appendTo($(".oe_popup_list", self.$element)).pipe(function() { self.view_list.appendTo($(".oe_popup_list", self.$element)).pipe(function() {
self.view_list.do_show(); self.view_list.do_show();

View 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) // whether the view rows can be reordered (via vertical drag & drop)
'reorderable': true, 'reorderable': true,
'action_buttons': 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 // if true, the 'Import', 'Export', etc... buttons will be shown
'import_enabled': true, 'import_enabled': true,
}, },

View File

@ -12,6 +12,8 @@ openerp.web.list_editable = function (instance) {
var self = this; var self = this;
this._super.apply(this, arguments); this._super.apply(this, arguments);
this._force_editability = null;
this._context_editable = false;
this.editor = this.make_editor(); this.editor = this.make_editor();
// Stores records of {field, cell}, allows for re-rendering fields // Stores records of {field, cell}, allows for re-rendering fields
// depending on cell state during and after resize events // 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 () { this.on('edit:after', this, function () {
self.$element.add(self.$buttons).addClass('oe_editing'); self.$element.add(self.$buttons).addClass('oe_editing');
}); });
@ -62,26 +69,18 @@ openerp.web.list_editable = function (instance) {
do_edit: function (index, id, dataset) { do_edit: function (index, id, dataset) {
_.extend(this.dataset, dataset); _.extend(this.dataset, dataset);
}, },
/** editable: function () {
* Sets editability status for the list, based on defaults, view if (this.fields_view.arch.attrs.editable || this._context_editable) {
* architecture and the provided flag, if any. return true;
* }
* @param {Boolean} [force] forces the list to editability. Sets new row edition status to "bottom".
*/ return this.options.editable;
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));
}, },
/** /**
* Replace do_search to handle editability process * Replace do_search to handle editability process
*/ */
do_search: function(domain, context, group_by) { do_search: function(domain, context, group_by) {
this.set_editable(context['set_editable']); this._context_editable = !!context.set_editable;
this._super.apply(this, arguments); 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) * as an editable row at the top or bottom of the list)
*/ */
do_add_record: function () { do_add_record: function () {
if (this.options.editable) { if (this.editable()) {
this.$element.find('table:first').show(); this.$element.find('table:first').show();
this.$element.find('.oe_view_nocontent').remove(); this.$element.find('.oe_view_nocontent').remove();
this.start_edition(); this.start_edition();
@ -103,9 +102,8 @@ openerp.web.list_editable = function (instance) {
this.editor.destroy(); this.editor.destroy();
} }
// tree/@editable takes priority on everything else if present. // 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); var result = this._super(data, grouped);
if (this.options.editable) { if (this.editable()) {
// FIXME: any hook available to ensure this is only done once? // FIXME: any hook available to ensure this is only done once?
this.$buttons this.$buttons
.off('click', '.oe_list_save') .off('click', '.oe_list_save')
@ -210,6 +208,12 @@ openerp.web.list_editable = function (instance) {
self.resize_fields(); self.resize_fields();
return record.attributes; 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); return this.reload_record(record);
}, },
prepends_on_create: function () { prepends_on_create: function () {
return this.options.editable === 'top'; return this.editable() === 'top';
}, },
setup_events: function () { setup_events: function () {
var self = this; var self = this;
@ -701,7 +705,7 @@ openerp.web.list_editable = function (instance) {
instance.web.ListView.List.include(/** @lends instance.web.ListView.List# */{ instance.web.ListView.List.include(/** @lends instance.web.ListView.List# */{
row_clicked: function (event) { row_clicked: function (event) {
if (!this.options.editable) { if (!this.view.editable()) {
return this._super.apply(this, arguments); return this._super.apply(this, arguments);
} }
var record_id = $(event.currentTarget).data('id'); var record_id = $(event.currentTarget).data('id');

View 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 capability of inline record edition by delegating to an embedded form
view. view.
.. todo:: Editability status
++++++++++++++++++
cleanup options and settings for editability configuration. Right The editability status of a list view can be queried through the
now there are: :js:func:`~openerp.web.ListView.editable` method, will return a falsy
value if the listview is not currently editable.
``defaults.editable`` The editability status is based on three flags:
``null``, ``"top"`` or ``"bottom"``, generally broken and ``tree/@editable``
useless
``context.set_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.
forces ``options.editable`` to ``"bottom"`` ``context.set_editable``
``view.arch.attrs.editable`` 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.
same as ``defaults.editable``, but applied separately (after ``defaults.editable``
reloading the view), if absent delegates to
``options.editable`` which may have been set previously.
``options.read_only`` Like ``tree/@editable``, one of absent (``null``)), ``"top"`` or
``"bottom"``, fallback for the list view if none of the previous
two flags are set.
force options.editable to false, or something? These three flags can only *make* a listview editable, they can *not*
override a previously set flag. To do that, a listview user should
.. note:: can probably be replaced by cancelling ``edit:before`` instead cancel :ref:`the edit:before event <listview-edit-before>`.
and :js:func:`~openerp.web.ListView.set_editable` which
ultimately behaves weird-as-fuck-ly.
The editable list view module adds a number of methods to the list The editable list view module adds a number of methods to the list
view, on top of implementing the :js:class:`EditorDelegate` protocol: 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 abort its current behavior as soon as possible, and rollback
any state modification. 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* ``edit:before`` *cancellable*
Invoked before the list view starts editing a record. Invoked before the list view starts editing a record.