]> BookStack Code Mirror - bookstack/commitdiff
Review of #2682, Also added parent deletion link on restore
authorDan Brown <redacted>
Sat, 26 Jun 2021 11:12:11 +0000 (12:12 +0100)
committerDan Brown <redacted>
Sat, 26 Jun 2021 11:12:11 +0000 (12:12 +0100)
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
app/Entities/Models/Entity.php
app/Http/Controllers/RecycleBinController.php
resources/lang/en/settings.php
resources/views/settings/recycle-bin/index.blade.php
resources/views/settings/recycle-bin/restore.blade.php
tests/RecycleBinTest.php

index 1be0ba4c678c17139cf1aa9ca2592f2ca0f692d7..2295a5e214bbf055293386be366aed2817c688dd 100644 (file)
@@ -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, '/'));
+    }
 }
index 5618767693b34311ee6057fae86dde062a1192e0..caa25d6784257af16ddea48c65eb296c5ad634ee 100644 (file)
@@ -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;
index a644a2889ca28f1b0b2d648798050a27963696ae..3126460080b62fea60f0cad128e72de74ed6269d 100644 (file)
@@ -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,
         ]);
     }
 
index b04ea01cdcd638321b8828d72c7eca552eb0cb13..789ef9d1b29e72d661aaba9663b9972ab909d90b 100755 (executable)
@@ -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.',
 
index 54420d0a22792034facac934be31c9720069bca5..cf9808d955df243dd66b7df863bb45d9854cd996 100644 (file)
@@ -47,7 +47,7 @@
                 </tr>
                 @if(count($deletions) === 0)
                     <tr>
-                        <td colspan="4">
+                        <td colspan="5">
                             <p class="text-muted"><em>{{ trans('settings.recycle_bin_contents_empty') }}</em></p>
                         </td>
                     </tr>
@@ -95,8 +95,8 @@
                         <div component="dropdown" class="dropdown-container">
                             <button type="button" refs="dropdown@toggle" class="button outline">{{ trans('common.actions') }}</button>
                             <ul refs="dropdown@menu" class="dropdown-menu">
-                                <li><a class="block" href="{{ url('/settings/recycle-bin/'.$deletion->id.'/restore') }}">{{ trans('settings.recycle_bin_restore') }}</a></li>
-                                <li><a class="block" href="{{ url('/settings/recycle-bin/'.$deletion->id.'/destroy') }}">{{ trans('settings.recycle_bin_permanently_delete') }}</a></li>
+                                <li><a class="block" href="{{ $deletion->getUrl('/restore') }}">{{ trans('settings.recycle_bin_restore') }}</a></li>
+                                <li><a class="block" href="{{ $deletion->getUrl('/destroy') }}">{{ trans('settings.recycle_bin_permanently_delete') }}</a></li>
                             </ul>
                         </div>
                     </td>
index c888aa8e54db0e1963d8d31c36555842cb21bd6f..8decd13f68a1889d965c291c24e686be9b847340 100644 (file)
@@ -10,7 +10,7 @@
         <div class="card content-wrap auto-height">
             <h2 class="list-heading">{{ trans('settings.recycle_bin_restore') }}</h2>
             <p class="text-muted">{{ trans('settings.recycle_bin_restore_confirm') }}</p>
-            <form action="{{ url('/settings/recycle-bin/' . $deletion->id . '/restore') }}" method="post">
+            <form action="{{ $deletion->getUrl('/restore') }}" method="post">
                 {!! csrf_field() !!}
                 <a href="{{ url('/settings/recycle-bin') }}" class="button outline">{{ trans('common.cancel') }}</a>
                 <button type="submit" class="button">{{ trans('settings.recycle_bin_restore') }}</button>
             @if($deletion->deletable instanceof \BookStack\Entities\Models\Entity)
                 <hr class="mt-m">
                 <h5>{{ trans('settings.recycle_bin_restore_list') }}</h5>
-                @if($deletion->deletable->getParent() && $deletion->deletable->getParent()->trashed())
-                    <p class="text-neg">{{ trans('settings.recycle_bin_restore_deleted_parent') }}</p>
-                @endif
+                <div class="flex-container-row mb-s items-center">
+                    @if($deletion->deletable->getParent() && $deletion->deletable->getParent()->trashed())
+                        <div class="text-neg flex">{{ trans('settings.recycle_bin_restore_deleted_parent') }}</div>
+                    @endif
+                    @if($parentDeletion)
+                        <div class="flex fit-content ml-m">
+                            <a class="button outline" href="{{ $parentDeletion->getUrl('/restore') }}">{{ trans('settings.recycle_bin_restore_parent') }}</a>
+                        </div>
+                    @endif
+                </div>
+
                 @include('settings.recycle-bin.deletable-entity-list', ['entity' => $deletion->deletable])
             @endif
 
index 55a9571de40acbdd1896cd7e5208012f8a28719d..1cfcc0bceb49d0e9a7509f29c5cb10f07fb53ff3 100644 (file)
@@ -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