Skip to content

Commit 2acc2ea

Browse files
committed
feat(editor): added phpMyFAQ internal link plugin, closes #2896
1 parent b9510e5 commit 2acc2ea

File tree

5 files changed

+84
-22
lines changed

5 files changed

+84
-22
lines changed

CHANGELOG.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ This is a log of major user-visible changes in each phpMyFAQ release.
1010

1111
- added configuration to edit robots.txt (Thorsten)
1212
- added Symfony Routing for administration backend (Thorsten)
13-
- WIP: migrated from WYSIWYG editor from TinyMCE to Jodit Editor (Thorsten)
13+
- migrated from WYSIWYG editor from TinyMCE to Jodit Editor (Thorsten)
1414
- removed Webpack, now using Vite v6 (Thorsten)
1515
- migrated from Jest to vitest (Thorsten)
1616

phpmyfaq/admin/assets/src/content/editor.js

+83-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* TinyMCE for phpMyFAQ
2+
* Jodit Editor for phpMyFAQ
33
*
44
* This Source Code Form is subject to the terms of the Mozilla Public License,
55
* 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';
3838
import 'jodit/esm/modules/uploader/uploader.js';
3939
import 'jodit/esm/plugins/video/video.js';
4040

41+
// Define the phpMyFAQ plugin
42+
Jodit.plugins.add('phpMyFAQ', (editor) => {
43+
// Register the button
44+
editor.registerButton({
45+
name: 'phpMyFAQ',
46+
});
47+
48+
// Register the command
49+
editor.registerCommand('phpMyFAQ', () => {
50+
const dialog = editor.dlg({ closeOnClickOverlay: true });
51+
52+
const content = `<form class="row row-cols-lg-auto g-3 align-items-center m-4">
53+
<div class="col-12">
54+
<label class="visually-hidden" for="pmf-search-internal-links">Search</label>
55+
<input type="text" class="form-control" id="pmf-search-internal-links" placeholder="Search">
56+
</div>
57+
</form>
58+
<div class="m-4" id="pmf-search-results"></div>
59+
<div class="m-4">
60+
<button type="button" class="btn btn-primary" id="select-faq-button">Select FAQ</button>
61+
</div>`;
62+
63+
dialog.setMod('theme', editor.o.theme).setHeader('phpMyFAQ Plugin').setContent(content);
64+
65+
dialog.open();
66+
67+
const searchInput = document.getElementById('pmf-search-internal-links');
68+
const resultsContainer = document.getElementById('pmf-search-results');
69+
const csrfToken = document.getElementById('pmf-csrf-token').value;
70+
const selectLink = document.getElementById('select-faq-button');
71+
72+
searchInput.addEventListener('keyup', () => {
73+
const query = searchInput.value;
74+
if (query.length > 0) {
75+
fetch('api/faq/search', {
76+
method: 'POST',
77+
headers: {
78+
'Content-Type': 'application/json',
79+
},
80+
body: JSON.stringify({
81+
search: query,
82+
csrf: csrfToken,
83+
}),
84+
})
85+
.then((response) => response.json())
86+
.then((data) => {
87+
resultsContainer.innerHTML = '';
88+
data.success.forEach((result) => {
89+
resultsContainer.innerHTML += `<label class="form-check-label">
90+
<input class="form-check-input" type="radio" name="faqURL" value="${result.url}">
91+
${result.question}
92+
</label><br>`;
93+
});
94+
})
95+
.catch((error) => console.error('Error:', error));
96+
} else {
97+
resultsContainer.innerHTML = '';
98+
}
99+
});
100+
101+
selectLink.addEventListener('click', () => {
102+
const selected = document.querySelector('input[name=faqURL]:checked');
103+
if (selected) {
104+
const url = selected.value;
105+
const question = selected.parentNode.textContent.trim();
106+
const anchor = `<a href="${url}">${question}</a>`;
107+
editor.selection.insertHTML(anchor);
108+
dialog.close();
109+
} else {
110+
alert('Please select an FAQ.');
111+
}
112+
});
113+
});
114+
});
115+
41116
export const renderEditor = () => {
42117
const editor = document.getElementById('editor');
43118
if (!editor) {
@@ -158,6 +233,7 @@ export const renderEditor = () => {
158233
imageDefaultWidth: 300,
159234
removeButtons: [],
160235
disablePlugins: [],
236+
extraPlugins: ['phpMyFAQ'],
161237
extraButtons: [],
162238
buttons: [
163239
'source',
@@ -172,7 +248,6 @@ export const renderEditor = () => {
172248
'superscript',
173249
'subscript',
174250
'|',
175-
'justify',
176251
'outdent',
177252
'indent',
178253
'|',
@@ -181,13 +256,16 @@ export const renderEditor = () => {
181256
'lineHeight',
182257
'brush',
183258
'paragraph',
259+
'left',
260+
'center',
261+
'right',
262+
'justify',
184263
'|',
185264
'copy',
186265
'cut',
187266
'paste',
188267
'selectall',
189268
'|',
190-
'file',
191269
'image',
192270
'video',
193271
'table',
@@ -205,6 +283,8 @@ export const renderEditor = () => {
205283
'fullsize',
206284
'preview',
207285
'print',
286+
'|',
287+
'phpMyFAQ',
208288
],
209289
events: {},
210290
textIcons: false,

phpmyfaq/faq.php

-2
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,6 @@
126126
$question = $faq->getQuestion($faqId);
127127
if ($faqConfig->get('main.enableMarkdownEditor')) {
128128
$answer = $converter->convert($faq->faqRecord['content'])->getContent();
129-
} else {
130-
$answer = $faqHelper->rewriteLanguageMarkupClass($faq->faqRecord['content']);
131129
}
132130

133131
// Cleanup answer content first

phpmyfaq/src/phpMyFAQ/Helper/FaqHelper.php

-8
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,6 @@ public function __construct(Configuration $configuration)
4545
$this->configuration = $configuration;
4646
}
4747

48-
/**
49-
* Rewrites the CSS class generated by TinyMCE for HighlightJS.
50-
*/
51-
public function rewriteLanguageMarkupClass(string $answer): string
52-
{
53-
return str_replace('class="language-markup"', 'class="language-html"', $answer);
54-
}
55-
5648
/**
5749
* Extends URL fragments (e.g. <a href="#foo">) with the full default URL.
5850
*/

tests/phpMyFAQ/Helper/FaqHelperTest.php

-8
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,6 @@ protected function setUp(): void
3030
$this->faqHelper = new FaqHelper($this->configuration);
3131
}
3232

33-
public function testRewriteLanguageMarkupClass(): void
34-
{
35-
$this->assertEquals(
36-
'<div class="language-html">Foobar</div>',
37-
$this->faqHelper->rewriteLanguageMarkupClass('<div class="language-markup">Foobar</div>')
38-
);
39-
}
40-
4133
public function testRewriteUrlFragments(): void
4234
{
4335
$content = '<a href="#Foobar">Hello, World</a>';

0 commit comments

Comments
 (0)