diff --git a/awx/ui/client/features/credentials/credentials.strings.js b/awx/ui/client/features/credentials/credentials.strings.js
index 958282aa09..64af74726e 100644
--- a/awx/ui/client/features/credentials/credentials.strings.js
+++ b/awx/ui/client/features/credentials/credentials.strings.js
@@ -27,6 +27,11 @@ function CredentialsStrings (BaseString) {
ns.permissions = {
TITLE: t.s('CREDENTIALS PERMISSIONS')
};
+
+ ns.deleteCredential = {
+ CONFIRM: t.s('Are you sure you want to delete this credential?'),
+ INVALIDATE: t.s('Doing so will invalidate the following:')
+ };
}
CredentialsStrings.$inject = ['BaseStringService'];
diff --git a/awx/ui/client/index.template.ejs b/awx/ui/client/index.template.ejs
index f222a054d1..5b082eb6e7 100644
--- a/awx/ui/client/index.template.ejs
+++ b/awx/ui/client/index.template.ejs
@@ -42,7 +42,7 @@
diff --git a/awx/ui/client/lib/models/Base.js b/awx/ui/client/lib/models/Base.js
index 72434e7d8d..854fb72de0 100644
--- a/awx/ui/client/lib/models/Base.js
+++ b/awx/ui/client/lib/models/Base.js
@@ -77,12 +77,16 @@ function search (params, config) {
});
}
-function httpGet (resource) {
+function httpGet (resource, config) {
const req = {
method: 'GET',
url: this.path
};
+ if (config && config.params) {
+ req.params = config.params;
+ }
+
if (typeof resource === 'object') {
this.model.GET = resource;
@@ -143,6 +147,19 @@ function httpOptions (resource) {
});
}
+function httpDelete (resource) {
+ const req = {
+ method: 'DELETE',
+ url: this.path
+ };
+
+ if (resource) {
+ req.url = `${this.path}${resource}/`;
+ }
+
+ return $http(req).then(res => res);
+}
+
function options (keys) {
return this.find('options', keys);
}
@@ -349,6 +366,32 @@ function graft (id) {
return new this.Constructor('get', item, true);
}
+function getDependentResourceCounts (id) {
+ if (this.setDependentResources) {
+ this.setDependentResources(id);
+ } else {
+ return Promise.resolve([]);
+ }
+
+ const dependentResourcePromises = [];
+
+ this.dependentResources.forEach(dependentResource => {
+ const config = {};
+
+ if (dependentResource.params) {
+ config.params = dependentResource.params;
+ }
+
+ dependentResourcePromises.push(dependentResource.model.http.get(undefined, config)
+ .then((val) => ({
+ label: dependentResource.model.label,
+ count: val.data.count
+ })));
+ });
+
+ return Promise.all(dependentResourcePromises);
+}
+
/**
* `create` is called on instantiation of every model. Models can be
* instantiated empty or with `GET` and/or `OPTIONS` requests that yield data.
@@ -407,12 +450,14 @@ function BaseModel (path, settings) {
this.set = set;
this.unset = unset;
this.extend = extend;
+ this.getDependentResourceCounts = getDependentResourceCounts;
this.http = {
get: httpGet.bind(this),
options: httpOptions.bind(this),
post: httpPost.bind(this),
put: httpPut.bind(this),
+ delete: httpDelete.bind(this)
};
this.model = {};
diff --git a/awx/ui/client/lib/models/Credential.js b/awx/ui/client/lib/models/Credential.js
index 3b0aafcf3d..0bfac7be07 100644
--- a/awx/ui/client/lib/models/Credential.js
+++ b/awx/ui/client/lib/models/Credential.js
@@ -1,6 +1,11 @@
const ENCRYPTED_VALUE = '$encrypted$';
let BaseModel;
+let ProjectModel;
+let JobTemplateModel;
+let InventoryModel;
+let InventorySourceModel;
+let ModelsStrings;
function createFormSchema (method, config) {
if (!config) {
@@ -40,22 +45,73 @@ function assignInputGroupValues (inputs) {
});
}
+function setDependentResources (id) {
+ this.dependentResources = [
+ {
+ model: new ProjectModel(),
+ params: {
+ credential: id
+ }
+ },
+ {
+ model: new JobTemplateModel(),
+ params: {
+ credential: id,
+ ask_credential_on_launch: false
+ }
+ },
+ {
+ model: new InventoryModel(),
+ params: {
+ insights_credential: id
+ }
+ },
+ {
+ model: new InventorySourceModel(),
+ params: {
+ credential: id
+ }
+ }
+ ];
+}
+
function CredentialModel (method, resource, graft) {
BaseModel.call(this, 'credentials');
this.Constructor = CredentialModel;
this.createFormSchema = createFormSchema.bind(this);
this.assignInputGroupValues = assignInputGroupValues.bind(this);
+ this.setDependentResources = setDependentResources.bind(this);
+ this.label = ModelsStrings.get('labels.CREDENTIAL');
return this.create(method, resource, graft);
}
-function CredentialModelLoader (_BaseModel_) {
+function CredentialModelLoader (
+ _BaseModel_,
+ _ProjectModel_,
+ _JobTemplateModel_,
+ _InventoryModel_,
+ _InventorySourceModel_,
+ _ModelsStrings_
+) {
BaseModel = _BaseModel_;
+ ProjectModel = _ProjectModel_;
+ JobTemplateModel = _JobTemplateModel_;
+ InventoryModel = _InventoryModel_;
+ InventorySourceModel = _InventorySourceModel_;
+ ModelsStrings = _ModelsStrings_;
return CredentialModel;
}
-CredentialModelLoader.$inject = ['BaseModel'];
+CredentialModelLoader.$inject = [
+ 'BaseModel',
+ 'ProjectModel',
+ 'JobTemplateModel',
+ 'InventoryModel',
+ 'InventorySourceModel',
+ 'ModelsStrings'
+];
export default CredentialModelLoader;
diff --git a/awx/ui/client/lib/models/CredentialType.js b/awx/ui/client/lib/models/CredentialType.js
index 334ba234ce..936a1d8bdd 100644
--- a/awx/ui/client/lib/models/CredentialType.js
+++ b/awx/ui/client/lib/models/CredentialType.js
@@ -1,4 +1,6 @@
let BaseModel;
+let CredentialModel;
+let ModelsStrings;
function categorizeByKind () {
const group = {};
@@ -30,22 +32,45 @@ function mergeInputProperties () {
});
}
+function setDependentResources (id) {
+ this.dependentResources = [
+ {
+ model: new CredentialModel(),
+ params: {
+ credential_type: id
+ }
+ }
+ ];
+}
+
function CredentialTypeModel (method, resource, graft) {
BaseModel.call(this, 'credential_types');
this.Constructor = CredentialTypeModel;
this.categorizeByKind = categorizeByKind.bind(this);
this.mergeInputProperties = mergeInputProperties.bind(this);
+ this.setDependentResources = setDependentResources.bind(this);
+ this.label = ModelsStrings.get('labels.CREDENTIAL_TYPE');
return this.create(method, resource, graft);
}
-function CredentialTypeModelLoader (_BaseModel_) {
+function CredentialTypeModelLoader (
+ _BaseModel_,
+ _CredentialModel_,
+ _ModelsStrings_
+) {
BaseModel = _BaseModel_;
+ CredentialModel = _CredentialModel_;
+ ModelsStrings = _ModelsStrings_;
return CredentialTypeModel;
}
-CredentialTypeModelLoader.$inject = ['BaseModel'];
+CredentialTypeModelLoader.$inject = [
+ 'BaseModel',
+ 'CredentialModel',
+ 'ModelsStrings'
+];
export default CredentialTypeModelLoader;
diff --git a/awx/ui/client/lib/models/Inventory.js b/awx/ui/client/lib/models/Inventory.js
new file mode 100644
index 0000000000..b1d5af3f54
--- /dev/null
+++ b/awx/ui/client/lib/models/Inventory.js
@@ -0,0 +1,44 @@
+let BaseModel;
+let JobTemplateModel;
+let ModelsStrings;
+
+function setDependentResources (id) {
+ this.dependentResources = [
+ {
+ model: new JobTemplateModel(),
+ params: {
+ inventory: id
+ }
+ }
+ ];
+}
+
+function InventoryModel (method, resource, graft) {
+ BaseModel.call(this, 'inventories');
+
+ this.Constructor = InventoryModel;
+ this.setDependentResources = setDependentResources.bind(this);
+ this.label = ModelsStrings.get('labels.INVENTORY');
+
+ return this.create(method, resource, graft);
+}
+
+function InventoryModelLoader (
+ _BaseModel_,
+ _JobTemplateModel_,
+ _ModelsStrings_
+) {
+ BaseModel = _BaseModel_;
+ JobTemplateModel = _JobTemplateModel_;
+ ModelsStrings = _ModelsStrings_;
+
+ return InventoryModel;
+}
+
+InventoryModelLoader.$inject = [
+ 'BaseModel',
+ 'JobTemplateModel',
+ 'ModelsStrings'
+];
+
+export default InventoryModelLoader;
diff --git a/awx/ui/client/lib/models/InventoryScript.js b/awx/ui/client/lib/models/InventoryScript.js
new file mode 100644
index 0000000000..aa37d09efd
--- /dev/null
+++ b/awx/ui/client/lib/models/InventoryScript.js
@@ -0,0 +1,44 @@
+let BaseModel;
+let InventorySourceModel;
+let ModelsStrings;
+
+function setDependentResources (id) {
+ this.dependentResources = [
+ {
+ model: new InventorySourceModel(),
+ params: {
+ source_script: id
+ }
+ }
+ ];
+}
+
+function InventoryScriptModel (method, resource, graft) {
+ BaseModel.call(this, 'inventory_scripts');
+
+ this.Constructor = InventoryScriptModel;
+ this.setDependentResources = setDependentResources.bind(this);
+ this.label = ModelsStrings.get('labels.INVENTORY_SCRIPT');
+
+ return this.create(method, resource, graft);
+}
+
+function InventoryScriptModelLoader (
+ _BaseModel_,
+ _InventorySourceModel_,
+ _ModelsStrings_
+) {
+ BaseModel = _BaseModel_;
+ InventorySourceModel = _InventorySourceModel_;
+ ModelsStrings = _ModelsStrings_;
+
+ return InventoryScriptModel;
+}
+
+InventoryScriptModelLoader.$inject = [
+ 'BaseModel',
+ 'InventorySourceModel',
+ 'ModelsStrings'
+];
+
+export default InventoryScriptModelLoader;
diff --git a/awx/ui/client/lib/models/InventorySource.js b/awx/ui/client/lib/models/InventorySource.js
new file mode 100644
index 0000000000..abde6883fb
--- /dev/null
+++ b/awx/ui/client/lib/models/InventorySource.js
@@ -0,0 +1,44 @@
+let BaseModel;
+let WorkflowJobTemplateNodeModel;
+let ModelsStrings;
+
+function setDependentResources (id) {
+ this.dependentResources = [
+ {
+ model: new WorkflowJobTemplateNodeModel(),
+ params: {
+ unified_job_template: id
+ }
+ }
+ ];
+}
+
+function InventorySourceModel (method, resource, graft) {
+ BaseModel.call(this, 'inventory_sources');
+
+ this.Constructor = InventorySourceModel;
+ this.setDependentResources = setDependentResources.bind(this);
+ this.label = ModelsStrings.get('labels.INVENTORY_SOURCE');
+
+ return this.create(method, resource, graft);
+}
+
+function InventorySourceModelLoader (
+ _BaseModel_,
+ _WorkflowJobTemplateNodeModel_,
+ _ModelsStrings_
+) {
+ BaseModel = _BaseModel_;
+ WorkflowJobTemplateNodeModel = _WorkflowJobTemplateNodeModel_;
+ ModelsStrings = _ModelsStrings_;
+
+ return InventorySourceModel;
+}
+
+InventorySourceModelLoader.$inject = [
+ 'BaseModel',
+ 'WorkflowJobTemplateNodeModel',
+ 'ModelsStrings'
+];
+
+export default InventorySourceModelLoader;
diff --git a/awx/ui/client/lib/models/JobTemplate.js b/awx/ui/client/lib/models/JobTemplate.js
new file mode 100644
index 0000000000..2b0a7ce021
--- /dev/null
+++ b/awx/ui/client/lib/models/JobTemplate.js
@@ -0,0 +1,44 @@
+let BaseModel;
+let WorkflowJobTemplateNodeModel;
+let ModelsStrings;
+
+function setDependentResources (id) {
+ this.dependentResources = [
+ {
+ model: new WorkflowJobTemplateNodeModel(),
+ params: {
+ unified_job_template: id
+ }
+ }
+ ];
+}
+
+function JobTemplateModel (method, resource, graft) {
+ BaseModel.call(this, 'job_templates');
+
+ this.Constructor = JobTemplateModel;
+ this.setDependentResources = setDependentResources.bind(this);
+ this.label = ModelsStrings.get('labels.JOB_TEMPLATE');
+
+ return this.create(method, resource, graft);
+}
+
+function JobTemplateModelLoader (
+ _BaseModel_,
+ _WorkflowJobTemplateNodeModel_,
+ _ModelsStrings_
+) {
+ BaseModel = _BaseModel_;
+ WorkflowJobTemplateNodeModel = _WorkflowJobTemplateNodeModel_;
+ ModelsStrings = _ModelsStrings_;
+
+ return JobTemplateModel;
+}
+
+JobTemplateModelLoader.$inject = [
+ 'BaseModel',
+ 'WorkflowJobTemplateNodeModel',
+ 'ModelsStrings'
+];
+
+export default JobTemplateModelLoader;
diff --git a/awx/ui/client/lib/models/Organization.js b/awx/ui/client/lib/models/Organization.js
index 53448cd80f..734d547395 100644
--- a/awx/ui/client/lib/models/Organization.js
+++ b/awx/ui/client/lib/models/Organization.js
@@ -1,19 +1,28 @@
let BaseModel;
+let ModelsStrings;
function OrganizationModel (method, resource, graft) {
BaseModel.call(this, 'organizations');
this.Constructor = OrganizationModel;
+ this.label = ModelsStrings.get('labels.ORGANIZATION');
return this.create(method, resource, graft);
}
-function OrganizationModelLoader (_BaseModel_) {
+function OrganizationModelLoader (
+ _BaseModel_,
+ _ModelsStrings_
+) {
BaseModel = _BaseModel_;
+ ModelsStrings = _ModelsStrings_;
return OrganizationModel;
}
-OrganizationModelLoader.$inject = ['BaseModel'];
+OrganizationModelLoader.$inject = [
+ 'BaseModel',
+ 'ModelsStrings'
+];
export default OrganizationModelLoader;
diff --git a/awx/ui/client/lib/models/Project.js b/awx/ui/client/lib/models/Project.js
new file mode 100644
index 0000000000..63d363bd92
--- /dev/null
+++ b/awx/ui/client/lib/models/Project.js
@@ -0,0 +1,64 @@
+let BaseModel;
+let JobTemplateModel;
+let WorkflowJobTemplateNodeModel;
+let InventorySourceModel;
+let ModelsStrings;
+
+function setDependentResources (id) {
+ this.dependentResources = [
+ {
+ model: new JobTemplateModel(),
+ params: {
+ project: id
+ }
+ },
+ {
+ model: new WorkflowJobTemplateNodeModel(),
+ params: {
+ unified_job_template: id
+ }
+ },
+ {
+ model: new InventorySourceModel(),
+ params: {
+ source_project: id
+ }
+ }
+ ];
+}
+
+function ProjectModel (method, resource, graft) {
+ BaseModel.call(this, 'projects');
+
+ this.Constructor = ProjectModel;
+ this.setDependentResources = setDependentResources.bind(this);
+ this.label = ModelsStrings.get('labels.PROJECT');
+
+ return this.create(method, resource, graft);
+}
+
+function ProjectModelLoader (
+ _BaseModel_,
+ _JobTemplateModel_,
+ _WorkflowJobTemplateNodeModel_,
+ _InventorySourceModel_,
+ _ModelsStrings_
+) {
+ BaseModel = _BaseModel_;
+ JobTemplateModel = _JobTemplateModel_;
+ WorkflowJobTemplateNodeModel = _WorkflowJobTemplateNodeModel_;
+ InventorySourceModel = _InventorySourceModel_;
+ ModelsStrings = _ModelsStrings_;
+
+ return ProjectModel;
+}
+
+ProjectModelLoader.$inject = [
+ 'BaseModel',
+ 'JobTemplateModel',
+ 'WorkflowJobTemplateNodeModel',
+ 'InventorySourceModel',
+ 'ModelsStrings'
+];
+
+export default ProjectModelLoader;
diff --git a/awx/ui/client/lib/models/WorkflowJobTemplateNode.js b/awx/ui/client/lib/models/WorkflowJobTemplateNode.js
new file mode 100644
index 0000000000..bc5cfdf107
--- /dev/null
+++ b/awx/ui/client/lib/models/WorkflowJobTemplateNode.js
@@ -0,0 +1,28 @@
+let BaseModel;
+let ModelsStrings;
+
+function WorkflowJobTemplateNodeModel (method, resource, graft) {
+ BaseModel.call(this, 'workflow_job_template_nodes');
+
+ this.Constructor = WorkflowJobTemplateNodeModel;
+ this.label = ModelsStrings.get('labels.WORKFLOW_JOB_TEMPLATE_NODE');
+
+ return this.create(method, resource, graft);
+}
+
+function WorkflowJobTemplateNodeModelLoader (
+ _BaseModel_,
+ _ModelsStrings_
+) {
+ BaseModel = _BaseModel_;
+ ModelsStrings = _ModelsStrings_;
+
+ return WorkflowJobTemplateNodeModel;
+}
+
+WorkflowJobTemplateNodeModelLoader.$inject = [
+ 'BaseModel',
+ 'ModelsStrings'
+];
+
+export default WorkflowJobTemplateNodeModelLoader;
diff --git a/awx/ui/client/lib/models/index.js b/awx/ui/client/lib/models/index.js
index 1213463e57..08de2e3786 100644
--- a/awx/ui/client/lib/models/index.js
+++ b/awx/ui/client/lib/models/index.js
@@ -6,6 +6,14 @@ import Credential from '~models/Credential';
import CredentialType from '~models/CredentialType';
import Me from '~models/Me';
import Organization from '~models/Organization';
+import Project from '~models/Project';
+import JobTemplate from '~models/JobTemplate';
+import WorkflowJobTemplateNode from '~models/WorkflowJobTemplateNode';
+import InventorySource from '~models/InventorySource';
+import Inventory from '~models/Inventory';
+import InventoryScript from '~models/InventoryScript';
+
+import ModelsStrings from '~models/models.strings';
const MODULE_NAME = 'at.lib.models';
@@ -18,6 +26,13 @@ angular
.service('CredentialModel', Credential)
.service('CredentialTypeModel', CredentialType)
.service('MeModel', Me)
- .service('OrganizationModel', Organization);
+ .service('OrganizationModel', Organization)
+ .service('ProjectModel', Project)
+ .service('JobTemplateModel', JobTemplate)
+ .service('WorkflowJobTemplateNodeModel', WorkflowJobTemplateNode)
+ .service('InventorySourceModel', InventorySource)
+ .service('InventoryModel', Inventory)
+ .service('InventoryScriptModel', InventoryScript)
+ .service('ModelsStrings', ModelsStrings);
export default MODULE_NAME;
diff --git a/awx/ui/client/lib/models/models.strings.js b/awx/ui/client/lib/models/models.strings.js
new file mode 100644
index 0000000000..55b72be5f0
--- /dev/null
+++ b/awx/ui/client/lib/models/models.strings.js
@@ -0,0 +1,22 @@
+function ModelsStrings (BaseString) {
+ BaseString.call(this, 'models');
+
+ const { t } = this;
+ const ns = this.models;
+
+ ns.labels = {
+ CREDENTIAL: t.s('Credentials'),
+ CREDENTIAL_TYPE: t.s('Credential Types'),
+ INVENTORY: t.s('Inventories'),
+ INVENTORY_SCRIPT: t.s('Inventory Scripts'),
+ INVENTORY_SOURCE: t.s('Inventory Sources'),
+ JOB_TEMPLATE: t.s('Job Templates'),
+ ORGANIZATION: t.s('Organizations'),
+ PROJECT: t.s('Projects'),
+ WORKFLOW_JOB_TEMPLATE_NODE: t.s('Workflow Job Template Nodes')
+ };
+}
+
+ModelsStrings.$inject = ['BaseStringService'];
+
+export default ModelsStrings;
diff --git a/awx/ui/client/src/credential-types/credential-types.strings.js b/awx/ui/client/src/credential-types/credential-types.strings.js
new file mode 100644
index 0000000000..ec2e478ef2
--- /dev/null
+++ b/awx/ui/client/src/credential-types/credential-types.strings.js
@@ -0,0 +1,15 @@
+function CredentialTypesStrings (BaseString) {
+ BaseString.call(this, 'credential_types');
+
+ let t = this.t;
+ let ns = this.credential_types;
+
+ ns.deleteCredentialType = {
+ CONFIRM: t.s('Are you sure you want to delete this credential type?'),
+ CREDENTIAL_TYPE_IN_USE: t.s('This credential type is currently being used by one or more credentials. Credentials that use this credential type must be deleted before the credential type can be deleted.')
+ };
+}
+
+CredentialTypesStrings.$inject = ['BaseStringService'];
+
+export default CredentialTypesStrings;
diff --git a/awx/ui/client/src/credential-types/list/list.controller.js b/awx/ui/client/src/credential-types/list/list.controller.js
index 129d415711..db44d738c6 100644
--- a/awx/ui/client/src/credential-types/list/list.controller.js
+++ b/awx/ui/client/src/credential-types/list/list.controller.js
@@ -5,11 +5,16 @@
*************************************************/
export default ['$rootScope', '$scope', 'Wait', 'CredentialTypesList',
- 'GetBasePath', 'Rest', 'ProcessErrors', 'Prompt', '$state', '$filter', 'Dataset', 'rbacUiControlService', 'Alert', '$q',
+ 'GetBasePath', 'Rest', 'ProcessErrors', 'Prompt', '$state', '$filter',
+ 'Dataset', 'rbacUiControlService', 'Alert', '$q', 'CredentialTypeModel',
+ 'CredentialTypesStrings', 'i18n',
function(
$rootScope, $scope, Wait, CredentialTypesList,
- GetBasePath, Rest, ProcessErrors, Prompt, $state, $filter, Dataset, rbacUiControlService, Alert, $q
+ GetBasePath, Rest, ProcessErrors, Prompt, $state, $filter,
+ Dataset, rbacUiControlService, Alert, $q, CredentialType,
+ CredentialTypesStrings, i18n
) {
+ let credentialType = new CredentialType();
var defaultUrl = GetBasePath('credential_types'),
list = CredentialTypesList;
@@ -58,8 +63,7 @@ export default ['$rootScope', '$scope', 'Wait', 'CredentialTypesList',
$('#prompt-modal').modal('hide');
Wait('start');
var url = defaultUrl + id + '/';
- Rest.setUrl(url);
- Rest.destroy()
+ credentialType.request('delete', id)
.then(() => {
let reloadListStateParams = null;
@@ -83,13 +87,29 @@ export default ['$rootScope', '$scope', 'Wait', 'CredentialTypesList',
});
};
- var bodyHtml = 'Are you sure you want to delete the credential type below?
' + $filter('sanitize')(name) + '
';
- Prompt({
- hdr: 'Delete',
- body: bodyHtml,
- action: action,
- actionText: 'DELETE'
- });
+ credentialType.getDependentResourceCounts(id)
+ .then((counts) => {
+ let credentialTypeInUse = false;
+ let deleteModalBody = `${CredentialTypesStrings.get('deleteCredentialType.CONFIRM')}
`;
+
+ counts.forEach(countObj => {
+ if(countObj.count && countObj.count > 0) {
+ credentialTypeInUse = true;
+ }
+ });
+
+ if (credentialTypeInUse) {
+ deleteModalBody = `${CredentialTypesStrings.get('deleteCredentialType.CREDENTIAL_TYPE_IN_USE')}
`;
+ }
+
+ Prompt({
+ hdr: i18n._('Delete') + ' ' + $filter('sanitize')(name),
+ body: deleteModalBody,
+ action: action,
+ hideActionButton: credentialTypeInUse ? true : false,
+ actionText: 'DELETE'
+ });
+ });
};
$scope.addCredentialType = function() {
diff --git a/awx/ui/client/src/credential-types/main.js b/awx/ui/client/src/credential-types/main.js
index be36639928..0f2ce9f186 100644
--- a/awx/ui/client/src/credential-types/main.js
+++ b/awx/ui/client/src/credential-types/main.js
@@ -10,6 +10,7 @@ import credentialTypesEdit from './edit/main';
import list from './credential-types.list';
import form from './credential-types.form';
import { N_ } from '../i18n';
+import CredentialTypesStrings from './credential-types.strings';
export default
angular.module('credentialTypes', [
@@ -19,6 +20,7 @@ angular.module('credentialTypes', [
])
.factory('CredentialTypesList', list)
.factory('CredentialTypesForm', form)
+ .service('CredentialTypesStrings', CredentialTypesStrings)
.config(['$stateProvider', 'stateDefinitionsProvider',
function($stateProvider, stateDefinitionsProvider) {
let stateDefinitions = stateDefinitionsProvider.$get();
diff --git a/awx/ui/client/src/credentials/list/credentials-list.controller.js b/awx/ui/client/src/credentials/list/credentials-list.controller.js
index 798ce5b024..efb3bfb9d1 100644
--- a/awx/ui/client/src/credentials/list/credentials-list.controller.js
+++ b/awx/ui/client/src/credentials/list/credentials-list.controller.js
@@ -6,9 +6,12 @@
export default ['$scope', 'Rest', 'CredentialList', 'Prompt', 'ProcessErrors', 'GetBasePath',
'Wait', '$state', '$filter', 'rbacUiControlService', 'Dataset', 'credentialType', 'i18n',
+ 'CredentialModel', 'CredentialsStrings',
function($scope, Rest, CredentialList, Prompt,
ProcessErrors, GetBasePath, Wait, $state, $filter, rbacUiControlService, Dataset,
- credentialType, i18n) {
+ credentialType, i18n, Credential, CredentialsStrings) {
+
+ let credential = new Credential();
var list = CredentialList,
defaultUrl = GetBasePath('credentials');
@@ -83,8 +86,7 @@ export default ['$scope', 'Rest', 'CredentialList', 'Prompt', 'ProcessErrors', '
$('#prompt-modal').modal('hide');
Wait('start');
var url = defaultUrl + id + '/';
- Rest.setUrl(url);
- Rest.destroy()
+ credential.request('delete', id)
.then(() => {
let reloadListStateParams = null;
@@ -109,12 +111,31 @@ export default ['$scope', 'Rest', 'CredentialList', 'Prompt', 'ProcessErrors', '
});
};
- Prompt({
- hdr: i18n._('Delete'),
- body: '' + i18n._('Are you sure you want to delete the credential below?') + '
' + $filter('sanitize')(name) + '
',
- action: action,
- actionText: i18n._('DELETE')
- });
+ credential.getDependentResourceCounts(id)
+ .then((counts) => {
+ const invalidateRelatedLines = [];
+ let deleteModalBody = `${CredentialsStrings.get('deleteCredential.CONFIRM')}
`;
+
+ counts.forEach(countObj => {
+ if(countObj.count && countObj.count > 0) {
+ invalidateRelatedLines.push(`${countObj.label} ${countObj.count}
`);
+ }
+ });
+
+ if (invalidateRelatedLines && invalidateRelatedLines.length > 0) {
+ deleteModalBody = `${CredentialsStrings.get('deleteCredential.CONFIRM')} ${CredentialsStrings.get('deleteCredential.INVALIDATE')}
`;
+ invalidateRelatedLines.forEach(invalidateRelatedLine => {
+ deleteModalBody += invalidateRelatedLine;
+ });
+ }
+
+ Prompt({
+ hdr: i18n._('Delete') + ' ' + $filter('sanitize')(name),
+ body: deleteModalBody,
+ action: action,
+ actionText: 'DELETE'
+ });
+ });
};
}
];
diff --git a/awx/ui/client/src/inventories-hosts/inventories/list/inventory-list.controller.js b/awx/ui/client/src/inventories-hosts/inventories/list/inventory-list.controller.js
index f55bd351e0..1653946a56 100644
--- a/awx/ui/client/src/inventories-hosts/inventories/list/inventory-list.controller.js
+++ b/awx/ui/client/src/inventories-hosts/inventories/list/inventory-list.controller.js
@@ -13,7 +13,9 @@
function InventoriesList($scope,
$filter, Rest, InventoryList, Prompt,
ProcessErrors, GetBasePath, Wait, $state,
- Dataset, canAdd, i18n) {
+ Dataset, canAdd, i18n, Inventory, InventoryHostsStrings) {
+
+ let inventory = new Inventory();
let list = InventoryList,
defaultUrl = GetBasePath('inventory');
@@ -83,8 +85,7 @@ function InventoriesList($scope,
var url = defaultUrl + id + '/';
Wait('start');
$('#prompt-modal').modal('hide');
- Rest.setUrl(url);
- Rest.destroy()
+ inventory.request('delete', id)
.then(() => {
Wait('stop');
})
@@ -95,13 +96,33 @@ function InventoriesList($scope,
});
};
- Prompt({
- hdr: 'Delete',
- body: '' + i18n._('Are you sure you want to delete the inventory below?') + '
' + $filter('sanitize')(name) + '
' +
- 'Note: ' + i18n._('The inventory will be in a pending status until the final delete is processed.') + '
',
- action: action,
- actionText: i18n._('DELETE')
- });
+ inventory.getDependentResourceCounts(id)
+ .then((counts) => {
+ const invalidateRelatedLines = [];
+ let deleteModalBody = `${InventoryHostsStrings.get('deleteInventory.CONFIRM')}
`;
+
+ counts.forEach(countObj => {
+ if(countObj.count && countObj.count > 0) {
+ invalidateRelatedLines.push(`${countObj.label} ${countObj.count}
`);
+ }
+ });
+
+ if (invalidateRelatedLines && invalidateRelatedLines.length > 0) {
+ deleteModalBody = `${InventoryHostsStrings.get('deleteInventory.CONFIRM')} ${InventoryHostsStrings.get('deleteInventory.INVALIDATE')}
`;
+ invalidateRelatedLines.forEach(invalidateRelatedLine => {
+ deleteModalBody += invalidateRelatedLine;
+ });
+ }
+
+ deleteModalBody += 'Note: ' + i18n._('The inventory will be in a pending status until the final delete is processed.') + '
';
+
+ Prompt({
+ hdr: i18n._('Delete') + ' ' + $filter('sanitize')(name),
+ body: deleteModalBody,
+ action: action,
+ actionText: 'DELETE'
+ });
+ });
};
$scope.$on(`ws-inventories`, function(e, data){
@@ -129,5 +150,6 @@ function InventoriesList($scope,
export default ['$scope',
'$filter', 'Rest', 'InventoryList', 'Prompt',
'ProcessErrors', 'GetBasePath', 'Wait',
- '$state', 'Dataset', 'canAdd', 'i18n', InventoriesList
+ '$state', 'Dataset', 'canAdd', 'i18n', 'InventoryModel',
+ 'InventoryHostsStrings', InventoriesList
];
diff --git a/awx/ui/client/src/inventories-hosts/inventories/related/sources/list/sources-list.controller.js b/awx/ui/client/src/inventories-hosts/inventories/related/sources/list/sources-list.controller.js
index 0d806bb4e3..d606a01b8d 100644
--- a/awx/ui/client/src/inventories-hosts/inventories/related/sources/list/sources-list.controller.js
+++ b/awx/ui/client/src/inventories-hosts/inventories/related/sources/list/sources-list.controller.js
@@ -9,12 +9,15 @@
'ViewUpdateStatus', 'rbacUiControlService', 'GetBasePath',
'GetSyncStatusMsg', 'Dataset', 'Find', 'QuerySet',
'inventoryData', '$filter', 'Prompt', 'Wait', 'SourcesService', 'inventorySourceOptions',
- 'canAdd', 'hasSyncableSources', 'i18n',
+ 'canAdd', 'hasSyncableSources', 'i18n', 'InventoryHostsStrings', 'InventorySourceModel',
function($scope, $rootScope, $state, $stateParams, SourcesListDefinition,
InventoryUpdate, CancelSourceUpdate,
ViewUpdateStatus, rbacUiControlService, GetBasePath, GetSyncStatusMsg,
Dataset, Find, qs, inventoryData, $filter, Prompt,
- Wait, SourcesService, inventorySourceOptions, canAdd, hasSyncableSources, i18n){
+ Wait, SourcesService, inventorySourceOptions, canAdd, hasSyncableSources, i18n,
+ InventoryHostsStrings, InventorySource){
+
+ let inventorySource = new InventorySource();
let list = SourcesListDefinition;
var inventory_source;
@@ -117,7 +120,6 @@
$state.go('inventories.edit.inventory_sources.edit', {inventory_source_id: id});
};
$scope.deleteSource = function(inventory_source){
- var body = '' + i18n._('Are you sure you want to permanently delete the inventory source below from the inventory?') + '
' + $filter('sanitize')(inventory_source.name) + '
';
var action = function(){
delete $rootScope.promptActionBtnClass;
Wait('start');
@@ -137,14 +139,34 @@
Wait('stop');
});
};
- // Prompt depends on having $rootScope.promptActionBtnClass available...
- Prompt({
- hdr: i18n._('Delete Source'),
- body: body,
- action: action,
- actionText: i18n._('DELETE'),
- });
- $rootScope.promptActionBtnClass = 'Modal-errorButton';
+
+ inventorySource.getDependentResourceCounts(inventory_source.id)
+ .then((counts) => {
+ const invalidateRelatedLines = [];
+ let deleteModalBody = `${InventoryHostsStrings.get('deleteSource.CONFIRM')}
`;
+
+ counts.forEach(countObj => {
+ if(countObj.count && countObj.count > 0) {
+ invalidateRelatedLines.push(`${countObj.label} ${countObj.count}
`);
+ }
+ });
+
+ if (invalidateRelatedLines && invalidateRelatedLines.length > 0) {
+ deleteModalBody = `${InventoryHostsStrings.get('deleteSource.CONFIRM')} ${InventoryHostsStrings.get('deleteSource.INVALIDATE')}
`;
+ invalidateRelatedLines.forEach(invalidateRelatedLine => {
+ deleteModalBody += invalidateRelatedLine;
+ });
+ }
+
+ Prompt({
+ hdr: i18n._('Delete Source') + ' ' + $filter('sanitize')(inventory_source.name),
+ body: deleteModalBody,
+ action: action,
+ actionText: 'DELETE'
+ });
+ $rootScope.promptActionBtnClass = 'Modal-errorButton';
+ });
+
};
$scope.updateSource = function(inventory_source) {
diff --git a/awx/ui/client/src/inventories-hosts/inventory-hosts.strings.js b/awx/ui/client/src/inventories-hosts/inventory-hosts.strings.js
index c64b73a933..0744be46db 100644
--- a/awx/ui/client/src/inventories-hosts/inventory-hosts.strings.js
+++ b/awx/ui/client/src/inventories-hosts/inventory-hosts.strings.js
@@ -4,6 +4,16 @@ function InventoryHostsStrings (BaseString) {
let t = this.t;
let ns = this['inventory-hosts'];
+ ns.deleteInventory = {
+ CONFIRM: t.s('Are you sure you want to delete this inventory?'),
+ INVALIDATE: t.s('Doing so will invalidate the following:')
+ };
+
+ ns.deleteSource = {
+ CONFIRM: t.s('Are you sure you want to delete this inventory source?'),
+ INVALIDATE: t.s('Doing so will invalidate the following:')
+ };
+
ns.deletegroup = {
GROUP: count => t.p(count, 'group', 'groups'),
HOST: count => t.p(count, 'host', 'hosts'),
diff --git a/awx/ui/client/src/inventory-scripts/inventory-scripts.strings.js b/awx/ui/client/src/inventory-scripts/inventory-scripts.strings.js
new file mode 100644
index 0000000000..9b3fccd4af
--- /dev/null
+++ b/awx/ui/client/src/inventory-scripts/inventory-scripts.strings.js
@@ -0,0 +1,15 @@
+function InventoryScriptsStrings (BaseString) {
+ BaseString.call(this, 'inventory_scripts');
+
+ let t = this.t;
+ let ns = this.inventory_scripts;
+
+ ns.deleteInventoryScript = {
+ CONFIRM: t.s('Are you sure you want to delete this inventory script?'),
+ INVALIDATE: t.s('Doing so will invalidate the following:')
+ };
+}
+
+InventoryScriptsStrings.$inject = ['BaseStringService'];
+
+export default InventoryScriptsStrings;
diff --git a/awx/ui/client/src/inventory-scripts/list/list.controller.js b/awx/ui/client/src/inventory-scripts/list/list.controller.js
index 979d6fc2e0..8f8156951e 100644
--- a/awx/ui/client/src/inventory-scripts/list/list.controller.js
+++ b/awx/ui/client/src/inventory-scripts/list/list.controller.js
@@ -5,11 +5,16 @@
*************************************************/
export default ['$rootScope', '$scope', 'Wait', 'InventoryScriptsList',
- 'GetBasePath', 'Rest', 'ProcessErrors', 'Prompt', '$state', '$filter', 'Dataset', 'rbacUiControlService',
+ 'GetBasePath', 'Rest', 'ProcessErrors', 'Prompt', '$state', '$filter',
+ 'Dataset', 'rbacUiControlService', 'InventoryScriptModel', 'InventoryScriptsStrings',
+ 'i18n',
function(
$rootScope, $scope, Wait, InventoryScriptsList,
- GetBasePath, Rest, ProcessErrors, Prompt, $state, $filter, Dataset, rbacUiControlService
+ GetBasePath, Rest, ProcessErrors, Prompt, $state, $filter,
+ Dataset, rbacUiControlService, InventoryScript, InventoryScriptsStrings,
+ i18n
) {
+ let inventoryScript = new InventoryScript();
var defaultUrl = GetBasePath('inventory_scripts'),
list = InventoryScriptsList;
@@ -48,8 +53,7 @@ export default ['$rootScope', '$scope', 'Wait', 'InventoryScriptsList',
$('#prompt-modal').modal('hide');
Wait('start');
var url = defaultUrl + id + '/';
- Rest.setUrl(url);
- Rest.destroy()
+ inventoryScript.request('delete', id)
.then(() => {
let reloadListStateParams = null;
@@ -73,13 +77,31 @@ export default ['$rootScope', '$scope', 'Wait', 'InventoryScriptsList',
});
};
- var bodyHtml = 'Are you sure you want to delete the inventory script below?
' + $filter('sanitize')(name) + '
';
- Prompt({
- hdr: 'Delete',
- body: bodyHtml,
- action: action,
- actionText: 'DELETE'
- });
+ inventoryScript.getDependentResourceCounts(id)
+ .then((counts) => {
+ const invalidateRelatedLines = [];
+ let deleteModalBody = `${InventoryScriptsStrings.get('deleteInventoryScript.CONFIRM')}
`;
+
+ counts.forEach(countObj => {
+ if(countObj.count && countObj.count > 0) {
+ invalidateRelatedLines.push(`${countObj.label} ${countObj.count}
`);
+ }
+ });
+
+ if (invalidateRelatedLines && invalidateRelatedLines.length > 0) {
+ deleteModalBody = `${InventoryScriptsStrings.get('deleteInventoryScript.CONFIRM')} ${InventoryScriptsStrings.get('deleteInventoryScript.INVALIDATE')}
`;
+ invalidateRelatedLines.forEach(invalidateRelatedLine => {
+ deleteModalBody += invalidateRelatedLine;
+ });
+ }
+
+ Prompt({
+ hdr: i18n._('Delete') + ' ' + $filter('sanitize')(name),
+ body: deleteModalBody,
+ action: action,
+ actionText: 'DELETE'
+ });
+ });
};
$scope.addCustomInv = function() {
diff --git a/awx/ui/client/src/inventory-scripts/main.js b/awx/ui/client/src/inventory-scripts/main.js
index ec7d3e9a94..4854878bca 100644
--- a/awx/ui/client/src/inventory-scripts/main.js
+++ b/awx/ui/client/src/inventory-scripts/main.js
@@ -10,6 +10,7 @@ import inventoryScriptsEdit from './edit/main';
import list from './inventory-scripts.list';
import form from './inventory-scripts.form';
import { N_ } from '../i18n';
+import InventoryScriptsStrings from './inventory-scripts.strings';
export default
angular.module('inventoryScripts', [
@@ -19,6 +20,7 @@ angular.module('inventoryScripts', [
])
.factory('InventoryScriptsList', list)
.factory('InventoryScriptsForm', form)
+ .service('InventoryScriptsStrings', InventoryScriptsStrings)
.config(['$stateProvider', 'stateDefinitionsProvider',
function($stateProvider, stateDefinitionsProvider) {
let stateDefinitions = stateDefinitionsProvider.$get();
diff --git a/awx/ui/client/src/organizations/list/organizations-list.controller.js b/awx/ui/client/src/organizations/list/organizations-list.controller.js
index 9cbe175efc..dd938c5f47 100644
--- a/awx/ui/client/src/organizations/list/organizations-list.controller.js
+++ b/awx/ui/client/src/organizations/list/organizations-list.controller.js
@@ -169,7 +169,7 @@ export default ['$stateParams', '$scope', '$rootScope',
Prompt({
hdr: i18n._('Delete'),
- body: '' + i18n._('Are you sure you want to delete the organization below?') + '
' + $filter('sanitize')(name) + '
',
+ body: '' + i18n._('Are you sure you want to delete the organization below? This makes everything in this organization unavailable.') + '
' + $filter('sanitize')(name) + '
',
action: action,
actionText: i18n._('DELETE')
});
diff --git a/awx/ui/client/src/projects/list/projects-list.controller.js b/awx/ui/client/src/projects/list/projects-list.controller.js
index 0e7d50f240..32f0c13179 100644
--- a/awx/ui/client/src/projects/list/projects-list.controller.js
+++ b/awx/ui/client/src/projects/list/projects-list.controller.js
@@ -7,14 +7,16 @@
export default ['$scope', '$rootScope', '$log', 'Rest', 'Alert',
'ProjectList', 'Prompt', 'ProcessErrors', 'GetBasePath', 'ProjectUpdate',
'Wait', 'Empty', 'Find', 'GetProjectIcon', 'GetProjectToolTip', '$filter',
- '$state', 'rbacUiControlService', 'Dataset', 'i18n', 'QuerySet',
+ '$state', 'rbacUiControlService', 'Dataset', 'i18n', 'QuerySet', 'ProjectModel',
+ 'ProjectsStrings',
function($scope, $rootScope, $log, Rest, Alert, ProjectList,
Prompt, ProcessErrors, GetBasePath, ProjectUpdate, Wait, Empty, Find,
GetProjectIcon, GetProjectToolTip, $filter, $state, rbacUiControlService,
- Dataset, i18n, qs) {
+ Dataset, i18n, qs, Project, ProjectsStrings) {
- var list = ProjectList,
- defaultUrl = GetBasePath('projects');
+ let project = new Project();
+
+ var list = ProjectList;
init();
@@ -176,9 +178,7 @@ export default ['$scope', '$rootScope', '$log', 'Rest', 'Alert',
var action = function() {
$('#prompt-modal').modal('hide');
Wait('start');
- var url = defaultUrl + id + '/';
- Rest.setUrl(url);
- Rest.destroy()
+ project.request('delete', id)
.then(() => {
let reloadListStateParams = null;
@@ -196,19 +196,38 @@ export default ['$scope', '$rootScope', '$log', 'Rest', 'Alert',
})
.catch(({data, status}) => {
ProcessErrors($scope, data, status, null, { hdr: i18n._('Error!'),
- msg: i18n.sprintf(i18n._('Call to %s failed. DELETE returned status: '), url) + status });
+ msg: i18n.sprintf(i18n._('Call to %s failed. DELETE returned status: '), `${project.path}${id}/`) + status });
})
.finally(function() {
Wait('stop');
});
};
- Prompt({
- hdr: i18n._('Delete'),
- body: '' + i18n._('Are you sure you want to delete the project below?') + '
' + '' + $filter('sanitize')(name) + '
',
- action: action,
- actionText: 'DELETE'
- });
+ project.getDependentResourceCounts(id)
+ .then((counts) => {
+ const invalidateRelatedLines = [];
+ let deleteModalBody = `${ProjectsStrings.get('deleteProject.CONFIRM')}
`;
+
+ counts.forEach(countObj => {
+ if(countObj.count && countObj.count > 0) {
+ invalidateRelatedLines.push(`${countObj.label} ${countObj.count}
`);
+ }
+ });
+
+ if (invalidateRelatedLines && invalidateRelatedLines.length > 0) {
+ deleteModalBody = `${ProjectsStrings.get('deleteProject.CONFIRM')} ${ProjectsStrings.get('deleteProject.INVALIDATE')}
`;
+ invalidateRelatedLines.forEach(invalidateRelatedLine => {
+ deleteModalBody += invalidateRelatedLine;
+ });
+ }
+
+ Prompt({
+ hdr: i18n._('Delete') + ' ' + $filter('sanitize')(name),
+ body: deleteModalBody,
+ action: action,
+ actionText: 'DELETE'
+ });
+ });
};
if ($scope.removeCancelUpdate) {
diff --git a/awx/ui/client/src/projects/main.js b/awx/ui/client/src/projects/main.js
index e5534b4a79..25e6eeaea1 100644
--- a/awx/ui/client/src/projects/main.js
+++ b/awx/ui/client/src/projects/main.js
@@ -14,6 +14,7 @@ import GetProjectPath from './factories/get-project-path.factory';
import GetProjectIcon from './factories/get-project-icon.factory';
import GetProjectToolTip from './factories/get-project-tool-tip.factory';
import ProjectsTemplatesRoute from './projects-templates.route';
+import ProjectsStrings from './projects.strings';
export default
angular.module('Projects', [])
@@ -25,6 +26,7 @@ angular.module('Projects', [])
.factory('GetProjectToolTip', GetProjectToolTip)
.factory('ProjectList', ProjectList)
.factory('ProjectsForm', ProjectsForm)
+ .service('ProjectsStrings', ProjectsStrings)
.config(['$stateProvider', 'stateDefinitionsProvider', '$stateExtenderProvider',
function($stateProvider, stateDefinitionsProvider,$stateExtenderProvider) {
let stateDefinitions = stateDefinitionsProvider.$get();
diff --git a/awx/ui/client/src/projects/projects.strings.js b/awx/ui/client/src/projects/projects.strings.js
new file mode 100644
index 0000000000..84566d12e7
--- /dev/null
+++ b/awx/ui/client/src/projects/projects.strings.js
@@ -0,0 +1,15 @@
+function ProjectsStrings (BaseString) {
+ BaseString.call(this, 'projects');
+
+ let t = this.t;
+ let ns = this.projects;
+
+ ns.deleteProject = {
+ CONFIRM: t.s('Are you sure you want to delete this project?'),
+ INVALIDATE: t.s('Doing so will invalidate the following:')
+ };
+}
+
+ProjectsStrings.$inject = ['BaseStringService'];
+
+export default ProjectsStrings;
diff --git a/awx/ui/client/src/shared/prompt-dialog.js b/awx/ui/client/src/shared/prompt-dialog.js
index 96152c0b89..dcb1812f78 100644
--- a/awx/ui/client/src/shared/prompt-dialog.js
+++ b/awx/ui/client/src/shared/prompt-dialog.js
@@ -42,6 +42,7 @@ angular.module('PromptDialog', ['Utilities'])
scope.promptBody = params.body;
scope.promptAction = params.action;
scope.promptActionText = (params.actionText === null || params.actionText === undefined || params.actionText === '') ? 'YES' : params.actionText;
+ scope.hideActionButton = params.hideActionButton ? true : false;
local_backdrop = (params.backdrop === undefined) ? "static" : params.backdrop;
diff --git a/awx/ui/client/src/templates/list/templates-list.controller.js b/awx/ui/client/src/templates/list/templates-list.controller.js
index b400ec66ed..9ecdecdd9d 100644
--- a/awx/ui/client/src/templates/list/templates-list.controller.js
+++ b/awx/ui/client/src/templates/list/templates-list.controller.js
@@ -8,14 +8,16 @@ export default ['$scope', '$rootScope',
'Alert','TemplateList', 'Prompt', 'ProcessErrors',
'GetBasePath', 'InitiatePlaybookRun', 'Wait', '$state', '$filter',
'Dataset', 'rbacUiControlService', 'TemplatesService','QuerySet',
- 'TemplateCopyService', 'i18n',
+ 'TemplateCopyService', 'i18n', 'JobTemplateModel', 'TemplatesStrings',
function(
$scope, $rootScope, Alert,
TemplateList, Prompt, ProcessErrors, GetBasePath,
InitiatePlaybookRun, Wait, $state, $filter, Dataset, rbacUiControlService, TemplatesService,
- qs, TemplateCopyService, i18n
+ qs, TemplateCopyService, i18n, JobTemplate, TemplatesStrings
) {
+ let jobTemplate = new JobTemplate();
+
var list = TemplateList;
init();
@@ -98,65 +100,85 @@ export default ['$scope', '$rootScope',
$scope.deleteJobTemplate = function(template) {
if(template) {
- Prompt({
- hdr: i18n._('Delete'),
- body: `${i18n._("Are you sure you want to delete the template below?")}
${$filter('sanitize')(template.name)}
`,
- action: function() {
+ var action = function() {
+ function handleSuccessfulDelete(isWorkflow) {
+ let stateParamId = isWorkflow ? $state.params.workflow_job_template_id : $state.params.job_template_id;
- function handleSuccessfulDelete(isWorkflow) {
- let stateParamId = isWorkflow ? $state.params.workflow_job_template_id : $state.params.job_template_id;
+ let reloadListStateParams = null;
- let reloadListStateParams = null;
+ if($scope.templates.length === 1 && $state.params.template_search && !_.isEmpty($state.params.template_search.page) && $state.params.template_search.page !== '1') {
+ reloadListStateParams = _.cloneDeep($state.params);
+ reloadListStateParams.template_search.page = (parseInt(reloadListStateParams.template_search.page)-1).toString();
+ }
- if($scope.templates.length === 1 && $state.params.template_search && !_.isEmpty($state.params.template_search.page) && $state.params.template_search.page !== '1') {
- reloadListStateParams = _.cloneDeep($state.params);
- reloadListStateParams.template_search.page = (parseInt(reloadListStateParams.template_search.page)-1).toString();
- }
+ if (parseInt(stateParamId) === template.id) {
+ // Move the user back to the templates list
+ $state.go("templates", reloadListStateParams, {reload: true});
+ } else {
+ $state.go(".", reloadListStateParams, {reload: true});
+ }
+ Wait('stop');
+ }
- if (parseInt(stateParamId) === template.id) {
- // Move the user back to the templates list
- $state.go("templates", reloadListStateParams, {reload: true});
- } else {
- $state.go(".", reloadListStateParams, {reload: true});
- }
- Wait('stop');
- }
+ $('#prompt-modal').modal('hide');
+ Wait('start');
+ if(template.type && (template.type === 'Workflow Job Template' || template.type === 'workflow_job_template')) {
+ TemplatesService.deleteWorkflowJobTemplate(template.id)
+ .then(function () {
+ handleSuccessfulDelete(true);
+ })
+ .catch(function (response) {
+ Wait('stop');
+ ProcessErrors($scope, response.data, response.status, null, { hdr: 'Error!',
+ msg: 'Call to delete workflow job template failed. DELETE returned status: ' + response.status + '.'});
+ });
+ }
+ else if(template.type && (template.type === 'Job Template' || template.type === 'job_template')) {
+ TemplatesService.deleteJobTemplate(template.id)
+ .then(function () {
+ handleSuccessfulDelete();
+ })
+ .catch(function (response) {
+ Wait('stop');
+ ProcessErrors($scope, response.data, response.status, null, { hdr: 'Error!',
+ msg: 'Call to delete job template failed. DELETE returned status: ' + response.status + '.'});
+ });
+ }
+ else {
+ Wait('stop');
+ Alert('Error: Unable to determine template type', 'We were unable to determine this template\'s type while deleting.');
+ }
+ };
- $('#prompt-modal').modal('hide');
- Wait('start');
- if(template.type && (template.type === 'Workflow Job Template' || template.type === 'workflow_job_template')) {
- TemplatesService.deleteWorkflowJobTemplate(template.id)
- .then(function () {
- handleSuccessfulDelete(true);
- })
- .catch(function (response) {
- Wait('stop');
- ProcessErrors($scope, response.data, response.status, null, { hdr: 'Error!',
- msg: 'Call to delete workflow job template failed. DELETE returned status: ' + response.status + '.'});
- });
- }
- else if(template.type && (template.type === 'Job Template' || template.type === 'job_template')) {
- TemplatesService.deleteJobTemplate(template.id)
- .then(function () {
- handleSuccessfulDelete();
- })
- .catch(function (response) {
- Wait('stop');
- ProcessErrors($scope, response.data, response.status, null, { hdr: 'Error!',
- msg: 'Call to delete job template failed. DELETE returned status: ' + response.status + '.'});
- });
- }
- else {
- Wait('stop');
- Alert('Error: Unable to determine template type', 'We were unable to determine this template\'s type while deleting.');
- }
- },
- actionText: i18n._('DELETE')
- });
- }
- else {
- Alert('Error: Unable to delete template', 'Template parameter is missing');
- }
+ jobTemplate.getDependentResourceCounts(template.id)
+ .then((counts) => {
+ const invalidateRelatedLines = [];
+ let deleteModalBody = `${TemplatesStrings.get('jobTemplates.deleteJobTemplate.CONFIRM')}
`;
+
+ counts.forEach(countObj => {
+ if(countObj.count && countObj.count > 0) {
+ invalidateRelatedLines.push(`${countObj.label} ${countObj.count}
`);
+ }
+ });
+
+ if (invalidateRelatedLines && invalidateRelatedLines.length > 0) {
+ deleteModalBody = `${TemplatesStrings.get('jobTemplates.deleteJobTemplate.CONFIRM')} ${TemplatesStrings.get('jobTemplates.deleteJobTemplate.INVALIDATE')}
`;
+ invalidateRelatedLines.forEach(invalidateRelatedLine => {
+ deleteModalBody += invalidateRelatedLine;
+ });
+ }
+
+ Prompt({
+ hdr: i18n._('Delete') + ' ' + $filter('sanitize')(template.name),
+ body: deleteModalBody,
+ action: action,
+ actionText: 'DELETE'
+ });
+ });
+ }
+ else {
+ Alert('Error: Unable to delete template', 'Template parameter is missing');
+ }
};
$scope.submitJob = function(template) {
diff --git a/awx/ui/client/src/templates/main.js b/awx/ui/client/src/templates/main.js
index 2712437a23..dfda8d26de 100644
--- a/awx/ui/client/src/templates/main.js
+++ b/awx/ui/client/src/templates/main.js
@@ -21,6 +21,7 @@ import WorkflowForm from './workflows.form';
import CompletedJobsList from './completed-jobs.list';
import InventorySourcesList from './inventory-sources.list';
import TemplateList from './templates.list';
+import TemplatesStrings from './templates.strings';
export default
angular.module('templates', [surveyMaker.name, templatesList.name, jobTemplates.name, labels.name, workflowAdd.name, workflowEdit.name,
@@ -33,6 +34,7 @@ angular.module('templates', [surveyMaker.name, templatesList.name, jobTemplates.
.factory('CompletedJobsList', CompletedJobsList)
.factory('TemplateList', TemplateList)
.value('InventorySourcesList', InventorySourcesList)
+ .service('TemplatesStrings', TemplatesStrings)
.config(['$stateProvider', 'stateDefinitionsProvider', '$stateExtenderProvider',
function($stateProvider, stateDefinitionsProvider, $stateExtenderProvider) {
let stateTree, addJobTemplate, editJobTemplate, addWorkflow, editWorkflow,
diff --git a/awx/ui/client/src/templates/templates.strings.js b/awx/ui/client/src/templates/templates.strings.js
new file mode 100644
index 0000000000..d8150b0d73
--- /dev/null
+++ b/awx/ui/client/src/templates/templates.strings.js
@@ -0,0 +1,17 @@
+function TemplatesStrings (BaseString) {
+ BaseString.call(this, 'templates');
+
+ let t = this.t;
+ let ns = this.templates;
+
+ ns.jobTemplates = {
+ deleteJobTemplate: {
+ CONFIRM: t.s('Are you sure you want to delete this job template?'),
+ INVALIDATE: t.s('Doing so will invalidate the following:')
+ }
+ };
+}
+
+TemplatesStrings.$inject = ['BaseStringService'];
+
+export default TemplatesStrings;