diff --git a/app/Http/Controllers/Api/BookApiController.php b/app/Http/Controllers/Api/BookApiController.php index 73cac6318..939300697 100644 --- a/app/Http/Controllers/Api/BookApiController.php +++ b/app/Http/Controllers/Api/BookApiController.php @@ -30,13 +30,15 @@ class BookApiController extends ApiController /** * Create a new book in the system. + * The cover image of a book can be set by sending a file via an 'image' property within a 'multipart/form-data' request. + * If the 'image' property is null then the book cover image will be removed. * * @throws ValidationException */ public function create(Request $request) { $this->checkPermission('book-create-all'); - $requestData = $this->validate($request, $this->rules['create']); + $requestData = $this->validate($request, $this->rules()['create']); $book = $this->bookRepo->create($requestData); @@ -55,6 +57,8 @@ class BookApiController extends ApiController /** * Update the details of a single book. + * The cover image of a book can be set by sending a file via an 'image' property within a 'multipart/form-data' request. + * If the 'image' property is null then the book cover image will be removed. * * @throws ValidationException */ @@ -63,7 +67,7 @@ class BookApiController extends ApiController $book = Book::visible()->findOrFail($id); $this->checkOwnablePermission('book-update', $book); - $requestData = $this->validate($request, $this->rules['update']); + $requestData = $this->validate($request, $this->rules()['update']); $book = $this->bookRepo->update($book, $requestData); return response()->json($book); diff --git a/app/Http/Controllers/Api/BookshelfApiController.php b/app/Http/Controllers/Api/BookshelfApiController.php index 400dff977..620df1638 100644 --- a/app/Http/Controllers/Api/BookshelfApiController.php +++ b/app/Http/Controllers/Api/BookshelfApiController.php @@ -37,13 +37,15 @@ class BookshelfApiController extends ApiController * Create a new shelf in the system. * An array of books IDs can be provided in the request. These * will be added to the shelf in the same order as provided. + * The cover image of a shelf can be set by sending a file via an 'image' property within a 'multipart/form-data' request. + * If the 'image' property is null then the shelf cover image will be removed. * * @throws ValidationException */ public function create(Request $request) { $this->checkPermission('bookshelf-create-all'); - $requestData = $this->validate($request, $this->rules['create']); + $requestData = $this->validate($request, $this->rules()['create']); $bookIds = $request->get('books', []); $shelf = $this->bookshelfRepo->create($requestData, $bookIds); @@ -71,6 +73,8 @@ class BookshelfApiController extends ApiController * An array of books IDs can be provided in the request. These * will be added to the shelf in the same order as provided and overwrite * any existing book assignments. + * The cover image of a shelf can be set by sending a file via an 'image' property within a 'multipart/form-data' request. + * If the 'image' property is null then the shelf cover image will be removed. * * @throws ValidationException */ @@ -79,7 +83,7 @@ class BookshelfApiController extends ApiController $shelf = Bookshelf::visible()->findOrFail($id); $this->checkOwnablePermission('bookshelf-update', $shelf); - $requestData = $this->validate($request, $this->rules['update']); + $requestData = $this->validate($request, $this->rules()['update']); $bookIds = $request->get('books', null); $shelf = $this->bookshelfRepo->update($shelf, $requestData, $bookIds); diff --git a/tests/Api/BooksApiTest.php b/tests/Api/BooksApiTest.php index 9fe8f8215..fb3244e55 100644 --- a/tests/Api/BooksApiTest.php +++ b/tests/Api/BooksApiTest.php @@ -6,10 +6,12 @@ use BookStack\Entities\Models\Book; use Carbon\Carbon; use Illuminate\Support\Facades\DB; use Tests\TestCase; +use Tests\Uploads\UsesImages; class BooksApiTest extends TestCase { use TestsApi; + use UsesImages; protected string $baseEndpoint = '/api/books'; @@ -118,6 +120,42 @@ class BooksApiTest extends TestCase $this->assertGreaterThan(Carbon::now()->subDay()->unix(), $book->updated_at->unix()); } + public function test_update_cover_image_control() + { + $this->actingAsApiEditor(); + /** @var Book $book */ + $book = Book::visible()->first(); + $this->assertNull($book->cover); + $file = $this->getTestImage('image.png'); + + // Ensure cover image can be set via API + $resp = $this->call('PUT', $this->baseEndpoint . "/{$book->id}", [ + 'name' => 'My updated API book with image', + ], [], ['image' => $file]); + $book->refresh(); + + $resp->assertStatus(200); + $this->assertNotNull($book->cover); + + // Ensure further updates without image do not clear cover image + $resp = $this->put($this->baseEndpoint . "/{$book->id}", [ + 'name' => 'My updated book again' + ]); + $book->refresh(); + + $resp->assertStatus(200); + $this->assertNotNull($book->cover); + + // Ensure update with null image property clears image + $resp = $this->put($this->baseEndpoint . "/{$book->id}", [ + 'image' => null, + ]); + $book->refresh(); + + $resp->assertStatus(200); + $this->assertNull($book->cover); + } + public function test_delete_endpoint() { $this->actingAsApiEditor(); diff --git a/tests/Api/ShelvesApiTest.php b/tests/Api/ShelvesApiTest.php index 034d4bc28..95b165402 100644 --- a/tests/Api/ShelvesApiTest.php +++ b/tests/Api/ShelvesApiTest.php @@ -7,10 +7,12 @@ use BookStack\Entities\Models\Bookshelf; use Carbon\Carbon; use Illuminate\Support\Facades\DB; use Tests\TestCase; +use Tests\Uploads\UsesImages; class ShelvesApiTest extends TestCase { use TestsApi; + use UsesImages; protected string $baseEndpoint = '/api/shelves'; @@ -146,6 +148,42 @@ class ShelvesApiTest extends TestCase $this->assertTrue($shelf->books()->count() === 0); } + public function test_update_cover_image_control() + { + $this->actingAsApiEditor(); + /** @var Book $shelf */ + $shelf = Bookshelf::visible()->first(); + $this->assertNull($shelf->cover); + $file = $this->getTestImage('image.png'); + + // Ensure cover image can be set via API + $resp = $this->call('PUT', $this->baseEndpoint . "/{$shelf->id}", [ + 'name' => 'My updated API shelf with image', + ], [], ['image' => $file]); + $shelf->refresh(); + + $resp->assertStatus(200); + $this->assertNotNull($shelf->cover); + + // Ensure further updates without image do not clear cover image + $resp = $this->put($this->baseEndpoint . "/{$shelf->id}", [ + 'name' => 'My updated shelf again' + ]); + $shelf->refresh(); + + $resp->assertStatus(200); + $this->assertNotNull($shelf->cover); + + // Ensure update with null image property clears image + $resp = $this->put($this->baseEndpoint . "/{$shelf->id}", [ + 'image' => null, + ]); + $shelf->refresh(); + + $resp->assertStatus(200); + $this->assertNull($shelf->cover); + } + public function test_delete_endpoint() { $this->actingAsApiEditor();