use BookStack\Uploads\AttachmentService;
use BookStack\Uploads\ImageService;
use Exception;
+use Illuminate\Support\Carbon;
class TrashCan
{
* Remove a bookshelf from the system.
* @throws Exception
*/
- public function destroyShelf(Bookshelf $shelf): int
+ protected function destroyShelf(Bookshelf $shelf): int
{
$this->destroyCommonRelations($shelf);
$shelf->forceDelete();
* Destroys any child chapters and pages.
* @throws Exception
*/
- public function destroyBook(Book $book): int
+ protected function destroyBook(Book $book): int
{
$count = 0;
$pages = $book->pages()->withTrashed()->get();
* Destroys all pages within.
* @throws Exception
*/
- public function destroyChapter(Chapter $chapter): int
+ protected function destroyChapter(Chapter $chapter): int
{
$count = 0;
$pages = $chapter->pages()->withTrashed()->get();
* Remove a page from the system.
* @throws Exception
*/
- public function destroyPage(Page $page): int
+ protected function destroyPage(Page $page): int
{
$this->destroyCommonRelations($page);
/**
* Destroy all items that have pending deletions.
+ * @throws Exception
*/
- public function destroyFromAllDeletions(): int
+ public function empty(): int
{
$deletions = Deletion::all();
$deleteCount = 0;
foreach ($deletions as $deletion) {
- // For each one we load in the relation since it may have already
- // been deleted as part of another deletion in this loop.
- $entity = $deletion->deletable()->first();
- if ($entity) {
- $count = $this->destroyEntity($deletion->deletable);
- $deleteCount += $count;
- }
- $deletion->delete();
+ $deleteCount += $this->destroyFromDeletion($deletion);
}
return $deleteCount;
}
+ /**
+ * Destroy an element from the given deletion model.
+ * @throws Exception
+ */
+ public function destroyFromDeletion(Deletion $deletion): int
+ {
+ // We directly load the deletable element here just to ensure it still
+ // exists in the event it has already been destroyed during this request.
+ $entity = $deletion->deletable()->first();
+ $count = 0;
+ if ($entity) {
+ $count = $this->destroyEntity($deletion->deletable);
+ }
+ $deletion->delete();
+ return $count;
+ }
+
+ /**
+ * Restore the content within the given deletion.
+ * @throws Exception
+ */
+ public function restoreFromDeletion(Deletion $deletion): int
+ {
+ $shouldRestore = true;
+ $restoreCount = 0;
+ $parent = $deletion->deletable->getParent();
+
+ if ($parent && $parent->trashed()) {
+ $shouldRestore = false;
+ }
+
+ if ($shouldRestore) {
+ $restoreCount = $this->restoreEntity($deletion->deletable);
+ }
+
+ $deletion->delete();
+ return $restoreCount;
+ }
+
+ /**
+ * Automatically clear old content from the recycle bin
+ * depending on the configured lifetime.
+ * Returns the total number of deleted elements.
+ * @throws Exception
+ */
+ public function autoClearOld(): int
+ {
+ $lifetime = intval(config('app.recycle_bin_lifetime'));
+ if ($lifetime < 0) {
+ return 0;
+ }
+
+ $clearBeforeDate = Carbon::now()->addSeconds(10)->subDays($lifetime);
+ $deleteCount = 0;
+
+ $deletionsToRemove = Deletion::query()->where('created_at', '<', $clearBeforeDate)->get();
+ foreach ($deletionsToRemove as $deletion) {
+ $deleteCount += $this->destroyFromDeletion($deletion);
+ }
+
+ return $deleteCount;
+ }
+
+ /**
+ * Restore an entity so it is essentially un-deleted.
+ * Deletions on restored child elements will be removed during this restoration.
+ */
+ protected function restoreEntity(Entity $entity): int
+ {
+ $count = 1;
+ $entity->restore();
+
+ $restoreAction = function ($entity) use (&$count) {
+ if ($entity->deletions_count > 0) {
+ $entity->deletions()->delete();
+ }
+
+ $entity->restore();
+ $count++;
+ };
+
+ if ($entity->isA('chapter') || $entity->isA('book')) {
+ $entity->pages()->withTrashed()->withCount('deletions')->get()->each($restoreAction);
+ }
+
+ if ($entity->isA('book')) {
+ $entity->chapters()->withTrashed()->withCount('deletions')->get()->each($restoreAction);
+ }
+
+ return $count;
+ }
+
/**
* Destroy the given entity.
*/