Allow admin to upload a new logo for the instance
This commit is contained in:
parent
13f92370cb
commit
056073983c
7 changed files with 129 additions and 3 deletions
|
@ -176,7 +176,7 @@ define([
|
||||||
h('div.row.cp-home-hero', [
|
h('div.row.cp-home-hero', [
|
||||||
h('div.cp-title.col-lg-6', [
|
h('div.cp-title.col-lg-6', [
|
||||||
h('img', {
|
h('img', {
|
||||||
src: '/customize/CryptPad_logo_hero.svg?' + urlArgs,
|
src: '/api/logo?' + urlArgs,
|
||||||
'aria-hidden': 'true',
|
'aria-hidden': 'true',
|
||||||
alt: ''
|
alt: ''
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
(function () {
|
(function () {
|
||||||
var logoPath = '/customize/CryptPad_logo.svg';
|
var logoPath = '/customize/CryptPad_logo.svg';
|
||||||
if (location.pathname === '/' || location.pathname === '/index.html') {
|
if (location.pathname === '/' || location.pathname === '/index.html') {
|
||||||
logoPath = '/customize/CryptPad_logo_hero.svg';
|
logoPath = '/api/logo';
|
||||||
}
|
}
|
||||||
|
|
||||||
var elem = document.createElement('div');
|
var elem = document.createElement('div');
|
||||||
|
|
|
@ -121,6 +121,11 @@
|
||||||
height: 40px;
|
height: 40px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
[type="file"] { // XXX hack, to fix with sidebar layout refactoring
|
||||||
|
height: auto;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 6.5px;
|
||||||
|
}
|
||||||
.cp-sidebarlayout-input-block {
|
.cp-sidebarlayout-input-block {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
width: @sidebar_button-width;
|
width: @sidebar_button-width;
|
||||||
|
|
|
@ -17,8 +17,9 @@ const BlockStore = require("../storage/block");
|
||||||
const MFA = require("../storage/mfa");
|
const MFA = require("../storage/mfa");
|
||||||
const ArchiveAccount = require('../archive-account');
|
const ArchiveAccount = require('../archive-account');
|
||||||
const { Worker } = require('node:worker_threads');
|
const { Worker } = require('node:worker_threads');
|
||||||
|
const Fse = require("fs-extra");
|
||||||
|
|
||||||
var Fs = require("fs");
|
const Fs = require("fs");
|
||||||
|
|
||||||
var Admin = module.exports;
|
var Admin = module.exports;
|
||||||
|
|
||||||
|
@ -915,6 +916,52 @@ var deleteInvitation = (Env, Server, cb, data) => {
|
||||||
Invitation.delete(Env, id, cb);
|
Invitation.delete(Env, id, cb);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const MAX_LOGO_SIZE = 200*1024; // 200KB
|
||||||
|
var uploadLogo = (Env, Server, cb, data, unsafeKey) => {
|
||||||
|
const args = Array.isArray(data) && data[1];
|
||||||
|
if (!args || typeof(args) !== 'object') { return void cb("EINVAL"); }
|
||||||
|
let dataURL = args.dataURL;
|
||||||
|
|
||||||
|
// (size*4/3) + 24 ==> base64 and dataURL overhead
|
||||||
|
if (!dataURL || dataURL.length > ((MAX_LOGO_SIZE*4/3)+24)) {
|
||||||
|
return void cb('E_TOO_LARGE');
|
||||||
|
}
|
||||||
|
|
||||||
|
let s = dataURL.split(',');
|
||||||
|
let base64 = s[1];
|
||||||
|
let mime = s[0].slice(s[0].indexOf(":")+1, s[0].indexOf(";"));
|
||||||
|
if (!base64 || !mime) { return void cb('EINVAL'); }
|
||||||
|
let buf;
|
||||||
|
try {
|
||||||
|
buf = Buffer.from(base64, 'base64');
|
||||||
|
} catch (e) {
|
||||||
|
return void cb(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
nThen(waitFor => {
|
||||||
|
Fse.mkdirp('customize', {}, waitFor((err) => {
|
||||||
|
if (!err) { return; }
|
||||||
|
waitFor.abort();
|
||||||
|
return void cb(err);
|
||||||
|
}));
|
||||||
|
}).nThen(waitFor => {
|
||||||
|
Fse.writeFile('./customize/CryptPad_logo_hero.svg', buf, waitFor((err) => {
|
||||||
|
if (!err) { return; }
|
||||||
|
waitFor.abort();
|
||||||
|
return void cb(err);
|
||||||
|
}));
|
||||||
|
}).nThen(() => {
|
||||||
|
adminDecree(Env, null, function (err) {
|
||||||
|
if (err) { return void cb(err); }
|
||||||
|
Env.flushCache();
|
||||||
|
cb(void 0, true);
|
||||||
|
}, ['UPLOAD_LOGO', [
|
||||||
|
'SET_LOGO_MIME',
|
||||||
|
[mime]
|
||||||
|
]], unsafeKey);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
var commands = {
|
var commands = {
|
||||||
ACTIVE_SESSIONS: getActiveSessions,
|
ACTIVE_SESSIONS: getActiveSessions,
|
||||||
ACTIVE_PADS: getActiveChannelCount,
|
ACTIVE_PADS: getActiveChannelCount,
|
||||||
|
@ -982,6 +1029,8 @@ var commands = {
|
||||||
ADD_KNOWN_USER: addKnownUser,
|
ADD_KNOWN_USER: addKnownUser,
|
||||||
DELETE_KNOWN_USER: deleteKnownUser,
|
DELETE_KNOWN_USER: deleteKnownUser,
|
||||||
UPDATE_KNOWN_USER: updateKnownUser,
|
UPDATE_KNOWN_USER: updateKnownUser,
|
||||||
|
|
||||||
|
UPLOAD_LOGO: uploadLogo,
|
||||||
};
|
};
|
||||||
|
|
||||||
// addFirstAdmin is an anon_rpc command
|
// addFirstAdmin is an anon_rpc command
|
||||||
|
|
|
@ -158,10 +158,16 @@ var makeGenericSetter = function (attr, validator) {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var isString = (str) => {
|
||||||
|
return str && typeof(str) === "string";
|
||||||
|
};
|
||||||
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);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var args_isString = function (args) {
|
||||||
|
return !(!Array.isArray(args) || !isString(args[0]));
|
||||||
|
};
|
||||||
var args_isInteger = function (args) {
|
var args_isInteger = function (args) {
|
||||||
return !(!Array.isArray(args) || !isInteger(args[0]));
|
return !(!Array.isArray(args) || !isInteger(args[0]));
|
||||||
};
|
};
|
||||||
|
@ -174,6 +180,9 @@ var arg_isPositiveInteger = function (args) {
|
||||||
return Array.isArray(args) && isInteger(args[0]) && args[0] > 0;
|
return Array.isArray(args) && isInteger(args[0]) && args[0] > 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// CryptPad_AsyncStore.rpc.send('ADMIN', [ 'ADMIN_DECREE', ['LOGO_MIME', ['image/png']]], console.log)
|
||||||
|
commands.SET_LOGO_MIME = makeGenericSetter('logoMimeType', args_isString);
|
||||||
|
|
||||||
// CryptPad_AsyncStore.rpc.send('ADMIN', [ 'ADMIN_DECREE', ['ENABLE_PROFILING', [true]]], console.log)
|
// CryptPad_AsyncStore.rpc.send('ADMIN', [ 'ADMIN_DECREE', ['ENABLE_PROFILING', [true]]], console.log)
|
||||||
commands.ENABLE_PROFILING = makeBooleanSetter('enableProfiling');
|
commands.ENABLE_PROFILING = makeBooleanSetter('enableProfiling');
|
||||||
|
|
||||||
|
|
|
@ -506,6 +506,7 @@ app.use("/block", (req, res, next) => {
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
app.use("/customize", Express.static('customize'));
|
app.use("/customize", Express.static('customize'));
|
||||||
app.use("/customize", Express.static('customize.dist'));
|
app.use("/customize", Express.static('customize.dist'));
|
||||||
app.use("/customize.dist", Express.static('customize.dist'));
|
app.use("/customize.dist", Express.static('customize.dist'));
|
||||||
|
@ -682,6 +683,17 @@ app.get('/api/profiling', function (req, res) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.get('/api/logo', function (req, res) {
|
||||||
|
let path = Path.resolve('./customize/CryptPad_logo.svg');
|
||||||
|
let base = Path.resolve('./customize.dist/CryptPad_logo.svg');
|
||||||
|
Fs.exists(path, function (exists) {
|
||||||
|
let mime = Env.logoMimeType || 'image/svg+xml';
|
||||||
|
res.setHeader('Content-Type', mime + '; charset=utf-8');
|
||||||
|
if (exists) { return Fs.createReadStream(path).pipe(res); }
|
||||||
|
Fs.createReadStream(base).pipe(res);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// This endpoint handles authenticated RPCs over HTTP
|
// This endpoint handles authenticated RPCs over HTTP
|
||||||
// via an interactive challenge-response protocol
|
// via an interactive challenge-response protocol
|
||||||
app.use(Express.json());
|
app.use(Express.json());
|
||||||
|
|
|
@ -72,6 +72,9 @@ define([
|
||||||
'cp-admin-jurisdiction',
|
'cp-admin-jurisdiction',
|
||||||
'cp-admin-notice',
|
'cp-admin-notice',
|
||||||
],
|
],
|
||||||
|
'customize': [
|
||||||
|
'cp-admin-logo'
|
||||||
|
],
|
||||||
'users': [ // Msg.admin_cat_quota
|
'users': [ // Msg.admin_cat_quota
|
||||||
'cp-admin-registration',
|
'cp-admin-registration',
|
||||||
'cp-admin-invitation',
|
'cp-admin-invitation',
|
||||||
|
@ -3895,6 +3898,54 @@ Example
|
||||||
return $div;
|
return $div;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Messages.admin_logoTitle = "Upload Logo";
|
||||||
|
Messages.admin_logoHint = "Max 200KB, svg, png or jpg";
|
||||||
|
Messages.admin_logoButton = "Upload";
|
||||||
|
create['logo'] = function () {
|
||||||
|
var key = 'logo';
|
||||||
|
var $div = makeBlock(key, true); // Msg.admin_emailHint, Msg.admin_emailTitle
|
||||||
|
|
||||||
|
let $button = $div.find('button');
|
||||||
|
|
||||||
|
var input = h('input', {
|
||||||
|
type: 'file',
|
||||||
|
accept: 'image/*',
|
||||||
|
'aria-labelledby': 'cp-admin-logo'
|
||||||
|
});
|
||||||
|
$(h('div', input)).insertBefore($button);
|
||||||
|
|
||||||
|
var spinner = UI.makeSpinner($div);
|
||||||
|
|
||||||
|
Util.onClickEnter($button, function () {
|
||||||
|
let files = input.files;
|
||||||
|
if (files.length !== 1) {
|
||||||
|
UI.warn(Messages.error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
spinner.spin();
|
||||||
|
$button.attr('disabled', 'disabled');
|
||||||
|
let reader = new FileReader();
|
||||||
|
reader.onloadend = function () {
|
||||||
|
let dataURL = this.result;
|
||||||
|
sframeCommand('UPLOAD_LOGO', {dataURL}, (err, response) => {
|
||||||
|
$button.removeAttr('disabled');
|
||||||
|
if (err) {
|
||||||
|
UI.warn(Messages.error);
|
||||||
|
$input.val('');
|
||||||
|
console.error(err, response);
|
||||||
|
spinner.hide();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
spinner.done();
|
||||||
|
UI.log(Messages.saved);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
reader.readAsDataURL(files[0]);
|
||||||
|
});
|
||||||
|
|
||||||
|
return $div;
|
||||||
|
};
|
||||||
|
|
||||||
var hideCategories = function () {
|
var hideCategories = function () {
|
||||||
APP.$rightside.find('> div').hide();
|
APP.$rightside.find('> div').hide();
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue