mirror of
http://git.whoc.org.uk/git/password-manager.git
synced 2024-11-14 07:49:02 +01:00
Added Import feature
This commit is contained in:
parent
e2781071d0
commit
d1d5fae5de
@ -2036,7 +2036,9 @@ span.count {
|
|||||||
font-weight: bold; }
|
font-weight: bold; }
|
||||||
#extraFeaturesPanel .extraFeatureContent {
|
#extraFeaturesPanel .extraFeatureContent {
|
||||||
border-right: 1px solid #222;
|
border-right: 1px solid #222;
|
||||||
color: white; }
|
color: white;
|
||||||
|
/* IMPORT */
|
||||||
|
/* /IMPORT */ }
|
||||||
#extraFeaturesPanel .extraFeatureContent header {
|
#extraFeaturesPanel .extraFeatureContent header {
|
||||||
display: none; }
|
display: none; }
|
||||||
#extraFeaturesPanel .extraFeatureContent .changePassphraseForm label {
|
#extraFeaturesPanel .extraFeatureContent .changePassphraseForm label {
|
||||||
@ -2053,6 +2055,22 @@ span.count {
|
|||||||
margin-bottom: 1em; }
|
margin-bottom: 1em; }
|
||||||
#extraFeaturesPanel .extraFeatureContent .deleteAccountForm .confirmCheckbox {
|
#extraFeaturesPanel .extraFeatureContent .deleteAccountForm .confirmCheckbox {
|
||||||
display: inline-block; }
|
display: inline-block; }
|
||||||
|
#extraFeaturesPanel .extraFeatureContent .importForm textarea {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 400px; }
|
||||||
|
#extraFeaturesPanel .extraFeatureContent .jsonPreview {
|
||||||
|
width: 100%;
|
||||||
|
height: 80%;
|
||||||
|
overflow: auto;
|
||||||
|
margin-top: 1em; }
|
||||||
|
#extraFeaturesPanel .extraFeatureContent .jsonPreview h3 {
|
||||||
|
font-weight: bold; }
|
||||||
|
#extraFeaturesPanel .extraFeatureContent .jsonPreview ul {
|
||||||
|
margin-bottom: 1em;
|
||||||
|
padding-left: 1em; }
|
||||||
|
#extraFeaturesPanel .extraFeatureContent .jsonPreview ul li .label {
|
||||||
|
font-weight: bold; }
|
||||||
#extraFeaturesPanel .extraFeatureContent form input.valid + .invalidMsg, #extraFeaturesPanel .extraFeatureContent form input.empty + .invalidMsg, #extraFeaturesPanel .extraFeatureContent form input:focus + .invalidMsg, #extraFeaturesPanel .extraFeatureContent form input.invalid:focus + .invalidMsg {
|
#extraFeaturesPanel .extraFeatureContent form input.valid + .invalidMsg, #extraFeaturesPanel .extraFeatureContent form input.empty + .invalidMsg, #extraFeaturesPanel .extraFeatureContent form input:focus + .invalidMsg, #extraFeaturesPanel .extraFeatureContent form input.invalid:focus + .invalidMsg {
|
||||||
visibility: hidden; }
|
visibility: hidden; }
|
||||||
#extraFeaturesPanel .extraFeatureContent form input:focus {
|
#extraFeaturesPanel .extraFeatureContent form input:focus {
|
||||||
|
File diff suppressed because one or more lines are too long
@ -163,30 +163,12 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.Record, Clipperz.PM.DataModel.Encrypt
|
|||||||
|
|
||||||
//............................................................................
|
//............................................................................
|
||||||
|
|
||||||
'tagRegExp': function () {
|
'tagRegExp': function () { return Clipperz.PM.DataModel.Record.tagRegExp(); },
|
||||||
return new RegExp('\\' + Clipperz.PM.DataModel.Record.tagChar + '(' + Clipperz.PM.DataModel.Record.specialTagChar + '?\\w+)', 'g');
|
'trimSpacesRegExp': function () { return Clipperz.PM.DataModel.Record.tagRegExp(); },
|
||||||
},
|
'filterOutTags': function (aValue) { return Clipperz.PM.DataModel.Record.filterOutTags(aValue); },
|
||||||
|
|
||||||
'trimSpacesRegExp': function () {
|
|
||||||
return new RegExp('^\\s+|\\s+$', 'g');
|
|
||||||
},
|
|
||||||
|
|
||||||
// 'tagCleanupRegExp': function () {
|
|
||||||
// return new RegExp('\\' + Clipperz.PM.DataModel.Record.tagSpace, 'g');
|
|
||||||
// },
|
|
||||||
|
|
||||||
//............................................................................
|
//............................................................................
|
||||||
|
|
||||||
'filterOutTags': function (aValue) {
|
|
||||||
var value;
|
|
||||||
|
|
||||||
value = aValue;
|
|
||||||
value = value.replace(this.tagRegExp(), '');
|
|
||||||
value = value.replace(this.trimSpacesRegExp(), '');
|
|
||||||
|
|
||||||
return value;
|
|
||||||
},
|
|
||||||
|
|
||||||
'label': function () {
|
'label': function () {
|
||||||
return Clipperz.Async.callbacks("Record.label", [
|
return Clipperz.Async.callbacks("Record.label", [
|
||||||
MochiKit.Base.method(this, 'fullLabel'),
|
MochiKit.Base.method(this, 'fullLabel'),
|
||||||
@ -1168,6 +1150,18 @@ console.log("Record.hasPendingChanges RESULT", result);
|
|||||||
MochiKit.Base.bind(function () { return this; }, this)
|
MochiKit.Base.bind(function () { return this; }, this)
|
||||||
], {trace:false});
|
], {trace:false});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
'setUpWithJSON': function(data) {
|
||||||
|
return Clipperz.Async.callbacks("Record.setUpWithJSON", [
|
||||||
|
// TODO: proper tag handling
|
||||||
|
MochiKit.Base.method(this,'setLabel',data.label),
|
||||||
|
MochiKit.Base.method(this,'setNotes',data.data.notes),
|
||||||
|
// TODO: check whether fields' order is kept or not
|
||||||
|
function(){ return MochiKit.Base.values(data.currentVersion.fields); },
|
||||||
|
MochiKit.Base.partial(MochiKit.Base.map,MochiKit.Base.method(this, 'addField')),
|
||||||
|
Clipperz.Async.collectAll
|
||||||
|
], {trace:false});
|
||||||
|
},
|
||||||
|
|
||||||
//=========================================================================
|
//=========================================================================
|
||||||
__syntaxFix__: "syntax fix"
|
__syntaxFix__: "syntax fix"
|
||||||
@ -1208,3 +1202,21 @@ Clipperz.PM.DataModel.Record.isRegularTag = function (aTag) {
|
|||||||
Clipperz.PM.DataModel.Record.regExpForSearch = function (aSearch) {
|
Clipperz.PM.DataModel.Record.regExpForSearch = function (aSearch) {
|
||||||
return new RegExp(aSearch.replace(/[^A-Za-z0-9]/g, '\\$&'), 'i');
|
return new RegExp(aSearch.replace(/[^A-Za-z0-9]/g, '\\$&'), 'i');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Clipperz.PM.DataModel.Record.tagRegExp = function () {
|
||||||
|
return new RegExp('\\' + Clipperz.PM.DataModel.Record.tagChar + '(' + Clipperz.PM.DataModel.Record.specialTagChar + '?\\w+)', 'g');
|
||||||
|
};
|
||||||
|
|
||||||
|
Clipperz.PM.DataModel.Record.trimSpacesRegExp = function () {
|
||||||
|
return new RegExp('^\\s+|\\s+$', 'g');
|
||||||
|
};
|
||||||
|
|
||||||
|
Clipperz.PM.DataModel.Record.filterOutTags = function (aValue) {
|
||||||
|
var value;
|
||||||
|
|
||||||
|
value = aValue;
|
||||||
|
value = value.replace(Clipperz.PM.DataModel.Record.tagRegExp(), '');
|
||||||
|
value = value.replace(Clipperz.PM.DataModel.Record.trimSpacesRegExp(), '');
|
||||||
|
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2008-2015 Clipperz Srl
|
||||||
|
|
||||||
|
This file is part of Clipperz, the online password manager.
|
||||||
|
For further information about its features and functionalities please
|
||||||
|
refer to http://www.clipperz.com.
|
||||||
|
|
||||||
|
* Clipperz is free software: you can redistribute it and/or modify it
|
||||||
|
under the terms of the GNU Affero General Public License as published
|
||||||
|
by the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
* Clipperz 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
* You should have received a copy of the GNU Affero General Public
|
||||||
|
License along with Clipperz. If not, see http://www.gnu.org/licenses/.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
Clipperz.Base.module('Clipperz.PM.UI.Components.ExtraFeatures.DataImport');
|
||||||
|
|
||||||
|
Clipperz.PM.UI.Components.ExtraFeatures.DataImport.CsvColumnsClass = React.createClass({
|
||||||
|
|
||||||
|
toggleColumn: function(columnN) {
|
||||||
|
var newState;
|
||||||
|
|
||||||
|
newState = {'importData': this.props.importState.importData};
|
||||||
|
newState.importData.selectedColumns[columnN] = ! newState.importData.selectedColumns[columnN];
|
||||||
|
|
||||||
|
this.props.setImportStateCallback(newState);
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
|
||||||
|
var columnSelectors;
|
||||||
|
var rowCount;
|
||||||
|
var i;
|
||||||
|
|
||||||
|
columnSelectors = [];
|
||||||
|
for (i=0; i<this.props.importState.importData.nColumns; i++) {
|
||||||
|
columnSelectors.push( React.DOM.td({'key': 'csv-colsel-'+i}, React.DOM.input({
|
||||||
|
'type': 'checkbox',
|
||||||
|
'checked': this.props.importState.importData.selectedColumns[i],
|
||||||
|
'onChange': MochiKit.Base.partial(this.toggleColumn,i)
|
||||||
|
}) ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
rowCount = 0;
|
||||||
|
|
||||||
|
return React.DOM.div({},[
|
||||||
|
React.DOM.h2({},"Columns"),
|
||||||
|
Clipperz.PM.UI.Components.ExtraFeatures.DataImport.StepsNavigation({
|
||||||
|
'format': 'csv',
|
||||||
|
'stepId': 'csv-columns',
|
||||||
|
'prevStep': 'input',
|
||||||
|
'nextStep': 'csv-labels',
|
||||||
|
'goToStepCallback': this.props.goToStepCallback,
|
||||||
|
}),
|
||||||
|
React.DOM.p({}, "Select the columns you want to import."),
|
||||||
|
React.DOM.table({'style': {'background': 'white'}},[
|
||||||
|
React.DOM.thead({}, React.DOM.tr({'className': 'columnSelectors', 'key': 'csv-colsel'}, columnSelectors)),
|
||||||
|
React.DOM.tbody({},
|
||||||
|
MochiKit.Base.map(function(row){
|
||||||
|
var cellCount;
|
||||||
|
var result
|
||||||
|
|
||||||
|
cellCount = 0;
|
||||||
|
result = React.DOM.tr({'key': 'csv-row-'+(rowCount++)}, MochiKit.Base.map(function(cell) {
|
||||||
|
return React.DOM.td({'key': 'csv-cell-'+rowCount+'-'+(cellCount++)},cell);
|
||||||
|
}, row));
|
||||||
|
rowCount++;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}, this.props.importState.importData.parsedCSV)
|
||||||
|
),
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
Clipperz.PM.UI.Components.ExtraFeatures.DataImport.CsvColumns = React.createFactory(Clipperz.PM.UI.Components.ExtraFeatures.DataImport.CsvColumnsClass);
|
@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2008-2015 Clipperz Srl
|
||||||
|
|
||||||
|
This file is part of Clipperz, the online password manager.
|
||||||
|
For further information about its features and functionalities please
|
||||||
|
refer to http://www.clipperz.com.
|
||||||
|
|
||||||
|
* Clipperz is free software: you can redistribute it and/or modify it
|
||||||
|
under the terms of the GNU Affero General Public License as published
|
||||||
|
by the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
* Clipperz 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
* You should have received a copy of the GNU Affero General Public
|
||||||
|
License along with Clipperz. If not, see http://www.gnu.org/licenses/.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
Clipperz.Base.module('Clipperz.PM.UI.Components.ExtraFeatures.DataImport');
|
||||||
|
|
||||||
|
Clipperz.PM.UI.Components.ExtraFeatures.DataImport.CsvHiddenClass = React.createClass({
|
||||||
|
|
||||||
|
checkedCallback: function(columnN) {
|
||||||
|
return this.props.importState.importData.hiddenColumns[columnN];
|
||||||
|
},
|
||||||
|
|
||||||
|
onChangeCallback: function(columnN) {
|
||||||
|
var newState = {'importData': this.props.importState.importData};
|
||||||
|
|
||||||
|
newState.importData.hiddenColumns[columnN] = ! newState.importData.hiddenColumns[columnN];
|
||||||
|
|
||||||
|
this.setState(newState);
|
||||||
|
},
|
||||||
|
|
||||||
|
disabledCallback: function(columnN) {
|
||||||
|
return (columnN == this.props.importState.importData.titlesColumn || columnN == this.props.importState.importData.notesColumn)
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
var importData = this.props.importState.importData;
|
||||||
|
|
||||||
|
return React.DOM.div({},[
|
||||||
|
React.DOM.h2({},"Hidden"),
|
||||||
|
|
||||||
|
Clipperz.PM.UI.Components.ExtraFeatures.DataImport.StepsNavigation({
|
||||||
|
'format': 'csv',
|
||||||
|
'stepId': 'csv-hidden'
|
||||||
|
}),
|
||||||
|
React.DOM.button({'onClick': MochiKit.Base.partial(this.props.goToStepCallback, 'csv-notes') }, "Back"),
|
||||||
|
React.DOM.span({}, " - "),
|
||||||
|
React.DOM.button({'onClick': MochiKit.Base.bind(function() {
|
||||||
|
var importData = this.props.importState.importData;
|
||||||
|
var json = this.props.csvToJsonCallback();
|
||||||
|
this.props.setImportStateCallback({
|
||||||
|
'importData': importData,
|
||||||
|
'jsonToImport': json,
|
||||||
|
'recordsToImport': MochiKit.Base.map(function(r){return r._importId},json),
|
||||||
|
'currentStep': 'preview',
|
||||||
|
'previousStep': 'csv-hidden'
|
||||||
|
});
|
||||||
|
}, this) }, "Preview"),
|
||||||
|
React.DOM.p({}, "Select the fields that should be hidden. (passwords, PINs, ...)"),
|
||||||
|
React.DOM.table({'style': {'background': 'white'}},[
|
||||||
|
React.DOM.thead({},
|
||||||
|
this.props.csvRenderTheadInputCallback('hidden', 'checkbox', this.checkedCallback, this.onChangeCallback, this.disabledCallback, true)
|
||||||
|
),
|
||||||
|
React.DOM.tbody({},
|
||||||
|
this.props.csvRenderTbodyCallback()
|
||||||
|
)
|
||||||
|
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
Clipperz.PM.UI.Components.ExtraFeatures.DataImport.CsvHidden = React.createFactory(Clipperz.PM.UI.Components.ExtraFeatures.DataImport.CsvHiddenClass);
|
@ -0,0 +1,115 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2008-2015 Clipperz Srl
|
||||||
|
|
||||||
|
This file is part of Clipperz, the online password manager.
|
||||||
|
For further information about its features and functionalities please
|
||||||
|
refer to http://www.clipperz.com.
|
||||||
|
|
||||||
|
* Clipperz is free software: you can redistribute it and/or modify it
|
||||||
|
under the terms of the GNU Affero General Public License as published
|
||||||
|
by the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
* Clipperz 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
* You should have received a copy of the GNU Affero General Public
|
||||||
|
License along with Clipperz. If not, see http://www.gnu.org/licenses/.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
Clipperz.Base.module('Clipperz.PM.UI.Components.ExtraFeatures.DataImport');
|
||||||
|
|
||||||
|
Clipperz.PM.UI.Components.ExtraFeatures.DataImport.CsvLabelsClass = React.createClass({
|
||||||
|
|
||||||
|
toggleFirstRow: function() {
|
||||||
|
var newState;
|
||||||
|
var cellCount;
|
||||||
|
|
||||||
|
newState = {'importData': this.props.importState.importData};
|
||||||
|
newState.importData.firstRowAsLabels = ! newState.importData.firstRowAsLabels;
|
||||||
|
|
||||||
|
cellCount = 0;
|
||||||
|
MochiKit.Base.map(function(cell){
|
||||||
|
newState.importData.columnLabelsFirstrow[cellCount++] = cell;
|
||||||
|
}, this.props.importState.importData.parsedCSV[0]);
|
||||||
|
|
||||||
|
this.props.setImportStateCallback(newState);
|
||||||
|
},
|
||||||
|
|
||||||
|
isNextDisabled: function() {
|
||||||
|
var result;
|
||||||
|
|
||||||
|
var importData = this.props.importState.importData;
|
||||||
|
|
||||||
|
var columnLabels = (importData.firstRowAsLabels) ? importData.columnLabelsFirstrow : importData.columnLabels;
|
||||||
|
|
||||||
|
result = false;
|
||||||
|
for (i in columnLabels) {
|
||||||
|
result = result || (columnLabels[i] == '');
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
|
||||||
|
valueCallback: function(columnN) {
|
||||||
|
var columnLabels = this.props.csvGetColumnLabelsCallback();
|
||||||
|
return columnLabels[columnN];
|
||||||
|
},
|
||||||
|
|
||||||
|
onChangeCallback: function(columnN) {
|
||||||
|
var newState;
|
||||||
|
|
||||||
|
newState = {'importData': this.props.importState.importData};
|
||||||
|
if (this.props.importState.importData.firstRowAsLabels) {
|
||||||
|
newState.importData.columnLabelsFirstrow[columnN] = this.refs['csv-labels-input-'+columnN].getDOMNode().value;
|
||||||
|
} else {
|
||||||
|
newState.importData.columnLabels[columnN] = this.refs['csv-labels-input-'+columnN].getDOMNode().value;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.props.setImportStateCallback(newState);
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
|
||||||
|
var importData = this.props.importState.importData;
|
||||||
|
|
||||||
|
return React.DOM.div({},[
|
||||||
|
React.DOM.h2({},"Labels"),
|
||||||
|
|
||||||
|
Clipperz.PM.UI.Components.ExtraFeatures.DataImport.StepsNavigation({
|
||||||
|
'format': 'csv',
|
||||||
|
'stepId': 'csv-labels',
|
||||||
|
'prevStep': 'csv-columns',
|
||||||
|
'nextStep': 'csv-titles',
|
||||||
|
'goToStepCallback': this.props.goToStepCallback,
|
||||||
|
'nextDisabled': this.isNextDisabled()
|
||||||
|
}),
|
||||||
|
|
||||||
|
React.DOM.p({}, "Set a label for each field in your data. If the first row of the CSV file contains field labels, tick off the checkbox below."),
|
||||||
|
React.DOM.input({
|
||||||
|
'id': 'csv-labels-firstrow',
|
||||||
|
'type': 'checkbox',
|
||||||
|
'checked': this.props.importState.importData.firstRowAsLabels,
|
||||||
|
'onChange': this.toggleFirstRow
|
||||||
|
}),
|
||||||
|
React.DOM.label({'htmlFor':'csv-labels-firstrow'}, "Use the first row as labels"),
|
||||||
|
React.DOM.table({'style': {'background': 'white'}},[
|
||||||
|
React.DOM.thead({},
|
||||||
|
this.props.csvRenderTheadInputCallback('labels', 'text', this.valueCallback, this.onChangeCallback, null, false)
|
||||||
|
),
|
||||||
|
React.DOM.tbody({},
|
||||||
|
this.props.csvRenderTbodyCallback()
|
||||||
|
)
|
||||||
|
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
Clipperz.PM.UI.Components.ExtraFeatures.DataImport.CsvLabels = React.createFactory(Clipperz.PM.UI.Components.ExtraFeatures.DataImport.CsvLabelsClass);
|
@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2008-2015 Clipperz Srl
|
||||||
|
|
||||||
|
This file is part of Clipperz, the online password manager.
|
||||||
|
For further information about its features and functionalities please
|
||||||
|
refer to http://www.clipperz.com.
|
||||||
|
|
||||||
|
* Clipperz is free software: you can redistribute it and/or modify it
|
||||||
|
under the terms of the GNU Affero General Public License as published
|
||||||
|
by the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
* Clipperz 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
* You should have received a copy of the GNU Affero General Public
|
||||||
|
License along with Clipperz. If not, see http://www.gnu.org/licenses/.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
Clipperz.Base.module('Clipperz.PM.UI.Components.ExtraFeatures.DataImport');
|
||||||
|
|
||||||
|
Clipperz.PM.UI.Components.ExtraFeatures.DataImport.CsvNotesClass = React.createClass({
|
||||||
|
|
||||||
|
checkedCallback: function(columnN) {
|
||||||
|
return columnN == this.props.importState.importData.notesColumn;
|
||||||
|
},
|
||||||
|
|
||||||
|
onChangeCallback: function(columnN) {
|
||||||
|
var newState = {'importData': this.props.importState.importData};
|
||||||
|
|
||||||
|
newState.importData.notesColumn = columnN;
|
||||||
|
|
||||||
|
this.setState(newState);
|
||||||
|
},
|
||||||
|
|
||||||
|
disabledCallback: function(columnN) {
|
||||||
|
return columnN == this.props.importState.importData.titlesColumn;
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
return React.DOM.div({},[
|
||||||
|
React.DOM.h2({},"Notes"),
|
||||||
|
|
||||||
|
Clipperz.PM.UI.Components.ExtraFeatures.DataImport.StepsNavigation({
|
||||||
|
'format': 'csv',
|
||||||
|
'stepId': 'csv-notes',
|
||||||
|
'prevStep': 'csv-titles',
|
||||||
|
'nextStep': 'csv-hidden',
|
||||||
|
'goToStepCallback': this.props.goToStepCallback
|
||||||
|
}),
|
||||||
|
|
||||||
|
React.DOM.p({}, "Select the column that represents a \"notes\" field. (optional)"),
|
||||||
|
React.DOM.input({
|
||||||
|
'id': 'csv-notes-nonotes',
|
||||||
|
'type': 'radio',
|
||||||
|
'checked': ! this.props.importState.importData.notesColumn,
|
||||||
|
'onChange': MochiKit.Base.partial(this.onChangeCallback, null)
|
||||||
|
}),
|
||||||
|
React.DOM.label({'htmlFor': 'csv-notes-nonotes'}, "\"notes\" field not present"),
|
||||||
|
React.DOM.table({'style': {'background': 'white'}},[
|
||||||
|
React.DOM.thead({},
|
||||||
|
this.props.csvRenderTheadInputCallback('notes', 'radio', this.checkedCallback, this.onChangeCallback, this.disabledCallback, true)
|
||||||
|
),
|
||||||
|
React.DOM.tbody({},
|
||||||
|
this.props.csvRenderTbodyCallback()
|
||||||
|
)
|
||||||
|
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
Clipperz.PM.UI.Components.ExtraFeatures.DataImport.CsvNotes = React.createFactory(Clipperz.PM.UI.Components.ExtraFeatures.DataImport.CsvNotesClass);
|
@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2008-2015 Clipperz Srl
|
||||||
|
|
||||||
|
This file is part of Clipperz, the online password manager.
|
||||||
|
For further information about its features and functionalities please
|
||||||
|
refer to http://www.clipperz.com.
|
||||||
|
|
||||||
|
* Clipperz is free software: you can redistribute it and/or modify it
|
||||||
|
under the terms of the GNU Affero General Public License as published
|
||||||
|
by the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
* Clipperz 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
* You should have received a copy of the GNU Affero General Public
|
||||||
|
License along with Clipperz. If not, see http://www.gnu.org/licenses/.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
Clipperz.Base.module('Clipperz.PM.UI.Components.ExtraFeatures.DataImport');
|
||||||
|
|
||||||
|
Clipperz.PM.UI.Components.ExtraFeatures.DataImport.CsvTitlesClass = React.createClass({
|
||||||
|
|
||||||
|
checkedCallback: function(columnN) {
|
||||||
|
return columnN == this.props.importState.importData.titlesColumn;
|
||||||
|
},
|
||||||
|
|
||||||
|
onChangeCallback: function(columnN) {
|
||||||
|
var newState = {'importData': this.props.importState.importData};
|
||||||
|
|
||||||
|
if (this.props.importState.importData.notesColumn == columnN) {
|
||||||
|
newState.importData.notesColumn = null;
|
||||||
|
}
|
||||||
|
newState.importData.titlesColumn = columnN;
|
||||||
|
|
||||||
|
this.props.setImportStateCallback(newState);
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
|
||||||
|
var importData = this.props.importState.importData;
|
||||||
|
|
||||||
|
return React.DOM.div({},[
|
||||||
|
React.DOM.h2({},"Titles"),
|
||||||
|
|
||||||
|
|
||||||
|
Clipperz.PM.UI.Components.ExtraFeatures.DataImport.StepsNavigation({
|
||||||
|
'format': 'csv',
|
||||||
|
'stepId': 'csv-titles',
|
||||||
|
'prevStep': 'csv-labels',
|
||||||
|
'nextStep': 'csv-notes',
|
||||||
|
'goToStepCallback': this.props.goToStepCallback,
|
||||||
|
'nextDisabled': (importData.titlesColumn != 0 && ! importData.titlesColumn )
|
||||||
|
}),
|
||||||
|
|
||||||
|
React.DOM.p({}, "Select the column that contains titles of the cards you are importing. (mandatory)"),
|
||||||
|
React.DOM.table({'style': {'background': 'white'}},[
|
||||||
|
React.DOM.thead({},
|
||||||
|
this.props.csvRenderTheadInputCallback('titles', 'radio', this.checkedCallback, this.onChangeCallback, null, true)
|
||||||
|
),
|
||||||
|
React.DOM.tbody({},
|
||||||
|
this.props.csvRenderTbodyCallback()
|
||||||
|
)
|
||||||
|
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
Clipperz.PM.UI.Components.ExtraFeatures.DataImport.CsvTitles = React.createFactory(Clipperz.PM.UI.Components.ExtraFeatures.DataImport.CsvTitlesClass);
|
@ -0,0 +1,140 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2008-2015 Clipperz Srl
|
||||||
|
|
||||||
|
This file is part of Clipperz, the online password manager.
|
||||||
|
For further information about its features and functionalities please
|
||||||
|
refer to http://www.clipperz.com.
|
||||||
|
|
||||||
|
* Clipperz is free software: you can redistribute it and/or modify it
|
||||||
|
under the terms of the GNU Affero General Public License as published
|
||||||
|
by the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
* Clipperz 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
* You should have received a copy of the GNU Affero General Public
|
||||||
|
License along with Clipperz. If not, see http://www.gnu.org/licenses/.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
Clipperz.Base.module('Clipperz.PM.UI.Components.ExtraFeatures.DataImport');
|
||||||
|
|
||||||
|
Clipperz.PM.UI.Components.ExtraFeatures.DataImport.InputClass = React.createClass({
|
||||||
|
handleUploadFiles: function(someFiles) {
|
||||||
|
var file;
|
||||||
|
var reader;
|
||||||
|
|
||||||
|
if (someFiles.length == 1) {
|
||||||
|
file = someFiles[0];
|
||||||
|
reader = new FileReader();
|
||||||
|
|
||||||
|
// TODO: check what happens with binary files
|
||||||
|
reader.onloadend = MochiKit.Base.bind(function() {
|
||||||
|
var extractedJson = this.props.extractJsonFromClipperzExportCallback(reader.result);
|
||||||
|
|
||||||
|
if (extractedJson) {
|
||||||
|
this.props.setImportStateCallback({'importData': {'input': extractedJson}});
|
||||||
|
} else {
|
||||||
|
this.props.setImportStateCallback({'importData': {'input': reader.result}});
|
||||||
|
}
|
||||||
|
},this,reader);
|
||||||
|
|
||||||
|
reader.readAsText(file);
|
||||||
|
} else {
|
||||||
|
alert("Error: expecting a file as input.");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
handleOnDrop: function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
this.handleUploadFiles(e.dataTransfer.files)
|
||||||
|
},
|
||||||
|
|
||||||
|
handleInputFiles: function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
this.handleUploadFiles(e.target.files)
|
||||||
|
},
|
||||||
|
|
||||||
|
handleOnDragOver: function(e) {
|
||||||
|
// Somehow necessary:
|
||||||
|
// http://enome.github.io/javascript/2014/03/24/drag-and-drop-with-react-js.html
|
||||||
|
// https://code.google.com/p/chromium/issues/detail?id=168387
|
||||||
|
// http://www.quirksmode.org/blog/archives/2009/09/the_html5_drag.html
|
||||||
|
e.preventDefault();
|
||||||
|
},
|
||||||
|
|
||||||
|
handleSubmit: function(event) {
|
||||||
|
var jsonData;
|
||||||
|
var newState;
|
||||||
|
|
||||||
|
var inputString = this.refs['input-textarea'].getDOMNode().value.trim();
|
||||||
|
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
if (newState = this.props.parseJsonCallback(inputString)) {
|
||||||
|
this.props.setImportStateCallback(newState);
|
||||||
|
} else if (newState = this.props.parseCsvCallback(inputString)) {
|
||||||
|
this.props.setImportStateCallback(newState);
|
||||||
|
} else {
|
||||||
|
alert("Unrecognized input format...");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
handleTextareaChange: function() {
|
||||||
|
this.props.setImportStateCallback({'importData': {'input': this.refs['input-textarea'].getDOMNode().value}});
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
return React.DOM.div({},[
|
||||||
|
React.DOM.h2({},"Input"),
|
||||||
|
Clipperz.PM.UI.Components.ExtraFeatures.DataImport.StepsNavigation({
|
||||||
|
'format': 'json',
|
||||||
|
'stepId': 'input'
|
||||||
|
}),
|
||||||
|
React.DOM.form({'key':'form', 'className':'importForm', 'onSubmit': this.handleSubmit }, [
|
||||||
|
React.DOM.button({'key':'input-next', 'type':'submit', 'className':'button'}, "Next"),
|
||||||
|
React.DOM.input({
|
||||||
|
'type': 'file',
|
||||||
|
'ref': 'upload-input',
|
||||||
|
'onClick': function(e) { e.target.value = null },
|
||||||
|
'onChange': this.handleInputFiles,
|
||||||
|
'style': {'display': 'none'}
|
||||||
|
}),
|
||||||
|
React.DOM.div({
|
||||||
|
'style': { // TODO: replace with proper CSS
|
||||||
|
'width': '90%',
|
||||||
|
'textAlign': 'center',
|
||||||
|
'lineHeight': '3em',
|
||||||
|
'border': '3px dashed white'
|
||||||
|
},
|
||||||
|
'onDragOver': this.handleOnDragOver,
|
||||||
|
'onDrop': this.handleOnDrop,
|
||||||
|
'onClick': MochiKit.Base.bind(function() { this.refs['upload-input'].getDOMNode().click() }, this)
|
||||||
|
}, "Drag your Clipperz export file here or click select it manually."),
|
||||||
|
React.DOM.p({}, "or"),
|
||||||
|
React.DOM.div({'key':'fields'},[
|
||||||
|
React.DOM.textarea({
|
||||||
|
'key':'input-textarea',
|
||||||
|
'name':'input-textarea',
|
||||||
|
'ref':'input-textarea',
|
||||||
|
'placeholder':"Open the JSON file exported from Clipperz in a text editor. Then copy and paste its content here.",
|
||||||
|
'value': this.props.importState.importData.input,
|
||||||
|
'onChange': this.handleTextareaChange,
|
||||||
|
'onDragOver': this.handleOnDragOver,
|
||||||
|
'onDrop': this.handleOnDrop,
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
Clipperz.PM.UI.Components.ExtraFeatures.DataImport.Input = React.createFactory(Clipperz.PM.UI.Components.ExtraFeatures.DataImport.InputClass);
|
@ -0,0 +1,122 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2008-2015 Clipperz Srl
|
||||||
|
|
||||||
|
This file is part of Clipperz, the online password manager.
|
||||||
|
For further information about its features and functionalities please
|
||||||
|
refer to http://www.clipperz.com.
|
||||||
|
|
||||||
|
* Clipperz is free software: you can redistribute it and/or modify it
|
||||||
|
under the terms of the GNU Affero General Public License as published
|
||||||
|
by the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
* Clipperz 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
* You should have received a copy of the GNU Affero General Public
|
||||||
|
License along with Clipperz. If not, see http://www.gnu.org/licenses/.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
Clipperz.Base.module('Clipperz.PM.UI.Components.ExtraFeatures.DataImport');
|
||||||
|
|
||||||
|
Clipperz.PM.UI.Components.ExtraFeatures.DataImport.PreviewClass = React.createClass({
|
||||||
|
|
||||||
|
// UNCOMMENT AFTER MERGE (uses methods in Record that were added in another branch)
|
||||||
|
// getTags: function (aTitle) {
|
||||||
|
// var result;
|
||||||
|
// var tagList;
|
||||||
|
//
|
||||||
|
// var tagObject = Clipperz.PM.DataModel.Record.extractTagsFromFullLabel(aTitle);
|
||||||
|
//
|
||||||
|
// tagList = MochiKit.Base.keys(tagObject);
|
||||||
|
// tagList = MochiKit.Base.filter(function(aTag) { return tagObject[aTag] }, tagList);
|
||||||
|
//
|
||||||
|
// if (tagList.length > 0) {
|
||||||
|
// result = React.DOM.ul({'className': 'tagList'},
|
||||||
|
// MochiKit.Base.map(function(aTag){
|
||||||
|
// return React.DOM.li({}, aTag);
|
||||||
|
// }, tagList)
|
||||||
|
// );
|
||||||
|
// } else {
|
||||||
|
// result = null;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return result;
|
||||||
|
// },
|
||||||
|
|
||||||
|
renderCardFields: function(someFields) {
|
||||||
|
return MochiKit.Base.map(function(key) {
|
||||||
|
var field = someFields[key];
|
||||||
|
|
||||||
|
return [
|
||||||
|
React.DOM.dt({},field.label),
|
||||||
|
React.DOM.dd({},field.value),
|
||||||
|
];
|
||||||
|
} ,MochiKit.Base.keys(someFields));
|
||||||
|
},
|
||||||
|
|
||||||
|
renderCard: function(aCard) {
|
||||||
|
var notesParagraph = (aCard.data.notes) ? React.DOM.p({'className': 'notes'}, aCard.data.notes) : null;
|
||||||
|
return React.DOM.li({'className': 'card'}, [
|
||||||
|
React.DOM.input({
|
||||||
|
'type': 'checkbox',
|
||||||
|
'checked': this.props.isRecordToImportCallback(aCard),
|
||||||
|
'onChange': MochiKit.Base.partial(this.props.toggleRecordToImportCallback,aCard)
|
||||||
|
}),
|
||||||
|
React.DOM.h3({}, Clipperz.PM.DataModel.Record.filterOutTags(aCard.label)),
|
||||||
|
// REMOVE THE PREVIOUS LINE AND UNCOMMENT THE FOLLOWING 2 AFTER MERGE
|
||||||
|
// React.DOM.h3({}, Clipperz.PM.DataModel.Record.extractLabelFromFullLabel(aCard.label)),
|
||||||
|
// this.getTags(aCard.label),
|
||||||
|
React.DOM.dl({'className': 'fields'}, this.renderCardFields(aCard.currentVersion.fields)),
|
||||||
|
notesParagraph
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
var result;
|
||||||
|
|
||||||
|
if (! this.props.importState.importData || typeof(this.props.importState.jsonToImport)=='undefined' || !this.props.importState.jsonToImport) {
|
||||||
|
result = "Error";
|
||||||
|
} else {
|
||||||
|
var renderedPreview = React.DOM.ul({},
|
||||||
|
MochiKit.Base.map(this.renderCard, this.props.importState.jsonToImport)
|
||||||
|
);
|
||||||
|
|
||||||
|
result = [
|
||||||
|
React.DOM.h2({},"Preview"),
|
||||||
|
Clipperz.PM.UI.Components.ExtraFeatures.DataImport.StepsNavigation({
|
||||||
|
'format': this.props.importState.importData.format,
|
||||||
|
'stepId': 'preview'
|
||||||
|
}),
|
||||||
|
React.DOM.button({
|
||||||
|
'onClick': MochiKit.Base.partial(this.props.goToStepCallback, this.props.importState.previousStep)}, "Back"),
|
||||||
|
React.DOM.span({}, " - "),
|
||||||
|
React.DOM.button({
|
||||||
|
'onClick': MochiKit.Base.bind(function() {
|
||||||
|
var filteredImportData = MochiKit.Base.filter(
|
||||||
|
MochiKit.Base.bind(function(r) {
|
||||||
|
return this.props.isRecordToImportCallback(r);
|
||||||
|
}, this),
|
||||||
|
this.props.importState.jsonToImport
|
||||||
|
);
|
||||||
|
|
||||||
|
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'importCards', filteredImportData);
|
||||||
|
|
||||||
|
this.props.resetImportStateCallback();
|
||||||
|
}, this)
|
||||||
|
}, "Import"),
|
||||||
|
React.DOM.div({'className': 'jsonPreview'},renderedPreview),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return React.DOM.div({},result);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
Clipperz.PM.UI.Components.ExtraFeatures.DataImport.Preview = React.createFactory(Clipperz.PM.UI.Components.ExtraFeatures.DataImport.PreviewClass);
|
@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2008-2015 Clipperz Srl
|
||||||
|
|
||||||
|
This file is part of Clipperz, the online password manager.
|
||||||
|
For further information about its features and functionalities please
|
||||||
|
refer to http://www.clipperz.com.
|
||||||
|
|
||||||
|
* Clipperz is free software: you can redistribute it and/or modify it
|
||||||
|
under the terms of the GNU Affero General Public License as published
|
||||||
|
by the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
* Clipperz 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
* You should have received a copy of the GNU Affero General Public
|
||||||
|
License along with Clipperz. If not, see http://www.gnu.org/licenses/.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
Clipperz.Base.module('Clipperz.PM.UI.Components.ExtraFeatures.DataImport');
|
||||||
|
|
||||||
|
Clipperz.PM.UI.Components.ExtraFeatures.DataImport.StepsNavigationClass = React.createClass({
|
||||||
|
|
||||||
|
_stepsInfo: [
|
||||||
|
{
|
||||||
|
id: 'input',
|
||||||
|
name: 'Input',
|
||||||
|
formats: ['json', 'csv']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'csv-columns',
|
||||||
|
name: 'Columns',
|
||||||
|
formats: ['csv']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'csv-labels',
|
||||||
|
name: 'Labels',
|
||||||
|
formats: ['csv']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'csv-titles',
|
||||||
|
name: 'Titles',
|
||||||
|
formats: ['csv']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'csv-notes',
|
||||||
|
name: 'Notes',
|
||||||
|
formats: ['csv']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'csv-hidden',
|
||||||
|
name: 'Hidden',
|
||||||
|
formats: ['csv']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'preview',
|
||||||
|
name: 'Preview',
|
||||||
|
formats: ['json', 'csv']
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
var navigationButtons;
|
||||||
|
|
||||||
|
if (this.props.prevStep && this.props.nextStep) {
|
||||||
|
navigationButtons = [
|
||||||
|
React.DOM.button({'onClick': MochiKit.Base.partial(this.props.goToStepCallback, this.props.prevStep)}, "Back"),
|
||||||
|
React.DOM.span({}, " - "),
|
||||||
|
React.DOM.button({'onClick': MochiKit.Base.partial(this.props.goToStepCallback, this.props.nextStep), 'disabled': this.props.nextDisabled }, "Next")
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return React.DOM.div({},[
|
||||||
|
React.DOM.ul({'className': 'stepsOverview'},
|
||||||
|
MochiKit.Base.map(MochiKit.Base.bind(function(aStep) {
|
||||||
|
var className;
|
||||||
|
|
||||||
|
className = (aStep.id == this.props.stepId) ? 'active' : 'inactive';
|
||||||
|
className = (MochiKit.Base.findValue(aStep.formats,this.props.format)>= 0) ? className+' enabled' : className+' disabled';
|
||||||
|
|
||||||
|
// TODO: replace with proper CSS
|
||||||
|
var style = (aStep.id == this.props.stepId) ? {'display': 'inline-block', 'textDecoration': 'underline'} : {'display': 'inline-block'};
|
||||||
|
return React.DOM.li({'className': className, 'style': style}, aStep.name);
|
||||||
|
},this), this._stepsInfo)
|
||||||
|
),
|
||||||
|
navigationButtons
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
Clipperz.PM.UI.Components.ExtraFeatures.DataImport.StepsNavigation = React.createFactory(Clipperz.PM.UI.Components.ExtraFeatures.DataImport.StepsNavigationClass);
|
@ -36,7 +36,6 @@ Clipperz.PM.UI.Components.ExtraFeatures.DeleteAccountClass = React.createClass({
|
|||||||
'username': 'empty',
|
'username': 'empty',
|
||||||
'passphrase': 'empty',
|
'passphrase': 'empty',
|
||||||
'confirm': '',
|
'confirm': '',
|
||||||
//~ 'error': ''
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -45,35 +44,10 @@ Clipperz.PM.UI.Components.ExtraFeatures.DeleteAccountClass = React.createClass({
|
|||||||
handleDeleteAccount: function(event) {
|
handleDeleteAccount: function(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
//~ if (this.refs['username'].getDOMNode().value != this.props.userInfo['username']) {
|
|
||||||
//~ this.setState({error: "Invalid username"});
|
|
||||||
//~ return;
|
|
||||||
//~ }
|
|
||||||
//~
|
|
||||||
//~ var deferredResult;
|
|
||||||
//~
|
|
||||||
//~ deferredResult = new Clipperz.Async.Deferred("DeleteAccount.handleDeleteAccount", {trace: false});
|
|
||||||
//~ deferredResult.addCallback(this.props.userInfo['checkPassphraseCallback'], this.refs['passphrase'].getDOMNode().value);
|
|
||||||
//~ deferredResult.addIf(
|
|
||||||
//~ [MochiKit.Base.partial(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'deleteAccount')],
|
|
||||||
//~ [MochiKit.Base.bind(this.setState, this, {error: "Invalid password"})]
|
|
||||||
//~ );
|
|
||||||
//~
|
|
||||||
//~ deferredResult.callback();
|
|
||||||
//~
|
|
||||||
//~ return deferredResult;
|
|
||||||
|
|
||||||
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'deleteAccount');
|
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'deleteAccount');
|
||||||
},
|
},
|
||||||
|
|
||||||
handleFormChange: function() {
|
handleFormChange: function() {
|
||||||
|
|
||||||
|
|
||||||
//~ if (this.refs['username'].getDOMNode().value != this.props.userInfo['username']) {
|
|
||||||
//~ this.setState({error: "Invalid username"});
|
|
||||||
//~ return;
|
|
||||||
//~ }
|
|
||||||
|
|
||||||
var deferredResult;
|
var deferredResult;
|
||||||
|
|
||||||
deferredResult = new Clipperz.Async.Deferred("DeleteAccount.handleDeleteAccount", {trace: false});
|
deferredResult = new Clipperz.Async.Deferred("DeleteAccount.handleDeleteAccount", {trace: false});
|
||||||
@ -92,14 +66,6 @@ Clipperz.PM.UI.Components.ExtraFeatures.DeleteAccountClass = React.createClass({
|
|||||||
deferredResult.callback();
|
deferredResult.callback();
|
||||||
|
|
||||||
return deferredResult;
|
return deferredResult;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//~ this.setState({
|
|
||||||
//~ 'username': this.refs['username'].getDOMNode().value,
|
|
||||||
//~ 'passphrase': this.refs['passphrase'].getDOMNode().value,
|
|
||||||
//~ 'confirm': this.refs['confirm'].getDOMNode().checked,
|
|
||||||
//~ });
|
|
||||||
},
|
},
|
||||||
|
|
||||||
shouldEnableDeleteAccountButton: function() {
|
shouldEnableDeleteAccountButton: function() {
|
||||||
@ -109,8 +75,6 @@ Clipperz.PM.UI.Components.ExtraFeatures.DeleteAccountClass = React.createClass({
|
|||||||
//=========================================================================
|
//=========================================================================
|
||||||
|
|
||||||
render: function () {
|
render: function () {
|
||||||
//~ var errorVisibility = (this.state.error) ? 'visible' : 'hidden';
|
|
||||||
|
|
||||||
return React.DOM.div({className:'extraFeature deleteAccount'}, [
|
return React.DOM.div({className:'extraFeature deleteAccount'}, [
|
||||||
React.DOM.h1({}, "Delete Account"),
|
React.DOM.h1({}, "Delete Account"),
|
||||||
React.DOM.form({'key':'form', 'className':'deleteAccountForm', 'onChange': this.handleFormChange, 'onSubmit':this.handleDeleteAccount}, [
|
React.DOM.form({'key':'form', 'className':'deleteAccountForm', 'onChange': this.handleFormChange, 'onSubmit':this.handleDeleteAccount}, [
|
||||||
@ -127,7 +91,6 @@ Clipperz.PM.UI.Components.ExtraFeatures.DeleteAccountClass = React.createClass({
|
|||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
React.DOM.button({'key':'button', 'type':'submit', 'disabled':!this.shouldEnableDeleteAccountButton(), 'className':'button'}, "Delete my account")
|
React.DOM.button({'key':'button', 'type':'submit', 'disabled':!this.shouldEnableDeleteAccountButton(), 'className':'button'}, "Delete my account")
|
||||||
//~ React.DOM.div({ref: 'errorMessage', className: 'errorMessage', style: {visibility: errorVisibility} }, this.state.error)
|
|
||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
|
@ -0,0 +1,409 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2008-2015 Clipperz Srl
|
||||||
|
|
||||||
|
This file is part of Clipperz, the online password manager.
|
||||||
|
For further information about its features and functionalities please
|
||||||
|
refer to http://www.clipperz.com.
|
||||||
|
|
||||||
|
* Clipperz is free software: you can redistribute it and/or modify it
|
||||||
|
under the terms of the GNU Affero General Public License as published
|
||||||
|
by the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
* Clipperz 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
* You should have received a copy of the GNU Affero General Public
|
||||||
|
License along with Clipperz. If not, see http://www.gnu.org/licenses/.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
Clipperz.Base.module('Clipperz.PM.UI.Components.ExtraFeatures');
|
||||||
|
|
||||||
|
Clipperz.PM.UI.Components.ExtraFeatures.DataImportClass = React.createClass({
|
||||||
|
|
||||||
|
getInitialState: function() {
|
||||||
|
return {
|
||||||
|
'currentStep': 'input',
|
||||||
|
'importData': {'input': ""},
|
||||||
|
'recordsToImport': null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
goToStep: function(aStep) {
|
||||||
|
this.setState({'currentStep': aStep});
|
||||||
|
},
|
||||||
|
|
||||||
|
resetState: function() {
|
||||||
|
this.replaceState( this.getInitialState() );
|
||||||
|
},
|
||||||
|
|
||||||
|
//=========================================================================
|
||||||
|
|
||||||
|
addImportIds: function (someJson) {
|
||||||
|
var count;
|
||||||
|
|
||||||
|
for (count=0; count < someJson.length; count++) {
|
||||||
|
someJson[count]['_importId'] = count;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
toggleRecordToImport: function(record) {
|
||||||
|
var newRecordsToImport;
|
||||||
|
var recordPosition;
|
||||||
|
|
||||||
|
newRecordsToImport = this.state.recordsToImport;
|
||||||
|
recordPosition = newRecordsToImport.indexOf(record._importId);
|
||||||
|
|
||||||
|
if (recordPosition === -1) {
|
||||||
|
newRecordsToImport.push(record._importId);
|
||||||
|
} else {
|
||||||
|
newRecordsToImport.splice(recordPosition,1);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({'recordsToImport': newRecordsToImport});
|
||||||
|
},
|
||||||
|
|
||||||
|
isRecordToImport: function(record) {
|
||||||
|
return (this.state.recordsToImport.indexOf(record._importId)>=0) ? true : false;
|
||||||
|
},
|
||||||
|
|
||||||
|
extractJsonFromClipperzExport: function(someHtml) {
|
||||||
|
var temporaryTextarea;
|
||||||
|
var regexMatch;
|
||||||
|
var result;
|
||||||
|
|
||||||
|
// Should move the regex to global?
|
||||||
|
var re = new RegExp('.*<textarea>(.*)<\/textarea>.*','g');
|
||||||
|
|
||||||
|
if (re.test(someHtml)) {
|
||||||
|
// Needed to escape HTML entities
|
||||||
|
temporaryTextarea = document.createElement('textarea');
|
||||||
|
temporaryTextarea.innerHTML = someHtml.replace(re, '$1');
|
||||||
|
result = temporaryTextarea.innerHTML;
|
||||||
|
} else {
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
|
||||||
|
parseJson: function(aJsonString) {
|
||||||
|
var result;
|
||||||
|
var jsonData;
|
||||||
|
|
||||||
|
try {
|
||||||
|
jsonData = JSON.parse(aJsonString);
|
||||||
|
this.addImportIds(jsonData);
|
||||||
|
result = {
|
||||||
|
'importData': {
|
||||||
|
'format': 'json',
|
||||||
|
'input': aJsonString,
|
||||||
|
},
|
||||||
|
'jsonToImport': jsonData,
|
||||||
|
'recordsToImport': jsonData.map(function(d){return d._importId}),
|
||||||
|
'currentStep': 'preview',
|
||||||
|
'previousStep': 'input'
|
||||||
|
};
|
||||||
|
} catch(e) {
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
|
||||||
|
parseCsv: function(aCsvString) {
|
||||||
|
var result;
|
||||||
|
var parsedCSV;
|
||||||
|
var nColumns;
|
||||||
|
var defaultSelectedColumns;
|
||||||
|
var defaultHiddenColumns;
|
||||||
|
var defaultColumnLabels;
|
||||||
|
var columnLabelsFirstrow;
|
||||||
|
var i;
|
||||||
|
|
||||||
|
var papaParsedCSV = Papa.parse(aCsvString);
|
||||||
|
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
if (papaParsedCSV.errors.length != 0) {
|
||||||
|
result = false;
|
||||||
|
} else {
|
||||||
|
parsedCSV = this.csvFillEmptyCells(papaParsedCSV.data);
|
||||||
|
nColumns = parsedCSV[0].length;
|
||||||
|
|
||||||
|
defaultSelectedColumns = {};
|
||||||
|
defaultHiddenColumns = {};
|
||||||
|
defaultColumnLabels = {};
|
||||||
|
columnLabelsFirstrow = {};
|
||||||
|
for (i=0; i<nColumns; i++) {
|
||||||
|
defaultSelectedColumns[i] = true;
|
||||||
|
defaultHiddenColumns[i] = false;
|
||||||
|
defaultColumnLabels[i] = "";
|
||||||
|
columnLabelsFirstrow[i] = parsedCSV[0][i];
|
||||||
|
}
|
||||||
|
|
||||||
|
result = {
|
||||||
|
'importData': {
|
||||||
|
'format': 'csv',
|
||||||
|
'input': aCsvString,
|
||||||
|
'parsedCSV': parsedCSV,
|
||||||
|
'nColumns': nColumns,
|
||||||
|
'selectedColumns': defaultSelectedColumns,
|
||||||
|
'firstRowAsLabels': false,
|
||||||
|
'columnLabels': defaultColumnLabels,
|
||||||
|
'columnLabelsFirstrow': columnLabelsFirstrow,
|
||||||
|
'titlesColumn': null,
|
||||||
|
'notesColumn': null,
|
||||||
|
'hiddenColumns': defaultHiddenColumns,
|
||||||
|
'json': []
|
||||||
|
},
|
||||||
|
'currentStep': 'csv-columns'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
|
||||||
|
csvFillEmptyCells: function(table) {
|
||||||
|
var i,j;
|
||||||
|
|
||||||
|
var result = [];
|
||||||
|
|
||||||
|
var maxColumns = MochiKit.Iter.reduce(function(prev,next) {
|
||||||
|
return Math.max(prev,next)
|
||||||
|
}, MochiKit.Base.map(function(row) {return row.length;}, table) );
|
||||||
|
|
||||||
|
for (i=0; i<table.length; i++) {
|
||||||
|
|
||||||
|
result[i] = [];
|
||||||
|
for (j=0; j<maxColumns; j++) {
|
||||||
|
result[i][j] = (typeof(table[i][j]) != "undefined") ? table[i][j] : "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
|
||||||
|
csvGetColumnLabels: function() {
|
||||||
|
return (this.state.importData.firstRowAsLabels) ? this.state.importData.columnLabelsFirstrow : this.state.importData.columnLabels;
|
||||||
|
},
|
||||||
|
|
||||||
|
csvToJson: function() {
|
||||||
|
var result;
|
||||||
|
|
||||||
|
var importData = this.state.importData;
|
||||||
|
var columnLabels = this.csvGetColumnLabels();
|
||||||
|
|
||||||
|
result = [];
|
||||||
|
|
||||||
|
for (rowCount=0; rowCount<importData.parsedCSV.length; rowCount++) {
|
||||||
|
var rowCount,cellCount;
|
||||||
|
|
||||||
|
if (rowCount != 0 || ! importData.firstRowAsLabels) {
|
||||||
|
var record;
|
||||||
|
|
||||||
|
record = {};
|
||||||
|
record._importId = rowCount;
|
||||||
|
record.label = importData.parsedCSV[rowCount][importData.titlesColumn];
|
||||||
|
record.data = {'notes': ""};
|
||||||
|
record.currentVersion = {'fields': {}};
|
||||||
|
|
||||||
|
for (cellCount=0; cellCount<importData.parsedCSV[rowCount].length; cellCount++) {
|
||||||
|
if (importData.selectedColumns[cellCount] && cellCount != importData.notesColumn && cellCount != importData.titlesColumn) {
|
||||||
|
var fieldKey = rowCount+"-"+cellCount;
|
||||||
|
var field = {
|
||||||
|
'label': columnLabels[cellCount],
|
||||||
|
'value': importData.parsedCSV[rowCount][cellCount],
|
||||||
|
'hidden': importData.hiddenColumns[cellCount]
|
||||||
|
};
|
||||||
|
record.currentVersion.fields[fieldKey] = field;
|
||||||
|
} else if (cellCount == importData.notesColumn) {
|
||||||
|
record.data.notes = importData.parsedCSV[rowCount][cellCount];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.push(record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
|
||||||
|
//=========================================================================
|
||||||
|
|
||||||
|
csvRenderTbody: function() {
|
||||||
|
var rowCount;
|
||||||
|
var cellCount;
|
||||||
|
|
||||||
|
var firstRowAsLabels = this.state.importData.firstRowAsLabels;
|
||||||
|
var selectedColumns = this.state.importData.selectedColumns;
|
||||||
|
|
||||||
|
rowCount = 0;
|
||||||
|
return MochiKit.Base.map(function(row){
|
||||||
|
var result;
|
||||||
|
|
||||||
|
cellCount = 0;
|
||||||
|
|
||||||
|
if (rowCount == 0 && firstRowAsLabels) {
|
||||||
|
result = null;
|
||||||
|
} else {
|
||||||
|
result = React.DOM.tr({'key': 'csv-row-'+(rowCount)}, MochiKit.Base.map(function(cell) {
|
||||||
|
return (selectedColumns[cellCount]) ? React.DOM.td({'key': 'csv-cell-'+rowCount+'-'+(cellCount++)},cell) : null;
|
||||||
|
}, row));
|
||||||
|
}
|
||||||
|
|
||||||
|
rowCount++;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}, this.state.importData.parsedCSV);
|
||||||
|
},
|
||||||
|
|
||||||
|
csvRenderTheadInput: function(stepName, inputType, valueCallback, onChange, disabledCallback, showLabels) {
|
||||||
|
var cellCount;
|
||||||
|
|
||||||
|
var importData = this.state.importData;
|
||||||
|
|
||||||
|
cellCount = 0;
|
||||||
|
return React.DOM.tr({},
|
||||||
|
MochiKit.Base.map(MochiKit.Base.bind(function(cell) {
|
||||||
|
var result;
|
||||||
|
|
||||||
|
var columnLabels = (importData.firstRowAsLabels) ? importData.columnLabelsFirstrow : importData.columnLabels;
|
||||||
|
var inputLabel = (showLabels) ? React.DOM.label({'htmlFor': 'csv-'+stepName+'-input-'+cellCount}, columnLabels[cellCount]) : null;
|
||||||
|
|
||||||
|
if (! importData.selectedColumns[cellCount]) {
|
||||||
|
result = null;
|
||||||
|
} else {
|
||||||
|
var inputProps = {
|
||||||
|
'type': inputType,
|
||||||
|
'id': 'csv-'+stepName+'-input-'+cellCount,
|
||||||
|
'key': 'csv-'+stepName+'-input-'+cellCount,
|
||||||
|
'ref': 'csv-'+stepName+'-input-'+cellCount,
|
||||||
|
'onChange': MochiKit.Base.partial(onChange,cellCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inputType == 'radio' || inputType == 'checkbox') {
|
||||||
|
inputProps['checked'] = MochiKit.Base.partial(valueCallback,cellCount)();
|
||||||
|
} else {
|
||||||
|
inputProps['value'] = MochiKit.Base.partial(valueCallback,cellCount)();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (disabledCallback) {
|
||||||
|
inputProps['disabled'] = MochiKit.Base.partial(disabledCallback,cellCount)();
|
||||||
|
}
|
||||||
|
|
||||||
|
result = React.DOM.th({'key': 'csv-'+stepName+'-header-'+cellCount}, [
|
||||||
|
inputLabel,
|
||||||
|
React.DOM.input(inputProps)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
cellCount++;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}, this), this.state.importData.parsedCSV[0])
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
setStateCB: function(aState) {
|
||||||
|
this.setState(aState);
|
||||||
|
},
|
||||||
|
|
||||||
|
_renderStepMethods: {
|
||||||
|
'input': function() {
|
||||||
|
return new Clipperz.PM.UI.Components.ExtraFeatures.DataImport.Input({
|
||||||
|
'importState': this.state,
|
||||||
|
'setImportStateCallback': this.setStateCB,
|
||||||
|
'goToStepCallback': this.goToStep,
|
||||||
|
'extractJsonFromClipperzExportCallback': this.extractJsonFromClipperzExport,
|
||||||
|
'parseJsonCallback': this.parseJson,
|
||||||
|
'parseCsvCallback': this.parseCsv
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
'csv-columns': function() {
|
||||||
|
return new Clipperz.PM.UI.Components.ExtraFeatures.DataImport.CsvColumns({
|
||||||
|
'importState': this.state,
|
||||||
|
'setImportStateCallback': this.setStateCB,
|
||||||
|
'goToStepCallback': this.goToStep,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
'csv-labels': function() {
|
||||||
|
return new Clipperz.PM.UI.Components.ExtraFeatures.DataImport.CsvLabels({
|
||||||
|
'importState': this.state,
|
||||||
|
'setImportStateCallback': this.setStateCB,
|
||||||
|
'goToStepCallback': this.goToStep,
|
||||||
|
'csvRenderTheadInputCallback': this.csvRenderTheadInput,
|
||||||
|
'csvRenderTbodyCallback': this.csvRenderTbody,
|
||||||
|
'csvGetColumnLabelsCallback': this.csvGetColumnLabels
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
'csv-titles': function() {
|
||||||
|
return new Clipperz.PM.UI.Components.ExtraFeatures.DataImport.CsvTitles({
|
||||||
|
'importState': this.state,
|
||||||
|
'setImportStateCallback': this.setStateCB,
|
||||||
|
'goToStepCallback': this.goToStep,
|
||||||
|
'csvRenderTheadInputCallback': this.csvRenderTheadInput,
|
||||||
|
'csvRenderTbodyCallback': this.csvRenderTbody,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
'csv-notes': function() {
|
||||||
|
return new Clipperz.PM.UI.Components.ExtraFeatures.DataImport.CsvNotes({
|
||||||
|
'importState': this.state,
|
||||||
|
'setImportStateCallback': this.setStateCB,
|
||||||
|
'goToStepCallback': this.goToStep,
|
||||||
|
'csvRenderTheadInputCallback': this.csvRenderTheadInput,
|
||||||
|
'csvRenderTbodyCallback': this.csvRenderTbody,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
'csv-hidden': function() {
|
||||||
|
return new Clipperz.PM.UI.Components.ExtraFeatures.DataImport.CsvHidden({
|
||||||
|
'importState': this.state,
|
||||||
|
'setImportStateCallback': this.setStateCB,
|
||||||
|
'goToStepCallback': this.goToStep,
|
||||||
|
'csvRenderTheadInputCallback': this.csvRenderTheadInput,
|
||||||
|
'csvRenderTbodyCallback': this.csvRenderTbody,
|
||||||
|
'csvToJsonCallback': this.csvToJson
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
'preview': function() {
|
||||||
|
return new Clipperz.PM.UI.Components.ExtraFeatures.DataImport.Preview({
|
||||||
|
'importState': this.state,
|
||||||
|
'resetImportStateCallback': this.resetState,
|
||||||
|
'goToStepCallback': this.goToStep,
|
||||||
|
'isRecordToImportCallback': this.isRecordToImport,
|
||||||
|
'toggleRecordToImportCallback': this.toggleRecordToImport
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
//=========================================================================
|
||||||
|
|
||||||
|
renderStep: function(step) {
|
||||||
|
return MochiKit.Base.method(this, this._renderStepMethods[step])();
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function () {
|
||||||
|
return React.DOM.div({className:'extraFeature'}, [
|
||||||
|
React.DOM.h1({}, "Import"),
|
||||||
|
this.renderStep(this.state.currentStep)
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
|
||||||
|
//=========================================================================
|
||||||
|
});
|
||||||
|
|
||||||
|
Clipperz.PM.UI.Components.ExtraFeatures.DataImport = React.createFactory(Clipperz.PM.UI.Components.ExtraFeatures.DataImportClass);
|
@ -190,7 +190,7 @@ Clipperz.PM.UI.Components.Panels.ExtraFeaturesPanelClass = React.createClass({
|
|||||||
React.DOM.a({'className':Clipperz.PM.UI.Components.classNames(offlineCopyButtonClasses), 'onClick':this.handleDownloadOfflineCopyLink}, "Download")
|
React.DOM.a({'className':Clipperz.PM.UI.Components.classNames(offlineCopyButtonClasses), 'onClick':this.handleDownloadOfflineCopyLink}, "Download")
|
||||||
])
|
])
|
||||||
]),
|
]),
|
||||||
React.DOM.li({'key':'data_2'}, [
|
React.DOM.li({'key':'data_2', 'onClick':this.showExtraFeatureComponent('DataImport')}, [
|
||||||
React.DOM.h2({}, "Import"),
|
React.DOM.h2({}, "Import"),
|
||||||
React.DOM.div({}, [
|
React.DOM.div({}, [
|
||||||
React.DOM.p({}, "")
|
React.DOM.p({}, "")
|
||||||
|
@ -62,7 +62,7 @@ Clipperz.PM.UI.MainController = function() {
|
|||||||
|
|
||||||
this.registerForNotificationCenterEvents([
|
this.registerForNotificationCenterEvents([
|
||||||
'doLogin', 'registerNewUser', 'showRegistrationForm', 'goBack',
|
'doLogin', 'registerNewUser', 'showRegistrationForm', 'goBack',
|
||||||
'changePassphrase', 'deleteAccount',
|
'changePassphrase', 'deleteAccount', 'importCards',
|
||||||
'toggleSelectionPanel', 'toggleSettingsPanel',
|
'toggleSelectionPanel', 'toggleSettingsPanel',
|
||||||
'matchMediaQuery', 'unmatchMediaQuery',
|
'matchMediaQuery', 'unmatchMediaQuery',
|
||||||
'selectAllCards', 'selectRecentCards', 'search', 'tagSelected', 'selectUntaggedCards',
|
'selectAllCards', 'selectRecentCards', 'search', 'tagSelected', 'selectUntaggedCards',
|
||||||
@ -1271,6 +1271,31 @@ console.log("THE BROWSER IS OFFLINE");
|
|||||||
|
|
||||||
return deferredResult;
|
return deferredResult;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
importCards_handler: function(data) {
|
||||||
|
return Clipperz.Async.callbacks("MainController.importCards_handler", [
|
||||||
|
MochiKit.Base.method(this.overlay(), 'show', "importing …", true),
|
||||||
|
function() { return data; },
|
||||||
|
MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.method(this, function(recordData) {
|
||||||
|
var newRecord;
|
||||||
|
// I have the feeling this should be done in a more elegant way
|
||||||
|
return Clipperz.Async.callbacks("MainController.importCards_handler-newRecord", [
|
||||||
|
MochiKit.Base.method(this.user(), 'createNewRecord'),
|
||||||
|
function (aValue) {
|
||||||
|
newRecord = aValue;
|
||||||
|
return newRecord;
|
||||||
|
},
|
||||||
|
MochiKit.Base.methodcaller('setUpWithJSON', recordData),
|
||||||
|
])
|
||||||
|
})),
|
||||||
|
Clipperz.Async.collectAll,
|
||||||
|
MochiKit.Base.method(this.user(), 'saveChanges'),
|
||||||
|
MochiKit.Base.partial(MochiKit.Base.method(this, 'resetRecordsInfo')),
|
||||||
|
MochiKit.Base.partial(MochiKit.Base.method(this, 'refreshUI', null)),
|
||||||
|
MochiKit.Base.method(this.overlay(), 'done', "finished", 1),
|
||||||
|
MochiKit.Base.method(this.pages()[this.currentPage()], 'setProps', {'mode':'view', 'showGlobalMask':false}),
|
||||||
|
], {trace:false});
|
||||||
|
},
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
1406
frontend/delta/js/PapaParse/papaparse.js
Normal file
1406
frontend/delta/js/PapaParse/papaparse.js
Normal file
File diff suppressed because it is too large
Load Diff
29
frontend/delta/js/PapaParse/papaparse.min.js
vendored
Normal file
29
frontend/delta/js/PapaParse/papaparse.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -22,7 +22,11 @@
|
|||||||
|
|
||||||
"mousetrap.repository": "https://github.com/ccampbell/mousetrap.git",
|
"mousetrap.repository": "https://github.com/ccampbell/mousetrap.git",
|
||||||
"mousetrap.version": "1.4.6",
|
"mousetrap.version": "1.4.6",
|
||||||
"mousetrap.commit": "dcdf4d85b7d98dc2a141a7418f1bdf8987586b40"
|
"mousetrap.commit": "dcdf4d85b7d98dc2a141a7418f1bdf8987586b40",
|
||||||
|
|
||||||
|
"papaparse.repository": "https://github.com/mholt/PapaParse",
|
||||||
|
"papaparse.version": "4.1.1",
|
||||||
|
"papaparse.commit": "1c64d5c098570f243911e920bf7cbe170f69a9eb"
|
||||||
},
|
},
|
||||||
|
|
||||||
"html.template": "index_template.html",
|
"html.template": "index_template.html",
|
||||||
@ -57,6 +61,9 @@
|
|||||||
"-- Cubiq/add2home.js",
|
"-- Cubiq/add2home.js",
|
||||||
"-- Modernizr/modernizr-2.8.2.js",
|
"-- Modernizr/modernizr-2.8.2.js",
|
||||||
"OnMediaQuery/onmediaquery-0.2.0.js",
|
"OnMediaQuery/onmediaquery-0.2.0.js",
|
||||||
|
|
||||||
|
"PapaParse/papaparse.js",
|
||||||
|
"-- PapaParse/papaparse.min.js",
|
||||||
|
|
||||||
"-- IT WOULD BE NICE TO BE ABLE TO GET RID OF THESE IMPORTS",
|
"-- IT WOULD BE NICE TO BE ABLE TO GET RID OF THESE IMPORTS",
|
||||||
"Clipperz/YUI/Utils.js",
|
"Clipperz/YUI/Utils.js",
|
||||||
@ -170,6 +177,16 @@
|
|||||||
"Clipperz/PM/UI/Components/ExtraFeatures/DevicePIN.js",
|
"Clipperz/PM/UI/Components/ExtraFeatures/DevicePIN.js",
|
||||||
"Clipperz/PM/UI/Components/ExtraFeatures/Passphrase.js",
|
"Clipperz/PM/UI/Components/ExtraFeatures/Passphrase.js",
|
||||||
"Clipperz/PM/UI/Components/ExtraFeatures/DeleteAccount.js",
|
"Clipperz/PM/UI/Components/ExtraFeatures/DeleteAccount.js",
|
||||||
|
"Clipperz/PM/UI/Components/ExtraFeatures/Import.js",
|
||||||
|
|
||||||
|
"Clipperz/PM/UI/Components/ExtraFeatures/DataImport/StepsNavigation.js",
|
||||||
|
"Clipperz/PM/UI/Components/ExtraFeatures/DataImport/Input.js",
|
||||||
|
"Clipperz/PM/UI/Components/ExtraFeatures/DataImport/CsvColumns.js",
|
||||||
|
"Clipperz/PM/UI/Components/ExtraFeatures/DataImport/CsvLabels.js",
|
||||||
|
"Clipperz/PM/UI/Components/ExtraFeatures/DataImport/CsvTitles.js",
|
||||||
|
"Clipperz/PM/UI/Components/ExtraFeatures/DataImport/CsvNotes.js",
|
||||||
|
"Clipperz/PM/UI/Components/ExtraFeatures/DataImport/CsvHidden.js",
|
||||||
|
"Clipperz/PM/UI/Components/ExtraFeatures/DataImport/Preview.js",
|
||||||
|
|
||||||
"Clipperz/PM/UI/Components/Cards/FavIcon.js",
|
"Clipperz/PM/UI/Components/Cards/FavIcon.js",
|
||||||
"Clipperz/PM/UI/Components/Cards/List.js",
|
"Clipperz/PM/UI/Components/Cards/List.js",
|
||||||
|
@ -185,6 +185,40 @@ refer to http://www.clipperz.com.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* IMPORT */
|
||||||
|
.importForm {
|
||||||
|
textarea {
|
||||||
|
display: block;
|
||||||
|
width:100%;
|
||||||
|
min-height:400px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.jsonPreview {
|
||||||
|
width: 100%;
|
||||||
|
height:80%;
|
||||||
|
overflow: auto;
|
||||||
|
margin-top:1em;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-weight:bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
margin-bottom:1em;
|
||||||
|
padding-left:1em;
|
||||||
|
|
||||||
|
li {
|
||||||
|
.label {
|
||||||
|
font-weight:bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* /IMPORT */
|
||||||
|
|
||||||
|
|
||||||
form {
|
form {
|
||||||
input.valid + .invalidMsg, input.empty + .invalidMsg, input:focus + .invalidMsg, input.invalid:focus + .invalidMsg {
|
input.valid + .invalidMsg, input.empty + .invalidMsg, input:focus + .invalidMsg, input.invalid:focus + .invalidMsg {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
|
Loading…
Reference in New Issue
Block a user