diff --git a/awx/ui/client/features/index.js b/awx/ui/client/features/index.js
index 01216e575f..763894c93c 100644
--- a/awx/ui/client/features/index.js
+++ b/awx/ui/client/features/index.js
@@ -6,6 +6,7 @@ import atFeaturesApplications from '~features/applications';
import atFeaturesCredentials from '~features/credentials';
import atFeaturesTemplates from '~features/templates';
import atFeaturesUsers from '~features/users';
+import atFeaturesJobs from '~features/jobs';
const MODULE_NAME = 'at.features';
@@ -16,7 +17,8 @@ angular.module(MODULE_NAME, [
atFeaturesApplications,
atFeaturesCredentials,
atFeaturesTemplates,
- atFeaturesUsers
+ atFeaturesUsers,
+ atFeaturesJobs
]);
export default MODULE_NAME;
diff --git a/awx/ui/client/features/jobs/index.js b/awx/ui/client/features/jobs/index.js
new file mode 100644
index 0000000000..c26f2b9d8f
--- /dev/null
+++ b/awx/ui/client/features/jobs/index.js
@@ -0,0 +1,13 @@
+import JobsStrings from './jobs.strings';
+import jobsRoute from './routes/jobs.route';
+
+const MODULE_NAME = 'at.features.jobs';
+
+angular
+ .module(MODULE_NAME, [])
+ .service('JobsStrings', JobsStrings)
+ .run(['$stateExtender', ($stateExtender) => {
+ $stateExtender.addState(jobsRoute);
+ }]);
+
+export default MODULE_NAME;
diff --git a/awx/ui/client/features/jobs/index.view.html b/awx/ui/client/features/jobs/index.view.html
new file mode 100644
index 0000000000..054e26c2ba
--- /dev/null
+++ b/awx/ui/client/features/jobs/index.view.html
@@ -0,0 +1,19 @@
+
diff --git a/awx/ui/client/features/jobs/jobs.strings.js b/awx/ui/client/features/jobs/jobs.strings.js
new file mode 100644
index 0000000000..a21c2b62e9
--- /dev/null
+++ b/awx/ui/client/features/jobs/jobs.strings.js
@@ -0,0 +1,20 @@
+function JobsStrings (BaseString) {
+ BaseString.call(this, 'jobs');
+
+ const { t } = this;
+ const ns = this.jobs;
+
+ ns.list = {
+ ROW_ITEM_LABEL_STARTED: t.s('Started'),
+ ROW_ITEM_LABEL_FINISHED: t.s('Finished'),
+ ROW_ITEM_LABEL_LAUNCHED_BY: t.s('Launched By'),
+ ROW_ITEM_LABEL_JOB_TEMPLATE: t.s('Job Template'),
+ ROW_ITEM_LABEL_INVENTORY: t.s('Inventory'),
+ ROW_ITEM_LABEL_PROJECT: t.s('Project'),
+ ROW_ITEM_LABEL_CREDENTIALS: t.s('Credentials'),
+ };
+}
+
+JobsStrings.$inject = ['BaseStringService'];
+
+export default JobsStrings;
diff --git a/awx/ui/client/features/jobs/jobsList.controller.js b/awx/ui/client/features/jobs/jobsList.controller.js
new file mode 100644
index 0000000000..b78a1b77b6
--- /dev/null
+++ b/awx/ui/client/features/jobs/jobsList.controller.js
@@ -0,0 +1,137 @@
+/** ***********************************************
+ * Copyright (c) 2018 Ansible, Inc.
+ *
+ * All Rights Reserved
+ ************************************************ */
+const mapChoices = choices => Object
+ .assign(...choices.map(([k, v]) => ({ [k]: v })));
+
+function ListJobsController (
+ $scope,
+ $state,
+ Dataset,
+ resolvedModels,
+ strings,
+ qs,
+ Prompt,
+ $filter,
+ ProcessErrors,
+ Wait,
+ Rest
+) {
+ const vm = this || {};
+ const [unifiedJob] = resolvedModels;
+
+ vm.strings = strings;
+
+ // smart-search
+ const name = 'jobs';
+ const iterator = 'job';
+ const key = 'job_dataset';
+
+ $scope.list = { iterator, name };
+ $scope.collection = { iterator, basePath: 'unified_jobs' };
+ $scope[key] = Dataset.data;
+ $scope[name] = Dataset.data.results;
+ $scope.$on('updateDataset', (e, dataset) => {
+ $scope[key] = dataset;
+ $scope[name] = dataset.results;
+ });
+ $scope.$on('ws-jobs', () => {
+ qs.search(unifiedJob.path, $state.params.job_search)
+ .then(({ data }) => {
+ $scope.$emit('updateDataset', data);
+ });
+ });
+
+ vm.jobTypes = mapChoices(unifiedJob
+ .options('actions.GET.type.choices'));
+
+ vm.getLink = ({ type, id }) => {
+ let link;
+
+ switch (type) {
+ case 'job':
+ link = `/#/jobs/${id}`;
+ break;
+ case 'ad_hoc_command':
+ link = `/#/ad_hoc_commands/${id}`;
+ break;
+ case 'system_job':
+ link = `/#/management_jobs/${id}`;
+ break;
+ case 'project_update':
+ link = `/#/scm_update/${id}`;
+ break;
+ case 'inventory_update':
+ link = `/#/inventory_sync/${id}`;
+ break;
+ case 'workflow_job':
+ link = `/#/workflows/${id}`;
+ break;
+ default:
+ link = '';
+ break;
+ }
+
+ return link;
+ };
+
+ vm.deleteJob = (job) => {
+ const action = () => {
+ $('#prompt-modal').modal('hide');
+ Wait('start');
+ Rest.setUrl(job.url);
+ Rest.destroy()
+ .then(() => {
+ let reloadListStateParams = null;
+
+ if ($scope.jobs.length === 1 && $state.params.job_search &&
+ !_.isEmpty($state.params.job_search.page) &&
+ $state.params.job_search.page !== '1') {
+ const page = `${(parseInt(reloadListStateParams
+ .job_search.page, 10) - 1)}`;
+ reloadListStateParams = _.cloneDeep($state.params);
+ reloadListStateParams.job_search.page = page;
+ }
+
+ $state.go('.', reloadListStateParams, { reload: true });
+ })
+ .catch(({ data, status }) => {
+ ProcessErrors($scope, data, status, null, {
+ hdr: strings.get('error.HEADER'),
+ msg: strings.get('error.CALL', { path: `${job.url}`, status })
+ });
+ })
+ .finally(() => {
+ Wait('stop');
+ });
+ };
+
+ const deleteModalBody = `${strings.get('deleteResource.CONFIRM', 'job')}
`;
+
+ Prompt({
+ hdr: strings.get('deleteResource.HEADER'),
+ resourceName: $filter('sanitize')(job.name),
+ body: deleteModalBody,
+ action,
+ actionText: 'DELETE'
+ });
+ };
+}
+
+ListJobsController.$inject = [
+ '$scope',
+ '$state',
+ 'Dataset',
+ 'resolvedModels',
+ 'JobsStrings',
+ 'QuerySet',
+ 'Prompt',
+ '$filter',
+ 'ProcessErrors',
+ 'Wait',
+ 'Rest'
+];
+
+export default ListJobsController;
diff --git a/awx/ui/client/features/jobs/jobsList.view.html b/awx/ui/client/features/jobs/jobsList.view.html
new file mode 100644
index 0000000000..6031a956ab
--- /dev/null
+++ b/awx/ui/client/features/jobs/jobsList.view.html
@@ -0,0 +1,82 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/awx/ui/client/features/jobs/routes/instanceGroupJobs.route.js b/awx/ui/client/features/jobs/routes/instanceGroupJobs.route.js
new file mode 100644
index 0000000000..fe86b78774
--- /dev/null
+++ b/awx/ui/client/features/jobs/routes/instanceGroupJobs.route.js
@@ -0,0 +1,64 @@
+import listContainerController from '~src/instance-groups/jobs/instanceGroupsJobsListContainer.controller';
+import { N_ } from '../../../src/i18n';
+import jobsListController from '../jobsList.controller';
+
+const jobsListTemplate = require('~features/jobs/jobsList.view.html');
+const listContainerTemplate = require('~src/instance-groups/jobs/instanceGroupsJobsListContainer.partial.html');
+
+export default {
+ name: 'instanceGroups.jobs',
+ url: '/:instance_group_id/jobs',
+ ncyBreadcrumb: {
+ parent: 'instanceGroups.edit',
+ label: N_('JOBS')
+ },
+ params: {
+ job_search: {
+ value: {
+ page_size: '10',
+ order_by: '-finished'
+ },
+ dynamic: true
+ }
+ },
+ views: {
+ 'instanceGroupsJobsContainer@instanceGroups': {
+ templateUrl: listContainerTemplate,
+ controller: listContainerController,
+ controllerAs: 'vm'
+ },
+ 'jobsList@instanceGroups.jobs': {
+ templateUrl: jobsListTemplate,
+ controller: jobsListController,
+ controllerAs: 'vm'
+ },
+ },
+ resolve: {
+ resolvedModels: [
+ 'UnifiedJobModel',
+ (UnifiedJob) => {
+ const models = [
+ new UnifiedJob(['options']),
+ ];
+ return Promise.all(models);
+ },
+ ],
+ Dataset: [
+ '$stateParams',
+ 'Wait',
+ 'GetBasePath',
+ 'QuerySet',
+ ($stateParams, Wait, GetBasePath, qs) => {
+ const groupId = $stateParams.instance_group_id;
+
+ const searchParam = $stateParams.job_search;
+
+ const searchPath = `api/v2/instance_groups/${groupId}/jobs`;
+
+ Wait('start');
+ return qs.search(searchPath, searchParam)
+ .finally(() => Wait('stop'));
+ }
+ ]
+ }
+};
diff --git a/awx/ui/client/features/jobs/routes/instanceJobs.route.js b/awx/ui/client/features/jobs/routes/instanceJobs.route.js
new file mode 100644
index 0000000000..06019bcf22
--- /dev/null
+++ b/awx/ui/client/features/jobs/routes/instanceJobs.route.js
@@ -0,0 +1,64 @@
+import listContainerController from '~src/instance-groups/jobs/instanceJobsListContainer.controller';
+import { N_ } from '../../../src/i18n';
+import jobsListController from '../jobsList.controller';
+
+const jobsListTemplate = require('~features/jobs/jobsList.view.html');
+const listContainerTemplate = require('~src/instance-groups/jobs/instanceJobsListContainer.partial.html');
+
+export default {
+ name: 'instanceGroups.instanceJobs',
+ url: '/:instance_group_id/instances/:instance_id/jobs',
+ ncyBreadcrumb: {
+ parent: 'instanceGroups.edit',
+ label: N_('JOBS')
+ },
+ views: {
+ 'instanceJobsContainer@instanceGroups': {
+ templateUrl: listContainerTemplate,
+ controller: listContainerController,
+ controllerAs: 'vm'
+ },
+ 'jobsList@instanceGroups.instanceJobs': {
+ templateUrl: jobsListTemplate,
+ controller: jobsListController,
+ controllerAs: 'vm'
+ },
+ },
+ params: {
+ job_search: {
+ value: {
+ page_size: '10',
+ order_by: '-finished'
+ },
+ dynamic: true
+ },
+ },
+ resolve: {
+ resolvedModels: [
+ 'UnifiedJobModel',
+ (UnifiedJob) => {
+ const models = [
+ new UnifiedJob(['options']),
+ ];
+ return Promise.all(models);
+ },
+ ],
+ Dataset: [
+ '$stateParams',
+ 'Wait',
+ 'GetBasePath',
+ 'QuerySet',
+ ($stateParams, Wait, GetBasePath, qs) => {
+ const instanceId = $stateParams.instance_id;
+
+ const searchParam = $stateParams.job_search;
+
+ const searchPath = `api/v2/instances/${instanceId}/jobs`;
+
+ Wait('start');
+ return qs.search(searchPath, searchParam)
+ .finally(() => Wait('stop'));
+ }
+ ]
+ }
+};
diff --git a/awx/ui/client/features/jobs/routes/inventoryCompletedJobs.route.js b/awx/ui/client/features/jobs/routes/inventoryCompletedJobs.route.js
new file mode 100644
index 0000000000..333359c6f6
--- /dev/null
+++ b/awx/ui/client/features/jobs/routes/inventoryCompletedJobs.route.js
@@ -0,0 +1,63 @@
+import { N_ } from '../../../src/i18n';
+import jobsListController from '../jobsList.controller';
+
+const jobsListTemplate = require('~features/jobs/jobsList.view.html');
+
+export default {
+ url: '/completed_jobs',
+ params: {
+ job_search: {
+ value: {
+ page_size: '20',
+ or__job__inventory: '',
+ or__adhoccommand__inventory: '',
+ or__inventoryupdate__inventory_source__inventory: '',
+ order_by: '-id'
+ },
+ dynamic: true,
+ squash: ''
+ }
+ },
+ ncyBreadcrumb: {
+ label: N_('JOBS')
+ },
+ views: {
+ related: {
+ templateUrl: jobsListTemplate,
+ controller: jobsListController,
+ controllerAs: 'vm'
+ }
+ },
+ resolve: {
+ resolvedModels: [
+ 'UnifiedJobModel',
+ (UnifiedJob) => {
+ const models = [
+ new UnifiedJob(['options']),
+ ];
+ return Promise.all(models);
+ },
+ ],
+ Dataset: [
+ '$stateParams',
+ 'Wait',
+ 'GetBasePath',
+ 'QuerySet',
+ ($stateParams, Wait, GetBasePath, qs) => {
+ const inventoryId = $stateParams.inventory_id ?
+ $stateParams.inventory_id : $stateParams.smartinventory_id;
+
+ const searchParam = _.assign($stateParams.job_search, {
+ or__job__inventory: inventoryId,
+ or__adhoccommand__inventory: inventoryId,
+ or__inventoryupdate__inventory_source__inventory: inventoryId });
+
+ const searchPath = GetBasePath('unified_jobs');
+
+ Wait('start');
+ return qs.search(searchPath, searchParam)
+ .finally(() => Wait('stop'));
+ }
+ ]
+ }
+};
diff --git a/awx/ui/client/features/jobs/routes/jobs.route.js b/awx/ui/client/features/jobs/routes/jobs.route.js
new file mode 100644
index 0000000000..c3ee0b4fef
--- /dev/null
+++ b/awx/ui/client/features/jobs/routes/jobs.route.js
@@ -0,0 +1,67 @@
+import { N_ } from '../../../src/i18n';
+import jobsListController from '../jobsList.controller';
+
+const indexTemplate = require('~features/jobs/index.view.html');
+const jobsListTemplate = require('~features/jobs/jobsList.view.html');
+
+export default {
+ searchPrefix: 'job',
+ name: 'jobs',
+ url: '/jobs',
+ ncyBreadcrumb: {
+ label: N_('JOBS')
+ },
+ params: {
+ job_search: {
+ value: {
+ not__launch_type: 'sync',
+ order_by: '-finished'
+ },
+ dynamic: true,
+ squash: false
+ }
+ },
+ data: {
+ socket: {
+ groups: {
+ jobs: ['status_changed'],
+ schedules: ['changed']
+ }
+ }
+ },
+ resolve: {
+ resolvedModels: [
+ 'UnifiedJobModel',
+ (UnifiedJob) => {
+ const models = [
+ new UnifiedJob(['options']),
+ ];
+ return Promise.all(models);
+ },
+ ],
+ Dataset: [
+ '$stateParams',
+ 'Wait',
+ 'GetBasePath',
+ 'QuerySet',
+ ($stateParams, Wait, GetBasePath, qs) => {
+ const searchParam = $stateParams.job_search;
+ const searchPath = GetBasePath('unified_jobs');
+
+ Wait('start');
+ return qs.search(searchPath, searchParam)
+ .finally(() => Wait('stop'));
+ }
+ ],
+ },
+ views: {
+ '@': {
+ templateUrl: indexTemplate
+ },
+ 'jobsList@jobs': {
+ templateUrl: jobsListTemplate,
+ controller: jobsListController,
+ controllerAs: 'vm'
+ }
+ }
+};
diff --git a/awx/ui/client/features/jobs/routes/portalModeAllJobs.route.js b/awx/ui/client/features/jobs/routes/portalModeAllJobs.route.js
new file mode 100644
index 0000000000..9595da3547
--- /dev/null
+++ b/awx/ui/client/features/jobs/routes/portalModeAllJobs.route.js
@@ -0,0 +1,50 @@
+import jobsListController from '../jobsList.controller';
+
+const jobsListTemplate = require('~features/jobs/jobsList.view.html');
+
+export default {
+ name: 'portalMode.allJobs',
+ url: '/alljobs?{job_search:queryset}',
+ params: {
+ job_search: {
+ value: {
+ page_size: '20',
+ order_by: '-finished'
+ },
+ dynamic: true
+ }
+ },
+ views: {
+ 'jobs@portalMode': {
+ templateUrl: jobsListTemplate,
+ controller: jobsListController,
+ controllerAs: 'vm'
+ }
+ },
+ resolve: {
+ resolvedModels: [
+ 'UnifiedJobModel',
+ (UnifiedJob) => {
+ const models = [
+ new UnifiedJob(['options']),
+ ];
+ return Promise.all(models);
+ },
+ ],
+ Dataset: [
+ '$stateParams',
+ 'Wait',
+ 'GetBasePath',
+ 'QuerySet',
+ ($stateParams, Wait, GetBasePath, qs) => {
+ const searchParam = $stateParams.job_search;
+
+ const searchPath = GetBasePath('unified_jobs');
+
+ Wait('start');
+ return qs.search(searchPath, searchParam)
+ .finally(() => Wait('stop'));
+ }
+ ]
+ }
+};
diff --git a/awx/ui/client/features/jobs/routes/portalModeMyJobs.route.js b/awx/ui/client/features/jobs/routes/portalModeMyJobs.route.js
new file mode 100644
index 0000000000..07506a553f
--- /dev/null
+++ b/awx/ui/client/features/jobs/routes/portalModeMyJobs.route.js
@@ -0,0 +1,53 @@
+import jobsListController from '../jobsList.controller';
+
+const jobsListTemplate = require('~features/jobs/jobsList.view.html');
+
+export default {
+ name: 'portalMode.myJobs',
+ url: '/myjobs?{job_search:queryset}',
+ params: {
+ job_search: {
+ value: {
+ page_size: '20',
+ order_by: '-finished',
+ created_by: null
+ },
+ dynamic: true
+ }
+ },
+ views: {
+ 'jobs@portalMode': {
+ templateUrl: jobsListTemplate,
+ controller: jobsListController,
+ controllerAs: 'vm'
+ }
+ },
+ resolve: {
+ resolvedModels: [
+ 'UnifiedJobModel',
+ (UnifiedJob) => {
+ const models = [
+ new UnifiedJob(['options']),
+ ];
+ return Promise.all(models);
+ },
+ ],
+ Dataset: [
+ '$stateParams',
+ 'Wait',
+ 'GetBasePath',
+ 'QuerySet',
+ '$rootScope',
+ ($stateParams, Wait, GetBasePath, qs, $rootScope) => {
+ const searchParam = _.assign($stateParams.job_search, {
+ created_by: $rootScope.current_user.id });
+
+ const searchPath = GetBasePath('unified_jobs');
+
+ Wait('start');
+ return qs.search(searchPath, searchParam)
+ .finally(() => Wait('stop'));
+ }
+ ]
+ }
+};
diff --git a/awx/ui/client/features/jobs/routes/templateCompletedJobs.route.js b/awx/ui/client/features/jobs/routes/templateCompletedJobs.route.js
new file mode 100644
index 0000000000..3fc69a5ffe
--- /dev/null
+++ b/awx/ui/client/features/jobs/routes/templateCompletedJobs.route.js
@@ -0,0 +1,60 @@
+import { N_ } from '../../../src/i18n';
+import jobsListController from '../jobsList.controller';
+
+const jobsListTemplate = require('~features/jobs/jobsList.view.html');
+
+export default {
+ url: '/completed_jobs',
+ name: 'templates.editJobTemplate.completed_jobs',
+ params: {
+ job_search: {
+ value: {
+ page_size: '20',
+ job__job_template: '',
+ order_by: '-id'
+ },
+ dynamic: true,
+ squash: ''
+ }
+ },
+ ncyBreadcrumb: {
+ label: N_('COMPLETED JOBS')
+ },
+ views: {
+ related: {
+ templateUrl: jobsListTemplate,
+ controller: jobsListController,
+ controllerAs: 'vm'
+ }
+ },
+ resolve: {
+ resolvedModels: [
+ 'UnifiedJobModel',
+ (UnifiedJob) => {
+ const models = [
+ new UnifiedJob(['options']),
+ ];
+ return Promise.all(models);
+ },
+ ],
+ Dataset: [
+ '$stateParams',
+ 'Wait',
+ 'GetBasePath',
+ 'QuerySet',
+ ($stateParams, Wait, GetBasePath, qs) => {
+ const templateId = $stateParams.job_template_id ?
+ $stateParams.job_template_id : $stateParams.job_template_id;
+
+ const searchParam = _.assign($stateParams
+ .job_search, { job__job_template: templateId });
+
+ const searchPath = GetBasePath('unified_jobs');
+
+ Wait('start');
+ return qs.search(searchPath, searchParam)
+ .finally(() => Wait('stop'));
+ }
+ ]
+ }
+};
diff --git a/awx/ui/client/lib/components/list/_index.less b/awx/ui/client/lib/components/list/_index.less
index b4eac97d30..d053764fe0 100644
--- a/awx/ui/client/lib/components/list/_index.less
+++ b/awx/ui/client/lib/components/list/_index.less
@@ -151,6 +151,10 @@
line-height: @at-height-list-row-item;
}
+.at-RowItem-status {
+ margin-right: @at-margin-right-list-row-item-status;
+}
+
.at-RowItem--isHeader {
color: @at-color-body-text;
margin-bottom: @at-margin-bottom-list-header;
@@ -263,6 +267,16 @@
margin: 2px 20px 0 0;
}
+.at-RowItem--inline {
+ display: inline-flex;
+ margin-right: @at-margin-right-list-row-item-inline;
+
+ .at-RowItem-label {
+ width: auto;
+ margin-right: @at-margin-right-list-row-item-inline-label;
+ }
+}
+
@media screen and (max-width: @at-breakpoint-compact-list) {
.at-Row-actions {
flex-direction: column;
@@ -271,4 +285,14 @@
.at-RowAction {
margin: @at-margin-list-row-action-mobile;
}
+
+ .at-RowItem--inline {
+ display: flex;
+ margin-right: inherit;
+
+ .at-RowItem-label {
+ width: @at-width-list-row-item-label;
+ margin-right: inherit;
+ }
+ }
}
diff --git a/awx/ui/client/lib/components/list/row-item.directive.js b/awx/ui/client/lib/components/list/row-item.directive.js
index e07820468e..296aa28249 100644
--- a/awx/ui/client/lib/components/list/row-item.directive.js
+++ b/awx/ui/client/lib/components/list/row-item.directive.js
@@ -7,10 +7,13 @@ function atRowItem () {
transclude: true,
templateUrl,
scope: {
+ inline: '@',
badge: '@',
headerValue: '@',
headerLink: '@',
headerTag: '@',
+ status: '@',
+ statusTip: '@',
labelValue: '@',
labelLink: '@',
labelState: '@',
diff --git a/awx/ui/client/lib/components/list/row-item.partial.html b/awx/ui/client/lib/components/list/row-item.partial.html
index ca58947b79..d504f0f928 100644
--- a/awx/ui/client/lib/components/list/row-item.partial.html
+++ b/awx/ui/client/lib/components/list/row-item.partial.html
@@ -1,5 +1,13 @@
-
+
+
@@ -41,4 +49,4 @@
{{ tag.name }}
-
\ No newline at end of file
+
diff --git a/awx/ui/client/lib/components/tabs/_index.less b/awx/ui/client/lib/components/tabs/_index.less
index 84b33c2134..e999a75613 100644
--- a/awx/ui/client/lib/components/tabs/_index.less
+++ b/awx/ui/client/lib/components/tabs/_index.less
@@ -29,4 +29,8 @@
.at-TabGroup + .at-Panel-body {
margin-top: 20px;
-}
\ No newline at end of file
+}
+
+.at-TabGroup--padBelow {
+ margin-bottom: 20px;
+}
diff --git a/awx/ui/client/lib/models/UnifiedJob.js b/awx/ui/client/lib/models/UnifiedJob.js
new file mode 100644
index 0000000000..13078f8fa2
--- /dev/null
+++ b/awx/ui/client/lib/models/UnifiedJob.js
@@ -0,0 +1,21 @@
+let Base;
+
+function UnifiedJobModel (method, resource, config) {
+ Base.call(this, 'unified_jobs');
+
+ this.Constructor = UnifiedJobModel;
+
+ return this.create(method, resource, config);
+}
+
+function UnifiedJobModelLoader (BaseModel) {
+ Base = BaseModel;
+
+ return UnifiedJobModel;
+}
+
+UnifiedJobModelLoader.$inject = [
+ 'BaseModel'
+];
+
+export default UnifiedJobModelLoader;
diff --git a/awx/ui/client/lib/models/index.js b/awx/ui/client/lib/models/index.js
index 3efc8d5299..fb902fb91c 100644
--- a/awx/ui/client/lib/models/index.js
+++ b/awx/ui/client/lib/models/index.js
@@ -23,6 +23,7 @@ import UnifiedJobTemplate from '~models/UnifiedJobTemplate';
import WorkflowJob from '~models/WorkflowJob';
import WorkflowJobTemplate from '~models/WorkflowJobTemplate';
import WorkflowJobTemplateNode from '~models/WorkflowJobTemplateNode';
+import UnifiedJob from '~models/UnifiedJob';
const MODULE_NAME = 'at.lib.models';
@@ -49,6 +50,7 @@ angular
.service('OrganizationModel', Organization)
.service('ProjectModel', Project)
.service('ScheduleModel', Schedule)
+ .service('UnifiedJobModel', UnifiedJob)
.service('UnifiedJobTemplateModel', UnifiedJobTemplate)
.service('WorkflowJobModel', WorkflowJob)
.service('WorkflowJobTemplateModel', WorkflowJobTemplate)
diff --git a/awx/ui/client/lib/theme/_variables.less b/awx/ui/client/lib/theme/_variables.less
index 2d640a0ccd..918f67342c 100644
--- a/awx/ui/client/lib/theme/_variables.less
+++ b/awx/ui/client/lib/theme/_variables.less
@@ -262,6 +262,9 @@
@at-margin-right-list-row-item-tag-icon: 8px;
@at-margin-left-list-row-item-tag-container: -10px;
@at-margin-list-row-action-mobile: 10px;
+@at-margin-right-list-row-item-status: @at-space-2x;
+@at-margin-right-list-row-item-inline: @at-space-4x;
+@at-margin-right-list-row-item-inline-label: @at-space-2x;
@at-height-divider: @at-margin-panel;
@at-height-input: 30px;
diff --git a/awx/ui/client/src/app.js b/awx/ui/client/src/app.js
index ab93141b7d..5e67b3a381 100644
--- a/awx/ui/client/src/app.js
+++ b/awx/ui/client/src/app.js
@@ -33,7 +33,6 @@ import login from './login/main';
import activityStream from './activity-stream/main';
import standardOut from './standard-out/main';
import Templates from './templates/main';
-import jobs from './jobs/main';
import teams from './teams/main';
import users from './users/main';
import projects from './projects/main';
@@ -92,7 +91,6 @@ angular
standardOut.name,
Templates.name,
portalMode.name,
- jobs.name,
teams.name,
users.name,
projects.name,
diff --git a/awx/ui/client/src/instance-groups/instance-groups.partial.html b/awx/ui/client/src/instance-groups/instance-groups.partial.html
index 217efd98d0..bdc05274b6 100644
--- a/awx/ui/client/src/instance-groups/instance-groups.partial.html
+++ b/awx/ui/client/src/instance-groups/instance-groups.partial.html
@@ -7,7 +7,9 @@
-
+
+
+
diff --git a/awx/ui/client/src/instance-groups/instance-groups.strings.js b/awx/ui/client/src/instance-groups/instance-groups.strings.js
index 79a21e075e..5ec8f411e6 100644
--- a/awx/ui/client/src/instance-groups/instance-groups.strings.js
+++ b/awx/ui/client/src/instance-groups/instance-groups.strings.js
@@ -23,8 +23,12 @@ function InstanceGroupsStrings (BaseString) {
IS_OFFLINE: t.s('Unavailable to run jobs.'),
IS_OFFLINE_LABEL: t.s('Unavailable')
};
+
+ ns.jobs = {
+ PANEL_TITLE: t.s('Jobs')
+ };
}
InstanceGroupsStrings.$inject = ['BaseStringService'];
-export default InstanceGroupsStrings;
\ No newline at end of file
+export default InstanceGroupsStrings;
diff --git a/awx/ui/client/src/instance-groups/instances/instance-jobs/instance-jobs.controller.js b/awx/ui/client/src/instance-groups/instances/instance-jobs/instance-jobs.controller.js
deleted file mode 100644
index 492c256e1d..0000000000
--- a/awx/ui/client/src/instance-groups/instances/instance-jobs/instance-jobs.controller.js
+++ /dev/null
@@ -1,89 +0,0 @@
-
-function InstanceJobsController ($scope, $filter, $state, model, strings, jobStrings, Instance) {
- const vm = this || {};
- let { instance } = model;
- const instance_id = instance.get('id');
-
- init();
-
- function init(){
- vm.strings = strings;
- vm.jobStrings = jobStrings;
- vm.queryset = { page_size: '10', order_by: '-finished'};
- vm.jobs = instance.get('related.jobs.results');
- vm.dataset = instance.get('related.jobs');
- vm.count = instance.get('related.jobs.count');
- vm.panelTitle = `${jobStrings.get('list.PANEL_TITLE')} | ${instance.get('hostname')}`;
-
- vm.tab = {
- details: {_hide: true},
- instances: {_hide: true},
- jobs: {_hide: true}
- };
- }
-
- vm.getTime = function(time) {
- let val = "";
- if (time) {
- val += $filter('longDate')(time);
- }
- if (val === "") {
- val = undefined;
- }
- return val;
- };
-
- $scope.isSuccessful = function (status) {
- return (status === "successful");
- };
-
- $scope.viewjobResults = function(job) {
- var goTojobResults = function(state) {
- $state.go(state, { id: job.id }, { reload: true });
- };
- switch (job.type) {
- case 'job':
- goTojobResults('jobResult');
- break;
- case 'ad_hoc_command':
- goTojobResults('adHocJobStdout');
- break;
- case 'system_job':
- goTojobResults('managementJobStdout');
- break;
- case 'project_update':
- goTojobResults('scmUpdateStdout');
- break;
- case 'inventory_update':
- goTojobResults('inventorySyncStdout');
- break;
- case 'workflow_job':
- goTojobResults('workflowResults');
- break;
- }
- };
-
- $scope.$on('ws-jobs', () => {
- new Instance(['get', 'options'], [instance_id, instance_id])
- .then((data) => {
- return data.extend('get', 'jobs', {params: {page_size: "10", order_by: "-finished"}});
- })
- .then((data) => {
- instance = data;
- init();
- });
- });
-
-}
-
-InstanceJobsController.$inject = [
- '$scope',
- '$filter',
- '$state',
- 'resolvedModels',
- 'InstanceGroupsStrings',
- 'JobStrings',
- 'InstanceModel'
-];
-
-export default InstanceJobsController;
\ No newline at end of file
diff --git a/awx/ui/client/src/instance-groups/jobs/instanceGroupsJobsListContainer.controller.js b/awx/ui/client/src/instance-groups/jobs/instanceGroupsJobsListContainer.controller.js
new file mode 100644
index 0000000000..62fb924efe
--- /dev/null
+++ b/awx/ui/client/src/instance-groups/jobs/instanceGroupsJobsListContainer.controller.js
@@ -0,0 +1,38 @@
+
+function InstanceGroupJobsContainerController (strings, $state) {
+ const vm = this || {};
+
+ init();
+ function init() {
+ const instanceGroupId = $state.params.instance_group_id;
+
+ vm.panelTitle = strings.get('jobs.PANEL_TITLE');
+ vm.strings = strings;
+
+ vm.tab = {
+ details: {
+ _go: 'instanceGroups.edit',
+ _params: { instance_group_id: instanceGroupId },
+ _label: strings.get('tab.DETAILS')
+ },
+ instances: {
+ _go: 'instanceGroups.instances',
+ _params: { instance_group_id: instanceGroupId },
+ _label: strings.get('tab.INSTANCES')
+ },
+ jobs: {
+ _active: true,
+ _params: { instance_group_id: instanceGroupId },
+ _label: strings.get('tab.JOBS')
+ }
+ };
+ }
+
+}
+
+InstanceGroupJobsContainerController.$inject = [
+ 'InstanceGroupsStrings',
+ '$state'
+];
+
+export default InstanceGroupJobsContainerController;
diff --git a/awx/ui/client/src/instance-groups/jobs/instanceGroupsJobsListContainer.partial.html b/awx/ui/client/src/instance-groups/jobs/instanceGroupsJobsListContainer.partial.html
new file mode 100644
index 0000000000..8b3f6e5bfd
--- /dev/null
+++ b/awx/ui/client/src/instance-groups/jobs/instanceGroupsJobsListContainer.partial.html
@@ -0,0 +1,11 @@
+
+
+ {{ vm.panelTitle }}
+
+
+ {{:: vm.strings.get('tab.DETAILS') }}
+ {{:: vm.strings.get('tab.INSTANCES') }}
+ {{:: vm.strings.get('tab.JOBS') }}
+
+
+
diff --git a/awx/ui/client/src/instance-groups/jobs/instanceJobsListContainer.controller.js b/awx/ui/client/src/instance-groups/jobs/instanceJobsListContainer.controller.js
new file mode 100644
index 0000000000..306249f842
--- /dev/null
+++ b/awx/ui/client/src/instance-groups/jobs/instanceJobsListContainer.controller.js
@@ -0,0 +1,17 @@
+
+function InstanceGroupJobsContainerController (strings) {
+ const vm = this || {};
+
+ init();
+ function init() {
+ vm.panelTitle = strings.get('jobs.PANEL_TITLE');
+ vm.strings = strings;
+ }
+
+}
+
+InstanceGroupJobsContainerController.$inject = [
+ 'InstanceGroupsStrings'
+];
+
+export default InstanceGroupJobsContainerController;
diff --git a/awx/ui/client/src/instance-groups/jobs/instanceJobsListContainer.partial.html b/awx/ui/client/src/instance-groups/jobs/instanceJobsListContainer.partial.html
new file mode 100644
index 0000000000..1104ee9548
--- /dev/null
+++ b/awx/ui/client/src/instance-groups/jobs/instanceJobsListContainer.partial.html
@@ -0,0 +1,6 @@
+
+
+ {{ vm.panelTitle }}
+
+
+
diff --git a/awx/ui/client/src/instance-groups/jobs/jobs-list.partial.html b/awx/ui/client/src/instance-groups/jobs/jobs-list.partial.html
deleted file mode 100644
index 8fdea7b2da..0000000000
--- a/awx/ui/client/src/instance-groups/jobs/jobs-list.partial.html
+++ /dev/null
@@ -1,86 +0,0 @@
-
-
- {{ vm.panelTitle }}
-
-
- {{:: vm.strings.get('tab.DETAILS') }}
- {{:: vm.strings.get('tab.INSTANCES') }}
- {{:: vm.strings.get('tab.JOBS') }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/awx/ui/client/src/instance-groups/jobs/jobs.controller.js b/awx/ui/client/src/instance-groups/jobs/jobs.controller.js
deleted file mode 100644
index 0b54a962b8..0000000000
--- a/awx/ui/client/src/instance-groups/jobs/jobs.controller.js
+++ /dev/null
@@ -1,100 +0,0 @@
-
-function InstanceGroupJobsController ($scope, $filter, $state, model, strings, jobStrings, InstanceGroup) {
- const vm = this || {};
- let { instanceGroup } = model;
- const instance_group_id = instanceGroup.get('id');
-
- init();
-
- function init(){
- vm.strings = strings;
- vm.jobStrings = jobStrings;
- vm.queryset = { page_size: '10', order_by: '-finished', instance_group_id: instance_group_id };
- vm.jobs = instanceGroup.get('related.jobs.results');
- vm.dataset = instanceGroup.get('related.jobs');
- vm.count = instanceGroup.get('related.jobs.count');
- vm.panelTitle = instanceGroup.get('name');
-
- vm.tab = {
- details: {
- _go: 'instanceGroups.edit',
- _params: { instance_group_id },
- _label: strings.get('tab.DETAILS')
- },
- instances: {
- _go: 'instanceGroups.instances',
- _params: { instance_group_id },
- _label: strings.get('tab.INSTANCES')
- },
- jobs: {
- _active: true,
- _label: strings.get('tab.JOBS')
- }
- };
- }
-
- vm.getTime = function(time) {
- let val = "";
- if (time) {
- val += $filter('longDate')(time);
- }
- if (val === "") {
- val = undefined;
- }
- return val;
- };
-
- $scope.isSuccessful = function (status) {
- return (status === "successful");
- };
-
- vm.viewjobResults = function(job) {
- var goTojobResults = function(state) {
- $state.go(state, { id: job.id }, { reload: true });
- };
- switch (job.type) {
- case 'job':
- goTojobResults('jobResult');
- break;
- case 'ad_hoc_command':
- goTojobResults('adHocJobStdout');
- break;
- case 'system_job':
- goTojobResults('managementJobStdout');
- break;
- case 'project_update':
- goTojobResults('scmUpdateStdout');
- break;
- case 'inventory_update':
- goTojobResults('inventorySyncStdout');
- break;
- case 'workflow_job':
- goTojobResults('workflowResults');
- break;
- }
- };
-
- $scope.$on('ws-jobs', () => {
- new InstanceGroup(['get', 'options'], [instance_group_id, instance_group_id])
- .then((instance_group) => {
- return instance_group.extend('get', 'jobs', {params: {page_size: "10", order_by: "-finished"}});
- })
- .then((instance_group) => {
- instanceGroup = instance_group;
- init();
- });
- });
-
-}
-
-InstanceGroupJobsController.$inject = [
- '$scope',
- '$filter',
- '$state',
- 'resolvedModels',
- 'InstanceGroupsStrings',
- 'JobStrings',
- 'InstanceGroupModel'
-];
-
-export default InstanceGroupJobsController;
\ No newline at end of file
diff --git a/awx/ui/client/src/instance-groups/jobs/jobs.list.js b/awx/ui/client/src/instance-groups/jobs/jobs.list.js
deleted file mode 100644
index 59e14ba19b..0000000000
--- a/awx/ui/client/src/instance-groups/jobs/jobs.list.js
+++ /dev/null
@@ -1,76 +0,0 @@
-export default ['i18n', function (i18n) {
- return {
- name: 'jobs',
- iterator: 'job',
- basePath: 'api/v2/instance_groups/{{$stateParams.instance_group_id}}/jobs/',
- index: false,
- hover: false,
- well: true,
- emptyListText: i18n._('No jobs have yet run.'),
- listTitle: false,
-
- fields: {
- status: {
- label: '',
- columnClass: 'col-lg-1 col-md-1 col-sm-2 col-xs-2 List-staticColumn--smallStatus',
- dataTipWatch: 'job.status_tip',
- awToolTip: "{{ job.status_tip }}",
- awTipPlacement: "right",
- dataTitle: "{{ job.status_popover_title }}",
- icon: 'icon-job-{{ job.status }}',
- iconOnly: true,
- ngClick: "viewjobResults(job)",
- nosort: true
- },
- id: {
- label: i18n._('ID'),
- ngClick: "viewjobResults(job)",
- columnClass: 'col-lg-1 col-md-1 col-sm-2 col-xs-2 List-staticColumnAdjacent',
- awToolTip: "{{ job.status_tip }}",
- dataPlacement: 'top',
- noLink: true
- },
- name: {
- label: i18n._('Name'),
- columnClass: 'col-lg-2 col-md-3 col-sm-4 col-xs-6',
- ngClick: "viewjobResults(job)",
- badgePlacement: 'right',
- badgeCustom: true,
- nosort: true,
- badgeIcon: `
-
- W
-
- `
- },
- type: {
- label: i18n._('Type'),
- ngBind: 'job.type_label',
- columnClass: "col-lg-2 hidden-md hidden-sm hidden-xs",
- nosort: true
- },
- finished: {
- label: i18n._('Finished'),
- noLink: true,
- filter: "longDate",
- columnClass: "col-lg-2 col-md-3 col-sm-3 hidden-xs",
- key: true,
- desc: true,
- nosort: true
- },
- labels: {
- label: i18n._('Labels'),
- type: 'labels',
- nosort: true,
- showDelete: false,
- columnClass: 'List-tableCell col-lg-4 col-md-4 hidden-sm hidden-xs',
- sourceModel: 'labels',
- sourceField: 'name'
- },
- }
- };
-}];
diff --git a/awx/ui/client/src/instance-groups/jobs/jobs.strings.js b/awx/ui/client/src/instance-groups/jobs/jobs.strings.js
deleted file mode 100644
index 8ecffd5a0a..0000000000
--- a/awx/ui/client/src/instance-groups/jobs/jobs.strings.js
+++ /dev/null
@@ -1,30 +0,0 @@
-function JobStrings (BaseString) {
- BaseString.call(this, 'jobs');
-
- const { t } = this;
- const ns = this.jobs;
-
- ns.state = {
- LIST_BREADCRUMB_LABEL: t.s('JOBS')
- };
-
- ns.list = {
- PANEL_TITLE: t.s('JOBS'),
- ADD_BUTTON_LABEL: t.s('ADD'),
- ADD_DD_JT_LABEL: t.s('Job Template'),
- ADD_DD_WF_LABEL: t.s('Workflow Template'),
- ROW_ITEM_LABEL_ACTIVITY: t.s('Activity'),
- ROW_ITEM_LABEL_INVENTORY: t.s('Inventory'),
- ROW_ITEM_LABEL_PROJECT: t.s('Project'),
- ROW_ITEM_LABEL_TEMPLATE: t.s('Template'),
- ROW_ITEM_LABEL_CREDENTIALS: t.s('Credentials'),
- ROW_ITEM_LABEL_MODIFIED: t.s('Last Modified'),
- ROW_ITEM_LABEL_RAN: t.s('Last Ran'),
- ROW_ITEM_LABEL_STARTED: t.s('Started'),
- ROW_ITEM_LABEL_FINISHED: t.s('Finished')
- };
-}
-
-JobStrings.$inject = ['BaseStringService'];
-
-export default JobStrings;
diff --git a/awx/ui/client/src/instance-groups/main.js b/awx/ui/client/src/instance-groups/main.js
index f241df97cb..2314aee41a 100644
--- a/awx/ui/client/src/instance-groups/main.js
+++ b/awx/ui/client/src/instance-groups/main.js
@@ -15,10 +15,6 @@ import InstanceGroupsListController from './list/instance-groups-list.controller
import InstancesTemplate from './instances/instances-list.partial.html';
import InstanceListController from './instances/instances.controller';
-import JobsTemplate from './jobs/jobs-list.partial.html';
-import InstanceGroupJobsListController from './jobs/jobs.controller';
-import InstanceJobsListController from './instances/instance-jobs/instance-jobs.controller';
-
import InstanceModalTemplate from './instances/instance-modal.partial.html';
import InstanceModalController from './instances/instance-modal.controller.js';
@@ -26,7 +22,9 @@ import list from './instance-groups.list';
import service from './instance-groups.service';
import InstanceGroupsStrings from './instance-groups.strings';
-import JobStrings from './jobs/jobs.strings';
+
+import instanceGroupJobsRoute from '~features/jobs/routes/instanceGroupJobs.route.js';
+import instanceJobsRoute from '~features/jobs/routes/instanceJobs.route.js';
const MODULE_NAME = 'instanceGroups';
@@ -255,73 +253,8 @@ function InstanceGroupsRun ($stateExtender, strings, ComponentsStrings) {
resolvedModels: InstanceGroupsResolve
});
- $stateExtender.addState({
- name: 'instanceGroups.instanceJobs',
- url: '/:instance_group_id/instances/:instance_id/jobs',
- ncyBreadcrumb: {
- parent: 'instanceGroups.instances',
- label: ComponentsStrings.get('layout.JOBS')
- },
- views: {
- 'instanceJobs@instanceGroups': {
- templateUrl: JobsTemplate,
- controller: 'InstanceJobsListController',
- controllerAs: 'vm'
- },
- },
- params: {
- job_search: {
- value: {
- page_size: '10',
- order_by: '-finished'
- },
- dynamic: true
- },
- },
- data: {
- socket: {
- "groups": {
- "jobs": ["status_changed"],
- }
- }
- },
- resolvedModels: InstanceGroupsResolve
- });
-
- $stateExtender.addState({
- name: 'instanceGroups.jobs',
- url: '/:instance_group_id/jobs',
- ncyBreadcrumb: {
- parent: 'instanceGroups.edit',
- label: ComponentsStrings.get('layout.JOBS')
- },
- params: {
- job_search: {
- value: {
- page_size: '10',
- order_by: '-finished'
- },
- dynamic: true
- }
- },
- data: {
- socket: {
- "groups": {
- "jobs": ["status_changed"],
- }
- }
- },
- views: {
- 'jobs@instanceGroups': {
- templateUrl: JobsTemplate,
- controller: 'InstanceGroupJobsListController',
- controllerAs: 'vm'
- },
- },
- resolve: {
- resolvedModels: InstanceGroupsResolve
- }
- });
+ $stateExtender.addState(instanceJobsRoute);
+ $stateExtender.addState(instanceGroupJobsRoute);
}
InstanceGroupsRun.$inject = [
@@ -334,16 +267,13 @@ angular.module(MODULE_NAME, [])
.service('InstanceGroupsService', service)
.factory('InstanceGroupList', list)
.controller('InstanceGroupsListController', InstanceGroupsListController)
- .controller('InstanceGroupJobsListController', InstanceGroupJobsListController)
.controller('InstanceListController', InstanceListController)
- .controller('InstanceJobsListController', InstanceJobsListController)
.directive('instanceListPolicy', InstanceListPolicy)
.directive('instanceGroupsMultiselect', instanceGroupsMultiselect)
.directive('instanceGroupsModal', instanceGroupsModal)
.directive('capacityAdjuster', CapacityAdjuster)
.directive('capacityBar', CapacityBar)
.service('InstanceGroupsStrings', InstanceGroupsStrings)
- .service('JobStrings', JobStrings)
.run(InstanceGroupsRun);
export default MODULE_NAME;
diff --git a/awx/ui/client/src/inventories-hosts/inventories/main.js b/awx/ui/client/src/inventories-hosts/inventories/main.js
index cf10245ffe..66ff152d23 100644
--- a/awx/ui/client/src/inventories-hosts/inventories/main.js
+++ b/awx/ui/client/src/inventories-hosts/inventories/main.js
@@ -8,12 +8,11 @@ import adhoc from './adhoc/main';
import group from './related/groups/main';
import sources from './related/sources/main';
import relatedHost from './related/hosts/main';
-import inventoryCompletedJobs from './related/completed-jobs/main';
import inventoryList from './list/main';
import InventoryList from './inventory.list';
import adHocRoute from './adhoc/adhoc.route';
import insights from './insights/main';
-import completedJobsRoute from './related/completed-jobs/completed-jobs.route';
+import completedJobsRoute from '~features/jobs/routes/inventoryCompletedJobs.route.js';
import inventorySourceEditRoute from './related/sources/edit/sources-edit.route';
import inventorySourceEditNotificationsRoute from './related/sources/edit/sources-notifications.route';
import inventorySourceAddRoute from './related/sources/add/sources-add.route';
@@ -53,7 +52,6 @@ angular.module('inventory', [
group.name,
sources.name,
relatedHost.name,
- inventoryCompletedJobs.name,
inventoryList.name,
insights.name,
SmartInventory.name,
diff --git a/awx/ui/client/src/inventories-hosts/inventories/related/completed-jobs/completed-jobs.list.js b/awx/ui/client/src/inventories-hosts/inventories/related/completed-jobs/completed-jobs.list.js
deleted file mode 100644
index 5ab048c55e..0000000000
--- a/awx/ui/client/src/inventories-hosts/inventories/related/completed-jobs/completed-jobs.list.js
+++ /dev/null
@@ -1,86 +0,0 @@
-/*************************************************
- * Copyright (c) 2015 Ansible, Inc.
- *
- * All Rights Reserved
- *************************************************/
-
-
-export default ['i18n', function(i18n) {
- return {
- // These tooltip fields are consumed to build disabled related tabs tooltips in the form > add view
- awToolTip: i18n._('Please save and run a job to view.'),
- dataPlacement: 'top',
- name: 'completed_jobs',
- basePath: 'unified_jobs',
- iterator: 'completed_job',
- search: {
- "or__job__inventory": ''
- },
- editTitle: i18n._('COMPLETED JOBS'),
- index: false,
- hover: true,
- well: true,
- emptyListText: i18n._('No completed jobs'),
-
- fields: {
- status: {
- label: '',
- columnClass: 'List-staticColumn--smallStatus',
- awToolTip: "{{ completed_job.status_tip }}",
- awTipPlacement: "right",
- dataTitle: "{{ completed_job.status_popover_title }}",
- icon: 'icon-job-{{ completed_job.status }}',
- iconOnly: true,
- uiSref: '{{completed_job.linkToDetails}}',
- nosort: true
- },
- id: {
- label: i18n._('ID'),
- uiSref: '{{completed_job.linkToDetails}}',
- columnClass: 'col-lg-1 col-md-1 col-sm-2 col-xs-2 List-staticColumnAdjacent',
- awToolTip: "{{ completed_job.status_tip }}",
- dataPlacement: 'top'
- },
- name: {
- label: i18n._('Name'),
- columnClass: 'col-lg-4 col-md-4 col-sm-4 col-xs-6',
- uiSref: '{{completed_job.linkToDetails}}',
- awToolTip: "{{ completed_job.name | sanitize }}",
- dataPlacement: 'top'
- },
- type: {
- label: i18n._('Type'),
- ngBind: 'completed_job.type_label',
- link: false,
- columnClass: "col-lg-2 col-md-2 hidden-sm hidden-xs",
- },
- finished: {
- label: i18n._('Finished'),
- noLink: true,
- filter: "longDate",
- columnClass: "col-lg-3 col-md-3 col-sm-3 hidden-xs",
- key: true,
- desc: true
- }
- },
-
- actions: { },
-
- fieldActions: {
-
- columnClass: 'col-lg-2 col-md-2 col-sm-3 col-xs-4',
-
- submit: {
- ngShow: "!completed_job.type == 'system_job' || completed_job.summary_fields.user_capabilities.start",
- // uses the at-relaunch directive
- relaunch: true
- },
- "delete": {
- mode: 'all',
- ngClick: 'deleteJob(completed_job.id)',
- awToolTip: i18n._('Delete the job'),
- dataPlacement: 'top',
- ngShow: 'completed_job.summary_fields.user_capabilities.delete'
- }
- }
- };}];
diff --git a/awx/ui/client/src/inventories-hosts/inventories/related/completed-jobs/completed-jobs.route.js b/awx/ui/client/src/inventories-hosts/inventories/related/completed-jobs/completed-jobs.route.js
deleted file mode 100644
index 9c150aa5b2..0000000000
--- a/awx/ui/client/src/inventories-hosts/inventories/related/completed-jobs/completed-jobs.route.js
+++ /dev/null
@@ -1,60 +0,0 @@
-import { N_ } from '../../../../i18n';
-
-export default {
- url: "/completed_jobs",
- params: {
- completed_job_search: {
- value: {
- page_size: '20',
- or__job__inventory:"",
- or__adhoccommand__inventory:"",
- or__inventoryupdate__inventory_source__inventory:"",
- order_by: "-id"
- },
- dynamic: true,
- squash:""
- }
- },
- ncyBreadcrumb: {
- label: N_("COMPLETED JOBS")
- },
- views: {
- 'related': {
- templateProvider: function(FormDefinition, GenerateForm) {
- let html = GenerateForm.buildCollection({
- mode: 'edit',
- related: 'completed_jobs',
- form: typeof(FormDefinition) === 'function' ?
- FormDefinition() : FormDefinition
- });
- return html;
- },
- controller: 'JobsList'
- }
- },
- resolve: {
- ListDefinition: ['InventoryCompletedJobsList', (InventoryCompletedJobsList) => {
- return InventoryCompletedJobsList;
- }],
- Dataset: ['InventoryCompletedJobsList', 'QuerySet', '$stateParams', 'GetBasePath', '$interpolate', '$rootScope',
- (list, qs, $stateParams, GetBasePath, $interpolate, $rootScope) => {
- // allow related list definitions to use interpolated $rootScope / $stateParams in basePath field
- let path, interpolator;
- if (GetBasePath(list.basePath)) {
- path = GetBasePath(list.basePath);
- } else {
- interpolator = $interpolate(list.basePath);
- path = interpolator({ $rootScope: $rootScope, $stateParams: $stateParams });
- }
-
- let inventory_id = $stateParams.inventory_id ? $stateParams.inventory_id : $stateParams.smartinventory_id;
-
- $stateParams[`${list.iterator}_search`].or__job__inventory = inventory_id;
- $stateParams[`${list.iterator}_search`].or__adhoccommand__inventory = inventory_id;
- $stateParams[`${list.iterator}_search`].or__inventoryupdate__inventory_source__inventory = inventory_id;
-
- return qs.search(path, $stateParams[`${list.iterator}_search`]);
- }
- ]
- }
-};
diff --git a/awx/ui/client/src/inventories-hosts/inventories/related/completed-jobs/main.js b/awx/ui/client/src/inventories-hosts/inventories/related/completed-jobs/main.js
deleted file mode 100644
index 7326b4c18b..0000000000
--- a/awx/ui/client/src/inventories-hosts/inventories/related/completed-jobs/main.js
+++ /dev/null
@@ -1,11 +0,0 @@
-/*************************************************
- * Copyright (c) 2017 Ansible, Inc.
- *
- * All Rights Reserved
- *************************************************/
-
-import list from './completed-jobs.list';
-
-export default
- angular.module('inventoryCompletedJobs', [])
- .factory('InventoryCompletedJobsList', list);
diff --git a/awx/ui/client/src/inventories-hosts/inventories/smart-inventory/smart-inventory.form.js b/awx/ui/client/src/inventories-hosts/inventories/smart-inventory/smart-inventory.form.js
index 08fc29dd60..46df8595b0 100644
--- a/awx/ui/client/src/inventories-hosts/inventories/smart-inventory/smart-inventory.form.js
+++ b/awx/ui/client/src/inventories-hosts/inventories/smart-inventory/smart-inventory.form.js
@@ -4,25 +4,7 @@
* All Rights Reserved
*************************************************/
-export default ['i18n', 'InventoryCompletedJobsList', function(i18n, InventoryCompletedJobsList) {
-
- var completed_jobs_object = {
- name: 'completed_jobs',
- index: false,
- basePath: "unified_jobs",
- title: i18n._('Completed Jobs'),
- iterator: 'completed_job',
- generateList: true,
- skipGenerator: true,
- search: {
- "or__job__inventory": ''
- },
- ngClick: "$state.go('inventories.editSmartInventory.completed_jobs')"
- };
-
- let clone = _.clone(InventoryCompletedJobsList);
- completed_jobs_object = angular.extend(clone, completed_jobs_object);
-
+export default ['i18n', function(i18n) {
return {
addTitle: i18n._('NEW SMART INVENTORY'),
@@ -169,7 +151,11 @@ export default ['i18n', 'InventoryCompletedJobsList', function(i18n, InventoryCo
ngClick: "$state.go('inventories.editSmartInventory.hosts');",
skipGenerator: true
},
- completed_jobs: completed_jobs_object
+ completed_jobs: {
+ title: i18n._('Completed Jobs'),
+ skipGenerator: true,
+ ngClick: "$state.go('inventories.editSmartInventory.completed_jobs')"
+ }
}
};
diff --git a/awx/ui/client/src/inventories-hosts/inventories/standard-inventory/inventory.form.js b/awx/ui/client/src/inventories-hosts/inventories/standard-inventory/inventory.form.js
index d07b53ec8e..ab8d562088 100644
--- a/awx/ui/client/src/inventories-hosts/inventories/standard-inventory/inventory.form.js
+++ b/awx/ui/client/src/inventories-hosts/inventories/standard-inventory/inventory.form.js
@@ -10,25 +10,8 @@
* @description This form is for adding/editing an inventory
*/
-export default ['i18n', 'InventoryCompletedJobsList',
-function(i18n, InventoryCompletedJobsList) {
-
- var completed_jobs_object = {
- name: 'completed_jobs',
- index: false,
- basePath: "unified_jobs",
- include: "InventoryCompletedJobsList",
- title: i18n._('Completed Jobs'),
- iterator: 'completed_job',
- generateList: true,
- skipGenerator: true,
- search: {
- "or__job__inventory": ''
- }
- };
- let clone = _.clone(InventoryCompletedJobsList);
- completed_jobs_object = angular.extend(clone, completed_jobs_object);
-
+export default ['i18n',
+function(i18n) {
return {
addTitle: i18n._('NEW INVENTORY'),
@@ -185,7 +168,10 @@ function(i18n, InventoryCompletedJobsList) {
iterator: 'inventory_source',
skipGenerator: true
},
- completed_jobs: completed_jobs_object
+ completed_jobs: {
+ title: i18n._('Completed Jobs'),
+ skipGenerator: true
+ }
},
relatedButtons: {
remediate_inventory: {
diff --git a/awx/ui/client/src/jobs/all-jobs.list.js b/awx/ui/client/src/jobs/all-jobs.list.js
deleted file mode 100644
index 08cb3b99f7..0000000000
--- a/awx/ui/client/src/jobs/all-jobs.list.js
+++ /dev/null
@@ -1,115 +0,0 @@
-/*************************************************
- * Copyright (c) 2015 Ansible, Inc.
- *
- * All Rights Reserved
- *************************************************/
-
-
-export default ['i18n', function(i18n) {
- return {
-
- name: 'jobs',
- basePath: 'unified_jobs',
- iterator: 'job',
- editTitle: i18n._('ALL JOBS'),
- index: false,
- hover: true,
- well: false,
- emptyListText: i18n._('No jobs have yet run.'),
- title: false,
-
- fields: {
- status: {
- label: '',
- columnClass: 'col-lg-1 col-md-1 col-sm-2 col-xs-2 List-staticColumn--smallStatus',
- dataTipWatch: 'job.status_tip',
- awToolTip: "{{ job.status_tip }}",
- awTipPlacement: "right",
- dataTitle: "{{ job.status_popover_title }}",
- icon: 'icon-job-{{ job.status }}',
- iconOnly: true,
- uiSref: '{{job.linkToDetails}}',
- nosort: true
- },
- id: {
- label: 'ID',
- columnClass: 'col-lg-1 col-md-1 col-sm-2 col-xs-2 List-staticColumnAdjacent',
- awToolTip: "{{ job.status_tip }}",
- dataPlacement: 'top',
- noLink: true
- },
- name: {
- label: i18n._('Name'),
- columnClass: 'col-lg-2 col-md-3 col-sm-4 col-xs-6',
- uiSref: '{{job.linkToDetails}}',
- badgePlacement: 'right',
- badgeCustom: true,
- badgeIcon: `
-
- W
-
- `
- },
- type: {
- label: i18n._('Type'),
- ngBind: 'job.type_label',
- link: false,
- columnClass: "col-lg-2 hidden-md hidden-sm hidden-xs",
- columnShow: "showJobType",
- },
- finished: {
- label: i18n._('Finished'),
- noLink: true,
- filter: "longDate",
- columnClass: "col-lg-2 col-md-3 col-sm-3 hidden-xs",
- key: true,
- desc: true
- },
- labels: {
- label: i18n._('Labels'),
- type: 'labels',
- nosort: true,
- showDelete: false,
- columnClass: 'List-tableCell col-lg-4 col-md-4 hidden-sm hidden-xs',
- sourceModel: 'labels',
- sourceField: 'name'
- },
- },
-
- actions: { },
-
- fieldActions: {
-
- columnClass: 'col-lg-2 col-md-2 col-sm-3 col-xs-4',
- "view": {
- mode: "all",
- ngClick: "viewjobResults(job)",
- awToolTip: i18n._("View the job"),
- dataPlacement: "top"
- },
- submit: {
- ngShow: "!(job.type == 'system_job') && job.summary_fields.user_capabilities.start",
- // uses the at-relaunch directive
- relaunch: true,
- },
- cancel: {
- mode: 'all',
- ngClick: 'deleteJob(job.id)',
- awToolTip: i18n._('Cancel the job'),
- dataPlacement: 'top',
- ngShow: "(job.status === 'running'|| job.status === 'waiting' || job.status === 'pending') && job.summary_fields.user_capabilities.start"
- },
- "delete": {
- mode: 'all',
- ngClick: 'deleteJob(job.id)',
- awToolTip: i18n._('Delete the job'),
- dataPlacement: 'top',
- ngShow: "(job.status !== 'running' && job.status !== 'waiting' && job.status !== 'pending') && job.summary_fields.user_capabilities.delete"
- }
- }
- };
-}];
diff --git a/awx/ui/client/src/jobs/factories/delete-job.factory.js b/awx/ui/client/src/jobs/factories/delete-job.factory.js
deleted file mode 100644
index 3900fc0da1..0000000000
--- a/awx/ui/client/src/jobs/factories/delete-job.factory.js
+++ /dev/null
@@ -1,145 +0,0 @@
- export default
- function DeleteJob($state, Find, Rest, Wait, ProcessErrors, Prompt, Alert,
- $filter, i18n) {
- return function(params) {
- var scope = params.scope,
- id = params.id,
- job = params.job,
- callback = params.callback,
- action, jobs, url, action_label, hdr;
-
- if (!job) {
- if (scope.completed_jobs) {
- jobs = scope.completed_jobs;
- }
- else if (scope.running_jobs) {
- jobs = scope.running_jobs;
- }
- else if (scope.queued_jobs) {
- jobs = scope.queued_jobs;
- }
- else if (scope.all_jobs) {
- jobs = scope.all_jobs;
- }
- else if (scope.jobs) {
- jobs = scope.jobs;
- }
- job = Find({list: jobs, key: 'id', val: id });
- }
-
- if (job.status === 'pending' || job.status === 'running' || job.status === 'waiting') {
- url = job.related.cancel;
- action_label = 'cancel';
- hdr = i18n._('Cancel');
- } else {
- url = job.url;
- action_label = 'delete';
- hdr = i18n._('Delete');
- }
-
- action = function () {
- Wait('start');
- Rest.setUrl(url);
- if (action_label === 'cancel') {
- Rest.post()
- .then(() => {
- $('#prompt-modal').modal('hide');
- if (callback) {
- scope.$emit(callback, action_label);
- }
- else {
- $state.reload();
- Wait('stop');
- }
- })
- .catch(({obj, status}) => {
- Wait('stop');
- $('#prompt-modal').modal('hide');
- if (status === 403) {
- Alert('Error', obj.detail);
- }
- // Ignore the error. The job most likely already finished.
- // ProcessErrors(scope, data, status, null, { hdr: 'Error!', msg: 'Call to ' + url +
- // ' failed. POST returned status: ' + status });
- });
- } else {
- Rest.destroy()
- .then(() => {
- $('#prompt-modal').modal('hide');
- if (callback) {
- scope.$emit(callback, action_label);
- }
- else {
- let reloadListStateParams = null;
-
- if(scope.jobs.length === 1 && $state.params.job_search && !_.isEmpty($state.params.job_search.page) && $state.params.job_search.page !== '1') {
- reloadListStateParams = _.cloneDeep($state.params);
- reloadListStateParams.job_search.page = (parseInt(reloadListStateParams.job_search.page)-1).toString();
- }
-
- $state.go('.', reloadListStateParams, {reload: true});
- Wait('stop');
- }
- })
- .catch(({obj, status}) => {
- Wait('stop');
- $('#prompt-modal').modal('hide');
- if (status === 403) {
- Alert('Error', obj.detail);
- }
- // Ignore the error. The job most likely already finished.
- //ProcessErrors(scope, data, status, null, { hdr: 'Error!', msg: 'Call to ' + url +
- // ' failed. DELETE returned status: ' + status });
- });
- }
- };
-
- if (scope.removeCancelNotAllowed) {
- scope.removeCancelNotAllowed();
- }
- scope.removeCancelNotAllowed = scope.$on('CancelNotAllowed', function() {
- Wait('stop');
- Alert('Job Completed', 'The request to cancel the job could not be submitted. The job already completed.', 'alert-info');
- });
-
- if (scope.removeCancelJob) {
- scope.removeCancelJob();
- }
- scope.removeCancelJob = scope.$on('CancelJob', function() {
- var cancelBody = "" + i18n._("Are you sure you want to submit the request to cancel this job?") + "
";
- var deleteBody = "" + i18n._("Are you sure you want to delete this job?") + "
";
- Prompt({
- hdr: hdr,
- resourceName: `#${job.id} ` + $filter('sanitize')(job.name),
- body: (action_label === 'cancel' || job.status === 'new') ? cancelBody : deleteBody,
- action: action,
- actionText: (action_label === 'cancel' || job.status === 'new') ? i18n._("OK") : i18n._("DELETE")
- });
- });
-
- if (action_label === 'cancel') {
- Rest.setUrl(url);
- Rest.get()
- .then(({data}) => {
- if (data.can_cancel) {
- scope.$emit('CancelJob');
- }
- else {
- scope.$emit('CancelNotAllowed');
- }
- })
- .catch(({data, status}) => {
- ProcessErrors(scope, data, status, null, { hdr: 'Error!', msg: 'Call to ' + url +
- ' failed. GET returned: ' + status });
- });
- }
- else {
- scope.$emit('CancelJob');
- }
- };
- }
-
-DeleteJob.$inject =
- [ '$state', 'Find', 'Rest', 'Wait',
- 'ProcessErrors', 'Prompt', 'Alert', '$filter', 'i18n'
- ];
diff --git a/awx/ui/client/src/jobs/jobs-list.controller.js b/awx/ui/client/src/jobs/jobs-list.controller.js
deleted file mode 100644
index 9707c87d87..0000000000
--- a/awx/ui/client/src/jobs/jobs-list.controller.js
+++ /dev/null
@@ -1,143 +0,0 @@
-/*************************************************
- * Copyright (c) 2015 Ansible, Inc.
- *
- * All Rights Reserved
- *************************************************/
-
-/**
- * @ngdoc function
- * @name controllers.function:Jobs
- * @description This controller's for the jobs page
- */
-
- export default ['$state', '$rootScope', '$scope', '$stateParams', 'Find', 'DeleteJob',
- 'GetBasePath', 'Dataset', 'QuerySet', 'ListDefinition', '$interpolate',
- function($state, $rootScope, $scope, $stateParams, Find, DeleteJob,
- GetBasePath, Dataset, qs, ListDefinition, $interpolate) {
-
- var list = ListDefinition;
-
- init();
-
- function init() {
- // search init
- $scope.list = list;
- $scope[`${list.iterator}_dataset`] = Dataset.data;
- $scope[list.name] = $scope[`${list.iterator}_dataset`].results;
-
- $scope.showJobType = true;
- }
-
- $scope.$on(`${list.iterator}_options`, function(event, data){
- $scope.options = data.data.actions.GET;
- optionsRequestDataProcessing();
- });
-
- $scope.$watchCollection(`${$scope.list.name}`, function() {
- optionsRequestDataProcessing();
- }
- );
-
- // iterate over the list and add fields like type label, after the
- // OPTIONS request returns, or the list is sorted/paginated/searched
- function optionsRequestDataProcessing(){
-
- if($scope[list.name] && $scope[list.name].length > 0) {
- $scope[list.name].forEach(function(item, item_idx) {
- var itm = $scope[list.name][item_idx];
-
- switch (item.type) {
- case 'job':
- item.linkToDetails = `jobResult({id: ${item.id}})`;
- break;
- case 'ad_hoc_command':
- item.linkToDetails = `adHocJobStdout({id: ${item.id}})`;
- break;
- case 'system_job':
- item.linkToDetails = `managementJobStdout({id: ${item.id}})`;
- break;
- case 'project_update':
- item.linkToDetails = `scmUpdateStdout({id: ${item.id}})`;
- break;
- case 'inventory_update':
- item.linkToDetails = `inventorySyncStdout({id: ${item.id}})`;
- break;
- case 'workflow_job':
- item.linkToDetails = `workflowResults({id: ${item.id}})`;
- break;
- }
-
- if(item.summary_fields && item.summary_fields.source_workflow_job &&
- item.summary_fields.source_workflow_job.id){
- item.workflow_result_link = `/#/workflows/${item.summary_fields.source_workflow_job.id}`;
- }
-
- // Set the item type label
- if (list.fields.type && $scope.options &&
- $scope.options.hasOwnProperty('type')) {
- $scope.options.type.choices.forEach(function(choice) {
- if (choice[0] === item.type) {
- itm.type_label = choice[1];
- }
- });
- }
- buildTooltips(itm);
- });
- }
- }
- function buildTooltips(job) {
- job.status_tip = 'Job ' + job.status + ". Click for details.";
- }
-
- $scope.deleteJob = function(id) {
- DeleteJob({ scope: $scope, id: id });
- };
-
- $scope.viewjobResults = function(job) {
-
- var goTojobResults = function(state) {
- $state.go(state, { id: job.id }, { reload: true });
- };
- switch (job.type) {
- case 'job':
- goTojobResults('jobResult');
- break;
- case 'ad_hoc_command':
- goTojobResults('adHocJobStdout');
- break;
- case 'system_job':
- goTojobResults('managementJobStdout');
- break;
- case 'project_update':
- goTojobResults('scmUpdateStdout');
- break;
- case 'inventory_update':
- goTojobResults('inventorySyncStdout');
- break;
- case 'workflow_job':
- goTojobResults('workflowResults');
- break;
- }
-
- };
-
- $scope.$on('ws-jobs', function(){
- let path;
- if (GetBasePath(list.basePath) || GetBasePath(list.name)) {
- path = GetBasePath(list.basePath) || GetBasePath(list.name);
- } else {
- // completed jobs base path involves $stateParams
- let interpolator = $interpolate(list.basePath);
- path = interpolator({ $rootScope: $rootScope, $stateParams: $stateParams });
- }
- qs.search(path, $state.params[`${list.iterator}_search`])
- .then(function(searchResponse) {
- $scope[`${list.iterator}_dataset`] = searchResponse.data;
- $scope[list.name] = $scope[`${list.iterator}_dataset`].results;
- });
- });
-
- $scope.$on('ws-schedules', function(){
- $state.reload();
- });
-}];
diff --git a/awx/ui/client/src/jobs/jobs.partial.html b/awx/ui/client/src/jobs/jobs.partial.html
deleted file mode 100644
index 46f7b09761..0000000000
--- a/awx/ui/client/src/jobs/jobs.partial.html
+++ /dev/null
@@ -1,23 +0,0 @@
-
diff --git a/awx/ui/client/src/jobs/jobs.route.js b/awx/ui/client/src/jobs/jobs.route.js
deleted file mode 100644
index 27d6631153..0000000000
--- a/awx/ui/client/src/jobs/jobs.route.js
+++ /dev/null
@@ -1,59 +0,0 @@
-/*************************************************
- * Copyright (c) 2016 Ansible, Inc.
- *
- * All Rights Reserved
- *************************************************/
-
- import { N_ } from '../i18n';
- import {templateUrl} from '../shared/template-url/template-url.factory';
-
-export default {
- searchPrefix: 'job',
- name: 'jobs',
- url: '/jobs',
- ncyBreadcrumb: {
- label: N_("JOBS")
- },
- params: {
- job_search: {
- value: {
- not__launch_type: 'sync',
- order_by: '-finished'
- },
- dynamic: true,
- squash: false
- }
- },
- data: {
- socket: {
- "groups": {
- "jobs": ["status_changed"],
- "schedules": ["changed"]
- }
- }
- },
- resolve: {
- Dataset: ['AllJobsList', 'QuerySet', '$stateParams', 'GetBasePath', (list, qs, $stateParams, GetBasePath) => {
- let path = GetBasePath(list.basePath) || GetBasePath(list.name);
- return qs.search(path, $stateParams[`${list.iterator}_search`]);
- }],
- ListDefinition: ['AllJobsList', (list) => {
- return list;
- }]
- },
- views: {
- '@': {
- templateUrl: templateUrl('jobs/jobs')
- },
- 'list@jobs': {
- templateProvider: function(AllJobsList, generateList) {
- let html = generateList.build({
- list: AllJobsList,
- mode: 'edit'
- });
- return html;
- },
- controller: 'JobsList'
- }
- }
-};
diff --git a/awx/ui/client/src/jobs/main.js b/awx/ui/client/src/jobs/main.js
deleted file mode 100644
index 7aaf97035c..0000000000
--- a/awx/ui/client/src/jobs/main.js
+++ /dev/null
@@ -1,19 +0,0 @@
-/*************************************************
- * Copyright (c) 2017 Ansible, Inc.
- *
- * All Rights Reserved
- *************************************************/
-
-import jobsList from './jobs-list.controller';
-import jobsRoute from './jobs.route';
-import DeleteJob from './factories/delete-job.factory';
-import AllJobsList from './all-jobs.list';
-
-export default
- angular.module('JobsModule', [])
- .run(['$stateExtender', function($stateExtender) {
- $stateExtender.addState(jobsRoute);
- }])
- .controller('JobsList', jobsList)
- .factory('DeleteJob', DeleteJob)
- .factory('AllJobsList', AllJobsList);
diff --git a/awx/ui/client/src/management-jobs/main.js b/awx/ui/client/src/management-jobs/main.js
index 12d25fbc6b..ef291e1ee5 100644
--- a/awx/ui/client/src/management-jobs/main.js
+++ b/awx/ui/client/src/management-jobs/main.js
@@ -6,7 +6,6 @@
import managementJobsCard from './card/main';
import managementJobsScheduler from './scheduler/main';
-import list from './management-jobs.list';
import managementJobsNotifications from './notifications/main';
export default
@@ -14,5 +13,4 @@ export default
managementJobsCard.name,
managementJobsScheduler.name,
managementJobsNotifications.name
- ])
- .factory('managementJobsListObject', list);
+ ]);
diff --git a/awx/ui/client/src/management-jobs/management-jobs.list.js b/awx/ui/client/src/management-jobs/management-jobs.list.js
deleted file mode 100644
index 7ad1af547a..0000000000
--- a/awx/ui/client/src/management-jobs/management-jobs.list.js
+++ /dev/null
@@ -1,43 +0,0 @@
-/*************************************************
- * Copyright (c) 2015 Ansible, Inc.
- *
- * All Rights Reserved
- *************************************************/
-
-export default function(){
- return {
- name: 'configure_jobs',
- iterator: 'configure_job',
- index: false,
- hover: true,
- listTitle: 'MANAGEMENT JOBS',
-
- fields: {
- name: {
- label: 'Name',
- columnClass: 'col-sm-4 col-xs-4',
- awToolTip: '{{configure_job.description | sanitize}}',
- dataPlacement: 'top'
- }
- },
- actions: {
-
- },
- fieldActions: {
- submit: {
- label: 'Launch',
- mode: 'all',
- ngClick: 'submitJob(configure_job.id, configure_job.name)',
- awToolTip: 'Start a job using this template',
- dataPlacement: 'top'
- },
- schedule: {
- label: 'Schedule',
- mode: 'all',
- ngClick: 'configureSchedule()',
- awToolTip: 'Schedule job template runs',
- dataPlacement: 'top',
- }
- }
- };
-}
diff --git a/awx/ui/client/src/portal-mode/jobs/portal-mode-all-jobs.route.js b/awx/ui/client/src/portal-mode/jobs/portal-mode-all-jobs.route.js
deleted file mode 100644
index 582f806b89..0000000000
--- a/awx/ui/client/src/portal-mode/jobs/portal-mode-all-jobs.route.js
+++ /dev/null
@@ -1,49 +0,0 @@
-import { PortalModeJobsController } from '../portal-mode-jobs.controller';
-
-// Using multiple named views requires a parent layout
-// https://github.com/angular-ui/ui-router/wiki/Multiple-Named-Views
-export default {
- name: 'portalMode.allJobs',
- url: '/alljobs?{job_search:queryset}',
- ncyBreadcrumb: {
- skip: true
- },
- params: {
- job_search: {
- value: {
- page_size: '20',
- order_by: '-finished'
- },
- dynamic: true
- }
- },
- data: {
- socket: {
- "groups": {
- "jobs": ["status_changed"]
- }
- }
- },
- views: {
- 'jobs@portalMode': {
- templateProvider: function(PortalJobsList, generateList) {
- let html = generateList.build({
- list: PortalJobsList,
- mode: 'edit'
- });
- return html;
- },
- controller: PortalModeJobsController
- }
- },
- resolve: {
- jobsDataset: ['PortalJobsList', 'QuerySet', '$rootScope', '$stateParams', 'GetBasePath',
- function(list, qs, $rootScope, $stateParams, GetBasePath) {
- let path = GetBasePath(list.basePath) || GetBasePath(list.name);
- return $rootScope.loginConfig.promise.then(() => {
- return qs.search(path, $stateParams[`${list.iterator}_search`]);
- });
- }
- ]
- }
-};
diff --git a/awx/ui/client/src/portal-mode/jobs/portal-mode-my-jobs.route.js b/awx/ui/client/src/portal-mode/jobs/portal-mode-my-jobs.route.js
deleted file mode 100644
index 9d5c0382f3..0000000000
--- a/awx/ui/client/src/portal-mode/jobs/portal-mode-my-jobs.route.js
+++ /dev/null
@@ -1,51 +0,0 @@
-import { PortalModeJobsController } from '../portal-mode-jobs.controller';
-
-// Using multiple named views requires a parent layout
-// https://github.com/angular-ui/ui-router/wiki/Multiple-Named-Views
-export default {
- name: 'portalMode.myJobs',
- url: '/myjobs?{job_search:queryset}',
- ncyBreadcrumb: {
- skip: true
- },
- params: {
- job_search: {
- value: {
- page_size: '20',
- order_by: '-finished',
- created_by: null
- },
- dynamic: true
- }
- },
- data: {
- socket: {
- "groups": {
- "jobs": ["status_changed"]
- }
- }
- },
- views: {
- 'jobs@portalMode': {
- templateProvider: function(PortalJobsList, generateList) {
- let html = generateList.build({
- list: PortalJobsList,
- mode: 'edit'
- });
- return html;
- },
- controller: PortalModeJobsController
- }
- },
- resolve: {
- jobsDataset: ['PortalJobsList', 'QuerySet', '$rootScope', '$stateParams', 'GetBasePath',
- function(list, qs, $rootScope, $stateParams, GetBasePath) {
- let path = GetBasePath(list.basePath) || GetBasePath(list.name);
- return $rootScope.loginConfig.promise.then(() => {
- $stateParams[`${list.iterator}_search`].created_by = $rootScope.current_user.id;
- return qs.search(path, $stateParams[`${list.iterator}_search`]);
- });
- }
- ]
- }
-};
diff --git a/awx/ui/client/src/portal-mode/main.js b/awx/ui/client/src/portal-mode/main.js
index a88d3f86b9..e4e1dc2a2c 100644
--- a/awx/ui/client/src/portal-mode/main.js
+++ b/awx/ui/client/src/portal-mode/main.js
@@ -5,17 +5,15 @@
*************************************************/
import route from './portal-mode.route';
-import myJobsRoute from './jobs/portal-mode-my-jobs.route';
-import allJobsRoute from './jobs/portal-mode-all-jobs.route';
-import PortalJobsList from './portal-jobs.list';
+import myJobsRoute from '~features/jobs/routes/portalModeMyJobs.route.js';
+import allJobsRoute from '~features/jobs/routes/portalModeAllJobs.route.js';
import PortalJobTemplateList from './portal-job-templates.list';
export default
angular.module('portalMode', [])
- .factory('PortalJobsList', PortalJobsList)
.factory('PortalJobTemplateList', PortalJobTemplateList)
.run(['$stateExtender', function($stateExtender){
- $stateExtender.addState(route);
- $stateExtender.addState(myJobsRoute);
- $stateExtender.addState(allJobsRoute);
+ $stateExtender.addState(route);
+ $stateExtender.addState(myJobsRoute);
+ $stateExtender.addState(allJobsRoute);
}]);
diff --git a/awx/ui/client/src/portal-mode/portal-jobs.list.js b/awx/ui/client/src/portal-mode/portal-jobs.list.js
deleted file mode 100644
index 24f9b2afe3..0000000000
--- a/awx/ui/client/src/portal-mode/portal-jobs.list.js
+++ /dev/null
@@ -1,50 +0,0 @@
-/*************************************************
- * Copyright (c) 2015 Ansible, Inc.
- *
- * All Rights Reserved
- *************************************************/
-
-
-export default ['i18n', function(i18n) {
- return {
-
- name: 'jobs',
- iterator: 'job',
- editTitle: i18n._('JOBS'),
- index: false,
- hover: true,
- well: true,
- listTitle: i18n._('JOBS'),
- emptyListText: i18n._('There are no jobs to display at this time'),
- searchBarFullWidth: true,
-
- fields: {
- status: {
- label: '',
- columnClass: 'List-staticColumn--smallStatus',
- dataTitle: "{{ job.status_popover_title }}",
- icon: 'icon-job-{{ job.status }}',
- iconOnly: true,
- nosort: true,
- awTipPlacement: "top",
- awToolTip: "{{ job.status_tip }}",
- dataTipWatch: 'job.status_tip',
- ngClick:"viewjobResults(job)",
- },
- name: {
- label: i18n._('Name'),
- columnClass: 'col-lg-4 col-md-4 col-sm-4 col-xs-6 List-staticColumnAdjacent',
- linkTo: '/#/jobs/{{job.id}}',
- },
- finished: {
- label: i18n._('Finished'),
- noLink: true,
- filter: "longDate",
- key: true,
- desc: true,
- columnClass: "col-lg-4 col-md-4 col-sm-3"
- }
- },
-
- actions: { }
- };}];
diff --git a/awx/ui/client/src/portal-mode/portal-mode-jobs.controller.js b/awx/ui/client/src/portal-mode/portal-mode-jobs.controller.js
deleted file mode 100644
index b297e126eb..0000000000
--- a/awx/ui/client/src/portal-mode/portal-mode-jobs.controller.js
+++ /dev/null
@@ -1,105 +0,0 @@
-/*************************************************
- * Copyright (c) 2016 Ansible, Inc.
- *
- * All Rights Reserved
- *************************************************/
-
-export function PortalModeJobsController($scope, $state, qs, GetBasePath, PortalJobsList, Dataset) {
-
- var list = PortalJobsList;
-
- $scope.$on('ws-jobs', function() {
- let path = GetBasePath(list.basePath) || GetBasePath(list.name);
- qs.search(path, $state.params[`${list.iterator}_search`])
- .then(function(searchResponse) {
- $scope[`${list.iterator}_dataset`] = searchResponse.data;
- $scope[list.name] = $scope[`${list.iterator}_dataset`].results;
- });
- });
-
- init();
-
- function init(data) {
- let d = (!data) ? Dataset : data;
- // search init
- $scope.list = list;
- $scope[`${list.iterator}_dataset`] = d.data;
- $scope[list.name] = $scope[`${list.iterator}_dataset`].results;
-
- $scope.iterator = list.iterator;
- }
-
- $scope.refresh = function() {
- $state.go('.', null, {reload: true});
- };
-
- $scope.$on(`${list.iterator}_options`, function(event, data){
- $scope.options = data.data.actions.GET;
- optionsRequestDataProcessing();
- });
-
- $scope.$watchCollection(`${$scope.list.name}`, function() {
- optionsRequestDataProcessing();
- }
- );
-
- // iterate over the list and add fields like type label, after the
- // OPTIONS request returns, or the list is sorted/paginated/searched
- function optionsRequestDataProcessing(){
-
- if($scope[list.name] && $scope[list.name].length > 0) {
- $scope[list.name].forEach(function(item, item_idx) {
- var itm = $scope[list.name][item_idx];
-
- if(item.summary_fields && item.summary_fields.source_workflow_job &&
- item.summary_fields.source_workflow_job.id){
- item.workflow_result_link = `/#/workflows/${item.summary_fields.source_workflow_job.id}`;
- }
-
- // Set the item type label
- if (list.fields.type && $scope.options &&
- $scope.options.hasOwnProperty('type')) {
- $scope.options.type.choices.forEach(function(choice) {
- if (choice[0] === item.type) {
- itm.type_label = choice[1];
- }
- });
- }
- buildTooltips(itm);
- });
- }
- }
- function buildTooltips(job) {
- job.status_tip = `Job ${job.status}. Click for details.`;
- }
-
- $scope.viewjobResults = function(job) {
-
- var goTojobResults = function(state) {
- $state.go(state, { id: job.id }, { reload: true });
- };
- switch (job.type) {
- case 'job':
- goTojobResults('jobResult');
- break;
- case 'ad_hoc_command':
- goTojobResults('adHocJobStdout');
- break;
- case 'system_job':
- goTojobResults('managementJobStdout');
- break;
- case 'project_update':
- goTojobResults('scmUpdateStdout');
- break;
- case 'inventory_update':
- goTojobResults('inventorySyncStdout');
- break;
- case 'workflow_job':
- goTojobResults('workflowResults');
- break;
- }
-
- };
-}
-
-PortalModeJobsController.$inject = ['$scope', '$state', 'QuerySet', 'GetBasePath', 'PortalJobsList', 'jobsDataset'];
diff --git a/awx/ui/client/src/portal-mode/portal-mode-jobs.partial.html b/awx/ui/client/src/portal-mode/portal-mode-jobs.partial.html
deleted file mode 100644
index 8543683d7c..0000000000
--- a/awx/ui/client/src/portal-mode/portal-mode-jobs.partial.html
+++ /dev/null
@@ -1,26 +0,0 @@
-
diff --git a/awx/ui/client/src/scheduler/main.js b/awx/ui/client/src/scheduler/main.js
index 7671614054..93357de409 100644
--- a/awx/ui/client/src/scheduler/main.js
+++ b/awx/ui/client/src/scheduler/main.js
@@ -349,7 +349,7 @@ export default
}]
},
views: {
- 'list@jobs': {
+ 'schedulesList@jobs': {
templateProvider: function(ScheduleList, generateList){
let html = generateList.build({
list: ScheduleList,
diff --git a/awx/ui/client/src/templates/completed-jobs.list.js b/awx/ui/client/src/templates/completed-jobs.list.js
deleted file mode 100644
index 54ba53a48e..0000000000
--- a/awx/ui/client/src/templates/completed-jobs.list.js
+++ /dev/null
@@ -1,87 +0,0 @@
-/*************************************************
- * Copyright (c) 2015 Ansible, Inc.
- *
- * All Rights Reserved
- *************************************************/
-
-
-export default ['i18n', function(i18n) {
- return {
- // These tooltip fields are consumed to build disabled related tabs tooltips in the form > add view
- awToolTip: i18n._('Please save and run a job to view.'),
- dataPlacement: 'top',
- name: 'completed_jobs',
- basePath: 'api/v2/job_templates/{{$stateParams.job_template_id}}/jobs',
- search: {
- or__status__in: "successful,failed,error,canceled",
- order_by: "-id"
- },
- iterator: 'completed_job',
- editTitle: i18n._('COMPLETED JOBS'),
- index: false,
- hover: true,
- well: false,
- emptyListText: i18n._('No completed jobs'),
-
- fields: {
- status: {
- label: '',
- columnClass: 'List-staticColumn--smallStatus',
- awToolTip: "{{ completed_job.status_tip }}",
- awTipPlacement: "right",
- dataTitle: "{{ completed_job.status_popover_title }}",
- icon: 'icon-job-{{ completed_job.status }}',
- iconOnly: true,
- uiSref: '{{completed_job.linkToDetails}}',
- nosort: true
- },
- id: {
- label: 'ID',
- uiSref: '{{completed_job.linkToDetails}}',
- columnClass: 'col-lg-1 col-md-1 col-sm-2 col-xs-2 List-staticColumnAdjacent',
- awToolTip: "{{ completed_job.status_tip }}",
- dataPlacement: 'top'
- },
- name: {
- label: i18n._('Name'),
- columnClass: 'col-lg-4 col-md-4 col-sm-4 col-xs-6',
- uiSref: '{{completed_job.linkToDetails}}',
- awToolTip: "{{ completed_job.name | sanitize }}",
- dataPlacement: 'top'
- },
- type: {
- label: i18n._('Type'),
- ngBind: 'completed_job.type_label',
- link: false,
- columnClass: "col-lg-2 col-md-2 hidden-sm hidden-xs",
- },
- finished: {
- label: i18n._('Finished'),
- noLink: true,
- filter: "longDate",
- columnClass: "col-lg-3 col-md-3 col-sm-3 hidden-xs",
- key: true,
- desc: true
- }
- },
-
- actions: { },
-
- fieldActions: {
-
- columnClass: 'col-lg-2 col-md-2 col-sm-3 col-xs-4',
-
- submit: {
- ngShow: "!completed_job.type == 'system_job' || completed_job.summary_fields.user_capabilities.start",
- // uses the at-relaunch directive
- relaunch: true
- },
- "delete": {
- mode: 'all',
- ngClick: 'deleteJob(completed_job.id)',
- awToolTip: i18n._('Delete the job'),
- dataPlacement: 'top',
- ngShow: 'completed_job.summary_fields.user_capabilities.delete'
- }
- }
- };}];
diff --git a/awx/ui/client/src/templates/job_templates/job-template.form.js b/awx/ui/client/src/templates/job_templates/job-template.form.js
index 422d722c3a..fc24852e2d 100644
--- a/awx/ui/client/src/templates/job_templates/job-template.form.js
+++ b/awx/ui/client/src/templates/job_templates/job-template.form.js
@@ -11,8 +11,8 @@
*/
-export default ['NotificationsList', 'CompletedJobsList', 'i18n',
-function(NotificationsList, CompletedJobsList, i18n) {
+export default ['NotificationsList', 'i18n',
+function(NotificationsList, i18n) {
return function() {
var JobTemplateFormObject = {
@@ -433,7 +433,9 @@ function(NotificationsList, CompletedJobsList, i18n) {
include: "NotificationsList"
},
"completed_jobs": {
- include: "CompletedJobsList"
+ title: i18n._('Completed Jobs'),
+ skipGenerator: true,
+ ngClick: "$state.go('templates.editJobTemplate.completed_jobs')"
}
},
@@ -473,11 +475,6 @@ function(NotificationsList, CompletedJobsList, i18n) {
JobTemplateFormObject.related[itm].ngClick = "$state.go('templates.editJobTemplate.notifications')";
JobTemplateFormObject.related[itm].generateList = true; // tell form generator to call list generator and inject a list
}
- if (JobTemplateFormObject.related[itm].include === "CompletedJobsList") {
- JobTemplateFormObject.related[itm] = CompletedJobsList;
- JobTemplateFormObject.related[itm].ngClick = "$state.go('templates.editJobTemplate.completed_jobs')";
- JobTemplateFormObject.related[itm].generateList = true;
- }
}
return JobTemplateFormObject;
diff --git a/awx/ui/client/src/templates/labels/labelsList.directive.js b/awx/ui/client/src/templates/labels/labelsList.directive.js
index 208c111b80..8402a14dd2 100644
--- a/awx/ui/client/src/templates/labels/labelsList.directive.js
+++ b/awx/ui/client/src/templates/labels/labelsList.directive.js
@@ -95,6 +95,11 @@ export default
if (scope.$parent.$parent.template) {
scope.labels = scope.$parent.$parent.template.summary_fields.labels.results.slice(0, 5);
scope.count = scope.$parent.$parent.template.summary_fields.labels.count;
+ } else if (scope.$parent.$parent.job) {
+ if (_.has(scope, '$parent.$parent.job.summary_fields.labels.results')) {
+ scope.labels = scope.$parent.$parent.job.summary_fields.labels.results.slice(0, 5);
+ scope.count = scope.$parent.$parent.job.summary_fields.labels.count;
+ }
} else {
scope.$watchCollection(scope.$parent.list.iterator, function() {
// To keep the array of labels fresh, we need to set up a watcher - otherwise, the
diff --git a/awx/ui/client/src/templates/main.js b/awx/ui/client/src/templates/main.js
index c2f2d0e9a6..f9280252a0 100644
--- a/awx/ui/client/src/templates/main.js
+++ b/awx/ui/client/src/templates/main.js
@@ -16,11 +16,11 @@ import workflowMaker from './workflows/workflow-maker/main';
import workflowControls from './workflows/workflow-controls/main';
import workflowService from './workflows/workflow.service';
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';
import listRoute from '~features/templates/list.route.js';
+import templateCompletedJobsRoute from '~features/jobs/routes/templateCompletedJobs.route.js';
export default
angular.module('templates', [surveyMaker.name, jobTemplates.name, labels.name, prompt.name, workflowAdd.name, workflowEdit.name,
@@ -29,7 +29,6 @@ angular.module('templates', [surveyMaker.name, jobTemplates.name, labels.name, p
.service('TemplatesService', templatesService)
.service('WorkflowService', workflowService)
.factory('WorkflowForm', WorkflowForm)
- .factory('CompletedJobsList', CompletedJobsList)
// TODO: currently being kept arround for rbac selection, templates within projects and orgs, etc.
.factory('TemplateList', TemplateList)
.value('InventorySourcesList', InventorySourcesList)
@@ -151,10 +150,7 @@ angular.module('templates', [surveyMaker.name, jobTemplates.name, labels.name, p
modes: ['edit'],
form: 'JobTemplateForm',
controllers: {
- edit: 'JobTemplateEdit',
- related: {
- completed_jobs: 'JobsList'
- }
+ edit: 'JobTemplateEdit'
},
data: {
activityStream: true,
@@ -742,6 +738,7 @@ angular.module('templates', [surveyMaker.name, jobTemplates.name, labels.name, p
return result.concat(definition.states);
}, [
stateExtender.buildDefinition(listRoute),
+ stateExtender.buildDefinition(templateCompletedJobsRoute),
stateExtender.buildDefinition(workflowMaker)
])
};
diff --git a/tower-license b/tower-license
new file mode 160000
index 0000000000..009cc73a80
--- /dev/null
+++ b/tower-license
@@ -0,0 +1 @@
+Subproject commit 009cc73a80e5123143e9c780c7011903d4c2a331