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

This commit is contained in:
ClemDee
2019-06-07 15:47:29 +02:00
70 changed files with 2677 additions and 692 deletions

View File

@@ -156,12 +156,14 @@ define([
]);
};
dialog.frame = function (content) {
dialog.frame = function (content, opt) {
opt = opt || {};
var cls = opt.wide ? '.wide' : '';
return $(h('div.alertify', {
tabindex: 1,
}, [
h('div.dialog', [
h('div', content),
h('div'+cls, content),
])
])).click(function (e) {
e.stopPropagation();
@@ -351,6 +353,9 @@ define([
var close = function (el) {
var $el = $(el).fadeOut(150, function () {
$el.detach();
if (opt.onClose) {
opt.onClose();
}
});
};
@@ -373,10 +378,10 @@ define([
if (opt.forefront) { $(frame).addClass('forefront'); }
return frame;
};
UI.openCustomModal = function (content) {
UI.openCustomModal = function (content, opt) {
var frame = dialog.frame([
content
]);
], opt);
$(frame).find('button[data-keys]').each(function (i, el) {
var keys = JSON.parse($(el).attr('data-keys'));
customListenForKeys(keys, function () {

View File

@@ -78,5 +78,27 @@ define([
});
};
Msg.updateMyData = function (store, curve) {
if (store.messenger) {
store.messenger.updateMyData();
}
var myData = createData(store.proxy);
var todo = function (friend) {
if (!friend || !friend.notifications) { return; }
myData.channel = friend.channel;
store.mailbox.sendTo('UPDATE_DATA', myData, {
channel: friend.notifications,
curvePublic: friend.curvePublic
}, function (obj) {
if (obj && obj.error) { console.error(obj); }
});
};
if (curve) {
var friend = getFriend(store.proxy, curve);
return void todo(friend);
}
eachFriend(store.proxy.friends || {}, todo);
};
return Msg;
});

View File

@@ -44,6 +44,7 @@ define([
profile: proxy.profile && proxy.profile.view,
edPublic: proxy.edPublic,
curvePublic: proxy.curvePublic,
notifications: Util.find(proxy, ['mailboxes', 'notifications', 'channel']),
avatar: proxy.profile && proxy.profile.avatar
};
};
@@ -611,6 +612,15 @@ define([
cb();
});
});
} else {
removeFromFriendList(curvePublic, function () {
delete channels[channel.id];
emit('UNFRIEND', {
curvePublic: curvePublic,
fromMe: true
});
cb();
});
}
channel.wc.bcast(cryptMsg).then(function () {}, function (err) {
console.error(err);

View File

@@ -205,7 +205,7 @@ define([
if (content === oldThumbnailState) { return; }
oldThumbnailState = content;
Thumb.fromDOM(opts, function (err, b64) {
Thumb.setPadThumbnail(common, opts.href, null, b64);
Thumb.setPadThumbnail(common, opts.type, null, b64);
});
};
var nafa = Util.notAgainForAnother(mkThumbnail, Thumb.UPDATE_INTERVAL);
@@ -243,11 +243,10 @@ define([
var getKey = function (type, channel) {
return 'thumbnail-' + type + '-' + channel;
};
Thumb.setPadThumbnail = function (common, href, channel, b64, cb) {
Thumb.setPadThumbnail = function (common, type, channel, b64, cb) {
cb = cb || function () {};
var parsed = Hash.parsePadUrl(href);
channel = channel || common.getMetadataMgr().getPrivateData().channel;
var k = getKey(parsed.type, channel);
var k = getKey(type, channel);
common.setThumbnail(k, b64, cb);
};
Thumb.displayThumbnail = function (common, href, channel, password, $container, cb) {
@@ -270,7 +269,7 @@ define([
if (!v) {
v = 'EMPTY';
}
Thumb.setPadThumbnail(common, href, hexFileName, v, function (err) {
Thumb.setPadThumbnail(common, parsed.type, hexFileName, v, function (err) {
if (!metadata.thumbnail) { return; }
addThumbnail(err, metadata.thumbnail, $container, cb);
});

View File

@@ -316,39 +316,209 @@ define([
});
};
var getFriendsList = function (config) {
var common = config.common;
var title = config.title;
var friends = config.friends;
var myName = common.getMetadataMgr().getUserData().name;
var order = [];
if (!friends) { return; }
var others = Object.keys(friends).map(function (curve, i) {
if (curve.length <= 40) { return; }
var data = friends[curve];
if (!data.notifications) { return; }
var avatar = h('span.cp-share-friend-avatar.cp-avatar');
UIElements.displayAvatar(common, $(avatar), data.avatar, data.displayName);
return h('div.cp-share-friend', {
'data-curve': data.curvePublic,
'data-name': data.displayName,
'data-order': i,
style: 'order:'+i+';'
},[
avatar,
h('span.cp-share-friend-name', data.displayName)
]);
}).filter(function (x) { return x; });
var smallCurves = Object.keys(friends).map(function (c) {
return friends[c].curvePublic.slice(0,8);
});
var noOthers = others.length === 0 ? '.cp-recent-only' : '';
var buttonSelect = h('button.cp-share-with-friends', Messages.share_selectAll);
var buttonDeselect = h('button.cp-share-with-friends', Messages.share_deselectAll);
var inputFilter = h('input', {
placeholder: Messages.share_filterFriend
});
var div = h('div.cp-share-friends.cp-share-column' + noOthers, [
h('label', Messages.share_linkFriends),
h('div.cp-share-grid-filter', [
inputFilter,
buttonSelect,
buttonDeselect
]),
]);
var $div = $(div);
// Fill with fake friends to have a uniform spacing (from the flexbox)
var addFake = function (els) {
$div.find('.cp-fake-friend').remove();
var n = (6 - els.length%6)%6;
for (var j = 0; j < n; j++) {
els.push(h('div.cp-share-friend.cp-fake-friend', {
style: 'order:9999999;'
}));
}
};
addFake(others);
// Hide friends when they are filtered using the text input
var redraw = function () {
var name = $(inputFilter).val().trim().replace(/"/g, '');
$div.find('.cp-share-friend').show();
if (!name) { return; }
$div.find('.cp-share-friend:not(.cp-selected):not([data-name*="'+name+'"])').hide();
};
$(inputFilter).on('keydown keyup change', redraw);
$(buttonSelect).click(function () {
$div.find('.cp-share-friend:not(.cp-selected):visible').addClass('cp-selected');
});
$(buttonDeselect).click(function () {
$div.find('.cp-share-friend.cp-selected').removeClass('cp-selected').each(function (i, el) {
var order = $(el).attr('data-order');
if (!order) { return; }
$(el).attr('style', 'order:'+order);
});
redraw();
});
// Replace "copy link" by "share with friends" if at least one friedn is selected
// Also create the "share with friends" button if it doesn't exist
var refreshButtons = function () {
var $nav = $div.parents('.alertify').find('nav');
if (!$nav.find('.cp-share-with-friends').length) {
var button = h('button.primary.cp-share-with-friends', {
'data-keys': '[13]'
}, Messages.share_withFriends);
$(button).click(function () {
var href = Hash.getRelativeHref($('#cp-share-link-preview').val());
var $friends = $div.find('.cp-share-friend.cp-selected');
$friends.each(function (i, el) {
var curve = $(el).attr('data-curve');
if (!curve || !friends[curve]) { return; }
var friend = friends[curve];
if (!friend.notifications || !friend.curvePublic) { return; }
common.mailbox.sendTo("SHARE_PAD", {
href: href,
name: myName,
title: title
}, {
channel: friend.notifications,
curvePublic: friend.curvePublic
});
});
UI.findCancelButton().click();
// Update the "recently shared with" array:
// Get the selected curves
var curves = $friends.toArray().map(function (el) {
return ($(el).attr('data-curve') || '').slice(0,8);
}).filter(function (x) { return x; });
// Prepend them to the "order" array
Array.prototype.unshift.apply(order, curves);
order = Util.deduplicateString(order);
// Make sure we don't have "old" friends and save
order = order.filter(function (curve) {
return smallCurves.indexOf(curve) !== -1;
});
common.setAttribute(['general', 'share-friends'], order);
});
$nav.append(button);
}
var friendMode = $div.find('.cp-share-friend.cp-selected').length;
if (friendMode) {
$nav.find('button.primary[data-keys]').hide();
$nav.find('button.cp-share-with-friends').show();
} else {
$nav.find('button.primary[data-keys]').show();
$nav.find('button.cp-share-with-friends').hide();
}
};
common.getAttribute(['general', 'share-friends'], function (err, val) {
order = val || [];
// Sort friends by "recently shared with"
others.sort(function (a, b) {
var ca = ($(a).attr('data-curve') || '').slice(0,8);
var cb = ($(b).attr('data-curve') || '').slice(0,8);
if (!ca && !cb) { return 0; }
if (!ca) { return 1; }
if (!cb) { return -1; }
var ia = order.indexOf(ca);
var ib = order.indexOf(cb);
if (ia === -1 && ib === -1) { return 0; }
if (ia === -1) { return 1; }
if (ib === -1) { return -1; }
return ia - ib;
});
// Reorder the friend icons
others.forEach(function (el, i) {
if ($(el).is('.cp-fake-friend')) { return; }
$(el).attr('data-order', i).css('order', i);
});
// Display them
$div.append(h('div.cp-share-grid', others));
$div.find('.cp-share-friend').click(function () {
var sel = $(this).hasClass('cp-selected');
if (!sel) {
$(this).addClass('cp-selected');
} else {
var order = $(this).attr('data-order');
order = order ? 'order:'+order : '';
$(this).removeClass('cp-selected').attr('style', order);
}
refreshButtons();
});
});
return div;
};
UIElements.createShareModal = function (config) {
var origin = config.origin;
var pathname = config.pathname;
var hashes = config.hashes;
var common = config.common;
if (!hashes) { return; }
// Share link tab
var link = h('div.cp-share-modal', [
h('label', Messages.share_linkAccess),
h('br'),
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,
}),
h('label', { 'for': 'cp-share-editable-true' }, Messages.share_linkEdit),
h('input#cp-share-editable-false.cp-share-editable-value', {
type: 'radio',
name: 'cp-share-editable',
value: 0
}),
h('label', { 'for': 'cp-share-editable-false' }, Messages.share_linkView),*/
h('br'),
h('label', Messages.share_linkOptions),
h('br'),
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'),
UI.dialog.selectable('', { id: 'cp-share-link-preview', tabindex: 1 })
var hasFriends = Object.keys(config.friends || {}).length !== 0;
var friendsList = hasFriends ? getFriendsList(config) : undefined;
var friendsUIClass = hasFriends ? '.cp-share-columns' : '';
var link = h('div.cp-share-modal' + friendsUIClass, [
h('div.cp-share-column', [
hasFriends ? h('p', Messages.share_description) : undefined,
h('label', Messages.share_linkAccess),
h('br'),
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('br'),
h('label', Messages.share_linkOptions),
h('br'),
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'),
UI.dialog.selectable('', { id: 'cp-share-link-preview', tabindex: 1 }),
]),
friendsList
]);
if (!hashes.editHash) {
$(link).find('#cp-share-editable-false').attr('checked', true);
@@ -380,6 +550,7 @@ define([
$(link).find('#cp-share-link-preview').val(getLinkValue());
});
var linkButtons = [{
className: 'cancel',
name: Messages.cancel,
onClick: function () {},
keys: [27]
@@ -403,7 +574,10 @@ define([
},
keys: [[13, 'ctrl']]
}];
var frameLink = UI.dialog.customModal(link, {buttons: linkButtons});
var frameLink = UI.dialog.customModal(link, {
buttons: linkButtons,
onClose: config.onClose,
});
// Embed tab
var getEmbedValue = function () {
@@ -420,6 +594,7 @@ define([
UI.dialog.selectable(getEmbedValue())
]);
var embedButtons = [{
className: 'cancel',
name: Messages.cancel,
onClick: function () {},
keys: [27]
@@ -433,7 +608,10 @@ define([
},
keys: [13]
}];
var frameEmbed = UI.dialog.customModal(embed, { buttons: embedButtons});
var frameEmbed = UI.dialog.customModal(embed, {
buttons: embedButtons,
onClose: config.onClose,
});
// Create modal
var tabs = [{
@@ -464,7 +642,9 @@ define([
$(link).find('#cp-share-link-preview').val(getLinkValue(val));
});
common.getMetadataMgr().onChange(function () {
hashes = common.getMetadataMgr().getPrivateData().availableHashes;
// "hashes" is only available is the secure "share" app
hashes = common.getMetadataMgr().getPrivateData().hashes;
if (!hashes) { return; }
$(link).find('#cp-share-link-preview').val(getLinkValue());
});
return tabs;
@@ -481,12 +661,20 @@ define([
// Share link tab
var link = h('div.cp-share-modal', [
UI.dialog.selectable('', { id: 'cp-share-link-preview' })
var hasFriends = Object.keys(config.friends || {}).length !== 0;
var friendsList = hasFriends ? getFriendsList(config) : undefined;
var friendsUIClass = hasFriends ? '.cp-share-columns' : '';
var link = h('div.cp-share-modal' + friendsUIClass, [
h('div.cp-share-column', [
hasFriends ? h('p', Messages.share_description) : undefined,
UI.dialog.selectable('', { id: 'cp-share-link-preview' }),
]),
friendsList
]);
var getLinkValue = function () { return url; };
$(link).find('#cp-share-link-preview').val(getLinkValue());
var linkButtons = [{
className: 'cancel',
name: Messages.cancel,
onClick: function () {},
keys: [27]
@@ -513,6 +701,7 @@ define([
UI.dialog.selectable(common.getMediatagFromHref(fileData)),
]);
var embedButtons = [{
className: 'cancel',
name: Messages.cancel,
onClick: function () {},
keys: [27]
@@ -554,12 +743,20 @@ define([
var url = origin + pathname + '#' + hashes.editHash;
// Share link tab
var link = h('div.cp-share-modal', [
h('label', Messages.sharedFolders_share),
h('br'),
UI.dialog.selectable(url, { id: 'cp-share-link-preview', tabindex: 1 })
var hasFriends = Object.keys(config.friends || {}).length !== 0;
var friendsList = hasFriends ? getFriendsList(config) : undefined;
var friendsUIClass = hasFriends ? '.cp-share-columns' : '';
var link = h('div.cp-share-modal' + friendsUIClass, [
h('div.cp-share-column', [
h('label', Messages.sharedFolders_share),
h('br'),
hasFriends ? h('p', Messages.share_description) : undefined,
UI.dialog.selectable(url, { id: 'cp-share-link-preview', tabindex: 1 })
]),
friendsList
]);
var linkButtons = [{
className: 'cancel',
name: Messages.cancel,
onClick: function () {},
keys: [27]
@@ -791,16 +988,23 @@ define([
button = $('<span>');
break;
}
var active = $(".cp-toolbar-history:visible").length !== 0;
button = $('<button>', {
title: Messages.historyButton,
title: active ? Messages.history_closeTitle : Messages.historyButton,
'class': "fa fa-history cp-toolbar-icon-history",
}).append($('<span>', {'class': 'cp-toolbar-drawer-element'}).text(Messages.historyText));
button.toggleClass("active", active);
if (data.histConfig) {
button
.click(common.prepareFeedback(type))
.on('click', function () {
common.getHistory(data.histConfig);
});
if (active) {
button.click(function () { $(".cp-toolbar-history-close").trigger("click"); });
}
else {
button
.click(common.prepareFeedback(type))
.on('click', function () {
common.getHistory(data.histConfig);
});
}
}
break;
case 'more':

View File

@@ -1,15 +1,19 @@
define([
'/customize/application_config.js',
'/common/common-feedback.js',
'/common/common-hash.js',
'/common/common-util.js',
'/common/common-messenger.js',
'/common/outer/mailbox.js',
'/bower_components/nthen/index.js',
], function (Feedback, Hash, Util, nThen) {
'/bower_components/chainpad-crypto/crypto.js',
], function (AppConfig, Feedback, Hash, Util, Messenger, Mailbox, nThen, Crypto) {
// Start migration check
// Versions:
// 1: migrate pad attributes
// 2: migrate indent settings (codemirror)
return function (userObject, cb, progress) {
return function (userObject, cb, progress, store) {
var version = userObject.version || 0;
nThen(function () {
@@ -186,6 +190,135 @@ define([
Feedback.send('Migrate-8', true);
userObject.version = version = 8;
}
}).nThen(function () {
if (!AppConfig.migrateFriends) { return; } // XXX
// Migration 9: send our mailbox channel to existing friends
var migrateFriends = function () {
var network = store.network;
var channels = {};
var ctx = {
store: store
};
var myData = Messenger.createData(userObject);
var close = function (chan) {
var channel = channels[chan];
if (!channel) { return; }
try {
channel.wc.leave();
} catch (e) {}
delete channels[chan];
};
var onDirectMessage = function (msg, sender) {
if (sender !== network.historyKeeper) { return; }
var parsed = JSON.parse(msg);
// Metadata msg? we don't care
if ((parsed.validateKey || parsed.owners) && parsed.channel) { return; }
// End of history message, "onReady"
if (parsed.channel && channels[parsed.channel]) {
// History cleared while we were offline
// ==> we asked for an invalid last known hash
if (parsed.error && parsed.error === "EINVAL") {
var histMsg = ['GET_HISTORY', parsed.channel, {}];
network.sendto(network.historyKeeper, JSON.stringify(histMsg))
.then(function () {}, function () {});
return;
}
// End of history
if (parsed.state && parsed.state === 1) {
// Channel is ready and we didn't receive their mailbox channel: send our channel
myData.channel = parsed.channel;
var updateMsg = ['UPDATE', myData.curvePublic, +new Date(), myData];
var cryptMsg = channels[parsed.channel].encrypt(JSON.stringify(updateMsg));
channels[parsed.channel].wc.bcast(cryptMsg).then(function () {}, function (err) {
console.error("Can't migrate this friend", channels[parsed.channel].friend, err);
});
close(parsed.channel);
return;
}
} else if (parsed.channel) {
return;
}
// History message: we only care about "UPDATE" messages
var chan = parsed[3];
if (!chan || !channels[chan]) { return; }
var channel = channels[chan];
var msgIn = channel.decrypt(parsed[4]);
var parsedMsg = JSON.parse(msgIn);
if (parsedMsg[0] === 'UPDATE') {
if (parsedMsg[1] === myData.curvePublic) { return; }
var data = parsedMsg[3];
// If it doesn't contain the mailbox channel, ignore the message
if (!data.notifications) { return; }
// Otherwise we know their channel, we can send them our own
channel.friend.notifications = data.notifications;
myData.channel = chan;
Mailbox.sendTo(ctx, 'UPDATE_DATA', myData, {
channel: data.notifications,
curvePublic: data.curvePublic
}, function (obj) {
if (obj && obj.error) { return void console.error(obj); }
console.log('friend migrated', channel.friend);
});
close(chan);
}
};
network.on('message', function(msg, sender) {
try {
onDirectMessage(msg, sender);
} catch (e) {
console.error(e);
}
});
var friends = userObject.friends || {};
Object.keys(friends).forEach(function (curve) {
if (curve.length !== 44) { return; }
var friend = friends[curve];
// Check if it is already a "new" friend
if (friend.notifications) { return; }
/** Old friend:
* 1. Open the messenger channel
* 2. Check if they sent us their mailbox channel
* 3.a. Yes ==> sent them a mail containing our mailbox channel
* 3.b. No ==> post our mailbox data to the messenger channel
*/
network.join(friend.channel).then(function (wc) {
var keys = Crypto.Curve.deriveKeys(friend.curvePublic, userObject.curvePrivate);
var encryptor = Crypto.Curve.createEncryptor(keys);
channels[friend.channel] = {
wc: wc,
friend: friend,
decrypt: encryptor.decrypt,
encrypt: encryptor.encrypt
};
var cfg = {
lastKnownHash: friend.lastKnownHash
};
var msg = ['GET_HISTORY', friend.channel, cfg];
network.sendto(network.historyKeeper, JSON.stringify(msg))
.then(function () {}, function (err) {
console.error("Can't migrate this friend", friend, err);
});
}, function (err) {
console.error("Can't migrate this friend", friend, err);
});
});
};
if (version < 9) {
migrateFriends();
Feedback.send('Migrate-9', true);
userObject.version = version = 9;
}
/*}).nThen(function (waitFor) {
// Test progress bar in the loading screen
var i = 0;
@@ -197,7 +330,7 @@ define([
}, 500);
progress(0, 0);*/
}).nThen(function () {
cb();
setTimeout(cb);
});
};
});

View File

@@ -1,12 +1,15 @@
define([
'jquery',
'/common/hyperscript.js',
'/common/common-hash.js',
'/common/common-ui-elements.js',
'/customize/messages.js',
], function ($, h, UIElements, Messages) {
], function ($, h, Hash, UIElements, Messages) {
var handlers = {};
// Friend request
handlers['FRIEND_REQUEST'] = function (common, data, el) {
var content = data.content;
var msg = content.msg;
@@ -17,9 +20,9 @@ define([
common.addFriendRequest(data);
// Display the notification
$(el).find('.cp-notification-content').addClass("cp-clickable");
$(el).find('.cp-notification-content p')
.html(Messages._getKey('friendRequest_notification', [msg.content.displayName || Messages.anonymous]))
.html(Messages._getKey('friendRequest_notification', [msg.content.displayName || Messages.anonymous]));
$(el).find('.cp-notification-content').addClass("cp-clickable")
.click(function () {
UIElements.displayFriendRequestModal(common, data);
});
@@ -41,6 +44,24 @@ define([
$(el).find('.cp-notification-dismiss').css('display', 'flex');
};
// Share pad
handlers['SHARE_PAD'] = function (common, data, el) {
var content = data.content;
var msg = content.msg;
var type = Hash.parsePadUrl(msg.content.href).type;
var key = type === 'drive' ? 'notification_folderShared' :
(type === 'file' ? 'notification_fileShared' :
'notification_padShared');
$(el).find('.cp-notification-content p')
.html(Messages._getKey(key, [msg.content.name || Messages.anonymous, msg.content.title]));
$(el).find('.cp-notification-content').addClass("cp-clickable")
.click(function () {
common.openURL(msg.content.href);
});
$(el).find('.cp-notification-dismiss').css('display', 'flex');
};
return {
add: function (common, data, el) {
var type = data.content.msg.type;

View File

@@ -66,7 +66,7 @@ define([
}
broadcast([clientId], "UPDATE_METADATA");
if (Array.isArray(path) && path[0] === 'profile' && store.messenger) {
store.messenger.updateMyData();
Messaging.updateMyData(store);
}
onSync(cb);
};
@@ -644,7 +644,7 @@ define([
}
store.proxy[Constants.displayNameKey] = value;
broadcast([clientId], "UPDATE_METADATA");
if (store.messenger) { store.messenger.updateMyData(); }
Messaging.updateMyData(store);
onSync(cb);
};
@@ -1644,14 +1644,16 @@ define([
});
userObject.migrate(waitFor());
}).nThen(function (waitFor) {
Store.initAnonRpc(null, null, waitFor());
Store.initRpc(null, null, waitFor());
}).nThen(function (waitFor) {
loadMailbox(waitFor);
Migrate(proxy, waitFor(), function (version, progress) {
postMessage(clientId, 'LOADING_DRIVE', {
state: (2 + (version / 10)),
progress: progress
});
});
Store.initAnonRpc(null, null, waitFor());
Store.initRpc(null, null, waitFor());
}).nThen(function (waitFor) {
postMessage(clientId, 'LOADING_DRIVE', {
state: 3
@@ -1661,7 +1663,6 @@ define([
loadMessenger();
loadCursor();
loadOnlyOffice();
loadMailbox(waitFor);
loadUniversal(Profile, 'profile', waitFor);
cleanFriendRequests();
}).nThen(function () {
@@ -1816,8 +1817,8 @@ define([
// Ping clients regularly to make sure one tab was not closed without sending a removeClient()
// command. This allow us to avoid phantom viewers in pads.
var PING_INTERVAL = 30000;
var MAX_PING = 5000;
var PING_INTERVAL = 120000;
var MAX_PING = 30000;
var MAX_FAILED_PING = 2;
setInterval(function () {

View File

@@ -144,8 +144,30 @@ define([
cb(true);
};
handlers['UPDATE_DATA'] = function (ctx, box, data, cb) {
var msg = data.msg;
var curve = msg.author;
var friend = ctx.store.proxy.friends && ctx.store.proxy.friends[curve];
if (!friend || typeof msg.content !== "object") { return void cb(true); }
Object.keys(msg.content).forEach(function (key) {
friend[key] = msg.content[key];
});
ctx.updateMetadata();
cb(true);
};
return {
add: function (ctx, box, data, cb) {
/**
* data = {
msg: {
type: 'STRING',
author: 'curvePublicString',
content: {} (depend on the "type")
},
hash: 'string'
}
*/
if (!data.msg) { return void cb(true); }
var type = data.msg.type;

View File

@@ -65,7 +65,7 @@ proxy.mailboxes = {
};
// Send a message to someone else
var sendTo = function (ctx, type, msg, user, cb) {
var sendTo = Mailbox.sendTo = function (ctx, type, msg, user, cb) {
if (!Crypto.Mailbox) {
return void cb({error: "chainpad-crypto is outdated and doesn't support mailboxes."});
}

View File

@@ -357,16 +357,14 @@ define([
UI.removeLoadingScreen(emitResize);
var privateDat = cpNfInner.metadataMgr.getPrivateData();
var hash = privateDat.availableHashes.editHash ||
privateDat.availableHashes.viewHash;
var href = privateDat.pathname + '#' + hash;
var type = privateDat.app;
if (AppConfig.textAnalyzer && textContentGetter) {
AppConfig.textAnalyzer(textContentGetter, privateDat.channel);
}
if (options.thumbnail && privateDat.thumbnails) {
if (hash) {
options.thumbnail.href = href;
if (type) {
options.thumbnail.type = type;
options.thumbnail.getContent = function () {
if (!cpNfInner.chainpad) { return; }
return cpNfInner.chainpad.getUserDoc();

View File

@@ -231,7 +231,20 @@ define([
};
var $block = exp.$language = UIElements.createDropdown(dropdownConfig);
$block.find('button').attr('title', Messages.languageButtonTitle);
$block.find('a').click(function () {
var isHovering = false;
var $aLanguages = $block.find('a');
$aLanguages.mouseenter(function () {
isHovering = true;
setMode($(this).attr('data-value'));
});
$aLanguages.mouseleave(function () {
if (isHovering) {
setMode($block.find(".cp-dropdown-element-active").attr('data-value'));
}
});
$aLanguages.click(function () {
isHovering = false;
setMode($(this).attr('data-value'), onModeChanged);
onLocal();
});
@@ -272,12 +285,26 @@ define([
setTheme(lastTheme, $block);
$block.find('a').click(function () {
var isHovering = false;
var $aThemes = $block.find('a');
$aThemes.mouseenter(function () {
isHovering = true;
var theme = $(this).attr('data-value');
setTheme(theme, $block);
});
$aThemes.mouseleave(function () {
if (isHovering) {
setTheme(lastTheme, $block);
Common.setAttribute(themeKey, lastTheme);
}
});
$aThemes.click(function () {
isHovering = false;
var theme = $(this).attr('data-value');
setTheme(theme, $block);
Common.setAttribute(themeKey, theme);
});
if ($drawer) { $drawer.append($block); }
if (cb) { cb(); }
};

View File

@@ -5,7 +5,8 @@ define([
'/common/common-ui-elements.js',
'/common/notifications.js',
'/common/hyperscript.js',
], function ($, Util, UI, UIElements, Notifications, h) {
'/customize/messages.js',
], function ($, Util, UI, UIElements, Notifications, h, Messages) {
var Mailbox = {};
Mailbox.create = function (Common) {
@@ -48,7 +49,10 @@ define([
};
var createElement = function (data) {
var notif;
var dismiss = h('span.fa.fa-times');
var dismissIcon = h('span.fa.fa-times');
var dismiss = h('div.cp-notification-dismiss', {
title: Messages.notifications_dismiss
}, dismissIcon)
dismiss.addEventListener('click', function (e) {
e.preventDefault();
e.stopPropagation();
@@ -65,7 +69,7 @@ define([
'data-hash': data.content.hash
}, [
h('div.cp-notification-content', h('p', formatData(data))),
h('div.cp-notification-dismiss', dismiss)
dismiss
]);
return notif;
};
@@ -132,9 +136,14 @@ define([
});
};
var subscribed = false;
// Get all existing notifications + the new ones when they come
mailbox.subscribe = function (cfg) {
if (!subscribed) {
execCommand('SUBSCRIBE', null, function () {});
subscribed = true;
}
if (typeof(cfg.onViewed) === "function") {
onViewedHandlers.push(cfg.onViewed);
}
@@ -166,10 +175,6 @@ define([
}
});
execCommand('SUBSCRIBE', null, function () {
//console.log('subscribed');
});
return mailbox;
};

View File

@@ -19,6 +19,7 @@ define([
var SFrameChannel;
var sframeChan;
var FilePicker;
var Share;
var Messaging;
var Notifier;
var Utils = {
@@ -38,6 +39,7 @@ define([
'/common/cryptget.js',
'/common/outer/worker-channel.js',
'/filepicker/main.js',
'/share/main.js',
'/common/common-messaging.js',
'/common/common-notifier.js',
'/common/common-hash.js',
@@ -49,7 +51,7 @@ define([
'/customize/application_config.js',
'/common/test.js',
], waitFor(function (_CpNfOuter, _Cryptpad, _Crypto, _Cryptget, _SFrameChannel,
_FilePicker, _Messaging, _Notifier, _Hash, _Util, _Realtime,
_FilePicker, _Share, _Messaging, _Notifier, _Hash, _Util, _Realtime,
_Constants, _Feedback, _LocalStore, _AppConfig, _Test) {
CpNfOuter = _CpNfOuter;
Cryptpad = _Cryptpad;
@@ -57,6 +59,7 @@ define([
Cryptget = _Cryptget;
SFrameChannel = _SFrameChannel;
FilePicker = _FilePicker;
Share = _Share;
Messaging = _Messaging;
Notifier = _Notifier;
Utils.Hash = _Hash;
@@ -269,9 +272,6 @@ define([
sessionStorage[Utils.Constants.displayPadCreationScreen];
delete sessionStorage[Utils.Constants.displayPadCreationScreen];
var updateMeta = function () {
// TODO availableHashes in privateData may need updates once we have
// a better privileges workflow
//console.log('EV_METADATA_UPDATE');
var metaObj, isTemplate;
nThen(function (waitFor) {
@@ -290,12 +290,12 @@ define([
type: cfg.type || parsed.type
};
var additionalPriv = {
app: parsed.type,
accountName: Utils.LocalStore.getAccountName(),
origin: window.location.origin,
pathname: window.location.pathname,
fileHost: ApiConfig.fileHost,
readOnly: readOnly,
availableHashes: hashes,
isTemplate: isTemplate,
feedbackAllowed: Utils.Feedback.state,
isPresent: parsed.hashData && parsed.hashData.present,
@@ -326,6 +326,10 @@ define([
additionalPriv.registeredOnly = true;
}
if (['debug', 'profile'].indexOf(parsed.type) !== -1) {
additionalPriv.hashes = hashes;
}
for (var k in additionalPriv) { metaObj.priv[k] = additionalPriv[k]; }
if (cfg.addData) {
@@ -380,6 +384,27 @@ define([
});
});
sframeChan.on('Q_GET_ATTRIBUTE', function (data, cb) {
Cryptpad.getAttribute(data.key, function (e, data) {
cb({
error: e,
data: data
});
});
});
sframeChan.on('Q_SET_ATTRIBUTE', function (data, cb) {
Cryptpad.setAttribute(data.key, data.value, function (e) {
cb({error:e});
});
});
Cryptpad.mailbox.onEvent.reg(function (data) {
sframeChan.event('EV_MAILBOX_EVENT', data);
});
sframeChan.on('Q_MAILBOX_COMMAND', function (data, cb) {
Cryptpad.mailbox.execCommand(data, cb);
});
};
addCommonRpc(sframeChan);
@@ -589,20 +614,6 @@ define([
}, href);
});
sframeChan.on('Q_GET_ATTRIBUTE', function (data, cb) {
Cryptpad.getAttribute(data.key, function (e, data) {
cb({
error: e,
data: data
});
});
});
sframeChan.on('Q_SET_ATTRIBUTE', function (data, cb) {
Cryptpad.setAttribute(data.key, data.value, function (e) {
cb({error:e});
});
});
sframeChan.on('Q_DRIVE_GETDELETED', function (data, cb) {
Cryptpad.getDeletedPads(data, function (err, obj) {
if (err) { return void console.error(err); }
@@ -711,6 +722,45 @@ define([
initFilePicker(data);
});
// Share modal
var ShareModal = {};
var initShareModal = function (cfg) {
cfg.hashes = hashes;
cfg.password = password;
// cfg.hidden means pre-loading the filepicker while keeping it hidden.
// if cfg.hidden is true and the iframe already exists, do nothing
if (!ShareModal.$iframe) {
var config = {};
config.onShareAction = function (data) {
sframeChan.event('EV_SHARE_ACTION', data);
};
config.onClose = function () {
ShareModal.$iframe.hide();
};
config.data = cfg;
config.addCommonRpc = addCommonRpc;
config.modules = {
Cryptpad: Cryptpad,
SFrameChannel: SFrameChannel,
Utils: Utils
};
ShareModal.$iframe = $('<iframe>', {id: 'sbox-share-iframe'}).appendTo($('body'));
ShareModal.modal = Share.create(config);
} else if (!cfg.hidden) {
ShareModal.modal.refresh(cfg, function () {
ShareModal.$iframe.show();
});
}
if (cfg.hidden) {
ShareModal.$iframe.hide();
return;
}
ShareModal.$iframe.focus();
};
sframeChan.on('EV_SHARE_OPEN', function (data) {
initShareModal(data || {});
});
sframeChan.on('Q_TEMPLATE_USE', function (data, cb) {
Cryptpad.useTemplate(data, Cryptget, cb);
});
@@ -879,13 +929,6 @@ define([
Cryptpad.universal.execCommand(data, cb);
});
Cryptpad.mailbox.onEvent.reg(function (data) {
sframeChan.event('EV_MAILBOX_EVENT', data);
});
sframeChan.on('Q_MAILBOX_COMMAND', function (data, cb) {
Cryptpad.mailbox.execCommand(data, cb);
});
Cryptpad.onTimeoutEvent.reg(function () {
sframeChan.event('EV_WORKER_TIMEOUT');
});

View File

@@ -121,13 +121,9 @@ define([
return '<script src="' + origin + '/common/media-tag-nacl.min.js"></script>';
};
funcs.getMediatagFromHref = function (obj) {
if (!obj || !obj.hash) { return; }
var data = ctx.metadataMgr.getPrivateData();
var secret;
if (obj) {
secret = Hash.getSecrets('file', obj.hash, obj.password);
} else {
secret = Hash.getSecrets('file', data.availableHashes.fileHash, data.password);
}
var secret = Hash.getSecrets('file', obj.hash, obj.password);
if (secret.keys && secret.channel) {
var key = Hash.encodeBase64(secret.keys && secret.keys.cryptKey);
var hexFileName = secret.channel;
@@ -391,12 +387,6 @@ define([
}
};
funcs.isStrongestStored = function () {
var data = ctx.metadataMgr.getPrivateData();
if (data.availableHashes.fileHash) { return true; }
return !data.readOnly || !data.availableHashes.editHash;
};
funcs.setDisplayName = function (name, cb) {
cb = cb || $.noop;
ctx.sframeChan.query('Q_SETTINGS_SET_DISPLAY_NAME', name, cb);
@@ -432,6 +422,19 @@ define([
return JSON.parse(JSON.stringify(friendRequests));
};
funcs.getFriends = function () {
var priv = ctx.metadataMgr.getPrivateData();
var friends = priv.friends;
var goodFriends = {};
Object.keys(friends).forEach(function (curve) {
if (curve.length !== 44) { return; }
var data = friends[curve];
if (!data.notifications) { return; }
goodFriends[curve] = friends[curve];
});
return goodFriends;
};
// Feedback
funcs.prepareFeedback = function (key) {
if (typeof(key) !== 'string') { return $.noop; }

View File

@@ -304,8 +304,10 @@ MessengerUI, Messages) {
} else if (Common.isLoggedIn() && data.curvePublic && !friends[data.curvePublic]
&& !priv.readOnly) {
if (pendingFriends[data.curvePublic] && pendingFriends[data.curvePublic] > friendTo) {
$('<span>', {'class': 'cp-toolbar-userlist-friend'}).text(Messages.userlist_pending)
.appendTo($rightCol);
$('<button>', {
'class': 'fa fa-hourglass-half cp-toolbar-userlist-button',
'title': Messages.profile_friendRequestSent
}).appendTo($nameSpan);
} else if (friendRequests[data.curvePublic]) {
$('<button>', {
'class': 'fa fa-bell cp-toolbar-userlist-button',
@@ -491,7 +493,7 @@ MessengerUI, Messages) {
if ($messagebox.length) {
$messagebox.scrollTop($messagebox[0].scrollHeight);
}
$button.addClass('cp-toolbar-button-active');
config.$contentContainer.addClass('cp-chat-visible');
$button.removeClass('cp-toolbar-notification');
@@ -523,23 +525,18 @@ MessengerUI, Messages) {
if (!config.metadataMgr) {
throw new Error("You must provide a `metadataMgr` to display the userlist");
}
var metadataMgr = config.metadataMgr;
var origin = config.metadataMgr.getPrivateData().origin;
var pathname = config.metadataMgr.getPrivateData().pathname;
var hashes = metadataMgr.getPrivateData().availableHashes;
var $shareBlock = $('<button>', {
'class': 'fa fa-shhare-alt cp-toolbar-share-button',
title: Messages.shareButton
});
var modal = UIElements.createShareModal({
origin: origin,
pathname: pathname,
hashes: hashes,
common: Common
Common.getSframeChannel().event('EV_SHARE_OPEN', {
hidden: true
});
$shareBlock.click(function () {
UI.openCustomModal(UI.dialog.tabs(modal));
Common.getSframeChannel().event('EV_SHARE_OPEN', {
title: Common.getMetadataMgr().getMetadata().title
});
});
toolbar.$leftside.append($shareBlock);
@@ -552,23 +549,19 @@ MessengerUI, Messages) {
if (!config.metadataMgr) {
throw new Error("You must provide a `metadataMgr` to display the userlist");
}
var metadataMgr = config.metadataMgr;
var origin = config.metadataMgr.getPrivateData().origin;
var pathname = config.metadataMgr.getPrivateData().pathname;
var hashes = metadataMgr.getPrivateData().availableHashes;
var $shareBlock = $('<button>', {
'class': 'fa fa-shhare-alt cp-toolbar-share-button',
title: Messages.shareButton
});
var modal = UIElements.createFileShareModal({
origin: origin,
pathname: pathname,
hashes: hashes,
common: Common
Common.getSframeChannel().event('EV_SHARE_OPEN', {
hidden: true,
file: true
});
$shareBlock.click(function () {
UI.openCustomModal(UI.dialog.tabs(modal));
Common.getSframeChannel().event('EV_SHARE_OPEN', {
file: true
});
});
toolbar.$leftside.append($shareBlock);

View File

@@ -0,0 +1,2 @@
{
}

View File

@@ -389,7 +389,7 @@
"profile_inviteButtonTitle": "Crear un enlace de invitación para este usuario.",
"profile_inviteExplanation": "Hacer clic en <strong>OK</strong> creará un enlace de mensaje seguro que <em>sólo {0} podrá ver.</em><br><br>El enlace será copiado a tu portapapeles y puede ser compartido públicamente.",
"profile_viewMyProfile": "Ver mi perfil",
"userlist_addAsFriendTitle": "Agregar \"{0}\" como contacto",
"userlist_addAsFriendTitle": "Enviar \"{0}\" una solicitud de amistad",
"userlist_thisIsYou": "Tú mismo (\"{0}\")",
"contacts_title": "Contactos",
"contacts_addError": "Error al agregar este contacto a la lista",
@@ -561,5 +561,34 @@
"fc_remove_sharedfolder": "Eliminar",
"fc_hashtag": "Etiquetas",
"register_passwordTooShort": "La contraseña debe tener por los menos {0} caracteres de largo.",
"useTemplateCancel": "Recomenzar (Esc)"
"useTemplateCancel": "Recomenzar (Esc)",
"register_whyRegister": "Por qué conectarse?",
"settings_cat_cursor": "Cursor",
"settings_cat_pad": "Texto enriquecido",
"settings_cat_creation": "Nueva hoja",
"settings_cat_subscription": "Suscripción",
"settings_backupHint": "Respalde o recupere todo sus contenido ne CryptDrive. No tendrán el contenido de sus archivos, solo las llaves para acceder a ellos.",
"settings_backupHint2": "Descargue el contenido actual de sus notas. Las notas serán descargadas en un formato de leíble si el formato esta disponible.",
"settings_backup2": "Descargar mi CryptDrive",
"settings_backup2Confirm": "Esto descargara todas las notas y archivos desde su CryptDrive. Si desea continuar, elija un nombre y presione OK",
"settings_exportTitle": "Exportar su CryptDrive",
"settings_exportDescription": "Por favor espere mientras descargamos y desencriptamos sus documentos. Esto podría tomar unos minutos. Al cerrar la etiqueta interrumpirá el proceso.",
"settings_exportFailed": "Si la nota requiere más de un minuto en ser descargada no será incluida en el archivo exportado. Un link para cada nota que no haya exportada será mostrada.",
"settings_exportWarning": "Nota: esta herramienta aún está en versión beta y podría tener problema al ser escalada. Para una mejor performance se recomiendo dejar esta etiqueta con foco.",
"settings_exportCancel": "Está seguro que quiera cancelar la exportación? Tendrá que comenzar desde el comienzo la próxima vez.",
"settings_export_reading": "Leyendo su CryptDrive...",
"settings_export_download": "Descargando y desencriptando sus documentos...",
"settings_export_compressing": "Comprimiendo...",
"settings_export_done": "Sus descarga está lista!",
"settings_exportError": "Ver errores",
"settings_exportErrorDescription": "No logramos agregar los siguientes documentos en lo exportado:",
"settings_exportErrorEmpty": "Este documento no puede ser exportado (contenido vacío o invalido).",
"settings_exportErrorMissing": "Este documento no puede ser encontrado en nuestros servidores (expirado o borrado por su dueño)",
"settings_exportErrorOther": "Ha ocurrido un error al tratar de exportar este documento: {0}",
"settings_thumbnails": "Imágenes en miniatura",
"settings_disableThumbnailsAction": "Deshabilitar la creación de imágenes pequeñas en su CryptDrive",
"settings_disableThumbnailsDescription": "Las imágenes en miniatura serán guardadas en su navegador de manera automática cuando visite una nueva hoja. Puede deshabilitar esta función acá.",
"settings_resetThumbnailsAction": "Limpiar",
"settings_resetThumbnailsDescription": "Limpiar todas las imágenes en miniatura de sus notas guardadas en su navegador.",
"settings_resetThumbnailsDone": "Todas las imágenes en miniatura han sido borradas."
}

View File

@@ -278,13 +278,8 @@
"profile_create": "Create a profile",
"profile_description": "Description",
"profile_fieldSaved": "New value saved: {0}",
"profile_inviteButton": "Connect",
"profile_inviteButtonTitle": "Create a link that will invite this user to connect with you.",
"profile_inviteExplanation": "Clicking <strong>OK</strong> will create a link to a secure messaging session that <em>only {0} will be able to redeem.</em><br><br>The link will be copied to your clipboard and can be shared publicly.",
"profile_viewMyProfile": "View my profile",
"userlist_addAsFriendTitle": "Send \"{0}\" a friend request",
"userlist_thisIsYou": "This is you (\"{0}\")",
"userlist_pending": "Pending...",
"contacts_title": "Contacts",
"contacts_addError": "Error while adding that contact to the list",
"contacts_added": "Contact invite accepted.",

View File

@@ -102,7 +102,7 @@
"forgetPrompt": "Нажав ОК, вы удалите документ в корзину. Уверены?",
"movedToTrash": "Документ был удалён в корзину.<br><a href=\"/drive/\">Доступ к диску</a>",
"shareButton": "Поделиться",
"shareSuccess": "Ссылка скопирована в буфер обмена",
"shareSuccess": "Ссылка скопирована в буфер обмена.",
"userListButton": "Список пользователей",
"chatButton": "Чат",
"userAccountButton": "Ваш профиль",
@@ -370,5 +370,28 @@
"fm_padIsOwned": "Вы владелец этого пэда",
"fm_padIsOwnedOther": "Этот пэд принадлежит другому пользователю",
"fm_deletedPads": "Эти пэды больше не существуют на сервере, они были удалены с вашего CryptDrive: {0}",
"fm_tags_name": "Имя тэга"
"fm_tags_name": "Имя тэга",
"printCSS": "Пользовательские настройки вида (CSS)",
"viewEmbedTag": "Чтобы встроить данный документ вставьте iframe в нужную страницу. Вы можете настроить внешний вид используя CSS и HTML атрибуты. ",
"debug_getGraphText": "Это код DOT для генерации графика истории этого документа:",
"fm_ownedPadsName": "Собственный",
"fm_info_anonymous": "Вы не вошли в учетную запись, поэтому срок действия ваших пэдов истечет через 3 месяца (<a href=\"https://blog.cryptpad.fr/2017/05/17/You-gotta-log-in/\" target=\"_blank\">find out more</a>). Они хранятся в вашем браузере, поэтому очистка истории может привести к их исчезновению..<br><a href=\"/register/\">Sign up</a> or <a href=\"/login/\">Log in</a> to keep them alive.<br>",
"fm_backup_title": "Резервная ссылка",
"fm_burnThisDriveButton": "Удалить всю информацию, хранящуюся на CryptPad в браузере.",
"fm_tags_used": "Количество использований",
"fm_restoreDrive": "Восстановление прежнего состояния диска. Для достижения наилучших результатов не вносите изменения в диск, пока этот процесс не будет завершен.",
"fm_passwordProtected": "Этот документ защищен паролем",
"fc_newfolder": "Новая папка",
"fc_newsharedfolder": "Новая общая папка",
"fc_rename": "Переименовать",
"fc_open": "Открыть",
"fc_open_ro": "Отркыть (режим чтения)",
"fc_delete": "Переместить в корзину",
"fc_delete_owned": "Удалить с сервера",
"fc_restore": "Восстановить",
"fc_remove_sharedfolder": "Удалить",
"fc_empty": "Удалить корзину",
"fc_prop": "Свойства",
"fc_hashtag": "Теги",
"fc_sizeInKilobytes": "Размер в килобайтах"
}

View File

@@ -0,0 +1,3 @@
{
}