/** * Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. **/ RED.projects = (function() { var dialog; var dialogBody; var activeProject; var screens = {}; function initScreens() { var migrateProjectHeader = $('
'); $(' ').appendTo(migrateProjectHeader) $('').text("Hello! We have introduced 'projects' to Node-RED.").appendTo(body); $('
').text("This is a new way for you to manage your flow files and includes version control of your flows.").appendTo(body); $('
').text("To get started you can create your first project using your current flow files in a few easy steps.").appendTo(body); $('
').text("If you are not sure, you can skip this for now. You will still be able to create your first project from the 'Projects' menu option at any time.").appendTo(body); // var buttons = $('
').appendTo(container); // var createNew = $('').appendTo(buttons); // createNew.click(function(e) { // e.preventDefault(); // show('create'); // }); // var openExisting = $('').appendTo(buttons); // openExisting.click(function(e) { // e.preventDefault(); // show('open') // }); return container; }, buttons: [ { // id: "clipboard-dialog-cancel", text: "Not right now", click: function() { createProjectOptions = {}; $( this ).dialog( "close" ); } }, { text: "Create your first project", // TODO: nls class: "primary", click: function() { show('git-config'); } } ] }, 'git-config': (function() { var gitUsernameInput; var gitEmailInput; return { content: function(options) { var container = $(''); migrateProjectHeader.appendTo(container); var body = $('').appendTo(container); $('').text("Step 1 : Setup your version control client").appendTo(body); $('
').text("Node-RED uses the open source tool Git for version control. It tracks changes to your project files and lets you push them to remote repositories.").appendTo(body); $('
').text("When you commit a set of changes, Git records who made the changes with a username and email address. The Username can be anything you want - it does not need to be your real name.").appendTo(body); $('
').text("If your Git client is already configured, you can skip this step.").appendTo(body); var currentGitSettings = RED.settings.get('git') || {}; currentGitSettings.user = currentGitSettings.user || {}; var row = $('
').appendTo(body); $('').appendTo(row); gitUsernameInput = $('').val(currentGitSettings.user.name||"").appendTo(row); // $('').text("This does not need to be your real name").appendTo(row); row = $('').appendTo(body); $('').appendTo(row); gitEmailInput = $('').val(currentGitSettings.user.email||"").appendTo(row); // $('').text("Something something email").appendTo(row); setTimeout(function() { gitUsernameInput.focus(); },50); return container; }, buttons: [ { // id: "clipboard-dialog-cancel", text: "Back", click: function() { show('welcome'); } }, { text: "Next", // TODO: nls class: "primary", click: function() { var currentGitSettings = RED.settings.get('git') || {}; currentGitSettings.user = currentGitSettings.user || {}; currentGitSettings.user.name = gitUsernameInput.val(); currentGitSettings.user.email = gitEmailInput.val(); RED.settings.set('git', currentGitSettings); show('project-details'); } } ] }; })(), 'project-details': (function() { var projectNameInput; var projectSummaryInput; return { content: function(options) { var container = $(''); migrateProjectHeader.appendTo(container); var body = $('').appendTo(container); $('').text("Step 2 : Create your project").appendTo(body); $('
').text("A project contains your flow files, a README file, a package.json file and a settings file. It makes it much easier to share your flows with others and to collaborate on them.").appendTo(body); $('
').text("You can create multiple projects and quickly switch between them from the editor.").appendTo(body); $('
').text("To begin, your project needs a name and an optional description.").appendTo(body); var validateForm = function() { var projectName = projectNameInput.val(); var valid = true; if (projectNameInputChanged) { projectNameStatus.empty(); if (!/^[a-zA-Z0-9\-_]+$/.test(projectName)) { projectNameInput.addClass("input-error"); $('').appendTo(projectNameStatus); projectNameValid = false; valid = false; } else { projectNameInput.removeClass("input-error"); $('').appendTo(projectNameStatus); projectNameValid = true; } projectNameLastChecked = projectName; } valid = projectNameValid; $("#projects-dialog-create-name").prop('disabled',!valid).toggleClass('disabled ui-button-disabled ui-state-disabled',!valid); } var row = $('
').appendTo(body); $('').appendTo(row); var subrow = $('').appendTo(row); projectNameInput = $('').val(createProjectOptions.name||"").appendTo(subrow); var projectNameStatus = $('').appendTo(subrow); var projectNameInputChanged = false; var projectNameLastChecked = ""; var projectNameValid; var checkProjectName; var autoInsertedName = ""; projectNameInput.on("change keyup paste",function() { projectNameInputChanged = (projectNameInput.val() !== projectNameLastChecked); if (checkProjectName) { clearTimeout(checkProjectName); } else if (projectNameInputChanged) { projectNameStatus.empty(); $('').appendTo(projectNameStatus); if (projectNameInput.val() === '') { validateForm(); return; } } checkProjectName = setTimeout(function() { validateForm(); checkProjectName = null; },300) }); projectNameSublabel = $('').appendTo(row).find("small"); // Empty Project row = $('').appendTo(body); $('').appendTo(row); projectSummaryInput = $('').val(createProjectOptions.summary||"").appendTo(row); $('').appendTo(row); setTimeout(function() { projectNameInput.focus(); },50); return container; }, buttons: [ { text: "Back", click: function() { show('git-config'); } }, { id: "projects-dialog-create-name", disabled: true, text: "Next", // TODO: nls class: "primary disabled", click: function() { createProjectOptions.name = projectNameInput.val(); createProjectOptions.summary = projectSummaryInput.val(); createProjectOptions.files = { migrateFiles: true }; show('encryption-config'); } } ] }; })(), 'encryption-config': (function() { var emptyProjectCredentialInput; return { content: function(options) { var container = $(''); migrateProjectHeader.appendTo(container); var body = $('').appendTo(container); $('').text("Step 3 : Setup encryption of your credentials file").appendTo(body); // $('
').text("Your flow credentials file can be encrypted to keep its contents secure.").appendTo(body); if (RED.settings.flowEncryptionType === 'disabled') { $('
').text("Your flow credentials file is not currently encrypted.").appendTo(body); $('
').text("That means its contents, such as passwords and access tokens, can be read by anyone with access to the file.").appendTo(body); $('
').text("If you want to store these credentials in a public Git repository, you must encrypt them by providing a secret key phrase.").appendTo(body); } else { if (RED.settings.flowEncryptionType === 'user') { $('
').text("Your flow credentials file is currently encrypted using the credentialSecret property from your settings file as the key.").appendTo(body); } else if (RED.settings.flowEncryptionType === 'system') { $('
').text("Your flow credentials file is currently encrypted using a system-generated secret as the key. You should provide a new secret key for this project.").appendTo(body); } $('
').text("The secret will be copied into the settings for your new project. You can then manage the secret within the editor.").appendTo(body); } // var row = $('
').appendTo(body); // $('').appendTo(row); // var gitUsernameInput = $('').val(currentGitSettings.user.name||"").appendTo(row); // // $('').text("This does not need to be your real name").appendTo(row); // // row = $('').appendTo(body); // $('').appendTo(row); // var gitEmailInput = $('').val(currentGitSettings.user.email||"").appendTo(row); // // $('').text("Something something email").appendTo(row); var validateForm = function() { var valid = true; var encryptionState = $("input[name=projects-encryption-type]:checked").val(); if (encryptionState === 'enabled') { var encryptionKeyType = $("input[name=projects-encryption-key]:checked").val(); if (encryptionKeyType === 'custom') { valid = valid && emptyProjectCredentialInput.val()!==''; } } $("#projects-dialog-create-encryption").prop('disabled',!valid).toggleClass('disabled ui-button-disabled ui-state-disabled',!valid); } var row = $('').appendTo(body); $('').appendTo(row); var credentialsBox = $('').text("You have successfully created your first project!").appendTo(body); $('
').text("You can now continue to use Node-RED just as you always have.").appendTo(body); $('
').text("The 'info' tab in the sidebar shows you what your current active project is. "+ "The button next to the name can be used to access the project settings view.").appendTo(body); $('
').text("The new 'history' tab in the sidebar can be used to view files that have changed "+ "in your project and to commit them. It shows you a complete history of your commits and "+ "allows you to push your changes to a remote repository.").appendTo(body); return container; }, buttons: [ { text: "Done", click: function() { $( this ).dialog( "close" ); } } ] }, 'create': (function() { var projectNameInput; var projectSummaryInput; var projectFlowFileInput; var projectSecretInput; var projectSecretSelect; var copyProject; var projectRepoInput; var emptyProjectCredentialInput; var projectRepoUserInput; var projectRepoPasswordInput; var projectNameSublabel; var projectRepoSSHKeySelect; var projectRepoPassphrase; var projectRepoRemoteName var projectRepoBranch; return { title: "Create a new project", // TODO: NLS content: function() { var projectList = null; var pendingFormValidation = false; $.getJSON("projects", function(data) { projectList = {}; data.projects.forEach(function(p) { projectList[p] = true; if (pendingFormValidation) { pendingFormValidation = false; validateForm(); } }) }); var container = $('
'); var row; var validateForm = function() { var projectName = projectNameInput.val(); var valid = true; if (projectNameInputChanged) { if (projectList === null) { pendingFormValidation = true; return; } projectNameStatus.empty(); if (!/^[a-zA-Z0-9\-_]+$/.test(projectName) || projectList[projectName]) { projectNameInput.addClass("input-error"); $('').appendTo(projectNameStatus); projectNameValid = false; valid = false; if (projectList[projectName]) { projectNameSublabel.text("Project already exists"); } else { projectNameSublabel.text("Must contain only A-Z 0-9 _ -"); } } else { projectNameInput.removeClass("input-error"); $('').appendTo(projectNameStatus); projectNameSublabel.text("Must contain only A-Z 0-9 _ -"); projectNameValid = true; } projectNameLastChecked = projectName; } valid = projectNameValid; var projectType = $(".projects-dialog-screen-create-type.selected").data('type'); if (projectType === 'copy') { if (!copyProject) { valid = false; } } else if (projectType === 'clone') { var repo = projectRepoInput.val(); var validRepo = /^(?:git|ssh|https?|[\d\w\.\-_]+@[\w\.]+):(?:\/\/)?[\w\.@:\/~_-]+\.git(?:\/?|\#[\d\w\.\-_]+?)$/.test(repo); if (!validRepo) { if (projectRepoChanged) { projectRepoInput.addClass("input-error"); } valid = false; } else { projectRepoInput.removeClass("input-error"); } if (/^(?:ssh|[\d\w\.\-_]+@[\w\.]+):(?:\/\/)?/.test(repo)) { $(".projects-dialog-screen-create-row-creds").hide(); $(".projects-dialog-screen-create-row-passphrase").show(); $(".projects-dialog-screen-create-row-sshkey").show(); if ( !getSelectedSSHKey(projectRepoSSHKeySelect) ) { valid = false; } } else if (/^https?:\/\//.test(repo)) { $(".projects-dialog-screen-create-row-creds").show(); $(".projects-dialog-screen-create-row-passphrase").hide(); $(".projects-dialog-screen-create-row-sshkey").hide(); } else { $(".projects-dialog-screen-create-row-creds").show(); $(".projects-dialog-screen-create-row-passphrase").hide(); $(".projects-dialog-screen-create-row-sshkey").hide(); } } else if (projectType === 'empty') { var flowFile = projectFlowFileInput.val(); if (flowFile === "" || !/\.json$/.test(flowFile)) { valid = false; if (!projectFlowFileInput.hasClass("input-error")) { projectFlowFileInput.addClass("input-error"); projectFlowFileInput.next().empty().append(''); } } else { if (projectFlowFileInput.hasClass("input-error")) { projectFlowFileInput.removeClass("input-error"); projectFlowFileInput.next().empty(); } } var encryptionState = $("input[name=projects-encryption-type]:checked").val(); if (encryptionState === 'enabled') { var encryptionKeyType = $("input[name=projects-encryption-key]:checked").val(); if (encryptionKeyType === 'custom') { valid = valid && emptyProjectCredentialInput.val()!=='' } } } $("#projects-dialog-create").prop('disabled',!valid).toggleClass('disabled ui-button-disabled ui-state-disabled',!valid); } row = $('').appendTo(container); var createAsEmpty = $('').appendTo(row); // var createAsCopy = $('').appendTo(row); var createAsClone = $('').appendTo(row); row.find(".projects-dialog-screen-create-type").click(function(evt) { evt.preventDefault(); $(".projects-dialog-screen-create-type").removeClass('selected'); $(this).addClass('selected'); $(".projects-dialog-screen-create-row").hide(); $(".projects-dialog-screen-create-row-"+$(this).data('type')).show(); validateForm(); projectNameInput.focus(); }) row = $('').appendTo(container); $('').appendTo(row); var subrow = $('').appendTo(row); projectNameInput = $('').appendTo(subrow); var projectNameStatus = $('').appendTo(subrow); var projectNameInputChanged = false; var projectNameLastChecked = ""; var projectNameValid; var checkProjectName; var autoInsertedName = ""; projectNameInput.on("change keyup paste",function() { projectNameInputChanged = (projectNameInput.val() !== projectNameLastChecked); if (checkProjectName) { clearTimeout(checkProjectName); } else if (projectNameInputChanged) { projectNameStatus.empty(); $('').appendTo(projectNameStatus); if (projectNameInput.val() === '') { validateForm(); return; } } checkProjectName = setTimeout(function() { validateForm(); checkProjectName = null; },300) }); projectNameSublabel = $('').appendTo(row).find("small"); // Empty Project row = $('').appendTo(container); $('').appendTo(row); projectSummaryInput = $('').appendTo(row); $('').appendTo(row); row = $('').appendTo(container); $('').appendTo(row); subrow = $('').appendTo(row); projectFlowFileInput = $('').val("flow.json") .on("change keyup paste",validateForm) .appendTo(subrow); $('').appendTo(subrow); $('').appendTo(row); row = $('').appendTo(container); $('').appendTo(row); var credentialsBox = $('