Skip to content

Commit 4c574c2

Browse files
committed
Implemented functionality to make books sort function
Also changed public user settings to be stored in session rather than DB. Cleaned existing list view type logic.
1 parent 0b976d9 commit 4c574c2

16 files changed

Lines changed: 216 additions & 45 deletions

File tree

app/Entities/Entity.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,11 @@ public function views()
102102
return $this->morphMany(View::class, 'viewable');
103103
}
104104

105+
public function viewCountQuery()
106+
{
107+
return $this->views()->selectRaw('viewable_id, sum(views) as view_count')->groupBy('viewable_id');
108+
}
109+
105110
/**
106111
* Get the Tag models that have been user assigned to this entity.
107112
* @return \Illuminate\Database\Eloquent\Relations\MorphMany

app/Entities/Repos/EntityRepo.php

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use BookStack\Exceptions\NotifyException;
1616
use BookStack\Uploads\AttachmentService;
1717
use DOMDocument;
18+
use Illuminate\Database\Eloquent\Builder;
1819
use Illuminate\Http\Request;
1920
use Illuminate\Support\Collection;
2021

@@ -179,11 +180,27 @@ public function getAll($type, $count = 20, $permission = 'view')
179180
* Get all entities in a paginated format
180181
* @param $type
181182
* @param int $count
183+
* @param string $sort
184+
* @param string $order
182185
* @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
183186
*/
184-
public function getAllPaginated($type, $count = 10)
187+
public function getAllPaginated($type, int $count = 10, string $sort = 'name', string $order = 'asc')
185188
{
186-
return $this->entityQuery($type)->orderBy('name', 'asc')->paginate($count);
189+
$query = $this->entityQuery($type);
190+
$query = $this->addSortToQuery($query, $sort, $order);
191+
return $query->paginate($count);
192+
}
193+
194+
protected function addSortToQuery(Builder $query, string $sort = 'name', string $order = 'asc')
195+
{
196+
$order = ($order === 'asc') ? 'asc' : 'desc';
197+
$propertySorts = ['name', 'created_at', 'updated_at'];
198+
199+
if (in_array($sort, $propertySorts)) {
200+
return $query->orderBy($sort, $order);
201+
}
202+
203+
return $query;
187204
}
188205

189206
/**

app/Http/Controllers/BookController.php

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,18 +36,30 @@ public function __construct(EntityRepo $entityRepo, UserRepo $userRepo, ExportSe
3636
*/
3737
public function index()
3838
{
39-
$books = $this->entityRepo->getAllPaginated('book', 18);
39+
$view = setting()->getUser($this->currentUser, 'books_view_type', config('app.views.books'));
40+
$sort = setting()->getUser($this->currentUser, 'books_sort', 'name');
41+
$order = setting()->getUser($this->currentUser, 'books_sort_order', 'asc');
42+
$sortOptions = [
43+
'name' => trans('common.sort_name'),
44+
'created_at' => trans('common.sort_created_at'),
45+
'updated_at' => trans('common.sort_updated_at'),
46+
];
47+
48+
$books = $this->entityRepo->getAllPaginated('book', 18, $sort, $order);
4049
$recents = $this->signedIn ? $this->entityRepo->getRecentlyViewed('book', 4, 0) : false;
4150
$popular = $this->entityRepo->getPopular('book', 4, 0);
4251
$new = $this->entityRepo->getRecentlyCreated('book', 4, 0);
43-
$booksViewType = setting()->getUser($this->currentUser, 'books_view_type', config('app.views.books', 'list'));
52+
4453
$this->setPageTitle(trans('entities.books'));
4554
return view('books/index', [
4655
'books' => $books,
4756
'recents' => $recents,
4857
'popular' => $popular,
4958
'new' => $new,
50-
'booksViewType' => $booksViewType
59+
'view' => $view,
60+
'sort' => $sort,
61+
'order' => $order,
62+
'sortOptions' => $sortOptions,
5163
]);
5264
}
5365

