Skip to content

Commit a7de251

Browse files
authored
Merge pull request #5457 from BookStackApp/sort_sets
Sort rules
2 parents 103a8a8 + 7bd8931 commit a7de251

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+2019
-549
lines changed

app/Activity/ActivityType.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ class ActivityType
7171
const IMPORT_RUN = 'import_run';
7272
const IMPORT_DELETE = 'import_delete';
7373

74+
const SORT_RULE_CREATE = 'sort_rule_create';
75+
const SORT_RULE_UPDATE = 'sort_rule_update';
76+
const SORT_RULE_DELETE = 'sort_rule_delete';
77+
7478
/**
7579
* Get all the possible values.
7680
*/

app/Activity/Controllers/AuditLogController.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use BookStack\Activity\ActivityType;
66
use BookStack\Activity\Models\Activity;
77
use BookStack\Http\Controller;
8+
use BookStack\Sorting\SortUrl;
89
use BookStack\Util\SimpleListOptions;
910
use Illuminate\Http\Request;
1011

@@ -65,6 +66,7 @@ public function index(Request $request)
6566
'filters' => $filters,
6667
'listOptions' => $listOptions,
6768
'activityTypes' => $types,
69+
'filterSortUrl' => new SortUrl('settings/audit', array_filter($request->except('page')))
6870
]);
6971
}
7072
}

app/App/helpers.php

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -96,35 +96,3 @@ function theme_path(string $path = ''): ?string
9696

