Added parseAsText to mention parser. Fix multiple email recipients
This commit is contained in:
parent
ccbf9faa3c
commit
ef12c82fe5
|
|
@ -116,7 +116,7 @@ class PublicFormController extends Controller
|
||||||
return ['id' => $key, 'value' => $value];
|
return ['id' => $key, 'value' => $value];
|
||||||
})->values()->all();
|
})->values()->all();
|
||||||
|
|
||||||
$redirectUrl = ($form->redirect_url) ? (new MentionParser($form->redirect_url, $formattedData))->parse() : null;
|
$redirectUrl = ($form->redirect_url) ? (new MentionParser($form->redirect_url, $formattedData))->parseAsText() : null;
|
||||||
|
|
||||||
if ($redirectUrl && !filter_var($redirectUrl, FILTER_VALIDATE_URL)) {
|
if ($redirectUrl && !filter_var($redirectUrl, FILTER_VALIDATE_URL)) {
|
||||||
$redirectUrl = null;
|
$redirectUrl = null;
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ class EmailIntegration extends AbstractEmailIntegrationHandler
|
||||||
if ($this->form->is_pro) { // For Send to field Mentions are Pro feature
|
if ($this->form->is_pro) { // For Send to field Mentions are Pro feature
|
||||||
$formatter = (new FormSubmissionFormatter($this->form, $this->submissionData))->outputStringsOnly();
|
$formatter = (new FormSubmissionFormatter($this->form, $this->submissionData))->outputStringsOnly();
|
||||||
$parser = new MentionParser($this->integrationData?->send_to, $formatter->getFieldsWithValue());
|
$parser = new MentionParser($this->integrationData?->send_to, $formatter->getFieldsWithValue());
|
||||||
$sendTo = $parser->parse();
|
$sendTo = $parser->parseAsText();
|
||||||
} else {
|
} else {
|
||||||
$sendTo = $this->integrationData?->send_to;
|
$sendTo = $this->integrationData?->send_to;
|
||||||
}
|
}
|
||||||
|
|
@ -73,6 +73,7 @@ class EmailIntegration extends AbstractEmailIntegrationHandler
|
||||||
'form_slug' => $this->form->slug,
|
'form_slug' => $this->form->slug,
|
||||||
'mailer' => $this->mailer
|
'mailer' => $this->mailer
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$recipients->each(function ($subscriber) {
|
$recipients->each(function ($subscriber) {
|
||||||
Notification::route('mail', $subscriber)->notify(
|
Notification::route('mail', $subscriber)->notify(
|
||||||
new FormEmailNotification($this->event, $this->integrationData, $this->mailer)
|
new FormEmailNotification($this->event, $this->integrationData, $this->mailer)
|
||||||
|
|
|
||||||
|
|
@ -110,14 +110,14 @@ class FormEmailNotification extends Notification implements ShouldQueue
|
||||||
private function parseReplyTo(string $replyTo): ?string
|
private function parseReplyTo(string $replyTo): ?string
|
||||||
{
|
{
|
||||||
$parser = new MentionParser($replyTo, $this->formatSubmissionData(false));
|
$parser = new MentionParser($replyTo, $this->formatSubmissionData(false));
|
||||||
return $parser->parse();
|
return $parser->parseAsText();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getSubject(): string
|
private function getSubject(): string
|
||||||
{
|
{
|
||||||
$defaultSubject = 'New form submission';
|
$defaultSubject = 'New form submission';
|
||||||
$parser = new MentionParser($this->integrationData->subject ?? $defaultSubject, $this->formatSubmissionData(false));
|
$parser = new MentionParser($this->integrationData->subject ?? $defaultSubject, $this->formatSubmissionData(false));
|
||||||
return $parser->parse();
|
return $parser->parseAsText();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function addCustomHeaders(Email $message): void
|
private function addCustomHeaders(Email $message): void
|
||||||
|
|
|
||||||
|
|
@ -62,26 +62,64 @@ class MentionParser
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function replaceMentions()
|
public function parseAsText()
|
||||||
{
|
{
|
||||||
$pattern = '/<span[^>]*mention-field-id="([^"]*)"[^>]*mention-fallback="([^"]*)"[^>]*>.*?<\/span>/';
|
// First use the existing parse method to handle mentions
|
||||||
return preg_replace_callback($pattern, function ($matches) {
|
$html = $this->parse();
|
||||||
$fieldId = $matches[1];
|
|
||||||
$fallback = $matches[2];
|
|
||||||
$value = $this->getData($fieldId);
|
|
||||||
|
|
||||||
if ($value !== null) {
|
$doc = new DOMDocument();
|
||||||
if (is_array($value)) {
|
$internalErrors = libxml_use_internal_errors(true);
|
||||||
return implode(' ', array_map(function ($v) {
|
|
||||||
return $v;
|
// Wrap in root element
|
||||||
}, $value));
|
$wrappedContent = '<root>' . $html . '</root>';
|
||||||
|
|
||||||
|
$doc->loadHTML(mb_convert_encoding($wrappedContent, 'HTML-ENTITIES', 'UTF-8'), LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
|
||||||
|
libxml_use_internal_errors($internalErrors);
|
||||||
|
|
||||||
|
// Convert HTML to plain text with proper line breaks
|
||||||
|
$text = '';
|
||||||
|
$this->domToText($doc->getElementsByTagName('root')->item(0), $text);
|
||||||
|
|
||||||
|
// Clean up the text:
|
||||||
|
// 1. Remove escaped newlines
|
||||||
|
// 2. Replace multiple newlines with single newline
|
||||||
|
// 3. Trim whitespace
|
||||||
|
$text = str_replace(['\\n', '\n'], "\n", $text);
|
||||||
|
$text = preg_replace('/\n+/', "\n", trim($text));
|
||||||
|
|
||||||
|
// Ensure each line has exactly one email
|
||||||
|
$lines = explode("\n", $text);
|
||||||
|
$lines = array_map('trim', $lines);
|
||||||
|
$lines = array_filter($lines); // Remove empty lines
|
||||||
|
|
||||||
|
return implode("\n", $lines);
|
||||||
}
|
}
|
||||||
return $value;
|
|
||||||
} elseif ($fallback) {
|
private function domToText($node, &$text)
|
||||||
return $fallback;
|
{
|
||||||
|
if ($node->nodeType === XML_TEXT_NODE) {
|
||||||
|
$text .= $node->nodeValue;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$block_elements = ['div', 'p', 'br', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ul', 'ol', 'li'];
|
||||||
|
$nodeName = strtolower($node->nodeName);
|
||||||
|
|
||||||
|
// Add newline before block elements
|
||||||
|
if (in_array($nodeName, $block_elements)) {
|
||||||
|
$text .= "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($node->hasChildNodes()) {
|
||||||
|
foreach ($node->childNodes as $child) {
|
||||||
|
$this->domToText($child, $text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add newline after block elements
|
||||||
|
if (in_array($nodeName, $block_elements)) {
|
||||||
|
$text .= "\n";
|
||||||
}
|
}
|
||||||
return '';
|
|
||||||
}, $this->content);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getData($fieldId)
|
private function getData($fieldId)
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,95 @@
|
||||||
|
|
||||||
use App\Open\MentionParser;
|
use App\Open\MentionParser;
|
||||||
|
|
||||||
|
describe('MentionParser', function () {
|
||||||
|
it('replaces mentions with their values in HTML', function () {
|
||||||
|
$content = '<div>Hello <span mention mention-field-id="123" mention-fallback="">Name</span></div>';
|
||||||
|
$data = [
|
||||||
|
['id' => '123', 'value' => 'John Doe']
|
||||||
|
];
|
||||||
|
|
||||||
|
$parser = new MentionParser($content, $data);
|
||||||
|
$result = $parser->parse();
|
||||||
|
|
||||||
|
expect($result)->toBe('<div>Hello John Doe</div>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses fallback when value is not found', function () {
|
||||||
|
$content = '<span mention mention-field-id="456" mention-fallback="Guest">Name</span>';
|
||||||
|
$data = [];
|
||||||
|
|
||||||
|
$parser = new MentionParser($content, $data);
|
||||||
|
$result = $parser->parse();
|
||||||
|
|
||||||
|
expect($result)->toBe('Guest');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('removes the element when no value and no fallback is provided', function () {
|
||||||
|
$content = '<div>Hello <span mention mention-field-id="789" mention-fallback="">Name</span>!</div>';
|
||||||
|
$data = [];
|
||||||
|
|
||||||
|
$parser = new MentionParser($content, $data);
|
||||||
|
$result = $parser->parse();
|
||||||
|
|
||||||
|
expect($result)->toBe('<div>Hello !</div>');
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('parseAsText', function () {
|
||||||
|
it('converts HTML to plain text with proper line breaks', function () {
|
||||||
|
$content = '<div>First line</div><div>Second line</div>';
|
||||||
|
|
||||||
|
$parser = new MentionParser($content, []);
|
||||||
|
$result = $parser->parseAsText();
|
||||||
|
|
||||||
|
expect($result)->toBe("First line\nSecond line");
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles email addresses with proper line breaks', function () {
|
||||||
|
$content = '<span mention mention-field-id="123" mention-fallback="">Email</span><div>john@example.com</div>';
|
||||||
|
$data = [
|
||||||
|
['id' => '123', 'value' => 'jane@example.com']
|
||||||
|
];
|
||||||
|
|
||||||
|
$parser = new MentionParser($content, $data);
|
||||||
|
$result = $parser->parseAsText();
|
||||||
|
|
||||||
|
expect($result)->toBe("jane@example.com\njohn@example.com");
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles multiple mentions and complex HTML structure', function () {
|
||||||
|
$content = '
|
||||||
|
<div>Contact: <span mention mention-field-id="123" mention-fallback="">Email1</span></div>
|
||||||
|
<div>CC: <span mention mention-field-id="456" mention-fallback="">Email2</span></div>
|
||||||
|
<div>Additional: test@example.com</div>
|
||||||
|
';
|
||||||
|
$data = [
|
||||||
|
['id' => '123', 'value' => 'primary@example.com'],
|
||||||
|
['id' => '456', 'value' => 'secondary@example.com'],
|
||||||
|
];
|
||||||
|
|
||||||
|
$parser = new MentionParser($content, $data);
|
||||||
|
$result = $parser->parseAsText();
|
||||||
|
|
||||||
|
expect($result)->toBe(
|
||||||
|
"Contact: primary@example.com\n" .
|
||||||
|
"CC: secondary@example.com\n" .
|
||||||
|
"Additional: test@example.com"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles array values in mentions', function () {
|
||||||
|
$content = '<span mention mention-field-id="123" mention-fallback="">Emails</span>';
|
||||||
|
$data = [
|
||||||
|
['id' => '123', 'value' => ['first@test.com', 'second@test.com']]
|
||||||
|
];
|
||||||
|
|
||||||
|
$parser = new MentionParser($content, $data);
|
||||||
|
$result = $parser->parseAsText();
|
||||||
|
|
||||||
|
expect($result)->toBe('first@test.com, second@test.com');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
test('it replaces mention elements with their corresponding values', function () {
|
test('it replaces mention elements with their corresponding values', function () {
|
||||||
$content = '<p>Hello <span mention mention-field-id="123">Placeholder</span></p>';
|
$content = '<p>Hello <span mention mention-field-id="123">Placeholder</span></p>';
|
||||||
$data = [['id' => '123', 'value' => 'World']];
|
$data = [['id' => '123', 'value' => 'World']];
|
||||||
|
|
@ -84,3 +173,4 @@ test('it handles content without surrounding paragraph tags', function () {
|
||||||
|
|
||||||
expect($result)->toBe('some text replaced text dewde');
|
expect($result)->toBe('some text replaced text dewde');
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue