Skip to content
Open
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
6 changes: 2 additions & 4 deletions app/App/SluggableInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,9 @@
/**
* Assigned to models that can have slugs.
* Must have the below properties.
*
* @property string $slug
*/
interface SluggableInterface
{
/**
* Regenerate the slug for this model.
*/
public function refreshSlug(): string;
}
26 changes: 0 additions & 26 deletions app/Entities/Models/BookChild.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

namespace BookStack\Entities\Models;

use BookStack\References\ReferenceUpdater;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

/**
Expand All @@ -22,29 +21,4 @@ public function book(): BelongsTo
{
return $this->belongsTo(Book::class)->withTrashed();
}

/**
* Change the book that this entity belongs to.
*/
public function changeBook(int $newBookId): self
{
$oldUrl = $this->getUrl();
$this->book_id = $newBookId;
$this->unsetRelation('book');
$this->refreshSlug();
$this->save();

if ($oldUrl !== $this->getUrl()) {
app()->make(ReferenceUpdater::class)->updateEntityReferences($this, $oldUrl);
}

// Update all child pages if a chapter
if ($this instanceof Chapter) {
foreach ($this->pages()->withTrashed()->get() as $page) {
$page->changeBook($newBookId);
}
}

return $this;
}
}
11 changes: 0 additions & 11 deletions app/Entities/Models/Entity.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
use BookStack\Activity\Models\Watch;
use BookStack\App\Model;
use BookStack\App\SluggableInterface;
use BookStack\Entities\Tools\SlugGenerator;
use BookStack\Permissions\JointPermissionBuilder;
use BookStack\Permissions\Models\EntityPermission;
use BookStack\Permissions\Models\JointPermission;
Expand Down Expand Up @@ -405,16 +404,6 @@ public function indexForSearch(): void
app()->make(SearchIndex::class)->indexEntity(clone $this);
}

/**
* {@inheritdoc}
*/
public function refreshSlug(): string
{
$this->slug = app()->make(SlugGenerator::class)->generate($this, $this->name);

return $this->slug;
}

/**
* {@inheritdoc}
*/
Expand Down
20 changes: 18 additions & 2 deletions app/Entities/Repos/BaseRepo.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
use BookStack\Entities\Models\HasDescriptionInterface;
use BookStack\Entities\Models\Entity;
use BookStack\Entities\Queries\PageQueries;
use BookStack\Entities\Tools\SlugGenerator;
use BookStack\Entities\Tools\SlugHistory;
use BookStack\Exceptions\ImageUploadException;
use BookStack\References\ReferenceStore;
use BookStack\References\ReferenceUpdater;
Expand All @@ -25,6 +27,8 @@ public function __construct(
protected ReferenceStore $referenceStore,
protected PageQueries $pageQueries,
protected BookSorter $bookSorter,
protected SlugGenerator $slugGenerator,
protected SlugHistory $slugHistory,
) {
}

Expand All @@ -43,7 +47,7 @@ public function create(Entity $entity, array $input): Entity
'updated_by' => user()->id,
'owned_by' => user()->id,
]);
$entity->refreshSlug();
$this->refreshSlug($entity);

if ($entity instanceof HasDescriptionInterface) {
$this->updateDescription($entity, $input);
Expand Down Expand Up @@ -78,7 +82,7 @@ public function update(Entity $entity, array $input): Entity
$entity->updated_by = user()->id;

if ($entity->isDirty('name') || empty($entity->slug)) {
$entity->refreshSlug();
$this->refreshSlug($entity);
}

if ($entity instanceof HasDescriptionInterface) {
Expand Down Expand Up @@ -155,4 +159,16 @@ protected function updateDescription(Entity $entity, array $input): void
$entity->descriptionInfo()->set('', $input['description']);
}
}

