From e05ff01d579b3747bd8d44b8af639330168e9c08 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Fri, 15 Feb 2019 21:53:27 +0000 Subject: [PATCH] Allow a project to be located below the root of repo --- .../editor-client/locales/en-US/editor.json | 18 +- .../@node-red/editor-client/src/js/red.js | 18 +- .../src/js/ui/projects/projectSettings.js | 201 ++++++++++++++---- .../src/js/ui/projects/projects.js | 4 +- .../editor-client/src/js/ui/utils.js | 3 +- .../editor-client/src/sass/projects.scss | 2 +- .../localfilesystem/projects/Project.js | 153 ++++++++----- 7 files changed, 286 insertions(+), 113 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json b/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json index 4d3ac6fcc..19d9e42d7 100755 --- a/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json +++ b/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json @@ -135,7 +135,12 @@ "updated": "Project '__project__' updated", "pull": "Project '__project__' reloaded", "revert": "Project '__project__' reverted", - "merge-complete": "Git merge completed" + "merge-complete": "Git merge completed", + "setupCredentials": "Setup credentials", + "setupProjectFiles": "Setup project files", + "no": "No thanks", + "createDefault": "Create default project files", + "mergeConflict": "Show merge conflicts" }, "label": { "manage-project-dep": "Manage project dependencies", @@ -544,14 +549,19 @@ "removeFromProject": "remove from project", "addToProject": "add to project", "files": "Files", + "package": "Package", "flow": "Flow", "credentials": "Credentials", + "package":"Package", + "packageCreate":"File will be created when changes are saved", + "fileNotExist":"File does not exist", + "selectFile": "Select File", "invalidEncryptionKey": "Invalid encryption key", "encryptionEnabled": "Encryption enabled", "encryptionDisabled": "Encryption disabled", - "setTheEncryptionKey": "Set the encryption key:", - "resetTheEncryptionKey": "Reset the encryption key:", - "changeTheEncryptionKey": "Change the encryption key:", + "setTheEncryptionKey": "Set the encryption key", + "resetTheEncryptionKey": "Reset the encryption key", + "changeTheEncryptionKey": "Change the encryption key", "currentKey": "Current key", "newKey": "New key", "credentialsAlert": "This will delete all existing credentials", diff --git a/packages/node_modules/@node-red/editor-client/src/js/red.js b/packages/node_modules/@node-red/editor-client/src/js/red.js index b00413e8a..4d02a189a 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/red.js +++ b/packages/node_modules/@node-red/editor-client/src/js/red.js @@ -211,7 +211,7 @@ var RED = (function() { } ] } else if (msg.error === "missing-types") { - text+=""; + text+=""; if (!!RED.projects.getActiveProject()) { options.buttons = [ { @@ -239,7 +239,7 @@ var RED = (function() { if (RED.user.hasPermission("projects.write")) { options.buttons = [ { - text: "Setup credentials", + text: RED._("notification.project.setupCredentials"), click: function() { persistentNotifications[notificationId].hideNotification(); RED.projects.showCredentialsPrompt(); @@ -250,7 +250,7 @@ var RED = (function() { } else { options.buttons = [ { - text: "Close", + text: RED._("common.label.close"), click: function() { persistentNotifications[notificationId].hideNotification(); } @@ -261,7 +261,7 @@ var RED = (function() { if (RED.user.hasPermission("projects.write")) { options.buttons = [ { - text: "Setup project files", + text: RED._("notification.project.setupProjectFiles"), click: function() { persistentNotifications[notificationId].hideNotification(); RED.projects.showFilesPrompt(); @@ -273,10 +273,10 @@ var RED = (function() { if (RED.user.hasPermission("projects.write")) { options.buttons = [ { - text: "Create default package file", + text: RED._("notification.project.setupProjectFiles"), click: function() { persistentNotifications[notificationId].hideNotification(); - RED.projects.createDefaultPackageFile(); + RED.projects.showFilesPrompt(); } } ] @@ -285,13 +285,13 @@ var RED = (function() { if (RED.user.hasPermission("projects.write")) { options.buttons = [ { - text: "No thanks", + text: RED._("notification.project.no"), click: function() { persistentNotifications[notificationId].hideNotification(); } }, { - text: "Create default project files", + text: RED._("notification.project.createDefault"), click: function() { persistentNotifications[notificationId].hideNotification(); RED.projects.createDefaultFileSet(); @@ -305,7 +305,7 @@ var RED = (function() { if (RED.user.hasPermission("projects.write")) { options.buttons = [ { - text: "Show merge conflicts", + text: RED._("notification.project.mergeConflict"), click: function() { persistentNotifications[notificationId].hideNotification(); RED.sidebar.versionControl.showLocalChanges(); diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/projects/projectSettings.js b/packages/node_modules/@node-red/editor-client/src/js/ui/projects/projectSettings.js index 9e0333c5a..16a31390e 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/projects/projectSettings.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/projects/projectSettings.js @@ -526,7 +526,7 @@ RED.projects.settings = (function() { } - function showProjectFileListing(row,activeProject,current,filter,done) { + function showProjectFileListing(row,activeProject,current,selectFilter,done) { var dialog; var dialogBody; var filesList; @@ -569,14 +569,15 @@ RED.projects.settings = (function() { }) return result; } - var files = sortFiles("",files,""); - createFileSubList(container,files.children,current,filter,done,"height: 175px"); + var files = sortFiles("",files,"") + + createFileSubList(container,files.children,current,selectFilter,done,"height: 175px"); spinner.remove(); }); return container; } - function createFileSubList(container, files, current, filter, onselect, style) { + function createFileSubList(container, files, current, selectFilter, onselect, style) { style = style || ""; var list = $('
    ',{class:"projects-dialog-file-list", style:style}).appendTo(container).editableList({ addButton: false, @@ -592,7 +593,7 @@ RED.projects.settings = (function() { } else { children.hide(); } - createFileSubList(children,entry.children,current,filter,onselect); + createFileSubList(children,entry.children,current,selectFilter,onselect); header.addClass("selectable"); header.click(function(e) { if ($(this).hasClass("expanded")) { @@ -618,7 +619,7 @@ RED.projects.settings = (function() { header.addClass("projects-dialog-file-list-entry-file-type-git"); } $(' ').appendTo(header); - if (filter.test(entry.name)) { + if (selectFilter(entry)) { header.addClass("selectable"); if (entry.path === current) { header.addClass("selected"); @@ -626,7 +627,7 @@ RED.projects.settings = (function() { header.click(function(e) { $(".projects-dialog-file-list-entry.selected").removeClass("selected"); $(this).addClass("selected"); - onselect(entry.path); + onselect(entry.path,true); }) header.dblclick(function(e) { e.preventDefault(); @@ -730,18 +731,27 @@ RED.projects.settings = (function() { var title = $('

    ').text(RED._("sidebar.project.projectSettings.files")).appendTo(pane); var filesContainer = $('').appendTo(pane); if (RED.user.hasPermission("projects.write")) { - var editFilesButton = $('') + var editFilesButton = $('') .appendTo(title) .click(function(evt) { evt.preventDefault(); formButtons.show(); editFilesButton.hide(); + // packageFileLabelText.hide(); + + if (!activeProject.files.package) { + packageFileSubLabel.find(".projects-edit-form-sublabel-text").text(RED._("sidebar.project.projectSettings.packageCreate")); + packageFileSubLabel.show(); + } + + packageFileInputSearch.show(); + // packageFileInputCreate.show(); flowFileLabelText.hide(); flowFileInput.show(); flowFileInputSearch.show(); - credFileLabel.hide(); - credFileInput.show(); - flowFileInput.focus(); + + flowFileInputResize(); + // credentialStateLabel.parent().hide(); credentialStateLabel.addClass("uneditable-input"); $(".user-settings-row-credentials").show(); @@ -752,14 +762,76 @@ RED.projects.settings = (function() { } var row; + // Flow files + row = $('').appendTo(filesContainer); + $('').text(RED._("sidebar.project.projectSettings.package")).appendTo(row); + var packageFileLabel = $('
    ').appendTo(row); + var packageFileLabelPrefixText = $('').text("/").appendTo(packageFileLabel); + var packageFileLabelText = $('').text(activeProject.files.package||"package.json").appendTo(packageFileLabel); + var packageFileInput = $('').val(activeProject.files.package||"package.json").appendTo(packageFileLabel); + + var packageFileInputSearch = $('') + .hide() + .appendTo(packageFileLabel) + .click(function(e) { + if ($(this).hasClass('selected')) { + $(this).removeClass('selected'); + packageFileLabel.find('.project-file-listing-container').slideUp(200,function() { + $(this).remove(); + packageFileLabel.css('height',''); + }); + packageFileLabel.css('color',''); + } else { + $(this).addClass('selected'); + packageFileLabel.css('color','inherit'); + var fileList = showProjectFileListing(packageFileLabel,activeProject,packageFileInput.val(), + function(entry) { return entry.children || /package\.json$/.test(entry.path); }, + function(result,close) { + if (result) { + packageFileInput.val(result); + packageFileLabelText.text(result); + var rootDir = result.substring(0,result.length - 12); + flowFileLabelPrefixText.text("/"+rootDir); + credFileLabelPrefixText.text("/"+rootDir); + flowFileInputResize(); + packageFileSubLabel.hide(); + } + if (close) { + $(packageFileInputSearch).click(); + } + checkFiles(); + }); + packageFileLabel.css('height','auto'); + setTimeout(function() { + fileList.slideDown(200); + },50); + + } + }) + RED.popover.tooltip(packageFileInputSearch,RED._("sidebar.project.projectSettings.selectFile")); + var packageFileSubLabel = $('').appendTo(row).hide(); + if (!activeProject.files.package) { + packageFileSubLabel.find(".projects-edit-form-sublabel-text").text(RED._("sidebar.project.projectSettings.fileNotExist")); + packageFileSubLabel.show(); + } + + + var projectPackage = "/"+(activeProject.files.package||"package.json"); + var projectRoot = projectPackage.substring(0,projectPackage.length - 12); + // Flow files row = $('').appendTo(filesContainer); $('').text(RED._("sidebar.project.projectSettings.flow")).appendTo(row); var flowFileLabel = $('
    ').appendTo(row); - var flowFileLabelText = $('').text(activeProject.files.flow).appendTo(flowFileLabel); - - var flowFileInput = $('').val(activeProject.files.flow).hide().appendTo(flowFileLabel); - var flowFileInputSearch = $('') + var flowFileLabelPrefixText = $('').text(projectRoot).appendTo(flowFileLabel); + var flowFileLabelText = $('').text(activeProject.files.flow||"flows.json").appendTo(flowFileLabel); + var flowFileInputResize = function() { + flowFileInput.css({ + "width": "calc(100% - "+(flowFileInputSearch.width() + flowFileLabelPrefixText.width())+"px)" + }); + } + var flowFileInput = $('').val(activeProject.files.flow||"flows.json").hide().appendTo(flowFileLabel); + var flowFileInputSearch = $('') .hide() .appendTo(flowFileLabel) .click(function(e) { @@ -773,15 +845,24 @@ RED.projects.settings = (function() { } else { $(this).addClass('selected'); flowFileLabel.css('color','inherit'); - var fileList = showProjectFileListing(flowFileLabel,activeProject,flowFileInput.val(), /.*\.json$/,function(result,isDblClick) { - if (result) { - flowFileInput.val(result); - } - if (isDblClick) { - $(flowFileInputSearch).click(); - } - checkFiles(); - }); + var packageFile = packageFileInput.val(); + var packagePrefix = packageFile.substring(0,packageFile.length - 12); + var re = new RegExp("^"+packagePrefix+".*\.json$"); + var fileList = showProjectFileListing(flowFileLabel, + activeProject, + flowFileInput.val(), + function(entry) { return !/package.json$/.test(entry.path) && re.test(entry.path) && !/_cred\.json$/.test(entry.path) }, + function(result,isDblClick) { + if (result) { + flowFileInput.val(result.substring(packagePrefix.length)); + + } + if (isDblClick) { + $(flowFileInputSearch).click(); + } + checkFiles(); + } + ); flowFileLabel.css('height','auto'); setTimeout(function() { fileList.slideDown(200); @@ -789,26 +870,32 @@ RED.projects.settings = (function() { } }) + RED.popover.tooltip(flowFileInputSearch,RED._("sidebar.project.projectSettings.selectFile")); row = $('').appendTo(filesContainer); $('').text(RED._("sidebar.project.projectSettings.credentials")).appendTo(row); - var credFileLabel = $('
    ').text(activeProject.files.credentials).appendTo(row); - var credFileInput = $('
    ').text(activeProject.files.credentials).hide().insertAfter(credFileLabel); + + var credFileLabel = $('
    ').appendTo(row); + var credFileLabelPrefixText = $('').text(projectRoot).appendTo(credFileLabel); + var credFileLabelText = $('').text(activeProject.files.credentials||"flows_cred.json").appendTo(credFileLabel); + + var credFileInput = $('').val(activeProject.files.credentials||"flows_cred.json").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")); + credFileLabelText.text(m[1]+"_cred"+(m[2]||".json")); } else if (currentFlowValue === "") { - credFileInput.text(""); + credFileLabelText.text(""); } + credFileInput.val(credFileLabelText.text()); var isFlowInvalid = currentFlowValue==="" || /\.\./.test(currentFlowValue) || /\/$/.test(currentFlowValue); - saveDisabled = isFlowInvalid || credFileInput.text()===""; + saveDisabled = isFlowInvalid || credFileLabelText.text()===""; if (credentialSecretExistingInput.is(":visible")) { credentialSecretExistingInput.toggleClass("input-error", credentialSecretExistingInput.val() === ""); @@ -821,19 +908,22 @@ RED.projects.settings = (function() { flowFileInput.toggleClass("input-error", isFlowInvalid); - credFileInput.toggleClass("input-error",credFileInput.text()===""); + // credFileInput.toggleClass("input-error",credFileInput.text()===""); saveButton.toggleClass('disabled',saveDisabled); saveButton.prop('disabled',saveDisabled); } flowFileInput.on("change keyup paste",checkFiles); - if (!activeProject.files.flow) { - $(' Missing').appendTo(flowFileLabelText); - } - if (!activeProject.files.credentials) { - $(' Missing').appendTo(credFileLabel); - } + // if (!activeProject.files.package) { + // $(' Missing').appendTo(packageFileLabelText); + // } + // if (!activeProject.files.flow) { + // $(' Missing').appendTo(flowFileLabelText); + // } + // if (!activeProject.files.credentials) { + // $(' Missing').appendTo(credFileLabel); + // } row = $('').appendTo(filesContainer); @@ -844,7 +934,7 @@ RED.projects.settings = (function() { credentialStateLabel.css('color','#666'); credentialSecretButtons.css('vertical-align','top'); - var credentialSecretResetButton = $('') + var credentialSecretResetButton = $('') .appendTo(credentialSecretButtons) .click(function(e) { e.preventDefault(); @@ -866,7 +956,9 @@ RED.projects.settings = (function() { } checkFiles(); }); - var credentialSecretEditButton = $('') + RED.popover.tooltip(credentialSecretResetButton,RED._("sidebar.project.projectSettings.resetTheEncryptionKey")); + + var credentialSecretEditButton = $('') .appendTo(credentialSecretButtons) .click(function(e) { e.preventDefault(); @@ -896,6 +988,7 @@ RED.projects.settings = (function() { checkFiles(); }) + RED.popover.tooltip(credentialSecretEditButton,RED._("sidebar.project.projectSettings.changeTheEncryptionKey")); row = $('').hide().appendTo(filesContainer); @@ -930,11 +1023,13 @@ RED.projects.settings = (function() { var hideEditForm = function() { editFilesButton.show(); formButtons.hide(); + // packageFileLabelText.show(); + packageFileInputSearch.hide(); + // packageFileInputCreate.hide(); flowFileLabelText.show(); flowFileInput.hide(); flowFileInputSearch.hide(); - credFileLabel.show(); - credFileInput.hide(); + // credentialStateLabel.parent().show(); credentialStateLabel.removeClass("uneditable-input"); credentialStateLabel.css('height',''); @@ -954,13 +1049,26 @@ RED.projects.settings = (function() { } var formButtons = $('').hide().appendTo(filesContainer); - $('') + $('') .appendTo(formButtons) .click(function(evt) { evt.preventDefault(); + var projectPackage = "/"+(activeProject.files.package||"package.json"); + var projectRoot = projectPackage.substring(0,projectPackage.length - 12); + flowFileLabelPrefixText.text(projectRoot); + credFileLabelPrefixText.text(projectRoot); + packageFileLabelText.text(activeProject.files.package||"package.json"); + if (!activeProject.files.package) { + packageFileSubLabel.find(".projects-edit-form-sublabel-text").text(RED._("sidebar.project.projectSettings.fileNotExist")); + packageFileSubLabel.show(); + } else { + packageFileSubLabel.hide(); + } + flowFileInput.val(flowFileLabelText.text()); + credFileLabelText.text(activeProject.files.credentials||"flows_cred.json"); hideEditForm(); }); - var saveButton = $('') + var saveButton = $('') .appendTo(formButtons) .click(function(evt) { evt.preventDefault(); @@ -972,13 +1080,15 @@ RED.projects.settings = (function() { return; } flowFileLabelText.text(flowFileInput.val()); - credFileLabel.text(credFileInput.text()); + credFileLabelText.text(credFileInput.val()); + packageFileSubLabel.hide(); hideEditForm(); } var payload = { files: { flow: flowFileInput.val(), - credentials: credFileInput.text() + credentials: credFileInput.val(), + package: packageFileInput.val() } } @@ -992,7 +1102,8 @@ RED.projects.settings = (function() { } } - // console.log(JSON.stringify(payload,null,4)); + console.log(payload); + return; RED.deploy.setDeployInflight(true); utils.sendRequest({ url: "projects/"+activeProject.name, diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/projects/projects.js b/packages/node_modules/@node-red/editor-client/src/js/ui/projects/projects.js index 01560e152..c39fd3e24 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/projects/projects.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/projects/projects.js @@ -2061,7 +2061,6 @@ RED.projects = (function() { console.log(xhr); console.log(textStatus); console.log(err); - console.log(stack); }).always(function() { var delta = Date.now() - start; delta = Math.max(0,500-delta); @@ -2389,6 +2388,9 @@ RED.projects = (function() { return; } RED.projects.settings.show('settings'); + setTimeout(function() { + $("#project-settings-tab-settings-file-edit").click(); + },200) }, showProjectDependencies: function() { RED.projects.settings.show('deps'); diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/utils.js b/packages/node_modules/@node-red/editor-client/src/js/ui/utils.js index 8d4dff437..b33d1c9e1 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/utils.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/utils.js @@ -967,6 +967,7 @@ RED.utils = (function() { addSpinnerOverlay: addSpinnerOverlay, decodeObject: decodeObject, parseContextKey: parseContextKey, - createIconElement: createIconElement + createIconElement: createIconElement, + sanitize: sanitize } })(); diff --git a/packages/node_modules/@node-red/editor-client/src/sass/projects.scss b/packages/node_modules/@node-red/editor-client/src/sass/projects.scss index 74fa801c9..3ef582bc3 100644 --- a/packages/node_modules/@node-red/editor-client/src/sass/projects.scss +++ b/packages/node_modules/@node-red/editor-client/src/sass/projects.scss @@ -60,7 +60,7 @@ .project-settings-tab-pane { & * .projects-edit-form-sublabel { margin-right: 50px; - margin-top: -10px; + margin-top: -10px !important; margin-bottom: 5px; } } diff --git a/packages/node_modules/@node-red/runtime/lib/storage/localfilesystem/projects/Project.js b/packages/node_modules/@node-red/runtime/lib/storage/localfilesystem/projects/Project.js index 43772a72c..8a280ee9a 100644 --- a/packages/node_modules/@node-red/runtime/lib/storage/localfilesystem/projects/Project.js +++ b/packages/node_modules/@node-red/runtime/lib/storage/localfilesystem/projects/Project.js @@ -53,6 +53,7 @@ function getGitUser(user) { } return null; } + function Project(path) { this.path = path; this.name = fspath.basename(path); @@ -72,7 +73,7 @@ Project.prototype.load = function () { projectSettings = globalProjectSettings.projects[this.name] || {}; } } - + this.paths.root = projectSettings.rootPath || ""; this.credentialSecret = projectSettings.credentialSecret; this.git = projectSettings.git || { user:{} }; @@ -83,8 +84,8 @@ Project.prototype.load = function () { return checkProjectFiles(project).then(function(missingFiles) { project.missingFiles = missingFiles; if (missingFiles.indexOf('package.json') === -1) { - project.paths['package.json'] = fspath.join(project.path,"package.json"); - promises.push(fs.readFile(project.paths['package.json'],"utf8").then(function(content) { + project.paths['package.json'] = fspath.join(project.paths.root,"package.json"); + promises.push(fs.readFile(fspath.join(project.path,project.paths['package.json']),"utf8").then(function(content) { try { project.package = util.parseJSON(content); if (project.package.hasOwnProperty('node-red')) { @@ -101,17 +102,19 @@ Project.prototype.load = function () { project.package = {}; } })); + if (missingFiles.indexOf('README.md') === -1) { + project.paths['README.md'] = fspath.join(project.paths.root,"README.md"); + promises.push(fs.readFile(fspath.join(project.path,project.paths['README.md']),"utf8").then(function(content) { + project.description = content; + })); + } else { + project.description = ""; + } } else { project.package = {}; - } - if (missingFiles.indexOf('README.md') === -1) { - project.paths['README.md'] = fspath.join(project.path,"README.md"); - promises.push(fs.readFile(project.paths['README.md'],"utf8").then(function(content) { - project.description = content; - })); - } else { project.description = ""; } + // if (missingFiles.indexOf('flow.json') !== -1) { // console.log("MISSING FLOW FILE"); // } else { @@ -131,6 +134,10 @@ Project.prototype.load = function () { }); }; +Project.prototype.getFilePath = function(file) { + return fspath.join(this.path,this.paths.root,this.paths[file]) +} + Project.prototype.initialise = function(user,data) { var project = this; // if (!this.empty) { @@ -224,6 +231,7 @@ Project.prototype.parseRemoteBranch = function (remoteBranch) { Project.prototype.isEmpty = function () { return this.empty; }; + Project.prototype.isMerging = function() { return this.merging; } @@ -243,6 +251,7 @@ Project.prototype.update = function (user, data) { var savePackage = false; var flowFilesChanged = false; var credentialSecretChanged = false; + var reloadProject = false; var globalProjectSettings = settings.get("projects"); if (!globalProjectSettings.projects.hasOwnProperty(this.name)) { @@ -250,7 +259,6 @@ Project.prototype.update = function (user, data) { saveSettings = true; } - if (data.credentialSecret && data.credentialSecret !== this.credentialSecret) { var existingSecret = data.currentCredentialSecret; var isReset = data.resetCredentialSecret; @@ -278,6 +286,55 @@ Project.prototype.update = function (user, data) { credentialSecretChanged = true; } + if (this.missingFiles.indexOf('package.json') !== -1) { + if (!data.files || !data.files.package) { + // Cannot update a project that doesn't have a known package.json + return Promise.reject("Cannot update project with missing package.json"); + } + } + + if (data.hasOwnProperty('files')) { + this.package['node-red'] = this.package['node-red'] || { settings: {}}; + if (data.files.hasOwnProperty('package') && data.files.package !== fspath.join(this.paths.root,"package.json")) { + // We have a package file. It could be one that doesn't exist yet, or + // does exist and we need to load it. + if (!/package\.json$/.test(data.files.package)) { + return Promise.reject("Invalid package file: "+data.files.package) + } + var root = data.files.package.substring(0,data.files.package.length-12); + this.paths.root = root; + this.paths['package.json'] = 'package.json'; + globalProjectSettings.projects[this.name].rootPath = root; + saveSettings = true; + // 1. check if it exists + if (fs.existsSync(fspath.join(this.path,this.paths.root,this.paths['package.json']))) { + // Load the existing one.... + } else { + var newPackage = defaultFileSet["package.json"](this); + fs.writeFileSync(fspath.join(this.path,this.paths.root,this.paths['package.json']),newPackage); + this.package = JSON.parse(newPackage); + } + reloadProject = true; + flowFilesChanged = true; + } + + if (data.files.hasOwnProperty('flow') && this.package['node-red'].settings.flowFile !== data.files.flow) { + this.paths.flowFile = data.files.flow; + 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 (data.hasOwnProperty('description')) { saveREADME = true; this.description = data.description; @@ -333,47 +390,32 @@ Project.prototype.update = function (user, data) { } } - if (data.hasOwnProperty('files')) { - this.package['node-red'] = this.package['node-red'] || { settings: {}}; - if (data.files.hasOwnProperty('flow') && this.package['node-red'].settings.flowFile !== data.files.flow) { - this.paths.flowFile = data.files.flow; - 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)); + promises.push(util.writeFile(fspath.join(this.path,this.paths['README.md']), this.description)); } if (savePackage) { - promises.push(fs.readFile(project.paths['package.json'],"utf8").then(content => { + promises.push(fs.readFile(fspath.join(project.path,project.paths['package.json']),"utf8").then(content => { var currentPackage = {}; try { currentPackage = util.parseJSON(content); } catch(err) { } this.package = Object.assign(currentPackage,this.package); - return util.writeFile(this.paths['package.json'], JSON.stringify(this.package,"",4)); + return util.writeFile(fspath.join(project.path,this.paths['package.json']), JSON.stringify(this.package,"",4)); })); } - return when.settle(promises).then(function(res) { - return { + return when.settle(promises).then(res => { + if (reloadProject) { + return this.load() + } + }).then(() => { return { flowFilesChanged: flowFilesChanged, credentialSecretChanged: credentialSecretChanged - } - }) + }}) }; Project.prototype.getFiles = function () { @@ -384,12 +426,15 @@ Project.prototype.getFiles = function () { throw err; }); }; + Project.prototype.stageFile = function(file) { return gitTools.stageFile(this.path,file); }; + Project.prototype.unstageFile = function(file) { return gitTools.unstageFile(this.path,file); } + Project.prototype.commit = function(user, options) { var self = this; return gitTools.commit(this.path,options.message,getGitUser(user)).then(function() { @@ -399,9 +444,11 @@ Project.prototype.commit = function(user, options) { } }); } + Project.prototype.getFileDiff = function(file,type) { return gitTools.getFileDiff(this.path,file,type); } + Project.prototype.getCommits = function(options) { return gitTools.getCommits(this.path,options).catch(function(err) { if (/bad default revision/i.test(err.message) || /ambiguous argument/i.test(err.message) || /does not have any commits yet/i.test(err.message)) { @@ -414,9 +461,11 @@ Project.prototype.getCommits = function(options) { throw err; }) } + Project.prototype.getCommit = function(sha) { return gitTools.getCommit(this.path,sha); } + Project.prototype.getFile = function (filePath,treeish) { if (treeish !== "_") { return gitTools.getFile(this.path, filePath, treeish); @@ -424,6 +473,7 @@ Project.prototype.getFile = function (filePath,treeish) { return fs.readFile(fspath.join(this.path,filePath),"utf8"); } }; + Project.prototype.revertFile = function (filePath) { var self = this; return gitTools.revertFile(this.path, filePath).then(function() { @@ -431,8 +481,6 @@ Project.prototype.revertFile = function (filePath) { }); }; - - Project.prototype.status = function(user, includeRemote) { var self = this; @@ -615,6 +663,7 @@ Project.prototype.resolveMerge = function (file,resolutions) { }) }); }; + Project.prototype.abortMerge = function () { var self = this; return gitTools.abortMerge(this.path).then(function() { @@ -675,12 +724,11 @@ Project.prototype.setBranch = function (branchName, isCreate) { return self.load(); }) }; + Project.prototype.getBranchStatus = function (branchName) { return gitTools.getBranchStatus(this.path,branchName); }; - - Project.prototype.getRemotes = function (user) { return gitTools.getRemotes(this.path).then(function(remotes) { var result = []; @@ -693,12 +741,14 @@ Project.prototype.getRemotes = function (user) { return {remotes:result}; }) }; + Project.prototype.addRemote = function(user,remote,options) { var project = this; return gitTools.addRemote(this.path,remote,options).then(function() { return project.loadRemotes() }); } + Project.prototype.updateRemote = function(user,remote,options) { var username; if (!user) { @@ -716,6 +766,7 @@ Project.prototype.updateRemote = function(user,remote,options) { } return Promise.resolve(); } + Project.prototype.removeRemote = function(user, remote) { // TODO: if this was the last remote using this url, then remove the authCache // details. @@ -725,11 +776,10 @@ Project.prototype.removeRemote = function(user, remote) { }); } - Project.prototype.getFlowFile = function() { // console.log("Project.getFlowFile = ",this.paths.flowFile); if (this.paths.flowFile) { - return fspath.join(this.path,this.paths.flowFile); + return fspath.join(this.path,this.paths.root,this.paths.flowFile); } else { return null; } @@ -742,14 +792,16 @@ Project.prototype.getFlowFileBackup = function() { } return null; } + Project.prototype.getCredentialsFile = function() { // console.log("Project.getCredentialsFile = ",this.paths.credentialsFile); if (this.paths.credentialsFile) { - return fspath.join(this.path,this.paths.credentialsFile); + return fspath.join(this.path,this.paths.root,this.paths.credentialsFile); } else { return this.paths.credentialsFile; } } + Project.prototype.getCredentialsFileBackup = function() { return getBackupFilename(this.getCredentialsFile()); } @@ -763,10 +815,11 @@ Project.prototype.export = function () { dependencies: this.package.dependencies||{}, empty: this.empty, settings: { - credentialsEncrypted: (typeof this.credentialSecret === "string"), + credentialsEncrypted: (typeof this.credentialSecret === "string") && this.credentialSecret.length > 0, credentialSecretInvalid: this.credentialSecretInvalid }, files: { + package: this.paths['package.json'], flow: this.paths.flowFile, credentials: this.paths.credentialsFile }, @@ -777,7 +830,6 @@ Project.prototype.export = function () { } }; - function getCredentialsFilename(filename) { filename = filename || "undefined"; // TODO: DRY - ./index.js @@ -793,7 +845,6 @@ function getBackupFilename(filename) { var ffDir = fspath.dirname(filename); return fspath.join(ffDir,"."+ffName+".backup"); } - function checkProjectExists(projectPath) { return fs.pathExists(projectPath).then(function(exists) { if (!exists) { @@ -805,7 +856,6 @@ function checkProjectExists(projectPath) { } }); } - function createDefaultProject(user, project) { var projectPath = fspath.join(projectsDir,project.name); // Create a basic skeleton of a project @@ -869,15 +919,15 @@ function createDefaultProject(user, project) { }) }); } - function checkProjectFiles(project) { var projectPath = project.path; + var projectRoot = project.paths.root; var promises = []; var paths = []; for (var file in defaultFileSet) { if (defaultFileSet.hasOwnProperty(file)) { paths.push(file); - promises.push(fs.stat(fspath.join(projectPath,file))); + promises.push(fs.stat(fspath.join(projectPath,projectRoot,file))); } } return when.settle(promises).then(function(results) { @@ -900,7 +950,6 @@ function checkProjectFiles(project) { // } }); } - function createProject(user, metadata) { var username; if (!user) { @@ -933,6 +982,9 @@ function createProject(user, metadata) { } projects.projects[project] = {}; if (metadata.hasOwnProperty('credentialSecret')) { + if (metadata.credentialSecret === "") { + metadata.credentialSecret = false; + } projects.projects[project].credentialSecret = metadata.credentialSecret; } return settings.set('projects',projects); @@ -970,7 +1022,6 @@ function createProject(user, metadata) { }) }) } - function deleteProject(user, projectPath) { return checkProjectExists(projectPath).then(function() { return fs.remove(projectPath).then(function() { @@ -981,14 +1032,12 @@ function deleteProject(user, projectPath) { }); }); } - function loadProject(projectPath) { return checkProjectExists(projectPath).then(function() { var project = new Project(projectPath); return project.load(); }); } - function init(_settings, _runtime) { settings = _settings; runtime = _runtime;