custom/plugins/MoorlFormBuilder/src/Core/Service/FormService.php line 210

Open in your IDE?
  1. <?php
  2. namespace MoorlFormBuilder\Core\Service;
  3. use \SoapClient;
  4. use Doctrine\DBAL\Connection;
  5. use MoorlFormBuilder\Core\Content\Form\FormCollection;
  6. use MoorlFormBuilder\Core\Content\Form\FormEntity;
  7. use MoorlFormBuilder\Core\Event\AutocompleteCriteriaEvent;
  8. use MoorlFormBuilder\Core\Event\CmsFormEvent;
  9. use MoorlFormBuilder\Core\Event\FormCriteriaEvent;
  10. use MoorlFormBuilder\Core\Event\FormFireEvent;
  11. use MoorlFormBuilder\Core\Event\FormLoadEvent;
  12. use MoorlFormBuilder\Core\Event\FormOptionCriteriaEvent;
  13. use MoorlFormBuilder\MoorlFormBuilder;
  14. use MoorlFoundation\Core\PluginHelpers;
  15. use Shopware\Core\Content\Media\MediaCollection;
  16. use Shopware\Core\Framework\Adapter\Translation\Translator;
  17. use Shopware\Core\Framework\Context;
  18. use Shopware\Core\Framework\DataAbstractionLayer\DefinitionInstanceRegistry;
  19. use Shopware\Core\Framework\DataAbstractionLayer\Entity;
  20. use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
  21. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  22. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\ContainsFilter;
  23. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
  24. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\MultiFilter;
  25. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\NotFilter;
  26. use Shopware\Core\Framework\DataAbstractionLayer\Search\Grouping\FieldGrouping;
  27. use Shopware\Core\Framework\DataAbstractionLayer\Search\Sorting\FieldSorting;
  28. use Shopware\Core\Framework\Event\EventData\MailRecipientStruct;
  29. use Shopware\Core\Framework\Uuid\Uuid;
  30. use Shopware\Core\Framework\Validation\DataBag\RequestDataBag;
  31. use Shopware\Core\System\SalesChannel\SalesChannelContext;
  32. use Shopware\Core\System\SystemConfig\SystemConfigService;
  33. use Shopware\Storefront\Framework\Captcha\AbstractCaptcha;
  34. use Shopware\Storefront\Framework\Captcha\Exception\CaptchaInvalidException;
  35. use Shopware\Storefront\Framework\Routing\RequestTransformer;
  36. use Symfony\Component\HttpFoundation\File\UploadedFile;
  37. use Symfony\Component\HttpFoundation\RequestStack;
  38. use Symfony\Component\HttpFoundation\Session\Session;
  39. use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
  40. class FormService
  41. {
  42.     protected Criteria $formCriteria;
  43.     protected StorefrontMediaUploader $mediaUploader;
  44.     protected ?array $blacklist;
  45.     private Context $context;
  46.     private ?SalesChannelContext $salesChannelContext = null;
  47.     private DefinitionInstanceRegistry $definitionInstanceRegistry;
  48.     private NewsletterService $newsletterService;
  49.     private SystemConfigService $systemConfigService;
  50.     private Connection $connection;
  51.     private EventDispatcherInterface $eventDispatcher;
  52.     private EntityRepositoryInterface $formRepo;
  53.     private array $whitelist;
  54.     private ?string $localeCode = null;
  55.     private ?string $formId = null;
  56.     private ?FormEntity $form = null;
  57.     private ?FormCollection $forms = null;
  58.     private ?Session $session = null;
  59.     private array $violations = [];
  60.     private RequestStack $requestStack;
  61.     private bool $checkCache = false;
  62.     /**
  63.      * @var iterable<AbstractCaptcha>
  64.      */
  65.     private iterable $captchas;
  66.     private Translator $translator;
  67.     public function __construct(
  68.         NewsletterService $newsletterService,
  69.         DefinitionInstanceRegistry $definitionInstanceRegistry,
  70.         Connection $connection,
  71.         SystemConfigService $systemConfigService,
  72.         EventDispatcherInterface $eventDispatcher,
  73.         EntityRepositoryInterface $formRepo,
  74.         StorefrontMediaUploader $mediaUploader,
  75.         RequestStack $requestStack,
  76.         iterable $captchas,
  77.         array $allowedExtensions,
  78.         Translator $translator
  79.     )
  80.     {
  81.         $this->newsletterService = $newsletterService;
  82.         $this->definitionInstanceRegistry = $definitionInstanceRegistry;
  83.         $this->connection = $connection;
  84.         $this->systemConfigService = $systemConfigService;
  85.         $this->eventDispatcher = $eventDispatcher;
  86.         $this->formRepo = $formRepo;
  87.         $this->mediaUploader = $mediaUploader;
  88.         $this->requestStack = $requestStack;
  89.         $this->whitelist = $allowedExtensions;
  90.         $this->captchas = $captchas;
  91.         $this->translator = $translator;
  92.         $this->context = Context::createDefaultContext();
  93.     }
  94.     /**
  95.      * @param FormEntity|null $form
  96.      */
  97.     public function setForm(?FormEntity $form): void
  98.     {
  99.         $this->form = $form;
  100.     }
  101.     /**
  102.      * @param bool $checkCache
  103.      */
  104.     public function setCheckCache(bool $checkCache): void
  105.     {
  106.         $this->checkCache = $checkCache;
  107.     }
  108.     public function fire(Context $context, SalesChannelContext $salesChannelContext = null): ?array
  109.     {
  110.         $this->setContext($context);
  111.         $this->setSalesChannelContext($salesChannelContext);
  112.         $this->initForm();
  113.         if ($this->hasViolations()) {
  114.             return $this->getViolations();
  115.         }
  116.         if ($this->getSalesChannelContext()) {
  117.             $salesChannelId = $this->getSalesChannelContext()->getSalesChannel()->getId();
  118.             $email = $this->systemConfigService->get('core.basicInformation.email', $salesChannelId);
  119.             $blacklist = $this->systemConfigService->get('MoorlFormBuilder.config.blacklist', $salesChannelId);
  120.         } else {
  121.             $email = $this->systemConfigService->get('core.basicInformation.email');
  122.             $blacklist = $this->systemConfigService->get('MoorlFormBuilder.config.blacklist');
  123.         }
  124.         $this->cacheUserData($email);
  125.         if ($this->getFormId()) {
  126.             // example: add to cart item has no form id and no data
  127.             $this->checkCaptcha();
  128.             $this->setBlacklist($blacklist);
  129.             $this->validateUserData();
  130.             $this->validateFiles();
  131.             if ($this->hasViolations()) {
  132.                 return $this->getViolations();
  133.             }
  134.             $this->uploadFiles();
  135.             if ($this->hasViolations()) {
  136.                 return $this->getViolations();
  137.             }
  138.         }
  139.         $this->setCurrentFormPayload();
  140.         $this->setCurrentFormSummaryHTML();
  141.         $this->setCurrentFormSummaryPlain();
  142.         return null;
  143.     }
  144.     public function initForm(): bool
  145.     {
  146.         if (!$this->form) {
  147.             if (!$this->getFormId()) {
  148.                 $this->violations[] = $this->trans('moorl-form-builder.general.internalError');
  149.             } else {
  150.                 $this->initCurrentForm($this->getFormId());
  151.                 if (!$this->form) {
  152.                     $this->violations[] = $this->trans('moorl-form-builder.general.internalError');
  153.                 }
  154.             }
  155.         }
  156.         return $this->hasViolations();
  157.     }
  158.     public function getFormId(): ?string
  159.     {
  160.         if (!$this->formId) {
  161.             $this->formId = $this->requestStack->getCurrentRequest()->get('_form_id');
  162.         }
  163.         return $this->formId;
  164.     }
  165.     public function setFormId(string $formId): void
  166.     {
  167.         $this->formId = $formId;
  168.     }
  169.     public function trans(string $snippet, ?array $data = []): array
  170.     {
  171.         return [
  172.             'snippet' => $snippet,
  173.             'data' => $data
  174.         ];
  175.     }
  176.     public function initCurrentForm(?string $formId = null)
  177.     {
  178.         $this->setCurrentForm($formId);
  179.         $this->_initCurrentForm();
  180.     }
  181.     public function setCurrentForm(?string $formId = null)
  182.     {
  183.         if ($formId || $this->getFormId()) {
  184.             if ($formId) {
  185.                 $this->setFormId($formId);
  186.             }
  187.             if (!$this->forms || !$this->forms->get($this->getFormId())) {
  188.                 $this->setFormById($this->getFormId());
  189.             }
  190.             $this->form = $this->forms->get($this->getFormId());
  191.         } else {
  192.             if (!$this->forms || !$this->forms->last()) {
  193.                 $this->setForms(); // context and criteria need to be set
  194.             }
  195.             $this->form = $this->forms->last(); // get last inserted form
  196.         }
  197.     }
  198.     public function setFormById(string $formId): void
  199.     {
  200.         $this->setFormCriteria(new Criteria([$formId]));
  201.         $this->setForms();
  202.     }
  203.     private function _initCurrentForm(): void
  204.     {
  205.         $this->getLocaleCode();
  206.         if ($this->form) {
  207.             if ($this->form->getInitialized()) {
  208.                 return;
  209.             }
  210.             $this->addEntitySelections();
  211.             $event = new FormLoadEvent($this->getContext(), $this->form);
  212.             $this->eventDispatcher->dispatch($event);
  213.             $this->setFormDataFromSession();
  214.             $this->setFormDataFromCustomer();
  215.             $this->setCurrentFormPayload();
  216.             $this->setCurrentFormSummaryHTML();
  217.             $this->setCurrentFormSummaryPlain();
  218.             $this->form->setInitialized(true);
  219.             $this->forms->add($this->form);
  220.         }
  221.     }
  222.     public function getLocaleCode(): string
  223.     {
  224.         if (!$this->localeCode && $this->getContext()) {
  225.             $repository = $this->definitionInstanceRegistry->getRepository('language');
  226.             $criteria = new Criteria([$this->getContext()->getLanguageId()]);
  227.             $criteria->addAssociation('locale');
  228.             $this->localeCode = $repository->search($criteria, $this->getContext())->first()->getLocale()->getCode() ?: 'en-GB';
  229.         }
  230.         return $this->localeCode ?: 'en-GB';
  231.     }
  232.     public function getContext(): Context
  233.     {
  234.         return $this->context;
  235.     }
  236.     public function setContext(Context $context)
  237.     {
  238.         $this->context = $context;
  239.     }
  240.     private function addEntitySelections(): void
  241.     {
  242.         $locale = $this->getLocaleCode();
  243.         /* Set translations FormEntity */
  244.         $this->form->setTranslated([
  245.             'label' => $this->getLocalVariable($this->form->getLabel()),
  246.             'successMessage' => $this->getLocalVariable($this->form->getSuccessMessage()),
  247.             'submitText' => $this->getLocalVariable($this->form->getSubmitText()),
  248.         ]);
  249.         $elements = $this->form->getData();
  250.         foreach ($elements as &$element) {
  251.             if ($this->getIsSet($element, 'isEntitySelect')) {
  252.                 $repository = $this->definitionInstanceRegistry->getRepository($element['entitySelect']['relatedEntity']);
  253.                 $criteria = new Criteria();
  254.                 // Product Media Hack
  255.                 if ($element['entitySelect']['relatedEntity'] == 'product') {
  256.                     $criteria->addAssociation('cover');
  257.                 }
  258.                 $criteria->addSorting(new FieldSorting($element['entitySelect']['labelProperty'], FieldSorting::ASCENDING));
  259.                 $event = new FormOptionCriteriaEvent($this->getContext(), $criteria, $element, $this->form->getAction());
  260.                 $this->eventDispatcher->dispatch($event);
  261.                 $collection = $repository->search($criteria, $this->getContext())->getEntities();
  262.                 $options = [];
  263.                 foreach ($collection as $entity) {
  264.                     $mediaId = null;
  265.                     if ($this->getIsSet($element, 'useImageSelection')) {
  266.                         // Product Media Hack
  267.                         if ($element['entitySelect']['relatedEntity'] == 'product' && $entity->getCover()) {
  268.                             $mediaId = $entity->getCover()->getMedia()->getId();
  269.                         } else {
  270.                             $mediaId = $entity->get($element['entitySelect']['mediaProperty']);
  271.                         }
  272.                     }
  273.                     try {
  274.                         if (isset($entity->getTranslated()[$element['entitySelect']['labelProperty']])) {
  275.                             $label = $entity->getTranslated()[$element['entitySelect']['labelProperty']];
  276.                         } else {
  277.                             $label = $entity->get($element['entitySelect']['labelProperty']);
  278.                         }
  279.                     } catch (\Exception $exception) {
  280.                     }
  281.                     $options[] = [
  282.                         'id' => $entity->get($element['entitySelect']['valueProperty']),
  283.                         'value' => $entity->get($element['entitySelect']['valueProperty']),
  284.                         'label' => [
  285.                             $locale => $label
  286.                         ],
  287.                         'entity' => $entity, // set complete entity for customizing
  288.                         'mediaId' => $mediaId,
  289.                         'useTrans' => null,
  290.                         'emailReceiver' => null,
  291.                     ];
  292.                 }
  293.                 $element['options'] = $options;
  294.             }
  295.             /* Set translations FormEntity - Element */
  296.             $element['translated'] = [
  297.                 'label' => $this->getLocalVariable($this->getIsSet($element, 'label')),
  298.                 'placeholder' => $this->getLocalVariable($this->getIsSet($element, 'placeholder')),
  299.             ];
  300.             /* Set translations FormEntity - Element options */
  301.             foreach ($element['options'] as &$option) {
  302.                 $option['translated'] = [
  303.                     'label' => $this->getLocalVariable($this->getIsSet($option, 'label')),
  304.                     'customField1' => $this->getLocalVariable($this->getIsSet($option, 'customField1')),
  305.                     'customField2' => $this->getLocalVariable($this->getIsSet($option, 'customField2')),
  306.                     'customField3' => $this->getLocalVariable($this->getIsSet($option, 'customField3')),
  307.                 ];
  308.             }
  309.         }
  310.         $this->form->setData($elements);
  311.     }
  312.     public function getLocalVariable(?array $var): ?string
  313.     {
  314.         if (isset($var[$this->localeCode])) {
  315.             return $var[$this->localeCode];
  316.         } elseif (is_array($var) && count($var) > 0) {
  317.             return reset($var);
  318.         } else {
  319.             return null;
  320.         }
  321.     }
  322.     public function getIsSet(?array $var, string $index, $alt = null)
  323.     {
  324.         if (isset($var[$index])) {
  325.             return $var[$index];
  326.         } else {
  327.             return $alt;
  328.         }
  329.     }
  330.     public function setFormDataFromCustomer(): void
  331.     {
  332.         if ($this->form->getRelatedEntity() !== 'customer') {
  333.             return;
  334.         }
  335.         if (!$this->getSalesChannelContext()) {
  336.             return;
  337.         }
  338.         $customer = $this->getSalesChannelContext()->getCustomer();
  339.         if (!$customer) {
  340.             return;
  341.         }
  342.         $customerCustomFields = $customer->getCustomFields();
  343.         $formElements = $this->form->getData();
  344.         foreach ($formElements as &$formElement) {
  345.             if ($formElement['mapping']) {
  346.                 $mapping = explode('.', $formElement['mapping']);
  347.                 if ($mapping[0] === 'customFields') {
  348.                     $formElement['value'] = $this->getIsSet($customerCustomFields, $mapping[1]);
  349.                 } else {
  350.                     $formElement['value'] = $customer->get($mapping[0]);
  351.                 }
  352.             }
  353.         }
  354.         $this->form->setData($formElements);
  355.     }
  356.     public function setFormDataFromSession(): void
  357.     {
  358.         if ($this->checkCache && getenv("SHOPWARE_HTTP_CACHE_ENABLED") == "1") {
  359.             return; // prevent prefill when cache turned on
  360.         }
  361.         if ($this->checkCache && $this->systemConfigService->get('MoorlFormBuilder.config.disableCache')) {
  362.             return; // prevent prefill by config
  363.         }
  364.         $this->initSession();
  365.         $userValues = $this->session->get('moorl-form-builder.values_' . $this->form->getId());
  366.         if (!$userValues) {
  367.             return;
  368.         }
  369.         $formElements = $this->form->getData();
  370.         foreach ($formElements as &$formElement) {
  371.             if ($userValue = $this->getIsSet($userValues, $formElement['name'])) {
  372.                 $formElement['value'] = $userValue;
  373.             }
  374.         }
  375.         $this->form->setData($formElements);
  376.     }
  377.     public function initSession(): void
  378.     {
  379.         if (!$this->session) {
  380.             $this->session = new Session();
  381.         }
  382.     }
  383.     public function validateTaxIdNumber(string $taxIdNumber): bool
  384.     {
  385.         try {
  386.             $vatid = preg_replace("/[^a-zA-Z0-9]]/", "", $taxIdNumber);
  387.             $vatidRegex = "/^[a-z]{2}[a-z0-9]{0,12}$/i";
  388.             if (preg_match($vatidRegex, $vatid) !== 1) {
  389.                 throw new \Exception('Invalid Vat Id Format');
  390.             }
  391.             $client = new \SoapClient("https://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl");
  392.             $params = [
  393.                 'countryCode' => substr($vatid, 0, 2),
  394.                 'vatNumber' => substr($vatid, 2),
  395.             ];
  396.             $result = $client->checkVatApprox($params);
  397.             if ($result->valid) {
  398.                 return true;
  399.             } else {
  400.                 return false;
  401.             }
  402.         } catch (\Exception $exception) {
  403.             return false;
  404.         }
  405.     }
  406.     public function setCurrentFormSummaryPlain()
  407.     {
  408.         $medias = $this->getMediaCollection();
  409.         $html = "\n";
  410.         foreach ($this->form->getData() as $formElement) {
  411.             if ($formElement['value'] || in_array($formElement['type'], ['checkbox', 'switch'])) {
  412.                 $html .= $formElement['translated']['label'] . " ";
  413.                 switch ($formElement['type']) {
  414.                     case 'datepicker':
  415.                         $html .= $formElement['value'];
  416.                         break;
  417.                     case 'upload':
  418.                         $media = $medias->get($formElement['value']);
  419.                         if ($media) {
  420.                             $html .= $media->getUrl();
  421.                         }
  422.                         break;
  423.                     case 'select':
  424.                     case 'multiselect':
  425.                     case 'radio-group':
  426.                     case 'checkbox-group':
  427.                         foreach ($formElement['options'] as $option) {
  428.                             if ($option['value'] == $formElement['value'] || (is_array($formElement['value']) && in_array($option['value'], $formElement['value']))) {
  429.                                 $html .= $option['translated']['label'] . " ";
  430.                                 if (isset($option['priceCalculation']) && is_numeric($option['priceCalculation'])) {
  431.                                     // Todo: get the currency here!?
  432.                                     $html .= number_format($option['priceCalculation'], 2, ',', '.');
  433.                                 }
  434.                             } elseif (isset($formElement['value'][$option['value']])) {
  435.                                 $html .= $option['translated']['label'] . ': ' . $formElement['value'][$option['value']] . ' ';
  436.                             }
  437.                         }
  438.                         break;
  439.                     case 'repeater-open':
  440.                         $html .= $this->form->getRepeaterDataValue($formElement['name']);
  441.                         break;
  442.                     case 'checkbox':
  443.                     case 'switch':
  444.                         $html .= empty($formElement['value']) ? $this->translator->trans("moorl-form-builder.general.no") : $this->translator->trans("moorl-form-builder.general.yes");
  445.                         break;
  446.                     default:
  447.                         $html .= $formElement['value'];
  448.                 }
  449.                 $html .= "\n";
  450.             }
  451.         }
  452.         $this->form->setSummaryPlain($html);
  453.     }
  454.     public function setCurrentFormSummaryHTML()
  455.     {
  456.         $medias = $this->getMediaCollection();
  457.         $html = '<table border="0" style="font-size: 1em; margin-bottom: 5px">';
  458.         foreach ($this->form->getData() as $formElement) {
  459.             $addHtml = '';
  460.             if ($formElement['value'] || in_array($formElement['type'], ['checkbox', 'switch'])) {
  461.                 $html .= '<tr><td bgcolor="#F7F7F2" style="border-bottom:1px solid #cccccc;">' . $formElement['translated']['label'] . '</td>';
  462.                 if ($this->getIsSet($formElement, 'usePriceCalculation')) {
  463.                     $cols = 1;
  464.                 } else {
  465.                     $cols = 2;
  466.                 }
  467.                 $html .= '<td colspan="' . $cols . '" style="border-bottom:1px solid #cccccc;">';
  468.                 switch ($formElement['type']) {
  469.                     case 'datepicker':
  470.                         $html .= $formElement['value'];
  471.                         break;
  472.                     case 'upload':
  473.                         $media = $medias->get($formElement['value']);
  474.                         if ($media) {
  475.                             if (in_array($media->getFileExtension(), ['png', 'jpg', 'jpeg', 'gif'])) {
  476.                                 $html .= '<a href="' . $media->getUrl() . '"><img style="width: 100px;" src="' . $media->getUrl() . '" alt="' . $media->getFileName() . '"></a>';
  477.                             } else {
  478.                                 $html .= '<a href="' . $media->getUrl() . '">' . $formElement['translated']['label'] . '</a>';
  479.                             }
  480.                         }
  481.                         break;
  482.                     case 'select':
  483.                     case 'multiselect':
  484.                     case 'radio-group':
  485.                     case 'checkbox-group':
  486.                         $addHtml = '<td align="right" style="border-bottom:1px solid #cccccc;">';
  487.                         foreach ($formElement['options'] as $option) {
  488.                             if ($option['value'] == $formElement['value'] || (is_array($formElement['value']) && in_array($option['value'], $formElement['value']))) {
  489.                                 $html .= $option['translated']['label'] . '<br>';
  490.                                 if (isset($option['priceCalculation']) && is_numeric($option['priceCalculation'])) {
  491.                                     // Todo: get the currency here!?
  492.                                     $addHtml .= number_format($option['priceCalculation'], 2, ',', '.') . '<br>';
  493.                                 }
  494.                             } elseif (isset($formElement['value'][$option['value']])) {
  495.                                 $html .= $option['translated']['label'] . ': ' . $formElement['value'][$option['value']] . '<br>';
  496.                             }
  497.                         }
  498.                         $addHtml .= '</td>';
  499.                         break;
  500.                     case 'repeater-open':
  501.                         $html .= nl2br($this->form->getRepeaterDataValue($formElement['name']));
  502.                         break;
  503.                     case 'checkbox':
  504.                     case 'switch':
  505.                     $html .= empty($formElement['value']) ? $this->translator->trans("moorl-form-builder.general.no") : $this->translator->trans("moorl-form-builder.general.yes");
  506.                         break;
  507.                     default:
  508.                         $html .= nl2br(trim(str_replace(["'", "\""], "&quot;", $formElement['value'])));
  509.                 }
  510.                 $html .= '</td>' . ($cols == 1 ? $addHtml : '') . '</tr>';
  511.             }
  512.         }
  513.         $html .= '</table>';
  514.         $html = strip_tags($html, '<table><tr><td><br><img><a>');
  515.         $this->form->setSummaryHTML($html);
  516.     }
  517.     public function getMediaCollection(?array $mediaIds = null): ?MediaCollection
  518.     {
  519.         if ($this->form->getMedias()) {
  520.             return $this->form->getMedias();
  521.         }
  522.         if (!$mediaIds) {
  523.             $mediaIds = [];
  524.             foreach ($this->form->getData() as $formElement) {
  525.                 if ($formElement['value'] && $formElement['type'] == 'upload') {
  526.                     $mediaIds[] = $formElement['value'];
  527.                 }
  528.             }
  529.         }
  530.         if (count($mediaIds) > 0) {
  531.             $repo = $this->definitionInstanceRegistry->getRepository('media');
  532.             $this->form->setMedias($repo->search(new Criteria($mediaIds), $this->getContext())->getEntities());
  533.         }
  534.         return $this->form->getMedias();
  535.     }
  536.     public function setCurrentFormPayload(): void
  537.     {
  538.         $payload = [];
  539.         foreach ($this->form->getData() as $formElement) {
  540.             if ($formElement['name'] && $formElement['mapping'] && $formElement['value']) {
  541.                 if (!empty($formElement['value'])) {
  542.                     if ($formElement['value'] === 'true') {
  543.                         $payload[$formElement['mapping']] = true;
  544.                     } elseif ($formElement['value'] === 'false') {
  545.                         $payload[$formElement['mapping']] = false;
  546.                     } else {
  547.                         if (in_array($formElement['type'], ['switch','checkbox'])) {
  548.                             $payload[$formElement['mapping']] = true;
  549.                         } else {
  550.                             $payload[$formElement['mapping']] = $formElement['value'];
  551.                         }
  552.                     }
  553.                 }
  554.             }
  555.             $formElements[] = $formElement;
  556.         }
  557.         PluginHelpers::getNestedVar($payload);
  558.         $this->form->setPayload($payload);
  559.     }
  560.     public function hasViolations(): bool
  561.     {
  562.         return count($this->violations) > 0 ? true : false;
  563.     }
  564.     public function getViolations(): array
  565.     {
  566.         return $this->violations;
  567.     }
  568.     /**
  569.      * @return SalesChannelContext|null
  570.      */
  571.     public function getSalesChannelContext(): ?SalesChannelContext
  572.     {
  573.         return $this->salesChannelContext;
  574.     }
  575.     /**
  576.      * @param SalesChannelContext|null $salesChannelContext
  577.      */
  578.     public function setSalesChannelContext(?SalesChannelContext $salesChannelContext): void
  579.     {
  580.         $this->salesChannelContext = $salesChannelContext;
  581.         if ($salesChannelContext) {
  582.             $this->context = $salesChannelContext->getContext();
  583.         }
  584.     }
  585.     public function cacheUserData($fallbackReceiver = null): bool
  586.     {
  587.         $data = $this->requestStack->getCurrentRequest();
  588.         $receivers = $this->form->getEmailReceiver() ? explode(";", $this->form->getEmailReceiver()) : ($fallbackReceiver ? [$fallbackReceiver] : []);
  589.         $customerEmailReceiver = [];
  590.         $userValues = [
  591.             'storefrontUrl' => $data->attributes->get(RequestTransformer::STOREFRONT_URL)
  592.         ];
  593.         $formElements = [];
  594.         foreach ($this->form->getData() as $formElement) {
  595.             $requestValue = $data->get($formElement['name']);
  596.             if ($formElement['name'] && $requestValue) {
  597.                 if (!empty($requestValue)) {
  598.                     if ($requestValue instanceof RequestDataBag) {
  599.                         $requestValue = $requestValue->all();
  600.                     }
  601.                     $userValues[$formElement['name']] = $requestValue;
  602.                     $formElement['value'] = $requestValue;
  603.                     if ($formElement['type'] == 'email' && ($this->form->getSendCopyType() === 'always' || $data->get('_send_copy'))) {
  604.                         $customerEmailReceiver[] = $requestValue;
  605.                     }
  606.                     if ($formElement['type'] == 'email') {
  607.                         $this->form->setReplyTo($requestValue);
  608.                     }
  609.                     if ($this->getIsSet($formElement, 'hasEmailReceiver') && is_array($formElement['options'])) {
  610.                         foreach ($formElement['options'] as $option) {
  611.                             if ($this->getIsSet($option, 'emailReceiver')) {
  612.                                 if (is_array($requestValue)) {
  613.                                     if (in_array($option['value'], $requestValue)) {
  614.                                         $receivers = array_merge(
  615.                                             $receivers,
  616.                                             explode(";", $option['emailReceiver'])
  617.                                         );
  618.                                     }
  619.                                 } else {
  620.                                     if ($option['value'] == $requestValue) {
  621.                                         $receivers = array_merge(
  622.                                             $receivers,
  623.                                             explode(";", $option['emailReceiver'])
  624.                                         );
  625.                                     }
  626.                                 }
  627.                             }
  628.                         }
  629.                     }
  630.                 }
  631.             }
  632.             $formElements[] = $formElement;
  633.         }
  634.         $this->form->setEmailReceiver(implode(";", $receivers));
  635.         $this->form->setCustomerEmailReceiver(implode(";", $customerEmailReceiver));
  636.         $this->form->setData($formElements);
  637.         $this->form->setUserValues($userValues);
  638.         $this->addSessionUserValues($userValues);
  639.         return $this->hasViolations();
  640.     }
  641.     public function addSessionUserValues(array $userValues = [], ?string $id = null): void
  642.     {
  643.         $this->initSession();
  644.         if ($id) {
  645.             $id = $id . $this->form->getId();
  646.         } else {
  647.             $id = $this->form->getId();
  648.         }
  649.         $userValues = array_merge($this->form->getUserValues(), $userValues);
  650.         $this->session->set('moorl-form-builder.values_' . $id, $userValues);
  651.     }
  652.     public function checkCaptcha(): bool
  653.     {
  654.         $this->initSession();
  655.         if ($this->form->getUseCaptcha()) {
  656.             $activeCaptchas = $this->systemConfigService->get('core.basicInformation.activeCaptchasV2');
  657.             foreach ($this->captchas as $captcha) {
  658.                 $captchaConfig = $activeCaptchas[$captcha->getName()] ?? [];
  659.                 $request = $this->requestStack->getCurrentRequest();
  660.                 if (!$captcha->supports($request, $captchaConfig)) {
  661.                     continue;
  662.                 }
  663.                 if (!$captcha->isValid($request, $captchaConfig)) {
  664.                     if ($captcha->shouldBreak()) {
  665.                         throw new CaptchaInvalidException($captcha);
  666.                     }
  667.                     $this->violations[] = $this->trans('moorl-form-builder.general.captchaError');
  668.                 }
  669.             }
  670.         }
  671.         return $this->hasViolations();
  672.     }
  673.     public function setBlacklist(?string $blacklist): void
  674.     {
  675.         if ($blacklist) {
  676.             $blacklist = explode(',', $blacklist);
  677.             $blacklist = array_map('trim', $blacklist);
  678.             $blacklist = array_map('mb_strtolower', $blacklist);
  679.             $this->blacklist = $blacklist;
  680.         }
  681.     }
  682.     public function validateUserData(): bool
  683.     {
  684.         $data = $this->requestStack->getCurrentRequest();
  685.         $formRepeaterName = null;
  686.         foreach ($this->form->getData() as $formElement) {
  687.             if ($formElement['type'] === 'repeater-open') {
  688.                 $formRepeaterName = $formElement['name'];
  689.             }
  690.             if ($formElement['type'] === 'repeater-close') {
  691.                 $formRepeaterName = null;
  692.             }
  693.             if ($formRepeaterName) {
  694.                 // Hotfix: Form elements within repeater will be validated client side
  695.                 continue;
  696.             }
  697.             $requestValue = $data->get($formElement['name']);
  698.             if (!$requestValue) {
  699.                 continue;
  700.             }
  701.             if ($formElement['type'] === 'tax-id-number') {
  702.                 if (!$this->validateTaxIdNumber($requestValue)) {
  703.                     $this->violations[] = $this->trans('moorl-form-builder.general.taxIdNumberError', [
  704.                         '%value%' => $this->getIsSet($formElement['translated'], 'label', $formElement['name']),
  705.                     ]);
  706.                 }
  707.                 continue;
  708.             }
  709.             if ($requestValue instanceof RequestDataBag) {
  710.                 $requestValue = $requestValue->all();
  711.             }
  712.             // Hotfix: Form elements with conditions will be validated client side
  713.             $formElementConditions = (isset($formElement['conditions']) && count($formElement['conditions']) > 0);
  714.             // Hotfix: Form elements with file uploads are set after the validation
  715.             $formElementIsUpload = ($formElement['type'] === 'upload');
  716.             if (!$requestValue && !$formElementConditions && !$formElementIsUpload && $formElement['required']) {
  717.                 $this->violations[] = $this->trans('moorl-form-builder.general.requiredError', [
  718.                     '%value%' => $this->getIsSet($formElement['translated'], 'label', $formElement['name']),
  719.                 ]);
  720.             }
  721.             if (!empty($formElement['useBlacklist'])) {
  722.                 if ($requestValue && $this->inBlacklist($requestValue)) {
  723.                     $this->violations[] = $this->trans('moorl-form-builder.general.blacklistError', [
  724.                         '%value%' => $this->getIsSet($formElement['translated'], 'label', $formElement['name']),
  725.                     ]);
  726.                 }
  727.             }
  728.             if (!empty($formElement['mapping']) && isset($formElement['isUnique']) && $requestValue) {
  729.                 if (!empty($requestValue) && $formElement['isUnique']) {
  730.                     $repo = $this->definitionInstanceRegistry->getRepository($this->form->getRelatedEntity());
  731.                     $criteria = new Criteria();
  732.                     $criteria->addFilter(new EqualsFilter($formElement['mapping'], $requestValue));
  733.                     if ($repo->search($criteria, $this->getContext())->first()) {
  734.                         $this->violations[] = $this->trans('moorl-form-builder.general.duplicateError', [
  735.                             '%value%' => $requestValue,
  736.                         ]);
  737.                     }
  738.                 }
  739.             }
  740.         }
  741.         return $this->hasViolations();
  742.     }
  743.     public function inBlacklist($var): bool
  744.     {
  745.         if (!$this->blacklist || !is_array($this->blacklist)) {
  746.             return false;
  747.         }
  748.         $vars = is_array($var) ? $var : [$var];
  749.         foreach ($vars as $item) {
  750.             foreach ($this->blacklist as $blacklistItem) {
  751.                 if (is_array($item)) {
  752.                     return $this->inBlacklist($item);
  753.                 } else {
  754.                     if (strpos(mb_strtolower($item), $blacklistItem) !== false) {
  755.                         return true;
  756.                     }
  757.                 }
  758.             }
  759.         }
  760.         return false;
  761.     }
  762.     /**
  763.      * @param UploadedFile $file
  764.      * @param string $technicalName
  765.      *
  766.      * Validates a single UploadedFile and add to binary attachment
  767.      */
  768.     private function validateFile(UploadedFile $file, string $technicalName): void
  769.     {
  770.         $formElement = $this->form->getFormElement($technicalName);
  771.         if ($formElement->getType() !== 'upload') {
  772.             return;
  773.         }
  774.         if (!$this->mediaUploader->validate($file, $formElement->getMediaType())) {
  775.             $this->violations[] = $this->trans('moorl-form-builder.general.mediaTypeError', [
  776.                 '%name%' => pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME),
  777.                 '%extension%' => $file->getClientOriginalExtension(),
  778.                 '%expected%' => $formElement->getMediaType()
  779.             ]);
  780.         }
  781.         if (!$this->mediaUploader->checkSize($file)) {
  782.             $this->violations[] = $this->trans('moorl-form-builder.general.mediaSizeError', [
  783.                 '%size%' => $this->mediaUploader->getFileSize(),
  784.                 '%maxSize%' => $this->mediaUploader->getMaxFileSize(),
  785.             ]);
  786.         }
  787.         if ($this->hasViolations()) {
  788.             return;
  789.         }
  790.         $formElement->setValue($file->getClientOriginalName());
  791.         $this->form->addBinAttachment([
  792.             'content' => file_get_contents($file->getPathname()),
  793.             'fileName' => $file->getClientOriginalName(),
  794.             'mimeType' => $file->getMimeType(),
  795.         ]);
  796.     }
  797.     public function validateFiles(): bool
  798.     {
  799.         $request = $this->requestStack->getCurrentRequest();
  800.         $this->mediaUploader->setMaxFileSize($this->form->getMaxFileSize() * 1000000); // MB to Bytes
  801.         foreach ($request->files as $name => $file) {
  802.             if (is_array($file)) {
  803.                 foreach ($file as $repeaterItem) {
  804.                     if (!is_array($repeaterItem)) {
  805.                         continue;
  806.                     }
  807.                     foreach ($repeaterItem as $repeaterName => $repeaterFile) {
  808.                         if ($repeaterFile instanceof UploadedFile) {
  809.                             $this->validateFile($repeaterFile, $repeaterName);
  810.                         }
  811.                     }
  812.                 }
  813.             } elseif ($file instanceof UploadedFile) {
  814.                 $this->validateFile($file, $name);
  815.             }
  816.         }
  817.         return $this->hasViolations();
  818.     }
  819.     private function getAttachment(UploadedFile $file): array
  820.     {
  821.         return [
  822.             'content' => file_get_contents($file->getPathname()),
  823.             'fileName' => $file->getClientOriginalName(),
  824.             'mimeType' => $file->getMimeType(),
  825.         ];
  826.     }
  827.     public function uploadFiles(): bool
  828.     {
  829.         // Attachment only if sendMail active
  830.         if ($this->form->getSendMail() && in_array($this->form->getType(), ['cms','snippet','customerRegister','productRequest'])) {
  831.             return false;
  832.         }
  833.         $request = $this->requestStack->getCurrentRequest();
  834.         $mediaIds = [];
  835.         $formElements = [];
  836.         $userValues = [];
  837.         try {
  838.             foreach ($this->form->getData() as $formElement) {
  839.                 if ($formElement['type'] == 'upload' && $file = $request->files->get($formElement['name'])) {
  840.                     $mediaId = $this->mediaUploader->upload($file, $this->form->getMediaFolderId(), $this->getContext());
  841.                     $formElement['value'] = $mediaId;
  842.                     $userValues[$formElement['name']] = $mediaId;
  843.                     $mediaIds[] = $mediaId;
  844.                     $this->addSessionUserMedia($mediaId);
  845.                 }
  846.                 $formElements[] = $formElement;
  847.             }
  848.             $this->form->setData($formElements);
  849.             if (count($mediaIds) > 0) {
  850.                 $this->getMediaCollection($mediaIds);
  851.             }
  852.         } catch (\Exception $exception) {
  853.             $this->violations[] = $this->trans('moorl-form-builder.general.internalError');
  854.             $this->violations[] = $this->trans($exception->getMessage());
  855.         }
  856.         $this->addSessionUserValues($userValues);
  857.         $this->form->setUserValues(array_merge($this->form->getUserValues(), $userValues));
  858.         return $this->hasViolations();
  859.     }
  860.     public function addSessionUserMedia(string $mediaId): void
  861.     {
  862.         $this->initSession();
  863.         $media = $this->session->get('moorl-form-builder.media') ?: [];
  864.         $media[] = $mediaId;
  865.         $this->session->set('moorl-form-builder.media', $media);
  866.     }
  867.     public function setFormDataFromEntity(Entity $entity): void
  868.     {
  869.         $formElements = $this->form->getData();
  870.         foreach ($formElements as &$formElement) {
  871.             if ($entity && isset($formElement['mapping']) && !empty($formElement['mapping'])) {
  872.                 try {
  873.                     if (isset($entity->getTranslated()[$formElement['mapping']])) {
  874.                         $formElement['value'] = $entity->getTranslated()[$formElement['mapping']];
  875.                     } else {
  876.                         $formElement['value'] = $entity->get($formElement['mapping']);
  877.                     }
  878.                 } catch (\Exception $exception) {
  879.                 }
  880.             }
  881.         }
  882.         $this->form->setData($formElements);
  883.     }
  884.     public function getFormCriteria(): Criteria
  885.     {
  886.         if (!$this->formCriteria && $this->getFormId()) {
  887.             $this->setFormCriteria(new Criteria([$this->getFormId()]));
  888.         }
  889.         return $this->formCriteria;
  890.     }
  891.     public function setFormCriteria(Criteria $formCriteria): void
  892.     {
  893.         $this->formCriteria = $formCriteria;
  894.     }
  895.     public function getFormById(string $formId): ?FormEntity
  896.     {
  897.         return $this->forms->get($formId);
  898.     }
  899.     public function setFormByCriteria(Criteria $criteria): void
  900.     {
  901.         $criteria->setLimit(1);
  902.         $criteria->addFilter(new EqualsFilter('active', true));
  903.         $this->setFormCriteria($criteria);
  904.         $this->setForms();
  905.     }
  906.     public function initCurrentFormByAction(string $action)
  907.     {
  908.         $this->form = $this->forms->getByAction($action);
  909.         $this->_initCurrentForm();
  910.     }
  911.     public function initCurrentFormByProductId(string $productId)
  912.     {
  913.         $this->form = $this->forms->getByProductId($productId);
  914.         $this->_initCurrentForm();
  915.     }
  916.     public function initCurrentFormByTypeProductId(string $type, ?string $productId = null)
  917.     {
  918.         $this->form = $this->forms->getByTypeProductId($type, $productId);
  919.         $this->_initCurrentForm();
  920.     }
  921.     public function initCurrentFormByTypeProductProperty(string $type, string $property, $value)
  922.     {
  923.         $this->form = $this->forms->getByTypeProductProperty($type, $property, $value);
  924.         $this->_initCurrentForm();
  925.     }
  926.     public function getForms(): ?FormCollection
  927.     {
  928.         return $this->forms;
  929.     }
  930.     public function setForms(): void
  931.     {
  932.         $criteria = $this->getFormCriteria();
  933.         $event = new FormCriteriaEvent($this->getContext(), $criteria);
  934.         $this->eventDispatcher->dispatch($event);
  935.         $formCollection = $this->formRepo->search($criteria, $this->getContext())->getEntities();
  936.         if ($this->forms instanceof FormCollection) {
  937.             $this->forms->merge($formCollection);
  938.         } else {
  939.             $this->forms = $formCollection;
  940.         }
  941.     }
  942.     public function initFormsByType(Context $context, string $type, $initializeDeep = true): void
  943.     {
  944.         $criteria = new Criteria();
  945.         $criteria->addFilter(new EqualsFilter('active', 1));
  946.         $criteria->addFilter(new EqualsFilter('type', $type));
  947.         if ($this->getSalesChannelContext()) {
  948.             $salesChannelId = $this->getSalesChannelContext()->getSalesChannelId();
  949.             $criteria->addFilter(new MultiFilter(MultiFilter::CONNECTION_OR, [
  950.                 new EqualsFilter('salesChannelId', $salesChannelId),
  951.                 new EqualsFilter('salesChannelId', null)
  952.             ]));
  953.         }
  954.         $criteria->addAssociation('products.children');
  955.         /* BUGFIX v6.4.6.0 */
  956.         $productsCriteria = $criteria->getAssociation('products');
  957.         $productsCriteria->addFilter(new NotFilter(NotFilter::CONNECTION_OR, [
  958.             new EqualsFilter('id', null)
  959.         ]));
  960.         $this->initForms($context, $criteria, $initializeDeep);
  961.     }
  962.     public function initFormsByTypeProductId(Context $context, string $type, ?string $productId = null, $initializeDeep = true): void
  963.     {
  964.         $criteria = new Criteria();
  965.         $criteria->addFilter(new EqualsFilter('active', 1));
  966.         $criteria->addFilter(new EqualsFilter('type', $type));
  967.         if ($this->getSalesChannelContext()) {
  968.             $salesChannelId = $this->getSalesChannelContext()->getSalesChannelId();
  969.             $criteria->addFilter(new MultiFilter(MultiFilter::CONNECTION_OR, [
  970.                 new EqualsFilter('salesChannelId', $salesChannelId),
  971.                 new EqualsFilter('salesChannelId', null)
  972.             ]));
  973.         }
  974.         $criteria->addAssociation('products.children');
  975.         $productsCriteria = $criteria->getAssociation('products');
  976.         $productsCriteria->addFilter(new EqualsFilter('id', $productId));
  977.         $this->initForms($context, $criteria, $initializeDeep);
  978.     }
  979.     public function initForms(Context $context, Criteria $criteria, $initializeDeep = true): void
  980.     {
  981.         $this->setContext($context);
  982.         $this->setFormCriteria($criteria);
  983.         $this->setForms();
  984.         if ($initializeDeep) {
  985.             foreach ($this->forms as $form) {
  986.                 if (!$form->getInitialized()) {
  987.                     $this->form = $form;
  988.                     $this->_initCurrentForm();
  989.                 }
  990.             }
  991.         }
  992.     }
  993.     public function getPageReload(): bool
  994.     {
  995.         $data = $this->requestStack->getCurrentRequest()->get('_reload');
  996.         return $data ? true : false;
  997.     }
  998.     public function unsetUserData(): void
  999.     {
  1000.         $this->initSession();
  1001.         $this->session->remove('moorl-form-builder.values_' . $this->form->getId());
  1002.     }
  1003.     public function unsetUserDataByFormId(string $formId): void
  1004.     {
  1005.         $this->initSession();
  1006.         $this->session->remove('moorl-form-builder.values_' . $formId);
  1007.     }
  1008.     public function unsetAllUserData(): void
  1009.     {
  1010.         $this->initSession();
  1011.         foreach ($this->forms as $form) {
  1012.             $this->session->remove('moorl-form-builder.values_' . $form->getId());
  1013.         }
  1014.     }
  1015.     public function autocomplete(string $formElementId, $isArray = true): array
  1016.     {
  1017.         $query = $this->requestStack->getCurrentRequest()->query->get('q');
  1018.         if (strlen($query) < 3) {
  1019.             return [];
  1020.         }
  1021.         $this->initForm();
  1022.         $formElements = $this->form->getData();
  1023.         $formElements = array_values(array_filter($formElements, function ($v, $k) use ($formElementId) {
  1024.             return $v['id'] == $formElementId;
  1025.         }, ARRAY_FILTER_USE_BOTH));
  1026.         if (count($formElements) == 0) {
  1027.             return [];
  1028.         }
  1029.         $formElement = $formElements[0];
  1030.         if (isset($formElement['autocomplete']) && isset($formElement['autocomplete']['relatedEntity']) && isset($formElement['autocomplete']['property'])) {
  1031.             $relatedEntity = $formElement['autocomplete']['relatedEntity'];
  1032.             $property = $formElement['autocomplete']['property'];
  1033.             $repo = $this->definitionInstanceRegistry->getRepository($relatedEntity);
  1034.             $criteria = new Criteria();
  1035.             $criteria->setLimit(10);
  1036.             $criteria->addFilter(new ContainsFilter($property, $query));
  1037.             $criteria->addGroupField(new FieldGrouping($property));
  1038.             $event = new AutocompleteCriteriaEvent($this->getContext(), $criteria, $formElement, $this->form->getAction());
  1039.             $this->eventDispatcher->dispatch($event);
  1040.             $results = [];
  1041.             /* @var $entity Entity */
  1042.             if ($isArray) {
  1043.                 foreach ($repo->search($criteria, $this->getContext())->getEntities() as $entity) {
  1044.                     $results[] = $entity->get($property);
  1045.                 }
  1046.             } else {
  1047.                 foreach ($repo->search($criteria, $this->getContext())->getEntities() as $entity) {
  1048.                     $results[$entity->getId()] = $entity->get($property);
  1049.                 }
  1050.             }
  1051.             return $results;
  1052.         }
  1053.         return [];
  1054.     }
  1055.     public function removeUserMedia(string $mediaId): void
  1056.     {
  1057.         $customer = $this->salesChannelContext->getCustomer();
  1058.         if ($customer) {
  1059.             $customerCustomFields = $customer->getCustomFields();
  1060.             if (is_array($customerCustomFields)) {
  1061.                 foreach ($customerCustomFields as $name => $value) {
  1062.                     if ($value === $mediaId) {
  1063.                         $this->mediaUploader->delete($mediaId, $this->getContext());
  1064.                         $payload = [
  1065.                             'id' => $customer->getId(),
  1066.                             'customFields' => [
  1067.                                 $name => null
  1068.                             ]
  1069.                         ];
  1070.                         $repository = $this->definitionInstanceRegistry->getRepository('customer');
  1071.                         $repository->upsert([$payload], $this->context);
  1072.                         return;
  1073.                     }
  1074.                 }
  1075.             }
  1076.         }
  1077.         $this->initSession();
  1078.         $media = $this->session->get('moorl-form-builder.media') ?: [];
  1079.         if (in_array($mediaId, $media)) {
  1080.             $this->mediaUploader->delete($mediaId, $this->getContext());
  1081.         } else {
  1082.             $this->violations[] = $this->trans('moorl-form-builder.general.internalError');
  1083.         }
  1084.     }
  1085.     public function fireForm()
  1086.     {
  1087.         $event = new FormFireEvent($this->getContext(), $this->form);
  1088.         $this->eventDispatcher->dispatch($event);
  1089.         if ($this->form->getSendMail()) {
  1090.             /* Send Mail to Shop Owner */
  1091.             $receivers = explode(";", $this->form->getEmailReceiver());
  1092.             $receivers = array_map('trim', $receivers);
  1093.             $receivers = array_unique($receivers);
  1094.             foreach ($receivers as $receiver) {
  1095.                 $this->setMailEvent($receiver);
  1096.             }
  1097.             /* Send Mail to Customer */
  1098.             if ($this->form->getCustomerEmailReceiver() && !empty($this->form->getCustomerEmailReceiver())) {
  1099.                 $customerMailTemplateId = $this->form->getCustomerMailTemplateId();
  1100.                 if ($customerMailTemplateId) {
  1101.                     $this->form->setMailTemplateId($customerMailTemplateId);
  1102.                 }
  1103.                 $receivers = explode(";", $this->form->getCustomerEmailReceiver());
  1104.                 $receivers = array_map('trim', $receivers);
  1105.                 $receivers = array_unique($receivers);
  1106.                 foreach ($receivers as $receiver) {
  1107.                     $this->setMailEvent($receiver);
  1108.                 }
  1109.             }
  1110.         }
  1111.         if ($this->form->getInsertNewsletter()) {
  1112.             if ($this->requestStack->getCurrentRequest()->get('_insert_newsletter')) {
  1113.                 $userValues = $this->form->getUserValues();
  1114.                 $this->newsletterService->subscribe($userValues, $this->getSalesChannelContext(), false);
  1115.             }
  1116.         }
  1117.         if ($this->form->getInsertHistory()) {
  1118.             $this->insertHistory();
  1119.         }
  1120.         if ($this->form->getInsertDatabase()) {
  1121.             $this->insertDatabase();
  1122.         }
  1123.     }
  1124.     private function setMailEvent(?string $receiver = null): void
  1125.     {
  1126.         if (!$receiver || empty($receiver)) {
  1127.             return;
  1128.         }
  1129.         $event = new CmsFormEvent(
  1130.             $this->getContext(),
  1131.             $this->getSalesChannelContext()->getSalesChannel()->getId(),
  1132.             new MailRecipientStruct([$receiver => $receiver]),
  1133.             $this->getCurrentForm(),
  1134.             $this->form->getMedias()
  1135.         );
  1136.         $this->eventDispatcher->dispatch(
  1137.             $event,
  1138.             MoorlFormBuilder::MAIL_TEMPLATE_MAIL_SEND_ACTION
  1139.         );
  1140.     }
  1141.     public function getCurrentForm(): ?FormEntity
  1142.     {
  1143.         return $this->form;
  1144.     }
  1145.     private function insertHistory(): void
  1146.     {
  1147.         $dataBag = $this->requestStack->getCurrentRequest();
  1148.         $data = [
  1149.             'id' => Uuid::randomHex(),
  1150.             'formId' => $this->form->getId(),
  1151.             'salesChannelId' => $this->getSalesChannelContext()->getSalesChannel()->getId(),
  1152.             'name' => $this->form->getName(),
  1153.             'email' => $this->form->getEmailReceiver(),
  1154.             'requestData' => $dataBag->request->all(),
  1155.             'data' => $this->form->getUserValues(),
  1156.             'media' => $this->form->getMedias() ? $this->form->getMedias()->getIds() : null
  1157.         ];
  1158.         $repository = $this->definitionInstanceRegistry->getRepository('moorl_form_history');
  1159.         $repository->create([$data], $this->getContext());
  1160.     }
  1161.     private function insertDatabase(): void
  1162.     {
  1163.         $entityName = $this->form->getRelatedEntity();
  1164.         if (!$entityName) {
  1165.             return;
  1166.         }
  1167.         if ($entityName === 'customer' && $this->salesChannelContext && $this->salesChannelContext->getCustomer()) {
  1168.             $data = ['id' => $this->salesChannelContext->getCustomer()->getId()];
  1169.         } else {
  1170.             $data = ['id' => Uuid::randomHex()];
  1171.         }
  1172.         $userValues = $this->form->getUserValues();
  1173.         foreach ($this->form->getData() as $formElement) {
  1174.             if (in_array($formElement['type'], ['checkbox','switch'])) {
  1175.                 if (isset($userValues[$formElement['name']])) {
  1176.                     $userValues[$formElement['name']] = 'true';
  1177.                 } else {
  1178.                     $userValues[$formElement['name']] = 'false';
  1179.                 }
  1180.             }
  1181.             if ($formElement['name'] && $formElement['mapping'] && isset($userValues[$formElement['name']])) {
  1182.                 if (!empty($userValues[$formElement['name']])) {
  1183.                     if ($userValues[$formElement['name']] === 'true') {
  1184.                         $data[$formElement['mapping']] = true;
  1185.                     } elseif ($userValues[$formElement['name']] === 'false') {
  1186.                         $data[$formElement['mapping']] = false;
  1187.                     } else {
  1188.                         $data[$formElement['mapping']] = $userValues[$formElement['name']];
  1189.                     }
  1190.                 }
  1191.             }
  1192.         }
  1193.         PluginHelpers::getNestedVar($data);
  1194.         $repository = $this->definitionInstanceRegistry->getRepository($entityName);
  1195.         $repository->upsert([$data], $this->getContext());
  1196.     }
  1197.     public function extendFileTypeWhitelist(?array $whitelist): array
  1198.     {
  1199.         if (!$whitelist) {
  1200.             $whitelist = $this->whitelist;
  1201.         }
  1202.         $whitelistConfig = $this->systemConfigService->get('MoorlFormBuilder.config.fileExtensions');
  1203.         if ($whitelistConfig) {
  1204.             $whitelistConfig = explode(",", $whitelistConfig);
  1205.             $whitelistConfig = array_map('trim', $whitelistConfig);
  1206.             if (is_array($whitelistConfig)) {
  1207.                 $whitelist = array_merge($whitelist, $whitelistConfig);
  1208.             }
  1209.         }
  1210.         return $whitelist;
  1211.     }
  1212.     public function sanitizeFormData()
  1213.     {
  1214.         // TODO: remove unused data to minify payloads etc
  1215.     }
  1216.     public function addFlashBag(array $violations): void
  1217.     {
  1218.         $this->initSession();
  1219.         if ($violations) {
  1220.             foreach ($violations as $violation) {
  1221.                 $this->session->getFlashBag()->add('danger', $violation['snippet']);
  1222.                 /* TODO: translate this
  1223.                 $this->trans($violation['snippet'], $violation['data']);
  1224.                 */
  1225.             }
  1226.         }
  1227.     }
  1228. }