Inventory refactor: add job status summary fly-out on host status. Click on a status and a second dialog appears for the specific job. Dialog can be resized and moved- built using jquery and styled to match TB dialog. Fixed home/groups and home/hosts pages to appear more inventory edit page. Home/groups page now allows you to start and inventory sync as well. Fixed tool-tip consistency. Click page forward/back now employs the spinner and should stop overclicking, which was resulting in page numbers < 0.

This commit is contained in:
chris Houseknecht
2014-01-22 04:53:18 -05:00
parent 2213268096
commit 7269c7bd06
62 changed files with 15517 additions and 177 deletions

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 335 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 332 B

After

Width:  |  Height:  |  Size: 332 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 372 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 212 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 206 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 336 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 341 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 333 B

File diff suppressed because one or more lines are too long

View File

@@ -60,6 +60,7 @@ angular.module('ansible', [
'JobEventsListDefinition', 'JobEventsListDefinition',
'JobEventDataDefinition', 'JobEventDataDefinition',
'JobHostDefinition', 'JobHostDefinition',
'JobSummaryDefinition',
'ParseHelper', 'ParseHelper',
'ChildrenHelper', 'ChildrenHelper',
'EventsHelper', 'EventsHelper',

View File

@@ -95,7 +95,8 @@ Home.$inject=['$scope', '$compile', '$routeParams', '$rootScope', '$location', '
function HomeGroups ($location, $routeParams, HomeGroupList, GenerateList, ProcessErrors, LoadBreadCrumbs, ReturnToCaller, ClearScope, function HomeGroups ($location, $routeParams, HomeGroupList, GenerateList, ProcessErrors, LoadBreadCrumbs, ReturnToCaller, ClearScope,
GetBasePath, SearchInit, PaginateInit, FormatDate, GetHostsStatusMsg, GetSyncStatusMsg, ViewUpdateStatus, Stream, GroupsEdit) { GetBasePath, SearchInit, PaginateInit, FormatDate, GetHostsStatusMsg, GetSyncStatusMsg, ViewUpdateStatus, Stream, GroupsEdit, Wait,
Alert, Rest, Empty, InventoryUpdate, Find) {
ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior
//scope. //scope.
@@ -111,35 +112,39 @@ function HomeGroups ($location, $routeParams, HomeGroupList, GenerateList, Proce
scope.removePostRefresh(); scope.removePostRefresh();
} }
scope.removePostRefresh = scope.$on('PostRefresh', function() { scope.removePostRefresh = scope.$on('PostRefresh', function() {
var msg, update_status, last_update; var hosts_status, update_status, last_update, stat;
for (var i=0; i < scope.home_groups.length; i++) { for (var i=0; i < scope.home_groups.length; i++) {
scope['home_groups'][i]['inventory_name'] = scope['home_groups'][i]['summary_fields']['inventory']['name']; scope['home_groups'][i]['inventory_name'] = scope['home_groups'][i]['summary_fields']['inventory']['name'];
last_update = (scope.home_groups[i].summary_fields.inventory_source.last_updated == null) ? null : stat = GetSyncStatusMsg({
FormatDate(new Date(scope.home_groups[i].summary_fields.inventory_source.last_updated)); status: scope.home_groups[i].summary_fields.inventory_source.status
}); // from helpers/Groups.js
// Set values for Failed Hosts column
scope.home_groups[i].failed_hosts = scope.home_groups[i].hosts_with_active_failures + ' / ' + scope.home_groups[i].total_hosts; hosts_status = GetHostsStatusMsg({
msg = GetHostsStatusMsg({
active_failures: scope.home_groups[i].hosts_with_active_failures, active_failures: scope.home_groups[i].hosts_with_active_failures,
total_hosts: scope.home_groups[i].total_hosts, total_hosts: scope.home_groups[i].total_hosts,
inventory_id: scope.home_groups[i].inventory, inventory_id: scope.home_groups[i].inventory,
group_id: scope.home_groups[i].id group_id: scope.home_groups[i].id
}); });
scope['home_groups'][i].status_class = stat['class'],
scope['home_groups'][i].status_tooltip = stat['tooltip'],
scope['home_groups'][i].launch_tooltip = stat['launch_tip'],
scope['home_groups'][i].launch_class = stat['launch_class'],
scope['home_groups'][i].hosts_status_tip = hosts_status['tooltip'],
scope['home_groups'][i].show_failures = hosts_status['failures'],
scope['home_groups'][i].hosts_status_class = hosts_status['class'],
update_status = GetSyncStatusMsg({ status: scope.home_groups[i].summary_fields.inventory_source.status }); //scope.home_groups[i].failed_hosts_tip = msg['tooltip'];
//scope.home_groups[i].failed_hosts_link = msg['url'];
scope.home_groups[i].failed_hosts_tip = msg['tooltip']; //scope.home_groups[i].failed_hosts_class = msg['class'];
scope.home_groups[i].failed_hosts_link = msg['url']; scope.home_groups[i].status = scope.home_groups[i].summary_fields.inventory_source.status;
scope.home_groups[i].failed_hosts_class = msg['class'];
scope.home_groups[i].status = update_status['status'];
scope.home_groups[i].source = (scope.home_groups[i].summary_fields.inventory_source) ? scope.home_groups[i].source = (scope.home_groups[i].summary_fields.inventory_source) ?
scope.home_groups[i].summary_fields.inventory_source.source : null; scope.home_groups[i].summary_fields.inventory_source.source : null;
scope.home_groups[i].last_updated = last_update; //scope.home_groups[i].last_updated = last_update;
scope.home_groups[i].status_badge_class = update_status['class']; //scope.home_groups[i].status_badge_class = update_status['class'];
scope.home_groups[i].status_badge_tooltip = update_status['tooltip']; //scope.home_groups[i].status_badge_tooltip = update_status['tooltip'];
} }
}); });
@@ -224,16 +229,52 @@ function HomeGroups ($location, $routeParams, HomeGroupList, GenerateList, Proce
ViewUpdateStatus({ scope: scope, tree_id: id }) ViewUpdateStatus({ scope: scope, tree_id: id })
}; };
// Launch inventory sync
scope.updateGroup = function(id) {
var group = Find({ list: scope.home_groups, key: 'id', val: id});
if (group) {
if (Empty(group.source)) {
// if no source, do nothing.
}
else if (group.status == 'updating') {
Alert('Update in Progress', 'The inventory update process is currently running for group <em>' +
scope.home_groups[i].name + '</em>. Use the Refresh button to monitor the status.', 'alert-info');
}
else {
Wait('start');
Rest.setUrl(group.related.inventory_source);
Rest.get()
.success( function(data, status, headers, config) {
InventoryUpdate({
scope: scope,
url: data.related.update,
group_name: data.summary_fields.group.name,
group_source: data.source,
tree_id: group.id,
group_id: group.id
});
})
.error( function(data, status, headers, config) {
ProcessErrors(scope, data, status, form,
{ hdr: 'Error!', msg: 'Failed to retrieve inventory source: ' + group.related.inventory_source +
' POST returned status: ' + status });
});
}
}
}
scope.refresh = function() { scope.search(list.iterator, null, false, true); }
} }
HomeGroups.$inject = [ '$location', '$routeParams', 'HomeGroupList', 'GenerateList', 'ProcessErrors', 'LoadBreadCrumbs', 'ReturnToCaller', HomeGroups.$inject = [ '$location', '$routeParams', 'HomeGroupList', 'GenerateList', 'ProcessErrors', 'LoadBreadCrumbs', 'ReturnToCaller',
'ClearScope', 'GetBasePath', 'SearchInit', 'PaginateInit', 'FormatDate', 'GetHostsStatusMsg', 'GetSyncStatusMsg', 'ViewUpdateStatus', 'ClearScope', 'GetBasePath', 'SearchInit', 'PaginateInit', 'FormatDate', 'GetHostsStatusMsg', 'GetSyncStatusMsg', 'ViewUpdateStatus',
'Stream', 'GroupsEdit' 'Stream', 'GroupsEdit', 'Wait', 'Alert', 'Rest', 'Empty', 'InventoryUpdate', 'Find'
]; ];
function HomeHosts ($location, $routeParams, HomeHostList, GenerateList, ProcessErrors, LoadBreadCrumbs, ReturnToCaller, ClearScope, function HomeHosts ($location, $routeParams, HomeHostList, GenerateList, ProcessErrors, LoadBreadCrumbs, ReturnToCaller, ClearScope,
GetBasePath, SearchInit, PaginateInit, FormatDate, SetHostStatus, ToggleHostEnabled, HostsEdit, Stream, Find) { GetBasePath, SearchInit, PaginateInit, FormatDate, SetStatus, ToggleHostEnabled, HostsEdit, Stream, Find, ShowJobSummary) {
ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior
//scope. //scope.
@@ -251,7 +292,8 @@ function HomeHosts ($location, $routeParams, HomeHostList, GenerateList, Process
scope.removePostRefresh = scope.$on('PostRefresh', function() { scope.removePostRefresh = scope.$on('PostRefresh', function() {
for (var i=0; i < scope.hosts.length; i++) { for (var i=0; i < scope.hosts.length; i++) {
scope['hosts'][i]['inventory_name'] = scope['hosts'][i]['summary_fields']['inventory']['name']; scope['hosts'][i]['inventory_name'] = scope['hosts'][i]['summary_fields']['inventory']['name'];
SetHostStatus(scope['hosts'][i]); //SetHostStatus(scope['hosts'][i]);
SetStatus({ scope: scope, host: scope['hosts'][i] });
} }
}); });
@@ -287,6 +329,7 @@ function HomeHosts ($location, $routeParams, HomeHostList, GenerateList, Process
LoadBreadCrumbs(); LoadBreadCrumbs();
scope.showActivity = function() { Stream(); } scope.showActivity = function() { Stream(); }
scope.toggle_host_enabled = function(id, sources) { ToggleHostEnabled({ host_id: id, external_source: sources, scope: scope }); } scope.toggle_host_enabled = function(id, sources) { ToggleHostEnabled({ host_id: id, external_source: sources, scope: scope }); }
scope.editHost = function(host_id, host_name) { scope.editHost = function(host_id, host_name) {
@@ -296,9 +339,13 @@ function HomeHosts ($location, $routeParams, HomeHostList, GenerateList, Process
} }
} }
scope.showJobSummary = function(job_id) {
ShowJobSummary({ job_id: job_id });
}
} }
HomeHosts.$inject = [ '$location', '$routeParams', 'HomeHostList', 'GenerateList', 'ProcessErrors', 'LoadBreadCrumbs', 'ReturnToCaller', HomeHosts.$inject = [ '$location', '$routeParams', 'HomeHostList', 'GenerateList', 'ProcessErrors', 'LoadBreadCrumbs', 'ReturnToCaller',
'ClearScope', 'GetBasePath', 'SearchInit', 'PaginateInit', 'FormatDate', 'SetHostStatus', 'ToggleHostEnabled', 'HostsEdit', 'Stream', 'ClearScope', 'GetBasePath', 'SearchInit', 'PaginateInit', 'FormatDate', 'SetStatus', 'ToggleHostEnabled', 'HostsEdit', 'Stream',
'Find' 'Find', 'ShowJobSummary'
]; ];

View File

@@ -304,7 +304,7 @@ function InventoriesEdit ($scope, $location, $routeParams, $compile, GenerateLis
GetSyncStatusMsg, InjectHosts, HostsReload, GroupsAdd, GroupsEdit, GroupsDelete, Breadcrumbs, LoadBreadCrumbs, Empty, GetSyncStatusMsg, InjectHosts, HostsReload, GroupsAdd, GroupsEdit, GroupsDelete, Breadcrumbs, LoadBreadCrumbs, Empty,
Rest, ProcessErrors, InventoryUpdate, Alert, ToggleChildren, ViewUpdateStatus, GroupsCancelUpdate, Find, Rest, ProcessErrors, InventoryUpdate, Alert, ToggleChildren, ViewUpdateStatus, GroupsCancelUpdate, Find,
HostsCreate, EditInventoryProperties, HostsEdit, HostsDelete, ToggleHostEnabled, CopyMoveGroup, CopyMoveHost, HostsCreate, EditInventoryProperties, HostsEdit, HostsDelete, ToggleHostEnabled, CopyMoveGroup, CopyMoveHost,
Stream, GetBasePath) Stream, GetBasePath, ShowJobSummary)
{ {
ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior
//scope. //scope.
@@ -510,6 +510,10 @@ function InventoriesEdit ($scope, $location, $routeParams, $compile, GenerateLis
var url = GetBasePath('activity_stream') + '?group__inventory__id=' + $scope.inventory_id; var url = GetBasePath('activity_stream') + '?group__inventory__id=' + $scope.inventory_id;
Stream({ inventory_name: $scope.inventory_name, url: url }); Stream({ inventory_name: $scope.inventory_name, url: url });
} }
$scope.showJobSummary = function(job_id) {
ShowJobSummary({ job_id: job_id });
}
//Load tree data for the first time //Load tree data for the first time
BuildTree({ scope: $scope, inventory_id: $scope.inventory_id, refresh: false }); BuildTree({ scope: $scope, inventory_id: $scope.inventory_id, refresh: false });
@@ -520,6 +524,6 @@ InventoriesEdit.$inject = [ '$scope', '$location', '$routeParams', '$compile', '
'BuildTree', 'Wait', 'GetSyncStatusMsg', 'InjectHosts', 'HostsReload', 'GroupsAdd', 'GroupsEdit', 'GroupsDelete', 'BuildTree', 'Wait', 'GetSyncStatusMsg', 'InjectHosts', 'HostsReload', 'GroupsAdd', 'GroupsEdit', 'GroupsDelete',
'Breadcrumbs', 'LoadBreadCrumbs', 'Empty', 'Rest', 'ProcessErrors', 'InventoryUpdate', 'Alert', 'ToggleChildren', 'Breadcrumbs', 'LoadBreadCrumbs', 'Empty', 'Rest', 'ProcessErrors', 'InventoryUpdate', 'Alert', 'ToggleChildren',
'ViewUpdateStatus', 'GroupsCancelUpdate', 'Find', 'HostsCreate', 'EditInventoryProperties', 'HostsEdit', 'ViewUpdateStatus', 'GroupsCancelUpdate', 'Find', 'HostsCreate', 'EditInventoryProperties', 'HostsEdit',
'HostsDelete', 'ToggleHostEnabled', 'CopyMoveGroup', 'CopyMoveHost', 'Stream', 'GetBasePath' 'HostsDelete', 'ToggleHostEnabled', 'CopyMoveGroup', 'CopyMoveHost', 'Stream', 'GetBasePath', 'ShowJobSummary'
]; ];

View File

@@ -93,5 +93,5 @@ angular.module('HostFormDefinition', [])
} }
}); //UserForm });

