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', 'AccessHelper',
'SelectionHelper', 'SelectionHelper',
'LicenseFormDefinition', 'LicenseFormDefinition',
'License' 'License',
'HostGroupsFormDefinition'
]) ])
.config(['$routeProvider', function($routeProvider) { .config(['$routeProvider', function($routeProvider) {
$routeProvider. $routeProvider.

View File

@@ -185,7 +185,7 @@ function InventoriesEdit ($scope, $rootScope, $compile, $location, $log, $routeP
RelatedPaginateInit, ReturnToCaller, ClearScope, LookUpInit, Prompt, RelatedPaginateInit, ReturnToCaller, ClearScope, LookUpInit, Prompt,
OrganizationList, TreeInit, GetBasePath, GroupsList, GroupsAdd, GroupsEdit, LoadInventory, OrganizationList, TreeInit, GetBasePath, GroupsList, GroupsAdd, GroupsEdit, LoadInventory,
GroupsDelete, HostsList, HostsAdd, HostsEdit, HostsDelete, RefreshGroupName, ParseTypeChange, 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 ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior
//scope. //scope.
@@ -204,6 +204,8 @@ function InventoriesEdit ($scope, $rootScope, $compile, $location, $log, $routeP
scope['inventoryParseType'] = 'yaml'; scope['inventoryParseType'] = 'yaml';
scope['inventory_id'] = id; scope['inventory_id'] = id;
scope['inventoryFailureFilter'] = false; scope['inventoryFailureFilter'] = false;
scope['hostDeleteDisabled'] = true;
scope['hostDeleteDisabledClass'] = 'disabled';
// Retrieve each related sets and any lookups // Retrieve each related sets and any lookups
if (scope.inventoryLoadedRemove) { 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 }); 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) { 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, HostsDelete({ scope: scope, "inventory_id": id, group_id: scope.group_id, host_id: host_id, host_name: host_name,
request: 'delete' }); request: 'delete' });
@@ -472,7 +478,8 @@ function InventoriesEdit ($scope, $rootScope, $compile, $location, $log, $routeP
} }
scope.toggleAllHosts = function() { 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++) { for (var i=0; i < scope.hosts.length; i++) {
scope.hosts[i].selected = scope.toggleAllFlag; scope.hosts[i].selected = scope.toggleAllFlag;
} }
@@ -486,9 +493,11 @@ function InventoriesEdit ($scope, $rootScope, $compile, $location, $log, $routeP
break; break;
} }
} }
scope.hostDeleteHide = result; scope.hostDeleteDisabled = result;
scope.hostDeleteDisabledClass = (scope.hostDeleteDisabled) ? "disabled" : "";
} }
// Respond to the scope.$emit from awTree directive // Respond to the scope.$emit from awTree directive
scope.$on('refreshHost', function(e, group, title) { scope.$on('refreshHost', function(e, group, title) {
scope.groupTitle = title; scope.groupTitle = title;
@@ -503,6 +512,8 @@ function InventoriesEdit ($scope, $rootScope, $compile, $location, $log, $routeP
scope.hostCreateHide = false; scope.hostCreateHide = false;
scope.hostDeleteHide = false; scope.hostDeleteHide = false;
} }
scope['hostDeleteDisabled'] = true;
scope['hostDeleteDisabledClass'] = 'disabled';
HostsReload({ scope: scope, inventory_id: scope['inventory_id'], group_id: group }); 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', 'RelatedPaginateInit', 'ReturnToCaller', 'ClearScope', 'LookUpInit', 'Prompt',
'OrganizationList', 'TreeInit', 'GetBasePath', 'GroupsList', 'GroupsAdd', 'GroupsEdit', 'LoadInventory', 'OrganizationList', 'TreeInit', 'GetBasePath', 'GroupsList', 'GroupsAdd', 'GroupsEdit', 'LoadInventory',
'GroupsDelete', 'HostsList', 'HostsAdd', 'HostsEdit', 'HostsDelete', 'RefreshGroupName', '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'; scope.parseType = 'yaml';
ParseTypeChange(scope); 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'); $('#form-modal .btn-none').removeClass('btn-none').addClass('btn-success');
generator.reset(); generator.reset();
@@ -116,6 +108,8 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', '
// Save // Save
scope.formModalAction = function() { scope.formModalAction = function() {
try { try {
scope.formModalActionDisabled = true;
// Make sure we have valid variable data // Make sure we have valid variable data
if (scope.parseType == 'json') { if (scope.parseType == 'json') {
@@ -162,11 +156,13 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', '
} }
}) })
.error( function(data, status, headers, config) { .error( function(data, status, headers, config) {
scope.formModalActionDisabled = false;
ProcessErrors(scope, data, status, form, ProcessErrors(scope, data, status, form,
{ hdr: 'Error!', msg: 'Failed to add new group. POST returned status: ' + status }); { hdr: 'Error!', msg: 'Failed to add new group. POST returned status: ' + status });
}); });
} }
catch(err) { catch(err) {
scope.formModalActionDisabled = false;
Alert("Error", "Error parsing group variables. Parser returned: " + err); Alert("Error", "Error parsing group variables. Parser returned: " + err);
} }
} }

View File

@@ -10,7 +10,7 @@
angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'HostListDefinition', angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'HostListDefinition',
'SearchHelper', 'PaginateHelper', 'ListGenerator', 'AuthService', 'HostsHelper', 'SearchHelper', 'PaginateHelper', 'ListGenerator', 'AuthService', 'HostsHelper',
'InventoryHelper', 'RelatedSearchHelper','RelatedPaginateHelper', 'InventoryHelper', 'RelatedSearchHelper','RelatedPaginateHelper',
'InventoryFormDefinition', 'SelectionHelper' 'InventoryFormDefinition', 'SelectionHelper', 'HostGroupsFormDefinition'
]) ])
.factory('HostsList', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'HostList', 'GenerateList', .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.removeHostsReload = scope.$on('hostsReload', function() { scope.removeHostsReload = scope.$on('hostsReload', function() {
console.log('here!');
HostsReload(params); HostsReload(params);
}); });
@@ -122,6 +121,9 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H
} }
try { try {
scope.formModalActionDisabled = true;
// Make sure we have valid variable data // Make sure we have valid variable data
if (scope.parseType == 'json') { if (scope.parseType == 'json') {
var json_data = JSON.parse(scope.variables); //make sure JSON parses 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) { .error( function(data, status, headers, config) {
scope.formModalActionDisabled = false;
ProcessErrors(scope, data, status, form, ProcessErrors(scope, data, status, form,
{ hdr: 'Error!', msg: 'Failed to add new host. POST returned status: ' + status }); { hdr: 'Error!', msg: 'Failed to add new host. POST returned status: ' + status });
}); });
} }
catch(err) { catch(err) {
scope.formModalActionDisabled = false;
Alert("Error", "Error parsing host variables. Parser returned: " + err); Alert("Error", "Error parsing host variables. Parser returned: " + err);
} }
} }
@@ -200,7 +204,7 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H
var relatedSets = {}; var relatedSets = {};
scope.formModalActionLabel = 'Save'; scope.formModalActionLabel = 'Save';
scope.formModalHeader = 'Edit Host'; scope.formModalHeader = 'Host Properties';
scope.formModalCancelShow = true; scope.formModalCancelShow = true;
scope.parseType = 'yaml'; scope.parseType = 'yaml';
ParseTypeChange(scope); ParseTypeChange(scope);
@@ -347,8 +351,14 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H
return function(params) { return function(params) {
// Remove the selected host from the current group by disassociating // Remove the selected host from the current group by disassociating
var scope = params.scope; var scope = params.scope;
if (scope.hostDeleteDisabled) {
// simulate a disabled link
return;
}
var group_id = scope.group_id; var group_id = scope.group_id;
var inventory_id = params.inventory_id; var inventory_id = params.inventory_id;
var host_id = params.host_id; var host_id = params.host_id;
@@ -432,13 +442,15 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H
}]) }])
.factory('HostsReload', ['RelatedSearchInit', 'RelatedPaginateInit', 'InventoryForm', 'GetBasePath', .factory('HostsReload', ['RelatedSearchInit', 'RelatedPaginateInit', 'InventoryForm', 'GetBasePath', 'Wait',
function(RelatedSearchInit, RelatedPaginateInit, InventoryForm, GetBasePath) { function(RelatedSearchInit, RelatedPaginateInit, InventoryForm, GetBasePath, Wait) {
return function(params) { return function(params) {
// Rerfresh the Hosts view on right side of page // Rerfresh the Hosts view on right side of page
var group_id = params.group_id; var group_id = params.group_id;
var scope = params.scope; var scope = params.scope;
var postAction = params.action;
scope['hosts'] = null; scope['hosts'] = null;
scope['toggleAllFlag'] = false; scope['toggleAllFlag'] = false;
scope['hostDeleteHide'] = true; scope['hostDeleteHide'] = true;
@@ -450,26 +462,24 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H
url += '?has_active_failures=true'; url += '?has_active_failures=true';
} }
// Set the groups value in each element of hosts array
if (scope.removeRelatedHosts) { if (scope.removeRelatedHosts) {
scope.removeRelatedHosts(); scope.removeRelatedHosts();
} }
scope.removeRelatedHosts = scope.$on('relatedhosts', function() { scope.removeRelatedHosts = scope.$on('relatedhosts', function() {
var groups, descr=''; var groups, descr, found, list;
for (var i=0; i < scope.hosts.length; i++) { for (var i=0; i < scope.hosts.length; i++) {
groups = scope.hosts[i].summary_fields.groups; groups = scope.hosts[i].summary_fields.groups;
for (var j=0; j < groups.length; j++) { scope.hosts[i].groups = '';
if (groups[j].name.match(/^_deleted/)) { for (var k=0; k < groups.length; k++) {
descr += groups[j].name.substr(1,18) + ', '; if (!groups[k].name.match(/^_deleted/)) {
} scope.hosts[i].groups += groups[k].name + ', '
else { }
descr += groups[j].name + ', '; }
} scope.hosts[i].groups = scope.hosts[i].groups.replace(/\, $/,'');
} }
descr = descr.replace(/, $/,''); if (postAction) {
if (descr.length > 50) { postAction();
descr = descr.substr(0,49) + '...';
}
scope.hosts[i].groups = descr;
} }
}); });
@@ -477,19 +487,6 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H
RelatedSearchInit({ scope: params.scope, form: InventoryForm, relatedSets: relatedSets }); RelatedSearchInit({ scope: params.scope, form: InventoryForm, relatedSets: relatedSets });
RelatedPaginateInit({ scope: params.scope, relatedSets: relatedSets, pageSize: 40 }); 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'); scope.search('host');
if (!params.scope.$$phase) { if (!params.scope.$$phase) {
@@ -498,16 +495,38 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H
} }
}]) }])
.factory('LoadSearchTree', ['Rest', 'GetBasePath', 'ProcessErrors', '$compile', .factory('LoadSearchTree', ['Rest', 'GetBasePath', 'ProcessErrors', '$compile',
function(Rest, GetBasePath, ProcessErrors, $compile) { function(Rest, GetBasePath, ProcessErrors, $compile) {
return function(params) { return function(params) {
var scope = params.scope; var scope = params.scope;
var inventory_id = params.inventory_id; var inventory_id = params.inventory_id;
var newTree = []; var newTree = [];
scope.searchTree = []; 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.setUrl (GetBasePath('inventory') + inventory_id + '/');
Rest.get() Rest.get()
.success( function(data, status, headers, config) { .success( function(data, status, headers, config) {
@@ -519,14 +538,323 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H
groups: data.related.root_groups, groups: data.related.root_groups,
children: [] children: []
}); });
scope.$emit('hostTabInit'); scope.$emit('buildAllGroups');
}) })
.error( function(data, status, headers, config) { .error( function(data, status, headers, config) {
ProcessErrors(scope, data, status, null, ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Failed to get inventory: ' + inventory_id + '. GET returned: ' + status }); { 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; var returnToCaller = params.returnToCaller;
scope.selected = []; //array of selected row IDs scope.selected = []; //array of selected row IDs
scope.formModalActionDisabled = true;
// toggle row selection // toggle row selection
scope['toggle_' + list.iterator] = function(id, ischeckbox) { 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() { if (target_url) {
Rest.setUrl(target_url); scope.finishSelection = function() {
scope.queue = []; Rest.setUrl(target_url);
scope.queue = [];
function finished() { scope.formModalActionDisabled = true;
scope.selected = [];
if (returnToCaller !== undefined) { function finished() {
ReturnToCaller(returnToCaller); 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 { else {
$('#form-modal').modal('hide'); finished();
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 {
finished();
}
}
scope.formModalAction = scope.finishSelection; scope.formModalAction = scope.finishSelection;

View File

@@ -59,7 +59,7 @@ angular.module('RelatedPaginateHelper', ['RefreshRelatedHelper', 'ngCookies'])
$cookieStore.put(iterator + 'PageSize', scope[iterator + 'PageSize']); $cookieStore.put(iterator + 'PageSize', scope[iterator + 'PageSize']);
url = url.replace(/\/\?.*$/,'/'); 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' ]; '?page_size=' + scope[iterator + 'PageSize' ];
RefreshRelated({ scope: scope, set: set, iterator: iterator, url: url }); 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 mode: 'all', // One of: edit, select, all
ngClick: 'addCredential()', ngClick: 'addCredential()',
basePaths: ['teams','users'], // base path must be in list, or action not available 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' awToolTip: 'Create a new credential'
} }
}, },
@@ -59,7 +59,7 @@ angular.module('CredentialsListDefinition', [])
ngClick: "editCredential(\{\{ credential.id \}\})", ngClick: "editCredential(\{\{ credential.id \}\})",
icon: 'icon-edit', icon: 'icon-edit',
label: 'Edit', label: 'Edit',
"class": 'btn-mini btn-default', "class": 'btn-xs btn-default',
awToolTip: 'View/Edit credential' awToolTip: 'View/Edit credential'
}, },
@@ -67,7 +67,7 @@ angular.module('CredentialsListDefinition', [])
ngClick: "deleteCredential(\{\{ credential.id \}\},'\{\{ credential.name \}\}')", ngClick: "deleteCredential(\{\{ credential.id \}\},'\{\{ credential.name \}\}')",
icon: 'icon-trash', icon: 'icon-trash',
label: 'Delete', label: 'Delete',
"class": 'btn-mini btn-danger', "class": 'btn-xs btn-danger',
awToolTip: 'Delete credential' awToolTip: 'Delete credential'
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

@@ -60,7 +60,7 @@ angular.module('JobHostDefinition', [])
label: 'Refresh', label: 'Refresh',
icon: 'icon-refresh', icon: 'icon-refresh',
ngClick: "refresh()", ngClick: "refresh()",
"class": 'btn-success btn-mini', "class": 'btn-success btn-sm',
awToolTip: 'Refresh the page', awToolTip: 'Refresh the page',
mode: 'all' mode: 'all'
}, },
@@ -68,7 +68,7 @@ angular.module('JobHostDefinition', [])
label: 'Details', label: 'Details',
icon: 'icon-edit', icon: 'icon-edit',
ngClick: "jobDetails()", ngClick: "jobDetails()",
"class": 'btn btn-default btn-mini', "class": 'btn btn-default btn-sm',
awToolTip: 'Edit job details', awToolTip: 'Edit job details',
mode: 'all' mode: 'all'
}, },
@@ -76,7 +76,7 @@ angular.module('JobHostDefinition', [])
label: 'Events', label: 'Events',
icon: 'icon-list-ul', icon: 'icon-list-ul',
ngClick: "jobEvents()", ngClick: "jobEvents()",
"class": 'btn btn-default btn-mini', "class": 'btn btn-default btn-sm',
awToolTip: 'View job events', awToolTip: 'View job events',
mode: 'all' 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>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" + "<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", "</dl>\n",
dataPlacement: 'right', dataPlacement: 'left',
dataContainer: ".container", dataContainer: "body",
icon: "icon-question-sign", icon: "icon-question-sign",
mode: 'all', mode: 'all',
'class': 'btn-info btn-mini btn-help', 'class': 'btn-info btn-xs btn-help pull-right',
awToolTip: 'Click for help', awToolTip: 'Click for help',
dataTitle: 'Job Host Summary', dataTitle: 'Job Host Summary',
id: 'jobhost-help-button', id: 'jobhost-help-button',

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -307,6 +307,7 @@ legend {
} }
.page-size { .page-size {
height: 25px;
font-size: 10.5px; font-size: 10.5px;
line-height: normal; line-height: normal;
} }
@@ -573,6 +574,10 @@ input[type="text"].job-successful {
} }
} }
a.disabled {
color: @grey;
}
.navbar-form { .navbar-form {
display: inline-block; display: inline-block;
float: right; float: right;
@@ -642,6 +647,25 @@ input[type="text"].job-successful {
margin: 5px 0; 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 { .search-tree {
ul { ul {
list-style-type: none; list-style-type: none;
@@ -671,6 +695,10 @@ input[type="text"].job-successful {
} }
} }
.disabled {
color: @grey;
}
.parse-selection { .parse-selection {
display: inline-block; display: inline-block;
margin: 5px 0 8px 0; 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 // 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', .directive('awTree', ['Rest', 'ProcessErrors', 'Authorization', '$compile', '$rootScope', 'Wait',
function(Rest, ProcessErrors, Authorization, $compile, $rootScope, Wait) { function(Rest, ProcessErrors, Authorization, $compile, $rootScope, Wait) {
return { return {
@@ -360,7 +392,8 @@ angular.module('AWDirectives', ['RestServices', 'Utilities', 'AuthService', 'Hos
else { else {
group = null; group = null;
title = 'All Hosts' title = 'All Hosts'
} }
// The following will trigger the host list to load. See Inventory.js controller.
scope.$emit('refreshHost', group, title); scope.$emit('refreshHost', group, title);
} }
@@ -527,6 +560,9 @@ angular.module('AWDirectives', ['RestServices', 'Utilities', 'AuthService', 'Hos
activateElm.bind('click', activate); 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) { if ($rootScope.hostTabInitRemove) {
$rootScope.hostTabInitRemove(); $rootScope.hostTabInitRemove();
} }

View File

@@ -52,15 +52,18 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies'])
if (options.buildTree) { if (options.buildTree) {
element.html(this.buildTree(options)); element.html(this.buildTree(options));
} }
else if (options.html) {
element.html(options.html);
}
else { 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 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' // From here use 'scope' to manipulate the form, as the form is not in '$scope'
$compile(element)(this.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 // Reset the scope to prevent displaying old data from our last visit to this form
for (var fld in form.fields) { for (var fld in form.fields) {
this.scope[fld] = null; this.scope[fld] = null;
@@ -86,7 +89,10 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies'])
}); });
if (options.modal) { 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 this.scope.formModalInfo = false //Disable info button for default modal
if (options.modal_selector) { if (options.modal_selector) {
$(options.modal_selector).modal({ show: true, backdrop: 'static', keyboard: true }); $(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"; "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\" " + 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"; "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"; "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 += "<li><a class=\"status\" ng-show=\"treeLoading\" href=\"\"><i class=\"icon-spinner icon-spin icon-large\"></i> Loading...</a></li>\n";
html += "</ul>\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 += "<table class=\"" + form.related[itm].iterator + " table table-condensed table-hover\">\n";
html += "<thead>\n"; html += "<thead>\n";
html += "<tr>\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\" " + html += "<th><input type=\"checkbox\" ng-model=\"toggleAllFlag\" ng-change=\"toggleAllHosts()\" aw-tool-tip=\"Select all hosts\" " +
"data-placement=\"top\"></th>\n"; "data-placement=\"top\"></th>\n";
for (var fld in form.related[itm].fields) { 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) { for (var fld in form.related[itm].fields) {
cnt++; cnt++;
rfield = form.related[itm].fields[fld]; 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 += "<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 += "<button type=\"button\" class=\"btn btn-default btn-mini dropdown-toggle\" data-toggle=\"dropdown\">";
html += "View <span class=\"caret\"></span></button>\n"; html += "View <span class=\"caret\"></span></button>\n";
html += "<ul class=\"dropdown-menu pull-right\" role=\"menu\" aria-labelledby=\"dropdownMenu1\">\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 += (params.size) ? params.size : "col-lg-4";
html += "\">\n"; html += "\">\n";
html += (label) ? "<label>" + label +"</label>" : ""; 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 += "<div class=\"input-group-btn\">\n";
html += "<button type=\"button\" class=\"btn "; 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 += "dropdown-toggle\" data-toggle=\"dropdown\">\n";
html += "<span ng-bind=\"" + iterator + "SearchFieldLabel\"></span>\n"; html += "<span ng-bind=\"" + iterator + "SearchFieldLabel\"></span>\n";
html += "<span class=\"caret\"></span>\n"; html += "<span class=\"caret\"></span>\n";
@@ -232,18 +234,18 @@ angular.module('GeneratorHelpers', ['GeneratorHelpers'])
html += "</div><!-- input-group-btn -->\n"; html += "</div><!-- input-group-btn -->\n";
html += "<select ng-show=\"" + iterator + "SelectShow\" ng-model=\""+ iterator + "SearchSelectValue\" ng-change=\"search('" + iterator + "')\" "; 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 += "ng-options=\"c.name for c in " + iterator + "SearchSelectOpts\" class=\"form-control search-select";
html += (useMini) ? " input-mini" : " input-small"; //html += (useMini) ? " input-sm" : "";
html += "\"></select>\n"; html += "\"></select>\n";
html += "<input type=\"text\" ng-hide=\"" + iterator + "SelectShow || " + iterator + "InputHide\" class=\"form-control "; 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 + html += "\" ng-model=\"" + iterator + "SearchValue\" ng-change=\"search('" + iterator +
"')\" placeholder=\"Search\" type=\"text\" >\n"; "')\" placeholder=\"Search\" type=\"text\" >\n";
html += "<div class=\"input-group-btn\">\n"; html += "<div class=\"input-group-btn\">\n";
html += "<button type=\"button\" ng-hide=\"" + iterator + "SelectShow || " + iterator + "HideSearchType || " + iterator + "InputHide\" class=\"btn "; 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 += "dropdown-toggle\" data-toggle=\"dropdown\">\n";
html += "<span ng-bind=\"" + iterator + "SearchTypeLabel\"></span>\n"; html += "<span ng-bind=\"" + iterator + "SearchTypeLabel\"></span>\n";
html += "<span class=\"caret\"></span>\n"; html += "<span class=\"caret\"></span>\n";
@@ -279,21 +281,20 @@ angular.module('GeneratorHelpers', ['GeneratorHelpers'])
html += (useMini) ? " related-footer" : ""; html += (useMini) ? " related-footer" : "";
html += "\">\n"; html += "\">\n";
html += "<form class=\"form-inline\">\n"; html += "<form class=\"form-inline\">\n";
html += "<button class=\"previous btn btn-light"; html += "<button type=\"button\" class=\"previous btn btn-light";
html += (useMini) ? " btn-mini\" " : "\" "; html += (useMini) ? " btn-xs\" " : "\" ";
html += "ng-click=\"prevSet('" + set + "','" + iterator + "')\" " + html += "ng-click=\"prevSet('" + set + "','" + iterator + "')\" " +
"ng-disabled=\"" + iterator + "PrevUrl == null || " + iterator + "PrevUrl == undefined\"><i class=\"icon-caret-left\"></i> Prev</button>\n"; "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 += "<button type=\"button\" class=\"next btn btn-light";
html += (useMini) ? " btn-mini\" " : "\" "; html += (useMini) ? " btn-xs\" " : "\" ";
html += " ng-click=\"nextSet('" + set + "','" + iterator + "')\"" + html += " ng-click=\"nextSet('" + set + "','" + iterator + "')\"" +
"ng-disabled=\"" + iterator + "NextUrl == null || " + iterator + "NextUrl == undefined\">Next <i class=\"icon-caret-right\"></i></button>\n"; "ng-disabled=\"" + iterator + "NextUrl == null || " + iterator + "NextUrl == undefined\">Next <i class=\"icon-caret-right\"></i></button>\n";
if (mode != 'lookup') { if (mode != 'lookup') {
html += "<label class=\"page-size-label\">Rows per page: </label>\n"; html += "<label class=\"page-size-label\">Rows per page: </label>\n";
html += "<select ng-model=\"" + iterator + "PageSize\" ng-change=\"changePageSize('" + html += "<select ng-model=\"" + iterator + "PageSize\" ng-change=\"changePageSize('" +
set + "'," + "'" + iterator + "')\" class=\"input-mini"; set + "'," + "'" + iterator + "')\" ";
//html += (useMini) ? " field-mini-height" : ""; html += "class=\"page-size\">\n";
html += " page-size\">\n";
html += "<option value=\"10\" selected>10</option>\n"; html += "<option value=\"10\" selected>10</option>\n";
html += "<option value=\"20\" selected>20</option>\n"; html += "<option value=\"20\" selected>20</option>\n";
html += "<option value=\"40\">40</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 // pass in button object, get back html
var html = ''; var html = '';
html += "<button type=\"button\" " + this.attr(btn, 'ngClick') + "class=\"btn"; 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 += (btn['awPopOver']) ? " help-link-white" : "";
html += "\" "; html += "\" ";
html += (btn.id) ? "id=\"" + btn.id + "\" " : ""; html += (btn.id) ? "id=\"" + btn.id + "\" " : "";
@@ -96,6 +96,7 @@ angular.module('ListGenerator', ['GeneratorHelpers'])
if (options.mode == 'lookup') { if (options.mode == 'lookup') {
// options should include {hdr: <dialog header>, action: <function...> } // options should include {hdr: <dialog header>, action: <function...> }
this.scope.formModalActionDisabled = false;
this.scope.lookupHeader = options.hdr; this.scope.lookupHeader = options.hdr;
$('#lookup-modal').modal({ backdrop: 'static', keyboard: true }); $('#lookup-modal').modal({ backdrop: 'static', keyboard: true });
$(document).bind('keydown', function(e) { $(document).bind('keydown', function(e) {
@@ -143,17 +144,17 @@ angular.module('ListGenerator', ['GeneratorHelpers'])
} }
if (options.mode == 'lookup' || options.id != undefined) { 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 { else {
html += SearchWidget({ iterator: list.iterator, template: list, mini: false }); html += SearchWidget({ iterator: list.iterator, template: list, mini: true });
} }
if (options.mode != 'lookup') { if (options.mode != 'lookup') {
//actions //actions
var base = $location.path().replace(/^\//,'').split('/')[0]; var base = $location.path().replace(/^\//,'').split('/')[0];
html += "<div class=\""; 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"; html += "\">\n";
for (action in list.actions) { for (action in list.actions) {
if (list.actions[action].mode == 'all' || list.actions[action].mode == options.mode) { if (list.actions[action].mode == 'all' || list.actions[action].mode == options.mode) {
@@ -170,7 +171,7 @@ angular.module('ListGenerator', ['GeneratorHelpers'])
dataPlacement: 'left', dataPlacement: 'left',
dataContainer: 'body', dataContainer: 'body',
icon: "icon-question-sign", icon: "icon-question-sign",
'class': 'btn-mini btn-help btn-info', 'class': 'btn-sm btn-help btn-info',
awToolTip: 'Click for help', awToolTip: 'Click for help',
dataTitle: 'Help', dataTitle: 'Help',
iconSize: 'large' iconSize: 'large'

View File

@@ -222,7 +222,6 @@ angular.module('Utilities',[])
var spinnyh = $('.spinny').height(); var spinnyh = $('.spinny').height();
var x = (docw - spinnyw) / 2; var x = (docw - spinnyw) / 2;
var y = (doch - spinnyh) / 2; var y = (doch - spinnyh) / 2;
console.log($(document));
$('.overlay').css({ $('.overlay').css({
width: $('html').width(), width: $('html').width(),
height: $(document).height() + 200 height: $(document).height() + 200
@@ -230,10 +229,10 @@ angular.module('Utilities',[])
$('.spinny').css({ $('.spinny').css({
top: y, top: y,
left: x left: x
}).fadeIn(500); }).fadeIn(400);
} }
else { 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 // http://blog.alexmaccaw.com/css-transitions
$.fn.emulateTransitionEnd = function (duration) { $.fn.emulateTransitionEnd = function (duration) {
var called = false, $el = this var called = false, $el = this
$(this).one($.support.transition.end, function () { called = true }) $(this).one($.support.transition.end, function () { called = true })
var callback = function () { if (!called) $($el).trigger($.support.transition.end) } var callback = function () { if (!called) $($el).trigger($.support.transition.end) }
setTimeout(callback, duration) setTimeout(callback, duration)
@@ -219,7 +219,9 @@ if (!jQuery) { throw new Error("Bootstrap requires jQuery") }
var $parent = this.$element.closest('[data-toggle="buttons"]') var $parent = this.$element.closest('[data-toggle="buttons"]')
if ($parent.length) { 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') 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) { $.fn.button = function (option) {
return this.each(function () { return this.each(function () {
var $this = $(this) var $this = $(this)
var data = $this.data('button') var data = $this.data('bs.button')
var options = typeof option == 'object' && option var options = typeof option == 'object' && option
if (!data) $this.data('bs.button', (data = new Button(this, options))) 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 = { Carousel.DEFAULTS = {
interval: 5000 interval: 5000
, pause: 'hover' , pause: 'hover'
, wrap: true
} }
Carousel.prototype.cycle = function (e) { Carousel.prototype.cycle = function (e) {
@@ -376,12 +379,15 @@ if (!jQuery) { throw new Error("Bootstrap requires jQuery") }
var fallback = type == 'next' ? 'first' : 'last' var fallback = type == 'next' ? 'first' : 'last'
var that = this var that = this
if (!$next.length) {
if (!this.options.wrap) return
$next = this.$element.find('.item')[fallback]()
}
this.sliding = true this.sliding = true
isCycling && this.pause() isCycling && this.pause()
$next = $next.length ? $next : this.$element.find('.item')[fallback]()
var e = $.Event('slide.bs.carousel', { relatedTarget: $next[0], direction: direction }) var e = $.Event('slide.bs.carousel', { relatedTarget: $next[0], direction: direction })
if ($next.hasClass('active')) return if ($next.hasClass('active')) return
@@ -533,7 +539,7 @@ if (!jQuery) { throw new Error("Bootstrap requires jQuery") }
this.$element.trigger(startEvent) this.$element.trigger(startEvent)
if (startEvent.isDefaultPrevented()) return 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) { if (actives && actives.length) {
var hasData = actives.data('bs.collapse') var hasData = actives.data('bs.collapse')
@@ -654,7 +660,7 @@ if (!jQuery) { throw new Error("Bootstrap requires jQuery") }
var $parent = parent && $(parent) var $parent = parent && $(parent)
if (!data || !data.transitioning) { 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') $this[$target.hasClass('in') ? 'addClass' : 'removeClass']('collapsed')
} }
@@ -705,7 +711,7 @@ if (!jQuery) { throw new Error("Bootstrap requires jQuery") }
clearMenus() clearMenus()
if (!isActive) { 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 // if mobile we we use a backdrop because click events don't delegate
$('<div class="dropdown-backdrop"/>').insertAfter($(this)).on('click', clearMenus) $('<div class="dropdown-backdrop"/>').insertAfter($(this)).on('click', clearMenus)
} }
@@ -717,9 +723,9 @@ if (!jQuery) { throw new Error("Bootstrap requires jQuery") }
$parent $parent
.toggleClass('open') .toggleClass('open')
.trigger('shown.bs.dropdown') .trigger('shown.bs.dropdown')
}
$this.focus() $this.focus()
}
return false return false
} }
@@ -845,11 +851,11 @@ if (!jQuery) { throw new Error("Bootstrap requires jQuery") }
var Modal = function (element, options) { var Modal = function (element, options) {
this.options = options this.options = options
this.$element = $(element).on('click.dismiss.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this)) this.$element = $(element)
this.$backdrop = this.$backdrop =
this.isShown = null 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 = { Modal.DEFAULTS = {
@@ -858,13 +864,13 @@ if (!jQuery) { throw new Error("Bootstrap requires jQuery") }
, show: true , show: true
} }
Modal.prototype.toggle = function () { Modal.prototype.toggle = function (_relatedTarget) {
return this[!this.isShown ? 'show' : 'hide']() return this[!this.isShown ? 'show' : 'hide'](_relatedTarget)
} }
Modal.prototype.show = function () { Modal.prototype.show = function (_relatedTarget) {
var that = this var that = this
var e = $.Event('show.bs.modal') var e = $.Event('show.bs.modal', { relatedTarget: _relatedTarget })
this.$element.trigger(e) this.$element.trigger(e)
@@ -874,6 +880,8 @@ if (!jQuery) { throw new Error("Bootstrap requires jQuery") }
this.escape() this.escape()
this.$element.on('click.dismiss.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this))
this.backdrop(function () { this.backdrop(function () {
var transition = $.support.transition && that.$element.hasClass('fade') var transition = $.support.transition && that.$element.hasClass('fade')
@@ -893,13 +901,15 @@ if (!jQuery) { throw new Error("Bootstrap requires jQuery") }
that.enforceFocus() that.enforceFocus()
var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget })
transition ? transition ?
that.$element that.$element.find('.modal-dialog') // wait for modal to slide in
.one($.support.transition.end, function () { .one($.support.transition.end, function () {
that.$element.focus().trigger('shown.bs.modal') that.$element.focus().trigger(e)
}) })
.emulateTransitionEnd(300) : .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 this.$element
.removeClass('in') .removeClass('in')
.attr('aria-hidden', true) .attr('aria-hidden', true)
.off('click.dismiss.modal')
$.support.transition && this.$element.hasClass('fade') ? $.support.transition && this.$element.hasClass('fade') ?
this.$element this.$element
@@ -973,7 +984,7 @@ if (!jQuery) { throw new Error("Bootstrap requires jQuery") }
this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />') this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />')
.appendTo(document.body) .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 if (e.target !== e.currentTarget) return
this.options.backdrop == 'static' this.options.backdrop == 'static'
? this.$element[0].focus.call(this.$element[0]) ? this.$element[0].focus.call(this.$element[0])
@@ -1012,15 +1023,15 @@ if (!jQuery) { throw new Error("Bootstrap requires jQuery") }
var old = $.fn.modal var old = $.fn.modal
$.fn.modal = function (option) { $.fn.modal = function (option, _relatedTarget) {
return this.each(function () { return this.each(function () {
var $this = $(this) var $this = $(this)
var data = $this.data('bs.modal') var data = $this.data('bs.modal')
var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option) var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option)
if (!data) $this.data('bs.modal', (data = new Modal(this, options))) if (!data) $this.data('bs.modal', (data = new Modal(this, options)))
if (typeof option == 'string') data[option]() if (typeof option == 'string') data[option](_relatedTarget)
else if (options.show) data.show() else if (options.show) data.show(_relatedTarget)
}) })
} }
@@ -1043,28 +1054,26 @@ if (!jQuery) { throw new Error("Bootstrap requires jQuery") }
var $this = $(this) var $this = $(this)
var href = $this.attr('href') var href = $this.attr('href')
var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) //strip for ie7 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() e.preventDefault()
$target $target
.modal(option) .modal(option, this)
.one('hide', function () { .one('hide', function () {
$this.is(':visible') && $this.focus() $this.is(':visible') && $this.focus()
}) })
}) })
$(function () { $(document)
var $body = $(document.body) .on('show.bs.modal', '.modal', function () { $(document.body).addClass('modal-open') })
.on('shown.bs.modal', '.modal', function () { $body.addClass('modal-open') }) .on('hidden.bs.modal', '.modal', function () { $(document.body).removeClass('modal-open') })
.on('hidden.bs.modal', '.modal', function () { $body.removeClass('modal-open') })
})
}(window.jQuery); }(window.jQuery);
/* ======================================================================== /* ========================================================================
* Bootstrap: tooltip.js v3.0.0 * 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 * Inspired by the original jQuery.tipsy by Jason Frame
* ======================================================================== * ========================================================================
* Copyright 2012 Twitter, Inc. * Copyright 2012 Twitter, Inc.
@@ -1128,7 +1137,7 @@ if (!jQuery) { throw new Error("Bootstrap requires jQuery") }
var eventIn = trigger == 'hover' ? 'mouseenter' : 'focus' var eventIn = trigger == 'hover' ? 'mouseenter' : 'focus'
var eventOut = trigger == 'hover' ? 'mouseleave' : 'blur' 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)) 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 return options
} }
Tooltip.prototype.enter = function (obj) { Tooltip.prototype.getDelegateOptions = function () {
var defaults = this.getDefaults()
var options = {} var options = {}
var defaults = this.getDefaults()
this._options && $.each(this._options, function (key, value) { this._options && $.each(this._options, function (key, value) {
if (defaults[key] != value) options[key] = value if (defaults[key] != value) options[key] = value
}) })
return options
}
Tooltip.prototype.enter = function (obj) {
var self = obj instanceof this.constructor ? 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) clearTimeout(self.timeout)
self.hoverState = 'in'
if (!self.options.delay || !self.options.delay.show) return self.show() 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() if (self.hoverState == 'in') self.show()
}, self.options.delay.show) }, self.options.delay.show)
} }
Tooltip.prototype.leave = function (obj) { Tooltip.prototype.leave = function (obj) {
var self = obj instanceof this.constructor ? 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) clearTimeout(self.timeout)
self.hoverState = 'out'
if (!self.options.delay || !self.options.delay.hide) return self.hide() 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() if (self.hoverState == 'out') self.hide()
}, self.options.delay.hide) }, self.options.delay.hide)
} }
@@ -1243,12 +1258,9 @@ if (!jQuery) { throw new Error("Bootstrap requires jQuery") }
.addClass(placement) .addClass(placement)
} }
var tp = placement == 'bottom' ? { top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2 } : var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight)
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 }
this.applyPlacement(tp, placement) this.applyPlacement(calculatedOffset, placement)
this.$element.trigger('shown.bs.' + this.type) this.$element.trigger('shown.bs.' + this.type)
} }
} }
@@ -1260,25 +1272,33 @@ if (!jQuery) { throw new Error("Bootstrap requires jQuery") }
var height = $tip[0].offsetHeight var height = $tip[0].offsetHeight
// manually read margins because getBoundingClientRect includes difference // manually read margins because getBoundingClientRect includes difference
offset.top = offset.top + parseInt($tip.css('margin-top'), 10) var marginTop = parseInt($tip.css('margin-top'), 10)
offset.left = offset.left + parseInt($tip.css('margin-left'), 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 $tip
.offset(offset) .offset(offset)
.addClass('in') .addClass('in')
// check to see if placing tip in new offset caused the tip to resize itself
var actualWidth = $tip[0].offsetWidth var actualWidth = $tip[0].offsetWidth
var actualHeight = $tip[0].offsetHeight var actualHeight = $tip[0].offsetHeight
if (placement == 'top' && actualHeight != height) { if (placement == 'top' && actualHeight != height) {
replace = true 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 var delta = 0
if (offset.left < 0){ if (offset.left < 0) {
delta = offset.left * -2 delta = offset.left * -2
offset.left = 0 offset.left = 0
@@ -1313,6 +1333,10 @@ if (!jQuery) { throw new Error("Bootstrap requires jQuery") }
var $tip = this.tip() var $tip = this.tip()
var e = $.Event('hide.bs.' + this.type) var e = $.Event('hide.bs.' + this.type)
function complete() {
if (that.hoverState != 'in') $tip.detach()
}
this.$element.trigger(e) this.$element.trigger(e)
if (e.isDefaultPrevented()) return if (e.isDefaultPrevented()) return
@@ -1321,9 +1345,9 @@ if (!jQuery) { throw new Error("Bootstrap requires jQuery") }
$.support.transition && this.$tip.hasClass('fade') ? $.support.transition && this.$tip.hasClass('fade') ?
$tip $tip
.one($.support.transition.end, $tip.detach) .one($.support.transition.end, complete)
.emulateTransitionEnd(150) : .emulateTransitionEnd(150) :
$tip.detach() complete()
this.$element.trigger('hidden.bs.' + this.type) this.$element.trigger('hidden.bs.' + this.type)
@@ -1349,6 +1373,13 @@ if (!jQuery) { throw new Error("Bootstrap requires jQuery") }
}, this.$element.offset()) }, 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 () { Tooltip.prototype.getTitle = function () {
var title var title
var $e = this.$element var $e = this.$element
@@ -1364,8 +1395,8 @@ if (!jQuery) { throw new Error("Bootstrap requires jQuery") }
return this.$tip = this.$tip || $(this.options.template) return this.$tip = this.$tip || $(this.options.template)
} }
Tooltip.prototype.arrow =function(){ Tooltip.prototype.arrow = function () {
return this.$arrow = this.$arrow || this.tip().find(".tooltip-arrow") return this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow')
} }
Tooltip.prototype.validate = function () { Tooltip.prototype.validate = function () {
@@ -1389,7 +1420,7 @@ if (!jQuery) { throw new Error("Bootstrap requires jQuery") }
} }
Tooltip.prototype.toggle = function (e) { 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) 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.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 () { Popover.prototype.hasContent = function () {
@@ -1504,15 +1537,15 @@ if (!jQuery) { throw new Error("Bootstrap requires jQuery") }
o.content) o.content)
} }
Popover.prototype.arrow = function () {
return this.$arrow = this.$arrow || this.tip().find('.arrow')
}
Popover.prototype.tip = function () { Popover.prototype.tip = function () {
if (!this.$tip) this.$tip = $(this.options.template) if (!this.$tip) this.$tip = $(this.options.template)
return this.$tip return this.$tip
} }
Popover.prototype.destroy = function () {
this.hide().$element.off('.' + this.type).removeData(this.type)
}
// POPOVER PLUGIN DEFINITION // 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/Permissions.js"></script>
<script src="{{ STATIC_URL }}js/forms/JobEventData.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/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/Users.js"></script>
<script src="{{ STATIC_URL }}js/lists/Organizations.js"></script> <script src="{{ STATIC_URL }}js/lists/Organizations.js"></script>
<script src="{{ STATIC_URL }}js/lists/Admins.js"></script> <script src="{{ STATIC_URL }}js/lists/Admins.js"></script>
@@ -220,7 +221,7 @@
<button ng-show="formModalInfo !== undefined && formModalInfo != ''" ng-click="formModalInfoAction()" <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> 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-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>
</div><!-- modal-content --> </div><!-- modal-content -->
</div><!-- modal-dialog --> </div><!-- modal-dialog -->