diff --git a/CHANGELOG.md b/CHANGELOG.md index 95f63a7527..17cac2cd4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ This is a log of major user-visible changes in each phpMyFAQ release. - added configuration to edit robots.txt (Thorsten) - added Symfony Routing for administration backend (Thorsten) -- WIP: migrated from WYSIWYG editor from TinyMCE to Jodit Editor (Thorsten) +- migrated from WYSIWYG editor from TinyMCE to Jodit Editor (Thorsten) - removed Webpack, now using Vite v6 (Thorsten) - migrated from Jest to vitest (Thorsten) diff --git a/phpmyfaq/admin/assets/src/content/editor.js b/phpmyfaq/admin/assets/src/content/editor.js index 866f4348da..21e7098f7d 100644 --- a/phpmyfaq/admin/assets/src/content/editor.js +++ b/phpmyfaq/admin/assets/src/content/editor.js @@ -1,5 +1,5 @@ /** - * TinyMCE for phpMyFAQ + * Jodit Editor for phpMyFAQ * * This Source Code Form is subject to the terms of the Mozilla Public License, * v. 2.0. If a copy of the MPL was not distributed with this file, You can @@ -38,6 +38,81 @@ import 'jodit/esm/plugins/symbols/symbols.js'; import 'jodit/esm/modules/uploader/uploader.js'; import 'jodit/esm/plugins/video/video.js'; +// Define the phpMyFAQ plugin +Jodit.plugins.add('phpMyFAQ', (editor) => { + // Register the button + editor.registerButton({ + name: 'phpMyFAQ', + }); + + // Register the command + editor.registerCommand('phpMyFAQ', () => { + const dialog = editor.dlg({ closeOnClickOverlay: true }); + + const content = `
+
+ + +
+
+
+
+ +
`; + + dialog.setMod('theme', editor.o.theme).setHeader('phpMyFAQ Plugin').setContent(content); + + dialog.open(); + + const searchInput = document.getElementById('pmf-search-internal-links'); + const resultsContainer = document.getElementById('pmf-search-results'); + const csrfToken = document.getElementById('pmf-csrf-token').value; + const selectLink = document.getElementById('select-faq-button'); + + searchInput.addEventListener('keyup', () => { + const query = searchInput.value; + if (query.length > 0) { + fetch('api/faq/search', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + search: query, + csrf: csrfToken, + }), + }) + .then((response) => response.json()) + .then((data) => { + resultsContainer.innerHTML = ''; + data.success.forEach((result) => { + resultsContainer.innerHTML += `
`; + }); + }) + .catch((error) => console.error('Error:', error)); + } else { + resultsContainer.innerHTML = ''; + } + }); + + selectLink.addEventListener('click', () => { + const selected = document.querySelector('input[name=faqURL]:checked'); + if (selected) { + const url = selected.value; + const question = selected.parentNode.textContent.trim(); + const anchor = `${question}`; + editor.selection.insertHTML(anchor); + dialog.close(); + } else { + alert('Please select an FAQ.'); + } + }); + }); +}); + export const renderEditor = () => { const editor = document.getElementById('editor'); if (!editor) { @@ -158,6 +233,7 @@ export const renderEditor = () => { imageDefaultWidth: 300, removeButtons: [], disablePlugins: [], + extraPlugins: ['phpMyFAQ'], extraButtons: [], buttons: [ 'source', @@ -172,7 +248,6 @@ export const renderEditor = () => { 'superscript', 'subscript', '|', - 'justify', 'outdent', 'indent', '|', @@ -181,13 +256,16 @@ export const renderEditor = () => { 'lineHeight', 'brush', 'paragraph', + 'left', + 'center', + 'right', + 'justify', '|', 'copy', 'cut', 'paste', 'selectall', '|', - 'file', 'image', 'video', 'table', @@ -205,6 +283,8 @@ export const renderEditor = () => { 'fullsize', 'preview', 'print', + '|', + 'phpMyFAQ', ], events: {}, textIcons: false, diff --git a/phpmyfaq/faq.php b/phpmyfaq/faq.php index 416ab214e1..5c7482bda2 100644 --- a/phpmyfaq/faq.php +++ b/phpmyfaq/faq.php @@ -126,8 +126,6 @@ $question = $faq->getQuestion($faqId); if ($faqConfig->get('main.enableMarkdownEditor')) { $answer = $converter->convert($faq->faqRecord['content'])->getContent(); -} else { - $answer = $faqHelper->rewriteLanguageMarkupClass($faq->faqRecord['content']); } // Cleanup answer content first diff --git a/phpmyfaq/src/phpMyFAQ/Helper/FaqHelper.php b/phpmyfaq/src/phpMyFAQ/Helper/FaqHelper.php index 5923b6aa62..058c87c23e 100644 --- a/phpmyfaq/src/phpMyFAQ/Helper/FaqHelper.php +++ b/phpmyfaq/src/phpMyFAQ/Helper/FaqHelper.php @@ -45,14 +45,6 @@ public function __construct(Configuration $configuration) $this->configuration = $configuration; } - /** - * Rewrites the CSS class generated by TinyMCE for HighlightJS. - */ - public function rewriteLanguageMarkupClass(string $answer): string - { - return str_replace('class="language-markup"', 'class="language-html"', $answer); - } - /** * Extends URL fragments (e.g. ) with the full default URL. */ diff --git a/tests/phpMyFAQ/Helper/FaqHelperTest.php b/tests/phpMyFAQ/Helper/FaqHelperTest.php index 81165a0ad5..fefe051640 100644 --- a/tests/phpMyFAQ/Helper/FaqHelperTest.php +++ b/tests/phpMyFAQ/Helper/FaqHelperTest.php @@ -30,14 +30,6 @@ protected function setUp(): void $this->faqHelper = new FaqHelper($this->configuration); } - public function testRewriteLanguageMarkupClass(): void - { - $this->assertEquals( - '
Foobar
', - $this->faqHelper->rewriteLanguageMarkupClass('
Foobar
') - ); - } - public function testRewriteUrlFragments(): void { $content = '
Hello, World';