]> BookStack Code Mirror - bookstack/commitdiff
Started pages API
authorDan Brown <redacted>
Sun, 22 Nov 2020 14:56:19 +0000 (14:56 +0000)
committerDan Brown <redacted>
Sun, 22 Nov 2020 14:56:19 +0000 (14:56 +0000)
app/Entities/Models/Page.php
app/Entities/Repos/PageRepo.php
app/Entities/Tools/PageContent.php
app/Http/Controllers/Api/ApiController.php
app/Http/Controllers/Api/PageApiController.php [new file with mode: 0644]
routes/api.php

index b3eb213212fc89f4eee5258de10dda62b08d5245..b0a3e2d3185143a9265e6079c3b615e88e5579a6 100644 (file)
@@ -29,6 +29,11 @@ class Page extends BookChild
 
     protected $hidden = ['html', 'markdown', 'text', 'restricted', 'pivot'];
 
+    protected $casts = [
+        'draft' => 'boolean',
+        'template' => 'boolean',
+    ];
+
     /**
      * Get the entities that are visible to the current user.
      */
index 9c6da0a81d38f5abb55fdf5e02eccc65693e2ca6..153ef857594bd7ee91f2fb5cf2db7bee80b50b2c 100644 (file)
@@ -35,9 +35,9 @@ class PageRepo
      * Get a page by ID.
      * @throws NotFoundException
      */
