diff --git a/awx/ui/client/features/output/details.component.js b/awx/ui/client/features/output/details.component.js index d6da44d913..23aa5c2a6d 100644 --- a/awx/ui/client/features/output/details.component.js +++ b/awx/ui/client/features/output/details.component.js @@ -354,6 +354,17 @@ function getSCMBranchDetails (scmBranch) { return { label, value }; } +function getSCMRefspecDetails (scmRefspec) { + const label = strings.get('labels.SCM_REFSPEC'); + const value = scmRefspec || resource.model.get('scm_refspec'); + + if (!value) { + return null; + } + + return { label, value }; +} + function getInventoryScmDetails (updateId, updateStatus) { const projectId = resource.model.get('summary_fields.source_project.id'); const projectName = resource.model.get('summary_fields.source_project.name'); @@ -812,6 +823,7 @@ function JobDetailsController ( vm.projectUpdate = getProjectUpdateDetails(); vm.projectStatus = getProjectStatusDetails(); vm.scmBranch = getSCMBranchDetails(); + vm.scmRefspec = getSCMRefspecDetails(); vm.scmRevision = getSCMRevisionDetails(); vm.inventoryScm = getInventoryScmDetails(); vm.playbook = getPlaybookDetails(); @@ -853,6 +865,7 @@ function JobDetailsController ( finished, scm, scmBranch, + scmRefspec, inventoryScm, scmRevision, instanceGroup, @@ -865,6 +878,7 @@ function JobDetailsController ( vm.projectUpdate = getProjectUpdateDetails(scm.id); vm.projectStatus = getProjectStatusDetails(scm.status); vm.scmBranch = getSCMBranchDetails(scmBranch); + vm.scmRefspec = getSCMRefspecDetails(scmRefspec); vm.environment = getEnvironmentDetails(environment); vm.artifacts = getArtifactsDetails(artifacts); vm.executionNode = getExecutionNodeDetails(executionNode); diff --git a/awx/ui/client/features/output/details.partial.html b/awx/ui/client/features/output/details.partial.html index a3b3b664e0..14bf856269 100644 --- a/awx/ui/client/features/output/details.partial.html +++ b/awx/ui/client/features/output/details.partial.html @@ -222,7 +222,13 @@
' + i18n._("Branch to checkout. You can input other refs like tags and commit hashes as well. You can set a custom refspec, to allow for other refs to be input.") + '
', + awPopOver: '' + i18n._("Branch to checkout. In addition to branches, you can input tags, commit hashes, and arbitrary refs. Some commit hashes and refs may not be availble unless you also provide a custom refspec.") + '
', dataTitle: i18n._('SCM Branch'), subForm: 'sourceSubForm', }, @@ -140,7 +140,8 @@ export default ['i18n', 'NotificationsList', 'TemplateList', '' + i18n._('The first fetches all references. The second fetches only the Github pull request number 62, in this example the branch needs to be `refs/pull/62/head`.') + - '
', + '' + + '' + i18n._('For more information, refer to the') + ' ' + i18n._('Ansible Tower Documentation') + '.
', dataTitle: i18n._('SCM Refspec'), subForm: 'sourceSubForm', }, diff --git a/awx/ui/client/src/scheduler/schedulerAdd.controller.js b/awx/ui/client/src/scheduler/schedulerAdd.controller.js index 0ff85f7688..7c2a45dfd6 100644 --- a/awx/ui/client/src/scheduler/schedulerAdd.controller.js +++ b/awx/ui/client/src/scheduler/schedulerAdd.controller.js @@ -20,6 +20,20 @@ export default ['$filter', '$state', '$stateParams', '$http', 'Wait', scheduler, job_type; + const shouldShowPromptButton = (launchConf) => launchConf.survey_enabled || + launchConf.ask_inventory_on_launch || + launchConf.ask_credential_on_launch || + launchConf.ask_verbosity_on_launch || + launchConf.ask_job_type_on_launch || + launchConf.ask_limit_on_launch || + launchConf.ask_tags_on_launch || + launchConf.ask_skip_tags_on_launch || + launchConf.ask_diff_mode_on_launch || + launchConf.credential_needed_to_start || + launchConf.ask_variables_on_launch || + launchConf.ask_scm_branch_on_launch || + launchConf.variables_needed_to_start.length !== 0; + var schedule_url = ParentObject.related.schedules || `${ParentObject.related.inventory_source}schedules`; if (ParentObject){ $scope.parentObject = ParentObject; @@ -152,20 +166,7 @@ export default ['$filter', '$state', '$stateParams', '$http', 'Wait', $scope.noVars = true; } - if (!launchConf.survey_enabled && - !launchConf.ask_inventory_on_launch && - !launchConf.ask_credential_on_launch && - !launchConf.ask_verbosity_on_launch && - !launchConf.ask_job_type_on_launch && - !launchConf.ask_limit_on_launch && - !launchConf.ask_tags_on_launch && - !launchConf.ask_skip_tags_on_launch && - !launchConf.ask_diff_mode_on_launch && - !launchConf.ask_scm_branch_on_launch && - !launchConf.survey_enabled && - !launchConf.credential_needed_to_start && - !launchConf.inventory_needed_to_start && - launchConf.variables_needed_to_start.length === 0) { + if (!shouldShowPromptButton(launchConf)) { $scope.showPromptButton = false; } else { $scope.showPromptButton = true; @@ -240,21 +241,8 @@ export default ['$filter', '$state', '$stateParams', '$http', 'Wait', }); }; - if (!launchConf.survey_enabled && - !launchConf.ask_inventory_on_launch && - !launchConf.ask_credential_on_launch && - !launchConf.ask_verbosity_on_launch && - !launchConf.ask_job_type_on_launch && - !launchConf.ask_limit_on_launch && - !launchConf.ask_tags_on_launch && - !launchConf.ask_skip_tags_on_launch && - !launchConf.ask_diff_mode_on_launch && - !launchConf.ask_scm_branch_on_launch && - !launchConf.survey_enabled && - !launchConf.credential_needed_to_start && - !launchConf.inventory_needed_to_start && - launchConf.variables_needed_to_start.length === 0) { - $scope.showPromptButton = false; + if (!shouldShowPromptButton(launchConf)) { + $scope.showPromptButton = false; } else { $scope.showPromptButton = true; diff --git a/awx/ui/client/src/scheduler/schedulerEdit.controller.js b/awx/ui/client/src/scheduler/schedulerEdit.controller.js index 7a180724e9..3caeb926e5 100644 --- a/awx/ui/client/src/scheduler/schedulerEdit.controller.js +++ b/awx/ui/client/src/scheduler/schedulerEdit.controller.js @@ -10,6 +10,21 @@ function($filter, $state, $stateParams, Wait, $scope, moment, let schedule, scheduler, scheduleCredentials = []; + const shouldShowPromptButton = (launchConf) => launchConf.survey_enabled || + launchConf.ask_inventory_on_launch || + launchConf.ask_credential_on_launch || + launchConf.ask_verbosity_on_launch || + launchConf.ask_job_type_on_launch || + launchConf.ask_limit_on_launch || + launchConf.ask_tags_on_launch || + launchConf.ask_skip_tags_on_launch || + launchConf.ask_diff_mode_on_launch || + launchConf.credential_needed_to_start || + launchConf.ask_variables_on_launch || + launchConf.ask_scm_branch_on_launch || + launchConf.passwords_needed_to_start.length !== 0 || + launchConf.variables_needed_to_start.length !== 0; + $scope.preventCredsWithPasswords = true; // initial end @ midnight values @@ -326,21 +341,7 @@ function($filter, $state, $stateParams, Wait, $scope, moment, // ask_variables_on_launch = true $scope.noVars = !launchConf.ask_variables_on_launch; - if (!launchConf.survey_enabled && - !launchConf.ask_inventory_on_launch && - !launchConf.ask_credential_on_launch && - !launchConf.ask_verbosity_on_launch && - !launchConf.ask_job_type_on_launch && - !launchConf.ask_limit_on_launch && - !launchConf.ask_tags_on_launch && - !launchConf.ask_skip_tags_on_launch && - !launchConf.ask_diff_mode_on_launch && - !launchConf.ask_scm_branch_on_launch && - !launchConf.survey_enabled && - !launchConf.credential_needed_to_start && - !launchConf.inventory_needed_to_start && - launchConf.passwords_needed_to_start.length === 0 && - launchConf.variables_needed_to_start.length === 0) { + if (!shouldShowPromptButton(launchConf)) { $scope.showPromptButton = false; if (launchConf.ask_variables_on_launch) { @@ -425,21 +426,7 @@ function($filter, $state, $stateParams, Wait, $scope, moment, currentValues: scheduleResolve }); - if (!launchConf.survey_enabled && - !launchConf.ask_inventory_on_launch && - !launchConf.ask_credential_on_launch && - !launchConf.ask_verbosity_on_launch && - !launchConf.ask_job_type_on_launch && - !launchConf.ask_limit_on_launch && - !launchConf.ask_tags_on_launch && - !launchConf.ask_skip_tags_on_launch && - !launchConf.ask_diff_mode_on_launch && - !launchConf.ask_scm_branch_on_launch && - !launchConf.survey_enabled && - !launchConf.credential_needed_to_start && - !launchConf.inventory_needed_to_start && - launchConf.passwords_needed_to_start.length === 0 && - launchConf.variables_needed_to_start.length === 0) { + if (!shouldShowPromptButton(launchConf)) { $scope.showPromptButton = false; } else { $scope.showPromptButton = true; diff --git a/awx/ui/client/src/shared/Utilities.js b/awx/ui/client/src/shared/Utilities.js index 456616ce93..cceeac1773 100644 --- a/awx/ui/client/src/shared/Utilities.js +++ b/awx/ui/client/src/shared/Utilities.js @@ -182,41 +182,52 @@ angular.module('Utilities', ['RestServices', 'Utilities']) } } else if (form) { //if no error code is detected it begins to loop through to see where the api threw an error fieldErrors = false; - for (field in form.fields) { - if (data[field] && form.fields[field].tab) { + + const addApiErrors = (data, field, fld) => { + if (data && field.tab) { // If the form is part of a tab group, activate the tab - $('#' + form.name + "_tabs a[href=\"#" + form.fields[field].tab + '"]').tab('show'); + $('#' + form.name + "_tabs a[href=\"#" + field.tab + '"]').tab('show'); } - if (form.fields[field].realName) { - if (data[form.fields[field].realName]) { - scope[field + '_api_error'] = data[form.fields[field].realName][0]; + if (field.realName) { + if (field.realName) { + scope[fld + '_api_error'] = data[field.realName][0]; //scope[form.name + '_form'][form.fields[field].realName].$setValidity('apiError', false); - $('[name="' + form.fields[field].realName + '"]').addClass('ng-invalid'); - $('html, body').animate({scrollTop: $('[name="' + form.fields[field].realName + '"]').offset().top}, 0); + $('[name="' + field.realName + '"]').addClass('ng-invalid'); + $('html, body').animate({scrollTop: $('[name="' + field.realName + '"]').offset().top}, 0); fieldErrors = true; } } - if (form.fields[field].sourceModel) { - if (data[field]) { - scope[form.fields[field].sourceModel + '_' + form.fields[field].sourceField + '_api_error'] = - data[field][0]; + if (field.sourceModel) { + if (data) { + scope[field.sourceModel + '_' + field.sourceField + '_api_error'] = + data[0]; //scope[form.name + '_form'][form.fields[field].sourceModel + '_' + form.fields[field].sourceField].$setValidity('apiError', false); - $('[name="' + form.fields[field].sourceModel + '_' + form.fields[field].sourceField + '"]').addClass('ng-invalid'); - $('[name="' + form.fields[field].sourceModel + '_' + form.fields[field].sourceField + '"]').ScrollTo({ "onlyIfOutside": true, "offsetTop": 100 }); + $('[name="' + field.sourceModel + '_' + field.sourceField + '"]').addClass('ng-invalid'); + $('[name="' + field.sourceModel + '_' + field.sourceField + '"]').ScrollTo({ "onlyIfOutside": true, "offsetTop": 100 }); fieldErrors = true; } } else { - if (data[field]) { - scope[field + '_api_error'] = data[field][0]; - $('[name="' + field + '"]').addClass('ng-invalid'); - $('label[for="' + field + '"] span').addClass('error-color'); - $('html, body').animate({scrollTop: $('[name="' + field + '"]').offset().top}, 0); + if (data) { + scope[fld + '_api_error'] = data[0]; + $('[name="' + fld + '"]').addClass('ng-invalid'); + $('label[for="' + fld + '"] span').addClass('error-color'); + $('html, body').animate({scrollTop: $('[name="' + fld + '"]').offset().top}, 0); fieldErrors = true; - if(form.fields[field].codeMirror){ - $(`#cm-${field}-container .CodeMirror`).addClass('error-border'); + if(field.codeMirror){ + $(`#cm-${fld}-container .CodeMirror`).addClass('error-border'); } } } + }; + + for (field in form.fields) { + if (form.fields[field].type === "checkbox_group") { + form.fields[field].fields.forEach(fld => { + addApiErrors(data[fld.name], fld, fld.name) + }); + } else { + addApiErrors(data[field], form.fields[field], field); + } } if (defaultMsg) { Alert(defaultMsg.hdr, defaultMsg.msg); diff --git a/awx/ui/client/src/shared/form-generator.js b/awx/ui/client/src/shared/form-generator.js index 994ec53a89..ba26bc6223 100644 --- a/awx/ui/client/src/shared/form-generator.js +++ b/awx/ui/client/src/shared/form-generator.js @@ -1156,6 +1156,9 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat field.max + "