Moved inventory detail load procedure from inventory controller to the helper so that it can be called when the detail page first loads and anytime we need to refresh tree data. Working on a way to refresh tree data, re-open previously open nodes and re-select previously active node after add/edit/delete of a group. Any change to tree data should be instantly reflect in the tree in an ajax/async fashion.

This commit is contained in:
chouseknecht 2013-06-03 07:08:08 -04:00
parent 9ca489d5ab
commit b19e1dd97a
10 changed files with 593 additions and 139 deletions

View File

@ -348,4 +348,8 @@
#hosts-title {
margin-bottom: 15px;
}
#tree-view {
min-height: 100px;
}

View File

@ -49,7 +49,8 @@ angular.module('ansible', [
'JobFormDefinition',
'JobEventsListDefinition',
'JobEventFormDefinition',
'JobHostDefinition'
'JobHostDefinition',
'GroupsHelper'
])
.config(['$routeProvider', function($routeProvider) {
$routeProvider.

View File

@ -147,13 +147,13 @@ InventoriesList.$inject = [ '$scope', '$rootScope', '$location', '$log', '$route
function InventoriesAdd ($scope, $rootScope, $compile, $location, $log, $routeParams, InventoryForm,
GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, ReturnToCaller, ClearScope,
GenerateList, OrganizationList, SearchInit, PaginateInit, LookUpInit)
GenerateList, OrganizationList, SearchInit, PaginateInit, LookUpInit, GetBasePath)
{
ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior
//scope.
// Inject dynamic view
var defaultUrl = '/api/v1/inventories/';
var defaultUrl = GetBasePath('inventory');
var form = InventoryForm;
var generator = GenerateForm;
var scope = generator.inject(form, {mode: 'add', related: false});
@ -197,13 +197,13 @@ function InventoriesAdd ($scope, $rootScope, $compile, $location, $log, $routePa
InventoriesAdd.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'InventoryForm', 'GenerateForm',
'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'ReturnToCaller', 'ClearScope', 'GenerateList',
'OrganizationList', 'SearchInit', 'PaginateInit', 'LookUpInit' ];
'OrganizationList', 'SearchInit', 'PaginateInit', 'LookUpInit', 'GetBasePath' ];
function InventoriesEdit ($scope, $rootScope, $compile, $location, $log, $routeParams, InventoryForm,
GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, RelatedSearchInit,
RelatedPaginateInit, ReturnToCaller, ClearScope, LookUpInit, Prompt,
OrganizationList, TreeInit, GetBasePath)
OrganizationList, TreeInit, GetBasePath, GroupsList, GroupsEdit, LoadInventory)
{
ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior
//scope.
@ -214,11 +214,8 @@ function InventoriesEdit ($scope, $rootScope, $compile, $location, $log, $routeP
var scope = generator.inject(form, {mode: 'edit', related: true});
generator.reset();
var base = $location.path().replace(/^\//,'').split('/')[0];
var master = {};
var id = $routeParams.id;
var relatedSets = {};
var hostsUrl;
scope['inventory_id'] = id;
// Retrieve each related set and any lookups
@ -228,47 +225,11 @@ function InventoriesEdit ($scope, $rootScope, $compile, $location, $log, $routeP
scope.inventoryLoadedRemove = scope.$on('inventoryLoaded', function() {
scope.groupTitle = 'All Hosts';
scope.createButtonShow = false;
scope.search(relatedSets['hosts'].iterator);
scope.search(scope.relatedSets['hosts'].iterator);
TreeInit(scope.TreeParams);
});
// Retrieve detail record and prepopulate the form
Rest.setUrl(defaultUrl + ':id/');
Rest.get({ params: {id: id} })
.success( function(data, status, headers, config) {
LoadBreadCrumbs({ path: '/inventories/' + id, title: data.name });
for (var fld in form.fields) {
if (data[fld]) {
scope[fld] = data[fld];
master[fld] = scope[fld];
}
if (form.fields[fld].type == 'lookup' && 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] =
scope[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField];
}
}
LookUpInit({
scope: scope,
form: form,
current_item: data.organization,
list: OrganizationList,
field: 'organization'
});
// Load the tree view
TreeInit({ scope: scope, inventory: data });
hostsUrl = data.related.hosts;
relatedSets['hosts'] = { url: hostsUrl, iterator: 'host' };
RelatedSearchInit({ scope: scope, form: form, relatedSets: relatedSets });
RelatedPaginateInit({ scope: scope, relatedSets: relatedSets });
scope.$emit('inventoryLoaded');
})
.error( function(data, status, headers, config) {
ProcessErrors(scope, data, status, form,
{ hdr: 'Error!', msg: 'Failed to retrieve inventory: ' + $routeParams.id + '. GET status: ' + status });
});
LoadInventory({ scope: scope });
// Save changes to the parent
scope.formSave = function() {
@ -291,8 +252,8 @@ function InventoriesEdit ($scope, $rootScope, $compile, $location, $log, $routeP
// Cancel
scope.formReset = function() {
generator.reset();
for (var fld in master) {
scope[fld] = master[fld];
for (var fld in scope.master) {
scope[fld] = scope.master[fld];
}
};
@ -419,37 +380,42 @@ function InventoriesEdit ($scope, $rootScope, $compile, $location, $log, $routeP
url = node.attr('all');
scope.groupAddHide = false;
scope.groupEditHide =false;
scope.groupDeleteHide = false;
scope.createButtonShow = true;
scope.group_id = node.attr('group_id');
scope.groupName = n.data;
scope.groupTitle = n.data;
scope.groupTitle += (node.attr('description')) ? ' -' + node.attr('description') : '';
}
else if (type == 'all-hosts-group') {
url = node.attr('url');
scope.createButtonShow = false;
scope.groupName = 'All Hosts';
scope.groupTitle = 'All Hosts';
}
else if (type == 'inventory') {
url = node.attr('hosts');
scope.groupAddHide = false;
scope.groupEditHide =true;
scope.groupDeleteHide = true;
scope.createButtonShow = false;
scope.groupName = 'All Hosts';
scope.groupTitle = 'All Hosts';
scope.group_id = null;
}
relatedSets['hosts'] = { url: url, iterator: 'host' };
RelatedSearchInit({ scope: scope, form: form, relatedSets: relatedSets });
RelatedPaginateInit({ scope: scope, relatedSets: relatedSets });
scope.relatedSets['hosts'] = { url: url, iterator: 'host' };
RelatedSearchInit({ scope: scope, form: form, relatedSets: scope.relatedSets });
RelatedPaginateInit({ scope: scope, relatedSets: scope.relatedSets });
scope.search('host');
scope.$digest();
});
scope.addGroup = function() {
GroupsList({ "inventory_id": id, group_id: scope.group_id });
}
scope.editGroup = function() {
GroupsEdit({ "inventory_id": id, group_id: scope.group_id });
}
}
InventoriesEdit.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'InventoryForm',
'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'RelatedSearchInit',
'RelatedPaginateInit', 'ReturnToCaller', 'ClearScope', 'LookUpInit', 'Prompt',
'OrganizationList', 'TreeInit', 'GetBasePath'
'OrganizationList', 'TreeInit', 'GetBasePath', 'GroupsList', 'GroupsEdit', 'LoadInventory'
];

View File

@ -13,7 +13,7 @@ angular.module('GroupFormDefinition', [])
addTitle: 'Create Group', //Legend in add mode
editTitle: '{{ name }}', //Legend in edit mode
name: 'group', //Form name attribute
well: true, //Wrap the form with TB well
well: false, //Wrap the form with TB well
fields: {
name: {
@ -34,7 +34,6 @@ angular.module('GroupFormDefinition', [])
addRequired: false,
editRequird: false,
rows: 10,
class: 'span12',
default: "\{\}",
dataTitle: 'Group Variables',
dataPlacement: 'right',

View File

@ -0,0 +1,381 @@
/*********************************************
* Copyright (c) 2013 AnsibleWorks, Inc.
*
* InventoryHelper
* Routines for building the tree. Everything related to the tree is here except
* for the menu piece. The routine for building the menu is in InventoriesEdit controller
* (controllers/Inventories.js)
*
*/
angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'GroupListDefinition',
'SearchHelper', 'PaginateHelper', 'ListGenerator', 'AuthService', 'GroupsHelper',
'InventoryHelper'
])
.factory('GroupsList', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'GroupList', 'GenerateList',
'Prompt', 'SearchInit', 'PaginateInit', 'ProcessErrors', 'GetBasePath', 'GroupsAdd', 'RefreshTree',
function($rootScope, $location, $log, $routeParams, Rest, Alert, GroupList, GenerateList, LoadBreadCrumbs, SearchInit,
PaginateInit, ProcessErrors, GetBasePath, GroupsAdd, RefreshTree) {
return function(params) {
var inventory_id = params.inventory_id;
var group_id = (params.group_id !== undefined) ? params.group_id : null;
var list = GroupList;
var defaultUrl = GetBasePath('inventory') + inventory_id + '/groups/';
var view = GenerateList;
var scope = view.inject(GroupList, {
id: 'form-modal-body',
mode: 'select',
breadCrumbs: false,
selectButton: false
});
scope.formModalActionLabel = 'Finished'
scope.formModalHeader = 'Add Group'
$('#form-modal').modal();
scope.selected = [];
if (scope.PostRefreshRemove) {
scope.PostRefreshRemove();
}
scope.PostRefreshRemove = scope.$on('PostRefresh', function() {
$("tr.success").each(function(index) {
var ngc = $(this).attr('ng-class');
scope[ngc] = "";
});
if ($routeParams.group_id) {
// Remove the current group from the list of available groups, thus
// preventing a group from being added to itself
for (var i=0; i < scope.groups.length; i++) {
if (scope.groups[i].id == $routeParams.group_id) {
scope.groups.splice(i,1);
}
}
}
//scope.$digest();
});
SearchInit({ scope: scope, set: 'groups', list: list, url: defaultUrl });
PaginateInit({ scope: scope, list: list, url: defaultUrl });
scope.search(list.iterator);
/*LoadBreadCrumbs();*/
scope.editGroup = function(id) {
$location.path($location.path() + '/' + id);
}
scope.deleteGroup = function(id, name) {
var action = function() {
var url = defaultUrl;
Rest.setUrl(url);
Rest.post({ id: id, disassociate: 1 })
.success( function(data, status, headers, config) {
$('#prompt-modal').modal('hide');
scope.search(list.iterator);
})
.error( function(data, status, headers, config) {
$('#prompt-modal').modal('hide');
ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Call to ' + url + ' failed. DELETE returned status: ' + status });
});
};
Prompt({ hdr: 'Delete',
body: 'Are you sure you want to remove group' + name + '?',
action: action
});
}
scope.formModalAction = function() {
var url = (group_id) ? GetBasePath('groups') + group_id + '/children/' :
GetBasePath('inventory') + inventory_id + '/groups/';
Rest.setUrl(url);
scope.queue = [];
if (scope.callFinishedRemove) {
scope.callFinishedRemove();
}
scope.callFinishedRemove = scope.$on('callFinished', function() {
// We call the API for each selected item. We need to hang out until all the api
// calls are finished.
if (scope.queue.length == scope.selected.length) {
// All the api calls finished
$('input[type="checkbox"]').prop("checked",false);
scope.selected = [];
var errors = 0;
for (var i=0; i < scope.queue.length; i++) {
if (scope.queue[i].result == 'error') {
errors++;
}
}
if (errors > 0) {
Alert('Error', 'There was an error while adding one or more of the selected groups.');
}
else {
$('#form-modal').modal('hide');
}
}
});
if (scope.selected.length > 0 ) {
var group;
for (var i=0; i < scope.selected.length; i++) {
group = null;
for (var j=0; j < scope.groups.length; j++) {
if (scope.groups[j].id == scope.selected[i]) {
group = scope.groups[j];
}
}
if (group !== null) {
Rest.post(group)
.success( function(data, status, headers, config) {
scope.queue.push({ result: 'success', data: data, status: status });
scope.$emit('callFinished');
})
.error( function(data, status, headers, config) {
scope.queue.push({ result: 'error', data: data, status: status, headers: headers });
scope.$emit('callFinished');
});
}
}
}
else {
$('#form-modal').modal('hide');
}
}
scope.toggle_group = function(id) {
if (scope[list.iterator + "_" + id + "_class"] == "success") {
scope[list.iterator + "_" + id + "_class"] = "";
document.getElementById('check_' + id).checked = false;
if (scope.selected.indexOf(id) > -1) {
scope.selected.splice(scope.selected.indexOf(id),1);
}
}
else {
scope[list.iterator + "_" + id + "_class"] = "success";
document.getElementById('check_' + id).checked = true;
if (scope.selected.indexOf(id) == -1) {
scope.selected.push(id);
}
}
}
scope.createGroup = function() {
$('#form-modal').modal('hide');
GroupsAdd({ inventory_id: inventory_id, group_id: group_id });
}
}
}])
.factory('GroupsAdd', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'GroupForm', 'GenerateForm',
'Prompt', 'ProcessErrors', 'GetBasePath',
function($rootScope, $location, $log, $routeParams, Rest, Alert, GroupForm, GenerateForm, Prompt, ProcessErrors,
GetBasePath) {
return function(params) {
var inventory_id = params.inventory_id;
var group_id = (params.group_id !== undefined) ? params.group_id : null;
// Inject dynamic view
var defaultUrl = (group_id !== null) ? GetBasePath('groups') + group_id + '/children/' :
GetBasePath('inventory') + inventory_id + '/groups/';
var form = GroupForm;
var generator = GenerateForm;
var scope = generator.inject(form, {mode: 'add', modal: true, related: false});
scope.formModalActionLabel = 'Save'
scope.formModalHeader = 'Create Group'
generator.reset();
var master={};
// Save
scope.formModalAction = function() {
try {
// Make sure we have valid JSON
var myjson = JSON.parse(scope.variables);
var data = {}
for (var fld in form.fields) {
if (fld != 'variables') {
data[fld] = scope[fld];
}
}
if (inventory_id) {
data['inventory'] = inventory_id;
}
Rest.setUrl(defaultUrl);
Rest.post(data)
.success( function(data, status, headers, config) {
if (scope.variables) {
Rest.setUrl(data.related.variable_data);
Rest.put({data: scope.variables})
.success( function(data, status, headers, config) {
$('#form-modal').modal('hide');
})
.error( function(data, status, headers, config) {
ProcessErrors(scope, data, status, form,
{ hdr: 'Error!', msg: 'Failed to add group varaibles. PUT returned status: ' + status });
});
}
})
.error( function(data, status, headers, config) {
ProcessErrors(scope, data, status, form,
{ hdr: 'Error!', msg: 'Failed to add new group. Post returned status: ' + status });
});
}
catch(err) {
Alert("Error", "Error parsing group variables. Expecting valid JSON. Parser returned " + err);
}
}
// Cancel
scope.formReset = function() {
// Defaults
generator.reset();
};
}
}])
.factory('GroupsEdit', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'GroupForm', 'GenerateForm',
'Prompt', 'ProcessErrors', 'GetBasePath',
function($rootScope, $location, $log, $routeParams, Rest, Alert, GroupForm, GenerateForm, Prompt, ProcessErrors,
GetBasePath) {
return function(params) {
var group_id = params.group_id;
var generator = GenerateForm;
var form = GroupForm;
var defaultUrl = GetBasePath('groups') + group_id + '/';
var scope = generator.inject(form, { mode: 'edit', modal: true, related: false});
generator.reset();
var master = {};
var relatedSets = {};
scope.formModalActionLabel = 'Save'
scope.formModalHeader = 'Edit Group'
// After the group record is loaded, retrieve any group variables
if (scope.groupLoadedRemove) {
scope.groupLoadedRemove();
}
scope.groupLoadedRemove = scope.$on('groupLoaded', function() {
for (var set in relatedSets) {
scope.search(relatedSets[set].iterator);
}
if (scope.variable_url) {
Rest.setUrl(scope.variable_url);
Rest.get()
.success( function(data, status, headers, config) {
if ($.isEmptyObject(data.data)) {
scope.variables = "\{\}";
}
else {
scope.variables = data.data;
}
})
.error( function(data, status, headers, config) {
scope.variables = null;
ProcessErrors(scope, data, status, form,
{ hdr: 'Error!', msg: 'Failed to retrieve host variables. GET returned status: ' + status });
});
}
else {
scope.variables = "\{\}";
}
});
// Retrieve detail record and prepopulate the form
Rest.setUrl(defaultUrl);
Rest.get()
.success( function(data, status, headers, config) {
LoadBreadCrumbs();
for (var fld in form.fields) {
if (data[fld]) {
scope[fld] = data[fld];
master[fld] = scope[fld];
}
}
var related = data.related;
for (var set in form.related) {
if (related[set]) {
relatedSets[set] = { url: related[set], iterator: form.related[set].iterator };
}
}
scope.variable_url = data.related.variable_data;
// Initialize related search functions. Doing it here to make sure relatedSets object is populated.
RelatedSearchInit({ scope: scope, form: form, relatedSets: relatedSets });
RelatedPaginateInit({ scope: scope, relatedSets: relatedSets });
scope.$emit('groupLoaded');
})
.error( function(data, status, headers, config) {
ProcessErrors(scope, data, status, form,
{ hdr: 'Error!', msg: 'Failed to retrieve group: ' + id + '. GET status: ' + status });
});
// Save changes to the parent
scope.formModalAction = function() {
try {
// Make sure we have valid JSON
var myjson = JSON.parse(scope.variables);
var data = {}
for (var fld in form.fields) {
data[fld] = scope[fld];
}
Rest.setUrl(defaultUrl);
Rest.put(data)
.success( function(data, status, headers, config) {
if (scope.variables) {
//update group variables
Rest.setUrl(GetBasePath('groups') + data.id + '/variable_data/');
Rest.put({data: scope.variables})
.success( function(data, status, headers, config) {
var base = $location.path().replace(/^\//,'').split('/')[0];
(base == 'groups') ? ReturnToCaller() : ReturnToCaller(1);
})
.error( function(data, status, headers, config) {
ProcessErrors(scope, data, status, form,
{ hdr: 'Error!', msg: 'Failed to update group varaibles. PUT returned status: ' + status });
});
}
$('#form-modal').modal('hide');
})
.error( function(data, status, headers, config) {
ProcessErrors(scope, data, status, form,
{ hdr: 'Error!', msg: 'Failed to update group: ' + id + '. PUT status: ' + status });
});
}
catch(err) {
Alert("Error", "Error parsing group variables. Expecting valid JSON. Parser returned " + err);
}
};
// Cancel
scope.formReset = function() {
generator.reset();
for (var fld in master) {
scope[fld] = master[fld];
}
}
}
}]);

View File

@ -9,10 +9,12 @@
*/
angular.module('InventoryHelper', [ 'RestServices', 'Utilities', 'OrganizationListDefinition',
'SearchHelper', 'PaginateHelper', 'ListGenerator', 'AuthService'
'SearchHelper', 'PaginateHelper', 'ListGenerator', 'AuthService',
'InventoryHelper', 'RelatedSearchHelper', 'RelatedPaginateHelper',
'InventoryFormDefinition'
])
.factory('TreeInit', ['Alert', 'Rest', 'Authorization', '$http',
.factory('LoadTreeData', ['Alert', 'Rest', 'Authorization', '$http',
function(Alert, Rest, Authorization, $http) {
return function(params) {
@ -24,15 +26,95 @@ angular.module('InventoryHelper', [ 'RestServices', 'Utilities', 'OrganizationLi
var inventory_url = inventory.url;
var inventory_id = inventory.id;
var inventory_descr = inventory.description;
var tree_id = '#tree-view';
var idx=0;
var treeData = [];
// Ater inventory top-level hosts, load top-level groups
if (scope.HostLoadedRemove) {
scope.HostLoadedRemove();
}
scope.HostLoadedRemove = scope.$on('hostsLoaded', function() {
Rest.setUrl(groups + '?order_by=name');
Rest.get()
.success( function(data, status, headers, config) {
for (var i=0; i < data.results.length; i++) {
treeData[0].children.push({
data: {
title: data.results[i].name
},
attr: {
id: idx,
group_id: data.results[i].id,
type: 'group',
name: data.results[i].name,
description: data.results[i].description,
inventory: data.results[i].inventory,
all: data.results[i].related.all_hosts,
children: data.results[i].related.children,
hosts: data.results[i].related.hosts,
variable: data.results[i].related.variable_data
},
state: 'closed'
});
idx++;
}
scope.$emit('buildTree', treeData, idx);
})
.error( function(data, status, headers, config) {
Alert('Error', 'Failed to laod tree data. Url: ' + groups + ' GET status: ' + status);
});
});
// Setup tree_data
Rest.setUrl(hosts + '?order_by=name');
Rest.get()
.success ( function(data, status, headers, config) {
treeData =
[{
data: {
title: inventory_name
},
attr: {
type: 'inventory',
id: 'inventory-node',
url: inventory_url,
'inventory_id': inventory_id,
hosts: hosts,
name: inventory_name,
description: inventory_descr
},
state: 'open',
children:[]
}];
scope.$emit('hostsLoaded');
})
.error ( function(data, status, headers, config) {
Alert('Error', 'Failed to laod tree data. Url: ' + hosts + ' GET status: ' + status);
});
}
}])
.factory('TreeInit', ['Alert', 'Rest', 'Authorization', '$http', 'LoadTreeData',
function(Alert, Rest, Authorization, $http, LoadTreeData) {
return function(params) {
var scope = params.scope;
var inventory = params.inventory;
var groups = inventory.related.root_groups;
var hosts = inventory.related.hosts;
var inventory_name = inventory.name;
var inventory_url = inventory.url;
var inventory_id = inventory.id;
var inventory_descr = inventory.description;
var tree_id = '#tree-view';
// After loading the Inventory top-level data, initialize the tree
if (scope.buildTreeRemove) {
scope.buildTreeRemove();
}
scope.buildTreeRemove = scope.$on('buildTree', function() {
scope.buildTreeRemove = scope.$on('buildTree', function(e, treeData, index) {
var idx = index;
$(tree_id).jstree({
"core": { "initially_open":['inventory-node'] },
"plugins": ['themes', 'json_data', 'ui', 'contextmenu'],
@ -83,75 +165,73 @@ angular.module('InventoryHelper', [ 'RestServices', 'Utilities', 'OrganizationLi
});
// When user clicks on a group, display the related hosts in the list view
$(tree_id).bind("select_node.jstree", function(evt, data){
$(tree_id).bind("select_node.jstree", function(e, data){
//selected node object: data.inst.get_json()[0];
//selected node text: data.inst.get_json()[0].data
scope.$emit('NodeSelect',data.inst.get_json()[0]);
});
});
LoadTreeData(params);
// Ater inventory top-level hosts, load top-level groups
if (scope.HostLoadedRemove) {
scope.HostLoadedRemove();
}
scope.HostLoadedRemove = scope.$on('hostsLoaded', function() {
Rest.setUrl(groups + '?order_by=name');
Rest.get()
.success( function(data, status, headers, config) {
for (var i=0; i < data.results.length; i++) {
treeData[0].children.push({
data: {
title: data.results[i].name
},
attr: {
id: idx,
group_id: data.results[i].id,
type: 'group',
name: data.results[i].name,
description: data.results[i].description,
inventory: data.results[i].inventory,
all: data.results[i].related.all_hosts,
children: data.results[i].related.children,
hosts: data.results[i].related.hosts,
variable: data.results[i].related.variable_data
},
state: 'closed'
});
idx++;
}
scope.$emit('buildTree');
})
.error( function(data, status, headers, config) {
Alert('Error', 'Failed to laod tree data. Url: ' + groups + ' GET status: ' + status);
});
});
}])
// Setup tree_data
Rest.setUrl(hosts + '?order_by=name');
.factory('RefreshTree', ['Alert', 'Rest', 'Authorization', '$http', 'TreeInit',
function(Alert, Rest, Authorization, $http, TreeInit) {
return function(params) {
$('#tree-view').jstree('destroy');
TreeInit(params);
}
}])
.factory('LoadInventory', ['$routeParams', 'Alert', 'Rest', 'Authorization', '$http', 'RefreshTree', 'ProcessErrors',
'RelatedSearchInit', 'RelatedPaginateInit', 'GetBasePath', 'LoadBreadCrumbs', 'InventoryForm',
function($routeParams, Alert, Rest, Authorization, $http, RefreshTree ,ProcessErrors, RelatedSearchInit, RelatedPaginateInit,
GetBasePath, LoadBreadCrumbs, InventoryForm) {
return function(params) {
// Load inventory detail record
var scope = params.scope;
var form = InventoryForm;
scope.relatedSets = [];
scope.master = {};
Rest.setUrl(GetBasePath('inventory') + $routeParams.id + '/');
Rest.get()
.success ( function(data, status, headers, config) {
treeData =
[{
data: {
title: inventory_name
},
attr: {
type: 'inventory',
id: 'inventory-node',
url: inventory_url,
'inventory_id': inventory_id,
hosts: hosts,
name: inventory_name,
description: inventory_descr
},
state: 'open',
children:[]
}];
scope.$emit('hostsLoaded');
})
.error ( function(data, status, headers, config) {
Alert('Error', 'Failed to laod tree data. Url: ' + hosts + ' GET status: ' + status);
});
.success( function(data, status, headers, config) {
LoadBreadCrumbs({ path: '/inventories/' + $routeParams.id, title: data.name });
for (var fld in form.fields) {
if (data[fld]) {
scope[fld] = data[fld];
scope.master[fld] = scope[fld];
}
if (form.fields[fld].type == 'lookup' && 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];
scope.master[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField] =
scope[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField];
}
}
// Load the tree view
scope.TreeParams = { scope: scope, inventory: data };
scope.relatedSets['hosts'] = { url: data.related.hosts, iterator: 'host' };
RelatedSearchInit({ scope: scope, form: form, relatedSets: scope.relatedSets });
RelatedPaginateInit({ scope: scope, relatedSets: scope.relatedSets });
scope.$emit('inventoryLoaded');
})
.error( function(data, status, headers, config) {
ProcessErrors(scope, data, status, form,
{ hdr: 'Error!', msg: 'Failed to retrieve inventory: ' + $routeParams.id + '. GET status: ' + status });
});
}
}]);

View File

@ -14,8 +14,9 @@ angular.module('GroupListDefinition', [])
iterator: 'group',
selectTitle: 'Add Group',
editTitle: 'Groups',
selectInstructions: 'Click on a row to select it, and click Finished when done. Use the green <i class=\"icon-plus\"></i> button to create a new row.',
selectInstructions: 'Click on a row to select it, and Finished when done. Click the green <i class=\"icon-plus\"></i> Add to create a new row.',
index: true,
well: false,
fields: {
name: {
@ -32,7 +33,7 @@ angular.module('GroupListDefinition', [])
label: 'Add',
icon: 'icon-plus',
mode: 'all', // One of: edit, select, all
ngClick: 'addGroup()',
ngClick: 'createGroup()',
class: 'btn-success btn-small',
awToolTip: 'Create a new group'
}

View File

@ -76,9 +76,12 @@ angular.module('FormGenerator', ['GeneratorHelpers'])
element = angular.element(document.getElementById('form-modal-body'));
}
else {
var element = angular.element(document.getElementById('htmlTemplate'));
element = angular.element(document.getElementById('htmlTemplate'));
}
this.mode = options.mode;
this.modal = (options.modal) ? true : false;
this.setForm(form);
element.html(this.build(options)); // Inject the html
this.scope = element.scope(); // Set scope specific to the element we're compiling, avoids circular reference
@ -94,12 +97,9 @@ angular.module('FormGenerator', ['GeneratorHelpers'])
}
if (options.modal) {
(options.mode == 'add') ? scope.formHeader = form.addTitle : form.editTitle;
this.scope.formHeader = (options.mode == 'add') ? form.addTitle : form.editTitle;
$('#form-modal').modal();
}
this.mode = options.mode;
this.modal = (options.modal) ? true : false;
return this.scope;
},
@ -641,8 +641,12 @@ angular.module('FormGenerator', ['GeneratorHelpers'])
if (form.related[itm].type == 'tree') {
html += "<div class=\"span5\">";
html += "<div class=\"inventory-buttons pull-right\">";
html += "<button ng-hide=\"groupAddHide\" id=\"inv-group-add\" class=\"btn btn-mini btn-success\"><i class=\"icon-plus\"></i> Add Group</button>";
html += "<button ng-hide=\"groupEditHide\" id=\"inv-group-edit\" class=\"btn btn-mini btn-success\"><i class=\"icon-edit\"></i> Edit Group</button>";
html += "<button ng-click=\"addGroup()\" ng-hide=\"groupAddHide\" id=\"inv-group-add\" " +
"class=\"btn btn-mini btn-success\"><i class=\"icon-plus\"></i> Add Group</button>";
html += "<button ng-hide=\"groupEditHide\" id=\"inv-group-edit\" class=\"btn btn-mini btn-success\">" +
"<i class=\"icon-edit\"></i> Edit Group</button>";
html += "<button ng-hide=\"groupDeleteHide\" id=\"inv-group-delete\" class=\"btn btn-mini btn-danger\">" +
"<i class=\"icon-remove\"></i> Delete Group</button>";
html += "</div>\n";
html += "<div id=\"tree-view\"></div>\n";
html += "</div>\n";

View File

@ -67,7 +67,10 @@ angular.module('ListGenerator', ['GeneratorHelpers',])
// For options.mode == 'lookup', include the following:
//
// hdr: <lookup dialog header>
//
//
// Inject into a custom element using options.id: <'.selector'>
// Control breadcrumb creation with options.breadCrumbs: <true | false>
//
if (options.mode == 'lookup') {
var element = angular.element(document.getElementById('lookup-modal-body'));
}
@ -130,7 +133,7 @@ angular.module('ListGenerator', ['GeneratorHelpers',])
html += "</div>\n";
}
if (options.mode != 'lookup') {
if (options.mode != 'lookup' && (list.well == undefined || list.well == 'true')) {
html += "<div class=\"well\">\n";
}
@ -161,7 +164,7 @@ angular.module('ListGenerator', ['GeneratorHelpers',])
}
}
}
if (options.mode == 'select') {
if (options.mode == 'select' && (options.selectButton == undefined || options.selectButton == true)) {
html += " <button class=\"btn btn-small btn-success\" aw-tool-tip=\"Complete your selection\" " +
"ng-click=\"finishSelection()\"><i class=\"icon-ok\"></i> Finished</button>\n";
}
@ -272,7 +275,7 @@ angular.module('ListGenerator', ['GeneratorHelpers',])
html += "</tbody>\n";
html += "</table>\n";
if (options.mode != 'lookup') {
if (options.mode != 'lookup' && (list.well == undefined || list.well == 'true')) {
html += "</div>\n"; //well
}

View File

@ -72,6 +72,7 @@
<script src="{{ STATIC_URL }}js/helpers/teams.js"></script>
<script src="{{ STATIC_URL }}js/helpers/JobTemplate.js"></script>
<script src="{{ STATIC_URL }}js/helpers/Lookup.js"></script>
<script src="{{ STATIC_URL }}js/helpers/Groups.js"></script>
<script src="{{ STATIC_URL }}lib/ansible/directives.js"></script>
<script src="{{ STATIC_URL }}lib/ansible/filters.js"></script>
<script src="{{ STATIC_URL }}lib/ansible/api-loader.js"></script>
@ -165,6 +166,20 @@
</div>
</div>
<!-- Generic Form dialog -->
<div id="form-modal" class="modal hide">
<div class="modal-header">
<button type="button" class="close" data-target="#alert-modal"
data-dismiss="modal" aria-hidden="true">&times;</button>
<h3 ng-bind="formModalHeader"></h3>
</div>
<div class="modal-body" id="form-modal-body"></div>
<div class="modal-footer">
<a href="#" data-target="#form-modal" data-dismiss="modal" class="btn btn">Cancel</a>
<a href="" ng-bind="formModalActionLabel" ng-click="formModalAction()" class="btn btn-success"></a>
</div>
</div>
<!-- Alerts/error handling dialog -->
<div id="alert-modal" class="modal hide">
<div class="modal-header">