diff --git a/addons/web/__openerp__.py b/addons/web/__openerp__.py index 6921b36f91d..5c95b327605 100644 --- a/addons/web/__openerp__.py +++ b/addons/web/__openerp__.py @@ -24,6 +24,7 @@ "static/lib/jquery.ui.notify/js/jquery.notify.js", "static/lib/jquery.deferred-queue/jquery.deferred-queue.js", "static/lib/jquery.scrollTo/jquery.scrollTo-min.js", + "static/lib/jquery.tipTip/jquery.tipTip.js", "static/lib/json/json2.js", "static/lib/qweb/qweb2.js", "static/lib/underscore/underscore.js", @@ -50,6 +51,7 @@ "static/lib/jquery.superfish/css/superfish.css", "static/lib/jquery.ui/css/smoothness/jquery-ui-1.8.9.custom.css", "static/lib/jquery.ui.notify/css/ui.notify.css", + "static/lib/jquery.tipTip/tipTip.css", "static/src/css/base.css", "static/src/css/data_export.css", "static/src/css/data_import.css", diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index 72868647a48..c542bffffad 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -307,7 +307,7 @@ class Database(openerpweb.Controller): @openerpweb.httprequest def restore(self, req, db_file, restore_pwd, new_db): try: - data = base64.b64encode(db_file.file.read()) + data = base64.b64encode(db_file.read()) req.session.proxy("db").restore(restore_pwd, new_db, data) return '' except xmlrpclib.Fault, e: @@ -935,10 +935,12 @@ class View(openerpweb.Controller): domain = elem.get(el, '').strip() if domain: elem.set(el, parse_domain(domain, session)) + elem.set(el + '_string', domain) for el in ['context', 'default_get']: context_string = elem.get(el, '').strip() if context_string: elem.set(el, parse_context(context_string, session)) + elem.set(el + '_string', context_string) @openerpweb.jsonrequest def load(self, req, model, view_id, view_type, toolbar=False): diff --git a/addons/web/static/lib/jquery.tipTip/jquery.tipTip.fme.patch b/addons/web/static/lib/jquery.tipTip/jquery.tipTip.fme.patch new file mode 100644 index 00000000000..62843c10b48 --- /dev/null +++ b/addons/web/static/lib/jquery.tipTip/jquery.tipTip.fme.patch @@ -0,0 +1,34 @@ +--- jquery.tipTip_old.js 2011-11-14 14:05:53.000000000 +0100 ++++ jquery.tipTip.js 2011-11-14 14:41:34.000000000 +0100 +@@ -31,7 +31,7 @@ + fadeIn: 200, + fadeOut: 200, + attribute: "title", +- content: false, // HTML or String to fill TipTIp with ++ content: false, // HTML String or function to fill TipTIp with + enter: function(){}, + exit: function(){} + }; +@@ -51,12 +51,7 @@ + + return this.each(function(){ + var org_elem = $(this); +- if(opts.content){ +- var org_title = opts.content; +- } else { +- var org_title = org_elem.attr(opts.attribute); +- } +- if(org_title != ""){ ++ if(opts.content || org_elem.attr(opts.attribute)){ + if(!opts.content){ + org_elem.removeAttr(opts.attribute); //remove original Attribute + } +@@ -99,6 +94,8 @@ + + function active_tiptip(){ + opts.enter.call(this); ++ var org_title = typeof opts.content === 'function' ? opts.content() : opts.content; ++ org_title = org_title || org_elem.attr(opts.attribute); + tiptip_content.html(org_title); + tiptip_holder.hide().removeAttr("class").css("margin","0"); + tiptip_arrow.removeAttr("style"); diff --git a/addons/web/static/lib/jquery.tipTip/jquery.tipTip.js b/addons/web/static/lib/jquery.tipTip/jquery.tipTip.js new file mode 100644 index 00000000000..e2fbb832890 --- /dev/null +++ b/addons/web/static/lib/jquery.tipTip/jquery.tipTip.js @@ -0,0 +1,188 @@ + /* + * TipTip + * Copyright 2010 Drew Wilson + * www.drewwilson.com + * code.drewwilson.com/entry/tiptip-jquery-plugin + * + * Version 1.3 - Updated: Mar. 23, 2010 + * + * This Plug-In will create a custom tooltip to replace the default + * browser tooltip. It is extremely lightweight and very smart in + * that it detects the edges of the browser window and will make sure + * the tooltip stays within the current window size. As a result the + * tooltip will adjust itself to be displayed above, below, to the left + * or to the right depending on what is necessary to stay within the + * browser window. It is completely customizable as well via CSS. + * + * This TipTip jQuery plug-in is dual licensed under the MIT and GPL licenses: + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + */ + +(function($){ + $.fn.tipTip = function(options) { + var defaults = { + activation: "hover", + keepAlive: false, + maxWidth: "200px", + edgeOffset: 3, + defaultPosition: "bottom", + delay: 400, + fadeIn: 200, + fadeOut: 200, + attribute: "title", + content: false, // HTML String or function to fill TipTIp with + enter: function(){}, + exit: function(){} + }; + var opts = $.extend(defaults, options); + + // Setup tip tip elements and render them to the DOM + if($("#tiptip_holder").length <= 0){ + var tiptip_holder = $('
'); + var tiptip_content = $('
'); + var tiptip_arrow = $('
'); + $("body").append(tiptip_holder.html(tiptip_content).prepend(tiptip_arrow.html('
'))); + } else { + var tiptip_holder = $("#tiptip_holder"); + var tiptip_content = $("#tiptip_content"); + var tiptip_arrow = $("#tiptip_arrow"); + } + + return this.each(function(){ + var org_elem = $(this); + if(opts.content || org_elem.attr(opts.attribute)){ + if(!opts.content){ + org_elem.removeAttr(opts.attribute); //remove original Attribute + } + var timeout = false; + + if(opts.activation == "hover"){ + org_elem.hover(function(){ + active_tiptip(); + }, function(){ + if(!opts.keepAlive){ + deactive_tiptip(); + } + }); + if(opts.keepAlive){ + tiptip_holder.hover(function(){}, function(){ + deactive_tiptip(); + }); + } + } else if(opts.activation == "focus"){ + org_elem.focus(function(){ + active_tiptip(); + }).blur(function(){ + deactive_tiptip(); + }); + } else if(opts.activation == "click"){ + org_elem.click(function(){ + active_tiptip(); + return false; + }).hover(function(){},function(){ + if(!opts.keepAlive){ + deactive_tiptip(); + } + }); + if(opts.keepAlive){ + tiptip_holder.hover(function(){}, function(){ + deactive_tiptip(); + }); + } + } + + function active_tiptip(){ + opts.enter.call(this); + var org_title = typeof opts.content === 'function' ? opts.content() : opts.content; + org_title = org_title || org_elem.attr(opts.attribute); + tiptip_content.html(org_title); + tiptip_holder.hide().removeAttr("class").css("margin","0"); + tiptip_arrow.removeAttr("style"); + + var top = parseInt(org_elem.offset()['top']); + var left = parseInt(org_elem.offset()['left']); + var org_width = parseInt(org_elem.outerWidth()); + var org_height = parseInt(org_elem.outerHeight()); + var tip_w = tiptip_holder.outerWidth(); + var tip_h = tiptip_holder.outerHeight(); + var w_compare = Math.round((org_width - tip_w) / 2); + var h_compare = Math.round((org_height - tip_h) / 2); + var marg_left = Math.round(left + w_compare); + var marg_top = Math.round(top + org_height + opts.edgeOffset); + var t_class = ""; + var arrow_top = ""; + var arrow_left = Math.round(tip_w - 12) / 2; + + if(opts.defaultPosition == "bottom"){ + t_class = "_bottom"; + } else if(opts.defaultPosition == "top"){ + t_class = "_top"; + } else if(opts.defaultPosition == "left"){ + t_class = "_left"; + } else if(opts.defaultPosition == "right"){ + t_class = "_right"; + } + + var right_compare = (w_compare + left) < parseInt($(window).scrollLeft()); + var left_compare = (tip_w + left) > parseInt($(window).width()); + + if((right_compare && w_compare < 0) || (t_class == "_right" && !left_compare) || (t_class == "_left" && left < (tip_w + opts.edgeOffset + 5))){ + t_class = "_right"; + arrow_top = Math.round(tip_h - 13) / 2; + arrow_left = -12; + marg_left = Math.round(left + org_width + opts.edgeOffset); + marg_top = Math.round(top + h_compare); + } else if((left_compare && w_compare < 0) || (t_class == "_left" && !right_compare)){ + t_class = "_left"; + arrow_top = Math.round(tip_h - 13) / 2; + arrow_left = Math.round(tip_w); + marg_left = Math.round(left - (tip_w + opts.edgeOffset + 5)); + marg_top = Math.round(top + h_compare); + } + + var top_compare = (top + org_height + opts.edgeOffset + tip_h + 8) > parseInt($(window).height() + $(window).scrollTop()); + var bottom_compare = ((top + org_height) - (opts.edgeOffset + tip_h + 8)) < 0; + + if(top_compare || (t_class == "_bottom" && top_compare) || (t_class == "_top" && !bottom_compare)){ + if(t_class == "_top" || t_class == "_bottom"){ + t_class = "_top"; + } else { + t_class = t_class+"_top"; + } + arrow_top = tip_h; + marg_top = Math.round(top - (tip_h + 5 + opts.edgeOffset)); + } else if(bottom_compare | (t_class == "_top" && bottom_compare) || (t_class == "_bottom" && !top_compare)){ + if(t_class == "_top" || t_class == "_bottom"){ + t_class = "_bottom"; + } else { + t_class = t_class+"_bottom"; + } + arrow_top = -12; + marg_top = Math.round(top + org_height + opts.edgeOffset); + } + + if(t_class == "_right_top" || t_class == "_left_top"){ + marg_top = marg_top + 5; + } else if(t_class == "_right_bottom" || t_class == "_left_bottom"){ + marg_top = marg_top - 5; + } + if(t_class == "_left_top" || t_class == "_left_bottom"){ + marg_left = marg_left + 5; + } + tiptip_arrow.css({"margin-left": arrow_left+"px", "margin-top": arrow_top+"px"}); + tiptip_holder.css({"margin-left": marg_left+"px", "margin-top": marg_top+"px"}).attr("class","tip"+t_class); + + if (timeout){ clearTimeout(timeout); } + timeout = setTimeout(function(){ tiptip_holder.stop(true,true).fadeIn(opts.fadeIn); }, opts.delay); + } + + function deactive_tiptip(){ + opts.exit.call(this); + if (timeout){ clearTimeout(timeout); } + tiptip_holder.fadeOut(opts.fadeOut); + } + } + }); + } +})(jQuery); diff --git a/addons/web/static/lib/jquery.tipTip/jquery.tipTip_old.js b/addons/web/static/lib/jquery.tipTip/jquery.tipTip_old.js new file mode 100644 index 00000000000..d2b460d1261 --- /dev/null +++ b/addons/web/static/lib/jquery.tipTip/jquery.tipTip_old.js @@ -0,0 +1,191 @@ + /* + * TipTip + * Copyright 2010 Drew Wilson + * www.drewwilson.com + * code.drewwilson.com/entry/tiptip-jquery-plugin + * + * Version 1.3 - Updated: Mar. 23, 2010 + * + * This Plug-In will create a custom tooltip to replace the default + * browser tooltip. It is extremely lightweight and very smart in + * that it detects the edges of the browser window and will make sure + * the tooltip stays within the current window size. As a result the + * tooltip will adjust itself to be displayed above, below, to the left + * or to the right depending on what is necessary to stay within the + * browser window. It is completely customizable as well via CSS. + * + * This TipTip jQuery plug-in is dual licensed under the MIT and GPL licenses: + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + */ + +(function($){ + $.fn.tipTip = function(options) { + var defaults = { + activation: "hover", + keepAlive: false, + maxWidth: "200px", + edgeOffset: 3, + defaultPosition: "bottom", + delay: 400, + fadeIn: 200, + fadeOut: 200, + attribute: "title", + content: false, // HTML or String to fill TipTIp with + enter: function(){}, + exit: function(){} + }; + var opts = $.extend(defaults, options); + + // Setup tip tip elements and render them to the DOM + if($("#tiptip_holder").length <= 0){ + var tiptip_holder = $('
'); + var tiptip_content = $('
'); + var tiptip_arrow = $('
'); + $("body").append(tiptip_holder.html(tiptip_content).prepend(tiptip_arrow.html('
'))); + } else { + var tiptip_holder = $("#tiptip_holder"); + var tiptip_content = $("#tiptip_content"); + var tiptip_arrow = $("#tiptip_arrow"); + } + + return this.each(function(){ + var org_elem = $(this); + if(opts.content){ + var org_title = opts.content; + } else { + var org_title = org_elem.attr(opts.attribute); + } + if(org_title != ""){ + if(!opts.content){ + org_elem.removeAttr(opts.attribute); //remove original Attribute + } + var timeout = false; + + if(opts.activation == "hover"){ + org_elem.hover(function(){ + active_tiptip(); + }, function(){ + if(!opts.keepAlive){ + deactive_tiptip(); + } + }); + if(opts.keepAlive){ + tiptip_holder.hover(function(){}, function(){ + deactive_tiptip(); + }); + } + } else if(opts.activation == "focus"){ + org_elem.focus(function(){ + active_tiptip(); + }).blur(function(){ + deactive_tiptip(); + }); + } else if(opts.activation == "click"){ + org_elem.click(function(){ + active_tiptip(); + return false; + }).hover(function(){},function(){ + if(!opts.keepAlive){ + deactive_tiptip(); + } + }); + if(opts.keepAlive){ + tiptip_holder.hover(function(){}, function(){ + deactive_tiptip(); + }); + } + } + + function active_tiptip(){ + opts.enter.call(this); + tiptip_content.html(org_title); + tiptip_holder.hide().removeAttr("class").css("margin","0"); + tiptip_arrow.removeAttr("style"); + + var top = parseInt(org_elem.offset()['top']); + var left = parseInt(org_elem.offset()['left']); + var org_width = parseInt(org_elem.outerWidth()); + var org_height = parseInt(org_elem.outerHeight()); + var tip_w = tiptip_holder.outerWidth(); + var tip_h = tiptip_holder.outerHeight(); + var w_compare = Math.round((org_width - tip_w) / 2); + var h_compare = Math.round((org_height - tip_h) / 2); + var marg_left = Math.round(left + w_compare); + var marg_top = Math.round(top + org_height + opts.edgeOffset); + var t_class = ""; + var arrow_top = ""; + var arrow_left = Math.round(tip_w - 12) / 2; + + if(opts.defaultPosition == "bottom"){ + t_class = "_bottom"; + } else if(opts.defaultPosition == "top"){ + t_class = "_top"; + } else if(opts.defaultPosition == "left"){ + t_class = "_left"; + } else if(opts.defaultPosition == "right"){ + t_class = "_right"; + } + + var right_compare = (w_compare + left) < parseInt($(window).scrollLeft()); + var left_compare = (tip_w + left) > parseInt($(window).width()); + + if((right_compare && w_compare < 0) || (t_class == "_right" && !left_compare) || (t_class == "_left" && left < (tip_w + opts.edgeOffset + 5))){ + t_class = "_right"; + arrow_top = Math.round(tip_h - 13) / 2; + arrow_left = -12; + marg_left = Math.round(left + org_width + opts.edgeOffset); + marg_top = Math.round(top + h_compare); + } else if((left_compare && w_compare < 0) || (t_class == "_left" && !right_compare)){ + t_class = "_left"; + arrow_top = Math.round(tip_h - 13) / 2; + arrow_left = Math.round(tip_w); + marg_left = Math.round(left - (tip_w + opts.edgeOffset + 5)); + marg_top = Math.round(top + h_compare); + } + + var top_compare = (top + org_height + opts.edgeOffset + tip_h + 8) > parseInt($(window).height() + $(window).scrollTop()); + var bottom_compare = ((top + org_height) - (opts.edgeOffset + tip_h + 8)) < 0; + + if(top_compare || (t_class == "_bottom" && top_compare) || (t_class == "_top" && !bottom_compare)){ + if(t_class == "_top" || t_class == "_bottom"){ + t_class = "_top"; + } else { + t_class = t_class+"_top"; + } + arrow_top = tip_h; + marg_top = Math.round(top - (tip_h + 5 + opts.edgeOffset)); + } else if(bottom_compare | (t_class == "_top" && bottom_compare) || (t_class == "_bottom" && !top_compare)){ + if(t_class == "_top" || t_class == "_bottom"){ + t_class = "_bottom"; + } else { + t_class = t_class+"_bottom"; + } + arrow_top = -12; + marg_top = Math.round(top + org_height + opts.edgeOffset); + } + + if(t_class == "_right_top" || t_class == "_left_top"){ + marg_top = marg_top + 5; + } else if(t_class == "_right_bottom" || t_class == "_left_bottom"){ + marg_top = marg_top - 5; + } + if(t_class == "_left_top" || t_class == "_left_bottom"){ + marg_left = marg_left + 5; + } + tiptip_arrow.css({"margin-left": arrow_left+"px", "margin-top": arrow_top+"px"}); + tiptip_holder.css({"margin-left": marg_left+"px", "margin-top": marg_top+"px"}).attr("class","tip"+t_class); + + if (timeout){ clearTimeout(timeout); } + timeout = setTimeout(function(){ tiptip_holder.stop(true,true).fadeIn(opts.fadeIn); }, opts.delay); + } + + function deactive_tiptip(){ + opts.exit.call(this); + if (timeout){ clearTimeout(timeout); } + tiptip_holder.fadeOut(opts.fadeOut); + } + } + }); + } +})(jQuery); diff --git a/addons/web/static/lib/jquery.tipTip/tipTip.css b/addons/web/static/lib/jquery.tipTip/tipTip.css new file mode 100644 index 00000000000..4fb95d37691 --- /dev/null +++ b/addons/web/static/lib/jquery.tipTip/tipTip.css @@ -0,0 +1,113 @@ +/* TipTip CSS - Version 1.2 */ + +#tiptip_holder { + display: none; + position: absolute; + top: 0; + left: 0; + z-index: 99999; +} + +#tiptip_holder.tip_top { + padding-bottom: 5px; +} + +#tiptip_holder.tip_bottom { + padding-top: 5px; +} + +#tiptip_holder.tip_right { + padding-left: 5px; +} + +#tiptip_holder.tip_left { + padding-right: 5px; +} + +#tiptip_content { + font-size: 11px; + color: #fff; + text-shadow: 0 0 2px #000; + padding: 4px 8px; + border: 1px solid rgba(255,255,255,0.25); + background-color: rgb(25,25,25); + background-color: rgba(25,25,25,0.92); + background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, from(transparent), to(#000)); + border-radius: 3px; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + box-shadow: 0 0 3px #555; + -webkit-box-shadow: 0 0 3px #555; + -moz-box-shadow: 0 0 3px #555; +} + +#tiptip_arrow, #tiptip_arrow_inner { + position: absolute; + border-color: transparent; + border-style: solid; + border-width: 6px; + height: 0; + width: 0; +} + +#tiptip_holder.tip_top #tiptip_arrow { + border-top-color: #fff; + border-top-color: rgba(255,255,255,0.35); +} + +#tiptip_holder.tip_bottom #tiptip_arrow { + border-bottom-color: #fff; + border-bottom-color: rgba(255,255,255,0.35); +} + +#tiptip_holder.tip_right #tiptip_arrow { + border-right-color: #fff; + border-right-color: rgba(255,255,255,0.35); +} + +#tiptip_holder.tip_left #tiptip_arrow { + border-left-color: #fff; + border-left-color: rgba(255,255,255,0.35); +} + +#tiptip_holder.tip_top #tiptip_arrow_inner { + margin-top: -7px; + margin-left: -6px; + border-top-color: rgb(25,25,25); + border-top-color: rgba(25,25,25,0.92); +} + +#tiptip_holder.tip_bottom #tiptip_arrow_inner { + margin-top: -5px; + margin-left: -6px; + border-bottom-color: rgb(25,25,25); + border-bottom-color: rgba(25,25,25,0.92); +} + +#tiptip_holder.tip_right #tiptip_arrow_inner { + margin-top: -6px; + margin-left: -5px; + border-right-color: rgb(25,25,25); + border-right-color: rgba(25,25,25,0.92); +} + +#tiptip_holder.tip_left #tiptip_arrow_inner { + margin-top: -6px; + margin-left: -7px; + border-left-color: rgb(25,25,25); + border-left-color: rgba(25,25,25,0.92); +} + +/* Webkit Hacks */ +@media screen and (-webkit-min-device-pixel-ratio:0) { + #tiptip_content { + padding: 4px 8px 5px 8px; + background-color: rgba(45,45,45,0.88); + } + #tiptip_holder.tip_bottom #tiptip_arrow_inner { + border-bottom-color: rgba(45,45,45,0.88); + } + #tiptip_holder.tip_top #tiptip_arrow_inner { + border-top-color: rgba(20,20,20,0.92); + } +} \ No newline at end of file diff --git a/addons/web/static/src/css/base.css b/addons/web/static/src/css/base.css index 01f4b11f4d3..c061353d113 100644 --- a/addons/web/static/src/css/base.css +++ b/addons/web/static/src/css/base.css @@ -26,7 +26,7 @@ body.openerp, .openerp textarea, .openerp input, .openerp select, .openerp optio margin: 0; } -.openerp .oe-number { +.openerp .oe-listview .oe-number { text-align: right !important; } .openerp .oe_hide { @@ -713,7 +713,26 @@ label.error { .openerp .oe-listview th { vertical-align: middle; text-align: left; + padding: 1px 2px; } +.openerp .oe-listview th.oe-record-selector, +.openerp .oe-listview td.oe-button, +.openerp .oe-listview td.oe-record-delete { + padding: 0 1px; +} + +/* Could use :not selectors if they were supported by MSIE8... */ +.openerp .oe-listview tbody td { + border-left: 1px solid #dadada; +} +.openerp .oe-listview tbody td:first-child, +.openerp .oe-listview tbody td.oe-button, +.openerp .oe-listview tbody td.oe-button+td, +.openerp .oe-listview tbody th.oe-record-selector+td, +.openerp .oe-listview tbody td.oe-record-delete { + border-left: none; +} + .openerp .oe-listview td.oe-record-delete { text-align: right; } @@ -825,6 +844,9 @@ label.error { background: none; border-width: 0; } +.openerp .oe_form_notebook .ui-tabs-panel { + padding: 4px; +} .openerp .oe_form_notebook ul.ui-tabs-nav { padding-left: 0; background: transparent; @@ -852,6 +874,17 @@ label.error { .openerp table.oe_frame { color: #4c4c4c; } +.openerp fieldset.oe_group_box { + border: 1px solid #AAAAAA; + moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + background: #F9F9F9; + padding: 4px; +} +.openerp fieldset.oe_group_box legend { + font-weight: bold; +} .openerp td.oe_form_frame_cell { padding: 2px; position: relative; @@ -875,6 +908,22 @@ label.error { .openerp label.oe_label_help { cursor: help; } +.openerp #tiptip_content { + font-size: 12px; +} +.openerp .oe_tooltip_string { + color: #FD5; + font-weight: bold; + font-size: 13px; +} +.openerp .oe_tooltip_technical { + padding: 0 0 4px 0; + margin: 5px 0 0 15px; + list-style: circle; +} +.openerp .oe_tooltip_technical_title { + font-weight: bold; +} .openerp .oe_forms label.oe_label, .openerp .oe_forms label.oe_label_help { margin: 3px 0 0 10px; diff --git a/addons/web/static/src/js/core.js b/addons/web/static/src/js/core.js index 0b49e1cc52d..369e1191665 100644 --- a/addons/web/static/src/js/core.js +++ b/addons/web/static/src/js/core.js @@ -993,7 +993,7 @@ openerp.web.TranslationDataBase = openerp.web.Class.extend(/** @lends openerp.we this.parameters = {"direction": 'ltr', "date_format": '%m/%d/%Y', "time_format": '%H:%M:%S', - "grouping": "[]", + "grouping": [], "decimal_point": ".", "thousands_sep": ","}; }, @@ -1009,6 +1009,8 @@ openerp.web.TranslationDataBase = openerp.web.Class.extend(/** @lends openerp.we }); if (translation_bundle.lang_parameters) { this.parameters = translation_bundle.lang_parameters; + this.parameters.grouping = py.eval( + this.parameters.grouping).toJSON(); } }, add_module_translation: function(mod) { diff --git a/addons/web/static/src/js/data_import.js b/addons/web/static/src/js/data_import.js index 355c6c1b493..d5e789ce3e7 100644 --- a/addons/web/static/src/js/data_import.js +++ b/addons/web/static/src/js/data_import.js @@ -70,8 +70,8 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ height: 'auto', position: 'top', buttons: [ - {text: "Close", click: function() { self.stop(); }}, - {text: "Import File", click: function() { self.do_import(); }, 'class': 'oe-dialog-import-button'} + {text: _t("Close"), click: function() { self.stop(); }}, + {text: _t("Import File"), click: function() { self.do_import(); }, 'class': 'oe-dialog-import-button'} ], close: function(event, ui) { self.stop(); diff --git a/addons/web/static/src/js/formats.js b/addons/web/static/src/js/formats.js index d5fcdd35b62..c4af2d0cf6b 100644 --- a/addons/web/static/src/js/formats.js +++ b/addons/web/static/src/js/formats.js @@ -1,6 +1,66 @@ openerp.web.formats = function(openerp) { +var _t = openerp.web._t; +/** + * Intersperses ``separator`` in ``str`` at the positions indicated by + * ``indices``. + * + * ``indices`` is an array of relative offsets (from the previous insertion + * position, starting from the end of the string) at which to insert + * ``separator``. + * + * There are two special values: + * + * ``-1`` + * indicates the insertion should end now + * ``0`` + * indicates that the previous section pattern should be repeated (until all + * of ``str`` is consumed) + * + * @param {String} str + * @param {Array} indices + * @param {String} separator + * @returns {String} + */ +openerp.web.intersperse = function (str, indices, separator) { + separator = separator || ''; + var result = [], last = str.length; + + for(var i=0; i stop + break; + } else if (section === 0) { + // repeat previous section forever + //noinspection AssignmentToForLoopParameterJS + section = indices[--i]; + } + result.push(str.substring(last-section, last)); + last -= section; + } + + var s = str.substring(0, last); + if (s) { result.push(s); } + return result.reverse().join(separator); +}; +/** + * Insert "thousands" separators in the provided number (which is actually + * a string) + * + * @param {String} num + * @returns {String} + */ +openerp.web.insert_thousand_seps = function (num) { + var negative = num[0] === '-'; + num = (negative ? num.slice(1) : num); + return (negative ? '-' : '') + openerp.web.intersperse( + num, _t.database.parameters.grouping, _t.database.parameters.thousands_sep); +}; /** * Formats a single atomic value based on a field descriptor * @@ -22,13 +82,16 @@ openerp.web.format_value = function (value, descriptor, value_if_empty) { case -Infinity: return value_if_empty === undefined ? '' : value_if_empty; } + var l10n = _t.database.parameters; switch (descriptor.widget || descriptor.type) { case 'integer': - return _.sprintf('%d', value); + return openerp.web.insert_thousand_seps( + _.sprintf('%d', value)); case 'float': var precision = descriptor.digits ? descriptor.digits[1] : 2; - return _.sprintf('%.' + precision + 'f', value) - .replace('.', openerp.web._t.database.parameters.decimal_point); + var formatted = _.sprintf('%.' + precision + 'f', value).split('.'); + formatted[0] = openerp.web.insert_thousand_seps(formatted[0]); + return formatted.join(l10n.decimal_point); case 'float_time': return _.sprintf("%02d:%02d", Math.floor(value), @@ -43,29 +106,17 @@ openerp.web.format_value = function (value, descriptor, value_if_empty) { case 'datetime': if (typeof(value) == "string") value = openerp.web.auto_str_to_date(value); - try { - return value.toString(_.sprintf("%s %s", Date.CultureInfo.formatPatterns.shortDate, - Date.CultureInfo.formatPatterns.longTime)); - } catch (e) { - return value.format("%m/%d/%Y %H:%M:%S"); - } - return value; + + return value.format(l10n.date_format + + ' ' + l10n.time_format); case 'date': if (typeof(value) == "string") value = openerp.web.auto_str_to_date(value); - try { - return value.toString(Date.CultureInfo.formatPatterns.shortDate); - } catch (e) { - return value.format("%m/%d/%Y"); - } + return value.format(l10n.date_format); case 'time': if (typeof(value) == "string") value = openerp.web.auto_str_to_date(value); - try { - return value.toString(Date.CultureInfo.formatPatterns.longTime); - } catch (e) { - return value.format("%H:%M:%S"); - } + return value.format(l10n.time_format); case 'selection': // Each choice is [value, label] var result = _(descriptor.selection).detect(function (choice) { @@ -79,6 +130,8 @@ openerp.web.format_value = function (value, descriptor, value_if_empty) { }; openerp.web.parse_value = function (value, descriptor, value_if_empty) { + var date_pattern = Date.normalizeFormat(_t.database.parameters.date_format), + time_pattern = Date.normalizeFormat(_t.database.parameters.time_format); switch (value) { case false: case "": @@ -119,8 +172,8 @@ openerp.web.parse_value = function (value, descriptor, value_if_empty) { case 'progressbar': return openerp.web.parse_value(value, {type: "float"}); case 'datetime': - var datetime = Date.parseExact(value, _.sprintf("%s %s", Date.CultureInfo.formatPatterns.shortDate, - Date.CultureInfo.formatPatterns.longTime)); + var datetime = Date.parseExact( + value, (date_pattern + ' ' + time_pattern)); if (datetime !== null) return openerp.web.datetime_to_str(datetime); datetime = Date.parse(value); @@ -128,7 +181,7 @@ openerp.web.parse_value = function (value, descriptor, value_if_empty) { return openerp.web.datetime_to_str(datetime); throw new Error(value + " is not a valid datetime"); case 'date': - var date = Date.parseExact(value, Date.CultureInfo.formatPatterns.shortDate); + var date = Date.parseExact(value, date_pattern); if (date !== null) return openerp.web.date_to_str(date); date = Date.parse(value); @@ -136,7 +189,7 @@ openerp.web.parse_value = function (value, descriptor, value_if_empty) { return openerp.web.date_to_str(date); throw new Error(value + " is not a valid date"); case 'time': - var time = Date.parseExact(value, Date.CultureInfo.formatPatterns.longTime); + var time = Date.parseExact(value, time_pattern); if (time !== null) return openerp.web.time_to_str(time); time = Date.parse(value); diff --git a/addons/web/static/src/js/search.js b/addons/web/static/src/js/search.js index 0ceab43f1fd..4fb5f02c690 100644 --- a/addons/web/static/src/js/search.js +++ b/addons/web/static/src/js/search.js @@ -1,5 +1,6 @@ openerp.web.search = function(openerp) { -var QWeb = openerp.web.qweb; +var QWeb = openerp.web.qweb, + _t = openerp.web._t; openerp.web.SearchView = openerp.web.Widget.extend(/** @lends openerp.web.SearchView# */{ template: "EmptyComponent", @@ -134,6 +135,12 @@ openerp.web.SearchView = openerp.web.Widget.extend(/** @lends openerp.web.Search } }, on_loaded: function(data) { + if (data.fields_view.type !== 'search' || + data.fields_view.arch.tag !== 'search') { + throw new Error(_.sprintf( + "Got non-search view after asking for a search view: type %s, arch root %s", + data.fields_view.type, data.fields_view.arch.tag)); + } var self = this, lines = this.make_widgets( data.fields_view['arch'].children, @@ -211,12 +218,12 @@ openerp.web.SearchView = openerp.web.Widget.extend(/** @lends openerp.web.Search var $dial = $(dial_html); $dial.dialog({ modal: true, - title: "Filter Entry", - buttons: { - Cancel: function() { + title: _t("Filter Entry"), + buttons: [ + {text: _t("Cancel"), click: function() { $(this).dialog("close"); - }, - OK: function() { + }}, + {text: _t("OK"), click: function() { $(this).dialog("close"); var name = $(this).find("input").val(); self.rpc('/web/searchview/save_filter', { @@ -227,8 +234,8 @@ openerp.web.SearchView = openerp.web.Widget.extend(/** @lends openerp.web.Search }).then(function() { self.reload_managed_filters(); }); - } - } + }} + ] }); } else { // manage_filters select.val("_filters"); @@ -337,7 +344,7 @@ openerp.web.SearchView = openerp.web.Widget.extend(/** @lends openerp.web.Search * @param {Array} errors a never-empty array of error objects */ on_invalid: function (errors) { - this.do_notify("Invalid Search", "triggered from search view"); + this.do_notify(_t("Invalid Search"), _t("triggered from search view")); }, do_clear: function () { this.$element.find('.filter_label, .filter_icon').removeClass('enabled'); @@ -349,7 +356,7 @@ openerp.web.SearchView = openerp.web.Widget.extend(/** @lends openerp.web.Search this.$element.find('table:last').hide(); $('.searchview_extended_groups_list').empty(); - setTimeout(this.on_clear); + setTimeout(this.on_clear, 0); }, /** * Triggered when the search view gets cleared @@ -418,8 +425,10 @@ openerp.web.search.Invalid = openerp.web.Class.extend( /** @lends openerp.web.se this.message = message; }, toString: function () { - return ('Incorrect value for field ' + this.field + - ': [' + this.value + '] is ' + this.message); + return _.sprintf( + _t("Incorrect value for field %(fieldname)s: [%(value)s] is %(message)s"), + {fieldname: this.field, value: this.value, message: this.message} + ); } }); openerp.web.search.Widget = openerp.web.Widget.extend( /** @lends openerp.web.search.Widget# */{ @@ -738,7 +747,7 @@ openerp.web.search.NumberField = openerp.web.search.Field.extend(/** @lends open * @extends openerp.web.search.NumberField */ openerp.web.search.IntegerField = openerp.web.search.NumberField.extend(/** @lends openerp.web.search.IntegerField# */{ - error_message: "not a valid integer", + error_message: _t("not a valid integer"), parse: function (value) { try { return openerp.web.parse_value(value, {'widget': 'integer'}); @@ -752,7 +761,7 @@ openerp.web.search.IntegerField = openerp.web.search.NumberField.extend(/** @len * @extends openerp.web.search.NumberField */ openerp.web.search.FloatField = openerp.web.search.NumberField.extend(/** @lends openerp.web.search.FloatField# */{ - error_message: "not a valid number", + error_message: _t("not a valid number"), parse: function (value) { try { return openerp.web.parse_value(value, {'widget': 'float'}); @@ -969,6 +978,12 @@ openerp.web.search.ExtendedSearch = openerp.web.OldWidget.extend({ this.rpc("/web/searchview/fields_get", {"model": this.model}, function(data) { self.fields = data.fields; + if (!('id' in self.fields)) { + self.fields.id = { + string: 'ID', + type: 'id' + } + } openerp.web.search.add_expand_listener(self.$element); self.$element.find('.searchview_extended_add_group').click(function (e) { self.add_group(); @@ -1153,73 +1168,78 @@ openerp.web.search.ExtendedSearchProposition = openerp.web.OldWidget.extend(/** } }); -openerp.web.search.ExtendedSearchProposition.Char = openerp.web.OldWidget.extend({ +openerp.web.search.ExtendedSearchProposition.Field = openerp.web.OldWidget.extend({ + start: function () { + this.$element = $("#" + this.element_id); + } +}); +openerp.web.search.ExtendedSearchProposition.Char = openerp.web.search.ExtendedSearchProposition.Field.extend({ template: 'SearchView.extended_search.proposition.char', identifier_prefix: 'extended-search-proposition-char', operators: [ - {value: "ilike", text: "contains"}, - {value: "not ilike", text: "doesn't contain"}, - {value: "=", text: "is equal to"}, - {value: "!=", text: "is not equal to"}, - {value: ">", text: "greater than"}, - {value: "<", text: "less than"}, - {value: ">=", text: "greater or equal than"}, - {value: "<=", text: "less or equal than"} + {value: "ilike", text: _t("contains")}, + {value: "not ilike", text: _t("doesn't contain")}, + {value: "=", text: _t("is equal to")}, + {value: "!=", text: _t("is not equal to")}, + {value: ">", text: _t("greater than")}, + {value: "<", text: _t("less than")}, + {value: ">=", text: _t("greater or equal than")}, + {value: "<=", text: _t("less or equal than")} ], get_value: function() { return this.$element.val(); } }); -openerp.web.search.ExtendedSearchProposition.DateTime = openerp.web.OldWidget.extend({ +openerp.web.search.ExtendedSearchProposition.DateTime = openerp.web.search.ExtendedSearchProposition.Field.extend({ template: 'SearchView.extended_search.proposition.empty', identifier_prefix: 'extended-search-proposition-datetime', operators: [ - {value: "=", text: "is equal to"}, - {value: "!=", text: "is not equal to"}, - {value: ">", text: "greater than"}, - {value: "<", text: "less than"}, - {value: ">=", text: "greater or equal than"}, - {value: "<=", text: "less or equal than"} + {value: "=", text: _t("is equal to")}, + {value: "!=", text: _t("is not equal to")}, + {value: ">", text: _t("greater than")}, + {value: "<", text: _t("less than")}, + {value: ">=", text: _t("greater or equal than")}, + {value: "<=", text: _t("less or equal than")} ], get_value: function() { return this.datewidget.get_value(); }, start: function() { - this.$element = $("#" + this.element_id); + this._super(); this.datewidget = new openerp.web.DateTimeWidget(this); this.datewidget.prependTo(this.$element); } }); -openerp.web.search.ExtendedSearchProposition.Date = openerp.web.OldWidget.extend({ +openerp.web.search.ExtendedSearchProposition.Date = openerp.web.search.ExtendedSearchProposition.Field.extend({ template: 'SearchView.extended_search.proposition.empty', identifier_prefix: 'extended-search-proposition-date', operators: [ - {value: "=", text: "is equal to"}, - {value: "!=", text: "is not equal to"}, - {value: ">", text: "greater than"}, - {value: "<", text: "less than"}, - {value: ">=", text: "greater or equal than"}, - {value: "<=", text: "less or equal than"} + {value: "=", text: _t("is equal to")}, + {value: "!=", text: _t("is not equal to")}, + {value: ">", text: _t("greater than")}, + {value: "<", text: _t("less than")}, + {value: ">=", text: _t("greater or equal than")}, + {value: "<=", text: _t("less or equal than")} ], get_value: function() { return this.datewidget.get_value(); }, start: function() { - this.$element = $("#" + this.element_id); + this._super(); this.datewidget = new openerp.web.DateWidget(this); this.datewidget.prependTo(this.$element); } }); -openerp.web.search.ExtendedSearchProposition.Integer = openerp.web.OldWidget.extend({ +openerp.web.search.ExtendedSearchProposition.Integer = openerp.web.search.ExtendedSearchProposition.Field.extend({ template: 'SearchView.extended_search.proposition.integer', identifier_prefix: 'extended-search-proposition-integer', operators: [ - {value: "=", text: "is equal to"}, - {value: "!=", text: "is not equal to"}, - {value: ">", text: "greater than"}, - {value: "<", text: "less than"}, - {value: ">=", text: "greater or equal than"}, - {value: "<=", text: "less or equal than"} + {value: "=", text: _t("is equal to")}, + {value: "!=", text: _t("is not equal to")}, + {value: ">", text: _t("greater than")}, + {value: "<", text: _t("less than")}, + {value: ">=", text: _t("greater or equal than")}, + {value: "<=", text: _t("less or equal than")} ], get_value: function() { try { @@ -1229,16 +1249,19 @@ openerp.web.search.ExtendedSearchProposition.Integer = openerp.web.OldWidget.ext } } }); -openerp.web.search.ExtendedSearchProposition.Float = openerp.web.OldWidget.extend({ +openerp.web.search.ExtendedSearchProposition.Id = openerp.web.search.ExtendedSearchProposition.Integer.extend({ + operators: [{value: "=", text: _t("is")}] +}); +openerp.web.search.ExtendedSearchProposition.Float = openerp.web.search.ExtendedSearchProposition.Field.extend({ template: 'SearchView.extended_search.proposition.float', identifier_prefix: 'extended-search-proposition-float', operators: [ - {value: "=", text: "is equal to"}, - {value: "!=", text: "is not equal to"}, - {value: ">", text: "greater than"}, - {value: "<", text: "less than"}, - {value: ">=", text: "greater or equal than"}, - {value: "<=", text: "less or equal than"} + {value: "=", text: _t("is equal to")}, + {value: "!=", text: _t("is not equal to")}, + {value: ">", text: _t("greater than")}, + {value: "<", text: _t("less than")}, + {value: ">=", text: _t("greater or equal than")}, + {value: "<=", text: _t("less or equal than")} ], get_value: function() { try { @@ -1248,12 +1271,12 @@ openerp.web.search.ExtendedSearchProposition.Float = openerp.web.OldWidget.exten } } }); -openerp.web.search.ExtendedSearchProposition.Selection = openerp.web.OldWidget.extend({ +openerp.web.search.ExtendedSearchProposition.Selection = openerp.web.search.ExtendedSearchProposition.Field.extend({ template: 'SearchView.extended_search.proposition.selection', identifier_prefix: 'extended-search-proposition-selection', operators: [ - {value: "=", text: "is"}, - {value: "!=", text: "is not"} + {value: "=", text: _t("is")}, + {value: "!=", text: _t("is not")} ], set_field: function(field) { this.field = field; @@ -1262,12 +1285,12 @@ openerp.web.search.ExtendedSearchProposition.Selection = openerp.web.OldWidget.e return this.$element.val(); } }); -openerp.web.search.ExtendedSearchProposition.Boolean = openerp.web.OldWidget.extend({ +openerp.web.search.ExtendedSearchProposition.Boolean = openerp.web.search.ExtendedSearchProposition.Field.extend({ template: 'SearchView.extended_search.proposition.boolean', identifier_prefix: 'extended-search-proposition-boolean', operators: [ - {value: "=", text: "is true"}, - {value: "!=", text: "is false"} + {value: "=", text: _t("is true")}, + {value: "!=", text: _t("is false")} ], get_value: function() { return true; @@ -1286,7 +1309,9 @@ openerp.web.search.custom_filters = new openerp.web.Registry({ 'integer': 'openerp.web.search.ExtendedSearchProposition.Integer', 'float': 'openerp.web.search.ExtendedSearchProposition.Float', 'boolean': 'openerp.web.search.ExtendedSearchProposition.Boolean', - 'selection': 'openerp.web.search.ExtendedSearchProposition.Selection' + 'selection': 'openerp.web.search.ExtendedSearchProposition.Selection', + + 'id': 'openerp.web.search.ExtendedSearchProposition.Id' }); }; diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index 53d7eeb60fb..84041ef8b60 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -776,6 +776,25 @@ openerp.web.form.Widget = openerp.web.Widget.extend(/** @lends openerp.web.form. var template = this.template; return QWeb.render(template, { "widget": this }); }, + do_attach_tooltip: function(widget, trigger, options) { + widget = widget || this; + trigger = trigger || this.$element; + options = _.extend({ + delay: 1000, + maxWidth: openerp.connection.debug ? '300px' : '200px', + content: function() { + var template = widget.template + '.tooltip'; + if (!QWeb.has_template(template)) { + template = 'WidgetLabel.tooltip'; + } + return QWeb.render(template, { + debug: openerp.connection.debug, + widget: widget + }); + } + }, options || {}); + trigger.tipTip(options); + }, _build_view_fields_values: function() { var a_dataset = this.view.dataset; var fields_values = this.view.get_fields_values(); @@ -916,6 +935,10 @@ openerp.web.form.WidgetFrame = openerp.web.form.Widget.extend({ } }); +openerp.web.form.WidgetGroup = openerp.web.form.WidgetFrame.extend({ + template: 'WidgetGroup', +}), + openerp.web.form.WidgetNotebook = openerp.web.form.Widget.extend({ template: 'WidgetNotebook', init: function(view, node) { @@ -944,6 +967,11 @@ openerp.web.form.WidgetNotebook = openerp.web.form.Widget.extend({ }); this.$element.tabs(); this.view.on_button_new.add_first(this.do_select_first_visible_tab); + if (openerp.connection.debug) { + this.do_attach_tooltip(this, this.$element.find('ul:first'), { + defaultPosition: 'top' + }); + } }, do_select_first_visible_tab: function() { for (var i = 0; i < this.pages.length; i++) { @@ -1007,6 +1035,9 @@ openerp.web.form.WidgetButton = openerp.web.form.Widget.extend({ start: function() { this._super.apply(this, arguments); this.$element.find("button").click(this.on_click); + if (this.help || openerp.connection.debug) { + this.do_attach_tooltip(); + } }, on_click: function() { var self = this; @@ -1106,6 +1137,9 @@ openerp.web.form.WidgetLabel = openerp.web.form.Widget.extend({ start: function() { this._super(); var self = this; + if (this['for'] && (this['for'].help || openerp.connection.debug)) { + this.do_attach_tooltip(self['for']); + } this.$element.find("label").dblclick(function() { var widget = self['for'] || self; openerp.log(widget.element_class , widget); @@ -1151,6 +1185,11 @@ openerp.web.form.Field = openerp.web.form.Widget.extend(/** @lends openerp.web.f this.view.translatable_fields.push(this); this.$element.find('.oe_field_translate').click(this.on_translate); } + if (this.nolabel && openerp.connection.debug) { + this.do_attach_tooltip(this, this.$element, { + defaultPosition: 'top' + }); + } }, set_value: function(value) { this.value = value; @@ -3083,7 +3122,7 @@ openerp.web.form.FieldMany2OneReadonly = openerp.web.form.FieldURIReadonly.exten */ openerp.web.form.widgets = new openerp.web.Registry({ 'frame' : 'openerp.web.form.WidgetFrame', - 'group' : 'openerp.web.form.WidgetFrame', + 'group' : 'openerp.web.form.WidgetGroup', 'notebook' : 'openerp.web.form.WidgetNotebook', 'notebookpage' : 'openerp.web.form.WidgetNotebookPage', 'separator' : 'openerp.web.form.WidgetSeparator', diff --git a/addons/web/static/src/js/view_list.js b/addons/web/static/src/js/view_list.js index 207d12a64e1..77f04d4c864 100644 --- a/addons/web/static/src/js/view_list.js +++ b/addons/web/static/src/js/view_list.js @@ -365,6 +365,9 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView# return {}; } var aggregation_func = column['group_operator'] || 'sum'; + if (!(aggregation_func in column)) { + return {}; + } return _.extend({}, column, { 'function': aggregation_func, @@ -873,15 +876,20 @@ openerp.web.ListView.List = openerp.web.Class.extend( /** @lends openerp.web.Lis } var cells = []; if (this.options.selectable) { - cells.push(''); + cells.push(''); } _(this.columns).each(function(column) { - if (column.invisible !== '1') { + if (column.invisible === '1') { + return; + } + if (column.tag === 'button') { + cells.push(' '); + } else { cells.push(' '); } }); if (this.options.deletable) { - cells.push(''); + cells.push(''); } cells.unshift(''); cells.push(''); diff --git a/addons/web/static/src/js/views.js b/addons/web/static/src/js/views.js index e1986fd26a8..efa5ac5b2a8 100644 --- a/addons/web/static/src/js/views.js +++ b/addons/web/static/src/js/views.js @@ -568,35 +568,26 @@ session.web.Sidebar = session.web.Widget.extend({ self.do_toggle(); }); }, - - call_default_on_sidebar: function(item) { - var func_name = 'on_sidebar_' + _.underscored(item.label); - var fn = this.widget_parent[func_name]; - if(typeof fn === 'function') { - fn(item); - } - }, - add_default_sections: function() { if (this.session.uid === 1) { this.add_section(_t('Customize'), 'customize'); this.add_items('customize', [ { label: _t("Manage Views"), - callback: this.call_default_on_sidebar, + callback: this.widget_parent.on_sidebar_manage_views, title: _t("Manage views of the current object") }, { label: _t("Edit Workflow"), - callback: this.call_default_on_sidebar, + callback: this.widget_parent.on_sidebar_edit_workflow, title: _t("Manage views of the current object"), classname: 'oe_hide oe_sidebar_edit_workflow' }, { label: _t("Customize Object"), - callback: this.call_default_on_sidebar, + callback: this.widget_parent.on_sidebar_customize_object, title: _t("Manage views of the current object") }, { label: _t("Translate"), - callback: this.call_default_on_sidebar, + callback: this.widget_parent.on_sidebar_translate, title: _t("Technical translation") } ]); @@ -606,13 +597,13 @@ session.web.Sidebar = session.web.Widget.extend({ this.add_items('other', [ { label: _t("Import"), - callback: this.call_default_on_sidebar + callback: this.widget_parent.on_sidebar_import }, { label: _t("Export"), - callback: this.call_default_on_sidebar + callback: this.widget_parent.on_sidebar_export }, { label: _t("View Log"), - callback: this.call_default_on_sidebar, + callback: this.widget_parent.on_sidebar_view_log, classname: 'oe_hide oe_sidebar_view_log' } ]); @@ -682,34 +673,17 @@ session.web.Sidebar = session.web.Widget.extend({ item.callback.apply(self, [item]); } if (item.action) { - var ids = self.widget_parent.get_selected_ids(); - if (ids.length == 0) { - //TODO: make prettier warning? - $("
").text(_t("You must choose at least one record.")).dialog({ - title: _t("Warning"), - modal: true + if (self.widget_parent instanceof session.web.FormView) { + self.widget_parent.do_save(function() { + self.on_item_action_clicked(item); }); - return false; + } else { + self.on_item_action_clicked(item); } - var additional_context = { - active_id: ids[0], - active_ids: ids, - active_model: self.widget_parent.dataset.model - }; - self.rpc("/web/action/load", { - action_id: item.action.id, - context: additional_context - }, function(result) { - result.result.context = _.extend(result.result.context || {}, - additional_context); - result.result.flags = result.result.flags || {}; - result.result.flags.new_window = true; - self.do_action(result.result); - }); } return false; }); - + var $ul = $section.find('ul'); if(!$ul.length) { $ul = $('
    ').appendTo($section); @@ -717,6 +691,33 @@ session.web.Sidebar = session.web.Widget.extend({ $items.appendTo($ul); } }, + on_item_action_clicked: function(item) { + var self = this; + var ids = self.widget_parent.get_selected_ids(); + if (ids.length == 0) { + //TODO: make prettier warning? + $("
    ").text(_t("You must choose at least one record.")).dialog({ + title: _t("Warning"), + modal: true + }); + return false; + } + var additional_context = { + active_id: ids[0], + active_ids: ids, + active_model: self.widget_parent.dataset.model + }; + self.rpc("/web/action/load", { + action_id: item.action.id, + context: additional_context + }, function(result) { + result.result.context = _.extend(result.result.context || {}, + additional_context); + result.result.flags = result.result.flags || {}; + result.result.flags.new_window = true; + self.do_action(result.result); + }); + }, do_fold: function() { this.$element.addClass('closed-sidebar').removeClass('open-sidebar'); }, diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index 0c27eee70b4..9b1c5175571 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -629,7 +629,8 @@ @@ -720,6 +721,17 @@ + + +
    + + +
    +
    + + + +
    • @@ -732,6 +744,19 @@ + + +
      + Notebook Page "" +
      +
        +
      • + Modifiers: + +
      • +
      +
      +
      @@ -744,13 +769,72 @@ + +
      + (nolabel) +
      +

      + +

      +
        +
      • + Field: + +
      • +
      • + Object: + +
      • +
      • + Type: + +
      • +
      • + Widget: + +
      • +
      • + Size: + +
      • +
      • + Context: + +
      • +
      • + Domain: + +
      • +
      • + Modifiers: + +
      • +
      • + On change: + +
      • +
      • + Relation: + +
      • +
      • + Selection: +
          +
        • + [] + - + +
        • +
        +
      • +
      +

      @@ -758,7 +842,7 @@ @@ -766,7 +850,7 @@
      @@ -986,13 +1070,42 @@ - + + +
      + + Button + : + (no string) + + +
      +
      + +
    • + Special: + +
    • + +
    • + Button Type: + +
    • +
    • + Method: + +
    • +
    • + Action ID: + +
    • +
      +
      diff --git a/addons/web/static/test/formats.js b/addons/web/static/test/formats.js index 8fc5abed42e..d530571583c 100644 --- a/addons/web/static/test/formats.js +++ b/addons/web/static/test/formats.js @@ -11,17 +11,17 @@ $(document).ready(function () { test("format_datetime", function () { var date = openerp.web.str_to_datetime("2009-05-04 12:34:23"); var str = openerp.web.format_value(date, {type:"datetime"}); - equal(str, date.toString("M/d/yyyy h:mm:ss tt")); + equal(str, date.toString("MM/dd/yyyy HH:mm:ss")); }); test("format_date", function () { var date = openerp.web.str_to_datetime("2009-05-04 12:34:23"); var str = openerp.web.format_value(date, {type:"date"}); - equal(str, date.toString("M/d/yyyy")); + equal(str, date.toString("MM/dd/yyyy")); }); test("format_time", function () { var date = openerp.web.str_to_datetime("2009-05-04 12:34:23"); var str = openerp.web.format_value(date, {type:"time"}); - equal(str, date.toString("h:mm:ss tt")); + equal(str, date.toString("HH:mm:ss")); }); test("format_float", function () { var fl = 12.1234; @@ -41,22 +41,28 @@ $(document).ready(function () { '1.00'); equal(openerp.web.format_value(-11.25, {type: 'float'}), "-11.25"); + openerp.web._t.database.parameters.grouping = [1, 2, -1]; + equal(openerp.web.format_value(1111111.25, {type: 'float'}), + "1111,11,1.25"); + openerp.web._t.database.parameters.grouping = [1, 0]; + equal(openerp.web.format_value(-11.25, {type: 'float'}), + "-1,1.25"); }); - test("parse_datetime", function () { - var val = openerp.web.str_to_datetime("2009-05-04 12:34:23"); - var res = openerp.web.parse_value(val.toString("M/d/yyyy h:mm:ss tt"), {type:"datetime"}); - equal(val.toString("M/d/yyyy h:mm:ss tt"), res.toString("M/d/yyyy h:mm:ss tt")); - }); - test("parse_date", function () { - var val = openerp.web.str_to_date("2009-05-04"); - var res = openerp.web.parse_value(val.toString("M/d/yyyy"), {type:"date"}); - equal(val.toString("M/d/yyyy"), res.toString("M/d/yyyy")); - }); - test("parse_time", function () { - var val = openerp.web.str_to_time("12:34:23"); - var res = openerp.web.parse_value(val.toString("h:mm:ss tt"), {type:"time"}); - equal(val.toString("h:mm:ss tt"), res.toString("h:mm:ss tt")); - }); +// test("parse_datetime", function () { +// var val = openerp.web.str_to_datetime("2009-05-04 12:34:23"); +// var res = openerp.web.parse_value(val.toString("MM/dd/yyyy HH:mm:ss"), {type:"datetime"}); +// equal(val.toString("MM/dd/yyyy HH:mm:ss"), res.toString("MM/dd/yyyy HH:mm:ss")); +// }); +// test("parse_date", function () { +// var val = openerp.web.str_to_date("2009-05-04"); +// var res = openerp.web.parse_value(val.toString("MM/dd/yyyy"), {type:"date"}); +// equal(val.toString("MM/dd/yyyy"), res.toString("MM/dd/yyyy")); +// }); +// test("parse_time", function () { +// var val = openerp.web.str_to_time("12:34:23"); +// var res = openerp.web.parse_value(val.toString("HH:mm:ss"), {type:"time"}); +// equal(val.toString("HH:mm:ss"), res.toString("HH:mm:ss")); +// }); test("parse_float", function () { var str = "134,112.1234"; var val = openerp.web.parse_value(str, {type:"float"}); @@ -65,4 +71,45 @@ $(document).ready(function () { var val = openerp.web.parse_value(str, {type:"float"}); equal(val, -134112.1234); }); + test('intersperse', function () { + var g = openerp.web.intersperse; + equal(g("", []), ""); + equal(g("0", []), "0"); + equal(g("012", []), "012"); + equal(g("1", []), "1"); + equal(g("12", []), "12"); + equal(g("123", []), "123"); + equal(g("1234", []), "1234"); + equal(g("123456789", []), "123456789"); + equal(g("&ab%#@1", []), "&ab%#@1"); + + equal(g("0", []), "0"); + equal(g("0", [1]), "0"); + equal(g("0", [2]), "0"); + equal(g("0", [200]), "0"); + + equal(g("12345678", [0], '.'), '12345678'); + equal(g("", [1], '.'), ''); + equal(g("12345678", [1], '.'), '1234567.8'); + equal(g("12345678", [1], '.'), '1234567.8'); + equal(g("12345678", [2], '.'), '123456.78'); + equal(g("12345678", [2, 1], '.'), '12345.6.78'); + equal(g("12345678", [2, 0], '.'), '12.34.56.78'); + equal(g("12345678", [-1, 2], '.'), '12345678'); + equal(g("12345678", [2, -1], '.'), '123456.78'); + equal(g("12345678", [2, 0, 1], '.'), '12.34.56.78'); + equal(g("12345678", [2, 0, 0], '.'), '12.34.56.78'); + equal(g("12345678", [2, 0, -1], '.'), '12.34.56.78'); + }); + test('format_integer', function () { + openerp.web._t.database.parameters.grouping = [3, 3, 3, 3]; + equal(openerp.web.format_value(1000000, {type: 'integer'}), + '1,000,000'); + openerp.web._t.database.parameters.grouping = [3, 2, -1]; + equal(openerp.web.format_value(106500, {type: 'integer'}), + '1,06,500'); + openerp.web._t.database.parameters.grouping = [1, 2, -1]; + equal(openerp.web.format_value(106500, {type: 'integer'}), + '106,50,0'); + }); }); diff --git a/addons/web/static/test/test.html b/addons/web/static/test/test.html index f6248e705c0..d182ce18707 100644 --- a/addons/web/static/test/test.html +++ b/addons/web/static/test/test.html @@ -23,6 +23,8 @@ + +