diff options
| author | Christophe Simonis <chs@odoo.com> | 2019-09-13 18:07:59 +0200 |
|---|---|---|
| committer | Christophe Simonis <chs@odoo.com> | 2019-09-13 18:07:59 +0200 |
| commit | 6c948c36bbf72dd896e0bff03aa7fbddadcbb0cd (patch) | |
| tree | f8620905c011735d8a3c340c46d1417a54a75972 /addons/web/static/src/js/widgets | |
| parent | 48503fcf08eac6fed12e080d4f501f9bbc8d257d (diff) | |
| parent | 197474e5b9ad43ab2d587a98136a1a809ce9a2e7 (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.js | 12 | ||||
| -rw-r--r-- | addons/web/static/src/js/widgets/data_export.js | 6 | ||||
| -rw-r--r-- | addons/web/static/src/js/widgets/date_picker.js | 5 | ||||
| -rw-r--r-- | addons/web/static/src/js/widgets/translation_dialog.js | 172 |
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; +}); |
