Merge branch 'sessionStorage' into rebrand

pull/1/head
yflory 4 years ago
commit 3f67cb05ef

@ -290,14 +290,7 @@ button.primary:hover{
].join('');
var built = false;
// XXX
var types = ['less', 'drive', 'migrate', 'sf', 'team', 'pad', 'end'];
Messages.loading_state_0 = "Less";
Messages.loading_state_1 = "Drive";
Messages.loading_state_2 = "Migrate";
Messages.loading_state_3 = "SF";
Messages.loading_state_4 = "Team";
Messages.loading_state_5 = "Pad";
var current;
var makeList = function (data) {
var c = types.indexOf(data.type);

@ -28,6 +28,19 @@ define([
};
var Nacl = window.nacl;
var redirectTo = '/drive/';
var setRedirectTo = function () {
var parsed = Hash.parsePadUrl(window.location.href);
if (parsed.hashData && parsed.hashData.newPadOpts) {
var newPad = Hash.decodeDataOptions(parsed.hashData.newPadOpts);
redirectTo = newPad.href;
}
};
if (window.location.hash) {
setRedirectTo();
}
var allocateBytes = Exports.allocateBytes = function (bytes) {
var dispense = Cred.dispenser(bytes);
@ -118,11 +131,11 @@ define([
};
var setMergeAnonDrive = function () {
sessionStorage.migrateAnonDrive = 1;
Exports.mergeAnonDrive = 1;
};
var setCreateReadme = function () {
sessionStorage.createReadme = 1;
Exports.createReadme = 1;
};
Exports.loginOrRegister = function (uname, passwd, isRegister, shouldImport, cb) {
@ -416,12 +429,20 @@ define([
});
};
Exports.redirect = function () {
if (sessionStorage.redirectTo) {
var h = sessionStorage.redirectTo;
if (redirectTo) {
var h = redirectTo;
var loginOpts = {};
if (Exports.mergeAnonDrive) {
loginOpts.mergeAnonDrive = 1;
}
if (Exports.createReadme) {
loginOpts.createReadme = 1;
}
h = Hash.getLoginURL(h, loginOpts);
var parser = document.createElement('a');
parser.href = h;
if (parser.origin === window.location.origin) {
delete sessionStorage.redirectTo;
window.location.href = h;
return;
}

@ -68,14 +68,6 @@ define([
target: '_blank',
rel: 'noopener noreferrer'
}, h('button.cp-features-register-button', Msg.features_f_subscribe));
/*$(premiumButton).click(function (e) {
if (LocalStore.isLoggedIn()) { return; }
// Not logged in: go to /login with a redirect to this page
e.preventDefault();
e.stopPropagation();
sessionStorage.redirectTo = '/features.html';
window.location.href = '/login/';
});*/
var anonymousFeatures =
h('div.col-12.col-sm-4.cp-anon-user',[

@ -4,12 +4,13 @@ define([
'/common/hyperscript.js',
'/common/common-feedback.js',
'/common/common-interface.js',
'/common/common-hash.js',
'/common/textFit.min.js',
'/customize/messages.js',
'/customize/application_config.js',
'/common/outer/local-store.js',
'/customize/pages.js'
], function ($, Config, h, Feedback, UI, TextFit, Msg, AppConfig, LocalStore, Pages) {
], function ($, Config, h, Feedback, UI, Hash, TextFit, Msg, AppConfig, LocalStore, Pages) {
var urlArgs = Config.requireConf.urlArgs;
var isAvailableType = function (x) {
@ -46,8 +47,9 @@ define([
var href = '/'+ x[0] +'/';
var attr = isEnabled ? { href: href } : {
onclick: function () {
sessionStorage.redirectTo = href;
window.location.href = '/login/';
var href = Hash.hashToHref('', 'login');
var url = Hash.getNewPadURL(href, { href: href });
window.location.href = url;
}
};
if (!isEnabled) {

@ -9,10 +9,6 @@
max-height: none;
overflow: visible;
display: block;
@page {
margin: 0;
size: landscape;
}
// Slide app
body.cp-app-slide {
display: block;
@ -48,11 +44,15 @@
// Code app
body.cp-app-code {
display: block;
height: auto;
* {
visibility: hidden;
height: auto;
max-height: none;
}
.cp-toolbar-userlist-drawer {
display: none;
}
#cme_toolbox {
display: none;
}
@ -64,6 +64,7 @@
#cp-app-code-preview {
display: block;
#cp-app-code-print {
font-size: 20px;
display: block;
overflow: visible !important;
width: 100%;

@ -0,0 +1,5 @@
@page {
margin: 0;
size: A4 landscape;
}

@ -0,0 +1,4 @@
@page {
margin: 3cm;
size: A4 portrait;
}

@ -1,9 +0,0 @@
<!DOCTYPE html>
<html class="cp">
<head>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<script data-bootload="main.js" data-main="/common/boot.js" src="/bower_components/requirejs/require.js"></script>
</head>
<body class="html">
</body>
</html>

@ -1,193 +0,0 @@
define([
'jquery',
'/api/config',
'/common/cryptget.js',
'/common/pinpad.js',
'/common/common-constants.js',
'/common/common-hash.js',
'/common/outer/local-store.js',
'/common/outer/login-block.js',
'/common/outer/network-config.js',
'/customize/login.js',
'/common/test.js',
'/bower_components/nthen/index.js',
'/bower_components/netflux-websocket/netflux-client.js',
'/bower_components/tweetnacl/nacl-fast.min.js'
], function ($, ApiConfig, Crypt, Pinpad, Constants, Hash, LocalStore, Block, NetConfig, Login, Test, nThen, Netflux) {
var Nacl = window.nacl;
var signMsg = function (msg, privKey) {
var signKey = Nacl.util.decodeBase64(privKey);
var buffer = Nacl.util.decodeUTF8(msg);
return Nacl.util.encodeBase64(Nacl.sign(buffer, signKey));
};
// TODO: Allow authing for any domain as long as the user clicks an "accept" button
// inside of the iframe.
var AUTHORIZED_DOMAINS = [
/\.cryptpad\.fr$/,
/^http(s)?:\/\/localhost\:/
];
// Safari is weird about localStorage in iframes but seems to let sessionStorage slide.
localStorage[Constants.userHashKey] = localStorage[Constants.userHashKey] ||
sessionStorage[Constants.userHashKey];
var proxy;
var rpc;
var network;
var rpcError;
var contacts = {};
var loadProxy = function (hash) {
nThen(function (waitFor) {
var wsUrl = NetConfig.getWebsocketURL();
var w = waitFor();
Netflux.connect(wsUrl).then(function (_network) {
network = _network;
w();
}, function (err) {
rpcError = err;
console.error(err);
});
}).nThen(function (waitFor) {
Crypt.get(hash, waitFor(function (err, val) {
if (err) {
waitFor.abort();
console.error(err);
return;
}
try {
var parsed = JSON.parse(val);
proxy = parsed;
} catch (e) {
console.log("Can't parse user drive", e);
}
}), {
network: network
});
}).nThen(function () {
var origin = ApiConfig.fileHost || window.location.origin;
// Get contacts and extract their avatar channel and key
var getData = function (obj, href) {
var parsed = Hash.parsePadUrl(href);
if (!parsed || parsed.type !== "file") { return; }
var secret = Hash.getSecrets('file', parsed.hash);
if (!secret.keys || !secret.channel) { return; }
obj.avatarKey = Hash.encodeBase64(secret.keys && secret.keys.cryptKey);
obj.avatarSrc = origin + Hash.getBlobPathFromHex(secret.channel);
};
contacts.teams = proxy.teams || {};
contacts.friends = proxy.friends || {};
Object.keys(contacts.friends).map(function (key) {
var friend = contacts.friends[key];
if (!friend) { return; }
var ret = {
edPublic: friend.edPublic,
name: friend.displayName,
};
getData(ret, friend.avatar);
contacts.friends[key] = ret;
});
Object.keys(contacts.teams).map(function (key) {
var team = contacts.teams[key];
if (!team) { return; }
var avatar = team.metadata && team.metadata.avatar;
var ret = {
edPublic: team.keys && team.keys.drive && team.keys.drive.edPublic,
name: team.metadata && team.metadata.name
};
getData(ret, avatar);
contacts.teams[key] = ret;
});
contacts.origin = window.location.origin;
}).nThen(function (waitFor) {
if (!network) { return void waitFor.abort(); }
Pinpad.create(network, proxy, waitFor(function (e, call) {
if (e) {
rpcError = e;
return void waitFor.abort();
}
rpc = call;
}));
}).nThen(function () {
Test(function () {
// This is only here to maybe trigger an error.
window.drive = proxy['drive'];
Test.passed();
});
});
};
var whenReady = function (cb) {
if (proxy && (rpc || rpcError)) { return void cb(); }
console.log('CryptPad not ready...');
setTimeout(function () {
whenReady(cb);
}, 100);
};
$(window).on("message", function (jqe) {
var evt = jqe.originalEvent;
var data = JSON.parse(evt.data);
var domain = evt.origin;
var srcWindow = evt.source;
var ret = { txid: data.txid };
console.log('CP receiving', data);
if (data.cmd === 'PING') {
ret.res = 'PONG';
} else if (data.cmd === 'LOGIN') {
Login.loginOrRegister(data.data.name, data.data.password, false, false, function (err) {
if (err) {
ret.error = 'LOGIN_ERROR';
srcWindow.postMessage(JSON.stringify(ret), domain);
return;
}
loadProxy(LocalStore.getUserHash());
srcWindow.postMessage(JSON.stringify(ret), domain);
});
return;
} else if (data.cmd === 'SIGN') {
if (!AUTHORIZED_DOMAINS.filter(function (x) { return x.test(domain); }).length) {
ret.error = "UNAUTH_DOMAIN";
} else if (!LocalStore.isLoggedIn()) {
ret.error = "NOT_LOGGED_IN";
} else {
return void whenReady(function () {
var sig = signMsg(data.data, proxy.edPrivate);
ret.res = {
uname: proxy.login_name,
edPublic: proxy.edPublic,
sig: sig
};
ret.contacts = contacts;
srcWindow.postMessage(JSON.stringify(ret), domain);
});
}
} else if (data.cmd === 'UPDATE_LIMIT') {
return void whenReady(function () {
if (rpcError) {
// Tell the user on accounts that there was an issue and they need to wait maximum 24h or contact an admin
ret.warning = true;
srcWindow.postMessage(JSON.stringify(ret), domain);
return;
}
rpc.updatePinLimits(function (e, limit, plan, note) {
if (e) {
ret.warning = true;
}
ret.res = [limit, plan, note];
srcWindow.postMessage(JSON.stringify(ret), domain);
});
});
} else {
ret.error = "UNKNOWN_CMD";
}
srcWindow.postMessage(JSON.stringify(ret), domain);
});
var userHash = LocalStore.getUserHash();
if (userHash) {
loadProxy(userHash);
}
});

@ -136,7 +136,6 @@
#cp-app-code-print {
position: relative;
display: none;
margin: 50px;
.markdown_preformatted-code;
.markdown_gfm-table(black);
}

@ -42,6 +42,7 @@ define([
'cm/addon/fold/comment-fold',
'cm/addon/display/placeholder',
'css!/customize/src/print.css',
'less!/code/app-code.less'
], function (

@ -43,6 +43,9 @@ define([
console.error("Require.js threw a Script Error. This probably means you're missing a dependency for CryptPad.\nIt is recommended that the admin of this server runs `bower install && bower update` to get the latest code, then modify their cache version.\nBest of luck,\nThe CryptPad Developers");
return void console.log();
}
if (window.CryptPad_loadingError) {
window.CryptPad_loadingError(e);
}
throw e;
};

@ -5,10 +5,6 @@ define(['/customize/application_config.js'], function (AppConfig) {
userNameKey: 'User_name',
blockHashKey: 'Block_hash',
fileHashKey: 'FS_hash',
// sessionStorage
newPadPathKey: "newPadPath",
newPadTeamKey: "newPadTeam",
newPadFileData: "newPadFileData",
// Store
displayNameKey: 'cryptpad.username',
oldStorageKey: 'CryptPad_RECENTPADS',

@ -169,6 +169,28 @@ Version 1
/code/#/1/edit/3Ujt4F2Sjnjbis6CoYWpoQ/usn4+9CqVja8Q7RZOGTfRgqI
*/
var getLoginOpts = function (hashArr) {
var k;
// Check if we have a ownerKey for this pad
hashArr.some(function (data) {
if (/^login=/.test(data)) {
k = data.slice(6);
return true;
}
});
return k || '';
};
var getNewPadOpts = function (hashArr) {
var k;
// Check if we have a ownerKey for this pad
hashArr.some(function (data) {
if (/^newpad=/.test(data)) {
k = data.slice(7);
return true;
}
});
return k || '';
};
var getVersionHash = function (hashArr) {
var k;
// Check if we have a ownerKey for this pad
@ -202,21 +224,52 @@ Version 1
parsed.present = options.indexOf('present') !== -1;
parsed.embed = options.indexOf('embed') !== -1;
parsed.versionHash = getVersionHash(options);
parsed.newPadOpts = getNewPadOpts(options);
parsed.loginOpts = getLoginOpts(options);
parsed.ownerKey = getOwnerKey(options);
};
// Version 4: only login or newpad options, smae for all the apps
if (hashArr[1] && hashArr[1] === '4') {
parsed.getHash = function (opts) {
if (!opts || !Object.keys(opts).length) { return ''; }
var hash = '/4/';
if (opts.newPadOpts) { hash += 'newpad=' + opts.newPadOpts + '/'; }
if (opts.loginOpts) { hash += 'login=' + opts.loginOpts + '/'; }
return hash;
};
parsed.getOptions = function () {
var options = {};
if (parsed.newPadOpts) { options.newPadOpts = parsed.newPadOpts; }
if (parsed.loginOpts) { options.loginOpts = parsed.loginOpts; }
return options;
};
parsed.version = 4;
options = hashArr.slice(2);
addOptions();
return parsed;
}
// The other versions depends on the type
if (['media', 'file', 'user', 'invite'].indexOf(type) === -1) {
parsed.type = 'pad';
parsed.getHash = function () { return hash; };
parsed.getHash = function () {
return hash;
};
parsed.getOptions = function () {
return {
embed: parsed.embed,
present: parsed.present,
ownerKey: parsed.ownerKey,
versionHash: parsed.versionHash,
newPadOpts: parsed.newPadOpts,
loginOpts: parsed.loginOpts,
password: parsed.password
};
};
if (hash.slice(0,1) !== '/' && hash.length >= 56) { // Version 0
// Old hash
parsed.channel = hash.slice(0, 32);
@ -237,6 +290,8 @@ Version 1
if (versionHash) {
hash += 'hash=' + Crypto.b64RemoveSlashes(versionHash) + '/';
}
if (opts.newPadOpts) { hash += 'newpad=' + opts.newPadOpts + '/'; }
if (opts.loginOpts) { hash += 'login=' + opts.loginOpts + '/'; }
return hash;
};
@ -284,6 +339,8 @@ Version 1
embed: parsed.embed,
present: parsed.present,
ownerKey: parsed.ownerKey,
newPadOpts: parsed.newPadOpts,
loginOpts: parsed.loginOpts,
password: parsed.password
};
};
@ -295,6 +352,8 @@ Version 1
if (parsed.password || opts.password) { hash += 'p/'; }
if (opts.embed) { hash += 'embed/'; }
if (opts.present) { hash += 'present/'; }
if (opts.newPadOpts) { hash += 'newpad=' + opts.newPadOpts + '/'; }
if (opts.loginOpts) { hash += 'login=' + opts.loginOpts + '/'; }
return hash;
};
@ -367,14 +426,26 @@ Version 1
var idx;
// When we start without a hash, use version 4 links to add login or newpad options
var getHash = function (opts) {
if (!opts || !Object.keys(opts).length) { return ''; }
var hash = '/4/';
if (opts.newPadOpts) { hash += 'newpad=' + opts.newPadOpts + '/'; }
if (opts.loginOpts) { hash += 'login=' + opts.loginOpts + '/'; }
return hash;
};
ret.getUrl = function (options) {
options = options || {};
var url = '/';
if (!ret.type) { return url; }
url += ret.type + '/';
// New pad with options: append the options to the hash
if (!ret.hashData && options && Object.keys(options).length) {
return url + '#' + getHash(options);
}
if (!ret.hashData) { return url; }
if (ret.hashData.type !== 'pad') { return url + '#' + ret.hash; }
if (ret.hashData.version === 0) { return url + '#' + ret.hash; }
//if (ret.hashData.version === 0) { return url + '#' + ret.hash; }
//if (ret.hashData.type !== 'pad') { return url + '#' + ret.hash; }
var hash = ret.hashData.getHash(options);
url += '#' + hash;
return url;
@ -523,7 +594,6 @@ Version 1
secret = JSON.parse(JSON.stringify(secret));
if (!secret.keys && !secret.key) {
console.error('e');
return hashes;
} else if (!secret.keys) {
secret.keys = {};
@ -575,6 +645,9 @@ Version 1
// Valid hash?
if (parsed.hash) {
if (!parsed.hashData) { return; }
// New pad: only newPadOpts allowed
if (Object.keys(parsed.hashData).length === 1 &&
parsed.hashData.newPadOpts) { return true; }
// Version should be a number
if (typeof(parsed.hashData.version) === "undefined") { return; }
// pads and files should have a base64 (or hex) key
@ -586,6 +659,29 @@ Version 1
return true;
};
Hash.decodeDataOptions = function (opts) {
var b64 = decodeURIComponent(opts);
var str = Nacl.util.encodeUTF8(Nacl.util.decodeBase64(b64));
return JSON.parse(str);
};
Hash.encodeDataOptions = function (opts) {
var str = JSON.stringify(opts);
var b64 = Nacl.util.encodeBase64(Nacl.util.decodeUTF8(str));
return encodeURIComponent(b64);
};
Hash.getNewPadURL = function (href, opts) {
var parsed = Hash.parsePadUrl(href);
var options = parsed.getOptions();
options.newPadOpts = Hash.encodeDataOptions(opts);
return parsed.getUrl(options);
};
Hash.getLoginURL = function (href, opts) {
var parsed = Hash.parsePadUrl(href);
var options = parsed.getOptions();
options.loginOpts = Hash.encodeDataOptions(opts);
return parsed.getUrl(options);
};
return Hash;
};

@ -281,12 +281,10 @@ define([
var $root = $t.parent();
Messages.add = "Add"; // XXX
Messages.edit = "Edit"; // XXX
var $input = $root.find('.token-input');
var $button = $(h('button.btn.btn-primary', [
h('i.fa.fa-plus'),
h('span', Messages.add)
h('span', Messages.tag_add)
]));
@ -311,7 +309,7 @@ define([
}
$form.append($input);
$form.append($button);
if (isEdit) { $button.find('span').text(Messages.edit); }
if (isEdit) { $button.find('span').text(Messages.tag_edit); }
else { $button.find('span').text(Messages.add); }
$container.append($form);
$input.focus();
@ -942,8 +940,6 @@ define([
$loading.removeClass('cp-loading-hidden');
$loading.removeClass('cp-loading-transparent');
if (config.newProgress) {
// XXX re-add progress bar for step 6 after password prompt for PPP
// XXX also burn after reading
var progress = h('div.cp-loading-progress', [
h('p.cp-loading-progress-list'),
h('p.cp-loading-progress-container')

@ -248,20 +248,15 @@ define([
className: 'secondary',
name: Messages.login_register,
onClick: function () {
common.setLoginRedirect(function () {
common.gotoURL('/register/');
});
common.setLoginRedirect('register');
}
}, {
className: 'secondary',
name: Messages.login_login,
onClick: function () {
common.setLoginRedirect(function () {
common.gotoURL('/login/');
});
common.setLoginRedirect('login');
}
}
]
}]
};
}
};
@ -633,6 +628,7 @@ define([
button
.click(common.prepareFeedback(type))
.click(function () {
if (callback) { return void callback(); }
UIElements.openTemplatePicker(common, true);
});
break;
@ -1802,9 +1798,7 @@ define([
attributes: {'class': 'cp-toolbar-menu-login fa fa-sign-in'},
content: h('span', Messages.login_login),
action: function () {
Common.setLoginRedirect(function () {
window.parent.location = origin+'/login/';
});
Common.setLoginRedirect('login');
},
});
options.push({
@ -1812,9 +1806,7 @@ define([
attributes: {'class': 'cp-toolbar-menu-register fa fa-user-plus'},
content: h('span', Messages.login_register),
action: function () {
Common.setLoginRedirect(function () {
window.parent.location = origin+'/register/';
});
Common.setLoginRedirect('register');
},
});
}
@ -1939,8 +1931,6 @@ define([
$modal.find('.cp-modal').append($title);
$modal.find('.cp-modal').append($description);
var $advanced;
var $container = $('<div>');
var i = 0;
var types = AppConfig.availablePadTypes.filter(function (p) {
@ -1964,15 +1954,7 @@ define([
$element.attr('data-type', p);
$element.click(function () {
$modal.hide();
if ($advanced && Util.isChecked($advanced)) {
common.sessionStorage.put(Constants.displayPadCreationScreen, true, function (){
common.openURL('/' + p + '/');
});
return;
}
common.sessionStorage.put(Constants.displayPadCreationScreen, "", function () {
common.openURL('/' + p + '/');
});
common.openURL('/' + p + '/');
});
});
@ -1997,12 +1979,6 @@ define([
}
return;
}
if (e.which === 32 && $advanced) {
$advanced.prop('checked', !$advanced.prop('checked'));
$modal.focus();
e.stopPropagation();
e.preventDefault();
}
});
@ -2109,7 +2085,7 @@ define([
var sframeChan = common.getSframeChannel();
var metadataMgr = common.getMetadataMgr();
var privateData = metadataMgr.getPrivateData();
var type = metadataMgr.getMetadataLazy().type;
var type = metadataMgr.getMetadataLazy().type || privateData.app;
var fromFileData = privateData.fromFileData;
var $body = $('body');
@ -2284,12 +2260,14 @@ define([
icon: h('span.cptools.cptools-new-template')
});
}
allData.unshift({
name: Messages.creation_noTemplate,
id: 0,
//icon: h('span.fa.fa-file')
icon: UI.getFileIcon({type: type})
});
if (!privateData.newTemplate) {
allData.unshift({
name: Messages.creation_noTemplate,
id: 0,
//icon: h('span.fa.fa-file')
icon: UI.getFileIcon({type: type})
});
}
var redraw = function (index) {
if (index < 0) { i = 0; }
else if (index > allData.length - 1) { return; }
@ -2765,13 +2743,8 @@ define([
$(link).click(function (e) {
e.preventDefault();
e.stopPropagation();
if (msg.content.password) {
common.sessionStorage.put('newPadPassword', msg.content.password, function () {
common.openURL(msg.content.href);
});
return;
}
common.openURL(msg.content.href);
var obj = { pw: msg.content.password || '' };
common.openURL(Hash.getNewPadURL(msg.content.href, obj));
});
var div = h('div', [

@ -148,6 +148,21 @@ define([
send();
};
common.setTabHref = function (href) {
var ohc = window.onhashchange;
window.onhashchange = function () {};
window.location.href = href;
window.onhashchange = ohc;
ohc({reset: true});
};
common.setTabHash = function (hash) {
var ohc = window.onhashchange;
window.onhashchange = function () {};
window.location.hash = hash;
window.onhashchange = ohc;
ohc({reset: true});
};
// RESTRICTED
// Settings only
common.resetDrive = function (cb) {
@ -673,6 +688,7 @@ define([
if (!val) {
return void cb('ENOENT');
}
if (data.oo) { return void cb(val); } // OnlyOffice template: are handled in inner
try {
// Try to fix the title before importing the template
var parsed = JSON.parse(val);
@ -1864,8 +1880,9 @@ define([
LocalStore.logout();
// redirect them to log in, and come back when they're done.
sessionStorage.redirectTo = currentPad.href;
window.location.href = '/login/';
var href = Hash.hashToHref('', 'login');
var url = Hash.getNewPadURL(href, { href: currentPad.href });
window.location.href = url;
};
common.startAccountDeletion = function (data, cb) {
@ -2089,26 +2106,6 @@ define([
language: common.getLanguage(),
driveEvents: true //rdyCfg.driveEvents // Boolean
};
// 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.hashData.type === 'pad') {
if (_parsed1.type !== _parsed2.type) { delete common.fromFileData; }
}
delete sessionStorage[Constants.newPadFileData];
}
if (sessionStorage[Constants.newPadPathKey]) {
common.initialPath = sessionStorage[Constants.newPadPathKey];
delete sessionStorage[Constants.newPadPathKey];
}
if (sessionStorage[Constants.newPadTeamKey]) {
common.initialTeam = sessionStorage[Constants.newPadTeamKey];
delete sessionStorage[Constants.newPadTeamKey];
}
var channelIsReady = waitFor();
@ -2281,12 +2278,6 @@ define([
if (data.anonHash && !cfg.userHash) { LocalStore.setFSHash(data.anonHash); }
/*if (cfg.userHash && sessionStorage) {
// copy User_hash into sessionStorage because cross-domain iframes
// on safari replaces localStorage with sessionStorage or something
sessionStorage.setItem(Constants.userHashKey, cfg.userHash);
}*/
if (cfg.userHash) {
var localToken = tryParsing(localStorage.getItem(Constants.tokenKey));
if (localToken === null) {
@ -2341,21 +2332,18 @@ define([
postMessage("DISCONNECT");
});
}).nThen(function (waitFor) {
if (sessionStorage.createReadme) {
if (common.createReadme) {
var data = {
driveReadme: Messages.driveReadme,
driveReadmeTitle: Messages.driveReadmeTitle,
};
postMessage("CREATE_README", data, waitFor(function (e) {
if (e && e.error) { return void console.error(e.error); }
delete sessionStorage.createReadme;
}));
}
}).nThen(function (waitFor) {
if (sessionStorage.migrateAnonDrive) {
common.mergeAnonDrive(waitFor(function() {
delete sessionStorage.migrateAnonDrive;
}));
if (common.migrateAnonDrive) {
common.mergeAnonDrive(waitFor());
}
}).nThen(function (waitFor) {
if (AppConfig.afterLogin) {

@ -1125,6 +1125,15 @@ define([
var hiddenHref = Hash.hashToHref(hash, parsed.type);
window.open(APP.origin + hiddenHref);
};
var openIn = function (type, path, team, fData) {
var obj = {
p: path,
t: team,
d: fData
};
var href = Hash.hashToHref('', type);
common.openURL(Hash.getNewPadURL(href, obj));
};
var refresh = APP.refresh = function () {
APP.displayDirectory(currentPath);
@ -2667,12 +2676,7 @@ define([
.click(function () {
var type = $(this).attr('data-type') || 'pad';
var path = manager.isPathIn(currentPath, [TRASH]) ? '' : currentPath;
nThen(function (waitFor) {
common.sessionStorage.put(Constants.newPadPathKey, path, waitFor());
common.sessionStorage.put(Constants.newPadTeamKey, APP.team, waitFor());
}).nThen(function () {
common.openURL('/' + type + '/');
});
openIn(type, path, APP.team);
});
};
var createNewButton = function (isInRoot, $container) {
@ -4227,60 +4231,37 @@ define([
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) {
(function () {
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 _metadata = manager.getFileData(el);
var _simpleData = {
title: _metadata.filename || _metadata.title,
href: _metadata.href || _metadata.roHref,
password: _metadata.password,
channel: _metadata.channel,
};
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);
});
openIn(parsed.type, path, APP.team, _simpleData);
})();
}
else if ($this.hasClass('cp-app-drive-context-openincode')) {
if (paths.length !== 1) { return; }
var p = paths[0];
el = manager.find(p.path);
var metadata = manager.getFileData(el);
var simpleData = {
title: metadata.filename || metadata.title,
href: metadata.href,
password: metadata.password,
channel: metadata.channel,
};
nThen(function (waitFor) {
common.sessionStorage.put(Constants.newPadFileData, JSON.stringify(simpleData), waitFor());
common.sessionStorage.put(Constants.newPadPathKey, currentPath, waitFor());
common.sessionStorage.put(Constants.newPadTeamKey, APP.team, waitFor());
}).nThen(function () {
common.openURL('/code/');
// 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);
});
(function () {
var path = currentPath;
if (path[0] !== ROOT) { path = [ROOT]; }
var _metadata = manager.getFileData(el);
var _simpleData = {
title: _metadata.filename || _metadata.title,
href: _metadata.href || _metadata.roHref,
password: _metadata.password,
channel: _metadata.channel,
};
openIn('code', path, APP.team, _simpleData);
})();
}
else if ($this.hasClass('cp-app-drive-context-expandall') ||
$this.hasClass('cp-app-drive-context-collapseall')) {
if (paths.length !== 1) { return; }
@ -4482,12 +4463,7 @@ define([
else if ($this.hasClass("cp-app-drive-context-newdoc")) {
var ntype = $this.data('type') || 'pad';
var path2 = manager.isPathIn(currentPath, [TRASH]) ? '' : currentPath;
nThen(function (waitFor) {
common.sessionStorage.put(Constants.newPadPathKey, path2, waitFor());
common.sessionStorage.put(Constants.newPadTeamKey, APP.team, waitFor());
}).nThen(function () {
common.openURL('/' + ntype + '/');
});
openIn(ntype, path2, APP.team);
}
else if ($this.hasClass("cp-app-drive-context-properties")) {
if (type === 'trash') {

@ -7,8 +7,7 @@ define([
'/common/common-util.js',
'/common/common-constants.js',
'/customize/messages.js',
'/bower_components/nthen/index.js'
], function($, h, Hash, UI, UIElements, Util, Constants, Messages, nThen) {
], function($, h, Hash, UI, UIElements, Util, Constants, Messages) {
var handlers = {};
@ -108,21 +107,13 @@ define([
return Messages._getKey(key, [name, title, teamName]);
};
content.handler = function() {
var todo = function() {
common.openURL(msg.content.href);
defaultDismiss(common, data)();
var obj = {
p: msg.content.isTemplate ? ['template'] : undefined,
t: teamNotification || undefined,
pw: msg.content.password || ''
};
nThen(function(waitFor) {
if (msg.content.isTemplate) {
common.sessionStorage.put(Constants.newPadPathKey, ['template'], waitFor());
}
if (teamNotification) {
common.sessionStorage.put(Constants.newPadTeamKey, teamNotification, waitFor());
}
common.sessionStorage.put('newPadPassword', msg.content.password || '', waitFor());
}).nThen(function() {
todo();
});
common.openURL(Hash.getNewPadURL(msg.content.href, obj));
defaultDismiss(common, data)();
};
if (!content.archived) {
content.dismissHandler = defaultDismiss(common, data);

@ -114,6 +114,7 @@ define([
};
var getEditor = function () {
if (!window.frames || !window.frames[0]) { return; }
return window.frames[0].editor || window.frames[0].editorCell;
};
@ -416,7 +417,7 @@ define([
clearTimeout(pendingChanges[key]);
delete pendingChanges[key];
});
if (APP.stopHistory) { APP.history = false; }
if (APP.stopHistory || APP.template) { APP.history = false; }
startOO(blob, type, true);
};
@ -426,14 +427,15 @@ define([
var file = getFileType();
blob.name = (metadataMgr.getMetadataLazy().title || file.doc) + '.' + file.type;
var data = {
hash: APP.history ? ooChannel.historyLastHash : ooChannel.lastHash,
index: APP.history ? ooChannel.currentIndex : ooChannel.cpIndex
hash: (APP.history || APP.template) ? ooChannel.historyLastHash : ooChannel.lastHash,
index: (APP.history || APP.template) ? ooChannel.currentIndex : ooChannel.cpIndex
};
fixSheets();
ooChannel.ready = false;
ooChannel.queue = [];
data.callback = function () {
if (APP.template) { APP.template = false; }
resetData(blob, file);
};
@ -458,15 +460,11 @@ define([
var actions = h('div', [cancel, register, login]);
var modal = UI.cornerPopup(Messages.oo_login, actions, '', {alt: true});
$(register).click(function () {
common.setLoginRedirect(function () {
common.gotoURL('/register/');
});
common.setLoginRedirect('register');
modal.delete();
});
$(login).click(function () {
common.setLoginRedirect(function () {
common.gotoURL('/login/');
});
common.setLoginRedirect('login');
modal.delete();
});
$(cancel).click(function () {
@ -1296,6 +1294,13 @@ define([
}
}
if (APP.template) {
getEditor().setViewModeDisconnect();
UI.removeLoadingScreen();
makeCheckpoint(true);
return;
}
if (APP.history) {
try {
getEditor().asc_setRestriction(true);
@ -1829,6 +1834,108 @@ define([
pinImages();
};
var loadCp = function (cp, keepQueue) {
loadLastDocument(cp, function () {
var file = getFileType();
var type = common.getMetadataMgr().getPrivateData().ooType;
var blob = loadInitDocument(type, true);
if (!keepQueue) { ooChannel.queue = []; }
resetData(blob, file);
}, function (blob, file) {
if (!keepQueue) { ooChannel.queue = []; }
resetData(blob, file);
});
};
var loadTemplate = function (href, pw, parsed) {
APP.history = true;
APP.template = true;
var editor = getEditor();
if (editor) { editor.setViewModeDisconnect(); }
var content = parsed.content;
// Get checkpoint
var hashes = content.hashes || {};
var idx = sortCpIndex(hashes);
var lastIndex = idx[idx.length - 1];
var lastCp = hashes[lastIndex] || {};
// Current cp or initial hash (invalid hash ==> initial hash)
var toHash = lastCp.hash || 'NONE';
// Last hash
var fromHash = 'NONE';
sframeChan.query('Q_GET_HISTORY_RANGE', {
href: href,
password: pw,
channel: content.channel,
lastKnownHash: fromHash,
toHash: toHash,
}, function (err, data) {
if (err) { return void console.error(err); }
if (!Array.isArray(data.messages)) { return void console.error('Not an array!'); }
// The first "cp" in history is the empty doc. It doesn't include the first patch
// of the history
var initialCp = !lastCp.hash;
var messages = (data.messages || []).slice(initialCp ? 0 : 1);
ooChannel.queue = messages.map(function (obj) {
return {
hash: obj.serverHash,
msg: JSON.parse(obj.msg)
};
});
ooChannel.historyLastHash = ooChannel.lastHash;
ooChannel.currentIndex = ooChannel.cpIndex;
loadCp(lastCp, true);
});
};
var openTemplatePicker = function () {
var metadataMgr = common.getMetadataMgr();
var type = metadataMgr.getPrivateData().app;
var sframeChan = common.getSframeChannel();
var pickerCfgInit = {
types: [type],
where: ['template'],
hidden: true
};
var pickerCfg = {
types: [type],
where: ['template'],
};
var onConfirm = function () {
common.openFilePicker(pickerCfg, function (data) {
if (data.type !== type) { return; }
UI.addLoadingScreen({hideTips: true});
sframeChan.query('Q_OO_TEMPLATE_USE', {
href: data.href,
}, function (err, val) {
var parsed;
try {
parsed = JSON.parse(val);
} catch (e) {
console.error(e, val);
UI.removeLoadingScreen();
return void UI.warn(Messages.error);
}
console.error(data);
loadTemplate(data.href, data.password, parsed);
});
});
};
sframeChan.query("Q_TEMPLATE_EXIST", type, function (err, data) {
if (data) {
common.openFilePicker(pickerCfgInit);
onConfirm();
} else {
UI.alert(Messages.template_empty);
}
});
};
config.onInit = function (info) {
var privateData = metadataMgr.getPrivateData();
@ -1850,6 +1957,7 @@ define([
Title.setToolbar(toolbar);
if (window.CP_DEV_MODE) {
var $save = common.createButton('save', true, {}, function () {
makeCheckpoint(true);
});
@ -1866,18 +1974,6 @@ define([
APP.stopHistory = true;
makeCheckpoint(true);
};
var loadCp = function (cp, keepQueue) {
loadLastDocument(cp, function () {
var file = getFileType();
var type = common.getMetadataMgr().getPrivateData().ooType;
var blob = loadInitDocument(type, true);
if (!keepQueue) { ooChannel.queue = []; }
resetData(blob, file);
}, function (blob, file) {
if (!keepQueue) { ooChannel.queue = []; }
resetData(blob, file);
});
};
var onPatch = function (patch) {
// Patch on the current cp
ooChannel.send(JSON.parse(patch.msg));
@ -1971,6 +2067,10 @@ define([
load: loadSnapshot
});
toolbar.$drawer.append($snapshot);
// Import template
var $template = common.createButton('importtemplate', true, {}, openTemplatePicker);
$template.appendTo(toolbar.$drawer);
})();
}
@ -2129,11 +2229,18 @@ define([
openRtChannel(function () {
setMyId();
oldHashes = JSON.parse(JSON.stringify(content.hashes));
loadDocument(newDoc, useNewDefault);
initializing = false;
common.openPadChat(APP.onLocal);
if (APP.startWithTemplate) {
var template = APP.startWithTemplate;
loadTemplate(template.href, template.password, template.content);
return;
}
loadDocument(newDoc, useNewDefault);
setEditable(!readOnly);
UI.removeLoadingScreen();
common.openPadChat(APP.onLocal);
});
};
@ -2257,8 +2364,11 @@ define([
}));
SFCommon.create(waitFor(function (c) { APP.common = common = c; }));
}).nThen(function (waitFor) {
common.getSframeChannel().on('EV_OO_TEMPLATE', function (data) {
APP.startWithTemplate = data;
});
common.handleNewFile(waitFor, {
noTemplates: true
//noTemplates: true
});
}).nThen(function (/*waitFor*/) {
andThen(common);

@ -55,7 +55,7 @@ define([
var addData = function (obj) {
obj.ooType = window.location.pathname.replace(/^\//, '').replace(/\/$/, '');
obj.ooVersionHash = version;
obj.ooForceVersion = localStorage.CryptPad_ooVersion || sessionStorage.CryptPad_ooVersion || "";
obj.ooForceVersion = localStorage.CryptPad_ooVersion || "";
};
var addRpc = function (sframeChan, Cryptpad, Utils) {
sframeChan.on('Q_OO_SAVE', function (data, cb) {

@ -2647,6 +2647,7 @@ define([
classic: true,
};
var rt = window.rt = Listmap.create(listmapConfig);
store.driveSecret = secret;
store.proxy = rt.proxy;
store.loggedIn = typeof(data.userHash) !== "undefined";
@ -2729,7 +2730,6 @@ define([
* - userHash or anonHash
* Todo in cb
* - LocalStore.setFSHash if needed
* - sessionStorage.User_Hash
* - stuff with tokenKey
* Event to outer
* - requestLogin

@ -82,19 +82,6 @@ define([
localStorage.setItem(Constants.userNameKey, name);
if (cb) { cb(); }
};
var eraseTempSessionValues = LocalStore.eraseTempSessionValues = function () {
// delete sessionStorage values that might have been left over
// from the main page's /user redirect
[
'login',
'login_user',
'login_pass',
'login_rmb',
'register'
].forEach(function (k) {
delete sessionStorage[k];
});
};
var logoutHandlers = [];
LocalStore.logout = function (cb, isDeletion) {
[
@ -104,10 +91,8 @@ define([
'loginToken',
'plan',
].forEach(function (k) {
sessionStorage.removeItem(k);
localStorage.removeItem(k);
delete localStorage[k];
delete sessionStorage[k];
});
try {
Object.keys(localStorage || {}).forEach(function (k) {
@ -122,7 +107,6 @@ define([
if (!LocalStore.getFSHash()) {
LocalStore.setFSHash(Hash.createRandomHash('drive'));
}
eraseTempSessionValues();
if (!isDeletion) {
logoutHandlers.forEach(function (h) {

@ -2,7 +2,8 @@ define([
'/common/common-messaging.js',
'/common/common-hash.js',
'/common/common-util.js',
], function (Messaging, Hash, Util) {
'/bower_components/chainpad-crypto/crypto.js',
], function (Messaging, Hash, Util, Crypto) {
// Random timeout between 10 and 30 times your sync time (lag + chainpad sync)
var getRandomTimeout = function (ctx) {
@ -221,6 +222,11 @@ define([
toRemove = old.data;
}
if (content.password) {
var key = ctx.store.driveSecret.keys.cryptKey;
content.password = Crypto.encrypt(content.password, key);
}
// Update the data
channels[channel] = {
mode: mode,
@ -315,6 +321,11 @@ define([
var channel = content.channel || content.teamChannel;
if (content.password) {
var key = ctx.store.driveSecret.keys.cryptKey;
content.password = Crypto.encrypt(content.password, key);
}
if (addOwners[channel]) { return void cb(true); }
addOwners[channel] = {
type: box.type,

@ -1,166 +0,0 @@
// This file provides the API for the channel for talking to and from the sandbox iframe.
define([
'/common/sframe-protocol.js',
'/common/common-util.js'
], function (SFrameProtocol, Util) {
var mkTxid = function () {
return Math.random().toString(16).replace('0.', '') + Math.random().toString(16).replace('0.', '');
};
var create = function (ow, cb, isSandbox, sendData) {
var otherWindow;
var evReady = Util.mkEvent(true);
var handlers = {};
var queries = {};
// list of handlers which are registered from the other side...
var insideHandlers = [];
var callWhenRegistered = {};
var chan = {};
// Send a query. channel.query('Q_SOMETHING', { args: "whatever" }, function (reply) { ... });
chan.query = function (q, content, cb, opts) {
if (!otherWindow) { throw new Error('not yet initialized'); }
if (!SFrameProtocol[q]) {
throw new Error('please only make queries are defined in sframe-protocol.js');
}
opts = opts || {};
var txid = mkTxid();
var to = opts.timeout || 30000;
var timeout = setTimeout(function () {
delete queries[txid];
console.log("Timeout making query " + q);
}, to);
queries[txid] = function (data, msg) {
clearTimeout(timeout);
delete queries[txid];
cb(undefined, data.content, msg);
};
evReady.reg(function () {
otherWindow.postMessage(JSON.stringify({
txid: txid,
content: content,
q: q
}), '*');
});
};
// Fire an event. channel.event('EV_SOMETHING', { args: "whatever" });
var event = chan.event = function (e, content) {
if (!SFrameProtocol[e]) {
throw new Error('please only fire events that are defined in sframe-protocol.js');
}
if (e.indexOf('EV_') !== 0) {
throw new Error('please only use events (starting with EV_) for event messages');
}
evReady.reg(function () {
otherWindow.postMessage(JSON.stringify({ content: content, q: e }), '*');
});
};
// Be notified on query or event. channel.on('EV_SOMETHING', function (args, reply) { ... });
// If the type is a query, your handler will be invoked with a reply function that takes
// one argument (the content to reply with).
chan.on = function (queryType, handler, quiet) {
if (!SFrameProtocol[queryType]) {
throw new Error('please only register handlers which are defined in sframe-protocol.js');
}
(handlers[queryType] = handlers[queryType] || []).push(function (data, msg) {
handler(data.content, function (replyContent) {
if (queryType.indexOf('Q_') !== 0) { throw new Error("replies to events are invalid"); }
msg.source.postMessage(JSON.stringify({
txid: data.txid,
content: replyContent
}), '*');
}, msg);
});
if (!quiet) {
event('EV_REGISTER_HANDLER', queryType);
}
};
// If a particular handler is registered, call the callback immediately, otherwise it will be called
// when that handler is first registered.
// channel.whenReg('Q_SOMETHING', function () { ...query Q_SOMETHING?... });
chan.whenReg = function (queryType, cb, always) {
if (!SFrameProtocol[queryType]) {
throw new Error('please only register handlers which are defined in sframe-protocol.js');
}
var reg = always;
if (insideHandlers.indexOf(queryType) > -1) {
cb();
} else {
reg = true;
}
if (reg) {
(callWhenRegistered[queryType] = callWhenRegistered[queryType] || []).push(cb);
}
};
// Same as whenReg except it will invoke every time there is another registration, not just once.
chan.onReg = function (queryType, cb) { chan.whenReg(queryType, cb, true); };
chan.on('EV_REGISTER_HANDLER', function (content) {
if (callWhenRegistered[content]) {
callWhenRegistered[content].forEach(function (f) { f(); });
delete callWhenRegistered[content];
}
insideHandlers.push(content);
});
chan.whenReg('EV_REGISTER_HANDLER', evReady.fire);
// Make sure both iframes are ready
var isReady =false;
chan.onReady = function (h) {
if (isReady) {
return void h();
}
if (typeof(h) !== "function") { return; }
chan.on('EV_RPC_READY', function () { isReady = true; h(); });
};
chan.ready = function () {
chan.whenReg('EV_RPC_READY', function () {
chan.event('EV_RPC_READY');
});
};
var txid;
window.addEventListener('message', function (msg) {
var data = JSON.parse(msg.data);
if (ow !== msg.source) {
return;
//console.log("DROP Message from unexpected source");
//console.log(msg);
} else if (!otherWindow) {
otherWindow = ow;
sendData = sendData || {};
sendData.txid = data.txid;
ow.postMessage(JSON.stringify(sendData), '*');
cb(chan);
} else if (typeof(data.q) === 'string' && handlers[data.q]) {
handlers[data.q].forEach(function (f) {
f(data || JSON.parse(msg.data), msg);
data = undefined;
});
} else if (typeof(data.q) === 'undefined' && queries[data.txid]) {
queries[data.txid](data, msg);
} else if (data.txid === txid) {
// stray message from init
return;
} else {
console.log("DROP Unhandled message");
console.log(msg);
}
});
if (isSandbox) {
// we're in the sandbox
otherWindow = ow;
evReady.fire();
cb(chan);
}
};
return { create: create };
});

@ -26,7 +26,7 @@ define([
};
var AppConfig;
var Test;
var password;
var password, newPadPassword;
var initialPathInDrive;
var burnAfterReading;
@ -123,6 +123,9 @@ define([
});
SFrameChannel.create(msgEv, postMsg, waitFor(function (sfc) {
Utils.sframeChan = sframeChan = sfc;
window.CryptPad_loadingError = function (e) {
sfc.event('EV_LOADING_ERROR', e)
};
}));
});
window.addEventListener('message', whenReady);
@ -131,12 +134,28 @@ define([
if (sframeChan) { sframeChan.event('EV_LOADING_INFO', data); }
});
try {
var parsed = Utils.Hash.parsePadUrl(currentPad.href);
var options = parsed.getOptions();
if (options.loginOpts) {
var loginOpts = Utils.Hash.decodeDataOptions(options.loginOpts);
if (loginOpts.createReadme) { Cryptpad.createReadme = true; }
if (loginOpts.mergeAnonDrive) { Cryptpad.migrateAnonDrive = true; }
// Remove newPadOpts from the hash
delete options.loginOpts;
currentPad.href = parsed.getUrl(options);
currentPad.hash = parsed.hashData.getHash ? parsed.hashData.getHash(options)
: '';
}
} catch (e) { console.error(e); }
Cryptpad.ready(waitFor(), {
driveEvents: cfg.driveEvents,
currentPad: currentPad
});
if (window.history && window.history.replaceState && currentPad.hash) {
// Remove the login hash if needed
if (window.history && window.history.replaceState && (currentPad.hash || window.location.hash)) {
var nHash = currentPad.hash;
if (!/^#/.test(nHash)) { nHash = '#' + nHash; }
window.history.replaceState({}, window.document.title, nHash);
@ -196,15 +215,53 @@ define([
}
// Rendered (maybe hidden) hash
var renderedParsed = Utils.Hash.parsePadUrl(window.location.href);
var ohc = window.onhashchange;
window.onhashchange = function () {};
window.location.href = renderedParsed.getUrl(opts);
window.onhashchange = ohc;
ohc({reset: true});
Cryptpad.setTabHref(renderedParsed.getUrl(opts));
}
}));
};
// New pad options
var options = parsed.getOptions();
if (options.newPadOpts) {
try {
var newPad = Utils.Hash.decodeDataOptions(options.newPadOpts);
Cryptpad.initialTeam = newPad.t;
Cryptpad.initialPath = newPad.p;
if (newPad.pw) {
try {
var uHash = Utils.LocalStore.getUserHash();
var uSecret = Utils.Hash.getSecrets('drive', uHash);
var uKey = uSecret.keys.cryptKey;
newPadPassword = Crypto.decrypt(newPad.pw, uKey);
} catch (e) { console.error(e); }
}
if (newPad.d) {
Cryptpad.fromFileData = newPad.d;
var _parsed1 = Utils.Hash.parsePadUrl(Cryptpad.fromFileData.href);
if (_parsed1.hashData.type === 'pad' && _parsed1.type !== parsed.type) {
delete Cryptpad.fromFileData;
}
}
} catch (e) {
console.error(e, parsed.hashData.newPadOpts);
}
delete options.newPadOpts;
currentPad.href = parsed.getUrl(options);
currentPad.hash = parsed.hashData.getHash ? parsed.hashData.getHash(options)
: '';
var version = parsed.hashData.version;
parsed = Utils.Hash.parsePadUrl(currentPad.href);
Cryptpad.setTabHash(currentPad.hash);
// If it's a new pad, don't check password
if (version === 4) {
return void todo();
}
// Otherwise, continue
}
if (!parsed.hashData) { // No hash, no need to check for a password
return void todo();
}
@ -335,9 +392,8 @@ define([
password = val;
}), parsed.getUrl());
}).nThen(function (w) {
if (!password && !stored && sessionStorage.newPadPassword) {
passwordCfg.value = sessionStorage.newPadPassword;
delete sessionStorage.newPadPassword;
if (!password && !stored && newPadPassword) {
passwordCfg.value = newPadPassword;
}
if (parsed.type === "file") {
@ -357,7 +413,7 @@ define([
waitFor.abort();
return;
}
if (!isNew) { return void todo(); }
if (!e && !isNew) { return void todo(); }
if (parsed.hashData.mode === 'view' && (password || !parsed.hashData.password)) {
// Error, wrong password stored, the view seed has changed with the password
// password will never work
@ -422,9 +478,6 @@ define([
var defaultTitle = Utils.UserObject.getDefaultName(parsed);
var edPublic, curvePublic, notifications, isTemplate;
var settings = {};
var forceCreationScreen = cfg.useCreationScreen &&
sessionStorage[Utils.Constants.displayPadCreationScreen];
delete sessionStorage[Utils.Constants.displayPadCreationScreen];
var isSafe = ['debug', 'profile', 'drive', 'teams'].indexOf(currentPad.app) !== -1;
var updateMeta = function () {
//console.log('EV_METADATA_UPDATE');
@ -457,6 +510,8 @@ define([
fileHost: ApiConfig.fileHost,
readOnly: readOnly,
isTemplate: isTemplate,
newTemplate: Array.isArray(Cryptpad.initialPath)
&& Cryptpad.initialPath[0] === "template",
feedbackAllowed: Utils.Feedback.state,
isPresent: parsed.hashData && parsed.hashData.present,
isEmbed: parsed.hashData && parsed.hashData.embed,
@ -467,7 +522,6 @@ define([
},
isNewFile: isNewFile,
isDeleted: isNewFile && currentPad.hash.length > 0,
forceCreationScreen: forceCreationScreen,
password: password,
channel: secret.channel,
enableSF: localStorage.CryptPad_SF === "1", // TODO to remove when enabled by default
@ -508,18 +562,10 @@ define([
sframeChan.onReg('EV_METADATA_UPDATE', updateMeta);
Utils.LocalStore.onLogin(function () {
var ohc = window.onhashchange;
window.onhashchange = function () {};
window.location.hash = currentPad.hash;
window.onhashchange = ohc;
ohc({reset: true});
Cryptpad.setTabHash(currentPad.hash);
});
Utils.LocalStore.onLogout(function () {
var ohc = window.onhashchange;
window.onhashchange = function () {};
window.location.hash = currentPad.hash;
window.onhashchange = ohc;
ohc({reset: true});
Cryptpad.setTabHash(currentPad.hash);
sframeChan.event('EV_LOGOUT');
});
@ -603,9 +649,10 @@ define([
Cryptpad.mailbox.execCommand(data, cb);
});
sframeChan.on('Q_SET_LOGIN_REDIRECT', function (data, cb) {
sessionStorage.redirectTo = currentPad.href;
cb();
sframeChan.on('EV_SET_LOGIN_REDIRECT', function (page) {
var href = Utils.Hash.hashToHref('', page);
var url = Utils.Hash.getNewPadURL(href, { href: currentPad.href });
window.location.href = url;
});
sframeChan.on('Q_STORE_IN_TEAM', function (data, cb) {
@ -1033,11 +1080,10 @@ define([
password: password,
title: currentTitle
};
sessionStorage[Utils.Constants.newPadFileData] = JSON.stringify(data);
window.open(window.location.pathname);
setTimeout(function () {
delete sessionStorage[Utils.Constants.newPadFileData];
}, 100);
var obj = { d: data };
var href = window.location.pathname;
var url = Utils.Hash.getNewPadURL(href, obj);
window.open(url);
});
// Messaging
@ -1097,6 +1143,10 @@ define([
nSecret = Utils.Hash.getSecrets('drive', hash, password);
}
}
if (data.href) {
var _parsed = Utils.Hash.parsePadUrl(data.href);
nSecret = Utils.Hash.getSecrets(_parsed.type, _parsed.hash, data.password);
}
var channel = nSecret.channel;
var validate = nSecret.keys.validateKey;
var crypto = Crypto.createEncryptor(nSecret.keys);
@ -1131,15 +1181,6 @@ define([
});
});
sframeChan.on('Q_SESSIONSTORAGE_PUT', function (data, cb) {
if (typeof (data.value) === "undefined") {
delete sessionStorage[data.key];
} else {
sessionStorage[data.key] = data.value;
}
cb();
});
sframeChan.on('Q_IS_ONLY_IN_SHARED_FOLDER', function (data, cb) {
Cryptpad.isOnlyInSharedFolder(secret.channel, function (err, t) {
if (err) { return void cb({error: err}); }
@ -1163,11 +1204,7 @@ define([
var hiddenParsed = Utils.Hash.parsePadUrl(window.location.href);
// Update the hash in the address bar
var ohc = window.onhashchange;
window.onhashchange = function () {};
window.location.href = hiddenParsed.getUrl(opts);
window.onhashchange = ohc;
ohc({reset: true});
Cryptpad.setTabHref(hiddenParsed.getUrl(opts));
});
@ -1277,6 +1314,10 @@ define([
sframeChan.on('Q_TEMPLATE_USE', function (data, cb) {
Cryptpad.useTemplate(data, Cryptget, cb);
});
sframeChan.on('Q_OO_TEMPLATE_USE', function (data, cb) {
data.oo = true;
Cryptpad.useTemplate(data, Cryptget, cb);
});
sframeChan.on('Q_TEMPLATE_EXIST', function (type, cb) {
Cryptpad.listTemplates(type, function (err, templates) {
cb(templates.length > 0);
@ -1514,11 +1555,7 @@ define([
// in the address bar
Cryptpad.padRpc.onChannelDeleted.reg(function (channel) {
if (channel !== secret.channel) { return; }
var ohc = window.onhashchange;
window.onhashchange = function () {};
window.location.href = currentPad.href;
window.onhashchange = ohc;
ohc({reset: true});
Cryptpad.setTabHref(currentPad.href);
});
// Join the netflux channel
@ -1602,13 +1639,9 @@ define([
Utils.crypto = Utils.Crypto.createEncryptor(Utils.secret.keys);
// Update the hash in the address bar
var ohc = window.onhashchange;
window.onhashchange = function () {};
window.location.hash = newHash;
currentPad.hash = newHash;
currentPad.href = '/' + parsed.type + '/#' + newHash;
window.onhashchange = ohc;
ohc({reset: true});
Cryptpad.setTabHash(newHash);
// Update metadata values and send new metadata inside
parsed = Utils.Hash.parsePadUrl(currentPad.href);
@ -1639,19 +1672,44 @@ define([
rtConfig.metadata.validateKey = (secret.keys && secret.keys.validateKey) || undefined;
Utils.rtConfig = rtConfig;
var templatePw;
nThen(function(waitFor) {
if (data.templateId) {
if (data.templateId === -1) {
isTemplate = true;
initialPathInDrive = ['template'];
return;
}
Cryptpad.getPadData(data.templateId, waitFor(function (err, d) {
data.template = d.href;
templatePw = d.password;
}));
}
}).nThen(function () {
var cryptputCfg = $.extend(true, {}, rtConfig, {password: password});
if (data.template) {
// Start OO with a template...
// Cryptget and give href, password and content to inner
if (parsed.type === "sheet") {
var then = function () {
startRealtime(rtConfig);
cb();
};
var _parsed = Utils.Hash.parsePadUrl(data.template);
Cryptget.get(_parsed.hash, function (err, val) {
if (err || !val) { return void then(); }
try {
var parsed = JSON.parse(val);
sframeChan.event('EV_OO_TEMPLATE', {
href: data.template,
password: templatePw,
content: parsed
});
} catch (e) { console.error(e); }
then();
}, {password: templatePw});
return;
}
// Pass rtConfig to useTemplate because Cryptput will create the file and
// we need to have the owners and expiration time in the first line on the
// server

@ -307,9 +307,8 @@ define([
ctx.sframeChan.event('EV_SET_HASH', hash);
};
funcs.setLoginRedirect = function (cb) {
cb = cb || $.noop;
ctx.sframeChan.query('Q_SET_LOGIN_REDIRECT', null, cb);
funcs.setLoginRedirect = function (page) {
ctx.sframeChan.query('EV_SET_LOGIN_REDIRECT', page);
};
funcs.isPresentUrl = function (cb) {
@ -463,15 +462,6 @@ define([
});
};
funcs.sessionStorage = {
put: function (key, value, cb) {
ctx.sframeChan.query('Q_SESSIONSTORAGE_PUT', {
key: key,
value: value
}, cb);
}
};
funcs.setDisplayName = function (name, cb) {
cb = cb || $.noop;
ctx.sframeChan.query('Q_SETTINGS_SET_DISPLAY_NAME', name, cb);
@ -758,9 +748,7 @@ define([
var mustLogin = privateData.registeredOnly;
if (mustLogin) {
UI.alert(Messages.mustLogin, function () {
funcs.setLoginRedirect(function () {
funcs.gotoURL('/login/');
});
funcs.setLoginRedirect('login');
}, {forefront: true});
return;
}

@ -1,271 +0,0 @@
// This file defines all of the RPC calls which are used between the inner and outer iframe.
// Define *querys* (which expect a response) using Q_<query name>
// Define *events* (which expect no response) using EV_<event name>
// Please document the queries and events you create, and please please avoid making generic
// "do stuff" events/queries which are used for many different things because it makes the
// protocol unclear.
//
// WARNING: At this point, this protocol is still EXPERIMENTAL. This is not it's final form.
// We need to define protocol one piece at a time and then when we are satisfied that we
// fully understand the problem, we will define the *right* protocol and this file will be dynomited.
//
define({
// When the iframe first launches, this query is sent repeatedly by the controller
// to wait for it to awake and give it the requirejs config to use.
'Q_INIT': true,
// When either the outside or inside registers a query handler, this is sent.
'EV_REGISTER_HANDLER': true,
// When an iframe is ready to receive messages
'EV_RPC_READY': true,
// Realtime events called from the outside.
// When someone joins the pad, argument is a string with their netflux id.
'EV_RT_JOIN': true,
// When someone leaves the pad, argument is a string with their netflux id.
'EV_RT_LEAVE': true,
// When you have been disconnected, no arguments.
'EV_RT_DISCONNECT': true,
// When you have connected, argument is an object with myID: string, members: list, readOnly: boolean.
'EV_RT_CONNECT': true,
// Called after the history is finished synchronizing, no arguments.
'EV_RT_READY': true,
// Called when the server returns an error in a pad (EEXPIRED, EDELETED).
'EV_RT_ERROR': true,
// Called from both outside and inside, argument is a (string) chainpad message.
'Q_RT_MESSAGE': true,
// Called from the outside, this informs the inside whenever the user's data has been changed.
// The argument is the object representing the content of the user profile minus the netfluxID
// which changes per-reconnect.
'EV_METADATA_UPDATE': true,
// Takes one argument only, the title to set for the CURRENT pad which the user is looking at.
// This changes the pad title in drive ONLY, the pad title needs to be changed inside of the
// iframe and synchronized with the other users. This will not trigger a EV_METADATA_UPDATE
// because the metadata contained in EV_METADATA_UPDATE does not contain the pad title.
// It also sets the page (tab) title to the selected title, unles it is overridden by
// the EV_SET_TAB_TITLE event.
'Q_SET_PAD_TITLE_IN_DRIVE': true,
// Set the page title (tab title) to the selected value which will override the pad title.
// The new title value can contain {title}, which will be replaced by the pad title when it
// is set or modified.
'EV_SET_TAB_TITLE': true,
// Update the user's display-name which will be shown to contacts and people in the same pads.
'Q_SETTINGS_SET_DISPLAY_NAME': true,
// Log the user out in all the tabs
'Q_LOGOUT': true,
// Tell the user that he has been logged out from outside (probably from another tab)
'EV_LOGOUT': true,
// When moving to the login or register page from a pad, we need to redirect to that pad at the
// end of the login process. This query set the current href to the sessionStorage.
'Q_SET_LOGIN_REDIRECT': true,
// Store the editing or readonly link of the current pad to the clipboard (share button).
'Q_STORE_LINK_TO_CLIPBOARD': true,
// Use anonymous rpc from inside the iframe (for avatars & pin usage).
'Q_ANON_RPC_MESSAGE': true,
// Get the user's pin limit, usage and plan
'Q_PIN_GET_USAGE': true,
// Write/update the login block when the account password is changed
'Q_WRITE_LOGIN_BLOCK': true,
// Remove login blocks
'Q_REMOVE_LOGIN_BLOCK': true,
// Check the pin limit to determine if we can store the pad in the drive or if we should.
// display a warning
'Q_GET_PIN_LIMIT_STATUS': true,
// Move a pad to the trash when using the forget button.
'Q_MOVE_TO_TRASH': true,
// Request the full history from the server when the users clicks on the history button.
// Callback is called when the FULL_HISTORY_END message is received in the outside.
'Q_GET_FULL_HISTORY': true,
'Q_GET_HISTORY_RANGE': true,
// When a (full) history message is received from the server.
'EV_RT_HIST_MESSAGE': true,
// Save a pad as a template using the toolbar button
'Q_SAVE_AS_TEMPLATE': true,
// Friend requests from the userlist
'Q_SEND_FRIEND_REQUEST': true, // Up query
'Q_INCOMING_FRIEND_REQUEST': true, // Down query
'EV_FRIEND_REQUEST': true, // Down event when the request is complete
// Set the tab notification when the content of the pad changes
'EV_NOTIFY': true,
// Send the new settings to the inner iframe when they are changed in the proxy
'EV_SETTINGS_UPDATE': true,
// Get and set (pad) attributes stored in the drive from the inner iframe
'Q_GET_ATTRIBUTE': true,
'Q_SET_ATTRIBUTE': true,
'Q_GET_PAD_ATTRIBUTE': true,
'Q_SET_PAD_ATTRIBUTE': true,
// Check if a pad is only in a shared folder or (also) in the main drive.
// This allows us to change the behavior of some buttons (trash icon...)
'Q_IS_ONLY_IN_SHARED_FOLDER': true,
// Open/close the File picker (sent from the iframe to the outside)
'EV_FILE_PICKER_OPEN': true,
'EV_FILE_PICKER_CLOSE': true,
'EV_FILE_PICKER_REFRESH': true,
// File selected in the file picker: sent from the filepicker iframe to the outside
// and then send to the inner iframe
'EV_FILE_PICKED': true,
// Get all the files from the drive to display them in a file picker secure app
'Q_GET_FILES_LIST': true,
// Template picked, replace the content of the pad
'Q_TEMPLATE_USE': true,
// Check if we have template(s) for the selected pad type
'Q_TEMPLATE_EXIST': true,
// File upload queries and events
'Q_UPLOAD_FILE': true,
'EV_FILE_UPLOAD_STATE': true,
'Q_CANCEL_PENDING_FILE_UPLOAD': true,
// Make the browser window navigate to a given URL, if no URL is passed then it will reload.
'EV_GOTO_URL': true,
// Make the parent window open a given URL in a new tab. It allows us to keep sessionStorage
// form the parent window.
'EV_OPEN_URL': true,
// Present mode URL
'Q_PRESENT_URL_GET_VALUE': true,
'EV_PRESENT_URL_SET_VALUE': true,
// Put one or more entries to the cache which will go in localStorage.
// Cache is wiped after each new release
'EV_CACHE_PUT': true,
// Chat
'EV_CHAT_EVENT': true,
'Q_CHAT_COMMAND': true,
'Q_CHAT_OPENPADCHAT': true,
// Cursor
'EV_CURSOR_EVENT': true,
'Q_CURSOR_COMMAND': true,
'Q_CURSOR_OPENCHANNEL': true,
// Put one or more entries to the localStore which will go in localStorage.
'EV_LOCALSTORE_PUT': true,
// Put one entry in the parent sessionStorage
'Q_SESSIONSTORAGE_PUT': true,
// Merge the anonymous drive (FS_hash) into the current logged in user's drive, to keep the pads
// in the drive at registration.
'Q_MERGE_ANON_DRIVE': true,
// Add or remove the avatar from the profile.
// We have to pin/unpin the avatar and store/remove the value from the user object
'Q_PROFILE_AVATAR_ADD': true,
'Q_PROFILE_AVATAR_REMOVE': true,
// Store outside and get thumbnails inside (stored with localForage (indexedDB) outside)
'Q_THUMBNAIL_SET': true,
'Q_THUMBNAIL_GET': true,
// Settings app only
// Clear all thumbnails
'Q_THUMBNAIL_CLEAR': true,
// Backup and restore a drive
'Q_SETTINGS_DRIVE_GET': true,
'Q_SETTINGS_DRIVE_SET': true,
'Q_SETTINGS_DRIVE_RESET': true,
// Logout from all the devices where the account is logged in
'Q_SETTINGS_LOGOUT': true,
// Import pads from this computer's anon session into the current user account
'Q_SETTINGS_IMPORT_LOCAL': true,
'Q_SETTINGS_DELETE_ACCOUNT': true,
// Store the language selected in the iframe into localStorage outside
'Q_LANGUAGE_SET': true,
// Anonymous users can empty their drive and remove FS_hash from localStorage
'EV_BURN_ANON_DRIVE': true,
// Inner drive needs to send command and receive updates from the async store
'Q_DRIVE_USEROBJECT': true,
'Q_DRIVE_GETOBJECT': true,
'Q_DRIVE_RESTORE': true,
// Get the pads deleted from the server by other users to remove them from the drive
'Q_DRIVE_GETDELETED': true,
// Store's userObject need to send log messages to inner to display them in the UI
'EV_DRIVE_LOG': true,
// Refresh the drive when the drive has changed ('change' or 'remove' events)
'EV_DRIVE_CHANGE': true,
'EV_DRIVE_REMOVE': true,
// Set shared folder hash in the address bar
'EV_DRIVE_SET_HASH': true,
// Remove an owned pad from the server
'Q_REMOVE_OWNED_CHANNEL': true,
// Clear an owned pad from the server (preserve metadata)
'Q_CLEAR_OWNED_CHANNEL': true,
// Notifications about connection and disconnection from the network
'EV_NETWORK_DISCONNECT': true,
'EV_NETWORK_RECONNECT': true,
// Reload on new version
'EV_NEW_VERSION': true,
// Pad creation screen: create a pad with the selected attributes (owned, expire)
'Q_CREATE_PAD': true,
// Get the available templates
'Q_CREATE_TEMPLATES': true,
// This is for sending data out of the iframe when we are in testing mode
// The exact protocol is defined in common/test.js
'EV_TESTDATA': true,
// OnlyOffice: save a new version
'Q_OO_SAVE': true,
// Ask for the pad password when a pad is protected
'EV_PAD_PASSWORD': true,
'Q_PAD_PASSWORD_VALUE': true,
// Change pad password
'Q_PAD_PASSWORD_CHANGE': true,
// Migrate drive to owned drive
'Q_CHANGE_USER_PASSWORD': true,
// Loading events to display in the loading screen
'EV_LOADING_INFO': true,
// Critical error outside the iframe during loading screen
'EV_LOADING_ERROR': true,
// Chrome 68 bug...
'EV_CHROME_68': true,
// Get all existing tags
'Q_GET_ALL_TAGS': true,
// Store pads in the drive
'EV_AUTOSTORE_DISPLAY_POPUP': true,
'Q_AUTOSTORE_STORE': true,
'Q_IS_PAD_STORED': true,
// Import mediatag from a pad
'Q_IMPORT_MEDIATAG': true,
// Ability to get a pad's content from its hash
'Q_CRYPTGET': true,
'EV_CRYPTGET_DISCONNECT': true,
});

@ -773,15 +773,11 @@ MessengerUI, Messages) {
]);
$msg.find('a.cp-pnp-login').click(function (ev) {
ev.preventDefault();
Common.setLoginRedirect(function () {
window.parent.location = o + '/login/';
});
Common.setLoginRedirect('login');
});
$msg.find('a.cp-pnp-register').click(function (ev) {
ev.preventDefault();
Common.setLoginRedirect(function () {
window.parent.location = o + '/register/';
});
Common.setLoginRedirect('register');
});
$('.cp-toolbar-top').append($msg);
//UI.addTooltips();

@ -188,12 +188,12 @@
"help_button": "Ohje",
"historyText": "Historia",
"historyButton": "Näytä asiakirjan historia",
"history_next": "Uudempi versio",
"history_prev": "Vanhempi versio",
"history_next": "Seuraava versio",
"history_prev": "Edellinen versio",
"history_loadMore": "Lataa lisää historiatietoja",
"history_closeTitle": "Sulje historia",
"history_restoreTitle": "Palauta asiakirjan valittu versio",
"history_restorePrompt": "Oletko varma, että haluat korvata asiakirjan nykyisen version esitetyllä versiolla?",
"history_restorePrompt": "Oletko varma, että haluat korvata asiakirjan tämänhetkisen version näytetyllä versiolla?",
"history_restoreDone": "Asiakirja palautettu",
"history_version": "Versio:",
"openLinkInNewTab": "Avaa linkki uuteen välilehteen",
@ -452,7 +452,7 @@
"settings_backupHint": "Varmuuskopioi tai palauta CryptDrivesi sisältö kokonaisuudessaan. Varmuuskopio ei sisällä padiesi sisältöä, ainoastaan niiden käyttöön tarvittavat avaimet.",
"settings_backup": "Varmuuskopioi",
"settings_restore": "Palauta",
"settings_backupHint2": "Lataa kaikkien padiesi nykyinen sisältö. Padit ladataan luettavassa tiedostomuodossa, jos sellainen on saatavilla.",
"settings_backupHint2": "Lataa kaikkien asiakirjojen nykyinen sisältö tietokoneellesi. Asiakirjat ladataan muissa sovelluksissa toimivissa tiedostomuodoissa, jos se on mahdollista. Jos sopivaa tiedostomuotoa ei ole saatavilla, asiakirjat ladataan CryptPad-yhteensopivassa tiedostomuodossa.",
"settings_backup2": "Lataa oma CryptDrive tietokoneellesi",
"settings_backup2Confirm": "Tämä lataa kaikki CryptDrivesi padit ja tiedostot tietokoneellesi. Jos haluat jatkaa, valitse nimi ja paina OK",
"settings_exportTitle": "Vie oma CryptDrive",
@ -1176,9 +1176,9 @@
"settings_safeLinksTitle": "Turvalliset linkit",
"settings_cat_security": "Luottamuksellisuus",
"imprint": "Oikeudellinen huomautus",
"oo_sheetMigration_anonymousEditor": "Taulukon muokkaaminen on poistettu käytöstä anonyymeille käyttäjille, kunnes rekisteröitynyt käyttäjä päivittää sen viimeisimpään versioon.",
"oo_sheetMigration_anonymousEditor": "Rekisteröitymättömät käyttäjät eivät voi muokata taulukkoa ennen kuin rekisteröitynyt käyttäjä päivittää sen viimeisimpään versioon.",
"oo_sheetMigration_complete": "Päivitetty versio saatavilla, paina OK ladataksesi uudelleen.",
"oo_sheetMigration_loading": "Päivitetään taulukkoasi viimeisimpään versioon",
"oo_sheetMigration_loading": "Päivitetään taulukkoasi viimeisimpään versioon. Ole hyvä ja odota noin 1 minuutti.",
"oo_exportInProgress": "Vienti menossa",
"oo_importInProgress": "Tuonti menossa",
"oo_invalidFormat": "Tätä tiedostoa ei voida tuoda",
@ -1404,5 +1404,60 @@
"readme_cat1_l2": "Avaa padeja CryptDrivestasi: kaksoisnapsauta padin kuvaketta avataksesi sen.",
"readme_cat1_l1": "Luo padi: Siirry CryptDriveesi, napsauta {0} ja sitten {1}, ja voit luoda padin.",
"readme_cat1": "Tutustu CryptDriveesi",
"readme_p2": "Tämä padi toimii pikaisena perehdytyksenä CryptPadin ominaisuuksiin - kuinka tehdä muistiinpanoja, järjestellä niitä ja tehdä yhteistyötä niiden parissa."
"readme_p2": "Tämä padi toimii pikaisena perehdytyksenä CryptPadin ominaisuuksiin - kuinka tehdä muistiinpanoja, järjestellä niitä ja tehdä yhteistyötä niiden parissa.",
"fm_shareFolderPassword": "Suojaa kansio salasanalla (vapaaehtoinen)",
"access_destroyPad": "Tuhoa tämä asiakirja tai kansio lopullisesti",
"fm_deletedFolder": "Poistettu kansio",
"admin_limitUser": "Käyttäjän julkinen avain",
"team_exportButton": "Lataa",
"team_exportHint": "Lataa kaikki tiimin CryptDriveen tallennetut asiakirjat tietokoneellesi. Asiakirjat ladataan muissa sovelluksissa toimivissa tiedostomuodoissa, jos se on mahdollista. Jos sopivaa tiedostomuotoa ei ole saatavilla, asiakirjat ladataan CryptPad-yhteensopivassa tiedostomuodossa.",
"team_exportTitle": "Lataa tiimin CryptDrive",
"admin_cat_quota": "Käyttäjätallennustila",
"admin_invalLimit": "Virheellinen arvo kiintiökentässä",
"admin_invalKey": "Virheellinen julkinen avain",
"admin_limitSetNote": "Huomautus",
"admin_limitMB": "Kiintiö (megatavuissa)",
"admin_setlimitTitle": "Ota mukautettu kiintiö käyttöön",
"admin_setlimitHint": "Aseta mukautettu tallennustilakiintiö käyttäjälle tämän julkisen avaimen avulla. Voit päivittää tai poistaa olemassaolevan tallennustilakiintiön.",
"admin_limitNote": "Huomautus: {0}",
"admin_limitPlan": "Tilaus: {0}",
"admin_defaultlimitHint": "Tallennustilakiintiö käyttäjien ja tiimien CryptDriveille, joilla ei ole erikseen määriteltyjä sääntöjä",
"admin_defaultlimitTitle": "Tallennustilakiintiö (Mt)",
"admin_getlimitsHint": "Listaa muokatut tallennustilakiintiöt, jotka ovat käytössä CryptPad-palvelimellasi.",
"admin_getlimitsTitle": "Muokatut tallennustilakiintiöt",
"admin_limit": "Nykyinen rajoitus: {0}",
"admin_setlimitButton": "Aseta rajoitus",
"admin_registrationAllow": "Avaa",
"admin_registrationButton": "Sulje",
"admin_registrationTitle": "Sulje rekisteröityminen",
"admin_registrationHint": "Älä salli uusien käyttäjien rekisteröitymistä",
"snapshots_cantMake": "Tilannevedoksen luominen epäonnistui. Yhteytesi on katkennut.",
"snapshots_notFound": "Tämä tilannevedos ei ole enää saatavilla, koska asiakirjan historia on poistettu.",
"snapshot_error_exists": "Tästä versiosta on jo olemassa tilannevedos",
"snapshots_ooPickVersion": "Valitse versio, jotta voit luoda tilannevedoksen",
"oo_version": "Versio: ",
"oo_version_latest": "Viimeisin",
"snapshots_delete": "Poista",
"oo_deletedVersion": "Tämä versio ei ole enää saatavilla historiassa.",
"snapshots_close": "Sulje",
"snapshots_restore": "Palauta",
"snapshots_open": "Avaa",
"snapshots_placeholder": "Tilannevedoksen otsikko",
"snapshots_new": "Uusi tilannevedos",
"snapshots_button": "Tilannevedokset",
"snaphot_title": "Tilannevedos",
"infobar_versionHash": "Katselet asiakirjan vanhaa versiota ({0}).",
"history_restoreDriveDone": "CryptDrive palautettu",
"history_restoreDrivePrompt": "Oletko varma, että haluat korvata CryptDriven nykyisen version valitulla versiolla?",
"history_restoreDriveTitle": "Palauta CryptDrive valitsemaasi versioon",
"history_userNext": "Seuraava laatija",
"history_fastNext": "Seuraava muokkaussessio",
"history_userPrev": "Edellinen laatija",
"history_fastPrev": "Edellinen muokkaussessio",
"share_versionHash": "Olet jakamassa valitsemasi historiaversion asiakirjastasi vain luku-tilassa. Tämä antaa myös <b>katseluoikeuden</b> asiakirjan kaikkiin versioihin.",
"history_shareTitle": "Jaa linkki tähän versioon",
"history_cantRestore": "Palauttaminen epäonnistui. Yhteytesi on katkennut.",
"history_close": "Sulje",
"history_restore": "Palauta",
"share_bar": "Luo linkki"
}

@ -1459,5 +1459,11 @@
"admin_limitUser": "Clé publique de l'utilisateur",
"fm_shareFolderPassword": "Protéger ce dossier avec un mot de passe (optionnel)",
"access_destroyPad": "Détruire ce document ou dossier définitivement",
"fm_deletedFolder": "Dossier supprimé"
"fm_deletedFolder": "Dossier supprimé",
"loading_state_5": "Reconstruction du document",
"loading_state_4": "Chargement des équipes",
"loading_state_3": "Chargement des dossiers partagés",
"loading_state_2": "Mise à jour du contenu",
"loading_state_1": "Chargement du drive",
"loading_state_0": "Construction de l'interface"
}

@ -1459,5 +1459,13 @@
"admin_limitUser": "User's public key",
"fm_deletedFolder": "Deleted folder",
"access_destroyPad": "Destroy this document or folder permanently",
"fm_shareFolderPassword": "Protect this folder with a password (optional)"
"fm_shareFolderPassword": "Protect this folder with a password (optional)",
"loading_state_0": "Build interface",
"loading_state_1": "Load drive",
"loading_state_2": "Update content",
"loading_state_3": "Load shared folders",
"loading_state_4": "Load Teams",
"loading_state_5": "Reconstruct document",
"tag_add": "Add",
"tag_edit": "Edit"
}

@ -53,14 +53,9 @@ define([
};
window.addEventListener('message', onMsg);
}).nThen(function (/*waitFor*/) {
var hash = localStorage[Constants.userHashKey];
var hash = localStorage[Constants.userHashKey] || localStorage[Constants.fileHashKey];
var drive = hash && ('#'+hash === window.location.hash);
if (!window.location.hash) {
if (!hash) {
sessionStorage.redirectTo = '/debug/';
window.location.href = '/login/';
return;
}
drive = true;
window.location.hash = hash;
} else {

@ -46,9 +46,12 @@ define([
window.addEventListener('message', onMsg);
}).nThen(function (/*waitFor*/) {
var afterSecrets = function (Cryptpad, Utils, secret, cb, sframeChan) {
var _hash = hash.slice(1);
if (_hash && Utils.LocalStore.isLoggedIn()) {
// Add a shared folder!
var parsed = Utils.Hash.parsePadUrl(href);
var isSf = parsed.hashData && parsed.hashData.type === 'pad';
if (!isSf) { return void cb(); }
// SF and logged in: add shared folder
if (Utils.LocalStore.isLoggedIn()) {
Cryptpad.addSharedFolder(null, secret, function (id) {
if (id && typeof(id) === "object" && id.error) {
sframeChan.event("EV_RESTRICTED_ERROR");
@ -65,16 +68,16 @@ define([
cb();
});
return;
} else if (_hash) {
var id = Utils.Util.createRandomInteger();
window.CryptPad_newSharedFolder = id;
var data = {
href: Utils.Hash.getRelativeHref(Cryptpad.currentPad.href),
password: secret.password
};
return void Cryptpad.loadSharedFolder(id, data, cb);
}
cb();
// Anon shared folder
var id = Utils.Util.createRandomInteger();
window.CryptPad_newSharedFolder = id;
var data = {
href: Utils.Hash.getRelativeHref(Cryptpad.currentPad.href),
password: secret.password
};
Cryptpad.loadSharedFolder(id, data, cb);
};
var addRpc = function (sframeChan, Cryptpad, Utils) {
sframeChan.on('EV_BURN_ANON_DRIVE', function () {

@ -231,9 +231,7 @@ define([
if (!common.isLoggedIn()) {
UI.removeLoadingScreen();
return UI.alert(Messages.upload_mustLogin, function () {
common.setLoginRedirect(function () {
common.gotoURL('/login/');
});
common.setLoginRedirect('login');
});
}

@ -67,10 +67,8 @@ define([
});
});
$('#register').on('click', function () {
if (sessionStorage) {
if ($uname.val()) {
sessionStorage.login_user = $uname.val();
}
if ($uname.val()) {
localStorage.login_user = $uname.val();
}
window.location.href = '/register/';
});

@ -1,5 +1,4 @@
define(['/bower_components/localforage/dist/localforage.min.js'], function (localForage) {
localForage.clear();
sessionStorage.clear();
localStorage.clear();
});

@ -1,5 +1,5 @@
<!DOCTYPE html>
<html class="cp-app-noscroll">
<html class="cp-app-noscroll cp-app-print">
<head>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<script async data-bootload="/pad/inner.js" data-main="/common/sframe-boot.js?ver=1.6" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>

@ -47,6 +47,7 @@ define([
'/bower_components/diff-dom/diffDOM.js',
'css!/customize/src/print.css',
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
'less!/pad/app-pad.less'
@ -500,6 +501,12 @@ define([
var mkPrintButton = function (framework, editor) {
var $printButton = framework._.sfCommon.createButton('print', true);
$printButton.click(function () {
/*
// NOTE: alternative print system in case we keep having more issues on Firefox
var $iframe = $('html').find('iframe');
var iframe = $iframe[0].contentWindow;
iframe.print();
*/
editor.execCommand('print');
framework.feedback('PRINT_PAD');
});

@ -611,15 +611,11 @@ define([
var actions = h('div', [cancel, register, login]);
var modal = UI.cornerPopup(Messages.profile_login, actions, '', {alt: true});
$(register).click(function () {
common.setLoginRedirect(function () {
common.gotoURL('/register/');
});
common.setLoginRedirect('register');
modal.delete();
});
$(login).click(function () {
common.setLoginRedirect(function () {
common.gotoURL('/login/');
});
common.setLoginRedirect('login');
modal.delete();
});
$(cancel).click(function () {

@ -37,9 +37,9 @@ define([
var $passwd = $('#password');
var $confirm = $('#password-confirm');
if (sessionStorage.login_user) {
delete sessionStorage.login_user;
$uname.val(sessionStorage.login_user);
if (localStorage.login_user) {
$uname.val(localStorage.login_user);
delete loginStorage.login_user;
}
[ $uname, $passwd, $confirm]

@ -124,6 +124,7 @@ define([
}
sframeChan.event("EV_SECURE_ACTION", {
type: parsed.type,
password: data.password,
href: data.url,
name: data.name
});

@ -74,6 +74,7 @@ define([
};
window.addEventListener('message', whenReady);
}).nThen(function () {
var isTemplate = config.data.isTemplate;
var updateMeta = function () {
//console.log('EV_METADATA_UPDATE');
var metaObj;
@ -96,7 +97,7 @@ define([
feedbackAllowed: Utils.Feedback.state,
hashes: config.data.hashes,
password: config.data.password,
isTemplate: config.data.isTemplate,
isTemplate: isTemplate,
file: config.data.file,
};
for (var k in additionalPriv) { metaObj.priv[k] = additionalPriv[k]; }

@ -15,6 +15,7 @@ define([
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
'css!/customize/src/print-landscape.css',
'less!/slide/app-slide.less',
'css!cm/lib/codemirror.css',

@ -1267,14 +1267,10 @@ define([
anonRegister = h('button.btn.btn-secondary', Messages.login_register),
]));
$(anonLogin).click(function () {
common.setLoginRedirect(function () {
common.gotoURL('/login/');
});
common.setLoginRedirect('login');
});
$(anonRegister).click(function () {
common.setLoginRedirect(function () {
common.gotoURL('/register/');
});
common.setLoginRedirect('register');
});
waitFor.abort();
}).nThen(function () {
@ -1402,9 +1398,7 @@ define([
var hash = privateData.teamInviteHash;
if (!hash && !driveAPP.loggedIn) {
UI.alert(Messages.mustLogin, function () {
common.setLoginRedirect(function () {
common.gotoURL('/login/');
});
common.setLoginRedirect('login');
}, {forefront: true});
return;
}

Loading…
Cancel
Save