Spaces:
No application file
No application file
namespace Mautic\EmailBundle\Controller; | |
use Mautic\CoreBundle\Controller\FormController as CommonFormController; | |
use Mautic\CoreBundle\Helper\TrackingPixelHelper; | |
use Mautic\CoreBundle\Twig\Helper\AnalyticsHelper; | |
use Mautic\EmailBundle\EmailEvents; | |
use Mautic\EmailBundle\Entity\Email; | |
use Mautic\EmailBundle\Entity\Stat; | |
use Mautic\EmailBundle\Event\EmailSendEvent; | |
use Mautic\EmailBundle\Event\TransportWebhookEvent; | |
use Mautic\EmailBundle\Helper\MailHashHelper; | |
use Mautic\EmailBundle\Helper\MailHelper; | |
use Mautic\EmailBundle\Model\EmailModel; | |
use Mautic\FormBundle\Model\FormModel; | |
use Mautic\LeadBundle\Controller\FrequencyRuleTrait; | |
use Mautic\LeadBundle\Entity\DoNotContact; | |
use Mautic\LeadBundle\Entity\Lead; | |
use Mautic\LeadBundle\Model\LeadModel; | |
use Mautic\LeadBundle\Tracker\ContactTracker; | |
use Mautic\MessengerBundle\Message\EmailHitNotification; | |
use Mautic\PageBundle\Entity\Page; | |
use Mautic\PageBundle\Event\PageDisplayEvent; | |
use Mautic\PageBundle\EventListener\BuilderSubscriber; | |
use Mautic\PageBundle\Model\PageModel; | |
use Mautic\PageBundle\PageEvents; | |
use Mautic\PluginBundle\Helper\IntegrationHelper; | |
use Psr\Log\LoggerInterface; | |
use Symfony\Component\HttpFoundation\Request; | |
use Symfony\Component\HttpFoundation\Response; | |
use Symfony\Component\Messenger\MessageBusInterface; | |
use Symfony\Contracts\Translation\LocaleAwareInterface; | |
use Symfony\Contracts\Translation\TranslatorInterface; | |
class PublicController extends CommonFormController | |
{ | |
use FrequencyRuleTrait; | |
/** | |
* @return Response | |
*/ | |
public function indexAction(Request $request, AnalyticsHelper $analyticsHelper, $idHash) | |
{ | |
/** @var EmailModel $model */ | |
$model = $this->getModel('email'); | |
$stat = $model->getEmailStatus($idHash); | |
if (!empty($stat)) { | |
if ($this->security->isAnonymous()) { | |
$model->hitEmail($stat, $request, true); | |
} | |
$tokens = $stat->getTokens(); | |
if (is_array($tokens)) { | |
// Override tracking_pixel so as to not cause a double hit | |
$tokens['{tracking_pixel}'] = MailHelper::getBlankPixel(); | |
} | |
if ($copy = $stat->getStoredCopy()) { | |
$subject = $copy->getSubject(); | |
$content = $copy->getBody(); | |
// Replace tokens | |
$content = str_ireplace(array_keys($tokens), $tokens, $content); | |
$subject = str_ireplace(array_keys($tokens), $tokens, $subject); | |
} else { | |
$subject = ''; | |
$content = ''; | |
} | |
$content = $analyticsHelper->addCode($content); | |
// Add subject as title | |
if (!empty($subject)) { | |
if (str_contains($content, '<title></title>')) { | |
$content = str_replace('<title></title>', "<title>$subject</title>", $content); | |
} elseif (!str_contains($content, '<title>')) { | |
$content = str_replace('<head>', "<head>\n<title>$subject</title>", $content); | |
} | |
} | |
return new Response($content); | |
} | |
return $this->notFound(); | |
} | |
public function trackingImageAction( | |
Request $request, | |
MessageBusInterface $messageBus, | |
LoggerInterface $logger, | |
string $idHash | |
): Response { | |
try { | |
$messageBus->dispatch(new EmailHitNotification($idHash, $request)); | |
} catch (\Exception $exception) { | |
$logger->error($exception->getMessage(), ['idHash' => $idHash]); | |
$emailModel = $this->getModel('email'); | |
assert($emailModel instanceof EmailModel); | |
$emailModel->hitEmail($idHash, $request); | |
} | |
return TrackingPixelHelper::getResponse($request); | |
} | |
/** | |
* @return Response | |
* | |
* @throws \Exception | |
* @throws \Mautic\CoreBundle\Exception\FileNotFoundException | |
*/ | |
public function unsubscribeAction(Request $request, ContactTracker $contactTracker, EmailModel $model, LeadModel $leadModel, FormModel $formModel, PageModel $pageModel, MailHashHelper $mailHash, $idHash, string $urlEmail = null, string $secretHash = null) | |
{ | |
$stat = $model->getEmailStatus($idHash); | |
$message = ''; | |
$email = null; | |
$lead = null; | |
$template = null; | |
$session = $request->getSession(); | |
$isOneClickUnsubscribe = $request->isMethod(Request::METHOD_POST) && 'One-Click' === $request->get('List-Unsubscribe'); | |
if (!empty($stat)) { | |
if ($isOneClickUnsubscribe) { | |
// RFC 8058 One-Click unsubscribe | |
$unsubscribeComment = $this->translator->trans('mautic.email.dnc.unsubscribed'); | |
$model->setDoNotContact($stat, $unsubscribeComment, DoNotContact::UNSUBSCRIBED); | |
return new Response($this->translator->trans('mautic.lead.do.not.contact_unsubscribed')); | |
} | |
$email = $stat->getEmail(); | |
} | |
$isCorrectHash = $secretHash && $urlEmail && $mailHash->getEmailHash($urlEmail) === $secretHash; | |
if ($email) { | |
$template = $email->getTemplate(); | |
if ('mautic_code_mode' === $template) { | |
// Use system default | |
$template = null; | |
} | |
/** @var \Mautic\FormBundle\Entity\Form $unsubscribeForm */ | |
$unsubscribeForm = $email->getUnsubscribeForm(); | |
if (null != $unsubscribeForm && $unsubscribeForm->isPublished()) { | |
$formTemplate = $unsubscribeForm->getTemplate(); | |
$formContent = '<div class="mautic-unsubscribeform">'.$formModel->getContent($unsubscribeForm).'</div>'; | |
} | |
} else { | |
if ($isOneClickUnsubscribe) { | |
return new Response($this->translator->trans('mautic.email.stat_record.not_found'), Response::HTTP_NOT_FOUND); | |
} | |
} | |
if (empty($template) && empty($formTemplate)) { | |
$template = $this->coreParametersHelper->get('theme'); | |
} elseif (!empty($formTemplate)) { | |
$template = $formTemplate; | |
} | |
$theme = $this->factory->getTheme($template); | |
if ($theme->getTheme() != $template) { | |
$template = $theme->getTheme(); | |
} | |
$contentTemplate = $this->factory->getHelper('theme')->checkForTwigTemplate('@themes/'.$template.'/html/message.html.twig'); | |
if (!empty($stat) || $isCorrectHash) { | |
$successSessionName = 'mautic.email.prefscenter.success'; | |
if (!empty($stat) && $lead = $stat->getLead()) { | |
// Set the lead as current lead | |
$contactTracker->setTrackedContact($lead); | |
// Set lead lang | |
if ($lead->getPreferredLocale()) { | |
$this->translator->setLocale($lead->getPreferredLocale()); | |
} | |
// Add contact ID to the session name in case more contacts | |
// share the same session/device and the contact is known. | |
$successSessionName .= ".{$lead->getId()}"; | |
} elseif (empty($stat)) { | |
$leadRepo = $leadModel->getRepository(); | |
$contacts = $leadRepo->getContactsByEmail($urlEmail); | |
$lead = null; | |
if (is_array($contacts) && count($contacts) > 0) { | |
$lead = array_pop($contacts); | |
} else { | |
$message = $this->translator->trans('mautic.email.stat_record.not_found'); | |
} | |
} | |
if (!$this->coreParametersHelper->get('show_contact_preferences')) { | |
if (!empty($stat)) { | |
$message = $this->getUnsubscribeMessage($idHash, $model, $stat, $this->translator); | |
} elseif ($lead && $lead instanceof Lead) { | |
$message = $this->getUnsubscribeMessageLead($idHash, $model, $lead, $this->translator, $urlEmail); | |
} | |
} elseif ($lead) { | |
$params = ['idHash' => $idHash, 'urlEmail' => $urlEmail]; | |
if ($urlEmail) { | |
$params['secretHash'] = $mailHash->getEmailHash($urlEmail); | |
} | |
$action = $this->generateUrl('mautic_email_unsubscribe', $params); | |
$viewParameters = [ | |
'lead' => $lead, | |
'idHash' => $idHash, | |
'showContactFrequency' => $this->coreParametersHelper->get('show_contact_frequency'), | |
'showContactPauseDates' => $this->coreParametersHelper->get('show_contact_pause_dates'), | |
'showContactPreferredChannels' => $this->coreParametersHelper->get('show_contact_preferred_channels'), | |
'showContactCategories' => $this->coreParametersHelper->get('show_contact_categories'), | |
'showContactSegments' => $this->coreParametersHelper->get('show_contact_segments'), | |
]; | |
if ($session->get($successSessionName)) { | |
$viewParameters['successMessage'] = $this->translator->trans('mautic.email.preferences_center_success_message.text'); | |
} | |
$form = $this->getFrequencyRuleForm($lead, $viewParameters, $data, true, $action, true); | |
if (true === $form) { | |
$session->set($successSessionName, 1); | |
return $this->postActionRedirect( | |
[ | |
'returnUrl' => $action, | |
'viewParameters' => $viewParameters, | |
'contentTemplate' => $contentTemplate, | |
] | |
); | |
} else { | |
// success message should not persist on page refresh | |
$session->set($successSessionName, 0); | |
} | |
$formView = $form->createView(); | |
/** @var Page $prefCenter */ | |
if ($email && ($prefCenter = $email->getPreferenceCenter()) && $prefCenter->getIsPreferenceCenter()) { | |
$html = $prefCenter->getCustomHtml(); | |
// check if tokens are present | |
if (str_contains($html, 'data-slot="saveprefsbutton"') || str_contains($html, BuilderSubscriber::saveprefsRegex)) { | |
// set custom tag to inject end form | |
// update show pref center slots by looking for their presence in the html | |
$showParameters = $this->buildSlotShowParametersBasedOnContent($html, $viewParameters); | |
$eventParameters = array_merge( | |
$viewParameters, | |
$showParameters, | |
[ | |
'form' => $formView, | |
'startform' => $this->renderView('@MauticCore/Default/form.html.twig', ['form' => $formView]), | |
'custom_tag' => '<a name="end-'.$formView->vars['id'].'"></a>', | |
] | |
); | |
$event = new PageDisplayEvent($html, $prefCenter, $eventParameters); | |
$this->dispatcher->dispatch($event, PageEvents::PAGE_ON_DISPLAY); | |
$html = $event->getContent(); | |
if (!$session->has($successSessionName)) { | |
$successMessageDataSlots = [ | |
'data-slot="successmessage"', | |
'class="pref-successmessage"', | |
]; | |
$successMessageDataSlotsHidden = []; | |
foreach ($successMessageDataSlots as $successMessageDataSlot) { | |
$successMessageDataSlotsHidden[] = $successMessageDataSlot.' style=display:none'; | |
} | |
$html = str_replace( | |
$successMessageDataSlots, | |
$successMessageDataSlotsHidden, | |
$html | |
); | |
} else { | |
$session->remove($successSessionName); | |
} | |
$html = preg_replace( | |
'/'.BuilderSubscriber::identifierToken.'/', | |
$lead->getPrimaryIdentifier(), | |
$html | |
); | |
$pageModel->hitPage($prefCenter, $request, 200, $lead); | |
} else { | |
unset($html); | |
} | |
} | |
if (empty($html)) { | |
$html = $this->render( | |
'@MauticEmail/Lead/preference_options.html.twig', | |
array_merge( | |
$viewParameters, | |
[ | |
'form' => $formView, | |
'currentRoute' => $this->generateUrl( | |
'mautic_contact_action', | |
[ | |
'objectAction' => 'contactFrequency', | |
'objectId' => $lead->getId(), | |
] | |
), | |
] | |
) | |
)->getContent(); | |
} | |
$message = $html; | |
} | |
} else { | |
$message = $this->translator->trans('mautic.email.stat_record.not_found'); | |
} | |
$config = $theme->getConfig(); | |
$viewParams = [ | |
'email' => $email, | |
'lead' => $lead, | |
'template' => $template, | |
'message' => $message, | |
]; | |
if (!empty($formContent)) { | |
$viewParams['content'] = $formContent; | |
if (in_array('form', $config['features'])) { | |
$contentTemplate = $this->factory->getHelper('theme')->checkForTwigTemplate('@themes/'.$template.'/html/form.html.twig'); | |
} else { | |
$viewParams['content'] = ''; | |
$viewParams['message'] = $message.$formContent; | |
} | |
} | |
return $this->render($contentTemplate, $viewParams); | |
} | |
/** | |
* @throws \Exception | |
* @throws \Mautic\CoreBundle\Exception\FileNotFoundException | |
*/ | |
public function resubscribeAction(ContactTracker $contactTracker, EmailModel $model, LeadModel $leadModel, MailHashHelper $mailHash, $idHash): Response | |
{ | |
$stat = $model->getEmailStatus($idHash); | |
if (!empty($stat)) { | |
$email = $stat->getEmail(); | |
$lead = $stat->getLead(); | |
if ($lead) { | |
// Set the lead as current lead | |
$contactTracker->setTrackedContact($lead); | |
if (!$this->translator instanceof LocaleAwareInterface) { | |
throw new \LogicException(sprintf('$this->translator must be an instance of "%s"', LocaleAwareInterface::class)); | |
} | |
// Set lead lang | |
if ($lead->getPreferredLocale()) { | |
$this->translator->setLocale($lead->getPreferredLocale()); | |
} | |
} | |
$model->removeDoNotContact($stat->getEmailAddress()); | |
$message = $this->coreParametersHelper->get('resubscribe_message'); | |
$toEmail = $stat->getEmailAddress(); | |
$unsubscribeHash = $mailHash->getEmailHash($toEmail); | |
if (!$message) { | |
$message = $this->translator->trans( | |
'mautic.email.resubscribed.success', | |
[ | |
'%unsubscribeUrl%' => '|URL|', | |
'%email%' => '|EMAIL|', | |
] | |
); | |
} | |
$message = str_replace( | |
[ | |
'|URL|', | |
'|EMAIL|', | |
], | |
[ | |
$this->generateUrl('mautic_email_unsubscribe', ['idHash' => $idHash, 'urlEmail' => $toEmail, 'secretHash' => $unsubscribeHash]), | |
$stat->getEmailAddress(), | |
], | |
$message | |
); | |
} else { | |
$email = $lead = false; | |
$message = $this->translator->trans('mautic.email.stat_record.not_found'); | |
} | |
$template = (!empty($email) && 'mautic_code_mode' !== $email->getTemplate()) ? $email->getTemplate() : $this->coreParametersHelper->get('theme'); | |
$theme = $this->factory->getTheme($template); | |
if ($theme->getTheme() != $template) { | |
$template = $theme->getTheme(); | |
} | |
// Ensure template still exists | |
$theme = $this->factory->getTheme($template); | |
if (empty($theme) || $theme->getTheme() !== $template) { | |
$template = $this->coreParametersHelper->get('theme'); | |
} | |
$analytics = $this->factory->getHelper('twig.analytics')->getCode(); | |
if (!empty($analytics)) { | |
$this->factory->getHelper('template.assets')->addCustomDeclaration($analytics); | |
} | |
$logicalName = $this->factory->getHelper('theme')->checkForTwigTemplate('@themes/'.$template.'/html/message.html.twig'); | |
return $this->render( | |
$logicalName, | |
[ | |
'message' => $message, | |
'type' => 'notice', | |
'email' => $email, | |
'lead' => $lead, | |
'template' => $template, | |
] | |
); | |
} | |
/** | |
* Handles mailer transport webhook post. | |
*/ | |
public function mailerCallbackAction(Request $request): Response | |
{ | |
$event = new TransportWebhookEvent($request); | |
$this->dispatcher->dispatch($event, EmailEvents::ON_TRANSPORT_WEBHOOK); | |
return $event->getResponse() ?? new Response('No email transport that could process this callback was found', Response::HTTP_NOT_FOUND); | |
} | |
/** | |
* Preview email. | |
* | |
* @return Response | |
*/ | |
public function previewAction(AnalyticsHelper $analyticsHelper, Request $request, string $objectId, string $objectType = null) | |
{ | |
$contactId = (int) $request->query->get('contactId'); | |
/** @var EmailModel $model */ | |
$model = $this->getModel('email'); | |
$emailEntity = $model->getEntity($objectId); | |
if (null === $emailEntity) { | |
return $this->notFound(); | |
} | |
if ( | |
($this->security->isAnonymous() && (!$emailEntity->getIsPublished() || !$emailEntity->isPublicPreview())) | |
|| (!$this->security->isAnonymous() | |
&& !$this->security->hasEntityAccess( | |
'email:emails:viewown', | |
'email:emails:viewother', | |
$emailEntity->getCreatedBy() | |
)) | |
) { | |
return $this->accessDenied(); | |
} | |
// bogus ID | |
if ($contactId && ( | |
!$this->security->isAdmin() | |
|| !$this->security->hasEntityAccess('lead:leads:viewown', 'lead:leads:viewother') | |
) | |
) { | |
// disallow displaying contact information | |
$contactId = null; | |
} | |
// bogus ID | |
$idHash = 'xxxxxxxxxxxxxx'; | |
$BCcontent = $emailEntity->getContent(); | |
$content = $emailEntity->getCustomHtml(); | |
if (empty($content) && !empty($BCcontent)) { | |
$template = $emailEntity->getTemplate(); | |
$slots = $this->factory->getTheme($template)->getSlots('email'); | |
$assetsHelper = $this->factory->getHelper('template.assets'); | |
$assetsHelper->addCustomDeclaration('<meta name="robots" content="noindex">'); | |
$this->processSlots($slots, $emailEntity); | |
$logicalName = $this->factory->getHelper('theme')->checkForTwigTemplate('@themes/'.$template.'/html/email.html.twig'); | |
$response = $this->render( | |
$logicalName, | |
[ | |
'inBrowser' => true, | |
'slots' => $slots, | |
'content' => $emailEntity->getContent(), | |
'email' => $emailEntity, | |
'lead' => null, | |
'template' => $template, | |
] | |
); | |
// replace tokens | |
$content = $response->getContent(); | |
} | |
// Override tracking_pixel | |
$tokens = ['{tracking_pixel}' => '']; | |
// Prepare contact | |
if ($contactId) { | |
// We have one from request parameter | |
/** @var LeadModel $leadModel */ | |
$leadModel = $this->getModel('lead.lead'); | |
/** @var Lead $contact */ | |
$contact = $leadModel->getEntity($contactId); | |
$contact = $contact->convertToArray(); | |
} else { | |
// Generate faked one | |
/** @var \Mautic\LeadBundle\Model\FieldModel $fieldModel */ | |
$fieldModel = $this->getModel('lead.field'); | |
$contact = $fieldModel->getFieldList(false, false); | |
array_walk( | |
$contact, | |
function (&$field): void { | |
$field = "[$field]"; | |
} | |
); | |
$contact['id'] = 0; | |
} | |
// Generate and replace tokens | |
$event = new EmailSendEvent( | |
null, | |
[ | |
'content' => $content, | |
'email' => $emailEntity, | |
'idHash' => $idHash, | |
'tokens' => $tokens, | |
'internalSend' => true, | |
'lead' => $contact, | |
] | |
); | |
$this->dispatcher->dispatch($event, EmailEvents::EMAIL_ON_DISPLAY); | |
$content = $event->getContent(true); | |
if ($this->security->isAnonymous()) { | |
$content = $analyticsHelper->addCode($content); | |
} | |
return new Response($content); | |
} | |
/** | |
* @param Email $entity | |
*/ | |
public function processSlots($slots, $entity): void | |
{ | |
/** @var \Mautic\CoreBundle\Twig\Helper\SlotsHelper $slotsHelper */ | |
$slotsHelper = $this->factory->getHelper('template.slots'); | |
$content = $entity->getContent(); | |
foreach ($slots as $slot => $slotConfig) { | |
if (is_numeric($slot)) { | |
$slot = $slotConfig; | |
$slotConfig = []; | |
} | |
$value = $content[$slot] ?? ''; | |
$slotsHelper->set($slot, $value); | |
} | |
} | |
/** | |
* @throws \Exception | |
*/ | |
private function doTracking(Request $request, IntegrationHelper $integrationHelper, MailHelper $mailer, LoggerInterface $mauticLogger, $integration): void | |
{ | |
$logger = $mauticLogger; | |
// if additional data were sent with the tracking pixel | |
$query_string = $request->server->get('QUERY_STRING'); | |
if (!$query_string) { | |
$logger->log('error', $integration.': query string is not available'); | |
return; | |
} | |
if (str_starts_with($query_string, 'r=')) { | |
$query_string = substr($query_string, strpos($query_string, '?') + 1); | |
} // remove route variable | |
parse_str($query_string, $query); | |
// URL attr 'd' is encoded so let's decode it first. | |
if (!isset($query['d'], $query['sig'])) { | |
$logger->log('error', $integration.': query variables are not found'); | |
return; | |
} | |
// get secret from plugin settings | |
$myIntegration = $integrationHelper->getIntegrationObject($integration); | |
if (!$myIntegration) { | |
$logger->log('error', $integration.': integration not found'); | |
return; | |
} | |
$keys = $myIntegration->getDecryptedApiKeys(); | |
// generate signature | |
$salt = $keys['secret']; | |
if (!str_contains($salt, '$1$')) { | |
$salt = '$1$'.$salt; | |
} // add MD5 prefix | |
$cr = crypt(urlencode($query['d']), $salt); | |
$mySig = hash('crc32b', $cr); // this hash type is used in c# | |
// compare signatures | |
if (hash_equals($mySig, $query['sig'])) { | |
// decode and parse query variables | |
$b64 = base64_decode($query['d']); | |
$gz = gzdecode($b64); | |
parse_str($gz, $query); | |
} else { | |
// signatures don't match: stop | |
$logger->log('error', $integration.': signatures don\'t match'); | |
unset($query); | |
} | |
if (empty($query) || !isset($query['email'], $query['subject'], $query['body'])) { | |
$logger->log('error', $integration.': query variables are empty'); | |
return; | |
} | |
if (MAUTIC_ENV === 'dev') { | |
$logger->log('error', $integration.': '.json_encode($query, JSON_PRETTY_PRINT)); | |
} | |
/** @var EmailModel $model */ | |
$model = $this->getModel('email'); | |
// email is a semicolon delimited list of emails | |
$emails = explode(';', $query['email']); | |
$leadModel = $this->getModel('lead'); | |
\assert($leadModel instanceof LeadModel); | |
$repo = $leadModel->getRepository(); | |
foreach ($emails as $email) { | |
$lead = $repo->getLeadByEmail($email); | |
if (null === $lead) { | |
$lead = $this->createLead($email, $repo); | |
if (null === $lead) { | |
continue; | |
} | |
} | |
$idHash = hash('crc32', $email.$query['body']); | |
$idHash = substr($idHash.$idHash, 0, 13); // 13 bytes length | |
$stat = $model->getEmailStatus($idHash); | |
// stat doesn't exist, create one | |
if (null === $stat) { | |
$lead['email'] = $email; // needed for stat | |
$stat = $this->addStat($mailer, $lead, $email, $query, $idHash); | |
} | |
$stat->setSource('email.client'); | |
if ($stat || 'Outlook' !== $integration) { // Outlook requests the tracking gif on send | |
$model->hitEmail($idHash, $request); // add email event | |
} | |
} | |
} | |
/** | |
* @return Response | |
*/ | |
public function pluginTrackingGifAction(Request $request, IntegrationHelper $integrationHelper, MailHelper $mailer, LoggerInterface $mauticLogger, $integration) | |
{ | |
$this->doTracking($request, $integrationHelper, $mailer, $mauticLogger, $integration); | |
return TrackingPixelHelper::getResponse($request); // send gif | |
} | |
private function addStat(MailHelper $mailer, $lead, $email, $query, $idHash): ?Stat | |
{ | |
if (null !== $lead) { | |
// To lead | |
$mailer->addTo($email); | |
// sanitize variables to prevent malicious content | |
$from = filter_var($query['from'], FILTER_SANITIZE_EMAIL); | |
$mailer->setFrom($from, ''); | |
// Set Content | |
$body = filter_var($query['body'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_HIGH); | |
$mailer->setBody($body); | |
$mailer->parsePlainText($body); | |
// Set lead | |
$mailer->setLead($lead); | |
$mailer->setIdHash($idHash); | |
$subject = filter_var($query['subject'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_HIGH); | |
$mailer->setSubject($subject); | |
return $mailer->createEmailStat(); | |
} | |
return null; | |
} | |
private function createLead($email, $repo): ?Lead | |
{ | |
$model = $this->getModel('lead.lead'); | |
\assert($model instanceof LeadModel); | |
$lead = $model->getEntity(); | |
// set custom field values | |
$data = ['email' => $email]; | |
$model->setFieldValues($lead, $data, true); | |
// create lead | |
$model->saveEntity($lead); | |
// return entity | |
return $repo->getLeadByEmail($email); | |
} | |
public function getUnsubscribeMessage($idHash, $model, $stat, $translator): string | |
{ | |
$model->setDoNotContact($stat, $translator->trans('mautic.email.dnc.unsubscribed'), DoNotContact::UNSUBSCRIBED); | |
return $this->getUnsubscribeText($translator, $stat->getEmailAddress(), $idHash); | |
} | |
public function getUnsubscribeMessageLead(string $idHash, EmailModel $model, Lead $lead, TranslatorInterface $translator, string $urlEmail): string | |
{ | |
$model->setDoNotContactLead($lead, $translator->trans('mautic.email.dnc.unsubscribed'), DoNotContact::UNSUBSCRIBED); | |
return $this->getUnsubscribeText($translator, $urlEmail, $idHash); | |
} | |
private function getUnsubscribeText(TranslatorInterface $translator, string $email, string $idHash): string | |
{ | |
$message = $this->coreParametersHelper->get('unsubscribe_message'); | |
if (!$message) { | |
$message = $translator->trans( | |
'mautic.email.unsubscribed.success', | |
[ | |
'%resubscribeUrl%' => '|URL|', | |
'%email%' => '|EMAIL|', | |
] | |
); | |
} | |
return str_replace( | |
[ | |
'|URL|', | |
'|EMAIL|', | |
], | |
[ | |
$this->generateUrl('mautic_email_resubscribe', ['idHash' => $idHash]), | |
$email, | |
], | |
$message | |
); | |
} | |
/** | |
* The $viewParameters here have already been used to build the $form. | |
* Fields that are set to show based on the app configuration are part | |
* of the form. If the field is not configured to show, but a slot exists | |
* for that field in the content, then we need to keep the configuration | |
* value instead of letting the content determine if it should show. This | |
* is because of what was stated above - fields that are not configured to | |
* to show are not part of the form. Attempting to render them will result | |
* in an error. | |
* | |
* @param mixed[] $viewParameters | |
* | |
* @return mixed[] | |
*/ | |
private function buildSlotShowParametersBasedOnContent(string $content, array $viewParameters): array | |
{ | |
/* | |
* Since we're going to be merging this with the $viewParameters, filter out `true` values. We do not | |
* want to change a configured value from `false` to `true` because a value of `false` in the $viewParameters | |
* means that the field is not configured to show and therefore is not part of the form. Attempting to | |
* render that field just because a slot for it exists will result in an error. | |
*/ | |
$showParamsBasedOnContent = array_filter([ | |
'showContactFrequency' => str_contains($content, 'data-slot="channelfrequency"') || str_contains($content, BuilderSubscriber::channelfrequency), | |
'showContactSegments' => str_contains($content, 'data-slot="segmentlist"') || str_contains($content, BuilderSubscriber::segmentListRegex), | |
'showContactCategories' => str_contains($content, 'data-slot="categorylist"') || str_contains($content, BuilderSubscriber::categoryListRegex), | |
'showContactPreferredChannels' => str_contains($content, 'data-slot="preferredchannel"') || str_contains($content, BuilderSubscriber::preferredchannel), | |
], fn (bool $value) =>!$value); | |
$showParamsBasedOnConfiguration = array_filter($viewParameters, fn ($key) => str_starts_with($key, 'show'), ARRAY_FILTER_USE_KEY); | |
return array_merge($showParamsBasedOnConfiguration, $showParamsBasedOnContent); | |
} | |
} | |