-    public function getById(int $id): Page
+    public function getById(int $id, array $relations = ['book']): Page
     {
-        $page = Page::visible()->with(['book'])->find($id);
+        $page = Page::visible()->with($relations)->find($id);
 
         if (!$page) {
             throw new NotFoundException(trans('errors.page_not_found'));
@@ -152,12 +152,8 @@ class PageRepo
     public function publishDraft(Page $draft, array $input): Page
     {
         $this->baseRepo->update($draft, $input);
-        if (isset($input['template']) && userCan('templates-manage')) {
-            $draft->template = ($input['template'] === 'true');
-        }
+        $this->updateTemplateStatusAndContentFromInput($draft, $input);
 
-        $pageContent = new PageContent($draft);
-        $pageContent->setNewHTML($input['html']);
         $draft->draft = false;
         $draft->revision_count = 1;
         $draft->priority = $this->getNewPriority($draft);
@@ -181,12 +177,7 @@ class PageRepo
         $oldHtml = $page->html;
         $oldName = $page->name;
 
-        if (isset($input['template']) && userCan('templates-manage')) {
-            $page->template = ($input['template'] === 'true');
-        }
-
-        $pageContent = new PageContent($page);
-        $pageContent->setNewHTML($input['html']);
+        $this->updateTemplateStatusAndContentFromInput($page, $input);
         $this->baseRepo->update($page, $input);
 
         // Update with new details
@@ -211,6 +202,20 @@ class PageRepo
         return $page;
     }
 
+    protected function updateTemplateStatusAndContentFromInput(Page $page, array $input)
+    {
+        if (isset($input['template']) && userCan('templates-manage')) {
+            $page->template = ($input['template'] === 'true');
+        }
+
+        $pageContent = new PageContent($page);
+        if (isset($input['html'])) {
+            $pageContent->setNewHTML($input['html']);
+        } else {
+            $pageContent->setNewMarkdown($input['markdown']);
+        }
+    }
+
     /**
      * Saves a page revision into the system.
      */
@@ -243,11 +248,10 @@ class PageRepo
     {
         // If the page itself is a draft simply update that
         if ($page->draft) {
-            $page->fill($input);
             if (isset($input['html'])) {
-                $content = new PageContent($page);
-                $content->setNewHTML($input['html']);
+                (new PageContent($page))->setNewHTML($input['html']);
             }
+            $page->fill($input);
             $page->save();
             return $page;
         }
index 011e1b2ac25f0ae87fe841a37b06b511447bec0c..f60971b8bbe02b4539419fe1103426ef1017fda7 100644 (file)
@@ -4,6 +4,7 @@ use BookStack\Entities\Models\Page;
 use DOMDocument;
 use DOMNodeList;
 use DOMXPath;
+use League\CommonMark\CommonMarkConverter;
 
 class PageContent
 {
@@ -25,6 +26,27 @@ class PageContent
     {
         $this->page->html = $this->formatHtml($html);
         $this->page->text = $this->toPlainText();
+        $this->page->markdown = '';
+    }
+
+    /**
+     * Update the content of the page with new provided Markdown content.
+     */
+    public function setNewMarkdown(string $markdown)
+    {
+        $this->page->markdown = $markdown;
+        $html = $this->markdownToHtml($markdown);
+        $this->page->html = $this->formatHtml($html);
+        $this->page->text = $this->toPlainText();
+    }
+
+    /**
+     * Convert the given Markdown content to a HTML string.
+     */
+    protected function markdownToHtml(string $markdown): string
+    {
+        $converter = new CommonMarkConverter();
+        return $converter->convertToHtml($markdown);
     }
 
     /**
index 65a5bb99f6ca3bfc6f99e4e0137d4e6029d91ff3..0a3d8945356cb9414fc592fb3c4f7668c272e4b7 100644 (file)
@@ -5,7 +5,7 @@ use BookStack\Http\Controllers\Controller;
 use Illuminate\Database\Eloquent\Builder;
 use Illuminate\Http\JsonResponse;
 
-class ApiController extends Controller
+abstract class ApiController extends Controller
 {
 
     protected $rules = [];
diff --git a/app/Http/Controllers/Api/PageApiController.php b/app/Http/Controllers/Api/PageApiController.php
new file mode 100644 (file)
index 0000000..2fc4e3b
--- /dev/null
@@ -0,0 +1,126 @@
+<?php
+
+namespace BookStack\Http\Controllers\Api;
+
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Page;
+use BookStack\Entities\Repos\PageRepo;
+use BookStack\Exceptions\PermissionsException;
+use Exception;
+use Illuminate\Http\Request;
+
+class PageApiController extends ApiController
+{
+    protected $pageRepo;
+
+    protected $rules = [
+        'create' => [
+            'book_id' => 'required_unless:chapter_id|integer',
+            'chapter_id' => 'required_unless:book_id|integer',
+            'name' => 'required|string|max:255',
+            'html' => 'required_without:markdown|string',
+            'markdown' => 'required_without:html|string',
+            'tags' => 'array',
+        ],
+        'update' => [
+            'book_id' => 'required|integer',
+            'chapter_id' => 'required|integer',
+            'name' => 'string|min:1|max:255',
+            'html' => 'string',
+            'markdown' => 'string',
+            'tags' => 'array',
+        ],
+    ];
+
+    public function __construct(PageRepo $pageRepo)
+    {
+        $this->pageRepo = $pageRepo;
+    }
+
+    /**
+     * Get a listing of pages visible to the user.
+     */
+    public function list()
+    {
+        $pages = Page::visible();
+        return $this->apiListingResponse($pages, [
+            'id', 'book_id', 'chapter_id', 'name', 'slug', 'priority',
+            'draft', 'template',
+            'created_at', 'updated_at', 'created_by', 'updated_by',
+        ]);
+    }
+
+    /**
+     * Create a new page in the system.
+     */
+    public function create(Request $request)
+    {
+        $this->validate($request, $this->rules['create']);
+
+        if ($request->has('chapter_id')) {
+            $parent = Chapter::visible()->findOrFail($request->get('chapter_id'));
+        } else {
+            $parent = Book::visible()->findOrFail($request->get('book_id'));
+        }
+        $this->checkOwnablePermission('page-create', $parent);
+
+        $draft = $this->pageRepo->getNewDraftPage($parent);
+        $this->pageRepo->publishDraft($draft, $request->only(array_keys($this->rules['create'])));
+
+        return response()->json($draft->load(['tags']));
+    }
+
+    /**
+     * View the details of a single page.
+     */
+    public function read(string $id)
+    {
+        $page = $this->pageRepo->getById($id, ['tags', 'createdBy', 'updatedBy']);
+        return response()->json($page);
+    }
+
+    /**
+     * Update the details of a single page.
+     */
+    public function update(Request $request, string $id)
+    {
+        $page = $this->pageRepo->getById($id, []);
+        $this->checkOwnablePermission('page-update', $page);
+
+        $parent = null;
+        if ($request->has('chapter_id')) {
+            $parent = Chapter::visible()->findOrFail($request->get('chapter_id'));
+        } else if ($request->has('book_id')) {
+            $parent = Book::visible()->findOrFail($request->get('book_id'));
+        }
+
+        if ($parent && !$parent->matches($page->getParent())) {
+            $this->checkOwnablePermission('page-delete', $page);
+            try {
+                $this->pageRepo->move($page, $parent->getType() . ':' . $parent->id);
+            } catch (Exception $exception) {
+                if ($exception instanceof  PermissionsException) {
+                    $this->showPermissionError();
+                }
+
+                return $this->jsonError(trans('errors.selected_book_chapter_not_found'));
+            }
+        }
+
+        $updatedPage = $this->pageRepo->update($page, $request->all());
+        return response()->json($updatedPage->load(['tags']));
+    }
+
+    /**
+     * Delete a page from the system.
+     */
+    public function delete(string $id)
+    {
+        $page = $this->pageRepo->getById($id, []);
+        $this->checkOwnablePermission('page-delete', $page);
+
+        $this->pageRepo->destroy($page);
+        return response('', 204);
+    }
+}
index 1b90d9b8fd12d591cf87b6e34fba2191b6711ff9..d02e2311b29fbf5ce534483203412f3328fbec00 100644 (file)
@@ -34,3 +34,9 @@ Route::post('shelves', 'BookshelfApiController@create');
 Route::get('shelves/{id}', 'BookshelfApiController@read');
 Route::put('shelves/{id}', 'BookshelfApiController@update');
 Route::delete('shelves/{id}', 'BookshelfApiController@delete');
+
+Route::get('pages', 'PageApiController@list');
+Route::post('pages', 'PageApiController@create');
+Route::get('pages/{id}', 'PageApiController@read');
+Route::put('pages/{id}', 'PageApiController@update');
+Route::delete('pages/{id}', 'PageApiController@delete');