Fixed form validation issues on Projects detail for manual projects. When editing an existing project, local_path value was not being set, even though it diplayed properly. Changed local_path from array of strings to array of objects. Now local_path values are sorted and the correct object in the list is selected.

This commit is contained in:
Chris Houseknecht
2013-11-04 07:14:21 +00:00
committed by Chris Church
parent bac22205a8
commit a5c44a391c
6 changed files with 99 additions and 126 deletions

View File

@@ -231,7 +231,7 @@ ProjectsList.$inject = [ '$scope', '$rootScope', '$location', '$log', '$routePar
function ProjectsAdd ($scope, $rootScope, $compile, $location, $log, $routeParams, ProjectsForm, function ProjectsAdd ($scope, $rootScope, $compile, $location, $log, $routeParams, ProjectsForm,
GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, ClearScope, GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, ClearScope,
GetBasePath, ReturnToCaller, GetProjectPath, LookUpInit, OrganizationList, GetBasePath, ReturnToCaller, GetProjectPath, LookUpInit, OrganizationList,
CredentialList, GetChoices) CredentialList, GetChoices, DebugForm)
{ {
ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior
//scope. //scope.
@@ -247,6 +247,7 @@ function ProjectsAdd ($scope, $rootScope, $compile, $location, $log, $routeParam
generator.reset(); generator.reset();
LoadBreadCrumbs(); LoadBreadCrumbs();
GetProjectPath({ scope: scope, master: master }); GetProjectPath({ scope: scope, master: master });
if (scope.removeChoicesReady) { if (scope.removeChoicesReady) {
@@ -259,6 +260,8 @@ function ProjectsAdd ($scope, $rootScope, $compile, $location, $log, $routeParam
break; break;
} }
} }
scope.scmRequired = false;
master['scm_type'] = scope['scm_type'];
}); });
// Load the list of options for Kind // Load the list of options for Kind
@@ -301,10 +304,11 @@ function ProjectsAdd ($scope, $rootScope, $compile, $location, $log, $routeParam
} }
} }
} }
if (scope.scm_type) { data.scm_type = scope.scm_type.value;
data.scm_type = scope.scm_type.value; if (data.scm_type.value !== '') {
delete data.local_path; delete data.local_path;
} }
var url = (base == 'teams') ? GetBasePath('teams') + $routeParams.team_id + '/projects/' : defaultUrl; var url = (base == 'teams') ? GetBasePath('teams') + $routeParams.team_id + '/projects/' : defaultUrl;
Rest.setUrl(url); Rest.setUrl(url);
Rest.post(data) Rest.post(data)
@@ -331,20 +335,9 @@ function ProjectsAdd ($scope, $rootScope, $compile, $location, $log, $routeParam
scope.scmChange = function() { scope.scmChange = function() {
// When an scm_type is set, path is not required // When an scm_type is set, path is not required
scope.pathRequired = (scope.scm_type) ? false : true; scope.pathRequired = (scope.scm_type.value == '') ? true : false;
scope.scmBranchLabel = (scope.scm_type && scope.scm_type.value && scope.scm_type.value == 'svn') ? 'Revision #' : 'SCM Branch'; scope.scmRequired = (scope.scm_type.value !== '') ? true : false;
} scope.scmBranchLabel = (scope.scm_type.value == 'svn') ? 'Revision #' : 'SCM Branch';
scope.authChange = function() {
if (!scope.auth_required) {
scope.scm_username = null;
scope.scm_password = null;
scope.scm_password_confirm = null;
scope.scm_key_data = null;
scope.scm_key_unlock = null;
scope.scm_key_unlock_confirm = null;
scope.scm_password_ask = false;
}
} }
// Cancel // Cancel
@@ -354,19 +347,21 @@ function ProjectsAdd ($scope, $rootScope, $compile, $location, $log, $routeParam
for (var fld in master) { for (var fld in master) {
scope[fld] = master[fld]; scope[fld] = master[fld];
} }
scope.scmChange();
}; };
} }
ProjectsAdd.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'ProjectsForm', ProjectsAdd.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'ProjectsForm',
'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'ClearScope', 'GetBasePath', 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'ClearScope', 'GetBasePath',
'ReturnToCaller', 'GetProjectPath', 'LookUpInit', 'OrganizationList', 'CredentialList', 'GetChoices' 'ReturnToCaller', 'GetProjectPath', 'LookUpInit', 'OrganizationList', 'CredentialList', 'GetChoices',
'DebugForm'
]; ];
function ProjectsEdit ($scope, $rootScope, $compile, $location, $log, $routeParams, ProjectsForm, function ProjectsEdit ($scope, $rootScope, $compile, $location, $log, $routeParams, ProjectsForm,
GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, RelatedSearchInit, GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, RelatedSearchInit,
RelatedPaginateInit, Prompt, ClearScope, GetBasePath, ReturnToCaller, GetProjectPath, RelatedPaginateInit, Prompt, ClearScope, GetBasePath, ReturnToCaller, GetProjectPath,
Authorization, CredentialList, LookUpInit, GetChoices) Authorization, CredentialList, LookUpInit, GetChoices, Empty, DebugForm)
{ {
ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior
//scope. //scope.
@@ -386,21 +381,6 @@ function ProjectsEdit ($scope, $rootScope, $compile, $location, $log, $routePara
scope.project_local_paths = []; scope.project_local_paths = [];
scope.base_dir = ''; scope.base_dir = '';
function setAskCheckboxes() {
for (var fld in form.fields) {
if (form.fields[fld].type == 'password' && form.fields[fld].ask && scope[fld] == 'ASK') {
// turn on 'ask' checkbox for password fields with value of 'ASK'
$("#" + fld + "-clear-btn").attr("disabled","disabled");
scope[fld + '_ask'] = true;
}
else {
scope[fld + '_ask'] = false;
$("#" + fld + "-clear-btn").removeAttr("disabled");
}
master[fld + '_ask'] = scope[fld + '_ask'];
}
}
// After the project is loaded, retrieve each related set // After the project is loaded, retrieve each related set
if (scope.projectLoadedRemove) { if (scope.projectLoadedRemove) {
scope.projectLoadedRemove(); scope.projectLoadedRemove();
@@ -409,25 +389,29 @@ function ProjectsEdit ($scope, $rootScope, $compile, $location, $log, $routePara
for (var set in relatedSets) { for (var set in relatedSets) {
scope.search(relatedSets[set].iterator); scope.search(relatedSets[set].iterator);
} }
if (Authorization.getUserInfo('is_superuser') == true) { if (Authorization.getUserInfo('is_superuser') == true) {
GetProjectPath({ scope: scope, master: master }); GetProjectPath({ scope: scope, master: master });
} }
else { else {
var opts = []; var opts = [];
opts.push(scope['local_path']); opts.push({ label: scope['local_path'], value: scope['local_path'] });
scope.project_local_paths = opts; scope.project_local_paths = opts;
scope.local_path = scope['project_local_paths'][0];
scope.base_dir = 'You do not have access to view this property'; scope.base_dir = 'You do not have access to view this property';
} }
LookUpInit({ LookUpInit({
url: GetBasePath('credentials') + '?kind=scm',
scope: scope, scope: scope,
form: form, form: form,
list: CredentialList, list: CredentialList,
field: 'credential' field: 'credential'
}); });
scope.auth_required = (scope.scm_type && (scope.scm_username || scope.scm_password || scope.scm_key_data)) ? true : false; scope.pathRequired = (scope.scm_type.value == '') ? true : false;
master.auth_required = scope.auth_required; scope.scmRequired = (scope.scm_type.value !== '') ? true : false;
scope.scmBranchLabel = (scope.scm_type.value == 'svn') ? 'Revision #' : 'SCM Branch';
}); });
if (scope.removeChoicesReady) { if (scope.removeChoicesReady) {
@@ -466,26 +450,28 @@ function ProjectsEdit ($scope, $rootScope, $compile, $location, $log, $routePara
relatedSets[set] = { url: related[set], iterator: form.related[set].iterator }; relatedSets[set] = { url: related[set], iterator: form.related[set].iterator };
} }
} }
if (data.scm_type !== "") { data.scm_type = (Empty(data.scm_type)) ? '' : data.scm_type;
for (var i=0; i < scope.scm_type_options.length; i++) {
if (scope.scm_type_options[i].value == data.scm_type) { for (var i=0; i < scope.scm_type_options.length; i++) {
scope.scm_type = scope.scm_type_options[i]; if (scope.scm_type_options[i].value == data.scm_type) {
break; scope.scm_type = scope.scm_type_options[i];
} break;
} }
}
if (scope.scm_type.value !== '') {
scope.pathRequired = false; scope.pathRequired = false;
scope.scmRequired = true;
} }
else { else {
scope.pathRequired = true; scope.pathRequired = true;
scope.scmRequired = false;
} }
master['scm_type'] = scope['scm_type']; master['scm_type'] = scope['scm_type'];
master['auth_required'] = scope['auth_required']; scope.scmBranchLabel = (scope.scm_type.value == 'svn') ? 'Revision #' : 'SCM Branch';
scope.scmBranchLabel = (scope.scm_type && scope.scm_type.value && scope.scm_type.value == 'svn') ? 'Revision #' : 'SCM Branch';
setAskCheckboxes();
// Initialize related search functions. Doing it here to make sure relatedSets object is populated. // Initialize related search functions. Doing it here to make sure relatedSets object is populated.
RelatedSearchInit({ scope: scope, form: form, relatedSets: relatedSets }); RelatedSearchInit({ scope: scope, form: form, relatedSets: relatedSets });
RelatedPaginateInit({ scope: scope, relatedSets: relatedSets }); RelatedPaginateInit({ scope: scope, relatedSets: relatedSets });
@@ -499,7 +485,7 @@ function ProjectsEdit ($scope, $rootScope, $compile, $location, $log, $routePara
// Load the list of options for Kind // Load the list of options for Kind
GetChoices({ GetChoices({
url: GetBasePath('credentials') + '?kind=scm', url: GetBasePath('projects'),
scope: scope, scope: scope,
field: 'scm_type', field: 'scm_type',
variable: 'scm_type_options', variable: 'scm_type_options',
@@ -524,10 +510,12 @@ function ProjectsEdit ($scope, $rootScope, $compile, $location, $log, $routePara
} }
} }
} }
if (scope.scm_type) {
params.scm_type = scope.scm_type.value; params.scm_type = scope.scm_type.value;
if (scope.scm_type.value !== '') {
delete params.local_path; delete params.local_path;
} }
Rest.setUrl(defaultUrl); Rest.setUrl(defaultUrl);
Rest.put(params) Rest.put(params)
.success( function(data, status, headers, config) { .success( function(data, status, headers, config) {
@@ -539,27 +527,6 @@ function ProjectsEdit ($scope, $rootScope, $compile, $location, $log, $routePara
}); });
}; };
// Reset the form
scope.formReset = function() {
$rootScope.flashMessage = null;
generator.reset();
for (var fld in master) {
scope[fld] = master[fld];
}
};
scope.authChange = function() {
if (!scope.auth_required) {
scope.scm_username = null;
scope.scm_password = null;
scope.scm_password_confirm = null;
scope.scm_key_data = null;
scope.scm_key_unlock = null;
scope.scm_key_unlock_confirm = null;
scope.scm_password_ask = false;
}
}
// Related set: Add button // Related set: Add button
scope.add = function(set) { scope.add = function(set) {
$rootScope.flashMessage = null; $rootScope.flashMessage = null;
@@ -595,46 +562,28 @@ function ProjectsEdit ($scope, $rootScope, $compile, $location, $log, $routePara
action: action action: action
}); });
} }
// Password change
scope.clearPWConfirm = function(fld) {
// If password value changes, make sure password_confirm must be re-entered
scope[fld] = '';
scope[form.name + '_form'][fld].$setValidity('awpassmatch', false);
}
// Respond to 'Ask at runtime?' checkbox
scope.ask = function(fld, associated) {
if (scope[fld + '_ask']) {
$("#" + fld + "-clear-btn").attr("disabled","disabled");
scope[fld] = 'ASK';
scope[associated] = '';
scope[form.name + '_form'][associated].$setValidity('awpassmatch', true);
}
else {
$("#" + fld + "-clear-btn").removeAttr("disabled");
scope[fld] = '';
scope[associated] = '';
scope[form.name + '_form'][associated].$setValidity('awpassmatch', true);
}
}
scope.clear = function(fld, associated) {
scope[fld] = '';
scope[associated] = '';
scope[form.name + '_form'][associated].$setValidity('awpassmatch', true);
scope[form.name + '_form'].$setDirty();
}
scope.scmChange = function() { scope.scmChange = function() {
// When an scm_type is set, path is not required scope.pathRequired = (scope.scm_type.value == '') ? true : false;
scope.pathRequired = (scope.scm_type) ? false : true; scope.scmRequired = (scope.scm_type.value !== '') ? true : false;
scope.scmBranchLabel = (scope.scm_type && scope.scm_type.value && scope.scm_type.value == 'svn') ? 'Revision #' : 'SCM Branch'; scope.scmBranchLabel = (scope.scm_type.value == 'svn') ? 'Revision #' : 'SCM Branch';
} }
// Reset the form
scope.formReset = function() {
$rootScope.flashMessage = null;
generator.reset();
for (var fld in master) {
scope[fld] = master[fld];
}
scope.scmChange();
//DebugForm({ scope: scope, form: form });
};
} }
ProjectsEdit.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'ProjectsForm', ProjectsEdit.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'ProjectsForm',
'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'RelatedSearchInit', 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'RelatedSearchInit',
'RelatedPaginateInit', 'Prompt', 'ClearScope', 'GetBasePath', 'ReturnToCaller', 'RelatedPaginateInit', 'Prompt', 'ClearScope', 'GetBasePath', 'ReturnToCaller',
'GetProjectPath', 'Authorization', 'CredentialList', 'LookUpInit', 'GetChoices' 'GetProjectPath', 'Authorization', 'CredentialList', 'LookUpInit', 'GetChoices', 'Empty',
'DebugForm'
]; ];

