diff --git a/awx/ui/static/js/controllers/Inventories.js b/awx/ui/static/js/controllers/Inventories.js index bfa9589fb3..894d71209f 100644 --- a/awx/ui/static/js/controllers/Inventories.js +++ b/awx/ui/static/js/controllers/Inventories.js @@ -10,9 +10,9 @@ 'use strict'; -function InventoriesList($scope, $rootScope, $location, $log, $routeParams, Rest, Alert, InventoryList, GenerateList, +function InventoriesList($scope, $rootScope, $location, $log, $routeParams, $compile, $filter, Rest, Alert, InventoryList, GenerateList, LoadBreadCrumbs, Prompt, SearchInit, PaginateInit, ReturnToCaller, ClearScope, ProcessErrors, GetBasePath, Wait, Stream, - EditInventoryProperties, Find) { + EditInventoryProperties, Find, Empty, LogViewer) { //ClearScope(); @@ -22,7 +22,7 @@ function InventoriesList($scope, $rootScope, $location, $log, $routeParams, Rest paths = $location.path().replace(/^\//, '').split('/'), mode = (paths[0] === 'inventories') ? 'edit' : 'select'; - view.inject(InventoryList, { mode: mode, $scope: $scope }); + view.inject(InventoryList, { mode: mode, scope: $scope }); $rootScope.flashMessage = null; SearchInit({ @@ -82,30 +82,6 @@ function InventoriesList($scope, $rootScope, $location, $log, $routeParams, Rest LoadBreadCrumbs(); - if ($scope.removeBuildPopover) { - $scope.removeBuildPopover(); - } - $scope.removeBuildPopover = $scope.$on('BuildPopover', function(e, data) { - var inventory, html = ''; - if (data.count) { - inventory = Find({ list: $scope.inventories, key: 'id', val: data.results[0].inventory }); - html += "
| Group | Source | Last Run | Status |
|---|---|---|---|
| " + row.summary_fields.group.name + " | " + - "" + row.source + " | " + - "" + row.last_update + " | " + - "
| Status | \n"; + html += "Group | \n"; + html += "Last Sync | \n"; + html += "
|---|---|---|
| "; + html += " | " + ellipsis(row.summary_fields.group.name) + " | "; + html += "" + $filter('date')(row.last_updated,'MM/dd HH:mm:ss') + " | "; + html += "
No recent job data available for this host.
\n" + + "\n"; + } + function setMsg(host) { var j, job, jobs; + if (host.has_active_failures === true || (host.has_active_failures === false && host.last_job !== null)) { if (host.has_active_failures === true) { host.badgeToolTip = 'Most recent job failed. Click to view jobs.'; - host.active_failures = 'failed'; + host.active_failures = 'error'; } else { host.badgeToolTip = "Most recent job successful. Click to view jobs."; - host.active_failures = 'success'; + host.active_failures = 'successful'; } if (host.summary_fields.recent_jobs.length > 0) { // build html table of job status info @@ -101,7 +108,7 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H for (j=0; j < jobs.length; j++) { job = jobs[j]; html += "No recent job data available for this host.
'; - html += "\n"; + noRecentJobs(); } } else if (host.has_active_failures === false && host.last_job === null) { - host.has_active_failures = 'none'; host.badgeToolTip = "No job data available."; - host.active_failures = 'n/a'; + host.active_failures = 'none'; + noRecentJobs(); } host.job_status_html = html; host.job_status_title = title; @@ -145,7 +150,18 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H }; }]) - + +.factory('ViewJob', ['LogViewer', 'GetBasePath', function(LogViewer, GetBasePath) { + return function(params) { + var scope = params.scope, + id = params.id; + LogViewer({ + scope: scope, + url: GetBasePath('jobs') + id + '/' + }); + }; +}]) + .factory('HostsReload', [ '$routeParams', 'Empty', 'InventoryHosts', 'GetBasePath', 'SearchInit', 'PaginateInit', 'Wait', 'SetHostStatus', 'SetStatus', 'ApplyEllipsis', function($routeParams, Empty, InventoryHosts, GetBasePath, SearchInit, PaginateInit, Wait, SetHostStatus, SetStatus, @@ -252,9 +268,8 @@ function(GetBasePath, Rest, Wait, ProcessErrors, Alert, Find, SetEnabledMsg) { }); } else { - Alert('Action Not Allowed', 'This host is part of a cloud inventory. It can only be disabled in the cloud.' + - ' After disabling it, run an inventory sync to see the new status reflected here.', - 'alert-info'); + Alert('Action Not Allowed', 'This host is managed by an external cloud source. Disable it at the external source, ' + + 'and then run an inventory sync to update Tower with the new status.', 'alert-info'); } }; }]) diff --git a/awx/ui/static/js/helpers/LogViewer.js b/awx/ui/static/js/helpers/LogViewer.js index 1c3b2899f2..f7c324f5cc 100644 --- a/awx/ui/static/js/helpers/LogViewer.js +++ b/awx/ui/static/js/helpers/LogViewer.js @@ -33,6 +33,25 @@ angular.module('LogViewerHelper', ['ModalDialog', 'Utilities', 'FormGenerator', scope.removeJobReady = scope.$on('JobReady', function(e, data) { var key, resizeText, elem; + $('#status-form-container').empty(); + $('#options-form-container').empty(); + $('#stdout-form-container').empty(); + $('#traceback-form-container').empty(); + $('#variables-container').empty(); + $('#source-container').empty(); + $('#logview-tabs li:eq(1)').hide(); + $('#logview-tabs li:eq(2)').hide(); + $('#logview-tabs li:eq(4)').hide(); + $('#logview-tabs li:eq(5)').hide(); + + // Make sure subsequenct scope references don't bubble up to the parent + for (key in LogViewerStatusForm.fields) { + scope[key] = ''; + } + for (key in LogViewerOptionsForm.fields) { + scope[key] = ''; + } + for (key in data) { scope[key] = data[key]; } @@ -47,10 +66,7 @@ angular.module('LogViewerHelper', ['ModalDialog', 'Utilities', 'FormGenerator', val: data.result_stdout }); } - else { - $('#logview-tabs li:eq(1)').hide(); - } - + if (data.result_traceback) { $('#logview-tabs li:eq(2)').show(); AddPreFormattedText({ @@ -58,10 +74,7 @@ angular.module('LogViewerHelper', ['ModalDialog', 'Utilities', 'FormGenerator', val: data.result_traceback }); } - else { - $('#logview-tabs li:eq(2)').hide(); - } - + /*if (data.job_env) { EnvTable({ id: 'env-form-container', @@ -77,10 +90,7 @@ angular.module('LogViewerHelper', ['ModalDialog', 'Utilities', 'FormGenerator', val: ParseVariableString(data.extra_vars) }); } - else { - $('#logview-tabs li:eq(4)').hide(); - } - + if (data.source_vars) { $('#logview-tabs li:eq(5)').show(); AddTextarea({ @@ -89,10 +99,7 @@ angular.module('LogViewerHelper', ['ModalDialog', 'Utilities', 'FormGenerator', val: ParseVariableString(data.source_vars) }); } - else { - $('#logview-tabs li:eq(5)').hide(); - } - + if (!Empty(scope.source)) { if (scope.removeChoicesReady) { scope.removeChoicesReady(); @@ -179,6 +186,7 @@ angular.module('LogViewerHelper', ['ModalDialog', 'Utilities', 'FormGenerator', onResizeStop: resizeText, onOpen: function() { $('#logview-tabs a:first').tab('show'); + $('#dialog-ok-button').focus(); resizeText(); } }); diff --git a/awx/ui/static/js/helpers/Schedules.js b/awx/ui/static/js/helpers/Schedules.js index 3d53c4c1b2..29859e6213 100644 --- a/awx/ui/static/js/helpers/Schedules.js +++ b/awx/ui/static/js/helpers/Schedules.js @@ -342,12 +342,12 @@ angular.module('SchedulesHelper', [ 'Utilities', 'RestServices', 'SchedulesHelpe Rest.setUrl(url); Rest.get() .success(function(data) { - scope.$emit('ScheduleFound', data); - }) - .error(function(data,status){ - ProcessErrors(scope, data, status, null, { hdr: 'Error!', - msg: 'Failed to retrieve schedule ' + id + ' GET returned: ' + status }); - }); + scope.$emit('ScheduleFound', data); + }) + .error(function(data,status){ + ProcessErrors(scope, data, status, null, { hdr: 'Error!', + msg: 'Failed to retrieve schedule ' + id + ' GET returned: ' + status }); + }); }; }]) @@ -390,7 +390,12 @@ angular.module('SchedulesHelper', [ 'Utilities', 'RestServices', 'SchedulesHelpe scope.$emit(callback, id); }) .error(function (data, status) { - $('#prompt-modal').modal('hide'); + try { + $('#prompt-modal').modal('hide'); + } + catch(e) { + // ignore + } ProcessErrors(scope, data, status, null, { hdr: 'Error!', msg: 'Call to ' + url + ' failed. DELETE returned: ' + status }); }); diff --git a/awx/ui/static/js/lists/Inventories.js b/awx/ui/static/js/lists/Inventories.js index b3a50725d9..202d0253ba 100644 --- a/awx/ui/static/js/lists/Inventories.js +++ b/awx/ui/static/js/lists/Inventories.js @@ -32,14 +32,12 @@ angular.module('InventoriesListDefinition', []) icon: "{{ 'icon-cloud-' + inventory.syncStatus }}", awToolTip: "{{ inventory.syncTip }}", awTipPlacement: "top", - awPopOver: "{{ inventory.syncPopOver }}", - dataPlacement: "right" + ngClick: "showGroupSummary($event, inventory.id)" },{ icon: "{{ 'icon-job-' + inventory.hostsStatus }}", awToolTip: "{{ inventory.hostsTip }}", awTipPlacement: "top", - awPopOver: "{{ inventory.hostsPopOver }}", - dataPlacement: "right" + ngClick: "showHostSummary($event, inventory.id)" }] }, name: { @@ -106,7 +104,7 @@ angular.module('InventoriesListDefinition', []) }, "delete": { label: 'Delete', - ngClick: "deleteInventory(inventory.id, inventory.names)", + ngClick: "deleteInventory(inventory.id, inventory.name)", awToolTip: 'Delete inventory', dataPlacement: 'top' } diff --git a/awx/ui/static/js/lists/InventoryHosts.js b/awx/ui/static/js/lists/InventoryHosts.js index 2b4ef82413..7c3733313a 100644 --- a/awx/ui/static/js/lists/InventoryHosts.js +++ b/awx/ui/static/js/lists/InventoryHosts.js @@ -63,7 +63,7 @@ angular.module('InventoryHostsDefinition', []) awToolTip: "{{ host.badgeToolTip }}", awTipPlacement: 'top', dataPlacement: 'left', - iconClass: "{{ 'fa icon-failures-' + host.has_active_failures }}", + iconClass: "{{ 'fa icon-job-' + host.active_failures }}", id: 'active-failutes-action' }, edit: { diff --git a/awx/ui/static/lib/ansible/Modal.js b/awx/ui/static/lib/ansible/Modal.js index 6977905242..61d50fb1c7 100644 --- a/awx/ui/static/lib/ansible/Modal.js +++ b/awx/ui/static/lib/ansible/Modal.js @@ -137,6 +137,14 @@ angular.module('ModalDialog', ['Utilities', 'ParseHelper']) } }, open: function () { + $('.tooltip').each(function () { + // Remove any lingering tooltip