Merge branch 'staging' into get_contacts

pull/1/head
David Benqué 5 years ago
commit b6228421f6

@ -159,6 +159,9 @@
margin-bottom: @alertify_padding-base; margin-bottom: @alertify_padding-base;
margin: 0; margin: 0;
overflow: auto; overflow: auto;
:last-child {
margin-bottom: 0;
}
} }
.alertify-tabs { .alertify-tabs {
max-height: 100%; max-height: 100%;
@ -222,14 +225,22 @@
background-color: @alertify-input-fg; background-color: @alertify-input-fg;
color: @cryptpad_text_col; color: @cryptpad_text_col;
border: 1px solid @alertify-input-bg; border: 1px solid @alertify-input-bg;
margin-bottom: 15px; margin-bottom: @alertify_padding-base;
width: 100%; width: 100%;
font-size: 100%; font-size: 100%;
padding: @alertify_padding-base; padding: @alertify_padding-base;
&[readonly] { &[readonly] {
background-color: @alertify-light-bg; background-color: @alertify-light-bg;
color: @cryptpad_text_col; color: @cryptpad_text_col;
border-color: @alertify-input-fg; border-color: @alertify-light-bg;
}
}
textarea {
overflow: hidden;
padding: 8px;
&[readonly] {
resize: none;
} }
} }
@ -509,5 +520,33 @@
overflow-x: auto; overflow-x: auto;
} }
} }
// Bootstrap Alerts
.alert {
margin: 0px 0px @alertify_padding-base 0px;
font-size: 12px;
padding: 5px;
border-radius: 0px;
i {
margin-right: 10px;
}
&.alert-primary {
background-color: @alertify-base;
color: @alertify-fg;
border-color: @alertify-fg;
a {
color: @alertify-fg;
text-decoration: underline;
}
}
&.dismissable {
display: flex;
align-items: center;
span.fa-times {
font-size: @colortheme_app-font-size;
margin-left: 20px;
cursor: pointer;
}
}
}
} }

@ -1,9 +1,12 @@
@import (reference) "./tools.less"; @import (reference) "./tools.less";
@import (reference) "./colortheme-all.less";
.avatar_vars( .avatar_vars(
@width: 30px @width: 30px
) { ) {
@avatar-width: @width; @avatar-width: @width;
@avatar-font-size: @width / 1.2; @avatar-font-size: @width / 1.2;
@avatar-default-bg: #D9D8D8;
@avatar-default-fg: darken(@avatar-default-bg, 40%);
} }
.avatar_main(@width: 30px) { .avatar_main(@width: 30px) {
--LessLoader_require: LessLoader_currentFile(); --LessLoader_require: LessLoader_currentFile();
@ -30,16 +33,16 @@
justify-content: center; justify-content: center;
align-items: center; align-items: center;
border-radius: 4px;
overflow: hidden; overflow: hidden;
box-sizing: content-box; box-sizing: content-box;
} }
.cp-avatar-default { .cp-avatar-default {
.tools_unselectable(); .tools_unselectable();
background: white; background: @avatar-default-bg;
color: black; color: @avatar-default-fg;
font-size: @avatar-font-size; font-size: @avatar-font-size;
font-size: var(--avatar-font-size); font-size: var(--avatar-font-size);
text-transform: capitalize;
} }
media-tag { media-tag {
min-height: @avatar-width; min-height: @avatar-width;

@ -1,12 +1,16 @@
@import (reference) "./colortheme-all.less"; @import (reference) "./colortheme-all.less";
@import (reference) "./variables.less";
.modals-ui-elements_main() { .modals-ui-elements_main() {
--LessLoader_require: LessLoader_currentFile(); --LessLoader_require: LessLoader_currentFile();
} }
& { & {
.cp-spacer {
height: @variables_padding;
}
// Share modal // Share modal
.msg.cp-inline-radio-group { .msg.cp-inline-radio-group {
overflow: unset !important; overflow: unset !important;
padding: 0px @variables_padding;
.radio-group { .radio-group {
display: flex; display: flex;
flex-direction: row; flex-direction: row;

@ -1,3 +1,4 @@
@import (reference) "./colortheme-all.less";
.password_main() { .password_main() {
--LessLoader_require: LessLoader_currentFile(); --LessLoader_require: LessLoader_currentFile();
} }
@ -17,7 +18,7 @@
justify-content: center; justify-content: center;
cursor: pointer; cursor: pointer;
&:hover { &:hover {
background-color: rgba(0,0,0,0.1); color: darken(@colortheme_alertify-primary, 10%);
} }
} }
} }

@ -6,16 +6,18 @@
--LessLoader_require: LessLoader_currentFile(); --LessLoader_require: LessLoader_currentFile();
}; };
& { & {
.cp-usergrid-container { .cp-usergrid-container {
margin-bottom: 12px !important; // even when last child of .msg
.cp-usergrid-grid { .cp-usergrid-grid {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
margin: -3px;
margin-bottom: 6px; margin-bottom: 6px;
} max-height: 130px;
&:not(.large) { overflow-y: auto;
.cp-usergrid-grid { @media screen and (max-height: 515px) {
margin: -3px; max-height: unset; // remove double scrollbar
margin-bottom: 6px;
} }
} }
&.cp-usergrid-empty { &.cp-usergrid-empty {
@ -28,17 +30,22 @@
input { input {
flex: 1; flex: 1;
min-width: 0; min-width: 0;
margin: 0;
margin-bottom: 0 !important; margin-bottom: 0 !important;
height: 38px;
&::placeholder { /* Chrome, Firefox, Opera, Safari 10.1+ */ &::placeholder { /* Chrome, Firefox, Opera, Safari 10.1+ */
color: @cryptpad_color_grey; color: @cryptpad_color_grey;
opacity: 1; /* Firefox */ opacity: 1; /* Firefox */
} }
} }
margin-bottom: 15px; margin-bottom: 10px;
&:empty { &:empty {
margin: 0; margin: 0;
display: none; display: none;
} }
button:last-child {
margin-right: 0px !important;
}
} }
.cp-usergrid-user { .cp-usergrid-user {
width: 70px; width: 70px;
@ -58,33 +65,48 @@
background-color: @colortheme_alertify-primary; background-color: @colortheme_alertify-primary;
color: @colortheme_alertify-primary-text; color: @colortheme_alertify-primary-text;
order: -1 !important; order: -1 !important;
.cp-usergrid-avatar {
media-tag, .cp-avatar-default {
opacity: 0.7;
}
}
} }
.cp-usergrid-user-avatar { .cp-usergrid-user-avatar {
min-height: 40px; min-height: 40px;
} }
.cp-usergrid-user-name { .cp-usergrid-user-name {
overflow: hidden; overflow: hidden;
white-space: nowrap; white-space: nowrap;
text-overflow: ellipsis; text-overflow: ellipsis;
width: 100%; width: 100%;
text-align: center; text-align: center;
line-height: 18px; line-height: 20px;
flex: 1;
} }
border: 1px solid @colortheme_alertify-primary;
&:not(.large) { &:not(.large) {
.avatar_main(40px); .avatar_main(40px);
} }
&.large { &.large {
.avatar_main(25px); .avatar_main(25px);
width: 140px; width: 145px;
height: 35px; height: 35px;
flex-flow: row; flex-flow: row;
margin: 0; margin: 3px;
margin-right: 15px; flex-basis: calc(33.3333333% - 6px);
margin-bottom: 1px; flex-shrink: 1;
&:nth-child(3n) { min-width: 0;
margin-right: 0; .cp-usergrid-user-name {
margin-left: 5px;
text-align: left;
line-height: 150%;
color: @cryptpad_text_col;
}
}
&.cp-selected {
.cp-usergrid-user-name {
color: @colortheme_alertify-primary-text;
} }
} }
} }

@ -8,6 +8,8 @@ If the result of IO or computation is requested while an identical request
is already in progress, wait until the first one completes and provide its is already in progress, wait until the first one completes and provide its
result to every routine that requested it. result to every routine that requested it.
Asynchrony is guaranteed.
## Usage ## Usage
Provide: Provide:
@ -51,11 +53,12 @@ module.exports = function (/* task */) {
var args = Array.prototype.slice.call(arguments); var args = Array.prototype.slice.call(arguments);
//if (map[id] && map[id].length > 1) { console.log("BATCH-READ DID ITS JOB for [%s][%s]", task, id); } //if (map[id] && map[id].length > 1) { console.log("BATCH-READ DID ITS JOB for [%s][%s]", task, id); }
setTimeout(function () {
map[id].forEach(function (h) { map[id].forEach(function (h) {
h.apply(null, args); h.apply(null, args);
});
delete map[id];
}); });
delete map[id];
}); });
}; };
}; };

