Merge pull request #1266 from thelounge/yamanickill/1228-theme-management
Allow themes from npm
This commit is contained in:
commit
2e2e201784
@ -264,8 +264,8 @@
|
|||||||
<label for="theme-select" class="sr-only">Theme</label>
|
<label for="theme-select" class="sr-only">Theme</label>
|
||||||
<select id="theme-select" name="theme" class="input">
|
<select id="theme-select" name="theme" class="input">
|
||||||
{{#each themes}}
|
{{#each themes}}
|
||||||
<option value="{{filename}}">
|
<option value="{{name}}">
|
||||||
{{name}}
|
{{displayName}}
|
||||||
</option>
|
</option>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</select>
|
</select>
|
||||||
|
@ -51,11 +51,12 @@ module.exports = {
|
|||||||
|
|
||||||
//
|
//
|
||||||
// Set the default theme.
|
// Set the default theme.
|
||||||
|
// Find out how to add new themes at https://thelounge.github.io/docs/packages/themes
|
||||||
//
|
//
|
||||||
// @type string
|
// @type string
|
||||||
// @default "themes/example.css"
|
// @default "example"
|
||||||
//
|
//
|
||||||
theme: "themes/example.css",
|
theme: "example",
|
||||||
|
|
||||||
//
|
//
|
||||||
// Prefetch URLs
|
// Prefetch URLs
|
||||||
|
@ -12,6 +12,8 @@ const colors = require("colors/safe");
|
|||||||
var Helper = {
|
var Helper = {
|
||||||
config: null,
|
config: null,
|
||||||
expandHome: expandHome,
|
expandHome: expandHome,
|
||||||
|
getPackagesPath: getPackagesPath,
|
||||||
|
getPackageModulePath: getPackageModulePath,
|
||||||
getStoragePath: getStoragePath,
|
getStoragePath: getStoragePath,
|
||||||
getUserConfigPath: getUserConfigPath,
|
getUserConfigPath: getUserConfigPath,
|
||||||
getUserLogsPath: getUserLogsPath,
|
getUserLogsPath: getUserLogsPath,
|
||||||
@ -96,6 +98,14 @@ function getStoragePath() {
|
|||||||
return path.join(this.HOME, "storage");
|
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) {
|
function ip2hex(address) {
|
||||||
// no ipv6 support
|
// no ipv6 support
|
||||||
if (!net.isIPv4(address)) {
|
if (!net.isIPv4(address)) {
|
||||||
|
85
src/plugins/themes.js
Normal file
85
src/plugins/themes.js
Normal 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
|
||||||
|
};
|
||||||
|
}
|
@ -14,6 +14,7 @@ var Helper = require("./helper");
|
|||||||
var colors = require("colors/safe");
|
var colors = require("colors/safe");
|
||||||
const net = require("net");
|
const net = require("net");
|
||||||
const Identification = require("./identification");
|
const Identification = require("./identification");
|
||||||
|
const themes = require("./plugins/themes");
|
||||||
|
|
||||||
// The order defined the priority: the first available plugin is used
|
// The order defined the priority: the first available plugin is used
|
||||||
// ALways keep local auth in the end, which should always be enabled.
|
// ALways keep local auth in the end, which should always be enabled.
|
||||||
@ -51,6 +52,15 @@ module.exports = function() {
|
|||||||
.set("view engine", "html")
|
.set("view engine", "html")
|
||||||
.set("views", path.join(__dirname, "..", "client"));
|
.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 config = Helper.config;
|
||||||
var server = null;
|
var server = null;
|
||||||
|
|
||||||
@ -191,16 +201,13 @@ function index(req, res, next) {
|
|||||||
pkg,
|
pkg,
|
||||||
Helper.config
|
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.gitCommit = Helper.getGitCommit();
|
||||||
data.themes = fs.readdirSync("client/themes/").filter(function(themeFile) {
|
data.themes = themes.getAll();
|
||||||
return themeFile.endsWith(".css");
|
|
||||||
}).map(function(css) {
|
|
||||||
const filename = css.slice(0, -4);
|
|
||||||
return {
|
|
||||||
name: filename.charAt(0).toUpperCase() + filename.slice(1),
|
|
||||||
filename: filename
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
const policies = [
|
const policies = [
|
||||||
"default-src *",
|
"default-src *",
|
||||||
|
Loading…
Reference in New Issue
Block a user