1
0
mirror of https://github.com/BookStackApp/BookStack.git synced 2025-08-09 10:22:51 +03:00

Started implementation of recycle bin functionality

This commit is contained in:
Dan Brown
2020-09-27 23:24:33 +01:00
parent d48ac0a37d
commit 691027a522
13 changed files with 266 additions and 73 deletions

View File

@@ -0,0 +1,41 @@
<?php namespace BookStack\Entities;
use BookStack\Auth\User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\MorphTo;
class DeleteRecord extends Model
{
/**
* Get the related deletable record.
*/
public function deletable(): MorphTo
{
return $this->morphTo();
}
/**
* The the user that performed the deletion.
*/
public function deletedBy(): BelongsTo
{
return $this->belongsTo(User::class);
}
/**
* Create a new deletion record for the provided entity.
*/
public static function createForEntity(Entity $entity): DeleteRecord
{
$record = (new self())->forceFill([
'deleted_by' => user()->id,
'deletable_type' => $entity->getMorphClass(),
'deletable_id' => $entity->id,
]);
$record->save();
return $record;
}
}

View File

@@ -12,6 +12,7 @@ use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Database\Eloquent\SoftDeletes;
/**
* Class Entity
@@ -36,6 +37,7 @@ use Illuminate\Database\Eloquent\Relations\MorphMany;
*/
class Entity extends Ownable
{
use SoftDeletes;
/**
* @var string - Name of property where the main text content is found
@@ -193,13 +195,20 @@ class Entity extends Ownable
/**
* Get the entity jointPermissions this is connected to.
* @return MorphMany
*/
public function jointPermissions()
public function jointPermissions(): MorphMany
{
return $this->morphMany(JointPermission::class, 'entity');
}
/**
* Get the related delete records for this entity.
*/
public function deleteRecords(): MorphMany
{
return $this->morphMany(DeleteRecord::class, 'deletable');
}
/**
* Check if this instance or class is a certain type of entity.
* Examples of $type are 'page', 'book', 'chapter'

View File

@@ -3,6 +3,7 @@
use BookStack\Entities\Book;
use BookStack\Entities\Bookshelf;
use BookStack\Entities\Chapter;
use BookStack\Entities\DeleteRecord;
use BookStack\Entities\Entity;
use BookStack\Entities\HasCoverImage;
use BookStack\Entities\Page;
@@ -11,46 +12,67 @@ use BookStack\Facades\Activity;
use BookStack\Uploads\AttachmentService;
use BookStack\Uploads\ImageService;
use Exception;
use Illuminate\Contracts\Container\BindingResolutionException;
class TrashCan
{
/**
* Remove a bookshelf from the system.
* @throws Exception
* Send a shelf to the recycle bin.
*/
public function destroyShelf(Bookshelf $shelf)
public function softDestroyShelf(Bookshelf $shelf)
{
$this->destroyCommonRelations($shelf);
DeleteRecord::createForEntity($shelf);
$shelf->delete();
}
/**
* Remove a book from the system.
* @throws NotifyException
* @throws BindingResolutionException
* Send a book to the recycle bin.
* @throws Exception
*/
public function destroyBook(Book $book)
public function softDestroyBook(Book $book)
{
DeleteRecord::createForEntity($book);
foreach ($book->pages as $page) {
$this->destroyPage($page);
$this->softDestroyPage($page, false);
}
foreach ($book->chapters as $chapter) {
$this->destroyChapter($chapter);
$this->softDestroyChapter($chapter, false);
}
$this->destroyCommonRelations($book);
$book->delete();
}
/**
* Remove a page from the system.
* @throws NotifyException
* Send a chapter to the recycle bin.
* @throws Exception
*/
public function destroyPage(Page $page)
public function softDestroyChapter(Chapter $chapter, bool $recordDelete = true)
{
if ($recordDelete) {
DeleteRecord::createForEntity($chapter);
}
if (count($chapter->pages) > 0) {
foreach ($chapter->pages as $page) {
$this->softDestroyPage($page, false);
}
}
$chapter->delete();
}
/**
* Send a page to the recycle bin.
* @throws Exception
*/
public function softDestroyPage(Page $page, bool $recordDelete = true)
{
if ($recordDelete) {
DeleteRecord::createForEntity($page);
}
// Check if set as custom homepage & remove setting if not used or throw error if active
$customHome = setting('app-homepage', '0:');
if (intval($page->id) === intval(explode(':', $customHome)[0])) {
@@ -60,6 +82,64 @@ class TrashCan
setting()->remove('app-homepage');
}
$page->delete();
}
/**
* Remove a bookshelf from the system.
* @throws Exception
*/
public function destroyShelf(Bookshelf $shelf)
{
$this->destroyCommonRelations($shelf);
$shelf->forceDelete();
}
/**
* Remove a book from the system.
* Destroys any child chapters and pages.
* @throws Exception
*/
public function destroyBook(Book $book)
{
$pages = $book->pages()->withTrashed()->get();
foreach ($pages as $page) {
$this->destroyPage($page);
}
$chapters = $book->chapters()->withTrashed()->get();
foreach ($chapters as $chapter) {
$this->destroyChapter($chapter);
}
$this->destroyCommonRelations($book);
$book->forceDelete();
}
/**
* Remove a chapter from the system.
* Destroys all pages within.
* @throws Exception
*/
public function destroyChapter(Chapter $chapter)
{
$pages = $chapter->pages()->withTrashed()->get();
if (count($pages)) {
foreach ($pages as $page) {
$this->destroyPage($page);
}
}
$this->destroyCommonRelations($chapter);
$chapter->forceDelete();
}
/**
* Remove a page from the system.
* @throws Exception
*/
public function destroyPage(Page $page)
{
$this->destroyCommonRelations($page);
// Delete Attached Files
@@ -68,24 +148,7 @@ class TrashCan
$attachmentService->deleteFile($attachment);
}
$page->delete();
}
/**
* Remove a chapter from the system.
* @throws Exception
*/
public function destroyChapter(Chapter $chapter)
{
if (count($chapter->pages) > 0) {
foreach ($chapter->pages as $page) {
$page->chapter_id = 0;
$page->save();
}
}
$this->destroyCommonRelations($chapter);
$chapter->delete();
$page->forceDelete();
}
/**
@@ -100,6 +163,7 @@ class TrashCan
$entity->comments()->delete();
$entity->jointPermissions()->delete();
$entity->searchTerms()->delete();
$entity->deleteRecords()->delete();
if ($entity instanceof HasCoverImage && $entity->cover) {
$imageService = app()->make(ImageService::class);

View File

@@ -123,12 +123,11 @@ class BookRepo
/**
* Remove a book from the system.
* @throws NotifyException
* @throws BindingResolutionException
* @throws Exception
*/
public function destroy(Book $book)
{
$trashCan = new TrashCan();
$trashCan->destroyBook($book);
$trashCan->softDestroyBook($book);
}
}

