AC-463. Reduced refresh button to automatic page refreh every 10 sec. Duration btwn refreshes can be adjusted in config.js.

This commit is contained in:
chouseknecht 2013-09-17 03:36:35 -04:00
parent fb3aa968f7
commit f97b889a9b
19 changed files with 253 additions and 124 deletions

View File

@ -235,6 +235,11 @@ angular.module('ansible', [
function($cookieStore, $rootScope, CheckLicense, $location, Authorization, LoadBasePaths, ViewLicense) {
LoadBasePaths();
if ( !(typeof $AnsibleConfig.refresh_rate == 'number' && $AnsibleConfig.refresh_rate >= 3
&& $AnsibleConfig.refresh_rate <= 99) ) {
$AnsibleConfig.refresh_rate = 10;
}
$rootScope.breadcrumbs = new Array();
$rootScope.crumbCache = new Array();
@ -252,6 +257,7 @@ angular.module('ansible', [
}
CheckLicense();
}
// Make the correct tab active
var base = $location.path().replace(/^\//,'').split('/')[0];
if (base == '') {

View File

@ -14,6 +14,9 @@ var $AnsibleConfig =
debug_mode: true, // Enable console logging messages
refresh_rate: 10, // Number of seconds before refreshing a page. Integer between 3 and 99, inclusive.
// Used by awRefresh directive to automatically refresh Jobs and Projects pages.
password_strength: 45 // User password strength. Integer between 0 and 100, 100 being impossibly strong.
// This value controls progress bar colors:
// 0 to password_strength - 15 = red;

View File

@ -141,6 +141,22 @@ function JobEventsList ($scope, $rootScope, $location, $log, $routeParams, Rest,
cDate = new Date(set[i].created);
set[i].created = FormatDate(cDate);
}
// need job_status so we can show/hide refresh button
Rest.setUrl(GetBasePath('jobs') + scope.job_id);
Rest.get()
.success( function(data, status, headers, config) {
scope.job_status = data.status;
if (!(data.status == 'pending' || data.status == 'waiting' || data.status == 'running')) {
if ($rootScope.timer) {
clearInterval($rootScope.timer);
}
}
})
.error( function(data, status, headers, config) {
ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Failed to get job status for job: ' + scope.job_id + '. GET status: ' + status });
});
});
SearchInit({ scope: scope, set: 'jobevents', list: list, url: defaultUrl });

View File

@ -50,7 +50,24 @@ function JobHostSummaryList ($scope, $rootScope, $location, $log, $routeParams,
for( var i=0; i < scope.jobhosts.length; i++) {
scope.jobhosts[i].host_name = scope.jobhosts[i].summary_fields.host.name;
scope.jobhosts[i].status = (scope.jobhosts[i].failed) ? 'error' : 'success';
}
}
if (scope.host_id == null) {
// need job_status so we can show/hide refresh button
Rest.setUrl(GetBasePath('jobs') + scope.job_id);
Rest.get()
.success( function(data, status, headers, config) {
scope.job_status = data.status;
if (!(data.status == 'pending' || data.status == 'waiting' || data.status == 'running')) {
if ($rootScope.timer) {
clearInterval($rootScope.timer);
}
}
})
.error( function(data, status, headers, config) {
ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Failed to get job status for job: ' + scope.job_id + '. GET status: ' + status });
});
}
});
SearchInit({ scope: scope, set: 'jobhosts', list: list, url: defaultUrl });

View File

@ -262,6 +262,12 @@ function JobsEdit ($scope, $rootScope, $compile, $location, $log, $routeParams,
scope.playbook_options = null;
scope.playbook = null;
function calcRows (content) {
var n = content.match(/\n/g);
var rows = (n) ? n.length : 1;
return (rows > 15) ? 15 : rows;
}
// Retrieve detail record and prepopulate the form
Rest.setUrl(defaultUrl + ':id/');
Rest.get({ params: {id: id} })
@ -336,17 +342,20 @@ function JobsEdit ($scope, $rootScope, $compile, $location, $log, $routeParams,
relatedSets[set] = { url: related[set], iterator: form.related[set].iterator };
}
}
// Calc row size of stdout and traceback textarea fields
var n = scope['result_stdout'].match(/\n/g);
var rows = (n) ? n.length : 1;
rows = (rows > 15) ? 15 : rows;
scope['stdout_rows'] = rows;
//var n = scope['result_stdout'].match(/\n/g);
//var rows = (n) ? n.length : 1;
//rows = (rows > 15) ? 15 : rows;
//rows;
n = scope['result_traceback'].match(/\n/g);
var rows = (n) ? n.length : 1;
rows = (rows > 15) ? 15 : rows;
scope['traceback_rows'] = rows;
scope['stdout_rows'] = calcRows(scope['result_stdout']);
//n = scope['result_traceback'].match(/\n/g);
//var rows = (n) ? n.length : 1;
//rows = (rows > 15) ? 15 : rows;
scope['traceback_rows'] = calcRows(scope['result_traceback']);
LookUpInit({
scope: scope,
@ -461,6 +470,13 @@ function JobsEdit ($scope, $rootScope, $compile, $location, $log, $routeParams,
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']);
if (!(data.status == 'pending' || data.status == 'waiting' || data.status == 'running')) {
if ($rootScope.timer) {
clearInterval($rootScope.timer);
}
}
scope.statusSearchSpin = false;
})
.error( function(data, status, headers, config) {

View File

@ -27,8 +27,11 @@ function ProjectsList ($scope, $rootScope, $location, $log, $routeParams, Rest,
$rootScope.flashMessage = null;
var url = (base == 'teams') ? GetBasePath('teams') + $routeParams.team_id + '/projects/' : defaultUrl;
SelectionInit({ scope: scope, list: list, url: url, returnToCaller: 1 });
if (mode == 'select') {
SelectionInit({ scope: scope, list: list, url: url, returnToCaller: 1 });
}
if (scope.projectsPostRefresh) {
scope.projectsPostRefresh();
}

View File

@ -291,12 +291,9 @@ angular.module('JobFormDefinition', [])
},
statusActions: {
refresh: {
label: 'Refresh',
icon: 'icon-refresh',
ngClick: "refresh()",
"class": 'btn-sm btn-primary',
awToolTip: 'Refresh job status &amp; output',
refresh: {
awRefresh: true,
ngShow: "(status == 'pending' || status == 'waiting' || status == 'running')",
mode: 'all'
}
}

View File

@ -133,22 +133,24 @@ angular.module('SelectionHelper', ['Utilities', 'RestServices'])
scope.SelectPostRefreshRemove();
}
scope.SelectPostRefreshRemove = scope.$on('PostRefresh', function() {
for (var i=0; i < scope[list.name].length; i++) {
var found = false;
for (var j=0; j < scope.selected.length; j++) {
if (scope.selected[j].id == scope[list.name][i].id) {
found = true;
break;
}
}
if (found) {
scope[list.name][i]['checked'] = '1';
scope[list.name][i]['success_class'] = 'success';
}
else {
scope[list.name][i]['checked'] = '0';
scope[list.name][i]['success_class'] = '';
}
if (scope[list.name]) {
for (var i=0; i < scope[list.name].length; i++) {
var found = false;
for (var j=0; j < scope.selected.length; j++) {
if (scope.selected[j].id == scope[list.name][i].id) {
found = true;
break;
}
}
if (found) {
scope[list.name][i]['checked'] = '1';
scope[list.name][i]['success_class'] = 'success';
}
else {
scope[list.name][i]['checked'] = '0';
scope[list.name][i]['success_class'] = '';
}
}
}
});
}

View File

@ -72,11 +72,8 @@ angular.module('JobEventsListDefinition', [])
actions: {
refresh: {
ngClick: "refresh()",
icon: 'icon-refresh',
label: 'Refresh',
awToolTip: 'Refresh the page',
"class": 'btn-sm btn-primary',
awRefresh: true,
ngShow: "job_status == 'pending' || job_status == 'waiting' || job_status == 'running'",
mode: 'all'
}
},

View File

@ -79,11 +79,8 @@ angular.module('JobHostDefinition', [])
actions: {
refresh: {
label: 'Refresh',
icon: 'icon-refresh',
ngClick: "refresh()",
"class": 'btn-primary btn-sm',
awToolTip: 'Refresh the page',
awRefresh: true,
ngShow: "host_id == null && (job_status == 'pending' || job_status == 'waiting' || job_status == 'running')",
mode: 'all'
},
help: {

View File

@ -59,11 +59,7 @@ angular.module('JobsListDefinition', [])
actions: {
refresh: {
label: 'Refresh',
"class": 'btn-primary btn-sm',
ngClick: "refreshJob(\{\{ job.id \}\})",
icon: 'icon-refresh',
awToolTip: 'Refresh the page',
awRefresh: true,
mode: 'all'
}
},

View File

@ -22,7 +22,7 @@ angular.module('ProjectsListDefinition', [])
fields: {
name: {
key: true,
label: 'Name',
label: 'Name'
},
description: {
label: 'Description',
@ -53,11 +53,7 @@ angular.module('ProjectsListDefinition', [])
awToolTip: 'Create a new project'
},
refresh: {
label: 'Refresh',
"class": 'btn-primary btn-sm',
ngClick: "refresh(\{\{ job.id \}\})",
icon: 'icon-refresh',
awToolTip: 'Refresh the page',
awRefresh: true,
mode: 'all'
},
help: {

View File

@ -145,6 +145,18 @@ td.actions {
border-color: #ddd;
}
.refresh-grp {
display: inline-block;
margin-left: 10px;
margin-top: 0;
padding: 0;
line-height: normal;
.refresh-msg {
font-size: 10px;
}
}
.btn-light:hover {
color: #333;
background-color: #ccc;
@ -392,7 +404,6 @@ legend {
.status-actions {
display: inline-block;
height: 25px;
margin-bottom: 20px;
}
.status-spin {

View File

@ -154,7 +154,6 @@ angular.module('Utilities',[])
//Keep a list of path/title mappings. When we see /organizations/XX in the path, for example,
//we'll know the actual organization name it maps to.
console.log($rootScope.crumbCache);
if (crumb !== null && crumb !== undefined) {
var found = false;
for (var i=0; i < $rootScope.crumbCache.length; i++) {

View File

@ -11,34 +11,45 @@ angular.module('ApiLoader', ['ngCookies'])
.factory('LoadBasePaths', ['$http', '$rootScope', '$cookieStore', 'ProcessErrors',
function($http, $rootScope, $cookieStore, ProcessErrors) {
return function() {
$http.get('/api/')
.success( function(data, status, headers, config) {
var base = data.current_version;
$http.get(base)
.success( function(data, status, headers, config) {
data['base'] = base;
$rootScope['defaultUrls'] = data;
$cookieStore.remove('api');
$cookieStore.put('api',data); //Preserve in cookie to prevent against
//loss during browser refresh
})
.error ( function(data, status, headers, config) {
$rootScope['defaultUrls'] = { status: 'error' };
ProcessErrors(null, data, status, null,
{ hdr: 'Error', msg: 'Failed to read ' + base + '. GET status: ' + status });
});
})
.error( function(data, status, headers, config) {
$rootScope['defaultUrls'] = { status: 'error' };
ProcessErrors(null, data, status, null,
{ hdr: 'Error', msg: 'Failed to read /api. GET status: ' + status });
});
if (!$rootScope['defaultUrls'])
// if 'defaultUrls', the data used by GetBasePath(), is not in $rootScope, then we need to
// restore it from cookieStore or by querying the API. it goes missing from $rootScope
// when user hits browser refresh
if (!$cookieStore.get('api')) {
// if it's not in cookieStore, then we need to retrieve it from the API
$http.get('/api/')
.success( function(data, status, headers, config) {
var base = data.current_version;
$http.get(base)
.success( function(data, status, headers, config) {
data['base'] = base;
$rootScope['defaultUrls'] = data;
$cookieStore.remove('api');
$cookieStore.put('api',data); //Preserve in cookie to prevent against
//loss during browser refresh
})
.error ( function(data, status, headers, config) {
$rootScope['defaultUrls'] = { status: 'error' };
ProcessErrors(null, data, status, null,
{ hdr: 'Error', msg: 'Failed to read ' + base + '. GET status: ' + status });
});
})
.error( function(data, status, headers, config) {
$rootScope['defaultUrls'] = { status: 'error' };
ProcessErrors(null, data, status, null,
{ hdr: 'Error', msg: 'Failed to read /api. GET status: ' + status });
});
}
else {
$rootScope['defaultUrls'] = $cookieStore.get('api');
}
}
}])
.factory('GetBasePath', ['$rootScope', '$cookieStore', 'LoadBasePaths',
function($rootScope, $cookieStore, LoadBasePaths) {
return function(set) {
// use /api/v1/ results to construct API URLs.
var answer;
if ($rootScope['defaultUrls'] == null || $rootScope['defaultUrls'] == undefined) {
// browser refresh must have occurred. use what's in session cookie and refresh

View File

@ -408,6 +408,42 @@ angular.module('AWDirectives', ['RestServices', 'Utilities', 'AuthService', 'Hos
});
}
}
}])
//
// awRefresh
//
// Creates a timer to call scope.refresh(iterator) ever N seconds, where
// N is a setting in config.js
//
.directive('awRefresh', [ '$rootScope', function($rootScope) {
return {
link: function(scope, elm, attrs, ctrl) {
function msg() {
var num = '' + scope.refreshCnt;
while (num.length < 2) {
num = '0' + num;
}
return 'Refresh in ' + num + ' sec.';
}
scope.refreshCnt = $AnsibleConfig.refresh_rate;
scope.refreshMsg = msg();
if ($rootScope.timer) {
clearInterval($rootScope.timer);
}
$rootScope.timer = setInterval( function() {
scope.refreshCnt--;
if (scope.refreshCnt <= 0) {
scope.refresh();
scope.refreshCnt = $AnsibleConfig.refresh_rate;
}
scope.refreshMsg = msg();
if (!scope.$$phase) {
scope.$digest();
}
}, 1000);
}
}
}]);

View File

@ -10,8 +10,8 @@
angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies'])
.factory('GenerateForm', [ '$location', '$cookieStore', '$compile', 'SearchWidget', 'PaginateWidget', 'Attr', 'Icon', 'Column',
'NavigationLink', 'HelpCollapse',
function($location, $cookieStore, $compile, SearchWidget, PaginateWidget, Attr, Icon, Column, NavigationLink, HelpCollapse) {
'NavigationLink', 'HelpCollapse', 'Button',
function($location, $cookieStore, $compile, SearchWidget, PaginateWidget, Attr, Icon, Column, NavigationLink, HelpCollapse, Button) {
return {
setForm: function(form) {
@ -86,6 +86,7 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies'])
$('.tooltip').each( function(index) {
$(this).remove();
});
$('.popover').each(function(index) {
// remove lingering popover <div>. Seems to be a bug in TB3 RC1
$(this).remove();
@ -277,7 +278,7 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies'])
return html;
},
button: function(btn, topOrBottom) {
/*button: function(btn, topOrBottom) {
// pass in a button object and get back an html string containing
// a <button> element.
var html = '';
@ -297,7 +298,9 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies'])
}
}
return html;
},
},*/
button: Button,
navigationLink: NavigationLink,
@ -854,21 +857,14 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies'])
// Add status fields section (used in Jobs form)
html += "<div class=\"well\">\n";
if (this.form.statusActions) {
html += "<div class=\"status-actions\">\n";
//html += "<div class=\"status-actions\">\n";
var act;
for (action in this.form.statusActions) {
act = this.form.statusActions[action];
html += "<button type=\"button\" " + this.attr(act, 'ngClick') + "class=\"btn btn-small";
html += (act['class']) ? " " + act['class'] : "";
html += "\" ";
html += (act.awToolTip) ? this.attr(act,'awToolTip') : "";
html += (act.awToolTip) ? "data-placement=\"top\" " : "";
html += " >" + this.icon(act.icon);
html += (act.label) ? act.label : "";
html += "</button> ";
}
html += "</div>\n";
html += "<div class=\"status-spin\"><i class=\"icon-spinner icon-spin\" ng-show=\"statusSearchSpin == true\"></i></div>\n";
html += this.button(act);
}
//html += "</div>\n";
//html += "<div class=\"status-spin\"><i class=\"icon-spinner icon-spin\" ng-show=\"statusSearchSpin == true\"></i></div>\n";
}
html += "<div class=\"form-horizontal status-fields\">\n";
for (var fld in this.form.statusFields) {
@ -899,7 +895,7 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies'])
html += "<div>\n";
}
if (this.form.navigation) {
/* if (this.form.navigation) {
html += "<div class=\"navigation-buttons navigation-buttons-top\">\n";
for (btn in this.form.navigation) {
var btn = this.form.navigation[btn];
@ -908,7 +904,7 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies'])
}
}
html += "</div>\n";
}
} */
// Start the well
if ( this.has('well') ) {
@ -1034,7 +1030,7 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies'])
html += "</div>\n";
}
if (this.form.navigation) {
/*if (this.form.navigation) {
html += "<div class=\"navigation-buttons navigation-buttons-bottom\">\n";
for (btn in this.form.navigation) {
var btn = this.form.navigation[btn];
@ -1043,7 +1039,7 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies'])
}
}
html += "</div>\n";
}
}*/
if ( this.form.collapse && this.form.collapseMode == options.mode ) {
html += "</div>\n";

View File

@ -93,6 +93,54 @@ angular.module('GeneratorHelpers', ['GeneratorHelpers'])
return "<i class=\"" + icon + "\"></i> ";
}
})
.factory('Button', ['Attr', function(Attr) {
return function(btn) {
// pass in button object, get back html
var html = '';
if (btn.awRefresh) {
html += "<div class=\"refresh-grp pull-right\" ";
html += (btn.ngShow) ? Attr(btn, 'ngShow') : "";
html += ">\n";
}
html += "<button type=\"button\" " + "class=\"btn";
if (btn.awRefresh && !btn['class']) {
html += ' btn-primary btn-xs refresh-btn';
}
else if (btn['class']) {
html += ' ' + btn['class'];
}
else {
html += " btn-sm";
}
html += (btn['awPopOver']) ? " help-link-white" : "";
html += "\" ";
html += (btn.ngClick) ? Attr(btn, 'ngClick') : "";
html += (btn.awRefresh) ? " ng-click=\"refreshCnt = " + $AnsibleConfig.refresh_rate + "; refresh()\" " : "";
html += (btn.id) ? "id=\"" + btn.id + "\" " : "";
html += (btn.ngHide) ? Attr(btn,'ngHide') : "";
html += (btn.awToolTip) ? Attr(btn,'awToolTip') : "";
html += (btn.awToolTip && btn.dataPlacement == undefined) ? "data-placement=\"top\" " : "";
html += (btn.awRefresh && !btn.awTooltip) ? "aw-tool-tip=\"Refresh page\" " : "";
html += (btn.awPopOver) ? "aw-pop-over=\"" +
btn.awPopOver.replace(/[\'\"]/g, '&quot;') + "\" " : "";
html += (btn.dataPlacement) ? Attr(btn, 'dataPlacement') : "";
html += (btn.dataContainer) ? Attr(btn, 'dataContainer') : "";
html += (btn.dataTitle) ? Attr(btn, 'dataTitle') : "";
html += (btn.ngShow) ? Attr(btn, 'ngShow') : "";
html += (btn.ngHide) ? Attr(btn, 'ngHide') : "";
html += " >";
html += (btn['icon']) ? Attr(btn,'icon') : "";
html += (btn['awRefresh'] && !btn['icon']) ? "<i class=\"icon-refresh\"></i> " : "";
html += (btn.label) ? " " + btn.label : "";
html += "</button> ";
if (btn['awRefresh']) {
html += '<span class=\"refresh-msg\" aw-refresh>{{ refreshMsg }}</span>\n';
html += "</div><!-- refresh-grp -->\n";
}
return html;
}
}])
.factory('NavigationLink', ['Attr', 'Icon', function(Attr, Icon) {
return function(link) {

View File

@ -9,8 +9,8 @@
angular.module('ListGenerator', ['GeneratorHelpers'])
.factory('GenerateList', [ '$location', '$compile', '$rootScope', 'SearchWidget', 'PaginateWidget', 'Attr', 'Icon',
'Column', 'DropDown', 'NavigationLink',
function($location, $compile, $rootScope, SearchWidget, PaginateWidget, Attr, Icon, Column, DropDown, NavigationLink) {
'Column', 'DropDown', 'NavigationLink', 'Button',
function($location, $compile, $rootScope, SearchWidget, PaginateWidget, Attr, Icon, Column, DropDown, NavigationLink, Button) {
return {
setList: function(list) {
@ -29,29 +29,7 @@ angular.module('ListGenerator', ['GeneratorHelpers'])
$('#lookup-modal').modal('hide');
},
button: function(btn) {
// pass in button object, get back html
var html = '';
html += "<button type=\"button\" " + this.attr(btn, 'ngClick') + "class=\"btn";
html += (btn['class']) ? " " + btn['class'] : " btn-sm";
html += (btn['awPopOver']) ? " help-link-white" : "";
html += "\" ";
html += (btn.id) ? "id=\"" + btn.id + "\" " : "";
html += (btn.ngHide) ? this.attr(btn,'ngHide') : "";
html += (btn.awToolTip) ? this.attr(btn,'awToolTip') : "";
html += (btn.awToolTip && btn.dataPlacement == undefined) ? "data-placement=\"top\" " : "";
html += (btn.awPopOver) ? "aw-pop-over=\"" +
btn.awPopOver.replace(/[\'\"]/g, '&quot;') + "\" " : "";
html += (btn.dataPlacement) ? this.attr(btn, 'dataPlacement') : "";
html += (btn.dataContainer) ? this.attr(btn, 'dataContainer') : "";
html += (btn.dataTitle) ? this.attr(btn, 'dataTitle') : "";
html += (btn.ngShow) ? this.attr(btn, 'ngShow') : "";
html += (btn.ngHide) ? this.attr(btn, 'ngHide') : "";
html += " >" + this.attr(btn,'icon');
html += (btn.label) ? " " + btn.label : "";
html += "</button> ";
return html;
},
button: Button,
inject: function(list, options) {
// options.mode = one of edit, select or lookup
@ -93,6 +71,10 @@ angular.module('ListGenerator', ['GeneratorHelpers'])
// remove lingering popover <div>. Seems to be a bug in TB3 RC1
$(this).remove();
});
// Remove leftover timer
//if (options.mode != 'lookup' && $rootScope.timer) {
// clearInterval($rootScope.timer);
//}
if (options.mode == 'lookup') {
// options should include {hdr: <dialog header>, action: <function...> }
@ -169,7 +151,7 @@ angular.module('ListGenerator', ['GeneratorHelpers'])
if (list.actions[action].mode == 'all' || list.actions[action].mode == options.mode) {
if ( (list.actions[action].basePaths == undefined) ||
(list.actions[action].basePaths && list.actions[action].basePaths.indexOf(base) > -1) ) {
html += this.button(list.actions[action]);
html += this.button(list.actions[action], list.iterator);
}
}
}