]> BookStack Code Mirror - bookstack/blobdiff - app/Entities/Controllers/ChapterApiController.php
Opensearch: Fixed XML declaration when php short tags enabled
[bookstack] / app / Entities / Controllers / ChapterApiController.php
index ce20e6b96c38fe1c89e16d81ca934fb0b8cb8d31..430654330f36b97d5ec16dbaf22c12a56b337738 100644 (file)
@@ -2,38 +2,44 @@
 
 namespace BookStack\Entities\Controllers;
 
-use BookStack\Entities\Models\Book;
 use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Queries\ChapterQueries;
+use BookStack\Entities\Queries\EntityQueries;
 use BookStack\Entities\Repos\ChapterRepo;
+use BookStack\Exceptions\PermissionsException;
 use BookStack\Http\ApiController;
+use Exception;
 use Illuminate\Database\Eloquent\Relations\HasMany;
 use Illuminate\Http\Request;
 
 class ChapterApiController extends ApiController
 {
-    protected $chapterRepo;
-
     protected $rules = [
         'create' => [
-            'book_id'     => ['required', 'integer'],
-            'name'        => ['required', 'string', 'max:255'],
-            'description' => ['string', 'max:1000'],
-            'tags'        => ['array'],
+            'book_id'             => ['required', 'integer'],
+            'name'                => ['required', 'string', 'max:255'],
+            'description'         => ['string', 'max:1900'],
+            'description_html'    => ['string', 'max:2000'],
+            'tags'                => ['array'],
+            'priority'            => ['integer'],
+            'default_template_id' => ['nullable', 'integer'],
         ],
         'update' => [
-            'book_id'     => ['integer'],
-            'name'        => ['string', 'min:1', 'max:255'],
-            'description' => ['string', 'max:1000'],
-            'tags'        => ['array'],
+            'book_id'             => ['integer'],
+            'name'                => ['string', 'min:1', 'max:255'],
+            'description'         => ['string', 'max:1900'],
+            'description_html'    => ['string', 'max:2000'],
+            'tags'                => ['array'],
+            'priority'            => ['integer'],
+            'default_template_id' => ['nullable', 'integer'],
         ],
     ];
 
-    /**
-     * ChapterController constructor.
-     */
-    public function __construct(ChapterRepo $chapterRepo)
-    {
-        $this->chapterRepo = $chapterRepo;
+    public function __construct(
+        protected ChapterRepo $chapterRepo,
+        protected ChapterQueries $queries,
+        protected EntityQueries $entityQueries,
+    ) {
     }
 
     /**
@@ -41,7 +47,8 @@ class ChapterApiController extends ApiController
      */
     public function list()
     {
-        $chapters = Chapter::visible();
+        $chapters = $this->queries->visibleForList()
+            ->addSelect(['created_by', 'updated_by']);
 
         return $this->apiListingResponse($chapters, [
             'id', 'book_id', 'name', 'slug', 'description', 'priority',
@@ -54,15 +61,15 @@ class ChapterApiController extends ApiController
      */
     public function create(Request $request)
     {
-        $this->validate($request, $this->rules['create']);
+        $requestData = $this->validate($request, $this->rules['create']);
 
         $bookId = $request->get('book_id');
-        $book = Book::visible()->findOrFail($bookId);
+        $book = $this->entityQueries->books->findVisibleByIdOrFail(intval($bookId));
         $this->checkOwnablePermission('chapter-create', $book);
 
-        $chapter = $this->chapterRepo->create($request->all(), $book);
+        $chapter = $this->chapterRepo->create($requestData, $book);
 
-        return response()->json($chapter->load(['tags']));
+        return response()->json($this->forJsonDisplay($chapter));
     }
 
     /**
@@ -70,24 +77,49 @@ class ChapterApiController extends ApiController
      */
     public function read(string $id)
     {
-        $chapter = Chapter::visible()->with(['tags', 'createdBy', 'updatedBy', 'ownedBy', 'pages' => function (HasMany $query) {
-            $query->scopes('visible')->get(['id', 'name', 'slug']);
-        }])->findOrFail($id);
+        $chapter = $this->queries->findVisibleByIdOrFail(intval($id));
+        $chapter = $this->forJsonDisplay($chapter);
+
+        $chapter->load(['createdBy', 'updatedBy', 'ownedBy']);
+
+        // Note: More fields than usual here, for backwards compatibility,
+        // due to previously accidentally including more fields that desired.
+        $pages = $this->entityQueries->pages->visibleForChapterList($chapter->id)
+            ->addSelect(['created_by', 'updated_by', 'revision_count', 'editor'])
+            ->get();
+        $chapter->setRelation('pages', $pages);
 
         return response()->json($chapter);
     }
 
     /**
      * Update the details of a single chapter.
+     * Providing a 'book_id' property will essentially move the chapter
+     * into that parent element if you have permissions to do so.
      */
     public function update(Request $request, string $id)
     {
-        $chapter = Chapter::visible()->findOrFail($id);
+        $requestData = $this->validate($request, $this->rules()['update']);
+        $chapter = $this->queries->findVisibleByIdOrFail(intval($id));
         $this->checkOwnablePermission('chapter-update', $chapter);
 
-        $updatedChapter = $this->chapterRepo->update($chapter, $request->all());
+        if ($request->has('book_id') && $chapter->book_id !== intval($requestData['book_id'])) {
+            $this->checkOwnablePermission('chapter-delete', $chapter);
+
+            try {
+                $this->chapterRepo->move($chapter, "book:{$requestData['book_id']}");
+            } catch (Exception $exception) {
+                if ($exception instanceof PermissionsException) {
+                    $this->showPermissionError();
+                }
 
-        return response()->json($updatedChapter->load(['tags']));
+                return $this->jsonError(trans('errors.selected_book_not_found'));
+            }
+        }
+
+        $updatedChapter = $this->chapterRepo->update($chapter, $requestData);
+
+        return response()->json($this->forJsonDisplay($updatedChapter));
     }
 
     /**
@@ -96,11 +128,24 @@ class ChapterApiController extends ApiController
      */
     public function delete(string $id)
     {
-        $chapter = Chapter::visible()->findOrFail($id);
+        $chapter = $this->queries->findVisibleByIdOrFail(intval($id));
         $this->checkOwnablePermission('chapter-delete', $chapter);
 
         $this->chapterRepo->destroy($chapter);
 
         return response('', 204);
     }
+
+    protected function forJsonDisplay(Chapter $chapter): Chapter
+    {
+        $chapter = clone $chapter;
+        $chapter->unsetRelations()->refresh();
+
+        $chapter->load(['tags']);
+        $chapter->makeVisible('description_html');
+        $chapter->setAttribute('description_html', $chapter->descriptionHtml());
+        $chapter->setAttribute('book_slug', $chapter->book()->first()->slug);
+
+        return $chapter;
+    }
 }