AC-331 Latest host tab changes. Editing the groups of a host is now possible

This commit is contained in:
chouseknecht 2013-08-20 18:22:28 -04:00
parent 36d4795c12
commit b76bf268f9
33 changed files with 5056 additions and 2033 deletions

384
awx/ui/static/css/bootstrap-theme.css vendored Normal file
View File

@ -0,0 +1,384 @@
.btn-default,
.btn-primary,
.btn-success,
.btn-info,
.btn-warning,
.btn-danger {
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2);
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);
}
.btn-default:active,
.btn-primary:active,
.btn-success:active,
.btn-info:active,
.btn-warning:active,
.btn-danger:active,
.btn-default.active,
.btn-primary.active,
.btn-success.active,
.btn-info.active,
.btn-warning.active,
.btn-danger.active {
-webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
}
.btn:active,
.btn.active {
background-image: none;
}
.btn-default {
text-shadow: 0 1px 0 #fff;
background-image: -webkit-gradient(linear, left 0%, left 100%, from(#ffffff), to(#e6e6e6));
background-image: -webkit-linear-gradient(top, #ffffff, 0%, #e6e6e6, 100%);
background-image: -moz-linear-gradient(top, #ffffff 0%, #e6e6e6 100%);
background-image: linear-gradient(to bottom, #ffffff 0%, #e6e6e6 100%);
background-repeat: repeat-x;
border-color: #e0e0e0;
border-color: #ccc;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe6e6e6', GradientType=0);
}
.btn-default:active,
.btn-default.active {
background-color: #e6e6e6;
border-color: #e0e0e0;
}
.btn-primary {
background-image: -webkit-gradient(linear, left 0%, left 100%, from(#428bca), to(#3071a9));
background-image: -webkit-linear-gradient(top, #428bca, 0%, #3071a9, 100%);
background-image: -moz-linear-gradient(top, #428bca 0%, #3071a9 100%);
background-image: linear-gradient(to bottom, #428bca 0%, #3071a9 100%);
background-repeat: repeat-x;
border-color: #2d6ca2;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0);
}
.btn-primary:active,
.btn-primary.active {
background-color: #3071a9;
border-color: #2d6ca2;
}
.btn-success {
background-image: -webkit-gradient(linear, left 0%, left 100%, from(#5cb85c), to(#449d44));
background-image: -webkit-linear-gradient(top, #5cb85c, 0%, #449d44, 100%);
background-image: -moz-linear-gradient(top, #5cb85c 0%, #449d44 100%);
background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);
background-repeat: repeat-x;
border-color: #419641;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);
}
.btn-success:active,
.btn-success.active {
background-color: #449d44;
border-color: #419641;
}
.btn-warning {
background-image: -webkit-gradient(linear, left 0%, left 100%, from(#f0ad4e), to(#ec971f));
background-image: -webkit-linear-gradient(top, #f0ad4e, 0%, #ec971f, 100%);
background-image: -moz-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);
background-repeat: repeat-x;
border-color: #eb9316;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);
}
.btn-warning:active,
.btn-warning.active {
background-color: #ec971f;
border-color: #eb9316;
}
.btn-danger {
background-image: -webkit-gradient(linear, left 0%, left 100%, from(#d9534f), to(#c9302c));
background-image: -webkit-linear-gradient(top, #d9534f, 0%, #c9302c, 100%);
background-image: -moz-linear-gradient(top, #d9534f 0%, #c9302c 100%);
background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);
background-repeat: repeat-x;
border-color: #c12e2a;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);
}
.btn-danger:active,
.btn-danger.active {
background-color: #c9302c;
border-color: #c12e2a;
}
.btn-info {
background-image: -webkit-gradient(linear, left 0%, left 100%, from(#5bc0de), to(#31b0d5));
background-image: -webkit-linear-gradient(top, #5bc0de, 0%, #31b0d5, 100%);
background-image: -moz-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);
background-repeat: repeat-x;
border-color: #2aabd2;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);
}
.btn-info:active,
.btn-info.active {
background-color: #31b0d5;
border-color: #2aabd2;
}
.thumbnail,
.img-thumbnail {
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
}
.dropdown-menu > li > a:hover,
.dropdown-menu > li > a:focus,
.dropdown-menu > .active > a,
.dropdown-menu > .active > a:hover,
.dropdown-menu > .active > a:focus {
background-color: #357ebd;
background-image: -webkit-gradient(linear, left 0%, left 100%, from(#428bca), to(#357ebd));
background-image: -webkit-linear-gradient(top, #428bca, 0%, #357ebd, 100%);
background-image: -moz-linear-gradient(top, #428bca 0%, #357ebd 100%);
background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0);
}
.navbar {
background-image: -webkit-gradient(linear, left 0%, left 100%, from(#ffffff), to(#f8f8f8));
background-image: -webkit-linear-gradient(top, #ffffff, 0%, #f8f8f8, 100%);
background-image: -moz-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);
background-image: linear-gradient(to bottom, #ffffff 0%, #f8f8f8 100%);
background-repeat: repeat-x;
border-radius: 4px;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);
}
.navbar .navbar-nav > .active > a {
background-color: #f8f8f8;
}
.navbar-brand,
.navbar-nav > li > a {
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.25);
}
.navbar-inverse {
background-image: -webkit-gradient(linear, left 0%, left 100%, from(#3c3c3c), to(#222222));
background-image: -webkit-linear-gradient(top, #3c3c3c, 0%, #222222, 100%);
background-image: -moz-linear-gradient(top, #3c3c3c 0%, #222222 100%);
background-image: linear-gradient(to bottom, #3c3c3c 0%, #222222 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);
}
.navbar-inverse .navbar-nav > .active > a {
background-color: #222222;
}
.navbar-inverse .navbar-brand,
.navbar-inverse .navbar-nav > li > a {
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
}
.navbar-static-top,
.navbar-fixed-top,
.navbar-fixed-bottom {
border-radius: 0;
}
.alert {
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.2);
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);
}
.alert-success {
background-image: -webkit-gradient(linear, left 0%, left 100%, from(#dff0d8), to(#c8e5bc));
background-image: -webkit-linear-gradient(top, #dff0d8, 0%, #c8e5bc, 100%);
background-image: -moz-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);
background-repeat: repeat-x;
border-color: #b2dba1;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);
}
.alert-info {
background-image: -webkit-gradient(linear, left 0%, left 100%, from(#d9edf7), to(#b9def0));
background-image: -webkit-linear-gradient(top, #d9edf7, 0%, #b9def0, 100%);
background-image: -moz-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);
background-repeat: repeat-x;
border-color: #9acfea;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);
}
.alert-warning {
background-image: -webkit-gradient(linear, left 0%, left 100%, from(#fcf8e3), to(#f8efc0));
background-image: -webkit-linear-gradient(top, #fcf8e3, 0%, #f8efc0, 100%);
background-image: -moz-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);
background-repeat: repeat-x;
border-color: #f5e79e;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);
}
.alert-danger {
background-image: -webkit-gradient(linear, left 0%, left 100%, from(#f2dede), to(#e7c3c3));
background-image: -webkit-linear-gradient(top, #f2dede, 0%, #e7c3c3, 100%);
background-image: -moz-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);
background-repeat: repeat-x;
border-color: #dca7a7;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);
}
.progress {
background-image: -webkit-gradient(linear, left 0%, left 100%, from(#ebebeb), to(#f5f5f5));
background-image: -webkit-linear-gradient(top, #ebebeb, 0%, #f5f5f5, 100%);
background-image: -moz-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);
}
.progress-bar {
background-image: -webkit-gradient(linear, left 0%, left 100%, from(#428bca), to(#3071a9));
background-image: -webkit-linear-gradient(top, #428bca, 0%, #3071a9, 100%);
background-image: -moz-linear-gradient(top, #428bca 0%, #3071a9 100%);
background-image: linear-gradient(to bottom, #428bca 0%, #3071a9 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0);
}
.progress-bar-success {
background-image: -webkit-gradient(linear, left 0%, left 100%, from(#5cb85c), to(#449d44));
background-image: -webkit-linear-gradient(top, #5cb85c, 0%, #449d44, 100%);
background-image: -moz-linear-gradient(top, #5cb85c 0%, #449d44 100%);
background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);
}
.progress-bar-info {
background-image: -webkit-gradient(linear, left 0%, left 100%, from(#5bc0de), to(#31b0d5));
background-image: -webkit-linear-gradient(top, #5bc0de, 0%, #31b0d5, 100%);
background-image: -moz-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);
}
.progress-bar-warning {
background-image: -webkit-gradient(linear, left 0%, left 100%, from(#f0ad4e), to(#ec971f));
background-image: -webkit-linear-gradient(top, #f0ad4e, 0%, #ec971f, 100%);
background-image: -moz-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);
}
.progress-bar-danger {
background-image: -webkit-gradient(linear, left 0%, left 100%, from(#d9534f), to(#c9302c));
background-image: -webkit-linear-gradient(top, #d9534f, 0%, #c9302c, 100%);
background-image: -moz-linear-gradient(top, #d9534f 0%, #c9302c 100%);
background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);
}
.list-group {
border-radius: 4px;
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
}
.list-group-item.active,
.list-group-item.active:hover,
.list-group-item.active:focus {
text-shadow: 0 -1px 0 #3071a9;
background-image: -webkit-gradient(linear, left 0%, left 100%, from(#428bca), to(#3278b3));
background-image: -webkit-linear-gradient(top, #428bca, 0%, #3278b3, 100%);
background-image: -moz-linear-gradient(top, #428bca 0%, #3278b3 100%);
background-image: linear-gradient(to bottom, #428bca 0%, #3278b3 100%);
background-repeat: repeat-x;
border-color: #3278b3;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3278b3', GradientType=0);
}
.panel {
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
}
.panel-default > .panel-heading {
background-image: -webkit-gradient(linear, left 0%, left 100%, from(#f5f5f5), to(#e8e8e8));
background-image: -webkit-linear-gradient(top, #f5f5f5, 0%, #e8e8e8, 100%);
background-image: -moz-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
}
.panel-primary > .panel-heading {
background-image: -webkit-gradient(linear, left 0%, left 100%, from(#428bca), to(#357ebd));
background-image: -webkit-linear-gradient(top, #428bca, 0%, #357ebd, 100%);
background-image: -moz-linear-gradient(top, #428bca 0%, #357ebd 100%);
background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0);
}
.panel-success > .panel-heading {
background-image: -webkit-gradient(linear, left 0%, left 100%, from(#dff0d8), to(#d0e9c6));
background-image: -webkit-linear-gradient(top, #dff0d8, 0%, #d0e9c6, 100%);
background-image: -moz-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);
}
.panel-info > .panel-heading {
background-image: -webkit-gradient(linear, left 0%, left 100%, from(#d9edf7), to(#c4e3f3));
background-image: -webkit-linear-gradient(top, #d9edf7, 0%, #c4e3f3, 100%);
background-image: -moz-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);
}
.panel-warning > .panel-heading {
background-image: -webkit-gradient(linear, left 0%, left 100%, from(#fcf8e3), to(#faf2cc));
background-image: -webkit-linear-gradient(top, #fcf8e3, 0%, #faf2cc, 100%);
background-image: -moz-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);
}
.panel-danger > .panel-heading {
background-image: -webkit-gradient(linear, left 0%, left 100%, from(#f2dede), to(#ebcccc));
background-image: -webkit-linear-gradient(top, #f2dede, 0%, #ebcccc, 100%);
background-image: -moz-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);
}
.well {
background-image: -webkit-gradient(linear, left 0%, left 100%, from(#e8e8e8), to(#f5f5f5));
background-image: -webkit-linear-gradient(top, #e8e8e8, 0%, #f5f5f5, 100%);
background-image: -moz-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);
background-repeat: repeat-x;
border-color: #dcdcdc;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);
-webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);
}

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -62,7 +62,8 @@ angular.module('ansible', [
'AccessHelper',
'SelectionHelper',
'LicenseFormDefinition',
'License'
'License',
'HostGroupsFormDefinition'
])
.config(['$routeProvider', function($routeProvider) {
$routeProvider.

View File

@ -185,7 +185,7 @@ function InventoriesEdit ($scope, $rootScope, $compile, $location, $log, $routeP
RelatedPaginateInit, ReturnToCaller, ClearScope, LookUpInit, Prompt,
OrganizationList, TreeInit, GetBasePath, GroupsList, GroupsAdd, GroupsEdit, LoadInventory,
GroupsDelete, HostsList, HostsAdd, HostsEdit, HostsDelete, RefreshGroupName, ParseTypeChange,
HostsReload, EditInventory, RefreshTree, LoadSearchTree)
HostsReload, EditInventory, RefreshTree, LoadSearchTree, EditHostGroups)
{
ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior
//scope.
@ -204,6 +204,8 @@ function InventoriesEdit ($scope, $rootScope, $compile, $location, $log, $routeP
scope['inventoryParseType'] = 'yaml';
scope['inventory_id'] = id;
scope['inventoryFailureFilter'] = false;
scope['hostDeleteDisabled'] = true;
scope['hostDeleteDisabledClass'] = 'disabled';
// Retrieve each related sets and any lookups
if (scope.inventoryLoadedRemove) {
@ -447,6 +449,10 @@ function InventoriesEdit ($scope, $rootScope, $compile, $location, $log, $routeP
HostsEdit({ scope: scope, "inventory_id": id, group_id: scope.group_id, host_id: host_id, host_name: host_name });
}
scope.editHostGroups = function(host_id) {
EditHostGroups({ inventory_id: id, host_id: host_id });
}
scope.deleteHost = function(host_id, host_name) {
HostsDelete({ scope: scope, "inventory_id": id, group_id: scope.group_id, host_id: host_id, host_name: host_name,
request: 'delete' });
@ -472,7 +478,8 @@ function InventoriesEdit ($scope, $rootScope, $compile, $location, $log, $routeP
}
scope.toggleAllHosts = function() {
scope.hostDeleteHide = (scope.toggleAllFlag) ? false : true;
scope.hostDeleteDisabled = (scope.toggleAllFlag) ? false : true;
scope.hostDeleteDisabledClass = (scope.hostDeleteDisabled) ? "disabled" : "";
for (var i=0; i < scope.hosts.length; i++) {
scope.hosts[i].selected = scope.toggleAllFlag;
}
@ -486,9 +493,11 @@ function InventoriesEdit ($scope, $rootScope, $compile, $location, $log, $routeP
break;
}
}
scope.hostDeleteHide = result;
scope.hostDeleteDisabled = result;
scope.hostDeleteDisabledClass = (scope.hostDeleteDisabled) ? "disabled" : "";
}
// Respond to the scope.$emit from awTree directive
scope.$on('refreshHost', function(e, group, title) {
scope.groupTitle = title;
@ -503,6 +512,8 @@ function InventoriesEdit ($scope, $rootScope, $compile, $location, $log, $routeP
scope.hostCreateHide = false;
scope.hostDeleteHide = false;
}
scope['hostDeleteDisabled'] = true;
scope['hostDeleteDisabledClass'] = 'disabled';
HostsReload({ scope: scope, inventory_id: scope['inventory_id'], group_id: group });
});
@ -513,6 +524,6 @@ InventoriesEdit.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$l
'RelatedPaginateInit', 'ReturnToCaller', 'ClearScope', 'LookUpInit', 'Prompt',
'OrganizationList', 'TreeInit', 'GetBasePath', 'GroupsList', 'GroupsAdd', 'GroupsEdit', 'LoadInventory',
'GroupsDelete', 'HostsList', 'HostsAdd', 'HostsEdit', 'HostsDelete', 'RefreshGroupName',
'ParseTypeChange', 'HostsReload', 'EditInventory', 'RefreshTree', 'LoadSearchTree'
'ParseTypeChange', 'HostsReload', 'EditInventory', 'RefreshTree', 'LoadSearchTree', 'EditHostGroups'
];

View File

@ -0,0 +1,61 @@
/*********************************************
* Copyright (c) 2013 AnsibleWorks, Inc.
*
* HostGroups.js
* Form definition for Host model
*
*
*/
angular.module('HostGroupsFormDefinition', [])
.value(
'HostGroupsForm', {
editTitle: 'Host Groups', //Legend in edit mode
name: 'host', //Form name attribute
well: false, //Wrap the form with TB well
formLabelSize: 'col-lg-3',
formFieldSize: 'col-lg-9',
fields: {
groups: {
label: 'Groups',
type: 'select',
multiple: true,
ngOptions: 'group.name for group in inventory_groups',
addRequired: true,
editRequired: true,
awPopOver: "<p>Provide a host name, ip address, or ip address:port. Examples include:</p>" +
"<blockquote>myserver.domain.com<br/>" +
"127.0.0.1<br />" +
"10.1.0.140:25<br />" +
"server.example.com:25" +
"</blockquote>",
dataTitle: 'Host Name',
dataPlacement: 'right',
dataContainer: '#form-modal .modal-content'
}
},
buttons: { //for now always generates <button> tags
save: {
label: 'Save',
icon: 'icon-ok',
"class": 'btn-success',
ngClick: 'formSave()', //$scope.function to call on click, optional
ngDisabled: true //Disable when $pristine or $invalid, optional
},
reset: {
ngClick: 'formReset()',
label: 'Reset',
icon: 'icon-trash',
'class': 'btn btn-default',
ngDisabled: true //Disabled when $pristine
}
},
related: { //related colletions (and maybe items?)
}
}); //UserForm

View File

@ -96,14 +96,6 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', '
scope.parseType = 'yaml';
ParseTypeChange(scope);
//$('#form-modal').on('hidden.bs.modal', function() {
// var me = $(this);
// $('.modal-backdrop').each(function(index) {
// $(this).remove();
// });
// me.unbind('hidden.bs.modal');
// });
$('#form-modal .btn-none').removeClass('btn-none').addClass('btn-success');
generator.reset();
@ -116,6 +108,8 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', '
// Save
scope.formModalAction = function() {
try {
scope.formModalActionDisabled = true;
// Make sure we have valid variable data
if (scope.parseType == 'json') {
@ -162,11 +156,13 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', '
}
})
.error( function(data, status, headers, config) {
scope.formModalActionDisabled = false;
ProcessErrors(scope, data, status, form,
{ hdr: 'Error!', msg: 'Failed to add new group. POST returned status: ' + status });
});
}
catch(err) {
scope.formModalActionDisabled = false;
Alert("Error", "Error parsing group variables. Parser returned: " + err);
}
}

View File

@ -10,7 +10,7 @@
angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'HostListDefinition',
'SearchHelper', 'PaginateHelper', 'ListGenerator', 'AuthService', 'HostsHelper',
'InventoryHelper', 'RelatedSearchHelper','RelatedPaginateHelper',
'InventoryFormDefinition', 'SelectionHelper'
'InventoryFormDefinition', 'SelectionHelper', 'HostGroupsFormDefinition'
])
.factory('HostsList', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'HostList', 'GenerateList',
@ -98,7 +98,6 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H
scope.removeHostsReload();
}
scope.removeHostsReload = scope.$on('hostsReload', function() {
console.log('here!');
HostsReload(params);
});
@ -122,6 +121,9 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H
}
try {
scope.formModalActionDisabled = true;
// Make sure we have valid variable data
if (scope.parseType == 'json') {
var json_data = JSON.parse(scope.variables); //make sure JSON parses
@ -162,11 +164,13 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H
}
})
.error( function(data, status, headers, config) {
scope.formModalActionDisabled = false;
ProcessErrors(scope, data, status, form,
{ hdr: 'Error!', msg: 'Failed to add new host. POST returned status: ' + status });
});
}
catch(err) {
scope.formModalActionDisabled = false;
Alert("Error", "Error parsing host variables. Parser returned: " + err);
}
}
@ -200,7 +204,7 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H
var relatedSets = {};
scope.formModalActionLabel = 'Save';
scope.formModalHeader = 'Edit Host';
scope.formModalHeader = 'Host Properties';
scope.formModalCancelShow = true;
scope.parseType = 'yaml';
ParseTypeChange(scope);
@ -347,8 +351,14 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H
return function(params) {
// Remove the selected host from the current group by disassociating
var scope = params.scope;
if (scope.hostDeleteDisabled) {
// simulate a disabled link
return;
}
var group_id = scope.group_id;
var inventory_id = params.inventory_id;
var host_id = params.host_id;
@ -432,13 +442,15 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H
}])
.factory('HostsReload', ['RelatedSearchInit', 'RelatedPaginateInit', 'InventoryForm', 'GetBasePath',
function(RelatedSearchInit, RelatedPaginateInit, InventoryForm, GetBasePath) {
.factory('HostsReload', ['RelatedSearchInit', 'RelatedPaginateInit', 'InventoryForm', 'GetBasePath', 'Wait',
function(RelatedSearchInit, RelatedPaginateInit, InventoryForm, GetBasePath, Wait) {
return function(params) {
// Rerfresh the Hosts view on right side of page
var group_id = params.group_id;
var scope = params.scope;
var postAction = params.action;
scope['hosts'] = null;
scope['toggleAllFlag'] = false;
scope['hostDeleteHide'] = true;
@ -450,26 +462,24 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H
url += '?has_active_failures=true';
}
// Set the groups value in each element of hosts array
if (scope.removeRelatedHosts) {
scope.removeRelatedHosts();
}
scope.removeRelatedHosts = scope.$on('relatedhosts', function() {
var groups, descr='';
var groups, descr, found, list;
for (var i=0; i < scope.hosts.length; i++) {
groups = scope.hosts[i].summary_fields.groups;
for (var j=0; j < groups.length; j++) {
if (groups[j].name.match(/^_deleted/)) {
descr += groups[j].name.substr(1,18) + ', ';
}
else {
descr += groups[j].name + ', ';
}
}
descr = descr.replace(/, $/,'');
if (descr.length > 50) {
descr = descr.substr(0,49) + '...';
}
scope.hosts[i].groups = descr;
groups = scope.hosts[i].summary_fields.groups;
scope.hosts[i].groups = '';
for (var k=0; k < groups.length; k++) {
if (!groups[k].name.match(/^_deleted/)) {
scope.hosts[i].groups += groups[k].name + ', '
}
}
scope.hosts[i].groups = scope.hosts[i].groups.replace(/\, $/,'');
}
if (postAction) {
postAction();
}
});
@ -477,19 +487,6 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H
RelatedSearchInit({ scope: params.scope, form: InventoryForm, relatedSets: relatedSets });
RelatedPaginateInit({ scope: params.scope, relatedSets: relatedSets, pageSize: 40 });
/*if (scope['hostFailureFilter']) {
// If the user checked 'show only hosts with failures', filter for hosts with failed jobs
scope['hostSearchFieldLabel'] = 'Failed jobs?';
scope['hostSearchField'] = 'has_active_failures';
scope['hostSelectShow'] = true;
scope.setSearchField('host','has_active_failures','Failed jobs?');
for (var i=0; i < scope['hostSearchSelectOpts'].length; i++) {
if (scope['hostSearchSelectOpts'][i].value == 1) {
scope['hostSearchSelectValue'] = scope['hostSearchSelectOpts'][i];
}
}
} */
scope.search('host');
if (!params.scope.$$phase) {
@ -498,16 +495,38 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H
}
}])
.factory('LoadSearchTree', ['Rest', 'GetBasePath', 'ProcessErrors', '$compile',
function(Rest, GetBasePath, ProcessErrors, $compile) {
return function(params) {
var scope = params.scope;
var inventory_id = params.inventory_id;
var newTree = [];
scope.searchTree = [];
// After the inventory is loaded, build an array of all unique groups found therein.
// The lis is used in the group drop-down selector widget for each host.
if (scope.buildAllGroupsRemove) {
scope.buildAllGroupsRemove();
}
scope.buildAllGroupsRemove = scope.$on('buildAllGroups', function() {
scope.inventory_groups = [];
Rest.setUrl(GetBasePath('inventory') + inventory_id + '/groups/?order_by=name');
Rest.get()
.success( function(data, status, headers, config) {
for (var i=0; i < data.results.length; i++) {
scope.inventory_groups.push({ name: data.results[i].name, id: data.results[i].id });
}
scope.$emit('hostTabInit');
})
.error( function(data, status, headers, config) {
ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Failed to get groups for inventory: ' + inventory_id + '. GET returned: ' + status });
});
});
// Load the root node
// Load the inventory root node
Rest.setUrl (GetBasePath('inventory') + inventory_id + '/');
Rest.get()
.success( function(data, status, headers, config) {
@ -519,14 +538,323 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H
groups: data.related.root_groups,
children: []
});
scope.$emit('hostTabInit');
scope.$emit('buildAllGroups');
})
.error( function(data, status, headers, config) {
ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Failed to get inventory: ' + inventory_id + '. GET returned: ' + status });
});
}
}])
.factory('EditHostGroups', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'GenerateForm',
'Prompt', 'ProcessErrors', 'GetBasePath', 'HostsReload', 'ParseTypeChange', 'Wait',
function($rootScope, $location, $log, $routeParams, Rest, Alert, GenerateForm, Prompt, ProcessErrors,
GetBasePath, HostsReload, ParseTypeChange, Wait) {
return function(params) {
var host_id = params.host_id;
var inventory_id = params.inventory_id;
var generator = GenerateForm;
var actions = [];
var html="<div class=\"host-groups-title\"><h4>{{ host.name }}</h4></div>\n";
html += "<div class=\"row host-groups\">\n";
html += "<div class=\"col-lg-6\">\n";
html += "<label>Available Groups:</label>\n";
html += "<select multiple class=\"form-control\" name=\"available-groups\" ng-model=\"selectedGroups\" ng-change=\"leftChange()\" " +
"ng-options=\"avail_group.name for avail_group in available_groups\"></select>\n";
html += "</div>\n";
html += "<div class=\"col-lg-6\">\n";
html += "<label>Belongs to Groups:</label>\n";
html += "<select multiple class=\"form-control\" name=\"selected-groups\" ng-model=\"assignedGroups\" ng-change=\"rightChange()\" " +
"ng-options=\"host_group.name for host_group in host_groups\"></select>\n";
html += "</div>\n";
html += "</div>\n";
html += "<div class=\"row host-group-buttons\">\n";
html += "<div class=\"col-lg-12\">\n";
html += "<button type=\"button\" ng-click=\"moveLeft()\" class=\"btn btn-sm btn-primary left-button\" ng-disabled=\"leftButtonDisabled\">" +
"<i class=\"icon-arrow-left\"></i></button>\n";
html += "<button type=\"button\" ng-click=\"moveRight()\" class=\"btn btn-sm btn-primary right-button\" ng-disabled=\"rightButtonDisabled\">" +
"<i class=\"icon-arrow-right\"></i></button>\n";
html += "<p>(move selected groups)</p>\n";
html += "</div>\n";
html += "</div>\n";
var defaultUrl = GetBasePath('hosts') + host_id + '/';
var scope = generator.inject(null, { mode: 'edit', modal: true, related: false, html: html });
for (var i=0; i < scope.hosts.length; i++) {
if (scope.hosts[i].id == host_id) {
scope.host = scope.hosts[i];
}
}
scope.selectedGroups = null;
scope.assignedGroups = null;
scope.leftButtonDisabled = true;
scope.rightButtonDisabled = true;
scope.formModalActionLabel = 'Save';
scope.formModalHeader = 'Host Groups';
scope.formModalCancelShow = true;
scope.formModalActionDisabled = true;
$('#form-modal .btn-none').removeClass('btn-none').addClass('btn-success');
if (scope.hostGroupChangeRemove) {
scope.hostGroupChangeRemove();
}
scope.hostGroupChangeRemove = scope.$on('hostGroupChange', function(e) {
actions.pop();
if (actions.length == 0) {
var postAction = function() {
setTimeout(function() { Wait('stop') }, 500);
};
HostsReload({ scope: scope, inventory_id: inventory_id, group_id: scope.group_id , action: postAction });
}
});
// Save changes
scope.formModalAction = function() {
var found;
$('#form-modal').modal('hide');
Wait('start');
// removed host from deleted groups
for (var i=0; i < scope.original_groups.length; i++) {
found = false;
for (var j=0; j < scope.host_groups.length; j++) {
if (scope.original_groups[i].id == scope.host_groups[j].id) {
found = true;
}
}
if (!found) {
// group was removed
actions.push({ group_id: scope.original_groups[i].id , action: 'delete' });
Rest.setUrl(GetBasePath('groups') + scope.original_groups[i].id + '/hosts/');
Rest.post({ id: host_id, disassociate: 1 })
.success( function(data, status, headers, config) {
scope.$emit('hostGroupChange');
})
.error( function(data, status, headers, config) {
scope.$emit('hostGroupChange');
ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Attempt to remove host from group ' + scope.original_groups[i].name +
' failed. POST returned status: ' + status });
});
}
}
// add host to new groups
for (var i=0; i < scope.host_groups.length; i++) {
found = false;
for (var j=0; j < scope.original_groups.length; j++) {
if (scope.original_groups[j].id == scope.host_groups[i].id) {
found = true;
}
}
if (!found) {
// group was added
actions.push({ group_id: scope.host_groups[i].id , action: 'add' });
Rest.setUrl(GetBasePath('groups') + scope.host_groups[i].id + '/hosts/');
Rest.post(scope.host)
.success( function(data, status, headers, config) {
scope.$emit('hostGroupChange');
})
.error( function(data, status, headers, config) {
scope.$emit('hostGroupChange');
ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Attempt to add host to group ' + scope.host_groups[i].name +
' failed. POST returned status: ' + status });
});
}
}
}
scope.leftChange = function() {
// Select/deselect on available groups list
if (scope.selectedGroups !== null && scope.selectedGroups.length > 0) {
scope.assignedGroups = null;
scope.leftButtonDisabled = true;
scope.rightButtonDisabled = false;
}
else {
scope.rightButtonDisabled = true;
}
}
scope.rightChange = function() {
// Select/deselect made on host groups list
if (scope.assignedGroups !== null && scope.assignedGroups.length > 0) {
scope.selectedGroups = null;
scope.leftButtonDisabled = false;
scope.rightButtonDisabled = true;
}
else {
scope.leftButtonDisabled = true;
}
}
scope.moveLeft = function() {
// Remove selected groups from the list of assigned groups
for (var i=0; i < scope.assignedGroups.length; i++){
for (var j=0 ; j < scope.host_groups.length; j++) {
if (scope.host_groups[j].id == scope.assignedGroups[i].id) {
scope.host_groups.splice(j,1);
break;
}
}
}
var found, placed;
for (var i=0; i < scope.assignedGroups.length; i++){
found = false;
for (var j=0; j < scope.available_groups.length && !found; j++){
if (scope.available_groups[j].id == scope.assignedGroups[i].id) {
found=true;
}
}
if (!found) {
placed = false;
for (var j=0; j < scope.available_groups.length && !placed; j++){
if (j == 0 && scope.assignedGroups[i].name.toLowerCase() < scope.available_groups[j].name.toLowerCase()) {
// prepend to the beginning of the array
placed=true;
scope.available_groups.unshift(scope.assignedGroups[i]);
}
else if (j + 1 < scope.available_groups.length) {
if (scope.assignedGroups[i].name.toLowerCase() > scope.available_groups[j].name.toLowerCase() &&
scope.assignedGroups[i].name.toLowerCase() < scope.available_groups[j + 1].name.toLowerCase() ) {
// insert into the middle of the array
placed = true;
scope.available_groups.splice(j + 1, 0, scope.assignedGroups[i]);
}
}
}
if (!placed) {
// append to the end of the array
scope.available_groups.push(scope.assignedGroups[i]);
}
}
}
scope.assignedGroups = null;
scope.leftButtonDisabled = true;
scope.rightButtonDisabled = true;
scope.formModalActionDisabled = false;
}
scope.moveRight = function() {
// Remove selected groups from list of available groups
for (var i=0; i < scope.selectedGroups.length; i++){
for (var j=0 ; j < scope.available_groups.length; j++) {
if (scope.available_groups[j].id == scope.selectedGroups[i].id) {
scope.available_groups.splice(j,1);
break;
}
}
}
var found, placed;
for (var i=0; i < scope.selectedGroups.length; i++){
found = false;
for (var j=0; j < scope.host_groups.length && !found; j++){
if (scope.host_groups[j].id == scope.selectedGroups[i].id) {
found=true;
}
}
if (!found) {
placed = false;
for (var j=0; j < scope.host_groups.length && !placed; j++){
if (j == 0 && scope.selectedGroups[i].name.toLowerCase() < scope.host_groups[j].name.toLowerCase()) {
// prepend to the beginning of the array
placed=true;
scope.host_groups.unshift(scope.selectedGroups[i]);
}
else if (j + 1 < scope.host_groups.length) {
if (scope.selectedGroups[i].name.toLowerCase() > scope.host_groups[j].name.toLowerCase() &&
scope.selectedGroups[i].name.toLowerCase() < scope.host_groups[j + 1].name.toLowerCase() ) {
// insert into the middle of the array
placed = true;
scope.host_groups.splice(j + 1, 0, scope.selectedGroups[i]);
}
}
}
if (!placed) {
// append to the end of the array
scope.host_groups.push(scope.selectedGroups[i]);
}
}
}
scope.selectedGroups = null;
scope.leftButtonDisabled = true;
scope.rightButtonDisabled = true;
scope.formModalActionDisabled = false;
}
// Load the host's current list of groups
scope.host_groups = [];
scope.original_groups = [];
scope.available_groups = [];
Rest.setUrl(scope.host.related.groups + '?order_by=name');
Rest.get()
.success( function(data, status, headers, config) {
for (var i=0; i < data.results.length; i++) {
scope.host_groups.push({
id: data.results[i].id,
name: data.results[i].name,
description: data.results[i].description
});
scope.original_groups.push({
id: data.results[i].id,
name: data.results[i].name,
description: data.results[i].description
});
}
var found;
for (var i=0; i < scope.inventory_groups.length; i++) {
found = false;
for (var j=0; j < scope.host_groups.length; j++) {
if (scope.inventory_groups[i].id == scope.host_groups[j].id) {
found = true;
}
}
if (!found) {
scope.available_groups.push(scope.inventory_groups[i]);
}
}
})
.error( function(data, status, headers, config) {
ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Failed to get current groups for host: ' + host_id + '. GET returned: ' + status });
});
if (scope.removeHostsReload) {
scope.removeHostsReload();
}
scope.removeHostsReload = scope.$on('hostsReload', function() {
HostsReload(params);
});
// After the group record is loaded, retrieve any group variables
if (scope.hostLoadedRemove) {
scope.hostLoadedRemove();
}
scope.hostLoadedRemove = scope.$on('hostLoaded', function() {
});
if (!scope.$$phase) {
scope.$digest();
}
}
}]);

View File

@ -22,6 +22,7 @@ angular.module('SelectionHelper', ['Utilities', 'RestServices'])
var returnToCaller = params.returnToCaller;
scope.selected = []; //array of selected row IDs
scope.formModalActionDisabled = true;
// toggle row selection
scope['toggle_' + list.iterator] = function(id, ischeckbox) {
@ -59,62 +60,71 @@ angular.module('SelectionHelper', ['Utilities', 'RestServices'])
}
}
}
if (scope.selected.length > 0) {
scope.formModalActionDisabled = false;
}
else {
scope.formModalActionDisabled = true;
}
}
scope.finishSelection = function() {
Rest.setUrl(target_url);
scope.queue = [];
function finished() {
scope.selected = [];
if (returnToCaller !== undefined) {
ReturnToCaller(returnToCaller);
if (target_url) {
scope.finishSelection = function() {
Rest.setUrl(target_url);
scope.queue = [];
scope.formModalActionDisabled = true;
function finished() {
scope.selected = [];
if (returnToCaller !== undefined) {
ReturnToCaller(returnToCaller);
}
else {
$('#form-modal').modal('hide');
scope.$emit('modalClosed');
}
}
if (scope.callFinishedRemove) {
scope.callFinishedRemove();
}
scope.callFinishedRemove = scope.$on('callFinished', function() {
// We call the API for each selected item. We need to hang out until all the api
// calls are finished.
if (scope.queue.length == scope.selected.length) {
var errors = 0;
for (var i=0; i < scope.queue.length; i++) {
if (scope.queue[i].result == 'error') {
ProcessErrors(scope, scope.queue[i].data, scope.queue[i].status, null,
{ hdr: 'POST Failure', msg: 'Failed to add ' + list.iterator +
'. POST returned status: ' + scope.queue[i].status });
errors++;
}
}
if (errors == 0) {
finished();
}
}
});
if (scope.selected.length > 0 ) {
for (var j=0; j < scope.selected.length; j++) {
Rest.post(scope.selected[j])
.success( function(data, status, headers, config) {
scope.queue.push({ result: 'success', data: data, status: status });
scope.$emit('callFinished');
})
.error( function(data, status, headers, config) {
scope.queue.push({ result: 'error', data: data, status: status, headers: headers });
scope.$emit('callFinished');
});
}
}
else {
$('#form-modal').modal('hide');
scope.$emit('modalClosed');
finished();
}
}
}
if (scope.callFinishedRemove) {
scope.callFinishedRemove();
}
scope.callFinishedRemove = scope.$on('callFinished', function() {
// We call the API for each selected item. We need to hang out until all the api
// calls are finished.
if (scope.queue.length == scope.selected.length) {
var errors = 0;
for (var i=0; i < scope.queue.length; i++) {
if (scope.queue[i].result == 'error') {
ProcessErrors(scope, scope.queue[i].data, scope.queue[i].status, null,
{ hdr: 'POST Failure', msg: 'Failed to add ' + list.iterator +
'. POST returned status: ' + scope.queue[i].status });
errors++;
}
}
if (errors == 0) {
finished();
}
}
});
if (scope.selected.length > 0 ) {
for (var j=0; j < scope.selected.length; j++) {
Rest.post(scope.selected[j])
.success( function(data, status, headers, config) {
scope.queue.push({ result: 'success', data: data, status: status });
scope.$emit('callFinished');
})
.error( function(data, status, headers, config) {
scope.queue.push({ result: 'error', data: data, status: status, headers: headers });
scope.$emit('callFinished');
});
}
}
else {
finished();
}
}
}
scope.formModalAction = scope.finishSelection;

View File

@ -59,7 +59,7 @@ angular.module('RelatedPaginateHelper', ['RefreshRelatedHelper', 'ngCookies'])
$cookieStore.put(iterator + 'PageSize', scope[iterator + 'PageSize']);
url = url.replace(/\/\?.*$/,'/');
url += (scope[iterator + 'SearchParams']) ? scope[iterator + 'SearchParams'] + '&page_size=' + scope[iterator + 'PageSize' ] :
url += (scope[iterator + 'SearchParams']) ? '?' + scope[iterator + 'SearchParams'] + '&page_size=' + scope[iterator + 'PageSize' ] :
'?page_size=' + scope[iterator + 'PageSize' ];
RefreshRelated({ scope: scope, set: set, iterator: iterator, url: url });
}

View File

@ -49,7 +49,7 @@ angular.module('CredentialsListDefinition', [])
mode: 'all', // One of: edit, select, all
ngClick: 'addCredential()',
basePaths: ['teams','users'], // base path must be in list, or action not available
"class": 'btn-success btn-mini',
"class": 'btn-success btn-sm',
awToolTip: 'Create a new credential'
}
},
@ -59,7 +59,7 @@ angular.module('CredentialsListDefinition', [])
ngClick: "editCredential(\{\{ credential.id \}\})",
icon: 'icon-edit',
label: 'Edit',
"class": 'btn-mini btn-default',
"class": 'btn-xs btn-default',
awToolTip: 'View/Edit credential'
},
@ -67,7 +67,7 @@ angular.module('CredentialsListDefinition', [])
ngClick: "deleteCredential(\{\{ credential.id \}\},'\{\{ credential.name \}\}')",
icon: 'icon-trash',
label: 'Delete',
"class": 'btn-mini btn-danger',
"class": 'btn-xs btn-danger',
awToolTip: 'Delete credential'
}
}

View File

@ -35,7 +35,7 @@ angular.module('GroupListDefinition', [])
dataContainer: '#form-modal .modal-content',
icon: "icon-question-sign",
mode: 'all',
'class': 'btn-mini btn-info btn-help',
'class': 'btn-xs btn-info btn-help pull-right',
awToolTip: 'Click for help',
dataTitle: 'Adding Groups',
id: 'group-help-button',
@ -48,7 +48,7 @@ angular.module('GroupListDefinition', [])
label: 'Edit',
ngClick: "editGroup(\{\{ group.id \}\})",
icon: 'icon-edit',
"class": 'btn-mini btn-default',
"class": 'btn-xs btn-default',
awToolTip: 'View/Edit group'
},
@ -56,7 +56,7 @@ angular.module('GroupListDefinition', [])
label: 'Delete',
ngClick: "deleteGroup(\{\{ group.id \}\},'\{\{ group.name \}\}')",
icon: 'icon-trash',
"class": 'btn-mini btn-danger',
"class": 'btn-xs btn-danger',
awToolTip: 'Delete group'
}
}

View File

@ -35,7 +35,7 @@ angular.module('HostListDefinition', [])
dataContainer: '#form-modal .modal-content',
icon: "icon-question-sign",
mode: 'all',
'class': 'btn-mini btn-info btn-help',
'class': 'btn-xs btn-info btn-help',
awToolTip: 'Click for help',
dataTitle: 'Selecting Hosts',
iconSize: 'large',
@ -48,7 +48,7 @@ angular.module('HostListDefinition', [])
label: 'Edit',
ngClick: "editHost(\{\{ host.id \}\})",
icon: 'icon-edit',
"class": 'btn-mini btn-default',
"class": 'btn-xs btn-default',
awToolTip: 'View/Edit host'
},
@ -56,7 +56,7 @@ angular.module('HostListDefinition', [])
label: 'Delete',
ngClick: "deleteHost(\{\{ host.id \}\},'\{\{ host.name \}\}')",
icon: 'icon-trash',
"class": 'btn-mini btn-danger',
"class": 'btn-xs btn-danger',
awToolTip: 'Delete host'
}
}

View File

@ -54,7 +54,7 @@ angular.module('InventoriesListDefinition', [])
icon: 'icon-plus',
mode: 'all', // One of: edit, select, all
ngClick: 'addInventory()',
"class": 'btn-mini btn-success',
"class": 'btn-sm btn-success',
awToolTip: 'Create a new inventory'
}
},
@ -64,7 +64,7 @@ angular.module('InventoriesListDefinition', [])
label: 'Edit',
ngClick: "editInventory(\{\{ inventory.id \}\})",
icon: 'icon-edit',
"class": 'btn-mini btn-default',
"class": 'btn-xs btn-default',
awToolTip: 'View/Edit inventory'
},
@ -72,7 +72,7 @@ angular.module('InventoriesListDefinition', [])
label: 'Delete',
ngClick: "deleteInventory(\{\{ inventory.id \}\},'\{\{ inventory.name \}\}')",
icon: 'icon-trash',
"class": 'btn-mini btn-danger',
"class": 'btn-xs btn-danger',
awToolTip: 'Delete inventory'
}
}

View File

@ -63,14 +63,14 @@ angular.module('JobEventsListDefinition', [])
icon: 'icon-refresh',
label: 'Refresh',
awToolTip: 'Refresh the page',
"class": 'btn-mini btn-success',
"class": 'btn-sm btn-success',
mode: 'all'
},
edit: {
label: 'Details',
ngClick: "jobDetails()",
icon: 'icon-zoom-in',
"class": 'btn btn-default btn-mini',
"class": 'btn btn-default btn-sm',
awToolTip: 'Edit job details',
mode: 'all'
},
@ -78,7 +78,7 @@ angular.module('JobEventsListDefinition', [])
label: 'Hosts',
icon: 'icon-th-large',
ngClick: "jobSummary()",
"class": 'btn btn-default btn-mini',
"class": 'btn btn-default btn-sm',
awToolTip: 'View host summary',
mode: 'all'
}
@ -89,7 +89,7 @@ angular.module('JobEventsListDefinition', [])
label: 'View',
ngClick: "viewJobEvent(\{\{ jobevent.id \}\})",
icon: 'icon-zoom-in',
"class": 'btn-default btn-mini',
"class": 'btn-default btn-xs',
awToolTip: 'View event details'
}
}

View File

@ -60,7 +60,7 @@ angular.module('JobHostDefinition', [])
label: 'Refresh',
icon: 'icon-refresh',
ngClick: "refresh()",
"class": 'btn-success btn-mini',
"class": 'btn-success btn-sm',
awToolTip: 'Refresh the page',
mode: 'all'
},
@ -68,7 +68,7 @@ angular.module('JobHostDefinition', [])
label: 'Details',
icon: 'icon-edit',
ngClick: "jobDetails()",
"class": 'btn btn-default btn-mini',
"class": 'btn btn-default btn-sm',
awToolTip: 'Edit job details',
mode: 'all'
},
@ -76,7 +76,7 @@ angular.module('JobHostDefinition', [])
label: 'Events',
icon: 'icon-list-ul',
ngClick: "jobEvents()",
"class": 'btn btn-default btn-mini',
"class": 'btn btn-default btn-sm',
awToolTip: 'View job events',
mode: 'all'
},
@ -87,11 +87,11 @@ angular.module('JobHostDefinition', [])
"<dt>Unreachable</dt><dd>Times the ansible server could not reach the host.</dd>\n" +
"<dt>Skipped</dt><dd>Tasks bypassed and not performed on the host due to prior task failure or the host being unreachable.</dd>\n" +
"</dl>\n",
dataPlacement: 'right',
dataContainer: ".container",
dataPlacement: 'left',
dataContainer: "body",
icon: "icon-question-sign",
mode: 'all',
'class': 'btn-info btn-mini btn-help',
'class': 'btn-info btn-xs btn-help pull-right',
awToolTip: 'Click for help',
dataTitle: 'Job Host Summary',
id: 'jobhost-help-button',

View File

@ -34,7 +34,7 @@ angular.module('JobTemplatesListDefinition', [])
icon: 'icon-plus',
mode: 'all', // One of: edit, select, all
ngClick: 'addJobTemplate()',
"class": 'btn-success btn-mini',
"class": 'btn-success btn-sm',
basePaths: ['job_templates'],
awToolTip: 'Create a new template'
}
@ -46,13 +46,13 @@ angular.module('JobTemplatesListDefinition', [])
ngClick: "editJobTemplate(\{\{ job_template.id \}\})",
icon: 'icon-edit',
awToolTip: 'Edit template',
"class": 'btn-default btn-mini'
"class": 'btn-default btn-xs'
},
submit: {
label: 'Launch',
icon: 'icon-rocket',
mode: 'all',
"class": 'btn-mini btn-success',
"class": 'btn-xs btn-success',
ngClick: 'submitJob(\{\{ job_template.id \}\})',
awToolTip: 'Start a job using this template'
},
@ -60,7 +60,7 @@ angular.module('JobTemplatesListDefinition', [])
label: 'Delete',
ngClick: "deleteJobTemplate(\{\{ job_template.id \}\},'\{\{ job_template.name \}\}')",
icon: 'icon-trash',
"class": 'btn-danger btn-mini',
"class": 'btn-danger btn-xs',
awToolTip: 'Delete template'
}
}

View File

@ -57,7 +57,7 @@ angular.module('JobsListDefinition', [])
actions: {
refresh: {
label: 'Refresh',
"class": 'btn-success btn-mini',
"class": 'btn-success btn-sm',
ngClick: "refreshJob(\{\{ job.id \}\})",
icon: 'icon-refresh',
awToolTip: 'Refresh the page',
@ -70,7 +70,7 @@ angular.module('JobsListDefinition', [])
label: 'Hosts',
icon: 'icon-th-large',
ngClick: "viewSummary(\{{ job.id \}\}, '\{\{ job.name \}\}')",
"class": 'btn btn-default btn-mini',
"class": 'btn btn-default btn-xs',
awToolTip: 'View host summary',
ngDisabled: "job.status == 'new'"
},
@ -79,7 +79,7 @@ angular.module('JobsListDefinition', [])
icon: 'icon-list-ul',
mode: 'all',
ngClick: "viewEvents(\{{ job.id \}\}, '\{\{ job.name \}\}')",
"class": 'btn btn-default btn-mini',
"class": 'btn btn-default btn-xs',
awToolTip: 'View events',
ngDisabled: "job.status == 'new'"
},
@ -87,21 +87,21 @@ angular.module('JobsListDefinition', [])
label: 'Details',
icon: 'icon-zoom-in',
ngClick: "editJob(\{\{ job.id \}\}, '\{\{ job.name \}\}')",
"class": 'btn btn-default btn-mini',
"class": 'btn btn-default btn-xs',
awToolTip: 'View job details'
},
rerun: {
icon: 'icon-retweet',
mode: 'all',
ngClick: "submitJob(\{\{ job.id \}\}, '\{\{ job.summary_fields.job_template.name \}\}' )",
"class": 'btn-success btn-mini',
"class": 'btn-success btn-xs',
awToolTip: 'Re-run this job'
},
cancel: {
icon: 'icon-minus-sign',
mode: 'all',
ngClick: 'deleteJob(\{\{ job.id \}\})',
"class": 'btn-danger btn-mini',
"class": 'btn-danger btn-xs',
awToolTip: 'Cancel job',
ngShow: "job.status == 'pending' || job.status == 'running'"
},
@ -109,7 +109,7 @@ angular.module('JobsListDefinition', [])
icon: 'icon-trash',
mode: 'all',
ngClick: 'deleteJob(\{\{ job.id \}\})',
"class": 'btn-danger btn-mini',
"class": 'btn-danger btn-xs',
awToolTip: 'Delete this job',
ngShow: "job.status != 'pending' && job.status != 'running'"
}

View File

@ -33,7 +33,7 @@ angular.module('OrganizationListDefinition', [])
icon: 'icon-plus',
mode: 'all', // One of: edit, select, all
ngClick: 'addOrganization()',
"class": 'btn-success btn-mini',
"class": 'btn-success btn-sm',
awToolTip: 'Create a new organization'
}
},
@ -43,7 +43,7 @@ angular.module('OrganizationListDefinition', [])
label: 'Edit',
ngClick: "editOrganization(\{\{ organization.id \}\})",
icon: 'icon-edit',
"class": 'btn-mini btn-default',
"class": 'btn-xs btn-default',
awToolTip: 'View/Edit organization'
},
@ -51,7 +51,7 @@ angular.module('OrganizationListDefinition', [])
label: 'Delete',
ngClick: "deleteOrganization(\{\{ organization.id \}\},'\{\{ organization.name \}\}')",
icon: 'icon-trash',
"class": 'btn-mini btn-danger',
"class": 'btn-xs btn-danger',
awToolTip: 'Delete organization'
}
}

View File

@ -47,7 +47,7 @@ angular.module('PermissionListDefinition', [])
label: 'Create New',
mode: 'all', // One of: edit, select, all
ngClick: 'addPermission()',
"class": 'btn-success btn-mini',
"class": 'btn-success btn-sm',
awToolTip: 'Add a new permission'
}
},
@ -57,7 +57,7 @@ angular.module('PermissionListDefinition', [])
label: 'Edit',
ngClick: "editPermission(\{\{ permission.id \}\})",
icon: 'icon-edit',
"class": 'btn-mini btn-default',
"class": 'btn-xs btn-default',
awToolTip: 'View/Edit permission'
},
@ -65,7 +65,7 @@ angular.module('PermissionListDefinition', [])
label: 'Delete',
ngClick: "deletePermission(\{\{ permission.id \}\},'\{\{ permission.name \}\}')",
icon: 'icon-trash',
"class": 'btn-mini btn-danger',
"class": 'btn-xs btn-danger',
awToolTip: 'Delete permission'
}
}

View File

@ -35,7 +35,7 @@ angular.module('ProjectsListDefinition', [])
icon: 'icon-plus',
mode: 'all', // One of: edit, select, all
ngClick: 'addProject()',
"class": 'btn-success btn-mini',
"class": 'btn-success btn-sm',
awToolTip: 'Create a new project'
}
},
@ -45,7 +45,7 @@ angular.module('ProjectsListDefinition', [])
label: 'Edit',
ngClick: "editProject(\{\{ project.id \}\})",
icon: 'icon-edit',
"class": 'btn-mini btn-default',
"class": 'btn-xs btn-default',
awToolTip: 'View/edit project'
},
@ -53,7 +53,7 @@ angular.module('ProjectsListDefinition', [])
label: 'Delete',
ngClick: "deleteProject(\{\{ project.id \}\},'\{\{ project.name \}\}')",
icon: 'icon-trash',
"class": 'btn-mini btn-danger',
"class": 'btn-xs btn-danger',
awToolTip: 'Delete project'
}
}

View File

@ -40,7 +40,7 @@ angular.module('TeamsListDefinition', [])
icon: 'icon-plus',
mode: 'all', // One of: edit, select, all
ngClick: 'addTeam()',
"class": 'btn-mini btn-success',
"class": 'btn-sm btn-success',
awToolTip: 'Create a new team'
}
},
@ -50,7 +50,7 @@ angular.module('TeamsListDefinition', [])
label: 'Edit',
ngClick: "editTeam(\{\{ team.id \}\})",
icon: 'icon-edit',
"class": 'btn-mini btn-default',
"class": 'btn-xs btn-default',
awToolTip: 'View/Edit team'
},
@ -58,7 +58,7 @@ angular.module('TeamsListDefinition', [])
label: 'Delete',
ngClick: "deleteTeam(\{\{ team.id \}\},'\{\{ team.name \}\}')",
icon: 'icon-trash',
"class": 'btn-mini btn-danger',
"class": 'btn-xs btn-danger',
awToolTip: 'Delete team'
}
}

View File

@ -40,7 +40,7 @@ angular.module('UserListDefinition', [])
mode: 'all', // One of: edit, select, all
ngClick: 'addUser()',
basePaths: ['organizations','users'], // base path must be in list, or action not available
"class": 'btn-success btn-mini',
"class": 'btn-success btn-sm',
awToolTip: 'Create a new user'
}
},
@ -50,7 +50,7 @@ angular.module('UserListDefinition', [])
label: 'Edit',
ngClick: "editUser(\{\{ user.id \}\})",
icon: 'icon-edit',
"class": 'btn-mini btn-default',
"class": 'btn-xs btn-default',
awToolTip: 'View/Edit user'
},
@ -58,7 +58,7 @@ angular.module('UserListDefinition', [])
label: 'Delete',
ngClick: "deleteUser(\{\{ user.id \}\},'\{\{ user.username \}\}')",
icon: 'icon-trash',
"class": 'btn-mini btn-danger',
"class": 'btn-xs btn-danger',
awToolTip: 'Delete user'
}
}

View File

@ -307,6 +307,7 @@ legend {
}
.page-size {
height: 25px;
font-size: 10.5px;
line-height: normal;
}
@ -573,6 +574,10 @@ input[type="text"].job-successful {
}
}
a.disabled {
color: @grey;
}
.navbar-form {
display: inline-block;
float: right;
@ -642,6 +647,25 @@ input[type="text"].job-successful {
margin: 5px 0;
}
.host-groups {
margin-top: 15px;
select {
height: 150px;
}
}
.host-group-buttons {
margin-top: 20px;
text-align: center;
p {
padding-top: 10px;
font-size: 12px;
font-weight: normal;
}
}
.search-tree {
ul {
list-style-type: none;
@ -671,6 +695,10 @@ input[type="text"].job-successful {
}
}
.disabled {
color: @grey;
}
.parse-selection {
display: inline-block;
margin: 5px 0 8px 0;

View File

@ -279,6 +279,35 @@ angular.module('AWDirectives', ['RestServices', 'Utilities', 'AuthService', 'Hos
}
}])
.directive('awMultiSelect', [ function() {
return {
require: 'ngModel',
link: function(scope, elm, attrs, ctrl) {
$(elm).multiselect ({
buttonClass: 'btn-default, btn-mini',
buttonWidth: 'auto',
buttonContainer: '<div class="btn-group" />',
maxHeight: false,
buttonText: function(options) {
if (options.length == 0) {
return 'None selected <b class="caret"></b>';
}
else if (options.length > 3) {
return options.length + ' selected <b class="caret"></b>';
}
else {
var selected = '';
options.each(function() {
selected += $(this).text() + ', ';
});
return selected.substr(0, selected.length -2) + ' <b class="caret"></b>';
}
}
});
}
}
}])
//
// Enable jqueryui spinner widget on a numeric input field
//
@ -317,6 +346,9 @@ angular.module('AWDirectives', ['RestServices', 'Utilities', 'AuthService', 'Hos
}
}])
/* This has become more than a simple directive. All the logic for building the group selector tree
on the Hosts tab is here. Probably needs to move into the Hosts helper and/or Inventory helper.
*/
.directive('awTree', ['Rest', 'ProcessErrors', 'Authorization', '$compile', '$rootScope', 'Wait',
function(Rest, ProcessErrors, Authorization, $compile, $rootScope, Wait) {
return {
@ -360,7 +392,8 @@ angular.module('AWDirectives', ['RestServices', 'Utilities', 'AuthService', 'Hos
else {
group = null;
title = 'All Hosts'
}
}
// The following will trigger the host list to load. See Inventory.js controller.
scope.$emit('refreshHost', group, title);
}
@ -527,6 +560,9 @@ angular.module('AWDirectives', ['RestServices', 'Utilities', 'AuthService', 'Hos
activateElm.bind('click', activate);
}
// Responds to hostTabInit event, thrown from Hosts.js helper. Once the initial
// inventory node is loaded, force the root level groups to load and populate the
// host list with All Hosts.
if ($rootScope.hostTabInitRemove) {
$rootScope.hostTabInitRemove();
}

View File

@ -52,15 +52,18 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies'])
if (options.buildTree) {
element.html(this.buildTree(options));
}
else if (options.html) {
element.html(options.html);
}
else {
element.html(this.build(options));
element.html(this.build(options));
}
this.scope = element.scope(); // Set scope specific to the element we're compiling, avoids circular reference
// From here use 'scope' to manipulate the form, as the form is not in '$scope'
$compile(element)(this.scope);
if (options.buildTree == undefined || options.buildTree == false) {
if (!options.buildTree == false && !options.html) {
// Reset the scope to prevent displaying old data from our last visit to this form
for (var fld in form.fields) {
this.scope[fld] = null;
@ -86,7 +89,10 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies'])
});
if (options.modal) {
this.scope.formHeader = (options.mode == 'add') ? form.addTitle : form.editTitle; //Default title for default modal
this.scope.formModalActionDisabled = false;
if (form) {
this.scope.formHeader = (options.mode == 'add') ? form.addTitle : form.editTitle; //Default title for default modal
}
this.scope.formModalInfo = false //Disable info button for default modal
if (options.modal_selector) {
$(options.modal_selector).modal({ show: true, backdrop: 'static', keyboard: true });
@ -954,7 +960,7 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies'])
"aw-tool-tip=\"Add an existing host\" data-placement=\"bottom\"><i class=\"icon-check\"></i> Add Existing Host</a></li>\n";
html += "<li><a href=\"\" ng-click=\"createHost()\" ng-hide=\"hostCreateHide\" " +
"aw-tool-tip=\"Create a new host\" data-placement=\"bottom\"><i class=\"icon-plus\"></i> Create New Host</a></li>\n";
html += "<li><a href=\"\" ng-click=\"deleteHost()\" ng-hide=\"hostDeleteHide\" " +
html += "<li><a href=\"\" ng-click=\"deleteHost()\" ng-class=\"hostDeleteDisabledClass\" ng-disabled=\"hostDeleteDisabled\" " +
"aw-tool-tip=\"Delete selected hosts\" data-placement=\"bottom\"><i class=\"icon-trash\"></i> Delete Hosts</a></li>\n";
html += "<li><a class=\"status\" ng-show=\"treeLoading\" href=\"\"><i class=\"icon-spinner icon-spin icon-large\"></i> Loading...</a></li>\n";
html += "</ul>\n";
@ -988,7 +994,6 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies'])
html += "<table class=\"" + form.related[itm].iterator + " table table-condensed table-hover\">\n";
html += "<thead>\n";
html += "<tr>\n";
//html += "<th>#</th>\n";
html += "<th><input type=\"checkbox\" ng-model=\"toggleAllFlag\" ng-change=\"toggleAllHosts()\" aw-tool-tip=\"Select all hosts\" " +
"data-placement=\"top\"></th>\n";
for (var fld in form.related[itm].fields) {
@ -1030,11 +1035,25 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies'])
for (var fld in form.related[itm].fields) {
cnt++;
rfield = form.related[itm].fields[fld];
html += Column({ list: form.related[itm], fld: fld, options: options, base: base })
if (fld == 'groups' ) {
// generate group form control/button widget
html += "<td>";
html += "<div class=\"input-group input-group-sm\">\n";
html += "<span class=\"input-group-btn\">\n";
html += "<button class=\"btn btn-default\" type=\"button\" ng-click=\"editHostGroups({{ host.id }})\"><i class=\"icon-list\"></i></button>\n";
html += "</span>\n";
html += "<input type=\"text\" ng-model=\"host.groups\" class=\"form-control\" disabled=\"disabled\" >\n";
html += "</div>\n";
//html += "<a href=\"\"><i class=\"icon-list\"></i></button> \{\{ host.groups \}\}</a>";
html += "</td>\n";
}
else {
html += Column({ list: form.related[itm], fld: fld, options: options, base: base });
}
}
html += "<td>";
html += "<div class=\"btn-group\">\n";
html += "<div class=\"btn-group btn-group-sm\">\n";
html += "<button type=\"button\" class=\"btn btn-default btn-mini dropdown-toggle\" data-toggle=\"dropdown\">";
html += "View <span class=\"caret\"></span></button>\n";
html += "<ul class=\"dropdown-menu pull-right\" role=\"menu\" aria-labelledby=\"dropdownMenu1\">\n";

View File

@ -211,10 +211,12 @@ angular.module('GeneratorHelpers', ['GeneratorHelpers'])
html += (params.size) ? params.size : "col-lg-4";
html += "\">\n";
html += (label) ? "<label>" + label +"</label>" : "";
html += "<div class=\"input-group\">\n";
html += "<div class=\"input-group";
html += (useMini) ? " input-group-sm" : " input-group-sm";
html += "\">\n";
html += "<div class=\"input-group-btn\">\n";
html += "<button type=\"button\" class=\"btn ";
html += (useMini) ? "btn-mini " : "btn-small ";
// html += (useMini) ? "btn-mini " : "btn-small ";
html += "dropdown-toggle\" data-toggle=\"dropdown\">\n";
html += "<span ng-bind=\"" + iterator + "SearchFieldLabel\"></span>\n";
html += "<span class=\"caret\"></span>\n";
@ -232,18 +234,18 @@ angular.module('GeneratorHelpers', ['GeneratorHelpers'])
html += "</div><!-- input-group-btn -->\n";
html += "<select ng-show=\"" + iterator + "SelectShow\" ng-model=\""+ iterator + "SearchSelectValue\" ng-change=\"search('" + iterator + "')\" ";
html += "ng-options=\"c.name for c in " + iterator + "SearchSelectOpts\" class=\"search-select";
html += (useMini) ? " input-mini" : " input-small";
html += "ng-options=\"c.name for c in " + iterator + "SearchSelectOpts\" class=\"form-control search-select";
//html += (useMini) ? " input-sm" : "";
html += "\"></select>\n";
html += "<input type=\"text\" ng-hide=\"" + iterator + "SelectShow || " + iterator + "InputHide\" class=\"form-control ";
html += (useMini) ? " input-mini" : " input-small";
//html += (useMini) ? " input-sm" : " input-sm";
html += "\" ng-model=\"" + iterator + "SearchValue\" ng-change=\"search('" + iterator +
"')\" placeholder=\"Search\" type=\"text\" >\n";
html += "<div class=\"input-group-btn\">\n";
html += "<button type=\"button\" ng-hide=\"" + iterator + "SelectShow || " + iterator + "HideSearchType || " + iterator + "InputHide\" class=\"btn ";
html += (useMini) ? "btn-mini " : "btn-small ";
//html += (useMini) ? "btn-x " : "btn-small ";
html += "dropdown-toggle\" data-toggle=\"dropdown\">\n";
html += "<span ng-bind=\"" + iterator + "SearchTypeLabel\"></span>\n";
html += "<span class=\"caret\"></span>\n";
@ -279,21 +281,20 @@ angular.module('GeneratorHelpers', ['GeneratorHelpers'])
html += (useMini) ? " related-footer" : "";
html += "\">\n";
html += "<form class=\"form-inline\">\n";
html += "<button class=\"previous btn btn-light";
html += (useMini) ? " btn-mini\" " : "\" ";
html += "<button type=\"button\" class=\"previous btn btn-light";
html += (useMini) ? " btn-xs\" " : "\" ";
html += "ng-click=\"prevSet('" + set + "','" + iterator + "')\" " +
"ng-disabled=\"" + iterator + "PrevUrl == null || " + iterator + "PrevUrl == undefined\"><i class=\"icon-caret-left\"></i> Prev</button>\n";
html += "<button class=\"next btn btn-light";
html += (useMini) ? " btn-mini\" " : "\" ";
html += "<button type=\"button\" class=\"next btn btn-light";
html += (useMini) ? " btn-xs\" " : "\" ";
html += " ng-click=\"nextSet('" + set + "','" + iterator + "')\"" +
"ng-disabled=\"" + iterator + "NextUrl == null || " + iterator + "NextUrl == undefined\">Next <i class=\"icon-caret-right\"></i></button>\n";
if (mode != 'lookup') {
html += "<label class=\"page-size-label\">Rows per page: </label>\n";
html += "<select ng-model=\"" + iterator + "PageSize\" ng-change=\"changePageSize('" +
set + "'," + "'" + iterator + "')\" class=\"input-mini";
//html += (useMini) ? " field-mini-height" : "";
html += " page-size\">\n";
set + "'," + "'" + iterator + "')\" ";
html += "class=\"page-size\">\n";
html += "<option value=\"10\" selected>10</option>\n";
html += "<option value=\"20\" selected>20</option>\n";
html += "<option value=\"40\">40</option>\n";

View File

@ -33,7 +33,7 @@ angular.module('ListGenerator', ['GeneratorHelpers'])
// 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-small";
html += (btn['class']) ? " " + btn['class'] : " btn-sm";
html += (btn['awPopOver']) ? " help-link-white" : "";
html += "\" ";
html += (btn.id) ? "id=\"" + btn.id + "\" " : "";
@ -96,6 +96,7 @@ angular.module('ListGenerator', ['GeneratorHelpers'])
if (options.mode == 'lookup') {
// options should include {hdr: <dialog header>, action: <function...> }
this.scope.formModalActionDisabled = false;
this.scope.lookupHeader = options.hdr;
$('#lookup-modal').modal({ backdrop: 'static', keyboard: true });
$(document).bind('keydown', function(e) {
@ -143,17 +144,17 @@ angular.module('ListGenerator', ['GeneratorHelpers'])
}
if (options.mode == 'lookup' || options.id != undefined) {
html += SearchWidget({ iterator: list.iterator, template: list, mini: true , size: 'col-lg-6' });
html += SearchWidget({ iterator: list.iterator, template: list, mini: true , size: 'col-lg-8' });
}
else {
html += SearchWidget({ iterator: list.iterator, template: list, mini: false });
html += SearchWidget({ iterator: list.iterator, template: list, mini: true });
}
if (options.mode != 'lookup') {
//actions
var base = $location.path().replace(/^\//,'').split('/')[0];
html += "<div class=\"";
html += (options.id != undefined) ? "col-lg-5" : "col-lg-7";
html += (options.id != undefined) ? "col-lg-3" : "col-lg-7";
html += "\">\n";
for (action in list.actions) {
if (list.actions[action].mode == 'all' || list.actions[action].mode == options.mode) {
@ -170,7 +171,7 @@ angular.module('ListGenerator', ['GeneratorHelpers'])
dataPlacement: 'left',
dataContainer: 'body',
icon: "icon-question-sign",
'class': 'btn-mini btn-help btn-info',
'class': 'btn-sm btn-help btn-info',
awToolTip: 'Click for help',
dataTitle: 'Help',
iconSize: 'large'

View File

@ -222,7 +222,6 @@ angular.module('Utilities',[])
var spinnyh = $('.spinny').height();
var x = (docw - spinnyw) / 2;
var y = (doch - spinnyh) / 2;
console.log($(document));
$('.overlay').css({
width: $('html').width(),
height: $(document).height() + 200
@ -230,10 +229,10 @@ angular.module('Utilities',[])
$('.spinny').css({
top: y,
left: x
}).fadeIn(500);
}).fadeIn(400);
}
else {
$('.spinny, .overlay').fadeOut(2000);
$('.spinny, .overlay').fadeOut(1000);
}
}
}]);

View File

@ -49,7 +49,7 @@ if (!jQuery) { throw new Error("Bootstrap requires jQuery") }
// http://blog.alexmaccaw.com/css-transitions
$.fn.emulateTransitionEnd = function (duration) {
var called = false, $el = this
var called = false, $el = this
$(this).one($.support.transition.end, function () { called = true })
var callback = function () { if (!called) $($el).trigger($.support.transition.end) }
setTimeout(callback, duration)
@ -219,7 +219,9 @@ if (!jQuery) { throw new Error("Bootstrap requires jQuery") }
var $parent = this.$element.closest('[data-toggle="buttons"]')
if ($parent.length) {
var $input = this.$element.find('input').prop('checked', !this.$element.hasClass('active'))
var $input = this.$element.find('input')
.prop('checked', !this.$element.hasClass('active'))
.trigger('change')
if ($input.prop('type') === 'radio') $parent.find('.active').removeClass('active')
}
@ -235,7 +237,7 @@ if (!jQuery) { throw new Error("Bootstrap requires jQuery") }
$.fn.button = function (option) {
return this.each(function () {
var $this = $(this)
var data = $this.data('button')
var data = $this.data('bs.button')
var options = typeof option == 'object' && option
if (!data) $this.data('bs.button', (data = new Button(this, options)))
@ -312,6 +314,7 @@ if (!jQuery) { throw new Error("Bootstrap requires jQuery") }
Carousel.DEFAULTS = {
interval: 5000
, pause: 'hover'
, wrap: true
}
Carousel.prototype.cycle = function (e) {
@ -376,12 +379,15 @@ if (!jQuery) { throw new Error("Bootstrap requires jQuery") }
var fallback = type == 'next' ? 'first' : 'last'
var that = this
if (!$next.length) {
if (!this.options.wrap) return
$next = this.$element.find('.item')[fallback]()
}
this.sliding = true
isCycling && this.pause()
$next = $next.length ? $next : this.$element.find('.item')[fallback]()
var e = $.Event('slide.bs.carousel', { relatedTarget: $next[0], direction: direction })
if ($next.hasClass('active')) return
@ -533,7 +539,7 @@ if (!jQuery) { throw new Error("Bootstrap requires jQuery") }
this.$element.trigger(startEvent)
if (startEvent.isDefaultPrevented()) return
var actives = this.$parent && this.$parent.find('> .accordion-group > .in')
var actives = this.$parent && this.$parent.find('> .panel > .in')
if (actives && actives.length) {
var hasData = actives.data('bs.collapse')
@ -654,7 +660,7 @@ if (!jQuery) { throw new Error("Bootstrap requires jQuery") }
var $parent = parent && $(parent)
if (!data || !data.transitioning) {
if ($parent) $parent.find('[data-toggle=collapse][data-parent=' + parent + ']').not($this).addClass('collapsed')
if ($parent) $parent.find('[data-toggle=collapse][data-parent="' + parent + '"]').not($this).addClass('collapsed')
$this[$target.hasClass('in') ? 'addClass' : 'removeClass']('collapsed')
}
@ -705,7 +711,7 @@ if (!jQuery) { throw new Error("Bootstrap requires jQuery") }
clearMenus()
if (!isActive) {
if ('ontouchstart' in document.documentElement) {
if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) {
// if mobile we we use a backdrop because click events don't delegate
$('<div class="dropdown-backdrop"/>').insertAfter($(this)).on('click', clearMenus)
}
@ -717,9 +723,9 @@ if (!jQuery) { throw new Error("Bootstrap requires jQuery") }
$parent
.toggleClass('open')
.trigger('shown.bs.dropdown')
}
$this.focus()
$this.focus()
}
return false
}
@ -845,11 +851,11 @@ if (!jQuery) { throw new Error("Bootstrap requires jQuery") }
var Modal = function (element, options) {
this.options = options
this.$element = $(element).on('click.dismiss.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this))
this.$element = $(element)
this.$backdrop =
this.isShown = null
if (this.options.remote) this.$element.find('.modal-body').load(this.options.remote)
if (this.options.remote) this.$element.load(this.options.remote)
}
Modal.DEFAULTS = {
@ -858,13 +864,13 @@ if (!jQuery) { throw new Error("Bootstrap requires jQuery") }
, show: true
}
Modal.prototype.toggle = function () {
return this[!this.isShown ? 'show' : 'hide']()
Modal.prototype.toggle = function (_relatedTarget) {
return this[!this.isShown ? 'show' : 'hide'](_relatedTarget)
}
Modal.prototype.show = function () {
Modal.prototype.show = function (_relatedTarget) {
var that = this
var e = $.Event('show.bs.modal')
var e = $.Event('show.bs.modal', { relatedTarget: _relatedTarget })
this.$element.trigger(e)
@ -874,6 +880,8 @@ if (!jQuery) { throw new Error("Bootstrap requires jQuery") }
this.escape()
this.$element.on('click.dismiss.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this))
this.backdrop(function () {
var transition = $.support.transition && that.$element.hasClass('fade')
@ -893,13 +901,15 @@ if (!jQuery) { throw new Error("Bootstrap requires jQuery") }
that.enforceFocus()
var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget })
transition ?
that.$element
that.$element.find('.modal-dialog') // wait for modal to slide in
.one($.support.transition.end, function () {
that.$element.focus().trigger('shown.bs.modal')
that.$element.focus().trigger(e)
})
.emulateTransitionEnd(300) :
that.$element.focus().trigger('shown.bs.modal')
that.$element.focus().trigger(e)
})
}
@ -921,6 +931,7 @@ if (!jQuery) { throw new Error("Bootstrap requires jQuery") }
this.$element
.removeClass('in')
.attr('aria-hidden', true)
.off('click.dismiss.modal')
$.support.transition && this.$element.hasClass('fade') ?
this.$element
@ -973,7 +984,7 @@ if (!jQuery) { throw new Error("Bootstrap requires jQuery") }
this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />')
.appendTo(document.body)
this.$element.on('click', $.proxy(function (e) {
this.$element.on('click.dismiss.modal', $.proxy(function (e) {
if (e.target !== e.currentTarget) return
this.options.backdrop == 'static'
? this.$element[0].focus.call(this.$element[0])
@ -1012,15 +1023,15 @@ if (!jQuery) { throw new Error("Bootstrap requires jQuery") }
var old = $.fn.modal
$.fn.modal = function (option) {
$.fn.modal = function (option, _relatedTarget) {
return this.each(function () {
var $this = $(this)
var data = $this.data('bs.modal')
var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option)
if (!data) $this.data('bs.modal', (data = new Modal(this, options)))
if (typeof option == 'string') data[option]()
else if (options.show) data.show()
if (typeof option == 'string') data[option](_relatedTarget)
else if (options.show) data.show(_relatedTarget)
})
}
@ -1043,28 +1054,26 @@ if (!jQuery) { throw new Error("Bootstrap requires jQuery") }
var $this = $(this)
var href = $this.attr('href')
var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) //strip for ie7
var option = $target.data('modal') ? 'toggle' : $.extend({ remote:!/#/.test(href) && href }, $target.data(), $this.data())
var option = $target.data('modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data())
e.preventDefault()
$target
.modal(option)
.modal(option, this)
.one('hide', function () {
$this.is(':visible') && $this.focus()
})
})
$(function () {
var $body = $(document.body)
.on('shown.bs.modal', '.modal', function () { $body.addClass('modal-open') })
.on('hidden.bs.modal', '.modal', function () { $body.removeClass('modal-open') })
})
$(document)
.on('show.bs.modal', '.modal', function () { $(document.body).addClass('modal-open') })
.on('hidden.bs.modal', '.modal', function () { $(document.body).removeClass('modal-open') })
}(window.jQuery);
/* ========================================================================
* Bootstrap: tooltip.js v3.0.0
* http://twbs.github.com/bootstrap/javascript.html#affix
* http://twbs.github.com/bootstrap/javascript.html#tooltip
* Inspired by the original jQuery.tipsy by Jason Frame
* ========================================================================
* Copyright 2012 Twitter, Inc.
@ -1128,7 +1137,7 @@ if (!jQuery) { throw new Error("Bootstrap requires jQuery") }
var eventIn = trigger == 'hover' ? 'mouseenter' : 'focus'
var eventOut = trigger == 'hover' ? 'mouseleave' : 'blur'
this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this))
this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this))
this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))
}
}
@ -1155,37 +1164,43 @@ if (!jQuery) { throw new Error("Bootstrap requires jQuery") }
return options
}
Tooltip.prototype.enter = function (obj) {
var defaults = this.getDefaults()
Tooltip.prototype.getDelegateOptions = function () {
var options = {}
var defaults = this.getDefaults()
this._options && $.each(this._options, function (key, value) {
if (defaults[key] != value) options[key] = value
})
return options
}
Tooltip.prototype.enter = function (obj) {
var self = obj instanceof this.constructor ?
obj : $(obj.currentTarget)[this.type](options).data('bs.' + this.type)
obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type)
clearTimeout(self.timeout)
self.hoverState = 'in'
if (!self.options.delay || !self.options.delay.show) return self.show()
self.hoverState = 'in'
self.timeout = setTimeout(function () {
self.timeout = setTimeout(function () {
if (self.hoverState == 'in') self.show()
}, self.options.delay.show)
}
Tooltip.prototype.leave = function (obj) {
var self = obj instanceof this.constructor ?
obj : $(obj.currentTarget)[this.type](this._options).data('bs.' + this.type)
obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type)
clearTimeout(self.timeout)
self.hoverState = 'out'
if (!self.options.delay || !self.options.delay.hide) return self.hide()
self.hoverState = 'out'
self.timeout = setTimeout(function () {
self.timeout = setTimeout(function () {
if (self.hoverState == 'out') self.hide()
}, self.options.delay.hide)
}
@ -1243,12 +1258,9 @@ if (!jQuery) { throw new Error("Bootstrap requires jQuery") }
.addClass(placement)
}
var tp = placement == 'bottom' ? { top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2 } :
placement == 'top' ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } :
placement == 'left' ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } :
/* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width }
var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight)
this.applyPlacement(tp, placement)
this.applyPlacement(calculatedOffset, placement)
this.$element.trigger('shown.bs.' + this.type)
}
}
@ -1260,25 +1272,33 @@ if (!jQuery) { throw new Error("Bootstrap requires jQuery") }
var height = $tip[0].offsetHeight
// manually read margins because getBoundingClientRect includes difference
offset.top = offset.top + parseInt($tip.css('margin-top'), 10)
offset.left = offset.left + parseInt($tip.css('margin-left'), 10)
var marginTop = parseInt($tip.css('margin-top'), 10)
var marginLeft = parseInt($tip.css('margin-left'), 10)
// we must check for NaN for ie 8/9
if (isNaN(marginTop)) marginTop = 0
if (isNaN(marginLeft)) marginLeft = 0
offset.top = offset.top + marginTop
offset.left = offset.left + marginLeft
$tip
.offset(offset)
.addClass('in')
// check to see if placing tip in new offset caused the tip to resize itself
var actualWidth = $tip[0].offsetWidth
var actualHeight = $tip[0].offsetHeight
if (placement == 'top' && actualHeight != height) {
replace = true
offset.top = offset.top + height - actualHeight
offset.top = offset.top + height - actualHeight
}
if (placement == 'bottom' || placement == 'top') {
if (/bottom|top/.test(placement)) {
var delta = 0
if (offset.left < 0){
if (offset.left < 0) {
delta = offset.left * -2
offset.left = 0
@ -1313,6 +1333,10 @@ if (!jQuery) { throw new Error("Bootstrap requires jQuery") }
var $tip = this.tip()
var e = $.Event('hide.bs.' + this.type)
function complete() {
if (that.hoverState != 'in') $tip.detach()
}
this.$element.trigger(e)
if (e.isDefaultPrevented()) return
@ -1321,9 +1345,9 @@ if (!jQuery) { throw new Error("Bootstrap requires jQuery") }
$.support.transition && this.$tip.hasClass('fade') ?
$tip
.one($.support.transition.end, $tip.detach)
.one($.support.transition.end, complete)
.emulateTransitionEnd(150) :
$tip.detach()
complete()
this.$element.trigger('hidden.bs.' + this.type)
@ -1349,6 +1373,13 @@ if (!jQuery) { throw new Error("Bootstrap requires jQuery") }
}, this.$element.offset())
}
Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) {
return placement == 'bottom' ? { top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2 } :
placement == 'top' ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } :
placement == 'left' ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } :
/* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width }
}
Tooltip.prototype.getTitle = function () {
var title
var $e = this.$element
@ -1364,8 +1395,8 @@ if (!jQuery) { throw new Error("Bootstrap requires jQuery") }
return this.$tip = this.$tip || $(this.options.template)
}
Tooltip.prototype.arrow =function(){
return this.$arrow = this.$arrow || this.tip().find(".tooltip-arrow")
Tooltip.prototype.arrow = function () {
return this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow')
}
Tooltip.prototype.validate = function () {
@ -1389,7 +1420,7 @@ if (!jQuery) { throw new Error("Bootstrap requires jQuery") }
}
Tooltip.prototype.toggle = function (e) {
var self = e ? $(e.currentTarget)[this.type](this._options).data('bs.' + this.type) : this
var self = e ? $(e.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type) : this
self.tip().hasClass('in') ? self.leave(self) : self.enter(self)
}
@ -1487,7 +1518,9 @@ if (!jQuery) { throw new Error("Bootstrap requires jQuery") }
$tip.removeClass('fade top bottom left right in')
$tip.find('.popover-title:empty').hide()
// IE8 doesn't accept hiding via the `:empty` pseudo selector, we have to do
// this manually by checking the contents.
if (!$tip.find('.popover-title').html()) $tip.find('.popover-title').hide()
}
Popover.prototype.hasContent = function () {
@ -1504,15 +1537,15 @@ if (!jQuery) { throw new Error("Bootstrap requires jQuery") }
o.content)
}
Popover.prototype.arrow = function () {
return this.$arrow = this.$arrow || this.tip().find('.arrow')
}
Popover.prototype.tip = function () {
if (!this.$tip) this.$tip = $(this.options.template)
return this.$tip
}
Popover.prototype.destroy = function () {
this.hide().$element.off('.' + this.type).removeData(this.type)
}
// POPOVER PLUGIN DEFINITION
// =========================

File diff suppressed because one or more lines are too long

View File

@ -62,6 +62,7 @@
<script src="{{ STATIC_URL }}js/forms/Permissions.js"></script>
<script src="{{ STATIC_URL }}js/forms/JobEventData.js"></script>
<script src="{{ STATIC_URL }}js/forms/License.js"></script>
<script src="{{ STATIC_URL }}js/forms/HostGroups.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/Admins.js"></script>
@ -220,7 +221,7 @@
<button ng-show="formModalInfo !== undefined && formModalInfo != ''" ng-click="formModalInfoAction()"
class="btn btn-mini pull-left"><i class="icon-zoom-in"></i> <span ng-bind="formModalInfo"></span></button>
<a href="#" ng-show="formModalCancelShow" data-target="#form-modal" data-dismiss="modal" class="btn btn-default">Cancel</a>
<a href="" ng-bind="formModalActionLabel" ng-click="formModalAction()" class="btn btn-primary"></a>
<a href="" ng-bind="formModalActionLabel" ng-click="formModalAction()" ng-disabled="formModalActionDisabled" class="btn btn-primary"></a>
</div>
</div><!-- modal-content -->
</div><!-- modal-dialog -->