View File

@@ -174,6 +174,6 @@ class BookshelfRepo
public function destroy(Bookshelf $shelf)
{
$trashCan = new TrashCan();
$trashCan->destroyShelf($shelf);
$trashCan->softDestroyShelf($shelf);
}
}

View File

@@ -6,10 +6,7 @@ use BookStack\Entities\Managers\BookContents;
use BookStack\Entities\Managers\TrashCan;
use BookStack\Exceptions\MoveOperationException;
use BookStack\Exceptions\NotFoundException;
use BookStack\Exceptions\NotifyException;
use Exception;
use Illuminate\Contracts\Container\BindingResolutionException;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Collection;
class ChapterRepo
@@ -19,7 +16,6 @@ class ChapterRepo
/**
* ChapterRepo constructor.
* @param $baseRepo
*/
public function __construct(BaseRepo $baseRepo)
{
@@ -77,7 +73,7 @@ class ChapterRepo
public function destroy(Chapter $chapter)
{
$trashCan = new TrashCan();
$trashCan->destroyChapter($chapter);
$trashCan->softDestroyChapter($chapter);
}
/**

View File

@@ -12,6 +12,7 @@ use BookStack\Exceptions\MoveOperationException;
use BookStack\Exceptions\NotFoundException;
use BookStack\Exceptions\NotifyException;
use BookStack\Exceptions\PermissionsException;
use Exception;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
@@ -259,12 +260,12 @@ class PageRepo
/**
* Destroy a page from the system.
* @throws NotifyException
* @throws Exception
*/
public function destroy(Page $page)
{
$trashCan = new TrashCan();
$trashCan->destroyPage($page);
$trashCan->softDestroyPage($page);
}
/**

View File

@@ -287,9 +287,12 @@ class SearchService
foreach ($this->entityProvider->all() as $entityModel) {
$selectFields = ['id', 'name', $entityModel->textField];
$entityModel->newQuery()->select($selectFields)->chunk(1000, function ($entities) {
$this->indexEntities($entities);
});
$entityModel->newQuery()
->withTrashed()
->select($selectFields)
->chunk(1000, function ($entities) {
$this->indexEntities($entities);
});
}
}