keycloak-uncached

Details

diff --git a/themes/src/main/resources/theme/base/admin/resources/js/app.js b/themes/src/main/resources/theme/base/admin/resources/js/app.js
index eeae412..adb0c5e 100755
--- a/themes/src/main/resources/theme/base/admin/resources/js/app.js
+++ b/themes/src/main/resources/theme/base/admin/resources/js/app.js
@@ -2680,20 +2680,38 @@ module.directive('kcOnReadFile', function ($parse) {
 });
 
 module.controller('PagingCtrl', function ($scope) {
-    $scope.isLastPage = function()
-    {
-        if ($scope.currentPage === $scope.numberOfPages) {
-            return true;
-        }
-        return false;
+    $scope.currentPageInput = 1;
+    
+    $scope.firstPage = function() {
+        if (!$scope.hasPrevious()) return;
+        $scope.currentPage = 1;
+        $scope.currentPageInput = 1;
     };
-
-    $scope.isFirstPage = function()
-    {
-        if ($scope.currentPage === 1) {
-            return true;
-        }
-        return false;
+    
+    $scope.lastPage = function() {
+        if (!$scope.hasNext()) return;
+        $scope.currentPage = $scope.numberOfPages;
+        $scope.currentPageInput = $scope.numberOfPages;
+    };
+    
+    $scope.previousPage = function() {
+        if (!$scope.hasPrevious()) return;
+        $scope.currentPage--;
+        $scope.currentPageInput = $scope.currentPage;
+    };
+    
+    $scope.nextPage = function() {
+        if (!$scope.hasNext()) return;
+        $scope.currentPage++;
+        $scope.currentPageInput = $scope.currentPage;
+    };
+    
+    $scope.hasNext = function() {
+        return $scope.currentPage < $scope.numberOfPages;
+    };
+    
+    $scope.hasPrevious = function() {
+        return $scope.currentPage > 1;
     };
 });
 
@@ -2701,15 +2719,34 @@ module.directive('kcPaging', function () {
     return {
         scope: {
             currentPage: '=',
+            currentPageInput: '=',
             numberOfPages: '='
         },
-        restrict: 'A',
+        restrict: 'E',
         replace: true,
         controller: 'PagingCtrl',
         templateUrl: resourceUrl + '/templates/kc-paging.html'
     }
 });
 
+// Tests the page number input from currentPageInput to see
+// if it represents a valid page.  If so, the current page is changed.
+module.directive('kcValidPage', function() {
+   return {
+       require: 'ngModel',
+       link: function(scope, element, attrs, ctrl) {
+           ctrl.$validators.inRange = function(modelValue, viewValue) {
+               if (viewValue >= 1 && viewValue <= scope.numberOfPages) {
+                   scope.currentPage = viewValue;
+               }
+               
+               return true;
+           }
+       }
+   } 
+});
+
+// filter used for paged tables
 module.filter('startFrom', function () {
     return function (input, start) {
         if (input) {
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js b/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js
index d81882d..7d8de51 100755
--- a/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js
+++ b/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js
@@ -735,6 +735,7 @@ module.controller('ClientListCtrl', function($scope, realm, clients, Client, ser
     $scope.realm = realm;
     $scope.clients = clients;
     $scope.currentPage = 1;
+    $scope.currentPageInput = 1;
     $scope.pageSize = 20;
     $scope.numberOfPages = Math.ceil($scope.clients.length/$scope.pageSize);
 
@@ -743,6 +744,7 @@ module.controller('ClientListCtrl', function($scope, realm, clients, Client, ser
         $scope.totalItems = $scope.filtered.length;
         $scope.numberOfPages = Math.ceil($scope.totalItems/$scope.pageSize);
         $scope.currentPage = 1;
+        $scope.currentPageInput = 1;
   }, true);
 
     $scope.removeClient = function(client) {
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js b/themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js
index a42ef3c..d7909ac 100644
--- a/themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js
+++ b/themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js
@@ -1227,14 +1227,16 @@ module.controller('RoleListCtrl', function($scope, $route, Dialog, Notifications
     $scope.realm = realm;
     $scope.roles = roles;
     $scope.currentPage = 1;
+    $scope.currentPageInput = 1;
     $scope.pageSize = 20;
     $scope.numberOfPages = Math.ceil($scope.roles.length/$scope.pageSize);
 
     $scope.$watch('searchQuery', function (newVal, oldVal) {
-        $scope.filtered = filterFilter($scope.roles, newVal);
+        $scope.filtered = filterFilter($scope.roles, {name: newVal});
         $scope.totalItems = $scope.filtered.length;
         $scope.numberOfPages = Math.ceil($scope.totalItems/$scope.pageSize);
         $scope.currentPage = 1;
+        $scope.currentPageInput = 1;
     }, true);
 
     $scope.removeRole = function (role) {
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-list.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-list.html
index 50c71dc..51aaa20 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/client-list.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-list.html
@@ -4,59 +4,53 @@
         <kc-tooltip>{{:: 'clients.tooltip' | translate}}</kc-tooltip>
     </h1>
 
-    <table class="table table-striped table-bordered">
+    <table class="datatable table table-striped table-bordered dataTable no-footer">
         <thead>
-        <tr>
-            <th class="kc-table-actions" colspan="6">
-                <div class="form-inline">
-                    <div class="form-group">
-                        <div class="input-group">
-                            <input type="text" placeholder="{{:: 'search.placeholder' | translate}}" data-ng-model="search.clientId" class="form-control search" onkeyup="if(event.keyCode == 13){$(this).next('I').click();}">
-                            <div class="input-group-addon">
-                                <i class="fa fa-search" type="submit"></i>
+            <tr>
+                <th class="kc-table-actions" colspan="6">
+                    <div class="form-inline">
+                        <div class="form-group">
+                            <div class="input-group">
+                            <input type="text" placeholder="{{:: 'search.placeholder' | translate}}" data-ng-model="search.clientId" class="form-control search" onkeyup="if(event.keyCode === 13){$(this).next('I').click();}">
+                                <div class="input-group-addon">
+                                    <i class="fa fa-search" type="submit"></i>
+                                </div>
                             </div>
                         </div>
-                    </div>
 
-                    <div class="pull-right" data-ng-show="access.manageClients">
-                        <a id="createClient" class="btn btn-default" href="#/create/client/{{realm.realm}}">{{:: 'create' | translate}}</a>
-                        <a id="importClient" class="btn btn-default" href="#/import/client/{{realm.realm}}" data-ng-show="importButton">{{:: 'import' | translate}}</a>
+                        <div class="pull-right" data-ng-show="access.manageClients">
+                            <a id="createClient" class="btn btn-default" href="#/create/client/{{realm.realm}}">{{:: 'create' | translate}}</a>
+                            <a id="importClient" class="btn btn-default" href="#/import/client/{{realm.realm}}" data-ng-show="importButton">{{:: 'import' | translate}}</a>
+                        </div>
                     </div>
-                </div>
-            </th>
-        </tr>
-        <tr data-ng-hide="clients.length == 0">
-            <th>{{:: 'client-id' | translate}}</th>
-            <th>{{:: 'enabled' | translate}}</th>
-            <th>{{:: 'base-url' | translate}}</th>
-            <th colspan="3">{{:: 'actions' | translate}}</th>
-        </tr>
+                </th>
+            </tr>
+            <tr data-ng-hide="clients.length == 0">
+                <th>{{:: 'client-id' | translate}}</th>
+                <th>{{:: 'enabled' | translate}}</th>
+                <th>{{:: 'base-url' | translate}}</th>
+                <th colspan="3">{{:: 'actions' | translate}}</th>
+            </tr>
         </thead>
-        <tfoot data-ng-hide="(clients | filter:search).length == 0">
-            <tr>
-                <td class="kc-action-cell" colspan="6">
-                    <div kc-paging current-page='currentPage' number-of-pages='numberOfPages'></div>
+        <tbody>
+            <tr ng-repeat="client in clients| filter:search | orderBy:'clientId' | startFrom:(currentPage - 1) * pageSize | limitTo:pageSize">
+                <td><a href="#/realms/{{realm.realm}}/clients/{{client.id}}">{{client.clientId}}</a></td>
+                <td translate="{{client.enabled}}"></td>
+                <td ng-class="{'text-muted': !client.baseUrl}">
+                    <a href="{{client.rootUrl}}{{client.baseUrl}}" target="_blank" data-ng-show="client.baseUrl">{{client.rootUrl}}{{client.baseUrl}}</a>
+                    <span data-ng-hide="client.baseUrl">{{:: 'not-defined' | translate}}</span>
                 </td>
+                <td class="kc-action-cell" kc-open="/realms/{{realm.realm}}/clients/{{client.id}}">{{:: 'edit' | translate}}</td>
+                <td class="kc-action-cell" data-ng-click="exportClient(client)">{{:: 'export' | translate}}</td>
+                <td class="kc-action-cell" data-ng-show="access.manageClients" data-ng-click="removeClient(client)">{{:: 'delete' | translate}}</td>
+            </tr>
+            <tr data-ng-show="(clients | filter:search).length == 0">
+                <td class="text-muted" colspan="4" data-ng-show="search.clientId">{{:: 'no-results' | translate}}</td>
+                <td class="text-muted" colspan="4" data-ng-hide="search.clientId">{{:: 'no-clients-available' | translate}}</td>
             </tr>
-        </tfoot>
-        <tbody>
-        <tr ng-repeat="client in clients | filter:search | orderBy:'clientId' | startFrom:(currentPage-1)*pageSize | limitTo:pageSize">
-            <td><a href="#/realms/{{realm.realm}}/clients/{{client.id}}">{{client.clientId}}</a></td>
-            <td translate="{{client.enabled}}"></td>
-            <td ng-class="{'text-muted': !client.baseUrl}">
-                <a href="{{client.rootUrl}}{{client.baseUrl}}" target="_blank" data-ng-show="client.baseUrl">{{client.rootUrl}}{{client.baseUrl}}</a>
-                <span data-ng-hide="client.baseUrl">{{:: 'not-defined' | translate}}</span>
-            </td>
-            <td class="kc-action-cell" kc-open="/realms/{{realm.realm}}/clients/{{client.id}}">{{:: 'edit' | translate}}</td>
-            <td class="kc-action-cell" data-ng-click="exportClient(client)">{{:: 'export' | translate}}</td>
-            <td class="kc-action-cell" data-ng-show="access.manageClients" data-ng-click="removeClient(client)">{{:: 'delete' | translate}}</td>
-        </tr>
-        <tr data-ng-show="(clients | filter:search).length == 0">
-            <td class="text-muted" colspan="4" data-ng-show="search.clientId">{{:: 'no-results' | translate}}</td>
-            <td class="text-muted" colspan="4" data-ng-hide="search.clientId">{{:: 'no-clients-available' | translate}}</td>
-        </tr>
         </tbody>
     </table>
+    <kc-paging current-page='currentPage' number-of-pages='numberOfPages' current-page-input='currentPageInput'></kc-paging>
 </div>
 
 <kc-menu></kc-menu>
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/role-list.html b/themes/src/main/resources/theme/base/admin/resources/partials/role-list.html
index ca27439..5c0d473 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/role-list.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/role-list.html
@@ -6,54 +6,48 @@
         <li><a href="#/realms/{{realm.realm}}/default-roles">{{:: 'default-roles' | translate}}</a></li>
     </ul>
 
-    <table class="table table-striped table-bordered">
+    <table class="datatable table table-striped table-bordered dataTable no-footer">
         <thead>
-        <tr>
-            <th class="kc-table-actions" colspan="5">
-                <div class="form-inline">
-                    <div class="form-group">
-                        <div class="input-group">
-                            <input type="text" placeholder="{{:: 'search.placeholder' | translate}}" data-ng-model="searchQuery" class="form-control search" onkeyup="if(event.keyCode == 13){$(this).next('I').click();}">
-                            <div class="input-group-addon">
-                                <i class="fa fa-search" type="submit"></i>
+            <tr>
+                <th class="kc-table-actions" colspan="5">
+                    <div class="form-inline">
+                        <div class="form-group">
+                            <div class="input-group">
+                                <input type="text" placeholder="{{:: 'search.placeholder' | translate}}" data-ng-model="searchQuery" class="form-control search" onkeyup="if (event.keyCode === 13){$(this).next('I').click(); }">
+                                <div class="input-group-addon">
+                                    <i class="fa fa-search" type="submit"></i>
+                                </div>
                             </div>
                         </div>
-                    </div>
 
-                    <div class="pull-right" data-ng-show="access.manageRealm">
-                        <a id="createRole" class="btn btn-default" href="#/create/role/{{realm.realm}}">{{:: 'add-role' | translate}}</a>
+                        <div class="pull-right" data-ng-show="access.manageRealm">
+                            <a id="createRole" class="btn btn-default" href="#/create/role/{{realm.realm}}">{{:: 'add-role' | translate}}</a>
+                        </div>
                     </div>
-                </div>
-            </th>
-        </tr>
-        <tr data-ng-show="roles && roles.length > 0">
-            <th>{{:: 'role-name' | translate}}</th>
-            <th>{{:: 'composite' | translate}}</th>
-            <th>{{:: 'description' | translate}}</th>
-            <th colspan="2">{{:: 'actions' | translate}}</th>
-        </tr>
-        </thead>
-        <tfoot data-ng-hide="(roles | filter:{name: searchQuery}).length == 0">
-            <tr>
-                <td class="kc-action-cell" colspan="6">
-                    <div kc-paging current-page='currentPage' number-of-pages='numberOfPages'></div>
-                </td>
+                </th>
             </tr>
-        </tfoot>
+            <tr data-ng-show="roles && roles.length > 0">
+                <th>{{:: 'role-name' | translate}}</th>
+                <th>{{:: 'composite' | translate}}</th>
+                <th>{{:: 'description' | translate}}</th>
+                <th colspan="2">{{:: 'actions' | translate}}</th>
+            </tr>
+        </thead>
         <tbody>
-        <tr ng-repeat="role in roles | filter:{name: searchQuery} | orderBy:'name'| startFrom:(currentPage-1)*pageSize | limitTo:pageSize">
-            <td><a href="#/realms/{{realm.realm}}/roles/{{role.id}}">{{role.name}}</a></td>
-            <td translate="{{role.composite}}"></td>
-            <td>{{role.description}}</td>
-            <td class="kc-action-cell" kc-open="/realms/{{realm.realm}}/roles/{{role.id}}">{{:: 'edit' | translate}}</td>
-            <td class="kc-action-cell" data-ng-click="removeRole(role)">{{:: 'delete' | translate}}</td>
-        </tr>
-        <tr data-ng-show="(roles | filter:{name: searchQuery}).length == 0">
-            <td class="text-muted" colspan="4" data-ng-show="searchQuery">{{:: 'no-results' | translate}}</td>
-            <td class="text-muted" colspan="4" data-ng-hide="searchQuery">{{:: 'no-realm-roles-available' | translate}}</td>
-        </tr>
+            <tr ng-repeat="role in roles| filter:{name: searchQuery} | orderBy:'name'| startFrom:(currentPage - 1) * pageSize | limitTo:pageSize">
+                <td><a href="#/realms/{{realm.realm}}/roles/{{role.id}}">{{role.name}}</a></td>
+                <td translate="{{role.composite}}"></td>
+                <td>{{role.description}}</td>
+                <td class="kc-action-cell" kc-open="/realms/{{realm.realm}}/roles/{{role.id}}">{{:: 'edit' | translate}}</td>
+                <td class="kc-action-cell" data-ng-click="removeRole(role)">{{:: 'delete' | translate}}</td>
+            </tr>
+            <tr data-ng-show="(roles | filter:{name: searchQuery}).length == 0">
+                <td class="text-muted" colspan="4" data-ng-show="searchQuery">{{:: 'no-results' | translate}}</td>
+                <td class="text-muted" colspan="4" data-ng-hide="searchQuery">{{:: 'no-realm-roles-available' | translate}}</td>
+            </tr>
         </tbody>
     </table>
+    <kc-paging current-page='currentPage' number-of-pages='numberOfPages' current-page-input='currentPageInput'></kc-paging>
 </div>
 
 <kc-menu></kc-menu>
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/admin/resources/templates/kc-paging.html b/themes/src/main/resources/theme/base/admin/resources/templates/kc-paging.html
index d875c72..653e4a5 100644
--- a/themes/src/main/resources/theme/base/admin/resources/templates/kc-paging.html
+++ b/themes/src/main/resources/theme/base/admin/resources/templates/kc-paging.html
@@ -1,19 +1,25 @@
-<div class="pull-right">
-    <ul class="pagination">
-        <li ng-class="{disabled: currentPage === 1}" ng-click="currentPage=1">
-            <span class="i fa fa-angle-double-left"></span>
-        </li>
-        <li ng-class="{disabled: currentPage === 1}" ng-click="isFirstPage() || (currentPage=currentPage-1)">
-            <span class="i fa fa-angle-left"></span>
-        </li>
-        <li>
-            <span style="padding: 1px 15px 0;"><input ng-model="currentPage" ng-max="numberOfPages" class="kc-pagination"> of&nbsp;&nbsp;{{numberOfPages}}</span>
+<div ng-hide="numberOfPages < 2" class="dataTables_footer">
+    <div class="dataTables_paginate paging_bootstrap_input">
+        <ul class="pagination">  
+            <li class="first" ng-class="{disabled: !hasPrevious()}" ng-click="firstPage()">
+                <span class="i fa fa-angle-double-left"></span>
+            </li>  
+            <li class="prev" ng-class="{disabled: !hasPrevious()}" ng-click="previousPage()">
+                <span class="i fa fa-angle-left"></span>
             </li>
-            <li ng-class="{disabled: currentPage === numberOfPages}" ng-click="isLastPage() || (currentPage=currentPage+1)">
-                <span class="i fa fa-angle-right" ></span>
-            </li>
-            <li ng-class="{disabled: currentPage === numberOfPages}" ng-click="currentPage=numberOfPages">
-                <span class="i fa fa-angle-double-right"></span>
+        </ul>
+        <div class="pagination-input">  
+            <input ng-model="currentPageInput" kc-valid-page class="paginate_input" type="text">
+            <span class="paginate_of">of <b>{{numberOfPages}}</b></span>
+        </div>
+        <ul class="pagination">  
+            <li class="next" ng-class="{disabled: !hasNext()}" ng-click="nextPage()">
+                <span class="i fa fa-angle-right"></span>
+            </li>  
+            <li class="last" ng-class="{disabled: !hasNext()}" ng-click="lastPage()">
+                <span class="i fa fa-angle-double-right">
+                </span>
             </li>
         </ul>
-    </div>
\ No newline at end of file
+    </div>
+</div>
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/keycloak/admin/resources/css/styles.css b/themes/src/main/resources/theme/keycloak/admin/resources/css/styles.css
index daa55f5..9253c8c 100755
--- a/themes/src/main/resources/theme/keycloak/admin/resources/css/styles.css
+++ b/themes/src/main/resources/theme/keycloak/admin/resources/css/styles.css
@@ -379,14 +379,4 @@ h1 i {
 .ace_editor {
     height: 600px;
     width: 100%;
-}
-
-.kc-pagination {
-    border: 1px solid #d1d1d1;
-    font-size: 12px;
-    height: 23px;
-    margin-right: 10px;
-    padding-right: 10px;
-    text-align: right;
-    width: 30px;
 }
\ No newline at end of file