mirror of
				https://github.com/node-red/node-red.git
				synced 2025-03-01 10:36:34 +00:00 
			
		
		
		
	Allow a project's flow file to be changed
This commit is contained in:
		@@ -69,10 +69,15 @@
 | 
				
			|||||||
                            return;
 | 
					                            return;
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        if (msg.text) {
 | 
					                        if (msg.text) {
 | 
				
			||||||
 | 
					                            console.log(msg);
 | 
				
			||||||
                            var text = RED._(msg.text,{default:msg.text});
 | 
					                            var text = RED._(msg.text,{default:msg.text});
 | 
				
			||||||
                            if (notificationId === "runtime-state") {
 | 
					                            if (notificationId === "runtime-state") {
 | 
				
			||||||
                                if (msg.error === "credentials_load_failed") {
 | 
					                                if (msg.error === "credentials_load_failed") {
 | 
				
			||||||
 | 
					                                    // TODO: NLS
 | 
				
			||||||
                                    text += '<p><a href="#" onclick="RED.projects.showCredentialsPrompt(); return false;">'+'Setup credentials'+'</a></p>';
 | 
					                                    text += '<p><a href="#" onclick="RED.projects.showCredentialsPrompt(); return false;">'+'Setup credentials'+'</a></p>';
 | 
				
			||||||
 | 
					                                } else if (msg.error === "missing_flow_file") {
 | 
				
			||||||
 | 
					                                    // TODO: NLS
 | 
				
			||||||
 | 
					                                    text += '<p><a href="#" onclick="RED.projects.showFilesPrompt(); return false;">'+'Setup project files'+'</a></p>';
 | 
				
			||||||
                                }
 | 
					                                }
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                            if (!persistentNotifications.hasOwnProperty(notificationId)) {
 | 
					                            if (!persistentNotifications.hasOwnProperty(notificationId)) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -33,6 +33,7 @@ RED.popover = (function() {
 | 
				
			|||||||
        var trigger = options.trigger;
 | 
					        var trigger = options.trigger;
 | 
				
			||||||
        var content = options.content;
 | 
					        var content = options.content;
 | 
				
			||||||
        var delay = options.delay;
 | 
					        var delay = options.delay;
 | 
				
			||||||
 | 
					        var autoClose = options.autoClose;
 | 
				
			||||||
        var width = options.width||"auto";
 | 
					        var width = options.width||"auto";
 | 
				
			||||||
        var size = options.size||"default";
 | 
					        var size = options.size||"default";
 | 
				
			||||||
        if (!deltaSizes[size]) {
 | 
					        if (!deltaSizes[size]) {
 | 
				
			||||||
@@ -92,7 +93,6 @@ RED.popover = (function() {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (trigger === 'hover') {
 | 
					        if (trigger === 'hover') {
 | 
				
			||||||
 | 
					 | 
				
			||||||
            target.on('mouseenter',function(e) {
 | 
					            target.on('mouseenter',function(e) {
 | 
				
			||||||
                clearTimeout(timer);
 | 
					                clearTimeout(timer);
 | 
				
			||||||
                active = true;
 | 
					                active = true;
 | 
				
			||||||
@@ -116,6 +116,11 @@ RED.popover = (function() {
 | 
				
			|||||||
                    openPopup();
 | 
					                    openPopup();
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					        } else if (autoClose) {
 | 
				
			||||||
 | 
					            setTimeout(function() {
 | 
				
			||||||
 | 
					                active = false;
 | 
				
			||||||
 | 
					                closePopup();
 | 
				
			||||||
 | 
					            },autoClose);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        var res = {
 | 
					        var res = {
 | 
				
			||||||
            setContent: function(_content) {
 | 
					            setContent: function(_content) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -170,7 +170,7 @@ RED.projects.settings = (function() {
 | 
				
			|||||||
                updateProjectSummary(activeProject.summary, container);
 | 
					                updateProjectSummary(activeProject.summary, container);
 | 
				
			||||||
                editButton.show();
 | 
					                editButton.show();
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
        $('<button class="editor-button">Done</button>')
 | 
					        $('<button class="editor-button">Save</button>')
 | 
				
			||||||
            .appendTo(bg)
 | 
					            .appendTo(bg)
 | 
				
			||||||
            .click(function(evt) {
 | 
					            .click(function(evt) {
 | 
				
			||||||
                evt.preventDefault();
 | 
					                evt.preventDefault();
 | 
				
			||||||
@@ -422,211 +422,517 @@ RED.projects.settings = (function() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    function createSettingsPane(activeProject) {
 | 
					    function showProjectFileListing(row,activeProject,current,done) {
 | 
				
			||||||
        var pane = $('<div id="project-settings-tab-settings" class="project-settings-tab-pane node-help"></div>');
 | 
					        var dialog;
 | 
				
			||||||
        $('<h3></h3>').text("Credentials").appendTo(pane);
 | 
					        var dialogBody;
 | 
				
			||||||
        var row = $('<div class="user-settings-row"></div>').appendTo(pane);
 | 
					        var filesList;
 | 
				
			||||||
        if (activeProject.settings.credentialsEncrypted) {
 | 
					        var selected;
 | 
				
			||||||
            $('<span style="margin-right: 20px;"><i class="fa fa-lock"></i> Credentials are encrypted</span>').appendTo(row);
 | 
					        var container = $('<div class="project-file-listing-container"></div>',{style:"position: relative; min-height: 175px; height: 175px;"}).appendTo(row);
 | 
				
			||||||
        } else {
 | 
					        var spinner = addSpinnerOverlay(container);
 | 
				
			||||||
            $('<span style="margin-right: 20px;"><i class="fa fa-unlock"></i> Credentials are not encrypted</span>').appendTo(row);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        var resetButton;
 | 
					 | 
				
			||||||
        var action;
 | 
					 | 
				
			||||||
        var changeButton = $('<button id="" class="editor-button"></button>')
 | 
					 | 
				
			||||||
            .text(activeProject.settings.credentialsEncrypted?"Change key":"Enable encryption")
 | 
					 | 
				
			||||||
            .appendTo(row)
 | 
					 | 
				
			||||||
            .click(function(evt) {
 | 
					 | 
				
			||||||
                evt.preventDefault();
 | 
					 | 
				
			||||||
                newKey.val("");
 | 
					 | 
				
			||||||
                if (currentKey) {
 | 
					 | 
				
			||||||
                    currentKey.val("");
 | 
					 | 
				
			||||||
                    currentKey.removeClass("input-error");
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                checkInputs();
 | 
					 | 
				
			||||||
                saveButton.text("Save");
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                $(".project-settings-credentials-row").show();
 | 
					        $.getJSON("/projects/"+activeProject.name+"/files",function(result) {
 | 
				
			||||||
                $(".project-settings-credentials-current-row").show();
 | 
					            var fileNames = Object.keys(result);
 | 
				
			||||||
                $(this).prop('disabled',true);
 | 
					            var files = {};
 | 
				
			||||||
                if (resetButton) {
 | 
					            fileNames.sort();
 | 
				
			||||||
                    resetButton.prop('disabled',true);
 | 
					            fileNames.forEach(function(file) {
 | 
				
			||||||
                }
 | 
					                file.split("/").reduce(function(r,v,i,arr) { if (v) { if (i<arr.length-1) { r[v] = r[v]||{};} else { r[v] = true }return r[v];}},files);
 | 
				
			||||||
                action = 'change';
 | 
					 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
        if (activeProject.settings.credentialsEncrypted) {
 | 
					            var sortFiles = function(key,value,fullPath) {
 | 
				
			||||||
            resetButton = $('<button id="" style="margin-left: 10px;" class="editor-button"></button>')
 | 
					                var result = {
 | 
				
			||||||
                .text("Reset key")
 | 
					                    name: key||"/",
 | 
				
			||||||
                .appendTo(row)
 | 
					                    path: fullPath+(fullPath?"/":"")+key,
 | 
				
			||||||
                .click(function(evt) {
 | 
					 | 
				
			||||||
                    evt.preventDefault();
 | 
					 | 
				
			||||||
                    newKey.val("");
 | 
					 | 
				
			||||||
                    if (currentKey) {
 | 
					 | 
				
			||||||
                        currentKey.val("");
 | 
					 | 
				
			||||||
                        currentKey.removeClass("input-error");
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    checkInputs();
 | 
					 | 
				
			||||||
                    saveButton.text("Reset key");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    $(".project-settings-credentials-row").show();
 | 
					 | 
				
			||||||
                    $(".project-settings-credentials-reset-row").show();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    $(this).prop('disabled',true);
 | 
					 | 
				
			||||||
                    changeButton.prop('disabled',true);
 | 
					 | 
				
			||||||
                    action = 'reset';
 | 
					 | 
				
			||||||
                });
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (activeProject.settings.credentialSecretInvalid) {
 | 
					 | 
				
			||||||
            row = $('<div class="user-settings-row"></div>').appendTo(pane);
 | 
					 | 
				
			||||||
            $('<div class="form-tips form-warning"><i class="fa fa-warning"></i> The current key is not valid. Set the correct key or reset credentials.</div>').appendTo(row);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        var credentialsContainer = $('<div>',{style:"position:relative"}).appendTo(pane);
 | 
					 | 
				
			||||||
        var currentKey;
 | 
					 | 
				
			||||||
        var newKey;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        var checkInputs = function() {
 | 
					 | 
				
			||||||
            var valid = true;
 | 
					 | 
				
			||||||
            if (newKey.val().length === 0) {
 | 
					 | 
				
			||||||
                valid = false;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            if (currentKey && currentKey.val() === 0) {
 | 
					 | 
				
			||||||
                valid = false;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            saveButton.toggleClass('disabled',!valid);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (activeProject.settings.credentialsEncrypted) {
 | 
					 | 
				
			||||||
            if  (!activeProject.settings.credentialSecretInvalid) {
 | 
					 | 
				
			||||||
                row = $('<div class="user-settings-row project-settings-credentials-current-row hide"></div>').appendTo(credentialsContainer);
 | 
					 | 
				
			||||||
                $('<label for="">Current key</label>').appendTo(row);
 | 
					 | 
				
			||||||
                currentKey = $('<input type="password">').appendTo(row);
 | 
					 | 
				
			||||||
                currentKey.on("change keyup paste",function() {
 | 
					 | 
				
			||||||
                    if (popover) {
 | 
					 | 
				
			||||||
                        popover.close();
 | 
					 | 
				
			||||||
                        popover = null;
 | 
					 | 
				
			||||||
                        $(this).removeClass('input-error');
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    checkInputs();
 | 
					 | 
				
			||||||
                });
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            row = $('<div class="user-settings-row project-settings-credentials-reset-row hide"></div>').appendTo(credentialsContainer);
 | 
					 | 
				
			||||||
            $('<div class="form-tips form-warning"><i class="fa fa-warning"></i> Resetting the key will delete all existing credentials</div>').appendTo(row);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        // $('<label for="" style="margin-left:20px; width: auto;"><input type="radio" name="project-settings-credentials-current" value="lost"> Forgotten key?</label>').appendTo(row);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        row = $('<div class="user-settings-row project-settings-credentials-row hide"></div>').appendTo(credentialsContainer);
 | 
					 | 
				
			||||||
        $('<label for=""></label>').text((activeProject.settings.credentialsEncrypted&& !activeProject.settings.credentialSecretInvalid)?"New key":"Encryption key").appendTo(row);
 | 
					 | 
				
			||||||
        newKey = $('<input type="password">').appendTo(row).on("change keyup paste",checkInputs);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        row = $('<div class="user-settings-row project-settings-credentials-row hide"></div>').appendTo(credentialsContainer);
 | 
					 | 
				
			||||||
        var bg = $('<div class="button-group" style="text-align: right; margin-right:20px;"></div>').appendTo(row);
 | 
					 | 
				
			||||||
        $('<button class="editor-button">Cancel</button>')
 | 
					 | 
				
			||||||
            .appendTo(bg)
 | 
					 | 
				
			||||||
            .click(function(evt) {
 | 
					 | 
				
			||||||
                evt.preventDefault();
 | 
					 | 
				
			||||||
                if (popover) {
 | 
					 | 
				
			||||||
                    popover.close();
 | 
					 | 
				
			||||||
                    popover = null;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                changeButton.prop('disabled',false);
 | 
					 | 
				
			||||||
                if (resetButton) {
 | 
					 | 
				
			||||||
                    resetButton.prop('disabled',false);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                $(".project-settings-credentials-row").hide();
 | 
					 | 
				
			||||||
                $(".project-settings-credentials-current-row").hide();
 | 
					 | 
				
			||||||
                $(".project-settings-credentials-reset-row").hide();
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        var saveButton = $('<button class="editor-button primary disabled"></button>')
 | 
					 | 
				
			||||||
            .text("Save")
 | 
					 | 
				
			||||||
            .appendTo(bg)
 | 
					 | 
				
			||||||
            .click(function(evt) {
 | 
					 | 
				
			||||||
                evt.preventDefault();
 | 
					 | 
				
			||||||
                if ($(this).hasClass('disabled')) {
 | 
					 | 
				
			||||||
                    return;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                var spinner = addSpinnerOverlay(credentialsContainer);
 | 
					 | 
				
			||||||
                var payload = {
 | 
					 | 
				
			||||||
                    credentialSecret: newKey.val()
 | 
					 | 
				
			||||||
                };
 | 
					                };
 | 
				
			||||||
                if (activeProject.settings.credentialSecretInvalid) {
 | 
					                if (value === true) {
 | 
				
			||||||
                    RED.deploy.setDeployInflight(true);
 | 
					                    result.type = 'f';
 | 
				
			||||||
 | 
					                    return result;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                result.type = 'd';
 | 
				
			||||||
 | 
					                result.children = [];
 | 
				
			||||||
 | 
					                result.path = result.path;
 | 
				
			||||||
 | 
					                var files = Object.keys(value);
 | 
				
			||||||
 | 
					                files.forEach(function(file) {
 | 
				
			||||||
 | 
					                    result.children.push(sortFiles(file,value[file],result.path));
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                result.children.sort(function(A,B) {
 | 
				
			||||||
 | 
					                    if (A.hasOwnProperty("children") && !B.hasOwnProperty("children")) {
 | 
				
			||||||
 | 
					                        return -1;
 | 
				
			||||||
 | 
					                    } else if (!A.hasOwnProperty("children") && B.hasOwnProperty("children")) {
 | 
				
			||||||
 | 
					                        return 1;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    return A.name.localeCompare(B.name);
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                return result;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            var files = sortFiles("",files,"");
 | 
				
			||||||
 | 
					            createFileSubList(container,files.children,current,done,"height: 175px");
 | 
				
			||||||
 | 
					            spinner.remove();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    function createFileSubList(container, files, current, onselect, style) {
 | 
				
			||||||
 | 
					        style = style || "";
 | 
				
			||||||
 | 
					        var list = $('<ol>',{class:"projects-dialog-file-list", style:style}).appendTo(container).editableList({
 | 
				
			||||||
 | 
					            addButton: false,
 | 
				
			||||||
 | 
					            scrollOnAdd: false,
 | 
				
			||||||
 | 
					            addItem: function(row,index,entry) {
 | 
				
			||||||
 | 
					                var header = $('<div></div>',{class:"projects-dialog-file-list-entry"}).appendTo(row);
 | 
				
			||||||
 | 
					                if (entry.children) {
 | 
				
			||||||
 | 
					                    $('<span class="projects-dialog-file-list-entry-folder"><i class="fa fa-angle-right"></i> <i class="fa fa-folder-o"></i></span>').appendTo(header);
 | 
				
			||||||
 | 
					                    if (entry.children.length > 0) {
 | 
				
			||||||
 | 
					                        var children = $('<div></div>',{style:"padding-left: 20px;"}).appendTo(row);
 | 
				
			||||||
 | 
					                        if (current.indexOf(entry.path+"/") === 0) {
 | 
				
			||||||
 | 
					                            header.addClass("expanded");
 | 
				
			||||||
 | 
					                        } else {
 | 
				
			||||||
 | 
					                            children.hide();
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        createFileSubList(children,entry.children,current,onselect);
 | 
				
			||||||
 | 
					                        header.addClass("selectable");
 | 
				
			||||||
 | 
					                        header.click(function(e) {
 | 
				
			||||||
 | 
					                            if ($(this).hasClass("expanded")) {
 | 
				
			||||||
 | 
					                                $(this).removeClass("expanded");
 | 
				
			||||||
 | 
					                                children.slideUp(200);
 | 
				
			||||||
 | 
					                            } else {
 | 
				
			||||||
 | 
					                                $(this).addClass("expanded");
 | 
				
			||||||
 | 
					                                children.slideDown(200);
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (activeProject.settings.credentialsEncrypted) {
 | 
					                        });
 | 
				
			||||||
                    if (action === 'reset') {
 | 
					
 | 
				
			||||||
                        payload.resetCredentialSecret = true;
 | 
					 | 
				
			||||||
                    } else if (!activeProject.settings.credentialSecretInvalid) {
 | 
					 | 
				
			||||||
                        payload.currentCredentialSecret = currentKey.val();
 | 
					 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    var fileIcon = "fa-file-o";
 | 
				
			||||||
 | 
					                    var fileClass = "";
 | 
				
			||||||
 | 
					                    if (/\.json$/i.test(entry.name)) {
 | 
				
			||||||
 | 
					                        fileIcon = "fa-file-code-o"
 | 
				
			||||||
 | 
					                    } else if (/\.md$/i.test(entry.name)) {
 | 
				
			||||||
 | 
					                        fileIcon = "fa-book";
 | 
				
			||||||
 | 
					                    } else if (/^\.git/i.test(entry.name)) {
 | 
				
			||||||
 | 
					                        fileIcon = "fa-code-fork";
 | 
				
			||||||
 | 
					                        header.addClass("projects-dialog-file-list-entry-file-type-git");
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                var done = function(err,res) {
 | 
					                    $('<span class="projects-dialog-file-list-entry-file"> <i class="fa '+fileIcon+'"></i></span>').appendTo(header);
 | 
				
			||||||
 | 
					                    header.addClass("selectable");
 | 
				
			||||||
 | 
					                    if (entry.path === current) {
 | 
				
			||||||
 | 
					                        header.addClass("selected");
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    header.click(function(e) {
 | 
				
			||||||
 | 
					                        $(".projects-dialog-file-list-entry.selected").removeClass("selected");
 | 
				
			||||||
 | 
					                        $(this).addClass("selected");
 | 
				
			||||||
 | 
					                        onselect(entry.path);
 | 
				
			||||||
 | 
					                    })
 | 
				
			||||||
 | 
					                    header.dblclick(function(e) {
 | 
				
			||||||
 | 
					                        e.preventDefault();
 | 
				
			||||||
 | 
					                        onselect(entry.path,true);
 | 
				
			||||||
 | 
					                    })
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                $('<span class="projects-dialog-file-list-entry-name" style=""></span>').text(entry.name).appendTo(header);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        if (!style) {
 | 
				
			||||||
 | 
					            list.parent().css("overflow-y","");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        files.forEach(function(f) {
 | 
				
			||||||
 | 
					            list.editableList('addItem',f);
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // function editFiles(activeProject, container,flowFile, flowFileLabel) {
 | 
				
			||||||
 | 
					    //     var editButton = container.children().first();
 | 
				
			||||||
 | 
					    //     editButton.hide();
 | 
				
			||||||
 | 
					    //
 | 
				
			||||||
 | 
					    //     var flowFileInput = $('<input id="" type="text" style="width: calc(100% - 300px);">').val(flowFile).insertAfter(flowFileLabel);
 | 
				
			||||||
 | 
					    //
 | 
				
			||||||
 | 
					    //     var flowFileInputSearch = $('<button class="editor-button" style="margin-left: 10px"><i class="fa fa-folder-open-o"></i></button>')
 | 
				
			||||||
 | 
					    //         .insertAfter(flowFileInput)
 | 
				
			||||||
 | 
					    //         .click(function(e) {
 | 
				
			||||||
 | 
					    //             showProjectFileListing(activeProject,'Select flow file',flowFileInput.val(),function(result) {
 | 
				
			||||||
 | 
					    //                 flowFileInput.val(result);
 | 
				
			||||||
 | 
					    //                 checkFiles();
 | 
				
			||||||
 | 
					    //             })
 | 
				
			||||||
 | 
					    //         })
 | 
				
			||||||
 | 
					    //
 | 
				
			||||||
 | 
					    //     var checkFiles = function() {
 | 
				
			||||||
 | 
					    //         saveButton.toggleClass('disabled',flowFileInput.val()==="");
 | 
				
			||||||
 | 
					    //         saveButton.prop('disabled',flowFileInput.val()==="");
 | 
				
			||||||
 | 
					    //     }
 | 
				
			||||||
 | 
					    //     flowFileInput.on("change keyup paste",checkFiles);
 | 
				
			||||||
 | 
					    //     flowFileLabel.hide();
 | 
				
			||||||
 | 
					    //
 | 
				
			||||||
 | 
					    //     var bg = $('<span class="button-group" style="position: relative; float: right; margin-right:0;"></span>').prependTo(container);
 | 
				
			||||||
 | 
					    //     $('<button class="editor-button">Cancel</button>')
 | 
				
			||||||
 | 
					    //         .appendTo(bg)
 | 
				
			||||||
 | 
					    //         .click(function(evt) {
 | 
				
			||||||
 | 
					    //             evt.preventDefault();
 | 
				
			||||||
 | 
					    //
 | 
				
			||||||
 | 
					    //             flowFileLabel.show();
 | 
				
			||||||
 | 
					    //             flowFileInput.remove();
 | 
				
			||||||
 | 
					    //             flowFileInputSearch.remove();
 | 
				
			||||||
 | 
					    //             bg.remove();
 | 
				
			||||||
 | 
					    //             editButton.show();
 | 
				
			||||||
 | 
					    //         });
 | 
				
			||||||
 | 
					    //     var saveButton = $('<button class="editor-button">Save</button>')
 | 
				
			||||||
 | 
					    //         .appendTo(bg)
 | 
				
			||||||
 | 
					    //         .click(function(evt) {
 | 
				
			||||||
 | 
					    //             evt.preventDefault();
 | 
				
			||||||
 | 
					    //             var newFlowFile = flowFileInput.val();
 | 
				
			||||||
 | 
					    //             var newCredsFile = credentialsFileInput.val();
 | 
				
			||||||
 | 
					    //             var spinner = addSpinnerOverlay(container);
 | 
				
			||||||
 | 
					    //             var done = function(err,res) {
 | 
				
			||||||
 | 
					    //                 if (err) {
 | 
				
			||||||
 | 
					    //                     spinner.remove();
 | 
				
			||||||
 | 
					    //                     return;
 | 
				
			||||||
 | 
					    //                 }
 | 
				
			||||||
 | 
					    //                 activeProject.summary = v;
 | 
				
			||||||
 | 
					    //                 spinner.remove();
 | 
				
			||||||
 | 
					    //                 flowFileLabel.text(newFlowFile);
 | 
				
			||||||
 | 
					    //                 flowFileLabel.show();
 | 
				
			||||||
 | 
					    //                 flowFileInput.remove();
 | 
				
			||||||
 | 
					    //                 flowFileInputSearch.remove();
 | 
				
			||||||
 | 
					    //                 bg.remove();
 | 
				
			||||||
 | 
					    //                 editButton.show();
 | 
				
			||||||
 | 
					    //             }
 | 
				
			||||||
 | 
					    //             // utils.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);
 | 
				
			||||||
 | 
					    //             //             }
 | 
				
			||||||
 | 
					    //             //         },
 | 
				
			||||||
 | 
					    //             //     }
 | 
				
			||||||
 | 
					    //             // },{summary:v});
 | 
				
			||||||
 | 
					    //         });
 | 
				
			||||||
 | 
					    //
 | 
				
			||||||
 | 
					    //
 | 
				
			||||||
 | 
					    //     checkFiles();
 | 
				
			||||||
 | 
					    //
 | 
				
			||||||
 | 
					    // }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function createFilesSection(activeProject,pane) {
 | 
				
			||||||
 | 
					        var title = $('<h3></h3>').text("Files").appendTo(pane);
 | 
				
			||||||
 | 
					        var filesContainer = $('<div class="user-settings-section"></div>').appendTo(pane);
 | 
				
			||||||
 | 
					        var editButton = $('<button class="editor-button editor-button-small" style="float: right;">edit</button>')
 | 
				
			||||||
 | 
					            .appendTo(title)
 | 
				
			||||||
 | 
					            .click(function(evt) {
 | 
				
			||||||
 | 
					                evt.preventDefault();
 | 
				
			||||||
 | 
					                formButtons.show();
 | 
				
			||||||
 | 
					                editButton.hide();
 | 
				
			||||||
 | 
					                flowFileLabelText.hide();
 | 
				
			||||||
 | 
					                flowFileInput.show();
 | 
				
			||||||
 | 
					                flowFileInputSearch.show();
 | 
				
			||||||
 | 
					                credFileLabel.hide();
 | 
				
			||||||
 | 
					                credFileInput.show();
 | 
				
			||||||
 | 
					                flowFileInput.focus();
 | 
				
			||||||
 | 
					                // credentialStateLabel.parent().hide();
 | 
				
			||||||
 | 
					                credentialStateLabel.addClass("uneditable-input");
 | 
				
			||||||
 | 
					                $(".user-settings-row-credentials").show();
 | 
				
			||||||
 | 
					                credentialStateLabel.css('height','auto');
 | 
				
			||||||
 | 
					                credentialFormRows.hide();
 | 
				
			||||||
 | 
					                credentialSecretButtons.show();
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var row;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Flow files
 | 
				
			||||||
 | 
					        row = $('<div class="user-settings-row"></div>').appendTo(filesContainer);
 | 
				
			||||||
 | 
					        $('<label for=""></label>').text('Flow').appendTo(row);
 | 
				
			||||||
 | 
					        var flowFileLabel = $('<div class="uneditable-input" style="padding:0">').appendTo(row);
 | 
				
			||||||
 | 
					        var flowFileLabelText = $('<span style="display:inline-block; padding: 6px">').text(activeProject.files.flow).appendTo(flowFileLabel);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var flowFileInput = $('<input id="" type="text" style="margin-bottom: 0;width: 100%; border: none;">').val(activeProject.files.flow).hide().appendTo(flowFileLabel);
 | 
				
			||||||
 | 
					        var flowFileInputSearch = $('<button class="editor-button" style="width: 36px; height: 36px; position: absolute; top: -1px; right: -1px;"><i class="fa fa-folder-open-o"></i></button>')
 | 
				
			||||||
 | 
					            .hide()
 | 
				
			||||||
 | 
					            .appendTo(flowFileLabel)
 | 
				
			||||||
 | 
					            .click(function(e) {
 | 
				
			||||||
 | 
					                if ($(this).hasClass('selected')) {
 | 
				
			||||||
 | 
					                    $(this).removeClass('selected');
 | 
				
			||||||
 | 
					                    flowFileLabel.find('.project-file-listing-container').remove();
 | 
				
			||||||
 | 
					                    flowFileLabel.css('height','');
 | 
				
			||||||
 | 
					                    flowFileLabel.css('color','');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    $(this).addClass('selected');
 | 
				
			||||||
 | 
					                    flowFileLabel.css('height','auto');
 | 
				
			||||||
 | 
					                    flowFileLabel.css('color','inherit');
 | 
				
			||||||
 | 
					                    showProjectFileListing(flowFileLabel,activeProject,flowFileInput.val(),function(result,isDblClick) {
 | 
				
			||||||
 | 
					                        if (result) {
 | 
				
			||||||
 | 
					                            flowFileInput.val(result);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        if (isDblClick) {
 | 
				
			||||||
 | 
					                            $(flowFileInputSearch).click();
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        checkFiles();
 | 
				
			||||||
 | 
					                    })
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        row = $('<div class="user-settings-row"></div>').appendTo(filesContainer);
 | 
				
			||||||
 | 
					        $('<label for=""></label>').text('Credentials').appendTo(row);
 | 
				
			||||||
 | 
					        var credFileLabel = $('<div class="uneditable-input">').text(activeProject.files.credentials).appendTo(row);
 | 
				
			||||||
 | 
					        var credFileInput = $('<div class="uneditable-input">').text(activeProject.files.credentials).hide().insertAfter(credFileLabel);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var checkFiles = function() {
 | 
				
			||||||
 | 
					            var saveDisabled;
 | 
				
			||||||
 | 
					            var currentFlowValue = flowFileInput.val();
 | 
				
			||||||
 | 
					            var m = /^(.+?)(\.[^.]*)?$/.exec(currentFlowValue);
 | 
				
			||||||
 | 
					            if (m) {
 | 
				
			||||||
 | 
					                credFileInput.text(m[1]+"_cred"+(m[2]||".json"));
 | 
				
			||||||
 | 
					            } else if (currentFlowValue === "") {
 | 
				
			||||||
 | 
					                credFileInput.text("");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            var isFlowInvalid = currentFlowValue==="" ||
 | 
				
			||||||
 | 
					                                /\.\./.test(currentFlowValue) ||
 | 
				
			||||||
 | 
					                                /\/$/.test(currentFlowValue);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            saveDisabled = isFlowInvalid || credFileInput.text()==="";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (credentialSecretExistingInput.is(":visible")) {
 | 
				
			||||||
 | 
					                credentialSecretExistingInput.toggleClass("input-error", credentialSecretExistingInput.val() === "");
 | 
				
			||||||
 | 
					                saveDisabled = saveDisabled || credentialSecretExistingInput.val() === "";
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (credentialSecretNewInput.is(":visible")) {
 | 
				
			||||||
 | 
					                credentialSecretNewInput.toggleClass("input-error", credentialSecretNewInput.val() === "");
 | 
				
			||||||
 | 
					                saveDisabled = saveDisabled || credentialSecretNewInput.val() === "";
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            flowFileInput.toggleClass("input-error", isFlowInvalid);
 | 
				
			||||||
 | 
					            credFileInput.toggleClass("input-error",credFileInput.text()==="");
 | 
				
			||||||
 | 
					            saveButton.toggleClass('disabled',saveDisabled);
 | 
				
			||||||
 | 
					            saveButton.prop('disabled',saveDisabled);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        flowFileInput.on("change keyup paste",checkFiles);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!activeProject.files.flow) {
 | 
				
			||||||
 | 
					            $('<span class="form-warning"><i class="fa fa-warning"></i> Missing</span>').appendTo(flowFileLabelText);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (!activeProject.files.credentials) {
 | 
				
			||||||
 | 
					            $('<span class="form-warning"><i class="fa fa-warning"></i> Missing</span>').appendTo(credFileLabel);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        row = $('<div class="user-settings-row"></div>').appendTo(filesContainer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $('<label></label>').appendTo(row);
 | 
				
			||||||
 | 
					        var credentialStateLabel = $('<span><i class="user-settings-credentials-state-icon fa"></i> <span class="user-settings-credentials-state"></span></span>').appendTo(row);
 | 
				
			||||||
 | 
					        var credentialSecretButtons = $('<span class="button-group" style="margin-left: -72px;">').hide().appendTo(row);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        credentialStateLabel.css('color','#666');
 | 
				
			||||||
 | 
					        credentialSecretButtons.css('vertical-align','top');
 | 
				
			||||||
 | 
					        var credentialSecretResetButton = $('<button class="editor-button" style="vertical-align: top; width: 36px; margin-bottom: 10px"><i class="fa fa-trash-o"></i></button>')
 | 
				
			||||||
 | 
					            .appendTo(credentialSecretButtons)
 | 
				
			||||||
 | 
					            .click(function(e) {
 | 
				
			||||||
 | 
					                e.preventDefault();
 | 
				
			||||||
 | 
					                if (!$(this).hasClass('selected')) {
 | 
				
			||||||
 | 
					                    credentialSecretNewInput.val("");
 | 
				
			||||||
 | 
					                    credentialSecretExistingRow.hide();
 | 
				
			||||||
 | 
					                    credentialSecretNewRow.show();
 | 
				
			||||||
 | 
					                    $(this).addClass("selected");
 | 
				
			||||||
 | 
					                    credentialSecretEditButton.removeClass("selected");
 | 
				
			||||||
 | 
					                    credentialResetLabel.show();
 | 
				
			||||||
 | 
					                    credentialResetWarning.show();
 | 
				
			||||||
 | 
					                    credentialSetLabel.hide();
 | 
				
			||||||
 | 
					                    credentialChangeLabel.hide();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    credentialFormRows.show();
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    $(this).removeClass("selected");
 | 
				
			||||||
 | 
					                    credentialFormRows.hide();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                checkFiles();
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        var credentialSecretEditButton = $('<button class="editor-button" style="vertical-align: top; width: 36px; margin-bottom: 10px"><i class="fa fa-pencil"></i></button>')
 | 
				
			||||||
 | 
					            .appendTo(credentialSecretButtons)
 | 
				
			||||||
 | 
					            .click(function(e) {
 | 
				
			||||||
 | 
					                e.preventDefault();
 | 
				
			||||||
 | 
					                if (!$(this).hasClass('selected')) {
 | 
				
			||||||
 | 
					                    credentialSecretExistingInput.val("");
 | 
				
			||||||
 | 
					                    credentialSecretNewInput.val("");
 | 
				
			||||||
 | 
					                    if (activeProject.settings.credentialSecretInvalid || !activeProject.settings.credentialsEncrypted) {
 | 
				
			||||||
 | 
					                        credentialSetLabel.show();
 | 
				
			||||||
 | 
					                        credentialChangeLabel.hide();
 | 
				
			||||||
 | 
					                        credentialSecretExistingRow.hide();
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        credentialSecretExistingRow.show();
 | 
				
			||||||
 | 
					                        credentialSetLabel.hide();
 | 
				
			||||||
 | 
					                        credentialChangeLabel.show();
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    credentialSecretNewRow.show();
 | 
				
			||||||
 | 
					                    credentialSecretEditButton.addClass("selected");
 | 
				
			||||||
 | 
					                    credentialSecretResetButton.removeClass("selected");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    credentialResetLabel.hide();
 | 
				
			||||||
 | 
					                    credentialResetWarning.hide();
 | 
				
			||||||
 | 
					                    credentialFormRows.show();
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    $(this).removeClass("selected");
 | 
				
			||||||
 | 
					                    credentialFormRows.hide();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                checkFiles();
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        row = $('<div class="user-settings-row user-settings-row-credentials"></div>').hide().appendTo(filesContainer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var credentialFormRows = $('<div>',{style:"margin-top:10px"}).hide().appendTo(credentialStateLabel);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var credentialSetLabel = $('<div style="margin: 20px 0 10px 5px;">Set the encryption key:</div>').hide().appendTo(credentialFormRows);
 | 
				
			||||||
 | 
					        var credentialChangeLabel = $('<div style="margin: 20px 0 10px 5px;">Change the encryption key:</div>').hide().appendTo(credentialFormRows);
 | 
				
			||||||
 | 
					        var credentialResetLabel = $('<div style="margin: 20px 0 10px 5px;">Reset the encryption key:</div>').hide().appendTo(credentialFormRows);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var credentialSecretExistingRow = $('<div class="user-settings-row user-settings-row-credentials"></div>').appendTo(credentialFormRows);
 | 
				
			||||||
 | 
					        $('<label for=""></label>').text('Current key').appendTo(credentialSecretExistingRow);
 | 
				
			||||||
 | 
					        var credentialSecretExistingInput = $('<input type="password">').appendTo(credentialSecretExistingRow)
 | 
				
			||||||
 | 
					            .on("change keyup paste",function() {
 | 
				
			||||||
 | 
					                if (popover) {
 | 
				
			||||||
 | 
					                    popover.close();
 | 
				
			||||||
 | 
					                    popover = null;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                checkFiles();
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var credentialSecretNewRow = $('<div class="user-settings-row user-settings-row-credentials"></div>').appendTo(credentialFormRows);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $('<label for=""></label>').text('New key').appendTo(credentialSecretNewRow);
 | 
				
			||||||
 | 
					        var credentialSecretNewInput = $('<input type="password">').appendTo(credentialSecretNewRow).on("change keyup paste",checkFiles);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var credentialResetWarning = $('<div class="form-tips form-warning" style="margin: 10px;"><i class="fa fa-warning"></i> This will delete all existing credentials</div>').hide().appendTo(credentialFormRows);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var hideEditForm = function() {
 | 
				
			||||||
 | 
					            editButton.show();
 | 
				
			||||||
 | 
					            formButtons.hide();
 | 
				
			||||||
 | 
					            flowFileLabelText.show();
 | 
				
			||||||
 | 
					            flowFileInput.hide();
 | 
				
			||||||
 | 
					            flowFileInputSearch.hide();
 | 
				
			||||||
 | 
					            credFileLabel.show();
 | 
				
			||||||
 | 
					            credFileInput.hide();
 | 
				
			||||||
 | 
					            // credentialStateLabel.parent().show();
 | 
				
			||||||
 | 
					            credentialStateLabel.removeClass("uneditable-input");
 | 
				
			||||||
 | 
					            credentialStateLabel.css('height','');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $(".user-settings-row-credentials").hide();
 | 
				
			||||||
 | 
					            credentialFormRows.hide();
 | 
				
			||||||
 | 
					            credentialSecretButtons.hide();
 | 
				
			||||||
 | 
					            credentialSecretResetButton.removeClass("selected");
 | 
				
			||||||
 | 
					            credentialSecretEditButton.removeClass("selected");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var formButtons = $('<span class="button-group" style="position: relative; float: right; margin-right:0;"></span>').hide().appendTo(filesContainer);
 | 
				
			||||||
 | 
					        var cancelButton = $('<button class="editor-button">Cancel</button>')
 | 
				
			||||||
 | 
					            .appendTo(formButtons)
 | 
				
			||||||
 | 
					            .click(function(evt) {
 | 
				
			||||||
 | 
					                evt.preventDefault();
 | 
				
			||||||
 | 
					                hideEditForm();
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        var saveButton = $('<button class="editor-button">Save</button>')
 | 
				
			||||||
 | 
					            .appendTo(formButtons)
 | 
				
			||||||
 | 
					            .click(function(evt) {
 | 
				
			||||||
 | 
					                evt.preventDefault();
 | 
				
			||||||
 | 
					                var spinner = addSpinnerOverlay(filesContainer);
 | 
				
			||||||
 | 
					                var done = function(err) {
 | 
				
			||||||
                    spinner.remove();
 | 
					                    spinner.remove();
 | 
				
			||||||
                    if (err) {
 | 
					                    if (err) {
 | 
				
			||||||
                        console.log(err);
 | 
					                        console.log(err);
 | 
				
			||||||
                        return;
 | 
					                        return;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					                    flowFileLabelText.text(flowFileInput.val());
 | 
				
			||||||
 | 
					                    credFileLabel.text(credFileInput.text());
 | 
				
			||||||
 | 
					                    hideEditForm();
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					                var payload = {
 | 
				
			||||||
 | 
					                    files: {
 | 
				
			||||||
 | 
					                        flow: flowFileInput.val(),
 | 
				
			||||||
 | 
					                        credentials: credFileInput.text()
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (credentialSecretResetButton.hasClass('selected')) {
 | 
				
			||||||
 | 
					                    payload.resetCredentialSecret = true;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if (credentialSecretResetButton.hasClass('selected') || credentialSecretEditButton.hasClass('selected')) {
 | 
				
			||||||
 | 
					                    payload.credentialSecret = credentialSecretNewInput.val();
 | 
				
			||||||
 | 
					                    if (credentialSecretExistingInput.is(":visible")) {
 | 
				
			||||||
 | 
					                        payload.currentCredentialSecret = credentialSecretExistingInput.val();
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // console.log(JSON.stringify(payload,null,4));
 | 
				
			||||||
 | 
					                RED.deploy.setDeployInflight(true);
 | 
				
			||||||
                utils.sendRequest({
 | 
					                utils.sendRequest({
 | 
				
			||||||
                    url: "projects/"+activeProject.name,
 | 
					                    url: "projects/"+activeProject.name,
 | 
				
			||||||
                    type: "PUT",
 | 
					                    type: "PUT",
 | 
				
			||||||
                    responses: {
 | 
					                    responses: {
 | 
				
			||||||
                        0: function(error) {
 | 
					                        0: function(error) {
 | 
				
			||||||
                            done(error,null);
 | 
					                            done(error);
 | 
				
			||||||
                        },
 | 
					                        },
 | 
				
			||||||
                        200: function(data) {
 | 
					                        200: function(data) {
 | 
				
			||||||
                            if (popover) {
 | 
					                            activeProject = data;
 | 
				
			||||||
                                popover.close();
 | 
					                            console.log("updating form");
 | 
				
			||||||
                                popover = null;
 | 
					                            updateForm();
 | 
				
			||||||
                            }
 | 
					                            done();
 | 
				
			||||||
                            changeButton.prop('disabled',false);
 | 
					 | 
				
			||||||
                            if (resetButton) {
 | 
					 | 
				
			||||||
                                resetButton.prop('disabled',false);
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                            $(".project-settings-credentials-row").hide();
 | 
					 | 
				
			||||||
                            $(".project-settings-credentials-current-row").hide();
 | 
					 | 
				
			||||||
                            $(".project-settings-credentials-reset-row").hide();
 | 
					 | 
				
			||||||
                        },
 | 
					                        },
 | 
				
			||||||
                        400: {
 | 
					                        400: {
 | 
				
			||||||
 | 
					                            'credentials_load_failed': function(error) {
 | 
				
			||||||
 | 
					                                done(error);
 | 
				
			||||||
 | 
					                            },
 | 
				
			||||||
                            'unexpected_error': function(error) {
 | 
					                            'unexpected_error': function(error) {
 | 
				
			||||||
                                done(error,null);
 | 
					                                console.log(error);
 | 
				
			||||||
 | 
					                                done(error);
 | 
				
			||||||
                            },
 | 
					                            },
 | 
				
			||||||
                            'missing_current_credential_key':  function(error) {
 | 
					                            'missing_current_credential_key':  function(error) {
 | 
				
			||||||
                                currentKey.addClass("input-error");
 | 
					                                credentialSecretExistingInput.addClass("input-error");
 | 
				
			||||||
                                popover = RED.popover.create({
 | 
					                                popover = RED.popover.create({
 | 
				
			||||||
                                    target: currentKey,
 | 
					                                    target: credentialSecretExistingInput,
 | 
				
			||||||
                                    direction: 'right',
 | 
					                                    direction: 'right',
 | 
				
			||||||
                                    size: 'small',
 | 
					                                    size: 'small',
 | 
				
			||||||
                                    content: "Incorrect key"
 | 
					                                    content: "Incorrect key",
 | 
				
			||||||
 | 
					                                    autoClose: 3000
 | 
				
			||||||
                                }).open();
 | 
					                                }).open();
 | 
				
			||||||
                                done();
 | 
					                                done(error);
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        },
 | 
					                        },
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                },payload).always(function() {
 | 
					                },payload).always(function() {
 | 
				
			||||||
                    if (activeProject.settings.credentialSecretInvalid) {
 | 
					 | 
				
			||||||
                    RED.deploy.setDeployInflight(false);
 | 
					                    RED.deploy.setDeployInflight(false);
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        var updateForm = function() {
 | 
				
			||||||
 | 
					            if (activeProject.settings.credentialSecretInvalid) {
 | 
				
			||||||
 | 
					                credentialStateLabel.find(".user-settings-credentials-state-icon").removeClass().addClass("user-settings-credentials-state-icon fa fa-warning");
 | 
				
			||||||
 | 
					                credentialStateLabel.find(".user-settings-credentials-state").text("Invalid encryption key");
 | 
				
			||||||
 | 
					            } else if (activeProject.settings.credentialsEncrypted) {
 | 
				
			||||||
 | 
					                credentialStateLabel.find(".user-settings-credentials-state-icon").removeClass().addClass("user-settings-credentials-state-icon fa fa-lock");
 | 
				
			||||||
 | 
					                credentialStateLabel.find(".user-settings-credentials-state").text("Encryption enabled");
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                credentialStateLabel.find(".user-settings-credentials-state-icon").removeClass().addClass("user-settings-credentials-state-icon fa fa-unlock");
 | 
				
			||||||
 | 
					                credentialStateLabel.find(".user-settings-credentials-state").text("Encryption disabled");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            credentialSecretResetButton.toggleClass('disabled',!activeProject.settings.credentialsEncrypted);
 | 
				
			||||||
 | 
					            credentialSecretResetButton.prop('disabled',!activeProject.settings.credentialsEncrypted);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
                });
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // $('<h3></h3>').text("Credentials").appendTo(pane);
 | 
					 | 
				
			||||||
        // row = $('<div class="user-settings-row"></div>').appendTo(pane);
 | 
					 | 
				
			||||||
        // $('<span style="margin-right: 20px;"><i class="fa fa-unlock"></i> Credentials are not encrypted</span>').appendTo(row);
 | 
					 | 
				
			||||||
        // $('<button id="" class="editor-button">Set key</button>').appendTo(row);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        $('<h3></h3>').text("Repository").appendTo(pane);
 | 
					 | 
				
			||||||
        row = $('<div class="user-settings-row"></div>').appendTo(pane);
 | 
					 | 
				
			||||||
        var input;
 | 
					 | 
				
			||||||
        $('<label for="">'+'Remote'+'</label>').appendTo(row);
 | 
					 | 
				
			||||||
        $('<input id="" type="text">').appendTo(row);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        checkFiles();
 | 
				
			||||||
 | 
					        updateForm();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    function createSettingsPane(activeProject) {
 | 
				
			||||||
 | 
					        var pane = $('<div id="project-settings-tab-settings" class="project-settings-tab-pane node-help"></div>');
 | 
				
			||||||
 | 
					        createFilesSection(activeProject,pane);
 | 
				
			||||||
        return pane;
 | 
					        return pane;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -933,6 +933,9 @@ function refresh() {
 | 
				
			|||||||
        showCredentialsPrompt: function() { //TODO: rename this function
 | 
					        showCredentialsPrompt: function() { //TODO: rename this function
 | 
				
			||||||
            RED.projects.settings.show('settings');
 | 
					            RED.projects.settings.show('settings');
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        showFilesPrompt: function() { //TODO: rename this function
 | 
				
			||||||
 | 
					            RED.projects.settings.show('settings');
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        // showSidebar: showSidebar,
 | 
					        // showSidebar: showSidebar,
 | 
				
			||||||
        refresh: refresh,
 | 
					        refresh: refresh,
 | 
				
			||||||
        editProject: function() {
 | 
					        editProject: function() {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -371,3 +371,76 @@
 | 
				
			|||||||
        transition: all 0.2s ease-in-out;
 | 
					        transition: all 0.2s ease-in-out;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					.project-file-listing-container > .red-ui-editableList > .red-ui-editableList-border {
 | 
				
			||||||
 | 
					    border-radius: 0;
 | 
				
			||||||
 | 
					    border: none;
 | 
				
			||||||
 | 
					    border-top: 1px solid $secondary-border-color;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.red-ui-editableList-container .projects-dialog-file-list {
 | 
				
			||||||
 | 
					    .red-ui-editableList-border {
 | 
				
			||||||
 | 
					        border: none;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    li {
 | 
				
			||||||
 | 
					        padding: 0 !important;
 | 
				
			||||||
 | 
					        border: none;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    .red-ui-editableList-container {
 | 
				
			||||||
 | 
					        padding: 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.projects-dialog-file-list-entry {
 | 
				
			||||||
 | 
					    padding: 3px 0;
 | 
				
			||||||
 | 
					    border-left: 2px solid #fff;
 | 
				
			||||||
 | 
					    border-right: 2px solid #fff;
 | 
				
			||||||
 | 
					    &.projects-list-entry-current {
 | 
				
			||||||
 | 
					        &:not(.selectable) {
 | 
				
			||||||
 | 
					            background: #f9f9f9;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        i {
 | 
				
			||||||
 | 
					            color: #999;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    &.selectable {
 | 
				
			||||||
 | 
					        cursor: pointer;
 | 
				
			||||||
 | 
					        &:hover {
 | 
				
			||||||
 | 
					            background: #f3f3f3;
 | 
				
			||||||
 | 
					            border-left-color:#999;
 | 
				
			||||||
 | 
					            border-right-color:#999;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    i {
 | 
				
			||||||
 | 
					        color: #999;
 | 
				
			||||||
 | 
					        width: 16px;
 | 
				
			||||||
 | 
					        text-align: center;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    &.selected {
 | 
				
			||||||
 | 
					        background: #efefef;
 | 
				
			||||||
 | 
					        border-left-color:#999;
 | 
				
			||||||
 | 
					        border-right-color:#999;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    span {
 | 
				
			||||||
 | 
					        display: inline-block;
 | 
				
			||||||
 | 
					        vertical-align:middle;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    .projects-dialog-file-list-entry-folder {
 | 
				
			||||||
 | 
					         margin: 0 10px 0 0px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					         .fa-angle-right {
 | 
				
			||||||
 | 
					             color: #333;
 | 
				
			||||||
 | 
					             transition: all 0.2s ease-in-out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					         }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    .projects-dialog-file-list-entry-file {
 | 
				
			||||||
 | 
					         margin: 0 10px 0 20px;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    .projects-dialog-file-list-entry-name {
 | 
				
			||||||
 | 
					        font-size: 1em;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    &.expanded .fa-angle-right {
 | 
				
			||||||
 | 
					        transform: rotate(90deg);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.projects-dialog-file-list-entry-file-type-git { color: #999 }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -43,8 +43,11 @@
 | 
				
			|||||||
            padding-bottom: 0;
 | 
					            padding-bottom: 0;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    input {
 | 
					    input, div.uneditable-input {
 | 
				
			||||||
        margin-bottom: 0;
 | 
					        //margin-bottom: 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    div.uneditable-input {
 | 
				
			||||||
 | 
					        position: relative;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    input[type='number'] {
 | 
					    input[type='number'] {
 | 
				
			||||||
        width: 60px;
 | 
					        width: 60px;
 | 
				
			||||||
@@ -57,3 +60,14 @@
 | 
				
			|||||||
.user-settings-row {
 | 
					.user-settings-row {
 | 
				
			||||||
    padding: 5px 10px 2px;
 | 
					    padding: 5px 10px 2px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					.user-settings-section {
 | 
				
			||||||
 | 
					    position: relative;
 | 
				
			||||||
 | 
					    &:after {
 | 
				
			||||||
 | 
					        content: "";
 | 
				
			||||||
 | 
					        display: table;
 | 
				
			||||||
 | 
					        clear: both;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    .uneditable-input, input {
 | 
				
			||||||
 | 
					        width: calc(100% - 150px);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -33,6 +33,7 @@ function handleStatus(event) {
 | 
				
			|||||||
    publish("status/"+event.id,event.status,true);
 | 
					    publish("status/"+event.id,event.status,true);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
function handleRuntimeEvent(event) {
 | 
					function handleRuntimeEvent(event) {
 | 
				
			||||||
 | 
					    log.trace("runtime event: "+JSON.stringify(event));
 | 
				
			||||||
    publish("notification/"+event.id,event.payload||{},event.retain);
 | 
					    publish("notification/"+event.id,event.payload||{},event.retain);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
function init(_server,runtime) {
 | 
					function init(_server,runtime) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -85,7 +85,8 @@
 | 
				
			|||||||
            "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"
 | 
					            "credentials_load_failed": "Flows stopped due to missing or invalid credentialSecret",
 | 
				
			||||||
 | 
					            "missing_flow_file": "Could not find the project flow file"
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        "error": "<strong>Error</strong>: __message__",
 | 
					        "error": "<strong>Error</strong>: __message__",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -77,14 +77,15 @@ module.exports = {
 | 
				
			|||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    })
 | 
					                    })
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    res.redirect(303,req.baseUrl + '/');
 | 
					                    res.redirect(303,req.baseUrl + '/'+ req.params.id);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            } else if (req.body.hasOwnProperty('credentialSecret') ||
 | 
					            } else if (req.body.hasOwnProperty('credentialSecret') ||
 | 
				
			||||||
                       req.body.hasOwnProperty('description') ||
 | 
					                       req.body.hasOwnProperty('description') ||
 | 
				
			||||||
                       req.body.hasOwnProperty('dependencies')||
 | 
					                       req.body.hasOwnProperty('dependencies')||
 | 
				
			||||||
                       req.body.hasOwnProperty('summary')) {
 | 
					                       req.body.hasOwnProperty('summary') ||
 | 
				
			||||||
 | 
					                       req.body.hasOwnProperty('files')) {
 | 
				
			||||||
                runtime.storage.projects.updateProject(req.params.id, req.body).then(function() {
 | 
					                runtime.storage.projects.updateProject(req.params.id, req.body).then(function() {
 | 
				
			||||||
                    res.redirect(303,req.baseUrl + '/');
 | 
					                    res.redirect(303,req.baseUrl + '/'+ req.params.id);
 | 
				
			||||||
                }).catch(function(err) {
 | 
					                }).catch(function(err) {
 | 
				
			||||||
                    if (err.code) {
 | 
					                    if (err.code) {
 | 
				
			||||||
                        res.status(400).json({error:err.code, message: err.message});
 | 
					                        res.status(400).json({error:err.code, message: err.message});
 | 
				
			||||||
@@ -92,6 +93,8 @@ module.exports = {
 | 
				
			|||||||
                        res.status(400).json({error:"unexpected_error", message:err.toString()});
 | 
					                        res.status(400).json({error:"unexpected_error", message:err.toString()});
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                })
 | 
					                })
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                res.status(400).json({error:"unexpected_error", message:"invalid_request"});
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -78,7 +78,7 @@ function loadFlows() {
 | 
				
			|||||||
        });
 | 
					        });
 | 
				
			||||||
    }).catch(function(err) {
 | 
					    }).catch(function(err) {
 | 
				
			||||||
        activeConfig = null;
 | 
					        activeConfig = null;
 | 
				
			||||||
        events.emit("runtime-event",{id:"runtime-state",payload:{type:"warning",error:"credentials_load_failed",text:"notification.warnings.invalid-credentials-secret"},retain:true});
 | 
					        events.emit("runtime-event",{id:"runtime-state",payload:{type:"warning",error:err.code,text:"notification.warnings."+err.code},retain:true});
 | 
				
			||||||
        log.warn(log._("nodes.flows.error",{message:err.toString()}));
 | 
					        log.warn(log._("nodes.flows.error",{message:err.toString()}));
 | 
				
			||||||
        throw err;
 | 
					        throw err;
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -49,11 +49,11 @@ Project.prototype.load = function () {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    this.credentialSecret = projectSettings.credentialSecret;
 | 
					    this.credentialSecret = projectSettings.credentialSecret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.paths.flowFile = fspath.join(this.path,"flow.json");
 | 
					    // this.paths.flowFile = fspath.join(this.path,"flow.json");
 | 
				
			||||||
    this.paths.credentialsFile = fspath.join(this.path,"flow_cred.json");
 | 
					    // this.paths.credentialsFile = fspath.join(this.path,"flow_cred.json");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    var promises = [];
 | 
					    var promises = [];
 | 
				
			||||||
    return checkProjectFiles(project.name).then(function(missingFiles) {
 | 
					    return checkProjectFiles(project).then(function(missingFiles) {
 | 
				
			||||||
        if (missingFiles.length > 0) {
 | 
					        if (missingFiles.length > 0) {
 | 
				
			||||||
            project.missingFiles = missingFiles;
 | 
					            project.missingFiles = missingFiles;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -61,7 +61,18 @@ Project.prototype.load = function () {
 | 
				
			|||||||
            project.paths['package.json'] = fspath.join(project.path,"package.json");
 | 
					            project.paths['package.json'] = fspath.join(project.path,"package.json");
 | 
				
			||||||
            promises.push(fs.readFile(project.paths['package.json'],"utf8").then(function(content) {
 | 
					            promises.push(fs.readFile(project.paths['package.json'],"utf8").then(function(content) {
 | 
				
			||||||
                project.package = util.parseJSON(content);
 | 
					                project.package = util.parseJSON(content);
 | 
				
			||||||
 | 
					                if (project.package.hasOwnProperty('node-red')) {
 | 
				
			||||||
 | 
					                    if (project.package['node-red'].hasOwnProperty('settings')) {
 | 
				
			||||||
 | 
					                        project.paths.flowFile = project.package['node-red'].settings.flowFile;
 | 
				
			||||||
 | 
					                        project.paths.credentialsFile = project.package['node-red'].settings.credentialsFile;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    // TODO: package.json doesn't have a node-red section
 | 
				
			||||||
 | 
					                    //       is that a bad thing?
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }));
 | 
					            }));
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            project.package = {};
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (missingFiles.indexOf('README.md') === -1) {
 | 
					        if (missingFiles.indexOf('README.md') === -1) {
 | 
				
			||||||
            project.paths['README.md'] = fspath.join(project.path,"README.md");
 | 
					            project.paths['README.md'] = fspath.join(project.path,"README.md");
 | 
				
			||||||
@@ -71,6 +82,16 @@ Project.prototype.load = function () {
 | 
				
			|||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            project.description = "";
 | 
					            project.description = "";
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        // if (missingFiles.indexOf('flow.json') !== -1) {
 | 
				
			||||||
 | 
					        //     console.log("MISSING FLOW FILE");
 | 
				
			||||||
 | 
					        // } else {
 | 
				
			||||||
 | 
					        //     project.paths.flowFile = fspath.join(project.path,"flow.json");
 | 
				
			||||||
 | 
					        // }
 | 
				
			||||||
 | 
					        // if (missingFiles.indexOf('flow_cred.json') !== -1) {
 | 
				
			||||||
 | 
					        //     console.log("MISSING CREDS FILE");
 | 
				
			||||||
 | 
					        // } else {
 | 
				
			||||||
 | 
					        //     project.paths.credentialsFile = fspath.join(project.path,"flow_cred.json");
 | 
				
			||||||
 | 
					        // }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return when.settle(promises).then(function() {
 | 
					        return when.settle(promises).then(function() {
 | 
				
			||||||
            return project;
 | 
					            return project;
 | 
				
			||||||
@@ -79,11 +100,23 @@ Project.prototype.load = function () {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Project.prototype.update = function (data) {
 | 
					Project.prototype.update = function (data) {
 | 
				
			||||||
    var filesToUpdate = {};
 | 
					
 | 
				
			||||||
    var promises = [];
 | 
					    var promises = [];
 | 
				
			||||||
    var project = this;
 | 
					    var project = this;
 | 
				
			||||||
 | 
					    var saveSettings = false;
 | 
				
			||||||
 | 
					    var saveREADME = false;
 | 
				
			||||||
 | 
					    var savePackage = false;
 | 
				
			||||||
 | 
					    var flowFilesChanged = false;
 | 
				
			||||||
 | 
					    var credentialSecretChanged = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (data.credentialSecret) {
 | 
					    var globalProjectSettings = settings.get("projects");
 | 
				
			||||||
 | 
					    if (!globalProjectSettings.projects.hasOwnProperty(this.name)) {
 | 
				
			||||||
 | 
					        globalProjectSettings.projects[this.name] = {};
 | 
				
			||||||
 | 
					        saveSettings = true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (data.credentialSecret && data.credentialSecret !== this.credentialSecret) {
 | 
				
			||||||
        var existingSecret = data.currentCredentialSecret;
 | 
					        var existingSecret = data.currentCredentialSecret;
 | 
				
			||||||
        var isReset = data.resetCredentialSecret;
 | 
					        var isReset = data.resetCredentialSecret;
 | 
				
			||||||
        var secret = data.credentialSecret;
 | 
					        var secret = data.credentialSecret;
 | 
				
			||||||
@@ -100,42 +133,62 @@ Project.prototype.update = function (data) {
 | 
				
			|||||||
            this.credentialSecret !== existingSecret) { // key doesn't match provided existing key
 | 
					            this.credentialSecret !== existingSecret) { // key doesn't match provided existing key
 | 
				
			||||||
                var e = new Error("Cannot change credentialSecret without current key");
 | 
					                var e = new Error("Cannot change credentialSecret without current key");
 | 
				
			||||||
                e.code = "missing_current_credential_key";
 | 
					                e.code = "missing_current_credential_key";
 | 
				
			||||||
                throw e;
 | 
					                return when.reject(e);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        this.credentialSecret = secret;
 | 
					        this.credentialSecret = secret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        var globalProjectSettings = settings.get("projects");
 | 
					 | 
				
			||||||
        globalProjectSettings.projects[this.name] = globalProjectSettings.projects[this.name]||{}
 | 
					 | 
				
			||||||
        globalProjectSettings.projects[this.name].credentialSecret = project.credentialSecret;
 | 
					        globalProjectSettings.projects[this.name].credentialSecret = project.credentialSecret;
 | 
				
			||||||
        delete this.credentialSecretInvalid;
 | 
					        delete this.credentialSecretInvalid;
 | 
				
			||||||
 | 
					        saveSettings = true;
 | 
				
			||||||
        return settings.set("projects",globalProjectSettings);
 | 
					        credentialSecretChanged = true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (data.hasOwnProperty('description')) {
 | 
					    if (data.hasOwnProperty('description')) {
 | 
				
			||||||
        filesToUpdate[this.paths['README.md']] = function() {
 | 
					        saveREADME = true;
 | 
				
			||||||
            return data.description;
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
        this.description = data.description;
 | 
					        this.description = data.description;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (data.hasOwnProperty('dependencies')) {
 | 
					    if (data.hasOwnProperty('dependencies')) {
 | 
				
			||||||
        filesToUpdate[this.paths['package.json']] = function() {
 | 
					        savePackage = true;
 | 
				
			||||||
            return JSON.stringify(project.package,"",4)
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
        this.package.dependencies = data.dependencies;
 | 
					        this.package.dependencies = data.dependencies;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (data.hasOwnProperty('summary')) {
 | 
					    if (data.hasOwnProperty('summary')) {
 | 
				
			||||||
        filesToUpdate[this.paths['package.json']] = function() {
 | 
					        savePackage = true;
 | 
				
			||||||
            return JSON.stringify(project.package,"",4)
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
        this.package.description = data.summary;
 | 
					        this.package.description = data.summary;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    var files = Object.keys(filesToUpdate);
 | 
					    if (data.hasOwnProperty('files')) {
 | 
				
			||||||
    files.forEach(function(f) {
 | 
					        this.package['node-red'] = this.package['node-red'] || { settings: {}};
 | 
				
			||||||
        promises.push(util.writeFile(f,filesToUpdate[f]()));
 | 
					        if (data.files.hasOwnProperty('flow') && this.package['node-red'].settings.flowFile !== data.files.flow) {
 | 
				
			||||||
    });
 | 
					            this.paths.flowFile = data.files.flow;
 | 
				
			||||||
    return when.settle(promises);
 | 
					            this.package['node-red'].settings.flowFile = data.files.flow;
 | 
				
			||||||
 | 
					            savePackage = true;
 | 
				
			||||||
 | 
					            flowFilesChanged = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (data.files.hasOwnProperty('credentials') && this.package['node-red'].settings.credentialsFile !== data.files.credentials) {
 | 
				
			||||||
 | 
					            this.paths.credentialsFile = data.files.credentials;
 | 
				
			||||||
 | 
					            this.package['node-red'].settings.credentialsFile = data.files.credentials;
 | 
				
			||||||
 | 
					            // Don't know if the credSecret is invalid or not so clear the flag
 | 
				
			||||||
 | 
					            delete this.credentialSecretInvalid;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            savePackage = true;
 | 
				
			||||||
 | 
					            flowFilesChanged = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (saveSettings) {
 | 
				
			||||||
 | 
					        promises.push(settings.set("projects",globalProjectSettings));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (saveREADME) {
 | 
				
			||||||
 | 
					        promises.push(util.writeFile(this.paths['README.md'], this.description));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (savePackage) {
 | 
				
			||||||
 | 
					        promises.push(util.writeFile(this.paths['package.json'], JSON.stringify(this.package,"",4)));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return when.settle(promises).then(function(res) {
 | 
				
			||||||
 | 
					        return {
 | 
				
			||||||
 | 
					            flowFilesChanged: flowFilesChanged,
 | 
				
			||||||
 | 
					            credentialSecretChanged: credentialSecretChanged
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Project.prototype.getFiles = function () {
 | 
					Project.prototype.getFiles = function () {
 | 
				
			||||||
@@ -161,13 +214,23 @@ Project.prototype.getCommit = function(sha) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Project.prototype.getFlowFile = function() {
 | 
					Project.prototype.getFlowFile = function() {
 | 
				
			||||||
    return this.paths.flowFile;
 | 
					    console.log("Project.getFlowFile = ",this.paths.flowFile);
 | 
				
			||||||
 | 
					    if (this.paths.flowFile) {
 | 
				
			||||||
 | 
					        return fspath.join(this.path,this.paths.flowFile);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        return null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
Project.prototype.getFlowFileBackup = function() {
 | 
					Project.prototype.getFlowFileBackup = function() {
 | 
				
			||||||
    return getBackupFilename(this.getFlowFile());
 | 
					    return getBackupFilename(this.getFlowFile());
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
Project.prototype.getCredentialsFile = function() {
 | 
					Project.prototype.getCredentialsFile = function() {
 | 
				
			||||||
 | 
					    console.log("Project.getCredentialsFile = ",this.paths.credentialsFile);
 | 
				
			||||||
 | 
					    if (this.paths.credentialsFile) {
 | 
				
			||||||
 | 
					        return fspath.join(this.path,this.paths.credentialsFile);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
        return this.paths.credentialsFile;
 | 
					        return this.paths.credentialsFile;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
Project.prototype.getCredentialsFileBackup = function() {
 | 
					Project.prototype.getCredentialsFileBackup = function() {
 | 
				
			||||||
    return getBackupFilename(this.getCredentialsFile());
 | 
					    return getBackupFilename(this.getCredentialsFile());
 | 
				
			||||||
@@ -186,8 +249,8 @@ Project.prototype.toJSON = function () {
 | 
				
			|||||||
            credentialSecretInvalid: this.credentialSecretInvalid
 | 
					            credentialSecretInvalid: this.credentialSecretInvalid
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        files: {
 | 
					        files: {
 | 
				
			||||||
            flowFile: this.paths.flowFile&&this.paths.flowFile.substring(this.path.length),
 | 
					            flow: this.paths.flowFile,
 | 
				
			||||||
            credentialsFile: this.paths.credentialsFile&&this.paths.credentialsFile.substring(this.path.length)
 | 
					            credentials: this.paths.credentialsFile
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
@@ -233,7 +296,7 @@ function createDefaultProject(project) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function checkProjectFiles(project) {
 | 
					function checkProjectFiles(project) {
 | 
				
			||||||
    var projectPath = fspath.join(projectsDir,project);
 | 
					    var projectPath = project.path;
 | 
				
			||||||
    var promises = [];
 | 
					    var promises = [];
 | 
				
			||||||
    var paths = [];
 | 
					    var paths = [];
 | 
				
			||||||
    for (var file in defaultFileSet) {
 | 
					    for (var file in defaultFileSet) {
 | 
				
			||||||
@@ -279,6 +342,9 @@ function createProject(metadata) {
 | 
				
			|||||||
                if (metadata.credentialSecret) {
 | 
					                if (metadata.credentialSecret) {
 | 
				
			||||||
                    projects.projects[project].credentialSecret = metadata.credentialSecret;
 | 
					                    projects.projects[project].credentialSecret = metadata.credentialSecret;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					                if (metadata.remote) {
 | 
				
			||||||
 | 
					                    projects.projects[project].remote = metadata.remote;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
                return settings.set('projects',projects);
 | 
					                return settings.set('projects',projects);
 | 
				
			||||||
            }).then(function() {
 | 
					            }).then(function() {
 | 
				
			||||||
                if (metadata.remote) {
 | 
					                if (metadata.remote) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -111,7 +111,7 @@ function getFiles(localRepo) {
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            files[fullName] = {
 | 
					            files[fullName] = {
 | 
				
			||||||
                type: "f"
 | 
					                type: /\/$/.test(fullName)?"d":"f"
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
        return runCommand(gitCommand,["status","--porcelain"],localRepo).then(function(output) {
 | 
					        return runCommand(gitCommand,["status","--porcelain"],localRepo).then(function(output) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -200,11 +200,36 @@ function updateProject(project,data) {
 | 
				
			|||||||
        // TODO standardise
 | 
					        // TODO standardise
 | 
				
			||||||
        throw new Error("Cannot update inactive project");
 | 
					        throw new Error("Cannot update inactive project");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (data.hasOwnProperty('credentialSecret')) {
 | 
					    // In case this triggers a credential secret change
 | 
				
			||||||
        return setCredentialSecret(data);
 | 
					    var isReset = data.resetCredentialSecret;
 | 
				
			||||||
    } else {
 | 
					    var wasInvalid = activeProject.credentialSecretInvalid;
 | 
				
			||||||
        return activeProject.update(data);
 | 
					
 | 
				
			||||||
 | 
					    return activeProject.update(data).then(function(result) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (result.flowFilesChanged) {
 | 
				
			||||||
 | 
					            flowsFullPath = activeProject.getFlowFile();
 | 
				
			||||||
 | 
					            flowsFileBackup = activeProject.getFlowFileBackup();
 | 
				
			||||||
 | 
					            credentialsFile = activeProject.getCredentialsFile();
 | 
				
			||||||
 | 
					            credentialsFileBackup = activeProject.getCredentialsFileBackup();
 | 
				
			||||||
 | 
					            return reloadActiveProject();
 | 
				
			||||||
 | 
					        } else if (result.credentialSecretChanged) {
 | 
				
			||||||
 | 
					            if (isReset || !wasInvalid) {
 | 
				
			||||||
 | 
					                if (isReset) {
 | 
				
			||||||
 | 
					                    runtime.nodes.clearCredentials();
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					                runtime.nodes.setCredentialSecret(activeProject.credentialSecret);
 | 
				
			||||||
 | 
					                return runtime.nodes.exportCredentials()
 | 
				
			||||||
 | 
					                    .then(runtime.storage.saveCredentials)
 | 
				
			||||||
 | 
					                    .then(function() {
 | 
				
			||||||
 | 
					                        if (wasInvalid) {
 | 
				
			||||||
 | 
					                            return reloadActiveProject();
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					            } else if (wasInvalid) {
 | 
				
			||||||
 | 
					                return reloadActiveProject();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
function setCredentialSecret(data) { //existingSecret,secret) {
 | 
					function setCredentialSecret(data) { //existingSecret,secret) {
 | 
				
			||||||
    var isReset = data.resetCredentialSecret;
 | 
					    var isReset = data.resetCredentialSecret;
 | 
				
			||||||
@@ -251,6 +276,14 @@ function getFlows() {
 | 
				
			|||||||
            log.info(log._("storage.localfilesystem.flows-file",{path:flowsFullPath}));
 | 
					            log.info(log._("storage.localfilesystem.flows-file",{path:flowsFullPath}));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    if (activeProject) {
 | 
				
			||||||
 | 
					        if (!activeProject.getFlowFile()) {
 | 
				
			||||||
 | 
					            log.warn("NLS: project has no flow file");
 | 
				
			||||||
 | 
					            var error = new Error("NLS: project has no flow file");
 | 
				
			||||||
 | 
					            error.code = "missing_flow_file";
 | 
				
			||||||
 | 
					            return when.reject(error);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    return util.readFile(flowsFullPath,flowsFileBackup,[],'flow');
 | 
					    return util.readFile(flowsFullPath,flowsFileBackup,[],'flow');
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user