diff --git a/addons/project/__manifest__.py b/addons/project/__manifest__.py index c950c348b27..129d5684a55 100644 --- a/addons/project/__manifest__.py +++ b/addons/project/__manifest__.py @@ -153,7 +153,6 @@ 'web/static/src/legacy/backend_utils.js', 'web/static/src/legacy/utils.js', 'web/static/src/legacy/web_client.js', - 'web/static/src/legacy/js/_deprecated/data.js', 'web/static/src/legacy/js/chrome/*', 'web/static/src/legacy/js/components/*', 'web/static/src/legacy/js/control_panel/*', @@ -177,7 +176,6 @@ 'web/static/src/legacy/js/services/session.js', 'web/static/src/legacy/js/tools/tools.js', 'web/static/src/legacy/js/views/**/*', - 'web/static/src/legacy/js/widgets/data_export.js', 'web/static/src/legacy/js/widgets/date_picker.js', 'web/static/src/legacy/js/widgets/domain_selector_dialog.js', 'web/static/src/legacy/js/widgets/domain_selector.js', diff --git a/addons/web/__manifest__.py b/addons/web/__manifest__.py index 45253951b0c..60f19ca9a89 100644 --- a/addons/web/__manifest__.py +++ b/addons/web/__manifest__.py @@ -216,7 +216,6 @@ This module provides the core of the Odoo Web Client. 'web/static/src/legacy/scss/ace.scss', 'web/static/src/legacy/scss/fields.scss', 'web/static/src/legacy/scss/views.scss', - 'web/static/src/legacy/scss/data_export.scss', 'base/static/src/scss/onboarding.scss', 'web/static/src/legacy/scss/attachment_preview.scss', 'web/static/src/legacy/scss/base_document_layout.scss', @@ -256,7 +255,6 @@ This module provides the core of the Odoo Web Client. 'web/static/src/legacy/js/services/session.js', 'web/static/src/legacy/js/tools/tools.js', 'web/static/src/legacy/js/views/**/*', - 'web/static/src/legacy/js/widgets/data_export.js', 'web/static/src/legacy/js/widgets/date_picker.js', 'web/static/src/legacy/js/widgets/domain_selector_dialog.js', 'web/static/src/legacy/js/widgets/domain_selector.js', diff --git a/addons/web/static/src/legacy/js/_deprecated/data.js b/addons/web/static/src/legacy/js/_deprecated/data.js deleted file mode 100644 index 71ae101da7a..00000000000 --- a/addons/web/static/src/legacy/js/_deprecated/data.js +++ /dev/null @@ -1,852 +0,0 @@ -odoo.define('web.data', function (require) { -"use strict"; - -var Class = require('web.Class'); -var Context = require('web.Context'); -var concurrency = require('web.concurrency'); -var mixins = require('web.mixins'); -var session = require('web.session'); -var translation = require('web.translation'); -var pyUtils = require('web.py_utils'); - -var _t = translation._t; - -/** - * Serializes the sort criterion array of a dataset into a form which can be - * consumed by OpenERP's RPC APIs. - * - * @param {Array} criterion array of fields, from first to last criteria, prefixed with '-' for reverse sorting - * @returns {String} SQL-like sorting string (``ORDER BY``) clause - */ -function serialize_sort(criterion) { - return _.map(criterion, - function (criteria) { - if (criteria[0] === '-') { - return criteria.slice(1) + ' DESC'; - } - return criteria + ' ASC'; - }).join(', '); -} - -/** - * Reverse of the serialize_sort function: convert an array of SQL-like sort - * descriptors into a list of fields prefixed with '-' if necessary. - */ -function deserialize_sort(criterion) { - return _.map(criterion, function (criteria) { - var split = _.without(criteria.split(' '), ''); - return (split[1] && split[1].toLowerCase() === 'desc' ? '-' : '') + split[0]; - }); -} - -var Query = Class.extend({ - init: function (model, fields) { - this._model = model; - this._fields = fields; - this._filter = []; - this._context = {}; - this._lazy = true; - this._limit = false; - this._offset = 0; - this._order_by = []; - }, - clone: function (to_set) { - to_set = to_set || {}; - var q = new Query(this._model, this._fields); - q._context = this._context; - q._filter = this._filter; - q._lazy = this._lazy; - q._limit = this._limit; - q._offset = this._offset; - q._order_by = this._order_by; - - for(var key in to_set) { - if (!to_set.hasOwnProperty(key)) { continue; } - switch(key) { - case 'filter': - q._filter = (q._filter || []).concat(to_set.filter || []); - break; - case 'context': - q._context = new Context( - q._context, to_set.context); - break; - case 'lazy': - case 'limit': - case 'offset': - case 'order_by': - q['_' + key] = to_set[key]; - } - } - return q; - }, - _execute: function (options) { - var self = this; - options = options || {}; - return session.rpc('/web/dataset/search_read', { - model: this._model.name, - fields: this._fields || false, - domain: pyUtils.eval('domains', - [this._model.domain(this._filter)]), - context: pyUtils.eval('contexts', - [this._model.context(this._context)]), - offset: this._offset, - limit: this._limit, - sort: serialize_sort(this._order_by) - }, options).then(function (results) { - self._count = results.length; - return results.records; - }, null); - }, - /** - * Fetches the first record matching the query, or null - * - * @param {Object} [options] additional options for the rpc() method - * @returns {Promise} - */ - first: function (options) { - var self = this; - return this.clone({limit: 1})._execute(options).then(function (records) { - delete self._count; - if (records.length) { return records[0]; } - return null; - }); - }, - /** - * Fetches all records matching the query - * - * @param {Object} [options] additional options for the rpc() method - * @returns {Promise>} - */ - all: function (options) { - return this._execute(options); - }, - /** - * Fetches the number of records matching the query in the database - * - * @returns {Promise} - */ - count: function () { - if (this._count !== undefined) { return Promise.resolve(this._count); } - return this._model.call( - 'search_count', [this._filter], { - context: this._model.context(this._context)}); - }, - /** - * Performs a groups read according to the provided grouping criterion - * - * @param {String|Array} grouping - * @returns {jQuery.Deferred> | null} - */ - group_by: function (grouping) { - var ctx = pyUtils.eval( - 'context', this._model.context(this._context)); - - // undefined passed in explicitly (!) - if (_.isUndefined(grouping)) { - grouping = []; - } - - if (!(grouping instanceof Array)) { - grouping = _.toArray(arguments); - } - if (_.isEmpty(grouping) && !ctx.group_by_no_leaf) { - return null; - } - var raw_fields = _.map(grouping.concat(this._fields || []), function (field) { - return field.split(':')[0]; - }); - - var self = this; - return this._model.call('read_group', { - groupby: grouping, - fields: _.uniq(raw_fields), - domain: this._model.domain(this._filter), - context: ctx, - offset: this._offset, - lazy: this._lazy, - limit: this._limit, - orderby: serialize_sort(this._order_by) || false - }).then(function (results) { - return _(results).map(function (result) { - // FIX: querygroup initialization - result.__context = result.__context || {}; - result.__context.group_by = result.__context.group_by || []; - _.defaults(result.__context, ctx); - var grouping_fields = self._lazy ? [grouping[0]] : grouping; - return new QueryGroup( - self._model.name, grouping_fields, result); - }); - }); - }, - /** - * Creates a new query with the union of the current query's context and - * the new context. - * - * @param context context data to add to the query - * @returns {openerp.web.Query} - */ - context: function (context) { - if (!context) { return this; } - return this.clone({context: context}); - }, - /** - * Creates a new query with the union of the current query's filter and - * the new domain. - * - * @param domain domain data to AND with the current query filter - * @returns {openerp.web.Query} - */ - filter: function (domain) { - if (!domain) { return this; } - return this.clone({filter: domain}); - }, - /** - * Creates a new query with the provided parameter lazy replacing the current - * query's own. - * - * @param {Boolean} lazy indicates if the read_group should return only the - * first level of groupby records, or should return the records grouped by - * all levels at once (so, it makes only 1 db request). - * @returns {openerp.web.Query} - */ - lazy: function (lazy) { - return this.clone({lazy: lazy}); - }, - /** - * Creates a new query with the provided limit replacing the current - * query's own limit - * - * @param {Number} limit maximum number of records the query should retrieve - * @returns {openerp.web.Query} - */ - limit: function (limit) { - return this.clone({limit: limit}); - }, - /** - * Creates a new query with the provided offset replacing the current - * query's own offset - * - * @param {Number} offset number of records the query should skip before starting its retrieval - * @returns {openerp.web.Query} - */ - offset: function (offset) { - return this.clone({offset: offset}); - }, - /** - * Creates a new query with the provided ordering parameters replacing - * those of the current query - * - * @param {String...} fields ordering clauses - * @returns {openerp.web.Query} - */ - order_by: function (fields) { - if (fields === undefined) { return this; } - if (!(fields instanceof Array)) { - fields = _.toArray(arguments); - } - if (_.isEmpty(fields)) { return this; } - return this.clone({order_by: fields}); - } -}); - -var QueryGroup = Class.extend({ - init: function (model, grouping_fields, read_group_group) { - // In cases where group_by_no_leaf and no group_by, the result of - // read_group has aggregate fields but no __context or __domain. - // Create default (empty) values for those so that things don't break - var fixed_group = _.extend( - {__context: {group_by: []}, __domain: []}, - read_group_group); - - var count_key = (grouping_fields[0] && grouping_fields[0].split(':')[0]) + '_count'; - var aggregates = {}; - for (var key in fixed_group) { - if (fixed_group.hasOwnProperty(key)) { - if (!(key.indexOf('__') === 0 - || _.contains(grouping_fields, key) - || (key === count_key))) { - aggregates[key] = fixed_group[key] || 0; - } - } - } - - this.model = new Model( - model, fixed_group.__context, fixed_group.__domain); - - var group_size = fixed_group[count_key] || fixed_group.__count || 0; - var leaf_group = fixed_group.__context.group_by.length === 0; - - var value = (grouping_fields.length === 1) - ? fixed_group[grouping_fields[0]] - : _.map(grouping_fields, function (field) { return fixed_group[field]; }); - var grouped_on = (grouping_fields.length === 1) - ? grouping_fields[0] - : grouping_fields; - this.attributes = { - folded: !!(fixed_group.__fold), - grouped_on: grouped_on, - // if terminal group (or no group) and group_by_no_leaf => use group.__count - length: group_size, - value: value, - // A group is open-able if it's not a leaf in group_by_no_leaf mode - has_children: !(leaf_group && fixed_group.__context.group_by_no_leaf), - - aggregates: aggregates - }; - }, - get: function (key) { - return this.attributes[key]; - }, - subgroups: function () { - return this.model.query().group_by(this.model.context().group_by); - }, - query: function () { - return this.model.query.apply(this.model, arguments); - } -}); - -var DataSet = Class.extend(mixins.PropertiesMixin, { - /** - * Collection of OpenERP records, used to share records and the current selection between views. - * - * @constructs instance.web.DataSet - * - * @param {String} model the OpenERP model this dataset will manage - */ - init: function (parent, model, context) { - mixins.PropertiesMixin.init.call(this); - this.setParent(parent); - this.model = model; - this.context = context || {}; - this.index = null; - this._sort = []; - this._model = new Model(model, context); - this.orderer = new concurrency.DropMisordered(); - }, - previous: function () { - this.index -= 1; - if (!this.ids.length) { - this.index = null; - } else if (this.index < 0) { - this.index = this.ids.length - 1; - } - return this; - }, - next: function () { - this.index += 1; - if (!this.ids.length) { - this.index = null; - } else if (this.index >= this.ids.length) { - this.index = 0; - } - return this; - }, - select_id: function (id) { - var idx = this.get_id_index(id); - if (idx === null) { - return false; - } else { - this.index = idx; - return true; - } - }, - get_id_index: function (id) { - for (var i=0, ii=this.ids.length; i= ids.length - 1) { - this.index = ids.length - 1; - } - }, - unlink: function (ids) { - this.set_ids(_.without.apply(null, [this.ids].concat(ids))); - this.trigger('unlink', ids); - return Promise.resolve({result: true}); - }, -}); - -var DataSetSearch = DataSet.extend({ - /** - * @constructs instance.web.DataSetSearch - * @extends instance.web.DataSet - * - * @param {Object} parent - * @param {String} model - * @param {Object} context - * @param {Array} domain - */ - init: function (parent, model, context, domain) { - this._super(parent, model, context); - this.domain = domain || []; - this._length = null; - this.ids = []; - this._model = new Model(model, context, domain); - }, - /** - * Read a slice of the records represented by this DataSet, based on its - * domain and context. - * - * @params {Object} options - * @param {Array} [options.fields] fields to read and return, by default all fields are returned - * @param {Object} [options.context] context data to add to the request payload, on top of the DataSet's own context - * @param {Array} [options.domain] domain data to add to the request payload, ANDed with the dataset's domain - * @param {Number} [options.offset=0] The index from which selected records should be returned - * @param {Number} [options.limit=null] The maximum number of records to return - * @returns {Promise} - */ - read_slice: function (fields, options) { - options = options || {}; - var self = this; - var q = this._model.query(fields || false) - .filter(options.domain) - .context(options.context) - .offset(options.offset || 0) - .limit(options.limit || false); - q = q.order_by.apply(q, this._sort); - - var prom = this.orderer.add(q.all()); - prom.then(function (records) { - // FIXME: not sure about that one, *could* have discarded count - q.count().then(function (count) { self._length = count; }); - self.ids = _(records).pluck('id'); - }); - return prom; - }, - get_domain: function (other_domain) { - return this._model.domain(other_domain); - }, - alter_ids: function (ids) { - this._super(ids); - if (this.index !== null && this.index >= this.ids.length) { - this.index = this.ids.length > 0 ? this.ids.length - 1 : 0; - } - }, - remove_ids: function (ids) { - var before = this.ids.length; - this._super(ids); - if (this._length) { - this._length -= (before - this.ids.length); - } - }, - add_ids: function (ids, at) { - var before = this.ids.length; - this._super(ids, at); - if(this._length){ - this._length += (this.ids.length - before); - } - }, - unlink: function (ids, callback, error_callback) { - var self = this; - var prom = this._super(ids); - prom.then(function () { - self.remove_ids( ids); - self.trigger("dataset_changed", ids, callback, error_callback); - }); - return prom; - }, - size: function () { - if (this._length !== null) { - return this._length; - } - return this._super(); - } -}); - -var data = { - Query: Query, - DataSet: DataSet, - DataSetStatic: DataSetStatic, - DataSetSearch: DataSetSearch, - /** @type String */ - noDisplayContent: "" + _t("Unnamed") + "", -}; - - -var Model = Class.extend({ - /** - new openerp.web.Model(name[, context[, domain]]) - - @constructs instance.web.Model - @extends instance.web.Class - - @param {String} name name of the OpenERP model this object is bound to - @param {Object} [context] - @param {Array} [domain] - */ - init: function(name, context, domain) { - this.name = name; - this._context = context || {}; - this._domain = domain || []; - }, - /** - * @deprecated does not allow to specify kwargs, directly use call() instead - */ - get_func: function (method_name) { - var self = this; - return function () { - return self.call(method_name, _.toArray(arguments)); - }; - }, - /** - * Fetches a Query instance bound to this model, for searching - * - * @param {Array} [fields] fields to ultimately fetch during the search - * @returns {instance.web.Query} - */ - query: function (fields) { - return new data.Query(this, fields); - }, - /** - * Fetches the model's domain, combined with the provided domain if any - * - * @param {Array} [domain] to combine with the model's internal domain - * @returns {Array} The model's internal domain, or the AND-ed union of the model's internal domain and the provided domain - */ - domain: function (domain) { - if (!domain) { return this._domain; } - return this._domain.concat(domain); - }, - /** - * Fetches the combination of the user's context and the domain context, - * combined with the provided context if any - * - * @param {Object} [context] to combine with the model's internal context - * @returns {web.Context} The union of the user's context and the model's internal context, as well as the provided context if any. In that order. - */ - context: function (context) { - return new Context(session.user_context, this._context, context || {}); - }, - /** - * Call a method (over RPC) on the bound OpenERP model. - * - * @param {String} method name of the method to call - * @param {Array} [args] positipyEvalonal arguments - * @param {Object} [kwargs] keyword arguments - * @param {Object} [options] additional options for the rpc() method - * @returns {Promise<>} call result - */ - call: function (method, args, kwargs, options) { - args = args || []; - kwargs = kwargs || {}; - if (!_.isArray(args)) { - // call(method, kwargs) - kwargs = args; - args = []; - } - pyUtils.ensure_evaluated(args, kwargs); - var call_kw = '/web/dataset/call_kw/' + this.name + '/' + method; - return session.rpc(call_kw, { - model: this.name, - method: method, - args: args, - kwargs: kwargs - }, options); - }, - call_button: function (method, args) { - pyUtils.ensure_evaluated(args, {}); - // context should be the last argument - var context = (args || []).length > 0 ? args.pop() : {}; - return session.rpc('/web/dataset/call_button', { - model: this.name, - method: method, - args: args || [], - kwargs: {context: context}, - }); - }, -}); - - -return data; - -}); diff --git a/addons/web/static/src/legacy/js/fields/relational_fields.js b/addons/web/static/src/legacy/js/fields/relational_fields.js index 3e13931f3df..dc0c82ac8e9 100644 --- a/addons/web/static/src/legacy/js/fields/relational_fields.js +++ b/addons/web/static/src/legacy/js/fields/relational_fields.js @@ -16,7 +16,6 @@ odoo.define('web.relational_fields', function (require) { var AbstractField = require('web.AbstractField'); var concurrency = require('web.concurrency'); var core = require('web.core'); -var data = require('web.data'); var Dialog = require('web.Dialog'); var dom = require('web.dom'); const { FormViewDialog } = require("@web/views/view_dialogs/form_view_dialog"); @@ -668,7 +667,7 @@ var FieldMany2One = AbstractField.extend({ result[1] = displayName; return { id, - label: escape(displayName) || data.noDisplayContent, + label: escape(displayName) || `${escape(_t("Unnamed"))}`, value: displayName, name: displayName, }; diff --git a/addons/web/static/src/legacy/js/widgets/data_export.js b/addons/web/static/src/legacy/js/widgets/data_export.js deleted file mode 100644 index 8f651933025..00000000000 --- a/addons/web/static/src/legacy/js/widgets/data_export.js +++ /dev/null @@ -1,694 +0,0 @@ -odoo.define('web.DataExport', function (require) { -"use strict"; - -var config = require('web.config'); -var core = require('web.core'); -var Dialog = require('web.Dialog'); -var data = require('web.data'); -var framework = require('web.framework'); -var pyUtils = require('web.py_utils'); - -var QWeb = core.qweb; -var _t = core._t; - -var DataExport = Dialog.extend({ - template: 'ExportDialog', - events: { - 'change .o_exported_lists_select': '_onChangeExportList', - 'change .o_import_compat input': '_onChangeCompatibleInput', - 'click .o_add_field': '_onClickAddField', - 'click .o_delete_exported_list': '_onClickDeleteExportListBtn', - 'click .o_expand': '_onClickExpand', - 'click .o_remove_field': '_onClickRemoveField', - 'click .o_save_list .o_save_list_btn': '_onClickSaveListBtn', - 'click .o_save_list .o_cancel_list_btn': '_resetTemplateField', - 'click .o_export_tree_item': '_onClickTreeItem', - 'dblclick .o_export_tree_item:not(.haschild)': '_onDblclickTreeItem', - 'keydown .o_export_tree_item': '_onKeydownTreeItem', - 'keydown .o_save_list_name': '_onKeydownSaveList', - 'input .o_export_search_input': '_onSearchInput', - }, - /** - * @constructor - * @param {Widget} parent - * @param {Object} record - * @param {string[]} defaultExportFields - */ - init: function (parent, record, defaultExportFields, groupedBy, activeDomain, idsToExport) { - var options = { - title: _t("Export Data"), - buttons: [ - {text: _t("Export"), click: this._onExportData, classes: 'btn-primary'}, - {text: _t("Close"), close: true}, - ], - fullscreen: config.device.isMobile, - }; - this._super(parent, options); - this.records = {}; - this.record = record; - this.defaultExportFields = defaultExportFields; - this.groupby = groupedBy; - this.exports = new data.DataSetSearch(this, 'ir.exports', this.record.getContext()); - this.rowIndex = 0; - this.rowIndexLevel = 0; - this.isCompatibleMode = false; - this.domain = activeDomain || this.record.domain; - this.idsToExport = activeDomain ? false: idsToExport; - }, - /** - * @override - */ - start: function () { - var self = this; - var proms = [this._super.apply(this, arguments)]; - - // The default for the ".modal_content" element is "max-height: 100%;" - // but we want it to always expand to "height: 100%;" for this modal. - // This can be achieved thanks to CSS modification without touching - // the ".modal-content" rules... but not with Internet explorer (11). - this.$modal.find('.modal-content').css('height', '100%'); - - this.$fieldsList = this.$('.o_fields_list'); - - proms.push(this._rpc({route: '/web/export/formats'}).then(doSetupExportFormats)); - proms.push(this._onChangeCompatibleInput().then(function () { - _.each(self.defaultExportFields, function (field) { - var record = self.records[field]; - self._addField(record.id, record.string); - }); - })); - - proms.push(this._showExportsList()); - - // Bind sortable events after Dialog is open - this.opened().then(function () { - self.$('.o_fields_list').sortable({ - axis: 'y', - cursor: 'grabbing', - handle: '.o_short_field', - forcePlaceholderSize: true, - placeholder: 'o-field-placeholder bg-light border border-primary', - update: self.proxy('_resetTemplateField'), - }); - }); - return Promise.all(proms); - - function doSetupExportFormats(formats) { - var $fmts = self.$('.o_export_format'); - - _.each(formats, function (format) { - var $radio = $('', {type: 'radio', value: format.tag, name: 'o_export_format_name', class: 'form-check-input', id: 'o_radio' + format.label}); - var $label = $('