]> BookStack Code Mirror - bookstack/commitdiff
Input WYSIWYG: Updated API to show/accept html descriptions
authorDan Brown <redacted>
Thu, 21 Dec 2023 13:23:52 +0000 (13:23 +0000)
committerDan Brown <redacted>
Thu, 21 Dec 2023 13:23:52 +0000 (13:23 +0000)
Also aligned books, shelves and chapters to return description content
and some relations (where not breaking API) in create/update responses
also so that information can be seen direct from that input in a
request.

API docs and tests not yet updated to match.

app/Entities/Controllers/BookApiController.php
app/Entities/Controllers/BookshelfApiController.php
app/Entities/Controllers/ChapterApiController.php
app/Entities/Models/Book.php
app/Entities/Models/Bookshelf.php
app/Entities/Models/Chapter.php
dev/api/responses/books-read.json
dev/api/responses/chapters-read.json
dev/api/responses/shelves-read.json

index 41ff11ddec3f2db68bee541c3a0aefa3eecb03b7..aa21aea472a8d044d718cb1d04d0e4745e0a7115 100644 (file)
@@ -45,7 +45,7 @@ class BookApiController extends ApiController
 
         $book = $this->bookRepo->create($requestData);
 
-        return response()->json($book);
+        return response()->json($this->forJsonDisplay($book));
     }
 
     /**
@@ -56,9 +56,9 @@ class BookApiController extends ApiController
      */
     public function read(string $id)
     {
-        $book = Book::visible()
-            ->with(['tags', 'cover', 'createdBy', 'updatedBy', 'ownedBy'])
-            ->findOrFail($id);
+        $book = Book::visible()->findOrFail($id);
+        $book = $this->forJsonDisplay($book);
+        $book->load(['createdBy', 'updatedBy', 'ownedBy']);
 
         $contents = (new BookContents($book))->getTree(true, false)->all();
         $contentsApiData = (new ApiEntityListFormatter($contents))
@@ -89,7 +89,7 @@ class BookApiController extends ApiController
         $requestData = $this->validate($request, $this->rules()['update']);
         $book = $this->bookRepo->update($book, $requestData);
 
-        return response()->json($book);
+        return response()->json($this->forJsonDisplay($book));
     }
 
     /**
@@ -108,21 +108,35 @@ class BookApiController extends ApiController
         return response('', 204);
     }
 
+    protected function forJsonDisplay(Book $book): Book
+    {
+        $book = clone $book;
+        $book->unsetRelations()->refresh();
+
+        $book->load(['tags', 'cover']);
+        $book->makeVisible('description_html')
+            ->setAttribute('description_html', $book->descriptionHtml());
+
+        return $book;
+    }
+
     protected function rules(): array
     {
         return [
             'create' => [
-                'name'        => ['required', 'string', 'max:255'],
-                'description' => ['string', 'max:1000'],
-                'tags'        => ['array'],
-                'image'       => array_merge(['nullable'], $this->getImageValidationRules()),
+                'name'                => ['required', 'string', 'max:255'],
+                'description'         => ['string', 'max:1900'],
+                'description_html'    => ['string', 'max:2000'],
+                'tags'                => ['array'],
+                'image'               => array_merge(['nullable'], $this->getImageValidationRules()),
                 'default_template_id' => ['nullable', 'integer'],
             ],
             'update' => [
-                'name'        => ['string', 'min:1', 'max:255'],
-                'description' => ['string', 'max:1000'],
-                'tags'        => ['array'],
-                'image'       => array_merge(['nullable'], $this->getImageValidationRules()),
+                'name'                => ['string', 'min:1', 'max:255'],
+                'description'         => ['string', 'max:1900'],
+                'description_html'    => ['string', 'max:2000'],
+                'tags'                => ['array'],
+                'image'               => array_merge(['nullable'], $this->getImageValidationRules()),
                 'default_template_id' => ['nullable', 'integer'],
             ],
         ];
index 9bdb8256df374367bbaf1c0a9cb1330ad4e004c7..a12dc90ac62cf654d34521fe8579ee38072d031b 100644 (file)
@@ -12,11 +12,9 @@ use Illuminate\Validation\ValidationException;
 
 class BookshelfApiController extends ApiController
 {
-    protected BookshelfRepo $bookshelfRepo;
-
-    public function __construct(BookshelfRepo $bookshelfRepo)
-    {
-        $this->bookshelfRepo = $bookshelfRepo;
+    public function __construct(
+        protected BookshelfRepo $bookshelfRepo
+    ) {
     }
 
     /**
@@ -48,7 +46,7 @@ class BookshelfApiController extends ApiController
         $bookIds = $request->get('books', []);
         $shelf = $this->bookshelfRepo->create($requestData, $bookIds);
 
-        return response()->json($shelf);
+        return response()->json($this->forJsonDisplay($shelf));
     }
 
     /**
@@ -56,12 +54,14 @@ class BookshelfApiController extends ApiController
      */
     public function read(string $id)
     {
-        $shelf = Bookshelf::visible()->with([
-            'tags', 'cover', 'createdBy', 'updatedBy', 'ownedBy',
+        $shelf = Bookshelf::visible()->findOrFail($id);
+        $shelf = $this->forJsonDisplay($shelf);
+        $shelf->load([
+            'createdBy', 'updatedBy', 'ownedBy',
             'books' => function (BelongsToMany $query) {
                 $query->scopes('visible')->get(['id', 'name', 'slug']);
             },
-        ])->findOrFail($id);
+        ]);
 
         return response()->json($shelf);
     }