View File

@@ -82,8 +82,8 @@ angular.module('ProjectFormDefinition', [])
label: 'Playbook Directory', label: 'Playbook Directory',
type: 'select', type: 'select',
id: 'local-path-select', id: 'local-path-select',
ngOptions: 'path for path in project_local_paths', ngOptions: 'path.label for path in project_local_paths',
awRequiredWhen: { variable: "pathRequired", init: "true" }, awRequiredWhen: { variable: "pathRequired", init: false },
ngShow: "scm_type.value == ''", ngShow: "scm_type.value == ''",
awPopOver: '<p>Select from the list of directories found in the base path.' + awPopOver: '<p>Select from the list of directories found in the base path.' +
'Together the base path and the playbook directory provide the full path used to locate playbooks.</p>' + 'Together the base path and the playbook directory provide the full path used to locate playbooks.</p>' +
@@ -96,7 +96,7 @@ angular.module('ProjectFormDefinition', [])
label: 'SCM URL', label: 'SCM URL',
type: 'text', type: 'text',
ngShow: "scm_type.value !== ''", ngShow: "scm_type.value !== ''",
awRequiredWhen: { variable: "scm_type", init: "true" }, awRequiredWhen: { variable: "scmRequired", init: false },
helpCollapse: [ helpCollapse: [
{ hdr: 'GIT URLs', { hdr: 'GIT URLs',
content: '<p>Example URLs for GIT SCM include:</p><ul class=\"no-bullets\"><li>https://github.com/ansible/ansible.git</li>' + content: '<p>Example URLs for GIT SCM include:</p><ul class=\"no-bullets\"><li>https://github.com/ansible/ansible.git</li>' +
@@ -140,14 +140,12 @@ angular.module('ProjectFormDefinition', [])
checkbox_group: { checkbox_group: {
label: 'SCM Options', label: 'SCM Options',
type: 'checkbox_group', type: 'checkbox_group',
ngShow: "scm_type !== '' && scm_type !== null", ngShow: "scm_type && scm_type.value !== ''",
fields: [ fields: [
{ {
name: 'scm_clean', name: 'scm_clean',
label: 'Clean', label: 'Clean',
type: 'checkbox', type: 'checkbox',
ngShow: "scm_type.value !== ''",
addRequired: false, addRequired: false,
editRequired: false, editRequired: false,
awPopOver: '<p>Remove any local modifications prior to performing an update.</p>', awPopOver: '<p>Remove any local modifications prior to performing an update.</p>',
@@ -160,7 +158,6 @@ angular.module('ProjectFormDefinition', [])
name: 'scm_delete_on_update', name: 'scm_delete_on_update',
label: 'Delete on Update', label: 'Delete on Update',
type: 'checkbox', type: 'checkbox',
ngShow: "scm_type.value !== ''",
addRequired: false, addRequired: false,
editRequired: false, editRequired: false,
awPopOver: '<p>Delete the local repository in its entirety prior to performing an update.</p><p>Depending on the size of the ' + awPopOver: '<p>Delete the local repository in its entirety prior to performing an update.</p><p>Depending on the size of the ' +
@@ -174,7 +171,6 @@ angular.module('ProjectFormDefinition', [])
name: 'scm_update_on_launch', name: 'scm_update_on_launch',
label: 'Update on Launch', label: 'Update on Launch',
type: 'checkbox', type: 'checkbox',
ngShow: "scm_type.value !== ''",
addRequired: false, addRequired: false,
editRequired: false, editRequired: false,
awPopOver: '<p>Each time a job runs using this project, perform an update to the local repository prior to starting the job.</p>', awPopOver: '<p>Each time a job runs using this project, perform an update to the local repository prior to starting the job.</p>',

View File

@@ -611,7 +611,7 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', '
var data = { var data = {
name: scope['name'], name: scope['name'],
description: scope['description'], description: scope['description']
}; };
if (inventory_id) { if (inventory_id) {

View File

@@ -316,7 +316,7 @@ angular.module('JobSubmissionHelper', [ 'RestServices', 'Utilities', 'Credential
scope.removeUpdateSubmitted = scope.$on('UpdateSubmitted', function(e, action) { scope.removeUpdateSubmitted = scope.$on('UpdateSubmitted', function(e, action) {
// Refresh the project list after update request submitted // Refresh the project list after update request submitted
Alert('Update Started', 'The request to start the SCM update process was submitted. ' + Alert('Update Started', 'The request to start the SCM update process was submitted. ' +
'The Projects page will refresh every 10 seconds, or refresh manually by clicking the <em>Refresh</em> button.', 'alert-info'); 'To monitor the update status, refresh the page by clicking the <em>Refresh</em> button.', 'alert-info');
scope.refresh(); scope.refresh();
}); });
@@ -402,8 +402,8 @@ angular.module('JobSubmissionHelper', [ 'RestServices', 'Utilities', 'Credential
scope.removeUpdateSubmitted = scope.$on('UpdateSubmitted', function(e, action) { scope.removeUpdateSubmitted = scope.$on('UpdateSubmitted', function(e, action) {
if (action == 'started') { if (action == 'started') {
// Refresh the project list after update request submitted // Refresh the project list after update request submitted
Alert('Update Started', 'The request to start the inventory update process was submitted. Monitor progress from the inventory summary screen. ' + Alert('Update Started', 'The request to start the inventory update process was submitted. Monitor progress of the update process ' +
'The screen will refresh every 10 seconds, or refresh manually by clicking the <em>Refresh</em> button.', 'alert-info'); 'by clicking the <em>Refresh</em> button.', 'alert-info');
//var node = $('#inventory-node') //var node = $('#inventory-node')
//var selected = $('#tree-view').jstree('get_selected'); //var selected = $('#tree-view').jstree('get_selected');
//scope['inventorySummaryGroup'] = null; //scope['inventorySummaryGroup'] = null;

View File

@@ -17,6 +17,24 @@ angular.module('ProjectPathHelper', ['RestServices', 'Utilities'])
var scope = params.scope; var scope = params.scope;
var master = params.master; var master = params.master;
function arraySort(data) {
//Sort nodes by name
var names = [];
var newData = [];
for (var i=0; i < data.length; i++) {
names.push(data[i].value);
}
names.sort();
for (var j=0; j < names.length; j++) {
for (i=0; i < data.length; i++) {
if (data[i].value == names[j]) {
newData.push(data[i]);
}
}
}
return newData;
}
scope.showMissingPlaybooksAlert = false; scope.showMissingPlaybooksAlert = false;
Rest.setUrl( GetBasePath('config') ); Rest.setUrl( GetBasePath('config') );
@@ -24,12 +42,22 @@ angular.module('ProjectPathHelper', ['RestServices', 'Utilities'])
.success( function(data, status, headers, config) { .success( function(data, status, headers, config) {
var opts = []; var opts = [];
for (var i=0; i < data.project_local_paths.length; i++) { for (var i=0; i < data.project_local_paths.length; i++) {
opts.push(data.project_local_paths[i]); opts.push({ label: data.project_local_paths[i], value: data.project_local_paths[i] });
} }
if (scope.local_path) { if (scope.local_path) {
opts.push(scope.local_path); // List only includes paths not assigned to projects, so add the
// path assigned to the current project.
opts.push({ label: scope.local_path, value: scope.local_path });
}
scope.project_local_paths = arraySort(opts);
if (scope.local_path) {
for (var i=0; scope.project_local_paths.length; i++) {
if (scope.project_local_paths[i].value == scope.local_path) {
scope.local_path = scope.project_local_paths[i];
break;
}
}
} }
scope.project_local_paths = opts;
scope.base_dir = data.project_base_dir; scope.base_dir = data.project_base_dir;
master.base_dir = scope.base_dir; // Keep in master object so that it doesn't get master.base_dir = scope.base_dir; // Keep in master object so that it doesn't get
// wiped out on form reset. // wiped out on form reset.

View File

@@ -107,7 +107,7 @@ angular.module('AWDirectives', ['RestServices', 'Utilities', 'AuthService', 'Hos
function checkIt () { function checkIt () {
var viewValue = elm.val(); var viewValue = elm.val();
var txt, label, p, l, s; var txt, label;
validity = true; validity = true;
if ( scope[attrs.awRequiredWhen] && (elm.attr('required') == null || elm.attr('required') == undefined) ) { if ( scope[attrs.awRequiredWhen] && (elm.attr('required') == null || elm.attr('required') == undefined) ) {
$(elm).attr('required','required'); $(elm).attr('required','required');