mirror of
https://github.com/ansible/awx.git
synced 2026-03-18 09:27:31 -02:30
Latest Inventory tree changes. Drag-n-Drop now works, complete with styling and API calls. Working through bugs.
This commit is contained in:
@@ -37,7 +37,8 @@ function InventoryGroups ($scope, $rootScope, $compile, $location, $log, $routeP
|
||||
scope: scope,
|
||||
inventory_id: id,
|
||||
emit_on_select: 'NodeSelect',
|
||||
target_id: 'search-tree-container'
|
||||
target_id: 'search-tree-container',
|
||||
moveable: true
|
||||
});
|
||||
if (!scope.$$phase) {
|
||||
scope.$digest();
|
||||
|
||||
@@ -459,7 +459,8 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', '
|
||||
inventory_id: scope['inventory_id'],
|
||||
emit_on_select: 'NodeSelect',
|
||||
target_id: 'search-tree-container',
|
||||
refresh: true
|
||||
refresh: true,
|
||||
moveable: true
|
||||
});
|
||||
}
|
||||
|
||||
@@ -514,9 +515,9 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', '
|
||||
|
||||
|
||||
.factory('GroupsAdd', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'GroupForm', 'GenerateForm',
|
||||
'Prompt', 'ProcessErrors', 'GetBasePath', 'RefreshTree', 'ParseTypeChange', 'GroupsEdit',
|
||||
'Prompt', 'ProcessErrors', 'GetBasePath', 'ParseTypeChange', 'GroupsEdit', 'BuildTree',
|
||||
function($rootScope, $location, $log, $routeParams, Rest, Alert, GroupForm, GenerateForm, Prompt, ProcessErrors,
|
||||
GetBasePath, RefreshTree, ParseTypeChange, GroupsEdit) {
|
||||
GetBasePath, ParseTypeChange, GroupsEdit, BuildTree) {
|
||||
return function(params) {
|
||||
|
||||
var inventory_id = params.inventory_id;
|
||||
@@ -578,29 +579,25 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', '
|
||||
if (inventory_id) {
|
||||
data['inventory'] = inventory_id;
|
||||
}
|
||||
|
||||
data.variables = json_data;
|
||||
|
||||
Rest.setUrl(defaultUrl);
|
||||
Rest.post(data)
|
||||
.success( function(data, status, headers, config) {
|
||||
var id = data.id;
|
||||
scope.showGroupHelp = false; // get rid of the Hint
|
||||
if (scope.variables) {
|
||||
Rest.setUrl(data.related.variable_data);
|
||||
Rest.put(json_data)
|
||||
.success( function(data, status, headers, config) {
|
||||
$('#form-modal').modal('hide');
|
||||
RefreshTree({ scope: scope, group_id: id });
|
||||
})
|
||||
.error( function(data, status, headers, config) {
|
||||
ProcessErrors(scope, data, status, form,
|
||||
{ hdr: 'Error!', msg: 'Failed to add group varaibles. PUT returned status: ' + status });
|
||||
});
|
||||
}
|
||||
else {
|
||||
$('#form-modal').modal('hide');
|
||||
RefreshTree({ scope: scope, group_id: id });
|
||||
}
|
||||
})
|
||||
$('#form-modal').modal('hide');
|
||||
BuildTree({
|
||||
scope: scope,
|
||||
inventory_id: scope['inventory_id'],
|
||||
emit_on_select: 'NodeSelect',
|
||||
target_id: 'search-tree-container',
|
||||
refresh: true,
|
||||
moveable: true,
|
||||
group_id: id
|
||||
});
|
||||
})
|
||||
.error( function(data, status, headers, config) {
|
||||
scope.formModalActionDisabled = false;
|
||||
ProcessErrors(scope, data, status, form,
|
||||
|
||||
@@ -1012,11 +1012,15 @@ select.field-mini-height {
|
||||
}
|
||||
|
||||
.activate:hover {
|
||||
background-color: #ddd;
|
||||
background-color: #ddd;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.active {
|
||||
font-weight: bold;
|
||||
box-shadow: 3px 3px 3px 0 @grey;
|
||||
border-bottom: 1px solid @grey;
|
||||
border-right: 1px solid @grey;
|
||||
}
|
||||
|
||||
.expand-container,
|
||||
@@ -1028,14 +1032,19 @@ select.field-mini-height {
|
||||
|
||||
.badge-container,
|
||||
.title-container {
|
||||
border-bottom: 1px solid @well;
|
||||
border-bottom: 2px solid @well;
|
||||
}
|
||||
|
||||
|
||||
.expand-container {
|
||||
width: 14px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#root-expand-container {
|
||||
width: 20px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.expand {
|
||||
padding: 3px;
|
||||
}
|
||||
@@ -1060,14 +1069,6 @@ select.field-mini-height {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
// drag-n-drop tree styles
|
||||
.droppable-hover {
|
||||
background-color: #C0C0C0;
|
||||
color: @black;
|
||||
border: 1px solid #808080;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.disabled {
|
||||
color: @grey;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@ angular.module('TreeSelector', ['Utilities', 'RestServices'])
|
||||
var emit_on_select = params.emit_on_select;
|
||||
var target_id = params.target_id;
|
||||
var refresh_tree = (params.refresh == undefined || params.refresh == false) ? false : true;
|
||||
var moveable = (params.moveable == undefined || params.moveable == false) ? false : true;
|
||||
var group_id = params.group_id;
|
||||
|
||||
var html = '';
|
||||
var toolTip = 'Hosts have failed jobs?';
|
||||
@@ -100,6 +102,119 @@ angular.module('TreeSelector', ['Utilities', 'RestServices'])
|
||||
}
|
||||
}
|
||||
|
||||
if (scope.moveNodeRemove) {
|
||||
scope.moveNodeRemove();
|
||||
}
|
||||
scope.moveNodeRemove = scope.$on('MoveNode', function(e, node, parent, target) {
|
||||
var inv_id = scope['inventory_id'];
|
||||
var variables;
|
||||
|
||||
function cleanUp(state) {
|
||||
if (state !== 'fail') {
|
||||
// Visually move the element. Elment will be appended to the
|
||||
// end of target element list
|
||||
var elm = $('#' + node.attr('id')).detach();
|
||||
if (target.find('ul').length > 0) {
|
||||
// parent has children
|
||||
target.find('ul').first().append(elm);
|
||||
}
|
||||
else {
|
||||
target.append('<ul></ul>');
|
||||
target.find('ul').first().append(elm);
|
||||
}
|
||||
|
||||
// Remove any styling that might be left on the target
|
||||
// and put the expander icon back the way it should be
|
||||
target.find('div').each(function(idx) {
|
||||
if (idx > 0 && idx < 3) {
|
||||
$(this).css({ 'border-bottom': '2px solid #f5f5f5' });
|
||||
}
|
||||
});
|
||||
|
||||
// Make sure the parent and target have the correct expander class/icon.
|
||||
function setExpander(n) {
|
||||
var c = n.find('.expand-container');
|
||||
var icon;
|
||||
c.first().empty();
|
||||
if (n.attr('id') == 'inventory-root-node') {
|
||||
c.first().html('<i class=\"icon-sitemap\"></i>');
|
||||
}
|
||||
else if (c.length > 1) {
|
||||
// not root and has children, put expander icon back
|
||||
icon = (n.attr('data-state') == 'opened') ? 'icon-caret-down' : 'icon-caret-right';
|
||||
c.first().html('<a class="expand"><i class="' + icon + '"></i></a>');
|
||||
c.first().find('a').first().bind('click', toggle);
|
||||
}
|
||||
}
|
||||
setExpander(target);
|
||||
setExpander(parent);
|
||||
}
|
||||
Wait('stop');
|
||||
}
|
||||
|
||||
// disassociate the group from the original parent
|
||||
if (scope.removeGroupRemove) {
|
||||
scope.removeGroupRemove();
|
||||
}
|
||||
scope.removeGroupRemove = scope.$on('removeGroup', function() {
|
||||
var url = (parent.attr('data-group-id')) ? GetBasePath('base') + 'groups/' + parent.attr('data-group-id') + '/children/' :
|
||||
GetBasePath('inventory') + inv_id + '/groups/';
|
||||
Rest.setUrl(url);
|
||||
Rest.post({ id: node.attr('data-group-id'), disassociate: 1 })
|
||||
.success( function(data, status, headers, config) {
|
||||
cleanUp('success');
|
||||
})
|
||||
.error( function(data, status, headers, config) {
|
||||
cleanUp('fail');
|
||||
ProcessErrors(scope, data, status, null,
|
||||
{ hdr: 'Error!', msg: 'Failed to remove ' + node.attr('name') + ' from ' +
|
||||
parent.attr('name') + '. POST returned status: ' + status });
|
||||
});
|
||||
});
|
||||
|
||||
if (scope['addToTargetRemove']) {
|
||||
scope.addToTargetRemove();
|
||||
}
|
||||
scope.addToTargetRemove = scope.$on('addToTarget', function() {
|
||||
// add the new group to the target parent
|
||||
var url = (target.attr('data-group-id')) ? GetBasePath('base') + 'groups/' + target.attr('data-group-id') + '/children/' :
|
||||
GetBasePath('inventory') + inv_id + '/groups/';
|
||||
var group = {
|
||||
id: node.attr('data-group-id'),
|
||||
name: node.attr('data-name'),
|
||||
description: node.attr('data-description'),
|
||||
inventory: inv_id
|
||||
}
|
||||
Rest.setUrl(url);
|
||||
Rest.post(group)
|
||||
.success( function(data, status, headers, config) {
|
||||
scope.$emit('removeGroup');
|
||||
})
|
||||
.error( function(data, status, headers, config) {
|
||||
cleanUp('fail');
|
||||
ProcessErrors(scope, data, status, null,
|
||||
{ hdr: 'Error!', msg: 'Failed to add ' + node.attr('name') + ' to ' +
|
||||
target.attr('name') + '. POST returned status: ' + status });
|
||||
});
|
||||
});
|
||||
|
||||
Wait('start');
|
||||
// Lookup the inventory. We already have what we need except for variables.
|
||||
var url = GetBasePath('base') + 'groups/' + node.attr('data-group-id') + '/';
|
||||
Rest.setUrl(url);
|
||||
Rest.get()
|
||||
.success( function(data, status, headers, config) {
|
||||
variables = (data.variables) ? JSON.parse(data.variables) : "";
|
||||
scope.$emit('addToTarget');
|
||||
})
|
||||
.error( function(data, status, headers, config) {
|
||||
cleanUp('fail');
|
||||
ProcessErrors(scope, data, status, null,
|
||||
{ hdr: 'Error!', msg: 'Failed to lookup group ' + node.attr('name') +
|
||||
'. GET returned status: ' + status });
|
||||
});
|
||||
});
|
||||
|
||||
// The HTML is ready. Insert it into the view.
|
||||
if (scope.searchTreeReadyRemove) {
|
||||
scope.searchTreeReadyRemove();
|
||||
@@ -121,6 +236,10 @@ angular.module('TreeSelector', ['Utilities', 'RestServices'])
|
||||
link.bind('click', activate);
|
||||
}
|
||||
}
|
||||
|
||||
if (refresh_tree && group_id !== undefined) {
|
||||
$('li[data-group-id="' + group_id + '"]').first().click();
|
||||
}
|
||||
|
||||
// Attempt to stop the title from dropping to the next
|
||||
// line
|
||||
@@ -132,48 +251,63 @@ angular.module('TreeSelector', ['Utilities', 'RestServices'])
|
||||
});
|
||||
|
||||
// Make the tree drag-n-droppable
|
||||
|
||||
$('#selector-tree .activate').draggable({
|
||||
cursor: "pointer",
|
||||
cursorAt: { top: -16, left: -10 },
|
||||
revert: 'invalid',
|
||||
helper: 'clone',
|
||||
start: function (e, ui) {
|
||||
var txt = '[ ' + ui.helper.text() + ' ]';
|
||||
ui.helper.css({ 'font-weight': 'normal', 'color': '#A9A9A9', 'background-color': '#f5f5f5' }).text(txt);
|
||||
}
|
||||
})
|
||||
.droppable({
|
||||
//hoverClass: 'droppable-hover',
|
||||
tolerance: 'pointer',
|
||||
over: function (e, ui) {
|
||||
var p = $(this).parent().parent();
|
||||
p.find('div').each(function(idx) {
|
||||
if (idx > 0 && idx < 3) {
|
||||
$(this).css({ 'border-bottom': '1px dotted #808080' });
|
||||
}
|
||||
});
|
||||
var c = p.find('.expand-container').first();
|
||||
c.empty().html('<i class="icon-double-angle-right"></i>');
|
||||
},
|
||||
out: function (e, ui) {
|
||||
var p = $(this).parent().parent();
|
||||
p.find('div').each(function(idx) {
|
||||
if (idx > 0 && idx < 3) {
|
||||
$(this).css({ 'border-bottom': '1px solid #f5f5f5' });
|
||||
}
|
||||
});
|
||||
var c = p.find('.expand-container');
|
||||
var icon;
|
||||
c.first().empty();
|
||||
if (c.length > 1) {
|
||||
// has children, put expander icon back
|
||||
icon = (p.attr('data-state') == 'opened') ? 'icon-caret-down' : 'icon-caret-right';
|
||||
c.first().html('<a class="expand"><i class="' + icon + '"></i></a>');
|
||||
c.first().find('a').first().bind('click', toggle);
|
||||
}
|
||||
if (moveable) {
|
||||
$('#selector-tree .activate').draggable({
|
||||
cursor: "pointer",
|
||||
cursorAt: { top: -16, left: -10 },
|
||||
revert: 'invalid',
|
||||
helper: 'clone',
|
||||
start: function (e, ui) {
|
||||
var txt = '[ ' + ui.helper.text() + ' ]';
|
||||
ui.helper.css({ 'font-weight': 'normal', 'color': '#A9A9A9', 'background-color': '#f5f5f5' }).text(txt);
|
||||
}
|
||||
})
|
||||
.droppable({
|
||||
//hoverClass: 'droppable-hover',
|
||||
tolerance: 'pointer',
|
||||
over: function (e, ui) {
|
||||
var p = $(this).parent().parent();
|
||||
p.find('div').each(function(idx) {
|
||||
if (idx > 0 && idx < 3) {
|
||||
$(this).css({ 'border-bottom': '2px solid #A9A9A9' });
|
||||
}
|
||||
});
|
||||
var c = p.find('.expand-container').first();
|
||||
c.empty().html('<i class="icon-arrow-right" style="color: #A9A9A9;"></i>');
|
||||
},
|
||||
out: function (e, ui) {
|
||||
var p = $(this).parent().parent();
|
||||
p.find('div').each(function(idx) {
|
||||
if (idx > 0 && idx < 3) {
|
||||
$(this).css({ 'border-bottom': '2px solid #f5f5f5' });
|
||||
}
|
||||
});
|
||||
var c = p.find('.expand-container');
|
||||
var icon;
|
||||
c.first().empty();
|
||||
if (c.length > 1) {
|
||||
// has children, put expander icon back
|
||||
icon = (p.attr('data-state') == 'opened') ? 'icon-caret-down' : 'icon-caret-right';
|
||||
c.first().html('<a class="expand"><i class="' + icon + '"></i></a>');
|
||||
c.first().find('a').first().bind('click', toggle);
|
||||
}
|
||||
},
|
||||
drop: function (e,ui) {
|
||||
var variables;
|
||||
var node = ui.draggable.parent().parent(); // node being moved
|
||||
var parent = node.parent().parent(); // node from
|
||||
var target = $(this).parent().parent(); // node to
|
||||
scope.$emit('MoveNode', node, parent, target);
|
||||
|
||||
// Make sure angular picks up changes and jQuery doesn't
|
||||
// leave us in limbo...
|
||||
if (!scope.$$phase) {
|
||||
scope.$digest();
|
||||
}
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
} // if moveable
|
||||
|
||||
Wait('stop');
|
||||
});
|
||||
@@ -199,7 +333,7 @@ angular.module('TreeSelector', ['Utilities', 'RestServices'])
|
||||
html += "</div> " +
|
||||
"<div class=\"badge-container\"><i class=\"field-badge icon-failures-" + sorted[i].has_active_failures + "\" " +
|
||||
"aw-tool-tip=\"" + toolTip + "\" data-placement=\"top\"></i></div> " +
|
||||
"<div class=\"title-container\"><a href=\"\" class=\"activate\">" + sorted[i].name + "</a></div>";
|
||||
"<div class=\"title-container\"><a class=\"activate\">" + sorted[i].name + "</a></div>";
|
||||
idx++;
|
||||
if (sorted[i].children.length > 0) {
|
||||
buildHTML(sorted[i].children);
|
||||
@@ -268,14 +402,17 @@ angular.module('TreeSelector', ['Utilities', 'RestServices'])
|
||||
"data-failures=\"" + data.has_active_failures + "\" " +
|
||||
"data-groups=\"" + data.related.groups + "\" " +
|
||||
"data-name=\"" + data.name + "\" " +
|
||||
">" +
|
||||
"<i class=\"icon-sitemap\"></i> " +
|
||||
"<a href=\"\" class=\"activate active\">" + data.name + "</a>";
|
||||
">"+
|
||||
"<div class=\"expand-container\" id=\"root-expand-container\"><i class=\"icon-sitemap\"></i></div>" +
|
||||
"<div class=\"badge-container\"><i class=\"field-badge icon-failures-" + data.has_active_failures + "\" " +
|
||||
"aw-tool-tip=\"" + toolTip + "\" data-placement=\"top\"></i></div>" +
|
||||
"<div class=\"title-container\"><a class=\"activate\">" + data.name + "</a></div>";
|
||||
|
||||
scope.$emit('buildAllGroups', data.name, data.related.tree, data.related.groups);
|
||||
|
||||
if (!refresh_tree) {
|
||||
// if caller requests refresh, let caller handle next steps / node selection
|
||||
// if caller requests with refresh true, let caller handle next steps / node selection
|
||||
// otherwise, we're refreshing to summary page
|
||||
scope.$emit(emit_on_select, 'inventory-root-node', null, 'All Hosts');
|
||||
}
|
||||
|
||||
@@ -307,7 +444,6 @@ angular.module('TreeSelector', ['Utilities', 'RestServices'])
|
||||
if (inventory_id !== null) {
|
||||
$('#inventory-root-node').attr('data-name', name).attr('data-description', descr).find('.activate').first().text(name);
|
||||
}
|
||||
|
||||
}
|
||||
}])
|
||||
|
||||
|
||||
Reference in New Issue
Block a user