mirror of
https://github.com/BookStackApp/BookStack.git
synced 2025-12-13 07:42:23 +03:00
List page settings: Review of #5606
Updated setting display to show mulitple number inputs under one heading group. Updated settings to use general number field form view template. Updated translations to match display changes, and to advise on counts. Added page count control for search results. Added setting service method, to get settings as integers, with min/max/default control. Updating sorting group to be names "Lists & Sorting". Added tests to cover.
This commit is contained in:
@@ -83,7 +83,7 @@ class HomeController extends Controller
|
|||||||
if ($homepageOption === 'bookshelves') {
|
if ($homepageOption === 'bookshelves') {
|
||||||
$shelves = $this->queries->shelves->visibleForListWithCover()
|
$shelves = $this->queries->shelves->visibleForListWithCover()
|
||||||
->orderBy($commonData['listOptions']->getSort(), $commonData['listOptions']->getOrder())
|
->orderBy($commonData['listOptions']->getSort(), $commonData['listOptions']->getOrder())
|
||||||
->paginate(intval(setting('sorting-shelves-per-page', '18')));
|
->paginate(setting()->getInteger('lists-page-count-shelves', 18, 1, 1000));
|
||||||
$data = array_merge($commonData, ['shelves' => $shelves]);
|
$data = array_merge($commonData, ['shelves' => $shelves]);
|
||||||
|
|
||||||
return view('home.shelves', $data);
|
return view('home.shelves', $data);
|
||||||
@@ -92,7 +92,7 @@ class HomeController extends Controller
|
|||||||
if ($homepageOption === 'books') {
|
if ($homepageOption === 'books') {
|
||||||
$books = $this->queries->books->visibleForListWithCover()
|
$books = $this->queries->books->visibleForListWithCover()
|
||||||
->orderBy($commonData['listOptions']->getSort(), $commonData['listOptions']->getOrder())
|
->orderBy($commonData['listOptions']->getSort(), $commonData['listOptions']->getOrder())
|
||||||
->paginate(intval(setting('sorting-books-per-page', '18')));
|
->paginate(setting()->getInteger('lists-page-count-books', 18, 1, 1000));
|
||||||
$data = array_merge($commonData, ['books' => $books]);
|
$data = array_merge($commonData, ['books' => $books]);
|
||||||
|
|
||||||
return view('home.books', $data);
|
return view('home.books', $data);
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ class BookController extends Controller
|
|||||||
|
|
||||||
$books = $this->queries->visibleForListWithCover()
|
$books = $this->queries->visibleForListWithCover()
|
||||||
->orderBy($listOptions->getSort(), $listOptions->getOrder())
|
->orderBy($listOptions->getSort(), $listOptions->getOrder())
|
||||||
->paginate(intval(setting('sorting-books-per-page', '18')));
|
->paginate(setting()->getInteger('lists-page-count-books', 18, 1, 1000));
|
||||||
$recents = $this->isSignedIn() ? $this->queries->recentlyViewedForCurrentUser()->take(4)->get() : false;
|
$recents = $this->isSignedIn() ? $this->queries->recentlyViewedForCurrentUser()->take(4)->get() : false;
|
||||||
$popular = $this->queries->popularForList()->take(4)->get();
|
$popular = $this->queries->popularForList()->take(4)->get();
|
||||||
$new = $this->queries->visibleForList()->orderBy('created_at', 'desc')->take(4)->get();
|
$new = $this->queries->visibleForList()->orderBy('created_at', 'desc')->take(4)->get();
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ class BookshelfController extends Controller
|
|||||||
|
|
||||||
$shelves = $this->queries->visibleForListWithCover()
|
$shelves = $this->queries->visibleForListWithCover()
|
||||||
->orderBy($listOptions->getSort(), $listOptions->getOrder())
|
->orderBy($listOptions->getSort(), $listOptions->getOrder())
|
||||||
->paginate(intval(setting('sorting-shelves-per-page', '18')));
|
->paginate(setting()->getInteger('lists-page-count-shelves', 18, 1, 1000));
|
||||||
$recents = $this->isSignedIn() ? $this->queries->recentlyViewedForCurrentUser()->get() : false;
|
$recents = $this->isSignedIn() ? $this->queries->recentlyViewedForCurrentUser()->get() : false;
|
||||||
$popular = $this->queries->popularForList()->get();
|
$popular = $this->queries->popularForList()->get();
|
||||||
$new = $this->queries->visibleForList()
|
$new = $this->queries->visibleForList()
|
||||||
|
|||||||
@@ -25,10 +25,11 @@ class SearchController extends Controller
|
|||||||
$searchOpts = SearchOptions::fromRequest($request);
|
$searchOpts = SearchOptions::fromRequest($request);
|
||||||
$fullSearchString = $searchOpts->toString();
|
$fullSearchString = $searchOpts->toString();
|
||||||
$page = intval($request->get('page', '0')) ?: 1;
|
$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);
|
$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->setPath('/search');
|
||||||
$paginator->appends($request->except('page'));
|
$paginator->appends($request->except('page'));
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ class AppSettingsStore
|
|||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function storeFromUpdateRequest(Request $request, string $category)
|
public function storeFromUpdateRequest(Request $request, string $category): void
|
||||||
{
|
{
|
||||||
$this->storeSimpleSettings($request);
|
$this->storeSimpleSettings($request);
|
||||||
if ($category === 'customization') {
|
if ($category === 'customization') {
|
||||||
@@ -76,7 +76,7 @@ class AppSettingsStore
|
|||||||
protected function storeSimpleSettings(Request $request): void
|
protected function storeSimpleSettings(Request $request): void
|
||||||
{
|
{
|
||||||
foreach ($request->all() as $name => $value) {
|
foreach ($request->all() as $name => $value) {
|
||||||
if (strpos($name, 'setting-') !== 0) {
|
if (!str_starts_with($name, 'setting-')) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,7 +85,7 @@ class AppSettingsStore
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function destroyExistingSettingImage(string $settingKey)
|
protected function destroyExistingSettingImage(string $settingKey): void
|
||||||
{
|
{
|
||||||
$existingVal = setting()->get($settingKey);
|
$existingVal = setting()->get($settingKey);
|
||||||
if ($existingVal) {
|
if ($existingVal) {
|
||||||
|
|||||||
@@ -28,6 +28,21 @@ class SettingService
|
|||||||
return $this->formatValue($value, $default);
|
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.
|
* Get a value from the session instead of the main store option.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -75,12 +75,8 @@ return [
|
|||||||
'reg_confirm_restrict_domain_placeholder' => 'No restriction set',
|
'reg_confirm_restrict_domain_placeholder' => 'No restriction set',
|
||||||
|
|
||||||
// Sorting Settings
|
// Sorting Settings
|
||||||
'sorting' => 'Sorting',
|
'sorting' => 'Lists & Sorting',
|
||||||
'sorting_shelves_per_page' => 'Shelves Per Page',
|
'sorting_book_default' => 'Default Book Sort Rule',
|
||||||
'sorting_shelves_per_page_desc' => 'Sets the number of shelves shown per page in shelf listings.',
|
|
||||||
'sorting_books_per_page' => 'Books Per Page',
|
|
||||||
'sorting_books_per_page_desc' => 'Sets the number of books shown per page in book listings.',
|
|
||||||
'sorting_book_default' => 'Default Book Sort',
|
|
||||||
'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_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' => 'Sort Rules',
|
||||||
'sorting_rules_desc' => 'These are predefined sorting operations which can be applied to content in the system.',
|
'sorting_rules_desc' => 'These are predefined sorting operations which can be applied to content in the system.',
|
||||||
@@ -107,6 +103,8 @@ return [
|
|||||||
'sort_rule_op_updated_date' => 'Updated Date',
|
'sort_rule_op_updated_date' => 'Updated Date',
|
||||||
'sort_rule_op_chapters_first' => 'Chapters First',
|
'sort_rule_op_chapters_first' => 'Chapters First',
|
||||||
'sort_rule_op_chapters_last' => 'Chapters Last',
|
'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
|
// Maintenance settings
|
||||||
'maint' => 'Maintenance',
|
'maint' => 'Maintenance',
|
||||||
|
|||||||
@@ -348,6 +348,10 @@ input[type=color] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.small-inputs input {
|
||||||
|
width: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
.simple-code-input {
|
.simple-code-input {
|
||||||
background-color: #F8F8F8;
|
background-color: #F8F8F8;
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
|
|||||||
@@ -6,7 +6,8 @@
|
|||||||
@if($readonly ?? false) readonly="readonly" @endif
|
@if($readonly ?? false) readonly="readonly" @endif
|
||||||
@if($min ?? false) min="{{ $min }}" @endif
|
@if($min ?? false) min="{{ $min }}" @endif
|
||||||
@if($max ?? false) max="{{ $max }}" @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))
|
@if($errors->has($name))
|
||||||
<div class="text-neg text-small">{{ $errors->first($name) }}</div>
|
<div class="text-neg text-small">{{ $errors->first($name) }}</div>
|
||||||
@endif
|
@endif
|
||||||
|
|||||||
@@ -10,41 +10,30 @@
|
|||||||
{{ csrf_field() }}
|
{{ csrf_field() }}
|
||||||
<input type="hidden" name="section" value="sorting">
|
<input type="hidden" name="section" value="sorting">
|
||||||
|
|
||||||
<div class="grid half gap-xl items-center">
|
|
||||||
<div>
|
|
||||||
<label for="setting-sorting-shelves-per-page"
|
|
||||||
class="setting-list-label">{{ trans('settings.sorting_shelves_per_page') }}</label>
|
|
||||||
<p class="small">{{ trans('settings.sorting_shelves_per_page_desc') }}</p>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<input type="number"
|
|
||||||
id="setting-sorting-shelves-per-page"
|
|
||||||
name="setting-sorting-shelves-per-page"
|
|
||||||
value="{{ intval(setting('sorting-shelves-per-page', 18)) }}"
|
|
||||||
min="1"
|
|
||||||
step="1"
|
|
||||||
class="@if($errors->has('setting-sorting-shelves-per-page')) neg @endif">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="grid half gap-xl items-center">
|
|
||||||
<div>
|
|
||||||
<label for="setting-sorting-books-per-page"
|
|
||||||
class="setting-list-label">{{ trans('settings.sorting_books_per_page') }}</label>
|
|
||||||
<p class="small">{{ trans('settings.sorting_books_per_page_desc') }}</p>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<input type="number"
|
|
||||||
id="setting-sorting-books-per-page"
|
|
||||||
name="setting-sorting-books-per-page"
|
|
||||||
value="{{ intval(setting('sorting-books-per-page', 18)) }}"
|
|
||||||
min="1"
|
|
||||||
step="1"
|
|
||||||
class="@if($errors->has('setting-sorting-books-per-page')) neg @endif">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="setting-list">
|
<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 class="grid half gap-xl items-center">
|
||||||
<div>
|
<div>
|
||||||
<label for="setting-sorting-book-default"
|
<label for="setting-sorting-book-default"
|
||||||
|
|||||||
81
tests/Settings/PageListLimitsTest.php
Normal file
81
tests/Settings/PageListLimitsTest.php
Normal file
@@ -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_shelf_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_shelf_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);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user