diff --git a/awx/ui/static/js/app.js b/awx/ui/static/js/app.js index c636181275..51937f7bca 100644 --- a/awx/ui/static/js/app.js +++ b/awx/ui/static/js/app.js @@ -21,9 +21,8 @@ angular.module('ansible', [ 'PromptDialog', 'ApiLoader', 'RelatedSearchHelper', - 'RelatedPaginateHelper', 'SearchHelper', - 'PaginateHelper', + 'PaginationHelpers', 'RefreshHelper', 'AdminListDefinition', 'AWDirectives', diff --git a/awx/ui/static/js/controllers/Credentials.js b/awx/ui/static/js/controllers/Credentials.js index 21404ec788..a8bbedc751 100644 --- a/awx/ui/static/js/controllers/Credentials.js +++ b/awx/ui/static/js/controllers/Credentials.js @@ -82,7 +82,7 @@ function CredentialsList ($scope, $rootScope, $location, $log, $routeParams, Res LoadBreadCrumbs(); - scope.showActivity = function() { Stream(); } + scope.showActivity = function() { Stream({ scope: scope }); } scope.addCredential = function() { $location.path($location.path() + '/add'); @@ -406,7 +406,7 @@ function CredentialsEdit ($scope, $rootScope, $compile, $location, $log, $routeP callback: 'choicesReadyCredential' }); - scope.showActivity = function() { Stream(); } + scope.showActivity = function() { Stream({ scope: scope }); } // Save changes to the parent scope.formSave = function() { generator.clearApiErrors(); FormSave({ scope: scope, mode: 'edit' }) }; diff --git a/awx/ui/static/js/controllers/Home.js b/awx/ui/static/js/controllers/Home.js index dd2117585a..90f37d786a 100644 --- a/awx/ui/static/js/controllers/Home.js +++ b/awx/ui/static/js/controllers/Home.js @@ -69,7 +69,7 @@ function Home ($scope, $compile, $routeParams, $rootScope, $location, Wait, Obje ObjectCount({ scope: $scope, target: 'container3', dashboard: data}); }); - $scope.showActivity = function() { Stream(); } + $scope.showActivity = function() { Stream({ scope: $scope }); } $scope.refresh = function() { Wait('start'); @@ -218,7 +218,7 @@ function HomeGroups ($location, $routeParams, HomeGroupList, GenerateList, Proce LoadBreadCrumbs(); - scope.showActivity = function() { Stream(); } + scope.showActivity = function() { Stream({ scope: scope }); } scope.editGroup = function(group_id, inventory_id) { GroupsEdit({ scope: scope, group_id: group_id, inventory_id: inventory_id, groups_reload: false }); @@ -328,7 +328,7 @@ function HomeHosts ($location, $routeParams, HomeHostList, GenerateList, Process LoadBreadCrumbs(); - scope.showActivity = function() { Stream(); } + scope.showActivity = function() { Stream({ scope: scope }); } scope.toggle_host_enabled = function(id, sources) { ToggleHostEnabled({ host_id: id, external_source: sources, scope: scope }); } diff --git a/awx/ui/static/js/controllers/Inventories.js b/awx/ui/static/js/controllers/Inventories.js index 5f4c36bf3c..6547d9565c 100644 --- a/awx/ui/static/js/controllers/Inventories.js +++ b/awx/ui/static/js/controllers/Inventories.js @@ -129,7 +129,7 @@ function InventoriesList ($scope, $rootScope, $location, $log, $routeParams, Res } }); - scope.showActivity = function() { Stream(); } + scope.showActivity = function() { Stream({ scope: scope }); } scope.addInventory = function() { $location.path($location.path() + '/add'); @@ -500,15 +500,10 @@ function InventoriesEdit ($scope, $location, $routeParams, $compile, GenerateLis $scope.toggleHostEnabled = function(host_id, external_source) { ToggleHostEnabled({ scope: $scope, host_id: host_id, external_source: external_source }); } - - $scope.showHostActivity = function() { - var url = GetBasePath('activity_stream') + '?host__inventory__id=' + $scope.inventory_id; - Stream({ inventory_name: $scope.inventory_name, url: url }); - } $scope.showGroupActivity = function() { var url = GetBasePath('activity_stream') + '?group__inventory__id=' + $scope.inventory_id; - Stream({ inventory_name: $scope.inventory_name, url: url }); + Stream({ scope: $scope, inventory_name: $scope.inventory_name, url: url }); } $scope.showJobSummary = function(job_id) { diff --git a/awx/ui/static/js/controllers/JobTemplates.js b/awx/ui/static/js/controllers/JobTemplates.js index dd9ba5bd91..fe10b9cdf6 100644 --- a/awx/ui/static/js/controllers/JobTemplates.js +++ b/awx/ui/static/js/controllers/JobTemplates.js @@ -48,7 +48,7 @@ function JobTemplatesList ($scope, $rootScope, $location, $log, $routeParams, Re LoadBreadCrumbs(); - scope.showActivity = function() { Stream(); } + scope.showActivity = function() { Stream({ scope: scope }); } scope.addJobTemplate = function() { $location.path($location.path() + '/add'); @@ -646,7 +646,7 @@ function JobTemplatesEdit ($scope, $rootScope, $compile, $location, $log, $route } }; - scope.showActivity = function() { Stream(); } + scope.showActivity = function() { Stream({ scope: scope }); } // Cancel scope.formReset = function() { diff --git a/awx/ui/static/js/controllers/Organizations.js b/awx/ui/static/js/controllers/Organizations.js index ae940fa77e..6e78ccd111 100644 --- a/awx/ui/static/js/controllers/Organizations.js +++ b/awx/ui/static/js/controllers/Organizations.js @@ -45,7 +45,7 @@ function OrganizationsList ($routeParams, $scope, $rootScope, $location, $log, R PaginateInit({ scope: scope, list: list, url: defaultUrl }); scope.search(list.iterator); - scope.showActivity = function() { Stream(); } + scope.showActivity = function() { Stream({ scope: scope }); } scope.addOrganization = function() { $location.path($location.path() + '/add'); @@ -220,7 +220,7 @@ function OrganizationsEdit ($scope, $rootScope, $compile, $location, $log, $rout }); }; - scope.showActivity = function() { Stream(); } + scope.showActivity = function() { Stream({ scope: scope }); } // Reset the form scope.formReset = function() { diff --git a/awx/ui/static/js/controllers/Projects.js b/awx/ui/static/js/controllers/Projects.js index e23623d3f7..b6f3a21f16 100644 --- a/awx/ui/static/js/controllers/Projects.js +++ b/awx/ui/static/js/controllers/Projects.js @@ -160,7 +160,7 @@ function ProjectsList ($scope, $rootScope, $location, $log, $routeParams, Rest, LoadBreadCrumbs(); - scope.showActivity = function() { Stream(); } + scope.showActivity = function() { Stream({ scope: scope }); } scope.addProject = function() { $location.path($location.path() + '/add'); @@ -642,7 +642,7 @@ function ProjectsEdit ($scope, $rootScope, $compile, $location, $log, $routePara }); }; - scope.showActivity = function() { Stream(); } + scope.showActivity = function() { Stream({ scope: scope }); } // Related set: Add button scope.add = function(set) { diff --git a/awx/ui/static/js/controllers/Teams.js b/awx/ui/static/js/controllers/Teams.js index c81d04121c..261a167f64 100644 --- a/awx/ui/static/js/controllers/Teams.js +++ b/awx/ui/static/js/controllers/Teams.js @@ -45,7 +45,7 @@ function TeamsList ($scope, $rootScope, $location, $log, $routeParams, Rest, Ale LoadBreadCrumbs(); - scope.showActivity = function() { Stream(); } + scope.showActivity = function() { Stream({ scope: scope }); } scope.addTeam = function() { $location.path($location.path() + '/add'); @@ -237,7 +237,7 @@ function TeamsEdit ($scope, $rootScope, $compile, $location, $log, $routeParams, { hdr: 'Error!', msg: 'Failed to retrieve team: ' + $routeParams.team_id + '. GET status: ' + status }); }); - scope.showActivity = function() { Stream(); } + scope.showActivity = function() { Stream({ scope: scope }); } // Save changes to the parent scope.formSave = function() { diff --git a/awx/ui/static/js/controllers/Users.js b/awx/ui/static/js/controllers/Users.js index 8f37b1c07f..50b157a2f9 100644 --- a/awx/ui/static/js/controllers/Users.js +++ b/awx/ui/static/js/controllers/Users.js @@ -45,7 +45,7 @@ function UsersList ($scope, $rootScope, $location, $log, $routeParams, Rest, Ale LoadBreadCrumbs(); - scope.showActivity = function() { Stream(); } + scope.showActivity = function() { Stream({ scope: scope }); } scope.addUser = function() { $location.path($location.path() + '/add'); @@ -300,7 +300,7 @@ function UsersEdit ($scope, $rootScope, $compile, $location, $log, $routeParams, }); }; - scope.showActivity = function() { Stream(); } + scope.showActivity = function() { Stream({ scope: scope }); } // Cancel scope.formReset = function() { diff --git a/awx/ui/static/js/helpers/Groups.js b/awx/ui/static/js/helpers/Groups.js index 2da171e82a..67322459ab 100644 --- a/awx/ui/static/js/helpers/Groups.js +++ b/awx/ui/static/js/helpers/Groups.js @@ -10,7 +10,7 @@ 'use strict'; angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'GroupListDefinition', - 'SearchHelper', 'PaginateHelper', 'ListGenerator', 'AuthService', 'GroupsHelper', + 'SearchHelper', 'PaginationHelpers', 'ListGenerator', 'AuthService', 'GroupsHelper', 'InventoryHelper', 'SelectionHelper', 'JobSubmissionHelper', 'RefreshHelper', 'PromptDialog', 'InventorySummaryHelpDefinition', 'CredentialsListDefinition', 'InventoryTree' diff --git a/awx/ui/static/js/helpers/Hosts.js b/awx/ui/static/js/helpers/Hosts.js index d99e9eba5c..74df35b788 100644 --- a/awx/ui/static/js/helpers/Hosts.js +++ b/awx/ui/static/js/helpers/Hosts.js @@ -8,9 +8,9 @@ */ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'HostListDefinition', - 'SearchHelper', 'PaginateHelper', 'ListGenerator', 'AuthService', 'HostsHelper', - 'InventoryHelper', 'RelatedSearchHelper','RelatedPaginateHelper', - 'InventoryFormDefinition', 'SelectionHelper', 'HostGroupsFormDefinition' + 'SearchHelper', 'PaginationHelpers', 'ListGenerator', 'AuthService', 'HostsHelper', + 'InventoryHelper', 'RelatedSearchHelper', 'InventoryFormDefinition', 'SelectionHelper', + 'HostGroupsFormDefinition' ]) diff --git a/awx/ui/static/js/helpers/Lookup.js b/awx/ui/static/js/helpers/Lookup.js index ac0dd7a203..5d3dfc4aab 100644 --- a/awx/ui/static/js/helpers/Lookup.js +++ b/awx/ui/static/js/helpers/Lookup.js @@ -14,7 +14,7 @@ * }) */ -angular.module('LookUpHelper', [ 'RestServices', 'Utilities', 'SearchHelper', 'PaginateHelper', 'ListGenerator', 'ApiLoader' ]) +angular.module('LookUpHelper', [ 'RestServices', 'Utilities', 'SearchHelper', 'PaginationHelpers', 'ListGenerator', 'ApiLoader' ]) .factory('LookUpInit', ['Alert', 'Rest', 'GenerateList', 'SearchInit', 'PaginateInit', 'GetBasePath', 'FormatDate', 'Empty', function(Alert, Rest, GenerateList, SearchInit, PaginateInit, GetBasePath, FormatDate, Empty) { return function(params) { diff --git a/awx/ui/static/js/helpers/PaginationHelpers.js b/awx/ui/static/js/helpers/PaginationHelpers.js new file mode 100644 index 0000000000..bcab66394c --- /dev/null +++ b/awx/ui/static/js/helpers/PaginationHelpers.js @@ -0,0 +1,169 @@ +/********************************************* + * Copyright (c) 2014 AnsibleWorks, Inc. + * + * PaginationHelpers.js + * + */ + +angular.module('PaginationHelpers', ['Utilities', 'RefreshHelper', 'RefreshRelatedHelper']) + + .factory('PageRangeSetup', ['Empty', function(Empty) { + return function(params) { + + var scope = params.scope; + var count = params.count; + var next = params.next; + var previous = params.previous; + var iterator = params.iterator; + + scope[iterator + '_page'] = 1; + scope[iterator + '_num_pages'] = Math.ceil((count / scope[iterator + '_page_size'])); + scope[iterator + '_num_pages'] = (scope[iterator + '_num_pages'] <= 0) ? 1 : scope[iterator + '_num_pages']; + scope[iterator + '_total_rows'] = count; + + // Which page are we on? + if ( Empty(next) && previous ) { + // no next page, but there is a previous page + scope[iterator + '_page'] = parseInt(previous.match(/page=\d+/)[0].replace(/page=/,'')) + 1; + } + else if ( next && Empty(previous) ) { + // next page available, but no previous page + scope[iterator + '_page'] = 1; + } + else if ( next && previous ) { + // we're in between next and previous + scope[iterator + '_page'] = parseInt(previous.match(/page=\d+/)[0].replace(/page=/,'')) + 1; + } + + // Calc the range of up to 10 pages to show + scope[iterator + '_page_range'] = new Array(); + var first = (scope[iterator + '_page'] > 5) ? scope[iterator + '_page'] - 5 : 1; + if (scope[iterator + '_page'] < 6) { + var last = (10 <= scope[iterator + '_num_pages']) ? 10 : scope[iterator + '_num_pages']; + } + else { + var last = (scope[iterator + '_page'] + 4 < scope[iterator + '_num_pages']) ? + scope[iterator + '_page'] + 4 : scope[iterator + '_num_pages']; + } + for (var i=first; i <= last; i++) { + scope[iterator + '_page_range'].push(i); + } + + } + }]) + + .factory('RelatedPaginateInit', [ 'RefreshRelated', '$cookieStore', + function(RefreshRelated, $cookieStore) { + return function(params) { + + var scope = params.scope; + var relatedSets = params.relatedSets; + var pageSize = (params.pageSize) ? params.pageSize : 10; + + for (var key in relatedSets){ + cookieSize = $cookieStore.get(relatedSets[key].iterator + '_page_size'); + scope[relatedSets[key].iterator + '_url'] = relatedSets[key].url; + if (cookieSize) { + // use the size found in session cookie, when available + scope[relatedSets[key].iterator + '_page_size'] = cookieSize; + } + else { + scope[relatedSets[key].iterator + '_page'] = 0; + scope[relatedSets[key].iterator + '_page_size'] = pageSize; + } + } + + scope.getPage = function(page, set, iterator) { + var new_url = scope[iterator + '_url'].replace(/.page\=\d+/,''); + var connect = (/\/$/.test(new_url)) ? '?' : '&'; + new_url += connect + 'page=' + page; + new_url += (scope[iterator + 'SearchParams']) ? '&' + scope[iterator + 'SearchParams'] + + '&page_size=' + scope[iterator + '_page_size' ] : 'page_size=' + scope[iterator + 'PageSize' ]; + Wait('start'); + RefreshRefresh({ scope: scope, set: set, iterator: iterator, url: new_url }); + } + + scope.pageIsActive = function(page, iterator) { + return (page == scope[iterator + '_page']) ? 'active' : ''; + } + + scope.changePageSize = function(set, iterator) { + // Called when a new page size is selected + + scope[iterator + '_page'] = 1; + var url = scope[iterator + '_url']; + + // Using the session cookie, keep track of user rows per page selection + $cookieStore.put(iterator + '_page_size', scope[iterator + '_page_size']); + + url = url.replace(/\/\?.*$/,'/'); + url += (scope[iterator + 'SearchParams']) ? '?' + scope[iterator + 'SearchParams'] + '&page_size=' + scope[iterator + '_page_size' ] : + '?page_size=' + scope[iterator + '_page_size' ]; + + RefreshRelated({ scope: scope, set: set, iterator: iterator, url: url }); + } + + } + }]) + + + .factory('PaginateInit', [ 'Refresh', '$cookieStore', 'Wait', + function(Refresh, $cookieStore, Wait) { + return function(params) { + + var scope = params.scope; + var list = params.list; + var iterator = (params.iterator) ? params.iterator : list.iterator; + var mode = (params.mode) ? params.mode : null; + var cookieSize = $cookieStore.get(iterator + '_page_size'); + + scope[iterator + '_page'] = (params.page) ? params.page : 1; + scope[iterator + '_url'] = params.url; + scope[iterator + '_mode'] = mode; + + // Set the default page size + if (cookieSize && mode != 'lookup') { + // use the size found in session cookie, when available + scope[iterator + '_page_size'] = cookieSize; + } + else { + if (params.pageSize) { + scope[iterator + '_page_size'] = params.pageSize; + } + else if (mode == 'lookup') { + scope[iterator + '_page_size'] = 5; + } + else { + scope[iterator + '_page_size'] = 20; + } + } + + scope.getPage = function(page, set, iterator) { + var new_url = scope[iterator + '_url'].replace(/.page\=\d+/,''); + var connect = (/\/$/.test(new_url)) ? '?' : '&'; + new_url += connect + 'page=' + page; + new_url += (scope[iterator + 'SearchParams']) ? '&' + scope[iterator + 'SearchParams'] + + '&page_size=' + scope[iterator + '_page_size' ] : 'page_size=' + scope[iterator + 'PageSize' ]; + Wait('start'); + Refresh({ scope: scope, set: set, iterator: iterator, url: new_url }); + } + + scope.pageIsActive = function(page, iterator) { + return (page == scope[iterator + '_page']) ? 'active' : ''; + } + + scope.changePageSize = function(set, iterator) { + // Called whenever a new page size is selected + // Using the session cookie, keep track of user rows per page selection + $cookieStore.put(iterator + '_page_size', scope[iterator + '_page_size']); + scope[iterator + '_page'] = 0; + var new_url = scope[iterator + '_url'].replace(/\?page_size\=\d+/,''); + var connect = (/\/$/.test(new_url)) ? '?' : '&'; + new_url += (scope[iterator + 'SearchParams']) ? connect + scope[iterator + 'SearchParams'] + '&page_size=' + scope[iterator + '_page_size' ] : + connect + 'page_size=' + scope[iterator + '_page_size' ]; + Wait('start'); + Refresh({ scope: scope, set: set, iterator: iterator, url: new_url }); + } + + } + }]); \ No newline at end of file diff --git a/awx/ui/static/js/helpers/inventory.js b/awx/ui/static/js/helpers/inventory.js index a335d6432a..30258b9c05 100644 --- a/awx/ui/static/js/helpers/inventory.js +++ b/awx/ui/static/js/helpers/inventory.js @@ -8,10 +8,8 @@ * */ -angular.module('InventoryHelper', [ 'RestServices', 'Utilities', 'OrganizationListDefinition', - 'SearchHelper', 'PaginateHelper', 'ListGenerator', 'AuthService', - 'InventoryHelper', 'RelatedSearchHelper', 'RelatedPaginateHelper', - 'InventoryFormDefinition', 'ParseHelper' +angular.module('InventoryHelper', [ 'RestServices', 'Utilities', 'OrganizationListDefinition', 'ListGenerator', 'AuthService', + 'InventoryHelper', 'InventoryFormDefinition', 'ParseHelper' ]) .factory('SaveInventory', ['InventoryForm', 'Rest', 'Alert', 'ProcessErrors', 'LookUpInit', 'OrganizationList', diff --git a/awx/ui/static/js/helpers/paginate.js b/awx/ui/static/js/helpers/paginate.js deleted file mode 100644 index 7ef1098b89..0000000000 --- a/awx/ui/static/js/helpers/paginate.js +++ /dev/null @@ -1,82 +0,0 @@ -/********************************************* - * Copyright (c) 2014 AnsibleWorks, Inc. - * - * PaginateHelper - * - * All the parts for controlling the search widget on - * related collections. - * - * PaginateInit({ - * scope: , - * list:
- * url: < - * }); - * - */ - -angular.module('PaginateHelper', ['RefreshHelper', 'ngCookies', 'Utilities']) - .factory('PaginateInit', [ 'Refresh', '$cookieStore', 'Wait', function(Refresh, $cookieStore, Wait) { - return function(params) { - - var scope = params.scope; - var list = params.list; - var iterator = (params.iterator) ? params.iterator : list.iterator; - var url = params.url; - var mode = (params.mode) ? params.mode : null; - var cookieSize = $cookieStore.get(iterator + 'PageSize'); - - if (params.page) { - scope[iterator + 'Page'] = params.page; - } - else { - scope[iterator + 'Page'] = 0; - } - - if (cookieSize && mode != 'lookup') { - // use the size found in session cookie, when available - scope[iterator + 'PageSize'] = cookieSize; - } - else { - if (params.pageSize) { - scope[iterator + 'PageSize'] = params.pageSize; - } - else if (mode == 'lookup') { - scope[iterator + 'PageSize'] = 5; - } - else { - scope[iterator + 'PageSize'] = 20; - } - } - - scope.nextSet = function(set, iterator) { - if (scope[iterator + 'NextUrl']) { - scope[iterator + 'Page']++; - Wait('start'); - Refresh({ scope: scope, set: set, iterator: iterator, url: scope[iterator + 'NextUrl'] }); - } - }; - - scope.prevSet = function(set, iterator) { - if (scope[iterator + 'PrevUrl']) { - scope[iterator + 'Page']--; - Wait('start'); - Refresh({ scope: scope, set: set, iterator: iterator, url: scope[iterator + 'PrevUrl'] }); - } - }; - - scope.changePageSize = function(set, iterator) { - // Called whenever a new page size is selected - - // Using the session cookie, keep track of user rows per page selection - $cookieStore.put(iterator + 'PageSize', scope[iterator + 'PageSize']); - - scope[iterator + 'Page'] = 0; - var new_url = url.replace(/\?page_size\=\d+/,''); - var connect = (/\/$/.test(new_url)) ? '?' : '&'; - new_url += (scope[iterator + 'SearchParams']) ? connect + scope[iterator + 'SearchParams'] + '&page_size=' + scope[iterator + 'PageSize' ] : - connect + 'page_size=' + scope[iterator + 'PageSize' ]; - Wait('start'); - Refresh({ scope: scope, set: set, iterator: iterator, url: new_url }); - } - } - }]); \ No newline at end of file diff --git a/awx/ui/static/js/helpers/refresh-related.js b/awx/ui/static/js/helpers/refresh-related.js index 47c5932325..7a53bd9d08 100644 --- a/awx/ui/static/js/helpers/refresh-related.js +++ b/awx/ui/static/js/helpers/refresh-related.js @@ -14,8 +14,9 @@ * */ -angular.module('RefreshRelatedHelper', ['RestServices', 'Utilities']) - .factory('RefreshRelated', ['ProcessErrors', 'Rest', 'Wait', function(ProcessErrors, Rest, Wait) { +angular.module('RefreshRelatedHelper', ['RestServices', 'Utilities', 'PaginationHelpers']) + .factory('RefreshRelated', ['ProcessErrors', 'Rest', 'Wait', 'PageRangeSetup', + function(ProcessErrors, Rest, Wait, PageRangeSetup) { return function(params) { var scope = params.scope; @@ -26,22 +27,15 @@ angular.module('RefreshRelatedHelper', ['RestServices', 'Utilities']) Rest.setUrl(url); Rest.get() .success( function(data, status, headers, config) { - Wait('stop'); + PageRangeSetup({ scope: scope, count: data.count, next: data.next, previous: data.previous, iterator: iterator }); scope[set] = data['results']; - scope[iterator + 'NextUrl'] = data.next; - scope[iterator + 'PrevUrl'] = data.previous; - scope[iterator + 'Count'] = data.count; - scope[iterator + 'PageCount'] = Math.ceil((data.count / scope[iterator + 'PageSize'])); - //scope[iterator + 'SearchSpin'] = false; scope[iterator + 'Loading'] = false; scope[iterator + 'HoldInput'] = false; + Wait('stop'); scope.$emit('related' + set); - if (!params.scope.$$phase) { - params.scope.$digest(); - } + }) .error ( function(data, status, headers, config) { - //scope[iterator + 'SearchSpin'] = true; ProcessErrors(scope, data, status, null, { hdr: 'Error!', msg: 'Failed to retrieve ' + set + '. GET returned status: ' + status }); }); diff --git a/awx/ui/static/js/helpers/refresh.js b/awx/ui/static/js/helpers/refresh.js index 19170d70e7..1efeb9fe07 100644 --- a/awx/ui/static/js/helpers/refresh.js +++ b/awx/ui/static/js/helpers/refresh.js @@ -14,34 +14,32 @@ * */ -angular.module('RefreshHelper', ['RestServices', 'Utilities']) - .factory('Refresh', ['ProcessErrors', 'Rest', 'Wait', function(ProcessErrors, Rest, Wait) { +angular.module('RefreshHelper', ['RestServices', 'Utilities', 'PaginationHelpers']) + .factory('Refresh', ['ProcessErrors', 'Rest', 'Wait', 'Empty', 'PageRangeSetup', + function(ProcessErrors, Rest, Wait, Empty, PageRangeSetup) { return function(params) { var scope = params.scope; var set = params.set; var iterator = params.iterator; var url = params.url; + scope.current_url = url; Rest.setUrl(url); Rest.get() .success( function(data, status, headers, config) { - Wait('stop'); - scope[iterator + 'NextUrl'] = data.next; - scope[iterator + 'PrevUrl'] = data.previous; - scope[iterator + 'Count'] = data.count; - scope[iterator + 'PageCount'] = Math.ceil((data.count / scope[iterator + 'PageSize'])); - //scope[iterator + 'SearchSpin'] = false; + PageRangeSetup({ scope: scope, count: data.count, next: data.next, previous: data.previous, iterator: iterator }); scope[iterator + 'Loading'] = false; for (var i=1; i <= 3; i++) { var modifier = (i == 1) ? '' : i; scope[iterator + 'HoldInput' + modifier] = false; } scope[set] = data['results']; + window.scrollTo(0,0); + Wait('stop'); scope.$emit('PostRefresh'); }) .error ( function(data, status, headers, config) { - //scope[iterator + 'SearchSpin'] = false; scope[iterator + 'HoldInput'] = false; ProcessErrors(scope, data, status, null, { hdr: 'Error!', msg: 'Failed to retrieve ' + set + '. GET returned status: ' + status }); diff --git a/awx/ui/static/js/helpers/related-paginate.js b/awx/ui/static/js/helpers/related-paginate.js deleted file mode 100644 index fa868962b6..0000000000 --- a/awx/ui/static/js/helpers/related-paginate.js +++ /dev/null @@ -1,67 +0,0 @@ -/********************************************* - * Copyright (c) 2014 AnsibleWorks, Inc. - * - * RelatedPaginateHelper - * - * All the parts for controlling the search widget on - * related collections. - * - * RelatedPaginateInit({ - * scope: , - * relatedSets: , - * form: - * }); - * - */ - -angular.module('RelatedPaginateHelper', ['RefreshRelatedHelper', 'ngCookies']) - .factory('RelatedPaginateInit', [ 'RefreshRelated', '$cookieStore', function(RefreshRelated, $cookieStore) { - return function(params) { - - var scope = params.scope; - var relatedSets = params.relatedSets; - var pageSize = (params.pageSize) ? params.pageSize : 10; - - for (var key in relatedSets){ - cookieSize = $cookieStore.get(relatedSets[key].iterator + 'PageSize'); - if (cookieSize) { - // use the size found in session cookie, when available - scope[relatedSets[key].iterator + 'PageSize'] = cookieSize; - } - else { - scope[relatedSets[key].iterator + 'Page'] = 0; - scope[relatedSets[key].iterator + 'PageSize'] = pageSize; - } - } - - scope.nextSet = function(set, iterator) { - scope[iterator + 'Page']++; - RefreshRelated({ scope: scope, set: set, iterator: iterator, url: scope[iterator + 'NextUrl'] }); - }; - - scope.prevSet = function(set, iterator) { - scope[iterator + 'Page']--; - RefreshRelated({ scope: scope, set: set, iterator: iterator, url: scope[iterator + 'PrevUrl'] }); - }; - - scope.changePageSize = function(set, iterator) { - // Called when a new page size is selected - var url; - scope[iterator + 'Page'] = 0; - for (var key in relatedSets) { - if (key == set) { - url = relatedSets[key].url; - break; - } - } - - // Using the session cookie, keep track of user rows per page selection - $cookieStore.put(iterator + 'PageSize', scope[iterator + 'PageSize']); - - url = url.replace(/\/\?.*$/,'/'); - url += (scope[iterator + 'SearchParams']) ? '?' + scope[iterator + 'SearchParams'] + '&page_size=' + scope[iterator + 'PageSize' ] : - '?page_size=' + scope[iterator + 'PageSize' ]; - RefreshRelated({ scope: scope, set: set, iterator: iterator, url: url }); - } - } - }]); \ No newline at end of file diff --git a/awx/ui/static/js/helpers/search.js b/awx/ui/static/js/helpers/search.js index 56f6ff4881..422636cf2f 100644 --- a/awx/ui/static/js/helpers/search.js +++ b/awx/ui/static/js/helpers/search.js @@ -16,8 +16,9 @@ */ angular.module('SearchHelper', ['RestServices', 'Utilities', 'RefreshHelper']) - .factory('SearchInit', ['Alert', 'Rest', 'Refresh', '$location', 'GetBasePath', 'Empty', '$timeout', 'Wait', - function(Alert, Rest, Refresh, $location, GetBasePath, Empty, $timeout, Wait) { + + .factory('SearchInit', ['Alert', 'Rest', 'Refresh', '$location', 'GetBasePath', 'Empty', '$timeout', 'Wait', 'Store', + function(Alert, Rest, Refresh, $location, GetBasePath, Empty, $timeout, Wait, Store) { return function(params) { var scope = params.scope; @@ -25,14 +26,19 @@ angular.module('SearchHelper', ['RestServices', 'Utilities', 'RefreshHelper']) var defaultUrl = params.url; var list = params.list; var iterator = (params.iterator) ? params.iterator : list.iterator; - var sort_order; - var expected_objects=0; - var found_objects=0; - - if (scope.searchTimer) { - $timeout.cancel(scope.searchTimer); - } + var setWidgets = (params.setWidgets == false) ? false : true; + var sort_order, expected_objects=0, found_objects=0; + + var params = { + set: set, + defaultUrl: defaultUrl, + list: list, + iterator: iterator + }; + + Store('CurrentSearchParams', params); // Save in case Activity Stream widget needs to restore + function setDefaults(widget) { // Set default values var modifier = (widget == undefined || widget == 1) ? '' : widget; @@ -119,15 +125,17 @@ angular.module('SearchHelper', ['RestServices', 'Utilities', 'RefreshHelper']) } } - // Set default values for each search widget on the page - var widgets = (list.searchWidgets) ? list.searchWidgets : 1; - for (var i=1; i <= widgets; i++) { - var modifier = (i == 1) ? '' : i; - if ( $('#search-widget-container' + modifier) ) { - setDefaults(i); + if (setWidgets) { + // Set default values for each search widget on the page + var widgets = (list.searchWidgets) ? list.searchWidgets : 1; + for (var i=1; i <= widgets; i++) { + var modifier = (i == 1) ? '' : i; + if ( $('#search-widget-container' + modifier) ) { + setDefaults(i); + } } } - + // Functions to handle search widget changes scope.setSearchField = function(iterator, fld, label, widget) { @@ -223,14 +231,13 @@ angular.module('SearchHelper', ['RestServices', 'Utilities', 'RefreshHelper']) if (scope.removeDoSearch) { scope.removeDoSearch(); } - scope.removeDoSearch = scope.$on('doSearch', function(e, iterator, page, load, spin) { + scope.removeDoSearch = scope.$on('doSearch', function(e, iterator, page, load) { // // Execute the search // - //scope[iterator + 'SearchSpin'] = (spin == undefined || spin == true) ? true : false; scope[iterator + 'Loading'] = (load == undefined || load == true) ? true : false; var url = defaultUrl; - + //finalize and execute the query scope[iterator + 'Page'] = (page) ? parseInt(page) - 1 : 0; if (scope[iterator + 'SearchParams']) { @@ -242,7 +249,7 @@ angular.module('SearchHelper', ['RestServices', 'Utilities', 'RefreshHelper']) } } url = url.replace(/\&\&/,'&'); - url += (scope[iterator + 'PageSize']) ? '&page_size=' + scope[iterator + 'PageSize'] : ""; + url += (scope[iterator + '_page_size']) ? '&page_size=' + scope[iterator + '_page_size'] : ""; if (page) { url += '&page=' + page; } @@ -252,38 +259,7 @@ angular.module('SearchHelper', ['RestServices', 'Utilities', 'RefreshHelper']) Refresh({ scope: scope, set: set, iterator: iterator, url: url }); }); - /* - if (scope.removeFoundObject) { - scope.removeFoundObject(); - } - scope.removeFoundObject = scope.$on('foundObject', function(e, iterator, page, load, spin, widget, pk) { - found_objects++; - // Add new criteria to search params - var modifier = (widget == 1) ? '' : widget; - var objs = list.fields[scope[iterator + 'SearchField' + modifier]].searchObject; - var o = (objs == 'inventories') ? 'inventory' : objs.replace(/s$/,''); - var searchFld = list.fields[scope[iterator + 'SearchField' + modifier]].searchField; - scope[iterator + 'SearchParams'] += '&' + searchFld + '__icontains=' + o; - if (!Empty(pk)) { - scope[iterator + 'SearchParams'] += '&' + searchFld + '_id__in=' + pk; - } - // Move to the next phase once all object types are processed - if (found_objects == expected_objects) { - scope.$emit('prepareSearch2', iterator, page, load, spin); - } - }); - */ - - /*if (scope.removeResultWarning) { - scope.removeResultWarning(); - } - scope.removeResultWarning = scope.$on('resultWarning', function(e, objs, length) { - // Alert the user that the # of objects was greater than 30 - var label = (objs == 'inventory') ? 'inventories' : objs.replace(/s$/,''); - Alert('Warning', 'The number of matching ' + label + ' was too large. We limited your search to the first 30.', 'alert-info'); - }); - */ - + if (scope.removePrepareSearch) { scope.removePrepareSearch(); } @@ -297,22 +273,6 @@ angular.module('SearchHelper', ['RestServices', 'Utilities', 'RefreshHelper']) var widgets = (list.searchWidgets) ? list.searchWidgets : 1; var modifier; - // Determine how many object values we're dealing with. - /* - expected_objects = 0; - found_objects = 0; - for (var i=1; i <= widgets; i++) { - modifier = (i == 1) ? '' : i; - scope[iterator + 'HoldInput' + modifier] = true; //Block any input until we're done. Refresh.js will flip this back. - if ($('#search-widget-container' + modifier) && - list.fields[scope[iterator + 'SearchField' + modifier]] && - list.fields[scope[iterator + 'SearchField' + modifier]].searchObject && - list.fields[scope[iterator + 'SearchField' + modifier]].searchObject !== 'all') { - expected_objects++; - } - } - */ - for (var i=1; i <= widgets; i++) { var modifier = (i == 1) ? '' : i; if ( $('#search-widget-container' + modifier) ) { @@ -329,33 +289,6 @@ angular.module('SearchHelper', ['RestServices', 'Utilities', 'RefreshHelper']) list.fields[scope[iterator + 'SearchField' + modifier]].searchObject + '__name__icontains=' + scope[iterator + 'SearchValue' + modifier]; - - //var objUrl = GetBasePath('base') + objs + '/?name__icontains=' + scope[iterator + 'SearchValue' + modifier]; - /* - Rest.setUrl(objUrl); - Rest.setHeader({ widget: i }); - Rest.setHeader({ object: objs }); - Rest.get() - .success( function(data, status, headers, config) { - var pk=''; - //limit result set to 30 - var len = (data.results.length > 30) ? 30 : data.results.length; - for (var j=0; j < len; j++) { - pk += "," + data.results[j].id; - } - pk = pk.replace(/^\,/,''); - scope.$emit('foundObject', iterator, page, load, spin, config.headers['widget'], pk); - if (data.results.length > 30) { - scope.$emit('resultWarning', config.headers['object'], data.results.length); - } - }) - .error( function(data, status, headers, config) { - Wait('stop'); - ProcessErrors(scope, data, status, null, - { hdr: 'Error!', msg: 'Retrieving list of ' + objs + ' where name contains: ' + scope[iterator + 'SearchValue' + modifier] + - ' GET returned status: ' + status }); - }); - */ } else { // Search value is empty @@ -363,7 +296,6 @@ angular.module('SearchHelper', ['RestServices', 'Utilities', 'RefreshHelper']) scope[iterator + 'SearchParams'] += '&' + list.fields[scope[iterator + 'SearchField' + modifier]].searchField + '=' + list.fields[scope[iterator + 'SearchField' + modifier]].searchObject; - //scope.$emit('foundObject', iterator, page, load, spin, i, null); } } else { @@ -374,10 +306,6 @@ angular.module('SearchHelper', ['RestServices', 'Utilities', 'RefreshHelper']) } } scope.$emit('prepareSearch2', iterator, page, load, spin); - //if (expected_objects == 0) { - // No search widgets contain objects - // scope.$emit('prepareSearch2', iterator, page, load, spin); - //} }); if (scope.removePrepareSearch2) { @@ -437,17 +365,6 @@ angular.module('SearchHelper', ['RestServices', 'Utilities', 'RefreshHelper']) scope[iterator + 'SearchSelectValue' + modifier].value == null) ) { scope[iterator + 'SearchParams'] += 'iexact='; } - /*else if ( (list.fields[scope[iterator + 'SearchField' + modifier]].searchType && - (list.fields[scope[iterator + 'SearchField' + modifier]].searchType == 'or')) ) { - scope[iterator + 'SearchParams'] = ''; //start over - var val = scope[iterator + 'SearchValue' + modifier]; - for (var k=0; k < list.fields[scope[iterator + 'SearchField' + modifier]].searchFields.length; k++) { - scope[iterator + 'SearchParams'] += '&or__' + - list.fields[scope[iterator + 'SearchField' + modifier]].searchFields[k] + - '__icontains=' + escape(val); - } - scope[iterator + 'SearchParams'].replace(/^\&/,''); - }*/ else { scope[iterator + 'SearchParams'] += scope[iterator + 'SearchType' + modifier] + '='; } @@ -491,12 +408,16 @@ angular.module('SearchHelper', ['RestServices', 'Utilities', 'RefreshHelper']) } } - scope.search = function(iterator, page, load, spin) { + scope.search = function(iterator, page, load) { // Called to initiate a searh. // Page is optional. Added to accomodate back function on Job Events detail. // Spin optional -set to false if spin not desired. // Load optional -set to false if loading message not desired - scope.$emit('prepareSearch', iterator, page, load, spin); + var load = (load === undefined) ? true : false; + if (load) { + scope[set] = []; + } + scope.$emit('prepareSearch', iterator, page, load); } diff --git a/awx/ui/static/js/helpers/teams.js b/awx/ui/static/js/helpers/teams.js index 00a73fe20b..8bd6e626b0 100644 --- a/awx/ui/static/js/helpers/teams.js +++ b/awx/ui/static/js/helpers/teams.js @@ -6,7 +6,7 @@ */ angular.module('TeamHelper', [ 'RestServices', 'Utilities', 'OrganizationListDefinition', - 'SearchHelper', 'PaginateHelper', 'ListGenerator' ]) + 'SearchHelper', 'PaginationHelpers', 'ListGenerator' ]) .factory('SetTeamListeners', ['Alert', 'Rest', function(Alert, Rest) { return function(params) { diff --git a/awx/ui/static/js/lists/InventoryGroups.js b/awx/ui/static/js/lists/InventoryGroups.js index 5aa136a611..f341e42b69 100644 --- a/awx/ui/static/js/lists/InventoryGroups.js +++ b/awx/ui/static/js/lists/InventoryGroups.js @@ -28,7 +28,8 @@ angular.module('InventoryGroupsDefinition', []) ngClick: "\{\{ 'showHosts(' + group.id + ',' + group.group_id + ', false)' \}\}", ngClass: "group.selected_class", hasChildren: true, - columnClass: 'col-lg-9 ellipsis', + columnClass: 'col-lg-9 col-md-9 col-sm-7 col-xs-7', + 'class': 'ellipsis', nosort: true, awDroppable: "\{\{ group.isDroppable \}\}", awDraggable: "\{\{ group.isDraggable \}\}", @@ -41,7 +42,7 @@ angular.module('InventoryGroupsDefinition', []) actions: { - columnClass: 'col-lg-3', + columnClass: 'col-lg-3 col-md-3 col-sm-5 col-xs-5', create: { mode: 'all', diff --git a/awx/ui/static/js/lists/InventoryHosts.js b/awx/ui/static/js/lists/InventoryHosts.js index 1a9760c02f..d59854352b 100644 --- a/awx/ui/static/js/lists/InventoryHosts.js +++ b/awx/ui/static/js/lists/InventoryHosts.js @@ -97,12 +97,6 @@ angular.module('InventoryHostsDefinition', []) ngHide: 'selected_tree_id == 1', //disable when 'All Hosts' selected awToolTip: "Create a new host" }, - stream: { - mode: 'all', - ngClick: "showHostActivity()", - awToolTip: "View Activity Stream", - ngShow: "user_is_superuser" - }, help: { mode: 'all', awToolTip: diff --git a/awx/ui/static/js/widgets/Stream.js b/awx/ui/static/js/widgets/Stream.js index dc2d47ccc2..5fa61f73f2 100644 --- a/awx/ui/static/js/widgets/Stream.js +++ b/awx/ui/static/js/widgets/Stream.js @@ -7,15 +7,16 @@ * */ -angular.module('StreamWidget', ['RestServices', 'Utilities', 'StreamListDefinition', 'SearchHelper', 'PaginateHelper', +angular.module('StreamWidget', ['RestServices', 'Utilities', 'StreamListDefinition', 'SearchHelper', 'PaginationHelpers', 'RefreshHelper', 'ListGenerator', 'StreamWidget', 'AuthService']) .factory('setStreamHeight', [ function() { return function() { // Try not to overlap footer. Because stream is positioned absolute, the parent // doesn't resize correctly when stream is loaded. - var stream = $('#stream-container'); - var height = stream.height() + 50; + var sheight = $('#stream-content').height(); + var theight = parseInt($('#tab-content-container').css('min-height').replace(/px/,'')); + var height = (theight < sheight) ? sheight : theight; $('#tab-content-container').css({ "min-height": height }); } }]) @@ -253,16 +254,20 @@ angular.module('StreamWidget', ['RestServices', 'Utilities', 'StreamListDefiniti .factory('Stream', ['$rootScope', '$location', 'Rest', 'GetBasePath', 'ProcessErrors', 'Wait', 'StreamList', 'SearchInit', 'PaginateInit', 'GenerateList', 'FormatDate', 'ShowStream', 'HideStream', 'BuildDescription', 'FixUrl', 'BuildUrl', - 'ShowDetail', 'StreamBreadCrumbs', 'setStreamHeight', 'Find', + 'ShowDetail', 'StreamBreadCrumbs', 'setStreamHeight', 'Find', 'Store', function($rootScope, $location, Rest, GetBasePath, ProcessErrors, Wait, StreamList, SearchInit, PaginateInit, GenerateList, FormatDate, ShowStream, HideStream, BuildDescription, FixUrl, BuildUrl, ShowDetail, StreamBreadCrumbs, setStreamHeight, - Find) { + Find, Store) { return function(params) { var list = StreamList; var defaultUrl = GetBasePath('activity_stream'); var view = GenerateList; var base = $location.path().replace(/^\//,'').split('/')[0]; + var parent_scope = params.scope; + + // Hang onto current search params + var PreviousSearchParams = Store('SearchInitParams'); // pass in an inventory name to fix breadcrumb display var inventory_name = (params) ? params.inventory_name : null; @@ -324,11 +329,23 @@ angular.module('StreamWidget', ['RestServices', 'Utilities', 'StreamListDefiniti scope.closeStream = function(inUrl) { HideStream(); - if (scope.searchCleanup) + if (scope.searchCleanup) { scope.searchCleanup(); - if (inUrl) + } + // Restore prior search state + if (PreviousSearchParams) { + SearchInit({ + scope: parent_scope, + set: PreviousSearchParams.set, + list: PreviousSearchParams.list, + url: PreviousSearchParams.defaultUrl, + iterator: PreviousSearchParams.iterator, + setWidgets: false }); + } + if (inUrl) { $location.path(inUrl); } + } scope.refreshStream = function() { scope.search(list.iterator); @@ -347,14 +364,6 @@ angular.module('StreamWidget', ['RestServices', 'Utilities', 'StreamListDefiniti cDate = new Date(scope['activities'][i].timestamp); scope['activities'][i].timestamp = FormatDate(cDate); - // Display username - /*scope['activities'][i].user = (scope['activities'][i].summary_fields.user) ? scope['activities'][i].summary_fields.user.username : - 'system'; - if (scope['activities'][i].user !== 'system') { - // turn user into a link when not 'system' - scope['activities'][i].user = "" + - scope['activities'][i].user + ""; - }*/ if (scope['activities'][i]['summary_fields']['actor']) { scope['activities'][i]['user'] = "" + scope['activities'][i]['summary_fields']['actor']['username'] + ""; @@ -399,7 +408,7 @@ angular.module('StreamWidget', ['RestServices', 'Utilities', 'StreamListDefiniti // Give ng-repeate a chance to show the data before adjusting the page size. setTimeout(function() { setStreamHeight(); }, 500); }); - + // Initialize search and paginate pieces and load data SearchInit({ scope: scope, set: list.name, list: list, url: defaultUrl }); PaginateInit({ scope: scope, list: list, url: defaultUrl }); diff --git a/awx/ui/static/less/ansible-ui.less b/awx/ui/static/less/ansible-ui.less index 1e02a23c0f..0666752e3d 100644 --- a/awx/ui/static/less/ansible-ui.less +++ b/awx/ui/static/less/ansible-ui.less @@ -116,13 +116,16 @@ a:focus { .actions { a { font-size: 14px; - margin-right: 15px; + margin-right: 12px; + } + a:last-child { + margin-right: 0; } a:hover { cursor: pointer; } .cancel { - padding-right: 10px; + padding-right: 8px; } .dropdown .caret { border-top-color: @blue; @@ -130,6 +133,7 @@ a:focus { } #home_groups_table .actions .cancel { padding-right: 3px; } +#jobs_table .actions .cancel { padding-right: 10px; } .success-badge { color: #ffffff; @@ -534,6 +538,39 @@ legend { font-size: 11px; } +/* Pagination */ + .page-label { + margin-top: 0; + text-align: right; + } + + .pagination { + margin-top: 0; + margin-bottom: 7px; + } + + .pagination > li > a { + padding: 3px 6px; + } + + .modal-body { + .pagination { + margin-top: 15px; + margin-bottom: 0; + } + .pagination > li > a { + border: none; + padding-top: 0; + padding-bottom: 0; + } + .pagination > .active > a { + background-color: #fff; + color: #428bca; + border-color: none; + border: 1px solid #428bca; + } + } + .footer-navigation { margin: 10px 0 10px 0; } diff --git a/awx/ui/static/lib/ansible/InventoryTree.js b/awx/ui/static/lib/ansible/InventoryTree.js index da621d0a7d..7b31832698 100644 --- a/awx/ui/static/lib/ansible/InventoryTree.js +++ b/awx/ui/static/lib/ansible/InventoryTree.js @@ -136,7 +136,6 @@ angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper', 'P .success( function(data, status, headers, config) { buildAllHosts(data); buildGroups(data, 0, 0); - //console.log(groups); if (refresh) { scope.groups = groups; scope.$emit('GroupTreeRefreshed', inventory_name, groups, emit); diff --git a/awx/ui/static/lib/ansible/Utilities.js b/awx/ui/static/lib/ansible/Utilities.js index 64b8d9e5dd..dfed22aea3 100644 --- a/awx/ui/static/lib/ansible/Utilities.js +++ b/awx/ui/static/lib/ansible/Utilities.js @@ -140,7 +140,8 @@ angular.module('Utilities',['RestServices', 'Utilities']) if (form.fields[field].realName) { if (data[form.fields[field].realName]) { scope[field + '_api_error'] = data[form.fields[field]][0]; - scope[form.name + '_form'][form.fields[field].realName].$setValidity('apiError', false); + //scope[form.name + '_form'][form.fields[field].realName].$setValidity('apiError', false); + $('[name="' + form.fields[field].realName + '"]').addClass('ng-invalid'); fieldErrors = true; } } @@ -148,15 +149,16 @@ angular.module('Utilities',['RestServices', 'Utilities']) if (data[field]) { scope[form.fields[field].sourceModel + '_' + form.fields[field].sourceField + '_api_error'] = data[field][0]; - scope[form.name + '_form'][form.fields[field].sourceModel + '_' + form.fields[field].sourceField].$setValidity('apiError', false); + //scope[form.name + '_form'][form.fields[field].sourceModel + '_' + form.fields[field].sourceField].$setValidity('apiError', false); + $('[name="' + form.fields[field].sourceModel + '_' + form.fields[field].sourceField + '"]').addClass('ng-invalid'); fieldErrors = true; } } else { if (data[field]) { - console.log('setting api error: ' + form.name + '_form.' + field); scope[field + '_api_error'] = data[field][0]; - scope[form.name + '_form'][field].$setValidity('apiError', false); + //scope[form.name + '_form'][field].$setValidity('apiError', false); + $('[name="' + field + '"]').addClass('ng-invalid'); fieldErrors = true; } } @@ -523,6 +525,7 @@ angular.module('Utilities',['RestServices', 'Utilities']) } }]) + /* Empty() * * Test if a value is 'empty'. Returns true if val is null | '' | undefined. @@ -533,7 +536,40 @@ angular.module('Utilities',['RestServices', 'Utilities']) return function(val) { return (val === null || val === undefined || val === '') ? true : false; } - }]); + }]) + + + /* Store + * + * Wrapper for local storage. All local storage requests flow through here so that we can + * stringify/unstringify objects and respond to future issues in one place. For example, + * we may at some point want to only use session storage rather than local storage. We might + * want to add a test for whether or not local/session storage exists for the browser, etc. + * + * store(key,value) will store the value using the key + * + * store(key) retrieves the value of the key + * + */ + .factory('Store', ['Empty', function(Empty) { + return function(key, value) { + if (!Empty(value)) { + // Store the value + localStorage[key] = JSON.stringify(value); + } + else if (!Empty(key)) { + // Return the value + var val = localStorage[key]; + return (!Empty(val)) ? JSON.parse(val) : null; + } + } + }]) + + + + + + diff --git a/awx/ui/static/lib/ansible/form-generator.js b/awx/ui/static/lib/ansible/form-generator.js index 8c251f586a..375f1e9a99 100644 --- a/awx/ui/static/lib/ansible/form-generator.js +++ b/awx/ui/static/lib/ansible/form-generator.js @@ -237,11 +237,13 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies', 'Utilities']) if (f.sourceModel) { scope[f.sourceModel + '_' + f.sourceField] = ''; scope[f.sourceModel + '_' + f.sourceField + '_api_error'] = ''; - scope[form.name + '_form'][f.sourceModel + '_' + f.sourceField].$setValidity('apiError', true); + if (scope[form.name + '_form'][f.sourceModel + '_' + f.sourceField]) { + scope[form.name + '_form'][f.sourceModel + '_' + f.sourceField].$setValidity('apiError', true); + } } if (f.type == 'lookup' && scope[form.name + '_form'][f.sourceModel + '_' + f.sourceField]) { scope[form.name + '_form'][f.sourceModel + '_' + f.sourceField].$setPristine(); - scope[form.name + '_form'][f.sourceModel + '_' + f.sourceField].$setValidity('apiError', true); + scope[form.name + '_form'][f.sourceModel + '_' + f.sourceField].$setValidity('apiError', true); } if (scope[form.name + '_form'][fld]) { scope[form.name + '_form'][fld].$setPristine(); @@ -361,12 +363,18 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies', 'Utilities']) }, clearApiErrors: function() { - for (fld in this.form.fields) { + for (var fld in this.form.fields) { if (this.form.fields[fld].sourceModel) { this.scope[this.form.fields[fld].sourceModel + '_' + this.form.fields[fld].sourceField + '_api_error'] = ''; + $('[name="' + this.form.fields[fld].sourceModel + '_' + this.form.fields[fld].sourceField + '"]').removeClass('ng-invalid'); + } + else if (this.form.fields[fld].realName) { + this.scope[this.form.fields[fld].realName + '_api_error'] = ''; + $('[name="' + this.form.fields[fld].realName + '"]').removeClass('ng-invalid'); } else { - this.scope[fld + '_api_error'] = ''; + this.scope[fld + '_api_error'] = ''; + $('[name="' + fld + '"]').removeClass('ng-invalid'); } } if (!this.scope.$$phase) { diff --git a/awx/ui/static/lib/ansible/generator-helpers.js b/awx/ui/static/lib/ansible/generator-helpers.js index 2c13b87a98..9d29e82882 100644 --- a/awx/ui/static/lib/ansible/generator-helpers.js +++ b/awx/ui/static/lib/ansible/generator-helpers.js @@ -233,7 +233,7 @@ angular.module('GeneratorHelpers', ['GeneratorHelpers']) var list = params['list']; var fld = params['fld']; var options = params['options']; - var field; + var html, field; if (params.field) { field = params.field; @@ -256,19 +256,6 @@ angular.module('GeneratorHelpers', ['GeneratorHelpers']) html = ''; } - /* - html += "
\n"; - html += "\n"; - */ html += "\n"; - return html; } }) - - .factory('PaginateWidget', function() { + + .factory('PaginateWidget', [ function() { return function(params) { - var set = params.set; - var iterator = params.iterator; - var useMini = params.mini; - var mode = (params.mode) ? params.mode : null; - var html = ''; - - if (mode == 'lookup') { - html += "
\n"; - html += "\n"; - html += "\n"; - html += "\n"; - - if (mode != 'lookup') { - html += "\n"; - html += "\n"; - } - - html += "
0\" "; - html += ">Page: {{ " + iterator + "Page + 1 }} of {{ " + iterator + "PageCount }}
\n"; - html += "\n"; + var iterator = params.iterator; + var set = params.set; + var html = ''; + html += "\n"; + html += "
\n"; + html += "
\n"; + html += "\n"; + html += "
\n"; + html += "
\n"; + html += "
\n"; + html += "Page {{ " + iterator + "_page }} of {{ " + iterator + "_num_pages }} for {{ " + iterator + "_total_rows | number:0 }} " + set + '.'; + html += "
\n"; + html += "
\n"; html += "
\n"; return html; - } - }); \ No newline at end of file + }]); + + diff --git a/awx/ui/static/lib/ansible/list-generator.js b/awx/ui/static/lib/ansible/list-generator.js index d8f30762f1..bc0951eafa 100644 --- a/awx/ui/static/lib/ansible/list-generator.js +++ b/awx/ui/static/lib/ansible/list-generator.js @@ -300,7 +300,7 @@ angular.module('ListGenerator', ['GeneratorHelpers']) html += "\""; html += ">\n"; if (list.index) { - html += "{{ $index + (" + list.iterator + "Page * " + list.iterator + "PageSize) + 1 }}.\n"; + html += "{{ $index + ((" + list.iterator + "_page - 1) * " + list.iterator + "_page_size) + 1 }}.\n"; } var cnt = 2; var base = (list.base) ? list.base : list.name; @@ -391,10 +391,10 @@ angular.module('ListGenerator', ['GeneratorHelpers']) if (list.name !== 'groups') { if ( options.mode == 'lookup' || (options.id && options.id == "form-modal-body") ) { - html += PaginateWidget({ set: list.name, iterator: list.iterator, mini: true, mode: 'lookup' }); + html += PaginateWidget({ set: list.name, iterator: list.iterator }); } else { - html += PaginateWidget({ set: list.name, iterator: list.iterator, mini: true }); + html += PaginateWidget({ set: list.name, iterator: list.iterator }); } } diff --git a/awx/ui/templates/ui/index.html b/awx/ui/templates/ui/index.html index 478f1e9c69..1e1ab7be0e 100644 --- a/awx/ui/templates/ui/index.html +++ b/awx/ui/templates/ui/index.html @@ -95,11 +95,10 @@ - - +