diff --git a/awx/ui/static/js/controllers/Home.js b/awx/ui/static/js/controllers/Home.js
index 19fb1ff682..a8b6afcba9 100644
--- a/awx/ui/static/js/controllers/Home.js
+++ b/awx/ui/static/js/controllers/Home.js
@@ -14,7 +14,7 @@ function Home($scope, $compile, $routeParams, $rootScope, $location, Wait, Objec
ClearScope, Stream, Rest, GetBasePath, ProcessErrors, Button) {
ClearScope('home');
-
+
var buttons, html, e, waitCount, loadedCount;
// Add buttons to the top of the Home page. We're using lib/ansible/generator_helpers.js-> Buttons()
@@ -31,19 +31,19 @@ function Home($scope, $compile, $routeParams, $rootScope, $location, Wait, Objec
mode: 'all'
}
};
-
+
html = Button({
btn: buttons.refresh,
action: 'refresh',
toolbar: true
});
-
+
html += Button({
btn: buttons.stream,
action: 'stream',
toolbar: true
});
-
+
e = angular.element(document.getElementById('home-list-actions'));
e.html(html);
$compile(e)($scope);
@@ -137,7 +137,7 @@ function HomeGroups($scope, $filter, $compile, $location, $routeParams, LogViewe
opt, PreviousSearchParams;
generator.inject(list, { mode: 'edit', scope: scope });
-
+
function ellipsis(a) {
if (a.length > 20) {
return a.substr(0,20) + '...';
@@ -299,7 +299,7 @@ function HomeGroups($scope, $filter, $compile, $location, $routeParams, LogViewe
};
scope.editGroup = function (group_id, inventory_id) {
- PreviousSearchParams = Store('CurrentSearchParams');
+ PreviousSearchParams = Store('group_current_search_params');
GroupsEdit({
scope: scope,
group_id: group_id,
@@ -335,7 +335,7 @@ function HomeGroups($scope, $filter, $compile, $location, $routeParams, LogViewe
var group = Find({ list: scope.home_groups, key: 'id', val: id });
if (group) {
if (Empty(group.source)) {
- // if no source, do nothing.
+ // if no source, do nothing.
} else if (group.status === 'updating') {
Alert('Update in Progress', 'The inventory update process is currently running for group ' +
group.name + '. Use the Refresh button to monitor the status.', 'alert-info');
@@ -410,7 +410,7 @@ function HomeGroups($scope, $filter, $compile, $location, $routeParams, LogViewe
}
scope.removeGroupSummaryReady = scope.$on('GroupSummaryReady', function(e, event, inventory, data) {
var html, title;
-
+
Wait('stop');
// Build the html for our popover
diff --git a/awx/ui/static/js/controllers/Inventories.js b/awx/ui/static/js/controllers/Inventories.js
index ef06994582..44aeb64f1a 100644
--- a/awx/ui/static/js/controllers/Inventories.js
+++ b/awx/ui/static/js/controllers/Inventories.js
@@ -471,238 +471,259 @@ InventoriesAdd.$inject = ['$scope', '$rootScope', '$compile', '$location', '$log
-function InventoriesEdit($scope, $location, $routeParams, $compile, $log, $rootScope, GenerateList, ClearScope, InventoryGroups, InventoryHosts, BuildTree, Wait,
- GetSyncStatusMsg, InjectHosts, HostsReload, GroupsEdit, GroupsDelete, Breadcrumbs, LoadBreadCrumbs, Empty, Rest, ProcessErrors,
- InventoryUpdate, Alert, ToggleChildren, ViewUpdateStatus, GroupsCancelUpdate, Find, EditInventoryProperties, HostsEdit,
- HostsDelete, ToggleHostEnabled, CopyMoveGroup, CopyMoveHost, Stream, GetBasePath, ShowJobSummary, ApplyEllipsis, WatchInventoryWindowResize,
- HelpDialog, InventoryGroupsHelp, Store, ViewJob, SetContainerHeights) {
+function InventoriesEdit ($scope, $location, $routeParams, $compile, GenerateList, ClearScope, Empty, Wait, Rest, Alert, LoadBreadCrumbs, GetBasePath, ProcessErrors,
+ Breadcrumbs, InventoryGroups, InjectHosts, Find, HostsReload, SearchInit, PaginateInit, GetSyncStatusMsg, GetHostsStatusMsg, GroupsEdit, InventoryUpdate,
+ GroupsCancelUpdate, ViewUpdateStatus, GroupsDelete, Store, HostsEdit, HostsDelete, EditInventoryProperties, ToggleHostEnabled, Stream, ShowJobSummary,
+ InventoryGroupsHelp, HelpDialog, ViewJob, WatchInventoryWindowResize, SetContainerHeights, GetHostContainerRows, GetGroupContainerRows, GetGroupContainerHeight,
+ GroupsCopy, HostsCopy)
+{
+
+ var PreviousSearchParams,
+ url,
+ hostScope = $scope.$new();
ClearScope();
- var generator = GenerateList,
- list = InventoryGroups;
+ $scope.group_breadcrumbs = [{
+ name: 'All',
+ id: 0,
+ description: '',
+ show: true,
+ ngicon: null,
+ has_children: false,
+ related: {},
+ active_class: 'active',
+ show_failures: false
+ }];
- $scope.inventory_id = $routeParams.inventory_id;
+ $scope.refreshHostsOnGroupRefresh = false;
+ $scope.selected_group_id = null;
- LoadBreadCrumbs({
- path: $location.path(),
- title: '{{ inventory_name }}'
- });
+ Wait('start');
- // Handle inventory sync status changes
- if ($rootScope.removeJobStatusChange) {
- $rootScope.removeJobStatusChange();
+
+ if ($scope.removeHostReloadComplete) {
+ $scope.removeHostReloadComplete();
}
- $rootScope.removeJobStatusChange = $rootScope.$on('JobStatusChange', function(e, data) {
- var group, stat;
- Wait('stop');
- $log.debug(data);
- if ($scope.groups) {
- // Assuming we have a list of groups available
- group = Find({ list: $scope.groups, key: 'group_id', val: data.group_id });
- if (group) {
- // And we found the affected group
- $log.debug('Received event for group: ' + group.name);
- if (data.status === 'failed' || data.status === 'successful') {
- $log.debug('Update completed. Refreshing the tree.');
- $scope.refreshGroups(group.id, group.group_id);
- }
- else {
- $log.debug('Status changed to: ' + data.status);
- stat = GetSyncStatusMsg({
- status: data.status,
- has_inventory_sources: group.has_inventory_sources,
- source: group.source
- });
- $log.debug('Changing tooltip to: ' + stat.tooltip);
- group.status = data.status;
- group.status_class = stat['class'];
- group.status_tooltip = stat.tooltip;
- group.launch_tooltip = stat.launch_tip;
- group.launch_class = stat.launch_class;
- }
- }
+ $scope.removeHostReloadComplete = $scope.$on('HostReloadComplete', function() {
+ if ($scope.initial_height) {
+ $('#hosts-container .well').height($scope.initial_height + 49);
+ $scope.initial_height = null;
}
});
- // After the tree data loads for the first time, generate the groups and hosts lists
- if ($scope.removeGroupTreeLoaded) {
- $scope.removeGroupTreeLoaded();
+ if ($scope.removeInventoryLoaded) {
+ $scope.removeInventoryLoaded();
}
- $scope.removeGroupTreeLoaded = $scope.$on('GroupTreeLoaded', function (event, inventory_name, groups) {
- // Add breadcrumbs
- var e, html, inventoryAutoHelp;
+ $scope.removeInventoryLoaded = $scope.$on('InventoryLoaded', function() {
+ var e, rows;
+
+ LoadBreadCrumbs({
+ path: $location.path(),
+ title: '{{ inventory.name }}'
+ });
+
+ // Build page breadcrumbs
e = angular.element(document.getElementById('breadcrumbs'));
- e.html(Breadcrumbs({ list: list, mode: 'edit' }));
+ e.html(Breadcrumbs({ list: InventoryGroups, mode: 'edit' }));
$compile(e)($scope);
// Add groups view
- generator.inject(list, {
+ GenerateList.inject(InventoryGroups, {
mode: 'edit',
- id: 'groups-container',
+ id: 'group-list-container',
breadCrumbs: false,
- searchSize: 'col-lg-5 col-md-5 col-sm-5',
- skipTableHead: true
+ searchSize: 'col-lg-6 col-md-6 col-sm-6',
+ scope: $scope
});
- // Keep the table header fixed while allowing the table body to scroll and still use
element
- html = "\n";
- $(html).insertBefore('#groups-container .list-table-container');
+ /*SetContainerHeights({
+ group_scope: $scope,
+ host_scope: hostScope,
+ reloadHosts: false
+ });*/
- $scope.groups = groups;
- $scope.inventory_name = inventory_name;
-
- // Default the selected group to the first node
- if ($scope.groups.length > 0) {
- $scope.selected_tree_id = $scope.groups[0].id;
- $scope.selected_group_id = $scope.groups[0].group_id;
- $scope.groups[0].selected_class = 'selected';
- $scope.groups[0].active_class = 'active-row';
- $scope.selected_group_name = $scope.groups[0].name;
- } else {
- $scope.selected_tree_id = null;
- $scope.selected_group_id = null;
+ if ($(window).width() > 1210) {
+ $scope.initial_height = GetGroupContainerHeight() - 20;
+ $('#groups-container .list-table-container').height($scope.initial_height);
+ rows = GetGroupContainerRows();
+ //$('#hosts-container .well').height( height );
}
-
- // Resize the containers based on viewport width/height
- SetContainerHeights({ scope: $scope, reloadHosts: false });
+ else {
+ rows = 20;
+ }
+ hostScope.host_page_size = rows;
+ $scope.group_page_size = rows;
// Add hosts view
$scope.show_failures = false;
InjectHosts({
- scope: $scope,
- inventory_id: $scope.inventory_id,
- tree_id: $scope.selected_tree_id,
- group_id: $scope.selected_group_id
+ group_scope: $scope,
+ host_scope: hostScope,
+ inventory_id: $scope.inventory.id,
+ tree_id: null,
+ group_id: null,
+ pageSize: rows
});
- // As the window shrinks and expands, apply ellipsis
- setTimeout(function () {
- // Hack to keep group name from slipping to a new line
- $('#groups_table .name-column').each(function () {
- var td_width, level_width, level_padding, level, pct;
- td_width = $(this).width();
- level_width = $(this).find('.level').width();
- level_padding = parseInt($(this).find('.level').css('padding-left').replace(/px/, ''));
- level = level_width + level_padding;
- pct = (100 - Math.ceil((level / td_width) * 100)) + '%';
- $(this).find('.group-name').css({
- width: pct
+ SearchInit({ scope: $scope, set: 'groups', list: InventoryGroups, url: $scope.inventory.related.root_groups });
+ PaginateInit({ scope: $scope, list: InventoryGroups , url: $scope.inventory.related.root_groups, pageSize: rows });
+ $scope.search(InventoryGroups.iterator, null, true);
+ });
+
+ if ($scope.removePostRefresh) {
+ $scope.removePostRefresh();
+ }
+ $scope.removePostRefresh = $scope.$on('PostRefresh', function(e, set) {
+ if (set === 'groups') {
+ $scope.groups.forEach( function(group, idx) {
+ var stat, hosts_status;
+ stat = GetSyncStatusMsg({
+ status: group.summary_fields.inventory_source.status,
+ has_inventory_sources: group.has_inventory_sources,
+ source: ( (group.summary_fields.inventory_source) ? group.summary_fields.inventory_source.source : null )
+ }); // from helpers/Groups.js
+ $scope.groups[idx].status_class = stat['class'];
+ $scope.groups[idx].status_tooltip = stat.tooltip;
+ $scope.groups[idx].launch_tooltip = stat.launch_tip;
+ $scope.groups[idx].launch_class = stat.launch_class;
+ hosts_status = GetHostsStatusMsg({
+ active_failures: group.hosts_with_active_failures,
+ total_hosts: group.total_hosts,
+ inventory_id: $scope.inventory.id,
+ group_id: group.id
+ }); // from helpers/Groups.js
+ $scope.groups[idx].hosts_status_tip = hosts_status.tooltip;
+ $scope.groups[idx].show_failures = hosts_status.failures;
+ $scope.groups[idx].hosts_status_class = hosts_status['class'];
+
+ $scope.groups[idx].source = (group.summary_fields.inventory_source) ? group.summary_fields.inventory_source.source : null;
+ $scope.groups[idx].status = (group.summary_fields.inventory_source) ? group.summary_fields.inventory_source.status : null;
+
+ });
+ if ($scope.refreshHostsOnGroupRefresh) {
+ $scope.refreshHostsOnGroupRefresh = false;
+ HostsReload({
+ scope: hostScope,
+ group_id: $scope.selected_group_id,
+ inventory_id: $scope.inventory.id,
+ pageSize: hostScope.host_page_size
});
- });
- ApplyEllipsis('#groups_table .group-name a');
- ApplyEllipsis('#hosts_table .host-name a');
- }, 2500); //give the window time to display
- WatchInventoryWindowResize({ scope: $scope });
-
- inventoryAutoHelp = Store('inventoryAutoHelp');
- if (inventoryAutoHelp !== 'off' && $scope.autoShowGroupHelp) {
- $scope.showGroupHelp({
- autoShow: true
- });
- }
-
- });
-
- // Called after tree data is reloaded on refresh button click.
- if ($scope.removeGroupTreeRefreshed) {
- $scope.removeGroupTreeRefreshed();
- }
- $scope.removeGroupTreeRefreshed = $scope.$on('GroupTreeRefreshed', function () {
- // Reapply ellipsis to groups
- setTimeout(function () {
- ApplyEllipsis('#groups_table .group-name a');
- }, 2500);
- // Reselect the preveiously selected group node, causing host view to refresh.
- $scope.showHosts($scope.selected_tree_id, $scope.selected_group_id, false);
- });
-
- // Group was deleted. Now we need to refresh the group view.
- if ($scope.removeGroupDeleteCompleted) {
- $scope.removeGroupDeleteCompleted();
- }
- $scope.removeGroupDeleteCompleted = $scope.$on('GroupDeleteCompleted', function () {
- $scope.selected_tree_id = 1;
- $scope.selected_group_id = null;
- BuildTree({
- scope: $scope,
- inventory_id: $scope.inventory_id,
- refresh: true
- });
- });
-
- // Respond to a group drag-n-drop
- if ($scope.removeCopMoveGroup) {
- $scope.removeCopyMoveGroup();
- }
- $scope.removeCopyMoveGroup = $scope.$on('CopyMoveGroup', function (e, inbound_tree_id, target_tree_id) {
- CopyMoveGroup({
- scope: $scope,
- target_tree_id: target_tree_id,
- inbound_tree_id: inbound_tree_id
- });
- });
-
- // Respond to a host drag-n-drop
- if ($scope.removeCopMoveHost) {
- $scope.removeCopyMoveHost();
- }
- $scope.removeCopyMoveHost = $scope.$on('CopyMoveHost', function (e, target_tree_id, host_id) {
- CopyMoveHost({
- scope: $scope,
- target_tree_id: target_tree_id,
- host_id: host_id
- });
- });
-
- $scope.showHosts = function (tree_id, group_id, show_failures) {
- // Clicked on group
- if (tree_id !== null) {
- Wait('start');
- $scope.selected_tree_id = tree_id;
- $scope.selected_group_id = group_id;
- $scope.hosts = [];
- $scope.show_failures = show_failures; // turn on failed hosts filter in hosts view
- for (var i = 0; i < $scope.groups.length; i++) {
- if ($scope.groups[i].id === tree_id) {
- $scope.groups[i].selected_class = 'selected';
- $scope.groups[i].active_class = 'active-row';
- $scope.selected_group_name = $scope.groups[i].name;
- } else {
- $scope.groups[i].selected_class = '';
- $scope.groups[i].active_class = '';
- }
}
- if (Empty($scope.inventory_id)) {
- $scope.inventory_id = $scope.groups[0].inentory_id;
+ else {
+ Wait('stop');
}
- HostsReload({
- scope: $scope,
- group_id: group_id,
- tree_id: tree_id,
- inventory_id: $scope.inventory_id
+
+ WatchInventoryWindowResize({
+ group_scope: $scope,
+ host_scope: hostScope
});
- } else {
- Wait('stop');
+
}
+ });
+
+ // Load Inventory
+ url = GetBasePath('inventory') + $routeParams.inventory_id + '/';
+ Rest.setUrl(url);
+ Rest.get()
+ .success(function (data) {
+ $scope.inventory = data;
+ $scope.$emit('InventoryLoaded');
+ })
+ .error(function (data, status) {
+ ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to retrieve inventory: ' + $routeParams.inventory_id +
+ ' GET returned status: ' + status });
+ });
+
+ // Load group on selection
+ function loadGroups(url) {
+ SearchInit({ scope: $scope, set: 'groups', list: InventoryGroups, url: url });
+ PaginateInit({ scope: $scope, list: InventoryGroups , url: url, pageSize: $scope.group_page_size });
+ $scope.search(InventoryGroups.iterator, null, true, false, true);
+ }
+
+ function setActiveGroupBreadcrumb() {
+ $scope.group_breadcrumbs.forEach(function(crumb, idx) {
+ $scope.group_breadcrumbs[idx].active_class = '';
+ });
+ $scope.group_breadcrumbs[$scope.group_breadcrumbs.length - 1].active_class = 'active';
+ $scope.refreshHostsOnGroupRefresh = true;
+ $scope.selected_group_id = ($scope.group_breadcrumbs[$scope.group_breadcrumbs.length - 1].id === 0) ? null : $scope.group_breadcrumbs[$scope.group_breadcrumbs.length - 1].id;
+ }
+
+ $scope.refreshHosts = function() {
+ HostsReload({
+ scope: hostScope,
+ group_id: $scope.selected_group_id,
+ inventory_id: $scope.inventory.id,
+ pageSize: hostScope.host_page_size
+ });
+ };
+
+ $scope.refreshGroups = function() {
+ $scope.refreshHostsOnGroupRefresh = true;
+ $scope.search(InventoryGroups.iterator, null, true, false, true);
+ };
+
+ $scope.restoreSearch = function() {
+ // Restore search params and related stuff, plus refresh
+ // groups and hosts lists
+ SearchInit({
+ scope: $scope,
+ set: PreviousSearchParams.set,
+ list: PreviousSearchParams.list,
+ url: PreviousSearchParams.defaultUrl,
+ iterator: PreviousSearchParams.iterator,
+ sort_order: PreviousSearchParams.sort_order,
+ setWidgets: false
+ });
+ $scope.refreshHostsOnGroupRefresh = true;
+ $scope.search(InventoryGroups.iterator, null, true, false, true);
+ };
+
+ $scope.groupSelect = function(id) {
+ var group = Find({ list: $scope.groups, key: 'id', val: id });
+ $scope.group_breadcrumbs.push(group);
+ setActiveGroupBreadcrumb();
+ loadGroups(group.related.children, group.id);
+ };
+
+ $scope.breadcrumbGroupSelect = function(id) {
+ var i, url;
+ $scope.group_breadcrumbs.every(function(crumb, idx) {
+ if (crumb.id === id) {
+ i = idx;
+ return false;
+ }
+ return true;
+ });
+ $scope.group_breadcrumbs = $scope.group_breadcrumbs.slice(0,i + 1);
+ if (id > 0) {
+ url = $scope.group_breadcrumbs[$scope.group_breadcrumbs.length - 1].related.children;
+ }
+ else {
+ url = $scope.inventory.related.root_groups;
+ }
+ setActiveGroupBreadcrumb();
+ loadGroups(url);
};
$scope.createGroup = function () {
+ PreviousSearchParams = Store('group_current_search_params');
GroupsEdit({
scope: $scope,
- inventory_id: $scope.inventory_id,
+ inventory_id: $scope.inventory.id,
group_id: $scope.selected_group_id,
mode: 'add'
});
};
- $scope.editGroup = function (group_id, tree_id) {
+ $scope.editGroup = function (id) {
+ PreviousSearchParams = Store('group_current_search_params');
GroupsEdit({
scope: $scope,
- inventory_id: $scope.inventory_id,
- group_id: group_id,
- tree_id: tree_id,
- groups_reload: true,
+ inventory_id: $scope.inventory.id,
+ group_id: id,
mode: 'edit'
});
};
@@ -715,7 +736,7 @@ function InventoriesEdit($scope, $location, $routeParams, $compile, $log, $rootS
// if no source, do nothing.
} else if (group.status === 'updating') {
Alert('Update in Progress', 'The inventory update process is currently running for group ' +
- group.name + '. Use the Refresh button to monitor the status.', 'alert-info');
+ group.name + ' Click the button to monitor the status.', 'alert-info');
} else {
Wait('start');
Rest.setUrl(group.related.inventory_source);
@@ -726,109 +747,164 @@ function InventoriesEdit($scope, $location, $routeParams, $compile, $log, $rootS
url: data.related.update,
group_name: data.summary_fields.group.name,
group_source: data.source,
- tree_id: group.id,
- group_id: group.group_id
+ group_id: group.id,
});
})
.error(function (data, status) {
- Wait('stop');
ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to retrieve inventory source: ' +
- group.related.inventory_source + ' POST returned status: ' + status });
+ group.related.inventory_source + ' GET returned status: ' + status });
});
}
}
};
- $scope.cancelUpdate = function (tree_id) {
- GroupsCancelUpdate({ scope: $scope, id: tree_id });
+ $scope.cancelUpdate = function (id) {
+ GroupsCancelUpdate({ scope: $scope, id: id });
};
- $scope.toggle = function (tree_id) {
- // Expand/collapse nodes
- ToggleChildren({ scope: $scope, list: list, id: tree_id });
- };
-
- $scope.refreshGroups = function (tree_id, group_id) {
- // Refresh the tree data when refresh button cicked
- if (tree_id) {
- $scope.selected_tree_id = tree_id;
- $scope.selected_group_id = group_id;
- }
- BuildTree({ scope: $scope, inventory_id: $scope.inventory_id, refresh: true });
- };
-
- $scope.viewUpdateStatus = function (tree_id, group_id) {
+ $scope.viewUpdateStatus = function (id) {
ViewUpdateStatus({
scope: $scope,
- tree_id: tree_id,
- group_id: group_id
+ group_id: id
});
};
- $scope.deleteGroup = function (tree_id, group_id) {
+ $scope.copyGroup = function(id) {
+ PreviousSearchParams = Store('group_current_search_params');
+ GroupsCopy({
+ scope: $scope,
+ group_id: id
+ });
+ };
+
+ $scope.deleteGroup = function (id) {
GroupsDelete({
scope: $scope,
- tree_id: tree_id,
- group_id: group_id,
- inventory_id: $scope.inventory_id
+ group_id: id,
+ inventory_id: $scope.inventory.id
});
};
- $scope.createHost = function () {
- HostsEdit({ scope: $scope, mode: 'add', host_id: null, selected_group_id: $scope.selected_tree_id, inventory_id: $scope.inventory_id });
- };
-
$scope.editInventoryProperties = function () {
- EditInventoryProperties({ scope: $scope, inventory_id: $scope.inventory_id });
+ EditInventoryProperties({ scope: $scope, inventory_id: $scope.inventory.id });
};
- $scope.editHost = function (host_id) {
- HostsEdit({ scope: $scope, mode: 'edit', host_id: host_id, inventory_id: $scope.inventory_id });
+ hostScope.createHost = function () {
+ HostsEdit({
+ host_scope: hostScope,
+ group_scope: $scope,
+ mode: 'add',
+ host_id: null,
+ selected_group_id: $scope.selected_group_id,
+ inventory_id: $scope.inventory.id
+ });
};
- $scope.deleteHost = function (host_id, host_name) {
- HostsDelete({ scope: $scope, host_id: host_id, host_name: host_name });
+ hostScope.editHost = function (host_id) {
+ HostsEdit({
+ host_scope: hostScope,
+ group_scope: $scope,
+ mode: 'edit',
+ host_id: host_id,
+ inventory_id: $scope.inventory.id
+ });
};
- $scope.toggleHostEnabled = function (host_id, external_source) {
- ToggleHostEnabled({ scope: $scope, host_id: host_id, external_source: external_source });
+ hostScope.deleteHost = function (host_id, host_name) {
+ HostsDelete({
+ parent_scope: $scope,
+ host_scope: hostScope,
+ host_id: host_id,
+ host_name: host_name
+ });
+ };
+
+ hostScope.copyHost = function(id) {
+ PreviousSearchParams = Store('group_current_search_params');
+ HostsCopy({
+ group_scope: $scope,
+ host_scope: hostScope,
+ host_id: id
+ });
+ };
+
+ /*hostScope.restoreSearch = function() {
+ SearchInit({
+ scope: hostScope,
+ set: PreviousSearchParams.set,
+ list: PreviousSearchParams.list,
+ url: PreviousSearchParams.defaultUrl,
+ iterator: PreviousSearchParams.iterator,
+ sort_order: PreviousSearchParams.sort_order,
+ setWidgets: false
+ });
+ hostScope.search('host');
+ };*/
+
+ hostScope.toggleHostEnabled = function (host_id, external_source) {
+ ToggleHostEnabled({
+ parent_scope: $scope,
+ host_scope: hostScope,
+ host_id: host_id,
+ external_source: external_source
+ });
};
$scope.showGroupActivity = function () {
var url, title, group;
if ($scope.selected_group_id) {
- group = Find({
- list: $scope.groups,
- key: 'id',
- val: $scope.selected_tree_id
+ $scope.group_breadcrumbs.every(function(crumb) {
+ if (crumb.id === $scope.selected_group_id) {
+ group = crumb;
+ return false;
+ }
+ return true;
});
url = GetBasePath('activity_stream') + '?group__id=' + $scope.selected_group_id;
title = 'Showing all activities for group ' + group.name;
} else {
- title = 'Showing all activities for all ' + $scope.inventory_name + ' groups';
- url = GetBasePath('activity_stream') + '?group__inventory__id=' + $scope.inventory_id;
+ title = 'Showing all activities for all ' + $scope.inventory.name + ' groups';
+ url = GetBasePath('activity_stream') + '?group__inventory__id=' + $scope.inventory.id;
}
Stream({
scope: $scope,
- inventory_name: $scope.inventory_name,
+ inventory_name: $scope.inventory.name,
url: url,
- title: title
+ title: title,
+ search_iterator: 'group',
+ onClose: 'GroupStreamClosed'
});
};
- $scope.showHostActivity = function () {
+ if ($scope.removeGroupStreamClosed) {
+ $scope.removeGroupStreamClosed();
+ }
+ $scope.removeGroupStreamClosed = $scope.$on('GroupStreamClosed', function() {
+ $scope.refreshGroups();
+ });
+
+ hostScope.showHostActivity = function () {
var url, title;
- title = 'Showing all activities for all ' + $scope.inventory_name + ' hosts';
- url = GetBasePath('activity_stream') + '?host__inventory__id=' + $scope.inventory_id;
+ title = 'Showing all activities for all ' + $scope.inventory.name + ' hosts';
+ url = GetBasePath('activity_stream') + '?host__inventory__id=' + $scope.inventory.id;
Stream({
- scope: $scope,
- inventory_name: $scope.inventory_name,
+ scope: hostScope,
+ inventory_name: $scope.inventory.name,
url: url,
- title: title
+ title: title,
+ search_iterator: 'host',
+ onClose: 'HostStreamClosed'
});
};
- $scope.showJobSummary = function (job_id) {
+ if (hostScope.removeHostStreamClosed) {
+ hostScope.removeHostStreamClosed();
+ }
+ hostScope.removeHostStreamClosed = hostScope.$on('HostStreamClosed', function() {
+ $scope.refreshGroups();
+ });
+
+ hostScope.showJobSummary = function (job_id) {
ShowJobSummary({
job_id: job_id
});
@@ -848,19 +924,31 @@ function InventoriesEdit($scope, $location, $routeParams, $compile, $log, $rootS
ViewJob({ scope: $scope, id: id });
};
- //Load tree data for the first time
- BuildTree({
- scope: $scope,
- inventory_id: $scope.inventory_id,
- refresh: false
+ $scope.showHosts = function (group_id, show_failures) {
+ // Clicked on group
+ if (group_id !== null) {
+ Wait('start');
+ hostScope.show_failures = show_failures;
+ $scope.groupSelect(group_id);
+ hostScope.hosts = [];
+ $scope.show_failures = show_failures; // turn on failed hosts filter in hosts view
+ } else {
+ Wait('stop');
+ }
+ };
+
+ if ($scope.removeGroupDeleteCompleted) {
+ $scope.removeGroupDeleteCompleted();
+ }
+ $scope.removeGroupDeleteCompleted = $scope.$on('GroupDeleteCompleted', function() {
+ $scope.refreshGroups();
});
}
-InventoriesEdit.$inject = ['$scope', '$location', '$routeParams', '$compile', '$log', '$rootScope', 'GenerateList', 'ClearScope', 'InventoryGroups', 'InventoryHosts',
- 'BuildTree', 'Wait', 'GetSyncStatusMsg', 'InjectHosts', 'HostsReload', 'GroupsEdit', 'GroupsDelete', 'Breadcrumbs',
- 'LoadBreadCrumbs', 'Empty', 'Rest', 'ProcessErrors', 'InventoryUpdate', 'Alert', 'ToggleChildren', 'ViewUpdateStatus', 'GroupsCancelUpdate',
- 'Find', 'EditInventoryProperties', 'HostsEdit', 'HostsDelete', 'ToggleHostEnabled', 'CopyMoveGroup', 'CopyMoveHost',
- 'Stream', 'GetBasePath', 'ShowJobSummary', 'ApplyEllipsis', 'WatchInventoryWindowResize', 'HelpDialog', 'InventoryGroupsHelp', 'Store',
- 'ViewJob', 'SetContainerHeights'
-];
+InventoriesEdit.$inject = ['$scope', '$location', '$routeParams', '$compile', 'GenerateList', 'ClearScope', 'Empty', 'Wait', 'Rest', 'Alert', 'LoadBreadCrumbs',
+ 'GetBasePath', 'ProcessErrors', 'Breadcrumbs', 'InventoryGroups', 'InjectHosts', 'Find', 'HostsReload', 'SearchInit', 'PaginateInit', 'GetSyncStatusMsg',
+ 'GetHostsStatusMsg', 'GroupsEdit', 'InventoryUpdate', 'GroupsCancelUpdate', 'ViewUpdateStatus', 'GroupsDelete', 'Store', 'HostsEdit', 'HostsDelete',
+ 'EditInventoryProperties', 'ToggleHostEnabled', 'Stream', 'ShowJobSummary', 'InventoryGroupsHelp', 'HelpDialog', 'ViewJob', 'WatchInventoryWindowResize',
+ 'SetContainerHeights', 'GetHostContainerRows', 'GetGroupContainerRows', 'GetGroupContainerHeight', 'GroupsCopy', 'HostsCopy'
+ ];
\ No newline at end of file
diff --git a/awx/ui/static/js/helpers/Groups.js b/awx/ui/static/js/helpers/Groups.js
index 4c40324751..778bbe87d1 100644
--- a/awx/ui/static/js/helpers/Groups.js
+++ b/awx/ui/static/js/helpers/Groups.js
@@ -319,8 +319,7 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', '
// Cancel the update process
if (Empty(group)) {
group = Find({ list: scope.groups, key: 'id', val: id });
- scope.selected_tree_id = group.id;
- scope.selected_group_id = group.group_id;
+ scope.selected_group_id = group.id;
}
if (group && (group.status === 'running' || group.status === 'pending')) {
@@ -615,26 +614,23 @@ function($compile, SchedulerInit, Rest, Wait, SetSchedulesInnerDialogSize, Sched
.factory('GroupsEdit', ['$rootScope', '$location', '$log', '$routeParams', '$compile', 'Rest', 'Alert', 'GroupForm', 'GenerateForm',
'Prompt', 'ProcessErrors', 'GetBasePath', 'SetNodeName', 'ParseTypeChange', 'GetSourceTypeOptions', 'InventoryUpdate',
- 'LookUpInit', 'Empty', 'Wait', 'GetChoices', 'UpdateGroup', 'SourceChange', 'Find',
- 'ParseVariableString', 'ToJSON', 'GroupsScheduleListInit', 'SourceForm', 'SetSchedulesInnerDialogSize', 'BuildTree',
+ 'LookUpInit', 'Empty', 'Wait', 'GetChoices', 'UpdateGroup', 'SourceChange', 'Find', 'WatchInventoryWindowResize',
+ 'ParseVariableString', 'ToJSON', 'GroupsScheduleListInit', 'SourceForm', 'SetSchedulesInnerDialogSize',
function ($rootScope, $location, $log, $routeParams, $compile, Rest, Alert, GroupForm, GenerateForm, Prompt, ProcessErrors,
GetBasePath, SetNodeName, ParseTypeChange, GetSourceTypeOptions, InventoryUpdate, LookUpInit, Empty, Wait,
- GetChoices, UpdateGroup, SourceChange, Find, ParseVariableString, ToJSON, GroupsScheduleListInit,
- SourceForm, SetSchedulesInnerDialogSize, BuildTree) {
+ GetChoices, UpdateGroup, SourceChange, Find, WatchInventoryWindowResize, ParseVariableString, ToJSON, GroupsScheduleListInit,
+ SourceForm, SetSchedulesInnerDialogSize) {
return function (params) {
var parent_scope = params.scope,
group_id = params.group_id,
- tree_id = params.tree_id,
mode = params.mode, // 'add' or 'edit'
inventory_id = params.inventory_id,
- groups_reload = params.groups_reload,
generator = GenerateForm,
group_created = false,
defaultUrl,
master = {},
choicesReady,
- base = $location.path().replace(/^\//, '').split('/')[0],
modal_scope = parent_scope.$new(),
properties_scope = parent_scope.$new(),
sources_scope = parent_scope.$new(),
@@ -730,7 +726,7 @@ function($compile, SchedulerInit, Rest, Wait, SetSchedulesInnerDialogSize, Sched
height: y,
autoOpen: false,
minWidth: 440,
- title: 'Edit Group',
+ title: (mode === 'edit') ? 'Edit Group' : 'Add Group',
closeOnEscape: false,
create: function () {
$('.ui-dialog[aria-describedby="group-modal-dialog"]').find('.ui-dialog-titlebar button').empty().attr({'class': 'close'}).text('x');
@@ -1038,14 +1034,8 @@ function($compile, SchedulerInit, Rest, Wait, SetSchedulesInnerDialogSize, Sched
}
parent_scope.removeAddTreeRefreshed = parent_scope.$on('GroupTreeRefreshed', function() {
// Clean up
- // Change the selected group
- if (groups_reload && parent_scope.selected_tree_id !== tree_id) {
- parent_scope.showHosts(tree_id, group_id, false);
- } else {
- Wait('stop');
- }
- //WatchInventoryWindowResize();
- parent_scope.removeAddTreeRefreshed();
+ Wait('stop');
+ WatchInventoryWindowResize();
if (modal_scope.searchCleanUp) {
modal_scope.searchCleanup();
}
@@ -1062,42 +1052,7 @@ function($compile, SchedulerInit, Rest, Wait, SetSchedulesInnerDialogSize, Sched
}
modal_scope.removeSaveComplete = modal_scope.$on('SaveComplete', function (e, error) {
if (!error) {
- // Update the parent view with any changes
- if (groups_reload) {
- $log.debug('calling UpdateGroup group_id: ' + group_id + ' name: ' + properties_scope.name + ' description: ' + properties_scope.description +
- 'has_inventory_sources: ' + ((sources_scope.source && sources_scope.source.value) ? 'true' : 'false') + ' source: ' + sources_scope.source.value );
- UpdateGroup({
- scope: parent_scope,
- group_id: group_id,
- properties: {
- name: properties_scope.name,
- description: properties_scope.description,
- has_inventory_sources: (sources_scope.source && sources_scope.source.value) ? true : false,
- source: (sources_scope.source && sources_scope.source.value) ? sources_scope.source.value : ''
- }
- });
- parent_scope.$emit('GroupTreeRefreshed');
- } else if (base === 'inventories') {
- if (mode === 'add') {
- BuildTree({
- scope: parent_scope,
- inventory_id: inventory_id,
- refresh: true,
- new_group_id: group_id
- });
- }
- else {
- parent_scope.$emit('GroupTreeRefreshed');
- }
- } else if (base === 'home') {
- parent_scope.restoreSearch();
- try {
- $('#group-modal-dialog').dialog('close');
- }
- catch(err) {
- // ignore
- }
- }
+ modal_scope.cancelModal();
}
});
@@ -1167,20 +1122,21 @@ function($compile, SchedulerInit, Rest, Wait, SetSchedulesInnerDialogSize, Sched
// Cancel
modal_scope.cancelModal = function () {
- Wait('stop');
try {
$('#group-modal-dialog').dialog('close');
}
catch(e) {
//ignore
}
-
- //if (modal_scope.searchCleanup) {
- // modal_scope.searchCleanup();
- //}
- //if (base === 'inventories') {
- // WatchInventoryWindowResize();
- //}
+ if (modal_scope.searchCleanup) {
+ modal_scope.searchCleanup();
+ }
+ if (parent_scope.restoreSearch) {
+ parent_scope.restoreSearch();
+ }
+ else {
+ Wait('stop');
+ }
};
// Save
@@ -1463,11 +1419,328 @@ function($compile, SchedulerInit, Rest, Wait, SetSchedulesInnerDialogSize, Sched
scope.$emit('DisassociateGroup');
}
};
-
};
}
])
+.factory('GetRootGroups', ['Rest', 'ProcessErrors', 'GetBasePath', function(Rest, ProcessErrors, GetBasePath) {
+ return function(params) {
+ var scope = params.scope,
+ inventory_id = params.inventory_id,
+ //group_id = params.group_id,
+ callback = params.callback,
+ url;
+
+ url = GetBasePath('inventory') + inventory_id + '/root_groups/';
+ Rest.setUrl(url);
+ Rest.get()
+ .success(function(data) {
+ scope.$emit(callback, data.results);
+ })
+ .error(function(data, status) {
+ ProcessErrors(scope, data, status, null, { hdr: 'Error!',
+ msg: 'Call to ' + url + ' failed. GET returned: ' + status });
+ });
+ };
+}])
+
+.factory('GroupsCopy', ['$compile', 'Rest', 'ProcessErrors', 'CreateDialog', 'GetBasePath', 'Wait', 'GenerateList', 'GroupList', 'SearchInit',
+ 'PaginateInit', 'GetRootGroups',
+ function($compile, Rest, ProcessErrors, CreateDialog, GetBasePath, Wait, GenerateList, GroupList, SearchInit, PaginateInit, GetRootGroups) {
+ return function(params) {
+
+ var group_id = params.group_id,
+ parent_scope = params.scope,
+ scope = parent_scope.$new(),
+ parent_group = parent_scope.selected_group_id,
+ buttonSet, url, group;
+
+ buttonSet = [{
+ label: "Cancel",
+ onClick: function() {
+ scope.cancel();
+ },
+ icon: "fa-times",
+ "class": "btn btn-default",
+ "id": "group-copy-cancel-button"
+ },{
+ label: "OK",
+ onClick: function() {
+ scope.performCopy();
+ },
+ icon: "fa-check",
+ "class": "btn btn-primary",
+ "id": "group-copy-ok-button"
+ }];
+
+ if (scope.removeGroupsCopyPostRefresh) {
+ scope.removeGroupsCopyPostRefresh();
+ }
+ scope.removeGroupCopyPostRefresh = scope.$on('PostRefresh', function() {
+ scope.copy_groups.forEach(function(row, i) {
+ scope.copy_groups[i].checked = '0';
+ });
+ Wait('stop');
+ $('#group-copy-dialog').dialog('open');
+ $('#group-copy-ok-button').attr('disabled','disabled');
+
+ // prevent backspace from navigation when not in input or textarea field
+ $(document).on("keydown", function (e) {
+ if (e.which === 8 && !$(e.target).is('input[type="text"], textarea')) {
+ e.preventDefault();
+ }
+ });
+
+ });
+
+ if (scope.removeCopyDialogReady) {
+ scope.removeCopyDialogReady();
+ }
+ scope.removeCopyDialogReady = scope.$on('CopyDialogReady', function() {
+ var url = GetBasePath('inventory') + parent_scope.inventory.id + '/groups/';
+ url += (parent_group) ? '?not__id__in=' + group_id + ',' + parent_group : '?not__id=' + group_id;
+ GenerateList.inject(GroupList, {
+ mode: 'lookup',
+ id: 'copy-select-container',
+ scope: scope
+ //,
+ //instructions: instructions
+ });
+ SearchInit({
+ scope: scope,
+ set: GroupList.name,
+ list: GroupList,
+ url: url
+ });
+ PaginateInit({
+ scope: scope,
+ list: GroupList,
+ url: url,
+ mode: 'lookup'
+ });
+ scope.search(GroupList.iterator);
+ });
+
+ if (scope.removeShowDialog) {
+ scope.removeShowDialog();
+ }
+ scope.removeShowDialog = scope.$on('ShowDialog', function() {
+ var d;
+ scope.name = group.name;
+ scope.copy_choice = "copy";
+ d = angular.element(document.getElementById('group-copy-dialog'));
+ $compile(d)(scope);
+
+ CreateDialog({
+ id: 'group-copy-dialog',
+ scope: scope,
+ buttons: buttonSet,
+ width: 650,
+ height: 650,
+ minWidth: 600,
+ title: 'Copy or Move Group',
+ callback: 'CopyDialogReady',
+ onClose: function() {
+ scope.cancel();
+ }
+ });
+ });
+
+ if (scope.removeRootGroupsReady) {
+ scope.removeRootGroupsReady();
+ }
+ scope.removeRootGroupsReady = scope.$on('RootGroupsReady', function(e, root_groups) {
+ scope.offer_root_group = true;
+ scope.use_root_group = false;
+ root_groups.every(function(row) {
+ if (row.id === group_id) {
+ scope.offer_root_group = false;
+ return false;
+ }
+ return true;
+ });
+ url = GetBasePath('groups') + group_id + '/';
+ Rest.setUrl(url);
+ Rest.get()
+ .success(function(data) {
+ group = data;
+ scope.$emit('ShowDialog');
+ })
+ .error(function(data, status) {
+ ProcessErrors(scope, data, status, null, { hdr: 'Error!',
+ msg: 'Call to ' + url + ' failed. GET returned: ' + status });
+ });
+ });
+
+ Wait('start');
+
+ GetRootGroups({
+ scope: scope,
+ group_id: group_id,
+ inventory_id: parent_scope.inventory.id,
+ callback: 'RootGroupsReady'
+ });
+
+ scope.cancel = function() {
+ $(document).off("keydown");
+ try {
+ $('#group-copy-dialog').dialog('close');
+ }
+ catch(e) {
+ // ignore
+ }
+ scope.searchCleanup();
+ parent_scope.restoreSearch();
+ scope.$destroy();
+ };
+
+ scope['toggle_' + GroupList.iterator] = function (id) {
+ var count = 0,
+ list = GroupList;
+ scope[list.name].forEach( function(row, i) {
+ if (row.id === id) {
+ if (row.checked === '0') {
+ scope[list.name][i].checked = '1';
+ scope[list.name][i].success_class = 'success';
+ }
+ else {
+ scope[list.name][i].checked = '0';
+ scope[list.name][i].success_class = '';
+ }
+ } else {
+ scope[list.name][i].checked = '0';
+ scope[list.name][i].success_class = '';
+ }
+ });
+ // Check if any rows are checked
+ scope[list.name].forEach(function(row) {
+ if (row.checked === '1') {
+ count++;
+ }
+ });
+ if (count === 0) {
+ $('#group-copy-ok-button').attr('disabled','disabled');
+ }
+ else {
+ $('#group-copy-ok-button').removeAttr('disabled');
+ }
+ };
+
+ scope.toggleUseRootGroup = function() {
+ var list = GroupList;
+ //console.log("scope.use_root_group: " + scope.use_root_group);
+ if (scope.use_root_group) {
+ $('#group-copy-ok-button').removeAttr('disabled');
+ }
+ else {
+ // check for group selection
+ $('#group-copy-ok-button').attr('disabled','disabled');
+ scope[list.name].every(function(row) {
+ if (row.checked === '1') {
+ $('#group-copy-ok-button').removeAttr('disabled');
+ return false;
+ }
+ return true;
+ });
+ }
+ };
+
+ scope.performCopy = function() {
+ var list = GroupList,
+ target,
+ url;
+
+ Wait('start');
+
+ if (scope.use_root_group) {
+ target = null;
+ }
+ else {
+ scope[list.name].every(function(row) {
+ if (row.checked === '1') {
+ target = row;
+ return false;
+ }
+ return true;
+ });
+ }
+
+ if (scope.copy_choice === 'move') {
+ // Respond to move
+
+ // disassociate the group from the original parent
+ if (scope.removeGroupRemove) {
+ scope.removeGroupRemove();
+ }
+ scope.removeGroupRemove = scope.$on('RemoveGroup', function () {
+ if (parent_group > 0) {
+ // Only remove a group from a parent when the parent is a group and not the inventory root
+ url = GetBasePath('groups') + parent_group + '/children/';
+ Rest.setUrl(url);
+ Rest.post({ id: group.id, disassociate: 1 })
+ .success(function () {
+ scope.cancel();
+ })
+ .error(function (data, status) {
+ ProcessErrors(scope, data, status, null, { hdr: 'Error!',
+ msg: 'Failed to remove ' + group.name + ' from group ' + parent_group + '. POST returned: ' + status });
+ });
+ } else {
+ scope.cancel();
+ }
+ });
+
+ // add the new group to the target
+ url = (target) ?
+ GetBasePath('groups') + target.id + '/children/' :
+ GetBasePath('inventory') + parent_scope.inventory.id + '/groups/';
+ group = {
+ id: group.id,
+ name: group.name,
+ description: group.description,
+ inventory: parent_scope.inventory.id
+ };
+ Rest.setUrl(url);
+ Rest.post(group)
+ .success(function () {
+ scope.$emit('RemoveGroup');
+ })
+ .error(function (data, status) {
+ var target_name = (target) ? target.name : 'inventory';
+ ProcessErrors(scope, data, status, null, { hdr: 'Error!',
+ msg: 'Failed to add ' + group.name + ' to ' + target_name + '. POST returned: ' + status });
+ });
+ }
+ else {
+ // Respond to copy by adding the new group to the target
+ url = (target) ?
+ GetBasePath('groups') + target.id + '/children/' :
+ GetBasePath('inventory') + parent_scope.inventory.id + '/groups/';
+
+ group = {
+ id: group.id,
+ name: group.name,
+ description: group.description,
+ inventory: parent_scope.inventory.id
+ };
+
+ Rest.setUrl(url);
+ Rest.post(group)
+ .success(function () {
+ scope.cancel();
+ })
+ .error(function (data, status) {
+ var target_name = (target) ? target.name : 'inventory';
+ ProcessErrors(scope, data, status, null, { hdr: 'Error!',
+ msg: 'Failed to add ' + group.name + ' to ' + target_name + '. POST returned: ' + status
+ });
+ });
+ }
+ };
+
+ };
+}])
+
.factory('ShowUpdateStatus', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'GenerateForm',
'Prompt', 'ProcessErrors', 'GetBasePath', 'FormatDate', 'InventoryStatusForm', 'Wait',
function ($rootScope, $location, $log, $routeParams, Rest, Alert, GenerateForm, Prompt, ProcessErrors, GetBasePath,
diff --git a/awx/ui/static/js/helpers/Hosts.js b/awx/ui/static/js/helpers/Hosts.js
index d9c0e9e409..a000d52529 100644
--- a/awx/ui/static/js/helpers/Hosts.js
+++ b/awx/ui/static/js/helpers/Hosts.js
@@ -14,7 +14,8 @@
angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'HostListDefinition',
'SearchHelper', 'PaginationHelpers', 'ListGenerator', 'AuthService', 'HostsHelper',
'InventoryHelper', 'RelatedSearchHelper', 'InventoryFormDefinition', 'SelectionHelper',
- 'HostGroupsFormDefinition', 'VariablesHelper', 'ModalDialog', 'LogViewerHelper'
+ 'HostGroupsFormDefinition', 'VariablesHelper', 'ModalDialog', 'LogViewerHelper',
+ 'GroupListDefinition'
])
.factory('SetEnabledMsg', [ function() {
@@ -167,18 +168,21 @@ function($routeParams, Empty, InventoryHosts, GetBasePath, SearchInit, PaginateI
return function(params) {
var scope = params.scope,
+ parent_scope = params.parent_scope,
group_id = params.group_id,
inventory_id = params.inventory_id,
list = InventoryHosts,
+ pageSize = (params.pageSize) ? params.pageSize : 20,
+
url = ( !Empty(group_id) ) ? GetBasePath('groups') + group_id + '/all_hosts/' :
GetBasePath('inventory') + inventory_id + '/hosts/';
scope.search_place_holder='Search ' + scope.selected_group_name;
- //if (scope.removePostRefresh) {
- // scope.removePostRefresh();
- //}
- scope.removePostRefresh = scope.$on('PostRefresh', function(e, set) {
+ if (scope.removeHostsReloadPostRefresh) {
+ scope.removeHostsReloadPostRefresh();
+ }
+ scope.removeHostsReloadPostRefresh = scope.$on('PostRefresh', function(e, set) {
if (set === 'hosts') {
for (var i=0; i < scope.hosts.length; i++) {
//Set tooltip for host enabled flag
@@ -187,7 +191,9 @@ function($routeParams, Empty, InventoryHosts, GetBasePath, SearchInit, PaginateI
SetStatus({ scope: scope });
setTimeout(function() { ApplyEllipsis('#hosts_table .host-name a'); }, 2500);
Wait('stop');
- scope.$emit('HostReloadComplete');
+ if (parent_scope) {
+ parent_scope.$emit('HostReloadComplete');
+ }
}
});
@@ -195,7 +201,7 @@ function($routeParams, Empty, InventoryHosts, GetBasePath, SearchInit, PaginateI
SetContainerHeights({ scope: scope, reloadHosts: false });
SearchInit({ scope: scope, set: 'hosts', list: list, url: url });
- PaginateInit({ scope: scope, list: list, url: url });
+ PaginateInit({ scope: scope, list: list, url: url, pageSize: pageSize });
if ($routeParams.host_name) {
scope[list.iterator + 'InputDisable'] = false;
@@ -220,16 +226,18 @@ function($routeParams, Empty, InventoryHosts, GetBasePath, SearchInit, PaginateI
function(GenerateList, InventoryHosts, HostsReload) {
return function(params) {
- var scope = params.scope,
+ var group_scope = params.group_scope,
+ host_scope = params.host_scope,
inventory_id = params.inventory_id,
group_id = params.group_id,
+ pageSize = params.pageSize,
generator = GenerateList;
// Inject the list html
- generator.inject(InventoryHosts, { scope: scope, mode: 'edit', id: 'hosts-container', breadCrumbs: false, searchSize: 'col-lg-6 col-md-6 col-sm-6' });
+ generator.inject(InventoryHosts, { scope: host_scope, mode: 'edit', id: 'hosts-container', breadCrumbs: false, searchSize: 'col-lg-6 col-md-6 col-sm-6' });
// Load data
- HostsReload({ scope: scope, group_id: group_id, inventory_id: inventory_id });
+ HostsReload({ scope: host_scope, group_id: group_id, inventory_id: inventory_id, parent_scope: group_scope, cpageSize: pageSize });
};
}])
@@ -336,8 +344,7 @@ function($rootScope, $location, $log, $routeParams, Rest, Alert, HostList, Gener
.factory('HostsCreate', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'HostForm', 'GenerateForm',
- 'Prompt', 'ProcessErrors', 'GetBasePath', 'HostsReload', 'ParseTypeChange', 'Wait',
- 'ToJSON',
+ 'Prompt', 'ProcessErrors', 'GetBasePath', 'HostsReload', 'ParseTypeChange', 'Wait', 'ToJSON',
function($rootScope, $location, $log, $routeParams, Rest, Alert, HostForm, GenerateForm, Prompt, ProcessErrors,
GetBasePath, HostsReload, ParseTypeChange, Wait, ToJSON) {
return function(params) {
@@ -437,13 +444,13 @@ function($rootScope, $location, $log, $routeParams, Rest, Alert, HostForm, Gener
.factory('HostsEdit', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'HostForm', 'GenerateForm',
'Prompt', 'ProcessErrors', 'GetBasePath', 'HostsReload', 'ParseTypeChange', 'Wait', 'Find', 'SetStatus', 'ApplyEllipsis',
- 'ToJSON', 'ParseVariableString', 'CreateDialog', 'TextareaResize', 'Empty',
+ 'ToJSON', 'ParseVariableString', 'CreateDialog', 'TextareaResize',
function($rootScope, $location, $log, $routeParams, Rest, Alert, HostForm, GenerateForm, Prompt, ProcessErrors,
GetBasePath, HostsReload, ParseTypeChange, Wait, Find, SetStatus, ApplyEllipsis, ToJSON,
- ParseVariableString, CreateDialog, TextareaResize, Empty) {
+ ParseVariableString, CreateDialog, TextareaResize) {
return function(params) {
- var parent_scope = params.scope,
+ var parent_scope = params.parent_scope,
host_id = params.host_id,
inventory_id = params.inventory_id,
mode = params.mode, // 'add' or 'edit'
@@ -451,10 +458,10 @@ function($rootScope, $location, $log, $routeParams, Rest, Alert, HostForm, Gener
generator = GenerateForm,
form = HostForm,
defaultUrl,
- scope = parent_scope.$new(),
+ scope = params.host_scope,
master = {},
relatedSets = {},
- group, buttons;
+ buttons, url;
generator.inject(HostForm, { mode: 'edit', id: 'host-modal-dialog', breadCrumbs: false, related: false, scope: scope });
generator.reset();
@@ -581,24 +588,26 @@ function($rootScope, $location, $log, $routeParams, Rest, Alert, HostForm, Gener
scope.$emit('hostLoaded');
})
.error( function(data, status) {
- ProcessErrors(scope, data, status, form,
+ ProcessErrors(parent_scope, data, status, form,
{ hdr: 'Error!', msg: 'Failed to retrieve host: ' + host_id + '. GET returned status: ' + status });
});
}
else {
// Add mode
- group = Find({ list: scope.groups, key: 'id', val: selected_group_id });
- if (!Empty(group)) {
- scope.has_inventory_sources = group.has_inventory_sources;
- scope.enabled = true;
- scope.variables = '---';
- defaultUrl = GetBasePath('groups') + group.group_id + '/hosts/';
- scope.$emit('hostVariablesLoaded');
- }
- else {
- ProcessErrors(scope, null, status, null, { hdr: 'Error',
- msg: 'Group lookup failed. Selected group id: ' + selected_group_id });
- }
+ url = GetBasePath('groups') + selected_group_id + '/';
+ Rest.setUrl(url);
+ Rest.get()
+ .success( function(data) {
+ scope.has_inventory_sources = data.has_inventory_sources;
+ scope.enabled = true;
+ scope.variables = '---';
+ defaultUrl = data.related.hosts;
+ scope.$emit('hostVariablesLoaded');
+ })
+ .error( function(data, status) {
+ ProcessErrors(parent_scope, data, status, form,
+ { hdr: 'Error!', msg: 'Failed to retrieve group: ' + selected_group_id + '. GET returned status: ' + status });
+ });
}
if (scope.removeSaveCompleted) {
@@ -608,12 +617,12 @@ function($rootScope, $location, $log, $routeParams, Rest, Alert, HostForm, Gener
var host, old_name;
if (mode === 'edit') {
// Update the name on the list
- host = Find({ list: parent_scope.hosts, key: 'id', val: host_id });
+ host = Find({ list: scope.hosts, key: 'id', val: host_id });
old_name = host.name;
host.name = scope.name;
host.enabled = (scope.enabled) ? true : false;
host.enabled_flag = host.enabled;
- SetStatus({ scope: parent_scope, host: host });
+ SetStatus({ scope: scope, host: host });
// Update any titles attributes created by ApplyEllipsis
if (old_name) {
setTimeout(function() {
@@ -630,12 +639,7 @@ function($rootScope, $location, $log, $routeParams, Rest, Alert, HostForm, Gener
}
else {
$('#host-modal-dialog').dialog('close');
- HostsReload({
- scope: parent_scope,
- group_id: parent_scope.selected_group_id,
- tree_id: parent_scope.selected_tree_id,
- inventory_id: parent_scope.inventory_id
- });
+ parent_scope.refreshHosts();
}
// Restore ellipsis response to window resize
@@ -691,48 +695,38 @@ function($rootScope, $location, $log, $routeParams, Rest, Alert, HostForm, Gener
};
scope.cancelModal = function() {
- $('#host-modal-dialog').dialog('close');
+ try {
+ $('#host-modal-dialog').dialog('close');
+ }
+ catch(err) {
+ // ignore
+ }
+ parent_scope.refreshHosts();
};
};
}])
-.factory('HostsDelete', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'Prompt', 'ProcessErrors', 'GetBasePath',
- 'HostsReload', 'Wait', 'Find', 'Empty',
-function($rootScope, $location, $log, $routeParams, Rest, Alert, Prompt, ProcessErrors, GetBasePath, HostsReload, Wait, Find, Empty) {
+.factory('HostsDelete', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'Prompt', 'ProcessErrors', 'GetBasePath', 'HostsReload', 'Wait',
+function($rootScope, $location, $log, $routeParams, Rest, Alert, Prompt, ProcessErrors, GetBasePath, HostsReload, Wait) {
return function(params) {
// Remove the selected host from the current group by disassociating
var action_to_take, body,
- scope = params.scope,
+ scope = params.parent_scope,
host_id = params.host_id,
host_name = params.host_name,
group,
url_list = [];
- function getChildren(tree_id) {
- var parent, found, j;
- for (j = 0; j < scope.groups.length; j++) {
- if (scope.groups[j].id === tree_id || scope.groups[j].parent === parent) {
- found = true;
- url_list.push(GetBasePath('groups') + scope.groups[j].group_id + '/hosts/');
- parent = scope.groups[j].id;
- }
- else {
- if (found) {
- break;
- }
- }
- }
- }
-
- if (!Empty(scope.selected_group_id)) {
- group = Find({ list: scope.groups, key: 'id', val: scope.selected_tree_id });
- getChildren(group.id);
+ if (scope.selected_group_id) {
+ //group = Find({ list: parent_scope.groups, key: 'id', val: parent_scope.selected_group_id });
+ //getChildren(group.id);
+ url_list.push(GetBasePath('groups') + scope.selected_group_id + '/hosts/');
}
else {
- url_list.push(GetBasePath('inventory') + scope.inventory_id + '/hosts/');
+ url_list.push(GetBasePath('inventory') + scope.inventory.id + '/hosts/');
}
if (scope.removeHostsReload) {
@@ -740,7 +734,7 @@ function($rootScope, $location, $log, $routeParams, Rest, Alert, Prompt, Process
}
scope.removeHostsReload = scope.$on('hostsReload', function() {
$('#prompt-modal').modal('hide');
- scope.showHosts(scope.selected_tree_id, scope.selected_group_id, false);
+ scope.refreshHosts();
});
$('#prompt-modal').on('hidden.bs.modal', function(){ Wait('stop'); });
@@ -782,6 +776,243 @@ function($rootScope, $location, $log, $routeParams, Rest, Alert, Prompt, Process
};
}])
+.factory('HostsCopy', ['$compile', 'Rest', 'ProcessErrors', 'CreateDialog', 'GetBasePath', 'Wait', 'GenerateList', 'GroupList', 'SearchInit',
+ 'PaginateInit',
+ function($compile, Rest, ProcessErrors, CreateDialog, GetBasePath, Wait, GenerateList, GroupList, SearchInit, PaginateInit) {
+ return function(params) {
+
+ var host_id = params.host_id,
+ group_scope = params.group_scope,
+ parent_scope = params.host_scope,
+ parent_group = group_scope.selected_group_id,
+ scope = parent_scope.$new(),
+ buttonSet, url, host;
+
+ buttonSet = [{
+ label: "Cancel",
+ onClick: function() {
+ scope.cancel();
+ },
+ icon: "fa-times",
+ "class": "btn btn-default",
+ "id": "host-copy-cancel-button"
+ },{
+ label: "OK",
+ onClick: function() {
+ scope.performCopy();
+ },
+ icon: "fa-check",
+ "class": "btn btn-primary",
+ "id": "host-copy-ok-button"
+ }];
+
+ if (scope.removeHostCopyPostRefresh) {
+ scope.removeHostCopyPostRefresh();
+ }
+ scope.removeHostCopyPostRefresh = scope.$on('PostRefresh', function() {
+ scope.copy_groups.forEach(function(row, i) {
+ scope.copy_groups[i].checked = '0';
+ });
+ Wait('stop');
+ $('#host-copy-dialog').dialog('open');
+ $('#host-copy-ok-button').attr('disabled','disabled');
+
+ // prevent backspace from navigation when not in input or textarea field
+ $(document).on("keydown", function (e) {
+ if (e.which === 8 && !$(e.target).is('input[type="text"], textarea')) {
+ e.preventDefault();
+ }
+ });
+ });
+
+ if (scope.removeHostCopyDialogReady) {
+ scope.removeHostCopyDialogReady();
+ }
+ scope.removeCopyDialogReady = scope.$on('HostCopyDialogReady', function() {
+ var url = GetBasePath('inventory') + group_scope.inventory.id + '/groups/';
+ GenerateList.inject(GroupList, {
+ mode: 'lookup',
+ id: 'copy-host-select-container',
+ scope: scope
+ //,
+ //instructions: instructions
+ });
+ SearchInit({
+ scope: scope,
+ set: GroupList.name,
+ list: GroupList,
+ url: url
+ });
+ PaginateInit({
+ scope: scope,
+ list: GroupList,
+ url: url,
+ mode: 'lookup'
+ });
+ scope.search(GroupList.iterator, null, true, false);
+ });
+
+ if (scope.removeShowDialog) {
+ scope.removeShowDialog();
+ }
+ scope.removeShowDialog = scope.$on('ShowDialog', function() {
+ var d;
+ scope.name = host.name;
+ scope.copy_choice = "copy";
+ d = angular.element(document.getElementById('host-copy-dialog'));
+ $compile(d)(scope);
+ CreateDialog({
+ id: 'host-copy-dialog',
+ scope: scope,
+ buttons: buttonSet,
+ width: 650,
+ height: 650,
+ minWidth: 600,
+ title: 'Copy or Move Host',
+ callback: 'HostCopyDialogReady',
+ onClose: function() {
+ scope.cancel();
+ }
+ });
+ });
+
+ Wait('start');
+
+ url = GetBasePath('hosts') + host_id + '/';
+ Rest.setUrl(url);
+ Rest.get()
+ .success(function(data) {
+ host = data;
+ scope.$emit('ShowDialog');
+ })
+ .error(function(data, status) {
+ ProcessErrors(scope, data, status, null, { hdr: 'Error!',
+ msg: 'Call to ' + url + ' failed. GET returned: ' + status });
+ });
+
+
+ scope.cancel = function() {
+ $(document).off("keydown");
+ try {
+ $('#host-copy-dialog').dialog('close');
+ }
+ catch(e) {
+ // ignore
+ }
+ scope.searchCleanup();
+ group_scope.restoreSearch(); // Restore all parent search stuff and refresh hosts and groups lists
+ scope.$destroy();
+ };
+
+ scope['toggle_' + GroupList.iterator] = function (id) {
+ var count = 0,
+ list = GroupList;
+ scope[list.name].forEach( function(row, i) {
+ if (row.id === id) {
+ if (row.checked === '0') {
+ scope[list.name][i].checked = '1';
+ scope[list.name][i].success_class = 'success';
+ }
+ else {
+ scope[list.name][i].checked = '0';
+ scope[list.name][i].success_class = '';
+ }
+ } else {
+ scope[list.name][i].checked = '0';
+ scope[list.name][i].success_class = '';
+ }
+ });
+ // Check if any rows are checked
+ scope[list.name].forEach(function(row) {
+ if (row.checked === '1') {
+ count++;
+ }
+ });
+ if (count === 0) {
+ $('#host-copy-ok-button').attr('disabled','disabled');
+ }
+ else {
+ $('#host-copy-ok-button').removeAttr('disabled');
+ }
+ };
+
+ scope.performCopy = function() {
+ var list = GroupList,
+ target,
+ url;
+
+ Wait('start');
+
+ if (scope.use_root_group) {
+ target = null;
+ }
+ else {
+ scope[list.name].every(function(row) {
+ if (row.checked === '1') {
+ target = row;
+ return false;
+ }
+ return true;
+ });
+ }
+
+ if (scope.copy_choice === 'move') {
+ // Respond to move
+
+ // disassociate the host from the original parent
+ if (scope.removeHostRemove) {
+ scope.removeHostRemove();
+ }
+ scope.removeHostRemove = scope.$on('RemoveHost', function () {
+ if (parent_group > 0) {
+ // Only remove a host from a parent when the parent is a group and not the inventory root
+ url = GetBasePath('groups') + parent_group + '/hosts/';
+ Rest.setUrl(url);
+ Rest.post({ id: host.id, disassociate: 1 })
+ .success(function () {
+ scope.cancel();
+ })
+ .error(function (data, status) {
+ ProcessErrors(scope, data, status, null, { hdr: 'Error!',
+ msg: 'Failed to remove ' + host.name + ' from group ' + parent_group + '. POST returned: ' + status });
+ });
+ } else {
+ scope.cancel();
+ }
+ });
+
+ // add the new host to the target
+ url = GetBasePath('groups') + target.id + '/hosts/';
+ Rest.setUrl(url);
+ Rest.post(host)
+ .success(function () {
+ scope.$emit('RemoveHost');
+ })
+ .error(function (data, status) {
+ ProcessErrors(scope, data, status, null, { hdr: 'Error!',
+ msg: 'Failed to add ' + host.name + ' to ' + target.name + '. POST returned: ' + status });
+ });
+ }
+ else {
+ // Respond to copy by adding the new host to the target
+ url = GetBasePath('groups') + target.id + '/hosts/';
+ Rest.setUrl(url);
+ Rest.post(host)
+ .success(function () {
+ scope.cancel();
+ })
+ .error(function (data, status) {
+ ProcessErrors(scope, data, status, null, { hdr: 'Error!',
+ msg: 'Failed to add ' + host.name + ' to ' + target.name + '. POST 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,
diff --git a/awx/ui/static/js/helpers/search.js b/awx/ui/static/js/helpers/search.js
index d0f6b5ca69..04db2bc5c6 100644
--- a/awx/ui/static/js/helpers/search.js
+++ b/awx/ui/static/js/helpers/search.js
@@ -279,7 +279,6 @@ angular.module('SearchHelper', ['RestServices', 'Utilities', 'RefreshHelper'])
url += connect + scope[iterator + 'ExtraParms'];
}
url = url.replace(/\&\&/g, '&');
-
if (calcOnly) {
scope.$emit('searchParamsReady', url);
}
diff --git a/awx/ui/static/js/lists/InventoryGroups.js b/awx/ui/static/js/lists/InventoryGroups.js
index 34c6dff9d9..e0f9f3533d 100644
--- a/awx/ui/static/js/lists/InventoryGroups.js
+++ b/awx/ui/static/js/lists/InventoryGroups.js
@@ -142,6 +142,7 @@ angular.module('InventoryGroupsDefinition', [])
mode: 'all',
ngClick: "copyGroup(group.id)",
awToolTip: 'Copy or move group',
+ ngShow: "group.id > 0",
dataPlacement: "top"
},
"delete": {
diff --git a/awx/ui/static/js/lists/InventoryHosts.js b/awx/ui/static/js/lists/InventoryHosts.js
index b8a1e1e867..ef0c371331 100644
--- a/awx/ui/static/js/lists/InventoryHosts.js
+++ b/awx/ui/static/js/lists/InventoryHosts.js
@@ -76,6 +76,12 @@ angular.module('InventoryHostsDefinition', [])
awToolTip: 'Edit host',
dataPlacement: 'top'
},
+ copy: {
+ mode: 'all',
+ ngClick: "copyHost(host.id)",
+ awToolTip: 'Copy or move host to another group',
+ dataPlacement: "top"
+ },
"delete": {
//label: 'Delete',
ngClick: "deleteHost(host.id, host.name)",
@@ -89,7 +95,7 @@ angular.module('InventoryHostsDefinition', [])
create: {
mode: 'all',
ngClick: "createHost()",
- ngHide: 'selected_tree_id == 1', //disable when 'All Hosts' selected
+ ngHide: '!selected_group_id', //disable when 'All Hosts' selected
awToolTip: "Create a new host"
},
stream: {
diff --git a/awx/ui/static/js/widgets/Stream.js b/awx/ui/static/js/widgets/Stream.js
index 5034a936ca..f08a06c4d1 100644
--- a/awx/ui/static/js/widgets/Stream.js
+++ b/awx/ui/static/js/widgets/Stream.js
@@ -92,7 +92,7 @@ angular.module('StreamWidget', ['RestServices', 'Utilities', 'StreamListDefiniti
.factory('StreamBreadCrumbs', ['$rootScope', '$location',
function ($rootScope, $location) {
return function () {
- // Load the breadcrumbs array. We have to do things a bit different than Utilities.LoadBreadcrumbs.
+ // Load the breadcrumbs array. We have to do things a bit different than Utilities.LoadBreadcrumbs.
// Rather than botch that all up, we'll do our own thing here.
$rootScope.breadcrumbs = [];
var path, title, i, j, paths = $location.path().split('/');
@@ -188,7 +188,7 @@ angular.module('StreamWidget', ['RestServices', 'Utilities', 'StreamListDefiniti
// labels
obj1 = activity.object1;
obj2 = activity.object2;
-
+
// objects
obj1_obj = (activity.summary_fields[obj1]) ? activity.summary_fields[obj1][0] : null;
if (obj1 === obj2) {
@@ -279,7 +279,7 @@ angular.module('StreamWidget', ['RestServices', 'Utilities', 'StreamListDefiniti
n, rows, scope;
if (activity) {
- // Setup changes field
+ // Setup changes field
activity.changes_stringified = JSON.stringify(activity.changes, null, '\t');
n = activity.changes_stringified.match(/\n/g);
rows = (n) ? n.length : 1;
@@ -300,7 +300,7 @@ angular.module('StreamWidget', ['RestServices', 'Utilities', 'StreamListDefiniti
$('#form-modal').on('show.bs.modal', function () {
$('#form-modal-body').css({
width: 'auto', //probably not needed
- height: 'auto', //probably not needed
+ height: 'auto', //probably not needed
'max-height': '100%'
});
});
@@ -349,7 +349,7 @@ angular.module('StreamWidget', ['RestServices', 'Utilities', 'StreamListDefiniti
if (paths.length > 1 && /^\d+/.test(paths[paths.length - 1])) {
type = paths[paths.length - 2];
type = (type === 'inventories') ? 'inventory' : type.replace(/s$/, '');
- //defaultUrl += '?object1=' + type + '&object1__id=' +
+ //defaultUrl += '?object1=' + type + '&object1__id=' +
defaultUrl += '?' + type + '__id=' + paths[paths.length - 1];
} else if (paths.length > 1) {
type = paths[paths.length - 1];
@@ -368,7 +368,7 @@ angular.module('StreamWidget', ['RestServices', 'Utilities', 'StreamListDefiniti
// Fix inventory name. The way we're doing breadcrumbs doesn't support bind variables.
if (inventory_name) {
- itm = Find({ list: $rootScope.breadcrumbs, key: 'title', val: '{{ inventory_name }}' });
+ itm = Find({ list: $rootScope.breadcrumbs, key: 'title', val: '{{ inventory.name }}' });
if (itm) {
itm.title = inventory_name;
}
@@ -424,7 +424,7 @@ angular.module('StreamWidget', ['RestServices', 'Utilities', 'StreamListDefiniti
scope.activities.forEach(function(activity, i) {
var row = scope.activities[i],
type, url;
-
+
if (scope.activities[i].summary_fields.actor) {
scope.activities[i].user = "" +
scope.activities[i].summary_fields.actor.username + "";
@@ -436,7 +436,7 @@ angular.module('StreamWidget', ['RestServices', 'Utilities', 'StreamListDefiniti
deleted = /^\_delete/;
obj1 = scope.activities[i].object1;
obj2 = scope.activities[i].object2;
-
+
if ((obj1 === "schedule" || obj2 === "schedule") && activity.summary_fields.schedule) {
if (activity.summary_fields.inventory_source) {
type = 'inventory_source';
diff --git a/awx/ui/static/less/ansible-ui.less b/awx/ui/static/less/ansible-ui.less
index 621ab37d05..93759e04ce 100644
--- a/awx/ui/static/less/ansible-ui.less
+++ b/awx/ui/static/less/ansible-ui.less
@@ -36,6 +36,7 @@
@import "log-viewer.less";
@import "job-details.less";
@import "jobs.less";
+@import "inventory-edit.less";
html, body { height: 100%; }
@@ -1823,7 +1824,7 @@ tr td button i {
}
#hosts-container.col-lg-6 {
- margin-toop: 15px;
+ margin-top: 15px;
padding-left: 15px;
padding-right: 15px;
}
diff --git a/awx/ui/static/less/inventory-edit.less b/awx/ui/static/less/inventory-edit.less
new file mode 100644
index 0000000000..b69d642c4a
--- /dev/null
+++ b/awx/ui/static/less/inventory-edit.less
@@ -0,0 +1,123 @@
+/*********************************************
+ * Copyright (c) 2014 AnsibleWorks, Inc.
+ *
+ * inventory-edit.less
+ *
+ * custom animation mixins for ansible-ui
+ *
+ */
+#inventory_edit {
+ #breadcrumbs .nav-path {
+ margin-bottom: 8px;
+ }
+}
+.group-breadcrumbs {
+ list-style: none;
+ overflow: hidden;
+ padding: 0;
+ margin: 0 0 8px 0;
+}
+.group-breadcrumbs li {
+ float: left;
+ height: 26px;
+ margin-top: 3px;
+ margin-bottom: 3px;
+}
+.group-breadcrumbs li a {
+ color: @white;
+ font-weight: normal;
+ text-decoration: none;
+ padding: 3px 8px 3px 20px;
+ background: @blue-dark; /* fallback color */
+ position: relative;
+ left: 0;
+ top: 0;
+ display: block;
+ float: left;
+}
+.group-breadcrumbs li.active a {
+ background: @grey;
+ color: @black;
+ font-weight: normal;
+}
+.group-breadcrumbs li a:after {
+ content: " ";
+ display: block;
+ width: 0;
+ height: 0;
+ border-top: 13px dashed transparent; /* Go big on the size, and let overflow hide */
+ border-bottom: 13px dashed transparent;
+ border-left: 11px solid @blue-dark;
+ position: absolute;
+ top: 50%;
+ margin-top: -13px;
+ left: 100%;
+ z-index: 2;
+}
+.group-breadcrumbs li.active a:after {
+ border-left: 13px solid @grey;
+}
+.group-breadcrumbs li a:before {
+ content: " ";
+ display: block;
+ width: 0;
+ height: 0;
+ border-top: 13px dashed transparent;
+ border-bottom: 13px dashed transparent;
+ border-left: 11px solid @white;
+ position: absolute;
+ top: 50%;
+ margin-top: -13px;
+ margin-left: 1px;
+ left: 100%;
+ z-index: 1;
+}
+.group-breadcrumbs li.active a:before {
+ border-left: 11px solid @white;
+}
+
+#group-copy-dialog,
+#host-copy-dialog {
+ .highlight {
+ font-size: 16px;
+ font-weight: bold;
+ color: red;
+ padding-right: 5px;
+ }
+ .title {
+ font-weight: bold;
+ margin-bottom: 15px;
+ }
+ .well {
+ padding-left: 8px;
+ padding-right: 8px;
+ padding-top: 8px;
+ padding-bottom: 8px;
+ }
+ .page-row ul li a {
+ font-size: 12px;
+ }
+ .page-row .col-lg-8 {
+ width: 100%;
+ }
+ .page-row .col-md-8 {
+ width: 100%;
+ }
+}
+
+#copy-group-radio-container .form-group {
+ margin-left: 20px;
+ margin-bottom: 10px;
+}
+
+#copy-group-target-container .form-group {
+ margin-top: 10px;
+ margin-left: 20px;
+ margin-bottom: 15px;
+}
+
+#group-list-container {
+ .list-actions {
+ margin-bottom: 0;
+ }
+}
diff --git a/awx/ui/static/lib/ansible/InventoryTree.js b/awx/ui/static/lib/ansible/InventoryTree.js
index 8ea9b739b7..99b3bb6081 100644
--- a/awx/ui/static/lib/ansible/InventoryTree.js
+++ b/awx/ui/static/lib/ansible/InventoryTree.js
@@ -7,7 +7,7 @@
* Build data for the tree selector table used on inventory detail page.
*
*/
-
+
'use strict';
angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper', 'PromptDialog'])
@@ -101,14 +101,14 @@ angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper', 'P
}
function buildGroups(tree_data, parent, level) {
-
+
var children, stat, hosts_status, group,
sorted = SortNodes(tree_data),
expand, show;
-
+
sorted.forEach( function(row, i) {
id++;
-
+
stat = GetSyncStatusMsg({
status: sorted[i].summary_fields.inventory_source.status,
has_inventory_sources: sorted[i].has_inventory_sources,
@@ -131,7 +131,7 @@ angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper', 'P
show = getShowState(sorted[i].id);
if (show === null) {
// this is a node we haven't seen before, so check the parent expand/collapse state
- // If parent is not expanded, then child should be hidden.
+ // If parent is not expanded, then child should be hidden.
show = true;
if (parent > 0) {
groups.every(function(g) {
@@ -249,7 +249,7 @@ angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper', 'P
])
-// Update a group with a set of properties
+// Update a group with a set of properties
.factory('UpdateGroup', ['ApplyEllipsis', 'GetSyncStatusMsg', 'Empty',
function (ApplyEllipsis, GetSyncStatusMsg, Empty) {
return function (params) {
@@ -258,9 +258,9 @@ angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper', 'P
group_id = params.group_id,
properties = params.properties,
i, p, grp, old_name, stat;
-
+
for (i = 0; i < scope.groups.length; i++) {
- if (scope.groups[i].group_id === group_id) {
+ if (scope.groups[i].id === group_id) {
grp = scope.groups[i];
for (p in properties) {
if (p === 'name') {
@@ -291,12 +291,12 @@ angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper', 'P
scope.groups[i][p] = properties[p];
}
}
- if (scope.groups[i].id === scope.selected_tree_id) {
+ /*if (scope.groups[i].id === scope.selected_tree_id) {
//Make sure potential group name change gets reflected throughout the page
scope.selected_group_name = scope.groups[i].name;
scope.search_place_holder = 'Search ' + scope.groups[i].name;
scope.hostSearchPlaceholder = 'Search ' + scope.groups[i].name;
- }
+ }*/
}
// Update any titles attributes created by ApplyEllipsis
@@ -406,7 +406,7 @@ angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper', 'P
show: true
});
- // Respond to move
+ // Respond to move
scope.moveGroup = function () {
var url, group, parent;
$('#copy-prompt-modal').modal('hide');
diff --git a/awx/ui/static/lib/ansible/Utilities.js b/awx/ui/static/lib/ansible/Utilities.js
index 76ad262b62..2089a3d4cd 100644
--- a/awx/ui/static/lib/ansible/Utilities.js
+++ b/awx/ui/static/lib/ansible/Utilities.js
@@ -19,7 +19,7 @@ angular.module('Utilities', ['RestServices', 'Utilities'])
.factory('ClearScope', [
function () {
return function () {
-
+
$('#form-modal .modal-body').empty();
$('#form-modal2 .modal-body').empty();
@@ -59,7 +59,7 @@ angular.module('Utilities', ['RestServices', 'Utilities'])
.factory('ToggleClass', function () {
return function (selector, cssClass) {
- // Toggles the existance of a css class on a given element
+ // Toggles the existance of a css class on a given element
if ($(selector) && $(selector).hasClass(cssClass)) {
$(selector).removeClass(cssClass);
} else if ($(selector)) {
@@ -69,7 +69,7 @@ angular.module('Utilities', ['RestServices', 'Utilities'])
})
-/*
+/*
* Pass in the header and message you want displayed on TB modal dialog found in index.html.
* Assumes an #id of 'alert-modal'. Pass in an optional TB alert class (i.e. alert-danger, alert-success,
* alert-info...). Pass an optional function(){}, if you want a specific action to occur when user
@@ -91,7 +91,7 @@ angular.module('Utilities', ['RestServices', 'Utilities'])
backdrop: 'static'
});
scope.disableButtons2 = (disableButtons) ? true : false;
-
+
$('#alert-modal2').on('hidden.bs.modal', function () {
if (action) {
action();
@@ -238,7 +238,7 @@ angular.module('Utilities', ['RestServices', 'Utilities'])
return (a === 'ies') ? 'y' : '';
}
- //Keep a list of path/title mappings. When we see /organizations/XX in the path, for example,
+ //Keep a list of path/title mappings. When we see /organizations/XX in the path, for example,
//we'll know the actual organization name it maps to.
if (!Empty(crumb)) {
found = false;
@@ -288,7 +288,7 @@ angular.module('Utilities', ['RestServices', 'Utilities'])
} else {
//if (/_/.test(paths[i])) {
// replace '_' with space and uppercase each word
-
+
//}
//title = paths[i].charAt(0).toUpperCase() + paths[i].slice(1);
title = paths[i].replace(/(?:^|_)\S/g, toUppercase).replace(/_/g, ' ');
@@ -313,7 +313,7 @@ angular.module('Utilities', ['RestServices', 'Utilities'])
.factory('HelpDialog', ['$rootScope', '$location', 'Store',
function ($rootScope, $location, Store) {
return function (params) {
-
+
var defn = params.defn,
current_step = params.step,
autoShow = params.autoShow || false;
@@ -463,9 +463,9 @@ angular.module('Utilities', ['RestServices', 'Utilities'])
])
-/*
+/*
* Split the current path by '/' and use the array elements from 0 up to and
- * including idx as the new path. If no idx value supplied, use 0 to length - 1.
+ * including idx as the new path. If no idx value supplied, use 0 to length - 1.
*
*/
.factory('ReturnToCaller', ['$location', 'Empty',
@@ -496,9 +496,9 @@ angular.module('Utilities', ['RestServices', 'Utilities'])
}
])
-/*
- * Display a spinning icon in the center of the screen to freeze the
- * UI while waiting on async things to complete (i.e. API calls).
+/*
+ * Display a spinning icon in the center of the screen to freeze the
+ * UI while waiting on async things to complete (i.e. API calls).
* Wait('start' | 'stop');
*
*/
@@ -575,16 +575,16 @@ angular.module('Utilities', ['RestServices', 'Utilities'])
}
])
-/*
+/*
* Make an Options call to the API and retrieve dropdown options
*
* GetChoices({
* scope: Parent $scope
- * url: API resource to access
+ * url: API resource to access
* field: API element in the response object that contains the option list.
* variable: Scope variable that will receive the list.
* callback: Optional. Will issue scope.$emit(callback) on completion.
- * choice_name: Optional. Used when list is found in a variable other than 'choices'.
+ * choice_name: Optional. Used when list is found in a variable other than 'choices'.
* })
*/
.factory('GetChoices', ['Rest', 'ProcessErrors',
@@ -655,8 +655,8 @@ angular.module('Utilities', ['RestServices', 'Utilities'])
}
])
-/*
- * DeugForm({ form: