From 3a402f6adc78fa939d898369e425088fed141544 Mon Sep 17 00:00:00 2001 From: Dan Brown Date: Sat, 26 Jun 2021 12:12:11 +0100 Subject: [PATCH] Review of #2682, Also added parent deletion link on restore On restore, added a link to the parent deletion restore if any exists on a cascading parent. Added a test to cover this case to ensure its shown. Also tweaked default empty state message on recycle bin item list to align with new column count. Also done a little existing code cleanup including a getUrl helper on the deletion items. Related to #2682 & #2594 --- app/Entities/Models/Deletion.php | 11 +++++++++++ app/Entities/Models/Entity.php | 4 ++-- app/Http/Controllers/RecycleBinController.php | 16 ++++++++++++++++ resources/lang/en/settings.php | 1 + .../views/settings/recycle-bin/index.blade.php | 6 +++--- .../settings/recycle-bin/restore.blade.php | 16 ++++++++++++---- tests/RecycleBinTest.php | 18 ++++++++++++++++++ 7 files changed, 63 insertions(+), 9 deletions(-) diff --git a/app/Entities/Models/Deletion.php b/app/Entities/Models/Deletion.php index 1be0ba4c6..2295a5e21 100644 --- a/app/Entities/Models/Deletion.php +++ b/app/Entities/Models/Deletion.php @@ -7,6 +7,9 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\MorphTo; +/** + * @property Model deletable + */ class Deletion extends Model implements Loggable { @@ -45,4 +48,12 @@ class Deletion extends Model implements Loggable $deletable = $this->deletable()->first(); return "Deletion ({$this->id}) for {$deletable->getType()} ({$deletable->id}) {$deletable->name}"; } + + /** + * Get a URL for this specific deletion. + */ + public function getUrl($path): string + { + return url("/settings/recycle-bin/{$this->id}/" . ltrim($path, '/')); + } } diff --git a/app/Entities/Models/Entity.php b/app/Entities/Models/Entity.php index 561876769..caa25d678 100644 --- a/app/Entities/Models/Entity.php +++ b/app/Entities/Models/Entity.php @@ -266,10 +266,10 @@ abstract class Entity extends Model implements Sluggable, Favouritable, Viewable */ public function getParent(): ?Entity { - if ($this->isA('page')) { + if ($this instanceof Page) { return $this->chapter_id ? $this->chapter()->withTrashed()->first() : $this->book()->withTrashed()->first(); } - if ($this->isA('chapter')) { + if ($this instanceof Chapter) { return $this->book()->withTrashed()->first(); } return null; diff --git a/app/Http/Controllers/RecycleBinController.php b/app/Http/Controllers/RecycleBinController.php index a644a2889..312646008 100644 --- a/app/Http/Controllers/RecycleBinController.php +++ b/app/Http/Controllers/RecycleBinController.php @@ -2,6 +2,7 @@ use BookStack\Actions\ActivityType; use BookStack\Entities\Models\Deletion; +use BookStack\Entities\Models\Entity; use BookStack\Entities\Tools\TrashCan; class RecycleBinController extends Controller @@ -44,8 +45,23 @@ class RecycleBinController extends Controller /** @var Deletion $deletion */ $deletion = Deletion::query()->findOrFail($id); + // Walk the parent chain to find any cascading parent deletions + $currentDeletable = $deletion->deletable; + $searching = true; + while ($searching && $currentDeletable instanceof Entity) { + $parent = $currentDeletable->getParent(); + if ($parent && $parent->trashed()) { + $currentDeletable = $parent; + } else { + $searching = false; + } + } + /** @var ?Deletion $parentDeletion */ + $parentDeletion = ($currentDeletable === $deletion->deletable) ? null : $currentDeletable->deletions()->first(); + return view('settings.recycle-bin.restore', [ 'deletion' => $deletion, + 'parentDeletion' => $parentDeletion, ]); } diff --git a/resources/lang/en/settings.php b/resources/lang/en/settings.php index b04ea01cd..789ef9d1b 100755 --- a/resources/lang/en/settings.php +++ b/resources/lang/en/settings.php @@ -105,6 +105,7 @@ return [ 'recycle_bin_restore_list' => 'Items to be Restored', 'recycle_bin_restore_confirm' => 'This action will restore the deleted item, including any child elements, to their original location. If the original location has since been deleted, and is now in the recycle bin, the parent item will also need to be restored.', 'recycle_bin_restore_deleted_parent' => 'The parent of this item has also been deleted. These will remain deleted until that parent is also restored.', + 'recycle_bin_restore_parent' => 'Restore Parent', 'recycle_bin_destroy_notification' => 'Deleted :count total items from the recycle bin.', 'recycle_bin_restore_notification' => 'Restored :count total items from the recycle bin.', diff --git a/resources/views/settings/recycle-bin/index.blade.php b/resources/views/settings/recycle-bin/index.blade.php index 54420d0a2..cf9808d95 100644 --- a/resources/views/settings/recycle-bin/index.blade.php +++ b/resources/views/settings/recycle-bin/index.blade.php @@ -47,7 +47,7 @@ @if(count($deletions) === 0) - +

