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:
parent
0dbf08453f
commit
b86ee6d252
@ -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
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -127,11 +135,11 @@ class PageRepo
|
|||||||
public function getNewDraftPage(Entity $parent)
|
public function getNewDraftPage(Entity $parent)
|
||||||
{
|
{
|
||||||
$page = (new Page())->forceFill([
|
$page = (new Page())->forceFill([
|
||||||
'name' => trans('entities.pages_initial_name'),
|
'name' => trans('entities.pages_initial_name'),
|
||||||
'created_by' => user()->id,
|
'created_by' => user()->id,
|
||||||
'owned_by' => user()->id,
|
'owned_by' => user()->id,
|
||||||
'updated_by' => user()->id,
|
'updated_by' => user()->id,
|
||||||
'draft' => true,
|
'draft' => true,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if ($parent instanceof Chapter) {
|
if ($parent instanceof Chapter) {
|
||||||
@ -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);
|
||||||
|
|
||||||
|
@ -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;
|
@ -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([
|
||||||
|
Loading…
Reference in New Issue
Block a user