*/
class PageRevision extends Model
{
- protected $fillable = ['name', 'html', 'text', 'markdown', 'summary'];
+ protected $fillable = ['name', 'text', 'summary'];
protected $hidden = ['html', 'markdown', 'restricted', 'text'];
/**
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 = '';
}
class HtmlToMarkdown
{
- protected $html;
+ protected string $html;
public function __construct(string $html)
{
--- /dev/null
+<?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
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;
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.
{
$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.
*/
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();
}
$this->warnings[] = $editActivity->getEditingActiveDraftMessage($userDraft);
}
+ $editorType = $this->getEditorType($page);
+ $this->updateContentForEditor($page, $editorType);
+
return [
'page' => $page,
'book' => $page->book,
'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
*
* @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());
*
* @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()));
}
<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>
@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') }}"