/**
* 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)
$('').appendTo(migrateProjectHeader);
var createProjectOptions = {};
screens = {
'welcome': {
content: function(options) {
var container = $('');
migrateProjectHeader.appendTo(container);
var body = $('').appendTo(container);
$('
').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);
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("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("Create your project").appendTo(body);
$('
').text("A project is maintained as a Git repository. 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;
var projectNameValid = /^[a-zA-Z0-9\-_]+$/.test(projectName);
if (projectNameInputChanged) {
projectNameStatus.empty();
if (!projectNameValid) {
projectNameInput.addClass("input-error");
$('').appendTo(projectNameStatus);
valid = false;
} else {
projectNameInput.removeClass("input-error");
$('').appendTo(projectNameStatus);
}
projectNameLastChecked = projectName;
}
valid = projectNameValid;
$("#projects-dialog-create-name").prop('disabled',!valid).toggleClass('disabled ui-button-disabled ui-state-disabled',!valid);
}
var row = $('
').text("Setup encryption of your credentials file").appendTo(body);
if (options.existingProject) {
$('
').text("Your flow credentials file can be encrypted to keep its contents secure.").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 === '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 = $('
').appendTo(row);
var credentialsRightBox = $('
').appendTo(credentialsBox);
var credentialsLeftBox = $('
The credentials file will not be encrypted and its contents easily read
').appendTo(row);
credentialsRightBox.find("input[name=projects-encryption-key]").click(function() {
var val = $(this).val();
emptyProjectCredentialInput.attr("disabled",val === 'default');
if (val === "custom") {
emptyProjectCredentialInput.focus();
}
validateForm();
});
setTimeout(function() {
credentialsLeftBox.find("input[name=projects-encryption-type][value=enabled]").click();
if (RED.settings.flowEncryptionType !== 'user') {
credentialsRightBox.find("input[name=projects-encryption-key][value=custom]").click();
} else {
credentialsRightBox.find("input[name=projects-encryption-key][value=default]").click();
}
validateForm();
},100);
return container;
},
buttons: function(options) {
return [
{
// id: "clipboard-dialog-cancel",
text: "Back",
click: function() {
show('default-files',options);
}
},
{
id: "projects-dialog-create-encryption",
text: options.existingProject?"Create project files":"Create project", // TODO: nls
class: "primary disabled",
disabled: true,
click: function() {
var encryptionState = $("input[name=projects-encryption-type]:checked").val();
if (encryptionState === 'enabled') {
var encryptionKeyType = $("input[name=projects-encryption-key]:checked").val();
if (encryptionKeyType === 'custom') {
createProjectOptions.credentialSecret = emptyProjectCredentialInput.val();
} else {
// If 'use existing', leave createProjectOptions.credentialSecret blank
// - that will trigger it to use the existing key
// TODO: this option should be disabled if encryption is disabled
}
} else {
// Disabled encryption by explicitly setting credSec to false
createProjectOptions.credentialSecret = false;
}
RED.deploy.setDeployInflight(true);
RED.projects.settings.switchProject(createProjectOptions.name);
var method = "POST";
var url = "projects";
if (options.existingProject) {
createProjectOptions.initialise = true;
method = "PUT";
url = "projects/"+activeProject.name;
}
var self = this;
sendRequest({
url: url,
type: method,
requireCleanWorkspace: true,
handleAuthFail: false,
responses: {
200: function(data) {
createProjectOptions = {};
if (options.existingProject) {
$( self ).dialog( "close" );
} else {
show('create-success');
}
},
400: {
'project_exists': function(error) {
console.log("already exists");
},
'git_error': function(error) {
console.log("git error",error);
},
'git_connection_failed': function(error) {
projectRepoInput.addClass("input-error");
},
'git_auth_failed': function(error) {
projectRepoUserInput.addClass("input-error");
projectRepoPasswordInput.addClass("input-error");
// getRepoAuthDetails(req);
console.log("git auth error",error);
},
'unexpected_error': function(error) {
console.log("unexpected_error",error)
}
}
}
},createProjectOptions).always(function() {
RED.deploy.setDeployInflight(false);
})
}
}
];
}
}
})(),
'create-success': {
content: function(options) {
var container = $('');
migrateProjectHeader.appendTo(container);
var body = $('').appendTo(container);
$('
').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 '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-sshkey").show();
// if ( !getSelectedSSHKey(projectRepoSSHKeySelect) ) {
// valid = false;
// }
} else if (/^https?:\/\//.test(repo)) {
$(".projects-dialog-screen-create-row-creds").show();
$(".projects-dialog-screen-create-row-sshkey").hide();
} else {
$(".projects-dialog-screen-create-row-creds").show();
$(".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 = $('
').appendTo(row);
var credentialsRightBox = $('
').appendTo(credentialsBox);
var credentialsLeftBox = $('