mirror of https://github.com/keeweb/keeweb.git
179 lines
5.5 KiB
JavaScript
179 lines
5.5 KiB
JavaScript
import kdbxweb from 'kdbxweb';
|
|
import { View } from 'framework/views/view';
|
|
import { Scrollable } from 'framework/views/scrollable';
|
|
import template from 'templates/import-csv.hbs';
|
|
import { EntryModel } from 'models/entry-model';
|
|
|
|
class ImportCsvView extends View {
|
|
parent = '.app__body';
|
|
|
|
template = template;
|
|
|
|
events = {
|
|
'click .back-button': 'returnToApp',
|
|
'click .import-csv__button-cancel': 'returnToApp',
|
|
'click .import-csv__button-run': 'runImport',
|
|
'change .import-csv__field-select': 'changeMapping',
|
|
'change .import-csv__target-select': 'changeGroup'
|
|
};
|
|
|
|
knownFields = [
|
|
{ field: 'Title', re: /title|\bname|account/i },
|
|
{ field: 'UserName', re: /user|login/i },
|
|
{ field: 'Password', re: /pass/i },
|
|
{ field: 'URL', re: /url|site/i },
|
|
{ field: 'Notes', re: /notes|comment|extra/i }
|
|
];
|
|
|
|
fieldMapping = [];
|
|
targetGroup = undefined;
|
|
|
|
constructor(model, options) {
|
|
super(model, options);
|
|
this.appModel = options.appModel;
|
|
this.fileName = options.fileName;
|
|
this.guessFieldMapping();
|
|
this.fillGroups();
|
|
this.initScroll();
|
|
}
|
|
|
|
render() {
|
|
super.render({
|
|
headers: this.model.headers,
|
|
rows: this.model.rows,
|
|
fieldMapping: this.fieldMapping,
|
|
groups: this.groups
|
|
});
|
|
this.createScroll({
|
|
root: this.$el.find('.import-csv__body')[0],
|
|
scroller: this.$el.find('.import-csv__body > .scroller')[0],
|
|
bar: this.$el.find('.import-csv__body > .scroller__bar-wrapper > .scroller__bar')[0]
|
|
});
|
|
this.pageResized();
|
|
if (!this.scroll._update) {
|
|
this.scroll._update = this.scroll.update;
|
|
this.scroll.update = this.scrollUpdate.bind(this);
|
|
}
|
|
}
|
|
|
|
scrollUpdate() {
|
|
this.scroller.css({ width: 'auto', minWidth: 'auto', maxWidth: 'auto' });
|
|
this.scroll._update();
|
|
}
|
|
|
|
returnToApp() {
|
|
this.emit('cancel');
|
|
}
|
|
|
|
changeMapping(e) {
|
|
const col = +e.target.dataset.col;
|
|
const field = e.target.value;
|
|
|
|
const isBuiltIn = this.knownFields.some((f) => f.field === field);
|
|
const mapping = field ? (isBuiltIn ? 'builtin' : 'custom') : 'ignore';
|
|
|
|
this.fieldMapping[col] = {
|
|
mapping,
|
|
field
|
|
};
|
|
|
|
if (field) {
|
|
let ix = 0;
|
|
for (const mapping of this.fieldMapping) {
|
|
if (mapping.field === field && col !== ix) {
|
|
mapping.type = 'ignore';
|
|
mapping.field = '';
|
|
const select = this.el.querySelector(
|
|
`.import-csv__field-select[data-col="${ix}"]`
|
|
);
|
|
select.value = '';
|
|
}
|
|
ix++;
|
|
}
|
|
}
|
|
}
|
|
|
|
guessFieldMapping() {
|
|
const usedFields = {};
|
|
|
|
for (const fieldName of this.model.headers.map((f) => f.trim())) {
|
|
if (!fieldName || /^(group|grouping)$/i.test(fieldName)) {
|
|
this.fieldMapping.push({ type: 'ignore' });
|
|
continue;
|
|
}
|
|
|
|
let found = false;
|
|
for (const { field, re } of this.knownFields) {
|
|
if (!usedFields[field] && re.test(fieldName)) {
|
|
this.fieldMapping.push({ type: 'builtin', field });
|
|
usedFields[field] = true;
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
this.fieldMapping.push({ type: 'custom', field: fieldName });
|
|
}
|
|
}
|
|
}
|
|
|
|
fillGroups() {
|
|
this.groups = [];
|
|
for (const file of this.appModel.files) {
|
|
file.forEachGroup((group) => {
|
|
const title = group.title;
|
|
const spaces = [];
|
|
for (let parent = group; parent.parentGroup; parent = parent.parentGroup) {
|
|
spaces.push(' ', ' ');
|
|
}
|
|
this.groups.push({ id: group.id, fileId: file.id, spaces, title });
|
|
});
|
|
}
|
|
}
|
|
|
|
changeGroup(e) {
|
|
const groupId = e.target.value;
|
|
if (!groupId) {
|
|
this.targetGroup = undefined;
|
|
return;
|
|
}
|
|
const fileId = e.target.querySelector(`option[value="${groupId}"]`).dataset.file;
|
|
const file = this.appModel.files.get(fileId);
|
|
this.targetGroup = file.getGroup(groupId);
|
|
}
|
|
|
|
runImport() {
|
|
let group = this.targetGroup;
|
|
let file = group ? group.file : undefined;
|
|
if (!group) {
|
|
const fileName = this.fileName.replace(/\.csv$/i, '');
|
|
file = this.appModel.createNewFile(fileName);
|
|
group = file.groups[0];
|
|
}
|
|
for (const row of this.model.rows) {
|
|
const newEntry = EntryModel.newEntry(group, file);
|
|
for (let ix = 0; ix < row.length; ix++) {
|
|
let value = row[ix];
|
|
if (!value) {
|
|
continue;
|
|
}
|
|
const mapping = this.fieldMapping[ix];
|
|
if (mapping.type === 'ignore' || !mapping.field) {
|
|
continue;
|
|
}
|
|
if (mapping.field === 'Password') {
|
|
value = kdbxweb.ProtectedValue.fromString(value);
|
|
}
|
|
newEntry.setField(mapping.field, value);
|
|
}
|
|
}
|
|
file.reload();
|
|
this.emit('done');
|
|
}
|
|
}
|
|
|
|
Object.assign(ImportCsvView.prototype, Scrollable);
|
|
|
|
export { ImportCsvView };
|