diff --git a/app/Entities/Controllers/BookApiController.php b/app/Entities/Controllers/BookApiController.php index 325f0583c..c47ece225 100644 --- a/app/Entities/Controllers/BookApiController.php +++ b/app/Entities/Controllers/BookApiController.php @@ -7,11 +7,14 @@ use BookStack\Entities\Models\Book; use BookStack\Entities\Models\Chapter; use BookStack\Entities\Models\Entity; use BookStack\Entities\Queries\BookQueries; +use BookStack\Entities\Queries\BookshelfQueries; use BookStack\Entities\Queries\PageQueries; use BookStack\Entities\Repos\BookRepo; use BookStack\Entities\Tools\BookContents; use BookStack\Http\ApiController; use BookStack\Permissions\Permission; +use Illuminate\Database\Eloquent\Builder; +use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Http\Request; use Illuminate\Validation\ValidationException; @@ -21,6 +24,7 @@ class BookApiController extends ApiController protected BookRepo $bookRepo, protected BookQueries $queries, protected PageQueries $pageQueries, + protected BookshelfQueries $shelfQueries, ) { } @@ -60,13 +64,20 @@ class BookApiController extends ApiController * View the details of a single book. * The response data will contain a 'content' property listing the chapter and pages directly within, in * the same structure as you'd see within the BookStack interface when viewing a book. Top-level - * contents will have a 'type' property to distinguish between pages & chapters. + * contents will have a 'type' property to distinguish between pages and chapters. */ public function read(string $id) { $book = $this->queries->findVisibleByIdOrFail(intval($id)); $book = $this->forJsonDisplay($book); - $book->load(['createdBy', 'updatedBy', 'ownedBy']); + $book->load([ + 'createdBy', + 'updatedBy', + 'ownedBy', + 'shelves' => function (BelongsToMany $query) { + $query->select(['id', 'name', 'slug'])->scopes('visible'); + } + ]); $contents = (new BookContents($book))->getTree(true, false)->all(); $contentsApiData = (new ApiEntityListFormatter($contents)) diff --git a/app/Entities/Models/Bookshelf.php b/app/Entities/Models/Bookshelf.php index 42dcc8f8f..320346512 100644 --- a/app/Entities/Models/Bookshelf.php +++ b/app/Entities/Models/Bookshelf.php @@ -19,7 +19,7 @@ class Bookshelf extends Entity implements HasDescriptionInterface, HasCoverInter public float $searchFactor = 1.2; - protected $hidden = ['image_id', 'deleted_at', 'description_html', 'priority', 'default_template_id', 'sort_rule_id', 'entity_id', 'entity_type', 'chapter_id', 'book_id']; + protected $hidden = ['pivot', 'image_id', 'deleted_at', 'description_html', 'priority', 'default_template_id', 'sort_rule_id', 'entity_id', 'entity_type', 'chapter_id', 'book_id']; protected $fillable = ['name']; /** diff --git a/tests/Api/BooksApiTest.php b/tests/Api/BooksApiTest.php index 86e10f58a..74f558f38 100644 --- a/tests/Api/BooksApiTest.php +++ b/tests/Api/BooksApiTest.php @@ -188,6 +188,37 @@ class BooksApiTest extends TestCase $resp->assertJsonMissing(['name' => $customName]); } + public function test_read_endpoint_lists_visible_shelves_the_book_is_assigned_to() + { + $this->actingAsApiEditor(); + $shelf = $this->entities->shelf(); + $otherShelf = $this->entities->shelf(); + $book = $this->entities->book(); + $book->shelves()->detach(); + + $book->shelves()->attach($shelf); + $book->shelves()->attach($otherShelf); + + $this->assertEquals(2, $book->shelves()->count()); + + $this->permissions->disableEntityInheritedPermissions($otherShelf); + + $resp = $this->getJson("{$this->baseEndpoint}/{$book->id}"); + $resp->assertOk(); + $resp->assertJsonCount(1, 'shelves'); + $resp->assertJson([ + 'shelves' => [ + [ + 'id' => $shelf->id, + 'name' => $shelf->name, + 'slug' => $shelf->slug, + ] + ] + ]); + $resp->assertJsonMissingPath('shelves.0.description'); + $resp->assertJsonMissingPath('shelves.0.pivot'); + } + public function test_update_endpoint() { $this->actingAsApiEditor();