summaryrefslogtreecommitdiff
path: root/addons/web/static/src
diff options
context:
space:
mode:
authorXavier Morel <xmo@openerp.com>2012-11-29 10:10:38 +0100
committerXavier Morel <xmo@openerp.com>2012-11-29 10:10:38 +0100
commite00cb36f41ced6e3ccadd9d0bac0e1ab88e21fc3 (patch)
treea2ce5fe45cdf3eee86b7e2ad561dd379b5b2dee9 /addons/web/static/src
parent8431c69016ef24980ff5f01a0f6e388399514d8c (diff)
parent4b464deeeebe61094a289cbd56cf3d780c295847 (diff)
[MERGE] from trunk
bzr revid: xmo@openerp.com-20121129091038-0fexjx8o12drjbga
Diffstat (limited to 'addons/web/static/src')
-rw-r--r--addons/web/static/src/css/base.css130
-rw-r--r--addons/web/static/src/css/base.sass104
-rw-r--r--addons/web/static/src/img/view_empty_arrow.pngbin4157 -> 2531 bytes
-rw-r--r--addons/web/static/src/js/chrome.js217
-rw-r--r--addons/web/static/src/js/corelib.js146
-rw-r--r--addons/web/static/src/js/coresetup.js5
-rw-r--r--addons/web/static/src/js/data_export.js1
-rw-r--r--addons/web/static/src/js/search.js14
-rw-r--r--addons/web/static/src/js/view_form.js41
-rw-r--r--addons/web/static/src/js/view_list.js2
-rw-r--r--addons/web/static/src/js/view_list_editable.js5
-rw-r--r--addons/web/static/src/js/views.js45
-rw-r--r--addons/web/static/src/xml/base.xml65
13 files changed, 474 insertions, 301 deletions
diff --git a/addons/web/static/src/css/base.css b/addons/web/static/src/css/base.css
index ab7b74f85fe..ee81492b8ad 100644
--- a/addons/web/static/src/css/base.css
+++ b/addons/web/static/src/css/base.css
@@ -652,7 +652,7 @@
cursor: pointer;
}
.openerp .oe_dropdown_toggle {
- color: rgba(0, 0, 0, 0.3);
+ color: #4C4C4C;
font-weight: normal;
}
.openerp .oe_dropdown_hover:hover .oe_dropdown_menu, .openerp .oe_dropdown_menu.oe_opened {
@@ -807,8 +807,6 @@
padding: 0 0 4px 0;
}
.openerp .oe_login button {
- float: right;
- display: inline-block;
cursor: pointer;
padding: 6px 16px;
border: 1px solid #222222;
@@ -1387,60 +1385,6 @@
.openerp .oe_view_manager table.oe_view_manager_header .oe_view_manager_buttons {
white-space: nowrap;
}
-.openerp .oe_view_manager .oe_view_manager_pager {
- line-height: 26px;
-}
-.openerp .oe_view_manager .oe_view_manager_pager .oe_list_pager_single_page .oe_pager_group {
- display: none;
-}
-.openerp .oe_view_manager .oe_pager_value {
- float: left;
- margin-right: 8px;
-}
-.openerp .oe_view_manager ul.oe_pager_group {
- padding: 0;
- margin: 0;
-}
-.openerp .oe_view_manager .oe_pager_group {
- float: left;
- height: 24px;
- line-height: 24px;
- display: inline-block;
- border: 1px solid #ababab;
- cursor: pointer;
- -moz-border-radius: 5px;
- -webkit-border-radius: 5px;
- border-radius: 5px;
-}
-.openerp .oe_view_manager .oe_pager_group li {
- height: 24px;
- line-height: 24px;
- padding: 0;
- margin: 0;
- list-style-type: none;
- float: left;
- border-right: 1px solid #ababab;
-}
-.openerp .oe_view_manager .oe_pager_group li:last-child {
- border: none;
-}
-.openerp .oe_view_manager .oe_pager_group a {
- color: #4c4c4c;
- padding: 0 8px;
-}
-.openerp .oe_view_manager .oe_pager_group a:hover {
- text-decoration: none;
-}
-.openerp .oe_view_manager .oe_pager_group .active {
- background: #999999;
- -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3) inset;
- -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3) inset;
- box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3) inset;
-}
-.openerp .oe_view_manager .oe_pager_group .active a {
- color: white;
- text-shadow: 0 1px 2px rgba(0, 0, 0, 0.4);
-}
.openerp .oe_view_manager .oe_view_manager_switch {
padding: 0;
margin: 0 0 0 8px;
@@ -1489,6 +1433,60 @@
.openerp .oe_view_manager .oe_view_manager_switch .oe_vm_switch_diagram:after {
content: "f";
}
+.openerp .oe_list_pager {
+ line-height: 26px;
+}
+.openerp .oe_pager_value {
+ float: left;
+ margin-right: 8px;
+}
+.openerp ul.oe_pager_group {
+ padding: 0;
+ margin: 0;
+}
+.openerp .oe_pager_group {
+ float: left;
+ height: 24px;
+ line-height: 24px;
+ display: inline-block;
+ border: 1px solid #ababab;
+ cursor: pointer;
+ -moz-border-radius: 5px;
+ -webkit-border-radius: 5px;
+ border-radius: 5px;
+}
+.openerp .oe_pager_group li {
+ height: 24px;
+ line-height: 24px;
+ padding: 0;
+ margin: 0;
+ list-style-type: none;
+ float: left;
+ border-right: 1px solid #ababab;
+}
+.openerp .oe_pager_group li:last-child {
+ border: none;
+}
+.openerp .oe_pager_group a {
+ color: #4c4c4c;
+ padding: 0 8px;
+}
+.openerp .oe_pager_group a:hover {
+ text-decoration: none;
+}
+.openerp .oe_pager_group .active {
+ background: #999999;
+ -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3) inset;
+ -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3) inset;
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3) inset;
+}
+.openerp .oe_pager_group .active a {
+ color: white;
+ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.4);
+}
+.openerp .oe_list_pager.oe_list_pager_single_page .oe_pager_group {
+ display: none;
+}
.openerp .oe_view_manager_current {
height: 100%;
}
@@ -1520,6 +1518,9 @@
.openerp .oe_popup_form > .oe_formview > .oe_form_pager {
display: none !important;
}
+.openerp .oe_popup_list_pager {
+ float: right;
+}
.openerp .oe_searchview {
cursor: text;
position: relative;
@@ -1842,6 +1843,7 @@
padding: 0;
}
.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_advanced li {
+ cursor: pointer;
position: relative;
list-style: none;
margin: 0;
@@ -1908,14 +1910,22 @@
max-width: 700px;
}
.openerp .oe_view_nocontent .oe_view_nocontent_create {
- background: transparent url(/web/static/src/img/view_empty_arrow.png) no-repeat 7px 0;
margin-top: 0;
padding-top: 35px;
- min-height: 28px;
color: #4c4c4c;
}
+.openerp .oe_view_nocontent .oe_view_nocontent_create:before {
+ content: "";
+ display: inline-block;
+ position: absolute;
+ width: 70px;
+ height: 80px;
+ margin-left: -70px;
+ margin-top: -50px;
+ background: transparent url(/web/static/src/img/view_empty_arrow.png) no-repeat 0px 0px;
+}
.openerp .oe_view_nocontent > p {
- padding-left: 95px;
+ padding-left: 78px;
}
.openerp .oe_view_nocontent .oe_empty_custom_dashboard {
background: transparent url(/web/static/src/img/graph_background.png) no-repeat 0 0;
diff --git a/addons/web/static/src/css/base.sass b/addons/web/static/src/css/base.sass
index c5fcb8008a5..3923fcaf2ff 100644
--- a/addons/web/static/src/css/base.sass
+++ b/addons/web/static/src/css/base.sass
@@ -564,7 +564,7 @@ $sheet-padding: 16px
position: relative
cursor: pointer
.oe_dropdown_toggle
- color: rgba(0,0,0,0.3)
+ color: #4C4C4C
font-weight: normal
.oe_dropdown_hover:hover .oe_dropdown_menu, .oe_dropdown_menu.oe_opened
display: block
@@ -686,8 +686,6 @@ $sheet-padding: 16px
list-style-type: none
padding: 0 0 4px 0
button
- float: right
- display: inline-block
cursor: pointer
padding: 6px 16px
border: 1px solid #222
@@ -1121,47 +1119,6 @@ $sheet-padding: 16px
.oe_view_manager_buttons
white-space: nowrap
// }}}
- // ViewManager.pager {{{
- .oe_view_manager_pager
- line-height: 26px
- .oe_list_pager_single_page .oe_pager_group
- display: none
- .oe_pager_value
- float: left
- margin-right: 8px
- ul.oe_pager_group
- padding: 0
- margin: 0
- .oe_pager_group
- float: left
- height: 24px
- line-height: 24px
- display: inline-block
- border: 1px solid #ababab
- cursor: pointer
- @include radius(5px)
- li
- height: 24px
- line-height: 24px
- padding: 0
- margin: 0
- list-style-type: none
- float: left
- border-right: 1px solid #ababab
- &:last-child
- border: none
- a
- color: #4c4c4c
- padding: 0 8px
- &:hover
- text-decoration: none
- .active
- background: #999
- @include box-shadow(0 1px 4px rgba(0,0,0,0.3) inset)
- a
- color: #fff
- text-shadow: 0 1px 2px rgba(0,0,0,0.4)
- // }}}
// ViewManager.switches {{{
.oe_view_manager_switch
padding: 0
@@ -1202,6 +1159,47 @@ $sheet-padding: 16px
.oe_vm_switch_diagram:after
content: "f"
// }}}
+ // List pager {{{
+ .oe_list_pager
+ line-height: 26px
+ .oe_pager_value
+ float: left
+ margin-right: 8px
+ ul.oe_pager_group
+ padding: 0
+ margin: 0
+ .oe_pager_group
+ float: left
+ height: 24px
+ line-height: 24px
+ display: inline-block
+ border: 1px solid #ababab
+ cursor: pointer
+ @include radius(5px)
+ li
+ height: 24px
+ line-height: 24px
+ padding: 0
+ margin: 0
+ list-style-type: none
+ float: left
+ border-right: 1px solid #ababab
+ &:last-child
+ border: none
+ a
+ color: #4c4c4c
+ padding: 0 8px
+ &:hover
+ text-decoration: none
+ .active
+ background: #999
+ @include box-shadow(0 1px 4px rgba(0,0,0,0.3) inset)
+ a
+ color: #fff
+ text-shadow: 0 1px 2px rgba(0,0,0,0.4)
+ .oe_list_pager.oe_list_pager_single_page .oe_pager_group
+ display: none
+ // }}}
// ViewManager application {{{
.oe_view_manager_current
height: 100%
@@ -1222,9 +1220,11 @@ $sheet-padding: 16px
> .oe_view_manager_header
display: none
// }}}
- // Viewmanager popup {{{
+ // FormPopup {{{
.oe_popup_form > .oe_formview > .oe_form_pager
display: none !important
+ .oe_popup_list_pager
+ float: right
// }}}
// SearchView {{{
.oe_searchview
@@ -1471,6 +1471,7 @@ $sheet-padding: 16px
list-style: none
padding: 0
li
+ cursor: pointer
position: relative
list-style: none
margin: 0
@@ -1533,13 +1534,20 @@ $sheet-padding: 16px
font-size: 125%
max-width: 700px
.oe_view_nocontent_create
- background: transparent url(/web/static/src/img/view_empty_arrow.png) no-repeat 7px 0
margin-top: 0
padding-top: 35px
- min-height: 28px
color: #4c4c4c
+ &:before
+ content: ""
+ display: inline-block
+ position: absolute
+ width: 70px
+ height: 80px
+ margin-left: -70px
+ margin-top: -50px
+ background: transparent url(/web/static/src/img/view_empty_arrow.png) no-repeat 0px 0px
> p
- padding-left: 95px
+ padding-left: 78px
.oe_empty_custom_dashboard
background: transparent url(/web/static/src/img/graph_background.png) no-repeat 0 0
margin-top: -15px
diff --git a/addons/web/static/src/img/view_empty_arrow.png b/addons/web/static/src/img/view_empty_arrow.png
index ef8f3c820d7..99561e3abf3 100644
--- a/addons/web/static/src/img/view_empty_arrow.png
+++ b/addons/web/static/src/img/view_empty_arrow.png
Binary files differ
diff --git a/addons/web/static/src/js/chrome.js b/addons/web/static/src/js/chrome.js
index 77d23d8fa53..66c1f82013c 100644
--- a/addons/web/static/src/js/chrome.js
+++ b/addons/web/static/src/js/chrome.js
@@ -24,7 +24,7 @@ instance.web.Notification = instance.web.Widget.extend({
if (sticky) {
opts.expires = false;
}
- this.$el.notify('create', {
+ return this.$el.notify('create', {
title: title,
text: text
}, opts);
@@ -35,7 +35,7 @@ instance.web.Notification = instance.web.Widget.extend({
if (sticky) {
opts.expires = false;
}
- this.$el.notify('create', 'oe_notification_alert', {
+ return this.$el.notify('create', 'oe_notification_alert', {
title: title,
text: text
}, opts);
@@ -52,8 +52,29 @@ instance.web.dialog = function(element) {
return result;
};
+/**
+ A useful class to handle dialogs.
+
+ Attributes:
+ - $buttons: A jQuery element targeting a dom part where buttons can be added. It always exists
+ during the lifecycle of the dialog.
+*/
instance.web.Dialog = instance.web.Widget.extend({
dialog_title: "",
+ /**
+ Constructor.
+
+ @param {Widget} parent
+ @param {dictionary} options A dictionary that will be forwarded to jQueryUI Dialog. Additionaly, that
+ dictionary can contain the following keys:
+ - buttons: Deprecated. The buttons key is not propagated to jQueryUI Dialog. It must be a dictionary (key = button
+ label, value = click handler) or a list of dictionaries (each element in the dictionary is send to the
+ corresponding method of a jQuery element targeting the <button> tag). It is deprecated because all dialogs
+ in OpenERP must be personalized in some way (button in red, link instead of button, ...) and this
+ feature does not allow that kind of personalization.
+ - destroy_on_close: Default true. If true and the dialog is closed, it is automatically destroyed.
+ @param {jQuery object} content Some content to replace this.$el .
+ */
init: function (parent, options, content) {
var self = this;
this._super(parent);
@@ -66,50 +87,49 @@ instance.web.Dialog = instance.web.Widget.extend({
max_width: '95%',
height: 'auto',
min_height: 0,
- max_height: this.get_height('100%') - 200,
+ max_height: $(window.top).height() - 200,
autoOpen: false,
position: [false, 40],
- buttons: {},
+ buttons: null,
beforeClose: function () {
self.trigger("closing");
},
- resizeStop: this.on_resized
+ resizeStop: function() {
+ self.trigger("resized");
+ },
};
- for (var f in this) {
- if (f.substr(0, 10) == 'on_button_') {
- this.dialog_options.buttons[f.substr(10)] = this[f];
- }
- }
if (options) {
_.extend(this.dialog_options, options);
}
this.on("closing", this, this._closing);
+ this.$buttons = $('<div class="ui-dialog-buttonpane ui-widget-content ui-helper-clearfix"><span class="oe_dialog_custom_buttons"/></div>');
},
- get_options: function(options) {
- var self = this,
- o = _.extend({}, this.dialog_options, options || {});
- _.each(['width', 'height'], function(unit) {
- o[unit] = self['get_' + unit](o[unit]);
- o['min_' + unit] = self['get_' + unit](o['min_' + unit] || 0);
- o['max_' + unit] = self['get_' + unit](o['max_' + unit] || 0);
- if (o[unit] !== 'auto' && o['min_' + unit] && o[unit] < o['min_' + unit]) o[unit] = o['min_' + unit];
- if (o[unit] !== 'auto' && o['max_' + unit] && o[unit] > o['max_' + unit]) o[unit] = o['max_' + unit];
+ _get_options: function() {
+ var self = this;
+ var o = _.extend({}, this.dialog_options);
+ var sizes = {
+ width: $(window.top).width(),
+ height: $(window.top).height(),
+ };
+ _.each(sizes, function(available_size, unit) {
+ o[unit] = self._get_size(o[unit], available_size);
+ o['min_' + unit] = self._get_size(o['min_' + unit] || 0, available_size);
+ o['max_' + unit] = self._get_size(o['max_' + unit] || 0, available_size);
+ if (o[unit] !== 'auto' && o['min_' + unit] && o[unit] < o['min_' + unit]) {
+ o[unit] = o['min_' + unit];
+ }
+ if (o[unit] !== 'auto' && o['max_' + unit] && o[unit] > o['max_' + unit]) {
+ o[unit] = o['max_' + unit];
+ }
});
- if (!o.title && this.dialog_title) {
- o.title = this.dialog_title;
- }
+ o.title = o.title || this.dialog_title;
return o;
},
- get_width: function(val) {
- return this.get_size(val.toString(), $(window.top).width());
- },
- get_height: function(val) {
- return this.get_size(val.toString(), $(window.top).height());
- },
- get_size: function(val, available_size) {
+ _get_size: function(val, available_size) {
+ val = val.toString();
if (val === 'auto') {
return val;
- } else if (val.slice(-1) == "%") {
+ } else if (val.slice(-1) === "%") {
return Math.round(available_size / 100 * parseInt(val.slice(0, -1), 10));
} else {
return parseInt(val, 10);
@@ -122,41 +142,58 @@ instance.web.Dialog = instance.web.Widget.extend({
this._super();
}
},
- open: function(options) {
- if (! this.dialog_inited)
+ /**
+ Opens the popup. Inits the dialog if it is not already inited.
+
+ @return this
+ */
+ open: function() {
+ if (!this.dialog_inited) {
this.init_dialog();
- var o = this.get_options(options);
- this.add_buttons(o.buttons);
- delete(o.buttons);
- this.$buttons.appendTo($("body"));
- instance.web.dialog(this.$el, o).dialog('open');
- this.$el.dialog("widget").find(".ui-dialog-buttonpane").remove();
- this.$buttons.appendTo(this.$el.dialog("widget"));
- if (o.height === 'auto' && o.max_height) {
- this.$el.css({ 'max-height': o.max_height, 'overflow-y': 'auto' });
}
+ this.$el.dialog('open');
+ this.$el.dialog("widget").append(this.$buttons);
return this;
},
- add_buttons: function(buttons) {
+ _add_buttons: function(buttons) {
var self = this;
- _.each(buttons, function(fn, but) {
- var $but = $(QWeb.render('WidgetButton', { widget : { string: but, node: { attrs: {} }}}));
- self.$buttons.append($but);
+ var $customButons = this.$buttons.find('.oe_dialog_custom_buttons').empty();
+ _.each(buttons, function(fn, text) {
+ // buttons can be object or array
+ if (!_.isFunction(fn)) {
+ text = fn.text;
+ fn = fn.click;
+ }
+ var $but = $(QWeb.render('WidgetButton', { widget : { string: text, node: { attrs: {} }}}));
+ $customButons.append($but);
$but.on('click', function(ev) {
fn.call(self.$el, ev);
});
});
},
- init_dialog: function(options) {
+ /**
+ Initializes the popup.
+
+ @return The result returned by start().
+ */
+ init_dialog: function() {
+ var options = this._get_options();
+ if (options.buttons) {
+ this._add_buttons(options.buttons);
+ delete(options.buttons);
+ }
this.renderElement();
- var o = this.get_options(options);
- instance.web.dialog(this.$el, o);
- this.$buttons = $('<div class="ui-dialog-buttonpane ui-widget-content ui-helper-clearfix" />');
- this.$el.dialog("widget").append(this.$buttons);
+ instance.web.dialog(this.$el, options);
+ if (options.height === 'auto' && options.max_height) {
+ this.$el.css({ 'max-height': options.max_height, 'overflow-y': 'auto' });
+ }
this.dialog_inited = true;
var res = this.start();
return res;
},
+ /**
+ Closes the popup, if destroy_on_close was passed to the constructor, it is also destroyed.
+ */
close: function() {
if (this.dialog_inited && this.$el.is(":data(dialog)")) {
this.$el.dialog('close');
@@ -171,9 +208,11 @@ instance.web.Dialog = instance.web.Widget.extend({
this.__tmp_dialog_closing = undefined;
}
},
- on_resized: function() {
- },
+ /**
+ Destroys the popup, also closes it.
+ */
destroy: function () {
+ this.$buttons.remove();
_.each(this.getChildren(), function(el) {
el.destroy();
});
@@ -182,7 +221,7 @@ instance.web.Dialog = instance.web.Widget.extend({
this.close();
this.__tmp_dialog_destroying = undefined;
}
- if (this.dialog_inited && !this.isDestroyed()) {
+ if (this.dialog_inited && !this.isDestroyed() && this.$el.is(":data(dialog)")) {
this.$el.dialog('destroy');
}
this._super();
@@ -726,20 +765,24 @@ instance.web.ChangePassword = instance.web.Widget.extend({
template: "ChangePassword",
start: function() {
var self = this;
- self.$el.validate({
- submitHandler: function (form) {
- self.rpc("/web/session/change_password",{
- 'fields': $(form).serializeArray()
- }).done(function(result) {
- if (result.error) {
- self.display_error(result);
- return;
- } else {
- instance.webclient.on_logout();
- }
- });
- }
- });
+ this.getParent().dialog_title = "Change Password";
+ var $button = self.$el.find('.oe_form_button');
+ $button.appendTo(this.getParent().$buttons);
+ $button.eq(2).click(function(){
+ self.getParent().close();
+ })
+ $button.eq(0).click(function(){
+ self.rpc("/web/session/change_password",{
+ 'fields': $("form[name=change_password_form]").serializeArray()
+ }).done(function(result) {
+ if (result.error) {
+ self.display_error(result);
+ return;
+ } else {
+ instance.webclient.on_logout();
+ }
+ });
+ })
},
display_error: function (error) {
return instance.web.dialog($('<div>'), {
@@ -946,6 +989,14 @@ instance.web.UserMenu = instance.web.Widget.extend({
if(res.company_id[0] > 1)
topbar_name = _.str.sprintf("%s (%s)", topbar_name, res.company_id[1]);
self.$el.find('.oe_topbar_name').text(topbar_name);
+ if(!instance.session.debug) {
+ self.rpc("/web/database/get_list", {}).done( function(result) {
+ if (result.length > 1) {
+ topbar_name = _.str.sprintf("%s (%s)", topbar_name, instance.session.db);
+ }
+ self.$el.find('.oe_topbar_name').text(topbar_name);
+ });
+ }
var avatar_src = self.session.url('/web/binary/image', {model:'res.users', field: 'image_small', id: self.session.uid});
$avatar.attr('src', avatar_src);
});
@@ -1055,9 +1106,7 @@ instance.web.WebClient = instance.web.Client.extend({
start: function() {
var self = this;
return $.when(this._super()).then(function() {
- self.$el.on('click', '.oe_logo', function() {
- self.action_manager.do_action('home');
- });
+ self.$(".oe_logo").attr("href", $.param.fragment("" + window.location, "", 2).slice(0, -1));
if (jQuery.param !== undefined && jQuery.deparam(jQuery.param.querystring()).kitten !== undefined) {
$("body").addClass("kitten-mode-activated");
if ($.blockUI) {
@@ -1114,6 +1163,30 @@ instance.web.WebClient = instance.web.Client.extend({
self.user_menu.do_update();
self.bind_hashchange();
self.set_title();
+ self.check_timezone();
+ },
+ check_timezone: function() {
+ var self = this;
+ var user_offset = instance.session.user_context.tz_offset;
+ var offset = -(new Date().getTimezoneOffset());
+ // _.str.sprintf()'s zero front padding is buggy with signed decimals, so doing it manually
+ var browser_offset = (offset < 0) ? "-" : "+";
+ browser_offset += _.str.sprintf("%02d", Math.abs(offset / 60));
+ browser_offset += _.str.sprintf("%02d", Math.abs(offset % 60));
+ if (browser_offset !== user_offset) {
+ var notification = this.do_warn(_t("Timezone"), QWeb.render('WebClient.timezone_notification', {
+ user_timezone: instance.session.user_context.tz || 'UTC',
+ user_offset: user_offset,
+ browser_offset: browser_offset,
+ }), true);
+ notification.element.find('.oe_webclient_timezone_notification').on('click', function() {
+ notification.close();
+ }).find('a').on('click', function() {
+ notification.close();
+ self.user_menu.on_menu_settings();
+ return false;
+ });
+ }
},
destroy_content: function() {
_.each(_.clone(this.getChildren()), function(el) {
@@ -1130,11 +1203,11 @@ instance.web.WebClient = instance.web.Client.extend({
},
do_notify: function() {
var n = this.notification;
- n.notify.apply(n, arguments);
+ return n.notify.apply(n, arguments);
},
do_warn: function() {
var n = this.notification;
- n.warn.apply(n, arguments);
+ return n.warn.apply(n, arguments);
},
on_logout: function() {
var self = this;
diff --git a/addons/web/static/src/js/corelib.js b/addons/web/static/src/js/corelib.js
index d05a42061de..55984162a55 100644
--- a/addons/web/static/src/js/corelib.js
+++ b/addons/web/static/src/js/corelib.js
@@ -430,6 +430,85 @@ instance.web.PropertiesMixin = _.extend({}, instance.web.EventDispatcherMixin, {
// Classes
/**
+ A class containing common utility methods useful when working with OpenERP as well as the PropertiesMixin.
+*/
+instance.web.Controller = instance.web.Class.extend(instance.web.PropertiesMixin, {
+ /**
+ * Constructs the object and sets its parent if a parent is given.
+ *
+ * @param {instance.web.Controller} parent Binds the current instance to the given Controller instance.
+ * When that controller is destroyed by calling destroy(), the current instance will be
+ * destroyed too. Can be null.
+ */
+ init: function(parent) {
+ instance.web.PropertiesMixin.init.call(this);
+ this.setParent(parent);
+ },
+ /**
+ * Proxies a method of the object, in order to keep the right ``this`` on
+ * method invocations.
+ *
+ * This method is similar to ``Function.prototype.bind`` or ``_.bind``, and
+ * even more so to ``jQuery.proxy`` with a fundamental difference: its
+ * resolution of the method being called is lazy, meaning it will use the
+ * method as it is when the proxy is called, not when the proxy is created.
+ *
+ * Other methods will fix the bound method to what it is when creating the
+ * binding/proxy, which is fine in most javascript code but problematic in
+ * OpenERP Web where developers may want to replace existing callbacks with
+ * theirs.
+ *
+ * The semantics of this precisely replace closing over the method call.
+ *
+ * @param {String|Function} method function or name of the method to invoke
+ * @returns {Function} proxied method
+ */
+ proxy: function (method) {
+ var self = this;
+ return function () {
+ var fn = (typeof method === 'string') ? self[method] : method;
+ return fn.apply(self, arguments);
+ }
+ },
+ /**
+ * Informs the action manager to do an action. This supposes that
+ * the action manager can be found amongst the ancestors of the current widget.
+ * If that's not the case this method will simply return `false`.
+ */
+ do_action: function() {
+ var parent = this.getParent();
+ if (parent) {
+ return parent.do_action.apply(parent, arguments);
+ }
+ return false;
+ },
+ do_notify: function() {
+ if (this.getParent()) {
+ return this.getParent().do_notify.apply(this,arguments);
+ }
+ return false;
+ },
+ do_warn: function() {
+ if (this.getParent()) {
+ return this.getParent().do_warn.apply(this,arguments);
+ }
+ return false;
+ },
+ rpc: function(url, data, options) {
+ var def = $.Deferred();
+ var self = this;
+ instance.session.rpc(url, data, options).done(function() {
+ if (!self.isDestroyed())
+ def.resolve.apply(def, arguments);
+ }).fail(function() {
+ if (!self.isDestroyed())
+ def.reject.apply(def, arguments);
+ });
+ return def.promise();
+ }
+});
+
+/**
* Base class for all visual components. Provides a lot of functionalities helpful
* for the management of a part of the DOM.
*
@@ -477,7 +556,7 @@ instance.web.PropertiesMixin = _.extend({}, instance.web.EventDispatcherMixin, {
*
* That will kill the widget in a clean way and erase its content from the dom.
*/
-instance.web.Widget = instance.web.Class.extend(instance.web.PropertiesMixin, {
+instance.web.Widget = instance.web.Controller.extend({
// Backbone-ish API
tagName: 'div',
id: null,
@@ -501,8 +580,7 @@ instance.web.Widget = instance.web.Class.extend(instance.web.PropertiesMixin, {
* destroyed too. Can be null.
*/
init: function(parent) {
- instance.web.PropertiesMixin.init.call(this);
- this.setParent(parent);
+ this._super(parent);
// Bind on_/do_* methods to this
// We might remove this automatic binding in the future
for (var name in this) {
@@ -605,32 +683,6 @@ instance.web.Widget = instance.web.Class.extend(instance.web.PropertiesMixin, {
return $.when();
},
/**
- * Proxies a method of the object, in order to keep the right ``this`` on
- * method invocations.
- *
- * This method is similar to ``Function.prototype.bind`` or ``_.bind``, and
- * even more so to ``jQuery.proxy`` with a fundamental difference: its
- * resolution of the method being called is lazy, meaning it will use the
- * method as it is when the proxy is called, not when the proxy is created.
- *
- * Other methods will fix the bound method to what it is when creating the
- * binding/proxy, which is fine in most javascript code but problematic in
- * OpenERP Web where developers may want to replace existing callbacks with
- * theirs.
- *
- * The semantics of this precisely replace closing over the method call.
- *
- * @param {String|Function} method function or name of the method to invoke
- * @returns {Function} proxied method
- */
- proxy: function (method) {
- var self = this;
- return function () {
- var fn = (typeof method === 'string') ? self[method] : method;
- return fn.apply(self, arguments);
- }
- },
- /**
* Renders the element. The default implementation renders the widget using QWeb,
* `this.template` must be defined. The context given to QWeb contains the "widget"
* key that references `this`.
@@ -749,42 +801,6 @@ instance.web.Widget = instance.web.Class.extend(instance.web.PropertiesMixin, {
*/
$: function(selector) {
return this.$el.find(selector);
- },
- /**
- * Informs the action manager to do an action. This supposes that
- * the action manager can be found amongst the ancestors of the current widget.
- * If that's not the case this method will simply return `false`.
- */
- do_action: function() {
- var parent = this.getParent();
- if (parent) {
- return parent.do_action.apply(parent, arguments);
- }
- return false;
- },
- do_notify: function() {
- if (this.getParent()) {
- return this.getParent().do_notify.apply(this,arguments);
- }
- return false;
- },
- do_warn: function() {
- if (this.getParent()) {
- return this.getParent().do_warn.apply(this,arguments);
- }
- return false;
- },
- rpc: function(url, data, options) {
- var def = $.Deferred();
- var self = this;
- instance.session.rpc(url, data, options).done(function() {
- if (!self.isDestroyed())
- def.resolve.apply(def, arguments);
- }).fail(function() {
- if (!self.isDestroyed())
- def.reject.apply(def, arguments);
- });
- return def.promise();
}
});
diff --git a/addons/web/static/src/js/coresetup.js b/addons/web/static/src/js/coresetup.js
index 7ddefa301f2..fa1348a5f8c 100644
--- a/addons/web/static/src/js/coresetup.js
+++ b/addons/web/static/src/js/coresetup.js
@@ -119,6 +119,9 @@ instance.web.Session = instance.web.JsonRPC.extend( /** @lends instance.web.Sess
},
session_logout: function() {
this.set_cookie('session_id', '');
+ var url = "#";
+ $.bbq.pushState(url);
+ window.location.search = "";
return this.rpc("/web/session/destroy", {});
},
get_cookie: function (name) {
@@ -543,12 +546,12 @@ instance.web._lt = function (s) {
return {toString: function () { return instance.web._t(s); }}
};
instance.web.qweb = new QWeb2.Engine();
-instance.web.qweb.default_dict['__debug__'] = instance.session.debug; // Which one ?
instance.web.qweb.debug = instance.session.debug;
instance.web.qweb.default_dict = {
'_' : _,
'_t' : instance.web._t,
'JSON': JSON,
+ '__debug__': instance.session.debug,
};
instance.web.qweb.preprocess_node = function() {
// Note that 'this' is the Qweb Node
diff --git a/addons/web/static/src/js/data_export.js b/addons/web/static/src/js/data_export.js
index 477d89c99b3..e38d21d1cd2 100644
--- a/addons/web/static/src/js/data_export.js
+++ b/addons/web/static/src/js/data_export.js
@@ -22,7 +22,6 @@ instance.web.DataExport = instance.web.Dialog.extend({
start: function() {
var self = this;
this._super.apply(this, arguments);
- this.open();
self.$el.removeClass('ui-dialog-content ui-widget-content');
self.$el.find('#add_field').click(function() {
if ($('#field-tree-structure tr.ui-selected')) {
diff --git a/addons/web/static/src/js/search.js b/addons/web/static/src/js/search.js
index b7514e81c88..29fa71b5a97 100644
--- a/addons/web/static/src/js/search.js
+++ b/addons/web/static/src/js/search.js
@@ -1890,6 +1890,9 @@ instance.web.search.ExtendedSearchProposition.DateTime = instance.web.search.Ext
get_value: function() {
return this.datewidget.get_value();
},
+ toString: function () {
+ return instance.web.format_value(this.get_value(), { type:"datetime" });
+ },
start: function() {
var ready = this._super();
this.datewidget = new (this.widget())(this);
@@ -1898,7 +1901,10 @@ instance.web.search.ExtendedSearchProposition.DateTime = instance.web.search.Ext
}
});
instance.web.search.ExtendedSearchProposition.Date = instance.web.search.ExtendedSearchProposition.DateTime.extend({
- widget: function () { return instance.web.DateWidget; }
+ widget: function () { return instance.web.DateWidget; },
+ toString: function () {
+ return instance.web.format_value(this.get_value(), { type:"date" });
+ }
});
instance.web.search.ExtendedSearchProposition.Integer = instance.web.search.ExtendedSearchProposition.Field.extend({
template: 'SearchView.extended_search.proposition.integer',
@@ -1915,7 +1921,8 @@ instance.web.search.ExtendedSearchProposition.Integer = instance.web.search.Exte
},
get_value: function() {
try {
- return instance.web.parse_value(this.$el.val(), {'widget': 'integer'});
+ var val =this.$el.val();
+ return instance.web.parse_value(val == "" ? 0 : val, {'widget': 'integer'});
} catch (e) {
return "";
}
@@ -1939,7 +1946,8 @@ instance.web.search.ExtendedSearchProposition.Float = instance.web.search.Extend
},
get_value: function() {
try {
- return instance.web.parse_value(this.$el.val(), {'widget': 'float'});
+ var val =this.$el.val();
+ return instance.web.parse_value(val == "" ? 0.0 : val, {'widget': 'float'});
} catch (e) {
return "";
}
diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js
index 5d94cdb5d89..49f4e30815f 100644
--- a/addons/web/static/src/js/view_form.js
+++ b/addons/web/static/src/js/view_form.js
@@ -195,7 +195,6 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
this.sidebar.add_items('other', _.compact([
self.is_action_enabled('delete') && { label: _t('Delete'), callback: self.on_button_delete },
self.is_action_enabled('create') && { label: _t('Duplicate'), callback: self.on_button_duplicate },
- { label: _t('Set Default'), callback: function (item) { self.open_defaults_dialog(); } }
]));
}
@@ -1003,8 +1002,8 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
}
return true;
},
- sidebar_context: function () {
- return this.save().then(_.bind(function() {return this.get_fields_values();}, this));
+ sidebar_eval_context: function () {
+ return $.when(this.build_eval_context());
},
open_defaults_dialog: function () {
var self = this;
@@ -1028,7 +1027,8 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|| field.get("readonly")
|| field.field.type === 'one2many'
|| field.field.type === 'many2many'
- || field.field.type === 'binary') {
+ || field.field.type === 'binary'
+ || field.password) {
return false;
}
@@ -1676,11 +1676,11 @@ instance.web.form.compute_domain = function(expr, fields) {
switch (op.toLowerCase()) {
case '=':
case '==':
- stack.push(field_value == val);
+ stack.push(_.isEqual(field_value, val));
break;
case '!=':
case '<>':
- stack.push(field_value != val);
+ stack.push(!_.isEqual(field_value, val));
break;
case '<':
stack.push(field_value < val);
@@ -2241,7 +2241,9 @@ instance.web.form.FieldChar = instance.web.form.AbstractField.extend(instance.we
var self = this;
var $input = this.$el.find('input');
$input.change(function() {
- self.internal_set_value(self.parse_value($input.val()));
+ if(self.is_syntax_valid()){
+ self.internal_set_value(self.parse_value($input.val()));
+ }
});
this.setupFocus($input);
},
@@ -4020,6 +4022,9 @@ instance.web.form.FieldMany2ManyTags = instance.web.form.AbstractField.extend(in
}
this._super(value_);
},
+ is_false: function() {
+ return _(this.get("value")).isEmpty();
+ },
get_value: function() {
var tmp = [commands.replace_with(this.get("value"))];
return tmp;
@@ -4230,16 +4235,14 @@ instance.web.form.FieldMany2ManyKanban = instance.web.form.AbstractField.extend(
var self = this;
self.load_view();
- this.is_loaded.done(function() {
- self.on("change:effective_readonly", self, function() {
- self.is_loaded = self.is_loaded.then(function() {
- self.kanban_view.destroy();
- return $.when(self.load_view()).done(function() {
- self.render_value();
- });
+ self.on("change:effective_readonly", self, function() {
+ self.is_loaded = self.is_loaded.then(function() {
+ self.kanban_view.destroy();
+ return $.when(self.load_view()).done(function() {
+ self.render_value();
});
});
- })
+ });
},
set_value: function(value_) {
value_ = value_ || [];
@@ -4622,6 +4625,8 @@ instance.web.form.SelectCreatePopup = instance.web.form.AbstractFormPopup.extend
'selectable': !self.options.disable_multiple_selection,
'import_enabled': false,
'$buttons': self.$buttonpane,
+ 'disable_editable_mode': true,
+ '$pager': self.$('.oe_popup_list_pager'),
}, self.options.list_view_options || {}));
self.view_list.on('edit:before', self, function (e) {
e.cancel = true;
@@ -4781,6 +4786,7 @@ instance.web.form.FieldBinary = instance.web.form.AbstractField.extend(instance.
this._super(field_manager, node);
this.binary_value = false;
this.useFileAPI = !!window.FileReader;
+ this.max_upload_size = 25 * 1024 * 1024; // 25Mo
if (!this.useFileAPI) {
this.fileupload_id = _.uniqueId('oe_fileupload');
$(window).on(this.fileupload_id, function() {
@@ -4806,6 +4812,11 @@ instance.web.form.FieldBinary = instance.web.form.AbstractField.extend(instance.
if ((this.useFileAPI && file_node.files.length) || (!this.useFileAPI && $(file_node).val() !== '')) {
if (this.useFileAPI) {
var file = file_node.files[0];
+ if (file.size > this.max_upload_size) {
+ var msg = _t("The selected file exceed the maximum file size of %s.");
+ instance.webclient.notification.warn(_t("File upload"), _.str.sprintf(msg, instance.web.human_size(this.max_upload_size)));
+ return false;
+ }
var filereader = new FileReader();
filereader.readAsDataURL(file);
filereader.onloadend = function(upload) {
diff --git a/addons/web/static/src/js/view_list.js b/addons/web/static/src/js/view_list.js
index 43d7ac6423e..13c41d48599 100644
--- a/addons/web/static/src/js/view_list.js
+++ b/addons/web/static/src/js/view_list.js
@@ -21,6 +21,8 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
// whether the view rows can be reordered (via vertical drag & drop)
'reorderable': true,
'action_buttons': true,
+ //whether the editable property of the view has to be disabled
+ 'disable_editable_mode': false,
},
view_type: 'tree',
events: {
diff --git a/addons/web/static/src/js/view_list_editable.js b/addons/web/static/src/js/view_list_editable.js
index 281e49c7fa2..7fe6a8ec42b 100644
--- a/addons/web/static/src/js/view_list_editable.js
+++ b/addons/web/static/src/js/view_list_editable.js
@@ -94,9 +94,10 @@ openerp.web.list_editable = function (instance) {
});
},
editable: function () {
- return this.fields_view.arch.attrs.editable
+ return !this.options.disable_editable_mode
+ && (this.fields_view.arch.attrs.editable
|| this._context_editable
- || this.options.editable;
+ || this.options.editable);
},
/**
* Replace do_search to handle editability process
diff --git a/addons/web/static/src/js/views.js b/addons/web/static/src/js/views.js
index 7a854150129..def265db0bb 100644
--- a/addons/web/static/src/js/views.js
+++ b/addons/web/static/src/js/views.js
@@ -330,7 +330,6 @@ instance.web.ActionManager = instance.web.Widget.extend({
dialogClass: executor.klass,
});
this.dialog.on("closing", null, options.on_close);
- this.dialog.init_dialog();
this.dialog.dialog_title = executor.action.name;
if (widget instanceof instance.web.ViewManager) {
_.extend(widget.flags, {
@@ -829,6 +828,9 @@ instance.web.ViewManagerAction = instance.web.ViewManager.extend({
case 'toggle_layout_outline':
current_view.rendering_engine.toggle_layout_debugging();
break;
+ case 'set_defaults':
+ current_view.open_defaults_dialog();
+ break;
case 'translate':
this.do_action({
name: "Technical Translation",
@@ -999,11 +1001,7 @@ instance.web.Sidebar = instance.web.Widget.extend({
this.fileupload_id = _.uniqueId('oe_fileupload');
$(window).on(this.fileupload_id, function() {
var args = [].slice.call(arguments).slice(1);
- if (args[0] && args[0].error) {
- alert(args[0].error);
- } else {
- self.do_attachement_update(self.dataset, self.model_id);
- }
+ self.do_attachement_update(self.dataset, self.model_id,args);
instance.web.unblockUI();
});
},
@@ -1084,24 +1082,27 @@ instance.web.Sidebar = instance.web.Widget.extend({
},
on_item_action_clicked: function(item) {
var self = this;
- self.getParent().sidebar_context().done(function (context) {
+ self.getParent().sidebar_eval_context().done(function (sidebar_eval_context) {
var ids = self.getParent().get_selected_ids();
if (ids.length == 0) {
instance.web.dialog($("<div />").text(_t("You must choose at least one record.")), { title: _t("Warning"), modal: true });
return false;
}
+ var active_ids_context = {
+ active_id: ids[0],
+ active_ids: ids,
+ active_model: self.getParent().dataset.model
+ };
var c = instance.web.pyeval.eval('context',
- new instance.web.CompoundContext({
- active_id: ids[0],
- active_ids: ids,
- active_model: self.getParent().dataset.model
- }, context));
+ new instance.web.CompoundContext(
+ sidebar_eval_context, active_ids_context));
self.rpc("/web/action/load", {
action_id: item.action.id,
context: c
}).done(function(result) {
result.context = new instance.web.CompoundContext(
- c, result.context);
+ result.context || {}, active_ids_context)
+ .set_eval_context(c);
result.flags = result.flags || {};
result.flags.new_window = true;
self.do_action(result, {
@@ -1113,9 +1114,21 @@ instance.web.Sidebar = instance.web.Widget.extend({
});
});
},
- do_attachement_update: function(dataset, model_id) {
+ do_attachement_update: function(dataset, model_id,args) {
+ var self = this;
this.dataset = dataset;
this.model_id = model_id;
+ if (args && args[0]["erorr"]) {
+ instance.web.dialog($('<div>'),{
+ modal: true,
+ title: "OpenERP " + _.str.capitalize(args[0]["title"]),
+ buttons: [{
+ text: _t("Ok"),
+ click: function(){
+ $(this).dialog("close");
+ }}]
+ }).html(args[0]["erorr"]);
+ }
if (!model_id) {
this.on_attachments_loaded([]);
} else {
@@ -1349,8 +1362,8 @@ instance.web.View = instance.web.Widget.extend({
on_sidebar_export: function() {
new instance.web.DataExport(this, this.dataset).open();
},
- sidebar_context: function () {
- return $.when();
+ sidebar_eval_context: function () {
+ return $.when({});
},
/**
* Asks the view to reload itself, if the reloading is asynchronous should
diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml
index 723f6707cce..62d8a872f25 100644
--- a/addons/web/static/src/xml/base.xml
+++ b/addons/web/static/src/xml/base.xml
@@ -71,9 +71,9 @@
</div>
<ul>
<li>Username</li>
- <li><input type="text" name="login" t-att-value="widget.selected_login || ''" autofocus="autofocus"/></li>
+ <li><input name="login" type="text" t-att-value="widget.selected_login || ''" autofocus="autofocus"/></li>
<li>Password</li>
- <li><input type="password" name="password" t-att-value="widget.selected_password || ''"/></li>
+ <li><input name="password" type="password" t-att-value="widget.selected_password || ''"/></li>
<li><button name="submit">Log in</button></li>
</ul>
</form>
@@ -98,7 +98,6 @@
</select>
</t>
-
<t t-name="DatabaseManager">
<div class="oe_view_manager_view_form">
<div class="oe_form">
@@ -315,26 +314,32 @@
<t t-name="ChangePassword">
<form name="change_password_form" method="POST">
+ <div class="oe_form">
<table align="center">
<tr>
- <td><label for="old_pwd">Old Password:</label></td>
- <td><input type="password" name="old_pwd"
+ <td class="oe_form_group_cell oe_form_group_cell_label"><label for="old_pwd" class="oe_form_label">Old Password:</label></td>
+ <td class="oe_form_group_cell"><input type="password" name="old_pwd"
minlength="1" autofocus="autofocus"/></td>
</tr>
<tr>
- <td><label for="new_password">New Password:</label></td>
- <td><input type="password" name="new_password"
+ <td class="oe_form_group_cell oe_form_group_cell_label"><label for="new_password" class="oe_form_label">New Password:</label></td>
+ <td class="oe_form_group_cell"><input type="password" name="new_password"
minlength="1"/></td>
</tr>
<tr>
- <td><label for="confirm_pwd">Confirm Password:</label></td>
- <td><input type="password" name="confirm_pwd"
+ <td class="oe_form_group_cell oe_form_group_cell_label"><label for="confirm_pwd" class="oe_form_label">Confirm New Password:</label></td>
+ <td class="oe_form_group_cell"><input type="password" name="confirm_pwd"
minlength="1"/></td>
</tr>
<tr>
- <td colspan="2" align="right"><button class="oe_button">Change Password</button></td>
+ <td colspan="2" align="right">
+ <button class='oe_button oe_form_button'>Change Password</button>
+ <span class="oe_fade oe_form_button"> or </span>
+ <button type="button" class="oe_button oe_form_button_cancel oe_form_button oe_link" href="javascript:void(0)"><span>Cancel</span></button>
+ </td>
</tr>
</table>
+ </div>
</form>
</t>
@@ -385,7 +390,7 @@
<t t-esc="menu.name"/>
<t t-if="menu.needaction_enabled and menu.needaction_counter">
<div class="oe_tag oe_tag_dark oe_menu_counter">
- <t t-esc="menu.needaction_counter"/>
+ <t t-if="menu.needaction_counter &gt; 99"> 99+ </t><t t-if="menu.needaction_counter &lt;= 99"> <t t-esc="menu.needaction_counter"/> </t>
</div>
</t>
</a>
@@ -443,6 +448,20 @@
</table>
</div>
</t>
+<t t-name="WebClient.timezone_notification">
+ <div class="oe_webclient_timezone_notification">
+ <p>Your user's preference timezone does not match your browser timezone:</p>
+
+ <dl>
+ <dt>User's timezone</dt>
+ <dd><t t-esc="user_timezone"/> (<t t-esc="user_offset"/>)</dd>
+ <dt>Browser's timezone</dt>
+ <dd><t t-esc="browser_offset"/></dd>
+ </dl>
+
+ <p><a href="#">Click here to change your user's timezone.</a></p>
+ </div>
+</t>
<t t-name="EmbedClient">
<div class="openerp">
@@ -501,8 +520,11 @@
</t>
<t t-name="ViewManagerDebug">
<option value="">Debug View#<t t-esc="view.fields_view.view_id"/></option>
- <option t-if="view_manager.active_view === 'form'" value="perm_read" data-views="form,page">View Log (perm_read)</option>
- <option t-if="view_manager.active_view === 'form'" value="toggle_layout_outline">Toggle Form Layout Outline</option>
+ <t t-if="view_manager.active_view === 'form'">
+ <option value="perm_read">View Log (perm_read)</option>
+ <option value="toggle_layout_outline">Toggle Form Layout Outline</option>
+ <option value="set_defaults">Set Defaults</option>
+ </t>
<option value="tests">JS Tests</option>
<option value="fields">View Fields</option>
<option value="fvg">Fields View Get</option>
@@ -673,13 +695,15 @@
</tr>
</tfoot>
</table>
-<div t-name="ListView.buttons" class="oe_list_buttons">
+<t t-name="ListView.buttons">
+ <div class="oe_list_buttons">
<t t-if="!widget.no_leaf and widget.options.action_buttons !== false and widget.options.addable and widget.is_action_enabled('create')">
<button type="button" class="oe_button oe_list_add oe_highlight">
<t t-esc="widget.options.addable"/>
</button>
</t>
-</div>
+ </div>
+</t>
<t t-name="ListView.pager">
<div class="oe_list_pager" t-att-colspan="widget.columns_count">
<t t-if="!widget.no_leaf and widget.options.pager !== false" t-call="ViewPager">
@@ -1025,7 +1049,7 @@
t-att-autofocus="widget.node.attrs.autofocus"
t-att-id="widget.id_for_label">
<t t-foreach="widget.values" t-as="option">
- <option><t t-esc="option[1]"/></option>
+ <option><t t-esc="widget.node.attrs.placeholder" t-if="option[0] == false and widget.node.attrs.placeholder"/><t t-esc="option[1]" t-if="option[0] != false"/></option>
</t>
</select>
</span>
@@ -1303,6 +1327,11 @@
</tr>
<tr style="width:100%">
<td style="width:100%">
+ <div class="oe_popup_list_pager"></div>
+ </td>
+ </tr>
+ <tr style="width:100%">
+ <td style="width:100%">
<div class="oe_popup_list" style="width:100%"></div>
</td>
</tr>
@@ -1589,10 +1618,10 @@
<span/>
</t>
<t t-name="SearchView.extended_search.proposition.integer">
- <input type="number" class="field_integer" step="1"/>
+ <input type="number" class="field_integer" value = "0" step="1"/>
</t>
<t t-name="SearchView.extended_search.proposition.float">
- <input type="number" class="field_float" step="0.01"/>
+ <input type="number" class="field_float" value = "0.0" step="0.01"/>
</t>
<t t-name="SearchView.extended_search.proposition.selection">
<select>