diff options
Diffstat (limited to 'addons/base_import/static/src')
| -rw-r--r-- | addons/base_import/static/src/css/import.css | 11 | ||||
| -rw-r--r-- | addons/base_import/static/src/js/import.js | 271 | ||||
| -rw-r--r-- | addons/base_import/static/src/xml/import.xml | 76 |
3 files changed, 275 insertions, 83 deletions
diff --git a/addons/base_import/static/src/css/import.css b/addons/base_import/static/src/css/import.css index f6258f5aa50..21e598ff707 100644 --- a/addons/base_import/static/src/css/import.css +++ b/addons/base_import/static/src/css/import.css @@ -10,7 +10,8 @@ .oe_import .oe_import_grid, .oe_import .oe_import_error_report, .oe_import .oe_import_with_file, -.oe_import .oe_import_noheaders { +.oe_import .oe_import_noheaders, +.oe_import .oe_import_report_more { display: none; } @@ -19,7 +20,8 @@ } .oe_import.oe_import_error .oe_import_error_report, .oe_import.oe_import_with_file .oe_import_with_file, -.oe_import.oe_import_noheaders .oe_import_noheaders { +.oe_import.oe_import_noheaders .oe_import_noheaders, +.oe_import .oe_import_report_showmore .oe_import_report_more { display: block; } @@ -29,6 +31,9 @@ .oe_import .oe_import_error_report ul .oe_import_report_warning { background-color: #FEFFD9; } +.oe_import .oe_import_error_report ul .oe_import_report_info { + background-color: #d3ffd3; +} .oe_import .oe_import_noheaders { color: #888; @@ -47,7 +52,7 @@ } .oe_import .oe_import_options label { display: inline-block; - width: 10em; + width: 8em; text-align: right; } diff --git a/addons/base_import/static/src/js/import.js b/addons/base_import/static/src/js/import.js index c2deb660ff2..33c86e88b2c 100644 --- a/addons/base_import/static/src/js/import.js +++ b/addons/base_import/static/src/js/import.js @@ -47,31 +47,35 @@ openerp.base_import = function (instance) { this._super.apply(this, arguments); if(add_button) { this.$buttons.on('click', '.oe_list_button_import', function() { - new instance.web.DataImport(self, self.dataset).open(); + self.do_action({ + type: 'ir.actions.client', + tag: 'import', + params: { + model: self.dataset.model + } + }, void 0, void 0, function () { + self.reload(); + }); return false; }); } } }); - instance.web.DataImport = instance.web.Dialog.extend({ + instance.web.client_actions.add( + 'import', 'instance.web.DataImport'); + instance.web.DataImport = instance.web.Widget.extend({ template: 'ImportView', - dialog_title: _lt("Import Data"), 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 input.oe_import_file': 'file_update', - 'change input.oe_import_has_header, .oe_import_options input': 'settings_updated', - 'click a.oe_import_csv': function (e) { - e.preventDefault(); - }, - 'click a.oe_import_export': function (e) { - e.preventDefault(); - }, + // '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); @@ -79,30 +83,84 @@ openerp.base_import = function (instance) { ? $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' + ]; + }); + 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, dataset) { + init: function (parent, params) { var self = this; - this._super(parent, { - buttons: [ - {text: _t("Import File"), click: function () { - self.do_import(); - }, 'class': 'oe_import_dialog_button'} - ] - }); - this.res_model = parent.model; + this._super.apply(this, arguments); + this.res_model = params.model; // import object id this.id = null; this.Import = new instance.web.Model('base_import.import'); }, start: function () { var self = this; - return this.Import.call('create', [{ - 'res_model': this.res_model - }]).then(function (id) { - self.id = id; - self.$('input[name=import_id]').val(id); - }); + this.setup_encoding_picker(); + + return $.when( + this._super(), + this.Import.call('create', [{ + 'res_model': this.res_model + }]).then(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'); }, import_options: function () { @@ -118,34 +176,52 @@ openerp.base_import = function (instance) { }, //- File & settings change section - file_update: function (e) { + onfile_loaded: function () { + this.$('.oe_import_button').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_updated')); + }, this.proxy('settings_changed')); }, - settings_updated: function () { + onpreviewing: function () { + var self = this; + this.$('.oe_import_button').prop('disabled', true); this.$el.addClass('oe_import_with_file'); // TODO: test that write // succeeded? - this.Import.call( - 'parse_preview', [this.id, this.import_options()]) - .then(this.proxy('preview')); - }, - preview: function (result) { + this.$el.removeClass('oe_import_preview_error oe_import_error'); this.$el.toggleClass( 'oe_import_noheaders', !this.$('input.oe_import_has_header').prop('checked')); - if (result.error) { - this.$el.addClass('oe_import_error'); - this.$('.oe_import_error_report').html( + this.Import.call( + 'parse_preview', [this.id, this.import_options()]) + .then(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.$el.addClass('oe_import_preview_error oe_import_error'); + this.$('.oe_import_error_report').html( QWeb.render('ImportView.preview.error', result)); - return; - } + }, + 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').prop('disabled', false); this.$el.addClass('oe_import_preview'); this.$('table').html(QWeb.render('ImportView.preview', result)); + 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); @@ -180,7 +256,6 @@ openerp.base_import = function (instance) { width: 'resolve', dropdownCssClass: 'oe_import_selector' }); - this.import_dryrun(); }, generate_fields_completion: function (root) { var basic = []; @@ -252,40 +327,116 @@ openerp.base_import = function (instance) { //- import itself call_import: function (options) { - var self = this; var fields = this.$('.oe_import_fields input.oe_import_match_field').map(function (index, el) { return $(el).select2('val') || false; }).get(); return this.Import.call( 'do', [this.id, fields, this.import_options()], options); }, - import_dryrun: function () { -// this.call_import({ dryrun: true }) -// .then(this.proxy('render_import_errors')); + onvalidate: function () { + return this.call_import({ dryrun: true }) + .then(this.proxy('validated')); }, - do_import: function () { + onimport: function () { var self = this; - this.call_import({ dryrun: false }).then(function (errors) { - if (_.isEmpty(errors)) { - if (self.getParent().reload_content) { - self.getParent().reload_content(); - } - self.close(); + return this.call_import({ dryrun: false }).then(function (message) { + if (!_.any(message, function (message) { + return message.type === 'error' })) { + self['import_succeeded'](); return; } - self.render_import_errors(errors); + self['import_failed'](message); }); }, - render_import_errors: function (errors) { - if (_.isEmpty(errors)) { - this.$el.removeClass('oe_import_error'); - return; + onimported: function () { + this.exit(); + }, + exit: function () { + this.do_action({ + type: 'ir.actions.client', + tag: 'history_back' + }); + }, + onresults: function (event, from, to, message) { + var no_messages = _.isEmpty(message); + this.$('.oe_import_import').toggleClass('oe_highlight', no_messages); + this.$('.oe_import_validate').toggleClass('oe_highlight', !no_messages); + if (no_messages) { + message.push({ + type: 'info', + message: _t("Everything seems valid.") + }); } - // import failed (or maybe just warnings, if we ever get - // warnings?) + // row indexes come back 0-indexed, spreadsheets + // display 1-indexed. + var offset = 1; + // offset more if header + if (this.import_options().headers) { offset += 1; } + this.$el.addClass('oe_import_error'); this.$('.oe_import_error_report').html( - QWeb.render('ImportView.error', {errors: errors})); + QWeb.render('ImportView.error', { + errors: _(message).groupBy('message'), + at: function (rows) { + var from = rows.from + offset; + var to = rows.to + offset; + if (from === to) { + return _.str.sprintf(_t("at row %d"), from); + } + return _.str.sprintf(_t("between rows %d and %d"), + from, to); + }, + more: function (n) { + return _.str.sprintf(_t("(%d more)"), n); + }, + info: function (msg) { + if (typeof msg === 'string') { + return _.str.sprintf( + '<div class="oe_import_moreinfo oe_import_moreinfo_message">%s</div>', + _.str.escapeHTML(msg)); + } + if (msg instanceof Array) { + return _.str.sprintf( + '<div class="oe_import_moreinfo oe_import_moreinfo_choices">%s <ul>%s</ul></div>', + _.str.escapeHTML(_t("Here are the possible values:")), + _(msg).map(function (msg) { + return '<li>' + + _.str.escapeHTML(msg) + + '</li>'; + }).join('')); + } + // Final should be object, action descriptor + return [ + '<div class="oe_import_moreinfo oe_import_moreinfo_action">', + _.str.sprintf('<a href="#" data-action="%s">', + _.str.escapeHTML(JSON.stringify(msg))), + _.str.escapeHTML( + _t("Get all possible values")), + '</a>', + '</div>' + ].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' } + ] + }) }; diff --git a/addons/base_import/static/src/xml/import.xml b/addons/base_import/static/src/xml/import.xml index 386348bea4b..1c57c40c9db 100644 --- a/addons/base_import/static/src/xml/import.xml +++ b/addons/base_import/static/src/xml/import.xml @@ -2,17 +2,45 @@ <t t-name="ImportView"> <t t-set="_id" t-value="_.uniqueId('export')"/> <form action="" method="post" enctype="multipart/form-data" class="oe_import"> + <header> + <button type="button" disabled="disabled" + class="oe_button oe_import_button oe_import_validate oe_highlight" + >Validate</button> + <button type="button" disabled="disabled" + class="oe_button oe_import_button oe_import_import" + >Import</button> + <span class="oe_fade">or</span> + <a class="oe_import_cancel" href="#">Cancel</a> + </header> <input type="hidden" name="session_id" t-att-value="widget.session.session_id"/> <input type="hidden" name="import_id"/> <h2>Upload your file</h2> - <p>Select the <a href="#" class="oe_import_csv">.CSV</a> + <p>Select the <a + href="http://en.wikipedia.org/wiki/Comma-separated_values" + class="oe_import_csv" target="_blank">.CSV</a> file to import. If you need a sample importable file, you - can use <a href="#" class="oe_import_export">the export - tool</a> to generate one.</p> + can use the export tool to generate one.</p> <label t-attf-for="file_#{_id}" autofocus="autofocus">CSV File:</label> <input type="file" id-attf-id="file_#{_id}" name="file" class="oe_import_file"/> + <button type="button" class="oe_import_file_reload"> + <img src="/web/static/src/img/icons/gtk-refresh.png"/> + </button> + <div class="oe_import_with_file"> + <a href="#" class="oe_import_toggle"> + File Format Options…</a> + <div class="oe_import_toggled oe_import_options"> + <p t-foreach="widget.opts" t-as="option"> + <!-- no @name, avoid submission when file_update called --> + <label t-attf-for="#{option.name}_#{_id}"> + <t t-esc="option.label"/></label> + <input t-attf-id="#{option.name}_#{_id}" + t-attf-class="oe_import_#{option.name}" + t-att-value="option.value"/> + </p> + </div> + </div> <div class="oe_import_with_file"> <h2>Map your data to OpenERP</h2> @@ -27,18 +55,6 @@ <div class="oe_import_error_report"></div> <table class="oe_import_grid" width="100%"/> - <a href="#" class="oe_import_toggle"> - File Format Options…</a> - <div class="oe_import_toggled oe_import_options"> - <p t-foreach="widget.opts" t-as="option"> - <!-- no @name, avoid submission when file_update called --> - <label t-attf-for="#{option.name}_#{_id}"> - <t t-esc="option.label"/></label> - <input t-attf-id="#{option.name}_#{_id}" - t-attf-class="oe_import_#{option.name}" - t-att-value="option.value"/> - </p> - </div> <h2>Frequently Asked Questions</h2> <dl> @@ -81,17 +97,37 @@ </tr> </t> <t t-name="ImportView.preview.error"> - <p>Import preview failed due to: <t t-esc="error"/></p> + <p>Import preview failed due to: <t t-esc="error"/>. The issue is + usually an incorrect file encoding.</p> <p>Here is the start of the file we could not import:</p> <pre><t t-esc="preview"/></pre> </t> <ul t-name="ImportView.error"> - <li t-foreach="errors" t-as="error" t-attf-class="oe_import_report_#{error.type}"> - <!-- can also have error.record, but may be *huge* if - e.g. has image fields --> - <t t-esc="error.message"/> + <li t-foreach="errors" t-as="error" + t-attf-class="oe_import_report oe_import_report_#{error_value[0].type}"> + <t t-call="ImportView.error.each"> + <t t-set="error" t-value="error_value[0]"/> + </t> + + <a href="#" class="oe_import_report_count" t-if="error_value.length gt 1"> + <t t-esc="more(error_value.length - 1)"/> + </a> + <ul class="oe_import_report_more" t-if="error_value.length gt 1"> + <li t-foreach="error_value.length - 1" t-as="index"> + <t t-call="ImportView.error.each"> + <t t-set="error" t-value="error_value[index + 1]"/> + </t> + </li> + </ul> </li> </ul> + <t t-name="ImportView.error.each"> + <span class="oe_import_report_message"> + <t t-esc="error.message"/> + </span> + <t t-if="error.rows" t-esc="at(error.rows)"/> + <t t-if="error.moreinfo" t-raw="info(error.moreinfo)"/> + </t> <t t-extend="ListView.buttons"> <t t-jquery="span.oe_alternative"> this.attr('t-if', 'widget.options.import_enabled'); |
