]> BookStack Code Mirror - bookstack/commitdiff
Added core editor switching functionality
authorDan Brown <redacted>
Mon, 18 Apr 2022 16:39:28 +0000 (17:39 +0100)
committerDan Brown <redacted>
Mon, 18 Apr 2022 16:39:28 +0000 (17:39 +0100)
app/Entities/Models/PageRevision.php
app/Entities/Repos/PageRepo.php
app/Entities/Tools/Markdown/HtmlToMarkdown.php
app/Entities/Tools/Markdown/MarkdownToHtml.php [new file with mode: 0644]
app/Entities/Tools/PageContent.php
app/Entities/Tools/PageEditorData.php
app/Http/Controllers/PageController.php
resources/views/pages/edit.blade.php
resources/views/pages/parts/form.blade.php

index 800e5e7f2d8d0332bfcc868bbdcdcb8c872d5698..55b2ffbe805c835277f2800247a021254f8c2573 100644 (file)
@@ -27,7 +27,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
  */
 class PageRevision extends Model
 {
-    protected $fillable = ['name', 'html', 'text', 'markdown', 'summary'];
+    protected $fillable = ['name', 'text', 'summary'];
     protected $hidden = ['html', 'markdown', 'restricted', 'text'];
 
     /**
index 828c4572fd100f7355d02077477a85a8728bc216..d47573a6cf516c80f4798618fb370af3e0bf50ae 100644 (file)
@@ -260,10 +260,15 @@ class PageRepo
             return $page;
         }
 
-        // Otherwise save the data to a revision
+        // Otherwise, save the data to a revision
         $draft = $this->getPageRevisionToUpdate($page);
         $draft->fill($input);
-        if (setting('app-editor') !== 'markdown') {
+
+        if (!empty($input['markdown'])) {
+            $draft->markdown = $input['markdown'];
+            $draft->html = '';
+        } else {
+            $draft->html = $input['html'];
             $draft->markdown = '';
         }
 
index 51366705ca1a1043df00a690bb5f5a70293e794a..5c7b388ea93ceda62bb8f8241ae6d924235e449c 100644 (file)
@@ -21,7 +21,7 @@ use League\HTMLToMarkdown\HtmlConverter;
 
 class HtmlToMarkdown
 {
-    protected $html;
+    protected string $html;
 
     public function __construct(string $html)
     {
diff --git a/app/Entities/Tools/Markdown/MarkdownToHtml.php b/app/Entities/Tools/Markdown/MarkdownToHtml.php
new file mode 100644 (file)
index 0000000..25413fb
--- /dev/null
@@ -0,0 +1,37 @@
+<?php
+
+namespace BookStack\Entities\Tools\Markdown;
+
+use BookStack\Facades\Theme;
+use BookStack\Theming\ThemeEvents;
+use League\CommonMark\Block\Element\ListItem;
+use League\CommonMark\CommonMarkConverter;
+use League\CommonMark\Environment;
+use League\CommonMark\Extension\Table\TableExtension;
+use League\CommonMark\Extension\TaskList\TaskListExtension;
+
+class MarkdownToHtml
+{
+
+    protected string $markdown;
+
+    public function __construct(string $markdown)
+    {
+        $this->markdown = $markdown;
+    }
+
+    public function convert(): string
+    {
+        $environment = Environment::createCommonMarkEnvironment();
+        $environment->addExtension(new TableExtension());
+        $environment->addExtension(new TaskListExtension());
+        $environment->addExtension(new CustomStrikeThroughExtension());
+        $environment = Theme::dispatch(ThemeEvents::COMMONMARK_ENVIRONMENT_CONFIGURE, $environment) ?? $environment;
+        $converter = new CommonMarkConverter([], $environment);
+
+        $environment->addBlockRenderer(ListItem::class, new CustomListItemRenderer(), 10);
+
+        return $converter->convertToHtml($this->markdown);
+    }
+
+}
\ No newline at end of file
index b1c750adbdd6a3a3645c75e91b0f44c9e940bc6c..ea6a185f161424f3cc85d0ef918860c980bc3227 100644 (file)
@@ -3,11 +3,8 @@
 namespace BookStack\Entities\Tools;
 
 use BookStack\Entities\Models\Page;
-use BookStack\Entities\Tools\Markdown\CustomListItemRenderer;
-use BookStack\Entities\Tools\Markdown\CustomStrikeThroughExtension;
+use BookStack\Entities\Tools\Markdown\MarkdownToHtml;
 use BookStack\Exceptions\ImageUploadException;
-use BookStack\Facades\Theme;
-use BookStack\Theming\ThemeEvents;
 use BookStack\Uploads\ImageRepo;
 use BookStack\Uploads\ImageService;
 use BookStack\Util\HtmlContentFilter;
@@ -17,15 +14,10 @@ use DOMNode;
 use DOMNodeList;
 use DOMXPath;
 use Illuminate\Support\Str;
-use League\CommonMark\Block\Element\ListItem;
-use League\CommonMark\CommonMarkConverter;
-use League\CommonMark\Environment;
-use League\CommonMark\Extension\Table\TableExtension;
-use League\CommonMark\Extension\TaskList\TaskListExtension;
 
 class PageContent
 {
-    protected $page;
+    protected Page $page;
 
     /**
      * PageContent constructor.
@@ -53,28 +45,11 @@ class PageContent
     {
         $markdown = $this->extractBase64ImagesFromMarkdown($markdown);
         $this->page->markdown = $markdown;
-        $html = $this->markdownToHtml($markdown);
+        $html = (new MarkdownToHtml($markdown))->convert();
         $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
-    {
-        $environment = Environment::createCommonMarkEnvironment();
-        $environment->addExtension(new TableExtension());
-        $environment->addExtension(new TaskListExtension());
-        $environment->addExtension(new CustomStrikeThroughExtension());
-        $environment = Theme::dispatch(ThemeEvents::COMMONMARK_ENVIRONMENT_CONFIGURE, $environment) ?? $environment;
-        $converter = new CommonMarkConverter([], $environment);
-
-        $environment->addBlockRenderer(ListItem::class, new CustomListItemRenderer(), 10);
-
-        return $converter->convertToHtml($markdown);
-    }
-
     /**
      * Convert all base64 image data to saved images.
      */
index a6818839dc27c9ed83491a3bfc3a10724a2f92d7..3e11641750c9a6cfe6c30f6d7e1ed345da27aa35 100644 (file)
@@ -4,19 +4,24 @@ namespace BookStack\Entities\Tools;
 
 use BookStack\Entities\Models\Page;
 use BookStack\Entities\Repos\PageRepo;
+use BookStack\Entities\Tools\Markdown\HtmlToMarkdown;
+use BookStack\Entities\Tools\Markdown\MarkdownToHtml;
 
 class PageEditorData
 {
     protected Page $page;
     protected PageRepo $pageRepo;
+    protected string $requestedEditor;
 
     protected array $viewData;
     protected array $warnings;
 
-    public function __construct(Page $page, PageRepo $pageRepo)
+    public function __construct(Page $page, PageRepo $pageRepo, string $requestedEditor)
     {
         $this->page = $page;
         $this->pageRepo = $pageRepo;
+        $this->requestedEditor = $requestedEditor;
+
         $this->viewData = $this->build();
     }
 
@@ -53,6 +58,9 @@ class PageEditorData
             $this->warnings[] = $editActivity->getEditingActiveDraftMessage($userDraft);
         }
 
+        $editorType = $this->getEditorType($page);
+        $this->updateContentForEditor($page, $editorType);
+
         return [
             'page'            => $page,
             'book'            => $page->book,
@@ -60,8 +68,44 @@ class PageEditorData
             'isDraftRevision' => $isDraftRevision,
             'draftsEnabled'   => $draftsEnabled,
             'templates'       => $templates,
-            'editor'          =>  setting('app-editor') === 'wysiwyg' ? 'wysiwyg' : 'markdown',
+            'editor'          => $editorType,
         ];
     }
 
