diff --git a/awx/ui/static/js/app.js b/awx/ui/static/js/app.js index 75f7912ca9..21416adfed 100644 --- a/awx/ui/static/js/app.js +++ b/awx/ui/static/js/app.js @@ -47,6 +47,8 @@ angular.module('ansible', [ 'JobSubmissionHelper', 'ProjectsListDefinition', 'ProjectFormDefinition', + 'ProjectStatusDefinition', + 'ProjectsHelper', 'PermissionFormDefinition', 'PermissionListDefinition', 'JobsListDefinition', @@ -108,6 +110,9 @@ angular.module('ansible', [ when('/projects/:project_id/organizations/add', { templateUrl: urlPrefix + 'partials/projects.html', controller: OrganizationsAdd }). + when('/hosts/:id/job_host_summaries', + { templateUrl: urlPrefix + 'partials/jobs.html', controller: JobHostSummaryList }). + when('/inventories', { templateUrl: urlPrefix + 'partials/inventories.html', controller: InventoriesList }). diff --git a/awx/ui/static/js/controllers/Hosts.js b/awx/ui/static/js/controllers/Hosts.js index 384d139a2f..0df47fd520 100644 --- a/awx/ui/static/js/controllers/Hosts.js +++ b/awx/ui/static/js/controllers/Hosts.js @@ -85,6 +85,15 @@ function InventoryHosts ($scope, $rootScope, $compile, $location, $log, $routePa $location.url('/jobs/?id__int=' + last_job ); } + scope.allJobs = function(id) { + $location.url('/jobs/?job_host_summaries__host=' + id); + } + + scope.allHostSummaries = function(id, name) { + LoadBreadCrumbs({ path: '/inventories/' + scope.inventory_id + '/hosts', title: name }); + $location.url('/hosts/' + id + '/job_host_summaries'); + } + scope.viewLastEvents = function(host_id, last_job, host_name, last_job_name) { // Choose View-> Latest job events LoadBreadCrumbs({ path: '/jobs/' + last_job, title: last_job_name }); diff --git a/awx/ui/static/js/controllers/JobHosts.js b/awx/ui/static/js/controllers/JobHosts.js index e7a28044cb..54d9230792 100644 --- a/awx/ui/static/js/controllers/JobHosts.js +++ b/awx/ui/static/js/controllers/JobHosts.js @@ -16,9 +16,9 @@ function JobHostSummaryList ($scope, $rootScope, $location, $log, $routeParams, { ClearScope('htmlTemplate'); var list = JobHostList; - var defaultUrl = GetBasePath('jobs') + $routeParams.id + '/job_host_summaries/'; - var view = GenerateList; var base = $location.path().replace(/^\//,'').split('/')[0]; + var defaultUrl = GetBasePath(base) + $routeParams.id + '/job_host_summaries/'; + var view = GenerateList; var scope = view.inject(list, { mode: 'edit' }); scope.selected = []; diff --git a/awx/ui/static/js/controllers/Jobs.js b/awx/ui/static/js/controllers/Jobs.js index 69a7535d34..9b5f7b9a5d 100644 --- a/awx/ui/static/js/controllers/Jobs.js +++ b/awx/ui/static/js/controllers/Jobs.js @@ -41,6 +41,14 @@ function JobsListCtrl ($scope, $rootScope, $location, $log, $routeParams, Rest, } }); + + if ($routeParams['job_host_summaries__host']) { + defaultUrl += '?job_host_summaries__host=' + $routeParams['job_host_summaries__host']; + } + if ($routeParams['inventory__int'] && $routeParams['status']) { + defaultUrl += '?inventory__int=' + $routeParams['inventory__int'] + '&status=' + + $routeParams['status']; + } SearchInit({ scope: scope, set: 'jobs', list: list, url: defaultUrl }); PaginateInit({ scope: scope, list: list, url: defaultUrl }); diff --git a/awx/ui/static/js/controllers/Projects.js b/awx/ui/static/js/controllers/Projects.js index 4f5fde8a09..321d3793e9 100644 --- a/awx/ui/static/js/controllers/Projects.js +++ b/awx/ui/static/js/controllers/Projects.js @@ -12,7 +12,7 @@ function ProjectsList ($scope, $rootScope, $location, $log, $routeParams, Rest, Alert, ProjectList, GenerateList, LoadBreadCrumbs, Prompt, SearchInit, PaginateInit, ReturnToCaller, - ClearScope, ProcessErrors, GetBasePath, SelectionInit, SCMUpdate) + ClearScope, ProcessErrors, GetBasePath, SelectionInit, SCMUpdate, ProjectStatus) { ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior //scope. @@ -54,6 +54,23 @@ function ProjectsList ($scope, $rootScope, $location, $log, $routeParams, Rest, scope.editProject = function(id) { $location.path($location.path() + '/' + id); } + + scope.showSCMStatus = function(id) { + var project; + for (var i=0; i < scope.projects.length; i++) { + if (scope.projects[i].id == id) { + project = scope.projects[i]; + break; + } + } + if (project.scm_type !== null) { + ProjectStatus({ project_id: id }); + } + else { + Alert('Missing SCM Configuration', 'The selected project is not configured for SCM. You must first edit the project, provide SCM settings, ' + + 'and then start an update.', 'alert-info'); + } + } scope.deleteProject = function(id, name) { @@ -95,7 +112,7 @@ function ProjectsList ($scope, $rootScope, $location, $log, $routeParams, Rest, ProjectsList.$inject = [ '$scope', '$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'ProjectList', 'GenerateList', 'LoadBreadCrumbs', 'Prompt', 'SearchInit', 'PaginateInit', 'ReturnToCaller', 'ClearScope', 'ProcessErrors', - 'GetBasePath', 'SelectionInit', 'SCMUpdate']; + 'GetBasePath', 'SelectionInit', 'SCMUpdate', 'ProjectStatus']; function ProjectsAdd ($scope, $rootScope, $compile, $location, $log, $routeParams, ProjectsForm, diff --git a/awx/ui/static/js/forms/InventoryHosts.js b/awx/ui/static/js/forms/InventoryHosts.js index 29c85b0be9..67ece1ec53 100644 --- a/awx/ui/static/js/forms/InventoryHosts.js +++ b/awx/ui/static/js/forms/InventoryHosts.js @@ -39,8 +39,8 @@ angular.module('InventoryHostsFormDefinition', []) "class": "btn-sm", //ngDisabled: 'host.last_job == null', options: [ - { ngClick: 'allJobs()', label: 'All jobs', ngShow: 'host.last_job' }, - { ngClick: 'allSummaries()', label: 'All host summaries', ngShow: 'host.last_job' }, + { ngClick: "allJobs(\{\{ host.id \}\})", label: 'All jobs', ngShow: 'host.last_job' }, + { ngClick: "allHostSummaries(\{\{ host.id \}\},'\{\{ host.name \}\}')", label: 'All host summaries', ngShow: 'host.last_job' }, { ngClick: 'viewJobs(\{\{ host.last_job \}\})', label: 'Latest job', ngShow: 'host.last_job' }, { ngClick: "viewLastEvents(\{\{ host.id \}\}, '\{\{ host.last_job \}\}', '\{\{ host.name \}\}', " + "'\{\{ host.summary_fields.last_job.name \}\}')", label: 'Latest job events', ngShow: 'host.last_job' }, diff --git a/awx/ui/static/js/forms/ProjectStatus.js b/awx/ui/static/js/forms/ProjectStatus.js new file mode 100644 index 0000000000..5a8558dd2a --- /dev/null +++ b/awx/ui/static/js/forms/ProjectStatus.js @@ -0,0 +1,44 @@ +/********************************************* + * Copyright (c) 2013 AnsibleWorks, Inc. + * + * ProjectStatus.js + * Form definition for Project Status -JSON view + * + * + */ +angular.module('ProjectStatusDefinition', []) + .value( + 'ProjectStatusForm', { + + name: 'project_update', + editTitle: 'Latest SCM Update', + well: false, + 'class': 'horizontal-narrow', + + fields: { + name: { + label: 'Name', + type: 'text', + readonly: true + }, + status: { + label: 'Status', + type: 'text', + readonly: true + }, + result_stdout: { + label: 'Std Out', + type: 'textarea', + ngShow: "result_stdout", + readonly: true, + rows: 15 + }, + result_traceback: { + label: 'Traceback', + type: 'textarea', + ngShow: "result_traceback", + readonly: true, + rows: 15 + } + } + }); //Form diff --git a/awx/ui/static/js/helpers/Projects.js b/awx/ui/static/js/helpers/Projects.js new file mode 100644 index 0000000000..d526e38193 --- /dev/null +++ b/awx/ui/static/js/helpers/Projects.js @@ -0,0 +1,67 @@ +/********************************************* + * Copyright (c) 2013 AnsibleWorks, Inc. + * + * ProjectsHelper + * + * Use GetProjectPath({ scope: , master: }) to + * load scope.project_local_paths (array of options for drop-down) and + * scope.base_dir (readonly field). + * + */ + +angular.module('ProjectsHelper', ['RestServices', 'Utilities', 'ProjectStatusDefinition']) +.factory('ProjectStatus', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'GenerateForm', + 'Prompt', 'ProcessErrors', 'GetBasePath', 'FormatDate', 'ProjectStatusForm', + function($rootScope, $location, $log, $routeParams, Rest, Alert, GenerateForm, Prompt, ProcessErrors, GetBasePath, + FormatDate, ProjectStatusForm) { + return function(params) { + + var project_id = params.project_id; + var generator = GenerateForm; + var form = ProjectStatusForm; + var scope; + var defaultUrl = GetBasePath('projects') + project_id + '/project_updates/'; + + // Retrieve detail record and prepopulate the form + Rest.setUrl(defaultUrl); + Rest.get() + .success( function(data, status, headers, config) { + // load up the form + if (data.results.length > 0) { + scope = generator.inject(form, { mode: 'edit', modal: true, related: false}); + generator.reset(); + var results = data.results[data.results.length - 1]; //get the latest + for (var fld in form.fields) { + if (results[fld]) { + scope[fld] = results[fld]; + } + else { + if (results.summary_fields.project[fld]) { + scope[fld] = results.summary_fields.project[fld] + } + } + } + scope.formModalAction = function() { + $('#form-modal').modal("hide"); + } + scope.formModalActionLabel = 'OK'; + scope.formModalCancelShow = false; + scope.formModalInfo = false; + $('#form-modal .btn-success').removeClass('btn-success').addClass('btn-none'); + $('#form-modal').addClass('skinny-modal'); + if (!scope.$$phase) { + scope.$digest(); + } + } + else { + Alert('No Updates Available', 'There is no SCM update information available for this project. An update has not yet been ' + + ' completed. If you have not already done so, start an update for this project.', 'alert-info'); + } + }) + .error( function(data, status, headers, config) { + $('#form-modal').modal("hide"); + ProcessErrors(scope, data, status, form, + { hdr: 'Error!', msg: 'Failed to retrieve status of project: ' + project_id + '. GET status: ' + status }); + }); + } + }]); \ No newline at end of file diff --git a/awx/ui/static/js/lists/Projects.js b/awx/ui/static/js/lists/Projects.js index 0c50f1466d..510b66bcd6 100644 --- a/awx/ui/static/js/lists/Projects.js +++ b/awx/ui/static/js/lists/Projects.js @@ -51,7 +51,13 @@ angular.module('ProjectsListDefinition', []) "class": 'btn-xs btn-default', awToolTip: 'View/edit project' }, - + scm_status: { + label: 'Status', + icon: 'icon-th-list', + ngClick: 'showSCMStatus(\{\{ project.id \}\})', + "class": 'btn-xs btn-default', + awToolTip: 'View SCM status' + }, scm_update: { label: 'Update', icon: 'icon-cloud-download', diff --git a/awx/ui/static/lib/ansible/form-generator.js b/awx/ui/static/lib/ansible/form-generator.js index c6dbcda1f0..b9112780a6 100644 --- a/awx/ui/static/lib/ansible/form-generator.js +++ b/awx/ui/static/lib/ansible/form-generator.js @@ -91,7 +91,8 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies']) if (options.modal) { this.scope.formModalActionDisabled = false; if (form) { - this.scope.formHeader = (options.mode == 'add') ? form.addTitle : form.editTitle; //Default title for default modal + this.scope.formModalHeader = (options.mode == 'add') ? form.addTitle : form.editTitle; //Default title for default modal + console.log('header: ' + this.scope.formHeader); } this.scope.formModalInfo = false //Disable info button for default modal if (options.modal_selector) { diff --git a/awx/ui/templates/ui/index.html b/awx/ui/templates/ui/index.html index 4cb104ff2a..3e72a172e8 100644 --- a/awx/ui/templates/ui/index.html +++ b/awx/ui/templates/ui/index.html @@ -63,6 +63,7 @@ + @@ -100,6 +101,7 @@ +