Merge branch 'communities-trim' into merge-trim

pull/1/head
ansuz 5 years ago
commit 672725629c

@ -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) {
@ -89,6 +105,7 @@
white-space: normal;
font-weight: bold;
}
&.danger, &.btn-danger {
background-color: @colortheme_alertify-red;
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 {
background-color: @colortheme_alertify-green;
border-color: @colortheme_alertify-green-border;

@ -26,6 +26,42 @@
// Properties modal
.cp-app-prop {
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 {

@ -599,6 +599,7 @@ const handleGetHistory = function (Env, Server, seq, userId, parsed) {
var config = parsed[2];
var metadata = {};
var lastKnownHash;
var txid;
// clients can optionally pass a map of attributes
// 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])) {
lastKnownHash = config.lastKnownHash;
metadata = config.metadata || {};
txid = config.txid;
if (metadata.expire) {
metadata.expire = +metadata.expire * 1000 + (+new Date());
}
@ -655,11 +657,12 @@ const handleGetHistory = function (Env, Server, seq, userId, parsed) {
msgCount++;
// avoid sending the metadata message a second time
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);
}, (err) => {
if (err && err.code !== 'ENOENT') {
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)]);
return;
}
@ -707,7 +710,7 @@ const handleGetHistory = function (Env, Server, seq, userId, parsed) {
}
// 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)]);
});

