From 55d74f097537b971254621c4cc0d583c922ffe86 Mon Sep 17 00:00:00 2001 From: Michael Abashian Date: Thu, 26 Jan 2017 15:37:18 -0500 Subject: [PATCH] Launch job with prompts fixes --- .../rbac-multiselect/permissionsTeams.list.js | 3 +- .../dashboard/hosts/dashboard-hosts.list.js | 3 +- awx/ui/client/src/forms/Credentials.js | 6 +- awx/ui/client/src/forms/Organizations.js | 6 +- awx/ui/client/src/forms/Teams.js | 3 +- .../job-submission.controller.js | 109 +++--------------- .../job-submission.partial.html | 4 +- .../job-sub-cred-list.controller.js | 32 +++++ .../credential/job-sub-cred-list.directive.js | 63 ++++++++++ .../credential/job-sub-cred-list.partial.html | 3 + .../inventory/job-sub-inv-list.controller.js | 32 +++++ .../inventory/job-sub-inv-list.directive.js | 60 ++++++++++ .../inventory/job-sub-inv-list.partial.html | 3 + awx/ui/client/src/job-submission/main.js | 6 +- .../notificationTemplates.list.js | 1 - .../src/notifications/notifications.list.js | 8 +- .../column-sort/column-sort.controller.js | 28 +++-- .../column-sort/column-sort.directive.js | 1 + .../list-generator/list-generator.factory.js | 38 +++--- .../shared/paginate/paginate.controller.js | 43 ++++++- .../src/shared/paginate/paginate.directive.js | 3 +- .../smart-search/smart-search.controller.js | 54 +++++++-- .../smart-search/smart-search.directive.js | 4 +- .../workflow-chart.directive.js | 2 +- 24 files changed, 355 insertions(+), 160 deletions(-) create mode 100644 awx/ui/client/src/job-submission/lists/credential/job-sub-cred-list.controller.js create mode 100644 awx/ui/client/src/job-submission/lists/credential/job-sub-cred-list.directive.js create mode 100644 awx/ui/client/src/job-submission/lists/credential/job-sub-cred-list.partial.html create mode 100644 awx/ui/client/src/job-submission/lists/inventory/job-sub-inv-list.controller.js create mode 100644 awx/ui/client/src/job-submission/lists/inventory/job-sub-inv-list.directive.js create mode 100644 awx/ui/client/src/job-submission/lists/inventory/job-sub-inv-list.partial.html diff --git a/awx/ui/client/src/access/rbac-multiselect/permissionsTeams.list.js b/awx/ui/client/src/access/rbac-multiselect/permissionsTeams.list.js index ae7bdd3d5d..8986478e85 100644 --- a/awx/ui/client/src/access/rbac-multiselect/permissionsTeams.list.js +++ b/awx/ui/client/src/access/rbac-multiselect/permissionsTeams.list.js @@ -25,8 +25,7 @@ label: 'organization', ngBind: 'team.summary_fields.organization.name', sourceModel: 'organization', - sourceField: 'name', - searchable: true + sourceField: 'name' } } diff --git a/awx/ui/client/src/dashboard/hosts/dashboard-hosts.list.js b/awx/ui/client/src/dashboard/hosts/dashboard-hosts.list.js index f439c0c859..dd4531c98e 100644 --- a/awx/ui/client/src/dashboard/hosts/dashboard-hosts.list.js +++ b/awx/ui/client/src/dashboard/hosts/dashboard-hosts.list.js @@ -42,8 +42,7 @@ export default [ 'i18n', function(i18n){ sourceModel: 'inventory', sourceField: 'name', columnClass: 'col-lg-5 col-md-4 col-sm-4 hidden-xs elllipsis', - linkTo: "{{ '/#/inventories/' + host.inventory_id }}", - searchable: false + linkTo: "{{ '/#/inventories/' + host.inventory_id }}" }, enabled: { label: i18n._('Status'), diff --git a/awx/ui/client/src/forms/Credentials.js b/awx/ui/client/src/forms/Credentials.js index 6a26c72d64..ccea4dca07 100644 --- a/awx/ui/client/src/forms/Credentials.js +++ b/awx/ui/client/src/forms/Credentials.js @@ -461,15 +461,13 @@ export default label: i18n._('Role'), type: 'role', noSort: true, - class: 'col-lg-4 col-md-4 col-sm-4 col-xs-4', - searchable: false + class: 'col-lg-4 col-md-4 col-sm-4 col-xs-4' }, team_roles: { label: i18n._('Team Roles'), type: 'team_roles', noSort: true, - class: 'col-lg-5 col-md-5 col-sm-5 col-xs-4', - searchable: false + class: 'col-lg-5 col-md-5 col-sm-5 col-xs-4' } } } diff --git a/awx/ui/client/src/forms/Organizations.js b/awx/ui/client/src/forms/Organizations.js index b50d7eeb3a..d8f12ea371 100644 --- a/awx/ui/client/src/forms/Organizations.js +++ b/awx/ui/client/src/forms/Organizations.js @@ -88,15 +88,13 @@ export default label: i18n._('Role'), type: 'role', noSort: true, - class: 'col-lg-4 col-md-4 col-sm-4 col-xs-4', - searchable: false + class: 'col-lg-4 col-md-4 col-sm-4 col-xs-4' }, team_roles: { label: i18n._('Team Roles'), type: 'team_roles', noSort: true, - class: 'col-lg-5 col-md-5 col-sm-5 col-xs-4', - searchable: false + class: 'col-lg-5 col-md-5 col-sm-5 col-xs-4' } } }, diff --git a/awx/ui/client/src/forms/Teams.js b/awx/ui/client/src/forms/Teams.js index 5ac0ee7aa1..3154dd42ed 100644 --- a/awx/ui/client/src/forms/Teams.js +++ b/awx/ui/client/src/forms/Teams.js @@ -98,8 +98,7 @@ export default label: i18n._('Role'), type: 'role', noSort: true, - class: 'col-lg-4 col-md-4 col-sm-4 col-xs-4', - searchable: false + class: 'col-lg-4 col-md-4 col-sm-4 col-xs-4' } } }, diff --git a/awx/ui/client/src/job-submission/job-submission.controller.js b/awx/ui/client/src/job-submission/job-submission.controller.js index 8a8215baef..49fb8b7640 100644 --- a/awx/ui/client/src/job-submission/job-submission.controller.js +++ b/awx/ui/client/src/job-submission/job-submission.controller.js @@ -63,9 +63,11 @@ export default [ '$scope', '$location', 'GetBasePath', 'Empty', 'Wait', 'Rest', 'ProcessErrors', - 'LaunchJob', '$state', 'generateList', 'InventoryList', 'CredentialList', 'ParseTypeChange', 'GetSurveyQuestions', + 'LaunchJob', '$state', 'generateList', 'InventoryList', 'CredentialList', 'ParseTypeChange', + 'GetSurveyQuestions', function($scope, $location, GetBasePath, Empty, Wait, Rest, ProcessErrors, - LaunchJob, $state, GenerateList, InventoryList, CredentialList, ParseTypeChange, GetSurveyQuestions) { + LaunchJob, $state, GenerateList, InventoryList, CredentialList, ParseTypeChange, + GetSurveyQuestions) { var launch_url; @@ -296,100 +298,10 @@ export default $scope.getListsAndSurvey = function() { if($scope.ask_inventory_on_launch) { - // @issue: OLD SEARCH - // var inventory_url = GetBasePath('inventory'); - - var invList = _.cloneDeep(InventoryList); - invList.fields.status.searchable = false; - invList.fields.organization.searchable = false; - invList.fields.has_inventory_sources.searchable = false; - invList.fields.has_active_failures.searchable = false; - invList.fields.inventory_sources_with_failures.searchable = false; - - GenerateList.inject(invList, { - mode: 'lookup', - id: 'job-submission-inventory-lookup', - scope: $scope, - input_type: 'radio' - }); - - // @issue: OLD SEARCH - // SearchInit({ - // scope: $scope, - // set: InventoryList.name, - // list: InventoryList, - // url: inventory_url - // }); - // - // PaginateInit({ - // scope: $scope, - // list: InventoryList, - // url: inventory_url, - // mode: 'lookup' - // }); - // - // $scope.search(InventoryList.iterator); - - $scope.$watchCollection('inventories', function () { - if($scope.selected_inventory) { - // Loop across the inventories and see if one of them should be "checked" - $scope.inventories.forEach(function(row, i) { - if (row.id === $scope.selected_inventory.id) { - $scope.inventories[i].checked = 1; - } - else { - $scope.inventories[i].checked = 0; - } - }); - } - }); + $scope.includeInventoryList = true; } if($scope.ask_credential_on_launch) { - // @issue: OLD SEARCH - // var credential_url = GetBasePath('credentials') + '?kind=ssh'; - - var credList = _.cloneDeep(CredentialList); - credList.basePath = GetBasePath('credentials') + '?kind=ssh'; - credList.fields.description.searchable = false; - credList.fields.kind.searchable = false; - - GenerateList.inject(credList, { - mode: 'lookup', - id: 'job-submission-credential-lookup', - scope: $scope, - input_type: 'radio' - }); - - // @issue: OLD SEARCH - // SearchInit({ - // scope: $scope, - // set: CredentialList.name, - // list: CredentialList, - // url: credential_url - // }); - // - // PaginateInit({ - // scope: $scope, - // list: CredentialList, - // url: credential_url, - // mode: 'lookup' - // }); - // - // $scope.search(CredentialList.iterator); - - $scope.$watchCollection('credentials', function () { - if($scope.selected_credential) { - // Loop across the inventories and see if one of them should be "checked" - $scope.credentials.forEach(function(row, i) { - if (row.id === $scope.selected_credential.id) { - $scope.credentials[i].checked = 1; - } - else { - $scope.credentials[i].checked = 0; - } - }); - } - }); + $scope.includeCredentialList = true; } if($scope.survey_enabled) { GetSurveyQuestions({ @@ -562,5 +474,14 @@ export default $scope.parseTypeChange('parseType', 'jobLaunchVariables'); }; + $scope.$on('inventorySelected', function(evt, selectedRow){ + $scope.selected_inventory = _.cloneDeep(selectedRow); + }); + + $scope.$on('credentialSelected', function(evt, selectedRow){ + $scope.selected_credential = _.cloneDeep(selectedRow); + updateRequiredPasswords(); + }); + } ]; diff --git a/awx/ui/client/src/job-submission/job-submission.partial.html b/awx/ui/client/src/job-submission/job-submission.partial.html index ed9b7ec620..d3c6689e1c 100644 --- a/awx/ui/client/src/job-submission/job-submission.partial.html +++ b/awx/ui/client/src/job-submission/job-submission.partial.html @@ -29,7 +29,7 @@ -
+
@@ -44,7 +44,7 @@
-
+
Launching this job requires the passwords listed below. Enter and confirm each password before continuing.
diff --git a/awx/ui/client/src/job-submission/lists/credential/job-sub-cred-list.controller.js b/awx/ui/client/src/job-submission/lists/credential/job-sub-cred-list.controller.js new file mode 100644 index 0000000000..781c9b98fe --- /dev/null +++ b/awx/ui/client/src/job-submission/lists/credential/job-sub-cred-list.controller.js @@ -0,0 +1,32 @@ +/************************************************* + * Copyright (c) 2017 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + +export default + [ '$scope', + function($scope) { + $scope.toggle_row = function(rowId) { + let list = $scope.list; + let count = 0; + $scope[list.name].forEach(function(row) { + if (row.id === rowId) { + if (row.checked) { + row.success_class = 'success'; + } else { + row.checked = true; + row.success_class = ''; + } + $scope.$emit('credentialSelected', row); + } else { + row.checked = 0; + row.success_class = ''; + } + if (row.checked) { + count++; + } + }); + }; + } + ]; diff --git a/awx/ui/client/src/job-submission/lists/credential/job-sub-cred-list.directive.js b/awx/ui/client/src/job-submission/lists/credential/job-sub-cred-list.directive.js new file mode 100644 index 0000000000..ca33857d21 --- /dev/null +++ b/awx/ui/client/src/job-submission/lists/credential/job-sub-cred-list.directive.js @@ -0,0 +1,63 @@ +/************************************************* + * Copyright (c) 2016 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + +import jobSubCredListController from './job-sub-cred-list.controller'; + +export default [ 'templateUrl', 'QuerySet', 'GetBasePath', 'generateList', '$compile', 'CredentialList', + function(templateUrl, qs, GetBasePath, GenerateList, $compile, CredentialList) { + return { + scope: {}, + templateUrl: templateUrl('job-submission/lists/credential/job-sub-cred-list'), + controller: jobSubCredListController, + restrict: 'E', + link: function(scope) { + scope.credential_default_params = { + order_by: 'name', + page_size: 5, + kind: 'ssh' + }; + + scope.credential_queryset = { + order_by: 'name', + page_size: 5, + kind: 'ssh' + }; + + // Fire off the initial search + qs.search(GetBasePath('credentials'), scope.credential_default_params) + .then(function(res) { + scope.credential_dataset = res.data; + scope.credentials = scope.credential_dataset.results; + + var credList = _.cloneDeep(CredentialList); + let html = GenerateList.build({ + list: credList, + input_type: 'radio', + mode: 'lookup' + }); + + scope.list = credList; + + $('#job-submission-credential-lookup').append($compile(html)(scope)); + + scope.$watchCollection('credentials', function () { + if(scope.selected_credential) { + // Loop across the inventories and see if one of them should be "checked" + scope.credentials.forEach(function(row, i) { + if (row.id === scope.selected_credential.id) { + scope.credentials[i].checked = 1; + } + else { + scope.credentials[i].checked = 0; + } + }); + } + }); + + }); + } + }; +}]; diff --git a/awx/ui/client/src/job-submission/lists/credential/job-sub-cred-list.partial.html b/awx/ui/client/src/job-submission/lists/credential/job-sub-cred-list.partial.html new file mode 100644 index 0000000000..d184dc7efb --- /dev/null +++ b/awx/ui/client/src/job-submission/lists/credential/job-sub-cred-list.partial.html @@ -0,0 +1,3 @@ +
+
+
diff --git a/awx/ui/client/src/job-submission/lists/inventory/job-sub-inv-list.controller.js b/awx/ui/client/src/job-submission/lists/inventory/job-sub-inv-list.controller.js new file mode 100644 index 0000000000..4c1acbcf94 --- /dev/null +++ b/awx/ui/client/src/job-submission/lists/inventory/job-sub-inv-list.controller.js @@ -0,0 +1,32 @@ +/************************************************* + * Copyright (c) 2017 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + +export default + [ '$scope', + function($scope) { + $scope.toggle_row = function(rowId) { + let list = $scope.list; + let count = 0; + $scope[list.name].forEach(function(row) { + if (row.id === rowId) { + if (row.checked) { + row.success_class = 'success'; + } else { + row.checked = true; + row.success_class = ''; + } + $scope.$emit('inventorySelected', row); + } else { + row.checked = 0; + row.success_class = ''; + } + if (row.checked) { + count++; + } + }); + }; + } + ]; diff --git a/awx/ui/client/src/job-submission/lists/inventory/job-sub-inv-list.directive.js b/awx/ui/client/src/job-submission/lists/inventory/job-sub-inv-list.directive.js new file mode 100644 index 0000000000..adb3bd8c12 --- /dev/null +++ b/awx/ui/client/src/job-submission/lists/inventory/job-sub-inv-list.directive.js @@ -0,0 +1,60 @@ +/************************************************* + * Copyright (c) 2016 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + +import jobSubInvListController from './job-sub-inv-list.controller'; + +export default [ 'templateUrl', 'QuerySet', 'GetBasePath', 'generateList', '$compile', 'InventoryList', + function(templateUrl, qs, GetBasePath, GenerateList, $compile, InventoryList) { + return { + scope: {}, + templateUrl: templateUrl('job-submission/lists/inventory/job-sub-inv-list'), + controller: jobSubInvListController, + restrict: 'E', + link: function(scope) { + scope.inventory_default_params = { + order_by: 'name', + page_size: 5 + }; + + scope.inventory_queryset = { + order_by: 'name', + page_size: 5 + }; + + // Fire off the initial search + qs.search(GetBasePath('inventory'), scope.inventory_default_params) + .then(function(res) { + scope.inventory_dataset = res.data; + scope.inventories = scope.inventory_dataset.results; + + var invList = _.cloneDeep(InventoryList); + let html = GenerateList.build({ + list: invList, + input_type: 'radio', + mode: 'lookup' + }); + + scope.list = invList; + + $('#job-submission-inventory-lookup').append($compile(html)(scope)); + + scope.$watchCollection('inventories', function () { + if(scope.selected_inventory) { + // Loop across the inventories and see if one of them should be "checked" + scope.inventories.forEach(function(row, i) { + if (row.id === scope.selected_inventory.id) { + scope.inventories[i].checked = 1; + } + else { + scope.inventories[i].checked = 0; + } + }); + } + }); + }); + } + }; +}]; diff --git a/awx/ui/client/src/job-submission/lists/inventory/job-sub-inv-list.partial.html b/awx/ui/client/src/job-submission/lists/inventory/job-sub-inv-list.partial.html new file mode 100644 index 0000000000..2d30395c72 --- /dev/null +++ b/awx/ui/client/src/job-submission/lists/inventory/job-sub-inv-list.partial.html @@ -0,0 +1,3 @@ +
+
+
diff --git a/awx/ui/client/src/job-submission/main.js b/awx/ui/client/src/job-submission/main.js index 711bf00a2f..71c4c5c0de 100644 --- a/awx/ui/client/src/job-submission/main.js +++ b/awx/ui/client/src/job-submission/main.js @@ -8,10 +8,14 @@ import InitiatePlaybookRun from './job-submission-factories/initiateplaybookrun. import LaunchJob from './job-submission-factories/launchjob.factory'; import GetSurveyQuestions from './job-submission-factories/getsurveyquestions.factory'; import submitJob from './job-submission.directive'; +import credentialList from './lists/credential/job-sub-cred-list.directive'; +import inventoryList from './lists/inventory/job-sub-inv-list.directive'; export default angular.module('jobSubmission', []) .factory('InitiatePlaybookRun', InitiatePlaybookRun) .factory('LaunchJob', LaunchJob) .factory('GetSurveyQuestions', GetSurveyQuestions) - .directive('submitJob', submitJob); + .directive('submitJob', submitJob) + .directive('jobSubCredList', credentialList) + .directive('jobSubInvList', inventoryList); diff --git a/awx/ui/client/src/notifications/notificationTemplates.list.js b/awx/ui/client/src/notifications/notificationTemplates.list.js index 091f6b1f22..92324c8701 100644 --- a/awx/ui/client/src/notifications/notificationTemplates.list.js +++ b/awx/ui/client/src/notifications/notificationTemplates.list.js @@ -20,7 +20,6 @@ export default ['i18n', function(i18n){ status: { label: '', iconOnly: true, - searchable: false, nosort: true, icon: 'icon-job-{{ notification_template.status }}', awPopOver: '{{ notification_template.template_status_html }}', diff --git a/awx/ui/client/src/notifications/notifications.list.js b/awx/ui/client/src/notifications/notifications.list.js index 7a7a345c96..ad554ac3f2 100644 --- a/awx/ui/client/src/notifications/notifications.list.js +++ b/awx/ui/client/src/notifications/notifications.list.js @@ -25,7 +25,7 @@ export default ['i18n', function(i18n){ key: true, label: i18n._('Name'), columnClass: 'col-md-3 col-sm-9 col-xs-9', - linkTo: '/#/notification_templates/{{notifier.id}}', + linkTo: '/#/notification_templates/{{notifier.id}}' }, notification_type: { label: i18n._('Type'), @@ -42,8 +42,7 @@ export default ['i18n', function(i18n){ awToolTip: "{{ schedule.play_tip }}", dataTipWatch: "schedule.play_tip", dataPlacement: "right", - searchable: false, - nosort: true, + nosort: true }, notification_templates_error: { label: i18n._('Failure'), @@ -54,8 +53,7 @@ export default ['i18n', function(i18n){ awToolTip: "{{ schedule.play_tip }}", dataTipWatch: "schedule.play_tip", dataPlacement: "right", - searchable: false, - nosort: true, + nosort: true } }, actions: { diff --git a/awx/ui/client/src/shared/column-sort/column-sort.controller.js b/awx/ui/client/src/shared/column-sort/column-sort.controller.js index eddf997f21..9280926fd0 100644 --- a/awx/ui/client/src/shared/column-sort/column-sort.controller.js +++ b/awx/ui/client/src/shared/column-sort/column-sort.controller.js @@ -1,7 +1,7 @@ export default ['$scope', '$state', 'QuerySet', 'GetBasePath', function($scope, $state, qs, GetBasePath) { - let queryset, path, order_by; + let queryset, path; function isDescending(str) { if (str){ @@ -16,13 +16,14 @@ export default ['$scope', '$state', 'QuerySet', 'GetBasePath', return str.charAt(0) === '-' ? `${str.substring(1, str.length)}` : `-${str}`; } $scope.orderByIcon = function() { - order_by = $state.params[`${$scope.columnIterator}_search`].order_by; + let order_by = $scope.querySet ? $scope.querySet.order_by : $state.params[`${$scope.columnIterator}_search`].order_by; // column sort is inactive if (order_by !== $scope.columnField && order_by !== invertOrderBy($scope.columnField)) { return 'fa-sort'; } // column sort is active (governed by order_by) and descending - else if (isDescending($state.params[`${$scope.columnIterator}_search`].order_by)) { + + else if (isDescending(order_by)) { return 'fa-sort-down'; } // column sort is active governed by order_by) and asscending @@ -32,7 +33,7 @@ export default ['$scope', '$state', 'QuerySet', 'GetBasePath', }; $scope.toggleColumnOrderBy = function() { - let order_by = $state.params[`${$scope.columnIterator}_search`].order_by; + let order_by = $scope.querySet ? $scope.querySet.order_by : $state.params[`${$scope.columnIterator}_search`].order_by; if (order_by === $scope.columnField || order_by === invertOrderBy($scope.columnField)) { order_by = invertOrderBy(order_by); @@ -41,11 +42,24 @@ export default ['$scope', '$state', 'QuerySet', 'GetBasePath', else { order_by = $scope.columnField; } - - queryset = _.merge($state.params[`${$scope.columnIterator}_search`], { order_by: order_by }); + if($scope.querySet) { + // merging $scope.querySet seems to destroy our initial reference which + // kills the two-way binding here. To fix that, clone the queryset first + // and merge with that object. + let origQuerySet = _.cloneDeep($scope.querySet); + queryset = _.merge(origQuerySet, { order_by: order_by }); + } + else { + queryset = _.merge($state.params[`${$scope.columnIterator}_search`], { order_by: order_by }); + } path = GetBasePath($scope.basePath) || $scope.basePath; - $state.go('.', { [$scope.columnIterator + '_search']: queryset }, {notify: false}); + if(!$scope.querySet) { + $state.go('.', { [$scope.columnIterator + '_search']: queryset }, {notify: false}); + } qs.search(path, queryset).then((res) =>{ + if($scope.querySet) { + $scope.querySet = queryset; + } $scope.dataset = res.data; $scope.collection = res.data.results; }); diff --git a/awx/ui/client/src/shared/column-sort/column-sort.directive.js b/awx/ui/client/src/shared/column-sort/column-sort.directive.js index d8e5f54bba..38aff7d41e 100644 --- a/awx/ui/client/src/shared/column-sort/column-sort.directive.js +++ b/awx/ui/client/src/shared/column-sort/column-sort.directive.js @@ -12,6 +12,7 @@ export default ['templateUrl', function(templateUrl) { columnIterator: '@', columnField: '@', columnLabel: '@', + querySet: '=' }, controller: 'ColumnSortController', templateUrl: templateUrl('shared/column-sort/column-sort') diff --git a/awx/ui/client/src/shared/list-generator/list-generator.factory.js b/awx/ui/client/src/shared/list-generator/list-generator.factory.js index 36cb5e5a65..5b71dad641 100644 --- a/awx/ui/client/src/shared/list-generator/list-generator.factory.js +++ b/awx/ui/client/src/shared/list-generator/list-generator.factory.js @@ -64,7 +64,6 @@ * | linkTo | Wraps the field value with an <a> element. Set to the value of the href attribute. | * | ngClick | Wraps the field value with an <a> and adds the ng-click directive. Set to the JS expression that ng-click will evaluate. | * | nosort | true or false. Setting to false removes the ability to sort the table by the column. | - * | searchable | true or fasel. Set to false if the field should not be included as in option in the search widget. | * | searchOnly | true or false. Set to true if the field should be included in the search widget but not included as a column in the generated HTML <table>. | * | searchOptions | Array of { name: 'Descriptive Name', value: 'api_value' } objects used to generate <options> for the <select> when searchType is 'select'. | * | searchType | One of the available search types defined in helpers/search.js. | @@ -200,18 +199,19 @@ export default ['$location', '$compile', '$rootScope', 'Attr', 'Icon', } if (options.showSearch === undefined || options.showSearch === true) { html += ` -
- - +
+ +
`; } @@ -450,7 +450,8 @@ export default ['$location', '$compile', '$rootScope', 'Attr', 'Icon', base-path="${list.basePath || list.name}" collection="${list.name}" dataset="${list.iterator}_dataset" - iterator="${list.iterator}"> + iterator="${list.iterator}" + query-set="${list.iterator}_queryset">
`; } @@ -499,7 +500,8 @@ export default ['$location', '$compile', '$rootScope', 'Attr', 'Icon', column-iterator="${list.iterator}" column-no-sort="${list.fields[fld].nosort}" column-label="${list.fields[fld].label}" - column-custom-class="${customClass}"> + column-custom-class="${customClass}" + query-set="${list.iterator}_queryset"> `; } } @@ -514,7 +516,8 @@ export default ['$location', '$compile', '$rootScope', 'Attr', 'Icon', column-iterator="${list.iterator}" column-no-sort="${list.fields.name.nosort}" column-label="${list.fields.name.label}" - column-custom-class="${customClass}"> + column-custom-class="${customClass}" + query-set="${list.iterator}_queryset"> `; if(list.fields.info) { @@ -529,7 +532,8 @@ export default ['$location', '$compile', '$rootScope', 'Attr', 'Icon', column-iterator="${list.iterator}" column-no-sort="${list.fields.info.nosort}" column-label="${list.fields.info.label}" - column-custom-class="${customClass}"> + column-custom-class="${customClass}" + query-set="${list.iterator}_queryset"> `; } } diff --git a/awx/ui/client/src/shared/paginate/paginate.controller.js b/awx/ui/client/src/shared/paginate/paginate.controller.js index 044ad55859..a4f45b7d5a 100644 --- a/awx/ui/client/src/shared/paginate/paginate.controller.js +++ b/awx/ui/client/src/shared/paginate/paginate.controller.js @@ -1,8 +1,18 @@ export default ['$scope', '$stateParams', '$state', '$filter', 'GetBasePath', 'QuerySet', function($scope, $stateParams, $state, $filter, GetBasePath, qs) { - let pageSize = $stateParams[`${$scope.iterator}_search`].page_size || 20, + let pageSize, queryset, path; + + // TODO: can we clean this if/else up? + if($scope.querySet) { + pageSize = $scope.querySet.page_size || 20; + } + else { + // Pull the page size from the url + pageSize = $stateParams[`${$scope.iterator}_search`].page_size || 20; + } + $scope.pageSize = pageSize; function init() { @@ -18,11 +28,27 @@ export default ['$scope', '$stateParams', '$state', '$filter', 'GetBasePath', 'Q return; } path = GetBasePath($scope.basePath) || $scope.basePath; - queryset = _.merge($stateParams[`${$scope.iterator}_search`], { page: page }); - $state.go('.', { - [$scope.iterator + '_search']: queryset - }, {notify: false}); + if($scope.querySet) { + // merging $scope.querySet seems to destroy our initial reference which + // kills the two-way binding here. To fix that, clone the queryset first + // and merge with that object. + let origQuerySet = _.cloneDeep($scope.querySet); + queryset = _.merge(origQuerySet, { page: page }); + + } + else { + queryset = _.merge($stateParams[`${$scope.iterator}_search`], { page: page }); + } + if(!$scope.querySet) { + $state.go('.', { + [$scope.iterator + '_search']: queryset + }, {notify: false}); + } qs.search(path, queryset).then((res) => { + if($scope.querySet) { + // Update the query set + $scope.querySet = queryset; + } $scope.dataset = res.data; $scope.collection = res.data.results; }); @@ -31,7 +57,12 @@ export default ['$scope', '$stateParams', '$state', '$filter', 'GetBasePath', 'Q }; $scope.current = function() { - return parseInt($stateParams[`${$scope.iterator}_search`].page || '1'); + if($scope.querySet) { + return parseInt($scope.querySet.page || '1'); + } + else { + return parseInt($stateParams[`${$scope.iterator}_search`].page || '1'); + } }; $scope.last = function() { diff --git a/awx/ui/client/src/shared/paginate/paginate.directive.js b/awx/ui/client/src/shared/paginate/paginate.directive.js index e839c5b6b0..ef849e7ff0 100644 --- a/awx/ui/client/src/shared/paginate/paginate.directive.js +++ b/awx/ui/client/src/shared/paginate/paginate.directive.js @@ -7,7 +7,8 @@ export default ['templateUrl', collection: '=', dataset: '=', iterator: '@', - basePath: '@' + basePath: '@', + querySet: '=' }, controller: 'PaginateController', templateUrl: templateUrl('shared/paginate/paginate') diff --git a/awx/ui/client/src/shared/smart-search/smart-search.controller.js b/awx/ui/client/src/shared/smart-search/smart-search.controller.js index f3d2602a26..c56fe07a0b 100644 --- a/awx/ui/client/src/shared/smart-search/smart-search.controller.js +++ b/awx/ui/client/src/shared/smart-search/smart-search.controller.js @@ -2,12 +2,26 @@ export default ['$stateParams', '$scope', '$state', 'QuerySet', 'GetBasePath', ' function($stateParams, $scope, $state, QuerySet, GetBasePath, qs, SmartSearchService) { let path, relations, + defaults, + queryset, + stateChangeSuccessListener; + + if($scope.defaultParams) { + defaults = $scope.defaultParams; + } + else { // steps through the current tree of $state configurations, grabs default search params defaults = _.find($state.$current.path, (step) => { return step.params.hasOwnProperty(`${$scope.iterator}_search`); - }).params[`${$scope.iterator}_search`].config.value, - queryset = $stateParams[`${$scope.iterator}_search`], - stateChangeSuccessListener; + }).params[`${$scope.iterator}_search`].config.value; + } + + if($scope.querySet) { + queryset = _.cloneDeep($scope.querySet); + } + else { + queryset = $stateParams[`${$scope.iterator}_search`]; + } // build $scope.tags from $stateParams.QuerySet, build fieldset key init(); @@ -109,8 +123,13 @@ export default ['$stateParams', '$scope', '$state', 'QuerySet', 'GetBasePath', ' let cleared = _.cloneDeep(defaults); delete cleared.page; queryset = cleared; - $state.go('.', {[$scope.iterator + '_search']: queryset}, {notify: false}); + if(!$scope.querySet) { + $state.go('.', {[$scope.iterator + '_search']: queryset}, {notify: false}); + } qs.search(path, queryset).then((res) => { + if($scope.querySet) { + $scope.querySet = queryset; + } $scope.dataset = res.data; $scope.collection = res.data.results; }); @@ -152,9 +171,14 @@ export default ['$stateParams', '$scope', '$state', 'QuerySet', 'GetBasePath', ' delete queryset[key]; } }); - $state.go('.', { - [$scope.iterator + '_search']: queryset }, {notify: false}); + if(!$scope.querySet) { + $state.go('.', { + [$scope.iterator + '_search']: queryset }, {notify: false}); + } qs.search(path, queryset).then((res) => { + if($scope.querySet) { + $scope.querySet = queryset; + } $scope.dataset = res.data; $scope.collection = res.data.results; }); @@ -223,9 +247,14 @@ export default ['$stateParams', '$scope', '$state', 'QuerySet', 'GetBasePath', ' // https://ui-router.github.io/docs/latest/interfaces/params.paramdeclaration.html#dynamic // This transition will not reload controllers/resolves/views // but will register new $stateParams[$scope.iterator + '_search'] terms - $state.go('.', { - [$scope.iterator + '_search']: queryset }, {notify: false}); + if(!$scope.querySet) { + $state.go('.', { + [$scope.iterator + '_search']: queryset }, {notify: false}); + } qs.search(path, queryset).then((res) => { + if($scope.querySet) { + $scope.querySet = queryset; + } $scope.dataset = res.data; $scope.collection = res.data.results; }) @@ -243,9 +272,14 @@ export default ['$stateParams', '$scope', '$state', 'QuerySet', 'GetBasePath', ' // https://ui-router.github.io/docs/latest/interfaces/params.paramdeclaration.html#dynamic // This transition will not reload controllers/resolves/views // but will register new $stateParams[$scope.iterator + '_search'] terms - $state.go('.', { - [$scope.iterator + '_search']: queryset }, {notify: false}); + if(!$scope.querySet) { + $state.go('.', { + [$scope.iterator + '_search']: queryset }, {notify: false}); + } qs.search(path, queryset).then((res) => { + if($scope.querySet) { + $scope.querySet = queryset; + } $scope.dataset = res.data; $scope.collection = res.data.results; }); diff --git a/awx/ui/client/src/shared/smart-search/smart-search.directive.js b/awx/ui/client/src/shared/smart-search/smart-search.directive.js index 73d3eafa89..180914dc1e 100644 --- a/awx/ui/client/src/shared/smart-search/smart-search.directive.js +++ b/awx/ui/client/src/shared/smart-search/smart-search.directive.js @@ -15,7 +15,9 @@ export default ['templateUrl', dataset: '=', collection: '=', searchTags: '=', - disableSearch: '=' + disableSearch: '=', + defaultParams: '=', + querySet: '=' }, controller: 'SmartSearchController', templateUrl: templateUrl('shared/smart-search/smart-search') diff --git a/awx/ui/client/src/templates/workflows/workflow-chart/workflow-chart.directive.js b/awx/ui/client/src/templates/workflows/workflow-chart/workflow-chart.directive.js index 0f0b9e1f58..945d1398a6 100644 --- a/awx/ui/client/src/templates/workflows/workflow-chart/workflow-chart.directive.js +++ b/awx/ui/client/src/templates/workflows/workflow-chart/workflow-chart.directive.js @@ -784,7 +784,7 @@ export default [ '$state','moment', } }); - scope.$on('refreshWorkflowChart', function(){console.log(scope.treeData); + scope.$on('refreshWorkflowChart', function(){ if(scope.treeData) { update(); }