mirror of https://github.com/xwiki-labs/cryptpad
Merge branch 'staging' into restricted-registration
This commit is contained in:
commit
30fc2a5edf
|
@ -5,6 +5,9 @@
|
||||||
* reminders in calendars
|
* reminders in calendars
|
||||||
* import/export
|
* import/export
|
||||||
* include LICENSE for ical.js
|
* include LICENSE for ical.js
|
||||||
|
* translations
|
||||||
|
* out of BETA
|
||||||
|
* available from user admin menu
|
||||||
* use a specific version of bootstrap-tokenfield in bower.json
|
* use a specific version of bootstrap-tokenfield in bower.json
|
||||||
* don't create readmes
|
* don't create readmes
|
||||||
* support displaying a roadmap in static pages' footer
|
* support displaying a roadmap in static pages' footer
|
||||||
|
@ -18,6 +21,12 @@
|
||||||
* lock sheets faster when applying checkpoints
|
* lock sheets faster when applying checkpoints
|
||||||
* guard against undefined checkpoints
|
* guard against undefined checkpoints
|
||||||
* don't spam users with prompts to checkpoints when they can't
|
* don't spam users with prompts to checkpoints when they can't
|
||||||
|
* decrees
|
||||||
|
* SET_ADMIN_EMAIL
|
||||||
|
* SET_SUPPORT_MAILBOX
|
||||||
|
* Add DAPSI to our sponsor list
|
||||||
|
* checkup
|
||||||
|
* check for duplicate or incorrect headers
|
||||||
|
|
||||||
# 4.4.0
|
# 4.4.0
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,10 @@ Core.isValidId = function (chan) {
|
||||||
[32, 33, 48].indexOf(chan.length) > -1;
|
[32, 33, 48].indexOf(chan.length) > -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Core.isValidPublicKey = function (owner) {
|
||||||
|
return typeof(owner) === 'string' && owner.length === 44;
|
||||||
|
};
|
||||||
|
|
||||||
var makeToken = Core.makeToken = function () {
|
var makeToken = Core.makeToken = function () {
|
||||||
return Number(Math.floor(Math.random() * Number.MAX_SAFE_INTEGER))
|
return Number(Math.floor(Math.random() * Number.MAX_SAFE_INTEGER))
|
||||||
.toString(16);
|
.toString(16);
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
var Decrees = module.exports;
|
var Decrees = module.exports;
|
||||||
|
var Core = require("./commands/core");
|
||||||
|
|
||||||
/* Admin decrees which modify global server state
|
/* Admin decrees which modify global server state
|
||||||
|
|
||||||
|
@ -29,6 +30,10 @@ SET_LAST_BROADCAST_HASH
|
||||||
SET_SURVEY_URL
|
SET_SURVEY_URL
|
||||||
SET_MAINTENANCE
|
SET_MAINTENANCE
|
||||||
|
|
||||||
|
// EASIER CONFIG
|
||||||
|
SET_ADMIN_EMAIL
|
||||||
|
SET_SUPPORT_MAILBOX
|
||||||
|
|
||||||
NOT IMPLEMENTED:
|
NOT IMPLEMENTED:
|
||||||
|
|
||||||
// RESTRICTED REGISTRATION
|
// RESTRICTED REGISTRATION
|
||||||
|
@ -37,9 +42,11 @@ REVOKE_INVITE
|
||||||
REDEEM_INVITE
|
REDEEM_INVITE
|
||||||
|
|
||||||
// 2.0
|
// 2.0
|
||||||
Env.adminEmail
|
|
||||||
Env.supportMailbox
|
|
||||||
Env.DEV_MODE || Env.FRESH_MODE,
|
Env.DEV_MODE || Env.FRESH_MODE,
|
||||||
|
|
||||||
|
ADD_ADMIN_KEY
|
||||||
|
RM_ADMIN_KEY
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var commands = {};
|
var commands = {};
|
||||||
|
@ -88,6 +95,20 @@ var isNonNegativeNumber = function (n) {
|
||||||
};
|
};
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
var default_validator = function () { return true; };
|
||||||
|
var makeGenericSetter = function (attr, validator) {
|
||||||
|
validator = validator || default_validator;
|
||||||
|
return function (Env, args) {
|
||||||
|
if (!validator(args)) {
|
||||||
|
throw new Error("INVALID_ARGS");
|
||||||
|
}
|
||||||
|
var value = args[0];
|
||||||
|
if (value === Env[attr]) { return false; }
|
||||||
|
Env[attr] = value;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
var isInteger = function (n) {
|
var isInteger = function (n) {
|
||||||
return !(typeof(n) !== 'number' || isNaN(n) || (n % 1) !== 0);
|
return !(typeof(n) !== 'number' || isNaN(n) || (n % 1) !== 0);
|
||||||
};
|
};
|
||||||
|
@ -97,15 +118,7 @@ var args_isInteger = function (args) {
|
||||||
};
|
};
|
||||||
|
|
||||||
var makeIntegerSetter = function (attr) {
|
var makeIntegerSetter = function (attr) {
|
||||||
return function (Env, args) {
|
return makeGenericSetter(attr, args_isInteger);
|
||||||
if (!args_isInteger(args)) {
|
|
||||||
throw new Error('INVALID_ARGS');
|
|
||||||
}
|
|
||||||
var integer = args[0];
|
|
||||||
if (integer === Env[attr]) { return false; }
|
|
||||||
Env[attr] = integer;
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// CryptPad_AsyncStore.rpc.send('ADMIN', [ 'ADMIN_DECREE', ['SET_MAX_UPLOAD_SIZE', [50 * 1024 * 1024]]], console.log)
|
// CryptPad_AsyncStore.rpc.send('ADMIN', [ 'ADMIN_DECREE', ['SET_MAX_UPLOAD_SIZE', [50 * 1024 * 1024]]], console.log)
|
||||||
|
@ -130,6 +143,14 @@ var args_isString = function (args) {
|
||||||
return Array.isArray(args) && typeof(args[0]) === "string";
|
return Array.isArray(args) && typeof(args[0]) === "string";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// CryptPad_AsyncStore.rpc.send('ADMIN', [ 'ADMIN_DECREE', ['SET_ADMIN_EMAIL', ['admin@website.tld']]], console.log)
|
||||||
|
commands.SET_ADMIN_EMAIL = makeGenericSetter('adminEmail', args_isString);
|
||||||
|
|
||||||
|
// CryptPad_AsyncStore.rpc.send('ADMIN', [ 'ADMIN_DECREE', ['SET_SUPPORT_MAILBOX', ["Tdz6+fE9N9XXBY93rW5qeNa/k27yd40c0vq7EJyt7jA="]]], console.log)
|
||||||
|
commands.SET_SUPPORT_MAILBOX = makeGenericSetter('supportMailbox', function (args) {
|
||||||
|
return args_isString(args) && Core.isValidPublicKey(args[0]);
|
||||||
|
});
|
||||||
|
|
||||||
// Maintenance: Empty string or an object with a start and end time
|
// Maintenance: Empty string or an object with a start and end time
|
||||||
var isNumber = function (value) {
|
var isNumber = function (value) {
|
||||||
return typeof(value) === "number" && !isNaN(value);
|
return typeof(value) === "number" && !isNaN(value);
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
var Meta = module.exports;
|
var Meta = module.exports;
|
||||||
|
var Core = require("./commands/core");
|
||||||
|
|
||||||
var deduplicate = require("./common-util").deduplicateString;
|
var deduplicate = require("./common-util").deduplicateString;
|
||||||
|
|
||||||
|
@ -35,9 +36,7 @@ the owners field is guaranteed to exist.
|
||||||
|
|
||||||
var commands = {};
|
var commands = {};
|
||||||
|
|
||||||
var isValidPublicKey = function (owner) {
|
var isValidPublicKey = Core.isValidPublicKey;
|
||||||
return typeof(owner) === 'string' && owner.length === 44;
|
|
||||||
};
|
|
||||||
|
|
||||||
// isValidPublicKey is a better indication of what the above function does
|
// isValidPublicKey is a better indication of what the above function does
|
||||||
// I'm preserving this function name in case we ever want to expand its
|
// I'm preserving this function name in case we ever want to expand its
|
||||||
|
|
|
@ -105,13 +105,15 @@ var setHeaders = (function () {
|
||||||
}
|
}
|
||||||
if (Object.keys(headers).length) {
|
if (Object.keys(headers).length) {
|
||||||
return function (req, res) {
|
return function (req, res) {
|
||||||
|
|
||||||
// apply a bunch of cross-origin headers for XLSX export in FF and printing elsewhere
|
// apply a bunch of cross-origin headers for XLSX export in FF and printing elsewhere
|
||||||
applyHeaderMap(res, {
|
applyHeaderMap(res, {
|
||||||
"Cross-Origin-Opener-Policy": /^\/sheet\//.test(req.url)? 'same-origin': '',
|
"Cross-Origin-Opener-Policy": /^\/sheet\//.test(req.url)? 'same-origin': '',
|
||||||
"Cross-Origin-Embedder-Policy": 'require-corp',
|
"Cross-Origin-Embedder-Policy": 'require-corp',
|
||||||
});
|
});
|
||||||
|
|
||||||
if (Env.NO_SANDBOX) {
|
if (Env.NO_SANDBOX) { // handles correct configuration for local development
|
||||||
|
// https://stackoverflow.com/questions/11531121/add-duplicate-http-response-headers-in-nodejs
|
||||||
applyHeaderMap(res, {
|
applyHeaderMap(res, {
|
||||||
"Cross-Origin-Resource-Policy": 'cross-origin',
|
"Cross-Origin-Resource-Policy": 'cross-origin',
|
||||||
});
|
});
|
||||||
|
@ -120,11 +122,13 @@ var setHeaders = (function () {
|
||||||
// Don't set CSP headers on /api/config because they aren't necessary and they cause problems
|
// Don't set CSP headers on /api/config because they aren't necessary and they cause problems
|
||||||
// when duplicated by NGINX in production environments
|
// when duplicated by NGINX in production environments
|
||||||
if (/^\/api\/(broadcast|config)/.test(req.url)) {
|
if (/^\/api\/(broadcast|config)/.test(req.url)) {
|
||||||
if (!Env.NO_SANDBOX) {
|
/*
|
||||||
|
if (Env.NO_SANDBOX) {
|
||||||
applyHeaderMap(res, {
|
applyHeaderMap(res, {
|
||||||
"Cross-Origin-Resource-Policy": 'cross-origin',
|
"Cross-Origin-Resource-Policy": 'cross-origin',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
applyHeaderMap(res, {
|
applyHeaderMap(res, {
|
||||||
|
|
|
@ -365,7 +365,7 @@ define([
|
||||||
});
|
});
|
||||||
|
|
||||||
assert(function (cb, msg) {
|
assert(function (cb, msg) {
|
||||||
msg = msg; // XXX
|
msg = msg;
|
||||||
return void cb(true);
|
return void cb(true);
|
||||||
/*
|
/*
|
||||||
msg.appendChild(h('span', [
|
msg.appendChild(h('span', [
|
||||||
|
@ -415,6 +415,58 @@ define([
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var checkAPIHeaders = function (url, cb) {
|
||||||
|
$.ajax(url, {
|
||||||
|
dataType: 'text',
|
||||||
|
complete: function (xhr) {
|
||||||
|
var allHeaders = xhr.getAllResponseHeaders();
|
||||||
|
console.error(allHeaders);
|
||||||
|
|
||||||
|
var headers = {};
|
||||||
|
|
||||||
|
var duplicated = allHeaders.split('\n').some(function (header) {
|
||||||
|
var duplicate;
|
||||||
|
header.replace(/([^:]+):(.*)/, function (all, type, value) {
|
||||||
|
type = type.trim();
|
||||||
|
if (typeof(headers[type]) !== 'undefined') {
|
||||||
|
duplicate = true;
|
||||||
|
}
|
||||||
|
headers[type] = value.trim();
|
||||||
|
});
|
||||||
|
return duplicate;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (duplicated) { return void cb(false); }
|
||||||
|
|
||||||
|
var expect = {
|
||||||
|
'cross-origin-resource-policy': 'cross-origin',
|
||||||
|
};
|
||||||
|
var incorrect = Object.keys(expect).some(function (k) {
|
||||||
|
var response = xhr.getResponseHeader(k);
|
||||||
|
if (response !== expect[k]) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
cb(!incorrect);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var INCORRECT_HEADER_TEXT = ' was served with duplicated or incorrect headers. Compare your reverse-proxy configuration against the provided example.';
|
||||||
|
|
||||||
|
assert(function (cb, msg) {
|
||||||
|
var url = '/api/config';
|
||||||
|
msg.innerText = url + INCORRECT_HEADER_TEXT;
|
||||||
|
checkAPIHeaders(url, cb);
|
||||||
|
});
|
||||||
|
|
||||||
|
assert(function (cb, msg) {
|
||||||
|
var url = '/api/broadcast';
|
||||||
|
msg.innerText = url + INCORRECT_HEADER_TEXT;
|
||||||
|
checkAPIHeaders(url, cb);
|
||||||
|
});
|
||||||
|
|
||||||
var row = function (cells) {
|
var row = function (cells) {
|
||||||
return h('tr', cells.map(function (cell) {
|
return h('tr', cells.map(function (cell) {
|
||||||
return h('td', cell);
|
return h('td', cell);
|
||||||
|
|
|
@ -798,7 +798,6 @@ define([
|
||||||
])).click(common.prepareFeedback(type)).click(function () {
|
])).click(common.prepareFeedback(type)).click(function () {
|
||||||
$(button).hide();
|
$(button).hide();
|
||||||
common.getSframeChannel().query("Q_AUTOSTORE_STORE", null, function (err, obj) {
|
common.getSframeChannel().query("Q_AUTOSTORE_STORE", null, function (err, obj) {
|
||||||
waitingForStoringCb = false;
|
|
||||||
var error = err || (obj && obj.error);
|
var error = err || (obj && obj.error);
|
||||||
if (error) {
|
if (error) {
|
||||||
$(button).show();
|
$(button).show();
|
||||||
|
|
|
@ -40,6 +40,7 @@ define([
|
||||||
Mermaid = _Mermaid;
|
Mermaid = _Mermaid;
|
||||||
Mermaid.initialize({
|
Mermaid.initialize({
|
||||||
gantt: { axisFormat: '%m-%d', },
|
gantt: { axisFormat: '%m-%d', },
|
||||||
|
flowchart: { htmlLabels: false, },
|
||||||
theme: (window.CryptPad_theme === 'dark') ? 'dark' : 'default',
|
theme: (window.CryptPad_theme === 'dark') ? 'dark' : 'default',
|
||||||
"themeCSS": mermaidThemeCSS,
|
"themeCSS": mermaidThemeCSS,
|
||||||
});
|
});
|
||||||
|
|
|
@ -386,11 +386,11 @@ define([
|
||||||
'tabindex': '-1',
|
'tabindex': '-1',
|
||||||
'data-icon': "fa-eye",
|
'data-icon': "fa-eye",
|
||||||
}, Messages.pad_mediatagPreview)),
|
}, Messages.pad_mediatagPreview)),
|
||||||
h('li.cp-svg', h('a.cp-app-code-context-openin.dropdown-item', {
|
h('li', h('a.cp-app-code-context-openin.dropdown-item', {
|
||||||
'tabindex': '-1',
|
'tabindex': '-1',
|
||||||
'data-icon': "fa-external-link",
|
'data-icon': "fa-external-link",
|
||||||
}, Messages.pad_mediatagOpen)),
|
}, Messages.pad_mediatagOpen)),
|
||||||
h('li.cp-svg', h('a.cp-app-code-context-share.dropdown-item', {
|
h('li', h('a.cp-app-code-context-share.dropdown-item', {
|
||||||
'tabindex': '-1',
|
'tabindex': '-1',
|
||||||
'data-icon': "fa-shhare-alt",
|
'data-icon': "fa-shhare-alt",
|
||||||
}, Messages.pad_mediatagShare)),
|
}, Messages.pad_mediatagShare)),
|
||||||
|
@ -398,7 +398,7 @@ define([
|
||||||
'tabindex': '-1',
|
'tabindex': '-1',
|
||||||
'data-icon': "fa-cloud-upload",
|
'data-icon': "fa-cloud-upload",
|
||||||
}, Messages.pad_mediatagImport)),
|
}, Messages.pad_mediatagImport)),
|
||||||
h('li', h('a.cp-app-code-context-download.dropdown-item', {
|
h('li.cp-svg', h('a.cp-app-code-context-download.dropdown-item', {
|
||||||
'tabindex': '-1',
|
'tabindex': '-1',
|
||||||
'data-icon': "fa-download",
|
'data-icon': "fa-download",
|
||||||
}, Messages.download_mt_button)),
|
}, Messages.download_mt_button)),
|
||||||
|
@ -429,6 +429,52 @@ define([
|
||||||
common.importMediaTag($mt);
|
common.importMediaTag($mt);
|
||||||
}
|
}
|
||||||
else if ($this.hasClass("cp-app-code-context-download")) {
|
else if ($this.hasClass("cp-app-code-context-download")) {
|
||||||
|
if ($mt.is('pre.mermaid') || $mt.is('pre.markmap')) {
|
||||||
|
(function () {
|
||||||
|
var name = 'image.svg'; // XXX
|
||||||
|
var svg = $mt.find('svg')[0].cloneNode(true);
|
||||||
|
$(svg).attr('xmlns', 'http://www.w3.org/2000/svg').attr('width', $mt.width()).attr('height', $mt.height());
|
||||||
|
$(svg).find('foreignObject').each(function (i, el) {
|
||||||
|
var $el = $(el);
|
||||||
|
$el.find('br').after('\n');
|
||||||
|
$el.find('br').remove();
|
||||||
|
var t = $el[0].innerText || $el[0].textContent;
|
||||||
|
t.split('\n').forEach(function (text, i) {
|
||||||
|
var dy = (i+1)+'em';
|
||||||
|
$el.after(h('text', {y:0, dy:dy, style: ''}, text));
|
||||||
|
});
|
||||||
|
$el.remove();
|
||||||
|
});
|
||||||
|
var html = svg.outerHTML;
|
||||||
|
html = html.replace('<br>', '<br/>');
|
||||||
|
var b = new Blob([html], { type: 'image/svg+xml' });
|
||||||
|
window.saveAs(b, name);
|
||||||
|
})();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ($mt.is('pre.mathjax')) {
|
||||||
|
(function () {
|
||||||
|
var name = 'image.png'; // XXX
|
||||||
|
var svg = $mt.find('> span > svg')[0];
|
||||||
|
var clone = svg.cloneNode(true);
|
||||||
|
var html = clone.outerHTML;
|
||||||
|
var b = new Blob([html], { type: 'image/svg+xml' });
|
||||||
|
var blobURL = URL.createObjectURL(b);
|
||||||
|
var i = new Image();
|
||||||
|
i.onload = function () {
|
||||||
|
var canvas = document.createElement('canvas');
|
||||||
|
canvas.width = i.width;
|
||||||
|
canvas.height = i.height;
|
||||||
|
var context = canvas.getContext('2d');
|
||||||
|
context.drawImage(i, 0, 0, i.width, i.height);
|
||||||
|
canvas.toBlob(function (blob) {
|
||||||
|
window.saveAs(blob, name);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
i.src = blobURL;
|
||||||
|
})();
|
||||||
|
return;
|
||||||
|
}
|
||||||
var media = Util.find($mt, [0, '_mediaObject']);
|
var media = Util.find($mt, [0, '_mediaObject']);
|
||||||
if (!media) { return void console.error('no media'); }
|
if (!media) { return void console.error('no media'); }
|
||||||
if (!media.complete) { return void UI.warn(Messages.mediatag_notReady); }
|
if (!media.complete) { return void UI.warn(Messages.mediatag_notReady); }
|
||||||
|
|
Loading…
Reference in New Issue