namespace BookStack\Http\Controllers\Api;
-use BookStack\Entities\Models\Book;
-use BookStack\Entities\Models\Chapter;
use BookStack\Entities\Models\Deletion;
use BookStack\Entities\Repos\DeletionRepo;
use Closure;
class RecycleBinApiController extends ApiController
{
+ protected $fieldsToExpose = [
+ 'id', 'deleted_by', 'created_at', 'updated_at', 'deletable_type', 'deletable_id',
+ ];
+
public function __construct()
{
$this->middleware(function ($request, $next) {
});
}
+ /**
+ * Get a top-level listing of the items in the recycle bin.
+ * Requires the permission to manage settings and restrictions.
+ */
public function list()
{
- return $this->apiListingResponse(Deletion::query(), [
+ return $this->apiListingResponse(Deletion::query()->with('deletable'), [
'id',
'deleted_by',
'created_at',
], [Closure::fromCallable([$this, 'listFormatter'])]);
}
+ /**
+ * Restore a single deletion from the recycle bin.
+ * You must provide the deletion id, not the id of the corresponding deleted item.
+ */
public function restore(DeletionRepo $deletionRepo, string $id)
{
$restoreCount = $deletionRepo->restore((int) $id);
return response()->json(['restore_count' => $restoreCount]);
}
+ /**
+ * Remove a single deletion from the recycle bin.
+ * Use this endpoint carefully as it will entirely remove the underlying deleted items from the system.
+ * You must provide the deletion id, not the id of the corresponding deleted item.
+ */
public function destroy(DeletionRepo $deletionRepo, string $id)
{
$deleteCount = $deletionRepo->destroy((int) $id);
protected function listFormatter(Deletion $deletion)
{
+ $deletion->makeVisible($this->fieldsToExpose);
+ $deletion->makeHidden('deletable');
+
$deletable = $deletion->deletable;
- $isBook = $deletable instanceof Book;
+ $isBook = $deletion->deletable_type === "BookStack\Book";
$parent = null;
$children = null;
if ($isBook) {
- $chapterCount = $deletable->chapters()->withTrashed()->count();
- $children['Bookstack\Chapter'] = $chapterCount;
+ $chapterCount = $deletable->chapters()->withTrashed()->count();
+ $children['BookStack\Chapter'] = $chapterCount;
}
- if ($isBook || $deletion->deletable instanceof Chapter) {
- $pageCount = $deletable->pages()->withTrashed()->count();
- $children['Bookstack\Page'] = $pageCount;
+ if ($isBook || $deletion->deletable_type === "BookStack\Chapter") {
+ $pageCount = $deletable->pages()->withTrashed()->count();
+ $children['BookStack\Page'] = $pageCount;
}
$parentEntity = $deletable->getParent();
- $parent = [];
+ $parent = null;
if ($parentEntity) {
$parent['type'] = $parentEntity->getMorphClass();
--- /dev/null
+{
+ "data": [
+ {
+ "id": 25,
+ "deleted_by": 1,
+ "created_at": "2022-04-24T07:59:34.000000Z",
+ "updated_at": "2022-04-24T07:59:34.000000Z",
+ "deletable_type": "BookStack\\Book",
+ "deletable_id": 4,
+ "parent": {
+ "type": "BookStack\\Book",
+ "id": 25
+ },
+ "children": {
+ "BookStack\\Chapter": 0,
+ "BookStack\\Page": 1
+ }
+ },
+ {
+ "id": 26,
+ "deleted_by": 1,
+ "created_at": "2022-04-24T07:59:35.000000Z",
+ "updated_at": "2022-04-24T07:59:35.000000Z",
+ "deletable_type": "BookStack\\Book",
+ "deletable_id": 3,
+ "parent": [],
+ "children": {
+ "BookStack\\Chapter": 1,
+ "BookStack\\Page": 1
+ }
+ }
+ ],
+ "total": 2
+}
\ No newline at end of file
}
}
- public function test_restrictions_manage_all_permission_neeed_for_all_endpoints()
+ public function test_restrictions_manage_all_permission_needed_for_all_endpoints()
{
$editor = $this->getEditor();
$this->giveUserPermissions($editor, ['restrictions-manage-all']);
$this->actingAs($editor);
-
+
foreach ($this->endpointMap as [$method, $uri]) {
$resp = $this->json($method, $uri);
$resp->assertStatus(403);
});
$resp->assertJson([
- 'data' => $expectedData->values()->all(),
+ 'data' => $expectedData->values()->all(),
'total' => 2,
]);
}
public function test_index_endpoint_returns_children()
{
$this->actingAsAuthorizedUser();
-
+
$book = Book::query()->whereHas('pages')->whereHas('chapters')->withCount(['pages', 'chapters'])->first();
$editor = $this->getEditor();
$this->actingAs($editor)->delete($book->getUrl());
'deletable_type' => $book->getMorphClass(),
'deletable_id' => $book->getKey(),
'children' => [
- 'Bookstack\Page' => $book->pages_count,
- 'Bookstack\Chapter' => $book->chapters_count,
+ 'BookStack\Page' => $book->pages_count,
+ 'BookStack\Chapter' => $book->chapters_count,
],
'parent' => null,
- ]
+ ],
];
$resp->assertJson([
- 'data' => $expectedData,
+ 'data' => $expectedData,
'total' => 1,
]);
}
'deletable_id' => $page->getKey(),
'parent' => [
'type' => 'BookStack\Chapter',
- 'id' => $page->chapter->getKey()
+ 'id' => $page->chapter->getKey(),
],
'children' => null,
- ]
+ ],
];
$resp->assertJson([
- 'data' => $expectedData,
- 'total' => 1
+ 'data' => $expectedData,
+ 'total' => 1,
]);
}
public function test_restore_endpoint()
{
$this->actingAsAuthorizedUser();
-
+
$page = Page::query()->first();
$editor = $this->getEditor();
$this->actingAs($editor)->delete($page->getUrl());
$deletion = Deletion::query()->orderBy('id')->first();
$this->assertDatabaseHas('pages', [
- 'id' => $page->getKey(),
- 'deleted_at' => $page->deleted_at,
+ 'id' => $page->getKey(),
+ 'deleted_at' => $page->deleted_at,
]);
$this->putJson($this->baseEndpoint . '/' . $deletion->getKey());
$this->assertDatabaseHas('pages', [
- 'id' => $page->getKey(),
- 'deleted_at' => null,
+ 'id' => $page->getKey(),
+ 'deleted_at' => null,
]);
}
public function test_destroy_endpoint()
{
$this->actingAsAuthorizedUser();
-
+
$page = Page::query()->first();
$editor = $this->getEditor();
$this->actingAs($editor)->delete($page->getUrl());
$deletion = Deletion::query()->orderBy('id')->first();
$this->assertDatabaseHas('pages', [
- 'id' => $page->getKey(),
- 'deleted_at' => $page->deleted_at,
+ 'id' => $page->getKey(),
+ 'deleted_at' => $page->deleted_at,
]);
$this->deleteJson($this->baseEndpoint . '/' . $deletion->getKey());