Merge branch 'staging' of github.com:xwiki-labs/cryptpad into staging

pull/1/head
yflory 5 years ago
commit a9e4af1d12

@ -52,6 +52,22 @@
} }
} }
div.cp-button-confirm {
display: inline-block;
button {
margin: 0;
}
.cp-button-timer {
height: 3px;
& > div {
height: 100%;
background-color: @colortheme_alertify-primary;
&.danger, &.btn-danger, &.danger-alt, &.btn-danger-alt {
background-color: @colortheme_alertify-red;
}
}
}
}
button:not(.pure-button):not(.md-button):not(.mdl-button) { button:not(.pure-button):not(.md-button):not(.mdl-button) {
@ -89,6 +105,7 @@
white-space: normal; white-space: normal;
font-weight: bold; font-weight: bold;
} }
&.danger, &.btn-danger { &.danger, &.btn-danger {
background-color: @colortheme_alertify-red; background-color: @colortheme_alertify-red;
border-color: @colortheme_alertify-red-border; border-color: @colortheme_alertify-red-border;
@ -98,6 +115,15 @@
} }
} }
&.danger-alt, &.btn-danger-alt {
border-color: @colortheme_alertify-red;
color: @colortheme_alertify-red;
&:hover, &:active {
color: @colortheme_alertify-red-color;
background-color: contrast(@colortheme_modal-bg, darken(@colortheme_alertify-red, 10%), lighten(@colortheme_alertify-red, 10%));
}
}
&.safe, &.btn-safe { &.safe, &.btn-safe {
background-color: @colortheme_alertify-green; background-color: @colortheme_alertify-green;
border-color: @colortheme_alertify-green-border; border-color: @colortheme_alertify-green-border;

@ -26,6 +26,42 @@
// Properties modal // Properties modal
.cp-app-prop { .cp-app-prop {
margin-bottom: 10px; margin-bottom: 10px;
.cp-app-prop-size-container {
height: 20px;
background-color: @colortheme_logo-2;
margin: 10px 0;
padding: 0;
div {
height: 20px;
margin: 0;
padding: 0;
background-color: #CCCCCC;
}
}
.cp-app-prop-size-legend {
color: @colortheme_modal-fg;
display: flex;
margin: 10px 0;
& > div {
display: flex;
align-items: center;
flex-basis: 50%;
margin: 0;
padding: 0;
}
.cp-app-prop-history-size-color, .cp-app-prop-contents-size-color {
display: inline-block;
height: 20px;
width: 20px;
margin-right: 10px;
}
.cp-app-prop-history-size-color {
background-color: #CCCCCC;
}
.cp-app-prop-contents-size-color {
background-color: @colortheme_logo-2;
}
}
} }
.cp-app-prop-content { .cp-app-prop-content {

@ -599,6 +599,7 @@ const handleGetHistory = function (Env, Server, seq, userId, parsed) {
var config = parsed[2]; var config = parsed[2];
var metadata = {}; var metadata = {};
var lastKnownHash; var lastKnownHash;
var txid;
// clients can optionally pass a map of attributes // clients can optionally pass a map of attributes
// if the channel already exists this map will be ignored // if the channel already exists this map will be ignored
@ -606,6 +607,7 @@ const handleGetHistory = function (Env, Server, seq, userId, parsed) {
if (config && typeof config === "object" && !Array.isArray(parsed[2])) { if (config && typeof config === "object" && !Array.isArray(parsed[2])) {
lastKnownHash = config.lastKnownHash; lastKnownHash = config.lastKnownHash;
metadata = config.metadata || {}; metadata = config.metadata || {};
txid = config.txid;
if (metadata.expire) { if (metadata.expire) {
metadata.expire = +metadata.expire * 1000 + (+new Date()); metadata.expire = +metadata.expire * 1000 + (+new Date());
} }
@ -655,11 +657,12 @@ const handleGetHistory = function (Env, Server, seq, userId, parsed) {
msgCount++; msgCount++;
// avoid sending the metadata message a second time // avoid sending the metadata message a second time
if (isMetadataMessage(msg) && metadata_cache[channelName]) { return readMore(); } if (isMetadataMessage(msg) && metadata_cache[channelName]) { return readMore(); }
if (txid) { msg[0] = txid; }
Server.send(userId, [0, HISTORY_KEEPER_ID, 'MSG', userId, JSON.stringify(msg)], readMore); Server.send(userId, [0, HISTORY_KEEPER_ID, 'MSG', userId, JSON.stringify(msg)], readMore);
}, (err) => { }, (err) => {
if (err && err.code !== 'ENOENT') { if (err && err.code !== 'ENOENT') {
if (err.message !== 'EINVAL') { Log.error("HK_GET_HISTORY", err); } if (err.message !== 'EINVAL') { Log.error("HK_GET_HISTORY", err); }
const parsedMsg = {error:err.message, channel: channelName}; const parsedMsg = {error:err.message, channel: channelName, txid: txid};
Server.send(userId, [0, HISTORY_KEEPER_ID, 'MSG', userId, JSON.stringify(parsedMsg)]); Server.send(userId, [0, HISTORY_KEEPER_ID, 'MSG', userId, JSON.stringify(parsedMsg)]);
return; return;
} }
@ -707,7 +710,7 @@ const handleGetHistory = function (Env, Server, seq, userId, parsed) {
} }
// End of history message: // End of history message:
let parsedMsg = {state: 1, channel: channelName}; let parsedMsg = {state: 1, channel: channelName, txid: txid};
Server.send(userId, [0, HISTORY_KEEPER_ID, 'MSG', userId, JSON.stringify(parsedMsg)]); Server.send(userId, [0, HISTORY_KEEPER_ID, 'MSG', userId, JSON.stringify(parsedMsg)]);
}); });

@ -975,10 +975,11 @@ var trimChannel = function (env, channelName, hash, _cb) {
if (msgHash === hash) { if (msgHash === hash) {
// everything from this point on should be retained // everything from this point on should be retained
retain = true; retain = true;
return void tempStream.write(msgObj.buff, function () { return void tempStream.write(s_msg + '\n', function () {
readMore(); readMore();
}); });
} }
readMore();
}; };
readMessagesBin(env, channelName, 0, handler, w(function (err) { readMessagesBin(env, channelName, 0, handler, w(function (err) {

@ -599,6 +599,59 @@ define([
} }
}); });
}; };
UI.confirmButton = function (originalBtn, config, _cb) {
config = config || {};
var cb = Util.once(Util.mkAsync(_cb));
var classes = 'btn ' + (config.classes || 'btn-primary');
var button = h('button', {
"class": classes,
title: config.title || ''
}, Messages.areYouSure || "Are you sure?"); // XXX
var $button = $(button);
var div = h('div', {
"class": config.classes || ''
});
var timer = h('div.cp-button-timer', div);
var content = h('div.cp-button-confirm', [
button,
timer
]);
var to;
var done = function (res) {
cb(res);
clearTimeout(to);
$(content).remove();
$(originalBtn).show();
};
$button.click(function () {
done(true);
});
var TIMEOUT = 3000;
var INTERVAL = 10;
var i = 1;
var todo = function () {
var p = 100 * ((TIMEOUT - (i * INTERVAL)) / TIMEOUT);
if (i++ * INTERVAL >= TIMEOUT) {
done(false);
return;
}
$(div).css('width', p+'%');
to = setTimeout(todo, INTERVAL);
};
to = setTimeout(todo, INTERVAL);
$(originalBtn).hide().after(content);
};
UI.proposal = function (content, cb) { UI.proposal = function (content, cb) {
var buttons = [{ var buttons = [{

@ -30,6 +30,13 @@ define([
}); });
} }
UIElements.prettySize = function (bytes) {
var kB = Util.bytesToKilobytes(bytes);
if (kB < 1024) { return kB + Messages.KB; }
var mB = Util.bytesToMegabytes(bytes);
return mB + Messages.MB;
};
UIElements.updateTags = function (common, href) { UIElements.updateTags = function (common, href) {
var existing, tags; var existing, tags;
NThen(function(waitFor) { NThen(function(waitFor) {
@ -704,6 +711,9 @@ define([
var $d = $('<div>'); var $d = $('<div>');
if (!data) { return void cb(void 0, $d); } if (!data) { return void cb(void 0, $d); }
var priv = common.getMetadataMgr().getPrivateData();
var edPublic = priv.edPublic;
if (data.href) { if (data.href) {
$('<label>', {'for': 'cp-app-prop-link'}).text(Messages.editShare).appendTo($d); $('<label>', {'for': 'cp-app-prop-link'}).text(Messages.editShare).appendTo($d);
$d.append(UI.dialog.selectable(data.href, { $d.append(UI.dialog.selectable(data.href, {
@ -730,13 +740,30 @@ define([
$d.append(h('div.cp-app-prop', [Messages.fm_lastAccess, h('br'), h('span.cp-app-prop-content', new Date(data.atime).toLocaleString())])); $d.append(h('div.cp-app-prop', [Messages.fm_lastAccess, h('br'), h('span.cp-app-prop-content', new Date(data.atime).toLocaleString())]));
} }
var owned = false;
if (common.isLoggedIn()) { if (common.isLoggedIn()) {
if (Array.isArray(data.owners)) {
if (data.owners.indexOf(edPublic) !== -1) {
owned = true;
} else {
Object.keys(priv.teams || {}).some(function (id) {
var team = priv.teams[id] || {};
if (team.viewer) { return; }
if (data.owners.indexOf(team.edPublic) === -1) { return; }
owned = id;
return true;
});
}
}
// check the size of this file... // check the size of this file...
var bytes = 0; var bytes = 0;
NThen(function (waitFor) { var historyBytes;
var chan = [data.channel]; var chan = [data.channel];
if (data.rtChannel) { chan.push(data.rtChannel); } if (data.rtChannel) { chan.push(data.rtChannel); }
if (data.lastVersion) { chan.push(Hash.hrefToHexChannelId(data.lastVersion)); } if (data.lastVersion) { chan.push(Hash.hrefToHexChannelId(data.lastVersion)); }
var history = common.makeUniversal('history');
var trimChannels = [];
NThen(function (waitFor) {
chan.forEach(function (c) { chan.forEach(function (c) {
common.getFileSize(c, waitFor(function (e, _bytes) { common.getFileSize(c, waitFor(function (e, _bytes) {
if (e) { if (e) {
@ -746,20 +773,88 @@ define([
bytes += _bytes; bytes += _bytes;
})); }));
}); });
if (!owned) { return; }
history.execCommand('GET_HISTORY_SIZE', {
pad: true,
channels: chan.filter(function (c) { return c.length === 32; }),
teamId: typeof(owned) === "number" && owned
}, waitFor(function (obj) {
if (obj && obj.error) { return; }
historyBytes = obj.size;
trimChannels = obj.channels;
}));
}).nThen(function () { }).nThen(function () {
if (bytes === 0) { return void cb(void 0, $d); } if (bytes === 0) { return void cb(void 0, $d); }
var KB = Util.bytesToKilobytes(bytes); var formatted = UIElements.prettySize(bytes);
var formatted = Messages._getKey('formattedKB', [KB]); if (!owned || !historyBytes || historyBytes > bytes || historyBytes < 0) {
$d.append(h('div.cp-app-prop', [Messages.upload_size, h('br'), h('span.cp-app-prop-content', formatted)])); $d.append(h('div.cp-app-prop', [
Messages.upload_size,
h('br'),
h('span.cp-app-prop-content', formatted)
]));
return void cb(void 0, $d);
}
if (data.sharedFolder && false) { Messages.historyTrim_historySize = 'History: {0}'; // XXX
$('<label>', {'for': 'cp-app-prop-channel'}).text('Channel ID').appendTo($d); Messages.historyTrim_contentsSize = 'Contents: {0}'; // XXX
if (AppConfig.pinBugRecovery) { $d.append(h('p', AppConfig.pinBugRecovery)); }
$d.append(UI.dialog.selectable(data.channel, { var p = Math.round((historyBytes / bytes) * 100);
id: 'cp-app-prop-link', var historyPrettySize = UIElements.prettySize(historyBytes);
})); var contentsPrettySize = UIElements.prettySize(bytes - historyBytes);
var button;
var spinner = UI.makeSpinner();
var size = h('div.cp-app-prop', [
Messages.upload_size,
h('br'),
h('div.cp-app-prop-size-container', [
h('div.cp-app-prop-size-history', { style: 'width:'+p+'%;' })
]),
h('div.cp-app-prop-size-legend', [
h('div.cp-app-prop-history-size', [
h('span.cp-app-prop-history-size-color'),
h('span.cp-app-prop-content', Messages._getKey('historyTrim_historySize', [historyPrettySize]))
]),
h('div.cp-app-prop-contents-size', [
h('span.cp-app-prop-contents-size-color'),
h('span.cp-app-prop-content', Messages._getKey('historyTrim_contentsSize', [contentsPrettySize]))
]),
]),
button = h('button.btn.btn-danger-alt.no-margin', Messages.trimHistory_button || 'test'), // XXX
spinner.spinner
]);
$d.append(size);
var $button = $(button);
$button.click(function () {
UI.confirmButton(button, {
classes: 'btn-danger'
}, function (yes) {
if (!yes) { return; }
$button.remove();
spinner.spin();
history.execCommand('TRIM_HISTORY', {
pad: true,
channels: trimChannels,
teamId: typeof(owned) === "number" && owned
}, function (obj) {
spinner.hide();
if (obj && obj.error) {
$(size).append(h('div.alert.alert-danger', Messages.trimHistory_error || 'error')); // XXX
return;
} }
$(size).remove();
var formatted = UIElements.prettySize(bytes - historyBytes);
$d.append(h('div.cp-app-prop', [
Messages.upload_size,
h('br'),
h('span.cp-app-prop-content', formatted)
]));
});
});
});
cb(void 0, $d); cb(void 0, $d);
}); });

@ -227,6 +227,7 @@
else if (bytes >= oneMegabyte) { return 'MB'; } else if (bytes >= oneMegabyte) { return 'MB'; }
}; };
// given a path, asynchronously return an arraybuffer // given a path, asynchronously return an arraybuffer
Util.fetch = function (src, cb, progress) { Util.fetch = function (src, cb, progress) {
var CB = Util.once(cb); var CB = Util.once(cb);

@ -17,6 +17,7 @@ define([
'/common/outer/profile.js', '/common/outer/profile.js',
'/common/outer/team.js', '/common/outer/team.js',
'/common/outer/messenger.js', '/common/outer/messenger.js',
'/common/outer/history.js',
'/common/outer/network-config.js', '/common/outer/network-config.js',
'/customize/application_config.js', '/customize/application_config.js',
@ -28,7 +29,7 @@ define([
'/bower_components/saferphore/index.js', '/bower_components/saferphore/index.js',
], function (Sortify, UserObject, ProxyManager, Migrate, Hash, Util, Constants, Feedback, ], function (Sortify, UserObject, ProxyManager, Migrate, Hash, Util, Constants, Feedback,
Realtime, Messaging, Pinpad, Realtime, Messaging, Pinpad,
SF, Cursor, OnlyOffice, Mailbox, Profile, Team, Messenger, SF, Cursor, OnlyOffice, Mailbox, Profile, Team, Messenger, History,
NetConfig, AppConfig, NetConfig, AppConfig,
Crypto, ChainPad, CpNetflux, Listmap, nThen, Saferphore) { Crypto, ChainPad, CpNetflux, Listmap, nThen, Saferphore) {
@ -49,8 +50,6 @@ define([
var sendDriveEvent = function () {}; var sendDriveEvent = function () {};
var registerProxyEvents = function () {}; var registerProxyEvents = function () {};
var storeHash, storeChannel;
var store = window.CryptPad_AsyncStore = { var store = window.CryptPad_AsyncStore = {
modules: {} modules: {}
}; };
@ -159,13 +158,7 @@ define([
}; };
var getUserChannelList = function () { var getUserChannelList = function () {
// start with your userHash... var userChannel = store.driveChannel;
var userHash = storeHash;
if (!userHash) { return null; }
// No password for drive
var secret = Hash.getSecrets('drive', userHash);
var userChannel = secret.channel;
if (!userChannel) { return null; } if (!userChannel) { return null; }
// Get the list of pads' channel ID in your drive // Get the list of pads' channel ID in your drive
@ -173,7 +166,7 @@ define([
// It now includes channels from shared folders // It now includes channels from shared folders
var list = store.manager.getChannelsList('pin'); var list = store.manager.getChannelsList('pin');
// Get the avatar // Get the avatar & profile
var profile = store.proxy.profile; var profile = store.proxy.profile;
if (profile) { if (profile) {
var profileChan = profile.edit ? Hash.hrefToHexChannelId('/profile/#' + profile.edit, null) : null; var profileChan = profile.edit ? Hash.hrefToHexChannelId('/profile/#' + profile.edit, null) : null;
@ -182,6 +175,10 @@ define([
if (avatarChan) { list.push(avatarChan); } if (avatarChan) { list.push(avatarChan); }
} }
if (store.proxy.todo) {
list.push(Hash.hrefToHexChannelId('/todo/#' + store.proxy.todo, null));
}
if (store.proxy.friends) { if (store.proxy.friends) {
var fList = Messaging.getFriendChannelsList(store.proxy); var fList = Messaging.getFriendChannelsList(store.proxy);
list = list.concat(fList); list = list.concat(fList);
@ -314,7 +311,7 @@ define([
teamId = data.teamId; teamId = data.teamId;
} }
if (channel === storeChannel && !force) { if (channel === store.driveChannel && !force) {
return void cb({error: 'User drive removal blocked!'}); return void cb({error: 'User drive removal blocked!'});
} }
@ -446,10 +443,12 @@ define([
}); });
}; };
Store.getFileSize = function (clientId, data, cb) { Store.getFileSize = function (clientId, data, _cb) {
var cb = Util.once(Util.mkAsync(_cb));
if (!store.anon_rpc) { return void cb({error: 'ANON_RPC_NOT_READY'}); } if (!store.anon_rpc) { return void cb({error: 'ANON_RPC_NOT_READY'}); }
var channelId = Hash.hrefToHexChannelId(data.href, data.password); var channelId = data.channel || Hash.hrefToHexChannelId(data.href, data.password);
store.anon_rpc.send("GET_FILE_SIZE", channelId, function (e, response) { store.anon_rpc.send("GET_FILE_SIZE", channelId, function (e, response) {
if (e) { return void cb({error: e}); } if (e) { return void cb({error: e}); }
if (response && response.length && typeof(response[0]) === 'number') { if (response && response.length && typeof(response[0]) === 'number') {
@ -713,11 +712,9 @@ define([
Store.deleteAccount = function (clientId, data, cb) { Store.deleteAccount = function (clientId, data, cb) {
var edPublic = store.proxy.edPublic; var edPublic = store.proxy.edPublic;
// No password for drive
var secret = Hash.getSecrets('drive', storeHash);
Store.anonRpcMsg(clientId, { Store.anonRpcMsg(clientId, {
msg: 'GET_METADATA', msg: 'GET_METADATA',
data: secret.channel data: store.driveChannel
}, function (data) { }, function (data) {
var metadata = data[0]; var metadata = data[0];
// Owned drive // Owned drive
@ -737,7 +734,7 @@ define([
}).nThen(function (waitFor) { }).nThen(function (waitFor) {
// Delete Drive // Delete Drive
Store.removeOwnedChannel(clientId, { Store.removeOwnedChannel(clientId, {
channel: secret.channel, channel: store.driveChannel,
force: true force: true
}, waitFor()); }, waitFor());
}).nThen(function () { }).nThen(function () {
@ -753,7 +750,7 @@ define([
var toSign = { var toSign = {
intent: 'Please delete my account.' intent: 'Please delete my account.'
}; };
toSign.drive = secret.channel; toSign.drive = store.driveChannel;
toSign.edPublic = edPublic; toSign.edPublic = edPublic;
var signKey = Crypto.Nacl.util.decodeBase64(store.proxy.edPrivate); var signKey = Crypto.Nacl.util.decodeBase64(store.proxy.edPrivate);
var proof = Crypto.Nacl.sign.detached(Crypto.Nacl.util.decodeUTF8(Sortify(toSign)), signKey); var proof = Crypto.Nacl.sign.detached(Crypto.Nacl.util.decodeUTF8(Sortify(toSign)), signKey);
@ -1768,7 +1765,9 @@ define([
// Fetch the latest version of the metadata on the server and return it. // Fetch the latest version of the metadata on the server and return it.
// If the pad is stored in our drive, update the local values of "owners" and "expire" // If the pad is stored in our drive, update the local values of "owners" and "expire"
Store.getPadMetadata = function (clientId, data, cb) { Store.getPadMetadata = function (clientId, data, _cb) {
var cb = Util.once(Util.mkAsync(_cb));
if (!data.channel) { return void cb({ error: 'ENOTFOUND'}); } if (!data.channel) { return void cb({ error: 'ENOTFOUND'}); }
store.anon_rpc.send('GET_METADATA', data.channel, function (err, obj) { store.anon_rpc.send('GET_METADATA', data.channel, function (err, obj) {
if (err) { return void cb({error: err}); } if (err) { return void cb({error: err}); }
@ -1850,7 +1849,9 @@ define([
network.sendto(hk, JSON.stringify(['GET_FULL_HISTORY', data.channel, data.validateKey])); network.sendto(hk, JSON.stringify(['GET_FULL_HISTORY', data.channel, data.validateKey]));
}; };
Store.getHistory = function (clientId, data, cb) { Store.getHistory = function (clientId, data, _cb, full) {
var cb = Util.once(Util.mkAsync(_cb));
var network = store.network; var network = store.network;
var hk = network.historyKeeper; var hk = network.historyKeeper;
@ -1862,6 +1863,8 @@ define([
} }
}; };
var txid = Math.floor(Math.random() * 1000000);
var msgs = []; var msgs = [];
var completed = false; var completed = false;
var onMsg = function (msg, sender) { var onMsg = function (msg, sender) {
@ -1870,6 +1873,8 @@ define([
var parsed = parse(msg); var parsed = parse(msg);
if (!parsed) { return; } if (!parsed) { return; }
if (parsed.txid && parsed.txid !== txid) { return; }
// Ignore the metadata message // Ignore the metadata message
if (parsed.validateKey && parsed.channel) { return; } if (parsed.validateKey && parsed.channel) { return; }
if (parsed.error && parsed.channel) { if (parsed.error && parsed.channel) {
@ -1890,9 +1895,20 @@ define([
return; return;
} }
msg = parsed[4]; if (Array.isArray(parsed) && parsed[0] && parsed[0] !== txid) { return; }
// Keep only the history for our channel // Keep only the history for our channel
if (parsed[3] !== data.channel) { return; } if (parsed[3] !== data.channel) { return; }
// If we want the full messages, push the parsed data
if (parsed[4] && full) {
msgs.push({
msg: msg,
hash: parsed[4].slice(0,64)
});
return;
}
// Otherwise, push the messages
msg = parsed[4];
if (msg) { if (msg) {
msg = msg.replace(/cp\|(([A-Za-z0-9+\/=]+)\|)?/, ''); msg = msg.replace(/cp\|(([A-Za-z0-9+\/=]+)\|)?/, '');
msgs.push(msg); msgs.push(msg);
@ -1901,6 +1917,7 @@ define([
network.on('message', onMsg); network.on('message', onMsg);
var cfg = { var cfg = {
txid: txid,
lastKnownHash: data.lastKnownHash lastKnownHash: data.lastKnownHash
}; };
var msg = ['GET_HISTORY', data.channel, cfg]; var msg = ['GET_HISTORY', data.channel, cfg];
@ -2071,6 +2088,7 @@ define([
store.mailbox.removeClient(clientId); store.mailbox.removeClient(clientId);
} catch (e) { console.error(e); } } catch (e) { console.error(e); }
Object.keys(store.modules).forEach(function (key) { Object.keys(store.modules).forEach(function (key) {
if (!store.modules[key].removeClient) { return; }
try { try {
store.modules[key].removeClient(clientId); store.modules[key].removeClient(clientId);
} catch (e) { console.error(e); } } catch (e) { console.error(e); }
@ -2332,6 +2350,7 @@ define([
store.messenger = store.modules['messenger']; store.messenger = store.modules['messenger'];
loadUniversal(Profile, 'profile', waitFor); loadUniversal(Profile, 'profile', waitFor);
loadUniversal(Team, 'team', waitFor); loadUniversal(Team, 'team', waitFor);
loadUniversal(History, 'history', waitFor);
cleanFriendRequests(); cleanFriendRequests();
}).nThen(function () { }).nThen(function () {
var requestLogin = function () { var requestLogin = function () {
@ -2427,13 +2446,12 @@ define([
var connect = function (clientId, data, cb) { var connect = function (clientId, data, cb) {
var hash = data.userHash || data.anonHash || Hash.createRandomHash('drive'); var hash = data.userHash || data.anonHash || Hash.createRandomHash('drive');
storeHash = hash;
if (!hash) { if (!hash) {
return void cb({error: '[Store.init] Unable to find or create a drive hash. Aborting...'}); return void cb({error: '[Store.init] Unable to find or create a drive hash. Aborting...'});
} }
// No password for drive // No password for drive
var secret = Hash.getSecrets('drive', hash); var secret = Hash.getSecrets('drive', hash);
storeChannel = secret.channel; store.driveChannel = secret.channel;
var listmapConfig = { var listmapConfig = {
data: {}, data: {},
websocketURL: NetConfig.getWebsocketURL(), websocketURL: NetConfig.getWebsocketURL(),

@ -0,0 +1,246 @@
define([
'/common/common-util.js',
'/common/common-hash.js',
'/common/userObject.js',
'/bower_components/nthen/index.js',
], function (Util, Hash, UserObject, nThen) {
var History = {};
var commands = {};
var getAccountChannels = function (ctx) {
var channels = [];
var edPublic = Util.find(ctx.store, ['proxy', 'edPublic']);
// Drive
var driveOwned = (Util.find(ctx.store, ['driveMetadata', 'owners']) || []).indexOf(edPublic) !== -1;
if (driveOwned) {
channels.push(ctx.store.driveChannel);
}
// Profile
var profile = ctx.store.proxy.profile;
if (profile) {
var profileChan = profile.edit ? Hash.hrefToHexChannelId('/profile/#' + profile.edit, null) : null;
if (profileChan) { channels.push(profileChan); }
}
// Todo
if (ctx.store.proxy.todo) {
channels.push(Hash.hrefToHexChannelId('/todo/#' + ctx.store.proxy.todo, null));
}
// Mailboxes
var mailboxes = ctx.store.proxy.mailboxes;
if (mailboxes) {
var mList = Object.keys(mailboxes).map(function (m) {
return {
lastKnownHash: mailboxes[m].lastKnownHash,
channel: mailboxes[m].channel
};
});
Array.prototype.push.apply(channels, mList);
}
// Shared folders owned by me
var sf = ctx.store.proxy[UserObject.SHARED_FOLDERS];
if (sf) {
var sfChannels = Object.keys(sf).map(function (fId) {
var data = sf[fId];
if (!data || !data.owners) { return; }
var isOwner = Array.isArray(data.owners) && data.owners.indexOf(edPublic) !== -1;
if (!isOwner) { return; }
return data.channel;
}).filter(Boolean);
Array.prototype.push.apply(channels, sfChannels);
}
return channels;
};
var getEdPublic = function (ctx, teamId) {
if (!teamId) { return Util.find(ctx.store, ['proxy', 'edPublic']); }
var teamData = Util.find(ctx, ['store', 'proxy', 'teams', teamId]);
return Util.find(teamData, ['keys', 'drive', 'edPublic']);
};
var getRpc = function (ctx, teamId) {
if (!teamId) { return ctx.store.rpc; }
var teams = ctx.store.modules['team'];
if (!teams) { return; }
var team = teams.getTeam(teamId);
if (!team) { return; }
return team.rpc;
};
var getHistoryData = function (ctx, channel, lastKnownHash, teamId, _cb) {
var cb = Util.once(Util.mkAsync(_cb));
var edPublic = getEdPublic(ctx, teamId);
var Store = ctx.Store;
var total = 0;
var history = 0;
var metadata = 0;
var hash;
nThen(function (waitFor) {
// Total size
Store.getFileSize(null, {
channel: channel
}, waitFor(function (obj) {
if (obj && obj.error) {
waitFor.abort();
return void cb(obj);
}
if (typeof(obj.size) === "undefined") {
waitFor.abort();
return void cb({error: 'ENOENT'});
}
total = obj.size;
}));
// Pad
Store.getHistory(null, {
channel: channel,
lastKnownHash: lastKnownHash
}, waitFor(function (obj) {
if (obj && obj.error) {
waitFor.abort();
return void cb(obj);
}
if (!Array.isArray(obj)) {
waitFor.abort();
return void cb({error: 'EINVAL'});
}
if (!obj.length) { return; }
hash = obj[0].hash;
var messages = obj.map(function(data) {
return data.msg;
});
history = messages.join('\n').length;
}), true);
// Metadata
Store.getPadMetadata(null, {
channel: channel
}, waitFor(function (obj) {
if (obj && obj.error) { return; }
if (!obj || typeof(obj) !== "object") { return; }
metadata = JSON.stringify(obj).length;
if (!obj || !Array.isArray(obj.owners) ||
obj.owners.indexOf(edPublic) === -1) {
waitFor.abort();
return void cb({error: 'INSUFFICIENT_PERMISSIONS'});
}
}));
}).nThen(function () {
cb({
size: (total - metadata - history),
hash: hash
});
});
};
commands.GET_HISTORY_SIZE = function (ctx, data, cId, cb) {
if (!ctx.store.loggedIn || !ctx.store.rpc) { return void cb({ error: 'INSUFFICIENT_PERMISSIONS' }); }
var channels = data.channels;
if (!Array.isArray(channels)) { return void cb({ error: 'EINVAL' }); }
var warning = [];
// If account trim history, get the correct channels here
if (data.account) {
channels = getAccountChannels(ctx);
}
var size = 0;
var res = [];
nThen(function (waitFor) {
channels.forEach(function (chan) {
var channel = chan;
var lastKnownHash;
if (typeof (chan) === "object" && chan.channel) {
channel = chan.channel;
lastKnownHash = chan.lastKnownHash;
}
getHistoryData(ctx, channel, lastKnownHash, data.teamId, waitFor(function (obj) {
if (obj && obj.error) {
warning.push(obj.error);
return;
}
size += obj.size;
if (!obj.hash) { return; }
res.push({
channel: channel,
hash: obj.hash
});
}));
});
}).nThen(function () {
cb({
warning: warning.length ? warning : undefined,
channels: res,
size: size
});
});
};
commands.TRIM_HISTORY = function (ctx, data, cId, cb) {
if (!ctx.store.loggedIn || !ctx.store.rpc) { return void cb({ error: 'INSUFFICIENT_PERMISSIONS' }); }
var channels = data.channels;
if (!Array.isArray(channels)) { return void cb({ error: 'EINVAL' }); }
var rpc = getRpc(ctx, data.teamId);
if (!rpc) { return void cb({ error: 'ENORPC'}); }
var warning = [];
nThen(function (waitFor) {
channels.forEach(function (obj) {
rpc.trimHistory(obj, waitFor(function (err) {
if (err) {
warning.push(err);
return;
}
}));
});
}).nThen(function () {
// Only one channel and warning: error
if (channels.length === 1 && warning.length) {
return void cb({error: warning[0]});
}
cb({
warning: warning.length ? warning : undefined
});
});
};
History.init = function (cfg, waitFor, emit) {
var history = {};
if (!cfg.store) { return; }
var ctx = {
store: cfg.store,
Store: cfg.Store,
pinPads: cfg.pinPads,
updateMetadata: cfg.updateMetadata,
emit: emit,
};
history.execCommand = function (clientId, obj, cb) {
var cmd = obj.cmd;
var data = obj.data;
try {
commands[cmd](ctx, data, clientId, cb);
} catch (e) {
console.error(e);
}
};
return history;
};
return History;
});

@ -125,6 +125,17 @@ var factory = function (Util, Rpc) {
}); });
}; };
exp.trimHistory = function (data, _cb) {
var cb = Util.once(Util.mkAsync(_cb));
if (typeof(data) !== 'object' || !data.channel || !data.hash) {
return void cb('INVALID_ARGUMENTS');
}
rpc.send('TRIM_HISTORY', data, function (e) {
if (e) { return cb(e); }
cb();
});
};
exp.clearOwnedChannel = function (channel, cb) { exp.clearOwnedChannel = function (channel, cb) {
if (typeof(channel) !== 'string' || channel.length !== 32) { if (typeof(channel) !== 'string' || channel.length !== 32) {
return void cb('INVALID_ARGUMENTS'); return void cb('INVALID_ARGUMENTS');

@ -188,13 +188,6 @@ define([
} }
}; };
var prettySize = function (bytes) {
var kB = Util.bytesToKilobytes(bytes);
if (kB < 1024) { return kB + Messages.KB; }
var mB = Util.bytesToMegabytes(bytes);
return mB + Messages.MB;
};
queue.next = function () { queue.next = function () {
if (queue.queue.length === 0) { if (queue.queue.length === 0) {
clearTimeout(queue.to); clearTimeout(queue.to);
@ -262,7 +255,7 @@ define([
// name // name
$('<td>').append($link).appendTo($tr); $('<td>').append($link).appendTo($tr);
// size // size
$('<td>').text(prettySize(estimate)).appendTo($tr); $('<td>').text(UIElements.prettySize(estimate)).appendTo($tr);
// progress // progress
$('<td>', {'class': 'cp-fileupload-table-progress'}).append($progressContainer).appendTo($tr); $('<td>', {'class': 'cp-fileupload-table-progress'}).append($progressContainer).appendTo($tr);
// cancel // cancel

@ -21,6 +21,11 @@
max-width: 650px; max-width: 650px;
} }
div.alert {
font-size: @colortheme_app-font-size;
padding: 10px;
}
#cp-export-container { #cp-export-container {
font-size: 16px; font-size: 16px;
display: flex; display: flex;
@ -171,7 +176,7 @@
} }
} }
.cp-settings-change-password, .cp-settings-migrate { .cp-settings-change-password, .cp-settings-own-drive {
[type="password"], [type="text"] { [type="password"], [type="text"] {
width: @sidebar_button-width; width: @sidebar_button-width;
flex: unset; flex: unset;

@ -48,12 +48,12 @@ define([
var categories = { var categories = {
'account': [ 'account': [
'cp-settings-own-drive',
'cp-settings-info-block', 'cp-settings-info-block',
'cp-settings-displayname', 'cp-settings-displayname',
'cp-settings-language-selector', 'cp-settings-language-selector',
'cp-settings-resettips', 'cp-settings-resettips',
'cp-settings-change-password', 'cp-settings-change-password',
'cp-settings-migrate',
'cp-settings-delete' 'cp-settings-delete'
], ],
'security': [ 'security': [
@ -73,7 +73,8 @@ define([
'cp-settings-thumbnails', 'cp-settings-thumbnails',
'cp-settings-drive-backup', 'cp-settings-drive-backup',
'cp-settings-drive-import-local', 'cp-settings-drive-import-local',
'cp-settings-drive-reset' 'cp-settings-trim-history'
//'cp-settings-drive-reset'
], ],
'cursor': [ 'cursor': [
'cp-settings-cursor-color', 'cp-settings-cursor-color',
@ -146,6 +147,11 @@ define([
hintFunction(safeKey).appendTo($div); hintFunction(safeKey).appendTo($div);
} }
getter(function (content) { getter(function (content) {
if (content === false) {
$div.remove();
$div = undefined;
return;
}
$div.append(content); $div.append(content);
}, $div); }, $div);
return $div; return $div;
@ -520,19 +526,12 @@ define([
return $div; return $div;
}; };
create['migrate'] = function () { makeBlock('own-drive', function (cb, $div) {
if (privateData.isDriveOwned) { return; } if (privateData.isDriveOwned || !common.isLoggedIn()) {
if (!common.isLoggedIn()) { return; } return void cb(false);
}
var $div = $('<div>', { 'class': 'cp-settings-migrate cp-sidebarlayout-element'});
$('<span>', {'class': 'label'}).text(Messages.settings_ownDriveTitle).appendTo($div);
$('<span>', {'class': 'cp-sidebarlayout-description'})
.append(Messages.settings_ownDriveHint).appendTo($div);
var $ok = $('<span>', {'class': 'fa fa-check', title: Messages.saved}); $div.addClass('alert alert-warning');
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'});
var form = h('div', [ var form = h('div', [
UI.passwordInput({ UI.passwordInput({
@ -541,13 +540,13 @@ define([
}, true), }, true),
h('button.btn.btn-primary', Messages.settings_ownDriveButton) h('button.btn.btn-primary', Messages.settings_ownDriveButton)
]); ]);
var $form = $(form);
$(form).appendTo($div); var spinner = UI.makeSpinner($form);
var todo = function () { var todo = function () {
var password = $(form).find('#cp-settings-migrate-password').val(); var password = $form.find('#cp-settings-migrate-password').val();
if (!password) { return; } if (!password) { return; }
$spinner.show(); spinner.spin();
UI.confirm(Messages.settings_ownDriveConfirm, function (yes) { UI.confirm(Messages.settings_ownDriveConfirm, function (yes) {
if (!yes) { return; } if (!yes) { return; }
var data = { var data = {
@ -561,16 +560,15 @@ define([
sframeChan.query('Q_CHANGE_USER_PASSWORD', data, function (err, obj) { sframeChan.query('Q_CHANGE_USER_PASSWORD', data, function (err, obj) {
UI.removeLoadingScreen(); UI.removeLoadingScreen();
if (err || obj.error) { return UI.alert(Messages.settings_changePasswordError); } if (err || obj.error) { return UI.alert(Messages.settings_changePasswordError); }
$ok.show(); spinner.done();
$spinner.hide();
}); });
}); });
}; };
$(form).find('button').click(function () { $form.find('button').click(function () {
todo(); todo();
}); });
$(form).find('input').keydown(function (e) { $form.find('input').keydown(function (e) {
// Save on Enter // Save on Enter
if (e.which === 13) { if (e.which === 13) {
e.preventDefault(); e.preventDefault();
@ -579,11 +577,9 @@ define([
} }
}); });
$spinner.hide().appendTo($div);
$ok.hide().appendTo($div);
return $div; cb(form);
}; }, true);
// Security // Security
@ -1215,6 +1211,90 @@ define([
return $div; return $div;
}; };
var redrawTrimHistory = function (cb, $div) {
var spinner = UI.makeSpinner();
var button = h('button.btn.btn-danger-alt', {
disabled: 'disabled'
}, Messages.trimHistory_button || 'delete history... xxx'); // XXX
var currentSize = h('p', $(spinner.spinner).clone()[0]);
var content = h('div#cp-settings-trim-container', [
currentSize,
button,
spinner.ok,
spinner.spinner
]);
if (!privateData.isDriveOwned) {
var href = privateData.origin + privateData.pathname + '#' + 'account';
$(currentSize).html(Messages.trimHistory_needMigration || 'Need migration <a>Click</a>'); // XXX
$(currentSize).find('a').prop('href', href).click(function (e) {
e.preventDefault();
$('.cp-sidebarlayout-category[data-category="account"]').click();
});
return void cb(content);
}
Messages.trimHistory_currentSize = 'Size XXX: <b>{0}</b>'; // XXX
var $button = $(button);
var size;
var channels = [];
nThen(function (waitFor) {
APP.history.execCommand('GET_HISTORY_SIZE', {
account: true,
channels: []
}, waitFor(function (obj) {
if (obj && obj.error) {
waitFor.abort();
var error = h('div.alert.alert-danger', Messages.trimHistory_error || 'error'); // XXX
$(content).empty().append(error);
return;
}
channels = obj.channels;
size = Number(obj.size);
}));
}).nThen(function () {
if (!size || size < 1024) {
$(currentSize).html(Messages.trimHistory_noHistory || 'no history...'); // XXX
return;
}
$(currentSize).html(Messages._getKey('trimHistory_currentSize', [UIElements.prettySize(size)]));
$button.click(function () {
//UI.confirm(Messages.trimHistory_confirm, function (yes) {
UI.confirmButton(button, {
classes: 'btn-danger'
}, function (yes) {
if (!yes) { return; }
$button.remove();
spinner.spin();
APP.history.execCommand('TRIM_HISTORY', {
channels: channels
}, function (obj) {
if (obj && obj.error) {
var error = h('div.alert.alert-danger', Messages.trimHistory_error || 'error'); // XXX
$(content).empty().append(error);
return;
}
spinner.hide();
redrawTrimHistory(cb, $div);
});
});
}).prop('disabled', '');
});
$div.find('#cp-settings-trim-container').remove();
cb(content);
};
makeBlock('trim-history', function (cb, $div) {
if (!common.isLoggedIn()) { return; }
// XXX settings_trimHistoryTitle, settings_trimHistoryHint, trimHistory_button, trimHistory_error
// XXX trimHistory_success, trimHistory_confirm, trimHistory_noHistory
// XXX trimHistory_needMigration (clickable <a> tag (no attribute) to go to the "account" part of settings)
redrawTrimHistory(cb, $div);
}, true);
/*
create['drive-reset'] = function () { create['drive-reset'] = function () {
var $div = $('<div>', {'class': 'cp-settings-drive-reset cp-sidebarlayout-element'}); var $div = $('<div>', {'class': 'cp-settings-drive-reset cp-sidebarlayout-element'});
$('<label>').text(Messages.settings_resetNewTitle).appendTo($div); $('<label>').text(Messages.settings_resetNewTitle).appendTo($div);
@ -1238,6 +1318,7 @@ define([
return $div; return $div;
}; };
*/
// Cursor settings // Cursor settings
@ -1638,7 +1719,10 @@ define([
APP.$usage = $('<div>', {'class': 'usage'}).appendTo(APP.$leftside); APP.$usage = $('<div>', {'class': 'usage'}).appendTo(APP.$leftside);
var active = privateData.category || 'account'; var active = privateData.category || 'account';
Object.keys(categories).forEach(function (key) { Object.keys(categories).forEach(function (key) {
var $category = $('<div>', {'class': 'cp-sidebarlayout-category'}).appendTo($categories); var $category = $('<div>', {
'class': 'cp-sidebarlayout-category',
'data-category': key
}).appendTo($categories);
if (key === 'account') { $category.append($('<span>', {'class': 'fa fa-user-o'})); } if (key === 'account') { $category.append($('<span>', {'class': 'fa fa-user-o'})); }
if (key === 'drive') { $category.append($('<span>', {'class': 'fa fa-hdd-o'})); } if (key === 'drive') { $category.append($('<span>', {'class': 'fa fa-hdd-o'})); }
if (key === 'cursor') { $category.append($('<span>', {'class': 'fa fa-i-cursor' })); } if (key === 'cursor') { $category.append($('<span>', {'class': 'fa fa-i-cursor' })); }
@ -1697,6 +1781,7 @@ define([
}; };
APP.toolbar = Toolbar.create(configTb); APP.toolbar = Toolbar.create(configTb);
APP.toolbar.$rightside.hide(); APP.toolbar.$rightside.hide();
APP.history = common.makeUniversal('history');
// Content // Content
var $rightside = APP.$rightside; var $rightside = APP.$rightside;
@ -1715,6 +1800,7 @@ define([
categories[cat].forEach(addItem); categories[cat].forEach(addItem);
} }
// TODO RPC // TODO RPC
//obj.proxy.on('change', [], refresh); //obj.proxy.on('change', [], refresh);
//obj.proxy.on('remove', [], refresh); //obj.proxy.on('remove', [], refresh);

Loading…
Cancel
Save