diff options
| author | Fabien Meghazi <fme@openerp.com> | 2012-02-15 10:59:40 +0100 |
|---|---|---|
| committer | Fabien Meghazi <fme@openerp.com> | 2012-02-15 10:59:40 +0100 |
| commit | bc233c30e5b9d28c9576b50c6f75dadbdf2e35ea (patch) | |
| tree | ffadfea5a36040b12635e11acdc4237d5a953db8 /addons/web/static/src | |
| parent | 5ab1b4f37ef93952567939444196cd9a91b92f02 (diff) | |
| parent | 750c5db2a30f89593cbd64137e765b11d5226808 (diff) | |
[FIX] Merge last trunk
bzr revid: fme@openerp.com-20120215095940-2jv27t91i7u5mm0r
Diffstat (limited to 'addons/web/static/src')
| -rw-r--r-- | addons/web/static/src/css/base.css | 135 | ||||
| -rw-r--r-- | addons/web/static/src/js/chrome.js | 102 | ||||
| -rw-r--r-- | addons/web/static/src/js/core.js | 121 | ||||
| -rw-r--r-- | addons/web/static/src/js/data.js | 96 | ||||
| -rw-r--r-- | addons/web/static/src/js/data_import.js | 3 | ||||
| -rw-r--r-- | addons/web/static/src/js/formats.js | 16 | ||||
| -rw-r--r-- | addons/web/static/src/js/search.js | 52 | ||||
| -rw-r--r-- | addons/web/static/src/js/view_editor.js | 112 | ||||
| -rw-r--r-- | addons/web/static/src/js/view_form.js | 226 | ||||
| -rw-r--r-- | addons/web/static/src/js/view_list.js | 13 | ||||
| -rw-r--r-- | addons/web/static/src/js/view_list_editable.js | 4 | ||||
| -rw-r--r-- | addons/web/static/src/js/view_page.js | 13 | ||||
| -rw-r--r-- | addons/web/static/src/js/views.js | 90 | ||||
| -rw-r--r-- | addons/web/static/src/xml/base.xml | 164 |
14 files changed, 835 insertions, 312 deletions
diff --git a/addons/web/static/src/css/base.css b/addons/web/static/src/css/base.css index a59992775d2..ac71b6f7a2d 100644 --- a/addons/web/static/src/css/base.css +++ b/addons/web/static/src/css/base.css @@ -1,9 +1,12 @@ -body { padding: 0; margin: 0; } +body.openerp { + padding: 0; + margin: 0; + overflow-y: scroll; +} .openerp { padding: 0; margin: 0; height: 100%; - overflow-y: scroll; font-size: 80%; font-family: Ubuntu, Helvetica, sans-serif; } @@ -340,7 +343,15 @@ table.db_option_table input.required { background-color: #D2D2FF !important; } -.db_option_table input[type="text"], .db_option_table input[type="password"], .db_option_table select { +.db_option_table label { + display: block; + text-align: right; +} + +.db_option_table input[type="text"], +.db_option_table input[type="password"], +.db_option_table input[type="file"], +.db_option_table select { width: 300px; } @@ -374,6 +385,14 @@ label.error { height: 100%; } +/* IE Hack - for IE < 9 + * Avoids footer to be placed statically at 100% cutting the middle of the views + * */ +.openerp .oe-application-container { + height: auto\9; + min-height: 100%\9; +} + /* Menu */ .openerp .menu { height: 34px; @@ -739,6 +758,7 @@ label.error { } .openerp .filter_label.enabled, .openerp .filter_icon.enabled { background: #aaa; + filter: none; -moz-box-shadow: none; -webkit-box-shadow: none; -o-box-shadow: none; @@ -1212,6 +1232,11 @@ label.error { .openerp td.oe_form_frame_cell { padding: 2px; position: relative; +} +.openerp td.oe_form_field_translatable, +.openerp td.oe_form_field_many2one, +.openerp td.oe_form_field_date, +.openerp td.oe_form_field_datetime { white-space: nowrap; } .openerp td.oe_form_field_boolean { @@ -1238,7 +1263,15 @@ label.error { } -.openerp label.oe_label_help, .openerp label.oe_label, .openerp .oe_forms input[type="text"], .openerp .oe_forms input[type="password"], .openerp .oe_forms select, .openerp .oe_forms .oe_button, .openerp .oe_forms textarea { +.openerp label.oe_label_help, .openerp label.oe_label, +.openerp .oe_form_paragraph, +.openerp .oe_form_field_statusbar, +.openerp .oe_forms input[type="text"], +.openerp .oe_forms input[type="password"], +.openerp .oe_forms input[type="file"], +.openerp .oe_forms select, +.openerp .oe_forms .oe_button, +.openerp .oe_forms textarea { font-size: 85%; } @@ -1303,12 +1336,26 @@ label.error { white-space: normal; } +.openerp .oe_forms .oe_form_paragraph.oe_multilines { + white-space: pre; +} + .openerp .oe_form_field_one2many .oe-actions h3.oe_view_title, .openerp .oe_form_field_one2many_list .oe-actions h3.oe_view_title{ display: inline; margin: 0 0.5em 0 0; } +.openerp .oe_forms .oe-listview th.oe-sortable .ui-icon, +.openerp .oe_forms .oe-listview th.oe-sortable .ui-icon { + height: 100%; + margin-top: -9px; +} + +.openerp table.oe_frame .oe-listview-content td { + color: inherit; +} + /* Uneditable Form View */ .openerp .oe_form_readonly { @@ -1354,7 +1401,11 @@ label.error { min-height:100px; } /* Inputs */ -.openerp .oe_forms input[type="text"], .openerp .oe_forms input[type="password"], .openerp .oe_forms select, .openerp .oe_forms textarea { +.openerp .oe_forms input[type="text"], +.openerp .oe_forms input[type="password"], +.openerp .oe_forms input[type="file"], +.openerp .oe_forms select, +.openerp .oe_forms textarea { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; -ms-box-sizing: border-box; @@ -1406,15 +1457,23 @@ label.error { .openerp .oe_forms select{ padding-top: 2px; } +.openerp .oe_forms input[readonly], +.openerp .oe_forms select[readonly], +.openerp .oe_forms textarea[readonly], .openerp .oe_forms input[disabled], .openerp .oe_forms select[disabled], .openerp .oe_forms textarea[disabled]{ - background: #E0E0E0; + background: #E5E5E5 !important; + color: #666; } .openerp .oe_forms textarea { resize:vertical; } -.openerp .oe_forms input[type="text"], .openerp .oe_forms input[type="password"], .openerp .oe_forms select, .openerp .oe_forms .oe_button { +.openerp .oe_forms input[type="text"], +.openerp .oe_forms input[type="password"], +.openerp .oe_forms input[type="file"], +.openerp .oe_forms select, +.openerp .oe_forms .oe_button { height: 22px; } @@ -1427,15 +1486,21 @@ label.error { min-width: 100%; width: 100%; } -.openerp .oe_forms .button { - height: 22px; -} @-moz-document url-prefix() { /* Strange firefox behaviour on width: 100% + white-space: nowrap */ .openerp .oe_forms .oe_form_button .oe_button { width: auto; } } +/* IE Hack - for IE < 9 + * Avoids buttons overflow + * */ +.openerp .oe_forms .oe_form_button .oe_button { + min-width: auto\9; +} +.openerp .oe_forms .button { + height: 22px; +} .openerp .oe_forms .oe_button span { position: relative; vertical-align: top; @@ -1480,7 +1545,7 @@ label.error { position: absolute; /* Should be adjusted for all browsers */ top: -2px; - left: -700px; + right: 0; opacity: 0; filter: alpha(opacity = 0); -ms-filter: "alpha(opacity=0)"; @@ -1869,6 +1934,7 @@ label.error { width: 100%; background-color : #FFFFFF; border-spacing: 0; + } .openerp .oe-treeview-table tr:hover{ color: blue; @@ -1880,7 +1946,7 @@ label.error { text-align: left; border-bottom: 1px solid #CFCCCC; } -.oe-number{ +.openerp .oe-treeview-table .oe-number { text-align: right !important; } .treeview-tr span, .treeview-td span { @@ -1889,14 +1955,14 @@ label.error { white-space: nowrap; display: block; } -.treeview-tr:first-of-type { +.treeview-tr.oe-treeview-first { background: transparent url(/web/static/src/img/expand.gif) 0 50% no-repeat; } -.oe-open .treeview-tr:first-of-type { +.oe-open .treeview-tr.oe-treeview-first { background-image: url(/web/static/src/img/collapse.gif); } -.treeview-tr:first-of-type span, -.treeview-td:first-of-type span { +.treeview-tr.oe-treeview-first span, +.treeview-td.oe-treeview-first span { margin-left: 16px; } @@ -2012,34 +2078,31 @@ ul.oe-arrow-list li.oe-arrow-list-selected .oe-arrow-list-after { border-bottom-right-radius: 3px; } .openerp .oe_view_editor { - border-collapse: collapse; - padding: 0; - align: left; + width:100%; + border-collapse : collapse; + margin-left: -12px; + width: 100%; + background-color : white; + border-spacing: 0; } -.openerp .oe_view_editor_colum{ +.openerp .oe_view_editor td{ + text-align: center; + white-space: nowrap; + border: 1px solid #D8D8D8; + + cursor: pointer; font-size: 90%; - font-weight: normal; - padding: 0; - border-bottom: 1px solid #CFCCCC; } -.openerp .oe_view_editor_row:hover { - background-color: #F3F3F3; +.openerp .oe_view_editor_field td{ + border: 0px !important; } -.openerp .oe_view_editor_tree_grid{ - text-align: left; - white-space: nowrap; - border-collapse: collapse; - width: 100%; -} -.openerp .oe_view_editor_tree_grid a:hover { - color: blue; -} -.openerp .oe_view_editor_tree_grid a { - display: block; +.openerp .oe_view_editor tr:hover { + background-color: #ecebf2; } + /* Dialog traceback cases */ .openerp .oe_error_detail{ display: block; 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(); }, diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index f8de697cca4..fc289b7f131 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -3,7 +3,7 @@ --> <templates id="template" xml:space="preserve"> <t t-name="Notification"> - <div class="oe_notification"> + <div class="oe_notification" t-translation="off"> <div id="oe_notification_default"> <a class="ui-notify-cross ui-notify-close" href="#">x</a> <h1>#{title}</h1> @@ -120,6 +120,8 @@ <option t-att-value="db"><t t-esc="db"/></option> </t> </select> + <input t-if="!db_list" name="drop_db" class="required" + type="text" autofocus="autofocus"/> </td> </tr> <tr> @@ -152,6 +154,8 @@ <option t-att-value="db"><t t-esc="db"/></option> </t> </select> + <input t-if="!db_list" name="drop_db" class="required" + type="text" autofocus="autofocus"/> </td> </tr> <tr> @@ -381,12 +385,12 @@ </t> <t t-name="Menu.secondary"> <div t-attf-class="oe_toggle_secondary_menu"> - <span class="oe_menu_fold" title="Fold menu">&laquo;</span> - <span class="oe_menu_unfold" title="Unfold menu">&raquo;</span> + <span class="oe_menu_fold" title="Fold menu"><t t-raw="'&laquo;'"/></span> + <span class="oe_menu_unfold" title="Unfold menu"><t t-raw="'&raquo;'"/></span> </div> <div t-foreach="widget.data.data.children" t-as="menu" style="display: none" class="oe_secondary_menu" t-att-data-menu-parent="menu.id"> <t t-foreach="menu.children" t-as="menu"> - <t t-set="classname">oe_secondary_menu_item</t> + <t t-set="classname" t-translation="off">oe_secondary_menu_item</t> <t t-set="level" t-value="0"/> <t t-call="Menu.secondary.children"/> </t> @@ -401,7 +405,7 @@ </a> <div t-attf-class="oe_secondary_submenu" t-if="menu.children.length" t-att-style="menu_first and level == 1 ? undefined : 'display: none'"> <t t-foreach="menu.children" t-as="menu"> - <t t-set="classname">oe_secondary_submenu_item</t> + <t t-set="classname" t-translation="off">oe_secondary_submenu_item</t> <t t-call="Menu.secondary.children"/> </t> </div> @@ -418,7 +422,7 @@ <div class="oe_vm_switch"> <t t-if="views.length != 1" t-foreach="views" t-as="view"> - <button type="button" t-attf-class="oe_vm_switch_#{view.view_type}" t-att-data-view-type="view.view_type" t-att-title="view.view_type"> + <button type="button" t-attf-class="oe_vm_switch_#{view.view_type}" t-att-data-view-type="view.view_type" t-att-title="view.label || view.view_type"> <span><t t-esc="view.label || view.view_type"/></span> </button> </t> @@ -480,7 +484,6 @@ <option t-if="view_manager.searchview" value="edit" data-model="ir.ui.view" t-att-data-id="view_manager.searchview.view_id">Edit SearchView</option> <option t-if="view_manager.action" value="edit" t-att-data-model="view_manager.action.type" t-att-data-id="view_manager.action.id">Edit Action</option> <option value="edit_workflow">Edit Workflow</option> - <option value="customize_object">Customize Object</option> </t> </t> <t t-name="ViewManagerDebugViewLog"> @@ -525,11 +528,11 @@ </div> </t> <t t-name="Sidebar.section.items"> - <li t-foreach="items" t-as="item" t-att-class="item.classname"> - <a class="oe_sidebar_action_a" t-att-id="item.element_id" t-att-title="item.title" href="#"> - <t t-esc="item.label"/> - </a> - </li> + <li t-foreach="items" t-as="item" t-att-class="item.classname"> + <a class="oe_sidebar_action_a" t-att-id="item.element_id" t-att-title="item.title" href="#"> + <t t-esc="item.label"/> + </a> + </li> </t> <t t-name="TranslateDialog"> @@ -576,20 +579,21 @@ t-att-data-id="record.id" t-att-data-level="level + 1"> <t t-set="children" t-value="record[children_field]"/> <t t-set="class" t-value="children and children.length ? 'treeview-tr' : 'treeview-td'"/> + <t t-set="rank" t-value="'oe-treeview-first'"/> <t t-set="style" t-value="'background-position: ' + 19*level + 'px; padding-left: ' + 19*level + 'px;'"/> <td t-foreach="fields_view" t-as="field" t-if="!field.attrs.modifiers.tree_invisible" t-att-data-id="record.id" t-att-style="color_for(record) + style " - t-att-class="(fields[field.attrs.name].type === 'float') or (fields[field.attrs.name].type === 'integer') - ? (class +' ' +'oe-number') : class"> + t-attf-class="#{class} #{rank} #{(fields[field.attrs.name].type === 'float') or (fields[field.attrs.name].type === 'integer') ? 'oe-number' : ''}"> <span t-if="!field.attrs.modifiers.invisible" > <t t-esc="render(record[field.attrs.name], fields[field.attrs.name])" /> </span> <t t-set="class" t-value="'treeview-td'"/> + <t t-set="rank" t-value="''"/> <t t-set="style" t-value="''"/> </td> </tr> @@ -676,10 +680,9 @@ <t t-jquery="td.oe-actions"> this.removeAttr('t-if'); - var $title = $(document.createElement('h3')).addClass('oe_view_title'); - $title.append($(document.createElement('t')).attr( - 't-esc', 'fields_view.arch.attrs.string')); - this.prepend($title); + </t> + <t t-jquery="td.oe-actions" t-operation="prepend"> + <h3 class="oe_view_title"><t t-esc="fields_view.arch.attrs.string"/></h3> </t> </t> <th t-name="Listview.navigation.button" t-if="!no_leaf and options.pager !== false" @@ -792,6 +795,62 @@ </li> </ul> </t> +<form t-name="FormView.set_default" class="oe_forms oe_frame"> + <t t-set="args" t-value="widget.dialog_options.args"/> + <table style="width: 100%"> + <tr> + <td> + <label for="formview_default_fields" + class="oe_label oe_align_right"> + Default: + </label> + </td> + <td class="required"> + <select id="formview_default_fields"> + <option value=""/> + <option t-foreach="args.fields" t-as="field" + t-att-value="field.name"> + <t t-esc="field.string"/> = <t t-esc="field.displayed"/> + </option> + </select> + </td> + </tr> + <tr t-if="args.conditions.length"> + <td> + <label for="formview_default_conditions" + class="oe_label oe_align_right"> + Condition: + </label> + </td> + <td> + <select id="formview_default_conditions"> + <option value=""/> + <option t-foreach="args.conditions" t-as="cond" + t-att-value="cond.name + '=' + cond.value"> + <t t-esc="cond.string"/>=<t t-esc="cond.displayed"/> + </option> + </select> + </td> + </tr> + <tr> + <td colspan="2"> + <input type="radio" id="formview_default_self" + value="self" name="scope" checked="checked"/> + <label for="formview_default_self" class="oe_label" + style="display: inline;"> + Only you + </label> + <br/> + <input type="radio" id="formview_default_all" + value="all" name="scope"/> + <label for="formview_default_all" class="oe_label" + style="display: inline;"> + All users + </label> + </td> + </tr> + </table> +</form> <t t-name="Widget"> Unhandled widget <t t-js="dict">console.warn('Unhandled widget', dict.widget);</t> @@ -908,6 +967,10 @@ <span class="oe_tooltip_technical_title">Modifiers:</span> <t t-esc="widget.node.attrs.modifiers"/> </li> + <li t-if="widget.field and widget.field.change_default" data-item="change_default"> + <span class="oe_tooltip_technical_title">Change default:</span> + Yes + </li> <li t-if="widget.node.attrs.on_change" data-item="on_change"> <span class="oe_tooltip_technical_title">On change:</span> <t t-esc="widget.node.attrs.on_change"/> @@ -929,7 +992,7 @@ </ul> </t> <t t-name="WidgetParagraph"> - <p t-attf-class="oe_form_paragraph oe_align_#{widget.align}"><t t-esc="widget.string"/></p> + <p t-attf-class="oe_form_paragraph oe_align_#{widget.align} #{widget.multilines ? 'oe_multilines' : ''}"><t t-esc="widget.string"/></p> </t> <t t-name="FieldChar"> <input t-att-type="widget.password ? 'password' : 'text'" size="1" @@ -947,7 +1010,7 @@ </t> <t t-name="FieldURI.readonly"> <div> - <a href="#" class="oe_form_uri">#</a> + <a href="#" class="oe_form_uri"/> </div> </t> <t t-name="FieldEmail"> @@ -1219,7 +1282,11 @@ <option class="oe-filters-title" value="">Filters</option> <optgroup label="-- Filters --"> <t t-foreach="filters" t-as="filter"> - <option t-attf-value="get:#{filter_index}"><t t-esc="filter.name"/></option> + <option t-attf-value="get:#{filter_index}" + t-att-disabled="filter.disabled and 'disabled'" + t-att-title="filter.disabled and disabled_filter_message"> + <t t-esc="filter.name"/> + </option> </t> </optgroup> <optgroup label="-- Actions --"> @@ -1295,7 +1362,7 @@ <span t-if="attrs.help">?</span> </label> <div t-att-style="style"> - <span t-att-id="element_id"></span> + <span t-att-id="element_id"/> <t t-if="filters.length" t-raw="filters.render(defaults)"/> </div> </t> @@ -1314,7 +1381,8 @@ <t t-foreach="attrs.selection" t-as="option"> <t t-set="selected" t-value="defaults[attrs.name] === option[0]"/> <option t-if="selected" - t-att-value="option_index" selected="selected"> + t-attf-selected="selected" + t-att-value="option_index"> <t t-esc="option[1]"/> </option> <option t-if="!selected" t-att-value="option_index"> @@ -1480,9 +1548,9 @@ </table> </t> <t t-name="view_editor.row"> - <tr class="oe_view_editor_row" t-att-id="'viewedit-' + rec.id" t-att-level="rec.level" t-foreach="data" t-as="rec"> - <td class="oe_view_editor_colum" width="85%"> - <table class="oe_view_editor_tree_grid"> + <tr t-att-id="'viewedit-' + rec.id" t-att-level="rec.level" t-foreach="data" t-as="rec"> + <td width="90%"> + <table class="oe_view_editor_field"> <tr> <td width="16px" t-att-style="'background-position: ' + 20*rec.level + 'px; padding-left: ' + 20*rec.level + 'px'"> <img t-if="rec.child_id.length" t-att-id="'parentimg-' + rec.id" @@ -1496,30 +1564,24 @@ </tr> </table> </td> - <td align="left" class="oe_view_editor_colum" width="15%"> - <table width="100%"> - <tr> - <td width="20%"> - <img t-if="rec.att_list.length" - id="side-add" src="/web/static/src/img/icons/gtk-add.png" style="cursor: pointer;"/> - </td> - <td width="20%"> - <img id="side-remove" src="/web/static/src/img/icons/gtk-remove.png" style="cursor: pointer;"/> - </td> - <td width="20%"> - <img t-if="rec.att_list.length and !_.include(no_properties, rec.att_list[0])" - id="side-edit" src="/web/static/src/img/icons/gtk-edit.png" style="cursor: pointer;"/> - </td> - <td width="20%"> - <img t-if="rec.att_list.length" - id="side-up" src="/web/static/src/img/icons/gtk-go-up.png" style="cursor: pointer;"/> - </td> - <td width="20%"> - <img t-if="rec.att_list.length" - id="side-down" src="/web/static/src/img/icons/gtk-go-down.png" style="cursor: pointer;"/> - </td> - </tr> - </table> + <td width="2%"> + <img t-if="rec.att_list.length" + id="side-add" src="/web/static/src/img/icons/gtk-add.png" style="cursor: pointer;"/> + </td> + <td width="2%"> + <img id="side-remove" src="/web/static/src/img/icons/gtk-remove.png" style="cursor: pointer;"/> + </td> + <td width="2%"> + <img t-if="rec.att_list.length and !_.include(no_properties, rec.att_list[0])" + id="side-edit" src="/web/static/src/img/icons/gtk-edit.png" style="cursor: pointer;"/> + </td> + <td width="2%"> + <img t-if="rec.att_list.length" + id="side-up" src="/web/static/src/img/icons/gtk-go-up.png" style="cursor: pointer;"/> + </td> + <td width="2%"> + <img t-if="rec.att_list.length" + id="side-down" src="/web/static/src/img/icons/gtk-go-down.png" style="cursor: pointer;"/> </td> <t t-if="rec.child_id.length"> <t t-set="data" t-value="rec.child_id"/> @@ -1609,7 +1671,7 @@ <tr class="oe_export_row"> <t t-foreach="(field.id).split('/')" t-as="level" > <t t-if="(field.id).split('/')[0] != level"> - <td width="18">&nbsp;</td> + <td width="18" t-translation="off">&nbsp;</td> </t> </t> <td valign="top" align="left" style="cursor: pointer;" width="18"> |