+    protected function updateContentForEditor(Page $page, string $editorType): void
+    {
+        $isHtml = !empty($page->html) && empty($page->markdown);
+
+        // HTML to markdown-clean conversion
+        if ($editorType === 'markdown' && $isHtml && $this->requestedEditor === 'markdown-clean') {
+            $page->markdown = (new HtmlToMarkdown($page->html))->convert();
+        }
+
+        // Markdown to HTML conversion if we don't have HTML
+        if ($editorType === 'wysiwyg' && !$isHtml) {
+            $page->html = (new MarkdownToHtml($page->markdown))->convert();
+        }
+    }
+
+    /**
+     * Get the type of editor to show for editing the given page.
+     * Defaults based upon the current content of the page otherwise will fall back
+     * to system default but will take a requested type (if provided) if permissions allow.
+     */
+    protected function getEditorType(Page $page): string
+    {
+        $emptyPage = empty($page->html) && empty($page->markdown);
+        $pageType = (!empty($page->html) && empty($page->markdown)) ? 'wysiwyg' : 'markdown';
+        $systemDefault = setting('app-editor') === 'wysiwyg' ? 'wysiwyg' : 'markdown';
+        $editorType = $emptyPage ? $systemDefault : $pageType;
+
+        // Use requested editor if valid and if we have permission
+        $requestedType = explode('-', $this->requestedEditor)[0];
+        if (($requestedType === 'markdown' || $requestedType === 'wysiwyg') && userCan('editor-change')) {
+            $editorType = $requestedType;
+        }
+
+        return $editorType;
+    }
+
 }