/**
* Refresh the slug for the given entity.
*/
public function refreshSlug(Entity $entity): void
{
if ($entity->id) {
$this->slugHistory->recordForEntity($entity);
}

$this->slugGenerator->regenerateForEntity($entity);
}
}
4 changes: 3 additions & 1 deletion app/Entities/Repos/ChapterRepo.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use BookStack\Entities\Models\Chapter;
use BookStack\Entities\Queries\EntityQueries;
use BookStack\Entities\Tools\BookContents;
use BookStack\Entities\Tools\ParentChanger;
use BookStack\Entities\Tools\TrashCan;
use BookStack\Exceptions\MoveOperationException;
use BookStack\Exceptions\PermissionsException;
Expand All @@ -21,6 +22,7 @@ public function __construct(
protected BaseRepo $baseRepo,
protected EntityQueries $entityQueries,
protected TrashCan $trashCan,
protected ParentChanger $parentChanger,
) {
}

Expand Down Expand Up @@ -97,7 +99,7 @@ public function move(Chapter $chapter, string $parentIdentifier): Book
}

return (new DatabaseTransaction(function () use ($chapter, $parent) {
$chapter = $chapter->changeBook($parent->id);
$this->parentChanger->changeBook($chapter, $parent->id);
$chapter->rebuildPermissions();
Activity::add(ActivityType::CHAPTER_MOVE, $chapter);

Expand Down
6 changes: 4 additions & 2 deletions app/Entities/Repos/PageRepo.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use BookStack\Entities\Tools\BookContents;
use BookStack\Entities\Tools\PageContent;
use BookStack\Entities\Tools\PageEditorType;
use BookStack\Entities\Tools\ParentChanger;
use BookStack\Entities\Tools\TrashCan;
use BookStack\Exceptions\MoveOperationException;
use BookStack\Exceptions\PermissionsException;
Expand All @@ -31,6 +32,7 @@ public function __construct(
protected ReferenceStore $referenceStore,
protected ReferenceUpdater $referenceUpdater,
protected TrashCan $trashCan,
protected ParentChanger $parentChanger,
) {
}

Expand Down Expand Up @@ -242,7 +244,7 @@ public function restoreRevision(Page $page, int $revisionId): Page
}

$page->updated_by = user()->id;
$page->refreshSlug();
$this->baseRepo->refreshSlug($page);
$page->save();
$page->indexForSearch();
$this->referenceStore->updateForEntity($page);
Expand Down Expand Up @@ -284,7 +286,7 @@ public function move(Page $page, string $parentIdentifier): Entity
return (new DatabaseTransaction(function () use ($page, $parent) {
$page->chapter_id = ($parent instanceof Chapter) ? $parent->id : null;
$newBookId = ($parent instanceof Chapter) ? $parent->book->id : $parent->id;
$page = $page->changeBook($newBookId);
$this->parentChanger->changeBook($page, $newBookId);
$page->rebuildPermissions();

Activity::add(ActivityType::PAGE_MOVE, $page);
Expand Down
5 changes: 3 additions & 2 deletions app/Entities/Tools/HierarchyTransformer.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ public function __construct(
protected BookRepo $bookRepo,
protected BookshelfRepo $shelfRepo,
protected Cloner $cloner,
protected TrashCan $trashCan
protected TrashCan $trashCan,
protected ParentChanger $parentChanger,
) {
}

Expand All @@ -35,7 +36,7 @@ public function transformChapterToBook(Chapter $chapter): Book
foreach ($chapter->pages as $page) {
$page->chapter_id = 0;
$page->save();
$page->changeBook($book->id);
$this->parentChanger->changeBook($page, $book->id);
}

$this->trashCan->destroyEntity($chapter);
Expand Down
40 changes: 40 additions & 0 deletions app/Entities/Tools/ParentChanger.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

namespace BookStack\Entities\Tools;

use BookStack\Entities\Models\BookChild;
use BookStack\Entities\Models\Chapter;
use BookStack\References\ReferenceUpdater;

class ParentChanger
{
public function __construct(
protected SlugGenerator $slugGenerator,
protected ReferenceUpdater $referenceUpdater
) {
}

/**
* Change the parent book of a chapter or page.
*/
public function changeBook(BookChild $child, int $newBookId): void
{
$oldUrl = $child->getUrl();

$child->book_id = $newBookId;
$child->unsetRelation('book');
$this->slugGenerator->regenerateForEntity($child);
$child->save();

if ($oldUrl !== $child->getUrl()) {
$this->referenceUpdater->updateEntityReferences($child, $oldUrl);
}

// Update all child pages if a chapter
if ($child instanceof Chapter) {
foreach ($child->pages()->withTrashed()->get() as $page) {
$this->changeBook($page, $newBookId);
}
}
}
}
24 changes: 23 additions & 1 deletion app/Entities/Tools/SlugGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@
use BookStack\App\Model;
use BookStack\App\SluggableInterface;
use BookStack\Entities\Models\BookChild;
use BookStack\Entities\Models\Entity;
use BookStack\Users\Models\User;
use Illuminate\Support\Str;

class SlugGenerator
{
/**
* Generate a fresh slug for the given entity.
* Generate a fresh slug for the given item.
* The slug will be generated so that it doesn't conflict within the same parent item.
*/
public function generate(SluggableInterface&Model $model, string $slugSource): string
Expand All @@ -23,6 +25,26 @@ public function generate(SluggableInterface&Model $model, string $slugSource): s
return $slug;
}

/**
* Regenerate the slug for the given entity.
*/
public function regenerateForEntity(Entity $entity): string
{
$entity->slug = $this->generate($entity, $entity->name);

return $entity->slug;
}

/**
* Regenerate the slug for a user.
*/
public function regenerateForUser(User $user): string
{
$user->slug = $this->generate($user, $user->name);

return $user->slug;
}

/**
* Format a name as a URL slug.
*/
Expand Down
40 changes: 40 additions & 0 deletions app/Entities/Tools/SlugHistory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

namespace BookStack\Entities\Tools;

use BookStack\Entities\Models\Entity;
use Illuminate\Support\Facades\DB;

class SlugHistory
{
/**
* Record the current slugs for the given entity.
*/
public function recordForEntity(Entity $entity): void
{
$latest = $this->getLatestEntryForEntity($entity);
if ($latest && $latest->slug === $entity->slug && $latest->parent_slug === $entity->getParent()?->slug) {
return;
}

$info = [
'sluggable_type' => $entity->getMorphClass(),
'sluggable_id' => $entity->id,
'slug' => $entity->slug,
'parent_slug' => $entity->getParent()?->slug,
'created_at' => now(),
'updated_at' => now(),
];

DB::table('slug_history')->insert($info);
}

protected function getLatestEntryForEntity(Entity $entity): \stdClass|null
{
return DB::table('slug_history')
->where('sluggable_type', '=', $entity->getMorphClass())
->where('sluggable_id', '=', $entity->id)
->orderBy('created_at', 'desc')
->first();
}
}
4 changes: 3 additions & 1 deletion app/Sorting/BookSorter.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@
use BookStack\Entities\Models\Entity;
use BookStack\Entities\Models\Page;
use BookStack\Entities\Queries\EntityQueries;
use BookStack\Entities\Tools\ParentChanger;
use BookStack\Permissions\Permission;

class BookSorter
{
public function __construct(
protected EntityQueries $queries,
protected ParentChanger $parentChanger,
) {
}

Expand Down Expand Up @@ -155,7 +157,7 @@ protected function applySortUpdates(BookSortMapItem $sortMapItem, array $modelMa

// Action the required changes
if ($bookChanged) {
$model = $model->changeBook($newBook->id);
$this->parentChanger->changeBook($model, $newBook->id);
}

if ($model instanceof Page && $chapterChanged) {
Expand Down
11 changes: 0 additions & 11 deletions app/Users/Models/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
use BookStack\Api\ApiToken;
use BookStack\App\Model;
use BookStack\App\SluggableInterface;
use BookStack\Entities\Tools\SlugGenerator;
use BookStack\Permissions\Permission;
use BookStack\Translation\LocaleDefinition;
use BookStack\Translation\LocaleManager;
Expand Down Expand Up @@ -358,14 +357,4 @@ public function logDescriptor(): string
{
return "({$this->id}) {$this->name}";
}

/**
* {@inheritdoc}
*/
public function refreshSlug(): string
{
$this->slug = app()->make(SlugGenerator::class)->generate($this, $this->name);

return $this->slug;
}
}
Loading
Loading