@@ -86,7 +86,7 @@ class BookshelfApiController extends ApiController
 
         $shelf = $this->bookshelfRepo->update($shelf, $requestData, $bookIds);
 
-        return response()->json($shelf);
+        return response()->json($this->forJsonDisplay($shelf));
     }
 
     /**
@@ -105,22 +105,36 @@ class BookshelfApiController extends ApiController
         return response('', 204);
     }
 
+    protected function forJsonDisplay(Bookshelf $shelf): Bookshelf
+    {
+        $shelf = clone $shelf;
+        $shelf->unsetRelations()->refresh();
+
+        $shelf->load(['tags', 'cover']);
+        $shelf->makeVisible('description_html')
+            ->setAttribute('description_html', $shelf->descriptionHtml());
+
+        return $shelf;
+    }
+
     protected function rules(): array
     {
         return [
             'create' => [
-                'name'        => ['required', 'string', 'max:255'],
-                'description' => ['string', 'max:1000'],
-                'books'       => ['array'],
-                'tags'        => ['array'],
-                'image'       => array_merge(['nullable'], $this->getImageValidationRules()),
+                'name'             => ['required', 'string', 'max:255'],
+                'description'      => ['string', 'max:1900'],
+                'description_html' => ['string', 'max:2000'],
+                'books'            => ['array'],
+                'tags'             => ['array'],
+                'image'            => array_merge(['nullable'], $this->getImageValidationRules()),
             ],
             'update' => [
-                'name'        => ['string', 'min:1', 'max:255'],
-                'description' => ['string', 'max:1000'],
-                'books'       => ['array'],
-                'tags'        => ['array'],
-                'image'       => array_merge(['nullable'], $this->getImageValidationRules()),
+                'name'             => ['string', 'min:1', 'max:255'],
+                'description'      => ['string', 'max:1900'],
+                'description_html' => ['string', 'max:2000'],
+                'books'            => ['array'],
+                'tags'             => ['array'],
+                'image'            => array_merge(['nullable'], $this->getImageValidationRules()),
             ],
         ];
     }
index 7f01e445a576d3d86cbbf097da50a09014472c44..c2132326200c5f186fdcfb82d680103a985ad20f 100644 (file)
@@ -15,18 +15,20 @@ class ChapterApiController extends ApiController
 {
     protected $rules = [
         'create' => [
-            'book_id'     => ['required', 'integer'],
-            'name'        => ['required', 'string', 'max:255'],
-            'description' => ['string', 'max:1000'],
-            'tags'        => ['array'],
-            'priority'    => ['integer'],
+            'book_id'          => ['required', 'integer'],
+            'name'             => ['required', 'string', 'max:255'],
+            'description'      => ['string', 'max:1900'],
+            'description_html' => ['string', 'max:2000'],
+            'tags'             => ['array'],
+            'priority'         => ['integer'],
         ],
         'update' => [
-            'book_id'     => ['integer'],
-            'name'        => ['string', 'min:1', 'max:255'],
-            'description' => ['string', 'max:1000'],
-            'tags'        => ['array'],
-            'priority'    => ['integer'],
+            'book_id'          => ['integer'],
+            'name'             => ['string', 'min:1', 'max:255'],
+            'description'      => ['string', 'max:1900'],
+            'description_html' => ['string', 'max:2000'],
+            'tags'             => ['array'],
+            'priority'         => ['integer'],
         ],
     ];
 
@@ -61,7 +63,7 @@ class ChapterApiController extends ApiController
 
         $chapter = $this->chapterRepo->create($requestData, $book);
 
-        return response()->json($chapter->load(['tags']));
+        return response()->json($this->forJsonDisplay($chapter));
     }
 
     /**
@@ -69,9 +71,15 @@ 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 = Chapter::visible()->findOrFail($id);
+        $chapter = $this->forJsonDisplay($chapter);
+
+        $chapter->load([
+            'createdBy', 'updatedBy', 'ownedBy',
+            'pages' => function (HasMany $query) {
+                $query->scopes('visible')->get(['id', 'name', 'slug']);
+            }
+        ]);
 
         return response()->json($chapter);
     }
@@ -93,7 +101,7 @@ class ChapterApiController extends ApiController
             try {
                 $this->chapterRepo->move($chapter, "book:{$requestData['book_id']}");
             } catch (Exception $exception) {
-                if ($exception instanceof  PermissionsException) {
+                if ($exception instanceof PermissionsException) {
                     $this->showPermissionError();
                 }
 
@@ -103,7 +111,7 @@ class ChapterApiController extends ApiController
 
         $updatedChapter = $this->chapterRepo->update($chapter, $requestData);
 
-        return response()->json($updatedChapter->load(['tags']));
+        return response()->json($this->forJsonDisplay($updatedChapter));
     }
 
     /**
@@ -119,4 +127,16 @@ class ChapterApiController extends ApiController
 
         return response('', 204);
     }
+
+    protected function forJsonDisplay(Chapter $chapter): Chapter
+    {
+        $chapter = clone $chapter;
+        $chapter->unsetRelations()->refresh();
+
+        $chapter->load(['tags']);
+        $chapter->makeVisible('description_html')
+            ->setAttribute('description_html', $chapter->descriptionHtml());
+
+        return $chapter;
+    }
 }
index 52674d8393435a68b9ccd0b08da11ee64cb7131f..14cb790c5c509c9283713aaa6a1b69fce4f22b8b 100644 (file)
@@ -31,7 +31,7 @@ class Book extends Entity implements HasCoverImage
     public float $searchFactor = 1.2;
 
     protected $fillable = ['name'];
-    protected $hidden = ['pivot', 'image_id', 'deleted_at'];
+    protected $hidden = ['pivot', 'image_id', 'deleted_at', 'description_html'];
 
     /**
      * Get the url for this book.
index 464c127b82f667fa47e3de5b176bd347db95a53f..9ffa0ea9cabd9a9b3beed43ea65cc4c21f2f0aad 100644 (file)
@@ -19,7 +19,7 @@ class Bookshelf extends Entity implements HasCoverImage
 
     protected $fillable = ['name', 'description', 'image_id'];
 
-    protected $hidden = ['image_id', 'deleted_at'];
+    protected $hidden = ['image_id', 'deleted_at', 'description_html'];
 
     /**
      * Get the books in this shelf.
index 6c5f059ac96bd6d19eeb8fa106b1934206be0cc2..f30d77b5c5c33d86d38554e431c4dbe64028d859 100644 (file)
@@ -20,7 +20,7 @@ class Chapter extends BookChild
     public float $searchFactor = 1.2;
 
     protected $fillable = ['name', 'description', 'priority'];
-    protected $hidden = ['pivot', 'deleted_at'];
+    protected $hidden = ['pivot', 'deleted_at', 'description_html'];
 
     /**
      * Get the pages that this chapter contains.
index 21e1829b8eb4215eda4d046156b4ea5a637a6b78..afeebade619cc4a1f73e8fd8e0ebb06e8b7d32f1 100644 (file)
@@ -3,6 +3,7 @@
   "name": "My own book",
   "slug": "my-own-book",
   "description": "This is my own little book",
+  "description_html": "<p>This is my own <em>little</em> book</p>",
   "created_at": "2020-01-12T14:09:59.000000Z",
   "updated_at": "2020-01-12T14:11:51.000000Z",
   "created_by": {
index 5f4de85f107ca6196d6fbedb203b7238bb40bdb9..192ffce7cd25cbe16f1a0488d7554431551065fe 100644 (file)
@@ -4,6 +4,7 @@
   "slug": "content-creation",
   "name": "Content Creation",
   "description": "How to create documentation on whatever subject you need to write about.",
+  "description_html": "<p>How to create <strong>documentation</strong> on whatever subject you need to write about.</p>",
   "priority": 3,
   "created_at": "2019-05-05T21:49:56.000000Z",
   "updated_at": "2019-09-28T11:24:23.000000Z",
index 802045bd8322f378fe823cadb2515b0e852c7f3f..eca06a46b329b617c4f3bcf813e82b9c48ea8137 100644 (file)
@@ -3,6 +3,7 @@
   "name": "My shelf",
   "slug": "my-shelf",
   "description": "This is my shelf with some books",
+  "description_html": "<p>This is my shelf with some books</p>",
   "created_by": {
     "id": 1,
     "name": "Admin",
Morty Proxy This is a proxified and sanitized view of the page, visit original site.