/** * @param object $entity * @param \Symfony\Component\Form\FormFactory $formFactory * @param null $action * @param array $options * * @return \Symfony\Component\Form\FormInterface */ public function createForm($entity, $formFactory, $action = null, $options = []) { $fields = $this->leadFieldModel->getFieldListWithProperties(); $choices = []; foreach ($fields as $alias => $field) { if (!isset($choices[$field['group_label']])) { $choices[$field['group_label']] = []; } $choices[$field['group_label']][$alias] = $field['label']; } // Only show the lead fields not already used $usedLeadFields = $this->session->get('mautic.form.' . $entity['formId'] . '.fields.leadfields', []); $testLeadFields = array_flip($usedLeadFields); $currentLeadField = isset($entity['leadField']) ? $entity['leadField'] : null; if (!empty($currentLeadField) && isset($testLeadFields[$currentLeadField])) { unset($testLeadFields[$currentLeadField]); } foreach ($choices as &$group) { $group = array_diff_key($group, $testLeadFields); } $options['leadFields'] = $choices; $options['leadFieldProperties'] = $fields; if ($action) { $options['action'] = $action; } return $formFactory->create('formfield', $entity, $options); }
/** * @param $post * @param $server * @param Form $form * * @return bool|array */ public function saveSubmission($post, $server, Form $form, Request $request = null, $returnEvent = false) { $leadFields = $this->leadFieldModel->getFieldListWithProperties(false); //everything matches up so let's save the results $submission = new Submission(); $submission->setDateSubmitted(new \DateTime()); $submission->setForm($form); //set the landing page the form was submitted from if applicable if (!empty($post['mauticpage'])) { $page = $this->pageModel->getEntity((int) $post['mauticpage']); if ($page != null) { $submission->setPage($page); } } $ipAddress = $this->ipLookupHelper->getIpAddress(); $submission->setIpAddress($ipAddress); if (!empty($post['return'])) { $referer = $post['return']; } elseif (!empty($server['HTTP_REFERER'])) { $referer = $server['HTTP_REFERER']; } else { $referer = ''; } //clean the referer by removing mauticError and mauticMessage $referer = InputHelper::url($referer, null, null, ['mauticError', 'mauticMessage']); $submission->setReferer($referer); // Create an event to be dispatched through the processes $submissionEvent = new SubmissionEvent($submission, $post, $server, $request); // Get a list of components to build custom fields from $components = $this->formModel->getCustomComponents(); $fields = $form->getFields(); $fieldArray = []; $results = []; $tokens = []; $leadFieldMatches = []; $validationErrors = []; /** @var Field $f */ foreach ($fields as $f) { $id = $f->getId(); $type = $f->getType(); $alias = $f->getAlias(); $value = isset($post[$alias]) ? $post[$alias] : ''; $fieldArray[$id] = ['id' => $id, 'type' => $type, 'alias' => $alias]; if ($type == 'captcha') { $captcha = $this->fieldHelper->validateFieldValue($type, $value, $f); if (!empty($captcha)) { $props = $f->getProperties(); //check for a custom message $validationErrors[$alias] = !empty($props['errorMessage']) ? $props['errorMessage'] : implode('<br />', $captcha); } continue; } if ($f->isRequired() && empty($value)) { //field is required, but hidden from form because of 'ShowWhenValueExists' if ($f->getShowWhenValueExists() === false && !isset($post[$alias])) { continue; } //somehow the user got passed the JS validation $msg = $f->getValidationMessage(); if (empty($msg)) { $msg = $this->translator->trans('mautic.form.field.generic.validationfailed', ['%label%' => $f->getLabel()], 'validators'); } $validationErrors[$alias] = $msg; continue; } if (in_array($type, $components['viewOnlyFields'])) { //don't save items that don't have a value associated with it continue; } //clean and validate the input if ($f->isCustom()) { if (!isset($components['fields'][$f->getType()])) { continue; } $params = $components['fields'][$f->getType()]; if (!empty($value)) { if (isset($params['valueFilter'])) { if (is_string($params['valueFilter']) && is_callable(['\\Mautic\\CoreBundle\\Helper\\InputHelper', $params['valueFilter']])) { $value = InputHelper::_($value, $params['valueFilter']); } elseif (is_callable($params['valueFilter'])) { $value = call_user_func_array($params['valueFilter'], [$f, $value]); } else { $value = InputHelper::_($value, 'clean'); } } else { $value = InputHelper::_($value, 'clean'); } } // @deprecated - BC support; to be removed in 3.0 - be sure to remove support in FormBuilderEvent as well if (isset($params['valueConstraints']) && is_callable($params['valueConstraints'])) { $customErrors = call_user_func_array($params['valueConstraints'], [$f, $value]); if (!empty($customErrors)) { $validationErrors[$alias] = is_array($customErrors) ? implode('<br />', $customErrors) : $customErrors; } } } elseif (!empty($value)) { $filter = $this->fieldHelper->getFieldFilter($type); $value = InputHelper::_($value, $filter); $isValid = $this->validateFieldValue($f, $value); if (true !== $isValid) { $validationErrors[$alias] = is_array($isValid) ? implode('<br />', $isValid) : $isValid; } } // Check for custom validators $isValid = $this->validateFieldValue($f, $value); if (true !== $isValid) { $validationErrors[$alias] = $isValid; } $leadField = $f->getLeadField(); if (!empty($leadField)) { $leadValue = $value; if (is_array($leadValue)) { // Multiselect lead fields store the values with bars $delimeter = 'multiselect' === $leadFields[$leadField]['type'] ? '|' : ', '; $leadValue = implode($delimeter, $leadValue); } $leadFieldMatches[$leadField] = $leadValue; } //convert array from checkbox groups and multiple selects if (is_array($value)) { $value = implode(', ', $value); } $tokens["{formfield={$alias}}"] = $value; //save the result if ($f->getSaveResult() !== false) { $results[$alias] = $value; } } // Set the results $submission->setResults($results); // Update the event $submissionEvent->setFields($fieldArray)->setTokens($tokens)->setResults($results)->setContactFieldMatches($leadFieldMatches); // @deprecated - BC support; to be removed in 3.0 - be sure to remove the validator option from addSubmitAction as well $this->validateActionCallbacks($submissionEvent, $validationErrors, $alias); //return errors if there any - this should be moved to right after foreach($fields) once validateActionCallbacks support is dropped if (!empty($validationErrors)) { return ['errors' => $validationErrors]; } // Create/update lead if (!empty($leadFieldMatches)) { $lead = $this->createLeadFromSubmit($form, $leadFieldMatches, $leadFields); $submission->setLead($lead); } // Get updated lead if applicable with tracking ID if ($form->isInKioskMode()) { $lead = $this->leadModel->getCurrentLead(); } else { list($lead, $trackingId, $generated) = $this->leadModel->getCurrentLead(true); //set tracking ID for stats purposes to determine unique hits $submission->setTrackingId($trackingId); } $submission->setLead($lead); // Save the submission $this->saveEntity($submission); // Now handle post submission actions try { $this->executeFormActions($submissionEvent); } catch (ValidationException $exception) { // The action invalidated the form for whatever reason $this->deleteEntity($submission); if ($validationErrors = $exception->getViolations()) { return ['errors' => $validationErrors]; } return ['errors' => [$exception->getMessage()]]; } if (!$form->isStandalone()) { // Find and add the lead to the associated campaigns $campaigns = $this->campaignModel->getCampaignsByForm($form); if (!empty($campaigns)) { foreach ($campaigns as $campaign) { $this->campaignModel->addLead($campaign, $lead); } } } if ($this->dispatcher->hasListeners(FormEvents::FORM_ON_SUBMIT)) { // Reset action config from executeFormActions() $submissionEvent->setActionConfig(null, []); // Dispatch to on submit listeners $this->dispatcher->dispatch(FormEvents::FORM_ON_SUBMIT, $submissionEvent); } //get callback commands from the submit action if ($submissionEvent->hasPostSubmitCallbacks()) { return ['callback' => $submissionEvent]; } // made it to the end so return the submission event to give the calling method access to tokens, results, etc // otherwise return false that no errors were encountered (to keep BC really) return $returnEvent ? ['submission' => $submissionEvent] : false; }
/** * Generate the form's html. * * @param Form $entity * @param bool $persist * * @return string */ public function generateHtml(Form $entity, $persist = true) { //generate cached HTML $theme = $entity->getTemplate(); $submissions = null; $lead = $this->leadModel->getCurrentLead(); $style = ''; if (!empty($theme)) { $theme .= '|'; } if ($entity->usesProgressiveProfiling()) { $submissions = $this->getRepository()->getFormResults($entity, ['leadId' => $lead->getId(), 'limit' => 200]); } if ($entity->getRenderStyle()) { $templating = $this->templatingHelper->getTemplating(); $styleTheme = $theme . 'MauticFormBundle:Builder:style.html.php'; $style = $templating->render($this->themeHelper->checkForTwigTemplate($styleTheme)); } // Determine pages $fields = $entity->getFields()->toArray(); // Ensure the correct order in case this is generated right after a form save with new fields uasort($fields, function ($a, $b) { if ($a->getOrder() === $b->getOrder()) { return 0; } return $a->getOrder() < $b->getOrder() ? -1 : 1; }); $pages = ['open' => [], 'close' => []]; $openFieldId = $closeFieldId = $previousId = $lastPage = false; $pageCount = 1; foreach ($fields as $fieldId => $field) { if ('pagebreak' == $field->getType() && $openFieldId) { // Open the page $pages['open'][$openFieldId] = $pageCount; $openFieldId = false; $lastPage = $fieldId; // Close the page at the next page break if ($previousId) { $pages['close'][$previousId] = $pageCount; ++$pageCount; } } else { if (!$openFieldId) { $openFieldId = $fieldId; } } $previousId = $fieldId; } if (!empty($pages)) { if ($openFieldId) { $pages['open'][$openFieldId] = $pageCount; } if ($previousId !== $lastPage) { $pages['close'][$previousId] = $pageCount; } } $html = $this->templatingHelper->getTemplating()->render($theme . 'MauticFormBundle:Builder:form.html.php', ['fieldSettings' => $this->getCustomComponents()['fields'], 'fields' => $fields, 'contactFields' => $this->leadFieldModel->getFieldListWithProperties(), 'form' => $entity, 'theme' => $theme, 'submissions' => $submissions, 'lead' => $lead, 'formPages' => $pages, 'lastFormPage' => $lastPage, 'style' => $style]); if (!$entity->usesProgressiveProfiling()) { $entity->setCachedHtml($html); if ($persist) { //bypass model function as events aren't needed for this $this->getRepository()->saveEntity($entity); } } return $html; }