diff --git a/awx/ui/client/src/adhoc/adhoc.route.js b/awx/ui/client/src/adhoc/adhoc.route.js deleted file mode 100644 index c10d565c72..0000000000 --- a/awx/ui/client/src/adhoc/adhoc.route.js +++ /dev/null @@ -1,14 +0,0 @@ -/************************************************* - * Copyright (c) 2015 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ - - import {templateUrl} from '../shared/template-url/template-url.factory'; - -export default { - route: '/adhoc', - name: 'inventoryManage.adhoc', - templateUrl: templateUrl('adhoc/adhoc'), - controller: 'adhocController' -}; diff --git a/awx/ui/client/src/adhoc/main.js b/awx/ui/client/src/adhoc/main.js deleted file mode 100644 index e854ce9f97..0000000000 --- a/awx/ui/client/src/adhoc/main.js +++ /dev/null @@ -1,10 +0,0 @@ -import route from './adhoc.route'; -import adhocController from './adhoc.controller'; -import form from './adhoc.form'; - -export default angular.module('adhoc', []) - .controller('adhocController', adhocController) - .run(['$stateExtender', function($stateExtender) { - $stateExtender.addState(route); - }]) - .factory('adhocForm', form); diff --git a/awx/ui/client/src/app.js b/awx/ui/client/src/app.js index 6d5673a77b..1fbd283e79 100644 --- a/awx/ui/client/src/app.js +++ b/awx/ui/client/src/app.js @@ -47,7 +47,6 @@ import browserData from './browser-data/main'; import dashboard from './dashboard/main'; import moment from './shared/moment/main'; import templateUrl from './shared/template-url/main'; -import adhoc from './adhoc/main'; import login from './login/main'; import activityStream from './activity-stream/main'; import standardOut from './standard-out/main'; @@ -67,7 +66,6 @@ import './shared/Modal'; import './shared/prompt-dialog'; import './shared/directives'; import './shared/filters'; -import './shared/InventoryTree'; import './shared/Socket'; import './shared/features/main'; import './login/authenticationServices/pendo/ng-pendo'; @@ -99,7 +97,6 @@ var tower = angular.module('Tower', [ dashboard.name, moment.name, templateUrl.name, - adhoc.name, login.name, activityStream.name, footer.name, @@ -169,7 +166,6 @@ var tower = angular.module('Tower', [ 'StreamWidget', 'JobsHelper', 'InventoryGroupsHelpDefinition', - 'InventoryTree', 'CredentialsHelper', 'StreamListDefinition', 'HomeGroupListDefinition', @@ -224,6 +220,7 @@ var tower = angular.module('Tower', [ // route to the details pane of /job/:id/host-event/:eventId if no other child specified $urlRouterProvider.when('/jobs/*/host-event/*', '/jobs/*/host-event/*/details'); + // $urlRouterProvider.otherwise("/home"); $urlRouterProvider.otherwise(function($injector){ var $state = $injector.get("$state"); diff --git a/awx/ui/client/src/forms/Groups.js b/awx/ui/client/src/forms/Groups.js index 35b7f65a2e..e21d0cfff4 100644 --- a/awx/ui/client/src/forms/Groups.js +++ b/awx/ui/client/src/forms/Groups.js @@ -61,18 +61,10 @@ export default label: 'Source', type: 'select', ngOptions: 'source.label for source in source_type_options track by source.value', - ngChange: 'sourceChange()', + ngChange: 'sourceChange(source)', addRequired: false, - editRequired: false - }, - source_path: { - label: 'Script Path', - ngShow: "source && source.value == 'file'", - type: 'text', - awRequiredWhen: { - reqExpression: "sourcePathRequired", - init: "false" - } + editRequired: false, + ngModel: 'source' }, credential: { label: 'Cloud Credential', @@ -147,7 +139,6 @@ export default }, inventory_script: { label : "Custom Inventory Script", - labelClass: 'prepend-asterisk', type: 'lookup', ngShow: "source && source.value === 'custom'", sourceModel: 'inventory_script', @@ -157,7 +148,8 @@ export default editRequired: true, ngRequired: "source && source.value === 'custom'", }, - extra_vars: { + custom_variables: { + id: 'custom_variables', label: 'Environment Variables', //"{{vars_label}}" , ngShow: "source && source.value=='custom' ", type: 'textarea', @@ -176,9 +168,10 @@ export default "
---
somevar: somevalue
password: magic
\n", dataContainer: 'body' }, - source_vars: { + ec2_variables: { + id: 'ec2_variables', label: 'Source Variables', //"{{vars_label}}" , - ngShow: "source && (source.value == 'file' || source.value == 'ec2')", + ngShow: "source && source.value == 'ec2'", type: 'textarea', class: 'Form-textAreaLabel Form-formGroup--fullWidth', addRequired: false, @@ -200,11 +193,11 @@ export default '

View YAML examples at docs.ansible.com

', dataContainer: 'body' }, - inventory_variables: { + vmware_variables: { + id: 'vmware_variables', label: 'Source Variables', //"{{vars_label}}" , - ngShow: "source && (source.value == 'vmware' || " + - "source.value == 'openstack')", + ngShow: "source && source.value == 'vmware'", type: 'textarea', addRequired: false, class: 'Form-textAreaLabel Form-formGroup--fullWidth', @@ -226,6 +219,32 @@ export default '

View YAML examples at docs.ansible.com

', dataContainer: 'body' }, + openstack_variables: { + id: 'openstack_variables', + label: 'Source Variables', //"{{vars_label}}" , + + ngShow: "source && source.value == 'openstack'", + type: 'textarea', + addRequired: false, + class: 'Form-textAreaLabel Form-formGroup--fullWidth', + editRequird: false, + rows: 6, + 'default': '---', + parseTypeName: 'envParseType', + dataTitle: "Source Variables", + dataPlacement: 'right', + awPopOver: "

Override variables found in openstack.yml and used by the inventory update script. For an example variable configuration " + + "" + + "view openstack.yml in the Ansible github repo.

" + + "

Enter variables using either JSON or YAML syntax. Use the radio button to toggle between the two.

" + + "JSON:
\n" + + "
{
 \"somevar\": \"somevalue\",
 \"password\": \"magic\"
}
\n" + + "YAML:
\n" + + "
---
somevar: somevalue
password: magic
\n" + + '

View JSON examples at www.json.org

' + + '

View YAML examples at docs.ansible.com

', + dataContainer: 'body' + }, checkbox_group: { label: 'Update Options', type: 'checkbox_group', @@ -295,12 +314,12 @@ export default }, buttons: { + save: { + ngClick: 'formSave()' + }, cancel: { ngClick: 'formCancel()' - }, - save: { - ngClick: 'saveGroup()' - } + } }, related: { diff --git a/awx/ui/client/src/forms/Hosts.js b/awx/ui/client/src/forms/Hosts.js index 637cba0981..a80882c13c 100644 --- a/awx/ui/client/src/forms/Hosts.js +++ b/awx/ui/client/src/forms/Hosts.js @@ -60,7 +60,7 @@ export default addRequired: false, editRequird: false, rows: 6, - "class": "modal-input-xlarge Form-textArea", + "class": "modal-input-xlarge Form-textArea Form-formGroup--fullWidth", "default": "---", awPopOver: "

Enter variables using either JSON or YAML syntax. Use the radio button to toggle between the two.

" + "JSON:
\n" + diff --git a/awx/ui/client/src/helpers/Groups.js b/awx/ui/client/src/helpers/Groups.js index 6eeaf797c2..b34a9a081c 100644 --- a/awx/ui/client/src/helpers/Groups.js +++ b/awx/ui/client/src/helpers/Groups.js @@ -17,8 +17,8 @@ import listGenerator from '../shared/list-generator/main'; export default angular.module('GroupsHelper', [ 'RestServices', 'Utilities', listGenerator.name, 'GroupListDefinition', 'SearchHelper', 'PaginationHelpers', listGenerator.name, 'GroupsHelper', 'InventoryHelper', 'SelectionHelper', - 'JobSubmissionHelper', 'RefreshHelper', 'PromptDialog', 'CredentialsListDefinition', 'InventoryTree', - 'InventoryStatusDefinition', 'VariablesHelper', 'SchedulesListDefinition', 'StandardOutHelper', + 'JobSubmissionHelper', 'RefreshHelper', 'PromptDialog', 'CredentialsListDefinition', + 'InventoryStatusDefinition', 'VariablesHelper', 'SchedulesListDefinition', 'StandardOutHelper', 'SchedulesHelper' ]) @@ -231,124 +231,6 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', listGenerator.name } ]) - -/** - * - * TODO: Document - * - */ -.factory('SourceChange', ['GetBasePath', 'CredentialList', 'LookUpInit', - 'Empty', 'Wait', 'ParseTypeChange', 'inventoryScriptsListObject', - 'CreateSelect2', - function (GetBasePath, CredentialList, LookUpInit, Empty, Wait, - ParseTypeChange, inventoryScriptsListObject, CreateSelect2) { - return function (params) { - - var scope = params.scope, - form = params.form, - kind, url, callback, invUrl; - - scope.cloudCredentialRequired = false; - - if (!Empty(scope.source)) { - 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; - $('#source_form').addClass('squeeze'); - CreateSelect2({ - element: '#source_source_regions' - }); - } else if (scope.source.value === 'ec2') { - scope.source_region_choices = scope.ec2_regions; - scope.group_by_choices = scope.ec2_group_by; - $('#source_form').addClass('squeeze'); - CreateSelect2({ - element: '#source_source_regions' - }); - CreateSelect2({ - element: '#source_group_by' - }); - } else if (scope.source.value === 'gce') { - scope.source_region_choices = scope.gce_regions; - $('#source_form').addClass('squeeze'); - CreateSelect2({ - element: '#source_source_regions' - }); - } else if (scope.source.value === 'azure' || scope.source.value === 'azure_rm') { - scope.source_region_choices = scope.azure_regions; - $('#source_form').addClass('squeeze'); - CreateSelect2({ - element: '#source_source_regions' - }); - } - if(scope.source.value==="custom"){ - // need to filter the possible custom scripts by the organization defined for the current inventory - invUrl = GetBasePath('inventory_scripts'); - LookUpInit({ - url: invUrl, - scope: scope, - form: form, - hdr: "Select Custom Inventory", - list: inventoryScriptsListObject, - field: 'inventory_script', - input_type: 'radio' - }); - scope.extra_vars = (Empty(scope.source_vars)) ? "---" : scope.source_vars; - ParseTypeChange({ scope: scope, variable: 'extra_vars', parse_variable: form.fields.extra_vars.parseTypeName, - field_id: 'source_extra_vars', onReady: callback }); - } - if(scope.source.value==="vmware" || - scope.source.value==="openstack"){ - scope.inventory_variables = (Empty(scope.source_vars)) ? "---" : scope.source_vars; - ParseTypeChange({ scope: scope, variable: 'inventory_variables', parse_variable: form.fields.inventory_variables.parseTypeName, - field_id: 'source_inventory_variables', onReady: callback }); - } - if (scope.source.value === 'rax' || - scope.source.value === 'ec2' || - scope.source.value ==='gce' || - scope.source.value === 'foreman' || - scope.source.value ==='cloudforms' || - scope.source.value === 'azure' || - scope.source.value === 'azure_rm' || - scope.source.value === 'vmware' || - scope.source.value === 'openstack') { - if (scope.source.value === 'ec2') { - kind = 'aws'; - } else { - kind = scope.source.value; - scope.cloudCredentialRequired = true; - } - url = GetBasePath('credentials') + '?cloud=true&kind=' + kind; - LookUpInit({ - url: url, - scope: scope, - form: form, - list: CredentialList, - field: 'credential', - input_type: "radio" - }); - if ($('#group_tabs .active a').text() === 'Source' && - (scope.source.value === 'ec2' )) { - callback = function(){ Wait('stop'); }; - Wait('start'); - scope.source_vars = (Empty(scope.source_vars)) ? "---" : scope.source_vars; - ParseTypeChange({ scope: scope, variable: 'source_vars', - parse_variable: form.fields.source_vars.parseTypeName, - field_id: 'source_source_vars', onReady: callback }); - } - } - } - }; - } -]) - /** * * Cancel a pending or running inventory sync @@ -433,284 +315,10 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', listGenerator.name } ]) -/** - * - * Add the list of schedules to the Group Edit modal - * - */ -.factory('GroupsScheduleListInit', ['GroupsScheduleEdit', 'SchedulesList', 'generateList', 'SearchInit', 'PaginateInit', 'Rest', - 'PageRangeSetup', 'Wait', 'ProcessErrors', 'Find', 'ToggleSchedule', 'DeleteSchedule', 'GetBasePath', 'SchedulesListInit', - function(GroupsScheduleEdit, SchedulesList, GenerateList, SearchInit, PaginateInit, Rest, PageRangeSetup, Wait, ProcessErrors, Find, - ToggleSchedule, DeleteSchedule, GetBasePath, SchedulesListInit) { - return function(params) { - var schedule_scope = params.scope, - url = params.url, - list; - - // Clean up - $('#schedules-list').hide().empty(); - $('#schedules-form-container').hide(); - $('#schedules-form').empty(); - $('.tooltip').each(function () { - $(this).remove(); - }); - $('.popover').each(function () { - $(this).remove(); - }); - - // Add schedules list - list = angular.copy(SchedulesList); - delete list.fields.dtend; - delete list.actions.stream; - list.well = false; - GenerateList.inject(list, { - mode: 'edit', - id: 'schedules-list', - searchSize: 'col-lg-6 col-md-5 col-sm-5 col-xs-5', - scope: schedule_scope - }); - - $('#schedules-list').show(); - - // Removing screws up /home/groups page - // if (schedule_scope.removePostRefresh) { - // schedule_scope.removePostRefresh(); - //} - schedule_scope.removePostRefresh = schedule_scope.$on('PostRefresh', function() { - SchedulesListInit({ - scope: schedule_scope, - list: list, - choices: null - }); - }); - SearchInit({ - scope: schedule_scope, - set: 'schedules', - list: SchedulesList, - url: url - }); - PaginateInit({ - scope: schedule_scope, - list: SchedulesList, - url: url, - pageSize: 5 - }); - schedule_scope.search(list.iterator); - - schedule_scope.refreshSchedules = function() { - schedule_scope.search(list.iterator); - }; - - schedule_scope.editSchedule = function(id) { - GroupsScheduleEdit({ scope: schedule_scope, mode: 'edit', url: GetBasePath('schedules') + id + '/' }); - }; - - schedule_scope.addSchedule = function() { - GroupsScheduleEdit({ scope: schedule_scope, mode: 'add', url: url }); - }; - - if (schedule_scope.removeSchedulesRefresh) { - schedule_scope.removeSchedulesRefresh(); - } - schedule_scope.removeSchedulesRefresh = schedule_scope.$on('SchedulesRefresh', function() { - schedule_scope.search(list.iterator); - }); - - schedule_scope.toggleSchedule = function(event, id) { - try { - $(event.target).tooltip('hide'); - } - catch(e) { - // ignore - } - ToggleSchedule({ - scope: schedule_scope, - id: id, - callback: 'SchedulesRefresh' - }); - }; - - schedule_scope.deleteSchedule = function(id) { - DeleteSchedule({ - scope: schedule_scope, - id: id, - callback: 'SchedulesRefresh' - }); - }; - }; - } -]) /** * - * TODO: Document - * - */ -.factory('SetSchedulesInnerDialogSize', [ - function() { - return function() { - var height = $('#group-modal-dialog').outerHeight() - $('#group_tabs').outerHeight() - 25; - height = height - 110 - $('#schedules-buttons').outerHeight(); - $('#schedules-form-container-body').height(height); - }; -} -]) - -/** - * - * Remove the schedule list, add the schedule widget and populate it with an rrule - * - */ -.factory('GroupsScheduleEdit', ['$compile','SchedulerInit', 'Rest', 'Wait', 'SetSchedulesInnerDialogSize', 'SchedulePost', 'ProcessErrors', - function($compile, SchedulerInit, Rest, Wait, SetSchedulesInnerDialogSize, SchedulePost, ProcessErrors) { - return function(params) { - var parent_scope = params.scope, - mode = params.mode, // 'add' or 'edit' - url = params.url, - scope = parent_scope.$new(), - schedule = {}, - scheduler, - target, - showForm, - list, - detail, - restoreList, - container, - elem; - - Wait('start'); - detail = $('#schedules-detail').hide(); - list = $('#schedules-list'); - target = $('#schedules-form'); - container = $('#schedules-form-container'); - // Clean up any lingering stuff - container.hide(); - target.empty(); - $('.tooltip').each(function () { - $(this).remove(); - }); - $('.popover').each(function () { - $(this).remove(); - }); - - elem = angular.element(document.getElementById('schedules-form-container')); - $compile(elem)(scope); - - if (scope.removeScheduleReady) { - scope.removeScheduleReady(); - } - scope.removeScheduleReady = scope.$on('ScheduleReady', function() { - // Insert the scheduler widget into the hidden div - scheduler = SchedulerInit({ scope: scope, requireFutureStartTime: false }); - scheduler.inject('schedules-form', false); - scheduler.injectDetail('schedules-detail', false); - scheduler.clear(); - scope.formShowing = true; - scope.showRRuleDetail = false; - scope.schedulesTitle = (mode === 'edit') ? 'Edit Schedule' : 'Create Schedule'; - - // display the scheduler widget - showForm = function() { - Wait('stop'); - $('#schedules-overlay').width($('#schedules-tab') - .width()).height($('#schedules-tab').height()).show(); - container.width($('#schedules-tab').width() - 18); - SetSchedulesInnerDialogSize(); - container.show('slide', { direction: 'left' }, 300); - $('#group-save-button').prop('disabled', true); - target.show(); - if (mode === 'edit') { - scope.$apply(function() { - scheduler.setRRule(schedule.rrule); - scheduler.setName(schedule.name); - }); - } - }; - setTimeout(function() { showForm(); }, 1000); - }); - - restoreList = function() { - $('#group-save-button').prop('disabled', false); - list.show('slide', { direction: 'right' }, 500); - $('#schedules-overlay').width($('#schedules-tab').width()).height($('#schedules-tab').height()).hide(); - parent_scope.refreshSchedules(); - }; - - scope.showScheduleDetail = function() { - if (scope.formShowing) { - if (scheduler.isValid()) { - detail.width($('#schedules-form').width()).height($('#schedules-form').height()); - target.hide(); - detail.show(); - scope.formShowing = false; - } - } - else { - detail.hide(); - target.show(); - scope.formShowing = true; - } - }; - - if (scope.removeScheduleSaved) { - scope.removeScheduleSaved(); - } - scope.removeScheduleSaved = scope.$on('ScheduleSaved', function() { - Wait('stop'); - container.hide('slide', { direction: 'right' }, 500, restoreList); - scope.$destroy(); - }); - - scope.saveScheduleForm = function() { - if (scheduler.isValid()) { - scope.schedulerIsValid = true; - SchedulePost({ - scope: scope, - url: url, - scheduler: scheduler, - callback: 'ScheduleSaved', - mode: mode, - schedule: schedule - }); - } - else { - scope.schedulerIsValid = false; - } - }; - - scope.cancelScheduleForm = function() { - container.hide('slide', { direction: 'right' }, 500, restoreList); - scope.$destroy(); - }; - - if (mode === 'edit') { - // Get the existing record - Rest.setUrl(url); - Rest.get() - .success(function(data) { - schedule = data; - if (!/DTSTART/.test(schedule.rrule)) { - schedule.rrule += ";DTSTART=" + schedule.dtstart.replace(/\.\d+Z$/,'Z'); - } - schedule.rrule = schedule.rrule.replace(/ RRULE:/,';'); - schedule.rrule = schedule.rrule.replace(/DTSTART:/,'DTSTART='); - scope.$emit('ScheduleReady'); - }) - .error(function(data,status){ - ProcessErrors(scope, data, status, null, { hdr: 'Error!', - msg: 'Failed to get: ' + url + ' GET returned: ' + status }); - }); - } - else { - scope.$emit('ScheduleReady'); - } - }; - } -]) - -/** - * - * TODO: Document + * Deprecated factory that used to support /#/home/groups/ * */ .factory('GroupsEdit', ['$filter', '$rootScope', '$location', '$log', '$stateParams', '$compile', 'Rest', 'Alert', 'GroupForm', 'GenerateForm', @@ -1427,710 +1035,6 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', listGenerator.name initSourceChange(); }; - }; - } -]) - -/** - * - * Set's up the process for deleting a group from an inventory page - * - */ -.factory('GroupsDelete', ['$rootScope', '$location', '$log', '$stateParams', 'Rest', 'Alert', 'GroupForm', 'GenerateForm', - 'Prompt', 'ProcessErrors', 'GetBasePath', 'Wait', 'BuildTree', 'Find', 'CreateDialog', - function ($rootScope, $location, $log, $stateParams, Rest, Alert, GroupForm, GenerateForm, Prompt, ProcessErrors, - GetBasePath, Wait, BuildTree, Find, CreateDialog) { - return function (params) { - - var scope = params.scope, - group_id = params.group_id, - node = Find({ list: scope.groups, key: 'id', val: group_id }), - hosts = [], - groups = [], - childCount = 0, - buttonSet; - - scope.deleteOption = "preserve-all"; - - scope.helpText = "
Delete
Deletes groups and hosts associated with the group being deleted. " + - "If a group or host is associated with other groups, it will still exist within those groups. Otherwise, " + - "the associated groups and hosts will no longer appear in the inventory.
\n" + - "
Promote
Groups and hosts associated with the group being removed will be " + - "promoted one level. Note: groups already associated with other groups cannot be promoted to the top level of the " + - "tree.
\n"; - buttonSet = [{ - label: "Cancel", - onClick: function() { - scope.cancel(); - }, - icon: "fa-times", - "class": "btn btn-default", - "id": "group-delete-cancel-button" - },{ - label: "Delete", - onClick: function() { - scope.performDelete(); - }, - icon: "fa-check", - "class": "btn btn-primary", - "id": "group-delete-ok-button" - }]; - - if (scope.removeDeleteDialogReady) { - scope.removeDeleteDialogReady(); - } - - scope.removeDeleteDialogReady = scope.$on('DeleteDialogReady', function() { - Wait('stop'); - $('#group-delete-dialog').dialog('open'); - }); - - if (scope.removeShowDeleteDialog) { - scope.removeShowDeleteDialog(); - } - - scope.removeShowDeleteDialog = scope.$on('ShowDeleteDialog', function() { - scope.group_name = node.name; - scope.groupsCount = groups.length; - scope.hostsCount = hosts.length; - CreateDialog({ - id: 'group-delete-dialog', - scope: scope, - buttons: buttonSet, - width: 650, - height: 350, - minWidth: 500, - title: 'Delete Group', - callback: 'DeleteDialogReady' - }); - }); - - if (scope.removeChildrenReady) { - scope.removeChildrenReady(); - } - - scope.removeChildrenReady = scope.$on('ChildrenReady', function() { - childCount++; - if (childCount === 2) { - scope.$emit('ShowDeleteDialog'); - } - }); - - Wait('start'); - - // this function is used to make sure that we get all of the groups/hosts, - // not just the first page - scope.next_iterator = function(next_url, type) { - Rest.setUrl(next_url); - Rest.get() - .success(function(data) { - if (data.count) { - if (type === "groups") { - data.results.forEach(function(group) { - groups.push(group); - }); - } - else if (type === "hosts") { - data.results.forEach(function(host) { - hosts.push(host); - }); - } - } - - if (data.next) { - if (type === "groups") { - scope.next_iterator(data.next, "groups"); - } else if (type === "hosts") { - scope.next_iterator(data.next, "hosts"); - } - } - else { - scope.$emit('ChildrenReady'); - } - }) - .error(function(data, status) { - ProcessErrors(scope, data, status, null, { hdr: 'Error!', - msg: 'Failed to retrieve related groups. GET returned: ' + status - }); - }); - }; - - if (node.related.children) { - Rest.setUrl(node.related.children); - Rest.get() - .success(function(data) { - if (data.count) { - data.results.forEach(function(group) { - groups.push(group); - }); - } - - if (data.next) { - scope.next_iterator(data.next, "groups"); - } else { - scope.$emit('ChildrenReady'); - } - }) - .error(function(data, status) { - ProcessErrors(scope, data, status, null, { hdr: 'Error!', - msg: 'Failed to retrieve related groups. GET returned: ' + status - }); - }); - } - else { - scope.$emit('ChildrenReady'); - } - - if (node.related.all_hosts) { - Rest.setUrl(node.related.all_hosts); - Rest.get() - .success( function(data) { - if (data.count) { - data.results.forEach(function(host) { - hosts.push(host); - }); - } - if (data.next) { - scope.next_iterator(data.next, "hosts"); - } else { - scope.$emit('ChildrenReady'); - } - }) - .error( function(data, status) { - ProcessErrors(scope, data, status, null, { hdr: 'Error!', - msg: 'Failed to retrieve related hosts. GET returned: ' + status - }); - }); - } - else { - scope.$emit('ChildrenReady'); - } - - if (scope.removeDisassociateGroup) { - scope.removeDisassociateGroup(); - } - - scope.removeDisassociateGroup = scope.$on('DisassociateGroup', function() { - var data, url; - if (!scope.selected_group_id) { - url = GetBasePath('inventory') + scope.inventory.id + '/groups/'; - data = { id: node.id, disassociate: 1 }; - } - else { - url = GetBasePath('groups') + node.id + '/children/'; - data = { disassociate: 1 }; - } - - Rest.setUrl(url); - Rest.post(data) - .success(function () { - scope.$emit('GroupDeleteCompleted'); // Signal a group refresh to start - }) - .error(function (data, status) { - ProcessErrors(scope, data, status, null, { hdr: 'Error!', - msg: 'Call to ' + url + ' failed. POST returned: ' + status - }); - }); - }); - - if (scope.removeDeleteGroup) { - scope.removeDeleteGroup(); - } - - scope.removeDeleteGroup = scope.$on('DeleteGroup', function() { - var url = GetBasePath('groups') + node.id + '/'; - Rest.setUrl(url); - Rest.destroy() - .success( function() { - scope.$emit('GroupDeleteCompleted'); // Signal a group refresh to start - }) - .error( function(data, status) { - ProcessErrors(scope, data, status, null, { hdr: 'Error!', - msg: 'Call to ' + url + ' failed. DELETE returned: ' + status - }); - }); - }); - - scope.cancel = function() { - $('#group-delete-dialog').dialog('close'); - }; - - scope.performDelete = function() { - $('#group-delete-dialog').dialog('close'); - Wait('start'); - if (scope.deleteOption === 'delete-all' || (scope.groupsCount === 0 && scope.hostsCount === 0)) { - // If user chooses Delete or there are no children, send DELETE request - scope.$emit('DeleteGroup'); - } - else { - scope.$emit('DisassociateGroup'); - } - }; - }; - } -]) - -/** - * - * TODO: Document - * - */ -.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 }); - }); - }; - } -]) - -/** - * - * TODO: Document - * - */ -.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) { - scope[list.name][i].success_class = 'success'; - } - else { - 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) { - 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 - }); - }); - } - }; - - }; - } -]) - -/** - * - * TODO: Document - * - */ -.factory('ShowUpdateStatus', ['$rootScope', '$location', '$log', '$stateParams', 'Rest', 'Alert', 'GenerateForm', - 'Prompt', 'ProcessErrors', 'GetBasePath', 'FormatDate', 'InventoryStatusForm', 'Wait', - function ($rootScope, $location, $log, $stateParams, Rest, Alert, GenerateForm, Prompt, ProcessErrors, GetBasePath, - FormatDate, InventoryStatusForm, Wait) { - return function (params) { - - var group_name = params.group_name, - last_update = params.last_update, - generator = GenerateForm, - form = InventoryStatusForm, - license_error = params.license_error, - maxrows, html, scope, ww, wh, x, y; - - function calcRows(content) { - var n, rows; - n = content.match(/\n/g); - rows = (n) ? n.length : 1; - return (rows > maxrows) ? maxrows : rows; - } - - if (last_update === undefined || last_update === null || last_update === '') { - Wait('stop'); - Alert('Missing Configuration', 'The selected group is not configured for inventory sync. ' + - 'Edit the group and provide Source information.', 'alert-info'); - } else { - html = "
\n" + - "
\n"; - - $('#inventory-modal-container').empty().append(html); - scope = generator.inject(form, { - mode: 'edit', - id: 'form-container', - related: false - }); - - // Set modal dimensions based on viewport width - ww = $(document).width(); - wh = $('body').height(); - if (ww > 1199) { - // desktop - x = 675; - y = (750 > wh) ? wh - 20 : 750; - maxrows = 18; - } else if (ww <= 1199 && ww >= 768) { - x = 550; - y = (620 > wh) ? wh - 15 : 620; - maxrows = 12; - } else { - x = (ww - 20); - y = (500 > wh) ? wh : 500; - maxrows = 10; - } - - // Create the modal - $('#status-modal-dialog').dialog({ - buttons: { - "OK": function () { - $(this).dialog("close"); - } - }, - modal: true, - width: x, - height: y, - autoOpen: false, - create: function () { - // fix the close button - $('.ui-dialog[aria-describedby="status-modal-dialog"]').find('.ui-dialog-titlebar button') - .empty().attr({ - 'class': 'close' - }).text('x'); - // fix the OK button - $('.ui-dialog[aria-describedby="status-modal-dialog"]').find('.ui-dialog-buttonset button:first') - .attr({ - 'class': 'btn btn-primary' - }); - }, - resizeStop: function () { - // for some reason, after resizing dialog the form and fields (the content) doesn't expand to 100% - var dialog = $('.ui-dialog[aria-describedby="status-modal-dialog"]'), - content = dialog.find('#status-modal-dialog'); - content.width(dialog.width() - 28); - }, - close: function () { - // Destroy on close - $('.tooltip').each(function () { - // Remove any lingering tooltip
elements - $(this).remove(); - }); - $('.popover').each(function () { - // remove lingering popover
elements - $(this).remove(); - }); - $('#status-modal-dialog').dialog('destroy'); - $('#inventory-modal-container').empty(); - }, - open: function () { - Wait('stop'); - } - }); - - Rest.setUrl(last_update); - Rest.get() - .success(function (data) { - for (var fld in form.fields) { - if (data[fld]) { - if (fld === 'created') { - scope[fld] = FormatDate(new Date(data[fld])); - } else { - scope[fld] = data[fld]; - } - } - } - scope.license_error = license_error; - scope.status_rows = calcRows(data.status); - scope.stdout_rows = calcRows(data.result_stdout); - scope.traceback_rows = calcRows(data.result_traceback); - $('#status-modal-dialog').dialog('open'); - }) - .error(function (data, status) { - $('#form-modal').modal("hide"); - ProcessErrors(scope, data, status, null, { - hdr: 'Error!', - msg: 'Failed to retrieve last update: ' + last_update + '. GET status: ' + status - }); - }); - } - }; } ]); diff --git a/awx/ui/client/src/helpers/Hosts.js b/awx/ui/client/src/helpers/Hosts.js index 1bd62c955c..7fafc5b7d9 100644 --- a/awx/ui/client/src/helpers/Hosts.js +++ b/awx/ui/client/src/helpers/Hosts.js @@ -217,489 +217,6 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', listGenerator.name, }; }]) -.factory('InjectHosts', ['generateList', 'InventoryHosts', 'HostsReload', - function(GenerateList, InventoryHosts, HostsReload) { - return function(params) { - - 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: host_scope, mode: 'edit', id: 'host-list-container', searchSize: 'col-lg-6 col-md-6 col-sm-6 col-xs-12' }); - - // Load data - HostsReload({ scope: host_scope, group_id: group_id, inventory_id: inventory_id, parent_scope: group_scope, pageSize: pageSize }); - }; - }]) - -.factory('HostsList', ['$rootScope', '$location', '$log', '$stateParams', 'Rest', 'Alert', 'HostList', 'generateList', - 'Prompt', 'SearchInit', 'PaginateInit', 'ProcessErrors', 'GetBasePath', 'HostsAdd', 'HostsReload', 'SelectionInit', - function($rootScope, $location, $log, $stateParams, Rest, Alert, HostList, GenerateList, Prompt, SearchInit, - PaginateInit, ProcessErrors, GetBasePath, HostsAdd, HostsReload, SelectionInit) { - return function(params) { - - var inventory_id = params.inventory_id, - group_id = params.group_id, - list = HostList, - generator = GenerateList, - defaultUrl, scope; - - list.iterator = 'subhost'; //Override the iterator and name so the scope of the modal dialog - list.name = 'subhosts'; //will not conflict with the parent scope - - - - scope = generator.inject(list, { - id: 'form-modal-body', - mode: 'select', - selectButton: false - }); - - defaultUrl = GetBasePath('inventory') + inventory_id + '/hosts/?not__groups__id=' + scope.group_id; - - scope.formModalActionLabel = 'Select'; - scope.formModalHeader = 'Add Existing Hosts'; - scope.formModalCancelShow = true; - - SelectionInit({ scope: scope, list: list, url: GetBasePath('groups') + group_id + '/hosts/' }); - - if (scope.removeModalClosed) { - scope.removeModalClosed(); - } - scope.removeModalClosed = scope.$on('modalClosed', function() { - // if the modal closed, assume something got changed and reload the host list - HostsReload(params); - }); - - $('.popover').popover('hide'); //remove any lingering pop-overs - $('#form-modal .btn-none').removeClass('btn-none').addClass('btn-success'); - $('#form-modal').modal({ backdrop: 'static', keyboard: false }); - - SearchInit({ scope: scope, set: 'subhosts', list: list, url: defaultUrl }); - PaginateInit({ scope: scope, list: list, url: defaultUrl, mode: 'lookup' }); - scope.search(list.iterator); - - if (!scope.$$phase) { - scope.$digest(); - } - - scope.createHost = function() { - $('#form-modal').modal('hide'); - HostsAdd({ scope: params.scope, inventory_id: inventory_id, group_id: group_id }); - }; - - }; - }]) - - -.factory('HostsCreate', ['$rootScope', '$location', '$log', '$stateParams', 'Rest', 'Alert', 'HostForm', 'GenerateForm', - 'Prompt', 'ProcessErrors', 'GetBasePath', 'HostsReload', 'ParseTypeChange', 'Wait', 'ToJSON', - function($rootScope, $location, $log, $stateParams, Rest, Alert, HostForm, GenerateForm, Prompt, ProcessErrors, - GetBasePath, HostsReload, ParseTypeChange, Wait, ToJSON) { - return function(params) { - - var parent_scope = params.scope, - inventory_id = parent_scope.inventory_id, - group_id = parent_scope.selected_group_id, - defaultUrl = GetBasePath('groups') + group_id + '/hosts/', - form = HostForm, - generator = GenerateForm, - scope = generator.inject(form, {mode: 'add', modal: true, related: false}), - master={}; - - scope.formModalActionLabel = 'Save'; - scope.formModalHeader = 'Create New Host'; - scope.formModalCancelShow = true; - - scope.parseType = 'yaml'; - ParseTypeChange({ scope: scope, field_id: 'host_variables' }); - - if (scope.removeHostsReload) { - scope.removeHostsReload(); - } - scope.removeHostsReload = scope.$on('hostsReload', function() { - HostsReload(params); - }); - - $('#form-modal .btn-none').removeClass('btn-none').addClass('btn-success'); - //$('#form-modal').unbind('hidden'); - //$('#form-modal').on('hidden', function () { scope.$emit('hostsReload'); }); - - generator.reset(); - master={}; - - if (!scope.$$phase) { - scope.$digest(); - } - - if (scope.removeHostSaveComplete) { - scope.removeHostSaveComplete(); - } - scope.removeHostSaveComplete = scope.$on('HostSaveComplete', function() { - Wait('stop'); - $('#form-modal').modal('hide'); - - HostsReload({ - scope: parent_scope, - group_id: parent_scope.selected_group_id, - tree_id: parent_scope.selected_tree_id, - inventory_id: parent_scope.inventory_id - }); - - }); - - // Save - scope.formModalAction = function() { - - Wait('start'); - - var fld, data={}; - scope.formModalActionDisabled = true; - data.variables = ToJSON(scope.parseType, scope.variables, true); - for (fld in form.fields) { - if (fld !== 'variables') { - data[fld] = scope[fld]; - } - } - data.inventory = inventory_id; - - Rest.setUrl(defaultUrl); - Rest.post(data) - .success( function() { - scope.$emit('HostSaveComplete'); - }) - .error( function(data, status) { - Wait('stop'); - scope.formModalActionDisabled = false; - ProcessErrors(scope, data, status, form, - { hdr: 'Error!', msg: 'Failed to add new host. POST returned status: ' + status }); - }); - }; - - // Cancel - scope.formReset = function() { - // Defaults - generator.reset(); - }; - - scope.cancelModal = function() { - - }; - - }; - }]) - - -.factory('HostsEdit', ['$rootScope', '$location', '$log', '$stateParams', 'Rest', 'Alert', 'HostForm', 'GenerateForm', - 'Prompt', 'ProcessErrors', 'GetBasePath', 'HostsReload', 'ParseTypeChange', 'Wait', 'Find', 'SetStatus', 'ApplyEllipsis', - 'ToJSON', 'ParseVariableString', 'CreateDialog', 'TextareaResize', - function($rootScope, $location, $log, $stateParams, Rest, Alert, HostForm, GenerateForm, Prompt, ProcessErrors, - GetBasePath, HostsReload, ParseTypeChange, Wait, Find, SetStatus, ApplyEllipsis, ToJSON, - ParseVariableString, CreateDialog, TextareaResize) { - return function(params) { - - var parent_scope = params.host_scope, - group_scope = params.group_scope, - host_id = params.host_id, - inventory_id = params.inventory_id, - mode = params.mode, // 'add' or 'edit' - selected_group_id = params.selected_group_id, - generator = GenerateForm, - form = HostForm, - defaultUrl, - scope = parent_scope.$new(), - master = {}, - relatedSets = {}, - buttons, url, form_scope; - - form_scope = - generator.inject(HostForm, { mode: 'edit', id: 'host-modal-dialog', related: false, scope: scope }); - generator.reset(); - - buttons = [{ - label: "Cancel", - onClick: function() { - scope.cancelModal(); - }, - icon: "fa-times", - "class": "btn btn-default", - "id": "host-cancel-button" - },{ - label: "Save", - onClick: function() { - scope.saveModal(); - }, - icon: "fa-check", - "class": "btn btn-primary", - "id": "host-save-button" - }]; - - CreateDialog({ - scope: scope, - buttons: buttons, - width: 675, - height: 750, - minWidth: 400, - title: 'Host Properties', - id: 'host-modal-dialog', - closeOnEscape: false, - form: form_scope.host_form, - onClose: function() { - Wait('stop'); - scope.codeMirror.destroy(); - $('#host-modal-dialog').empty(); - }, - onResizeStop: function() { - TextareaResize({ - scope: scope, - textareaId: 'host_variables', - modalId: 'host-modal-dialog', - formId: 'host_form' - }); - }, - beforeDestroy: function() { - if (scope.codeMirror) { - scope.codeMirror.destroy(); - } - $('#host-modal-dialog').empty(); - }, - onOpen: function() { - $('#host_name').focus(); - }, - callback: 'HostEditDialogReady' - }); - - scope.parseType = 'yaml'; - - if (scope.hostVariablesLoadedRemove) { - scope.hostVariablesLoadedRemove(); - } - scope.hostVariablesLoadedRemove = scope.$on('hostVariablesLoaded', function() { - $('#host-modal-dialog').dialog('open'); - setTimeout(function() { - TextareaResize({ - scope: scope, - textareaId: 'host_variables', - modalId: 'host-modal-dialog', - formId: 'host_form', - parse: true - }); - }, 300); - //ParseTypeChange({ scope: scope, field_id: 'host_variables', onReady: callback }); - }); - - Wait('start'); - - // Retrieve detail record and prepopulate the form - if (mode === 'edit') { - defaultUrl = GetBasePath('hosts') + host_id + '/'; - Rest.setUrl(defaultUrl); - Rest.get() - .success( function(data) { - var set, fld, related; - for (fld in form.fields) { - if (data[fld]) { - scope[fld] = data[fld]; - master[fld] = scope[fld]; - } - } - related = data.related; - for (set in form.related) { - if (related[set]) { - relatedSets[set] = { url: related[set], iterator: form.related[set].iterator }; - } - } - scope.variable_url = data.related.variable_data; - scope.has_inventory_sources = data.has_inventory_sources; - scope.$emit('hostVariablesLoaded'); - }) - .error( function(data, status) { - ProcessErrors(parent_scope, data, status, form, - { hdr: 'Error!', msg: 'Failed to retrieve host: ' + host_id + '. GET returned status: ' + status }); - }); - } - else { - if (selected_group_id) { - // adding hosts to a group - url = GetBasePath('groups') + selected_group_id + '/'; - } else { - // adding hosts to the top-level (inventory) - url = GetBasePath('inventory') + inventory_id + '/'; - } - // Add mode - 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) { - scope.removeSaveCompleted(); - } - scope.removeSaveCompleted = scope.$on('saveCompleted', function() { - try { - $('#host-modal-dialog').dialog('close'); - } - catch(err) { - // ignore - } - if (group_scope && group_scope.refreshHosts) { - group_scope.refreshHosts(); - } - if (parent_scope.refreshHosts) { - parent_scope.refreshHosts(); - } - scope.$destroy(); - }); - - // Save changes to the parent - scope.saveModal = function() { - - Wait('start'); - var fld, data={}; - - try { - data.variables = ToJSON(scope.parseType, scope.variables, true); - for (fld in form.fields) { - data[fld] = scope[fld]; - } - data.inventory = inventory_id; - Rest.setUrl(defaultUrl); - if (mode === 'edit') { - Rest.put(data) - .success( function() { - scope.$emit('saveCompleted'); - }) - .error( function(data, status) { - ProcessErrors(scope, data, status, form, - { hdr: 'Error!', msg: 'Failed to update host: ' + host_id + '. PUT returned status: ' + status }); - }); - } - else { - Rest.post(data) - .success( function() { - scope.$emit('saveCompleted'); - }) - .error( function(data, status) { - ProcessErrors(scope, data, status, form, - { hdr: 'Error!', msg: 'Failed to create host. POST returned status: ' + status }); - }); - } - } - catch(e) { - // ignore. ToJSON will have already alerted the user - } - }; - - // Cancel - scope.formReset = function() { - generator.reset(); - for (var fld in master) { - scope[fld] = master[fld]; - } - scope.parseType = 'yaml'; - }; - - scope.cancelModal = function() { - try { - $('#host-modal-dialog').dialog('close'); - } - catch(err) { - // ignore - } - scope.$destroy(); - }; - - }; - }]) - - -.factory('HostsDelete', ['$rootScope', '$location', '$log', '$stateParams', 'Rest', 'Alert', 'Prompt', 'ProcessErrors', 'GetBasePath', 'HostsReload', 'Wait', - function($rootScope, $location, $log, $stateParams, 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.parent_scope, - host_id = params.host_id, - host_name = params.host_name, - group, - url_list = []; - - 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/'); - } - - if (scope.removeHostsReload) { - scope.removeHostsReload(); - } - scope.removeHostsReload = scope.$on('hostsReload', function() { - $('#prompt-modal').modal('hide'); - scope.refreshHosts(); - }); - - $('#prompt-modal').on('hidden.bs.modal', function(){ Wait('stop'); }); - - action_to_take = function() { - var count=0, i; - - Wait('start'); - - if (scope.removeHostRemoved) { - scope.removeHostRemoved(); - } - scope.removeHostRemoved = scope.$on('hostRemoved', function(){ - count++; - if (count === url_list.length) { - Wait('start'); - scope.$emit('hostsReload'); - } - }); - - for(i=0; i < url_list.length; i++) { - Rest.setUrl(url_list[i]); - Rest.post({ id: host_id, disassociate: 1 }) - .success( function() { - scope.$emit('hostRemoved'); - }) - .error( function(data, status) { - ProcessErrors(scope, data, status, null, - { hdr: 'Error!', msg: 'Attempt to delete ' + host_name + ' failed. DELETE returned status: ' + status }); - }); - } - }; - - body = (group) ? '

