summaryrefslogtreecommitdiff
path: root/addons/base_import/static/src
diff options
context:
space:
mode:
authorFrédéric van der Essen <fva@openerp.com>2012-10-17 16:05:09 +0200
committerFrédéric van der Essen <fva@openerp.com>2012-10-17 16:05:09 +0200
commitcbdb0ccf9c03fc2f9eef6b447d5c42df64ba3e4b (patch)
tree6e373389997ee6cff6273ede9a6e3a1b1c7e23e2 /addons/base_import/static/src
parent3f48855822d1532e6b6284222307b3674642cb52 (diff)
parent04eef2c7a0775a0c6c7252c990d5c691effc262e (diff)
[MERGE] from trunk
bzr revid: fva@openerp.com-20121017140509-67yq1gjzrs871hb6
Diffstat (limited to 'addons/base_import/static/src')
-rw-r--r--addons/base_import/static/src/css/import.css11
-rw-r--r--addons/base_import/static/src/js/import.js271
-rw-r--r--addons/base_import/static/src/xml/import.xml76
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');