mirror of
https://github.com/node-red/node-red.git
synced 2023-10-10 13:36:53 +02:00
Initial projects implementation
This commit is contained in:
parent
9a8b404054
commit
b1cd13d629
@ -153,6 +153,7 @@ module.exports = function(grunt) {
|
|||||||
"editor/js/ui/typeSearch.js",
|
"editor/js/ui/typeSearch.js",
|
||||||
"editor/js/ui/subflow.js",
|
"editor/js/ui/subflow.js",
|
||||||
"editor/js/ui/userSettings.js",
|
"editor/js/ui/userSettings.js",
|
||||||
|
"editor/js/ui/projects.js",
|
||||||
"editor/js/ui/touch/radialMenu.js"
|
"editor/js/ui/touch/radialMenu.js"
|
||||||
],
|
],
|
||||||
dest: "public/red/red.js"
|
dest: "public/red/red.js"
|
||||||
|
@ -321,6 +321,9 @@ RED.history = (function() {
|
|||||||
},
|
},
|
||||||
peek: function() {
|
peek: function() {
|
||||||
return undo_history[undo_history.length-1];
|
return undo_history[undo_history.length-1];
|
||||||
|
},
|
||||||
|
clear: function() {
|
||||||
|
undo_history = [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,11 @@
|
|||||||
"ctrl-e": "core:show-export-dialog",
|
"ctrl-e": "core:show-export-dialog",
|
||||||
"ctrl-i": "core:show-import-dialog",
|
"ctrl-i": "core:show-import-dialog",
|
||||||
"ctrl-space": "core:toggle-sidebar",
|
"ctrl-space": "core:toggle-sidebar",
|
||||||
"ctrl-,": "core:show-user-settings"
|
"ctrl-,": "core:show-user-settings",
|
||||||
|
|
||||||
|
"ctrl-alt-n": "core:new-project",
|
||||||
|
"ctrl-alt-o": "core:open-project",
|
||||||
|
"ctrl-g p": "core:show-projects-tab"
|
||||||
},
|
},
|
||||||
"workspace": {
|
"workspace": {
|
||||||
"backspace": "core:delete-selection",
|
"backspace": "core:delete-selection",
|
||||||
|
@ -42,12 +42,127 @@
|
|||||||
$("#palette > .palette-spinner").hide();
|
$("#palette > .palette-spinner").hide();
|
||||||
$(".palette-scroll").removeClass("hide");
|
$(".palette-scroll").removeClass("hide");
|
||||||
$("#palette-search").removeClass("hide");
|
$("#palette-search").removeClass("hide");
|
||||||
loadFlows();
|
loadFlows(function() {
|
||||||
|
RED.projects.refreshSidebar();
|
||||||
|
|
||||||
|
var persistentNotifications = {};
|
||||||
|
RED.comms.subscribe("notification/#",function(topic,msg) {
|
||||||
|
var parts = topic.split("/");
|
||||||
|
var notificationId = parts[1];
|
||||||
|
if (notificationId === "runtime-deploy") {
|
||||||
|
// handled in ui/deploy.js
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (notificationId === "node") {
|
||||||
|
// handled below
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (notificationId === "project-change") {
|
||||||
|
RED.nodes.clear();
|
||||||
|
RED.history.clear();
|
||||||
|
RED.projects.refreshSidebar();
|
||||||
|
RED.projects.showSidebar();
|
||||||
|
loadFlows(function() {
|
||||||
|
RED.notify("NLS: Project changed to "+msg.project);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (msg.text) {
|
||||||
|
var text = RED._(msg.text,{default:msg.text});
|
||||||
|
if (notificationId === "runtime-state") {
|
||||||
|
if (msg.error === "credentials_load_failed") {
|
||||||
|
text += '<p><a href="#" onclick="RED.projects.showCredentialsPrompt(); return false;">'+'Setup credentials'+'</a></p>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!persistentNotifications.hasOwnProperty(notificationId)) {
|
||||||
|
persistentNotifications[notificationId] = RED.notify(text,msg.type,msg.timeout === undefined,msg.timeout);
|
||||||
|
} else {
|
||||||
|
persistentNotifications[notificationId].update(text,msg.timeout);
|
||||||
|
}
|
||||||
|
} else if (persistentNotifications.hasOwnProperty(notificationId)) {
|
||||||
|
persistentNotifications[notificationId].close();
|
||||||
|
delete persistentNotifications[notificationId];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
RED.comms.subscribe("status/#",function(topic,msg) {
|
||||||
|
var parts = topic.split("/");
|
||||||
|
var node = RED.nodes.node(parts[1]);
|
||||||
|
if (node) {
|
||||||
|
if (msg.hasOwnProperty("text")) {
|
||||||
|
if (msg.text[0] !== ".") {
|
||||||
|
msg.text = node._(msg.text.toString(),{defaultValue:msg.text.toString()});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
node.status = msg;
|
||||||
|
node.dirty = true;
|
||||||
|
RED.view.redraw();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
RED.comms.subscribe("notification/node/#",function(topic,msg) {
|
||||||
|
var i,m;
|
||||||
|
var typeList;
|
||||||
|
var info;
|
||||||
|
if (topic == "notification/node/added") {
|
||||||
|
var addedTypes = [];
|
||||||
|
msg.forEach(function(m) {
|
||||||
|
var id = m.id;
|
||||||
|
RED.nodes.addNodeSet(m);
|
||||||
|
addedTypes = addedTypes.concat(m.types);
|
||||||
|
RED.i18n.loadCatalog(id, function() {
|
||||||
|
$.get('nodes/'+id, function(data) {
|
||||||
|
$("body").append(data);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
if (addedTypes.length) {
|
||||||
|
typeList = "<ul><li>"+addedTypes.join("</li><li>")+"</li></ul>";
|
||||||
|
RED.notify(RED._("palette.event.nodeAdded", {count:addedTypes.length})+typeList,"success");
|
||||||
|
}
|
||||||
|
} else if (topic == "notification/node/removed") {
|
||||||
|
for (i=0;i<msg.length;i++) {
|
||||||
|
m = msg[i];
|
||||||
|
info = RED.nodes.removeNodeSet(m.id);
|
||||||
|
if (info.added) {
|
||||||
|
typeList = "<ul><li>"+m.types.join("</li><li>")+"</li></ul>";
|
||||||
|
RED.notify(RED._("palette.event.nodeRemoved", {count:m.types.length})+typeList,"success");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (topic == "notification/node/enabled") {
|
||||||
|
if (msg.types) {
|
||||||
|
info = RED.nodes.getNodeSet(msg.id);
|
||||||
|
if (info.added) {
|
||||||
|
RED.nodes.enableNodeSet(msg.id);
|
||||||
|
typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>";
|
||||||
|
RED.notify(RED._("palette.event.nodeEnabled", {count:msg.types.length})+typeList,"success");
|
||||||
|
} else {
|
||||||
|
$.get('nodes/'+msg.id, function(data) {
|
||||||
|
$("body").append(data);
|
||||||
|
typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>";
|
||||||
|
RED.notify(RED._("palette.event.nodeAdded", {count:msg.types.length})+typeList,"success");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (topic == "notification/node/disabled") {
|
||||||
|
if (msg.types) {
|
||||||
|
RED.nodes.disableNodeSet(msg.id);
|
||||||
|
typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>";
|
||||||
|
RED.notify(RED._("palette.event.nodeDisabled", {count:msg.types.length})+typeList,"success");
|
||||||
|
}
|
||||||
|
} else if (topic == "node/upgraded") {
|
||||||
|
RED.notify(RED._("palette.event.nodeUpgraded", {module:msg.module,version:msg.version}),"success");
|
||||||
|
RED.nodes.registry.setModulePendingUpdated(msg.module,msg.version);
|
||||||
|
}
|
||||||
|
// Refresh flow library to ensure any examples are updated
|
||||||
|
RED.library.loadFlowLibrary();
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadFlows() {
|
function loadFlows(done) {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
headers: {
|
headers: {
|
||||||
"Accept":"application/json",
|
"Accept":"application/json",
|
||||||
@ -55,110 +170,17 @@
|
|||||||
cache: false,
|
cache: false,
|
||||||
url: 'flows',
|
url: 'flows',
|
||||||
success: function(nodes) {
|
success: function(nodes) {
|
||||||
var currentHash = window.location.hash;
|
if (nodes) {
|
||||||
RED.nodes.version(nodes.rev);
|
var currentHash = window.location.hash;
|
||||||
RED.nodes.import(nodes.flows);
|
RED.nodes.version(nodes.rev);
|
||||||
RED.nodes.dirty(false);
|
RED.nodes.import(nodes.flows);
|
||||||
RED.view.redraw(true);
|
RED.nodes.dirty(false);
|
||||||
if (/^#flow\/.+$/.test(currentHash)) {
|
RED.view.redraw(true);
|
||||||
RED.workspaces.show(currentHash.substring(6));
|
if (/^#flow\/.+$/.test(currentHash)) {
|
||||||
|
RED.workspaces.show(currentHash.substring(6));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
done();
|
||||||
var persistentNotifications = {};
|
|
||||||
RED.comms.subscribe("notification/#",function(topic,msg) {
|
|
||||||
var parts = topic.split("/");
|
|
||||||
var notificationId = parts[1];
|
|
||||||
if (notificationId === "runtime-deploy") {
|
|
||||||
// handled in ui/deploy.js
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (notificationId === "node") {
|
|
||||||
// handled below
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (msg.text) {
|
|
||||||
var text = RED._(msg.text,{default:msg.text});
|
|
||||||
if (!persistentNotifications.hasOwnProperty(notificationId)) {
|
|
||||||
persistentNotifications[notificationId] = RED.notify(text,msg.type,msg.timeout === undefined,msg.timeout);
|
|
||||||
} else {
|
|
||||||
persistentNotifications[notificationId].update(text,msg.timeout);
|
|
||||||
}
|
|
||||||
} else if (persistentNotifications.hasOwnProperty(notificationId)) {
|
|
||||||
persistentNotifications[notificationId].close();
|
|
||||||
delete persistentNotifications[notificationId];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
RED.comms.subscribe("status/#",function(topic,msg) {
|
|
||||||
var parts = topic.split("/");
|
|
||||||
var node = RED.nodes.node(parts[1]);
|
|
||||||
if (node) {
|
|
||||||
if (msg.hasOwnProperty("text")) {
|
|
||||||
if (msg.text[0] !== ".") {
|
|
||||||
msg.text = node._(msg.text.toString(),{defaultValue:msg.text.toString()});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
node.status = msg;
|
|
||||||
node.dirty = true;
|
|
||||||
RED.view.redraw();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
RED.comms.subscribe("notification/node/#",function(topic,msg) {
|
|
||||||
var i,m;
|
|
||||||
var typeList;
|
|
||||||
var info;
|
|
||||||
if (topic == "notification/node/added") {
|
|
||||||
var addedTypes = [];
|
|
||||||
msg.forEach(function(m) {
|
|
||||||
var id = m.id;
|
|
||||||
RED.nodes.addNodeSet(m);
|
|
||||||
addedTypes = addedTypes.concat(m.types);
|
|
||||||
RED.i18n.loadCatalog(id, function() {
|
|
||||||
$.get('nodes/'+id, function(data) {
|
|
||||||
$("body").append(data);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
if (addedTypes.length) {
|
|
||||||
typeList = "<ul><li>"+addedTypes.join("</li><li>")+"</li></ul>";
|
|
||||||
RED.notify(RED._("palette.event.nodeAdded", {count:addedTypes.length})+typeList,"success");
|
|
||||||
}
|
|
||||||
} else if (topic == "notification/node/removed") {
|
|
||||||
for (i=0;i<msg.length;i++) {
|
|
||||||
m = msg[i];
|
|
||||||
info = RED.nodes.removeNodeSet(m.id);
|
|
||||||
if (info.added) {
|
|
||||||
typeList = "<ul><li>"+m.types.join("</li><li>")+"</li></ul>";
|
|
||||||
RED.notify(RED._("palette.event.nodeRemoved", {count:m.types.length})+typeList,"success");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (topic == "notification/node/enabled") {
|
|
||||||
if (msg.types) {
|
|
||||||
info = RED.nodes.getNodeSet(msg.id);
|
|
||||||
if (info.added) {
|
|
||||||
RED.nodes.enableNodeSet(msg.id);
|
|
||||||
typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>";
|
|
||||||
RED.notify(RED._("palette.event.nodeEnabled", {count:msg.types.length})+typeList,"success");
|
|
||||||
} else {
|
|
||||||
$.get('nodes/'+msg.id, function(data) {
|
|
||||||
$("body").append(data);
|
|
||||||
typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>";
|
|
||||||
RED.notify(RED._("palette.event.nodeAdded", {count:msg.types.length})+typeList,"success");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (topic == "notification/node/disabled") {
|
|
||||||
if (msg.types) {
|
|
||||||
RED.nodes.disableNodeSet(msg.id);
|
|
||||||
typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>";
|
|
||||||
RED.notify(RED._("palette.event.nodeDisabled", {count:msg.types.length})+typeList,"success");
|
|
||||||
}
|
|
||||||
} else if (topic == "node/upgraded") {
|
|
||||||
RED.notify(RED._("palette.event.nodeUpgraded", {module:msg.module,version:msg.version}),"success");
|
|
||||||
RED.nodes.registry.setModulePendingUpdated(msg.module,msg.version);
|
|
||||||
}
|
|
||||||
// Refresh flow library to ensure any examples are updated
|
|
||||||
RED.library.loadFlowLibrary();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -176,6 +198,13 @@
|
|||||||
|
|
||||||
function loadEditor() {
|
function loadEditor() {
|
||||||
var menuOptions = [];
|
var menuOptions = [];
|
||||||
|
|
||||||
|
menuOptions.push({id:"menu-item-projects-menu",label:"NLS: Projects",options:[
|
||||||
|
{id:"menu-item-projects-new",label:"New...",disabled:false,onselect:"core:new-project"},
|
||||||
|
{id:"menu-item-projects-open",label:"Open...",disabled:false,onselect:"core:open-project"},
|
||||||
|
]});
|
||||||
|
|
||||||
|
|
||||||
menuOptions.push({id:"menu-item-view-menu",label:RED._("menu.label.view.view"),options:[
|
menuOptions.push({id:"menu-item-view-menu",label:RED._("menu.label.view.view"),options:[
|
||||||
// {id:"menu-item-view-show-grid",setting:"view-show-grid",label:RED._("menu.label.view.showGrid"),toggle:true,onselect:"core:toggle-show-grid"},
|
// {id:"menu-item-view-show-grid",setting:"view-show-grid",label:RED._("menu.label.view.showGrid"),toggle:true,onselect:"core:toggle-show-grid"},
|
||||||
// {id:"menu-item-view-snap-grid",setting:"view-snap-grid",label:RED._("menu.label.view.snapGrid"),toggle:true,onselect:"core:toggle-snap-grid"},
|
// {id:"menu-item-view-snap-grid",setting:"view-snap-grid",label:RED._("menu.label.view.snapGrid"),toggle:true,onselect:"core:toggle-snap-grid"},
|
||||||
@ -241,6 +270,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
RED.sidebar.init();
|
RED.sidebar.init();
|
||||||
|
RED.projects.init();
|
||||||
RED.subflow.init();
|
RED.subflow.init();
|
||||||
RED.workspaces.init();
|
RED.workspaces.init();
|
||||||
RED.clipboard.init();
|
RED.clipboard.init();
|
||||||
|
@ -1201,11 +1201,12 @@ RED.nodes = (function() {
|
|||||||
});
|
});
|
||||||
defaultWorkspace = null;
|
defaultWorkspace = null;
|
||||||
|
|
||||||
RED.nodes.dirty(true);
|
RED.nodes.dirty(false);
|
||||||
RED.view.redraw(true);
|
RED.view.redraw(true);
|
||||||
RED.palette.refresh();
|
RED.palette.refresh();
|
||||||
RED.workspaces.refresh();
|
RED.workspaces.refresh();
|
||||||
RED.sidebar.config.refresh();
|
RED.sidebar.config.refresh();
|
||||||
|
RED.sidebar.info.refresh();
|
||||||
|
|
||||||
// var node_defs = {};
|
// var node_defs = {};
|
||||||
// var nodes = [];
|
// var nodes = [];
|
||||||
|
@ -17,11 +17,25 @@
|
|||||||
RED.stack = (function() {
|
RED.stack = (function() {
|
||||||
function createStack(options) {
|
function createStack(options) {
|
||||||
var container = options.container;
|
var container = options.container;
|
||||||
|
container.addClass("red-ui-stack");
|
||||||
|
var contentHeight = 0;
|
||||||
var entries = [];
|
var entries = [];
|
||||||
|
|
||||||
var visible = true;
|
var visible = true;
|
||||||
|
var resizeStack = function() {
|
||||||
|
if (entries.length > 0) {
|
||||||
|
var headerHeight = entries[0].header.outerHeight();
|
||||||
|
var height = container.innerHeight();
|
||||||
|
contentHeight = height - entries.length*headerHeight - (entries.length-1);
|
||||||
|
entries.forEach(function(e) {
|
||||||
|
e.contentWrap.height(contentHeight);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (options.fill && options.singleExpanded) {
|
||||||
|
$(window).resize(resizeStack);
|
||||||
|
$(window).focus(resizeStack);
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
add: function(entry) {
|
add: function(entry) {
|
||||||
entries.push(entry);
|
entries.push(entry);
|
||||||
@ -30,7 +44,12 @@ RED.stack = (function() {
|
|||||||
entry.container.hide();
|
entry.container.hide();
|
||||||
}
|
}
|
||||||
var header = $('<div class="palette-header"></div>').appendTo(entry.container);
|
var header = $('<div class="palette-header"></div>').appendTo(entry.container);
|
||||||
entry.content = $('<div></div>').appendTo(entry.container);
|
entry.header = header;
|
||||||
|
entry.contentWrap = $('<div></div>',{style:"position:relative"}).appendTo(entry.container);
|
||||||
|
if (options.fill) {
|
||||||
|
entry.contentWrap.css("height",contentHeight);
|
||||||
|
}
|
||||||
|
entry.content = $('<div></div>').appendTo(entry.contentWrap);
|
||||||
if (entry.collapsible !== false) {
|
if (entry.collapsible !== false) {
|
||||||
header.click(function() {
|
header.click(function() {
|
||||||
if (options.singleExpanded) {
|
if (options.singleExpanded) {
|
||||||
@ -49,11 +68,13 @@ RED.stack = (function() {
|
|||||||
var icon = $('<i class="fa fa-angle-down"></i>').appendTo(header);
|
var icon = $('<i class="fa fa-angle-down"></i>').appendTo(header);
|
||||||
|
|
||||||
if (entry.expanded) {
|
if (entry.expanded) {
|
||||||
|
entry.container.addClass("palette-category-expanded");
|
||||||
icon.addClass("expanded");
|
icon.addClass("expanded");
|
||||||
} else {
|
} else {
|
||||||
entry.content.hide();
|
entry.contentWrap.hide();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
$('<i style="opacity: 0.5;" class="fa fa-angle-down expanded"></i>').appendTo(header);
|
||||||
header.css("cursor","default");
|
header.css("cursor","default");
|
||||||
}
|
}
|
||||||
entry.title = $('<span></span>').html(entry.title).appendTo(header);
|
entry.title = $('<span></span>').html(entry.title).appendTo(header);
|
||||||
@ -75,23 +96,26 @@ RED.stack = (function() {
|
|||||||
entry.onexpand.call(entry);
|
entry.onexpand.call(entry);
|
||||||
}
|
}
|
||||||
icon.addClass("expanded");
|
icon.addClass("expanded");
|
||||||
entry.content.slideDown(200);
|
entry.container.addClass("palette-category-expanded");
|
||||||
|
entry.contentWrap.slideDown(200);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
entry.collapse = function() {
|
entry.collapse = function() {
|
||||||
if (entry.isExpanded()) {
|
if (entry.isExpanded()) {
|
||||||
icon.removeClass("expanded");
|
icon.removeClass("expanded");
|
||||||
entry.content.slideUp(200);
|
entry.container.removeClass("palette-category-expanded");
|
||||||
|
entry.contentWrap.slideUp(200);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
entry.isExpanded = function() {
|
entry.isExpanded = function() {
|
||||||
return icon.hasClass("expanded");
|
return entry.container.hasClass("palette-category-expanded");
|
||||||
};
|
};
|
||||||
|
if (options.fill && options.singleExpanded) {
|
||||||
|
resizeStack();
|
||||||
|
}
|
||||||
return entry;
|
return entry;
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
hide: function() {
|
hide: function() {
|
||||||
@ -108,9 +132,13 @@ RED.stack = (function() {
|
|||||||
entry.container.show();
|
entry.container.show();
|
||||||
});
|
});
|
||||||
return this;
|
return this;
|
||||||
|
},
|
||||||
|
resize: function() {
|
||||||
|
if (resizeStack) {
|
||||||
|
resizeStack();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -437,6 +437,10 @@ RED.deploy = (function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
init: init
|
init: init,
|
||||||
|
setDeployInflight: function(state) {
|
||||||
|
deployInflight = state;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
@ -496,8 +496,12 @@ RED.editor = (function() {
|
|||||||
label = RED._("expressionEditor.title");
|
label = RED._("expressionEditor.title");
|
||||||
} else if (node.type === '_json') {
|
} else if (node.type === '_json') {
|
||||||
label = RED._("jsonEditor.title");
|
label = RED._("jsonEditor.title");
|
||||||
|
} else if (node.type === '_markdown') {
|
||||||
|
label = RED._("markdownEditor.title");
|
||||||
} else if (node.type === '_buffer') {
|
} else if (node.type === '_buffer') {
|
||||||
label = RED._("bufferEditor.title");
|
label = RED._("bufferEditor.title");
|
||||||
|
} else if (node.type === '_project') {
|
||||||
|
label = "NLS: Edit project settings";
|
||||||
} else if (node.type === 'subflow') {
|
} else if (node.type === 'subflow') {
|
||||||
label = RED._("subflow.editSubflow",{name:node.name})
|
label = RED._("subflow.editSubflow",{name:node.name})
|
||||||
} else if (node.type.indexOf("subflow:")===0) {
|
} else if (node.type.indexOf("subflow:")===0) {
|
||||||
@ -1979,7 +1983,7 @@ RED.editor = (function() {
|
|||||||
var expressionEditor;
|
var expressionEditor;
|
||||||
|
|
||||||
var trayOptions = {
|
var trayOptions = {
|
||||||
title: getEditStackTitle(),
|
title: options.title || getEditStackTitle(),
|
||||||
buttons: [
|
buttons: [
|
||||||
{
|
{
|
||||||
id: "node-dialog-cancel",
|
id: "node-dialog-cancel",
|
||||||
@ -2044,6 +2048,139 @@ RED.editor = (function() {
|
|||||||
RED.tray.show(trayOptions);
|
RED.tray.show(trayOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function editMarkdown(options) {
|
||||||
|
var value = options.value;
|
||||||
|
var onComplete = options.complete;
|
||||||
|
var type = "_markdown"
|
||||||
|
editStack.push({type:type});
|
||||||
|
RED.view.state(RED.state.EDITING);
|
||||||
|
var expressionEditor;
|
||||||
|
|
||||||
|
var trayOptions = {
|
||||||
|
title: options.title || getEditStackTitle(),
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
id: "node-dialog-cancel",
|
||||||
|
text: RED._("common.label.cancel"),
|
||||||
|
click: function() {
|
||||||
|
RED.tray.close();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "node-dialog-ok",
|
||||||
|
text: RED._("common.label.done"),
|
||||||
|
class: "primary",
|
||||||
|
click: function() {
|
||||||
|
onComplete(expressionEditor.getValue());
|
||||||
|
RED.tray.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
resize: function(dimensions) {
|
||||||
|
editTrayWidthCache[type] = dimensions.width;
|
||||||
|
|
||||||
|
var rows = $("#dialog-form>div:not(.node-text-editor-row)");
|
||||||
|
var editorRow = $("#dialog-form>div.node-text-editor-row");
|
||||||
|
var height = $("#dialog-form").height();
|
||||||
|
for (var i=0;i<rows.size();i++) {
|
||||||
|
height -= $(rows[i]).outerHeight(true);
|
||||||
|
}
|
||||||
|
height -= (parseInt($("#dialog-form").css("marginTop"))+parseInt($("#dialog-form").css("marginBottom")));
|
||||||
|
$(".node-text-editor").css("height",height+"px");
|
||||||
|
expressionEditor.resize();
|
||||||
|
},
|
||||||
|
open: function(tray) {
|
||||||
|
var trayBody = tray.find('.editor-tray-body');
|
||||||
|
var dialogForm = buildEditForm(tray.find('.editor-tray-body'),'dialog-form',type,'editor');
|
||||||
|
expressionEditor = RED.editor.createEditor({
|
||||||
|
id: 'node-input-markdown',
|
||||||
|
value: value,
|
||||||
|
mode:"ace/mode/markdown"
|
||||||
|
});
|
||||||
|
dialogForm.i18n();
|
||||||
|
},
|
||||||
|
close: function() {
|
||||||
|
editStack.pop();
|
||||||
|
expressionEditor.destroy();
|
||||||
|
},
|
||||||
|
show: function() {}
|
||||||
|
}
|
||||||
|
if (editTrayWidthCache.hasOwnProperty(type)) {
|
||||||
|
trayOptions.width = editTrayWidthCache[type];
|
||||||
|
}
|
||||||
|
RED.tray.show(trayOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
function editProject(options) {
|
||||||
|
var project = options.project;
|
||||||
|
var onComplete = options.complete;
|
||||||
|
var type = "_project"
|
||||||
|
editStack.push({type:type});
|
||||||
|
RED.view.state(RED.state.EDITING);
|
||||||
|
|
||||||
|
var trayOptions = {
|
||||||
|
title: options.title || getEditStackTitle(),
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
id: "node-dialog-cancel",
|
||||||
|
text: RED._("common.label.cancel"),
|
||||||
|
click: function() {
|
||||||
|
RED.tray.close();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "node-dialog-ok",
|
||||||
|
text: RED._("common.label.done"),
|
||||||
|
class: "primary",
|
||||||
|
click: function() {
|
||||||
|
onComplete("Whheeeeee");
|
||||||
|
RED.tray.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
resize: function(dimensions) {
|
||||||
|
// editTrayWidthCache[type] = dimensions.width;
|
||||||
|
//
|
||||||
|
// var rows = $("#dialog-form>div:not(.node-text-editor-row)");
|
||||||
|
// var editorRow = $("#dialog-form>div.node-text-editor-row");
|
||||||
|
// var height = $("#dialog-form").height();
|
||||||
|
// for (var i=0;i<rows.size();i++) {
|
||||||
|
// height -= $(rows[i]).outerHeight(true);
|
||||||
|
// }
|
||||||
|
// height -= (parseInt($("#dialog-form").css("marginTop"))+parseInt($("#dialog-form").css("marginBottom")));
|
||||||
|
// $(".node-text-editor").css("height",height+"px");
|
||||||
|
// expressionEditor.resize();
|
||||||
|
},
|
||||||
|
open: function(tray) {
|
||||||
|
var trayBody = tray.find('.editor-tray-body');
|
||||||
|
trayBody.addClass("projects-edit-form");
|
||||||
|
var dialogForm = buildEditForm(trayBody,'dialog-form',type,'editor');
|
||||||
|
project._def = projectNodeDefinition;
|
||||||
|
prepareEditDialog(project,project._def,"node-input-project");
|
||||||
|
dialogForm.i18n();
|
||||||
|
},
|
||||||
|
close: function() {
|
||||||
|
editStack.pop();
|
||||||
|
},
|
||||||
|
show: function() {}
|
||||||
|
}
|
||||||
|
if (editTrayWidthCache.hasOwnProperty(type)) {
|
||||||
|
trayOptions.width = editTrayWidthCache[type];
|
||||||
|
}
|
||||||
|
RED.tray.show(trayOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
var projectNodeDefinition = {
|
||||||
|
defaults:{
|
||||||
|
name: {default:""},
|
||||||
|
summary: {default:""},
|
||||||
|
key: {default:""}
|
||||||
|
},
|
||||||
|
oneditprepare: function() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function stringToUTF8Array(str) {
|
function stringToUTF8Array(str) {
|
||||||
var data = [];
|
var data = [];
|
||||||
var i=0, l = str.length;
|
var i=0, l = str.length;
|
||||||
@ -2248,13 +2385,15 @@ RED.editor = (function() {
|
|||||||
editSubflow: showEditSubflowDialog,
|
editSubflow: showEditSubflowDialog,
|
||||||
editExpression: editExpression,
|
editExpression: editExpression,
|
||||||
editJSON: editJSON,
|
editJSON: editJSON,
|
||||||
|
editMarkdown: editMarkdown,
|
||||||
editBuffer: editBuffer,
|
editBuffer: editBuffer,
|
||||||
|
editProject: editProject,
|
||||||
validateNode: validateNode,
|
validateNode: validateNode,
|
||||||
updateNodeProperties: updateNodeProperties, // TODO: only exposed for edit-undo
|
updateNodeProperties: updateNodeProperties, // TODO: only exposed for edit-undo
|
||||||
|
|
||||||
|
|
||||||
createEditor: function(options) {
|
createEditor: function(options) {
|
||||||
var editor = ace.edit(options.id);
|
var editor = ace.edit(options.id||options.element);
|
||||||
editor.setTheme("ace/theme/tomorrow");
|
editor.setTheme("ace/theme/tomorrow");
|
||||||
var session = editor.getSession();
|
var session = editor.getSession();
|
||||||
if (options.mode) {
|
if (options.mode) {
|
||||||
|
@ -206,11 +206,11 @@ RED.palette = (function() {
|
|||||||
RED.view.focus();
|
RED.view.focus();
|
||||||
var helpText;
|
var helpText;
|
||||||
if (nt.indexOf("subflow:") === 0) {
|
if (nt.indexOf("subflow:") === 0) {
|
||||||
helpText = marked(RED.nodes.subflow(nt.substring(8)).info||"");
|
helpText = marked(RED.nodes.subflow(nt.substring(8)).info||"")||('<span class="node-info-none">'+RED._("sidebar.info.none")+'</span>');
|
||||||
} else {
|
} else {
|
||||||
helpText = $("script[data-help-name='"+d.type+"']").html()||"";
|
helpText = $("script[data-help-name='"+d.type+"']").html()||('<span class="node-info-none">'+RED._("sidebar.info.none")+'</span>');
|
||||||
}
|
}
|
||||||
RED.sidebar.info.set(helpText);
|
RED.sidebar.info.set(helpText,RED._("sidebar.info.nodeHelp"));
|
||||||
});
|
});
|
||||||
var chart = $("#chart");
|
var chart = $("#chart");
|
||||||
var chartOffset = chart.offset();
|
var chartOffset = chart.offset();
|
||||||
|
998
editor/js/ui/projects.js
Normal file
998
editor/js/ui/projects.js
Normal file
@ -0,0 +1,998 @@
|
|||||||
|
/**
|
||||||
|
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
**/
|
||||||
|
RED.projects = (function() {
|
||||||
|
|
||||||
|
var dialog;
|
||||||
|
var dialogBody;
|
||||||
|
|
||||||
|
var activeProject;
|
||||||
|
|
||||||
|
var screens = {};
|
||||||
|
function initScreens() {
|
||||||
|
screens = {
|
||||||
|
'welcome': {
|
||||||
|
content: function() {
|
||||||
|
var container = $('<div class="projects-dialog-screen-start"></div>');
|
||||||
|
var buttons = $('<div style="margin: 30px"></div>').appendTo(container);
|
||||||
|
var createNew = $('<button class="editor-button"><i class="fa fa-archive fa-2x"></i><i class="fa fa-plus-circle" style="position:absolute"></i><br/>Create a project</button>').appendTo(buttons);
|
||||||
|
createNew.click(function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
show('create');
|
||||||
|
});
|
||||||
|
var openExisting = $('<button class="editor-button"><i class="fa fa-folder-open-o fa-2x"></i><br/>Open a project</button>').appendTo(buttons);
|
||||||
|
openExisting.click(function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
show('open')
|
||||||
|
});
|
||||||
|
return container;
|
||||||
|
},
|
||||||
|
buttons: [
|
||||||
|
]
|
||||||
|
},
|
||||||
|
'create': (function() {
|
||||||
|
var projectNameInput;
|
||||||
|
var projectSummaryEditor;
|
||||||
|
var projectSecretInput;
|
||||||
|
var projectSecretSelect;
|
||||||
|
var copyProject;
|
||||||
|
var projectRepoInput;
|
||||||
|
|
||||||
|
return {
|
||||||
|
title: "Create a new project", // TODO: NLS
|
||||||
|
content: function() {
|
||||||
|
var container = $('<div class="projects-dialog-screen-create"></div>');
|
||||||
|
var row;
|
||||||
|
|
||||||
|
var validateForm = function() {
|
||||||
|
var projectName = projectNameInput.val();
|
||||||
|
var valid = true;
|
||||||
|
if (!/^[a-zA-Z0-9\-_]+$/.test(projectName)) {
|
||||||
|
if (projectNameInputChanged) {
|
||||||
|
projectNameInput.addClass("input-error");
|
||||||
|
}
|
||||||
|
valid = false;
|
||||||
|
} else {
|
||||||
|
projectNameInput.removeClass("input-error");
|
||||||
|
}
|
||||||
|
var projectType = $(".projects-dialog-screen-create-type.selected").data('type');
|
||||||
|
if (projectType === 'copy') {
|
||||||
|
if (!copyProject) {
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
} else if (projectType === 'clone') {
|
||||||
|
var repo = projectRepoInput.val();
|
||||||
|
if (repo.trim() === '') {
|
||||||
|
// TODO: could do more url regex checking...
|
||||||
|
if (projectRepoChanged) {
|
||||||
|
projectRepoInput.addClass("input-error");
|
||||||
|
}
|
||||||
|
valid = false;
|
||||||
|
} else {
|
||||||
|
projectRepoInput.removeClass("input-error");
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#projects-dialog-create").prop('disabled',!valid).toggleClass('disabled ui-button-disabled ui-state-disabled',!valid);
|
||||||
|
}
|
||||||
|
|
||||||
|
row = $('<div class="form-row button-group"></div>').appendTo(container);
|
||||||
|
var createAsEmpty = $('<button data-type="empty" class="editor-button projects-dialog-screen-create-type toggle selected"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-asterisk"></i><br/>Empty Project</button>').appendTo(row);
|
||||||
|
var createAsCopy = $('<button data-type="copy" class="editor-button projects-dialog-screen-create-type toggle"><i class="fa fa-archive fa-2x"></i><i class="fa fa-long-arrow-right fa-2x"></i><i class="fa fa-archive fa-2x"></i><br/>Copy existing</button>').appendTo(row);
|
||||||
|
var createAsClone = $('<button data-type="clone" class="editor-button projects-dialog-screen-create-type toggle"><i class="fa fa-git fa-2x"></i><i class="fa fa-arrows-h fa-2x"></i><i class="fa fa-archive fa-2x"></i><br/>Clone repository</button>').appendTo(row);
|
||||||
|
row.find(".projects-dialog-screen-create-type").click(function(evt) {
|
||||||
|
evt.preventDefault();
|
||||||
|
$(".projects-dialog-screen-create-type").removeClass('selected');
|
||||||
|
$(this).addClass('selected');
|
||||||
|
$(".projects-dialog-screen-create-row").hide();
|
||||||
|
$(".projects-dialog-screen-create-row-"+$(this).data('type')).show();
|
||||||
|
validateForm();
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
row = $('<div class="form-row"></div>').appendTo(container);
|
||||||
|
$('<label>Project name</label>').appendTo(row);
|
||||||
|
|
||||||
|
projectNameInput = $('<input type="text"></input>').appendTo(row);
|
||||||
|
var projectNameInputChanged = false;
|
||||||
|
projectNameInput.on("change keyup paste",function() { validateForm(); });
|
||||||
|
|
||||||
|
// Empty Project
|
||||||
|
row = $('<div class="form-row projects-dialog-screen-create-row projects-dialog-screen-create-row-empty"></div>').appendTo(container);
|
||||||
|
$('<label>Summary <small>(optional)</small></label>').appendTo(row);
|
||||||
|
projectSummaryEditor = $('<input type="text">').appendTo(row);
|
||||||
|
|
||||||
|
// Copy Project
|
||||||
|
row = $('<div class="hide form-row projects-dialog-screen-create-row projects-dialog-screen-create-row-copy"></div>').appendTo(container);
|
||||||
|
$('<label> Select project to copy</label>').appendTo(row);
|
||||||
|
var autoInsertedName = "";
|
||||||
|
createProjectList({
|
||||||
|
height: "250px",
|
||||||
|
small: true,
|
||||||
|
select: function(project) {
|
||||||
|
copyProject = project;
|
||||||
|
var projectName = projectNameInput.val();
|
||||||
|
if (projectName === "" || projectName === autoInsertedName) {
|
||||||
|
autoInsertedName = project.name+"-copy";
|
||||||
|
projectNameInput.val(autoInsertedName);
|
||||||
|
}
|
||||||
|
validateForm();
|
||||||
|
}
|
||||||
|
}).appendTo(row);
|
||||||
|
|
||||||
|
// Clone Project
|
||||||
|
row = $('<div class="hide form-row projects-dialog-screen-create-row projects-dialog-screen-create-row-clone"></div>').appendTo(container);
|
||||||
|
$('<label>Git repository URL</label>').appendTo(row);
|
||||||
|
projectRepoInput = $('<input type="text" placeholder="https://git.example.com/path/my-project.git"></input>').appendTo(row);
|
||||||
|
var projectRepoChanged = false;
|
||||||
|
projectRepoInput.on("change keyup paste",function() {
|
||||||
|
var repo = $(this).val();
|
||||||
|
var m = /\/([^/]+)\.git/.exec(repo);
|
||||||
|
if (m) {
|
||||||
|
var projectName = projectNameInput.val();
|
||||||
|
if (projectName === "" || projectName === autoInsertedName) {
|
||||||
|
autoInsertedName = m[1];
|
||||||
|
projectNameInput.val(autoInsertedName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
validateForm();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Secret - empty/clone
|
||||||
|
row = $('<div class="form-row projects-dialog-screen-create-row projects-dialog-screen-create-row-empty projects-dialog-screen-create-row-clone"></div>').appendTo(container);
|
||||||
|
$('<label>Credentials key</label>').appendTo(row);
|
||||||
|
projectSecretInput = $('<input type="text"></input>').appendTo(row);
|
||||||
|
|
||||||
|
return container;
|
||||||
|
},
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
// id: "clipboard-dialog-cancel",
|
||||||
|
text: RED._("common.label.cancel"),
|
||||||
|
click: function() {
|
||||||
|
$( this ).dialog( "close" );
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "projects-dialog-create",
|
||||||
|
text: "Create project", // TODO: nls
|
||||||
|
class: "primary disabled",
|
||||||
|
disabled: true,
|
||||||
|
click: function() {
|
||||||
|
var projectType = $(".projects-dialog-screen-create-type.selected").data('type');
|
||||||
|
var projectData = {
|
||||||
|
name: projectNameInput.val(),
|
||||||
|
}
|
||||||
|
if (projectType === 'empty') {
|
||||||
|
projectData.summary = projectSummaryEditor.val();
|
||||||
|
projectData.credentialSecret = projectSecretInput.val();
|
||||||
|
} else if (projectType === 'copy') {
|
||||||
|
projectData.copy = copyProject.name;
|
||||||
|
} else if (projectType === 'clone') {
|
||||||
|
projectData.credentialSecret = projectSecretInput.val();
|
||||||
|
projectData.remote = {
|
||||||
|
url: projectRepoInput.val()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sendRequest({
|
||||||
|
url: "projects",
|
||||||
|
type: "POST",
|
||||||
|
responses: {
|
||||||
|
200: function(data) {
|
||||||
|
switchProject(projectData.name,function(err,data) {
|
||||||
|
if (err) {
|
||||||
|
console.log("unexpected_error",error)
|
||||||
|
} else {
|
||||||
|
dialog.dialog( "close" );
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
400: {
|
||||||
|
'project_exists': function(error) {
|
||||||
|
console.log("already exists");
|
||||||
|
},
|
||||||
|
'git_error': function(error) {
|
||||||
|
console.log("git error",error);
|
||||||
|
},
|
||||||
|
'git_auth_failed': function(error) {
|
||||||
|
// getRepoAuthDetails(req);
|
||||||
|
console.log("git auth error",error);
|
||||||
|
},
|
||||||
|
'unexpected_error': function(error) {
|
||||||
|
console.log("unexpected_error",error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},projectData)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// if (projectType === 'empty') {
|
||||||
|
// show('credentialSecret');
|
||||||
|
// } else if (projectType === 'copy') {
|
||||||
|
// show('copy');
|
||||||
|
// } else if (projectType === 'clone') {
|
||||||
|
// show('clone');
|
||||||
|
// }
|
||||||
|
|
||||||
|
// var projectName = projectNameInput.val().trim();
|
||||||
|
// var projectRepoEnabled = projectRepoEnabledInput.prop('checked');
|
||||||
|
// var projectRepo = projectRepoInput.val().trim();
|
||||||
|
// if (projectName !== '') {
|
||||||
|
// var req = {
|
||||||
|
// name:projectName
|
||||||
|
// };
|
||||||
|
// if (projectRepoEnabled && projectRepo !== '') {
|
||||||
|
// req.remote = projectRepo;
|
||||||
|
// }
|
||||||
|
// console.log(req);
|
||||||
|
// sendRequest({
|
||||||
|
// url: "projects",
|
||||||
|
// type: "POST",
|
||||||
|
// responses: {
|
||||||
|
// 200: function(data) {
|
||||||
|
// console.log("Success!",data);
|
||||||
|
// },
|
||||||
|
// 400: {
|
||||||
|
// 'project_exists': function(error) {
|
||||||
|
// console.log("already exists");
|
||||||
|
// },
|
||||||
|
// 'git_error': function(error) {
|
||||||
|
// console.log("git error",error);
|
||||||
|
// },
|
||||||
|
// 'git_auth_failed': function(error) {
|
||||||
|
// // getRepoAuthDetails(req);
|
||||||
|
// console.log("git auth error",error);
|
||||||
|
// },
|
||||||
|
// 'unexpected_error': function(error) {
|
||||||
|
// console.log("unexpected_error",error)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// },req)
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
// $( this ).dialog( "close" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})(),
|
||||||
|
'credentialSecret': {
|
||||||
|
content: function() {
|
||||||
|
// Provide new secret or reset credentials.
|
||||||
|
var container = $('<div class="projects-dialog-screen-secret"></div>');
|
||||||
|
var row = $('<div class="form-row"></div>').appendTo(container);
|
||||||
|
// $('<label for=""><input type="radio" name="projects-dialog-credential-secret" value="set"> Update credential key</label>').appendTo(row);
|
||||||
|
$('<label for="projects-dialog-secret">Update credential key</label>').appendTo(row);
|
||||||
|
var projectSecret = $('<input id="projects-dialog-secret" type="password"></input>').appendTo(row);
|
||||||
|
|
||||||
|
return container;
|
||||||
|
},
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
// id: "clipboard-dialog-cancel",
|
||||||
|
text: RED._("common.label.cancel"),
|
||||||
|
click: function() {
|
||||||
|
dialog.dialog( "close" );
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// id: "clipboard-dialog-cancel",
|
||||||
|
text: "Update",
|
||||||
|
click: function() {
|
||||||
|
var done = function(err,data) {
|
||||||
|
if (err) {
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
dialog.dialog( "close" );
|
||||||
|
}
|
||||||
|
RED.deploy.setDeployInflight(true);
|
||||||
|
sendRequest({
|
||||||
|
url: "projects/"+activeProject.name,
|
||||||
|
type: "PUT",
|
||||||
|
responses: {
|
||||||
|
200: function(data) {
|
||||||
|
done(null,data);
|
||||||
|
},
|
||||||
|
400: {
|
||||||
|
'credentials_load_failed': function(error) {
|
||||||
|
done(error,null);
|
||||||
|
},
|
||||||
|
'unexpected_error': function(error) {
|
||||||
|
done(error,null);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},{credentialSecret:$('#projects-dialog-secret').val()}).always(function() {
|
||||||
|
RED.deploy.setDeployInflight(false);
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
'open': {
|
||||||
|
content: function() {
|
||||||
|
return createProjectList({
|
||||||
|
canSelectActive: false,
|
||||||
|
dblclick: function() {
|
||||||
|
$("#projects-dialog-open").click();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
// id: "clipboard-dialog-cancel",
|
||||||
|
text: RED._("common.label.cancel"),
|
||||||
|
click: function() {
|
||||||
|
$( this ).dialog( "close" );
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "projects-dialog-open",
|
||||||
|
text: "Open project", // TODO: nls
|
||||||
|
class: "primary disabled",
|
||||||
|
disabled: true,
|
||||||
|
click: function() {
|
||||||
|
switchProject(selectedProject.name,function(err,data) {
|
||||||
|
if (err) {
|
||||||
|
if (err.error === 'credentials_load_failed') {
|
||||||
|
dialog.dialog( "close" );
|
||||||
|
} else {
|
||||||
|
console.log("unexpected_error",err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dialog.dialog( "close" );
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function switchProject(name,done) {
|
||||||
|
RED.deploy.setDeployInflight(true);
|
||||||
|
modulesInUse = {};
|
||||||
|
sendRequest({
|
||||||
|
url: "projects/"+name,
|
||||||
|
type: "PUT",
|
||||||
|
responses: {
|
||||||
|
200: function(data) {
|
||||||
|
done(null,data);
|
||||||
|
},
|
||||||
|
400: {
|
||||||
|
'credentials_load_failed': function(error) {
|
||||||
|
done(error,null);
|
||||||
|
},
|
||||||
|
'unexpected_error': function(error) {
|
||||||
|
done(error,null);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},{active:true}).always(function() {
|
||||||
|
RED.deploy.setDeployInflight(false);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function show(s,options) {
|
||||||
|
if (!dialog) {
|
||||||
|
RED.projects.init();
|
||||||
|
}
|
||||||
|
var screen = screens[s];
|
||||||
|
var container = screen.content();
|
||||||
|
|
||||||
|
dialogBody.empty();
|
||||||
|
dialog.dialog('option','buttons',screen.buttons);
|
||||||
|
dialogBody.append(container);
|
||||||
|
dialog.dialog('option','title',screen.title||"");
|
||||||
|
dialog.dialog("open");
|
||||||
|
dialog.dialog({position: { 'my': 'center', 'at': 'center', 'of': window }});
|
||||||
|
}
|
||||||
|
|
||||||
|
var selectedProject = null;
|
||||||
|
|
||||||
|
function createProjectList(options) {
|
||||||
|
options = options||{};
|
||||||
|
var height = options.height || "300px";
|
||||||
|
selectedProject = null;
|
||||||
|
var container = $('<div></div>',{style:"min-height: "+height+"; height: "+height+";"});
|
||||||
|
|
||||||
|
var list = $('<ol>',{class:"projects-dialog-project-list", style:"height:"+height}).appendTo(container).editableList({
|
||||||
|
addButton: false,
|
||||||
|
scrollOnAdd: false,
|
||||||
|
addItem: function(row,index,entry) {
|
||||||
|
var header = $('<div></div>',{class:"projects-dialog-project-list-entry"}).appendTo(row);
|
||||||
|
$('<span class="projects-dialog-project-list-entry-icon"><i class="fa fa-archive"></i></span>').appendTo(header);
|
||||||
|
$('<span class="projects-dialog-project-list-entry-name" style=""></span>').text(entry.name).appendTo(header);
|
||||||
|
if (activeProject && activeProject.name === entry.name) {
|
||||||
|
header.addClass("projects-list-entry-current");
|
||||||
|
$('<span class="projects-dialog-project-list-entry-current">current</span>').appendTo(header);
|
||||||
|
if (options.canSelectActive === false) {
|
||||||
|
// active project cannot be selected; so skip the rest
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
header.addClass("selectable");
|
||||||
|
row.click(function(evt) {
|
||||||
|
$('.projects-dialog-project-list-entry').removeClass('selected');
|
||||||
|
header.addClass('selected');
|
||||||
|
$("#projects-dialog-open").prop('disabled',false).removeClass('disabled ui-button-disabled ui-state-disabled');
|
||||||
|
selectedProject = entry;
|
||||||
|
if (options.select) {
|
||||||
|
options.select(entry);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (options.dblclick) {
|
||||||
|
row.dblclick(function(evt) {
|
||||||
|
evt.preventDefault();
|
||||||
|
options.dblclick();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (options.small) {
|
||||||
|
list.addClass("projects-dialog-project-list-small")
|
||||||
|
}
|
||||||
|
$.getJSON("projects", function(data) {
|
||||||
|
data.projects.forEach(function(project) {
|
||||||
|
list.editableList('addItem',{name:project});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendRequest(options,body) {
|
||||||
|
// dialogBody.hide();
|
||||||
|
console.log(options.url);
|
||||||
|
var start = Date.now();
|
||||||
|
$(".projects-dialog-spinner").show();
|
||||||
|
$("#projects-dialog").parent().find(".ui-dialog-buttonset").children().css("visibility","hidden")
|
||||||
|
if (body) {
|
||||||
|
options.data = JSON.stringify(body);
|
||||||
|
options.contentType = "application/json; charset=utf-8";
|
||||||
|
}
|
||||||
|
var resultCallback;
|
||||||
|
var resultCallbackArgs;
|
||||||
|
return $.ajax(options).done(function(data,textStatus,xhr) {
|
||||||
|
if (options.responses && options.responses[200]) {
|
||||||
|
resultCallback = options.responses[200];
|
||||||
|
resultCallbackArgs = data;
|
||||||
|
}
|
||||||
|
}).fail(function(xhr,textStatus,err) {
|
||||||
|
if (options.responses && options.responses[xhr.status]) {
|
||||||
|
var responses = options.responses[xhr.status];
|
||||||
|
if (typeof responses === 'function') {
|
||||||
|
resultCallback = responses;
|
||||||
|
resultCallbackArgs = {error:responses.statusText};
|
||||||
|
return;
|
||||||
|
} else if (responses[xhr.responseJSON.error]) {
|
||||||
|
resultCallback = responses[xhr.responseJSON.error];
|
||||||
|
resultCallbackArgs = xhr.responseJSON;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log("Unhandled error response:");
|
||||||
|
console.log(xhr);
|
||||||
|
console.log(textStatus);
|
||||||
|
console.log(err);
|
||||||
|
}).always(function() {
|
||||||
|
var delta = Date.now() - start;
|
||||||
|
delta = Math.max(0,1000-delta);
|
||||||
|
setTimeout(function() {
|
||||||
|
// dialogBody.show();
|
||||||
|
$(".projects-dialog-spinner").hide();
|
||||||
|
$("#projects-dialog").parent().find(".ui-dialog-buttonset").children().css("visibility","")
|
||||||
|
if (resultCallback) {
|
||||||
|
resultCallback(resultCallbackArgs)
|
||||||
|
}
|
||||||
|
},delta);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
dialog = $('<div id="projects-dialog" class="hide node-red-dialog projects-edit-form"><form class="form-horizontal"></form><div class="projects-dialog-spinner hide"><img src="red/images/spin.svg"/></div></div>')
|
||||||
|
.appendTo("body")
|
||||||
|
.dialog({
|
||||||
|
modal: true,
|
||||||
|
autoOpen: false,
|
||||||
|
width: 600,
|
||||||
|
resizable: false,
|
||||||
|
open: function(e) {
|
||||||
|
$(this).parent().find(".ui-dialog-titlebar-close").hide();
|
||||||
|
// $("#header-shade").show();
|
||||||
|
// $("#editor-shade").show();
|
||||||
|
// $("#palette-shade").show();
|
||||||
|
// $("#sidebar-shade").show();
|
||||||
|
},
|
||||||
|
close: function(e) {
|
||||||
|
// $("#header-shade").hide();
|
||||||
|
// $("#editor-shade").hide();
|
||||||
|
// $("#palette-shade").hide();
|
||||||
|
// $("#sidebar-shade").hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
dialogBody = dialog.find("form");
|
||||||
|
|
||||||
|
RED.actions.add("core:new-project",RED.projects.newProject);
|
||||||
|
RED.actions.add("core:open-project",RED.projects.selectProject);
|
||||||
|
|
||||||
|
initScreens();
|
||||||
|
initSidebar();
|
||||||
|
}
|
||||||
|
|
||||||
|
// function getRepoAuthDetails(req) {
|
||||||
|
// var container = $('<div></div>');
|
||||||
|
//
|
||||||
|
// var row = $('<div class="form-row"></div>').appendTo(container);
|
||||||
|
// $('<label>Username</label>').appendTo(row);
|
||||||
|
// var usernameInput = $('<input type="text"></input>').appendTo(row);
|
||||||
|
//
|
||||||
|
// row = $('<div class="form-row"></div>').appendTo(container);
|
||||||
|
// $('<label>Password</label>').appendTo(row);
|
||||||
|
// var passwordInput = $('<input type="password"></input>').appendTo(row);
|
||||||
|
//
|
||||||
|
// dialogBody.empty();
|
||||||
|
// dialogBody.append(container);
|
||||||
|
// dialog.dialog('option','buttons',[
|
||||||
|
// {
|
||||||
|
// // id: "clipboard-dialog-cancel",
|
||||||
|
// text: RED._("common.label.cancel"),
|
||||||
|
// click: function() {
|
||||||
|
// // $( this ).dialog( "close" );
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// id: "projects-dialog-create",
|
||||||
|
// text: "Create project", // TODO: nls
|
||||||
|
// class: "primary",
|
||||||
|
// // disabled: true,
|
||||||
|
// click: function() {
|
||||||
|
// var username = usernameInput.val();
|
||||||
|
// var password = passwordInput.val();
|
||||||
|
//
|
||||||
|
// req.remote = parts[1]+username+":"+password+"@"+parts[3];
|
||||||
|
// sendRequest({
|
||||||
|
// url: "projects",
|
||||||
|
// type: "POST",
|
||||||
|
// responses: {
|
||||||
|
// 200: function(data) {
|
||||||
|
// console.log("Success!",data);
|
||||||
|
// },
|
||||||
|
// 400: {
|
||||||
|
// 'project_exists': function(error) {
|
||||||
|
// console.log("already exists");
|
||||||
|
// },
|
||||||
|
// 'git_error': function(error) {
|
||||||
|
// console.log("git error",error);
|
||||||
|
// },
|
||||||
|
// 'git_auth_failed': function(error) {
|
||||||
|
// console.log("git auth error",error);
|
||||||
|
// },
|
||||||
|
// 'unexpected_error': function(error) {
|
||||||
|
// console.log("unexpected_error",error)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// },req)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// ])
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
var sidebarContent;
|
||||||
|
var sidebarSections;
|
||||||
|
var sidebarSectionsInfo;
|
||||||
|
var sidebarSectionsDesc;
|
||||||
|
var sidebarSectionsDeps;
|
||||||
|
var sidebarSectionsDepsList;
|
||||||
|
var sidebarSectionsSettings;
|
||||||
|
var modulesInUse = {};
|
||||||
|
|
||||||
|
function initSidebar() {
|
||||||
|
sidebarContent = $('<div>', {class:"sidebar-projects"});
|
||||||
|
var infoStackContainer = $("<div>",{class:"sidebar-projects-stack-info"}).appendTo(sidebarContent);
|
||||||
|
var stackContainer = $("<div>",{class:"sidebar-projects-stack"}).appendTo(sidebarContent);
|
||||||
|
|
||||||
|
RED.actions.add("core:show-projects-tab",showSidebar);
|
||||||
|
|
||||||
|
|
||||||
|
outerSections = RED.stack.create({
|
||||||
|
container: infoStackContainer,
|
||||||
|
fill: true
|
||||||
|
});
|
||||||
|
|
||||||
|
var a = outerSections.add({
|
||||||
|
title: "Project",
|
||||||
|
collapsible: false
|
||||||
|
})
|
||||||
|
|
||||||
|
sidebarSectionsInfo = $("<div>",{class:"node-help"}).appendTo(a.content);
|
||||||
|
|
||||||
|
sidebarSections = RED.stack.create({
|
||||||
|
container: stackContainer,
|
||||||
|
singleExpanded: true,
|
||||||
|
fill: true
|
||||||
|
});
|
||||||
|
|
||||||
|
sidebarSectionsDesc = sidebarSections.add({
|
||||||
|
title: RED._("sidebar.project.description"),
|
||||||
|
expanded: true
|
||||||
|
});
|
||||||
|
sidebarSectionsDesc.content.css({padding:"6px"});
|
||||||
|
|
||||||
|
var editDescription = $('<button style="position: absolute; top: 8px; right: 8px;" class="editor-button editor-button-small">edit</button>').appendTo(sidebarSectionsDesc.header);
|
||||||
|
var editDescriptionFunc = function() {
|
||||||
|
RED.editor.editMarkdown({
|
||||||
|
title: RED._('sidebar.project.editDescription'),
|
||||||
|
value: activeProject.description,
|
||||||
|
complete: function(v) {
|
||||||
|
var spinner = addSpinnerOverlay(sidebarSectionsDesc.content);
|
||||||
|
editDescription.addClass('disabled');
|
||||||
|
var done = function(err,res) {
|
||||||
|
if (err) {
|
||||||
|
editDescriptionFunc();
|
||||||
|
}
|
||||||
|
activeProject.description = v;
|
||||||
|
updateProjectDescription();
|
||||||
|
}
|
||||||
|
sendRequest({
|
||||||
|
url: "projects/"+activeProject.name,
|
||||||
|
type: "PUT",
|
||||||
|
responses: {
|
||||||
|
0: function(error) {
|
||||||
|
done(error,null);
|
||||||
|
},
|
||||||
|
200: function(data) {
|
||||||
|
done(null,data);
|
||||||
|
},
|
||||||
|
400: {
|
||||||
|
'unexpected_error': function(error) {
|
||||||
|
done(error,null);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},{description:v}).always(function() {
|
||||||
|
spinner.remove();
|
||||||
|
editDescription.removeClass('disabled');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
editDescription.click(function(evt) {
|
||||||
|
evt.preventDefault();
|
||||||
|
if ($(this).hasClass('disabled')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
editDescriptionFunc();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
sidebarSectionsDeps = sidebarSections.add({
|
||||||
|
title:RED._("sidebar.project.dependencies")
|
||||||
|
});
|
||||||
|
sidebarSectionsDeps.content.addClass("sidebar-projects-dependencies");
|
||||||
|
|
||||||
|
var editDependencies = $('<button style="position: absolute; top: 8px; right: 8px;" class="editor-button editor-button-small">edit</button>').appendTo(sidebarSectionsDeps.header);
|
||||||
|
var editDependenciesFunc = function(depsJSON) {
|
||||||
|
|
||||||
|
RED.editor.editJSON({
|
||||||
|
title: RED._('sidebar.project.editDependencies'),
|
||||||
|
value: JSON.stringify(depsJSON||activeProject.dependencies||{},"",4),
|
||||||
|
complete: function(v) {
|
||||||
|
try {
|
||||||
|
var parsed = JSON.parse(v);
|
||||||
|
var spinner = addSpinnerOverlay(sidebarSectionsDeps.content);
|
||||||
|
|
||||||
|
editDependencies.addClass('disabled');
|
||||||
|
var done = function(err,res) {
|
||||||
|
if (err) {
|
||||||
|
editDependenciesFunc(depsJSON);
|
||||||
|
}
|
||||||
|
activeProject.dependencies = parsed;
|
||||||
|
updateProjectDependencies();
|
||||||
|
}
|
||||||
|
sendRequest({
|
||||||
|
url: "projects/"+activeProject.name,
|
||||||
|
type: "PUT",
|
||||||
|
responses: {
|
||||||
|
0: function(error) {
|
||||||
|
done(error,null);
|
||||||
|
},
|
||||||
|
200: function(data) {
|
||||||
|
done(null,data);
|
||||||
|
},
|
||||||
|
400: {
|
||||||
|
'unexpected_error': function(error) {
|
||||||
|
done(error,null);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},{dependencies:parsed}).always(function() {
|
||||||
|
spinner.remove();
|
||||||
|
editDependencies.removeClass('disabled');
|
||||||
|
});
|
||||||
|
} catch(err) {
|
||||||
|
editDependenciesFunc(depsJSON);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
editDependencies.click(function(evt) {
|
||||||
|
evt.preventDefault();
|
||||||
|
editDependenciesFunc();
|
||||||
|
});
|
||||||
|
|
||||||
|
sidebarSectionsDepsList = $("<ol>",{style:"position: absolute;top: 0;bottom: 0;left: 0;right: 0;"}).appendTo(sidebarSectionsDeps.content);
|
||||||
|
sidebarSectionsDepsList.editableList({
|
||||||
|
addButton: false,
|
||||||
|
addItem: function(row,index,entry) {
|
||||||
|
// console.log(entry);
|
||||||
|
var headerRow = $('<div>',{class:"palette-module-header"}).appendTo(row);
|
||||||
|
if (entry.label) {
|
||||||
|
row.parent().addClass("palette-module-section");
|
||||||
|
headerRow.text(entry.label);
|
||||||
|
if (entry.index === 1) {
|
||||||
|
var addButton = $('<button class="editor-button editor-button-small palette-module-button">add to project</button>').appendTo(headerRow).click(function(evt) {
|
||||||
|
evt.preventDefault();
|
||||||
|
var deps = $.extend(true, {}, activeProject.dependencies);
|
||||||
|
for (var m in modulesInUse) {
|
||||||
|
if (modulesInUse.hasOwnProperty(m) && !modulesInUse[m].known) {
|
||||||
|
deps[m] = modulesInUse[m].version;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
editDependenciesFunc(deps);
|
||||||
|
});
|
||||||
|
} else if (entry.index === 3) {
|
||||||
|
var removeButton = $('<button class="editor-button editor-button-small palette-module-button">remove from project</button>').appendTo(headerRow).click(function(evt) {
|
||||||
|
evt.preventDefault();
|
||||||
|
var deps = $.extend(true, {}, activeProject.dependencies);
|
||||||
|
for (var m in modulesInUse) {
|
||||||
|
if (modulesInUse.hasOwnProperty(m) && modulesInUse[m].known && modulesInUse[m].count === 0) {
|
||||||
|
delete deps[m];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
editDependenciesFunc(deps);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
headerRow.toggleClass("palette-module-unused",entry.count === 0);
|
||||||
|
entry.element = headerRow;
|
||||||
|
var titleRow = $('<div class="palette-module-meta palette-module-name"></div>').appendTo(headerRow);
|
||||||
|
var icon = $('<i class="fa fa-'+(entry.known?'cube':'warning')+'"></i>').appendTo(titleRow);
|
||||||
|
entry.icon = icon;
|
||||||
|
$('<span>').html(entry.module).appendTo(titleRow);
|
||||||
|
var metaRow = $('<div class="palette-module-meta palette-module-version"><i class="fa fa-tag"></i></div>').appendTo(headerRow);
|
||||||
|
var versionSpan = $('<span>').html(entry.version).appendTo(metaRow);
|
||||||
|
if (!entry.known) {
|
||||||
|
headerRow.addClass("palette-module-unknown");
|
||||||
|
} else if (entry.known && entry.count === 0) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
sort: function(A,B) {
|
||||||
|
if (A.index && B.index) {
|
||||||
|
return A.index - B.index;
|
||||||
|
}
|
||||||
|
var Acategory = A.index?A.index:(A.known?(A.count>0?0:4):2);
|
||||||
|
var Bcategory = B.index?B.index:(B.known?(B.count>0?0:4):2);
|
||||||
|
if (Acategory === Bcategory) {
|
||||||
|
return A.module.localeCompare(B.module);
|
||||||
|
} else {
|
||||||
|
return Acategory - Bcategory;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
sidebarSectionsDeps.container.css("border-bottom","none");
|
||||||
|
|
||||||
|
// sidebarSectionsSettings = sidebarSections.add({
|
||||||
|
// title: RED._("sidebar.project.settings")
|
||||||
|
// });
|
||||||
|
// sidebarSectionsSettings.container.css("border-bottom","none");
|
||||||
|
|
||||||
|
RED.sidebar.addTab({
|
||||||
|
id: "project",
|
||||||
|
label: RED._("sidebar.project.label"),
|
||||||
|
name: RED._("sidebar.project.name"),
|
||||||
|
content: sidebarContent,
|
||||||
|
onchange: function() {
|
||||||
|
setTimeout(function() {
|
||||||
|
sidebarSections.resize();
|
||||||
|
},10);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
RED.events.on('nodes:add', function(n) {
|
||||||
|
if (!/^subflow:/.test(n.type)) {
|
||||||
|
var module = RED.nodes.registry.getNodeSetForType(n.type).module;
|
||||||
|
if (module !== 'node-red') {
|
||||||
|
if (!modulesInUse.hasOwnProperty(module)) {
|
||||||
|
modulesInUse[module] = {
|
||||||
|
module: module,
|
||||||
|
version: RED.nodes.registry.getModule(module).version,
|
||||||
|
count: 0,
|
||||||
|
known: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
modulesInUse[module].count++;
|
||||||
|
if (modulesInUse[module].count === 1 && !modulesInUse[module].known) {
|
||||||
|
sidebarSectionsDepsList.editableList('addItem',modulesInUse[module]);
|
||||||
|
} else {
|
||||||
|
sidebarSectionsDepsList.editableList('sort');
|
||||||
|
if (modulesInUse[module].element) {
|
||||||
|
modulesInUse[module].element.removeClass("palette-module-unused");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
RED.events.on('nodes:remove', function(n) {
|
||||||
|
if (!/^subflow:/.test(n.type)) {
|
||||||
|
var module = RED.nodes.registry.getNodeSetForType(n.type).module;
|
||||||
|
if (module !== 'node-red' && modulesInUse.hasOwnProperty(module)) {
|
||||||
|
modulesInUse[module].count--;
|
||||||
|
if (modulesInUse[module].count === 0) {
|
||||||
|
if (!modulesInUse[module].known) {
|
||||||
|
sidebarSectionsDepsList.editableList('removeItem',modulesInUse[module]);
|
||||||
|
delete modulesInUse[module];
|
||||||
|
} else {
|
||||||
|
// TODO: a known dependency is now unused by the flow
|
||||||
|
sidebarSectionsDepsList.editableList('sort');
|
||||||
|
modulesInUse[module].element.addClass("palette-module-unused");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
function showSidebar() {
|
||||||
|
RED.sidebar.show("project");
|
||||||
|
}
|
||||||
|
function addSpinnerOverlay(container) {
|
||||||
|
var spinner = $('<div class="projects-dialog-spinner projects-dialog-spinner-sidebar"><img src="red/images/spin.svg"/></div>').appendTo(container);
|
||||||
|
return spinner;
|
||||||
|
|
||||||
|
}
|
||||||
|
function updateProjectSummary() {
|
||||||
|
sidebarSectionsInfo.empty();
|
||||||
|
if (activeProject) {
|
||||||
|
var table = $('<table class="node-info"></table>').appendTo(sidebarSectionsInfo);
|
||||||
|
var tableBody = $('<tbody>').appendTo(table);
|
||||||
|
var propRow;
|
||||||
|
propRow = $('<tr class="node-info-node-row"><td>Project</td><td></td></tr>').appendTo(tableBody);
|
||||||
|
$(propRow.children()[1]).html(' '+(activeProject.name||""))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function updateProjectDescription() {
|
||||||
|
sidebarSectionsDesc.content.empty();
|
||||||
|
if (activeProject) {
|
||||||
|
var div = $('<div class="node-help"></div>').appendTo(sidebarSectionsDesc.content);
|
||||||
|
var desc = marked(activeProject.description||"");
|
||||||
|
var description = addTargetToExternalLinks($('<div class="node-help"><span class="bidiAware" dir=\"'+RED.text.bidi.resolveBaseTextDir(desc)+'">'+desc+'</span></div>')).appendTo(div);
|
||||||
|
description.find(".bidiAware").contents().filter(function() { return this.nodeType === 3 && this.textContent.trim() !== "" }).wrap( "<span></span>" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function updateProjectDependencies() {
|
||||||
|
if (activeProject) {
|
||||||
|
sidebarSectionsDepsList.editableList('empty');
|
||||||
|
sidebarSectionsDepsList.editableList('addItem',{index:1, label:"Unknown Dependencies"});
|
||||||
|
sidebarSectionsDepsList.editableList('addItem',{index:3, label:"Unused dependencies"});
|
||||||
|
var dependencies = activeProject.dependencies||{};
|
||||||
|
var moduleList = Object.keys(dependencies);
|
||||||
|
if (moduleList.length > 0) {
|
||||||
|
moduleList.sort();
|
||||||
|
moduleList.forEach(function(module) {
|
||||||
|
if (modulesInUse.hasOwnProperty(module)) {
|
||||||
|
// TODO: this module is used by not currently 'known'
|
||||||
|
modulesInUse[module].known = true;
|
||||||
|
} else {
|
||||||
|
modulesInUse[module] = {module:module,version:dependencies[module], known: true, count:0};
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
for (var module in modulesInUse) {
|
||||||
|
if (modulesInUse.hasOwnProperty(module)) {
|
||||||
|
var m = modulesInUse[module];
|
||||||
|
if (!dependencies.hasOwnProperty(module) && m.count === 0) {
|
||||||
|
delete modulesInUse[module];
|
||||||
|
} else {
|
||||||
|
sidebarSectionsDepsList.editableList('addItem',modulesInUse[module]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function refreshSidebar() {
|
||||||
|
$.getJSON("projects",function(data) {
|
||||||
|
if (data.active) {
|
||||||
|
$.getJSON("projects/"+data.active, function(project) {
|
||||||
|
activeProject = project;
|
||||||
|
updateProjectSummary();
|
||||||
|
updateProjectDescription();
|
||||||
|
updateProjectDependencies();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// function getUsedModules() {
|
||||||
|
// var inuseModules = {};
|
||||||
|
// var inuseTypes = {};
|
||||||
|
// var getNodeModule = function(node) {
|
||||||
|
// if (inuseTypes[node.type]) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// inuseTypes[node.type] = true;
|
||||||
|
// inuseModules[RED.nodes.registry.getNodeSetForType(node.type).module] = true;
|
||||||
|
// }
|
||||||
|
// RED.nodes.eachNode(getNodeModule);
|
||||||
|
// RED.nodes.eachConfig(getNodeModule);
|
||||||
|
// console.log(Object.keys(inuseModules));
|
||||||
|
// }
|
||||||
|
|
||||||
|
// TODO: DRY - tab-info.js
|
||||||
|
function addTargetToExternalLinks(el) {
|
||||||
|
$(el).find("a").each(function(el) {
|
||||||
|
var href = $(this).attr('href');
|
||||||
|
if (/^https?:/.test(href)) {
|
||||||
|
$(this).attr('target','_blank');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
init: init,
|
||||||
|
showStartup: function() {
|
||||||
|
show('welcome');
|
||||||
|
},
|
||||||
|
newProject: function() {
|
||||||
|
show('create')
|
||||||
|
},
|
||||||
|
selectProject: function() {
|
||||||
|
show('open')
|
||||||
|
},
|
||||||
|
showCredentialsPrompt: function() {
|
||||||
|
show('credentialSecret');
|
||||||
|
},
|
||||||
|
showSidebar: showSidebar,
|
||||||
|
refreshSidebar: refreshSidebar,
|
||||||
|
showProjectEditor: function() {
|
||||||
|
RED.editor.editProject({
|
||||||
|
project: activeProject,
|
||||||
|
complete: function(result) {
|
||||||
|
console.log(result);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getActiveProject: function() {
|
||||||
|
return activeProject;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})();
|
@ -35,7 +35,8 @@ RED.sidebar = (function() {
|
|||||||
tab.onremove.call(tab);
|
tab.onremove.call(tab);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
minimumActiveTabWidth: 110
|
minimumActiveTabWidth: 70
|
||||||
|
// scrollable: true
|
||||||
});
|
});
|
||||||
|
|
||||||
var knownTabs = {
|
var knownTabs = {
|
||||||
|
@ -50,13 +50,15 @@ RED.sidebar.info = (function() {
|
|||||||
}).hide();
|
}).hide();
|
||||||
|
|
||||||
nodeSection = sections.add({
|
nodeSection = sections.add({
|
||||||
title: RED._("sidebar.info.node"),
|
title: RED._("sidebar.info.info"),
|
||||||
collapsible: false
|
collapsible: true
|
||||||
});
|
});
|
||||||
|
nodeSection.expand();
|
||||||
infoSection = sections.add({
|
infoSection = sections.add({
|
||||||
title: RED._("sidebar.info.information"),
|
title: RED._("sidebar.info.help"),
|
||||||
collapsible: false
|
collapsible: true
|
||||||
});
|
});
|
||||||
|
infoSection.expand();
|
||||||
infoSection.content.css("padding","6px");
|
infoSection.content.css("padding","6px");
|
||||||
infoSection.container.css("border-bottom","none");
|
infoSection.container.css("border-bottom","none");
|
||||||
|
|
||||||
@ -96,23 +98,7 @@ RED.sidebar.info = (function() {
|
|||||||
RED.sidebar.show("info");
|
RED.sidebar.show("info");
|
||||||
}
|
}
|
||||||
|
|
||||||
function jsonFilter(key,value) {
|
// TODO: DRY - projects.js
|
||||||
if (key === "") {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
var t = typeof value;
|
|
||||||
if ($.isArray(value)) {
|
|
||||||
return "[array:"+value.length+"]";
|
|
||||||
} else if (t === "object") {
|
|
||||||
return "[object]"
|
|
||||||
} else if (t === "string") {
|
|
||||||
if (value.length > 30) {
|
|
||||||
return value.substring(0,30)+" ...";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
function addTargetToExternalLinks(el) {
|
function addTargetToExternalLinks(el) {
|
||||||
$(el).find("a").each(function(el) {
|
$(el).find("a").each(function(el) {
|
||||||
var href = $(this).attr('href');
|
var href = $(this).attr('href');
|
||||||
@ -127,72 +113,29 @@ RED.sidebar.info = (function() {
|
|||||||
$(nodeSection.content).empty();
|
$(nodeSection.content).empty();
|
||||||
$(infoSection.content).empty();
|
$(infoSection.content).empty();
|
||||||
|
|
||||||
var table = $('<table class="node-info"></table>');
|
|
||||||
var tableBody = $('<tbody>').appendTo(table);
|
|
||||||
var propRow;
|
var propRow;
|
||||||
|
|
||||||
|
var table = $('<table class="node-info"></table>').appendTo(nodeSection.content);
|
||||||
|
var tableBody = $('<tbody>').appendTo(table);
|
||||||
var subflowNode;
|
var subflowNode;
|
||||||
if (node.type === "tab") {
|
var subflowUserCount;
|
||||||
nodeSection.title.html(RED._("sidebar.info.flow"));
|
|
||||||
propRow = $('<tr class="node-info-node-row"><td>'+RED._("sidebar.info.tabName")+'</td><td></td></tr>').appendTo(tableBody);
|
var activeProject = RED.projects.getActiveProject();
|
||||||
$(propRow.children()[1]).html(' '+(node.label||""))
|
if (activeProject) {
|
||||||
propRow = $('<tr class="node-info-node-row"><td>'+RED._("sidebar.info.id")+"</td><td></td></tr>").appendTo(tableBody);
|
propRow = $('<tr class="node-info-node-row"><td>Project</td><td></td></tr>').appendTo(tableBody);
|
||||||
RED.utils.createObjectElement(node.id).appendTo(propRow.children()[1]);
|
$(propRow.children()[1]).html(' '+(activeProject.name||""))
|
||||||
propRow = $('<tr class="node-info-node-row"><td>'+RED._("sidebar.info.status")+'</td><td></td></tr>').appendTo(tableBody);
|
$('<tr class="node-info-property-expand blank"><td colspan="2"></td></tr>').appendTo(tableBody);
|
||||||
$(propRow.children()[1]).html((!!!node.disabled)?RED._("sidebar.info.enabled"):RED._("sidebar.info.disabled"))
|
}
|
||||||
|
infoSection.container.show();
|
||||||
|
if (node === undefined) {
|
||||||
|
return;
|
||||||
|
} else if (Array.isArray(node)) {
|
||||||
|
infoSection.container.hide();
|
||||||
|
propRow = $('<tr class="node-info-node-row"><td>'+RED._("sidebar.info.selection")+"</td><td></td></tr>").appendTo(tableBody);
|
||||||
|
$(propRow.children()[1]).html(' '+RED._("sidebar.info.nodes",{count:node.length}))
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
nodeSection.title.html(RED._("sidebar.info.node"));
|
|
||||||
if (node.type !== "subflow" && node.name) {
|
|
||||||
$('<tr class="node-info-node-row"><td>'+RED._("common.label.name")+'</td><td> <span class="bidiAware" dir="'+RED.text.bidi.resolveBaseTextDir(node.name)+'">'+node.name+'</span></td></tr>').appendTo(tableBody);
|
|
||||||
}
|
|
||||||
$('<tr class="node-info-node-row"><td>'+RED._("sidebar.info.type")+"</td><td> "+node.type+"</td></tr>").appendTo(tableBody);
|
|
||||||
propRow = $('<tr class="node-info-node-row"><td>'+RED._("sidebar.info.id")+"</td><td></td></tr>").appendTo(tableBody);
|
|
||||||
RED.utils.createObjectElement(node.id).appendTo(propRow.children()[1]);
|
|
||||||
|
|
||||||
var m = /^subflow(:(.+))?$/.exec(node.type);
|
var m = /^subflow(:(.+))?$/.exec(node.type);
|
||||||
|
|
||||||
if (!m && node.type != "subflow" && node.type != "comment") {
|
|
||||||
if (node._def) {
|
|
||||||
var count = 0;
|
|
||||||
var defaults = node._def.defaults;
|
|
||||||
for (var n in defaults) {
|
|
||||||
if (n != "name" && defaults.hasOwnProperty(n)) {
|
|
||||||
var val = node[n];
|
|
||||||
var type = typeof val;
|
|
||||||
count++;
|
|
||||||
propRow = $('<tr class="node-info-property-row'+(expandedSections.property?"":" hide")+'"><td>'+n+"</td><td></td></tr>").appendTo(tableBody);
|
|
||||||
if (defaults[n].type) {
|
|
||||||
var configNode = RED.nodes.node(val);
|
|
||||||
if (!configNode) {
|
|
||||||
RED.utils.createObjectElement(undefined).appendTo(propRow.children()[1]);
|
|
||||||
} else {
|
|
||||||
var configLabel = RED.utils.getNodeLabel(configNode,val);
|
|
||||||
var container = propRow.children()[1];
|
|
||||||
|
|
||||||
var div = $('<span>',{class:""}).appendTo(container);
|
|
||||||
var nodeDiv = $('<div>',{class:"palette_node palette_node_small"}).appendTo(div);
|
|
||||||
var colour = configNode._def.color;
|
|
||||||
var icon_url = RED.utils.getNodeIcon(configNode._def);
|
|
||||||
nodeDiv.css({'backgroundColor':colour, "cursor":"pointer"});
|
|
||||||
var iconContainer = $('<div/>',{class:"palette_icon_container"}).appendTo(nodeDiv);
|
|
||||||
$('<div/>',{class:"palette_icon",style:"background-image: url("+icon_url+")"}).appendTo(iconContainer);
|
|
||||||
var nodeContainer = $('<span></span>').css({"verticalAlign":"top","marginLeft":"6px"}).html(configLabel).appendTo(container);
|
|
||||||
|
|
||||||
nodeDiv.on('dblclick',function() {
|
|
||||||
RED.editor.editConfig("", configNode.type, configNode.id);
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
RED.utils.createObjectElement(val).appendTo(propRow.children()[1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (count > 0) {
|
|
||||||
$('<tr class="node-info-property-expand blank"><td colspan="2"><a href="#" class=" node-info-property-header'+(expandedSections.property?" expanded":"")+'"><span class="node-info-property-show-more">'+RED._("sidebar.info.showMore")+'</span><span class="node-info-property-show-less">'+RED._("sidebar.info.showLess")+'</span> <i class="fa fa-caret-down"></i></a></td></tr>').appendTo(tableBody);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m) {
|
if (m) {
|
||||||
if (m[2]) {
|
if (m[2]) {
|
||||||
subflowNode = RED.nodes.subflow(m[2]);
|
subflowNode = RED.nodes.subflow(m[2]);
|
||||||
@ -200,49 +143,120 @@ RED.sidebar.info = (function() {
|
|||||||
subflowNode = node;
|
subflowNode = node;
|
||||||
}
|
}
|
||||||
|
|
||||||
$('<tr class="blank"><th colspan="2">'+RED._("sidebar.info.subflow")+'</th></tr>').appendTo(tableBody);
|
subflowUserCount = 0;
|
||||||
|
|
||||||
var userCount = 0;
|
|
||||||
var subflowType = "subflow:"+subflowNode.id;
|
var subflowType = "subflow:"+subflowNode.id;
|
||||||
RED.nodes.eachNode(function(n) {
|
RED.nodes.eachNode(function(n) {
|
||||||
if (n.type === subflowType) {
|
if (n.type === subflowType) {
|
||||||
userCount++;
|
subflowUserCount++;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
$('<tr class="node-info-subflow-row"><td>'+RED._("common.label.name")+'</td><td><span class="bidiAware" dir=\"'+RED.text.bidi.resolveBaseTextDir(subflowNode.name)+'">'+subflowNode.name+'</span></td></tr>').appendTo(tableBody);
|
|
||||||
$('<tr class="node-info-subflow-row"><td>'+RED._("sidebar.info.instances")+"</td><td>"+userCount+'</td></tr>').appendTo(tableBody);
|
|
||||||
}
|
}
|
||||||
|
if (node.type === "tab" || node.type === "subflow") {
|
||||||
|
propRow = $('<tr class="node-info-node-row"><td>'+RED._("sidebar.info."+(node.type==='tab'?'flow':'subflow'))+'</td><td></td></tr>').appendTo(tableBody);
|
||||||
|
RED.utils.createObjectElement(node.id).appendTo(propRow.children()[1]);
|
||||||
|
propRow = $('<tr class="node-info-node-row"><td>'+RED._("sidebar.info.tabName")+"</td><td></td></tr>").appendTo(tableBody);
|
||||||
|
$(propRow.children()[1]).html(' '+(node.label||node.name||""))
|
||||||
|
if (node.type === "tab") {
|
||||||
|
propRow = $('<tr class="node-info-node-row"><td>'+RED._("sidebar.info.status")+'</td><td></td></tr>').appendTo(tableBody);
|
||||||
|
$(propRow.children()[1]).html((!!!node.disabled)?RED._("sidebar.info.enabled"):RED._("sidebar.info.disabled"))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
propRow = $('<tr class="node-info-node-row"><td>'+RED._("sidebar.info.node")+"</td><td></td></tr>").appendTo(tableBody);
|
||||||
|
RED.utils.createObjectElement(node.id).appendTo(propRow.children()[1]);
|
||||||
|
|
||||||
|
|
||||||
|
if (node.type !== "subflow" && node.name) {
|
||||||
|
$('<tr class="node-info-node-row"><td>'+RED._("common.label.name")+'</td><td> <span class="bidiAware" dir="'+RED.text.bidi.resolveBaseTextDir(node.name)+'">'+node.name+'</span></td></tr>').appendTo(tableBody);
|
||||||
|
}
|
||||||
|
if (!m) {
|
||||||
|
$('<tr class="node-info-node-row"><td>'+RED._("sidebar.info.type")+"</td><td> "+node.type+"</td></tr>").appendTo(tableBody);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m && node.type != "subflow" && node.type != "comment") {
|
||||||
|
if (node._def) {
|
||||||
|
var count = 0;
|
||||||
|
var defaults = node._def.defaults;
|
||||||
|
for (var n in defaults) {
|
||||||
|
if (n != "name" && defaults.hasOwnProperty(n)) {
|
||||||
|
var val = node[n];
|
||||||
|
var type = typeof val;
|
||||||
|
count++;
|
||||||
|
propRow = $('<tr class="node-info-property-row'+(expandedSections.property?"":" hide")+'"><td>'+n+"</td><td></td></tr>").appendTo(tableBody);
|
||||||
|
if (defaults[n].type) {
|
||||||
|
var configNode = RED.nodes.node(val);
|
||||||
|
if (!configNode) {
|
||||||
|
RED.utils.createObjectElement(undefined).appendTo(propRow.children()[1]);
|
||||||
|
} else {
|
||||||
|
var configLabel = RED.utils.getNodeLabel(configNode,val);
|
||||||
|
var container = propRow.children()[1];
|
||||||
|
|
||||||
|
var div = $('<span>',{class:""}).appendTo(container);
|
||||||
|
var nodeDiv = $('<div>',{class:"palette_node palette_node_small"}).appendTo(div);
|
||||||
|
var colour = configNode._def.color;
|
||||||
|
var icon_url = RED.utils.getNodeIcon(configNode._def);
|
||||||
|
nodeDiv.css({'backgroundColor':colour, "cursor":"pointer"});
|
||||||
|
var iconContainer = $('<div/>',{class:"palette_icon_container"}).appendTo(nodeDiv);
|
||||||
|
$('<div/>',{class:"palette_icon",style:"background-image: url("+icon_url+")"}).appendTo(iconContainer);
|
||||||
|
var nodeContainer = $('<span></span>').css({"verticalAlign":"top","marginLeft":"6px"}).html(configLabel).appendTo(container);
|
||||||
|
|
||||||
|
nodeDiv.on('dblclick',function() {
|
||||||
|
RED.editor.editConfig("", configNode.type, configNode.id);
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
RED.utils.createObjectElement(val).appendTo(propRow.children()[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (count > 0) {
|
||||||
|
$('<tr class="node-info-property-expand blank"><td colspan="2"><a href="#" class=" node-info-property-header'+(expandedSections.property?" expanded":"")+'"><span class="node-info-property-show-more">'+RED._("sidebar.info.showMore")+'</span><span class="node-info-property-show-less">'+RED._("sidebar.info.showLess")+'</span> <i class="fa fa-caret-down"></i></a></td></tr>').appendTo(tableBody);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (node.type !== 'tab') {
|
||||||
|
if (m) {
|
||||||
|
$('<tr class="blank"><th colspan="2">'+RED._("sidebar.info.subflow")+'</th></tr>').appendTo(tableBody);
|
||||||
|
$('<tr class="node-info-subflow-row"><td>'+RED._("common.label.name")+'</td><td><span class="bidiAware" dir=\"'+RED.text.bidi.resolveBaseTextDir(subflowNode.name)+'">'+subflowNode.name+'</span></td></tr>').appendTo(tableBody);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (m) {
|
||||||
|
$('<tr class="node-info-subflow-row"><td>'+RED._("sidebar.info.instances")+"</td><td>"+subflowUserCount+'</td></tr>').appendTo(tableBody);
|
||||||
|
}
|
||||||
|
|
||||||
|
var infoText = "";
|
||||||
|
if (!subflowNode && node.type !== "comment" && node.type !== "tab") {
|
||||||
|
infoSection.title.html(RED._("sidebar.info.nodeHelp"));
|
||||||
|
var helpText = $("script[data-help-name='"+node.type+"']").html()||('<span class="node-info-none">'+RED._("sidebar.info.none")+'</span>');
|
||||||
|
infoText = helpText;
|
||||||
|
} else if (node.type === "tab") {
|
||||||
|
infoSection.title.html(RED._("sidebar.info.flowDesc"));
|
||||||
|
infoText = marked(node.info||"")||('<span class="node-info-none">'+RED._("sidebar.info.none")+'</span>');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subflowNode) {
|
||||||
|
infoText = infoText + (marked(subflowNode.info||"")||('<span class="node-info-none">'+RED._("sidebar.info.none")+'</span>'));
|
||||||
|
infoSection.title.html(RED._("sidebar.info.subflowDesc"));
|
||||||
|
} else if (node._def && node._def.info) {
|
||||||
|
infoSection.title.html(RED._("sidebar.info.nodeHelp"));
|
||||||
|
var info = node._def.info;
|
||||||
|
var textInfo = (typeof info === "function" ? info.call(node) : info);
|
||||||
|
// TODO: help
|
||||||
|
infoText = infoText + marked(textInfo);
|
||||||
|
}
|
||||||
|
if (infoText) {
|
||||||
|
setInfoText(infoText);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$(".node-info-property-header").click(function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
expandedSections["property"] = !expandedSections["property"];
|
||||||
|
$(this).toggleClass("expanded",expandedSections["property"]);
|
||||||
|
$(".node-info-property-row").toggle(expandedSections["property"]);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
$(table).appendTo(nodeSection.content);
|
|
||||||
|
|
||||||
var infoText = "";
|
|
||||||
|
|
||||||
if (!subflowNode && node.type !== "comment" && node.type !== "tab") {
|
|
||||||
var helpText = $("script[data-help-name='"+node.type+"']").html()||"";
|
|
||||||
infoText = helpText;
|
|
||||||
} else if (node.type === "tab") {
|
|
||||||
infoText = marked(node.info||"");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (subflowNode) {
|
|
||||||
infoText = infoText + marked(subflowNode.info||"");
|
|
||||||
} else if (node._def && node._def.info) {
|
|
||||||
var info = node._def.info;
|
|
||||||
var textInfo = (typeof info === "function" ? info.call(node) : info);
|
|
||||||
// TODO: help
|
|
||||||
infoText = infoText + marked(textInfo);
|
|
||||||
}
|
|
||||||
if (infoText) {
|
|
||||||
setInfoText(infoText);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
$(".node-info-property-header").click(function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
expandedSections["property"] = !expandedSections["property"];
|
|
||||||
$(this).toggleClass("expanded",expandedSections["property"]);
|
|
||||||
$(".node-info-property-row").toggle(expandedSections["property"]);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
function setInfoText(infoText) {
|
function setInfoText(infoText) {
|
||||||
var info = addTargetToExternalLinks($('<div class="node-help"><span class="bidiAware" dir=\"'+RED.text.bidi.resolveBaseTextDir(infoText)+'">'+infoText+'</span></div>')).appendTo(infoSection.content);
|
var info = addTargetToExternalLinks($('<div class="node-help"><span class="bidiAware" dir=\"'+RED.text.bidi.resolveBaseTextDir(infoText)+'">'+infoText+'</span></div>')).appendTo(infoSection.content);
|
||||||
@ -342,14 +356,16 @@ RED.sidebar.info = (function() {
|
|||||||
})();
|
})();
|
||||||
|
|
||||||
function clear() {
|
function clear() {
|
||||||
sections.hide();
|
// sections.hide();
|
||||||
//
|
refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
function set(html) {
|
function set(html,title) {
|
||||||
// tips.stop();
|
// tips.stop();
|
||||||
sections.show();
|
// sections.show();
|
||||||
nodeSection.container.hide();
|
// nodeSection.container.hide();
|
||||||
|
infoSection.title.text(title||"");
|
||||||
|
refresh();
|
||||||
$(infoSection.content).empty();
|
$(infoSection.content).empty();
|
||||||
setInfoText(html);
|
setInfoText(html);
|
||||||
$(".sidebar-node-info-stack").scrollTop(0);
|
$(".sidebar-node-info-stack").scrollTop(0);
|
||||||
@ -366,6 +382,8 @@ RED.sidebar.info = (function() {
|
|||||||
} else {
|
} else {
|
||||||
refresh(node);
|
refresh(node);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
refresh(selection.nodes);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var activeWS = RED.workspaces.active();
|
var activeWS = RED.workspaces.active();
|
||||||
@ -378,7 +396,8 @@ RED.sidebar.info = (function() {
|
|||||||
if (workspace && workspace.info) {
|
if (workspace && workspace.info) {
|
||||||
refresh(workspace);
|
refresh(workspace);
|
||||||
} else {
|
} else {
|
||||||
clear();
|
refresh()
|
||||||
|
// clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -396,10 +396,10 @@ RED.view = (function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
$("#chart").focus(function() {
|
$("#chart").focus(function() {
|
||||||
$("#workspace-tabs").addClass("workspace-focussed")
|
$("#workspace-tabs").addClass("workspace-focussed");
|
||||||
});
|
});
|
||||||
$("#chart").blur(function() {
|
$("#chart").blur(function() {
|
||||||
$("#workspace-tabs").removeClass("workspace-focussed")
|
$("#workspace-tabs").removeClass("workspace-focussed");
|
||||||
});
|
});
|
||||||
|
|
||||||
RED.actions.add("core:copy-selection-to-internal-clipboard",copySelection);
|
RED.actions.add("core:copy-selection-to-internal-clipboard",copySelection);
|
||||||
|
@ -244,10 +244,16 @@ RED.workspaces = (function() {
|
|||||||
if (tab.disabled) {
|
if (tab.disabled) {
|
||||||
$("#red-ui-tab-"+(tab.id.replace(".","-"))).addClass('workspace-disabled');
|
$("#red-ui-tab-"+(tab.id.replace(".","-"))).addClass('workspace-disabled');
|
||||||
}
|
}
|
||||||
RED.menu.setDisabled("menu-item-workspace-delete",workspace_tabs.count() == 1);
|
RED.menu.setDisabled("menu-item-workspace-delete",workspace_tabs.count() <= 1);
|
||||||
|
if (workspace_tabs.count() === 1) {
|
||||||
|
showWorkspace();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onremove: function(tab) {
|
onremove: function(tab) {
|
||||||
RED.menu.setDisabled("menu-item-workspace-delete",workspace_tabs.count() == 1);
|
RED.menu.setDisabled("menu-item-workspace-delete",workspace_tabs.count() <= 1);
|
||||||
|
if (workspace_tabs.count() === 0) {
|
||||||
|
hideWorkspace();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onreorder: function(oldOrder, newOrder) {
|
onreorder: function(oldOrder, newOrder) {
|
||||||
RED.history.push({t:'reorder',order:oldOrder,dirty:RED.nodes.dirty()});
|
RED.history.push({t:'reorder',order:oldOrder,dirty:RED.nodes.dirty()});
|
||||||
@ -261,6 +267,16 @@ RED.workspaces = (function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
function showWorkspace() {
|
||||||
|
$("#workspace .red-ui-tabs").show()
|
||||||
|
$("#chart").show()
|
||||||
|
$("#workspace-footer").children().show()
|
||||||
|
}
|
||||||
|
function hideWorkspace() {
|
||||||
|
$("#workspace .red-ui-tabs").hide()
|
||||||
|
$("#chart").hide()
|
||||||
|
$("#workspace-footer").children().hide()
|
||||||
|
}
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
createWorkspaceTabs();
|
createWorkspaceTabs();
|
||||||
@ -280,6 +296,8 @@ RED.workspaces = (function() {
|
|||||||
RED.actions.add("core:add-flow",addWorkspace);
|
RED.actions.add("core:add-flow",addWorkspace);
|
||||||
RED.actions.add("core:edit-flow",editWorkspace);
|
RED.actions.add("core:edit-flow",editWorkspace);
|
||||||
RED.actions.add("core:remove-flow",removeWorkspace);
|
RED.actions.add("core:remove-flow",removeWorkspace);
|
||||||
|
|
||||||
|
hideWorkspace();
|
||||||
}
|
}
|
||||||
|
|
||||||
function editWorkspace(id) {
|
function editWorkspace(id) {
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
**/
|
**/
|
||||||
|
|
||||||
#notifications {
|
#notifications {
|
||||||
z-index: 10000;
|
z-index: 100;
|
||||||
width: 500px;
|
width: 500px;
|
||||||
margin-left: -250px;
|
margin-left: -250px;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
|
@ -95,29 +95,6 @@
|
|||||||
.palette-module-shade-status {
|
.palette-module-shade-status {
|
||||||
color: #666;
|
color: #666;
|
||||||
}
|
}
|
||||||
|
|
||||||
.palette-module-meta {
|
|
||||||
color: #666;
|
|
||||||
position: relative;
|
|
||||||
&.disabled {
|
|
||||||
color: #ccc;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fa {
|
|
||||||
width: 15px;
|
|
||||||
text-align: center;
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.palette-module-name {
|
|
||||||
white-space: nowrap;
|
|
||||||
@include enable-selection;
|
|
||||||
}
|
|
||||||
.palette-module-version, .palette-module-updated, .palette-module-link {
|
|
||||||
font-style:italic;
|
|
||||||
font-size: 0.8em;
|
|
||||||
@include enable-selection;
|
|
||||||
}
|
|
||||||
.palette-module-updated {
|
.palette-module-updated {
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
@ -224,3 +201,32 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
.palette-module-meta {
|
||||||
|
color: #666;
|
||||||
|
position: relative;
|
||||||
|
&.disabled {
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fa {
|
||||||
|
width: 15px;
|
||||||
|
text-align: center;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.palette-module-name {
|
||||||
|
white-space: nowrap;
|
||||||
|
@include enable-selection;
|
||||||
|
}
|
||||||
|
.palette-module-version, .palette-module-updated, .palette-module-link {
|
||||||
|
font-style:italic;
|
||||||
|
font-size: 0.8em;
|
||||||
|
@include enable-selection;
|
||||||
|
}
|
||||||
|
.palette-module-section {
|
||||||
|
padding:0 !important;
|
||||||
|
background: #f9f9f9 !important;
|
||||||
|
font-size: 0.9em;
|
||||||
|
color: #666;
|
||||||
|
|
||||||
|
}
|
||||||
|
233
editor/sass/projects.scss
Normal file
233
editor/sass/projects.scss
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
/**
|
||||||
|
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#projects-dialog {
|
||||||
|
.red-ui-editableList-container {
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
.projects-edit-form form {
|
||||||
|
margin: 0;
|
||||||
|
.form-row {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
label {
|
||||||
|
width: auto;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
input[type=text], input[type=password],textarea {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
input[type=checkbox], input[type=radio] {
|
||||||
|
width: auto;
|
||||||
|
vertical-align: top;
|
||||||
|
} }
|
||||||
|
}
|
||||||
|
.projects-dialog-spinner {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
text-align: center;
|
||||||
|
padding: 40px;
|
||||||
|
background: white;
|
||||||
|
&:before {
|
||||||
|
content: '';
|
||||||
|
display: inline-block;
|
||||||
|
height: 100%;
|
||||||
|
vertical-align: middle;
|
||||||
|
margin-right: -0.25em;
|
||||||
|
}
|
||||||
|
img {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
width: 80px;
|
||||||
|
}
|
||||||
|
&.projects-dialog-spinner-sidebar {
|
||||||
|
padding: 20px;
|
||||||
|
background: $shade-color;
|
||||||
|
img {
|
||||||
|
width: 50px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
.projects-dialog-screen-start {
|
||||||
|
button.editor-button {
|
||||||
|
width: calc(50% - 40px);
|
||||||
|
margin: 20px;
|
||||||
|
height: 200px;
|
||||||
|
line-height: 2em;
|
||||||
|
font-size: 1.5em !important;
|
||||||
|
i {
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
&:hover i {
|
||||||
|
color: #aaa;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.button-group {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.projects-dialog-screen-create {
|
||||||
|
min-height: 500px;
|
||||||
|
button.editor-button {
|
||||||
|
height: auto;
|
||||||
|
padding: 10px;
|
||||||
|
|
||||||
|
}
|
||||||
|
.button-group {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.projects-dialog-screen-secret {
|
||||||
|
min-height: auto;
|
||||||
|
}
|
||||||
|
.projects-dialog-project-list {
|
||||||
|
li {
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
&.projects-dialog-project-list-small {
|
||||||
|
.projects-dialog-project-list-entry {
|
||||||
|
padding: 6px 0;
|
||||||
|
i {
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.projects-dialog-project-list-entry-name {
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
.projects-dialog-project-list-entry-current {
|
||||||
|
margin-right: 10px;
|
||||||
|
padding-top: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
.projects-dialog-project-list-entry {
|
||||||
|
padding: 12px 0;
|
||||||
|
border-left: 3px solid #fff;
|
||||||
|
border-right: 3px solid #fff;
|
||||||
|
&.projects-list-entry-current {
|
||||||
|
&:not(.selectable) {
|
||||||
|
background: #f9f9f9;
|
||||||
|
}
|
||||||
|
i {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.selectable {
|
||||||
|
cursor: pointer;
|
||||||
|
&:hover {
|
||||||
|
background: #f3f3f3;
|
||||||
|
border-left-color: #aaa;
|
||||||
|
border-right-color: #aaa;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i {
|
||||||
|
color: #ccc;
|
||||||
|
font-size: 2em;
|
||||||
|
|
||||||
|
}
|
||||||
|
&.selected {
|
||||||
|
background: #efefef;
|
||||||
|
border-left-color:#999;
|
||||||
|
border-right-color:#999;
|
||||||
|
}
|
||||||
|
span {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align:middle;
|
||||||
|
}
|
||||||
|
.projects-dialog-project-list-entry-icon {
|
||||||
|
margin: 0 10px 0 5px;
|
||||||
|
}
|
||||||
|
.projects-dialog-project-list-entry-name {
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
.projects-dialog-project-list-entry-current {
|
||||||
|
float: right;
|
||||||
|
margin-right: 20px;
|
||||||
|
font-size: 0.9em;
|
||||||
|
color: #999;
|
||||||
|
padding-top: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.sidebar-projects {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.sidebar-projects-stack-info {
|
||||||
|
height: 100px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border-bottom: 1px solid $secondary-border-color;
|
||||||
|
color: #333;
|
||||||
|
i {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.sidebar-projects-stack {
|
||||||
|
position: absolute;
|
||||||
|
top: 100px;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
overflow-y: scroll;
|
||||||
|
|
||||||
|
.palette-category {
|
||||||
|
&:not(.palette-category-expanded) button {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-projects-dependencies {
|
||||||
|
position: relative;
|
||||||
|
height: 100%;
|
||||||
|
.red-ui-editableList-container {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.red-ui-editableList-border {
|
||||||
|
border: none;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
.red-ui-editableList-item-content {
|
||||||
|
padding: 0px 6px;
|
||||||
|
}
|
||||||
|
.palette-module-header {
|
||||||
|
padding: 6px 4px;
|
||||||
|
}
|
||||||
|
.palette-module-button {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
.palette-module-unused {
|
||||||
|
& > * {
|
||||||
|
color: #bbb;
|
||||||
|
}
|
||||||
|
// border: 1px dashed #bbb;
|
||||||
|
}
|
||||||
|
.palette-module-unknown {
|
||||||
|
// border: 1px dashed #b72828;
|
||||||
|
i.fa-warning {
|
||||||
|
color: #b07575; //#b72828;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -48,12 +48,15 @@
|
|||||||
|
|
||||||
@import "userSettings";
|
@import "userSettings";
|
||||||
|
|
||||||
|
@import "projects";
|
||||||
|
|
||||||
|
|
||||||
@import "ui/common/editableList";
|
@import "ui/common/editableList";
|
||||||
@import "ui/common/searchBox";
|
@import "ui/common/searchBox";
|
||||||
@import "ui/common/typedInput";
|
@import "ui/common/typedInput";
|
||||||
@import "ui/common/nodeList";
|
@import "ui/common/nodeList";
|
||||||
@import "ui/common/checkboxSet";
|
@import "ui/common/checkboxSet";
|
||||||
|
@import "ui/common/stack";
|
||||||
|
|
||||||
@import "dragdrop";
|
@import "dragdrop";
|
||||||
|
|
||||||
|
@ -87,12 +87,16 @@ table.node-info tr.blank {
|
|||||||
padding-left: 5px;
|
padding-left: 5px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.node-info-none {
|
||||||
|
font-style: italic;
|
||||||
|
color: #aaa;
|
||||||
|
}
|
||||||
table.node-info tr:not(.blank) td:first-child{
|
table.node-info tr:not(.blank) td:first-child{
|
||||||
color: #666;
|
color: #444;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
width: 90px;
|
width: 90px;
|
||||||
padding: 3px 3px 3px 6px;
|
padding: 3px 3px 3px 6px;
|
||||||
|
background:#f9f9f9;
|
||||||
border-right: 1px solid #ddd;
|
border-right: 1px solid #ddd;
|
||||||
}
|
}
|
||||||
table.node-info tr:not(.blank) td:last-child{
|
table.node-info tr:not(.blank) td:last-child{
|
||||||
|
22
editor/sass/ui/common/stack.scss
Normal file
22
editor/sass/ui/common/stack.scss
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
/**
|
||||||
|
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
**/
|
||||||
|
|
||||||
|
.red-ui-stack {
|
||||||
|
background: white;
|
||||||
|
.palette-category {
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
}
|
@ -209,6 +209,14 @@
|
|||||||
<div style="height: 200px;min-height: 150px;" class="node-text-editor" id="node-input-json"></div>
|
<div style="height: 200px;min-height: 150px;" class="node-text-editor" id="node-input-json"></div>
|
||||||
</div>
|
</div>
|
||||||
</script>
|
</script>
|
||||||
|
<script type="text/x-red" data-template-name="_markdown">
|
||||||
|
<div class="form-row" style="margin-bottom: 3px; text-align: right;">
|
||||||
|
<!--<button id="node-input-json-reformat" class="editor-button editor-button-small"><span data-i18n="jsonEditor.format"></span></button>-->
|
||||||
|
</div>
|
||||||
|
<div class="form-row node-text-editor-row">
|
||||||
|
<div style="height: 200px;min-height: 150px;" class="node-text-editor" id="node-input-markdown"></div>
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
<script type="text/x-red" data-template-name="_buffer">
|
<script type="text/x-red" data-template-name="_buffer">
|
||||||
<div id="node-input-buffer-panels">
|
<div id="node-input-buffer-panels">
|
||||||
<div id="node-input-buffer-panel-str" class="red-ui-panel">
|
<div id="node-input-buffer-panel-str" class="red-ui-panel">
|
||||||
@ -226,6 +234,20 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</script>
|
</script>
|
||||||
|
<script type="text/x-red" data-template-name="_project">
|
||||||
|
<div class="form-row">
|
||||||
|
<label>Project name</label>
|
||||||
|
<input type="text" id="node-input-project-name">
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<label>Summary</label>
|
||||||
|
<input type="text" id="node-input-project-summary">
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<label>Credential key</label>
|
||||||
|
<input type="password" id="node-input-project-key">
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
|
||||||
<script src="vendor/vendor.js"></script>
|
<script src="vendor/vendor.js"></script>
|
||||||
<script src="vendor/jsonata/jsonata.min.js"></script>
|
<script src="vendor/jsonata/jsonata.min.js"></script>
|
||||||
|
@ -120,6 +120,8 @@ module.exports = function(RED) {
|
|||||||
tlsNode.addTLSOptions(this.options);
|
tlsNode.addTLSOptions(this.options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
console.log(this.brokerurl,this.options);
|
||||||
|
|
||||||
// If there's no rejectUnauthorized already, then this could be an
|
// If there's no rejectUnauthorized already, then this could be an
|
||||||
// old config where this option was provided on the broker node and
|
// old config where this option was provided on the broker node and
|
||||||
// not the tls node
|
// not the tls node
|
||||||
@ -164,6 +166,7 @@ module.exports = function(RED) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
this.connect = function () {
|
this.connect = function () {
|
||||||
|
console.log("CONNECT");
|
||||||
if (!node.connected && !node.connecting) {
|
if (!node.connected && !node.connecting) {
|
||||||
node.connecting = true;
|
node.connecting = true;
|
||||||
node.client = mqtt.connect(node.brokerurl ,node.options);
|
node.client = mqtt.connect(node.brokerurl ,node.options);
|
||||||
|
@ -20,7 +20,6 @@ var settings;
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
init: function(_runtime) {
|
init: function(_runtime) {
|
||||||
console.log("info.init");
|
|
||||||
runtime = _runtime;
|
runtime = _runtime;
|
||||||
settings = runtime.settings;
|
settings = runtime.settings;
|
||||||
},
|
},
|
||||||
|
@ -66,10 +66,10 @@ module.exports = {
|
|||||||
editorApp.use("/theme",theme.app());
|
editorApp.use("/theme",theme.app());
|
||||||
editorApp.use("/",ui.editorResources);
|
editorApp.use("/",ui.editorResources);
|
||||||
|
|
||||||
// //Projects
|
//Projects
|
||||||
// var projects = require("./projects");
|
var projects = require("./projects");
|
||||||
// projects.init(runtime);
|
projects.init(runtime);
|
||||||
// editorApp.get("/projects",projects.app());
|
editorApp.use("/projects",projects.app());
|
||||||
|
|
||||||
// Locales
|
// Locales
|
||||||
var locales = require("./locales");
|
var locales = require("./locales");
|
||||||
|
@ -84,7 +84,8 @@
|
|||||||
"undeployedChanges": "node has undeployed changes",
|
"undeployedChanges": "node has undeployed changes",
|
||||||
"nodeActionDisabled": "node actions disabled within subflow",
|
"nodeActionDisabled": "node actions disabled within subflow",
|
||||||
"missing-types": "Flows stopped due to missing node types. Check logs for details.",
|
"missing-types": "Flows stopped due to missing node types. Check logs for details.",
|
||||||
"restartRequired": "Node-RED must be restarted to enable upgraded modules"
|
"restartRequired": "Node-RED must be restarted to enable upgraded modules",
|
||||||
|
"invalid-credentials-secret": "Flows stopped due to missing or invalid credentialSecret"
|
||||||
},
|
},
|
||||||
|
|
||||||
"error": "<strong>Error</strong>: __message__",
|
"error": "<strong>Error</strong>: __message__",
|
||||||
@ -388,7 +389,12 @@
|
|||||||
"showMore": "show more",
|
"showMore": "show more",
|
||||||
"showLess": "show less",
|
"showLess": "show less",
|
||||||
"flow": "Flow",
|
"flow": "Flow",
|
||||||
"information": "Information",
|
"selection":"Selection",
|
||||||
|
"nodes":"__count__ nodes",
|
||||||
|
"flowDesc": "Flow Description",
|
||||||
|
"subflowDesc": "Subflow Description",
|
||||||
|
"nodeHelp": "Node Help",
|
||||||
|
"none":"None",
|
||||||
"arrayItems": "__count__ items",
|
"arrayItems": "__count__ items",
|
||||||
"showTips":"You can open the tips from the settings panel"
|
"showTips":"You can open the tips from the settings panel"
|
||||||
},
|
},
|
||||||
@ -406,6 +412,15 @@
|
|||||||
"palette": {
|
"palette": {
|
||||||
"name": "Palette management",
|
"name": "Palette management",
|
||||||
"label": "palette"
|
"label": "palette"
|
||||||
|
},
|
||||||
|
"project": {
|
||||||
|
"label": "project",
|
||||||
|
"name": "Project",
|
||||||
|
"description": "Description",
|
||||||
|
"dependencies": "Dependencies",
|
||||||
|
"settings": "Settings",
|
||||||
|
"editDescription": "Edit project description",
|
||||||
|
"editDependencies": "Edit project dependencies"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"typedInput": {
|
"typedInput": {
|
||||||
@ -447,6 +462,9 @@
|
|||||||
"title": "JSON editor",
|
"title": "JSON editor",
|
||||||
"format": "format JSON"
|
"format": "format JSON"
|
||||||
},
|
},
|
||||||
|
"markdownEditor": {
|
||||||
|
"title": "Markdown editor"
|
||||||
|
},
|
||||||
"bufferEditor": {
|
"bufferEditor": {
|
||||||
"title": "Buffer editor",
|
"title": "Buffer editor",
|
||||||
"modeString": "Handle as UTF-8 String",
|
"modeString": "Handle as UTF-8 String",
|
||||||
|
137
red/api/editor/projects/index.js
Normal file
137
red/api/editor/projects/index.js
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
/**
|
||||||
|
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
**/
|
||||||
|
|
||||||
|
var express = require("express");
|
||||||
|
var runtime;
|
||||||
|
var settings;
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
init: function(_runtime) {
|
||||||
|
runtime = _runtime;
|
||||||
|
settings = runtime.settings;
|
||||||
|
},
|
||||||
|
app: function() {
|
||||||
|
var app = express();
|
||||||
|
|
||||||
|
// Projects
|
||||||
|
|
||||||
|
app.get("/", function(req,res) {
|
||||||
|
// List projects
|
||||||
|
runtime.storage.projects.listProjects().then(function(list) {
|
||||||
|
var active = runtime.storage.projects.getActiveProject();
|
||||||
|
var response = {
|
||||||
|
active: active,
|
||||||
|
projects: list
|
||||||
|
};
|
||||||
|
res.json(response);
|
||||||
|
}).otherwise(function(err) {
|
||||||
|
if (err.code) {
|
||||||
|
res.status(400).json({error:err.code, message: err.message});
|
||||||
|
} else {
|
||||||
|
res.status(400).json({error:"unexpected_error", message:err.toString()});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post("/", function(req,res) {
|
||||||
|
// Create project
|
||||||
|
runtime.storage.projects.createProject(req.body).then(function(name) {
|
||||||
|
console.log("Created project",name);
|
||||||
|
runtime.storage.projects.getProject(name).then(function(data) {
|
||||||
|
res.json(data);
|
||||||
|
});
|
||||||
|
}).otherwise(function(err) {
|
||||||
|
if (err.code) {
|
||||||
|
res.status(400).json({error:err.code, message: err.message});
|
||||||
|
} else {
|
||||||
|
res.status(400).json({error:"unexpected_error", message:err.toString()});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
app.put("/:id", function(req,res) {
|
||||||
|
// Update a project
|
||||||
|
//TODO: validate the payload properly
|
||||||
|
if (req.body.active) {
|
||||||
|
var currentProject = runtime.storage.projects.getActiveProject();
|
||||||
|
if (req.params.id !== currentProject) {
|
||||||
|
runtime.storage.projects.setActiveProject(req.params.id).then(function() {
|
||||||
|
res.redirect(303,req.baseUrl + '/');
|
||||||
|
}).otherwise(function(err) {
|
||||||
|
if (err.code) {
|
||||||
|
res.status(400).json({error:err.code, message: err.message});
|
||||||
|
} else {
|
||||||
|
res.status(400).json({error:"unexpected_error", message:err.toString()});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
res.redirect(303,req.baseUrl + '/');
|
||||||
|
}
|
||||||
|
} else if (req.body.credentialSecret || req.body.description || req.body.dependencies) {
|
||||||
|
runtime.storage.projects.updateProject(req.params.id, req.body).then(function() {
|
||||||
|
res.redirect(303,req.baseUrl + '/');
|
||||||
|
}).otherwise(function(err) {
|
||||||
|
if (err.code) {
|
||||||
|
res.status(400).json({error:err.code, message: err.message});
|
||||||
|
} else {
|
||||||
|
res.status(400).json({error:"unexpected_error", message:err.toString()});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/:id", function(req,res) {
|
||||||
|
// Get project metadata
|
||||||
|
runtime.storage.projects.getProject(req.params.id).then(function(data) {
|
||||||
|
if (data) {
|
||||||
|
res.json(data);
|
||||||
|
} else {
|
||||||
|
res.status(404).end();
|
||||||
|
}
|
||||||
|
}).otherwise(function(err) {
|
||||||
|
console.log(err.stack);
|
||||||
|
res.status(400).json({error:"unexpected_error", message:err.toString()});
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
app.delete("/:id",function(req,res) {
|
||||||
|
// Delete project
|
||||||
|
});
|
||||||
|
|
||||||
|
// Project Files
|
||||||
|
|
||||||
|
app.get("/:id/files", function(req,res) {
|
||||||
|
runtime.storage.projects.getFiles(req.params.id).then(function(data) {
|
||||||
|
res.json(data);
|
||||||
|
})
|
||||||
|
.otherwise(function(err) {
|
||||||
|
console.log(err.stack);
|
||||||
|
res.status(400).json({error:"unexpected_error", message:err.toString()});
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get(new RegExp("/([^\/]+)\/files\/(.*)"), function(req,res) {
|
||||||
|
// Get project file
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post(new RegExp("/([^\/]+)\/files\/(.*)"), function(req,res) {
|
||||||
|
// Update project file
|
||||||
|
});
|
||||||
|
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
}
|
@ -158,7 +158,7 @@ function start() {
|
|||||||
if (settings.httpStatic) {
|
if (settings.httpStatic) {
|
||||||
log.info(log._("runtime.paths.httpStatic",{path:path.resolve(settings.httpStatic)}));
|
log.info(log._("runtime.paths.httpStatic",{path:path.resolve(settings.httpStatic)}));
|
||||||
}
|
}
|
||||||
redNodes.loadFlows().then(redNodes.startFlows);
|
redNodes.loadFlows().then(redNodes.startFlows).otherwise(function(err) {});
|
||||||
started = true;
|
started = true;
|
||||||
}).otherwise(function(err) {
|
}).otherwise(function(err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
|
@ -133,6 +133,8 @@
|
|||||||
"localfilesystem": {
|
"localfilesystem": {
|
||||||
"user-dir": "User directory : __path__",
|
"user-dir": "User directory : __path__",
|
||||||
"flows-file": "Flows file : __path__",
|
"flows-file": "Flows file : __path__",
|
||||||
|
"changing-project": "Setting active project : __project__",
|
||||||
|
"active-project": "Active project : __project__",
|
||||||
"create": "Creating new __type__ file",
|
"create": "Creating new __type__ file",
|
||||||
"empty": "Existing __type__ file is empty",
|
"empty": "Existing __type__ file is empty",
|
||||||
"invalid": "Existing __type__ file is not valid json",
|
"invalid": "Existing __type__ file is not valid json",
|
||||||
|
@ -58,7 +58,7 @@ var api = module.exports = {
|
|||||||
*/
|
*/
|
||||||
var credentialsEncrypted = credentials.hasOwnProperty("$") && Object.keys(credentials).length === 1;
|
var credentialsEncrypted = credentials.hasOwnProperty("$") && Object.keys(credentials).length === 1;
|
||||||
var setupEncryptionPromise = when.resolve();
|
var setupEncryptionPromise = when.resolve();
|
||||||
if (encryptionEnabled === null) {
|
// if (encryptionEnabled === null) {
|
||||||
var defaultKey;
|
var defaultKey;
|
||||||
try {
|
try {
|
||||||
defaultKey = settings.get('_credentialSecret');
|
defaultKey = settings.get('_credentialSecret');
|
||||||
@ -73,12 +73,34 @@ var api = module.exports = {
|
|||||||
} catch(err) {
|
} catch(err) {
|
||||||
userKey = false;
|
userKey = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var projectKey = false;
|
||||||
|
try {
|
||||||
|
var projects = settings.get('projects');
|
||||||
|
if (projects && projects.activeProject) {
|
||||||
|
projectKey = projects.projects[projects.activeProject].credentialSecret;
|
||||||
|
}
|
||||||
|
} catch(err) {
|
||||||
|
}
|
||||||
|
if (projectKey) {
|
||||||
|
log.debug("red/runtime/nodes/credentials.load : using active project key - ignoring user provided key");
|
||||||
|
console.log(projectKey);
|
||||||
|
userKey = projectKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: Need to consider the various migration scenarios from no-project to project
|
||||||
|
// - _credentialSecret exists, projectKey exists
|
||||||
|
// - _credentialSecret does not exist, projectKey exists
|
||||||
|
// - userKey exists, projectKey exists
|
||||||
if (userKey === false) {
|
if (userKey === false) {
|
||||||
log.debug("red/runtime/nodes/credentials.load : user disabled encryption");
|
log.debug("red/runtime/nodes/credentials.load : user disabled encryption");
|
||||||
// User has disabled encryption
|
// User has disabled encryption
|
||||||
encryptionEnabled = false;
|
encryptionEnabled = false;
|
||||||
// Check if we have a generated _credSecret to decrypt with and remove
|
// Check if we have a generated _credSecret to decrypt with and remove
|
||||||
if (defaultKey) {
|
if (defaultKey) {
|
||||||
|
console.log("****************************************************************");
|
||||||
|
console.log("* Oh oh - default key present. We don't handle this well today *");
|
||||||
|
console.log("****************************************************************");
|
||||||
log.debug("red/runtime/nodes/credentials.load : default key present. Will migrate");
|
log.debug("red/runtime/nodes/credentials.load : default key present. Will migrate");
|
||||||
if (credentialsEncrypted) {
|
if (credentialsEncrypted) {
|
||||||
try {
|
try {
|
||||||
@ -86,13 +108,18 @@ var api = module.exports = {
|
|||||||
} catch(err) {
|
} catch(err) {
|
||||||
credentials = {};
|
credentials = {};
|
||||||
log.warn(log._("nodes.credentials.error",{message:err.toString()}))
|
log.warn(log._("nodes.credentials.error",{message:err.toString()}))
|
||||||
|
var error = new Error("Failed to decrypt credentials");
|
||||||
|
error.code = "credentials_load_failed";
|
||||||
|
return when.reject(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dirty = true;
|
dirty = true;
|
||||||
removeDefaultKey = true;
|
removeDefaultKey = true;
|
||||||
}
|
}
|
||||||
} else if (typeof userKey === 'string') {
|
} else if (typeof userKey === 'string') {
|
||||||
log.debug("red/runtime/nodes/credentials.load : user provided key");
|
if (!projectKey) {
|
||||||
|
log.debug("red/runtime/nodes/credentials.load : user provided key");
|
||||||
|
}
|
||||||
// User has provided own encryption key, get the 32-byte hash of it
|
// User has provided own encryption key, get the 32-byte hash of it
|
||||||
encryptionKey = crypto.createHash('sha256').update(userKey).digest();
|
encryptionKey = crypto.createHash('sha256').update(userKey).digest();
|
||||||
encryptionEnabled = true;
|
encryptionEnabled = true;
|
||||||
@ -107,6 +134,9 @@ var api = module.exports = {
|
|||||||
} catch(err) {
|
} catch(err) {
|
||||||
credentials = {};
|
credentials = {};
|
||||||
log.warn(log._("nodes.credentials.error",{message:err.toString()}))
|
log.warn(log._("nodes.credentials.error",{message:err.toString()}))
|
||||||
|
var error = new Error("Failed to decrypt credentials");
|
||||||
|
error.code = "credentials_load_failed";
|
||||||
|
return when.reject(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dirty = true;
|
dirty = true;
|
||||||
@ -136,7 +166,7 @@ var api = module.exports = {
|
|||||||
log.debug("red/runtime/nodes/credentials.load : using default key");
|
log.debug("red/runtime/nodes/credentials.load : using default key");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
//}
|
||||||
if (encryptionEnabled && !dirty) {
|
if (encryptionEnabled && !dirty) {
|
||||||
encryptedCredentials = credentials;
|
encryptedCredentials = credentials;
|
||||||
}
|
}
|
||||||
@ -149,6 +179,9 @@ var api = module.exports = {
|
|||||||
credentialCache = {};
|
credentialCache = {};
|
||||||
dirty = true;
|
dirty = true;
|
||||||
log.warn(log._("nodes.credentials.error",{message:err.toString()}))
|
log.warn(log._("nodes.credentials.error",{message:err.toString()}))
|
||||||
|
var error = new Error("Failed to decrypt credentials");
|
||||||
|
error.code = "credentials_load_failed";
|
||||||
|
return when.reject(error);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
credentialCache = credentials;
|
credentialCache = credentials;
|
||||||
|
@ -73,15 +73,18 @@ function loadFlows() {
|
|||||||
return storage.getFlows().then(function(config) {
|
return storage.getFlows().then(function(config) {
|
||||||
log.debug("loaded flow revision: "+config.rev);
|
log.debug("loaded flow revision: "+config.rev);
|
||||||
return credentials.load(config.credentials).then(function() {
|
return credentials.load(config.credentials).then(function() {
|
||||||
|
events.emit("runtime-event",{id:"runtime-state",retain:true});
|
||||||
return config;
|
return config;
|
||||||
});
|
});
|
||||||
}).otherwise(function(err) {
|
}).otherwise(function(err) {
|
||||||
|
activeConfig = null;
|
||||||
|
events.emit("runtime-event",{id:"runtime-state",payload:{type:"warning",error:"credentials_load_failed",text:"notification.warnings.invalid-credentials-secret"},retain:true});
|
||||||
log.warn(log._("nodes.flows.error",{message:err.toString()}));
|
log.warn(log._("nodes.flows.error",{message:err.toString()}));
|
||||||
console.log(err.stack);
|
throw err;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function load() {
|
function load(forceStart) {
|
||||||
return setFlows(null,"load",false);
|
return setFlows(null,"load",false,forceStart);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -89,7 +92,7 @@ function load() {
|
|||||||
* type - full/nodes/flows/load (default full)
|
* type - full/nodes/flows/load (default full)
|
||||||
* muteLog - don't emit the standard log messages (used for individual flow api)
|
* muteLog - don't emit the standard log messages (used for individual flow api)
|
||||||
*/
|
*/
|
||||||
function setFlows(_config,type,muteLog) {
|
function setFlows(_config,type,muteLog,forceStart) {
|
||||||
type = type||"full";
|
type = type||"full";
|
||||||
|
|
||||||
var configSavePromise = null;
|
var configSavePromise = null;
|
||||||
@ -131,7 +134,7 @@ function setFlows(_config,type,muteLog) {
|
|||||||
rev:flowRevision
|
rev:flowRevision
|
||||||
};
|
};
|
||||||
activeFlowConfig = newFlowConfig;
|
activeFlowConfig = newFlowConfig;
|
||||||
if (started) {
|
if (forceStart || started) {
|
||||||
return stop(type,diff,muteLog).then(function() {
|
return stop(type,diff,muteLog).then(function() {
|
||||||
context.clean(activeFlowConfig);
|
context.clean(activeFlowConfig);
|
||||||
start(type,diff,muteLog).then(function() {
|
start(type,diff,muteLog).then(function() {
|
||||||
@ -228,6 +231,7 @@ function handleStatus(node,statusMessage) {
|
|||||||
|
|
||||||
|
|
||||||
function start(type,diff,muteLog) {
|
function start(type,diff,muteLog) {
|
||||||
|
console.log("START----")
|
||||||
//dumpActiveNodes();
|
//dumpActiveNodes();
|
||||||
type = type||"full";
|
type = type||"full";
|
||||||
started = true;
|
started = true;
|
||||||
@ -323,6 +327,9 @@ function start(type,diff,muteLog) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function stop(type,diff,muteLog) {
|
function stop(type,diff,muteLog) {
|
||||||
|
if (!started) {
|
||||||
|
return when.resolve();
|
||||||
|
}
|
||||||
type = type||"full";
|
type = type||"full";
|
||||||
diff = diff||{
|
diff = diff||{
|
||||||
added:[],
|
added:[],
|
||||||
|
@ -54,7 +54,10 @@ var storageModuleInterface = {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
return when.reject(e);
|
return when.reject(e);
|
||||||
}
|
}
|
||||||
return storageModule.init(runtime.settings);
|
if (storageModule.projects) {
|
||||||
|
storageModuleInterface.projects = storageModule.projects;
|
||||||
|
}
|
||||||
|
return storageModule.init(runtime.settings,runtime);
|
||||||
},
|
},
|
||||||
getFlows: function() {
|
getFlows: function() {
|
||||||
return storageModule.getFlows().then(function(flows) {
|
return storageModule.getFlows().then(function(flows) {
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
var fs = require('fs-extra');
|
var fs = require('fs-extra');
|
||||||
var when = require('when');
|
var when = require('when');
|
||||||
var nodeFn = require('when/node/function');
|
|
||||||
var fspath = require("path");
|
var fspath = require("path");
|
||||||
|
|
||||||
var log = require("../../log");
|
var log = require("../../log");
|
||||||
@ -24,6 +23,7 @@ var util = require("./util");
|
|||||||
var library = require("./library");
|
var library = require("./library");
|
||||||
var sessions = require("./sessions");
|
var sessions = require("./sessions");
|
||||||
var runtimeSettings = require("./settings");
|
var runtimeSettings = require("./settings");
|
||||||
|
var projects = require("./projects");
|
||||||
|
|
||||||
var initialFlowLoadComplete = false;
|
var initialFlowLoadComplete = false;
|
||||||
var settings;
|
var settings;
|
||||||
@ -32,10 +32,9 @@ var flowsFullPath;
|
|||||||
var flowsFileBackup;
|
var flowsFileBackup;
|
||||||
var credentialsFile;
|
var credentialsFile;
|
||||||
var credentialsFileBackup;
|
var credentialsFileBackup;
|
||||||
var oldCredentialsFile;
|
|
||||||
|
|
||||||
var localfilesystem = {
|
var localfilesystem = {
|
||||||
init: function(_settings) {
|
init: function(_settings, runtime) {
|
||||||
settings = _settings;
|
settings = _settings;
|
||||||
|
|
||||||
var promises = [];
|
var promises = [];
|
||||||
@ -62,50 +61,14 @@ var localfilesystem = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (settings.flowFile) {
|
|
||||||
flowsFile = settings.flowFile;
|
|
||||||
// handle Unix and Windows "C:\"
|
|
||||||
if ((flowsFile[0] == "/") || (flowsFile[1] == ":")) {
|
|
||||||
// Absolute path
|
|
||||||
flowsFullPath = flowsFile;
|
|
||||||
} else if (flowsFile.substring(0,2) === "./") {
|
|
||||||
// Relative to cwd
|
|
||||||
flowsFullPath = fspath.join(process.cwd(),flowsFile);
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
fs.statSync(fspath.join(process.cwd(),flowsFile));
|
|
||||||
// Found in cwd
|
|
||||||
flowsFullPath = fspath.join(process.cwd(),flowsFile);
|
|
||||||
} catch(err) {
|
|
||||||
// Use userDir
|
|
||||||
flowsFullPath = fspath.join(settings.userDir,flowsFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
flowsFile = 'flows_'+require('os').hostname()+'.json';
|
|
||||||
flowsFullPath = fspath.join(settings.userDir,flowsFile);
|
|
||||||
}
|
|
||||||
var ffExt = fspath.extname(flowsFullPath);
|
|
||||||
var ffName = fspath.basename(flowsFullPath);
|
|
||||||
var ffBase = fspath.basename(flowsFullPath,ffExt);
|
|
||||||
var ffDir = fspath.dirname(flowsFullPath);
|
|
||||||
|
|
||||||
credentialsFile = fspath.join(settings.userDir,ffBase+"_cred"+ffExt);
|
|
||||||
credentialsFileBackup = fspath.join(settings.userDir,"."+ffBase+"_cred"+ffExt+".backup");
|
|
||||||
|
|
||||||
oldCredentialsFile = fspath.join(settings.userDir,"credentials.json");
|
|
||||||
|
|
||||||
flowsFileBackup = fspath.join(ffDir,"."+ffName+".backup");
|
|
||||||
|
|
||||||
sessions.init(settings);
|
sessions.init(settings);
|
||||||
runtimeSettings.init(settings);
|
runtimeSettings.init(settings);
|
||||||
|
promises.push(library.init(settings));
|
||||||
|
promises.push(projects.init(settings, runtime));
|
||||||
|
|
||||||
var packageFile = fspath.join(settings.userDir,"package.json");
|
var packageFile = fspath.join(settings.userDir,"package.json");
|
||||||
var packagePromise = when.resolve();
|
var packagePromise = when.resolve();
|
||||||
|
|
||||||
promises.push(library.init(settings));
|
|
||||||
|
|
||||||
if (!settings.readOnly) {
|
if (!settings.readOnly) {
|
||||||
packagePromise = function() {
|
packagePromise = function() {
|
||||||
try {
|
try {
|
||||||
@ -124,64 +87,19 @@ var localfilesystem = {
|
|||||||
return when.all(promises).then(packagePromise);
|
return when.all(promises).then(packagePromise);
|
||||||
},
|
},
|
||||||
|
|
||||||
getFlows: function() {
|
|
||||||
if (!initialFlowLoadComplete) {
|
|
||||||
initialFlowLoadComplete = true;
|
|
||||||
log.info(log._("storage.localfilesystem.user-dir",{path:settings.userDir}));
|
|
||||||
log.info(log._("storage.localfilesystem.flows-file",{path:flowsFullPath}));
|
|
||||||
}
|
|
||||||
return util.readFile(flowsFullPath,flowsFileBackup,[],'flow');
|
|
||||||
},
|
|
||||||
|
|
||||||
saveFlows: function(flows) {
|
getFlows: projects.getFlows,
|
||||||
if (settings.readOnly) {
|
saveFlows: projects.saveFlows,
|
||||||
return when.resolve();
|
getCredentials: projects.getCredentials,
|
||||||
}
|
saveCredentials: projects.saveCredentials,
|
||||||
|
|
||||||
try {
|
|
||||||
fs.renameSync(flowsFullPath,flowsFileBackup);
|
|
||||||
} catch(err) {
|
|
||||||
}
|
|
||||||
|
|
||||||
var flowData;
|
|
||||||
|
|
||||||
if (settings.flowFilePretty) {
|
|
||||||
flowData = JSON.stringify(flows,null,4);
|
|
||||||
} else {
|
|
||||||
flowData = JSON.stringify(flows);
|
|
||||||
}
|
|
||||||
return util.writeFile(flowsFullPath, flowData);
|
|
||||||
},
|
|
||||||
|
|
||||||
getCredentials: function() {
|
|
||||||
return util.readFile(credentialsFile,credentialsFileBackup,{},'credentials');
|
|
||||||
},
|
|
||||||
|
|
||||||
saveCredentials: function(credentials) {
|
|
||||||
if (settings.readOnly) {
|
|
||||||
return when.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
fs.renameSync(credentialsFile,credentialsFileBackup);
|
|
||||||
} catch(err) {
|
|
||||||
}
|
|
||||||
var credentialData;
|
|
||||||
if (settings.flowFilePretty) {
|
|
||||||
credentialData = JSON.stringify(credentials,null,4);
|
|
||||||
} else {
|
|
||||||
credentialData = JSON.stringify(credentials);
|
|
||||||
}
|
|
||||||
return util.writeFile(credentialsFile, credentialData);
|
|
||||||
},
|
|
||||||
|
|
||||||
getSettings: runtimeSettings.getSettings,
|
getSettings: runtimeSettings.getSettings,
|
||||||
saveSettings: runtimeSettings.saveSettings,
|
saveSettings: runtimeSettings.saveSettings,
|
||||||
getSessions: sessions.getSessions,
|
getSessions: sessions.getSessions,
|
||||||
saveSessions: sessions.saveSessions,
|
saveSessions: sessions.saveSessions,
|
||||||
getLibraryEntry: library.getLibraryEntry,
|
getLibraryEntry: library.getLibraryEntry,
|
||||||
saveLibraryEntry: library.saveLibraryEntry
|
saveLibraryEntry: library.saveLibraryEntry,
|
||||||
|
projects: projects
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = localfilesystem;
|
module.exports = localfilesystem;
|
||||||
|
86
red/runtime/storage/localfilesystem/projects/git/index.js
Normal file
86
red/runtime/storage/localfilesystem/projects/git/index.js
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
/**
|
||||||
|
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
**/
|
||||||
|
|
||||||
|
var when = require('when');
|
||||||
|
var exec = require('child_process').exec;
|
||||||
|
var spawn = require('child_process').spawn;
|
||||||
|
|
||||||
|
|
||||||
|
function execCommand(command,args,cwd) {
|
||||||
|
return when.promise(function(resolve,reject) {
|
||||||
|
var fullCommand = command+" "+args.join(" ");
|
||||||
|
child = exec(fullCommand, {cwd: cwd, timeout:3000, killSignal: 'SIGTERM'}, function (error, stdout, stderr) {
|
||||||
|
if (error) {
|
||||||
|
reject(error);
|
||||||
|
} else {
|
||||||
|
resolve(stdout);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function runCommand(command,args,cwd) {
|
||||||
|
console.log(cwd,command,args);
|
||||||
|
return when.promise(function(resolve,reject) {
|
||||||
|
var child = spawn(command, args, {cwd:cwd, detached:true});
|
||||||
|
var stdout = "";
|
||||||
|
var stderr = "";
|
||||||
|
child.stdout.on('data', function(data) {
|
||||||
|
stdout += data;
|
||||||
|
});
|
||||||
|
|
||||||
|
child.stderr.on('data', function(data) {
|
||||||
|
stderr += data;
|
||||||
|
});
|
||||||
|
|
||||||
|
child.on('close', function(code) {
|
||||||
|
if (code !== 0) {
|
||||||
|
var err = new Error(stderr);
|
||||||
|
if (/fatal: could not read Username/.test(stderr)) {
|
||||||
|
err.code = "git_auth_failed";
|
||||||
|
} else if(/HTTP Basic: Access denied/.test(stderr)) {
|
||||||
|
err.code = "git_auth_failed";
|
||||||
|
} else {
|
||||||
|
err.code = "git_error";
|
||||||
|
}
|
||||||
|
return reject(err);
|
||||||
|
}
|
||||||
|
resolve(stdout);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function isAuthError(err) {
|
||||||
|
// var lines = err.toString().split("\n");
|
||||||
|
// lines.forEach(console.log);
|
||||||
|
}
|
||||||
|
|
||||||
|
var gitCommand = "git";
|
||||||
|
module.exports = {
|
||||||
|
initRepo: function(cwd) {
|
||||||
|
return runCommand(gitCommand,["init"],cwd);
|
||||||
|
},
|
||||||
|
pull: function(repo, cwd) {
|
||||||
|
if (repo.url) {
|
||||||
|
repo = repo.url;
|
||||||
|
}
|
||||||
|
var args = ["pull",repo,"master"];
|
||||||
|
return runCommand(gitCommand,args,cwd);
|
||||||
|
},
|
||||||
|
clone: function(repo, cwd) {
|
||||||
|
var args = ["clone",repo,"."];
|
||||||
|
return runCommand(gitCommand,args,cwd);
|
||||||
|
}
|
||||||
|
}
|
519
red/runtime/storage/localfilesystem/projects/index.js
Normal file
519
red/runtime/storage/localfilesystem/projects/index.js
Normal file
@ -0,0 +1,519 @@
|
|||||||
|
/**
|
||||||
|
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
**/
|
||||||
|
|
||||||
|
var fs = require('fs-extra');
|
||||||
|
var when = require('when');
|
||||||
|
var fspath = require("path");
|
||||||
|
var nodeFn = require('when/node/function');
|
||||||
|
var crypto = require('crypto');
|
||||||
|
|
||||||
|
var storageSettings = require("../settings");
|
||||||
|
var util = require("../util");
|
||||||
|
var gitTools = require("./git");
|
||||||
|
|
||||||
|
var settings;
|
||||||
|
var runtime;
|
||||||
|
|
||||||
|
var projectsDir;
|
||||||
|
|
||||||
|
|
||||||
|
function init(_settings, _runtime) {
|
||||||
|
settings = _settings;
|
||||||
|
runtime = _runtime;
|
||||||
|
log = runtime.log;
|
||||||
|
|
||||||
|
projectsDir = fspath.join(settings.userDir,"projects");
|
||||||
|
|
||||||
|
if (settings.flowFile) {
|
||||||
|
flowsFile = settings.flowFile;
|
||||||
|
// handle Unix and Windows "C:\"
|
||||||
|
if ((flowsFile[0] == "/") || (flowsFile[1] == ":")) {
|
||||||
|
// Absolute path
|
||||||
|
flowsFullPath = flowsFile;
|
||||||
|
} else if (flowsFile.substring(0,2) === "./") {
|
||||||
|
// Relative to cwd
|
||||||
|
flowsFullPath = fspath.join(process.cwd(),flowsFile);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
fs.statSync(fspath.join(process.cwd(),flowsFile));
|
||||||
|
// Found in cwd
|
||||||
|
flowsFullPath = fspath.join(process.cwd(),flowsFile);
|
||||||
|
} catch(err) {
|
||||||
|
// Use userDir
|
||||||
|
flowsFullPath = fspath.join(settings.userDir,flowsFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
flowsFile = 'flows_'+require('os').hostname()+'.json';
|
||||||
|
flowsFullPath = fspath.join(settings.userDir,flowsFile);
|
||||||
|
}
|
||||||
|
var ffExt = fspath.extname(flowsFullPath);
|
||||||
|
var ffBase = fspath.basename(flowsFullPath,ffExt);
|
||||||
|
|
||||||
|
flowsFileBackup = getBackupFilename(flowsFullPath);
|
||||||
|
credentialsFile = fspath.join(settings.userDir,ffBase+"_cred"+ffExt);
|
||||||
|
credentialsFileBackup = getBackupFilename(credentialsFile)
|
||||||
|
|
||||||
|
if (!settings.readOnly) {
|
||||||
|
return util.promiseDir(projectsDir)
|
||||||
|
//TODO: this is accessing settings from storage directly as settings
|
||||||
|
// has not yet been initialised. That isn't ideal - can this be deferred?
|
||||||
|
.then(storageSettings.getSettings)
|
||||||
|
.then(function(globalSettings) {
|
||||||
|
if (!globalSettings.projects) {
|
||||||
|
// TODO: Migration Case
|
||||||
|
console.log("TODO: Migration from single file to project");
|
||||||
|
globalSettings.projects = {
|
||||||
|
activeProject: "",
|
||||||
|
projects: {}
|
||||||
|
}
|
||||||
|
return storageSettings.saveSettings(globalSettings);
|
||||||
|
} else {
|
||||||
|
activeProject = globalSettings.projects.activeProject;
|
||||||
|
var projectPath = fspath.join(projectsDir,activeProject);
|
||||||
|
flowsFullPath = fspath.join(projectPath,"flow.json");
|
||||||
|
flowsFileBackup = getBackupFilename(flowsFullPath);
|
||||||
|
credentialsFile = fspath.join(projectPath,"flow_cred.json");
|
||||||
|
credentialsFileBackup = getBackupFilename(credentialsFile);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return when.resolve();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getBackupFilename(filename) {
|
||||||
|
var ffName = fspath.basename(filename);
|
||||||
|
var ffDir = fspath.dirname(filename);
|
||||||
|
return fspath.join(ffDir,"."+ffName+".backup");
|
||||||
|
}
|
||||||
|
|
||||||
|
function listProjects() {
|
||||||
|
return nodeFn.call(fs.readdir, projectsDir).then(function(fns) {
|
||||||
|
var dirs = [];
|
||||||
|
fns.sort().filter(function(fn) {
|
||||||
|
var fullPath = fspath.join(projectsDir,fn);
|
||||||
|
if (fn[0] != ".") {
|
||||||
|
var stats = fs.lstatSync(fullPath);
|
||||||
|
if (stats.isDirectory()) {
|
||||||
|
dirs.push(fn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return dirs;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getProject(project) {
|
||||||
|
|
||||||
|
return when.promise(function(resolve,reject) {
|
||||||
|
if (project === "") {
|
||||||
|
return reject(new Error("NLS: No active project set"));
|
||||||
|
}
|
||||||
|
var projectPath = fspath.join(projectsDir,project);
|
||||||
|
var globalProjectSettings = settings.get("projects");
|
||||||
|
var projectSettings = {};
|
||||||
|
if (globalProjectSettings.projects) {
|
||||||
|
projectSettings = globalProjectSettings.projects[project]||{};
|
||||||
|
}
|
||||||
|
|
||||||
|
// console.log(projectSettings);
|
||||||
|
var projectData = {
|
||||||
|
name: project
|
||||||
|
};
|
||||||
|
var promises = [];
|
||||||
|
checkProjectFiles(project).then(function(missingFiles) {
|
||||||
|
if (missingFiles.length > 0) {
|
||||||
|
projectData.missingFiles = missingFiles;
|
||||||
|
}
|
||||||
|
if (missingFiles.indexOf('package.json') === -1) {
|
||||||
|
promises.push(nodeFn.call(fs.readFile,fspath.join(projectPath,"package.json"),"utf8").then(function(content) {
|
||||||
|
var package = util.parseJSON(content);
|
||||||
|
projectData.dependencies = package.dependencies||{};
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
if (missingFiles.indexOf('README.md') === -1) {
|
||||||
|
promises.push(nodeFn.call(fs.readFile,fspath.join(projectPath,"README.md"),"utf8").then(function(content) {
|
||||||
|
projectData.description = content;
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
projectData.description = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
when.settle(promises).then(function() {
|
||||||
|
resolve(projectData);
|
||||||
|
})
|
||||||
|
// if (missingFiles.indexOf('flow_cred.json') === -1) {
|
||||||
|
// promises.push(nodeFn.call(fs.readFile,fspath.join(projectPath,"flow_cred.json"),"utf8").then(function(creds) {
|
||||||
|
// var credentials = util.parseJSON(creds);
|
||||||
|
// if (credentials.hasOwnProperty('$')) {
|
||||||
|
// // try {
|
||||||
|
// // decryptCredentials
|
||||||
|
// // }
|
||||||
|
// }
|
||||||
|
// }));
|
||||||
|
// }
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// fs.stat(projectPath,function(err,stat) {
|
||||||
|
// if (err) {
|
||||||
|
// return resolve(null);
|
||||||
|
// }
|
||||||
|
// resolve(nodeFn.call(fs.readFile,projectPackage,'utf8').then(util.parseJSON));
|
||||||
|
// })
|
||||||
|
}).otherwise(function(err) {
|
||||||
|
console.log(err);
|
||||||
|
var e = new Error("NLD: project not found");
|
||||||
|
e.code = "project_not_found";
|
||||||
|
throw e;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var encryptionAlgorithm = "aes-256-ctr";
|
||||||
|
function decryptCredentials(key,credentials) {
|
||||||
|
var creds = credentials["$"];
|
||||||
|
var initVector = new Buffer(creds.substring(0, 32),'hex');
|
||||||
|
creds = creds.substring(32);
|
||||||
|
var decipher = crypto.createDecipheriv(encryptionAlgorithm, key, initVector);
|
||||||
|
var decrypted = decipher.update(creds, 'base64', 'utf8') + decipher.final('utf8');
|
||||||
|
return JSON.parse(decrypted);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setCredentialSecret(project,secret) {
|
||||||
|
var globalProjectSettings = settings.get("projects");
|
||||||
|
globalProjectSettings.projects = globalProjectSettings.projects || {};
|
||||||
|
globalProjectSettings.projects[project] = globalProjectSettings.projects[project] || {};
|
||||||
|
globalProjectSettings.projects[project].credentialSecret = secret;
|
||||||
|
return settings.set("projects",globalProjectSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createProject(metadata) {
|
||||||
|
var project = metadata.name;
|
||||||
|
return when.promise(function(resolve,reject) {
|
||||||
|
if (project === "") {
|
||||||
|
return reject(new Error("NLS: No project set"));
|
||||||
|
}
|
||||||
|
var projectPath = fspath.join(projectsDir,project);
|
||||||
|
fs.stat(projectPath, function(err,stat) {
|
||||||
|
if (!err) {
|
||||||
|
var e = new Error("NLS: Project already exists");
|
||||||
|
e.code = "project_exists";
|
||||||
|
return reject(e);
|
||||||
|
}
|
||||||
|
createProjectDirectory(project).then(function() {
|
||||||
|
if (metadata.credentialSecret) {
|
||||||
|
return setCredentialSecret(project,metadata.credentialSecret);
|
||||||
|
}
|
||||||
|
return when.resolve();
|
||||||
|
}).then(function() {
|
||||||
|
if (metadata.remote) {
|
||||||
|
return gitTools.pull(metadata.remote,projectPath).then(function(result) {
|
||||||
|
// Check this is a valid project
|
||||||
|
// If it is empty
|
||||||
|
// - if 'populate' flag is set, call populateProject
|
||||||
|
// - otherwise reject with suitable error to allow UI to confirm population
|
||||||
|
// If it is missing package.json/flow.json/flow_cred.json
|
||||||
|
// - reject as invalid project
|
||||||
|
|
||||||
|
checkProjectFiles(project).then(function(results) {
|
||||||
|
console.log("checkProjectFiles");
|
||||||
|
console.log(results);
|
||||||
|
});
|
||||||
|
|
||||||
|
resolve(project);
|
||||||
|
}).otherwise(function(error) {
|
||||||
|
fs.remove(projectPath,function() {
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
createDefaultProject(metadata).then(function() { resolve(project)}).otherwise(reject);
|
||||||
|
}
|
||||||
|
}).otherwise(reject);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function createProjectDirectory(project) {
|
||||||
|
var projectPath = fspath.join(projectsDir,project);
|
||||||
|
return util.promiseDir(projectPath).then(function() {
|
||||||
|
return gitTools.initRepo(projectPath)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaultFileSet = {
|
||||||
|
"package.json": function(project) {
|
||||||
|
return JSON.stringify({
|
||||||
|
"name": project.name,
|
||||||
|
"description": project.summary||"A Node-RED Project",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"dependencies": {}
|
||||||
|
},"",4);
|
||||||
|
},
|
||||||
|
"README.md": function(project) {
|
||||||
|
return project.name+"\n"+("=".repeat(project.name.length))+"\n\n"+(project.summary||"A Node-RED Project")+"\n\n";
|
||||||
|
},
|
||||||
|
"settings.json": function() { return "{}" },
|
||||||
|
"flow.json": function() { return "[]" },
|
||||||
|
"flow_cred.json": function() { return "{}" }
|
||||||
|
}
|
||||||
|
|
||||||
|
function createDefaultProject(project) {
|
||||||
|
var projectPath = fspath.join(projectsDir,project.name);
|
||||||
|
// Create a basic skeleton of a project
|
||||||
|
var promises = [];
|
||||||
|
for (var file in defaultFileSet) {
|
||||||
|
if (defaultFileSet.hasOwnProperty(file)) {
|
||||||
|
promises.push(util.writeFile(fspath.join(projectPath,file),defaultFileSet[file](project)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return when.all(promises);
|
||||||
|
}
|
||||||
|
function checkProjectExists(project) {
|
||||||
|
var projectPath = fspath.join(projectsDir,project);
|
||||||
|
return nodeFn.call(fs.stat,projectPath).otherwise(function(err) {
|
||||||
|
var e = new Error("NLD: project not found");
|
||||||
|
e.code = "project_not_found";
|
||||||
|
throw e;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function checkProjectFiles(project) {
|
||||||
|
var projectPath = fspath.join(projectsDir,project);
|
||||||
|
var promises = [];
|
||||||
|
var paths = [];
|
||||||
|
for (var file in defaultFileSet) {
|
||||||
|
if (defaultFileSet.hasOwnProperty(file)) {
|
||||||
|
paths.push(file);
|
||||||
|
promises.push(nodeFn.call(fs.stat,fspath.join(projectPath,file)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return when.settle(promises).then(function(results) {
|
||||||
|
var missing = [];
|
||||||
|
results.forEach(function(result,i) {
|
||||||
|
if (result.state === 'rejected') {
|
||||||
|
missing.push(paths[i]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return missing;
|
||||||
|
}).then(function(missing) {
|
||||||
|
// if (createMissing) {
|
||||||
|
// var promises = [];
|
||||||
|
// missing.forEach(function(file) {
|
||||||
|
// promises.push(util.writeFile(fspath.join(projectPath,file),defaultFileSet[file](project)));
|
||||||
|
// });
|
||||||
|
// return promises;
|
||||||
|
// } else {
|
||||||
|
return missing;
|
||||||
|
// }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function getFiles(project) {
|
||||||
|
var projectPath = fspath.join(projectsDir,project);
|
||||||
|
return nodeFn.call(listFiles,projectPath,"/");
|
||||||
|
}
|
||||||
|
function getFile(project,path) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function listFiles(root,path,done) {
|
||||||
|
var entries = [];
|
||||||
|
var fullPath = fspath.join(root,path);
|
||||||
|
fs.readdir(fullPath, function(err,fns) {
|
||||||
|
var childCount = fns.length;
|
||||||
|
fns.sort().forEach(function(fn) {
|
||||||
|
if (fn === ".git") {
|
||||||
|
childCount--;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var child = {
|
||||||
|
path: fspath.join(path,fn),
|
||||||
|
name: fn
|
||||||
|
};
|
||||||
|
entries.push(child);
|
||||||
|
var childFullPath = fspath.join(fullPath,fn);
|
||||||
|
fs.lstat(childFullPath, function(err, stats) {
|
||||||
|
if (stats.isDirectory()) {
|
||||||
|
child.type = 'd';
|
||||||
|
listFiles(root,child.path,function(err,children) {
|
||||||
|
child.children = children;
|
||||||
|
childCount--;
|
||||||
|
if (childCount === 0) {
|
||||||
|
done(null,entries);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
child.type = 'f';
|
||||||
|
childCount--;
|
||||||
|
console.log(child,childCount)
|
||||||
|
if (childCount === 0) {
|
||||||
|
done(null,entries);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var activeProject
|
||||||
|
function getActiveProject() {
|
||||||
|
return activeProject;
|
||||||
|
}
|
||||||
|
|
||||||
|
function reloadActiveProject(project) {
|
||||||
|
return runtime.nodes.stopFlows().then(function() {
|
||||||
|
return runtime.nodes.loadFlows(true).then(function() {
|
||||||
|
runtime.events.emit("runtime-event",{id:"project-change",payload:{ project: project}});
|
||||||
|
}).otherwise(function(err) {
|
||||||
|
// We're committed to the project change now, so notify editors
|
||||||
|
// that it has changed.
|
||||||
|
runtime.events.emit("runtime-event",{id:"project-change",payload:{ project: project}});
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function setActiveProject(project) {
|
||||||
|
return checkProjectExists(project).then(function() {
|
||||||
|
activeProject = project;
|
||||||
|
var globalProjectSettings = settings.get("projects");
|
||||||
|
globalProjectSettings.activeProject = project;
|
||||||
|
return settings.set("projects",globalProjectSettings).then(function() {
|
||||||
|
var projectPath = fspath.join(projectsDir,project);
|
||||||
|
flowsFullPath = fspath.join(projectPath,"flow.json");
|
||||||
|
flowsFileBackup = getBackupFilename(flowsFullPath);
|
||||||
|
credentialsFile = fspath.join(projectPath,"flow_cred.json");
|
||||||
|
credentialsFileBackup = getBackupFilename(credentialsFile);
|
||||||
|
|
||||||
|
log.info(log._("storage.localfilesystem.changing-project",{project:activeProject||"none"}));
|
||||||
|
log.info(log._("storage.localfilesystem.flows-file",{path:flowsFullPath}));
|
||||||
|
|
||||||
|
console.log("Updated file targets to");
|
||||||
|
console.log(flowsFullPath)
|
||||||
|
console.log(credentialsFile)
|
||||||
|
|
||||||
|
return reloadActiveProject(project);
|
||||||
|
|
||||||
|
})
|
||||||
|
// return when.promise(function(resolve,reject) {
|
||||||
|
// console.log("Activating project");
|
||||||
|
// resolve();
|
||||||
|
// });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function updateProject(project,data) {
|
||||||
|
return checkProjectExists(project).then(function() {
|
||||||
|
if (data.credentialSecret) {
|
||||||
|
// TODO: this path assumes we aren't trying to migrate the secret
|
||||||
|
return setCredentialSecret(project,data.credentialSecret).then(function() {
|
||||||
|
return reloadActiveProject(project);
|
||||||
|
})
|
||||||
|
} else if (data.description) {
|
||||||
|
var projectPath = fspath.join(projectsDir,project);
|
||||||
|
var readmeFile = fspath.join(projectPath,"README.md");
|
||||||
|
return util.writeFile(readmeFile, data.description);
|
||||||
|
} else if (data.dependencies) {
|
||||||
|
var projectPath = fspath.join(projectsDir,project);
|
||||||
|
var packageJSON = fspath.join(projectPath,"package.json");
|
||||||
|
return nodeFn.call(fs.readFile,packageJSON,"utf8").then(function(content) {
|
||||||
|
var package = util.parseJSON(content);
|
||||||
|
package.dependencies = data.dependencies;
|
||||||
|
return util.writeFile(packageJSON,JSON.stringify(package,"",4));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var initialFlowLoadComplete = false;
|
||||||
|
|
||||||
|
var flowsFile;
|
||||||
|
var flowsFullPath;
|
||||||
|
var flowsFileBackup;
|
||||||
|
var credentialsFile;
|
||||||
|
var credentialsFileBackup;
|
||||||
|
|
||||||
|
function getFlows() {
|
||||||
|
if (!initialFlowLoadComplete) {
|
||||||
|
initialFlowLoadComplete = true;
|
||||||
|
log.info(log._("storage.localfilesystem.user-dir",{path:settings.userDir}));
|
||||||
|
log.info(log._("storage.localfilesystem.flows-file",{path:flowsFullPath}));
|
||||||
|
log.info(log._("storage.localfilesystem.active-project",{project:activeProject||"none"}));
|
||||||
|
}
|
||||||
|
return util.readFile(flowsFullPath,flowsFileBackup,[],'flow');
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveFlows(flows) {
|
||||||
|
if (settings.readOnly) {
|
||||||
|
return when.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
fs.renameSync(flowsFullPath,flowsFileBackup);
|
||||||
|
} catch(err) {
|
||||||
|
}
|
||||||
|
|
||||||
|
var flowData;
|
||||||
|
|
||||||
|
if (settings.flowFilePretty) {
|
||||||
|
flowData = JSON.stringify(flows,null,4);
|
||||||
|
} else {
|
||||||
|
flowData = JSON.stringify(flows);
|
||||||
|
}
|
||||||
|
return util.writeFile(flowsFullPath, flowData);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCredentials() {
|
||||||
|
return util.readFile(credentialsFile,credentialsFileBackup,{},'credentials');
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveCredentials(credentials) {
|
||||||
|
if (settings.readOnly) {
|
||||||
|
return when.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
fs.renameSync(credentialsFile,credentialsFileBackup);
|
||||||
|
} catch(err) {
|
||||||
|
}
|
||||||
|
var credentialData;
|
||||||
|
if (settings.flowFilePretty) {
|
||||||
|
credentialData = JSON.stringify(credentials,null,4);
|
||||||
|
} else {
|
||||||
|
credentialData = JSON.stringify(credentials);
|
||||||
|
}
|
||||||
|
return util.writeFile(credentialsFile, credentialData);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
init: init,
|
||||||
|
listProjects: listProjects,
|
||||||
|
getActiveProject: getActiveProject,
|
||||||
|
setActiveProject: setActiveProject,
|
||||||
|
getProject: getProject,
|
||||||
|
createProject: createProject,
|
||||||
|
updateProject: updateProject,
|
||||||
|
getFiles: getFiles,
|
||||||
|
|
||||||
|
getFlows: getFlows,
|
||||||
|
saveFlows: saveFlows,
|
||||||
|
getCredentials: getCredentials,
|
||||||
|
saveCredentials: saveCredentials
|
||||||
|
|
||||||
|
};
|
@ -23,7 +23,7 @@ var when = require('when');
|
|||||||
|
|
||||||
var flow = require("../../../../red/api/admin/flow");
|
var flow = require("../../../../red/api/admin/flow");
|
||||||
|
|
||||||
describe("flow api", function() {
|
describe("api/admin/flow", function() {
|
||||||
|
|
||||||
var app;
|
var app;
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ var when = require('when');
|
|||||||
|
|
||||||
var flows = require("../../../../red/api/admin/flows");
|
var flows = require("../../../../red/api/admin/flows");
|
||||||
|
|
||||||
describe("flows api", function() {
|
describe("api/admin/flows", function() {
|
||||||
|
|
||||||
var app;
|
var app;
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ var app = express();
|
|||||||
var info = require("../../../../red/api/admin/info");
|
var info = require("../../../../red/api/admin/info");
|
||||||
var theme = require("../../../../red/api/editor/theme");
|
var theme = require("../../../../red/api/editor/theme");
|
||||||
|
|
||||||
describe("info api", function() {
|
describe("api/admin/info", function() {
|
||||||
describe("settings handler", function() {
|
describe("settings handler", function() {
|
||||||
before(function() {
|
before(function() {
|
||||||
sinon.stub(theme,"settings",function() { return { test: 456 };});
|
sinon.stub(theme,"settings",function() { return { test: 456 };});
|
||||||
|
@ -24,7 +24,7 @@ var when = require('when');
|
|||||||
var nodes = require("../../../../red/api/admin/nodes");
|
var nodes = require("../../../../red/api/admin/nodes");
|
||||||
var apiUtil = require("../../../../red/api/util");
|
var apiUtil = require("../../../../red/api/util");
|
||||||
|
|
||||||
describe("nodes api", function() {
|
describe("api/admin/nodes", function() {
|
||||||
|
|
||||||
var app;
|
var app;
|
||||||
function initNodes(runtime) {
|
function initNodes(runtime) {
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
var should = require("should");
|
var should = require("should");
|
||||||
var Clients = require("../../../../red/api/auth/clients");
|
var Clients = require("../../../../red/api/auth/clients");
|
||||||
|
|
||||||
describe("Clients", function() {
|
describe("api/auth/clients", function() {
|
||||||
it('finds the known editor client',function(done) {
|
it('finds the known editor client',function(done) {
|
||||||
Clients.get("node-red-editor").then(function(client) {
|
Clients.get("node-red-editor").then(function(client) {
|
||||||
client.should.have.property("id","node-red-editor");
|
client.should.have.property("id","node-red-editor");
|
||||||
@ -44,4 +44,3 @@ describe("Clients", function() {
|
|||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
@ -24,7 +24,7 @@ var auth = require("../../../../red/api/auth");
|
|||||||
var Users = require("../../../../red/api/auth/users");
|
var Users = require("../../../../red/api/auth/users");
|
||||||
var Tokens = require("../../../../red/api/auth/tokens");
|
var Tokens = require("../../../../red/api/auth/tokens");
|
||||||
|
|
||||||
describe("api auth middleware",function() {
|
describe("api/auth/index",function() {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
var should = require("should");
|
var should = require("should");
|
||||||
var permissions = require("../../../../red/api/auth/permissions");
|
var permissions = require("../../../../red/api/auth/permissions");
|
||||||
|
|
||||||
describe("Auth permissions", function() {
|
describe("api/auth/permissions", function() {
|
||||||
describe("hasPermission", function() {
|
describe("hasPermission", function() {
|
||||||
it('a user with no permissions',function() {
|
it('a user with no permissions',function() {
|
||||||
permissions.hasPermission([],"*").should.be.false();
|
permissions.hasPermission([],"*").should.be.false();
|
||||||
|
@ -23,7 +23,7 @@ var Users = require("../../../../red/api/auth/users");
|
|||||||
var Tokens = require("../../../../red/api/auth/tokens");
|
var Tokens = require("../../../../red/api/auth/tokens");
|
||||||
var Clients = require("../../../../red/api/auth/clients");
|
var Clients = require("../../../../red/api/auth/clients");
|
||||||
|
|
||||||
describe("Auth strategies", function() {
|
describe("api/auth/strategies", function() {
|
||||||
before(function() {
|
before(function() {
|
||||||
strategies.init({log:{audit:function(){}}})
|
strategies.init({log:{audit:function(){}}})
|
||||||
});
|
});
|
||||||
|
@ -21,7 +21,7 @@ var sinon = require("sinon");
|
|||||||
var Tokens = require("../../../../red/api/auth/tokens");
|
var Tokens = require("../../../../red/api/auth/tokens");
|
||||||
|
|
||||||
|
|
||||||
describe("Tokens", function() {
|
describe("api/auth/tokens", function() {
|
||||||
describe("#init",function() {
|
describe("#init",function() {
|
||||||
it('loads sessions', function(done) {
|
it('loads sessions', function(done) {
|
||||||
Tokens.init({}).then(done);
|
Tokens.init({}).then(done);
|
||||||
|
@ -20,7 +20,7 @@ var sinon = require('sinon');
|
|||||||
|
|
||||||
var Users = require("../../../../red/api/auth/users");
|
var Users = require("../../../../red/api/auth/users");
|
||||||
|
|
||||||
describe("Users", function() {
|
describe("api/auth/users", function() {
|
||||||
describe('Initalised with a credentials object, no anon',function() {
|
describe('Initalised with a credentials object, no anon',function() {
|
||||||
before(function() {
|
before(function() {
|
||||||
Users.init({
|
Users.init({
|
||||||
|
19
test/red/api/editor/projects/index_spec.js
Normal file
19
test/red/api/editor/projects/index_spec.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
**/
|
||||||
|
|
||||||
|
describe("api/editor/projects", function() {
|
||||||
|
it.skip("NEEDS TESTS WRITING",function() {});
|
||||||
|
});
|
@ -424,8 +424,11 @@ describe('red/runtime/nodes/credentials', function() {
|
|||||||
var cryptedFlows = {"$":"5b89d8209b5158a3c313675561b1a5b5phN1gDBe81Zv98KqS/hVDmc9EKvaKqRIvcyXYvBlFNzzzJtvN7qfw06i"};
|
var cryptedFlows = {"$":"5b89d8209b5158a3c313675561b1a5b5phN1gDBe81Zv98KqS/hVDmc9EKvaKqRIvcyXYvBlFNzzzJtvN7qfw06i"};
|
||||||
credentials.init(runtime);
|
credentials.init(runtime);
|
||||||
credentials.load(cryptedFlows).then(function() {
|
credentials.load(cryptedFlows).then(function() {
|
||||||
credentials.dirty().should.be.true();
|
// credentials.dirty().should.be.true();
|
||||||
should.not.exist(credentials.get("node"));
|
// should.not.exist(credentials.get("node"));
|
||||||
|
done();
|
||||||
|
}).otherwise(function(err) {
|
||||||
|
err.should.have.property('code','credentials_load_failed');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -437,8 +440,11 @@ describe('red/runtime/nodes/credentials', function() {
|
|||||||
var cryptedFlows = {"$":"5b89d8209b5158a3c313675561b1a5b5phN1gDBe81Zv98KqS/hVDmc9EKvaKqRIvcyXYvBlFNzzzJtvN7qfw06i"};
|
var cryptedFlows = {"$":"5b89d8209b5158a3c313675561b1a5b5phN1gDBe81Zv98KqS/hVDmc9EKvaKqRIvcyXYvBlFNzzzJtvN7qfw06i"};
|
||||||
credentials.init(runtime);
|
credentials.init(runtime);
|
||||||
credentials.load(cryptedFlows).then(function() {
|
credentials.load(cryptedFlows).then(function() {
|
||||||
credentials.dirty().should.be.true();
|
// credentials.dirty().should.be.true();
|
||||||
should.not.exist(credentials.get("node"));
|
// should.not.exist(credentials.get("node"));
|
||||||
|
done();
|
||||||
|
}).otherwise(function(err) {
|
||||||
|
err.should.have.property('code','credentials_load_failed');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -21,6 +21,12 @@ var path = require('path');
|
|||||||
var localfilesystem = require("../../../../../red/runtime/storage/localfilesystem");
|
var localfilesystem = require("../../../../../red/runtime/storage/localfilesystem");
|
||||||
|
|
||||||
describe('storage/localfilesystem', function() {
|
describe('storage/localfilesystem', function() {
|
||||||
|
var mockRuntime = {
|
||||||
|
log:{
|
||||||
|
_:function() { return "placeholder message"},
|
||||||
|
info: function() { }
|
||||||
|
}
|
||||||
|
};
|
||||||
var userDir = path.join(__dirname,".testUserHome");
|
var userDir = path.join(__dirname,".testUserHome");
|
||||||
var testFlow = [{"type":"tab","id":"d8be2a6d.2741d8","label":"Sheet 1"}];
|
var testFlow = [{"type":"tab","id":"d8be2a6d.2741d8","label":"Sheet 1"}];
|
||||||
beforeEach(function(done) {
|
beforeEach(function(done) {
|
||||||
@ -33,7 +39,7 @@ describe('storage/localfilesystem', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should initialise the user directory',function(done) {
|
it('should initialise the user directory',function(done) {
|
||||||
localfilesystem.init({userDir:userDir}).then(function() {
|
localfilesystem.init({userDir:userDir}, mockRuntime).then(function() {
|
||||||
fs.existsSync(path.join(userDir,"lib")).should.be.true();
|
fs.existsSync(path.join(userDir,"lib")).should.be.true();
|
||||||
fs.existsSync(path.join(userDir,"lib",'flows')).should.be.true();
|
fs.existsSync(path.join(userDir,"lib",'flows')).should.be.true();
|
||||||
done();
|
done();
|
||||||
@ -49,7 +55,7 @@ describe('storage/localfilesystem', function() {
|
|||||||
fs.mkdirSync(process.env.NODE_RED_HOME);
|
fs.mkdirSync(process.env.NODE_RED_HOME);
|
||||||
fs.writeFileSync(path.join(process.env.NODE_RED_HOME,".config.json"),"{}","utf8");
|
fs.writeFileSync(path.join(process.env.NODE_RED_HOME,".config.json"),"{}","utf8");
|
||||||
var settings = {};
|
var settings = {};
|
||||||
localfilesystem.init(settings).then(function() {
|
localfilesystem.init(settings, mockRuntime).then(function() {
|
||||||
try {
|
try {
|
||||||
fs.existsSync(path.join(process.env.NODE_RED_HOME,"lib")).should.be.true();
|
fs.existsSync(path.join(process.env.NODE_RED_HOME,"lib")).should.be.true();
|
||||||
fs.existsSync(path.join(process.env.NODE_RED_HOME,"lib",'flows')).should.be.true();
|
fs.existsSync(path.join(process.env.NODE_RED_HOME,"lib",'flows')).should.be.true();
|
||||||
@ -74,7 +80,7 @@ describe('storage/localfilesystem', function() {
|
|||||||
fs.mkdirSync(path.join(process.env.HOMEPATH,".node-red"));
|
fs.mkdirSync(path.join(process.env.HOMEPATH,".node-red"));
|
||||||
fs.writeFileSync(path.join(process.env.HOMEPATH,".node-red",".config.json"),"{}","utf8");
|
fs.writeFileSync(path.join(process.env.HOMEPATH,".node-red",".config.json"),"{}","utf8");
|
||||||
var settings = {};
|
var settings = {};
|
||||||
localfilesystem.init(settings).then(function() {
|
localfilesystem.init(settings, mockRuntime).then(function() {
|
||||||
try {
|
try {
|
||||||
fs.existsSync(path.join(process.env.HOMEPATH,".node-red","lib")).should.be.true();
|
fs.existsSync(path.join(process.env.HOMEPATH,".node-red","lib")).should.be.true();
|
||||||
fs.existsSync(path.join(process.env.HOMEPATH,".node-red","lib",'flows')).should.be.true();
|
fs.existsSync(path.join(process.env.HOMEPATH,".node-red","lib",'flows')).should.be.true();
|
||||||
@ -101,7 +107,7 @@ describe('storage/localfilesystem', function() {
|
|||||||
|
|
||||||
fs.mkdirSync(process.env.HOME);
|
fs.mkdirSync(process.env.HOME);
|
||||||
var settings = {};
|
var settings = {};
|
||||||
localfilesystem.init(settings).then(function() {
|
localfilesystem.init(settings, mockRuntime).then(function() {
|
||||||
try {
|
try {
|
||||||
fs.existsSync(path.join(process.env.HOME,".node-red","lib")).should.be.true();
|
fs.existsSync(path.join(process.env.HOME,".node-red","lib")).should.be.true();
|
||||||
fs.existsSync(path.join(process.env.HOME,".node-red","lib",'flows')).should.be.true();
|
fs.existsSync(path.join(process.env.HOME,".node-red","lib",'flows')).should.be.true();
|
||||||
@ -131,7 +137,7 @@ describe('storage/localfilesystem', function() {
|
|||||||
|
|
||||||
fs.mkdirSync(process.env.USERPROFILE);
|
fs.mkdirSync(process.env.USERPROFILE);
|
||||||
var settings = {};
|
var settings = {};
|
||||||
localfilesystem.init(settings).then(function() {
|
localfilesystem.init(settings, mockRuntime).then(function() {
|
||||||
try {
|
try {
|
||||||
fs.existsSync(path.join(process.env.USERPROFILE,".node-red","lib")).should.be.true();
|
fs.existsSync(path.join(process.env.USERPROFILE,".node-red","lib")).should.be.true();
|
||||||
fs.existsSync(path.join(process.env.USERPROFILE,".node-red","lib",'flows')).should.be.true();
|
fs.existsSync(path.join(process.env.USERPROFILE,".node-red","lib",'flows')).should.be.true();
|
||||||
@ -151,7 +157,7 @@ describe('storage/localfilesystem', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should handle missing flow file',function(done) {
|
it('should handle missing flow file',function(done) {
|
||||||
localfilesystem.init({userDir:userDir}).then(function() {
|
localfilesystem.init({userDir:userDir}, mockRuntime).then(function() {
|
||||||
var flowFile = 'flows_'+require('os').hostname()+'.json';
|
var flowFile = 'flows_'+require('os').hostname()+'.json';
|
||||||
var flowFilePath = path.join(userDir,flowFile);
|
var flowFilePath = path.join(userDir,flowFile);
|
||||||
fs.existsSync(flowFilePath).should.be.false();
|
fs.existsSync(flowFilePath).should.be.false();
|
||||||
@ -167,7 +173,7 @@ describe('storage/localfilesystem', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should handle empty flow file, no backup',function(done) {
|
it('should handle empty flow file, no backup',function(done) {
|
||||||
localfilesystem.init({userDir:userDir}).then(function() {
|
localfilesystem.init({userDir:userDir}, mockRuntime).then(function() {
|
||||||
var flowFile = 'flows_'+require('os').hostname()+'.json';
|
var flowFile = 'flows_'+require('os').hostname()+'.json';
|
||||||
var flowFilePath = path.join(userDir,flowFile);
|
var flowFilePath = path.join(userDir,flowFile);
|
||||||
var flowFileBackupPath = path.join(userDir,"."+flowFile+".backup");
|
var flowFileBackupPath = path.join(userDir,"."+flowFile+".backup");
|
||||||
@ -185,7 +191,7 @@ describe('storage/localfilesystem', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should handle empty flow file, restores backup',function(done) {
|
it('should handle empty flow file, restores backup',function(done) {
|
||||||
localfilesystem.init({userDir:userDir}).then(function() {
|
localfilesystem.init({userDir:userDir}, mockRuntime).then(function() {
|
||||||
var flowFile = 'flows_'+require('os').hostname()+'.json';
|
var flowFile = 'flows_'+require('os').hostname()+'.json';
|
||||||
var flowFilePath = path.join(userDir,flowFile);
|
var flowFilePath = path.join(userDir,flowFile);
|
||||||
var flowFileBackupPath = path.join(userDir,"."+flowFile+".backup");
|
var flowFileBackupPath = path.join(userDir,"."+flowFile+".backup");
|
||||||
@ -208,7 +214,7 @@ describe('storage/localfilesystem', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should save flows to the default file',function(done) {
|
it('should save flows to the default file',function(done) {
|
||||||
localfilesystem.init({userDir:userDir}).then(function() {
|
localfilesystem.init({userDir:userDir}, mockRuntime).then(function() {
|
||||||
var flowFile = 'flows_'+require('os').hostname()+'.json';
|
var flowFile = 'flows_'+require('os').hostname()+'.json';
|
||||||
var flowFilePath = path.join(userDir,flowFile);
|
var flowFilePath = path.join(userDir,flowFile);
|
||||||
var flowFileBackupPath = path.join(userDir,"."+flowFile+".backup");
|
var flowFileBackupPath = path.join(userDir,"."+flowFile+".backup");
|
||||||
@ -237,7 +243,7 @@ describe('storage/localfilesystem', function() {
|
|||||||
var flowFile = 'test.json';
|
var flowFile = 'test.json';
|
||||||
var flowFilePath = path.join(userDir,flowFile);
|
var flowFilePath = path.join(userDir,flowFile);
|
||||||
|
|
||||||
localfilesystem.init({userDir:userDir, flowFile:flowFilePath}).then(function() {
|
localfilesystem.init({userDir:userDir, flowFile:flowFilePath}, mockRuntime).then(function() {
|
||||||
fs.existsSync(defaultFlowFilePath).should.be.false();
|
fs.existsSync(defaultFlowFilePath).should.be.false();
|
||||||
fs.existsSync(flowFilePath).should.be.false();
|
fs.existsSync(flowFilePath).should.be.false();
|
||||||
|
|
||||||
@ -261,7 +267,7 @@ describe('storage/localfilesystem', function() {
|
|||||||
it('should format the flows file when flowFilePretty specified',function(done) {
|
it('should format the flows file when flowFilePretty specified',function(done) {
|
||||||
var flowFile = 'test.json';
|
var flowFile = 'test.json';
|
||||||
var flowFilePath = path.join(userDir,flowFile);
|
var flowFilePath = path.join(userDir,flowFile);
|
||||||
localfilesystem.init({userDir:userDir, flowFile:flowFilePath,flowFilePretty:true}).then(function() {
|
localfilesystem.init({userDir:userDir, flowFile:flowFilePath,flowFilePretty:true}, mockRuntime).then(function() {
|
||||||
localfilesystem.saveFlows(testFlow).then(function() {
|
localfilesystem.saveFlows(testFlow).then(function() {
|
||||||
var content = fs.readFileSync(flowFilePath,"utf8");
|
var content = fs.readFileSync(flowFilePath,"utf8");
|
||||||
content.split("\n").length.should.be.above(1);
|
content.split("\n").length.should.be.above(1);
|
||||||
@ -286,7 +292,7 @@ describe('storage/localfilesystem', function() {
|
|||||||
var flowFilePath = path.join(userDir,flowFile);
|
var flowFilePath = path.join(userDir,flowFile);
|
||||||
var flowFileBackupPath = path.join(userDir,"."+flowFile+".backup");
|
var flowFileBackupPath = path.join(userDir,"."+flowFile+".backup");
|
||||||
|
|
||||||
localfilesystem.init({userDir:userDir, flowFile:flowFilePath}).then(function() {
|
localfilesystem.init({userDir:userDir, flowFile:flowFilePath}, mockRuntime).then(function() {
|
||||||
fs.existsSync(defaultFlowFilePath).should.be.false();
|
fs.existsSync(defaultFlowFilePath).should.be.false();
|
||||||
fs.existsSync(flowFilePath).should.be.false();
|
fs.existsSync(flowFilePath).should.be.false();
|
||||||
fs.existsSync(flowFileBackupPath).should.be.false();
|
fs.existsSync(flowFileBackupPath).should.be.false();
|
||||||
@ -326,7 +332,7 @@ describe('storage/localfilesystem', function() {
|
|||||||
var flowFile = 'test.json';
|
var flowFile = 'test.json';
|
||||||
var flowFilePath = path.join(userDir,flowFile);
|
var flowFilePath = path.join(userDir,flowFile);
|
||||||
var credFile = path.join(userDir,"test_cred.json");
|
var credFile = path.join(userDir,"test_cred.json");
|
||||||
localfilesystem.init({userDir:userDir, flowFile:flowFilePath}).then(function() {
|
localfilesystem.init({userDir:userDir, flowFile:flowFilePath}, mockRuntime).then(function() {
|
||||||
fs.existsSync(credFile).should.be.false();
|
fs.existsSync(credFile).should.be.false();
|
||||||
|
|
||||||
localfilesystem.getCredentials().then(function(creds) {
|
localfilesystem.getCredentials().then(function(creds) {
|
||||||
@ -345,7 +351,7 @@ describe('storage/localfilesystem', function() {
|
|||||||
var flowFilePath = path.join(userDir,flowFile);
|
var flowFilePath = path.join(userDir,flowFile);
|
||||||
var credFile = path.join(userDir,"test_cred.json");
|
var credFile = path.join(userDir,"test_cred.json");
|
||||||
|
|
||||||
localfilesystem.init({userDir:userDir, flowFile:flowFilePath}).then(function() {
|
localfilesystem.init({userDir:userDir, flowFile:flowFilePath}, mockRuntime).then(function() {
|
||||||
|
|
||||||
fs.existsSync(credFile).should.be.false();
|
fs.existsSync(credFile).should.be.false();
|
||||||
|
|
||||||
@ -374,7 +380,7 @@ describe('storage/localfilesystem', function() {
|
|||||||
var credFile = path.join(userDir,"test_cred.json");
|
var credFile = path.join(userDir,"test_cred.json");
|
||||||
var credFileBackup = path.join(userDir,".test_cred.json.backup");
|
var credFileBackup = path.join(userDir,".test_cred.json.backup");
|
||||||
|
|
||||||
localfilesystem.init({userDir:userDir, flowFile:flowFilePath}).then(function() {
|
localfilesystem.init({userDir:userDir, flowFile:flowFilePath}, mockRuntime).then(function() {
|
||||||
|
|
||||||
fs.writeFileSync(credFile,"{}","utf8");
|
fs.writeFileSync(credFile,"{}","utf8");
|
||||||
|
|
||||||
@ -400,7 +406,7 @@ describe('storage/localfilesystem', function() {
|
|||||||
var flowFilePath = path.join(userDir,flowFile);
|
var flowFilePath = path.join(userDir,flowFile);
|
||||||
var credFile = path.join(userDir,"test_cred.json");
|
var credFile = path.join(userDir,"test_cred.json");
|
||||||
|
|
||||||
localfilesystem.init({userDir:userDir, flowFile:flowFilePath, flowFilePretty:true}).then(function() {
|
localfilesystem.init({userDir:userDir, flowFile:flowFilePath, flowFilePretty:true}, mockRuntime).then(function() {
|
||||||
|
|
||||||
fs.existsSync(credFile).should.be.false();
|
fs.existsSync(credFile).should.be.false();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user