]> BookStack Code Mirror - bookstack/blob - app/Entities/Controllers/ChapterApiController.php
Merge pull request #5721 from BookStackApp/zip_export_api_endpoints
[bookstack] / app / Entities / Controllers / ChapterApiController.php
1 <?php
2
3 namespace BookStack\Entities\Controllers;
4
5 use BookStack\Entities\Models\Chapter;
6 use BookStack\Entities\Queries\ChapterQueries;
7 use BookStack\Entities\Queries\EntityQueries;
8 use BookStack\Entities\Repos\ChapterRepo;
9 use BookStack\Exceptions\PermissionsException;
10 use BookStack\Http\ApiController;
11 use Exception;
12 use Illuminate\Http\Request;
13
14 class ChapterApiController extends ApiController
15 {
16     protected array $rules = [
17         'create' => [
18             'book_id'             => ['required', 'integer'],
19             'name'                => ['required', 'string', 'max:255'],
20             'description'         => ['string', 'max:1900'],
21             'description_html'    => ['string', 'max:2000'],
22             'tags'                => ['array'],
23             'priority'            => ['integer'],
24             'default_template_id' => ['nullable', 'integer'],
25         ],
26         'update' => [
27             'book_id'             => ['integer'],
28             'name'                => ['string', 'min:1', 'max:255'],
29             'description'         => ['string', 'max:1900'],
30             'description_html'    => ['string', 'max:2000'],
31             'tags'                => ['array'],
32             'priority'            => ['integer'],
33             'default_template_id' => ['nullable', 'integer'],
34         ],
35     ];
36
37     public function __construct(
38         protected ChapterRepo $chapterRepo,
39         protected ChapterQueries $queries,
40         protected EntityQueries $entityQueries,
41     ) {
42     }
43
44     /**
45      * Get a listing of chapters visible to the user.
46      */
47     public function list()
48     {
49         $chapters = $this->queries->visibleForList()
50             ->addSelect(['created_by', 'updated_by']);
51
52         return $this->apiListingResponse($chapters, [
53             'id', 'book_id', 'name', 'slug', 'description', 'priority',
54             'created_at', 'updated_at', 'created_by', 'updated_by', 'owned_by',
55         ]);
56     }
57
58     /**
59      * Create a new chapter in the system.
60      */
61     public function create(Request $request)
62     {
63         $requestData = $this->validate($request, $this->rules['create']);
64
65         $bookId = $request->get('book_id');
66         $book = $this->entityQueries->books->findVisibleByIdOrFail(intval($bookId));
67         $this->checkOwnablePermission('chapter-create', $book);
68
69         $chapter = $this->chapterRepo->create($requestData, $book);
70
71         return response()->json($this->forJsonDisplay($chapter));
72     }
73
74     /**
75      * View the details of a single chapter.
76      */
77     public function read(string $id)
78     {
79         $chapter = $this->queries->findVisibleByIdOrFail(intval($id));
80         $chapter = $this->forJsonDisplay($chapter);
81
82         $chapter->load(['createdBy', 'updatedBy', 'ownedBy']);
83
84         // Note: More fields than usual here, for backwards compatibility,
85         // due to previously accidentally including more fields that desired.
86         $pages = $this->entityQueries->pages->visibleForChapterList($chapter->id)
87             ->addSelect(['created_by', 'updated_by', 'revision_count', 'editor'])
88             ->get();
89         $chapter->setRelation('pages', $pages);
90
91         return response()->json($chapter);
92     }
93
94     /**
95      * Update the details of a single chapter.
96      * Providing a 'book_id' property will essentially move the chapter
97      * into that parent element if you have permissions to do so.
98      */
99     public function update(Request $request, string $id)
100     {
101         $requestData = $this->validate($request, $this->rules()['update']);
102         $chapter = $this->queries->findVisibleByIdOrFail(intval($id));
103         $this->checkOwnablePermission('chapter-update', $chapter);
104
105         if ($request->has('book_id') && $chapter->book_id !== intval($requestData['book_id'])) {
106             $this->checkOwnablePermission('chapter-delete', $chapter);
107
108             try {
109                 $this->chapterRepo->move($chapter, "book:{$requestData['book_id']}");
110             } catch (Exception $exception) {
111                 if ($exception instanceof PermissionsException) {
112                     $this->showPermissionError();
113                 }
114
115                 return $this->jsonError(trans('errors.selected_book_not_found'));
116             }
117         }
118
119         $updatedChapter = $this->chapterRepo->update($chapter, $requestData);
120
121         return response()->json($this->forJsonDisplay($updatedChapter));
122     }
123
124     /**
125      * Delete a chapter.
126      * This will typically send the chapter to the recycle bin.
127      */
128     public function delete(string $id)
129     {
130         $chapter = $this->queries->findVisibleByIdOrFail(intval($id));
131         $this->checkOwnablePermission('chapter-delete', $chapter);
132
133         $this->chapterRepo->destroy($chapter);
134
135         return response('', 204);
136     }
137
138     protected function forJsonDisplay(Chapter $chapter): Chapter
139     {
140         $chapter = clone $chapter;
141         $chapter->unsetRelations()->refresh();
142
143         $chapter->load(['tags']);
144         $chapter->makeVisible('description_html');
145         $chapter->setAttribute('description_html', $chapter->descriptionHtml());
146         $chapter->setAttribute('book_slug', $chapter->book()->first()->slug);
147
148         return $chapter;
149     }
150 }