Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions app/App/HomeController.php
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ public function index(
if ($homepageOption === 'bookshelves') {
$shelves = $this->queries->shelves->visibleForListWithCover()
->orderBy($commonData['listOptions']->getSort(), $commonData['listOptions']->getOrder())
->paginate(18);
->paginate(setting()->getInteger('lists-page-count-shelves', 18, 1, 1000));
$data = array_merge($commonData, ['shelves' => $shelves]);

return view('home.shelves', $data);
Expand All @@ -92,7 +92,7 @@ public function index(
if ($homepageOption === 'books') {
$books = $this->queries->books->visibleForListWithCover()
->orderBy($commonData['listOptions']->getSort(), $commonData['listOptions']->getOrder())
->paginate(18);
->paginate(setting()->getInteger('lists-page-count-books', 18, 1, 1000));
$data = array_merge($commonData, ['books' => $books]);

return view('home.books', $data);
Expand Down
2 changes: 1 addition & 1 deletion app/Entities/Controllers/BookController.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public function index(Request $request)

$books = $this->queries->visibleForListWithCover()
->orderBy($listOptions->getSort(), $listOptions->getOrder())
->paginate(18);
->paginate(setting()->getInteger('lists-page-count-books', 18, 1, 1000));
$recents = $this->isSignedIn() ? $this->queries->recentlyViewedForCurrentUser()->take(4)->get() : false;
$popular = $this->queries->popularForList()->take(4)->get();
$new = $this->queries->visibleForList()->orderBy('created_at', 'desc')->take(4)->get();
Expand Down
2 changes: 1 addition & 1 deletion app/Entities/Controllers/BookshelfController.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public function index(Request $request)

$shelves = $this->queries->visibleForListWithCover()
->orderBy($listOptions->getSort(), $listOptions->getOrder())
->paginate(18);
->paginate(setting()->getInteger('lists-page-count-shelves', 18, 1, 1000));
$recents = $this->isSignedIn() ? $this->queries->recentlyViewedForCurrentUser()->get() : false;
$popular = $this->queries->popularForList()->get();
$new = $this->queries->visibleForList()
Expand Down
5 changes: 3 additions & 2 deletions app/Search/SearchController.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,11 @@ public function search(Request $request, SearchResultsFormatter $formatter)
$searchOpts = SearchOptions::fromRequest($request);
$fullSearchString = $searchOpts->toString();
$page = intval($request->get('page', '0')) ?: 1;
$count = setting()->getInteger('lists-page-count-search', 18, 1, 1000);

$results = $this->searchRunner->searchEntities($searchOpts, 'all', $page, 20);
$results = $this->searchRunner->searchEntities($searchOpts, 'all', $page, $count);
$formatter->format($results['results']->all(), $searchOpts);
$paginator = new LengthAwarePaginator($results['results'], $results['total'], 20, $page);
$paginator = new LengthAwarePaginator($results['results'], $results['total'], $count, $page);
$paginator->setPath('/search');
$paginator->appends($request->except('page'));

Expand Down
6 changes: 3 additions & 3 deletions app/Settings/AppSettingsStore.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public function __construct(
) {
}

public function storeFromUpdateRequest(Request $request, string $category)
public function storeFromUpdateRequest(Request $request, string $category): void
{
$this->storeSimpleSettings($request);
if ($category === 'customization') {
Expand Down Expand Up @@ -76,7 +76,7 @@ protected function updateAppLogo(Request $request): void
protected function storeSimpleSettings(Request $request): void
{
foreach ($request->all() as $name => $value) {
if (strpos($name, 'setting-') !== 0) {
if (!str_starts_with($name, 'setting-')) {
continue;
}

Expand All @@ -85,7 +85,7 @@ protected function storeSimpleSettings(Request $request): void
}
}

protected function destroyExistingSettingImage(string $settingKey)
protected function destroyExistingSettingImage(string $settingKey): void
{
$existingVal = setting()->get($settingKey);
if ($existingVal) {
Expand Down
15 changes: 15 additions & 0 deletions app/Settings/SettingService.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,21 @@ public function get(string $key, $default = null): mixed
return $this->formatValue($value, $default);
}

/**
* Get a setting from the database as an integer.
* Returns the default value if not found or not an integer, and clamps the value to the given min/max range.
*/
public function getInteger(string $key, int $default, int $min = 0, int $max = PHP_INT_MAX): int
{
$value = $this->get($key, $default);
if (!is_numeric($value)) {
return $default;
}

$int = intval($value);
return max($min, min($max, $int));
}

/**
* Get a value from the session instead of the main store option.
*/
Expand Down
6 changes: 4 additions & 2 deletions lang/en/settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@
'reg_confirm_restrict_domain_placeholder' => 'No restriction set',

// Sorting Settings
'sorting' => 'Sorting',
'sorting_book_default' => 'Default Book Sort',
'sorting' => 'Lists & Sorting',
'sorting_book_default' => 'Default Book Sort Rule',
'sorting_book_default_desc' => 'Select the default sort rule to apply to new books. This won\'t affect existing books, and can be overridden per-book.',
'sorting_rules' => 'Sort Rules',
'sorting_rules_desc' => 'These are predefined sorting operations which can be applied to content in the system.',
Expand All @@ -103,6 +103,8 @@
'sort_rule_op_updated_date' => 'Updated Date',
'sort_rule_op_chapters_first' => 'Chapters First',
'sort_rule_op_chapters_last' => 'Chapters Last',
'sorting_page_limits' => 'Per-Page Display Limits',
'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using an even multiple of 3 (18, 24, 30, etc...) is recommended.',

// Maintenance settings
'maint' => 'Maintenance',
Expand Down
4 changes: 4 additions & 0 deletions resources/sass/_forms.scss
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,10 @@ input[type=color] {
}
}

.small-inputs input {
width: 150px;
}

.simple-code-input {
background-color: #F8F8F8;
font-family: monospace;
Expand Down
3 changes: 2 additions & 1 deletion resources/views/form/number.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
@if($readonly ?? false) readonly="readonly" @endif
@if($min ?? false) min="{{ $min }}" @endif
@if($max ?? false) max="{{ $max }}" @endif
@if(isset($model) || old($name)) value="{{ old($name) ? old($name) : $model->$name}}" @endif>
@if($step ?? false) step="{{ $step }}" @endif
@if(isset($model) || old($name) || isset($value)) value="{{ old($name) ?? $model->$name ?? $value }}" @endif>
@if($errors->has($name))
<div class="text-neg text-small">{{ $errors->first($name) }}</div>
@endif
23 changes: 23 additions & 0 deletions resources/views/settings/categories/sorting.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,29 @@
<input type="hidden" name="section" value="sorting">

<div class="setting-list">
<div>
<div class="mb-m">
<label class="setting-list-label">{{ trans('settings.sorting_page_limits') }}</label>
<p class="small">{{ trans('settings.sorting_page_limits_desc') }}</p>
</div>
<div class="flex-container-row wrap gap-m small-inputs">
@php
$labelByKey = ['shelves' => trans('entities.shelves'), 'books' => trans('entities.books'), 'search' => trans('entities.search_results')];
@endphp
@foreach($labelByKey as $key => $label)
<div>
<label for="setting-lists-page-count-{{ $key }}">{{ $label }}</label>
@include('form.number', [
'name' => 'setting-lists-page-count-' . $key,
'value' => setting()->getInteger('lists-page-count-' . $key, 18, 1, 1000),
'min' => 1,
'step' => 1,
])
</div>
@endforeach
</div>
</div>

<div class="grid half gap-xl items-center">
<div>
<label for="setting-sorting-book-default"
Expand Down
81 changes: 81 additions & 0 deletions tests/Settings/PageListLimitsTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<?php

namespace Settings;

use BookStack\Entities\Models\Book;
use BookStack\Entities\Models\Bookshelf;
use Tests\TestCase;

class PageListLimitsTest extends TestCase
{
public function test_saving_setting_and_loading()
{
$resp = $this->asAdmin()->post('/settings/sorting', [
'setting-lists-page-count-shelves' => '3',
'setting-lists-page-count-books' => '6',
'setting-lists-page-count-search' => '9',
]);
$resp->assertRedirect('/settings/sorting');

$this->assertEquals(3, setting()->getInteger('lists-page-count-shelves', 18));
$this->assertEquals(6, setting()->getInteger('lists-page-count-books', 18));
$this->assertEquals(9, setting()->getInteger('lists-page-count-search', 18));

$resp = $this->get('/settings/sorting');
$html = $this->withHtml($resp);

$html->assertFieldHasValue('setting-lists-page-count-shelves', '3');
$html->assertFieldHasValue('setting-lists-page-count-books', '6');
$html->assertFieldHasValue('setting-lists-page-count-search', '9');
}

public function test_invalid_counts_will_use_default_when_fetched_as_an_integer()
{
$this->asAdmin()->post('/settings/sorting', [
'setting-lists-page-count-shelves' => 'cat',
]);

$this->assertEquals(18, setting()->getInteger('lists-page-count-shelves', 18));
}

public function test_shelf_count_is_used_on_shelves_view()
{
$resp = $this->asAdmin()->get('/shelves');
$defaultCount = min(Bookshelf::query()->count(), 18);
$this->withHtml($resp)->assertElementCount('main [data-entity-type="bookshelf"]', $defaultCount);

$this->post('/settings/sorting', [
'setting-lists-page-count-shelves' => '1',
]);

$resp = $this->get('/shelves');
$this->withHtml($resp)->assertElementCount('main [data-entity-type="bookshelf"]', 1);
}

public function test_book_count_is_used_on_books_view()
{
$resp = $this->asAdmin()->get('/books');
$defaultCount = min(Book::query()->count(), 18);
$this->withHtml($resp)->assertElementCount('main [data-entity-type="book"]', $defaultCount);

$this->post('/settings/sorting', [
'setting-lists-page-count-books' => '1',
]);

$resp = $this->get('/books');
$this->withHtml($resp)->assertElementCount('main [data-entity-type="book"]', 1);
}

public function test_search_count_is_used_on_search_view()
{
$resp = $this->asAdmin()->get('/search');
$this->withHtml($resp)->assertElementCount('.entity-list [data-entity-id]', 18);

$this->post('/settings/sorting', [
'setting-lists-page-count-search' => '1',
]);

$resp = $this->get('/search');
$this->withHtml($resp)->assertElementCount('.entity-list [data-entity-id]', 1);
}
}