1
0
mirror of https://github.com/BookStackApp/BookStack.git synced 2024-11-23 11:22:33 +01:00

Rolled out reference link updating logic usage

Added test to cover updating of content on reference url change
This commit is contained in:
Dan Brown 2022-08-21 18:05:19 +01:00
parent 0dbf08453f
commit b86ee6d252
No known key found for this signature in database
GPG Key ID: 46D9F943C24A2EF9
5 changed files with 101 additions and 15 deletions

View File

@ -2,6 +2,7 @@
namespace BookStack\Entities\Models; namespace BookStack\Entities\Models;
use BookStack\References\ReferenceUpdater;
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsTo;
@ -57,9 +58,15 @@ abstract class BookChild extends Entity
*/ */
public function changeBook(int $newBookId): Entity public function changeBook(int $newBookId): Entity
{ {
$oldUrl = $this->getUrl();
$this->book_id = $newBookId; $this->book_id = $newBookId;
$this->refreshSlug(); $this->refreshSlug();
$this->save(); $this->save();
if ($oldUrl !== $this->getUrl()) {
app()->make(ReferenceUpdater::class)->updateEntityPageReferences($this, $oldUrl);
}
$this->refresh(); $this->refresh();
// Update all child pages if a chapter // Update all child pages if a chapter

View File

@ -6,6 +6,7 @@ use BookStack\Actions\TagRepo;
use BookStack\Entities\Models\Entity; use BookStack\Entities\Models\Entity;
use BookStack\Entities\Models\HasCoverImage; use BookStack\Entities\Models\HasCoverImage;
use BookStack\Exceptions\ImageUploadException; use BookStack\Exceptions\ImageUploadException;
use BookStack\References\ReferenceUpdater;
use BookStack\Uploads\ImageRepo; use BookStack\Uploads\ImageRepo;
use Illuminate\Http\UploadedFile; use Illuminate\Http\UploadedFile;
@ -13,11 +14,13 @@ class BaseRepo
{ {
protected TagRepo $tagRepo; protected TagRepo $tagRepo;
protected ImageRepo $imageRepo; protected ImageRepo $imageRepo;
protected ReferenceUpdater $referenceUpdater;
public function __construct(TagRepo $tagRepo, ImageRepo $imageRepo) public function __construct(TagRepo $tagRepo, ImageRepo $imageRepo, ReferenceUpdater $referenceUpdater)
{ {
$this->tagRepo = $tagRepo; $this->tagRepo = $tagRepo;
$this->imageRepo = $imageRepo; $this->imageRepo = $imageRepo;
$this->referenceUpdater = $referenceUpdater;
} }
/** /**
@ -48,6 +51,8 @@ class BaseRepo
*/ */
public function update(Entity $entity, array $input) public function update(Entity $entity, array $input)
{ {
$oldUrl = $entity->getUrl();
$entity->fill($input); $entity->fill($input);
$entity->updated_by = user()->id; $entity->updated_by = user()->id;
@ -64,6 +69,10 @@ class BaseRepo
$entity->rebuildPermissions(); $entity->rebuildPermissions();
$entity->indexForSearch(); $entity->indexForSearch();
if ($oldUrl !== $entity->getUrl()) {
$this->referenceUpdater->updateEntityPageReferences($entity, $oldUrl);
}
} }
/** /**

View File

@ -17,6 +17,7 @@ use BookStack\Exceptions\NotFoundException;
use BookStack\Exceptions\PermissionsException; use BookStack\Exceptions\PermissionsException;
use BookStack\Facades\Activity; use BookStack\Facades\Activity;
use BookStack\References\ReferenceStore; use BookStack\References\ReferenceStore;
use BookStack\References\ReferenceUpdater;
use Exception; use Exception;
use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Pagination\LengthAwarePaginator;
@ -24,16 +25,23 @@ class PageRepo
{ {
protected BaseRepo $baseRepo; protected BaseRepo $baseRepo;
protected RevisionRepo $revisionRepo; protected RevisionRepo $revisionRepo;
protected ReferenceStore $references; protected ReferenceStore $referenceStore;
protected ReferenceUpdater $referenceUpdater;
/** /**
* PageRepo constructor. * PageRepo constructor.
*/ */
public function __construct(BaseRepo $baseRepo, RevisionRepo $revisionRepo, ReferenceStore $references) public function __construct(
BaseRepo $baseRepo,
RevisionRepo $revisionRepo,
ReferenceStore $referenceStore,
ReferenceUpdater $referenceUpdater
)
{ {
$this->baseRepo = $baseRepo; $this->baseRepo = $baseRepo;
$this->revisionRepo = $revisionRepo; $this->revisionRepo = $revisionRepo;
$this->references = $references; $this->referenceStore = $referenceStore;
$this->referenceUpdater = $referenceUpdater;
} }
/** /**
@ -158,12 +166,10 @@ class PageRepo
$draft->draft = false; $draft->draft = false;
$draft->revision_count = 1; $draft->revision_count = 1;
$draft->priority = $this->getNewPriority($draft); $draft->priority = $this->getNewPriority($draft);
$draft->refreshSlug();
$draft->save(); $draft->save();
$this->revisionRepo->storeNewForPage($draft, trans('entities.pages_initial_revision')); $this->revisionRepo->storeNewForPage($draft, trans('entities.pages_initial_revision'));
$draft->indexForSearch(); $this->referenceStore->updateForPage($draft);
$this->references->updateForPage($draft);
$draft->refresh(); $draft->refresh();
Activity::add(ActivityType::PAGE_CREATE, $draft); Activity::add(ActivityType::PAGE_CREATE, $draft);
@ -183,7 +189,7 @@ class PageRepo
$this->updateTemplateStatusAndContentFromInput($page, $input); $this->updateTemplateStatusAndContentFromInput($page, $input);
$this->baseRepo->update($page, $input); $this->baseRepo->update($page, $input);
$this->references->updateForPage($page); $this->referenceStore->updateForPage($page);
// Update with new details // Update with new details
$page->revision_count++; $page->revision_count++;
@ -283,6 +289,7 @@ class PageRepo
*/ */
public function restoreRevision(Page $page, int $revisionId): Page public function restoreRevision(Page $page, int $revisionId): Page
{ {
$oldUrl = $page->getUrl();
$page->revision_count++; $page->revision_count++;
/** @var PageRevision $revision */ /** @var PageRevision $revision */
@ -301,11 +308,15 @@ class PageRepo
$page->refreshSlug(); $page->refreshSlug();
$page->save(); $page->save();
$page->indexForSearch(); $page->indexForSearch();
$this->references->updateForPage($page); $this->referenceStore->updateForPage($page);
$summary = trans('entities.pages_revision_restored_from', ['id' => strval($revisionId), 'summary' => $revision->summary]); $summary = trans('entities.pages_revision_restored_from', ['id' => strval($revisionId), 'summary' => $revision->summary]);
$this->revisionRepo->storeNewForPage($page, $summary); $this->revisionRepo->storeNewForPage($page, $summary);
if ($oldUrl !== $page->getUrl()) {
$this->referenceUpdater->updateEntityPageReferences($page, $oldUrl);
}
Activity::add(ActivityType::PAGE_RESTORE, $page); Activity::add(ActivityType::PAGE_RESTORE, $page);
Activity::add(ActivityType::REVISION_RESTORE, $revision); Activity::add(ActivityType::REVISION_RESTORE, $revision);

View File

@ -8,7 +8,7 @@ use BookStack\Entities\Repos\RevisionRepo;
use DOMDocument; use DOMDocument;
use DOMXPath; use DOMXPath;
class CrossLinkReplacer class ReferenceUpdater
{ {
protected ReferenceFetcher $referenceFetcher; protected ReferenceFetcher $referenceFetcher;
protected RevisionRepo $revisionRepo; protected RevisionRepo $revisionRepo;
@ -53,10 +53,10 @@ class CrossLinkReplacer
return $markdown; return $markdown;
} }
$commonLinkRegex = '/(\[.*?\]\()' . preg_quote($oldLink) . '(.*?\))/i'; $commonLinkRegex = '/(\[.*?\]\()' . preg_quote($oldLink, '/') . '(.*?\))/i';
$markdown = preg_replace($commonLinkRegex, '$1' . $newLink . '$2', $markdown); $markdown = preg_replace($commonLinkRegex, '$1' . $newLink . '$2', $markdown);
$referenceLinkRegex = '/(\[.*?\]:\s?)' . preg_quote($oldLink) . '(.*?)($|\s)/i'; $referenceLinkRegex = '/(\[.*?\]:\s?)' . preg_quote($oldLink, '/') . '(.*?)($|\s)/i';
$markdown = preg_replace($referenceLinkRegex, '$1' . $newLink . '$2$3', $markdown); $markdown = preg_replace($referenceLinkRegex, '$1' . $newLink . '$2$3', $markdown);
return $markdown; return $markdown;

View File

@ -2,6 +2,7 @@
namespace Tests\References; namespace Tests\References;
use BookStack\Entities\Models\Book;
use BookStack\Entities\Models\Page; use BookStack\Entities\Models\Page;
use BookStack\Entities\Repos\PageRepo; use BookStack\Entities\Repos\PageRepo;
use BookStack\Entities\Tools\TrashCan; use BookStack\Entities\Tools\TrashCan;
@ -116,6 +117,64 @@ class ReferencesTest extends TestCase
->assertSee('There are no tracked references'); ->assertSee('There are no tracked references');
} }
public function test_pages_leading_to_entity_updated_on_url_change()
{
/** @var Page $pageA */
/** @var Page $pageB */
/** @var Book $book */
$pageA = Page::query()->first();
$pageB = Page::query()->where('id', '!=', $pageA->id)->first();
$book = Book::query()->first();
foreach ([$pageA, $pageB] as $page) {
$page->html = '<a href="' . $book->getUrl() . '">Link</a>';
$page->save();
$this->createReference($page, $book);
}
$this->asEditor()->put($book->getUrl(), [
'name' => 'my updated book slugaroo',
]);
foreach ([$pageA, $pageB] as $page) {
$page->refresh();
$this->assertStringContainsString('href="http://localhost/books/my-updated-book-slugaroo"', $page->html);
$this->assertDatabaseHas('page_revisions', [
'page_id' => $page->id,
'summary' => 'System auto-update of internal links'
]);
}
}
public function test_markdown_links_leading_to_entity_updated_on_url_change()
{
/** @var Page $page */
/** @var Book $book */
$page = Page::query()->first();
$book = Book::query()->first();
$bookUrl = $book->getUrl();
$markdown = '
[An awesome link](' . $bookUrl . ')
[An awesome link with query & hash](' . $bookUrl . '?test=yes#cats)
[An awesome link with path](' . $bookUrl . '/an/extra/trail)
[An awesome link with title](' . $bookUrl . ' "title")
[ref]: ' . $bookUrl . '?test=yes#dogs
[ref_without_space]:' . $bookUrl . '
[ref_with_title]: ' . $bookUrl . ' "title"';
$page->markdown = $markdown;
$page->save();
$this->createReference($page, $book);
$this->asEditor()->put($book->getUrl(), [
'name' => 'my updated book slugadoo',
]);
$page->refresh();
$expected = str_replace($bookUrl, 'http://localhost/books/my-updated-book-slugadoo', $markdown);
$this->assertEquals($expected, $page->markdown);
}
protected function createReference(Model $from, Model $to) protected function createReference(Model $from, Model $to)
{ {
(new Reference())->forceFill([ (new Reference())->forceFill([