mirror of https://github.com/xwiki-labs/cryptpad
Basic CryptDrive export with pads
This commit is contained in:
parent
f4d5a7567f
commit
92ce311694
|
@ -46,7 +46,8 @@
|
|||
"html2canvas": "^0.4.1",
|
||||
"croppie": "^2.5.0",
|
||||
"sortablejs": "#^1.6.0",
|
||||
"saferphore": "^0.0.1"
|
||||
"saferphore": "^0.0.1",
|
||||
"jszip": "Stuk/jszip#^3.1.5"
|
||||
},
|
||||
"resolutions": {
|
||||
"bootstrap": "^v4.0.0"
|
||||
|
|
|
@ -744,6 +744,15 @@ define([
|
|||
Cryptpad.removeLoginBlock(data, cb);
|
||||
});
|
||||
|
||||
sframeChan.on('Q_CRYPTGET', function (data, cb) {
|
||||
Cryptget.get(data.hash, function (err, val) {
|
||||
cb({
|
||||
error: err,
|
||||
data: val
|
||||
});
|
||||
}, data.opts);
|
||||
});
|
||||
|
||||
if (cfg.addRpc) {
|
||||
cfg.addRpc(sframeChan, Cryptpad, Utils);
|
||||
}
|
||||
|
|
|
@ -270,5 +270,9 @@ define({
|
|||
'Q_IS_PAD_STORED': true,
|
||||
|
||||
// Import mediatag from a pad
|
||||
'Q_IMPORT_MEDIATAG': true
|
||||
'Q_IMPORT_MEDIATAG': true,
|
||||
|
||||
// Ability to get a pad's content from its hash
|
||||
'Q_CRYPTGET': true,
|
||||
|
||||
});
|
||||
|
|
|
@ -562,6 +562,8 @@ define(function () {
|
|||
out.settings_backupCategory = "Backup";
|
||||
out.settings_backupHint = "Backup or restore all your CryptDrive's content. It won't contain the content of your pads, just the link to access them.";
|
||||
out.settings_backup = "Backup";
|
||||
out.settings_backupHint2 = "Download all your pads. Pads will be downloaded in an readable format when available.";
|
||||
out.settings_backup2 = "Download my CryptDrive";
|
||||
out.settings_restore = "Restore";
|
||||
|
||||
out.settings_resetNewTitle = "Clean CryptDrive";
|
||||
|
|
|
@ -12,6 +12,7 @@ define([
|
|||
'/customize/credential.js',
|
||||
'/customize/application_config.js',
|
||||
'/api/config',
|
||||
'/settings/make-backup.js',
|
||||
|
||||
'/bower_components/file-saver/FileSaver.min.js',
|
||||
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
|
||||
|
@ -30,7 +31,8 @@ define([
|
|||
h,
|
||||
Cred,
|
||||
AppConfig,
|
||||
ApiConfig
|
||||
ApiConfig,
|
||||
Backup,
|
||||
)
|
||||
{
|
||||
var saveAs = window.saveAs;
|
||||
|
@ -50,6 +52,7 @@ define([
|
|||
'cp-settings-autostore',
|
||||
'cp-settings-userfeedback',
|
||||
'cp-settings-change-password',
|
||||
'cp-settings-backup',
|
||||
'cp-settings-delete'
|
||||
],
|
||||
'creation': [
|
||||
|
@ -300,6 +303,45 @@ define([
|
|||
return $div;
|
||||
};
|
||||
|
||||
create['backup'] = function () {
|
||||
if (!common.isLoggedIn()) { return; }
|
||||
var $div = $('<div>', { 'class': 'cp-settings-backup cp-sidebarlayout-element'});
|
||||
|
||||
$('<span>', {'class': 'label'}).text(Messages.settings_backupTitle || 'TODO BACKUP').appendTo($div); // XXX
|
||||
|
||||
$('<span>', {'class': 'cp-sidebarlayout-description'})
|
||||
.append(Messages.settings_backupHint || 'TODO').appendTo($div); // XXX
|
||||
|
||||
var $ok = $('<span>', {'class': 'fa fa-check', title: Messages.saved});
|
||||
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'});
|
||||
|
||||
var $button = $('<button>', {'id': 'cp-settings-delete', 'class': 'btn btn-danger'})
|
||||
.text(Messages.settings_backupButton || 'BACKUP').appendTo($div); // XXX
|
||||
|
||||
$button.click(function () {
|
||||
$spinner.show();
|
||||
UI.confirm(Messages.settings_backupConfirm || 'TODO Are you sure?', function (yes) { // XXX
|
||||
if (!yes) { return; }
|
||||
});
|
||||
// TODO
|
||||
/*
|
||||
UI.confirm("Are you sure?", function (yes) {
|
||||
// Logout everywhere
|
||||
// Disconnect other tabs
|
||||
// Remove owned pads
|
||||
// Remove owned drive
|
||||
// Remove pinstore
|
||||
// Alert: "Account deleted", press OK to be redirected to the home page
|
||||
$spinner.hide();
|
||||
});*/
|
||||
});
|
||||
|
||||
$spinner.hide().appendTo($div);
|
||||
$ok.hide().appendTo($div);
|
||||
|
||||
return $div;
|
||||
};
|
||||
|
||||
create['delete'] = function () {
|
||||
if (!common.isLoggedIn()) { return; }
|
||||
var $div = $('<div>', { 'class': 'cp-settings-delete cp-sidebarlayout-element'});
|
||||
|
@ -861,6 +903,40 @@ define([
|
|||
$import.attr('class', 'btn btn-success').text(Messages.settings_restore);
|
||||
$div.append($import);
|
||||
|
||||
// Backup all the pads
|
||||
var exportDrive = function () {
|
||||
var todo = function (data, filename) {
|
||||
var getPad = function (data, cb) {
|
||||
sframeChan.query("Q_CRYPTGET", data, function (err, obj) {
|
||||
if (err) { return void cb(err); }
|
||||
if (obj.error) { return void cb(obj.error); }
|
||||
cb(null, obj.data);
|
||||
});
|
||||
};
|
||||
|
||||
Backup.create(data, getPad, function (blob) {
|
||||
saveAs(blob, filename);
|
||||
});
|
||||
};
|
||||
sframeChan.query("Q_SETTINGS_DRIVE_GET", null, function (err, data) {
|
||||
if (err) { return void console.error(err); }
|
||||
var sjson = JSON.stringify(data);
|
||||
var name = displayName || accountName || Messages.anonymous;
|
||||
var suggestion = name + '-' + new Date().toDateString();
|
||||
|
||||
UI.prompt('TODO are you sure? if ye,s pick a name...', // XXX
|
||||
Util.fixFileName(suggestion) + '.json', function (filename) {
|
||||
if (!(typeof(filename) === 'string' && filename)) { return; }
|
||||
todo(data, filename);
|
||||
});
|
||||
});
|
||||
};
|
||||
$('<span>', {'class': 'cp-sidebarlayout-description'})
|
||||
.text(Messages.settings_backupHint2).appendTo($div);
|
||||
var $export = common.createButton('export', true, {}, exportDrive);
|
||||
$export.attr('class', 'btn btn-success').text(Messages.settings_backup2);
|
||||
$div.append($export);
|
||||
|
||||
return $div;
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
define([
|
||||
'/common/cryptget.js',
|
||||
'/common/common-hash.js',
|
||||
'/bower_components/nthen/index.js',
|
||||
'/bower_components/saferphore/index.js',
|
||||
'/bower_components/jszip/dist/jszip.min.js',
|
||||
], function (Crypt, Hash, nThen, Saferphore, JsZip) {
|
||||
|
||||
var sanitize = function (str) {
|
||||
return str.replace(/[^a-z0-9]/gi, '_').toLowerCase();
|
||||
};
|
||||
|
||||
var getUnique = function (name, ext, existing) {
|
||||
var n = name;
|
||||
var i = 1;
|
||||
while (existing.indexOf(n) !== -1) {
|
||||
n = name + ' ('+ i++ + ')';
|
||||
}
|
||||
return n;
|
||||
};
|
||||
|
||||
var addFile = function (ctx, zip, fData, existingNames) {
|
||||
if (!fData.href && !fData.roHref) {
|
||||
return void ctx.errors.push({
|
||||
error: 'EINVAL',
|
||||
data: fData
|
||||
});
|
||||
}
|
||||
|
||||
var parsed = Hash.parsePadUrl(fData.href || fData.roHref);
|
||||
// TODO deal with files here
|
||||
if (parsed.hashData.type !== 'pad') { return; }
|
||||
|
||||
var w = ctx.waitFor();
|
||||
ctx.sem.take(function (give) {
|
||||
var opts = {
|
||||
password: fData.password
|
||||
};
|
||||
var rawName = fData.fileName || fData.title || 'File';
|
||||
console.log(rawName);
|
||||
ctx.get({
|
||||
hash: parsed.hash,
|
||||
opts: opts
|
||||
}, give(function (err, val) {
|
||||
w();
|
||||
if (err) {
|
||||
return void ctx.errors.push({
|
||||
error: err,
|
||||
data: fData
|
||||
});
|
||||
}
|
||||
// TODO transform file here
|
||||
// var blob = transform(val, type);
|
||||
var opts = {};
|
||||
var fileName = getUnique(sanitize(rawName), '.txt', existingNames);
|
||||
existingNames.push(fileName);
|
||||
zip.file(fileName, val, opts);
|
||||
console.log('DONE ---- ' + rawName);
|
||||
}));
|
||||
});
|
||||
// cb(err, blob);
|
||||
// wiht blob.name not undefined
|
||||
};
|
||||
|
||||
var makeFolder = function (ctx, root, zip) {
|
||||
if (typeof (root) !== "object") { return; }
|
||||
var existingNames = [];
|
||||
Object.keys(root).forEach(function (k) {
|
||||
var el = root[k];
|
||||
if (typeof el === "object") {
|
||||
var fName = getUnique(sanitize(k), '', existingNames);
|
||||
existingNames.push(fName);
|
||||
return void makeFolder(ctx, el, zip.folder(fName));
|
||||
}
|
||||
if (ctx.data.sharedFolders[el]) {
|
||||
// TODO later...
|
||||
return;
|
||||
}
|
||||
var fData = ctx.data.filesData[el];
|
||||
if (fData) {
|
||||
addFile(ctx, zip, fData, existingNames);
|
||||
return;
|
||||
}
|
||||
// What is this element?
|
||||
console.error(el);
|
||||
});
|
||||
};
|
||||
|
||||
var create = function (data, getPad, cb) {
|
||||
if (!data || !data.drive) { return void cb('EEMPTY'); }
|
||||
var sem = Saferphore.create(10);
|
||||
var ctx = {
|
||||
get: getPad,
|
||||
data: data.drive,
|
||||
zip: new JsZip(),
|
||||
errors: [],
|
||||
sem: sem,
|
||||
};
|
||||
nThen(function (waitFor) {
|
||||
ctx.waitFor = waitFor;
|
||||
var zipRoot = ctx.zip.folder('Root');
|
||||
makeFolder(ctx, data.drive.root, zipRoot);
|
||||
}).nThen(function () {
|
||||
// TODO call cb with ctx.zip here
|
||||
console.log(ctx.zip);
|
||||
console.log(ctx.errors);
|
||||
ctx.zip.generateAsync({type: 'blob'}).then(function (content) {
|
||||
cb(content);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
create: create
|
||||
};
|
||||
});
|
Loading…
Reference in New Issue