diff --git a/addons/web/static/src/js/widgets/data_export.js b/addons/web/static/src/js/widgets/data_export.js index 347769b4042..e24818f7336 100644 --- a/addons/web/static/src/js/widgets/data_export.js +++ b/addons/web/static/src/js/widgets/data_export.js @@ -3,8 +3,8 @@ odoo.define('web.DataExport', function (require) { var core = require('web.core'); var crash_manager = require('web.crash_manager'); -var data = require('web.data'); var Dialog = require('web.Dialog'); +var data = require('web.data'); var framework = require('web.framework'); var pyUtils = require('web.py_utils'); @@ -14,201 +14,32 @@ var _t = core._t; var DataExport = Dialog.extend({ template: 'ExportDialog', events: { - 'click .o_expand': function(e) { - this.on_expand_action(this.records[$(e.target).closest('.o_export_tree_item').data('id')]); - }, - 'click .o_export_tree_item': function(e) { - e.stopPropagation(); - var $elem = $(e.currentTarget); - - var row_index = $elem.prevAll('.o_export_tree_item').length; - var row_index_level = $elem.parents('.o_export_tree_item').length; - - if(e.shiftKey && row_index_level === this.row_index_level) { - var minIndex = Math.min(row_index, this.row_index); - var maxIndex = Math.max(row_index, this.row_index); - - this.$records.filter(function() { return ($elem.parent()[0] === $(this).parent()[0]); }) - .slice(minIndex, maxIndex+1) - .addClass('o_selected') - .filter(':not(:last)') - .each(process_children); - } - - this.row_index = row_index; - this.row_index_level = row_index_level; - - if(e.ctrlKey) { - $elem.toggleClass('o_selected').focus(); - } else if(e.shiftKey) { - $elem.addClass('o_selected').focus(); - } else { - this.$(".o_selected").removeClass("o_selected") - $elem.addClass("o_selected").focus(); - } - - function process_children() { - var $this = $(this); - if($this.hasClass('show')) { - $this.children('.o_export_tree_item') - .addClass('o_selected') - .each(process_children); - } - } - }, - 'dblclick .o_export_tree_item:not(.haschild)': function(e) { - var self = this; - function addElement(el) { - self.add_field(el.getAttribute('data-id'), el.querySelector('.o_tree_column').textContent); - } - var target = e.currentTarget; - target.classList.remove('o_selected'); - // Add parent fields to export - [].reverse.call($(target).parents('.o_export_tree_item')).each(function () { - addElement(this); - }); - // add field itself - addElement(target); - }, - 'keydown .o_export_tree_item': function(e) { - e.stopPropagation(); - var $elem = $(e.currentTarget); - var record = this.records[$elem.data('id')]; - - switch(e.keyCode || e.which) { - case $.ui.keyCode.LEFT: - if ($elem.hasClass('show')) { - this.on_expand_action(record); - } - break; - case $.ui.keyCode.RIGHT: - if (!$elem.hasClass('show')) { - this.on_expand_action(record); - } - break; - case $.ui.keyCode.UP: - var $prev = $elem.prev('.o_export_tree_item'); - if($prev.length === 1) { - while($prev.hasClass('show')) { - $prev = $prev.children('.o_export_tree_item').last(); - } - } else { - $prev = $elem.parent('.o_export_tree_item'); - if($prev.length === 0) { - break; - } - } - - $elem.removeClass("o_selected").blur(); - $prev.addClass("o_selected").focus(); - break; - case $.ui.keyCode.DOWN: - var $next; - if($elem.hasClass('show')) { - $next = $elem.children('.o_export_tree_item').first(); - } else { - $next = $elem.next('.o_export_tree_item'); - if($next.length === 0) { - $next = $elem.parent('.o_export_tree_item').next('.o_export_tree_item'); - if($next.length === 0) { - break; - } - } - } - - $elem.removeClass("o_selected").blur(); - $next.addClass("o_selected").focus(); - break; - } - }, - - 'click .o_add_field': function() { - var self = this; - this.$('.o_field_tree_structure .o_selected') - .removeClass('o_selected') - .each(function() { - var $this = $(this); - self.add_field($this.data('id'), $this.children('.o_tree_column').text()); - }); - }, - 'click .o_remove_field': function() { - this.$fields_list.find('option:selected').remove(); - }, - 'click .o_remove_all_field': function() { - this.$fields_list.empty(); - }, - 'click .o_move_up': function() { - var $selected_rows = this.$fields_list.find('option:selected'); - - var $prev_row = $selected_rows.first().prev(); - if($prev_row.length){ - $prev_row.before($selected_rows.detach()); - } - }, - 'click .o_move_down': function () { - var $selected_rows = this.$fields_list.find('option:selected'); - - var $next_row = $selected_rows.last().next(); - if($next_row.length){ - $next_row.after($selected_rows.detach()); - } - }, - - 'click .o_toggle_save_list': function(e) { - e.preventDefault(); - - var $saveList = this.$(".o_save_list"); - if($saveList.is(':empty')) { - $saveList.append(QWeb.render('Export.SaveList')); - } else { - if($saveList.is(':hidden')) { - $saveList.show(); - $saveList.find(".o_export_list_input").val(""); - } else { - $saveList.hide(); - } - } - }, - 'click .o_save_list > button': function(e) { - var $saveList = this.$(".o_save_list"); - - var value = $saveList.find("input").val(); - if(!value) { - Dialog.alert(this, _t("Please enter save field list name")); - return; - } - - var fields = this.get_fields(); - if (fields.length === 0) { - return; - } - - $saveList.hide(); - - var self = this; - this.exports.create({ - name: value, - resource: this.record.model, - export_fields: _.map(fields, function (field) { - return [0, 0, {name: field}]; - }), - }).then(function(export_list_id) { - if(!export_list_id) { - return; - } - var $select = self.$(".o_exported_lists_select"); - if($select.length === 0 || $select.is(":hidden")) { - self.show_exports_list(); - } - $select.append(new Option(value, export_list_id)); - }); - }, + '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_move_down': '_onClickMovedown', + 'click .o_move_up': '_onClickMoveup', + 'click .o_remove_all_field': '_onClickRemoveAllFields', + 'click .o_remove_field': '_onClickRemoveField', + 'click .o_toggle_save_list': '_onClickSaveList', + 'click .o_save_list > button': '_onClickSaveListBtn', + 'click .o_export_tree_item': '_onClickTreeItem', + 'dblclick .o_export_tree_item:not(.haschild)': '_onDblclickTreeItem', + 'keydown .o_export_tree_item': '_onKeydownTreeItem' }, - init: function(parent, record, defaultExportFields) { + /** + * @constructor + * @param {Widget} parent + * @param {Object} record + * @param {string[]} defaultExportFields + */ + init: function (parent, record, defaultExportFields) { var options = { title: _t("Export Data"), buttons: [ - {text: _t("Export To File"), click: this.export_data, classes: "btn-primary"}, + {text: _t("Export To File"), click: this._exportData, classes: 'btn-primary'}, {text: _t("Close"), close: true}, ], }; @@ -217,87 +48,52 @@ var DataExport = Dialog.extend({ this.record = record; this.defaultExportFields = defaultExportFields; this.exports = new data.DataSetSearch(this, 'ir.exports', this.record.getContext()); - - this.row_index = 0; - this.row_index_level = 0; + this.rowIndex = 0; + this.rowIndexLevel = 0; }, - start: function() { + /** + * @override + */ + start: function () { var self = this; - var waitFor = [this._super.apply(this, arguments)]; + var defs = [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.$modal.find('.modal-content').css('height', '100%'); - this.$fields_list = this.$('.o_fields_list'); - this.$import_compat_radios = this.$('.o_import_compat input'); + this.$fieldsList = this.$('.o_fields_list'); + this.$importCompatRadios = this.$('.o_import_compat input'); - waitFor.push(this._rpc({route: '/web/export/formats'}).then(do_setup_export_formats)); + defs.push(this._rpc({route: '/web/export/formats'}).then(doSetupExportFormats)); + defs.push(this._onChangeCompatibleInput()); - var got_fields = new $.Deferred(); - this.$import_compat_radios.change(function(e) { - self.isCompatibleMode = !!$(e.target).val(); - self.$('.o_field_tree_structure').remove(); - - self._rpc({ - route: '/web/export/get_fields', - params: { - model: self.record.model, - import_compat: self.isCompatibleMode, - }, - }) - .done(function (records) { - var compatible_fields = _.map(records, function (record) {return record.id; }); - self.$fields_list - .find('option') - .filter(function () { - var option_field = $(this).attr('value'); - if (compatible_fields.indexOf(option_field) === -1) { - return true; - } - }) - .remove(); - got_fields.resolve(); - self.on_show_data(records); - // In compatible mode add ID field as first field to export - if (self.isCompatibleMode) { - self.$('.o_fields_list').prepend(new Option(_('External ID'), 'id')); - } - _.each(records, function (record) { - if (_.contains(self.defaultExportFields, record.id)) { - self.add_field(record.id, record.string); - } - }); - }); - }).eq(0).change(); - waitFor.push(got_fields); - - waitFor.push(this.getParent().getActiveDomain().then(function (domain) { + defs.push(this.getParent().getActiveDomain().then(function (domain) { if (domain === undefined) { - self.ids_to_export = self.getParent().getSelectedIds(); + self.idsToExport = self.getParent().getSelectedIds(); self.domain = self.record.domain; } else { - self.ids_to_export = false; + self.idsToExport = false; self.domain = domain; } })); - waitFor.push(this.show_exports_list().then(function () { + defs.push(this._showExportsList().then(function () { _.each(self.records, function (record, key) { if (_.contains(self.defaultExportFields, key)) { - self.add_field(key, record.string); + self._addField(key, record.string); } }); })); - return $.when.apply($, waitFor); + return $.when.apply($, defs); - function do_setup_export_formats(formats) { + function doSetupExportFormats (formats) { var $fmts = self.$('.o_export_format'); - _.each(formats, function(format, i) { + _.each(formats, function (format, i) { var $radio = $('', {type: 'radio', value: format.tag, name: 'o_export_format_name'}); var $label = $('', {html: format.label}); @@ -310,137 +106,176 @@ var DataExport = Dialog.extend({ $fmts.append($("
").append($radioButton)); }); - self.$export_format_inputs = $fmts.find('input'); - self.$export_format_inputs.filter(':enabled').first().prop('checked', true); + self.$exportFormatInputs = $fmts.find('input'); + self.$exportFormatInputs.filter(':enabled').first().prop('checked', true); } }, - show_exports_list: function() { - if (this.$('.o_exported_lists_select').is(':hidden')) { - this.$('.o_exported_lists').show(); - return $.when(); - } + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * Add the field in the export list + * + * @private + * @param {string} fieldID + * @param {string} label + */ + _addField: function (fieldID, label) { + var $fieldList = this.$('.o_fields_list'); + fieldID = this.records[fieldID].value || fieldID; + if ($fieldList.find('option[value="' + fieldID + '"]').length === 0) { + $fieldList.append(new Option(label, fieldID)); + } + }, + /** + * Submit the user data and export the file + * + * @private + */ + _exportData: function () { var self = this; - return this._rpc({ - model: 'ir.exports', - method: 'search_read', - fields: ['name'], - domain: [['resource', '=', this.record.model]] - }).then(function (export_list) { - if (!export_list.length) { - return; - } - self.$('.o_exported_lists').append(QWeb.render('Export.SavedList', {'existing_exports': export_list})); - self.$('.o_exported_lists_select').on('change', function() { - self.$fields_list.empty(); - var export_id = self.$('.o_exported_lists_select option:selected').val(); - if(export_id) { - self._rpc({ - route: '/web/export/namelist', - params: { - model: self.record.model, - export_id: parseInt(export_id, 10), - }, - }) - .then(do_load_export_field); - } - }); - self.$('.o_delete_exported_list').click(function() { - var select_exp = self.$('.o_exported_lists_select option:selected'); - var options = { - confirm_callback: function () { - if (select_exp.val()) { - self.exports.unlink([parseInt(select_exp.val(), 10)]); - select_exp.remove(); - self.$fields_list.empty(); - if (self.$('.o_exported_lists_select option').length <= 1) { - self.$('.o_exported_lists').hide(); - } - } - } - }; - Dialog.confirm(this, _t("Do you really want to delete this export template?"), options); - }); - }); + var exportedFields = this.$('.o_fields_list option').map(function () { + return { + name: (self.records[this.value] || this).value, + label: this.textContent || this.innerText, // DOM property is textContent, but IE8 only knows innerText + }; + }).get(); - function do_load_export_field(field_list) { - _.each(field_list, function (field) { - self.$fields_list.append(new Option(field.label, field.name)); - }); + if (_.isEmpty(exportedFields)) { + Dialog.alert(this, _t("Please select fields to export...")); + return; } + if (!this.isCompatibleMode) { + exportedFields.unshift({ name: 'id', label: _t('External ID') }); + } + + var exportFormat = this.$exportFormatInputs.filter(':checked').val(); + + framework.blockUI(); + this.getSession().get_file({ + url: '/web/export/' + exportFormat, + data: { + data: JSON.stringify({ + model: this.record.model, + fields: exportedFields, + ids: this.idsToExport, + domain: this.domain, + context: pyUtils.eval('contexts', [this.record.getContext()]), + import_compat: !!this.$importCompatRadios.filter(':checked').val(), + }) + }, + complete: framework.unblockUI, + error: crash_manager.rpc_error.bind(crash_manager), + }); }, - on_expand_action: function(record) { - if(!record['children']) { + /** + * @private + * @returns {string[]} exportFields + */ + _getFields: function () { + var exportFields = this.$('.o_fields_list option').map(function () { + return $(this).val(); + }).get(); + if (exportFields.length === 0) { + Dialog.alert(this, _t("Please select fields to save export list...")); + } + return exportFields; + }, + /** + * Fetch the field info for the relational field. This method will be + * invoked when the user expands the relational field from keyboard/mouse. + * + * @private + * @param {Object} record + */ + _onExpandAction: function (record) { + var self = this; + if (!record['children']) { return; } var model = record['params']['model']; var prefix = record['params']['prefix']; var name = record['params']['name']; - var exclude_fields = []; - if(record['relation_field']) { - exclude_fields.push(record['relation_field']); + var excludeFields = []; + if (record['relation_field']) { + excludeFields.push(record['relation_field']); } - if(!record.loaded) { - var self = this; + if (!record.loaded) { this._rpc({ - route: '/web/export/get_fields', - params: { - model: model, - prefix: prefix, - parent_name: name, - import_compat: !!this.$import_compat_radios.filter(':checked').val(), - parent_field_type : record['field_type'], - parent_field: record['params']['parent_field'], - exclude: exclude_fields, - }, - }) - .done(function(results) { - record.loaded = true; - self.on_show_data(results, record.id); - }); + route: '/web/export/get_fields', + params: { + model: model, + prefix: prefix, + parent_name: name, + import_compat: !!this.$importCompatRadios.filter(':checked').val(), + parent_field_type: record['field_type'], + parent_field: record['params']['parent_field'], + exclude: excludeFields, + }, + }).done(function (results) { + record.loaded = true; + self._onShowData(results, record.id); + }); } else { - this.show_content(record.id); + this._showContent(record.id); } }, - on_show_data: function(records, expansion) { + /** + * After the fetching the fields info for the relational field, this method + * will render a list of a field for expanded relational field. + * + * @private + * @param {Object[]} records + * @param {string} expansion + */ + _onShowData: function (records, expansion) { var self = this; - if(expansion) { + if (expansion) { this.$('.o_export_tree_item[data-id="' + expansion + '"]') .addClass('show') .find('.o_expand_parent') .toggleClass('fa-chevron-right fa-chevron-down') .next() - .after(QWeb.render('Export.TreeItems', {'fields': records, 'debug': this.getSession().debug})); + .after(QWeb.render('Export.TreeItems', {fields: records, debug: this.getSession().debug})); } else { this.$('.o_left_field_panel').empty().append( - $("").addClass('o_field_tree_structure') - .append(QWeb.render('Export.TreeItems', {'fields': records, 'debug': this.getSession().debug})) + $('').addClass('o_field_tree_structure') + .append(QWeb.render('Export.TreeItems', {fields: records, debug: this.getSession().debug})) ); } _.extend(this.records, _.object(_.pluck(records, 'id'), records)); - this.$records = this.$(".o_export_tree_item"); - this.$records.each(function(i, el) { - var $elem = $(el); - $elem.find('.o_tree_column').first().toggleClass('o_required', !!self.records[$elem.data('id')].required); + this.$records = this.$('.o_export_tree_item'); + this.$records.each(function (i, el) { + var $el = $(el); + $el.find('.o_tree_column').first().toggleClass('o_required', !!self.records[$el.data('id')].required); }); }, - show_content: function(id) { - var $this = this.$('.o_export_tree_item[data-id="' + id + '"]'); - $this.toggleClass('show'); - var is_open = $this.hasClass('show'); + /** + * If relational fields info is already fetched then this method is + * used to display fields. + * + * @private + * @param {string} fieldID + */ + _showContent: function (fieldID) { + var $item = this.$('.o_export_tree_item[data-id="' + fieldID + '"]'); + $item.toggleClass('show'); + var isOpen = $item.hasClass('show'); - $this.children('.o_expand_parent').toggleClass('fa-chevron-down', !!is_open).toggleClass('fa-chevron-right', !is_open); + $item.children('.o_expand_parent').toggleClass('fa-chevron-down', !!isOpen).toggleClass('fa-chevron-right', !isOpen); - var $child_field = $this.find('.o_export_tree_item'); - var child_len = (id.split("/")).length + 1; - for (var i = 0 ; i < $child_field.length ; i++) { - var $child = $child_field.eq(i); - if(!is_open) { + var $childField = $item.find('.o_export_tree_item'); + var childLength = (fieldID.split('/')).length + 1; + for (var i = 0 ; i < $childField.length ; i++) { + var $child = $childField.eq(i); + if (!isOpen) { $child.hide(); - } else if(child_len === $child_field.eq(i).data('id').split("/").length) { + } else if (childLength === $childField.eq(i).data('id').split('/').length) { if ($child.hasClass('show')) { $child.removeClass('show'); $child.children('.o_expand_parent').removeClass('fa-chevron-down').addClass('fa-chevron-right'); @@ -449,56 +284,358 @@ var DataExport = Dialog.extend({ } } }, - add_field: function(field_id, string) { - var $field_list = this.$('.o_fields_list'); - field_id = this.records[field_id].value || field_id; - if($field_list.find("option[value='" + field_id + "']").length === 0) { - $field_list.append(new Option(string, field_id)); - } - }, - get_fields: function() { - var $export_fields = this.$(".o_fields_list option").map(function() { - return $(this).val(); - }).get(); - if($export_fields.length === 0) { - Dialog.alert(this, _t("Please select fields to save export list...")); - } - return $export_fields; - }, - export_data: function() { + /** + * Fetches the saved export list for the current model + * + * @private + * @returns {Deferred} + */ + _showExportsList: function () { var self = this; - var exported_fields = this.$('.o_fields_list option').map(function () { - return { - name: (self.records[this.value] || this).value, - label: this.textContent || this.innerText // DOM property is textContent, but IE8 only knows innerText - }; - }).get(); + if (this.$('.o_exported_lists_select').is(':hidden')) { + this.$('.o_exported_lists').show(); + return $.when(); + } - if (_.isEmpty(exported_fields)) { - Dialog.alert(this, _t("Please select fields to export...")); + return this._rpc({ + model: 'ir.exports', + method: 'search_read', + fields: ['name'], + domain: [['resource', '=', this.record.model]] + }).then(function (exportList) { + if (!exportList.length) { + return; + } + self.$('.o_exported_lists').append(QWeb.render('Export.SavedList', { + existing_exports: exportList, + })); + }); + }, + + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- + + /** + * This method will fill fields to export when user change exported field list + * + * @private + */ + _onChangeExportList: function () { + var self = this; + this.$fieldsList.empty(); + var exportID = this.$('.o_exported_lists_select option:selected').val(); + if (exportID) { + this._rpc({ + route: '/web/export/namelist', + params: { + model: this.record.model, + export_id: parseInt(exportID, 10), + }, + }).then(function (fieldList) { + _.each(fieldList, function (field) { + self.$fieldsList.append(new Option(field.label, field.name)); + }); + }); + } + }, + /** + * @private + * @returns {Deferred} + */ + _onChangeCompatibleInput: function () { + var self = this; + this.isCompatibleMode = this.$('.o_import_compat input[value="yes"]').is(':checked'); + + this.$('.o_field_tree_structure').remove(); + + return this._rpc({ + route: '/web/export/get_fields', + params: { + model: this.record.model, + import_compat: this.isCompatibleMode, + }, + }).done(function (records) { + var compatibleFields = _.map(records, function (record) { return record.id; }); + self.$fieldsList + .find('option') + .filter(function () { + var optionField = $(this).attr('value'); + if (compatibleFields.indexOf(optionField) === -1) { + return true; + } + }) + .remove(); + self._onShowData(records); + // In compatible mode add ID field as first field to export + if (self.isCompatibleMode) { + self.$('.o_fields_list').prepend(new Option(_t("External ID"), 'id')); + } + _.each(records, function (record) { + if (_.contains(self.defaultExportFields, record.id)) { + self._addField(record.id, record.string); + } + }); + }); + }, + /** + * Add a field to export list + * + * @private + */ + _onClickAddField: function () { + var self = this; + this.$('.o_field_tree_structure .o_selected') + .removeClass('o_selected') + .each(function () { + var $this = $(this); + self._addField($this.data('id'), $this.children('.o_tree_column').text()); + }); + }, + /** + * Delete selected export list item from the saved export list + * + * @private + */ + _onClickDeleteExportListBtn: function () { + var self = this; + var selectExp = this.$('.o_exported_lists_select option:selected'); + var options = { + confirm_callback: function () { + if (selectExp.val()) { + self.exports.unlink([parseInt(selectExp.val(), 10)]); + selectExp.remove(); + self.$fieldsList.empty(); + if (self.$('.o_exported_lists_select option').length <= 1) { + self.$('.o_exported_lists').hide(); + } + } + } + }; + Dialog.confirm(this, _t("Do you really want to delete this export template?"), options); + }, + /** + * @private + * @param {Event} ev + */ + _onClickExpand: function (ev) { + this._onExpandAction(this.records[$(ev.target).closest('.o_export_tree_item').data('id')]); + }, + /** + * @private + */ + _onClickMovedown: function () { + var $selectedRows = this.$fieldsList.find('option:selected'); + var $nextRow = $selectedRows.last().next(); + if ($nextRow.length) { + $nextRow.after($selectedRows.detach()); + } + }, + /** + * @private + */ + _onClickMoveup: function () { + var $selectedRows = this.$fieldsList.find('option:selected'); + var $prevRow = $selectedRows.first().prev(); + if ($prevRow.length) { + $prevRow.before($selectedRows.detach()); + } + }, + /** + * Remove all fields from export field list + * + * @private + */ + _onClickRemoveAllFields: function () { + this.$fieldsList.empty(); + }, + /** + * Remove selected field from export field list + * + * @private + */ + _onClickRemoveField: function () { + this.$fieldsList.find('option:selected').remove(); + }, + /** + * @private + * @param {Event} ev + */ + _onClickSaveList: function (ev) { + ev.preventDefault(); + + var $saveList = this.$('.o_save_list'); + if ($saveList.is(':empty')) { + $saveList.append(QWeb.render('Export.SaveList')); + } else { + if ($saveList.is(':hidden')) { + $saveList.show(); + $saveList.find('.o_export_list_input').val(''); + } else { + $saveList.hide(); + } + } + }, + /** + * This method will create a record in 'ir.exports' model with list of + * selected fields. + * + * @private + */ + _onClickSaveListBtn: function () { + var self = this; + var $saveList = this.$('.o_save_list'); + + var value = $saveList.find('input').val(); + if (!value) { + Dialog.alert(this, _t("Please enter save field list name")); return; } - if (!this.isCompatibleMode) { - exported_fields.unshift({name: 'id', label: _t('External ID')}); + + var fields = this._getFields(); + if (fields.length === 0) { + return; } - var export_format = this.$export_format_inputs.filter(':checked').val(); + $saveList.hide(); - framework.blockUI(); - this.getSession().get_file({ - url: '/web/export/' + export_format, - data: {data: JSON.stringify({ - model: this.record.model, - fields: exported_fields, - ids: this.ids_to_export, - domain: this.domain, - context: pyUtils.eval('contexts', [this.record.getContext()]), - import_compat: !!this.$import_compat_radios.filter(':checked').val(), - })}, - complete: framework.unblockUI, - error: crash_manager.rpc_error.bind(crash_manager), + this.exports.create({ + name: value, + resource: this.record.model, + export_fields: _.map(fields, function (field) { + return [0, 0, { name: field }]; + }), + }).then(function (exportListID) { + if (!exportListID) { + return; + } + var $select = self.$('.o_exported_lists_select'); + if ($select.length === 0 || $select.is(':hidden')) { + self._showExportsList(); + } + $select.append(new Option(value, exportListID)); }); }, + /** + * @private + * @param {Event} ev + */ + _onClickTreeItem: function (ev) { + ev.stopPropagation(); + var $elem = $(ev.currentTarget); + + var rowIndex = $elem.prevAll('.o_export_tree_item').length; + var rowIndexLevel = $elem.parents('.o_export_tree_item').length; + + if (ev.shiftKey && rowIndexLevel === this.rowIndexLevel) { + var minIndex = Math.min(rowIndex, this.rowIndex); + var maxIndex = Math.max(rowIndex, this.rowIndex); + + this.$records.filter(function () { return ($elem.parent()[0] === $(this).parent()[0]); }) + .slice(minIndex, maxIndex + 1) + .addClass('o_selected') + .filter(':not(:last)') + .each(processChildren); + } + + this.rowIndex = rowIndex; + this.rowIndexLevel = rowIndexLevel; + + if (ev.ctrlKey) { + $elem.toggleClass('o_selected').focus(); + } else if (ev.shiftKey) { + $elem.addClass('o_selected').focus(); + } else { + this.$('.o_selected').removeClass('o_selected') + $elem.addClass('o_selected').focus(); + } + + function processChildren() { + var $child = $(this); + if ($child.hasClass('show')) { + $child.children('.o_export_tree_item') + .addClass('o_selected') + .each(processChildren); + } + } + }, + /** + * Add a field to export field list on double click + * + * @private + * @param {Event} ev + */ + _onDblclickTreeItem: function (ev) { + var self = this; + function addElement(el) { + self._addField(el.getAttribute('data-id'), el.querySelector('.o_tree_column').textContent); + } + var target = ev.currentTarget; + target.classList.remove('o_selected'); + // Add parent fields to export + [].reverse.call($(target).parents('.o_export_tree_item')).each(function () { + addElement(this); + }); + // add field itself + addElement(target); + }, + /** + * Handles the keyboard navigation for the fields + * + * @private + * @param {Event} ev + */ + _onKeydownTreeItem: function (ev) { + ev.stopPropagation(); + var $el = $(ev.currentTarget); + var record = this.records[$el.data('id')]; + + switch (ev.keyCode || ev.which) { + case $.ui.keyCode.LEFT: + if ($el.hasClass('show')) { + this._onExpandAction(record); + } + break; + case $.ui.keyCode.RIGHT: + if (!$el.hasClass('show')) { + this._onExpandAction(record); + } + break; + case $.ui.keyCode.UP: + var $prev = $el.prev('.o_export_tree_item'); + if ($prev.length === 1) { + while ($prev.hasClass('show')) { + $prev = $prev.children('.o_export_tree_item').last(); + } + } else { + $prev = $el.parent('.o_export_tree_item'); + if ($prev.length === 0) { + break; + } + } + + $el.removeClass('o_selected').blur(); + $prev.addClass("o_selected").focus(); + break; + case $.ui.keyCode.DOWN: + var $next; + if ($el.hasClass('show')) { + $next = $el.children('.o_export_tree_item').first(); + } else { + $next = $el.next('.o_export_tree_item'); + if ($next.length === 0) { + $next = $el.parent('.o_export_tree_item').next('.o_export_tree_item'); + if ($next.length === 0) { + break; + } + } + } + + $el.removeClass('o_selected').blur(); + $next.addClass('o_selected').focus(); + break; + } + }, }); return DataExport;