1 | <?php |
||||||
2 | |||||||
3 | /* |
||||||
4 | * @copyright 2014 Mautic Contributors. All rights reserved |
||||||
5 | * @author Mautic |
||||||
6 | * |
||||||
7 | * @link http://mautic.org |
||||||
8 | * |
||||||
9 | * @license GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html |
||||||
10 | */ |
||||||
11 | |||||||
12 | namespace Mautic\EmailBundle\Controller; |
||||||
13 | |||||||
14 | use Mautic\CoreBundle\Controller\FormController as CommonFormController; |
||||||
15 | use Mautic\CoreBundle\Helper\EmojiHelper; |
||||||
16 | use Mautic\CoreBundle\Helper\TrackingPixelHelper; |
||||||
17 | use Mautic\EmailBundle\EmailEvents; |
||||||
18 | use Mautic\EmailBundle\Entity\Email; |
||||||
19 | use Mautic\EmailBundle\Event\EmailSendEvent; |
||||||
20 | use Mautic\EmailBundle\Event\TransportWebhookEvent; |
||||||
21 | use Mautic\EmailBundle\Helper\MailHelper; |
||||||
22 | use Mautic\EmailBundle\Model\EmailModel; |
||||||
23 | use Mautic\EmailBundle\Swiftmailer\Transport\CallbackTransportInterface; |
||||||
24 | use Mautic\LeadBundle\Controller\FrequencyRuleTrait; |
||||||
25 | use Mautic\LeadBundle\Entity\DoNotContact; |
||||||
26 | use Mautic\PageBundle\Entity\Page; |
||||||
27 | use Mautic\PageBundle\Event\PageDisplayEvent; |
||||||
28 | use Mautic\PageBundle\EventListener\BuilderSubscriber; |
||||||
29 | use Mautic\PageBundle\PageEvents; |
||||||
30 | use Mautic\QueueBundle\Queue\QueueName; |
||||||
31 | use Symfony\Component\HttpFoundation\Response; |
||||||
32 | |||||||
33 | class PublicController extends CommonFormController |
||||||
34 | { |
||||||
35 | use FrequencyRuleTrait; |
||||||
36 | |||||||
37 | /** |
||||||
38 | * @param $idHash |
||||||
39 | * |
||||||
40 | * @return Response |
||||||
41 | */ |
||||||
42 | public function indexAction($idHash) |
||||||
43 | { |
||||||
44 | /** @var \Mautic\EmailBundle\Model\EmailModel $model */ |
||||||
45 | $model = $this->getModel('email'); |
||||||
46 | $stat = $model->getEmailStatus($idHash); |
||||||
47 | |||||||
48 | if (!empty($stat)) { |
||||||
49 | if ($this->get('mautic.security')->isAnonymous()) { |
||||||
50 | $model->hitEmail($stat, $this->request, true); |
||||||
51 | } |
||||||
52 | |||||||
53 | $tokens = $stat->getTokens(); |
||||||
54 | if (is_array($tokens)) { |
||||||
55 | // Override tracking_pixel so as to not cause a double hit |
||||||
56 | $tokens['{tracking_pixel}'] = MailHelper::getBlankPixel(); |
||||||
57 | } |
||||||
58 | |||||||
59 | if ($copy = $stat->getStoredCopy()) { |
||||||
60 | $subject = $copy->getSubject(); |
||||||
61 | $content = $copy->getBody(); |
||||||
62 | |||||||
63 | // Convert emoji |
||||||
64 | $content = EmojiHelper::toEmoji($content, 'short'); |
||||||
65 | $subject = EmojiHelper::toEmoji($subject, 'short'); |
||||||
66 | |||||||
67 | // Replace tokens |
||||||
68 | if (!empty($tokens)) { |
||||||
69 | $content = str_ireplace(array_keys($tokens), $tokens, $content); |
||||||
70 | $subject = str_ireplace(array_keys($tokens), $tokens, $subject); |
||||||
71 | } |
||||||
72 | } else { |
||||||
73 | $subject = ''; |
||||||
74 | $content = ''; |
||||||
75 | } |
||||||
76 | |||||||
77 | $content = $this->get('mautic.helper.template.analytics')->addCode($content); |
||||||
78 | |||||||
79 | // Add subject as title |
||||||
80 | if (!empty($subject)) { |
||||||
81 | if (false !== strpos($content, '<title></title>')) { |
||||||
82 | $content = str_replace('<title></title>', "<title>$subject</title>", $content); |
||||||
83 | } elseif (false === strpos($content, '<title>')) { |
||||||
84 | $content = str_replace('<head>', "<head>\n<title>$subject</title>", $content); |
||||||
85 | } |
||||||
86 | } |
||||||
87 | |||||||
88 | return new Response($content); |
||||||
89 | } |
||||||
90 | |||||||
91 | return $this->notFound(); |
||||||
92 | } |
||||||
93 | |||||||
94 | /** |
||||||
95 | * @param $idHash |
||||||
96 | * |
||||||
97 | * @return Response |
||||||
98 | */ |
||||||
99 | public function trackingImageAction($idHash) |
||||||
100 | { |
||||||
101 | $queueService = $this->get('mautic.queue.service'); |
||||||
102 | if ($queueService->isQueueEnabled()) { |
||||||
103 | $msg = [ |
||||||
104 | 'request' => $this->request, |
||||||
105 | 'idHash' => $idHash, |
||||||
106 | ]; |
||||||
107 | $queueService->publishToQueue(QueueName::EMAIL_HIT, $msg); |
||||||
108 | } else { |
||||||
109 | /** @var EmailModel $model */ |
||||||
110 | $model = $this->getModel('email'); |
||||||
111 | $model->hitEmail($idHash, $this->request); |
||||||
112 | } |
||||||
113 | |||||||
114 | return TrackingPixelHelper::getResponse($this->request); |
||||||
115 | } |
||||||
116 | |||||||
117 | /** |
||||||
118 | * @param $idHash |
||||||
119 | * |
||||||
120 | * @return Response |
||||||
121 | * |
||||||
122 | * @throws \Exception |
||||||
123 | * @throws \Mautic\CoreBundle\Exception\FileNotFoundException |
||||||
124 | */ |
||||||
125 | public function unsubscribeAction($idHash) |
||||||
126 | { |
||||||
127 | // Find the email |
||||||
128 | /** @var \Mautic\EmailBundle\Model\EmailModel $model */ |
||||||
129 | $model = $this->getModel('email'); |
||||||
130 | $translator = $this->get('translator'); |
||||||
131 | $stat = $model->getEmailStatus($idHash); |
||||||
132 | $message = ''; |
||||||
133 | $email = null; |
||||||
134 | $lead = null; |
||||||
135 | $template = null; |
||||||
136 | $session = $this->get('session'); |
||||||
137 | |||||||
138 | /** @var \Mautic\LeadBundle\Model\LeadModel $leadModel */ |
||||||
139 | $leadModel = $this->getModel('lead'); |
||||||
140 | |||||||
141 | if (!empty($stat)) { |
||||||
142 | if ($email = $stat->getEmail()) { |
||||||
143 | $template = $email->getTemplate(); |
||||||
144 | if ('mautic_code_mode' === $template) { |
||||||
145 | // Use system default |
||||||
146 | $template = null; |
||||||
147 | } |
||||||
148 | |||||||
149 | /** @var \Mautic\FormBundle\Entity\Form $unsubscribeForm */ |
||||||
150 | $unsubscribeForm = $email->getUnsubscribeForm(); |
||||||
151 | if (null != $unsubscribeForm && $unsubscribeForm->isPublished()) { |
||||||
152 | $formTemplate = $unsubscribeForm->getTemplate(); |
||||||
153 | $formModel = $this->getModel('form'); |
||||||
154 | $formContent = '<div class="mautic-unsubscribeform">'.$formModel->getContent($unsubscribeForm).'</div>'; |
||||||
155 | } |
||||||
156 | } |
||||||
157 | } |
||||||
158 | |||||||
159 | if (empty($template) && empty($formTemplate)) { |
||||||
160 | $template = $this->coreParametersHelper->get('theme'); |
||||||
161 | } elseif (!empty($formTemplate)) { |
||||||
162 | $template = $formTemplate; |
||||||
163 | } |
||||||
164 | |||||||
165 | $theme = $this->factory->getTheme($template); |
||||||
166 | if ($theme->getTheme() != $template) { |
||||||
167 | $template = $theme->getTheme(); |
||||||
168 | } |
||||||
169 | $contentTemplate = $this->factory->getHelper('theme')->checkForTwigTemplate(':'.$template.':message.html.php'); |
||||||
170 | if (!empty($stat)) { |
||||||
171 | $successSessionName = 'mautic.email.prefscenter.success'; |
||||||
172 | |||||||
173 | if ($lead = $stat->getLead()) { |
||||||
174 | // Set the lead as current lead |
||||||
175 | $this->get('mautic.tracker.contact')->setTrackedContact($lead); |
||||||
176 | |||||||
177 | // Set lead lang |
||||||
178 | if ($lead->getPreferredLocale()) { |
||||||
179 | $translator->setLocale($lead->getPreferredLocale()); |
||||||
180 | } |
||||||
181 | |||||||
182 | // Add contact ID to the session name in case more contacts |
||||||
183 | // share the same session/device and the contact is known. |
||||||
184 | $successSessionName .= ".{$lead->getId()}"; |
||||||
185 | } |
||||||
186 | |||||||
187 | if (!$this->get('mautic.helper.core_parameters')->get('show_contact_preferences')) { |
||||||
188 | $message = $this->getUnsubscribeMessage($idHash, $model, $stat, $translator); |
||||||
189 | } elseif ($lead) { |
||||||
190 | $action = $this->generateUrl('mautic_email_unsubscribe', ['idHash' => $idHash]); |
||||||
191 | |||||||
192 | $viewParameters = [ |
||||||
193 | 'lead' => $lead, |
||||||
194 | 'idHash' => $idHash, |
||||||
195 | 'showContactFrequency' => $this->get('mautic.helper.core_parameters')->get('show_contact_frequency'), |
||||||
196 | 'showContactPauseDates' => $this->get('mautic.helper.core_parameters')->get('show_contact_pause_dates'), |
||||||
197 | 'showContactPreferredChannels' => $this->get('mautic.helper.core_parameters')->get('show_contact_preferred_channels'), |
||||||
198 | 'showContactCategories' => $this->get('mautic.helper.core_parameters')->get('show_contact_categories'), |
||||||
199 | 'showContactSegments' => $this->get('mautic.helper.core_parameters')->get('show_contact_segments'), |
||||||
200 | ]; |
||||||
201 | |||||||
202 | $form = $this->getFrequencyRuleForm($lead, $viewParameters, $data, true, $action, true); |
||||||
203 | if (true === $form) { |
||||||
204 | $session->set($successSessionName, 1); |
||||||
205 | |||||||
206 | return $this->postActionRedirect( |
||||||
207 | [ |
||||||
208 | 'returnUrl' => $this->generateUrl('mautic_email_unsubscribe', ['idHash' => $idHash]), |
||||||
209 | 'viewParameters' => $viewParameters, |
||||||
210 | 'contentTemplate' => $contentTemplate, |
||||||
211 | ] |
||||||
212 | ); |
||||||
213 | } |
||||||
214 | |||||||
215 | $formView = $form->createView(); |
||||||
216 | /** @var Page $prefCenter */ |
||||||
217 | if ($email && ($prefCenter = $email->getPreferenceCenter()) && ($prefCenter->getIsPreferenceCenter())) { |
||||||
218 | $html = $prefCenter->getCustomHtml(); |
||||||
219 | // check if tokens are present |
||||||
220 | $savePrefsPresent = false !== strpos($html, 'data-slot="saveprefsbutton"') || |
||||||
221 | false !== strpos($html, BuilderSubscriber::saveprefsRegex); |
||||||
222 | if ($savePrefsPresent) { |
||||||
223 | // set custom tag to inject end form |
||||||
224 | // update show pref center slots by looking for their presence in the html |
||||||
225 | /** @var \Mautic\CoreBundle\Templating\Helper\FormHelper $formHelper */ |
||||||
226 | $formHelper =$this->get('templating.helper.form'); |
||||||
227 | $params = array_merge( |
||||||
228 | $viewParameters, |
||||||
229 | [ |
||||||
230 | 'form' => $formView, |
||||||
231 | 'startform' => $formHelper->start($formView), |
||||||
232 | 'custom_tag' => '<a name="end-'.$formView->vars['id'].'"></a>', |
||||||
233 | 'showContactFrequency' => false !== strpos($html, 'data-slot="channelfrequency"') || false !== strpos($html, BuilderSubscriber::channelfrequency), |
||||||
234 | 'showContactSegments' => false !== strpos($html, 'data-slot="segmentlist"') || false !== strpos($html, BuilderSubscriber::segmentListRegex), |
||||||
235 | 'showContactCategories' => false !== strpos($html, 'data-slot="categorylist"') || false !== strpos($html, BuilderSubscriber::categoryListRegex), |
||||||
236 | 'showContactPreferredChannels' => false !== strpos($html, 'data-slot="preferredchannel"') || false !== strpos($html, BuilderSubscriber::preferredchannel), |
||||||
237 | ] |
||||||
238 | ); |
||||||
239 | // Replace tokens in preference center page |
||||||
240 | $event = new PageDisplayEvent($html, $prefCenter, $params); |
||||||
241 | $this->get('event_dispatcher') |
||||||
242 | ->dispatch(PageEvents::PAGE_ON_DISPLAY, $event); |
||||||
243 | $html = $event->getContent(); |
||||||
244 | if (!$session->has($successSessionName)) { |
||||||
245 | $successMessageDataSlots = [ |
||||||
246 | 'data-slot="successmessage"', |
||||||
247 | 'class="pref-successmessage"', |
||||||
248 | ]; |
||||||
249 | $successMessageDataSlotsHidden = []; |
||||||
250 | foreach ($successMessageDataSlots as $successMessageDataSlot) { |
||||||
251 | $successMessageDataSlotsHidden[] = $successMessageDataSlot.' style=display:none'; |
||||||
252 | } |
||||||
253 | $html = str_replace( |
||||||
254 | $successMessageDataSlots, |
||||||
255 | $successMessageDataSlotsHidden, |
||||||
256 | $html |
||||||
257 | ); |
||||||
258 | } else { |
||||||
259 | $session->remove($successSessionName); |
||||||
260 | } |
||||||
261 | $html = preg_replace( |
||||||
262 | '/'.BuilderSubscriber::identifierToken.'/', |
||||||
263 | $lead->getPrimaryIdentifier(), |
||||||
264 | $html |
||||||
265 | ); |
||||||
266 | } else { |
||||||
267 | unset($html); |
||||||
268 | } |
||||||
269 | } |
||||||
270 | |||||||
271 | if (empty($html)) { |
||||||
272 | $html = $this->get('mautic.helper.templating')->getTemplating()->render( |
||||||
273 | 'MauticEmailBundle:Lead:preference_options.html.php', |
||||||
274 | array_merge( |
||||||
275 | $viewParameters, |
||||||
276 | [ |
||||||
277 | 'form' => $formView, |
||||||
278 | 'currentRoute' => $this->generateUrl( |
||||||
279 | 'mautic_contact_action', |
||||||
280 | [ |
||||||
281 | 'objectAction' => 'contactFrequency', |
||||||
282 | 'objectId' => $lead->getId(), |
||||||
283 | ] |
||||||
284 | ), |
||||||
285 | ] |
||||||
286 | ) |
||||||
287 | ); |
||||||
288 | } |
||||||
289 | $message = $html; |
||||||
290 | } |
||||||
291 | } else { |
||||||
292 | $message = $translator->trans('mautic.email.stat_record.not_found'); |
||||||
293 | } |
||||||
294 | |||||||
295 | $config = $theme->getConfig(); |
||||||
296 | |||||||
297 | $viewParams = [ |
||||||
298 | 'email' => $email, |
||||||
299 | 'lead' => $lead, |
||||||
300 | 'template' => $template, |
||||||
301 | 'message' => $message, |
||||||
302 | ]; |
||||||
303 | |||||||
304 | if (!empty($formContent)) { |
||||||
305 | $viewParams['content'] = $formContent; |
||||||
306 | if (in_array('form', $config['features'])) { |
||||||
307 | $contentTemplate = $this->factory->getHelper('theme')->checkForTwigTemplate(':'.$template.':form.html.php'); |
||||||
308 | } else { |
||||||
309 | $contentTemplate = 'MauticFormBundle::form.html.php'; |
||||||
310 | } |
||||||
311 | } |
||||||
312 | |||||||
313 | return $this->render($contentTemplate, $viewParams); |
||||||
314 | } |
||||||
315 | |||||||
316 | /** |
||||||
317 | * @param $idHash |
||||||
318 | * |
||||||
319 | * @return Response |
||||||
320 | * |
||||||
321 | * @throws \Exception |
||||||
322 | * @throws \Mautic\CoreBundle\Exception\FileNotFoundException |
||||||
323 | */ |
||||||
324 | public function resubscribeAction($idHash) |
||||||
325 | { |
||||||
326 | //find the email |
||||||
327 | $model = $this->getModel('email'); |
||||||
328 | $stat = $model->getEmailStatus($idHash); |
||||||
329 | |||||||
330 | if (!empty($stat)) { |
||||||
331 | $email = $stat->getEmail(); |
||||||
332 | $lead = $stat->getLead(); |
||||||
333 | |||||||
334 | if ($lead) { |
||||||
335 | // Set the lead as current lead |
||||||
336 | /** @var \Mautic\LeadBundle\Model\LeadModel $leadModel */ |
||||||
337 | $leadModel = $this->getModel('lead'); |
||||||
338 | $this->get('mautic.tracker.contact')->setTrackedContact($lead); |
||||||
339 | |||||||
340 | // Set lead lang |
||||||
341 | if ($lead->getPreferredLocale()) { |
||||||
342 | $this->translator->setLocale($lead->getPreferredLocale()); |
||||||
343 | } |
||||||
344 | } |
||||||
345 | |||||||
346 | $model->removeDoNotContact($stat->getEmailAddress()); |
||||||
347 | |||||||
348 | $message = $this->coreParametersHelper->get('resubscribe_message'); |
||||||
349 | if (!$message) { |
||||||
350 | $message = $this->translator->trans( |
||||||
351 | 'mautic.email.resubscribed.success', |
||||||
352 | [ |
||||||
353 | '%unsubscribeUrl%' => '|URL|', |
||||||
354 | '%email%' => '|EMAIL|', |
||||||
355 | ] |
||||||
356 | ); |
||||||
357 | } |
||||||
358 | $message = str_replace( |
||||||
359 | [ |
||||||
360 | '|URL|', |
||||||
361 | '|EMAIL|', |
||||||
362 | ], |
||||||
363 | [ |
||||||
364 | $this->generateUrl('mautic_email_unsubscribe', ['idHash' => $idHash]), |
||||||
365 | $stat->getEmailAddress(), |
||||||
366 | ], |
||||||
367 | $message |
||||||
368 | ); |
||||||
369 | } else { |
||||||
370 | $email = $lead = false; |
||||||
371 | $message = $this->translator->trans('mautic.email.stat_record.not_found'); |
||||||
372 | } |
||||||
373 | |||||||
374 | $template = (null !== $email && 'mautic_code_mode' !== $email->getTemplate()) ? $email->getTemplate() : $this->coreParametersHelper->get('theme'); |
||||||
375 | |||||||
376 | $theme = $this->factory->getTheme($template); |
||||||
377 | |||||||
378 | if ($theme->getTheme() != $template) { |
||||||
379 | $template = $theme->getTheme(); |
||||||
380 | } |
||||||
381 | |||||||
382 | // Ensure template still exists |
||||||
383 | $theme = $this->factory->getTheme($template); |
||||||
384 | if (empty($theme) || $theme->getTheme() !== $template) { |
||||||
385 | $template = $this->coreParametersHelper->get('theme'); |
||||||
386 | } |
||||||
387 | |||||||
388 | $analytics = $this->factory->getHelper('template.analytics')->getCode(); |
||||||
389 | |||||||
390 | if (!empty($analytics)) { |
||||||
391 | $this->factory->getHelper('template.assets')->addCustomDeclaration($analytics); |
||||||
392 | } |
||||||
393 | |||||||
394 | $logicalName = $this->factory->getHelper('theme')->checkForTwigTemplate(':'.$template.':message.html.php'); |
||||||
395 | |||||||
396 | return $this->render( |
||||||
397 | $logicalName, |
||||||
398 | [ |
||||||
399 | 'message' => $message, |
||||||
400 | 'type' => 'notice', |
||||||
401 | 'email' => $email, |
||||||
402 | 'lead' => $lead, |
||||||
403 | 'template' => $template, |
||||||
404 | ] |
||||||
405 | ); |
||||||
406 | } |
||||||
407 | |||||||
408 | /** |
||||||
409 | * Handles mailer transport webhook post. |
||||||
410 | * |
||||||
411 | * @param $transport |
||||||
412 | * |
||||||
413 | * @return Response |
||||||
414 | */ |
||||||
415 | public function mailerCallbackAction($transport) |
||||||
416 | { |
||||||
417 | ignore_user_abort(true); |
||||||
418 | |||||||
419 | $realTransport = $this->container->get('swiftmailer.transport.real'); |
||||||
420 | |||||||
421 | if (!$realTransport instanceof CallbackTransportInterface || $realTransport->getCallbackPath() !== $transport) { |
||||||
422 | return $this->notFound(); |
||||||
423 | } |
||||||
424 | |||||||
425 | $event = new TransportWebhookEvent($realTransport, $this->request); |
||||||
426 | $this->dispatcher->dispatch(EmailEvents::ON_TRANSPORT_WEBHOOK, $event); |
||||||
427 | |||||||
428 | return new Response('success'); |
||||||
429 | } |
||||||
430 | |||||||
431 | /** |
||||||
432 | * Preview email. |
||||||
433 | * |
||||||
434 | * @param $objectId |
||||||
435 | * |
||||||
436 | * @return \Symfony\Component\HttpFoundation\Response |
||||||
437 | */ |
||||||
438 | public function previewAction($objectId) |
||||||
439 | { |
||||||
440 | /** @var \Mautic\EmailBundle\Model\EmailModel $model */ |
||||||
441 | $model = $this->getModel('email'); |
||||||
442 | $emailEntity = $model->getEntity($objectId); |
||||||
443 | |||||||
444 | if (null === $emailEntity) { |
||||||
445 | return $this->notFound(); |
||||||
446 | } |
||||||
447 | |||||||
448 | if ( |
||||||
449 | ($this->get('mautic.security')->isAnonymous() && (!$emailEntity->isPublished() || !$emailEntity->isPublicPreview())) |
||||||
450 | || (!$this->get('mautic.security')->isAnonymous() |
||||||
451 | && !$this->get('mautic.security')->hasEntityAccess( |
||||||
452 | 'email:emails:viewown', |
||||||
453 | 'email:emails:viewother', |
||||||
454 | $emailEntity->getCreatedBy() |
||||||
455 | )) |
||||||
456 | ) { |
||||||
457 | return $this->accessDenied(); |
||||||
458 | } |
||||||
459 | |||||||
460 | //bogus ID |
||||||
461 | $idHash = 'xxxxxxxxxxxxxx'; |
||||||
462 | |||||||
463 | $BCcontent = $emailEntity->getContent(); |
||||||
464 | $content = $emailEntity->getCustomHtml(); |
||||||
465 | if (empty($content) && !empty($BCcontent)) { |
||||||
466 | $template = $emailEntity->getTemplate(); |
||||||
467 | $slots = $this->factory->getTheme($template)->getSlots('email'); |
||||||
468 | |||||||
469 | $assetsHelper = $this->factory->getHelper('template.assets'); |
||||||
470 | |||||||
471 | $assetsHelper->addCustomDeclaration('<meta name="robots" content="noindex">'); |
||||||
472 | |||||||
473 | $this->processSlots($slots, $emailEntity); |
||||||
474 | |||||||
475 | $logicalName = $this->factory->getHelper('theme')->checkForTwigTemplate(':'.$template.':email.html.php'); |
||||||
476 | |||||||
477 | $response = $this->render( |
||||||
478 | $logicalName, |
||||||
479 | [ |
||||||
480 | 'inBrowser' => true, |
||||||
481 | 'slots' => $slots, |
||||||
482 | 'content' => $emailEntity->getContent(), |
||||||
483 | 'email' => $emailEntity, |
||||||
484 | 'lead' => null, |
||||||
485 | 'template' => $template, |
||||||
486 | ] |
||||||
487 | ); |
||||||
488 | |||||||
489 | //replace tokens |
||||||
490 | $content = $response->getContent(); |
||||||
491 | } |
||||||
492 | |||||||
493 | // Convert emojis |
||||||
494 | $content = EmojiHelper::toEmoji($content, 'short'); |
||||||
495 | |||||||
496 | // Override tracking_pixel |
||||||
497 | $tokens = ['{tracking_pixel}' => '']; |
||||||
498 | |||||||
499 | // Prepare a fake lead |
||||||
500 | /** @var \Mautic\LeadBundle\Model\FieldModel $fieldModel */ |
||||||
501 | $fieldModel = $this->getModel('lead.field'); |
||||||
502 | $fields = $fieldModel->getFieldList(false, false); |
||||||
503 | array_walk( |
||||||
504 | $fields, |
||||||
505 | function (&$field) { |
||||||
506 | $field = "[$field]"; |
||||||
507 | } |
||||||
508 | ); |
||||||
509 | $fields['id'] = 0; |
||||||
510 | |||||||
511 | // Generate and replace tokens |
||||||
512 | $event = new EmailSendEvent( |
||||||
513 | null, |
||||||
514 | [ |
||||||
515 | 'content' => $content, |
||||||
516 | 'email' => $emailEntity, |
||||||
517 | 'idHash' => $idHash, |
||||||
518 | 'tokens' => $tokens, |
||||||
519 | 'internalSend' => true, |
||||||
520 | 'lead' => $fields, |
||||||
521 | ] |
||||||
522 | ); |
||||||
523 | $this->dispatcher->dispatch(EmailEvents::EMAIL_ON_DISPLAY, $event); |
||||||
524 | |||||||
525 | $content = $event->getContent(true); |
||||||
526 | |||||||
527 | if ($this->get('mautic.security')->isAnonymous()) { |
||||||
528 | $content = $this->get('mautic.helper.template.analytics')->addCode($content); |
||||||
529 | } |
||||||
530 | |||||||
531 | return new Response($content); |
||||||
532 | } |
||||||
533 | |||||||
534 | /** |
||||||
535 | * @param $slots |
||||||
536 | * @param Email $entity |
||||||
537 | */ |
||||||
538 | public function processSlots($slots, $entity) |
||||||
539 | { |
||||||
540 | /** @var \Mautic\CoreBundle\Templating\Helper\SlotsHelper $slotsHelper */ |
||||||
541 | $slotsHelper = $this->factory->getHelper('template.slots'); |
||||||
542 | |||||||
543 | $content = $entity->getContent(); |
||||||
544 | |||||||
545 | foreach ($slots as $slot => $slotConfig) { |
||||||
546 | if (is_numeric($slot)) { |
||||||
547 | $slot = $slotConfig; |
||||||
548 | $slotConfig = []; |
||||||
549 | } |
||||||
550 | |||||||
551 | $value = isset($content[$slot]) ? $content[$slot] : ''; |
||||||
552 | $slotsHelper->set($slot, $value); |
||||||
553 | } |
||||||
554 | } |
||||||
555 | |||||||
556 | /** |
||||||
557 | * @param $integration |
||||||
558 | * |
||||||
559 | * @throws \Exception |
||||||
560 | */ |
||||||
561 | private function doTracking($integration) |
||||||
562 | { |
||||||
563 | $logger = $this->get('monolog.logger.mautic'); |
||||||
564 | |||||||
565 | // if additional data were sent with the tracking pixel |
||||||
566 | $query_string = $this->request->server->get('QUERY_STRING'); |
||||||
567 | if (!$query_string) { |
||||||
568 | $logger->log('error', $integration.': query string is not available'); |
||||||
569 | |||||||
570 | return; |
||||||
571 | } |
||||||
572 | |||||||
573 | if (0 === strpos($query_string, 'r=')) { |
||||||
574 | $query_string = substr($query_string, strpos($query_string, '?') + 1); |
||||||
575 | } // remove route variable |
||||||
576 | |||||||
577 | parse_str($query_string, $query); |
||||||
578 | |||||||
579 | // URL attr 'd' is encoded so let's decode it first. |
||||||
580 | if (!isset($query['d'], $query['sig'])) { |
||||||
581 | $logger->log('error', $integration.': query variables are not found'); |
||||||
582 | |||||||
583 | return; |
||||||
584 | } |
||||||
585 | |||||||
586 | // get secret from plugin settings |
||||||
587 | $integrationHelper = $this->get('mautic.helper.integration'); |
||||||
588 | $myIntegration = $integrationHelper->getIntegrationObject($integration); |
||||||
589 | |||||||
590 | if (!$myIntegration) { |
||||||
591 | $logger->log('error', $integration.': integration not found'); |
||||||
592 | |||||||
593 | return; |
||||||
594 | } |
||||||
595 | $keys = $myIntegration->getDecryptedApiKeys(); |
||||||
596 | |||||||
597 | // generate signature |
||||||
598 | $salt = $keys['secret']; |
||||||
599 | if (false === strpos($salt, '$1$')) { |
||||||
600 | $salt = '$1$'.$salt; |
||||||
601 | } // add MD5 prefix |
||||||
602 | $cr = crypt(urlencode($query['d']), $salt); |
||||||
603 | $mySig = hash('crc32b', $cr); // this hash type is used in c# |
||||||
604 | |||||||
605 | // compare signatures |
||||||
606 | if (hash_equals($mySig, $query['sig'])) { |
||||||
607 | // decode and parse query variables |
||||||
608 | $b64 = base64_decode($query['d']); |
||||||
609 | $gz = gzdecode($b64); |
||||||
610 | parse_str($gz, $query); |
||||||
611 | } else { |
||||||
612 | // signatures don't match: stop |
||||||
613 | $logger->log('error', $integration.': signatures don\'t match'); |
||||||
614 | |||||||
615 | unset($query); |
||||||
616 | } |
||||||
617 | |||||||
618 | if (empty($query) || !isset($query['email'], $query['subject'], $query['body'])) { |
||||||
619 | $logger->log('error', $integration.': query variables are empty'); |
||||||
620 | |||||||
621 | return; |
||||||
622 | } |
||||||
623 | |||||||
624 | if (MAUTIC_ENV === 'dev') { |
||||||
625 | $logger->log('error', $integration.': '.json_encode($query, JSON_PRETTY_PRINT)); |
||||||
626 | } |
||||||
627 | |||||||
628 | /** @var \Mautic\EmailBundle\Model\EmailModel $model */ |
||||||
629 | $model = $this->getModel('email'); |
||||||
630 | |||||||
631 | // email is a semicolon delimited list of emails |
||||||
632 | $emails = explode(';', $query['email']); |
||||||
633 | $repo = $this->getModel('lead')->getRepository(); |
||||||
634 | |||||||
635 | foreach ($emails as $email) { |
||||||
636 | $lead = $repo->getLeadByEmail($email); |
||||||
0 ignored issues
–
show
Bug
introduced
by
Loading history...
|
|||||||
637 | if (null === $lead) { |
||||||
638 | $lead = $this->createLead($email, $repo); |
||||||
639 | } |
||||||
640 | |||||||
641 | if (null === $lead) { |
||||||
642 | continue; |
||||||
643 | } // lead was not created |
||||||
644 | |||||||
645 | $idHash = hash('crc32', $email.$query['body']); |
||||||
646 | $idHash = substr($idHash.$idHash, 0, 13); // 13 bytes length |
||||||
647 | |||||||
648 | $stat = $model->getEmailStatus($idHash); |
||||||
649 | |||||||
650 | // stat doesn't exist, create one |
||||||
651 | if (null === $stat) { |
||||||
652 | $lead['email'] = $email; // needed for stat |
||||||
653 | $stat = $this->addStat($lead, $email, $query, $idHash); |
||||||
654 | } |
||||||
655 | |||||||
656 | $stat->setSource('email.client'); |
||||||
657 | |||||||
658 | if ($stat || 'Outlook' !== $integration) { // Outlook requests the tracking gif on send |
||||||
659 | $model->hitEmail($idHash, $this->request); // add email event |
||||||
660 | } |
||||||
661 | } |
||||||
662 | } |
||||||
663 | |||||||
664 | /** |
||||||
665 | * @param $integration |
||||||
666 | * |
||||||
667 | * @return Response |
||||||
668 | */ |
||||||
669 | public function pluginTrackingGifAction($integration) |
||||||
670 | { |
||||||
671 | $this->doTracking($integration); |
||||||
672 | |||||||
673 | return TrackingPixelHelper::getResponse($this->request); // send gif |
||||||
674 | } |
||||||
675 | |||||||
676 | /** |
||||||
677 | * @param $lead |
||||||
678 | * @param $email |
||||||
679 | * @param $query |
||||||
680 | * @param $idHash |
||||||
681 | */ |
||||||
682 | private function addStat($lead, $email, $query, $idHash) |
||||||
683 | { |
||||||
684 | if (null !== $lead) { |
||||||
685 | /** @var \Mautic\EmailBundle\Helper\MailHelper $mailer */ |
||||||
686 | $mailer = $this->get('mautic.helper.mailer'); |
||||||
687 | |||||||
688 | // To lead |
||||||
689 | $mailer->addTo($email); |
||||||
690 | |||||||
691 | // sanitize variables to prevent malicious content |
||||||
692 | $from = filter_var($query['from'], FILTER_SANITIZE_EMAIL); |
||||||
693 | $mailer->setFrom($from, ''); |
||||||
694 | |||||||
695 | // Set Content |
||||||
696 | $body = filter_var($query['body'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_HIGH); |
||||||
697 | $mailer->setBody($body); |
||||||
698 | $mailer->parsePlainText($body); |
||||||
699 | |||||||
700 | // Set lead |
||||||
701 | $mailer->setLead($lead); |
||||||
702 | $mailer->setIdHash($idHash); |
||||||
703 | |||||||
704 | $subject = filter_var($query['subject'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_HIGH); |
||||||
705 | $mailer->setSubject($subject); |
||||||
706 | |||||||
707 | return $mailer->createEmailStat(); |
||||||
708 | } |
||||||
709 | |||||||
710 | return null; |
||||||
711 | } |
||||||
712 | |||||||
713 | /** |
||||||
714 | * @param $email |
||||||
715 | * @param $repo |
||||||
716 | * |
||||||
717 | * @return mixed |
||||||
718 | */ |
||||||
719 | private function createLead($email, $repo) |
||||||
720 | { |
||||||
721 | $model = $this->getModel('lead.lead'); |
||||||
722 | $lead = $model->getEntity(); |
||||||
723 | // set custom field values |
||||||
724 | $data = ['email' => $email]; |
||||||
725 | $model->setFieldValues($lead, $data, true); |
||||||
0 ignored issues
–
show
The method
setFieldValues() does not exist on Mautic\CoreBundle\Model\AbstractCommonModel . It seems like you code against a sub-type of Mautic\CoreBundle\Model\AbstractCommonModel such as Mautic\LeadBundle\Model\LeadModel or Mautic\LeadBundle\Model\CompanyModel .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||||
726 | // create lead |
||||||
727 | $model->saveEntity($lead); |
||||||
0 ignored issues
–
show
The method
saveEntity() does not exist on Mautic\CoreBundle\Model\AbstractCommonModel . It seems like you code against a sub-type of Mautic\CoreBundle\Model\AbstractCommonModel such as Mautic\CampaignBundle\Model\EventLogModel or Mautic\CoreBundle\Model\FormModel .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||||
728 | |||||||
729 | // return entity |
||||||
730 | return $repo->getLeadByEmail($email); |
||||||
731 | } |
||||||
732 | |||||||
733 | /** |
||||||
734 | * @param $idHash |
||||||
735 | * @param $model |
||||||
736 | * @param $stat |
||||||
737 | * @param $translator |
||||||
738 | * |
||||||
739 | * @return mixed |
||||||
740 | */ |
||||||
741 | public function getUnsubscribeMessage($idHash, $model, $stat, $translator) |
||||||
742 | { |
||||||
743 | $model->setDoNotContact($stat, $translator->trans('mautic.email.dnc.unsubscribed'), DoNotContact::UNSUBSCRIBED); |
||||||
744 | |||||||
745 | $message = $this->coreParametersHelper->get('unsubscribe_message'); |
||||||
746 | if (!$message) { |
||||||
747 | $message = $translator->trans( |
||||||
748 | 'mautic.email.unsubscribed.success', |
||||||
749 | [ |
||||||
750 | '%resubscribeUrl%' => '|URL|', |
||||||
751 | '%email%' => '|EMAIL|', |
||||||
752 | ] |
||||||
753 | ); |
||||||
754 | } |
||||||
755 | |||||||
756 | return str_replace( |
||||||
757 | [ |
||||||
758 | '|URL|', |
||||||
759 | '|EMAIL|', |
||||||
760 | ], |
||||||
761 | [ |
||||||
762 | $this->generateUrl('mautic_email_resubscribe', ['idHash' => $idHash]), |
||||||
763 | $stat->getEmailAddress(), |
||||||
764 | ], |
||||||
765 | $message |
||||||
766 | ); |
||||||
767 | } |
||||||
768 | } |
||||||
769 |