mirror of https://github.com/xwiki-labs/cryptpad
merge staging into pending allow-list work
This commit is contained in:
commit
cac114bb52
|
@ -386,6 +386,7 @@ Meta.commands = Object.keys(commands);
|
|||
Meta.createLineHandler = function (ref, errorHandler) {
|
||||
ref.meta = {};
|
||||
ref.index = 0;
|
||||
ref.logged = {};
|
||||
|
||||
return function (err, line) {
|
||||
if (err) {
|
||||
|
@ -404,10 +405,15 @@ Meta.createLineHandler = function (ref, errorHandler) {
|
|||
var index = ref.index++;
|
||||
if (typeof(line) === 'undefined') { return; }
|
||||
|
||||
|
||||
if (Array.isArray(line)) {
|
||||
try {
|
||||
handleCommand(ref.meta, line);
|
||||
} catch (err2) {
|
||||
var code = err2.message;
|
||||
if (ref.logged[code]) { return; }
|
||||
|
||||
ref.logged[code] = true;
|
||||
errorHandler("METADATA_COMMAND_ERR", {
|
||||
error: err2.stack,
|
||||
line: line,
|
||||
|
|
|
@ -1592,6 +1592,17 @@ define([
|
|||
if (data.accept) { $input.attr('accept', data.accept); }
|
||||
button.click(function () { $input.click(); });
|
||||
break;
|
||||
case 'copy':
|
||||
button = $('<button>', {
|
||||
'class': 'fa fa-clone cp-toolbar-icon-import',
|
||||
title: Messages.makeACopy,
|
||||
}).append($('<span>', {'class': 'cp-toolbar-drawer-element'}).text(Messages.makeACopy));
|
||||
button
|
||||
.click(common.prepareFeedback(type))
|
||||
.click(function () {
|
||||
sframeChan.query('EV_MAKE_A_COPY');
|
||||
});
|
||||
break;
|
||||
case 'importtemplate':
|
||||
if (!AppConfig.enableTemplates) { return; }
|
||||
if (!common.isLoggedIn()) { return; }
|
||||
|
|
|
@ -543,6 +543,28 @@ define([
|
|||
});
|
||||
};
|
||||
|
||||
var fixPadMetadata = function (parsed, copy) {
|
||||
var meta;
|
||||
if (Array.isArray(parsed) && typeof(parsed[3]) === "object") {
|
||||
meta = parsed[3].metadata; // pad
|
||||
} else if (parsed.info) {
|
||||
meta = parsed.info; // poll
|
||||
} else {
|
||||
meta = parsed.metadata;
|
||||
}
|
||||
if (typeof(meta) === "object") {
|
||||
meta.defaultTitle = meta.title || meta.defaultTitle;
|
||||
if (copy) {
|
||||
meta.defaultTitle = Messages._getKey('copy_title', [meta.defaultTitle]);
|
||||
}
|
||||
meta.title = "";
|
||||
delete meta.users;
|
||||
delete meta.chat2;
|
||||
delete meta.chat;
|
||||
delete meta.cursor;
|
||||
}
|
||||
};
|
||||
|
||||
common.useTemplate = function (data, Crypt, cb, optsPut) {
|
||||
// opts is used to overrides options for chainpad-netflux in cryptput
|
||||
// it allows us to add owners and expiration time if it is a new file
|
||||
|
@ -576,24 +598,7 @@ define([
|
|||
try {
|
||||
// Try to fix the title before importing the template
|
||||
var parsed = JSON.parse(val);
|
||||
var meta;
|
||||
if (Array.isArray(parsed) && typeof(parsed[3]) === "object") {
|
||||
meta = parsed[3].metadata; // pad
|
||||
} else if (parsed.info) {
|
||||
meta = parsed.info; // poll
|
||||
} else {
|
||||
meta = parsed.metadata;
|
||||
}
|
||||
if (typeof(meta) === "object") {
|
||||
meta.defaultTitle = meta.title || meta.defaultTitle;
|
||||
meta.title = "";
|
||||
delete meta.users;
|
||||
delete meta.chat2;
|
||||
delete meta.chat;
|
||||
delete meta.cursor;
|
||||
if (data.chat) { meta.chat2 = data.chat; }
|
||||
if (data.cursor) { meta.cursor = data.cursor; }
|
||||
}
|
||||
fixPadMetadata(parsed);
|
||||
val = JSON.stringify(parsed);
|
||||
} catch (e) {
|
||||
console.log("Can't fix template title", e);
|
||||
|
@ -608,58 +613,96 @@ define([
|
|||
var data = common.fromFileData;
|
||||
var parsed = Hash.parsePadUrl(data.href);
|
||||
var parsed2 = Hash.parsePadUrl(currentPad.href);
|
||||
var hash = parsed.hash;
|
||||
var name = data.title;
|
||||
var secret = Hash.getSecrets('file', hash, data.password);
|
||||
var src = fileHost + Hash.getBlobPathFromHex(secret.channel);
|
||||
var key = secret.keys && secret.keys.cryptKey;
|
||||
|
||||
var u8;
|
||||
var res;
|
||||
var mode;
|
||||
if (parsed2.type === 'poll') { optsPut.initialState = '{}'; }
|
||||
|
||||
var val;
|
||||
Nthen(function(waitFor) {
|
||||
Util.fetch(src, waitFor(function (err, _u8) {
|
||||
if (err) { return void waitFor.abort(); }
|
||||
u8 = _u8;
|
||||
}));
|
||||
}).nThen(function (waitFor) {
|
||||
require(["/file/file-crypto.js"], waitFor(function (FileCrypto) {
|
||||
FileCrypto.decrypt(u8, key, waitFor(function (err, _res) {
|
||||
if (err || !_res.content) { return void waitFor.abort(); }
|
||||
res = _res;
|
||||
}));
|
||||
}));
|
||||
}).nThen(function (waitFor) {
|
||||
var ext = Util.parseFilename(data.title).ext;
|
||||
if (!ext) {
|
||||
mode = "text";
|
||||
Nthen(function(_waitFor) {
|
||||
// If pad, use cryptget
|
||||
if (parsed.hashData && parsed.hashData.type === 'pad') {
|
||||
var optsGet = {
|
||||
password: data.password,
|
||||
initialState: parsed.type === 'poll' ? '{}' : undefined
|
||||
};
|
||||
Crypt.get(parsed.hash, _waitFor(function (err, _val) {
|
||||
if (err) {
|
||||
_waitFor.abort();
|
||||
return void cb();
|
||||
}
|
||||
try {
|
||||
val = JSON.parse(_val);
|
||||
fixPadMetadata(val, true);
|
||||
} catch (e) {
|
||||
_waitFor.abort();
|
||||
return void cb();
|
||||
}
|
||||
}), optsGet);
|
||||
return;
|
||||
}
|
||||
require(["/common/modes.js"], waitFor(function (Modes) {
|
||||
Modes.list.some(function (fType) {
|
||||
if (fType.ext === ext) {
|
||||
mode = fType.mode;
|
||||
return true;
|
||||
|
||||
var name = data.title;
|
||||
var secret = Hash.getSecrets(parsed.type, parsed.hash, data.password);
|
||||
var src = fileHost + Hash.getBlobPathFromHex(secret.channel);
|
||||
var key = secret.keys && secret.keys.cryptKey;
|
||||
var u8;
|
||||
var res;
|
||||
var mode;
|
||||
|
||||
// Otherwise, it's a text blob "open in code": get blob data & convert format
|
||||
Nthen(function (waitFor) {
|
||||
Util.fetch(src, waitFor(function (err, _u8) {
|
||||
if (err) {
|
||||
_waitFor.abort();
|
||||
return void waitFor.abort();
|
||||
}
|
||||
});
|
||||
}));
|
||||
}).nThen(function (waitFor) {
|
||||
var reader = new FileReader();
|
||||
reader.addEventListener('loadend', waitFor(function (e) {
|
||||
val = {
|
||||
content: e.srcElement.result,
|
||||
highlightMode: mode,
|
||||
metadata: {
|
||||
defaultTitle: name,
|
||||
title: name,
|
||||
type: "code",
|
||||
},
|
||||
};
|
||||
}));
|
||||
reader.readAsText(res.content);
|
||||
u8 = _u8;
|
||||
}));
|
||||
}).nThen(function (waitFor) {
|
||||
require(["/file/file-crypto.js"], waitFor(function (FileCrypto) {
|
||||
FileCrypto.decrypt(u8, key, waitFor(function (err, _res) {
|
||||
if (err || !_res.content) {
|
||||
_waitFor.abort();
|
||||
return void waitFor.abort();
|
||||
}
|
||||
res = _res;
|
||||
}));
|
||||
}));
|
||||
}).nThen(function (waitFor) {
|
||||
var ext = Util.parseFilename(data.title).ext;
|
||||
if (!ext) {
|
||||
mode = "text";
|
||||
return;
|
||||
}
|
||||
require(["/common/modes.js"], waitFor(function (Modes) {
|
||||
Modes.list.some(function (fType) {
|
||||
if (fType.ext === ext) {
|
||||
mode = fType.mode;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}));
|
||||
}).nThen(function (waitFor) {
|
||||
var reader = new FileReader();
|
||||
reader.addEventListener('loadend', waitFor(function (e) {
|
||||
val = {
|
||||
content: e.srcElement.result,
|
||||
highlightMode: mode,
|
||||
metadata: {
|
||||
defaultTitle: name,
|
||||
title: name,
|
||||
type: "code",
|
||||
},
|
||||
};
|
||||
}));
|
||||
reader.readAsText(res.content);
|
||||
}).nThen(_waitFor());
|
||||
}).nThen(function () {
|
||||
Crypt.put(parsed2.hash, JSON.stringify(val), cb, optsPut);
|
||||
Crypt.put(parsed2.hash, JSON.stringify(val), function () {
|
||||
cb();
|
||||
Crypt.get(parsed2.hash, function (err, val) {
|
||||
console.warn(val);
|
||||
});
|
||||
}, optsPut);
|
||||
});
|
||||
|
||||
};
|
||||
|
@ -1906,6 +1949,9 @@ define([
|
|||
// if a pad is created from a file
|
||||
if (sessionStorage[Constants.newPadFileData]) {
|
||||
common.fromFileData = JSON.parse(sessionStorage[Constants.newPadFileData]);
|
||||
var _parsed1 = Hash.parsePadUrl(common.fromFileData.href);
|
||||
var _parsed2 = Hash.parsePadUrl(window.location.href);
|
||||
if (_parsed1.type !== _parsed2.type) { delete common.fromFileData; }
|
||||
delete sessionStorage[Constants.newPadFileData];
|
||||
}
|
||||
|
||||
|
|
|
@ -82,6 +82,7 @@ define([
|
|||
var faRename = 'fa-pencil';
|
||||
var faColor = 'cptools-palette';
|
||||
var faTrash = 'fa-trash';
|
||||
var faCopy = 'fa-clone';
|
||||
var faDelete = 'fa-eraser';
|
||||
var faAccess = 'fa-unlock-alt';
|
||||
var faProperties = 'fa-info-circle';
|
||||
|
@ -436,6 +437,10 @@ define([
|
|||
'data-icon': faTags,
|
||||
}, Messages.fc_hashtag)),
|
||||
$separator.clone()[0],
|
||||
h('li', h('a.cp-app-drive-context-makeacopy.dropdown-item.cp-app-drive-context-editable', {
|
||||
'tabindex': '-1',
|
||||
'data-icon': faCopy,
|
||||
}, Messages.makeACopy)),
|
||||
h('li', h('a.cp-app-drive-context-delete.dropdown-item.cp-app-drive-context-editable', {
|
||||
'tabindex': '-1',
|
||||
'data-icon': faTrash,
|
||||
|
@ -1188,6 +1193,9 @@ define([
|
|||
if (!metadata || !Util.isPlainTextFile(metadata.fileType, metadata.title)) {
|
||||
hide.push('openincode');
|
||||
}
|
||||
if (!metadata.channel || metadata.channel.length > 32) {
|
||||
hide.push('makeacopy'); // Not for blobs
|
||||
}
|
||||
} else if ($element.is('.cp-app-drive-element-sharedf')) {
|
||||
if (containsFolder) {
|
||||
// More than 1 folder selected: cannot create a new subfolder
|
||||
|
@ -1200,6 +1208,7 @@ define([
|
|||
hide.push('openincode');
|
||||
hide.push('hashtag');
|
||||
hide.push('delete');
|
||||
hide.push('makeacopy');
|
||||
//hide.push('deleteowned');
|
||||
} else { // it's a folder
|
||||
if (containsFolder) {
|
||||
|
@ -1214,6 +1223,7 @@ define([
|
|||
hide.push('openincode');
|
||||
hide.push('properties', 'access');
|
||||
hide.push('hashtag');
|
||||
hide.push('makeacopy');
|
||||
}
|
||||
// If we're in the trash, hide restore and properties for non-root elements
|
||||
if (type === "trash" && path && path.length > 4) {
|
||||
|
@ -1250,6 +1260,7 @@ define([
|
|||
hide.push('share');
|
||||
hide.push('savelocal');
|
||||
hide.push('openincode'); // can't because of race condition
|
||||
hide.push('makeacopy');
|
||||
}
|
||||
if (containsFolder && paths.length > 1) {
|
||||
// Cannot open multiple folders
|
||||
|
@ -1267,11 +1278,11 @@ define([
|
|||
break;
|
||||
case 'tree':
|
||||
show = ['open', 'openro', 'openincode', 'expandall', 'collapseall',
|
||||
'color', 'download', 'share', 'savelocal', 'rename', 'delete',
|
||||
'deleteowned', 'removesf', 'properties', 'access', 'hashtag'];
|
||||
'color', 'download', 'share', 'savelocal', 'rename', 'delete', 'makeacopy',
|
||||
'deleteowned', 'removesf', 'properties', 'hashtag'];
|
||||
break;
|
||||
case 'default':
|
||||
show = ['open', 'openro', 'share', 'openparent', 'delete', 'deleteowned', 'properties', 'access', 'hashtag'];
|
||||
show = ['open', 'openro', 'share', 'openparent', 'delete', 'deleteowned', 'properties', 'access', 'hashtag', 'makeacopy'];
|
||||
break;
|
||||
case 'trashtree': {
|
||||
show = ['empty'];
|
||||
|
@ -3984,6 +3995,35 @@ define([
|
|||
openFile(el, true);
|
||||
});
|
||||
}
|
||||
else if ($this.hasClass('cp-app-drive-context-makeacopy')) {
|
||||
if (paths.length !== 1) { return; }
|
||||
el = manager.find(paths[0].path);
|
||||
var _metadata = manager.getFileData(el);
|
||||
var _simpleData = {
|
||||
title: _metadata.filename || _metadata.title,
|
||||
href: _metadata.href || _metadata.roHref,
|
||||
password: _metadata.password,
|
||||
channel: _metadata.channel,
|
||||
};
|
||||
nThen(function (waitFor) {
|
||||
var path = currentPath;
|
||||
if (path[0] !== ROOT) { path = [ROOT]; }
|
||||
common.sessionStorage.put(Constants.newPadFileData, JSON.stringify(_simpleData), waitFor());
|
||||
common.sessionStorage.put(Constants.newPadPathKey, path, waitFor());
|
||||
common.sessionStorage.put(Constants.newPadTeamKey, APP.team, waitFor());
|
||||
}).nThen(function () {
|
||||
var parsed = Hash.parsePadUrl(_metadata.href || _metadata.roHref);
|
||||
common.openURL(Hash.hashToHref('', parsed.type));
|
||||
// We need to restore sessionStorage for the next time we want to create a pad from this tab
|
||||
// NOTE: the 100ms timeout is to fix a race condition in firefox where sessionStorage
|
||||
// would be deleted before the new tab was created
|
||||
setTimeout(function () {
|
||||
common.sessionStorage.put(Constants.newPadFileData, '', function () {});
|
||||
common.sessionStorage.put(Constants.newPadPathKey, '', function () {});
|
||||
common.sessionStorage.put(Constants.newPadTeamKey, '', function () {});
|
||||
}, 100);
|
||||
});
|
||||
}
|
||||
else if ($this.hasClass('cp-app-drive-context-openincode')) {
|
||||
if (paths.length !== 1) { return; }
|
||||
var p = paths[0];
|
||||
|
|
|
@ -674,6 +674,9 @@ define([
|
|||
$hist.addClass('cp-hidden-if-readonly');
|
||||
toolbar.$drawer.append($hist);
|
||||
|
||||
var $copy = common.createButton('copy', true);
|
||||
toolbar.$drawer.append($copy);
|
||||
|
||||
if (!cpNfInner.metadataMgr.getPrivateData().isTemplate) {
|
||||
var templateObj = {
|
||||
rt: cpNfInner.chainpad,
|
||||
|
|
|
@ -801,6 +801,20 @@ define([
|
|||
Cryptpad.saveAsTemplate(Cryptget.put, data, cb);
|
||||
});
|
||||
|
||||
sframeChan.on('EV_MAKE_A_COPY', function () {
|
||||
var data = {
|
||||
channel: secret.channel,
|
||||
href: currentPad.href,
|
||||
password: password,
|
||||
title: currentTitle
|
||||
};
|
||||
sessionStorage[Utils.Constants.newPadFileData] = JSON.stringify(data);
|
||||
window.open(window.location.pathname);
|
||||
setTimeout(function () {
|
||||
delete sessionStorage[Utils.Constants.newPadFileData];
|
||||
}, 100);
|
||||
});
|
||||
|
||||
// Messaging
|
||||
sframeChan.on('Q_SEND_FRIEND_REQUEST', function (data, cb) {
|
||||
Cryptpad.messaging.sendFriendRequest(data, cb);
|
||||
|
|
|
@ -1297,5 +1297,17 @@
|
|||
"profile_login": "Du musst dich einloggen, um diesen Benutzer zu deinen Kontakten hinzuzufügen",
|
||||
"safeLinks_error": "Dieser Link gibt dir keinen Zugriff auf das Dokument",
|
||||
"settings_safeLinksCheckbox": "Sichere Links aktivieren",
|
||||
"settings_safeLinksTitle": "Sichere Links"
|
||||
"settings_safeLinksTitle": "Sichere Links",
|
||||
"settings_trimHistoryHint": "Spare Speicherplatz, indem du den Verlauf deines CryptDrives und der Benachrichtigungen löschst. Dies hat keinen Einfluss auf den Verlauf deiner Pads. Du kannst den Verlauf der Pads in deren Eigenschaften-Dialog löschen.",
|
||||
"trimHistory_noHistory": "Kein Verlauf kann gelöscht werden",
|
||||
"settings_trimHistoryTitle": "Verlauf löschen",
|
||||
"trimHistory_currentSize": "Aktuelle Größe des Verlaufs: <b>{0}</b>",
|
||||
"trimHistory_needMigration": "Bitte <a>aktualisiere dein CryptDrive</a>, um diese Funktion zu aktivieren.",
|
||||
"trimHistory_success": "Verlauf wurde gelöscht",
|
||||
"trimHistory_error": "Fehler beim Löschen des Verlaufs",
|
||||
"trimHistory_getSizeError": "Bei der Berechnung der Größe des Verlaufs deines CryptDrives ist ein Fehler aufgetreten",
|
||||
"trimHistory_button": "Verlauf löschen",
|
||||
"historyTrim_contentsSize": "Inhalte: {0}",
|
||||
"historyTrim_historySize": "Verlauf: {0}",
|
||||
"areYouSure": "Bist du sicher?"
|
||||
}
|
||||
|
|
|
@ -1309,5 +1309,7 @@
|
|||
"trimHistory_button": "Effacer l'historique",
|
||||
"historyTrim_contentsSize": "Contenu : {0}",
|
||||
"historyTrim_historySize": "Historique : {0}",
|
||||
"areYouSure": "Êtes-vous sûr ?"
|
||||
"areYouSure": "Êtes-vous sûr ?",
|
||||
"copy_title": "{0} (copie)",
|
||||
"makeACopy": "Créer une copie"
|
||||
}
|
||||
|
|
|
@ -537,5 +537,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"whatis_zeroknowledge_p2": "Quando ti registri e accedi, il tuo nome utente e la tua password vengono computati in una chiave segreta utilizzando la <a href=\"https://en.wikipedia.org/wiki/Scrypt\">funzione di derivazione scrypt</a>. Ne questa chiave, ne il tuo nome utente o la tua password vengono inviati al server. Infatti sono usati soltanto dal lato client per decriptare il contenuto del tuo CryptDrive, che contiene le chiavi per tutti i pad a cui hai accesso."
|
||||
"whatis_zeroknowledge_p2": "Quando ti registri e accedi, il tuo nome utente e la tua password vengono computati in una chiave segreta utilizzando la <a href=\"https://en.wikipedia.org/wiki/Scrypt\">funzione di derivazione scrypt</a>. Ne questa chiave, ne il tuo nome utente o la tua password vengono inviati al server. Infatti sono usati soltanto dal lato client per decriptare il contenuto del tuo CryptDrive, che contiene le chiavi per tutti i pad a cui hai accesso.",
|
||||
"faq_title": "Domande frequenti"
|
||||
}
|
||||
|
|
|
@ -1309,5 +1309,7 @@
|
|||
"trimHistory_currentSize": "Current history size: <b>{0}</b>",
|
||||
"trimHistory_noHistory": "No history can be deleted",
|
||||
"settings_trimHistoryTitle": "Delete History",
|
||||
"settings_trimHistoryHint": "Save storage space by deleting the history of your drive and notifications. This will not affect the history of your pads. You can delete the history of pads in their properties dialog."
|
||||
"settings_trimHistoryHint": "Save storage space by deleting the history of your drive and notifications. This will not affect the history of your pads. You can delete the history of pads in their properties dialog.",
|
||||
"makeACopy": "Make a copy",
|
||||
"copy_title": "{0} (copy)"
|
||||
}
|
||||
|
|
|
@ -1186,6 +1186,9 @@ define([
|
|||
$rightside.append($templateButton);
|
||||
}
|
||||
|
||||
var $copy = common.createButton('copy', true);
|
||||
$drawer.append($copy);
|
||||
|
||||
/* add an export button */
|
||||
var $export = common.createButton('export', true, {}, exportFile);
|
||||
$drawer.append($export);
|
||||
|
|
Loading…
Reference in New Issue