mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-11-02 20:16:04 +01:00
Support scoped theme packages
This commit is contained in:
committed by
Julian Lam
parent
d656c65c9a
commit
70ff2d9b88
@@ -18,3 +18,4 @@ logs/
|
|||||||
/build
|
/build
|
||||||
.eslintrc
|
.eslintrc
|
||||||
test/files
|
test/files
|
||||||
|
*.min.js
|
||||||
|
|||||||
@@ -34,8 +34,11 @@ function buildTargets() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var themeNamePattern = /^(@.*?\/)?nodebb-theme-.*$/;
|
||||||
|
var pluginNamePattern = /^(@.*?\/)?nodebb-(theme|plugin|widget|rewards)-.*$/;
|
||||||
|
|
||||||
function activate(plugin) {
|
function activate(plugin) {
|
||||||
if (plugin.startsWith('nodebb-theme-')) {
|
if (themeNamePattern.test(plugin)) {
|
||||||
reset.reset({
|
reset.reset({
|
||||||
theme: plugin,
|
theme: plugin,
|
||||||
}, function (err) {
|
}, function (err) {
|
||||||
@@ -50,7 +53,7 @@ function activate(plugin) {
|
|||||||
db.init(next);
|
db.init(next);
|
||||||
},
|
},
|
||||||
function (next) {
|
function (next) {
|
||||||
if (!plugin.startsWith('nodebb-')) {
|
if (!pluginNamePattern.test(plugin)) {
|
||||||
// Allow omission of `nodebb-plugin-`
|
// Allow omission of `nodebb-plugin-`
|
||||||
plugin = 'nodebb-plugin-' + plugin;
|
plugin = 'nodebb-plugin-' + plugin;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,9 @@ var widgets = require('../widgets');
|
|||||||
|
|
||||||
var dirname = require('./paths').baseDir;
|
var dirname = require('./paths').baseDir;
|
||||||
|
|
||||||
|
var themeNamePattern = /^(@.*?\/)?nodebb-theme-.*$/;
|
||||||
|
var pluginNamePattern = /^(@.*?\/)?nodebb-(theme|plugin|widget|rewards)-.*$/;
|
||||||
|
|
||||||
exports.reset = function (options, callback) {
|
exports.reset = function (options, callback) {
|
||||||
var map = {
|
var map = {
|
||||||
theme: function (next) {
|
theme: function (next) {
|
||||||
@@ -21,7 +24,7 @@ exports.reset = function (options, callback) {
|
|||||||
if (themeId === true) {
|
if (themeId === true) {
|
||||||
resetThemes(next);
|
resetThemes(next);
|
||||||
} else {
|
} else {
|
||||||
if (!themeId.startsWith('nodebb-theme-')) {
|
if (!themeNamePattern.test(themeId)) {
|
||||||
// Allow omission of `nodebb-theme-`
|
// Allow omission of `nodebb-theme-`
|
||||||
themeId = 'nodebb-theme-' + themeId;
|
themeId = 'nodebb-theme-' + themeId;
|
||||||
}
|
}
|
||||||
@@ -34,7 +37,7 @@ exports.reset = function (options, callback) {
|
|||||||
if (pluginId === true) {
|
if (pluginId === true) {
|
||||||
resetPlugins(next);
|
resetPlugins(next);
|
||||||
} else {
|
} else {
|
||||||
if (!pluginId.startsWith('nodebb-plugin-')) {
|
if (!pluginNamePattern.test(pluginId)) {
|
||||||
// Allow omission of `nodebb-plugin-`
|
// Allow omission of `nodebb-plugin-`
|
||||||
pluginId = 'nodebb-plugin-' + pluginId;
|
pluginId = 'nodebb-plugin-' + pluginId;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,22 +16,26 @@ themesController.get = function (req, res, next) {
|
|||||||
var screenshotPath;
|
var screenshotPath;
|
||||||
async.waterfall([
|
async.waterfall([
|
||||||
function (next) {
|
function (next) {
|
||||||
file.exists(themeConfigPath, next);
|
fs.readFile(themeConfigPath, 'utf8', function (err, config) {
|
||||||
},
|
if (err) {
|
||||||
function (exists, next) {
|
if (err.code === 'ENOENT') {
|
||||||
if (!exists) {
|
return next(Error('invalid-data'));
|
||||||
return next(Error('invalid-data'));
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fs.readFile(themeConfigPath, 'utf8', next);
|
return next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return next(null, config);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
function (themeConfig, next) {
|
function (themeConfig, next) {
|
||||||
try {
|
try {
|
||||||
themeConfig = JSON.parse(themeConfig);
|
themeConfig = JSON.parse(themeConfig);
|
||||||
next(null, themeConfig.screenshot ? path.join(themeDir, themeConfig.screenshot) : defaultScreenshotPath);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
next(e);
|
return next(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
next(null, themeConfig.screenshot ? path.join(themeDir, themeConfig.screenshot) : defaultScreenshotPath);
|
||||||
},
|
},
|
||||||
function (_screenshotPath, next) {
|
function (_screenshotPath, next) {
|
||||||
screenshotPath = _screenshotPath;
|
screenshotPath = _screenshotPath;
|
||||||
|
|||||||
@@ -34,13 +34,15 @@ Dependencies.check = function (callback) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var pluginNamePattern = /^(@.*?\/)?nodebb-(theme|plugin|widget|rewards)-.*$/;
|
||||||
|
|
||||||
Dependencies.checkModule = function (moduleName, callback) {
|
Dependencies.checkModule = function (moduleName, callback) {
|
||||||
fs.readFile(path.join(__dirname, '../../node_modules/', moduleName, 'package.json'), {
|
fs.readFile(path.join(__dirname, '../../node_modules/', moduleName, 'package.json'), {
|
||||||
encoding: 'utf-8',
|
encoding: 'utf-8',
|
||||||
}, function (err, pkgData) {
|
}, function (err, pkgData) {
|
||||||
if (err) {
|
if (err) {
|
||||||
// If a bundled plugin/theme is not present, skip the dep check (#3384)
|
// If a bundled plugin/theme is not present, skip the dep check (#3384)
|
||||||
if (err.code === 'ENOENT' && (moduleName === 'nodebb-rewards-essentials' || moduleName.startsWith('nodebb-plugin') || moduleName.startsWith('nodebb-theme'))) {
|
if (err.code === 'ENOENT' && pluginNamePattern.test(moduleName)) {
|
||||||
winston.warn('[meta/dependencies] Bundled plugin ' + moduleName + ' not found, skipping dependency check.');
|
winston.warn('[meta/dependencies] Bundled plugin ' + moduleName + ' not found, skipping dependency check.');
|
||||||
return callback(null, true);
|
return callback(null, true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,9 +45,11 @@ function processImports(paths, templatePath, source, callback) {
|
|||||||
}
|
}
|
||||||
Templates.processImports = processImports;
|
Templates.processImports = processImports;
|
||||||
|
|
||||||
|
var themeNamePattern = /^(@.*?\/)?nodebb-theme-.*$/;
|
||||||
|
|
||||||
function getTemplateDirs(activePlugins, callback) {
|
function getTemplateDirs(activePlugins, callback) {
|
||||||
var pluginTemplates = activePlugins.map(function (id) {
|
var pluginTemplates = activePlugins.map(function (id) {
|
||||||
if (id.startsWith('nodebb-theme-')) {
|
if (themeNamePattern.test(id)) {
|
||||||
return nconf.get('theme_templates_path');
|
return nconf.get('theme_templates_path');
|
||||||
}
|
}
|
||||||
if (!plugins.pluginsData[id]) {
|
if (!plugins.pluginsData[id]) {
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ var events = require('../events');
|
|||||||
|
|
||||||
var Themes = module.exports;
|
var Themes = module.exports;
|
||||||
|
|
||||||
|
var themeNamePattern = /^(@.*?\/)?nodebb-theme-.*$/;
|
||||||
|
|
||||||
Themes.get = function (callback) {
|
Themes.get = function (callback) {
|
||||||
var themePath = nconf.get('themes_path');
|
var themePath = nconf.get('themes_path');
|
||||||
if (typeof themePath !== 'string') {
|
if (typeof themePath !== 'string') {
|
||||||
@@ -24,9 +26,13 @@ Themes.get = function (callback) {
|
|||||||
function (next) {
|
function (next) {
|
||||||
fs.readdir(themePath, next);
|
fs.readdir(themePath, next);
|
||||||
},
|
},
|
||||||
function (files, next) {
|
function (dirs, next) {
|
||||||
async.filter(files, function (file, next) {
|
async.map(dirs.filter(function (dir) {
|
||||||
fs.stat(path.join(themePath, file), function (err, fileStat) {
|
return themeNamePattern.test(dir) || dir.startsWith('@');
|
||||||
|
}), function (dir, next) {
|
||||||
|
var dirpath = path.join(themePath, dir);
|
||||||
|
|
||||||
|
fs.stat(dirpath, function (err, stat) {
|
||||||
if (err) {
|
if (err) {
|
||||||
if (err.code === 'ENOENT') {
|
if (err.code === 'ENOENT') {
|
||||||
return next(null, false);
|
return next(null, false);
|
||||||
@@ -34,11 +40,54 @@ Themes.get = function (callback) {
|
|||||||
return next(err);
|
return next(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
next(null, (fileStat.isDirectory() && file.slice(0, 13) === 'nodebb-theme-'));
|
if (!stat.isDirectory()) {
|
||||||
|
return next(null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dir.startsWith('@')) {
|
||||||
|
return next(null, dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.readdir(dirpath, function (err, themes) {
|
||||||
|
if (err) {
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
async.filter(themes.filter(function (theme) {
|
||||||
|
return themeNamePattern.test(theme);
|
||||||
|
}), function (theme, next) {
|
||||||
|
fs.stat(path.join(dirpath, theme), function (err, stat) {
|
||||||
|
if (err) {
|
||||||
|
if (err.code === 'ENOENT') {
|
||||||
|
return next(null, false);
|
||||||
|
}
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
next(null, stat.isDirectory());
|
||||||
|
});
|
||||||
|
}, function (err, themes) {
|
||||||
|
if (err) {
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
next(null, themes.map(function (theme) {
|
||||||
|
return dir + '/' + theme;
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}, next);
|
}, next);
|
||||||
},
|
},
|
||||||
function (themes, next) {
|
function (themes, next) {
|
||||||
|
themes = themes.reduce(function (prev, theme) {
|
||||||
|
if (!theme) {
|
||||||
|
return prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
return prev.concat(theme);
|
||||||
|
}, []);
|
||||||
|
|
||||||
async.map(themes, function (theme, next) {
|
async.map(themes, function (theme, next) {
|
||||||
var config = path.join(themePath, theme, 'theme.json');
|
var config = path.join(themePath, theme, 'theme.json');
|
||||||
|
|
||||||
@@ -55,9 +104,9 @@ Themes.get = function (callback) {
|
|||||||
// Minor adjustments for API output
|
// Minor adjustments for API output
|
||||||
configObj.type = 'local';
|
configObj.type = 'local';
|
||||||
if (configObj.screenshot) {
|
if (configObj.screenshot) {
|
||||||
configObj.screenshot_url = nconf.get('relative_path') + '/css/previews/' + configObj.id;
|
configObj.screenshot_url = 'css/previews/' + encodeURIComponent(configObj.id);
|
||||||
} else {
|
} else {
|
||||||
configObj.screenshot_url = nconf.get('relative_path') + '/assets/images/themes/default.png';
|
configObj.screenshot_url = 'assets/images/themes/default.png';
|
||||||
}
|
}
|
||||||
next(null, configObj);
|
next(null, configObj);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -150,14 +199,14 @@ Themes.setupPaths = function (callback) {
|
|||||||
function (data, next) {
|
function (data, next) {
|
||||||
var themeId = data.currentThemeId || 'nodebb-theme-persona';
|
var themeId = data.currentThemeId || 'nodebb-theme-persona';
|
||||||
|
|
||||||
var themeObj = data.themesData.filter(function (themeObj) {
|
|
||||||
return themeObj.id === themeId;
|
|
||||||
})[0];
|
|
||||||
|
|
||||||
if (process.env.NODE_ENV === 'development') {
|
if (process.env.NODE_ENV === 'development') {
|
||||||
winston.info('[themes] Using theme ' + themeId);
|
winston.info('[themes] Using theme ' + themeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var themeObj = data.themesData.find(function (themeObj) {
|
||||||
|
return themeObj.id === themeId;
|
||||||
|
});
|
||||||
|
|
||||||
if (!themeObj) {
|
if (!themeObj) {
|
||||||
return callback(new Error('[[error:theme-not-found]]'));
|
return callback(new Error('[[error:theme-not-found]]'));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -138,6 +138,8 @@ Plugins.reloadRoutes = function (callback) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var themeNamePattern = /^(@.*?\/)?nodebb-theme-.*$/;
|
||||||
|
|
||||||
// DEPRECATED: remove in v1.8.0
|
// DEPRECATED: remove in v1.8.0
|
||||||
Plugins.getTemplates = function (callback) {
|
Plugins.getTemplates = function (callback) {
|
||||||
var templates = {};
|
var templates = {};
|
||||||
@@ -151,7 +153,7 @@ Plugins.getTemplates = function (callback) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async.eachSeries(plugins, function (plugin, next) {
|
async.eachSeries(plugins, function (plugin, next) {
|
||||||
if (plugin.templates || plugin.id.startsWith('nodebb-theme-')) {
|
if (plugin.templates || themeNamePattern.test(plugin.id)) {
|
||||||
winston.verbose('[plugins] Loading templates (' + plugin.id + ')');
|
winston.verbose('[plugins] Loading templates (' + plugin.id + ')');
|
||||||
var templatesPath = path.join(__dirname, '../node_modules', plugin.id, plugin.templates || 'templates');
|
var templatesPath = path.join(__dirname, '../node_modules', plugin.id, plugin.templates || 'templates');
|
||||||
file.walk(templatesPath, function (err, pluginTemplates) {
|
file.walk(templatesPath, function (err, pluginTemplates) {
|
||||||
@@ -261,7 +263,7 @@ Plugins.normalise = function (apiReturn, callback) {
|
|||||||
pluginMap[plugin.id].description = plugin.description;
|
pluginMap[plugin.id].description = plugin.description;
|
||||||
pluginMap[plugin.id].url = pluginMap[plugin.id].url || plugin.url;
|
pluginMap[plugin.id].url = pluginMap[plugin.id].url || plugin.url;
|
||||||
pluginMap[plugin.id].installed = true;
|
pluginMap[plugin.id].installed = true;
|
||||||
pluginMap[plugin.id].isTheme = !!plugin.id.match('nodebb-theme-');
|
pluginMap[plugin.id].isTheme = themeNamePattern.test(plugin.id);
|
||||||
pluginMap[plugin.id].error = plugin.error || false;
|
pluginMap[plugin.id].error = plugin.error || false;
|
||||||
pluginMap[plugin.id].active = plugin.active;
|
pluginMap[plugin.id].active = plugin.active;
|
||||||
pluginMap[plugin.id].version = plugin.version;
|
pluginMap[plugin.id].version = plugin.version;
|
||||||
|
|||||||
@@ -122,13 +122,16 @@ module.exports = function (Plugins) {
|
|||||||
], callback);
|
], callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var themeNamePattern = /(@.*?\/)?nodebb-theme-.*$/;
|
||||||
|
|
||||||
Plugins.loadPlugin = function (pluginPath, callback) {
|
Plugins.loadPlugin = function (pluginPath, callback) {
|
||||||
Plugins.data.loadPluginInfo(pluginPath, function (err, pluginData) {
|
Plugins.data.loadPluginInfo(pluginPath, function (err, pluginData) {
|
||||||
if (err) {
|
if (err) {
|
||||||
if (err.message === '[[error:parse-error]]') {
|
if (err.message === '[[error:parse-error]]') {
|
||||||
return callback();
|
return callback();
|
||||||
}
|
}
|
||||||
return callback(pluginPath.match('nodebb-theme') ? null : err);
|
|
||||||
|
return callback(themeNamePattern.test(pluginPath) ? null : err);
|
||||||
}
|
}
|
||||||
|
|
||||||
checkVersion(pluginData);
|
checkVersion(pluginData);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<!-- BEGIN themes -->
|
<!-- BEGIN themes -->
|
||||||
<div class="col-lg-4 col-md-6 col-sm-12 col-xs-12" data-type="{themes.type}" data-theme="{themes.id}"<!-- IF themes.css --> data-css="{themes.css}"<!-- ENDIF themes.css -->>
|
<div class="col-lg-4 col-md-6 col-sm-12 col-xs-12" data-type="{themes.type}" data-theme="{themes.id}"<!-- IF themes.css --> data-css="{themes.css}"<!-- ENDIF themes.css -->>
|
||||||
<div class="theme-card mdl-card mdl-shadow--2dp">
|
<div class="theme-card mdl-card mdl-shadow--2dp">
|
||||||
<div class="mdl-card__title mdl-card--expand" style="background-image: url('{themes.screenshot_url}');"></div>
|
<div class="mdl-card__title mdl-card--expand" style="background-image: url('{relative_path}/{themes.screenshot_url}');"></div>
|
||||||
<div class="mdl-card__supporting-text">
|
<div class="mdl-card__supporting-text">
|
||||||
<h2 class="mdl-card__title-text">{themes.name}</h2>
|
<h2 class="mdl-card__title-text">{themes.name}</h2>
|
||||||
<p>
|
<p>
|
||||||
|
|||||||
Reference in New Issue
Block a user