]> BookStack Code Mirror - bookstack/commitdiff
Rolled out reference link updating logic usage
authorDan Brown <redacted>
Sun, 21 Aug 2022 17:05:19 +0000 (18:05 +0100)
committerDan Brown <redacted>
Sun, 21 Aug 2022 17:05:19 +0000 (18:05 +0100)
Added test to cover updating of content on reference url change

app/Entities/Models/BookChild.php
app/Entities/Repos/BaseRepo.php
app/Entities/Repos/PageRepo.php
app/References/ReferenceUpdater.php [moved from app/References/CrossLinkReplacer.php with 95% similarity]
tests/References/ReferencesTest.php

index e1ba0b6f708d75a18f5cb38dc38262e7396d61c0..3b1ac1bab74d68f1db67a0e3ede27788a6a2b1f3 100644 (file)
@@ -2,6 +2,7 @@
 
 namespace BookStack\Entities\Models;
 
+use BookStack\References\ReferenceUpdater;
 use Illuminate\Database\Eloquent\Builder;
 use Illuminate\Database\Eloquent\Relations\BelongsTo;
 
@@ -57,9 +58,15 @@ abstract class BookChild extends Entity
      */
     public function changeBook(int $newBookId): Entity
     {
+        $oldUrl = $this->getUrl();
         $this->book_id = $newBookId;
         $this->refreshSlug();
         $this->save();
+
+        if ($oldUrl !== $this->getUrl()) {
+            app()->make(ReferenceUpdater::class)->updateEntityPageReferences($this, $oldUrl);
+        }
+
         $this->refresh();
 
         // Update all child pages if a chapter
index 39b901383529effbb3b453d24a75336b24f9a666..cfde7fe1c578eb6876b1e77237c86e74bb584493 100644 (file)
@@ -6,6 +6,7 @@ use BookStack\Actions\TagRepo;
 use BookStack\Entities\Models\Entity;
 use BookStack\Entities\Models\HasCoverImage;
 use BookStack\Exceptions\ImageUploadException;
+use BookStack\References\ReferenceUpdater;
 use BookStack\Uploads\ImageRepo;
 use Illuminate\Http\UploadedFile;
 
@@ -13,11 +14,13 @@ class BaseRepo
 {
     protected TagRepo $tagRepo;
     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->imageRepo = $imageRepo;
+        $this->referenceUpdater = $referenceUpdater;
     }
 
     /**
@@ -48,6 +51,8 @@ class BaseRepo
      */
     public function update(Entity $entity, array $input)
     {
+        $oldUrl = $entity->getUrl();
+
         $entity->fill($input);
         $entity->updated_by = user()->id;
 
@@ -64,6 +69,10 @@ class BaseRepo
 
         $entity->rebuildPermissions();
         $entity->indexForSearch();
+
+        if ($oldUrl !== $entity->getUrl()) {
+            $this->referenceUpdater->updateEntityPageReferences($entity, $oldUrl);
+        }
     }
 
     /**
index e491d6070942510eb3088d6840beaaad35a2da12..c80cbdb149e60b9d1f758038a94e793f62f4104e 100644 (file)
@@ -17,6 +17,7 @@ use BookStack\Exceptions\NotFoundException;
 use BookStack\Exceptions\PermissionsException;
 use BookStack\Facades\Activity;
 use BookStack\References\ReferenceStore;
+use BookStack\References\ReferenceUpdater;
 use Exception;
 use Illuminate\Pagination\LengthAwarePaginator;
 
@@ -24,16 +25,23 @@ class PageRepo
 {
     protected BaseRepo $baseRepo;
     protected RevisionRepo $revisionRepo;
-    protected ReferenceStore $references;
+    protected ReferenceStore $referenceStore;
+    protected ReferenceUpdater $referenceUpdater;
 
     /**
      * 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->revisionRepo = $revisionRepo;
-        $this->references = $references;
+        $this->referenceStore = $referenceStore;
+        $this->referenceUpdater = $referenceUpdater;
     }
 
     /**
@@ -127,11 +135,11 @@ class PageRepo
     public function getNewDraftPage(Entity $parent)
     {
         $page = (new Page())->forceFill([
-            'name'       => trans('entities.pages_initial_name'),
+            'name' => trans('entities.pages_initial_name'),
             'created_by' => user()->id,
-            'owned_by'   => user()->id,
+            'owned_by' => user()->id,
             'updated_by' => user()->id,
-            'draft'      => true,
+            'draft' => true,
         ]);
 
         if ($parent instanceof Chapter) {
@@ -158,12 +166,10 @@ class PageRepo
         $draft->draft = false;
         $draft->revision_count = 1;
         $draft->priority = $this->getNewPriority($draft);
-        $draft->refreshSlug();
         $draft->save();
 
         $this->revisionRepo->storeNewForPage($draft, trans('entities.pages_initial_revision'));
-        $draft->indexForSearch();
-        $this->references->updateForPage($draft);
+        $this->referenceStore->updateForPage($draft);
         $draft->refresh();
 
         Activity::add(ActivityType::PAGE_CREATE, $draft);
@@ -183,7 +189,7 @@ class PageRepo
 
         $this->updateTemplateStatusAndContentFromInput($page, $input);
         $this->baseRepo->update($page, $input);
-        $this->references->updateForPage($page);
+        $this->referenceStore->updateForPage($page);
 
         // Update with new details
         $page->revision_count++;
@@ -283,6 +289,7 @@ class PageRepo
      */
     public function restoreRevision(Page $page, int $revisionId): Page
     {
+        $oldUrl = $page->getUrl();
         $page->revision_count++;
 
         /** @var PageRevision $revision */
@@ -301,11 +308,15 @@ class PageRepo
         $page->refreshSlug();
         $page->save();
         $page->indexForSearch();
-        $this->references->updateForPage($page);
+        $this->referenceStore->updateForPage($page);
 
         $summary = trans('entities.pages_revision_restored_from', ['id' => strval($revisionId), 'summary' => $revision->summary]);
         $this->revisionRepo->storeNewForPage($page, $summary);
 
+        if ($oldUrl !== $page->getUrl()) {
+            $this->referenceUpdater->updateEntityPageReferences($page, $oldUrl);
+        }
+
         Activity::add(ActivityType::PAGE_RESTORE, $page);
         Activity::add(ActivityType::REVISION_RESTORE, $revision);
 
similarity index 95%
rename from app/References/CrossLinkReplacer.php
rename to app/References/ReferenceUpdater.php
index 2df87fc83109702ca8570c0440b1264bbdc06617..15619bc31c87d512a77ac4735e64c2ff3db9e514 100644 (file)
@@ -8,7 +8,7 @@ use BookStack\Entities\Repos\RevisionRepo;
 use DOMDocument;
 use DOMXPath;
 
-class CrossLinkReplacer
+class ReferenceUpdater
 {
     protected ReferenceFetcher $referenceFetcher;
     protected RevisionRepo $revisionRepo;
@@ -53,10 +53,10 @@ class CrossLinkReplacer
             return $markdown;
         }
 
-        $commonLinkRegex = '/(\[.*?\]\()' . preg_quote($oldLink) . '(.*?\))/i';
+        $commonLinkRegex = '/(\[.*?\]\()' . preg_quote($oldLink, '/') . '(.*?\))/i';
         $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);
 
         return $markdown;
index 9ae226bb71dd17a6c910833249fe5a67091e0deb..82cd1668075218c08283dbe7190de742a00d17a7 100644 (file)
@@ -2,6 +2,7 @@
 
 namespace Tests\References;
 
+use BookStack\Entities\Models\Book;
 use BookStack\Entities\Models\Page;
 use BookStack\Entities\Repos\PageRepo;
 use BookStack\Entities\Tools\TrashCan;
@@ -116,6 +117,64 @@ class ReferencesTest extends TestCase
             ->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)
     {
         (new Reference())->forceFill([
Morty Proxy This is a proxified and sanitized view of the page, visit original site.