mirror of
https://github.com/node-red/node-red.git
synced 2023-10-10 13:36:53 +02:00
Merge pull request #2836 from node-red/theme-plugins
Add support for Theme Plugins
This commit is contained in:
commit
575d07e41a
@ -17,6 +17,10 @@ module.exports = {
|
|||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
opts.lang = apiUtils.determineLangFromHeaders(req.acceptsLanguages());
|
opts.lang = apiUtils.determineLangFromHeaders(req.acceptsLanguages());
|
||||||
|
if (/[^a-z\-\*]/i.test(opts.lang)) {
|
||||||
|
res.json({});
|
||||||
|
return;
|
||||||
|
}
|
||||||
runtimeAPI.plugins.getPluginConfigs(opts).then(function(configs) {
|
runtimeAPI.plugins.getPluginConfigs(opts).then(function(configs) {
|
||||||
res.send(configs);
|
res.send(configs);
|
||||||
})
|
})
|
||||||
@ -28,6 +32,10 @@ module.exports = {
|
|||||||
lang: req.query.lng,
|
lang: req.query.lng,
|
||||||
req: apiUtils.getRequestLogObject(req)
|
req: apiUtils.getRequestLogObject(req)
|
||||||
}
|
}
|
||||||
|
if (/[^a-z\-\*]/i.test(opts.lang)) {
|
||||||
|
res.json({});
|
||||||
|
return;
|
||||||
|
}
|
||||||
runtimeAPI.plugins.getPluginCatalogs(opts).then(function(result) {
|
runtimeAPI.plugins.getPluginCatalogs(opts).then(function(result) {
|
||||||
res.json(result);
|
res.json(result);
|
||||||
}).catch(function(err) {
|
}).catch(function(err) {
|
||||||
|
@ -76,7 +76,7 @@ module.exports = {
|
|||||||
editorApp.get("/icons/:scope/:module/:icon",ui.icon);
|
editorApp.get("/icons/:scope/:module/:icon",ui.icon);
|
||||||
|
|
||||||
var theme = require("./theme");
|
var theme = require("./theme");
|
||||||
theme.init(settings);
|
theme.init(settings, runtimeAPI);
|
||||||
editorApp.use("/theme",theme.app());
|
editorApp.use("/theme",theme.app());
|
||||||
editorApp.use("/",ui.editorResources);
|
editorApp.use("/",ui.editorResources);
|
||||||
|
|
||||||
|
@ -41,6 +41,10 @@ var theme = null;
|
|||||||
var themeContext = clone(defaultContext);
|
var themeContext = clone(defaultContext);
|
||||||
var themeSettings = null;
|
var themeSettings = null;
|
||||||
|
|
||||||
|
var activeTheme = null;
|
||||||
|
var activeThemeInitialised = false;
|
||||||
|
|
||||||
|
var runtimeAPI;
|
||||||
var themeApp;
|
var themeApp;
|
||||||
|
|
||||||
function serveFile(app,baseUrl,file) {
|
function serveFile(app,baseUrl,file) {
|
||||||
@ -58,7 +62,7 @@ function serveFile(app,baseUrl,file) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function serveFilesFromTheme(themeValue, themeApp, directory) {
|
function serveFilesFromTheme(themeValue, themeApp, directory, baseDirectory) {
|
||||||
var result = [];
|
var result = [];
|
||||||
if (themeValue) {
|
if (themeValue) {
|
||||||
var array = themeValue;
|
var array = themeValue;
|
||||||
@ -67,7 +71,14 @@ function serveFilesFromTheme(themeValue, themeApp, directory) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (var i=0;i<array.length;i++) {
|
for (var i=0;i<array.length;i++) {
|
||||||
var url = serveFile(themeApp,directory,array[i]);
|
let fullPath = array[i];
|
||||||
|
if (baseDirectory) {
|
||||||
|
fullPath = path.resolve(baseDirectory,array[i]);
|
||||||
|
if (fullPath.indexOf(baseDirectory) !== 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var url = serveFile(themeApp,directory,fullPath);
|
||||||
if (url) {
|
if (url) {
|
||||||
result.push(url);
|
result.push(url);
|
||||||
}
|
}
|
||||||
@ -77,10 +88,12 @@ function serveFilesFromTheme(themeValue, themeApp, directory) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
init: function(settings) {
|
init: function(settings, _runtimeAPI) {
|
||||||
|
runtimeAPI = _runtimeAPI;
|
||||||
themeContext = clone(defaultContext);
|
themeContext = clone(defaultContext);
|
||||||
themeSettings = null;
|
themeSettings = null;
|
||||||
theme = settings.editorTheme || {};
|
theme = settings.editorTheme || {};
|
||||||
|
activeTheme = theme.theme;
|
||||||
},
|
},
|
||||||
|
|
||||||
app: function() {
|
app: function() {
|
||||||
@ -169,7 +182,9 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
themeApp.get("/", function(req,res) {
|
themeApp.get("/", async function(req,res) {
|
||||||
|
const themePluginList = await runtimeAPI.plugins.getPluginsByType({type:"node-red-theme"});
|
||||||
|
themeContext.themes = themePluginList.map(theme => theme.id);
|
||||||
res.json(themeContext);
|
res.json(themeContext);
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -185,10 +200,38 @@ module.exports = {
|
|||||||
themeSettings.projects = theme.projects;
|
themeSettings.projects = theme.projects;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (theme.theme) {
|
||||||
|
themeSettings.theme = theme.theme;
|
||||||
|
}
|
||||||
return themeApp;
|
return themeApp;
|
||||||
},
|
},
|
||||||
context: function() {
|
context: async function() {
|
||||||
|
if (activeTheme && !activeThemeInitialised) {
|
||||||
|
const themePlugin = await runtimeAPI.plugins.getPlugin({
|
||||||
|
id:activeTheme
|
||||||
|
});
|
||||||
|
if (themePlugin) {
|
||||||
|
if (themePlugin.css) {
|
||||||
|
const cssFiles = serveFilesFromTheme(
|
||||||
|
themePlugin.css,
|
||||||
|
themeApp,
|
||||||
|
"/css/",
|
||||||
|
themePlugin.path
|
||||||
|
);
|
||||||
|
themeContext.page.css = cssFiles.concat(themeContext.page.css || [])
|
||||||
|
}
|
||||||
|
if (themePlugin.scripts) {
|
||||||
|
const scriptFiles = serveFilesFromTheme(
|
||||||
|
themePlugin.scripts,
|
||||||
|
themeApp,
|
||||||
|
"/scripts/",
|
||||||
|
themePlugin.path
|
||||||
|
)
|
||||||
|
themeContext.page.scripts = scriptFiles.concat(themeContext.page.scripts || [])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
activeThemeInitialised = true;
|
||||||
|
}
|
||||||
return themeContext;
|
return themeContext;
|
||||||
},
|
},
|
||||||
settings: function() {
|
settings: function() {
|
||||||
|
@ -68,8 +68,8 @@ module.exports = {
|
|||||||
apiUtils.rejectHandler(req,res,err);
|
apiUtils.rejectHandler(req,res,err);
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
editor: function(req,res) {
|
editor: async function(req,res) {
|
||||||
res.send(Mustache.render(editorTemplate,theme.context()));
|
res.send(Mustache.render(editorTemplate,await theme.context()));
|
||||||
},
|
},
|
||||||
editorResources: express.static(path.join(editorClientDir,'public'))
|
editorResources: express.static(path.join(editorClientDir,'public'))
|
||||||
};
|
};
|
||||||
|
@ -681,9 +681,12 @@ var RED = (function() {
|
|||||||
$('<span>').html(theme.header.title).appendTo(logo);
|
$('<span>').html(theme.header.title).appendTo(logo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (theme.themes) {
|
||||||
|
knownThemes = theme.themes;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
var knownThemes = null;
|
||||||
var initialised = false;
|
var initialised = false;
|
||||||
|
|
||||||
function init(options) {
|
function init(options) {
|
||||||
@ -703,7 +706,13 @@ var RED = (function() {
|
|||||||
buildEditor(options);
|
buildEditor(options);
|
||||||
|
|
||||||
RED.i18n.init(options, function() {
|
RED.i18n.init(options, function() {
|
||||||
RED.settings.init(options, loadEditor);
|
RED.settings.init(options, function() {
|
||||||
|
if (knownThemes) {
|
||||||
|
RED.settings.editorTheme = RED.settings.editorTheme || {};
|
||||||
|
RED.settings.editorTheme.themes = knownThemes;
|
||||||
|
}
|
||||||
|
loadEditor();
|
||||||
|
});
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,7 +115,13 @@ RED.userSettings = (function() {
|
|||||||
options: [
|
options: [
|
||||||
{setting:"editor-language",local: true, label:"menu.label.view.language",options:function(done){ done([{val:'',text:RED._('menu.label.view.browserDefault')}].concat(RED.settings.theme("languages").map(localeToName).sort(compText))) }},
|
{setting:"editor-language",local: true, label:"menu.label.view.language",options:function(done){ done([{val:'',text:RED._('menu.label.view.browserDefault')}].concat(RED.settings.theme("languages").map(localeToName).sort(compText))) }},
|
||||||
]
|
]
|
||||||
},{
|
},
|
||||||
|
// {
|
||||||
|
// options: [
|
||||||
|
// {setting:"theme", label:"Theme",options:function(done){ done([{val:'',text:'default'}].concat(RED.settings.theme("themes"))) }},
|
||||||
|
// ]
|
||||||
|
// },
|
||||||
|
{
|
||||||
title: "menu.label.view.grid",
|
title: "menu.label.view.grid",
|
||||||
options: [
|
options: [
|
||||||
{setting:"view-show-grid",oldSetting:"menu-menu-item-view-show-grid",label:"menu.label.view.showGrid", default: true, toggle:true,onchange:"core:toggle-show-grid"},
|
{setting:"view-show-grid",oldSetting:"menu-menu-item-view-show-grid",label:"menu.label.view.showGrid", default: true, toggle:true,onchange:"core:toggle-show-grid"},
|
||||||
|
@ -24,6 +24,9 @@ function registerPlugin(nodeSetId,id,definition) {
|
|||||||
pluginToId[id] = nodeSetId;
|
pluginToId[id] = nodeSetId;
|
||||||
plugins[id] = definition;
|
plugins[id] = definition;
|
||||||
var module = registry.getModule(moduleId);
|
var module = registry.getModule(moduleId);
|
||||||
|
|
||||||
|
definition.path = module.path;
|
||||||
|
|
||||||
module.plugins[pluginId].plugins.push(definition);
|
module.plugins[pluginId].plugins.push(definition);
|
||||||
if (definition.type) {
|
if (definition.type) {
|
||||||
pluginsByType[definition.type] = pluginsByType[definition.type] || [];
|
pluginsByType[definition.type] = pluginsByType[definition.type] || [];
|
||||||
|
@ -9,21 +9,59 @@ var api = module.exports = {
|
|||||||
runtime = _runtime;
|
runtime = _runtime;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a plugin definition from the registry
|
||||||
|
* @param {Object} opts
|
||||||
|
* @param {String} opts.id - the id of the plugin to get
|
||||||
|
* @param {User} opts.user - the user calling the api
|
||||||
|
* @param {Object} opts.req - the request to log (optional)
|
||||||
|
* @return {Promise<PluginDefinition>} - the plugin definition
|
||||||
|
* @memberof @node-red/runtime_plugins
|
||||||
|
*/
|
||||||
|
getPlugin: async function(opts) {
|
||||||
|
return runtime.plugins.getPlugin(opts.id);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets all plugin definitions of a given type
|
||||||
|
* @param {Object} opts
|
||||||
|
* @param {String} opts.type - the type of the plugins to get
|
||||||
|
* @param {User} opts.user - the user calling the api
|
||||||
|
* @param {Object} opts.req - the request to log (optional)
|
||||||
|
* @return {Promise<Array>} - the plugin definitions
|
||||||
|
* @memberof @node-red/runtime_plugins
|
||||||
|
*/
|
||||||
|
getPluginsByType: async function(opts) {
|
||||||
|
return runtime.plugins.getPluginsByType(opts.type);
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the editor content for an individual plugin
|
* Gets the editor content for an individual plugin
|
||||||
* @param {Object} opts
|
* @param {String} opts.lang - the locale language to return
|
||||||
* @param {User} opts.user - the user calling the api
|
* @param {User} opts.user - the user calling the api
|
||||||
* @param {Object} opts.req - the request to log (optional)
|
* @param {Object} opts.req - the request to log (optional)
|
||||||
* @return {Promise<NodeInfo>} - the node information
|
* @return {Promise<NodeInfo>} - the node information
|
||||||
* @memberof @node-red/runtime_nodes
|
* @memberof @node-red/runtime_plugins
|
||||||
*/
|
*/
|
||||||
getPluginList: async function(opts) {
|
getPluginList: async function(opts) {
|
||||||
runtime.log.audit({event: "plugins.list.get"}, opts.req);
|
runtime.log.audit({event: "plugins.list.get"}, opts.req);
|
||||||
return runtime.plugins.getPluginList();
|
return runtime.plugins.getPluginList();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the editor content for all registered plugins
|
||||||
|
* @param {Object} opts
|
||||||
|
* @param {User} opts.user - the user calling the api
|
||||||
|
* @param {User} opts.user - the user calling the api
|
||||||
|
* @param {Object} opts.req - the request to log (optional)
|
||||||
|
* @return {Promise<NodeInfo>} - the node information
|
||||||
|
* @memberof @node-red/runtime_plugins
|
||||||
|
*/
|
||||||
getPluginConfigs: async function(opts) {
|
getPluginConfigs: async function(opts) {
|
||||||
|
if (/[^a-z\-]/i.test(opts.lang)) {
|
||||||
|
throw new Error("Invalid language: "+opts.lang)
|
||||||
|
return;
|
||||||
|
}
|
||||||
runtime.log.audit({event: "plugins.configs.get"}, opts.req);
|
runtime.log.audit({event: "plugins.configs.get"}, opts.req);
|
||||||
return runtime.plugins.getPluginConfigs(opts.lang);
|
return runtime.plugins.getPluginConfigs(opts.lang);
|
||||||
},
|
},
|
||||||
@ -34,7 +72,7 @@ var api = module.exports = {
|
|||||||
* @param {User} opts.lang - the i18n language to return. If not set, uses runtime default (en-US)
|
* @param {User} opts.lang - the i18n language to return. If not set, uses runtime default (en-US)
|
||||||
* @param {Object} opts.req - the request to log (optional)
|
* @param {Object} opts.req - the request to log (optional)
|
||||||
* @return {Promise<Object>} - the message catalogs
|
* @return {Promise<Object>} - the message catalogs
|
||||||
* @memberof @node-red/runtime_nodes
|
* @memberof @node-red/runtime_plugins
|
||||||
*/
|
*/
|
||||||
getPluginCatalogs: async function(opts) {
|
getPluginCatalogs: async function(opts) {
|
||||||
var lang = opts.lang;
|
var lang = opts.lang;
|
||||||
|
@ -33,11 +33,11 @@ describe("api/editor/theme", function () {
|
|||||||
theme.init({settings: {}});
|
theme.init({settings: {}});
|
||||||
fs.statSync.restore();
|
fs.statSync.restore();
|
||||||
});
|
});
|
||||||
it("applies the default theme", function () {
|
it("applies the default theme", async function () {
|
||||||
var result = theme.init({});
|
var result = theme.init({});
|
||||||
should.not.exist(result);
|
should.not.exist(result);
|
||||||
|
|
||||||
var context = theme.context();
|
var context = await theme.context();
|
||||||
context.should.have.a.property("page");
|
context.should.have.a.property("page");
|
||||||
context.page.should.have.a.property("title", "Node-RED");
|
context.page.should.have.a.property("title", "Node-RED");
|
||||||
context.page.should.have.a.property("favicon", "favicon.ico");
|
context.page.should.have.a.property("favicon", "favicon.ico");
|
||||||
@ -52,7 +52,7 @@ describe("api/editor/theme", function () {
|
|||||||
should.not.exist(theme.settings());
|
should.not.exist(theme.settings());
|
||||||
});
|
});
|
||||||
|
|
||||||
it("picks up custom theme", function () {
|
it("picks up custom theme", async function () {
|
||||||
theme.init({
|
theme.init({
|
||||||
editorTheme: {
|
editorTheme: {
|
||||||
page: {
|
page: {
|
||||||
@ -104,7 +104,7 @@ describe("api/editor/theme", function () {
|
|||||||
|
|
||||||
theme.app();
|
theme.app();
|
||||||
|
|
||||||
var context = theme.context();
|
var context = await theme.context();
|
||||||
context.should.have.a.property("page");
|
context.should.have.a.property("page");
|
||||||
context.page.should.have.a.property("title", "Test Page Title");
|
context.page.should.have.a.property("title", "Test Page Title");
|
||||||
context.page.should.have.a.property("favicon", "theme/favicon/favicon");
|
context.page.should.have.a.property("favicon", "theme/favicon/favicon");
|
||||||
|
Loading…
Reference in New Issue
Block a user