@ -4,7 +4,7 @@ q(id, function (next) {
// whatever you need to do.... // whatever you need to do....
// when you're done // when you're done
next(); next(); // guaranteed to be asynchronous :D
}); });
*/ */
@ -16,9 +16,11 @@ module.exports = function () {
var map = {}; var map = {};
var next = function (id) { var next = function (id) {
if (map[id] && map[id].length === 0) { return void delete map[id]; } setTimeout(function () {
var task = map[id].shift(); if (map[id] && map[id].length === 0) { return void delete map[id]; }
task(fix1(next, id)); var task = map[id].shift();
task(fix1(next, id));
});
}; };
return function (id, task) { return function (id, task) {

@ -1,12 +1,17 @@
define([ define([
'jquery', 'jquery',
'/common/cryptpad-common.js', '/common/cryptget.js',
'/common/pinpad.js',
'/common/common-constants.js', '/common/common-constants.js',
'/common/outer/local-store.js', '/common/outer/local-store.js',
'/common/outer/login-block.js',
'/common/outer/network-config.js',
'/customize/login.js',
'/common/test.js', '/common/test.js',
'/bower_components/nthen/index.js', '/bower_components/nthen/index.js',
'/bower_components/netflux-websocket/netflux-client.js',
'/bower_components/tweetnacl/nacl-fast.min.js' '/bower_components/tweetnacl/nacl-fast.min.js'
], function ($, Cryptpad, Constants, LocalStore, Test, nThen) { ], function ($, Crypt, Pinpad, Constants, LocalStore, Block, NetConfig, Login, Test, nThen, Netflux) {
var Nacl = window.nacl; var Nacl = window.nacl;
var signMsg = function (msg, privKey) { var signMsg = function (msg, privKey) {
@ -27,9 +32,57 @@ define([
sessionStorage[Constants.userHashKey]; sessionStorage[Constants.userHashKey];
var proxy; var proxy;
var rpc;
var network;
var rpcError;
var loadProxy = function (hash) {
nThen(function (waitFor) {
var wsUrl = NetConfig.getWebsocketURL();
var w = waitFor();
Netflux.connect(wsUrl).then(function (_network) {
network = _network;
w();
}, function (err) {
rpcError = err;
console.error(err);
});
}).nThen(function (waitFor) {
Crypt.get(hash, waitFor(function (err, val) {
if (err) {
waitFor.abort();
console.error(err);
return;
}
try {
var parsed = JSON.parse(val);
proxy = parsed;
} catch (e) {
console.log("Can't parse user drive", e);
}
}), {
network: network
});
}).nThen(function (waitFor) {
if (!network) { return void waitFor.abort(); }
Pinpad.create(network, proxy, waitFor(function (e, call) {
if (e) {
rpcError = e;
return void waitFor.abort();
}
rpc = call;
}));
}).nThen(function () {
Test(function () {
// This is only here to maybe trigger an error.
window.drive = proxy['drive'];
Test.passed();
});
});
};
var whenReady = function (cb) { var whenReady = function (cb) {
if (proxy) { return void cb(); } if (proxy && (rpc || rpcError)) { return void cb(); }
console.log('CryptPad not ready...'); console.log('CryptPad not ready...');
setTimeout(function () { setTimeout(function () {
whenReady(cb); whenReady(cb);
@ -45,6 +98,17 @@ define([
console.log('CP receiving', data); console.log('CP receiving', data);
if (data.cmd === 'PING') { if (data.cmd === 'PING') {
ret.res = 'PONG'; ret.res = 'PONG';
} else if (data.cmd === 'LOGIN') {
Login.loginOrRegister(data.data.name, data.data.password, false, false, function (err) {
if (err) {
ret.error = 'LOGIN_ERROR';
srcWindow.postMessage(JSON.stringify(ret), domain);
return;
}
loadProxy(LocalStore.getUserHash());
srcWindow.postMessage(JSON.stringify(ret), domain);
});
return;
} else if (data.cmd === 'SIGN') { } else if (data.cmd === 'SIGN') {
if (!AUTHORIZED_DOMAINS.filter(function (x) { return x.test(domain); }).length) { if (!AUTHORIZED_DOMAINS.filter(function (x) { return x.test(domain); }).length) {
ret.error = "UNAUTH_DOMAIN"; ret.error = "UNAUTH_DOMAIN";
@ -63,7 +127,16 @@ define([
} }
} else if (data.cmd === 'UPDATE_LIMIT') { } else if (data.cmd === 'UPDATE_LIMIT') {
return void whenReady(function () { return void whenReady(function () {
Cryptpad.updatePinLimit(function (e, limit, plan, note) { if (rpcError) {
// Tell the user on accounts that there was an issue and they need to wait maximum 24h or contact an admin
ret.warning = true;
srcWindow.postMessage(JSON.stringify(ret), domain);
return;
}
rpc.updatePinLimits(function (e, limit, plan, note) {
if (e) {
ret.warning = true;
}
ret.res = [limit, plan, note]; ret.res = [limit, plan, note];
srcWindow.postMessage(JSON.stringify(ret), domain); srcWindow.postMessage(JSON.stringify(ret), domain);
}); });
@ -74,18 +147,8 @@ define([
srcWindow.postMessage(JSON.stringify(ret), domain); srcWindow.postMessage(JSON.stringify(ret), domain);
}); });
nThen(function (waitFor) { var userHash = LocalStore.getUserHash();
Cryptpad.ready(waitFor()); if (userHash) {
}).nThen(function (waitFor) { loadProxy(userHash);
Cryptpad.getUserObject(null, waitFor(function (obj) { }
proxy = obj;
}));
}).nThen(function () {
console.log('IFRAME READY');
Test(function () {
// This is only here to maybe trigger an error.
window.drive = proxy['drive'];
Test.passed();
});
});
}); });

@ -131,7 +131,7 @@ define([
if (['markdown', 'gfm'].indexOf(CodeMirror.highlightMode) === -1) { return; } if (['markdown', 'gfm'].indexOf(CodeMirror.highlightMode) === -1) { return; }
if (!$previewButton.is('.cp-toolbar-button-active')) { return; } if (!$previewButton.is('.cp-toolbar-button-active')) { return; }
forceDrawPreview(); forceDrawPreview();
}, 150); }, 400);
var previewTo; var previewTo;
$previewButton.click(function () { $previewButton.click(function () {

@ -121,6 +121,10 @@ text.actor {
font-size: 11px; font-size: 11px;
text-height: 14px; text-height: 14px;
} }
.sectionTitle, .titleText {
font-weight: bold;
}
/* Grid and axis */ /* Grid and axis */
.grid .tick { .grid .tick {
stroke: lightgrey; stroke: lightgrey;

@ -127,6 +127,18 @@ define([
return input; return input;
}; };
dialog.selectableArea = function (value, opt) {
var attrs = merge({
readonly: 'readonly',
}, opt);
var input = h('textarea', attrs);
$(input).val(value).click(function () {
input.select();
});
return input;
};
dialog.okButton = function (content, classString) { dialog.okButton = function (content, classString) {
var sel = typeof(classString) === 'string'? 'button.ok.' + classString:'button.ok.primary'; var sel = typeof(classString) === 'string'? 'button.ok.' + classString:'button.ok.primary';
return h(sel, { tabindex: '2', }, content || Messages.okButton); return h(sel, { tabindex: '2', }, content || Messages.okButton);

@ -147,6 +147,7 @@ define([
: Messages.owner_removeText; : Messages.owner_removeText;
var removeCol = UIElements.getUserGrid(msg, { var removeCol = UIElements.getUserGrid(msg, {
common: common, common: common,
large: true,
data: _owners, data: _owners,
noFilter: true noFilter: true
}, function () { }, function () {
@ -238,6 +239,7 @@ define([
}); });
var addCol = UIElements.getUserGrid(Messages.owner_addText, { var addCol = UIElements.getUserGrid(Messages.owner_addText, {
common: common, common: common,
large: true,
data: _friends data: _friends
}, function () { }, function () {
//console.log(arguments); //console.log(arguments);
@ -254,6 +256,7 @@ define([
}); });
var teamsList = UIElements.getUserGrid(Messages.owner_addTeamText, { var teamsList = UIElements.getUserGrid(Messages.owner_addTeamText, {
common: common, common: common,
large: true,
noFilter: true, noFilter: true,
data: teamsData data: teamsData
}, function () {}); }, function () {});
@ -551,9 +554,10 @@ define([
$d.append(password); $d.append(password);
} }
if (!data.noEditPassword && owned && parsed.type !== "sheet") { // FIXME SHEET fix password change for sheets if (!data.noEditPassword && owned) { // FIXME SHEET fix password change for sheets
var sframeChan = common.getSframeChannel(); var sframeChan = common.getSframeChannel();
var isOO = parsed.type === 'sheet';
var isFile = parsed.hashData.type === 'file'; var isFile = parsed.hashData.type === 'file';
var isSharedFolder = parsed.type === 'drive'; var isSharedFolder = parsed.type === 'drive';
@ -586,7 +590,8 @@ define([
UI.confirm(changePwConfirm, function (yes) { UI.confirm(changePwConfirm, function (yes) {
if (!yes) { pLocked = false; return; } if (!yes) { pLocked = false; return; }
$(passwordOk).html('').append(h('span.fa.fa-spinner.fa-spin', {style: 'margin-left: 0'})); $(passwordOk).html('').append(h('span.fa.fa-spinner.fa-spin', {style: 'margin-left: 0'}));
var q = isFile ? 'Q_BLOB_PASSWORD_CHANGE' : 'Q_PAD_PASSWORD_CHANGE'; var q = isFile ? 'Q_BLOB_PASSWORD_CHANGE' :
(isOO ? 'Q_OO_PASSWORD_CHANGE' : 'Q_PAD_PASSWORD_CHANGE');
// If this is a file password change, register to the upload events: // If this is a file password change, register to the upload events:
// * if there is a pending upload, ask if we shoudl interrupt // * if there is a pending upload, ask if we shoudl interrupt
@ -737,12 +742,22 @@ define([
UIElements.getProperties = function (common, data, cb) { UIElements.getProperties = function (common, data, cb) {
var c1; var c1;
var c2; var c2;
var button = [{
className: 'primary',
name: Messages.okButton,
onClick: function () {},
keys: [13]
}];
NThen(function (waitFor) { NThen(function (waitFor) {
getPadProperties(common, data, waitFor(function (e, c) { getPadProperties(common, data, waitFor(function (e, c) {
c1 = c[0]; c1 = UI.dialog.customModal(c[0], {
buttons: button
});
})); }));
getRightsProperties(common, data, waitFor(function (e, c) { getRightsProperties(common, data, waitFor(function (e, c) {
c2 = c[0]; c2 = UI.dialog.customModal(c[0], {
buttons: button
});
})); }));
}).nThen(function () { }).nThen(function () {
var tabs = UI.dialog.tabs([{ var tabs = UI.dialog.tabs([{
@ -782,8 +797,6 @@ define([
var noOthers = icons.length === 0 ? '.cp-usergrid-empty' : ''; var noOthers = icons.length === 0 ? '.cp-usergrid-empty' : '';
var buttonSelect = h('button', Messages.share_selectAll);
var buttonDeselect = h('button', Messages.share_deselectAll);
var inputFilter = h('input', { var inputFilter = h('input', {
placeholder: Messages.share_filterFriend placeholder: Messages.share_filterFriend
}); });
@ -791,9 +804,7 @@ define([
var div = h('div.cp-usergrid-container' + noOthers + (config.large?'.large':''), [ var div = h('div.cp-usergrid-container' + noOthers + (config.large?'.large':''), [
label ? h('label', label) : undefined, label ? h('label', label) : undefined,
h('div.cp-usergrid-filter', (config.noFilter || config.noSelect) ? undefined : [ h('div.cp-usergrid-filter', (config.noFilter || config.noSelect) ? undefined : [
inputFilter, inputFilter
buttonSelect,
buttonDeselect
]), ]),
]); ]);
var $div = $(div); var $div = $(div);
@ -806,23 +817,8 @@ define([
$div.find('.cp-usergrid-user:not(.cp-selected):not([data-name*="'+name+'"])').hide(); $div.find('.cp-usergrid-user:not(.cp-selected):not([data-name*="'+name+'"])').hide();
} }
}; };
$(inputFilter).on('keydown keyup change', redraw); $(inputFilter).on('keydown keyup change', redraw);
$(buttonSelect).click(function () {
$div.find('.cp-usergrid-user:not(.cp-selected):visible').addClass('cp-selected');
onSelect();
});
$(buttonDeselect).click(function () {
$div.find('.cp-usergrid-user.cp-selected').removeClass('cp-selected').each(function (i, el) {
var order = $(el).attr('data-order');
if (!order) { return; }
$(el).attr('style', 'order:'+order);
});
redraw();
onSelect();
});
$(div).append(h('div.cp-usergrid-grid', icons)); $(div).append(h('div.cp-usergrid-grid', icons));
if (!config.noSelect) { if (!config.noSelect) {
$div.on('click', '.cp-usergrid-user', function () { $div.on('click', '.cp-usergrid-user', function () {
@ -883,7 +879,8 @@ define([
var friendsList = UIElements.getUserGrid(null, { var friendsList = UIElements.getUserGrid(null, {
common: common, common: common,
data: friends, data: friends,
noFilter: false noFilter: false,
large: true
}, refreshButtons); }, refreshButtons);
var friendDiv = friendsList.div; var friendDiv = friendsList.div;
$div.append(friendDiv); $div.append(friendDiv);
@ -909,6 +906,7 @@ define([
var teamsList = UIElements.getUserGrid(Messages.share_linkTeam, { var teamsList = UIElements.getUserGrid(Messages.share_linkTeam, {
common: common, common: common,
noFilter: true, noFilter: true,
large: true,
data: teams data: teams
}, refreshButtons); }, refreshButtons);
$div.append(teamsList.div); $div.append(teamsList.div);
@ -1057,12 +1055,29 @@ define([
if (!hashes || (!hashes.editHash && !hashes.viewHash)) { return; } if (!hashes || (!hashes.editHash && !hashes.viewHash)) { return; }
// check if the pad is password protected
var hash = hashes.editHash || hashes.viewHash;
var href = origin + pathname + '#' + hash;
var parsedHref = Hash.parsePadUrl(href);
var hasPassword = parsedHref.hashData.password;
var makeFaqLink = function () {
var link = h('span', [
h('i.fa.fa-question-circle'),
h('a', {href: '#'}, Messages.passwordFaqLink)
]);
$(link).click(function () {
common.openURL(config.origin + "/faq.html#security-pad_password");
});
return link;
};
var parsed = Hash.parsePadUrl(pathname); var parsed = Hash.parsePadUrl(pathname);
var canPresent = ['code', 'slide'].indexOf(parsed.type) !== -1; var canPresent = ['code', 'slide'].indexOf(parsed.type) !== -1;
var rights = h('div.msg.cp-inline-radio-group', [ var rights = h('div.msg.cp-inline-radio-group', [
h('label', Messages.share_linkAccess), h('label', Messages.share_linkAccess),
h('br'),
h('div.radio-group',[ h('div.radio-group',[
UI.createRadio('accessRights', 'cp-share-editable-false', UI.createRadio('accessRights', 'cp-share-editable-false',
Messages.share_linkView, true, { mark: {tabindex:1} }), Messages.share_linkView, true, { mark: {tabindex:1} }),
@ -1109,9 +1124,42 @@ define([
h('br'), h('br'),
] : [ ] : [
UI.createCheckbox('cp-share-embed', Messages.share_linkEmbed, false, { mark: {tabindex:1} }), UI.createCheckbox('cp-share-embed', Messages.share_linkEmbed, false, { mark: {tabindex:1} }),
h('br'),
]; ];
linkContent.push(UI.dialog.selectable('', { id: 'cp-share-link-preview', tabindex: 1 })); linkContent.push(h('div.cp-spacer'));
linkContent.push(UI.dialog.selectableArea('', { id: 'cp-share-link-preview', tabindex: 1, rows:3}));
// Show alert if the pad is password protected
if (hasPassword) {
linkContent.push(h('div.alert.alert-primary', [
h('i.fa.fa-lock'),
Messages.share_linkPasswordAlert, h('br'),
makeFaqLink()
]));
}
// warning about sharing links
var localStore = window.cryptpadStore;
var dismissButton = h('span.fa.fa-times');
var shareLinkWarning = h('div.alert.alert-warning.dismissable',
{ style: 'display: none;' },
[
h('span.cp-inline-alert-text', Messages.share_linkWarning),
dismissButton
]);
linkContent.push(shareLinkWarning);
localStore.get('hide-alert-shareLinkWarning', function (val) {
if (val === '1') { return; }
$(shareLinkWarning).show();
$(dismissButton).on('click', function () {
localStore.put('hide-alert-shareLinkWarning', '1');
$(shareLinkWarning).remove();
});
});
var link = h('div.cp-share-modal', linkContent); var link = h('div.cp-share-modal', linkContent);
var $link = $(link); var $link = $(link);
@ -1177,7 +1225,19 @@ define([
// XXX Don't display access rights if no contacts // XXX Don't display access rights if no contacts
var contactsContent = h('div.cp-share-modal'); var contactsContent = h('div.cp-share-modal');
$(contactsContent).append(friendsList); var $contactsContent = $(contactsContent);
$contactsContent.append(friendsList);
// Show alert if the pad is password protected
if (hasPassword) {
$contactsContent.append(h('div.alert.alert-primary', [
h('i.fa.fa-unlock'),
Messages.share_contactPasswordAlert, h('br'),
makeFaqLink()
]));
}
var contactButtons = friendsObject.buttons; var contactButtons = friendsObject.buttons;
contactButtons.unshift(makeCancelButton()); contactButtons.unshift(makeCancelButton());
@ -1196,9 +1256,18 @@ define([
}; };
var embedContent = [ var embedContent = [
h('p', Messages.viewEmbedTag), h('p', Messages.viewEmbedTag),
h('br'), UI.dialog.selectableArea(getEmbedValue(), { id: 'cp-embed-link-preview', tabindex: 1, rows: 3})
UI.dialog.selectable(getEmbedValue(), { id: 'cp-embed-link-preview', tabindex: 1 })
]; ];
// Show alert if the pad is password protected
if (hasPassword) {
embedContent.push(h('div.alert.alert-primary', [
h('i.fa.fa-lock'), ' ',
Messages.share_embedPasswordAlert, h('br'),
makeFaqLink()
]));
}
var embedButtons = [ var embedButtons = [
makeCancelButton(), { makeCancelButton(), {
className: 'primary', className: 'primary',
@ -1303,6 +1372,21 @@ define([
if (!hashes.fileHash) { throw new Error("You must provide a file hash"); } if (!hashes.fileHash) { throw new Error("You must provide a file hash"); }
var url = origin + pathname + '#' + hashes.fileHash; var url = origin + pathname + '#' + hashes.fileHash;
// check if the file is password protected
var parsedHref = Hash.parsePadUrl(url);
var hasPassword = parsedHref.hashData.password;
var makeFaqLink = function () {
var link = h('span', [
h('i.fa.fa-question-circle'),
h('a', {href: '#'}, Messages.passwordFaqLink)
]);
$(link).click(function () {
common.openURL(config.origin + "/faq.html#security-pad_password");
});
return link;
};
var getLinkValue = function () { return url; }; var getLinkValue = function () { return url; };
var makeCancelButton = function() { var makeCancelButton = function() {
@ -1314,9 +1398,40 @@ define([
// Share link tab // Share link tab
var linkContent = [ var linkContent = [
UI.dialog.selectable(getLinkValue(), { id: 'cp-share-link-preview', tabindex: 1 }) UI.dialog.selectableArea(getLinkValue(), { id: 'cp-share-link-preview', tabindex: 1, rows:2 })
]; ];
// Show alert if the pad is password protected
if (hasPassword) {
linkContent.push(h('div.alert.alert-primary', [
h('i.fa.fa-lock'),
Messages.share_linkPasswordAlert, h('br'),
makeFaqLink()
]));
}
// warning about sharing links
var localStore = window.cryptpadStore;
var dismissButton = h('span.fa.fa-times');
var shareLinkWarning = h('div.alert.alert-warning.dismissable',
{ style: 'display: none;' },
[
h('span.cp-inline-alert-text', Messages.share_linkWarning),
dismissButton
]);
linkContent.push(shareLinkWarning);
localStore.get('hide-alert-shareLinkWarning', function (val) {
if (val === '1') { return; }
$(shareLinkWarning).show();
$(dismissButton).on('click', function () {
localStore.put('hide-alert-shareLinkWarning', '1');
$(shareLinkWarning).remove();
});
});
var link = h('div.cp-share-modal', linkContent); var link = h('div.cp-share-modal', linkContent);
var linkButtons = [ var linkButtons = [
@ -1346,7 +1461,17 @@ define([
var friendsList = friendsObject.content; var friendsList = friendsObject.content;
var contactsContent = h('div.cp-share-modal'); var contactsContent = h('div.cp-share-modal');
$(contactsContent).append(friendsList); var $contactsContent = $(contactsContent);
$contactsContent.append(friendsList);
// Show alert if the pad is password protected
if (hasPassword) {
$contactsContent.append(h('div.alert.alert-primary', [
h('i.fa.fa-unlock'),
Messages.share_contactPasswordAlert, h('br'),
makeFaqLink()
]));
}
var contactButtons = friendsObject.buttons; var contactButtons = friendsObject.buttons;
contactButtons.unshift(makeCancelButton()); contactButtons.unshift(makeCancelButton());
@ -1360,12 +1485,20 @@ define([
// Embed tab // Embed tab
var embed = h('div.cp-share-modal', [ var embed = h('div.cp-share-modal', [
h('p', Messages.fileEmbedScript), h('p', Messages.fileEmbedScript),
h('br'),
UI.dialog.selectable(common.getMediatagScript()), UI.dialog.selectable(common.getMediatagScript()),
h('p', Messages.fileEmbedTag), h('p', Messages.fileEmbedTag),
h('br'),
UI.dialog.selectable(common.getMediatagFromHref(fileData)), UI.dialog.selectable(common.getMediatagFromHref(fileData)),
]); ]);
// Show alert if the pad is password protected
if (hasPassword) {
embed.append(h('div.alert.alert-primary', [
h('i.fa.fa-lock'), ' ',
Messages.share_embedPasswordAlert, h('br'),
makeFaqLink()
]));
}
var embedButtons = [{ var embedButtons = [{
className: 'cancel', className: 'cancel',
name: Messages.cancel, name: Messages.cancel,
@ -1800,7 +1933,7 @@ define([
if (e) { return void console.error(e); } if (e) { return void console.error(e); }
UIElements.getProperties(common, data, function (e, $prop) { UIElements.getProperties(common, data, function (e, $prop) {
if (e) { return void console.error(e); } if (e) { return void console.error(e); }
UI.alert($prop[0], undefined, true); UI.openCustomModal($prop[0]);
}); });
}); });
}); });

@ -1155,6 +1155,242 @@ define([
}); });
}; };
common.changeOOPassword = function (data, _cb) {
var cb = Util.once(Util.mkAsync(_cb));
var href = data.href;
var newPassword = data.password;
var teamId = data.teamId;
if (!href) { return void cb({ error: 'EINVAL_HREF' }); }
var parsed = Hash.parsePadUrl(href);
if (!parsed.hash) { return void cb({ error: 'EINVAL_HREF' }); }
if (parsed.type !== 'sheet') { return void cb({ error: 'EINVAL_TYPE' }); }
var warning = false;
var newHash, newRoHref;
var oldSecret;
var oldMetadata;
var oldRtChannel;
var privateData;
var padData;
var newSecret;
if (parsed.hashData.version >= 2) {
newSecret = Hash.getSecrets(parsed.type, parsed.hash, newPassword);
if (!(newSecret.keys && newSecret.keys.editKeyStr)) {
return void cb({error: 'EAUTH'});
}
newHash = Hash.getEditHashFromKeys(newSecret);
}
var newHref = '/' + parsed.type + '/#' + newHash;
var newRtChannel = Hash.createChannelId();
var Crypt, Crypto;
var cryptgetVal;
var optsPut = {
password: newPassword,
metadata: {
validateKey: newSecret.keys.validateKey
},
};
Nthen(function (waitFor) {
common.getPadAttribute('', waitFor(function (err, _data) {
padData = _data;
}), href);
}).nThen(function (waitFor) {
oldSecret = Hash.getSecrets(parsed.type, parsed.hash, padData.password);
require([
'/common/cryptget.js',
'/bower_components/chainpad-crypto/crypto.js',
], waitFor(function (_Crypt, _Crypto) {
Crypt = _Crypt;
Crypto = _Crypto;
}));
common.getPadMetadata({channel: oldSecret.channel}, waitFor(function (metadata) {
oldMetadata = metadata;
}));
common.getMetadata(waitFor(function (err, data) {
if (err) {
waitFor.abort();
return void cb({ error: err });
}
privateData = data.priv;
}));
}).nThen(function (waitFor) {
// Check if we're allowed to change the password
var owners = oldMetadata.owners;
optsPut.metadata.owners = owners;
var edPublic = teamId ? (privateData.teams[teamId] || {}).edPublic : privateData.edPublic;
var isOwner = Array.isArray(owners) && edPublic && owners.indexOf(edPublic) !== -1;
if (!isOwner) {
// We're not an owner, we shouldn't be able to change the password!
waitFor.abort();
return void cb({ error: 'EPERM' });
}
var mailbox = oldMetadata.mailbox;
if (mailbox) {
// Create the encryptors to be able to decrypt and re-encrypt the mailboxes
var oldCrypto = Crypto.createEncryptor(oldSecret.keys);
var newCrypto = Crypto.createEncryptor(newSecret.keys);
var m;
if (typeof(mailbox) === "string") {
try {
m = newCrypto.encrypt(oldCrypto.decrypt(mailbox, true, true));
} catch (e) {}
} else if (mailbox && typeof(mailbox) === "object") {
m = {};
Object.keys(mailbox).forEach(function (ed) {
try {
m[ed] = newCrypto.encrypt(oldCrypto.decrypt(mailbox[ed], true, true));
} catch (e) {
console.error(e);
}
});
}
optsPut.metadata.mailbox = m;
}
var expire = oldMetadata.expire;
if (expire) {
optsPut.metadata.expire = (expire - (+new Date())) / 1000; // Lifetime in seconds
}
// Get last cp (cryptget)
Crypt.get(parsed.hash, waitFor(function (err, val) {
if (err) {
waitFor.abort();
return void cb({ error: err });
}
try {
cryptgetVal = JSON.parse(val);
if (!cryptgetVal.content) {
waitFor.abort();
return void cb({ error: 'INVALID_CONTENT' });
}
} catch (e) {
waitFor.abort();
return void cb({ error: 'CANT_PARSE' });
}
}), {
password: padData.password
});
}).nThen(function (waitFor) {
// Re-encrypt rtchannel
oldRtChannel = Util.find(cryptgetVal, ['content', 'channel']);
var newCrypto = Crypto.createEncryptor(newSecret.keys);
var oldCrypto = Crypto.createEncryptor(oldSecret.keys);
var cps = Util.find(cryptgetVal, ['content', 'hashes']);
var l = Object.keys(cps).length;
var lastCp = l ? cps[l] : {};
cryptgetVal.content.hashes = {};
common.getHistory({
channel: oldRtChannel,
lastKnownHash: lastCp.hash
}, waitFor(function (obj) {
if (obj && obj.error) {
waitFor.abort();
console.error(obj);
return void cb(obj.error);
}
var msgs = obj;
var newHistory = msgs.map(function (str) {
try {
var d = oldCrypto.decrypt(str, true, true);
return newCrypto.encrypt(d);
} catch (e) {
console.log(e);
waitFor.abort();
return void cb({error: e});
}
});
// Update last knwon hash in cryptgetVal
if (lastCp) {
lastCp.hash = newHistory[0].slice(0, 64);
lastCp.index = 50;
cryptgetVal.content.hashes[1] = lastCp;
}
common.onlyoffice.execCommand({
cmd: 'REENCRYPT',
data: {
channel: newRtChannel,
msgs: newHistory,
metadata: optsPut.metadata
}
}, waitFor(function (obj) {
if (obj && obj.error) {
waitFor.abort();
console.warn(obj);
return void cb(obj.error);
}
}));
}));
}).nThen(function (waitFor) {
// The new rt channel is ready
// The blob uses its own encryption and doesn't need to be reencrypted
cryptgetVal.content.channel = newRtChannel;
Crypt.put(newHash, JSON.stringify(cryptgetVal), waitFor(function (err) {
if (err) {
waitFor.abort();
return void cb({ error: err });
}
}), optsPut);
}).nThen(function (waitFor) {
pad.leavePad({
channel: oldSecret.channel
}, waitFor());
pad.onDisconnectEvent.fire(true);
}).nThen(function (waitFor) {
// Set the new password to our pad data
common.setPadAttribute('password', newPassword, waitFor(function (err) {
if (err) { warning = true; }
}), href);
common.setPadAttribute('channel', newSecret.channel, waitFor(function (err) {
if (err) { warning = true; }
}), href);
common.setPadAttribute('rtChannel', newRtChannel, waitFor(function (err) {
if (err) { warning = true; }
}), href);
var viewHash = Hash.getViewHashFromKeys(newSecret);
newRoHref = '/' + parsed.type + '/#' + viewHash;
common.setPadAttribute('roHref', newRoHref, waitFor(function (err) {
if (err) { warning = true; }
}), href);
if (parsed.hashData.password && newPassword) { return; } // same hash
common.setPadAttribute('href', newHref, waitFor(function (err) {
if (err) { warning = true; }
}), href);
}).nThen(function (waitFor) {
// delete the old pad
common.removeOwnedChannel({
channel: oldSecret.channel,
teamId: teamId
}, waitFor(function (obj) {
if (obj && obj.error) {
waitFor.abort();
console.info(obj);
return void cb(obj.error);
}
common.removeOwnedChannel({
channel: oldRtChannel,
teamId: teamId
}, waitFor());
}));
}).nThen(function () {
cb({
warning: warning,
hash: newHash,
href: newHref,
roHref: newRoHref
});
});
};
common.changeUserPassword = function (Crypt, edPublic, data, cb) { common.changeUserPassword = function (Crypt, edPublic, data, cb) {
if (!edPublic) { if (!edPublic) {
return void cb({ return void cb({
@ -1350,6 +1586,9 @@ define([
common.getFullHistory = function (data, cb) { common.getFullHistory = function (data, cb) {
postMessage("GET_FULL_HISTORY", data, cb, {timeout: 180000}); postMessage("GET_FULL_HISTORY", data, cb, {timeout: 180000});
}; };
common.getHistory = function (data, cb) {
postMessage("GET_HISTORY", data, cb, {timeout: 180000});
};
common.getHistoryRange = function (data, cb) { common.getHistoryRange = function (data, cb) {
postMessage("GET_HISTORY_RANGE", data, cb); postMessage("GET_HISTORY_RANGE", data, cb);
}; };

@ -197,7 +197,6 @@ define([
'APPLET', 'APPLET',
'VIDEO', // privacy implications of videos are the same as images 'VIDEO', // privacy implications of videos are the same as images
'AUDIO', // same with audio 'AUDIO', // same with audio
'SVG'
]; ];
var unsafeTag = function (info) { var unsafeTag = function (info) {
/*if (info.node && $(info.node).parents('media-tag').length) { /*if (info.node && $(info.node).parents('media-tag').length) {
@ -307,8 +306,39 @@ define([
var Dom = domFromHTML($('<div>').append($div).html()); var Dom = domFromHTML($('<div>').append($div).html());
$content[0].normalize(); $content[0].normalize();
$content.find('pre.mermaid[data-processed="true"]').remove();
var mermaid_source = [];
var mermaid_cache = {};
// iterate over the unrendered mermaid inputs, caching their source as you go
$(newDomFixed).find('pre.mermaid').each(function (index, el) {
if (el.childNodes.length === 1 && el.childNodes[0].nodeType === 3) {
var src = el.childNodes[0].wholeText;
el.setAttribute('mermaid-source', src);
mermaid_source[index] = src;
}
});
// iterate over rendered mermaid charts
$content.find('pre.mermaid:not([processed="true"])').each(function (index, el) {
// retrieve the attached source code which it was drawn
var src = el.getAttribute('mermaid-source');
// check if that source exists in the set of charts which are about to be rendered
if (mermaid_source.indexOf(src) === -1) {
// if it's not, then you can remove it
if (el.parentNode && el.parentNode.children.length) {
el.parentNode.removeChild(el);
}
} else if (el.childNodes.length === 1 && el.childNodes[0].nodeType !== 3) {
// otherwise, confirm that the content of the rendered chart is not a text node
// and keep a copy of it
mermaid_cache[src] = el.childNodes[0];
}
});
var oldDom = domFromHTML($content[0].outerHTML); var oldDom = domFromHTML($content[0].outerHTML);
var patch = makeDiff(oldDom, Dom, id); var patch = makeDiff(oldDom, Dom, id);
if (typeof(patch) === 'string') { if (typeof(patch) === 'string') {
throw new Error(patch); throw new Error(patch);
@ -348,8 +378,32 @@ define([
var target = document.getElementById($a.attr('data-href')); var target = document.getElementById($a.attr('data-href'));
if (target) { target.scrollIntoView(); } if (target) { target.scrollIntoView(); }
}); });
// loop over mermaid elements in the rendered content
$content.find('pre.mermaid').each(function (index, el) {
// since you've simply drawn the content that was supplied via markdown
// you can assume that the index of your rendered charts matches that
// of those in the markdown source.
var src = mermaid_source[index];
el.setAttribute('mermaid-source', src);
var cached = mermaid_cache[src];
// check if you had cached a pre-rendered instance of the supplied source
if (typeof(cached) !== 'object') { return; }
// if there's a cached rendering, empty out the contained source code
// which would otherwise be drawn again.
// apparently this is the fastest way to empty out an element
while (el.firstChild) { el.removeChild(el.firstChild); } //el.innerHTML = '';
// insert the cached graph
el.appendChild(cached);
// and set a flag indicating that this graph need not be reprocessed
el.setAttribute('data-processed', true);
});
try { try {
Mermaid.init(); // finally, draw any graphs which have changed and were thus not cached
Mermaid.init(undefined, $content.find('pre.mermaid:not([data-processed="true"])'));
} catch (e) { console.error(e); } } catch (e) { console.error(e); }
} }
}; };

@ -4187,7 +4187,7 @@ define([
} }
getProperties(el, function (e, $prop) { getProperties(el, function (e, $prop) {
if (e) { return void logError(e); } if (e) { return void logError(e); }
UI.alert($prop[0], undefined, true); UI.openCustomModal($prop[0]);
}); });
} }
else if ($this.hasClass("cp-app-drive-context-hashtag")) { else if ($this.hasClass("cp-app-drive-context-hashtag")) {

@ -1693,7 +1693,7 @@ define([
// GET_FULL_HISTORY from sframe-common-outer // GET_FULL_HISTORY from sframe-common-outer
Store.getFullHistory = function (clientId, data, cb) { Store.getFullHistory = function (clientId, data, cb) {
var network = store.network; var network = store.network;
var hkn = network.historyKeeper; var hk = network.historyKeeper;
//var crypto = Crypto.createEncryptor(data.keys); //var crypto = Crypto.createEncryptor(data.keys);
// Get the history messages and send them to the iframe // Get the history messages and send them to the iframe
var parse = function (msg) { var parse = function (msg) {
@ -1708,8 +1708,10 @@ define([
var onMsg = function (msg) { var onMsg = function (msg) {
if (completed) { return; } if (completed) { return; }
var parsed = parse(msg); var parsed = parse(msg);
if (!parsed) { return; }
if (parsed[0] === 'FULL_HISTORY_END') { if (parsed[0] === 'FULL_HISTORY_END') {
cb(msgs); cb(msgs);
network.off('message', onMsg);
completed = true; completed = true;
return; return;
} }
@ -1726,12 +1728,69 @@ define([
} }
}; };
network.on('message', onMsg); network.on('message', onMsg);
network.sendto(hkn, 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) {
var network = store.network;
var hk = network.historyKeeper;
var parse = function (msg) {
try {
return JSON.parse(msg);
} catch (e) {
return null;
}
};
var msgs = [];
var completed = false;
var onMsg = function (msg, sender) {
if (completed) { return; }
if (sender !== hk) { return; }
var parsed = parse(msg);
if (!parsed) { return; }
// Ignore the metadata message
if (parsed.validateKey && parsed.channel) { return; }
if (parsed.error && parsed.channel) {
if (parsed.channel === data.channel) {
network.off('message', onMsg);
completed = true;
cb({error: parsed.error});
}
return;
}
// End of history: cb
if (parsed.state === 1 && parsed.channel) {
if (parsed.channel !== data.channel) { return; }
cb(msgs);
network.off('message', onMsg);
completed = true;
return;
}
msg = parsed[4];
// Keep only the history for our channel
if (parsed[3] !== data.channel) { return; }
if (msg) {
msg = msg.replace(/cp\|(([A-Za-z0-9+\/=]+)\|)?/, '');
msgs.push(msg);
}
};
network.on('message', onMsg);
var cfg = {
lastKnownHash: data.lastKnownHash
};
var msg = ['GET_HISTORY', data.channel, cfg];
network.sendto(hk, JSON.stringify(msg));
}; };
Store.getHistoryRange = function (clientId, data, cb) { Store.getHistoryRange = function (clientId, data, cb) {
var network = store.network; var network = store.network;
var hkn = network.historyKeeper; var hk = network.historyKeeper;
var parse = function (msg) { var parse = function (msg) {
try { try {
return JSON.parse(msg); return JSON.parse(msg);
@ -1779,7 +1838,7 @@ define([
}; };
network.on('message', onMsg); network.on('message', onMsg);
network.sendto(hkn, JSON.stringify(['GET_HISTORY_RANGE', data.channel, { network.sendto(hk, JSON.stringify(['GET_HISTORY_RANGE', data.channel, {
from: data.lastKnownHash, from: data.lastKnownHash,
cpCount: 2, cpCount: 2,
txid: txid txid: txid

@ -200,6 +200,41 @@ define([
})); }));
}; };
var reencrypt = function (ctx, data, cId, cb) {
var channel = data.channel;
var network = ctx.store.network;
var onOpen = function (wc) {
var hk = network.historyKeeper;
var cfg = {
metadata: data.metadata
};
var msg = ['GET_HISTORY', wc.id, cfg];
network.sendto(hk, JSON.stringify(msg));
data.msgs.forEach(function (msg) {
wc.bcast(msg);
});
wc.leave();
cb();
};
ctx.store.anon_rpc.send("IS_NEW_CHANNEL", channel, function (e, response) {
if (e) { return void cb({error: e}); }
var isNew;
if (response && response.length && typeof(response[0]) === 'boolean') {
isNew = response[0];
} else {
cb({error: 'INVALID_RESPONSE'});
}
if (!isNew) { return void cb({error: 'EEXISTS'}); }
// Channel is new: we can push our reencrypted history
network.join(channel).then(onOpen, function (err) {
return void cb({error: err});
});
});
};
var leaveChannel = function (ctx, padChan) { var leaveChannel = function (ctx, padChan) {
// Leave channel and prevent reconnect when we leave a pad // Leave channel and prevent reconnect when we leave a pad
Object.keys(ctx.channels).some(function (ooChan) { Object.keys(ctx.channels).some(function (ooChan) {
@ -267,6 +302,9 @@ define([
if (cmd === 'OPEN_CHANNEL') { if (cmd === 'OPEN_CHANNEL') {
return void openChannel(ctx, data, clientId, cb); return void openChannel(ctx, data, clientId, cb);
} }
if (cmd === 'REENCRYPT') {
return void reencrypt(ctx, data, clientId, cb);
}
}; };
return oo; return oo;

@ -73,6 +73,7 @@ define([
JOIN_PAD: Store.joinPad, JOIN_PAD: Store.joinPad,
LEAVE_PAD: Store.leavePad, LEAVE_PAD: Store.leavePad,
GET_FULL_HISTORY: Store.getFullHistory, GET_FULL_HISTORY: Store.getFullHistory,
GET_HISTORY: Store.getHistory,
GET_HISTORY_RANGE: Store.getHistoryRange, GET_HISTORY_RANGE: Store.getHistoryRange,
IS_NEW_CHANNEL: Store.isNewChannel, IS_NEW_CHANNEL: Store.isNewChannel,
REQUEST_PAD_ACCESS: Store.requestPadAccess, REQUEST_PAD_ACCESS: Store.requestPadAccess,

@ -493,6 +493,20 @@ define([
Cryptpad.storeInTeam(data, cb); Cryptpad.storeInTeam(data, cb);
}); });
sframeChan.on('EV_GOTO_URL', function (url) {
if (url) {
window.location.href = url;
} else {
window.location.reload();
}
});
sframeChan.on('EV_OPEN_URL', function (url) {
if (url) {
window.open(url);
}
});
}; };
addCommonRpc(sframeChan); addCommonRpc(sframeChan);
@ -956,20 +970,6 @@ define([
}); });
}); });
sframeChan.on('EV_GOTO_URL', function (url) {
if (url) {
window.location.href = url;
} else {
window.location.reload();
}
});
sframeChan.on('EV_OPEN_URL', function (url) {
if (url) {
window.open(url);
}
});
sframeChan.on('Q_PIN_GET_USAGE', function (teamId, cb) { sframeChan.on('Q_PIN_GET_USAGE', function (teamId, cb) {
Cryptpad.isOverPinLimit(teamId, function (err, overLimit, data) { Cryptpad.isOverPinLimit(teamId, function (err, overLimit, data) {
cb({ cb({
@ -1008,6 +1008,11 @@ define([
}, cb); }, cb);
}); });
sframeChan.on('Q_OO_PASSWORD_CHANGE', function (data, cb) {
data.href = data.href || window.location.href;
Cryptpad.changeOOPassword(data, cb);
});
sframeChan.on('Q_PAD_PASSWORD_CHANGE', function (data, cb) { sframeChan.on('Q_PAD_PASSWORD_CHANGE', function (data, cb) {
data.href = data.href || window.location.href; data.href = data.href || window.location.href;
Cryptpad.changePadPassword(Cryptget, Crypto, data, cb); Cryptpad.changePadPassword(Cryptget, Crypto, data, cb);

@ -286,7 +286,7 @@ define([
kanban.inEditMode = true; kanban.inEditMode = true;
// create a form to enter element // create a form to enter element
var boardId = $(el.parentNode.parentNode).attr("data-id"); var boardId = $(el.parentNode.parentNode).attr("data-id");
var $item = $('<div>', {'class': 'kanban-item'}); var $item = $('<div>', {'class': 'kanban-item new-item'});
var $input = getInput().val(name).appendTo($item); var $input = getInput().val(name).appendTo($item);
kanban.addForm(boardId, $item[0]); kanban.addForm(boardId, $item[0]);
$input.focus(); $input.focus();

@ -147,9 +147,13 @@
self.drake = self.dragula(self.boardContainer, { self.drake = self.dragula(self.boardContainer, {
moves: function (el, source, handle, sibling) { moves: function (el, source, handle, sibling) {
if (self.options.readOnly) { return false; } if (self.options.readOnly) { return false; }
if (el.classList.contains('new-item')) { return false; }
return handle.classList.contains('kanban-item'); return handle.classList.contains('kanban-item');
}, },
accepts: function (el, target, source, sibling) { accepts: function (el, target, source, sibling) {
if (sibling === null) {
return false;
}
if (self.options.readOnly) { return false; } if (self.options.readOnly) { return false; }
return true; return true;
}, },

@ -51,6 +51,7 @@ define([
'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-logout-everywhere', 'cp-settings-logout-everywhere',
'cp-settings-autostore', 'cp-settings-autostore',
'cp-settings-userfeedback', 'cp-settings-userfeedback',
@ -67,7 +68,6 @@ define([
], ],
'drive': [ 'drive': [
'cp-settings-drive-duplicate', 'cp-settings-drive-duplicate',
'cp-settings-resettips',
'cp-settings-thumbnails', 'cp-settings-thumbnails',
'cp-settings-drive-backup', 'cp-settings-drive-backup',
'cp-settings-drive-import-local', 'cp-settings-drive-import-local',
@ -835,7 +835,7 @@ define([
var localStore = window.cryptpadStore; var localStore = window.cryptpadStore;
$button.click(function () { $button.click(function () {
Object.keys(localStore.store).forEach(function (k) { Object.keys(localStore.store).forEach(function (k) {
if(k.slice(0, 9) === "hide-info") { if(/^(hide-(info|alert))/.test(k)) {
localStore.put(k, null); localStore.put(k, null);
} }
}); });

Loading…
Cancel
Save