\ No newline at end of file
index 232c6b034e3c7a3ddcc8dc88185ecfddb1632811..268dce0573a9c51a0beacb264c7dc9ca14435481 100644 (file)
@@ -83,12 +83,12 @@ class PageController extends Controller
      *
      * @throws NotFoundException
      */
-    public function editDraft(string $bookSlug, int $pageId)
+    public function editDraft(Request $request, string $bookSlug, int $pageId)
     {
         $draft = $this->pageRepo->getById($pageId);
         $this->checkOwnablePermission('page-create', $draft->getParent());
 
-        $editorData = new PageEditorData($draft, $this->pageRepo);
+        $editorData = new PageEditorData($draft, $this->pageRepo, $request->query('editor', ''));
         $this->setPageTitle(trans('entities.pages_edit_draft'));
 
         return view('pages.edit', $editorData->getViewData());
@@ -182,12 +182,12 @@ class PageController extends Controller
      *
      * @throws NotFoundException
      */
-    public function edit(string $bookSlug, string $pageSlug)
+    public function edit(Request $request, string $bookSlug, string $pageSlug)
     {
         $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug);
         $this->checkOwnablePermission('page-update', $page);
 
-        $editorData = new PageEditorData($page, $this->pageRepo);
+        $editorData = new PageEditorData($page, $this->pageRepo, $request->query('editor', ''));
         if ($editorData->getWarnings()) {
             $this->showWarningNotification(implode("\n", $editorData->getWarnings()));
         }
index de7c82d2102f5b3f3c62f2b84ab329c95eaa5f66..cd9635758060fe77f58a99427138d81075ca9749 100644 (file)
@@ -8,7 +8,7 @@
         <form action="{{ $page->getUrl() }}" autocomplete="off" data-page-id="{{ $page->id }}" method="POST" class="flex flex-fill">
             {{ csrf_field() }}
 
-            @if($isDraft) {{ method_field('PUT') }} @endif
+            @if(!$isDraft) {{ method_field('PUT') }} @endif
             @include('pages.parts.form', ['model' => $page])
             @include('pages.parts.editor-toolbox')
         </form>
index 3507705aa31ba5b581d7a12b29e604997c94f24c..ebad2bd7261f7c2c2d6cdc0ee72c2ebc1faa6593 100644 (file)
@@ -6,7 +6,7 @@
      @if($model->name === trans('entities.pages_initial_name'))
         option:page-editor:has-default-title="true"
      @endif
-     option:page-editor:editor-type="{{ setting('app-editor') }}"
+     option:page-editor:editor-type="{{ $editor }}"
      option:page-editor:page-id="{{ $model->id ?? '0' }}"
      option:page-editor:page-new-draft="{{ $isDraft ? 'true' : 'false' }}"
      option:page-editor:draft-text="{{ ($isDraft || $isDraftRevision) ? trans('entities.pages_editing_draft') : trans('entities.pages_editing_page') }}"