summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js
diff options
context:
space:
mode:
authorFabien Meghazi <fme@openerp.com>2012-02-15 10:59:40 +0100
committerFabien Meghazi <fme@openerp.com>2012-02-15 10:59:40 +0100
commitbc233c30e5b9d28c9576b50c6f75dadbdf2e35ea (patch)
treeffadfea5a36040b12635e11acdc4237d5a953db8 /addons/web/static/src/js
parent5ab1b4f37ef93952567939444196cd9a91b92f02 (diff)
parent750c5db2a30f89593cbd64137e765b11d5226808 (diff)
[FIX] Merge last trunk
bzr revid: fme@openerp.com-20120215095940-2jv27t91i7u5mm0r
Diffstat (limited to 'addons/web/static/src/js')
-rw-r--r--addons/web/static/src/js/chrome.js102
-rw-r--r--addons/web/static/src/js/core.js121
-rw-r--r--addons/web/static/src/js/data.js96
-rw-r--r--addons/web/static/src/js/data_import.js3
-rw-r--r--addons/web/static/src/js/formats.js16
-rw-r--r--addons/web/static/src/js/search.js52
-rw-r--r--addons/web/static/src/js/view_editor.js112
-rw-r--r--addons/web/static/src/js/view_form.js226
-rw-r--r--addons/web/static/src/js/view_list.js13
-rw-r--r--addons/web/static/src/js/view_list_editable.js4
-rw-r--r--addons/web/static/src/js/view_page.js13
-rw-r--r--addons/web/static/src/js/views.js90
12 files changed, 623 insertions, 225 deletions
diff --git a/addons/web/static/src/js/chrome.js b/addons/web/static/src/js/chrome.js
index 1eb61f6a0ce..ba31ec81846 100644
--- a/addons/web/static/src/js/chrome.js
+++ b/addons/web/static/src/js/chrome.js
@@ -20,17 +20,27 @@ openerp.web.Notification = openerp.web.OldWidget.extend(/** @lends openerp.web.
expires: 2500
});
},
- notify: function(title, text) {
+ notify: function(title, text, sticky) {
+ sticky = !!sticky;
+ var opts = {};
+ if (sticky) {
+ opts.expires = false;
+ }
this.$element.notify('create', {
title: title,
text: text
- });
+ }, opts);
},
- warn: function(title, text) {
+ warn: function(title, text, sticky) {
+ sticky = !!sticky;
+ var opts = {};
+ if (sticky) {
+ opts.expires = false;
+ }
this.$element.notify('create', 'oe_notification_alert', {
title: title,
text: text
- });
+ }, opts);
}
});
@@ -141,12 +151,7 @@ openerp.web.Dialog = openerp.web.OldWidget.extend(/** @lends openerp.web.Dialog#
});
openerp.web.CrashManager = openerp.web.CallbackEnabled.extend({
- init: function() {
- this._super();
- openerp.connection.on_rpc_error.add(this.on_rpc_error);
- },
on_rpc_error: function(error) {
- this.error = error;
if (error.data.fault_code) {
var split = ("" + error.data.fault_code).split('\n')[0].split(' -- ');
if (split.length > 1) {
@@ -195,7 +200,7 @@ openerp.web.CrashManager = openerp.web.CallbackEnabled.extend({
};
}
var dialog = new openerp.web.Dialog(this, {
- title: "OpenERP " + _.str.capitalize(this.error.type),
+ title: "OpenERP " + _.str.capitalize(error.type),
width: '80%',
height: '50%',
min_width: '800px',
@@ -247,8 +252,8 @@ openerp.web.Loading = openerp.web.OldWidget.extend(/** @lends openerp.web.Loadin
this.count += increment;
if (this.count > 0) {
- //this.$element.html(QWeb.render("Loading", {}));
- $(".loading",this.$element).html("Loading ("+this.count+")");
+ $(".loading",this.$element).text(_.str.sprintf(
+ _t("Loading (%d)"), this.count));
$(".loading",this.$element).show();
this.widget_parent.$element.addClass('loading');
} else {
@@ -286,9 +291,9 @@ openerp.web.Database = openerp.web.OldWidget.extend(/** @lends openerp.web.Datab
this.$option_id = $("#oe_db_options");
var self = this;
- var fetch_db = this.rpc("/web/database/get_list", {}, function(result) {
- self.db_list = result.db_list;
- });
+ var fetch_db = this.rpc("/web/database/get_list", {}).pipe(
+ function(result) { self.db_list = result.db_list; },
+ function (_, ev) { ev.preventDefault(); self.db_list = null; });
var fetch_langs = this.rpc("/web/session/get_lang_list", {}, function(result) {
if (result.error) {
self.display_error(result);
@@ -423,9 +428,11 @@ openerp.web.Database = openerp.web.OldWidget.extend(/** @lends openerp.web.Datab
self.display_error(result);
return;
}
- self.db_list.push(self.to_object(fields)['db_name']);
- self.db_list.sort();
- self.widget_parent.set_db_list(self.db_list);
+ if (self.db_list) {
+ self.db_list.push(self.to_object(fields)['db_name']);
+ self.db_list.sort();
+ self.widget_parent.set_db_list(self.db_list);
+ }
var form_obj = self.to_object(fields);
self.wait_for_newdb(result, {
password: form_obj['super_admin_pwd'],
@@ -454,8 +461,10 @@ openerp.web.Database = openerp.web.OldWidget.extend(/** @lends openerp.web.Datab
return;
}
$db_list.find(':selected').remove();
- self.db_list.splice(_.indexOf(self.db_list, db, true), 1);
- self.widget_parent.set_db_list(self.db_list);
+ if (self.db_list) {
+ self.db_list.splice(_.indexOf(self.db_list, db, true), 1);
+ self.widget_parent.set_db_list(self.db_list);
+ }
self.do_notify("Dropping database", "The database '" + db + "' has been dropped");
});
}
@@ -607,6 +616,10 @@ openerp.web.Login = openerp.web.OldWidget.extend(/** @lends openerp.web.Login#
}
var $e = this.$element;
var db = $e.find("form [name=db]").val();
+ if (!db) {
+ this.do_warn("Login", "No database selected !");
+ return false;
+ }
var login = $e.find("form input[name=login]").val();
var password = $e.find("form input[name=password]").val();
@@ -621,6 +634,7 @@ openerp.web.Login = openerp.web.OldWidget.extend(/** @lends openerp.web.Login#
*/
do_login: function (db, login, password) {
var self = this;
+ this.$element.removeClass('login_invalid');
this.session.on_session_invalid.add({
callback: function () {
self.$element.addClass("login_invalid");
@@ -914,7 +928,7 @@ openerp.web.Menu = openerp.web.OldWidget.extend(/** @lends openerp.web.Menu# */
}
$primary_menu.addClass('active');
this.$secondary_menu.find(
- 'div[data-menu-parent=' + $primary_menu.data('menu') + ']').show();
+ 'div[data-menu-parent=' + $primary_menu.data('menu') + ']').addClass('active').toggle(!this.folded);
},
on_menu_click: function(ev, id) {
id = id || 0;
@@ -945,7 +959,8 @@ openerp.web.Menu = openerp.web.OldWidget.extend(/** @lends openerp.web.Menu# */
do_menu_click: function($clicked_menu, manual) {
var $sub_menu, $main_menu,
active = $clicked_menu.is('.active'),
- sub_menu_visible = false;
+ sub_menu_visible = false,
+ has_submenu_items = false;
if (this.$secondary_menu.has($clicked_menu).length) {
$sub_menu = $clicked_menu.parents('.oe_secondary_menu');
@@ -956,15 +971,18 @@ openerp.web.Menu = openerp.web.OldWidget.extend(/** @lends openerp.web.Menu# */
}
sub_menu_visible = $sub_menu.is(':visible');
+ has_submenu_items = !!$sub_menu.children().length;
this.$secondary_menu.find('.oe_secondary_menu').hide();
$('.active', this.$element.add(this.$secondary_menu)).removeClass('active');
$main_menu.add($clicked_menu).add($sub_menu).addClass('active');
- if (!(this.folded && manual)) {
- this.do_show_secondary($sub_menu, $main_menu);
- } else {
- this.do_show_secondary();
+ if (has_submenu_items) {
+ if (!(this.folded && manual)) {
+ this.do_show_secondary($sub_menu, $main_menu);
+ } else {
+ this.do_show_secondary();
+ }
}
if ($main_menu != $clicked_menu) {
@@ -980,7 +998,7 @@ openerp.web.Menu = openerp.web.OldWidget.extend(/** @lends openerp.web.Menu# */
return true;
}
} else if (this.folded) {
- if (active && sub_menu_visible) {
+ if ((active && sub_menu_visible) || !has_submenu_items) {
$sub_menu.hide();
return true;
}
@@ -1079,9 +1097,10 @@ openerp.web.WebClient = openerp.web.OldWidget.extend(/** @lends openerp.web.WebC
self.action_manager = new openerp.web.ActionManager(self);
self.action_manager.appendTo($("#oe_app"));
self.bind_hashchange();
+ var version_label = _t("OpenERP - Unsupported/Community Version");
if (!self.session.openerp_entreprise) {
- self.$element.find('.oe_footer_powered').append('<span> - <a href="http://www.openerp.com/support-or-publisher-warranty-contract" target="_blank">Unsupported/Community Version</a></span>');
- document.title = _t("OpenERP - Unsupported/Community Version");
+ self.$element.find('.oe_footer_powered').append(_.str.sprintf('<span> - <a href="http://www.openerp.com/support-or-publisher-warranty-contract" target="_blank">%s</a></span>', version_label));
+ document.title = version_label;
}
});
},
@@ -1107,8 +1126,17 @@ openerp.web.WebClient = openerp.web.OldWidget.extend(/** @lends openerp.web.WebC
self.menu.start();
},
show_common: function() {
+ var self = this;
if (!this.crashmanager) {
this.crashmanager = new openerp.web.CrashManager();
+ openerp.connection.on_rpc_error.add(this.crashmanager.on_rpc_error);
+ window.onerror = function (message, file, line) {
+ self.crashmanager.on_traceback({
+ type: _t("Client Error"),
+ message: message,
+ data: {debug: file + ':' + line}
+ });
+ }
}
this.notification = new openerp.web.Notification(this);
this.notification.appendTo(this.$element);
@@ -1137,12 +1165,14 @@ openerp.web.WebClient = openerp.web.OldWidget.extend(/** @lends openerp.web.WebC
n.warn.apply(n, arguments);
},
on_logout: function() {
- this.session.session_logout();
- $(window).unbind('hashchange', this.on_hashchange);
- this.do_push_state({});
- //would be cool to be able to do this, but I think it will make addons do strange things
- //this.show_login();
- window.location.reload();
+ var self = this;
+ this.session.session_logout().then(function () {
+ $(window).unbind('hashchange', self.on_hashchange);
+ self.do_push_state({});
+ //would be cool to be able to do this, but I think it will make addons do strange things
+ //this.show_login();
+ window.location.reload();
+ });
},
bind_hashchange: function() {
$(window).bind('hashchange', this.on_hashchange);
@@ -1157,7 +1187,7 @@ openerp.web.WebClient = openerp.web.OldWidget.extend(/** @lends openerp.web.WebC
on_hashchange: function(event) {
var state = event.getState(true);
if (!_.isEqual(this._current_state, state)) {
- this.action_manager.do_load_state(state);
+ this.action_manager.do_load_state(state, !!this._current_state);
}
this._current_state = state;
},
diff --git a/addons/web/static/src/js/core.js b/addons/web/static/src/js/core.js
index 500cf58d05f..c4e37a8a3b1 100644
--- a/addons/web/static/src/js/core.js
+++ b/addons/web/static/src/js/core.js
@@ -254,6 +254,7 @@ openerp.web.Registry = openerp.web.Class.extend( /** @lends openerp.web.Registry
* @param {Object} mapping a mapping of keys to object-paths
*/
init: function (mapping) {
+ this.parent = null;
this.map = mapping || {};
},
/**
@@ -269,6 +270,9 @@ openerp.web.Registry = openerp.web.Class.extend( /** @lends openerp.web.Registry
get_object: function (key, silent_error) {
var path_string = this.map[key];
if (path_string === undefined) {
+ if (this.parent) {
+ return this.parent.get_object(key, silent_error);
+ }
if (silent_error) { return void 'nooo'; }
throw new openerp.web.KeyNotFound(key);
}
@@ -287,6 +291,21 @@ openerp.web.Registry = openerp.web.Class.extend( /** @lends openerp.web.Registry
return object_match;
},
/**
+ * Checks if the registry contains an object mapping for this key.
+ *
+ * @param {String} key key to look for
+ */
+ contains: function (key) {
+ if (key === undefined) { return false; }
+ if (key in this.map) {
+ return true
+ }
+ if (this.parent) {
+ return this.parent.contains(key);
+ }
+ return false;
+ },
+ /**
* Tries a number of keys, and returns the first object matching one of
* the keys.
*
@@ -299,7 +318,7 @@ openerp.web.Registry = openerp.web.Class.extend( /** @lends openerp.web.Registry
get_any: function (keys) {
for (var i=0; i<keys.length; ++i) {
var key = keys[i];
- if (key === undefined || !(key in this.map)) {
+ if (!this.contains(key)) {
continue;
}
@@ -324,11 +343,22 @@ openerp.web.Registry = openerp.web.Class.extend( /** @lends openerp.web.Registry
* Creates and returns a copy of the current mapping, with the provided
* mapping argument added in (replacing existing keys if needed)
*
+ * Parent and child remain linked, a new key in the parent (which is not
+ * overwritten by the child) will appear in the child.
+ *
* @param {Object} [mapping={}] a mapping of keys to object-paths
*/
+ extend: function (mapping) {
+ var child = new openerp.web.Registry(mapping);
+ child.parent = this;
+ return child;
+ },
+ /**
+ * @deprecated use Registry#extend
+ */
clone: function (mapping) {
- return new openerp.web.Registry(
- _.extend({}, this.map, mapping || {}));
+ console.warn('Registry#clone is deprecated, use Registry#extend');
+ return this.extend(mapping);
}
});
@@ -393,7 +423,7 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.
this.qweb_mutex = new $.Mutex();
},
bind: function(origin) {
- var window_origin = location.protocol+"//"+location.host;
+ var window_origin = location.protocol+"//"+location.host, self=this;
this.origin = origin ? _.str.rtrim(origin,'/') : window_origin;
this.prefix = this.origin;
this.server = this.origin; // keep chs happy
@@ -405,8 +435,11 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.
this.user_context= {};
this.db = false;
this.openerp_entreprise = false;
- this.module_list = [];
- this.module_loaded = {"web": true};
+ this.module_list = openerp._modules.slice();
+ this.module_loaded = {};
+ _(this.module_list).each(function (mod) {
+ self.module_loaded[mod] = true;
+ });
this.context = {};
this.shortcuts = [];
this.active_id = null;
@@ -641,6 +674,7 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.
},
session_logout: function() {
this.set_cookie('session_id', '');
+ return this.rpc("/web/session/destroy", {});
},
on_session_valid: function() {
},
@@ -694,21 +728,26 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.
load_modules: function(no_session_valid_signal) {
var self = this;
return this.rpc('/web/session/modules', {}).pipe(function(result) {
- var lang = self.user_context.lang;
- var params = { mods: ["web"].concat(result), lang: lang};
+ var lang = self.user_context.lang,
+ all_modules = _.uniq(self.module_list.concat(result));
+ var params = { mods: all_modules, lang: lang};
var to_load = _.difference(result, self.module_list).join(',');
- self.module_list = result;
- return $.when(
- self.rpc('/web/webclient/csslist', {mods: to_load}, self.do_load_css),
- self.rpc('/web/webclient/qweblist', {mods: to_load}).pipe(self.do_load_qweb),
- self.rpc('/web/webclient/translations', params).pipe(function(trans) {
- openerp.web._t.database.set_bundle(trans);
- var file_list = ["/web/static/lib/datejs/globalization/" + lang.replace("_", "-") + ".js"];
- return self.rpc('/web/webclient/jslist', {mods: to_load}).pipe(function(files) {
- return self.do_load_js(file_list.concat(files));
- });
- })
- ).then(function() {
+ self.module_list = all_modules;
+
+ var loaded = $.Deferred().resolve().promise();
+ if (to_load.length) {
+ loaded = $.when(
+ self.rpc('/web/webclient/csslist', {mods: to_load}, self.do_load_css),
+ self.rpc('/web/webclient/qweblist', {mods: to_load}).pipe(self.do_load_qweb),
+ self.rpc('/web/webclient/translations', params).pipe(function(trans) {
+ openerp.web._t.database.set_bundle(trans);
+ var file_list = ["/web/static/lib/datejs/globalization/" + lang.replace("_", "-") + ".js"];
+ return self.rpc('/web/webclient/jslist', {mods: to_load}).pipe(function(files) {
+ return self.do_load_js(file_list.concat(files));
+ });
+ }))
+ }
+ return loaded.then(function() {
self.on_modules_loaded();
if (!no_session_valid_signal) {
self.on_session_valid();
@@ -1174,9 +1213,7 @@ openerp.web.TranslationDataBase = openerp.web.Class.extend(/** @lends openerp.we
add_module_translation: function(mod) {
var self = this;
_.each(mod.messages, function(message) {
- if (self.db[message.id] === undefined) {
- self.db[message.id] = message.string;
- }
+ self.db[message.id] = message.string;
});
},
build_translation_function: function() {
@@ -1223,18 +1260,34 @@ openerp.web.qweb.default_dict = {
'_' : _,
'_t' : openerp.web._t
};
-openerp.web.qweb.format_text_node = function (s) {
- // Note that 'this' is the Qweb Node of the text
- var translation = this.node.parentNode.attributes['t-translation'];
- if (translation && translation.value === 'off') {
- return s;
- }
- var ts = _.str.trim(s);
- if (ts.length === 0) {
- return s;
+openerp.web.qweb.preprocess_node = function() {
+ // Note that 'this' is the Qweb Node
+ switch (this.node.nodeType) {
+ case 3:
+ case 4:
+ // Text and CDATAs
+ var translation = this.node.parentNode.attributes['t-translation'];
+ if (translation && translation.value === 'off') {
+ return;
+ }
+ var ts = _.str.trim(this.node.data);
+ if (ts.length === 0) {
+ return;
+ }
+ var tr = openerp.web._t(ts);
+ if (tr !== ts) {
+ this.node.data = tr;
+ }
+ break;
+ case 1:
+ // Element
+ var attr, attrs = ['label', 'title', 'alt'];
+ while (attr = attrs.pop()) {
+ if (this.attributes[attr]) {
+ this.attributes[attr] = openerp.web._t(this.attributes[attr]);
+ }
+ }
}
- var tr = openerp.web._t(ts);
- return tr === ts ? s : tr;
};
/** Jquery extentions */
diff --git a/addons/web/static/src/js/data.js b/addons/web/static/src/js/data.js
index be7d707c91c..636ad575f99 100644
--- a/addons/web/static/src/js/data.js
+++ b/addons/web/static/src/js/data.js
@@ -248,6 +248,7 @@ openerp.web.DataSet = openerp.web.OldWidget.extend( /** @lends openerp.web.Data
this.model = model;
this.context = context || {};
this.index = null;
+ this._sort = [];
},
previous: function () {
this.index -= 1;
@@ -496,7 +497,38 @@ openerp.web.DataSet = openerp.web.OldWidget.extend( /** @lends openerp.web.Data
return new openerp.web.CompoundContext(this.context, request_context);
}
return this.context;
- }
+ },
+ /**
+ * Reads or changes sort criteria on the dataset.
+ *
+ * If not provided with any argument, serializes the sort criteria to
+ * an SQL-like form usable by OpenERP's ORM.
+ *
+ * If given a field, will set that field as first sorting criteria or,
+ * if the field is already the first sorting criteria, will reverse it.
+ *
+ * @param {String} [field] field to sort on, reverses it (toggle from ASC to DESC) if already the main sort criteria
+ * @param {Boolean} [force_reverse=false] forces inserting the field as DESC
+ * @returns {String|undefined}
+ */
+ sort: function (field, force_reverse) {
+ if (!field) {
+ return openerp.web.serialize_sort(this._sort);
+ }
+ var reverse = force_reverse || (this._sort[0] === field);
+ this._sort.splice.apply(
+ this._sort, [0, this._sort.length].concat(
+ _.without(this._sort, field, '-' + field)));
+
+ this._sort.unshift((reverse ? '-' : '') + field);
+ return undefined;
+ },
+ size: function () {
+ return this.ids.length;
+ },
+ alter_ids: function(n_ids) {
+ this.ids = n_ids;
+ },
});
openerp.web.DataSetStatic = openerp.web.DataSet.extend({
init: function(parent, model, context, ids) {
@@ -507,7 +539,6 @@ openerp.web.DataSetStatic = openerp.web.DataSet.extend({
read_slice: function (fields, options) {
options = options || {};
fields = fields || {};
- // TODO remove fields from options
var offset = options.offset || 0,
limit = options.limit || false;
var end_pos = limit && limit !== -1 ? offset + limit : this.ids.length;
@@ -542,8 +573,8 @@ openerp.web.DataSetSearch = openerp.web.DataSet.extend(/** @lends openerp.web.D
init: function(parent, model, context, domain) {
this._super(parent, model, context);
this.domain = domain || [];
- this._sort = [];
this.offset = 0;
+ this._length;
// subset records[offset:offset+limit]
// is it necessary ?
this.ids = [];
@@ -575,6 +606,7 @@ openerp.web.DataSetSearch = openerp.web.DataSet.extend(/** @lends openerp.web.D
}).pipe(function (result) {
self.ids = result.ids;
self.offset = offset;
+ self._length = result.length;
return result.records;
});
},
@@ -584,31 +616,6 @@ openerp.web.DataSetSearch = openerp.web.DataSet.extend(/** @lends openerp.web.D
}
return this.domain;
},
- /**
- * Reads or changes sort criteria on the dataset.
- *
- * If not provided with any argument, serializes the sort criteria to
- * an SQL-like form usable by OpenERP's ORM.
- *
- * If given a field, will set that field as first sorting criteria or,
- * if the field is already the first sorting criteria, will reverse it.
- *
- * @param {String} [field] field to sort on, reverses it (toggle from ASC to DESC) if already the main sort criteria
- * @param {Boolean} [force_reverse=false] forces inserting the field as DESC
- * @returns {String|undefined}
- */
- sort: function (field, force_reverse) {
- if (!field) {
- return openerp.web.serialize_sort(this._sort);
- }
- var reverse = force_reverse || (this._sort[0] === field);
- this._sort.splice.apply(
- this._sort, [0, this._sort.length].concat(
- _.without(this._sort, field, '-' + field)));
-
- this._sort.unshift((reverse ? '-' : '') + field);
- return undefined;
- },
unlink: function(ids, callback, error_callback) {
var self = this;
return this._super(ids, function(result) {
@@ -620,6 +627,12 @@ openerp.web.DataSetSearch = openerp.web.DataSet.extend(/** @lends openerp.web.D
if (callback)
callback(result);
}, error_callback);
+ },
+ size: function () {
+ if (this._length !== undefined) {
+ return this._length;
+ }
+ return this._super();
}
});
openerp.web.BufferedDataSet = openerp.web.DataSetStatic.extend({
@@ -641,7 +654,6 @@ openerp.web.BufferedDataSet = openerp.web.DataSetStatic.extend({
defaults: this.last_default_get};
this.to_create.push(_.extend(_.clone(cached), {values: _.clone(cached.values)}));
this.cache.push(cached);
- this.on_change();
var prom = $.Deferred().then(callback);
prom.resolve({result: cached.id});
return prom.promise();
@@ -723,6 +735,24 @@ openerp.web.BufferedDataSet = openerp.web.DataSetStatic.extend({
throw "Record not correctly loaded";
}
}
+ var sort_fields = self._sort,
+ compare = function (v1, v2) {
+ return (v1 < v2) ? -1
+ : (v1 > v2) ? 1
+ : 0;
+ };
+ records.sort(function (a, b) {
+ return _.reduce(sort_fields, function (acc, field) {
+ if (acc) { return acc; }
+
+ var sign = 1;
+ if (field[0] === '-') {
+ sign = -1;
+ field = field.slice(1);
+ }
+ return sign * compare(a[field], b[field]);
+ }, 0);
+ });
completion.resolve(records);
};
if(to_get.length > 0) {
@@ -734,7 +764,7 @@ openerp.web.BufferedDataSet = openerp.web.DataSetStatic.extend({
self.cache.push({id: id, values: record});
} else {
// I assume cache value is prioritary
- _.defaults(cached.values, record);
+ cached.values = _.defaults(_.clone(cached.values), record);
}
});
return_records();
@@ -757,7 +787,11 @@ openerp.web.BufferedDataSet = openerp.web.DataSetStatic.extend({
}
}
return this._super(method, args, callback, error_callback);
- }
+ },
+ alter_ids: function(n_ids) {
+ this._super(n_ids);
+ this.on_change();
+ },
});
openerp.web.BufferedDataSet.virtual_id_regex = /^one2many_v_id_.*$/;
diff --git a/addons/web/static/src/js/data_import.js b/addons/web/static/src/js/data_import.js
index 18870ca2157..4bcdd4dd1df 100644
--- a/addons/web/static/src/js/data_import.js
+++ b/addons/web/static/src/js/data_import.js
@@ -109,9 +109,10 @@ openerp.web.DataImport = openerp.web.Dialog.extend({
_(fields).each(function (field, field_name) {
// Ignore spec for id field
// Don't import function fields (function and related)
- if (field_name === 'id' || 'function' in field) {
+ if (field_name === 'id' || field.readonly) {
return;
}
+
var f = {
id: field_name,
name: field_name,
diff --git a/addons/web/static/src/js/formats.js b/addons/web/static/src/js/formats.js
index eb7d6c335ac..e8970b65f52 100644
--- a/addons/web/static/src/js/formats.js
+++ b/addons/web/static/src/js/formats.js
@@ -101,7 +101,12 @@ openerp.web.format_value = function (value, descriptor, value_if_empty) {
formatted[0] = openerp.web.insert_thousand_seps(formatted[0]);
return formatted.join(l10n.decimal_point);
case 'float_time':
- return _.str.sprintf("%02d:%02d",
+ var pattern = '%02d:%02d';
+ if (value < 0) {
+ value = Math.abs(value);
+ pattern = '-' + pattern;
+ }
+ return _.str.sprintf(pattern,
Math.floor(value),
Math.round((value % 1) * 60));
case 'many2one':
@@ -171,12 +176,17 @@ openerp.web.parse_value = function (value, descriptor, value_if_empty) {
throw new Error(value + " is not a correct float");
return parsed;
case 'float_time':
+ var factor = 1;
+ if (value[0] === '-') {
+ value = value.slice(1);
+ factor = -1;
+ }
var float_time_pair = value.split(":");
if (float_time_pair.length != 2)
- return openerp.web.parse_value(value, {type: "float"});
+ return factor * openerp.web.parse_value(value, {type: "float"});
var hours = openerp.web.parse_value(float_time_pair[0], {type: "integer"});
var minutes = openerp.web.parse_value(float_time_pair[1], {type: "integer"});
- return hours + (minutes / 60);
+ return factor * (hours + (minutes / 60));
case 'progressbar':
return openerp.web.parse_value(value, {type: "float"});
case 'datetime':
diff --git a/addons/web/static/src/js/search.js b/addons/web/static/src/js/search.js
index 96af74672d3..74ae65785f9 100644
--- a/addons/web/static/src/js/search.js
+++ b/addons/web/static/src/js/search.js
@@ -154,7 +154,6 @@ openerp.web.SearchView = openerp.web.OldWidget.extend(/** @lends openerp.web.Sea
// for extended search view
var ext = new openerp.web.search.ExtendedSearch(this, this.model);
lines.push([ext]);
- this.inputs.push(ext);
this.extended_search = ext;
var render = QWeb.render("SearchView", {
@@ -187,7 +186,10 @@ openerp.web.SearchView = openerp.web.OldWidget.extend(/** @lends openerp.web.Sea
}).then(function(result) {
self.managed_filters = result;
var filters = self.$element.find(".oe_search-view-filters-management");
- filters.html(QWeb.render("SearchView.managed-filters", {filters: result}));
+ filters.html(QWeb.render("SearchView.managed-filters", {
+ filters: result,
+ disabled_filter_message: _t('Filter disabled due to invalid syntax')
+ }));
filters.change(self.on_filters_management);
});
},
@@ -425,13 +427,9 @@ openerp.web.SearchView = openerp.web.OldWidget.extend(/** @lends openerp.web.Sea
this.$element.find('table:last').hide();
$('.searchview_extended_groups_list').empty();
- _.each(this.inputs, function (input) {
- if(input.datewidget && input.datewidget.value) {
- input.datewidget.set_value(false);
- }
- });
- return $.async_when().pipe(
- reload_view !== false ? this.on_clear : null);
+ return $.async_when.apply(
+ null, _(this.inputs).invoke('clear')).pipe(
+ reload_view !== false ? this.on_clear : null);
},
/**
* Triggered when the search view gets cleared
@@ -474,6 +472,7 @@ openerp.web.search.fields = new openerp.web.Registry({
'text': 'openerp.web.search.CharField',
'boolean': 'openerp.web.search.BooleanField',
'integer': 'openerp.web.search.IntegerField',
+ 'id': 'openerp.web.search.IntegerField',
'float': 'openerp.web.search.FloatField',
'selection': 'openerp.web.search.SelectionField',
'datetime': 'openerp.web.search.DateTimeField',
@@ -617,7 +616,11 @@ openerp.web.search.Input = openerp.web.search.Widget.extend( /** @lends openerp.
}
}
this.attrs = attrs;
- }
+ },
+ /**
+ * Specific clearing operations, if any
+ */
+ clear: function () {}
});
openerp.web.search.FilterGroup = openerp.web.search.Input.extend(/** @lends openerp.web.search.FilterGroup# */{
template: 'SearchView.filters',
@@ -899,6 +902,22 @@ openerp.web.search.SelectionField = openerp.web.search.Field.extend(/** @lends o
defaults[this.attrs.name] = false;
}
return this._super(defaults);
+ },
+ clear: function () {
+ var self = this, d = $.Deferred(), selection = this.attrs.selection;
+ for(var index=0; index<selection.length; ++index) {
+ var item = selection[index];
+ if (!item[1]) {
+ setTimeout(function () {
+ // won't override mutable, because we immediately bail out
+ //noinspection JSReferencingMutableVariableFromClosure
+ self.$element.val(index);
+ d.resolve();
+ }, 0);
+ return d.promise();
+ }
+ }
+ return d.resolve().promise();
}
});
openerp.web.search.BooleanField = openerp.web.search.SelectionField.extend(/** @lends openerp.web.search.BooleanField# */{
@@ -946,15 +965,20 @@ openerp.web.search.DateField = openerp.web.search.Field.extend(/** @lends opener
template: "SearchView.date",
start: function () {
this._super();
+ // FIXME: this insanity puts a div inside a span
this.datewidget = new openerp.web.DateWidget(this);
this.datewidget.prependTo(this.$element);
- this.datewidget.$element.find("input").attr("size", 15);
- this.datewidget.$element.find("input").attr("autofocus",
- this.attrs.default_focus === '1' ? 'autofocus' : undefined);
+ this.datewidget.$element.find("input")
+ .attr("size", 15)
+ .attr("autofocus", this.attrs.default_focus === '1' ? 'autofocus' : null)
+ .removeAttr('style');
this.datewidget.set_value(this.defaults[this.attrs.name] || false);
},
get_value: function () {
return this.datewidget.get_value() || null;
+ },
+ clear: function () {
+ this.datewidget.set_value(false);
}
});
/**
@@ -1050,7 +1074,7 @@ openerp.web.search.ManyToOneField = openerp.web.search.CharField.extend({
}
});
-openerp.web.search.ExtendedSearch = openerp.web.search.Widget.extend({
+openerp.web.search.ExtendedSearch = openerp.web.search.Input.extend({
template: 'SearchView.extended_search',
init: function (parent, model) {
this._super(parent);
diff --git a/addons/web/static/src/js/view_editor.js b/addons/web/static/src/js/view_editor.js
index 26376f24da2..0258509f172 100644
--- a/addons/web/static/src/js/view_editor.js
+++ b/addons/web/static/src/js/view_editor.js
@@ -27,6 +27,7 @@ openerp.web.ViewEditor = openerp.web.OldWidget.extend({
type: 'ir.actions.act_window',
target: "current",
limit: this.dataset.limit || 80,
+ auto_search : true,
flags: {
sidebar: false,
deletable: false,
@@ -45,9 +46,10 @@ openerp.web.ViewEditor = openerp.web.OldWidget.extend({
{text: _t("Create"), click: function() { self.on_create_view(); }},
{text: _t("Edit"), click: function() { self.xml_element_id = 0; self.get_arch(); }},
{text: _t("Remove"), click: function() { self.do_delete_view(); }},
- {text: _t("Close"), click: function() { self.view_edit_dialog.close(); }}
+ {text: _t("Close"), click: function() { self.view_edit_dialog.close(); window.location.reload(); }}
]
}).open();
+ this.view_edit_dialog.on_close.add_last(function(){window.location.reload();});
this.main_view_id = this.parent.fields_view.view_id;
this.action_manager = new openerp.web.ActionManager(this);
this.action_manager.appendTo(this.view_edit_dialog);
@@ -162,11 +164,12 @@ openerp.web.ViewEditor = openerp.web.OldWidget.extend({
}
},
do_delete_view: function() {
+ var self = this;
if (confirm(_t("Do you really want to remove this view?"))) {
var controller = this.action_manager.inner_viewmanager.views[this.action_manager.inner_viewmanager.active_view].controller;
this.dataset.unlink([this.main_view_id]).then(function() {
controller.reload_content();
- this.main_view_id = self.parent.fields_view.view_id;
+ self.main_view_id = self.parent.fields_view.view_id;
});
}
},
@@ -227,14 +230,14 @@ openerp.web.ViewEditor = openerp.web.OldWidget.extend({
get_arch: function() {
var self = this;
var view_arch_list = [];
- this.dataset.read_ids([parseInt(self.main_view_id)], ['arch', 'type']).then(function(arch) {
+ this.dataset.read_ids([parseInt(self.main_view_id)], ['arch', 'type','priority']).then(function(arch) {
if (arch.length) {
var arch_object = self.parse_xml(arch[0].arch, self.main_view_id);
self.main_view_type = arch[0].type == 'tree'? 'list': arch[0].type;
- view_arch_list.push({"view_id": self.main_view_id, "arch": arch[0].arch});
+ view_arch_list.push({"view_id": self.main_view_id, "arch": arch[0].arch,"priority":arch[0].priority});
self.dataset.read_slice([], {domain: [['inherit_id','=', parseInt(self.main_view_id)]]}).then(function(result) {
_.each(result, function(res) {
- view_arch_list.push({"view_id": res.id, "arch": res.arch});
+ view_arch_list.push({"view_id": res.id, "arch": res.arch,"priority":res.priority});
self.inherit_view(arch_object, res);
});
return self.edit_view({"main_object": arch_object,
@@ -303,7 +306,7 @@ openerp.web.ViewEditor = openerp.web.OldWidget.extend({
case 3:
//for field[@name='type']
obj = _.detect(arch_object, function(element){
- if ((_.intersection(_.flatten(element.att_list), _.uniq(check))).length == check.length) {
+ if ((_.intersection(_.flatten(element.att_list), _.uniq(check))).length == _.uniq(check).length) {
return element;
}
});
@@ -361,6 +364,20 @@ openerp.web.ViewEditor = openerp.web.OldWidget.extend({
title: _.str.sprintf(_t("View Editor %d - %s"), self.main_view_id, self.model),
height: '90%',
buttons: [
+ {text: _t("Inherited View"), click: function(){
+ var selected_row = self.edit_xml_dialog.$element.find('.ui-selected');
+ if (selected_row.length) {
+ if(selected_row.find('a').text().search("field") != -1){
+ if (confirm(_t("Do you really wants to create an inherited view here?"))) {
+ self.inherited_view(selected_row);
+ }
+ }else{
+ alert("Can't Update View");
+ }
+ }else{
+ alert("Select an element");
+ }
+ }},
{text: _t("Preview"), click: function() {
var action = {
context: self.session.user_context,
@@ -368,6 +385,7 @@ openerp.web.ViewEditor = openerp.web.OldWidget.extend({
views: [[self.main_view_id, self.main_view_type]],
type: 'ir.actions.act_window',
target: "new",
+ auto_search: true,
flags: {
sidebar: false,
views_switcher: false,
@@ -378,6 +396,7 @@ openerp.web.ViewEditor = openerp.web.OldWidget.extend({
action_manager.do_action(action);
}},
{text: _t("Close"), click: function(){
+ self.action_manager.inner_viewmanager.views[self.action_manager.inner_viewmanager.active_view].controller.reload_content();
self.edit_xml_dialog.close();
}}
]
@@ -397,6 +416,53 @@ openerp.web.ViewEditor = openerp.web.OldWidget.extend({
self.on_select_img(this);
});
},
+ inherited_view: function(selected_row){
+ var self = this;
+ var row_id = parseInt((selected_row.attr('id')).split('-')[1]);
+ var obj = self.get_object_by_id(row_id,self.one_object['main_object'], [])[0];
+ var view_name = this.model + '.inherit_' + Math.round(Math.random() * 1000);
+ var view_find = selected_row;
+ var view_id;
+ var min_level = parseInt(selected_row.attr('level'));
+ while (1) {
+ view_find = view_find.prev();
+ if (view_find.length == 0 ||
+ self.edit_xml_dialog.$element.find(view_find).find('a').text().search("view_id") != -1 &&
+ parseInt(view_find.attr('level')) < min_level ) {
+ view_id = parseInt($(view_find).find('a').text().replace(/[^0-9]+/g, ''));
+ break;
+ }
+ if (view_find.attr('level') < min_level) {
+ min_level = parseInt(view_find.attr('level'));
+ }
+ }
+ var val = _.detect(obj.att_list, function(val) {return val[0] == "name";});
+ var priority = _.detect(self.one_object['arch'], function(val) {return val.view_id == view_id;});
+ var arch = _.str.sprintf("<?xml version='1.0'?>\n\t <field name='%s' position='after'> </field>", val[1]);
+ var vals = {'model': self.model, 'name': view_name, 'priority': priority.priority + 1, 'type': "form", 'arch': arch,'inherit_id':self.main_view_id};
+ this.dataset.create(vals, function(suc) {
+ var arch_to_obj = self.parse_xml(arch,suc.result);
+ obj.child_id.push(arch_to_obj[0]);
+ self.one_object['parent_child_id'] = self.parent_child_list(self.one_object['main_object'],[]);
+ self.one_object['arch'].push({'view_id':suc.result,"arch":arch,'priority': priority.priority + 1});
+ self.increase_level(arch_to_obj[0],obj.level+1);
+ self.render_inherited_view(selected_row,arch_to_obj[0]);
+ });
+ },
+ render_inherited_view: function(selected_row,obj){
+ var self = this,row_id = parseInt((selected_row.attr('id')).split('-')[1]);
+ var clone = this.create_clone(selected_row.clone(),obj);
+ if (selected_row.find("img[id^='parentimg-']").length == 0) {
+ ($(selected_row.find('a').parent()).siblings('td'))
+ .append($('<img width="16" height="16"></img>').attr('src', '/web/static/src/img/collapse.gif').
+ attr('id','parentimg-'+ row_id).click(function(){
+ self.do_parent_img_hide_show(this);
+ }));
+ }
+ self.edit_xml_dialog.$element.
+ find("tr[id='viewedit-"+row_id+"']").after(clone.removeClass('ui-selected'));
+ _.each(obj.child_id,function(obj){self.render_inherited_view(clone,obj);});
+ },
on_select_img: function(element_img) {
var self = this;
var side = $(element_img).closest("tr[id^='viewedit-']");
@@ -449,7 +515,7 @@ openerp.web.ViewEditor = openerp.web.OldWidget.extend({
},
do_node_add: function(side){
var self = this;
- var tr = $(side).find('a').text();
+ var tr = self.get_object_by_id(this.one_object.clicked_tr_id, this.one_object['main_object'], [])[0].att_list[0];
var parent_tr = ($(side).prevAll("tr[level=" + String(this.one_object.clicked_tr_level - 1) + "]"))[0];
var field_dataset = new openerp.web.DataSetSearch(this, this.model, null, null);
parent_tr = $(parent_tr).find('a').text();
@@ -562,13 +628,13 @@ openerp.web.ViewEditor = openerp.web.OldWidget.extend({
arch.arch = _.detect(children, function(xml_child) {
var temp_obj = self.create_View_Node(xml_child),
insert = _.intersection(_.flatten(temp_obj.att_list),_.uniq(check_list));
- if (insert.length == check_list.length ) {return xml_child;}
+ if (insert.length == _.uniq(check_list).length ) {return xml_child;}
});
}
arch_to_pass = _.filter($(arch.arch), function (child) {
return child.nodeType == 1;
});
- return self.do_save_xml(arch_to_pass[0], obj[0].child_id[0],[], move_direct, update_values,arch);
+ return self.do_save_xml(arch_to_pass[0], obj[0].child_id[0],obj[0].child_id, move_direct, update_values,arch);
},
get_object_by_id: function(id, one_object, result) {
var self = this;
@@ -583,7 +649,7 @@ openerp.web.ViewEditor = openerp.web.OldWidget.extend({
}
return result;
},
- create_clone: function(clone, new_node_obj, position){
+ create_clone: function(clone, new_node_obj){
var self = this;
clone.find('a').text(new_node_obj.name);
($(clone.find('a').parent()).siblings('td')).css( "padding-left", 20 * new_node_obj.level);
@@ -599,7 +665,8 @@ openerp.web.ViewEditor = openerp.web.OldWidget.extend({
return clone;
},
do_save_xml: function(arch1, obj, child_list, move_direct, update_values, arch){
- var self = this, children_list = $(arch1).children(), list_obj_xml = _.zip(children_list, obj.child_id);
+ var self = this, children_list = $(arch1).children(),list_obj_xml;
+ try{list_obj_xml = _.zip(children_list, obj.child_id);}catch(err){return;}
if (this.one_object.clicked_tr_id) {
if (obj.id == this.one_object.clicked_tr_id) {
var parent = false, index = _.indexOf(child_list, obj);
@@ -823,6 +890,7 @@ openerp.web.ViewEditor = openerp.web.OldWidget.extend({
var value = _.detect(arch_val[0]['att_list'],function(res) {
return res instanceof Array? _.include(res, widget.name): false;
});
+
value = value instanceof Array ? value[1] : value;
self.edit_node_dialog.$element.find('table[id=rec_table]').append('<tr><td align="right">' + widget.string + ':</td>' + type_widget.render() + '</tr>');
type_widget.start();
@@ -845,7 +913,7 @@ openerp.web.ViewEditor = openerp.web.OldWidget.extend({
res_groups.read_slice([], {}).done(function (res_grp) {
_.each(res_grp, function (res) {
var key = res.id;
- group_names[key] = res.name;
+ group_names[key]=res.full_name;
group_ids.push(res.id);
});
model_data.read_slice([], {domain: [
@@ -933,16 +1001,16 @@ openerp.web.ViewEditor = openerp.web.OldWidget.extend({
var action_manager = new openerp.web.ActionManager(self);
$.when(action_manager.do_action(action)).then(function() {
var controller = action_manager.dialog_viewmanager.views['form'].controller;
- // TODO NIV: use page view
- controller.do_set_readonly.add_last(function(){
+ controller.on_button_cancel.add_last(function(){
+ action_manager.stop()
+ });
+ controller.do_save.add_last(function(){
action_manager.stop();
- new_fields_name = new openerp.web.DataSetSearch(self,'ir.model.fields', null, null);
- new_fields_name.read_ids([controller.datarecord.id], ['name']).then(function(result) {
- self.add_node_dialog.$element.find('select[id=field_value]').append($("<option selected></option>").attr("value", result[0].name).text(result[0].name));
+ var value =controller.fields.name.value;
+ self.add_node_dialog.$element.find('select[id=field_value]').append($("<option selected></option>").attr("value",value).text(value));
_.detect(self.add_widget,function(widget){
- widget.name == "field_value"? widget.selection.push(result[0].name): false;
+ widget.name == "field_value"? widget.selection.push(value): false;
});
- });
});
});
}
@@ -1039,7 +1107,7 @@ openerp.web.ViewEditor.FieldSelect = openerp.web.ViewEditor.Field.extend({
var index = 0;
value = value === null? false: value;
for (var i = 0, ii = this.selection.length; i < ii; i++) {
- if ((this.selection[i] instanceof Array && this.selection[i][1] === value) || this.selection[i] === value) index = i;
+ if ((this.selection[i] instanceof Array && this.selection[i][0] === value) || this.selection[i] === value) index = i;
}
this.$element.find("select[id=" + this.name + "]")[0].selectedIndex = index;
},
@@ -1092,10 +1160,10 @@ var _CHILDREN = {
'calendar': ['field'],
'notebook': ['page'],
'page': ['notebook', 'group', 'field', 'label', 'button', 'newline', 'separator'],
- 'group': ['field', 'label', 'button', 'separator', 'newline'],
+ 'group': ['field', 'label', 'button', 'separator', 'newline','group'],
'board': ['column'],
'action': [],
- 'field': ['form', 'tree', 'graph'],
+ 'field': ['form', 'tree', 'graph','field'],
'label': [],
'button' : [],
'newline': [],
diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js
index d84a10c0236..02a42046fa5 100644
--- a/addons/web/static/src/js/view_form.js
+++ b/addons/web/static/src/js/view_form.js
@@ -117,18 +117,28 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
this.sidebar.attachments = new openerp.web.form.SidebarAttachments(this.sidebar, this);
this.sidebar.add_toolbar(this.fields_view.toolbar);
this.set_common_sidebar_sections(this.sidebar);
+
+ this.sidebar.add_section(_t('Customize'), 'customize');
+ this.sidebar.add_items('customize', [{
+ label: _t('Set Default'),
+ form: this,
+ callback: function (item) {
+ item.form.open_defaults_dialog();
+ }
+ }]);
}
this.has_been_loaded.resolve();
},
- do_load_state: function(state) {
+ do_load_state: function(state, warm) {
if (state.id && this.datarecord.id != state.id) {
- var idx = this.dataset.get_id_index(state.id);
- if (idx === null) {
+ if (!this.dataset.get_id_index(state.id)) {
this.dataset.ids.push(state.id);
- this.dataset.index = this.dataset.ids.length - 1;
}
- this.do_show();
+ this.dataset.select_id(state.id);
+ if (warm) {
+ this.do_show();
+ }
}
},
@@ -163,7 +173,8 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
on_record_loaded: function(record) {
var self = this, set_values = [];
if (!record) {
- throw new Error("Form: No record received");
+ this.do_warn("Form", "The record could not be found in the database.", true);
+ return $.Deferred().reject();
}
this.datarecord = record;
@@ -313,7 +324,9 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
var self = this;
return this.on_change_mutex.exec(function() {
try {
+ var response = {}, can_process_onchange = $.Deferred();
processed = processed || [];
+ processed.push(widget.name);
var on_change = widget.node.attrs.on_change;
if (on_change) {
var change_spec = self.parse_on_change(on_change, widget);
@@ -322,18 +335,60 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
url: '/web/dataset/onchange',
async: false
};
- return self.rpc(ajax, {
+ can_process_onchange = self.rpc(ajax, {
model: self.dataset.model,
method: change_spec.method,
args: [(self.datarecord.id == null ? [] : [self.datarecord.id])].concat(change_spec.args),
context_id: change_spec.context_index == undefined ? null : change_spec.context_index + 1
- }).pipe(function(response) {
- return self.on_processed_onchange(response, processed);
+ }).then(function(r) {
+ _.extend(response, r);
});
} else {
console.warn("Wrong on_change format", on_change);
}
}
+ // fail if onchange failed
+ if (can_process_onchange.isRejected()) {
+ return can_process_onchange;
+ }
+
+ if (widget.field['change_default']) {
+ var fieldname = widget.name, value;
+ if (response.value && (fieldname in response.value)) {
+ // Use value from onchange if onchange executed
+ value = response.value[fieldname];
+ } else {
+ // otherwise get form value for field
+ value = self.fields[fieldname].get_on_change_value();
+ }
+ var condition = fieldname + '=' + value;
+
+ if (value) {
+ can_process_onchange = self.rpc({
+ url: '/web/dataset/call',
+ async: false
+ }, {
+ model: 'ir.values',
+ method: 'get_defaults',
+ args: [self.model, condition]
+ }).then(function (results) {
+ if (!results.length) { return; }
+ if (!response.value) {
+ response.value = {};
+ }
+ for(var i=0; i<results.length; ++i) {
+ // [whatever, key, value]
+ var triplet = results[i];
+ response.value[triplet[1]] = triplet[2];
+ }
+ });
+ }
+ }
+ if (can_process_onchange.isRejected()) {
+ return can_process_onchange;
+ }
+
+ return self.on_processed_onchange(response, processed);
} catch(e) {
console.error(e);
return $.Deferred().reject();
@@ -350,11 +405,10 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
// If field is not defined in the view, just ignore it
if (field) {
var value = result.value[f];
- processed.push(field.name);
if (field.get_value() != value) {
field.set_value(value);
field.dirty = true;
- if (_.indexOf(processed, field.name) < 0) {
+ if (!_.contains(processed, field.name)) {
this.do_onchange(field, processed);
}
}
@@ -515,10 +569,10 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
} else {
this.datarecord.id = r.result;
if (!prepend_on_create) {
- this.dataset.ids.push(this.datarecord.id);
+ this.dataset.alter_ids(this.dataset.ids.concat([this.datarecord.id]));
this.dataset.index = this.dataset.ids.length - 1;
} else {
- this.dataset.ids.unshift(this.datarecord.id);
+ this.dataset.alter_ids([this.datarecord.id].concat(this.dataset.ids));
this.dataset.index = 0;
}
this.do_update_pager();
@@ -587,6 +641,82 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
},
sidebar_context: function () {
return this.do_save().pipe(_.bind(function() {return this.get_fields_values();}, this));
+ },
+ open_defaults_dialog: function () {
+ var self = this;
+ var fields = _.chain(this.fields)
+ .map(function (field, name) {
+ var value = field.get_value();
+ // ignore fields which are empty, invisible, readonly, o2m
+ // or m2m
+ if (!value
+ || field.invisible
+ || field.readonly
+ || field.field.type === 'one2many'
+ || field.field.type === 'many2many') {
+ return false;
+ }
+ var displayed;
+ switch(field.field.type) {
+ case 'selection':
+ displayed = _(field.values).find(function (option) {
+ return option[0] === value;
+ })[1];
+ break;
+ case 'many2one':
+ displayed = field.value[1] || value;
+ break;
+ default:
+ displayed = value;
+ }
+
+ return {
+ name: name,
+ string: field.string,
+ value: value,
+ displayed: displayed,
+ // convert undefined to false
+ change_default: !!field.field.change_default
+ }
+ })
+ .compact()
+ .sortBy(function (field) { return field.string; })
+ .value();
+ var conditions = _.chain(fields)
+ .filter(function (field) { return field.change_default; })
+ .value();
+
+ var d = new openerp.web.Dialog(this, {
+ title: _t("Set Default"),
+ args: {
+ fields: fields,
+ conditions: conditions
+ },
+ buttons: [
+ {text: _t("Close"), click: function () { d.close(); }},
+ {text: _t("Save default"), click: function () {
+ var $defaults = d.$element.find('#formview_default_fields');
+ var field_to_set = $defaults.val();
+ if (!field_to_set) {
+ $defaults.parent().addClass('invalid');
+ return;
+ }
+ var condition = d.$element.find('#formview_default_conditions').val(),
+ all_users = d.$element.find('#formview_default_all').is(':checked');
+ new openerp.web.DataSet(self, 'ir.values').call(
+ 'set_default', [
+ self.dataset.model,
+ field_to_set,
+ self.fields[field_to_set].get_value(),
+ all_users,
+ false,
+ condition || false
+ ]).then(function () { d.close(); });
+ }}
+ ]
+ });
+ d.template = 'FormView.set_default';
+ d.open();
}
});
openerp.web.FormDialog = openerp.web.Dialog.extend({
@@ -692,13 +822,17 @@ openerp.web.form.compute_domain = function(expr, fields) {
stack.push(!top);
continue;
default:
- throw new Error('Unknown domain operator ' + ex);
+ throw new Error(_.str.sprintf(
+ _t("Unknown operator %s in domain %s"),
+ ex, JSON.stringify(expr)));
}
}
var field = fields[ex[0]];
if (!field) {
- throw new Error("Domain references unknown field : " + ex[0]);
+ throw new Error(_.str.sprintf(
+ _t("Unknown field %s in domain %s"),
+ ex[0], JSON.stringify(expr)));
}
var field_value = field.get_value ? fields[ex[0]].get_value() : fields[ex[0]].value;
var op = ex[1];
@@ -734,7 +868,9 @@ openerp.web.form.compute_domain = function(expr, fields) {
stack.push(!_(val).contains(field_value));
break;
default:
- console.warn("Unsupported operator in modifiers :", op);
+ console.warn(
+ _t("Unsupported operator %s in domain %s"),
+ op, JSON.stringify(expr));
}
}
return _.all(stack, _.identity);
@@ -790,7 +926,7 @@ openerp.web.form.Widget = openerp.web.OldWidget.extend(/** @lends openerp.web.fo
},
stop: function() {
this._super.apply(this, arguments);
- $('div.tipsy').stop().remove();
+ $.fn.tipsy.clear();
},
process_modifiers: function() {
var compute_domain = openerp.web.form.compute_domain;
@@ -821,7 +957,7 @@ openerp.web.form.Widget = openerp.web.OldWidget.extend(/** @lends openerp.web.fo
debug: openerp.connection.debug,
widget: widget
})},
- gravity: $.fn.tipsy.autoNS,
+ gravity: $.fn.tipsy.autoBounds(50, 'nw'),
html: true,
opacity: 0.85,
trigger: 'hover'
@@ -1151,6 +1287,11 @@ openerp.web.form.WidgetLabel = openerp.web.form.Widget.extend({
this._super(view, node);
+ if (this.node.tag == 'label' && !this.string && this.node.children.length) {
+ this.string = this.node.children[0];
+ this.align = 'left';
+ }
+
if (this.node.tag == 'label' && (this.align === 'left' || this.node.attrs.colspan || (this.string && this.string.length > 32))) {
this.template = "WidgetParagraph";
this.colspan = parseInt(this.node.attrs.colspan || 1, 10);
@@ -1159,6 +1300,8 @@ openerp.web.form.WidgetLabel = openerp.web.form.Widget.extend({
if (isNaN(parseFloat(this.node.attrs.align))) {
this.align = 'left';
}
+
+ this.multilines = this.string && _.str.lines(this.string).length > 1;
} else {
this.colspan = 1;
this.width = '1%';
@@ -1222,6 +1365,7 @@ openerp.web.form.Field = openerp.web.form.Widget.extend(/** @lends openerp.web.f
this._super.apply(this, arguments);
if (this.field.translate) {
this.view.translatable_fields.push(this);
+ this.$element.addClass('oe_form_field_translatable');
this.$element.find('.oe_field_translate').click(this.on_translate);
}
if (this.nolabel && openerp.connection.debug) {
@@ -1318,7 +1462,7 @@ openerp.web.form.FieldChar = openerp.web.form.Field.extend({
},
update_dom: function() {
this._super.apply(this, arguments);
- this.$element.find('input').prop('disabled', this.readonly);
+ this.$element.find('input').prop('readonly', this.readonly);
},
set_value_from_ui: function() {
this.value = openerp.web.parse_value(this.$element.find('input').val(), this);
@@ -1341,7 +1485,7 @@ openerp.web.form.FieldChar = openerp.web.form.Field.extend({
openerp.web.form.FieldID = openerp.web.form.FieldChar.extend({
update_dom: function() {
this._super.apply(this, arguments);
- this.$element.find('input').prop('disabled', true);
+ this.$element.find('input').prop('readonly', true);
}
});
@@ -1419,7 +1563,7 @@ openerp.web.DateTimeWidget = openerp.web.OldWidget.extend({
showButtonPanel: true
});
this.$element.find('img.oe_datepicker_trigger').click(function() {
- if (!self.readonly) {
+ if (!self.readonly && !self.picker('widget').is(':visible')) {
self.picker('setDate', self.value ? openerp.web.auto_str_to_date(self.value) : new Date());
self.$input_picker.show();
self.picker('show');
@@ -1449,7 +1593,7 @@ openerp.web.DateTimeWidget = openerp.web.OldWidget.extend({
},
set_readonly: function(readonly) {
this.readonly = readonly;
- this.$input.prop('disabled', this.readonly);
+ this.$input.prop('readonly', this.readonly);
this.$element.find('img.oe_datepicker_trigger').toggleClass('oe_input_icon_disabled', readonly);
},
is_valid: function(required) {
@@ -1538,7 +1682,7 @@ openerp.web.form.FieldText = openerp.web.form.Field.extend({
},
update_dom: function() {
this._super.apply(this, arguments);
- this.$element.find('textarea').prop('disabled', this.readonly);
+ this.$element.find('textarea').prop('readonly', this.readonly);
},
set_value_from_ui: function() {
this.value = openerp.web.parse_value(this.$element.find('textarea').val(), this);
@@ -1896,7 +2040,12 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({
self.last_search = data;
// possible selections for the m2o
var values = _.map(data, function(x) {
- return {label: $('<span />').text(x[1]).html(), name:x[1], id:x[0]};
+ return {
+ label: _.str.escapeHTML(x[1]),
+ value:x[1],
+ name:x[1],
+ id:x[0]
+ };
});
// search more... if more results that max
@@ -2050,7 +2199,7 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({
},
update_dom: function() {
this._super.apply(this, arguments);
- this.$input.prop('disabled', this.readonly);
+ this.$input.prop('readonly', this.readonly);
}
});
@@ -2172,7 +2321,7 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({
this.viewmanager = new openerp.web.ViewManager(this, this.dataset, views, {});
this.viewmanager.template = 'One2Many.viewmanager';
- this.viewmanager.registry = openerp.web.views.clone({
+ this.viewmanager.registry = openerp.web.views.extend({
list: 'openerp.web.form.One2ManyListView',
form: 'openerp.web.form.One2ManyFormView',
page: 'openerp.web.PageView'
@@ -2250,13 +2399,13 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({
obj['id'] = _.uniqueId(self.dataset.virtual_id_prefix);
obj.defaults = {};
self.dataset.to_create.push(obj);
- self.dataset.cache.push(_.clone(obj));
+ self.dataset.cache.push(_.extend(_.clone(obj), {values: _.clone(command[2])}));
ids.push(obj.id);
return;
case commands.UPDATE:
obj['id'] = command[1];
self.dataset.to_write.push(obj);
- self.dataset.cache.push(_.clone(obj));
+ self.dataset.cache.push(_.extend(_.clone(obj), {values: _.clone(command[2])}));
ids.push(obj.id);
return;
case commands.DELETE:
@@ -2689,7 +2838,7 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope
contexts, groupbys);
self.initial_ids = undefined;
} else {
- self.do_search(domains.concat([self.domain]), contexts, groupbys);
+ self.do_search(domains.concat([self.domain]), contexts.concat(self.context), groupbys);
}
});
this.searchview.on_loaded.add_last(function () {
@@ -3046,10 +3195,19 @@ openerp.web.form.FieldBinary = openerp.web.form.Field.extend({
on_file_uploaded_and_valid: function(size, name, content_type, file_base64) {
},
on_save_as: function() {
- var url = '/web/binary/saveas?session_id=' + this.session.session_id + '&model=' +
- this.view.dataset.model +'&id=' + (this.view.datarecord.id || '') + '&field=' + this.name +
- '&filename_field=' + (this.node.attrs.filename || '') + '&t=' + (new Date().getTime());
- window.open(url);
+ $.blockUI();
+ this.session.get_file({
+ url: '/web/binary/saveas_ajax',
+ data: {data: JSON.stringify({
+ model: this.view.dataset.model,
+ id: (this.view.datarecord.id || ''),
+ field: this.name,
+ filename_field: (this.node.attrs.filename || ''),
+ context: this.view.dataset.get_context()
+ })},
+ complete: $.unblockUI,
+ error: openerp.webclient.crashmanager.on_rpc_error
+ });
},
on_clear: function() {
if (this.value !== false) {
@@ -3066,7 +3224,7 @@ openerp.web.form.FieldBinaryFile = openerp.web.form.FieldBinary.extend({
update_dom: function() {
this._super.apply(this, arguments);
this.$element.find('.oe-binary-file-set, .oe-binary-file-clear').toggle(!this.readonly);
- this.$element.find('input[type=text]').prop('disabled', this.readonly);
+ this.$element.find('input[type=text]').prop('readonly', this.readonly);
},
set_value: function(value) {
this._super.apply(this, arguments);
diff --git a/addons/web/static/src/js/view_list.js b/addons/web/static/src/js/view_list.js
index f9f845f889c..eb13bbd927b 100644
--- a/addons/web/static/src/js/view_list.js
+++ b/addons/web/static/src/js/view_list.js
@@ -97,7 +97,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
this._limit = (this.options.limit
|| this.defaults.limit
|| (this.widget_parent.action || {}).limit
- || null);
+ || 80);
}
return this._limit;
},
@@ -246,7 +246,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
self.page = 0; break;
case 'last':
self.page = Math.floor(
- self.dataset.ids.length / self.limit());
+ self.dataset.size() / self.limit());
break;
case 'next':
self.page += 1; break;
@@ -293,7 +293,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
this.dataset.ids = dataset.ids;
var limit = this.limit(),
- total = dataset.ids.length,
+ total = dataset.size(),
first = (this.page * limit),
last;
if (!limit || (total - first) < limit) {
@@ -451,6 +451,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
*/
reload_content: function () {
var self = this;
+ self.$element.find('.all-record-selector').prop('checked', false);
this.records.reset();
var reloaded = $.Deferred();
this.$element.find('.oe-listview-content').append(
@@ -475,7 +476,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
return this.reload_content();
},
- do_load_state: function(state) {
+ do_load_state: function(state, warm) {
var reload = false;
if (state.page && this.page !== state.page) {
this.page = state.page;
@@ -1316,11 +1317,11 @@ openerp.web.ListView.Groups = openerp.web.Class.extend( /** @lends openerp.web.L
if (!self.datagroup.openable) {
view.configure_pager(dataset);
} else {
- if (dataset.ids.length == records.length) {
+ if (dataset.size() == records.length) {
// only one page
self.$row.find('td.oe-group-pagination').empty();
} else {
- var pages = Math.ceil(dataset.ids.length / limit);
+ var pages = Math.ceil(dataset.size() / limit);
self.$row
.find('.oe-pager-state')
.text(_.str.sprintf(_t("%(page)d/%(page_count)d"), {
diff --git a/addons/web/static/src/js/view_list_editable.js b/addons/web/static/src/js/view_list_editable.js
index 4f6a1531c7a..c7222455889 100644
--- a/addons/web/static/src/js/view_list_editable.js
+++ b/addons/web/static/src/js/view_list_editable.js
@@ -386,7 +386,7 @@ openerp.web.list_editable = function (openerp) {
template: 'ListView.row.frame'
});
var form_widgets = openerp.web.form.widgets;
- openerp.web.list.form.widgets = form_widgets.clone({
+ openerp.web.list.form.widgets = form_widgets.extend({
'frame': 'openerp.web.list.form.WidgetFrame'
});
// All form widgets inherit a problematic behavior from
@@ -395,7 +395,7 @@ openerp.web.list_editable = function (openerp) {
// former should completely remove the cell. We need to override update_dom
// on all widgets since we can't just hit on widget itself (I think)
var list_form_widgets = openerp.web.list.form.widgets;
- _(list_form_widgets.map).each(function (widget_path, key) {
+ _(form_widgets.map).each(function (widget_path, key) {
if (key === 'frame') { return; }
var new_path = 'openerp.web.list.form.' + key;
diff --git a/addons/web/static/src/js/view_page.js b/addons/web/static/src/js/view_page.js
index d5c37419764..46bd1cf2485 100644
--- a/addons/web/static/src/js/view_page.js
+++ b/addons/web/static/src/js/view_page.js
@@ -92,17 +92,22 @@ openerp.web.page = function (openerp) {
openerp.web.page.FieldURIReadonly = openerp.web.page.FieldCharReadonly.extend({
template: 'FieldURI.readonly',
scheme: null,
+ format_value: function (value) {
+ return value;
+ },
set_value: function (value) {
- var displayed = this._super.apply(this, arguments);
this.$element.find('a')
- .attr('href', this.scheme + ':' + displayed)
- .text(displayed);
+ .attr('href', this.scheme + ':' + value)
+ .text(this.format_value(value));
}
});
openerp.web.page.FieldEmailReadonly = openerp.web.page.FieldURIReadonly.extend({
scheme: 'mailto'
});
openerp.web.page.FieldUrlReadonly = openerp.web.page.FieldURIReadonly.extend({
+ format_value: function (value) {
+ return value.slice(2);
+ },
set_value: function (value) {
var s = /(\w+):(.+)/.exec(value);
if (!s || !(s[1] === 'http' || s[1] === 'https')) { return; }
@@ -242,7 +247,7 @@ openerp.web.page = function (openerp) {
}
}
});
- openerp.web.page.readonly = openerp.web.form.widgets.clone({
+ openerp.web.page.readonly = openerp.web.form.widgets.extend({
'frame': 'openerp.web.page.WidgetFrameReadonly',
'char': 'openerp.web.page.FieldCharReadonly',
'id': 'openerp.web.page.FieldCharReadonly',
diff --git a/addons/web/static/src/js/views.js b/addons/web/static/src/js/views.js
index 9f871d8509a..56997190bc1 100644
--- a/addons/web/static/src/js/views.js
+++ b/addons/web/static/src/js/views.js
@@ -57,7 +57,7 @@ session.web.ActionManager = session.web.OldWidget.extend({
this.widget_parent.do_push_state(state);
}
},
- do_load_state: function(state) {
+ do_load_state: function(state, warm) {
var self = this,
action_loaded;
if (state.action_id) {
@@ -76,6 +76,15 @@ session.web.ActionManager = session.web.OldWidget.extend({
views: [[false, 'page'], [false, 'form']]
};
action_loaded = this.do_action(action);
+ } else if (state.sa) {
+ // load session action
+ var self = this;
+ this.null_action();
+ action_loaded = this.rpc('/web/session/get_session_action', {key: state.sa}).pipe(function(action) {
+ if (action) {
+ return self.do_action(action);
+ }
+ });
} else if (state.client_action) {
this.null_action();
this.ir_actions_client(state.client_action);
@@ -83,7 +92,7 @@ session.web.ActionManager = session.web.OldWidget.extend({
$.when(action_loaded || null).then(function() {
if (self.inner_viewmanager) {
- self.inner_viewmanager.do_load_state(state);
+ self.inner_viewmanager.do_load_state(state, warm);
}
});
},
@@ -593,16 +602,6 @@ session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepner
width: '95%'}, $root).open();
});
break;
- case 'customize_object':
- this.rpc('/web/dataset/search_read', {
- model: 'ir.model',
- fields: ['id'],
- domain: [['model', '=', this.dataset.model]]
- }, function (result) {
- self.do_edit_resource('ir.model', result.ids[0], {
- name : _t("Customize Object") });
- });
- break;
case 'manage_views':
if (current_view.fields_view && current_view.fields_view.arch) {
var view_editor = new session.web.ViewEditor(current_view, current_view.$element, this.dataset, current_view.fields_view.arch);
@@ -686,7 +685,7 @@ session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepner
this.widget_parent.do_push_state(state);
}
},
- do_load_state: function(state) {
+ do_load_state: function(state, warm) {
var self = this,
defs = [];
if (state.view_type && state.view_type !== this.active_view) {
@@ -698,7 +697,7 @@ session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepner
}
$.when(defs).then(function() {
- self.views[self.active_view].controller.do_load_state(state);
+ self.views[self.active_view].controller.do_load_state(state, warm);
});
},
shortcut_check : function(view) {
@@ -756,16 +755,21 @@ session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepner
var $logs_list = $logs.find('ul').empty();
$logs.toggleClass('oe-has-more', log_records.length > cutoff);
_(log_records.reverse()).each(function (record) {
+ var context = {};
+ if (record.context) {
+ try { context = py.eval(record.context).toJSON(); }
+ catch (e) { /* TODO: what do I do now? */ }
+ }
$(_.str.sprintf('<li><a href="#">%s</a></li>', record.name))
.appendTo($logs_list)
- .delegate('a', 'click', function (e) {
+ .delegate('a', 'click', function () {
self.do_action({
type: 'ir.actions.act_window',
res_model: record.res_model,
res_id: record.res_id,
// TODO: need to have an evaluated context here somehow
- //context: record.context,
- views: [[false, 'form']]
+ context: context,
+ views: [[context.view_id || false, 'form']]
});
return false;
});
@@ -812,10 +816,6 @@ session.web.Sidebar = session.web.OldWidget.extend({
}, {
label: _t("Export"),
callback: view.on_sidebar_export
- }, {
- label: _t("View Log"),
- callback: view.on_sidebar_view_log,
- classname: 'oe_hide oe_sidebar_view_log'
}
]);
},
@@ -854,18 +854,32 @@ session.web.Sidebar = session.web.OldWidget.extend({
}
return $section;
},
-
+ /**
+ * For each item added to the section:
+ *
+ * ``label``
+ * will be used as the item's name in the sidebar
+ *
+ * ``action``
+ * descriptor for the action which will be executed, ``action`` and
+ * ``callback`` should be exclusive
+ *
+ * ``callback``
+ * function to call when the item is clicked in the sidebar, called
+ * with the item descriptor as its first argument (so information
+ * can be stored as additional keys on the object passed to
+ * ``add_items``)
+ *
+ * ``classname`` (optional)
+ * ``@class`` set on the sidebar serialization of the item
+ *
+ * ``title`` (optional)
+ * will be set as the item's ``@title`` (tooltip)
+ *
+ * @param {String} section_code
+ * @param {Array<{label, action | callback[, classname][, title]}>} items
+ */
add_items: function(section_code, items) {
- // An item is a dictonary : {
- // label: label to be displayed for the link,
- // action: action to be launch when the link is clicked,
- // callback: a function to be executed when the link is clicked,
- // classname: optional dom class name for the line,
- // title: optional title for the link
- // }
- // Note: The item should have one action or/and a callback
- //
-
var self = this,
$section = this.add_section(_.str.titleize(section_code.replace('_', ' ')), section_code),
section_id = $section.attr('id');
@@ -1029,7 +1043,8 @@ session.web.TranslateDialog = session.web.Dialog.extend({
},
on_button_Save: function() {
var trads = {},
- self = this;
+ self = this,
+ trads_mutex = new $.Mutex();
self.$fields_form.find('.oe_trad_field.touched').each(function() {
var field = $(this).attr('name').split('-');
if (!trads[field[0]]) {
@@ -1042,9 +1057,10 @@ session.web.TranslateDialog = session.web.Dialog.extend({
_.each(data, function(value, field) {
self.view.fields[field].set_value(value);
});
- } else {
- self.view.dataset.write(self.view.datarecord.id, data, { 'lang': code });
}
+ trads_mutex.exec(function() {
+ return self.view.dataset.write(self.view.datarecord.id, data, { context : { 'lang': code } });
+ });
});
this.close();
},
@@ -1172,7 +1188,7 @@ session.web.View = session.web.Widget.extend(/** @lends session.web.View# */{
this.widget_parent.do_push_state(state);
}
},
- do_load_state: function(state) {
+ do_load_state: function(state, warm) {
},
/**
* Switches to a specific view type
@@ -1213,8 +1229,6 @@ session.web.View = session.web.Widget.extend(/** @lends session.web.View# */{
view_mode : "list"
});
},
- on_sidebar_view_log: function() {
- },
sidebar_context: function () {
return $.when();
},