View File

@@ -0,0 +1,50 @@
/*********************************************
* Copyright (c) 2014 AnsibleWorks, Inc.
*
* JobSummary.js
*
* Display job status info in a dialog
*
*/
angular.module('JobSummaryDefinition', [])
.value(
'JobSummary', {
editTitle: '{{ id }} - {{ name }}',
name: 'jobs',
well: false,
fields: {
status: {
//label: 'Job Status',
type: 'custom',
control: '<div class=\"job-detail-status\"><span style="padding-right: 15px; font-weight: bold;">Status</span> ' +
'<i class=\"fa icon-job-\{\{ status \}\}\"></i> \{\{ status \}\}</div>',
readonly: true
},
created: {
label: 'Created On',
type: 'text',
readonly: true
},
result_stdout: {
label: 'Standard Out',
type: 'textarea',
readonly: true,
xtraWide: true,
rows: "\{\{ stdout_rows \}\}",
"class": 'nowrap mono-space resizable',
ngShow: "result_stdout != ''"
},
result_traceback: {
label: 'Traceback',
type: 'textarea',
xtraWide: true,
readonly: true,
rows: "\{\{ traceback_rows \}\}",
"class": 'nowrap mono-space resizable',
ngShow: "result_traceback != ''"
}
}
});

View File

