Merge branch 'coati' into kanban

This commit is contained in:
yflory
2018-05-16 11:41:55 +02:00
114 changed files with 4160 additions and 1595 deletions

View File

@@ -223,6 +223,33 @@ define([
hd.type === 'invite');
}, "test support for invite urls");
// test support for V2
assert(function (cb) {
var parsed = Hash.parsePadUrl('/pad/#/2/pad/edit/oRE0oLCtEXusRDyin7GyLGcS/');
var secret = Hash.getSecrets('pad', '/2/pad/edit/oRE0oLCtEXusRDyin7GyLGcS/');
return cb(parsed.hashData.version === 2 &&
parsed.hashData.mode === "edit" &&
parsed.hashData.type === "pad" &&
parsed.hashData.key === "oRE0oLCtEXusRDyin7GyLGcS" &&
secret.channel === "d8d51b4aea863f3f050f47f8ad261753" &&
window.nacl.util.encodeBase64(secret.keys.cryptKey) === "0Ts1M6VVEozErV2Nx/LTv6Im5SCD7io2LlhasyyBPQo=" &&
secret.keys.validateKey === "f5A1FM9Gp55tnOcM75RyHD1oxBG9ZPh9WDA7qe2Fvps=" &&
!parsed.hashData.present);
}, "test support for version 2 hash failed to parse");
assert(function (cb) {
var parsed = Hash.parsePadUrl('/pad/#/2/pad/edit/HGu0tK2od-2BBnwAz2ZNS-t4/p/embed');
var secret = Hash.getSecrets('pad', '/2/pad/edit/HGu0tK2od-2BBnwAz2ZNS-t4/p/embed', 'pewpew');
return cb(parsed.hashData.version === 2 &&
parsed.hashData.mode === "edit" &&
parsed.hashData.type === "pad" &&
parsed.hashData.key === "HGu0tK2od-2BBnwAz2ZNS-t4" &&
secret.channel === "3fb6dc93807d903aff390b5f798c92c9" &&
window.nacl.util.encodeBase64(secret.keys.cryptKey) === "EeCkGJra8eJgVu7v4Yl2Hc3yUjrgpKpxr0Lcc3bSWVs=" &&
secret.keys.validateKey === "WGkBczJf2V6vQZfAScz8V1KY6jKdoxUCckrD+E75gGE=" &&
parsed.hashData.embed &&
parsed.hashData.password);
}, "test support for password in version 2 hash failed to parse");
assert(function (cb) {
var url = '/pad/?utm_campaign=new_comment&utm_medium=email&utm_source=thread_mailer#/1/edit/3Ujt4F2Sjnjbis6CoYWpoQ/usn4+9CqVja8Q7RZOGTfRgqI/';
var secret = Hash.parsePadUrl(url);

View File

@@ -334,6 +334,7 @@ define([
//var cursor = editor.getCursor();
//var cleanName = data.name.replace(/[\[\]]/g, '');
//var text = '!['+cleanName+']('+data.url+')';
// PASSWORD_FILES
var parsed = Hash.parsePadUrl(data.url);
var hexFileName = Util.base64ToHex(parsed.hashData.channel);
var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName;

View File

@@ -34,7 +34,7 @@ define([
url: '/common/feedback.html?NO_LOCALSTORAGE=' + (+new Date()),
});
});
window.alert("CryptPad needs localStorage to work, try a different browser");
window.alert("CryptPad needs localStorage to work. Try changing your cookie permissions, or using a different browser");
};
window.onerror = function (e) {

View File

@@ -19,7 +19,50 @@ define([
.decodeUTF8(JSON.stringify(list))));
};
var getEditHashFromKeys = Hash.getEditHashFromKeys = function (chanKey, keys) {
var getEditHashFromKeys = Hash.getEditHashFromKeys = function (secret) {
var version = secret.version;
var data = secret.keys;
if (version === 0) {
return secret.channel + secret.key;
}
if (version === 1) {
if (!data.editKeyStr) { return; }
return '/1/edit/' + hexToBase64(secret.channel) +
'/' + Crypto.b64RemoveSlashes(data.editKeyStr) + '/';
}
if (version === 2) {
if (!data.editKeyStr) { return; }
var pass = secret.password ? 'p/' : '';
return '/2/' + secret.type + '/edit/' + Crypto.b64RemoveSlashes(data.editKeyStr) + '/' + pass;
}
};
var getViewHashFromKeys = Hash.getViewHashFromKeys = function (secret) {
var version = secret.version;
var data = secret.keys;
if (version === 0) { return; }
if (version === 1) {
if (!data.viewKeyStr) { return; }
return '/1/view/' + hexToBase64(secret.channel) +
'/'+Crypto.b64RemoveSlashes(data.viewKeyStr)+'/';
}
if (version === 2) {
if (!data.viewKeyStr) { return; }
var pass = secret.password ? 'p/' : '';
return '/2/' + secret.type + '/view/' + Crypto.b64RemoveSlashes(data.viewKeyStr) + '/' + pass;
}
};
var getFileHashFromKeys = Hash.getFileHashFromKeys = function (secret) {
var version = secret.version;
var data = secret.keys;
if (version === 0) { return; }
if (version === 1) {
return '/1/' + hexToBase64(secret.channel) + '/' +
Crypto.b64RemoveSlashes(data.fileKeyStr) + '/';
}
};
// V1
/*var getEditHashFromKeys = Hash.getEditHashFromKeys = function (chanKey, keys) {
if (typeof keys === 'string') {
return chanKey + keys;
}
@@ -34,7 +77,7 @@ define([
};
var getFileHashFromKeys = Hash.getFileHashFromKeys = function (fileKey, cryptKey) {
return '/1/' + hexToBase64(fileKey) + '/' + Crypto.b64RemoveSlashes(cryptKey) + '/';
};
};*/
Hash.getUserHrefFromKeys = function (origin, username, pubkey) {
return origin + '/user/#/1/' + username + '/' + pubkey.replace(/\//g, '-');
};
@@ -43,6 +86,24 @@ define([
return s.replace(/\/+/g, '/');
};
Hash.createChannelId = function () {
var id = uint8ArrayToHex(Crypto.Nacl.randomBytes(16));
if (id.length !== 32 || /[^a-f0-9]/.test(id)) {
throw new Error('channel ids must consist of 32 hex characters');
}
return id;
};
Hash.createRandomHash = function (type, password) {
var cryptor = Crypto.createEditCryptor2(void 0, void 0, password);
return getEditHashFromKeys({
password: Boolean(password),
version: 2,
type: type,
keys: { editKeyStr: cryptor.editKeyStr }
});
};
/*
Version 0
/pad/#67b8385b07352be53e40746d2be6ccd7XAYSuJYYqa9NfmInyHci7LNy
@@ -56,25 +117,56 @@ Version 1
var hashArr = fixDuplicateSlashes(hash).split('/');
if (['media', 'file', 'user', 'invite'].indexOf(type) === -1) {
parsed.type = 'pad';
if (hash.slice(0,1) !== '/' && hash.length >= 56) {
if (hash.slice(0,1) !== '/' && hash.length >= 56) { // Version 0
// Old hash
parsed.channel = hash.slice(0, 32);
parsed.key = hash.slice(32, 56);
parsed.version = 0;
parsed.getHash = function () { return hash; };
return parsed;
}
if (hashArr[1] && hashArr[1] === '1') {
var options;
if (hashArr[1] && hashArr[1] === '1') { // Version 1
parsed.version = 1;
parsed.mode = hashArr[2];
parsed.channel = hashArr[3];
parsed.key = hashArr[4].replace(/-/g, '/');
var options = hashArr.slice(5);
parsed.key = Crypto.b64AddSlashes(hashArr[4]);
options = hashArr.slice(5);
parsed.present = options.indexOf('present') !== -1;
parsed.embed = options.indexOf('embed') !== -1;
parsed.getHash = function (opts) {
var hash = hashArr.slice(0, 5).join('/') + '/';
if (opts.embed) { hash += 'embed/'; }
if (opts.present) { hash += 'present/'; }
return hash;
};
return parsed;
}
if (hashArr[1] && hashArr[1] === '2') { // Version 2
parsed.version = 2;
parsed.app = hashArr[2];
parsed.mode = hashArr[3];
parsed.key = hashArr[4];
options = hashArr.slice(5);
parsed.password = options.indexOf('p') !== -1;
parsed.present = options.indexOf('present') !== -1;
parsed.embed = options.indexOf('embed') !== -1;
parsed.getHash = function (opts) {
var hash = hashArr.slice(0, 5).join('/') + '/';
if (parsed.password) { hash += 'p/'; }
if (opts.embed) { hash += 'embed/'; }
if (opts.present) { hash += 'present/'; }
return hash;
};
return parsed;
}
return parsed;
}
parsed.getHash = function () { return hashArr.join('/'); };
if (['media', 'file'].indexOf(type) !== -1) {
parsed.type = 'file';
if (hashArr[1] && hashArr[1] === '1') {
@@ -125,17 +217,9 @@ Version 1
url += ret.type + '/';
if (!ret.hashData) { return url; }
if (ret.hashData.type !== 'pad') { return url + '#' + ret.hash; }
if (ret.hashData.version !== 1) { return url + '#' + ret.hash; }
url += '#/' + ret.hashData.version +
'/' + ret.hashData.mode +
'/' + ret.hashData.channel.replace(/\//g, '-') +
'/' + ret.hashData.key.replace(/\//g, '-') +'/';
if (options.embed) {
url += 'embed/';
}
if (options.present) {
url += 'present/';
}
if (ret.hashData.version === 0) { return url + '#' + ret.hash; }
var hash = ret.hashData.getHash(options);
url += '#' + hash;
return url;
};
@@ -153,12 +237,13 @@ Version 1
return '';
});
idx = href.indexOf('/#');
if (idx === -1) { return ret; }
ret.hash = href.slice(idx + 2);
ret.hashData = parseTypeHash(ret.type, ret.hash);
return ret;
};
var getRelativeHref = Hash.getRelativeHref = function (href) {
Hash.getRelativeHref = function (href) {
if (!href) { return; }
if (href.indexOf('#') === -1) { return; }
var parsed = parsePadUrl(href);
@@ -170,11 +255,13 @@ Version 1
* - no argument: use the URL hash or create one if it doesn't exist
* - secretHash provided: use secretHash to find the keys
*/
Hash.getSecrets = function (type, secretHash) {
Hash.getSecrets = function (type, secretHash, password) {
var secret = {};
var generate = function () {
secret.keys = Crypto.createEditCryptor();
secret.key = Crypto.createEditCryptor().editKeyStr;
secret.keys = Crypto.createEditCryptor2(void 0, void 0, password);
secret.channel = base64ToHex(secret.keys.chanId);
secret.version = 2;
secret.type = type;
};
if (!secretHash && !window.location.hash) { //!/#/.test(window.location.href)) {
generate();
@@ -191,7 +278,6 @@ Version 1
parsed = pHref.hashData;
hash = pHref.hash;
}
//var parsed = parsePadUrl(window.location.href);
//var hash = secretHash || window.location.hash.slice(1);
if (hash.length === 0) {
generate();
@@ -203,9 +289,10 @@ Version 1
// Old hash
secret.channel = parsed.channel;
secret.key = parsed.key;
}
else if (parsed.version === 1) {
secret.version = 0;
} else if (parsed.version === 1) {
// New hash
secret.version = 1;
if (parsed.type === "pad") {
secret.channel = base64ToHex(parsed.channel);
if (parsed.mode === 'edit') {
@@ -229,49 +316,63 @@ Version 1
// version 2 hashes are to be used for encrypted blobs
throw new Error("User hashes can't be opened (yet)");
}
} else if (parsed.version === 2) {
// New hash
secret.version = 2;
secret.type = type;
secret.password = password;
if (parsed.type === "pad") {
if (parsed.mode === 'edit') {
secret.keys = Crypto.createEditCryptor2(parsed.key, void 0, password);
secret.channel = base64ToHex(secret.keys.chanId);
secret.key = secret.keys.editKeyStr;
if (secret.channel.length !== 32 || secret.key.length !== 24) {
throw new Error("The channel key and/or the encryption key is invalid");
}
}
else if (parsed.mode === 'view') {
secret.keys = Crypto.createViewCryptor2(parsed.key, password);
secret.channel = base64ToHex(secret.keys.chanId);
if (secret.channel.length !== 32) {
throw new Error("The channel key is invalid");
}
}
} else if (parsed.type === "file") {
throw new Error("File hashes should be version 1");
} else if (parsed.type === "user") {
throw new Error("User hashes can't be opened (yet)");
}
}
}
return secret;
};
Hash.getHashes = function (channel, secret) {
Hash.getHashes = function (secret) {
var hashes = {};
if (!secret.keys) {
secret = JSON.parse(JSON.stringify(secret));
if (!secret.keys && !secret.key) {
console.error('e');
return hashes;
} else if (!secret.keys) {
secret.keys = {};
}
if (secret.keys.editKeyStr) {
hashes.editHash = getEditHashFromKeys(channel, secret.keys);
if (secret.keys.editKeyStr || (secret.version === 0 && secret.key)) {
hashes.editHash = getEditHashFromKeys(secret);
}
if (secret.keys.viewKeyStr) {
hashes.viewHash = getViewHashFromKeys(channel, secret.keys);
hashes.viewHash = getViewHashFromKeys(secret);
}
if (secret.keys.fileKeyStr) {
hashes.fileHash = getFileHashFromKeys(channel, secret.keys.fileKeyStr);
hashes.fileHash = getFileHashFromKeys(secret);
}
return hashes;
};
var createChannelId = Hash.createChannelId = function () {
var id = uint8ArrayToHex(Crypto.Nacl.randomBytes(16));
if (id.length !== 32 || /[^a-f0-9]/.test(id)) {
throw new Error('channel ids must consist of 32 hex characters');
}
return id;
};
Hash.createRandomHash = function () {
// 16 byte channel Id
var channelId = Util.hexToBase64(createChannelId());
// 18 byte encryption key
var key = Crypto.b64RemoveSlashes(Crypto.rand64(18));
return '/1/edit/' + [channelId, key].join('/') + '/';
};
// STORAGE
Hash.findWeaker = function (href, recents) {
var rHref = href || getRelativeHref(window.location.href);
var parsed = parsePadUrl(rHref);
Hash.findWeaker = function (href, channel, recents) {
var parsed = parsePadUrl(href);
if (!parsed.hash) { return false; }
var weaker;
Object.keys(recents).some(function (id) {
@@ -279,6 +380,8 @@ Version 1
var p = parsePadUrl(pad.href);
if (p.type !== parsed.type) { return; } // Not the same type
if (p.hash === parsed.hash) { return; } // Same hash, not stronger
if (channel !== pad.channel) { return; } // Not the same channel
var pHash = p.hashData;
var parsedHash = parsed.hashData;
if (!parsedHash || !pHash) { return; }
@@ -287,18 +390,16 @@ Version 1
if (pHash.type !== 'pad' && parsedHash.type !== 'pad') { return; }
if (pHash.version !== parsedHash.version) { return; }
if (pHash.channel !== parsedHash.channel) { return; }
if (pHash.mode === 'view' && parsedHash.mode === 'edit') {
weaker = pad.href;
weaker = pad;
return true;
}
return;
});
return weaker;
};
var findStronger = Hash.findStronger = function (href, recents) {
var rHref = href || getRelativeHref(window.location.href);
var parsed = parsePadUrl(rHref);
Hash.findStronger = function (href, channel, recents) {
var parsed = parsePadUrl(href);
if (!parsed.hash) { return false; }
// We can't have a stronger hash if we're already in edit mode
if (parsed.hashData && parsed.hashData.mode === 'edit') { return; }
@@ -308,6 +409,8 @@ Version 1
var p = parsePadUrl(pad.href);
if (p.type !== parsed.type) { return; } // Not the same type
if (p.hash === parsed.hash) { return; } // Same hash, not stronger
if (channel !== pad.channel) { return; } // Not the same channel
var pHash = p.hashData;
var parsedHash = parsed.hashData;
if (!parsedHash || !pHash) { return; }
@@ -316,37 +419,20 @@ Version 1
if (pHash.type !== 'pad' && parsedHash.type !== 'pad') { return; }
if (pHash.version !== parsedHash.version) { return; }
if (pHash.channel !== parsedHash.channel) { return; }
if (pHash.mode === 'edit' && parsedHash.mode === 'view') {
stronger = pad.href;
stronger = pad;
return true;
}
return;
});
return stronger;
};
Hash.isNotStrongestStored = function (href, recents) {
return findStronger(href, recents);
};
Hash.hrefToHexChannelId = function (href) {
Hash.hrefToHexChannelId = function (href, password) {
var parsed = Hash.parsePadUrl(href);
if (!parsed || !parsed.hash) { return; }
parsed = parsed.hashData;
if (parsed.version === 0) {
return parsed.channel;
} else if (parsed.version !== 1 && parsed.version !== 2) {
console.error("parsed href had no version");
console.error(parsed);
return;
}
var channel = parsed.channel;
if (!channel) { return; }
var hex = base64ToHex(channel);
return hex;
var secret = Hash.getSecrets(parsed.type, parsed.hash, password);
return secret.channel;
};
Hash.getBlobPathFromHex = function (id) {

View File

@@ -6,15 +6,18 @@ define([
'/common/common-notifier.js',
'/customize/application_config.js',
'/bower_components/alertifyjs/dist/js/alertify.js',
'/common/tippy.min.js',
'/common/tippy/tippy.min.js',
'/customize/pages.js',
'/common/hyperscript.js',
'/customize/loading.js',
'/common/test.js',
'/common/jquery-ui/jquery-ui.min.js',
'/bower_components/bootstrap-tokenfield/dist/bootstrap-tokenfield.js',
'css!/common/tippy.css',
'css!/common/tippy/tippy.css',
'css!/common/jquery-ui/jquery-ui.min.css'
], function ($, Messages, Util, Hash, Notifier, AppConfig,
Alertify, Tippy, Pages, h, Test) {
Alertify, Tippy, Pages, h, Loading, Test) {
var UI = {};
/*
@@ -182,11 +185,17 @@ define([
]);
};
UI.tokenField = function (target) {
UI.tokenField = function (target, autocomplete) {
var t = {
element: target || h('input'),
};
var $t = t.tokenfield = $(t.element).tokenfield();
var $t = t.tokenfield = $(t.element).tokenfield({
autocomplete: {
source: autocomplete,
delay: 100
},
showAutocompleteOnFocus: false
});
t.getTokens = function (ignorePending) {
var tokens = $t.tokenfield('getTokens').map(function (token) {
@@ -209,10 +218,17 @@ define([
t.preventDuplicates = function (cb) {
$t.on('tokenfield:createtoken', function (ev) {
// Close the suggest list when a token is added because we're going to wipe the input
var $input = $t.closest('.tokenfield').find('.token-input');
$input.autocomplete('close');
var val;
ev.attrs.value = ev.attrs.value.toLowerCase();
if (t.getTokens(true).some(function (t) {
if (t === ev.attrs.value) { return ((val = t)); }
if (t === ev.attrs.value) {
ev.preventDefault();
return ((val = t));
}
})) {
ev.preventDefault();
if (typeof(cb) === 'function') { cb(val); }
@@ -240,7 +256,7 @@ define([
return t;
};
dialog.tagPrompt = function (tags, cb) {
dialog.tagPrompt = function (tags, existing, cb) {
var input = dialog.textInput();
var tagger = dialog.frame([
@@ -254,7 +270,7 @@ define([
dialog.nav(),
]);
var field = UI.tokenField(input).preventDuplicates(function (val) {
var field = UI.tokenField(input, existing).preventDuplicates(function (val) {
UI.warn(Messages._getKey('tags_duplicate', [val]));
});
@@ -395,7 +411,7 @@ define([
stopListening(listener);
cb();
});
listener = listenForKeys(close, close, ok);
listener = listenForKeys(close, close);
var $ok = $(ok).click(close);
document.body.appendChild(frame);
@@ -512,6 +528,50 @@ define([
Alertify.error(Util.fixHTML(msg));
};
UI.passwordInput = function (opts, displayEye) {
opts = opts || {};
var attributes = merge({
type: 'password'
}, opts);
var input = h('input.cp-password-input', attributes);
var reveal = UI.createCheckbox('cp-password-reveal', Messages.password_show);
var eye = h('span.fa.fa-eye.cp-password-reveal');
$(reveal).find('input').on('change', function () {
if($(this).is(':checked')) {
$(input).prop('type', 'text');
$(input).focus();
return;
}
$(input).prop('type', 'password');
$(input).focus();
});
$(eye).mousedown(function () {
$(input).prop('type', 'text');
$(input).focus();
}).mouseup(function(){
$(input).prop('type', 'password');
$(input).focus();
}).mouseout(function(){
$(input).prop('type', 'password');
$(input).focus();
});
if (displayEye) {
$(reveal).hide();
} else {
$(eye).hide();
}
return h('span.cp-password-container', [
input,
reveal,
eye
]);
};
/*
* spinner
*/
@@ -539,48 +599,99 @@ define([
var LOADING = 'cp-loading';
var getRandomTip = function () {
/*var getRandomTip = function () {
if (!Messages.tips || !Object.keys(Messages.tips).length) { return ''; }
var keys = Object.keys(Messages.tips);
var rdm = Math.floor(Math.random() * keys.length);
return Messages.tips[keys[rdm]];
};*/
var loading = {
error: false,
driveState: 0,
padState: 0
};
UI.addLoadingScreen = function (config) {
config = config || {};
var loadingText = config.loadingText;
var hideTips = config.hideTips || AppConfig.hideLoadingScreenTips;
var hideLogo = config.hideLogo;
var $loading, $container;
if ($('#' + LOADING).length) {
$loading = $('#' + LOADING); //.show();
var todo = function () {
var $loading = $('#' + LOADING); //.show();
$loading.css('display', '');
$loading.removeClass('cp-loading-hidden');
$('.cp-loading-spinner-container').show();
if (!config.noProgress && !$loading.find('.cp-loading-progress').length) {
var progress = h('div.cp-loading-progress', [
h('p.cp-loading-progress-drive'),
h('p.cp-loading-progress-pad')
]);
$loading.find('.cp-loading-container').append(progress);
} else if (config.noProgress) {
$loading.find('.cp-loading-progress').remove();
}
if (loadingText) {
$('#' + LOADING).find('p').text(loadingText);
$('#' + LOADING).find('#cp-loading-message').show().text(loadingText);
} else {
$('#' + LOADING).find('p').text('');
$('#' + LOADING).find('#cp-loading-message').hide().text('');
}
$container = $loading.find('.cp-loading-container');
loading.error = false;
};
if ($('#' + LOADING).length) {
todo();
} else {
$loading = $(Pages.loadingScreen());
$container = $loading.find('.cp-loading-container');
if (hideLogo) {
$loading.find('img').hide();
} else {
$loading.find('img').show();
}
var $spinner = $loading.find('.cp-loading-spinner-container');
$spinner.show();
$('body').append($loading);
Loading();
todo();
}
if (Messages.tips && !hideTips) {
var $loadingTip = $('<div>', {'id': 'cp-loading-tip'});
$('<span>', {'class': 'tips'}).text(getRandomTip()).appendTo($loadingTip);
$loadingTip.css({
'bottom': $('body').height()/2 - $container.height()/2 + 20 + 'px'
});
$('body').append($loadingTip);
};
UI.updateLoadingProgress = function (data, isDrive) {
var $loading = $('#' + LOADING);
if (!$loading.length || loading.error) { return; }
var $progress;
if (isDrive) {
console.log(data);
// Drive state
if (loading.driveState === -1) { return; } // Already loaded
$progress = $loading.find('.cp-loading-progress-drive');
if (!$progress.length) { return; } // Can't find the box to display data
// If state is -1, remove the box, drive is loaded
if (data.state === -1) {
loading.driveState = -1;
$progress.remove();
} else {
if (data.state < loading.driveState) { return; } // We should not display old data
// Update the current state
loading.driveState = data.state;
data.progress = data.progress || 100;
data.msg = Messages['loading_drive_'+data.state] || '';
$progress.html(data.msg);
if (data.progress) {
$progress.append(h('div.cp-loading-progress-bar', [
h('div.cp-loading-progress-bar-value', {style: 'width:'+data.progress+'%;'})
]));
}
}
} else {
// Pad state
if (loading.padState === -1) { return; } // Already loaded
$progress = $loading.find('.cp-loading-progress-pad');
if (!$progress.length) { return; } // Can't find the box to display data
// If state is -1, remove the box, pad is loaded
if (data.state === -1) {
loading.padState = -1;
$progress.remove();
} else {
if (data.state < loading.padState) { return; } // We should not display old data
// Update the current state
loading.padState = data.state;
data.progress = data.progress || 100;
data.msg = Messages['loading_pad_'+data.state] || '';
$progress.html(data.msg);
if (data.progress) {
$progress.append(h('div.cp-loading-progress-bar', [
h('div.cp-loading-progress-bar-value', {style: 'width:'+data.progress+'%;'})
]));
}
}
}
};
UI.removeLoadingScreen = function (cb) {
@@ -591,7 +702,7 @@ define([
$('#' + LOADING).addClass("cp-loading-hidden");
setTimeout(cb, 750);
//$('#' + LOADING).fadeOut(750, cb);
loading.error = false;
var $tip = $('#cp-loading-tip').css('top', '')
// loading.less sets transition-delay: $wait-time
// and transition: opacity $fadeout-time
@@ -605,18 +716,27 @@ define([
// jquery.fadeout can get stuck
};
UI.errorLoadingScreen = function (error, transparent, exitable) {
if (!$('#' + LOADING).is(':visible') || $('#' + LOADING).hasClass('cp-loading-hidden')) {
var $loading = $('#' + LOADING);
if (!$loading.is(':visible') || $loading.hasClass('cp-loading-hidden')) {
UI.addLoadingScreen({hideTips: true});
}
loading.error = true;
$loading.find('.cp-loading-progress').remove();
$('.cp-loading-spinner-container').hide();
$('#cp-loading-tip').remove();
if (transparent) { $('#' + LOADING).css('opacity', 0.8); }
$('#' + LOADING).find('p').html(error || Messages.error);
if (transparent) { $loading.css('opacity', 0.9); }
var $error = $loading.find('#cp-loading-message').show();
if (error instanceof Element) {
$error.html('').append(error);
} else {
$error.html(error || Messages.error);
}
if (exitable) {
$(window).focus();
$(window).keydown(function (e) {
if (e.which === 27) {
$('#' + LOADING).hide();
$loading.hide();
loading.error = false;
if (typeof(exitable) === "function") { exitable(); }
}
});
@@ -660,18 +780,40 @@ define([
});
};
var delay = typeof(AppConfig.tooltipDelay) === "number" ? AppConfig.tooltipDelay : 500;
$.extend(true, Tippy.defaults, {
placement: 'bottom',
performance: true,
delay: [delay, 0],
//sticky: true,
theme: 'cryptpad',
arrow: true,
maxWidth: '200px',
flip: true,
popperOptions: {
modifiers: {
preventOverflow: { boundariesElement: 'window' }
}
},
//arrowType: 'round',
dynamicTitle: true,
arrowTransform: 'scale(2)',
zIndex: 100000001
});
UI.addTooltips = function () {
var MutationObserver = window.MutationObserver;
var delay = typeof(AppConfig.tooltipDelay) === "number" ? AppConfig.tooltipDelay : 500;
var addTippy = function (i, el) {
if (el._tippy) { return; }
if (el.nodeName === 'IFRAME') { return; }
Tippy(el, {
position: 'bottom',
distance: 0,
performance: true,
delay: [delay, 0],
sticky: true
var opts = {
distance: 15
};
Array.prototype.slice.apply(el.attributes).filter(function (obj) {
return /^data-tippy-/.test(obj.name);
}).forEach(function (obj) {
opts[obj.name.slice(11)] = obj.value;
});
Tippy(el, opts);
};
// This is the robust solution to remove dangling tooltips
// The mutation observer does not always find removed nodes.
@@ -720,5 +862,9 @@ define([
});
};
UI.createCheckbox = Pages.createCheckbox;
UI.createRadio = Pages.createRadio;
return UI;
});

View File

@@ -99,7 +99,7 @@ define([
try {
var parsed = Hash.parsePadUrl(window.location.href);
if (!parsed.hashData) { return; }
var chan = parsed.hashData.channel;
var chan = Hash.hrefToHexChannelId(window.location.href);
// Decrypt
var keyStr = parsed.hashData.key;
var cryptor = Crypto.createEditCryptor(keyStr);
@@ -113,7 +113,7 @@ define([
if (!decryptMsg) { return; }
// Parse
msg = JSON.parse(decryptMsg);
if (msg[1] !== parsed.hashData.channel) { return; }
if (msg[1] !== chan) { return; }
var msgData = msg[2];
var msgStr;
if (msg[0] === "FRIEND_REQ") {
@@ -199,7 +199,7 @@ define([
var parsed = Hash.parsePadUrl(data.href);
if (!parsed.hashData) { return; }
// Message
var chan = parsed.hashData.channel;
var chan = Hash.hrefToHexChannelId(data.href);
var myData = createData(cfg.proxy);
var msg = ["FRIEND_REQ", chan, myData];
// Encryption

View File

@@ -205,7 +205,7 @@ define([
if (content === oldThumbnailState) { return; }
oldThumbnailState = content;
Thumb.fromDOM(opts, function (err, b64) {
Thumb.setPadThumbnail(common, opts.href, b64);
Thumb.setPadThumbnail(common, opts.href, null, b64);
});
};
var nafa = Util.notAgainForAnother(mkThumbnail, Thumb.UPDATE_INTERVAL);
@@ -240,20 +240,22 @@ define([
Thumb.addThumbnail = function(thumb, $span, cb) {
return addThumbnail(null, thumb, $span, cb);
};
var getKey = function (href) {
var parsed = Hash.parsePadUrl(href);
return 'thumbnail-' + parsed.type + '-' + parsed.hashData.channel;
var getKey = function (type, channel) {
return 'thumbnail-' + type + '-' + channel;
};
Thumb.setPadThumbnail = function (common, href, b64, cb) {
Thumb.setPadThumbnail = function (common, href, channel, b64, cb) {
cb = cb || function () {};
var k = getKey(href);
var parsed = Hash.parsePadUrl(href);
channel = channel || common.getMetadataMgr().getPrivateData().channel;
var k = getKey(parsed.type, channel);
common.setThumbnail(k, b64, cb);
};
Thumb.displayThumbnail = function (common, href, $container, cb) {
Thumb.displayThumbnail = function (common, href, channel, $container, cb) {
cb = cb || function () {};
var parsed = Hash.parsePadUrl(href);
var k = getKey(href);
var k = getKey(parsed.type, channel);
var whenNewThumb = function () {
// PASSWORD_FILES
var secret = Hash.getSecrets('file', parsed.hash);
var hexFileName = Util.base64ToHex(secret.channel);
var src = Hash.getBlobPathFromHex(hexFileName);
@@ -270,7 +272,7 @@ define([
if (!v) {
v = 'EMPTY';
}
Thumb.setPadThumbnail(common, href, v, function (err) {
Thumb.setPadThumbnail(common, href, hexFileName, v, function (err) {
if (!metadata.thumbnail) { return; }
addThumbnail(err, metadata.thumbnail, $container, cb);
});

View File

@@ -13,8 +13,6 @@ define([
'/customize/messages.js',
'/customize/application_config.js',
'/bower_components/nthen/index.js',
'css!/common/tippy.css',
], function ($, Config, Util, Hash, Language, UI, Constants, Feedback, h, MediaTag, Clipboard,
Messages, AppConfig, NThen) {
var UIElements = {};
@@ -25,20 +23,27 @@ define([
}
UIElements.updateTags = function (common, href) {
var sframeChan = common.getSframeChannel();
sframeChan.query('Q_TAGS_GET', href || null, function (err, res) {
if (err || res.error) {
if (res.error === 'NO_ENTRY') {
UI.alert(Messages.tags_noentry);
var existing, tags;
NThen(function(waitFor) {
common.getSframeChannel().query("Q_GET_ALL_TAGS", null, waitFor(function(err, res) {
if (err || res.error) { return void console.error(err || res.error); }
existing = Object.keys(res.tags).sort();
}));
}).nThen(function (waitFor) {
common.getPadAttribute('tags', waitFor(function (err, res) {
if (err) {
if (err === 'NO_ENTRY') {
UI.alert(Messages.tags_noentry);
}
waitFor.abort();
return void console.error(err);
}
return void console.error(err || res.error);
}
UI.dialog.tagPrompt(res.data, function (tags) {
if (!Array.isArray(tags)) { return; }
sframeChan.event('EV_TAGS_SET', {
tags: tags,
href: href,
});
tags = res || [];
}), href);
}).nThen(function () {
UI.dialog.tagPrompt(tags, existing, function (newTags) {
if (!Array.isArray(newTags)) { return; }
common.setPadAttribute('tags', newTags, null, href);
});
});
};
@@ -62,6 +67,10 @@ define([
var getPropertiesData = function (common, cb) {
var data = {};
NThen(function (waitFor) {
common.getPadAttribute('password', waitFor(function (err, val) {
data.password = val;
}));
}).nThen(function (waitFor) {
common.getPadAttribute('href', waitFor(function (err, val) {
var base = common.getMetadataMgr().getPrivateData().origin;
@@ -73,15 +82,19 @@ define([
// We're not in a read-only pad
data.href = base + val;
// Get Read-only href
if (parsed.hashData.type !== "pad") { return; }
var i = data.href.indexOf('#') + 1;
var hBase = data.href.slice(0, i);
var hrefsecret = Hash.getSecrets(parsed.type, parsed.hash);
var hrefsecret = Hash.getSecrets(parsed.type, parsed.hash, data.password);
if (!hrefsecret.keys) { return; }
var viewHash = Hash.getViewHashFromKeys(hrefsecret.channel, hrefsecret.keys);
var viewHash = Hash.getViewHashFromKeys(hrefsecret);
data.roHref = hBase + viewHash;
}));
common.getPadAttribute('channel', waitFor(function (err, val) {
data.channel = val;
}));
common.getPadAttribute('atime', waitFor(function (err, val) {
data.atime = val;
}));
@@ -137,6 +150,22 @@ define([
$d.append(UI.dialog.selectable(expire, {
id: 'cp-app-prop-expire',
}));
if (typeof data.password !== "undefined") {
$('<label>', {'for': 'cp-app-prop-password'}).text(Messages.creation_passwordValue)
.appendTo($d);
var password = UI.passwordInput({
id: 'cp-app-prop-expire',
readonly: 'readonly'
});
var $pwInput = $(password).find('.cp-password-input');
$pwInput.val(data.password).click(function () {
$pwInput[0].select();
});
$(password).find('.cp-checkmark').css('margin-bottom', '15px');
$d.append(password);
}
cb(void 0, $d);
};
var getPadProperties = function (common, data, cb) {
@@ -178,7 +207,7 @@ define([
if (common.isLoggedIn() && AppConfig.enablePinning) {
// check the size of this file...
common.getFileSize(data.href, function (e, bytes) {
common.getFileSize(data.channel, function (e, bytes) {
if (e) {
// there was a problem with the RPC
console.error(e);
@@ -237,7 +266,11 @@ define([
var link = h('div.cp-share-modal', [
h('label', Messages.share_linkAccess),
h('br'),
h('input#cp-share-editable-true.cp-share-editable-value', {
UI.createRadio('cp-share-editable', 'cp-share-editable-true',
Messages.share_linkEdit, true, { mark: {tabindex:1} }),
UI.createRadio('cp-share-editable', 'cp-share-editable-false',
Messages.share_linkView, false, { mark: {tabindex:1} }),
/*h('input#cp-share-editable-true.cp-share-editable-value', {
type: 'radio',
name: 'cp-share-editable',
value: 1,
@@ -248,25 +281,14 @@ define([
name: 'cp-share-editable',
value: 0
}),
h('label', { 'for': 'cp-share-editable-false' }, Messages.share_linkView),
h('br'),
h('label', { 'for': 'cp-share-editable-false' }, Messages.share_linkView),*/
h('br'),
h('label', Messages.share_linkOptions),
h('br'),
h('input#cp-share-embed', {
type: 'checkbox',
name: 'cp-share-embed'
}),
h('label', { 'for': 'cp-share-embed' }, Messages.share_linkEmbed),
UI.createCheckbox('cp-share-embed', Messages.share_linkEmbed, false, { mark: {tabindex:1} }),
UI.createCheckbox('cp-share-present', Messages.share_linkPresent, false, { mark: {tabindex:1} }),
h('br'),
h('input#cp-share-present', {
type: 'checkbox',
name: 'cp-share-present'
}),
h('label', { 'for': 'cp-share-present' }, Messages.share_linkPresent),
h('br'),
h('br'),
UI.dialog.selectable('', { id: 'cp-share-link-preview' })
UI.dialog.selectable('', { id: 'cp-share-link-preview', tabindex: 1 })
]);
if (!hashes.editHash) {
$(link).find('#cp-share-editable-false').attr('checked', true);
@@ -482,13 +504,44 @@ define([
'class': 'fa fa-upload cp-toolbar-icon-import',
title: Messages.importButtonTitle,
}).append($('<span>', {'class': 'cp-toolbar-drawer-element'}).text(Messages.importButton));
if (callback) {
/*if (data.types) {
// New import button in the toolbar
var importFunction = {
template: function () {
UIElements.openTemplatePicker(common, true);
},
file: function (cb) {
importContent('text/plain', function (content, file) {
cb(content, file);
}, {accept: data ? data.accept : undefined})
}
};
var toImport = [];
Object.keys(data.types).forEach(function (importType) {
if (!importFunction[importType] || !data.types[importType]) { return; }
var option = h('button', importType);
$(option).click(function () {
importFunction[importType](data.types[importType]);
});
toImport.push(options);
});
button.click(common.prepareFeedback(type));
if (toImport.length === 1) {
button.click(function () { $(toImport[0]).click(); });
} else {
Cryptpad.alert(h('p.cp-import-container', toImport));
}
}
else if (callback) {*/
// Old import button, used in settings
button
.click(common.prepareFeedback(type))
.click(importContent('text/plain', function (content, file) {
callback(content, file);
}, {accept: data ? data.accept : undefined}));
}
//}
break;
case 'upload':
button = $('<button>', {
@@ -520,6 +573,19 @@ define([
if (data.accept) { $input.attr('accept', data.accept); }
button.click(function () { $input.click(); });
break;
case 'importtemplate':
if (!AppConfig.enableTemplates) { return; }
if (!common.isLoggedIn()) { return; }
button = $('<button>', {
'class': 'fa fa-upload cp-toolbar-icon-import',
title: Messages.template_import,
}).append($('<span>', {'class': 'cp-toolbar-drawer-element'}).text(Messages.template_import));
button
.click(common.prepareFeedback(type))
.click(function () {
UIElements.openTemplatePicker(common, true);
});
break;
case 'template':
if (!AppConfig.enableTemplates) { return; }
if (!common.isLoggedIn()) { return; }
@@ -584,7 +650,8 @@ define([
sframeChan.query('Q_MOVE_TO_TRASH', null, function (err) {
if (err) { return void callback(err); }
var cMsg = common.isLoggedIn() ? Messages.movedToTrash : Messages.deleted;
UI.alert(cMsg, undefined, true);
var msg = common.fixLinks($('<div>').html(cMsg));
UI.alert(msg);
callback();
return;
});
@@ -890,14 +957,14 @@ define([
};
};
var setHTML = function (e, html) {
e.innerHTML = html;
return e;
};
UIElements.createHelpMenu = function (common, categories) {
var type = common.getMetadataMgr().getMetadata().type || 'pad';
var setHTML = function (e, html) {
e.innerHTML = html;
return e;
};
var elements = [];
if (Messages.help && Messages.help.generic) {
Object.keys(Messages.help.generic).forEach(function (el) {
@@ -920,18 +987,7 @@ define([
h('ul', elements)
]);
var origin = common.getMetadataMgr().getPrivateData().origin || '';
$(text).find('a').click(function (e) {
e.preventDefault();
e.stopPropagation();
var href = $(this).attr('href');
var absolute = /^https?:\/\//i;
if (!absolute.test(href)) {
if (href.slice(0,1) !== '/') { href = '/' + href; }
href = origin + href;
}
common.openUnsafeURL(href);
});
common.fixLinks(text);
var closeButton = h('span.cp-help-close.fa.fa-window-close');
var $toolbarButton = common.createButton('', true, {
@@ -1110,12 +1166,13 @@ define([
};
return;
}
// No password for avatars
var secret = Hash.getSecrets('file', parsed.hash);
if (secret.keys && secret.channel) {
var cryptKey = secret.keys && secret.keys.fileKeyStr;
var hexFileName = Util.base64ToHex(secret.channel);
var src = Hash.getBlobPathFromHex(hexFileName);
Common.getFileSize(href, function (e, data) {
Common.getFileSize(hexFileName, function (e, data) {
if (e) {
displayDefault();
return void console.error(e);
@@ -1148,7 +1205,7 @@ define([
// so we can just use those and only check for errors
var $container = $('<span>', {'class':'cp-limit-container'});
var todo = function (err, data) {
if (err) { return void console.error(err); }
if (err || !data) { return void console.error(err || 'No data'); }
var usage = data.usage;
var limit = data.limit;
@@ -1422,50 +1479,51 @@ define([
tag: 'a',
attributes: {
'target': '_blank',
'href': origin+'/drive/'
'href': origin+'/drive/',
'class': 'fa fa-hdd-o'
},
content: Messages.login_accessDrive
content: h('span', Messages.login_accessDrive)
});
}
// Add the change display name button if not in read only mode
if (config.changeNameButtonCls && config.displayChangeName && !AppConfig.disableProfile) {
options.push({
tag: 'a',
attributes: {'class': config.changeNameButtonCls},
content: Messages.user_rename
attributes: {'class': config.changeNameButtonCls + ' fa fa-user'},
content: h('span', Messages.user_rename)
});
}
if (accountName && !AppConfig.disableProfile) {
options.push({
tag: 'a',
attributes: {'class': 'cp-toolbar-menu-profile'},
content: Messages.profileButton
attributes: {'class': 'cp-toolbar-menu-profile fa fa-user-circle'},
content: h('span', Messages.profileButton)
});
}
if (padType !== 'settings') {
options.push({
tag: 'a',
attributes: {'class': 'cp-toolbar-menu-settings'},
content: Messages.settingsButton
attributes: {'class': 'cp-toolbar-menu-settings fa fa-cog'},
content: h('span', Messages.settingsButton)
});
}
// Add login or logout button depending on the current status
if (accountName) {
options.push({
tag: 'a',
attributes: {'class': 'cp-toolbar-menu-logout'},
content: Messages.logoutButton
attributes: {'class': 'cp-toolbar-menu-logout fa fa-sign-out'},
content: h('span', Messages.logoutButton)
});
} else {
options.push({
tag: 'a',
attributes: {'class': 'cp-toolbar-menu-login'},
content: Messages.login_login
attributes: {'class': 'cp-toolbar-menu-login fa fa-sign-in'},
content: h('span', Messages.login_login)
});
options.push({
tag: 'a',
attributes: {'class': 'cp-toolbar-menu-register'},
content: Messages.login_register
attributes: {'class': 'cp-toolbar-menu-register fa fa-user-plus'},
content: h('span', Messages.login_register)
});
}
var $icon = $('<span>', {'class': 'fa fa-user-secret'});
@@ -1521,9 +1579,8 @@ define([
UIElements.displayAvatar(Common, $avatar, url,
newName || Messages.anonymous, function ($img) {
oldUrl = url;
if ($img) {
$userAdmin.find('> button').addClass('cp-avatar');
}
$userAdmin.find('> button').removeClass('cp-avatar');
if ($img) { $userAdmin.find('> button').addClass('cp-avatar'); }
loadingAvatar = false;
});
return;
@@ -1651,14 +1708,10 @@ define([
var priv = common.getMetadataMgr().getPrivateData();
var c = (priv.settings.general && priv.settings.general.creation) || {};
if (AppConfig.displayCreationScreen && common.isLoggedIn() && c.skip) {
$advanced = $('<input>', {
type: 'checkbox',
checked: 'checked',
id: 'cp-app-toolbar-creation-advanced'
}).appendTo($advancedContainer);
$('<label>', {
for: 'cp-app-toolbar-creation-advanced'
}).text(Messages.creation_newPadModalAdvanced).appendTo($advancedContainer);
var $cboxLabel = $(UI.createCheckbox('cp-app-toolbar-creation-advanced',
Messages.creation_newPadModalAdvanced, true))
.appendTo($advancedContainer);
$advanced = $cboxLabel.find('input');
$description.append('<br>');
$description.append(Messages.creation_newPadModalDescriptionAdvanced);
}
@@ -1742,17 +1795,21 @@ define([
sframeChan.event("EV_FILE_PICKER_OPEN", types);
};
UIElements.openTemplatePicker = function (common) {
UIElements.openTemplatePicker = function (common, force) {
var metadataMgr = common.getMetadataMgr();
var type = metadataMgr.getMetadataLazy().type;
var sframeChan = common.getSframeChannel();
var focus;
var pickerCfg = {
var pickerCfgInit = {
types: [type],
where: ['template'],
hidden: true
};
var pickerCfg = {
types: [type],
where: ['template'],
};
var onConfirm = function (yes) {
if (!yes) {
if (focus) { focus.focus(); }
@@ -1780,12 +1837,15 @@ define([
sframeChan.query("Q_TEMPLATE_EXIST", type, function (err, data) {
if (data) {
common.openFilePicker(pickerCfg);
common.openFilePicker(pickerCfgInit);
focus = document.activeElement;
if (force) { return void onConfirm(true); }
UI.confirm(Messages.useTemplate, onConfirm, {
ok: Messages.useTemplateOK,
cancel: Messages.useTemplateCancel,
});
} else if (force) {
UI.alert(Messages.template_empty);
}
});
};
@@ -1821,11 +1881,15 @@ define([
var $body = $('body');
var $creationContainer = $('<div>', { id: 'cp-creation-container' }).appendTo($body);
var urlArgs = (Config.requireConf && Config.requireConf.urlArgs) || '';
var l = h('div.cp-creation-logo', h('img', { src: '/customize/loading-logo.png?' + urlArgs }));
$(l).appendTo($creationContainer);
var $creation = $('<div>', { id: 'cp-creation', tabindex: 1 }).appendTo($creationContainer);
// Title
var colorClass = 'cp-icon-color-'+type;
$creation.append(h('h2.cp-creation-title', Messages.newButtonTitle));
//var colorClass = 'cp-icon-color-'+type;
//$creation.append(h('h2.cp-creation-title', Messages.newButtonTitle));
$creation.append(h('h3.cp-creation-title', Messages['button_new'+type]));
//$creation.append(h('h2.cp-creation-title.'+colorClass, Messages.newButtonTitle));
// Deleted pad warning
@@ -1837,10 +1901,11 @@ define([
var origin = common.getMetadataMgr().getPrivateData().origin;
var createHelper = function (href, text) {
var q = h('a.cp-creation-help.fa.fa-question', {
var q = h('a.cp-creation-help.fa.fa-question-circle', {
title: text,
href: origin + href,
target: "_blank"
target: "_blank",
'data-tippy-placement': "right"
});
return q;
};
@@ -1848,30 +1913,14 @@ define([
// Owned pads
// Default is Owned pad
var owned = h('div.cp-creation-owned', [
h('label.cp-checkmark', [
h('input', {
type: 'checkbox',
id: 'cp-creation-owned',
checked: 'checked'
}),
h('span.cp-checkmark-mark'),
Messages.creation_owned
]),
UI.createCheckbox('cp-creation-owned', Messages.creation_owned, true),
createHelper('/faq.html#keywords-owned', Messages.creation_owned1)
]);
// Life time
var expire = h('div.cp-creation-expire', [
h('label.cp-checkmark', [
h('input', {
type: 'checkbox',
id: 'cp-creation-expire'
}),
h('span.cp-checkmark-mark'),
Messages.creation_expire
]),
createHelper('/faq.html#keywords-expiring', Messages.creation_expire2),
h('div.cp-creation-expire-picker.cp-creation-slider', [
UI.createCheckbox('cp-creation-expire', Messages.creation_expire, false),
h('span.cp-creation-expire-picker.cp-creation-slider', [
h('input#cp-creation-expire-val', {
type: "number",
min: 1,
@@ -1886,106 +1935,168 @@ define([
selected: 'selected'
}, Messages.creation_expireMonths)
])
]),
createHelper('/faq.html#keywords-expiring', Messages.creation_expire2),
]);
// Password
var password = h('div.cp-creation-password', [
UI.createCheckbox('cp-creation-password', Messages.creation_password, false),
h('span.cp-creation-password-picker.cp-creation-slider', [
UI.passwordInput({id: 'cp-creation-password-val'})
/*h('input#cp-creation-password-val', {
type: "text" // TODO type password with click to show
}),*/
]),
//createHelper('#', "TODO: password protection adds another layer of security ........") // TODO
]);
var right = h('span.fa.fa-chevron-right.cp-creation-template-more');
var left = h('span.fa.fa-chevron-left.cp-creation-template-more');
var templates = h('div.cp-creation-template', [
left,
h('div.cp-creation-template-container', [
h('span.fa.fa-circle-o-notch.fa-spin.fa-4x.fa-fw')
]),
right
]);
var settings = h('div.cp-creation-remember', [
UI.createCheckbox('cp-creation-remember', Messages.creation_saveSettings, false),
createHelper('/settings/#creation', Messages.creation_settings),
h('div.cp-creation-remember-help.cp-creation-slider', [
h('span.fa.fa-exclamation-circle.cp-creation-warning'),
Messages.creation_rememberHelp
])
]);
var createDiv = h('div.cp-creation-create');
var $create = $(createDiv);
var templates = h('div.cp-creation-template', [
h('h3.cp-creation-title.'+colorClass, Messages['button_new'+type]),
h('div.cp-creation-template-container', [
h('span.fa.fa-circle-o-notch.fa-spin.fa-4x.fa-fw')
]),
createDiv
]);
var settings = h('div.cp-creation-remember', [
h('label.cp-checkmark', [
h('input', {
type: 'checkbox',
id: 'cp-creation-remember'
}),
h('span.cp-checkmark-mark'),
Messages.creation_saveSettings
]),
createHelper('/settings/#creation', Messages.creation_settings),
h('div.cp-creation-remember-help.cp-creation-slider', Messages.creation_rememberHelp)
]);
$(h('div#cp-creation-form', [
owned,
expire,
password,
settings,
templates
templates,
createDiv
])).appendTo($creation);
// Display templates
var selected = 0;
var selected = 0; // Selected template in the list (highlighted)
var TEMPLATES_DISPLAYED = 4; // Max templates displayed per page
var next = function () {}; // Function called when pressing tab to highlight the next template
var i = 0; // Index of the first template displayed in the current page
sframeChan.query("Q_CREATE_TEMPLATES", type, function (err, res) {
if (!res.data || !Array.isArray(res.data)) {
return void console.error("Error: get the templates list");
}
var data = res.data.slice().sort(function (a, b) {
if (a.name === b.name) { return 0; }
return a.name < b.name ? -1 : 1;
var allData = res.data.slice().sort(function (a, b) {
if (a.used === b.used) {
// Sort by name
if (a.name === b.name) { return 0; }
return a.name < b.name ? -1 : 1;
}
return b.used - a.used;
});
data.unshift({
name: Messages.creation_noTemplate,
id: 0,
icon: h('span.fa.fa-file')
});
data.push({
allData.unshift({
name: Messages.creation_newTemplate,
id: -1,
icon: h('span.fa.fa-bookmark')
});
var $container = $(templates).find('.cp-creation-template-container').html('');
data.forEach(function (obj, idx) {
var name = obj.name;
var $span = $('<span>', {
'class': 'cp-creation-template-element',
'title': name,
}).appendTo($container);
$span.data('id', obj.id);
if (idx === 0) { $span.addClass('cp-creation-template-selected'); }
$span.append(obj.icon || UI.getFileIcon({type: type}));
$('<span>', {'class': 'cp-creation-template-element-name'}).text(name)
.appendTo($span);
$span.click(function () {
$container.find('.cp-creation-template-selected')
.removeClass('cp-creation-template-selected');
$span.addClass('cp-creation-template-selected');
selected = idx;
});
// Add thumbnail if it exists
if (obj.thumbnail) {
common.addThumbnail(obj.thumbnail, $span, function () {});
}
allData.unshift({
name: Messages.creation_noTemplate,
id: 0,
icon: h('span.fa.fa-file')
});
});
// Change template selection when Tab is pressed
var next = function (revert) {
var max = $creation.find('.cp-creation-template-element').length;
selected = revert ?
(--selected < 0 ? max-1 : selected) :
++selected % max;
$creation.find('.cp-creation-template-element')
.removeClass('cp-creation-template-selected');
$($creation.find('.cp-creation-template-element').get(selected))
.addClass('cp-creation-template-selected');
};
var redraw = function (index) {
if (index < 0) { i = 0; }
else if (index > allData.length - 1) { return; }
else { i = index; }
var data = allData.slice(i, i + TEMPLATES_DISPLAYED);
var $container = $(templates).find('.cp-creation-template-container').html('');
data.forEach(function (obj, idx) {
var name = obj.name;
var $span = $('<span>', {
'class': 'cp-creation-template-element',
'title': name,
}).appendTo($container);
$span.data('id', obj.id);
if (idx === selected) { $span.addClass('cp-creation-template-selected'); }
$span.append(obj.icon || UI.getFileIcon({type: type}));
$('<span>', {'class': 'cp-creation-template-element-name'}).text(name)
.appendTo($span);
$span.click(function () {
$container.find('.cp-creation-template-selected')
.removeClass('cp-creation-template-selected');
$span.addClass('cp-creation-template-selected');
selected = idx;
});
// Add thumbnail if it exists
if (obj.thumbnail) {
common.addThumbnail(obj.thumbnail, $span, function () {});
}
});
$(right).off('click').removeClass('hidden').click(function () {
selected = 0;
redraw(i + TEMPLATES_DISPLAYED);
});
if (i >= allData.length - TEMPLATES_DISPLAYED ) { $(right).addClass('hidden'); }
$(left).off('click').removeClass('hidden').click(function () {
selected = TEMPLATES_DISPLAYED - 1;
redraw(i - TEMPLATES_DISPLAYED);
});
if (i < TEMPLATES_DISPLAYED) { $(left).addClass('hidden'); }
};
redraw(0);
// Change template selection when Tab is pressed
next = function (revert) {
var max = $creation.find('.cp-creation-template-element').length;
if (selected + 1 === max && !revert) {
selected = i + TEMPLATES_DISPLAYED < allData.length ? 0 : max;
return void redraw(i + TEMPLATES_DISPLAYED);
}
if (selected === 0 && revert) {
selected = i - TEMPLATES_DISPLAYED >= 0 ? TEMPLATES_DISPLAYED - 1 : 0;
return void redraw(i - TEMPLATES_DISPLAYED);
}
selected = revert ?
(--selected < 0 ? 0 : selected) :
++selected >= max ? max-1 : selected;
$creation.find('.cp-creation-template-element')
.removeClass('cp-creation-template-selected');
$($creation.find('.cp-creation-template-element').get(selected))
.addClass('cp-creation-template-selected');
};
});
// Display expiration form when checkbox checked
$creation.find('#cp-creation-expire').on('change', function () {
if ($(this).is(':checked')) {
$creation.find('.cp-creation-expire-picker:not(.active)').addClass('active');
$creation.find('.cp-creation-expire:not(.active)').addClass('active');
$creation.find('#cp-creation-expire-val').focus();
return;
}
$creation.find('.cp-creation-expire-picker').removeClass('active');
$creation.find('.cp-creation-expire').removeClass('active');
$creation.focus();
});
// Display expiration form when checkbox checked
$creation.find('#cp-creation-password').on('change', function () {
if ($(this).is(':checked')) {
$creation.find('.cp-creation-password-picker:not(.active)').addClass('active');
$creation.find('.cp-creation-password:not(.active)').addClass('active');
$creation.find('#cp-creation-password-val').focus();
return;
}
$creation.find('.cp-creation-password-picker').removeClass('active');
$creation.find('.cp-creation-password').removeClass('active');
$creation.focus();
});
@@ -2037,12 +2148,16 @@ define([
}
expireVal = ($('#cp-creation-expire-val').val() || 0) * unit;
}
// Password
var passwordVal = $('#cp-creation-password').is(':checked') ?
$('#cp-creation-password-val').val() : undefined;
var $template = $creation.find('.cp-creation-template-selected');
var templateId = $template.data('id') || undefined;
return {
owned: ownedVal,
password: passwordVal,
expire: expireVal,
templateId: templateId
};
@@ -2112,5 +2227,38 @@ define([
(cb || function () {})();
};
UIElements.displayPasswordPrompt = function (common, isError) {
var error;
if (isError) { error = setHTML(h('p.cp-password-error'), Messages.password_error); }
var info = h('p.cp-password-info', Messages.password_info);
var password = UI.passwordInput({placeholder: Messages.password_placeholder});
var button = h('button', Messages.password_submit);
var submit = function () {
var value = $(password).find('.cp-password-input').val();
UI.addLoadingScreen();
common.getSframeChannel().query('Q_PAD_PASSWORD_VALUE', value, function (err, data) {
if (!data) {
UIElements.displayPasswordPrompt(common, true);
}
});
};
$(password).find('.cp-password-input').on('keydown', function (e) { if (e.which === 13) { submit(); } });
$(button).on('click', function () { submit(); });
var block = h('div#cp-loading-password-prompt', [
error,
info,
h('p.cp-password-form', [
password,
button
])
]);
UI.errorLoadingScreen(block);
$(password).find('.cp-password-input').focus();
};
return UIElements;
});

View File

@@ -20,9 +20,9 @@ define([
}
};
var makeConfig = function (hash) {
var makeConfig = function (hash, password) {
// We can't use cryptget with a file or a user so we can use 'pad' as hash type
var secret = Hash.getSecrets('pad', hash);
var secret = Hash.getSecrets('pad', hash, password);
if (!secret.keys) { secret.keys = secret.key; } // support old hashses
var config = {
websocketURL: NetConfig.getWebsocketURL(),
@@ -47,8 +47,10 @@ define([
if (typeof(cb) !== 'function') {
throw new Error('Cryptget expects a callback');
}
opt = opt || {};
var config = makeConfig(hash, opt.password);
var Session = { cb: cb, };
var config = makeConfig(hash);
config.onReady = function (info) {
var rt = Session.session = info.realtime;
@@ -64,9 +66,11 @@ define([
if (typeof(cb) !== 'function') {
throw new Error('Cryptput expects a callback');
}
opt = opt || {};
var config = makeConfig(hash);
var config = makeConfig(hash, opt.password);
var Session = { cb: cb, };
config.onReady = function (info) {
var realtime = Session.session = info.realtime;
Session.network = info.network;

View File

@@ -246,8 +246,8 @@ define([
});
};
common.getFileSize = function (href, cb) {
postMessage("GET_FILE_SIZE", {href: href}, function (obj) {
common.getFileSize = function (href, password, cb) {
postMessage("GET_FILE_SIZE", {href: href, password: password}, function (obj) {
if (obj && obj.error) { return void cb(obj.error); }
cb(undefined, obj.size);
});
@@ -260,8 +260,8 @@ define([
});
};
common.isNewChannel = function (href, cb) {
postMessage('IS_NEW_CHANNEL', {href: href}, function (obj) {
common.isNewChannel = function (href, password, cb) {
postMessage('IS_NEW_CHANNEL', {href: href, password: password}, function (obj) {
if (obj.error) { return void cb(obj.error); }
if (!obj) { return void cb('INVALID_RESPONSE'); }
cb(undefined, obj.isNew);
@@ -395,8 +395,10 @@ define([
common.saveAsTemplate = function (Cryptput, data, cb) {
var p = Hash.parsePadUrl(window.location.href);
if (!p.type) { return; }
var hash = Hash.createRandomHash();
// PPP: password for the new template?
var hash = Hash.createRandomHash(p.type);
var href = '/' + p.type + '/#' + hash;
// PPP: add password as cryptput option
Cryptput(hash, data.toSave, function (e) {
if (e) { throw new Error(e); }
postMessage("ADD_PAD", {
@@ -419,15 +421,33 @@ define([
});
};
common.useTemplate = function (href, Crypt, cb, opts) {
common.useTemplate = function (href, 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
var parsed = Hash.parsePadUrl(href);
var parsed2 = Hash.parsePadUrl(window.location.href);
if(!parsed) { throw new Error("Cannot get template hash"); }
Crypt.get(parsed.hash, function (err, val) {
if (err) { throw new Error(err); }
var p = Hash.parsePadUrl(window.location.href);
Crypt.put(p.hash, val, cb, opts);
postMessage("INCREMENT_TEMPLATE_USE", href);
optsPut = optsPut || {};
var optsGet = {};
Nthen(function (waitFor) {
if (parsed.hashData && parsed.hashData.password) {
common.getPadAttribute('password', waitFor(function (err, password) {
optsGet.password = password;
}), href);
}
if (parsed2.hashData && parsed2.hashData.password) {
common.getPadAttribute('password', waitFor(function (err, password) {
optsPut.password = password;
}));
}
}).nThen(function () {
Crypt.get(parsed.hash, function (err, val) {
if (err) { throw new Error(err); }
Crypt.put(parsed2.hash, val, cb, optsPut);
}, optsGet);
});
};
@@ -438,20 +458,18 @@ define([
};
// When opening a new pad or renaming it, store the new title
common.setPadTitle = function (title, padHref, path, cb) {
var href = padHref || window.location.href;
common.setPadTitle = function (data, cb) {
if (!data || typeof (data) !== "object") { return cb ('Data is not an object'); }
var href = data.href || window.location.href;
var parsed = Hash.parsePadUrl(href);
if (!parsed.hash) { return; }
href = parsed.getUrl({present: parsed.present});
if (!parsed.hash) { return cb ('Invalid hash'); }
data.href = parsed.getUrl({present: parsed.present});
if (title === null) { return; }
if (title.trim() === "") { title = Hash.getDefaultName(parsed); }
if (typeof (data.title) !== "string") { return cb('Missing title'); }
if (data.title.trim() === "") { data.title = Hash.getDefaultName(parsed); }
postMessage("SET_PAD_TITLE", {
href: href,
title: title,
path: path
}, function (obj) {
postMessage("SET_PAD_TITLE", data, function (obj) {
if (obj && obj.error) {
console.log("unable to set pad title");
return void cb(obj.error);
@@ -472,10 +490,6 @@ define([
cb(void 0, data);
});
};
// Set initial path when creating a pad from pad creation screen
common.setInitialPath = function (path) {
postMessage("SET_INITIAL_PATH", path);
};
// Messaging (manage friends from the userlist)
common.inviteFromUserlist = function (netfluxId, cb) {
@@ -548,6 +562,10 @@ define([
pad.onDisconnectEvent = Util.mkEvent();
pad.onErrorEvent = Util.mkEvent();
// Loading events
common.loading = {};
common.loading.onDriveEvent = Util.mkEvent();
common.getFullHistory = function (data, cb) {
postMessage("GET_FULL_HISTORY", data, cb);
};
@@ -555,15 +573,15 @@ define([
common.getShareHashes = function (secret, cb) {
var hashes;
if (!window.location.hash) {
hashes = Hash.getHashes(secret.channel, secret);
hashes = Hash.getHashes(secret);
return void cb(null, hashes);
}
var parsed = Hash.parsePadUrl(window.location.href);
if (!parsed.type || !parsed.hashData) { return void cb('E_INVALID_HREF'); }
if (parsed.type === 'file') { secret.channel = Util.base64ToHex(secret.channel); }
hashes = Hash.getHashes(secret.channel, secret);
hashes = Hash.getHashes(secret);
if (!hashes.editHash && !hashes.viewHash && parsed.hashData && !parsed.hashData.mode) {
if (secret.version === 0) {
// It means we're using an old hash
hashes.editHash = window.location.hash.slice(1);
return void cb(null, hashes);
@@ -575,7 +593,8 @@ define([
}
postMessage("GET_STRONGER_HASH", {
href: window.location.href
href: window.location.href,
password: secret.password
}, function (hash) {
if (hash) { hashes.editHash = hash; }
cb(null, hashes);
@@ -622,6 +641,12 @@ define([
window.location.href = '/login/';
};
common.startAccountDeletion = function (cb) {
// Logout other tabs
LocalStore.logout(null, true);
cb();
};
var onMessage = function (cmd, data, cb) {
cb = cb || function () {};
switch (cmd) {
@@ -701,6 +726,14 @@ define([
case 'DRIVE_REMOVE': {
common.drive.onRemove.fire(data); break;
}
// Account deletion
case 'DELETE_ACCOUNT': {
common.startAccountDeletion(cb); break;
}
// Loading
case 'LOADING_DRIVE': {
common.loading.onDriveEvent.fire(data); break;
}
}
};
@@ -779,11 +812,11 @@ define([
if (data.anonHash && !cfg.userHash) { LocalStore.setFSHash(data.anonHash); }
if (cfg.userHash && sessionStorage) {
/*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));
@@ -802,18 +835,18 @@ define([
window.onhashchange = function (ev) {
if (ev && ev.reset) { oldHref = document.location.href; return; }
var newHref = document.location.href;
var parsedOld = Hash.parsePadUrl(oldHref).hashData;
var parsedNew = Hash.parsePadUrl(newHref).hashData;
if (parsedOld && parsedNew && (
parsedOld.type !== parsedNew.type
|| parsedOld.channel !== parsedNew.channel
|| parsedOld.mode !== parsedNew.mode
|| parsedOld.key !== parsedNew.key)) {
if (!parsedOld.channel) { oldHref = newHref; return; }
// Compare the URLs without /embed and /present
var parsedOld = Hash.parsePadUrl(oldHref);
var parsedNew = Hash.parsePadUrl(newHref);
if (parsedOld.hashData && parsedNew.hashData &&
parsedOld.getUrl() !== parsedNew.getUrl()) {
if (!parsedOld.hashData.key) { oldHref = newHref; return; }
// If different, reload
document.location.reload();
return;
}
if (parsedNew) { oldHref = newHref; }
if (parsedNew.hashData) { oldHref = newHref; }
};
// Listen for login/logout in other tabs
window.addEventListener('storage', function (e) {

View File

@@ -41,6 +41,7 @@ define([
};
renderer.image = function (href, title, text) {
if (href.slice(0,6) === '/file/') {
// PASSWORD_FILES
var parsed = Hash.parsePadUrl(href);
var hexFileName = Util.base64ToHex(parsed.hashData.channel);
var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName;

File diff suppressed because one or more lines are too long

6
www/common/jquery-ui/jquery-ui.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -1,94 +1,3 @@
define([], function () {
var loadingStyle = (function(){/*
#cp-loading {
transition: opacity 0.75s, visibility 0s 0.75s;
visibility: visible;
position: fixed;
z-index: 10000000;
top: 0px;
bottom: 0px;
left: 0px;
right: 0px;
background: #222;
color: #fafafa;
text-align: center;
font-size: 1.5em;
opacity: 1;
}
#cp-loading.cp-loading-hidden {
opacity: 0;
visibility: hidden;
}
#cp-loading .cp-loading-container {
margin-top: 50vh;
transform: translateY(-50%);
}
#cp-loading .cp-loading-cryptofist {
margin-left: auto;
margin-right: auto;
height: 300px;
margin-bottom: 2em;
}
@media screen and (max-height: 450px) {
#cp-loading .cp-loading-cryptofist {
display: none;
}
}
#cp-loading .cp-loading-spinner-container {
position: relative;
height: 100px;
}
#cp-loading .cp-loading-spinner-container > div {
height: 100px;
}
#cp-loading-tip {
position: fixed;
z-index: 10000000;
top: 80%;
left: 0;
right: 0;
text-align: center;
transition: opacity 750ms;
transition-delay: 3000ms;
}
@media screen and (max-height: 600px) {
#cp-loading-tip {
display: none;
}
}
#cp-loading-tip span {
background: #222;
color: #fafafa;
text-align: center;
font-size: 1.5em;
opacity: 0.7;
font-family: 'Open Sans', 'Helvetica Neue', sans-serif;
padding: 15px;
max-width: 60%;
display: inline-block;
}
*/}).toString().slice(14, -3);
var urlArgs = window.location.href.replace(/^.*\?([^\?]*)$/, function (all, x) { return x; });
var elem = document.createElement('div');
elem.setAttribute('id', 'cp-loading');
elem.innerHTML = [
'<style>',
loadingStyle,
'</style>',
'<div class="cp-loading-container">',
'<img class="cp-loading-cryptofist" src="/customize/cryptpad-new-logo-colors-logoonly.png?' + urlArgs + '">',
'<div class="cp-loading-spinner-container">',
'<span class="fa fa-circle-o-notch fa-spin fa-4x fa-fw"></span>',
'</div>',
'<p id="cp-loading-message"></p>',
'</div>'
].join('');
var intr;
var append = function () {
if (!document.body) { return; }
clearInterval(intr);
document.body.appendChild(elem);
};
intr = setInterval(append, 100);
append();
require(['/customize/loading.js'], function (Loading) {
Loading();
});

View File

@@ -122,21 +122,15 @@ define([
// Do not migrate a pad if we already have it, it would create a duplicate in the drive
if (newHrefs.indexOf(href) !== -1) { return; }
// If we have a stronger version, do not add the current href
if (Hash.findStronger(href, newRecentPads)) { return; }
if (Hash.findStronger(href, oldRecentPads[id].channel, newRecentPads)) { return; }
// If we have a weaker version, replace the href by the new one
// NOTE: if that weaker version is in the trash, the strong one will be put in unsorted
var weaker = Hash.findWeaker(href, newRecentPads);
var weaker = Hash.findWeaker(href, oldRecentPads[id].channel, newRecentPads);
if (weaker) {
// Update RECENTPADS
newRecentPads.some(function (pad) {
if (pad.href === weaker) {
pad.href = href;
return true;
}
return;
});
weaker.href = href;
// Update the file in the drive
newFo.replace(weaker, href);
newFo.replace(weaker.href, href);
return;
}
// Here it means we have a new href, so we should add it to the drive at its old location

View File

@@ -34,6 +34,10 @@ define(['json.sortify'], function (Sortify) {
}
if (!metadataObj.users) { metadataObj.users = {}; }
if (!metadataLazyObj.users) { metadataLazyObj.users = {}; }
if (!metadataObj.type) { metadataObj.type = meta.doc.type; }
if (!metadataLazyObj.type) { metadataLazyObj.type = meta.doc.type; }
var mdo = {};
// We don't want to add our user data to the object multiple times.
//var containsYou = false;

View File

@@ -1,106 +1,152 @@
define(['/common/common-feedback.js'], function (Feedback) {
define([
'/common/common-feedback.js',
'/common/common-hash.js',
'/common/common-util.js',
'/bower_components/nthen/index.js',
], function (Feedback, Hash, Util, nThen) {
// Start migration check
// Versions:
// 1: migrate pad attributes
// 2: migrate indent settings (codemirror)
return function (userObject) {
return function (userObject, cb, progress) {
var version = userObject.version || 0;
// DEPRECATED
// Migration 1: pad attributes moved to filesData
var migratePadAttributesToData = function () {
return true;
};
if (version < 1) {
migratePadAttributesToData();
}
// Migration 2: global attributes from root to 'settings' subobjects
var migrateAttributes = function () {
var drawer = 'cryptpad.userlist-drawer';
var polls = 'cryptpad.hide_poll_text';
var indentKey = 'cryptpad.indentUnit';
var useTabsKey = 'cryptpad.indentWithTabs';
var settings = userObject.settings = userObject.settings || {};
if (typeof(userObject[indentKey]) !== "undefined") {
settings.codemirror = settings.codemirror || {};
settings.codemirror.indentUnit = userObject[indentKey];
delete userObject[indentKey];
nThen(function () {
// DEPRECATED
// Migration 1: pad attributes moved to filesData
var migratePadAttributesToData = function () {
return true;
};
if (version < 1) {
migratePadAttributesToData();
}
if (typeof(userObject[useTabsKey]) !== "undefined") {
settings.codemirror = settings.codemirror || {};
settings.codemirror.indentWithTabs = userObject[useTabsKey];
delete userObject[useTabsKey];
}).nThen(function () {
// Migration 2: global attributes from root to 'settings' subobjects
var migrateAttributes = function () {
var drawer = 'cryptpad.userlist-drawer';
var polls = 'cryptpad.hide_poll_text';
var indentKey = 'cryptpad.indentUnit';
var useTabsKey = 'cryptpad.indentWithTabs';
var settings = userObject.settings = userObject.settings || {};
if (typeof(userObject[indentKey]) !== "undefined") {
settings.codemirror = settings.codemirror || {};
settings.codemirror.indentUnit = userObject[indentKey];
delete userObject[indentKey];
}
if (typeof(userObject[useTabsKey]) !== "undefined") {
settings.codemirror = settings.codemirror || {};
settings.codemirror.indentWithTabs = userObject[useTabsKey];
delete userObject[useTabsKey];
}
if (typeof(userObject[drawer]) !== "undefined") {
settings.toolbar = settings.toolbar || {};
settings.toolbar['userlist-drawer'] = userObject[drawer];
delete userObject[drawer];
}
if (typeof(userObject[polls]) !== "undefined") {
settings.poll = settings.poll || {};
settings.poll['hide-text'] = userObject[polls];
delete userObject[polls];
}
};
if (version < 2) {
migrateAttributes();
Feedback.send('Migrate-2', true);
userObject.version = version = 2;
}
if (typeof(userObject[drawer]) !== "undefined") {
settings.toolbar = settings.toolbar || {};
settings.toolbar['userlist-drawer'] = userObject[drawer];
delete userObject[drawer];
}).nThen(function () {
// Migration 3: language from localStorage to settings
var migrateLanguage = function () {
if (!localStorage.CRYPTPAD_LANG) { return; }
var l = localStorage.CRYPTPAD_LANG;
userObject.settings.language = l;
};
if (version < 3) {
migrateLanguage();
Feedback.send('Migrate-3', true);
userObject.version = version = 3;
}
if (typeof(userObject[polls]) !== "undefined") {
settings.poll = settings.poll || {};
settings.poll['hide-text'] = userObject[polls];
delete userObject[polls];
}).nThen(function () {
// Migration 4: allowUserFeedback to settings
var migrateFeedback = function () {
var settings = userObject.settings = userObject.settings || {};
if (typeof(userObject['allowUserFeedback']) !== "undefined") {
settings.general = settings.general || {};
settings.general.allowUserFeedback = userObject['allowUserFeedback'];
delete userObject['allowUserFeedback'];
}
};
if (version < 4) {
migrateFeedback();
Feedback.send('Migrate-4', true);
userObject.version = version = 4;
}
};
if (version < 2) {
migrateAttributes();
Feedback.send('Migrate-2', true);
userObject.version = version = 2;
}
// Migration 3: language from localStorage to settings
var migrateLanguage = function () {
if (!localStorage.CRYPTPAD_LANG) { return; }
var l = localStorage.CRYPTPAD_LANG;
userObject.settings.language = l;
};
if (version < 3) {
migrateLanguage();
Feedback.send('Migrate-3', true);
userObject.version = version = 3;
}
// Migration 4: allowUserFeedback to settings
var migrateFeedback = function () {
var settings = userObject.settings = userObject.settings || {};
if (typeof(userObject['allowUserFeedback']) !== "undefined") {
settings.general = settings.general || {};
settings.general.allowUserFeedback = userObject['allowUserFeedback'];
delete userObject['allowUserFeedback'];
}
};
if (version < 4) {
migrateFeedback();
Feedback.send('Migrate-4', true);
userObject.version = version = 4;
}
// Migration 5: dates to Number
var migrateDates = function () {
var data = userObject.drive && userObject.drive.filesData;
if (data) {
for (var id in data) {
if (typeof data[id].ctime !== "number") {
data[id].ctime = +new Date(data[id].ctime);
}
if (typeof data[id].atime !== "number") {
data[id].atime = +new Date(data[id].atime);
}).nThen(function () {
// Migration 5: dates to Number
var migrateDates = function () {
var data = userObject.drive && userObject.drive.filesData;
if (data) {
for (var id in data) {
if (typeof data[id].ctime !== "number") {
data[id].ctime = +new Date(data[id].ctime);
}
if (typeof data[id].atime !== "number") {
data[id].atime = +new Date(data[id].atime);
}
}
}
};
if (version < 5) {
migrateDates();
Feedback.send('Migrate-5', true);
userObject.version = version = 5;
}
};
if (version < 5) {
migrateDates();
Feedback.send('Migrate-5', true);
userObject.version = version = 5;
}
}).nThen(function (waitFor) {
var addChannelId = function () {
var data = userObject.drive.filesData;
var el, parsed;
var n = nThen(function () {});
var padsLength = Object.keys(data).length;
Object.keys(data).forEach(function (k, i) {
n = n.nThen(function (w) {
setTimeout(w(function () {
el = data[k];
parsed = Hash.parsePadUrl(el.href);
if (!el.href) { return; }
if (!el.channel) {
if (parsed.hashData && parsed.hashData.type === "file") {
// PASSWORD_FILES
el.channel = Util.base64ToHex(parsed.hashData.channel);
} else {
var secret = Hash.getSecrets(parsed.type, parsed.hash, el.password);
el.channel = secret.channel;
}
progress(6, Math.round(100*i/padsLength));
console.log('Adding missing channel in filesData ', el.channel);
}
}));
});
});
n.nThen(waitFor());
};
if (version < 6) {
addChannelId();
Feedback.send('Migrate-6', true);
userObject.version = version = 6;
}
/*}).nThen(function (waitFor) {
// Test progress bar in the loading screen
var i = 0;
var w = waitFor();
var it = setInterval(function () {
i += 5;
if (i >= 100) { w(); clearInterval(it); i = 100;}
progress(0, i);
}, 500);
progress(0, 0);*/
}).nThen(function () {
cb();
});
};
});

View File

@@ -16,9 +16,11 @@ define([
'/bower_components/chainpad-crypto/crypto.js?v=0.1.5',
'/bower_components/chainpad/chainpad.dist.js',
'/bower_components/chainpad-listmap/chainpad-listmap.js',
'/bower_components/nthen/index.js',
'/bower_components/saferphore/index.js',
], function (Sortify, UserObject, Migrate, Hash, Util, Constants, Feedback, Realtime, Messaging, Messenger,
CpNfWorker, NetConfig, AppConfig,
Crypto, ChainPad, Listmap) {
Crypto, ChainPad, Listmap, nThen, Saferphore) {
var Store = {};
var postMessage = function () {};
@@ -66,8 +68,9 @@ define([
var userHash = storeHash;
if (!userHash) { return null; }
var userParsedHash = Hash.parseTypeHash('drive', userHash);
var userChannel = userParsedHash && userParsedHash.channel;
// No password for drive
var secret = Hash.getSecrets('drive', userHash);
var userChannel = secret.channel;
if (!userChannel) { return null; }
// Get the list of pads' channel ID in your drive
@@ -79,16 +82,16 @@ define([
var d = store.userObject.getFileData(id);
if (d.owners && d.owners.length && edPublic &&
d.owners.indexOf(edPublic) === -1) { return; }
return Hash.hrefToHexChannelId(d.href);
return d.channel;
})
.filter(function (x) { return x; });
// Get the avatar
var profile = store.proxy.profile;
if (profile) {
var profileChan = profile.edit ? Hash.hrefToHexChannelId('/profile/#' + profile.edit) : null;
var profileChan = profile.edit ? Hash.hrefToHexChannelId('/profile/#' + profile.edit, null) : null;
if (profileChan) { list.push(profileChan); }
var avatarChan = profile.avatar ? Hash.hrefToHexChannelId(profile.avatar) : null;
var avatarChan = profile.avatar ? Hash.hrefToHexChannelId(profile.avatar, null) : null;
if (avatarChan) { list.push(avatarChan); }
}
@@ -97,7 +100,7 @@ define([
list = list.concat(fList);
}
list.push(Util.base64ToHex(userChannel));
list.push(userChannel);
list.sort();
return list;
@@ -113,7 +116,7 @@ define([
// because of the expiration time
if ((data.owners && data.owners.length && data.owners.indexOf(edPublic) === -1) ||
(data.expire && data.expire < (+new Date()))) {
list.push(Hash.hrefToHexChannelId(data.href));
list.push(data.channel);
}
});
return list;
@@ -301,7 +304,7 @@ define([
Store.getFileSize = function (data, cb) {
if (!store.anon_rpc) { return void cb({error: 'ANON_RPC_NOT_READY'}); }
var channelId = Hash.hrefToHexChannelId(data.href);
var channelId = Hash.hrefToHexChannelId(data.href, data.password);
store.anon_rpc.send("GET_FILE_SIZE", channelId, function (e, response) {
if (e) { return void cb({error: e}); }
if (response && response.length && typeof(response[0]) === 'number') {
@@ -314,7 +317,7 @@ define([
Store.isNewChannel = function (data, cb) {
if (!store.anon_rpc) { return void cb({error: 'ANON_RPC_NOT_READY'}); }
var channelId = Hash.hrefToHexChannelId(data.href);
var channelId = Hash.hrefToHexChannelId(data.href, data.password);
store.anon_rpc.send("IS_NEW_CHANNEL", channelId, function (e, response) {
if (e) { return void cb({error: e}); }
if (response && response.length && typeof(response[0]) === 'boolean') {
@@ -378,6 +381,7 @@ define([
// Get the metadata for sframe-common-outer
Store.getMetadata = function (data, cb) {
var disableThumbnails = Util.find(store.proxy, ['settings', 'general', 'disableThumbnails']);
var metadata = {
// "user" is shared with everybody via the userlist
user: {
@@ -392,7 +396,7 @@ define([
edPublic: store.proxy.edPublic,
friends: store.proxy.friends || {},
settings: store.proxy.settings,
thumbnails: !Util.find(store.proxy, ['settings', 'general', 'disableThumbnails'])
thumbnails: disableThumbnails === false
}
};
cb(JSON.parse(JSON.stringify(metadata)));
@@ -413,6 +417,8 @@ define([
var pad = makePad(data.href, data.title);
if (data.owners) { pad.owners = data.owners; }
if (data.expire) { pad.expire = data.expire; }
if (data.password) { pad.password = data.password; }
if (data.channel) { pad.channel = data.channel; }
store.userObject.pushData(pad, function (e, id) {
if (e) { return void cb({error: "Error while adding a template:"+ e}); }
var path = data.path || ['root'];
@@ -421,20 +427,98 @@ define([
});
};
var getOwnedPads = function () {
var list = [];
store.userObject.getFiles([store.userObject.FILES_DATA]).forEach(function (id) {
var data = store.userObject.getFileData(id);
var edPublic = store.proxy.edPublic;
// Push channels owned by someone else or channel that should have expired
// because of the expiration time
if (data.owners && data.owners.length === 1 && data.owners.indexOf(edPublic) !== -1) {
list.push(data.channel);
}
});
if (store.proxy.todo) {
// No password for todo
list.push(Hash.hrefToHexChannelId('/todo/#' + store.proxy.todo, null));
}
if (store.proxy.profile && store.proxy.profile.edit) {
// No password for profile
list.push(Hash.hrefToHexChannelId('/profile/#' + store.proxy.profile.edit, null));
}
return list;
};
var removeOwnedPads = function (waitFor) {
// Delete owned pads
var ownedPads = getOwnedPads();
var sem = Saferphore.create(10);
ownedPads.forEach(function (c) {
var w = waitFor();
sem.take(function (give) {
Store.removeOwnedChannel(c, give(function (obj) {
if (obj && obj.error) { console.error(obj.error); }
w();
}));
});
});
};
Store.deleteAccount = function (data, cb) {
var toSign = {
intent: 'Please delete my account.'
};
var edPublic = store.proxy.edPublic;
// No password for drive
var secret = Hash.getSecrets('drive', storeHash);
toSign.drive = secret.channel;
toSign.edPublic = store.proxy.edPublic;
var signKey = Crypto.Nacl.util.decodeBase64(secret.keys.signKey);
console.log(Sortify(toSign));
var proof = Crypto.Nacl.sign.detached(Crypto.Nacl.util.decodeUTF8(Sortify(toSign)), signKey);
var proofTxt = Crypto.Nacl.util.encodeBase64(proof);
cb({
proof: proofTxt,
toSign: JSON.parse(Sortify(toSign))
Store.anonRpcMsg({
msg: 'GET_METADATA',
data: secret.channel
}, function (data) {
var metadata = data[0];
// Owned drive
if (metadata && metadata.owners && metadata.owners.length === 1 &&
metadata.owners.indexOf(edPublic) !== -1) {
nThen(function (waitFor) {
var token = Math.floor(Math.random()*Number.MAX_SAFE_INTEGER);
store.proxy[Constants.tokenKey] = token;
postMessage("DELETE_ACCOUNT", token, waitFor());
}).nThen(function (waitFor) {
removeOwnedPads(waitFor);
}).nThen(function (waitFor) {
// Delete Pin Store
store.rpc.removePins(waitFor(function (err) {
if (err) { console.error(err); }
}));
}).nThen(function (waitFor) {
// Delete Drive
Store.removeOwnedChannel(secret.channel, waitFor());
}).nThen(function () {
store.network.disconnect();
cb({
state: true
});
});
return;
}
// Not owned drive
var toSign = {
intent: 'Please delete my account.'
};
toSign.drive = secret.channel;
toSign.edPublic = edPublic;
var signKey = Crypto.Nacl.util.decodeBase64(store.proxy.edPrivate);
var proof = Crypto.Nacl.sign.detached(Crypto.Nacl.util.decodeUTF8(Sortify(toSign)), signKey);
var check = Crypto.Nacl.sign.detached.verify(Crypto.Nacl.util.decodeUTF8(Sortify(toSign)),
proof,
Crypto.Nacl.util.decodeBase64(edPublic));
if (!check) { console.error('signed message failed verification'); }
var proofTxt = Crypto.Nacl.util.encodeBase64(proof);
cb({
proof: proofTxt,
toSign: JSON.parse(Sortify(toSign))
});
});
};
@@ -446,25 +530,19 @@ define([
*/
Store.createReadme = function (data, cb) {
require(['/common/cryptget.js'], function (Crypt) {
var hash = Hash.createRandomHash();
var hash = Hash.createRandomHash('pad');
Crypt.put(hash, data.driveReadme, function (e) {
if (e) {
return void cb({ error: "Error while creating the default pad:"+ e});
}
var href = '/pad/#' + hash;
var channel = Hash.hrefToHexChannelId(href, null);
var fileData = {
href: href,
channel: channel,
title: data.driveReadmeTitle,
atime: +new Date(),
ctime: +new Date()
};
store.userObject.pushData(fileData, function (e, id) {
if (e) {
return void cb({ error: "Error while creating the default pad:"+ e});
}
store.userObject.add(id);
onSync(cb);
});
Store.addPad(fileData, cb);
});
});
};
@@ -515,8 +593,12 @@ define([
// Reset the drive part of the userObject (from settings)
Store.resetDrive = function (data, cb) {
store.proxy.drive = store.fo.getStructure();
onSync(cb);
nThen(function (waitFor) {
removeOwnedPads(waitFor);
}).nThen(function () {
store.proxy.drive = store.fo.getStructure();
onSync(cb);
});
};
/**
@@ -554,18 +636,7 @@ define([
// Tags
Store.listAllTags = function (data, cb) {
var all = [];
var files = Util.find(store.proxy, ['drive', 'filesData']);
if (typeof(files) !== 'object') { return cb({error: 'invalid_drive'}); }
Object.keys(files).forEach(function (k) {
var file = files[k];
if (!Array.isArray(file.tags)) { return; }
file.tags.forEach(function (tag) {
if (all.indexOf(tag) === -1) { all.push(tag); }
});
});
cb(all);
cb(store.userObject.getTagsList());
};
// Templates
@@ -578,6 +649,15 @@ define([
});
cb(res);
};
Store.incrementTemplateUse = function (href) {
store.userObject.getPadAttribute(href, 'used', function (err, data) {
// This is a not critical function, abort in case of error to make sure we won't
// create any issue with the user object or the async store
if (err) { return; }
var used = typeof data === "number" ? ++data : 1;
store.userObject.setPadAttribute(href, 'used', used);
});
};
// Pads
Store.moveToTrash = function (data, cb) {
@@ -588,17 +668,18 @@ define([
Store.setPadTitle = function (data, cb) {
var title = data.title;
var href = data.href;
var channel = data.channel;
var p = Hash.parsePadUrl(href);
var h = p.hashData;
if (AppConfig.disableAnonymousStore && !store.loggedIn) { return void cb(); }
var owners;
if (Store.channel && Store.channel.wc && Util.base64ToHex(h.channel) === Store.channel.wc.id) {
if (Store.channel && Store.channel.wc && channel === Store.channel.wc.id) {
owners = Store.channel.data.owners || undefined;
}
var expire;
if (Store.channel && Store.channel.wc && Util.base64ToHex(h.channel) === Store.channel.wc.id) {
if (Store.channel && Store.channel.wc && channel === Store.channel.wc.id) {
expire = +Store.channel.data.expire || undefined;
}
@@ -621,13 +702,13 @@ define([
// Different types, proceed to the next one
// No hash data: corrupted pad?
if (p.type !== p2.type || !h2) { continue; }
// Different channel: continue
if (pad.channel !== channel) { continue; }
var shouldUpdate = p.hash.replace(/\/$/, '') === p2.hash.replace(/\/$/, '');
// If the hash is different but represents the same channel, check if weaker or stronger
if (!shouldUpdate &&
h.version === 1 && h2.version === 1 &&
h.channel === h2.channel) {
if (!shouldUpdate && h.version !== 0) {
// We had view & now we have edit, update
if (h2.mode === 'view' && h.mode === 'edit') { shouldUpdate = true; }
// Same mode and we had present URL, update
@@ -664,10 +745,12 @@ define([
if (!contains) {
Store.addPad({
href: href,
channel: channel,
title: title,
owners: owners,
expire: expire,
path: data.path || (store.data && store.data.initialPath)
password: data.password,
path: data.path
}, cb);
return;
}
@@ -707,10 +790,7 @@ define([
Store.getPadData = function (id, cb) {
cb(store.userObject.getFileData(id));
};
Store.setInitialPath = function (path) {
if (!store.data) { return; }
store.data.initialPath = path;
};
// Messaging (manage friends from the userlist)
var getMessagingCfg = function () {
@@ -742,9 +822,9 @@ define([
var allPads = Util.find(store.proxy, ['drive', 'filesData']) || {};
// If we have a stronger version in drive, add it and add a redirect button
var stronger = Hash.findStronger(data.href, allPads);
var stronger = Hash.findStronger(data.href, data.channel, allPads);
if (stronger) {
var parsed2 = Hash.parsePadUrl(stronger);
var parsed2 = Hash.parsePadUrl(stronger.href);
return void cb(parsed2.hash);
}
cb();
@@ -836,8 +916,12 @@ define([
channel.data = padData || {};
postMessage("PAD_READY");
}, // post EV_PAD_READY
onMessage: function (m) {
postMessage("PAD_MESSAGE", m);
onMessage: function (user, m, validateKey) {
postMessage("PAD_MESSAGE", {
user: user,
msg: m,
validateKey: validateKey
});
}, // post EV_PAD_MESSAGE
onJoin: function (m) {
postMessage("PAD_JOIN", m);
@@ -906,7 +990,7 @@ define([
if (parsed[1][3] !== data.channel) { return; }
msg = parsed[1][4];
if (msg) {
msg = msg.replace(/^cp\|/, '');
msg = msg.replace(/cp\|(([A-Za-z0-9+\/=]+)\|)?/, '');
//var decryptedMsg = crypto.decrypt(msg, true);
msgs.push(msg);
}
@@ -956,11 +1040,24 @@ define([
postMessage("DRIVE_LOG", msg);
}
});
var todo = function () {
nThen(function (waitFor) {
postMessage('LOADING_DRIVE', {
state: 2
});
userObject.migrate(waitFor());
}).nThen(function (waitFor) {
Migrate(proxy, waitFor(), function (version, progress) {
postMessage('LOADING_DRIVE', {
state: 2,
progress: progress
});
});
}).nThen(function () {
postMessage('LOADING_DRIVE', {
state: 3
});
userObject.fixFiles();
Migrate(proxy);
var requestLogin = function () {
postMessage("REQUEST_LOGIN");
};
@@ -1023,16 +1120,16 @@ define([
proxy.on('change', [Constants.tokenKey], function () {
postMessage("UPDATE_TOKEN", { token: proxy[Constants.tokenKey] });
});
};
userObject.migrate(todo);
});
};
var connect = function (data, cb) {
var hash = data.userHash || data.anonHash || Hash.createRandomHash();
var hash = data.userHash || data.anonHash || Hash.createRandomHash('drive');
storeHash = hash;
if (!hash) {
throw new Error('[Store.init] Unable to find or create a drive hash. Aborting...');
}
// No password for drive
var secret = Hash.getSecrets('drive', hash);
var listmapConfig = {
data: {},
@@ -1055,7 +1152,7 @@ define([
store.realtime = info.realtime;
store.network = info.network;
if (!data.userHash) {
returned.anonHash = Hash.getEditHashFromKeys(info.channel, secret.keys);
returned.anonHash = Hash.getEditHashFromKeys(secret);
}
}).on('ready', function () {
if (store.userObject) { return; } // the store is already ready, it is a reconnection
@@ -1066,6 +1163,7 @@ define([
&& !drive['filesData']) {
drive[Constants.oldStorageKey] = [];
}
postMessage('LOADING_DRIVE', { state: 1 });
// Drive already exist: return the existing drive, don't load data from legacy store
onReady(returned, cb);
})

View File

@@ -68,7 +68,7 @@ define([], function () {
// shim between chainpad and netflux
var msgIn = function (peerId, msg) {
return msg.replace(/^cp\|/, '');
return msg.replace(/^cp\|([A-Za-z0-9+\/=]+\|)?/, '');
};
var msgOut = function (msg) {
@@ -130,7 +130,7 @@ define([], function () {
message = unBencode(message);//.slice(message.indexOf(':[') + 1);
// pass the message into Chainpad
onMessage(message);
onMessage(peer, message, validateKey);
//sframeChan.query('Q_RT_MESSAGE', message, function () { });
};

View File

@@ -92,7 +92,7 @@ define([
});
};
var logoutHandlers = [];
LocalStore.logout = function (cb) {
LocalStore.logout = function (cb, isDeletion) {
[
Constants.userNameKey,
Constants.userHashKey,
@@ -108,13 +108,15 @@ define([
// Make sure we have an FS_hash in localStorage before reloading all the tabs
// so that we don't end up with tabs using different anon hashes
if (!LocalStore.getFSHash()) {
LocalStore.setFSHash(Hash.createRandomHash());
LocalStore.setFSHash(Hash.createRandomHash('drive'));
}
eraseTempSessionValues();
logoutHandlers.forEach(function (h) {
if (typeof (h) === "function") { h(); }
});
if (!isDeletion) {
logoutHandlers.forEach(function (h) {
if (typeof (h) === "function") { h(); }
});
}
if (typeof(AppConfig.customizeLogout) === 'function') {
return void AppConfig.customizeLogout(cb);

View File

@@ -120,12 +120,12 @@ define([
case 'GET_PAD_DATA': {
Store.getPadData(data, cb); break;
}
case 'SET_INITIAL_PATH': {
Store.setInitialPath(data); break;
}
case 'GET_STRONGER_HASH': {
Store.getStrongerHash(data, cb); break;
}
case 'INCREMENT_TEMPLATE_USE': {
Store.incrementTemplateUse(data); break;
}
// Messaging
case 'INVITE_FROM_USERLIST': {
Store.inviteFromUserlist(data, cb); break;

View File

@@ -51,14 +51,28 @@ define([
var b64Key = Nacl.util.encodeBase64(key);
var hash = Hash.getFileHashFromKeys(id, b64Key);
var secret = {
version: 1,
channel: id,
keys: {
fileKeyStr: b64Key
}
};
var hash = Hash.getFileHashFromKeys(secret);
var href = '/file/#' + hash;
var title = metadata.name;
if (noStore) { return void onComplete(href); }
common.setPadTitle(title || "", href, path, function (err) {
// PASSWORD_FILES
var data = {
title: title || "",
href: href,
path: path,
channel: id
};
common.setPadTitle(data, function (err) {
if (err) { return void console.error(err); }
onComplete(href);
common.setPadAttribute('fileType', metadata.type, null, href);

View File

@@ -75,7 +75,7 @@ define([
return void todo();
}
if (!pinPads) { return; }
pinPads([Hash.hrefToHexChannelId(data.href)], function (obj) {
pinPads([data.channel], function (obj) {
if (obj && obj.error) { return void cb(obj.error); }
todo();
});
@@ -98,7 +98,7 @@ define([
exp.getFiles([FILES_DATA]).forEach(function (id) {
if (filesList.indexOf(id) === -1) {
var fd = exp.getFileData(id);
var channelId = fd && fd.href && Hash.hrefToHexChannelId(fd.href);
var channelId = fd.channel;
// If trying to remove an owned pad, remove it from server also
if (!isOwnPadRemoved &&
fd.owners && fd.owners.indexOf(edPublic) !== -1 && channelId) {
@@ -552,32 +552,52 @@ define([
for (var id in fd) {
id = Number(id);
var el = fd[id];
// Clean corrupted data
if (!el || typeof(el) !== "object") {
debug("An element in filesData was not an object.", el);
toClean.push(id);
continue;
}
// Clean missing href
if (!el.href) {
debug("Removing an element in filesData with a missing href.", el);
toClean.push(id);
continue;
}
if (/^https*:\/\//.test(el.href)) { el.href = Hash.getRelativeHref(el.href); }
if (!el.ctime) { el.ctime = el.atime; }
var parsed = Hash.parsePadUrl(el.href);
if (!el.title) { el.title = Hash.getDefaultName(parsed); }
// Clean invalid hash
if (!parsed.hash) {
debug("Removing an element in filesData with a invalid href.", el);
toClean.push(id);
continue;
}
// Clean invalid type
if (!parsed.type) {
debug("Removing an element in filesData with a invalid type.", el);
toClean.push(id);
continue;
}
// Fix href
if (/^https*:\/\//.test(el.href)) { el.href = Hash.getRelativeHref(el.href); }
// Fix creation time
if (!el.ctime) { el.ctime = el.atime; }
// Fix title
if (!el.title) { el.title = Hash.getDefaultName(parsed); }
// Fix channel
if (!el.channel) {
if (parsed.hashData && parsed.hashData.type === "file") {
// PASSWORD_FILES
el.channel = Util.base64ToHex(parsed.hashData.channel);
} else {
var secret = Hash.getSecrets(parsed.type, parsed.hash, el.password);
el.channel = secret.channel;
}
console.log('Adding missing channel in filesData ', el.channel);
}
if ((loggedIn || config.testMode) && rootFiles.indexOf(id) === -1) {
debug("An element in filesData was not in ROOT, TEMPLATE or TRASH.", id, el);
var newName = Hash.createChannelId();

View File

@@ -165,6 +165,17 @@ define([
});
};
exp.removePins = function (cb) {
rpc.send('REMOVE_PINS', undefined, function (e, response) {
if (e) { return void cb(e); }
if (response && response.length && response[0] === "OK") {
cb();
} else {
cb('INVALID_RESPONSE');
}
});
};
exp.uploadComplete = function (cb) {
rpc.send('UPLOAD_COMPLETE', null, function (e, res) {
if (e) { return void cb(e); }

View File

@@ -17,7 +17,7 @@ define([
'/bower_components/file-saver/FileSaver.min.js',
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
'less!/bower_components/components-font-awesome/css/font-awesome.min.css',
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
'less!/customize/src/less2/main.less',
], function (
$,
@@ -279,6 +279,8 @@ define([
var newContentStr = cpNfInner.chainpad.getUserDoc();
if (state === STATE.DELETED) { return; }
//UI.updateLoadingProgress({ state: -1 }, false);
var newPad = false;
if (newContentStr === '') { newPad = true; }
@@ -315,8 +317,7 @@ define([
privateDat.availableHashes.viewHash;
var href = privateDat.pathname + '#' + hash;
if (AppConfig.textAnalyzer && textContentGetter) {
var channelId = Hash.hrefToHexChannelId(href);
AppConfig.textAnalyzer(textContentGetter, channelId);
AppConfig.textAnalyzer(textContentGetter, privateDat.channel);
}
if (options.thumbnail && privateDat.thumbnails) {
@@ -432,6 +433,9 @@ define([
nThen(function (waitFor) {
UI.addLoadingScreen();
SFCommon.create(waitFor(function (c) { common = c; }));
/*UI.updateLoadingProgress({
state: 1
}, false);*/
}).nThen(function (waitFor) {
common.getSframeChannel().onReady(waitFor());
}).nThen(function (waitFor) {
@@ -443,7 +447,7 @@ define([
patchTransformer: options.patchTransformer || ChainPad.SmartJSONTransformer,
// cryptpad debug logging (default is 1)
// logLevel: 2,
logLevel: 1,
validateContent: options.validateContent || function (content) {
try {
JSON.parse(content);
@@ -457,7 +461,12 @@ define([
},
onRemote: onRemote,
onLocal: onLocal,
onInit: function () { stateChange(STATE.INITIALIZING); },
onInit: function () {
/*UI.updateLoadingProgress({
state: 2
}, false);*/
stateChange(STATE.INITIALIZING);
},
onReady: function () { evStart.reg(onReady); },
onConnectionChange: onConnectionChange,
onError: onError
@@ -572,6 +581,9 @@ define([
toolbar.$rightside.append($templateButton);
}
var $importTemplateButton = common.createButton('importtemplate', true);
toolbar.$drawer.append($importTemplateButton);
/* add a forget button */
toolbar.$rightside.append(common.createButton('forget', true, {}, function (err) {
if (err) { return; }

View File

@@ -41,7 +41,7 @@ define([
var patchTransformer = config.patchTransformer;
var validateContent = config.validateContent;
var avgSyncMilliseconds = config.avgSyncMilliseconds;
var logLevel = typeof(config.logLevel) !== 'undefined'? config.logLevel : 2;
var logLevel = typeof(config.logLevel) !== 'undefined'? config.logLevel : 1;
var readOnly = config.readOnly || false;
var sframeChan = config.sframeChan;
var metadataMgr = config.metadataMgr;

View File

@@ -39,9 +39,11 @@ define([], function () {
});
// shim between chainpad and netflux
var msgIn = function (msg) {
var msgIn = function (peer, msg) {
try {
var decryptedMsg = Crypto.decrypt(msg, isNewHash);
var isHk = peer.length !== 32;
var key = isNewHash ? validateKey : false;
var decryptedMsg = Crypto.decrypt(msg, key, isHk);
return decryptedMsg;
} catch (err) {
console.error(err);
@@ -53,7 +55,16 @@ define([], function () {
if (readOnly) { return; }
try {
var cmsg = Crypto.encrypt(msg);
if (msg.indexOf('[4') === 0) { cmsg = 'cp|' + cmsg; }
if (msg.indexOf('[4') === 0) {
var id = '';
if (window.nacl) {
var hash = window.nacl.hash(window.nacl.util.decodeUTF8(msg));
id = window.nacl.util.encodeBase64(hash.slice(0, 8)) + '|';
} else {
console.log("Checkpoint sent without an ID. Nacl is missing.");
}
cmsg = 'cp|' + id + cmsg;
}
return cmsg;
} catch (err) {
console.log(msg);
@@ -67,8 +78,11 @@ define([], function () {
padRpc.sendPadMsg(msg, cb);
});
var onMessage = function(msg) {
var message = msgIn(msg);
var onMessage = function(msgObj) {
if (msgObj.validateKey && !validateKey) {
validateKey = msgObj.validateKey;
}
var message = msgIn(msgObj.user, msgObj.msg);
verbose(message);

View File

@@ -332,6 +332,7 @@ define([
//var cursor = editor.getCursor();
//var cleanName = data.name.replace(/[\[\]]/g, '');
//var text = '!['+cleanName+']('+data.url+')';
// PASSWORD_FILES
var parsed = Hash.parsePadUrl(data.url);
var hexFileName = Util.base64ToHex(parsed.hashData.channel);
var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName;

View File

@@ -333,6 +333,8 @@ define([
var editor = config.ckeditor;
editor.document.on('drop', function (ev) {
var dropped = ev.data.$.dataTransfer.files;
editor.document.focus();
if (!dropped || !dropped.length) { return; }
onFileDrop(dropped, ev);
ev.data.preventDefault(true);
});

View File

@@ -24,6 +24,8 @@ define([
var Utils = {};
var AppConfig;
var Test;
var password;
var initialPathInDrive;
nThen(function (waitFor) {
// Load #2, the loading screen is up so grab whatever you need...
@@ -88,7 +90,16 @@ define([
SFrameChannel.create($('#sbox-iframe')[0].contentWindow, waitFor(function (sfc) {
sframeChan = sfc;
}), false, { cache: cache, localStore: localStore, language: Cryptpad.getLanguage() });
Cryptpad.ready(waitFor(), {
Cryptpad.loading.onDriveEvent.reg(function (data) {
if (sframeChan) { sframeChan.event('EV_LOADING_INFO', data); }
});
Cryptpad.ready(waitFor(function () {
if (sframeChan) {
sframeChan.event('EV_LOADING_INFO', {
state: -1
});
}
}), {
messenger: cfg.messaging,
driveEvents: cfg.driveEvents
});
@@ -113,6 +124,7 @@ define([
if (cfg.getSecrets) {
var w = waitFor();
// No password for drive, profile and todo
cfg.getSecrets(Cryptpad, Utils, waitFor(function (err, s) {
secret = s;
Cryptpad.getShareHashes(secret, function (err, h) {
@@ -121,19 +133,54 @@ define([
});
}));
} else {
secret = Utils.Hash.getSecrets();
if (!secret.channel) {
// New pad: create a new random channel id
secret.channel = Utils.Hash.createChannelId();
var parsed = Utils.Hash.parsePadUrl(window.location.href);
var todo = function () {
secret = Utils.Hash.getSecrets(parsed.type, void 0, password);
Cryptpad.getShareHashes(secret, waitFor(function (err, h) { hashes = h; }));
};
// Prompt the password here if we have a hash containing /p/
// or get it from the pad attributes
var needPassword = parsed.hashData && parsed.hashData.password;
if (needPassword) {
Cryptpad.getPadAttribute('password', waitFor(function (err, val) {
if (val) {
// We already know the password, use it!
password = val;
todo();
} else {
// Ask for the password and check if the pad exists
// If the pad doesn't exist, it means the password is oncorrect
// or the pad has been deleted
var correctPassword = waitFor();
sframeChan.on('Q_PAD_PASSWORD_VALUE', function (data, cb) {
password = data;
Cryptpad.isNewChannel(window.location.href, password, function (e, isNew) {
if (Boolean(isNew)) {
// Ask again in the inner iframe
// We should receive a new Q_PAD_PASSWORD_VALUE
cb(false);
} else {
todo();
correctPassword();
cb(true);
}
});
});
sframeChan.event("EV_PAD_PASSWORD");
}
}), parsed.getUrl());
return;
}
Cryptpad.getShareHashes(secret, waitFor(function (err, h) { hashes = h; }));
// If no password, continue...
todo();
}
}).nThen(function (waitFor) {
// Check if the pad exists on server
if (!window.location.hash) { isNewFile = true; return; }
if (realtime) {
Cryptpad.isNewChannel(window.location.href, waitFor(function (e, isNew) {
Cryptpad.isNewChannel(window.location.href, password, waitFor(function (e, isNew) {
if (e) { return console.error(e); }
isNewFile = Boolean(isNew);
}));
@@ -188,7 +235,9 @@ define([
},
isNewFile: isNewFile,
isDeleted: isNewFile && window.location.hash.length > 0,
forceCreationScreen: forceCreationScreen
forceCreationScreen: forceCreationScreen,
password: password,
channel: secret.channel
};
for (var k in additionalPriv) { metaObj.priv[k] = additionalPriv[k]; }
@@ -256,7 +305,13 @@ define([
sframeChan.on('Q_SET_PAD_TITLE_IN_DRIVE', function (newTitle, cb) {
currentTitle = newTitle;
setDocumentTitle();
Cryptpad.setPadTitle(newTitle, undefined, undefined, function (err) {
var data = {
password: password,
title: newTitle,
channel: secret.channel,
path: initialPathInDrive // Where to store the pad if we don't have it in our drive
};
Cryptpad.setPadTitle(data, function (err) {
cb(err);
});
});
@@ -325,7 +380,9 @@ define([
validateKey: secret.keys.validateKey
}, function (encryptedMsgs) {
cb(encryptedMsgs.map(function (msg) {
return crypto.decrypt(msg, true);
// The 3rd parameter "true" means we're going to skip signature validation.
// We don't need it since the message is already validated serverside by hk
return crypto.decrypt(msg, true, true);
}));
});
});
@@ -336,6 +393,7 @@ define([
// If we have a stronger hash, use it for pad attributes
href = window.location.pathname + '#' + hashes.editHash;
}
if (data.href) { href = data.href; }
Cryptpad.getPadAttribute(data.key, function (e, data) {
cb({
error: e,
@@ -349,6 +407,7 @@ define([
// If we have a stronger hash, use it for pad attributes
href = window.location.pathname + '#' + hashes.editHash;
}
if (data.href) { href = data.href; }
Cryptpad.setPadAttribute(data.key, data.value, function (e) {
cb({error:e});
}, href);
@@ -429,6 +488,8 @@ define([
// File picker
var FP = {};
var initFilePicker = function (cfg) {
// cfg.hidden means pre-loading the filepicker while keeping it hidden.
// if cfg.hidden is true and the iframe already exists, do nothing
if (!FP.$iframe) {
var config = {};
config.onFilePicked = function (data) {
@@ -447,7 +508,7 @@ define([
};
FP.$iframe = $('<iframe>', {id: 'sbox-filePicker-iframe'}).appendTo($('body'));
FP.picker = FilePicker.create(config);
} else {
} else if (!cfg.hidden) {
FP.$iframe.show();
FP.picker.refresh(cfg);
}
@@ -469,9 +530,9 @@ define([
cb(templates.length > 0);
});
});
var getKey = function (href) {
var getKey = function (href, channel) {
var parsed = Utils.Hash.parsePadUrl(href);
return 'thumbnail-' + parsed.type + '-' + parsed.hashData.channel;
return 'thumbnail-' + parsed.type + '-' + channel;
};
sframeChan.on('Q_CREATE_TEMPLATES', function (type, cb) {
Cryptpad.getSecureFilesList({
@@ -484,12 +545,13 @@ define([
var res = [];
nThen(function (waitFor) {
Object.keys(data).map(function (el) {
var k = getKey(data[el].href);
var k = getKey(data[el].href, data[el].channel);
Utils.LocalStore.getThumbnail(k, waitFor(function (e, thumb) {
res.push({
id: el,
name: data[el].filename || data[el].title || '?',
thumbnail: thumb
thumbnail: thumb,
used: data[el].used || 0
});
}));
});
@@ -513,19 +575,6 @@ define([
}
});
sframeChan.on('Q_TAGS_GET', function (data, cb) {
Cryptpad.getPadTags(data, function (err, data) {
cb({
error: err,
data: data
});
});
});
sframeChan.on('EV_TAGS_SET', function (data) {
Cryptpad.resetTags(data.href, data.tags);
});
sframeChan.on('Q_PIN_GET_USAGE', function (data, cb) {
Cryptpad.isOverPinLimit(function (err, overLimit, data) {
cb({
@@ -546,6 +595,15 @@ define([
Cryptpad.removeOwnedChannel(channel, cb);
});
sframeChan.on('Q_GET_ALL_TAGS', function (data, cb) {
Cryptpad.listAllTags(function (err, tags) {
cb({
error: err,
tags: tags
});
});
});
if (cfg.addRpc) {
cfg.addRpc(sframeChan, Cryptpad, Utils);
}
@@ -630,7 +688,7 @@ define([
isNewHash: isNewHash,
readOnly: readOnly,
crypto: Crypto.createEncryptor(secret.keys),
onConnect: function (wc) {
onConnect: function () {
if (window.location.hash && window.location.hash !== '#') {
window.location = parsed.getUrl({
present: parsed.hashData.present,
@@ -639,20 +697,36 @@ define([
return;
}
if (readOnly || cfg.noHash) { return; }
replaceHash(Utils.Hash.getEditHashFromKeys(wc, secret.keys));
replaceHash(Utils.Hash.getEditHashFromKeys(secret));
}
};
Object.keys(rtConfig).forEach(function (k) {
cpNfCfg[k] = rtConfig[k];
nThen(function (waitFor) {
if (isNewFile && cfg.owned && !window.location.hash) {
Cryptpad.getMetadata(waitFor(function (err, m) {
cpNfCfg.owners = [m.priv.edPublic];
}));
} else if (isNewFile && !cfg.useCreationScreen && window.location.hash) {
console.log("new file with hash in the address bar in an app without pcs and which requires owners");
sframeChan.onReady(function () {
sframeChan.query("EV_LOADING_ERROR", "DELETED");
});
waitFor.abort();
}
}).nThen(function () {
Object.keys(rtConfig).forEach(function (k) {
cpNfCfg[k] = rtConfig[k];
});
CpNfOuter.start(cpNfCfg);
});
CpNfOuter.start(cpNfCfg);
};
sframeChan.on('Q_CREATE_PAD', function (data, cb) {
if (!isNewFile || rtStarted) { return; }
// Create a new hash
var newHash = Utils.Hash.createRandomHash();
secret = Utils.Hash.getSecrets(parsed.type, newHash);
password = data.password;
var newHash = Utils.Hash.createRandomHash(parsed.type, password);
secret = Utils.Hash.getSecrets(parsed.type, newHash, password);
// Update the hash in the address bar
var ohc = window.onhashchange;
@@ -664,7 +738,7 @@ define([
// Update metadata values and send new metadata inside
parsed = Utils.Hash.parsePadUrl(window.location.href);
defaultTitle = Utils.Hash.getDefaultName(parsed);
hashes = Utils.Hash.getHashes(secret.channel, secret);
hashes = Utils.Hash.getHashes(secret);
readOnly = false;
updateMeta();
@@ -678,7 +752,7 @@ define([
nThen(function(waitFor) {
if (data.templateId) {
if (data.templateId === -1) {
Cryptpad.setInitialPath(['template']);
initialPathInDrive = ['template'];
return;
}
Cryptpad.getPadData(data.templateId, waitFor(function (err, d) {
@@ -706,8 +780,8 @@ define([
Utils.Feedback.reportAppUsage();
if (!realtime) { return; }
if (isNewFile && cfg.useCreationScreen) { return; }
if (!realtime && !Test.testing) { return; }
if (isNewFile && cfg.useCreationScreen && !Test.testing) { return; }
//if (isNewFile && Utils.LocalStore.isLoggedIn()
// && AppConfig.displayCreationScreen && cfg.useCreationScreen) { return; }

View File

@@ -113,6 +113,7 @@ define([
return '<script src="' + origin + '/common/media-tag-nacl.min.js"></script>';
};
funcs.getMediatagFromHref = function (href) {
// PASSWORD_FILES
var parsed = Hash.parsePadUrl(href);
var secret = Hash.getSecrets('file', parsed.hash);
var data = ctx.metadataMgr.getPrivateData();
@@ -126,8 +127,7 @@ define([
}
return;
};
funcs.getFileSize = function (href, cb) {
var channelId = Hash.hrefToHexChannelId(href);
funcs.getFileSize = function (channelId, cb) {
funcs.sendAnonRpcMsg("GET_FILE_SIZE", channelId, function (data) {
if (!data) { return void cb("No response"); }
if (data.error) { return void cb(data.error); }
@@ -170,6 +170,7 @@ define([
// Store
funcs.handleNewFile = function (waitFor) {
if (window.__CRYPTPAD_TEST__) { return; }
var priv = ctx.metadataMgr.getPrivateData();
if (priv.isNewFile) {
var c = (priv.settings.general && priv.settings.general.creation) || {};
@@ -196,6 +197,7 @@ define([
ctx.sframeChan.query("Q_CREATE_PAD", {
owned: cfg.owned,
expire: cfg.expire,
password: cfg.password,
template: cfg.template,
templateId: cfg.templateId
}, cb);
@@ -233,17 +235,20 @@ define([
});
};
funcs.getPadAttribute = function (key, cb) {
// href is optional here: if not provided, we use the href of the current tab
funcs.getPadAttribute = function (key, cb, href) {
ctx.sframeChan.query('Q_GET_PAD_ATTRIBUTE', {
key: key
key: key,
href: href
}, function (err, res) {
cb (err || res.error, res.data);
});
};
funcs.setPadAttribute = function (key, value, cb) {
funcs.setPadAttribute = function (key, value, cb, href) {
cb = cb || $.noop;
ctx.sframeChan.query('Q_SET_PAD_ATTRIBUTE', {
key: key,
href: href,
value: value
}, cb);
};
@@ -343,6 +348,27 @@ define([
window.open(bounceHref);
};
funcs.fixLinks = function (domElement) {
var origin = ctx.metadataMgr.getPrivateData().origin;
$(domElement).find('a[target="_blank"]').click(function (e) {
e.preventDefault();
e.stopPropagation();
var href = $(this).attr('href');
var absolute = /^https?:\/\//i;
if (!absolute.test(href)) {
if (href.slice(0,1) !== '/') { href = '/' + href; }
href = origin + href;
}
funcs.openUnsafeURL(href);
});
$(domElement).find('a[target!="_blank"]').click(function (e) {
e.preventDefault();
e.stopPropagation();
funcs.gotoURL($(this).attr('href'));
});
return $(domElement)[0];
};
funcs.whenRealtimeSyncs = evRealtimeSynced.reg;
var logoutHandlers = [];
@@ -397,19 +423,6 @@ define([
UI.addTooltips();
ctx.sframeChan.on('EV_LOGOUT', function () {
$(window).on('keyup', function (e) {
if (e.keyCode === 27) {
UI.removeLoadingScreen();
}
});
UI.addLoadingScreen({hideTips: true});
UI.errorLoadingScreen(Messages.onLogout, true);
logoutHandlers.forEach(function (h) {
if (typeof (h) === "function") { h(); }
});
});
ctx.sframeChan.on('Q_INCOMING_FRIEND_REQUEST', function (confirmMsg, cb) {
UI.confirm(confirmMsg, cb, null, true);
});
@@ -419,12 +432,46 @@ define([
UI.log(data.logText);
});
ctx.sframeChan.on("EV_PAD_PASSWORD", function () {
UIElements.displayPasswordPrompt(funcs);
});
ctx.sframeChan.on('EV_LOADING_INFO', function (data) {
UI.updateLoadingProgress(data, true);
});
ctx.metadataMgr.onReady(waitFor());
}).nThen(function () {
try {
var feedback = ctx.metadataMgr.getPrivateData().feedbackAllowed;
Feedback.init(feedback);
} catch (e) { Feedback.init(false); }
ctx.sframeChan.on('EV_LOADING_ERROR', function (err) {
if (err === 'DELETED') {
var msg = Messages.deletedError + '<br>' + Messages.errorRedirectToHome;
UI.errorLoadingScreen(msg, false, function () {
funcs.gotoURL('/drive/');
});
}
});
ctx.sframeChan.on('EV_LOGOUT', function () {
$(window).on('keyup', function (e) {
if (e.keyCode === 27) {
UI.removeLoadingScreen();
}
});
UI.addLoadingScreen({hideTips: true});
var origin = ctx.metadataMgr.getPrivateData().origin;
var href = origin + "/login/";
var onLogoutMsg = Messages._getKey('onLogout', ['<a href="' + href + '" target="_blank">', '</a>']);
UI.errorLoadingScreen(onLogoutMsg, true);
logoutHandlers.forEach(function (h) {
if (typeof (h) === "function") { h(); }
});
});
ctx.sframeChan.ready();
cb(funcs);
});

View File

@@ -165,10 +165,6 @@ define({
// Put one entry in the parent sessionStorage
'Q_SESSIONSTORAGE_PUT': true,
// Set and get the tags using the tag prompt button
'Q_TAGS_GET': true,
'EV_TAGS_SET': 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,
@@ -227,4 +223,17 @@ define({
// 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,
// Critical error outside the iframe during loading screen
'EV_LOADING_ERROR': true,
// Ask for the pad password when a pad is protected
'EV_PAD_PASSWORD': true,
'Q_PAD_PASSWORD_VALUE': true,
// Loading events to display in the loading screen
'EV_LOADING_INFO': true,
// Get all existing tags
'Q_GET_ALL_TAGS': true,
});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
www/common/tippy/tippy.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -576,9 +576,7 @@ define([
if (Common.isLoggedIn()) { return; }
var pd = config.metadataMgr.getPrivateData();
var o = pd.origin;
var hashes = pd.availableHashes;
var url = pd.origin + pd.pathname + '#' + (hashes.editHash || hashes.viewHash);
var cid = Hash.hrefToHexChannelId(url);
var cid = pd.channel;
Common.sendAnonRpcMsg('IS_CHANNEL_PINNED', cid, function (x) {
if (x.error || !Array.isArray(x.response)) { return void console.log(x); }
if (x.response[0] === true) {

View File

@@ -384,11 +384,9 @@ define([
// Get drive ids of files from their channel ids
exp.findChannels = function (channels) {
var allFilesList = files[FILES_DATA];
var channels64 = channels.slice().map(Util.hexToBase64);
return getFiles([FILES_DATA]).filter(function (k) {
var data = allFilesList[k];
var parsed = Hash.parsePadUrl(data.href);
return parsed.hashData && channels64.indexOf(parsed.hashData.channel) !== -1;
return channels.indexOf(data.channel) !== -1;
});
};
@@ -629,6 +627,21 @@ define([
if (typeof cb === "function") { cb(); }
};
// Tags
exp.getTagsList = function () {
var tags = {};
var data;
var pushTag = function (tag) {
tags[tag] = tags[tag] ? ++tags[tag] : 1;
};
for (var id in files[FILES_DATA]) {
data = files[FILES_DATA][id];
if (!data.tags || !Array.isArray(data.tags)) { continue; }
data.tags.forEach(pushTag);
}
return tags;
};
return exp;
};
return module;

View File

@@ -1,17 +1,13 @@
@import (once) "../../customize/src/less2/include/browser.less";
@import (once) "../../customize/src/less2/include/toolbar.less";
@import (once) "../../customize/src/less2/include/markdown.less";
@import (once) '../../customize/src/less2/include/fileupload.less';
@import (once) '../../customize/src/less2/include/alertify.less';
@import (once) '../../customize/src/less2/include/avatar.less';
@import (once) '../../customize/src/less2/include/framework.less';
.toolbar_main(
.framework_min_main(
@bg-color: @colortheme_friends-bg,
@warn-color: @colortheme_friends-warn,
@color: @colortheme_friends-color
);
.fileupload_main();
.alertify_main();
// body
&.cp-app-contacts {

View File

@@ -11,7 +11,7 @@ define([
'/common/common-interface.js',
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
'less!/bower_components/components-font-awesome/css/font-awesome.min.css',
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
'less!/customize/src/less2/main.less',
], function (
$,

View File

@@ -1,15 +1,11 @@
@import (once) "../../customize/src/less2/include/browser.less";
@import (once) "../../customize/src/less2/include/toolbar.less";
@import (once) "../../customize/src/less2/include/markdown.less";
@import (once) '../../customize/src/less2/include/fileupload.less';
@import (once) '../../customize/src/less2/include/alertify.less';
@import (once) '../../customize/src/less2/include/tools.less';
@import (once) '../../customize/src/less2/include/tokenfield.less';
@import (once) '../../customize/src/less2/include/framework.less';
.toolbar_main();
.fileupload_main();
.alertify_main();
.tokenfield_main();
.framework_min_main();
// body
&.cp-app-debug {

View File

@@ -15,7 +15,7 @@ define([
'/bower_components/secure-fabric.js/dist/fabric.min.js',
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
'less!/bower_components/components-font-awesome/css/font-awesome.min.css',
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
'less!/customize/src/less2/main.less',
], function (
$,

View File

@@ -1,20 +1,17 @@
@import (once) "../../customize/src/less2/include/browser.less";
@import (once) "../../customize/src/less2/include/toolbar.less";
@import (once) "../../customize/src/less2/include/markdown.less";
@import (once) '../../customize/src/less2/include/fileupload.less';
@import (once) '../../customize/src/less2/include/alertify.less';
@import (once) '../../customize/src/less2/include/leftside-menu.less';
@import (once) "../../customize/src/less2/include/tools.less";
@import (once) "../../customize/src/less2/include/limit-bar.less";
@import (once) "../../customize/src/less2/include/tokenfield.less";
@import (once) '../../customize/src/less2/include/framework.less';
.toolbar_main(
.framework_min_main(
@bg-color: @colortheme_drive-bg,
@warn-color: @colortheme_drive-warn,
@color: @colortheme_drive-color
);
.fileupload_main();
.alertify_main();
.limit-bar_main();
.tokenfield_main();
@@ -465,6 +462,8 @@ span {
padding-right: 15px;
}
.cp-app-drive-search-opendir {
display: flex;
justify-content: space-between;
a {
cursor: pointer;
color: #41b7d8;
@@ -498,6 +497,19 @@ span {
}
}
}
&.cp-app-drive-tags-list {
width: 100%;
table {
margin: 10px 50px;
width: ~"calc(100% - 100px)";
table-layout: fixed;
td, th {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
}
}
.cp-app-drive-element {
@@ -547,6 +559,10 @@ span {
cursor: pointer;
opacity: 0.5;
padding: 0;
flex-flow: column;
align-items: center;
justify-content: center;
display: inline-flex;
&:hover {
opacity: 0.7;
}

View File

@@ -18,7 +18,7 @@ define([
'/customize/messages.js',
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
'less!/bower_components/components-font-awesome/css/font-awesome.min.css',
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
'less!/customize/src/less2/main.less',
], function (
$,
@@ -51,20 +51,65 @@ define([
var E_OVER_LIMIT = 'E_OVER_LIMIT';
var SEARCH = "search";
var SEARCH_NAME = Messages.fm_searchName;
var ROOT = "root";
var ROOT_NAME = Messages.fm_rootName;
var SEARCH = "search";
var SEARCH_NAME = Messages.fm_searchName;
var TRASH = "trash";
var TRASH_NAME = Messages.fm_trashName;
var FILES_DATA = Constants.storageKey;
var FILES_DATA_NAME = Messages.fm_filesDataName;
var TEMPLATE = "template";
var TEMPLATE_NAME = Messages.fm_templateName;
var TRASH = "trash";
var TRASH_NAME = Messages.fm_trashName;
var RECENT = "recent";
var RECENT_NAME = Messages.fm_recentPadsName;
var OWNED = "owned";
var OWNED_NAME = Messages.fm_ownedPadsName;
var TAGS = "tags";
var TAGS_NAME = Messages.fm_tagsName;
// Icons
var faFolder = 'fa-folder';
var faFolderOpen = 'fa-folder-open';
var faReadOnly = 'fa-eye';
var faRename = 'fa-pencil';
var faTrash = 'fa-trash';
var faDelete = 'fa-eraser';
var faProperties = 'fa-database';
var faTags = 'fa-hashtag';
var faEmpty = 'fa-trash-o';
var faRestore = 'fa-repeat';
var faShowParent = 'fa-location-arrow';
var $folderIcon = $('<span>', {
"class": faFolder + " fa cp-app-drive-icon-folder cp-app-drive-content-icon"
});
//var $folderIcon = $('<img>', {src: "/customize/images/icons/folder.svg", "class": "folder icon"});
var $folderEmptyIcon = $folderIcon.clone();
var $folderOpenedIcon = $('<span>', {"class": faFolderOpen + " fa cp-app-drive-icon-folder"});
//var $folderOpenedIcon = $('<img>', {src: "/customize/images/icons/folderOpen.svg", "class": "folder icon"});
var $folderOpenedEmptyIcon = $folderOpenedIcon.clone();
//var $upIcon = $('<span>', {"class": "fa fa-arrow-circle-up"});
var $unsortedIcon = $('<span>', {"class": "fa fa-files-o"});
var $templateIcon = $('<span>', {"class": "fa fa-cubes"});
var $recentIcon = $('<span>', {"class": "fa fa-clock-o"});
var $trashIcon = $('<span>', {"class": "fa " + faTrash});
var $trashEmptyIcon = $('<span>', {"class": "fa fa-trash-o"});
//var $collapseIcon = $('<span>', {"class": "fa fa-minus-square-o cp-app-drive-icon-expcol"});
var $expandIcon = $('<span>', {"class": "fa fa-plus-square-o cp-app-drive-icon-expcol"});
var $emptyTrashIcon = $('<button>', {"class": "fa fa-ban"});
var $listIcon = $('<button>', {"class": "fa fa-list"});
var $gridIcon = $('<button>', {"class": "fa fa-th-large"});
var $sortAscIcon = $('<span>', {"class": "fa fa-angle-up sortasc"});
var $sortDescIcon = $('<span>', {"class": "fa fa-angle-down sortdesc"});
var $closeIcon = $('<span>', {"class": "fa fa-window-close"});
//var $backupIcon = $('<span>', {"class": "fa fa-life-ring"});
var $searchIcon = $('<span>', {"class": "fa fa-search cp-app-drive-tree-search-con"});
var $addIcon = $('<span>', {"class": "fa fa-plus"});
var $renamedIcon = $('<span>', {"class": "fa fa-flag"});
var $readonlyIcon = $('<span>', {"class": "fa " + faReadOnly});
var $ownedIcon = $('<span>', {"class": "fa fa-id-card-o"});
var $ownerIcon = $('<span>', {"class": "fa fa-id-card"});
var $tagsIcon = $('<span>', {"class": "fa " + faTags});
var LS_LAST = "app-drive-lastOpened";
var LS_OPENED = "app-drive-openedFolders";
@@ -157,48 +202,6 @@ define([
}
};
// Icons
var faFolder = 'fa-folder';
var faFolderOpen = 'fa-folder-open';
var faReadOnly = 'fa-eye';
var faRename = 'fa-pencil';
var faTrash = 'fa-trash';
var faDelete = 'fa-eraser';
var faProperties = 'fa-database';
var faTags = 'fa-hashtag';
var faEmpty = 'fa-trash-o';
var faRestore = 'fa-repeat';
var faShowParent = 'fa-location-arrow';
var $folderIcon = $('<span>', {
"class": faFolder + " fa cp-app-drive-icon-folder cp-app-drive-content-icon"
});
//var $folderIcon = $('<img>', {src: "/customize/images/icons/folder.svg", "class": "folder icon"});
var $folderEmptyIcon = $folderIcon.clone();
var $folderOpenedIcon = $('<span>', {"class": faFolderOpen + " fa cp-app-drive-icon-folder"});
//var $folderOpenedIcon = $('<img>', {src: "/customize/images/icons/folderOpen.svg", "class": "folder icon"});
var $folderOpenedEmptyIcon = $folderOpenedIcon.clone();
//var $upIcon = $('<span>', {"class": "fa fa-arrow-circle-up"});
var $unsortedIcon = $('<span>', {"class": "fa fa-files-o"});
var $templateIcon = $('<span>', {"class": "fa fa-cubes"});
var $recentIcon = $('<span>', {"class": "fa fa-clock-o"});
var $trashIcon = $('<span>', {"class": "fa " + faTrash});
var $trashEmptyIcon = $('<span>', {"class": "fa fa-trash-o"});
//var $collapseIcon = $('<span>', {"class": "fa fa-minus-square-o cp-app-drive-icon-expcol"});
var $expandIcon = $('<span>', {"class": "fa fa-plus-square-o cp-app-drive-icon-expcol"});
var $emptyTrashIcon = $('<button>', {"class": "fa fa-ban"});
var $listIcon = $('<button>', {"class": "fa fa-list"});
var $gridIcon = $('<button>', {"class": "fa fa-th-large"});
var $sortAscIcon = $('<span>', {"class": "fa fa-angle-up sortasc"});
var $sortDescIcon = $('<span>', {"class": "fa fa-angle-down sortdesc"});
var $closeIcon = $('<span>', {"class": "fa fa-window-close"});
//var $backupIcon = $('<span>', {"class": "fa fa-life-ring"});
var $searchIcon = $('<span>', {"class": "fa fa-search cp-app-drive-tree-search-con"});
var $addIcon = $('<span>', {"class": "fa fa-plus"});
var $renamedIcon = $('<span>', {"class": "fa fa-flag"});
var $readonlyIcon = $('<span>', {"class": "fa " + faReadOnly});
var $ownedIcon = $('<span>', {"class": "fa fa-id-card-o"});
var $ownerIcon = $('<span>', {"class": "fa fa-id-card"});
var history = {
isHistoryMode: false,
};
@@ -360,17 +363,24 @@ define([
// Categories dislayed in the menu
// _WORKGROUP_ : do not display unsorted
var displayedCategories = [ROOT, TRASH, SEARCH, RECENT];
// PCS enabled: display owned pads
if (AppConfig.displayCreationScreen) { displayedCategories.push(OWNED); }
// Templates enabled: display template category
if (AppConfig.enableTemplates) { displayedCategories.push(TEMPLATE); }
// Tags used: display Tags category
if (Object.keys(filesOp.getTagsList()).length) { displayedCategories.push(TAGS); }
if (isWorkgroup()) { displayedCategories = [ROOT, TRASH, SEARCH]; }
var virtualCategories = [SEARCH, RECENT, OWNED];
var virtualCategories = [SEARCH, RECENT, OWNED, TAGS];
if (!APP.loggedIn) {
displayedCategories = [FILES_DATA];
currentPath = [FILES_DATA];
$tree.hide();
if (Object.keys(files.root).length && !proxy.anonymousAlert) {
UI.alert(Messages.fm_alert_anonymous, null, true);
var msg = common.fixLinks($('<div>').html(Messages.fm_alert_anonymous));
UI.alert(msg);
proxy.anonymousAlert = true;
}
}
@@ -1295,7 +1305,7 @@ define([
$span.attr('title', name);
var type = Messages.type[hrefData.type] || hrefData.type;
common.displayThumbnail(data.href, $span, function ($thumb) {
common.displayThumbnail(data.href, data.channel, $span, function ($thumb) {
// Called only if the thumbnail exists
// Remove the .hide() added by displayThumnail() because it hides the icon in
// list mode too
@@ -1443,6 +1453,7 @@ define([
case SEARCH: pName = SEARCH_NAME; break;
case RECENT: pName = RECENT_NAME; break;
case OWNED: pName = OWNED_NAME; break;
case TAGS: pName = TAGS_NAME; break;
default: pName = name;
}
return pName;
@@ -1511,18 +1522,14 @@ define([
case OWNED:
msg = Messages.fm_info_owned;
break;
case TAGS:
break;
default:
msg = undefined;
}
if (!APP.loggedIn) {
msg = Messages.fm_info_anonymous;
$box.html(msg);
$box.find('a[target!="_blank"]').click(function (e) {
e.preventDefault();
var href = $(this).attr('href');
common.gotoURL(href);
});
return $box;
return $(common.fixLinks($box.html(msg)));
}
if (!msg || APP.store['hide-info-' + path[0]] === '1') {
$box.hide();
@@ -2057,6 +2064,7 @@ define([
$element.data('context', 'default');
$container.append($element);
});
createGhostIcon($container);
};
var displayTrashRoot = function ($list, $folderHeader, $fileHeader) {
@@ -2100,7 +2108,7 @@ define([
var parsed = Hash.parsePadUrl(href);
var $table = $('<table>');
var $icon = $('<td>', {'rowspan': '3', 'class': 'cp-app-drive-search-icon'})
.append(getFileIcon(href));
.append(getFileIcon(r.id));
var $title = $('<td>', {
'class': 'cp-app-drive-search-col1 cp-app-drive-search-title'
}).text(r.data.title)
@@ -2140,6 +2148,13 @@ define([
}
var $openDir = $('<td>', {'class': 'cp-app-drive-search-opendir'}).append($a);
$('<a>').text(Messages.fc_prop).click(function () {
APP.getProperties(r.id, function (e, $prop) {
if (e) { return void logError(e); }
UI.alert($prop[0], undefined, true);
});
}).appendTo($openDir);
// rows 1-3
$('<tr>').append($icon).append($title).append($typeName).append($type).appendTo($table);
$('<tr>').append($path).append($atimeName).append($atime).appendTo($table);
@@ -2230,6 +2245,35 @@ define([
});
};
// Tags category
var displayTags = function ($container) {
var list = filesOp.getTagsList();
if (Object.keys(list).length === 0) { return; }
var sortedTags = Object.keys(list);
sortedTags.sort(function (a, b) {
return list[b] - list[a];
});
var lines = [
h('tr', [
h('th', Messages.fm_tags_name),
h('th', Messages.fm_tags_used)
])
];
sortedTags.forEach(function (tag) {
var tagLink = h('a', { href: '#' }, '#' + tag);
$(tagLink).click(function () {
if (displayedCategories.indexOf(SEARCH) !== -1) {
APP.Search.$input.val('#' + tag).keyup();
}
});
lines.push(h('tr', [
h('td', tagLink),
h('td.cp-app-drive-tags-used', list[tag])
]));
});
$(h('li.cp-app-drive-tags-list', h('table', lines))).appendTo($container);
};
// Display the selected directory into the content part (rightside)
// NOTE: Elements in the trash are not using the same storage structure as the others
// _WORKGROUP_ : do not change the lastOpenedFolder value in localStorage
@@ -2259,10 +2303,9 @@ define([
var isTrashRoot = filesOp.comparePath(path, [TRASH]);
var isTemplate = filesOp.comparePath(path, [TEMPLATE]);
var isAllFiles = filesOp.comparePath(path, [FILES_DATA]);
var isSearch = path[0] === SEARCH;
var isRecent = path[0] === RECENT;
var isOwned = path[0] === OWNED;
var isVirtual = virtualCategories.indexOf(path[0]) !== -1;
var isSearch = path[0] === SEARCH;
var isTags = path[0] === TAGS;
var root = isVirtual ? undefined : filesOp.find(path);
if (!isVirtual && typeof(root) === "undefined") {
@@ -2296,7 +2339,7 @@ define([
var $dirContent = $('<div>', {id: FOLDER_CONTENT_ID});
$dirContent.data('path', path);
if (!isSearch) {
if (!isSearch && !isTags) {
var mode = getViewMode();
if (mode) {
$dirContent.addClass(getViewModeClass());
@@ -2358,10 +2401,12 @@ define([
displayTrashRoot($list, $folderHeader, $fileHeader);
} else if (isSearch) {
displaySearch($list, path[1]);
} else if (isRecent) {
} else if (path[0] === RECENT) {
displayRecent($list);
} else if (isOwned) {
} else if (path[0] === OWNED) {
displayOwned($list);
} else if (isTags) {
displayTags($list);
} else {
$dirContent.contextmenu(openContextMenu('content'));
if (filesOp.hasSubfolder(root)) { $list.append($folderHeader); }
@@ -2503,25 +2548,6 @@ define([
});
};
var createTemplate = function ($container, path) {
var $icon = $templateIcon.clone();
var isOpened = filesOp.comparePath(path, currentPath);
var $element = createTreeElement(TEMPLATE_NAME, $icon, [TEMPLATE], false, true, false, isOpened);
$element.addClass('cp-app-drive-tree-root');
var $list = $('<ul>', { 'class': 'cp-app-drive-tree-category' }).append($element);
$container.append($list);
};
var createAllFiles = function ($container, path) {
var $icon = $unsortedIcon.clone();
var isOpened = filesOp.comparePath(path, currentPath);
var $allfilesElement = createTreeElement(FILES_DATA_NAME, $icon, [FILES_DATA], false, false, false, isOpened);
$allfilesElement.addClass('root');
var $allfilesList = $('<ul>', { 'class': 'cp-app-drive-tree-category' })
.append($allfilesElement);
$container.append($allfilesList);
};
var createTrash = function ($container, path) {
var $icon = filesOp.isFolderEmpty(files[TRASH]) ? $trashEmptyIcon.clone() : $trashIcon.clone();
var isOpened = filesOp.comparePath(path, currentPath);
@@ -2534,29 +2560,11 @@ define([
$container.append($trashList);
};
var createRecent = function ($container, path) {
var $icon = $recentIcon.clone();
var isOpened = filesOp.comparePath(path, currentPath);
var $element = createTreeElement(RECENT_NAME, $icon, [RECENT], false, false, false, isOpened);
$element.addClass('root');
var $list = $('<ul>', { 'class': 'cp-app-drive-tree-category' }).append($element);
$container.append($list);
};
var createOwned = function ($container, path) {
var $icon = $ownedIcon.clone(); // TODO
var isOpened = filesOp.comparePath(path, currentPath);
var $element = createTreeElement(OWNED_NAME, $icon, [OWNED], false, false, false, isOpened);
$element.addClass('root');
var $list = $('<ul>', { 'class': 'cp-app-drive-tree-category' }).append($element);
$container.append($list);
};
var search = APP.Search = {};
var createSearch = function ($container) {
var isInSearch = currentPath[0] === SEARCH;
var $div = $('<div>', {'id': 'cp-app-drive-tree-search', 'class': 'cp-unselectable'});
var $input = $('<input>', {
var $input = APP.Search.$input = $('<input>', {
id: 'cp-app-drive-tree-search-input',
type: 'text',
draggable: false,
@@ -2606,6 +2614,38 @@ define([
$container.append($div);
};
var categories = {};
categories[FILES_DATA] = {
name: FILES_DATA_NAME,
$icon: $unsortedIcon
};
categories[TEMPLATE] = {
name: TEMPLATE_NAME,
droppable: true,
$icon: $templateIcon
};
categories[RECENT] = {
name: RECENT_NAME,
$icon: $recentIcon
};
categories[OWNED] = {
name: OWNED_NAME,
$icon: $ownedIcon
};
categories[TAGS] = {
name: TAGS_NAME,
$icon: $tagsIcon
};
var createCategory = function ($container, cat) {
var options = categories[cat];
var $icon = options.$icon.clone();
var isOpened = filesOp.comparePath([cat], currentPath);
var $element = createTreeElement(options.name, $icon, [cat], options.draggable, options.droppable, false, isOpened);
$element.addClass('cp-app-drive-tree-root');
var $list = $('<ul>', { 'class': 'cp-app-drive-tree-category' }).append($element);
$container.append($list);
};
APP.resetTree = function () {
var $categories = $tree.find('.cp-app-drive-tree-categories-container');
var s = $categories.scrollTop() || 0;
@@ -2614,11 +2654,12 @@ define([
if (displayedCategories.indexOf(SEARCH) !== -1) { createSearch($tree); }
var $div = $('<div>', {'class': 'cp-app-drive-tree-categories-container'})
.appendTo($tree);
if (displayedCategories.indexOf(RECENT) !== -1) { createRecent($div, [RECENT]); }
if (displayedCategories.indexOf(OWNED) !== -1) { createOwned($div, [OWNED]); }
if (displayedCategories.indexOf(TAGS) !== -1) { createCategory($div, TAGS); }
if (displayedCategories.indexOf(RECENT) !== -1) { createCategory($div, RECENT); }
if (displayedCategories.indexOf(OWNED) !== -1) { createCategory($div, OWNED); }
if (displayedCategories.indexOf(ROOT) !== -1) { createTree($div, [ROOT]); }
if (displayedCategories.indexOf(TEMPLATE) !== -1) { createTemplate($div, [TEMPLATE]); }
if (displayedCategories.indexOf(FILES_DATA) !== -1) { createAllFiles($div, [FILES_DATA]); }
if (displayedCategories.indexOf(TEMPLATE) !== -1) { createCategory($div, TEMPLATE); }
if (displayedCategories.indexOf(FILES_DATA) !== -1) { createCategory($div, FILES_DATA); }
if (displayedCategories.indexOf(TRASH) !== -1) { createTrash($div, [TRASH]); }
$tree.append(APP.$limit);
@@ -2657,9 +2698,9 @@ define([
if (parsed.hashData.type !== "pad") { return; }
var i = data.href.indexOf('#') + 1;
var base = data.href.slice(0, i);
var hrefsecret = Hash.getSecrets(parsed.type, parsed.hash);
var hrefsecret = Hash.getSecrets(parsed.type, parsed.hash, data.password);
if (!hrefsecret.keys) { return; }
var viewHash = Hash.getViewHashFromKeys(hrefsecret.channel, hrefsecret.keys);
var viewHash = Hash.getViewHashFromKeys(hrefsecret);
return base + viewHash;
};
@@ -2672,7 +2713,7 @@ define([
}
});
var getProperties = function (el, cb) {
var getProperties = APP.getProperties = function (el, cb) {
if (!filesOp.isFile(el)) {
return void cb('NOT_FILE');
}
@@ -2724,24 +2765,6 @@ define([
$(window).focus();
if (!res) { return; }
filesOp.delete(pathsList, refresh);
/*
// Try to delete each selected pad from server, and delete from drive if no error
var n = nThen(function () {});
pathsList.forEach(function (p) {
var el = filesOp.find(p);
var data = filesOp.getFileData(el);
var parsed = Hash.parsePadUrl(data.href);
var channel = Util.base64ToHex(parsed.hashData.channel);
n = n.nThen(function (waitFor) {
sframeChan.query('Q_REMOVE_OWNED_CHANNEL', channel,
waitFor(function (e) {
if (e) { return void console.error(e); }
filesOp.delete([p], function () {}, false, true);
}));
});
});
n.nThen(function () { refresh(); });
*/
});
};
$contextMenu.on("click", "a", function(e) {

View File

@@ -39,6 +39,7 @@ define([
var getSecrets = function (Cryptpad, Utils, cb) {
var hash = window.location.hash.slice(1) || Utils.LocalStore.getUserHash() ||
Utils.LocalStore.getFSHash();
// No password for drive
cb(null, Utils.Hash.getSecrets('drive', hash));
};
var addRpc = function (sframeChan, Cryptpad, Utils) {

View File

@@ -1,26 +1,17 @@
@import (once) "../../customize/src/less2/include/browser.less";
@import (once) "../../customize/src/less2/include/toolbar.less";
@import (once) "../../customize/src/less2/include/markdown.less";
@import (once) '../../customize/src/less2/include/fileupload.less';
@import (once) '../../customize/src/less2/include/alertify.less';
@import (once) '../../customize/src/less2/include/tokenfield.less';
@import (once) '../../customize/src/less2/include/framework.less';
.toolbar_main(
.framework_min_main(
@bg-color: @colortheme_file-bg,
@warn-color: @colortheme_file-warn,
@color: @colortheme_file-color
);
.fileupload_main();
.alertify_main();
.tokenfield_main();
@button-border: 2px;
/*html, body {
margin: 0px;
height: 100%;
}*/
// body
display: flex;
flex-flow: column;

View File

@@ -2,7 +2,7 @@ define([
'/bower_components/tweetnacl/nacl-fast.min.js',
], function () {
var Nacl = window.nacl;
var PARANOIA = true;
//var PARANOIA = true;
var plainChunkLength = 128 * 1024;
var cypherChunkLength = 131088;
@@ -36,24 +36,11 @@ define([
var increment = function (N) {
var l = N.length;
while (l-- > 1) {
if (PARANOIA) {
if (typeof(N[l]) !== 'number') {
throw new Error('E_UNSAFE_TYPE');
}
if (N[l] > 255) {
throw new Error('E_OUT_OF_BOUNDS');
}
}
/* jshint probably suspects this is unsafe because we lack types
but as long as this is only used on nonces, it should be safe */
if (N[l] !== 255) { return void N[l]++; } // jshint ignore:line
if (l === 0) { throw new Error('E_NONCE_TOO_LARGE'); }
N[l] = 0;
// you don't need to worry about this running out.
// you'd need a REAAAALLY big file
if (l === 0) {
throw new Error('E_NONCE_TOO_LARGE');
}
}
};
@@ -160,19 +147,21 @@ define([
}
var takeChunk = function (cb) {
var start = i * cypherChunkLength + 2 + metadataLength;
var end = start + cypherChunkLength;
i++;
var box = new Uint8Array(u8.subarray(start, end));
setTimeout(function () {
var start = i * cypherChunkLength + 2 + metadataLength;
var end = start + cypherChunkLength;
i++;
var box = new Uint8Array(u8.subarray(start, end));
// decrypt the chunk
var plaintext = Nacl.secretbox.open(box, nonce, key);
increment(nonce);
// decrypt the chunk
var plaintext = Nacl.secretbox.open(box, nonce, key);
increment(nonce);
if (!plaintext) { return cb('DECRYPTION_ERROR'); }
if (!plaintext) { return cb('DECRYPTION_ERROR'); }
_progress(end);
cb(void 0, plaintext);
_progress(end);
cb(void 0, plaintext);
});
};
var chunks = [];
@@ -219,7 +208,7 @@ define([
var state = 0;
var next = function (cb) {
if (state === 2) { return void cb(); }
if (state === 2) { return void setTimeout(cb); }
var start;
var end;
@@ -238,7 +227,9 @@ define([
.concat(slice(box)));
state++;
return void cb(void 0, prefixed);
return void setTimeout(function () {
cb(void 0, prefixed);
});
}
// encrypt the rest of the file...
@@ -253,7 +244,9 @@ define([
// regular data is done
if (i * plainChunkLength >= u8.length) { state = 2; }
return void cb(void 0, box);
setTimeout(function () {
cb(void 0, box);
});
};
return next;

View File

@@ -16,7 +16,7 @@ define([
'/bower_components/file-saver/FileSaver.min.js',
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
'less!/bower_components/components-font-awesome/css/font-awesome.min.css',
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
'less!/customize/src/less2/main.less',
], function (
@@ -61,6 +61,7 @@ define([
if (!priv.filehash) {
uploadMode = true;
} else {
// PASSWORD_FILES
secret = Hash.getSecrets('file', priv.filehash);
if (!secret.keys) { throw new Error("You need a hash"); }
hexFileName = Util.base64ToHex(secret.channel);
@@ -124,6 +125,12 @@ define([
var rightsideDisplayed = false;
$(window.document).on('decryption', function (e) {
/* FIXME
we're listening for decryption events and assuming that only
the main media-tag exists. In practice there is also your avatar
and there could be other things in the future, so we should
figure out a generic way target media-tag decryption events.
*/
var decrypted = e.originalEvent;
if (decrypted.callback) {
decrypted.callback();
@@ -133,9 +140,6 @@ define([
$dlform.hide();
var $dlButton = $dlview.find('media-tag button');
if (ev) { $dlButton.click(); }
if (!$dlButton.length) {
$appContainer.css('background', 'white');
}
$dlButton.addClass('btn btn-success');
var text = Messages.download_mt_button + '<br>';
text += '<b>' + Util.fixHTML(title) + '</b><br>';
@@ -230,8 +234,7 @@ define([
if (typeof(sizeMb) === 'number' && sizeMb < 5) { return void onClick(); }
$dlform.find('#cp-app-file-dlfile, #cp-app-file-dlprogress').click(onClick);
};
var href = priv.origin + priv.pathname + priv.filehash;
common.getFileSize(href, function (e, data) {
common.getFileSize(hexFileName, function (e, data) {
if (e) {
return void UI.errorLoadingScreen(e);
}

View File

@@ -3,10 +3,12 @@
@import (once) '../../customize/src/less2/include/icon-colors.less';
@import (once) '../../customize/src/less2/include/fileupload.less';
@import (once) '../../customize/src/less2/include/alertify.less';
@import (once) '../../customize/src/less2/include/tippy.less';
.iconColors_main();
.fileupload_main();
.alertify_main();
.tippy_main();
#cp-filepicker-dialog {
display: none;
@@ -36,7 +38,8 @@
line-height: 1em;
cursor: pointer;
background-color: #111;
background-color: @colortheme_modal-bg;
box-shadow: 2px 2px 5px #000;
color: @darker;
transition: all 0.1s;

View File

@@ -11,7 +11,7 @@ define([
'/customize/messages.js',
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
'less!/bower_components/components-font-awesome/css/font-awesome.min.css',
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
'less!/customize/src/less2/main.less',
], function (
$,
@@ -40,6 +40,7 @@ define([
var parsed = Hash.parsePadUrl(data.url);
hideFileDialog();
if (parsed.type === 'file') {
// PASSWORD_FILES
var hexFileName = Util.base64ToHex(parsed.hashData.channel);
var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName;
sframeChan.event("EV_FILE_PICKED", {
@@ -138,7 +139,7 @@ define([
});
// Add thumbnail if it exists
common.displayThumbnail(data.href, $span);
common.displayThumbnail(data.href, data.channel, $span);
});
$input.focus();
};

View File

@@ -8,7 +8,7 @@ define([
'/common/outer/local-store.js',
'/common/test.js',
'less!/bower_components/components-font-awesome/css/font-awesome.min.css',
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
], function ($, Cryptpad, Login, UI, Realtime, Feedback, LocalStore, Test) {
$(function () {
var $main = $('#mainBlock');

View File

@@ -3,6 +3,43 @@
<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>
<style>
html, body {
margin: 0px;
}
#cke_1_top {
overflow: visible;
padding: 0px;
display: flex;
}
#cke_1_toolbox {
display: inline-block;
width: 100%;
background-color: #c1e7ff;
}
#cke_1_toolbox .cke_toolbar {
height: 28px;
padding: 2px 0;
}
#cke_1_top .cryptpad-toolbar {
padding: 0;
display: block;
}
.cke_wysiwyg_frame {
min-width: 60%;
}
@media print {
#cke_1_top {
display:none;
}
body.app-pad .userlist-drawer {
display:none;
}
}
</style>
</head>
<body class="cp-app-pad">
<textarea style="display:none" id="editor1" name="editor1"></textarea>

View File

@@ -36,7 +36,7 @@ define([
'/bower_components/diff-dom/diffDOM.js',
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
'less!/bower_components/components-font-awesome/css/font-awesome.min.css',
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
'less!/customize/src/less2/main.less',
], function (
$,
@@ -82,12 +82,54 @@ define([
Cursor: Cursor,
};
// MEDIATAG: Filter elements to serialize
// * Remove the drag&drop and resizers from the hyperjson
var isWidget = function (el) {
return typeof (el.getAttribute) === "function" &&
(el.getAttribute('data-cke-hidden-sel') ||
(el.getAttribute('class') &&
(/cke_widget_drag/.test(el.getAttribute('class')) ||
/cke_image_resizer/.test(el.getAttribute('class')))
)
);
};
var isNotMagicLine = function (el) {
return !(el && typeof(el.getAttribute) === 'function' &&
el.getAttribute('class') &&
el.getAttribute('class').split(' ').indexOf('non-realtime') !== -1);
};
var shouldSerialize = function (el) {
return isNotMagicLine(el) && !isWidget(el);
};
// MEDIATAG: Filter attributes in the serialized elements
var widgetFilter = function (hj) {
// Send a widget ID == 0 to avoid a fight between browsers and
// prevent the container from having the "selected" class (blue border)
if (hj[1].class) {
var split = hj[1].class.split(' ');
if (split.indexOf('cke_widget_wrapper') !== -1 &&
split.indexOf('cke_widget_block') !== -1) {
hj[1].class = "cke_widget_wrapper cke_widget_block";
hj[1]['data-cke-widget-id'] = "0";
}
if (split.indexOf('cke_widget_wrapper') !== -1 &&
split.indexOf('cke_widget_inline') !== -1) {
hj[1].class = "cke_widget_wrapper cke_widget_inline";
delete hj[1]['data-cke-widget-id'];
//hj[1]['data-cke-widget-id'] = "0";
}
// Remove the title attribute of the drag&drop icons (translation conflicts)
if (split.indexOf('cke_widget_drag_handler') !== -1 ||
split.indexOf('cke_image_resizer') !== -1) {
hj[1].title = undefined;
}
}
return hj;
};
var hjsonFilters = function (hj) {
/* catch `type="_moz"` before it goes over the wire */
var brFilter = function (hj) {
@@ -100,6 +142,7 @@ define([
};
brFilter(hj);
mediatagContentFilter(hj);
widgetFilter(hj);
return hj;
};
@@ -168,6 +211,36 @@ define([
return true;
}
}
// MEDIATAG
// Never modify widget ids
if (info.node && info.node.tagName === 'SPAN' && info.diff.name === 'data-cke-widget-id') {
return true;
}
if (info.node && info.node.tagName === 'SPAN' &&
info.node.getAttribute('class') &&
/cke_widget_wrapper/.test(info.node.getAttribute('class'))) {
if (info.diff.action === 'modifyAttribute' && info.diff.name === 'class') {
return true;
}
//console.log(info);
}
// CkEditor drag&drop icon container
if (info.node && info.node.tagName === 'SPAN' &&
info.node.getAttribute('class') &&
info.node.getAttribute('class').split(' ').indexOf('cke_widget_drag_handler_container') !== -1) {
return true;
}
// CkEditor drag&drop title (language fight)
if (info.node && info.node.getAttribute &&
info.node.getAttribute('class') &&
(info.node.getAttribute('class').split(' ').indexOf('cke_widget_drag_handler') !== -1 ||
info.node.getAttribute('class').split(' ').indexOf('cke_image_resizer') !== -1 ) ) {
return true;
}
/*
Also reject any elements which would insert any one of
our forbidden tag types: script, iframe, object,
@@ -199,20 +272,26 @@ define([
if (info.node && info.node.tagName === 'SPAN' &&
info.node.getAttribute('contentEditable') === "false") {
// it seems to be a magicline plugin element...
// but it can also be a widget (MEDIATAG), in which case the removal was
// probably intentional
if (info.diff.action === 'removeElement') {
// and you're about to remove it...
// this probably isn't what you want
if (!info.node.getAttribute('class') ||
!/cke_widget_wrapper/.test(info.node.getAttribute('class'))) {
// This element is not a widget!
// this probably isn't what you want
/*
I have never seen this in the console, but the
magic line is still getting removed on remote
edits. This suggests that it's getting removed
by something other than diffDom.
*/
console.log("preventing removal of the magic line!");
/*
I have never seen this in the console, but the
magic line is still getting removed on remote
edits. This suggests that it's getting removed
by something other than diffDom.
*/
console.log("preventing removal of the magic line!");
// return true to prevent diff application
return true;
// return true to prevent diff application
return true;
}
}
}
@@ -322,7 +401,7 @@ define([
var src = tag.getAttribute('src');
if (mediaTagMap[src]) {
mediaTagMap[src].forEach(function (n) {
tag.appendChild(n);
tag.appendChild(n.cloneNode());
});
}
});
@@ -370,8 +449,11 @@ define([
framework.setMediaTagEmbedder(function ($mt) {
$mt.attr('contenteditable', 'false');
$mt.attr('tabindex', '1');
editor.insertElement(new window.CKEDITOR.dom.element($mt[0]));
//$mt.attr('tabindex', '1');
//MEDIATAG
var element = new window.CKEDITOR.dom.element($mt[0]);
editor.insertElement(element);
editor.widgets.initOn( element, 'mediatag' );
});
framework.setTitleRecommender(function () {
@@ -403,7 +485,18 @@ define([
var patch = (DD).diff(inner, userDocStateDom);
(DD).apply(inner, patch);
// MEDIATAG: Migrate old mediatags to the widget system
$(inner).find('media-tag:not(.cke_widget_element)').each(function (i, el) {
var element = new window.CKEDITOR.dom.element(el);
editor.widgets.initOn( element, 'mediatag' );
});
displayMediaTags(framework, inner, mediaTagMap);
// MEDIATAG: Initialize mediatag widgets inserted in the document by other users
editor.widgets.checkWidgets();
if (framework.isReadOnly()) {
var $links = $(inner).find('a');
// off so that we don't end up with multiple identical handlers
@@ -425,7 +518,7 @@ define([
framework.setContentGetter(function () {
displayMediaTags(framework, inner, mediaTagMap);
inner.normalize();
return Hyperjson.fromDOM(inner, isNotMagicLine, hjsonFilters);
return Hyperjson.fromDOM(inner, shouldSerialize, hjsonFilters);
});
$bar.find('#cke_1_toolbar_collapser').hide();
@@ -459,11 +552,15 @@ define([
ckeditor: editor,
body: $('body'),
onUploaded: function (ev, data) {
// PASSWORD_FILES
var parsed = Hash.parsePadUrl(data.url);
var hexFileName = Util.base64ToHex(parsed.hashData.channel);
var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName;
var mt = '<media-tag contenteditable="false" src="' + src + '" data-crypto-key="cryptpad:' + parsed.hashData.key + '" tabindex="1"></media-tag>';
editor.insertElement(window.CKEDITOR.dom.element.createFromHtml(mt));
// MEDIATAG
var element = window.CKEDITOR.dom.element.createFromHtml(mt);
editor.insertElement(element);
editor.widgets.initOn( element, 'mediatag' );
}
};
window.APP.FM = framework._.sfCommon.createFileManager(fmConfig);
@@ -508,6 +605,8 @@ define([
Util.blobToImage($(el).data('blob'), waitFor(function (imgSrc) {
$clone.find('media-tag[src="' + $(el).attr('src') + '"] img')
.attr('src', imgSrc);
$clone.find('media-tag').parent()
.find('.cke_widget_drag_handler_container').remove();
}));
});
}).nThen(function () {

View File

@@ -23,7 +23,13 @@ CKEDITOR.dialog.add('mediatag', function (editor) {
},
],
onShow: function () {
var el = editor.plugins.mediatag.clicked;
var sel = editor.getSelection();
element = sel.getSelectedElement();
if (!element) { return; }
var el = element.findOne('media-tag');
if (!el) { return; }
var rect = el.getClientRect();
var dialog = this.parts.contents.$;
var inputs = dialog.querySelectorAll('input');
@@ -34,7 +40,14 @@ CKEDITOR.dialog.add('mediatag', function (editor) {
},
onOk: function() {
var dialog = this;
var el = editor.plugins.mediatag.clicked;
var sel = editor.getSelection();
element = sel.getSelectedElement();
if (!element) { return; }
var el = element.findOne('media-tag');
if (!el) { return; }
var dialog = this.parts.contents.$;
var inputs = dialog.querySelectorAll('input');
var wInput = inputs[0];

View File

@@ -10,7 +10,7 @@
( function() {
CKEDITOR.plugins.add( 'mediatag', {
requires: 'dialog',
requires: 'dialog,widget',
//icons: 'image',
//hidpi: true,
onLoad: function () {
@@ -38,144 +38,17 @@
// Register the dialog.
CKEDITOR.dialog.add( pluginName, this.path + 'mediatag-plugin-dialog.js' );
var allowed = 'media-tag[!data-crypto-key,!src,contenteditable,width,height]{border-style,border-width,float,height,margin,margin-bottom,margin-left,margin-right,margin-top,width}',
required = 'media-tag[data-crypto-key,src]';
editor.widgets.add( 'mediatag', {
// Register the command.
editor.addCommand( pluginName, new CKEDITOR.dialogCommand( pluginName, {
allowedContent: allowed,
requiredContent: required,
contentTransformations: [
[ 'media-tag{width}: sizeToStyle', 'media-tag[width]: sizeToAttribute' ],
[ 'media-tag{float}: alignmentToStyle', 'media-tag[align]: alignmentToAttribute' ]
]
} ) );
var isMediaTag = function (el) {
if (el.is('media-tag')) { return el; }
var mt = el.getParents().slice().filter(function (p) {
return p.is('media-tag');
});
if (mt.length !== 1) { return; }
return mt[0];
};
editor.on('doubleclick', function (evt) {
var element = evt.data.element;
var mt = isMediaTag(element);
if (mt && !element.data('cke-realelement')) {
editor.plugins.mediatag.clicked = mt;
evt.data.dialog = 'mediatag';
getLabel: function () { return " "; },
dialog: pluginName,
inline: true,
upcast: function( element ) {
return element.name === 'media-tag';
}
});
// If the "contextmenu" plugin is loaded, register the listeners.
if (editor.contextMenu) {
editor.contextMenu.addListener(function (element) {
if (getSelectedMediatag(editor, element)) {
return { mediatag: CKEDITOR.TRISTATE_OFF };
}
});
}
},
afterInit: function( editor ) {
// Customize the behavior of the alignment commands. (http://dev.ckeditor.com/ticket/7430)
setupAlignCommand('left');
setupAlignCommand('right');
setupAlignCommand('center');
setupAlignCommand('block');
function setupAlignCommand (value) {
var command = editor.getCommand('justify' + value);
if (command) {
if (value === 'left' || value === 'right') {
command.on('exec', function (evt) {
var img = getSelectedMediatag(editor), align;
if (img) {
align = getMediatagAlignment(img);
if (align === value) {
img.removeStyle('float');
// Remove "align" attribute when necessary.
if (value === getMediatagAlignment(img))
img.removeAttribute( 'align' );
} else {
img.setStyle( 'float', value );
}
evt.cancel();
}
} );
}
command.on('refresh', function (evt) {
var img = getSelectedMediatag(editor), align;
if (img) {
align = getMediatagAlignment(img);
this.setState(
(align === value) ? CKEDITOR.TRISTATE_ON : ( value === 'right' || value === 'left' ) ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED );
evt.cancel();
}
} );
}
}
}
} );
function getSelectedMediatag (editor, element) {
if (!element) {
var sel = editor.getSelection();
element = sel.getSelectedElement();
}
if (element && element.is('media-tag') && !element.data('cke-realelement')
&& !element.isReadOnly()) {
return element;
}
}
function getMediatagAlignment (element) {
var align = element.getStyle('float');
if (align === 'inherit' || align === 'none') {
align = 0;
}
if (!align) {
align = element.getAttribute('align');
}
return align;
}
} )();
/**
* Determines whether dimension inputs should be automatically filled when the image URL changes in the Image plugin dialog window.
*
* config.image_prefillDimensions = false;
*
* @since 4.5
* @cfg {Boolean} [image_prefillDimensions=true]
* @member CKEDITOR.config
*/
/**
* Whether to remove links when emptying the link URL field in the Image dialog window.
*
* config.image_removeLinkByEmptyURL = false;
*
* @cfg {Boolean} [image_removeLinkByEmptyURL=true]
* @member CKEDITOR.config
*/
CKEDITOR.config.mediatag_removeLinkByEmptyURL = true;
/**
* Padding text to set off the image in the preview area.
*
* config.image_previewText = CKEDITOR.tools.repeat( '___ ', 100 );
*
* @cfg {String} [image_previewText='Lorem ipsum dolor...' (placeholder text)]
* @member CKEDITOR.config
*/

View File

@@ -1,22 +1,15 @@
@import (once) "../../customize/src/less2/include/browser.less";
@import (once) "../../customize/src/less2/include/toolbar.less";
@import (once) "../../customize/src/less2/include/markdown.less";
@import (once) '../../customize/src/less2/include/fileupload.less';
@import (once) '../../customize/src/less2/include/alertify.less';
@import (once) '../../customize/src/less2/include/tokenfield.less';
@import (once) '../../customize/src/less2/include/tools.less';
@import (once) '../../customize/src/less2/include/avatar.less';
@import (once) '../../customize/src/less2/include/creation.less';
@import (once) "../../customize/src/less2/include/framework.less";
.toolbar_main(
.framework_main(
@bg-color: @colortheme_poll-bg,
@warn-color: @colortheme_poll-warn,
@color: @colortheme_poll-color
);
.fileupload_main();
.alertify_main();
.tokenfield_main();
.creation_main();
@poll-fore: #555;

View File

@@ -2,7 +2,6 @@ define([
'jquery',
'/common/toolbar3.js',
'/common/common-util.js',
'/common/cryptget.js',
'/bower_components/nthen/index.js',
'/common/sframe-common.js',
'/common/common-realtime.js',
@@ -26,13 +25,12 @@ define([
'/bower_components/file-saver/FileSaver.min.js',
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
'less!/bower_components/components-font-awesome/css/font-awesome.min.css',
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
'less!/customize/src/less2/main.less',
], function (
$,
Toolbar,
Util,
Cryptget,
nThen,
SFCommon,
CommonRealtime,

View File

@@ -1,19 +1,15 @@
@import (once) "../../customize/src/less2/include/browser.less";
@import (once) "../../customize/src/less2/include/toolbar.less";
@import (once) "../../customize/src/less2/include/markdown.less";
@import (once) '../../customize/src/less2/include/fileupload.less';
@import (once) '../../customize/src/less2/include/alertify.less';
@import (once) '../../customize/src/less2/include/framework.less';
@import (once) '../../customize/src/less2/include/avatar.less';
@import (once) '../../customize/src/less2/include/sidebar-layout.less';
.toolbar_main(
.framework_min_main(
@bg-color: @colortheme_profile-bg,
@warn-color: @colortheme_profile-warn,
@color: @colortheme_profile-color
);
.fileupload_main();
.alertify_main();
.sidebar-layout_main();
// body

View File

@@ -19,7 +19,7 @@ define([
'css!/bower_components/codemirror/addon/dialog/dialog.css',
'css!/bower_components/codemirror/addon/fold/foldgutter.css',
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
'less!/bower_components/components-font-awesome/css/font-awesome.min.css',
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
'less!/customize/src/less2/main.less',
'/bower_components/croppie/croppie.min.js',
'css!/bower_components/croppie/croppie.css',

View File

@@ -40,6 +40,7 @@ define([
var Hash = Utils.Hash;
// 1st case: visiting someone else's profile with hash in the URL
if (window.location.hash) {
// No password for profiles
return void cb(null, Hash.getSecrets('profile', window.location.hash.slice(1)));
}
var editHash;
@@ -50,11 +51,17 @@ define([
}));
}).nThen(function () {
if (editHash) {
// No password for profile
return void cb(null, Hash.getSecrets('profile', editHash));
}
// 3rd case: profile creation (create a new random hash, store it later if needed)
if (!Utils.LocalStore.isLoggedIn()) { return void cb(); }
var hash = Hash.createRandomHash();
if (!Utils.LocalStore.isLoggedIn()) {
// Unregistered users can't create a profile
window.location.href = '/drive/';
return void cb();
}
// No password for profile
var hash = Hash.createRandomHash('profile');
var secret = Hash.getSecrets('profile', hash);
Cryptpad.pinPads([secret.channel], function (e) {
if (e) {
@@ -65,8 +72,8 @@ define([
//return void UI.log(Messages._getKey('profile_error', [e])) // TODO
}
var profile = {};
profile.edit = Utils.Hash.getEditHashFromKeys(secret.channel, secret.keys);
profile.view = Utils.Hash.getViewHashFromKeys(secret.channel, secret.keys);
profile.edit = Utils.Hash.getEditHashFromKeys(secret);
profile.view = Utils.Hash.getViewHashFromKeys(secret);
Cryptpad.setNewProfile(profile);
});
cb(null, secret);
@@ -75,7 +82,7 @@ define([
var addRpc = function (sframeChan, Cryptpad, Utils) {
// Adding a new avatar from the profile: pin it and store it in the object
sframeChan.on('Q_PROFILE_AVATAR_ADD', function (data, cb) {
var chanId = Utils.Hash.hrefToHexChannelId(data);
var chanId = Utils.Hash.hrefToHexChannelId(data, null);
Cryptpad.pinPads([chanId], function (e) {
if (e) { return void cb(e); }
Cryptpad.setAvatar(data, cb);
@@ -83,7 +90,7 @@ define([
});
// Removing the avatar from the profile: unpin it
sframeChan.on('Q_PROFILE_AVATAR_REMOVE', function (data, cb) {
var chanId = Utils.Hash.hrefToHexChannelId(data);
var chanId = Utils.Hash.hrefToHexChannelId(data, null);
Cryptpad.unpinPads([chanId], function () {
Cryptpad.setAvatar(undefined, cb);
});
@@ -93,6 +100,7 @@ define([
getSecrets: getSecrets,
noHash: true, // Don't add the hash in the URL if it doesn't already exist
addRpc: addRpc,
owned: true
});
});
});

View File

@@ -11,7 +11,7 @@ define([
'/common/common-feedback.js',
'/common/outer/local-store.js',
'less!/bower_components/components-font-awesome/css/font-awesome.min.css',
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
], function ($, Login, Cryptpad, Test, Cred, UI, Util, Realtime, Constants, Feedback, LocalStore) {
var Messages = Cryptpad.Messages;
@@ -118,15 +118,10 @@ define([
}, 500);
$register.on('keypress', function (e) {
e.preventDefault();
e.stopPropagation();
console.error(e.which);
switch (e.which) {
case 13: return clickRegister();
case 13: return clickRegister();
default:
//console.log(e.which);
if (e.which === 13) {
e.preventDefault();
e.stopPropagation();
return clickRegister();
}
});

View File

@@ -1,18 +1,15 @@
@import (once) "../../customize/src/less2/include/colortheme-all.less";
@import (once) "../../customize/src/less2/include/browser.less";
@import (once) "../../customize/src/less2/include/toolbar.less";
@import (once) "../../customize/src/less2/include/markdown.less";
@import (once) '../../customize/src/less2/include/alertify.less';
@import (once) '../../customize/src/less2/include/sidebar-layout.less';
@import (once) "../../customize/src/less2/include/limit-bar.less";
@import (once) "../../customize/src/less2/include/creation.less";
@import (once) '../../customize/src/less2/include/framework.less';
.toolbar_main(
.framework_min_main(
@bg-color: @colortheme_settings-bg,
@warn-color: @colortheme_settings-warn,
@color: @colortheme_settings-color
);
.alertify_main();
.sidebar-layout_main();
.limit-bar_main();
.creation_main();
@@ -21,6 +18,7 @@
&.cp-app-settings {
display: flex;
flex-flow: column;
font: @colortheme_app-font;
#cp-sidebarlayout-container {
#cp-sidebarlayout-rightside {
input[type="checkbox"] {

View File

@@ -14,7 +14,7 @@ define([
'/bower_components/file-saver/FileSaver.min.js',
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
'less!/bower_components/components-font-awesome/css/font-awesome.min.css',
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
'less!/customize/src/less2/main.less',
], function (
$,
@@ -233,16 +233,14 @@ define([
// Disable
$('<span>', {'class': 'cp-sidebarlayout-description'})
.text(Messages.settings_disableThumbnailsDescription).appendTo($div);
var $label = $('<label>', { 'for': 'disableThumbnails', 'class': 'noTitle' })
.text(Messages.settings_disableThumbnailsAction);
var $ok = $('<span>', {'class': 'fa fa-check', title: Messages.saved});
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'});
var $checkbox = $('<input>', {
'type': 'checkbox',
id: 'disableThumbnails'
}).on('change', function () {
var $cbox = $(UI.createCheckbox('disableThumbnails',
Messages.settings_disableThumbnailsAction,
false, { label: {class: 'noTitle'} }));
var $checkbox = $cbox.find('input').on('change', function () {
$spinner.show();
$ok.hide();
var val = $checkbox.is(':checked') || false;
@@ -252,14 +250,13 @@ define([
});
});
$checkbox.appendTo($div);
$label.appendTo($div);
$cbox.appendTo($div);
$ok.hide().appendTo($div);
$spinner.hide().appendTo($div);
$ok.hide().appendTo($cbox);
$spinner.hide().appendTo($cbox);
common.getAttribute(['general', 'disableThumbnails'], function (e, val) {
$checkbox[0].checked = val;
$checkbox[0].checked = typeof(val) === "undefined" || val;
});
// Reset
@@ -283,9 +280,6 @@ define([
$('<span>', {'class': 'label'}).text(Messages.settings_userFeedbackTitle).appendTo($div);
var $label = $('<label>', { 'for': 'cp-settings-userfeedback', 'class': 'noTitle' })
.text(Messages.settings_userFeedback);
$('<span>', {'class': 'cp-sidebarlayout-description'})
.append(Messages.settings_userFeedbackHint1)
.append(Messages.settings_userFeedbackHint2).appendTo($div);
@@ -293,10 +287,10 @@ define([
var $ok = $('<span>', {'class': 'fa fa-check', title: Messages.saved});
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'});
var $checkbox = $('<input>', {
'type': 'checkbox',
id: 'cp-settings-userfeedback'
}).on('change', function () {
var $cbox = $(UI.createCheckbox('cp-settings-userfeedback',
Messages.settings_userFeedback,
false, { label: {class: 'noTitle'} }));
var $checkbox = $cbox.find('input').on('change', function () {
$spinner.show();
$ok.hide();
var val = $checkbox.is(':checked') || false;
@@ -306,11 +300,10 @@ define([
});
});
$checkbox.appendTo($div);
$label.appendTo($div);
$cbox.appendTo($div);
$ok.hide().appendTo($div);
$spinner.hide().appendTo($div);
$ok.hide().appendTo($cbox);
$spinner.hide().appendTo($cbox);
if (privateData.feedbackAllowed) {
$checkbox[0].checked = true;
@@ -326,7 +319,7 @@ define([
$('<span>', {'class': 'cp-sidebarlayout-description'})
.append(Messages.settings_deleteHint).appendTo($div);
//var $ok = $('<span>', {'class': 'fa fa-check', title: Messages.saved});
var $ok = $('<span>', {'class': 'fa fa-check', title: Messages.saved});
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'});
var $button = $('<button>', {'id': 'cp-settings-delete', 'class': 'btn btn-danger'})
@@ -334,13 +327,27 @@ define([
$button.click(function () {
$spinner.show();
sframeChan.query("Q_SETTINGS_DELETE_ACCOUNT", null, function (err, data) {
var msg = h('div.cp-app-settings-delete-alert', [
h('p', Messages.settings_deleteModal),
h('pre', JSON.stringify(data, 0, 2))
]);
UI.alert(msg);
$spinner.hide();
UI.confirm(Messages.settings_deleteConfirm, function (yes) {
if (!yes) { return; }
sframeChan.query("Q_SETTINGS_DELETE_ACCOUNT", null, function (err, data) {
// Owned drive
if (data.state === true) {
sframeChan.query('Q_SETTINGS_LOGOUT', null, function () {});
UI.alert(Messages.settings_deleted, function () {
common.gotoURL('/');
});
$ok.show();
$spinner.hide();
return;
}
// Not owned drive
var msg = h('div.cp-app-settings-delete-alert', [
h('p', Messages.settings_deleteModal),
h('pre', JSON.stringify(data, 0, 2))
]);
UI.alert(msg);
$spinner.hide();
});
});
// TODO
/*
@@ -356,6 +363,7 @@ define([
});
$spinner.hide().appendTo($div);
$ok.hide().appendTo($div);
return $div;
};
@@ -715,30 +723,28 @@ define([
var $ok = $('<span>', {'class': 'fa fa-check', title: Messages.saved});
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'});
var $label = $('<label>', { 'for': 'cp-settings-padwidth', 'class': 'noTitle' })
.text(Messages.settings_padWidthLabel);
var $input = $('<input>', {
type: 'checkbox',
id: 'cp-settings-padwidth'
}).on('change', function () {
var $cbox = $(UI.createCheckbox('cp-settings-padwidth',
Messages.settings_padWidthLabel,
false, { label: {class: 'noTitle'} }));
var $checkbox = $cbox.find('input').on('change', function () {
$spinner.show();
$ok.hide();
var val = $input.is(':checked');
var val = $checkbox.is(':checked');
common.setAttribute(['pad', 'width'], val, function () {
$spinner.hide();
$ok.show();
});
}).appendTo($div);
$label.appendTo($div);
});
$cbox.appendTo($div);
$ok.hide().appendTo($div);
$spinner.hide().appendTo($div);
$ok.hide().appendTo($cbox);
$spinner.hide().appendTo($cbox);
common.getAttribute(['pad', 'width'], function (e, val) {
if (e) { return void console.error(e); }
if (val) {
$input.attr('checked', 'checked');
$checkbox.attr('checked', 'checked');
}
});
return $div;
@@ -790,13 +796,14 @@ define([
}).css('flex-flow', 'column')
.appendTo($div);
var $input = $('<input>', {
type: 'checkbox',
}).on('change', function () {
var val = $input.is(':checked');
var $cbox = $(UI.createCheckbox('cp-settings-codeindent'));
var $checkbox = $cbox.find('input').on('change', function () {
var val = $checkbox.is(':checked');
if (typeof(val) !== 'boolean') { return; }
common.setAttribute(['codemirror', key], val);
}).appendTo($inputBlock);
});
$cbox.appendTo($inputBlock);
/*proxy.on('change', ['settings', 'codemirror', key], function (o, n) {
$input[0].checked = !!n;
@@ -804,7 +811,7 @@ define([
common.getAttribute(['codemirror', key], function (e, val) {
if (e) { return void console.error(e); }
$input[0].checked = !!val;
$checkbox[0].checked = !!val;
});
return $div;
};

View File

@@ -14,7 +14,7 @@ define([
'cm/lib/codemirror',
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
'less!/bower_components/components-font-awesome/css/font-awesome.min.css',
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
'less!/customize/src/less2/main.less',
'css!cm/lib/codemirror.css',
@@ -153,53 +153,33 @@ define([
$('<b>').text(Messages.printOptions).appendTo($p);
$p.append($('<br>'));
// Slide number
$('<input>', {
type: 'checkbox',
id: 'cp-app-slide-options-number',
checked: slideOptionsTmp.slide
}).on('change', function () {
var cbox = UI.createCheckbox('cp-app-slide-options-number', Messages.printSlideNumber,
slideOptionsTmp.slide);
$(cbox).appendTo($p).find('input').on('change', function () {
var c = this.checked;
slideOptionsTmp.slide = c;
}).appendTo($p).css('width', 'auto');
$('<label>', {'for': 'cp-app-slide-options-number'}).text(Messages.printSlideNumber)
.appendTo($p);
$p.append($('<br>'));
}).css('width', 'auto');
// Date
$('<input>', {
type: 'checkbox',
id: 'cp-app-slide-options-date',
checked: slideOptionsTmp.date
}).on('change', function () {
var cboxDate = UI.createCheckbox('cp-app-slide-options-date', Messages.printDate,
slideOptionsTmp.date);
$(cboxDate).appendTo($p).find('input').on('change', function () {
var c = this.checked;
slideOptionsTmp.date = c;
}).appendTo($p).css('width', 'auto');
$('<label>', {'for': 'cp-app-slide-options-date'}).text(Messages.printDate)
.appendTo($p);
$p.append($('<br>'));
}).css('width', 'auto');
// Title
$('<input>', {
type: 'checkbox',
id: 'cp-app-slide-options-title',
checked: slideOptionsTmp.title
}).on('change', function () {
var cboxTitle = UI.createCheckbox('cp-app-slide-options-title', Messages.printTitle,
slideOptionsTmp.title);
$(cboxTitle).appendTo($p).find('input').on('change', function () {
var c = this.checked;
slideOptionsTmp.title = c;
}).appendTo($p).css('width', 'auto');
$('<label>', {'for': 'cp-app-slide-options-title'}).text(Messages.printTitle)
.appendTo($p);
$p.append($('<br>'));
}).css('width', 'auto');
// Transition
$('<input>', {
type: 'checkbox',
id: 'cp-app-slide-options-transition',
checked: slideOptionsTmp.transition
}).on('change', function () {
var cboxTransition = UI.createCheckbox('cp-app-slide-options-transition', Messages.printTransition,
slideOptionsTmp.transition);
$(cboxTransition).appendTo($p).find('input').on('change', function () {
var c = this.checked;
slideOptionsTmp.transition = c;
}).appendTo($p).css('width', 'auto');
$('<label>', {'for': 'cp-app-slide-options-transition'}).text(Messages.printTransition)
.appendTo($p);
$p.append($('<br>'));
}).css('width', 'auto');
$p.append($('<br>'));
// Background image
$('<label>', {'for': 'cp-app-slide-options-bg'}).text(Messages.printBackground)
@@ -520,9 +500,7 @@ define([
dropArea: $('.CodeMirror'),
body: $('body'),
onUploaded: function (ev, data) {
//var cursor = editor.getCursor();
//var cleanName = data.name.replace(/[\[\]]/g, '');
//var text = '!['+cleanName+']('+data.url+')';
// PASSWORD_FILES
var parsed = Hash.parsePadUrl(data.url);
var hexFileName = Util.base64ToHex(parsed.hashData.channel);
var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName;

View File

@@ -1,17 +1,13 @@
@import (once) "../../customize/src/less2/include/browser.less";
@import (once) "../../customize/src/less2/include/toolbar.less";
@import (once) "../../customize/src/less2/include/markdown.less";
@import (once) '../../customize/src/less2/include/fileupload.less';
@import (once) '../../customize/src/less2/include/alertify.less';
@import (once) '../../customize/src/less2/include/avatar.less';
@import (once) '../../customize/src/less2/include/framework.less';
.toolbar_main(
.framework_min_main(
@bg-color: @colortheme_todo-bg,
@warn-color: @colortheme_todo-warn,
@color: @colortheme_todo-color
);
.fileupload_main();
.alertify_main();
// body
&.cp-app-todo {

View File

@@ -12,7 +12,7 @@ define([
'/bower_components/sortablejs/Sortable.min.js',
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
'less!/bower_components/components-font-awesome/css/font-awesome.min.css',
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
'less!/customize/src/less2/main.less',
], function (
$,

View File

@@ -38,7 +38,8 @@ define([
}).nThen(function (/*waitFor*/) {
var getSecrets = function (Cryptpad, Utils, cb) {
Cryptpad.getTodoHash(function (hash) {
var nHash = hash || Utils.Hash.createRandomHash();
// No password for todo
var nHash = hash || Utils.Hash.createRandomHash('todo');
if (!hash) { Cryptpad.setTodoHash(nHash); }
cb(null, Utils.Hash.getSecrets('todo', nHash));
});
@@ -46,6 +47,7 @@ define([
SFCommonO.start({
getSecrets: getSecrets,
noHash: true,
owned: true
});
});
});

View File

@@ -1,21 +1,13 @@
@import (once) "../../customize/src/less2/include/browser.less";
@import (once) "../../customize/src/less2/include/toolbar.less";
@import (once) "../../customize/src/less2/include/markdown.less";
@import (once) '../../customize/src/less2/include/fileupload.less';
@import (once) '../../customize/src/less2/include/alertify.less';
@import (once) '../../customize/src/less2/include/tools.less';
@import (once) '../../customize/src/less2/include/tokenfield.less';
@import (once) '../../customize/src/less2/include/creation.less';
@import (once) "../../customize/src/less2/include/framework.less";
.toolbar_main(
.framework_main(
@bg-color: @colortheme_whiteboard-bg,
@warn-color: @colortheme_whiteboard-warn,
@color: @colortheme_whiteboard-color
);
.fileupload_main();
.alertify_main();
.tokenfield_main();
.creation_main();
// body
&.cp-app-whiteboard {

View File

@@ -20,7 +20,7 @@ define([
'/bower_components/file-saver/FileSaver.min.js',
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
'less!/bower_components/components-font-awesome/css/font-awesome.min.css',
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
'less!/customize/src/less2/main.less',
], function (
$,
@@ -389,7 +389,7 @@ define([
var D = Thumb.getResizedDimensions($canvas[0], 'pad');
Thumb.fromCanvas($canvas[0], D, function (err, b64) {
oldThumbnailState = content;
Thumb.setPadThumbnail(common, href, b64);
Thumb.setPadThumbnail(common, href, privateDat.channel, b64);
});
};
window.setInterval(mkThumbnail, Thumb.UPDATE_INTERVAL);

View File

@@ -1,14 +1,9 @@
@import (once) "../../customize/src/less2/include/browser.less";
@import (once) "../../customize/src/less2/include/toolbar.less";
@import (once) "../../customize/src/less2/include/markdown.less";
@import (once) '../../customize/src/less2/include/fileupload.less';
@import (once) '../../customize/src/less2/include/alertify.less';
@import (once) '../../customize/src/less2/include/avatar.less';
@import (once) '../../customize/src/less2/include/framework.less';
.toolbar_main();
.fileupload_main();
.alertify_main();
.framework_min_main();
// body
&.cp-app-worker {

View File

@@ -8,7 +8,7 @@ define([
'/customize/messages.js',
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
'less!/bower_components/components-font-awesome/css/font-awesome.min.css',
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
'less!/customize/src/less2/main.less',
], function (
$,