diff --git a/awx/ui/static/js/controllers/Inventories.js b/awx/ui/static/js/controllers/Inventories.js index 6022108e47..a112b87717 100644 --- a/awx/ui/static/js/controllers/Inventories.js +++ b/awx/ui/static/js/controllers/Inventories.js @@ -276,7 +276,7 @@ function InventoriesAdd ($scope, $rootScope, $compile, $location, $log, $routePa Rest.put(json_data) .success( function(data, status, headers, config) { Wait('stop'); - $location.path('/inventories/' + inventory_id + '/groups'); + $location.path('/inventories/' + inventory_id + '/'); }) .error( function(data, status, headers, config) { Wait('stop'); @@ -286,7 +286,7 @@ function InventoriesAdd ($scope, $rootScope, $compile, $location, $log, $routePa } else { Wait('stop'); - $location.path('/inventories/' + inventory_id + '/groups'); + $location.path('/inventories/' + inventory_id + '/'); } }) .error( function(data, status, headers, config) { @@ -317,8 +317,8 @@ InventoriesAdd.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$lo function InventoriesEdit ($scope, $location, $routeParams, $compile, GenerateList, ClearScope, InventoryGroups, InventoryHosts, BuildTree, Wait, - GetSyncStatusMsg, InjectHosts, HostsReload, GroupsAdd, GroupsEdit, Breadcrumbs, LoadBreadCrumbs, Empty, Rest, - ProcessErrors, InventoryUpdate, Alert, ToggleChildren) + GetSyncStatusMsg, InjectHosts, HostsReload, GroupsAdd, GroupsEdit, GroupsDelete, Breadcrumbs, LoadBreadCrumbs, Empty, + Rest, ProcessErrors, InventoryUpdate, Alert, ToggleChildren, ViewUpdateStatus) { ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior //scope. @@ -331,37 +331,61 @@ function InventoriesEdit ($scope, $location, $routeParams, $compile, GenerateLis LoadBreadCrumbs(); - if ($scope.removeSearchTreeReady) { - $scope.removeSearchTreeReady(); + if ($scope.removeGroupTreeLoaded) { + $scope.removeGroupTreeLoaded(); } - $scope.removeSearchTreeReady = $scope.$on('searchTreeReady', function(e, inventory_name, groups) { + $scope.removeGroupTreeLoaded = $scope.$on('groupTreeLoaded', function(e, inventory_name, groups) { // After the tree data loads, generate the groups list // Add breadcrumbs var e = angular.element(document.getElementById('breadcrumbs')); e.html(Breadcrumbs({ list: list, mode: 'edit' })); $compile(e)($scope); - // Add groups + + // Add groups view generator.inject(list, { mode: 'edit', id: 'groups-container', breadCrumbs: false, searchSize: 'col-lg-5 col-md-5 col-sm-5' }); $scope.groups = groups; $scope.inventory_name = inventory_name; - // Add hosts - InjectHosts({ scope: $scope, inventory_id: $scope.inventory_id }); + + // 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; + } + else { + $scope.selected_tree_id = null; + $scope.selected_group_id = null; + } + + // Add hosts view + InjectHosts({ scope: $scope, inventory_id: $scope.inventory_id, tree_id: $scope.selected_tree_id, group_id: $scope.selected_group_id }); + Wait('stop'); }); + + if ($scope.removeGroupTreeRefreshed) { + $scope.removeGroupTreeRefreshed(); + } + $scope.removeGroupTreeRefreshed = $scope.$on('groupTreeRefreshed', function(e, inventory_name, groups) { + // Called after tree data is reloaded on refresh button click. + // Reselect the preveiously selected group node, causing host view to refresh. + $scope.showHosts($scope.selected_tree_id, $scope.selected_group_id); + }); + + if ($scope.removeGroupDeleteCompleted) { + $scope.removeGroupDeleteCompleted(); + } + $scope.removeGroupDeleteCompleted = $scope.$on('groupDeleteCompleted', function(e) { + // Group was deleted. Now we need to refresh the group view. + BuildTree({ scope: $scope, inventory_id: $scope.inventory_id, refresh: true }); + }); $scope.showHosts = function(tree_id, group_id) { // Clicked on group - //$scope.groups[tree_id].selected_class = 'selected'; - $scope.selected_tree_id = tree_id; - $scope.selected_group_id = group_id; - for (var i=0; i < $scope.groups.length; i++) { - $scope.groups[i].selected_class = ($scope.groups[i].id == tree_id) ? 'selected' : ''; - if ($scope.groups[i].id == tree_id) { - $scope.selected_group_name = $scope.groups[i].name; - } + if (tree_id !== null) { + $scope.selected_tree_id = tree_id; + $scope.selected_group_id = group_id; + HostsReload({ scope: $scope, group_id: group_id, tree_id: tree_id, inventory_id: $scope.inventory_id }); } - $scope.search_place_holder='Search ' + $scope.selected_group_name; - HostsReload({ scope: $scope, group_id: group_id, inventory_id: $scope.inventory_id }); } $scope.createGroup = function() { @@ -407,20 +431,32 @@ function InventoriesEdit ($scope, $location, $routeParams, $compile, GenerateLis } } - // Expand/collapse nodes $scope.toggle = function(id) { - ToggleChildren({ - scope: $scope, - list: list, - id: id, - }); + // Expand/collapse nodes + ToggleChildren({ scope: $scope, list: list, id: id }); } - BuildTree({ scope: $scope, inventory_id: $scope.inventory_id }); + $scope.refreshGroups = function() { + // Refresh the tree data when refresh button cicked + BuildTree({ scope: $scope, inventory_id: $scope.inventory_id, refresh: true }); + } + + $scope.viewUpdateStatus = function(id) { + ViewUpdateStatus({ scope: $scope, tree_id: id }); + } + + $scope.deleteGroup = function(id) { + GroupsDelete({ scope: $scope, tree_id: id, inventory_id: $scope.inventory_id }); + } + + //Load tree data for the first time + BuildTree({ scope: $scope, inventory_id: $scope.inventory_id, refresh: false }); + } InventoriesEdit.$inject = [ '$scope', '$location', '$routeParams', '$compile', 'GenerateList', 'ClearScope', 'InventoryGroups', 'InventoryHosts', - 'BuildTree', 'Wait', 'GetSyncStatusMsg', 'InjectHosts', 'HostsReload', 'GroupsAdd', 'GroupsEdit', 'Breadcrumbs', - 'LoadBreadCrumbs', 'Empty', 'Rest', 'ProcessErrors', 'InventoryUpdate', 'Alert', 'ToggleChildren' + 'BuildTree', 'Wait', 'GetSyncStatusMsg', 'InjectHosts', 'HostsReload', 'GroupsAdd', 'GroupsEdit', 'GroupsDelete', + 'Breadcrumbs', 'LoadBreadCrumbs', 'Empty', 'Rest', 'ProcessErrors', 'InventoryUpdate', 'Alert', 'ToggleChildren', + 'ViewUpdateStatus' ]; diff --git a/awx/ui/static/js/forms/Groups.js b/awx/ui/static/js/forms/Groups.js index 74c5bff6c0..1ffd681af6 100644 --- a/awx/ui/static/js/forms/Groups.js +++ b/awx/ui/static/js/forms/Groups.js @@ -109,7 +109,7 @@ angular.module('GroupFormDefinition', []) type: 'textarea', addRequired: false, editRequird: false, - rows: 10, + rows: 6, 'default': '---', parseTypeName: 'envParseType', dataTitle: 'Source Variables', diff --git a/awx/ui/static/js/helpers/Groups.js b/awx/ui/static/js/helpers/Groups.js index 648c443e9f..14e3dcba85 100644 --- a/awx/ui/static/js/helpers/Groups.js +++ b/awx/ui/static/js/helpers/Groups.js @@ -82,12 +82,12 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', ' } }]) - .factory('ViewUpdateStatus', [ 'Rest', 'ProcessErrors', 'GetBasePath', 'ShowUpdateStatus', 'Alert', 'Wait', - function(Rest, ProcessErrors, GetBasePath, ShowUpdateStatus, Alert, Wait) { + .factory('ViewUpdateStatus', [ 'Rest', 'ProcessErrors', 'GetBasePath', 'ShowUpdateStatus', 'Alert', 'Wait', 'Empty', + function(Rest, ProcessErrors, GetBasePath, ShowUpdateStatus, Alert, Wait, Empty) { return function(params) { var scope = params.scope; - var id = params.group_id; + var id = params.tree_id; var found = false; var group; for (var i=0; i < scope.groups.length; i++) { @@ -97,14 +97,13 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', ' } } if (found) { - if (group.summary_fields.inventory_source.source == "" || group.summary_fields.inventory_source.source == null) { - Alert('Missing Configuration', 'The selected group is not configured for inventory updates. ' + - 'You must first edit the group, provide Source settings, and then run an update.', 'alert-info'); + if (Empty(group.source)) { + Alert('Missing Configuration', 'The selected group is not configured for inventory sync. ' + + 'You must first edit the group, provide Source settings, and then run the sync process.', 'alert-info'); } - else if (group.summary_fields.inventory_source.status == "" || group.summary_fields.inventory_source.status == null || - group.summary_fields.inventory_source.status == "never updated") { - Alert('No Status Available', 'The inventory update process has not run for the selected group. Start the process by ' + - 'clicking the Update button.', 'alert-info'); + else if (Empty(group.status) || group.status == "never updated") { + Alert('No Status Available', 'An inventory sync has not been performed for the selected group. Start the process by ' + + 'clicking the button.', 'alert-info'); } else { Wait('start'); @@ -115,7 +114,7 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', ' ShowUpdateStatus({ group_name: data.summary_fields.group.name, last_update: url, - license_error: [ (data.summary_fields.last_update && data.summary_fields.last_update.license_error) ? true : false ] + license_error: (data.summary_fields.last_update && data.summary_fields.last_update.license_error) ? true : false }); }) .error( function(data, status, headers, config) { @@ -167,7 +166,7 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', ' // many hosts with 0 failures tip = "All " + total_hosts + " hosts in this group are happy! None of them have " + " a recent job failure. Click to view the hosts."; - links = '/#/inventories/' + inventory_id + '/hosts/?group=' + group_id; + link = '/#/inventories/' + inventory_id + '/hosts/?group=' + group_id; html_class = 'false'; } } @@ -596,15 +595,53 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', ' } } } - } - }]) + } + }]) + + .factory('SourceChange', [ 'GetBasePath', 'CredentialList', 'LookUpInit', + function(GetBasePath, CredentialList, LookUpInit){ + return function(params) { + + var scope = params.scope; + var form = params.form; + + if (scope['source'].value == 'file') { + scope.sourcePathRequired = true; + } + else { + scope.sourcePathRequired = false; + // reset fields + scope.source_path = ''; + scope[form.name + '_form']['source_path'].$setValidity('required',true); + } + if (scope['source'].value == 'rax') { + scope['source_region_choices'] = scope['rax_regions']; + //$('#s2id_group_source_regions').select2('data', []); + $('#s2id_group_source_regions').select2('data', [{ id: 'all', text: 'All' }]); + } + else if (scope['source'].value == 'ec2') { + scope['source_region_choices'] = scope['ec2_regions']; + //$('#s2id_group_source_regions').select2('data', []); + $('#s2id_group_source_regions').select2('data', [{ id: 'all', text: 'All' }]); + } + var kind = (scope.source.value == 'rax') ? 'rax' : 'aws'; + var url = GetBasePath('credentials') + '?cloud=true&kind=' + kind; + LookUpInit({ + url: url, + scope: scope, + form: form, + list: CredentialList, + field: 'credential' + }); + } + }]) .factory('GroupsAdd', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'GroupForm', 'GenerateForm', 'Prompt', 'ProcessErrors', 'GetBasePath', 'ParseTypeChange', 'GroupsEdit', 'ClickNode', 'Wait', 'GetChoices', - 'GetSourceTypeOptions', 'CredentialList', 'LookUpInit', 'BuildTree', + 'GetSourceTypeOptions', 'LookUpInit', 'BuildTree', 'SourceChange', function($rootScope, $location, $log, $routeParams, Rest, Alert, GroupForm, GenerateForm, Prompt, ProcessErrors, - GetBasePath, ParseTypeChange, GroupsEdit, ClickNode, Wait, GetChoices, GetSourceTypeOptions, CredentialList, - LookUpInit, BuildTree) { + GetBasePath, ParseTypeChange, GroupsEdit, ClickNode, Wait, GetChoices, GetSourceTypeOptions, LookUpInit, BuildTree, + SourceChange) { return function(params) { var inventory_id = params.inventory_id; @@ -635,12 +672,13 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', ' generator.reset(); var master={}; - if (scope.removeSearchTreeReady) { - scope.removeSearchTreeReady(); + if (scope.removeAddTreeRefreshed) { + scope.removeAddTreeRefreshed(); } - scope.removeSearchTreeReady = scope.$on('searchTreeReady', function(e) { + scope.removeAddTreeRefreshed = scope.$on('groupTreeRefreshed', function(e) { Wait('stop'); $('#form-modal').modal('hide'); + scope.removeAddTreeRefreshed(); }); if (scope.removeSaveComplete) { @@ -650,7 +688,7 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', ' if (!error) { scope.formModalActionDisabled = false; scope.showGroupHelp = false; //get rid of the Hint - BuildTree({ scope: inventory_scope, inventory_id: inventory_id }); + BuildTree({ scope: inventory_scope, inventory_id: inventory_id, refresh: true }); } }); @@ -671,15 +709,15 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', ' if (scope.source.value !== null && scope.source.value !== '') { var data = { - group: group_id, - source: scope['source'].value, - source_path: scope['source_path'], - credential: scope['credential'], - overwrite: scope['overwrite'], - overwrite_vars: scope['overwrite_vars'], - update_on_launch: scope['update_on_launch'] - //update_interval: scope['update_interval'].value - }; + group: group_id, + source: scope['source'].value, + source_path: scope['source_path'], + credential: scope['credential'], + overwrite: scope['overwrite'], + overwrite_vars: scope['overwrite_vars'], + update_on_launch: scope['update_on_launch'] + //update_interval: scope['update_interval'].value + }; // Create a string out of selected list of regions var regions = $('#s2id_group_source_regions').select2("data"); @@ -793,32 +831,18 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', ' } } - scope.sourceChange = function() { - if (scope['source'].value == 'rax') { - scope['source_region_choices'] = scope['rax_regions']; - //$('#s2id_group_source_regions').select2('data', []); - $('#s2id_group_source_regions').select2('data', [{ id: 'all', text: 'All' }]); - } - else if (scope['source'].value == 'ec2') { - scope['source_region_choices'] = scope['ec2_regions']; - //$('#s2id_group_source_regions').select2('data', []); - $('#s2id_group_source_regions').select2('data', [{ id: 'all', text: 'All' }]); - } - LookUpInit({ - url: GetBasePath('credentials') + - '?cloud=true&kind=' + [ (scope.source.value == 'rax') ? 'rax' : 'aws' ], - scope: scope, - form: form, - list: CredentialList, - field: 'credential' - }); - } + scope.sourceChange = function() { + SourceChange({ + scope: scope, + form: GroupForm + }); + } // Cancel scope.formReset = function() { - // Defaults - generator.reset(); - }; + // Defaults + generator.reset(); + }; var choicesReady = 0; @@ -860,10 +884,10 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', ' .factory('GroupsEdit', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'GroupForm', 'GenerateForm', 'Prompt', 'ProcessErrors', 'GetBasePath', 'SetNodeName', 'ParseTypeChange', 'GetSourceTypeOptions', 'InventoryUpdate', - 'GetUpdateIntervalOptions', 'ClickNode', 'LookUpInit', 'CredentialList', 'Empty', 'Wait', 'GetChoices', 'UpdateGroup', + 'GetUpdateIntervalOptions', 'ClickNode', 'LookUpInit', 'Empty', 'Wait', 'GetChoices', 'UpdateGroup', 'SourceChange', function($rootScope, $location, $log, $routeParams, Rest, Alert, GroupForm, GenerateForm, Prompt, ProcessErrors, GetBasePath, SetNodeName, ParseTypeChange, GetSourceTypeOptions, InventoryUpdate, GetUpdateIntervalOptions, ClickNode, - LookUpInit, CredentialList, Empty, Wait, GetChoices, UpdateGroup) { + LookUpInit, Empty, Wait, GetChoices, UpdateGroup, SourceChange) { return function(params) { var group_id = params.group_id; @@ -892,7 +916,7 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', ' ParseTypeChange(scope); ParseTypeChange(scope, 'source_vars', form.fields['source_vars'].parseTypeName); - + // After the group record is loaded, retrieve related data if (scope.groupLoadedRemove) { scope.groupLoadedRemove(); @@ -995,15 +1019,6 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', ' } } - LookUpInit({ - url: GetBasePath('credentials') + - '?cloud=true&kind=' + (scope.source.value == 'rax') ? 'rax' : 'aws', - scope: scope, - form: form, - list: CredentialList, - field: 'credential' - }); - scope.sourceChange(); //set defaults that rely on source value if (data['source_regions']) { @@ -1262,36 +1277,6 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', ' } }; - scope.sourceChange = function() { - if (scope['source'].value == 'file') { - scope.sourcePathRequired = true; - } - else { - scope.sourcePathRequired = false; - // reset fields - scope.source_path = ''; - scope[form.name + '_form']['source_path'].$setValidity('required',true); - } - if (scope['source'].value == 'rax') { - scope['source_region_choices'] = scope['rax_regions']; - //$('#s2id_group_source_regions').select2('data', []); - $('#s2id_group_source_regions').select2('data', [{ id: 'all', text: 'All' }]); - } - else if (scope['source'].value == 'ec2') { - scope['source_region_choices'] = scope['ec2_regions']; - //$('#s2id_group_source_regions').select2('data', []); - $('#s2id_group_source_regions').select2('data', [{ id: 'all', text: 'All' }]); - } - LookUpInit({ - url: GetBasePath('credentials') + - '?cloud=true&kind=' + (scope.source.value == 'rax') ? 'rax' : 'aws', - scope: scope, - form: form, - list: CredentialList, - field: 'credential' - }); - } - // Start the update process scope.updateGroup = function() { if (scope.source == "" || scope.source == null) { @@ -1322,7 +1307,15 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', ' }); } } - + + // Change the lookup and regions when the source changes + scope.sourceChange = function() { + SourceChange({ + scope: scope, + form: GroupForm + }); + } + // Cancel scope.formReset = function() { generator.reset(); @@ -1338,20 +1331,37 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', ' .factory('GroupsDelete', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'GroupForm', 'GenerateForm', - 'Prompt', 'ProcessErrors', 'GetBasePath', 'Wait', 'ClickNode', + 'Prompt', 'ProcessErrors', 'GetBasePath', 'Wait', 'ClickNode', 'BuildTree', function($rootScope, $location, $log, $routeParams, Rest, Alert, GroupForm, GenerateForm, Prompt, ProcessErrors, - GetBasePath, Wait, ClickNode) { + GetBasePath, Wait, ClickNode, BuildTree) { return function(params) { // Delete the selected group node. Disassociates it from its parent. - var scope = params.scope; - var group_id = params.group_id; - var inventory_id = params.inventory_id; - var obj = scope['selectedNode']; - var parent = obj.parent().parent(); - var url; - if (parent.attr('data-group-id')) { - url = GetBasePath('base') + 'groups/' + parent.attr('data-group-id') + '/children/'; + var scope = params.scope; + var tree_id = params.tree_id; + var inventory_id = params.inventory_id; + + var node, parent, url, parent_name; + + for (var i=0; i < scope.groups.length; i++) { + if (scope.groups[i].id == tree_id) { + node = scope.groups[i]; + } + } + if (node.parent != 0) { + for (var i=0; i < scope.groups.length; i++) { + if (scope.groups[i].id == node.parent) { + parent = scope.groups[i]; + parent_name = scope.groups[i].name; + } + } + } + else { + parent_name = scope.inventory_name; + } + + if (parent) { + url = GetBasePath('base') + 'groups/' + parent.group_id + '/children/'; } else { url = GetBasePath('inventory') + inventory_id + '/groups/'; @@ -1360,37 +1370,33 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', ' $('#prompt-modal').on('hidden.bs.modal', function(){ Wait('start'); }); $('#prompt-modal').modal('hide'); Rest.setUrl(url); - Rest.post({ id: group_id, disassociate: 1 }) + Rest.post({ id: node.group_id, disassociate: 1 }) .success( function(data, status, headers, config) { - //DeleteNode({ selector: '#' + obj.attr('id') }); - /*BuildTree({ - scope: scope, - inventory_id: scope['inventory_id'], - emit_on_select: 'NodeSelect', - target_id: 'search-tree-container', - refresh: true, - id: parent.attr('id'), - moveable: true - });*/ $('#prompt-modal').off(); + scope.$emit('groupDeleteCompleted'); // Signal a group refresh to start }) .error( function(data, status, headers, config) { Wait('stop'); ProcessErrors(scope, data, status, null, - { hdr: 'Error!', msg: 'Call to ' + url + ' failed. DELETE returned status: ' + status }); + { hdr: 'Error!', msg: 'Call to ' + url + ' failed. POST returned status: ' + status }); }); }; + + Prompt({ hdr: 'Delete Group', body: '

Are you sure you want to remove group ' + node.name + + ' from ' + parent_name + '?

', action: action_to_take, + 'class': 'btn-danger' }); + //Force binds to work. Not working usual way. - $('#prompt-header').text('Delete Group'); - $('#prompt-body').html('

Are you sure you want to remove group ' + $(obj).attr('data-name') + ' from group ' + - parent.attr('data-name') + '?

'); - $('#prompt-action-btn').addClass('btn-danger'); - scope.promptAction = action_to_take; // for some reason this binds? - $('#prompt-modal').modal({ - backdrop: 'static', - keyboard: true, - show: true - }); + //$('#prompt-header').text('Delete Group'); + //$('#prompt-body').html('

Are you sure you want to remove group ' + $(obj).attr('data-name') + ' from group ' + + // parent.attr('data-name') + '?

'); + //$('#prompt-action-btn').addClass('btn-danger'); + //scope.promptAction = action_to_take; // for some reason this binds? + //('#prompt-modal').modal({ + // backdrop: 'static', + // keyboard: true, + // show: true + // }); } }]) diff --git a/awx/ui/static/js/helpers/Hosts.js b/awx/ui/static/js/helpers/Hosts.js index e03583ec3d..fb24216f7c 100644 --- a/awx/ui/static/js/helpers/Hosts.js +++ b/awx/ui/static/js/helpers/Hosts.js @@ -20,11 +20,20 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H var scope = params.scope; var group_id = params.group_id; + var tree_id = params.tree_id var inventory_id = params.inventory_id; var url = ( !Empty(group_id) ) ? GetBasePath('groups') + group_id + '/all_hosts/' : GetBasePath('inventory') + inventory_id + '/hosts/'; - + + for (var i=0; i < scope.groups.length; i++) { + scope.groups[i].selected_class = (scope.groups[i].id == tree_id) ? 'selected' : ''; + if (scope.groups[i].id == tree_id) { + scope.selected_group_name = scope.groups[i].name; + } + } + scope.search_place_holder='Search ' + scope.selected_group_name; + SearchInit({ scope: scope, set: 'hosts', list: InventoryHosts, url: url }); PaginateInit({ scope: scope, list: InventoryHosts, url: url }); scope.search(InventoryHosts.iterator); @@ -37,10 +46,12 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H var scope = params.scope; var inventory_id = params.inventory_id; + var group_id = params.group_id; + var tree_id = params.tree_id; var generator = GenerateList; generator.inject(InventoryHosts, { mode: 'edit', id: 'hosts-container', breadCrumbs: false, searchSize: 'col-lg-6 col-md-6 col-sm-6' }); - HostsReload({ scope: scope, group_id: null, inventory_id: inventory_id }); + HostsReload({ scope: scope, group_id: group_id, tree_id: tree_id, inventory_id: inventory_id }); } }]) diff --git a/awx/ui/static/js/lists/InventoryGroups.js b/awx/ui/static/js/lists/InventoryGroups.js index 4177403e9c..1b855a5b66 100644 --- a/awx/ui/static/js/lists/InventoryGroups.js +++ b/awx/ui/static/js/lists/InventoryGroups.js @@ -23,12 +23,13 @@ angular.module('InventoryGroupsDefinition', []) fields: { name: { - label: 'Group', + label: 'Groups', key: true, ngClick: "\{\{ 'showHosts(' + group.id + ',' + group.group_id + ')' \}\}", ngClass: "group.selected_class", hasChildren: true, - columnClass: 'col-lg-9' + columnClass: 'col-lg-9', + nosort: true }, source: { label: 'Source', @@ -87,7 +88,7 @@ angular.module('InventoryGroupsDefinition', []) refresh: { mode: 'all', awToolTip: "Refresh the page", - ngClick: "refresh()" + ngClick: "refreshGroups()" }, stream: { ngClick: "showActivity()", @@ -110,6 +111,7 @@ angular.module('InventoryGroupsDefinition', []) sync_status: { mode: 'all', ngClick: "viewUpdateStatus(\{\{ group.id \}\})", + ngShow: "group.id > 1", // hide for all hosts awToolTip: "\{\{ group.status_tooltip \}\}", ngClass: "group.status_class", dataPlacement: "top" @@ -117,6 +119,7 @@ angular.module('InventoryGroupsDefinition', []) failed_hosts: { mode: 'all', awToolTip: "\{\{ group.hosts_status_tip \}\}", + ngShow: "group.id > 1", // hide for all hosts dataPlacement: "top", ngClick: "viewFailedHosts(\{\{ group.id \}\})", iconClass: "\{\{ 'fa icon-failures-' + group.hosts_status_class \}\}" @@ -125,6 +128,7 @@ angular.module('InventoryGroupsDefinition', []) //label: 'Sync', ngClick: 'updateGroup(\{\{ group.id \}\})', awToolTip: "\{\{ group.launch_tooltip \}\}", + ngShow: "group.id > 1", // hide for all hosts ngClass: "group.launch_class", dataPlacement: "top" }, @@ -133,19 +137,21 @@ angular.module('InventoryGroupsDefinition', []) ngClick: "cancelUpdate(\{\{ group.id \}\}, '\{\{ group.name \}\}')", awToolTip: "Cancel sync process", ngClass: "group.cancel_class", - ngShow: "group.status == 'running' || group.status == 'pending'", + ngShow: "group.id > 1 && (group.status == 'running' || group.status == 'pending')", dataPlacement: "top" }, edit: { //label: 'Edit', ngClick: "editGroup(\{\{ group.group_id \}\})", awToolTip: 'Edit group', + ngShow: "group.id > 1", // hide for all hosts dataPlacement: "top" }, "delete": { //label: 'Delete', - ngClick: "deleteGroup(\{\{ group.group_id \}\},'\{\{ group.name \}\}')", + ngClick: "deleteGroup(\{\{ group.id \}\})", awToolTip: 'Delete group', + ngShow: "group.id != 1", // hide for all hosts dataPlacement: "top" } } diff --git a/awx/ui/static/js/lists/InventoryHosts.js b/awx/ui/static/js/lists/InventoryHosts.js index 28307a146e..32553e0609 100644 --- a/awx/ui/static/js/lists/InventoryHosts.js +++ b/awx/ui/static/js/lists/InventoryHosts.js @@ -23,7 +23,7 @@ angular.module('InventoryHostsDefinition', []) fields: { name: { key: true, - label: 'Name', + label: 'Hosts', ngClick: "editHost(\{\{ host.id \}\}, '\{\{ host.name \}\}')", searchPlaceholder: "search_place_holder" }, @@ -51,14 +51,14 @@ angular.module('InventoryHostsDefinition', []) searchable: false, showValue: false }, - groups: { + /*groups: { label: 'Groups', searchable: true, sourceModel: 'groups', sourceField: 'name', nosort: true, searchPlaceholder: "search_place_holder" - }, + },*/ enabled: { label: 'Disabled?', searchSingleValue: true, @@ -72,27 +72,28 @@ angular.module('InventoryHostsDefinition', []) searchType: 'boolean', searchValue: 'true', searchOnly: true - }, + } + /* , has_inventory_sources: { label: 'Has external source?', searchSingleValue: true, searchType: 'boolean', searchValue: 'true', searchOnly: true - } + }*/ }, fieldActions: { edit: { label: 'Edit', - ngClick: "editGroup(\{\{ group.id \}\})", + ngClick: "editGroup(\{\{ host.id \}\})", icon: 'icon-edit', "class": 'btn-xs btn-primary', awToolTip: 'Edit host' }, "delete": { label: 'Delete', - ngClick: "deleteGroup(\{\{ group.id \}\},'\{\{ group.name \}\}')", + ngClick: "deleteHoust(\{\{ host.id \}\},'\{\{ host.name \}\}')", icon: 'icon-trash', "class": 'btn-xs btn-primary', awToolTip: 'Delete host' diff --git a/awx/ui/static/less/ansible-ui.less b/awx/ui/static/less/ansible-ui.less index 20909be475..65b99c6d4f 100644 --- a/awx/ui/static/less/ansible-ui.less +++ b/awx/ui/static/less/ansible-ui.less @@ -849,16 +849,21 @@ input[type="checkbox"].checkbox-no-label { */ .icon-cloud-na, - .icon-cloud-never { + .icon-cloud-never, + a.icon-cloud-na:hover, + a.icon-cloud-never:hover { color: @grey; } .icon-cloud-updating, - .icon-cloud-successful { + .icon-cloud-successful, + a.icon-cloud-updating:hover, + a.icon-cloud-successful:hover { color: @green; } - .icon-cloud-failed { + .icon-cloud-failed, + a.icon-cloud-failed:hover { color: @red; } diff --git a/awx/ui/static/lib/ansible/InventoryTree.js b/awx/ui/static/lib/ansible/InventoryTree.js index 695c3d7d61..78dd068bba 100644 --- a/awx/ui/static/lib/ansible/InventoryTree.js +++ b/awx/ui/static/lib/ansible/InventoryTree.js @@ -177,10 +177,17 @@ angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper']) var inventory_id = params.inventory_id; var scope = params.scope; + var refresh = params.refresh; + //var selected_id = params. var groups = []; - var id = 0; + var id = 1; + + var all_hosts = { + name: 'All Hosts', id: 1, group_id: null, parent: 0, description: '', show: true, ngicon: null, + has_children: false, related: {}, selected_class: '' }; + groups.push(all_hosts); function buildGroups(tree_data, parent, level) { var sorted = SortNodes(tree_data); @@ -243,7 +250,13 @@ angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper']) .success( function(data, status, headers, config) { buildGroups(data, 0, 0); //console.log(groups); - scope.$emit('searchTreeReady', inventory_name, groups); + if (refresh) { + scope.groups = groups; + scope.$emit('groupTreeRefreshed'); + } + else { + scope.$emit('groupTreeLoaded', inventory_name, groups); + } }) .error( function(data, status, headers, config) { Wait('stop'); diff --git a/awx/ui/static/lib/ansible/prompt-dialog.js b/awx/ui/static/lib/ansible/prompt-dialog.js index f8cbec912e..85c69bf48e 100644 --- a/awx/ui/static/lib/ansible/prompt-dialog.js +++ b/awx/ui/static/lib/ansible/prompt-dialog.js @@ -17,18 +17,18 @@ angular.module('PromptDialog', ['Utilities']) .factory('Prompt', ['Alert', function(Alert) { return function(params) { - // - + var dialog = angular.element(document.getElementById('prompt-modal')); var scope = dialog.scope(); + scope.promptHeader = params.hdr; scope.promptBody = params.body; - var cls = (params['class'] == null || params['class'] == undefined) ? 'btn-danger' : params['class']; - $('#prompt_action_btn').addClass(cls); //Use jquery because django template engine conflicts with Angular's - // use of {{...}} - //scope.id = params.id; - //scope.url = params.url; scope.promptAction = params.action; + var cls = (params['class'] == null || params['class'] == undefined) ? 'btn-danger' : params['class']; + + $('#prompt_action_btn').addClass(cls); // Use jquery because django template engine conflicts with Angular's + // use of {{...}} + $(dialog).modal({ backdrop: 'static', keyboard: true, @@ -36,21 +36,4 @@ angular.module('PromptDialog', ['Utilities']) }); } }]); - - - - - -// \ No newline at end of file + \ No newline at end of file