Support scoped theme packages

v1.18.x
Peter Jaszkowiak 7 years ago committed by Julian Lam
parent d656c65c9a
commit 70ff2d9b88

@ -18,3 +18,4 @@ logs/
/build
.eslintrc
test/files
*.min.js

@ -34,8 +34,11 @@ function buildTargets() {
);
}
var themeNamePattern = /^(@.*?\/)?nodebb-theme-.*$/;
var pluginNamePattern = /^(@.*?\/)?nodebb-(theme|plugin|widget|rewards)-.*$/;
function activate(plugin) {
if (plugin.startsWith('nodebb-theme-')) {
if (themeNamePattern.test(plugin)) {
reset.reset({
theme: plugin,
}, function (err) {
@ -50,7 +53,7 @@ function activate(plugin) {
db.init(next);
},
function (next) {
if (!plugin.startsWith('nodebb-')) {
if (!pluginNamePattern.test(plugin)) {
// Allow omission of `nodebb-plugin-`
plugin = 'nodebb-plugin-' + plugin;
}

@ -14,6 +14,9 @@ var widgets = require('../widgets');
var dirname = require('./paths').baseDir;
var themeNamePattern = /^(@.*?\/)?nodebb-theme-.*$/;
var pluginNamePattern = /^(@.*?\/)?nodebb-(theme|plugin|widget|rewards)-.*$/;
exports.reset = function (options, callback) {
var map = {
theme: function (next) {
@ -21,7 +24,7 @@ exports.reset = function (options, callback) {
if (themeId === true) {
resetThemes(next);
} else {
if (!themeId.startsWith('nodebb-theme-')) {
if (!themeNamePattern.test(themeId)) {
// Allow omission of `nodebb-theme-`
themeId = 'nodebb-theme-' + themeId;
}
@ -34,7 +37,7 @@ exports.reset = function (options, callback) {
if (pluginId === true) {
resetPlugins(next);
} else {
if (!pluginId.startsWith('nodebb-plugin-')) {
if (!pluginNamePattern.test(pluginId)) {
// Allow omission of `nodebb-plugin-`
pluginId = 'nodebb-plugin-' + pluginId;
}

@ -16,22 +16,26 @@ themesController.get = function (req, res, next) {
var screenshotPath;
async.waterfall([
function (next) {
file.exists(themeConfigPath, next);
},
function (exists, next) {
if (!exists) {
fs.readFile(themeConfigPath, 'utf8', function (err, config) {
if (err) {
if (err.code === 'ENOENT') {
return next(Error('invalid-data'));
}
fs.readFile(themeConfigPath, 'utf8', next);
return next(err);
}
return next(null, config);
});
},
function (themeConfig, next) {
try {
themeConfig = JSON.parse(themeConfig);
next(null, themeConfig.screenshot ? path.join(themeDir, themeConfig.screenshot) : defaultScreenshotPath);
} catch (e) {
next(e);
return next(e);
}
next(null, themeConfig.screenshot ? path.join(themeDir, themeConfig.screenshot) : defaultScreenshotPath);
},
function (_screenshotPath, next) {
screenshotPath = _screenshotPath;

@ -34,13 +34,15 @@ Dependencies.check = function (callback) {
});
};
var pluginNamePattern = /^(@.*?\/)?nodebb-(theme|plugin|widget|rewards)-.*$/;
Dependencies.checkModule = function (moduleName, callback) {
fs.readFile(path.join(__dirname, '../../node_modules/', moduleName, 'package.json'), {
encoding: 'utf-8',
}, function (err, pkgData) {
if (err) {
// 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.');
return callback(null, true);
}

@ -45,9 +45,11 @@ function processImports(paths, templatePath, source, callback) {
}
Templates.processImports = processImports;
var themeNamePattern = /^(@.*?\/)?nodebb-theme-.*$/;
function getTemplateDirs(activePlugins, callback) {
var pluginTemplates = activePlugins.map(function (id) {
if (id.startsWith('nodebb-theme-')) {
if (themeNamePattern.test(id)) {
return nconf.get('theme_templates_path');
}
if (!plugins.pluginsData[id]) {

@ -14,6 +14,8 @@ var events = require('../events');
var Themes = module.exports;
var themeNamePattern = /^(@.*?\/)?nodebb-theme-.*$/;
Themes.get = function (callback) {
var themePath = nconf.get('themes_path');
if (typeof themePath !== 'string') {
@ -24,9 +26,13 @@ Themes.get = function (callback) {
function (next) {
fs.readdir(themePath, next);
},
function (files, next) {
async.filter(files, function (file, next) {
fs.stat(path.join(themePath, file), function (err, fileStat) {
function (dirs, next) {
async.map(dirs.filter(function (dir) {
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.code === 'ENOENT') {
return next(null, false);
@ -34,11 +40,54 @@ Themes.get = function (callback) {
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);
},
function (themes, next) {
themes = themes.reduce(function (prev, theme) {
if (!theme) {
return prev;
}
return prev.concat(theme);
}, []);
async.map(themes, function (theme, next) {
var config = path.join(themePath, theme, 'theme.json');
@ -55,9 +104,9 @@ Themes.get = function (callback) {
// Minor adjustments for API output
configObj.type = 'local';
if (configObj.screenshot) {
configObj.screenshot_url = nconf.get('relative_path') + '/css/previews/' + configObj.id;
configObj.screenshot_url = 'css/previews/' + encodeURIComponent(configObj.id);
} else {
configObj.screenshot_url = nconf.get('relative_path') + '/assets/images/themes/default.png';
configObj.screenshot_url = 'assets/images/themes/default.png';
}
next(null, configObj);
} catch (err) {
@ -150,14 +199,14 @@ Themes.setupPaths = function (callback) {
function (data, next) {
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') {
winston.info('[themes] Using theme ' + themeId);
}
var themeObj = data.themesData.find(function (themeObj) {
return themeObj.id === themeId;
});
if (!themeObj) {
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
Plugins.getTemplates = function (callback) {
var templates = {};
@ -151,7 +153,7 @@ Plugins.getTemplates = function (callback) {
}
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 + ')');
var templatesPath = path.join(__dirname, '../node_modules', plugin.id, plugin.templates || 'templates');
file.walk(templatesPath, function (err, pluginTemplates) {
@ -261,7 +263,7 @@ Plugins.normalise = function (apiReturn, callback) {
pluginMap[plugin.id].description = plugin.description;
pluginMap[plugin.id].url = pluginMap[plugin.id].url || plugin.url;
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].active = plugin.active;
pluginMap[plugin.id].version = plugin.version;

@ -122,13 +122,16 @@ module.exports = function (Plugins) {
], callback);
};
var themeNamePattern = /(@.*?\/)?nodebb-theme-.*$/;
Plugins.loadPlugin = function (pluginPath, callback) {
Plugins.data.loadPluginInfo(pluginPath, function (err, pluginData) {
if (err) {
if (err.message === '[[error:parse-error]]') {
return callback();
}
return callback(pluginPath.match('nodebb-theme') ? null : err);
return callback(themeNamePattern.test(pluginPath) ? null : err);
}
checkVersion(pluginData);

@ -1,7 +1,7 @@
<!-- 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="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">
<h2 class="mdl-card__title-text">{themes.name}</h2>
<p>

Loading…
Cancel
Save