diff --git a/awx/ui/client/src/inventories/copy-move/copy-move-groups.controller.js b/awx/ui/client/src/inventories/copy-move/copy-move-groups.controller.js new file mode 100644 index 0000000000..2ebf91cc93 --- /dev/null +++ b/awx/ui/client/src/inventories/copy-move/copy-move-groups.controller.js @@ -0,0 +1,72 @@ +/************************************************* + * Copyright (c) 2016 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + + export default + ['$scope', '$state', '$stateParams', 'GroupManageService', 'CopyMoveGroupList', 'group', 'Dataset', + function($scope, $state, $stateParams, GroupManageService, CopyMoveGroupList, group, Dataset){ + var list = CopyMoveGroupList; + + $scope.item = group; + $scope.submitMode = $stateParams.groups === undefined ? 'move' : 'copy'; + $scope.toggle_row = function(id){ + // toggle off anything else currently selected + _.forEach($scope.groups, (item) => {return item.id === id ? item.checked = 1 : item.checked = null;}); + // yoink the currently selected thing + $scope.selected = _.find($scope.groups, (item) => {return item.id === id;}); + }; + $scope.formCancel = function(){ + $state.go('^'); + }; + $scope.formSave = function(){ + switch($scope.submitMode) { + case 'copy': + GroupManageService.associateGroup(group, $scope.selected.id).then(() => $state.go('^', null, {reload: true})); + break; + case 'move': + switch($scope.targetRootGroup){ + case true: + // disassociating group will bubble it to the root group level + GroupManageService.disassociateGroup(group.id, _.last($stateParams.group)).then(() => $state.go('^', null, {reload: true})); + break; + default: + // at the root group level, no dissassociation is needed + if (!$stateParams.group){ + GroupManageService.associateGroup(group, $scope.selected.id).then(() => $state.go('^', null, {reload: true})); + } + else{ + // unsure if orphaned resources get garbage collected, safe bet is to associate before disassociate + GroupManageService.associateGroup(group, $scope.selected.id).then(() => { + GroupManageService.disassociateGroup(group.id, _.last($stateParams.group)) + .then(() => $state.go('^', null, {reload: true})); + }); + } + break; + } + } + }; + $scope.toggleTargetRootGroup = function(){ + $scope.selected = !$scope.selected; + // cannot perform copy operations to root group level + $scope.submitMode = 'move'; + // toggle off anything currently selected in the list, for clarity + _.forEach($scope.groups, (item) => {item.checked = null;}); + // disable list selections + $('#copyMove-list :input').each((idx, el) => { + $(el).prop('disabled', (idx, value) => !value); + }); + }; + + function init(){ + $scope.atRootLevel = $stateParams.group ? false : true; + + // search init + $scope.list = list; + $scope[`${list.iterator}_dataset`] = Dataset.data; + $scope[list.name] = $scope[`${list.iterator}_dataset`].results; + } + + init(); + }]; diff --git a/awx/ui/client/src/inventories/copy-move/copy-move-groups.list.js b/awx/ui/client/src/inventories/copy-move/copy-move-groups.list.js new file mode 100644 index 0000000000..a207fd8e06 --- /dev/null +++ b/awx/ui/client/src/inventories/copy-move/copy-move-groups.list.js @@ -0,0 +1,24 @@ +/************************************************* + * Copyright (c) 2015 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + + + + +export default { + name: 'groups', + iterator: 'copy', + selectTitle: 'Copy Groups', + index: false, + well: false, + emptyListText: 'PLEASE CREATE ADDITIONAL GROUPS / HOSTS TO PERFORM THIS ACTION', + fields: { + name: { + key: true, + label: 'Target Group Name' + } + }, + basePath: 'api/v2/inventories/{{$stateParams.inventory_id}}/groups' +}; diff --git a/awx/ui/client/src/inventories/copy-move/copy-move-hosts.controller.js b/awx/ui/client/src/inventories/copy-move/copy-move-hosts.controller.js new file mode 100644 index 0000000000..8e347dc2c8 --- /dev/null +++ b/awx/ui/client/src/inventories/copy-move/copy-move-hosts.controller.js @@ -0,0 +1,50 @@ +/************************************************* + * Copyright (c) 2016 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + + export default + ['$scope', '$state', '$stateParams', 'HostManageService', 'CopyMoveGroupList', 'host', 'Dataset', + function($scope, $state, $stateParams, HostManageService, CopyMoveGroupList, host, Dataset){ + var list = CopyMoveGroupList; + + $scope.item = host; + $scope.submitMode = 'copy'; + $scope.toggle_row = function(id){ + // toggle off anything else currently selected + _.forEach($scope.groups, (item) => {return item.id === id ? item.checked = 1 : item.checked = null;}); + // yoink the currently selected thing + $scope.selected = _.find($scope.groups, (item) => {return item.id === id;}); + }; + $scope.formCancel = function(){ + $state.go('^'); + }; + $scope.formSave = function(){ + switch($scope.submitMode) { + case 'copy': + HostManageService.associateGroup(host, $scope.selected.id).then(() => $state.go('^')); + break; + case 'move': + // at the root group level, no dissassociation is needed + if (!$stateParams.group){ + HostManageService.associateGroup(host, $scope.selected.id).then(() => $state.go('^', null, {reload: true})); + } + else{ + HostManageService.associateGroup(host, $scope.selected.id).then(() => { + HostManageService.disassociateGroup(host, _.last($stateParams.group)) + .then(() => $state.go('^', null, {reload: true})); + }); + } + break; + } + }; + var init = function(){ + // search init + $scope.list = list; + $scope[`${list.iterator}_dataset`] = Dataset.data; + $scope[list.name] = $scope[`${list.iterator}_dataset`].results; + + }; + init(); + }]; diff --git a/awx/ui/client/src/inventories/copy-move/copy-move.block.less b/awx/ui/client/src/inventories/copy-move/copy-move.block.less new file mode 100644 index 0000000000..608f59654a --- /dev/null +++ b/awx/ui/client/src/inventories/copy-move/copy-move.block.less @@ -0,0 +1,33 @@ +@import "./client/src/shared/branding/colors.default.less"; + +#Inventory-copyMovePanel { + .List-searchRow { + width: 50%; + } + .Form-header { + width: 50%; + margin-top: -20px; + } + + .Form-saveButton { + &:disabled { + border-color: @default-icon-hov; + } + } +} +.copyMove-choices { + float: right; + width: 25%; + text-align: right; +} +.copyMove-buttons{ + height: 30px; + margin-top: 20px; + + button { + margin-left: 20px; + } +} +.copyMove-root{ + margin-top: 10px; +} diff --git a/awx/ui/client/src/inventories/copy-move/copy-move.partial.html b/awx/ui/client/src/inventories/copy-move/copy-move.partial.html new file mode 100644 index 0000000000..030f0c7e3a --- /dev/null +++ b/awx/ui/client/src/inventories/copy-move/copy-move.partial.html @@ -0,0 +1,21 @@ +
+
+
{{item.name}}
+
+
+ + +
+
+
+ Use the inventory root +
+
+ + +
+
diff --git a/awx/ui/client/src/inventories/copy-move/copy-move.route.js b/awx/ui/client/src/inventories/copy-move/copy-move.route.js new file mode 100644 index 0000000000..e68c6de633 --- /dev/null +++ b/awx/ui/client/src/inventories/copy-move/copy-move.route.js @@ -0,0 +1,98 @@ +/************************************************* + * Copyright (c) 2016 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ +import {templateUrl} from '../../shared/template-url/template-url.factory'; +import { N_ } from '../../i18n'; + +import CopyMoveGroupsController from './copy-move-groups.controller'; +import CopyMoveHostsController from './copy-move-hosts.controller'; + +var copyMoveGroupRoute = { + name: 'inventories.edit.groups.copyMoveGroup', + url: '/copy-move-group/{group_id:int}', + searchPrefix: 'copy', + data: { + group_id: 'group_id', + }, + params: { + copy_search: { + value: { + not__id__in: null + }, + dynamic: true, + squash: '' + } + }, + ncyBreadcrumb: { + label: N_("COPY OR MOVE") + " {{item.name}}" + }, + resolve: { + Dataset: ['CopyMoveGroupList', 'QuerySet', '$stateParams', 'GetBasePath', 'group', + function(list, qs, $stateParams, GetBasePath, group) { + $stateParams.copy_search.not__id__in = ($stateParams.group && $stateParams.group.length > 0 ? group.id + ',' + _.last($stateParams.group) : group.id.toString()); + let path = GetBasePath('inventory') + $stateParams.inventory_id + '/groups/'; + return qs.search(path, $stateParams.copy_search); + } + ], + group: ['GroupManageService', '$stateParams', function(GroupManageService, $stateParams){ + return GroupManageService.get({id: $stateParams.group_id}).then(res => res.data.results[0]); + }] + }, + views: { + 'copyMove@inventories' : { + controller: CopyMoveGroupsController, + templateUrl: templateUrl('inventories/copy-move/copy-move'), + }, + 'copyMoveList@inventories.edit.groups.copyMoveGroup': { + templateProvider: function(CopyMoveGroupList, generateList) { + let html = generateList.build({ + list: CopyMoveGroupList, + mode: 'lookup', + input_type: 'radio' + }); + return html; + } + } + } +}; +var copyMoveHostRoute = { + name: 'inventories.edit.hosts.copyMoveHost', + url: '/copy-move-host/{host_id}', + searchPrefix: 'copy', + ncyBreadcrumb: { + label: N_("COPY OR MOVE") + " {{item.name}}" + }, + resolve: { + Dataset: ['CopyMoveGroupList', 'QuerySet', '$stateParams', 'GetBasePath', + function(list, qs, $stateParams, GetBasePath) { + let path = GetBasePath('inventory') + $stateParams.inventory_id + '/groups/'; + return qs.search(path, $stateParams.copy_search); + } + ], + host: ['HostManageService', '$stateParams', function(HostManageService, $stateParams){ + return HostManageService.get({id: $stateParams.host_id}).then(res => res.data.results[0]); + }] + }, + views: { + 'copyMove@inventories': { + templateUrl: templateUrl('inventories/copy-move/copy-move'), + controller: CopyMoveHostsController, + }, + 'copyMoveList@inventories.edit.hosts.copyMoveHost': { + templateProvider: function(CopyMoveGroupList, generateList, $stateParams, GetBasePath) { + let list = CopyMoveGroupList; + list.basePath = GetBasePath('inventory') + $stateParams.inventory_id + '/groups/'; + let html = generateList.build({ + list: CopyMoveGroupList, + mode: 'lookup', + input_type: 'radio' + }); + return html; + } + } + } +}; + +export {copyMoveGroupRoute, copyMoveHostRoute}; diff --git a/awx/ui/client/src/inventories/copy-move/main.js b/awx/ui/client/src/inventories/copy-move/main.js new file mode 100644 index 0000000000..a9fbd47660 --- /dev/null +++ b/awx/ui/client/src/inventories/copy-move/main.js @@ -0,0 +1,15 @@ +/************************************************* + * Copyright (c) 2015 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + +import CopyMoveGroupsController from './copy-move-groups.controller'; +import CopyMoveHostsController from './copy-move-hosts.controller'; +import CopyMoveGroupList from './copy-move-groups.list'; + +export default +angular.module('manageCopyMove', []) + .controller('CopyMoveGroupsController', CopyMoveGroupsController) + .controller('CopyMoveHostsController', CopyMoveHostsController) + .value('CopyMoveGroupList', CopyMoveGroupList); diff --git a/awx/ui/client/src/inventories/groups/list/groups-list.controller.js b/awx/ui/client/src/inventories/groups/list/groups-list.controller.js index ba36eb6d7a..9e66fa5c9d 100644 --- a/awx/ui/client/src/inventories/groups/list/groups-list.controller.js +++ b/awx/ui/client/src/inventories/groups/list/groups-list.controller.js @@ -194,7 +194,7 @@ }; $scope.copyMoveGroup = function(id){ - // TODO: implement + $state.go('inventories.edit.groups.copyMoveGroup', {group_id: id, groups: $stateParams.groups}); }; var cleanUpStateChangeListener = $rootScope.$on('$stateChangeSuccess', function(event, toState, toParams) { diff --git a/awx/ui/client/src/inventories/inventories.partial.html b/awx/ui/client/src/inventories/inventories.partial.html index 767cd197fa..d8fc0b2c60 100644 --- a/awx/ui/client/src/inventories/inventories.partial.html +++ b/awx/ui/client/src/inventories/inventories.partial.html @@ -1,4 +1,5 @@
+
diff --git a/awx/ui/client/src/inventories/main.js b/awx/ui/client/src/inventories/main.js index d753bc8cd5..5df33882a7 100644 --- a/awx/ui/client/src/inventories/main.js +++ b/awx/ui/client/src/inventories/main.js @@ -20,6 +20,8 @@ import InventoryForm from './inventory.form'; import InventoryManageService from './inventory-manage.service'; import adHocRoute from './adhoc/adhoc.route'; import ansibleFacts from './ansible_facts/main'; +import { copyMoveGroupRoute, copyMoveHostRoute } from './copy-move/copy-move.route'; +import copyMove from './copy-move/main'; export default angular.module('inventory', [ adhoc.name, @@ -31,7 +33,8 @@ angular.module('inventory', [ inventoryAdd.name, inventoryEdit.name, inventoryList.name, - ansibleFacts.name + ansibleFacts.name, + copyMove.name ]) .factory('InventoryForm', InventoryForm) .factory('InventoryList', InventoryList) @@ -41,6 +44,34 @@ angular.module('inventory', [ let stateDefinitions = stateDefinitionsProvider.$get(), stateExtender = $stateExtenderProvider.$get(); + function factsConfig(stateName) { + return { + name: stateName, + url: '/ansible_facts', + ncyBreadcrumb: { + label: N_("FACTS") + }, + views: { + 'related': { + controller: 'AnsibleFactsController', + templateUrl: templateUrl('inventories/ansible_facts/ansible_facts') + } + }, + resolve: { + Facts: ['$stateParams', 'GetBasePath', 'Rest', + function($stateParams, GetBasePath, Rest) { + let ansibleFactsUrl = GetBasePath('hosts') + $stateParams.host_id + '/ansible_facts'; + Rest.setUrl(ansibleFactsUrl); + return Rest.get() + .success(function(data) { + return data; + }); + } + ] + } + }; + } + function generateInventoryStates() { let basicInventoryAdd = stateDefinitions.generateTree({ @@ -224,31 +255,7 @@ angular.module('inventory', [ } }; - let relatedHostsAnsibleFacts = { - name: 'inventories.edit.hosts.edit.ansible_facts', - url: '/ansible_facts', - ncyBreadcrumb: { - label: N_("FACTS") - }, - views: { - 'related': { - controller: 'AnsibleFactsController', - templateUrl: templateUrl('inventories/ansible_facts/ansible_facts') - } - }, - resolve: { - Facts: ['$stateParams', 'GetBasePath', 'Rest', - function($stateParams, GetBasePath, Rest) { - let ansibleFactsUrl = GetBasePath('hosts') + $stateParams.host_id + '/ansible_facts'; - Rest.setUrl(ansibleFactsUrl); - return Rest.get() - .success(function(data) { - return data; - }); - } - ] - } - }; + let relatedHostsAnsibleFacts = factsConfig('inventories.edit.hosts.edit.ansible_facts'); return Promise.all([ basicInventoryAdd, @@ -296,7 +303,9 @@ angular.module('inventory', [ stateExtender.buildDefinition(listSchedules), stateExtender.buildDefinition(addSchedule), stateExtender.buildDefinition(editSchedule), - stateExtender.buildDefinition(relatedHostsAnsibleFacts) + stateExtender.buildDefinition(relatedHostsAnsibleFacts), + stateExtender.buildDefinition(copyMoveGroupRoute), + stateExtender.buildDefinition(copyMoveHostRoute) ]) }; }); @@ -347,31 +356,7 @@ angular.module('inventory', [ } }); - let hostAnsibleFacts = { - name: 'hosts.edit.ansible_facts', - url: '/ansible_facts', - ncyBreadcrumb: { - label: N_("FACTS") - }, - views: { - 'related': { - controller: 'AnsibleFactsController', - templateUrl: templateUrl('inventories/ansible_facts/ansible_facts') - } - }, - resolve: { - Facts: ['$stateParams', 'GetBasePath', 'Rest', - function($stateParams, GetBasePath, Rest) { - let ansibleFactsUrl = GetBasePath('hosts') + $stateParams.host_id + '/ansible_facts'; - Rest.setUrl(ansibleFactsUrl); - return Rest.get() - .success(function(data) { - return data; - }); - } - ] - } - }; + let hostAnsibleFacts = factsConfig('hosts.edit.ansible_facts'); return Promise.all([ hostTree diff --git a/awx/ui/client/src/inventories/related-hosts/list/host-list.controller.js b/awx/ui/client/src/inventories/related-hosts/list/host-list.controller.js index e9eaea0973..f2f960ceea 100644 --- a/awx/ui/client/src/inventories/related-hosts/list/host-list.controller.js +++ b/awx/ui/client/src/inventories/related-hosts/list/host-list.controller.js @@ -156,4 +156,8 @@ export default ['$scope', 'RelatedHostsListDefinition', '$rootScope', 'GetBasePa $state.go('^.adhoc', {pattern: pattern}); }; + + $scope.copyMoveHost = function(id) { + $state.go('inventories.edit.hosts.copyMoveHost', {host_id: id}); + }; }];