Are you sure you want to remove the host below from group ' + group.name + '?' + - ' It will still be part of the inventory and available in All Hosts.

' + host_name + '
' : - '
Are you sure you want to permanently delete the host below from the inventory?
' + host_name + '
'; - Prompt({ - hdr: 'Delete Host', - body: body, - action: action_to_take, - actionText: 'DELETE' - }); - - }; - }]) - .factory('HostsCopy', ['$compile', 'Rest', 'ProcessErrors', 'CreateDialog', 'GetBasePath', 'Wait', 'generateList', 'GroupList', 'SearchInit', 'PaginateInit', function($compile, Rest, ProcessErrors, CreateDialog, GetBasePath, Wait, GenerateList, GroupList, SearchInit, PaginateInit) { @@ -933,304 +450,4 @@ return function(params) { }; -}]) - -.factory('EditHostGroups', ['$rootScope', '$location', '$log', '$stateParams', 'Rest', 'Alert', 'GenerateForm', 'Prompt', - 'ProcessErrors', 'GetBasePath', 'HostsReload', 'ParseTypeChange', 'Wait', - function($rootScope, $location, $log, $stateParams, Rest, Alert, GenerateForm, Prompt, ProcessErrors, GetBasePath, HostsReload, - ParseTypeChange, Wait) { - return function(params) { - - var host_id = params.host_id, - inventory_id = params.inventory_id, - generator = GenerateForm, - actions = [], - i, html, defaultUrl, scope, postAction; - - html = "
\n"; - html += "
\n"; - html += "\n"; - html += "\n"; - html += "
\n"; - html += "
\n"; - html += "\n"; - html += "\n"; - html += "
\n"; - html += "
\n"; - html += "
\n"; - html += "
\n"; - html += "\n"; - html += "\n"; - html += "

(move selected groups)

\n"; - html += "
\n"; - html += "
\n"; - - defaultUrl = GetBasePath('hosts') + host_id + '/'; - scope = generator.inject(null, { mode: 'edit', modal: true, related: false, html: html }); - - for (i=0; i < scope.hosts.length; i++) { - if (scope.hosts[i].id === host_id) { - scope.host = scope.hosts[i]; - } - } - - scope.selectedGroups = null; - scope.assignedGroups = null; - scope.leftButtonDisabled = true; - scope.rightButtonDisabled = true; - - scope.formModalActionLabel = 'Save'; - //scope.formModalHeader = 'Host Groups'; - scope.formModalHeader = scope.host.name + ' - Groups'; - scope.formModalCancelShow = true; - scope.formModalActionDisabled = true; - - $('#form-modal .btn-none').removeClass('btn-none').addClass('btn-success'); - - if (scope.hostGroupChangeRemove) { - scope.hostGroupChangeRemove(); - } - scope.hostGroupChangeRemove = scope.$on('hostGroupChange', function() { - actions.pop(); - if (actions.length === 0) { - postAction = function() { - setTimeout(function() { Wait('stop'); }, 500); - }; - HostsReload({ scope: scope, inventory_id: inventory_id, group_id: scope.group_id , action: postAction }); - } - }); - - // Save changes - scope.formModalAction = function() { - var i, j, found; - - $('#form-modal').modal('hide'); - Wait('start'); - - // removed host from deleted groups - for (i=0; i < scope.original_groups.length; i++) { - found = false; - for (j=0; j < scope.host_groups.length; j++) { - if (scope.original_groups[i].id === scope.host_groups[j].id) { - found = true; - } - } - if (!found) { - // group was removed - actions.push({ group_id: scope.original_groups[i].id , action: 'delete' }); - Rest.setUrl(GetBasePath('groups') + scope.original_groups[i].id + '/hosts/'); - Rest.post({ id: host_id, disassociate: 1 }) - .success( function() { - scope.$emit('hostGroupChange'); - }) - .error( function(data, status) { - scope.$emit('hostGroupChange'); - ProcessErrors(scope, data, status, null, - { hdr: 'Error!', msg: 'Attempt to remove host from group ' + scope.original_groups[i].name + - ' failed. POST returned status: ' + status }); - }); - } - } - - // add host to new groups - for (i=0; i < scope.host_groups.length; i++) { - found = false; - for (j=0; j < scope.original_groups.length; j++) { - if (scope.original_groups[j].id === scope.host_groups[i].id) { - found = true; - } - } - if (!found) { - // group was added - actions.push({ group_id: scope.host_groups[i].id , action: 'add' }); - Rest.setUrl(GetBasePath('groups') + scope.host_groups[i].id + '/hosts/'); - Rest.post(scope.host) - .success( function() { - scope.$emit('hostGroupChange'); - }) - .error( function(data, status) { - scope.$emit('hostGroupChange'); - ProcessErrors(scope, data, status, null, - { hdr: 'Error!', msg: 'Attempt to add host to group ' + scope.host_groups[i].name + - ' failed. POST returned status: ' + status }); - }); - } - } - }; - - scope.leftChange = function() { - // Select/deselect on available groups list - if (scope.selectedGroups !== null && scope.selectedGroups.length > 0) { - scope.assignedGroups = null; - scope.leftButtonDisabled = true; - scope.rightButtonDisabled = false; - } - else { - scope.rightButtonDisabled = true; - } - }; - - scope.rightChange = function() { - // Select/deselect made on host groups list - if (scope.assignedGroups !== null && scope.assignedGroups.length > 0) { - scope.selectedGroups = null; - scope.leftButtonDisabled = false; - scope.rightButtonDisabled = true; - } - else { - scope.leftButtonDisabled = true; - } - }; - - scope.moveLeft = function() { - // Remove selected groups from the list of assigned groups - - var i, j, found, placed; - - for (i=0; i < scope.assignedGroups.length; i++){ - for (j=0 ; j < scope.host_groups.length; j++) { - if (scope.host_groups[j].id === scope.assignedGroups[i].id) { - scope.host_groups.splice(j,1); - break; - } - } - } - for (i=0; i < scope.assignedGroups.length; i++){ - found = false; - for (j=0; j < scope.available_groups.length && !found; j++){ - if (scope.available_groups[j].id === scope.assignedGroups[i].id) { - found=true; - } - } - if (!found) { - placed = false; - for (j=0; j < scope.available_groups.length && !placed; j++){ - if (j === 0 && scope.assignedGroups[i].name.toLowerCase() < scope.available_groups[j].name.toLowerCase()) { - // prepend to the beginning of the array - placed=true; - scope.available_groups.unshift(scope.assignedGroups[i]); - } - else if (j + 1 < scope.available_groups.length) { - if (scope.assignedGroups[i].name.toLowerCase() > scope.available_groups[j].name.toLowerCase() && - scope.assignedGroups[i].name.toLowerCase() < scope.available_groups[j + 1].name.toLowerCase() ) { - // insert into the middle of the array - placed = true; - scope.available_groups.splice(j + 1, 0, scope.assignedGroups[i]); - } - } - } - if (!placed) { - // append to the end of the array - scope.available_groups.push(scope.assignedGroups[i]); - } - } - } - scope.assignedGroups = null; - scope.leftButtonDisabled = true; - scope.rightButtonDisabled = true; - scope.formModalActionDisabled = false; - }; - - scope.moveRight = function() { - // Remove selected groups from list of available groups - - var i, j, found, placed; - - for (i=0; i < scope.selectedGroups.length; i++){ - for (j=0 ; j < scope.available_groups.length; j++) { - if (scope.available_groups[j].id === scope.selectedGroups[i].id) { - scope.available_groups.splice(j,1); - break; - } - } - } - for (i=0; i < scope.selectedGroups.length; i++){ - found = false; - for (j=0; j < scope.host_groups.length && !found; j++){ - if (scope.host_groups[j].id === scope.selectedGroups[i].id) { - found=true; - } - } - if (!found) { - placed = false; - for (j=0; j < scope.host_groups.length && !placed; j++) { - if (j === 0 && scope.selectedGroups[i].name.toLowerCase() < scope.host_groups[j].name.toLowerCase()) { - // prepend to the beginning of the array - placed=true; - scope.host_groups.unshift(scope.selectedGroups[i]); - } - else if (j + 1 < scope.host_groups.length) { - if (scope.selectedGroups[i].name.toLowerCase() > scope.host_groups[j].name.toLowerCase() && - scope.selectedGroups[i].name.toLowerCase() < scope.host_groups[j + 1].name.toLowerCase() ) { - // insert into the middle of the array - placed = true; - scope.host_groups.splice(j + 1, 0, scope.selectedGroups[i]); - } - } - } - if (!placed) { - // append to the end of the array - scope.host_groups.push(scope.selectedGroups[i]); - } - } - } - scope.selectedGroups = null; - scope.leftButtonDisabled = true; - scope.rightButtonDisabled = true; - scope.formModalActionDisabled = false; - }; - - - // Load the host's current list of groups - scope.host_groups = []; - scope.original_groups = []; - scope.available_groups = []; - Rest.setUrl(scope.host.related.groups + '?order_by=name'); - Rest.get() - .success( function(data) { - var i, j, found; - for (i=0; i < data.results.length; i++) { - scope.host_groups.push({ - id: data.results[i].id, - name: data.results[i].name, - description: data.results[i].description - }); - scope.original_groups.push({ - id: data.results[i].id, - name: data.results[i].name, - description: data.results[i].description - }); - } - for (i=0; i < scope.inventory_groups.length; i++) { - found = false; - for (j=0; j < scope.host_groups.length; j++) { - if (scope.inventory_groups[i].id === scope.host_groups[j].id) { - found = true; - } - } - if (!found) { - scope.available_groups.push(scope.inventory_groups[i]); - } - } - }) - .error( function(data, status) { - ProcessErrors(scope, data, status, null, - { hdr: 'Error!', msg: 'Failed to get current groups for host: ' + host_id + '. GET returned: ' + status }); - }); - - if (scope.removeHostsReload) { - scope.removeHostsReload(); - } - scope.removeHostsReload = scope.$on('hostsReload', function() { - HostsReload(params); - }); - - if (!scope.$$phase) { - scope.$digest(); - } - }; - }]); +}]); \ No newline at end of file diff --git a/awx/ui/client/src/helpers/Jobs.js b/awx/ui/client/src/helpers/Jobs.js index 7d8fee0d62..9d764d449f 100644 --- a/awx/ui/client/src/helpers/Jobs.js +++ b/awx/ui/client/src/helpers/Jobs.js @@ -156,126 +156,7 @@ export default }; } ]) - - .factory('ShowJobSummary', ['Rest', 'Wait', 'GetBasePath', 'FormatDate', 'ProcessErrors', 'GenerateForm', 'JobSummary', - function (Rest, Wait, GetBasePath, FormatDate, ProcessErrors, GenerateForm, JobSummary) { - return function (params) { - // Display status info in a modal dialog- called from inventory edit page - - var job_id = params.job_id, - generator = GenerateForm, - form = JobSummary, - scope, ww, wh, x, y, maxrows, url, html; - - html = '
' + - '
\n'; - - $('#inventory-modal-container').empty().append(html); - - scope = generator.inject(form, { mode: 'edit', id: 'form-container', related: false }); - - // Set modal dimensions based on viewport width - ww = $(document).width(); - wh = $('body').height(); - if (ww > 1199) { - // desktop - x = 675; - y = (750 > wh) ? wh - 20 : 750; - maxrows = 20; - } else if (ww <= 1199 && ww >= 768) { - x = 550; - y = (620 > wh) ? wh - 15 : 620; - maxrows = 15; - } else { - x = (ww - 20); - y = (500 > wh) ? wh : 500; - maxrows = 10; - } - - // Create the modal - $('#status-modal-dialog').dialog({ - buttons: { - 'OK': function () { - $(this).dialog('close'); - } - }, - modal: true, - width: x, - height: y, - autoOpen: false, - closeOnEscape: false, - create: function () { - // fix the close button - $('.ui-dialog[aria-describedby="status-modal-dialog"]').find('.ui-dialog-titlebar button') - .empty().attr({ - 'class': 'close' - }).text('x'); - // fix the OK button - $('.ui-dialog[aria-describedby="status-modal-dialog"]').find('.ui-dialog-buttonset button:first') - .attr({ - 'class': 'btn btn-primary' - }); - }, - resizeStop: function () { - // for some reason, after resizing dialog the form and fields (the content) doesn't expand to 100% - var dialog = $('.ui-dialog[aria-describedby="status-modal-dialog"]'), - titleHeight = dialog.find('.ui-dialog-titlebar').outerHeight(), - buttonHeight = dialog.find('.ui-dialog-buttonpane').outerHeight(), - content = dialog.find('#status-modal-dialog'); - content.width(dialog.width() - 28); - content.css({ height: (dialog.height() - titleHeight - buttonHeight - 10) }); - }, - close: function () { - // Destroy on close - $('.tooltip').each(function () { - // Remove any lingering tooltip
elements - $(this).remove(); - }); - $('.popover').each(function () { - // remove lingering popover
elements - $(this).remove(); - }); - $('#status-modal-dialog').dialog('destroy'); - $('#inventory-modal-container').empty(); - }, - open: function () { - Wait('stop'); - } - }); - - function calcRows(content) { - var n = content.match(/\n/g), - rows = (n) ? n.length : 1; - return (rows > maxrows) ? 20 : rows; - } - - Wait('start'); - url = GetBasePath('jobs') + job_id + '/'; - Rest.setUrl(url); - Rest.get() - .success(function (data) { - var cDate; - scope.id = data.id; - scope.name = data.name; - scope.status = data.status; - scope.result_stdout = data.result_stdout; - scope.result_traceback = data.result_traceback; - scope.stdout_rows = calcRows(scope.result_stdout); - scope.traceback_rows = calcRows(scope.result_traceback); - cDate = new Date(data.created); - scope.created = FormatDate(cDate); - $('#status-modal-dialog').dialog('open'); - }) - .error(function (data, status) { - ProcessErrors(scope, data, status, null, { hdr: 'Error!', - msg: 'Attempt to load job failed. GET returned status: ' + status }); - }); - }; - - } - ]) - - + .factory('JobsListUpdate', ['Rest', function(Rest) { return function(params) { var scope = params.scope, diff --git a/awx/ui/client/src/helpers/inventory.js b/awx/ui/client/src/helpers/inventory.js index 9becb2dbb9..18a8b27883 100644 --- a/awx/ui/client/src/helpers/inventory.js +++ b/awx/ui/client/src/helpers/inventory.js @@ -81,217 +81,4 @@ export default }); }; } - ]) - - .factory('EditInventoryProperties', ['InventoryForm', 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LookUpInit', 'OrganizationList', - 'GetBasePath', 'ParseTypeChange', 'SaveInventory', 'Wait', 'Store', 'SearchInit', 'ParseVariableString', 'CreateDialog', 'TextareaResize', - function (InventoryForm, GenerateForm, Rest, Alert, ProcessErrors, LookUpInit, OrganizationList, GetBasePath, ParseTypeChange, SaveInventory, - Wait, Store, SearchInit, ParseVariableString, CreateDialog, TextareaResize) { - return function (params) { - - var parent_scope = params.scope, - inventory_id = params.inventory_id, - generator = GenerateForm, - form = InventoryForm, - master = {}, - //PreviousSearchParams = Store('CurrentSearchParams'), - buttons, - scope = parent_scope.$new(); - - form.well = false; - - var form_scope = - generator.inject(form, { - mode: 'edit', - showButtons: false, - showActions: false, - id: 'inventory-edit-modal-dialog', - related: false, - scope: scope - }); - - /* Reset form properties. Otherwise it screws up future requests of the Inventories detail page */ - form.well = true; - - buttons = [{ - label: "Cancel", - onClick: function() { - scope.cancelModal(); - }, - icon: "fa-times", - "class": "btn btn-default", - "id": "inventory-edit-cancel-button" - },{ - label: "Save", - onClick: function() { - scope.saveModal(); - }, - icon: "fa-check", - "class": "btn btn-primary", - "id": "inventory-edit-save-button" - }]; - - CreateDialog({ - scope: scope, - buttons: buttons, - width: 675, - height: 750, - minWidth: 400, - title: 'Inventory Properties', - id: 'inventory-edit-modal-dialog', - closeOnEscape: false, - form: form_scope.inventory_form, - onClose: function() { - Wait('stop'); - scope.codeMirror.destroy(); - $('#inventory-edit-modal-dialog').empty(); - }, - onResizeStop: function() { - TextareaResize({ - scope: scope, - textareaId: 'inventory_variables', - modalId: 'inventory-edit-modal-dialog', - formId: 'inventory_form' - }); - }, - beforeDestroy: function() { - if (scope.codeMirror) { - scope.codeMirror.destroy(); - } - $('#inventory-edit-modal-dialog').empty(); - }, - onOpen: function() { - $('#inventory_name').focus(); - setTimeout(function() { - TextareaResize({ - scope: scope, - textareaId: 'inventory_variables', - modalId: 'inventory-edit-modal-dialog', - formId: 'inventory_form', - parse: true - }); - }, 300); - }, - callback: 'InventoryEditDialogReady' - }); - - scope.parseType = 'yaml'; - - if (scope.removeInventoryPropertiesLoaded) { - scope.removeInventoryPropertiesLoaded(); - } - scope.removeInventoryPropertiesLoaded = scope.$on('inventoryPropertiesLoaded', function() { - Wait('stop'); - $('#inventory-edit-modal-dialog').dialog('open'); - }); - - scope.formModalActionLabel = 'Save'; - scope.formModalCancelShow = true; - scope.formModalInfo = false; - scope.formModalHeader = 'Inventory Properties'; - - Wait('start'); - Rest.setUrl(GetBasePath('inventory') + inventory_id + '/'); - Rest.get() - .success(function (data) { - var fld; - for (fld in form.fields) { - if (fld === 'variables') { - scope.variables = ParseVariableString(data.variables); - master.variables = scope.variables; - } else if (fld === 'inventory_name') { - scope[fld] = data.name; - master[fld] = scope[fld]; - } else if (fld === 'inventory_description') { - scope[fld] = data.description; - master[fld] = scope[fld]; - } else if (data[fld]) { - scope[fld] = data[fld]; - master[fld] = scope[fld]; - } - if (form.fields[fld].sourceModel && data.summary_fields && - data.summary_fields[form.fields[fld].sourceModel]) { - scope[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField] = - data.summary_fields[form.fields[fld].sourceModel][form.fields[fld].sourceField]; - master[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField] = - data.summary_fields[form.fields[fld].sourceModel][form.fields[fld].sourceField]; - } - } - - LookUpInit({ - scope: scope, - form: form, - current_item: scope.organization, - list: OrganizationList, - field: 'organization', - input_type: 'radio' - }); - - scope.$emit('inventoryPropertiesLoaded'); - - }) - .error(function (data, status) { - ProcessErrors(scope, data, status, null, { hdr: 'Error!', - msg: 'Failed to get inventory: ' + inventory_id + '. GET returned: ' + status }); - }); - - if (scope.removeInventorySaved) { - scope.removeInventorySaved(); - } - scope.removeInventorySaved = scope.$on('InventorySaved', function () { - //$('#form-modal').modal('hide'); - // Restore prior search state - //if (scope.searchCleanp) { - // scope.searchCleanup(); - //} - //SearchInit({ - // scope: parent_scope, - // set: PreviousSearchParams.set, - // list: PreviousSearchParams.list, - // url: PreviousSearchParams.defaultUrl, - // iterator: PreviousSearchParams.iterator, - // sort_order: PreviousSearchParams.sort_order, - // setWidgets: false - //}); - //parent_scope.$emit('RefreshInventories'); - try { - $('#inventory-edit-modal-dialog').dialog('close'); - } - catch(err) { - // ignore - } - parent_scope.$emit('RefreshInventories'); - scope.$destroy(); - }); - - scope.cancelModal = function () { - // Restore prior search state - /*if (scope.searchCleanp) { - scope.searchCleanup(); - } - SearchInit({ - scope: parent_scope, - set: PreviousSearchParams.set, - list: PreviousSearchParams.list, - url: PreviousSearchParams.defaultUrl, - iterator: PreviousSearchParams.iterator, - sort_order: PreviousSearchParams.sort_order, - setWidgets: false - });*/ - try { - $('#inventory-edit-modal-dialog').dialog('close'); - } - catch(err) { - // ignore - } - scope.$destroy(); - }; - - scope.saveModal = function () { - scope.inventory_id = inventory_id; - SaveInventory({ scope: scope, parent_scope: parent_scope }); - }; - - }; - } ]); diff --git a/awx/ui/client/src/inventories/inventories.partial.html b/awx/ui/client/src/inventories/inventories.partial.html index 46d04b20dd..0284089d54 100644 --- a/awx/ui/client/src/inventories/inventories.partial.html +++ b/awx/ui/client/src/inventories/inventories.partial.html @@ -1,13 +1,5 @@
-
+
-
-
diff --git a/awx/ui/client/src/adhoc/adhoc.controller.js b/awx/ui/client/src/inventories/manage/adhoc/adhoc.controller.js similarity index 97% rename from awx/ui/client/src/adhoc/adhoc.controller.js rename to awx/ui/client/src/inventories/manage/adhoc/adhoc.controller.js index 4ccf56ca54..a1be88d05c 100644 --- a/awx/ui/client/src/adhoc/adhoc.controller.js +++ b/awx/ui/client/src/inventories/manage/adhoc/adhoc.controller.js @@ -9,7 +9,7 @@ * @name controllers.function:Adhoc * @description This controller controls the adhoc form creation, command launching and navigating to standard out after command has been succesfully ran. */ -function adhocController($q, $scope, $rootScope, $location, $stateParams, +function adhocController($q, $scope, $location, $stateParams, $state, CheckPasswords, PromptForPasswords, CreateLaunchDialog, adhocForm, GenerateForm, Rest, ProcessErrors, ClearScope, GetBasePath, GetChoices, KindChange, LookUpInit, CredentialList, Empty, Wait) { @@ -23,7 +23,7 @@ function adhocController($q, $scope, $rootScope, $location, $stateParams, this.privateFn = privateFn; var id = $stateParams.inventory_id, - hostPattern = $rootScope.hostPatterns || "all"; + hostPattern = $stateParams.pattern; // note: put any urls that the controller will use in here!!!! privateFn.setAvailableUrls = function() { @@ -102,7 +102,6 @@ function adhocController($q, $scope, $rootScope, $location, $stateParams, privateFn.instantiateHostPatterns = function(hostPattern) { $scope.limit = hostPattern; $scope.providedHostPatterns = $scope.limit; - delete $rootScope.hostPatterns; }; // call helpers to initialize lookup and select fields through get @@ -295,7 +294,7 @@ function adhocController($q, $scope, $rootScope, $location, $stateParams, } -export default ['$q', '$scope', '$rootScope', '$location', '$stateParams', +export default ['$q', '$scope', '$location', '$stateParams', '$state', 'CheckPasswords', 'PromptForPasswords', 'CreateLaunchDialog', 'adhocForm', 'GenerateForm', 'Rest', 'ProcessErrors', 'ClearScope', 'GetBasePath', 'GetChoices', 'KindChange', 'LookUpInit', 'CredentialList', 'Empty', 'Wait', diff --git a/awx/ui/client/src/adhoc/adhoc.form.js b/awx/ui/client/src/inventories/manage/adhoc/adhoc.form.js similarity index 97% rename from awx/ui/client/src/adhoc/adhoc.form.js rename to awx/ui/client/src/inventories/manage/adhoc/adhoc.form.js index 44a5c3ba6d..6c61349352 100644 --- a/awx/ui/client/src/adhoc/adhoc.form.js +++ b/awx/ui/client/src/inventories/manage/adhoc/adhoc.form.js @@ -122,20 +122,19 @@ export default function() { dataContainer: "body" }, }, - buttons: { - reset: { - ngClick: 'formReset()', - ngDisabled: true, - label: 'Reset', - 'class': 'Form-buttonDefault Form-button' - }, launch: { label: 'Save', ngClick: 'launchJob()', ngDisabled: true, - 'class': 'Form-buttonDefault Form-button' - } + 'class': 'btn btn-sm List-buttonSubmit' + }, + reset: { + ngClick: 'formReset()', + ngDisabled: true, + label: 'Reset', + 'class': 'btn btn-sm Form-cancelButton' + } }, related: {} diff --git a/awx/ui/client/src/adhoc/adhoc.partial.html b/awx/ui/client/src/inventories/manage/adhoc/adhoc.partial.html similarity index 100% rename from awx/ui/client/src/adhoc/adhoc.partial.html rename to awx/ui/client/src/inventories/manage/adhoc/adhoc.partial.html diff --git a/awx/ui/client/src/inventories/manage/adhoc/adhoc.route.js b/awx/ui/client/src/inventories/manage/adhoc/adhoc.route.js new file mode 100644 index 0000000000..17307ff562 --- /dev/null +++ b/awx/ui/client/src/inventories/manage/adhoc/adhoc.route.js @@ -0,0 +1,24 @@ +/************************************************* + * Copyright (c) 2015 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + + import {templateUrl} from '../../../shared/template-url/template-url.factory'; + +export default { + route: '/adhoc', + params:{ + pattern: { + value: 'all', + squash: true + } + }, + name: 'inventoryManage.adhoc', + views: { + 'form@inventoryManage': { + templateUrl: templateUrl('inventories/manage/adhoc/adhoc'), + controller: 'adhocController' + } + } +}; diff --git a/awx/ui/client/src/inventories/manage/adhoc/main.js b/awx/ui/client/src/inventories/manage/adhoc/main.js new file mode 100644 index 0000000000..7072a23063 --- /dev/null +++ b/awx/ui/client/src/inventories/manage/adhoc/main.js @@ -0,0 +1,11 @@ +import route from './adhoc.route'; +import adhocController from './adhoc.controller'; +import form from './adhoc.form'; + +export default + angular.module('adhoc', []) + .controller('adhocController', adhocController) + .run(['$stateExtender', function($stateExtender) { + $stateExtender.addState(route); + }]) + .factory('adhocForm', form); diff --git a/awx/ui/client/src/inventories/manage/breadcrumbs/breadcrumbs.block.less b/awx/ui/client/src/inventories/manage/breadcrumbs/breadcrumbs.block.less new file mode 100644 index 0000000000..d53e9174c8 --- /dev/null +++ b/awx/ui/client/src/inventories/manage/breadcrumbs/breadcrumbs.block.less @@ -0,0 +1,25 @@ +.InventoryManageBreadCrumbs .BreadCrumb-list{ + padding-right: 0px; +} +.InventoryManageBreadCrumb-ncy.BreadCrumb-list{ + padding-left: 5px; +} +.InventoryManageBreadCrumbs-separator{ + content: "/"; + padding: 0 5px; + color: #B7B7B7; +} +.InventoryManageBreadCrumbs{ + position: relative; + height: auto; + top: -40px; + .BreadCrumb-list{ + margin-bottom: 0px; + } +} +.InventoryManage-breakWord{ + word-break: break-all; +} +ol.BreadCrumb-list{ + display: inline-block; +} \ No newline at end of file diff --git a/awx/ui/client/src/inventories/manage/breadcrumbs/breadcrumbs.controller.js b/awx/ui/client/src/inventories/manage/breadcrumbs/breadcrumbs.controller.js new file mode 100644 index 0000000000..c04f014df2 --- /dev/null +++ b/awx/ui/client/src/inventories/manage/breadcrumbs/breadcrumbs.controller.js @@ -0,0 +1,27 @@ +/************************************************* + * Copyright (c) 2016 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + +export default + ['$state', '$stateParams', '$scope', 'inventoryData', 'breadCrumbData', function($state, $stateParams, $scope, inventoryData, breadCrumbData){ + // process result data into the same order specified in the traversal path + $scope.groups = _.sortBy(breadCrumbData, function(item){ + var index = _.indexOf($stateParams.group, item.id); + return (index === -1) ? $stateParams.group.length : index; + }); + $scope.inventory = inventoryData; + // slices the group stack at $index to supply new group params to $state.go() + $scope.goToGroup = function($index){ + var group = $stateParams.group.slice(0, $index); + $state.go('inventoryManage', {group: group}, {reload: true}); + }; + $scope.state = $state; + $scope.isRootState = function(){ + return $state.current.name === 'inventoryManage'; + }; + $scope.goToInventory = function(){ + $state.go('inventoryManage', {group: undefined}, {reload: true}); + }; + }]; \ No newline at end of file diff --git a/awx/ui/client/src/inventories/manage/breadcrumbs/breadcrumbs.partial.html b/awx/ui/client/src/inventories/manage/breadcrumbs/breadcrumbs.partial.html new file mode 100644 index 0000000000..16a59d4031 --- /dev/null +++ b/awx/ui/client/src/inventories/manage/breadcrumbs/breadcrumbs.partial.html @@ -0,0 +1,50 @@ + diff --git a/awx/ui/client/src/inventories/manage/copy-move/copy-move-groups.controller.js b/awx/ui/client/src/inventories/manage/copy-move/copy-move-groups.controller.js new file mode 100644 index 0000000000..90ce9c5fda --- /dev/null +++ b/awx/ui/client/src/inventories/manage/copy-move/copy-move-groups.controller.js @@ -0,0 +1,68 @@ +/************************************************* + * Copyright (c) 2016 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + + export default + ['$scope', '$state', '$stateParams', 'generateList', 'SearchInit', 'PaginateInit', 'GroupManageService', 'GetBasePath', 'CopyMoveGroupList', 'group', + function($scope, $state, $stateParams, GenerateList, SearchInit, PaginateInit, GroupManageService, GetBasePath, CopyMoveGroupList, group){ + var list = CopyMoveGroupList, + view = GenerateList; + $scope.item = group; + $scope.submitMode = $stateParams.groups === undefined ? 'move' : 'copy'; + $scope['toggle_'+ list.iterator] = 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': + // 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, _.last($stateParams.group)) + .then(() => $state.go('^', null, {reload: true})); + }); + } + break; + } + }; + var init = function(){ + var url = GetBasePath('inventory') + $stateParams.inventory_id + '/groups/'; + url += $stateParams.group ? '?not__id__in=' + group.id + ',' + _.last($stateParams.group) : '?not__id=' + group.id; + list.basePath = url; + view.inject(list, { + mode: 'lookup', + id: 'copyMove-list', + scope: $scope + }); + SearchInit({ + scope: $scope, + set: list.name, + list: list, + url: url + }); + PaginateInit({ + scope: $scope, + list: list, + url : url, + mode: 'lookup' + }); + $scope.search(list.iterator, null, true, false); + // remove the current group from list + }; + init(); + }]; \ No newline at end of file diff --git a/awx/ui/client/src/inventories/manage/copy-move/copy-move-hosts.controller.js b/awx/ui/client/src/inventories/manage/copy-move/copy-move-hosts.controller.js new file mode 100644 index 0000000000..14774de3ba --- /dev/null +++ b/awx/ui/client/src/inventories/manage/copy-move/copy-move-hosts.controller.js @@ -0,0 +1,65 @@ +/************************************************* + * Copyright (c) 2016 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + + export default + ['$scope', '$state', '$stateParams', 'generateList', 'SearchInit', 'PaginateInit', 'HostManageService', 'GetBasePath', 'CopyMoveGroupList', 'host', + function($scope, $state, $stateParams, GenerateList, SearchInit, PaginateInit, HostManageService, GetBasePath, CopyMoveGroupList, host){ + var list = CopyMoveGroupList, + view = GenerateList; + $scope.item = host; + $scope.submitMode = 'copy'; + $scope['toggle_'+ list.iterator] = 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(){ + var url = GetBasePath('inventory') + $stateParams.inventory_id + '/groups/'; + list.basePath = url; + view.inject(list, { + mode: 'lookup', + id: 'copyMove-list', + scope: $scope + }); + SearchInit({ + scope: $scope, + set: list.name, + list: list, + url: url + }); + PaginateInit({ + scope: $scope, + list: list, + url : url, + mode: 'lookup' + }); + $scope.search(list.iterator, null, true, false); + }; + init(); + }]; \ No newline at end of file diff --git a/awx/ui/client/src/inventories/manage/copy/copy.block.less b/awx/ui/client/src/inventories/manage/copy-move/copy-move.block.less similarity index 69% rename from awx/ui/client/src/inventories/manage/copy/copy.block.less rename to awx/ui/client/src/inventories/manage/copy-move/copy-move.block.less index 34b93417f6..76bdc2724c 100644 --- a/awx/ui/client/src/inventories/manage/copy/copy.block.less +++ b/awx/ui/client/src/inventories/manage/copy-move/copy-move.block.less @@ -4,13 +4,6 @@ .List-searchRow { width: 50%; } - - .ui-dialog-buttonpane.ui-widget-content { - border: none; - text-align: right; - margin-top: 15px; - } - .Form-header { width: 50%; margin-top: -20px; @@ -22,9 +15,12 @@ } } } - -.copyMove-directive--copyMoveChoices { +.copyMove-choices { float: right; width: 25%; text-align: right; } +.copyMove-buttons{ + height: 30px; + margin-top: 10px; +} \ No newline at end of file diff --git a/awx/ui/client/src/inventories/manage/copy-move/copy-move.partial.html b/awx/ui/client/src/inventories/manage/copy-move/copy-move.partial.html new file mode 100644 index 0000000000..a148985287 --- /dev/null +++ b/awx/ui/client/src/inventories/manage/copy-move/copy-move.partial.html @@ -0,0 +1,19 @@ +
+
+
{{item.name}}
+
+
+ + +
+
+
+
+ + +
+
diff --git a/awx/ui/client/src/inventories/manage/copy-move/copy-move.route.js b/awx/ui/client/src/inventories/manage/copy-move/copy-move.route.js new file mode 100644 index 0000000000..85d856c5ed --- /dev/null +++ b/awx/ui/client/src/inventories/manage/copy-move/copy-move.route.js @@ -0,0 +1,51 @@ +/************************************************* + * Copyright (c) 2016 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ +import {templateUrl} from '../../../shared/template-url/template-url.factory'; + +import CopyMoveGroupsController from './copy-move-groups.controller'; +import CopyMoveHostsController from './copy-move-hosts.controller'; + +var copyMoveGroup = { + name: 'inventoryManage.copyMoveGroup', + route: '/copy-move-group/{group_id}', + data: { + group_id: 'group_id', + }, + ncyBreadcrumb: { + label: "COPY OR MOVE {{item.name}}" + }, + resolve: { + group: ['GroupManageService', '$stateParams', function(GroupManageService, $stateParams){ + return GroupManageService.get({id: $stateParams.group_id}).then(res => res.data.results[0]); + }] + }, + views: { + 'form@inventoryManage' : { + controller: CopyMoveGroupsController, + templateUrl: templateUrl('inventories/manage/copy-move/copy-move'), + } + } +}; +var copyMoveHost = { + name: 'inventoryManage.copyMoveHost', + route: '/copy-move-host/{host_id}', + ncyBreadcrumb: { + label: "COPY OR MOVE {{item.name}}" + }, + resolve: { + host: ['HostManageService', '$stateParams', function(HostManageService, $stateParams){ + return HostManageService.get({id: $stateParams.host_id}).then(res => res.data.results[0]); + }] + }, + views: { + 'form@inventoryManage': { + templateUrl: templateUrl('inventories/manage/copy-move/copy-move'), + controller: CopyMoveHostsController, + } + } +}; + +export {copyMoveGroup, copyMoveHost}; diff --git a/awx/ui/client/src/inventories/manage/copy/main.js b/awx/ui/client/src/inventories/manage/copy-move/main.js similarity index 53% rename from awx/ui/client/src/inventories/manage/copy/main.js rename to awx/ui/client/src/inventories/manage/copy-move/main.js index 898d5f9ae1..d68348cba8 100644 --- a/awx/ui/client/src/inventories/manage/copy/main.js +++ b/awx/ui/client/src/inventories/manage/copy-move/main.js @@ -4,12 +4,11 @@ * All Rights Reserved *************************************************/ -import route from './copy.route'; +import {copyMoveGroup, copyMoveHost} from './copy-move.route'; export default -angular.module('inventory-copy', []) +angular.module('manageCopyMove', []) .run(['$stateExtender', function($stateExtender) { - $stateExtender.addState(route.copy); - $stateExtender.addState(route.copyGroup); - $stateExtender.addState(route.copyHost); + $stateExtender.addState(copyMoveGroup); + $stateExtender.addState(copyMoveHost); }]); diff --git a/awx/ui/client/src/inventories/manage/copy/copy-groups.controller.js b/awx/ui/client/src/inventories/manage/copy/copy-groups.controller.js deleted file mode 100644 index 4690bb9b62..0000000000 --- a/awx/ui/client/src/inventories/manage/copy/copy-groups.controller.js +++ /dev/null @@ -1,306 +0,0 @@ -function CopyGroupsCtrl($compile, $state, $scope, $location, Rest, ProcessErrors, CreateDialog, - GetBasePath, Wait, GenerateList, GroupList, SearchInit, PaginateInit, GetRootGroups, ParamPass, Store) { - var vm = this; - var name; - - var params = ParamPass.get(), - group_id, - parent_scope, - scope; - - if (params !== undefined) { - group_id = $state.params.group_id; - parent_scope = params.scope; - scope = parent_scope.$new(); - var parent_group = parent_scope.selected_group_id, - url, group; - } else { - group_id = $state.params.group_id; - parent_scope = $scope.$new(); - scope = parent_scope.$new(); - } - - var inventory_id = $state.params.inventory_id; - var PreviousSearchParams = Store('group_current_search_params'); - - 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'); - - // 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') + inventory_id + '/groups/'; - url += (parent_group) ? '?not__id__in=' + group_id + ',' + parent_group : '?not__id=' + group_id; - GenerateList.inject(GroupList, { - mode: 'lookup', - id: 'copyMove-directive--copyGroupSelect', - scope: scope - }); - 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('copyMove-directive--copyGroupSelect')); - $compile(d)(scope); - scope.$emit('CopyDialogReady'); - }); - - 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; - vm.name = group.name; - 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: $state.params.inventory_id, - callback: 'RootGroupsReady' - }); - - var 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); - }; - - var cancel = function() { - restoreSearch(); // Restore all parent search stuff and refresh hosts and groups lists - scope.$destroy(); - $state.go('inventoryManage', {}, { - reload: true - }); - }; - - var allowSave = false; - 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) { - scope[list.name][i].success_class = 'success'; - } else { - 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) { - count++; - } - }); - if (count === 0) { - vm.allowSave = false; - } else { - vm.allowSave = true; - } - }; - - scope.toggleUseRootGroup = function() { - var list = GroupList; - 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; - }); - } - }; - - var 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 (vm.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() { - vm.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 { - vm.cancel(); - } - }); - - // add the new group to the target - url = (target) ? - GetBasePath('groups') + target.id + '/children/' : - GetBasePath('inventory') + inventory_id + '/groups/'; - group = { - id: group.id, - name: group.name, - description: group.description, - inventory: 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') + inventory_id + '/groups/'; - - group = { - id: group.id, - name: group.name, - description: group.description, - inventory: inventory_id - }; - - Rest.setUrl(url); - Rest.post(group) - .success(function() { - vm.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 - }); - }); - } - }; - - var copy_choice = 'copy'; - - angular.extend(vm, { - cancel: cancel, - performCopy: performCopy, - copy_choice: copy_choice, - name: name, - allowSave: allowSave - }); -} - -export default ['$compile', '$state', '$scope', '$location', 'Rest', 'ProcessErrors', 'CreateDialog', 'GetBasePath', 'Wait', 'generateList', 'GroupList', 'SearchInit', - 'PaginateInit', 'GetRootGroups', 'ParamPass', 'Store', CopyGroupsCtrl -]; diff --git a/awx/ui/client/src/inventories/manage/copy/copy-groups.partial.html b/awx/ui/client/src/inventories/manage/copy/copy-groups.partial.html deleted file mode 100644 index d82ca6956f..0000000000 --- a/awx/ui/client/src/inventories/manage/copy/copy-groups.partial.html +++ /dev/null @@ -1,22 +0,0 @@ -
-
-
{{vm.name}}
-
-
- - -
-
-
-
- - -
-
-
diff --git a/awx/ui/client/src/inventories/manage/copy/copy-hosts.controller.js b/awx/ui/client/src/inventories/manage/copy/copy-hosts.controller.js deleted file mode 100644 index 3883fed6ad..0000000000 --- a/awx/ui/client/src/inventories/manage/copy/copy-hosts.controller.js +++ /dev/null @@ -1,239 +0,0 @@ -function CopyHostsCtrl($compile, $state, $scope, Rest, ProcessErrors, CreateDialog, GetBasePath, Wait, GenerateList, GroupList, SearchInit, PaginateInit, ParamPass, Store) { - var vm = this; - var name; - - var host_id = $state.params.host_id; - var inventory_id = $state.params.inventory_id; - var url, host, group_scope, parent_scope, scope, parent_group; - - var params = ParamPass.get(); - if (params !== undefined) { - group_scope = params.group_scope; - parent_scope = params.host_scope; - parent_group = group_scope.selected_group_id; - scope = parent_scope.$new(); - } else { - group_scope = $scope.$new(); - parent_scope = $scope.$new(); - scope = parent_scope.$new(); - } - - var PreviousSearchParams = Store('group_current_search_params'); - - 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'); - // 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') + inventory_id + '/groups/'; - GenerateList.inject(GroupList, { - mode: 'lookup', - id: 'copyMove-directive--copyHostSelect', - 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; - d = angular.element(document.getElementById('copyMove-directive--copyHostPanel')); - $compile(d)(scope); - scope.$emit('HostCopyDialogReady'); - }); - - Wait('start'); - - url = GetBasePath('hosts') + host_id + '/'; - Rest.setUrl(url); - Rest.get() - .success(function(data) { - host = data; - vm.name = host.name; - scope.$emit('ShowDialog'); - }) - .error(function(data, status) { - ProcessErrors(scope, data, status, null, { - hdr: 'Error!', - msg: 'Call to ' + url + ' failed. GET returned: ' + status - }); - }); - - var 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; - }; - - var cancel = function() { - $(document).off("keydown"); - restoreSearch(); // Restore all parent search stuff and refresh hosts and groups lists - scope.$destroy(); - $state.go('inventoryManage', {}, { - reload: true - }); - }; - - var allowSave = false; - 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) { - scope[list.name][i].success_class = 'success'; - } else { - 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) { - count++; - } - }); - if (count === 0) { - vm.allowSave = false; - } else { - vm.allowSave = true; - } - }; - - var 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 (vm.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() { - vm.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 { - vm.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() { - vm.cancel(); - }) - .error(function(data, status) { - ProcessErrors(scope, data, status, null, { - hdr: 'Error!', - msg: 'Failed to add ' + host.name + ' to ' + target.name + '. POST returned: ' + status - }); - }); - } - }; - - - var copy_choice = 'copy'; - - angular.extend(vm, { - copy_choice: copy_choice, - name: name, - cancel: cancel, - allowSave: allowSave, - performCopy: performCopy - }); -} - -export default ['$compile', '$state', '$scope', 'Rest', 'ProcessErrors', 'CreateDialog', 'GetBasePath', 'Wait', 'generateList', 'GroupList', 'SearchInit', - 'PaginateInit', 'ParamPass', 'Store', CopyHostsCtrl -]; diff --git a/awx/ui/client/src/inventories/manage/copy/copy-hosts.partial.html b/awx/ui/client/src/inventories/manage/copy/copy-hosts.partial.html deleted file mode 100644 index aa2990434a..0000000000 --- a/awx/ui/client/src/inventories/manage/copy/copy-hosts.partial.html +++ /dev/null @@ -1,23 +0,0 @@ -
-
-
{{vm.name}}
-
-
- - -
-
-
-
-
- - -
-
-
diff --git a/awx/ui/client/src/inventories/manage/copy/copy.controller.js b/awx/ui/client/src/inventories/manage/copy/copy.controller.js deleted file mode 100644 index 144f55da70..0000000000 --- a/awx/ui/client/src/inventories/manage/copy/copy.controller.js +++ /dev/null @@ -1,16 +0,0 @@ -function inventoryManageCopyCtrl($state) { - var vm = this; - - var cancelPanel = function() { - $state.go('inventoryManage', {}, { - reload: true - }); - }; - - angular.extend(vm, { - cancelPanel: cancelPanel - }); -} - -export default ['$state', inventoryManageCopyCtrl -]; diff --git a/awx/ui/client/src/inventories/manage/copy/copy.partial.html b/awx/ui/client/src/inventories/manage/copy/copy.partial.html deleted file mode 100644 index ff19016e98..0000000000 --- a/awx/ui/client/src/inventories/manage/copy/copy.partial.html +++ /dev/null @@ -1,10 +0,0 @@ -
-
-
- -
- -
-
diff --git a/awx/ui/client/src/inventories/manage/copy/copy.route.js b/awx/ui/client/src/inventories/manage/copy/copy.route.js deleted file mode 100644 index 360cc30edc..0000000000 --- a/awx/ui/client/src/inventories/manage/copy/copy.route.js +++ /dev/null @@ -1,54 +0,0 @@ -/************************************************* - * Copyright (c) 2016 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ -import { - templateUrl -} from '../../../shared/template-url/template-url.factory'; - -import inventoryManageCopyCtrl from './copy.controller'; -import CopyGroupsCtrl from './copy-groups.controller'; -import CopyHostsCtrl from './copy-hosts.controller'; - -export default { - copy: { - name: 'inventoryManage.copy', - route: '/copy', - templateUrl: templateUrl('inventories/manage/copy/copy'), - ncyBreadcrumb: { - label: "COPY" - }, - controller: inventoryManageCopyCtrl, - controllerAs: 'vm', - bindToController: true, - }, - copyGroup: { - name: 'inventoryManage.copy.group', - route: '/group/:group_id?groups', - templateUrl: templateUrl('inventories/manage/copy/copy-groups'), - data: { - group_id: 'group_id', - }, - ncyBreadcrumb: { - label: "GROUP" - }, - controller: CopyGroupsCtrl, - controllerAs: 'vm', - bindToController: true - }, - copyHost: { - name: 'inventoryManage.copy.host', - route: '/host/:host_id?groups', - templateUrl: templateUrl('inventories/manage/copy/copy-hosts'), - data: { - host_id: 'host_id', - }, - ncyBreadcrumb: { - label: "HOST" - }, - controller: CopyHostsCtrl, - controllerAs: 'vm', - bindToController: true - } -}; diff --git a/awx/ui/client/src/inventories/manage/groups/groups-add.controller.js b/awx/ui/client/src/inventories/manage/groups/groups-add.controller.js new file mode 100644 index 0000000000..dff577c546 --- /dev/null +++ b/awx/ui/client/src/inventories/manage/groups/groups-add.controller.js @@ -0,0 +1,215 @@ +/************************************************* + * Copyright (c) 2016 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + + export default + ['$state', '$stateParams', '$scope', 'GroupForm', 'CredentialList', 'inventoryScriptsListObject', 'ParseTypeChange', 'GenerateForm', 'inventoryData', 'LookUpInit', + 'GroupManageService', 'GetChoices', 'GetBasePath', 'CreateSelect2', 'GetSourceTypeOptions', + function($state, $stateParams, $scope, GroupForm, CredentialList, InventoryScriptsList, ParseTypeChange, GenerateForm, inventoryData, LookUpInit, + GroupManageService, GetChoices, GetBasePath, CreateSelect2, GetSourceTypeOptions){ + var generator = GenerateForm, + form = GroupForm(); + + $scope.formCancel = function(){ + $state.go('^'); + }; + $scope.formSave = function(){ + var params, source; + // group fields + var group = { + variables: $scope.variables === ('---' || '{}') ? null : $scope.variables, + name: $scope.name, + description: $scope.description, + inventory: inventoryData.id + }; + if ($scope.source){ + // inventory_source fields + params = { + instance_filters: $scope.instance_filters, + source_vars: $scope[$scope.source.value + '_variables'] === ('---' || '{}') ? null : $scope[$scope.source.value + '_variables'], + source_script: $scope.inventory_script, + source: $scope.source.value, + credential: $scope.credential, + overwrite: $scope.overwrite, + overwrite_vars: $scope.overwrite_vars, + update_on_launch: $scope.update_on_launch, + update_cache_timeout: $scope.update_cache_timeout || 0, + // comma-delimited strings + group_by: _.map($scope.group_by, 'value').join(','), + source_regions: _.map($scope.source_regions, 'value').join(',') + }; + source = $scope.source.value; + } + else{ + source = null; + } + switch(source){ + // no inventory source set, just create a new group + // '' is the value supplied for Manual source type + case null || '': + GroupManageService.post(group).then(res => { + // associate + if ($stateParams.group){ + return GroupManageService.associateGroup(res.data, _.last($stateParams.group)) + .then(() => $state.go('^', null, {reload: true})); + } + else{ + $state.go('^', null, {reload: true}); + } + }); + break; + // create a new group and create/associate an inventory source + // equal to case 'rax' || 'ec2' || 'azure' || 'azure_rm' || 'vmware' || 'foreman' || 'cloudforms' || 'openstack' || 'custom' + default: + GroupManageService.post(group) + // associate to group + .then(res => { + if ($stateParams.group){ + GroupManageService.associateGroup(res.data, _.last($stateParams.group)); + return res; + } + else {return res;} + // pass the original POST response and not the association response + }) + .then(res => GroupManageService.putInventorySource( + // put the received group ID into inventory source payload + // and pass the related endpoint + _.assign(params, {group: res.data.id}), res.data.related.inventory_source)) + .then(res => $state.go('inventoryManage.editGroup', {group_id: res.data.group}, {reload: true})); + break; + } + }; + $scope.sourceChange = function(source){ + source = source.value === 'azure_rm' ? 'azure' : source.value; + if (source === 'custom'){ + LookUpInit({ + scope: $scope, + url: GetBasePath('inventory_script'), + form: form, + list: InventoryScriptsList, + field: 'inventory_script', + input_type: "radio" + }); + } + else if (source === 'ec2'){ + LookUpInit({ + scope: $scope, + url: GetBasePath('credentials') + '?kind=aws', + form: form, + list: CredentialList, + field: 'credential', + input_type: "radio" + }); + } + // equal to case 'rax' || 'azure' || 'azure_rm' || 'vmware' || 'foreman' || 'cloudforms' || 'openstack' || 'custom' + else{ + LookUpInit({ + scope: $scope, + url: GetBasePath('credentials') + (source === '' ? '' : '?kind=' + source), + form: form, + list: CredentialList, + field: 'credential', + input_type: "radio" + }); + } + // reset fields + $scope.group_by_choices = source === 'ec2' ? $scope.ec2_group_by : null; + $scope.source_region_choices = $scope[source + '_regions']; + $scope.cloudCredentialRequired = source !== 'manual' && source !== 'custom' ? true : false; + $scope.group_by = null; + $scope.source_regions = null; + $scope.credential = null; + $scope.credential_name = null; + initRegionSelect(); + }; + var initRegionSelect = function(){ + CreateSelect2({ + element: '#group_source_regions', + multiple: true + }); + CreateSelect2({ + element: '#group_group_by', + multiple: true + }); + }; + var initSourceSelect = function(){ + CreateSelect2({ + element: '#group_source', + multiple: false + }); + }; + var initSources = function(){ + GetChoices({ + scope: $scope, + url: GetBasePath('inventory_sources'), + field: 'source_regions', + variable: 'rax_regions', + choice_name: 'rax_region_choices', + callback: 'choicesReadyGroup' + }); + + GetChoices({ + scope: $scope, + url: GetBasePath('inventory_sources'), + field: 'source_regions', + variable: 'ec2_regions', + choice_name: 'ec2_region_choices', + callback: 'choicesReadyGroup' + }); + + GetChoices({ + scope: $scope, + url: GetBasePath('inventory_sources'), + field: 'source_regions', + variable: 'gce_regions', + choice_name: 'gce_region_choices', + callback: 'choicesReadyGroup' + }); + + GetChoices({ + scope: $scope, + url: GetBasePath('inventory_sources'), + field: 'source_regions', + variable: 'azure_regions', + choice_name: 'azure_region_choices', + callback: 'choicesReadyGroup' + }); + + // Load options for group_by + GetChoices({ + scope: $scope, + url: GetBasePath('inventory_sources'), + field: 'group_by', + variable: 'ec2_group_by', + choice_name: 'ec2_group_by_choices', + callback: 'choicesReadyGroup' + }); + GetSourceTypeOptions({ + scope: $scope, + variable: 'source_type_options', + //callback: 'sourceTypeOptionsReady' this callback is hard-coded into GetSourceTypeOptions(), included for ref + }); + }; + // region / source options callback + $scope.$on('choicesReadyGroup', function(){ + initRegionSelect(); + }); + + $scope.$on('sourceTypeOptionsReady', function(){ + initSourceSelect(); + }); + var init = function(){ + $scope.parseType = 'yaml'; + $scope.variables = '---'; + generator.inject(form, {mode: 'add', related: false, id: 'Inventory-groupManage--panel', scope: $scope}); + ParseTypeChange({ + scope: $scope, + field_id: 'group_variables', + variable: 'variables', + }); + initSources(); + }; + init(); + }]; diff --git a/awx/ui/client/src/inventories/manage/groups/groups-edit.controller.js b/awx/ui/client/src/inventories/manage/groups/groups-edit.controller.js new file mode 100644 index 0000000000..6b60ebfa38 --- /dev/null +++ b/awx/ui/client/src/inventories/manage/groups/groups-edit.controller.js @@ -0,0 +1,288 @@ +/************************************************* + * Copyright (c) 2016 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + + export default + ['$state', '$stateParams', '$scope', 'GroupForm', 'CredentialList', 'inventoryScriptsListObject', 'ToggleNotification', + 'ParseTypeChange', 'GenerateForm', 'LookUpInit', 'RelatedSearchInit', 'RelatedPaginateInit', 'NotificationsListInit', + 'GroupManageService','GetChoices', 'GetBasePath', 'CreateSelect2', 'GetSourceTypeOptions', 'groupData', 'inventorySourceData', + function($state, $stateParams, $scope, GroupForm, CredentialList, InventoryScriptsList, ToggleNotification, + ParseTypeChange, GenerateForm, LookUpInit, RelatedSearchInit, RelatedPaginateInit, NotificationsListInit, + GroupManageService, GetChoices, GetBasePath, CreateSelect2, GetSourceTypeOptions, groupData, inventorySourceData){ + var generator = GenerateForm, + form = GroupForm(); + $scope.formCancel = function(){ + $state.go('^'); + }; + $scope.formSave = function(){ + var params, source; + // group fields + var group = { + variables: $scope.variables === ('---' || '{}') ? null : $scope.variables, + name: $scope.name, + description: $scope.description, + inventory: $scope.inventory, + id: groupData.id + }; + if ($scope.source){ + // inventory_source fields + params = { + group: groupData.id, + source: $scope.source.value, + credential: $scope.credential, + overwrite: $scope.overwrite, + overwrite_vars: $scope.overwrite_vars, + source_script: $scope.inventory_script, + update_on_launch: $scope.update_on_launch, + update_cache_timeout: $scope.update_cache_timeout || 0, + // comma-delimited strings + group_by: _.map($scope.group_by, 'value').join(','), + source_regions: _.map($scope.source_regions, 'value').join(','), + instance_filters: $scope.instance_filters, + source_vars: $scope[$scope.source.value + '_variables'] === '' ? null : $scope[$scope.source.value + '_variables'] + }; + source = $scope.source.value; + } + else{ + source = null; + } + switch(source){ + // no inventory source set, just create a new group + // '' is the value supplied for Manual source type + case null || '': + GroupManageService.put(group).then(() => $state.go('^', null, {reload: true})); + break; + // create a new group and create/associate an inventory source + // equal to case 'rax' || 'ec2' || 'azure' || 'azure_rm' || 'vmware' || 'foreman' || 'cloudforms' || 'openstack' || 'custom' + default: + GroupManageService.put(group) + .then(() => GroupManageService.putInventorySource(params, groupData.related.inventory_source)) + .then(() => $state.go('^', null, {reload: true})); + break; + } + }; + $scope.toggleNotification = function(event, notifier_id, column) { + var notifier = this.notification; + try { + $(event.target).tooltip('hide'); + } + catch(e) { + // ignore + } + ToggleNotification({ + scope: $scope, + url: GetBasePath('inventory_sources'), + id: inventorySourceData.id, + notifier: notifier, + column: column, + callback: 'NotificationRefresh' + }); + }; + $scope.sourceChange = function(source){ + $scope.source = source; + if (source.value === 'custom'){ + LookUpInit({ + scope: $scope, + url: GetBasePath('inventory_script'), + form: form, + list: InventoryScriptsList, + field: 'inventory_script', + input_type: "radio" + }); + } + else if (source.value === 'ec2'){ + LookUpInit({ + scope: $scope, + url: GetBasePath('credentials') + '?kind=aws', + form: form, + list: CredentialList, + field: 'credential', + input_type: "radio" + }); + } + else{ + LookUpInit({ + scope: $scope, + url: GetBasePath('credentials') + (source.value === '' ? '' : '?kind=' + (source.value)), + form: form, + list: CredentialList, + field: 'credential', + input_type: "radio" + }); + } + // reset fields + $scope.source_region_choices = $scope[source + '_regions']; + $scope.cloudCredentialRequired = source !== 'manual' && source !== 'custom' ? true : false; + $scope.group_by = null; + $scope.source_regions = null; + $scope.credential = null; + $scope.credential_name = null; + initRegionSelect(); + }; + var initRegionSelect = function(){ + CreateSelect2({ + element: '#group_source_regions', + multiple: true + }); + CreateSelect2({ + element: '#group_group_by', + multiple: true + }); + }; + var initSourceSelect = function(){ + $scope.source = _.find($scope.source_type_options, {value: inventorySourceData.source}); + CreateSelect2({ + element: '#group_source', + multiple: false + }); + }; + var initRegionData = function(){ + var source = $scope.source.value === 'azure_rm' ? 'azure' : $scope.source.value; + var regions = inventorySourceData.source_regions.split(','); + $scope.source_region_choices = $scope[source + '_regions']; + $scope.source_regions = _.map(regions, (region) => _.find($scope[source+'_regions'], {value: region})); + $scope.group_by_choices = source === 'ec2' ? $scope.ec2_group_by : null; + if (source ==='ec2'){ + var group_by = inventorySourceData.group_by.split(','); + $scope.group_by = _.map(group_by, (item) => _.find($scope.ec2_group_by, {value: item})); + } + initRegionSelect(); + }; + var initSources = function(){ + GetSourceTypeOptions({ + scope: $scope, + variable: 'source_type_options', + //callback: 'sourceTypeOptionsReady' this callback is hard-coded into GetSourceTypeOptions(), included for ref + }); + GetChoices({ + scope: $scope, + url: GetBasePath('inventory_sources'), + field: 'source_regions', + variable: 'rax_regions', + choice_name: 'rax_region_choices', + callback: 'choicesReadyGroup' + }); + GetChoices({ + scope: $scope, + url: GetBasePath('inventory_sources'), + field: 'source_regions', + variable: 'ec2_regions', + choice_name: 'ec2_region_choices', + callback: 'choicesReadyGroup' + }); + GetChoices({ + scope: $scope, + url: GetBasePath('inventory_sources'), + field: 'source_regions', + variable: 'gce_regions', + choice_name: 'gce_region_choices', + callback: 'choicesReadyGroup' + }); + GetChoices({ + scope: $scope, + url: GetBasePath('inventory_sources'), + field: 'source_regions', + variable: 'azure_regions', + choice_name: 'azure_region_choices', + callback: 'choicesReadyGroup' + }); + GetChoices({ + scope: $scope, + url: GetBasePath('inventory_sources'), + field: 'group_by', + variable: 'ec2_group_by', + choice_name: 'ec2_group_by_choices', + callback: 'choicesReadyGroup' + }); + }; + // region / source options callback + $scope.$on('choicesReadyGroup', function(){ + if (angular.isObject($scope.source)){ + initRegionData(); + } + }); + + $scope.$on('sourceTypeOptionsReady', function(){ + initSourceSelect(); + }); + var init = function(){ + // instantiate expected $scope values from inventorySourceData & groupData + var relatedSets = form.relatedSets(groupData.related); + generator.inject(form, {mode: 'edit', related: false, id: 'Inventory-groupManage--panel', scope: $scope}); + _.assign($scope, + {credential: inventorySourceData.credential}, + {overwrite: inventorySourceData.overwrite}, + {overwrite_vars: inventorySourceData.overwrite_vars}, + {update_on_launch: inventorySourceData.update_on_launch}, + {update_cache_timeout: inventorySourceData.update_cache_timeout}, + {instance_filters: inventorySourceData.instance_filters}, + {inventory_script: inventorySourceData.source_script} + ); + if(inventorySourceData.source === ('ec2' || 'openstack' || 'custom' || 'vmware')){ + $scope[inventorySourceData.source + '_variables'] = inventorySourceData.source_vars; + } + if (inventorySourceData.credential){ + GroupManageService.getCredential(inventorySourceData.credential).then(res => $scope.credential_name = res.data.name); + } + $scope = angular.extend($scope, groupData); + $scope.variables = $scope.variables === (null || '') ? '---' : $scope.variables; + $scope.parseType = 'yaml'; + // instantiate lookup fields + if (inventorySourceData.source !== 'custom'){ + LookUpInit({ + scope: $scope, + url: GetBasePath('credentials') + (inventorySourceData.source === '' ? '' : 'kind=' + (inventorySourceData.source)), + form: form, + list: CredentialList, + field: 'credential', + input_type: "radio" + }); + } + else if (inventorySourceData.source === 'ec2'){ + LookUpInit({ + scope: $scope, + url: GetBasePath('credentials') + '?kind=aws', + form: form, + list: CredentialList, + field: 'credential', + input_type: "radio" + }); + } + // equal to case 'rax' || 'azure' || 'azure_rm' || 'vmware' || 'foreman' || 'cloudforms' || 'openstack' || 'custom' + else{ + $scope.inventory_script_name = inventorySourceData.summary_fields.source_script.name; + LookUpInit({ + scope: $scope, + url: GetBasePath('inventory_script'), + form: form, + list: InventoryScriptsList, + field: 'inventory_script', + input_type: "radio" + }); + } + ParseTypeChange({ + scope: $scope, + field_id: 'group_variables', + variable: 'variables', + }); + NotificationsListInit({ + scope: $scope, + url: GetBasePath('inventory_sources'), + id: inventorySourceData.id + }); + RelatedSearchInit({ + scope: $scope, + form: form, + relatedSets: relatedSets + }); + RelatedPaginateInit({ + scope: $scope, + relatedSets: relatedSets + }); + initSources(); + _.forEach(relatedSets, (value, key) => $scope.search(relatedSets[key].iterator)); + }; + init(); + }]; diff --git a/awx/ui/client/src/inventories/manage/manage-groups/manage-groups.partial.html b/awx/ui/client/src/inventories/manage/groups/groups-form.partial.html similarity index 77% rename from awx/ui/client/src/inventories/manage/manage-groups/manage-groups.partial.html rename to awx/ui/client/src/inventories/manage/groups/groups-form.partial.html index e39e0984ac..45ebffb9e3 100644 --- a/awx/ui/client/src/inventories/manage/manage-groups/manage-groups.partial.html +++ b/awx/ui/client/src/inventories/manage/groups/groups-form.partial.html @@ -1,5 +1,4 @@
-
diff --git a/awx/ui/client/src/inventories/manage/groups/groups-list.controller.js b/awx/ui/client/src/inventories/manage/groups/groups-list.controller.js new file mode 100644 index 0000000000..b5969304bc --- /dev/null +++ b/awx/ui/client/src/inventories/manage/groups/groups-list.controller.js @@ -0,0 +1,145 @@ +/************************************************* + * Copyright (c) 2016 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + export default + ['$scope', '$rootScope', '$state', '$stateParams', 'InventoryGroups', 'generateList', 'InventoryUpdate', 'GroupManageService', 'GroupsCancelUpdate', 'ViewUpdateStatus', + 'InventoryManageService', 'groupsUrl', 'SearchInit', 'PaginateInit', 'GetSyncStatusMsg', 'GetHostsStatusMsg', + function($scope, $rootScope, $state, $stateParams, InventoryGroups, generateList, InventoryUpdate, GroupManageService, GroupsCancelUpdate, ViewUpdateStatus, + InventoryManageService, groupsUrl, SearchInit, PaginateInit, GetSyncStatusMsg, GetHostsStatusMsg){ + var list = InventoryGroups, + view = generateList, + pageSize = 20; + $scope.inventory_id = $stateParams.inventory_id; + $scope.groupSelect = function(id){ + var group = $stateParams.group === undefined ? [id] : _($stateParams.group).concat(id).value(); + $state.go('inventoryManage', {inventory_id: $stateParams.inventory_id, group: group}, {reload: true}); + }; + $scope.createGroup = function(){ + $state.go('inventoryManage.addGroup'); + }; + $scope.editGroup = function(id){ + $state.go('inventoryManage.editGroup', {group_id: id}); + }; + $scope.deleteGroup = function(group){ + $scope.toDelete = {}; + angular.extend($scope.toDelete, group); + $('#group-delete-modal').modal('show'); + }; + $scope.confirmDelete = function(){ + switch($scope.deleteOption){ + case 'promote': + GroupManageService.promote($scope.toDelete.id, $stateParams.inventory_id) + .then(() => { + $state.go('inventoryManage', null, {reload: true}); + $('#group-delete-modal').modal('hide'); + }); + break; + case 'delete': + GroupManageService.delete($scope.toDelete.id).then(() => { + $state.go('inventoryManage', null, {reload: true}); + $('#group-delete-modal').modal('hide'); + }); + } + }; + $scope.updateGroup = function(group) { + GroupManageService.getInventorySource({group: group.id}).then(res =>InventoryUpdate({ + scope: $scope, + group_id: group.id, + url: res.data.results[0].related.update, + group_name: group.name, + group_source: res.data.results[0].source + })); + $scope.$emit('WatchUpdateStatus'); // init socket io conneciton and start watching for status updates + $rootScope.$on('JobStatusChange-inventory', (event, data) => { + switch(data.status){ + case 'failed' || 'successful': + $state.reload(); + break; + default: + var status = GetSyncStatusMsg({ + status: data.status, + has_inventory_sources: group.has_inventory_sources, + source: group.source + }); + group.status = data.status; + group.status_class = status.class; + group.status_tooltip = status.tooltip; + group.launch_tooltip = status.launch_tip; + group.launch_class = status.launch_class; + } + }); + }; + $scope.cancelUpdate = function (id) { + GroupsCancelUpdate({ scope: $scope, id: id }); + }; + $scope.viewUpdateStatus = function (id) { + ViewUpdateStatus({ + scope: $scope, + group_id: id + }); + }; + $scope.showFailedHosts = function(x, y, z){ + $state.go('inventoryManage', {failed: true}, {reload: true}); + }; + $scope.scheduleGroup = function(id) { + $state.go('inventoryManage.schedules', {id: id}); + }; + // $scope.$parent governed by InventoryManageController, for unified multiSelect options + $scope.$on('multiSelectList.selectionChanged', (event, selection) => { + $scope.$parent.groupsSelected = selection.length > 0 ? true : false; + $scope.$parent.groupsSelectedItems = selection.selectedItems; + }); + $scope.$on('PostRefresh', () =>{ + $scope.groups.forEach( (group, index) => { + var group_status, hosts_status; + group_status = 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 ) + }); + hosts_status = GetHostsStatusMsg({ + active_failures: group.hosts_with_active_failures, + total_hosts: group.total_hosts, + inventory_id: $scope.inventory_id, + group_id: group.id + }); + _.assign($scope.groups[index], + {status_class: group_status.class}, + {status_tooltip: group_status.tooltip}, + {launch_tooltip: group_status.launch_tip}, + {launch_class: group_status.launch_class}, + {hosts_status_tip: hosts_status.tooltip}, + {hosts_status_class: hosts_status.class}, + {source: group.summary_fields.inventory_source ? group.summary_fields.inventory_source.source : null}, + {status: group.summary_fields.inventory_source ? group.summary_fields.inventory_source.status : null}); + }); + }); + $scope.copyMoveGroup = function(id){ + $state.go('inventoryManage.copyMoveGroup', {group_id: id, groups: $stateParams.groups}); + }; + + var init = function(){ + list.basePath = groupsUrl; + view.inject(list,{ + id: 'groups-list', + $scope: $scope, + mode: 'edit' + }); + SearchInit({ + scope: $scope, + list: list, + url: groupsUrl, + set: 'groups' + }); + PaginateInit({ + scope: $scope, + list: list, + url: groupsUrl, + pageSize: pageSize + }); + $scope.search(list.iterator); + }; + init(); + }]; diff --git a/awx/ui/client/src/inventories/manage/groups/groups-list.partial.html b/awx/ui/client/src/inventories/manage/groups/groups-list.partial.html new file mode 100644 index 0000000000..84f0025183 --- /dev/null +++ b/awx/ui/client/src/inventories/manage/groups/groups-list.partial.html @@ -0,0 +1,73 @@ +
+ \ No newline at end of file diff --git a/awx/ui/client/src/inventories/manage/groups/groups.block.less b/awx/ui/client/src/inventories/manage/groups/groups.block.less new file mode 100644 index 0000000000..67deadef22 --- /dev/null +++ b/awx/ui/client/src/inventories/manage/groups/groups.block.less @@ -0,0 +1,19 @@ +.select2-selection.select2-selection--multiple.Form-dropDown{ + height: auto !important; +} +.GroupDelete-help--container{ + margin-right: auto; +} +.GroupDelete .Modal-header{ + margin-bottom: 0; +} +.GroupDelete .modal-body{ + padding-top: 20px; +} +.Inventory-groupManage{ + // ugly hack to avoid the surface area of changing form generator's default classes + .checkbox-inline{ + display: block; + padding-bottom: 5px; + } +} \ No newline at end of file diff --git a/awx/ui/client/src/inventories/manage/groups/groups.route.js b/awx/ui/client/src/inventories/manage/groups/groups.route.js new file mode 100644 index 0000000000..3f95c9a484 --- /dev/null +++ b/awx/ui/client/src/inventories/manage/groups/groups.route.js @@ -0,0 +1,51 @@ +/************************************************* + * Copyright (c) 2016 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ +import {templateUrl} from '../../../shared/template-url/template-url.factory'; +import addController from './groups-add.controller'; +import editController from './groups-edit.controller'; + +var ManageGroupsEdit = { + name: 'inventoryManage.editGroup', + route: '/edit-group?group_id', + ncyBreadcrumb: { + label: "EDIT {{group.name}}" + }, + data: { + mode: 'edit' + }, + resolve: { + groupData: ['$stateParams', 'GroupManageService', function($stateParams, GroupManageService){ + return GroupManageService.get({id: $stateParams.group_id}).then(res => res.data.results[0]); + }], + inventorySourceData: ['$stateParams', 'GroupManageService', function($stateParams, GroupManageService){ + return GroupManageService.getInventorySource({group: $stateParams.group_id}).then(res => res.data.results[0]); + }] + }, + views: { + 'form@inventoryManage': { + controller: editController, + templateUrl: templateUrl('inventories/manage/groups/groups-form'), + } + } +}; +var ManageGroupsAdd = { + name: 'inventoryManage.addGroup', + route: '/add-group', + // use a query string to break regex search + ncyBreadcrumb: { + label: "ADD GROUP" + }, + data: { + mode: 'add' + }, + views: { + 'form@inventoryManage': { + controller: addController, + templateUrl: templateUrl('inventories/manage/groups/groups-form'), + } + } +}; +export {ManageGroupsAdd, ManageGroupsEdit}; diff --git a/awx/ui/client/src/inventories/manage/groups/groups.service.js b/awx/ui/client/src/inventories/manage/groups/groups.service.js new file mode 100644 index 0000000000..83b5ed224e --- /dev/null +++ b/awx/ui/client/src/inventories/manage/groups/groups.service.js @@ -0,0 +1,113 @@ +export default + ['$rootScope', 'Rest', 'GetBasePath', 'ProcessErrors', 'Wait', function($rootScope, Rest, GetBasePath, ProcessErrors, Wait){ + return { + stringifyParams: function(params){ + return _.reduce(params, (result, value, key) => { + return result + key + '=' + value + '&'; + }, ''); + }, + // cute abstractions via fn.bind() + url: function(){ + return ''; + }, + error: function(data, status) { + ProcessErrors($rootScope, data, status, null, { hdr: 'Error!', + msg: 'Call to ' + this.url + '. GET returned: ' + status }); + }, + success: function(data){ + return data; + }, + // HTTP methods + get: function(params){ + Wait('start'); + this.url = GetBasePath('groups') + '?' + this.stringifyParams(params); + Rest.setUrl(this.url); + return Rest.get() + .success(this.success.bind(this)) + .error(this.error.bind(this)) + .finally(Wait('stop')); + }, + post: function(group){ + Wait('start'); + this.url = GetBasePath('groups'); + Rest.setUrl(this.url); + return Rest.post(group) + .success(this.success.bind(this)) + .error(this.error.bind(this)) + .finally(Wait('stop')); + }, + put: function(group){ + Wait('start'); + this.url = GetBasePath('groups') + group.id; + Rest.setUrl(this.url); + return Rest.put(group) + .success(this.success.bind(this)) + .error(this.error.bind(this)) + .finally(Wait('stop')); + }, + delete: function(id){ + Wait('start'); + this.url = GetBasePath('groups') + id; + Rest.setUrl(this.url); + return Rest.destroy() + .success(this.success.bind(this)) + .error(this.error.bind(this)) + .finally(Wait('stop')); + }, + getCredential: function(id){ + Wait('start'); + this.url = GetBasePath('credentials') + id; + Rest.setUrl(this.url); + return Rest.get() + .success(this.success.bind(this)) + .error(this.error.bind(this)) + .finally(Wait('stop')); + }, + getInventorySource: function(params){ + Wait('start'); + this.url = GetBasePath('inventory_sources') + '?' + this.stringifyParams(params); + Rest.setUrl(this.url); + return Rest.get() + .success(this.success.bind(this)) + .error(this.error.bind(this)) + .finally(Wait('stop')); + }, + putInventorySource: function(params, url){ + Wait('start'); + this.url = url; + Rest.setUrl(this.url); + return Rest.put(params) + .success(this.success.bind(this)) + .error(this.error.bind(this)) + .finally(Wait('stop')); + }, + // these relationship setters could be consolidated, but verbosity makes the operation feel more clear @ controller level + associateGroup: function(group, target){ + Wait('start'); + this.url = GetBasePath('groups') + target + '/children/'; + Rest.setUrl(this.url); + return Rest.post(group) + .success(this.success.bind(this)) + .error(this.error.bind(this)) + .finally(Wait('stop')); + }, + disassociateGroup: function(group, parent){ + Wait('start'); + this.url = GetBasePath('groups') + parent + '/children/'; + Rest.setUrl(this.url); + return Rest.post({id: group, disassociate: 1}) + .success(this.success.bind(this)) + .error(this.error.bind(this)) + .finally(Wait('stop')); + }, + promote: function(group, inventory){ + Wait('start'); + this.url = GetBasePath('inventory') + inventory + '/groups/'; + Rest.setUrl(this.url); + return Rest.post({id: group, disassociate: 1}) + .success(this.success.bind(this)) + .error(this.error.bind(this)) + .finally(Wait('stop')); + } + }; + }]; \ No newline at end of file diff --git a/awx/ui/client/src/inventories/manage/groups/main.js b/awx/ui/client/src/inventories/manage/groups/main.js new file mode 100644 index 0000000000..ffade1862a --- /dev/null +++ b/awx/ui/client/src/inventories/manage/groups/main.js @@ -0,0 +1,14 @@ +/************************************************* + * Copyright (c) 2016 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + +import {ManageGroupsAdd, ManageGroupsEdit} from './groups.route'; + +export default + angular.module('manageGroups', []) + .run(['$stateExtender', function($stateExtender){ + $stateExtender.addState(ManageGroupsAdd); + $stateExtender.addState(ManageGroupsEdit); + }]); diff --git a/awx/ui/client/src/inventories/manage/manage-hosts/manage-hosts-add.controller.js b/awx/ui/client/src/inventories/manage/hosts/hosts-add.controller.js similarity index 66% rename from awx/ui/client/src/inventories/manage/manage-hosts/manage-hosts-add.controller.js rename to awx/ui/client/src/inventories/manage/hosts/hosts-add.controller.js index 620a68f1ac..1eefe86020 100644 --- a/awx/ui/client/src/inventories/manage/manage-hosts/manage-hosts-add.controller.js +++ b/awx/ui/client/src/inventories/manage/hosts/hosts-add.controller.js @@ -5,16 +5,16 @@ *************************************************/ export default - ['$state', '$stateParams', '$scope', 'HostForm', 'ParseTypeChange', 'GenerateForm', 'ManageHostsService', - function($state, $stateParams, $scope, HostForm, ParseTypeChange, GenerateForm, ManageHostsService){ + ['$state', '$stateParams', '$scope', 'HostForm', 'ParseTypeChange', 'GenerateForm', 'HostManageService', + function($state, $stateParams, $scope, HostForm, ParseTypeChange, GenerateForm, HostManageService){ var generator = GenerateForm, form = HostForm; $scope.parseType = 'yaml'; $scope.extraVars = '---'; $scope.formCancel = function(){ - $state.go('^', null, {reload: true}); + $state.go('^'); }; - $scope.toggleEnabled = function(){ + $scope.toggleHostEnabled = function(){ $scope.host.enabled = !$scope.host.enabled; }; $scope.formSave = function(){ @@ -25,8 +25,16 @@ enabled: $scope.host.enabled, inventory: $stateParams.inventory_id }; - ManageHostsService.post(params).then(function(){ - $state.go('^', null, {reload: true}); + HostManageService.post(params).then(function(res){ + // assign the host to current group if not at the root level + if ($stateParams.group){ + HostManageService.associateGroup(res.data, _.last($stateParams.group)).then(function(){ + $state.go('inventoryManage', null, {reload: true}); + }); + } + else{ + $state.go('inventoryManage', null, {reload: true}); + } }); }; var init = function(){ diff --git a/awx/ui/client/src/inventories/manage/manage-hosts/manage-hosts-edit.controller.js b/awx/ui/client/src/inventories/manage/hosts/hosts-edit.controller.js similarity index 88% rename from awx/ui/client/src/inventories/manage/manage-hosts/manage-hosts-edit.controller.js rename to awx/ui/client/src/inventories/manage/hosts/hosts-edit.controller.js index de733124fa..8e764d95d5 100644 --- a/awx/ui/client/src/inventories/manage/manage-hosts/manage-hosts-edit.controller.js +++ b/awx/ui/client/src/inventories/manage/hosts/hosts-edit.controller.js @@ -5,13 +5,13 @@ *************************************************/ export default - ['$state', '$stateParams', '$scope', 'HostForm', 'ParseTypeChange', 'GenerateForm', 'ManageHostsService', 'host', - function($state, $stateParams, $scope, HostForm, ParseTypeChange, GenerateForm, ManageHostsService, host){ + ['$state', '$stateParams', '$scope', 'HostForm', 'ParseTypeChange', 'GenerateForm', 'HostManageService', 'host', + function($state, $stateParams, $scope, HostForm, ParseTypeChange, GenerateForm, HostManageService, host){ var generator = GenerateForm, form = HostForm; $scope.parseType = 'yaml'; $scope.formCancel = function(){ - $state.go('^', null, {reload: true}); + $state.go('^'); }; $scope.toggleHostEnabled = function(){ $scope.host.enabled = !$scope.host.enabled; @@ -24,7 +24,7 @@ description: $scope.description, enabled: $scope.host.enabled }; - ManageHostsService.put(host).then(function(){ + HostManageService.put(host).then(function(){ $state.go('^', null, {reload: true}); }); }; diff --git a/awx/ui/client/src/inventories/manage/manage-hosts/manage-hosts.partial.html b/awx/ui/client/src/inventories/manage/hosts/hosts-form.partial.html similarity index 100% rename from awx/ui/client/src/inventories/manage/manage-hosts/manage-hosts.partial.html rename to awx/ui/client/src/inventories/manage/hosts/hosts-form.partial.html diff --git a/awx/ui/client/src/inventories/manage/hosts/hosts-list.controller.js b/awx/ui/client/src/inventories/manage/hosts/hosts-list.controller.js new file mode 100644 index 0000000000..13540fb489 --- /dev/null +++ b/awx/ui/client/src/inventories/manage/hosts/hosts-list.controller.js @@ -0,0 +1,84 @@ +/************************************************* + * Copyright (c) 2016 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + export default + ['$scope', '$rootScope', '$state', '$stateParams', 'InventoryHosts', 'generateList', 'InventoryManageService', 'HostManageService', + 'hostsUrl', 'SearchInit', 'PaginateInit', 'SetStatus', 'Prompt', 'Wait', 'inventoryData', + function($scope, $rootScope, $state, $stateParams, InventoryHosts, generateList, InventoryManageService, HostManageService, + hostsUrl, SearchInit, PaginateInit, SetStatus, Prompt, Wait, inventoryData){ + var list = InventoryHosts, + view = generateList, + pageSize = 20; + $scope.createHost = function(){ + $state.go('inventoryManage.addHost'); + }; + $scope.editHost = function(id){ + $state.go('inventoryManage.editHost', {host_id: id}); + }; + $scope.deleteHost = function(id, name){ + var body = '
Are you sure you want to permanently delete the host below from the inventory?
' + name + '
'; + var action = function(){ + delete $rootScope.promptActionBtnClass; + Wait('start'); + HostManageService.delete(id).then(() => { + $('#prompt-modal').modal('hide'); + $state.go($state.current.name, null, {reload: true}); + Wait('stop'); + }); + }; + // Prompt depends on having $rootScope.promptActionBtnClass available... + Prompt({ + hdr: 'Delete Host', + body: body, + action: action, + actionText: 'DELETE', + }); + $rootScope.promptActionBtnClass = 'Modal-errorButton'; + }; + $scope.copyMoveHost = function(id){ + $state.go('inventoryManage.copyMoveHost', {host_id: id}); + }; + $scope.systemTracking = function(){ + var hostIds = _.map($scope.$parent.hostsSelectedItems, (host) => host.id); + $state.go('systemTracking', { + inventory: inventoryData, + inventoryId: $stateParams.inventory_id, + hosts: $scope.$parent.hostsSelectedItems, + hostIds: hostIds + }); + }; + // $scope.$parent governed by InventoryManageController, for unified multiSelect options + $scope.$on('multiSelectList.selectionChanged', (event, selection) => { + $scope.$parent.hostsSelected = selection.length > 0 ? true : false; + $scope.$parent.hostsSelectedItems = selection.selectedItems; + $scope.$parent.systemTrackingDisabled = selection.length > 0 && selection.length < 3 ? false : true; + $scope.$parent.systemTrackingTooltip = selection.length === 1 ? "Compare host facts over time" : "Compare hosts' facts"; + }); + $scope.$on('PostRefresh', ()=>{ + _.forEach($scope.hosts, (host) => SetStatus({scope: $scope, host: host})); + }); + var init = function(){ + list.basePath = hostsUrl; + view.inject(list,{ + id: 'hosts-list', + scope: $scope, + mode: 'edit' + }); + SearchInit({ + scope: $scope, + list: list, + url: hostsUrl, + set: 'hosts' + }); + PaginateInit({ + scope: $scope, + list: list, + url: hostsUrl, + pageSize: pageSize + }); + $scope.search(list.iterator); + }; + init(); + }]; \ No newline at end of file diff --git a/awx/ui/client/src/inventories/manage/hosts/hosts.route.js b/awx/ui/client/src/inventories/manage/hosts/hosts.route.js new file mode 100644 index 0000000000..54a9c3f7d0 --- /dev/null +++ b/awx/ui/client/src/inventories/manage/hosts/hosts.route.js @@ -0,0 +1,50 @@ +/************************************************* + * Copyright (c) 2016 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ +import {templateUrl} from '../../../shared/template-url/template-url.factory'; +import addController from './hosts-add.controller'; +import editController from './hosts-edit.controller'; + +var ManageHostsEdit = { + name: 'inventoryManage.editHost', + route: '/edit-host?host_id', + ncyBreadcrumb: { + label: "EDIT {{host.name}}", + }, + data: { + mode: 'edit' + }, + resolve: { + host: ['$stateParams', 'HostManageService', function($stateParams, HostManageService){ + return HostManageService.get({id: $stateParams.host_id}).then(function(res){ + return res.data.results[0]; + }); + }] + }, + views: { + 'form@inventoryManage': { + controller: editController, + templateUrl: templateUrl('inventories/manage/hosts/hosts-form'), + } + } +}; +var ManageHostsAdd = { + name: 'inventoryManage.addHost', + route: '/add-host', + // use a query string to break regex search + ncyBreadcrumb: { + label: "ADD HOST" + }, + data: { + mode: 'add' + }, + views: { + 'form@inventoryManage': { + controller: addController, + templateUrl: templateUrl('inventories/manage/hosts/hosts-form'), + } + } +}; +export {ManageHostsAdd, ManageHostsEdit}; diff --git a/awx/ui/client/src/inventories/manage/hosts/hosts.service.js b/awx/ui/client/src/inventories/manage/hosts/hosts.service.js new file mode 100644 index 0000000000..a85a159c8a --- /dev/null +++ b/awx/ui/client/src/inventories/manage/hosts/hosts.service.js @@ -0,0 +1,84 @@ +/************************************************* + * Copyright (c) 2016 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + + export default + ['$rootScope', 'Rest', 'GetBasePath', 'ProcessErrors', 'Wait', + function($rootScope, Rest, GetBasePath, ProcessErrors, Wait){ + return { + stringifyParams: function(params){ + return _.reduce(params, (result, value, key) => { + return result + key + '=' + value + '&'; + }, ''); + }, + // cute abstractions via fn.bind() + url: function(){ + return ''; + }, + error: function(data, status) { + ProcessErrors($rootScope, data, status, null, { hdr: 'Error!', + msg: 'Call to ' + this.url + '. GET returned: ' + status }); + }, + success: function(data){ + return data; + }, + // HTTP methods + get: function(params){ + Wait('start'); + this.url = GetBasePath('hosts') + '?' + this.stringifyParams(params); + Rest.setUrl(this.url); + return Rest.get() + .success(this.success.bind(this)) + .error(this.error.bind(this)) + .finally(Wait('stop')); + }, + post: function(host){ + Wait('start'); + this.url = GetBasePath('hosts'); + Rest.setUrl(this.url); + return Rest.post(host) + .success(this.success.bind(this)) + .error(this.error.bind(this)) + .finally(Wait('stop')); + }, + put: function(host){ + Wait('start'); + this.url = GetBasePath('hosts') + host.id; + Rest.setUrl(this.url); + return Rest.put(host) + .success(this.success.bind(this)) + .error(this.error.bind(this)) + .finally(Wait('stop')); + }, + delete: function(id){ + Wait('start'); + this.url = GetBasePath('hosts') + id; + Rest.setUrl(this.url); + return Rest.destroy() + .success(this.success.bind(this)) + .error(this.error.bind(this)) + .finally(Wait('stop')); + }, + // these relationship setters could be consolidated, but verbosity makes the operation feel more clear @ controller level + associateGroup: function(host, group){ + Wait('start'); + this.url = GetBasePath('groups') + group + '/hosts/'; + Rest.setUrl(this.url); + return Rest.post(host) + .success(this.success.bind(this)) + .error(this.error.bind(this)) + .finally(Wait('stop')); + }, + disassociateGroup: function(host, group){ + Wait('start'); + this.url = GetBasePath('groups') + group + '/hosts/'; + Rest.setUrl(this.url); + return Rest.post({id: host.id, disassociate: 1}) + .success(this.success.bind(this)) + .error(this.error.bind(this)) + .finally(Wait('stop')); + } + }; + }]; \ No newline at end of file diff --git a/awx/ui/client/src/inventories/manage/manage-hosts/main.js b/awx/ui/client/src/inventories/manage/hosts/main.js similarity index 60% rename from awx/ui/client/src/inventories/manage/manage-hosts/main.js rename to awx/ui/client/src/inventories/manage/hosts/main.js index 90d5cf3e70..8b71ce162a 100644 --- a/awx/ui/client/src/inventories/manage/manage-hosts/main.js +++ b/awx/ui/client/src/inventories/manage/hosts/main.js @@ -4,13 +4,11 @@ * All Rights Reserved *************************************************/ -import {ManageHostsAdd, ManageHostsEdit} from './manage-hosts.route'; -import service from './manage-hosts.service'; +import {ManageHostsAdd, ManageHostsEdit} from './hosts.route'; export default angular.module('manageHosts', []) - .service('ManageHostsService', service) - .run(['$stateExtender', function($stateExtender){ + .run(['$stateExtender', '$state', function($stateExtender, $state){ $stateExtender.addState(ManageHostsAdd); $stateExtender.addState(ManageHostsEdit); }]); diff --git a/awx/ui/client/src/inventories/manage/inventory-manage.block.less b/awx/ui/client/src/inventories/manage/inventory-manage.block.less new file mode 100644 index 0000000000..4bf257dffe --- /dev/null +++ b/awx/ui/client/src/inventories/manage/inventory-manage.block.less @@ -0,0 +1,3 @@ +.InventoryManage-container{ + margin-top: -40px; +} \ No newline at end of file diff --git a/awx/ui/client/src/inventories/manage/inventory-manage.controller.js b/awx/ui/client/src/inventories/manage/inventory-manage.controller.js index fab1b0334e..0588e8ed17 100644 --- a/awx/ui/client/src/inventories/manage/inventory-manage.controller.js +++ b/awx/ui/client/src/inventories/manage/inventory-manage.controller.js @@ -3,532 +3,18 @@ * * All Rights Reserved *************************************************/ - -/** - * @ngdoc function - * @name controllers.function:Inventories - * @description This controller's for the Inventory page - */ - -function InventoriesManage($log, $scope, $rootScope, $location, - $state, $compile, generateList, ClearScope, Empty, Wait, Rest, Alert, - GetBasePath, ProcessErrors, InventoryGroups, - InjectHosts, Find, HostsReload, SearchInit, PaginateInit, GetSyncStatusMsg, - GetHostsStatusMsg, GroupsEdit, InventoryUpdate, GroupsCancelUpdate, - ViewUpdateStatus, GroupsDelete, Store, HostsEdit, HostsDelete, - EditInventoryProperties, ShowJobSummary, - InventoryGroupsHelp, HelpDialog, - GroupsCopy, HostsCopy, $stateParams, ParamPass) { - - var PreviousSearchParams, - url, - hostScope = $scope.$new(); - - ClearScope(); - - // TODO: only display adhoc button if the user has permission to use it. - // TODO: figure out how to get the action-list partial to update so that - // the tooltip can be changed based off things being selected or not. - $scope.adhocButtonTipContents = "Launch adhoc command for the inventory"; - - // watcher for the group list checkbox changes - $scope.$on('multiSelectList.selectionChanged', function(e, selection) { - if (selection.length > 0) { - $scope.groupsSelected = true; - // $scope.adhocButtonTipContents = "Launch adhoc command for the " - // + "selected groups and hosts."; - } else { - $scope.groupsSelected = false; - // $scope.adhocButtonTipContents = "Launch adhoc command for the " - // + "inventory."; - } - $scope.groupsSelectedItems = selection.selectedItems; - }); - - // watcher for the host list checkbox changes - hostScope.$on('multiSelectList.selectionChanged', function(e, selection) { - // you need this so that the event doesn't bubble to the watcher above - // for the host list - e.stopPropagation(); - var trackingButton = angular.element(document.querySelector('.system-tracking')); - trackingButton.html('SYSTEM TRACKING'); - if (selection.length === 0) { - $scope.hostsSelected = false; - } else if (selection.length === 1) { - $scope.systemTrackingTooltip = "Compare host over time"; - $scope.hostsSelected = true; - $scope.systemTrackingDisabled = false; - } else if (selection.length === 2) { - $scope.systemTrackingTooltip = "Compare hosts against each other"; - $scope.hostsSelected = true; - $scope.systemTrackingDisabled = false; - } else { - $scope.hostsSelected = true; - $scope.systemTrackingDisabled = true; - } - $scope.hostsSelectedItems = selection.selectedItems; - }); - - $scope.systemTracking = function() { - var hostIds = _.map($scope.hostsSelectedItems, function(x){ - return x.id; - }); - $state.transitionTo('systemTracking', - { inventory: $scope.inventory, - inventoryId: $scope.inventory.id, - hosts: $scope.hostsSelectedItems, - hostIds: hostIds - }); - }; - - // populates host patterns based on selected hosts/groups - $scope.populateAdhocForm = function() { - var host_patterns = "all"; - if ($scope.hostsSelected || $scope.groupsSelected) { - var allSelectedItems = []; - if ($scope.groupsSelectedItems) { - allSelectedItems = allSelectedItems.concat($scope.groupsSelectedItems); - } - if ($scope.hostsSelectedItems) { - allSelectedItems = allSelectedItems.concat($scope.hostsSelectedItems); - } - if (allSelectedItems) { - host_patterns = _.pluck(allSelectedItems, "name").join(":"); - } - } - $rootScope.hostPatterns = host_patterns; - $state.go('inventoryManage.adhoc'); - }; - - $scope.refreshHostsOnGroupRefresh = false; - $scope.selected_group_id = null; - - Wait('start'); - - - if ($scope.removeHostReloadComplete) { - $scope.removeHostReloadComplete(); - } - $scope.removeHostReloadComplete = $scope.$on('HostReloadComplete', function() { - if ($scope.initial_height) { - var host_height = $('#hosts-container .well').height(), - group_height = $('#group-list-container .well').height(), - new_height; - - if (host_height > group_height) { - new_height = host_height - (host_height - group_height); - } - else if (host_height < group_height) { - new_height = host_height + (group_height - host_height); - } - if (new_height) { - $('#hosts-container .well').height(new_height); - } - $scope.initial_height = null; - } - }); - - if ($scope.removeRowCountReady) { - $scope.removeRowCountReady(); - } - $scope.removeRowCountReady = $scope.$on('RowCountReady', function(e, rows) { - // Add hosts view - $scope.show_failures = false; - InjectHosts({ - group_scope: $scope, - host_scope: hostScope, - inventory_id: $scope.inventory.id, - tree_id: null, - group_id: null, - pageSize: rows - }); - - 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.removeInventoryLoaded) { - $scope.removeInventoryLoaded(); - } - $scope.removeInventoryLoaded = $scope.$on('InventoryLoaded', function() { - var rows; - generateList.inject(InventoryGroups, { - mode: 'edit', - id: 'group-list-container', - searchSize: 'col-lg-6 col-md-6 col-sm-6 col-xs-12', - scope: $scope - }); - - rows = 20; - hostScope.host_page_size = rows; - $scope.group_page_size = rows; - - $scope.show_failures = false; - InjectHosts({ - group_scope: $scope, - host_scope: hostScope, - inventory_id: $scope.inventory.id, - tree_id: null, - group_id: null, - pageSize: rows - }); - - // Load data - 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); - - $scope.$emit('WatchUpdateStatus'); // init socket io conneciton and start watching for status updates - }); - - 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 - }); - } - else { - Wait('stop'); - } - } - }); - - // Load Inventory - url = GetBasePath('inventory') + $stateParams.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: ' + $stateParams.inventory_id + - ' GET returned status: ' + status }); - }); - - // start watching for real-time updates - if ($rootScope.removeWatchUpdateStatus) { - $rootScope.removeWatchUpdateStatus(); - } - $rootScope.removeWatchUpdateStatus = $rootScope.$on('JobStatusChange-inventory', function(e, data) { - var stat, group; - if (data.group_id) { - group = Find({ list: $scope.groups, key: 'id', val: data.group_id }); - if (data.status === "failed" || data.status === "successful") { - if (data.group_id === $scope.selected_group_id || group) { - // job completed, fefresh all groups - $log.debug('Update completed. Refreshing the tree.'); - $scope.refreshGroups(); - } - } - else if (group) { - // incremental update, just update - $log.debug('Status of group: ' + data.group_id + ' 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; - } - } - }); - - // 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); - } - - $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 groups = [], group = Find({ list: $scope.groups, key: 'id', val: id }); - if($state.params.groups){ - groups.push($state.params.groups); - } - groups.push(group.id); - groups = groups.join(); - $state.transitionTo('inventoryManage', {inventory_id: $state.params.inventory_id, groups: groups}, { notify: false}); - loadGroups(group.related.children, group.id); - $scope.selected_group_id = group.id; - HostsReload({ - scope: hostScope, - group_id: $scope.selected_group_id, - inventory_id: $scope.inventory.id, - pageSize: hostScope.host_page_size - }); - }; - - $scope.createGroup = function () { - PreviousSearchParams = Store('group_current_search_params'); - var params = { - scope: $scope, - inventory_id: $scope.inventory.id, - group_id: $scope.selected_group_id, - mode: 'add' - }; - ParamPass.set(params); - $state.go('inventoryManage.addGroup'); - }; - - $scope.editGroup = function (id) { - PreviousSearchParams = Store('group_current_search_params'); - var params = { - scope: $scope, - inventory_id: $scope.inventory.id, - group_id: id, - mode: 'edit' - }; - ParamPass.set(params); - $state.go('inventoryManage.editGroup', {group_id: id}); - }; - - // Launch inventory sync - $scope.updateGroup = function (id) { - var group = Find({ list: $scope.groups, key: 'id', val: id }); - if (group) { - if (Empty(group.source)) { - // 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 + ' Click the button to monitor the status.', 'alert-info', null, null, null, null, true); - } else { - Wait('start'); - Rest.setUrl(group.related.inventory_source); - Rest.get() - .success(function (data) { - InventoryUpdate({ - scope: $scope, - url: data.related.update, - group_name: data.summary_fields.group.name, - group_source: data.source, - group_id: group.id, - }); - }) - .error(function (data, status) { - ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to retrieve inventory source: ' + - group.related.inventory_source + ' GET returned status: ' + status }); - }); - } - } - }; - - $scope.cancelUpdate = function (id) { - GroupsCancelUpdate({ scope: $scope, id: id }); - }; - - $scope.viewUpdateStatus = function (id) { - ViewUpdateStatus({ - scope: $scope, - group_id: id - }); - }; - - $scope.scheduleGroup = function(id) { - $state.go('inventoryManageSchedules', { - inventory_id: $scope.inventory.id, id: id - }); - }; - - $scope.copyGroup = function(id) { - PreviousSearchParams = Store('group_current_search_params'); - var params = { - scope: $scope - }; - ParamPass.set(params); - $location.search('groups', null); - $state.go('inventoryManage.copy.group', {group_id: id}); - }; - - $scope.deleteGroup = function (id) { - GroupsDelete({ - scope: $scope, - group_id: id, - inventory_id: $scope.inventory.id - }); - }; - - $scope.editInventoryProperties = function () { - // EditInventoryProperties({ scope: $scope, inventory_id: $scope.inventory.id }); - $location.path('/inventories/' + $scope.inventory.id + '/'); - }; - - hostScope.createHost = function () { - var params = { - host_scope: hostScope, - group_scope: $scope, - mode: 'add', - host_id: null, - selected_group_id: $scope.selected_group_id, - inventory_id: $scope.inventory.id - }; - ParamPass.set(params); - $state.go('inventoryManage.addHost'); - }; - - hostScope.editHost = function (host_id) { - var params = { - host_scope: hostScope, - group_scope: $scope, - mode: 'edit', - host_id: host_id, - inventory_id: $scope.inventory.id - }; - ParamPass.set(params); - $state.go('inventoryManage.editHost', {host_id: host_id}); - }; - - 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'); - var params = { - group_scope: $scope, - host_scope: hostScope, - host_id: id - }; - - ParamPass.set(params); - - $state.go('inventoryManage.copy.host', {host_id: id}); - }; - - hostScope.showJobSummary = function (job_id) { - ShowJobSummary({ - job_id: job_id - }); - }; - - $scope.showGroupHelp = function (params) { - var opts = { - defn: InventoryGroupsHelp - }; - if (params) { - opts.autoShow = params.autoShow || false; - } - HelpDialog(opts); - } -; - $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(); - } - ); -} - -export default [ - '$log', '$scope', '$rootScope', '$location', - '$state', '$compile', 'generateList', 'ClearScope', 'Empty', 'Wait', - 'Rest', 'Alert', 'GetBasePath', 'ProcessErrors', - 'InventoryGroups', 'InjectHosts', 'Find', 'HostsReload', - 'SearchInit', 'PaginateInit', 'GetSyncStatusMsg', 'GetHostsStatusMsg', - 'GroupsEdit', 'InventoryUpdate', 'GroupsCancelUpdate', 'ViewUpdateStatus', - 'GroupsDelete', 'Store', 'HostsEdit', 'HostsDelete', - 'EditInventoryProperties', 'ShowJobSummary', 'InventoryGroupsHelp', 'HelpDialog', 'GroupsCopy', - 'HostsCopy', '$stateParams', 'ParamPass', InventoriesManage -]; + export default + ['$scope', '$state', function($scope, $state){ + $scope.groupsSelected = false; + $scope.hostsSelected = false; + $scope.hostsSelectedItems = []; + $scope.groupsSelectedItems = []; + $scope.setAdhocPattern = function(){ + var pattern = _($scope.groupsSelectedItems) + .concat($scope.hostsSelectedItems) + .map(function(item){ + return item.name; + }).value().join(':'); + $state.go('inventoryManage.adhoc', {pattern: pattern}); + }; + }]; \ No newline at end of file diff --git a/awx/ui/client/src/inventories/manage/inventory-manage.partial.html b/awx/ui/client/src/inventories/manage/inventory-manage.partial.html index 7daf685f3f..41e8084a42 100644 --- a/awx/ui/client/src/inventories/manage/inventory-manage.partial.html +++ b/awx/ui/client/src/inventories/manage/inventory-manage.partial.html @@ -1,152 +1,10 @@ -
-
+
+
-
-
-
-
-
-
+
+
- - - - - - - - - - -
-
-
diff --git a/awx/ui/client/src/inventories/manage/inventory-manage.route.js b/awx/ui/client/src/inventories/manage/inventory-manage.route.js index 674297bb0a..317f584930 100644 --- a/awx/ui/client/src/inventories/manage/inventory-manage.route.js +++ b/awx/ui/client/src/inventories/manage/inventory-manage.route.js @@ -6,18 +6,71 @@ import {templateUrl} from '../../shared/template-url/template-url.factory'; import InventoriesManage from './inventory-manage.controller'; +import BreadcrumbsController from './breadcrumbs/breadcrumbs.controller'; +import HostsListController from './hosts/hosts-list.controller'; +import GroupsListController from './groups/groups-list.controller'; export default { name: 'inventoryManage', - url: '/inventories/:inventory_id/manage?groups', - templateUrl: templateUrl('inventories/manage/inventory-manage'), - controller: InventoriesManage, + url: '/inventories/:inventory_id/manage?{group:int}{failed}', data: { activityStream: true, activityStreamTarget: 'inventory', activityStreamId: 'inventory_id' }, + params:{ + group:{ + array: true + }, + failed:{ + value: 'false', + squash: true + } + }, ncyBreadcrumb: { - label: "INVENTORY MANAGE" + skip: true // Never display this state in ncy-breadcrumb. + }, + // enforce uniqueness in group param + onEnter: function($stateParams){ + $stateParams.group = _.uniq($stateParams.group); + }, + resolve: { + groupsUrl: ['InventoryManageService', '$stateParams', function(InventoryManageService, $stateParams){ + return !$stateParams.group ? + InventoryManageService.rootGroupsUrl($stateParams.inventory_id) : + InventoryManageService.childGroupsUrl(_.last($stateParams.group)); + }], + hostsUrl: ['InventoryManageService', '$stateParams', function(InventoryManageService, $stateParams){ + // at the root group level + return !$stateParams.group ? + InventoryManageService.rootHostsUrl($stateParams.inventory_id, $stateParams.failed) : + InventoryManageService.childHostsUrl(_.last($stateParams.group, $stateParams.failed)); + }], + inventoryData: ['InventoryManageService', '$stateParams', function(InventoryManageService, $stateParams){ + return InventoryManageService.getInventory($stateParams.inventory_id).then(res => res.data); + }], + breadCrumbData: ['InventoryManageService', '$stateParams', function(InventoryManageService, $stateParams){ + return ( (!$stateParams.group) ? false : InventoryManageService.getBreadcrumbs($stateParams.group).then(res => res.data.results)); + }] + }, + views:{ + // target the ui-view with name "groupBreadcrumbs" at the root template level + 'groupBreadcrumbs@': { + controller: BreadcrumbsController, + templateUrl: templateUrl('inventories/manage/breadcrumbs/breadcrumbs') + }, + '': { + templateUrl: templateUrl('inventories/manage/inventory-manage'), + controller: InventoriesManage + }, + // target ui-views with name@inventoryManage template level + 'groupsList@inventoryManage': { + templateUrl: templateUrl('inventories/manage/groups/groups-list'), + controller: GroupsListController + }, + 'hostsList@inventoryManage': { + template: '
', + controller: HostsListController + } } }; diff --git a/awx/ui/client/src/inventories/manage/inventory-manage.service.js b/awx/ui/client/src/inventories/manage/inventory-manage.service.js new file mode 100644 index 0000000000..d61d27e758 --- /dev/null +++ b/awx/ui/client/src/inventories/manage/inventory-manage.service.js @@ -0,0 +1,63 @@ +/************************************************* + * Copyright (c) 2016 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + + export default + ['$rootScope', 'Rest', 'GetBasePath', 'ProcessErrors', 'Wait', + function($rootScope, Rest, GetBasePath, ProcessErrors, Wait){ + return { + // cute abstractions via fn.bind() + url: function(){ + return ''; + }, + error: function(data, status) { + ProcessErrors($rootScope, data, status, null, { hdr: 'Error!', + msg: 'Call to ' + this.url + '. GET returned: ' + status }); + }, + success: function(data){ + return data; + }, + // data getters + getInventory: function(id){ + Wait('start'); + this.url = GetBasePath('inventory') + id; + Rest.setUrl(this.url); + return Rest.get() + .success(this.success.bind(this)) + .error(this.error.bind(this)) + .finally(Wait('stop')); + }, + getBreadcrumbs: function(groups){ + Wait('start'); + this.url = GetBasePath('groups') + '?' + _.map(groups, function(item){ + return '&or__id=' + item; + }).join(''); + Rest.setUrl(this.url); + return Rest.get() + .success(this.success.bind(this)) + .error(this.error.bind(this)); + }, + // these methods generate a query string to pass to PaginateInit(), SearchInit() + // always supply trailing slashes and ? prefix + rootHostsUrl: function(id, failed){ + var url = GetBasePath('inventory') + id + '/hosts' + + (failed === 'true' ? '?has_active_failures=true' : '?'); + return url; + }, + childHostsUrl: function(id, failed){ + var url = GetBasePath('groups') + id + '/hosts' + + (failed === 'true' ? '?has_active_failures=true' : '?'); + return url; + }, + childGroupsUrl: function(id){ + var url = GetBasePath('groups') + id + '/children?'; + return url; + }, + rootGroupsUrl: function(id){ + var url = GetBasePath('inventory') + id+ '/root_groups/'; + return url; + } + }; + }]; diff --git a/awx/ui/client/src/inventories/manage/main.js b/awx/ui/client/src/inventories/manage/main.js index 435db696f7..df2b14469e 100644 --- a/awx/ui/client/src/inventories/manage/main.js +++ b/awx/ui/client/src/inventories/manage/main.js @@ -1,21 +1,28 @@ /************************************************* - * Copyright (c) 2015 Ansible, Inc. + * Copyright (c) 2016 Ansible, Inc. * * All Rights Reserved *************************************************/ import route from './inventory-manage.route'; - -import manageHosts from './manage-hosts/main'; -import manageGroups from './manage-groups/main'; -import copy from './copy/main'; +import InventoryManageService from './inventory-manage.service'; +import HostManageService from './hosts/hosts.service'; +import GroupManageService from './groups/groups.service'; +import hosts from './hosts/main'; +import groups from './groups/main'; +import adhoc from './adhoc/main'; +import copyMove from './copy-move/main'; export default angular.module('inventoryManage', [ - manageHosts.name, - manageGroups.name, - copy.name, + hosts.name, + groups.name, + copyMove.name, + adhoc.name ]) + .service('InventoryManageService', InventoryManageService) + .service('HostManageService', HostManageService) + .service('GroupManageService', GroupManageService) .run(['$stateExtender', function($stateExtender) { $stateExtender.addState(route); }]); diff --git a/awx/ui/client/src/inventories/manage/manage-groups/directive/manage-groups.directive.controller.js b/awx/ui/client/src/inventories/manage/manage-groups/directive/manage-groups.directive.controller.js deleted file mode 100644 index 6bad2c274a..0000000000 --- a/awx/ui/client/src/inventories/manage/manage-groups/directive/manage-groups.directive.controller.js +++ /dev/null @@ -1,574 +0,0 @@ -/************************************************* - * Copyright (c) 2016 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ - -function manageGroupsDirectiveController($filter, $location, $log, - $stateParams, $compile, $state, $scope, Rest, Alert, GroupForm, - GenerateForm, Prompt, ProcessErrors, GetBasePath, SetNodeName, - ParseTypeChange, GetSourceTypeOptions, InventoryUpdate, LookUpInit, Empty, - Wait, GetChoices, UpdateGroup, SourceChange, Find, ParseVariableString, - ToJSON, GroupsScheduleListInit, SetSchedulesInnerDialogSize, - CreateSelect2, ToggleNotification, NotificationsListInit, - RelatedSearchInit, RelatedPaginateInit) { - - var vm = this; - - var group_id = $stateParams.group_id, - mode = $state.current.data.mode, - inventory_id = $stateParams.inventory_id, - generator = GenerateForm, - group_created = false, - defaultUrl, - master = {}, - form = GroupForm(), - relatedSets = {}, - choicesReady, group; - - if (mode === 'edit') { - defaultUrl = GetBasePath('groups') + group_id + '/'; - } else { - defaultUrl = (group_id !== undefined) ? GetBasePath('groups') + group_id + '/children/' : - GetBasePath('inventory') + inventory_id + '/groups/'; - } - - Rest.setUrl(defaultUrl); - Rest.get() - .success(function(data) { - group = data; - for (var fld in form.fields) { - if (data[fld]) { - $scope[fld] = data[fld]; - master[fld] = $scope[fld]; - } - } - if(mode === 'edit') { - $scope.variable_url = data.related.variable_data; - $scope.source_url = data.related.inventory_source; - $scope.source_id = data.related.inventory_source.split('/')[4]; - $scope.$emit('LoadSourceData'); - } - }) - .error(function(data, status) { - ProcessErrors($scope, data, status, { - hdr: 'Error!', - msg: 'Failed to retrieve group: ' + defaultUrl + '. GET status: ' + status - }); - }); - - - $scope.parseType = 'yaml'; - - generator.inject(form, { - mode: mode, - id: 'group-manage-panel', - scope: $scope, - related: false, - cancelButton: false - }); - - generator.reset(); - - GetSourceTypeOptions({ - scope: $scope, - variable: 'source_type_options' - }); - - $scope.source = form.fields.source['default']; - $scope.sourcePathRequired = false; - $scope[form.fields.source_vars.parseTypeName] = 'yaml'; - $scope.update_cache_timeout = 0; - $scope.parseType = 'yaml'; - - - function initSourceChange() { - $scope.showSchedulesTab = (mode === 'edit' && $scope.source && $scope.source.value !== "manual") ? true : false; - SourceChange({ - scope: $scope, - form: form - }); - } - - // JT -- this gets called after the properties & properties variables are loaded, and is emitted from (groupLoaded) - if ($scope.removeLoadSourceData) { - $scope.removeLoadSourceData(); - } - $scope.removeLoadSourceData = $scope.$on('LoadSourceData', function() { - ParseTypeChange({ - scope: $scope, - variable: 'variables', - parse_variable: 'parseType', - field_id: 'group_variables' - }); - - NotificationsListInit({ - scope: $scope, - url: GetBasePath('inventory_sources'), - id: $scope.source_id - }); - - if ($scope.source_url) { - // get source data - Rest.setUrl($scope.source_url); - Rest.get() - .success(function(data) { - var fld, i, j, flag, found, set, opts, list; - for (fld in form.fields) { - if (fld === 'checkbox_group') { - for (i = 0; i < form.fields[fld].fields.length; i++) { - flag = form.fields[fld].fields[i]; - if (data[flag.name] !== undefined) { - $scope[flag.name] = data[flag.name]; - master[flag.name] = $scope[flag.name]; - } - } - } - if (fld === 'source') { - found = false; - data.source = (data.source === "") ? "manual" : data.source; - for (i = 0; i < $scope.source_type_options.length; i++) { - if ($scope.source_type_options[i].value === data.source) { - $scope.source = $scope.source_type_options[i]; - found = true; - } - } - if (!found || $scope.source.value === "manual") { - $scope.groupUpdateHide = true; - } else { - $scope.groupUpdateHide = false; - } - master.source = $scope.source; - } else if (fld === 'source_vars') { - // Parse source_vars, converting to YAML. - $scope.source_vars = ParseVariableString(data.source_vars); - master.source_vars = $scope.variables; - } else if (fld === "inventory_script") { - // the API stores it as 'source_script', we call it inventory_script - data.summary_fields.inventory_script = data.summary_fields.source_script; - $scope.inventory_script = data.source_script; - master.inventory_script = $scope.inventory_script; - } else if (fld === "source_regions") { - if (data[fld] === "") { - $scope[fld] = data[fld]; - master[fld] = $scope[fld]; - } else { - $scope[fld] = data[fld].split(","); - master[fld] = $scope[fld]; - } - } else if (data[fld] !== undefined && - fld !== "description" && - fld !== "name" && - fld !== "variables") { - $scope[fld] = data[fld]; - master[fld] = $scope[fld]; - } - - if (form.fields[fld].sourceModel && data.summary_fields && - data.summary_fields[form.fields[fld].sourceModel]) { - $scope[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField] = - data.summary_fields[form.fields[fld].sourceModel][form.fields[fld].sourceField]; - master[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField] = - data.summary_fields[form.fields[fld].sourceModel][form.fields[fld].sourceField]; - } - } - relatedSets = form.relatedSets(data.related); - RelatedSearchInit({ - scope: $scope, - form: form, - relatedSets: relatedSets - }); - - RelatedPaginateInit({ - scope: $scope, - relatedSets: relatedSets - }); - initSourceChange(); - - if (data.source_regions) { - if (data.source === 'ec2' || - data.source === 'rax' || - data.source === 'gce' || - data.source === 'azure') { - if (data.source === 'ec2') { - set = $scope.ec2_regions; - } else if (data.source === 'rax') { - set = $scope.rax_regions; - } else if (data.source === 'gce') { - set = $scope.gce_regions; - } else if (data.source === 'azure') { - set = $scope.azure_regions; - } - opts = []; - list = data.source_regions.split(','); - for (i = 0; i < list.length; i++) { - for (j = 0; j < set.length; j++) { - if (list[i] === set[j].value) { - opts.push({ - id: set [j].value, - text: set [j].label - }); - } - } - } - master.source_regions = opts; - CreateSelect2({ - element: "group_source_regions", - multiple: true, - opts: opts - }); - - } - } else { - // If empty, default to all - master.source_regions = [{ - id: 'all', - text: 'All' - }]; - } - if (data.group_by && data.source === 'ec2') { - set = $scope.ec2_group_by; - opts = []; - list = data.group_by.split(','); - for (i = 0; i < list.length; i++) { - for (j = 0; j < set.length; j++) { - if (list[i] === set[j].value) { - opts.push({ - id: set [j].value, - text: set [j].label - }); - } - } - } - master.group_by = opts; - CreateSelect2({ - element: "#group_group_by", - multiple: true, - opts: opts - }); - } - - $scope.group_update_url = data.related.update; - for (set in relatedSets) { - $scope.search(relatedSets[set].iterator); - } - }) - .error(function(data, status) { - $scope.source = ""; - ProcessErrors($scope, data, status, null, { - hdr: 'Error!', - msg: 'Failed to retrieve inventory source. GET status: ' + status - }); - }); - } - }); - - if ($scope.remove$scopeSourceTypeOptionsReady) { - $scope.remove$scopeSourceTypeOptionsReady(); - } - $scope.remove$scopeSourceTypeOptionsReady = $scope.$on('sourceTypeOptionsReady', function() { - if (mode === 'add') { - $scope.source = Find({ - list: $scope.source_type_options, - key: 'value', - val: '' - }); - $scope.showSchedulesTab = false; - } - }); - - choicesReady = 0; - - if ($scope.removeChoicesReady) { - $scope.removeChoicesReady(); - } - $scope.removeChoicesReady = $scope.$on('choicesReadyGroup', function() { - CreateSelect2({ - element: '#group_source', - multiple: false - }); - $scope.$emit('LoadSourceData'); - - choicesReady++; - if (choicesReady === 5) { - if (mode !== 'edit') { - $scope.variables = "---"; - master.variables = $scope.variables; - } - } - }); - - // Load options for source regions - GetChoices({ - scope: $scope, - url: GetBasePath('inventory_sources'), - field: 'source_regions', - variable: 'rax_regions', - choice_name: 'rax_region_choices', - callback: 'choicesReadyGroup' - }); - - GetChoices({ - scope: $scope, - url: GetBasePath('inventory_sources'), - field: 'source_regions', - variable: 'ec2_regions', - choice_name: 'ec2_region_choices', - callback: 'choicesReadyGroup' - }); - - GetChoices({ - scope: $scope, - url: GetBasePath('inventory_sources'), - field: 'source_regions', - variable: 'gce_regions', - choice_name: 'gce_region_choices', - callback: 'choicesReadyGroup' - }); - - GetChoices({ - scope: $scope, - url: GetBasePath('inventory_sources'), - field: 'source_regions', - variable: 'azure_regions', - choice_name: 'azure_region_choices', - callback: 'choicesReadyGroup' - }); - - // Load options for group_by - GetChoices({ - scope: $scope, - url: GetBasePath('inventory_sources'), - field: 'group_by', - variable: 'ec2_group_by', - choice_name: 'ec2_group_by_choices', - callback: 'choicesReadyGroup' - }); - - //Wait('start'); - - if ($scope.removeAddTreeRefreshed) { - $scope.removeAddTreeRefreshed(); - } - $scope.removeAddTreeRefreshed = $scope.$on('GroupTreeRefreshed', function() { - // Clean up - Wait('stop'); - - if ($scope.searchCleanUp) { - $scope.searchCleanup(); - } - try { - //$('#group-modal-dialog').dialog('close'); - } catch (e) { - // ignore - } - }); - - if ($scope.removeSaveComplete) { - $scope.removeSaveComplete(); - } - $scope.removeSaveComplete = $scope.$on('SaveComplete', function(e, error) { - if (!error) { - $scope.formCancel(); - } - }); - - if ($scope.removeFormSaveSuccess) { - $scope.removeFormSaveSuccess(); - } - $scope.removeFormSaveSuccess = $scope.$on('formSaveSuccess', function() { - - // Source data gets stored separately from the group. Validate and store Source - // related fields, then call SaveComplete to wrap things up. - - var parseError = false, - regions, r, i, - group_by, - data = { - group: group_id, - source: (($scope.source && $scope.source.value !== 'manual') ? $scope.source.value : ''), - source_path: $scope.source_path, - credential: $scope.credential, - overwrite: $scope.overwrite, - overwrite_vars: $scope.overwrite_vars, - source_script: $scope.inventory_script, - update_on_launch: $scope.update_on_launch, - update_cache_timeout: ($scope.update_cache_timeout || 0) - }; - - // Create a string out of selected list of regions - if ($scope.source_regions) { - regions = $('#group_source_regions').select2("data"); - r = []; - for (i = 0; i < regions.length; i++) { - r.push(regions[i].id); - } - data.source_regions = r.join(); - } - - if ($scope.source && ($scope.source.value === 'ec2')) { - data.instance_filters = $scope.instance_filters; - // Create a string out of selected list of regions - group_by = $('#group_group_by').select2("data"); - r = []; - for (i = 0; i < group_by.length; i++) { - r.push(group_by[i].id); - } - data.group_by = r.join(); - } - - if ($scope.source && ($scope.source.value === 'ec2')) { - // for ec2, validate variable data - data.source_vars = ToJSON($scope.envParseType, $scope.source_vars, true); - } - - if ($scope.source && ($scope.source.value === 'custom')) { - data.source_vars = ToJSON($scope.envParseType, $scope.extra_vars, true); - } - - if ($scope.source && ($scope.source.value === 'vmware' || - $scope.source.value === 'openstack')) { - data.source_vars = ToJSON($scope.envParseType, $scope.inventory_variables, true); - } - - // the API doesn't expect the credential to be passed with a custom inv script - if ($scope.source && $scope.source.value === 'custom') { - delete(data.credential); - } - - if (!parseError) { - Rest.setUrl($scope.source_url); - Rest.put(data) - .success(function() { - $scope.$emit('SaveComplete', false); - }) - .error(function(data, status) { - $('#group_tabs a:eq(1)').tab('show'); - ProcessErrors($scope, data, status, form, { - hdr: 'Error!', - msg: 'Failed to update group inventory source. PUT status: ' + status - }); - }); - } - }); - $scope.toggleNotification = function(event, notifier_id, column) { - var notifier = this.notification; - try { - $(event.target).tooltip('hide'); - } - catch(e) { - // ignore - } - ToggleNotification({ - scope: $scope, - url: GetBasePath('inventory_sources'), - id: $scope.source_id, - notifier: notifier, - column: column, - callback: 'NotificationRefresh' - }); - }; - // Cancel - $scope.formCancel = function() { - Wait('stop'); - $state.go('inventoryManage', {}, {reload: true}); - }; - - // Save - $scope.saveGroup = function() { - Wait('start'); - var fld, data, json_data; - - try { - - json_data = ToJSON($scope.parseType, $scope.variables, true); - - data = {}; - for (fld in form.fields) { - data[fld] = $scope[fld]; - } - - data.inventory = inventory_id; - - Rest.setUrl(defaultUrl); - if (mode === 'edit' || (mode === 'add' && group_created)) { - Rest.put(data) - .success(function() { - $scope.$emit('formSaveSuccess'); - }) - .error(function(data, status) { - $('#group_tabs a:eq(0)').tab('show'); - ProcessErrors($scope, data, status, form, { - hdr: 'Error!', - msg: 'Failed to update group: ' + group_id + '. PUT status: ' + status - }); - }); - } else { - Rest.post(data) - .success(function(data) { - group_created = true; - group_id = data.id; - $scope.source_url = data.related.inventory_source; - $scope.source_id = $scope.source_url.split('/')[4]; - $scope.$emit('formSaveSuccess'); - }) - .error(function(data, status) { - $('#group_tabs a:eq(0)').tab('show'); - ProcessErrors($scope, data, status, form, { - hdr: 'Error!', - msg: 'Failed to create group: ' + group_id + '. POST status: ' + status - }); - }); - } - } catch (e) { - // ignore. ToJSON will have already alerted the user - } - }; - - // Start the update process - $scope.updateGroup = function() { - if ($scope.source === "manual" || $scope.source === null) { - Alert('Missing Configuration', 'The selected group is not configured for updates. You must first edit the group, provide Source settings, ' + - 'and then run an update.', 'alert-info'); - } else if ($scope.status === 'updating') { - Alert('Update in Progress', 'The inventory update process is currently running for group ' + - $filter('sanitize')($scope.summary_fields.group.name) + '. Use the Refresh button to monitor the status.', 'alert-info', null, null, null, null, true); - } else { - InventoryUpdate({ - scope: $scope, - group_id: group_id, - url: $scope.group_update_url, - group_name: $scope.name, - group_source: $scope.source.value - }); - } - }; - - // Change the lookup and regions when the source changes - $scope.sourceChange = function() { - $scope.credential_name = ""; - $scope.credential = ""; - if ($scope.credential_name_api_error) { - delete $scope.credential_name_api_error; - } - initSourceChange(); - }; - - - angular.extend(vm, { - formCancel : $scope.formCancel, - saveGroup: $scope.saveGroup - }); -} - -export default ['$filter', '$location', '$log', '$stateParams', - '$compile', '$state', '$scope', 'Rest', 'Alert', 'GroupForm', - 'GenerateForm','Prompt', 'ProcessErrors', 'GetBasePath', 'SetNodeName', - 'ParseTypeChange', 'GetSourceTypeOptions', 'InventoryUpdate', 'LookUpInit', - 'Empty', 'Wait', 'GetChoices', 'UpdateGroup', 'SourceChange', 'Find', - 'ParseVariableString', 'ToJSON', 'GroupsScheduleListInit', - 'SetSchedulesInnerDialogSize', 'CreateSelect2', - 'ToggleNotification', 'NotificationsListInit', 'RelatedSearchInit', - 'RelatedPaginateInit', - manageGroupsDirectiveController -]; diff --git a/awx/ui/client/src/inventories/manage/manage-groups/directive/manage-groups.directive.js b/awx/ui/client/src/inventories/manage/manage-groups/directive/manage-groups.directive.js deleted file mode 100644 index b4c3fbff73..0000000000 --- a/awx/ui/client/src/inventories/manage/manage-groups/directive/manage-groups.directive.js +++ /dev/null @@ -1,25 +0,0 @@ -/************************************************* - * Copyright (c) 2015 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ - -/* jshint unused: vars */ -import manageGroupsDirectiveController from './manage-groups.directive.controller'; - -export default ['templateUrl', 'ParamPass', - function(templateUrl, ParamPass) { - return { - restrict: 'EA', - scope: true, - replace: true, - templateUrl: templateUrl('inventories/manage/manage-groups/directive/manage-groups.directive'), - link: function(scope, element, attrs) { - - }, - controller: manageGroupsDirectiveController, - controllerAs: 'vm', - bindToController: true - }; - } -]; diff --git a/awx/ui/client/src/inventories/manage/manage-groups/directive/manage-groups.directive.partial.html b/awx/ui/client/src/inventories/manage/manage-groups/directive/manage-groups.directive.partial.html deleted file mode 100644 index 889e561114..0000000000 --- a/awx/ui/client/src/inventories/manage/manage-groups/directive/manage-groups.directive.partial.html +++ /dev/null @@ -1,5 +0,0 @@ -
-
-
-
-
diff --git a/awx/ui/client/src/inventories/manage/manage-groups/main.js b/awx/ui/client/src/inventories/manage/manage-groups/main.js deleted file mode 100644 index b015b64628..0000000000 --- a/awx/ui/client/src/inventories/manage/manage-groups/main.js +++ /dev/null @@ -1,16 +0,0 @@ -/************************************************* - * Copyright (c) 2015 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ - -import route from './manage-groups.route'; -import manageGroupsDirective from './directive/manage-groups.directive'; - -export default - angular.module('manage-groups', []) - .directive('manageGroups', manageGroupsDirective) - .run(['$stateExtender', function($stateExtender) { - $stateExtender.addState(route.edit); - $stateExtender.addState(route.add); - }]); diff --git a/awx/ui/client/src/inventories/manage/manage-groups/manage-groups.route.js b/awx/ui/client/src/inventories/manage/manage-groups/manage-groups.route.js deleted file mode 100644 index 8fb8b16952..0000000000 --- a/awx/ui/client/src/inventories/manage/manage-groups/manage-groups.route.js +++ /dev/null @@ -1,36 +0,0 @@ -/************************************************* - * Copyright (c) 2016 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ -import { - templateUrl -} from '../../../shared/template-url/template-url.factory'; - -export default { - edit: { - name: 'inventoryManage.editGroup', - route: '/:group_id/editGroup', - templateUrl: templateUrl('inventories/manage/manage-groups/manage-groups'), - data: { - group_id: 'group_id', - mode: 'edit' - }, - ncyBreadcrumb: { - label: "INVENTORY EDIT GROUPS" - } - }, - - add: { - name: 'inventoryManage.addGroup', - route: '/addGroup', - templateUrl: templateUrl('inventories/manage/manage-groups/manage-groups'), - ncyBreadcrumb: { - label: "INVENTORY ADD GROUP" - }, - data: { - mode: 'add' - } - }, - -}; diff --git a/awx/ui/client/src/inventories/manage/manage-hosts/manage-hosts.route.js b/awx/ui/client/src/inventories/manage/manage-hosts/manage-hosts.route.js deleted file mode 100644 index bf349a9e1c..0000000000 --- a/awx/ui/client/src/inventories/manage/manage-hosts/manage-hosts.route.js +++ /dev/null @@ -1,41 +0,0 @@ -/************************************************* - * Copyright (c) 2016 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ -import {templateUrl} from '../../../shared/template-url/template-url.factory'; -import addController from './manage-hosts-add.controller'; -import editController from './manage-hosts-edit.controller'; - -var ManageHostsEdit = { - name: 'inventoryManage.editHost', - route: '/host/:host_id', - controller: editController, - templateUrl: templateUrl('inventories/manage/manage-hosts/manage-hosts'), - ncyBreadcrumb: { - label: "INVENTORY EDIT HOSTS" - }, - data: { - mode: 'edit' - }, - resolve: { - host: ['$stateParams', 'ManageHostsService', function($stateParams, ManageHostsService){ - return ManageHostsService.get({id: $stateParams.host_id}).then(function(res){ - return res.data.results[0]; - }); - }] - } -}; -var ManageHostsAdd = { - name: 'inventoryManage.addHost', - route: '/host/add', - controller: addController, - templateUrl: templateUrl('inventories/manage/manage-hosts/manage-hosts'), - ncyBreadcrumb: { - label: "INVENTORY ADD HOST" - }, - data: { - mode: 'add' - } -}; -export {ManageHostsAdd, ManageHostsEdit}; diff --git a/awx/ui/client/src/inventories/manage/manage-hosts/manage-hosts.service.js b/awx/ui/client/src/inventories/manage/manage-hosts/manage-hosts.service.js deleted file mode 100644 index c7a64cb60d..0000000000 --- a/awx/ui/client/src/inventories/manage/manage-hosts/manage-hosts.service.js +++ /dev/null @@ -1,53 +0,0 @@ -/************************************************* - * Copyright (c) 2016 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ - - export default - ['$rootScope', 'Rest', 'GetBasePath', 'ProcessErrors', - function($rootScope, Rest, GetBasePath, ProcessErrors){ - return { - stringifyParams: function(params){ - return _.reduce(params, (result, value, key) => { - return result + key + '=' + value + '&'; - }, ''); - }, - get: function(params){ - var url = GetBasePath('hosts') + '?' + this.stringifyParams(params); - Rest.setUrl(url); - return Rest.get() - .success(function(res){ - return res; - }) - .error(function(data, status) { - ProcessErrors($rootScope, data, status, null, { hdr: 'Error!', - msg: 'Call to ' + url + '. GET returned: ' + status }); - }); - }, - post: function(params){ - var url = GetBasePath('hosts'); - Rest.setUrl(url); - return Rest.post(params) - .success(function(res){ - return res; - }) - .error(function(data, status) { - ProcessErrors($rootScope, data, status, null, { hdr: 'Error!', - msg: 'Call to ' + url + '. GET returned: ' + status }); - }); - }, - put: function(host){ - var url = GetBasePath('hosts') + host.id; - Rest.setUrl(url); - return Rest.put(host) - .success(function(res){ - return res; - }) - .error(function(data, status) { - ProcessErrors($rootScope, data, status, null, { hdr: 'Error!', - msg: 'Call to ' + url + '. GET returned: ' + status }); - }); - } - }; - }]; \ No newline at end of file diff --git a/awx/ui/client/src/inventories/manage/manage.block.less b/awx/ui/client/src/inventories/manage/manage.block.less deleted file mode 100644 index 772423c2e4..0000000000 --- a/awx/ui/client/src/inventories/manage/manage.block.less +++ /dev/null @@ -1,18 +0,0 @@ -#Inventory-groupManage--panel, -#Inventory-hostManage--panel { - .ui-dialog-buttonpane.ui-widget-content { - border: none; - text-align: right; - } - - #host-panel-form, - #properties-tab { - .Form-header { - margin-top: -20px; - } - } - - .Form-textArea { - width: 100%; - } -} diff --git a/awx/ui/client/src/job-detail/job-detail.controller.js b/awx/ui/client/src/job-detail/job-detail.controller.js index 2061d00df3..2e927fad2c 100644 --- a/awx/ui/client/src/job-detail/job-detail.controller.js +++ b/awx/ui/client/src/job-detail/job-detail.controller.js @@ -11,22 +11,17 @@ */ export default - [ '$location', '$rootScope', '$filter', '$scope', '$compile', - '$stateParams', '$log', 'ClearScope', 'GetBasePath', 'Wait', - 'ProcessErrors', 'SelectPlay', 'SelectTask', 'GetElapsed', - 'JobIsFinished', 'SetTaskStyles', 'DigestEvent', 'UpdateDOM', 'DeleteJob', 'InitiatePlaybookRun', - 'LoadPlays', 'LoadTasks', 'HostsEdit', - 'ParseVariableString', 'GetChoices', 'fieldChoices', 'fieldLabels', - 'EditSchedule', 'ParseTypeChange', 'JobDetailService', + [ '$location', '$rootScope', '$filter', '$scope', '$compile', '$stateParams', '$log', 'ClearScope', + 'GetBasePath', 'Wait', 'ProcessErrors', 'SelectPlay', 'SelectTask', 'GetElapsed', 'JobIsFinished', + 'SetTaskStyles', 'DigestEvent', 'UpdateDOM', 'DeleteJob', 'InitiatePlaybookRun', 'LoadPlays', 'LoadTasks', + 'ParseVariableString', 'GetChoices', 'fieldChoices', 'fieldLabels', 'EditSchedule', + 'ParseTypeChange', 'JobDetailService', function( - $location, $rootScope, $filter, $scope, $compile, $stateParams, - $log, ClearScope, GetBasePath, Wait, ProcessErrors, - SelectPlay, SelectTask, GetElapsed, - JobIsFinished, - SetTaskStyles, DigestEvent, UpdateDOM, DeleteJob, - InitiatePlaybookRun, LoadPlays, LoadTasks, - HostsEdit, ParseVariableString, GetChoices, fieldChoices, - fieldLabels, EditSchedule, ParseTypeChange, JobDetailService + $location, $rootScope, $filter, $scope, $compile, $stateParams, $log, ClearScope, + GetBasePath, Wait, ProcessErrors, SelectPlay, SelectTask, GetElapsed, JobIsFinished, + SetTaskStyles, DigestEvent, UpdateDOM, DeleteJob, InitiatePlaybookRun, LoadPlays, LoadTasks, + ParseVariableString, GetChoices, fieldChoices, fieldLabels, EditSchedule, + ParseTypeChange, JobDetailService ) { ClearScope(); diff --git a/awx/ui/client/src/lists/Groups.js b/awx/ui/client/src/lists/Groups.js index 33bf611371..f93162b302 100644 --- a/awx/ui/client/src/lists/Groups.js +++ b/awx/ui/client/src/lists/Groups.js @@ -9,41 +9,17 @@ export default angular.module('GroupListDefinition', []) - .value('GroupList', { + .value('CopyMoveGroupList', { - name: 'copy_groups', - iterator: 'copy_group', + name: 'groups', + iterator: 'group', selectTitle: 'Copy Groups', - editTitle: 'Groups', index: false, - well: false, - + well: false, fields: { name: { key: true, - label: 'Name' - } - }, - - actions: { }, - - fieldActions: { - edit: { - label: 'Edit', - ngClick: "editGroup(group.id)", - icon: 'icon-edit', - "class": 'btn-xs', - awToolTip: 'Edit group', - dataPlacement: 'top' - }, - - "delete": { - label: 'Delete', - ngClick: "deleteGroup(group.id, group.name)", - icon: 'icon-trash', - "class": 'btn-xs', - awToolTip: 'Delete group', - dataPlacement: 'top' + label: 'Target Group Name' } } }); diff --git a/awx/ui/client/src/lists/Inventories.js b/awx/ui/client/src/lists/Inventories.js index a40850649f..60543239c0 100644 --- a/awx/ui/client/src/lists/Inventories.js +++ b/awx/ui/client/src/lists/Inventories.js @@ -96,7 +96,7 @@ export default edit: { label: 'Edit', - ngClick: 'editInventory(inventory.id)', //'editInventoryProperties(inventory.id)', + ngClick: 'editInventory(inventory.id)', awToolTip: 'Edit inventory', dataPlacement: 'top' }, diff --git a/awx/ui/client/src/lists/InventoryGroups.js b/awx/ui/client/src/lists/InventoryGroups.js index 6002000e53..1f12a3af5a 100644 --- a/awx/ui/client/src/lists/InventoryGroups.js +++ b/awx/ui/client/src/lists/InventoryGroups.js @@ -21,18 +21,47 @@ export default multiSelect: true, fields: { + sync_status: { + label: '', + nosort: true, + searchable: false, + mode: 'all', + iconOnly: true, + ngClick: 'viewUpdateStatus(group.id)', + awToolTip: "{{ group.status_tooltip }}", + dataTipWatch: "group.status_tooltip", + icon: "{{ 'fa icon-cloud-' + group.status_class }}", + ngClass: "group.status_class", + dataPlacement: "top", + columnClass: 'status-column List-staticColumn--smallStatus' + }, + failed_hosts: { + label: '', + nosort: true, + searchable: false, + mode: 'all', + iconOnly: true, + awToolTip: "{{ group.hosts_status_tip }}", + dataPlacement: "top", + ngClick: "showFailedHosts(group)", + icon: "{{ 'fa icon-job-' + group.hosts_status_class }}", + columnClass: 'status-column List-staticColumn--smallStatus' + }, name: { label: 'Groups', key: true, ngClick: "groupSelect(group.id)", - columnClick: "groupSelect(group.id)", - columnClass: 'col-lg-3 col-md-3 col-sm-3 col-xs-3' + columnClass: 'col-lg-3 col-md-3 col-sm-3 col-xs-3', + class: 'InventoryManage-breakWord' }, total_groups: { nosort: true, label: '', type: 'badgeCount', ngHide: 'group.total_groups == 0', + noLink: true, + awToolTip: "{{group.name}} contains {{group.total_groups}} {{group.total_groups === 1 ? 'child' : 'children'}}", + }, source: { label: 'Source', @@ -100,8 +129,9 @@ export default }, launch: { mode: 'all', - ngShow: 'inventory.can_run_ad_hoc_commands', - ngClick: 'populateAdhocForm()', + // $scope.$parent is governed by InventoryManageController, + ngShow: '$parent.groupsSelected || $parent.hostsSelected', + ngClick: '$parent.setAdhocPattern()', awToolTip: "Run a command on the selected inventory", actionClass: 'btn List-buttonDefault', buttonContent: 'RUN COMMANDS' @@ -116,7 +146,7 @@ export default ngClick: "createGroup()", awToolTip: "Create a new group", actionClass: 'btn List-buttonSubmit', - buttonContent: '+ ADD' + buttonContent: '+ ADD GROUP' } }, @@ -124,32 +154,16 @@ export default columnClass: 'col-lg-6 col-md-6 col-sm-6 col-xs-6 text-right', - sync_status: { - mode: 'all', - ngClick: "viewUpdateStatus(group.id)", - awToolTip: "{{ group.status_tooltip }}", - dataTipWatch: "group.status_tooltip", - iconClass: "{{ 'fa icon-cloud-' + group.status_class }}", - ngClass: "group.status_class", - dataPlacement: "top" - }, - failed_hosts: { - mode: 'all', - awToolTip: "{{ group.hosts_status_tip }}", - dataPlacement: "top", - ngClick: "showHosts(group.id, group.group_id, group.show_failures)", - iconClass: "{{ 'fa icon-job-' + group.hosts_status_class }}" - }, group_update: { //label: 'Sync', mode: 'all', - ngClick: 'updateGroup(group.id)', + ngClick: 'updateGroup(group)', awToolTip: "{{ group.launch_tooltip }}", dataTipWatch: "group.launch_tooltip", ngShow: "group.status !== 'running' && group.status " + "!== 'pending' && group.status !== 'updating'", ngClass: "group.launch_class", - dataPlacement: "top" + dataPlacement: "top", }, cancel: { //label: 'Cancel', @@ -159,11 +173,12 @@ export default 'class': 'red-txt', ngShow: "group.status == 'running' || group.status == 'pending' " + "|| group.status == 'updating'", - dataPlacement: "top" + dataPlacement: "top", + iconClass: "fa fa-minus-circle" }, copy: { mode: 'all', - ngClick: "copyGroup(group.id)", + ngClick: "copyMoveGroup(group.id)", awToolTip: 'Copy or move group', ngShow: "group.id > 0", dataPlacement: "top" @@ -186,7 +201,7 @@ export default "delete": { //label: 'Delete', mode: 'all', - ngClick: "deleteGroup(group.id)", + ngClick: "deleteGroup(group)", awToolTip: 'Delete group', dataPlacement: "top" } diff --git a/awx/ui/client/src/lists/InventoryHosts.js b/awx/ui/client/src/lists/InventoryHosts.js index 655f8e28ac..d99589968e 100644 --- a/awx/ui/client/src/lists/InventoryHosts.js +++ b/awx/ui/client/src/lists/InventoryHosts.js @@ -22,6 +22,23 @@ export default multiSelect: true, fields: { + active_failures: { + label: '', + iconOnly: true, + searchable: false, + nosort: true, + // do not remove this ng-click directive + // the list generator case to handle fields without ng-click + // cannot handle the aw-* directives + ngClick: 'noop()', + awPopOver: "{{ host.job_status_html }}", + dataTitle: "{{ host.job_status_title }}", + awToolTip: "{{ host.badgeToolTip }}", + dataPlacement: 'top', + icon: "{{ 'fa icon-job-' + host.active_failures }}", + id: 'active-failures-action', + columnClass: 'status-column List-staticColumn--smallStatus' + }, name: { key: true, label: 'Hosts', @@ -29,7 +46,8 @@ export default ngClass: "{ 'host-disabled-label': !host.enabled }", columnClass: 'col-lg-6 col-md-8 col-sm-8 col-xs-7', dataHostId: "{{ host.id }}", - dataType: "host" + dataType: "host", + class: 'InventoryManage-breakWord' }, enabled: { label: 'Disabled?', @@ -50,20 +68,9 @@ export default fieldActions: { columnClass: 'col-lg-6 col-md-4 col-sm-4 col-xs-5 text-right', - label: false, - - active_failures: { - awPopOver: "{{ host.job_status_html }}", - dataTitle: "{{ host.job_status_title }}", - awToolTip: "{{ host.badgeToolTip }}", - awTipPlacement: 'top', - dataPlacement: 'left', - iconClass: "{{ 'fa icon-job-' + host.active_failures }}", - id: 'active-failutes-action' - }, copy: { mode: 'all', - ngClick: "copyHost(host.id)", + ngClick: "copyMoveHost(host.id)", awToolTip: 'Copy or move host to another group', dataPlacement: "top" }, @@ -85,15 +92,15 @@ export default actions: { system_tracking: { - label: 'System Tracking', - ngClick: 'systemTracking()', //'editInventoryProperties(inventory.id)', + buttonContent: 'System Tracking', + ngClick: 'systemTracking()', awToolTip: "{{ systemTrackingTooltip }}", dataTipWatch: "systemTrackingTooltip", dataPlacement: 'top', awFeature: 'system_tracking', - ngDisabled: 'systemTrackingDisabled', actionClass: 'btn List-buttonDefault system-tracking', - ngShow: 'hostsSelected' + ngShow: 'hostsSelected', + ngDisabled: 'systemTrackingDisabled' }, refresh: { mode: 'all', @@ -108,7 +115,7 @@ export default ngClick: "createHost()", awToolTip: "Create a new host", actionClass: 'btn List-buttonSubmit', - buttonContent: '+ ADD' + buttonContent: '+ ADD HOST' } } diff --git a/awx/ui/client/src/main-menu/main-menu.directive.js b/awx/ui/client/src/main-menu/main-menu.directive.js index a8056e233f..8a3ea0917f 100644 --- a/awx/ui/client/src/main-menu/main-menu.directive.js +++ b/awx/ui/client/src/main-menu/main-menu.directive.js @@ -10,6 +10,9 @@ export default scope.isCurrentState = function(name){ return $state.current.name === name; }; + scope.includesCurrentState = function(name){ + return $state.includes(name); + }; // set up the user tooltip $rootScope.$on('current_user', function(user) { diff --git a/awx/ui/client/src/scheduler/main.js b/awx/ui/client/src/scheduler/main.js index b75ee987b9..faaf6ecce7 100644 --- a/awx/ui/client/src/scheduler/main.js +++ b/awx/ui/client/src/scheduler/main.js @@ -52,19 +52,26 @@ export default controller: 'schedulerEditController' }); $stateExtender.addState({ - name: 'inventoryManageSchedules', - route: '/inventory/:inventory_id/manage/:id/schedules', - templateUrl: templateUrl("scheduler/scheduler"), - controller: 'schedulerController' + name: 'inventoryManage.schedules', + route: '/schedules/:id', + views: { + 'form@inventoryManage': { + templateUrl: templateUrl("scheduler/scheduler"), + controller: 'schedulerController' + } + }, + ncyBreadcrumb: { + label: "{{name}} SCHEDULES" + }, }); $stateExtender.addState({ - name: 'inventoryManageSchedules.add', + name: 'inventoryManage.schedules.add', route: '/add', templateUrl: templateUrl("scheduler/schedulerForm"), controller: 'schedulerAddController' }); $stateExtender.addState({ - name: 'inventoryManageSchedules.edit', + name: 'inventoryManage.schedules.edit', route: '/:schedule_id', templateUrl: templateUrl("scheduler/schedulerForm"), controller: 'schedulerEditController' diff --git a/awx/ui/client/src/shared/InventoryTree.js b/awx/ui/client/src/shared/InventoryTree.js deleted file mode 100644 index f6c9ecb925..0000000000 --- a/awx/ui/client/src/shared/InventoryTree.js +++ /dev/null @@ -1,609 +0,0 @@ -/************************************************* - * Copyright (c) 2015 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ - - - /** - * @ngdoc function - * @name shared.function:inventoryTree - * @description - * InventoryTree.js - * - * Build data for the tree selector table used on inventory detail page. - * - */ - - -export default -angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper', 'PromptDialog']) - -.factory('SortNodes', [ - function () { - return function (data) { - //Sort nodes by name - var i, j, names = [], newData = []; - for (i = 0; i < data.length; i++) { - names.push(data[i].name); - } - names.sort(); - for (j = 0; j < names.length; j++) { - for (i = 0; i < data.length; i++) { - if (data[i].name === names[j]) { - newData.push(data[i]); - } - } - } - return newData; - }; - } -]) - -.factory('BuildTree', ['$location', 'Rest', 'GetBasePath', 'ProcessErrors', 'SortNodes', 'Wait', 'GetSyncStatusMsg', 'GetHostsStatusMsg', 'Store', - function ($location, Rest, GetBasePath, ProcessErrors, SortNodes, Wait, GetSyncStatusMsg, GetHostsStatusMsg, Store) { - return function (params) { - - var inventory_id = params.inventory_id, - scope = params.scope, - refresh = params.refresh, - emit = params.emit, - new_group_id = params.new_group_id, - groups = [], - id = 1, - local_child_store, - path = $location.path(); - - function buildAllHosts(tree_data) { - // Start our tree object with All Hosts - var children = [], - sorted = SortNodes(tree_data), - j, all_hosts; - - for (j = 0; j < sorted.length; j++) { - children.push(sorted[j].id); - } - - all_hosts = { - name: 'All Hosts', - id: 1, - group_id: null, - parent: 0, - description: '', - show: true, - ngicon: null, - has_children: false, - related: {}, - selected_class: '', - show_failures: false, - isDraggable: false, - isDroppable: true, - children: children - }; - groups.push(all_hosts); - } - - function getExpandState(key) { - var result = true; - local_child_store.every(function(child) { - if (child.key === key) { - result = child.expand; - return false; - } - return true; - }); - return result; - } - - function getShowState(key) { - var result = null; - local_child_store.every(function(child) { - if (child.key === key) { - result = (child.show !== undefined) ? child.show : true; - return false; - } - return true; - }); - return result; - } - - 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, - source: ( (sorted[i].summary_fields.inventory_source) ? sorted[i].summary_fields.inventory_source.source : null ) - }); // from helpers/Groups.js - - hosts_status = GetHostsStatusMsg({ - active_failures: sorted[i].hosts_with_active_failures, - total_hosts: sorted[i].total_hosts, - inventory_id: inventory_id, - group_id: sorted[i].id - }); // from helpers/Groups.js - - children = []; - sorted[i].children.forEach( function(child, j) { - children.push(sorted[i].children[j].id); - }); - - expand = (sorted[i].children.length > 0) ? getExpandState(sorted[i].id) : false; - 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. - show = true; - if (parent > 0) { - groups.every(function(g) { - if (g.id === parent) { - show = getExpandState(g.key); - return false; - } - return true; - }); - } - } - - group = { - name: sorted[i].name, - has_active_failures: sorted[i].has_active_failures, - total_hosts: sorted[i].total_hosts, - hosts_with_active_failures: sorted[i].hosts_with_active_failures, - total_groups: sorted[i].total_groups, - groups_with_active_failures: sorted[i].groups_with_active_failures, - parent: parent, - has_children: (sorted[i].children.length > 0) ? true : false, - has_inventory_sources: sorted[i].has_inventory_sources, - id: id, - source: sorted[i].summary_fields.inventory_source.source, - key: sorted[i].id, - group_id: sorted[i].id, - event_level: level, - children: children, - show: show, - related: sorted[i].related, - status: sorted[i].summary_fields.inventory_source.status, - status_class: stat['class'], - status_tooltip: stat.tooltip, - launch_tooltip: stat.launch_tip, - launch_class: stat.launch_class, - hosts_status_tip: hosts_status.tooltip, - show_failures: hosts_status.failures, - hosts_status_class: hosts_status['class'], - inventory_id: inventory_id, - selected_class: '', - isDraggable: true, - isDroppable: true - }; - if (sorted[i].children.length > 0) { - if (expand) { - group.ngicon = 'fa fa-minus-square-o node-toggle'; - } - else { - group.ngicon = 'fa fa-plus-square-o node-toggle'; - } - } - else { - group.ngicon = 'fa fa-square-o node-no-toggle'; - } - if (new_group_id && group.group_id === new_group_id) { - scope.selected_tree_id = id; - scope.selected_group_id = group.group_id; - } - groups.push(group); - if (sorted[i].children.length > 0) { - buildGroups(sorted[i].children, id, level + 1); - } - }); - } - - // Build the HTML for our tree - if (scope.buildAllGroupsRemove) { - scope.buildAllGroupsRemove(); - } - scope.buildAllGroupsRemove = scope.$on('buildAllGroups', function (e, inventory_name, inventory_tree) { - Rest.setUrl(inventory_tree); - Rest.get() - .success(function (data) { - buildAllHosts(data); - buildGroups(data, 0, 0); - scope.autoShowGroupHelp = (data.length === 0) ? true : false; - if (refresh) { - scope.groups = groups; - scope.$emit('GroupTreeRefreshed', inventory_name, groups, emit); - } else { - scope.$emit('GroupTreeLoaded', inventory_name, groups, emit); - } - }) - .error(function (data, status) { - ProcessErrors(scope, data, status, null, { hdr: 'Error!', - msg: 'Failed to get inventory tree for: ' + inventory_id + '. GET returned: ' + status - }); - }); - }); - - - function loadTreeData() { - // Load the inventory root node - Wait('start'); - Rest.setUrl(GetBasePath('inventory') + inventory_id + '/'); - Rest.get() - .success(function (data) { - scope.$emit('buildAllGroups', data.name, data.related.tree, data.related.groups); - }) - .error(function (data, status) { - Wait('stop'); - ProcessErrors(scope, data, status, null, { - hdr: 'Error!', - msg: 'Failed to get inventory: ' + inventory_id + '. GET returned: ' + status - }); - }); - } - local_child_store = Store(path + '_children'); - if (!local_child_store) { - local_child_store = []; - } - loadTreeData(); - }; - } -]) - - -// Update a group with a set of properties -.factory('UpdateGroup', ['ApplyEllipsis', 'GetSyncStatusMsg', 'Empty', - function (ApplyEllipsis, GetSyncStatusMsg, Empty) { - return function (params) { - - var scope = params.scope, - 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].id === group_id) { - grp = scope.groups[i]; - for (p in properties) { - if (p === 'name') { - old_name = scope.groups[i].name; - } - if (p === 'source') { - if (properties[p] !== scope.groups[i][p]) { - // User changed source - if (!Empty(properties[p]) && (scope.groups[i].status === 'none' || Empty(scope.groups[i].status))) { - // We have a source but no status, seed the status with 'never' to enable sync button - scope.groups[i].status = 'never updated'; - } else if (!properties[p]) { - // User removed source - scope.groups[i].status = 'none'; - } - // Update date sync status links/icons - stat = GetSyncStatusMsg({ - status: scope.groups[i].status, - has_inventory_sources: properties.has_inventory_sources, - source: properties.source - }); - scope.groups[i].status_class = stat['class']; - scope.groups[i].status_tooltip = stat.tooltip; - scope.groups[i].launch_tooltip = stat.launch_tip; - scope.groups[i].launch_class = stat.launch_class; - } - } - scope.groups[i][p] = properties[p]; - } - } - /*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 - if (old_name) { - setTimeout(function () { - $('#groups_table .group-name a[title="' + old_name + '"]').attr('title', properties.name); - ApplyEllipsis('#groups_table .group-name a'); - }, 2500); - } - - }; - } -]) - - -// Set node name and description after an update to Group properties. -.factory('SetNodeName', [ - function () { - return function (params) { - var name = params.name, - descr = params.description, - group_id = (params.group_id !== undefined) ? params.group_id : null, - inventory_id = (params.inventory_id !== undefined) ? params.inventory_id : null; - - if (group_id !== null) { - $('#inventory-tree').find('li [data-group-id="' + group_id + '"]').each(function () { - $(this).attr('data-name', name); - $(this).attr('data-description', descr); - $(this).find('.activate').first().text(name); - }); - } - - if (inventory_id !== null) { - $('#inventory-root-node').attr('data-name', name).attr('data-description', descr).find('.activate').first().text(name); - } - }; - } -]) - - -// Copy or Move a group on the tree after drag-n-drop -.factory('CopyMoveGroup', ['$compile', 'Alert', 'ProcessErrors', 'Find', 'Wait', 'Rest', 'Empty', 'GetBasePath', - function ($compile, Alert, ProcessErrors, Find, Wait, Rest, Empty, GetBasePath) { - return function (params) { - - var scope = params.scope, - target = Find({ list: scope.groups, key: 'id', val: params.target_tree_id }), - inbound = Find({ list: scope.groups, key: 'id', val: params.inbound_tree_id }), - e, html = ''; - - // Build the html for our prompt dialog - html += "
\n"; - html += "
\n"; - html += "
\n"; - html += "
\n"; - html += "\n"; - - if (target.id === 1 || inbound.parent === 0) { - // We're moving the group to the top level, or we're moving a top level group down - html += "

Move Group

\n"; - } else { - html += "

Copy or Move?

\n"; - } - - html += "
\n"; - html += "
\n"; - - if (target.id === 1) { - html += "
Are you sure you want to move group " + inbound.name + " to the top level?
"; - } else if (inbound.parent === 0) { - html += "
Are you sure you want to move group " + inbound.name + " from the top level and make it a child of " + - target.name + "?
"; - } else { - html += "
\n"; - html += "

Would you like to copy or move group " + inbound.name + " to group " + target.name + "?

\n"; - html += "
\n"; - html += " Move\n"; - html += " Copy\n"; - html += "
\n"; - html += "
\n"; - } - - html += "
\n"; - html += "
\n"; - html += "Cancel\n"; - - if (target.id === 1 || inbound.parent === 0) { - // We're moving the group to the top level, or we're moving a top level group down - html += "Yes\n"; - } - - html += "
\n"; - html += "
\n"; - html += "
\n"; - html += "
\n"; - - // Inject our custom dialog - e= angular.element(document.getElementById('inventory-modal-container')); - e.empty().append(html); - $compile(e)(scope); - - // Display it - $('#copy-prompt-modal').modal({ - backdrop: 'static', - keyboard: true, - show: true - }); - - // Respond to move - scope.moveGroup = function () { - var url, group, parent; - $('#copy-prompt-modal').modal('hide'); - Wait('start'); - - // disassociate the group from the original parent - if (scope.removeGroupRemove) { - scope.removeGroupRemove(); - } - scope.removeGroupRemove = scope.$on('removeGroup', function () { - if (inbound.parent > 0) { - // Only remove a group from a parent when the parent is a group and not the inventory root - parent = Find({ list: scope.groups, key: 'id', val: inbound.parent }); - url = GetBasePath('base') + 'groups/' + parent.group_id + '/children/'; - Rest.setUrl(url); - Rest.post({ id: inbound.group_id, disassociate: 1 }) - .success(function () { - //Triggers refresh of group list in inventory controller - scope.$emit('GroupDeleteCompleted'); - }) - .error(function (data, status) { - Wait('stop'); - ProcessErrors(scope, data, status, null, { - hdr: 'Error!', - msg: 'Failed to remove ' + inbound.name + - ' from ' + parent.name + '. POST returned status: ' + status - }); - }); - } else { - //Triggers refresh of group list in inventory controller - scope.$emit('GroupDeleteCompleted'); - } - }); - - // add the new group to the target parent - url = (!Empty(target.group_id)) ? - GetBasePath('base') + 'groups/' + target.group_id + '/children/' : - GetBasePath('inventory') + scope.inventory_id + '/groups/'; - group = { - id: inbound.group_id, - name: inbound.name, - description: inbound.description, - inventory: scope.inventory_id - }; - Rest.setUrl(url); - Rest.post(group) - .success(function () { - scope.$emit('removeGroup'); - }) - .error(function (data, status) { - var target_name = (Empty(target.group_id)) ? 'inventory' : target.name; - Wait('stop'); - ProcessErrors(scope, data, status, null, { hdr: 'Error!', - msg: 'Failed to add ' + inbound.name + ' to ' + target_name + '. POST returned status: ' + status }); - }); - }; - - - scope.copyGroup = function () { - $('#copy-prompt-modal').modal('hide'); - Wait('start'); - // add the new group to the target parent - var url = (!Empty(target.group_id)) ? - GetBasePath('base') + 'groups/' + target.group_id + '/children/' : - GetBasePath('inventory') + scope.inventory_id + '/groups/', - group = { - id: inbound.group_id, - name: inbound.name, - description: inbound.description, - inventory: scope.inventory_id - }; - - Rest.setUrl(url); - Rest.post(group) - .success(function () { - //Triggers refresh of group list in inventory controller - scope.$emit('GroupDeleteCompleted'); - }) - .error(function (data, status) { - var target_name = (Empty(target.group_id)) ? 'inventory' : target.name; - Wait('stop'); - ProcessErrors(scope, data, status, null, { hdr: 'Error!', - msg: 'Failed to add ' + inbound.name + ' to ' + target_name + '. POST returned status: ' + status - }); - }); - }; - - }; - } -]) - -// Copy a host after drag-n-drop -.factory('CopyMoveHost', ['$compile', 'Alert', 'ProcessErrors', 'Find', 'Wait', 'Rest', 'Empty', 'GetBasePath', - function ($compile, Alert, ProcessErrors, Find, Wait, Rest, Empty, GetBasePath) { - return function (params) { - - var scope = params.scope, - target = Find({ list: scope.groups, key: 'id', val: params.target_tree_id }), - host = Find({ list: scope.hosts, key: 'id', val: params.host_id }), - found = false, e, i, html = ''; - - if (host.summary_fields.all_groups) { - for (i = 0; i < host.summary_fields.all_groups.length; i++) { - if (host.summary_fields.all_groups[i].id === target.group_id) { - found = true; - break; - } - } - } - if (found) { - html += "
\n"; - html += "
\n"; - html += "
\n"; - html += "
\n"; - html += "\n"; - html += "

Already in Group

\n"; - html += "
\n"; - html += "
\n"; - html += "

Host " + host.name + " is already in group " + target.name + ".

\n"; - html += "
\n"; - html += "
\n"; - html += "OK\n"; - html += "
\n"; - html += "
\n"; - html += "
\n"; - html += "
\n"; - - // Inject our custom dialog - e = angular.element(document.getElementById('inventory-modal-container')); - e.empty().append(html); - $compile(e)(scope); - - // Display it - $('#copy-alert-modal').modal({ - backdrop: 'static', - keyboard: true, - show: true - }); - - } else { - // Build the html for our prompt dialog - html = ''; - html += "
\n"; - html += "
\n"; - html += "
\n"; - html += "
\n"; - html += "\n"; - html += "

Copy Host

\n"; - html += "
\n"; - html += "
\n"; - html += "
Are you sure you want to copy host " + host.name + ' to group ' + target.name + '?
'; - html += "
\n"; - html += "
\n"; - html += "No\n"; - html += "Yes\n"; - html += "
\n"; - html += "
\n"; - html += "
\n"; - html += "
\n"; - - // Inject our custom dialog - e = angular.element(document.getElementById('inventory-modal-container')); - e.empty().append(html); - $compile(e)(scope); - - // Display it - $('#copy-prompt-modal').modal({ - backdrop: 'static', - keyboard: true, - show: true - }); - - scope.copyHost = function () { - $('#copy-prompt-modal').modal('hide'); - Wait('start'); - Rest.setUrl(GetBasePath('groups') + target.group_id + '/hosts/'); - Rest.post(host) - .success(function () { - // Signal the controller to refresh the hosts view - scope.$emit('GroupTreeRefreshed'); - }) - .error(function (data, status) { - Wait('stop'); - ProcessErrors(scope, data, status, null, { hdr: 'Error!', msg: 'Failed to add ' + host.name + ' to ' + - target.name + '. POST returned status: ' + status }); - }); - }; - } - }; - } -]); diff --git a/awx/ui/client/src/shared/generator-helpers.js b/awx/ui/client/src/shared/generator-helpers.js index fc62bbecbd..7a0c517a58 100644 --- a/awx/ui/client/src/shared/generator-helpers.js +++ b/awx/ui/client/src/shared/generator-helpers.js @@ -277,10 +277,14 @@ angular.module('GeneratorHelpers', [systemStatus.name]) html = "\n"; - html += ""; diff --git a/awx/ui/client/src/shared/list-generator/list-generator.factory.js b/awx/ui/client/src/shared/list-generator/list-generator.factory.js index 9e2da09fc4..a0a8258595 100644 --- a/awx/ui/client/src/shared/list-generator/list-generator.factory.js +++ b/awx/ui/client/src/shared/list-generator/list-generator.factory.js @@ -439,6 +439,7 @@ export default ['$location', '$compile', '$rootScope', 'SearchWidget', 'Paginate innerTable += "ng-class-odd=\"'List-tableRow--oddRow'\" "; innerTable += "ng-class-even=\"'List-tableRow--evenRow'\" "; innerTable += "ng-repeat=\"" + list.iterator + " in " + list.name; + innerTable += (list.trackBy) ? " track by " + list.trackBy : " track by $index"; innerTable += (list.orderBy) ? " | orderBy:'" + list.orderBy + "'" : ""; innerTable += (list.filterBy) ? " | filter: " + list.filterBy : ""; innerTable += "\">\n"; @@ -459,7 +460,7 @@ export default ['$location', '$compile', '$rootScope', 'SearchWidget', 'Paginate "ng-false-value=\"0\" id=\"check_" + list.iterator + "_{{" + list.iterator + ".id}}\" />"; } else { // its assumed that options.input_type = checkbox - innerTable += ""; } @@ -619,7 +620,7 @@ export default ['$location', '$compile', '$rootScope', 'SearchWidget', 'Paginate html += buildSelectAll().prop('outerHTML'); } else if (options.mode === 'lookup') { - html += ""; + html += ""; } for (fld in list.fields) { if ((list.fields[fld].searchOnly === undefined || list.fields[fld].searchOnly === false) && diff --git a/awx/ui/client/tests/adhoc/adhoc.controller-test.js b/awx/ui/client/tests/adhoc/adhoc.controller-test.js index 27d61b3f30..d49c699994 100644 --- a/awx/ui/client/tests/adhoc/adhoc.controller-test.js +++ b/awx/ui/client/tests/adhoc/adhoc.controller-test.js @@ -1,6 +1,6 @@ import '../support/node'; -import adhocModule from 'adhoc/main'; +import adhocModule from 'inventories/manage/adhoc/main'; import RestStub from '../support/rest-stub'; describe("adhoc.controller", function() { diff --git a/awx/ui/templates/ui/index.html b/awx/ui/templates/ui/index.html index 7960ba38c9..3e8395b3e8 100644 --- a/awx/ui/templates/ui/index.html +++ b/awx/ui/templates/ui/index.html @@ -40,7 +40,7 @@ - +