diff --git a/addons/base_import/static/src/js/import.js b/addons/base_import/static/src/js/import.js index 0ee88e0a203..1bd4fa4cfd9 100644 --- a/addons/base_import/static/src/js/import.js +++ b/addons/base_import/static/src/js/import.js @@ -1,489 +1,499 @@ -openerp.base_import = function (instance) { - var QWeb = instance.web.qweb; - var _t = instance.web._t; - var _lt = instance.web._lt; +odoo.define('base_import.import', ['web.core', 'web.ListView', 'web.Model', 'web.session', 'web.Widget'], function (require) { +"use strict"; - /** - * Safari does not deal well at all with raw JSON data being - * returned. As a result, we're going to cheat by using a - * pseudo-jsonp: instead of getting JSON data in the iframe, we're - * getting a ``script`` tag which consists of a function call and - * the returned data (the json dump). - * - * The function is an auto-generated name bound to ``window``, - * which calls back into the callback provided here. - * - * @param {Object} form the form element (DOM or jQuery) to use in the call - * @param {Object} attributes jquery.form attributes object - * @param {Function} callback function to call with the returned data - */ - function jsonp(form, attributes, callback) { - attributes = attributes || {}; - var options = {jsonp: _.uniqueId('import_callback_')}; - window[options.jsonp] = function () { - delete window[options.jsonp]; - callback.apply(null, arguments); - }; - if ('data' in attributes) { - _.extend(attributes.data, options); - } else { - _.extend(attributes, {data: options}); - } - _.extend(attributes, { - dataType: 'script', - }); - $(form).ajaxSubmit(attributes); +var core = require('web.core'); +var ListView = require('web.ListView'); +var Model = require('web.Model'); +var session = require('web.session'); +var Widget = require('web.Widget'); + +var QWeb = core.qweb; +var _t = core._t; +var _lt = core._lt; +var StateMachine = window.StateMachine; + +/** + * Safari does not deal well at all with raw JSON data being + * returned. As a result, we're going to cheat by using a + * pseudo-jsonp: instead of getting JSON data in the iframe, we're + * getting a ``script`` tag which consists of a function call and + * the returned data (the json dump). + * + * The function is an auto-generated name bound to ``window``, + * which calls back into the callback provided here. + * + * @param {Object} form the form element (DOM or jQuery) to use in the call + * @param {Object} attributes jquery.form attributes object + * @param {Function} callback function to call with the returned data + */ +function jsonp(form, attributes, callback) { + attributes = attributes || {}; + var options = {jsonp: _.uniqueId('import_callback_')}; + window[options.jsonp] = function () { + delete window[options.jsonp]; + callback.apply(null, arguments); + }; + if ('data' in attributes) { + _.extend(attributes.data, options); + } else { + _.extend(attributes, {data: options}); } - - // if true, the 'Import', 'Export', etc... buttons will be shown - instance.web.ListView.prototype.defaults.import_enabled = true; - instance.web.ListView.include({ - load_list: function () { - var self = this; - var add_button = false; - if (!this.$buttons) { - add_button = true; - } - this._super.apply(this, arguments); - if(add_button) { - this.$buttons.on('click', '.oe_list_button_import', function() { - self.do_action({ - type: 'ir.actions.client', - tag: 'import', - params: { - model: self.dataset.model, - // self.dataset.get_context() could be a compound? - // not sure. action's context should be evaluated - // so safer bet. Odd that timezone & al in it - // though - context: self.getParent().action.context, - } - }, { - on_reverse_breadcrumb: function () { - self.reload(); - }, - }); - return false; - }); - } - } + _.extend(attributes, { + dataType: 'script', }); + $(form).ajaxSubmit(attributes); +} - instance.web.client_actions.add( - 'import', 'instance.web.DataImport'); - instance.web.DataImport = instance.web.Widget.extend({ - template: 'ImportView', - opts: [ - {name: 'encoding', label: _lt("Encoding:"), value: 'utf-8'}, - {name: 'separator', label: _lt("Separator:"), value: ','}, - {name: 'quoting', label: _lt("Quoting:"), value: '"'} - ], - events: { - // 'change .oe_import_grid input': 'import_dryrun', - 'change .oe_import_file': 'loaded_file', - 'click .oe_import_file_reload': 'loaded_file', - 'change input.oe_import_has_header, .oe_import_options input': 'settings_changed', - 'click a.oe_import_toggle': function (e) { - e.preventDefault(); - var $el = $(e.target); - ($el.next().length - ? $el.next() - : $el.parent().next()) - .toggle(); - }, - 'click .oe_import_report a.oe_import_report_count': function (e) { - e.preventDefault(); - $(e.target).parent().toggleClass('oe_import_report_showmore'); - }, - 'click .oe_import_moreinfo_action a': function (e) { - e.preventDefault(); - // #data will parse the attribute on its own, we don't like - // that sort of things - var action = JSON.parse($(e.target).attr('data-action')); - // FIXME: when JS-side clean_action - action.views = _(action.views).map(function (view) { - var id = view[0], type = view[1]; - return [ - id, - type !== 'tree' ? type - : action.view_type === 'form' ? 'list' - : 'tree' - ]; +// if true, the 'Import', 'Export', etc... buttons will be shown +ListView.prototype.defaults.import_enabled = true; +ListView.include({ + load_list: function () { + var self = this; + var add_button = false; + if (!this.$buttons) { + add_button = true; + } + this._super.apply(this, arguments); + if(add_button) { + this.$buttons.on('click', '.oe_list_button_import', function() { + self.do_action({ + type: 'ir.actions.client', + tag: 'import', + params: { + model: self.dataset.model, + // self.dataset.get_context() could be a compound? + // not sure. action's context should be evaluated + // so safer bet. Odd that timezone & al in it + // though + context: self.getParent().action.context, + } + }, { + on_reverse_breadcrumb: function () { + self.reload(); + }, }); - this.do_action(_.extend(action, { - target: 'new', - flags: { - search_view: true, - display_title: true, - pager: true, - list: {selectable: false} - } - })); - }, - // buttons - 'click .oe_import_validate': 'validate', - 'click .oe_import_import': 'import', - 'click .oe_import_cancel': function (e) { - e.preventDefault(); - this.exit(); - } - }, - init: function (parent, action) { - var self = this; - this._super.apply(this, arguments); - this.res_model = action.params.model; - this.parent_context = action.params.context || {}; - // import object id - this.id = null; - this.Import = new instance.web.Model('base_import.import'); - }, - start: function () { - var self = this; - this.setup_encoding_picker(); - this.setup_separator_picker(); + return false; + }); + } + } +}); - return $.when( - this._super(), - this.Import.call('create', [{ - 'res_model': this.res_model - }]).done(function (id) { - self.id = id; - self.$('input[name=import_id]').val(id); - }) - ) +var DataImport = Widget.extend({ + template: 'ImportView', + opts: [ + {name: 'encoding', label: _lt("Encoding:"), value: 'utf-8'}, + {name: 'separator', label: _lt("Separator:"), value: ','}, + {name: 'quoting', label: _lt("Quoting:"), value: '"'} + ], + events: { + // 'change .oe_import_grid input': 'import_dryrun', + 'change .oe_import_file': 'loaded_file', + 'click .oe_import_file_reload': 'loaded_file', + 'change input.oe_import_has_header, .oe_import_options input': 'settings_changed', + 'click a.oe_import_toggle': function (e) { + e.preventDefault(); + var $el = $(e.target); + ($el.next().length + ? $el.next() + : $el.parent().next()) + .toggle(); }, - setup_encoding_picker: function () { - this.$('input.oe_import_encoding').select2({ - width: '160px', - query: function (q) { - var make = function (term) { return {id: term, text: term}; }; - var suggestions = _.map( - ('utf-8 utf-16 windows-1252 latin1 latin2 big5 ' + - 'gb18030 shift_jis windows-1251 koir8_r').split(/\s+/), - make); - if (q.term) { - suggestions.unshift(make(q.term)); - } - q.callback({results: suggestions}); - }, - initSelection: function (e, c) { - return c({id: 'utf-8', text: 'utf-8'}); + 'click .oe_import_report a.oe_import_report_count': function (e) { + e.preventDefault(); + $(e.target).parent().toggleClass('oe_import_report_showmore'); + }, + 'click .oe_import_moreinfo_action a': function (e) { + e.preventDefault(); + // #data will parse the attribute on its own, we don't like + // that sort of things + var action = JSON.parse($(e.target).attr('data-action')); + // FIXME: when JS-side clean_action + action.views = _(action.views).map(function (view) { + var id = view[0], type = view[1]; + return [ + id, + type !== 'tree' ? type + : action.view_type === 'form' ? 'list' + : 'tree' + ]; + }); + this.do_action(_.extend(action, { + target: 'new', + flags: { + search_view: true, + display_title: true, + pager: true, + list: {selectable: false} } - }).select2('val', 'utf-8'); + })); }, - setup_separator_picker: function () { - this.$('input.oe_import_separator').select2({ - width: '160px', - query: function (q) { - var suggestions = [ - {id: ',', text: _t("Comma")}, - {id: ';', text: _t("Semicolon")}, - {id: '\t', text: _t("Tab")}, - {id: ' ', text: _t("Space")} - ]; - if (q.term) { - suggestions.unshift({id: q.term, text: q.term}); - } - q.callback({results: suggestions}); - }, - initSelection: function (e, c) { - return c({id: ',', text: _t("Comma")}); - }, + // buttons + 'click .oe_import_validate': 'validate', + 'click .oe_import_import': 'import', + 'click .oe_import_cancel': function (e) { + e.preventDefault(); + this.exit(); + } + }, + init: function (parent, action) { + this._super.apply(this, arguments); + this.res_model = action.params.model; + this.parent_context = action.params.context || {}; + // import object id + this.id = null; + this.Import = new Model('base_import.import'); + this.session = session; + }, + start: function () { + var self = this; + this.setup_encoding_picker(); + this.setup_separator_picker(); + + return $.when( + this._super(), + this.Import.call('create', [{ + 'res_model': this.res_model + }]).done(function (id) { + self.id = id; + self.$('input[name=import_id]').val(id); + }) + ); + }, + setup_encoding_picker: function () { + this.$('input.oe_import_encoding').select2({ + width: '160px', + query: function (q) { + var make = function (term) { return {id: term, text: term}; }; + var suggestions = _.map( + ('utf-8 utf-16 windows-1252 latin1 latin2 big5 ' + + 'gb18030 shift_jis windows-1251 koir8_r').split(/\s+/), + make); + if (q.term) { + suggestions.unshift(make(q.term)); + } + q.callback({results: suggestions}); + }, + initSelection: function (e, c) { + return c({id: 'utf-8', text: 'utf-8'}); + } + }).select2('val', 'utf-8'); + }, + setup_separator_picker: function () { + this.$('input.oe_import_separator').select2({ + width: '160px', + query: function (q) { + var suggestions = [ + {id: ',', text: _t("Comma")}, + {id: ';', text: _t("Semicolon")}, + {id: '\t', text: _t("Tab")}, + {id: ' ', text: _t("Space")} + ]; + if (q.term) { + suggestions.unshift({id: q.term, text: q.term}); + } + q.callback({results: suggestions}); + }, + initSelection: function (e, c) { + return c({id: ',', text: _t("Comma")}); + }, + }); + }, + + import_options: function () { + var self = this; + var options = { + headers: this.$('input.oe_import_has_header').prop('checked') + }; + _(this.opts).each(function (opt) { + options[opt.name] = + self.$('input.oe_import_' + opt.name).val(); + }); + return options; + }, + + //- File & settings change section + onfile_loaded: function () { + this.$('.oe_import_button, .oe_import_file_reload') + .prop('disabled', true); + if (!this.$('input.oe_import_file').val()) { return; } + + this.$el.removeClass('oe_import_preview oe_import_error'); + jsonp(this.$el, { + url: '/base_import/set_file' + }, this.proxy('settings_changed')); + }, + onpreviewing: function () { + var self = this; + this.$('.oe_import_button, .oe_import_file_reload') + .prop('disabled', true); + this.$el.addClass('oe_import_with_file'); + // TODO: test that write // succeeded? + this.$el.removeClass('oe_import_preview_error oe_import_error'); + this.$el.toggleClass( + 'oe_import_noheaders', + !this.$('input.oe_import_has_header').prop('checked')); + this.Import.call( + 'parse_preview', [this.id, this.import_options()]) + .done(function (result) { + var signal = result.error ? 'preview_failed' : 'preview_succeeded'; + self[signal](result); }); - }, + }, + onpreview_error: function (event, from, to, result) { + this.$('.oe_import_options').show(); + this.$('.oe_import_file_reload').prop('disabled', false); + this.$el.addClass('oe_import_preview_error oe_import_error'); + this.$('.oe_import_error_report').html( + QWeb.render('ImportView.preview.error', result)); + }, + onpreview_success: function (event, from, to, result) { + this.$('.oe_import_import').removeClass('oe_highlight'); + this.$('.oe_import_validate').addClass('oe_highlight'); + this.$('.oe_import_button, .oe_import_file_reload') + .prop('disabled', false); + this.$el.addClass('oe_import_preview'); + this.$('table').html(QWeb.render('ImportView.preview', result)); - import_options: function () { - var self = this; - var options = { - headers: this.$('input.oe_import_has_header').prop('checked') - }; - _(this.opts).each(function (opt) { - options[opt.name] = - self.$('input.oe_import_' + opt.name).val(); - }); - return options; - }, - - //- File & settings change section - onfile_loaded: function () { - this.$('.oe_import_button, .oe_import_file_reload') - .prop('disabled', true); - if (!this.$('input.oe_import_file').val()) { return; } - - this.$el.removeClass('oe_import_preview oe_import_error'); - jsonp(this.$el, { - url: '/base_import/set_file' - }, this.proxy('settings_changed')); - }, - onpreviewing: function () { - var self = this; - this.$('.oe_import_button, .oe_import_file_reload') - .prop('disabled', true); - this.$el.addClass('oe_import_with_file'); - // TODO: test that write // succeeded? - this.$el.removeClass('oe_import_preview_error oe_import_error'); - this.$el.toggleClass( - 'oe_import_noheaders', - !this.$('input.oe_import_has_header').prop('checked')); - this.Import.call( - 'parse_preview', [this.id, this.import_options()]) - .done(function (result) { - var signal = result.error ? 'preview_failed' : 'preview_succeeded'; - self[signal](result); - }); - }, - onpreview_error: function (event, from, to, result) { + if (result.headers.length === 1) { this.$('.oe_import_options').show(); - this.$('.oe_import_file_reload').prop('disabled', false); - this.$el.addClass('oe_import_preview_error oe_import_error'); - this.$('.oe_import_error_report').html( - QWeb.render('ImportView.preview.error', result)); - }, - onpreview_success: function (event, from, to, result) { - this.$('.oe_import_import').removeClass('oe_highlight'); - this.$('.oe_import_validate').addClass('oe_highlight'); - this.$('.oe_import_button, .oe_import_file_reload') - .prop('disabled', false); - this.$el.addClass('oe_import_preview'); - this.$('table').html(QWeb.render('ImportView.preview', result)); + this.onresults(null, null, null, [{ + type: 'warning', + message: _t("A single column was found in the file, this often means the file separator is incorrect") + }]); + } - if (result.headers.length === 1) { - this.$('.oe_import_options').show(); - this.onresults(null, null, null, [{ - type: 'warning', - message: _t("A single column was found in the file, this often means the file separator is incorrect") - }]); - } - - var $fields = this.$('.oe_import_fields input'); - this.render_fields_matches(result, $fields); - var data = this.generate_fields_completion(result); - var item_finder = function (id, items) { - items = items || data; - for (var i=0; i < items.length; ++i) { - var item = items[i]; - if (item.id === id) { - return item; - } - var val; - if (item.children && (val = item_finder(id, item.children))) { - return val; - } + var $fields = this.$('.oe_import_fields input'); + this.render_fields_matches(result, $fields); + var data = this.generate_fields_completion(result); + var item_finder = function (id, items) { + items = items || data; + for (var i=0; i < items.length; ++i) { + var item = items[i]; + if (item.id === id) { + return item; } - return ''; - }; - $fields.select2({ - allowClear: true, - minimumInputLength: 0, - data: data, - initSelection: function (element, callback) { - var default_value = element.val(); - if (!default_value) { - callback(''); - return; - } - - callback(item_finder(default_value)); - }, - placeholder: _t('Don\'t import'), - width: 'resolve', - dropdownCssClass: 'oe_import_selector' - }); - }, - generate_fields_completion: function (root) { - var basic = []; - var regulars = []; - var o2m = []; - function traverse(field, ancestors, collection) { - var subfields = field.fields; - var field_path = ancestors.concat(field); - var label = _(field_path).pluck('string').join(' / '); - var id = _(field_path).pluck('name').join('/'); - - // If non-relational, m2o or m2m, collection is regulars - if (!collection) { - if (field.name === 'id') { - collection = basic - } else if (_.isEmpty(subfields) - || _.isEqual(_.pluck(subfields, 'name'), ['id', '.id'])) { - collection = regulars; - } else { - collection = o2m; - } - } - - collection.push({ - id: id, - text: label, - required: field.required - }); - - for(var i=0, end=subfields.length; i%s', - _.str.escapeHTML(msg)); - } - if (msg instanceof Array) { - return _.str.sprintf( - '
%s
', - _.str.escapeHTML(_t("Here are the possible values:")), - _(msg).map(function (msg) { - return '
  • ' - + _.str.escapeHTML(msg) - + '
  • '; - }).join('')); - } - // Final should be object, action descriptor - return [ - '
    ', - _.str.sprintf('', - _.str.escapeHTML(JSON.stringify(msg))), - _.str.escapeHTML( - _t("Get all possible values")), - '', - '
    ' - ].join('') - }, - })); - }, - }); - // FSM-ize DataImport - StateMachine.create({ - target: instance.web.DataImport.prototype, - events: [ - { name: 'loaded_file', - from: ['none', 'file_loaded', 'preview_error', 'preview_success', 'results'], - to: 'file_loaded' }, - { name: 'settings_changed', - from: ['file_loaded', 'preview_error', 'preview_success', 'results'], - to: 'previewing' }, - { name: 'preview_failed', from: 'previewing', to: 'preview_error' }, - { name: 'preview_succeeded', from: 'previewing', to: 'preview_success' }, - { name: 'validate', from: 'preview_success', to: 'validating' }, - { name: 'validate', from: 'results', to: 'validating' }, - { name: 'validated', from: 'validating', to: 'results' }, - { name: 'import', from: ['preview_success', 'results'], to: 'importing' }, - { name: 'import_succeeded', from: 'importing', to: 'imported'}, - { name: 'import_failed', from: 'importing', to: 'results' } - ] - }) -}; + callback(item_finder(default_value)); + }, + placeholder: _t('Don\'t import'), + width: 'resolve', + dropdownCssClass: 'oe_import_selector' + }); + }, + generate_fields_completion: function (root) { + var basic = []; + var regulars = []; + var o2m = []; + function traverse(field, ancestors, collection) { + var subfields = field.fields; + var field_path = ancestors.concat(field); + var label = _(field_path).pluck('string').join(' / '); + var id = _(field_path).pluck('name').join('/'); + + // If non-relational, m2o or m2m, collection is regulars + if (!collection) { + if (field.name === 'id') { + collection = basic; + } else if (_.isEmpty(subfields) + || _.isEqual(_.pluck(subfields, 'name'), ['id', '.id'])) { + collection = regulars; + } else { + collection = o2m; + } + } + + collection.push({ + id: id, + text: label, + required: field.required + }); + + for(var i=0, end=subfields.length; i%s', + _.str.escapeHTML(msg)); + } + if (msg instanceof Array) { + return _.str.sprintf( + '
    %s
      %s
    ', + _.str.escapeHTML(_t("Here are the possible values:")), + _(msg).map(function (msg) { + return '
  • ' + + _.str.escapeHTML(msg) + + '
  • '; + }).join('')); + } + // Final should be object, action descriptor + return [ + '' + ].join(''); + }, + })); + }, +}); +core.action_registry.add('import', DataImport); + +// FSM-ize DataImport +StateMachine.create({ + target: DataImport.prototype, + events: [ + { name: 'loaded_file', + from: ['none', 'file_loaded', 'preview_error', 'preview_success', 'results'], + to: 'file_loaded' }, + { name: 'settings_changed', + from: ['file_loaded', 'preview_error', 'preview_success', 'results'], + to: 'previewing' }, + { name: 'preview_failed', from: 'previewing', to: 'preview_error' }, + { name: 'preview_succeeded', from: 'previewing', to: 'preview_success' }, + { name: 'validate', from: 'preview_success', to: 'validating' }, + { name: 'validate', from: 'results', to: 'validating' }, + { name: 'validated', from: 'validating', to: 'results' }, + { name: 'import', from: ['preview_success', 'results'], to: 'importing' }, + { name: 'import_succeeded', from: 'importing', to: 'imported'}, + { name: 'import_failed', from: 'importing', to: 'results' } + ] +}); + +});