diff --git a/CHANGELOG b/CHANGELOG
index 1e1f1ececd..f9fd8f83f0 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,4 @@
+* Add CSV Import/Export
* Manage translated field
* Add favorites menu
* Show records names in wizard title
diff --git a/bower.json b/bower.json
index 103ee34734..6beaf4d129 100644
--- a/bower.json
+++ b/bower.json
@@ -22,6 +22,7 @@
"gettext.js": "~0.5.0",
"d3": "~3.5.0",
"c3": "~0.4.10",
+ "papaparse": "~4.1.2"
"fullcalendar": "~3.0.0"
},
"devDependencies": {
diff --git a/index.html b/index.html
index bfe6739c03..7f788eccf9 100644
--- a/index.html
+++ b/index.html
@@ -17,6 +17,7 @@ this repository contains the full copyright notices and license terms. -->
+
diff --git a/src/sao.less b/src/sao.less
index d7c3ffad93..faa83cc780 100644
--- a/src/sao.less
+++ b/src/sao.less
@@ -225,6 +225,15 @@ button {
margin-bottom: 0px;
}
+.csv {
+ & .modal-dialog {
+ & .column-fields {
+ height: 35vh;
+ overflow-y: scroll;
+ }
+ }
+}
+
@media screen and (max-width: @screen-xs-max) {
.dict-label {
text-align: left;
diff --git a/src/tab.js b/src/tab.js
index fcfb8a4bf7..2c7e3470c3 100644
--- a/src/tab.js
+++ b/src/tab.js
@@ -363,7 +363,9 @@
['glyphicon-comment', Sao.i18n.gettext('Note'), 'note'],
['glyphicon-cog', Sao.i18n.gettext('Action'), 'action'],
['glyphicon-share-alt', Sao.i18n.gettext('Relate'), 'relate'],
- ['glyphicon-print', Sao.i18n.gettext('Print'), 'print']
+ ['glyphicon-print', Sao.i18n.gettext('Print'), 'print'],
+ ['glyphicon-export', Sao.i18n.gettext('Export'), 'export'],
+ ['glyphicon-import', Sao.i18n.gettext('Import'), 'import']
];
},
create_toolbar: function() {
@@ -805,6 +807,16 @@
this.buttons.print.find('ul.dropdown-menu')
.dropdown('toggle');
}.bind(this));
+ },
+ export: function(){
+ new Sao.Window.Export(this.screen,
+ this.screen.current_view.selected_records().map(function(r) {
+ return r.id;
+ }),
+ this.screen.current_view.get_fields());
+ },
+ import: function(){
+ new Sao.Window.Import(this.screen);
}
});
diff --git a/src/window.js b/src/window.js
index 893cefc63f..b43ff02da2 100644
--- a/src/window.js
+++ b/src/window.js
@@ -732,4 +732,981 @@
this.callback(revision);
}
});
+
+ Sao.Window.CSV = Sao.class_(Object, {
+ init: function(title) {
+ this.encodings = ["866", "ansi_x3.4-1968", "arabic", "ascii",
+ "asmo-708", "big5", "big5-hkscs", "chinese", "cn-big5", "cp1250",
+ "cp1251", "cp1252", "cp1253", "cp1254", "cp1255", "cp1256",
+ "cp1257", "cp1258", "cp819", "cp866", "csbig5", "cseuckr",
+ "cseucpkdfmtjapanese", "csgb2312", "csibm866", "csiso2022jp",
+ "csiso2022kr", "csiso58gb231280", "csiso88596e", "csiso88596i",
+ "csiso88598e", "csiso88598i", "csisolatin1", "csisolatin2",
+ "csisolatin3", "csisolatin4", "csisolatin5", "csisolatin6",
+ "csisolatin9", "csisolatinarabic", "csisolatincyrillic",
+ "csisolatingreek", "csisolatinhebrew", "cskoi8r", "csksc56011987",
+ "csmacintosh", "csshiftjis", "cyrillic", "dos-874", "ecma-114",
+ "ecma-118", "elot_928", "euc-jp", "euc-kr", "gb18030", "gb2312",
+ "gb_2312", "gb_2312-80", "gbk", "greek", "greek8", "hebrew",
+ "hz-gb-2312", "ibm819", "ibm866", "iso-2022-cn", "iso-2022-cn-ext",
+ "iso-2022-jp", "iso-2022-kr", "iso-8859-1", "iso-8859-10",
+ "iso-8859-11", "iso-8859-13", "iso-8859-14", "iso-8859-15",
+ "iso-8859-16", "iso-8859-2", "iso-8859-3", "iso-8859-4",
+ "iso-8859-5", "iso-8859-6", "iso-8859-6-e", "iso-8859-6-i",
+ "iso-8859-7", "iso-8859-8", "iso-8859-8-e", "iso-8859-8-i",
+ "iso-8859-9", "iso-ir-100", "iso-ir-101", "iso-ir-109",
+ "iso-ir-110", "iso-ir-126", "iso-ir-127", "iso-ir-138",
+ "iso-ir-144", "iso-ir-148", "iso-ir-149", "iso-ir-157", "iso-ir-58",
+ "iso8859-1", "iso8859-10", "iso8859-11", "iso8859-13", "iso8859-14",
+ "iso8859-15", "iso8859-2", "iso8859-3", "iso8859-4", "iso8859-5",
+ "iso8859-6", "iso8859-7", "iso8859-8", "iso8859-9", "iso88591",
+ "iso885910", "iso885911", "iso885913", "iso885914", "iso885915",
+ "iso88592", "iso88593", "iso88594", "iso88595", "iso88596",
+ "iso88597", "iso88598", "iso88599", "iso_8859-1", "iso_8859-15",
+ "iso_8859-1:1987", "iso_8859-2", "iso_8859-2:1987", "iso_8859-3",
+ "iso_8859-3:1988", "iso_8859-4", "iso_8859-4:1988", "iso_8859-5",
+ "iso_8859-5:1988", "iso_8859-6", "iso_8859-6:1987", "iso_8859-7",
+ "iso_8859-7:1987", "iso_8859-8", "iso_8859-8:1988", "iso_8859-9",
+ "iso_8859-9:1989", "koi", "koi8", "koi8-r", "koi8-ru", "koi8-u",
+ "koi8_r", "korean", "ks_c_5601-1987", "ks_c_5601-1989", "ksc5601",
+ "ksc_5601", "l1", "l2", "l3", "l4", "l5", "l6", "l9", "latin1",
+ "latin2", "latin3", "latin4", "latin5", "latin6", "logical", "mac",
+ "macintosh", "ms932", "ms_kanji", "shift-jis", "shift_jis", "sjis",
+ "sun_eu_greek", "tis-620", "unicode-1-1-utf-8", "us-ascii",
+ "utf-16", "utf-16be", "utf-16le", "utf-8", "utf8", "visual",
+ "windows-1250", "windows-1251", "windows-1252", "windows-1253",
+ "windows-1254", "windows-1255", "windows-1256", "windows-1257",
+ "windows-1258", "windows-31j", "windows-874", "windows-949",
+ "x-cp1250", "x-cp1251", "x-cp1252", "x-cp1253", "x-cp1254",
+ "x-cp1255", "x-cp1256", "x-cp1257", "x-cp1258", "x-euc-jp", "x-gbk",
+ "x-mac-cyrillic", "x-mac-roman", "x-mac-ukrainian", "x-sjis",
+ "x-user-defined", "x-x-big5"];
+ this.dialog = new Sao.Dialog(title, 'csv', 'lg');
+ this.el = this.dialog.modal;
+
+ this.fields = {};
+ this.fields_model = {};
+ jQuery('', {
+ 'class': 'btn btn-link',
+ 'type': 'button'
+ }).append(Sao.i18n.gettext('Cancel')).click(function(){
+ this.response('RESPONSE_CANCEL');
+ }.bind(this)).appendTo(this.dialog.footer);
+
+ jQuery('', {
+ 'class': 'btn btn-primary',
+ 'type': 'submit'
+ }).append(Sao.i18n.gettext('OK')).click(function(e){
+ this.response('RESPONSE_OK');
+ e.preventDefault();
+ }.bind(this)).appendTo(this.dialog.footer);
+
+ var row_fields = jQuery('
', {
+ 'class': 'row'
+ }).appendTo(this.dialog.body);
+
+ jQuery('
').appendTo(this.dialog.body);
+
+ var column_fields_all = jQuery('', {
+ 'class': 'col-md-4 column-fields'
+ }).append(jQuery('', {
+ 'text': Sao.i18n.gettext('All Fields')
+ })).appendTo(row_fields);
+
+ this.fields_all = jQuery('', {
+ 'class': 'list-unstyled'
+ }).css('cursor', 'pointer')
+ .appendTo(column_fields_all);
+
+ var prm = jQuery.Deferred();
+ this.get_fields(this.screen.model_name)
+ .done(function(fields){
+ this.model_populate(fields).then(function() {
+ prm.resolve();
+ });
+ this.view_populate(this.fields_model, this.fields_all);
+ }.bind(this));
+
+ this.column_buttons = jQuery('', {
+ 'class': 'col-md-4'
+ }).append('').appendTo(row_fields);
+
+ var button_add = jQuery('', {
+ 'class': 'btn btn-default btn-block',
+ 'type': 'button'
+ }).append(jQuery('', {
+ 'class': 'glyphicon glyphicon-plus'
+ })).click(function(){
+ this.fields_all.find('.bg-primary').each(function(i, el_field) {
+ this.sig_sel_add(el_field);
+ }.bind(this));
+ }.bind(this)).append(' '+Sao.i18n.gettext('Add'))
+ .appendTo(this.column_buttons);
+
+ jQuery('', {
+ 'class': 'btn btn-default btn-block',
+ 'type': 'button'
+ }).append(jQuery('', {
+ 'class': 'glyphicon glyphicon-minus'
+ })).click(function(){
+ // sig_unsel
+ this.fields_selected.children('li.bg-primary').remove();
+ }.bind(this)).append(' '+Sao.i18n.gettext('Remove'))
+ .appendTo(this.column_buttons);
+
+ jQuery('', {
+ 'class': 'btn btn-default btn-block',
+ 'type': 'button'
+ }).append(jQuery('', {
+ 'class': 'glyphicon glyphicon-remove'
+ })).click(function(){
+ this.fields_selected.empty();
+ }.bind(this)).append(' '+Sao.i18n.gettext('Clear'))
+ .appendTo(this.column_buttons);
+
+ jQuery('
').appendTo(this.column_buttons);
+
+ var column_fields_selected = jQuery('', {
+ 'class': 'col-md-4 column-fields'
+ }).append(jQuery('', {
+ 'text': Sao.i18n.gettext('Fields Selected')
+ })).appendTo(row_fields);
+
+ // TODO: Make them draggable to re-order
+ this.fields_selected = jQuery('', {
+ 'class': 'list-unstyled'
+ }).css('cursor', 'pointer').appendTo(column_fields_selected);
+
+ this.chooser_form = jQuery('', {
+ 'class': 'form-inline'
+ }).appendTo(this.dialog.body);
+
+ var row_csv_param = jQuery('', {
+ 'class': 'row'
+ }).appendTo(this.dialog.body);
+
+ var expander_icon = jQuery('', {
+ 'class': 'glyphicon glyphicon-plus',
+ }).css('cursor', 'pointer').html(' ');
+
+ var csv_param_label = jQuery('', {
+ 'text': Sao.i18n.gettext('CSV Parameters')
+ }).css('cursor', 'pointer');
+
+ jQuery('', {
+ 'class': 'col-md-12'
+ }).append(expander_icon).append(csv_param_label)
+ .on('click', function(){
+ expander_icon.toggleClass('glyphicon-plus')
+ .toggleClass('glyphicon-minus');
+ this.expander_csv.collapse('toggle');
+ }.bind(this)).appendTo(row_csv_param);
+
+ this.expander_csv = jQuery('', {
+ 'id': 'expander_csv',
+ 'class': 'collapse'
+ }).appendTo(row_csv_param);
+
+ var delimiter_label = jQuery('', {
+ 'text': Sao.i18n.gettext('Delimiter:'),
+ 'class': 'col-sm-2 control-label',
+ 'for': 'input-delimiter'
+ });
+
+ this.el_csv_delimiter = jQuery('', {
+ 'type': 'text',
+ 'class': 'form-control',
+ 'id': 'input-delimiter',
+ 'size': '1',
+ 'maxlength': '1',
+ 'value': ','
+ });
+
+ jQuery('', {
+ 'class': 'form-group'
+ }).append(delimiter_label).append(jQuery('', {
+ 'class': 'col-sm-4'
+ }).append(this.el_csv_delimiter)).appendTo(this.expander_csv);
+
+ var quotechar_label = jQuery('', {
+ 'text': Sao.i18n.gettext('Quote Char:'),
+ 'class': 'col-sm-2 control-label',
+ 'for': 'input-quotechar'
+ });
+
+ this.el_csv_quotechar = jQuery('', {
+ 'type': 'text',
+ 'class': 'form-control',
+ 'id': 'input-quotechar',
+ 'size': '1',
+ 'maxlength': '1',
+ 'value': '\"',
+ 'readonly': '' // Until PapaParse releases custom quote feature
+ });
+
+ jQuery('', {
+ 'class': 'form-group'
+ }).append(quotechar_label).append(jQuery('', {
+ 'class': 'col-sm-4'
+ }).append(this.el_csv_quotechar))
+ .appendTo(this.expander_csv);
+
+ var encoding_label = jQuery('', {
+ 'text': Sao.i18n.gettext('Encoding:'),
+ 'class': 'col-sm-2 control-label',
+ 'for': 'input-encoding'
+ });
+
+ this.el_csv_encoding = jQuery('', {
+ 'class': 'form-control',
+ 'id': 'input-encoding'
+ });
+
+ for(var i=0; i', {
+ 'val': this.encodings[i]
+ }).html(this.encodings[i]).appendTo(this.el_csv_encoding);
+ }
+
+ var enc = 'utf-8';
+ if (navigator.platform == 'Win32' ||
+ navigator.platform == 'Windows') {
+ enc = 'cp1252';
+ }
+ this.el_csv_encoding.children('option[value="' + enc + '"]')
+ .attr('selected', 'selected');
+
+ jQuery('', {
+ 'class': 'form-group'
+ }).append(encoding_label).append(jQuery('', {
+ 'class': 'col-sm-4'
+ }).append(this.el_csv_encoding))
+ .appendTo(this.expander_csv);
+
+ this.el.modal('show');
+ this.el.on('hidden.bs.modal', function() {
+ jQuery(this).remove();
+ });
+ return prm;
+ },
+ get_fields: function(model) {
+ return Sao.rpc({
+ 'method': 'model.' + model + '.fields_get'
+ }, this.session);
+ },
+ on_row_expanded: function(node) {
+ var container_view = jQuery('').css('list-style', 'none')
+ .insertAfter(node.view);
+ this.children_expand(node).done(function() {
+ this.view_populate(node.children, container_view);
+ }.bind(this));
+ }
+ });
+
+ Sao.Window.Import = Sao.class_(Sao.Window.CSV, {
+ init: function(screen) {
+ this.screen = screen;
+ this.session = Sao.Session.current_session;
+ this.fields_data = {}; // Ask before Removing this.
+ this.fields_invert = {};
+ Sao.Window.Import._super.init.call(this,
+ Sao.i18n.gettext('Import from CSV'));
+
+ jQuery('', {
+ 'class': 'btn btn-default btn-block',
+ 'type': 'button'
+ }).append(jQuery('', {
+ 'class': 'glyphicon glyphicon-search'
+ })).click(function(){
+ this.autodetect();
+ }.bind(this)).append(' '+Sao.i18n.gettext('Auto-Detect'))
+ .appendTo(this.column_buttons);
+
+ var chooser_label = jQuery('', {
+ 'text': Sao.i18n.gettext('File to Import'),
+ 'class': 'col-sm-6 control-label',
+ 'for': 'input-csv-file'
+ });
+
+ this.file_input = jQuery('', {
+ 'type': 'file',
+ 'id': 'input-csv-file'
+ });
+
+ jQuery('', {
+ 'class': 'form-group'
+ }).append(chooser_label).append(jQuery('', {
+ 'class': 'col-sm-6'
+ }).append(this.file_input))
+ .appendTo(this.chooser_form);
+
+ jQuery('
').insertAfter(this.chooser_form);
+
+ var skip_label = jQuery('', {
+ 'text': Sao.i18n.gettext('Lines to Skip:'),
+ 'class': 'col-sm-2 control-label',
+ 'for': 'input-skip'
+ });
+
+ this.el_csv_skip = jQuery('', {
+ 'type': 'number',
+ 'class': 'form-control',
+ 'id': 'input-skip',
+ 'value': '0'
+ });
+
+ jQuery('', {
+ 'class': 'form-group'
+ }).append(skip_label).append(jQuery('', {
+ 'class': 'col-sm-4'
+ }).append(this.el_csv_skip))
+ .appendTo(this.expander_csv);
+ },
+ sig_sel_add: function(el_field) {
+ el_field = jQuery(el_field);
+ var field = el_field.attr('field');
+ var node = jQuery('', {
+ 'field': field,
+ }).html(el_field.attr('name')).click(function(e) {
+ if (e.ctrlKey) {
+ node.toggleClass('bg-primary');
+ } else {
+ jQuery(e.target).addClass('bg-primary')
+ .siblings().removeClass('bg-primary');
+ }
+ }).appendTo(this.fields_selected);
+ },
+ view_populate: function (parent_node, parent_view) {
+ var fields_order = Object.keys(parent_node).sort(function(a,b) {
+ if (parent_node[b].string < parent_node[a].string) {
+ return -1;
+ }
+ else {
+ return 1;
+ }
+ }).reverse();
+
+ fields_order.forEach(function(field) {
+ var name = parent_node[field].string || field;
+ var node = jQuery('', {
+ 'field': parent_node[field].field,
+ 'name': parent_node[field].name
+ }).html(name).click(function(e) {
+ if(e.ctrlKey) {
+ node.toggleClass('bg-primary');
+ } else {
+ this.fields_all.find('li').removeClass('bg-primary');
+ node.addClass('bg-primary');
+ }
+ }.bind(this)).appendTo(parent_view);
+ parent_node[field].view = node;
+
+ if (parent_node[field].relation) {
+ node.prepend(' ');
+ var expander_icon = jQuery('', {
+ 'class': 'glyphicon glyphicon-plus'
+ }).click(function(e) {
+ e.stopPropagation();
+ expander_icon.toggleClass('glyphicon-plus')
+ .toggleClass('glyphicon-minus');
+ if(expander_icon.hasClass('glyphicon-minus')) {
+ this.on_row_expanded(parent_node[field]);
+ }
+ else {
+ node.next('ul').remove();
+ }
+ }.bind(this)).prependTo(node);
+ }
+ }.bind(this));
+ },
+ model_populate: function (fields, parent_node, prefix_field,
+ prefix_name) {
+ parent_node = parent_node || this.fields_model;
+ prefix_field = prefix_field || '';
+ prefix_name = prefix_name || '';
+
+ Object.keys(fields).forEach(function(field) {
+ if(!fields[field].readonly) {
+ var name = fields[field].string || field;
+ name = prefix_name + name;
+ // Only One2Many can be nested for import
+ var relation;
+ if (fields[field].type == 'one2many') {
+ relation = fields[field].relation;
+ } else {
+ relation = null;
+ }
+ var node = {
+ name: name,
+ field: prefix_field + field,
+ relation: relation,
+ string: fields[field].string
+ };
+ parent_node[field] = node;
+ this.fields[prefix_field + field] = node;
+ this.fields_invert[name] = prefix_field + field;
+ if (relation) {
+ node.children = {};
+ }
+ }
+ }.bind(this));
+ return jQuery.Deferred();
+ },
+ children_expand: function(node) {
+ var dfd = jQuery.Deferred();
+ if (jQuery.isEmptyObject(node.children)) {
+ this.get_fields(node.relation).done(function(fields) {
+ this.model_populate(fields, node.children,
+ node.field + '/', node.name + '/');
+ dfd.resolve(this);
+ }.bind(this));
+ } else {
+ dfd.resolve(this);
+ }
+ return dfd.promise();
+ },
+ autodetect: function() {
+ var fname = this.file_input.val();
+ if(!fname) {
+ Sao.common.message.run(
+ Sao.i18n.gettext('You must select an import file first'));
+ return;
+ }
+ this.fields_selected.empty();
+ this.el_csv_skip.val(1);
+ Papa.parse(this.file_input[0].files[0], {
+ config: {
+ delimiter: this.el_csv_delimiter.val(),
+ // TODO quoteChar: this.el_csv_quotechar.val(),
+ preview: 1,
+ encoding: this.el_csv_encoding.val()
+ },
+ error: function(err, file, inputElem, reason) {
+ Sao.common.warning(
+ Sao.i18n.gettext('Error occured in loading the file'));
+ },
+ complete: function(results) {
+ results.data[0].forEach(function(word) {
+ if(word in this.fields_invert || word in this.fields) {
+ this.auto_select(word);
+ }
+ else {
+ var fields = this.fields_model;
+ var prefix = '';
+ var parents = word.split('/');
+ this.traverse(fields, prefix, parents, 0);
+ }
+ }.bind(this));
+ }.bind(this)
+ });
+ },
+ auto_select: function(word) {
+ var name,field;
+ if(word in this.fields_invert) {
+ name = word;
+ field = this.fields_invert[word];
+ }
+ else if (word in this.fields) {
+ name = this.fields[word].name;
+ field = [word];
+ }
+ else {
+ Sao.common.warning.run(
+ Sao.i18n.gettext(
+ 'Error processing the file at field %1.', word),
+ Sao.i18n.gettext('Error'));
+ return;
+ }
+ var node = jQuery('', {
+ 'field': field
+ }).html(name).click(function(){
+ node.addClass('bg-primary')
+ .siblings().removeClass('bg-primary');
+ }).appendTo(this.fields_selected);
+ },
+ traverse: function(fields, prefix, parents, i) {
+ if(i >= parents.length - 1) {
+ this.auto_select(parents.join('/'));
+ return;
+ }
+ var field, item;
+ var names = Object.keys(fields);
+ for(item = 0; item', {
+ 'class': 'row'
+ }).prependTo(this.dialog.body);
+
+ var predefined_exports_column = jQuery('', {
+ 'class': 'col-md-12'
+ }).append(jQuery('', {
+ 'text': Sao.i18n.gettext('Predefined Exports')
+ })).appendTo(row_header);
+
+ this.predef_exports_list = jQuery('', {
+ 'class': 'list-unstyled predef-exports'
+ }).css('cursor', 'pointer')
+ .appendTo(predefined_exports_column);
+
+ predefined_exports_column.append('
');
+
+ this.predef_exports = {};
+ this.fill_predefwin();
+
+ jQuery('', {
+ 'class': 'btn btn-default btn-block',
+ 'type': 'button'
+ }).append(jQuery('', {
+ 'class': 'glyphicon glyphicon-floppy-save'
+ })).click(function(){
+ this.addreplace_predef();
+ }.bind(this)).append(' '+Sao.i18n.gettext('Save Export'))
+ .appendTo(this.column_buttons);
+
+ jQuery('', {
+ 'class': 'btn btn-default btn-block',
+ 'type': 'button'
+ }).append(jQuery('', {
+ 'class': 'glyphicon glyphicon-floppy-remove'
+ })).click(function(){
+ this.remove_predef();
+ }.bind(this)).append(' '+Sao.i18n.gettext('Delete Export'))
+ .appendTo(this.column_buttons);
+
+ this.el_add_field_names = jQuery('', {
+ 'type': 'checkbox',
+ 'checked': 'checked'
+ });
+
+ jQuery('', {
+ 'class': 'form-group'
+ }).append(jQuery('', {
+ 'class': 'col-md-6'
+ }).append(jQuery('', {
+ 'text': ' '+Sao.i18n.gettext('Add Field Names')
+ }).prepend(this.el_add_field_names))).appendTo(this.expander_csv);
+ },
+ view_populate: function(parent_node, parent_view) {
+ var names = Object.keys(parent_node).sort(function(a, b) {
+ if (parent_node[b].string < parent_node[a].string) {
+ return -1;
+ }
+ else {
+ return 1;
+ }
+ }).reverse();
+
+ names.forEach(function(name) {
+ var path = parent_node[name].path;
+ var node = jQuery('', {
+ 'path': path
+ }).html(parent_node[name].string).click(function(e) {
+ if(e.ctrlKey) {
+ node.toggleClass('bg-primary');
+ } else {
+ this.fields_all.find('li')
+ .removeClass('bg-primary');
+ node.addClass('bg-primary');
+ }
+ }.bind(this)).appendTo(parent_view);
+ parent_node[name].view = node;
+
+ if (parent_node[name].children) {
+ node.prepend(' ');
+ var expander_icon = jQuery('', {
+ 'class': 'glyphicon glyphicon-plus'
+ }).click(function(e){
+ e.stopPropagation();
+ expander_icon.toggleClass('glyphicon-plus')
+ .toggleClass('glyphicon-minus');
+ if(expander_icon.hasClass('glyphicon-minus')) {
+ this.on_row_expanded(parent_node[name]);
+ }
+ else {
+ node.next('ul').remove();
+ }
+ }.bind(this)).prependTo(node);
+ }
+ }.bind(this));
+ },
+ model_populate: function(fields, parent_node, prefix_field,
+ prefix_name) {
+ parent_node = parent_node || this.fields_model;
+ prefix_field = prefix_field || '';
+ prefix_name = prefix_name || '';
+
+ var prm = jQuery.Deferred();
+ Object.keys(fields).forEach(function(name) {
+ var field = fields[name];
+ var string = field.string || name;
+ var items = [{ name: name, field: field, string: string }];
+
+ if (field.type == 'selection') {
+ items.push({
+ name: name+'.translated',
+ field: field,
+ string: Sao.i18n.gettext('%1 (string)', string)
+ });
+ }
+
+ items.forEach(function(item) {
+ var path = prefix_field + item.name;
+ var long_string = item.string;
+
+ if (prefix_field) {
+ long_string = prefix_name + item.string;
+ }
+
+ var node = {
+ path: path,
+ string: item.string,
+ long_string: long_string,
+ relation: item.field.relation
+ };
+ parent_node[item.name] = node;
+ this.fields[path] = node;
+
+ // Insert relation only to real field
+ if (item.name.indexOf('.') == -1 && item.field.relation) {
+ node.children = {};
+ }
+ }.bind(this));
+ }.bind(this));
+ prm.resolve();
+ return prm;
+ },
+ children_expand: function(node) {
+ var dfd = jQuery.Deferred();
+ if(jQuery.isEmptyObject(node.children)) {
+ this.get_fields(node.relation).done(function(fields) {
+ this.model_populate(fields, node.children,
+ node.path + '/', node.string + '/');
+ dfd.resolve(this);
+ }.bind(this));
+ } else {
+ dfd.resolve(this);
+ }
+ return dfd.promise();
+ },
+ sig_sel_add: function(el_field) {
+ el_field = jQuery(el_field);
+ var name = el_field.attr('path');
+ this.sel_field(name);
+ },
+ fill_predefwin: function() {
+ Sao.rpc({
+ 'method': 'model.ir.export.search',
+ 'params': [['resource', '=', this.screen.model_name], {}]
+ }, this.session).done(function(export_ids) {
+ Sao.rpc({
+ 'method': 'model.ir.export.read',
+ 'params': [export_ids, {}]
+ }, this.session).done(function(exports) {
+ var arr = [];
+ exports.forEach(function(o) {
+ for (var i = 0; i < o.export_fields.length;
+ arr.push(o.export_fields[i++]));
+ });
+ Sao.rpc({
+ 'method': 'model.ir.export.line.read',
+ 'params': [arr, {}]
+ }, this.session).done(function(lines) {
+ var id2lines = {};
+ lines.forEach(function(line) {
+ id2lines[line.export] = id2lines[line.export] || [];
+ id2lines[line.export].push(line);
+ });
+ exports.forEach(function(export_) {
+ this.predef_exports[export_.id] =
+ id2lines[export_.id].map(function(obj) {
+ if(obj.export == export_.id)
+ return obj.name;
+ });
+ this.add_to_predef(export_.id, export_.name);
+ }.bind(this));
+ this.predef_exports_list.children('li').first().focus();
+ }.bind(this));
+ }.bind(this));
+ }.bind(this));
+ },
+ add_to_predef: function(id, name) {
+ var node = jQuery('', {
+ 'text': name,
+ 'export_id': id,
+ 'tabindex': 0
+ }).on('keypress', function(e) {
+ var keyCode = (e.keyCode ? e.keyCode : e.which);
+ if(keyCode == 13 || keyCode == 32) {
+ node.click();
+ }
+ }).click(function(event) {
+ node.addClass('bg-primary')
+ .siblings().removeClass('bg-primary');
+ this.sel_predef(jQuery(event.target).attr('export_id'));
+ }.bind(this));
+ this.predef_exports_list.append(node);
+ },
+ addreplace_predef: function() {
+ var fields = [];
+ var selected_fields = this.fields_selected.children('li');
+ for(var i=0; i= parents.length-1) {
+ this.sel_field(parents.join('/'));
+ return;
+ }
+ var field, item;
+ var names = Object.keys(fields);
+ for(item = 0; item < names.length; item++) {
+ field = fields[names[item]];
+ if(field.path == (prefix+parents[i])) {
+ this.children_expand(field).done(callback);
+ break;
+ }
+ }
+ if(item == names.length) {
+ this.sel_field(parents.join('/'));
+ return;
+ }
+ function callback(self){
+ fields = field.children;
+ prefix += parents[i] + '/';
+ self.traverse(fields, prefix, parents, ++i);
+ }
+ },
+ sel_field: function(name) {
+ var long_string = this.fields[name].long_string;
+ var relation = this.fields[name].relation;
+ if (relation) {
+ return;
+ }
+ var node = jQuery('', {
+ 'path': name,
+ }).html(long_string).click(function(e) {
+ if(e.ctrlKey) {
+ node.toggleClass('bg-primary');
+ } else {
+ jQuery(e.target).addClass('bg-primary')
+ .siblings().removeClass('bg-primary');
+ }
+ }).appendTo(this.fields_selected);
+ },
+ response: function(response_id) {
+ if(response_id == 'RESPONSE_OK') {
+ var fields = [];
+ var fields2 = [];
+ this.fields_selected.children('li').each(function(i, field) {
+ fields.push(field.getAttribute('path'));
+ fields2.push(field.innerText);
+ });
+ Sao.rpc({
+ 'method': 'model.' + this.screen.model_name +
+ '.export_data',
+ 'params': [this.ids, fields, {}]
+ }, this.session).then(function(data) {
+ this.export_csv(fields2, data);
+ }.bind(this));
+ } else {
+ this.el.modal('hide');
+ }
+ },
+ export_csv: function(fields, data) {
+ var encoding = this.el_csv_encoding.val();
+ var unparse_obj = {};
+ unparse_obj.data = data;
+ if (this.el_add_field_names.is(':checked')) {
+ unparse_obj.fields = fields;
+ }
+ var csv = Papa.unparse(unparse_obj, {
+ // TODO quoteChar: this.el_csv_quotechar.val(),
+ delimiter: this.el_csv_delimiter.val()
+ });
+ var blob = new Blob([csv], {type: 'text/csv;charset=' + encoding});
+ var blob_url = window.URL.createObjectURL(blob);
+ if (this.blob_url) {
+ window.URL.revokeObjectURL(this.blob_url);
+ }
+ this.blob_url = blob_url;
+ window.open(blob_url);
+
+ Sao.common.message.run(
+ Sao.i18n.ngettext('%1 record saved', '%1 records saved',
+ data.length));
+ }
+ });
+
}());