Fix the settings app to allow backup for unregistered users

pull/1/head
yflory 8 years ago
parent f62267f8a1
commit 4206382865

@ -131,9 +131,10 @@ define([
}; };
var onReady = function (f, proxy, Cryptpad, exp) { var onReady = function (f, proxy, Cryptpad, exp) {
var fo = FO.init(proxy.drive, { var fo = exp.fo = FO.init(proxy.drive, {
Cryptpad: Cryptpad Cryptpad: Cryptpad
}); });
//storeObj = proxy; //storeObj = proxy;
store = initStore(fo, proxy, exp); store = initStore(fo, proxy, exp);
if (typeof(f) === 'function') { if (typeof(f) === 'function') {

@ -25,6 +25,7 @@ define(function () {
out.loading = "Chargement..."; out.loading = "Chargement...";
out.error = "Erreur"; out.error = "Erreur";
out.saved = "Enregistré";
out.disconnected = 'Déconnecté'; out.disconnected = 'Déconnecté';
out.synchronizing = 'Synchronisation'; out.synchronizing = 'Synchronisation';
@ -248,6 +249,11 @@ define(function () {
out.settings_resetTipsButton = "Réinitialiser les astuces visibles dans CryptDrive"; out.settings_resetTipsButton = "Réinitialiser les astuces visibles dans CryptDrive";
out.settings_resetTipsDone = "Toutes les astuces sont de nouveau visibles."; out.settings_resetTipsDone = "Toutes les astuces sont de nouveau visibles.";
out.settings_importTitle = "Importer les pads récents de ce navigateur dans mon CryptDrive";
out.settings_import = "Importer";
out.settings_importConfirm = "Êtes-vous sûr de vouloir importer les pads récents de ce navigateur dans le CryptDrive de votre compte utilisateur ?";
out.settings_importDone = "Importation terminée";
out.settings_userFeedbackHint1 = "CryptPad peut envoyer des retours d'expérience très limités vers le serveur, de manière à nous permettre d'améliorer l'expérience des utilisateurs."; out.settings_userFeedbackHint1 = "CryptPad peut envoyer des retours d'expérience très limités vers le serveur, de manière à nous permettre d'améliorer l'expérience des utilisateurs.";
out.settings_userFeedbackHint2 = "Le contenu de vos pads et les clés de déchiffrement ne seront jamais partagés avec le serveur."; out.settings_userFeedbackHint2 = "Le contenu de vos pads et les clés de déchiffrement ne seront jamais partagés avec le serveur.";
out.settings_userFeedback = "Activer l'envoi de retours d'expérience"; out.settings_userFeedback = "Activer l'envoi de retours d'expérience";

@ -27,6 +27,7 @@ define(function () {
out.loading = "Loading..."; out.loading = "Loading...";
out.error = "Error"; out.error = "Error";
out.saved = "Saved";
out.disconnected = 'Disconnected'; out.disconnected = 'Disconnected';
out.synchronizing = 'Synchronizing'; out.synchronizing = 'Synchronizing';
@ -250,6 +251,11 @@ define(function () {
out.settings_resetTipsButton = "Reset the available tips in CryptDrive"; out.settings_resetTipsButton = "Reset the available tips in CryptDrive";
out.settings_resetTipsDone = "All the tips are now visible again."; out.settings_resetTipsDone = "All the tips are now visible again.";
out.settings_importTitle = "Import this browser's recent pads in my CryptDrive";
out.settings_import = "Import";
out.settings_confirm = "Are you sure you want to import recent pads from this browser to your user account's CryptDrive?";
out.settings_done = "Import completed";
out.settings_userFeedbackHint1 = "CryptPad provides some very basic feedback to the server, to let us know how to improve your experience."; out.settings_userFeedbackHint1 = "CryptPad provides some very basic feedback to the server, to let us know how to improve your experience.";
out.settings_userFeedbackHint2 = "Your pad's content will never be shared with the server."; out.settings_userFeedbackHint2 = "Your pad's content will never be shared with the server.";
out.settings_userFeedback = "Enable user feedback"; out.settings_userFeedback = "Enable user feedback";

@ -621,6 +621,28 @@ define([
}; };
// STORAGE // STORAGE
var findWeaker = common.findWeaker = function (href, recents) {
var rHref = href || getRelativeHref(window.location.href);
var parsed = parsePadUrl(rHref);
if (!parsed.hash) { return false; }
var weaker;
recents.some(function (pad) {
var p = parsePadUrl(pad.href);
if (p.type !== parsed.type) { return; } // Not the same type
if (p.hash === parsed.hash) { return; } // Same hash, not stronger
var pHash = parseHash(p.hash);
var parsedHash = parseHash(parsed.hash);
if (!parsedHash || !pHash) { return; }
if (pHash.version !== parsedHash.version) { return; }
if (pHash.channel !== parsedHash.channel) { return; }
if (pHash.mode === 'view' && parsedHash.mode === 'edit') {
weaker = pad.href;
return true;
}
return;
});
return weaker;
};
var findStronger = common.findStronger = function (href, recents) { var findStronger = common.findStronger = function (href, recents) {
var rHref = href || getRelativeHref(window.location.href); var rHref = href || getRelativeHref(window.location.href);
var parsed = parsePadUrl(rHref); var parsed = parsePadUrl(rHref);

@ -6,10 +6,10 @@ define([
var Messages = {}; var Messages = {};
var ROOT = "root"; var ROOT = module.ROOT = "root";
var UNSORTED = "unsorted"; var UNSORTED = module.UNSORTED = "unsorted";
var TRASH = "trash"; var TRASH = module.TRASH = "trash";
var TEMPLATE = "template"; var TEMPLATE = module.TEMPLATE = "template";
var init = module.init = function (files, config) { var init = module.init = function (files, config) {
var Cryptpad = config.Cryptpad; var Cryptpad = config.Cryptpad;

@ -2,8 +2,9 @@ require.config({ paths: { 'json.sortify': '/bower_components/json.sortify/dist/J
define([ define([
'/common/cryptpad-common.js', '/common/cryptpad-common.js',
'/common/cryptget.js', '/common/cryptget.js',
'/common/fileObject.js',
'json.sortify' 'json.sortify'
], function (Cryptpad, Crypt, Sortify) { ], function (Cryptpad, Crypt, FO, Sortify) {
var exp = {}; var exp = {};
var getType = function (el) { var getType = function (el) {
@ -17,6 +18,7 @@ define([
var nkey = key; var nkey = key;
while (typeof (obj[nkey]) !== "undefined") { while (typeof (obj[nkey]) !== "undefined") {
nkey = key + '_' + i; nkey = key + '_' + i;
i++;
} }
return nkey; return nkey;
}; };
@ -39,7 +41,8 @@ define([
}; };
// Merge obj2 into obj1 // Merge obj2 into obj1
// If keepOld is true, obj1 values are kept in case of conflict in the ROOT object // If keepOld is true, obj1 values are kept in case of conflicti
// Not used ATM
var merge = function (obj1, obj2, keepOld) { var merge = function (obj1, obj2, keepOld) {
if (typeof (obj1) !== "object" || typeof (obj2) !== "object") { return; }; if (typeof (obj1) !== "object" || typeof (obj2) !== "object") { return; };
Object.keys(obj2).forEach(function (k) { Object.keys(obj2).forEach(function (k) {
@ -57,40 +60,116 @@ define([
} }
// Else, they're both maps or both arrays // Else, they're both maps or both arrays
if (getType(obj1[k]) === "array" && getType(obj2[k]) === "array") { if (getType(obj1[k]) === "array" && getType(obj2[k]) === "array") {
// CryptPad_RECENTPADS var c = obj1[k].concat(obj2[k]);
if (k === 'CryptPad_RECENTPADS') { obj1[k] = deduplicate(c);
var old = obj1[k]; return;
obj2[k].forEach(function (pad) {
if (!old.some(function (op) {
// TODO read-only links
return op.href === pad.href;
})) {
old.push(pad);
} }
merge(obj1[k], obj2[k], keepOld);
}); });
};
var createFromPath = function (proxy, oldFo, path, href) {
var root = proxy.drive;
var error = function (msg) {
console.error(msg || "Unable to find that path", path);
};
if (path[0] === FO.TRASH && path.length === 4) {
href = oldFo.getTrashElementData(path);
path.pop();
}
var p, next, nextRoot;
path.forEach(function (p, i) {
if (!root) { return; }
if (typeof(p) === "string") {
if (getType(root) !== "object") { root = undefined; error(); return; }
if (i === path.length - 1) {
root[findAvailableKey(root, p)] = href;
return; return;
} }
var c = obj1[k].concat(obj2[k]); next = getType(path[i+1]);
obj1[k] = deduplicate(c); nextRoot = getType(root[p]);
if (nextRoot !== "undefined") {
if (next === "string" && nextRoot === "object" || next === "number" && nextRoot === "array") {
root = root[p];
return; return;
} }
merge(obj1[k], obj2[k]); p = findAvailableKey(root, p);
}
if (next === "number") {
root[p] = [];
root = root[p];
return;
}
root[p] = {};
root = root[p];
return;
}
// Path contains a non-string element: it's an array index
if (typeof(p) !== "number") { root = undefined; error(); return; }
if (getType(root) !== "array") { root = undefined; error(); return; }
if (i === path.length - 1) {
if (root.indexOf(href) === -1) { root.push(href); }
return;
}
next = getType(path[i+1]);
if (next === "number") {
error('2 consecutives arrays in the user object');
root = undefined;
//root.push([]);
//root = root[root.length - 1];
return;
}
root.push({});
root = root[root.length - 1];
return;
}); });
}; };
var mergeAnonDrive = exp.anonDriveIntoUser = function (proxy, cb) { var mergeAnonDrive = exp.anonDriveIntoUser = function (proxy, cb) {
// Make sure we have an FS_hash and we don't use it, otherwise just stop the migration and cb // Make sure we have an FS_hash and we don't use it, otherwise just stop the migration and cb
if (!localStorage.FS_hash || !Cryptpad.isLoggedIn()) { if (!localStorage.FS_hash || !Cryptpad.isLoggedIn()) {
delete sessionStorage.migrateAnonDrive;
if (typeof(cb) === "function") { cb(); } if (typeof(cb) === "function") { cb(); }
} }
// Get the content of FS_hash and then merge the objects, remove the migration key and cb // Get the content of FS_hash and then merge the objects, remove the migration key and cb
var todo = function (err, doc) { var todo = function (err, doc) {
if (err) { logError("Cannot migrate recent pads", err); return; } if (err) { console.error("Cannot migrate recent pads", err); return; }
var parsed; var parsed;
try { parsed = JSON.parse(doc); } catch (e) { logError("Cannot parsed recent pads", e); } try { parsed = JSON.parse(doc); } catch (e) { logError("Cannot parsed recent pads", e); }
if (parsed) { if (parsed) {
merge(proxy, parsed, true); //merge(proxy, parsed, true);
var oldFo = FO.init(parsed.drive, {
Cryptpad: Cryptpad
});
var newData = Cryptpad.getStore().getProxy();
var newFo = newData.fo;
var newRecentPads = proxy.drive[Cryptpad.storageKey];
var newFiles = newFo.getFilesDataFiles();
var oldFiles = oldFo.getFilesDataFiles();
oldFiles.forEach(function (href) {
// Do not migrate a pad if we already have it, it would create a duplicate in the drive
if (newFiles.indexOf(href) !== -1) { return; }
// If we have a stronger version, do not add the current href
if (Cryptpad.findStronger(href, newRecentPads)) { return; }
// If we have a weaker version, replace the href by the new one
// NOTE: if that weaker version is in the trash, the strong one will be put in unsorted
var weaker = Cryptpad.findWeaker(href, newRecentPads);
if (weaker) {
newFo.replaceHref(weaker, href);
return;
}
// Here it means we have a new href, so we should add it to the drive at its old location
var paths = oldFo.findFile(href);
if (paths.length === 0) { return; }
createFromPath(proxy, oldFo, paths[0], href);
// Also, push the file data in our array
var data = oldFo.getFileData(href);
if (data) {
newRecentPads.push(data);
}
});
} }
if (typeof(cb) === "function") { cb(); } if (typeof(cb) === "function") { cb(); }
}; };

@ -66,10 +66,9 @@ define([
'id': 'displayName', 'id': 'displayName',
'placeholder': Messages.anonymous}).appendTo($div); 'placeholder': Messages.anonymous}).appendTo($div);
var $save = $('<button>', {'class': 'btn btn-primary'}).text(Messages.settings_save).appendTo($div); var $save = $('<button>', {'class': 'btn btn-primary'}).text(Messages.settings_save).appendTo($div);
var $ok = $('<span>', {'class': 'fa fa-check'}).appendTo($div); var $ok = $('<span>', {'class': 'fa fa-check', title: Messages.saved}).hide().appendTo($div);
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'}).appendTo($div); var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'}).hide().appendTo($div);
$spinner.hide();
var displayName = obj[USERNAME_KEY] || ''; var displayName = obj[USERNAME_KEY] || '';
$input.val(displayName); $input.val(displayName);
@ -136,7 +135,7 @@ define([
}; };
var importFile = function (content, file) { var importFile = function (content, file) {
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'}).appendTo($div); var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'}).appendTo($div);
Crypt.put(Cryptpad.getUserHash(), content, function (e) { Crypt.put(Cryptpad.getUserHash() || localStorage[Cryptpad.fileHashKey], content, function (e) {
if (e) { console.error(e); } if (e) { console.error(e); }
$spinner.remove(); $spinner.remove();
}); });
@ -188,17 +187,27 @@ define([
$('<br>').appendTo($div); $('<br>').appendTo($div);
var $ok = $('<span>', {'class': 'fa fa-check', title: Messages.saved});
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'});
var $checkbox = $('<input>', { var $checkbox = $('<input>', {
'type': 'checkbox', 'type': 'checkbox',
}).on('change', function () { }).on('change', function () {
$spinner.show();
$ok.hide();
obj.proxy.allowUserFeedback = $checkbox.is(':checked') || false; obj.proxy.allowUserFeedback = $checkbox.is(':checked') || false;
// TODO provide feedback to show if this is synced Cryptpad.whenRealtimeSyncs(obj.info.realtime, function () {
// Cryptpad.whenRealtimeSyncs... $spinner.hide();
$ok.show();
});
}); });
$checkbox.appendTo($div); $checkbox.appendTo($div);
$label.appendTo($div); $label.appendTo($div);
$ok.hide().appendTo($div);
$spinner.hide().appendTo($div);
if (obj.proxy.allowUserFeedback) { if (obj.proxy.allowUserFeedback) {
$checkbox[0].checked = true; $checkbox[0].checked = true;
} }
@ -206,17 +215,25 @@ define([
}; };
var createImportLocalPads = function (obj) { var createImportLocalPads = function (obj) {
if (!Cryptpad.isLoggedIn()) { return; }
var $div = $('<div>', {'class': 'importLocalPads'}); var $div = $('<div>', {'class': 'importLocalPads'});
var $label = $('<label>', {'for' : 'importLocalPads'}).text("TODO: IMPORT LOCAL PADS LABEL").appendTo($div); var $label = $('<label>', {'for' : 'importLocalPads'}).text(Messages.settings_importTitle).appendTo($div);
$('<br>').appendTo($div); $('<br>').appendTo($div);
var $button = $('<button>', {'id': 'importLocalPads', 'class': 'btn btn-default'}) var $button = $('<button>', {'id': 'importLocalPads', 'class': 'btn btn-primary'})
.text("TODO: IMPORT LOCAL PADS").appendTo($div); .text(Messages.settings_import).appendTo($div);
var $ok = $('<span>', {'class': 'fa fa-check', title: Messages.saved}).hide().appendTo($div);
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'}).hide().appendTo($div);
$button.click(function () { $button.click(function () {
Cryptpad.confirm("ARE YOU SURE?", function (err) { Cryptpad.confirm(Messages.settings_importConfirm, function (yes) {
if (err) { return; } if (!yes) { return; }
$spinner.show();
$ok.hide();
Merge.anonDriveIntoUser(obj.proxy, function () { Merge.anonDriveIntoUser(obj.proxy, function () {
Cryptpad.alert("DONE"); $spinner.hide();
$ok.show();
Cryptpad.alert(Messages.settings_importDone);
}); });
}, undefined, true); }, undefined, true);
}); });
@ -230,6 +247,7 @@ define([
APP.$container.append(createDisplayNameInput(obj)); APP.$container.append(createDisplayNameInput(obj));
APP.$container.append(createResetTips()); APP.$container.append(createResetTips());
APP.$container.append(createBackupDrive(obj)); APP.$container.append(createBackupDrive(obj));
APP.$container.append(createImportLocalPads(obj));
APP.$container.append(createResetDrive(obj)); APP.$container.append(createResetDrive(obj));
APP.$container.append(createUserFeedbackToggle(obj)); APP.$container.append(createUserFeedbackToggle(obj));
obj.proxy.on('change', [], refresh); obj.proxy.on('change', [], refresh);

Loading…
Cancel
Save