9797
return base_path('themes/' . $theme . ($path ? DIRECTORY_SEPARATOR . $path : $path));
9898
}
99-
100-
/**
101-
* Generate a URL with multiple parameters for sorting purposes.
102-
* Works out the logic to set the correct sorting direction
103-
* Discards empty parameters and allows overriding.
104-
*/
105-
function sortUrl(string $path, array $data, array $overrideData = []): string
106-
{
107-
$queryStringSections = [];
108-
$queryData = array_merge($data, $overrideData);
109-
110-
// Change sorting direction is already sorted on current attribute
111-
if (isset($overrideData['sort']) && $overrideData['sort'] === $data['sort']) {
112-
$queryData['order'] = ($data['order'] === 'asc') ? 'desc' : 'asc';
113-
} elseif (isset($overrideData['sort'])) {
114-
$queryData['order'] = 'asc';
115-
}
116-
117-
foreach ($queryData as $name => $value) {
118-
$trimmedVal = trim($value);
119-
if ($trimmedVal === '') {
120-
continue;
121-
}
122-
$queryStringSections[] = urlencode($name) . '=' . urlencode($trimmedVal);
123-
}
124-
125-
if (count($queryStringSections) === 0) {
126-
return url($path);
127-
}
128-
129-
return url($path . '?' . implode('&', $queryStringSections));
130-
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
<?php
2+
3+
namespace BookStack\Console\Commands;
4+
5+
use BookStack\Entities\Models\Book;
6+
use BookStack\Sorting\BookSorter;
7+
use BookStack\Sorting\SortRule;
8+
use Illuminate\Console\Command;
9+
10+
class AssignSortRuleCommand extends Command
11+
{
12+
/**
13+
* The name and signature of the console command.
14+
*
15+
* @var string
16+
*/
17+
protected $signature = 'bookstack:assign-sort-rule
18+
{sort-rule=0: ID of the sort rule to apply}
19+
{--all-books : Apply to all books in the system}
20+
{--books-without-sort : Apply to only books without a sort rule already assigned}
21+
{--books-with-sort= : Apply to only books with the sort rule of given id}';
22+
23+
/**
24+
* The console command description.
25+
*
26+
* @var string
27+
*/
28+
protected $description = 'Assign a sort rule to content in the system';
29+
30+
/**
31+
* Execute the console command.
32+
*/
33+
public function handle(BookSorter $sorter): int
34+
{
35+
$sortRuleId = intval($this->argument('sort-rule')) ?? 0;
36+
if ($sortRuleId === 0) {
37+
return $this->listSortRules();
38+
}
39+
40+
$rule = SortRule::query()->find($sortRuleId);
41+
if ($this->option('all-books')) {
42+
$query = Book::query();
43+
} else if ($this->option('books-without-sort')) {
44+
$query = Book::query()->whereNull('sort_rule_id');
45+
} else if ($this->option('books-with-sort')) {
46+
$sortId = intval($this->option('books-with-sort')) ?: 0;
47+
if (!$sortId) {
48+
$this->error("Provided --books-with-sort option value is invalid");
49+
return 1;
50+
}
51+
$query = Book::query()->where('sort_rule_id', $sortId);
52+
} else {
53+
$this->error("No option provided to specify target. Run with the -h option to see all available options.");
54+
return 1;
55+
}
56+
57+
if (!$rule) {
58+
$this->error("Sort rule of provided id {$sortRuleId} not found!");
59+
return 1;
60+
}
61+
62+
$count = $query->clone()->count();
63+
$this->warn("This will apply sort rule [{$rule->id}: {$rule->name}] to {$count} book(s) and run the sort on each.");
64+
$confirmed = $this->confirm("Are you sure you want to continue?");
65+
66+
if (!$confirmed) {
67+
return 1;
68+
}
69+
70+
$processed = 0;
71+
$query->chunkById(10, function ($books) use ($rule, $sorter, $count, &$processed) {
72+
$max = min($count, ($processed + 10));
73+
$this->info("Applying to {$processed}-{$max} of {$count} books");
74+
foreach ($books as $book) {
75+
$book->sort_rule_id = $rule->id;
76+
$book->save();
77+
$sorter->runBookAutoSort($book);
78+
}
79+
$processed = $max;
80+
});
81+
82+
$this->info("Sort applied to {$processed} book(s)!");
83+
84+
return 0;
85+
}
86+
87+
protected function listSortRules(): int
88+
{
89+
90+
$rules = SortRule::query()->orderBy('id', 'asc')->get();
91+
$this->error("Sort rule ID required!");
92+
$this->warn("\nAvailable sort rules:");
93+
foreach ($rules as $rule) {
94+
$this->info("{$rule->id}: {$rule->name}");
95+
}
96+
97+
return 1;
98+
}
99+
}

app/Entities/Models/Book.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace BookStack\Entities\Models;
44

5+
use BookStack\Sorting\SortRule;
56
use BookStack\Uploads\Image;
67
use Exception;
78
use Illuminate\Database\Eloquent\Factories\HasFactory;
@@ -16,12 +17,14 @@
1617
* @property string $description
1718
* @property int $image_id
1819
* @property ?int $default_template_id
20+
* @property ?int $sort_rule_id
1921
* @property Image|null $cover
2022
* @property \Illuminate\Database\Eloquent\Collection $chapters
2123
* @property \Illuminate\Database\Eloquent\Collection $pages
2224
* @property \Illuminate\Database\Eloquent\Collection $directPages
2325
* @property \Illuminate\Database\Eloquent\Collection $shelves
2426
* @property ?Page $defaultTemplate
27+
* @property ?SortRule $sortRule
2528
*/
2629
class Book extends Entity implements HasCoverImage
2730
{
@@ -82,6 +85,14 @@ public function defaultTemplate(): BelongsTo
8285
return $this->belongsTo(Page::class, 'default_template_id');
8386
}
8487

88+
/**
89+
* Get the sort set assigned to this book, if existing.
90+
*/
91+
public function sortRule(): BelongsTo
92+
{
93+
return $this->belongsTo(SortRule::class);
94+
}
95+
8596
/**
8697
* Get all pages within this book.
8798
*/

app/Entities/Repos/BaseRepo.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use BookStack\Activity\TagRepo;
66
use BookStack\Entities\Models\Book;
7+
use BookStack\Entities\Models\BookChild;
78
use BookStack\Entities\Models\Chapter;
89
use BookStack\Entities\Models\Entity;
910
use BookStack\Entities\Models\HasCoverImage;
@@ -12,6 +13,7 @@
1213
use BookStack\Exceptions\ImageUploadException;
1314
use BookStack\References\ReferenceStore;
1415
use BookStack\References\ReferenceUpdater;
16+
use BookStack\Sorting\BookSorter;
1517
use BookStack\Uploads\ImageRepo;
1618
use BookStack\Util\HtmlDescriptionFilter;
1719
use Illuminate\Http\UploadedFile;
@@ -24,6 +26,7 @@ public function __construct(
2426
protected ReferenceUpdater $referenceUpdater,
2527
protected ReferenceStore $referenceStore,
2628
protected PageQueries $pageQueries,
29+
protected BookSorter $bookSorter,
2730
) {
2831
}
2932

@@ -134,6 +137,18 @@ public function updateDefaultTemplate(Book|Chapter $entity, int $templateId): vo
134137
$entity->save();
135138
}
136139

