diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index d3cb227d7d1..d93aef6526c 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -227,6 +227,8 @@ def module_installed_bypass_session(dbname): return sorted_modules def module_boot(req): + return [m for m in req.config.server_wide_modules if m in openerpweb.addons_manifest] + # TODO the following will be enabled once we separate the module code and translation loading serverside = [] dbside = [] for i in req.config.server_wide_modules: @@ -531,6 +533,20 @@ def parse_context(context, session): except ValueError: return common.nonliterals.Context(session, context) + +def _local_web_translations(trans_file): + messages = [] + try: + with open(trans_file) as t_file: + po = babel.messages.pofile.read_po(t_file) + except Exception: + return + for x in po: + if x.id and x.string and "openerp-web" in x.auto_comments: + messages.append({'id': x.id, 'string': x.string}) + return messages + + #---------------------------------------------------------- # OpenERP Web web Controllers #---------------------------------------------------------- @@ -576,6 +592,7 @@ class Home(openerpweb.Controller): def login(self, req, db, login, key): return login_and_redirect(req, db, login, key) + class WebClient(openerpweb.Controller): _cp_path = "/web/webclient" @@ -658,41 +675,53 @@ class WebClient(openerpweb.Controller): last_modified, checksum) @openerpweb.jsonrequest - def translations(self, req, mods, lang): - lang_model = req.session.model('res.lang') - ids = lang_model.search([("code", "=", lang)]) - if ids: - lang_obj = lang_model.read(ids[0], ["direction", "date_format", "time_format", - "grouping", "decimal_point", "thousands_sep"]) - else: - lang_obj = None + def bootstrap_translations(self, req, mods): + """ Load local translations from *.po files, as a temporary solution + until we have established a valid session. This is meant only + for translating the login page and db management chrome, using + the browser's language. """ + lang = req.httprequest.accept_languages.best or 'en' + # For performance reasons we only load a single translation, so for + # sub-languages (that should only be partially translated) we load the + # main language PO instead - that should be enough for the login screen. + if '-' in lang: # RFC2616 uses '-' separators for sublanguages + lang = lang.split('-')[0] - if "_" in lang: - separator = "_" - else: - separator = "@" - langs = lang.split(separator) - langs = [separator.join(langs[:x]) for x in range(1, len(langs) + 1)] - - transs = {} + translations_per_module = {} for addon_name in mods: - transl = {"messages":[]} - transs[addon_name] = transl addons_path = openerpweb.addons_manifest[addon_name]['addons_path'] - for l in langs: - f_name = os.path.join(addons_path, addon_name, "i18n", l + ".po") - if not os.path.exists(f_name): - continue - try: - with open(f_name) as t_file: - po = babel.messages.pofile.read_po(t_file) - except Exception: - continue - for x in po: - if x.id and x.string and "openerp-web" in x.auto_comments: - transl["messages"].append({'id': x.id, 'string': x.string}) - return {"modules": transs, - "lang_parameters": lang_obj} + f_name = os.path.join(addons_path, addon_name, "i18n", lang + ".po") + if not os.path.exists(f_name): + continue + translations_per_module[addon_name] = {'messages': _local_web_translations(f_name)} + + return {"modules": translations_per_module, + "lang_parameters": None} + + @openerpweb.jsonrequest + def translations(self, req, mods, lang): + res_lang = req.session.model('res.lang') + ids = res_lang.search([("code", "=", lang)]) + lang_params = None + if ids: + lang_params = res_lang.read(ids[0], ["direction", "date_format", "time_format", + "grouping", "decimal_point", "thousands_sep"]) + + # Regional languages (ll_CC) must inherit/override their parent lang (ll), but this is + # done server-side when the language is loaded, so we only need to load the user's lang. + ir_translation = req.session.model('ir.translation') + translations_per_module = {} + messages = ir_translation.search_read([('module','in',mods),('lang','=',lang), + ('comments','like','openerp-web'),('value','!=',False), + ('value','!=','')], + ['module','src','value','lang'], order='module') + for mod, msg_group in itertools.groupby(messages, key=operator.itemgetter('module')): + translations_per_module.setdefault(mod,{'messages':[]}) + translations_per_module[mod]['messages'].extend({'id': m['src'], + 'string': m['value']} \ + for m in msg_group) + return {"modules": translations_per_module, + "lang_parameters": lang_params} @openerpweb.jsonrequest def version_info(self, req): @@ -769,7 +798,7 @@ class Database(openerpweb.Controller): {'fileToken': int(token)} ) except xmlrpclib.Fault, e: - return simplejson.dumps([[],[{'error': e.faultCode, 'title': 'backup Database'}]]) + return simplejson.dumps([[],[{'error': e.faultCode, 'title': 'backup Database'}]]) @openerpweb.httprequest def restore(self, req, db_file, restore_pwd, new_db): @@ -1180,8 +1209,8 @@ class DataSet(openerpweb.Controller): def call_button(self, req, model, method, args, domain_id=None, context_id=None): action = self.call_common(req, model, method, args, domain_id, context_id) if isinstance(action, dict) and action.get('type') != '': - return {'result': clean_action(req, action)} - return {'result': False} + return clean_action(req, action) + return False @openerpweb.jsonrequest def exec_workflow(self, req, model, id, signal): @@ -1593,7 +1622,7 @@ class Action(openerpweb.Controller): action = req.session.model(action_type).read([action_id], False, ctx) if action: value = clean_action(req, action[0], do_not_eval) - return {'result': value} + return value @openerpweb.jsonrequest def run(self, req, action_id): diff --git a/addons/web/static/src/css/base.css b/addons/web/static/src/css/base.css index aa9d5eab36f..14496fe3522 100644 --- a/addons/web/static/src/css/base.css +++ b/addons/web/static/src/css/base.css @@ -1103,9 +1103,8 @@ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4) inset; } .openerp .oe_menu > li > .oe_active { - background: rgba(0, 0, 0, 0.2); + background: rgba(0, 0, 0, 0.3); text-shadow: black 0px 0px 3px; - font-weight: bold; -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4) inset; -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4) inset; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4) inset; @@ -1198,7 +1197,7 @@ color: white; padding: 2px 4px; margin: 1px 6px 0 0; - border: 1px solid lightGray; + border: 1px solid lightgrey; text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); -moz-border-radius: 4px; -webkit-border-radius: 4px; @@ -1223,7 +1222,7 @@ transform: scale(1.1); } .openerp .oe_secondary_submenu .oe_active { - border-top: 1px solid lightGray; + border-top: 1px solid lightgrey; border-bottom: 1px solid #dedede; text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); -moz-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.2), inset 0 -1px 3px rgba(40, 40, 40, 0.2); @@ -1865,6 +1864,13 @@ margin: 0; white-space: nowrap; } +.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_advanced li:first-child .searchview_extended_prop_or { + visibility: hidden; +} +.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_advanced .searchview_extended_prop_or { + opacity: 0.5; + margin-left: -14px; +} .openerp .oe_searchview .oe_searchview_drawer .oe_opened h4:before { content: "▾ "; position: relative; @@ -2117,7 +2123,7 @@ } .openerp .oe_form .oe_form_label_help[for] span, .openerp .oe_form .oe_form_label[for] span { font-size: 80%; - color: darkGreen; + color: darkgreen; vertical-align: top; position: relative; top: -4px; @@ -2206,16 +2212,12 @@ min-width: 60px; color: #1f1f1f; } -.openerp .oe_form textarea { - height: 32px; -} .openerp .oe_form input[readonly], .openerp .oe_form select[readonly], .openerp .oe_form textarea[readonly], .openerp .oe_form input[disabled], .openerp .oe_form select[disabled] { background: #e5e5e5 !important; color: #666666; } .openerp .oe_form textarea[disabled] { border: none; - border-left: 8px solid #eeeeee; padding-left: 8px; -moz-box-shadow: none; -webkit-box-shadow: none; @@ -2224,6 +2226,9 @@ -webkit-border-radius: 0px; border-radius: 0px; } +.openerp .oe_form textarea.oe_inline[disabled] { + border-left: 8px solid #eeeeee; +} .openerp .oe_form .oe_form_field_url button img { vertical-align: top; } @@ -2241,7 +2246,7 @@ .openerp .oe_form .oe_datepicker_root { display: inline-block; } -.openerp .oe_form .oe_form_required input, .openerp .oe_form .oe_form_required select, .openerp .oe_form .oe_form_required textarea { +.openerp .oe_form .oe_form_required input:not([disabled]):not([readonly]), .openerp .oe_form .oe_form_required select:not([disabled]):not([readonly]), .openerp .oe_form .oe_form_required textarea:not([disabled]):not([readonly]) { background-color: #d2d2ff !important; } .openerp .oe_form .oe_form_invalid input, .openerp .oe_form .oe_form_invalid select, .openerp .oe_form .oe_form_invalid textarea { @@ -2279,7 +2284,7 @@ position: relative; overflow: hidden; } -.openerp .oe_form .oe_form_field_html { +.openerp .oe_form .oe_form_embedded_html { position: relative; width: 600px; margin-left: 130px; diff --git a/addons/web/static/src/css/base.sass b/addons/web/static/src/css/base.sass index c56c4d6a3e9..784f93d94cf 100644 --- a/addons/web/static/src/css/base.sass +++ b/addons/web/static/src/css/base.sass @@ -891,9 +891,8 @@ $sheet-max-width: 860px color: white @include box-shadow(0 1px 2px rgba(0,0,0,0.4) inset) > .oe_active - background: rgba(0,0,0,0.2) + background: rgba(0,0,0,0.30) text-shadow: black 0px 0px 3px - font-weight: bold @include box-shadow(0 1px 2px rgba(0,0,0,0.4) inset) .oe_user_menu @@ -1482,6 +1481,11 @@ $sheet-max-width: 860px list-style: none margin: 0 white-space: nowrap + &:first-child .searchview_extended_prop_or + visibility: hidden + .searchview_extended_prop_or + opacity: 0.5 + margin-left: -14px .oe_opened h4:before content: "▾ " @@ -1753,17 +1757,16 @@ $sheet-max-width: 860px background: white min-width: 60px color: #1f1f1f - textarea - height: 32px input[readonly], select[readonly], textarea[readonly], input[disabled], select[disabled] background: #E5E5E5 !important color: #666 textarea[disabled] border: none - border-left: 8px solid #eee padding-left: 8px @include box-shadow(none) @include radius(0px) + textarea.oe_inline[disabled] + border-left: 8px solid #eee .oe_form_field_url button img vertical-align: top .oe_form_field_date, @@ -1776,8 +1779,8 @@ $sheet-max-width: 860px display: none .oe_datepicker_root display: inline-block - .oe_form_required - input, select, textarea + .oe_form_required + input:not([disabled]):not([readonly]), select:not([disabled]):not([readonly]), textarea:not([disabled]):not([readonly]) background-color: #D2D2FF !important .oe_form_invalid input, select, textarea @@ -1808,7 +1811,7 @@ $sheet-max-width: 860px > div position: relative overflow: hidden - .oe_form_field_html + .oe_form_embedded_html position: relative width: 600px margin-left: 130px diff --git a/addons/web/static/src/js/chrome.js b/addons/web/static/src/js/chrome.js index bed4a159535..80693d26e46 100644 --- a/addons/web/static/src/js/chrome.js +++ b/addons/web/static/src/js/chrome.js @@ -146,8 +146,8 @@ instance.web.Dialog = instance.web.Widget.extend({ this.$buttons = $('
'); this.$el.dialog("widget").append(this.$buttons); } - var res = this.start(); this.dialog_inited = true; + var res = this.start(); return res; }, close: function() { @@ -392,7 +392,7 @@ instance.web.DatabaseManager = instance.web.Widget.extend({ do_create: function(form) { var self = this; var fields = $(form).serializeArray(); - self.rpc("/web/database/create", {'fields': fields}, function(result) { + self.rpc("/web/database/create", {'fields': fields}).then(function(result) { var form_obj = self.to_object(fields); var client_action = { type: 'ir.actions.client', @@ -418,7 +418,7 @@ instance.web.DatabaseManager = instance.web.Widget.extend({ if (!db || !confirm("Do you really want to delete the database: " + db + " ?")) { return; } - self.rpc("/web/database/drop", {'fields': fields}, function(result) { + self.rpc("/web/database/drop", {'fields': fields}).then(function(result) { if (result.error) { self.display_error(result); return; @@ -481,7 +481,7 @@ instance.web.DatabaseManager = instance.web.Widget.extend({ var self = this; self.rpc("/web/database/change_password", { 'fields': $(form).serializeArray() - }, function(result) { + }).then(function(result) { if (result.error) { self.display_error(result); return; @@ -660,7 +660,7 @@ instance.web.ChangePassword = instance.web.Widget.extend({ submitHandler: function (form) { self.rpc("/web/session/change_password",{ 'fields': $(form).serializeArray() - }, function(result) { + }).then(function(result) { if (result.error) { self.display_error(result); return; @@ -886,8 +886,8 @@ instance.web.UserMenu = instance.web.Widget.extend({ var self = this; if (!this.getParent().has_uncommitted_changes()) { self.rpc("/web/action/load", { action_id: "base.action_res_users_my" }, function(result) { - result.result.res_id = instance.session.uid; - self.getParent().action_manager.do_action(result.result); + result.res_id = instance.session.uid; + self.getParent().action_manager.do_action(result); }); } }, @@ -1121,7 +1121,7 @@ instance.web.WebClient = instance.web.Client.extend({ var self = this; return this.rpc("/web/action/load", { action_id: options.action_id }) .pipe(function (result) { - var action = result.result; + var action = result; if (options.needaction) { action.context.search_default_needaction_pending = true; } @@ -1165,8 +1165,8 @@ instance.web.EmbeddedClient = instance.web.Client.extend({ var self = this; return $.when(this._super()).pipe(function() { return instance.session.session_authenticate(self.dbname, self.login, self.key, true).pipe(function() { - return self.rpc("/web/action/load", { action_id: self.action_id }, function(result) { - var action = result.result; + return self.rpc("/web/action/load", { action_id: self.action_id }).then(function(result) { + var action = result; action.flags = _.extend({ //views_switcher : false, search_view : false, diff --git a/addons/web/static/src/js/corelib.js b/addons/web/static/src/js/corelib.js index 62ac48f0c61..ff193605033 100644 --- a/addons/web/static/src/js/corelib.js +++ b/addons/web/static/src/js/corelib.js @@ -820,9 +820,10 @@ instance.web.Widget = instance.web.Class.extend(instance.web.WidgetMixin, { * the action manager can be found amongst the ancestors of the current widget. * If that's not the case this method will simply return `false`. */ - do_action: function(action, on_finished) { - if (this.getParent()) { - return this.getParent().do_action(action, on_finished); + do_action: function() { + var parent = this.getParent(); + if (parent) { + return parent.do_action.apply(parent, arguments); } return false; }, @@ -841,7 +842,7 @@ instance.web.Widget = instance.web.Class.extend(instance.web.WidgetMixin, { rpc: function(url, data, success, error) { var def = $.Deferred().then(success, error); var self = this; - instance.session.rpc(url, data). then(function() { + instance.session.rpc(url, data).then(function() { if (!self.isDestroyed()) def.resolve.apply(def, arguments); }, function() { @@ -1299,7 +1300,7 @@ instance.web.JsonRPC = instance.web.CallbackEnabled.extend({ * @param {Function} error_callback function to execute on RPC call failure * @returns {jQuery.Deferred} jquery-provided ajax deferred */ - rpc: function(url, params, success_callback, error_callback) { + rpc: function(url, params) { var self = this; // url can be an $.ajax option object if (_.isString(url)) { @@ -1316,8 +1317,6 @@ instance.web.JsonRPC = instance.web.CallbackEnabled.extend({ }; var deferred = $.Deferred(); this.trigger('request', url, payload); - var aborter = params.aborter; - delete params.aborter; var request = this.rpc_function(url, payload).then( function (response, textStatus, jqXHR) { self.trigger('response', response); @@ -1341,16 +1340,6 @@ instance.web.JsonRPC = instance.web.CallbackEnabled.extend({ }; deferred.reject(error, $.Event()); }); - if (aborter) { - aborter.abort_last = function () { - if (!(request.isResolved() || request.isRejected())) { - deferred.fail(function (error, event) { - event.preventDefault(); - }); - request.abort(); - } - }; - } // Allow deferred user to disable on_rpc_error in fail deferred.fail(function() { deferred.fail(function(error, event) { @@ -1358,7 +1347,7 @@ instance.web.JsonRPC = instance.web.CallbackEnabled.extend({ self.on_rpc_error(error, event); } }); - }).then(success_callback, error_callback).promise(); + }); return deferred; }, /** diff --git a/addons/web/static/src/js/coresetup.js b/addons/web/static/src/js/coresetup.js index ea160d6faee..1f1112df2f2 100644 --- a/addons/web/static/src/js/coresetup.js +++ b/addons/web/static/src/js/coresetup.js @@ -19,9 +19,9 @@ instance.web.Session = instance.web.JsonRPC.extend( /** @lends instance.web.Sess this.name = instance._session_id; this.qweb_mutex = new $.Mutex(); }, - rpc: function(url, params, success_callback, error_callback) { + rpc: function(url, params) { params.session_id = this.session_id; - return this._super(url, params, success_callback, error_callback); + return this._super(url, params); }, /** * Setup a sessionm @@ -57,7 +57,12 @@ instance.web.Session = instance.web.JsonRPC.extend( /** @lends instance.web.Sess if(self.session_is_valid()) { return deferred.pipe(function() { return self.load_modules(); }); } - return deferred; + return $.when( + deferred, + self.rpc('/web/webclient/bootstrap_translations', {mods: instance._modules}).pipe(function(trans) { + instance.web._t.database.set_bundle(trans); + }) + ); }); }, /** @@ -537,13 +542,9 @@ instance.web.qweb.preprocess_node = function() { if (translation && translation.value === 'off') { return; } - var ts = _.str.trim(this.node.data); - if (ts.length === 0) { - return; - } - var tr = instance.web._t(ts); - if (tr !== ts) { - this.node.data = tr; + var match = /^(\s*)(.+?)(\s*)$/.exec(this.node.data); + if (match) { + this.node.data = match[1] + instance.web._t(match[2]) + match[3]; } break; case 1: @@ -600,8 +601,7 @@ var messages_by_seconds = function() { [120, _t("Don't leave yet,
it's still loading...")], [300, _t("You may not believe it,
but the application is actually loading...")], [420, _t("Take a minute to get a coffee,
because it's loading...")], - [600, _t("It's loading...
By the way, did you tried the kitten mode?")], - [3600, _t("Maybe you should consider pressing F5...")], + [3600, _t("Maybe you should consider reloading the application by pressing F5...")], ]; }; diff --git a/addons/web/static/src/js/data.js b/addons/web/static/src/js/data.js index 2b82e618203..220d42737da 100644 --- a/addons/web/static/src/js/data.js +++ b/addons/web/static/src/js/data.js @@ -586,26 +586,8 @@ instance.web.DataSet = instance.web.CallbackEnabled.extend({ * @param {Function} error_callback * @returns {$.Deferred} */ - call: function (method, args, callback, error_callback) { - return this._model.call(method, args).then(callback, error_callback); - }, - /** - * Calls an arbitrary method, with more crazy - * - * @param {String} method - * @param {Array} [args] - * @param {Number} [domain_index] index of a domain to evaluate in the args array - * @param {Number} [context_index] index of a context to evaluate in the args array - * @returns {$.Deferred} - */ - call_and_eval: function (method, args, domain_index, context_index) { - return instance.session.rpc('/web/dataset/call', { - model: this.model, - method: method, - domain_id: domain_index == undefined ? null : domain_index, - context_id: context_index == undefined ? null : context_index, - args: args || [] - }); + call: function (method, args) { + return this._model.call(method, args); }, /** * Calls a button method, usually returning some sort of action diff --git a/addons/web/static/src/js/data_export.js b/addons/web/static/src/js/data_export.js index 29aea6362f7..b4beb4c3a58 100644 --- a/addons/web/static/src/js/data_export.js +++ b/addons/web/static/src/js/data_export.js @@ -5,7 +5,15 @@ instance.web.DataExport = instance.web.Dialog.extend({ template: 'ExportTreeView', dialog_title: {toString: function () { return _t("Export Data"); }}, init: function(parent, dataset) { - this._super(parent); + var self = this; + options = { + buttons : [ + {text: _t("Close"), click: function() { self.close(); }}, + {text: _t("Export To File"), click: function() { self.on_click_export_data(); }} + ], + close: function(event, ui){ self.close();} + } + this._super(parent, options); this.records = {}; this.dataset = dataset; this.exports = new instance.web.DataSetSearch( @@ -14,13 +22,7 @@ instance.web.DataExport = instance.web.Dialog.extend({ start: function() { var self = this; this._super.apply(this, arguments); - this.open({ - buttons : [ - {text: _t("Close"), click: function() { self.close(); }}, - {text: _t("Export To File"), click: function() { self.on_click_export_data(); }} - ], - close: function(event, ui){ self.close();} - }); + this.open(); self.$el.removeClass('ui-dialog-content ui-widget-content'); self.$el.find('#add_field').click(function() { if ($('#field-tree-structure tr.ui-selected')) { @@ -49,7 +51,7 @@ instance.web.DataExport = instance.web.Dialog.extend({ self.rpc("/web/export/get_fields", { model: self.dataset.model, import_compat: Boolean(import_comp) - }, function (records) { + }).then(function (records) { got_fields.resolve(); self.on_show_data(records); }); @@ -57,7 +59,7 @@ instance.web.DataExport = instance.web.Dialog.extend({ return $.when( got_fields, - this.rpc('/web/export/formats', {}, this.do_setup_export_formats), + this.rpc('/web/export/formats', {}).then(this.do_setup_export_formats), this.show_exports_list()); }, do_setup_export_formats: function (formats) { @@ -91,7 +93,7 @@ instance.web.DataExport = instance.web.Dialog.extend({ self.$el.find('#fields_list option').remove(); var export_id = self.$el.find('#saved_export_list option:selected').val(); if (export_id) { - self.rpc('/web/export/namelist', {'model': self.dataset.model, export_id: parseInt(export_id)}, self.do_load_export_field); + self.rpc('/web/export/namelist', {'model': self.dataset.model, export_id: parseInt(export_id)}).then(self.do_load_export_field); } }); self.$el.find('#delete_export_list').click(function() { @@ -181,7 +183,7 @@ instance.web.DataExport = instance.web.Dialog.extend({ import_compat: Boolean(import_comp), parent_field_type : record['field_type'], exclude: exclude_fields - }, function(results) { + }).then(function(results) { record.loaded = true; self.on_show_data(results, record.id); }); diff --git a/addons/web/static/src/js/search.js b/addons/web/static/src/js/search.js index 01bc0c76c87..a5e1aaeb3ae 100644 --- a/addons/web/static/src/js/search.js +++ b/addons/web/static/src/js/search.js @@ -1494,7 +1494,7 @@ instance.web.search.ManyToOneField = instance.web.search.CharField.extend({ return $.when(facet_from(this, value)); } assert(value.length <= 1, - _("M2O search fields do not currently handle multiple default values")); + _t("M2O search fields do not currently handle multiple default values")); // there are many cases of {search_default_$m2ofield: [id]}, need // to handle this as if it were a single value. value = value[0]; @@ -1578,7 +1578,7 @@ instance.web.search.CustomFilters = instance.web.search.Input.extend({ $filter.unbind('click').click(function () { self.view.query.reset([{ - category: _("Custom Filter"), + category: _t("Custom Filter"), icon: 'M', field: { get_context: function () { return filter.context; }, @@ -1693,7 +1693,7 @@ instance.web.search.Advanced = instance.web.search.Input.extend({ }); return $.when( this._super(), - this.rpc("/web/searchview/fields_get", {model: this.view.model}, function(data) { + this.rpc("/web/searchview/fields_get", {model: this.view.model}).then(function(data) { self.fields = _.extend({ id: { string: 'ID', type: 'id' } }, data.fields); diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index bf6b869482b..b41039361c0 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -30,7 +30,7 @@ instance.web.form.FieldManagerMixin = { /** * Must return the asked field as in fields_get. */ - get_field: function(field_name) {}, + get_field_desc: function(field_name) {}, /** * Returns the current value of a field present in the view. See the get_value() method * method in FieldInterface for further information. @@ -44,6 +44,21 @@ instance.web.form.FieldManagerMixin = { @return (Deferred) Is resolved after all the values are setted. */ set_values: function(values) {}, + /** + Computes an OpenERP domain. + + @param (list) expression An OpenERP domain. + @return (boolean) The computed value of the domain. + */ + compute_domain: function(expression) {}, + /** + Builds an evaluation context for the resolution of the fields' contexts. Please note + the field are only supposed to use this context to evualuate their own, they should not + extend it. + + @return (CompoundContext) An OpenERP context. + */ + build_eval_context: function() {}, }; instance.web.views.add('form', 'instance.web.FormView'); @@ -1085,7 +1100,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM field.on('focused', null, this.proxy('widgetFocused')) .on('blurred', null, this.proxy('widgetBlurred')); - if (this.get_field(name).translate) { + if (this.get_field_desc(name).translate) { this.translatable_fields.push(field); } field.on('changed_value', this, function() { @@ -1103,12 +1118,34 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM } }); }, - get_field: function(field_name) { + get_field_desc: function(field_name) { return this.fields_view.fields[field_name]; }, get_field_value: function(field_name) { return this.fields[field_name].get_value(); }, + compute_domain: function(expression) { + return instance.web.form.compute_domain(expression, this.fields); + }, + _build_view_fields_values: function(blacklist) { + var a_dataset = this.dataset; + var fields_values = this.get_fields_values(blacklist); + var active_id = a_dataset.ids[a_dataset.index]; + _.extend(fields_values, { + active_id: active_id || false, + active_ids: active_id ? [active_id] : [], + active_model: a_dataset.model, + parent: {} + }); + if (a_dataset.parent_view) { + fields_values.parent = a_dataset.parent_view.get_fields_values([a_dataset.child_name]); + } + return fields_values; + }, + build_eval_context: function(blacklist) { + var a_dataset = this.dataset; + return new instance.web.CompoundContext(a_dataset.get_context(), this._build_view_fields_values(blacklist)); + }, }); /** @@ -1175,7 +1212,7 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt this.$form.appendTo(this.$target); - _.each(this.fields_to_init, function($elem) { + var ws = _.map(this.fields_to_init, function($elem) { var name = $elem.attr("name"); if (!self.fvg.fields[name]) { throw new Error("Field '" + name + "' specified in view could not be found."); @@ -1191,7 +1228,10 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt } self.alter_field(w); self.view.register_field(w, $elem.attr("name")); - w.replace($elem); + return [w, $elem]; + }); + _.each(ws, function(w) { + w[0].replace(w[1]); }); _.each(this.tags_to_init, function($elem) { var tag_name = $elem[0].tagName.toLowerCase(); @@ -1528,6 +1568,52 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt }, }); +/** + Welcome. + + If you read this documentation, it probably means that you were asked to use a form view widget outside of + a form view. Before going further, you must understand that those fields were never really created for + that usage. Don't think that this class will hold the answer to all your problems, at best it will allow + you to hack the system with more style. +*/ +instance.web.form.DefaultFieldManager = instance.web.Widget.extend({ + init: function(parent, eval_context) { + this._super(parent); + this.field_descs = {}; + this.eval_context = eval_context || {}; + this.set({ + display_invalid_fields: false, + actual_mode: 'create', + }); + }, + get_field_desc: function(field_name) { + if (this.field_descs[field_name] === undefined) { + this.field_descs[field_name] = { + string: field_name, + }; + } + return this.field_descs[field_name]; + }, + extend_field_desc: function(fields) { + var self = this; + _.each(fields, function(v, k) { + _.extend(self.get_field_desc(k), v); + }); + }, + get_field_value: function(field_name) { + return false; + }, + set_values: function(values) { + // nothing + }, + compute_domain: function(expression) { + return instance.web.form.compute_domain(expression, {}); + }, + build_eval_context: function() { + return new instance.web.CompoundContext(this.eval_context); + }, +}); + instance.web.form.FormDialog = instance.web.Dialog.extend({ init: function(parent, options, view_id, dataset) { this._super(parent, options); @@ -1641,9 +1727,7 @@ instance.web.form.InvisibilityChangerMixin = { this._ic_invisible_modifier = invisible_domain; this._ic_field_manager.on("view_content_has_changed", this, function() { var result = self._ic_invisible_modifier === undefined ? false : - instance.web.form.compute_domain( - self._ic_invisible_modifier, - self._ic_field_manager.fields); + self._ic_field_manager.compute_domain(self._ic_invisible_modifier); self.set({"invisible": result}); }); this.set({invisible: this._ic_invisible_modifier === true, force_invisible: false}); @@ -1694,19 +1778,22 @@ instance.web.form.FormWidget = instance.web.Widget.extend(instance.web.form.Invi */ init: function(field_manager, node) { this._super(field_manager); - this.view = field_manager; this.field_manager = field_manager; + if (this.field_manager instanceof instance.web.FormView) + this.view = this.field_manager; this.node = node; this.modifiers = JSON.parse(this.node.attrs.modifiers || '{}'); instance.web.form.InvisibilityChangerMixin.init.call(this, this.field_manager, this.modifiers.invisible); this.field_manager.on("view_content_has_changed", this, this.process_modifiers); - this.set({required: this.modifiers['required'] === true}); + this.set({ + required: false, + readonly: false, + }); // some events to make the property "effective_readonly" sync automatically with "readonly" and // "mode" on field_manager var self = this; - this.set({"readonly": this.modifiers['readonly'] === true}); var test_effective_readonly = function() { self.set({"effective_readonly": self.get("readonly") || self.field_manager.get("actual_mode") === "view"}); }; @@ -1715,6 +1802,7 @@ instance.web.form.FormWidget = instance.web.Widget.extend(instance.web.form.Invi test_effective_readonly.call(this); }, renderElement: function() { + this.process_modifiers(); this._super(); this.$el.addClass(this.node.attrs["class"] || ""); }, @@ -1737,12 +1825,11 @@ instance.web.form.FormWidget = instance.web.Widget.extend(instance.web.form.Invi }); }, process_modifiers: function() { - var compute_domain = instance.web.form.compute_domain; var to_set = {}; for (var a in this.modifiers) { if (!this.modifiers.hasOwnProperty(a)) { continue; } if (!_.include(["invisible"], a)) { - var val = compute_domain(this.modifiers[a], this.view.fields); + var val = this.field_manager.compute_domain(this.modifiers[a]); to_set[a] = val; } } @@ -1771,25 +1858,6 @@ instance.web.form.FormWidget = instance.web.Widget.extend(instance.web.form.Invi }, options || {}); $(trigger).tipsy(options); }, - _build_view_fields_values: function(blacklist) { - var a_dataset = this.view.dataset; - var fields_values = this.view.get_fields_values(blacklist); - var active_id = a_dataset.ids[a_dataset.index]; - _.extend(fields_values, { - active_id: active_id || false, - active_ids: active_id ? [active_id] : [], - active_model: a_dataset.model, - parent: {} - }); - if (a_dataset.parent_view) { - fields_values.parent = a_dataset.parent_view.get_fields_values([a_dataset.child_name]); - } - return fields_values; - }, - _build_eval_context: function(blacklist) { - var a_dataset = this.view.dataset; - return new instance.web.CompoundContext(a_dataset.get_context(), this._build_view_fields_values(blacklist)); - }, /** * Builds a new context usable for operations related to fields by merging * the fields'context with the action's context. @@ -1802,7 +1870,7 @@ instance.web.form.FormWidget = instance.web.Widget.extend(instance.web.form.Invi } if (v_context.__ref || true) { //TODO: remove true - var fields_values = this._build_eval_context(blacklist); + var fields_values = this.field_manager.build_eval_context(blacklist); v_context = new instance.web.CompoundContext(v_context).set_eval_context(fields_values); } return v_context; @@ -1813,7 +1881,7 @@ instance.web.form.FormWidget = instance.web.Widget.extend(instance.web.form.Invi // if there is a domain on the node, overrides the model's domain var final_domain = n_domain !== null ? n_domain : f_domain; if (!(final_domain instanceof Array) || true) { //TODO: remove true - var fields_values = this._build_eval_context(); + var fields_values = this.field_manager.build_eval_context(); final_domain = new instance.web.CompoundDomain(final_domain).set_eval_context(fields_values); } return final_domain; @@ -2001,7 +2069,7 @@ instance.web.form.AbstractField = instance.web.form.FormWidget.extend(instance.w var self = this this._super(field_manager, node); this.name = this.node.attrs.name; - this.field = this.field_manager.get_field(this.name); + this.field = this.field_manager.get_field_desc(this.name); this.widget = this.node.attrs.widget; this.string = this.node.attrs.string || this.field.string || this.name; this.options = JSON.parse(this.node.attrs.options || '{}'); @@ -2015,11 +2083,11 @@ instance.web.form.AbstractField = instance.web.form.FormWidget.extend(instance.w renderElement: function() { var self = this; this._super(); - if (this.field.translate) { + if (this.field.translate && this.view) { this.$el.addClass('oe_form_field_translatable'); this.$el.find('.oe_field_translate').click(this.on_translate); } - this.$label = this.view.$el.find('label[for=' + this.id_for_label + ']'); + this.$label = this.view ? this.view.$el.find('label[for=' + this.id_for_label + ']') : $(); if (instance.session.debug) { this.do_attach_tooltip(this, this.$label[0] || this.$el); this.$label.off('dblclick').on('dblclick', function() { @@ -2081,7 +2149,7 @@ instance.web.form.AbstractField = instance.web.form.FormWidget.extend(instance.w var self = this; var trans = new instance.web.DataSet(this, 'ir.translation'); return trans.call_button('translate_fields', [this.view.dataset.model, this.view.datarecord.id, this.name, this.view.dataset.get_context()]).then(function(r) { - self.do_action(r.result); + self.do_action(r); }); }, }); @@ -2884,7 +2952,7 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc self.focus(); return; } - var pop = new instance.web.form.FormOpenPopup(self.view); + var pop = new instance.web.form.FormOpenPopup(self); pop.show_element( self.field.relation, self.get("value"), @@ -3479,7 +3547,7 @@ instance.web.form.One2ManyViewManager = instance.web.ViewManager.extend({ } var self = this; var id = self.o2m.dataset.index !== null ? self.o2m.dataset.ids[self.o2m.dataset.index] : null; - var pop = new instance.web.form.FormOpenPopup(self.o2m.view); + var pop = new instance.web.form.FormOpenPopup(this); pop.show_element(self.o2m.field.relation, id, self.o2m.build_context(), { title: _t("Open: ") + self.o2m.string, create_function: function(data) { @@ -3595,7 +3663,7 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({ }, do_activate_record: function(index, id) { var self = this; - var pop = new instance.web.form.FormOpenPopup(self.o2m.view); + var pop = new instance.web.form.FormOpenPopup(self); pop.show_element(self.o2m.field.relation, id, self.o2m.build_context(), { title: _t("Open: ") + self.o2m.string, write_function: function(id, data) { @@ -3623,7 +3691,10 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({ var parent_form = this.o2m.view; var self = this; this.ensure_saved().pipe(function () { - return parent_form.do_save(); + if (parent_form) + return parent_form.do_save(); + else + return $.when(); }).then(function () { self.handle_button(name, id, callback); }); @@ -3879,7 +3950,7 @@ instance.web.form.FieldMany2ManyTags = instance.web.form.AbstractField.extend(in }, render_value: function() { var self = this; - var dataset = new instance.web.DataSetStatic(this, this.field.relation, self.view.dataset.get_context()); + var dataset = new instance.web.DataSetStatic(this, this.field.relation, self.build_context()); var values = self.get("value") var handle_names = function(data) { var indexed = {}; @@ -3914,6 +3985,7 @@ instance.web.form.FieldMany2Many = instance.web.form.AbstractField.extend({ disable_utility_classes: true, init: function(field_manager, node) { this._super(field_manager, node); + this.set({"value": []}); this.is_loaded = $.Deferred(); this.initial_is_loaded = this.is_loaded; this.is_setted = $.Deferred(); @@ -4149,7 +4221,7 @@ instance.web.form.FieldMany2ManyKanban = instance.web.form.AbstractField.extend( }); } else { var id = self.dataset.ids[self.dataset.index]; - var pop = new instance.web.form.FormOpenPopup(self.view); + var pop = new instance.web.form.FormOpenPopup(this); pop.show_element(self.field.relation, id, self.build_context(), { title: _t("Open: ") + self.string, write_function: function(id, data, options) { @@ -4282,7 +4354,7 @@ instance.web.form.AbstractFormPopup = instance.web.Widget.extend({ this.dataset.create_function = function(data, sup) { var fct = self.options.create_function || sup; return fct.call(this, data).then(function(r) { - self.created_elements.push(r.result); + self.created_elements.push(r); }); }; this.dataset.write_function = function(id, data, options, sup) { @@ -4417,7 +4489,7 @@ instance.web.form.SelectCreatePopup = instance.web.form.AbstractFormPopup.extend self.rpc('/web/session/eval_domain_and_context', { domains: [], contexts: [this.context] - }, function (results) { + }).then(function (results) { var search_defaults = {}; _.each(results.context, function (value_, key) { var match = /^search_default_(.*)$/.exec(key); @@ -4485,7 +4557,7 @@ instance.web.form.SelectCreatePopup = instance.web.form.AbstractFormPopup.extend domains: domains || [], contexts: contexts || [], group_by_seq: groupbys || [] - }, function (results) { + }).then(function (results) { self.view_list.do_search(results.domain, results.context, results.group_by); }); }, @@ -4540,22 +4612,28 @@ instance.web.form.FieldReference = instance.web.form.AbstractField.extend(instan } }, destroy_content: function() { - if (this.selection) { - this.selection.destroy(); - this.selection = undefined; - } - if (this.m2o) { - this.m2o.destroy(); - this.m2o = undefined; + if (this.fm) { + this.fm.destroy(); } }, initialize_content: function() { var self = this; - this.selection = new instance.web.form.FieldSelection(this, { attrs: { + var fm = new instance.web.form.DefaultFieldManager(this); + this.fm = fm; + fm.extend_field_desc({ + "selection": { + selection: this.field_manager.get_field_desc(this.name).selection, + type: "selection", + }, + "m2o": { + relation: null, + type: "many2one", + }, + }); + this.selection = new instance.web.form.FieldSelection(fm, { attrs: { name: 'selection', modifiers: JSON.stringify({readonly: this.get('effective_readonly')}), }}); - this.selection.view = this.view; this.selection.on("change:value", this, this.on_selection_changed); this.selection.setElement(this.$(".oe_form_view_reference_selection")); this.selection.renderElement(); @@ -4564,11 +4642,10 @@ instance.web.form.FieldReference = instance.web.form.AbstractField.extend(instan .on('focused', null, function () {self.trigger('focused')}) .on('blurred', null, function () {self.trigger('blurred')}); - this.m2o = new instance.web.form.FieldMany2One(this, { attrs: { + this.m2o = new instance.web.form.FieldMany2One(fm, { attrs: { name: 'm2o', modifiers: JSON.stringify({readonly: this.get('effective_readonly')}), }}); - this.m2o.view = this.view; this.m2o.on("change:value", this, this.data_changed); this.m2o.setElement(this.$(".oe_form_view_reference_m2o")); this.m2o.renderElement(); @@ -4608,20 +4685,6 @@ instance.web.form.FieldReference = instance.web.form.AbstractField.extend(instan this.set({'value': false}); } }, - get_field: function(name) { - if (name === "selection") { - return { - selection: this.view.fields_view.fields[this.name].selection, - type: "selection", - }; - } else if (name === "m2o") { - return { - relation: null, - type: "many2one", - }; - } - throw Exception("Should not happen"); - }, }); instance.web.form.FieldBinary = instance.web.form.AbstractField.extend(instance.web.form.ReinitializeFieldMixin, { @@ -4709,9 +4772,10 @@ instance.web.form.FieldBinary = instance.web.form.AbstractField.extend(instance. }, set_filename: function(value) { var filename = this.node.attrs.filename; - if (this.view.fields[filename]) { - this.view.fields[filename].set_value(value); - this.view.fields[filename].on_ui_change(); + if (filename) { + var tmp = {}; + tmp[filename] = value; + this.field_manager.set_values(tmp); } }, on_clear: function() { @@ -4753,7 +4817,9 @@ instance.web.form.FieldBinaryFile = instance.web.form.FieldBinary.extend({ } else { this.$el.find('a').show(!!this.get('value')); if (this.get('value')) { - var show_value = _t("Download") + " " + (this.view.datarecord[this.node.attrs.filename] || ''); + var show_value = _t("Download") + if (this.view) + show_value += " " + (this.view.datarecord[this.node.attrs.filename] || ''); this.$el.find('a').text(show_value); } } @@ -4765,12 +4831,6 @@ instance.web.form.FieldBinaryFile = instance.web.form.FieldBinary.extend({ this.$el.find('input').eq(0).val(show_value); this.set_filename(name); }, - set_filename: function(value_) { - var filename = this.node.attrs.filename; - if (this.view.fields[filename]) { - this.view.fields[filename].set({value: value_}); - } - }, on_clear: function() { this._super.apply(this, arguments); this.$el.find('input').eq(0).val(''); @@ -4819,10 +4879,12 @@ instance.web.form.FieldBinaryImage = instance.web.form.FieldBinary.extend({ this.set({'value': file_base64}); this.binary_value = true; this.render_value(); + this.set_filename(name); }, on_clear: function() { this._super.apply(this, arguments); this.render_value(); + this.set_filename(''); } }); diff --git a/addons/web/static/src/js/view_list.js b/addons/web/static/src/js/view_list.js index fd34ffddb7b..7457ca5ef67 100644 --- a/addons/web/static/src/js/view_list.js +++ b/addons/web/static/src/js/view_list.js @@ -482,7 +482,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi view_type: "tree", context: this.dataset.get_context(context), toolbar: !!this.options.$sidebar - }, callback); + }).then(callback); } }, /** diff --git a/addons/web/static/src/js/view_tree.js b/addons/web/static/src/js/view_tree.js index e0493a446e2..05760420ba8 100644 --- a/addons/web/static/src/js/view_tree.js +++ b/addons/web/static/src/js/view_tree.js @@ -45,7 +45,7 @@ instance.web.TreeView = instance.web.View.extend(/** @lends instance.web.TreeVie view_type: "tree", toolbar: this.view_manager ? !!this.view_manager.sidebar : false, context: this.dataset.get_context() - }, this.on_loaded); + }).then(this.on_loaded); }, /** * Returns the list of fields needed to correctly read objects. diff --git a/addons/web/static/src/js/views.js b/addons/web/static/src/js/views.js index 2fff0e7b683..21f539fed1b 100644 --- a/addons/web/static/src/js/views.js +++ b/addons/web/static/src/js/views.js @@ -99,6 +99,10 @@ instance.web.ActionManager = instance.web.Widget.extend({ this.select_breadcrumb(index, subindex); }, select_breadcrumb: function(index, subindex) { + var next_item = this.breadcrumbs[index + 1]; + if (next_item && next_item.on_reverse_breadcrumb) { + next_item.on_reverse_breadcrumb(this.breadcrumbs[index].widget); + } for (var i = this.breadcrumbs.length - 1; i >= 0; i--) { if (i > index) { if (this.remove_breadcrumb(i) === false) { @@ -220,14 +224,14 @@ instance.web.ActionManager = instance.web.Widget.extend({ } }); }, - do_action: function(action, on_close, clear_breadcrumbs) { + do_action: function(action, on_close, clear_breadcrumbs, on_reverse_breadcrumb) { if (_.isString(action) && instance.web.client_actions.contains(action)) { var action_client = { type: "ir.actions.client", tag: action }; - return this.do_action(action_client, on_close, clear_breadcrumbs); + return this.do_action(action_client, on_close, clear_breadcrumbs, on_reverse_breadcrumb); } else if (_.isNumber(action) || _.isString(action)) { var self = this; return self.rpc("/web/action/load", { action_id: action }).pipe(function(result) { - return self.do_action(result.result, on_close, clear_breadcrumbs); + return self.do_action(result, on_close, clear_breadcrumbs, on_reverse_breadcrumb); }); } if (!action.type) { @@ -249,7 +253,7 @@ instance.web.ActionManager = instance.web.Widget.extend({ console.error("Action manager can't handle action of type " + action.type, action); return $.Deferred().reject(); } - return this[type](action, on_close, clear_breadcrumbs); + return this[type](action, on_close, clear_breadcrumbs, on_reverse_breadcrumb); }, null_action: function() { this.dialog_stop(); @@ -300,24 +304,24 @@ instance.web.ActionManager = instance.web.Widget.extend({ return this.inner_widget.appendTo(this.$el); } }, - ir_actions_act_window: function (action, on_close, clear_breadcrumbs) { + ir_actions_act_window: function (action, on_close, clear_breadcrumbs, on_reverse_breadcrumb) { var self = this; return this.ir_actions_common({ widget: function () { return new instance.web.ViewManagerAction(self, action); }, action: action, klass: 'oe_act_window', - post_process: function (widget) { widget.add_breadcrumb(); } - }, on_close, clear_breadcrumbs); + post_process: function (widget) { widget.add_breadcrumb(on_reverse_breadcrumb); } + }, on_close, clear_breadcrumbs, on_reverse_breadcrumb); }, - ir_actions_client: function (action, on_close, clear_breadcrumbs) { + ir_actions_client: function (action, on_close, clear_breadcrumbs, on_reverse_breadcrumb) { var self = this; var ClientWidget = instance.web.client_actions.get_object(action.tag); if (!(ClientWidget.prototype instanceof instance.web.Widget)) { var next; if (next = ClientWidget(this, action.params)) { - return this.do_action(next, on_close, clear_breadcrumbs); + return this.do_action(next, on_close, clear_breadcrumbs, on_reverse_breadcrumb); } return $.when(); } @@ -329,13 +333,14 @@ instance.web.ActionManager = instance.web.Widget.extend({ post_process: function(widget) { self.push_breadcrumb({ widget: widget, - title: action.name + title: action.name, + on_reverse_breadcrumb: on_reverse_breadcrumb, }); if (action.tag !== 'reload') { self.do_push_state({}); } } - }, on_close, clear_breadcrumbs); + }, on_close, clear_breadcrumbs, on_reverse_breadcrumb); }, ir_actions_act_window_close: function (action, on_closed) { if (!this.dialog && on_closed) { @@ -343,13 +348,13 @@ instance.web.ActionManager = instance.web.Widget.extend({ } this.dialog_stop(); }, - ir_actions_server: function (action, on_closed, clear_breadcrumbs) { + ir_actions_server: function (action, on_closed, clear_breadcrumbs, on_reverse_breadcrumb) { var self = this; this.rpc('/web/action/run', { action_id: action.id, context: action.context || {} }).then(function (action) { - self.do_action(action, on_closed, clear_breadcrumbs) + self.do_action(action, on_closed, clear_breadcrumbs, on_reverse_breadcrumb) }); }, ir_actions_report_xml: function(action, on_closed) { @@ -544,7 +549,7 @@ instance.web.ViewManager = instance.web.Widget.extend({ set_title: function(title) { this.$el.find('.oe_view_title_text:first').text(title); }, - add_breadcrumb: function() { + add_breadcrumb: function(on_reverse_breadcrumb) { var self = this; var views = [this.active_view || this.views_src[0].view_type]; this.on_mode_switch.add(function(mode) { @@ -589,7 +594,8 @@ instance.web.ViewManager = instance.web.Widget.extend({ titles.pop(); } return titles; - } + }, + on_reverse_breadcrumb: on_reverse_breadcrumb, }); }, /** @@ -648,7 +654,7 @@ instance.web.ViewManager = instance.web.Widget.extend({ domains: [this.action.domain || []].concat(domains || []), contexts: [action_context].concat(contexts || []), group_by_seq: groupbys || [] - }, function (results) { + }).then(function (results) { self.dataset._model = new instance.web.Model( self.dataset.model, results.context, results.domain); var groupby = results.group_by.length @@ -807,13 +813,11 @@ instance.web.ViewManagerAction = instance.web.ViewManager.extend({ }); break; case 'fields': - this.dataset.call_and_eval( - 'fields_get', [false, {}], null, 1).then(function (fields) { + this.dataset.call('fields_get', [false, {}]).then(function (fields) { var $root = $('
'); _(fields).each(function (attributes, name) { $root.append($('
').append($('

').text(name))); - var $attrs = $('
').appendTo( - $('
').appendTo($root)); + var $attrs = $('
').appendTo($('
').appendTo($root)); _(attributes).each(function (def, name) { if (def instanceof Object) { def = JSON.stringify(def); @@ -1054,7 +1058,7 @@ instance.web.Sidebar = instance.web.Widget.extend({ self.rpc("/web/action/load", { action_id: item.action.id, context: additional_context - }, function(result) { + }).then(function(result) { result.result.context = _.extend(result.result.context || {}, additional_context); result.result.flags = result.result.flags || {}; @@ -1190,7 +1194,7 @@ instance.web.View = instance.web.Widget.extend({ var context = new instance.web.CompoundContext(dataset.get_context(), action_data.context || {}); var handler = function (r) { - var action = r.result; + var action = r; if (action && action.constructor == Object) { var ncontext = new instance.web.CompoundContext(context); if (record_id) { @@ -1217,7 +1221,7 @@ instance.web.View = instance.web.Widget.extend({ }; if (action_data.special) { - return handler({result: {"type":"ir.actions.act_window_close"}}); + return handler({"type":"ir.actions.act_window_close"}); } else if (action_data.type=="object") { var args = [[record_id]], additional_args = []; if (action_data.args) { @@ -1233,7 +1237,7 @@ instance.web.View = instance.web.Widget.extend({ args.push(context); return dataset.call_button(action_data.name, args).then(handler); } else if (action_data.type=="action") { - return this.rpc('/web/action/load', { action_id: action_data.name, context: context, do_not_eval: true}, handler); + return this.rpc('/web/action/load', { action_id: action_data.name, context: context, do_not_eval: true}).then(handler); } else { return dataset.exec_workflow(record_id, action_data.name).then(handler); } diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index f4c5de237ac..fa6dbd3a27c 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -2,12 +2,6 @@ - -
- -
-
-
@@ -413,7 +407,7 @@
@@ -1128,7 +1122,7 @@ - width: 83px; + width: 83px;
  • + or