app/Http/Controllers/Controller.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,20 @@ protected function checkPermissionOr($permissionName, $callback)
123123
return true;
124124
}
125125

126+
/**
127+
* Check if the current user has a permission or bypass if the provided user
128+
* id matches the current user.
129+
* @param string $permissionName
130+
* @param int $userId
131+
* @return bool
132+
*/
133+
protected function checkPermissionOrCurrentUser(string $permissionName, int $userId)
134+
{
135+
return $this->checkPermissionOr($permissionName, function() use ($userId) {
136+
return $userId === $this->currentUser->id;
137+
});
138+
}
139+
126140
/**
127141
* Send back a json error message.
128142
* @param string $messageText

app/Http/Controllers/UserController.php

Lines changed: 59 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -247,41 +247,83 @@ public function showProfilePage($id)
247247
*/
248248
public function switchBookView($id, Request $request)
249249
{
250-
$this->checkPermissionOr('users-manage', function () use ($id) {
251-
return $this->currentUser->id == $id;
252-
});
250+
return $this->switchViewType($id, $request, 'books');
251+
}
252+
253+
/**
254+
* Update the user's preferred shelf-list display setting.
255+
* @param $id
256+
* @param Request $request
257+
* @return \Illuminate\Http\RedirectResponse
258+
*/
259+
public function switchShelfView($id, Request $request)
260+
{
261+
return $this->switchViewType($id, $request, 'bookshelves');
262+
}
263+
264+
/**
265+
* For a type of list, switch with stored view type for a user.
266+
* @param integer $userId
267+
* @param Request $request
268+
* @param string $listName
269+
* @return \Illuminate\Http\RedirectResponse
270+
*/
271+
protected function switchViewType($userId, Request $request, string $listName)
272+
{
273+
$this->checkPermissionOrCurrentUser('users-manage', $userId);
253274

254275
$viewType = $request->get('view_type');
255276
if (!in_array($viewType, ['grid', 'list'])) {
256277
$viewType = 'list';
257278
}
258279

259-
$user = $this->user->findOrFail($id);
260-
setting()->putUser($user, 'books_view_type', $viewType);
280+
$user = $this->user->findOrFail($userId);
281+
$key = $listName . '_view_type';
282+
setting()->putUser($user, $key, $viewType);
261283

262-
return redirect()->back(302, [], "/settings/users/$id");
284+
return redirect()->back(302, [], "/settings/users/$userId");
263285
}
264286

265287
/**
266-
* Update the user's preferred shelf-list display setting.
288+
* Change the stored sort type for the books view.
267289
* @param $id
268290
* @param Request $request
269291
* @return \Illuminate\Http\RedirectResponse
270292
*/
271-
public function switchShelfView($id, Request $request)
293+
public function changeBooksSort($id, Request $request)
272294
{
273-
$this->checkPermissionOr('users-manage', function () use ($id) {
274-
return $this->currentUser->id == $id;
275-
});
295+
// TODO - Test this endpoint
296+
return $this->changeListSort($id, $request, 'books');
297+
}
276298

277-
$viewType = $request->get('view_type');
278-
if (!in_array($viewType, ['grid', 'list'])) {
279-
$viewType = 'list';
299+
/**
300+
* Changed the stored preference for a list sort order.
301+
* @param int $userId
302+
* @param Request $request
303+
* @param string $listName
304+
* @return \Illuminate\Http\RedirectResponse
305+
*/
306+
protected function changeListSort(int $userId, Request $request, string $listName)
307+
{
308+
$this->checkPermissionOrCurrentUser('users-manage', $userId);
309+
310+
$sort = $request->get('sort');
311+
if (!in_array($sort, ['name', 'created_at', 'updated_at'])) {
312+
$sort = 'name';
280313
}
281314

282-
$user = $this->user->findOrFail($id);
283-
setting()->putUser($user, 'bookshelves_view_type', $viewType);
315+
$order = $request->get('order');
316+
if (!in_array($order, ['asc', 'desc'])) {
317+
$order = 'asc';
318+
}
284319

285-
return redirect()->back(302, [], "/settings/users/$id");
320+
$user = $this->user->findOrFail($userId);
321+
$sortKey = $listName . '_sort';
322+
$orderKey = $listName . '_sort_order';
323+
setting()->putUser($user, $sortKey, $sort);
324+
setting()->putUser($user, $orderKey, $order);
325+
326+
return redirect()->back(302, [], "/settings/users/$userId");
286327
}
328+
287329
}

