mirror of
https://github.com/ansible/awx.git
synced 2024-10-31 06:51:10 +03:00
Dynamic inventory add form work
This commit is contained in:
parent
62fafc9870
commit
cc80cd8549
@ -53,6 +53,8 @@ function SmartInventoryAdd($scope, $location,
|
||||
parse_variable: 'parseType',
|
||||
field_id: 'smartinventory_variables'
|
||||
});
|
||||
|
||||
$scope.dynamic_hosts = $state.params.hostfilter ? JSON.parse($state.params.hostfilter) : '';
|
||||
}
|
||||
|
||||
// Save
|
||||
|
@ -0,0 +1,15 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2017 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
export default ['$scope', 'QuerySet',
|
||||
function($scope, qs) {
|
||||
$scope.hostFilterTags = [];
|
||||
|
||||
$scope.$watch('hostFilter', function(){
|
||||
$scope.hostFilterTags = qs.stripDefaultParams($scope.hostFilter);
|
||||
});
|
||||
}
|
||||
];
|
@ -0,0 +1,25 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2017 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import dynamicInventoryHostFilterController from './dynamic-inventory-host-filter.controller';
|
||||
|
||||
export default ['templateUrl', '$compile',
|
||||
function(templateUrl, $compile) {
|
||||
return {
|
||||
scope: {
|
||||
hostFilter: '='
|
||||
},
|
||||
restrict: 'E',
|
||||
templateUrl: templateUrl('inventories/hosts/smart-inventory/dynamic-inventory-host-filter/dynamic-inventory-host-filter'),
|
||||
controller: dynamicInventoryHostFilterController,
|
||||
link: function(scope) {
|
||||
scope.openHostFilterModal = function() {
|
||||
$('#content-container').append($compile('<host-filter-modal host-filter="hostFilter"></host-filter-modal>')(scope));
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
];
|
@ -0,0 +1,12 @@
|
||||
<div class="input-group Form-mixedInputGroup">
|
||||
<span class="input-group-btn">
|
||||
<button type="button" class="Form-lookupButton btn btn-default" ng-click="openHostFilterModal()">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
</span>
|
||||
<span class="form-control Form-textInput input-medium lookup" style="padding: 4px 6px;">
|
||||
<span class="LabelList-tag" ng-repeat="tag in hostFilterTags">
|
||||
<span class="LabelList-name">{{tag}}</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
@ -0,0 +1,81 @@
|
||||
export default ['templateUrl', function(templateUrl) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
scope: {
|
||||
hostFilter: '='
|
||||
},
|
||||
templateUrl: templateUrl('inventories/hosts/smart-inventory/dynamic-inventory-host-filter/host-filter-modal/host-filter-modal'),
|
||||
link: function(scope, element) {
|
||||
|
||||
$('#host-filter-modal').on('hidden.bs.modal', function () {
|
||||
$('#host-filter-modal').off('hidden.bs.modal');
|
||||
$(element).remove();
|
||||
});
|
||||
|
||||
scope.showModal = function() {
|
||||
$('#host-filter-modal').modal('show');
|
||||
};
|
||||
|
||||
scope.destroyModal = function() {
|
||||
$('#host-filter-modal').modal('hide');
|
||||
};
|
||||
|
||||
},
|
||||
controller: ['$scope', 'QuerySet', 'GetBasePath', 'HostsList', '$compile', 'generateList', function($scope, qs, GetBasePath, HostsList, $compile, GenerateList) {
|
||||
|
||||
function init() {
|
||||
|
||||
$scope.host_default_params = {
|
||||
order_by: 'name',
|
||||
page_size: 5
|
||||
};
|
||||
|
||||
$scope.host_queryset = _.merge({
|
||||
order_by: 'name',
|
||||
page_size: 5
|
||||
}, $scope.hostFilter ? $scope.hostFilter : {});
|
||||
|
||||
// Fire off the initial search
|
||||
qs.search(GetBasePath('hosts'), $scope.host_queryset)
|
||||
.then(res => {
|
||||
$scope.host_dataset = res.data;
|
||||
$scope.hosts = $scope.host_dataset.results;
|
||||
|
||||
let hostList = _.cloneDeep(HostsList);
|
||||
delete hostList.fields.toggleHost;
|
||||
delete hostList.fields.active_failures;
|
||||
delete hostList.fields.inventory_name;
|
||||
let html = GenerateList.build({
|
||||
list: hostList,
|
||||
input_type: 'foobar',
|
||||
mode: 'lookup'
|
||||
});
|
||||
|
||||
$scope.list = hostList;
|
||||
|
||||
$('#foobar').append($compile(html)($scope));
|
||||
|
||||
$scope.showModal();
|
||||
});
|
||||
}
|
||||
|
||||
init();
|
||||
|
||||
$scope.cancelForm = function() {
|
||||
$scope.destroyModal();
|
||||
};
|
||||
|
||||
$scope.saveForm = function() {
|
||||
// Strip defaults out of the state params copy
|
||||
angular.forEach(Object.keys($scope.host_default_params), function(value) {
|
||||
delete $scope.host_queryset[value];
|
||||
});
|
||||
|
||||
$scope.hostFilter = angular.copy($scope.host_queryset);
|
||||
|
||||
$scope.destroyModal();
|
||||
};
|
||||
|
||||
}]
|
||||
};
|
||||
}];
|
@ -0,0 +1,21 @@
|
||||
<div id="host-filter-modal" class="Lookup modal fade">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header Form-header">
|
||||
<div class="Form-title Form-title--uppercase">DYNAMIC HOSTS</div>
|
||||
<!-- optional: transclude header fields -->
|
||||
<div class="Form-header--fields"></div>
|
||||
<div class="Form-exitHolder">
|
||||
<button type="button" class="Form-exit" ng-click="cancelForm()">
|
||||
<i class="fa fa-times-circle"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-body" id="foobar"></div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" ng-click="cancelForm()" class="Lookup-cancel btn btn-default">CANCEL</button>
|
||||
<button type="button" ng-click="saveForm()" class="Lookup-save btn btn-primary">SAVE</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -7,10 +7,14 @@
|
||||
import smartInventoryAdd from './add/main';
|
||||
import smartInventoryEdit from './edit/main';
|
||||
import SmartInventoryForm from './smart-inventory.form';
|
||||
import dynamicInventoryHostFilter from './dynamic-inventory-host-filter/dynamic-inventory-host-filter.directive';
|
||||
import hostFilterModal from './dynamic-inventory-host-filter/host-filter-modal/host-filter-modal.directive';
|
||||
|
||||
export default
|
||||
angular.module('smartInventory', [
|
||||
smartInventoryAdd.name,
|
||||
smartInventoryEdit.name
|
||||
])
|
||||
.factory('SmartInventoryForm', SmartInventoryForm);
|
||||
.factory('SmartInventoryForm', SmartInventoryForm)
|
||||
.directive('dynamicInventoryHostFilter', dynamicInventoryHostFilter)
|
||||
.directive('hostFilterModal', hostFilterModal);
|
||||
|
@ -43,6 +43,17 @@ export default ['i18n', 'buildHostListState', function(i18n, buildHostListState)
|
||||
ngDisabled: '!(inventory_obj.summary_fields.user_capabilities.edit || canAdd) || !canEditOrg',
|
||||
awLookupWhen: '(inventory_obj.summary_fields.user_capabilities.edit || canAdd) && canEditOrg'
|
||||
},
|
||||
dynamic_hosts: {
|
||||
label: i18n._('Dynamic Hosts'),
|
||||
type: 'custom',
|
||||
control: '<dynamic-inventory-host-filter host-filter="dynamic_hosts"></dynamic-inventory-host-filter>',
|
||||
basePath: 'hosts',
|
||||
list: 'HostsList',
|
||||
sourceModel: 'host',
|
||||
sourceField: 'name',
|
||||
required: true
|
||||
// TODO: add required, ngDisabled, awLookupWhen (?)
|
||||
},
|
||||
variables: {
|
||||
label: i18n._('Variables'),
|
||||
type: 'textarea',
|
||||
|
@ -124,130 +124,6 @@ angular.module('inventory', [
|
||||
|
||||
}
|
||||
|
||||
// function generateInventoryStates() {
|
||||
//
|
||||
// let smartInventoryAdd = {
|
||||
// name: 'inventories.addSmartInventory',
|
||||
// url: '/smartinventory',
|
||||
// form: 'SmartInventoryForm',
|
||||
// ncyBreadcrumb: {
|
||||
// label: "CREATE SMART INVENTORY"
|
||||
// },
|
||||
// views: {
|
||||
// 'form@inventories': {
|
||||
// templateProvider: function(SmartInventoryForm, GenerateForm) {
|
||||
// return GenerateForm.buildHTML(SmartInventoryForm, {
|
||||
// mode: 'add',
|
||||
// related: false
|
||||
// });
|
||||
// },
|
||||
// controller: 'SmartInventoryAddController'
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
//
|
||||
// let smartInventoryAddOrgLookup = {
|
||||
// searchPrefix: 'organization',
|
||||
// name: 'inventories.addSmartInventory.organization',
|
||||
// url: '/organization',
|
||||
// data: {
|
||||
// formChildState: true
|
||||
// },
|
||||
// params: {
|
||||
// organization_search: {
|
||||
// value: {
|
||||
// page_size: '5'
|
||||
// },
|
||||
// squash: true,
|
||||
// dynamic: true
|
||||
// }
|
||||
// },
|
||||
// ncyBreadcrumb: {
|
||||
// skip: true
|
||||
// },
|
||||
// views: {
|
||||
// 'related': {
|
||||
// templateProvider: function(ListDefinition, generateList) {
|
||||
// let list_html = generateList.build({
|
||||
// mode: 'lookup',
|
||||
// list: ListDefinition,
|
||||
// input_type: 'radio'
|
||||
// });
|
||||
// return `<lookup-modal>${list_html}</lookup-modal>`;
|
||||
//
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
// resolve: {
|
||||
// ListDefinition: ['OrganizationList', function(OrganizationList) {
|
||||
// let list = _.cloneDeep(OrganizationList);
|
||||
// list.lookupConfirmText = 'SELECT';
|
||||
// return list;
|
||||
// }],
|
||||
// Dataset: ['ListDefinition', 'QuerySet', '$stateParams', 'GetBasePath',
|
||||
// (list, qs, $stateParams, GetBasePath) => {
|
||||
// let path = GetBasePath(list.name) || GetBasePath(list.basePath);
|
||||
// return qs.search(path, $stateParams[`${list.iterator}_search`]);
|
||||
// }
|
||||
// ]
|
||||
// },
|
||||
// onExit: function($state) {
|
||||
// if ($state.transition) {
|
||||
// $('#form-modal').modal('hide');
|
||||
// $('.modal-backdrop').remove();
|
||||
// $('body').removeClass('modal-open');
|
||||
// }
|
||||
// },
|
||||
// };
|
||||
//
|
||||
// let inventories = stateDefinitions.generateTree({
|
||||
// parent: 'inventories', // top-most node in the generated tree (will replace this state definition)
|
||||
// modes: ['add', 'edit'],
|
||||
// list: 'InventoryList',
|
||||
// form: 'InventoryForm',
|
||||
// controllers: {
|
||||
// list: 'InventoryListController',
|
||||
// add: 'InventoryAddController',
|
||||
// edit: 'InventoryEditController'
|
||||
// },
|
||||
// urls: {
|
||||
// list: '/inventories'
|
||||
// },
|
||||
// ncyBreadcrumb: {
|
||||
// label: N_('INVENTORIES')
|
||||
// },
|
||||
// views: {
|
||||
// '@': {
|
||||
// templateUrl: templateUrl('inventories/inventories')
|
||||
// },
|
||||
// 'list@inventories': {
|
||||
// templateProvider: function(InventoryList, generateList) {
|
||||
// let html = generateList.build({
|
||||
// list: InventoryList,
|
||||
// mode: 'edit'
|
||||
// });
|
||||
// return html;
|
||||
// },
|
||||
// controller: 'InventoryListController'
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
//
|
||||
// return Promise.all([
|
||||
// inventories
|
||||
// ]).then((generated) => {
|
||||
// return {
|
||||
// states: _.reduce(generated, (result, definition) => {
|
||||
// return result.concat(definition.states);
|
||||
// }, [
|
||||
// stateExtender.buildDefinition(smartInventoryAdd),
|
||||
// stateExtender.buildDefinition(smartInventoryAddOrgLookup)
|
||||
// ])
|
||||
// };
|
||||
// });
|
||||
//
|
||||
// }
|
||||
|
||||
$stateProvider.state({
|
||||
name: 'hosts',
|
||||
url: '/hosts',
|
||||
|
@ -316,7 +316,11 @@ export default ['$compile', 'Attr', 'Icon',
|
||||
if (options.mode === 'lookup') {
|
||||
if (options.input_type === "radio") { //added by JT so that lookup forms can be either radio inputs or check box inputs
|
||||
innerTable += `<td class="List-tableCell"> <input type="radio" ng-model="${list.iterator}.checked" ng-value="1" ng-false-value="0" name="check_${list.iterator}_{{${list.iterator}.id}}" ng-click="toggle_row(${list.iterator}.id)"></td>`;
|
||||
} else { // its assumed that options.input_type = checkbox
|
||||
}
|
||||
else if (options.input_type === "foobar") {
|
||||
|
||||
}
|
||||
else { // its assumed that options.input_type = checkbox
|
||||
innerTable += "<td class=\"List-tableCell select-column List-staticColumn--smallStatus\"><input type=\"checkbox\" ng-model=\"" + list.iterator + ".checked\" name=\"check_{{" +
|
||||
list.iterator + ".id }}\" ng-click=\"toggle_" + list.iterator + "(" + list.iterator + ".id, true)\" ng-true-value=\"1\" " +
|
||||
"ng-false-value=\"0\" id=\"check_" + list.iterator + "_{{" + list.iterator + ".id}}\" /></td>";
|
||||
@ -483,7 +487,7 @@ export default ['$compile', 'Attr', 'Icon',
|
||||
|
||||
if (list.multiSelect) {
|
||||
html += buildSelectAll().prop('outerHTML');
|
||||
} else if (options.mode === 'lookup') {
|
||||
} else if (options.mode === 'lookup' && options.input_type !== 'foobar') {
|
||||
html += "<th class=\"List-tableHeader select-column List-staticColumn--smallStatus\"></th>";
|
||||
}
|
||||
|
||||
|
@ -274,6 +274,32 @@ export default ['$q', 'Rest', 'ProcessErrors', '$rootScope', 'Wait', 'DjangoSear
|
||||
hdr: 'Error!',
|
||||
msg: `Invalid search term entered. GET returned: ${status}`
|
||||
});
|
||||
},
|
||||
// Removes state definition defaults and pagination terms
|
||||
stripDefaultParams(params, defaults) {
|
||||
if(defaults) {
|
||||
let stripped =_.pick(params, (value, key) => {
|
||||
// setting the default value of a term to null in a state definition is a very explicit way to ensure it will NEVER generate a search tag, even with a non-default value
|
||||
return defaults[key] !== value && key !== 'order_by' && key !== 'page' && key !== 'page_size' && defaults[key] !== null;
|
||||
});
|
||||
let strippedCopy = _.cloneDeep(stripped);
|
||||
if(_.keys(_.pick(defaults, _.keys(strippedCopy))).length > 0){
|
||||
for (var key in strippedCopy) {
|
||||
if (strippedCopy.hasOwnProperty(key)) {
|
||||
let value = strippedCopy[key];
|
||||
if(_.isArray(value)){
|
||||
let index = _.indexOf(value, defaults[key]);
|
||||
value = value.splice(index, 1)[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
stripped = strippedCopy;
|
||||
}
|
||||
return _(strippedCopy).map(this.decodeParam).flatten().value();
|
||||
}
|
||||
else {
|
||||
return _(params).map(this.decodeParam).flatten().value();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ export default ['$stateParams', '$scope', '$state', 'GetBasePath', 'QuerySet', '
|
||||
queryset = _.cloneDeep($scope.querySet);
|
||||
}
|
||||
else {
|
||||
queryset = $stateParams[`${$scope.iterator}_search`];
|
||||
queryset = $state.params[`${$scope.iterator}_search`];
|
||||
}
|
||||
|
||||
// build $scope.tags from $stateParams.QuerySet, build fieldset key
|
||||
@ -30,7 +30,7 @@ export default ['$stateParams', '$scope', '$state', 'GetBasePath', 'QuerySet', '
|
||||
|
||||
function init() {
|
||||
path = GetBasePath($scope.basePath) || $scope.basePath;
|
||||
$scope.searchTags = stripDefaultParams($state.params[`${$scope.iterator}_search`]);
|
||||
$scope.searchTags = qs.stripDefaultParams(queryset, defaults);
|
||||
qs.initFieldset(path, $scope.djangoModel).then((data) => {
|
||||
$scope.models = data.models;
|
||||
$scope.options = data.options.data;
|
||||
@ -69,7 +69,7 @@ export default ['$stateParams', '$scope', '$state', 'GetBasePath', 'QuerySet', '
|
||||
});
|
||||
|
||||
$scope.searchTerm = null;
|
||||
$scope.searchTags = stripDefaultParams(queryset);
|
||||
$scope.searchTags = qs.stripDefaultParams(queryset, defaults);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -86,28 +86,6 @@ export default ['$stateParams', '$scope', '$state', 'GetBasePath', 'QuerySet', '
|
||||
});
|
||||
}
|
||||
|
||||
// Removes state definition defaults and pagination terms
|
||||
function stripDefaultParams(params) {
|
||||
let stripped =_.pick(params, (value, key) => {
|
||||
// setting the default value of a term to null in a state definition is a very explicit way to ensure it will NEVER generate a search tag, even with a non-default value
|
||||
return defaults[key] !== value && key !== 'order_by' && key !== 'page' && key !== 'page_size' && defaults[key] !== null;
|
||||
});
|
||||
let strippedCopy = _.cloneDeep(stripped);
|
||||
if(_.keys(_.pick(defaults, _.keys(strippedCopy))).length > 0){
|
||||
for (var key in strippedCopy) {
|
||||
if (strippedCopy.hasOwnProperty(key)) {
|
||||
let value = strippedCopy[key];
|
||||
if(_.isArray(value)){
|
||||
let index = _.indexOf(value, defaults[key]);
|
||||
value = value.splice(index, 1)[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
stripped = strippedCopy;
|
||||
}
|
||||
return _(strippedCopy).map(qs.decodeParam).flatten().value();
|
||||
}
|
||||
|
||||
function setDefaults(term) {
|
||||
if ($scope.list.defaultSearchParams) {
|
||||
return $scope.list.defaultSearchParams(encodeURIComponent(term));
|
||||
@ -136,7 +114,7 @@ export default ['$stateParams', '$scope', '$state', 'GetBasePath', 'QuerySet', '
|
||||
$scope.dataset = res.data;
|
||||
$scope.collection = res.data.results;
|
||||
});
|
||||
$scope.searchTags = stripDefaultParams(queryset);
|
||||
$scope.searchTags = qs.stripDefaultParams(queryset, defaults);
|
||||
};
|
||||
|
||||
// remove tag, merge new queryset, $state.go
|
||||
@ -208,7 +186,7 @@ export default ['$stateParams', '$scope', '$state', 'GetBasePath', 'QuerySet', '
|
||||
$scope.dataset = res.data;
|
||||
$scope.collection = res.data.results;
|
||||
});
|
||||
$scope.searchTags = stripDefaultParams(queryset);
|
||||
$scope.searchTags = qs.stripDefaultParams(queryset, defaults);
|
||||
};
|
||||
|
||||
// add a search tag, merge new queryset, $state.go()
|
||||
@ -311,7 +289,7 @@ export default ['$stateParams', '$scope', '$state', 'GetBasePath', 'QuerySet', '
|
||||
});
|
||||
|
||||
$scope.searchTerm = null;
|
||||
$scope.searchTags = stripDefaultParams(queryset);
|
||||
$scope.searchTags = qs.stripDefaultParams(queryset, defaults);
|
||||
}
|
||||
};
|
||||
|
||||
@ -333,7 +311,7 @@ export default ['$stateParams', '$scope', '$state', 'GetBasePath', 'QuerySet', '
|
||||
});
|
||||
|
||||
$scope.searchTerm = null;
|
||||
$scope.searchTags = stripDefaultParams(queryset);
|
||||
$scope.searchTags = qs.stripDefaultParams(queryset, defaults);
|
||||
};
|
||||
}
|
||||
];
|
||||
|
@ -699,6 +699,11 @@ function($injector, $stateExtender, $log, i18n) {
|
||||
organization: null
|
||||
};
|
||||
}
|
||||
else if(field.sourceModel === 'host') {
|
||||
params = {
|
||||
page_size: '5'
|
||||
};
|
||||
}
|
||||
else {
|
||||
params = {
|
||||
page_size: '5',
|
||||
|
Loading…
Reference in New Issue
Block a user