summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js/widgets
diff options
context:
space:
mode:
authorChristophe Simonis <chs@odoo.com>2019-09-13 18:07:59 +0200
committerChristophe Simonis <chs@odoo.com>2019-09-13 18:07:59 +0200
commit6c948c36bbf72dd896e0bff03aa7fbddadcbb0cd (patch)
treef8620905c011735d8a3c340c46d1417a54a75972 /addons/web/static/src/js/widgets
parent48503fcf08eac6fed12e080d4f501f9bbc8d257d (diff)
parent197474e5b9ad43ab2d587a98136a1a809ce9a2e7 (diff)
[MERGE] forward port branch saas-12.5 up to 197474e5b9a
Diffstat (limited to 'addons/web/static/src/js/widgets')
-rw-r--r--addons/web/static/src/js/widgets/auto_complete.js12
-rw-r--r--addons/web/static/src/js/widgets/data_export.js6
-rw-r--r--addons/web/static/src/js/widgets/date_picker.js5
-rw-r--r--addons/web/static/src/js/widgets/translation_dialog.js172
4 files changed, 191 insertions, 4 deletions
diff --git a/addons/web/static/src/js/widgets/auto_complete.js b/addons/web/static/src/js/widgets/auto_complete.js
index 4da54cf8b5c..1bb00be8d38 100644
--- a/addons/web/static/src/js/widgets/auto_complete.js
+++ b/addons/web/static/src/js/widgets/auto_complete.js
@@ -140,12 +140,22 @@ return Widget.extend({
var $item = self.make_list_item(result).appendTo($list);
result.$el = $item;
});
+ // IE9 doesn't support addEventListener with option { once: true }
+ this.el.onmousemove = function (ev) {
+ self.$('li').each(function (index, li) {
+ li.onmouseenter = self.focus_element.bind(self, $(li));
+ });
+ var targetFocus = ev.target.tagName === 'LI' ?
+ ev.target :
+ ev.target.closest('li');
+ self.focus_element($(targetFocus));
+ self.el.onmousemove = null;
+ };
this.show();
},
make_list_item: function (result) {
var self = this;
var $li = $('<li>')
- .hover(function () {self.focus_element($li);})
.mousedown(function (ev) {
if (ev.button === 0) { // left button
self.select(ev, {item: {facet: result.facet}});
diff --git a/addons/web/static/src/js/widgets/data_export.js b/addons/web/static/src/js/widgets/data_export.js
index 964e03e4aa1..070b1927c5f 100644
--- a/addons/web/static/src/js/widgets/data_export.js
+++ b/addons/web/static/src/js/widgets/data_export.js
@@ -178,7 +178,7 @@ var DataExport = Dialog.extend({
})
},
complete: framework.unblockUI,
- error: () => this.call('crash_manager', 'rpc_error', ...arguments),
+ error: (error) => this.call('crash_manager', 'rpc_error', error),
});
},
/**
@@ -647,7 +647,9 @@ var DataExport = Dialog.extend({
}
var matchItems = this.$(".o_tree_column").filter(function () {
- return this.innerText.toUpperCase().indexOf(searchText) >= 0;
+ var title = this.getAttribute('title');
+ return this.innerText.toUpperCase().indexOf(searchText) >= 0
+ || title && title.toUpperCase().indexOf(searchText) >= 0;
}).parent();
this.$(".o_export_tree_item").hide();
if (matchItems.length) {
diff --git a/addons/web/static/src/js/widgets/date_picker.js b/addons/web/static/src/js/widgets/date_picker.js
index 5b4480838fe..ac8e83c8e30 100644
--- a/addons/web/static/src/js/widgets/date_picker.js
+++ b/addons/web/static/src/js/widgets/date_picker.js
@@ -92,8 +92,8 @@ var DateWidget = Widget.extend({
* set datetime value
*/
changeDatetime: function () {
+ var oldValue = this.getValue();
if (this.isValid()) {
- var oldValue = this.getValue();
this._setValueFromUi();
var newValue = this.getValue();
var hasChanged = !oldValue !== !newValue;
@@ -110,6 +110,9 @@ var DateWidget = Widget.extend({
}
this.trigger("datetime_changed");
}
+ } else {
+ var formattedValue = oldValue ? this._formatClient(oldValue) : null;
+ this.$input.val(formattedValue);
}
},
/**
diff --git a/addons/web/static/src/js/widgets/translation_dialog.js b/addons/web/static/src/js/widgets/translation_dialog.js
new file mode 100644
index 00000000000..39d841a48d0
--- /dev/null
+++ b/addons/web/static/src/js/widgets/translation_dialog.js
@@ -0,0 +1,172 @@
+odoo.define('web.TranslationDialog', function (require) {
+ 'use strict';
+
+ var core = require('web.core');
+ var Dialog = require('web.Dialog');
+ var session = require('web.session');
+
+ var _t = core._t;
+
+ var TranslationDialog = Dialog.extend({
+ xmlDependencies: (Dialog.prototype.xmlDependencies || [])
+ .concat(['/web/static/src/xml/translation_dialog.xml']),
+ template: 'TranslationDialog',
+
+ /**
+ * @constructor
+ * @param {Widget} parent
+ * @param {Object} [options]
+ * @param {string} [options.domain] the domain needed to get the translation terms
+ * @param {string} [options.fieldName] the name of the field currently translated (from the model of the form view)
+ * @param {string} [options.searchName] the name of the actual field that is the reference for translation (in the form of model,field)
+ * @param {string} [options.userLanguageValue] the value of the translation in the language of the user, as seen in the from view (might be empty)
+ * @param {string} [options.dataPointID] the data point id of the record for which we do the translations
+ * @param {boolean} [options.isComingFromTranslationAlert] the initiator of the dialog, might be a link on a field or the translation alert on top of the form
+ * @param {boolean} [options.isText] is the field a text field (multiline) or char (single line)
+ *
+ */
+ init: function (parent, options) {
+ options = options || {};
+
+ this.fieldName = options.fieldName;
+ this.domain = options.domain;
+ this.searchName = options.searchName;
+ this.userLanguageValue = options.userLanguageValue;
+ this.domain.push(['name', "=", `${this.searchName}`]);
+ this.dataPointID = options.dataPointID;
+ this.isComingFromTranslationAlert = options.isComingFromTranslationAlert;
+ this.currentInterfaceLanguage = session.user_context.lang;
+ this.isText = options.isText;
+
+ this._super(parent, _.extend({
+ size: 'large',
+ title: _t('Translate: ') + `${this.fieldName}`,
+ buttons: [
+ { text: _t('Save'), classes: 'btn-primary', close: true, click: this._onSave.bind(this) },
+ { text: _t('Discard'), close: true },
+ ],
+ }, options));
+ },
+ /**
+ * @override
+ */
+ willStart: function () {
+ return Promise.all([
+ this._super(),
+ this._loadLanguages().then((l) => {
+ this.languages = l;
+ }),
+ this._loadTranslations().then((t) => {
+ this.translations = t;
+ })
+ ]).then(() => {
+ this.data = this.translations.map((term) => {
+ let relatedLanguage = this.languages.find((language) => language[0] === term.lang);
+ return {
+ id: term.id,
+ lang: term.lang,
+ langName: relatedLanguage[1],
+ // we set the translation value coming from the database, except for the language
+ // the user is currently utilizing. Then we set the translation value coming
+ // from the value of the field in the form
+ value: (term.lang === this.currentInterfaceLanguage &&
+ !this.isComingFromTranslationAlert) ?
+ this.userLanguageValue : term.value || ''
+ };
+ });
+ this.data.sort((left, right) => left.langName < right.langName ? -1 : 1);
+ });
+ },
+
+ //--------------------------------------------------------------------------
+ // Private
+ //--------------------------------------------------------------------------
+ /**
+ * Load the translation terms for the installed language, for the current model and res_id
+ * @private
+ */
+ _loadTranslations: function () {
+ return this._rpc({
+ model: 'ir.translation',
+ method: 'search_read',
+ fields: ['lang', 'value'],
+ domain: this.domain,
+ });
+ },
+ /**
+ * Load the installed languages long names and code
+ *
+ * The result of the call is put in cache on the prototype of this dialog.
+ * If any new language is installed, a full page refresh will happen,
+ * so there is no need invalidate it.
+ * @private
+ */
+ _loadLanguages: function () {
+ if (TranslationDialog.prototype.installedLanguagesCache)
+ return Promise.resolve(TranslationDialog.prototype.installedLanguagesCache);
+
+ return this._rpc({
+ model: 'res.lang',
+ method: 'get_installed',
+ fields: ['code', 'name', 'iso_code'],
+ }).then((installedLanguages) => {
+ TranslationDialog.prototype.installedLanguagesCache = installedLanguages;
+ return TranslationDialog.prototype.installedLanguagesCache
+ });
+ },
+
+ //--------------------------------------------------------------------------
+ // Handlers
+ //--------------------------------------------------------------------------
+ /**
+ * Save all the terms that have been updated
+ * @private
+ * @returns a promise that is resolved when all the save have occured
+ */
+ _onSave: function () {
+ var updatedTerm = {};
+ var updateFormViewField;
+
+ this.el.querySelectorAll('input[type=text],textarea').forEach((t) => {
+ var initialValue = this.data.find((d) => d.id == t.dataset.id);
+ if (initialValue.value !== t.value) {
+ updatedTerm[t.dataset.id] = t.value;
+
+ if (initialValue.lang === this.currentInterfaceLanguage) {
+ // when the user has changed the term for the language he is
+ // using in the interface, this change should be reflected
+ // in the form view
+ var changes = {};
+ changes[this.fieldName] = updatedTerm[initialValue.id];
+ updateFormViewField = {
+ dataPointID: this.dataPointID,
+ changes: changes,
+ doNotSetDirty: false,
+ };
+ }
+ }
+ });
+
+ // updatedTerm only contains the id and values of the terms that
+ // have been updated by the user
+ var saveUpdatedTermsProms = Object.keys(updatedTerm).map((id) => {
+ var writeTranslation = {
+ model: 'ir.translation',
+ method: 'write',
+ context: this.context,
+ args: [[parseInt(id, 10)], { value: updatedTerm[id] }]
+ };
+ return this._rpc(writeTranslation);
+ });
+ return Promise.all(saveUpdatedTermsProms).then(() => {
+ // we might have to update the value of the field on the form
+ // view that opened the translation dialog
+ if (updateFormViewField) {
+ this.trigger_up('field_changed', updateFormViewField);
+ }
+ });
+ }
+ });
+
+ return TranslationDialog;
+});