@@ -316,7 +316,7 @@ angular.module('JobFormDefinition', [])
readonly: true, readonly: true,
xtraWide: true, xtraWide: true,
rows: "\{\{ stdout_rows \}\}", rows: "\{\{ stdout_rows \}\}",
"class": 'nowrap', "class": 'nowrap mono-space',
ngShow: "result_stdout != ''" ngShow: "result_stdout != ''"
}, },
result_traceback: { result_traceback: {
@@ -325,7 +325,7 @@ angular.module('JobFormDefinition', [])
xtraWide: true, xtraWide: true,
readonly: true, readonly: true,
rows: "\{\{ traceback_rows \}\}", rows: "\{\{ traceback_rows \}\}",
"class": 'nowrap', "class": 'nowrap mono-space',
ngShow: "result_traceback != ''" ngShow: "result_traceback != ''"
} }
}, },

View File

@@ -51,9 +51,85 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H
} }
}]) }])
.factory('SetStatus', ['SetEnabledMsg', 'Empty', function(SetEnabledMsg, Empty) {
return function(params) {
var scope = params.scope;
var host = params.host;
var html, title;
function setMsg(host) {
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';
}
else {
host.badgeToolTip = "Most recent job successful. Click to view jobs.";
host.active_failures = 'success';
}
if (host.summary_fields.recent_jobs.length > 0) {
// build html table of job status info
var jobs = host.summary_fields.recent_jobs.sort(
function(a,b) {
// reverse numerical order
return -1 * (a - b);
});
title = "Recent Jobs";
html = "<table class=\"table table-condensed\">\n";
html += "<thead>\n";
html += "<tr>\n";
html += "<th>ID</td>\n";
html += "<th>Status</td>\n";
html += "<th>Name</td>\n";
html += "</tr>\n";
html += "</thead>\n";
html += "<tbody>\n";
for (var j=0; j < jobs.length; j++) {
var job = jobs[j];
html += "<tr>\n";
html += "<td><a href=\"/#/jobs/" + job.id + "\">" + job.id + "</a></td>\n";
html += "<td><a ng-click=\"showJobSummary(" + job.id + ")\"><i class=\"fa icon-job-" + job.status + "\"></i> " + job.status + "</a></td>\n";
html += "<td>" + job.name + "</td>\n";
html += "</tr>\n";
}
html += "</tbody>\n";
html += "</table>\n";
}
else {
title = 'No job data';
html = '<p>No recent job data available for this host.</p>';
}
}
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.job_status_html = html;
host.job_status_title = title;
}
if (!Empty(host)) {
// update single host
setMsg(host);
SetEnabledMsg(host);
}
else {
// update all hosts
for (var i=0; i < scope.hosts.length; i++) {
setMsg(scope.hosts[i]);
SetEnabledMsg(scope.hosts[i]);
}
}
}
}])
.factory('HostsReload', [ '$routeParams', 'Empty', 'InventoryHosts', 'GetBasePath', 'SearchInit', 'PaginateInit', 'Wait', 'SetHostStatus', .factory('HostsReload', [ '$routeParams', 'Empty', 'InventoryHosts', 'GetBasePath', 'SearchInit', 'PaginateInit', 'Wait',
function($routeParams, Empty, InventoryHosts, GetBasePath, SearchInit, PaginateInit, Wait, SetHostStatus) { 'SetHostStatus', 'SetStatus',
function($routeParams, Empty, InventoryHosts, GetBasePath, SearchInit, PaginateInit, Wait, SetHostStatus, SetStatus) {
return function(params) { return function(params) {
var scope = params.scope; var scope = params.scope;
@@ -74,9 +150,9 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H
for (var i=0; i < scope.hosts.length; i++) { for (var i=0; i < scope.hosts.length; i++) {
//Set tooltip for host enabled flag //Set tooltip for host enabled flag
scope.hosts[i].enabled_flag = scope.hosts[i].enabled; scope.hosts[i].enabled_flag = scope.hosts[i].enabled;
//SetEnabledMsg(scope.hosts[i]); //SetHostStatus(scope.hosts[i]);
SetHostStatus(scope.hosts[i]);
} }
SetStatus({ scope: scope });
Wait('stop'); Wait('stop');
scope.$emit('HostReloadComplete'); scope.$emit('HostReloadComplete');
}); });
@@ -347,9 +423,9 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H
.factory('HostsEdit', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'HostForm', 'GenerateForm', .factory('HostsEdit', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'HostForm', 'GenerateForm',
'Prompt', 'ProcessErrors', 'GetBasePath', 'HostsReload', 'ParseTypeChange', 'Wait', 'Find', 'SetEnabledMsg', 'Prompt', 'ProcessErrors', 'GetBasePath', 'HostsReload', 'ParseTypeChange', 'Wait', 'Find', 'SetStatus',
function($rootScope, $location, $log, $routeParams, Rest, Alert, HostForm, GenerateForm, Prompt, ProcessErrors, function($rootScope, $location, $log, $routeParams, Rest, Alert, HostForm, GenerateForm, Prompt, ProcessErrors,
GetBasePath, HostsReload, ParseTypeChange, Wait, Find, SetEnabledMsg) { GetBasePath, HostsReload, ParseTypeChange, Wait, Find, SetStatus) {
return function(params) { return function(params) {
var parent_scope = params.scope; var parent_scope = params.scope;
@@ -371,7 +447,6 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H
scope.parseType = 'yaml'; scope.parseType = 'yaml';
ParseTypeChange(scope); ParseTypeChange(scope);
$('#form-modal .btn-none').removeClass('btn-none').addClass('btn-success');
if (scope.hostLoadedRemove) { if (scope.hostLoadedRemove) {
scope.hostLoadedRemove(); scope.hostLoadedRemove();
@@ -441,7 +516,7 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H
host.name = scope.name; host.name = scope.name;
host.enabled = scope.enabled; host.enabled = scope.enabled;
host.enabled_flag = scope.enabled; host.enabled_flag = scope.enabled;
SetEnabledMsg(host); SetStatus({ scope: parent_scope, host: host });
// Close modal // Close modal
Wait('stop'); Wait('stop');
$('#form-modal').modal('hide'); $('#form-modal').modal('hide');

View File

@@ -423,7 +423,9 @@ angular.module('JobSubmissionHelper', [ 'RestServices', 'Utilities', 'Credential
Wait('stop'); Wait('stop');
Alert('Update Started', 'Your request to start the inventory sync process was submitted. Monitor progress ' + Alert('Update Started', 'Your request to start the inventory sync process was submitted. Monitor progress ' +
'by clicking the <i class="fa fa-refresh fa-lg"></i> button.', 'alert-info'); 'by clicking the <i class="fa fa-refresh fa-lg"></i> button.', 'alert-info');
scope.removeHostReloadComplete(); if (scope.removeHostReloadComplete) {
scope.removeHostReloadComplete();
}
}); });
if (scope.removeUpdateSubmitted) { if (scope.removeUpdateSubmitted) {
@@ -431,10 +433,14 @@ angular.module('JobSubmissionHelper', [ 'RestServices', 'Utilities', 'Credential
} }
scope.removeUpdateSubmitted = scope.$on('UpdateSubmitted', function(e, action) { scope.removeUpdateSubmitted = scope.$on('UpdateSubmitted', function(e, action) {
if (action == 'started') { if (action == 'started') {
// Cancel the update process if (scope.refreshGroups) {
scope.selected_tree_id = tree_id; scope.selected_tree_id = tree_id;
scope.selected_group_id = group_id; scope.selected_group_id = group_id;
scope.refreshGroups(); scope.refreshGroups();
}
else {
scope.$emit('HostReloadComplete');
}
} }
}); });

View File

@@ -7,7 +7,8 @@
* *
*/ */
angular.module('JobsHelper', [ ]) angular.module('JobsHelper', ['Utilities', 'FormGenerator', 'JobSummaryDefinition'])
.factory('JobStatusToolTip', [ function() { .factory('JobStatusToolTip', [ function() {
return function(status) { return function(status) {
var toolTip; var toolTip;
@@ -37,4 +38,97 @@ angular.module('JobsHelper', [ ])
} }
return toolTip; return toolTip;
} }
}]); }])
.factory('ShowJobSummary', ['Rest', 'Wait', 'GetBasePath', 'FormatDate', 'ProcessErrors', 'GenerateForm', 'JobSummary',
function(Rest, Wait, GetBasePath, FormatDate, ProcessErrors, GenerateForm, JobSummary) {
return function(params) {
// Display status info in a modal dialog- called from inventory edit page
var job_id = params.job_id;
var generator = GenerateForm;
var form = JobSummary;
// Using jquery dialog for its expandable property
var html = "<div id=\"status-modal-dialog\" title=\"Job " + job_id + "\"><div id=\"form-container\" style=\"width: 100%;\"></div></div>\n";
$('#inventory-modal-container').empty().append(html);
var scope = generator.inject(form, { mode: 'edit', id: 'form-container', breadCrumbs: false, related: false });
// Set modal dimensions based on viewport width
var ww = $(document).width();
var wh = $('body').height();
var x, y, maxrows;
if (ww > 1199) {
// desktop
x = 675;
y = (750 > wh) ? wh - 20 : 750;
maxrows = 20;
}
else if (ww <= 1199 && ww >= 768) {
x = 550;
y = (620 > wh) ? wh - 15 : 620;
maxrows = 15;
}
else {
x = (ww - 20);
y = (500 > wh) ? wh : 500;
maxrows = 10;
}
// Create the modal
$('#status-modal-dialog').dialog({
buttons: { "OK": function() { $( this ).dialog( "close" ); } },
modal: true,
width: x,
height: y,
autoOpen: false,
create: function (e, ui) {
// fix the close button
$('.ui-dialog[aria-describedby="status-modal-dialog"]').find('.ui-dialog-titlebar button').empty().attr({ 'class': 'close' }).text('x');
// fix the OK button
$('.ui-dialog[aria-describedby="status-modal-dialog"]').find('.ui-dialog-buttonset button:first')
.attr({ 'class': 'btn btn-primary' });
},
resizeStop: function(e, ui) {
// for some reason, after resizing dialog the form and fields (the content) doesn't expand to 100%
var dialog = $('.ui-dialog[aria-describedby="status-modal-dialog"]');
var content = dialog.find('#status-modal-dialog');
content.width( dialog.width() - 28 );
},
close: function(e, ui) {
// Destroy on close
$('#status-modal-dialog').dialog('destroy');
$('#inventory-modal-container').empty();
}
});
function calcRows (content) {
var n = content.match(/\n/g);
var rows = (n) ? n.length : 1;
return (rows > maxrows) ? 20 : rows;
}
Wait('start');
var url = GetBasePath('jobs') + job_id + '/';
Rest.setUrl(url);
Rest.get()
.success( function(data, status, headers, config) {
scope.id = data.id;
scope.name = data.name;
scope.status = data.status;
scope.result_stdout = data.result_stdout;
scope.result_traceback = data.result_traceback;
scope['stdout_rows'] = calcRows(scope['result_stdout']);
scope['traceback_rows'] = calcRows(scope['result_traceback']);
var cDate = new Date(data.created);
scope.created = FormatDate(cDate);
Wait('stop');
$('#status-modal-dialog').dialog('open');
})
.error( function(data, status, headers, config) {
ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Attempt to load job failed. GET returned status: ' + status });
});
}
}]);

View File

@@ -14,8 +14,8 @@
* *
*/ */
angular.module('PaginateHelper', ['RefreshHelper', 'ngCookies']) angular.module('PaginateHelper', ['RefreshHelper', 'ngCookies', 'Utilities'])
.factory('PaginateInit', [ 'Refresh', '$cookieStore', function(Refresh, $cookieStore) { .factory('PaginateInit', [ 'Refresh', '$cookieStore', 'Wait', function(Refresh, $cookieStore, Wait) {
return function(params) { return function(params) {
var scope = params.scope; var scope = params.scope;
@@ -51,6 +51,7 @@ angular.module('PaginateHelper', ['RefreshHelper', 'ngCookies'])
scope.nextSet = function(set, iterator) { scope.nextSet = function(set, iterator) {
if (scope[iterator + 'NextUrl']) { if (scope[iterator + 'NextUrl']) {
scope[iterator + 'Page']++; scope[iterator + 'Page']++;
Wait('start');
Refresh({ scope: scope, set: set, iterator: iterator, url: scope[iterator + 'NextUrl'] }); Refresh({ scope: scope, set: set, iterator: iterator, url: scope[iterator + 'NextUrl'] });
} }
}; };
@@ -58,6 +59,7 @@ angular.module('PaginateHelper', ['RefreshHelper', 'ngCookies'])
scope.prevSet = function(set, iterator) { scope.prevSet = function(set, iterator) {
if (scope[iterator + 'PrevUrl']) { if (scope[iterator + 'PrevUrl']) {
scope[iterator + 'Page']--; scope[iterator + 'Page']--;
Wait('start');
Refresh({ scope: scope, set: set, iterator: iterator, url: scope[iterator + 'PrevUrl'] }); Refresh({ scope: scope, set: set, iterator: iterator, url: scope[iterator + 'PrevUrl'] });
} }
}; };
@@ -70,11 +72,10 @@ angular.module('PaginateHelper', ['RefreshHelper', 'ngCookies'])
scope[iterator + 'Page'] = 0; scope[iterator + 'Page'] = 0;
var new_url = url.replace(/\?page_size\=\d+/,''); var new_url = url.replace(/\?page_size\=\d+/,'');
console.log('new_url: ' + new_url);
var connect = (/\/$/.test(new_url)) ? '?' : '&'; var connect = (/\/$/.test(new_url)) ? '?' : '&';
new_url += (scope[iterator + 'SearchParams']) ? connect + scope[iterator + 'SearchParams'] + '&page_size=' + scope[iterator + 'PageSize' ] : new_url += (scope[iterator + 'SearchParams']) ? connect + scope[iterator + 'SearchParams'] + '&page_size=' + scope[iterator + 'PageSize' ] :
connect + 'page_size=' + scope[iterator + 'PageSize' ]; connect + 'page_size=' + scope[iterator + 'PageSize' ];
console.log('new_url: ' + new_url); Wait('start');
Refresh({ scope: scope, set: set, iterator: iterator, url: new_url }); Refresh({ scope: scope, set: set, iterator: iterator, url: new_url });
} }
} }

View File

@@ -41,7 +41,6 @@ angular.module('RefreshRelatedHelper', ['RestServices', 'Utilities'])
} }
}) })
.error ( function(data, status, headers, config) { .error ( function(data, status, headers, config) {
Wait('stop');
//scope[iterator + 'SearchSpin'] = true; //scope[iterator + 'SearchSpin'] = true;
ProcessErrors(scope, data, status, null, ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Failed to retrieve ' + set + '. GET returned status: ' + status }); { hdr: 'Error!', msg: 'Failed to retrieve ' + set + '. GET returned status: ' + status });

View File

@@ -41,7 +41,6 @@ angular.module('RefreshHelper', ['RestServices', 'Utilities'])
scope.$emit('PostRefresh'); scope.$emit('PostRefresh');
}) })
.error ( function(data, status, headers, config) { .error ( function(data, status, headers, config) {
Wait('stop');
//scope[iterator + 'SearchSpin'] = false; //scope[iterator + 'SearchSpin'] = false;
scope[iterator + 'HoldInput'] = false; scope[iterator + 'HoldInput'] = false;
ProcessErrors(scope, data, status, null, ProcessErrors(scope, data, status, null,

View File

@@ -33,7 +33,7 @@ angular.module('HomeGroupListDefinition', [])
columnClass: 'col-lg-3 col-md3 col-sm-2', columnClass: 'col-lg-3 col-md3 col-sm-2',
linkTo: "\{\{ '/#/inventories/' + group.inventory + '/' \}\}" linkTo: "\{\{ '/#/inventories/' + group.inventory + '/' \}\}"
}, },
failed_hosts: { /*failed_hosts: {
label: 'Failed Hosts', label: 'Failed Hosts',
ngHref: "\{\{ group.failed_hosts_link \}\}", ngHref: "\{\{ group.failed_hosts_link \}\}",
badgeIcon: "\{\{ 'fa icon-failures-' + group.failed_hosts_class \}\}", badgeIcon: "\{\{ 'fa icon-failures-' + group.failed_hosts_class \}\}",
@@ -46,8 +46,8 @@ angular.module('HomeGroupListDefinition', [])
searchable: false, searchable: false,
excludeModal: true, excludeModal: true,
sortField: "hosts_with_active_failures" sortField: "hosts_with_active_failures"
}, },*/
status: { /*status: {
label: 'Status', label: 'Status',
ngClick: "viewUpdateStatus(\{\{ group.id \}\})", ngClick: "viewUpdateStatus(\{\{ group.id \}\})",
searchType: 'select', searchType: 'select',
@@ -65,14 +65,14 @@ angular.module('HomeGroupListDefinition', [])
{ name: "updating", value: "updating" }], { name: "updating", value: "updating" }],
sourceModel: 'inventory_source', sourceModel: 'inventory_source',
sourceField: 'status' sourceField: 'status'
}, },*/
last_updated: { /*last_updated: {
label: 'Last<br>Updated', label: 'Last<br>Updated',
sourceModel: 'inventory_source', sourceModel: 'inventory_source',
sourceField: 'last_updated', sourceField: 'last_updated',
searchable: false, searchable: false,
nosort: false nosort: false
}, },*/
source: { source: {
label: 'Source', label: 'Source',
searchType: 'select', searchType: 'select',
@@ -113,8 +113,55 @@ angular.module('HomeGroupListDefinition', [])
searchOnly: true searchOnly: true
} }
}, },
fieldActions: {
sync_status: {
mode: 'all',
ngClick: "viewUpdateStatus(group.id, group.group_id)",
awToolTip: "\{\{ group.status_tooltip \}\}",
ngClass: "group.status_class",
dataPlacement: "top"
},
failed_hosts: {
mode: 'all',
awToolTip: "{{ group.hosts_status_tip }}",
dataPlacement: "top",
ngHref: "/#/inventories/{{ group.inventory }}/",
iconClass: "{{ 'fa icon-failures-' + group.hosts_status_class }}"
},
group_update: {
//label: 'Sync',
mode: 'all',
ngClick: 'updateGroup(\{\{ group.id \}\})',
awToolTip: "\{\{ group.launch_tooltip \}\}",
ngShow: "(group.status !== 'running' && group.status !== 'pending' && group.status !== 'updating')",
ngClass: "group.launch_class",
dataPlacement: "top"
},
cancel: {
//label: 'Cancel',
mode: 'all',
ngClick: "cancelUpdate(\{\{ group.id \}\})",
awToolTip: "Cancel sync process",
'class': 'red-txt',
ngShow: "(group.status == 'running' || group.status == 'pending' || group.status == 'updating')",
dataPlacement: "top"
},
edit: {
label: 'Edit',
mode: 'all',
ngClick: "editGroup(group.id)",
awToolTip: 'Edit group',
dataPlacement: "top"
}
},
actions: { actions: {
refresh: {
mode: 'all',
awToolTip: "Refresh the page",
ngClick: "refresh()"
},
stream: { stream: {
ngClick: "showActivity()", ngClick: "showActivity()",
awToolTip: "View Activity Stream", awToolTip: "View Activity Stream",

View File

@@ -33,30 +33,6 @@ angular.module('HomeHostListDefinition', [])
columnClass: 'col-lg-3 col-md3 col-sm-2', columnClass: 'col-lg-3 col-md3 col-sm-2',
linkTo: "\{\{ '/#/inventories/' + host.inventory \}\}" linkTo: "\{\{ '/#/inventories/' + host.inventory \}\}"
}, },
active_failures: {
label: 'Job Status',
ngHref: "\{\{ host.activeFailuresLink \}\}",
awToolTip: "\{\{ host.badgeToolTip \}\}",
dataPlacement: 'top',
badgeNgHref: '\{\{ host.activeFailuresLink \}\}',
badgeIcon: "\{\{ 'fa icon-failures-' + host.has_active_failures \}\}",
badgePlacement: 'left',
badgeToolTip: "\{\{ host.badgeToolTip \}\}",
badgeTipPlacement: 'top',
searchable: false,
nosort: true
},
enabled_flag: {
label: 'Enabled',
badgeIcon: "\{\{ 'fa icon-enabled-' + host.enabled \}\}",
badgePlacement: 'left',
badgeToolTip: "\{\{ host.enabledToolTip \}\}",
badgeTipPlacement: "top",
badgeTipWatch: "host.enabledToolTip",
ngClick: "toggle_host_enabled(\{\{ host.id \}\}, \{\{ host.has_inventory_sources \}\})",
searchable: false,
showValue: false
},
enabled: { enabled: {
label: 'Disabled?', label: 'Disabled?',
searchSingleValue: true, searchSingleValue: true,
@@ -83,7 +59,35 @@ angular.module('HomeHostListDefinition', [])
searchOnly: true searchOnly: true
} }
}, },
fieldActions: {
enabled_flag: {
//label: 'Enabled',
iconClass: "{{ 'fa icon-enabled-' + host.enabled }}",
dataPlacement: 'top',
awToolTip: "{{ host.enabledToolTip }}",
dataTipWatch: "host.enabledToolTip",
ngClick: "toggleHostEnabled(host.id, host.has_inventory_sources)"
},
active_failures: {
//label: 'Job Status',
//ngHref: "\{\{'/#/hosts/' + host.id + '/job_host_summaries/?inventory=' + inventory_id \}\}",
awPopOver: "{{ host.job_status_html }}",
dataTitle: "{{ host.job_status_title }}",
awToolTip: "{{ host.badgeToolTip }}",
awTipPlacement: 'top',
dataPlacement: 'left',
iconClass: "{{ 'fa icon-failures-' + host.has_active_failures }}"
},
edit: {
label: 'Edit',
ngClick: "editHost(host.id)",
icon: 'icon-edit',
awToolTip: 'Edit host',
dataPlacement: 'top'
}
},
actions: { actions: {
stream: { stream: {
ngClick: "showActivity()", ngClick: "showActivity()",

View File

@@ -4,7 +4,6 @@
* Inventories.js * Inventories.js
* List view object for Inventories data model. * List view object for Inventories data model.
* *
*
*/ */
angular.module('InventoriesListDefinition', []) angular.module('InventoriesListDefinition', [])
.value( .value(
@@ -31,50 +30,22 @@ angular.module('InventoriesListDefinition', [])
sourceField: 'name', sourceField: 'name',
excludeModal: true excludeModal: true
}, },
/*failed_hosts: {
label: 'Failures',
ngHref: "\{\{ inventory.failed_hosts_link \}\}",
badgeIcon: "\{\{ 'fa icon-failures-' + inventory.failed_hosts_class \}\}",
badgeNgHref: "\{\{ inventory.failed_hosts_link \}\}",
badgePlacement: 'left',
badgeToolTip: "\{\{ inventory.failed_hosts_tip \}\}",
badgeTipPlacement: 'top',
awToolTip: "\{\{ inventory.failed_hosts_tip \}\}",
dataPlacement: "top",
searchable: false,
excludeModal: true,
sortField: "hosts_with_active_failures"
},
status: {
label: 'Status',
ngHref: "\{\{ inventory.status_link \}\}",
badgeIcon: "\{\{ 'fa fa-cloud icon-cloud-' + inventory.status_class \}\}",
badgeNgHref: "\{\{ inventory.status_link \}\}",
badgePlacement: 'left',
badgeTipPlacement: 'top',
badgeToolTip: "\{\{ inventory.status_tip \}\}",
awToolTip: "\{\{ inventory.status_tip \}\}",
dataPlacement: "top",
searchable: false,
excludeModal: true,
sortField: "inventory_sources_with_failures"
},*/
has_inventory_sources: { has_inventory_sources: {
label: 'Cloud sourced', label: 'Cloud sourced?',
searchSingleValue: true, searchSingleValue: true,
searchType: 'boolean', searchType: 'boolean',
searchValue: 'true', searchValue: 'true',
searchOnly: true searchOnly: true
}, },
has_active_failures: { has_active_failures: {
label: 'Failed hosts', label: 'Failed hosts?',
searchSingleValue: true, searchSingleValue: true,
searchType: 'boolean', searchType: 'boolean',
searchValue: 'true', searchValue: 'true',
searchOnly: true searchOnly: true
}, },
inventory_sources_with_failures: { inventory_sources_with_failures: {
label: 'Sync failures', label: 'Sync failures?',
searchType: 'gtzero', searchType: 'gtzero',
searchValue: 'true', searchValue: 'true',
searchOnly: true searchOnly: true

View File

@@ -47,37 +47,32 @@ angular.module('InventoryHostsDefinition', [])
searchOnly: true searchOnly: true
}, },
has_active_failures: { has_active_failures: {
label: 'Has failed jobs?', label: 'Failed jobs?',
searchSingleValue: true, searchSingleValue: true,
searchType: 'boolean', searchType: 'boolean',
searchValue: 'true', searchValue: 'true',
searchOnly: true searchOnly: true
} }
/* ,
has_inventory_sources: {
label: 'Has external source?',
searchSingleValue: true,
searchType: 'boolean',
searchValue: 'true',
searchOnly: true
}*/
}, },
fieldActions: { fieldActions: {
enabled_flag: { enabled_flag: {
//label: 'Enabled', //label: 'Enabled',
iconClass: "\{\{ 'fa icon-enabled-' + host.enabled \}\}", iconClass: "{{ 'fa icon-enabled-' + host.enabled }}",
dataPlacement: 'top', dataPlacement: 'top',
awToolTip: "\{\{ host.enabledToolTip \}\}", awToolTip: "{{ host.enabledToolTip }}",
dataTipWatch: "host.enabledToolTip", dataTipWatch: "host.enabledToolTip",
ngClick: "toggleHostEnabled(\{\{ host.id \}\}, \{\{ host.has_inventory_sources \}\})" ngClick: "toggleHostEnabled(host.id, host.has_inventory_sources)"
}, },
active_failures: { active_failures: {
//label: 'Job Status', //label: 'Job Status',
ngHref: "\{\{'/#/hosts/' + host.id + '/job_host_summaries/?inventory=' + inventory_id \}\}", //ngHref: "\{\{'/#/hosts/' + host.id + '/job_host_summaries/?inventory=' + inventory_id \}\}",
awToolTip: "\{\{ host.badgeToolTip \}\}", awPopOver: "{{ host.job_status_html }}",
dataPlacement: 'top', dataTitle: "{{ host.job_status_title }}",
iconClass: "\{\{ 'fa icon-failures-' + host.has_active_failures \}\}" awToolTip: "{{ host.badgeToolTip }}",
awTipPlacement: 'top',
dataPlacement: 'left',
iconClass: "{{ 'fa icon-failures-' + host.has_active_failures }}"
}, },
edit: { edit: {
//label: 'Edit', //label: 'Edit',

View File

@@ -127,10 +127,6 @@ angular.module('StreamWidget', ['RestServices', 'Utilities', 'StreamListDefiniti
url += 'inventories/' + obj.id + '/'; url += 'inventories/' + obj.id + '/';
break; break;
default: default:
console.log('here');
console.log('url: ' + url);
console.log('base: ' + obj.base);
console.log('id: ' + obj.id);
url += obj.base + 's/' + obj.id + '/'; url += obj.base + 's/' + obj.id + '/';
} }
return url; return url;

View File

@@ -7,9 +7,6 @@
* *
*/ */
@import "animations.less";
@black: #171717; @black: #171717;
@white: #FFF; @white: #FFF;
@warning: #FF9900; @warning: #FF9900;
@@ -28,6 +25,11 @@
@tip-background: #0088CC; @tip-background: #0088CC;
@tip-color: #fff; @tip-color: #fff;
@import "animations.less";
@import "jquery-ui-overrides.less";
html, body { height: 100%; } html, body { height: 100%; }
html { html {
@@ -280,7 +282,7 @@ td.actions {
border-color: #ccc; border-color: #ccc;
} }
.ssh-key-field { .ssh-key-field, .mono-space {
font-family: Fixed, monospace; font-family: Fixed, monospace;
} }
@@ -1297,12 +1299,6 @@ tr td button i {
font-weight: bold; font-weight: bold;
} }
.ui-dialog-titlebar.ui-widget-header {
color: #0088cc;
background-color: #F0F0F0;
background-image: none;
}
/* Activity Stream Widget */ /* Activity Stream Widget */

View File

@@ -0,0 +1,69 @@
/*********************************************
* Copyright (c) 2014 AnsibleWorks, Inc.
*
* jquery-ui-overrides.less
*
* Additions to the custom-theme to make things
* look closer to Twitter Bootstrap
*
*/
/* Modal dialog */
.ui-dialog-title {
font-size: 22px;
color: @blue;
font-weight: bold;
line-height: normal;
}
.ui-dialog {
.close {
font-size: 18px;
font-weight: bold;
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
line-height: 1;
opacity: .7;
text-shadow: 0 1px 0 @white;
}
.ui-widget-header {
border-radius: 0;
border: none;
border-bottom: 1px solid #A9A9A9;
height: 55px;
}
.ui-dialog-titlebar {
padding-bottom: 0;
padding-top: 12px;
}
.ui-dialog-titlebar .ui-state-default {
background-image: none;
background-color: @white;
border-color: @white;
color: #A9A9A9;
}
.mono-space {
font-family: Fixed, monospace;
}
textarea.resizable {
resize: vertical;
}
.ui-resizable-se {
right: 5px;
bottom: 5px;
background-position: -80px -224px;
color: @black;
}
}
.ui-dialog-buttonset {
button.btn.btn-primary.ui-state-hover,
button.btn.btn-primary.ui-state-active {
background-image: none;
color: @white;
background-color: @blue-dark;
border-color: #285e8e;
text-decoration: none;
font-weight: normal;
}
}

View File

@@ -8,7 +8,7 @@
var INTEGER_REGEXP = /^\-?\d*$/; var INTEGER_REGEXP = /^\-?\d*$/;
angular.module('AWDirectives', ['RestServices', 'Utilities', 'AuthService']) angular.module('AWDirectives', ['RestServices', 'Utilities', 'AuthService', 'JobsHelper'])
// awpassmatch: Add to password_confirm field. Will test if value // awpassmatch: Add to password_confirm field. Will test if value
// matches that of 'input[name="password"]' // matches that of 'input[name="password"]'
.directive('awpassmatch', function() { .directive('awpassmatch', function() {
@@ -277,7 +277,7 @@ angular.module('AWDirectives', ['RestServices', 'Utilities', 'AuthService'])
* Include the standard TB data-XXX attributes to controll the pop-over's appearance. We will * Include the standard TB data-XXX attributes to controll the pop-over's appearance. We will
* default placement to the left, delay to 0 seconds, content type to HTML, and title to 'Help'. * default placement to the left, delay to 0 seconds, content type to HTML, and title to 'Help'.
*/ */
.directive('awPopOver', function() { .directive('awPopOver', ['$compile', function($compile) {
return function(scope, element, attrs) { return function(scope, element, attrs) {
var placement = (attrs.placement != undefined && attrs.placement != null) ? attrs.placement : 'left'; var placement = (attrs.placement != undefined && attrs.placement != null) ? attrs.placement : 'left';
var title = (attrs.title != undefined && attrs.title != null) ? attrs.title : 'Help'; var title = (attrs.title != undefined && attrs.title != null) ? attrs.title : 'Help';
@@ -289,26 +289,34 @@ angular.module('AWDirectives', ['RestServices', 'Utilities', 'AuthService'])
var e = $(this); var e = $(this);
$('.help-link, .help-link-white').each( function(index) { $('.help-link, .help-link-white').each( function(index) {
if (me != $(this).attr('id')) { if (me != $(this).attr('id')) {
$(this).popover('hide'); $(this).popover('hide');
} }
}); });
$('.popover').each(function(index) { $('.popover').each(function(index) {
// remove lingering popover <div>. Seems to be a bug in TB3 RC1 // remove lingering popover <div>. Seems to be a bug in TB3 RC1
$(this).remove(); $(this).remove();
}); });
$(this).popover('toggle'); $(this).popover('toggle');
$('.popover').each(function(index) {
$compile($(this))(scope); //make nested directives work!
});
}); });
$(document).bind('keydown', function(e) { $(document).bind('keydown', function(e) {
if (e.keyCode === 27) { if (e.keyCode === 27) {
$(element).popover('hide'); $(element).popover('hide');
$('.popover').each(function(index) { $('.popover').each(function(index) {
// remove lingering popover <div>. Seems to be a bug in TB3 RC1 // remove lingering popover <div>. Seems to be a bug in TB3 RC1
$(this).remove(); $(this).remove();
}); });
} }
}); });
} }
}) }])
// //
// Enable jqueryui slider widget on a numeric input field // Enable jqueryui slider widget on a numeric input field

View File

@@ -60,7 +60,7 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies', 'Utilities'])
this.modal = (options.modal) ? true : false; this.modal = (options.modal) ? true : false;
this.setForm(form); this.setForm(form);
if (options.html) { if (options.html) {
element.html(options.html); element.html(options.html);
} }
else { else {

View File

@@ -148,16 +148,6 @@ angular.module('ListGenerator', ['GeneratorHelpers'])
html += "<div class=\"row\">\n"; html += "<div class=\"row\">\n";
if (list.name != 'groups') { if (list.name != 'groups') {
// // Inventory groups
// html += "<div class=\"inventory-title col-lg-5\">" + list.editTitle + "</div>\n";
//}
//else if (list.name == 'hosts') {
// html += "<div class=\"col-lg-4\">\n";
// html += "<span class=\"hosts-title\">{{ selected_group_name }}</span>";
// html += "</div><!-- col-lg-4 -->";
// html += SearchWidget({ iterator: list.iterator, template: list, mini: true , size: 'col-lg-5',
// searchWidgets: list.searchWidgets });
//}
if (options.searchSize) { if (options.searchSize) {
html += SearchWidget({ iterator: list.iterator, template: list, mini: true , size: options.searchSize, html += SearchWidget({ iterator: list.iterator, template: list, mini: true , size: options.searchSize,
searchWidgets: list.searchWidgets }); searchWidgets: list.searchWidgets });
@@ -347,9 +337,13 @@ angular.module('ListGenerator', ['GeneratorHelpers'])
html += "<a "; html += "<a ";
html += (fAction.href) ? "href=\"" + fAction.href + "\" " : ""; html += (fAction.href) ? "href=\"" + fAction.href + "\" " : "";
html += (fAction.ngHref) ? "ng-href=\"" + fAction.ngHref + "\" " : ""; html += (fAction.ngHref) ? "ng-href=\"" + fAction.ngHref + "\" " : "";
html += (action == 'cancel') ? " class=\"cancel red-txt\" " : ""; html += (action == 'cancel') ? "class=\"cancel red-txt\" " : "";
html += (fAction.awPopOver) ? "aw-pop-over=\"" + fAction.awPopOver + "\" " : "";
html += (fAction.dataPlacement) ? Attr(fAction, 'dataPlacement') : "";
html += (fAction.dataTitle) ? Attr(fAction, 'dataTitle') : "";
for (itm in fAction) { for (itm in fAction) {
if (itm != 'ngHref' && itm != 'href' && itm != 'label' && itm != 'icon' && itm != 'class' && itm != 'iconClass') { if (itm != 'ngHref' && itm != 'href' && itm != 'label' && itm != 'icon' && itm != 'class' &&
itm != 'iconClass' && itm != "dataPlacement" && itm != "awPopOver" && itm != "dataTitle") {
html += Attr(fAction, itm); html += Attr(fAction, itm);
} }
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -1,3 +1,4 @@
<div class="tab-pane" id="home"> <div class="tab-pane" id="home">
<div ng-cloak id="htmlTemplate"></div> <div ng-cloak id="htmlTemplate"></div>
<div id="inventory-modal-container"></div>
</div> </div>

View File

@@ -4,12 +4,11 @@
<meta charset="utf-8"> <meta charset="utf-8">
<title>AnsibleWorks AWX</title> <title>AnsibleWorks AWX</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="{{ STATIC_URL }}css/custom-theme/jquery-ui-1.10.3.custom.css" /> <link rel="stylesheet" href="{{ STATIC_URL }}css/custom-theme/jquery-ui-1.10.3.custom.min.css" />
<link rel="stylesheet" href="{{ STATIC_URL }}css/bootstrap.min.css" /> <link rel="stylesheet" href="{{ STATIC_URL }}css/bootstrap.min.css" />
<link rel="stylesheet" href="{{ STATIC_URL }}css/font-awesome.min.css" /> <link rel="stylesheet" href="{{ STATIC_URL }}css/font-awesome.min.css" />
<link rel="stylesheet" href="{{ STATIC_URL }}css/select2.css" /> <link rel="stylesheet" href="{{ STATIC_URL }}css/select2.css" />
<link rel="stylesheet" href="{{ STATIC_URL }}css/select2-bootstrap.css" /> <link rel="stylesheet" href="{{ STATIC_URL }}css/select2-bootstrap.css" />
{% if settings.USE_MINIFIED_JS %} {% if settings.USE_MINIFIED_JS %}
<link rel="stylesheet" href="{{ STATIC_URL }}css/awx.min.css" /> <link rel="stylesheet" href="{{ STATIC_URL }}css/awx.min.css" />
@@ -75,6 +74,7 @@
<script src="{{ STATIC_URL }}js/forms/HostGroups.js"></script> <script src="{{ STATIC_URL }}js/forms/HostGroups.js"></script>
<script src="{{ STATIC_URL }}js/forms/InventoryStatus.js"></script> <script src="{{ STATIC_URL }}js/forms/InventoryStatus.js"></script>
<script src="{{ STATIC_URL }}js/forms/ActivityDetail.js"></script> <script src="{{ STATIC_URL }}js/forms/ActivityDetail.js"></script>
<script src="{{ STATIC_URL }}js/forms/JobSummary.js"></script>
<script src="{{ STATIC_URL }}js/lists/Users.js"></script> <script src="{{ STATIC_URL }}js/lists/Users.js"></script>
<script src="{{ STATIC_URL }}js/lists/Organizations.js"></script> <script src="{{ STATIC_URL }}js/lists/Organizations.js"></script>
<script src="{{ STATIC_URL }}js/lists/Admins.js"></script> <script src="{{ STATIC_URL }}js/lists/Admins.js"></script>
@@ -383,11 +383,13 @@
</div> </div>
</div><!-- site footer --> </div><!-- site footer -->
<script src="{{ STATIC_URL }}lib/jquery/jquery-ui-1.10.3.custom.min.js"></script> <!-- <script src="{{ STATIC_URL }}lib/jquery/jquery-ui-1.10.3.custom.min.js"></script> -->
<script src="{{ STATIC_URL }}lib/twitter/bootstrap.min.js"></script> <script src="{{ STATIC_URL }}lib/twitter/bootstrap.min.js"></script>
<script src="{{ STATIC_URL }}lib/js-yaml/js-yaml.min.js"></script> <script src="{{ STATIC_URL }}lib/js-yaml/js-yaml.min.js"></script>
<script src="{{ STATIC_URL }}lib/md5/jquery.md5.js"></script> <script src="{{ STATIC_URL }}lib/md5/jquery.md5.js"></script>
<script src="{{ STATIC_URL }}lib/select2/select2.js"></script> <script src="{{ STATIC_URL }}lib/select2/select2.js"></script>
<script src="{{ STATIC_URL }}lib/jquery/jquery-ui-1.10.4.custom.min.js"></script>
<script> <script>
$('a[data-toggle="tab"]').on('show.bs.tab', function (e) { $('a[data-toggle="tab"]').on('show.bs.tab', function (e) {