@@ -140,7 +170,7 @@
@@ -212,12 +242,11 @@
const editor = form?.querySelector('[data-helpdesk-rich-editor]');
const textarea = form?.querySelector('textarea[name="commentBody"]');
const selected = select?.selectedOptions?.[0];
- const body = normaliseCannedHtml(selected?.dataset?.body || '');
+ const cannedResponses = getCannedResponseBodies();
+ const body = normaliseCannedHtml(cannedResponses[String(selected?.value || '')] || selected?.dataset?.body || '');
if (editor && textarea && body.trim() !== '') {
- editor.innerHTML = editor.innerHTML.trim() === ''
- ? body
- : editor.innerHTML.replace(/\s+$/, '') + '
' + body;
+ insertHtmlAtEnd(editor, body);
syncRichEditor(editor);
editor.focus();
}
@@ -343,6 +372,33 @@
collapseButton.classList.toggle('inline-flex', hasExpandedComments && ! hasExpandableComments);
}
+ function getCannedResponseBodies() {
+ const source = document.getElementById('helpdesk-canned-response-bodies');
+
+ if (! source) {
+ return {};
+ }
+
+ try {
+ return JSON.parse(source.textContent || '{}') || {};
+ } catch (error) {
+ console.error('Unable to read canned response bodies.', error);
+ return {};
+ }
+ }
+
+ function insertHtmlAtEnd(editor, html) {
+ const current = editor.innerHTML.trim();
+ const cleaned = html.trim();
+
+ if (current === '' || current === '
') {
+ editor.innerHTML = cleaned;
+ return;
+ }
+
+ editor.innerHTML = current + '
' + cleaned;
+ }
+
function decodeHtmlEntities(value) {
const textarea = document.createElement('textarea');
textarea.innerHTML = value;
@@ -350,9 +406,13 @@
}
function normaliseCannedHtml(value) {
- let decoded = decodeHtmlEntities(value);
+ let decoded = decodeHtmlEntities(value || '');
+
+ for (let i = 0; i < 5; i += 1) {
+ if (! decoded.includes('<') && ! decoded.includes('>') && ! decoded.includes('<')) {
+ break;
+ }
- while (decoded.includes('<') || decoded.includes('>') || decoded.includes('<')) {
const next = decodeHtmlEntities(decoded);
if (next === decoded) {
@@ -362,7 +422,22 @@
decoded = next;
}
- return decoded;
+ decoded = decoded.trim();
+
+ if (decoded === '') {
+ return '';
+ }
+
+ if (/<[a-z][\s\S]*>/i.test(decoded)) {
+ return decoded;
+ }
+
+ return decoded
+ .split(/\n{2,}/)
+ .map((paragraph) => paragraph.trim())
+ .filter((paragraph) => paragraph !== '')
+ .map((paragraph) => '
' + escapeHtml(paragraph).replace(/\n/g, '
') + '
')
+ .join('');
}
function syncRichEditor(editor) {
diff --git a/Modules/Helpdesk/Tests/Browser/HelpdeskTicketBrowserTest.php b/Modules/Helpdesk/Tests/Browser/HelpdeskTicketBrowserTest.php
index 92ea184c..d9010c0a 100644
--- a/Modules/Helpdesk/Tests/Browser/HelpdeskTicketBrowserTest.php
+++ b/Modules/Helpdesk/Tests/Browser/HelpdeskTicketBrowserTest.php
@@ -64,7 +64,7 @@ final class HelpdeskTicketBrowserTest extends DuskTestCase
'name' => 'Browser canned response',
'slug' => 'browser-canned-response',
'visibility' => 'agents',
- 'body' => '
Inserted canned browser response
',
+ 'body' => '
Hello {{ requester.name }}
Inserted canned browser response for {{ ticket.reference }}
',
'is_active' => true,
'sort_order' => 1,
]);
@@ -76,19 +76,19 @@ final class HelpdeskTicketBrowserTest extends DuskTestCase
->assertSee('Browser response ticket')
->select('canned_response_id', (string) $cannedResponse->id)
->press('@insert-canned-response')
- ->assertInputValue('commentBody', '
Inserted canned browser response
');
-
- $this->setHelpdeskEditor($browser, 'ticket-comment-editor', '
First browser response
');
-
- $browser
+ ->waitForText('Hello Response Browser User', 10)
+ ->assertSee('Inserted canned browser response')
+ ->assertDontSee('
Hello')
+ ->assertInputValue('commentBody', '
Hello Response Browser User
Inserted canned browser response for '.$ticket->reference.'
')
->press('@post-comment')
- ->waitForText('First browser response', 10)
- ->assertSee('First browser response');
+ ->waitForText('Hello Response Browser User', 10)
+ ->assertSee('Inserted canned browser response')
+ ->assertDontSee('
Hello');
$comment = Comment::query()
->where('ticket_id', $ticket->id)
->whereNull('parent_id')
- ->where('body', '
First browser response
')
+ ->where('body', 'like', '%Hello Response Browser User%')
->firstOrFail();
$browser->press('@reply-to-comment-'.$comment->id)
@@ -102,13 +102,13 @@ final class HelpdeskTicketBrowserTest extends DuskTestCase
$this->assertDatabaseHas('helpdesk_comments', [
'ticket_id' => $ticket->id,
'parent_id' => null,
- 'body' => '
First browser response
',
+ 'body' => '
Hello Response Browser User
Inserted canned browser response for '.$ticket->reference.'
',
]);
$parentId = Comment::query()
->where('ticket_id', $ticket->id)
->whereNull('parent_id')
- ->where('body', '
First browser response
')
+ ->where('body', 'like', '%Inserted canned browser response%')
->value('id');
$this->assertDatabaseHas('helpdesk_comments', [