From 816442f5f05d8efcf6e82d0e77a3795bb33545ca Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Wed, 20 Dec 2017 23:45:17 +0000 Subject: [PATCH] Update sshey UI to use common list style --- editor/js/ui/projectSettings.js | 11 +- editor/js/ui/projectUserSettings.js | 363 +++++++++++------- editor/sass/projects.scss | 19 +- .../storage/localfilesystem/sshkeys.js | 8 +- 4 files changed, 250 insertions(+), 151 deletions(-) diff --git a/editor/js/ui/projectSettings.js b/editor/js/ui/projectSettings.js index 2c3e1f54d..e93d8b9ad 100644 --- a/editor/js/ui/projectSettings.js +++ b/editor/js/ui/projectSettings.js @@ -1088,14 +1088,15 @@ RED.projects.settings = (function() { .appendTo(title) .click(function(evt) { editRepoButton.attr('disabled',true); - addBranchDialog.slideDown(200, function() { - addBranchDialog[0].scrollIntoView(); + addRemoteDialog.slideDown(200, function() { + addRemoteDialog[0].scrollIntoView(); }); }); var emptyItem = { empty: true }; - + var row = $('
').appendTo(repoContainer); + var addRemoteDialog = $('
').hide().appendTo(row); row = $('
').appendTo(repoContainer); var remotesList = $('
    ').appendTo(row); remotesList.editableList({ @@ -1192,7 +1193,7 @@ RED.projects.settings = (function() { } }; var popover; - var addRemoteDialog = $('
    ').hide().appendTo(row); + $('
    ').text('Add remote').appendTo(addRemoteDialog); row = $('').appendTo(addRemoteDialog); @@ -1204,7 +1205,7 @@ RED.projects.settings = (function() { var remoteNameInputChanged = false; $('').appendTo(row).find("small"); row = $('').appendTo(addRemoteDialog); - var fetchLabel = $('').text('URL').appendTo(row); + $('').text('URL').appendTo(row); var remoteURLInput = $('').appendTo(row).on("change keyup paste",validateForm); var hideEditForm = function() { diff --git a/editor/js/ui/projectUserSettings.js b/editor/js/ui/projectUserSettings.js index 3c32984a8..6af0dcd59 100644 --- a/editor/js/ui/projectUserSettings.js +++ b/editor/js/ui/projectUserSettings.js @@ -16,18 +16,17 @@ RED.projects.userSettings = (function() { - var gitconfigContainer; var gitUsernameInput; var gitEmailInput; - function createRemoteRepositorySection(pane) { + function createGitUserSection(pane) { var currentGitSettings = RED.settings.get('git') || {}; currentGitSettings.user = currentGitSettings.user || {}; var title = $('

    ').text("Committer Details").appendTo(pane); - gitconfigContainer = $('').appendTo(pane); + var gitconfigContainer = $('').appendTo(pane); $('
    ').appendTo(gitconfigContainer).text("Leave blank to use system default"); var row = $('').appendTo(gitconfigContainer); @@ -39,162 +38,248 @@ RED.projects.userSettings = (function() { $('').text('Email').appendTo(row); gitEmailInput = $('').appendTo(row); gitEmailInput.val(currentGitSettings.user.email||""); + } - var sshkeyTitle = $('

    ').text("SSH Keys").appendTo(gitconfigContainer); - var editSshKeyListButton = $('') - .appendTo(sshkeyTitle) + + function createSSHKeySection(pane) { + var container = $('').appendTo(pane); + var popover; + var title = $('

    ').text("SSH Keys").appendTo(container); + + var addKeyButton = $('') + .appendTo(title) .click(function(evt) { - editSshKeyListButton.hide(); - formButtons.show(); - sshkeyInputRow.show(); - $(".projects-dialog-sshkey-list-button-remove").css('display', 'inline-block'); - }); - - var sshkeyListOptions = { - height: "300px", - deleteAction: function(entry, header) { - var spinner = utils.addSpinnerOverlay(header).addClass('projects-dialog-spinner-contain'); - var notification = RED.notify("Are you sure you want to delete the SSH Keys '"+entry.name+"'? This cannot be undone.", { - type: 'warning', - modal: true, - fixed: true, - buttons: [ - { - text: RED._("common.label.cancel"), - click: function() { - spinner.remove(); - notification.close(); - } - }, - { - text: "Delete SSH Keys", - click: function() { - notification.close(); - sendSSHKeyManagementAPI("DELETE_KEY", entry.name, null, function(data) { - spinner.remove(); - hideSSHKeyGenerateForm(); - utils.refreshSSHKeyList(sshkeysList); - }, function(err) { - spinner.remove(); - console.log('Delete error! error:', err); - notification = RED.notify("Failed to delete the SSH Keys '"+entry.name+"'.", { - type: "error", - modal: true, - fixed: false - }); - }); - } - } - ] + addKeyButton.attr('disabled',true); + addKeyDialog.slideDown(200, function() { + // addKeyDialog[0].scrollIntoView(); }); - }, - selectAction: function(entry, header) { - var spinner = utils.addSpinnerOverlay(header).addClass('projects-dialog-spinner-contain'); - sendSSHKeyManagementAPI("GET_KEY_DETAIL", entry.name, null, function(data) { - spinner.remove(); - setDialogContext(entry.name, data.publickey); - dialog.dialog("open"); - }, function(err) { - console.log('Get SSH Key detail error! error:', err); - spinner.remove(); - notification = RED.notify("Failed to get the SSH Key detail '"+entry.name+"'.", { - type: "error", - modal: true, - fixed: false - }); - }); + }); + + var validateForm = function() { + var validName = /^[a-zA-Z0-9\-_]+$/.test(keyNameInput.val()); + saveButton.attr('disabled',!validName); + keyNameInput.toggleClass('input-error',keyNameInputChanged&&!validName); + if (popover) { + popover.close(); + popover = null; } }; - var sshkeysListRow = $('').appendTo(gitconfigContainer); - var sshkeysList = utils.createSSHKeyList(sshkeyListOptions).appendTo(sshkeysListRow); - var sshkeyInputRow = $('').hide().appendTo(gitconfigContainer); - var sshkeyNameLabel = $('').text('Key Name').appendTo(sshkeyInputRow); - var sshkeyNameInput = $('').appendTo(sshkeyInputRow); - var sshkeyPassphraseLabel = $('').text('Passphrase').appendTo(sshkeyInputRow); - var sshkeyPassphraseInput = $('').appendTo(sshkeyInputRow); - var sshkeySamePassphraseLabel = $('').text('Same Passphrase').appendTo(sshkeyInputRow); - var sshkeySamePassphraseInput = $('').appendTo(sshkeyInputRow); + var row = $('').appendTo(container); + var addKeyDialog = $('
    ').hide().appendTo(row); + $('
    ').text('Generate SSH Key').appendTo(addKeyDialog); - var formButtonArea = $('
    ').appendTo(gitconfigContainer); - var formButtons = $('') - .hide().appendTo(formButtonArea); - - function hideSSHKeyGenerateForm() { - editSshKeyListButton.show(); - formButtons.hide(); - sshkeyInputRow.hide(); - sshkeyNameInput.val(""); - sshkeyPassphraseInput.val(""); - sshkeySamePassphraseInput.val(""); - if ( sshkeyNameInput.hasClass('input-error') ) { - sshkeyNameInput.removeClass('input-error'); + row = $('').appendTo(addKeyDialog); + $('').text('Name').appendTo(row); + var keyNameInput = $('').appendTo(row).on("change keyup paste",function() { + keyNameInputChanged = true; + validateForm(); + }); + var keyNameInputChanged = false; + $('').appendTo(row).find("small"); + + row = $('').appendTo(addKeyDialog); + $('').text('Passphrase').appendTo(row); + passphraseInput = $('').appendTo(row).on("change keyup paste",validateForm); + $('').appendTo(row).find("small"); + + var hideEditForm = function() { + addKeyButton.attr('disabled',false); + addKeyDialog.hide(); + keyNameInput.val(""); + passphraseInput.val(""); + if (popover) { + popover.close(); + popover = null; } - if ( sshkeyPassphraseInput.hasClass('input-error') ) { - sshkeyPassphraseInput.removeClass('input-error'); - } - if ( sshkeySamePassphraseInput.hasClass('input-error') ) { - sshkeySamePassphraseInput.removeClass('input-error'); - } - $(".projects-dialog-sshkey-list-button-remove").hide(); } - + var formButtons = $('').appendTo(addKeyDialog); $('') .appendTo(formButtons) .click(function(evt) { evt.preventDefault(); - hideSSHKeyGenerateForm(); + hideEditForm(); }); - var generateButton = $('') + var saveButton = $('') .appendTo(formButtons) .click(function(evt) { evt.preventDefault(); - if ( sshkeyNameInput.hasClass('input-error') ) { - sshkeyNameInput.removeClass('input-error'); + var spinner = utils.addSpinnerOverlay(addKeyDialog).addClass('projects-dialog-spinner-contain'); + var payload = { + name: keyNameInput.val(), + comment: gitEmailInput.val(), + password: passphraseInput.val(), + size: 4096 + }; + var done = function(err) { + spinner.remove(); + if (err) { + return; + } + hideEditForm(); } - if ( sshkeyPassphraseInput.hasClass('input-error') ) { - sshkeyPassphraseInput.removeClass('input-error'); - } - if ( sshkeySamePassphraseInput.hasClass('input-error') ) { - sshkeySamePassphraseInput.removeClass('input-error'); - } - var valid = true; - if ( sshkeyNameInput.val() === "" ) { - sshkeyNameInput.addClass('input-error'); - valid = false; - } - if ( sshkeyPassphraseInput.val() !== sshkeySamePassphraseInput.val() ) { - sshkeySamePassphraseInput.addClass('input-error'); - valid = false; - } - if ( valid ) { - sendSSHKeyManagementAPI("GENERATE_KEY", - { - name: sshkeyNameInput.val(), - email: gitEmailInput.val(), - password: sshkeyPassphraseInput.val(), - size: 4096 + // console.log(JSON.stringify(payload,null,4)); + RED.deploy.setDeployInflight(true); + utils.sendRequest({ + url: "settings/user/keys", + type: "POST", + responses: { + 0: function(error) { + done(error); }, - gitconfigContainer, - function() { - hideSSHKeyGenerateForm(); - utils.refreshSSHKeyList(sshkeysList); + 200: function(data) { + refreshSSHKeyList(); + done(); }, - function(err) { - console.log('err message:', err.message); - if ( err.message.includes('Some SSH Keyfile exists') ) { - sshkeyNameInput.addClass('input-error'); + 400: { + 'unexpected_error': function(error) { + console.log(error); + done(error); } - else if ( err.message.includes('Failed to generate ssh key files') ) { - sshkeyPassphraseInput.addClass('input-error'); - sshkeySamePassphraseInput.addClass('input-error'); - } - } - ); - } + }, + } + },payload); }); + + row = $('').appendTo(container); + var emptyItem = { empty: true }; + var expandKey = function(container,entry) { + var row = $('
    ',{style:"position:relative"}).appendTo(container); + var keyBox = $('
    ',{style:"min-height: 80px"}).appendTo(row);
    +            var spinner = utils.addSpinnerOverlay(keyBox).addClass('projects-dialog-spinner-contain');
    +            var options = {
    +                url: 'settings/user/keys/'+entry.name,
    +                type: "GET",
    +                responses: {
    +                    200: function(data) {
    +                        keyBox.text(data.publickey);
    +                        spinner.remove();
    +                    },
    +                    400: {
    +                        'unexpected_error': function(error) {
    +                            console.log(error);
    +                            spinner.remove();
    +                        }
    +                    },
    +                }
    +            }
    +            utils.sendRequest(options);
    +
    +            var formButtons = $('').appendTo(row);
    +            $('')
    +                .appendTo(formButtons)
    +                .click(function(evt) {
    +                    evt.preventDefault();
    +                    document.getSelection().selectAllChildren(keyBox[0]);
    +                    var ret = document.execCommand('copy');
    +                    document.getSelection().empty();
    +                });
    +
    +            return row;
    +        }
    +        var keyList = $('
      ').appendTo(row).editableList({ + height: 'auto', + addButton: false, + scrollOnAdd: false, + addItem: function(row,index,entry) { + var container = $('
      ').appendTo(row); + + if (entry.empty) { + container.addClass('red-ui-search-empty'); + container.text("No SSH keys"); + return; + } + + + $('').appendTo(container); + var content = $('').appendTo(container); + var topRow = $('
      ').appendTo(content); + $('').text(entry.name).appendTo(topRow); + + var tools = $('').appendTo(container); + var expandedRow; + $('') + .appendTo(tools) + .click(function(e) { + if (expandedRow) { + expandedRow.slideUp(200,function() { + expandedRow.remove(); + expandedRow = null; + }) + } else { + expandedRow = expandKey(container,entry); + } + }) + $('') + .appendTo(tools) + .click(function(e) { + var spinner = utils.addSpinnerOverlay(row).addClass('projects-dialog-spinner-contain'); + var notification = RED.notify("Are you sure you want to delete the SSH key '"+entry.name+"'? This cannot be undone.", { + type: 'warning', + modal: true, + fixed: true, + buttons: [ + { + text: RED._("common.label.cancel"), + click: function() { + spinner.remove(); + notification.close(); + } + }, + { + text: "Delete key", + click: function() { + notification.close(); + var url = "settings/user/keys/"+entry.name; + var options = { + url: url, + type: "DELETE", + responses: { + 200: function(data) { + row.fadeOut(200,function() { + keyList.editableList('removeItem',entry); + setTimeout(spinner.remove, 100); + if (keyList.editableList('length') === 0) { + keyList.editableList('addItem',emptyItem); + } + }); + }, + 400: { + 'unexpected_error': function(error) { + console.log(error); + spinner.remove(); + } + }, + } + } + utils.sendRequest(options); + } + } + ] + }); + }); + } + }); + + var refreshSSHKeyList = function() { + $.getJSON("settings/user/keys",function(result) { + if (result.keys) { + result.keys.sort(function(A,B) { + return A.name.localeCompare(B.name); + }); + keyList.editableList('empty'); + result.keys.forEach(function(key) { + keyList.editableList('addItem',key); + }) + } + }) + } + refreshSSHKeyList(); + } + + function sendSSHKeyManagementAPI(type, param, overlay, successCallback, failCallback) { var url; var method; @@ -223,7 +308,7 @@ RED.projects.userSettings = (function() { } // var spinner = utils.addSpinnerOverlay(gitconfigContainer); var spinner = overlay ? utils.addSpinnerOverlay(overlay) : null; - + var done = function(err) { if ( spinner ) { spinner.remove(); @@ -263,7 +348,7 @@ RED.projects.userSettings = (function() { } }, } - },payload); + },payload); } var dialog; @@ -320,7 +405,9 @@ RED.projects.userSettings = (function() { function createSettingsPane(activeProject) { var pane = $('
      '); - createRemoteRepositorySection(pane); + createGitUserSection(pane); + createSSHKeySection(pane); + createPublicKeyDialog(); return pane; } diff --git a/editor/sass/projects.scss b/editor/sass/projects.scss index a97bcb98e..ce10a3466 100644 --- a/editor/sass/projects.scss +++ b/editor/sass/projects.scss @@ -756,9 +756,17 @@ } div.projects-dialog-ssh-public-key { + position: relative; + padding: 15px 20px 0; pre { + position: relative; word-break: break-all; } + &:after { + content: ""; + display: table; + clear: both; + } } .projects-dialog-list { @@ -808,10 +816,8 @@ div.projects-dialog-ssh-public-key { } } .projects-dialog-list-dialog { - position: absolute; - top: 5px; - right: 10px; - left: 10px; + margin-top: 10px; + margin-bottom: 20px; background: white; border-radius: 4px; border: 1px solid $secondary-border-color; @@ -820,6 +826,11 @@ div.projects-dialog-ssh-public-key { display: block !important; width: auto !important; } + &:after { + content: ""; + display: table; + clear: both; + } .projects-dialog-list-dialog-header { font-weight: bold; diff --git a/red/runtime/storage/localfilesystem/sshkeys.js b/red/runtime/storage/localfilesystem/sshkeys.js index 4a5211cde..d2d67daf3 100644 --- a/red/runtime/storage/localfilesystem/sshkeys.js +++ b/red/runtime/storage/localfilesystem/sshkeys.js @@ -28,7 +28,7 @@ function init(_settings, _runtime) { settings = _settings; runtime = _runtime; log = runtime.log; - sshkeyDir = fspath.join(settings.userDir, "projects", ".sshkeys"); + sshkeyDir = fspath.join(settings.userDir, "projects", ".sshkeys"); // console.log('sshkeys.init()'); return createSSHKeyDirectory(); } @@ -73,7 +73,7 @@ function listSSHKeys(username) { name: filename }; }); - }); + }); } function getSSHKey(username, name) { @@ -92,12 +92,12 @@ function generateSSHKey(username, options) { throw new Error('Some SSH Keyfile exists'); } else { - var email = options.email || ""; + var comment = options.comment || ""; var password = options.password || ""; var size = options.size || 2048; var sshKeyFileBasename = username + '_' + name; var privateKeyFilePath = fspath.join(sshkeyDir, sshKeyFileBasename); - return generateSSHKeyPair(privateKeyFilePath, email, password, size) + return generateSSHKeyPair(privateKeyFilePath, comment, password, size) .then(function() { return name; });