summaryrefslogtreecommitdiff
path: root/addons/web/static/src
diff options
context:
space:
mode:
authorVidhin Mehta (OpenERP) <vme@tinyerp.com>2011-11-22 11:47:53 +0530
committerVidhin Mehta (OpenERP) <vme@tinyerp.com>2011-11-22 11:47:53 +0530
commit67e7ba0d0037e82196d34c4a0e57244ea40f015e (patch)
treeaf48ad5669afedcb28bd3ab9a862f3ec5a50e332 /addons/web/static/src
parentd206d8dc42f4c364a407637d346e18d01ae4eaad (diff)
parent1a2277c1407315546559eb8ed838018febcba9d5 (diff)
[MERGE]resove conflict and merge with main branch.
bzr revid: vme@tinyerp.com-20111122061753-5uxqy3mxw0eedthn
Diffstat (limited to 'addons/web/static/src')
-rw-r--r--addons/web/static/src/css/base.css60
-rw-r--r--addons/web/static/src/js/chrome.js55
-rw-r--r--addons/web/static/src/js/core.js31
-rw-r--r--addons/web/static/src/js/data.js11
-rw-r--r--addons/web/static/src/js/formats.js14
-rw-r--r--addons/web/static/src/js/search.js10
-rw-r--r--addons/web/static/src/js/view_editor.js288
-rw-r--r--addons/web/static/src/js/view_form.js159
-rw-r--r--addons/web/static/src/js/view_list.js18
-rw-r--r--addons/web/static/src/js/view_tree.js17
-rw-r--r--addons/web/static/src/js/views.js8
-rw-r--r--addons/web/static/src/xml/base.xml22
12 files changed, 389 insertions, 304 deletions
diff --git a/addons/web/static/src/css/base.css b/addons/web/static/src/css/base.css
index c061353d113..d8f31af1d35 100644
--- a/addons/web/static/src/css/base.css
+++ b/addons/web/static/src/css/base.css
@@ -809,33 +809,33 @@ label.error {
also on the first and last children of the first and last row
*/
.openerp .oe-listview table.oe-listview-content {
- -webkit-border-radius: 7px;
- -moz-border-radius: 7px;
- border-radius: 7px;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
}
.openerp .oe-listview table.oe-listview-content thead tr:first-child th:first-child {
- -webkit-border-top-left-radius: 7px;
- -moz-border-radius-topleft: 7px;
- border-top-left-radius: 7px;
+ -webkit-border-top-left-radius: 4px;
+ -moz-border-radius-topleft: 4px;
+ border-top-left-radius: 4px;
}
.openerp .oe-listview table.oe-listview-content thead tr:first-child th:last-child {
- -webkit-border-top-right-radius: 7px;
- -moz-border-radius-topright: 7px;
- border-top-right-radius: 7px;
+ -webkit-border-top-right-radius: 4px;
+ -moz-border-radius-topright: 4px;
+ border-top-right-radius: 4px;
}
.openerp .oe-listview table.oe-listview-content tfoot tr:last-child th:first-child,
.openerp .oe-listview table.oe-listview-content tfoot tr:last-child td:first-child,
.openerp .oe-listview table.oe-listview-content tbody:last-child tr:last-child th:first-child {
- -webkit-border-bottom-left-radius: 7px;
- -moz-border-radius-bottomleft: 7px;
- border-bottom-left-radius: 7px;
+ -webkit-border-bottom-left-radius: 4px;
+ -moz-border-radius-bottomleft: 4px;
+ border-bottom-left-radius: 4px;
}
.openerp .oe-listview table.oe-listview-content tfoot tr:last-child th:last-child,
.openerp .oe-listview table.oe-listview-content tfoot tr:last-child td:last-child,
.openerp .oe-listview table.oe-listview-content tbody:last-child tr:last-child td:last-child {
- -webkit-border-bottom-right-radius: 7px;
- -moz-border-radius-bottomright: 7px;
- border-bottom-right-radius: 7px;
+ -webkit-border-bottom-right-radius: 4px;
+ -moz-border-radius-bottomright: 4px;
+ border-bottom-right-radius: 4px;
}
/* Notebook */
@@ -967,20 +967,25 @@ label.error {
.openerp .oe_forms textarea {
resize:vertical;
}
-.openerp .oe_forms input[type="text"], .openerp .oe_forms input[type="password"], .openerp .oe_forms select, .openerp .oe_forms .button {
+.openerp .oe_forms input[type="text"], .openerp .oe_forms input[type="password"], .openerp .oe_forms select, .openerp .oe_forms .oe_button {
height: 22px;
}
.openerp .oe_forms input.field_datetime {
min-width: 11em;
}
-.openerp .oe_forms.oe_frame .oe_datepicker_root {
- width: 100%;
-}
-.openerp .oe_forms .button {
+.openerp .oe_forms .oe_button {
color: #4c4c4c;
white-space: nowrap;
+ min-width: 100%;
+ width: 100%;
}
-.openerp .oe_forms .button span {
+@-moz-document url-prefix() {
+ /* Strange firefox behaviour on width: 100% + white-space: nowrap */
+ .openerp .oe_forms .oe_button {
+ width: auto;
+ }
+}
+.openerp .oe_forms .oe_button span {
position: relative;
vertical-align: top;
}
@@ -989,13 +994,20 @@ label.error {
cursor: pointer;
right: 5px;
top: 3px;
+ z-index: 2;
+}
+.openerp .oe_datepicker_container {
+ position: absolute;
+ top: 0;
+ right: 0;
+ display: none;
}
.openerp .oe_datepicker_root {
position: relative;
display: inline-block;
}
-.openerp .oe_datepicker_root input[type="text"] {
- min-width: 160px;
+.openerp .oe_forms.oe_frame .oe_datepicker_root {
+ width: 100%;
}
.openerp .oe_input_icon_disabled {
position: absolute;
@@ -1003,7 +1015,7 @@ label.error {
opacity: 0.5;
filter:alpha(opacity=50);
right: 5px;
- top: 5px;
+ top: 3px;
}
.openerp img.oe_field_translate {
margin-left: -21px;
diff --git a/addons/web/static/src/js/chrome.js b/addons/web/static/src/js/chrome.js
index 25b73ee59d5..2b838ee3743 100644
--- a/addons/web/static/src/js/chrome.js
+++ b/addons/web/static/src/js/chrome.js
@@ -158,7 +158,7 @@ openerp.web.CrashManager = openerp.web.CallbackEnabled.extend({
},
on_managed_error: function(error) {
$('<div>' + QWeb.render('DialogWarning', {error: error}) + '</div>').dialog({
- title: "OpenERP " + _.capitalize(error.type),
+ title: "OpenERP " + _.str.capitalize(error.type),
buttons: {
Ok: function() {
$(this).dialog("close");
@@ -168,7 +168,7 @@ openerp.web.CrashManager = openerp.web.CallbackEnabled.extend({
},
on_traceback: function(error) {
var dialog = new openerp.web.Dialog(this, {
- title: "OpenERP " + _.capitalize(error.type),
+ title: "OpenERP " + _.str.capitalize(error.type),
autoOpen: true,
width: '90%',
height: '90%',
@@ -184,7 +184,8 @@ openerp.web.CrashManager = openerp.web.CallbackEnabled.extend({
}
});
-openerp.web.Loading = openerp.web.Widget.extend(/** @lends openerp.web.Loading# */{
+openerp.web.Loading = openerp.web.Widget.extend(/** @lends openerp.web.Loading# */{
+ template: 'Loading',
/**
* @constructs openerp.web.Loading
* @extends openerp.web.Widget
@@ -192,8 +193,8 @@ openerp.web.Loading = openerp.web.Widget.extend(/** @lends openerp.web.Loading#
* @param parent
* @param element_id
*/
- init: function(parent, element_id) {
- this._super(parent, element_id);
+ init: function(parent) {
+ this._super(parent);
this.count = 0;
this.blocked_ui = false;
this.session.on_rpc_request.add_first(this.on_rpc_event, 1);
@@ -210,12 +211,13 @@ openerp.web.Loading = openerp.web.Widget.extend(/** @lends openerp.web.Loading#
}
this.count += increment;
- if (this.count) {
+ if (this.count > 0) {
//this.$element.html(QWeb.render("Loading", {}));
this.$element.html("Loading ("+this.count+")");
this.$element.show();
this.widget_parent.$element.addClass('loading');
} else {
+ this.count = 0;
clearTimeout(this.long_running_timer);
// Don't unblock if blocked by somebody else
if (self.blocked_ui) {
@@ -242,10 +244,8 @@ openerp.web.Database = openerp.web.Widget.extend(/** @lends openerp.web.Database
this.$option_id = $('#' + option_id);
},
start: function() {
+ this._super();
this.$element.html(QWeb.render("Database", this));
- this.$element.closest(".openerp")
- .removeClass("login-mode")
- .addClass("database_block");
var self = this;
var fetch_db = this.rpc("/web/database/get_list", {}, function(result) {
@@ -266,23 +266,30 @@ openerp.web.Database = openerp.web.Widget.extend(/** @lends openerp.web.Database
this.$element.find('#db-restore').click(this.do_restore);
this.$element.find('#db-change-password').click(this.do_change_password);
this.$element.find('#back-to-login').click(function() {
- self.stop();
+ self.hide();
});
},
stop: function () {
+ this.hide();
this.$option_id.empty();
this.$element
.find('#db-create, #db-drop, #db-backup, #db-restore, #db-change-password, #back-to-login')
.unbind('click')
.end()
- .closest(".openerp")
- .addClass("login-mode")
- .removeClass("database_block")
- .end()
.empty();
this._super();
},
+ show: function () {
+ this.$element.closest(".openerp")
+ .removeClass("login-mode")
+ .addClass("database_block");
+ },
+ hide: function () {
+ this.$element.closest(".openerp")
+ .addClass("login-mode")
+ .removeClass("database_block")
+ },
/**
* Converts a .serializeArray() result into a dict. Does not bother folding
* multiple identical keys into an array, last key wins.
@@ -368,6 +375,7 @@ openerp.web.Database = openerp.web.Widget.extend(/** @lends openerp.web.Database
}
self.db_list.push(self.to_object(fields)['db_name']);
self.db_list.sort();
+ self.widget_parent.set_db_list(self.db_list);
var form_obj = self.to_object(fields);
self.wait_for_newdb(result, {
password: form_obj['super_admin_pwd'],
@@ -397,6 +405,7 @@ openerp.web.Database = openerp.web.Widget.extend(/** @lends openerp.web.Database
}
$db_list.find(':selected').remove();
self.db_list.splice(_.indexOf(self.db_list, db, true), 1);
+ self.widget_parent.set_db_list(self.db_list);
self.do_notify("Dropping database", "The database '" + db + "' has been dropped");
});
}
@@ -529,16 +538,16 @@ openerp.web.Login = openerp.web.Widget.extend(/** @lends openerp.web.Login# */{
var self = this;
this.database = new openerp.web.Database(
this, "oe_database", "oe_db_options");
+ this.database.start();
this.$element.find('#oe-db-config').click(function() {
- self.database.start();
+ self.database.show();
});
this.$element.find("form").submit(this.on_submit);
this.rpc("/web/database/get_list", {}, function(result) {
- var tpl = openerp.web.qweb.render('Login_dblist', {db_list: result.db_list, selected_db: self.selected_db});
- self.$element.find("input[name=db]").replaceWith(tpl)
+ self.set_db_list(result.db_list);
},
function(error, event) {
if (error.data.fault_code === 'AccessDenied') {
@@ -547,6 +556,15 @@ openerp.web.Login = openerp.web.Widget.extend(/** @lends openerp.web.Login# */{
});
},
+ stop: function () {
+ this.database.stop();
+ this._super();
+ },
+ set_db_list: function (list) {
+ this.$element.find("[name=db]").replaceWith(
+ openerp.web.qweb.render('Login_dblist', {
+ db_list: list, selected_db: this.selected_db}))
+ },
on_login_invalid: function() {
this.$element.closest(".openerp").addClass("login-mode");
},
@@ -966,7 +984,7 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie
this.$element.html(QWeb.render("Interface", params));
this.notification = new openerp.web.Notification(this);
- this.loading = new openerp.web.Loading(this,"oe_loading");
+ this.loading = new openerp.web.Loading(this);
this.crashmanager = new openerp.web.CrashManager();
this.header = new openerp.web.Header(this);
@@ -990,6 +1008,7 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie
start: function() {
this._super.apply(this, arguments);
this.notification.prependTo(this.$element);
+ this.loading.appendTo($('#oe_loading'));
this.header.appendTo($("#oe_header"));
this.session.start();
this.login.appendTo($('#oe_login'));
diff --git a/addons/web/static/src/js/core.js b/addons/web/static/src/js/core.js
index 369e1191665..a9731ef5b16 100644
--- a/addons/web/static/src/js/core.js
+++ b/addons/web/static/src/js/core.js
@@ -125,7 +125,11 @@ openerp.web.callback = function(obj, method) {
callback.callback_chain.splice(i, 1);
i -= 1;
}
- r = c.callback.apply(c.self, c.args.concat(args));
+ var result = c.callback.apply(c.self, c.args.concat(args));
+ if (c.callback === method) {
+ // return the result of the original method
+ r = result;
+ }
// TODO special value to stop the chain
// openerp.web.callback_stop
}
@@ -354,6 +358,7 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.
this.debug = (window.location.search.indexOf('?debug') !== -1);
this.session_id = false;
this.uid = false;
+ this.username = false;
this.user_context= {};
this.db = false;
this.module_list = [];
@@ -484,10 +489,13 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.
var self = this;
var params = { db: db, login: login, password: password };
return this.rpc("/web/session/login", params, function(result) {
- self.session_id = result.session_id;
- self.uid = result.uid;
- self.user_context = result.context;
- self.db = result.db;
+ _.extend(self, {
+ session_id: result.session_id,
+ uid: result.uid,
+ user_context: result.context,
+ db: result.db,
+ username: result.login
+ });
self.session_save();
self.on_session_valid();
return true;
@@ -501,9 +509,12 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.
var self = this;
this.session_id = this.get_cookie('session_id');
return this.rpc("/web/session/get_session_info", {}).then(function(result) {
- self.uid = result.uid;
- self.user_context = result.context;
- self.db = result.db;
+ _.extend(self, {
+ uid: result.uid,
+ user_context: result.context,
+ db: result.db,
+ username: result.login
+ });
if (self.uid)
self.on_session_valid();
else
@@ -716,7 +727,7 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp.
if (parseInt(cookie_val, 10) !== token) { continue; }
// clear cookie
- document.cookie = _.sprintf("%s=;expires=%s;path=/",
+ document.cookie = _.str.sprintf("%s=;expires=%s;path=/",
cookie_name, new Date().toGMTString());
if (options.success) { options.success(); }
complete();
@@ -1055,7 +1066,7 @@ openerp.web.qweb.format_text_node = function(s) {
if (translation && translation.value === 'off') {
return s;
}
- var ts = _.trim(s);
+ var ts = _.str.trim(s);
if (ts.length === 0) {
return s;
}
diff --git a/addons/web/static/src/js/data.js b/addons/web/static/src/js/data.js
index f4509c82a81..69d8fd9ddfd 100644
--- a/addons/web/static/src/js/data.js
+++ b/addons/web/static/src/js/data.js
@@ -119,17 +119,18 @@ openerp.web.ContainerDataGroup = openerp.web.DataGroup.extend( /** @lends opener
aggregates[key] = value || 0;
});
+ var group_size = fixed_group[field_name + '_count'] || fixed_group.__count || 0;
+ var leaf_group = fixed_group.__context.group_by.length === 0;
return {
__context: fixed_group.__context,
__domain: fixed_group.__domain,
grouped_on: field_name,
// if terminal group (or no group) and group_by_no_leaf => use group.__count
- length: fixed_group[field_name + '_count'] || fixed_group.__count,
+ length: group_size,
value: fixed_group[field_name],
-
- openable: !(this.context['group_by_no_leaf']
- && fixed_group.__context.group_by.length === 0),
+ // A group is openable if it's not a leaf in group_by_no_leaf mode
+ openable: !(leaf_group && this.context['group_by_no_leaf']),
aggregates: aggregates
};
@@ -315,7 +316,7 @@ openerp.web.DataSet = openerp.web.Widget.extend( /** @lends openerp.web.DataSet
def.reject();
} else {
fields = fields || false;
- return this.read_ids([this.ids[this.index]], fields).then(function(records) {
+ this.read_ids([this.ids[this.index]], fields).then(function(records) {
def.resolve(records[0]);
}, function() {
def.reject.apply(def, arguments);
diff --git a/addons/web/static/src/js/formats.js b/addons/web/static/src/js/formats.js
index c4af2d0cf6b..3151b34636b 100644
--- a/addons/web/static/src/js/formats.js
+++ b/addons/web/static/src/js/formats.js
@@ -76,7 +76,13 @@ openerp.web.format_value = function (value, descriptor, value_if_empty) {
if (typeof value === 'number' && isNaN(value)) {
value = false;
}
+ //noinspection FallthroughInSwitchStatementJS
switch (value) {
+ case '':
+ if (descriptor.type === 'char') {
+ return '';
+ }
+ console.warn('Field', descriptor, 'had an empty string as value, treating as false...');
case false:
case Infinity:
case -Infinity:
@@ -86,18 +92,18 @@ openerp.web.format_value = function (value, descriptor, value_if_empty) {
switch (descriptor.widget || descriptor.type) {
case 'integer':
return openerp.web.insert_thousand_seps(
- _.sprintf('%d', value));
+ _.str.sprintf('%d', value));
case 'float':
var precision = descriptor.digits ? descriptor.digits[1] : 2;
- var formatted = _.sprintf('%.' + precision + 'f', value).split('.');
+ var formatted = _.str.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",
+ return _.str.sprintf("%02d:%02d",
Math.floor(value),
Math.round((value % 1) * 60));
case 'progressbar':
- return _.sprintf(
+ return _.str.sprintf(
'<progress value="%.2f" max="100.0">%.2f%%</progress>',
value, value);
case 'many2one':
diff --git a/addons/web/static/src/js/search.js b/addons/web/static/src/js/search.js
index 4fb5f02c690..f95801975b5 100644
--- a/addons/web/static/src/js/search.js
+++ b/addons/web/static/src/js/search.js
@@ -41,7 +41,11 @@ openerp.web.SearchView = openerp.web.Widget.extend(/** @lends openerp.web.Search
if (this.headless) {
this.ready.resolve();
} else {
- this.rpc("/web/searchview/load", {"model": this.model, "view_id":this.view_id}, this.on_loaded);
+ this.rpc("/web/searchview/load", {
+ model: this.model,
+ view_id: this.view_id,
+ context: this.dataset.get_context()
+ }, this.on_loaded);
}
return this.ready.promise();
},
@@ -137,7 +141,7 @@ 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(
+ throw new Error(_.str.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));
}
@@ -425,7 +429,7 @@ openerp.web.search.Invalid = openerp.web.Class.extend( /** @lends openerp.web.se
this.message = message;
},
toString: function () {
- return _.sprintf(
+ return _.str.sprintf(
_t("Incorrect value for field %(fieldname)s: [%(value)s] is %(message)s"),
{fieldname: this.field, value: this.value, message: this.message}
);
diff --git a/addons/web/static/src/js/view_editor.js b/addons/web/static/src/js/view_editor.js
index 033c2a31850..f907612bc1c 100644
--- a/addons/web/static/src/js/view_editor.js
+++ b/addons/web/static/src/js/view_editor.js
@@ -18,7 +18,7 @@ openerp.web.ViewEditor = openerp.web.Widget.extend({
init_view_editor: function() {
var self = this;
var action = {
- name: _.sprintf("Manage Views (%s)", this.model),
+ name: _.str.sprintf("Manage Views (%s)", this.model),
context: this.session.user_context,
domain: [["model", "=", this.model]],
res_model: 'ir.ui.view',
@@ -78,7 +78,7 @@ openerp.web.ViewEditor = openerp.web.Widget.extend({
var self = this;
this.create_view_dialog = new openerp.web.Dialog(this, {
modal: true,
- title: _.sprintf("Create a view (%s)", self.model),
+ title: _.str.sprintf("Create a view (%s)", self.model),
width: 500,
height: 400,
buttons: {
@@ -86,16 +86,16 @@ openerp.web.ViewEditor = openerp.web.Widget.extend({
var view_values = {};
var warn = false;
_.each(self.create_view_widget, function(widget) {
- if (widget.invalid) {
+ if (widget.is_invalid) {
warn = true;
return false;
};
- if (widget.dirty && !widget.invalid) {
+ if (widget.dirty && !widget.is_invalid) {
view_values[widget.name] = widget.get_value();
}
});
if (warn) {
- self.on_valid_create_view();
+ self.on_valid_create_view(self.create_view_widget);
} else {
$.when(self.do_save_view(view_values)).then(function() {
self.create_view_dialog.close();
@@ -112,15 +112,11 @@ openerp.web.ViewEditor = openerp.web.Widget.extend({
this.create_view_dialog.start().open();
var view_widget = [{'name': 'view_name', 'string':'View Name', 'type': 'char', 'required': true, 'value' : this.model + '.custom_' + Math.round(Math.random() * 1000)},
{'name': 'view_type', 'string': 'View Type', 'type': 'selection', 'required': true, 'value': 'Form', 'selection': [['',''],['tree', 'Tree'],['form', 'Form'],['graph', 'Graph'],['calendar', 'Calender']]},
- {'name': 'proirity', 'string': 'Priority', 'type': 'char', 'required': true, 'value':'16'}];
+ {'name': 'proirity', 'string': 'Priority', 'type': 'float', 'required': true, 'value':'16'}];
this.create_view_dialog.$element.append('<table id="create_view" style="width:400px" class="oe_forms"></table>');
this.create_view_widget = [];
_.each(view_widget, function(widget) {
- var type_widget = new (self.property.get_any([widget.type])) (self.create_view_dialog, widget.name);
- if (widget.selection) {
- type_widget.selection = widget.selection;
- }
- type_widget.required = widget.required;
+ var type_widget = new (self.property.get_any([widget.type])) (self.create_view_dialog, widget);
self.create_view_dialog.$element.find('table[id=create_view]').append('<tr><td width="100px" align="right">' + widget.string + ':</td>' + type_widget.render()+'</tr>');
var value = null;
if (widget.value) {
@@ -147,7 +143,7 @@ openerp.web.ViewEditor = openerp.web.Widget.extend({
if (field_name) {
model_dataset.read_slice(['name','field_id'], {"domain": [['model','=',self.model]]}, function(records) {
if (records) {view_string = records[0].name;}
- var arch = _.sprintf("<?xml version='1.0'?>\n<%s string='%s'>\n\t<field name='%s'/>\n</%s>", values.view_type, view_string, field_name, values.view_type);
+ var arch = _.str.sprintf("<?xml version='1.0'?>\n<%s string='%s'>\n\t<field name='%s'/>\n</%s>", values.view_type, view_string, field_name, values.view_type);
var vals = {'model': self.model, 'name': values.view_name, 'priority': values.priority, 'type': values.view_type, 'arch': arch};
self.dataset.create(vals, function(suc) {
def.resolve();
@@ -157,25 +153,29 @@ openerp.web.ViewEditor = openerp.web.Widget.extend({
});
return def.promise();
},
- on_valid_create_view: function() {
+ on_valid_create_view: function(widgets) {
var msg = "<ul>";
- _.each(self.create_view_widget, function(widget) {
- if (widget.invalid) {
+ _.each(widgets, function(widget) {
+ if (widget.is_invalid) {
msg += "<li>" + widget.name + "</li>";
}
});
msg += "</ul>";
- self.do_warn("The following fields are invalid :", msg);
+ this.do_warn("The following fields are invalid :", msg);
},
add_node_name : function(node) {
if(node.tagName.toLowerCase() == "button" || node.tagName.toLowerCase() == "field"){
return (node.getAttribute('name'))?
- _.sprintf( "<%s name='%s'>",node.tagName.toLowerCase(), node.getAttribute('name')):
- _.sprintf( "<%s>",node.tagName.toLowerCase());
+ _.str.sprintf( "<%s name='%s'>",node.tagName.toLowerCase(), node.getAttribute('name')):
+ _.str.sprintf( "<%s>",node.tagName.toLowerCase());
+ }else if(node.tagName.toLowerCase() == "group"){
+ return (node.getAttribute('string'))?
+ _.str.sprintf( "<%s>",node.getAttribute('string')):
+ _.str.sprintf( "<%s>",node.tagName.toLowerCase());
}else{
return (node.getAttribute('string'))?
- _.sprintf( "<%s string='%s'>",node.tagName.toLowerCase(), node.getAttribute('string')):
- _.sprintf( "<%s>",node.tagName.toLowerCase());
+ _.str.sprintf( "<%s string='%s'>",node.tagName.toLowerCase(), node.getAttribute('string')):
+ _.str.sprintf( "<%s>",node.tagName.toLowerCase());
}
},
do_delete_view: function() {
@@ -237,7 +237,7 @@ openerp.web.ViewEditor = openerp.web.Widget.extend({
'level': 0,
'id': this.xml_element_id +=1,
'att_list': [],
- 'name': _.sprintf("<view view_id = %s>", view_id),
+ 'name': _.str.sprintf("<view view_id = %s>", view_id),
'child_id': []
};
var xml_arch = QWeb.load_xml(arch);
@@ -249,7 +249,7 @@ openerp.web.ViewEditor = openerp.web.Widget.extend({
this.dataset.read_ids([parseInt(self.main_view_id)], ['arch', 'type'], function(arch) {
if (arch.length) {
var arch_object = self.parse_xml(arch[0].arch, self.main_view_id);
- self.main_view_type = arch[0].type
+ self.main_view_type = arch[0].type == 'tree'? 'list': arch[0].type;
view_arch_list.push({"view_id": self.main_view_id, "arch": arch[0].arch});
self.dataset.read_slice([], {domain: [['inherit_id','=', parseInt(self.main_view_id)]]}, function(result) {
_.each(result, function(res) {
@@ -295,7 +295,7 @@ openerp.web.ViewEditor = openerp.web.Widget.extend({
});
}else{
var temp = _.reject(xpath_arch_object[0].child_id[0].att_list, function(list) {
- return _.include(list, "position")
+ return list instanceof Array? _.include(list, "position"): false
});
expr_to_list = [_.flatten(temp)];
}
@@ -382,12 +382,12 @@ openerp.web.ViewEditor = openerp.web.Widget.extend({
this.one_object = one_object;
this.edit_xml_dialog = new openerp.web.Dialog(this, {
modal: true,
- title: _.sprintf("View Editor %d - %s", self.main_view_id, self.model),
+ title: _.str.sprintf("View Editor %d - %s", self.main_view_id, self.model),
width: 750,
height: 500,
buttons: {
"Inherited View": function() {
- //todo
+ //TODO
},
"Preview": function() {
var action = {
@@ -439,7 +439,7 @@ openerp.web.ViewEditor = openerp.web.Widget.extend({
var view_id,view_xml_id;
var view_find = side;
- //for view id found
+ //for view id finding
var min_level = this.one_object.clicked_tr_id;
if(($(side).find('a').text()).search("view_id") != -1){
view_id = parseInt(($(view_find).find('a').text()).replace(/[^0-9]+/g, ''));
@@ -494,7 +494,7 @@ openerp.web.ViewEditor = openerp.web.Widget.extend({
_.each([tr,parent_tr],function(element){
property_to_check.push(
_.detect(_.keys(_CHILDREN),function(res){
- return _.includes(element, res);
+ return _.str.include(element, res);
}));
});
self.on_add_node(property_to_check, fields);
@@ -502,12 +502,17 @@ openerp.web.ViewEditor = openerp.web.Widget.extend({
},
do_node_edit: function(side){
var self = this;
- var tr = $(side).find('a').text();
+ var result = self.get_object_by_id(this.one_object.clicked_tr_id,this.one_object['main_object'], []);
+ if (result.length && result[0] && result[0].att_list) {
+ var properties = _PROPERTIES[result[0].att_list[0]];
+ self.on_edit_node(properties);
+ }
+/* var tr = $(side).find('a').text();
var tag = _.detect(_.keys(_PROPERTIES),function(res){
return _.includes(tr, res);
});
var properties = _PROPERTIES[tag];
- self.on_edit_node(properties);
+ self.on_edit_node(properties);*/
},
do_node_down: function(cur_tr, img){
var self = this;
@@ -663,18 +668,15 @@ openerp.web.ViewEditor = openerp.web.Widget.extend({
parent = $(arch1).parents();
} else if (move_direct == "update_node") {
-
_.each(update_values, function(val){
- if(val[0] == "required"){
- $(arch1).attr("required", "true");
- }else{
- $(arch1).attr(val[0],val[1]);
- }
+ if (val[1]) $(arch1)[0].setAttribute(val[0], val[1]);
+ else $(arch1)[0].removeAttribute(val[0]);
});
var new_obj = self.create_View_Node(arch1);
new_obj.id = obj.id,new_obj.child_id = obj.child_id;
- self.edit_xml_dialog.$element.find("tr[id='viewedit-"+self.one_object.clicked_tr_id+"']")
- .find('a').text(new_obj.name);
+ self.edit_xml_dialog.$element.
+ find("tr[id='viewedit-"+this.one_object.clicked_tr_id+"']").
+ find('a').text(new_obj.name);
child_list.splice(index, 1, new_obj);
parent = $(arch1).parents();
@@ -802,14 +804,23 @@ openerp.web.ViewEditor = openerp.web.Widget.extend({
height: 400,
buttons: {
"Update": function(){
+ var warn = false;
var update_values = [];
_.each(self.edit_widget, function(widget) {
- if (widget.dirty && !widget.invalid) {
+ if (widget.is_invalid) {
+ warn = true;
+ return false;
+ };
+ if (widget.dirty && !widget.is_invalid) {
update_values.push([widget.name, widget.get_value()]);
}
});
- self.do_save_update_arch("update_node", update_values);
- self.edit_node_dialog.close();
+ if (warn) {
+ self.on_valid_create_view(self.edit_widget);
+ } else {
+ self.do_save_update_arch("update_node", update_values);
+ self.edit_node_dialog.close();
+ }
},
"Cancel": function(){
self.edit_node_dialog.close();
@@ -817,24 +828,63 @@ openerp.web.ViewEditor = openerp.web.Widget.extend({
}
});
this.edit_node_dialog.start().open();
- var widget = _.keys(self.property.map);
+ var _PROPERTIES_ATTRIBUTES = {
+ 'name' : {'name':'name', 'string': 'Name', 'type': 'char'},
+ 'string' : {'name':'string', 'string': 'String', 'type': 'char'},
+ 'required' : {'name':'required', 'string': 'Required', 'type': 'boolean'},
+ 'readonly' : {'name':'readonly', 'string': 'Readonly', 'type': 'boolean'},
+ 'domain' : {'name':'domain', 'string': 'Domain', 'type': 'char'},
+ 'context' : {'name':'context', 'string': 'Context', 'type': 'char'},
+ 'limit' : {'name':'limit', 'string': 'Limit', 'type': 'float'},
+ 'min_rows' : {'name':'min_rows', 'string': 'Minimum rows', 'type': 'float'},
+ 'date_start' : {'name':'date_start', 'string': 'Start date', 'type': 'char'},
+ 'date_delay' : {'name':'date_delay', 'string': 'Delay date', 'type': 'char'},
+ 'day_length' : {'name':'day_length', 'string': 'Day length', 'type': 'char'},
+ 'mode' : {'name':'mode', 'string': 'Mode', 'type': 'char'},
+ 'align' : {'name':'align', 'string': 'Alignment ', 'type': 'selection', 'selection': [['', ''], ['0.0', 'Left'], ['0.5', 'Center'], ['1.0', 'Right']]},
+ 'icon' : {'name':'icon', 'string': 'Icon', 'type': 'selection', 'selection': _ICONS},
+ 'type' : {'name':'type', 'string': 'Type', 'type': 'selection', 'selection': [['', ''], ['action', 'Action'], ['object', 'Object'], ['workflow', 'Workflow'], ['server_action', 'Server Action']]},
+ 'special' : {'name':'special', 'string': 'Special', 'type': 'selection', 'selection': [['',''],['save', 'Save Button'], ['cancel', 'Cancel Button'], ['open', 'Open Button']]},
+ 'target' : {'name':'target', 'string': 'Target', 'type': 'selection', 'selection': [['', ''], ['new', 'New Window']]},
+ 'confirm' : {'name':'confirm', 'string': 'Confirm', 'type': 'char'},
+ 'style' : {'name':'style', 'string': 'Style', 'type': 'selection', 'selection':[["",""],["1", "1"],["1-1", "1-1"],["1-2", "1-2"],["2-1", "2-1"],["1-1-1", "1-1-1"]]},
+ 'filename' : {'name':'filename', 'string': 'File Name', 'type': 'char'},
+ 'width' : {'name':'width', 'string': 'Width', 'type': 'float'},
+ 'height' : {'name':'height', 'string': 'Height', 'type': 'float'},
+ 'attrs' : {'name':'attrs', 'string': 'Attrs', 'type': 'char'},
+ 'col' : {'name':'col', 'string': 'col', 'type': 'float'},
+ 'link' : {'name':'link', 'string': 'Link', 'type': 'char'},
+ 'position' : {'name':'position', 'string': 'Position', 'type': 'selection', 'selection': [['',''],['after', 'After'],['before', 'Before'],['inside', 'Inside'],['replace', 'Replace']]},
+ 'states' : {'name':'states', 'string': 'states', 'type': 'char'},
+ 'eval' : {'name':'eval', 'string': 'Eval', 'type': 'char'},
+ 'ref' : {'name':'ref', 'string': 'Ref', 'type': 'char'},
+ 'on_change' : {'name':'on_change', 'string': 'On change', 'type': 'char'},
+ 'nolabel' : {'name':'nolabel', 'string': 'No label', 'type': 'boolean'},
+ 'completion' : {'name':'completion', 'string': 'Completion', 'type': 'boolean'},
+ 'colspan' : {'name':'colspan', 'string': 'Colspan', 'type': 'float'},
+ 'widget' : {'name':'widget', 'string': 'widget', 'type': 'selection'},
+ 'colors' : {'name':'colors', 'string': 'Colors', 'type': 'char'},
+ 'editable' : {'name':'editable', 'string': 'Editable', 'type': 'selection', 'selection': [["",""],["top","Top"],["bottom", "Bottom"]]},
+ 'groups' : {'name':'groups', 'string': 'Groups', 'type': 'seleciton_multi'},
+ };
var arch_val = self.get_object_by_id(this.one_object.clicked_tr_id,this.one_object['main_object'], []);
this.edit_node_dialog.$element.append('<table id="rec_table" style="width:400px" class="oe_forms"></table>');
this.edit_widget = [];
- _.each(properties, function(property) {
- type_widget = false;
- self.ready = $.when(self.on_groups(property)).then(function () {
- if (_.include(widget, property)){
- type_widget = new (self.property.get_any([property])) (self.edit_node_dialog, property);
- } else {
- type_widget = new openerp.web.ViewEditor.FieldChar (self.edit_node_dialog, property);
- }
+ self.ready = $.when(self.on_groups(properties)).then(function () {
+ _PROPERTIES_ATTRIBUTES['groups']['selection'] = self.groups;
+ var values = _.keys( openerp.web.form.widgets.map);
+ values.push('');
+ values.sort();
+ _PROPERTIES_ATTRIBUTES['widget']['selection'] = values;
+ var widgets = _.filter(_PROPERTIES_ATTRIBUTES, function(property){ return _.include(properties, property.name)})
+ _.each(widgets, function(widget) {
+ var type_widget = new (self.property.get_any([widget.type])) (self.edit_node_dialog, widget);
var value = _.detect(arch_val[0]['att_list'],function(res) {
- return _.include(res, property);
+ if (res instanceof Array) return _.include(res, widget.name);
+ else return false;
});
value = value instanceof Array ? value[1] : value;
- if (property == 'groups') type_widget.selection = self.groups;
- self.edit_node_dialog.$element.find('table[id=rec_table]').append('<tr><td align="right">' + property + ':</td>' + type_widget.render() + '</tr>');
+ self.edit_node_dialog.$element.find('table[id=rec_table]').append('<tr><td align="right">' + widget.string + ':</td>' + type_widget.render() + '</tr>');
type_widget.start();
type_widget.set_value(value);
self.edit_widget.push(type_widget);
@@ -842,12 +892,12 @@ openerp.web.ViewEditor = openerp.web.Widget.extend({
});
},
//for getting groups
- on_groups: function(property){
+ on_groups: function(properties){
var self = this,
def = $.Deferred();
- if (property != 'groups') {
+ if (!_.include(properties, 'groups')) {
self.groups = false;
- return false;
+ def.resolve();
}
var group_ids = [],
group_names = {},
@@ -862,7 +912,7 @@ openerp.web.ViewEditor = openerp.web.Widget.extend({
group_names[key]=res.name;
group_ids.push(res.id);
});
- model_data
+ model_data
.read_slice([],{domain:[['res_id', 'in', group_ids],['model','=','res.groups']]})
.done(function(model_grp) {
_.each(model_grp,function(res_group){
@@ -879,9 +929,9 @@ openerp.web.ViewEditor = openerp.web.Widget.extend({
var positions = ['After','Before','Inside'];
var render_list = [];
var render_list =[{'name': 'node_type','selection': _.keys(_CHILDREN).sort(),
- 'value': 'field', 'string': 'Node Type'},
- {'name': 'position','selection': positions, 'value': false, 'string': 'Position'},
- {'name': 'field_value','selection': fields, 'value': false, 'string': ''}];
+ 'value': 'field', 'string': 'Node Type','type': 'selection'},
+ {'name': 'position','selection': positions, 'value': false, 'string': 'Position','type': 'selection'},
+ {'name': 'field_value','selection': fields, 'value': false, 'string': '','type': 'selection'}];
this.add_widget = [];
this.add_node_dialog = new openerp.web.Dialog(this,{
modal: true,
@@ -903,8 +953,8 @@ openerp.web.ViewEditor = openerp.web.Widget.extend({
}
if(check_add_node){
var tag = (values.node_type == "field")?
- _.sprintf("<%s name='%s'> </%s>",values.node_type,values.field_value,values.node_type):
- _.sprintf("<%s> </%s>",values.node_type,values.node_type);
+ _.str.sprintf("<%s name='%s'> </%s>",values.node_type,values.field_value,values.node_type):
+ _.str.sprintf("<%s> </%s>",values.node_type,values.node_type);
self.do_save_update_arch("add_node", [tag, values.position]);
}else{alert("Can't Update View");}
},
@@ -917,8 +967,7 @@ openerp.web.ViewEditor = openerp.web.Widget.extend({
append('<table id="rec_table" style="width:400px" class="oe_forms"></table>');
var table_selector = self.add_node_dialog.$element.find('table[id=rec_table]');
_.each(render_list,function(node){
- type_widget = new openerp.web.ViewEditor.FieldSelect (self.add_node_dialog, node.name);
- type_widget.selection = node.selection;
+ type_widget = new (self.property.get_any([node.type])) (self.add_node_dialog, node);
table_selector.append('<tr><td align="right">' + node.string + ':</td>' + type_widget.render() + '</tr>');
type_widget.start();
type_widget.set_value(node.value);
@@ -963,33 +1012,39 @@ openerp.web.ViewEditor = openerp.web.Widget.extend({
}
});
openerp.web.ViewEditor.Field = openerp.web.Class.extend({
- init: function(view, name) {
+ init: function(view, widget) {
this.$element = view.$element;
this.dirty = false;
- this.name = name;
- this.required = false;
- this.invalid = false;
+ this.name = widget.name;
+ this.selection = widget.selection || [];
+ this.required = widget.required || false;
+ this.string = widget.string || "";
+ this.type = widget.type;
+ this.is_invalid = false;
},
start: function () {
this.update_dom();
},
update_dom: function() {
- this.$element.find("td[id="+ this.name+"]").toggleClass('invalid', this.invalid);
+ this.$element.find("td[id="+ this.name+"]").toggleClass('invalid', this.is_invalid);
this.$element.find("td[id="+ this.name+"]").toggleClass('required', this.required);
},
on_ui_change: function() {
- var value = this.get_value();
- value = value instanceof Array ? value[1] : value;
- if (this.required && !value) {
- this.invalid = true;
- } else {
- this.invalid = false;
- }
+ this.validate();
this.dirty = true;
this.update_dom();
},
+ validate: function() {
+ this.is_invalid = false;
+ try {
+ var value = openerp.web.parse_value(this.get_value(), this, '');
+ this.is_invalid = this.required && value === '';
+ } catch(e) {
+ this.is_invalid = true;
+ }
+ },
render: function() {
- return _.sprintf("<td id = %s>%s</td>", this.name, QWeb.render(this.template, {widget: this}))
+ return _.str.sprintf("<td id = %s>%s</td>", this.name, QWeb.render(this.template, {widget: this}))
},
});
openerp.web.ViewEditor.FieldBoolean = openerp.web.ViewEditor.Field.extend({
@@ -1000,7 +1055,6 @@ openerp.web.ViewEditor.FieldBoolean = openerp.web.ViewEditor.Field.extend({
this.$element.find("input[id="+ self.name+"]").change(function() {
self.on_ui_change();
});
-
},
set_value: function(value) {
if (value) {
@@ -1008,7 +1062,8 @@ openerp.web.ViewEditor.FieldBoolean = openerp.web.ViewEditor.Field.extend({
}
},
get_value: function() {
- return this.$element.find("input[id=" + this.name + "]").is(':checked') || null;
+ var value = this.$element.find("input[id=" + this.name + "]").is(':checked')
+ return value? "1" : null;
}
});
openerp.web.ViewEditor.FieldChar = openerp.web.ViewEditor.Field.extend({
@@ -1029,10 +1084,6 @@ openerp.web.ViewEditor.FieldChar = openerp.web.ViewEditor.Field.extend({
});
openerp.web.ViewEditor.FieldSelect = openerp.web.ViewEditor.Field.extend({
template : "vieweditor_selection",
- init: function(view, name) {
- this._super(view, name);
- this.selection = false;
- },
start: function () {
var self = this;
this._super();
@@ -1058,53 +1109,7 @@ openerp.web.ViewEditor.FieldSelect = openerp.web.ViewEditor.Field.extend({
return this.$element.find("select[id=" + this.name + "]").val();
}
});
-openerp.web.ViewEditor.WidgetProperty = openerp.web.ViewEditor.FieldSelect.extend({
- init: function(view, name) {
- this._super(view, name);
- this.registry = openerp.web.form.widgets;
- var values = _.keys(this.registry.map);
- values.push('');
- values.sort();
- this.selection = values;
- },
-});
-openerp.web.ViewEditor.IconProperty = openerp.web.ViewEditor.FieldSelect.extend({
- init: function(view, name) {
- this._super(view, name);
- this.selection = _ICONS;
- },
-});
-openerp.web.ViewEditor.ButtonTargetProperty = openerp.web.ViewEditor.FieldSelect.extend({
- init: function(view, name) {
- this._super(view, name);
- this.selection = [['', ''], ['new', 'New Window']];
- },
-});
-openerp.web.ViewEditor.ButtonTypeProperty = openerp.web.ViewEditor.FieldSelect.extend({
- init: function(view, name) {
- this._super(view, name);
- this.selection = [['', ''], ['action', 'Action'], ['object', 'Object'], ['workflow', 'Workflow'], ['server_action', 'Server Action']];
- },
-});
-openerp.web.ViewEditor.AlignProperty = openerp.web.ViewEditor.FieldSelect.extend({
- init: function(view, name) {
- this._super(view, name);
- this.selection = [['', ''], ['0.0', 'Left'], ['0.5', 'Center'], ['1.0', 'Right']];
- },
-});
-openerp.web.ViewEditor.ButtonSpecialProperty = openerp.web.ViewEditor.FieldSelect.extend({
- init: function(view, name) {
- this._super(view, name);
- this.selection = [['',''],['save', 'Save Button'], ['cancel', 'Cancel Button'], ['open', 'Open Button']];
- },
-});
-openerp.web.ViewEditor.PositionProperty = openerp.web.ViewEditor.FieldSelect.extend({
- init: function(view, name) {
- this._super(view, name);
- this.selection = [['',''],['after', 'After'],['before', 'Before'],['inside', 'Inside'],['replace', 'Replace']];
- },
-});
-openerp.web.ViewEditor.GroupsProperty = openerp.web.ViewEditor.FieldSelect.extend({
+openerp.web.ViewEditor.FieldSelectMulti = openerp.web.ViewEditor.FieldSelect.extend({
start: function () {
this._super();
this.$element.find("select[id=" + this.name + "]").css('height', '100px').attr("multiple",true);
@@ -1120,9 +1125,13 @@ openerp.web.ViewEditor.GroupsProperty = openerp.web.ViewEditor.FieldSelect.exten
});
}
});
+openerp.web.ViewEditor.FieldFloat = openerp.web.ViewEditor.FieldChar.extend({
+
+});
+
var _PROPERTIES = {
'field' : ['name', 'string', 'required', 'readonly', 'domain', 'context', 'nolabel', 'completion',
- 'colspan', 'widget', 'eval', 'ref', 'on_change', 'groups', 'attrs'],
+ 'colspan', 'widget', 'eval', 'ref', 'on_change', 'attrs', 'groups'],
'form' : ['string', 'col', 'link'],
'notebook' : ['colspan', 'position', 'groups'],
'page' : ['string', 'states', 'attrs', 'groups'],
@@ -1130,7 +1139,7 @@ var _PROPERTIES = {
'image' : ['filename', 'width', 'height', 'groups'],
'separator' : ['string', 'colspan', 'groups'],
'label': ['string', 'align', 'colspan', 'groups'],
- 'button': ['name', 'string', 'icon', 'type', 'states', 'readonly', 'special', 'target', 'confirm', 'context', 'attrs', 'groups','colspan'],
+ 'button': ['name', 'string', 'icon', 'type', 'states', 'readonly', 'special', 'target', 'confirm', 'context', 'attrs', 'colspan', 'groups'],
'newline' : [],
'board': ['style'],
'column' : [],
@@ -1156,7 +1165,7 @@ var _CHILDREN = {
'newline': [],
'separator': [],
};
-var icons = ['','STOCK_ABOUT', 'STOCK_ADD', 'STOCK_APPLY', 'STOCK_BOLD',
+var _ICONS = ['','STOCK_ABOUT', 'STOCK_ADD', 'STOCK_APPLY', 'STOCK_BOLD',
'STOCK_CANCEL', 'STOCK_CDROM', 'STOCK_CLEAR', 'STOCK_CLOSE', 'STOCK_COLOR_PICKER',
'STOCK_CONNECT', 'STOCK_CONVERT', 'STOCK_COPY', 'STOCK_CUT', 'STOCK_DELETE',
'STOCK_DIALOG_AUTHENTICATION', 'STOCK_DIALOG_ERROR', 'STOCK_DIALOG_INFO',
@@ -1184,19 +1193,10 @@ var icons = ['','STOCK_ABOUT', 'STOCK_ADD', 'STOCK_APPLY', 'STOCK_BOLD',
'terp-project', 'terp-report', 'terp-stock', 'terp-calendar', 'terp-graph'
];
openerp.web.ViewEditor.property_widget = new openerp.web.Registry({
- 'required' : 'openerp.web.ViewEditor.FieldBoolean',
- 'readonly' : 'openerp.web.ViewEditor.FieldBoolean',
- 'nolabel' : 'openerp.web.ViewEditor.FieldBoolean',
- 'completion' : 'openerp.web.ViewEditor.FieldBoolean',
- 'widget' : 'openerp.web.ViewEditor.WidgetProperty',
- 'groups' : 'openerp.web.ViewEditor.GroupsProperty',
- 'position' : 'openerp.web.ViewEditor.PositionProperty',
- 'icon' : 'openerp.web.ViewEditor.IconProperty',
- 'align' : 'openerp.web.ViewEditor.AlignProperty',
- 'special' : 'openerp.web.ViewEditor.ButtonSpecialProperty',
- 'type' : 'openerp.web.ViewEditor.ButtonTypeProperty',
- 'target' : 'openerp.web.ViewEditor.ButtonTargetProperty',
+ 'boolean' : 'openerp.web.ViewEditor.FieldBoolean',
+ 'seleciton_multi' : 'openerp.web.ViewEditor.FieldSelectMulti',
'selection' : 'openerp.web.ViewEditor.FieldSelect',
'char' : 'openerp.web.ViewEditor.FieldChar',
+ 'float' : 'openerp.web.ViewEditor.FieldFloat',
});
};
diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js
index 84041ef8b60..d833b7471c6 100644
--- a/addons/web/static/src/js/view_form.js
+++ b/addons/web/static/src/js/view_form.js
@@ -35,7 +35,6 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
this.fields = {};
this.datarecord = {};
this.show_invalid = true;
- this.dirty_for_user = false;
this.default_focus_field = null;
this.default_focus_button = null;
this.registry = this.readonly ? openerp.web.form.readonly : openerp.web.form.widgets;
@@ -141,7 +140,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
// null index means we should start a new record
promise = this.on_button_new();
} else {
- promise = this.dataset.read_index(_.keys(this.fields_view.fields), this.on_record_loaded);
+ promise = this.dataset.read_index(_.keys(this.fields_view.fields)).pipe(this.on_record_loaded);
}
this.$element.show();
if (this.sidebar) {
@@ -156,6 +155,8 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
}
},
on_record_loaded: function(record) {
+ var self = this,
+ deferred_stack = $.Deferred.queue();
if (!record) {
throw("Form: No record received");
}
@@ -172,35 +173,42 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
}
this.$form_header.find('.oe_form_on_readonly').toggle(this.readonly);
this.$form_header.find('.oe_form_on_editable').toggle(!this.readonly);
- this.dirty_for_user = false;
this.datarecord = record;
- for (var f in this.fields) {
- var field = this.fields[f];
+
+ _(this.fields).each(function (field, f) {
field.dirty = false;
- field.set_value(this.datarecord[f] || false);
- field.validate();
- }
- if (!record.id) {
- // New record: Second pass in order to trigger the onchanges
- this.show_invalid = false;
- for (var f in record) {
- var field = this.fields[f];
- if (field) {
- field.dirty = true;
- this.do_onchange(field);
+ var result = field.set_value(self.datarecord[f] || false);
+ if (result && _.isFunction(result.promise)) {
+ deferred_stack.push(result);
+ }
+ $.when(result).then(function() {
+ field.validate();
+ });
+ });
+ deferred_stack.push('force resolution if no fields');
+ return deferred_stack.then(function() {
+ if (!record.id) {
+ // New record: Second pass in order to trigger the onchanges
+ self.show_invalid = false;
+ for (var f in record) {
+ var field = self.fields[f];
+ if (field) {
+ field.dirty = true;
+ self.do_onchange(field);
+ }
}
}
- }
- this.on_form_changed();
- this.initial_mutating_lock.resolve();
- this.show_invalid = true;
- this.do_update_pager(record.id == null);
- if (this.sidebar) {
- this.sidebar.attachments.do_update();
- }
- if (this.default_focus_field && !this.embedded_view) {
- this.default_focus_field.focus();
- }
+ self.on_form_changed();
+ self.initial_mutating_lock.resolve();
+ self.show_invalid = true;
+ self.do_update_pager(record.id == null);
+ if (self.sidebar) {
+ self.sidebar.attachments.do_update();
+ }
+ if (self.default_focus_field && !self.embedded_view) {
+ self.default_focus_field.focus();
+ }
+ });
},
on_form_changed: function() {
for (var w in this.widgets) {
@@ -240,7 +248,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
try {
processed = processed || [];
if (widget.node.attrs.on_change) {
- var onchange = _.trim(widget.node.attrs.on_change);
+ var onchange = _.str.trim(widget.node.attrs.on_change);
var call = onchange.match(/^\s?(.*?)\((.*?)\)\s?$/);
if (call) {
var method = call[1], args = [];
@@ -257,7 +265,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
};
var parent_fields = null;
_.each(call[2].split(','), function(a, i) {
- var field = _.trim(a);
+ var field = _.str.trim(a);
if (field in argument_replacement) {
args.push(argument_replacement[field](i));
return;
@@ -267,11 +275,11 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
return;
} else {
var splitted = field.split('.');
- if (splitted.length > 1 && _.trim(splitted[0]) === "parent" && self.dataset.parent_view) {
+ if (splitted.length > 1 && _.str.trim(splitted[0]) === "parent" && self.dataset.parent_view) {
if (parent_fields === null) {
parent_fields = self.dataset.parent_view.get_fields_values();
}
- var p_val = parent_fields[_.trim(splitted[1])];
+ var p_val = parent_fields[_.str.trim(splitted[1])];
if (p_val !== undefined) {
args.push(p_val == null ? false : p_val);
return;
@@ -317,7 +325,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
processed.push(field.name);
if (field.get_value() != value) {
field.set_value(value);
- field.dirty = this.dirty_for_user = true;
+ field.dirty = true;
if (_.indexOf(processed, field.name) < 0) {
this.do_onchange(field, processed);
}
@@ -356,12 +364,13 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
var keys = _.keys(self.fields_view.fields);
$.when(self.do_set_editable()).then(function() {
if (keys.length) {
- self.dataset.default_get(keys).then(self.on_record_loaded).then(function() {
+ self.dataset.default_get(keys).pipe(self.on_record_loaded).then(function() {
def.resolve();
});
} else {
- self.on_record_loaded({});
- def.resolve();
+ self.on_record_loaded({}).then(function() {
+ def.resolve();
+ });
}
});
}
@@ -402,7 +411,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
return def.promise();
},
can_be_discarded: function() {
- return !this.dirty_for_user || confirm(_t("Warning, the record has been modified, your changes will be discarded."));
+ return !this.is_dirty() || confirm(_t("Warning, the record has been modified, your changes will be discarded."));
},
/**
* Triggers saving the form's record. Chooses between creating a new
@@ -418,8 +427,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
try {
if (!self.initial_mutating_lock.isResolved() && !self.initial_mutating_lock.isRejected())
return;
- var form_dirty = false,
- form_invalid = false,
+ var form_invalid = false,
values = {},
first_invalid_field = null;
for (var f in self.fields) {
@@ -431,7 +439,6 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
first_invalid_field = f;
}
} else if (f.is_dirty()) {
- form_dirty = true;
values[f.name] = f.get_value();
}
}
@@ -480,8 +487,8 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
// should not happen in the server, but may happen for internal purpose
return $.Deferred().reject();
} else {
- this.reload();
- return $.when(r).then(success);
+ return $.when(this.reload()).pipe(function () {
+ return $.when(r).then(success); }, null);
}
},
/**
@@ -528,7 +535,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
if (self.dataset.index == null || self.dataset.index < 0) {
return $.when(self.on_button_new());
} else {
- return self.dataset.read_index(_.keys(self.fields_view.fields), self.on_record_loaded);
+ return self.dataset.read_index(_.keys(self.fields_view.fields)).pipe(self.on_record_loaded);
}
};
this.reload_lock = this.reload_lock.pipe(act, act);
@@ -553,6 +560,11 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
return self.dataset.parent_view.recursive_save();
});
},
+ is_dirty: function() {
+ return _.any(this.fields, function (value) {
+ return value.is_dirty();
+ });
+ },
is_interactible_record: function() {
var id = this.datarecord.id;
if (!id) {
@@ -638,7 +650,7 @@ openerp.web.form.SidebarAttachments = openerp.web.Widget.extend({
},
on_attachment_delete: function(e) {
var self = this, $e = $(e.currentTarget);
- var name = _.trim($e.parent().find('a.oe-sidebar-attachments-link').text());
+ var name = _.str.trim($e.parent().find('a.oe-sidebar-attachments-link').text());
if (confirm("Do you really want to delete the attachment " + name + " ?")) {
this.rpc('/web/dataset/unlink', {
model: 'ir.attachment',
@@ -798,8 +810,13 @@ openerp.web.form.Widget = openerp.web.Widget.extend(/** @lends openerp.web.form.
_build_view_fields_values: function() {
var a_dataset = this.view.dataset;
var fields_values = this.view.get_fields_values();
- var parent_values = a_dataset.parent_view ? a_dataset.parent_view.get_fields_values() : {};
- fields_values.parent = parent_values;
+ var active_id = a_dataset.ids[a_dataset.index];
+ _.extend(fields_values, {
+ active_id: active_id || false,
+ active_ids: active_id ? [active_id] : [],
+ active_model: a_dataset.model,
+ parent: a_dataset.parent_view ? a_dataset.parent_view.get_fields_values() : {}
+ });
return fields_values;
},
_build_eval_context: function() {
@@ -1231,7 +1248,7 @@ openerp.web.form.Field = openerp.web.form.Widget.extend(/** @lends openerp.web.f
}
},
on_ui_change: function() {
- this.dirty = this.view.dirty_for_user = true;
+ this.dirty = true;
this.validate();
if (this.is_valid()) {
this.set_value_from_ui();
@@ -1344,54 +1361,58 @@ openerp.web.DateTimeWidget = openerp.web.Widget.extend({
template: "web.datetimepicker",
jqueryui_object: 'datetimepicker',
type_of_date: "datetime",
+ init: function(parent) {
+ this._super(parent);
+ this.name = parent.name;
+ },
start: function() {
var self = this;
- this.$element.find('input').change(this.on_change);
+ this.$input = this.$element.find('input.oe_datepicker_master');
+ this.$input_picker = this.$element.find('input.oe_datepicker_container');
+ this.$input.change(this.on_change);
this.picker({
onSelect: this.on_picker_select,
changeMonth: true,
changeYear: true,
showWeek: true,
- showButtonPanel: false
+ showButtonPanel: true
});
this.$element.find('img.oe_datepicker_trigger').click(function() {
if (!self.readonly) {
self.picker('setDate', self.value ? openerp.web.auto_str_to_date(self.value) : new Date());
- self.$element.find('.oe_datepicker').toggle();
+ self.$input_picker.show();
+ self.picker('show');
+ self.$input_picker.hide();
}
});
- this.$element.find('.ui-datepicker-inline').removeClass('ui-widget-content ui-corner-all');
- this.$element.find('button.oe_datepicker_close').click(function() {
- self.$element.find('.oe_datepicker').hide();
- });
this.set_readonly(false);
this.value = false;
},
picker: function() {
- return $.fn[this.jqueryui_object].apply(this.$element.find('.oe_datepicker_container'), arguments);
+ return $.fn[this.jqueryui_object].apply(this.$input_picker, arguments);
},
on_picker_select: function(text, instance) {
var date = this.picker('getDate');
- this.$element.find('input').val(date ? this.format_client(date) : '').change();
+ this.$input.val(date ? this.format_client(date) : '').change();
},
set_value: function(value) {
this.value = value;
- this.$element.find('input').val(value ? this.format_client(value) : '');
+ this.$input.val(value ? this.format_client(value) : '');
},
get_value: function() {
return this.value;
},
set_value_from_ui: function() {
- var value = this.$element.find('input').val() || false;
+ var value = this.$input.val() || false;
this.value = this.parse_client(value);
},
set_readonly: function(readonly) {
this.readonly = readonly;
- this.$element.find('input').attr('disabled', this.readonly);
+ this.$input.attr('disabled', this.readonly);
this.$element.find('img.oe_datepicker_trigger').toggleClass('oe_input_icon_disabled', readonly);
},
is_valid: function(required) {
- var value = this.$element.find('input').val();
+ var value = this.$input.val();
if (value === "") {
return !required;
} else {
@@ -1404,7 +1425,7 @@ openerp.web.DateTimeWidget = openerp.web.Widget.extend({
}
},
focus: function() {
- this.$element.find('input').focus();
+ this.$input.focus();
},
parse_client: function(v) {
return openerp.web.parse_value(v, {"widget": this.type_of_date});
@@ -1421,11 +1442,7 @@ openerp.web.DateTimeWidget = openerp.web.Widget.extend({
openerp.web.DateWidget = openerp.web.DateTimeWidget.extend({
jqueryui_object: 'datepicker',
- type_of_date: "date",
- on_picker_select: function(text, instance) {
- this._super(text, instance);
- this.$element.find('.oe_datepicker').hide();
- }
+ type_of_date: "date"
});
openerp.web.form.FieldDatetime = openerp.web.form.Field.extend({
@@ -1437,7 +1454,7 @@ openerp.web.form.FieldDatetime = openerp.web.form.Field.extend({
var self = this;
this._super.apply(this, arguments);
this.datewidget = this.build_widget();
- this.datewidget.on_change.add(this.on_ui_change);
+ this.datewidget.on_change.add_last(this.on_ui_change);
this.datewidget.appendTo(this.$element);
},
set_value: function(value) {
@@ -1825,7 +1842,7 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({
if (search_val.length > 0 &&
!_.include(raw_result, search_val) &&
(!self.value || search_val !== self.value[1])) {
- values.push({label: _.sprintf(_t('<em>   Create "<strong>%s</strong>"</em>'),
+ values.push({label: _.str.sprintf(_t('<em>   Create "<strong>%s</strong>"</em>'),
$('<span />').text(search_val).html()), action: function() {
self._quick_create(search_val);
}});
@@ -2103,7 +2120,7 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({
},
reload_current_view: function() {
var self = this;
- self.is_loaded = self.is_loaded.pipe(function() {
+ return self.is_loaded = self.is_loaded.pipe(function() {
var view = self.viewmanager.views[self.viewmanager.active_view].controller;
if(self.viewmanager.active_view === "list") {
return view.reload_content();
@@ -2176,8 +2193,8 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({
if (this.dataset.index === null && this.dataset.ids.length > 0) {
this.dataset.index = 0;
}
- self.reload_current_view();
- this.is_setted.resolve();
+ self.is_setted.resolve();
+ return self.reload_current_view();
},
get_value: function() {
var self = this;
@@ -2955,7 +2972,7 @@ openerp.web.form.FieldStatus = openerp.web.form.Field.extend({
render_list: function() {
var self = this;
var shown = _.map(((this.node.attrs || {}).statusbar_visible || "").split(","),
- function(x) { return _.trim(x); });
+ function(x) { return _.str.trim(x); });
shown = _.select(shown, function(x) { return x.length > 0; });
if (shown.length == 0) {
diff --git a/addons/web/static/src/js/view_list.js b/addons/web/static/src/js/view_list.js
index 77f04d4c864..728b090d679 100644
--- a/addons/web/static/src/js/view_list.js
+++ b/addons/web/static/src/js/view_list.js
@@ -295,7 +295,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
} else {
last = first + limit;
}
- this.$element.find('span.oe-pager-state').empty().text(_.sprintf(
+ this.$element.find('span.oe-pager-state').empty().text(_.str.sprintf(
"[%d to %d] of %d", first + 1, last, total));
this.$element
@@ -647,7 +647,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
return;
}
- $footer_cells.filter(_.sprintf('[data-field=%s]', column.id))
+ $footer_cells.filter(_.str.sprintf('[data-field=%s]', column.id))
.html(openerp.web.format_cell(aggregation, column, undefined, false));
});
},
@@ -1127,7 +1127,7 @@ openerp.web.ListView.Groups = openerp.web.Class.extend( /** @lends openerp.web.L
child.datagroup = group;
var $row = child.$row = $('<tr>');
- if (group.openable) {
+ if (group.openable && group.length) {
$row.click(function (e) {
if (!$row.data('open')) {
$row.data('open', true)
@@ -1159,7 +1159,13 @@ openerp.web.ListView.Groups = openerp.web.Class.extend( /** @lends openerp.web.L
} catch (e) {
$group_column.html(row_data[group_column.id].value);
}
- if (group.openable) {
+ if (!group.length) {
+ // Kinda-ugly hack: jquery-ui has no "empty" icon, so set
+ // wonky background position to ensure nothing is displayed
+ // there but the rest of the behavior is ui-icon's
+ $group_column.prepend(
+ '<span class="ui-icon" style="float: left; background-position: 150px 150px">');
+ } else if (group.openable) {
// Make openable if not terminal group & group_by_no_leaf
$group_column
.prepend('<span class="ui-icon ui-icon-triangle-1-e" style="float: left;">');
@@ -1186,7 +1192,7 @@ openerp.web.ListView.Groups = openerp.web.Class.extend( /** @lends openerp.web.L
format = "%.2f";
}
$('<td>')
- .text(_.sprintf(format, value))
+ .text(_.str.sprintf(format, value))
.appendTo($row);
} else {
$row.append('<td>');
@@ -1245,7 +1251,7 @@ openerp.web.ListView.Groups = openerp.web.Class.extend( /** @lends openerp.web.L
var pages = Math.ceil(dataset.ids.length / limit);
self.$row
.find('.oe-pager-state')
- .text(_.sprintf('%d/%d', page + 1, pages))
+ .text(_.str.sprintf('%d/%d', page + 1, pages))
.end()
.find('button[data-pager-action=previous]')
.attr('disabled', page === 0)
diff --git a/addons/web/static/src/js/view_tree.js b/addons/web/static/src/js/view_tree.js
index 5143adb3838..34d47b92813 100644
--- a/addons/web/static/src/js/view_tree.js
+++ b/addons/web/static/src/js/view_tree.js
@@ -42,7 +42,8 @@ openerp.web.TreeView = openerp.web.View.extend(/** @lends openerp.web.TreeView#
model: this.model,
view_id: this.view_id,
view_type: "tree",
- toolbar: this.view_manager ? !!this.view_manager.sidebar : false
+ toolbar: this.view_manager ? !!this.view_manager.sidebar : false,
+ context: this.dataset.get_context()
}, this.on_loaded);
},
/**
@@ -214,17 +215,23 @@ openerp.web.TreeView = openerp.web.View.extend(/** @lends openerp.web.TreeView#
// Get details in listview
activate: function(id) {
var self = this;
+ var local_context = {
+ active_model: self.dataset.model,
+ active_id: id,
+ active_ids: [id]};
this.rpc('/web/treeview/action', {
id: id,
model: this.dataset.model,
context: new openerp.web.CompoundContext(
- this.dataset.get_context(), {
- active_model: this.dataset.model,
- active_id: id,
- active_ids: [id]})
+ this.dataset.get_context(), local_context)
}, function (actions) {
if (!actions.length) { return; }
var action = actions[0][2];
+ var c = new openerp.web.CompoundContext(local_context);
+ if (action.context) {
+ c.add(action.context);
+ }
+ action.context = c;
self.do_action(action);
});
},
diff --git a/addons/web/static/src/js/views.js b/addons/web/static/src/js/views.js
index efa5ac5b2a8..eff94b95ad7 100644
--- a/addons/web/static/src/js/views.js
+++ b/addons/web/static/src/js/views.js
@@ -210,7 +210,7 @@ session.web.ViewManager = session.web.Widget.extend(/** @lends session.web.View
sidebar_id : self.element_id + '_sidebar_' + view.view_type,
action : self.action,
action_views_ids : views_ids
- }, self.flags, view.options || {})
+ }, self.flags, self.flags[view.view_type] || {}, view.options || {})
});
views_ids[view.view_type] = view.view_id;
});
@@ -534,7 +534,7 @@ session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepner
$logs_list = $logs.find('ul').empty();
$logs.toggleClass('oe-has-more', log_records.length > cutoff);
_(log_records.reverse()).each(function (record) {
- $(_.sprintf('<li><a href="#">%s</a></li>', record.name))
+ $(_.str.sprintf('<li><a href="#">%s</a></li>', record.name))
.appendTo($logs_list)
.delegate('a', 'click', function (e) {
self.do_action({
@@ -628,7 +628,7 @@ session.web.Sidebar = session.web.Widget.extend({
},
add_section: function(name, code) {
- if(!code) code = _.underscored(name);
+ if(!code) code = _.str.underscored(name);
var $section = this.sections[code];
if(!$section) {
@@ -656,7 +656,7 @@ session.web.Sidebar = session.web.Widget.extend({
//
var self = this,
- $section = this.add_section(_.titleize(section_code.replace('_', ' ')), section_code),
+ $section = this.add_section(_.str.titleize(section_code.replace('_', ' ')), section_code),
section_id = $section.attr('id');
if (items) {
diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml
index 25cfaee1232..6d35cf15d6c 100644
--- a/addons/web/static/src/xml/base.xml
+++ b/addons/web/static/src/xml/base.xml
@@ -18,7 +18,7 @@
</div>
</t>
<t t-name="Interface">
- <div id="oe_loading" class="loading"></div>
+ <div id="oe_loading"></div>
<table border="0" cellpadding="0" cellspacing="0" width="100%" height="100%" class="main_table">
<tr>
<td colspan="2" valign="top">
@@ -63,7 +63,9 @@
</table>
</t>
<t t-name="Loading">
+ <div class="loading">
Loading...
+ </div>
</t>
<t t-name="Database">
<ul class="db_options" style="padding: 0px; display: inline;">
@@ -703,7 +705,7 @@
</t>
<t t-name="Widget">
Unhandled widget
- <t t-raw="console.log('Unhandled widget', widget)"/>
+ <t t-js="dict">console.warn('Unhandled widget', dict.widget);</t>
</t>
<t t-name="WidgetFrame">
<table border="0" width="100%" cellpadding="0" cellspacing="0" class="oe_frame oe_forms">
@@ -896,13 +898,13 @@
</t>
<t t-name="web.datetimepicker">
<div class="oe_datepicker_root">
- <input type="text" size="1" style="width: 100%"/>
+ <input type="text" size="1" style="width: 100%"
+ t-att-name="widget.name"
+ t-attf-class="oe_datepicker_master field_#{widget.type_of_date}"
+ />
<img class="oe_input_icon oe_datepicker_trigger" src="/web/static/src/img/ui/field_calendar.png"
title="Select date" width="16" height="16" border="0"/>
- <div class="oe_datepicker ui-widget-content ui-corner-all" style="display: none; position: absolute; z-index: 1;">
- <div class="oe_datepicker_container"/>
- <button type="button" class="oe_datepicker_close ui-state-default ui-priority-primary ui-corner-all" style="float: right;">Done</button>
- </div>
+ <input type="text" size="1" class="oe_datepicker_container" disabled="disabled"/>
</div>
</t>
<t t-name="FieldSelection">
@@ -1070,7 +1072,7 @@
</table>
</t>
<t t-name="WidgetButton">
- <button type="button" style="width: 100%" class="button">
+ <button type="button" class="oe_button">
<img t-if="widget.node.attrs.icon" t-att-src="'/web/static/src/img/icons/' + widget.node.attrs.icon + '.png'" width="16" height="16"/>
<span t-if="widget.string"><t t-esc="widget.string"/></span>
</button>
@@ -1152,8 +1154,8 @@
t-att-title="attrs.help"
t-att-class="classes.join(' ')"
t-att-autofocus="attrs.default_focus === '1' ? 'autofocus' : undefined">
- <img t-if="attrs.icon" t-att-src="'/web/static/src/img/icons/' + attrs.icon + '.png'" width="16" height="16"/>
- <br t-if="attrs.icon and attrs.string"/>
+ <img t-att-src="'/web/static/src/img/icons/' + (attrs.icon || 'gtk-home') + '.png'" width="16" height="16"/>
+ <br t-if="attrs.string"/>
<t t-esc="attrs.string"/>
</button>
<span t-name="SearchView.filters" class="filter_label_group"