]> BookStack Code Mirror - bookstack/commitdiff
Converted the page editor from vue to component
authorDan Brown <redacted>
Sun, 5 Jul 2020 20:18:17 +0000 (21:18 +0100)
committerDan Brown <redacted>
Sun, 5 Jul 2020 20:18:17 +0000 (21:18 +0100)
app/Http/Controllers/PageController.php
resources/js/components/index.js
resources/js/components/markdown-editor.js
resources/js/components/page-editor.js [new file with mode: 0644]
resources/js/components/wysiwyg-editor.js
resources/js/vues/page-editor.js [deleted file]
resources/js/vues/vues.js
resources/views/pages/form.blade.php
resources/views/pages/markdown-editor.blade.php
resources/views/pages/wysiwyg-editor.blade.php
tests/Entity/PageDraftTest.php

index b216c19a8e7689a2e4797f12c9da8e2c92512c0b..57d70fb3247f8177b4879e9f25eecf54dbbfd76d 100644 (file)
@@ -163,6 +163,8 @@ class PageController extends Controller
     public function getPageAjax(int $pageId)
     {
         $page = $this->pageRepo->getById($pageId);
+        $page->setHidden(array_diff($page->getHidden(), ['html', 'markdown']));
+        $page->addHidden(['book']);
         return response()->json($page);
     }
 
