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 @@
+
+
+
+
+
+
+
+
+
+
+
+ Notebook Page ""
+
+
+
+
@@ -744,13 +769,72 @@
+
+
+ (nolabel)
+
+
+
+
+
+
@@ -758,7 +842,7 @@

@@ -766,7 +850,7 @@
@@ -986,13 +1070,42 @@
-
+
+
+
+
+ Button
+ :
+ (no string)
+
+
+
+
+
+
+ Special:
+
+
+
+
+ Button Type:
+
+
+
+ Method:
+
+
+
+ Action ID:
+
+
+
+