diff --git a/awx/ui/static/js/app.js b/awx/ui/static/js/app.js index fc0964a028..9256860cb2 100644 --- a/awx/ui/static/js/app.js +++ b/awx/ui/static/js/app.js @@ -267,6 +267,11 @@ var tower = angular.module('Tower', [ controller: JobTemplatesAdd }). + when('/inventories/:inventory_id/job_templates/:template_id', { + templateUrl: urlPrefix + 'partials/job_templates.html', + controller: JobTemplatesEdit + }). + when('/inventories/:inventory_id/manage', { templateUrl: urlPrefix + 'partials/inventory-manage.html', controller: InventoriesManage diff --git a/awx/ui/static/js/controllers/Inventories.js b/awx/ui/static/js/controllers/Inventories.js index 6cf2d5474a..67cd2cc271 100644 --- a/awx/ui/static/js/controllers/Inventories.js +++ b/awx/ui/static/js/controllers/Inventories.js @@ -482,7 +482,7 @@ InventoriesAdd.$inject = ['$scope', '$rootScope', '$compile', '$location', '$log export function InventoriesEdit($scope, $rootScope, $compile, $location, $log, $routeParams, InventoryForm, GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, ReturnToCaller, ClearScope, generateList, OrganizationList, SearchInit, PaginateInit, - LookUpInit, GetBasePath, ParseTypeChange, Wait, ToJSON, ParseVariableString, Stream) { + LookUpInit, GetBasePath, ParseTypeChange, Wait, ToJSON, ParseVariableString, Stream, RelatedSearchInit, RelatedPaginateInit) { ClearScope(); @@ -492,17 +492,30 @@ export function InventoriesEdit($scope, $rootScope, $compile, $location, $log, $ generator = GenerateForm, inventory_id = $routeParams.inventory_id, master = {}, - fld, json_data, data; + fld, json_data, data, + relatedSets = {}; form.well = true; form.formLabelSize = null; form.formFieldSize = null; - + $scope.inventory_id = inventory_id; generator.inject(form, { mode: 'edit', related: true, scope: $scope }); generator.reset(); LoadBreadCrumbs(); + // After the project is loaded, retrieve each related set + if ($scope.inventoryLoadedRemove) { + $scope.inventoryLoadedRemove(); + } + $scope.projectLoadedRemove = $scope.$on('inventoryLoaded', function () { + var set, opts=[]; + + for (set in relatedSets) { + $scope.search(relatedSets[set].iterator); + } + }); + Wait('start'); Rest.setUrl(GetBasePath('inventory') + inventory_id + '/'); Rest.get() @@ -530,6 +543,19 @@ export function InventoriesEdit($scope, $rootScope, $compile, $location, $log, $ data.summary_fields[form.fields[fld].sourceModel][form.fields[fld].sourceField]; } } + relatedSets = form.relatedSets(data.related); + + // Initialize related search functions. Doing it here to make sure relatedSets object is populated. + RelatedSearchInit({ + scope: $scope, + form: form, + relatedSets: relatedSets + }); + RelatedPaginateInit({ + scope: $scope, + relatedSets: relatedSets + }); + Wait('stop'); $scope.parseType = 'yaml'; ParseTypeChange({ @@ -546,6 +572,7 @@ export function InventoriesEdit($scope, $rootScope, $compile, $location, $log, $ field: 'organization', input_type: 'radio' }); + $scope.$emit('inventoryLoaded'); }) .error(function (data, status) { ProcessErrors($scope, data, status, null, { hdr: 'Error!', @@ -627,11 +654,19 @@ export function InventoriesEdit($scope, $rootScope, $compile, $location, $log, $ $scope.addScanJob = function(){ $location.path($location.path()+'/job_templates/add'); }; + + $scope.editScanJob = function(){ + $location.path($location.path()+'/job_templates/'+this.scan_job_template.id); + }; + + $scope.deleteScanJob = function(){ + $location.path($location.path()+'/job_templates/add'); + }; } InventoriesEdit.$inject = ['$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'InventoryForm', 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'ReturnToCaller', 'ClearScope', 'generateList', 'OrganizationList', 'SearchInit', - 'PaginateInit', 'LookUpInit', 'GetBasePath', 'ParseTypeChange', 'Wait', 'ToJSON', 'ParseVariableString', 'Stream' + 'PaginateInit', 'LookUpInit', 'GetBasePath', 'ParseTypeChange', 'Wait', 'ToJSON', 'ParseVariableString', 'Stream', 'RelatedSearchInit', 'RelatedPaginateInit' ]; diff --git a/awx/ui/static/js/controllers/JobTemplates.js b/awx/ui/static/js/controllers/JobTemplates.js index 990bb31e02..b2a0f26331 100644 --- a/awx/ui/static/js/controllers/JobTemplates.js +++ b/awx/ui/static/js/controllers/JobTemplates.js @@ -302,12 +302,13 @@ export function JobTemplatesAdd($scope, $rootScope, $compile, $location, $log, $ LookUpInit({ scope: $scope, form: form, - current_item: null, + current_item: ($routeParams.inventory_id !== undefined) ? $routeParams.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); @@ -341,14 +342,62 @@ export function JobTemplatesAdd($scope, $rootScope, $compile, $location, $log, $ parent_scope: $scope }); - // $scope.jobTypeChange = function(){ - // - // }; + + $scope.jobTypeChange = function(){ + if($scope.job_type){ + if($scope.job_type.value === 'scan'){ + $scope.default_scan = true; + $scope.project_name = 'Default'; + $scope.project = null; + } + else{ + $scope.default_scan = false; + $scope.project_name = null; + $scope.project = null; + $scope.playbook_options = []; + $scope.playbook = 'null'; + } + } + }; + + $scope.toggleScanInfo = function() { + if($scope.default_scan){ + $scope.project_name = 'Default'; + $scope.project = null; + } + if(!$scope.default_scan){ + $scope.project_name = null; + $scope.playbook_options = []; + $scope.playbook = 'null'; + } + }; + + if ($routeParams.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 = $routeParams.inventory_id; + Rest.setUrl(GetBasePath('inventory') + $routeParams.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 }); + }); + } // Update playbook select whenever project value changes selectPlaybook = function (oldValue, newValue) { var url; - if (oldValue !== newValue) { + if($scope.job_type.value === 'scan' && $scope.default_scan === true){ + $scope.playbook_options = ['Default']; + $scope.playbook = 'Default'; + Wait('stop'); + } + else if (oldValue !== newValue) { if ($scope.project) { Wait('start'); url = GetBasePath('projects') + $scope.project + '/playbooks/'; @@ -477,7 +526,10 @@ export function JobTemplatesAdd($scope, $rootScope, $compile, $location, $log, $ } } 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) { @@ -621,9 +673,42 @@ export function JobTemplatesEdit($scope, $rootScope, $compile, $location, $log, $scope.playbook = null; generator.reset(); + $scope.jobTypeChange = function(){ + if($scope.job_type){ + if($scope.job_type.value === 'scan'){ + $scope.default_scan = true; + $scope.project_name = 'Default'; + $scope.project = null; + } + else{ + $scope.default_scan = false; + $scope.project_name = null; + $scope.playbook_options = []; + $scope.playbook = 'null'; + } + } + }; + + $scope.toggleScanInfo = function() { + if($scope.default_scan){ + $scope.project_name = 'Default'; + $scope.project = null; + } + if(!$scope.default_scan){ + $scope.project_name = null; + $scope.playbook_options = []; + $scope.playbook = 'null'; + } + }; + getPlaybooks = function (project) { var url; - if (!Empty(project)) { + if($scope.job_type.value === 'scan' && $scope.default_scan === true){ + $scope.playbook_options = ['Default']; + $scope.playbook = 'Default'; + Wait('stop'); + } + else if (!Empty(project)) { url = GetBasePath('projects') + project + '/playbooks/'; Wait('start'); Rest.setUrl(url); diff --git a/awx/ui/static/js/forms/Inventories.js b/awx/ui/static/js/forms/Inventories.js index 5d0bd79d60..a67919453d 100644 --- a/awx/ui/static/js/forms/Inventories.js +++ b/awx/ui/static/js/forms/Inventories.js @@ -97,10 +97,10 @@ export default }, related: { - scan_jobs: { + scan_job_templates: { type: 'collection', title: 'Scan Jobs', - iterator: 'scan_job', + iterator: 'scan_job_template', index: false, open: false, @@ -109,14 +109,15 @@ export default ngClick: "addScanJob(inventory_id)", icon: 'icon-plus', label: 'Add', - awToolTip: 'Add a scan job' + awToolTip: 'Add a scan job template' } }, fields: { name: { key: true, - label: 'Name' + label: 'Name', + linkTo: '/#/inventories/{{inventory_id}}/job_templates/{{scan_job_template.id}}' }, description: { label: 'Description' @@ -126,17 +127,17 @@ export default fieldActions: { edit: { label: 'Edit', - ngClick: "edit('organizations', organization.id, organization.name)", + ngClick: "editScanJob(inventory_id)", icon: 'icon-edit', - awToolTip: 'Edit the organization', + awToolTip: 'Edit the scan job template', 'class': 'btn btn-default' }, "delete": { label: 'Delete', - ngClick: "delete('organizations', organization.id, organization.name, 'organizations')", + ngClick: "deleteScanJob(inventory_id)", icon: 'icon-trash', "class": 'btn-danger', - awToolTip: 'Delete the organization' + awToolTip: 'Delete the scan job template' } } } @@ -144,14 +145,10 @@ export default relatedSets: function(urls) { return { - scan_jobs: { - iterator: 'scan_job', - url: urls.organizations - }, - // schedules: { - // iterator: 'schedule', - // url: urls.schedules - // } + scan_job_templates: { + iterator: 'scan_job_template', + url: urls.scan_job_templates + } }; } diff --git a/awx/ui/static/js/forms/JobTemplates.js b/awx/ui/static/js/forms/JobTemplates.js index 404e5f2729..96e6db7d4f 100644 --- a/awx/ui/static/js/forms/JobTemplates.js +++ b/awx/ui/static/js/forms/JobTemplates.js @@ -102,7 +102,9 @@ export default awPopOver: "
Select the project containing the playbook you want this job to execute.
", dataTitle: 'Project', dataPlacement: 'right', - dataContainer: "body" + dataContainer: "body", + ngDisabled: 'default_scan === true', + ngRequired: 'default_scan === false' }, playbook: { label: 'Playbook', @@ -114,7 +116,20 @@ export default awPopOver: "Select the playbook to be executed by this job.
", dataTitle: 'Playbook', dataPlacement: 'right', - dataContainer: "body" + dataContainer: "body", + ngDisabled: 'default_scan === true', + ngRequired: 'default_scan === false' + }, + default_scan: { + label: "Use default scan job project and playbook", + type: 'checkbox', + ngChange: 'toggleScanInfo()', + ngShow: 'job_type.value === "scan"', + column: 1, + awPopOver: "Scan jobs templates use a default project and default playbook. Uncheck this box to override these defaults.
", + dataTitle: 'Scan jobs', + dataPlacement: 'right', + dataContainer: "body" }, credential: { label: 'Machine Credential', diff --git a/awx/ui/static/js/helpers/JobTemplates.js b/awx/ui/static/js/helpers/JobTemplates.js index 5e55668282..7ab79fc385 100644 --- a/awx/ui/static/js/helpers/JobTemplates.js +++ b/awx/ui/static/js/helpers/JobTemplates.js @@ -171,6 +171,12 @@ angular.module('JobTemplatesHelper', ['Utilities']) input_type: "radio" }); + + if(scope.project === "" && scope.playbook === ""){ + scope.default_scan = true; + scope.toggleScanInfo(); + } + RelatedSearchInit({ scope: scope, form: form, @@ -193,7 +199,4 @@ angular.module('JobTemplatesHelper', ['Utilities']) }; }; - - - }]); diff --git a/awx/ui/static/js/lists/ScanJobs.js b/awx/ui/static/js/lists/ScanJobs.js index c13b864f4f..33b1d6b0f8 100644 --- a/awx/ui/static/js/lists/ScanJobs.js +++ b/awx/ui/static/js/lists/ScanJobs.js @@ -15,8 +15,8 @@ export default angular.module('ScanJobsListDefinition', []) .value( 'ScanJobsList', { - name: 'scan_jobs', - iterator: 'scan_job', + name: 'scan_job_templates', + iterator: 'scan_job_template', editTitle: 'Scan Jobs', 'class': 'table-condensed', index: false, diff --git a/awx/ui/static/js/shared/form-generator.js b/awx/ui/static/js/shared/form-generator.js index 9affa6dcd8..516b478081 100644 --- a/awx/ui/static/js/shared/form-generator.js +++ b/awx/ui/static/js/shared/form-generator.js @@ -1107,6 +1107,8 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat html += "\" "; html += this.attr(field, 'ngOptions'); html += (field.ngChange) ? this.attr(field, 'ngChange') : ""; + html += (field.ngDisabled) ? this.attr(field, 'ngDisabled'): ""; + html += (field.ngRequired) ? this.attr(field, 'ngRequired') : ""; html += buildId(field, fld, this.form); html += (options.mode === 'edit' && field.editRequired) ? "required " : ""; html += (options.mode === 'add' && field.addRequired) ? "required " : ""; @@ -1331,6 +1333,7 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat html += "\n"; html += "\n"; html += "\n"; html += "