index 4908dcd730937d7884a7aaead8b8157522d62ba9..a0269ea61f771422d97f27e49e173681dd5885e9 100644 (file)
@@ -129,7 +129,7 @@ function parseOpts(name, element) {
 function kebabToCamel(kebab) {
     const ucFirst = (word) => word.slice(0,1).toUpperCase() + word.slice(1);
     const words = kebab.split('-');
-    return words[0] + words.slice(1).map(ucFirst).join();
+    return words[0] + words.slice(1).map(ucFirst).join('');
 }
 
 /**
index cc9a7b859ddb9eeb6f540d1b1c545dd8f1820987..6e646c72bbf32af50378697782c73c96f6ee5164 100644 (file)
@@ -8,12 +8,11 @@ import DrawIO from "../services/drawio";
 
 class MarkdownEditor {
 
-    constructor(elem) {
-        this.elem = elem;
+    setup() {
+        this.elem = this.$el;
 
-        const pageEditor = document.getElementById('page-editor');
-        this.pageId = pageEditor.getAttribute('page-id');
-        this.textDirection = pageEditor.getAttribute('text-direction');
+        this.pageId = this.$opts.pageId;
+        this.textDirection = this.$opts.textDirection;
 
         this.markdown = new MarkdownIt({html: true});
         this.markdown.use(mdTasksLists, {label: true});
@@ -27,12 +26,18 @@ class MarkdownEditor {
 
         this.onMarkdownScroll = this.onMarkdownScroll.bind(this);
 
-        this.display.addEventListener('load', () => {
+        const displayLoad = () => {
             this.displayDoc = this.display.contentDocument;
             this.init();
-        });
+        };
+
+        if (this.display.contentDocument.readyState === 'complete') {
+            displayLoad();
+        } else {
+            this.display.addEventListener('load', displayLoad.bind(this));
+        }
 
-        window.$events.emitPublic(elem, 'editor-markdown::setup', {
+        window.$events.emitPublic(this.elem, 'editor-markdown::setup', {
             markdownIt: this.markdown,
             displayEl: this.display,
             codeMirrorInstance: this.cm,
diff --git a/resources/js/components/page-editor.js b/resources/js/components/page-editor.js
new file mode 100644 (file)
index 0000000..4fb472e
--- /dev/null
@@ -0,0 +1,182 @@
+import * as Dates from "../services/dates";
+import {onSelect} from "../services/dom";
+
+/**
+ * Page Editor
+ * @extends {Component}
+ */
+class PageEditor {
+    setup() {
+        // Options
+        this.draftsEnabled = this.$opts.draftsEnabled === 'true';
+        this.editorType = this.$opts.editorType;
+        this.pageId = Number(this.$opts.pageId);
+        this.isNewDraft = this.$opts.pageNewDraft === 'true';
+        this.hasDefaultTitle = this.$opts.isDefaultTitle || false;
+
+        // Elements
+        this.container = this.$el;
+        this.titleElem = this.$refs.titleContainer.querySelector('input');
+        this.saveDraftButton = this.$refs.saveDraft;
+        this.discardDraftButton = this.$refs.discardDraft;
+        this.discardDraftWrap = this.$refs.discardDraftWrap;
+        this.draftDisplay = this.$refs.draftDisplay;
+        this.draftDisplayIcon = this.$refs.draftDisplayIcon;
+        this.changelogInput = this.$refs.changelogInput;
+        this.changelogDisplay = this.$refs.changelogDisplay;
+
+        // Translations
+        this.draftText = this.$opts.draftText;
+        this.autosaveFailText = this.$opts.autosaveFailText;
+        this.editingPageText = this.$opts.editingPageText;
+        this.draftDiscardedText = this.$opts.draftDiscardedText;
+        this.setChangelogText = this.$opts.setChangelogText;
+
+        // State data
+        this.editorHTML = '';
+        this.editorMarkdown = '';
+        this.autoSave = {
+            interval: null,
+            frequency: 30000,
+            last: 0,
+        };
+        this.draftHasError = false;
+
+        if (this.pageId !== 0 && this.draftsEnabled) {
+            window.setTimeout(() => {
+                this.startAutoSave();
+            }, 1000);
+        }
+        this.draftDisplay.innerHTML = this.draftText;
+
+        this.setupListeners();
+        this.setInitialFocus();
+    }
+
+    setupListeners() {
+        // Listen to save events from editor
+        window.$events.listen('editor-save-draft', this.saveDraft.bind(this));
+        window.$events.listen('editor-save-page', this.savePage.bind(this));
+
+        // Listen to content changes from the editor
+        window.$events.listen('editor-html-change', html => {
+            this.editorHTML = html;
+        });
+        window.$events.listen('editor-markdown-change', markdown => {
+            this.editorMarkdown = markdown;
+        });
+
+        // Changelog controls
+        this.changelogInput.addEventListener('change', this.updateChangelogDisplay.bind(this));
+
+        // Draft Controls
+        onSelect(this.saveDraftButton, this.saveDraft.bind(this));
+        onSelect(this.discardDraftButton, this.discardDraft.bind(this));
+    }
+
+    setInitialFocus() {
+        if (this.hasDefaultTitle) {
+            return this.titleElem.select();
+        }
+
+        window.setTimeout(() => {
+            window.$events.emit('editor::focus', '');
+        }, 500);
+    }
+
+    startAutoSave() {
+        let lastContent = this.titleElem.value.trim() + '::' + this.editorHTML;
+        this.autoSaveInterval = window.setInterval(() => {
+            // Stop if manually saved recently to prevent bombarding the server
+            let savedRecently = (Date.now() - this.autoSave.last < (this.autoSave.frequency)/2);
+            if (savedRecently) return;
+            const newContent = this.titleElem.value.trim() + '::' + this.editorHTML;
+            if (newContent !== lastContent) {
+                lastContent = newContent;
+                this.saveDraft();
+            }
+
+        }, this.autoSave.frequency);
+    }
+
+    savePage() {
+        this.container.closest('form').submit();
+    }
+
+    async saveDraft() {
+        const data = {
+            name: this.titleElem.value.trim(),
+            html: this.editorHTML,
+        };
+
+        if (this.editorType === 'markdown') {
+            data.markdown = this.editorMarkdown;
+        }
+
+        try {
+            const resp = await window.$http.put(`/ajax/page/${this.pageId}/save-draft`, data);
+            this.draftHasError = false;
+            if (!this.isNewDraft) {
+                this.toggleDiscardDraftVisibility(true);
+            }
+            this.draftNotifyChange(`${resp.data.message} ${Dates.utcTimeStampToLocalTime(resp.data.timestamp)}`);
+            this.autoSave.last = Date.now();
+        } catch (err) {
+            if (!this.draftHasError) {
+                this.draftHasError = true;
+                window.$events.emit('error', this.autosaveFailText);
+            }
+        }
+
+    }
+
+    draftNotifyChange(text) {
+        this.draftDisplay.innerText = text;
+        this.draftDisplayIcon.classList.add('visible');
+        window.setTimeout(() => {
+            this.draftDisplayIcon.classList.remove('visible');
+        }, 2000);
+    }
+
+    async discardDraft() {
+        let response;
+        try {
+            response = await window.$http.get(`/ajax/page/${this.pageId}`);
+        } catch (e) {
+            return console.error(e);
+        }
+
+        if (this.autoSave.interval) {
+            window.clearInterval(this.autoSave.interval);
+        }
+
+        this.draftDisplay.innerText = this.editingPageText;
+        this.toggleDiscardDraftVisibility(false);
+        window.$events.emit('editor-html-update', response.data.html || '');
+        window.$events.emit('editor-markdown-update', response.data.markdown || response.data.html);
+
+        this.titleElem.value = response.data.name;
+        window.setTimeout(() => {
+            this.startAutoSave();
+        }, 1000);
+        window.$events.emit('success', this.draftDiscardedText);
+
+    }
+
+    updateChangelogDisplay() {
+        let summary = this.changelogInput.value.trim();
+        if (summary.length === 0) {
+            summary = this.setChangelogText;
+        } else if (summary.length > 16) {
+            summary = summary.slice(0, 16) + '...';
+        }
+        this.changelogDisplay.innerText = summary;
+    }
+
+    toggleDiscardDraftVisibility(show) {
+        this.discardDraftWrap.classList.toggle('hidden', !show);
+    }
+
+}
+
+export default PageEditor;
\ No newline at end of file
index 5956b5e7a421145f52bbb98672f6aa4ce4e438f3..5e3ce8d969f5469bb16b4c05802fb948ac442e78 100644 (file)
@@ -236,7 +236,7 @@ function codePlugin() {
     });
 }
 
