summaryrefslogtreecommitdiff
path: root/addons/base_import/static/src
diff options
context:
space:
mode:
authorJigar Amin - OpenERP <jam@tinyerp.com>2012-09-14 09:57:25 +0530
committerJigar Amin - OpenERP <jam@tinyerp.com>2012-09-14 09:57:25 +0530
commitd7dfc3dc1cad3591280b7a7aeb9e2e352cbc1fa8 (patch)
treed8b5acdbd13b6a99af89b22e9f46fe8872478d04 /addons/base_import/static/src
parent275b274dd53aa42a0129fa8c5a356cd2faf6dcf6 (diff)
parent9df7381ced04b44e470fe0a060b6b82773f20c84 (diff)
[MERGE] lp:openobject-addons
bzr revid: jam@tinyerp.com-20120912044315-t7gtrwck9fc3h9vd bzr revid: jam@tinyerp.com-20120913045246-iuvrqt992b60tekt bzr revid: jam@tinyerp.com-20120914042725-jiagjctwti0vd68m
Diffstat (limited to 'addons/base_import/static/src')
-rw-r--r--addons/base_import/static/src/css/import.css57
-rw-r--r--addons/base_import/static/src/js/import.js291
-rw-r--r--addons/base_import/static/src/xml/import.xml103
3 files changed, 451 insertions, 0 deletions
diff --git a/addons/base_import/static/src/css/import.css b/addons/base_import/static/src/css/import.css
new file mode 100644
index 00000000000..f6258f5aa50
--- /dev/null
+++ b/addons/base_import/static/src/css/import.css
@@ -0,0 +1,57 @@
+.openerp .oe_list_buttons .oe_alternative {
+ visibility: visible;
+}
+.openerp .oe_list_buttons.oe_editing .oe_list_button_import {
+ display: none;
+}
+
+.oe_import dd,
+.oe_import .oe_import_toggled,
+.oe_import .oe_import_grid,
+.oe_import .oe_import_error_report,
+.oe_import .oe_import_with_file,
+.oe_import .oe_import_noheaders {
+ display: none;
+}
+
+.oe_import.oe_import_preview .oe_import_grid {
+ display: table;
+}
+.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 {
+ display: block;
+}
+
+.oe_import .oe_import_error_report ul .oe_import_report_error {
+ background-color: #FFD9DB;
+}
+.oe_import .oe_import_error_report ul .oe_import_report_warning {
+ background-color: #FEFFD9;
+}
+
+.oe_import .oe_import_noheaders {
+ color: #888;
+}
+
+.oe_import a.oe_import_toggle {
+ display: block;
+}
+.oe_import a.oe_import_toggle:before {
+ content: '> '
+}
+
+.oe_import .oe_import_options p {
+ margin: 0;
+ padding: 0;
+}
+.oe_import .oe_import_options label {
+ display: inline-block;
+ width: 10em;
+ text-align: right;
+}
+
+.oe_import_selector ul,
+.oe_import_selector li {
+ margin: 0; padding: 0;
+}
diff --git a/addons/base_import/static/src/js/import.js b/addons/base_import/static/src/js/import.js
new file mode 100644
index 00000000000..c2deb660ff2
--- /dev/null
+++ b/addons/base_import/static/src/js/import.js
@@ -0,0 +1,291 @@
+openerp.base_import = function (instance) {
+ var QWeb = instance.web.qweb;
+ var _t = instance.web._t;
+ var _lt = instance.web._lt;
+
+ /**
+ * 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);
+ }
+
+ // if true, the 'Import', 'Export', etc... buttons will be shown
+ instance.web.ListView.prototype.defaults.import_enabled = true;
+ instance.web.ListView.include({
+ on_loaded: 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() {
+ new instance.web.DataImport(self, self.dataset).open();
+ return false;
+ });
+ }
+ }
+ });
+
+ instance.web.DataImport = instance.web.Dialog.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();
+ },
+ 'click a.oe_import_toggle': function (e) {
+ e.preventDefault();
+ var $el = $(e.target);
+ ($el.next().length
+ ? $el.next()
+ : $el.parent().next())
+ .toggle();
+ }
+ },
+ init: function (parent, dataset) {
+ 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;
+ // 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);
+ });
+ },
+
+ 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
+ file_update: function (e) {
+ 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'));
+ },
+ settings_updated: function () {
+ 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.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(
+ QWeb.render('ImportView.preview.error', result));
+ return;
+ }
+ this.$el.addClass('oe_import_preview');
+ this.$('table').html(QWeb.render('ImportView.preview', result));
+
+ 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;
+ }
+ }
+ 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));
+ },
+
+ width: 'resolve',
+ dropdownCssClass: 'oe_import_selector'
+ });
+ this.import_dryrun();
+ },
+ 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<end; ++i) {
+ traverse(subfields[i], field_path, collection);
+ }
+ }
+ _(root.fields).each(function (field) {
+ traverse(field, []);
+ });
+
+ var cmp = function (field1, field2) {
+ return field1.text.localeCompare(field2.text);
+
+ };
+ regulars.sort(cmp);
+ o2m.sort(cmp);
+ return basic.concat([
+ { text: _t("Normal Fields"), children: regulars },
+ { text: _t("Relation Fields"), children: o2m }
+ ]);
+ },
+ render_fields_matches: function (result, $fields) {
+ if (_(result.matches).isEmpty()) { return; }
+ $fields.each(function (index, input) {
+ var match = result.matches[index];
+ if (!match) { return; }
+
+ var current_field = result;
+ input.value = _(match).chain()
+ .map(function (name) {
+ // WARNING: does both mapping and folding (over the
+ // ``field`` iterator variable)
+ return current_field = _(current_field.fields).find(function (subfield) {
+ return subfield.name === name;
+ });
+ })
+ .pluck('name')
+ .value()
+ .join('/');
+ });
+ },
+
+ //- 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'));
+ },
+ do_import: 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;
+ }
+ self.render_import_errors(errors);
+ });
+ },
+ render_import_errors: function (errors) {
+ if (_.isEmpty(errors)) {
+ this.$el.removeClass('oe_import_error');
+ return;
+ }
+ // import failed (or maybe just warnings, if we ever get
+ // warnings?)
+ this.$el.addClass('oe_import_error');
+ this.$('.oe_import_error_report').html(
+ QWeb.render('ImportView.error', {errors: errors}));
+ },
+ });
+};
diff --git a/addons/base_import/static/src/xml/import.xml b/addons/base_import/static/src/xml/import.xml
new file mode 100644
index 00000000000..386348bea4b
--- /dev/null
+++ b/addons/base_import/static/src/xml/import.xml
@@ -0,0 +1,103 @@
+<templates>
+ <t t-name="ImportView">
+ <t t-set="_id" t-value="_.uniqueId('export')"/>
+ <form action="" method="post" enctype="multipart/form-data" class="oe_import">
+ <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>
+ 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>
+ <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"/>
+
+ <div class="oe_import_with_file">
+ <h2>Map your data to OpenERP</h2>
+ <input type="checkbox" class="oe_import_has_header"
+ id="oe_import_has_header" checked="checked"/>
+ <label for="oe_import_has_header">The first row of the
+ file contains the label of the column</label>
+ <p class="oe_import_noheaders">If the file contains
+ the column names, OpenERP can try auto-detecting the
+ field corresponding to the column. This makes imports
+ simpler especially when the file has many columns.</p>
+
+ <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>
+ <dt><a href="#" class="oe_import_toggle">
+ Need to import data from an other application?</a></dt>
+ <dd>
+ <p>In order to re-create relationships between
+ different records, you should use the unique
+ identifier from the original application and
+ map it to the <abbr title="External ID">ID</abbr>
+ column in OpenERP. When you
+ import an other record that links to the first
+ one, use <abbr title="XXX/External ID">XXX/ID</abbr>
+ to the original unique identifier.</p>
+ <p>The <abbr title="External ID">ID</abbr>
+ will also be used to update the original
+ import if you need to re-import modified data
+ later, it's thus good practice to specify it
+ whenever possible</p>
+ </dd>
+ </dl>
+ </div>
+ </form>
+ </t>
+ <t t-name="ImportView.preview">
+ <tr t-if="headers" class="oe_import_grid-header">
+ <td t-foreach="headers" t-as="header" class="oe_import_grid-cell"
+ ><t t-esc="header"/></td>
+ </tr>
+ <tr class="oe_import_fields">
+ <!-- Iterate on first row to ensure we have all columns -->
+ <td t-foreach="preview[0]" t-as="column">
+ <input placeholder="Don't Import"
+ class="oe_import_match_field"/>
+ </td>
+ </tr>
+ <tr t-foreach="preview" t-as="row" class="oe_import_grid-row">
+ <td t-foreach="row" t-as="cell" class="oe_import_grid-cell"
+ ><t t-esc="cell"/></td>
+ </tr>
+ </t>
+ <t t-name="ImportView.preview.error">
+ <p>Import preview failed due to: <t t-esc="error"/></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>
+ </ul>
+ <t t-extend="ListView.buttons">
+ <t t-jquery="span.oe_alternative">
+ this.attr('t-if', 'widget.options.import_enabled');
+ </t>
+ <t t-jquery="span.oe_alternative" t-operation="append">
+ <a href="#" class="oe_bold oe_list_button_import">Import</a>
+ </t>
+ </t>
+</templates>