diff --git a/awx/ui/static/js/controllers/Jobs.js b/awx/ui/static/js/controllers/Jobs.js index 35f49dbef3..05c49479ad 100644 --- a/awx/ui/static/js/controllers/Jobs.js +++ b/awx/ui/static/js/controllers/Jobs.js @@ -34,7 +34,16 @@ function JobsListController ($scope, $compile, ClearScope, Breadcrumbs, LoadBrea $scope.removeBuildJobsList(); } $scope.removeBuildJobsList = $scope.$on('buildJobsList', function() { - completed_scope = $scope.$new(); + if (CompletedJobsList.fields.type) { + CompletedJobsList.fields.type.searchOptions = $scope.type_choices; + } + if (RunningJobsList.fields.type) { + RunningJobsList.fields.type.searchOptions = $scope.type_choices; + } + if (QueuedJobsList.fields.type) { + QueuedJobsList.fields.type.searchOptions = $scope.type_choices; + } + completed_scope = $scope.$new(true); LoadJobsScope({ parent_scope: $scope, scope: completed_scope, @@ -42,7 +51,7 @@ function JobsListController ($scope, $compile, ClearScope, Breadcrumbs, LoadBrea id: 'completed-jobs', url: GetBasePath('unified_jobs') + '?or__status=successful&or__status=failed&or__status=error&or__status=canceled' }); - running_scope = $scope.$new(); + running_scope = $scope.$new(true); LoadJobsScope({ parent_scope: $scope, scope: running_scope, @@ -50,7 +59,7 @@ function JobsListController ($scope, $compile, ClearScope, Breadcrumbs, LoadBrea id: 'active-jobs', url: GetBasePath('unified_jobs') + '?status=running' }); - queued_scope = $scope.$new(); + queued_scope = $scope.$new(true); LoadJobsScope({ parent_scope: $scope, scope: queued_scope, @@ -58,7 +67,7 @@ function JobsListController ($scope, $compile, ClearScope, Breadcrumbs, LoadBrea id: 'queued-jobs', url: GetBasePath('unified_jobs') + '?or__status=pending&or__status=waiting&or__status=new' }); - scheduled_scope = $scope.$new(); + scheduled_scope = $scope.$new(true); LoadSchedulesScope({ parent_scope: $scope, scope: scheduled_scope, diff --git a/awx/ui/static/js/helpers/JobSubmission.js b/awx/ui/static/js/helpers/JobSubmission.js index 3a6cc4c1f9..0a9b88d4a7 100644 --- a/awx/ui/static/js/helpers/JobSubmission.js +++ b/awx/ui/static/js/helpers/JobSubmission.js @@ -140,6 +140,7 @@ function(Wait, GetBasePath, LookUpInit, JobTemplateForm, CredentialList) { } else { parent_scope.$emit(callback, acceptedPasswords); + scope.$destroy(); } }; @@ -147,6 +148,7 @@ function(Wait, GetBasePath, LookUpInit, JobTemplateForm, CredentialList) { $('#password-modal').modal('hide'); Alert('Missing Password', 'Required password(s) not provided. Your request will not be submitted.', 'alert-info'); parent_scope.$emit('PasswordsCanceled'); + scope.$destroy(); }; promptPassword(); @@ -223,7 +225,7 @@ function(Wait, GetBasePath, LookUpInit, JobTemplateForm, CredentialList) { if (scope.removeStartPlaybookRun) { scope.removeStartPlaybookRun(); } - scope.removeStartJob = scope.$on('StartPlaybookRun', function(e, passwords) { + scope.removeStartPlaybookRun = scope.$on('StartPlaybookRun', function(e, passwords) { LaunchJob({ scope: scope, url: launch_url, diff --git a/awx/ui/static/js/helpers/search.js b/awx/ui/static/js/helpers/search.js index ae66ef371a..586f623bef 100644 --- a/awx/ui/static/js/helpers/search.js +++ b/awx/ui/static/js/helpers/search.js @@ -57,8 +57,20 @@ angular.module('SearchHelper', ['RestServices', 'Utilities', 'RefreshHelper']) } } + // Default the search field to 'defaultSearchField', if one exists + for (fld in list.fields) { + if (list.fields[fld].searchWidget === undefined && widget === 1 || + list.fields[fld].searchWidget === widget) { + if (list.fields[fld].defaultSearchField) { + scope[iterator + 'SearchField' + modifier] = fld; + scope[iterator + 'SearchFieldLabel' + modifier] = list.fields[fld].label; + } + } + } + + // A field marked as key may not be 'searchable', and there might not be a 'defaultSearchField', + // so find the first searchable field. if (Empty(scope[iterator + 'SearchField' + modifier])) { - // A field marked as key may not be 'searchable'. Find the first searchable field. for (fld in list.fields) { if (list.fields[fld].searchWidget === undefined && widget === 1 || list.fields[fld].searchWidget === widget) { @@ -203,6 +215,7 @@ angular.module('SearchHelper', ['RestServices', 'Utilities', 'RefreshHelper']) list.fields[fld].searchType === 'select' || list.fields[fld].searchType === 'select_or')) { scope[iterator + 'SelectShow' + modifier] = true; scope[iterator + 'SearchSelectOpts' + modifier] = list.fields[fld].searchOptions; + scope[iterator + 'SearchType' + modifier] = ''; } else if (list.fields[fld].searchType && list.fields[fld].searchType === 'int') { //scope[iterator + 'HideSearchType' + modifier] = true; scope[iterator + 'SearchType' + modifier] = 'int'; @@ -366,10 +379,12 @@ angular.module('SearchHelper', ['RestServices', 'Utilities', 'RefreshHelper']) // handle fields whose source is a related model e.g. inventories.organization scope[iterator + 'SearchParams'] += '&' + list.fields[scope[iterator + 'SearchField' + modifier]].sourceModel + '__' + list.fields[scope[iterator + 'SearchField' + modifier]].sourceField + '__'; - } else if ((list.fields[scope[iterator + 'SearchField' + modifier]].searchType === 'select') && - (scope[iterator + 'SearchSelectValue' + modifier].value === '' || - scope[iterator + 'SearchSelectValue' + modifier].value === null)) { + } else if ( list.fields[scope[iterator + 'SearchField' + modifier]].searchType === 'select' && + Empty(scope[iterator + 'SearchSelectValue' + modifier].value) ) { scope[iterator + 'SearchParams'] += '&' + scope[iterator + 'SearchField' + modifier] + '__'; + } else if ( list.fields[scope[iterator + 'SearchField' + modifier]].searchType === 'select' && + !Empty(scope[iterator + 'SearchSelectValue' + modifier].value) ) { + scope[iterator + 'SearchParams'] += '&' + scope[iterator + 'SearchField' + modifier]; } else { scope[iterator + 'SearchParams'] += '&' + scope[iterator + 'SearchField' + modifier] + '__'; } @@ -381,10 +396,9 @@ angular.module('SearchHelper', ['RestServices', 'Utilities', 'RefreshHelper']) } else if (list.fields[scope[iterator + 'SearchField' + modifier]].searchType && list.fields[scope[iterator + 'SearchField' + modifier]].searchType === 'gtzero') { scope[iterator + 'SearchParams'] += 'gt=0'; - } else if ((list.fields[scope[iterator + 'SearchField' + modifier]].searchType === 'select') && - (scope[iterator + 'SearchSelectValue' + modifier].value === '' || - scope[iterator + 'SearchSelectValue' + modifier].value === null)) { - scope[iterator + 'SearchParams'] += 'iexact='; + } else if ( (list.fields[scope[iterator + 'SearchField' + modifier]].searchType === 'select') && + Empty(scope[iterator + 'SearchSelectValue' + modifier].value) ) { + scope[iterator + 'SearchParams'] += '=iexact='; } else { scope[iterator + 'SearchParams'] += scope[iterator + 'SearchType' + modifier] + '='; } diff --git a/awx/ui/static/js/lists/CompletedJobs.js b/awx/ui/static/js/lists/CompletedJobs.js index 38e6f77a75..cea774a111 100644 --- a/awx/ui/static/js/lists/CompletedJobs.js +++ b/awx/ui/static/js/lists/CompletedJobs.js @@ -60,12 +60,15 @@ angular.module('CompletedJobsDefinition', []) link: false, columnClass: "col-md-2 hidden-sm hidden-xs", columnShow: "showJobType", - searchable: false + searchable: true, + searchType: 'select', + searchOptions: [] // populated via GetChoices() in controller }, name: { label: 'Name', columnClass: 'col-md-3 col-xs-5', - ngClick: "viewJobLog(completed_job.id, completed_job.nameHref)" + ngClick: "viewJobLog(completed_job.id, completed_job.nameHref)", + defaultSearchField: true }, failed: { label: 'Job failed?', diff --git a/awx/ui/static/js/lists/QueuedJobs.js b/awx/ui/static/js/lists/QueuedJobs.js index ca7b6da22e..afe8619f03 100644 --- a/awx/ui/static/js/lists/QueuedJobs.js +++ b/awx/ui/static/js/lists/QueuedJobs.js @@ -51,12 +51,15 @@ angular.module('QueuedJobsDefinition', []) ngBind: 'queued_job.type_label', link: false, columnClass: "col-md-2 hidden-sm hidden-xs", - searchable: false + searchable: true, + searchType: 'select', + searchOptions: [] // populated via GetChoices() in controller }, name: { label: 'Name', columnClass: 'col-sm-3 col-xs-5', - ngClick: "viewJobLog(queued_job.id, queued_job.nameHref)" + ngClick: "viewJobLog(queued_job.id, queued_job.nameHref)", + defaultSearchField: true } }, diff --git a/awx/ui/static/js/lists/RunningJobs.js b/awx/ui/static/js/lists/RunningJobs.js index 6b0424b0fb..19a5fd67ec 100644 --- a/awx/ui/static/js/lists/RunningJobs.js +++ b/awx/ui/static/js/lists/RunningJobs.js @@ -51,12 +51,15 @@ angular.module('RunningJobsDefinition', []) ngBind: 'running_job.type_label', link: false, columnClass: "col-md-2 hidden-sm hidden-xs", - searchable: false + searchable: true, + searchType: 'select', + searchOptions: [] // populated via GetChoices() in controller }, name: { label: 'Name', columnClass: 'col-md-3 col-xs-5', - ngClick: "viewJobLog(running_job.id, running_job.nameHref)" + ngClick: "viewJobLog(running_job.id, running_job.nameHref)", + defaultSearchField: true } }, diff --git a/awx/ui/static/js/lists/ScheduledJobs.js b/awx/ui/static/js/lists/ScheduledJobs.js index ce21fe9444..64565b011a 100644 --- a/awx/ui/static/js/lists/ScheduledJobs.js +++ b/awx/ui/static/js/lists/ScheduledJobs.js @@ -56,7 +56,8 @@ angular.module('ScheduledJobsDefinition', []) sourceField: 'name', ngClick: "editSchedule(schedule.id)", awToolTip: "{{ schedule.nameTip }}", - dataPlacement: "top" + dataPlacement: "top", + defaultSearchField: true } }, diff --git a/awx/ui/static/lib/ansible/Utilities.js b/awx/ui/static/lib/ansible/Utilities.js index 5dc5fee4ca..324f40afed 100644 --- a/awx/ui/static/lib/ansible/Utilities.js +++ b/awx/ui/static/lib/ansible/Utilities.js @@ -77,14 +77,14 @@ angular.module('Utilities', ['RestServices', 'Utilities']) */ .factory('Alert', ['$rootScope', '$compile', function ($rootScope, $compile) { return function (hdr, msg, cls, action, secondAlert, disableButtons) { - var scope = $rootScope.$new(), e; + var scope = $rootScope.$new(), alertClass, e; if (secondAlert) { - $('#alert2-modal-msg').attr({ "class": "alert" }); - scope.alertHeader2 = hdr; - scope.alertBody2 = msg; - scope.alertClass2 = (cls) ? cls : 'alert-danger'; //default alert class is alert-danger e = angular.element(document.getElementById('alert-modal2')); $compile(e)(scope); + scope.alertHeader2 = hdr; + scope.alertBody2 = msg; + alertClass = (cls) ? cls : 'alert-danger'; //default alert class is alert-danger + $('#alert2-modal-msg').attr({ "class": "alert " + alertClass }); $('#alert-modal2').modal({ show: true, keyboard: true, @@ -103,12 +103,12 @@ angular.module('Utilities', ['RestServices', 'Utilities']) } }); } else { - $('#alert-modal-msg').attr({ "class": "alert" }); - scope.alertHeader = hdr; - scope.alertBody = msg; - scope.alertClass = (cls) ? cls : 'alert-danger'; //default alert class is alert-danger e = angular.element(document.getElementById('alert-modal')); $compile(e)(scope); + scope.alertHeader = hdr; + scope.alertBody = msg; + alertClass = (cls) ? cls : 'alert-danger'; //default alert class is alert-danger + $('#alert-modal-msg').attr({ "class": "alert " + alertClass }); $('#alert-modal').modal({ show: true, keyboard: true, diff --git a/awx/ui/static/lib/ansible/generator-helpers.js b/awx/ui/static/lib/ansible/generator-helpers.js index 792908706d..009e78a7e3 100644 --- a/awx/ui/static/lib/ansible/generator-helpers.js +++ b/awx/ui/static/lib/ansible/generator-helpers.js @@ -687,10 +687,31 @@ angular.module('GeneratorHelpers', []) form = params.template, size = params.size, includeSize = (params.includeSize === undefined) ? true : params.includeSize, - fld, i, html = '', modifier, - searchWidgets = (params.searchWidgets) ? params.searchWidgets : 1; + searchWidgets = (params.searchWidgets) ? params.searchWidgets : 1, + sortedKeys; + + function addSearchFields(idx) { + var html = ''; + sortedKeys = Object.keys(form.fields).sort(); + sortedKeys.forEach(function(fld) { + if ((form.fields[fld].searchable === undefined || form.fields[fld].searchable === true) && + (((form.fields[fld].searchWidget === undefined || form.fields[fld].searchWidget === 1) && idx === 1) || + (form.fields[fld].searchWidget === idx))) { + html += "