From 59d2f93f61769201c51686b82ece4c1650a6d131 Mon Sep 17 00:00:00 2001 From: Alistair McKinlay Date: Thu, 22 Jun 2017 22:09:55 +0100 Subject: [PATCH] Allow themes from npm --- client/index.html | 4 +- defaults/config.js | 5 ++- src/helper.js | 10 +++++ src/plugins/themes.js | 85 +++++++++++++++++++++++++++++++++++++++++++ src/server.js | 25 ++++++++----- 5 files changed, 116 insertions(+), 13 deletions(-) create mode 100644 src/plugins/themes.js diff --git a/client/index.html b/client/index.html index 843e738b..cdce541f 100644 --- a/client/index.html +++ b/client/index.html @@ -264,8 +264,8 @@ diff --git a/defaults/config.js b/defaults/config.js index 7090760b..a2e3f136 100644 --- a/defaults/config.js +++ b/defaults/config.js @@ -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 diff --git a/src/helper.js b/src/helper.js index 288dcfec..73c53262 100644 --- a/src/helper.js +++ b/src/helper.js @@ -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)) { diff --git a/src/plugins/themes.js b/src/plugins/themes.js new file mode 100644 index 00000000..8c90cdd9 --- /dev/null +++ b/src/plugins/themes.js @@ -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 + }; +} diff --git a/src/server.js b/src/server.js index 5b708555..aebc6bfb 100644 --- a/src/server.js +++ b/src/server.js @@ -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 *",