{{ trans('settings.recycle_bin_contents_empty') }}

@@ -95,8 +95,8 @@ diff --git a/resources/views/settings/recycle-bin/restore.blade.php b/resources/views/settings/recycle-bin/restore.blade.php index c888aa8e5..8decd13f6 100644 --- a/resources/views/settings/recycle-bin/restore.blade.php +++ b/resources/views/settings/recycle-bin/restore.blade.php @@ -10,7 +10,7 @@

{{ trans('settings.recycle_bin_restore') }}

{{ trans('settings.recycle_bin_restore_confirm') }}

-
+ {!! csrf_field() !!} {{ trans('common.cancel') }} @@ -19,9 +19,17 @@ @if($deletion->deletable instanceof \BookStack\Entities\Models\Entity)
{{ trans('settings.recycle_bin_restore_list') }}
- @if($deletion->deletable->getParent() && $deletion->deletable->getParent()->trashed()) -

{{ trans('settings.recycle_bin_restore_deleted_parent') }}

- @endif +
+ @if($deletion->deletable->getParent() && $deletion->deletable->getParent()->trashed()) +
{{ trans('settings.recycle_bin_restore_deleted_parent') }}
+ @endif + @if($parentDeletion) + + @endif +
+ @include('settings.recycle-bin.deletable-entity-list', ['entity' => $deletion->deletable]) @endif diff --git a/tests/RecycleBinTest.php b/tests/RecycleBinTest.php index 55a9571de..1cfcc0bce 100644 --- a/tests/RecycleBinTest.php +++ b/tests/RecycleBinTest.php @@ -247,4 +247,22 @@ class RecycleBinTest extends TestCase $chapter->refresh(); $this->assertNull($chapter->deleted_at); } + + public function test_restore_page_shows_link_to_parent_restore_if_parent_also_deleted() + { + /** @var Book $book */ + $book = Book::query()->whereHas('pages')->whereHas('chapters')->with(['pages', 'chapters'])->firstOrFail(); + $chapter = $book->chapters->first(); + /** @var Page $page */ + $page = $chapter->pages->first(); + $this->asEditor()->delete($page->getUrl()); + $this->asEditor()->delete($book->getUrl()); + + $bookDeletion = $book->deletions()->first(); + $pageDeletion = $page->deletions()->first(); + + $pageRestoreView = $this->asAdmin()->get("/settings/recycle-bin/{$pageDeletion->id}/restore"); + $pageRestoreView->assertSee('The parent of this item has also been deleted.'); + $pageRestoreView->assertElementContains('a[href$="/settings/recycle-bin/' . $bookDeletion->id. '/restore"]', 'Restore Parent'); + } } \ No newline at end of file