From 492ffff0a488d3bd9b3759686a037edf6190844b Mon Sep 17 00:00:00 2001 From: Dan Brown Date: Mon, 18 Apr 2022 17:39:28 +0100 Subject: [PATCH] Added core editor switching functionality --- app/Entities/Models/PageRevision.php | 2 +- app/Entities/Repos/PageRepo.php | 9 +++- .../Tools/Markdown/HtmlToMarkdown.php | 2 +- .../Tools/Markdown/MarkdownToHtml.php | 37 ++++++++++++++ app/Entities/Tools/PageContent.php | 31 ++---------- app/Entities/Tools/PageEditorData.php | 48 ++++++++++++++++++- app/Http/Controllers/PageController.php | 8 ++-- resources/views/pages/edit.blade.php | 2 +- resources/views/pages/parts/form.blade.php | 2 +- 9 files changed, 101 insertions(+), 40 deletions(-) create mode 100644 app/Entities/Tools/Markdown/MarkdownToHtml.php diff --git a/app/Entities/Models/PageRevision.php b/app/Entities/Models/PageRevision.php index 800e5e7f2..55b2ffbe8 100644 --- a/app/Entities/Models/PageRevision.php +++ b/app/Entities/Models/PageRevision.php @@ -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']; /** diff --git a/app/Entities/Repos/PageRepo.php b/app/Entities/Repos/PageRepo.php index 828c4572f..d47573a6c 100644 --- a/app/Entities/Repos/PageRepo.php +++ b/app/Entities/Repos/PageRepo.php @@ -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 = ''; } diff --git a/app/Entities/Tools/Markdown/HtmlToMarkdown.php b/app/Entities/Tools/Markdown/HtmlToMarkdown.php index 51366705c..5c7b388ea 100644 --- a/app/Entities/Tools/Markdown/HtmlToMarkdown.php +++ b/app/Entities/Tools/Markdown/HtmlToMarkdown.php @@ -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 index 000000000..25413fb33 --- /dev/null +++ b/app/Entities/Tools/Markdown/MarkdownToHtml.php @@ -0,0 +1,37 @@ +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 diff --git a/app/Entities/Tools/PageContent.php b/app/Entities/Tools/PageContent.php index b1c750adb..ea6a185f1 100644 --- a/app/Entities/Tools/PageContent.php +++ b/app/Entities/Tools/PageContent.php @@ -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. */ diff --git a/app/Entities/Tools/PageEditorData.php b/app/Entities/Tools/PageEditorData.php index a6818839d..3e1164175 100644 --- a/app/Entities/Tools/PageEditorData.php +++ b/app/Entities/Tools/PageEditorData.php @@ -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 diff --git a/app/Http/Controllers/PageController.php b/app/Http/Controllers/PageController.php index 232c6b034..268dce057 100644 --- a/app/Http/Controllers/PageController.php +++ b/app/Http/Controllers/PageController.php @@ -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())); } diff --git a/resources/views/pages/edit.blade.php b/resources/views/pages/edit.blade.php index de7c82d21..cd9635758 100644 --- a/resources/views/pages/edit.blade.php +++ b/resources/views/pages/edit.blade.php @@ -8,7 +8,7 @@
{{ 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')
diff --git a/resources/views/pages/parts/form.blade.php b/resources/views/pages/parts/form.blade.php index 3507705aa..ebad2bd72 100644 --- a/resources/views/pages/parts/form.blade.php +++ b/resources/views/pages/parts/form.blade.php @@ -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') }}"