fixed tag rendering
This commit is contained in:
@@ -133,6 +133,36 @@
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@php
|
||||
$cannedResponseBodies = $cannedResponses->mapWithKeys(function ($response) use ($ticket, $currentUser) {
|
||||
$body = (string) $response->body;
|
||||
|
||||
$body = strtr($body, [
|
||||
'{{ ticket.reference }}' => e((string) ($ticket->reference ?? '')),
|
||||
'{{ ticket.subject }}' => e((string) ($ticket->subject ?? '')),
|
||||
'{{ ticket.status }}' => e((string) ($ticket->status?->name ?? '')),
|
||||
'{{ ticket.priority }}' => e((string) ($ticket->priority?->name ?? '')),
|
||||
'{{ ticket.category }}' => e((string) ($ticket->category?->name ?? '')),
|
||||
'{{ ticket.project }}' => e((string) ($ticket->project?->title ?? '')),
|
||||
'{{ ticket.team }}' => e((string) ($ticket->team?->name ?? '')),
|
||||
'{{ requester.name }}' => e((string) ($ticket->owner?->name ?? $ticket->user?->name ?? '')),
|
||||
'{{ agent.name }}' => e((string) ($ticket->agent?->name ?? $currentUser?->name ?? '')),
|
||||
'{{ actor.name }}' => e((string) ($currentUser?->name ?? '')),
|
||||
'{{ ticket.url }}' => e(route('helpdesk.tickets.show', ['ticket' => $ticket->getKey()])),
|
||||
]);
|
||||
|
||||
$body = html_entity_decode($body, ENT_QUOTES | ENT_HTML5, 'UTF-8');
|
||||
$body = preg_replace('/<\s*(script|style)[^>]*>.*?<\s*\/\s*\1\s*>/is', '', $body) ?: '';
|
||||
$body = preg_replace('/\s+on[a-z]+\s*=\s*("[^"]*"|\'[^\']*\'|[^\s>]+)/i', '', $body) ?: '';
|
||||
$body = preg_replace('/javascript\s*:/i', '', $body) ?: '';
|
||||
$body = strip_tags($body, '<p><br><strong><b><em><i><u><ul><ol><li><blockquote><pre><code><a>');
|
||||
|
||||
return [(string) $response->id => trim($body)];
|
||||
})->all();
|
||||
@endphp
|
||||
|
||||
<script type="application/json" id="helpdesk-canned-response-bodies">@json($cannedResponseBodies)</script>
|
||||
|
||||
@if ($cannedResponses->isNotEmpty())
|
||||
<div class="mb-4 grid gap-3 md:grid-cols-[1fr_auto]">
|
||||
<div>
|
||||
@@ -140,7 +170,7 @@
|
||||
<select id="ticket-canned-response" name="canned_response_id" class="hd-select block w-full rounded-xl border border-zinc-300 bg-white p-2.5 text-sm font-semibold text-zinc-900 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:border-zinc-700 dark:bg-zinc-950 dark:text-zinc-100" data-canned-response-select>
|
||||
<option value="">Choose a saved response...</option>
|
||||
@foreach ($cannedResponses as $response)
|
||||
<option value="{{ $response->id }}" data-body="{{ e($response->body) }}">{{ $response->name }}</option>
|
||||
<option value="{{ $response->id }}">{{ $response->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
@@ -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+$/, '') + '<br><br>' + 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 === '<br>') {
|
||||
editor.innerHTML = cleaned;
|
||||
return;
|
||||
}
|
||||
|
||||
editor.innerHTML = current + '<p><br></p>' + 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('&lt;')) {
|
||||
break;
|
||||
}
|
||||
|
||||
while (decoded.includes('<') || decoded.includes('>') || decoded.includes('&lt;')) {
|
||||
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) => '<p>' + escapeHtml(paragraph).replace(/\n/g, '<br>') + '</p>')
|
||||
.join('');
|
||||
}
|
||||
|
||||
function syncRichEditor(editor) {
|
||||
|
||||
Reference in New Issue
Block a user