summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js
diff options
context:
space:
mode:
authorChristophe Simonis <chs@odoo.com>2018-02-21 20:19:26 +0100
committerChristophe Simonis <chs@odoo.com>2018-02-21 20:19:26 +0100
commitae6a65753e57fd2e31f2fb9a0675fdc7266ad38c (patch)
treea16174284684978612798d19fab2e05ac492aef1 /addons/web/static/src/js
parent90e8da9b4f792919c14c7de1d8596bf2056f58b2 (diff)
parentdbb713c2f836892de516e8d5e313a5dab8919a56 (diff)
[MERGE] forward port branch 11.0 up to dbb713c2f83
Diffstat (limited to 'addons/web/static/src/js')
-rw-r--r--addons/web/static/src/js/chrome/web_client.js11
-rw-r--r--addons/web/static/src/js/core/domain.js2
-rw-r--r--addons/web/static/src/js/core/utils.js17
-rw-r--r--addons/web/static/src/js/views/basic/basic_controller.js4
-rw-r--r--addons/web/static/src/js/views/basic/basic_model.js73
-rw-r--r--addons/web/static/src/js/views/kanban/kanban_model.js1
-rw-r--r--addons/web/static/src/js/views/list/list_editable_renderer.js9
7 files changed, 89 insertions, 28 deletions
diff --git a/addons/web/static/src/js/chrome/web_client.js b/addons/web/static/src/js/chrome/web_client.js
index e725aaee8f6..4b32ffecfb3 100644
--- a/addons/web/static/src/js/chrome/web_client.js
+++ b/addons/web/static/src/js/chrome/web_client.js
@@ -59,7 +59,7 @@ return AbstractWebClient.extend({
update_logo: function(reload) {
var company = session.company_id;
var img = session.url('/web/binary/company_logo' + '?db=' + session.db + (company ? '&company=' + company : ''));
- this.$('.o_sub_menu_logo img').attr('src', '').attr('src', img + (reload ? "#" + Date.now() : ''));
+ this.$('.o_sub_menu_logo img').attr('src', '').attr('src', img + (reload ? "&t=" + Date.now() : ''));
this.$('.oe_logo_edit').toggleClass('oe_logo_edit_admin', session.is_superuser);
},
logo_edit: function(ev) {
@@ -83,13 +83,8 @@ return AbstractWebClient.extend({
action_buttons: true,
headless: true,
};
- self.action_manager.do_action(result).then(function () {
- var form = self.action_manager.dialog_widget.views.form.controller;
- form.on("on_button_cancel", self.action_manager, self.action_manager.dialog_stop);
- form.on('record_saved', self, function() {
- self.action_manager.dialog_stop();
- self.update_logo();
- });
+ self.action_manager.do_action(result, {
+ on_close: self.update_logo.bind(self, true),
});
});
});
diff --git a/addons/web/static/src/js/core/domain.js b/addons/web/static/src/js/core/domain.js
index 75aaf0105ed..8ce570b8605 100644
--- a/addons/web/static/src/js/core/domain.js
+++ b/addons/web/static/src/js/core/domain.js
@@ -240,7 +240,7 @@ var Domain = collections.Tree.extend({
*/
stringToArray: function (domain, evalContext) {
if (!_.isString(domain)) return _.clone(domain);
- return pyeval.eval("domain", domain || "[]", evalContext);
+ return pyeval.eval("domain", domain ? domain.replace(/%%/g, '%') : "[]", evalContext);
},
/**
* Makes implicit "&" operators explicit in the given JS prefix-array
diff --git a/addons/web/static/src/js/core/utils.js b/addons/web/static/src/js/core/utils.js
index 1bdd9e70a69..251cd2b508c 100644
--- a/addons/web/static/src/js/core/utils.js
+++ b/addons/web/static/src/js/core/utils.js
@@ -392,6 +392,23 @@ var utils = {
array[i2] = elem1;
array[i1] = elem2;
},
+
+ /**
+ * @param {string} value
+ * @param {boolean} allow_mailto
+ * @returns boolean
+ */
+ is_email: function (value, allow_mailto) {
+ // http://stackoverflow.com/questions/46155/validate-email-address-in-javascript
+ var re;
+ if (allow_mailto) {
+ re = /^(mailto:)?(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i;
+ } else {
+ re = /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i;
+ }
+ return re.test(value);
+ },
+
/**
* @param {any} str
* @param {any} elseValues
diff --git a/addons/web/static/src/js/views/basic/basic_controller.js b/addons/web/static/src/js/views/basic/basic_controller.js
index 432d810bbbb..6f07ac1c2f8 100644
--- a/addons/web/static/src/js/views/basic/basic_controller.js
+++ b/addons/web/static/src/js/views/basic/basic_controller.js
@@ -332,7 +332,9 @@ var BasicController = AbstractController.extend(FieldManagerMixin, {
}
self.model.discardChanges(recordID);
if (self.model.isNew(recordID)) {
- self._abandonRecord(recordID);
+ if (self.model.canBeAbandoned(recordID)) {
+ self._abandonRecord(recordID);
+ }
return;
}
return self._confirmSave(recordID);
diff --git a/addons/web/static/src/js/views/basic/basic_model.js b/addons/web/static/src/js/views/basic/basic_model.js
index b5b668a43a1..6eef41f149d 100644
--- a/addons/web/static/src/js/views/basic/basic_model.js
+++ b/addons/web/static/src/js/views/basic/basic_model.js
@@ -478,6 +478,30 @@ var BasicModel = AbstractModel.extend({
return _t("New");
},
/**
+ * Returns true if a record can be abandoned from a list datapoint.
+ *
+ * A record cannot be abandonned if it has been registered as "added"
+ * in the parent's savepoint, otherwise it can be abandonned.
+ *
+ * This is useful when discarding changes on this record, as it means that
+ * we must keep the record even if some fields are invalids (e.g. required
+ * field is empty).
+ *
+ * @param {string} id id for a local resource
+ * @returns {boolean}
+ */
+ canBeAbandoned: function (id) {
+ var data = this.localData[id];
+ var parent = this.localData[data.parentID];
+ var abandonable = true;
+ if (parent) {
+ abandonable = !_.some(parent._savePoint, function (entry) {
+ return entry.operation === 'ADD' && entry.id === id;
+ });
+ }
+ return abandonable;
+ },
+ /**
* Returns true if a record is dirty. A record is considered dirty if it has
* some unsaved changes, marked by the _isDirty property on the record or
* one of its subrecords.
@@ -1428,25 +1452,23 @@ var BasicModel = AbstractModel.extend({
defs.push(self._applyOnChange(command[2], rec));
} else if (command[0] === 4) {
// LINK TO
- rec = self.localData[list._cache[command[1]]];
- if (rec) {
- // modifications done on a record are discarded if
- // the onchange uses a LINK TO
- self.discardChanges(rec.id);
- }
- // the dataPoint id will be set when the record will be
- // fetched (for now, this dataPoint may not exist yet)
- list._changes.push({
- operation: 'ADD',
- id: rec ? rec.id : null,
- resID: command[1],
- });
+ linkRecord(list, command[1]);
} else if (command[0] === 5) {
// DELETE ALL
list._changes = [{operation: 'REMOVE_ALL'}];
+ } else if (command[0] === 6) {
+ list._changes = [{operation: 'REMOVE_ALL'}];
+ _.each(command[2], function (resID) {
+ linkRecord(list, resID);
+ });
}
});
- defs.push(self._readUngroupedList(list));
+ var def = self._readUngroupedList(list).then(function () {
+ var x2ManysDef = self._fetchX2ManysBatched(list);
+ var referencesDef = self._fetchReferencesBatched(list);
+ return $.when(x2ManysDef, referencesDef);
+ });
+ defs.push(def);
} else {
var newValue = self._parseServerValue(field, val);
if (newValue !== oldValue) {
@@ -1455,6 +1477,25 @@ var BasicModel = AbstractModel.extend({
}
});
return $.when.apply($, defs);
+
+ // inner function that adds a record (based on its res_id) to a list
+ // dataPoint (used for onchanges that return commands 4 (LINK TO) or
+ // commands 6 (REPLACE WITH))
+ function linkRecord (list, resID) {
+ rec = self.localData[list._cache[resID]];
+ if (rec) {
+ // modifications done on a record are discarded if the onchange
+ // uses a LINK TO or a REPLACE WITH
+ self.discardChanges(rec.id);
+ }
+ // the dataPoint id will be set when the record will be fetched (for
+ // now, this dataPoint may not exist yet)
+ list._changes.push({
+ operation: 'ADD',
+ id: rec ? rec.id : null,
+ resID: resID,
+ });
+ }
},
/**
* When an operation is applied to a x2many field, the field widgets
@@ -1951,7 +1992,9 @@ var BasicModel = AbstractModel.extend({
_.each(list.data, function (dataPoint) {
var record = self.localData[dataPoint];
var value = record.data[fieldName];
- if (value) {
+ // if the reference field has already been fetched, the value is a
+ // datapoint ID, and in this case there's nothing to do
+ if (value && !self.localData[value]) {
var model = value.split(',')[0];
var resID = value.split(',')[1];
if (!(model in toFetch)) {
diff --git a/addons/web/static/src/js/views/kanban/kanban_model.js b/addons/web/static/src/js/views/kanban/kanban_model.js
index df6172f0d8b..0b0ec09ddf4 100644
--- a/addons/web/static/src/js/views/kanban/kanban_model.js
+++ b/addons/web/static/src/js/views/kanban/kanban_model.js
@@ -73,7 +73,6 @@ var KanbanModel = BasicModel.extend({
domain: parent.domain.concat([[groupBy,"=",result[0]]]),
fields: parent.fields,
fieldsInfo: parent.fieldsInfo,
- groupedBy: parent.groupedBy,
isOpen: true,
limit: parent.limit,
parentID: parent.id,
diff --git a/addons/web/static/src/js/views/list/list_editable_renderer.js b/addons/web/static/src/js/views/list/list_editable_renderer.js
index b57d44e7de2..48078e2a22c 100644
--- a/addons/web/static/src/js/views/list/list_editable_renderer.js
+++ b/addons/web/static/src/js/views/list/list_editable_renderer.js
@@ -54,8 +54,13 @@ ListRenderer.include({
* @returns {Deferred}
*/
start: function () {
- if (this._isEditable()) {
- this.$el.css({height: '100%'});
+ // deliberately use the 'editable' attribute instead of '_isEditable'
+ // function, because the groupBy must not be taken into account to
+ // enable the '_onWindowClicked' handler (otherwise, an editable grouped
+ // list which is reloaded without groupBy wouldn't have this handler
+ // bound, and edited rows couldn't be left by clicking outside the list)
+ if (this.editable) {
+ this.$el.css({height: '100%'}); // seems useless: to remove in master
core.bus.on('click', this, this._onWindowClicked.bind(this));
}
return this._super();