mirror of
https://github.com/ZwareBear/awx.git
synced 2026-04-03 06:31:49 -05:00
Boolean / Smart Search (#3631)
* Part 1: building new search components Directives: smart-search, column-sort, paginate Service: QuerySet Model: DjangoSearchModel * Part 2: Implementing new search components, de-implementing old search components Remove old code: * tagSearch directive * old pagination strategy * old column sorting strategy * lookup Add new directives to list/form generator: * smart-search, * paginate * column-sort Connect $state + dataset resolution * upgrade ui-router lib to v1.0.0-beta3 * Custom $urlMatcherFactory.type - queryset * Render lists, forms, related, lookups in named views * Provide html templates in list/form/lookup/related state definitions * Provide dataset through resolve block in state definitions Update utilities * isEmpty filter * use async validation strategy in awlookup directive * Part 3: State implementations (might split into per-module commits) * Support optional state definition flag: squashSearchUrl. *_search params are only URI-encoded if squashSearchUrl is falsey. * * Fix list badge counts * Clear search input after search term(s) applied * Chain of multiple search terms in one submission * Hook up activity stream * Hook up portal mode * Fix pagination range calculations * Hook up organization sub-list views * Hook up listDefinition.search defaults * Fix ng-disabled conditions reflecting RBAC access on form fields * Fix actively-editing indicator in generated lists * form generator - fix undefined span, remove dead event listeners * wrap hosts/groups lists in a panel, fix groups list error * Smart search directive: clear all search tags * Search tags - ‘Clear All’ text - 12px Search key - remove top padding/margin Search key - reverse bolding of relationship fields / label, add commas Search tags - remove padding-bottom Lookup modal - “X” close button styled incorrectly Lookup modal - List title not rendered Lookup modal - 20px margin between buttons * Portal Mode Fix default column-sort on jobs list Hide column-oort on job status column Apply custom search bar sizes * stateDefinition.factory Return ES6 Promise instead of $q promise. $q cannot be safely provided during module.config() phase Some generated state trees (inventory / inventoryManage) need to be reduced to one promise. Side-step issues caused by ui-router de-registering ALL registered states that match placeholder state name/url pattern. e.g. inventories.lazyLoad() would de-register inventoryManage states if a page refresh occured @ /#/inventories/** * Combine generated state trees: inventories + inventoryManage Hook up inventory sync schedule list/form add /form edit views * Hook up system job schedule list/add/edit states * Fix breadcrumb of generated states in /setup view Fix typo in scheduler search prefix * Remove old search system deritus from list definitions * Fix breadcrumb definitions in states registered in app.js config block * Transclude list action buttons in generated form lists * Lookup Modal passes acceptance criterea: Modal cancel/exit - don’t update form field’s ng-model Modal save - do update form field's ng-model Transclude generated list contents into <lookup-modal> directive Lookup modal test spec * Fix typo in merge conflict resolution * Disable failing unit tests pending revision * Integrate smart-search architechture into add-permissions modal * use a semicolon delimiter instead of comma to avoid collision with django __in comparator * Hook up Dashboard > Hosts states, update Dashboard Inventory/Project counts with new search filters * Misc bug splat Add 20px spacing around root ui-view Fix missing closing div in related views Remove dupe line in smart-search controller * Remove defunct LookupHelper code * Rebuild inventories list status tooltips on updates to dataset Code cleanup - remove defunct modules Remove LookupHelper / LookupInit code Remove pre-RBAC permissions module * Add mising stateTree / basePath properties to form definitions * Resolve i18n conflicts in list and form generator Freeze dependencies * Integrate sockets * Final bug splat: fix jobs > job details and jobs > scheduled routing fix mis-resolved merge conflicts swap console.info for $log.debug
This commit is contained in:
@@ -4,27 +4,29 @@
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
export default
|
||||
[ 'Rest', 'Wait',
|
||||
'NotificationsFormObject', 'ProcessErrors', 'GetBasePath',
|
||||
'GenerateForm', 'SearchInit' , 'PaginateInit',
|
||||
'LookUpInit', 'OrganizationList', 'notification_template',
|
||||
'$scope', '$state', 'GetChoices', 'CreateSelect2', 'Empty',
|
||||
'$rootScope', 'NotificationsTypeChange', 'ParseTypeChange',
|
||||
function(
|
||||
Rest, Wait,
|
||||
NotificationsFormObject, ProcessErrors, GetBasePath,
|
||||
GenerateForm, SearchInit, PaginateInit,
|
||||
LookUpInit, OrganizationList, notification_template,
|
||||
$scope, $state, GetChoices, CreateSelect2, Empty,
|
||||
$rootScope, NotificationsTypeChange, ParseTypeChange
|
||||
) {
|
||||
var generator = GenerateForm,
|
||||
id = notification_template.id,
|
||||
form = NotificationsFormObject,
|
||||
master = {},
|
||||
url = GetBasePath('notification_templates');
|
||||
export default ['Rest', 'Wait',
|
||||
'NotificationsFormObject', 'ProcessErrors', 'GetBasePath',
|
||||
'GenerateForm',
|
||||
'OrganizationList', 'notification_template',
|
||||
'$scope', '$state', 'GetChoices', 'CreateSelect2', 'Empty',
|
||||
'$rootScope', 'NotificationsTypeChange', 'ParseTypeChange',
|
||||
function(
|
||||
Rest, Wait,
|
||||
NotificationsFormObject, ProcessErrors, GetBasePath,
|
||||
GenerateForm,
|
||||
OrganizationList, notification_template,
|
||||
$scope, $state, GetChoices, CreateSelect2, Empty,
|
||||
$rootScope, NotificationsTypeChange, ParseTypeChange
|
||||
) {
|
||||
var generator = GenerateForm,
|
||||
id = notification_template.id,
|
||||
form = NotificationsFormObject,
|
||||
master = {},
|
||||
url = GetBasePath('notification_templates');
|
||||
|
||||
init();
|
||||
|
||||
function init() {
|
||||
$scope.notification_template = notification_template;
|
||||
|
||||
$scope.$watch('notification_template.summary_fields.user_capabilities.edit', function(val) {
|
||||
@@ -33,110 +35,6 @@ export default
|
||||
}
|
||||
});
|
||||
|
||||
generator.inject(form, {
|
||||
mode: 'edit' ,
|
||||
scope:$scope,
|
||||
related: false
|
||||
});
|
||||
if ($scope.removeChoicesReady) {
|
||||
$scope.removeChoicesReady();
|
||||
}
|
||||
$scope.removeChoicesReady = $scope.$on('choicesReady', function () {
|
||||
var i;
|
||||
for (i = 0; i < $scope.notification_type_options.length; i++) {
|
||||
if ($scope.notification_type_options[i].value === '') {
|
||||
$scope.notification_type_options[i].value="manual";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Wait('start');
|
||||
Rest.setUrl(url + id+'/');
|
||||
Rest.get()
|
||||
.success(function (data) {
|
||||
var fld;
|
||||
for (fld in form.fields) {
|
||||
if (data[fld]) {
|
||||
$scope[fld] = data[fld];
|
||||
master[fld] = data[fld];
|
||||
}
|
||||
|
||||
if(form.fields[fld].type === 'checkbox_group') {
|
||||
// Loop across the group and put the child data on scope
|
||||
for(var j=0; j<form.fields[fld].fields.length; j++) {
|
||||
if(data.notification_configuration[form.fields[fld].fields[j].name]) {
|
||||
$scope[form.fields[fld].fields[j].name] = data.notification_configuration[form.fields[fld].fields[j].name];
|
||||
master[form.fields[fld].fields[j].name] = data.notification_configuration[form.fields[fld].fields[j].name];
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(data.notification_configuration[fld]){
|
||||
$scope[fld] = data.notification_configuration[fld];
|
||||
master[fld] = data.notification_configuration[fld];
|
||||
|
||||
if(form.fields[fld].type === 'textarea'){
|
||||
if (form.fields[fld].name === 'headers') {
|
||||
$scope[fld] = JSON.stringify($scope[fld], null, 2);
|
||||
} else {
|
||||
$scope[fld] = $scope[fld].toString().replace(',' , '\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (form.fields[fld].sourceModel && data.summary_fields &&
|
||||
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] =
|
||||
data.summary_fields[form.fields[fld].sourceModel][form.fields[fld].sourceField];
|
||||
}
|
||||
}
|
||||
}
|
||||
data.notification_type = (Empty(data.notification_type)) ? '' : data.notification_type;
|
||||
for (var i = 0; i < $scope.notification_type_options.length; i++) {
|
||||
if ($scope.notification_type_options[i].value === data.notification_type) {
|
||||
$scope.notification_type = $scope.notification_type_options[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
master.notification_type = $scope.notification_type;
|
||||
CreateSelect2({
|
||||
element: '#notification_template_notification_type',
|
||||
multiple: false
|
||||
});
|
||||
NotificationsTypeChange.getDetailFields($scope.notification_type.value).forEach(function(field) {
|
||||
$scope[field[0]] = field[1];
|
||||
});
|
||||
$scope.notification_obj = data;
|
||||
|
||||
$scope.parse_type = 'json';
|
||||
if (!$scope.headers) {
|
||||
$scope.headers = "{\n}";
|
||||
}
|
||||
ParseTypeChange({
|
||||
scope: $scope,
|
||||
parse_variable: 'parse_type',
|
||||
variable: 'headers',
|
||||
field_id: 'notification_template_headers',
|
||||
});
|
||||
Wait('stop');
|
||||
})
|
||||
.error(function (data, status) {
|
||||
ProcessErrors($scope, data, status, form, { hdr: 'Error!',
|
||||
msg: 'Failed to retrieve notification: ' + id + '. GET status: ' + status });
|
||||
});
|
||||
});
|
||||
LookUpInit({
|
||||
url: GetBasePath('organization'),
|
||||
scope: $scope,
|
||||
form: form,
|
||||
list: OrganizationList,
|
||||
field: 'organization',
|
||||
input_type: 'radio'
|
||||
});
|
||||
|
||||
GetChoices({
|
||||
scope: $scope,
|
||||
url: url,
|
||||
@@ -144,130 +42,224 @@ export default
|
||||
variable: 'notification_type_options',
|
||||
callback: 'choicesReady'
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
$scope.$watch('headers', function validate_headers(str) {
|
||||
try {
|
||||
let headers = JSON.parse(str);
|
||||
if (_.isObject(headers) && !_.isArray(headers)) {
|
||||
let valid = true;
|
||||
for (let k in headers) {
|
||||
if (_.isObject(headers[k])) {
|
||||
valid = false;
|
||||
}
|
||||
if (headers[k] === null) {
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
$scope.notification_template_form.headers.$setValidity('json', valid);
|
||||
return;
|
||||
}
|
||||
} catch (err) {
|
||||
if ($scope.removeChoicesReady) {
|
||||
$scope.removeChoicesReady();
|
||||
}
|
||||
$scope.removeChoicesReady = $scope.$on('choicesReady', function() {
|
||||
var i;
|
||||
for (i = 0; i < $scope.notification_type_options.length; i++) {
|
||||
if ($scope.notification_type_options[i].value === '') {
|
||||
$scope.notification_type_options[i].value = "manual";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$scope.notification_template_form.headers.$setValidity('json', false);
|
||||
});
|
||||
Wait('start');
|
||||
Rest.setUrl(url + id + '/');
|
||||
Rest.get()
|
||||
.success(function(data) {
|
||||
var fld;
|
||||
for (fld in form.fields) {
|
||||
if (data[fld]) {
|
||||
$scope[fld] = data[fld];
|
||||
master[fld] = data[fld];
|
||||
}
|
||||
|
||||
$scope.typeChange = function () {
|
||||
for(var fld in form.fields){
|
||||
if(form.fields[fld] && form.fields[fld].subForm){
|
||||
if(form.fields[fld].type === 'checkbox_group' && form.fields[fld].fields) {
|
||||
// Need to loop across the groups fields to null them out
|
||||
for(var i=0; i<form.fields[fld].fields.length; i++) {
|
||||
// Pull the name out of the object (array of objects)
|
||||
var subFldName = form.fields[fld].fields[i].name;
|
||||
$scope[subFldName] = null;
|
||||
$scope.notification_template_form[subFldName].$setPristine();
|
||||
if (form.fields[fld].type === 'checkbox_group') {
|
||||
// Loop across the group and put the child data on scope
|
||||
for (var j = 0; j < form.fields[fld].fields.length; j++) {
|
||||
if (data.notification_configuration[form.fields[fld].fields[j].name]) {
|
||||
$scope[form.fields[fld].fields[j].name] = data.notification_configuration[form.fields[fld].fields[j].name];
|
||||
master[form.fields[fld].fields[j].name] = data.notification_configuration[form.fields[fld].fields[j].name];
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
$scope[fld] = null;
|
||||
$scope.notification_template_form[fld].$setPristine();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NotificationsTypeChange.getDetailFields($scope.notification_type.value).forEach(function(field) {
|
||||
$scope[field[0]] = field[1];
|
||||
});
|
||||
|
||||
$scope.parse_type = 'json';
|
||||
if (!$scope.headers) {
|
||||
$scope.headers = "{\n}";
|
||||
}
|
||||
ParseTypeChange({
|
||||
scope: $scope,
|
||||
parse_variable: 'parse_type',
|
||||
variable: 'headers',
|
||||
field_id: 'notification_template_headers',
|
||||
});
|
||||
};
|
||||
|
||||
$scope.formSave = function(){
|
||||
var params,
|
||||
v = $scope.notification_type.value;
|
||||
|
||||
generator.clearApiErrors();
|
||||
params = {
|
||||
"name" : $scope.name,
|
||||
"description": $scope.description,
|
||||
"organization": $scope.organization,
|
||||
"notification_type" : v,
|
||||
"notification_configuration": {}
|
||||
};
|
||||
|
||||
function processValue(value, i , field){
|
||||
if(field.type === 'textarea'){
|
||||
if (field.name === 'headers') {
|
||||
$scope[i] = JSON.parse($scope[i]);
|
||||
} else {
|
||||
$scope[i] = $scope[i].toString().split('\n');
|
||||
if (data.notification_configuration[fld]) {
|
||||
$scope[fld] = data.notification_configuration[fld];
|
||||
master[fld] = data.notification_configuration[fld];
|
||||
|
||||
if (form.fields[fld].type === 'textarea') {
|
||||
if (form.fields[fld].name === 'headers') {
|
||||
$scope[fld] = JSON.stringify($scope[fld], null, 2);
|
||||
} else {
|
||||
$scope[fld] = $scope[fld].toString().replace(',', '\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (form.fields[fld].sourceModel && data.summary_fields &&
|
||||
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] =
|
||||
data.summary_fields[form.fields[fld].sourceModel][form.fields[fld].sourceField];
|
||||
}
|
||||
}
|
||||
}
|
||||
if(field.type === 'checkbox'){
|
||||
$scope[i] = Boolean($scope[i]);
|
||||
data.notification_type = (Empty(data.notification_type)) ? '' : data.notification_type;
|
||||
for (var i = 0; i < $scope.notification_type_options.length; i++) {
|
||||
if ($scope.notification_type_options[i].value === data.notification_type) {
|
||||
$scope.notification_type = $scope.notification_type_options[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(field.type === 'number'){
|
||||
$scope[i] = Number($scope[i]);
|
||||
}
|
||||
if(field.name === "username" && $scope.notification_type.value === "email" && value === null){
|
||||
$scope[i] = "";
|
||||
}
|
||||
if(field.type === 'sensitive' && value === null){
|
||||
$scope[i] = "";
|
||||
}
|
||||
return $scope[i];
|
||||
}
|
||||
|
||||
params.notification_configuration = _.object(Object.keys(form.fields)
|
||||
.filter(i => (form.fields[i].ngShow && form.fields[i].ngShow.indexOf(v) > -1))
|
||||
.map(i => [i, processValue($scope[i], i , form.fields[i])]));
|
||||
master.notification_type = $scope.notification_type;
|
||||
CreateSelect2({
|
||||
element: '#notification_template_notification_type',
|
||||
multiple: false
|
||||
});
|
||||
NotificationsTypeChange.getDetailFields($scope.notification_type.value).forEach(function(field) {
|
||||
$scope[field[0]] = field[1];
|
||||
});
|
||||
$scope.notification_obj = data;
|
||||
|
||||
delete params.notification_configuration.checkbox_group;
|
||||
|
||||
for(var j = 0; j < form.fields.checkbox_group.fields.length; j++) {
|
||||
if(form.fields.checkbox_group.fields[j].ngShow && form.fields.checkbox_group.fields[j].ngShow.indexOf(v) > -1) {
|
||||
params.notification_configuration[form.fields.checkbox_group.fields[j].name] = Boolean($scope[form.fields.checkbox_group.fields[j].name]);
|
||||
$scope.parse_type = 'json';
|
||||
if (!$scope.headers) {
|
||||
$scope.headers = "{\n}";
|
||||
}
|
||||
}
|
||||
|
||||
Wait('start');
|
||||
Rest.setUrl(url+ id+'/');
|
||||
Rest.put(params)
|
||||
.success(function () {
|
||||
$state.go($state.current, null, {reload: true});
|
||||
ParseTypeChange({
|
||||
scope: $scope,
|
||||
parse_variable: 'parse_type',
|
||||
variable: 'headers',
|
||||
field_id: 'notification_template_headers',
|
||||
});
|
||||
Wait('stop');
|
||||
})
|
||||
.error(function (data, status) {
|
||||
ProcessErrors($scope, data, status, form, { hdr: 'Error!',
|
||||
msg: 'Failed to add new notification template. POST returned status: ' + status });
|
||||
.error(function(data, status) {
|
||||
ProcessErrors($scope, data, status, form, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to retrieve notification: ' + id + '. GET status: ' + status
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
$scope.$watch('headers', function validate_headers(str) {
|
||||
try {
|
||||
let headers = JSON.parse(str);
|
||||
if (_.isObject(headers) && !_.isArray(headers)) {
|
||||
let valid = true;
|
||||
for (let k in headers) {
|
||||
if (_.isObject(headers[k])) {
|
||||
valid = false;
|
||||
}
|
||||
if (headers[k] === null) {
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
$scope.notification_template_form.headers.$setValidity('json', valid);
|
||||
return;
|
||||
}
|
||||
} catch (err) {}
|
||||
|
||||
$scope.notification_template_form.headers.$setValidity('json', false);
|
||||
});
|
||||
|
||||
$scope.typeChange = function() {
|
||||
for (var fld in form.fields) {
|
||||
if (form.fields[fld] && form.fields[fld].subForm) {
|
||||
if (form.fields[fld].type === 'checkbox_group' && form.fields[fld].fields) {
|
||||
// Need to loop across the groups fields to null them out
|
||||
for (var i = 0; i < form.fields[fld].fields.length; i++) {
|
||||
// Pull the name out of the object (array of objects)
|
||||
var subFldName = form.fields[fld].fields[i].name;
|
||||
$scope[subFldName] = null;
|
||||
$scope.notification_template_form[subFldName].$setPristine();
|
||||
}
|
||||
} else {
|
||||
$scope[fld] = null;
|
||||
$scope.notification_template_form[fld].$setPristine();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NotificationsTypeChange.getDetailFields($scope.notification_type.value).forEach(function(field) {
|
||||
$scope[field[0]] = field[1];
|
||||
});
|
||||
|
||||
$scope.parse_type = 'json';
|
||||
if (!$scope.headers) {
|
||||
$scope.headers = "{\n}";
|
||||
}
|
||||
ParseTypeChange({
|
||||
scope: $scope,
|
||||
parse_variable: 'parse_type',
|
||||
variable: 'headers',
|
||||
field_id: 'notification_template_headers',
|
||||
});
|
||||
};
|
||||
|
||||
$scope.formSave = function() {
|
||||
var params,
|
||||
v = $scope.notification_type.value;
|
||||
|
||||
generator.clearApiErrors();
|
||||
params = {
|
||||
"name": $scope.name,
|
||||
"description": $scope.description,
|
||||
"organization": $scope.organization,
|
||||
"notification_type": v,
|
||||
"notification_configuration": {}
|
||||
};
|
||||
|
||||
function processValue(value, i, field) {
|
||||
if (field.type === 'textarea') {
|
||||
if (field.name === 'headers') {
|
||||
$scope[i] = JSON.parse($scope[i]);
|
||||
} else {
|
||||
$scope[i] = $scope[i].toString().split('\n');
|
||||
}
|
||||
}
|
||||
if (field.type === 'checkbox') {
|
||||
$scope[i] = Boolean($scope[i]);
|
||||
}
|
||||
if (field.type === 'number') {
|
||||
$scope[i] = Number($scope[i]);
|
||||
}
|
||||
if (field.name === "username" && $scope.notification_type.value === "email" && value === null) {
|
||||
$scope[i] = "";
|
||||
}
|
||||
if (field.type === 'sensitive' && value === null) {
|
||||
$scope[i] = "";
|
||||
}
|
||||
return $scope[i];
|
||||
}
|
||||
|
||||
$scope.formCancel = function () {
|
||||
$state.transitionTo('notifications');
|
||||
};
|
||||
params.notification_configuration = _.object(Object.keys(form.fields)
|
||||
.filter(i => (form.fields[i].ngShow && form.fields[i].ngShow.indexOf(v) > -1))
|
||||
.map(i => [i, processValue($scope[i], i, form.fields[i])]));
|
||||
|
||||
}
|
||||
];
|
||||
delete params.notification_configuration.checkbox_group;
|
||||
|
||||
for (var j = 0; j < form.fields.checkbox_group.fields.length; j++) {
|
||||
if (form.fields.checkbox_group.fields[j].ngShow && form.fields.checkbox_group.fields[j].ngShow.indexOf(v) > -1) {
|
||||
params.notification_configuration[form.fields.checkbox_group.fields[j].name] = Boolean($scope[form.fields.checkbox_group.fields[j].name]);
|
||||
}
|
||||
}
|
||||
|
||||
Wait('start');
|
||||
Rest.setUrl(url + id + '/');
|
||||
Rest.put(params)
|
||||
.success(function() {
|
||||
$state.go($state.current, null, { reload: true });
|
||||
Wait('stop');
|
||||
})
|
||||
.error(function(data, status) {
|
||||
ProcessErrors($scope, data, status, form, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to add new notification template. POST returned status: ' + status
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
$scope.formCancel = function() {
|
||||
$state.go('notifications');
|
||||
};
|
||||
|
||||
}
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user