diff --git a/awx/ui/client/legacy-styles/forms.less b/awx/ui/client/legacy-styles/forms.less index 2da1af9e62..e34359ee18 100644 --- a/awx/ui/client/legacy-styles/forms.less +++ b/awx/ui/client/legacy-styles/forms.less @@ -472,6 +472,7 @@ input[type='radio']:checked:before { transition: background-color 0.2s; padding-left:15px; padding-right: 15px; + margin-left: 20px; } .Form-cancelButton:hover{ @@ -496,6 +497,7 @@ input[type='radio']:checked:before { .Form-formGroup--singleColumn { width: 100% !important; padding-right: 0px; + max-width: 100% !important; } .Form-subCheckbox { diff --git a/awx/ui/client/src/app.js b/awx/ui/client/src/app.js index bd53e65ed4..1f60836f62 100644 --- a/awx/ui/client/src/app.js +++ b/awx/ui/client/src/app.js @@ -33,6 +33,7 @@ import organizations from './organizations/main'; import permissions from './permissions/main'; import managementJobs from './management-jobs/main'; import jobDetail from './job-detail/main'; +import jobSubmission from './job-submission/main'; import notifications from './notifications/main'; import access from './access/main'; @@ -103,6 +104,7 @@ var tower = angular.module('Tower', [ activityStream.name, footer.name, jobDetail.name, + jobSubmission.name, notifications.name, standardOut.name, access.name, diff --git a/awx/ui/client/src/dashboard/lists/job-templates/job-templates-list.directive.js b/awx/ui/client/src/dashboard/lists/job-templates/job-templates-list.directive.js index 9fb9839cfb..5052e073a4 100644 --- a/awx/ui/client/src/dashboard/lists/job-templates/job-templates-list.directive.js +++ b/awx/ui/client/src/dashboard/lists/job-templates/job-templates-list.directive.js @@ -1,9 +1,9 @@ /* jshint unused: vars */ export default - [ "PlaybookRun", + [ 'InitiatePlaybookRun', 'templateUrl', '$location', - function JobTemplatesList(PlaybookRun, templateUrl, $location) { + function JobTemplatesList(InitiatePlaybookRun, templateUrl, $location) { return { restrict: 'E', link: link, @@ -43,7 +43,7 @@ export default }; scope.launchJobTemplate = function(jobTemplateId){ - PlaybookRun({ scope: scope, id: jobTemplateId }); + InitiatePlaybookRun({ scope: scope, id: jobTemplateId }); }; scope.editJobTemplate = function (jobTemplateId) { diff --git a/awx/ui/client/src/helpers/Adhoc.js b/awx/ui/client/src/helpers/Adhoc.js index ef2baed981..6754a60611 100644 --- a/awx/ui/client/src/helpers/Adhoc.js +++ b/awx/ui/client/src/helpers/Adhoc.js @@ -55,12 +55,9 @@ export default // Submit request to run an adhoc comamand .factory('AdhocRun', ['$location','$stateParams', 'LaunchJob', 'PromptForPasswords', 'Rest', 'GetBasePath', 'Alert', 'ProcessErrors', - 'Wait', 'Empty', 'PromptForCredential', 'PromptForVars', - 'PromptForSurvey' , 'CreateLaunchDialog', + 'Wait', 'Empty', 'CreateLaunchDialog', function ($location, $stateParams, LaunchJob, PromptForPasswords, - Rest, GetBasePath, Alert, ProcessErrors, Wait, Empty, - PromptForCredential, PromptForVars, PromptForSurvey, - CreateLaunchDialog) { + Rest, GetBasePath, Alert, ProcessErrors, Wait, Empty, CreateLaunchDialog) { return function (params) { var id = params.project_id, scope = params.scope.$new(), diff --git a/awx/ui/client/src/helpers/JobSubmission.js b/awx/ui/client/src/helpers/JobSubmission.js index f185231c40..5cbb426ade 100644 --- a/awx/ui/client/src/helpers/JobSubmission.js +++ b/awx/ui/client/src/helpers/JobSubmission.js @@ -1,306 +1,17 @@ /************************************************* - * Copyright (c) 2015 Ansible, Inc. + * Copyright (c) 2016 Ansible, Inc. * * All Rights Reserved *************************************************/ -/** -* @ngdoc function -* @name helpers.function:JobSubmission -* @description -* The JobSubmission.js file handles launching a job via a playbook run. There is a workflow that is involved in gathering all the -* variables needed to launch a job, including credentials, passwords, extra variables, and survey data. Depending on what information -* is needed to launch the job, a modal is built that prompts the user for any required information. This modal is built by creating -* an html string with all the fields necessary to launch the job. This html string then gets compiled and opened in a dialog modal. -* -* #Workflow when user hits launch button -* -* A 'get' call is made to the API's 'job_templates/:job_template_id/launch' endpoint for that job template. The response from the API will specify -* -*``` -* "credential_needed_to_start": true, -* "can_start_without_user_input": false, -* "ask_variables_on_launch": false, -* "passwords_needed_to_start": [], -* "variables_needed_to_start": [], -* "survey_enabled": false -*``` -* #Step 1a - Check if there is a credential included in the job template: PromptForCredential -* -* The first step is to check if a credential was specified in the job template, by looking at the value of `credential_needed_to_start` . -* If this boolean is true, then that means that the user did NOT specify a credential in the job template and we must prompt them to select a credential. -* This emits a call to `PromptForCredential` which will do a lookup on the credentials endpoint and show a modal window with the list -* of credentials for the user to choose from. -* -* #Step 1b - Check if the credential requires a password: CheckPasswords -* -* The second part of this process is to check if the credential the user picks requires a prompt for a password. A call is made (in the `CheckPasswords` factory) -* to the chosen credential -* and checks if ``password: ASK`` , ``become_password:ASK`` , or ``vault_password: ASK``. If any of these are ASK, then we begin building the html string for -* each required password (see step 2). If none of these require a password, then we contine on to prompting for vars (see step 3) -* -* #Step 2 - Build password html string: PromptForPasswords -* -* We may detect from the inital 'get' call that we may need to prompt the user for passwords. The ``passwords_needed_to_start`` array from the 'get' call -* will explictly tell us which passwords we must prompt for. Alternatively, we may have found that in steps 1a and 1b that -* we have must prompt for passwords. Either way, we arrive in `PromptForPasswords` factory which builds the html string depending on how the particular credential is setup. -* -* #Step 3 - extra vars text editor: PromptForVars -* -* We may arrive at step three if the credential selected does not require a password, or if the password html string is already done being built. -* if ``ask_variables_on_launch`` was true in the inital 'get' call, then we build the extra_vars text editor in the `PromptForVars` factory. -* This factory makes a REST call to the job template and finds if any 'extra_vars' were specified in the job template. It takes any specified -* extra vars and includes them in the extra_vars text editor that is built in the same factory. This code is added to the html string and passed along -* to the next step. -* -* #Step 4 - Survey Taker: PromptForSurvey -* -* The last step in building the job submission modal is building the survey taker. If ``survey_enabled`` is true from the initial 'get' call, -* we make a REST call to the survey endpoint for the specified job and gather the survey data. The `PromptForSurvey` factory takes the survey -* data and adds to the html string any various survey question. -* -* #Step 5 - build the modal: CreateLaunchDialog -* -* At this point, we need to compile our giant html string onto the modal and open the job submission modal. This happens in the `CreateLaunch` -* factory. In this factory the 'Launch' button for the job is tied to the validity of the form, which handles the validation of these fields. -* -* #Step 6 - Launch the job: LaunchJob -* -* This is maybe the most crucial step. We have setup everything we need in order to gather information from the user and now we want to be sure -* we handle it correctly. And there are many scenarios to take into account. The first scenario we check for is is ``survey_enabled=true`` and -* ``prompt_for_vars=false``, in which case we want to make sure to include the extra_vars from the job template in the data being -* sent to the API (it is important to note that anything specified in the extra vars on job submission will override vars specified in the job template. -* Likewise, any variables specified in the extra vars that are duplicated by the survey vars, will get overridden by the survey vars). -* If the previous scenario is NOT the case, then we continue to gather the modal's answers regularly: gather the passwords, then the extra_vars, then -* any survey results. Also note that we must gather any required survey answers, as well as any optional survey answers that happened to be provided -* by the user. We also include the credential that was chosen if the user was prompted to select a credential. -* At this point we have all the info we need and we are almost ready to perform a POST to the '/launch' endpoint. We must lastly check -* if the user was not prompted for anything and therefore we don't want to pass any extra_vars to the POST. Once this is done we -* make the REST POST call and provide all the data to hte API. The response from the API will be the job ID, which is used to redirect the user -* to the job detail page for that job run. -* -* @Usage -* This is usage information. -*/ - 'use strict'; export default angular.module('JobSubmissionHelper', [ 'RestServices', 'Utilities', 'CredentialFormDefinition', 'CredentialsListDefinition', 'LookUpHelper', 'JobSubmissionHelper', 'JobTemplateFormDefinition', 'ModalDialog', 'FormGenerator', 'JobVarsPromptFormDefinition']) -.factory('LaunchJob', ['Rest', 'Wait', 'ProcessErrors', 'ToJSON', 'Empty', 'GetBasePath', -function(Rest, Wait, ProcessErrors, ToJSON, Empty, GetBasePath) { - return function(params) { - var scope = params.scope, - callback = params.callback || 'JobLaunched', - job_launch_data = {}, - url = params.url, - vars_url = GetBasePath('job_templates')+scope.job_template_id + '/', - // fld, - extra_vars; - - //found it easier to assume that there will be extra vars, and then check for a blank object at the end - job_launch_data.extra_vars = {}; - - //gather the extra vars from the job template if survey is enabled and prompt for vars is false - if (scope.removeGetExtraVars) { - scope.removeGetExtraVars(); - } - scope.removeGetExtraVars = scope.$on('GetExtraVars', function() { - - Rest.setUrl(vars_url); - Rest.get() - .success(function (data) { - if(!Empty(data.extra_vars)){ - data.extra_vars = ToJSON('yaml', data.extra_vars, false); - $.each(data.extra_vars, function(key,value){ - job_launch_data.extra_vars[key] = value; - }); - } - scope.$emit('BuildData'); - }) - .error(function (data, status) { - ProcessErrors(scope, data, status, { hdr: 'Error!', - msg: 'Failed to retrieve job template extra variables.' }); - }); - }); - - //build the data object to be sent to the job launch endpoint. Any variables gathered from the survey and the extra variables text editor are inserted into the extra_vars dict of the job_launch_data - if (scope.removeBuildData) { - scope.removeBuildData(); - } - scope.removeBuildData = scope.$on('BuildData', function() { - if(!Empty(scope.passwords_needed_to_start) && scope.passwords_needed_to_start.length>0){ - scope.passwords.forEach(function(password) { - job_launch_data[password] = scope[password]; - scope.passwords_needed_to_start.push(password+'_confirm'); // i'm pushing these values into this array for use during the survey taker parsing - }); - } - if(scope.prompt_for_vars===true){ - extra_vars = ToJSON(scope.parseType, scope.extra_vars, false); - if(!Empty(extra_vars)){ - $.each(extra_vars, function(key,value){ - job_launch_data.extra_vars[key] = value; - }); - } - - } - - if(scope.survey_enabled===true){ - for (var i=0; i < scope.survey_questions.length; i++){ - var fld = scope.survey_questions[i].variable; - // grab all survey questions that have answers - if(scope.survey_questions[i].required || (scope.survey_questions[i].required === false && scope[fld].toString()!=="")) { - job_launch_data.extra_vars[fld] = scope[fld]; - } - - - if(scope.survey_questions[i].required === false && _.isEmpty(scope[fld])) { - switch (scope.survey_questions[i].type) { - // for optional text and text-areas, submit a blank string if min length is 0 - // -- this is confusing, for an explanation see: - // http://docs.ansible.com/ansible-tower/latest/html/userguide/job_templates.html#optional-survey-questions - // - case "text": - case "textarea": - if (scope.survey_questions[i].min === 0) { - job_launch_data.extra_vars[fld] = ""; - } - break; - - // for optional select lists, if they are left blank make sure we submit - // a value that the API will consider "empty" - // - case "multiplechoice": - job_launch_data.extra_vars[fld] = ""; - break; - case "multiselect": - job_launch_data.extra_vars[fld] = []; - break; - } - } - } - } - - // include the credential used if the user was prompted to choose a cred - if(!Empty(scope.credential)){ - job_launch_data.credential_id = scope.credential; - } - - // If the extra_vars dict is empty, we don't want to include it if we didn't prompt for anything. - if(jQuery.isEmptyObject(job_launch_data.extra_vars)===true && scope.prompt_for_vars===false){ - delete job_launch_data.extra_vars; - } - - Rest.setUrl(url); - Rest.post(job_launch_data) - .success(function(data) { - Wait('stop'); - if(!$('#password-modal').is(':hidden')){ - $('#password-modal').dialog('close'); - } - scope.$emit(callback, data); - }) - .error(function(data, status) { - ProcessErrors(scope, data, status, null, { hdr: 'Error!', - msg: 'Failed updating job ' + scope.job_template_id + ' with variables. POST returned: ' + status }); - }); - }); - - // if the user has a survey and does not have 'prompt for vars' selected, then we want to - // include the extra vars from the job template in the job launch. so first check for these conditions - // and then overlay any survey vars over those. - if(scope.prompt_for_vars===false && scope.survey_enabled===true){ - scope.$emit('GetExtraVars'); - } - else { - scope.$emit('BuildData'); - } - - - }; -}]) - -.factory('PromptForCredential', ['$location', 'Wait', 'GetBasePath', 'LookUpInit', 'JobTemplateForm', 'CredentialList', 'Rest', 'Prompt', 'ProcessErrors', - 'CheckPasswords', -function($location, Wait, GetBasePath, LookUpInit, JobTemplateForm, CredentialList, Rest, Prompt, ProcessErrors, CheckPasswords) { - return function(params) { - - var scope = params.scope, - selectionMade; - - Wait('stop'); - scope.credential = ''; - - if (scope.removeShowLookupDialog) { - scope.removeShowLookupDialog(); - } - scope.removeShowLookupDialog = scope.$on('ShowLookupDialog', function() { - selectionMade = function () { - // scope.$emit(callback, scope.credential); - CheckPasswords({ - scope: scope, - credential: scope.credential, - callback: 'ContinueCred' - }); - }; - - LookUpInit({ - url: GetBasePath('credentials') + '?kind=ssh', - scope: scope, - form: JobTemplateForm(), - current_item: null, - list: CredentialList, - field: 'credential', - hdr: 'Credential Required', - instructions: "Launching this job requires a machine credential. Please select your machine credential now or Cancel to quit.", - postAction: selectionMade, - input_type: 'radio' - }); - scope.lookUpCredential(); - }); - - if (scope.removeAlertNoCredentials) { - scope.removeAlertNoCredentials(); - } - scope.removeAlertNoCredentials = scope.$on('AlertNoCredentials', function() { - var action = function () { - $('#prompt-modal').modal('hide'); - $location.url('/credentials/add'); - }; - - Prompt({ - hdr: 'Machine Credential Required', - body: "
working...