The communications platform that puts data protection first.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
Rocket.Chat/app/theme/server/server.js

176 lines
4.3 KiB

import crypto from 'crypto';
import _ from 'underscore';
import less from 'less';
import Autoprefixer from 'less-plugin-autoprefixer';
import { WebApp } from 'meteor/webapp';
import { Meteor } from 'meteor/meteor';
import { settings } from '../../settings';
import { Logger } from '../../logger';
import { getURL } from '../../utils/lib/getURL';
import { injectIntoHead } from '../../ui-master/server';
const logger = new Logger('rocketchat:theme', {
methods: {
stop_rendering: {
type: 'info',
},
},
});
let currentHash = '';
let currentSize = 0;
export const theme = new class {
constructor() {
this.variables = {};
this.packageCallbacks = [];
this.files = ['server/colors.less'];
this.customCSS = '';
settings.add('css', '');
settings.addGroup('Layout');
settings.onload('css', Meteor.bindEnvironment((key, value, initialLoad) => {
if (!initialLoad) {
Meteor.startup(function() {
process.emit('message', {
refresh: 'client',
});
});
}
}));
this.compileDelayed = _.debounce(Meteor.bindEnvironment(this.compile.bind(this)), 100);
Meteor.startup(() => {
settings.onAfterInitialLoad(() => {
settings.get(/^theme-./, Meteor.bindEnvironment((key, value) => {
if (key === 'theme-custom-css' && value != null) {
this.customCSS = value;
} else {
const name = key.replace(/^theme-[a-z]+-/, '');
if (this.variables[name] != null) {
this.variables[name].value = value;
}
}
this.compileDelayed();
}));
});
});
}
compile() {
let content = [this.getVariablesAsLess()];
content.push(...this.files.map((name) => Assets.getText(name)));
content.push(...this.packageCallbacks.map((name) => name()));
content.push(this.customCSS);
content = content.join('\n');
const options = {
compress: true,
plugins: [new Autoprefixer()],
};
const start = Date.now();
return less.render(content, options, function(err, data) {
logger.stop_rendering(Date.now() - start);
if (err != null) {
return console.log(err);
}
settings.updateById('css', data.css);
return Meteor.startup(function() {
return Meteor.setTimeout(function() {
return process.emit('message', {
refresh: 'client',
});
}, 200);
});
});
}
addColor(name, value, section, properties) {
const config = {
group: 'Colors',
type: 'color',
editor: 'color',
public: true,
properties,
section,
};
return settings.add(`theme-color-${ name }`, value, config);
}
addVariable(type, name, value, section, persist = true, editor, allowedTypes, property) {
this.variables[name] = {
type,
value,
};
if (persist) {
const config = {
group: 'Layout',
type,
editor: editor || type,
section,
public: true,
allowedTypes,
property,
};
return settings.add(`theme-${ type }-${ name }`, value, config);
}
}
addPublicColor(name, value, section, editor = 'color', property) {
return this.addVariable('color', name, value, section, true, editor, ['color', 'expression'], property);
}
addPublicFont(name, value) {
return this.addVariable('font', name, value, 'Fonts', true);
}
getVariablesAsObject() {
return Object.keys(this.variables).reduce((obj, name) => {
obj[name] = this.variables[name].value;
return obj;
}, {});
}
getVariablesAsLess() {
return Object.keys(this.variables).map((name) => {
const variable = this.variables[name];
return `@${ name }: ${ variable.value };`;
}).join('\n');
}
addPackageAsset(cb) {
this.packageCallbacks.push(cb);
return this.compileDelayed();
}
getCss() {
return settings.get('css') || '';
}
}();
Meteor.startup(() => {
settings.get('css', (key, value = '') => {
currentHash = crypto.createHash('sha1').update(value).digest('hex');
currentSize = value.length;
injectIntoHead('css-theme', `<link rel="stylesheet" type="text/css" href="${ getURL(`/theme.css?${ currentHash }`) }">`);
});
});
WebApp.rawConnectHandlers.use(function(req, res, next) {
const path = req.url.split('?')[0];
const prefix = __meteor_runtime_config__.ROOT_URL_PATH_PREFIX || '';
if (path !== `${ prefix }/theme.css`) {
return next();
}
res.setHeader('Content-Type', 'text/css; charset=UTF-8');
res.setHeader('Content-Length', currentSize);
res.setHeader('ETag', `"${ currentHash }"`);
res.write(theme.getCss());
res.end();
});