awx/awx/ui/client/src/helpers/Groups.js
2015-08-20 09:59:32 -07:00

2168 lines
111 KiB
JavaScript

/*************************************************
* Copyright (c) 2015 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
'use strict';
/**
* @ngdoc function
* @name helpers.function:Groups
* @description inventory tree widget add/edit/delete
*/
import listGenerator from '../shared/list-generator/main';
export default
angular.module('GroupsHelper', [ 'RestServices', 'Utilities', listGenerator.name, 'GroupListDefinition', 'SearchHelper',
'PaginationHelpers', listGenerator.name, 'AuthService', 'GroupsHelper', 'InventoryHelper', 'SelectionHelper',
'JobSubmissionHelper', 'RefreshHelper', 'PromptDialog', 'CredentialsListDefinition', 'InventoryTree',
'InventoryStatusDefinition', 'VariablesHelper', 'SchedulesListDefinition', 'SourceFormDefinition', 'LogViewerHelper',
'SchedulesHelper'
])
/**
*
* Lookup options for group source and build an array of drop-down choices
*
*/
.factory('GetSourceTypeOptions', ['Rest', 'ProcessErrors', 'GetBasePath',
function (Rest, ProcessErrors, GetBasePath) {
return function (params) {
var scope = params.scope,
variable = params.variable;
if (scope[variable] === undefined) {
scope[variable] = [];
Rest.setUrl(GetBasePath('inventory_sources'));
Rest.options()
.success(function (data) {
var i, choices = data.actions.GET.source.choices;
for (i = 0; i < choices.length; i++) {
if (choices[i][0] !== 'file') {
scope[variable].push({
label: choices[i][1],
value: choices[i][0]
});
}
}
scope.$emit('sourceTypeOptionsReady');
})
.error(function (data, status) {
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
msg: 'Failed to retrieve options for inventory_sources.source. OPTIONS status: ' + status
});
});
}
};
}
])
/**
*
* TODO: Document
*
*/
.factory('ViewUpdateStatus', ['Rest', 'ProcessErrors', 'GetBasePath', 'Alert', 'Wait', 'Empty', 'Find', 'LogViewer',
function (Rest, ProcessErrors, GetBasePath, Alert, Wait, Empty, Find, LogViewer) {
return function (params) {
var scope = params.scope,
group_id = params.group_id,
group = Find({ list: scope.groups, key: 'id', val: group_id });
if (scope.removeSourceReady) {
scope.removeSourceReady();
}
scope.removeSourceReady = scope.$on('SourceReady', function(e, url) {
LogViewer({
scope: scope,
url: url
});
});
if (group) {
if (Empty(group.source)) {
// do nothing
} else if (Empty(group.status) || group.status === "never updated") {
Alert('No Status Available', 'An inventory sync has not been performed for the selected group. Start the process by ' +
'clicking the <i class="fa fa-exchange"></i> button.', 'alert-info', null, null, null, null, true);
} else {
Wait('start');
Rest.setUrl(group.related.inventory_source);
Rest.get()
.success(function (data) {
var url = (data.related.current_update) ? data.related.current_update : data.related.last_update;
scope.$emit('SourceReady', url);
})
.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 });
});
}
}
};
}
])
/**
*
* TODO: Document
*
*/
.factory('GetHostsStatusMsg', [
function () {
return function (params) {
var active_failures = params.active_failures,
total_hosts = params.total_hosts,
tip, failures, html_class;
// Return values for use on host status indicator
if (active_failures > 0) {
tip = total_hosts + ((total_hosts === 1) ? ' host' : ' hosts') + '. ' + active_failures + ' with failed jobs.';
html_class = 'error';
failures = true;
} else {
failures = false;
if (total_hosts === 0) {
// no hosts
tip = "Contains 0 hosts.";
html_class = 'none';
} else {
// many hosts with 0 failures
tip = total_hosts + ((total_hosts === 1) ? ' host' : ' hosts') + '. No job failures';
html_class = 'success';
}
}
return {
tooltip: tip,
failures: failures,
'class': html_class
};
};
}
])
/**
*
* TODO: Document
*
*/
.factory('GetSyncStatusMsg', [ 'Empty',
function (Empty) {
return function (params) {
var status = params.status,
source = params.source,
has_inventory_sources = params.has_inventory_sources,
launch_class = '',
launch_tip = 'Start sync process',
stat, stat_class, status_tip;
stat = status;
stat_class = stat;
switch (status) {
case 'never updated':
stat = 'never';
stat_class = 'na';
status_tip = 'Sync not performed. Click <i class="fa fa-exchange"></i> to start it now.';
break;
case 'none':
case 'ok':
case '':
launch_class = 'btn-disabled';
stat = 'n/a';
stat_class = 'na';
status_tip = 'Cloud source not configured. Click <i class="fa fa-pencil"></i> to update.';
launch_tip = 'Cloud source not configured.';
break;
case 'canceled':
status_tip = 'Sync canceled. Click to view log.';
break;
case 'failed':
status_tip = 'Sync failed. Click to view log.';
break;
case 'successful':
status_tip = 'Sync completed. Click to view log.';
break;
case 'pending':
status_tip = 'Sync pending.';
launch_class = "btn-disabled";
launch_tip = "Sync pending";
break;
case 'updating':
case 'running':
launch_class = "btn-disabled";
launch_tip = "Sync running";
status_tip = "Sync running. Click to view log.";
break;
}
if (has_inventory_sources && Empty(source)) {
// parent has a source, therefore this group should not have a source
launch_class = "btn-disabled";
status_tip = 'Managed by an external cloud source.';
launch_tip = 'Can only be updated by running a sync on the parent group.';
}
if (has_inventory_sources === false && Empty(source)) {
launch_class = 'btn-disabled';
status_tip = 'Cloud source not configured. Click <i class="fa fa-pencil"></i> to update.';
launch_tip = 'Cloud source not configured.';
}
return {
"class": stat_class,
"tooltip": status_tip,
"status": stat,
"launch_class": launch_class,
"launch_tip": launch_tip
};
};
}
])
/**
*
* 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;
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_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') + '?organization='+scope.$parent.inventory.organization;
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 === 'azure' ||
scope.source.value === 'vmware' ||
scope.source.value === 'openstack') {
if (scope.source.value === 'ec2') {
kind = 'aws';
} else {
kind = scope.source.value;
}
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
*
*/
.factory('GroupsCancelUpdate', ['Empty', 'Rest', 'ProcessErrors', 'Alert', 'Wait', 'Find',
function (Empty, Rest, ProcessErrors, Alert, Wait, Find) {
return function (params) {
var scope = params.scope,
id = params.id,
group = params.group;
if (scope.removeCancelUpdate) {
scope.removeCancelUpdate();
}
scope.removeCancelUpdate = scope.$on('CancelUpdate', function (e, url) {
// Cancel the update process
Rest.setUrl(url);
Rest.post()
.success(function () {
Wait('stop');
//Alert('Inventory Sync Cancelled', 'Request to cancel the sync process was submitted to the task manger. ' +
// 'Click the <i class="fa fa-refresh fa-lg"></i> button to monitor the status.', 'alert-info');
})
.error(function (data, status) {
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
msg: 'Call to ' + url + ' failed. POST status: ' + status
});
});
});
if (scope.removeCheckCancel) {
scope.removeCheckCancel();
}
scope.removeCheckCancel = scope.$on('CheckCancel', function (e, last_update, current_update) {
// Check that we have access to cancelling an update
var url = (current_update) ? current_update : last_update;
url += 'cancel/';
Rest.setUrl(url);
Rest.get()
.success(function (data) {
if (data.can_cancel) {
scope.$emit('CancelUpdate', url);
//} else {
// Wait('stop');
// Alert('Cancel Inventory Sync', 'The sync process completed. Click the <i class="fa fa-refresh fa-lg"></i> button to view ' +
// 'the latest status.', 'alert-info');
}
else {
Wait('stop');
}
})
.error(function (data, status) {
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
msg: 'Call to ' + url + ' failed. GET status: ' + status
});
});
});
// Cancel the update process
if (Empty(group)) {
group = Find({ list: scope.groups, key: 'id', val: id });
scope.selected_group_id = group.id;
}
if (group && (group.status === 'running' || group.status === 'pending')) {
// We found the group, and there is a running update
Wait('start');
Rest.setUrl(group.related.inventory_source);
Rest.get()
.success(function (data) {
scope.$emit('CheckCancel', data.related.last_update, data.related.current_update);
})
.error(function (data, status) {
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
msg: 'Call to ' + group.related.inventory_source + ' failed. GET status: ' + status
});
});
}
};
}
])
/**
*
* 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',
breadCrumbs: false,
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
*
*/
.factory('GroupsEdit', ['$filter', '$rootScope', '$location', '$log', '$routeParams', '$compile', 'Rest', 'Alert', 'GroupForm', 'GenerateForm',
'Prompt', 'ProcessErrors', 'GetBasePath', 'SetNodeName', 'ParseTypeChange', 'GetSourceTypeOptions', 'InventoryUpdate',
'LookUpInit', 'Empty', 'Wait', 'GetChoices', 'UpdateGroup', 'SourceChange', 'Find',
'ParseVariableString', 'ToJSON', 'GroupsScheduleListInit', 'SourceForm', 'SetSchedulesInnerDialogSize', 'CreateSelect2',
function ($filter, $rootScope, $location, $log, $routeParams, $compile, Rest, Alert, GroupForm, GenerateForm, Prompt, ProcessErrors,
GetBasePath, SetNodeName, ParseTypeChange, GetSourceTypeOptions, InventoryUpdate, LookUpInit, Empty, Wait,
GetChoices, UpdateGroup, SourceChange, Find, ParseVariableString, ToJSON, GroupsScheduleListInit,
SourceForm, SetSchedulesInnerDialogSize, CreateSelect2) {
return function (params) {
var parent_scope = params.scope,
group_id = params.group_id,
mode = params.mode, // 'add' or 'edit'
inventory_id = params.inventory_id,
generator = GenerateForm,
group_created = false,
defaultUrl,
master = {},
choicesReady,
modal_scope = parent_scope.$new(),
properties_scope = parent_scope.$new(),
sources_scope = parent_scope.$new(),
elem, x, y, ww, wh, maxrows,
group,
schedules_url = '';
if (mode === 'edit') {
defaultUrl = GetBasePath('groups') + group_id + '/';
}
else {
defaultUrl = (group_id !== null) ? GetBasePath('groups') + group_id + '/children/' :
GetBasePath('inventory') + inventory_id + '/groups/';
}
$('#properties-tab').empty();
$('#sources-tab').empty();
$('#schedules-list').empty();
$('#schedules-form').empty();
$('#schedules-detail').empty();
elem = document.getElementById('group-modal-dialog');
$compile(elem)(modal_scope);
var form_scope =
generator.inject(GroupForm, { mode: 'edit', id: 'properties-tab', breadCrumbs: false, related: false, scope: properties_scope });
var source_form_scope =
generator.inject(SourceForm, { mode: 'edit', id: 'sources-tab', breadCrumbs: false, related: false, scope: sources_scope });
//generator.reset();
GetSourceTypeOptions({ scope: sources_scope, variable: 'source_type_options' });
sources_scope.source = SourceForm.fields.source['default'];
sources_scope.sourcePathRequired = false;
sources_scope[SourceForm.fields.source_vars.parseTypeName] = 'yaml';
sources_scope.update_cache_timeout = 0;
properties_scope.parseType = 'yaml';
function waitStop() { Wait('stop'); }
// Attempt to create the largest textarea field that will fit on the window. Minimum
// height is 6 rows, so on short windows you will see vertical scrolling
function textareaResize(textareaID) {
var textArea, formHeight, model, windowHeight, offset, rows;
textArea = $('#' + textareaID);
if (properties_scope.codeMirror) {
model = textArea.attr('ng-model');
properties_scope[model] = properties_scope.codeMirror.getValue();
properties_scope.codeMirror.destroy();
}
textArea.attr('rows', 1);
formHeight = $('#group_form').height();
windowHeight = $('#group-modal-dialog').height() - 20; //leave a margin of 20px
offset = Math.floor(windowHeight - formHeight);
rows = Math.floor(offset / 24);
rows = (rows < 6) ? 6 : rows;
textArea.attr('rows', rows);
while(rows > 6 && $('#group_form').height() > $('#group-modal-dialog').height()) {
rows--;
textArea.attr('rows', rows);
}
ParseTypeChange({ scope: properties_scope, field_id: textareaID, onReady: waitStop });
}
// Set modal dimensions based on viewport width
ww = $(document).width();
wh = $('body').height();
if (ww > 1199) {
// desktop
x = 675;
y = (800 > wh) ? wh - 15 : 800;
maxrows = 18;
} else if (ww <= 1199 && ww >= 768) {
x = 550;
y = (770 > wh) ? wh - 15 : 770;
maxrows = 12;
} else {
x = (ww - 20);
y = (770 > wh) ? wh - 15 : 770;
maxrows = 10;
}
// Create the modal
$('#group-modal-dialog').dialog({
buttons: {
'Cancel': function() {
modal_scope.cancelModal();
},
'Save': function () {
modal_scope.saveGroup();
}
},
modal: true,
width: x,
height: y,
autoOpen: false,
minWidth: 440,
title: (mode === 'edit') ? 'Edit Group' : 'Add Group',
closeOnEscape: false,
create: function () {
$('.ui-dialog[aria-describedby="group-modal-dialog"]').find('.ui-dialog-titlebar button').empty().attr({'class': 'close'}).text('x');
$('.ui-dialog[aria-describedby="group-modal-dialog"]').find('.ui-dialog-buttonset button').each(function () {
var c, h, i, l;
l = $(this).text();
if (l === 'Cancel') {
h = "fa-times";
c = "btn btn-default";
i = "group-close-button";
$(this).attr({
'class': c,
'id': i
}).html("<i class=\"fa " + h + "\"></i> Cancel");
} else if (l === 'Save') {
h = "fa-check";
c = "btn btn-primary";
i = "group-save-button";
$(this).attr({
'class': c,
'id': i
}).html("<i class=\"fa " + h + "\"></i> Save");
}
});
},
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="group-modal-dialog"]'),
titleHeight = dialog.find('.ui-dialog-titlebar').outerHeight(),
buttonHeight = dialog.find('.ui-dialog-buttonpane').outerHeight(),
content = dialog.find('#group-modal-dialog'),
w;
content.width(dialog.width() - 28);
content.css({ height: (dialog.height() - titleHeight - buttonHeight - 10) });
if ($('#group_tabs .active a').text() === 'Properties') {
textareaResize('group_variables', properties_scope);
}
else if ($('#group_tabs .active a').text() === 'Schedule') {
w = $('#group_tabs').width() - 18;
$('#schedules-overlay').width(w);
$('#schedules-form-container').width(w);
SetSchedulesInnerDialogSize();
}
},
close: function () {
// Destroy on close
$('.tooltip').each(function () {
// Remove any lingering tooltip <div> elements
$(this).remove();
});
$('.popover').each(function () {
// remove lingering popover <div> elements
$(this).remove();
});
if (properties_scope.codeMirror) {
properties_scope.codeMirror.destroy();
}
if (sources_scope.codeMirror) {
sources_scope.codeMirror.destroy();
}
$('#properties-tab').empty();
$('#sources-tab').empty();
$('#schedules-list').empty();
$('#schedules-form').empty();
$('#schedules-detail').empty();
$('#group-modal-dialog').hide();
$('#group-modal-dialog').dialog('destroy');
modal_scope.cancelModal();
},
open: function () {
function updateButtonStatus(isValid) {
$('.ui-dialog[aria-describedby="group-modal-dialog"]').find('.btn-primary').prop('disabled', !isValid);
}
form_scope.$watch('group_form.$valid', updateButtonStatus);
source_form_scope.$watch('source_form.$valid', updateButtonStatus);
$('#group_name').focus();
Wait('stop');
}
});
$('#group_tabs a[data-toggle="tab"]').on('show.bs.tab', function (e) {
if ($(e.target).text() === 'Properties') {
Wait('start');
setTimeout(function(){ textareaResize('group_variables'); }, 300);
}
else if ($(e.target).text() === 'Source') {
if (sources_scope.source && (sources_scope.source.value === 'ec2')) {
Wait('start');
ParseTypeChange({ scope: sources_scope, variable: 'source_vars', parse_variable: SourceForm.fields.source_vars.parseTypeName,
field_id: 'source_source_vars', onReady: waitStop });
} else if (sources_scope.source && (sources_scope.source.value === 'vmware' ||
sources_scope.source.value === 'openstack')) {
Wait('start');
ParseTypeChange({ scope: sources_scope, variable: 'inventory_variables', parse_variable: SourceForm.fields.inventory_variables.parseTypeName,
field_id: 'source_inventory_variables', onReady: waitStop });
}
else if (sources_scope.source && (sources_scope.source.value === 'custom')) {
Wait('start');
ParseTypeChange({ scope: sources_scope, variable: 'extra_vars', parse_variable: SourceForm.fields.extra_vars.parseTypeName,
field_id: 'source_extra_vars', onReady: waitStop });
}
}
else if ($(e.target).text() === 'Schedule') {
$('#schedules-overlay').hide();
}
});
if (modal_scope.groupVariablesLoadedRemove) {
modal_scope.groupVariablesLoadedRemove();
}
modal_scope.groupVariablesLoadedRemove = modal_scope.$on('groupVariablesLoaded', function () {
if (mode === 'edit' &&
group.has_inventory_sources &&
Empty(group.summary_fields.inventory_source.source) &&
sources_scope.source &&
sources_scope.source.value !== 'manual') {
modal_scope.showSourceTab = false;
} else {
modal_scope.showSourceTab = true;
}
modal_scope.showSchedulesTab = (mode === 'edit' && sources_scope.source && sources_scope.source.value!=='manual') ? true : false;
if (mode === 'edit' && modal_scope.showSourceTab) {
// the use has access to the source tab, so they may create a schedule
GroupsScheduleListInit({ scope: modal_scope, url: schedules_url });
}
$('#group_tabs a:first').tab('show');
Wait('start');
$('#group-modal-dialog').dialog('open');
setTimeout(function() { textareaResize('group_variables', properties_scope); }, 300);
});
// After the group record is loaded, retrieve related data.
// jt-- i'm changing this to act sequentially: first load properties, then sources, then schedule
// I accomplished this by adding "LoadSourceData" which will run the source retrieval code after the property
// variables are set.
if (modal_scope.groupLoadedRemove) {
modal_scope.groupLoadedRemove();
}
modal_scope.groupLoadedRemove = modal_scope.$on('groupLoaded', function () {
if (properties_scope.variable_url) {
// get group variables
Rest.setUrl(properties_scope.variable_url);
Rest.get()
.success(function (data) {
properties_scope.variables = ParseVariableString(data);
master.variables = properties_scope.variables;
modal_scope.$emit('LoadSourceData');
//modal_scope.$emit('groupVariablesLoaded'); jt- this needs to get called after sources are loaded
})
.error(function (data, status) {
properties_scope.variables = null;
ProcessErrors(modal_scope, data, status, null, { hdr: 'Error!',
msg: 'Failed to retrieve group variables. GET returned status: ' + status });
});
} else {
properties_scope.variables = "---";
master.variables = properties_scope.variables;
modal_scope.$emit('LoadSourceData');
//properties_scope.$emit('groupVariablesLoaded');
}
});
// JT -- this gets called after the properties & properties variables are loaded, and is emitted from (groupLoaded)
if (modal_scope.removeLoadSourceData) {
modal_scope.removeLoadSourceData();
}
modal_scope.removeLoadSourceData = modal_scope.$on('LoadSourceData', function () {
if (sources_scope.source_url) {
// get source data
Rest.setUrl(sources_scope.source_url);
Rest.get()
.success(function (data) {
var fld, i, j, flag, found, set, opts, list, form;
form = SourceForm;
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) {
sources_scope[flag.name] = data[flag.name];
master[flag.name] = sources_scope[flag.name];
}
}
}
if (fld === 'source') {
found = false;
data.source = (data.source === "" ) ? "manual" : data.source;
for (i = 0; i < sources_scope.source_type_options.length; i++) {
if (sources_scope.source_type_options[i].value === data.source) {
sources_scope.source = sources_scope.source_type_options[i];
found = true;
}
}
if (!found || sources_scope.source.value === "manual") {
sources_scope.groupUpdateHide = true;
} else {
sources_scope.groupUpdateHide = false;
}
master.source = sources_scope.source;
} else if (fld === 'source_vars') {
// Parse source_vars, converting to YAML.
sources_scope.source_vars = ParseVariableString(data.source_vars);
master.source_vars = sources_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;
}
else if (data[fld] !== undefined) {
sources_scope[fld] = data[fld];
master[fld] = sources_scope[fld];
}
if (form.fields[fld].sourceModel && data.summary_fields &&
data.summary_fields[form.fields[fld].sourceModel]) {
sources_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];
}
}
sources_scope.sourceChange(); //set defaults that rely on source value
if (data.source_regions) {
if (data.source === 'ec2' ||
data.source === 'rax' ||
data.source === 'gce' ||
data.source === 'azure') {
if (data.source === 'ec2') {
set = sources_scope.ec2_regions;
} else if (data.source === 'rax') {
set = sources_scope.rax_regions;
} else if (data.source === 'gce') {
set = sources_scope.gce_regions;
} else if (data.source === 'azure') {
set = sources_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: "#source_source_regions",
opts: opts
});
}
} else {
// If empty, default to all
master.source_regions = [{
id: 'all',
text: 'All'
}];
}
if (data.group_by && data.source === 'ec2') {
set = sources_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: "#source_group_by",
opts: opts
});
}
sources_scope.group_update_url = data.related.update;
modal_scope.$emit('groupVariablesLoaded'); // JT-- "groupVariablesLoaded" is where the schedule info is loaded, so I make a call after the sources_scope.source has been loaded
//Wait('stop');
})
.error(function (data, status) {
sources_scope.source = "";
ProcessErrors(modal_scope, data, status, null, { hdr: 'Error!',
msg: 'Failed to retrieve inventory source. GET status: ' + status });
});
}
else {
modal_scope.$emit('groupVariablesLoaded'); // JT-- "groupVariablesLoaded" is where the schedule info is loaded, so I make a call after the sources_scope.source has been loaded
}
});
if (sources_scope.removeScopeSourceTypeOptionsReady) {
sources_scope.removeScopeSourceTypeOptionsReady();
}
sources_scope.removeScopeSourceTypeOptionsReady = sources_scope.$on('sourceTypeOptionsReady', function() {
if (mode === 'add') {
sources_scope.source = Find({
list: sources_scope.source_type_options,
key: 'value',
val: ''
});
modal_scope.showSchedulesTab = false;
}
});
if (modal_scope.removeChoicesComplete) {
modal_scope.removeChoicesComplete();
}
modal_scope.removeChoicesComplete = modal_scope.$on('choicesCompleteGroup', function () {
// Retrieve detail record and prepopulate the form
Rest.setUrl(defaultUrl);
Rest.get()
.success(function (data) {
group = data;
for (var fld in GroupForm.fields) {
if (data[fld]) {
properties_scope[fld] = data[fld];
master[fld] = properties_scope[fld];
}
}
schedules_url = data.related.inventory_source + 'schedules/';
properties_scope.variable_url = data.related.variable_data;
sources_scope.source_url = data.related.inventory_source;
modal_scope.$emit('groupLoaded');
})
.error(function (data, status) {
ProcessErrors(modal_scope, data, status, { hdr: 'Error!',
msg: 'Failed to retrieve group: ' + defaultUrl + '. GET status: ' + status });
});
});
choicesReady = 0;
if (sources_scope.removeChoicesReady) {
sources_scope.removeChoicesReady();
}
sources_scope.removeChoicesReady = sources_scope.$on('choicesReadyGroup', function () {
choicesReady++;
if (choicesReady === 2) {
if (mode === 'edit') {
modal_scope.$emit('choicesCompleteGroup');
}
else {
properties_scope.variables = "---";
master.variables = properties_scope.variables;
modal_scope.$emit('groupVariablesLoaded');
}
}
});
// Load options for source regions
GetChoices({
scope: sources_scope,
url: GetBasePath('inventory_sources'),
field: 'source_regions',
variable: 'rax_regions',
choice_name: 'rax_region_choices',
callback: 'choicesReadyGroup'
});
GetChoices({
scope: sources_scope,
url: GetBasePath('inventory_sources'),
field: 'source_regions',
variable: 'ec2_regions',
choice_name: 'ec2_region_choices',
callback: 'choicesReadyGroup'
});
GetChoices({
scope: sources_scope,
url: GetBasePath('inventory_sources'),
field: 'source_regions',
variable: 'gce_regions',
choice_name: 'gce_region_choices',
callback: 'choicesReadyGroup'
});
GetChoices({
scope: sources_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: sources_scope,
url: GetBasePath('inventory_sources'),
field: 'group_by',
variable: 'ec2_group_by',
choice_name: 'ec2_group_by_choices',
callback: 'choicesReadyGroup'
});
Wait('start');
if (parent_scope.removeAddTreeRefreshed) {
parent_scope.removeAddTreeRefreshed();
}
parent_scope.removeAddTreeRefreshed = parent_scope.$on('GroupTreeRefreshed', function() {
// Clean up
Wait('stop');
if (modal_scope.searchCleanUp) {
modal_scope.searchCleanup();
}
try {
$('#group-modal-dialog').dialog('close');
}
catch(e) {
// ignore
}
});
if (modal_scope.removeSaveComplete) {
modal_scope.removeSaveComplete();
}
modal_scope.removeSaveComplete = modal_scope.$on('SaveComplete', function (e, error) {
if (!error) {
modal_scope.cancelModal();
}
});
if (modal_scope.removeFormSaveSuccess) {
modal_scope.removeFormSaveSuccess();
}
modal_scope.removeFormSaveSuccess = modal_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: ((sources_scope.source && sources_scope.source.value!=='manual') ? sources_scope.source.value : ''),
source_path: sources_scope.source_path,
credential: sources_scope.credential,
overwrite: sources_scope.overwrite,
overwrite_vars: sources_scope.overwrite_vars,
source_script: sources_scope.inventory_script,
update_on_launch: sources_scope.update_on_launch,
update_cache_timeout: (sources_scope.update_cache_timeout || 0)
};
// Create a string out of selected list of regions
if(sources_scope.source_regions){
regions = $('#source_source_regions').select2("data");
r = [];
for (i = 0; i < regions.length; i++) {
r.push(regions[i].id);
}
data.source_regions = r.join();
}
if (sources_scope.source && (sources_scope.source.value === 'ec2')) {
data.instance_filters = sources_scope.instance_filters;
// Create a string out of selected list of regions
group_by = $('#source_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 (sources_scope.source && (sources_scope.source.value === 'ec2' )) {
// for ec2, validate variable data
data.source_vars = ToJSON(sources_scope.envParseType, sources_scope.source_vars, true);
}
if (sources_scope.source && (sources_scope.source.value === 'custom')) {
data.source_vars = ToJSON(sources_scope.envParseType, sources_scope.extra_vars, true);
}
if (sources_scope.source && (sources_scope.source.value === 'vmware' ||
sources_scope.source.value === 'openstack')) {
data.source_vars = ToJSON(sources_scope.envParseType, sources_scope.inventory_variables, true);
}
// the API doesn't expect the credential to be passed with a custom inv script
if(sources_scope.source && sources_scope.source.value === 'custom'){
delete(data.credential);
}
if (!parseError) {
Rest.setUrl(sources_scope.source_url);
Rest.put(data)
.success(function () {
modal_scope.$emit('SaveComplete', false);
})
.error(function (data, status) {
$('#group_tabs a:eq(1)').tab('show');
ProcessErrors(sources_scope, data, status, SourceForm, { hdr: 'Error!',
msg: 'Failed to update group inventory source. PUT status: ' + status });
});
}
});
if (modal_scope.removeUpdateVariables) {
modal_scope.removeUpdateVariables();
}
modal_scope.removeUpdateVariables = modal_scope.$on('updateVariables', function(e, data, url) {
Rest.setUrl(url);
Rest.put(data)
.success(function () {
modal_scope.$emit('formSaveSuccess');
})
.error(function (data, status) {
$('#group_tabs a:eq(0)').tab('show');
ProcessErrors(modal_scope, data, status, null, { hdr: 'Error!',
msg: 'Failed to update group variables. PUT status: ' + status });
});
});
// Cancel
modal_scope.cancelModal = function () {
try {
$('#group-modal-dialog').dialog('close');
}
catch(e) {
//ignore
}
if (modal_scope.searchCleanup) {
modal_scope.searchCleanup();
}
if (parent_scope.restoreSearch) {
parent_scope.restoreSearch();
}
else {
Wait('stop');
}
};
// Save
modal_scope.saveGroup = function () {
Wait('start');
var fld, data, json_data;
try {
json_data = ToJSON(properties_scope.parseType, properties_scope.variables);
data = {};
for (fld in GroupForm.fields) {
if (fld !== 'variables') {
data[fld] = properties_scope[fld];
}
}
data.inventory = inventory_id;
Rest.setUrl(defaultUrl);
if (mode === 'edit' || (mode === 'add' && group_created)) {
Rest.put(data)
.success(function () {
if (properties_scope.variables) {
modal_scope.$emit('updateVariables', json_data, properties_scope.variable_url);
}
else {
modal_scope.$emit('formSaveSuccess');
}
})
.error(function (data, status) {
$('#group_tabs a:eq(0)').tab('show');
ProcessErrors(properties_scope, data, status, GroupForm, { 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;
sources_scope.source_url = data.related.inventory_source;
if (properties_scope.variables && properties_scope.variables !== "---") {
modal_scope.$emit('updateVariables', json_data, data.related.variable_data);
}
else {
modal_scope.$emit('formSaveSuccess');
}
})
.error(function (data, status) {
$('#group_tabs a:eq(0)').tab('show');
ProcessErrors(properties_scope, data, status, GroupForm, { 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
modal_scope.updateGroup = function () {
if (sources_scope.source === "manual" || sources_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 (sources_scope.status === 'updating') {
Alert('Update in Progress', 'The inventory update process is currently running for group <em>' +
$filter('sanitize')(sources_scope.summary_fields.group.name) + '</em>. Use the Refresh button to monitor the status.', 'alert-info', null, null, null, null, true);
} else {
InventoryUpdate({
scope: parent_scope,
group_id: group_id,
url: properties_scope.group_update_url,
group_name: properties_scope.name,
group_source: sources_scope.source.value
});
}
};
// Change the lookup and regions when the source changes
sources_scope.sourceChange = function () {
parent_scope.showSchedulesTab = (mode === 'edit' && sources_scope.source && sources_scope.source.value!=="manual") ? true : false;
SourceChange({ scope: sources_scope, form: SourceForm });
};
};
}
])
/**
*
* Set's up the process for deleting a group from an inventory page
*
*/
.factory('GroupsDelete', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'GroupForm', 'GenerateForm',
'Prompt', 'ProcessErrors', 'GetBasePath', 'Wait', 'BuildTree', 'Find', 'CreateDialog',
function ($rootScope, $location, $log, $routeParams, 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 = "<dl><dt>Delete</dt><dd>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.</dd>\n" +
"<dt style=\"margin-top: 5px;\">Promote</dt><dd>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.</dd></dl>\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', '$routeParams', 'Rest', 'Alert', 'GenerateForm',
'Prompt', 'ProcessErrors', 'GetBasePath', 'FormatDate', 'InventoryStatusForm', 'Wait',
function ($rootScope, $location, $log, $routeParams, 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 = "<div id=\"status-modal-dialog\" title=\"" + group_name + "- Inventory Sync\">\n" +
"<div id=\"form-container\" style=\"width: 100%;\"></div></div>\n";
$('#inventory-modal-container').empty().append(html);
scope = generator.inject(form, {
mode: 'edit',
id: 'form-container',
breadCrumbs: false,
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 <div> elements
$(this).remove();
});
$('.popover').each(function () {
// remove lingering popover <div> 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
});
});
}
};
}
]);