-function drawIoPlugin(drawioUrl, isDarkMode) {
+function drawIoPlugin(drawioUrl, isDarkMode, pageId) {
 
     let pageEditor = null;
     let currentNode = null;
@@ -270,7 +270,6 @@ function drawIoPlugin(drawioUrl, isDarkMode) {
     async function updateContent(pngData) {
         const id = "image-" + Math.random().toString(16).slice(2);
         const loadingImage = window.baseUrl('/loading.gif');
-        const pageId = Number(document.getElementById('page-editor').getAttribute('page-id'));
 
         // Handle updating an existing image
         if (currentNode) {
@@ -410,19 +409,19 @@ function listenForBookStackEditorEvents(editor) {
 
 class WysiwygEditor {
 
-    constructor(elem) {
-        this.elem = elem;
 
-        const pageEditor = document.getElementById('page-editor');
-        this.pageId = pageEditor.getAttribute('page-id');
-        this.textDirection = pageEditor.getAttribute('text-direction');
+    setup() {
+        this.elem = this.$el;
+
+        this.pageId = this.$opts.pageId;
+        this.textDirection = this.$opts.textDirection;
         this.isDarkMode = document.documentElement.classList.contains('dark-mode');
 
         this.plugins = "image table textcolor paste link autolink fullscreen imagetools code customhr autosave lists codeeditor media";
         this.loadPlugins();
 
         this.tinyMceConfig = this.getTinyMceConfig();
-        window.$events.emitPublic(elem, 'editor-tinymce::pre-init', {config: this.tinyMceConfig});
+        window.$events.emitPublic(this.elem, 'editor-tinymce::pre-init', {config: this.tinyMceConfig});
         window.tinymce.init(this.tinyMceConfig);
     }
 
@@ -433,7 +432,7 @@ class WysiwygEditor {
         const drawioUrlElem = document.querySelector('[drawio-url]');
         if (drawioUrlElem) {
             const url = drawioUrlElem.getAttribute('drawio-url');
-            drawIoPlugin(url, this.isDarkMode);
+            drawIoPlugin(url, this.isDarkMode, this.pageId);
             this.plugins += ' drawio';
         }
 
diff --git a/resources/js/vues/page-editor.js b/resources/js/vues/page-editor.js
deleted file mode 100644 (file)
index a79ad20..0000000
+++ /dev/null
@@ -1,167 +0,0 @@
-import * as Dates from "../services/dates";
-
-let autoSaveFrequency = 30;
-
-let autoSave = false;
-let draftErroring = false;
-
-let currentContent = {
-    title: false,
-    html: false
-};
-
-let lastSave = 0;
-
-function mounted() {
-    let elem = this.$el;
-    this.draftsEnabled = elem.getAttribute('drafts-enabled') === 'true';
-    this.editorType = elem.getAttribute('editor-type');
-    this.pageId= Number(elem.getAttribute('page-id'));
-    this.isNewDraft = Number(elem.getAttribute('page-new-draft')) === 1;
-    this.isUpdateDraft = Number(elem.getAttribute('page-update-draft')) === 1;
-    this.titleElem = elem.querySelector('input[name=name]');
-    this.hasDefaultTitle = this.titleElem.closest('[is-default-value]') !== null;
-
-    if (this.pageId !== 0 && this.draftsEnabled) {
-        window.setTimeout(() => {
-            this.startAutoSave();
-        }, 1000);
-    }
-
-    if (this.isUpdateDraft || this.isNewDraft) {
-        this.draftText = trans('entities.pages_editing_draft');
-    } else {
-        this.draftText = trans('entities.pages_editing_page');
-    }
-
-    // Listen to save events from editor
-    window.$events.listen('editor-save-draft', this.saveDraft);
-    window.$events.listen('editor-save-page', this.savePage);
-
-    // Listen to content changes from the editor
-    window.$events.listen('editor-html-change', html => {
-        this.editorHTML = html;
-    });
-    window.$events.listen('editor-markdown-change', markdown => {
-        this.editorMarkdown = markdown;
-    });
-
-    this.setInitialFocus();
-}
-
-let data = {
-    draftsEnabled: false,
-    editorType: 'wysiwyg',
-    pagedId: 0,
-    isNewDraft: false,
-    isUpdateDraft: false,
-
-    draftText: '',
-    draftUpdated : false,
-    changeSummary: '',
-
-    editorHTML: '',
-    editorMarkdown: '',
-
-    hasDefaultTitle: false,
-    titleElem: null,
-};
-
-let methods = {
-
-    setInitialFocus() {
-        if (this.hasDefaultTitle) {
-            this.titleElem.select();
-        } else {
-            window.setTimeout(() => {
-                this.$events.emit('editor::focus', '');
-            }, 500);
-        }
-    },
-
-    startAutoSave() {
-        currentContent.title = this.titleElem.value.trim();
-        currentContent.html = this.editorHTML;
-
-        autoSave = window.setInterval(() => {
-            // Return if manually saved recently to prevent bombarding the server
-            if (Date.now() - lastSave < (1000 * autoSaveFrequency)/2) return;
-            const newTitle = this.titleElem.value.trim();
-            const newHtml = this.editorHTML;
-
-            if (newTitle !== currentContent.title || newHtml !== currentContent.html) {
-                currentContent.html = newHtml;
-                currentContent.title = newTitle;
-                this.saveDraft();
-            }
-
-        }, 1000 * autoSaveFrequency);
-    },
-
-    saveDraft() {
-        if (!this.draftsEnabled) return;
-
-        const data = {
-            name: this.titleElem.value.trim(),
-            html: this.editorHTML
-        };
-
-        if (this.editorType === 'markdown') data.markdown = this.editorMarkdown;
-
-        const url = window.baseUrl(`/ajax/page/${this.pageId}/save-draft`);
-        window.$http.put(url, data).then(response => {
-            draftErroring = false;
-            if (!this.isNewDraft) this.isUpdateDraft = true;
-            this.draftNotifyChange(`${response.data.message} ${Dates.utcTimeStampToLocalTime(response.data.timestamp)}`);
-            lastSave = Date.now();
-        }, errorRes => {
-            if (draftErroring) return;
-            window.$events.emit('error', trans('errors.page_draft_autosave_fail'));
-            draftErroring = true;
-        });
-    },
-
-    savePage() {
-        this.$el.closest('form').submit();
-    },
-
-    draftNotifyChange(text) {
-        this.draftText = text;
-        this.draftUpdated = true;
-        window.setTimeout(() => {
-            this.draftUpdated = false;
-        }, 2000);
-    },
-
-    discardDraft() {
-        let url = window.baseUrl(`/ajax/page/${this.pageId}`);
-        window.$http.get(url).then(response => {
-            if (autoSave) window.clearInterval(autoSave);
-
-            this.draftText = trans('entities.pages_editing_page');
-            this.isUpdateDraft = false;
-            window.$events.emit('editor-html-update', response.data.html);
-            window.$events.emit('editor-markdown-update', response.data.markdown || response.data.html);
-
-            this.titleElem.value = response.data.name;
-            window.setTimeout(() => {
-                this.startAutoSave();
-            }, 1000);
-            window.$events.emit('success', trans('entities.pages_draft_discarded'));
-        });
-    },
-
-};
-
-let computed = {
-    changeSummaryShort() {
-        let len = this.changeSummary.length;
-        if (len === 0) return trans('entities.pages_edit_set_changelog');
-        if (len <= 16) return this.changeSummary;
-        return this.changeSummary.slice(0, 16) + '...';
-    }
-};
-
-export default {
-    mounted, data, methods, computed,
-};
\ No newline at end of file
index d0bd529efe309a4e0878f329fb7b60159d8e21de..faa191b95952559d8d605c1764df2a99caa4da95 100644 (file)
@@ -5,11 +5,9 @@ function exists(id) {
 }
 
 import imageManager from "./image-manager";
-import pageEditor from "./page-editor";
 
 let vueMapping = {
     'image-manager': imageManager,
-    'page-editor': pageEditor,
 };
 
 window.vues = {};
index 47a9369ce5f5620e1ad91d9577bb07eb8e75dff2..d153aed99af71e0598ecd4a7aa443a85e619159f 100644 (file)
@@ -1,22 +1,19 @@
-<div class="page-editor flex-fill flex" id="page-editor"
-     drafts-enabled="{{ $draftsEnabled ? 'true' : 'false' }}"
+<div component="page-editor" class="page-editor flex-fill flex"
+     option:page-editor:drafts-enabled="{{ $draftsEnabled ? 'true' : 'false' }}"
      @if(config('services.drawio'))
         drawio-url="{{ is_string(config('services.drawio')) ? config('services.drawio') : 'https://www.draw.io/?embed=1&proto=json&spin=1' }}"
      @endif
-     editor-type="{{ setting('app-editor') }}"
-     page-id="{{ $model->id ?? 0 }}"
-     text-direction="{{ config('app.rtl') ? 'rtl' : 'ltr' }}"
-     page-new-draft="{{ $model->draft ?? 0 }}"
-     page-update-draft="{{ $model->isDraft ?? 0 }}">
-
-    @exposeTranslations([
-        'entities.pages_editing_draft',
-        'entities.pages_editing_page',
-        'errors.page_draft_autosave_fail',
-        'entities.pages_editing_page',
-        'entities.pages_draft_discarded',
-        'entities.pages_edit_set_changelog',
-    ])
+     @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:page-id="{{ $model->id ?? '0' }}"
+     option:page-editor:page-new-draft="{{ ($model->draft ?? false) ? 'true' : 'false' }}"
+     option:page-editor:draft-text="{{ ($model->draft || $model->isDraft) ? trans('entities.pages_editing_draft') : trans('entities.pages_editing_page') }}"
+     option:page-editor:autosave-fail-text="{{ trans('errors.page_draft_autosave_fail') }}"
+     option:page-editor:editing-page-text="{{ trans('entities.pages_editing_page') }}"
+     option:page-editor:draft-discarded-text="{{ trans('entities.pages_draft_discarded') }}"
+     option:page-editor:set-changelog-text="{{ trans('entities.pages_edit_set_changelog') }}">
 
     {{--Header Bar--}}
     <div class="primary-background-light toolbar page-edit-toolbar">
             </div>
 
             <div class="text-center px-m py-xs">
-                <div v-show="draftsEnabled"
-                     component="dropdown"
+                <div component="dropdown"
                      option:dropdown:move-menu="true"
-                     class="dropdown-container draft-display text">
-                    <button type="button" refs="dropdown@toggle" aria-haspopup="true" aria-expanded="false" title="{{ trans('entities.pages_edit_draft_options') }}" class="text-primary text-button"><span class="faded-text" v-text="draftText"></span>&nbsp; @icon('more')</button>
-                    @icon('check-circle', ['class' => 'text-pos draft-notification svg-icon', ':class' => '{visible: draftUpdated}'])
+                     class="dropdown-container draft-display text {{ $draftsEnabled ? '' : 'hidden' }}">
+                    <button type="button" refs="dropdown@toggle" aria-haspopup="true" aria-expanded="false" title="{{ trans('entities.pages_edit_draft_options') }}" class="text-primary text-button"><span refs="page-editor@draftDisplay" class="faded-text"></span>&nbsp; @icon('more')</button>
+                    @icon('check-circle', ['class' => 'text-pos draft-notification svg-icon', 'refs' => 'page-editor@draftDisplayIcon'])
                     <ul refs="dropdown@menu" class="dropdown-menu" role="menu">
                         <li>
-                            <button type="button" @click="saveDraft()" class="text-pos">@icon('save'){{ trans('entities.pages_edit_save_draft') }}</button>
+                            <button refs="page-editor@saveDraft" type="button" class="text-pos">@icon('save'){{ trans('entities.pages_edit_save_draft') }}</button>
                         </li>
-                        <li v-if="isNewDraft">
+                        @if ($model->draft)
+                        <li>
                             <a href="{{ $model->getUrl('/delete') }}" class="text-neg">@icon('delete'){{ trans('entities.pages_edit_delete_draft') }}</a>
                         </li>
-                        <li v-if="isUpdateDraft">
-                            <button type="button" @click="discardDraft" class="text-neg">@icon('cancel'){{ trans('entities.pages_edit_discard_draft') }}</button>
+                        @endif
+                        <li refs="page-editor@discardDraftWrap" class="{{ ($model->isDraft ?? false) ? '' : 'hidden' }}">
+                            <button refs="page-editor@discardDraft" type="button" class="text-neg">@icon('cancel'){{ trans('entities.pages_edit_discard_draft') }}</button>
                         </li>
                     </ul>
                 </div>
             </div>
 
-            <div class="action-buttons px-m py-xs" v-cloak>
+            <div class="action-buttons px-m py-xs">
                 <div component="dropdown" dropdown-move-menu class="dropdown-container">
-                    <button refs="dropdown@toggle" type="button" aria-haspopup="true" aria-expanded="false" class="text-primary text-button">@icon('edit') <span v-text="changeSummaryShort"></span></button>
+                    <button refs="dropdown@toggle" type="button" aria-haspopup="true" aria-expanded="false" class="text-primary text-button">@icon('edit') <span refs="page-editor@changelogDisplay">{{ trans('entities.pages_edit_set_changelog') }}</span></button>
                     <ul refs="dropdown@menu" class="wide dropdown-menu">
                         <li class="px-l py-m">
                             <p class="text-muted pb-s">{{ trans('entities.pages_edit_enter_changelog_desc') }}</p>
-                            <input name="summary" id="summary-input" type="text" placeholder="{{ trans('entities.pages_edit_enter_changelog') }}" v-model="changeSummary" />
+                            <input refs="page-editor@changelogInput"
+                                   name="summary"
+                                   id="summary-input"
+                                   type="text"
+                                   placeholder="{{ trans('entities.pages_edit_enter_changelog') }}" />
                         </li>
                     </ul>
                     <span>{{-- Prevents button jumping on menu show --}}</span>
@@ -65,8 +67,8 @@
     </div>
 
     {{--Title input--}}
-    <div class="title-input page-title clearfix" v-pre>
-        <div class="input" @if($model->name === trans('entities.pages_initial_name')) is-default-value @endif>
+    <div class="title-input page-title clearfix">
+        <div refs="page-editor@titleContainer" class="input">
             @include('form.text', ['name' => 'name', 'model' => $model, 'placeholder' => trans('entities.pages_title')])
         </div>
     </div>
@@ -86,5 +88,8 @@
 
     </div>
 
-    <button type="submit" id="save-button-mobile" title="{{ trans('entities.pages_save') }}" class="text-primary text-button hide-over-m page-save-mobile-button">@icon('save')</button>
+    <button type="submit"
+            id="save-button-mobile"
+            title="{{ trans('entities.pages_save') }}"
+            class="text-primary text-button hide-over-m page-save-mobile-button">@icon('save')</button>
 </div>
\ No newline at end of file
index 85afbea069ab4726c6588971445e439d65ebb5d2..a004dbd8be7024226d12d8b7d9b68c37a79b6359 100644 (file)
@@ -1,4 +1,7 @@
-<div v-pre id="markdown-editor" markdown-editor class="flex-fill flex code-fill">
+<div id="markdown-editor" component="markdown-editor"
+     option:markdown-editor:page-id="{{ $model->id ?? 0 }}"
+     option:markdown-editor:text-direction="{{ config('app.rtl') ? 'rtl' : 'ltr' }}"
+     class="flex-fill flex code-fill">
     @exposeTranslations([
         'errors.image_upload_error',
     ])
index 1a67ee76f2d0992f7eb552878359f6d151db3b51..2804612cd1a926fced06c706304d21c6154d0b2f 100644 (file)
@@ -1,4 +1,7 @@
-<div wysiwyg-editor class="flex-fill flex">
+<div component="wysiwyg-editor"
+     option:wysiwyg-editor:page-id="{{ $model->id ?? 0 }}"
+     option:wysiwyg-editor:text-direction="{{ config('app.rtl') ? 'rtl' : 'ltr' }}"
+     class="flex-fill flex">
 
     @exposeTranslations([
         'errors.image_upload_error',
index 5c984940d32569ce3e0b5d1cdce60bef2bf9d1cb..a0cf9e5fca9267aa6a8866f00f9e940e87b27ece 100644 (file)
@@ -1,5 +1,6 @@
 <?php namespace Tests\Entity;
 
+use BookStack\Entities\Page;
 use BookStack\Entities\Repos\PageRepo;
 use Tests\BrowserKitTest;
 
@@ -101,4 +102,15 @@ class PageDraftTest extends BrowserKitTest
             ->dontSeeInElement('.book-contents', 'New Page');
     }
 
+    public function test_page_html_in_ajax_fetch_response()
+    {
+        $this->asAdmin();
+        $page = Page::query()->first();
+
+        $this->getJson('/ajax/page/' . $page->id);
+        $this->seeJson([
+            'html' => $page->html,
+        ]);
+    }
+
 }