1
0
mirror of https://github.com/node-red/node-red.git synced 2023-10-10 13:36:53 +02:00
node-red/editor/js/ui/projects.js

1625 lines
90 KiB
JavaScript
Raw Normal View History

2017-09-20 11:30:07 +02:00
/**
* 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 = $('<div class="projects-dialog-screen-start-hero"></div>');
$('<span><i class="fa fa-files-o fa-2x"></i> &nbsp; &nbsp; <i class="fa fa-long-arrow-right fa-2x"></i> &nbsp; &nbsp; <i class="fa fa-archive fa-2x"></i></span>').appendTo(migrateProjectHeader)
$('<hr>').appendTo(migrateProjectHeader);
var createProjectOptions = {};
2017-09-20 11:30:07 +02:00
screens = {
'welcome': {
content: function(options) {
2017-09-20 11:30:07 +02:00
var container = $('<div class="projects-dialog-screen-start"></div>');
migrateProjectHeader.appendTo(container);
var body = $('<div class="projects-dialog-screen-start-body"></div>').appendTo(container);
$('<p>').text("Hello! We have introduced 'projects' to Node-RED.").appendTo(body);
$('<p>').text("This is a new way for you to manage your flow files and includes version control of your flows.").appendTo(body);
$('<p>').text("To get started you can create your first project using your current flow files in a few easy steps.").appendTo(body);
$('<p>').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 = $('<div style="margin: 30px"></div>').appendTo(container);
// var createNew = $('<button class="editor-button"><i class="fa fa-archive fa-2x"></i><i class="fa fa-plus-circle" style="position:absolute"></i><br/>Create a project</button>').appendTo(buttons);
// createNew.click(function(e) {
// e.preventDefault();
// show('create');
// });
// var openExisting = $('<button class="editor-button"><i class="fa fa-folder-open-o fa-2x"></i><br/>Open a project</button>').appendTo(buttons);
// openExisting.click(function(e) {
// e.preventDefault();
// show('open')
// });
return container;
},
buttons: [
{
// 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 = $('<div class="projects-dialog-screen-start"></div>');
migrateProjectHeader.appendTo(container);
var body = $('<div class="projects-dialog-screen-start-body"></div>').appendTo(container);
$('<p>').text("Step 1 : Setup your version control client").appendTo(body);
$('<p>').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);
$('<p>').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);
$('<p>').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 = $('<div class="form-row"></div>').appendTo(body);
$('<label for="">Username</label>').appendTo(row);
gitUsernameInput = $('<input type="text">').val(currentGitSettings.user.name||"").appendTo(row);
// $('<div style="position:relative;"></div>').text("This does not need to be your real name").appendTo(row);
row = $('<div class="form-row"></div>').appendTo(body);
$('<label for="">Email</label>').appendTo(row);
gitEmailInput = $('<input type="text">').val(currentGitSettings.user.email||"").appendTo(row);
// $('<div style="position:relative;"></div>').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 = $('<div class="projects-dialog-screen-start"></div>');
migrateProjectHeader.appendTo(container);
var body = $('<div class="projects-dialog-screen-start-body"></div>').appendTo(container);
$('<p>').text("Step 2 : Create your project").appendTo(body);
$('<p>').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);
$('<p>').text("You can create multiple projects and quickly switch between them from the editor.").appendTo(body);
$('<p>').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");
$('<i style="margin-top: 8px;" class="fa fa-exclamation-triangle"></i>').appendTo(projectNameStatus);
projectNameValid = false;
valid = false;
} else {
projectNameInput.removeClass("input-error");
$('<i style="margin-top: 8px;" class="fa fa-check"></i>').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 = $('<div class="form-row"></div>').appendTo(body);
$('<label for="projects-dialog-screen-create-project-name">Project name</label>').appendTo(row);
var subrow = $('<div style="position:relative;"></div>').appendTo(row);
projectNameInput = $('<input id="projects-dialog-screen-create-project-name" type="text"></input>').val(createProjectOptions.name||"").appendTo(subrow);
var projectNameStatus = $('<div class="projects-dialog-screen-input-status"></div>').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();
$('<img src="red/images/spin.svg"/>').appendTo(projectNameStatus);
if (projectNameInput.val() === '') {
validateForm();
return;
}
}
checkProjectName = setTimeout(function() {
validateForm();
checkProjectName = null;
},300)
});
projectNameSublabel = $('<label class="projects-edit-form-sublabel"><small>Must contain only A-Z 0-9 _ -</small></label>').appendTo(row).find("small");
// Empty Project
row = $('<div class="form-row projects-dialog-screen-create-row projects-dialog-screen-create-row-empty"></div>').appendTo(body);
$('<label for="projects-dialog-screen-create-project-desc">Description</label>').appendTo(row);
projectSummaryInput = $('<input id="projects-dialog-screen-create-project-desc" type="text">').val(createProjectOptions.summary||"").appendTo(row);
$('<label class="projects-edit-form-sublabel"><small>Optional</small></label>').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 = $('<div class="projects-dialog-screen-start"></div>');
migrateProjectHeader.appendTo(container);
var body = $('<div class="projects-dialog-screen-start-body"></div>').appendTo(container);
$('<p>').text("Step 3 : Setup encryption of your credentials file").appendTo(body);
// $('<p>').text("Your flow credentials file can be encrypted to keep its contents secure.").appendTo(body);
if (RED.settings.flowEncryptionType === 'disabled') {
$('<p>').text("Your flow credentials file is not currently encrypted.").appendTo(body);
$('<p>').text("That means its contents, such as passwords and access tokens, can be read by anyone with access to the file.").appendTo(body);
$('<p>').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') {
$('<p>').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') {
$('<p>').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);
}
$('<p>').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 = $('<div class="form-row"></div>').appendTo(body);
// $('<label for="">Username</label>').appendTo(row);
// var gitUsernameInput = $('<input type="text">').val(currentGitSettings.user.name||"").appendTo(row);
// // $('<div style="position:relative;"></div>').text("This does not need to be your real name").appendTo(row);
//
// row = $('<div class="form-row"></div>').appendTo(body);
// $('<label for="">Email</label>').appendTo(row);
// var gitEmailInput = $('<input type="text">').val(currentGitSettings.user.email||"").appendTo(row);
// // $('<div style="position:relative;"></div>').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 = $('<div class="form-row projects-dialog-screen-create-row projects-dialog-screen-create-row-empty"></div>').appendTo(body);
$('<label>Credentials</label>').appendTo(row);
var credentialsBox = $('<div style="width: 550px">').appendTo(row);
var credentialsRightBox = $('<div style="min-height:150px; box-sizing: border-box; float: right; vertical-align: top; width: 331px; margin-left: -1px; padding: 15px; margin-top: -15px; border: 1px solid #ccc; border-radius: 3px; display: inline-block">').appendTo(credentialsBox);
var credentialsLeftBox = $('<div style="vertical-align: top; width: 220px; display: inline-block">').appendTo(credentialsBox);
var credentialsEnabledBox = $('<div class="form-row" style="padding: 7px 8px 3px 8px;border: 1px solid #ccc;border-radius: 4px;border-top-right-radius: 0;border-bottom-right-radius: 0;border-right-color: white;"></div>').appendTo(credentialsLeftBox);
$('<label class="projects-edit-form-inline-label" style="margin-left: 5px"><input type="radio" style="vertical-align: middle; margin-top:0; margin-right: 10px;" name="projects-encryption-type" value="enabled"> <i style="font-size: 1.4em; margin-right: 8px; vertical-align: middle; color: #888;" class="fa fa-lock"></i> <span style="vertical-align: middle;">Enable encryption</span></label>').appendTo(credentialsEnabledBox);
var credentialsDisabledBox = $('<div class="form-row" style="padding: 7px 8px 3px 8px;border: 1px solid white;border-radius: 4px;border-top-right-radius: 0;border-bottom-right-radius: 0;border-right-color: #ccc; "></div>').appendTo(credentialsLeftBox);
$('<label class="projects-edit-form-inline-label" style="margin-left: 5px"><input type="radio" style="vertical-align: middle; margin-top:0; margin-right: 10px;" name="projects-encryption-type" value="disabled"> <i style="font-size: 1.4em; margin-right: 8px; vertical-align: middle; color: #888;" class="fa fa-unlock"></i> <span style="vertical-align: middle;">Disable encryption</span></label>').appendTo(credentialsDisabledBox);
credentialsLeftBox.find("input[name=projects-encryption-type]").click(function(e) {
var val = $(this).val();
var toEnable;
var toDisable;
if (val === 'enabled') {
toEnable = credentialsEnabledBox;
toDisable = credentialsDisabledBox;
$(".projects-encryption-enabled-row").show();
$(".projects-encryption-disabled-row").hide();
if ($("input[name=projects-encryption-key]:checked").val() === 'custom') {
emptyProjectCredentialInput.focus();
}
} else {
toDisable = credentialsEnabledBox;
toEnable = credentialsDisabledBox;
$(".projects-encryption-enabled-row").hide();
$(".projects-encryption-disabled-row").show();
}
toEnable.css({
borderColor: "#ccc",
borderRightColor: "white"
});
toDisable.css({
borderColor: "white",
borderRightColor: "#ccc"
});
validateForm();
})
row = $('<div class="form-row projects-encryption-enabled-row"></div>').appendTo(credentialsRightBox);
$('<label class="projects-edit-form-inline-label '+((RED.settings.flowEncryptionType !== 'user')?'disabled':'')+'" style="margin-left: 5px"><input '+((RED.settings.flowEncryptionType !== 'user')?'disabled':'')+' type="radio" style="vertical-align: middle; margin-top:0; margin-right: 10px;" value="default" name="projects-encryption-key"> <span style="vertical-align: middle;">Copy over existing key</span></label>').appendTo(row);
row = $('<div class="form-row projects-encryption-enabled-row"></div>').appendTo(credentialsRightBox);
$('<label class="projects-edit-form-inline-label" style="margin-left: 5px"><input type="radio" style="vertical-align: middle; margin-top:0; margin-right: 10px;" value="custom" name="projects-encryption-key"> <span style="vertical-align: middle;">Use custom key</span></label>').appendTo(row);
row = $('<div class="projects-encryption-enabled-row"></div>').appendTo(credentialsRightBox);
emptyProjectCredentialInput = $('<input disabled type="password" style="margin-left: 25px; width: calc(100% - 30px);"></input>').appendTo(row);
emptyProjectCredentialInput.on("change keyup paste", validateForm);
row = $('<div class="form-row projects-encryption-disabled-row"></div>').hide().appendTo(credentialsRightBox);
$('<div class="" style="padding: 5px 20px;"><i class="fa fa-warning"></i> The credentials file will not be encrypted and its contents easily read</div>').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: [
{
// id: "clipboard-dialog-cancel",
text: "Back",
click: function() {
show('project-details');
}
},
{
id: "projects-dialog-create-encryption",
text: "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);
sendRequest({
url: "projects",
type: "POST",
requireCleanWorkspace: true,
handleAuthFail: false,
responses: {
200: function(data) {
createProjectOptions = {};
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 = $('<div class="projects-dialog-screen-start"></div>');
migrateProjectHeader.appendTo(container);
var body = $('<div class="projects-dialog-screen-start-body"></div>').appendTo(container);
$('<p>').text("You have successfully created your first project!").appendTo(body);
$('<p>').text("You can now continue to use Node-RED just as you always have.").appendTo(body);
$('<p>').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);
$('<p>').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);
2017-09-20 11:30:07 +02:00
return container;
},
buttons: [
{
text: "Done",
click: function() {
$( this ).dialog( "close" );
}
}
2017-09-20 11:30:07 +02:00
]
},
'create': (function() {
var projectNameInput;
2017-10-25 16:26:24 +02:00
var projectSummaryInput;
var projectFlowFileInput;
2017-09-20 11:30:07 +02:00
var projectSecretInput;
var projectSecretSelect;
var copyProject;
var projectRepoInput;
2017-10-25 16:26:24 +02:00
var emptyProjectCredentialInput;
var projectRepoUserInput;
var projectRepoPasswordInput;
var projectNameSublabel;
var projectRepoSSHKeySelect;
var projectRepoPassphrase;
var projectRepoRemoteName
var projectRepoBranch;
2017-09-20 11:30:07 +02:00
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();
}
})
});
2017-09-20 11:30:07 +02:00
var container = $('<div class="projects-dialog-screen-create"></div>');
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]) {
2017-09-20 11:30:07 +02:00
projectNameInput.addClass("input-error");
$('<i style="margin-top: 8px;" class="fa fa-exclamation-triangle"></i>').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");
$('<i style="margin-top: 8px;" class="fa fa-check"></i>').appendTo(projectNameStatus);
projectNameSublabel.text("Must contain only A-Z 0-9 _ -");
projectNameValid = true;
2017-09-20 11:30:07 +02:00
}
projectNameLastChecked = projectName;
2017-09-20 11:30:07 +02:00
}
valid = projectNameValid;
2017-09-20 11:30:07 +02:00
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) {
2017-09-20 11:30:07 +02:00
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();
}
2017-10-25 16:26:24 +02:00
} 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('<i style="margin-top: 8px;" class="fa fa-exclamation-triangle"></i>');
}
} else {
if (projectFlowFileInput.hasClass("input-error")) {
projectFlowFileInput.removeClass("input-error");
projectFlowFileInput.next().empty();
}
}
2017-10-25 16:26:24 +02:00
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()!==''
}
}
2017-09-20 11:30:07 +02:00
}
2017-09-20 11:30:07 +02:00
$("#projects-dialog-create").prop('disabled',!valid).toggleClass('disabled ui-button-disabled ui-state-disabled',!valid);
}
row = $('<div class="form-row button-group"></div>').appendTo(container);
var createAsEmpty = $('<button data-type="empty" class="editor-button projects-dialog-screen-create-type toggle selected"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-asterisk"></i><br/>Empty Project</button>').appendTo(row);
// var createAsCopy = $('<button data-type="copy" class="editor-button projects-dialog-screen-create-type toggle"><i class="fa fa-archive fa-2x"></i><i class="fa fa-long-arrow-right fa-2x"></i><i class="fa fa-archive fa-2x"></i><br/>Copy existing</button>').appendTo(row);
2017-09-20 11:30:07 +02:00
var createAsClone = $('<button data-type="clone" class="editor-button projects-dialog-screen-create-type toggle"><i class="fa fa-git fa-2x"></i><i class="fa fa-arrows-h fa-2x"></i><i class="fa fa-archive fa-2x"></i><br/>Clone repository</button>').appendTo(row);
row.find(".projects-dialog-screen-create-type").click(function(evt) {
evt.preventDefault();
$(".projects-dialog-screen-create-type").removeClass('selected');
$(this).addClass('selected');
$(".projects-dialog-screen-create-row").hide();
$(".projects-dialog-screen-create-row-"+$(this).data('type')).show();
validateForm();
projectNameInput.focus();
2017-09-20 11:30:07 +02:00
})
row = $('<div class="form-row"></div>').appendTo(container);
$('<label for="projects-dialog-screen-create-project-name">Project name</label>').appendTo(row);
var subrow = $('<div style="position:relative;"></div>').appendTo(row);
projectNameInput = $('<input id="projects-dialog-screen-create-project-name" type="text"></input>').appendTo(subrow);
var projectNameStatus = $('<div class="projects-dialog-screen-input-status"></div>').appendTo(subrow);
2017-09-20 11:30:07 +02:00
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();
$('<img src="red/images/spin.svg"/>').appendTo(projectNameStatus);
if (projectNameInput.val() === '') {
validateForm();
return;
}
}
checkProjectName = setTimeout(function() {
validateForm();
checkProjectName = null;
},300)
});
projectNameSublabel = $('<label class="projects-edit-form-sublabel"><small>Must contain only A-Z 0-9 _ -</small></label>').appendTo(row).find("small");
2017-09-20 11:30:07 +02:00
// Empty Project
row = $('<div class="form-row projects-dialog-screen-create-row projects-dialog-screen-create-row-empty"></div>').appendTo(container);
$('<label for="projects-dialog-screen-create-project-desc">Description</label>').appendTo(row);
projectSummaryInput = $('<input id="projects-dialog-screen-create-project-desc" type="text">').appendTo(row);
2017-10-25 16:26:24 +02:00
$('<label class="projects-edit-form-sublabel"><small>Optional</small></label>').appendTo(row);
row = $('<div class="form-row projects-dialog-screen-create-row projects-dialog-screen-create-row-empty"></div>').appendTo(container);
$('<label for="projects-dialog-screen-create-project-file">Flow file</label>').appendTo(row);
subrow = $('<div style="position:relative;"></div>').appendTo(row);
projectFlowFileInput = $('<input id="projects-dialog-screen-create-project-file" type="text">').val("flow.json")
2017-10-25 16:26:24 +02:00
.on("change keyup paste",validateForm)
.appendTo(subrow);
$('<div class="projects-dialog-screen-input-status"></div>').appendTo(subrow);
2017-10-25 16:26:24 +02:00
$('<label class="projects-edit-form-sublabel"><small>*.json</small></label>').appendTo(row);
row = $('<div class="form-row projects-dialog-screen-create-row projects-dialog-screen-create-row-empty"></div>').appendTo(container);
$('<label>Credentials</label>').appendTo(row);
var credentialsBox = $('<div style="width: 550px">').appendTo(row);
var credentialsRightBox = $('<div style="min-height:150px; box-sizing: border-box; float: right; vertical-align: top; width: 331px; margin-left: -1px; padding: 15px; margin-top: -15px; border: 1px solid #ccc; border-radius: 3px; display: inline-block">').appendTo(credentialsBox);
var credentialsLeftBox = $('<div style="vertical-align: top; width: 220px; display: inline-block">').appendTo(credentialsBox);
var credentialsEnabledBox = $('<div class="form-row" style="padding: 7px 8px 3px 8px;border: 1px solid #ccc;border-radius: 4px;border-top-right-radius: 0;border-bottom-right-radius: 0;border-right-color: white;"></div>').appendTo(credentialsLeftBox);
$('<label class="projects-edit-form-inline-label" style="margin-left: 5px"><input type="radio" checked style="vertical-align: middle; margin-top:0; margin-right: 10px;" name="projects-encryption-type" value="enabled"> <i style="font-size: 1.4em; margin-right: 8px; vertical-align: middle; color: #888;" class="fa fa-lock"></i> <span style="vertical-align: middle;">Enable encryption</span></label>').appendTo(credentialsEnabledBox);
var credentialsDisabledBox = $('<div class="form-row" style="padding: 7px 8px 3px 8px;border: 1px solid white;border-radius: 4px;border-top-right-radius: 0;border-bottom-right-radius: 0;border-right-color: #ccc; "></div>').appendTo(credentialsLeftBox);
$('<label class="projects-edit-form-inline-label" style="margin-left: 5px"><input type="radio" style="vertical-align: middle; margin-top:0; margin-right: 10px;" name="projects-encryption-type" value="disabled"> <i style="font-size: 1.4em; margin-right: 8px; vertical-align: middle; color: #888;" class="fa fa-unlock"></i> <span style="vertical-align: middle;">Disable encryption</span></label>').appendTo(credentialsDisabledBox);
credentialsLeftBox.find("input[name=projects-encryption-type]").click(function(e) {
var val = $(this).val();
var toEnable;
var toDisable;
if (val === 'enabled') {
toEnable = credentialsEnabledBox;
toDisable = credentialsDisabledBox;
$(".projects-encryption-enabled-row").show();
$(".projects-encryption-disabled-row").hide();
if ($("input[name=projects-encryption-key]:checked").val() === 'custom') {
emptyProjectCredentialInput.focus();
}
2017-10-25 16:26:24 +02:00
} else {
toDisable = credentialsEnabledBox;
toEnable = credentialsDisabledBox;
$(".projects-encryption-enabled-row").hide();
$(".projects-encryption-disabled-row").show();
}
toEnable.css({
borderColor: "#ccc",
borderRightColor: "white"
});
toDisable.css({
borderColor: "white",
borderRightColor: "#ccc"
})
validateForm();
})
row = $('<div class="form-row projects-encryption-enabled-row"></div>').appendTo(credentialsRightBox);
$('<label class="projects-edit-form-inline-label">Encryption key</label>').appendTo(row);
// row = $('<div class="projects-encryption-enabled-row"></div>').appendTo(credentialsRightBox);
emptyProjectCredentialInput = $('<input type="password"></input>').appendTo(row);
2017-10-25 16:26:24 +02:00
emptyProjectCredentialInput.on("change keyup paste", validateForm);
$('<label class="projects-edit-form-sublabel"><small>A phrase to secure your credentials with</small></label>').appendTo(row);
2017-10-25 16:26:24 +02:00
row = $('<div class="form-row projects-encryption-disabled-row"></div>').hide().appendTo(credentialsRightBox);
$('<div class="" style="padding: 5px 20px;"><i class="fa fa-warning"></i> The credentials file will not be encrypted and its contents easily read</div>').appendTo(row);
2017-10-25 16:26:24 +02:00
credentialsRightBox.find("input[name=projects-encryption-key]").click(function() {
var val = $(this).val();
emptyProjectCredentialInput.attr("disabled",val === 'default');
if (val === "custom") {
emptyProjectCredentialInput.focus();
}
2017-10-25 16:26:24 +02:00
validateForm();
})
2017-09-20 11:30:07 +02:00
// Copy Project
// row = $('<div class="hide form-row projects-dialog-screen-create-row projects-dialog-screen-create-row-copy"></div>').appendTo(container);
// $('<label> Select project to copy</label>').appendTo(row);
// createProjectList({
// height: "250px",
// small: true,
// select: function(project) {
// copyProject = project;
// var projectName = projectNameInput.val();
// if (projectName === "" || projectName === autoInsertedName) {
// autoInsertedName = project.name+"-copy";
// projectNameInput.val(autoInsertedName);
// }
// validateForm();
// }
// }).appendTo(row);
2017-09-20 11:30:07 +02:00
// Clone Project
row = $('<div class="hide form-row projects-dialog-screen-create-row projects-dialog-screen-create-row-clone"></div>').appendTo(container);
$('<label for="projects-dialog-screen-create-project-repo">Git repository URL</label>').appendTo(row);
projectRepoInput = $('<input id="projects-dialog-screen-create-project-repo" type="text" placeholder="https://git.example.com/path/my-project.git"></input>').appendTo(row);
$('<label class="projects-edit-form-sublabel"><small>https:// or ssh://</small></label>').appendTo(row);
2017-09-20 11:30:07 +02:00
var projectRepoChanged = false;
projectRepoInput.on("change keyup paste",function() {
projectRepoChanged = true;
2017-09-20 11:30:07 +02:00
var repo = $(this).val();
var m = /\/([^/]+)\.git/.exec(repo);
if (m) {
var projectName = projectNameInput.val();
if (projectName === "" || projectName === autoInsertedName) {
autoInsertedName = m[1];
projectNameInput.val(autoInsertedName);
projectNameInput.change();
2017-09-20 11:30:07 +02:00
}
}
validateForm();
});
row = $('<div class="hide form-row projects-dialog-screen-create-row projects-dialog-screen-create-row-clone projects-dialog-screen-create-row-creds"></div>').hide().appendTo(container);
var subrow = $('<div style="width: calc(50% - 10px); display:inline-block;"></div>').appendTo(row);
$('<label for="projects-dialog-screen-create-project-repo-user">Username</label>').appendTo(subrow);
projectRepoUserInput = $('<input id="projects-dialog-screen-create-project-repo-user" type="text"></input>').appendTo(subrow);
subrow = $('<div style="width: calc(50% - 10px); margin-left: 20px; display:inline-block;"></div>').appendTo(row);
$('<label for="projects-dialog-screen-create-project-repo-pass">Password</label>').appendTo(subrow);
projectRepoPasswordInput = $('<input id="projects-dialog-screen-create-project-repo-pass" type="password"></input>').appendTo(subrow);
row = $('<div class="form-row projects-dialog-screen-create-row projects-dialog-screen-create-row-sshkey"></div>').hide().appendTo(container);
$('<label for="projects-dialog-screen-create-project-repo-passphrase">SSH Private key file</label>').appendTo(row);
projectRepoSSHKeySelect = createSSHKeyList({
height: "120px",
selectAction: function(entry, header) {
$('.projects-dialog-sshkey-list-entry').removeClass('selected');
header.addClass('selected');
}
}).appendTo(row);
row = $('<div class="form-row projects-dialog-screen-create-row projects-dialog-screen-create-row-passphrase"></div>').hide().appendTo(container);
$('<label for="projects-dialog-screen-create-project-repo-passphrase">SSH key passphrase</label>').appendTo(row);
projectRepoPassphrase = $('<input id="projects-dialog-screen-create-project-repo-passphrase" type="password" style="width: calc(100% - 250px);"></input>').appendTo(row);
// row = $('<div style="width: calc(50% - 10px); display:inline-block;" class="form-row projects-dialog-screen-create-row projects-dialog-screen-create-row-clone"></div>').hide().appendTo(container);
// $('<label for="projects-dialog-screen-create-project-repo-remote-name">Remote name</label>').appendTo(row);
// projectRepoRemoteName = $('<input id="projects-dialog-screen-create-project-repo-remote-name" type="text" style="width: 100%;"></input>').val("origin").appendTo(row);
//
// row = $('<div style="width: calc(50% - 10px); margin-left: 20px; display:inline-block;" class="form-row projects-dialog-screen-create-row projects-dialog-screen-create-row-clone"></div>').hide().appendTo(container);
// $('<label for="projects-dialog-screen-create-project-repo-branch">Branch</label>').appendTo(row);
// projectRepoBranch = $('<input id="projects-dialog-screen-create-project-repo-branch" type="text"></input>').val('master').appendTo(row);
// // Secret - clone
// row = $('<div class="hide form-row projects-dialog-screen-create-row projects-dialog-screen-create-row-clone"></div>').appendTo(container);
// $('<label>Credentials encryption key</label>').appendTo(row);
// projectSecretInput = $('<input type="text"></input>').appendTo(row);
2017-09-20 11:30:07 +02:00
2017-10-25 16:26:24 +02:00
createAsEmpty.click();
setTimeout(function() {
projectNameInput.focus();
},50);
2017-09-20 11:30:07 +02:00
return container;
},
buttons: [
{
// id: "clipboard-dialog-cancel",
text: RED._("common.label.cancel"),
click: function() {
$( this ).dialog( "close" );
}
},
{
id: "projects-dialog-create",
text: "Create project", // TODO: nls
class: "primary disabled",
disabled: true,
click: function() {
var projectType = $(".projects-dialog-screen-create-type.selected").data('type');
var projectData = {
name: projectNameInput.val(),
}
if (projectType === 'empty') {
2017-10-25 16:26:24 +02:00
projectData.summary = projectSummaryInput.val();
projectData.files = {
flow: projectFlowFileInput.val()
};
var encryptionState = $("input[name=projects-encryption-type]:checked").val();
if (encryptionState === 'enabled') {
var encryptionKeyType = $("input[name=projects-encryption-key]:checked").val();
if (encryptionKeyType === 'custom') {
projectData.credentialSecret = emptyProjectCredentialInput.val();
} else {
// If 'use default', leave projectData.credentialSecret blank - as that will trigger
// it to use the default (TODO: if its set...)
}
} else {
// Disabled encryption by explicitly setting credSec to false
projectData.credentialSecret = false;
}
2017-09-20 11:30:07 +02:00
} else if (projectType === 'copy') {
projectData.copy = copyProject.name;
} else if (projectType === 'clone') {
// projectData.credentialSecret = projectSecretInput.val();
var repoUrl = projectRepoInput.val();
var metaData = {};
if (/^(?:ssh|[\d\w\.\-_]+@[\w\.]+):(?:\/\/)?/.test(repoUrl)) {
var selected = getSelectedSSHKey(projectRepoSSHKeySelect);
if ( selected && selected.name ) {
projectData.git = {
remotes: {
'origin': {
url: repoUrl,
key_file: selected.name,
passphrase: projectRepoPassphrase.val()
}
}
};
}
else {
console.log("Error! Can't get selected SSH key path.");
return;
}
2017-09-20 11:30:07 +02:00
}
else {
projectData.git = {
remotes: {
'origin': {
url: repoUrl,
username: projectRepoUserInput.val(),
password: projectRepoPasswordInput.val()
}
}
};
2017-09-20 11:30:07 +02:00
}
}
RED.deploy.setDeployInflight(true);
RED.projects.settings.switchProject(projectData.name);
2017-09-20 11:30:07 +02:00
sendRequest({
url: "projects",
type: "POST",
requireCleanWorkspace: true,
2017-11-23 01:27:13 +01:00
handleAuthFail: false,
2017-09-20 11:30:07 +02:00
responses: {
200: function(data) {
dialog.dialog( "close" );
2017-09-20 11:30:07 +02:00
},
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");
},
2017-09-20 11:30:07 +02:00
'git_auth_failed': function(error) {
projectRepoUserInput.addClass("input-error");
projectRepoPasswordInput.addClass("input-error");
2017-09-20 11:30:07 +02:00
// getRepoAuthDetails(req);
projectRepoSSHKeySelect.addClass("input-error");
projectRepoPassphrase.addClass("input-error");
2017-09-20 11:30:07 +02:00
console.log("git auth error",error);
},
'unexpected_error': function(error) {
console.log("unexpected_error",error)
}
}
}
},projectData).always(function() {
RED.deploy.setDeployInflight(false);
})
2017-09-20 11:30:07 +02:00
// if (projectType === 'empty') {
// show('credentialSecret');
// } else if (projectType === 'copy') {
// show('copy');
// } else if (projectType === 'clone') {
// show('clone');
// }
// var projectName = projectNameInput.val().trim();
// var projectRepoEnabled = projectRepoEnabledInput.prop('checked');
// var projectRepo = projectRepoInput.val().trim();
// if (projectName !== '') {
// var req = {
// name:projectName
// };
// if (projectRepoEnabled && projectRepo !== '') {
// req.remote = projectRepo;
// }
// console.log(req);
// sendRequest({
// url: "projects",
// type: "POST",
// responses: {
// 200: function(data) {
// console.log("Success!",data);
// },
// 400: {
// 'project_exists': function(error) {
// console.log("already exists");
// },
// 'git_error': function(error) {
// console.log("git error",error);
// },
// 'git_auth_failed': function(error) {
// // getRepoAuthDetails(req);
// console.log("git auth error",error);
// },
// 'unexpected_error': function(error) {
// console.log("unexpected_error",error)
// }
// }
// }
// },req)
// }
// $( this ).dialog( "close" );
}
}
]
}
})(),
'open': {
content: function() {
return createProjectList({
canSelectActive: false,
dblclick: function() {
$("#projects-dialog-open").click();
},
select: function() {
$("#projects-dialog-open").prop('disabled',false).removeClass('disabled ui-button-disabled ui-state-disabled');
2017-09-20 11:30:07 +02:00
}
})
},
buttons: [
{
// id: "clipboard-dialog-cancel",
text: RED._("common.label.cancel"),
click: function() {
$( this ).dialog( "close" );
}
},
{
id: "projects-dialog-open",
text: "Open project", // TODO: nls
class: "primary disabled",
disabled: true,
click: function() {
switchProject(selectedProject.name,function(err,data) {
if (err) {
if (err.error === 'credentials_load_failed') {
dialog.dialog( "close" );
} else {
console.log("unexpected_error",err)
}
} else {
dialog.dialog( "close" );
}
})
}
}
]
},
'delete': {
content: function() {
return createProjectList({
canSelectActive: false,
dblclick: function() {
$("#projects-dialog-delete").click();
},
select: function() {
$("#projects-dialog-delete").prop('disabled',false).removeClass('disabled ui-button-disabled ui-state-disabled');
}
})
},
buttons: [
{
// id: "clipboard-dialog-cancel",
text: RED._("common.label.cancel"),
click: function() {
$( this ).dialog( "close" );
}
},
{
id: "projects-dialog-delete",
text: "Delete project", // TODO: nls
class: "primary disabled",
disabled: true,
click: function() {
deleteProject(selectedProject.name,function(err,data) {
if (err) {
if (err.error === 'credentials_load_failed') {
dialog.dialog( "close" );
} else {
console.log("unexpected_error",err)
}
} else {
dialog.dialog( "close" );
}
})
}
}
]
2017-09-20 11:30:07 +02:00
}
}
}
function switchProject(name,done) {
RED.deploy.setDeployInflight(true);
RED.projects.settings.switchProject(name);
2017-09-20 11:30:07 +02:00
sendRequest({
url: "projects/"+name,
type: "PUT",
requireCleanWorkspace: true,
2017-09-20 11:30:07 +02:00
responses: {
200: function(data) {
done(null,data);
},
400: {
'credentials_load_failed': function(error) {
done(error,null);
},
'unexpected_error': function(error) {
done(error,null);
}
},
}
},{active:true}).always(function() {
RED.deploy.setDeployInflight(false);
})
}
function deleteProject(name,done) {
sendRequest({
url: "projects/"+name,
type: "DELETE",
responses: {
200: function(data) {
done(null,data);
},
400: {
'unexpected_error': function(error) {
done(error,null);
}
}
}
});
}
2017-09-20 11:30:07 +02:00
function show(s,options) {
if (!dialog) {
RED.projects.init();
}
var screen = screens[s];
var container = screen.content(options);
2017-09-20 11:30:07 +02:00
dialogBody.empty();
dialog.dialog('option','buttons',screen.buttons);
dialogBody.append(container);
dialog.dialog('option','title',screen.title||"");
dialog.dialog("open");
dialog.dialog({position: { 'my': 'center top', 'at': 'center top+10%', 'of': window }});
2017-09-20 11:30:07 +02:00
}
var selectedProject = null;
function createProjectList(options) {
options = options||{};
var height = options.height || "300px";
selectedProject = null;
var container = $('<div></div>',{style:"min-height: "+height+"; height: "+height+";"});
var list = $('<ol>',{class:"projects-dialog-project-list", style:"height:"+height}).appendTo(container).editableList({
addButton: false,
scrollOnAdd: false,
addItem: function(row,index,entry) {
var header = $('<div></div>',{class:"projects-dialog-project-list-entry"}).appendTo(row);
$('<span class="projects-dialog-project-list-entry-icon"><i class="fa fa-archive"></i></span>').appendTo(header);
$('<span class="projects-dialog-project-list-entry-name" style=""></span>').text(entry.name).appendTo(header);
if (activeProject && activeProject.name === entry.name) {
header.addClass("projects-list-entry-current");
$('<span class="projects-dialog-project-list-entry-current">current</span>').appendTo(header);
if (options.canSelectActive === false) {
// active project cannot be selected; so skip the rest
return
}
}
header.addClass("selectable");
row.click(function(evt) {
$('.projects-dialog-project-list-entry').removeClass('selected');
header.addClass('selected');
selectedProject = entry;
if (options.select) {
options.select(entry);
}
})
if (options.dblclick) {
row.dblclick(function(evt) {
evt.preventDefault();
options.dblclick();
})
}
}
});
if (options.small) {
list.addClass("projects-dialog-project-list-small")
}
$.getJSON("projects", function(data) {
data.projects.forEach(function(project) {
list.editableList('addItem',{name:project});
});
})
return container;
}
function createSSHKeyList(options) {
options = options || {};
var minHeight = "33px";
var maxHeight = options.height || "120px";
var container = $('<div></div>',{style:"max-height: "+maxHeight+";"});
var sshkeyList = $('<ol>',{class:"projects-dialog-sshkey-list", style:"max-height:"+maxHeight+";min-height:"+minHeight+";"}).appendTo(container).editableList({
addButton: false,
scrollOnAdd: false,
addItem: function(row,index,entry) {
var header = $('<div></div>',{class:"projects-dialog-sshkey-list-entry"}).appendTo(row);
$('<span class="projects-dialog-sshkey-list-entry-icon"><i class="fa fa-key"></i></span>').appendTo(header);
$('<span class="projects-dialog-sshkey-list-entry-name" style=""></span>').text(entry.name).appendTo(header);
var deleteButton = $('<span/>',{class:"projects-dialog-sshkey-list-entry-icon projects-dialog-sshkey-list-button-remove editor-button editor-button-small"})
.hide()
.appendTo(header)
.click(function(evt) {
evt.preventDefault();
console.log('deleteButton --- click');
if ( options.deleteAction ) {
options.deleteAction(entry, header);
}
return false;
});
$('<i/>',{class:"fa fa-trash-o"}).appendTo(deleteButton);
header.addClass("selectable");
row.click(function(evt) {
if ( !deleteButton.is(":visible") ) {
if ( options.selectAction ) {
options.selectAction(entry, header);
}
$.data(container[0], 'selected', entry);
}
return false;
})
}
});
$.getJSON("settings/user/keys", function(data) {
data.keys.forEach(function(key) {
if ( sshkeyList ) {
sshkeyList.editableList('addItem',key);
}
else {
console.log('[create] Error! selectedSSHKey is not set up.');
}
});
});
if ( sshkeyList ) {
sshkeyList.addClass("projects-dialog-sshkey-list-small");
$.data(container[0], 'selected', null);
$.data(container[0], 'sshkeys', sshkeyList);
}
return container;
}
function getSelectedSSHKey(container) {
var selected = $.data(container[0], 'selected');
if ( container && selected ) {
return selected;
}
else {
return null;
}
}
function refreshSSHKeyList(container) {
var sshkeyList = $.data(container[0], 'sshkeys');
if ( container && sshkeyList ) {
sshkeyList.empty();
$.getJSON("settings/user/keys", function(data) {
var keyList = $.data(container[0], 'sshkeys');
data.keys.forEach(function(key) {
if ( keyList ) {
keyList.editableList('addItem',key);
}
else {
console.log('[refresh] Error! selectedSSHKey is not set up.');
}
});
});
}
}
2017-09-20 11:30:07 +02:00
function sendRequest(options,body) {
// dialogBody.hide();
console.log(options.url,body);
if (options.requireCleanWorkspace && RED.nodes.dirty()) {
var message = 'You have undeployed changes that will be lost. Do you want to continue?';
var alwaysCallback;
var cleanNotification = RED.notify(message,{
type:"info",
fixed: true,
modal: true,
buttons: [
{
//id: "node-dialog-delete",
//class: 'leftButton',
text: RED._("common.label.cancel"),
click: function() {
cleanNotification.close();
if (options.cancel) {
options.cancel();
}
if (alwaysCallback) {
alwaysCallback();
}
}
},{
text: 'Continue',
click: function() {
cleanNotification.close();
delete options.requireCleanWorkspace;
sendRequest(options,body).always(function() {
if (alwaysCallback) {
alwaysCallback();
}
})
}
}
]
});
return {
always: function(done) { alwaysCallback = done; }
}
}
2017-09-20 11:30:07 +02:00
var start = Date.now();
// TODO: this is specific to the dialog-based requests
2017-09-20 11:30:07 +02:00
$(".projects-dialog-spinner").show();
$("#projects-dialog").parent().find(".ui-dialog-buttonset").children().css("visibility","hidden")
if (body) {
options.data = JSON.stringify(body);
options.contentType = "application/json; charset=utf-8";
}
var resultCallback;
var resultCallbackArgs;
return $.ajax(options).done(function(data,textStatus,xhr) {
if (options.responses && options.responses[200]) {
resultCallback = options.responses[200];
resultCallbackArgs = data;
}
}).fail(function(xhr,textStatus,err) {
if (options.responses && options.responses[xhr.status]) {
var responses = options.responses[xhr.status];
if (typeof responses === 'function') {
resultCallback = responses;
resultCallbackArgs = {error:responses.statusText};
return;
2017-11-23 01:27:13 +01:00
} else if (options.handleAuthFail !== false && xhr.responseJSON.error === 'git_auth_failed') {
var url = activeProject.git.remotes[options.remote||'origin'].fetch;
2017-11-23 01:27:13 +01:00
var message = $('<div>'+
'<div class="form-row">Authentication required for repository:</div>'+
'<div class="form-row"><div style="margin-left: 20px;">'+url+'</div></div>'+
'<div class="form-row"><label for="projects-user-auth-username">Username</label><input id="projects-user-auth-username" type="text"></input></div>'+
'<div class="form-row"><label for=projects-user-auth-password">Password</label><input id="projects-user-auth-password" type="password"></input></div>'+
'</div>');
var notification = RED.notify(message,{
type:"error",
fixed: true,
modal: true,
buttons: [
{
//id: "node-dialog-delete",
//class: 'leftButton',
text: RED._("common.label.cancel"),
click: function() {
notification.close();
}
},{
text: $('<span><i class="fa fa-refresh"></i> Retry</span>'),
click: function() {
var username = $('#projects-user-auth-username').val();
var password = $('#projects-user-auth-password').val();
body = body || {};
var authBody = {git:{remotes:{}}};
authBody.git.remotes[options.remote||'origin'] = {
username: username,
password: password
};
var done = function(err) {
if (err) {
console.log("Failed to update auth");
console.log(err);
} else {
sendRequest(options,body);
notification.close();
}
2017-11-23 01:27:13 +01:00
}
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);
}
},
}
},authBody);
2017-11-23 01:27:13 +01:00
}
}
]
2017-11-23 01:27:13 +01:00
});
return;
2017-09-20 11:30:07 +02:00
} else if (responses[xhr.responseJSON.error]) {
resultCallback = responses[xhr.responseJSON.error];
resultCallbackArgs = xhr.responseJSON;
return;
}
}
console.log("Unhandled error response:");
console.log(xhr);
console.log(textStatus);
console.log(err);
}).always(function() {
var delta = Date.now() - start;
delta = Math.max(0,500-delta);
2017-09-20 11:30:07 +02:00
setTimeout(function() {
// dialogBody.show();
$(".projects-dialog-spinner").hide();
$("#projects-dialog").parent().find(".ui-dialog-buttonset").children().css("visibility","")
if (resultCallback) {
resultCallback(resultCallbackArgs)
}
},delta);
});
}
function createBranchList(options) {
var branchFilterTerm = "";
var branchFilterCreateItem;
var branches = [];
var branchPrefix = "";
var container = $('<div class="projects-branch-list">').appendTo(options.container);
var branchFilter = $('<input type="text">').attr('placeholder',options.placeholder).appendTo(container).searchBox({
delay: 200,
change: function() {
branchFilterTerm = $(this).val();
if (/(\.\.|\/\.|[?*[~^: \\]|\/\/|\/.$|\/$)/.test(branchFilterTerm)) {
if (!branchFilterCreateItem.hasClass("input-error")) {
branchFilterCreateItem.addClass("input-error");
branchFilterCreateItem.find("i").addClass("fa-warning").removeClass("fa-code-fork");
}
branchFilterCreateItem.find("span").text("Invalid branch: "+branchPrefix+branchFilterTerm);
} else {
if (branchFilterCreateItem.hasClass("input-error")) {
branchFilterCreateItem.removeClass("input-error");
branchFilterCreateItem.find("i").removeClass("fa-warning").addClass("fa-code-fork");
}
branchFilterCreateItem.find(".sidebar-version-control-branch-list-entry-create-name").text(branchPrefix+branchFilterTerm);
}
branchList.editableList("filter");
}
});
var branchList = $("<ol>",{style:"height: 130px;"}).appendTo(container);
branchList.editableList({
addButton: false,
scrollOnAdd: false,
addItem: function(row,index,entry) {
var container = $('<div class="sidebar-version-control-branch-list-entry">').appendTo(row);
if (!entry.hasOwnProperty('commit')) {
branchFilterCreateItem = container;
$('<i class="fa fa-code-fork"></i>').appendTo(container);
$('<span>').text("Create branch:").appendTo(container);
$('<div class="sidebar-version-control-branch-list-entry-create-name" style="margin-left: 10px;">').text(entry.name).appendTo(container);
} else {
$('<i class="fa fa-code-fork"></i>').appendTo(container);
$('<span>').text(entry.name).appendTo(container);
if (entry.current) {
container.addClass("selected");
$('<span class="current"></span>').text(options.currentLabel||"current").appendTo(container);
}
}
container.click(function(evt) {
evt.preventDefault();
if ($(this).hasClass('input-error')) {
return;
}
var body = {};
if (!entry.hasOwnProperty('commit')) {
body.name = branchFilter.val();
body.create = true;
if (options.remote) {
body.name = options.remote()+"/"+body.name;
}
} else {
if ($(this).hasClass('selected')) {
body.current = true;
}
body.name = entry.name;
}
if (options.onselect) {
options.onselect(body);
}
});
},
filter: function(data) {
var isCreateEntry = (!data.hasOwnProperty('commit'));
return (
isCreateEntry &&
(
branchFilterTerm !== "" &&
branches.indexOf(branchPrefix+branchFilterTerm) === -1
)
) ||
(
!isCreateEntry &&
data.name.indexOf(branchFilterTerm) !== -1
);
}
});
return {
refresh: function(url) {
branchFilter.searchBox("value","");
branchList.editableList('empty');
var start = Date.now();
var spinner = addSpinnerOverlay(container).addClass("projects-dialog-spinner-contain");
if (options.remote) {
branchPrefix = options.remote()+"/";
} else {
branchPrefix = "";
}
sendRequest({
url: url,
type: "GET",
responses: {
0: function(error) {
console.log(error);
},
200: function(result) {
branches = result.branches;
result.branches.forEach(function(b) {
branchList.editableList('addItem',b);
});
branchList.editableList('addItem',{});
setTimeout(function() {
spinner.remove();
},Math.max(300-(Date.now() - start),0));
},
400: {
'unexpected_error': function(error) {
console.log(error);
}
}
}
})
},
// addItem: function(data) { branchList.editableList('addItem',data) },
filter: function() { branchList.editableList('filter') },
focus: function() { branchFilter.focus() }
}
}
function addSpinnerOverlay(container) {
var spinner = $('<div class="projects-dialog-spinner"><img src="red/images/spin.svg"/></div>').appendTo(container);
return spinner;
}
2017-09-20 11:30:07 +02:00
function init() {
dialog = $('<div id="projects-dialog" class="hide node-red-dialog projects-edit-form"><form class="form-horizontal"></form><div class="projects-dialog-spinner hide"><img src="red/images/spin.svg"/></div></div>')
.appendTo("body")
.dialog({
modal: true,
autoOpen: false,
width: 600,
resizable: false,
open: function(e) {
$(this).parent().find(".ui-dialog-titlebar-close").hide();
// $("#header-shade").show();
// $("#editor-shade").show();
// $("#palette-shade").show();
// $("#sidebar-shade").show();
},
close: function(e) {
// $("#header-shade").hide();
// $("#editor-shade").hide();
// $("#palette-shade").hide();
// $("#sidebar-shade").hide();
}
});
dialogBody = dialog.find("form");
RED.actions.add("core:new-project",RED.projects.newProject);
RED.actions.add("core:open-project",RED.projects.selectProject);
RED.actions.add("core:delete-project",RED.projects.deleteProject);
var projectsAPI = {
sendRequest:sendRequest,
createBranchList:createBranchList,
addSpinnerOverlay:addSpinnerOverlay,
createSSHKeyList:createSSHKeyList,
refreshSSHKeyList:refreshSSHKeyList
};
RED.projects.settings.init(projectsAPI);
RED.projects.userSettings.init(projectsAPI);
RED.sidebar.versionControl.init(projectsAPI);
2017-09-20 11:30:07 +02:00
initScreens();
// initSidebar();
2017-09-20 11:30:07 +02:00
}
function refresh(done) {
$.getJSON("projects",function(data) {
if (data.active) {
$.getJSON("projects/"+data.active, function(project) {
activeProject = project;
// updateProjectSummary();
// updateProjectDescription();
// updateProjectDependencies();
RED.sidebar.versionControl.refresh(true);
if (done) {
done(activeProject);
2017-09-20 11:30:07 +02:00
}
});
} else {
if (done) {
done(null);
}
2017-09-20 11:30:07 +02:00
}
});
}
2017-09-20 11:30:07 +02:00
return {
init: init,
showStartup: function() {
show('welcome');
},
newProject: function() {
if (!activeProject) {
show('welcome');
} else {
show('create')
}
2017-09-20 11:30:07 +02:00
},
selectProject: function() {
show('open')
},
deleteProject: function() {
show('delete')
},
showCredentialsPrompt: function() { //TODO: rename this function
RED.projects.settings.show('settings');
2017-09-20 11:30:07 +02:00
},
showFilesPrompt: function() { //TODO: rename this function
RED.projects.settings.show('settings');
},
// showSidebar: showSidebar,
refresh: refresh,
editProject: function() {
RED.projects.settings.show();
2017-09-20 11:30:07 +02:00
},
getActiveProject: function() {
return activeProject;
}
}
})();