app/Settings/SettingService.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ public function get($key, $default = false)
6060
*/
6161
public function getUser($user, $key, $default = false)
6262
{
63+
if ($user->isDefault()) {
64+
return session()->get($key, $default);
65+
}
6366
return $this->get($this->userKey($user->id, $key), $default);
6467
}
6568

@@ -179,6 +182,9 @@ public function put($key, $value)
179182
*/
180183
public function putUser($user, $key, $value)
181184
{
185+
if ($user->isDefault()) {
186+
return session()->put($key, $value);
187+
}
182188
return $this->put($this->userKey($user->id, $key), $value);
183189
}
184190

resources/assets/js/components/index.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import pageDisplay from "./page-display";
1919
import shelfSort from "./shelf-sort";
2020
import homepageControl from "./homepage-control";
2121
import headerMobileToggle from "./header-mobile-toggle";
22+
import listSortControl from "./list-sort-control";
2223

2324

2425
const componentMapping = {
@@ -42,7 +43,8 @@ const componentMapping = {
4243
'page-display': pageDisplay,
4344
'shelf-sort': shelfSort,
4445
'homepage-control': homepageControl,
45-
'header-mobile-toggle': headerMobileToggle,
46+
'header-mobile-toggle': headerMobileToggle,
47+
'list-sort-control': listSortControl,
4648
};
4749

4850
window.components = {};
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/**
2+
* ListSortControl
3+
* Manages the logic for the control which provides list sorting options.
4+
*/
5+
class ListSortControl {
6+
7+
constructor(elem) {
8+
this.elem = elem;
9+
10+
this.sortInput = elem.querySelector('[name="sort"]');
11+
this.orderInput = elem.querySelector('[name="order"]');
12+
this.form = elem.querySelector('form');
13+
14+
this.elem.addEventListener('click', event => {
15+
if (event.target.closest('[data-sort-value]') !== null) {
16+
this.sortOptionClick(event);
17+
}
18+
if (event.target.closest('[data-sort-dir]') !== null) {
19+
this.sortDirectionClick(event);
20+
}
21+
})
22+
23+
}
24+
25+
sortOptionClick(event) {
26+
const sortOption = event.target.closest('[data-sort-value]');
27+
this.sortInput.value = sortOption.getAttribute('data-sort-value');
28+
event.preventDefault();
29+
this.form.submit();
30+
}
31+
32+
sortDirectionClick(event) {
33+
const currentDir = this.orderInput.value;
34+
const newDir = (currentDir === 'asc') ? 'desc' : 'asc';
35+
this.orderInput.value = newDir;
36+
event.preventDefault();
37+
this.form.submit();
38+
}
39+
40+
}
41+
42+
export default ListSortControl;

resources/assets/sass/_lists.scss

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,9 @@ ul.pagination {
369369
padding: $-xs $-m;
370370
line-height: 1.2;
371371
}
372+
li.active a {
373+
font-weight: 600;
374+
}
372375
a, button {
373376
display: block;
374377
padding: $-xs $-m;

resources/assets/sass/styles.scss

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,9 @@ $btt-size: 40px;
258258

259259
.list-sort-container {
260260
display: inline-block;
261+
form {
262+
display: inline-block;
263+
}
261264
.list-sort {
262265
display: inline-grid;
263266
margin-left: $-s;

0 commit comments

Comments
 (0)