mirror of
https://github.com/ansible/awx.git
synced 2026-03-22 19:35:02 -02:30
Inventory refactory: working on inventory group copy/move via drag-n-drop. The graphical piece is mostly working.
This commit is contained in:
@@ -319,7 +319,7 @@ InventoriesAdd.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$lo
|
|||||||
function InventoriesEdit ($scope, $location, $routeParams, $compile, GenerateList, ClearScope, InventoryGroups, InventoryHosts, BuildTree, Wait,
|
function InventoriesEdit ($scope, $location, $routeParams, $compile, GenerateList, ClearScope, InventoryGroups, InventoryHosts, BuildTree, Wait,
|
||||||
GetSyncStatusMsg, InjectHosts, HostsReload, GroupsAdd, GroupsEdit, GroupsDelete, Breadcrumbs, LoadBreadCrumbs, Empty,
|
GetSyncStatusMsg, InjectHosts, HostsReload, GroupsAdd, GroupsEdit, GroupsDelete, Breadcrumbs, LoadBreadCrumbs, Empty,
|
||||||
Rest, ProcessErrors, InventoryUpdate, Alert, ToggleChildren, ViewUpdateStatus, GroupsCancelUpdate, Find,
|
Rest, ProcessErrors, InventoryUpdate, Alert, ToggleChildren, ViewUpdateStatus, GroupsCancelUpdate, Find,
|
||||||
HostsCreate, EditInventoryProperties, HostsEdit, HostsDelete, ToggleHostEnabled)
|
HostsCreate, EditInventoryProperties, HostsEdit, HostsDelete, ToggleHostEnabled, CopyMoveGroup)
|
||||||
{
|
{
|
||||||
ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior
|
ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior
|
||||||
//scope.
|
//scope.
|
||||||
@@ -384,6 +384,13 @@ function InventoriesEdit ($scope, $location, $routeParams, $compile, GenerateLis
|
|||||||
BuildTree({ scope: $scope, inventory_id: $scope.inventory_id, refresh: true });
|
BuildTree({ scope: $scope, inventory_id: $scope.inventory_id, refresh: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if ($scope.removeCopMoveGroup) {
|
||||||
|
$scope.removeCopyMoveGroup();
|
||||||
|
}
|
||||||
|
$scope.removeCopyMoveGroup = $scope.$on('CopyMoveGroup', function(e, inbound_tree_id, target_tree_id) {
|
||||||
|
CopyMoveGroup({ scope: $scope, target_tree_id: target_tree_id, inbound_tree_id: inbound_tree_id });
|
||||||
|
});
|
||||||
|
|
||||||
$scope.showHosts = function(tree_id, group_id, show_failures) {
|
$scope.showHosts = function(tree_id, group_id, show_failures) {
|
||||||
// Clicked on group
|
// Clicked on group
|
||||||
if (tree_id !== null) {
|
if (tree_id !== null) {
|
||||||
@@ -512,6 +519,6 @@ InventoriesEdit.$inject = [ '$scope', '$location', '$routeParams', '$compile', '
|
|||||||
'BuildTree', 'Wait', 'GetSyncStatusMsg', 'InjectHosts', 'HostsReload', 'GroupsAdd', 'GroupsEdit', 'GroupsDelete',
|
'BuildTree', 'Wait', 'GetSyncStatusMsg', 'InjectHosts', 'HostsReload', 'GroupsAdd', 'GroupsEdit', 'GroupsDelete',
|
||||||
'Breadcrumbs', 'LoadBreadCrumbs', 'Empty', 'Rest', 'ProcessErrors', 'InventoryUpdate', 'Alert', 'ToggleChildren',
|
'Breadcrumbs', 'LoadBreadCrumbs', 'Empty', 'Rest', 'ProcessErrors', 'InventoryUpdate', 'Alert', 'ToggleChildren',
|
||||||
'ViewUpdateStatus', 'GroupsCancelUpdate', 'Find', 'HostsCreate', 'EditInventoryProperties', 'HostsEdit',
|
'ViewUpdateStatus', 'GroupsCancelUpdate', 'Find', 'HostsCreate', 'EditInventoryProperties', 'HostsEdit',
|
||||||
'HostsDelete', 'ToggleHostEnabled'
|
'HostsDelete', 'ToggleHostEnabled', 'CopyMoveGroup'
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', '
|
|||||||
}
|
}
|
||||||
}])
|
}])
|
||||||
|
|
||||||
|
|
||||||
.factory('GetUpdateIntervalOptions', [ function() {
|
.factory('GetUpdateIntervalOptions', [ function() {
|
||||||
return function() {
|
return function() {
|
||||||
return [
|
return [
|
||||||
@@ -217,110 +218,6 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', '
|
|||||||
}
|
}
|
||||||
}])
|
}])
|
||||||
|
|
||||||
.factory('GroupsList', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'GroupList', 'GenerateList',
|
|
||||||
'Prompt', 'SearchInit', 'PaginateInit', 'ProcessErrors', 'GetBasePath', 'GroupsAdd', 'SelectionInit',
|
|
||||||
function($rootScope, $location, $log, $routeParams, Rest, Alert, GroupList, GenerateList, Prompt, SearchInit, PaginateInit,
|
|
||||||
ProcessErrors, GetBasePath, GroupsAdd, SelectionInit) {
|
|
||||||
return function(params) {
|
|
||||||
|
|
||||||
// build and present the list of groups we can add to an existing group
|
|
||||||
|
|
||||||
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 = 'Select';
|
|
||||||
scope.formModalHeader = 'Copy Groups';
|
|
||||||
scope.formModalCancelShow = true;
|
|
||||||
scope.formModalActionClass = 'btn btn-success';
|
|
||||||
|
|
||||||
$('.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 });
|
|
||||||
|
|
||||||
// Initialize the selection list
|
|
||||||
var url = (group_id) ? GetBasePath('groups') + group_id + '/children/' :
|
|
||||||
GetBasePath('inventory') + inventory_id + '/groups/';
|
|
||||||
var selected = [];
|
|
||||||
SelectionInit({ scope: scope, list: list, url: url, selected: selected });
|
|
||||||
|
|
||||||
scope.formModalAction = function() {
|
|
||||||
var groups = [];
|
|
||||||
for (var j=0; j < selected.length; j++) {
|
|
||||||
if (scope.inventoryRootGroups.indexOf(selected[j].id) > -1) {
|
|
||||||
groups.push(selected[j].name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (groups.length > 0) {
|
|
||||||
var action = function() {
|
|
||||||
$('#prompt-modal').modal('hide');
|
|
||||||
scope.finishSelection();
|
|
||||||
}
|
|
||||||
if (groups.length == 1) {
|
|
||||||
Prompt({ hdr: 'Warning', body: 'Be aware that ' + groups[0] +
|
|
||||||
' is a top level group. Adding it to ' + scope.selectedNodeName + ' will remove it from the top level. Do you ' +
|
|
||||||
' want to continue with this action?',
|
|
||||||
action: action });
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var list = '';
|
|
||||||
for (var i=0; i < groups.length; i++) {
|
|
||||||
if (i+1 == groups.length) {
|
|
||||||
list += ' and ' + groups[i];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
list += groups[i] + ', ';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Prompt({ hdr: 'Warning', body: 'Be aware that ' + list +
|
|
||||||
' are top level groups. Adding them to ' + scope.selectedNodeName + ' will remove them from the top level. Do you ' +
|
|
||||||
' want to continue with this action?',
|
|
||||||
action: action });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
scope.finishSelection();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var searchUrl = (group_id) ? GetBasePath('groups') + group_id + '/potential_children/' :
|
|
||||||
GetBasePath('inventory') + inventory_id + '/groups/';
|
|
||||||
|
|
||||||
SearchInit({ scope: scope, set: 'groups', list: list, url: searchUrl });
|
|
||||||
PaginateInit({ scope: scope, list: list, url: searchUrl, mode: 'lookup' });
|
|
||||||
scope.search(list.iterator);
|
|
||||||
|
|
||||||
if (!scope.$$phase) {
|
|
||||||
scope.$digest();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (scope.removeModalClosed) {
|
|
||||||
scope.removeModalClosed();
|
|
||||||
}
|
|
||||||
scope.removeModalClosed = scope.$on('modalClosed', function() {
|
|
||||||
/*BuildTree({
|
|
||||||
scope: scope,
|
|
||||||
inventory_id: inventory_id,
|
|
||||||
emit_on_select: 'NodeSelect',
|
|
||||||
target_id: 'search-tree-container',
|
|
||||||
refresh: true,
|
|
||||||
moveable: true
|
|
||||||
});*/
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}])
|
|
||||||
|
|
||||||
.factory('SourceChange', [ 'GetBasePath', 'CredentialList', 'LookUpInit',
|
.factory('SourceChange', [ 'GetBasePath', 'CredentialList', 'LookUpInit',
|
||||||
function(GetBasePath, CredentialList, LookUpInit){
|
function(GetBasePath, CredentialList, LookUpInit){
|
||||||
return function(params) {
|
return function(params) {
|
||||||
@@ -488,7 +385,8 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', '
|
|||||||
}
|
}
|
||||||
scope.removeSaveComplete = scope.$on('SaveComplete', function(e, group_id, error) {
|
scope.removeSaveComplete = scope.$on('SaveComplete', function(e, group_id, error) {
|
||||||
if (!error) {
|
if (!error) {
|
||||||
scope.searchCleanup();
|
if (scope.searchCleanup)
|
||||||
|
scope.searchCleanup();
|
||||||
scope.formModalActionDisabled = false;
|
scope.formModalActionDisabled = false;
|
||||||
scope.showGroupHelp = false; //get rid of the Hint
|
scope.showGroupHelp = false; //get rid of the Hint
|
||||||
BuildTree({ scope: parent_scope, inventory_id: inventory_id, refresh: true, new_group_id: group_id });
|
BuildTree({ scope: parent_scope, inventory_id: inventory_id, refresh: true, new_group_id: group_id });
|
||||||
|
|||||||
@@ -187,7 +187,6 @@ angular.module('InventoryHelper', [ 'RestServices', 'Utilities', 'OrganizationLi
|
|||||||
scope.formModalAction = function() {
|
scope.formModalAction = function() {
|
||||||
scope.inventory_id = inventory_id;
|
scope.inventory_id = inventory_id;
|
||||||
parent_scope.inventory_name = scope.inventory_name;
|
parent_scope.inventory_name = scope.inventory_name;
|
||||||
console.log('set inventory_name to: ' + parent_scope.inventory_name);
|
|
||||||
SaveInventory({ scope: scope });
|
SaveInventory({ scope: scope });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,8 +22,6 @@ angular.module('RefreshHelper', ['RestServices', 'Utilities'])
|
|||||||
var set = params.set;
|
var set = params.set;
|
||||||
var iterator = params.iterator;
|
var iterator = params.iterator;
|
||||||
var url = params.url;
|
var url = params.url;
|
||||||
console.log('Inside refresh');
|
|
||||||
console.log(url);
|
|
||||||
scope.current_url = url;
|
scope.current_url = url;
|
||||||
Rest.setUrl(url);
|
Rest.setUrl(url);
|
||||||
Rest.get()
|
Rest.get()
|
||||||
|
|||||||
@@ -29,9 +29,15 @@ angular.module('InventoryGroupsDefinition', [])
|
|||||||
ngClass: "group.selected_class",
|
ngClass: "group.selected_class",
|
||||||
hasChildren: true,
|
hasChildren: true,
|
||||||
columnClass: 'col-lg-9',
|
columnClass: 'col-lg-9',
|
||||||
nosort: true
|
nosort: true,
|
||||||
},
|
awDroppable: "\{\{ group.isDroppable \}\}",
|
||||||
source: {
|
awDraggable: "\{\{ group.isDraggable \}\}",
|
||||||
|
dataContainment: "#groups_table",
|
||||||
|
dataTreeId: "\{\{ group.id \}\}",
|
||||||
|
dataGroupId: "\{\{ group.group_id \}\}",
|
||||||
|
dataAccept: "dropAccept" //function determining when draggable is accepted by droppable
|
||||||
|
}
|
||||||
|
/*source: {
|
||||||
label: 'Source',
|
label: 'Source',
|
||||||
searchType: 'select',
|
searchType: 'select',
|
||||||
searchOptions: [
|
searchOptions: [
|
||||||
@@ -65,7 +71,7 @@ angular.module('InventoryGroupsDefinition', [])
|
|||||||
searchOnly: true,
|
searchOnly: true,
|
||||||
sourceModel: 'inventory_source',
|
sourceModel: 'inventory_source',
|
||||||
sourceField: 'status'
|
sourceField: 'status'
|
||||||
}
|
}*/
|
||||||
},
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
|
|||||||
@@ -240,6 +240,7 @@ td.actions {
|
|||||||
|
|
||||||
.dropdown-toggle,
|
.dropdown-toggle,
|
||||||
|
|
||||||
|
/*
|
||||||
.dropdown-toggle:hover,
|
.dropdown-toggle:hover,
|
||||||
.btn-default:visited,
|
.btn-default:visited,
|
||||||
.btn-default:hover,
|
.btn-default:hover,
|
||||||
@@ -249,6 +250,7 @@ td.actions {
|
|||||||
background-color: #bbb;
|
background-color: #bbb;
|
||||||
border-color: #bbb;
|
border-color: #bbb;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
.btn-light {
|
.btn-light {
|
||||||
color: #333;
|
color: #333;
|
||||||
@@ -1029,34 +1031,51 @@ input[type="checkbox"].checkbox-no-label {
|
|||||||
|
|
||||||
/* Inventory Edit */
|
/* Inventory Edit */
|
||||||
|
|
||||||
.selected {
|
.selected {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: @blue-dark;
|
color: @blue-dark;
|
||||||
}
|
}
|
||||||
|
|
||||||
.inventory-title {
|
.inventory-title {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.active-row {
|
.active-row {
|
||||||
background-color: @white;
|
background-color: @white;
|
||||||
border-bottom: 1px solid #ddd;
|
border-bottom: 1px solid #ddd;
|
||||||
border-right: 1px solid #ddd;
|
border-right: 1px solid #ddd;
|
||||||
}
|
}
|
||||||
|
|
||||||
.node-toggle, .node-no-toggle {
|
.node-toggle, .node-no-toggle {
|
||||||
/* also used on job evetns */
|
/* also used on job evetns */
|
||||||
float: none;
|
float: none;
|
||||||
padding-top: 3px;
|
padding-top: 3px;
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.node-no-toggle {
|
||||||
|
opacity: .30;
|
||||||
|
}
|
||||||
|
|
||||||
|
.draggable-clone {
|
||||||
|
/* padding: 4px;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid @grey; */
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.droppable-hover {
|
||||||
|
background-color: @info;
|
||||||
|
color: @info-color;
|
||||||
|
padding: 4px;
|
||||||
|
border: 1px solid @info-border;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
.node-no-toggle {
|
|
||||||
opacity: .30;
|
|
||||||
}
|
|
||||||
|
|
||||||
.disabled {
|
.disabled {
|
||||||
color: @grey;
|
color: @grey;
|
||||||
@@ -1336,7 +1355,7 @@ tr td button i {
|
|||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tree-form-container {
|
/*.tree-form-container {
|
||||||
padding-left: 15px;
|
padding-left: 15px;
|
||||||
padding-right: 15px;
|
padding-right: 15px;
|
||||||
|
|
||||||
@@ -1358,7 +1377,7 @@ tr td button i {
|
|||||||
|
|
||||||
#tree-form {
|
#tree-form {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
}
|
}*/
|
||||||
|
|
||||||
.label-text {
|
.label-text {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper'])
|
angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper', 'PromptDialog'])
|
||||||
|
|
||||||
.factory('SortNodes', [ function() {
|
.factory('SortNodes', [ function() {
|
||||||
return function(data) {
|
return function(data) {
|
||||||
@@ -31,6 +31,7 @@ angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper'])
|
|||||||
}
|
}
|
||||||
}])
|
}])
|
||||||
|
|
||||||
|
|
||||||
// Figure out the group level tool tip
|
// Figure out the group level tool tip
|
||||||
.factory('GetToolTip', [ 'FormatDate', function(FormatDate) {
|
.factory('GetToolTip', [ 'FormatDate', function(FormatDate) {
|
||||||
return function(params) {
|
return function(params) {
|
||||||
@@ -106,6 +107,7 @@ angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper'])
|
|||||||
}
|
}
|
||||||
}])
|
}])
|
||||||
|
|
||||||
|
|
||||||
.factory('GetInventoryToolTip', [ 'FormatDate', function(FormatDate) {
|
.factory('GetInventoryToolTip', [ 'FormatDate', function(FormatDate) {
|
||||||
return function(params) {
|
return function(params) {
|
||||||
|
|
||||||
@@ -171,6 +173,7 @@ angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper'])
|
|||||||
}
|
}
|
||||||
}])
|
}])
|
||||||
|
|
||||||
|
|
||||||
.factory('BuildTree', ['Rest', 'GetBasePath', 'ProcessErrors', 'SortNodes', 'Wait', 'GetSyncStatusMsg', 'GetHostsStatusMsg',
|
.factory('BuildTree', ['Rest', 'GetBasePath', 'ProcessErrors', 'SortNodes', 'Wait', 'GetSyncStatusMsg', 'GetHostsStatusMsg',
|
||||||
function(Rest, GetBasePath, ProcessErrors, SortNodes, Wait, GetSyncStatusMsg, GetHostsStatusMsg) {
|
function(Rest, GetBasePath, ProcessErrors, SortNodes, Wait, GetSyncStatusMsg, GetHostsStatusMsg) {
|
||||||
return function(params) {
|
return function(params) {
|
||||||
@@ -180,16 +183,22 @@ angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper'])
|
|||||||
var refresh = params.refresh;
|
var refresh = params.refresh;
|
||||||
var emit = params.emit;
|
var emit = params.emit;
|
||||||
var new_group_id = params.new_group_id;
|
var new_group_id = params.new_group_id;
|
||||||
|
|
||||||
//var selected_id = params.
|
|
||||||
|
|
||||||
var groups = [];
|
var groups = [];
|
||||||
var id = 1;
|
var id = 1;
|
||||||
|
|
||||||
var all_hosts = {
|
function buildAllHosts(tree_data) {
|
||||||
name: 'All Hosts', id: 1, group_id: null, parent: 0, description: '', show: true, ngicon: null,
|
// Start our tree object with All Hosts
|
||||||
has_children: false, related: {}, selected_class: '', show_failures: false };
|
var children = [];
|
||||||
groups.push(all_hosts);
|
var sorted = SortNodes(tree_data);
|
||||||
|
for (var j=0; j < sorted[j].length; i++) {
|
||||||
|
push(sorted[j].id);
|
||||||
|
}
|
||||||
|
var 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 buildGroups(tree_data, parent, level) {
|
function buildGroups(tree_data, parent, level) {
|
||||||
var sorted = SortNodes(tree_data);
|
var sorted = SortNodes(tree_data);
|
||||||
@@ -204,6 +213,12 @@ angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper'])
|
|||||||
inventory_id: inventory_id,
|
inventory_id: inventory_id,
|
||||||
group_id: sorted[i].id
|
group_id: sorted[i].id
|
||||||
}); // from helpers/Groups.js
|
}); // from helpers/Groups.js
|
||||||
|
|
||||||
|
var children = [];
|
||||||
|
for (var j=0; j < sorted[j].children.length; i++) {
|
||||||
|
children.push(sorted[j].id);
|
||||||
|
}
|
||||||
|
|
||||||
var group = {
|
var group = {
|
||||||
name: sorted[i].name,
|
name: sorted[i].name,
|
||||||
has_active_failures: sorted[i].has_active_failures,
|
has_active_failures: sorted[i].has_active_failures,
|
||||||
@@ -218,6 +233,7 @@ angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper'])
|
|||||||
source: sorted[i].summary_fields.inventory_source.source,
|
source: sorted[i].summary_fields.inventory_source.source,
|
||||||
group_id: sorted[i].id,
|
group_id: sorted[i].id,
|
||||||
event_level: level,
|
event_level: level,
|
||||||
|
children: children,
|
||||||
ngicon: (sorted[i].children.length > 0) ? 'fa fa-minus-square-o node-toggle' : 'fa fa-square-o node-no-toggle',
|
ngicon: (sorted[i].children.length > 0) ? 'fa fa-minus-square-o node-toggle' : 'fa fa-square-o node-no-toggle',
|
||||||
ngclick: 'toggle(' + id + ')',
|
ngclick: 'toggle(' + id + ')',
|
||||||
related: {
|
related: {
|
||||||
@@ -233,7 +249,9 @@ angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper'])
|
|||||||
show_failures: hosts_status['failures'],
|
show_failures: hosts_status['failures'],
|
||||||
hosts_status_class: hosts_status['class'],
|
hosts_status_class: hosts_status['class'],
|
||||||
selected_class: '',
|
selected_class: '',
|
||||||
show: true
|
show: true,
|
||||||
|
isDraggable: true,
|
||||||
|
isDroppable: true
|
||||||
}
|
}
|
||||||
groups.push(group);
|
groups.push(group);
|
||||||
if (new_group_id && group.group_id == new_group_id) {
|
if (new_group_id && group.group_id == new_group_id) {
|
||||||
@@ -255,6 +273,7 @@ angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper'])
|
|||||||
Rest.setUrl(inventory_tree);
|
Rest.setUrl(inventory_tree);
|
||||||
Rest.get()
|
Rest.get()
|
||||||
.success( function(data, status, headers, config) {
|
.success( function(data, status, headers, config) {
|
||||||
|
buildAllHosts(data);
|
||||||
buildGroups(data, 0, 0);
|
buildGroups(data, 0, 0);
|
||||||
//console.log(groups);
|
//console.log(groups);
|
||||||
if (refresh) {
|
if (refresh) {
|
||||||
@@ -292,6 +311,7 @@ angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper'])
|
|||||||
}
|
}
|
||||||
}])
|
}])
|
||||||
|
|
||||||
|
|
||||||
// Update a group with a set of properties
|
// Update a group with a set of properties
|
||||||
.factory('UpdateGroup', [ function() {
|
.factory('UpdateGroup', [ function() {
|
||||||
return function(params) {
|
return function(params) {
|
||||||
@@ -339,4 +359,87 @@ angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper'])
|
|||||||
$('#inventory-root-node').attr('data-name', name).attr('data-description', descr).find('.activate').first().text(name);
|
$('#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',
|
||||||
|
function($compile, Alert, ProcessErrors, Find) {
|
||||||
|
return function(params) {
|
||||||
|
var scope = params.scope;
|
||||||
|
|
||||||
|
var target = Find({ list: scope.groups, key: 'id', val: params.target_tree_id });
|
||||||
|
var inbound = Find({ list: scope.groups, key: 'id', val: params.inbound_tree_id });
|
||||||
|
|
||||||
|
var html = '';
|
||||||
|
html += "<div id=\"copy-prompt-modal\" class=\"modal fade\">\n";
|
||||||
|
html += "<div class=\"modal-dialog\">\n";
|
||||||
|
html += "<div class=\"modal-content\">\n";
|
||||||
|
html += "<div class=\"modal-header\">\n";
|
||||||
|
html += "<button type=\"button\" class=\"close\" data-target=\"#copy-prompt-modal\" " +
|
||||||
|
"data-dismiss=\"modal\" aria-hidden=\"true\">×</button>\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 += "<h3>Move Group</h3>\n";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
html += "<h3>Copy or Move?</h3>\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
html += "</div>\n";
|
||||||
|
html += "<div class=\"modal-body text-center\">\n";
|
||||||
|
|
||||||
|
if (target.id == 1) {
|
||||||
|
html += "<p>Are you sure you want to move group " + inbound.name + " to the top level?</p>";
|
||||||
|
}
|
||||||
|
else if (inbound.parent == 0) {
|
||||||
|
html += "<p>Are you sure you want to move group " + inbound.name + " away from the top level?</p>";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
html += "<p>Would you like to copy or move group <em>" + inbound.name + "</em> to group <em>" + target.name + "</em>?</p>\n";
|
||||||
|
html += "<div style=\"margin-top: 30px;\">\n";
|
||||||
|
html += "<a href=\"\" ng-click=\"moveGroup()\" class=\"btn btn-primary\" style=\"margin-right: 15px;\"><i class=\"fa fa-cut\"></i> Move</a>\n";
|
||||||
|
html += "<a href=\"\" ng-click=\"copyGroup()\" class=\"btn btn-primary\"><i class=\"fa fa-copy\"></i> Copy</a>\n";
|
||||||
|
html += "</div>\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
html += "</div>\n";
|
||||||
|
html += "<div class=\"modal-footer\">\n";
|
||||||
|
html += "<a href=\"#\" data-target=\"#prompt-modal\" data-dismiss=\"modal\" class=\"btn btn-default\">Cancel</a>\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 += "<a href=\"\" data-target=\"#prompt-modal\" ng-click=\"moveGroup()\" class=\"btn btn-primary\">Yes</a>\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
html += "</div>\n";
|
||||||
|
html += "</div><!-- modal-content -->\n";
|
||||||
|
html += "</div><!-- modal-dialog -->\n";
|
||||||
|
html += "</div><!-- modal -->\n";
|
||||||
|
|
||||||
|
// Inject our custom dialog
|
||||||
|
var e = angular.element(document.getElementById('inventory-modal-container'));
|
||||||
|
e.append(html);
|
||||||
|
$compile(e)(scope);
|
||||||
|
|
||||||
|
// Display it
|
||||||
|
$('#copy-prompt-modal').modal({
|
||||||
|
backdrop: 'static',
|
||||||
|
keyboard: true,
|
||||||
|
show: true
|
||||||
|
});
|
||||||
|
|
||||||
|
// Respond to copy or move...
|
||||||
|
scope.moveGroup = function() {
|
||||||
|
$('#copy-prompt-modal').modal('hide');
|
||||||
|
console.log('moving the group...');
|
||||||
|
}
|
||||||
|
|
||||||
|
scope.copyGroup = function() {
|
||||||
|
$('#copy-prompt-modal').modal('hide');
|
||||||
|
console.log('copying the group...');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}]);
|
||||||
|
|||||||
@@ -511,6 +511,72 @@ angular.module('AWDirectives', ['RestServices', 'Utilities', 'AuthService'])
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}])
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make an element draggable. Used on inventory groups tree.
|
||||||
|
*
|
||||||
|
* awDraggable: boolean || {{ expression }}
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
.directive('awDraggable', [ function() {
|
||||||
|
return function(scope, element, attrs) {
|
||||||
|
|
||||||
|
if (attrs.awDraggable == "true") {
|
||||||
|
var containment = attrs.containment; //provide dataContainment:"#id"
|
||||||
|
$(element).draggable({
|
||||||
|
containment: containment,
|
||||||
|
scroll: true,
|
||||||
|
revert: "invalid",
|
||||||
|
helper: "clone",
|
||||||
|
start: function(e, ui) {
|
||||||
|
ui.helper.addClass('draggable-clone');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}])
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make an element droppable- it can receive draggable elements
|
||||||
|
*
|
||||||
|
* awDroppable: boolean || {{ expression }}
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
.directive('awDroppable', ['Find', function(Find) {
|
||||||
|
return function(scope, element, attrs) {
|
||||||
|
if (attrs.awDroppable == "true") {
|
||||||
|
$(element).droppable({
|
||||||
|
// the following is inventory specific accept checking and
|
||||||
|
// drop processing.
|
||||||
|
accept: function(draggable) {
|
||||||
|
var node = Find({ list: scope.groups, key: 'id', val: parseInt($(this).attr('data-tree-id')) });
|
||||||
|
if (node) {
|
||||||
|
var group = draggable.attr('data-group-id');
|
||||||
|
return (node.children.indexOf(group) > -1) ? false : true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// this shouldn't be possible
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
over: function(e, ui) {
|
||||||
|
$(this).addClass('droppable-hover');
|
||||||
|
},
|
||||||
|
out: function(e, ui) {
|
||||||
|
$(this).removeClass('droppable-hover');
|
||||||
|
},
|
||||||
|
drop: function(e, ui) {
|
||||||
|
// Drag-n-drop succeeded. Trigger a response from the inventory.edit controller
|
||||||
|
$(this).removeClass('droppable-hover');
|
||||||
|
scope.$emit('CopyMoveGroup', ui.draggable.attr('data-tree-id'), $(this).attr('data-tree-id'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
|
|||||||
@@ -13,82 +13,58 @@ angular.module('GeneratorHelpers', ['GeneratorHelpers'])
|
|||||||
return function(obj, key, fld) {
|
return function(obj, key, fld) {
|
||||||
var result;
|
var result;
|
||||||
var value = (typeof obj[key] === "string") ? obj[key].replace(/[\'\"]/g, '"') : obj[key];
|
var value = (typeof obj[key] === "string") ? obj[key].replace(/[\'\"]/g, '"') : obj[key];
|
||||||
switch(key) {
|
|
||||||
case 'ngClick':
|
if (/^ng/.test(key)) {
|
||||||
result = "ng-click=\"" + value + "\" ";
|
result = 'ng-' + key.replace(/^ng/,'').toLowerCase() + "=\"" + value + "\" ";
|
||||||
break;
|
|
||||||
case 'ngOptions':
|
|
||||||
result = "ng-options=\"" + value + "\" ";
|
|
||||||
break;
|
|
||||||
case 'ngClass':
|
|
||||||
result = "ng-class=\"" + value + "\" ";
|
|
||||||
break;
|
|
||||||
case 'ngChange':
|
|
||||||
result = "ng-change=\"" + value + "\" ";
|
|
||||||
break;
|
|
||||||
case 'ngDisabled':
|
|
||||||
result = "ng-disabled=\"" + value + "\" ";
|
|
||||||
break;
|
|
||||||
case 'ngShow':
|
|
||||||
result = "ng-show=\"" + value + "\" ";
|
|
||||||
break;
|
|
||||||
case 'ngHide':
|
|
||||||
result = "ng-hide=\"" + value + "\" ";
|
|
||||||
break;
|
|
||||||
case 'ngBind':
|
|
||||||
result = "ng-bind=\"" + value + "\" ";
|
|
||||||
break;
|
|
||||||
case 'trueValue':
|
|
||||||
result = "ng-true-value=\"" + value + "\" ";
|
|
||||||
break;
|
|
||||||
case 'falseValue':
|
|
||||||
result = "ng-false-value=\"" + value + "\" ";
|
|
||||||
break;
|
|
||||||
case 'awToolTip':
|
|
||||||
result = "aw-tool-tip=\"" + value + "\" ";
|
|
||||||
break;
|
|
||||||
case 'awPopOver':
|
|
||||||
// construct the entire help link
|
|
||||||
result = "<a id=\"awp-" + fld + "\" href=\"\" aw-pop-over=\'" + value + "\' ";
|
|
||||||
result += (obj.dataTitle) ? "data-title=\"" + obj['dataTitle'].replace(/[\'\"]/g, '"') + "\" " : "";
|
|
||||||
result += (obj.dataPlacement) ? "data-placement=\"" + obj['dataPlacement'].replace(/[\'\"]/g, '"') + "\" " : "";
|
|
||||||
result += (obj.dataContainer) ? "data-container=\"" + obj['dataContainer'].replace(/[\'\"]/g, '"') + "\" " : "";
|
|
||||||
result += "class=\"help-link\" ";
|
|
||||||
result += "><i class=\"fa fa-question-circle\"></i></a> ";
|
|
||||||
break;
|
|
||||||
case 'dataTitle':
|
|
||||||
result = "data-title=\"" + value + "\" ";
|
|
||||||
break;
|
|
||||||
case 'awTipPlacement':
|
|
||||||
result = "aw-tip-placement=\"" + value + "\" ";
|
|
||||||
break;
|
|
||||||
case 'dataTipWatch':
|
|
||||||
result = "data-tip-watch=\"" + value + "\" ";
|
|
||||||
break;
|
|
||||||
case 'columnShow':
|
|
||||||
result = "ng-show=\"" + value + "\" ";
|
|
||||||
break;
|
|
||||||
case 'dataPlacement':
|
|
||||||
result = "data-placement=\"" + value + "\" ";
|
|
||||||
break;
|
|
||||||
case 'dataContainer':
|
|
||||||
result = "data-container=\"" + value + "\" ";
|
|
||||||
break;
|
|
||||||
case 'icon':
|
|
||||||
// new method of constructing <i> icon tag. Replces Icon method.
|
|
||||||
result = "<i class=\"fa fa-" + value;
|
|
||||||
result += (obj['iconSize']) ? " " + obj['iconSize'] : "";
|
|
||||||
result += "\"></i>";
|
|
||||||
break;
|
|
||||||
case 'autocomplete':
|
|
||||||
result = "autocomplete=\"";
|
|
||||||
result += (value) ? 'true' : 'false';
|
|
||||||
result += "\" ";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
result = key + "=\"" + value + "\" ";
|
|
||||||
}
|
}
|
||||||
|
else if (/^data|^aw/.test(key) && key != 'awPopOver') {
|
||||||
|
var s = '';
|
||||||
|
for (var i=0; i < key.length; i++) {
|
||||||
|
if (/[A-Z]/.test(key.charAt(i))) {
|
||||||
|
s += '-' + key.charAt(i).toLowerCase();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
s += key.charAt(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result = s + "=\"" + value + "\" ";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
switch(key) {
|
||||||
|
case 'trueValue':
|
||||||
|
result = "ng-true-value=\"" + value + "\" ";
|
||||||
|
break;
|
||||||
|
case 'falseValue':
|
||||||
|
result = "ng-false-value=\"" + value + "\" ";
|
||||||
|
break;
|
||||||
|
case 'awPopOver':
|
||||||
|
// construct the entire help link
|
||||||
|
result = "<a id=\"awp-" + fld + "\" href=\"\" aw-pop-over=\'" + value + "\' ";
|
||||||
|
result += (obj.dataTitle) ? "data-title=\"" + obj['dataTitle'].replace(/[\'\"]/g, '"') + "\" " : "";
|
||||||
|
result += (obj.dataPlacement) ? "data-placement=\"" + obj['dataPlacement'].replace(/[\'\"]/g, '"') + "\" " : "";
|
||||||
|
result += (obj.dataContainer) ? "data-container=\"" + obj['dataContainer'].replace(/[\'\"]/g, '"') + "\" " : "";
|
||||||
|
result += "class=\"help-link\" ";
|
||||||
|
result += "><i class=\"fa fa-question-circle\"></i></a> ";
|
||||||
|
break;
|
||||||
|
case 'columnShow':
|
||||||
|
result = "ng-show=\"" + value + "\" ";
|
||||||
|
break;
|
||||||
|
case 'icon':
|
||||||
|
// new method of constructing <i> icon tag. Replces Icon method.
|
||||||
|
result = "<i class=\"fa fa-" + value;
|
||||||
|
result += (obj['iconSize']) ? " " + obj['iconSize'] : "";
|
||||||
|
result += "\"></i>";
|
||||||
|
break;
|
||||||
|
case 'autocomplete':
|
||||||
|
result = "autocomplete=\"";
|
||||||
|
result += (value) ? 'true' : 'false';
|
||||||
|
result += "\" ";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
result = key + "=\"" + value + "\" ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -532,14 +508,20 @@ angular.module('GeneratorHelpers', ['GeneratorHelpers'])
|
|||||||
html += "<a href=\"#/" + base + "/{{" + list.iterator + ".id }}\" ";
|
html += "<a href=\"#/" + base + "/{{" + list.iterator + ".id }}\" ";
|
||||||
cap = true;
|
cap = true;
|
||||||
}
|
}
|
||||||
|
if (field.awDroppable) {
|
||||||
|
html += Attr(field, 'awDroppable');
|
||||||
|
html += (field.dataAccept) ? Attr(field, 'dataAccept') : '';
|
||||||
|
}
|
||||||
|
if (field.awDraggable) {
|
||||||
|
html += Attr(field, 'awDraggable');
|
||||||
|
html += (field.dataContainment) ? Attr(field, 'dataContainment') : '';
|
||||||
|
html += (field.dataTreeId) ? Attr(field, 'dataTreeId') : '';
|
||||||
|
html += (field.dataGroupId) ? Attr(field, 'dataGroupId') : '';
|
||||||
|
}
|
||||||
if (field.awToolTip) {
|
if (field.awToolTip) {
|
||||||
html += Attr(field, 'awToolTip');
|
html += Attr(field, 'awToolTip');
|
||||||
if (field.dataPlacement) {
|
html += (field.dataPlacement) ? Attr(field,'dataPlacement') : "";
|
||||||
html += Attr(field,'dataPlacement');
|
html += (field.dataTipWatch) ? Attr(field,'dataTipWatch') : "";
|
||||||
}
|
|
||||||
if (field.dataTipWatch) {
|
|
||||||
html += Attr(field,'dataTipWatch');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
html += (cap) ? ">" : "";
|
html += (cap) ? ">" : "";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,5 +10,6 @@
|
|||||||
<div id="groups-container" class="col-lg-6"></div>
|
<div id="groups-container" class="col-lg-6"></div>
|
||||||
<div id="hosts-container" class="col-lg-6"></div>
|
<div id="hosts-container" class="col-lg-6"></div>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="inventory-modal-container"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
Reference in New Issue
Block a user