140+
/**
141+
* Sort the parent of the given entity, if any auto sort actions are set for it.
142+
* Typical ran during create/update/insert events.
143+
*/
144+
public function sortParent(Entity $entity): void
145+
{
146+
if ($entity instanceof BookChild) {
147+
$book = $entity->book;
148+
$this->bookSorter->runBookAutoSort($book);
149+
}
150+
}
151+
137152
protected function updateDescription(Entity $entity, array $input): void
138153
{
139154
if (!in_array(HasHtmlDescription::class, class_uses($entity))) {

app/Entities/Repos/BookRepo.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use BookStack\Entities\Tools\TrashCan;
99
use BookStack\Exceptions\ImageUploadException;
1010
use BookStack\Facades\Activity;
11+
use BookStack\Sorting\SortRule;
1112
use BookStack\Uploads\ImageRepo;
1213
use Exception;
1314
use Illuminate\Http\UploadedFile;
@@ -33,6 +34,12 @@ public function create(array $input): Book
3334
$this->baseRepo->updateDefaultTemplate($book, intval($input['default_template_id'] ?? null));
3435
Activity::add(ActivityType::BOOK_CREATE, $book);
3536

37+
$defaultBookSortSetting = intval(setting('sorting-book-default', '0'));
38+
if ($defaultBookSortSetting && SortRule::query()->find($defaultBookSortSetting)) {
39+
$book->sort_rule_id = $defaultBookSortSetting;
40+
$book->save();
41+
}
42+
3643
return $book;
3744
}
3845

app/Entities/Repos/ChapterRepo.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ public function create(array $input, Book $parentBook): Chapter
3434
$this->baseRepo->updateDefaultTemplate($chapter, intval($input['default_template_id'] ?? null));
3535
Activity::add(ActivityType::CHAPTER_CREATE, $chapter);
3636

37+
$this->baseRepo->sortParent($chapter);
38+
3739
return $chapter;
3840
}
3941

@@ -50,6 +52,8 @@ public function update(Chapter $chapter, array $input): Chapter
5052

5153
Activity::add(ActivityType::CHAPTER_UPDATE, $chapter);
5254

55+
$this->baseRepo->sortParent($chapter);
56+
5357
return $chapter;
5458
}
5559

@@ -88,6 +92,8 @@ public function move(Chapter $chapter, string $parentIdentifier): Book
8892
$chapter->rebuildPermissions();
8993
Activity::add(ActivityType::CHAPTER_MOVE, $chapter);
9094

95+
$this->baseRepo->sortParent($chapter);
96+
9197
return $parent;
9298
}
9399
}

app/Entities/Repos/PageRepo.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ public function publishDraft(Page $draft, array $input): Page
8383
$draft->refresh();
8484

8585
Activity::add(ActivityType::PAGE_CREATE, $draft);
86+
$this->baseRepo->sortParent($draft);
8687

8788
return $draft;
8889
}
@@ -128,6 +129,7 @@ public function update(Page $page, array $input): Page
128129
}
129130

130131
Activity::add(ActivityType::PAGE_UPDATE, $page);
132+
$this->baseRepo->sortParent($page);
131133

132134
return $page;
133135
}
@@ -243,6 +245,8 @@ public function restoreRevision(Page $page, int $revisionId): Page
243245
Activity::add(ActivityType::PAGE_RESTORE, $page);
244246
Activity::add(ActivityType::REVISION_RESTORE, $revision);
245247

248+
$this->baseRepo->sortParent($page);
249+
246250
return $page;
247251
}
248252

@@ -272,6 +276,8 @@ public function move(Page $page, string $parentIdentifier): Entity
272276

273277
Activity::add(ActivityType::PAGE_MOVE, $page);
274278

279+
$this->baseRepo->sortParent($page);
280+
275281
return $parent;
276282
}
277283

0 commit comments

Comments
 (0)