![]() Server : Apache System : Linux server2.corals.io 4.18.0-348.2.1.el8_5.x86_64 #1 SMP Mon Nov 15 09:17:08 EST 2021 x86_64 User : corals ( 1002) PHP Version : 7.4.33 Disable Function : exec,passthru,shell_exec,system Directory : /home/corals/mautic.corals.io/app/bundles/FormBundle/EventListener/ |
<?php namespace Mautic\FormBundle\EventListener; use GuzzleHttp\Client; use GuzzleHttp\Exception\ClientException; use GuzzleHttp\Exception\ServerException; use GuzzleHttp\Psr7\Response; use Mautic\CoreBundle\Helper\IpLookupHelper; use Mautic\CoreBundle\Helper\LanguageHelper; use Mautic\CoreBundle\Model\AuditLogModel; use Mautic\EmailBundle\Helper\MailHelper; use Mautic\FormBundle\Event as Events; use Mautic\FormBundle\Exception\ValidationException; use Mautic\FormBundle\Form\Type\SubmitActionEmailType; use Mautic\FormBundle\Form\Type\SubmitActionRepostType; use Mautic\FormBundle\FormEvents; use Mautic\LeadBundle\Entity\Lead; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Routing\RouterInterface; use Symfony\Contracts\Translation\TranslatorInterface; class FormSubscriber implements EventSubscriberInterface { private MailHelper $mailer; public function __construct( private IpLookupHelper $ipLookupHelper, private AuditLogModel $auditLogModel, MailHelper $mailer, private TranslatorInterface $translator, private RouterInterface $router, private LanguageHelper $languageHelper ) { $this->mailer = $mailer->getMailer(); } public static function getSubscribedEvents(): array { return [ FormEvents::FORM_POST_SAVE => ['onFormPostSave', 0], FormEvents::FORM_POST_DELETE => ['onFormDelete', 0], FormEvents::FORM_ON_BUILD => ['onFormBuilder', 0], FormEvents::ON_EXECUTE_SUBMIT_ACTION => [ ['onFormSubmitActionSendEmail', 0], ['onFormSubmitActionRepost', 0], ], ]; } /** * Add an entry to the audit log. */ public function onFormPostSave(Events\FormEvent $event): void { $form = $event->getForm(); if ($details = $event->getChanges()) { $log = [ 'bundle' => 'form', 'object' => 'form', 'objectId' => $form->getId(), 'action' => ($event->isNew()) ? 'create' : 'update', 'details' => $details, 'ipAddress' => $this->ipLookupHelper->getIpAddressFromRequest(), ]; $this->auditLogModel->writeToLog($log); } if (!array_key_exists($form->getLanguage(), $this->languageHelper->getSupportedLanguages())) { $this->languageHelper->extractLanguagePackage($form->getLanguage()); } } /** * Add a delete entry to the audit log. */ public function onFormDelete(Events\FormEvent $event): void { $form = $event->getForm(); $log = [ 'bundle' => 'form', 'object' => 'form', 'objectId' => $form->deletedId, 'action' => 'delete', 'details' => ['name' => $form->getName()], 'ipAddress' => $this->ipLookupHelper->getIpAddressFromRequest(), ]; $this->auditLogModel->writeToLog($log); } /** * Add a simple email form. */ public function onFormBuilder(Events\FormBuilderEvent $event): void { $event->addSubmitAction('form.email', [ 'group' => 'mautic.email.actions', 'label' => 'mautic.form.action.sendemail', 'description' => 'mautic.form.action.sendemail.descr', 'formType' => SubmitActionEmailType::class, 'formTheme' => '@MauticForm/FormTheme/FormAction/_formaction_properties_row.html.twig', 'formTypeCleanMasks' => [ 'message' => 'raw', ], 'eventName' => FormEvents::ON_EXECUTE_SUBMIT_ACTION, 'allowCampaignForm' => true, ]); $event->addSubmitAction('form.repost', [ 'group' => 'mautic.form.actions', 'label' => 'mautic.form.action.repost', 'description' => 'mautic.form.action.repost.descr', 'formType' => SubmitActionRepostType::class, 'formTheme' => '@MauticForm/FormTheme/SubmitAction/_submit_action_repost_widget.html.twig', 'formTypeCleanMasks' => [ 'post_url' => 'url', 'failure_email' => 'string', 'authorization_header' => 'string', ], 'eventName' => FormEvents::ON_EXECUTE_SUBMIT_ACTION, 'allowCampaignForm' => true, ]); } public function onFormSubmitActionSendEmail(Events\SubmissionEvent $event): void { if (!$event->checkContext('form.email')) { return; } // replace line brakes with <br> for textarea values if ($tokens = $event->getTokens()) { foreach ($tokens as &$value) { $value = nl2br(html_entity_decode($value, ENT_QUOTES)); } unset($value); } $config = $event->getActionConfig(); $lead = $event->getSubmission()->getLead(); $leadEmail = null !== $lead ? $lead->getEmail() : null; $ccEmails = $bccEmails = []; $emails = $this->getEmailsFromString($config['to']); if (isset($config['cc']) && '' !== $config['cc']) { $ccEmails = $this->getEmailsFromString($config['cc']); unset($config['cc']); } if (isset($config['bcc']) && '' !== $config['bcc']) { $bccEmails = $this->getEmailsFromString($config['bcc']); unset($config['bcc']); } if (count($emails) > 0 || count($ccEmails) > 0 || count($bccEmails) > 0) { $this->setMailer($config, $tokens, $emails, $lead); // Check for !isset to keep BC to existing behavior prior to 2.13.0 if ((!isset($config['set_replyto']) || !empty($config['set_replyto'])) && !empty($leadEmail)) { // Reply to lead for user convenience $this->mailer->setReplyTo($leadEmail); } if (count($ccEmails) > 0) { $this->mailer->setCc($ccEmails); } if (count($bccEmails) > 0) { $this->mailer->setBcc($bccEmails); } $this->mailer->send(true); } if ($config['copy_lead'] && !empty($leadEmail)) { // Send copy to lead $this->setMailer($config, $tokens, [$leadEmail => null], $lead, false); $this->mailer->send(true); } $owner = null !== $lead ? $lead->getOwner() : null; if (!empty($config['email_to_owner']) && $config['email_to_owner'] && null !== $owner) { // Send copy to owner $this->setMailer($config, $tokens, [$owner->getEmail() => null], $lead); $this->mailer->send(true); } } public function onFormSubmitActionRepost(Events\SubmissionEvent $event): void { if (!$event->checkContext('form.repost')) { return; } $post = $event->getPost(); $results = $event->getResults(); $config = $event->getActionConfig(); $fields = $event->getFields(); $lead = $event->getSubmission()->getLead(); $matchedFields = []; $payload = [ 'mautic_contact' => $lead->getProfileFields(), 'mautic_form' => [ 'id' => $post['formId'], 'name' => $post['formName'], 'url' => $post['return'], ], ]; $fieldTypes = []; foreach ($fields as $field) { $fieldTypes[$field['alias']] = $field['type']; if (!isset($post[$field['alias']]) || 'button' == $field['type']) { continue; } $key = (!empty($config[$field['alias']])) ? $config[$field['alias']] : $field['alias']; // Use the cleaned value by default - but if set to not save result, get from post $value = $results[$field['alias']] ?? $post[$field['alias']]; $matchedFields[$key] = $field['alias']; // decode html chars and quotes before posting to next form $payload[$key] = html_entity_decode(htmlspecialchars_decode($value, ENT_QUOTES), ENT_QUOTES); } $event->setPostSubmitPayload($payload); $headers = [ 'X-Forwarded-For' => $event->getSubmission()->getIpAddress()->getIpAddress(), ]; if (!empty($config['authorization_header'])) { if (str_contains($config['authorization_header'], ':')) { [$key, $value] = explode(':', $config['authorization_header']); } else { $key = 'Authorization'; $value = $config['authorization_header']; } $headers[trim($key)] = trim($value); } try { $client = new Client(['timeout' => 15]); $response = $client->post( $config['post_url'], [ 'form_params' => $payload, 'headers' => $headers, ] ); if ($redirect = $this->parseResponse($response, $matchedFields)) { $event->setPostSubmitCallbackResponse('form.repost', new RedirectResponse($redirect)); } } catch (ClientException|ServerException $exception) { $this->parseResponse($exception->getResponse(), $matchedFields); } catch (\Exception $exception) { if ($exception instanceof ValidationException) { if ($violations = $exception->getViolations()) { throw $exception; } } $email = $config['failure_email']; // Failed so send email if applicable if (!empty($email)) { // Remove Mautic values and password fields foreach ($post as $key => $value) { if (in_array($key, ['messenger', 'submit', 'formId', 'formid', 'formName', 'return'])) { unset($post[$key]); } if (isset($fieldTypes[$key]) && in_array($fieldTypes[$key], ['password'])) { $post[$key] = '*********'; } } $post['mautic_contact'] = array_filter($payload['mautic_contact']); $post['mautic_form'] = $payload['mautic_form']; $results = $this->postToHtml($post); $submission = $event->getSubmission(); $emails = $emails = $this->getEmailsFromString($email); $this->mailer->setTo($emails); $this->mailer->setSubject( $this->translator->trans('mautic.form.action.repost.failed_subject', ['%form%' => $submission->getForm()->getName()]) ); $this->mailer->setBody( $this->translator->trans( 'mautic.form.action.repost.failed_message', [ '%link%' => $this->router->generate( 'mautic_form_results', ['objectId' => $submission->getForm()->getId(), 'result' => $submission->getId()], UrlGeneratorInterface::ABSOLUTE_URL ), '%message%' => $exception->getMessage(), '%results%' => $results, ] ) ); $this->mailer->parsePlainText(); $this->mailer->send(); } } } /** * @return bool|mixed */ private function parseResponse(Response $response, array $matchedFields = []) { $body = (string) $response->getBody(); $error = false; $redirect = false; $violations = []; if ($json = json_decode($body, true)) { $body = $json; } else { parse_str($body, $output); if ($output) { $body = $output; } } if (is_array($body)) { if (isset($body['error'])) { $error = $body['error']; } elseif (isset($body['errors'])) { $error = implode(', ', $body['errors']); } elseif (isset($body['violations'])) { $error = $this->translator->trans('mautic.form.action.repost.validation_failed'); $formViolations = $body['violations']; // Ensure the violations match up to Mautic's foreach ($formViolations as $field => $violation) { if (isset($matchedFields[$field])) { $violations[$matchedFields[$field]] = $violation; } else { $error .= ' '.$violation; } } } elseif (isset($body['redirect'])) { $redirect = $body['redirect']; } } if (!$error && 200 !== $response->getStatusCode()) { $error = (string) $response->getBody(); } if ($error || $violations) { $exception = (new ValidationException($error)) ->setViolations($violations); throw $exception; } return $redirect; } private function postToHtml($post): string { $output = '<table>'; foreach ($post as $key => $row) { $output .= "<tr><td style='vertical-align: top'><strong>$key</strong></td><td>"; if (is_array($row)) { $output .= $this->postToHtml($row); } else { $output .= $row; } $output .= '</td></tr>'; } return $output.'</table>'; } /** * @return array<string, null> */ private function getEmailsFromString($emailString): array { return (!empty($emailString)) ? array_fill_keys(array_map('trim', explode(',', $emailString)), null) : []; } /** * @param array<mixed> $config * @param array<mixed> $tokens * @param array<string, string|null> $to */ private function setMailer(array $config, array $tokens, array $to, Lead $lead = null, bool $internalSend = true): void { $this->mailer->reset(); if (count($to)) { $this->mailer->setTo($to); } $this->mailer->setSubject($config['subject']); $this->mailer->addTokens($tokens); $this->mailer->setBody($config['message']); $this->mailer->parsePlainText($config['message']); if ($lead) { $this->mailer->setLead($lead->getProfileFields(), $internalSend); } } }