@ -975,10 +975,11 @@ var trimChannel = function (env, channelName, hash, _cb) {
if (msgHash === hash) {
// everything from this point on should be retained
retain = true;
return void tempStream.write(msgObj.buff, function () {
return void tempStream.write(s_msg + '\n', function () {
readMore();
});
}
readMore();
};
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) {
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) {
var existing, tags;
NThen(function(waitFor) {
@ -704,6 +711,9 @@ define([
var $d = $('<div>');
if (!data) { return void cb(void 0, $d); }
var priv = common.getMetadataMgr().getPrivateData();
var edPublic = priv.edPublic;
if (data.href) {
$('<label>', {'for': 'cp-app-prop-link'}).text(Messages.editShare).appendTo($d);
$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())]));
}
var owned = false;
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...
var bytes = 0;
NThen(function (waitFor) {
var historyBytes;
var chan = [data.channel];
if (data.rtChannel) { chan.push(data.rtChannel); }
if (data.lastVersion) { chan.push(Hash.hrefToHexChannelId(data.lastVersion)); }
var history = common.makeUniversal('history');
var trimChannels = [];
NThen(function (waitFor) {
chan.forEach(function (c) {
common.getFileSize(c, waitFor(function (e, _bytes) {
if (e) {
@ -746,20 +773,88 @@ define([
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 () {
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]);
$d.append(h('div.cp-app-prop', [Messages.upload_size, h('br'), h('span.cp-app-prop-content', formatted)]));
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)
]));
return void cb(void 0, $d);
}
if (data.sharedFolder && false) {
$('<label>', {'for': 'cp-app-prop-channel'}).text('Channel ID').appendTo($d);
if (AppConfig.pinBugRecovery) { $d.append(h('p', AppConfig.pinBugRecovery)); }
$d.append(UI.dialog.selectable(data.channel, {
id: 'cp-app-prop-link',
}));
Messages.historyTrim_historySize = 'History: {0}'; // XXX
Messages.historyTrim_contentsSize = 'Contents: {0}'; // XXX
var p = Math.round((historyBytes / bytes) * 100);
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);
});

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

@ -17,6 +17,7 @@ define([
'/common/outer/profile.js',
'/common/outer/team.js',
'/common/outer/messenger.js',
'/common/outer/history.js',
'/common/outer/network-config.js',
'/customize/application_config.js',
@ -28,7 +29,7 @@ define([
'/bower_components/saferphore/index.js',
], function (Sortify, UserObject, ProxyManager, Migrate, Hash, Util, Constants, Feedback,
Realtime, Messaging, Pinpad,
SF, Cursor, OnlyOffice, Mailbox, Profile, Team, Messenger,
SF, Cursor, OnlyOffice, Mailbox, Profile, Team, Messenger, History,
NetConfig, AppConfig,
Crypto, ChainPad, CpNetflux, Listmap, nThen, Saferphore) {
@ -49,8 +50,6 @@ define([
var sendDriveEvent = function () {};
var registerProxyEvents = function () {};
var storeHash, storeChannel;
var store = window.CryptPad_AsyncStore = {
modules: {}
};
@ -159,13 +158,7 @@ define([
};
var getUserChannelList = function () {
// start with your userHash...
var userHash = storeHash;
if (!userHash) { return null; }
// No password for drive
var secret = Hash.getSecrets('drive', userHash);
var userChannel = secret.channel;
var userChannel = store.driveChannel;
if (!userChannel) { return null; }
// Get the list of pads' channel ID in your drive
@ -173,7 +166,7 @@ define([
// It now includes channels from shared folders
var list = store.manager.getChannelsList('pin');
// Get the avatar
// Get the avatar & profile
var profile = store.proxy.profile;
if (profile) {
var profileChan = profile.edit ? Hash.hrefToHexChannelId('/profile/#' + profile.edit, null) : null;
@ -182,6 +175,10 @@ define([
if (avatarChan) { list.push(avatarChan); }
}
if (store.proxy.todo) {
list.push(Hash.hrefToHexChannelId('/todo/#' + store.proxy.todo, null));
}
if (store.proxy.friends) {
var fList = Messaging.getFriendChannelsList(store.proxy);
list = list.concat(fList);
@ -314,7 +311,7 @@ define([
teamId = data.teamId;
}
if (channel === storeChannel && !force) {
if (channel === store.driveChannel && !force) {
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'}); }
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) {
if (e) { return void cb({error: e}); }
if (response && response.length && typeof(response[0]) === 'number') {
@ -713,11 +712,9 @@ define([
Store.deleteAccount = function (clientId, data, cb) {
var edPublic = store.proxy.edPublic;
// No password for drive
var secret = Hash.getSecrets('drive', storeHash);
Store.anonRpcMsg(clientId, {
msg: 'GET_METADATA',
data: secret.channel
data: store.driveChannel
}, function (data) {
var metadata = data[0];
// Owned drive
@ -737,7 +734,7 @@ define([
}).nThen(function (waitFor) {
// Delete Drive
Store.removeOwnedChannel(clientId, {
channel: secret.channel,
channel: store.driveChannel,
force: true
}, waitFor());
}).nThen(function () {
@ -753,7 +750,7 @@ define([
var toSign = {
intent: 'Please delete my account.'
};
toSign.drive = secret.channel;
toSign.drive = store.driveChannel;
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);
@ -1768,7 +1765,9 @@ define([
// 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"
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'}); }
store.anon_rpc.send('GET_METADATA', data.channel, function (err, obj) {
if (err) { return void cb({error: err}); }
@ -1850,7 +1849,9 @@ define([
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 hk = network.historyKeeper;
@ -1862,6 +1863,8 @@ define([
}
};
var txid = Math.floor(Math.random() * 1000000);
var msgs = [];
var completed = false;
var onMsg = function (msg, sender) {
@ -1870,6 +1873,8 @@ define([
var parsed = parse(msg);
if (!parsed) { return; }
if (parsed.txid && parsed.txid !== txid) { return; }
// Ignore the metadata message
if (parsed.validateKey && parsed.channel) { return; }
if (parsed.error && parsed.channel) {
@ -1890,9 +1895,20 @@ define([
return;
}
msg = parsed[4];
if (Array.isArray(parsed) && parsed[0] && parsed[0] !== txid) { return; }
// Keep only the history for our channel
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) {
msg = msg.replace(/cp\|(([A-Za-z0-9+\/=]+)\|)?/, '');
msgs.push(msg);
@ -1901,6 +1917,7 @@ define([
network.on('message', onMsg);
var cfg = {
txid: txid,
lastKnownHash: data.lastKnownHash
};
var msg = ['GET_HISTORY', data.channel, cfg];
@ -2071,6 +2088,7 @@ define([
store.mailbox.removeClient(clientId);
} catch (e) { console.error(e); }
Object.keys(store.modules).forEach(function (key) {
if (!store.modules[key].removeClient) { return; }
try {
store.modules[key].removeClient(clientId);
} catch (e) { console.error(e); }
@ -2332,6 +2350,7 @@ define([
store.messenger = store.modules['messenger'];
loadUniversal(Profile, 'profile', waitFor);
loadUniversal(Team, 'team', waitFor);
loadUniversal(History, 'history', waitFor);
cleanFriendRequests();
}).nThen(function () {
var requestLogin = function () {
@ -2427,13 +2446,12 @@ define([
var connect = function (clientId, data, cb) {
var hash = data.userHash || data.anonHash || Hash.createRandomHash('drive');
storeHash = hash;
if (!hash) {
return void cb({error: '[Store.init] Unable to find or create a drive hash. Aborting...'});
}
// No password for drive
var secret = Hash.getSecrets('drive', hash);
storeChannel = secret.channel;
store.driveChannel = secret.channel;
var listmapConfig = {
data: {},
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) {
if (typeof(channel) !== 'string' || channel.length !== 32) {
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 () {
if (queue.queue.length === 0) {
clearTimeout(queue.to);
@ -262,7 +255,7 @@ define([
// name
$('<td>').append($link).appendTo($tr);
// size
$('<td>').text(prettySize(estimate)).appendTo($tr);
$('<td>').text(UIElements.prettySize(estimate)).appendTo($tr);
// progress
$('<td>', {'class': 'cp-fileupload-table-progress'}).append($progressContainer).appendTo($tr);
// cancel

@ -21,6 +21,11 @@
max-width: 650px;
}
div.alert {
font-size: @colortheme_app-font-size;
padding: 10px;
}
#cp-export-container {
font-size: 16px;
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"] {
width: @sidebar_button-width;
flex: unset;

@ -48,12 +48,12 @@ define([
var categories = {
'account': [
'cp-settings-own-drive',
'cp-settings-info-block',
'cp-settings-displayname',
'cp-settings-language-selector',
'cp-settings-resettips',
'cp-settings-change-password',
'cp-settings-migrate',
'cp-settings-delete'
],
'security': [
@ -73,7 +73,8 @@ define([
'cp-settings-thumbnails',
'cp-settings-drive-backup',
'cp-settings-drive-import-local',
'cp-settings-drive-reset'
'cp-settings-trim-history'
//'cp-settings-drive-reset'
],
'cursor': [
'cp-settings-cursor-color',
@ -146,6 +147,11 @@ define([
hintFunction(safeKey).appendTo($div);
}
getter(function (content) {
if (content === false) {
$div.remove();
$div = undefined;
return;
}
$div.append(content);
}, $div);
return $div;
@ -520,19 +526,12 @@ define([
return $div;
};
create['migrate'] = function () {
if (privateData.isDriveOwned) { return; }
if (!common.isLoggedIn()) { return; }
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);
makeBlock('own-drive', function (cb, $div) {
if (privateData.isDriveOwned || !common.isLoggedIn()) {
return void cb(false);
}
var $ok = $('<span>', {'class': 'fa fa-check', title: Messages.saved});
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'});
$div.addClass('alert alert-warning');
var form = h('div', [
UI.passwordInput({
@ -541,13 +540,13 @@ define([
}, true),
h('button.btn.btn-primary', Messages.settings_ownDriveButton)
]);
$(form).appendTo($div);
var $form = $(form);
var spinner = UI.makeSpinner($form);
var todo = function () {
var password = $(form).find('#cp-settings-migrate-password').val();
var password = $form.find('#cp-settings-migrate-password').val();
if (!password) { return; }
$spinner.show();
spinner.spin();
UI.confirm(Messages.settings_ownDriveConfirm, function (yes) {
if (!yes) { return; }
var data = {
@ -561,16 +560,15 @@ define([
sframeChan.query('Q_CHANGE_USER_PASSWORD', data, function (err, obj) {
UI.removeLoadingScreen();
if (err || obj.error) { return UI.alert(Messages.settings_changePasswordError); }
$ok.show();
$spinner.hide();
spinner.done();
});
});
};
$(form).find('button').click(function () {
$form.find('button').click(function () {
todo();
});
$(form).find('input').keydown(function (e) {
$form.find('input').keydown(function (e) {
// Save on Enter
if (e.which === 13) {
e.preventDefault();
@ -579,11 +577,9 @@ define([
}
});
$spinner.hide().appendTo($div);
$ok.hide().appendTo($div);
return $div;
};
cb(form);
}, true);
// Security
@ -1215,6 +1211,90 @@ define([
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 () {
var $div = $('<div>', {'class': 'cp-settings-drive-reset cp-sidebarlayout-element'});
$('<label>').text(Messages.settings_resetNewTitle).appendTo($div);
@ -1238,6 +1318,7 @@ define([
return $div;
};
*/
// Cursor settings
@ -1638,7 +1719,10 @@ define([
APP.$usage = $('<div>', {'class': 'usage'}).appendTo(APP.$leftside);
var active = privateData.category || 'account';
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 === 'drive') { $category.append($('<span>', {'class': 'fa fa-hdd-o'})); }
if (key === 'cursor') { $category.append($('<span>', {'class': 'fa fa-i-cursor' })); }
@ -1697,6 +1781,7 @@ define([
};
APP.toolbar = Toolbar.create(configTb);
APP.toolbar.$rightside.hide();
APP.history = common.makeUniversal('history');
// Content
var $rightside = APP.$rightside;
@ -1715,6 +1800,7 @@ define([
categories[cat].forEach(addItem);
}
// TODO RPC
//obj.proxy.on('change', [], refresh);
//obj.proxy.on('remove', [], refresh);

Loading…
Cancel
Save