node-red/packages/node_modules/@node-red/editor-client/src/js/ui/projects/projects.js

2464 lines
142 KiB
JavaScript

/**
* 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;
function reportUnexpectedError(error) {
var notification;
if (error.code === 'git_missing_user') {
notification = RED.notify("<p>"+RED._("projects.errors.no-username-email")+"</p>",{
fixed: true,
type:'error',
buttons: [
{
text: RED._("common.label.cancel"),
click: function() {
notification.close();
}
},
{
text: RED._("projects.config-git"),
click: function() {
RED.userSettings.show('gitconfig');
notification.close();
}
}
]
})
} else {
console.log(error);
notification = RED.notify("<p>"+RED._("projects.errors.unexpected")+":</p><p>"+error.message+"</p><small>"+RED._("projects.errors.code")+": "+error.code+"</small>",{
fixed: true,
modal: true,
type: 'error',
buttons: [
{
text: RED._("common.label.close"),
click: function() {
notification.close();
}
}
]
})
}
}
var screens = {};
function initScreens() {
var migrateProjectHeader = $('<div class="red-ui-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 = {};
screens = {
'welcome': {
content: function(options) {
var container = $('<div class="red-ui-projects-dialog-screen-start"></div>');
migrateProjectHeader.appendTo(container);
var body = $('<div class="red-ui-projects-dialog-screen-start-body"></div>').appendTo(container);
$('<p>').text(RED._("projects.welcome.hello")).appendTo(body);
$('<p>').text(RED._("projects.welcome.desc0")).appendTo(body);
$('<p>').text(RED._("projects.welcome.desc1")).appendTo(body);
$('<p>').text(RED._("projects.welcome.desc2")).appendTo(body);
var row = $('<div style="text-align: center"></div>').appendTo(body);
var createAsEmpty = $('<button data-type="empty" class="red-ui-button red-ui-projects-dialog-button red-ui-projects-dialog-screen-create-type"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-asterisk"></i><br/>'+RED._("projects.welcome.create")+'</button>').appendTo(row);
var createAsClone = $('<button data-type="clone" class="red-ui-button red-ui-projects-dialog-button red-ui-projects-dialog-screen-create-type"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-git"></i><br/>'+RED._("projects.welcome.clone")+'</button>').appendTo(row);
createAsEmpty.on("click", function(e) {
e.preventDefault();
createProjectOptions = {
action: "create"
}
show('git-config');
})
createAsClone.on("click", function(e) {
e.preventDefault();
createProjectOptions = {
action: "clone"
}
show('git-config');
})
return container;
},
buttons: [
{
// id: "red-ui-clipboard-dialog-cancel",
text: RED._("projects.welcome.openExistingProject"),
class: "secondary",
click: function() {
createProjectOptions = {
action: "open"
}
show('git-config');
}
},
{
// id: "red-ui-clipboard-dialog-cancel",
text: RED._("projects.welcome.not-right-now"),
click: function() {
createProjectOptions = {};
$( this ).dialog( "close" );
}
}
]
},
'git-config': (function() {
var gitUsernameInput;
var gitEmailInput;
return {
content: function(options) {
var isGlobalConfig = false;
var existingGitSettings = RED.settings.get('git');
if (existingGitSettings && existingGitSettings.user) {
existingGitSettings = existingGitSettings.user;
} else if (RED.settings.git && RED.settings.git.globalUser) {
isGlobalConfig = true;
existingGitSettings = RED.settings.git.globalUser;
}
var validateForm = function() {
var name = gitUsernameInput.val().trim();
var email = gitEmailInput.val().trim();
var valid = name.length > 0 && email.length > 0;
$("#red-ui-projects-dialog-git-config").prop('disabled',!valid).toggleClass('disabled ui-button-disabled ui-state-disabled',!valid);
}
var container = $('<div class="red-ui-projects-dialog-screen-start"></div>');
migrateProjectHeader.appendTo(container);
var body = $('<div class="red-ui-projects-dialog-screen-start-body"></div>').appendTo(container);
$('<p>').text(RED._("projects.git-config.setup")).appendTo(body);
$('<p>').text(RED._("projects.git-config.desc0")).appendTo(body);
$('<p>').text(RED._("projects.git-config.desc1")).appendTo(body);
if (isGlobalConfig) {
$('<p>').text(RED._("projects.git-config.desc2")).appendTo(body);
}
$('<p>').text(RED._("projects.git-config.desc3")).appendTo(body);
var row = $('<div class="form-row"></div>').appendTo(body);
$('<label for="">'+RED._("projects.git-config.username")+'</label>').appendTo(row);
gitUsernameInput = $('<input type="text">').val((existingGitSettings&&existingGitSettings.name)||"").appendTo(row);
// $('<div style="position:relative;"></div>').text("This does not need to be your real name").appendTo(row);
gitUsernameInput.on("change keyup paste",validateForm);
row = $('<div class="form-row"></div>').appendTo(body);
$('<label for="">'+RED._("projects.git-config.email")+'</label>').appendTo(row);
gitEmailInput = $('<input type="text">').val((existingGitSettings&&existingGitSettings.email)||"").appendTo(row);
gitEmailInput.on("change keyup paste",validateForm);
// $('<div style="position:relative;"></div>').text("Something something email").appendTo(row);
setTimeout(function() {
gitUsernameInput.trigger("focus");
validateForm();
},50);
return container;
},
buttons: [
{
// id: "red-ui-clipboard-dialog-cancel",
text: RED._("common.label.back"),
click: function() {
show('welcome');
}
},
{
id: "red-ui-projects-dialog-git-config",
text: RED._("common.label.next"),
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);
if (createProjectOptions.action === "create") {
show('project-details');
} else if (createProjectOptions.action === "clone") {
show('clone-project');
} else if (createProjectOptions.action === "open") {
show('create',{screen:'open'})
}
}
}
]
};
})(),
'project-details': (function() {
var projectNameInput;
var projectSummaryInput;
return {
content: function(options) {
var projectList = null;
var projectNameValid;
var pendingFormValidation = false;
$.getJSON("projects", function(data) {
projectList = {};
data.projects.forEach(function(p) {
projectList[p] = true;
if (pendingFormValidation) {
pendingFormValidation = false;
validateForm();
}
})
});
var container = $('<div class="red-ui-projects-dialog-screen-start"></div>');
migrateProjectHeader.appendTo(container);
var body = $('<div class="red-ui-projects-dialog-screen-start-body"></div>').appendTo(container);
$('<p>').text(RED._("projects.project-details.create")).appendTo(body);
$('<p>').text(RED._("projects.project-details.desc0")).appendTo(body);
$('<p>').text(RED._("projects.project-details.desc1")).appendTo(body);
$('<p>').text(RED._("projects.project-details.desc2")).appendTo(body);
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");
$('<i style="margin-top: 8px;" class="fa fa-exclamation-triangle"></i>').appendTo(projectNameStatus);
projectNameValid = false;
valid = false;
if (projectList[projectName]) {
projectNameSublabel.text(RED._("projects.project-details.already-exists"));
} else {
projectNameSublabel.text(RED._("projects.project-details.must-contain"));
}
} else {
projectNameInput.removeClass("input-error");
$('<i style="margin-top: 8px;" class="fa fa-check"></i>').appendTo(projectNameStatus);
projectNameSublabel.text(RED._("projects.project-details.must-contain"));
projectNameValid = true;
}
projectNameLastChecked = projectName;
}
valid = projectNameValid;
$("#red-ui-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="red-ui-projects-dialog-screen-create-project-name">'+RED._("projects.project-details.project-name")+'</label>').appendTo(row);
var subrow = $('<div style="position:relative;"></div>').appendTo(row);
projectNameInput = $('<input id="red-ui-projects-dialog-screen-create-project-name" type="text"></input>').val(createProjectOptions.name||"").appendTo(subrow);
var projectNameStatus = $('<div class="red-ui-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="red-ui-projects-edit-form-sublabel"><small>'+RED._("projects.project-details.must-contain")+'</small></label>').appendTo(row).find("small");
// Empty Project
row = $('<div class="form-row red-ui-projects-dialog-screen-create-row red-ui-projects-dialog-screen-create-row-empty"></div>').appendTo(body);
$('<label for="red-ui-projects-dialog-screen-create-project-desc">'+RED._("projects.project-details.desc")+'</label>').appendTo(row);
projectSummaryInput = $('<input id="red-ui-projects-dialog-screen-create-project-desc" type="text">').val(createProjectOptions.summary||"").appendTo(row);
$('<label class="red-ui-projects-edit-form-sublabel"><small>'+RED._("projects.project-details.opt")+'</small></label>').appendTo(row);
setTimeout(function() {
projectNameInput.trigger("focus");
projectNameInput.trigger("change");
},50);
return container;
},
buttons: function(options) {
return [
{
text: RED._("common.label.back"),
click: function() {
show('git-config');
}
},
{
id: "red-ui-projects-dialog-create-name",
disabled: true,
text: RED._("common.label.next"),
class: "primary disabled",
click: function() {
createProjectOptions.name = projectNameInput.val();
createProjectOptions.summary = projectSummaryInput.val();
show('default-files', options);
}
}
]
}
};
})(),
'clone-project': (function() {
var projectNameInput;
var projectSummaryInput;
var projectFlowFileInput;
var projectSecretInput;
var projectSecretSelect;
var copyProject;
var projectRepoInput;
var projectCloneSecret;
var emptyProjectCredentialInput;
var projectRepoUserInput;
var projectRepoPasswordInput;
var projectNameSublabel;
var projectRepoSSHKeySelect;
var projectRepoPassphrase;
var projectRepoRemoteName
var projectRepoBranch;
var selectedProject;
return {
content: function(options) {
var container = $('<div class="red-ui-projects-dialog-screen-start"></div>');
migrateProjectHeader.appendTo(container);
var body = $('<div class="red-ui-projects-dialog-screen-start-body"></div>').appendTo(container);
$('<p>').text(RED._("projects.clone-project.clone")).appendTo(body);
$('<p>').text(RED._("projects.clone-project.desc0")).appendTo(body);
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 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");
$('<i style="margin-top: 8px;" class="fa fa-exclamation-triangle"></i>').appendTo(projectNameStatus);
projectNameValid = false;
valid = false;
if (projectList[projectName]) {
projectNameSublabel.text(RED._("projects.clone-project.already-exists"));
} else {
projectNameSublabel.text(RED._("projects.clone-project.must-contain"));
}
} else {
projectNameInput.removeClass("input-error");
$('<i style="margin-top: 8px;" class="fa fa-check"></i>').appendTo(projectNameStatus);
projectNameSublabel.text(RED._("projects.clone-project.must-contain"));
projectNameValid = true;
}
projectNameLastChecked = projectName;
}
valid = projectNameValid;
var repo = projectRepoInput.val();
// var validRepo = /^(?:file|git|ssh|https?|[\d\w\.\-_]+@[\w\.]+):(?:\/\/)?[\w\.@:\/~_-]+(?:\/?|\#[\d\w\.\-_]+?)$/.test(repo);
var validRepo = repo.length > 0 && !/\s/.test(repo);
if (/^https?:\/\/[^/]+@/i.test(repo)) {
$("#red-ui-projects-dialog-screen-create-project-repo-label small").text(RED._("projects.clone-project.no-info-in-url"));
validRepo = false;
}
if (!validRepo) {
if (projectRepoChanged) {
projectRepoInput.addClass("input-error");
}
valid = false;
} else {
projectRepoInput.removeClass("input-error");
}
if (/^https?:\/\//.test(repo)) {
$(".red-ui-projects-dialog-screen-create-row-creds").show();
$(".red-ui-projects-dialog-screen-create-row-sshkey").hide();
} else if (/^(?:ssh|[\S]+?@[\S]+?):(?:\/\/)?/.test(repo)) {
$(".red-ui-projects-dialog-screen-create-row-creds").hide();
$(".red-ui-projects-dialog-screen-create-row-sshkey").show();
// if ( !getSelectedSSHKey(projectRepoSSHKeySelect) ) {
// valid = false;
// }
} else {
$(".red-ui-projects-dialog-screen-create-row-creds").hide();
$(".red-ui-projects-dialog-screen-create-row-sshkey").hide();
}
$("#red-ui-projects-dialog-clone-project").prop('disabled',!valid).toggleClass('disabled ui-button-disabled ui-state-disabled',!valid);
}
var row;
row = $('<div class="form-row red-ui-projects-dialog-screen-create-row red-ui-projects-dialog-screen-create-row-empty red-ui-projects-dialog-screen-create-row-clone"></div>').appendTo(body);
$('<label for="red-ui-projects-dialog-screen-create-project-name">'+RED._("projects.clone-project.project-name")+'</label>').appendTo(row);
var subrow = $('<div style="position:relative;"></div>').appendTo(row);
projectNameInput = $('<input id="red-ui-projects-dialog-screen-create-project-name" type="text"></input>').appendTo(subrow);
var projectNameStatus = $('<div class="red-ui-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="red-ui-projects-edit-form-sublabel"><small>'+RED._("projects.clone-project.must-contain")+'</small></label>').appendTo(row).find("small");
row = $('<div class="form-row red-ui-projects-dialog-screen-create-row red-ui-projects-dialog-screen-create-row-clone"></div>').appendTo(body);
$('<label for="red-ui-projects-dialog-screen-create-project-repo">'+RED._("projects.clone-project.git-url")+'</label>').appendTo(row);
projectRepoInput = $('<input id="red-ui-projects-dialog-screen-create-project-repo" type="text" placeholder="https://git.example.com/path/my-project.git"></input>').appendTo(row);
$('<label id="red-ui-projects-dialog-screen-create-project-repo-label" class="red-ui-projects-edit-form-sublabel"><small>'+RED._("projects.clone-project.protocols")+'</small></label>').appendTo(row);
var projectRepoChanged = false;
var lastProjectRepo = "";
projectRepoInput.on("change keyup paste",function() {
projectRepoChanged = true;
var repo = $(this).val();
if (lastProjectRepo !== repo) {
$("#red-ui-projects-dialog-screen-create-project-repo-label small").text(RED._("projects.clone-project.protocols"));
}
lastProjectRepo = repo;
var m = /\/([^/]+?)(?:\.git)?$/.exec(repo);
if (m) {
var projectName = projectNameInput.val();
if (projectName === "" || projectName === autoInsertedName) {
autoInsertedName = m[1];
projectNameInput.val(autoInsertedName);
projectNameInput.trigger("change");
}
}
validateForm();
});
var cloneAuthRows = $('<div class="red-ui-projects-dialog-screen-create-row"></div>').appendTo(body);
row = $('<div class="form-row red-ui-projects-dialog-screen-create-row-auth-error"></div>').hide().appendTo(cloneAuthRows);
$('<div><i class="fa fa-warning"></i> '+RED._("projects.clone-project.auth-failed")+'</div>').appendTo(row);
// Repo credentials - username/password ----------------
row = $('<div class="hide form-row red-ui-projects-dialog-screen-create-row-creds"></div>').hide().appendTo(cloneAuthRows);
var subrow = $('<div style="width: calc(50% - 10px); display:inline-block;"></div>').appendTo(row);
$('<label for="red-ui-projects-dialog-screen-create-project-repo-user">'+RED._("projects.clone-project.username")+'</label>').appendTo(subrow);
projectRepoUserInput = $('<input id="red-ui-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="red-ui-projects-dialog-screen-create-project-repo-pass">'+RED._("projects.clone-project.passwd")+'</label>').appendTo(subrow);
projectRepoPasswordInput = $('<input style="width:100%" id="red-ui-projects-dialog-screen-create-project-repo-pass" type="password"></input>').appendTo(subrow);
projectRepoPasswordInput.typedInput({type:"cred"});
// -----------------------------------------------------
// Repo credentials - key/passphrase -------------------
row = $('<div class="form-row red-ui-projects-dialog-screen-create-row red-ui-projects-dialog-screen-create-row-sshkey"></div>').hide().appendTo(cloneAuthRows);
subrow = $('<div style="width: calc(50% - 10px); display:inline-block;"></div>').appendTo(row);
$('<label for="red-ui-projects-dialog-screen-create-project-repo-passphrase">'+RED._("projects.clone-project.ssh-key")+'</label>').appendTo(subrow);
projectRepoSSHKeySelect = $("<select>",{style:"width: 100%"}).appendTo(subrow);
$.getJSON("settings/user/keys", function(data) {
var count = 0;
data.keys.forEach(function(key) {
projectRepoSSHKeySelect.append($("<option></option>").val(key.name).text(key.name));
count++;
});
if (count === 0) {
projectRepoSSHKeySelect.addClass("input-error");
projectRepoSSHKeySelect.attr("disabled",true);
sshwarningRow.show();
} else {
projectRepoSSHKeySelect.removeClass("input-error");
projectRepoSSHKeySelect.attr("disabled",false);
sshwarningRow.hide();
}
});
subrow = $('<div style="width: calc(50% - 10px); margin-left: 20px; display:inline-block;"></div>').appendTo(row);
$('<label for="red-ui-projects-dialog-screen-create-project-repo-passphrase">'+RED._("projects.clone-project.passphrase")+'</label>').appendTo(subrow);
projectRepoPassphrase = $('<input id="red-ui-projects-dialog-screen-create-project-repo-passphrase" type="password"></input>').appendTo(subrow);
projectRepoPassphrase.typedInput({type:"cred"});
subrow = $('<div class="form-row red-ui-projects-dialog-screen-create-row red-ui-projects-dialog-screen-create-row-sshkey"></div>').appendTo(cloneAuthRows);
var sshwarningRow = $('<div class="red-ui-projects-dialog-screen-create-row-auth-error-no-keys"></div>').hide().appendTo(subrow);
$('<div class="form-row"><i class="fa fa-warning"></i> '+RED._("projects.clone-project.ssh-key-desc")+'</div>').appendTo(sshwarningRow);
subrow = $('<div style="text-align: center">').appendTo(sshwarningRow);
$('<button type="button" class="red-ui-button red-ui-projects-dialog-button">'+RED._("projects.clone-project.ssh-key-add")+'</button>').appendTo(subrow).on("click", function(e) {
e.preventDefault();
dialog.dialog( "close" );
RED.userSettings.show('gitconfig');
setTimeout(function() {
$("#user-settings-gitconfig-add-key").trigger("click");
},500);
});
// -----------------------------------------------------
// Secret - clone
row = $('<div class="form-row red-ui-projects-dialog-screen-create-row red-ui-projects-dialog-screen-create-row-clone"></div>').appendTo(body);
$('<label>'+RED._("projects.clone-project.credential-key")+'</label>').appendTo(row);
projectSecretInput = $('<input style="width: 100%" type="password"></input>').appendTo(row);
projectSecretInput.typedInput({type:"cred"});
return container;
},
buttons: function(options) {
return [
{
text: RED._("common.label.back"),
click: function() {
show('git-config');
}
},
{
id: "red-ui-projects-dialog-clone-project",
disabled: true,
text: RED._("common.label.clone"),
class: "primary disabled",
click: function() {
var projectType = $(".red-ui-projects-dialog-screen-create-type.selected").data('type');
var projectData = {
name: projectNameInput.val(),
}
projectData.credentialSecret = projectSecretInput.val();
var repoUrl = projectRepoInput.val();
var metaData = {};
if (/^(?:ssh|[\d\w\.\-_]+@[\w\.]+):(?:\/\/)?/.test(repoUrl)) {
var selected = projectRepoSSHKeySelect.val();//false;//getSelectedSSHKey(projectRepoSSHKeySelect);
if ( selected ) {
projectData.git = {
remotes: {
'origin': {
url: repoUrl,
keyFile: selected,
passphrase: projectRepoPassphrase.val()
}
}
};
}
else {
console.log(RED._("projects.clone-project.cant-get-ssh-key"));
return;
}
}
else {
projectData.git = {
remotes: {
'origin': {
url: repoUrl,
username: projectRepoUserInput.val(),
password: projectRepoPasswordInput.val()
}
}
};
}
$(".red-ui-projects-dialog-screen-create-row-auth-error").hide();
$("#red-ui-projects-dialog-screen-create-project-repo-label small").text(RED._("projects.clone-project.protocols"));
projectRepoUserInput.removeClass("input-error");
projectRepoPasswordInput.removeClass("input-error");
projectRepoSSHKeySelect.removeClass("input-error");
projectRepoPassphrase.removeClass("input-error");
RED.deploy.setDeployInflight(true);
RED.projects.settings.switchProject(projectData.name);
sendRequest({
url: "projects",
type: "POST",
handleAuthFail: false,
responses: {
200: function(data) {
dialog.dialog( "close" );
},
400: {
'project_exists': function(error) {
console.log(RED._("projects.clone-project.already-exists2"));
},
'git_error': function(error) {
console.log(RED._("projects.clone-project.git-error"),error);
},
'git_connection_failed': function(error) {
projectRepoInput.addClass("input-error");
$("#red-ui-projects-dialog-screen-create-project-repo-label small").text(RED._("projects.clone-project.connection-failed"));
},
'git_not_a_repository': function(error) {
projectRepoInput.addClass("input-error");
$("#red-ui-projects-dialog-screen-create-project-repo-label small").text(RED._("projects.clone-project.not-git-repo"));
},
'git_repository_not_found': function(error) {
projectRepoInput.addClass("input-error");
$("#red-ui-projects-dialog-screen-create-project-repo-label small").text(RED._("projects.clone-project.repo-not-found"));
},
'git_auth_failed': function(error) {
$(".red-ui-projects-dialog-screen-create-row-auth-error").show();
projectRepoUserInput.addClass("input-error");
projectRepoPasswordInput.addClass("input-error");
// getRepoAuthDetails(req);
projectRepoSSHKeySelect.addClass("input-error");
projectRepoPassphrase.addClass("input-error");
},
'missing_flow_file': function(error) {
// This is handled via a runtime notification.
dialog.dialog("close");
},
'missing_package_file': function(error) {
// This is handled via a runtime notification.
dialog.dialog("close");
},
'project_empty': function(error) {
// This is handled via a runtime notification.
dialog.dialog("close");
},
'credentials_load_failed': function(error) {
// This is handled via a runtime notification.
dialog.dialog("close");
},
'*': function(error) {
reportUnexpectedError(error);
$( dialog ).dialog( "close" );
}
}
}
},projectData).then(function() {
RED.menu.setDisabled('menu-item-projects-open',false);
RED.menu.setDisabled('menu-item-projects-settings',false);
RED.events.emit("project:change", {name:name});
}).always(function() {
setTimeout(function() {
RED.deploy.setDeployInflight(false);
},500);
})
}
}
]
}
}
})(),
'default-files': (function() {
var projectFlowFileInput;
var projectCredentialFileInput;
return {
content: function(options) {
var container = $('<div class="red-ui-projects-dialog-screen-start"></div>');
migrateProjectHeader.appendTo(container);
var body = $('<div class="red-ui-projects-dialog-screen-start-body"></div>').appendTo(container);
$('<p>').text(RED._("projects.default-files.create")).appendTo(body);
$('<p>').text(RED._("projects.default-files.desc0")).appendTo(body);
$('<p>').text(RED._("projects.default-files.desc1")).appendTo(body);
if (!options.existingProject && RED.settings.files) {
$('<p>').text(RED._("projects.default-files.desc2")).appendTo(body);
}
var validateForm = function() {
var valid = true;
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>');
}
projectCredentialFileInput.text("");
if (!projectCredentialFileInput.hasClass("input-error")) {
projectCredentialFileInput.addClass("input-error");
projectCredentialFileInput.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();
}
if (projectCredentialFileInput.hasClass("input-error")) {
projectCredentialFileInput.removeClass("input-error");
projectCredentialFileInput.next().empty();
}
projectCredentialFileInput.text(flowFile.substring(0,flowFile.length-5)+"_cred.json");
}
$("#red-ui-projects-dialog-create-default-files").prop('disabled',!valid).toggleClass('disabled ui-button-disabled ui-state-disabled',!valid);
}
var row = $('<div class="form-row"></div>').appendTo(body);
$('<label for="red-ui-projects-dialog-screen-create-project-file">'+RED._("projects.default-files.flow-file")+'</label>').appendTo(row);
var subrow = $('<div style="position:relative;"></div>').appendTo(row);
var defaultFlowFile = (createProjectOptions.files &&createProjectOptions.files.flow) || (RED.settings.files && RED.settings.files.flow) || "flows.json";
projectFlowFileInput = $('<input id="red-ui-projects-dialog-screen-create-project-file" type="text">').val(defaultFlowFile)
.on("change keyup paste",validateForm)
.appendTo(subrow);
$('<div class="red-ui-projects-dialog-screen-input-status"></div>').appendTo(subrow);
$('<label class="red-ui-projects-edit-form-sublabel"><small>*.json</small></label>').appendTo(row);
var defaultCredentialsFile = (createProjectOptions.files &&createProjectOptions.files.credentials) || (RED.settings.files && RED.settings.files.credentials) || "flows_cred.json";
row = $('<div class="form-row"></div>').appendTo(body);
$('<label for="red-ui-projects-dialog-screen-create-project-credfile">'+RED._("projects.default-files.credentials-file")+'</label>').appendTo(row);
subrow = $('<div style="position:relative;"></div>').appendTo(row);
projectCredentialFileInput = $('<div style="width: 100%" class="uneditable-input" id="red-ui-projects-dialog-screen-create-project-credentials">').text(defaultCredentialsFile)
.appendTo(subrow);
$('<div class="red-ui-projects-dialog-screen-input-status"></div>').appendTo(subrow);
setTimeout(function() {
projectFlowFileInput.trigger("focus");
validateForm();
},50);
return container;
},
buttons: function(options) {
return [
{
// id: "red-ui-clipboard-dialog-cancel",
text: RED._(options.existingProject ? "common.label.cancel": "common.label.back"),
click: function() {
if (options.existingProject) {
$(this).dialog('close');
} else {
show('project-details',options);
}
}
},
{
id: "red-ui-projects-dialog-create-default-files",
text: RED._("common.label.next"),
class: "primary",
click: function() {
createProjectOptions.files = {
flow: projectFlowFileInput.val(),
credentials: projectCredentialFileInput.text()
}
if (!options.existingProject) {
createProjectOptions.migrateFiles = true;
}
show('encryption-config',options);
}
}
]
}
}
})(),
'encryption-config': (function() {
var emptyProjectCredentialInput;
return {
content: function(options) {
var container = $('<div class="red-ui-projects-dialog-screen-start"></div>');
migrateProjectHeader.appendTo(container);
var body = $('<div class="red-ui-projects-dialog-screen-start-body"></div>').appendTo(container);
$('<p>').text(RED._("projects.encryption-config.setup")).appendTo(body);
if (options.existingProject) {
$('<p>').text(RED._("projects.encryption-config.desc0")).appendTo(body);
$('<p>').text(RED._("projects.encryption-config.desc1")).appendTo(body);
} else {
if (RED.settings.flowEncryptionType === 'disabled') {
$('<p>').text(RED._("projects.encryption-config.desc2")).appendTo(body);
$('<p>').text(RED._("projects.encryption-config.desc3")).appendTo(body);
$('<p>').text(RED._("projects.encryption-config.desc4")).appendTo(body);
} else {
if (RED.settings.flowEncryptionType === 'user') {
$('<p>').text(RED._("projects.encryption-config.desc5")).appendTo(body);
} else if (RED.settings.flowEncryptionType === 'system') {
$('<p>').text(RED._("projects.encryption-config.desc6")).appendTo(body);
}
$('<p>').text(RED._("projects.encryption-config.desc7")).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()!=='';
}
}
$("#red-ui-projects-dialog-create-encryption").prop('disabled',!valid).toggleClass('disabled ui-button-disabled ui-state-disabled',!valid);
}
var row = $('<div class="form-row red-ui-projects-dialog-screen-create-row red-ui-projects-dialog-screen-create-row-empty"></div>').appendTo(body);
$('<label>'+RED._("projects.encryption-config.credentials")+'</label>').appendTo(row);
var credentialsBox = $('<div class="red-ui-projects-dialog-credentials-box">').appendTo(row);
var credentialsRightBox = $('<div class="red-ui-projects-dialog-credentials-box-right">').appendTo(credentialsBox);
var credentialsLeftBox = $('<div class="red-ui-projects-dialog-credentials-box-left">').appendTo(credentialsBox);
var credentialsEnabledBox = $('<div class="form-row red-ui-projects-dialog-credentials-box-enabled"></div>').appendTo(credentialsLeftBox);
$('<label class="red-ui-projects-edit-form-inline-label"><input type="radio" name="projects-encryption-type" value="enabled"> <i class="fa fa-lock"></i> <span>'+RED._("projects.encryption-config.enable")+'</span></label>').appendTo(credentialsEnabledBox);
var credentialsDisabledBox = $('<div class="form-row red-ui-projects-dialog-credentials-box-disabled"></div>').appendTo(credentialsLeftBox);
$('<label class="red-ui-projects-edit-form-inline-label"><input type="radio" name="projects-encryption-type" value="disabled"> <i class="fa fa-unlock"></i> <span>'+RED._("projects.encryption-config.disable")+'</span></label>').appendTo(credentialsDisabledBox);
credentialsLeftBox.find("input[name=projects-encryption-type]").on("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.trigger("focus");
}
} else {
toDisable = credentialsEnabledBox;
toEnable = credentialsDisabledBox;
$(".projects-encryption-enabled-row").hide();
$(".projects-encryption-disabled-row").show();
}
toEnable.removeClass("disabled");
toDisable.addClass("disabled");
validateForm();
})
row = $('<div class="form-row projects-encryption-enabled-row"></div>').appendTo(credentialsRightBox);
$('<label class="red-ui-projects-edit-form-inline-label '+((RED.settings.flowEncryptionType !== 'user')?'disabled':'')+'" style="margin-left: 5px"><input '+((RED.settings.flowEncryptionType !== 'user')?RED._("projects.encryption-config.disabled"):'')+' type="radio" style="vertical-align: middle; margin-top:0; margin-right: 10px;" value="default" name="projects-encryption-key"> <span style="vertical-align: middle;">'+RED._("projects.encryption-config.copy")+'</span></label>').appendTo(row);
row = $('<div class="form-row projects-encryption-enabled-row"></div>').appendTo(credentialsRightBox);
$('<label class="red-ui-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;">'+RED._("projects.encryption-config.use-custom")+'</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.typedInput({type:"cred"});
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> '+RED._("projects.encryption-config.desc8")+'</div>').appendTo(row);
credentialsRightBox.find("input[name=projects-encryption-key]").on("click", function() {
var val = $(this).val();
emptyProjectCredentialInput.attr("disabled",val === 'default');
if (val === "custom") {
emptyProjectCredentialInput.trigger("focus");
}
validateForm();
});
setTimeout(function() {
credentialsLeftBox.find("input[name=projects-encryption-type][value=enabled]").trigger("click");
if (RED.settings.flowEncryptionType !== 'user') {
credentialsRightBox.find("input[name=projects-encryption-key][value=custom]").trigger("click");
} else {
credentialsRightBox.find("input[name=projects-encryption-key][value=default]").trigger("click");
}
validateForm();
},100);
return container;
},
buttons: function(options) {
return [
{
// id: "red-ui-clipboard-dialog-cancel",
text: RED._("common.label.back"),
click: function() {
show('default-files',options);
}
},
{
id: "red-ui-projects-dialog-create-encryption",
text: RED._(options.existingProject?"projects.encryption-config.create-project-files":"projects.encryption-config.create-project"),
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');
RED.menu.setDisabled('menu-item-projects-open',false);
RED.menu.setDisabled('menu-item-projects-settings',false);
}
},
400: {
'project_exists': function(error) {
console.log(RED._("projects.encryption-config.already-exists"));
},
'git_error': function(error) {
console.log(RED._("projects.encryption-config.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(RED._("projects.encryption-config.git-auth-error"),error);
},
'*': function(error) {
reportUnexpectedError(error);
$( dialog ).dialog( "close" );
}
}
}
},createProjectOptions).always(function() {
setTimeout(function() {
RED.deploy.setDeployInflight(false);
},500);
})
}
}
];
}
}
})(),
'create-success': {
content: function(options) {
var container = $('<div class="red-ui-projects-dialog-screen-start"></div>');
migrateProjectHeader.appendTo(container);
var body = $('<div class="red-ui-projects-dialog-screen-start-body"></div>').appendTo(container);
$('<p>').text(RED._("projects.create-success.success")).appendTo(body);
$('<p>').text(RED._("projects.create-success.desc0")).appendTo(body);
$('<p>').text(RED._("projects.create-success.desc1")).appendTo(body);
$('<p>').text(RED._("projects.create-success.desc2")).appendTo(body);
return container;
},
buttons: [
{
text: RED._("common.label.done"),
click: function() {
$( this ).dialog( "close" );
}
}
]
},
'create': (function() {
var projectNameInput;
var projectSummaryInput;
var projectFlowFileInput;
var projectSecretInput;
var projectSecretSelect;
var copyProject;
var projectRepoInput;
var projectCloneSecret;
var emptyProjectCredentialInput;
var projectRepoUserInput;
var projectRepoPasswordInput;
var projectNameSublabel;
var projectRepoSSHKeySelect;
var projectRepoPassphrase;
var projectRepoRemoteName
var projectRepoBranch;
var selectedProject;
return {
title: RED._("projects.create.projects"),
content: function(options) {
var projectList = null;
selectedProject = null;
var pendingFormValidation = false;
$.getJSON("projects", function(data) {
projectList = {};
data.projects.forEach(function(p) {
projectList[p] = true;
if (pendingFormValidation) {
pendingFormValidation = false;
validateForm();
}
})
});
var container = $('<div class="red-ui-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]) {
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(RED._("projects.create.already-exists"));
} else {
projectNameSublabel.text(RED._("projects.create.must-contain"));
}
} else {
projectNameInput.removeClass("input-error");
$('<i style="margin-top: 8px;" class="fa fa-check"></i>').appendTo(projectNameStatus);
projectNameSublabel.text(RED._("projects.create.must-contain"));
projectNameValid = true;
}
projectNameLastChecked = projectName;
}
valid = projectNameValid;
var projectType = $(".red-ui-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 = /^(?:file|git|ssh|https?|[\d\w\.\-_]+@[\w\.]+):(?:\/\/)?[\w\.@:\/~_-]+(?:\/?|\#[\d\w\.\-_]+?)$/.test(repo);
var validRepo = repo.length > 0 && !/\s/.test(repo);
if (/^https?:\/\/[^/]+@/i.test(repo)) {
$("#red-ui-projects-dialog-screen-create-project-repo-label small").text(RED._("projects.create.no-info-in-url"));
validRepo = false;
}
if (!validRepo) {
if (projectRepoChanged) {
projectRepoInput.addClass("input-error");
}
valid = false;
} else {
projectRepoInput.removeClass("input-error");
}
if (/^https?:\/\//.test(repo)) {
$(".red-ui-projects-dialog-screen-create-row-creds").show();
$(".red-ui-projects-dialog-screen-create-row-sshkey").hide();
} else if (/^(?:ssh|[\S]+?@[\S]+?):(?:\/\/)?/.test(repo)) {
$(".red-ui-projects-dialog-screen-create-row-creds").hide();
$(".red-ui-projects-dialog-screen-create-row-sshkey").show();
// if ( !getSelectedSSHKey(projectRepoSSHKeySelect) ) {
// valid = false;
// }
} else {
$(".red-ui-projects-dialog-screen-create-row-creds").hide();
$(".red-ui-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('<i style="margin-top: 8px;" class="fa fa-exclamation-triangle"></i>');
}
} 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()!==''
}
}
} else if (projectType === 'open') {
valid = !!selectedProject;
}
$("#red-ui-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 openProject = $('<button type="button" data-type="open" class="red-ui-button red-ui-projects-dialog-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-folder-open"></i><br/>'+RED._("projects.create.open")+'</button>').appendTo(row);
var createAsEmpty = $('<button type="button" data-type="empty" class="red-ui-button red-ui-projects-dialog-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-asterisk"></i><br/>'+RED._("projects.create.create")+'</button>').appendTo(row);
// var createAsCopy = $('<button type="button" data-type="copy" class="red-ui-button red-ui-projects-dialog-button red-ui-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);
var createAsClone = $('<button type="button" data-type="clone" class="red-ui-button red-ui-projects-dialog-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-git"></i><br/>'+RED._("projects.create.clone")+'</button>').appendTo(row);
// var createAsClone = $('<button type="button" data-type="clone" class="red-ui-button red-ui-projects-dialog-button red-ui-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(".red-ui-projects-dialog-screen-create-type").on("click", function(evt) {
evt.preventDefault();
container.find(".red-ui-projects-dialog-screen-create-type").removeClass('selected');
$(this).addClass('selected');
container.find(".red-ui-projects-dialog-screen-create-row").hide();
container.find(".red-ui-projects-dialog-screen-create-row-"+$(this).data('type')).show();
validateForm();
projectNameInput.trigger("focus");
switch ($(this).data('type')) {
case "open": $("#red-ui-projects-dialog-create").text(RED._("projects.create.open")); break;
case "empty": $("#red-ui-projects-dialog-create").text(RED._("projects.create.create")); break;
case "clone": $("#red-ui-projects-dialog-create").text(RED._("projects.create.clone")); break;
}
})
row = $('<div class="form-row red-ui-projects-dialog-screen-create-row red-ui-projects-dialog-screen-create-row-open"></div>').hide().appendTo(container);
createProjectList({
canSelectActive: false,
dblclick: function(project) {
selectedProject = project;
$("#red-ui-projects-dialog-create").trigger("click");
},
select: function(project) {
selectedProject = project;
validateForm();
},
delete: function(project) {
if (projectList) {
delete projectList[project.name];
}
selectedProject = null;
validateForm();
}
}).appendTo(row);
row = $('<div class="form-row red-ui-projects-dialog-screen-create-row red-ui-projects-dialog-screen-create-row-open"></div>').hide().appendTo(container);
$('<span style="display: flex; align-items: center;"><input style="padding:0; margin: 0 5px 0 0" checked type="checkbox" id="red-ui-projects-dialog-screen-clear-context"> <label for="red-ui-projects-dialog-screen-clear-context" style="padding:0; margin: 0"> <span data-i18n="projects.create.clearContext"></span></label></span>').appendTo(row).i18n();
row = $('<div class="form-row red-ui-projects-dialog-screen-create-row red-ui-projects-dialog-screen-create-row-empty red-ui-projects-dialog-screen-create-row-clone"></div>').appendTo(container);
$('<label for="red-ui-projects-dialog-screen-create-project-name">'+RED._("projects.create.project-name")+'</label>').appendTo(row);
var subrow = $('<div style="position:relative;"></div>').appendTo(row);
projectNameInput = $('<input id="red-ui-projects-dialog-screen-create-project-name" type="text"></input>').appendTo(subrow);
var projectNameStatus = $('<div class="red-ui-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="red-ui-projects-edit-form-sublabel"><small>'+RED._("projects.create.must-contain")+'</small></label>').appendTo(row).find("small");
// Empty Project
row = $('<div class="form-row red-ui-projects-dialog-screen-create-row red-ui-projects-dialog-screen-create-row-empty"></div>').appendTo(container);
$('<label for="red-ui-projects-dialog-screen-create-project-desc">'+RED._("projects.create.desc")+'</label>').appendTo(row);
projectSummaryInput = $('<input id="red-ui-projects-dialog-screen-create-project-desc" type="text">').appendTo(row);
$('<label class="red-ui-projects-edit-form-sublabel"><small>'+RED._("projects.create.opt")+'</small></label>').appendTo(row);
row = $('<div class="form-row red-ui-projects-dialog-screen-create-row red-ui-projects-dialog-screen-create-row-empty"></div>').appendTo(container);
$('<label for="red-ui-projects-dialog-screen-create-project-file">'+RED._("projects.create.flow-file")+'</label>').appendTo(row);
subrow = $('<div style="position:relative;"></div>').appendTo(row);
projectFlowFileInput = $('<input id="red-ui-projects-dialog-screen-create-project-file" type="text">').val("flows.json")
.on("change keyup paste",validateForm)
.appendTo(subrow);
$('<div class="red-ui-projects-dialog-screen-input-status"></div>').appendTo(subrow);
$('<label class="red-ui-projects-edit-form-sublabel"><small>*.json</small></label>').appendTo(row);
row = $('<div class="form-row red-ui-projects-dialog-screen-create-row red-ui-projects-dialog-screen-create-row-empty"></div>').appendTo(container);
$('<label>'+RED._("projects.create.credentials")+'</label>').appendTo(row);
var credentialsBox = $('<div class="red-ui-projects-dialog-credentials-box">').appendTo(row);
var credentialsRightBox = $('<div class="red-ui-projects-dialog-credentials-box-right">').appendTo(credentialsBox);
var credentialsLeftBox = $('<div class="red-ui-projects-dialog-credentials-box-left">').appendTo(credentialsBox);
var credentialsEnabledBox = $('<div class="form-row red-ui-projects-dialog-credentials-box-enabled"></div>').appendTo(credentialsLeftBox);
$('<label class="red-ui-projects-edit-form-inline-label"><input type="radio" name="projects-encryption-type" value="enabled" checked> <i class="fa fa-lock"></i> <span>'+RED._("projects.encryption-config.enable")+'</span></label>').appendTo(credentialsEnabledBox);
var credentialsDisabledBox = $('<div class="form-row red-ui-projects-dialog-credentials-box-disabled disabled"></div>').appendTo(credentialsLeftBox);
$('<label class="red-ui-projects-edit-form-inline-label"><input type="radio" name="projects-encryption-type" value="disabled"> <i class="fa fa-unlock"></i> <span>'+RED._("projects.encryption-config.disable")+'</span></label>').appendTo(credentialsDisabledBox);
credentialsLeftBox.find("input[name=projects-encryption-type]").on("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.trigger("focus");
}
} else {
toDisable = credentialsEnabledBox;
toEnable = credentialsDisabledBox;
$(".projects-encryption-enabled-row").hide();
$(".projects-encryption-disabled-row").show();
}
toEnable.removeClass("disabled");
toDisable.addClass("disabled");
validateForm();
})
row = $('<div class="form-row projects-encryption-enabled-row"></div>').appendTo(credentialsRightBox);
$('<label class="red-ui-projects-edit-form-inline-label">'+RED._("projects.create.encryption-key")+'</label>').appendTo(row);
// row = $('<div class="projects-encryption-enabled-row"></div>').appendTo(credentialsRightBox);
emptyProjectCredentialInput = $('<input type="password"></input>').appendTo(row);
emptyProjectCredentialInput.typedInput({type:"cred"});
emptyProjectCredentialInput.on("change keyup paste", validateForm);
$('<label class="red-ui-projects-edit-form-sublabel"><small>'+RED._("projects.create.desc0")+'</small></label>').appendTo(row);
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> '+RED._("projects.create.desc1")+'</div>').appendTo(row);
credentialsRightBox.find("input[name=projects-encryption-key]").on("click", function() {
var val = $(this).val();
emptyProjectCredentialInput.attr("disabled",val === 'default');
if (val === "custom") {
emptyProjectCredentialInput.trigger("focus");
}
validateForm();
})
// Clone Project
row = $('<div class="hide form-row red-ui-projects-dialog-screen-create-row red-ui-projects-dialog-screen-create-row-clone"></div>').appendTo(container);
$('<label for="red-ui-projects-dialog-screen-create-project-repo">'+RED._("projects.create.git-url")+'</label>').appendTo(row);
projectRepoInput = $('<input id="red-ui-projects-dialog-screen-create-project-repo" type="text" placeholder="https://git.example.com/path/my-project.git"></input>').appendTo(row);
$('<label id="red-ui-projects-dialog-screen-create-project-repo-label" class="red-ui-projects-edit-form-sublabel"><small>'+RED._("projects.create.protocols")+'</small></label>').appendTo(row);
var projectRepoChanged = false;
var lastProjectRepo = "";
projectRepoInput.on("change keyup paste",function() {
projectRepoChanged = true;
var repo = $(this).val();
if (lastProjectRepo !== repo) {
$("#red-ui-projects-dialog-screen-create-project-repo-label small").text(RED._("projects.create.protocols"));
}
lastProjectRepo = repo;
var m = /\/([^/]+?)(?:\.git)?$/.exec(repo);
if (m) {
var projectName = projectNameInput.val();
if (projectName === "" || projectName === autoInsertedName) {
autoInsertedName = m[1];
projectNameInput.val(autoInsertedName);
projectNameInput.trigger("change");
}
}
validateForm();
});
var cloneAuthRows = $('<div class="hide red-ui-projects-dialog-screen-create-row red-ui-projects-dialog-screen-create-row-clone"></div>').hide().appendTo(container);
row = $('<div class="form-row red-ui-projects-dialog-screen-create-row-auth-error"></div>').hide().appendTo(cloneAuthRows);
$('<div><i class="fa fa-warning"></i> '+RED._("projects.create.auth-failed")+'</div>').appendTo(row);
// Repo credentials - username/password ----------------
row = $('<div class="hide form-row red-ui-projects-dialog-screen-create-row-creds"></div>').hide().appendTo(cloneAuthRows);
var subrow = $('<div style="width: calc(50% - 10px); display:inline-block;"></div>').appendTo(row);
$('<label for="red-ui-projects-dialog-screen-create-project-repo-user">'+RED._("projects.create.username")+'</label>').appendTo(subrow);
projectRepoUserInput = $('<input id="red-ui-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="red-ui-projects-dialog-screen-create-project-repo-pass">'+RED._("projects.create.password")+'</label>').appendTo(subrow);
projectRepoPasswordInput = $('<input style="width:100%" id="red-ui-projects-dialog-screen-create-project-repo-pass" type="password"></input>').appendTo(subrow);
projectRepoPasswordInput.typedInput({type:"cred"});
// -----------------------------------------------------
// Repo credentials - key/passphrase -------------------
row = $('<div class="form-row red-ui-projects-dialog-screen-create-row red-ui-projects-dialog-screen-create-row-sshkey"></div>').hide().appendTo(cloneAuthRows);
subrow = $('<div style="width: calc(50% - 10px); display:inline-block;"></div>').appendTo(row);
$('<label for="red-ui-projects-dialog-screen-create-project-repo-passphrase">'+RED._("projects.create.ssh-key")+'</label>').appendTo(subrow);
projectRepoSSHKeySelect = $("<select>",{style:"width: 100%"}).appendTo(subrow);
$.getJSON("settings/user/keys", function(data) {
var count = 0;
data.keys.forEach(function(key) {
projectRepoSSHKeySelect.append($("<option></option>").val(key.name).text(key.name));
count++;
});
if (count === 0) {
projectRepoSSHKeySelect.addClass("input-error");
projectRepoSSHKeySelect.attr("disabled",true);
sshwarningRow.show();
} else {
projectRepoSSHKeySelect.removeClass("input-error");
projectRepoSSHKeySelect.attr("disabled",false);
sshwarningRow.hide();
}
});
subrow = $('<div style="width: calc(50% - 10px); margin-left: 20px; display:inline-block;"></div>').appendTo(row);
$('<label for="red-ui-projects-dialog-screen-create-project-repo-passphrase">'+RED._("projects.create.passphrase")+'</label>').appendTo(subrow);
projectRepoPassphrase = $('<input id="red-ui-projects-dialog-screen-create-project-repo-passphrase" type="password"></input>').appendTo(subrow);
projectRepoPassphrase.typedInput({type:"cred"});
subrow = $('<div class="form-row red-ui-projects-dialog-screen-create-row red-ui-projects-dialog-screen-create-row-sshkey"></div>').appendTo(cloneAuthRows);
var sshwarningRow = $('<div class="red-ui-projects-dialog-screen-create-row-auth-error-no-keys"></div>').hide().appendTo(subrow);
$('<div class="form-row"><i class="fa fa-warning"></i> '+RED._("projects.create.desc2")+'</div>').appendTo(sshwarningRow);
subrow = $('<div style="text-align: center">').appendTo(sshwarningRow);
$('<button type="button" class="red-ui-button red-ui-projects-dialog-button">'+RED._("projects.create.add-ssh-key")+'</button>').appendTo(subrow).on("click", function(e) {
e.preventDefault();
$('#red-ui-projects-dialog-cancel').trigger("click");
RED.userSettings.show('gitconfig');
setTimeout(function() {
$("#user-settings-gitconfig-add-key").trigger("click");
},500);
});
// -----------------------------------------------------
// Secret - clone
row = $('<div class="hide form-row red-ui-projects-dialog-screen-create-row red-ui-projects-dialog-screen-create-row-clone"></div>').appendTo(container);
$('<label>'+RED._("projects.create.credentials-encryption-key")+'</label>').appendTo(row);
projectSecretInput = $('<input style="width:100%" type="password"></input>').appendTo(row);
projectSecretInput.typedInput({type:"cred"});
switch(options.screen||"empty") {
case "empty": createAsEmpty.trigger("click"); break;
case "open": openProject.trigger("click"); break;
case "clone": createAsClone.trigger("click"); break;
}
setTimeout(function() {
if ((options.screen||"empty") !== "open") {
projectNameInput.trigger("focus");
} else {
$("#red-ui-projects-dialog-project-list-search").trigger("focus");
}
},50);
return container;
},
buttons: function(options) {
var initialLabel;
switch (options.screen||"empty") {
case "open": initialLabel = RED._("projects.create.open"); break;
case "empty": initialLabel = RED._("projects.create.create"); break;
case "clone": initialLabel = RED._("projects.create.clone"); break;
}
return [
{
id: "red-ui-projects-dialog-cancel",
text: RED._("common.label.cancel"),
click: function() {
$( this ).dialog( "close" );
}
},
{
id: "red-ui-projects-dialog-create",
text: initialLabel,
class: "primary disabled",
disabled: true,
click: function() {
var projectType = $(".red-ui-projects-dialog-screen-create-type.selected").data('type');
var projectData = {
name: projectNameInput.val(),
}
if (projectType === 'empty') {
projectData.summary = projectSummaryInput.val();
projectData.files = {
flow: projectFlowFileInput.val()
};
var encryptionState = $("input[name=projects-encryption-type]:checked").val();
if (encryptionState === 'enabled') {
projectData.credentialSecret = emptyProjectCredentialInput.val();
} else {
// Disabled encryption by explicitly setting credSec to false
projectData.credentialSecret = false;
}
} 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 = projectRepoSSHKeySelect.val();//false;//getSelectedSSHKey(projectRepoSSHKeySelect);
if ( selected ) {
projectData.git = {
remotes: {
'origin': {
url: repoUrl,
keyFile: selected,
passphrase: projectRepoPassphrase.val()
}
}
};
}
else {
console.log(RED._("projects.create.cant-get-ssh-key-path"));
return;
}
}
else {
projectData.git = {
remotes: {
'origin': {
url: repoUrl,
username: projectRepoUserInput.val(),
password: projectRepoPasswordInput.val()
}
}
};
}
} else if (projectType === 'open') {
var clearContext = $("#red-ui-projects-dialog-screen-clear-context").prop("checked")
return switchProject(selectedProject.name, clearContext, function(err,data) {
if (err) {
if (err.code !== 'credentials_load_failed') {
console.log(RED._("projects.create.unexpected_error"),err)
}
}
})
}
$(".red-ui-projects-dialog-screen-create-row-auth-error").hide();
$("#red-ui-projects-dialog-screen-create-project-repo-label small").text(RED._("projects.create.protocols"));
projectRepoUserInput.removeClass("input-error");
projectRepoPasswordInput.removeClass("input-error");
projectRepoSSHKeySelect.removeClass("input-error");
projectRepoPassphrase.removeClass("input-error");
RED.deploy.setDeployInflight(true);
RED.projects.settings.switchProject(projectData.name);
sendRequest({
url: "projects",
type: "POST",
handleAuthFail: false,
responses: {
200: function(data) {
dialog.dialog( "close" );
},
400: {
'project_exists': function(error) {
console.log(RED._("projects.create.already-exists-2"));
},
'git_error': function(error) {
console.log(RED._("projects.create.git-error"),error);
},
'git_connection_failed': function(error) {
projectRepoInput.addClass("input-error");
$("#red-ui-projects-dialog-screen-create-project-repo-label small").text(RED._("projects.create.con-failed"));
},
'git_not_a_repository': function(error) {
projectRepoInput.addClass("input-error");
$("#red-ui-projects-dialog-screen-create-project-repo-label small").text(RED._("projects.create.not-git"));
},
'git_repository_not_found': function(error) {
projectRepoInput.addClass("input-error");
$("#red-ui-projects-dialog-screen-create-project-repo-label small").text(RED._("projects.create.no-resource"));
},
'git_auth_failed': function(error) {
$(".red-ui-projects-dialog-screen-create-row-auth-error").show();
projectRepoUserInput.addClass("input-error");
projectRepoPasswordInput.addClass("input-error");
// getRepoAuthDetails(req);
projectRepoSSHKeySelect.addClass("input-error");
projectRepoPassphrase.addClass("input-error");
},
'missing_flow_file': function(error) {
// This is handled via a runtime notification.
dialog.dialog("close");
},
'missing_package_file': function(error) {
// This is handled via a runtime notification.
dialog.dialog("close");
},
'project_empty': function(error) {
// This is handled via a runtime notification.
dialog.dialog("close");
},
'credentials_load_failed': function(error) {
// This is handled via a runtime notification.
dialog.dialog("close");
},
'*': function(error) {
reportUnexpectedError(error);
$( dialog ).dialog( "close" );
}
}
}
},projectData).then(function() {
RED.events.emit("project:change", {name:name});
}).always(function() {
setTimeout(function() {
RED.deploy.setDeployInflight(false);
},500);
})
}
}
]
}
}
})()
}
}
function switchProject(name,clearContext,done) {
RED.deploy.setDeployInflight(true);
RED.projects.settings.switchProject(name);
sendRequest({
url: "projects/"+name,
type: "PUT",
responses: {
200: function(data) {
done(null,data);
},
400: {
'credentials_load_failed': function(data) {
dialog.dialog( "close" );
RED.events.emit("project:change", {name:name});
done(null,data);
},
'*': done
},
}
},{active:true, clearContext:clearContext}).then(function() {
dialog.dialog( "close" );
RED.events.emit("project:change", {name:name});
}).always(function() {
setTimeout(function() {
RED.deploy.setDeployInflight(false);
},500);
})
}
function deleteProject(row,name,done) {
var cover = $('<div class="red-ui-projects-dialog-project-list-entry-delete-confirm"></div>').on("click", function(evt) { evt.stopPropagation(); }).appendTo(row);
$('<span>').text(RED._("projects.delete.confirm")).appendTo(cover);
$('<button type="button" class="red-ui-button red-ui-projects-dialog-button">'+RED._("common.label.cancel")+'</button>')
.appendTo(cover)
.on("click", function(e) {
e.stopPropagation();
cover.remove();
done(true);
});
$('<button type="button" class="red-ui-button red-ui-projects-dialog-button primary">'+RED._("common.label.delete")+'</button>')
.appendTo(cover)
.on("click", function(e) {
e.stopPropagation();
cover.remove();
sendRequest({
url: "projects/"+name,
type: "DELETE",
responses: {
200: function(data) {
done(false);
},
400: {
'unexpected_error': function(error) {
cover.remove();
done(true);
}
}
}
});
});
setTimeout(function() {
cover.css("left",0);
},50);
//
}
function show(s,options) {
if (!dialog) {
RED.projects.init();
}
var screen = screens[s];
var container = screen.content(options||{});
dialogBody.empty();
var buttons = screen.buttons;
if (typeof buttons === 'function') {
buttons = buttons(options||{});
}
dialog.dialog('option','buttons',buttons);
dialogBody.append(container);
var dialogHeight = 590;
var winHeight = $(window).height();
if (winHeight < 750) {
dialogHeight = 590 - (750 - winHeight);
}
$(".red-ui-projects-dialog-box").height(dialogHeight);
$(".red-ui-projects-dialog-project-list-inner-container").height(Math.max(500,dialogHeight) - 210);
dialog.dialog('option','title',screen.title||"");
dialog.dialog("open");
}
function createProjectList(options) {
options = options||{};
var height = options.height || "200px";
var container = $('<div></div>',{class:"red-ui-projects-dialog-project-list-container" });
var filterTerm = "";
var searchDiv = $("<div>",{class:"red-ui-search-container"}).appendTo(container);
var searchInput = $('<input id="red-ui-projects-dialog-project-list-search" type="text" placeholder="'+RED._("projects.create-project-list.search")+'">').appendTo(searchDiv).searchBox({
//data-i18n="[placeholder]menu.label.searchInput"
delay: 200,
change: function() {
filterTerm = $(this).val().toLowerCase();
list.editableList('filter');
if (selectedListItem && !selectedListItem.is(":visible")) {
selectedListItem.children().children().removeClass('selected');
selectedListItem = list.children(":visible").first();
selectedListItem.children().children().addClass('selected');
if (options.select) {
options.select(selectedListItem.children().data('data'));
}
} else {
selectedListItem = list.children(":visible").first();
selectedListItem.children().children().addClass('selected');
if (options.select) {
options.select(selectedListItem.children().data('data'));
}
}
ensureSelectedIsVisible();
}
});
var selectedListItem;
searchInput.on('keydown',function(evt) {
if (evt.keyCode === 40) {
evt.preventDefault();
// Down
var next = selectedListItem;
if (selectedListItem) {
do {
next = next.next();
} while(next.length !== 0 && !next.is(":visible"));
if (next.length === 0) {
return;
}
selectedListItem.children().children().removeClass('selected');
} else {
next = list.children(":visible").first();
}
selectedListItem = next;
selectedListItem.children().children().addClass('selected');
if (options.select) {
options.select(selectedListItem.children().data('data'));
}
ensureSelectedIsVisible();
} else if (evt.keyCode === 38) {
evt.preventDefault();
// Up
var prev = selectedListItem;
if (selectedListItem) {
do {
prev = prev.prev();
} while(prev.length !== 0 && !prev.is(":visible"));
if (prev.length === 0) {
return;
}
selectedListItem.children().children().removeClass('selected');
} else {
prev = list.children(":visible").first();
}
selectedListItem = prev;
selectedListItem.children().children().addClass('selected');
if (options.select) {
options.select(selectedListItem.children().data('data'));
}
ensureSelectedIsVisible();
} else if (evt.keyCode === 13) {
evt.preventDefault();
// Enter
if (selectedListItem) {
if (options.dblclick) {
options.dblclick(selectedListItem.children().data('data'));
}
}
}
});
searchInput.i18n();
var ensureSelectedIsVisible = function() {
var selectedEntry = list.find(".red-ui-projects-dialog-project-list-entry.selected").parent().parent();
if (selectedEntry.length === 1) {
var scrollWindow = listContainer;
var scrollHeight = scrollWindow.height();
var scrollOffset = scrollWindow.scrollTop();
var y = selectedEntry.position().top;
var h = selectedEntry.height();
if (y+h > scrollHeight) {
scrollWindow.animate({scrollTop: '-='+(scrollHeight-y-h)},50);
} else if (y<0) {
scrollWindow.animate({scrollTop: '+='+y},50);
}
}
}
var listContainer = $('<div></div>',{class:"red-ui-projects-dialog-project-list-inner-container" }).appendTo(container);
var list = $('<ol>',{class:"red-ui-projects-dialog-project-list"}).appendTo(listContainer).editableList({
addButton: false,
height:"auto",
scrollOnAdd: false,
addItem: function(row,index,entry) {
var header = $('<div></div>',{class:"red-ui-projects-dialog-project-list-entry"}).appendTo(row);
$('<span class="red-ui-projects-dialog-project-list-entry-icon"><i class="fa fa-archive"></i></span>').appendTo(header);
$('<span class="red-ui-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="red-ui-projects-dialog-project-list-entry-current">'+RED._("projects.create-project-list.current")+'</span>').appendTo(header);
if (options.canSelectActive === false) {
// active project cannot be selected; so skip the rest
return
}
}
header.addClass("selectable");
var tools = $('<div class="red-ui-projects-dialog-project-list-entry-tools"></div>').appendTo(header);
$('<button type="button" class="red-ui-button red-ui-projects-dialog-button red-ui-button-small" style="float: right;"><i class="fa fa-trash"></i></button>')
.appendTo(tools)
.on("click", function(e) {
e.stopPropagation();
e.preventDefault();
deleteProject(row,entry.name, function(cancelled) {
if (!cancelled) {
row.fadeOut(300,function() {
list.editableList('removeItem',entry);
if (options.delete) {
options.delete(entry);
}
});
}
})
});
row.on("click", function(evt) {
$('.red-ui-projects-dialog-project-list-entry').removeClass('selected');
header.addClass('selected');
selectedListItem = row.parent();
if (options.select) {
options.select(entry);
}
ensureSelectedIsVisible();
searchInput.trigger("focus");
})
if (options.dblclick) {
row.on("dblclick", function(evt) {
evt.preventDefault();
options.dblclick(entry);
})
}
},
filter: function(data) {
if (filterTerm === "") { return true; }
return data.name.toLowerCase().indexOf(filterTerm) !== -1;
}
});
$.getJSON("projects", function(data) {
data.projects.forEach(function(project) {
list.editableList('addItem',{name:project});
});
})
return container;
}
function requireCleanWorkspace(done) {
if (RED.nodes.dirty()) {
var message = RED._("projects.require-clean.confirm");
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();
done(true);
}
},{
text: RED._("common.label.cont"),
click: function() {
cleanNotification.close();
done(false);
}
}
]
});
}
}
function sendRequest(options,body) {
// dialogBody.hide();
// console.log(options.url,body);
if (options.requireCleanWorkspace && RED.nodes.dirty()) {
var thenCallback;
var alwaysCallback;
requireCleanWorkspace(function(cancelled) {
if (cancelled) {
if (options.cancel) {
options.cancel();
if (alwaysCallback) {
alwaysCallback();
}
}
} else {
delete options.requireCleanWorkspace;
sendRequest(options,body).then(function() {
if (thenCallback) {
thenCallback();
}
}).always(function() {
if (alwaysCallback) {
alwaysCallback();
}
})
}
})
// What follows is a very hacky Promise-like api thats good enough
// for our needs.
return {
then: function(done) {
thenCallback = done;
return { always: function(done) { alwaysCallback = done; }}
},
always: function(done) { alwaysCallback = done; }
}
}
var start = Date.now();
// TODO: this is specific to the dialog-based requests
$(".red-ui-component-spinner").show();
$("#red-ui-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) {
var responses;
if (options.responses && options.responses[xhr.status]) {
responses = options.responses[xhr.status];
if (typeof responses === 'function') {
resultCallback = responses;
resultCallbackArgs = {error:responses.statusText};
return;
} else if (options.handleAuthFail !== false && (xhr.responseJSON.code === 'git_auth_failed' || xhr.responseJSON.code === 'git_host_key_verification_failed')) {
if (xhr.responseJSON.code === 'git_auth_failed') {
var url = activeProject.git.remotes[xhr.responseJSON.remote||options.remote||'origin'].fetch;
var message = $('<div>'+
'<div class="form-row">'+RED._("projects.send-req.auth-req")+':</div>'+
'<div class="form-row"><div style="margin-left: 20px;">'+url+'</div></div>'+
'</div>');
var isSSH = false;
if (/^https?:\/\//.test(url)) {
$('<div class="form-row"><label for="projects-user-auth-username">'+RED._("projects.send-req.username")+'</label><input id="projects-user-auth-username" type="text"></input></div>'+
'<div class="form-row"><label for="projects-user-auth-password">'+RED._("projects.send-req.password")+'</label><input id="projects-user-auth-password" type="password"></input></div>').appendTo(message);
message.find("#projects-user-auth-password").typedInput({type:"cred"})
} else if (/^(?:ssh|[\d\w\.\-_]+@[\w\.]+):(?:\/\/)?/.test(url)) {
isSSH = true;
var row = $('<div class="form-row"></div>').appendTo(message);
$('<label for="projects-user-auth-key">SSH Key</label>').appendTo(row);
var projectRepoSSHKeySelect = $('<select id="projects-user-auth-key">').width('70%').appendTo(row);
$.getJSON("settings/user/keys", function(data) {
var count = 0;
data.keys.forEach(function(key) {
projectRepoSSHKeySelect.append($("<option></option>").val(key.name).text(key.name));
count++;
});
if (count === 0) {
//TODO: handle no keys yet setup
}
});
row = $('<div class="form-row"></div>').appendTo(message);
$('<label for="projects-user-auth-passphrase">'+RED._("projects.send-req.passphrase")+'</label>').appendTo(row);
$('<input id="projects-user-auth-passphrase" type="password"></input>').appendTo(row).typedInput({type:"cred"});
}
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> ' +RED._("projects.send-req.retry") +'</span>',
click: function() {
body = body || {};
var authBody = {};
if (isSSH) {
authBody.keyFile = $('#projects-user-auth-key').val();
authBody.passphrase = $('#projects-user-auth-passphrase').val();
} else {
authBody.username = $('#projects-user-auth-username').val();
authBody.password = $('#projects-user-auth-password').val();
}
var done = function(err) {
if (err) {
console.log(RED._("projects.send-req.update-failed"));
console.log(err);
} else {
sendRequest(options,body);
notification.close();
}
}
sendRequest({
url: "projects/"+activeProject.name+"/remotes/"+(xhr.responseJSON.remote||options.remote||'origin'),
type: "PUT",
responses: {
0: function(error) {
done(error,null);
},
200: function(data) {
done(null,data);
},
400: {
'unexpected_error': function(error) {
done(error,null);
}
},
}
},{auth:authBody});
}
}
]
});
return;
} else if (xhr.responseJSON.code === 'git_host_key_verification_failed') {
var message = $('<div>'+
'<div class="form-row">'+RED._("projects.send-req.host-key-verify-failed")+'</div>'+
'</div>');
var notification = RED.notify(message,{
type:"error",
fixed: true,
modal: true,
buttons: [
{
text: RED._("common.label.close"),
click: function() {
notification.close();
}
}
]
});
return;
}
} else if (responses[xhr.responseJSON.code]) {
resultCallback = responses[xhr.responseJSON.code];
resultCallbackArgs = xhr.responseJSON;
return;
} else if (responses['*']) {
resultCallback = responses['*'];
resultCallbackArgs = xhr.responseJSON;
return;
}
}
console.log(responses)
console.log(RED._("projects.send-req.unhandled")+":");
console.log(xhr);
console.log(textStatus);
console.log(err);
}).always(function() {
var delta = Date.now() - start;
delta = Math.max(0,500-delta);
setTimeout(function() {
// dialogBody.show();
$(".red-ui-component-spinner").hide();
$("#red-ui-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 branchNames = new Set();
var remotes = [];
var branchPrefix = "";
var container = $('<div class="red-ui-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 there is a / then
// - check what preceeds it is a known remote
var valid = false;
var hasRemote = false;
var m = /^([^/]+)\/[^/.~*?\[]/.exec(branchFilterTerm);
if (m && remotes.indexOf(m[1]) > -1) {
valid = true;
hasRemote = true;
}
if (!valid && /(\.\.|\/\.|[?*[~^: \\]|\/\/|\/.$|\/$)/.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(RED._("projects.create-branch-list.invalid")+": "+(hasRemote?"":branchPrefix)+branchFilterTerm);
} else {
if (branchFilterCreateItem.hasClass("input-error")) {
branchFilterCreateItem.removeClass("input-error");
branchFilterCreateItem.find("i").removeClass("fa-warning").addClass("fa-code-fork");
}
branchFilterCreateItem.find("span").text(RED._("projects.create-branch-list.create")+":");
branchFilterCreateItem.find(".red-ui-sidebar-vc-branch-list-entry-create-name").text((hasRemote?"":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="red-ui-sidebar-vc-branch-list-entry">').appendTo(row);
if (!entry.hasOwnProperty('commit')) {
branchFilterCreateItem = container;
$('<i class="fa fa-code-fork"></i>').appendTo(container);
$('<span>').text(RED._("projects.create-branch-list.create")+":").appendTo(container);
$('<div class="red-ui-sidebar-vc-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||RED._("projects.create-branch-list.current")).appendTo(container);
}
}
container.on("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.remotes) {
var m = /^([^/]+)\/[^/.~*?\[]/.exec(body.name);
if (!m || remotes.indexOf(m[1]) === -1) {
body.name = remotes[0]+"/"+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'));
var filterTerm = branchFilterTerm;
if (remotes.length > 0) {
var m = /^([^/]+)\/[^/.~*?\[]/.exec(filterTerm);
if (filterTerm !== "" && (!m || remotes.indexOf(m[1]) == -1)) {
filterTerm = remotes[0]+"/"+filterTerm;
}
}
return (
isCreateEntry &&
(
filterTerm !== "" && !branchNames.has(filterTerm)
)
) ||
(
!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("red-ui-component-spinner-contain");
if (options.remotes) {
remotes = options.remotes();
branchPrefix = remotes[0]+"/";
} else {
branchPrefix = "";
remotes = [];
}
branchNames = new Set();
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);
branchNames.add(b.name);
});
branchList.editableList('addItem',{});
setTimeout(function() {
spinner.remove();
},Math.max(300-(Date.now() - start),0));
},
400: {
'git_connection_failed': function(error) {
RED.notify(error.message,'error');
},
'git_not_a_repository': function(error) {
RED.notify(error.message,'error');
},
'git_repository_not_found': function(error) {
RED.notify(error.message,'error');
},
'unexpected_error': function(error) {
reportUnexpectedError(error);
}
}
}
})
},
// addItem: function(data) { branchList.editableList('addItem',data) },
filter: function() { branchList.editableList('filter') },
focus: function() { branchFilter.trigger("focus") }
}
}
function addSpinnerOverlay(container) {
var spinner = $('<div class="red-ui-component-spinner"><img src="red/images/spin.svg"/></div>').appendTo(container);
return spinner;
}
function init() {
dialog = $('<div id="red-ui-projects-dialog" class="hide red-ui-projects-edit-form"><div class="red-ui-projects-dialog-box"><form class="form-horizontal"></form><div class="red-ui-component-spinner hide"><img src="red/images/spin.svg"/></div></div></div>')
.appendTo("#red-ui-editor")
.dialog({
modal: true,
autoOpen: false,
width: 600,
resizable: false,
open: function(e) {
RED.keyboard.disable();
},
close: function(e) {
RED.keyboard.enable();
},
classes: {
"ui-dialog": "red-ui-editor-dialog",
"ui-dialog-titlebar-close": "hide",
"ui-widget-overlay": "red-ui-editor-dialog"
}
});
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:show-project-settings",RED.projects.settings.show);
var projectsAPI = {
sendRequest:sendRequest,
createBranchList:createBranchList,
addSpinnerOverlay:addSpinnerOverlay,
reportUnexpectedError:reportUnexpectedError
};
RED.projects.settings.init(projectsAPI);
RED.projects.userSettings.init(projectsAPI);
RED.sidebar.versionControl.init(projectsAPI);
initScreens();
// initSidebar();
}
function createDefaultFileSet() {
if (!activeProject) {
throw new Error(RED._("projects.create-default-file-set.no-active"));
} else if (!activeProject.empty) {
throw new Error(RED._("projects.create-default-file-set.no-empty"));
}
if (!RED.user.hasPermission("projects.write")) {
RED.notify(RED._("user.errors.notAuthorized"),"error");
return;
}
createProjectOptions = {};
show('default-files',{existingProject: true});
}
function createDefaultPackageFile() {
RED.deploy.setDeployInflight(true);
RED.projects.settings.switchProject(activeProject.name);
var method = "PUT";
var url = "projects/"+activeProject.name;
var createProjectOptions = {
initialise: true
};
sendRequest({
url: url,
type: method,
requireCleanWorkspace: true,
handleAuthFail: false,
responses: {
200: function(data) { },
400: {
'git_error': function(error) {
console.log(RED._("projects.create-default-file-set.git-error"),error);
},
'missing_flow_file': function(error) {
// This is a natural next error - but let the runtime event
// trigger the dialog rather than double-report it.
$( dialog ).dialog( "close" );
},
'*': function(error) {
reportUnexpectedError(error);
$( dialog ).dialog( "close" );
}
}
}
},createProjectOptions).always(function() {
setTimeout(function() {
RED.deploy.setDeployInflight(false);
},500);
})
}
function refresh(done) {
$.getJSON("projects",function(data) {
if (data.active) {
$.getJSON("projects/"+data.active, function(project) {
activeProject = project;
RED.events.emit("projects:load",activeProject);
RED.sidebar.versionControl.refresh(true);
if (done) {
done(activeProject);
}
});
} else {
if (done) {
done(null);
}
}
});
}
function showNewProjectScreen() {
createProjectOptions = {};
if (!activeProject) {
show('welcome');
} else {
show('create',{screen:'empty'})
}
}
return {
init: init,
showStartup: function() {
console.warn("showStartup")
if (!RED.user.hasPermission("projects.write")) {
RED.notify(RED._("user.errors.notAuthorized"),"error");
return;
}
show('welcome');
},
newProject: function() {
if (!RED.user.hasPermission("projects.write")) {
RED.notify(RED._("user.errors.notAuthorized"),"error");
return;
}
if (RED.nodes.dirty()) {
return requireCleanWorkspace(function(cancelled) {
if (!cancelled) {
showNewProjectScreen();
}
})
} else {
showNewProjectScreen();
}
},
selectProject: function() {
if (!RED.user.hasPermission("projects.write")) {
RED.notify(RED._("user.errors.notAuthorized"),"error");
return;
}
if (RED.nodes.dirty()) {
return requireCleanWorkspace(function(cancelled) {
if (!cancelled) {
show('create',{screen:'open'})
}
})
} else {
show('create',{screen:'open'})
}
},
showCredentialsPrompt: function() { //TODO: rename this function
if (!RED.user.hasPermission("projects.write")) {
RED.notify(RED._("user.errors.notAuthorized"),"error");
return;
}
RED.projects.settings.show('settings');
},
showFilesPrompt: function() { //TODO: rename this function
if (!RED.user.hasPermission("projects.write")) {
RED.notify(RED._("user.errors.notAuthorized"),"error");
return;
}
RED.projects.settings.show('settings');
setTimeout(function() {
$("#project-settings-tab-settings-file-edit").trigger("click");
},200)
},
showProjectDependencies: function() {
RED.projects.settings.show('deps');
},
createDefaultFileSet: createDefaultFileSet,
createDefaultPackageFile: createDefaultPackageFile,
// showSidebar: showSidebar,
refresh: refresh,
editProject: function() {
RED.projects.settings.show();
},
getActiveProject: function() {
return activeProject;
}
}
})();