Added Export feature

This commit is contained in:
Dario Chiappetta 2015-04-20 18:18:22 +02:00
parent 7ad8e42d9c
commit b812ea4efb
8 changed files with 455 additions and 1 deletions

View File

@ -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'),

View File

@ -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"
});

View File

@ -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"
});

View File

@ -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);

View File

@ -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({}, "")

View 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();
};

View File

@ -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;

View File

@ -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"
],