Merge pull request #1266 from thelounge/yamanickill/1228-theme-management

Allow themes from npm
This commit is contained in:
Jérémie Astori 2017-09-17 20:05:59 -04:00 committed by GitHub
commit 2e2e201784
5 changed files with 116 additions and 13 deletions

View File

@ -264,8 +264,8 @@
<label for="theme-select" class="sr-only">Theme</label>
<select id="theme-select" name="theme" class="input">
{{#each themes}}
<option value="{{filename}}">
{{name}}
<option value="{{name}}">
{{displayName}}
</option>
{{/each}}
</select>

View File

@ -51,11 +51,12 @@ module.exports = {
//
// Set the default theme.
// Find out how to add new themes at https://thelounge.github.io/docs/packages/themes
//
// @type string
// @default "themes/example.css"
// @default "example"
//
theme: "themes/example.css",
theme: "example",
//
// Prefetch URLs

View File

@ -12,6 +12,8 @@ const colors = require("colors/safe");
var Helper = {
config: null,
expandHome: expandHome,
getPackagesPath: getPackagesPath,
getPackageModulePath: getPackageModulePath,
getStoragePath: getStoragePath,
getUserConfigPath: getUserConfigPath,
getUserLogsPath: getUserLogsPath,
@ -96,6 +98,14 @@ function getStoragePath() {
return path.join(this.HOME, "storage");
}
function getPackagesPath() {
return path.join(this.HOME, "packages", "node_modules");
}
function getPackageModulePath(packageName) {
return path.join(Helper.getPackagesPath(), packageName);
}
function ip2hex(address) {
// no ipv6 support
if (!net.isIPv4(address)) {

85
src/plugins/themes.js Normal file
View File

@ -0,0 +1,85 @@
"use strict";
const fs = require("fs");
const Helper = require("../helper");
const colors = require("colors/safe");
const path = require("path");
const _ = require("lodash");
const themes = new Map();
module.exports = {
getAll: getAll,
getFilename: getFilename
};
fs.readdir("client/themes/", (err, builtInThemes) => {
if (err) {
return;
}
builtInThemes
.filter((theme) => theme.endsWith(".css"))
.map(makeLocalThemeObject)
.forEach((theme) => themes.set(theme.name, theme));
});
fs.readdir(Helper.getPackagesPath(), (err, packages) => {
if (err) {
return;
}
packages
.map(makePackageThemeObject)
.forEach((theme) => {
if (theme) {
themes.set(theme.name, theme);
}
});
});
function getAll() {
return _.sortBy(Array.from(themes.values()), "displayName");
}
function getFilename(module) {
if (themes.has(module)) {
return themes.get(module).filename;
}
}
function makeLocalThemeObject(css) {
const themeName = css.slice(0, -4);
return {
displayName: themeName.charAt(0).toUpperCase() + themeName.slice(1),
filename: path.join(__dirname, "..", "client", "themes", `${themeName}.css`),
name: themeName
};
}
function getModuleInfo(packageName) {
let module;
try {
module = require(Helper.getPackageModulePath(packageName));
} catch (e) {
log.warn(`Specified theme ${colors.yellow(packageName)} is not installed in packages directory`);
return;
}
if (!module.lounge) {
log.warn(`Specified theme ${colors.yellow(packageName)} doesn't have required information.`);
return;
}
return module.lounge;
}
function makePackageThemeObject(moduleName) {
const module = getModuleInfo(moduleName);
if (!module || module.type !== "theme") {
return;
}
const modulePath = Helper.getPackageModulePath(moduleName);
const displayName = module.name || moduleName;
const filename = path.join(modulePath, module.css);
return {
displayName: displayName,
filename: filename,
name: moduleName
};
}

View File

@ -14,6 +14,7 @@ var Helper = require("./helper");
var colors = require("colors/safe");
const net = require("net");
const Identification = require("./identification");
const themes = require("./plugins/themes");
// The order defined the priority: the first available plugin is used
// ALways keep local auth in the end, which should always be enabled.
@ -51,6 +52,15 @@ module.exports = function() {
.set("view engine", "html")
.set("views", path.join(__dirname, "..", "client"));
app.get("/themes/:theme.css", (req, res) => {
const themeName = req.params.theme;
const theme = themes.getFilename(themeName);
if (theme === undefined) {
return res.status(404).send("Not found");
}
return res.sendFile(theme);
});
var config = Helper.config;
var server = null;
@ -191,16 +201,13 @@ function index(req, res, next) {
pkg,
Helper.config
);
if (!data.theme.includes(".css")) { // Backwards compatibility for old way of specifying themes in settings
data.theme = `themes/${data.theme}.css`;
} else {
log.warn(`Referring to CSS files in the ${colors.green("theme")} setting of ${colors.green(Helper.CONFIG_PATH)} is ${colors.bold("deprecated")} and will be removed in a future version.`);
}
data.gitCommit = Helper.getGitCommit();
data.themes = fs.readdirSync("client/themes/").filter(function(themeFile) {
return themeFile.endsWith(".css");
}).map(function(css) {
const filename = css.slice(0, -4);
return {
name: filename.charAt(0).toUpperCase() + filename.slice(1),
filename: filename
};
});
data.themes = themes.getAll();
const policies = [
"default-src *",