mirror of
https://github.com/node-red/node-red.git
synced 2023-10-10 13:36:53 +02:00
Add partial implementation of adding library sources via editor
This adds lots of commented out code that provides a settings panel to add new library sources. It is incomplete as it doesn't actually add/update the library sources on the runtime. For 1.3, I'm focussing on allowing additional sources get added via the settings file only. I've done enough work on the editor side to convince myself more work is needed than I can justify at this time on what is otherwise not going to be a widely used feature.
This commit is contained in:
parent
8a076c01ab
commit
3f9a29730f
@ -93,6 +93,7 @@ module.exports = {
|
||||
// Library
|
||||
var library = require("./library");
|
||||
library.init(runtimeAPI);
|
||||
// editorApp.get("/library/:id",needsPermission("library.read"),library.getLibraryConfig);
|
||||
editorApp.get(/^\/library\/([^\/]+)\/([^\/]+)(?:$|\/(.*))/,needsPermission("library.read"),library.getEntry);
|
||||
editorApp.post(/^\/library\/([^\/]+)\/([^\/]+)\/(.*)/,needsPermission("library.write"),library.saveEntry);
|
||||
|
||||
|
@ -24,6 +24,17 @@ module.exports = {
|
||||
init: function(_runtimeAPI) {
|
||||
runtimeAPI = _runtimeAPI;
|
||||
},
|
||||
// getLibraryConfig: function(req,res) {
|
||||
// var opts = {
|
||||
// user: req.user,
|
||||
// library: req.params.id
|
||||
// }
|
||||
// runtimeAPI.library.getConfig(opts).then(function(result) {
|
||||
// res.json(result);
|
||||
// }).catch(function(err) {
|
||||
// apiUtils.rejectHandler(req,res,err);
|
||||
// });
|
||||
// },
|
||||
getEntry: function(req,res) {
|
||||
var opts = {
|
||||
user: req.user,
|
||||
|
@ -854,10 +854,15 @@ RED.clipboard = (function() {
|
||||
}
|
||||
|
||||
function loadFlowLibrary(browser,library) {
|
||||
var icon = 'fa fa-hdd-o';
|
||||
if (library.icon) {
|
||||
var fullIcon = RED.utils.separateIconPath(library.icon);
|
||||
icon = (fullIcon.module==="font-awesome"?"fa ":"")+fullIcon.file;
|
||||
}
|
||||
browser.data([{
|
||||
library: library.id,
|
||||
type: "flows",
|
||||
icon: library.icon || 'fa fa-hdd-o',
|
||||
icon: icon,
|
||||
label: RED._(library.label||library.id),
|
||||
path: "",
|
||||
expanded: true,
|
||||
|
@ -483,9 +483,235 @@ RED.library = (function() {
|
||||
}
|
||||
}
|
||||
|
||||
// var libraryPlugins = {};
|
||||
//
|
||||
// function showLibraryDetailsDialog(container, lib, done) {
|
||||
// var dialog = $('<div>').addClass("red-ui-projects-dialog-list-dialog").hide().appendTo(container);
|
||||
// $('<div>').addClass("red-ui-projects-dialog-list-dialog-header").text(lib?"Edit library source":"Add library source").appendTo(dialog);
|
||||
// var formRow = $('<div class="red-ui-settings-row"></div>').appendTo(dialog);
|
||||
// $('<label>').text("Type").appendTo(formRow);
|
||||
// var typeSelect = $('<select>').appendTo(formRow);
|
||||
// for (var type in libraryPlugins) {
|
||||
// if (libraryPlugins.hasOwnProperty(type)) {
|
||||
// $('<option>').attr('value',type).attr('selected',(lib && lib.type === type)?true:null).text(libraryPlugins[type].name).appendTo(typeSelect);
|
||||
// }
|
||||
// }
|
||||
// var dialogBody = $("<div>").addClass("red-ui-settings-section").appendTo(dialog);
|
||||
// var libraryFields = {};
|
||||
// var fieldsModified = {};
|
||||
// function validateFields() {
|
||||
// var validForm = true;
|
||||
// for (var p in libraryFields) {
|
||||
// if (libraryFields.hasOwnProperty(p)) {
|
||||
// var v = libraryFields[p].input.val().trim();
|
||||
// if (v === "") {
|
||||
// validForm = false;
|
||||
// if (libraryFields[p].modified) {
|
||||
// libraryFields[p].input.addClass("input-error");
|
||||
// }
|
||||
// } else {
|
||||
// libraryFields[p].input.removeClass("input-error");
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// okayButton.attr("disabled",validForm?null:"disabled");
|
||||
// }
|
||||
// typeSelect.on("change", function(evt) {
|
||||
// dialogBody.empty();
|
||||
// libraryFields = {};
|
||||
// fieldsModified = {};
|
||||
// var libDef = libraryPlugins[$(this).val()];
|
||||
// var defaultIcon = lib?lib.icon:(libDef.icon || "font-awesome/fa-image");
|
||||
// formRow = $('<div class="red-ui-settings-row"></div>').appendTo(dialogBody);
|
||||
// $('<label>').text(RED._("editor.settingIcon")).appendTo(formRow);
|
||||
// libraryFields['icon'] = {input: $('<input type="hidden">').val(defaultIcon) };
|
||||
// var iconButton = $('<button type="button" class="red-ui-button"></button>').appendTo(formRow);
|
||||
// iconButton.on("click", function(evt) {
|
||||
// evt.preventDefault();
|
||||
// var icon = libraryFields['icon'].input.val() || "";
|
||||
// var iconPath = (icon ? RED.utils.separateIconPath(icon) : {});
|
||||
// RED.editor.showIconPicker(iconButton, null, iconPath, true, function (newIcon) {
|
||||
// iconButton.empty();
|
||||
// var path = newIcon || "";
|
||||
// var newPath = RED.utils.separateIconPath(path);
|
||||
// if (newPath) {
|
||||
// $('<i class="fa"></i>').addClass(newPath.file).appendTo(iconButton);
|
||||
// }
|
||||
// libraryFields['icon'].input.val(path);
|
||||
// });
|
||||
// })
|
||||
// var newPath = RED.utils.separateIconPath(defaultIcon);
|
||||
// $('<i class="fa '+newPath.file+'"></i>').appendTo(iconButton);
|
||||
//
|
||||
// var libProps = libDef.defaults;
|
||||
// var libPropKeys = Object.keys(libProps).map(function(p) { return {id: p, def: libProps[p]}});
|
||||
// libPropKeys.unshift({id: "label", def: {value:""}})
|
||||
//
|
||||
// libPropKeys.forEach(function(prop) {
|
||||
// var p = prop.id;
|
||||
// var def = prop.def;
|
||||
// formRow = $('<div class="red-ui-settings-row"></div>').appendTo(dialogBody);
|
||||
// var label = libDef._(def.label || "label."+p,{defaultValue: p});
|
||||
// if (label === p) {
|
||||
// label = libDef._("editor:common.label."+p,{defaultValue:p});
|
||||
// }
|
||||
// $('<label>').text(label).appendTo(formRow);
|
||||
// libraryFields[p] = {
|
||||
// input: $('<input type="text">').val(lib?(lib[p]||lib.config[p]):def.value).appendTo(formRow),
|
||||
// modified: false
|
||||
// }
|
||||
// if (def.type === "password") {
|
||||
// libraryFields[p].input.attr("type","password").typedInput({type:"cred"})
|
||||
// }
|
||||
//
|
||||
// libraryFields[p].input.on("change paste keyup", function(evt) {
|
||||
// if (!evt.key || evt.key.length === 1) {
|
||||
// libraryFields[p].modified = true;
|
||||
// }
|
||||
// validateFields();
|
||||
// })
|
||||
// var desc = libDef._("desc."+p, {defaultValue: ""});
|
||||
// if (desc) {
|
||||
// $('<label class="red-ui-projects-edit-form-sublabel"></label>').append($('<small>').text(desc)).appendTo(formRow);
|
||||
// }
|
||||
// });
|
||||
// validateFields();
|
||||
// })
|
||||
//
|
||||
// var dialogButtons = $('<span class="button-row" style="position: relative; float: right; margin: 10px;"></span>').appendTo(dialog);
|
||||
// var cancelButton = $('<button class="red-ui-button"></button>').text(RED._("common.label.cancel")).appendTo(dialogButtons).on("click", function(evt) {
|
||||
// evt.preventDefault();
|
||||
// done(false);
|
||||
// })
|
||||
// var okayButton = $('<button class="red-ui-button"></button>').text(lib?"Update library":"Add library").appendTo(dialogButtons).on("click", function(evt) {
|
||||
// evt.preventDefault();
|
||||
// var item;
|
||||
// if (!lib) {
|
||||
// item = {
|
||||
// id: libraryFields['label'].input.val().trim().toLowerCase().replace(/( |[^a-z0-9])/g,"-"),
|
||||
// user: true,
|
||||
// type: typeSelect.val(),
|
||||
// config: {}
|
||||
// }
|
||||
// } else {
|
||||
// item = lib;
|
||||
// }
|
||||
//
|
||||
// item.label = libraryFields['label'].input.val().trim();
|
||||
// item.icon = libraryFields['icon'].input.val();
|
||||
//
|
||||
// for (var p in libraryFields) {
|
||||
// if (libraryFields.hasOwnProperty(p) && p !== 'label') {
|
||||
// item.config[p] = libraryFields[p].input.val().trim();
|
||||
// }
|
||||
// }
|
||||
// done(item);
|
||||
// });
|
||||
//
|
||||
// typeSelect.trigger("change");
|
||||
// if (lib) {
|
||||
// typeSelect.attr('disabled',true);
|
||||
// }
|
||||
//
|
||||
// dialog.slideDown(200);
|
||||
// }
|
||||
//
|
||||
// function createSettingsPane() {
|
||||
// var pane = $('<div id="red-ui-settings-tab-library-manager"></div>');
|
||||
// var toolbar = $('<div>').css("text-align","right").appendTo(pane);
|
||||
// var addButton = $('<button class="red-ui-button"><i class="fa fa-plus"></i> Add library</button>').appendTo(toolbar);
|
||||
//
|
||||
// var addingLibrary = false;
|
||||
//
|
||||
// var libraryList = $("<ol>").css({
|
||||
// position: "absolute",
|
||||
// left: "10px",
|
||||
// right: "10px",
|
||||
// top: "50px",
|
||||
// bottom: "10px"
|
||||
// }).appendTo(pane).editableList({
|
||||
// addButton: false,
|
||||
// addItem: function(row,index,itemData) {
|
||||
// if (itemData.id) {
|
||||
// row.addClass("red-ui-settings-tab-library-entry");
|
||||
// var iconCell = $("<span>").appendTo(row);
|
||||
// if (itemData.icon) {
|
||||
// var iconPath = RED.utils.separateIconPath(itemData.icon);
|
||||
// if (iconPath) {
|
||||
// $("<i>").addClass("fa "+iconPath.file).appendTo(iconCell);
|
||||
// }
|
||||
// }
|
||||
// $("<span>").text(RED._(itemData.label)).appendTo(row);
|
||||
// $("<span>").text(RED._(itemData.type)).appendTo(row);
|
||||
// $('<button class="red-ui-button red-ui-button-small"></button>').text(RED._("sidebar.project.projectSettings.edit")).appendTo(
|
||||
// $('<span>').appendTo(row)
|
||||
// ).on("click", function(evt) {
|
||||
// if (addingLibrary) {
|
||||
// return;
|
||||
// }
|
||||
// evt.preventDefault();
|
||||
// addingLibrary = true;
|
||||
// row.empty();
|
||||
// row.removeClass("red-ui-settings-tab-library-entry");
|
||||
// showLibraryDetailsDialog(row,itemData,function(newItem) {
|
||||
// var itemIndex = libraryList.editableList("indexOf", itemData);
|
||||
// libraryList.editableList("removeItem", itemData);
|
||||
// if (newItem) {
|
||||
// libraryList.editableList("insertItemAt", newItem, itemIndex);
|
||||
// } else {
|
||||
// libraryList.editableList("insertItemAt", itemData,itemIndex);
|
||||
// }
|
||||
// addingLibrary = false;
|
||||
//
|
||||
// })
|
||||
// })
|
||||
//
|
||||
// } else {
|
||||
// showLibraryDetailsDialog(row,null,function(newItem) {
|
||||
// libraryList.editableList("removeItem", itemData);
|
||||
// if (newItem) {
|
||||
// libraryList.editableList("addItem", newItem);
|
||||
// }
|
||||
// addingLibrary = false;
|
||||
// })
|
||||
//
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
//
|
||||
// addButton.on('click', function(evt) {
|
||||
// evt.preventDefault();
|
||||
// if (!addingLibrary) {
|
||||
// addingLibrary = true;
|
||||
// libraryList.editableList("addItem",{user:true});
|
||||
// }
|
||||
// })
|
||||
// var libraries = RED.settings.libraries || [];
|
||||
// libraries.forEach(function(library) {
|
||||
// if (library.user) {
|
||||
// libraryList.editableList("addItem",library)
|
||||
// }
|
||||
// })
|
||||
//
|
||||
// return pane;
|
||||
// }
|
||||
//
|
||||
//
|
||||
return {
|
||||
init: function() {
|
||||
|
||||
// RED.events.on("registry:plugin-added", function(id) {
|
||||
// var plugin = RED.plugins.getPlugin(id);
|
||||
// if (plugin.type === "node-red-library-source") {
|
||||
// libraryPlugins[id] = plugin;
|
||||
// }
|
||||
// });
|
||||
//
|
||||
// RED.userSettings.add({
|
||||
// id:'library-manager',
|
||||
// title: "NLS: Libraries",
|
||||
// get: createSettingsPane,
|
||||
// close: function() {}
|
||||
// });
|
||||
$(_librarySave).appendTo("#red-ui-editor").i18n();
|
||||
$(_libraryLookup).appendTo("#red-ui-editor").i18n();
|
||||
|
||||
|
@ -232,3 +232,41 @@
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
#red-ui-settings-tab-library-manager {
|
||||
box-sizing: border-box;
|
||||
padding: 10px;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
li {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.red-ui-settings-tab-library-entry {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
span:not(:nth-child(2)) {
|
||||
@include disable-selection;
|
||||
}
|
||||
span {
|
||||
padding: 8px 0;
|
||||
}
|
||||
span:first-child {
|
||||
display: inline-block;
|
||||
width: 20px;
|
||||
padding-right: 8px;
|
||||
text-align: center;
|
||||
flex-grow: 0;
|
||||
}
|
||||
span:nth-child(2) {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
span:nth-child(3), span:nth-child(4) {
|
||||
flex-grow: 0;
|
||||
padding-right: 5px;
|
||||
color: $secondary-text-color;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
}
|
@ -807,6 +807,7 @@ div.red-ui-projects-dialog-ssh-public-key {
|
||||
border: 1px solid $secondary-border-color;
|
||||
.red-ui-projects-edit-form-sublabel {
|
||||
margin-top: -8px !important;
|
||||
margin-right: 50px;
|
||||
display: block !important;
|
||||
width: auto !important;
|
||||
}
|
||||
|
@ -25,6 +25,27 @@ var api = module.exports = {
|
||||
runtime = _runtime;
|
||||
},
|
||||
|
||||
// /* *
|
||||
// * Gets the configuration of a library source.
|
||||
// * @param {Object} opts
|
||||
// * @param {User} opts.user - the user calling the api
|
||||
// * @param {String} opts.library - the library
|
||||
// * @param {Object} opts.req - the request to log (optional)
|
||||
// * @return {Promise<String|Object>} - resolves when complete
|
||||
// * @memberof @node-red/runtime_library
|
||||
// */
|
||||
// getConfig: async function(opts) {
|
||||
// runtime.log.audit({event: "library.getConfig",library:opts.library}, opts.req);
|
||||
// try {
|
||||
// return runtime.library.getConfig(opts.library)
|
||||
// } catch(err) {
|
||||
// var error = new Error();
|
||||
// error.code = "not_found";
|
||||
// error.status = 404;
|
||||
// throw error;
|
||||
// }
|
||||
// },
|
||||
|
||||
/**
|
||||
* Gets an entry from the library.
|
||||
* @param {Object} opts
|
||||
|
@ -95,7 +95,11 @@ function getEntry(type,path) {
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
name: '_examples_',
|
||||
id: "examples",
|
||||
label: "editor:library.types.examples",
|
||||
icon: "font-awesome/fa-life-ring",
|
||||
types: ["flows"],
|
||||
readOnly: true,
|
||||
init: init,
|
||||
getEntry: getEntry
|
||||
}
|
||||
|
@ -18,6 +18,7 @@
|
||||
const {events} = require("@node-red/util")
|
||||
const knownTypes = {};
|
||||
const libraries = {};
|
||||
const libraryConfigs = {};
|
||||
const libraryPlugins = {};
|
||||
|
||||
// Libraries defined in the settings file. Their configurations
|
||||
@ -37,7 +38,9 @@ function init(runtime) {
|
||||
runtimeLibraries.forEach(library => {
|
||||
if (library.type === id) {
|
||||
library.local = false;
|
||||
libraryConfigs[library.id] = library;
|
||||
libraries[library.id] = new plugin.class(library)
|
||||
libraryConfigs[library.id].type = id;
|
||||
if (libraries[library.id].init) {
|
||||
libraries[library.id].init();
|
||||
}
|
||||
@ -49,11 +52,13 @@ function init(runtime) {
|
||||
})
|
||||
|
||||
knownTypes.flows = 'node-red';
|
||||
libraries["local"] = require("./local");
|
||||
libraries["local"].init(runtime);
|
||||
libraryConfigs["local"] = libraries["local"]
|
||||
|
||||
libraries["examples"] = require("./examples");
|
||||
libraries["examples"].init(runtime);
|
||||
libraries["local"] = require("./local");
|
||||
libraries["local"].init(runtime);
|
||||
libraryConfigs["examples"] = libraries["examples"]
|
||||
|
||||
try {
|
||||
runtimeLibraries = runtime.settings.editorTheme.library.sources;
|
||||
@ -100,42 +105,58 @@ function saveEntry(library,type,path,meta,body) {
|
||||
}
|
||||
|
||||
function getLibraries() {
|
||||
const libraryList = [
|
||||
{
|
||||
id: "local",
|
||||
label: "editor:library.types.local",
|
||||
user: false,
|
||||
icon: "fa fa-bath"
|
||||
},
|
||||
{
|
||||
id: "examples",
|
||||
label: "editor:library.types.examples",
|
||||
user: false,
|
||||
readOnly: true,
|
||||
types: ['flows']
|
||||
}
|
||||
];
|
||||
|
||||
const libraryList = []
|
||||
for (let id in libraries) {
|
||||
if (libraries.hasOwnProperty(id)) {
|
||||
if (id !== 'local' && id !== 'examples') {
|
||||
libraryList.push({
|
||||
id: id,
|
||||
label: libraries[id].name || id,
|
||||
user: false,
|
||||
icon: libraries[id].icon
|
||||
})
|
||||
var config = getConfig(id);
|
||||
// Don't include the full configuration of each library when providing
|
||||
// the list of all libraries
|
||||
delete config.config;
|
||||
libraryList.push(config);
|
||||
}
|
||||
}
|
||||
return libraryList;
|
||||
}
|
||||
|
||||
return libraryList;
|
||||
function getConfig(id) {
|
||||
var lib = {
|
||||
id: id,
|
||||
label: libraryConfigs[id].label || id,
|
||||
user: false,
|
||||
icon: libraryConfigs[id].icon
|
||||
}
|
||||
if (libraryConfigs[id].types) {
|
||||
lib.types = libraryConfigs[id].types
|
||||
}
|
||||
if (libraryConfigs[id].readOnly) {
|
||||
lib.readOnly = libraryConfigs[id].readOnly
|
||||
}
|
||||
|
||||
if (libraryConfigs[id].type) {
|
||||
lib.type = libraryConfigs[id].type;
|
||||
|
||||
var def = libraryPlugins[lib.type];
|
||||
if (def && def.defaults) {
|
||||
lib.config = {};
|
||||
for (var d in def.defaults) {
|
||||
if (def.defaults.hasOwnProperty(d)) {
|
||||
if (def.defaults[d].type !== 'password') {
|
||||
lib.config[d] = libraryConfigs[id][d];
|
||||
} else if (!!libraryConfigs[id][d]) {
|
||||
lib.config["has_"+d] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return lib;
|
||||
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
init: init,
|
||||
getLibraries: getLibraries,
|
||||
// getConfig: getConfig,
|
||||
register: registerType,
|
||||
getEntry: getEntry,
|
||||
saveEntry: saveEntry
|
||||
|
@ -30,7 +30,9 @@ function saveEntry(type,path,meta,body) {
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
name: 'local',
|
||||
id: "local",
|
||||
label: "editor:library.types.local",
|
||||
icon: "font-awesome/fa-hdd-o",
|
||||
init: init,
|
||||
getEntry: getEntry,
|
||||
saveEntry: saveEntry
|
||||
|
@ -4,8 +4,19 @@
|
||||
name: "Local File-System Library",
|
||||
icon: "font-awesome/fa-hdd-o",
|
||||
defaults: {
|
||||
name: { value: "" },
|
||||
path: { value: ""}
|
||||
path: { value: ""},
|
||||
// secret: { type: "password" }
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
|
||||
RED.plugins.registerPlugin("node-red-library-dbstore", {
|
||||
type: "node-red-library-source",
|
||||
name: "Database Library",
|
||||
icon: "font-awesome/fa-database",
|
||||
defaults: {
|
||||
connection: { value: ""}
|
||||
}
|
||||
})
|
||||
</script>
|
@ -1,11 +1,13 @@
|
||||
|
||||
module.exports = function(RED) {
|
||||
const PLUGIN_TYPE_ID = "node-red-library-filestore";
|
||||
|
||||
class FileStorePlugin {
|
||||
constructor(config) {
|
||||
this.type = PLUGIN_TYPE_ID;
|
||||
this.id = config.id;
|
||||
this.name = config.name;
|
||||
this.config = config;
|
||||
this.label = config.label;
|
||||
this.config = config.config;
|
||||
this.icon = config.icon;
|
||||
|
||||
console.log("FileStorePlugin",config)
|
||||
@ -20,13 +22,16 @@ module.exports = function(RED) {
|
||||
}
|
||||
async saveEntry(type,path,meta,body) {
|
||||
console.log("FileStorePlugin.saveLibraryEntry",type,path)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
RED.plugins.registerPlugin("node-red-library-filestore", {
|
||||
RED.plugins.registerPlugin(PLUGIN_TYPE_ID, {
|
||||
type: "node-red-library-source",
|
||||
class: FileStorePlugin
|
||||
class: FileStorePlugin,
|
||||
defaults: {
|
||||
"path": { value: "" },
|
||||
// "secret": { type: "password" }
|
||||
}
|
||||
})
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
{
|
||||
"label": {
|
||||
"path": "Path"
|
||||
},
|
||||
"desc": {
|
||||
"path":"The local file-system path to the library"
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user