diff --git a/awx/ui/client/lib/components/tag/_index.less b/awx/ui/client/lib/components/tag/_index.less index d1b14a6893..5834c5208d 100644 --- a/awx/ui/client/lib/components/tag/_index.less +++ b/awx/ui/client/lib/components/tag/_index.less @@ -18,7 +18,6 @@ margin: 2px @at-space-2x; align-self: center; word-break: break-word; - text-transform: lowercase; &:hover, &:focus { diff --git a/awx/ui/client/lib/services/base-string.service.js b/awx/ui/client/lib/services/base-string.service.js index cab067700e..9ecf87b69b 100644 --- a/awx/ui/client/lib/services/base-string.service.js +++ b/awx/ui/client/lib/services/base-string.service.js @@ -58,6 +58,7 @@ function BaseStringService (namespace) { * the project. */ this.CANCEL = t.s('CANCEL'); + this.CLOSE = t.s('CLOSE'); this.SAVE = t.s('SAVE'); this.OK = t.s('OK'); this.NEXT = t.s('NEXT'); diff --git a/awx/ui/client/src/inventories-hosts/inventories/inventory.list.js b/awx/ui/client/src/inventories-hosts/inventories/inventory.list.js index c821221d28..4e8a0efc61 100644 --- a/awx/ui/client/src/inventories-hosts/inventories/inventory.list.js +++ b/awx/ui/client/src/inventories-hosts/inventories/inventory.list.js @@ -19,7 +19,7 @@ export default ['i18n', function(i18n) { basePath: 'inventory', title: false, disableRow: "{{ inventory.pending_deletion }}", - disableRowValue: 'pending_deletion', + disableRowValue: 'inventory.pending_deletion', fields: { status: { diff --git a/awx/ui/client/src/scheduler/schedulerAdd.controller.js b/awx/ui/client/src/scheduler/schedulerAdd.controller.js index 8af2fd78b5..c5e04c5035 100644 --- a/awx/ui/client/src/scheduler/schedulerAdd.controller.js +++ b/awx/ui/client/src/scheduler/schedulerAdd.controller.js @@ -8,12 +8,12 @@ export default ['$filter', '$state', '$stateParams', '$http', 'Wait', '$scope', '$rootScope', 'CreateSelect2', 'ParseTypeChange', 'GetBasePath', 'Rest', 'ParentObject', 'JobTemplateModel', '$q', 'Empty', 'SchedulePost', 'ProcessErrors', 'SchedulerInit', '$location', 'PromptService', 'RRuleToAPI', 'moment', - 'WorkflowJobTemplateModel', 'TemplatesStrings', 'rbacUiControlService', + 'WorkflowJobTemplateModel', 'TemplatesStrings', 'rbacUiControlService', 'Alert', 'i18n', function($filter, $state, $stateParams, $http, Wait, $scope, $rootScope, CreateSelect2, ParseTypeChange, GetBasePath, Rest, ParentObject, JobTemplate, $q, Empty, SchedulePost, ProcessErrors, SchedulerInit, $location, PromptService, RRuleToAPI, moment, - WorkflowJobTemplate, TemplatesStrings, rbacUiControlService + WorkflowJobTemplate, TemplatesStrings, rbacUiControlService, Alert, i18n ) { var base = $scope.base || $location.path().replace(/^\//, '').split('/')[0], @@ -112,6 +112,14 @@ export default ['$filter', '$state', '$stateParams', '$http', 'Wait', .then((responses) => { let launchConf = responses[1].data; + if (launchConf.passwords_needed_to_start && + launchConf.passwords_needed_to_start.length > 0 && + !launchConf.ask_credential_on_launch + ) { + Alert(i18n._('Warning'), i18n._('This Job Template has a default credential that requires a password before launch. Adding or editing schedules is prohibited while this credential is selected. To add or edit a schedule, credentials that require a password must be removed from the Job Template.'), 'alert-info'); + $state.go('^', { reload: true }); + } + let watchForPromptChanges = () => { let promptValuesToWatch = [ 'promptData.prompts.inventory.value', @@ -156,7 +164,6 @@ export default ['$filter', '$state', '$stateParams', '$http', 'Wait', !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) { $scope.showPromptButton = false; } else { diff --git a/awx/ui/client/src/scheduler/schedulerEdit.controller.js b/awx/ui/client/src/scheduler/schedulerEdit.controller.js index 9a981d352a..6d63b04361 100644 --- a/awx/ui/client/src/scheduler/schedulerEdit.controller.js +++ b/awx/ui/client/src/scheduler/schedulerEdit.controller.js @@ -1,11 +1,11 @@ export default ['$filter', '$state', '$stateParams', 'Wait', '$scope', 'moment', '$rootScope', '$http', 'CreateSelect2', 'ParseTypeChange', 'ParentObject', 'ProcessErrors', 'Rest', 'GetBasePath', 'SchedulerInit', 'SchedulePost', 'JobTemplateModel', '$q', 'Empty', 'PromptService', 'RRuleToAPI', -'WorkflowJobTemplateModel', 'TemplatesStrings', 'scheduleResolve', 'timezonesResolve', +'WorkflowJobTemplateModel', 'TemplatesStrings', 'scheduleResolve', 'timezonesResolve', 'Alert', 'i18n', function($filter, $state, $stateParams, Wait, $scope, moment, $rootScope, $http, CreateSelect2, ParseTypeChange, ParentObject, ProcessErrors, Rest, GetBasePath, SchedulerInit, SchedulePost, JobTemplate, $q, Empty, PromptService, RRuleToAPI, - WorkflowJobTemplate, TemplatesStrings, scheduleResolve, timezonesResolve + WorkflowJobTemplate, TemplatesStrings, scheduleResolve, timezonesResolve, Alert, i18n ) { let schedule, scheduler, scheduleCredentials = []; @@ -249,8 +249,15 @@ function($filter, $state, $stateParams, Wait, $scope, moment, .then((responses) => { let launchOptions = responses[0].data, launchConf = responses[1].data; + scheduleCredentials = responses[2].data.results; - scheduleCredentials = responses[2].data.results; + if (launchConf.passwords_needed_to_start && + launchConf.passwords_needed_to_start.length > 0 && + !launchConf.ask_credential_on_launch + ) { + Alert(i18n._('Warning'), i18n._('This Job Template has a default credential that requires a password before launch. Adding or editing schedules is prohibited while this credential is selected. To add or edit a schedule, credentials that require a password must be removed from the Job Template.'), 'alert-info'); + $scope.credentialRequiresPassword = true; + } let watchForPromptChanges = () => { let promptValuesToWatch = [ diff --git a/awx/ui/client/src/scheduler/schedulerForm.partial.html b/awx/ui/client/src/scheduler/schedulerForm.partial.html index d3bfd2f249..6b89b335df 100644 --- a/awx/ui/client/src/scheduler/schedulerForm.partial.html +++ b/awx/ui/client/src/scheduler/schedulerForm.partial.html @@ -28,7 +28,7 @@ name="schedulerName" id="schedulerName" ng-model="schedulerName" required - ng-disabled="!(schedule_obj.summary_fields.user_capabilities.edit || canAdd)" + ng-disabled="!(schedule_obj.summary_fields.user_capabilities.edit || canAdd) || credentialRequiresPassword" placeholder="Schedule name">
@@ -42,7 +42,7 @@
+ disabled="!(schedule_obj.summary_fields.user_capabilities.edit || canAdd) || credentialRequiresPassword">
* *
@@ -263,7 +263,7 @@ *
@@ -665,26 +665,26 @@ -
+
+ ng-disabled="!schedulerIsValid || promptModalMissingReqFields || (preview_list.isEmpty && scheduler_form.$dirty) || credentialRequiresPassword"> Save
- + diff --git a/awx/ui/client/src/scheduler/schedulerList.controller.js b/awx/ui/client/src/scheduler/schedulerList.controller.js index e09566a797..d171e52d25 100644 --- a/awx/ui/client/src/scheduler/schedulerList.controller.js +++ b/awx/ui/client/src/scheduler/schedulerList.controller.js @@ -13,13 +13,14 @@ export default [ '$filter', '$scope', '$location', '$stateParams', 'ScheduleList', 'Rest', - 'rbacUiControlService', - 'ToggleSchedule', 'DeleteSchedule', '$q', '$state', 'Dataset', 'ParentObject', 'UnifiedJobsOptions', - function($filter, $scope, $location, $stateParams, - ScheduleList, Rest, - rbacUiControlService, - ToggleSchedule, DeleteSchedule, - $q, $state, Dataset, ParentObject, UnifiedJobsOptions) { + 'rbacUiControlService', 'JobTemplateModel', 'ToggleSchedule', 'DeleteSchedule', + '$q', '$state', 'Dataset', 'ParentObject', 'UnifiedJobsOptions', 'i18n', + 'Alert', + function($filter, $scope, $location, $stateParams, ScheduleList, Rest, + rbacUiControlService, JobTemplate, ToggleSchedule, DeleteSchedule, + $q, $state, Dataset, ParentObject, UnifiedJobsOptions, i18n, + Alert + ) { var base, scheduleEndpoint, list = ScheduleList; @@ -35,6 +36,19 @@ export default [ .then(function(params) { $scope.canAdd = params.canAdd; }); + if (_.has(ParentObject, 'type') && ParentObject.type === 'job_template') { + const jobTemplate = new JobTemplate(); + jobTemplate.getLaunch(ParentObject.id) + .then(({data}) => { + if (data.passwords_needed_to_start && + data.passwords_needed_to_start.length > 0 && + !ParentObject.ask_credential_on_launch + ) { + $scope.credentialRequiresPassword = true; + $scope.addTooltip = i18n._("Using a credential that requires a password on launch is prohibited when creating a Job Template schedule"); + } + }); + } } // search init @@ -107,13 +121,15 @@ export default [ function buildTooltips(schedule) { var job = schedule.summary_fields.unified_job_template; if (schedule.enabled) { - schedule.play_tip = 'Schedule is active. Click to stop.'; + const tip = (schedule.summary_fields.user_capabilities.edit || $scope.credentialRequiresPassword) ? i18n._('Schedule is active.') : i18n._('Schedule is active. Click to stop.'); + schedule.play_tip = tip; schedule.status = 'active'; - schedule.status_tip = 'Schedule is active. Click to stop.'; + schedule.status_tip = tip; } else { - schedule.play_tip = 'Schedule is stopped. Click to activate.'; + const tip = (schedule.summary_fields.user_capabilities.edit || $scope.credentialRequiresPassword) ? i18n._('Schedule is stopped.') : i18n._('Schedule is stopped. Click to activate.'); + schedule.play_tip = tip; schedule.status = 'stopped'; - schedule.status_tip = 'Schedule is stopped. Click to activate.'; + schedule.status_tip = tip; } schedule.nameTip = $filter('sanitize')(schedule.name); diff --git a/awx/ui/client/src/scheduler/schedules.list.js b/awx/ui/client/src/scheduler/schedules.list.js index c1e3e54185..a3a32b174d 100644 --- a/awx/ui/client/src/scheduler/schedules.list.js +++ b/awx/ui/client/src/scheduler/schedules.list.js @@ -26,7 +26,7 @@ export default ['i18n', function(i18n) { ngShow: '!isValid(schedule)' }, toggleSchedule: { - ngDisabled: "!schedule.summary_fields.user_capabilities.edit", + ngDisabled: "!schedule.summary_fields.user_capabilities.edit || credentialRequiresPassword", label: '', columnClass: 'List-staticColumn--toggle', type: "toggle", @@ -70,11 +70,13 @@ export default ['i18n', function(i18n) { }, add: { mode: 'all', - ngClick: 'addSchedule()', + ngClick: 'credentialRequiresPassword || addSchedule()', awToolTip: i18n._('Add a new schedule'), + dataTipWatch: 'addTooltip', actionClass: 'at-Button--add', actionId: 'button-add', - ngShow: 'canAdd' + ngShow: 'canAdd', + ngClass: "{ 'Form-tab--disabled': credentialRequiresPassword }" } }, @@ -85,14 +87,14 @@ export default ['i18n', function(i18n) { icon: 'icon-edit', awToolTip: i18n._('Edit schedule'), dataPlacement: 'top', - ngShow: 'schedule.summary_fields.user_capabilities.edit' + ngShow: 'schedule.summary_fields.user_capabilities.edit && !credentialRequiresPassword' }, view: { label: i18n._('View'), ngClick: "editSchedule(schedule)", awToolTip: i18n._('View schedule'), dataPlacement: 'top', - ngShow: '!schedule.summary_fields.user_capabilities.edit' + ngShow: '!schedule.summary_fields.user_capabilities.edit || credentialRequiresPassword' }, "delete": { label: i18n._('Delete'), diff --git a/awx/ui/client/src/shared/list-generator/list-actions.partial.html b/awx/ui/client/src/shared/list-generator/list-actions.partial.html index 7581e3195d..8144650a95 100644 --- a/awx/ui/client/src/shared/list-generator/list-actions.partial.html +++ b/awx/ui/client/src/shared/list-generator/list-actions.partial.html @@ -50,6 +50,7 @@ data-placement="{{options.dataPlacement}}" data-container="{{options.dataContainer}}" class="{{options.actionClass}}" + ng-class="{{options.ngClass}}" id="{{options.actionId}}" data-title="{{options.dataTitle}}" ng-disabled="{{options.ngDisabled}}" diff --git a/awx/ui/client/src/shared/list-generator/list-generator.factory.js b/awx/ui/client/src/shared/list-generator/list-generator.factory.js index 8e6b240c87..b529cb2788 100644 --- a/awx/ui/client/src/shared/list-generator/list-generator.factory.js +++ b/awx/ui/client/src/shared/list-generator/list-generator.factory.js @@ -314,7 +314,7 @@ export default ['$compile', 'Attr', 'Icon', innerTable += `, {'List-tableRow--selected' : $stateParams['${list.iterator}_id'] == ${list.iterator}.id}`; } - innerTable += (list.disableRow) ? `, {true: 'List-tableRow--disabled'}[${list.iterator}.${list.disableRowValue}]` : ""; + innerTable += (list.disableRow) ? `, {'List-tableRow--disabled': ${list.disableRowValue}}` : ""; if (list.multiSelect) { innerTable += ", " + list.iterator + ".isSelected ? 'is-selected-row' : ''"; @@ -338,13 +338,13 @@ export default ['$compile', 'Attr', 'Icon', } if (list.multiSelect) { - innerTable += ''; + innerTable += ''; } // Change layout if a lookup list, place radio buttons before labels if (options.mode === 'lookup') { if (options.input_type === "radio") { //added by JT so that lookup forms can be either radio inputs or check box inputs - innerTable += ` `; + innerTable += ` `; } else { // its assumed that options.input_type = checkbox innerTable += " { @@ -559,19 +559,19 @@ angular.module('templates', [surveyMaker.name, jobTemplates.name, labels.name, p } $scope.toggle_row = function(selectedRow) { + if ($scope.workflowJobTemplateObj.summary_fields.user_capabilities.edit) { + $scope.workflow_inventory_sources.forEach(function(row, i) { + if (row.id === selectedRow.id) { + $scope.workflow_inventory_sources[i].checked = 1; + $scope.selection[list.iterator] = { + id: row.id, + name: row.name + }; - $scope.workflow_inventory_sources.forEach(function(row, i) { - if (row.id === selectedRow.id) { - $scope.workflow_inventory_sources[i].checked = 1; - $scope.selection[list.iterator] = { - id: row.id, - name: row.name - }; - - $scope.templateManuallySelected(row); - } - }); - + $scope.templateManuallySelected(row); + } + }); + } }; $scope.$watch('selectedTemplate', () => { @@ -636,19 +636,19 @@ angular.module('templates', [surveyMaker.name, jobTemplates.name, labels.name, p } $scope.toggle_row = function(selectedRow) { + if ($scope.workflowJobTemplateObj.summary_fields.user_capabilities.edit) { + $scope.projects.forEach(function(row, i) { + if (row.id === selectedRow.id) { + $scope.projects[i].checked = 1; + $scope.selection[list.iterator] = { + id: row.id, + name: row.name + }; - $scope.projects.forEach(function(row, i) { - if (row.id === selectedRow.id) { - $scope.projects[i].checked = 1; - $scope.selection[list.iterator] = { - id: row.id, - name: row.name - }; - - $scope.templateManuallySelected(row); - } - }); - + $scope.templateManuallySelected(row); + } + }); + } }; $scope.$watch('selectedTemplate', () => { @@ -708,6 +708,8 @@ angular.module('templates', [surveyMaker.name, jobTemplates.name, labels.name, p delete list.fields.labels; delete list.fieldActions; list.fields.name.columnClass = "col-md-8"; + list.disableRow = "{{ !workflowJobTemplateObj.summary_fields.user_capabilities.edit }}"; + list.disableRowValue = '!workflowJobTemplateObj.summary_fields.user_capabilities.edit'; list.iterator = 'job_template'; list.name = 'job_templates'; list.basePath = "job_templates"; @@ -733,6 +735,8 @@ angular.module('templates', [surveyMaker.name, jobTemplates.name, labels.name, p list.fields.name.columnClass = "col-md-11"; list.maxVisiblePages = 5; list.searchBarFullWidth = true; + list.disableRow = "{{ !workflowJobTemplateObj.summary_fields.user_capabilities.edit }}"; + list.disableRowValue = '!workflowJobTemplateObj.summary_fields.user_capabilities.edit'; return list; } @@ -742,6 +746,8 @@ angular.module('templates', [surveyMaker.name, jobTemplates.name, labels.name, p let list = _.cloneDeep(InventorySourcesList); list.maxVisiblePages = 5; list.searchBarFullWidth = true; + list.disableRow = "{{ !workflowJobTemplateObj.summary_fields.user_capabilities.edit }}"; + list.disableRowValue = '!workflowJobTemplateObj.summary_fields.user_capabilities.edit'; return list; } diff --git a/awx/ui/client/src/templates/prompt/prompt.block.less b/awx/ui/client/src/templates/prompt/prompt.block.less index 4b2814d5f5..1b72cfb042 100644 --- a/awx/ui/client/src/templates/prompt/prompt.block.less +++ b/awx/ui/client/src/templates/prompt/prompt.block.less @@ -21,6 +21,7 @@ padding-left:15px; padding-right: 15px; min-width: 85px; + margin-left: 20px; } .Prompt-actionButton:disabled { background-color: @d7grey; @@ -42,7 +43,6 @@ padding-right: 15px; height: 30px; min-width: 85px; - margin-right: 20px; } .Prompt-defaultButton:hover{ background-color: @btn-bg-hov; @@ -65,8 +65,6 @@ border: 1px solid @default-border; padding: 10px; border-radius: 5px; - max-height: 120px; - overflow-y: auto; } .Prompt-selectedItemRevert { display: flex; @@ -108,8 +106,9 @@ line-height: 29px; } .Prompt-previewTags--outer { + display: flex; flex: 1 0 auto; - max-width: ~"calc(100% - 140px)"; + width: ~"calc(100% - 140px)"; } .Prompt-previewTags--inner { display: flex; @@ -123,8 +122,9 @@ color: @default-list-header-bg; } .Prompt-previewTagRevert { - flex: 0 0 60px; - line-height: 29px; + display: flex; + align-items: center; + justify-content: center; } .Prompt-previewTagContainer { display: flex; @@ -142,8 +142,10 @@ text-transform: uppercase; } .Prompt-previewRowValue { - flex: 1 0 auto; max-width: 508px; + display: flex; + flex-wrap: wrap; + align-items: flex-start; } .Prompt-noSelectedItem { height: 30px; diff --git a/awx/ui/client/src/templates/prompt/prompt.controller.js b/awx/ui/client/src/templates/prompt/prompt.controller.js index 2f7baae551..16c1b61a91 100644 --- a/awx/ui/client/src/templates/prompt/prompt.controller.js +++ b/awx/ui/client/src/templates/prompt/prompt.controller.js @@ -145,7 +145,7 @@ export default [ 'Rest', 'GetBasePath', 'ProcessErrors', 'CredentialTypeModel', vm.steps.credential.includeStep = true; vm.steps.credential.tab = { _active: order === 1 ? true : false, - _disabled: order === 1 ? false : true, + _disabled: (order === 1 || vm.readOnlyPrompts) ? false : true, order: order }; order++; @@ -154,7 +154,7 @@ export default [ 'Rest', 'GetBasePath', 'ProcessErrors', 'CredentialTypeModel', vm.steps.other_prompts.includeStep = true; vm.steps.other_prompts.tab = { _active: order === 1 ? true : false, - _disabled: order === 1 ? false : true, + _disabled: (order === 1 || vm.readOnlyPrompts) ? false : true, order: order }; order++; @@ -170,12 +170,13 @@ export default [ 'Rest', 'GetBasePath', 'ProcessErrors', 'CredentialTypeModel', vm.steps.survey.includeStep = true; vm.steps.survey.tab = { _active: order === 1 ? true : false, - _disabled: order === 1 ? false : true, + _disabled: (order === 1 || vm.readOnlyPrompts) ? false : true, order: order }; order++; } vm.steps.preview.tab.order = order; + vm.steps.preview.tab._disabled = vm.readOnlyPrompts ? false : true; modal.show('PROMPT'); vm.promptData.triggerModalOpen = false; diff --git a/awx/ui/client/src/templates/prompt/prompt.directive.js b/awx/ui/client/src/templates/prompt/prompt.directive.js index e151760bb7..dcc25fb784 100644 --- a/awx/ui/client/src/templates/prompt/prompt.directive.js +++ b/awx/ui/client/src/templates/prompt/prompt.directive.js @@ -6,7 +6,8 @@ export default [ 'templateUrl', promptData: '=', onFinish: '&', actionText: '@', - preventCredsWithPasswords: '<' + preventCredsWithPasswords: '<', + readOnlyPrompts: '=' }, templateUrl: templateUrl('templates/prompt/prompt'), replace: true, diff --git a/awx/ui/client/src/templates/prompt/prompt.partial.html b/awx/ui/client/src/templates/prompt/prompt.partial.html index e336f35744..f722e58820 100644 --- a/awx/ui/client/src/templates/prompt/prompt.partial.html +++ b/awx/ui/client/src/templates/prompt/prompt.partial.html @@ -9,37 +9,53 @@
- + +
+ prevent-creds-with-passwords="vm.preventCredsWithPasswords" + read-only-prompts="vm.readOnlyPrompts">
- + +
- + +
diff --git a/awx/ui/client/src/templates/prompt/steps/credential/prompt-credential.controller.js b/awx/ui/client/src/templates/prompt/steps/credential/prompt-credential.controller.js index f42792a822..4a1da23de7 100644 --- a/awx/ui/client/src/templates/prompt/steps/credential/prompt-credential.controller.js +++ b/awx/ui/client/src/templates/prompt/steps/credential/prompt-credential.controller.js @@ -18,9 +18,16 @@ export default if(scope.credentials && scope.credentials.length > 0) { scope.credentials.forEach((credential, i) => { scope.credentials[i].checked = 0; + }); scope.promptData.prompts.credentials.value.forEach((selectedCredential) => { - if(selectedCredential.credential_type === parseInt(scope.promptData.prompts.credentials.credentialKind)) { + if (_.has(selectedCredential, 'inputs.vault_id') || _.has(selectedCredential, 'vault_id')) { + const vaultId = selectedCredential.vault_id ? selectedCredential.vault_id : _.get(selectedCredential, 'inputs.vault_id'); + selectedCredential.tag = `${selectedCredential.name } | ${vaultId}`; + } else { + selectedCredential.tag = selectedCredential.name; + } + if (selectedCredential.credential_type === parseInt(scope.promptData.prompts.credentials.credentialKind)) { scope.credentials.forEach((credential, i) => { if(scope.credentials[i].id === selectedCredential.id) { scope.credentials[i].checked = 1; @@ -135,78 +142,82 @@ export default launch = _launch_; scope.toggle_row = (selectedRow) => { - let selectedCred = _.cloneDeep(selectedRow); + if (!scope.readOnlyPrompts) { + let selectedCred = _.cloneDeep(selectedRow); - for (let i = scope.promptData.prompts.credentials.value.length - 1; i >= 0; i--) { - if(scope.promptData.prompts.credentials.value[i].credential_type === parseInt(scope.promptData.prompts.credentials.credentialKind)) { - wipePasswords(scope.promptData.prompts.credentials.value[i]); - scope.promptData.prompts.credentials.value.splice(i, 1); + for (let i = scope.promptData.prompts.credentials.value.length - 1; i >= 0; i--) { + if(scope.promptData.prompts.credentials.value[i].credential_type === parseInt(scope.promptData.prompts.credentials.credentialKind)) { + wipePasswords(scope.promptData.prompts.credentials.value[i]); + scope.promptData.prompts.credentials.value.splice(i, 1); + } } - } - scope.promptData.prompts.credentials.value.push(selectedCred); - updateNeededPasswords(selectedRow); + scope.promptData.prompts.credentials.value.push(selectedCred); + updateNeededPasswords(selectedRow); - for (let i = scope.promptData.credentialTypeMissing.length - 1; i >= 0; i--) { - if(scope.promptData.credentialTypeMissing[i].credential_type === selectedRow.credential_type) { - scope.promptData.credentialTypeMissing.splice(i,1); - i = -1; + for (let i = scope.promptData.credentialTypeMissing.length - 1; i >= 0; i--) { + if(scope.promptData.credentialTypeMissing[i].credential_type === selectedRow.credential_type) { + scope.promptData.credentialTypeMissing.splice(i,1); + i = -1; + } } } }; scope.toggle_credential = (cred) => { - // This is a checkbox click. At the time of writing this the only - // multi-select credentials on launch are vault credentials so this - // logic should only get executed when a vault credential checkbox - // is clicked. + if (!scope.readOnlyPrompts) { + // This is a checkbox click. At the time of writing this the only + // multi-select credentials on launch are vault credentials so this + // logic should only get executed when a vault credential checkbox + // is clicked. - let uncheck = false; + let uncheck = false; - let removeCredential = (credentialToRemove, index) => { - wipePasswords(credentialToRemove); - scope.promptData.prompts.credentials.value.splice(index, 1); - }; + let removeCredential = (credentialToRemove, index) => { + wipePasswords(credentialToRemove); + scope.promptData.prompts.credentials.value.splice(index, 1); + }; - // Only one vault credential per vault_id is allowed so we need to check - // to see if one has already been selected and if so replace it. - for (let i = scope.promptData.prompts.credentials.value.length - 1; i >= 0; i--) { - if(cred.credential_type === scope.promptData.prompts.credentials.value[i].credential_type) { - if(scope.promptData.prompts.credentials.value[i].id === cred.id) { - removeCredential(scope.promptData.prompts.credentials.value[i], i); - i = -1; - uncheck = true; - } - else if(scope.promptData.prompts.credentials.value[i].inputs) { - if(cred.inputs.vault_id === scope.promptData.prompts.credentials.value[i].inputs.vault_id) { + // Only one vault credential per vault_id is allowed so we need to check + // to see if one has already been selected and if so replace it. + for (let i = scope.promptData.prompts.credentials.value.length - 1; i >= 0; i--) { + if(cred.credential_type === scope.promptData.prompts.credentials.value[i].credential_type) { + if(scope.promptData.prompts.credentials.value[i].id === cred.id) { removeCredential(scope.promptData.prompts.credentials.value[i], i); + i = -1; + uncheck = true; } - } else if(scope.promptData.prompts.credentials.value[i].vault_id) { - if(cred.inputs.vault_id === scope.promptData.prompts.credentials.value[i].vault_id) { - removeCredential(scope.promptData.prompts.credentials.value[i], i); - } - } else { - // The currently selected vault credential does not have a vault_id - if(!cred.inputs.vault_id || cred.inputs.vault_id === "") { - removeCredential(scope.promptData.prompts.credentials.value[i], i); + else if(scope.promptData.prompts.credentials.value[i].inputs) { + if(cred.inputs.vault_id === scope.promptData.prompts.credentials.value[i].inputs.vault_id) { + removeCredential(scope.promptData.prompts.credentials.value[i], i); + } + } else if(scope.promptData.prompts.credentials.value[i].vault_id) { + if(cred.inputs.vault_id === scope.promptData.prompts.credentials.value[i].vault_id) { + removeCredential(scope.promptData.prompts.credentials.value[i], i); + } + } else { + // The currently selected vault credential does not have a vault_id + if(!cred.inputs.vault_id || cred.inputs.vault_id === "") { + removeCredential(scope.promptData.prompts.credentials.value[i], i); + } } } } - } - if(!uncheck) { - scope.promptData.prompts.credentials.value.push(cred); - updateNeededPasswords(cred); + if(!uncheck) { + scope.promptData.prompts.credentials.value.push(cred); + updateNeededPasswords(cred); - _.remove(scope.promptData.credentialTypeMissing, (missingCredType) => { - return ( - missingCredType.credential_type === cred.credential_type && - _.get(cred, 'inputs.vault_id') === _.get(missingCredType, 'vault_id') - ); - }); - } else { - if(scope.promptData.launchConf.defaults.credentials && scope.promptData.launchConf.defaults.credentials.length > 0) { - checkMissingCredType(cred); + _.remove(scope.promptData.credentialTypeMissing, (missingCredType) => { + return ( + missingCredType.credential_type === cred.credential_type && + _.get(cred, 'inputs.vault_id') === _.get(missingCredType, 'vault_id') + ); + }); + } else { + if(scope.promptData.launchConf.defaults.credentials && scope.promptData.launchConf.defaults.credentials.length > 0) { + checkMissingCredType(cred); + } } } }; @@ -259,7 +270,8 @@ export default }); }; - vm.deleteSelectedCredential = (credentialToDelete) => { + vm.deleteSelectedCredential = (index) => { + const credentialToDelete = scope.promptData.prompts.credentials.value[index]; for (let i = scope.promptData.prompts.credentials.value.length - 1; i >= 0; i--) { if(scope.promptData.prompts.credentials.value[i].id === credentialToDelete.id) { if(scope.promptData.launchConf.defaults.credentials && scope.promptData.launchConf.defaults.credentials.length > 0) { @@ -312,7 +324,7 @@ export default }; vm.showRevertCredentials = () => { - if(scope.promptData.launchConf.ask_credential_on_launch) { + if(!scope.readOnlyPrompts && scope.promptData.launchConf.ask_credential_on_launch) { if(scope.promptData.prompts.credentials.value && _.has(scope, 'promptData.launchConf.defaults.credentials') && (scope.promptData.prompts.credentials.value.length === scope.promptData.launchConf.defaults.credentials.length)) { let selectedIds = scope.promptData.prompts.credentials.value.map((x) => { return x.id; }).sort(); let defaultIds = _.has(scope, 'promptData.launchConf.defaults.credentials') ? scope.promptData.launchConf.defaults.credentials.map((x) => { return x.id; }).sort() : []; diff --git a/awx/ui/client/src/templates/prompt/steps/credential/prompt-credential.directive.js b/awx/ui/client/src/templates/prompt/steps/credential/prompt-credential.directive.js index 80d277db7a..8d971dfff8 100644 --- a/awx/ui/client/src/templates/prompt/steps/credential/prompt-credential.directive.js +++ b/awx/ui/client/src/templates/prompt/steps/credential/prompt-credential.directive.js @@ -12,7 +12,8 @@ export default [ 'templateUrl', '$compile', 'generateList', scope: { promptData: '=', credentialPasswordsForm: '=', - preventCredsWithPasswords: '<' + preventCredsWithPasswords: '<', + readOnlyPrompts: '<' }, templateUrl: templateUrl('templates/prompt/steps/credential/prompt-credential'), controller: promptCredentialController, @@ -43,6 +44,9 @@ export default [ 'templateUrl', '$compile', 'generateList', }; } + list.disableRow = "{{ readOnlyPrompts }}"; + list.disableRowValue = "readOnlyPrompts"; + let html = GenerateList.build({ list: list, input_type: inputType, diff --git a/awx/ui/client/src/templates/prompt/steps/credential/prompt-credential.partial.html b/awx/ui/client/src/templates/prompt/steps/credential/prompt-credential.partial.html index 4421e9c933..ed1e7204c8 100644 --- a/awx/ui/client/src/templates/prompt/steps/credential/prompt-credential.partial.html +++ b/awx/ui/client/src/templates/prompt/steps/credential/prompt-credential.partial.html @@ -7,31 +7,19 @@
{{:: vm.strings.get('prompt.NO_CREDENTIALS_SELECTED') }}
-
-
- - - - - - -
-
- - {{ credential.name }} - - - {{ credential.name }} | {{ credential.vault_id ? credential.vault_id : credential.inputs.vault_id }} - -
-
- -
-
+ + + + + +
diff --git a/awx/ui/client/src/templates/prompt/steps/inventory/prompt-inventory.controller.js b/awx/ui/client/src/templates/prompt/steps/inventory/prompt-inventory.controller.js index 2529558349..658626906f 100644 --- a/awx/ui/client/src/templates/prompt/steps/inventory/prompt-inventory.controller.js +++ b/awx/ui/client/src/templates/prompt/steps/inventory/prompt-inventory.controller.js @@ -18,8 +18,23 @@ export default launch = _launch_; scope.toggle_row = (row) => { - scope.promptData.prompts.inventory.value = row; + if (!scope.readOnlyPrompts) { + scope.promptData.prompts.inventory.value = row; + } }; + + scope.$watchCollection('inventories', () => { + if(scope.inventories && scope.inventories.length > 0) { + scope.inventories.forEach((credential, i) => { + if (_.has(scope, 'promptData.prompts.inventory.value.id') && scope.promptData.prompts.inventory.value.id === scope.inventories[i].id) { + scope.inventories[i].checked = 1; + } else { + scope.inventories[i].checked = 0; + } + + }); + } + }); }; vm.deleteSelectedInventory = () => { diff --git a/awx/ui/client/src/templates/prompt/steps/inventory/prompt-inventory.directive.js b/awx/ui/client/src/templates/prompt/steps/inventory/prompt-inventory.directive.js index e3b1d24823..4f6c4eed8d 100644 --- a/awx/ui/client/src/templates/prompt/steps/inventory/prompt-inventory.directive.js +++ b/awx/ui/client/src/templates/prompt/steps/inventory/prompt-inventory.directive.js @@ -10,7 +10,8 @@ export default [ 'templateUrl', 'QuerySet', 'GetBasePath', 'generateList', '$com (templateUrl, qs, GetBasePath, GenerateList, $compile, InventoryList) => { return { scope: { - promptData: '=' + promptData: '=', + readOnlyPrompts: '<' }, templateUrl: templateUrl('templates/prompt/steps/inventory/prompt-inventory'), controller: promptInventoryController, @@ -43,6 +44,8 @@ export default [ 'templateUrl', 'QuerySet', 'GetBasePath', 'generateList', '$com scope.inventories = scope.inventory_dataset.results; let invList = _.cloneDeep(InventoryList); + invList.disableRow = "{{ readOnlyPrompts }}"; + invList.disableRowValue = "readOnlyPrompts"; let html = GenerateList.build({ list: invList, input_type: 'radio', diff --git a/awx/ui/client/src/templates/prompt/steps/inventory/prompt-inventory.partial.html b/awx/ui/client/src/templates/prompt/steps/inventory/prompt-inventory.partial.html index 3292da4a98..17dc1ce918 100644 --- a/awx/ui/client/src/templates/prompt/steps/inventory/prompt-inventory.partial.html +++ b/awx/ui/client/src/templates/prompt/steps/inventory/prompt-inventory.partial.html @@ -6,19 +6,11 @@
{{:: vm.strings.get('prompt.NO_INVENTORY_SELECTED') }}
-
-
-
- {{promptData.prompts.inventory.value.name}} -
-
- -
-
-
+ +
- {{:: vm.strings.get('prompt.REVERT') }} + {{:: vm.strings.get('prompt.REVERT') }}
diff --git a/awx/ui/client/src/templates/prompt/steps/other-prompts/prompt-other-prompts.directive.js b/awx/ui/client/src/templates/prompt/steps/other-prompts/prompt-other-prompts.directive.js index 3a4990ae10..361f60e145 100644 --- a/awx/ui/client/src/templates/prompt/steps/other-prompts/prompt-other-prompts.directive.js +++ b/awx/ui/client/src/templates/prompt/steps/other-prompts/prompt-other-prompts.directive.js @@ -13,7 +13,8 @@ export default [ 'templateUrl', promptData: '=', otherPromptsForm: '=', isActiveStep: '=', - validate: '=' + validate: '=', + readOnlyPrompts: '<' }, templateUrl: templateUrl('templates/prompt/steps/other-prompts/prompt-other-prompts'), controller: promptOtherPrompts, diff --git a/awx/ui/client/src/templates/prompt/steps/other-prompts/prompt-other-prompts.partial.html b/awx/ui/client/src/templates/prompt/steps/other-prompts/prompt-other-prompts.partial.html index 9d731d185f..9ab3aa09d5 100644 --- a/awx/ui/client/src/templates/prompt/steps/other-prompts/prompt-other-prompts.partial.html +++ b/awx/ui/client/src/templates/prompt/steps/other-prompts/prompt-other-prompts.partial.html @@ -13,6 +13,7 @@ name="job_type" tabindex="-1" aria-hidden="true" + ng-disabled="readOnlyPrompts" required> @@ -23,7 +24,12 @@ {{:: vm.strings.get('prompt.LIMIT') }}
- +
@@ -40,6 +46,7 @@ name="verbosity" tabindex="-1" aria-hidden="true" + ng-disabled="readOnlyPrompts" required> @@ -58,6 +65,7 @@ name="job_tags" tabindex="-1" aria-hidden="true" + ng-disabled="readOnlyPrompts" multiple>
@@ -75,6 +83,7 @@ name="skip_tags" tabindex="-1" aria-hidden="true" + ng-disabled="readOnlyPrompts" multiple> @@ -85,8 +94,8 @@
- - + +
@@ -104,7 +113,7 @@
- +
diff --git a/awx/ui/client/src/templates/prompt/steps/preview/prompt-preview.partial.html b/awx/ui/client/src/templates/prompt/steps/preview/prompt-preview.partial.html index ba83655f9b..8c1ccf1f60 100644 --- a/awx/ui/client/src/templates/prompt/steps/preview/prompt-preview.partial.html +++ b/awx/ui/client/src/templates/prompt/steps/preview/prompt-preview.partial.html @@ -9,19 +9,11 @@
{{:: vm.strings.get('prompt.CREDENTIAL') }}
-
-
- - - - - - - - - -
-
+ +
diff --git a/awx/ui/client/src/templates/prompt/steps/survey/prompt-survey.directive.js b/awx/ui/client/src/templates/prompt/steps/survey/prompt-survey.directive.js index 80e07fd404..eb1ae7169f 100644 --- a/awx/ui/client/src/templates/prompt/steps/survey/prompt-survey.directive.js +++ b/awx/ui/client/src/templates/prompt/steps/survey/prompt-survey.directive.js @@ -11,7 +11,8 @@ export default [ 'templateUrl', return { scope: { promptData: '=', - surveyForm: '=' + surveyForm: '=', + readOnlyPrompts: '<' }, templateUrl: templateUrl('templates/prompt/steps/survey/prompt-survey'), controller: promptSurvey, diff --git a/awx/ui/client/src/templates/prompt/steps/survey/prompt-survey.partial.html b/awx/ui/client/src/templates/prompt/steps/survey/prompt-survey.partial.html index 73d23c6f81..98c32ff217 100644 --- a/awx/ui/client/src/templates/prompt/steps/survey/prompt-survey.partial.html +++ b/awx/ui/client/src/templates/prompt/steps/survey/prompt-survey.partial.html @@ -9,12 +9,12 @@
- +
{{:: vm.strings.get('prompt.PLEASE_ENTER_ANSWER') }}
Please enter an answer between {{question.minlength}} to {{question.maxlength}} characters long.
- +
{{:: vm.strings.get('prompt.PLEASE_ENTER_ANSWER') }}
Please enter an answer between {{question.minlength}} to {{question.maxlength}} characters long.
@@ -23,20 +23,20 @@ - - + +
{{:: vm.strings.get('prompt.PLEASE_ENTER_ANSWER') }}
Please enter an answer between {{question.minlength}} to {{question.maxlength}} characters long.
- +
{{:: vm.strings.get('prompt.PLEASE_ENTER_ANSWER') }}
{{:: vm.strings.get('prompt.VALID_INTEGER') }}
Please enter an answer between {{question.minValue}} and {{question.maxValue}}.
- +
{{:: vm.strings.get('prompt.PLEASE_ENTER_ANSWER') }}
{{:: vm.strings.get('prompt.VALID_DECIMAL') }}
Please enter an answer between {{question.minValue}} and {{question.maxValue}}.
@@ -49,6 +49,7 @@ choices="question.choices" ng-required="question.required" ng-model="question.model" + ng-disabled="readOnlyPrompts" form-element-name="survey_question_{{$index}}">
@@ -61,6 +62,7 @@ choices="question.choices" ng-required="question.required" ng-model="question.model" + ng-disabled="readOnlyPrompts" form-element-name="survey_question_{{$index}}">
{{:: vm.strings.get('prompt.PLEASE_SELECT_VALUE') }}
diff --git a/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.partial.html b/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.partial.html index aca4d30f13..6d92841eb0 100644 --- a/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.partial.html +++ b/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.partial.html @@ -112,6 +112,7 @@ class="form-control Form-dropDown" name="edgeType" tabindex="-1" + ng-disabled="!workflowJobTemplateObj.summary_fields.user_capabilities.edit" aria-hidden="true"> @@ -129,5 +130,5 @@ - +