Merge branch 'staging' into ooPassword

This commit is contained in:
yflory 2019-12-09 16:05:24 +01:00
commit 3528516ab9
29 changed files with 589 additions and 151 deletions

View File

@ -1,3 +1,39 @@
# GoldenFrog release (3.6.0)
## Goals
We're following up our last few releases of major core developments with an effort to improve reliability in some unstable areas and make some superficial tweaks to improve usability of some critical interfaces.
## Update notes
Update to 3.6.0 from 3.5.0 using the normal update procedure:
1. stop your server
2. pull the latest code via git
3. run `bower update`
4. restart your server
## Features
* We've introduced a word-count feature in our rich text editor.
* The "share modal" which is accessible from both the "right-click menu" in the drive and the sharing button in the toolbar has been redesigned:
* different means of sharing access to documents have been split into different tabs to present users with less information to process
* each sharing method has an associated icon to make their actions easier to recognize at a glance
* various UI elements have been restyled to make their purpose and importance more obvious
* cancel buttons have a grey border to draw less attention
* OK buttons have a blue or grey background depending on whether they are active
* secondary buttons like "preview" have only a thin blue border so that they don't draw attention away from the primary button
* read-only text fields have a subtler appearance since they are shown primarily for the purpose of previewing your action
* text input fields (such as search) have a light background to suggest that you can use them
* We've made a minor adjustment to some of our styles for small screen to detect when a screen is very short in addition to when it is very narrow. As a result it should be somewhat easier to use on-screen keyboards.
## Bug fixes
* We found and fixed a subtle race condition which caused teams' quotas to be calculated incorrectly in certain circumstances.
* A minor bug in our login process caused users with premium accounts to incorrectly see an entry in their user menu as linking to our 'pricing' page instead of their 'subscription' management tools. This has since been fixed.
* We noticed that some of the rendered messages in the history mode of the notifications panel could fail to display text for some message types. These incorrect messages will be hidden from view wherever it is impossible to decide what should be displayed. We plan to address the issue in a deeper way in the near future.
* We've become aware of some odd behaviour in long-lived sessions where tabs seem to lose their connection to the sharedWorker which is common to all tabs open in a particular browser session. As far as we can tell the bug only affects Firefox browser. Unfortunately, debugging sharedWorkers in Firefox has been broken for a number of major versions, so we haven't been able to determine the cause of the issue. Until we're able to determine the underlying cause we've added extra checks to detect when particular features become isolated from the worker, where previously we assumed that if the worker was connected to the server then everything was behaving correctly. We recommend that you reload the tab if you notice that aspects of your shared folders or drives (for users or teams) display a read-only warning while your other tabs are behaving normally.
# FalklandWolf release (3.5.0) # FalklandWolf release (3.5.0)
## Goals ## Goals

View File

@ -18,7 +18,7 @@
"tests" "tests"
], ],
"dependencies": { "dependencies": {
"jquery": "~2.1.3", "jquery": "2.2.4",
"tweetnacl": "0.12.2", "tweetnacl": "0.12.2",
"components-font-awesome": "^4.6.3", "components-font-awesome": "^4.6.3",
"ckeditor": "4.7.3", "ckeditor": "4.7.3",
@ -51,6 +51,7 @@
"requirejs-plugins": "^1.0.3" "requirejs-plugins": "^1.0.3"
}, },
"resolutions": { "resolutions": {
"bootstrap": "^v4.0.0" "bootstrap": "^v4.0.0",
"jquery": "2.2.4"
} }
} }

View File

@ -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;
}
}
}
} }

View File

@ -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;

View File

@ -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;

View File

@ -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%);
} }
} }
} }

View File

@ -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-bottom: 6px;
}
&:not(.large) {
.cp-usergrid-grid {
margin: -3px; margin: -3px;
margin-bottom: 6px; margin-bottom: 6px;
max-height: 130px;
overflow-y: auto;
@media screen and (max-height: 515px) {
max-height: unset; // remove double scrollbar
} }
} }
&.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;
} }
} }
} }

View File

@ -0,0 +1,14 @@
/*
* You can override the translation text using this file.
* The recommended method is to make a copy of this file (/customize.dist/translations/messages.{LANG}.js)
in a 'customize' directory (/customize/translations/messages.{LANG}.js).
* If you want to check all the existing translation keys, you can open the internal language file
but you should not change it directly (/common/translations/messages.{LANG}.js)
*/
define(['/common/translations/messages.fi.js'], function (Messages) {
// Replace the existing keys in your copied file here:
// Messages.button_newpad = "New Rich Text Document";
return Messages;
});

View File

@ -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];
}); });
});
}; };
}; };

View File

@ -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) {
setTimeout(function () {
if (map[id] && map[id].length === 0) { return void delete map[id]; } if (map[id] && map[id].length === 0) { return void delete map[id]; }
var task = map[id].shift(); var task = map[id].shift();
task(fix1(next, id)); task(fix1(next, id));
});
}; };
return function (id, task) { return function (id, task) {

View File

@ -7,6 +7,10 @@
"type": "git", "type": "git",
"url": "git://github.com/xwiki-labs/cryptpad.git" "url": "git://github.com/xwiki-labs/cryptpad.git"
}, },
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/cryptpad"
},
"dependencies": { "dependencies": {
"chainpad-crypto": "^0.2.2", "chainpad-crypto": "^0.2.2",
"chainpad-server": "^3.0.5", "chainpad-server": "^3.0.5",

46
rpc.js
View File

@ -228,13 +228,15 @@ var truthyKeys = function (O) {
}); });
}; };
var getChannelList = function (Env, publicKey, cb) { var getChannelList = function (Env, publicKey, _cb) {
var cb = Util.once(Util.mkAsync(_cb));
loadUserPins(Env, publicKey, function (pins) { loadUserPins(Env, publicKey, function (pins) {
cb(truthyKeys(pins)); cb(truthyKeys(pins));
}); });
}; };
var getFileSize = function (Env, channel, cb) { var getFileSize = function (Env, channel, _cb) {
var cb = Util.once(Util.mkAsync(_cb));
if (!isValidId(channel)) { return void cb('INVALID_CHAN'); } if (!isValidId(channel)) { return void cb('INVALID_CHAN'); }
if (channel.length === 32) { if (channel.length === 32) {
if (typeof(Env.msgStore.getChannelSize) !== 'function') { if (typeof(Env.msgStore.getChannelSize) !== 'function') {
@ -416,14 +418,41 @@ var getDeletedPads = function (Env, channels, cb) {
const batchTotalSize = BatchRead("GET_TOTAL_SIZE"); const batchTotalSize = BatchRead("GET_TOTAL_SIZE");
var getTotalSize = function (Env, publicKey, cb) { var getTotalSize = function (Env, publicKey, cb) {
batchTotalSize(publicKey, cb, function (done) { var unescapedKey = unescapeKeyCharacters(publicKey);
var bytes = 0; var limit = Env.limits[unescapedKey];
return void getChannelList(Env, publicKey, function (channels) {
if (!channels) { return done('INVALID_PIN_LIST'); } // unexpected
nThen(function (w) { // Get a common key if multiple users share the same quota, otherwise take the public key
var batchKey = (limit && Array.isArray(limit.users)) ? limit.users.join('') : publicKey;
batchTotalSize(batchKey, cb, function (done) {
var channels = [];
var bytes = 0;
nThen(function (waitFor) {
// Get the channels list for our user account
getChannelList(Env, publicKey, waitFor(function (_channels) {
if (!_channels) {
waitFor.abort();
return done('INVALID_PIN_LIST');
}
Array.prototype.push.apply(channels, _channels);
}));
// Get the channels list for users sharing our quota
if (limit && Array.isArray(limit.users) && limit.users.length > 1) {
limit.users.forEach(function (key) {
if (key === unescapedKey) { return; } // Don't count ourselves twice
getChannelList(Env, key, waitFor(function (_channels) {
if (!_channels) { return; } // Broken user, don't count their quota
Array.prototype.push.apply(channels, _channels);
}));
});
}
}).nThen(function (waitFor) {
// Get size of the channels
var list = []; // Contains the channels already counted in the quota to avoid duplicates
channels.forEach(function (channel) { // TODO semaphore? channels.forEach(function (channel) { // TODO semaphore?
getFileSize(Env, channel, w(function (e, size) { if (list.indexOf(channel) !== -1) { return; }
list.push(channel);
getFileSize(Env, channel, waitFor(function (e, size) {
if (!e) { bytes += size; } if (!e) { bytes += size; }
})); }));
}); });
@ -431,7 +460,6 @@ var getTotalSize = function (Env, publicKey, cb) {
done(void 0, bytes); done(void 0, bytes);
}); });
}); });
});
}; };
var hashChannelList = function (A) { var hashChannelList = function (A) {

View File

@ -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();
});
});
}); });

View File

@ -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 () {

View File

@ -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;

View File

@ -6,22 +6,24 @@ define([
CodeMirror.defineSimpleMode("orgmode", { CodeMirror.defineSimpleMode("orgmode", {
start: [ start: [
{regex: /^(^\*{1,6}\s)(TODO|DOING|WAITING|NEXT){0,1}(CANCELLED|CANCEL|DEFERRED|DONE|REJECTED|STOP|STOPPED){0,1}(.*)$/, token: ["header org-level-star", "header org-todo", "header org-done", "header"]}, {regex: /(\*\s)(TODO|DOING|WAITING|NEXT|PENDING|)(CANCELLED|CANCELED|CANCEL|DONE|REJECTED|STOP|STOPPED|)(\s+\[\#[A-C]\]\s+|)(.*?)(?:(\s{10,}|))(\:[\S]+\:|)$/, sol: true, token: ["header level1 org-level-star","header level1 org-todo","header level1 org-done", "header level1 org-priority", "header level1", "header level1 void", "header level1 comment"]},
{regex: /(\*{1,}\s)(TODO|DOING|WAITING|NEXT|PENDING|)(CANCELLED|CANCELED|CANCEL|DEFERRED|DONE|REJECTED|STOP|STOPPED|)(\s+\[\#[A-C]\]\s+|)(.*?)(?:(\s{10,}|))(\:[\S]+\:|)$/, sol: true, token: ["header org-level-star","header org-todo","header org-done", "header org-priority", "header", "header void", "header comment"]},
{regex: /(\+[^\+]+\+)/, token: ["strikethrough"]}, {regex: /(\+[^\+]+\+)/, token: ["strikethrough"]},
{regex: /(\*[^\*]+\*)/, token: ["strong"]}, {regex: /(\*[^\*]+\*)/, token: ["strong"]},
{regex: /(\/[^\/]+\/)/, token: ["em"]}, {regex: /(\/[^\/]+\/)/, token: ["em"]},
{regex: /(\_[^\_]+\_)/, token: ["link"]}, {regex: /(\_[^\_]+\_)/, token: ["link"]},
{regex: /(\~[^\~]+\~)/, token: ["comment"]}, {regex: /(\~[^\~]+\~)/, token: ["comment"]},
{regex: /(\=[^\=]+\=)/, token: ["comment"]}, {regex: /(\=[^\=]+\=)/, token: ["comment"]},
{regex: /\[\[[^\[\]]*\]\[[^\[\]]*\]\]/, token: "url"}, // links {regex: /\[\[[^\[\]]+\]\[[^\[\]]+\]\]/, token: "org-url"}, // links
{regex: /\[[xX\s]?\]/, token: 'qualifier'}, // checkbox {regex: /\[\[[^\[\]]+\]\]/, token: "org-image"}, // image
{regex: /\#\+BEGIN_[A-Z]*/, token: "comment", next: "env"}, // comments {regex: /\[[xX\s\-\_]\]/, token: 'qualifier org-toggle'}, // checkbox
{regex: /:?[A-Z_]+\:.*/, token: "comment"}, // property drawers {regex: /\#\+(?:(BEGIN|begin))_[a-zA-Z]*/, token: "comment", next: "env", sol: true}, // comments
{regex: /(\#\+[A-Z_]*)(\:.*)/, token: ["keyword", 'qualifier']}, // environments {regex: /:?[A-Z_]+\:.*/, token: "comment", sol: true}, // property drawers
{regex: /(CLOCK\:|SHEDULED\:)(\s.+)/, token: ["comment", "keyword"]} {regex: /(\#\+[a-zA-Z_]*)(\:.*)/, token: ["keyword", 'qualifier'], sol: true}, // environments
{regex: /(CLOCK\:|SHEDULED\:|DEADLINE\:)(\s.+)/, token: ["comment", "keyword"]}
], ],
env: [ env: [
{regex: /.*?\#\+END_[A-Z]*/, token: "comment", next: "start"}, {regex: /\#\+(?:(END|end))_[a-zA-Z]*/, token: "comment", next: "start", sol: true},
{regex: /.*/, token: "comment"} {regex: /.*/, token: "comment"}
] ]
}); });

View File

@ -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);
@ -187,7 +199,8 @@ define([
dialog.tabs = function (tabs) { dialog.tabs = function (tabs) {
var contents = []; var contents = [];
var titles = []; var titles = [];
tabs.forEach(function (tab) { var active = 0;
tabs.forEach(function (tab, i) {
if (!tab.content || !tab.title) { return; } if (!tab.content || !tab.title) { return; }
var content = h('div.alertify-tabs-content', tab.content); var content = h('div.alertify-tabs-content', tab.content);
var title = h('span.alertify-tabs-title', tab.title); var title = h('span.alertify-tabs-title', tab.title);
@ -203,10 +216,11 @@ define([
}); });
titles.push(title); titles.push(title);
contents.push(content); contents.push(content);
if (tab.active) { active = i; }
}); });
if (contents.length) { if (contents.length) {
$(contents[0]).addClass('alertify-tabs-content-active'); $(contents[active]).addClass('alertify-tabs-content-active');
$(titles[0]).addClass('alertify-tabs-active'); $(titles[active]).addClass('alertify-tabs-active');
} }
return h('div.alertify-tabs', [ return h('div.alertify-tabs', [
h('div.alertify-tabs-titles', titles), h('div.alertify-tabs-titles', titles),

View File

@ -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 () {});
@ -739,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([{
@ -784,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
}); });
@ -793,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);
@ -808,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 () {
@ -885,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);
@ -911,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);
@ -1016,12 +1012,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} }),
@ -1068,9 +1081,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);
@ -1137,7 +1183,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 = [makeCancelButton(), var contactButtons = [makeCancelButton(),
friendsObject.button]; friendsObject.button];
@ -1156,9 +1214,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',
@ -1187,13 +1254,15 @@ define([
// Create modal // Create modal
var tabs = [{ var tabs = [{
title: Messages.share_linkCategory,
icon: "fa fa-link",
content: frameLink
}, {
title: Messages.share_contactCategory, title: Messages.share_contactCategory,
icon: "fa fa-address-book", icon: "fa fa-address-book",
content: frameContacts content: frameContacts,
active: hasFriends
}, {
title: Messages.share_linkCategory,
icon: "fa fa-link",
content: frameLink,
active: !hasFriends
}, { }, {
title: Messages.share_embedCategory, title: Messages.share_embedCategory,
icon: "fa fa-code", icon: "fa fa-code",
@ -1261,6 +1330,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() {
@ -1272,9 +1356,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 = [
@ -1307,7 +1422,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 = [makeCancelButton(), var contactButtons = [makeCancelButton(),
friendsObject.button]; friendsObject.button];
@ -1321,12 +1446,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,
@ -1349,13 +1482,15 @@ define([
// Create modal // Create modal
var tabs = [{ var tabs = [{
title: Messages.share_linkCategory,
icon: "fa fa-link",
content: frameLink
}, {
title: Messages.share_contactCategory, title: Messages.share_contactCategory,
icon: "fa fa-address-book", icon: "fa fa-address-book",
content: frameContacts content: frameContacts,
active: hasFriends,
}, {
title: Messages.share_linkCategory,
icon: "fa fa-link",
content: frameLink,
active: !hasFriends
}, { }, {
title: Messages.share_embedCategory, title: Messages.share_embedCategory,
icon: "fa fa-code", icon: "fa fa-code",
@ -1759,7 +1894,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]);
}); });
}); });
}); });

View File

@ -84,7 +84,7 @@ define([
var defaultCode = renderer.code; var defaultCode = renderer.code;
renderer.code = function (code, language) { renderer.code = function (code, language) {
if (language === 'mermaid' && (code.match(/^sequenceDiagram/) || code.match(/^graph/))) { if (language === 'mermaid' && code.match(/^(graph|pie|gantt|sequenceDiagram|classDiagram|gitGraph)/)) {
return '<pre class="mermaid">'+code+'</pre>'; return '<pre class="mermaid">'+code+'</pre>';
} else { } else {
return defaultCode.apply(renderer, arguments); return defaultCode.apply(renderer, arguments);
@ -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); }
} }
}; };

View File

@ -79,7 +79,7 @@ define([
var faColor = 'cptools-palette'; var faColor = 'cptools-palette';
var faTrash = 'fa-trash'; var faTrash = 'fa-trash';
var faDelete = 'fa-eraser'; var faDelete = 'fa-eraser';
var faProperties = 'fa-database'; var faProperties = 'fa-info-circle';
var faTags = 'fa-hashtag'; var faTags = 'fa-hashtag';
var faUploadFiles = 'cptools-file-upload'; var faUploadFiles = 'cptools-file-upload';
var faUploadFolder = 'cptools-folder-upload'; var faUploadFolder = 'cptools-folder-upload';
@ -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")) {

View File

@ -43,7 +43,7 @@ define([
MessengerUI.create = function ($container, common, toolbar) { MessengerUI.create = function ($container, common, toolbar) {
var metadataMgr = common.getMetadataMgr(); var metadataMgr = common.getMetadataMgr();
var origin = metadataMgr.getPrivateData().origin; var origin = metadataMgr.getPrivateData().origin;
var readOnly = metadataMgr.getPrivateData().readOnly || toolbar.readOnly; var readOnly = metadataMgr.getPrivateData().readOnly || (toolbar && toolbar.readOnly);
var isApp = typeof(toolbar) !== "undefined"; var isApp = typeof(toolbar) !== "undefined";

View File

@ -769,18 +769,18 @@ define([
continue; continue;
} }
var href; var decryptedHref;
try { try {
href = el.href && ((el.href.indexOf('#') !== -1) ? el.href : exp.cryptor.decrypt(el.href)); decryptedHref = el.href && ((el.href.indexOf('#') !== -1) ? el.href : exp.cryptor.decrypt(el.href));
} catch (e) {} } catch (e) {}
if (href && href.indexOf('#') === -1) { if (decryptedHref && decryptedHref.indexOf('#') === -1) {
// If we can't decrypt the href, it means we don't have the correct secondaryKey and we're in readOnly mode: // If we can't decrypt the href, it means we don't have the correct secondaryKey and we're in readOnly mode:
// abort now, we won't be able to fix anything anyway // abort now, we won't be able to fix anything anyway
continue; continue;
} }
var parsed = Hash.parsePadUrl(href || el.roHref); var parsed = Hash.parsePadUrl(decryptedHref || el.roHref);
var secret; var secret;
// Clean invalid hash // Clean invalid hash
@ -797,9 +797,9 @@ define([
} }
// If we have an edit link, check the view link // If we have an edit link, check the view link
if (href && parsed.hashData.type === "pad" && parsed.hashData.version) { if (decryptedHref && parsed.hashData.type === "pad" && parsed.hashData.version) {
if (parsed.hashData.mode === "view") { if (parsed.hashData.mode === "view") {
el.roHref = href; el.roHref = decryptedHref;
delete el.href; delete el.href;
} else if (!el.roHref) { } else if (!el.roHref) {
secret = Hash.getSecrets(parsed.type, parsed.hash, el.password); secret = Hash.getSecrets(parsed.type, parsed.hash, el.password);
@ -818,7 +818,9 @@ define([
} }
// Fix href // Fix href
if (href && href.slice(0,1) !== '/') { el.href = exp.cryptor.encrypt(Hash.getRelativeHref(el.href)); } if (decryptedHref && decryptedHref.slice(0,1) !== '/') {
el.href = exp.cryptor.encrypt(Hash.getRelativeHref(decryptedHref));
}
// Fix creation time // Fix creation time
if (!el.ctime) { el.ctime = el.atime; } if (!el.ctime) { el.ctime = el.atime; }
// Fix title // Fix title

View File

@ -488,6 +488,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({

View File

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

View File

@ -54,6 +54,7 @@
.kanban-item-text { .kanban-item-text {
cursor: text; cursor: text;
overflow-wrap: anywhere; overflow-wrap: anywhere;
flex: 1;
} }
} }
@ -67,7 +68,7 @@
margin-right: 10px; margin-right: 10px;
min-width: 0; min-width: 0;
overflow: hidden; overflow: hidden;
white-space: nowrap; //white-space: nowrap;
text-overflow: ellipsis; text-overflow: ellipsis;
} }
#kanban-edit { #kanban-edit {

View File

@ -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();

View File

@ -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;
}, },
@ -349,7 +353,7 @@
titleBoard = document.createElement('div'); titleBoard = document.createElement('div');
titleBoard.classList.add('kanban-title-board'); titleBoard.classList.add('kanban-title-board');
titleBoard.innerHTML = board.title; titleBoard.innerHTML = board.title;
titleBoard.setAttribute('title', board.title); //titleBoard.setAttribute('title', board.title);
titleBoard.clickfn = board.boardTitleClick; titleBoard.clickfn = board.boardTitleClick;
__onboardTitleClickHandler(titleBoard); __onboardTitleClickHandler(titleBoard);
headerBoard.appendChild(titleBoard); headerBoard.appendChild(titleBoard);

View File

@ -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);
} }
}); });