/* Copyright (c) 2015-present terrestris GmbH & Co. KG
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* HTML 5 - CSV Importer
*
* This form is used to import csv files to an Ext-JS Grid. As it uses the HTML5
* File-Api InternetExplorer < 10 is not supported.
*
* Current CSV Requirements:
* First Row contains headers.
* Seperator is comma.
* Strings are in doublequotes.
*
* @class BasiGX.view.form.CsvImport
*/
Ext.define('BasiGX.view.form.CsvImport', {
extend: 'Ext.form.Panel',
xtype: 'form-csvimport',
config: {
grid: null,
dataArray: null
},
/**
* Initializes the CsvImport form.
*
* @param {Object} conf The configuration for the CsvImport form.
*/
initComponent: function(conf) {
var me = this;
me.callParent(conf);
if (!me.getGrid()) {
Ext.Error.raise('No grid defined for csv-importer.');
}
},
/**
* You can put default associations here. These will fill the comboboxes.
*
* The `key` represents the csv-column. The `value` represents the grid
* column text, e.g.:
*
* // …
* associatonObject: {
* Name: "Nachname",
* Vorname: "Vorname",
* Straße: null,
* Hausnummer: null,
* Postleitzahl: null,
* Stadt: null,
* "E-Mail_1": "E-Mail",
* "E-Mail_2": null
* },
* // …
*/
associatonObject: {},
bodyPadding: 10,
items: [{
xtype: 'filefield',
name: 'csv_file',
fieldLabel: 'CSV-Datei', // TODO i18n
labelWidth: 70,
width: 400,
msgTarget: 'side',
allowBlank: false,
buttonText: 'Datei auswählen …', // TODO i18n
validator: function(val) {
var fileName = /^.*\.(csv)$/i;
// TODO i18n
var errMsg = 'Der Datenimport ist nur mit CSV-Dateien möglich.';
return fileName.test(val) || errMsg;
}
}],
buttons: [{
xtype: 'button',
name: 'importBtn',
text: 'Importieren', // TODO i18n
handler: function(btn) {
var csvImportForm = this.up('form');
csvImportForm.startImport(btn);
},
disabled: true
}, {
text: 'Datei einlesen', // TODO i18n
handler: function() {
if (window.FileReader) {
var csvImportForm = this.up('form');
var fileField = csvImportForm.down('filefield');
var file = fileField.extractFileInput().files[0];
var reader = new FileReader();
reader.readAsText(file);
reader.onload = csvImportForm.onLoad;
reader.onerror = csvImportForm.onError;
} else {
// TODO i18n
Ext.Toast('FileReader are not supported in this browser.');
}
}
}],
/**
* Bound on the FileReader.load event, this decodes the uploaded file as CSV
* and calls #setDataArray and #setupAssociations.
*
* @param {Object} event The event from the FileReader.
*/
onLoad: function(event) {
var csvImportForm = Ext.ComponentQuery.query('form-csvimport')[0];
var csv = event.target.result;
var dataArray = Ext.util.CSV.decode(csv);
csvImportForm.setDataArray(dataArray);
csvImportForm.setupAssociations(dataArray[0], csvImportForm);
},
/**
* Bound on the FileReader.error event, this warns if a `NotReadableError`
* occured.
*
* @param {Object} evt The event from the FileReader.
*/
onError: function(evt) {
if (evt.target.error.name === 'NotReadableError') {
// TODO i18n
Ext.toast('Canno\'t read file !');
}
},
/**
* Sets up associations between CSV and grid columns.
*
* @param {Array<String>} titleRow The first row of the CSV (the titles).
* @param {BasiGX.view.form.CsvImport} csvImportForm The CSV import form.
*/
setupAssociations: function(titleRow, csvImportForm) {
var dataModelColumns = csvImportForm.getGrid().query(
'gridcolumn[hidden=false]'
);
var columnTitles = [];
var assoFieldset = Ext.create('Ext.form.FieldSet', {
title: 'Felder assoziieren', // TODO i18n
name: 'assoFieldset',
layout: 'form',
scrollable: 'y',
maxHeight: 300,
collapsible: true,
margin: '0 0 30 0'
});
Ext.each(dataModelColumns, function(column) {
columnTitles.push(column.text);
}, csvImportForm);
Ext.each(titleRow, function(columnName) {
assoFieldset.add({
xtype: 'combobox',
name: columnName,
fieldLabel: columnName,
store: columnTitles,
value: csvImportForm.associatonObject[columnName],
msgTarget: 'side'
});
}, csvImportForm);
csvImportForm.down('button[name=importBtn]').enable();
csvImportForm.add(assoFieldset);
},
/**
* Adds a data array to the grid.
*
* @param {Array<Object>} dataArray The CSV rows.
*/
addToGrid: function(dataArray) {
var me = this;
var store = me.getGrid().getStore();
me.setLoading(true);
Ext.each(dataArray, function(dataRow, index) {
if (index > 0) { //skip first row of csv. it contains the header
var instance = this.parseDataFromRow(dataRow);
store.add(instance);
}
}, me);
me.setLoading(false);
},
/**
* Turns a data row into a record.
*
* @param {Object} dataRow A CSV data row.
* @return {Ext.data.Model} A created record for the grid.
*/
parseDataFromRow: function(dataRow) {
var me = this;
var data = {};
Ext.each(dataRow, function(csvColumn, rowIdx) {
var gridColumText = me.associatonObject[
me.getDataArray()[0][rowIdx]];
if (!Ext.isEmpty(csvColumn) && !Ext.isEmpty(gridColumText)) {
var gridColumn = me.getGrid()
.down('gridcolumn[text=' + gridColumText + ']');
if (gridColumn) {
var dataIndex = gridColumn.dataIndex;
data[dataIndex] = csvColumn;
} else {
// TODO i18n
Ext.Error.raise(gridColumText,
' does not exist. Please check you associationObject.');
}
}
});
return Ext.create(this.getGrid().getStore().getModel(), data);
},
/**
* Starts the import.
*
* @param {Ext.button.Button} btn The button.
*/
startImport: function(btn) {
var me = this;
var combos = btn.up('form').down('fieldset[name=assoFieldset]').
query('combo');
var comboValues = [];
var formValid = true;
Ext.each(combos, function(combo) {
var comboVal = combo.getValue();
if (Ext.Array.contains(comboValues, comboVal) &&
!Ext.isEmpty(comboVal)) {
// TODO i18n
combo.markInvalid('Diese Feld wurde bereits mit einer anderen' +
'Spalte asoziert');
formValid = false;
} else {
this.associatonObject[combo.getFieldLabel()] = comboVal;
}
comboValues.push(comboVal);
}, me);
if (formValid && me.getDataArray()) {
me.addToGrid(me.getDataArray());
}
me.fireEvent('importcomplete', me);
}
});