Fixed password prompting on job submission. Prompting for passwords one at a time was causing Angular to do bad things. Now we prompt for everything in one nicely formatted dialog.

This commit is contained in:
Chris Houseknecht
2014-04-07 18:14:10 -04:00
parent 78e208dcdb
commit bcbc7ed7ac
4 changed files with 118 additions and 84 deletions

View File

@@ -21,8 +21,6 @@ function JobsListController ($scope, $compile, ClearScope, Breadcrumbs, LoadBrea
LoadBreadCrumbs(); LoadBreadCrumbs();
$scope.showJobType = true;
// Add breadcrumbs // Add breadcrumbs
e = angular.element(document.getElementById('breadcrumbs')); e = angular.element(document.getElementById('breadcrumbs'));
e.html(Breadcrumbs({ list: { editTitle: 'Jobs' } , mode: 'edit' })); e.html(Breadcrumbs({ list: { editTitle: 'Jobs' } , mode: 'edit' }));
@@ -44,6 +42,7 @@ function JobsListController ($scope, $compile, ClearScope, Breadcrumbs, LoadBrea
QueuedJobsList.fields.type.searchOptions = $scope.type_choices; QueuedJobsList.fields.type.searchOptions = $scope.type_choices;
} }
completed_scope = $scope.$new(true); completed_scope = $scope.$new(true);
completed_scope.showJobType = true;
LoadJobsScope({ LoadJobsScope({
parent_scope: $scope, parent_scope: $scope,
scope: completed_scope, scope: completed_scope,

View File

@@ -8,7 +8,7 @@
'use strict'; 'use strict';
angular.module('JobSubmissionHelper', [ 'RestServices', 'Utilities', 'CredentialFormDefinition', 'CredentialsListDefinition', angular.module('JobSubmissionHelper', [ 'RestServices', 'Utilities', 'CredentialFormDefinition', 'CredentialsListDefinition',
'LookUpHelper', 'JobSubmissionHelper', 'JobTemplateFormDefinition' ]) 'LookUpHelper', 'JobSubmissionHelper', 'JobTemplateFormDefinition', 'ModalDialog'])
.factory('LaunchJob', ['Rest', 'Wait', 'ProcessErrors', function(Rest, Wait, ProcessErrors) { .factory('LaunchJob', ['Rest', 'Wait', 'ProcessErrors', function(Rest, Wait, ProcessErrors) {
return function(params) { return function(params) {
@@ -97,103 +97,147 @@ function($location, Wait, GetBasePath, LookUpInit, JobTemplateForm, CredentialLi
}; };
}]) }])
.factory('PromptForPasswords', ['$compile', 'Wait', 'Alert', 'CredentialForm', .factory('PromptForPasswords', ['$compile', 'Wait', 'Alert', 'CredentialForm', 'CreateDialog',
function($compile, Wait, Alert, CredentialForm) { function($compile, Wait, Alert, CredentialForm, CreateDialog) {
return function(params) { return function(params) {
var parent_scope = params.scope, var parent_scope = params.scope,
passwords = params.passwords, passwords = params.passwords,
callback = params.callback || 'PasswordsAccepted', callback = params.callback || 'PasswordsAccepted',
password,
form = CredentialForm, form = CredentialForm,
html = "",
acceptedPasswords = {}, acceptedPasswords = {},
scope = parent_scope.$new(); scope = parent_scope.$new(),
e, buttons;
Wait('stop'); Wait('stop');
function promptPassword() { function buildHtml() {
var e, fld, field; var fld, field, html;
password = passwords.pop();
// Prompt for password
html = ""; html = "";
html += "<div class=\"alert alert-info\">Launching this job requires the passwords listed below. Enter and confirm each password before continuing.</div>\n";
html += "<form name=\"password_form\" novalidate>\n"; html += "<form name=\"password_form\" novalidate>\n";
field = form.fields[password];
fld = password;
scope[fld] = '';
html += "<div class=\"form-group\">\n";
html += "<label for=\"" + fld + "\">* " + field.label + "</label>\n";
html += "<input type=\"password\" ";
html += "ng-model=\"" + fld + '" ';
html += 'name="' + fld + '" ';
html += "class=\"password-field form-control\" ";
html += "required ";
html += "/>";
html += "<br />\n";
// Add error messages
html += "<span class=\"error\" ng-show=\"password_form." + fld + ".$dirty && " +
"password_form." + fld + ".$error.required\">A value is required!</span>\n";
html += "<span class=\"error api-error\" ng-bind=\"" + fld + "_api_error\"></span>\n";
html += "</div>\n";
// Add the related confirm field passwords.forEach(function(password) {
if (field.associated) { // Prompt for password
fld = field.associated; field = form.fields[password];
field = form.fields[field.associated]; fld = password;
scope[fld] = ''; scope[fld] = '';
html += "<div class=\"form-group\">\n"; html += "<div class=\"form-group\">\n";
html += "<label for=\"" + fld + "\">* " + field.label + "</label>\n"; html += "<label for=\"" + fld + "\">* " + field.label + "</label>\n";
html += "<input type=\"password\" "; html += "<input type=\"password\" ";
html += "ng-model=\"" + fld + '" '; html += "ng-model=\"" + fld + '" ';
html += 'name="' + fld + '" '; html += 'name="' + fld + '" ';
html += "class=\"form-control\" "; html += "class=\"password-field form-control input-sm\" ";
html += (field.associated) ? "ng-change=\"clearPWConfirm('" + field.associated + "')\" " : "";
html += "required "; html += "required ";
html += (field.awPassMatch) ? "awpassmatch=\"" + field.associated + "\" " : ""; html += " >";
html += "/>";
html += "<br />\n";
// Add error messages // Add error messages
html += "<span class=\"error\" ng-show=\"password_form." + fld + ".$dirty && " + html += "<div class=\"error\" ng-show=\"password_form." + fld + ".$dirty && " +
"password_form." + fld + ".$error.required\">A value is required!</span>\n"; "password_form." + fld + ".$error.required\">A value is required!</div>\n";
html += (field.awPassMatch) ? "<span class=\"error\" ng-show=\"password_form." + fld + html += "<div class=\"error api-error\" ng-bind=\"" + fld + "_api_error\"></div>\n";
".$error.awpassmatch\">Must match Password value</span>\n" : "";
html += "<span class=\"error api-error\" ng-bind=\"" + fld + "_api_error\"></span>\n";
html += "</div>\n"; html += "</div>\n";
}
html += "</form>\n"; // Add the related confirm field
$('#password-body').html(html); if (field.associated) {
e = angular.element(document.getElementById('password-modal')); fld = field.associated;
$compile(e)(scope); field = form.fields[field.associated];
$('#password-modal').modal(); scope[fld] = '';
$('#password-modal').on('shown.bs.modal', function () { html += "<div class=\"form-group\">\n";
$('#password-body').find('input[type="password"]:first').focus(); html += "<label for=\"" + fld + "\">* " + field.label + "</label>\n";
html += "<input type=\"password\" ";
html += "ng-model=\"" + fld + '" ';
html += 'name="' + fld + '" ';
html += "class=\"form-control input-sm\" ";
html += "ng-change=\"checkStatus()\" ";
html += "required ";
html += (field.awPassMatch) ? "awpassmatch=\"" + field.associated + "\" " : "";
html += "/>";
// Add error messages
html += "<div class=\"error\" ng-show=\"password_form." + fld + ".$dirty && " +
"password_form." + fld + ".$error.required\">A value is required!</span>\n";
html += (field.awPassMatch) ? "<span class=\"error\" ng-show=\"password_form." + fld +
".$error.awpassmatch\">Must match Password value</div>\n" : "";
html += "<div class=\"error api-error\" ng-bind=\"" + fld + "_api_error\"></div>\n";
html += "</div>\n";
}
}); });
html += "</form>\n";
return html;
} }
$('#password-modal').empty().html(buildHtml);
e = angular.element(document.getElementById('password-modal'));
$compile(e)(scope);
buttons = [{
label: "Cancel",
onClick: function() {
scope.passwordCancel();
},
icon: "fa-times",
"class": "btn btn-default",
"id": "password-cancel-button"
},{
label: "Continue",
onClick: function() {
scope.passwordAccept();
},
icon: "fa-check",
"class": "btn btn-primary",
"id": "password-accept-button"
}];
CreateDialog({
id: 'password-modal',
scope: scope,
buttons: buttons,
width: 600,
height: (passwords.length > 1) ? 700 : 500,
minWidth: 500,
title: 'Passwords Required',
callback: 'DialogReady'
});
if (scope.removeDialogReady) {
scope.removeDialogReady();
}
scope.removeDialogReady = scope.$on('DialogReady', function() {
$('#password-modal').dialog('open');
$('#password-accept-button').attr({ "disabled": "disabled" });
});
scope.passwordAccept = function() { scope.passwordAccept = function() {
$('#password-modal').modal('hide'); if (!scope.password_form.$invalid) {
$('#password-modal').off('shown.bs.modal'); passwords.forEach(function(password) {
$('#password-body').empty(); acceptedPasswords[password] = scope[password];
acceptedPasswords[password] = scope[password]; });
if (passwords.length > 0) { $('#password-modal').dialog('close');
setTimeout(function() {
promptPassword();
}, 500);
}
else {
parent_scope.$emit(callback, acceptedPasswords); parent_scope.$emit(callback, acceptedPasswords);
scope.$destroy();
} }
}; };
scope.passwordCancel = function() { scope.passwordCancel = function() {
$('#password-modal').modal('hide'); $('#password-modal').dialog('close');
$('#password-modal').off('shown.bs.modal');
$('#password-body').empty();
Alert('Missing Password', 'Required password(s) not provided. Your request will not be submitted.', 'alert-info'); Alert('Missing Password', 'Required password(s) not provided. Your request will not be submitted.', 'alert-info');
parent_scope.$emit('PasswordsCanceled'); parent_scope.$emit('PasswordsCanceled');
scope.$destroy(); scope.$destroy();
}; };
promptPassword();
// Password change
scope.clearPWConfirm = function (fld) {
// If password value changes, make sure password_confirm must be re-entered
scope[fld] = '';
scope.password_form[fld].$setValidity('awpassmatch', false);
scope.checkStatus();
};
scope.checkStatus = function() {
if (!scope.password_form.$invalid) {
$('#password-accept-button').removeAttr('disabled');
}
else {
$('#password-accept-button').attr({ "disabled": "disabled" });
}
};
}; };
}]) }])
@@ -238,7 +282,7 @@ function($location, Wait, GetBasePath, LookUpInit, JobTemplateForm, CredentialLi
if (scope.removePasswordsCanceled) { if (scope.removePasswordsCanceled) {
scope.removePasswordsCanceled(); scope.removePasswordsCanceled();
} }
scope.removePasswordCanceled = scope.$on('PasswordCanceled', function() { scope.removePasswordsCanceled = scope.$on('PasswordsCanceled', function() {
// Delete the job // Delete the job
Wait('start'); Wait('start');
Rest.setUrl(GetBasePath('jobs') + new_job_id + '/'); Rest.setUrl(GetBasePath('jobs') + new_job_id + '/');

View File

@@ -1079,6 +1079,11 @@ input[type="checkbox"].checkbox-no-label {
} }
} }
#password-modal .alert-info {
margin-top: 0;
margin-bottom: 25px;
}
/* Inventory job status badge */ /* Inventory job status badge */
.failures-true { .failures-true {
background-color: @red; background-color: @red;

View File

@@ -246,21 +246,7 @@
</div><!-- modal --> </div><!-- modal -->
<!-- Password Dialog --> <!-- Password Dialog -->
<div id="password-modal" class="modal fade"> <div id="password-modal" style="display: none;"></div>
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" ng-click="cancelJob()" aria-hidden="true">&times;</button>
<h3>Authentication Required</h3>
</div>
<div class="modal-body" id="password-body"></div>
<div class="modal-footer">
<a href="" ng-click="passwordCancel()" class="btn btn-default" id="password_cancel_btn">Cancel</a>
<a href="" ng-click="passwordAccept()" class="btn btn-primary" id="password_continue_btn" ng-disabled="password_form.$pristine || password_form.$invalid">Continue</a>
</div>
</div><!-- modal-content -->
</div><!-- modal-dialog -->
</div><!-- modal -->
<!-- Generic Form dialog --> <!-- Generic Form dialog -->
<div id="form-modal" class="modal fade"> <div id="form-modal" class="modal fade">