mirror of
https://github.com/ansible/awx.git
synced 2026-01-11 10:00:01 -03:30
Merge pull request #1202 from mabashian/modularize-job-templates
Moved job template routes, controllers, and views out into its own module
This commit is contained in:
commit
92f8885946
@ -47,7 +47,7 @@ import login from './login/main';
|
||||
import activityStream from './activity-stream/main';
|
||||
import standardOut from './standard-out/main';
|
||||
import lookUpHelper from './lookup/main';
|
||||
import {JobTemplatesList, JobTemplatesAdd, JobTemplatesEdit} from './controllers/JobTemplates';
|
||||
import JobTemplates from './job-templates/main';
|
||||
import {ScheduleEditController} from './controllers/Schedules';
|
||||
import {ProjectsList, ProjectsAdd, ProjectsEdit} from './controllers/Projects';
|
||||
import {OrganizationsList, OrganizationsAdd, OrganizationsEdit} from './controllers/Organizations';
|
||||
@ -65,7 +65,6 @@ import './shared/directives';
|
||||
import './shared/filters';
|
||||
import './shared/InventoryTree';
|
||||
import './shared/Socket';
|
||||
import './job-templates/main';
|
||||
import './shared/features/main';
|
||||
import './login/authenticationServices/pendo/ng-pendo';
|
||||
import footer from './footer/main';
|
||||
@ -101,6 +100,7 @@ var tower = angular.module('Tower', [
|
||||
jobDetail.name,
|
||||
notifications.name,
|
||||
standardOut.name,
|
||||
JobTemplates.name,
|
||||
'templates',
|
||||
'Utilities',
|
||||
'OrganizationFormDefinition',
|
||||
@ -297,52 +297,6 @@ var tower = angular.module('Tower', [
|
||||
}
|
||||
}).
|
||||
|
||||
state('jobTemplates', {
|
||||
url: '/job_templates',
|
||||
templateUrl: urlPrefix + 'partials/job_templates.html',
|
||||
controller: JobTemplatesList,
|
||||
data: {
|
||||
activityStream: true,
|
||||
activityStreamTarget: 'job_template'
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
label: "JOB TEMPLATES"
|
||||
},
|
||||
resolve: {
|
||||
features: ['FeaturesService', function(FeaturesService) {
|
||||
return FeaturesService.get();
|
||||
}]
|
||||
}
|
||||
}).
|
||||
|
||||
state('jobTemplates.add', {
|
||||
url: '/add',
|
||||
templateUrl: urlPrefix + 'partials/job_templates.html',
|
||||
controller: JobTemplatesAdd,
|
||||
ncyBreadcrumb: {
|
||||
parent: "jobTemplates",
|
||||
label: "CREATE JOB TEMPLATE"
|
||||
},
|
||||
resolve: {
|
||||
features: ['FeaturesService', function(FeaturesService) {
|
||||
return FeaturesService.get();
|
||||
}]
|
||||
}
|
||||
}).
|
||||
|
||||
state('jobTemplates.edit', {
|
||||
url: '/:template_id',
|
||||
templateUrl: urlPrefix + 'partials/job_templates.html',
|
||||
controller: JobTemplatesEdit,
|
||||
data: {
|
||||
activityStreamId: 'template_id'
|
||||
},
|
||||
resolve: {
|
||||
features: ['FeaturesService', function(FeaturesService) {
|
||||
return FeaturesService.get();
|
||||
}]
|
||||
}
|
||||
}).
|
||||
state('projects', {
|
||||
url: '/projects',
|
||||
templateUrl: urlPrefix + 'partials/projects.html',
|
||||
@ -458,28 +412,6 @@ var tower = angular.module('Tower', [
|
||||
}
|
||||
}).
|
||||
|
||||
state('inventoryJobTemplateAdd', {
|
||||
url: '/inventories/:inventory_id/job_templates/add',
|
||||
templateUrl: urlPrefix + 'partials/job_templates.html',
|
||||
controller: JobTemplatesAdd,
|
||||
resolve: {
|
||||
features: ['FeaturesService', function(FeaturesService) {
|
||||
return FeaturesService.get();
|
||||
}]
|
||||
}
|
||||
}).
|
||||
|
||||
state('inventoryJobTemplateEdit', {
|
||||
url: '/inventories/:inventory_id/job_templates/:template_id',
|
||||
templateUrl: urlPrefix + 'partials/job_templates.html',
|
||||
controller: JobTemplatesEdit,
|
||||
resolve: {
|
||||
features: ['FeaturesService', function(FeaturesService) {
|
||||
return FeaturesService.get();
|
||||
}]
|
||||
}
|
||||
}).
|
||||
|
||||
state('inventoryManage', {
|
||||
url: '/inventories/:inventory_id/manage?groups',
|
||||
templateUrl: urlPrefix + 'partials/inventory-manage.html',
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,19 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import {templateUrl} from '../../shared/template-url/template-url.factory';
|
||||
|
||||
export default {
|
||||
name: 'inventoryJobTemplateAdd',
|
||||
url: '/inventories/:inventory_id/job_templates/add',
|
||||
templateUrl: templateUrl('job-templates/add/job-templates-add'),
|
||||
controller: 'JobTemplatesAdd',
|
||||
resolve: {
|
||||
features: ['FeaturesService', function(FeaturesService) {
|
||||
return FeaturesService.get();
|
||||
}]
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,452 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
export default
|
||||
[ 'Refresh', '$filter', '$scope', '$rootScope', '$compile',
|
||||
'$location', '$log', '$stateParams', 'JobTemplateForm', 'GenerateForm',
|
||||
'Rest', 'Alert', 'ProcessErrors', 'ReturnToCaller', 'ClearScope',
|
||||
'GetBasePath', 'InventoryList', 'CredentialList', 'ProjectList',
|
||||
'LookUpInit', 'md5Setup', 'ParseTypeChange', 'Wait', 'Empty', 'ToJSON',
|
||||
'CallbackHelpInit', 'initSurvey', 'Prompt', 'GetChoices', '$state',
|
||||
'CreateSelect2',
|
||||
function(
|
||||
Refresh, $filter, $scope, $rootScope, $compile,
|
||||
$location, $log, $stateParams, JobTemplateForm, GenerateForm, Rest, Alert,
|
||||
ProcessErrors, ReturnToCaller, ClearScope, GetBasePath, InventoryList,
|
||||
CredentialList, ProjectList, LookUpInit, md5Setup, ParseTypeChange, Wait,
|
||||
Empty, ToJSON, CallbackHelpInit, SurveyControllerInit, Prompt, GetChoices,
|
||||
$state, CreateSelect2
|
||||
) {
|
||||
|
||||
ClearScope();
|
||||
|
||||
// Inject dynamic view
|
||||
var defaultUrl = GetBasePath('job_templates'),
|
||||
form = JobTemplateForm(),
|
||||
generator = GenerateForm,
|
||||
master = {},
|
||||
CloudCredentialList = {},
|
||||
selectPlaybook, checkSCMStatus,
|
||||
callback,
|
||||
base = $location.path().replace(/^\//, '').split('/')[0],
|
||||
context = (base === 'job_templates') ? 'job_template' : 'inv';
|
||||
|
||||
CallbackHelpInit({ scope: $scope });
|
||||
$scope.can_edit = true;
|
||||
generator.inject(form, { mode: 'add', related: false, scope: $scope });
|
||||
|
||||
callback = function() {
|
||||
// Make sure the form controller knows there was a change
|
||||
$scope[form.name + '_form'].$setDirty();
|
||||
};
|
||||
$scope.mode = "add";
|
||||
$scope.parseType = 'yaml';
|
||||
ParseTypeChange({ scope: $scope, field_id: 'job_templates_variables', onChange: callback });
|
||||
|
||||
$scope.playbook_options = [];
|
||||
$scope.allow_callbacks = false;
|
||||
|
||||
generator.reset();
|
||||
|
||||
md5Setup({
|
||||
scope: $scope,
|
||||
master: master,
|
||||
check_field: 'allow_callbacks',
|
||||
default_val: false
|
||||
});
|
||||
|
||||
LookUpInit({
|
||||
scope: $scope,
|
||||
form: form,
|
||||
current_item: ($stateParams.inventory_id !== undefined) ? $stateParams.inventory_id : null,
|
||||
list: InventoryList,
|
||||
field: 'inventory',
|
||||
input_type: "radio"
|
||||
});
|
||||
|
||||
|
||||
// Clone the CredentialList object for use with cloud_credential. Cloning
|
||||
// and changing properties to avoid collision.
|
||||
jQuery.extend(true, CloudCredentialList, CredentialList);
|
||||
CloudCredentialList.name = 'cloudcredentials';
|
||||
CloudCredentialList.iterator = 'cloudcredential';
|
||||
|
||||
SurveyControllerInit({
|
||||
scope: $scope,
|
||||
parent_scope: $scope
|
||||
});
|
||||
|
||||
if ($scope.removeLookUpInitialize) {
|
||||
$scope.removeLookUpInitialize();
|
||||
}
|
||||
$scope.removeLookUpInitialize = $scope.$on('lookUpInitialize', function () {
|
||||
LookUpInit({
|
||||
url: GetBasePath('credentials') + '?cloud=true',
|
||||
scope: $scope,
|
||||
form: form,
|
||||
current_item: null,
|
||||
list: CloudCredentialList,
|
||||
field: 'cloud_credential',
|
||||
hdr: 'Select Cloud Credential',
|
||||
input_type: 'radio'
|
||||
});
|
||||
|
||||
LookUpInit({
|
||||
url: GetBasePath('credentials') + '?kind=ssh',
|
||||
scope: $scope,
|
||||
form: form,
|
||||
current_item: null,
|
||||
list: CredentialList,
|
||||
field: 'credential',
|
||||
hdr: 'Select Machine Credential',
|
||||
input_type: "radio"
|
||||
});
|
||||
});
|
||||
|
||||
var selectCount = 0;
|
||||
|
||||
if ($scope.removeChoicesReady) {
|
||||
$scope.removeChoicesReady();
|
||||
}
|
||||
$scope.removeChoicesReady = $scope.$on('choicesReadyVerbosity', function () {
|
||||
selectCount++;
|
||||
if (selectCount === 2) {
|
||||
var verbosity;
|
||||
// this sets the default options for the selects as specified by the controller.
|
||||
for (verbosity in $scope.verbosity_options) {
|
||||
if ($scope.verbosity_options[verbosity].isDefault) {
|
||||
$scope.verbosity = $scope.verbosity_options[verbosity];
|
||||
}
|
||||
}
|
||||
$scope.job_type = $scope.job_type_options[$scope.job_type_field.default];
|
||||
|
||||
// if you're getting to the form from the scan job section on inventories,
|
||||
// set the job type select to be scan
|
||||
if ($stateParams.inventory_id) {
|
||||
// This means that the job template form was accessed via inventory prop's
|
||||
// This also means the job is a scan job.
|
||||
$scope.job_type.value = 'scan';
|
||||
$scope.jobTypeChange();
|
||||
$scope.inventory = $stateParams.inventory_id;
|
||||
Rest.setUrl(GetBasePath('inventory') + $stateParams.inventory_id + '/');
|
||||
Rest.get()
|
||||
.success(function (data) {
|
||||
$scope.inventory_name = data.name;
|
||||
})
|
||||
.error(function (data, status) {
|
||||
ProcessErrors($scope, data, status, form, { hdr: 'Error!',
|
||||
msg: 'Failed to lookup inventory: ' + data.id + '. GET returned status: ' + status });
|
||||
});
|
||||
}
|
||||
CreateSelect2({
|
||||
element:'#job_templates_job_type',
|
||||
multiple: false
|
||||
});
|
||||
|
||||
CreateSelect2({
|
||||
element:'#playbook-select',
|
||||
multiple: false
|
||||
});
|
||||
|
||||
CreateSelect2({
|
||||
element:'#job_templates_verbosity',
|
||||
multiple: false
|
||||
});
|
||||
|
||||
$scope.$emit('lookUpInitialize');
|
||||
}
|
||||
});
|
||||
|
||||
// setup verbosity options select
|
||||
GetChoices({
|
||||
scope: $scope,
|
||||
url: defaultUrl,
|
||||
field: 'verbosity',
|
||||
variable: 'verbosity_options',
|
||||
callback: 'choicesReadyVerbosity'
|
||||
});
|
||||
|
||||
// setup job type options select
|
||||
GetChoices({
|
||||
scope: $scope,
|
||||
url: defaultUrl,
|
||||
field: 'job_type',
|
||||
variable: 'job_type_options',
|
||||
callback: 'choicesReadyVerbosity'
|
||||
});
|
||||
|
||||
// Update playbook select whenever project value changes
|
||||
selectPlaybook = function (oldValue, newValue) {
|
||||
var url;
|
||||
if($scope.job_type.value === 'scan' && $scope.project_name === "Default"){
|
||||
$scope.playbook_options = ['Default'];
|
||||
$scope.playbook = 'Default';
|
||||
Wait('stop');
|
||||
}
|
||||
else if (oldValue !== newValue) {
|
||||
if ($scope.project) {
|
||||
Wait('start');
|
||||
url = GetBasePath('projects') + $scope.project + '/playbooks/';
|
||||
Rest.setUrl(url);
|
||||
Rest.get()
|
||||
.success(function (data) {
|
||||
var i, opts = [];
|
||||
for (i = 0; i < data.length; i++) {
|
||||
opts.push(data[i]);
|
||||
}
|
||||
$scope.playbook_options = opts;
|
||||
Wait('stop');
|
||||
})
|
||||
.error(function (data, status) {
|
||||
ProcessErrors($scope, data, status, form, { hdr: 'Error!',
|
||||
msg: 'Failed to get playbook list for ' + url + '. GET returned status: ' + status });
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
$scope.jobTypeChange = function(){
|
||||
if($scope.job_type){
|
||||
if($scope.job_type.value === 'scan'){
|
||||
$scope.toggleScanInfo();
|
||||
}
|
||||
else if($scope.project_name === "Default"){
|
||||
$scope.project_name = null;
|
||||
$scope.playbook_options = [];
|
||||
// $scope.playbook = 'null';
|
||||
$scope.job_templates_form.playbook.$setPristine();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
$scope.toggleScanInfo = function() {
|
||||
$scope.project_name = 'Default';
|
||||
if($scope.project === null){
|
||||
selectPlaybook();
|
||||
}
|
||||
else {
|
||||
$scope.project = null;
|
||||
}
|
||||
};
|
||||
|
||||
// Detect and alert user to potential SCM status issues
|
||||
checkSCMStatus = function (oldValue, newValue) {
|
||||
if (oldValue !== newValue && !Empty($scope.project)) {
|
||||
Rest.setUrl(GetBasePath('projects') + $scope.project + '/');
|
||||
Rest.get()
|
||||
.success(function (data) {
|
||||
var msg;
|
||||
switch (data.status) {
|
||||
case 'failed':
|
||||
msg = "The selected project has a <em>failed</em> status. Review the project's SCM settings" +
|
||||
" and run an update before adding it to a template.";
|
||||
break;
|
||||
case 'never updated':
|
||||
msg = 'The selected project has a <em>never updated</em> status. You will need to run a successful' +
|
||||
' update in order to selected a playbook. Without a valid playbook you will not be able ' +
|
||||
' to save this template.';
|
||||
break;
|
||||
case 'missing':
|
||||
msg = 'The selected project has a status of <em>missing</em>. Please check the server and make sure ' +
|
||||
' the directory exists and file permissions are set correctly.';
|
||||
break;
|
||||
}
|
||||
if (msg) {
|
||||
Alert('Warning', msg, 'alert-info', null, null, null, null, true);
|
||||
}
|
||||
})
|
||||
.error(function (data, status) {
|
||||
ProcessErrors($scope, data, status, form, { hdr: 'Error!',
|
||||
msg: 'Failed to get project ' + $scope.project + '. GET returned status: ' + status });
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// $scope.selectPlaybookUnregister = $scope.$watch('project_name', function (newval, oldval) {
|
||||
// selectPlaybook(oldval, newval);
|
||||
// checkSCMStatus(oldval, newval);
|
||||
// });
|
||||
|
||||
// Register a watcher on project_name
|
||||
if ($scope.selectPlaybookUnregister) {
|
||||
$scope.selectPlaybookUnregister();
|
||||
}
|
||||
$scope.selectPlaybookUnregister = $scope.$watch('project', function (newValue, oldValue) {
|
||||
if (newValue !== oldValue) {
|
||||
selectPlaybook(oldValue, newValue);
|
||||
checkSCMStatus();
|
||||
}
|
||||
});
|
||||
|
||||
LookUpInit({
|
||||
scope: $scope,
|
||||
form: form,
|
||||
current_item: null,
|
||||
list: ProjectList,
|
||||
field: 'project',
|
||||
input_type: "radio",
|
||||
autopopulateLookup: (context === 'inv') ? false : true
|
||||
});
|
||||
|
||||
if ($scope.removeSurveySaved) {
|
||||
$scope.rmoveSurveySaved();
|
||||
}
|
||||
$scope.removeSurveySaved = $scope.$on('SurveySaved', function() {
|
||||
Wait('stop');
|
||||
$scope.survey_exists = true;
|
||||
$scope.invalid_survey = false;
|
||||
$('#job_templates_survey_enabled_chbox').attr('checked', true);
|
||||
$('#job_templates_delete_survey_btn').show();
|
||||
$('#job_templates_edit_survey_btn').show();
|
||||
$('#job_templates_create_survey_btn').hide();
|
||||
|
||||
});
|
||||
|
||||
|
||||
function saveCompleted() {
|
||||
setTimeout(function() {
|
||||
$scope.$apply(function() {
|
||||
var base = $location.path().replace(/^\//, '').split('/')[0];
|
||||
if (base === 'job_templates') {
|
||||
ReturnToCaller();
|
||||
}
|
||||
else {
|
||||
ReturnToCaller(1);
|
||||
}
|
||||
});
|
||||
}, 500);
|
||||
}
|
||||
|
||||
if ($scope.removeTemplateSaveSuccess) {
|
||||
$scope.removeTemplateSaveSuccess();
|
||||
}
|
||||
$scope.removeTemplateSaveSuccess = $scope.$on('templateSaveSuccess', function(e, data) {
|
||||
Wait('stop');
|
||||
if (data.related && data.related.callback) {
|
||||
Alert('Callback URL', '<p>Host callbacks are enabled for this template. The callback URL is:</p>'+
|
||||
'<p style="padding: 10px 0;"><strong>' + $scope.callback_server_path + data.related.callback + '</strong></p>'+
|
||||
'<p>The host configuration key is: <strong>' + $filter('sanitize')(data.host_config_key) + '</strong></p>', 'alert-info', saveCompleted, null, null, null, true);
|
||||
}
|
||||
else {
|
||||
saveCompleted();
|
||||
}
|
||||
});
|
||||
|
||||
// Save
|
||||
$scope.formSave = function () {
|
||||
$scope.invalid_survey = false;
|
||||
if ($scope.removeGatherFormFields) {
|
||||
$scope.removeGatherFormFields();
|
||||
}
|
||||
$scope.removeGatherFormFields = $scope.$on('GatherFormFields', function(e, data) {
|
||||
generator.clearApiErrors();
|
||||
Wait('start');
|
||||
data = {};
|
||||
var fld;
|
||||
try {
|
||||
for (fld in form.fields) {
|
||||
if (form.fields[fld].type === 'select' && fld !== 'playbook') {
|
||||
data[fld] = $scope[fld].value;
|
||||
} else {
|
||||
if (fld !== 'variables') {
|
||||
data[fld] = $scope[fld];
|
||||
}
|
||||
}
|
||||
}
|
||||
data.extra_vars = ToJSON($scope.parseType, $scope.variables, true);
|
||||
if(data.job_type === 'scan' && $scope.default_scan === true){
|
||||
data.project = "";
|
||||
data.playbook = "";
|
||||
}
|
||||
Rest.setUrl(defaultUrl);
|
||||
Rest.post(data)
|
||||
.success(function(data) {
|
||||
$scope.$emit('templateSaveSuccess', data);
|
||||
|
||||
$scope.addedItem = data.id;
|
||||
|
||||
Refresh({
|
||||
scope: $scope,
|
||||
set: 'job_templates',
|
||||
iterator: 'job_template',
|
||||
url: $scope.current_url
|
||||
});
|
||||
|
||||
if(data.survey_enabled===true){
|
||||
//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 });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
})
|
||||
.error(function (data, status) {
|
||||
ProcessErrors($scope, data, status, form, { hdr: 'Error!',
|
||||
msg: 'Failed to add new job template. POST returned status: ' + status
|
||||
});
|
||||
});
|
||||
|
||||
} catch (err) {
|
||||
Wait('stop');
|
||||
Alert("Error", "Error parsing extra variables. Parser returned: " + err);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
if ($scope.removePromptForSurvey) {
|
||||
$scope.removePromptForSurvey();
|
||||
}
|
||||
$scope.removePromptForSurvey = $scope.$on('PromptForSurvey', function() {
|
||||
var action = function () {
|
||||
// $scope.$emit("GatherFormFields");
|
||||
Wait('start');
|
||||
$('#prompt-modal').modal('hide');
|
||||
$scope.addSurvey();
|
||||
|
||||
};
|
||||
Prompt({
|
||||
hdr: 'Incomplete Survey',
|
||||
body: '<div class="Prompt-bodyQuery">Do you want to create a survey before proceeding?</div>',
|
||||
action: action
|
||||
});
|
||||
});
|
||||
|
||||
// users can't save a survey with a scan job
|
||||
if($scope.job_type.value === "scan" && $scope.survey_enabled === true){
|
||||
$scope.survey_enabled = false;
|
||||
}
|
||||
if($scope.survey_enabled === true && $scope.survey_exists!==true){
|
||||
// $scope.$emit("PromptForSurvey");
|
||||
|
||||
// The original design for this was a pop up that would prompt the user if they wanted to create a
|
||||
// survey, because they had enabled one but not created it yet. We switched this for now so that
|
||||
// an error message would be displayed by the survey buttons that tells the user to add a survey or disabled
|
||||
// surveys.
|
||||
$scope.invalid_survey = true;
|
||||
return;
|
||||
} else {
|
||||
$scope.$emit("GatherFormFields");
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
$scope.formCancel = function () {
|
||||
$state.transitionTo('jobTemplates');
|
||||
};
|
||||
}
|
||||
|
||||
];
|
||||
@ -0,0 +1,5 @@
|
||||
<div class="tab-pane" id="job_templates_create">
|
||||
<div ui-view></div>
|
||||
<div ng-cloak id="htmlTemplate" class="Panel"></div>
|
||||
<div id="survey-modal-dialog"></div>
|
||||
</div>
|
||||
@ -0,0 +1,23 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import {templateUrl} from '../../shared/template-url/template-url.factory';
|
||||
|
||||
export default {
|
||||
name: 'jobTemplates.add',
|
||||
url: '/add',
|
||||
templateUrl: templateUrl('job-templates/add/job-templates-add'),
|
||||
controller: 'JobTemplatesAdd',
|
||||
ncyBreadcrumb: {
|
||||
parent: "jobTemplates",
|
||||
label: "CREATE JOB TEMPLATE"
|
||||
},
|
||||
resolve: {
|
||||
features: ['FeaturesService', function(FeaturesService) {
|
||||
return FeaturesService.get();
|
||||
}]
|
||||
}
|
||||
};
|
||||
17
awx/ui/client/src/job-templates/add/main.js
Normal file
17
awx/ui/client/src/job-templates/add/main.js
Normal file
@ -0,0 +1,17 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import jobTemplateAddRoute from './job-templates-add.route';
|
||||
import inventoryJobTemplateAddRoute from './inventory-job-templates-add.route';
|
||||
import controller from './job-templates-add.controller';
|
||||
|
||||
export default
|
||||
angular.module('jobTemplatesAdd', [])
|
||||
.controller('JobTemplatesAdd', controller)
|
||||
.run(['$stateExtender', function($stateExtender) {
|
||||
$stateExtender.addState(jobTemplateAddRoute);
|
||||
$stateExtender.addState(inventoryJobTemplateAddRoute);
|
||||
}]);
|
||||
@ -0,0 +1,22 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import {templateUrl} from '../../shared/template-url/template-url.factory';
|
||||
|
||||
export default {
|
||||
name: 'inventoryJobTemplateEdit',
|
||||
url: '/inventories/:inventory_id/job_templates/:template_id',
|
||||
templateUrl: templateUrl('job-templates/edit/job-templates-edit'),
|
||||
controller: 'JobTemplatesEdit',
|
||||
data: {
|
||||
activityStreamId: 'template_id'
|
||||
},
|
||||
resolve: {
|
||||
features: ['FeaturesService', function(FeaturesService) {
|
||||
return FeaturesService.get();
|
||||
}]
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,596 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name controllers.function:JobTemplatesEdit
|
||||
* @description This controller's for Job Template Edit
|
||||
*/
|
||||
|
||||
export default
|
||||
[ '$filter', '$scope', '$rootScope', '$compile',
|
||||
'$location', '$log', '$stateParams', 'JobTemplateForm', 'GenerateForm',
|
||||
'Rest', 'Alert', 'ProcessErrors', 'RelatedSearchInit',
|
||||
'RelatedPaginateInit','ReturnToCaller', 'ClearScope', 'InventoryList',
|
||||
'CredentialList', 'ProjectList', 'LookUpInit', 'GetBasePath', 'md5Setup',
|
||||
'ParseTypeChange', 'JobStatusToolTip', 'FormatDate', 'Wait',
|
||||
'Empty', 'Prompt', 'ParseVariableString', 'ToJSON',
|
||||
'SchedulesControllerInit', 'JobsControllerInit', 'JobsListUpdate',
|
||||
'GetChoices', 'SchedulesListInit', 'SchedulesList', 'CallbackHelpInit',
|
||||
'PlaybookRun' , 'initSurvey', '$state', 'CreateSelect2',
|
||||
function(
|
||||
$filter, $scope, $rootScope, $compile,
|
||||
$location, $log, $stateParams, JobTemplateForm, GenerateForm, Rest, Alert,
|
||||
ProcessErrors, RelatedSearchInit, RelatedPaginateInit, ReturnToCaller,
|
||||
ClearScope, InventoryList, CredentialList, ProjectList, LookUpInit,
|
||||
GetBasePath, md5Setup, ParseTypeChange, JobStatusToolTip, FormatDate, Wait,
|
||||
Empty, Prompt, ParseVariableString, ToJSON, SchedulesControllerInit,
|
||||
JobsControllerInit, JobsListUpdate, GetChoices, SchedulesListInit,
|
||||
SchedulesList, CallbackHelpInit, PlaybookRun, SurveyControllerInit, $state,
|
||||
CreateSelect2
|
||||
) {
|
||||
|
||||
ClearScope();
|
||||
|
||||
var defaultUrl = GetBasePath('job_templates'),
|
||||
generator = GenerateForm,
|
||||
form = JobTemplateForm(),
|
||||
base = $location.path().replace(/^\//, '').split('/')[0],
|
||||
master = {},
|
||||
id = $stateParams.template_id,
|
||||
relatedSets = {},
|
||||
checkSCMStatus, getPlaybooks, callback,
|
||||
choicesCount = 0;
|
||||
|
||||
|
||||
CallbackHelpInit({ scope: $scope });
|
||||
|
||||
SchedulesList.well = false;
|
||||
generator.inject(form, { mode: 'edit', related: true, scope: $scope });
|
||||
$scope.mode = 'edit';
|
||||
$scope.parseType = 'yaml';
|
||||
$scope.showJobType = false;
|
||||
|
||||
SurveyControllerInit({
|
||||
scope: $scope,
|
||||
parent_scope: $scope,
|
||||
id: id
|
||||
});
|
||||
|
||||
callback = function() {
|
||||
// Make sure the form controller knows there was a change
|
||||
$scope[form.name + '_form'].$setDirty();
|
||||
};
|
||||
|
||||
$scope.playbook_options = null;
|
||||
$scope.playbook = null;
|
||||
generator.reset();
|
||||
|
||||
getPlaybooks = function (project) {
|
||||
var url;
|
||||
if($scope.job_type.value === 'scan' && $scope.project_name === "Default"){
|
||||
$scope.playbook_options = ['Default'];
|
||||
$scope.playbook = 'Default';
|
||||
Wait('stop');
|
||||
}
|
||||
else if (!Empty(project)) {
|
||||
url = GetBasePath('projects') + project + '/playbooks/';
|
||||
Wait('start');
|
||||
Rest.setUrl(url);
|
||||
Rest.get()
|
||||
.success(function (data) {
|
||||
var i;
|
||||
$scope.playbook_options = [];
|
||||
for (i = 0; i < data.length; i++) {
|
||||
$scope.playbook_options.push(data[i]);
|
||||
if (data[i] === $scope.playbook) {
|
||||
$scope.job_templates_form.playbook.$setValidity('required', true);
|
||||
}
|
||||
}
|
||||
if ($scope.playbook) {
|
||||
$scope.$emit('jobTemplateLoadFinished');
|
||||
} else {
|
||||
Wait('stop');
|
||||
}
|
||||
})
|
||||
.error(function () {
|
||||
Wait('stop');
|
||||
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');
|
||||
});
|
||||
}
|
||||
else {
|
||||
Wait('stop');
|
||||
}
|
||||
};
|
||||
|
||||
$scope.jobTypeChange = function(){
|
||||
if($scope.job_type){
|
||||
if($scope.job_type.value === 'scan'){
|
||||
$scope.toggleScanInfo();
|
||||
}
|
||||
else if($scope.project_name === "Default"){
|
||||
$scope.project_name = null;
|
||||
$scope.playbook_options = [];
|
||||
// $scope.playbook = 'null';
|
||||
$scope.job_templates_form.playbook.$setPristine();
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
$scope.toggleScanInfo = function() {
|
||||
$scope.project_name = 'Default';
|
||||
if($scope.project === null){
|
||||
getPlaybooks();
|
||||
}
|
||||
else {
|
||||
$scope.project = null;
|
||||
}
|
||||
};
|
||||
|
||||
// Detect and alert user to potential SCM status issues
|
||||
checkSCMStatus = function () {
|
||||
if (!Empty($scope.project)) {
|
||||
Wait('start');
|
||||
Rest.setUrl(GetBasePath('projects') + $scope.project + '/');
|
||||
Rest.get()
|
||||
.success(function (data) {
|
||||
var msg;
|
||||
switch (data.status) {
|
||||
case 'failed':
|
||||
msg = "The selected project has a <em>failed</em> status. Review the project's SCM settings" +
|
||||
" and run an update before adding it to a template.";
|
||||
break;
|
||||
case 'never updated':
|
||||
msg = 'The selected project has a <em>never updated</em> status. You will need to run a successful' +
|
||||
' update in order to selected a playbook. Without a valid playbook you will not be able ' +
|
||||
' to save this template.';
|
||||
break;
|
||||
case 'missing':
|
||||
msg = 'The selected project has a status of <em>missing</em>. Please check the server and make sure ' +
|
||||
' the directory exists and file permissions are set correctly.';
|
||||
break;
|
||||
}
|
||||
Wait('stop');
|
||||
if (msg) {
|
||||
Alert('Warning', msg, 'alert-info', null, null, null, null, true);
|
||||
}
|
||||
})
|
||||
.error(function (data, status) {
|
||||
ProcessErrors($scope, data, status, form, { hdr: 'Error!', msg: 'Failed to get project ' + $scope.project +
|
||||
'. GET returned status: ' + status });
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
if ($scope.removerelatedschedules) {
|
||||
$scope.removerelatedschedules();
|
||||
}
|
||||
$scope.removerelatedschedules = $scope.$on('relatedschedules', function() {
|
||||
SchedulesListInit({
|
||||
scope: $scope,
|
||||
list: SchedulesList,
|
||||
choices: null,
|
||||
related: true
|
||||
});
|
||||
});
|
||||
|
||||
// Register a watcher on project_name. Refresh the playbook list on change.
|
||||
if ($scope.watchProjectUnregister) {
|
||||
$scope.watchProjectUnregister();
|
||||
}
|
||||
$scope.watchProjectUnregister = $scope.$watch('project', function (newValue, oldValue) {
|
||||
if (newValue !== oldValue) {
|
||||
getPlaybooks($scope.project);
|
||||
checkSCMStatus();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
// Turn off 'Wait' after both cloud credential and playbook list come back
|
||||
if ($scope.removeJobTemplateLoadFinished) {
|
||||
$scope.removeJobTemplateLoadFinished();
|
||||
}
|
||||
$scope.removeJobTemplateLoadFinished = $scope.$on('jobTemplateLoadFinished', function () {
|
||||
CreateSelect2({
|
||||
element:'#job_templates_job_type',
|
||||
multiple: false
|
||||
});
|
||||
|
||||
CreateSelect2({
|
||||
element:'#playbook-select',
|
||||
multiple: false
|
||||
});
|
||||
|
||||
CreateSelect2({
|
||||
element:'#job_templates_verbosity',
|
||||
multiple: false
|
||||
});
|
||||
|
||||
for (var set in relatedSets) {
|
||||
$scope.search(relatedSets[set].iterator);
|
||||
}
|
||||
SchedulesControllerInit({
|
||||
scope: $scope,
|
||||
parent_scope: $scope,
|
||||
iterator: 'schedule'
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// Set the status/badge for each related job
|
||||
if ($scope.removeRelatedCompletedJobs) {
|
||||
$scope.removeRelatedCompletedJobs();
|
||||
}
|
||||
$scope.removeRelatedCompletedJobs = $scope.$on('relatedcompleted_jobs', function () {
|
||||
JobsControllerInit({
|
||||
scope: $scope,
|
||||
parent_scope: $scope,
|
||||
iterator: form.related.completed_jobs.iterator
|
||||
});
|
||||
JobsListUpdate({
|
||||
scope: $scope,
|
||||
parent_scope: $scope,
|
||||
list: form.related.completed_jobs
|
||||
});
|
||||
});
|
||||
|
||||
if ($scope.cloudCredentialReadyRemove) {
|
||||
$scope.cloudCredentialReadyRemove();
|
||||
}
|
||||
$scope.cloudCredentialReadyRemove = $scope.$on('cloudCredentialReady', function (e, name) {
|
||||
var CloudCredentialList = {};
|
||||
$scope.cloud_credential_name = name;
|
||||
master.cloud_credential_name = name;
|
||||
// Clone the CredentialList object for use with cloud_credential. Cloning
|
||||
// and changing properties to avoid collision.
|
||||
jQuery.extend(true, CloudCredentialList, CredentialList);
|
||||
CloudCredentialList.name = 'cloudcredentials';
|
||||
CloudCredentialList.iterator = 'cloudcredential';
|
||||
LookUpInit({
|
||||
url: GetBasePath('credentials') + '?cloud=true',
|
||||
scope: $scope,
|
||||
form: form,
|
||||
current_item: $scope.cloud_credential,
|
||||
list: CloudCredentialList,
|
||||
field: 'cloud_credential',
|
||||
hdr: 'Select Cloud Credential',
|
||||
input_type: "radio"
|
||||
});
|
||||
$scope.$emit('jobTemplateLoadFinished');
|
||||
});
|
||||
|
||||
|
||||
// Retrieve each related set and populate the playbook list
|
||||
if ($scope.jobTemplateLoadedRemove) {
|
||||
$scope.jobTemplateLoadedRemove();
|
||||
}
|
||||
$scope.jobTemplateLoadedRemove = $scope.$on('jobTemplateLoaded', function (e, related_cloud_credential, masterObject, relatedSets) {
|
||||
var dft, set;
|
||||
master = masterObject;
|
||||
getPlaybooks($scope.project);
|
||||
|
||||
for (set in relatedSets) {
|
||||
$scope.search(relatedSets[set].iterator);
|
||||
}
|
||||
|
||||
dft = ($scope.host_config_key === "" || $scope.host_config_key === null) ? false : true;
|
||||
md5Setup({
|
||||
scope: $scope,
|
||||
master: master,
|
||||
check_field: 'allow_callbacks',
|
||||
default_val: dft
|
||||
});
|
||||
|
||||
ParseTypeChange({ scope: $scope, field_id: 'job_templates_variables', onChange: callback });
|
||||
|
||||
if (related_cloud_credential) {
|
||||
Rest.setUrl(related_cloud_credential);
|
||||
Rest.get()
|
||||
.success(function (data) {
|
||||
$scope.$emit('cloudCredentialReady', data.name);
|
||||
})
|
||||
.error(function (data, status) {
|
||||
ProcessErrors($scope, data, status, null, {hdr: 'Error!',
|
||||
msg: 'Failed to related cloud credential. GET returned status: ' + status });
|
||||
});
|
||||
} else {
|
||||
// No existing cloud credential
|
||||
$scope.$emit('cloudCredentialReady', null);
|
||||
}
|
||||
});
|
||||
|
||||
Wait('start');
|
||||
|
||||
if ($scope.removeEnableSurvey) {
|
||||
$scope.removeEnableSurvey();
|
||||
}
|
||||
$scope.removeEnableSurvey = $scope.$on('EnableSurvey', function(fld) {
|
||||
|
||||
$('#job_templates_survey_enabled_chbox').attr('checked', $scope[fld]);
|
||||
Rest.setUrl(defaultUrl + id+ '/survey_spec/');
|
||||
Rest.get()
|
||||
.success(function (data) {
|
||||
if(!data || !data.name){
|
||||
$('#job_templates_delete_survey_btn').hide();
|
||||
$('#job_templates_edit_survey_btn').hide();
|
||||
$('#job_templates_create_survey_btn').show();
|
||||
}
|
||||
else {
|
||||
$scope.survey_exists = true;
|
||||
$('#job_templates_delete_survey_btn').show();
|
||||
$('#job_templates_edit_survey_btn').show();
|
||||
$('#job_templates_create_survey_btn').hide();
|
||||
}
|
||||
})
|
||||
.error(function (data, status) {
|
||||
ProcessErrors($scope, data, status, form, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to retrieve job template: ' + $stateParams.template_id + '. GET status: ' + status
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
if ($scope.removeSurveySaved) {
|
||||
$scope.rmoveSurveySaved();
|
||||
}
|
||||
$scope.removeSurveySaved = $scope.$on('SurveySaved', function() {
|
||||
Wait('stop');
|
||||
$scope.survey_exists = true;
|
||||
$scope.invalid_survey = false;
|
||||
$('#job_templates_survey_enabled_chbox').attr('checked', true);
|
||||
$('#job_templates_delete_survey_btn').show();
|
||||
$('#job_templates_edit_survey_btn').show();
|
||||
$('#job_templates_create_survey_btn').hide();
|
||||
|
||||
});
|
||||
|
||||
if ($scope.removeLoadJobs) {
|
||||
$scope.rmoveLoadJobs();
|
||||
}
|
||||
$scope.removeLoadJobs = $scope.$on('LoadJobs', function() {
|
||||
$scope.fillJobTemplate();
|
||||
});
|
||||
|
||||
if ($scope.removeChoicesReady) {
|
||||
$scope.removeChoicesReady();
|
||||
}
|
||||
$scope.removeChoicesReady = $scope.$on('choicesReady', function() {
|
||||
choicesCount++;
|
||||
if (choicesCount === 4) {
|
||||
$scope.$emit('LoadJobs');
|
||||
}
|
||||
});
|
||||
|
||||
GetChoices({
|
||||
scope: $scope,
|
||||
url: GetBasePath('unified_jobs'),
|
||||
field: 'status',
|
||||
variable: 'status_choices',
|
||||
callback: 'choicesReady'
|
||||
});
|
||||
|
||||
GetChoices({
|
||||
scope: $scope,
|
||||
url: GetBasePath('unified_jobs'),
|
||||
field: 'type',
|
||||
variable: 'type_choices',
|
||||
callback: 'choicesReady'
|
||||
});
|
||||
|
||||
// setup verbosity options lookup
|
||||
GetChoices({
|
||||
scope: $scope,
|
||||
url: defaultUrl,
|
||||
field: 'verbosity',
|
||||
variable: 'verbosity_options',
|
||||
callback: 'choicesReady'
|
||||
});
|
||||
|
||||
// setup job type options lookup
|
||||
GetChoices({
|
||||
scope: $scope,
|
||||
url: defaultUrl,
|
||||
field: 'job_type',
|
||||
variable: 'job_type_options',
|
||||
callback: 'choicesReady'
|
||||
});
|
||||
|
||||
function saveCompleted() {
|
||||
setTimeout(function() {
|
||||
$scope.$apply(function() {
|
||||
var base = $location.path().replace(/^\//, '').split('/')[0];
|
||||
if (base === 'job_templates') {
|
||||
ReturnToCaller();
|
||||
}
|
||||
else {
|
||||
ReturnToCaller(1);
|
||||
}
|
||||
});
|
||||
}, 500);
|
||||
}
|
||||
|
||||
if ($scope.removeTemplateSaveSuccess) {
|
||||
$scope.removeTemplateSaveSuccess();
|
||||
}
|
||||
$scope.removeTemplateSaveSuccess = $scope.$on('templateSaveSuccess', function(e, data) {
|
||||
Wait('stop');
|
||||
if ($scope.allow_callbacks && ($scope.host_config_key !== master.host_config_key || $scope.callback_url !== master.callback_url)) {
|
||||
if (data.related && data.related.callback) {
|
||||
Alert('Callback URL', '<p>Host callbacks are enabled for this template. The callback URL is:</p>'+
|
||||
'<p style="padding: 10px 0;"><strong>' + $scope.callback_server_path + data.related.callback + '</strong></p>'+
|
||||
'<p>The host configuration key is: <strong>' + $filter('sanitize')(data.host_config_key) + '</strong></p>', 'alert-info', saveCompleted, null, null, null, true);
|
||||
}
|
||||
else {
|
||||
saveCompleted();
|
||||
}
|
||||
}
|
||||
else {
|
||||
saveCompleted();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
// Save changes to the parent
|
||||
$scope.formSave = function () {
|
||||
$scope.invalid_survey = false;
|
||||
if ($scope.removeGatherFormFields) {
|
||||
$scope.removeGatherFormFields();
|
||||
}
|
||||
$scope.removeGatherFormFields = $scope.$on('GatherFormFields', function(e, data) {
|
||||
generator.clearApiErrors();
|
||||
Wait('start');
|
||||
data = {};
|
||||
var fld;
|
||||
try {
|
||||
// Make sure we have valid variable data
|
||||
data.extra_vars = ToJSON($scope.parseType, $scope.variables, true);
|
||||
if(data.extra_vars === undefined ){
|
||||
throw 'undefined variables';
|
||||
}
|
||||
for (fld in form.fields) {
|
||||
if (form.fields[fld].type === 'select' && fld !== 'playbook') {
|
||||
data[fld] = $scope[fld].value;
|
||||
} else {
|
||||
if (fld !== 'variables' && fld !== 'callback_url') {
|
||||
data[fld] = $scope[fld];
|
||||
}
|
||||
}
|
||||
}
|
||||
Rest.setUrl(defaultUrl + id + '/');
|
||||
Rest.put(data)
|
||||
.success(function (data) {
|
||||
$scope.$emit('templateSaveSuccess', data);
|
||||
})
|
||||
.error(function (data, status) {
|
||||
ProcessErrors($scope, data, status, form, { hdr: 'Error!',
|
||||
msg: 'Failed to update job template. PUT returned status: ' + status });
|
||||
});
|
||||
|
||||
} catch (err) {
|
||||
Wait('stop');
|
||||
Alert("Error", "Error parsing extra variables. Parser returned: " + err);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
if ($scope.removePromptForSurvey) {
|
||||
$scope.removePromptForSurvey();
|
||||
}
|
||||
$scope.removePromptForSurvey = $scope.$on('PromptForSurvey', function() {
|
||||
var action = function () {
|
||||
// $scope.$emit("GatherFormFields");
|
||||
Wait('start');
|
||||
$('#prompt-modal').modal('hide');
|
||||
$scope.addSurvey();
|
||||
|
||||
};
|
||||
Prompt({
|
||||
hdr: 'Incomplete Survey',
|
||||
body: '<div class="Prompt-bodyQuery">Do you want to create a survey before proceeding?</div>',
|
||||
action: action
|
||||
});
|
||||
});
|
||||
|
||||
// users can't save a survey with a scan job
|
||||
if($scope.job_type.value === "scan" && $scope.survey_enabled === true){
|
||||
$scope.survey_enabled = false;
|
||||
}
|
||||
if($scope.survey_enabled === true && $scope.survey_exists!==true){
|
||||
// $scope.$emit("PromptForSurvey");
|
||||
|
||||
// The original design for this was a pop up that would prompt the user if they wanted to create a
|
||||
// survey, because they had enabled one but not created it yet. We switched this for now so that
|
||||
// an error message would be displayed by the survey buttons that tells the user to add a survey or disabled
|
||||
// surveys.
|
||||
$scope.invalid_survey = true;
|
||||
return;
|
||||
} else {
|
||||
$scope.$emit("GatherFormFields");
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
$scope.formCancel = function () {
|
||||
$state.transitionTo('jobTemplates');
|
||||
};
|
||||
|
||||
// Related set: Add button
|
||||
$scope.add = function (set) {
|
||||
$rootScope.flashMessage = null;
|
||||
$location.path('/' + base + '/' + $stateParams.template_id + '/' + set);
|
||||
};
|
||||
|
||||
// Related set: Edit button
|
||||
$scope.edit = function (set, id) {
|
||||
$rootScope.flashMessage = null;
|
||||
$location.path('/' + set + '/' + id);
|
||||
};
|
||||
|
||||
// Launch a job using the selected template
|
||||
$scope.launch = function() {
|
||||
|
||||
if ($scope.removePromptForSurvey) {
|
||||
$scope.removePromptForSurvey();
|
||||
}
|
||||
$scope.removePromptForSurvey = $scope.$on('PromptForSurvey', function() {
|
||||
var action = function () {
|
||||
// $scope.$emit("GatherFormFields");
|
||||
Wait('start');
|
||||
$('#prompt-modal').modal('hide');
|
||||
$scope.addSurvey();
|
||||
|
||||
};
|
||||
Prompt({
|
||||
hdr: 'Incomplete Survey',
|
||||
body: '<div class="Prompt-bodyQuery">Do you want to create a survey before proceeding?</div>',
|
||||
action: action
|
||||
});
|
||||
});
|
||||
if($scope.survey_enabled === true && $scope.survey_exists!==true){
|
||||
$scope.$emit("PromptForSurvey");
|
||||
}
|
||||
else {
|
||||
|
||||
PlaybookRun({
|
||||
scope: $scope,
|
||||
id: id
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// handler for 'Enable Survey' button
|
||||
$scope.surveyEnabled = function(){
|
||||
Rest.setUrl(defaultUrl + id+ '/');
|
||||
Rest.patch({"survey_enabled": $scope.survey_enabled})
|
||||
.success(function (data) {
|
||||
|
||||
if(Empty(data.summary_fields.survey)){
|
||||
$('#job_templates_delete_survey_btn').hide();
|
||||
$('#job_templates_edit_survey_btn').hide();
|
||||
$('#job_templates_create_survey_btn').show();
|
||||
}
|
||||
else{
|
||||
$scope.survey_exists = true;
|
||||
$('#job_templates_delete_survey_btn').show();
|
||||
$('#job_templates_edit_survey_btn').show();
|
||||
$('#job_templates_create_survey_btn').hide();
|
||||
}
|
||||
})
|
||||
.error(function (data, status) {
|
||||
ProcessErrors($scope, data, status, form, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to retrieve save survey_enabled: ' + $stateParams.template_id + '. GET status: ' + status
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
];
|
||||
@ -0,0 +1,5 @@
|
||||
<div class="tab-pane" id="job_templates_edit">
|
||||
<div ui-view></div>
|
||||
<div ng-cloak id="htmlTemplate" class="Panel"></div>
|
||||
<div id="survey-modal-dialog"></div>
|
||||
</div>
|
||||
@ -0,0 +1,22 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import {templateUrl} from '../../shared/template-url/template-url.factory';
|
||||
|
||||
export default {
|
||||
name: 'jobTemplates.edit',
|
||||
url: '/:template_id',
|
||||
templateUrl: templateUrl('job-templates/edit/job-templates-edit'),
|
||||
controller: 'JobTemplatesEdit',
|
||||
data: {
|
||||
activityStreamId: 'template_id'
|
||||
},
|
||||
resolve: {
|
||||
features: ['FeaturesService', function(FeaturesService) {
|
||||
return FeaturesService.get();
|
||||
}]
|
||||
}
|
||||
};
|
||||
17
awx/ui/client/src/job-templates/edit/main.js
Normal file
17
awx/ui/client/src/job-templates/edit/main.js
Normal file
@ -0,0 +1,17 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import jobTemplateEditRoute from './job-templates-edit.route';
|
||||
import inventoryJobTemplateEditRoute from './inventory-job-templates-edit.route';
|
||||
import controller from './job-templates-edit.controller';
|
||||
|
||||
export default
|
||||
angular.module('jobTemplatesEdit', [])
|
||||
.controller('JobTemplatesEdit', controller)
|
||||
.run(['$stateExtender', function($stateExtender) {
|
||||
$stateExtender.addState(jobTemplateEditRoute);
|
||||
$stateExtender.addState(inventoryJobTemplateEditRoute);
|
||||
}]);
|
||||
@ -0,0 +1,241 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
export default
|
||||
[ '$scope', '$rootScope', '$location', '$log',
|
||||
'$stateParams', 'Rest', 'Alert', 'JobTemplateList', 'generateList',
|
||||
'Prompt', 'SearchInit', 'PaginateInit', 'ReturnToCaller', 'ClearScope',
|
||||
'ProcessErrors', 'GetBasePath', 'JobTemplateForm', 'CredentialList',
|
||||
'LookUpInit', 'PlaybookRun', 'Wait', 'CreateDialog' , '$compile',
|
||||
'$state',
|
||||
|
||||
function(
|
||||
$scope, $rootScope, $location, $log,
|
||||
$stateParams, Rest, Alert, JobTemplateList, GenerateList, Prompt,
|
||||
SearchInit, PaginateInit, ReturnToCaller, ClearScope, ProcessErrors,
|
||||
GetBasePath, JobTemplateForm, CredentialList, LookUpInit, PlaybookRun,
|
||||
Wait, CreateDialog, $compile, $state
|
||||
) {
|
||||
|
||||
ClearScope();
|
||||
|
||||
var list = JobTemplateList,
|
||||
defaultUrl = GetBasePath('job_templates'),
|
||||
view = GenerateList,
|
||||
base = $location.path().replace(/^\//, '').split('/')[0],
|
||||
mode = (base === 'job_templates') ? 'edit' : 'select';
|
||||
|
||||
view.inject(list, { mode: mode, scope: $scope });
|
||||
$rootScope.flashMessage = null;
|
||||
|
||||
if ($scope.removePostRefresh) {
|
||||
$scope.removePostRefresh();
|
||||
}
|
||||
$scope.removePostRefresh = $scope.$on('PostRefresh', function () {
|
||||
// Cleanup after a delete
|
||||
Wait('stop');
|
||||
$('#prompt-modal').modal('hide');
|
||||
});
|
||||
|
||||
SearchInit({
|
||||
scope: $scope,
|
||||
set: 'job_templates',
|
||||
list: list,
|
||||
url: defaultUrl
|
||||
});
|
||||
PaginateInit({
|
||||
scope: $scope,
|
||||
list: list,
|
||||
url: defaultUrl
|
||||
});
|
||||
|
||||
// Called from Inventories tab, host failed events link:
|
||||
if ($stateParams.name) {
|
||||
$scope[list.iterator + 'SearchField'] = 'name';
|
||||
$scope[list.iterator + 'SearchValue'] = $stateParams.name;
|
||||
$scope[list.iterator + 'SearchFieldLabel'] = list.fields.name.label;
|
||||
}
|
||||
|
||||
$scope.search(list.iterator);
|
||||
|
||||
$scope.addJobTemplate = function () {
|
||||
$state.transitionTo('jobTemplates.add');
|
||||
};
|
||||
|
||||
$scope.editJobTemplate = function (id) {
|
||||
$state.transitionTo('jobTemplates.edit', {template_id: id});
|
||||
};
|
||||
|
||||
$scope.deleteJobTemplate = function (id, name) {
|
||||
var action = function () {
|
||||
$('#prompt-modal').modal('hide');
|
||||
Wait('start');
|
||||
var url = defaultUrl + id + '/';
|
||||
Rest.setUrl(url);
|
||||
Rest.destroy()
|
||||
.success(function () {
|
||||
$scope.search(list.iterator);
|
||||
})
|
||||
.error(function (data) {
|
||||
Wait('stop');
|
||||
ProcessErrors($scope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'Call to ' + url + ' failed. DELETE returned status: ' + status });
|
||||
});
|
||||
};
|
||||
|
||||
Prompt({
|
||||
hdr: 'Delete',
|
||||
body: '<div class="Prompt-bodyQuery">Are you sure you want to delete the job template below?</div><div class="Prompt-bodyTarget">' + name + '</div>',
|
||||
action: action,
|
||||
actionText: 'DELETE'
|
||||
});
|
||||
};
|
||||
|
||||
$scope.copyJobTemplate = function(id, name){
|
||||
var element,
|
||||
buttons = [{
|
||||
"label": "Cancel",
|
||||
"onClick": function() {
|
||||
$(this).dialog('close');
|
||||
},
|
||||
"icon": "fa-times",
|
||||
"class": "btn btn-default",
|
||||
"id": "copy-close-button"
|
||||
},{
|
||||
"label": "Copy",
|
||||
"onClick": function() {
|
||||
copyAction();
|
||||
// setTimeout(function(){
|
||||
// scope.$apply(function(){
|
||||
// if(mode==='survey-taker'){
|
||||
// scope.$emit('SurveyTakerCompleted');
|
||||
// } else{
|
||||
// scope.saveSurvey();
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
},
|
||||
"icon": "fa-copy",
|
||||
"class": "btn btn-primary",
|
||||
"id": "job-copy-button"
|
||||
}],
|
||||
copyAction = function () {
|
||||
// retrieve the copy of the job template object from the api, then overwrite the name and throw away the id
|
||||
Wait('start');
|
||||
var url = defaultUrl + id + '/';
|
||||
Rest.setUrl(url);
|
||||
Rest.get()
|
||||
.success(function (data) {
|
||||
data.name = $scope.new_copy_name;
|
||||
delete data.id;
|
||||
$scope.$emit('GoToCopy', data);
|
||||
})
|
||||
.error(function (data) {
|
||||
Wait('stop');
|
||||
ProcessErrors($scope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'Call to ' + url + ' failed. DELETE returned status: ' + status });
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
CreateDialog({
|
||||
id: 'copy-job-modal',
|
||||
title: "Copy",
|
||||
scope: $scope,
|
||||
buttons: buttons,
|
||||
width: 500,
|
||||
height: 300,
|
||||
minWidth: 200,
|
||||
callback: 'CopyDialogReady'
|
||||
});
|
||||
|
||||
$('#job_name').text(name);
|
||||
$('#copy-job-modal').show();
|
||||
|
||||
|
||||
if ($scope.removeCopyDialogReady) {
|
||||
$scope.removeCopyDialogReady();
|
||||
}
|
||||
$scope.removeCopyDialogReady = $scope.$on('CopyDialogReady', function() {
|
||||
//clear any old remaining text
|
||||
$scope.new_copy_name = "" ;
|
||||
$scope.copy_form.$setPristine();
|
||||
$('#copy-job-modal').dialog('open');
|
||||
$('#job-copy-button').attr('ng-disabled', "!copy_form.$valid");
|
||||
element = angular.element(document.getElementById('job-copy-button'));
|
||||
$compile(element)($scope);
|
||||
|
||||
});
|
||||
|
||||
if ($scope.removeGoToCopy) {
|
||||
$scope.removeGoToCopy();
|
||||
}
|
||||
$scope.removeGoToCopy = $scope.$on('GoToCopy', function(e, data) {
|
||||
var url = defaultUrl,
|
||||
old_survey_url = (data.related.survey_spec) ? data.related.survey_spec : "" ;
|
||||
Rest.setUrl(url);
|
||||
Rest.post(data)
|
||||
.success(function (data) {
|
||||
if(data.survey_enabled===true){
|
||||
$scope.$emit("CopySurvey", data, old_survey_url);
|
||||
}
|
||||
else {
|
||||
$('#copy-job-modal').dialog('close');
|
||||
Wait('stop');
|
||||
$location.path($location.path() + '/' + data.id);
|
||||
}
|
||||
|
||||
})
|
||||
.error(function (data) {
|
||||
Wait('stop');
|
||||
ProcessErrors($scope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'Call to ' + url + ' failed. DELETE returned status: ' + status });
|
||||
});
|
||||
});
|
||||
|
||||
if ($scope.removeCopySurvey) {
|
||||
$scope.removeCopySurvey();
|
||||
}
|
||||
$scope.removeCopySurvey = $scope.$on('CopySurvey', function(e, new_data, old_url) {
|
||||
// var url = data.related.survey_spec;
|
||||
Rest.setUrl(old_url);
|
||||
Rest.get()
|
||||
.success(function (survey_data) {
|
||||
|
||||
Rest.setUrl(new_data.related.survey_spec);
|
||||
Rest.post(survey_data)
|
||||
.success(function () {
|
||||
$('#copy-job-modal').dialog('close');
|
||||
Wait('stop');
|
||||
$location.path($location.path() + '/' + new_data.id);
|
||||
})
|
||||
.error(function (data) {
|
||||
Wait('stop');
|
||||
ProcessErrors($scope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'Call to ' + new_data.related.survey_spec + ' failed. DELETE returned status: ' + status });
|
||||
});
|
||||
|
||||
|
||||
})
|
||||
.error(function (data) {
|
||||
Wait('stop');
|
||||
ProcessErrors($scope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'Call to ' + old_url + ' failed. DELETE returned status: ' + status });
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
$scope.submitJob = function (id) {
|
||||
PlaybookRun({ scope: $scope, id: id });
|
||||
};
|
||||
|
||||
$scope.scheduleJob = function (id) {
|
||||
$state.go('jobTemplateSchedules', {id: id});
|
||||
};
|
||||
}
|
||||
];
|
||||
@ -3,7 +3,6 @@
|
||||
<div ng-cloak id="htmlTemplate" class="Panel"></div>
|
||||
<div ng-include="'/static/partials/schedule_dialog.html'"></div>
|
||||
<div ng-include="'/static/partials/logviewer.html'"></div>
|
||||
<div id="survey-modal-dialog"></div>
|
||||
|
||||
<div id="copy-job-modal" style="display:none">
|
||||
<form name="copy_form" id="copy_form">
|
||||
@ -0,0 +1,26 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import {templateUrl} from '../../shared/template-url/template-url.factory';
|
||||
|
||||
export default {
|
||||
name: 'jobTemplates',
|
||||
url: '/job_templates',
|
||||
templateUrl: templateUrl('job-templates/list/job-templates-list'),
|
||||
controller: 'JobTemplatesList',
|
||||
data: {
|
||||
activityStream: true,
|
||||
activityStreamTarget: 'job_template'
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
label: "JOB TEMPLATES"
|
||||
},
|
||||
resolve: {
|
||||
features: ['FeaturesService', function(FeaturesService) {
|
||||
return FeaturesService.get();
|
||||
}]
|
||||
}
|
||||
};
|
||||
15
awx/ui/client/src/job-templates/list/main.js
Normal file
15
awx/ui/client/src/job-templates/list/main.js
Normal file
@ -0,0 +1,15 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import route from './job-templates-list.route';
|
||||
import controller from './job-templates-list.controller';
|
||||
|
||||
export default
|
||||
angular.module('jobTemplatesList', [])
|
||||
.controller('JobTemplatesList', controller)
|
||||
.run(['$stateExtender', function($stateExtender) {
|
||||
$stateExtender.addState(route);
|
||||
}]);
|
||||
@ -5,10 +5,12 @@
|
||||
*************************************************/
|
||||
|
||||
import deleteJobTemplate from './delete-job-template.service';
|
||||
|
||||
import surveyMaker from './survey-maker/main';
|
||||
import jobTemplatesList from './list/main';
|
||||
import jobTemplatesAdd from './add/main';
|
||||
import jobTemplatesEdit from './edit/main';
|
||||
|
||||
export default
|
||||
angular.module('jobTemplates',
|
||||
[ surveyMaker.name
|
||||
])
|
||||
angular.module('jobTemplates', [surveyMaker.name, jobTemplatesList.name, jobTemplatesAdd.name, jobTemplatesEdit.name])
|
||||
.service('deleteJobTemplate', deleteJobTemplate);
|
||||
|
||||
@ -10,30 +10,30 @@ export default
|
||||
element,
|
||||
target = (mode==='survey-taker') ? 'password-modal' : "survey-modal-dialog",
|
||||
buttons = [{
|
||||
"label": "Cancel",
|
||||
"onClick": function() {
|
||||
scope.cancelSurvey(this);
|
||||
},
|
||||
"icon": "fa-times",
|
||||
"class": "btn btn-default",
|
||||
"id": "survey-close-button"
|
||||
},{
|
||||
"label": (mode==='survey-taker') ? "Launch" : "Save" ,
|
||||
"onClick": function() {
|
||||
setTimeout(function(){
|
||||
scope.$apply(function(){
|
||||
if(mode==='survey-taker'){
|
||||
scope.$emit('SurveyTakerCompleted');
|
||||
} else{
|
||||
scope.saveSurvey();
|
||||
}
|
||||
"label": "Cancel",
|
||||
"onClick": function() {
|
||||
scope.cancelSurvey(this);
|
||||
},
|
||||
"icon": "fa-times",
|
||||
"class": "btn btn-default",
|
||||
"id": "survey-close-button"
|
||||
},{
|
||||
"label": (mode==='survey-taker') ? "Launch" : "Save" ,
|
||||
"onClick": function() {
|
||||
setTimeout(function(){
|
||||
scope.$apply(function(){
|
||||
if(mode==='survey-taker'){
|
||||
scope.$emit('SurveyTakerCompleted');
|
||||
} else{
|
||||
scope.saveSurvey();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
"icon": (mode==='survey-taker') ? "fa-rocket" : "fa-check",
|
||||
"class": "btn btn-primary",
|
||||
"id": "survey-save-button"
|
||||
}];
|
||||
},
|
||||
"icon": (mode==='survey-taker') ? "fa-rocket" : "fa-check",
|
||||
"class": "btn btn-primary",
|
||||
"id": "survey-save-button"
|
||||
}];
|
||||
|
||||
CreateDialog({
|
||||
id: target,
|
||||
@ -84,4 +84,3 @@ ShowFactory.$inject =
|
||||
'Empty',
|
||||
'$compile'
|
||||
];
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user