diff options
Diffstat (limited to 'addons/web/static/src/js')
| -rw-r--r-- | addons/web/static/src/js/chrome.js | 5 | ||||
| -rw-r--r-- | addons/web/static/src/js/core.js | 13 | ||||
| -rw-r--r-- | addons/web/static/src/js/data.js | 15 | ||||
| -rw-r--r-- | addons/web/static/src/js/data_export.js | 1 | ||||
| -rw-r--r-- | addons/web/static/src/js/search.js | 11 | ||||
| -rw-r--r-- | addons/web/static/src/js/tour.js | 2 | ||||
| -rw-r--r-- | addons/web/static/src/js/view_form.js | 137 | ||||
| -rw-r--r-- | addons/web/static/src/js/view_list.js | 4 | ||||
| -rw-r--r-- | addons/web/static/src/js/view_list_editable.js | 2 | ||||
| -rw-r--r-- | addons/web/static/src/js/views.js | 51 |
10 files changed, 153 insertions, 88 deletions
diff --git a/addons/web/static/src/js/chrome.js b/addons/web/static/src/js/chrome.js index 608f76cd6ca..8fa16a90b6e 100644 --- a/addons/web/static/src/js/chrome.js +++ b/addons/web/static/src/js/chrome.js @@ -1077,7 +1077,10 @@ instance.web.UserMenu = instance.web.Widget.extend({ state: JSON.stringify(state), scope: 'userinfo', }; - instance.web.redirect('https://accounts.openerp.com/oauth2/auth?'+$.param(params)); + instance.web.redirect('https://accounts.odoo.com/oauth2/auth?'+$.param(params)); + }).fail(function(result, ev){ + ev.preventDefault(); + instance.web.redirect('https://accounts.openerp.com/web'); }); } }, diff --git a/addons/web/static/src/js/core.js b/addons/web/static/src/js/core.js index 169547d78c0..5ed174a128d 100644 --- a/addons/web/static/src/js/core.js +++ b/addons/web/static/src/js/core.js @@ -772,6 +772,7 @@ instance.web.unblockUI = function() { $.fn.tooltip.Constructor.DEFAULTS.placement = 'auto top'; $.fn.tooltip.Constructor.DEFAULTS.html = true; $.fn.tooltip.Constructor.DEFAULTS.trigger = 'hover focus click'; +$.fn.tooltip.Constructor.DEFAULTS.container = 'body'; //overwrite bootstrap tooltip method to prevent showing 2 tooltip at the same time var bootstrap_show_function = $.fn.tooltip.Constructor.prototype.show; $.fn.tooltip.Constructor.prototype.show = function () { @@ -785,18 +786,6 @@ $.fn.tooltip.Constructor.prototype.show = function () { if (e.isDefaultPrevented() || !inDom) return; return bootstrap_show_function.call(this); }; -//overwrite bootstrap tooltip init method in order to check if tooltip is in a modal or not and -//if so it needs to have a container body in order to be visible -var bootstrap_init_tooltip_fnct = $.fn.tooltip.Constructor.prototype.init; -$.fn.tooltip.Constructor.prototype.init = function (type, element, options) { - options = options || {} - if ($('.modal[aria-hidden="false"]').length !== 0){ - if (options && !options.container){ - options = _.extend({container: 'body'},options); - } - } - return bootstrap_init_tooltip_fnct.call(this, type, element, options); -} /** * Registry for all the client actions key: tag value: widget diff --git a/addons/web/static/src/js/data.js b/addons/web/static/src/js/data.js index f580d282516..c22faaae9bd 100644 --- a/addons/web/static/src/js/data.js +++ b/addons/web/static/src/js/data.js @@ -73,8 +73,9 @@ instance.web.Query = instance.web.Class.extend({ } return q; }, - _execute: function () { + _execute: function (options) { var self = this; + options = options || {}; return instance.session.rpc('/web/dataset/search_read', { model: this._model.name, fields: this._fields || false, @@ -85,7 +86,7 @@ instance.web.Query = instance.web.Class.extend({ offset: this._offset, limit: this._limit, sort: instance.web.serialize_sort(this._order_by) - }).then(function (results) { + }, options).then(function (results) { self._count = results.length; return results.records; }, null); @@ -93,11 +94,12 @@ instance.web.Query = instance.web.Class.extend({ /** * Fetches the first record matching the query, or null * + * @param {Object} [options] additional options for the rpc() method * @returns {jQuery.Deferred<Object|null>} */ - first: function () { + first: function (options) { var self = this; - return this.clone({limit: 1})._execute().then(function (records) { + return this.clone({limit: 1})._execute(options).then(function (records) { delete self._count; if (records.length) { return records[0]; } return null; @@ -106,10 +108,11 @@ instance.web.Query = instance.web.Class.extend({ /** * Fetches all records matching the query * + * @param {Object} [options] additional options for the rpc() method * @returns {jQuery.Deferred<Array<>>} */ - all: function () { - return this._execute(); + all: function (options) { + return this._execute(options); }, /** * Fetches the number of records matching the query in the database diff --git a/addons/web/static/src/js/data_export.js b/addons/web/static/src/js/data_export.js index a6cb35423fb..f64ae7c139c 100644 --- a/addons/web/static/src/js/data_export.js +++ b/addons/web/static/src/js/data_export.js @@ -422,6 +422,7 @@ instance.web.DataExport = instance.web.Dialog.extend({ fields: exported_fields, ids: this.ids_to_export, domain: this.domain, + context: this.dataset.context, import_compat: !!this.$el.find("#import_compat").val(), })}, complete: instance.web.unblockUI, diff --git a/addons/web/static/src/js/search.js b/addons/web/static/src/js/search.js index 566179c2a38..17458e1c185 100644 --- a/addons/web/static/src/js/search.js +++ b/addons/web/static/src/js/search.js @@ -487,7 +487,6 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea */ setup_global_completion: function () { var self = this; - this.autocomplete = new instance.web.search.AutoComplete(this, { source: this.proxy('complete_global_search'), select: this.proxy('select_completion'), @@ -1624,7 +1623,7 @@ instance.web.search.ManyToOneField = instance.web.search.CharField.extend({ facet: { category: this.attrs.string, field: this, - values: [{label: value, value: value}] + values: [{label: value, value: value, operator: 'ilike'}] }, expand: this.expand.bind(this), }]); @@ -1672,9 +1671,13 @@ instance.web.search.ManyToOneField = instance.web.search.CharField.extend({ return facetValue.get('label'); }, make_domain: function (name, operator, facetValue) { + operator = facetValue.get('operator') || operator; + switch(operator){ case this.default_operator: return [[name, '=', facetValue.get('value')]]; + case 'ilike': + return [[name, 'ilike', facetValue.get('value')]]; case 'child_of': return [[name, 'child_of', facetValue.get('value')]]; } @@ -1963,7 +1966,7 @@ instance.web.search.Advanced = instance.web.search.Input.extend({ context: this.view.dataset.context }).done(function(data) { self.fields = { - id: { string: 'ID', type: 'id' } + id: { string: 'ID', type: 'id', searchable: true } }; _.each(data, function(field_def, field_name) { if (field_def.selectable !== false && field_name != 'id') { @@ -2037,7 +2040,7 @@ instance.web.search.ExtendedSearchProposition = instance.web.Widget.extend(/** @ this._super(parent); this.fields = _(fields).chain() .map(function(val, key) { return _.extend({}, val, {'name': key}); }) - .filter(function (field) { return !field.deprecated && (field.store === void 0 || field.store || field.fnct_search); }) + .filter(function (field) { return !field.deprecated && field.searchable; }) .sortBy(function(field) {return field.string;}) .value(); this.attrs = {_: _, fields: this.fields, selected: null}; diff --git a/addons/web/static/src/js/tour.js b/addons/web/static/src/js/tour.js index 0179d3eda65..6813a421cb7 100644 --- a/addons/web/static/src/js/tour.js +++ b/addons/web/static/src/js/tour.js @@ -323,7 +323,7 @@ var Tour = { }, log: function (message, add_user) { if (add_user) { - var user = $(".navbar .dropdown:has(>.js_usermenu) a:first, .navbar .oe_topbar_name").text(); + var user = $(".navbar .dropdown:has(>.js_usermenu) a:first, .navbar .oe_topbar_name, .pos .username").text(); if (!user && $('a[href*="/login"]')) user = 'Public User'; message += " (" + (user||"").replace(/^\s*|\s*$/g, '') + ")"; } diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index 93ebc8b8167..5f5fb401d8e 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -103,6 +103,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM this.fields_order = []; this.datarecord = {}; this._onchange_specs = {}; + this.onchanges_defs = []; this.default_focus_field = null; this.default_focus_button = null; this.fields_registry = instance.web.form.widgets; @@ -508,7 +509,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM def = self.alive(new instance.web.Model(self.dataset.model).call( "onchange", [ids, values, trigger_field_name, onchange_specs, context])); } - return def.then(function(response) { + var onchange_def = def.then(function(response) { if (widget && widget.field['change_default']) { var fieldname = widget.name; var value_; @@ -544,6 +545,8 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM }).then(function(response) { return self.on_processed_onchange(response); }); + this.onchanges_defs.push(onchange_def); + return onchange_def; } catch(e) { console.error(e); instance.webclient.crashmanager.show_message(e); @@ -643,6 +646,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM * if the current record is not yet saved. It will then stay in create mode. */ to_edit_mode: function() { + this.onchanges_defs = []; this._actualize_mode("edit"); }, /** @@ -792,12 +796,14 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM var self = this; var save_obj = {prepend_on_create: prepend_on_create, ret: null}; this.save_list.push(save_obj); - return this._process_operations().then(function() { - if (save_obj.error) - return $.Deferred().reject(); - return $.when.apply($, save_obj.ret); - }).done(function() { - self.$el.removeClass('oe_form_dirty'); + return $.when.apply($, self.onchanges_defs).then(function(){ + return self._process_operations().then(function() { + if (save_obj.error) + return $.Deferred().reject(); + return $.when.apply($, save_obj.ret); + }).done(function() { + self.$el.removeClass('oe_form_dirty'); + }); }); }, _process_save: function(save_obj) { @@ -2337,26 +2343,31 @@ instance.web.form.KanbanSelection = instance.web.form.FieldChar.extend({ }, render_value: function() { var self = this; - this.record_id = self.view.datarecord.id; - this.states = self.prepare_dropdown_selection();; + this.record_id = this.view.datarecord.id; + this.states = this.prepare_dropdown_selection();; this.$el.html(QWeb.render("KanbanSelection", {'widget': self})); - this.$el.find('.oe_legend').click(self.do_action.bind(self)); + this.$el.find('li').on('click', this.set_kanban_selection.bind(this)); }, - do_action: function(e) { + /* setting the value: in view mode, perform an asynchronous call and reload + the form view; in edit mode, use set_value to save the new value that will + be written when saving the record. */ + set_kanban_selection: function (ev) { var self = this; - var li = $(e.target).closest( "li" ); + var li = $(ev.target).closest('li'); if (li.length) { - var value = {}; - value[self.name] = String(li.data('value')); - self.record_id = self.view.datarecord.id; - if (self.record_id) { - return self.view.dataset._model.call('write', [[self.record_id], value, self.view.dataset.get_context()]).done(self.reload_record.bind(self)); - } else { - return self.view.on_button_save().done(function(result) { - if (result) { - self.view.dataset._model.call('write', [[result], value, self.view.dataset.get_context()]).done(self.reload_record.bind(self)); - } - }); + var value = String(li.data('value')); + if (this.view.get('actual_mode') == 'view') { + var write_values = {} + write_values[self.name] = value; + return this.view.dataset._model.call( + 'write', [ + [self.record_id], + write_values, + self.view.dataset.get_context() + ]).done(self.reload_record.bind(self)); + } + else { + return this.set_value(value); } } }, @@ -2388,27 +2399,34 @@ instance.web.form.Priority = instance.web.form.FieldChar.extend({ }, render_value: function() { var self = this; - this.record_id = self.view.datarecord.id; - this.priorities = self.prepare_priority(); + this.record_id = this.view.datarecord.id; + this.priorities = this.prepare_priority(); this.$el.html(QWeb.render("Priority", {'widget': this})); - this.$el.find('.oe_legend').click(self.do_action.bind(self)); + this.$el.find('li').on('click', this.set_priority.bind(this)); }, - do_action: function(e) { + /* setting the value: in view mode, perform an asynchronous call and reload + the form view; in edit mode, use set_value to save the new value that will + be written when saving the record. */ + set_priority: function (ev) { var self = this; - var li = $(e.target).closest( "li" ); + var li = $(ev.target).closest('li'); if (li.length) { - var value = {}; - value[self.name] = String(li.data('value')); - if (self.record_id) { - return self.view.dataset._model.call('write', [[self.record_id], value, self.view.dataset.get_context()]).done(self.reload_record.bind(self)); - } else { - return self.view.on_button_save().done(function(result) { - if (result) { - self.view.dataset._model.call('write', [[result], value, self.view.dataset.get_context()]).done(self.reload_record.bind(self)); - } - }); + var value = String(li.data('value')); + if (this.view.get('actual_mode') == 'view') { + var write_values = {} + write_values[self.name] = value; + return this.view.dataset._model.call( + 'write', [ + [self.record_id], + write_values, + self.view.dataset.get_context() + ]).done(self.reload_record.bind(self)); + } + else { + return this.set_value(value); } } + }, reload_record: function() { this.view.reload(); @@ -2515,14 +2533,10 @@ instance.web.form.FieldCharDomain = instance.web.form.AbstractField.extend(insta this._super.apply(this, arguments); this.on("change:effective_readonly", this, function () { this.display_field(); - this.render_value(); }); this.display_field(); return this._super(); }, - render_value: function() { - this.$('button.select_records').css('visibility', this.get('effective_readonly') ? 'hidden': ''); - }, set_value: function(value_) { var self = this; this.set('value', value_ || false); @@ -2537,7 +2551,12 @@ instance.web.form.FieldCharDomain = instance.web.form.AbstractField.extend(insta var ds = new instance.web.DataSetStatic(self, model, self.build_context()); ds.call('search_count', [domain]).then(function (results) { $('.oe_domain_count', self.$el).text(results + ' records selected'); - $('button span', self.$el).text(' Change selection'); + if (self.get('effective_readonly')) { + $('button span', self.$el).text(' See selection'); + } + else { + $('button span', self.$el).text(' Change selection'); + } }); } else { $('.oe_domain_count', this.$el).text('0 record selected'); @@ -2551,8 +2570,12 @@ instance.web.form.FieldCharDomain = instance.web.form.AbstractField.extend(insta var model = this.options.model || this.field_manager.get_field_value(this.options.model_field); this.pop = new instance.web.form.SelectCreatePopup(this); this.pop.select_element( - model, {title: 'Select records...'}, - [], this.build_context()); + model, { + title: this.get('effective_readonly') ? 'Selected records' : 'Select records...', + readonly: this.get('effective_readonly'), + disable_multiple_selection: this.get('effective_readonly'), + no_create: this.get('effective_readonly'), + }, [], this.build_context()); this.pop.on("elements_selected", self, function(element_ids) { if (this.pop.$('input.oe_list_record_selector').prop('checked')) { var search_data = this.pop.searchview.build_search_data(); @@ -2834,7 +2857,7 @@ instance.web.form.FieldTextHtml = instance.web.form.AbstractField.extend(instanc if (! this.get("effective_readonly")) { self._updating_editor = false; this.$textarea = this.$el.find('textarea'); - var width = ((this.node.attrs || {}).editor_width || '100%'); + var width = ((this.node.attrs || {}).editor_width || 'auto'); var height = ((this.node.attrs || {}).editor_height || 250); this.$textarea.cleditor({ width: width, // width not including margins, borders or padding @@ -3300,7 +3323,7 @@ instance.web.form.CompletionFieldMixin = { }); } // create... - if (!(self.options && self.options.no_create)){ + if (!(self.options && (self.options.no_create || self.options.no_create_edit))){ values.push({ label: _t("Create and Edit..."), action: function() { @@ -3575,7 +3598,7 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc } self.floating = false; } - if (used && self.get("value") === false && ! self.no_ed && (self.options.no_create === false || self.options.no_create === undefined)) { + if (used && self.get("value") === false && ! self.no_ed && ! (self.options && (self.options.no_create || self.options.no_quick_create))) { self.ed_def.reject(); self.uned_def.reject(); self.ed_def = $.Deferred(); @@ -3604,10 +3627,10 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc focusout: anyoneLoosesFocus, focus: function () { self.trigger('focused'); }, autocompleteopen: function () { ignore_blur = true; }, - autocompleteclose: function () { ignore_blur = false; }, + autocompleteclose: function () { setTimeout(function() {ignore_blur = false;},0); }, blur: function () { // autocomplete open - if (ignore_blur) { return; } + if (ignore_blur) { $(this).focus(); return; } if (_(self.getChildren()).any(function (child) { return child instanceof instance.web.form.AbstractFormPopup; })) { return; } @@ -5663,6 +5686,20 @@ instance.web.form.FieldBinaryImage = instance.web.form.FieldBinary.extend({ this._super.apply(this, arguments); this.render_value(); this.set_filename(''); + }, + set_value: function(value_){ + var changed = value_ !== this.get_value(); + this._super.apply(this, arguments); + // By default, on binary images read, the server returns the binary size + // This is possible that two images have the exact same size + // Therefore we trigger the change in case the image value hasn't changed + // So the image is re-rendered correctly + if (!changed){ + this.trigger("change:value", this, { + oldValue: value_, + newValue: value_ + }); + } } }); diff --git a/addons/web/static/src/js/view_list.js b/addons/web/static/src/js/view_list.js index fca5dd4f969..b36974e859e 100644 --- a/addons/web/static/src/js/view_list.js +++ b/addons/web/static/src/js/view_list.js @@ -167,7 +167,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi * @returns {String} CSS style declaration */ style_for: function (record) { - var style= ''; + var len, style= ''; var context = _.extend({}, record.attributes, { uid: this.session.uid, @@ -522,7 +522,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi self.dataset.index = 0; } } else if (self.dataset.index >= self.records.length) { - self.dataset.index = 0; + self.dataset.index = self.records.length ? 0 : null; } self.compute_aggregates(); diff --git a/addons/web/static/src/js/view_list_editable.js b/addons/web/static/src/js/view_list_editable.js index a83e9042199..62a0717d374 100644 --- a/addons/web/static/src/js/view_list_editable.js +++ b/addons/web/static/src/js/view_list_editable.js @@ -258,7 +258,7 @@ if (!focus_field){ focus_field = _.find(self.editor.form.fields_order, function(field){ return fields[field] && fields[field].$el.is(':visible:has(input)'); }); } - if (focus_field) fields[focus_field].$el.find('input').select(); + if (focus_field && fields[focus_field]) fields[focus_field].$el.find('input').select(); return record.attributes; }); }).fail(function () { diff --git a/addons/web/static/src/js/views.js b/addons/web/static/src/js/views.js index a3fc2784748..85e8e274d30 100644 --- a/addons/web/static/src/js/views.js +++ b/addons/web/static/src/js/views.js @@ -339,7 +339,12 @@ instance.web.ActionManager = instance.web.Widget.extend({ return this.do_action(action_client, options); } else if (_.isNumber(action) || _.isString(action)) { var self = this; - return self.rpc("/web/action/load", { action_id: action }).then(function(result) { + var additional_context = { + active_id : options.additional_context.active_id, + active_ids : options.additional_context.active_ids, + active_model : options.additional_context.active_model + }; + return self.rpc("/web/action/load", { action_id: action, additional_context : additional_context }).then(function(result) { return self.do_action(result, options); }); } @@ -407,15 +412,36 @@ instance.web.ActionManager = instance.web.Widget.extend({ } var widget = executor.widget(); if (executor.action.target === 'new') { + var pre_dialog = this.dialog; + if (pre_dialog){ + // prevent previous dialog to consider itself closed, + // right now, as we're opening a new one (prevents + // reload of original form view) + pre_dialog.off('closing', null, pre_dialog.on_close); + } if (this.dialog_widget && !this.dialog_widget.isDestroyed()) { this.dialog_widget.destroy(); } + // explicitly passing a closing action to dialog_stop() prevents + // it from reloading the original form view this.dialog_stop(executor.action); this.dialog = new instance.web.Dialog(this, { title: executor.action.name, dialogClass: executor.klass, }); - this.dialog.on("closing", null, options.on_close); + + // chain on_close triggers with previous dialog, if any + this.dialog.on_close = function(){ + options.on_close.apply(null, arguments); + if (pre_dialog && pre_dialog.on_close){ + // no parameter passed to on_close as this will + // only be called when the last dialog is truly + // closing, and *should* trigger a reload of the + // underlying form view (see comments above) + pre_dialog.on_close(); + } + }; + this.dialog.on("closing", null, this.dialog.on_close); if (widget instanceof instance.web.ViewManager) { _.extend(widget.flags, { $buttons: this.dialog.$buttons, @@ -428,6 +454,9 @@ instance.web.ActionManager = instance.web.Widget.extend({ this.dialog.open(); return initialized; } else { + // explicitly passing a closing action to dialog_stop() prevents + // it from reloading the original form view - we're opening a + // completely new action anyway this.dialog_stop(executor.action); this.inner_action = executor.action; this.inner_widget = widget; @@ -600,7 +629,7 @@ instance.web.ViewManager = instance.web.Widget.extend({ action_views_ids : views_ids }, self.flags, self.flags[view.view_type] || {}, view.options || {}) }); - + views_ids[view.view_type] = view.view_id; }); if (this.flags.views_switcher === false) { @@ -608,10 +637,10 @@ instance.web.ViewManager = instance.web.Widget.extend({ } // If no default view defined, switch to the first one in sequence var default_view = this.flags.default_view || this.views_src[0].view_type; - + return this.switch_mode(default_view, null, this.flags[default_view] && this.flags[default_view].options); - - + + }, switch_mode: function(view_type, no_store, view_options) { var self = this; @@ -689,7 +718,7 @@ instance.web.ViewManager = instance.web.Widget.extend({ } controller.on('switch_mode', self, this.switch_mode); controller.on('previous_view', self, this.prev_view); - + var container = this.$el.find("> div > div > .oe_view_manager_body > .oe_view_manager_view_" + view_type); var view_promise = controller.appendTo(container); this.views[view_type].controller = controller; @@ -1123,7 +1152,7 @@ instance.web.ViewManagerAction = instance.web.ViewManager.extend({ return self.switch_mode(state.view_type, true); }) ); - } + } $.when(this.views[this.active_view] ? this.views[this.active_view].deferred : $.when(), defs).done(function() { self.views[self.active_view].controller.do_load_state(state, warm); @@ -1445,12 +1474,12 @@ instance.web.View = instance.web.Widget.extend({ if (action_data.special === 'cancel') { return handler({"type":"ir.actions.act_window_close"}); } else if (action_data.type=="object") { - var args = [[record_id]], additional_args = []; + var args = [[record_id]]; if (action_data.args) { try { // Warning: quotes and double quotes problem due to json and xml clash // Maybe we should force escaping in xml or do a better parse of the args array - additional_args = JSON.parse(action_data.args.replace(/'/g, '"')); + var additional_args = JSON.parse(action_data.args.replace(/'/g, '"')); args = args.concat(additional_args); } catch(e) { console.error("Could not JSON.parse arguments", action_data.args); @@ -1520,7 +1549,7 @@ instance.web.View = instance.web.Widget.extend({ /** * Switches to a specific view type */ - do_switch_view: function() { + do_switch_view: function() { this.trigger.apply(this, ['switch_mode'].concat(_.toArray(arguments))); }, /** |
