From 8b32e6c15ac4a188b48aed478be4382acae120ee Mon Sep 17 00:00:00 2001 From: Dan Brown Date: Sun, 22 Sep 2024 20:06:55 +0100 Subject: [PATCH] Page Editors: Added switching/options for new lexical editor --- app/Entities/Models/Page.php | 1 + app/Entities/Repos/PageRepo.php | 11 ++-- app/Entities/Tools/PageEditorData.php | 22 ++----- app/Entities/Tools/PageEditorType.php | 37 +++++++++++ lang/en/entities.php | 2 + resources/js/wysiwyg/todo.md | 1 + .../pages/parts/editor-toolbar.blade.php | 15 ++++- resources/views/pages/parts/form.blade.php | 7 +- .../views/settings/customization.blade.php | 1 + tests/Entity/PageEditorTest.php | 65 +++++++++++++++++-- 10 files changed, 134 insertions(+), 28 deletions(-) create mode 100644 app/Entities/Tools/PageEditorType.php diff --git a/app/Entities/Models/Page.php b/app/Entities/Models/Page.php index 3a433338b..499ef4d72 100644 --- a/app/Entities/Models/Page.php +++ b/app/Entities/Models/Page.php @@ -3,6 +3,7 @@ namespace BookStack\Entities\Models; use BookStack\Entities\Tools\PageContent; +use BookStack\Entities\Tools\PageEditorType; use BookStack\Permissions\PermissionApplicator; use BookStack\Uploads\Attachment; use Illuminate\Database\Eloquent\Builder; diff --git a/app/Entities/Repos/PageRepo.php b/app/Entities/Repos/PageRepo.php index 2526b6c44..0d9541c52 100644 --- a/app/Entities/Repos/PageRepo.php +++ b/app/Entities/Repos/PageRepo.php @@ -12,6 +12,7 @@ use BookStack\Entities\Queries\EntityQueries; use BookStack\Entities\Tools\BookContents; use BookStack\Entities\Tools\PageContent; use BookStack\Entities\Tools\PageEditorData; +use BookStack\Entities\Tools\PageEditorType; use BookStack\Entities\Tools\TrashCan; use BookStack\Exceptions\MoveOperationException; use BookStack\Exceptions\PermissionsException; @@ -126,7 +127,9 @@ class PageRepo } $pageContent = new PageContent($page); - $currentEditor = $page->editor ?: PageEditorData::getSystemDefaultEditor(); + $defaultEditor = PageEditorType::getSystemDefault(); + $currentEditor = PageEditorType::forPage($page) ?: $defaultEditor; + $inputEditor = PageEditorType::fromRequestValue($input['editor'] ?? '') ?? $currentEditor; $newEditor = $currentEditor; $haveInput = isset($input['markdown']) || isset($input['html']); @@ -135,15 +138,15 @@ class PageRepo if ($haveInput && $inputEmpty) { $pageContent->setNewHTML('', user()); } elseif (!empty($input['markdown']) && is_string($input['markdown'])) { - $newEditor = 'markdown'; + $newEditor = PageEditorType::Markdown; $pageContent->setNewMarkdown($input['markdown'], user()); } elseif (isset($input['html'])) { - $newEditor = 'wysiwyg'; + $newEditor = ($inputEditor->isHtmlBased() ? $inputEditor : null) ?? ($defaultEditor->isHtmlBased() ? $defaultEditor : null) ?? PageEditorType::WysiwygTinymce; $pageContent->setNewHTML($input['html'], user()); } if ($newEditor !== $currentEditor && userCan('editor-change')) { - $page->editor = $newEditor; + $page->editor = $newEditor->value; } } diff --git a/app/Entities/Tools/PageEditorData.php b/app/Entities/Tools/PageEditorData.php index f0bd23589..e4fe2fd25 100644 --- a/app/Entities/Tools/PageEditorData.php +++ b/app/Entities/Tools/PageEditorData.php @@ -74,17 +74,17 @@ class PageEditorData ]; } - protected function updateContentForEditor(Page $page, string $editorType): void + protected function updateContentForEditor(Page $page, PageEditorType $editorType): void { $isHtml = !empty($page->html) && empty($page->markdown); // HTML to markdown-clean conversion - if ($editorType === 'markdown' && $isHtml && $this->requestedEditor === 'markdown-clean') { + if ($editorType === PageEditorType::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) { + if ($editorType->isHtmlBased() && !$isHtml) { $page->html = (new MarkdownToHtml($page->markdown))->convert(); } } @@ -94,24 +94,16 @@ class PageEditorData * 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 + protected function getEditorType(Page $page): PageEditorType { - $editorType = $page->editor ?: self::getSystemDefaultEditor(); + $editorType = PageEditorType::forPage($page) ?: PageEditorType::getSystemDefault(); // Use requested editor if valid and if we have permission - $requestedType = explode('-', $this->requestedEditor)[0]; - if (($requestedType === 'markdown' || $requestedType === 'wysiwyg') && userCan('editor-change')) { + $requestedType = PageEditorType::fromRequestValue($this->requestedEditor); + if ($requestedType && userCan('editor-change')) { $editorType = $requestedType; } return $editorType; } - - /** - * Get the configured system default editor. - */ - public static function getSystemDefaultEditor(): string - { - return setting('app-editor') === 'markdown' ? 'markdown' : 'wysiwyg'; - } } diff --git a/app/Entities/Tools/PageEditorType.php b/app/Entities/Tools/PageEditorType.php new file mode 100644 index 000000000..1c1d430e4 --- /dev/null +++ b/app/Entities/Tools/PageEditorType.php @@ -0,0 +1,37 @@ + true, + self::Markdown => false, + }; + } + + public static function fromRequestValue(string $value): static|null + { + $editor = explode('-', $value)[0]; + return static::tryFrom($editor); + } + + public static function forPage(Page $page): static|null + { + return static::tryFrom($page->editor); + } + + public static function getSystemDefault(): static + { + $setting = setting('app-editor'); + return static::tryFrom($setting) ?? static::WysiwygTinymce; + } +} diff --git a/lang/en/entities.php b/lang/en/entities.php index 9e620b24e..35e6f050b 100644 --- a/lang/en/entities.php +++ b/lang/en/entities.php @@ -224,6 +224,8 @@ return [ 'pages_edit_switch_to_markdown_clean' => '(Clean Content)', 'pages_edit_switch_to_markdown_stable' => '(Stable Content)', 'pages_edit_switch_to_wysiwyg' => 'Switch to WYSIWYG Editor', + 'pages_edit_switch_to_new_wysiwyg' => 'Switch to new WYSIWYG', + 'pages_edit_switch_to_new_wysiwyg_desc' => '(In Alpha Testing)', 'pages_edit_set_changelog' => 'Set Changelog', 'pages_edit_enter_changelog_desc' => 'Enter a brief description of the changes you\'ve made', 'pages_edit_enter_changelog' => 'Enter Changelog', diff --git a/resources/js/wysiwyg/todo.md b/resources/js/wysiwyg/todo.md index 91e1a9678..31e3533b1 100644 --- a/resources/js/wysiwyg/todo.md +++ b/resources/js/wysiwyg/todo.md @@ -19,5 +19,6 @@ ## Bugs +- Editor theme classes remain on items after export - List selection can get lost on nesting/unnesting - Content not properly saving on new pages \ No newline at end of file diff --git a/resources/views/pages/parts/editor-toolbar.blade.php b/resources/views/pages/parts/editor-toolbar.blade.php index d25f6a0a4..341fbf67d 100644 --- a/resources/views/pages/parts/editor-toolbar.blade.php +++ b/resources/views/pages/parts/editor-toolbar.blade.php @@ -55,7 +55,7 @@
  • - @if($editor === 'wysiwyg') + @if($editor !== \BookStack\Entities\Tools\PageEditorType::Markdown) @icon('swap-horizontal')
    @@ -72,12 +72,23 @@ {{ trans('entities.pages_edit_switch_to_markdown_stable') }}
    - @else + @endif + @if($editor !== \BookStack\Entities\Tools\PageEditorType::WysiwygTinymce) @icon('swap-horizontal')
    {{ trans('entities.pages_edit_switch_to_wysiwyg') }}
    @endif + @if($editor !== \BookStack\Entities\Tools\PageEditorType::WysiwygLexical) + + @icon('swap-horizontal') +
    + {{ trans('entities.pages_edit_switch_to_new_wysiwyg') }} +
    + {{ trans('entities.pages_edit_switch_to_new_wysiwyg_desc') }} +
    +
    + @endif
  • @endif diff --git a/resources/views/pages/parts/form.blade.php b/resources/views/pages/parts/form.blade.php index 490374e40..e1104b406 100644 --- a/resources/views/pages/parts/form.blade.php +++ b/resources/views/pages/parts/form.blade.php @@ -32,18 +32,19 @@
    {{--Editors--}}
    + - @if($editor === 'wysiwyg') + @if($editor === \BookStack\Entities\Tools\PageEditorType::WysiwygLexical) @include('pages.parts.wysiwyg-editor', ['model' => $model]) @endif {{--WYSIWYG Editor (TinyMCE - Deprecated)--}} - @if($editor === 'wysiwyg-tinymce') + @if($editor === \BookStack\Entities\Tools\PageEditorType::WysiwygTinymce) @include('pages.parts.wysiwyg-editor-tinymce', ['model' => $model]) @endif {{--Markdown Editor--}} - @if($editor === 'markdown') + @if($editor === \BookStack\Entities\Tools\PageEditorType::Markdown) @include('pages.parts.markdown-editor', ['model' => $model]) @endif diff --git a/resources/views/settings/customization.blade.php b/resources/views/settings/customization.blade.php index 4845e2055..70a490298 100644 --- a/resources/views/settings/customization.blade.php +++ b/resources/views/settings/customization.blade.php @@ -32,6 +32,7 @@
    diff --git a/tests/Entity/PageEditorTest.php b/tests/Entity/PageEditorTest.php index b2fb85955..934024956 100644 --- a/tests/Entity/PageEditorTest.php +++ b/tests/Entity/PageEditorTest.php @@ -4,6 +4,7 @@ namespace Tests\Entity; use BookStack\Entities\Models\Chapter; use BookStack\Entities\Models\Page; +use BookStack\Entities\Tools\PageEditorType; use Tests\TestCase; class PageEditorTest extends TestCase @@ -25,7 +26,7 @@ class PageEditorTest extends TestCase public function test_markdown_setting_shows_markdown_editor_for_new_pages() { - $this->setSettings(['app-editor' => 'markdown']); + $this->setSettings(['app-editor' => PageEditorType::Markdown->value]); $resp = $this->asAdmin()->get($this->page->book->getUrl('/create-page')); $this->withHtml($this->followRedirects($resp)) @@ -37,7 +38,7 @@ class PageEditorTest extends TestCase { $mdContent = '# hello. This is a test'; $this->page->markdown = $mdContent; - $this->page->editor = 'markdown'; + $this->page->editor = PageEditorType::Markdown; $this->page->save(); $resp = $this->asAdmin()->get($this->page->getUrl('/edit')); @@ -135,6 +136,19 @@ class PageEditorTest extends TestCase $resp = $this->asAdmin()->get($page->getUrl('/edit?editor=wysiwyg')); $resp->assertStatus(200); + $this->withHtml($resp)->assertElementExists('[component="wysiwyg-editor-tinymce"]'); + $resp->assertSee("

    A Header

    \n

    Some content with bold text!

    ", true); + } + + public function test_switching_from_markdown_to_wysiwyg2024_works() + { + $page = $this->entities->page(); + $page->html = ''; + $page->markdown = "## A Header\n\nSome content with **bold** text!"; + $page->save(); + + $resp = $this->asAdmin()->get($page->getUrl('/edit?editor=wysiwyg2024')); + $resp->assertStatus(200); $this->withHtml($resp)->assertElementExists('[component="wysiwyg-editor"]'); $resp->assertSee("

    A Header

    \n

    Some content with bold text!

    ", true); } @@ -142,7 +156,7 @@ class PageEditorTest extends TestCase public function test_page_editor_changes_with_editor_property() { $resp = $this->asAdmin()->get($this->page->getUrl('/edit')); - $this->withHtml($resp)->assertElementExists('[component="wysiwyg-editor"]'); + $this->withHtml($resp)->assertElementExists('[component="wysiwyg-editor-tinymce"]'); $this->page->markdown = "## A Header\n\nSome content with **bold** text!"; $this->page->editor = 'markdown'; @@ -150,6 +164,12 @@ class PageEditorTest extends TestCase $resp = $this->asAdmin()->get($this->page->getUrl('/edit')); $this->withHtml($resp)->assertElementExists('[component="markdown-editor"]'); + + $this->page->editor = 'wysiwyg2024'; + $this->page->save(); + + $resp = $this->asAdmin()->get($this->page->getUrl('/edit')); + $this->withHtml($resp)->assertElementExists('[component="wysiwyg-editor"]'); } public function test_editor_type_switch_options_show() @@ -158,6 +178,7 @@ class PageEditorTest extends TestCase $editLink = $this->page->getUrl('/edit') . '?editor='; $this->withHtml($resp)->assertElementContains("a[href=\"${editLink}markdown-clean\"]", '(Clean Content)'); $this->withHtml($resp)->assertElementContains("a[href=\"${editLink}markdown-stable\"]", '(Stable Content)'); + $this->withHtml($resp)->assertElementContains("a[href=\"${editLink}wysiwyg2024\"]", '(In Alpha Testing)'); $resp = $this->asAdmin()->get($this->page->getUrl('/edit?editor=markdown-stable')); $editLink = $this->page->getUrl('/edit') . '?editor='; @@ -179,7 +200,7 @@ class PageEditorTest extends TestCase $resp = $this->asEditor()->get($page->getUrl('/edit?editor=markdown-stable')); $resp->assertStatus(200); - $this->withHtml($resp)->assertElementExists('[component="wysiwyg-editor"]'); + $this->withHtml($resp)->assertElementExists('[component="wysiwyg-editor-tinymce"]'); $this->withHtml($resp)->assertElementNotExists('[component="markdown-editor"]'); } @@ -193,4 +214,40 @@ class PageEditorTest extends TestCase $this->asEditor()->put($page->getUrl(), ['name' => $page->name, 'markdown' => '## Updated content abc']); $this->assertEquals('wysiwyg', $page->refresh()->editor); } + + public function test_editor_type_change_to_wysiwyg_infers_type_from_request_or_uses_system_default() + { + $tests = [ + [ + 'setting' => 'wysiwyg', + 'request' => 'wysiwyg2024', + 'expected' => 'wysiwyg2024', + ], + [ + 'setting' => 'wysiwyg2024', + 'request' => 'wysiwyg', + 'expected' => 'wysiwyg', + ], + [ + 'setting' => 'wysiwyg', + 'request' => null, + 'expected' => 'wysiwyg', + ], + [ + 'setting' => 'wysiwyg2024', + 'request' => null, + 'expected' => 'wysiwyg2024', + ] + ]; + + $page = $this->entities->page(); + foreach ($tests as $test) { + $page->editor = 'markdown'; + $page->save(); + + $this->setSettings(['app-editor' => $test['setting']]); + $this->asAdmin()->put($page->getUrl(), ['name' => $page->name, 'html' => '

    Hello

    ', 'editor' => $test['request']]); + $this->assertEquals($test['expected'], $page->refresh()->editor, "Failed asserting global editor {$test['setting']} with request editor {$test['request']} results in {$test['expected']} set for the page"); + } + } }