diff --git a/awx/ui/client/src/shared/main.js b/awx/ui/client/src/shared/main.js index eccf67be4e..3fdf3bdf95 100644 --- a/awx/ui/client/src/shared/main.js +++ b/awx/ui/client/src/shared/main.js @@ -23,6 +23,7 @@ import apiLoader from './api-loader'; import variables from './variables/main'; import parse from './parse/main'; import loadconfig from './load-config/main'; +import nextpage from './next-page/main'; import Modal from './Modal'; import moment from './moment/main'; import config from './config/main'; @@ -50,6 +51,7 @@ angular.module('shared', [listGenerator.name, variables.name, parse.name, loadconfig.name, + nextpage.name, Modal.name, moment.name, config.name, diff --git a/awx/ui/client/src/shared/next-page/main.js b/awx/ui/client/src/shared/next-page/main.js new file mode 100644 index 0000000000..8d9e83f28a --- /dev/null +++ b/awx/ui/client/src/shared/next-page/main.js @@ -0,0 +1,5 @@ +import NextPage from './next-page.factory'; + +export default + angular.module('nextpage', []) + .factory('NextPage', NextPage); diff --git a/awx/ui/client/src/shared/next-page/next-page.factory.js b/awx/ui/client/src/shared/next-page/next-page.factory.js new file mode 100644 index 0000000000..fa5bce3dc7 --- /dev/null +++ b/awx/ui/client/src/shared/next-page/next-page.factory.js @@ -0,0 +1,28 @@ +export default + function NextPage(Rest, $q) { + return function(params) { + + let getNext = function(getNextParams){ + Rest.setUrl(getNextParams.url); + return Rest.get() + .then(function (res) { + if (res.data.next) { + return getNext({ + url: res.data.next, + arrayOfValues: getNextParams.arrayOfValues.concat(res.data.results) + }); + } else { + return $q.resolve(getNextParams.arrayOfValues.concat(res.data.results)); + } + }) + .catch(function(response) { + return $q.reject( response ); + }); + }; + + return getNext(params); + + }; + } + +NextPage.$inject = ['Rest', '$q']; diff --git a/awx/ui/client/src/templates/job_templates/add-job-template/job-template-add.controller.js b/awx/ui/client/src/templates/job_templates/add-job-template/job-template-add.controller.js index a76ac90481..e9b2176471 100644 --- a/awx/ui/client/src/templates/job_templates/add-job-template/job-template-add.controller.js +++ b/awx/ui/client/src/templates/job_templates/add-job-template/job-template-add.controller.js @@ -8,25 +8,16 @@ [ '$filter', '$scope', '$stateParams', 'JobTemplateForm', 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'GetBasePath', 'md5Setup', 'ParseTypeChange', 'Wait', - 'Empty', 'ToJSON', 'CallbackHelpInit', 'GetChoices', '$state', - 'CreateSelect2', '$q', 'i18n', 'Inventory', 'Project', 'InstanceGroupsService', 'MultiCredentialService', + 'Empty', 'ToJSON', 'CallbackHelpInit', 'GetChoices', '$state', 'availableLabels', + 'CreateSelect2', '$q', 'i18n', 'Inventory', 'Project', 'InstanceGroupsService', 'MultiCredentialService', function( $filter, $scope, $stateParams, JobTemplateForm, GenerateForm, Rest, Alert, ProcessErrors, GetBasePath, md5Setup, ParseTypeChange, Wait, Empty, ToJSON, CallbackHelpInit, GetChoices, - $state, CreateSelect2, $q, i18n, Inventory, Project, InstanceGroupsService, MultiCredentialService + $state, availableLabels, CreateSelect2, $q, i18n, Inventory, Project, InstanceGroupsService, MultiCredentialService ) { - - Rest.setUrl(GetBasePath('job_templates')); - Rest.options() - .success(function(data) { - if (!data.actions.POST) { - $state.go("^"); - Alert(i18n._('Permission Error'), i18n._('You do not have permission to add a job template.'), 'alert-info'); - } - }); - + // Inject dynamic view let defaultUrl = GetBasePath('job_templates'), form = JobTemplateForm(), @@ -61,7 +52,6 @@ $scope[form.name + '_form'].$setDirty(); }; - var selectCount = 0; if ($scope.removeChoicesReady) { @@ -118,20 +108,9 @@ callback: 'choicesReadyVerbosity' }); - Rest.setUrl(GetBasePath('labels')); - Rest.get() - .success(function (data) { - $scope.labelOptions = data.results - .map((i) => ({label: i.name, value: i.id})); - $scope.$emit("choicesReadyVerbosity"); - }) - .error(function (data, status) { - ProcessErrors($scope, data, status, form, { - hdr: 'Error!', - msg: 'Failed to get labels. GET returned ' + - 'status: ' + status - }); - }); + $scope.labelOptions = availableLabels + .map((i) => ({label: i.name, value: i.id})); + $scope.$emit("choicesReadyVerbosity"); function sync_playbook_select2() { CreateSelect2({ @@ -236,131 +215,6 @@ $state.go('templates.editJobTemplate', {job_template_id: id}, {reload: true}); } - - if ($scope.removeTemplateSaveSuccess) { - $scope.removeTemplateSaveSuccess(); - } - $scope.removeTemplateSaveSuccess = $scope.$on('templateSaveSuccess', function(e, data) { - Wait('stop'); - if (data.related && - data.related.callback) { - Alert('Callback URL', -`Host callbacks are enabled for this template. The callback URL is: -
- - ${$scope.callback_server_path} - ${data.related.callback} - -
-The host configuration key is: - - ${$filter('sanitize')(data.host_config_key)} - -
`, - 'alert-danger', saveCompleted, null, null, - null, true); - } - - MultiCredentialService - .saveExtraCredentials({ - creds: $scope.selectedCredentials.extra, - url: data.related.extra_credentials - }); - - var orgDefer = $q.defer(); - var associationDefer = $q.defer(); - Rest.setUrl(data.related.labels); - - var currentLabels = Rest.get() - .then(function(data) { - return data.data.results - .map(val => val.id); - }); - - currentLabels.then(function (current) { - var labelsToAdd = ($scope.labels || []) - .map(val => val.value); - var labelsToDisassociate = current - .filter(val => labelsToAdd - .indexOf(val) === -1) - .map(val => ({id: val, disassociate: true})); - var labelsToAssociate = labelsToAdd - .filter(val => current - .indexOf(val) === -1) - .map(val => ({id: val, associate: true})); - var pass = labelsToDisassociate - .concat(labelsToAssociate); - associationDefer.resolve(pass); - }); - - Rest.setUrl(GetBasePath("organizations")); - Rest.get() - .success(function(data) { - orgDefer.resolve(data.results[0].id); - }); - - orgDefer.promise.then(function(orgId) { - var toPost = []; - $scope.newLabels = $scope.newLabels - .map(function(i, val) { - val.organization = orgId; - return val; - }); - - $scope.newLabels.each(function(i, val) { - toPost.push(val); - }); - - associationDefer.promise.then(function(arr) { - toPost = toPost - .concat(arr); - - Rest.setUrl(data.related.labels); - - var defers = []; - for (var i = 0; i < toPost.length; i++) { - defers.push(Rest.post(toPost[i])); - } - $q.all(defers) - .then(function() { - $scope.addedItem = data.id; - - if($scope.survey_questions && - $scope.survey_questions.length > 0){ - //once the job template information - // is saved we submit the survey - // info to the correct endpoint - var url = data.url+ 'survey_spec/'; - Rest.setUrl(url); - Rest.post({ name: $scope.survey_name, - description: $scope.survey_description, - spec: $scope.survey_questions }) - .success(function () { - Wait('stop'); - }) - .error(function (data, - status) { - ProcessErrors( - $scope, - data, - status, - form, - { - hdr: 'Error!', - msg: 'Failed to add new ' + - 'survey. Post returned ' + - 'status: ' + - status - }); - }); - } - - saveCompleted(data.id); - }); - }); - }); - }); - // Save $scope.formSave = function () { var fld, data = {}; @@ -443,7 +297,124 @@ Rest.setUrl(defaultUrl); Rest.post(data) .then(({data}) => { - $scope.$emit('templateSaveSuccess', data); + + Wait('stop'); + if (data.related && data.related.callback) { + Alert('Callback URL', + `Host callbacks are enabled for this template. The callback URL is: ++ + ${$scope.callback_server_path} + ${data.related.callback} + +
+The host configuration key is: + + ${$filter('sanitize')(data.host_config_key)} + +
`, + 'alert-danger', saveCompleted, null, null, + null, true); + } + + MultiCredentialService + .saveExtraCredentials({ + creds: $scope.selectedCredentials.extra, + url: data.related.extra_credentials + }); + + var orgDefer = $q.defer(); + var associationDefer = $q.defer(); + Rest.setUrl(data.related.labels); + + var currentLabels = Rest.get() + .then(function(data) { + return data.data.results + .map(val => val.id); + }); + + currentLabels.then(function (current) { + var labelsToAdd = ($scope.labels || []) + .map(val => val.value); + var labelsToDisassociate = current + .filter(val => labelsToAdd + .indexOf(val) === -1) + .map(val => ({id: val, disassociate: true})); + var labelsToAssociate = labelsToAdd + .filter(val => current + .indexOf(val) === -1) + .map(val => ({id: val, associate: true})); + var pass = labelsToDisassociate + .concat(labelsToAssociate); + associationDefer.resolve(pass); + }); + + Rest.setUrl(GetBasePath("organizations")); + Rest.get() + .success(function(data) { + orgDefer.resolve(data.results[0].id); + }); + + orgDefer.promise.then(function(orgId) { + var toPost = []; + $scope.newLabels = $scope.newLabels + .map(function(i, val) { + val.organization = orgId; + return val; + }); + + $scope.newLabels.each(function(i, val) { + toPost.push(val); + }); + + associationDefer.promise.then(function(arr) { + toPost = toPost + .concat(arr); + + Rest.setUrl(data.related.labels); + + var defers = []; + for (var i = 0; i < toPost.length; i++) { + defers.push(Rest.post(toPost[i])); + } + $q.all(defers) + .then(function() { + $scope.addedItem = data.id; + + if($scope.survey_questions && + $scope.survey_questions.length > 0){ + //once the job template information + // is saved we submit the survey + // info to the correct endpoint + var url = data.url+ 'survey_spec/'; + Rest.setUrl(url); + Rest.post({ name: $scope.survey_name, + description: $scope.survey_description, + spec: $scope.survey_questions }) + .success(function () { + Wait('stop'); + }) + .error(function (data, + status) { + ProcessErrors( + $scope, + data, + status, + form, + { + hdr: 'Error!', + msg: 'Failed to add new ' + + 'survey. Post returned ' + + 'status: ' + + status + }); + }); + } + + saveCompleted(data.id); + }); + }); + }); const instance_group_url = data.related.instance_groups; InstanceGroupsService.addInstanceGroups(instance_group_url, $scope.instance_groups) diff --git a/awx/ui/client/src/templates/job_templates/edit-job-template/job-template-edit.controller.js b/awx/ui/client/src/templates/job_templates/edit-job-template/job-template-edit.controller.js index a0173af964..ff88cf9473 100644 --- a/awx/ui/client/src/templates/job_templates/edit-job-template/job-template-edit.controller.js +++ b/awx/ui/client/src/templates/job_templates/edit-job-template/job-template-edit.controller.js @@ -14,17 +14,17 @@ export default [ '$filter', '$scope', '$rootScope', '$location', '$stateParams', 'JobTemplateForm', 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'GetBasePath', 'md5Setup', - 'ParseTypeChange', 'Wait', + 'ParseTypeChange', 'Wait', 'selectedLabels', 'Empty', 'Prompt', 'ToJSON', 'GetChoices', 'CallbackHelpInit', 'InitiatePlaybookRun' , 'initSurvey', '$state', 'CreateSelect2', - 'ToggleNotification','$q', 'InstanceGroupsService', 'InstanceGroupsData', 'MultiCredentialService', + 'ToggleNotification','$q', 'InstanceGroupsService', 'InstanceGroupsData', 'MultiCredentialService', 'availableLabels', function( $filter, $scope, $rootScope, $location, $stateParams, JobTemplateForm, GenerateForm, Rest, Alert, ProcessErrors, GetBasePath, md5Setup, - ParseTypeChange, Wait, + ParseTypeChange, Wait, selectedLabels, Empty, Prompt, ToJSON, GetChoices, CallbackHelpInit, InitiatePlaybookRun, SurveyControllerInit, $state, - CreateSelect2, ToggleNotification, $q, InstanceGroupsService, InstanceGroupsData, MultiCredentialService + CreateSelect2, ToggleNotification, $q, InstanceGroupsService, InstanceGroupsData, MultiCredentialService, availableLabels ) { $scope.$watch('job_template_obj.summary_fields.user_capabilities.edit', function(val) { @@ -39,7 +39,7 @@ export default base = $location.path().replace(/^\//, '').split('/')[0], master = {}, id = $stateParams.job_template_id, - checkSCMStatus, getPlaybooks, callback, + callback, choicesCount = 0, instance_group_url = defaultUrl + id + '/instance_groups'; @@ -61,6 +61,88 @@ export default id: id, templateType: 'job_template' }); + + $scope.$watch('project', function (newValue, oldValue) { + if (newValue !== oldValue) { + var url; + if ($scope.playbook) { + $scope.playbook_options = [$scope.playbook]; + } + + if (!Empty($scope.project)) { + let promises = []; + url = GetBasePath('projects') + $scope.project + '/playbooks/'; + Wait('start'); + Rest.setUrl(url); + promises.push(Rest.get() + .success(function (data) { + $scope.disablePlaybookBecausePermissionDenied = false; + $scope.playbook_options = []; + var playbookNotFound = true; + for (var i = 0; i < data.length; i++) { + $scope.playbook_options.push(data[i]); + if (data[i] === $scope.playbook) { + $scope.job_template_form.playbook.$setValidity('required', true); + playbookNotFound = false; + } + } + $scope.playbookNotFound = playbookNotFound; + sync_playbook_select2(); + if ($scope.playbook) { + jobTemplateLoadFinished(); + } + }) + .error(function (ret,status_code) { + if (status_code === 403) { + /* user doesn't have access to see the project, no big deal. */ + $scope.disablePlaybookBecausePermissionDenied = true; + } else { + Alert('Missing Playbooks', 'Unable to retrieve the list of playbooks for this project. Choose a different ' + + ' project or make the playbooks available on the file system.', 'alert-info'); + } + Wait('stop'); + })); + + + Rest.setUrl(GetBasePath('projects') + $scope.project + '/'); + promises.push(Rest.get() + .success(function (data) { + var msg; + switch (data.status) { + case 'failed': + msg = "