Added pagination, sorting & searching to users list
As requested on #113
Showing
7 changed files
with
119 additions
and
13 deletions
| ... | @@ -31,14 +31,21 @@ class UserController extends Controller | ... | @@ -31,14 +31,21 @@ class UserController extends Controller |
| 31 | 31 | ||
| 32 | /** | 32 | /** |
| 33 | * Display a listing of the users. | 33 | * Display a listing of the users. |
| 34 | + * @param Request $request | ||
| 34 | * @return Response | 35 | * @return Response |
| 35 | */ | 36 | */ |
| 36 | - public function index() | 37 | + public function index(Request $request) |
| 37 | { | 38 | { |
| 38 | $this->checkPermission('users-manage'); | 39 | $this->checkPermission('users-manage'); |
| 39 | - $users = $this->userRepo->getAllUsers(); | 40 | + $listDetails = [ |
| 41 | + 'order' => $request->has('order') ? $request->get('order') : 'asc', | ||
| 42 | + 'search' => $request->has('search') ? $request->get('search') : '', | ||
| 43 | + 'sort' => $request->has('sort') ? $request->get('sort') : 'name', | ||
| 44 | + ]; | ||
| 45 | + $users = $this->userRepo->getAllUsersPaginatedAndSorted(20, $listDetails); | ||
| 40 | $this->setPageTitle('Users'); | 46 | $this->setPageTitle('Users'); |
| 41 | - return view('users/index', ['users' => $users]); | 47 | + $users->appends($listDetails); |
| 48 | + return view('users/index', ['users' => $users, 'listDetails' => $listDetails]); | ||
| 42 | } | 49 | } |
| 43 | 50 | ||
| 44 | /** | 51 | /** | ... | ... |
| ... | @@ -52,6 +52,27 @@ class UserRepo | ... | @@ -52,6 +52,27 @@ class UserRepo |
| 52 | } | 52 | } |
| 53 | 53 | ||
| 54 | /** | 54 | /** |
| 55 | + * Get all the users with their permissions in a paginated format. | ||
| 56 | + * @param int $count | ||
| 57 | + * @param $sortData | ||
| 58 | + * @return \Illuminate\Database\Eloquent\Builder|static | ||
| 59 | + */ | ||
| 60 | + public function getAllUsersPaginatedAndSorted($count = 20, $sortData) | ||
| 61 | + { | ||
| 62 | + $query = $this->user->with('roles', 'avatar')->orderBy($sortData['sort'], $sortData['order']); | ||
| 63 | + | ||
| 64 | + if ($sortData['search']) { | ||
| 65 | + $term = '%' . $sortData['search'] . '%'; | ||
| 66 | + $query->where(function($query) use ($term) { | ||
| 67 | + $query->where('name', 'like', $term) | ||
| 68 | + ->orWhere('email', 'like', $term); | ||
| 69 | + }); | ||
| 70 | + } | ||
| 71 | + | ||
| 72 | + return $query->paginate($count); | ||
| 73 | + } | ||
| 74 | + | ||
| 75 | + /** | ||
| 55 | * Creates a new user and attaches a role to them. | 76 | * Creates a new user and attaches a role to them. |
| 56 | * @param array $data | 77 | * @param array $data |
| 57 | * @return User | 78 | * @return User | ... | ... |
| ... | @@ -59,3 +59,35 @@ function setting($key, $default = false) | ... | @@ -59,3 +59,35 @@ function setting($key, $default = false) |
| 59 | $settingService = app('BookStack\Services\SettingService'); | 59 | $settingService = app('BookStack\Services\SettingService'); |
| 60 | return $settingService->get($key, $default); | 60 | return $settingService->get($key, $default); |
| 61 | } | 61 | } |
| 62 | + | ||
| 63 | +/** | ||
| 64 | + * Generate a url with multiple parameters for sorting purposes. | ||
| 65 | + * Works out the logic to set the correct sorting direction | ||
| 66 | + * Discards empty parameters and allows overriding. | ||
| 67 | + * @param $path | ||
| 68 | + * @param array $data | ||
| 69 | + * @param array $overrideData | ||
| 70 | + * @return string | ||
| 71 | + */ | ||
| 72 | +function sortUrl($path, $data, $overrideData = []) | ||
| 73 | +{ | ||
| 74 | + $queryStringSections = []; | ||
| 75 | + $queryData = array_merge($data, $overrideData); | ||
| 76 | + | ||
| 77 | + // Change sorting direction is already sorted on current attribute | ||
| 78 | + if (isset($overrideData['sort']) && $overrideData['sort'] === $data['sort']) { | ||
| 79 | + $queryData['order'] = ($data['order'] === 'asc') ? 'desc' : 'asc'; | ||
| 80 | + } else { | ||
| 81 | + $queryData['order'] = 'asc'; | ||
| 82 | + } | ||
| 83 | + | ||
| 84 | + foreach ($queryData as $name => $value) { | ||
| 85 | + $trimmedVal = trim($value); | ||
| 86 | + if ($trimmedVal === '') continue; | ||
| 87 | + $queryStringSections[] = urlencode($name) . '=' . urlencode($trimmedVal); | ||
| 88 | + } | ||
| 89 | + | ||
| 90 | + if (count($queryStringSections) === 0) return $path; | ||
| 91 | + | ||
| 92 | + return $path . '?' . implode('&', $queryStringSections); | ||
| 93 | +} | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
| ... | @@ -266,6 +266,7 @@ ul.pagination { | ... | @@ -266,6 +266,7 @@ ul.pagination { |
| 266 | display: inline-block; | 266 | display: inline-block; |
| 267 | list-style: none; | 267 | list-style: none; |
| 268 | margin: $-m 0; | 268 | margin: $-m 0; |
| 269 | + padding-left: 1px; | ||
| 269 | li { | 270 | li { |
| 270 | float: left; | 271 | float: left; |
| 271 | } | 272 | } |
| ... | @@ -300,6 +301,10 @@ ul.pagination { | ... | @@ -300,6 +301,10 @@ ul.pagination { |
| 300 | } | 301 | } |
| 301 | } | 302 | } |
| 302 | 303 | ||
| 304 | +.compact ul.pagination { | ||
| 305 | + margin: 0; | ||
| 306 | +} | ||
| 307 | + | ||
| 303 | .entity-list { | 308 | .entity-list { |
| 304 | >div { | 309 | >div { |
| 305 | padding: $-m 0; | 310 | padding: $-m 0; | ... | ... |
| ... | @@ -6,11 +6,15 @@ | ... | @@ -6,11 +6,15 @@ |
| 6 | 6 | ||
| 7 | <div class="container small"> | 7 | <div class="container small"> |
| 8 | 8 | ||
| 9 | + <div class="row action-header"> | ||
| 10 | + <div class="col-sm-8"> | ||
| 9 | <h1>User Roles</h1> | 11 | <h1>User Roles</h1> |
| 10 | - | 12 | + </div> |
| 11 | - <p> | 13 | + <div class="col-sm-4"> |
| 12 | - <a href="/settings/roles/new" class="text-pos"><i class="zmdi zmdi-lock-open"></i>Add new role</a> | 14 | + <p></p> |
| 13 | - </p> | 15 | + <a href="/settings/roles/new" class="button float right pos"><i class="zmdi zmdi-lock-open"></i>Add new role</a> |
| 16 | + </div> | ||
| 17 | + </div> | ||
| 14 | 18 | ||
| 15 | <table class="table"> | 19 | <table class="table"> |
| 16 | <tr> | 20 | <tr> | ... | ... |
| ... | @@ -7,17 +7,42 @@ | ... | @@ -7,17 +7,42 @@ |
| 7 | 7 | ||
| 8 | 8 | ||
| 9 | <div class="container small" ng-non-bindable> | 9 | <div class="container small" ng-non-bindable> |
| 10 | + <div class="row action-header"> | ||
| 11 | + <div class="col-sm-8"> | ||
| 10 | <h1>Users</h1> | 12 | <h1>Users</h1> |
| 13 | + </div> | ||
| 14 | + <div class="col-sm-4"> | ||
| 15 | + <p></p> | ||
| 11 | @if(userCan('users-manage')) | 16 | @if(userCan('users-manage')) |
| 12 | - <p> | 17 | + <a href="/settings/users/create" class="pos button float right"><i class="zmdi zmdi-account-add"></i>Add new user</a> |
| 13 | - <a href="/settings/users/create" class="text-pos"><i class="zmdi zmdi-account-add"></i>Add new user</a> | ||
| 14 | - </p> | ||
| 15 | @endif | 18 | @endif |
| 19 | + </div> | ||
| 20 | + </div> | ||
| 21 | + | ||
| 22 | + <div class="row"> | ||
| 23 | + <div class="col-sm-8"> | ||
| 24 | + <div class="compact"> | ||
| 25 | + {!! $users->links() !!} | ||
| 26 | + </div> | ||
| 27 | + </div> | ||
| 28 | + <div class="col-sm-4"> | ||
| 29 | + <form method="get" class="float right" action="/settings/users"> | ||
| 30 | + @foreach(collect($listDetails)->except('search') as $name => $val) | ||
| 31 | + <input type="hidden" name="{{$name}}" value="{{$val}}"> | ||
| 32 | + @endforeach | ||
| 33 | + <input type="text" name="search" placeholder="Search Users" @if($listDetails['search']) value="{{$listDetails['search']}}" @endif> | ||
| 34 | + </form> | ||
| 35 | + </div> | ||
| 36 | + </div> | ||
| 37 | + <div class="text-center"> | ||
| 38 | + | ||
| 39 | + </div> | ||
| 40 | + | ||
| 16 | <table class="table"> | 41 | <table class="table"> |
| 17 | <tr> | 42 | <tr> |
| 18 | <th></th> | 43 | <th></th> |
| 19 | - <th>Name</th> | 44 | + <th><a href="{{ sortUrl('/settings/users', $listDetails, ['sort' => 'name']) }}">Name</a></th> |
| 20 | - <th>Email</th> | 45 | + <th><a href="{{ sortUrl('/settings/users', $listDetails, ['sort' => 'email']) }}">Email</a></th> |
| 21 | <th>User Roles</th> | 46 | <th>User Roles</th> |
| 22 | </tr> | 47 | </tr> |
| 23 | @foreach($users as $user) | 48 | @foreach($users as $user) |
| ... | @@ -42,11 +67,17 @@ | ... | @@ -42,11 +67,17 @@ |
| 42 | @endif | 67 | @endif |
| 43 | </td> | 68 | </td> |
| 44 | <td> | 69 | <td> |
| 45 | - <small> {{ $user->roles->implode('display_name', ', ') }}</small> | 70 | + @foreach($user->roles as $index => $role) |
| 71 | + <small><a href="/settings/roles/{{$role->id}}">{{$role->display_name}}</a>@if($index !== count($user->roles) -1),@endif</small> | ||
| 72 | + @endforeach | ||
| 46 | </td> | 73 | </td> |
| 47 | </tr> | 74 | </tr> |
| 48 | @endforeach | 75 | @endforeach |
| 49 | </table> | 76 | </table> |
| 77 | + | ||
| 78 | + <div> | ||
| 79 | + {!! $users->links() !!} | ||
| 80 | + </div> | ||
| 50 | </div> | 81 | </div> |
| 51 | 82 | ||
| 52 | @stop | 83 | @stop | ... | ... |
-
Please register or sign in to post a comment