Added Export feature
This commit is contained in:
parent
7ad8e42d9c
commit
b812ea4efb
@ -233,6 +233,8 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.DirectLogin, Object, {
|
||||
|
||||
'serializedData': function () {
|
||||
return Clipperz.Async.collectResults("DirectLogin.serializedData", {
|
||||
'favicon': MochiKit.Base.method(this,'favicon'),
|
||||
'label': MochiKit.Base.method(this,'label'),
|
||||
'bookmarkletVersion': MochiKit.Base.method(this, 'getValue', 'bookmarkletVersion'),
|
||||
'formData': MochiKit.Base.method(this, 'getValue', 'formData'),
|
||||
'formValues': MochiKit.Base.method(this, 'getValue', 'formValues'),
|
||||
|
@ -316,6 +316,35 @@ console.log("Record.Version.hasPendingChanges");
|
||||
* /
|
||||
},
|
||||
*/
|
||||
|
||||
//=========================================================================
|
||||
|
||||
// TODO: this function may mix up the order of the fields
|
||||
'exportFields': function() {
|
||||
var deferredResult;
|
||||
var fields;
|
||||
|
||||
deferredResult = new Clipperz.Async.Deferred('Record.Version.export', {trace:false});
|
||||
deferredResult.addMethod(this,'fields');
|
||||
deferredResult.addCallback(MochiKit.Base.values);
|
||||
deferredResult.addCallback(MochiKit.Base.map, function(fieldIn) {
|
||||
return fieldIn.content();
|
||||
});
|
||||
deferredResult.addCallback(Clipperz.Async.collectAll);
|
||||
deferredResult.addCallback(function(listIn) {
|
||||
return listIn.reduce(function(result, field) {
|
||||
var ref = field.reference;
|
||||
result[ref] = field;
|
||||
delete result[ref].reference;
|
||||
return result;
|
||||
}, {});
|
||||
});
|
||||
|
||||
deferredResult.callback();
|
||||
|
||||
return deferredResult;
|
||||
},
|
||||
|
||||
//=========================================================================
|
||||
__syntaxFix__: "syntax fix"
|
||||
});
|
||||
|
@ -1169,6 +1169,65 @@ console.log("Record.hasPendingChanges RESULT", result);
|
||||
], {trace:false});
|
||||
},
|
||||
|
||||
//=========================================================================
|
||||
|
||||
'exportDirectLogins': function() {
|
||||
var result;
|
||||
|
||||
var directLoginsObject = this.directLogins();
|
||||
|
||||
if (Object.keys(directLoginsObject).length == 0) {
|
||||
result = {};
|
||||
} else {
|
||||
var callbackObject = Object.keys(directLoginsObject).reduce(function(previous, current) {
|
||||
previous[current] = MochiKit.Base.method( directLoginsObject[current], 'serializedData' );
|
||||
return previous;
|
||||
}, {});
|
||||
|
||||
result = Clipperz.Async.collectResults("Record.exportDirectLogins",callbackObject,{trace:false})();
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
'export': function() {
|
||||
var deferredResult;
|
||||
var label;
|
||||
var data;
|
||||
var currentVersion;
|
||||
var directLogins;
|
||||
var currentVersionObject;
|
||||
|
||||
data = {};
|
||||
currentVersion = {};
|
||||
directLogins = {};
|
||||
deferredResult = new Clipperz.Async.Deferred('Record.export', {trace:false});
|
||||
deferredResult.addMethod(this,'getCurrentRecordVersion');
|
||||
deferredResult.addCallback(function(recordVersionIn) { currentVersionObject = recordVersionIn; })
|
||||
deferredResult.addMethod(this,'fullLabel');
|
||||
deferredResult.addMethod(this,function(labelIn) {label = labelIn});
|
||||
deferredResult.addMethod(this,'exportDirectLogins');
|
||||
deferredResult.addCallback(function(directLoginsIn) { data['directLogins'] = directLoginsIn; });
|
||||
deferredResult.addCallback(function() { return currentVersionObject.getKey(); }),
|
||||
deferredResult.addMethod(this,function(keyIn) { data['currentVersionKey'] = keyIn; });
|
||||
deferredResult.addMethod(this,'notes');
|
||||
deferredResult.addMethod(this,function(notesIn) { data['notes'] = notesIn; });
|
||||
deferredResult.addMethod(this,function() { currentVersion['reference'] = this.currentVersionReference(); });
|
||||
deferredResult.addCallback(function() { return currentVersionObject.exportFields(); }),
|
||||
deferredResult.addCallback(function(fieldsIn) { currentVersion['fields'] = fieldsIn; });
|
||||
deferredResult.addMethod(this,function() {
|
||||
return {
|
||||
'label': label,
|
||||
'data': data,
|
||||
'currentVersion': currentVersion
|
||||
};
|
||||
});
|
||||
|
||||
deferredResult.callback();
|
||||
|
||||
return deferredResult;
|
||||
},
|
||||
|
||||
//=========================================================================
|
||||
__syntaxFix__: "syntax fix"
|
||||
});
|
||||
|
@ -0,0 +1,51 @@
|
||||
/*
|
||||
|
||||
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.DataExportClass = React.createClass({
|
||||
|
||||
propTypes: {
|
||||
// featureSet: React.PropTypes.oneOf(['FULL', 'EXPIRED', 'TRIAL']).isRequired,
|
||||
// 'level': React.PropTypes.oneOf(['hide', 'info', 'warning', 'error']).isRequired
|
||||
},
|
||||
|
||||
//=========================================================================
|
||||
|
||||
render: function () {
|
||||
return React.DOM.div({className:'extraFeature devicePIN'}, [
|
||||
React.DOM.h1({}, "Export"),
|
||||
React.DOM.p({'className': 'link', 'onClick': MochiKit.Base.method(this, function(){
|
||||
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'export','json');
|
||||
})}, "JSON"),
|
||||
React.DOM.p({'className': 'link', 'onClick': MochiKit.Base.method(this, function(){
|
||||
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'export','printable');
|
||||
})}, "Printable version")
|
||||
]);
|
||||
},
|
||||
|
||||
//=========================================================================
|
||||
});
|
||||
|
||||
Clipperz.PM.UI.Components.ExtraFeatures.DataExport = React.createFactory(Clipperz.PM.UI.Components.ExtraFeatures.DataExportClass);
|
@ -195,7 +195,7 @@ Clipperz.PM.UI.Components.Panels.ExtraFeaturesPanelClass = React.createClass({
|
||||
React.DOM.p({}, "")
|
||||
])
|
||||
]),
|
||||
React.DOM.li({'key':'data_3'}, [
|
||||
React.DOM.li({'key':'data_3', 'onClick':this.showExtraFeatureComponent('DataExport')}, [
|
||||
React.DOM.h2({}, "Export"),
|
||||
React.DOM.div({}, [
|
||||
React.DOM.p({}, "")
|
||||
|
301
frontend/delta/js/Clipperz/PM/UI/ExportController.js
Normal file
301
frontend/delta/js/Clipperz/PM/UI/ExportController.js
Normal file
@ -0,0 +1,301 @@
|
||||
/*
|
||||
|
||||
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/.
|
||||
|
||||
*/
|
||||
|
||||
Clipperz.Base.module('Clipperz.PM.UI');
|
||||
|
||||
Clipperz.PM.UI.ExportController = function(args) {
|
||||
this._type = args['type'] || Clipperz.Base.exception.raise('MandatoryParameter');
|
||||
this._recordsInfo = args['recordsInfo'] || Clipperz.Base.exception.raise('MandatoryParameter');
|
||||
this._target = Clipperz.PM.Crypto.randomKey();
|
||||
|
||||
this._style = "body {"+
|
||||
" margin: 0;"+
|
||||
" padding: 0;"+
|
||||
" font-family: monospace;"+
|
||||
"}"+
|
||||
""+
|
||||
"p {"+
|
||||
" padding-left: 1em;"+
|
||||
"}"+
|
||||
""+
|
||||
"h1 {"+
|
||||
" color: #ff9900;"+
|
||||
" background: black;"+
|
||||
" box-shadow: 0px 5px 6px 0 rgba(0, 0, 0, 0.15);"+
|
||||
" margin: 0;"+
|
||||
" padding:1em;"+
|
||||
"}"+
|
||||
""+
|
||||
".progressBar {"+
|
||||
" position: absolute;"+
|
||||
" width: 100%;"+
|
||||
" margin-top: 0px;"+
|
||||
" "+
|
||||
"}"+
|
||||
""+
|
||||
"#completed {"+
|
||||
" background: #ff9900;"+
|
||||
" color: white;"+
|
||||
" width: 0;"+
|
||||
" overflow: hidden;"+
|
||||
" font-size: 0.8em;"+
|
||||
" box-shadow: 0px 4px 6px 0 rgba(0, 0, 0, 0.15);"+
|
||||
"}"+
|
||||
""+
|
||||
"#printableUl {"+
|
||||
" width:100%;"+
|
||||
" height:80%;"+
|
||||
" margin: 0;"+
|
||||
" padding: 0;"+
|
||||
" list-style-type: none;"+
|
||||
"}"+
|
||||
""+
|
||||
"#printableUl li {"+
|
||||
" border: 1px solid #1863a1;"+
|
||||
" margin: 1em;"+
|
||||
""+
|
||||
"}"+
|
||||
""+
|
||||
"#printableUl li .label {"+
|
||||
" background: #1863a1;"+
|
||||
" color: white;"+
|
||||
" display: block;"+
|
||||
" padding: 1em;"+
|
||||
"}"+
|
||||
""+
|
||||
"#printableUl li dl {"+
|
||||
" padding: 1em;"+
|
||||
"}"+
|
||||
""+
|
||||
"#printableUl li dl dt {"+
|
||||
" color: darkgray;"+
|
||||
"}"+
|
||||
""+
|
||||
"#printableUl li dl dd {"+
|
||||
" padding: 0;"+
|
||||
" margin: 0 0 .5em 0;"+
|
||||
"}"+
|
||||
""+
|
||||
"#printableUl li .notes {"+
|
||||
" font-style: italic;"+
|
||||
" padding: 1em 0 0 1em;"+
|
||||
" display: block;"+
|
||||
"}"+
|
||||
"";
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
MochiKit.Base.update(Clipperz.PM.UI.ExportController.prototype, {
|
||||
|
||||
'toString': function() {
|
||||
return "Clipperz.PM.UI.ExportController";
|
||||
},
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
'type': function () {
|
||||
return this._type;
|
||||
},
|
||||
|
||||
'recordsInfo': function () {
|
||||
return this._recordsInfo;
|
||||
},
|
||||
|
||||
'target': function () {
|
||||
return this._target;
|
||||
},
|
||||
|
||||
//=============================================================================
|
||||
|
||||
'setWindowTitle': function (aWindow, aTitle) {
|
||||
aWindow.document.title = aTitle;
|
||||
},
|
||||
|
||||
'setWindowBody': function (aWindow, anHTML) {
|
||||
aWindow.document.body.innerHTML = anHTML;
|
||||
},
|
||||
|
||||
//=============================================================================
|
||||
|
||||
'initialWindowSetup': function (aWindow) {
|
||||
var dom = MochiKit.DOM.DIV({'id': 'main'},
|
||||
MochiKit.DOM.H1("Clipperz Exported Data (loading...)"),
|
||||
MochiKit.DOM.DIV({'class': 'progressBar'},
|
||||
MochiKit.DOM.DIV({'id': 'completed'},
|
||||
MochiKit.DOM.P({'style': 'margin:0; padding:0; text-align:center;'}, MochiKit.DOM.SPAN({'id': 'nCompleted'},"0"),"/",MochiKit.DOM.SPAN({'id': 'nTotal'},"") )
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
aWindow.document.getElementsByTagName('head')[0].appendChild( MochiKit.DOM.STYLE(this._style) );
|
||||
|
||||
this.setWindowTitle(aWindow, "Clipperz Exported Data (loading...)");
|
||||
this.setWindowBody (aWindow, MochiKit.DOM.toHTML(dom));
|
||||
},
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
'updateWindowWithHTMLContent': function (aWindow, anHtml) {
|
||||
this.setWindowBody(aWindow, anHtml);
|
||||
},
|
||||
|
||||
'updateWindowJSON': function (aWindow, exportedJSON) {
|
||||
var dom = MochiKit.DOM.DIV({'id': 'main'},
|
||||
MochiKit.DOM.H1("Clipperz Exported Data"),
|
||||
MochiKit.DOM.P("You can now save the following data and load it at any time using the Clipperz import feature."),
|
||||
MochiKit.DOM.TEXTAREA({'style': 'width:100%; height:80%'}, Clipperz.Base.serializeJSON(exportedJSON))
|
||||
);
|
||||
|
||||
this.setWindowTitle(aWindow, "Clipperz Exported Data");
|
||||
this.setWindowBody(aWindow, MochiKit.DOM.toHTML(dom));
|
||||
},
|
||||
|
||||
'updateWindowPrintable': function (aWindow, exportedJSON) {
|
||||
var dom = MochiKit.DOM.DIV({'id': 'main'},
|
||||
MochiKit.DOM.H1("Clipperz Exported Data"),
|
||||
MochiKit.DOM.P("You can now print this page and store it in a safe place."),
|
||||
MochiKit.DOM.UL({'id': 'printableUl'},
|
||||
exportedJSON.map(function(card){
|
||||
var label = (card.label.indexOf('')>=0) ? card.label.slice(0,card.label.indexOf('')).trim() : card.label;
|
||||
var notes = (card.data.notes) ? MochiKit.DOM.SPAN({'class': 'notes'}, card.data.notes) : "";
|
||||
|
||||
return MochiKit.DOM.LI({},
|
||||
MochiKit.DOM.SPAN({'class': 'label'}, label),
|
||||
notes,
|
||||
MochiKit.DOM.DL({},
|
||||
Object.keys(card.currentVersion.fields).map(function(key) {
|
||||
return [
|
||||
MochiKit.DOM.DT(card.currentVersion.fields[key].label),
|
||||
MochiKit.DOM.DD(card.currentVersion.fields[key].value),
|
||||
];
|
||||
})
|
||||
)
|
||||
);
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
this.setWindowTitle(aWindow, "Clipperz Exported Data");
|
||||
this.setWindowBody(aWindow, MochiKit.DOM.toHTML(dom));
|
||||
},
|
||||
|
||||
'updateWindowError': function (aWindow, errorMessage) {
|
||||
this.setWindowBody(aWindow,
|
||||
"<h3>Error</h3>"+
|
||||
"<p>The following error occured while exporting your data:</p>"+
|
||||
"<code>"+errorMessage+"</code>"
|
||||
);
|
||||
},
|
||||
|
||||
//=============================================================================
|
||||
|
||||
'runExportJSON': function (aWindow) {
|
||||
var deferredResult;
|
||||
var exportedRecords;
|
||||
|
||||
var totalRecords = this.recordsInfo().length;
|
||||
|
||||
exportedRecords = 0;
|
||||
|
||||
deferredResult = new Clipperz.Async.Deferred("DirectLoginRunner.exportJSON", {trace:false});
|
||||
deferredResult.addMethod(this, 'initialWindowSetup', aWindow);
|
||||
deferredResult.addCallback(function() { return "Export Data"});
|
||||
deferredResult.addMethod(this, 'setWindowTitle', aWindow);
|
||||
|
||||
deferredResult.addMethod( this, function() { return this.recordsInfo(); });
|
||||
deferredResult.addCallback( MochiKit.Base.map, function(recordIn) {
|
||||
var dr = new Clipperz.Async.Deferred("DirectLoginRunner.exportJSON__exportRecord", {trace:false});
|
||||
dr.addMethod(recordIn._rowObject, 'export');
|
||||
dr.addCallback(MochiKit.Base.method(this, function (exportedRecord) {
|
||||
var percentage = Math.round(100*exportedRecords/totalRecords);
|
||||
|
||||
aWindow.document.getElementById('nCompleted').innerText = ++exportedRecords;
|
||||
aWindow.document.getElementById('nTotal').innerText = totalRecords;
|
||||
aWindow.document.getElementById('completed').style.width = percentage+'%';
|
||||
|
||||
return exportedRecord;
|
||||
}));
|
||||
dr.callback();
|
||||
return dr;
|
||||
});
|
||||
|
||||
deferredResult.addCallback(Clipperz.Async.collectAll);
|
||||
deferredResult.addMethod( this, function(exportedJSONIn) {
|
||||
// console.log('return',exportedJSONIn);
|
||||
|
||||
sortedJSON = exportedJSONIn.sort( function(a,b) { return a.label.toUpperCase().localeCompare(b.label.toUpperCase()); } );
|
||||
|
||||
switch (this.type()) {
|
||||
case 'json':
|
||||
this.updateWindowJSON(aWindow,exportedJSONIn);
|
||||
break;
|
||||
case 'printable':
|
||||
this.updateWindowPrintable(aWindow,exportedJSONIn);
|
||||
break;
|
||||
default:
|
||||
this.updateWindowError(aWindow,"ExportController.runExportJSON: invalid value '"+this.type()+"' for parameter 'type'.");
|
||||
}
|
||||
});
|
||||
|
||||
deferredResult.callback();
|
||||
|
||||
return deferredResult;
|
||||
},
|
||||
|
||||
//=============================================================================
|
||||
|
||||
'run': function () {
|
||||
var newWindow;
|
||||
|
||||
newWindow = window.open("", this.target());
|
||||
|
||||
return this.runExportJSON(newWindow);
|
||||
},
|
||||
|
||||
//=============================================================================
|
||||
|
||||
'test': function () {
|
||||
var iFrame;
|
||||
var newWindow;
|
||||
|
||||
iFrame = MochiKit.DOM.createDOM('iframe');
|
||||
MochiKit.DOM.appendChildNodes(MochiKit.DOM.currentDocument().body, iFrame);
|
||||
|
||||
newWindow = iFrame.contentWindow;
|
||||
|
||||
return this.runDirectLogin(newWindow);
|
||||
},
|
||||
|
||||
//=============================================================================
|
||||
__syntaxFix__: "syntax fix"
|
||||
});
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
Clipperz.PM.UI.ExportController.exportJSON = function (recordsInfoIn, typeIn) {
|
||||
var runner;
|
||||
|
||||
runner = new Clipperz.PM.UI.ExportController({type:typeIn, recordsInfo: recordsInfoIn});
|
||||
return runner.run();
|
||||
};
|
@ -63,6 +63,7 @@ Clipperz.PM.UI.MainController = function() {
|
||||
this.registerForNotificationCenterEvents([
|
||||
'doLogin', 'registerNewUser', 'showRegistrationForm', 'goBack',
|
||||
'changePassphrase', 'deleteAccount',
|
||||
'export',
|
||||
'toggleSelectionPanel', 'toggleSettingsPanel',
|
||||
'matchMediaQuery', 'unmatchMediaQuery',
|
||||
'selectAllCards', 'selectRecentCards', 'search', 'tagSelected', 'selectUntaggedCards',
|
||||
@ -499,6 +500,9 @@ console.log("THE BROWSER IS OFFLINE");
|
||||
|
||||
deferredResult = new Clipperz.Async.Deferred('MainController.updateSelectedCard', {trace:false});
|
||||
deferredResult.addMethod(this.user(), 'getRecord', someInfo['reference']);
|
||||
|
||||
// deferredResult.addMethod(this, function(d) {console.log(d); return d;});
|
||||
|
||||
deferredResult.addMethod(this, 'collectRecordInfo');
|
||||
|
||||
deferredResult.addMethod(this, 'setPageProperties', 'mainPage', 'selectedCard');
|
||||
@ -1236,6 +1240,12 @@ console.log("THE BROWSER IS OFFLINE");
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
export_handler: function(exportType) {
|
||||
return Clipperz.PM.UI.ExportController.exportJSON( this.recordsInfo(), exportType );
|
||||
},
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
changePassphrase_handler: function(newPassphrase) {
|
||||
var currentPage = this.pages()[this.currentPage()];
|
||||
var deferredResult;
|
||||
|
@ -170,6 +170,7 @@
|
||||
"Clipperz/PM/UI/Components/ExtraFeatures/DevicePIN.js",
|
||||
"Clipperz/PM/UI/Components/ExtraFeatures/Passphrase.js",
|
||||
"Clipperz/PM/UI/Components/ExtraFeatures/DeleteAccount.js",
|
||||
"Clipperz/PM/UI/Components/ExtraFeatures/DataExport.js",
|
||||
|
||||
"Clipperz/PM/UI/Components/Cards/FavIcon.js",
|
||||
"Clipperz/PM/UI/Components/Cards/List.js",
|
||||
@ -186,6 +187,7 @@
|
||||
"Clipperz/PM/UI/MainController.js",
|
||||
"-- Clipperz/PM/UI/MainDesktopController.js",
|
||||
"Clipperz/PM/UI/DirectLoginController.js",
|
||||
"Clipperz/PM/UI/ExportController.js",
|
||||
"main.js"
|
||||
],
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user