/** * @param Form $form * @param $formHtml */ public function populateValuesWithLead(Form $form, &$formHtml) { $formName = $form->generateFormName(); $lead = $this->leadModel->getCurrentLead(); $fields = $form->getFields(); /** @var \Mautic\FormBundle\Entity\Field $f */ foreach ($fields as $f) { $leadField = $f->getLeadField(); $isAutoFill = $f->getIsAutoFill(); if (isset($leadField) && $isAutoFill) { $value = $lead->getFieldValue($leadField); if (!empty($value)) { $this->fieldHelper->populateField($f, $value, $formName, $formHtml); } } } }
/** * Validates a field value. * * @param Field $field * @param $value * * @return bool|string True if valid; otherwise string with invalid reason */ protected function validateFieldValue(Field $field, $value) { $standardValidation = $this->fieldHelper->validateFieldValue($field->getType(), $value); if (!empty($standardValidation)) { return $standardValidation; } $components = $this->formModel->getCustomComponents(); foreach ([$field->getType(), 'form'] as $type) { if (isset($components['validators'][$type])) { foreach ($components['validators'][$type] as $validator) { $event = $this->dispatcher->dispatch($validator['eventName'], new ValidationEvent($field, $value)); if (!$event->isValid()) { return $event->getInvalidReason(); } } } } return true; }
/** * Generates edit form and processes post data * * @param int $objectId * @param bool $ignorePost * @param bool $forceTypeSelection * * @return \Symfony\Component\HttpFoundation\JsonResponse|Response */ public function editAction($objectId, $ignorePost = false, $forceTypeSelection = false) { /** @var \Mautic\FormBundle\Model\FormModel $model */ $model = $this->factory->getModel('form'); $formData = $this->request->request->get('mauticform'); $sessionId = isset($formData['sessionId']) ? $formData['sessionId'] : null; if ($objectId instanceof Form) { $entity = $objectId; $objectId = sha1(uniqid(mt_rand(), true)); } else { $entity = $model->getEntity($objectId); // Process submit of cloned form if ($entity == null && $objectId == $sessionId) { $entity = $model->getEntity(); } } $session = $this->factory->getSession(); $cleanSlate = true; //set the page we came from $page = $this->factory->getSession()->get('mautic.form.page', 1); //set the return URL $returnUrl = $this->generateUrl('mautic_form_index', array('page' => $page)); $postActionVars = array('returnUrl' => $returnUrl, 'viewParameters' => array('page' => $page), 'contentTemplate' => 'MauticFormBundle:Form:index', 'passthroughVars' => array('activeLink' => '#mautic_form_index', 'mauticContent' => 'form')); //form not found if ($entity === null) { return $this->postActionRedirect(array_merge($postActionVars, array('flashes' => array(array('type' => 'error', 'msg' => 'mautic.form.error.notfound', 'msgVars' => array('%id%' => $objectId)))))); } elseif (!$this->factory->getSecurity()->hasEntityAccess('form:forms:editown', 'form:forms:editother', $entity->getCreatedBy())) { return $this->accessDenied(); } elseif ($model->isLocked($entity)) { //deny access if the entity is locked return $this->isLocked($postActionVars, $entity, 'form.form'); } $action = $this->generateUrl('mautic_form_action', array('objectAction' => 'edit', 'objectId' => $objectId)); $form = $model->createForm($entity, $this->get('form.factory'), $action); ///Check for a submitted form and process it if (!$ignorePost && $this->request->getMethod() == 'POST') { $valid = false; if (!($cancelled = $this->isFormCancelled($form))) { //set added/updated fields $modifiedFields = $session->get('mautic.form.' . $objectId . '.fields.modified', array()); $deletedFields = $session->get('mautic.form.' . $objectId . '.fields.deleted', array()); $fields = array_diff_key($modifiedFields, array_flip($deletedFields)); //set added/updated actions $modifiedActions = $session->get('mautic.form.' . $objectId . '.actions.modified', array()); $deletedActions = $session->get('mautic.form.' . $objectId . '.actions.deleted', array()); $actions = array_diff_key($modifiedActions, array_flip($deletedActions)); if ($valid = $this->isFormValid($form)) { //make sure that at least one field is selected if (empty($fields)) { //set the error $form->addError(new FormError($this->get('translator')->trans('mautic.form.form.fields.notempty', array(), 'validators'))); $valid = false; } else { $model->setFields($entity, $fields); $model->deleteFields($entity, $deletedFields); if ($entity->isStandalone()) { if (!($alias = $entity->getAlias())) { $alias = $model->cleanAlias($entity->getName(), '', 10); $entity->setAlias($alias); } if (!$entity->getId()) { // Set timestamps because this is a new clone $model->setTimestamps($entity, true, false); } // save the form first so that new fields are available to actions // use the repository method to not trigger listeners twice $model->getRepository()->saveEntity($entity); if (count($actions)) { // Now set and persist the actions $model->setActions($entity, $actions); } // Delete deleted actions if (count($deletedActions)) { $this->factory->getModel('form.action')->deleteEntities($deletedActions); } } else { // Clear the actions $entity->clearActions(); // Delete all actions if (count($modifiedActions)) { $this->factory->getModel('form.action')->deleteEntities(array_keys($modifiedActions)); } } // Persist and execute listeners $model->saveEntity($entity, $form->get('buttons')->get('save')->isClicked()); // Reset objectId to entity ID (can be session ID in case of cloned entity) $objectId = $entity->getId(); $this->addFlash('mautic.core.notice.updated', array('%name%' => $entity->getName(), '%menu_link%' => 'mautic_form_index', '%url%' => $this->generateUrl('mautic_form_action', array('objectAction' => 'edit', 'objectId' => $entity->getId())))); if ($form->get('buttons')->get('save')->isClicked()) { $viewParameters = array('objectAction' => 'view', 'objectId' => $entity->getId()); $returnUrl = $this->generateUrl('mautic_form_action', $viewParameters); $template = 'MauticFormBundle:Form:view'; } } } } else { //unlock the entity $model->unlockEntity($entity); $viewParameters = array('page' => $page); $returnUrl = $this->generateUrl('mautic_form_index', $viewParameters); $template = 'MauticFormBundle:Form:index'; } if ($cancelled || $valid && $form->get('buttons')->get('save')->isClicked()) { //remove fields from session $this->clearSessionComponents($objectId); // Clear session items in case columns changed $session->remove('mautic.formresult.' . $entity->getId() . '.orderby'); $session->remove('mautic.formresult.' . $entity->getId() . '.orderbydir'); $session->remove('mautic.formresult.' . $entity->getId() . '.filters'); return $this->postActionRedirect(array_merge($postActionVars, array('returnUrl' => $returnUrl, 'viewParameters' => $viewParameters, 'contentTemplate' => $template))); } elseif ($form->get('buttons')->get('apply')->isClicked()) { //rebuild everything to include new ids $cleanSlate = true; $reorder = true; } } else { $cleanSlate = true; //lock the entity $model->lockEntity($entity); $form->get('sessionId')->setData($objectId); } // Get field and action settings $customComponents = $model->getCustomComponents(); $fieldHelper = new FormFieldHelper($this->get('translator')); $availableFields = $fieldHelper->getList($customComponents['fields']); if ($cleanSlate) { //clean slate $this->clearSessionComponents($objectId); //load existing fields into session $modifiedFields = array(); $usedLeadFields = array(); $existingFields = $entity->getFields()->toArray(); foreach ($existingFields as $formField) { // Check to see if the field still exists if ($formField->getType() !== 'button' && !isset($availableFields[$formField->getType()])) { continue; } $id = $formField->getId(); $field = $formField->convertToArray(); if (!$id) { // Cloned entity $id = $field['id'] = $field['sessionId'] = 'new' . hash('sha1', uniqid(mt_rand())); } unset($field['form']); if (isset($customComponents['fields'][$field['type']])) { // Set the custom parameters $field['customParameters'] = $customComponents['fields'][$field['type']]; } $modifiedFields[$id] = $field; if (!empty($field['leadField'])) { $usedLeadFields[$id] = $field['leadField']; } } $session->set('mautic.form.' . $objectId . '.fields.leadfields', $usedLeadFields); if (!empty($reorder)) { uasort($modifiedFields, function ($a, $b) { return $a['order'] > $b['order']; }); } $session->set('mautic.form.' . $objectId . '.fields.modified', $modifiedFields); $deletedFields = array(); // Load existing actions into session $modifiedActions = array(); $existingActions = $entity->getActions()->toArray(); foreach ($existingActions as $formAction) { // Check to see if the action still exists if (!isset($customComponents['actions'][$formAction->getType()])) { continue; } $id = $formAction->getId(); $action = $formAction->convertToArray(); if (!$id) { // Cloned entity so use a random Id instead $action['id'] = $id = 'new' . hash('sha1', uniqid(mt_rand())); } unset($action['form']); $modifiedActions[$id] = $action; } if (!empty($reorder)) { uasort($modifiedActions, function ($a, $b) { return $a['order'] > $b['order']; }); } $session->set('mautic.form.' . $objectId . '.actions.modified', $modifiedActions); $deletedActions = array(); } return $this->delegateView(array('viewParameters' => array('fields' => $availableFields, 'actions' => $customComponents['choices'], 'formFields' => $modifiedFields, 'formActions' => $modifiedActions, 'deletedFields' => $deletedFields, 'deletedActions' => $deletedActions, 'tmpl' => $this->request->isXmlHttpRequest() ? $this->request->get('tmpl', 'index') : 'index', 'activeForm' => $entity, 'form' => $form->createView(), 'forceTypeSelection' => $forceTypeSelection), 'contentTemplate' => 'MauticFormBundle:Builder:index.html.php', 'passthroughVars' => array('activeLink' => '#mautic_form_index', 'mauticContent' => 'form', 'route' => $this->generateUrl('mautic_form_action', array('objectAction' => 'edit', 'objectId' => $entity->getId()))))); }
/** * @param $post * @param $server * @param Form $form * * @return boolean|string false if no error was encountered; otherwise the error message */ public function saveSubmission($post, $server, Form $form) { $fieldHelper = new FormFieldHelper($this->translator); //everything matches up so let's save the results $submission = new Submission(); $submission->setDateSubmitted(new \DateTime()); $submission->setForm($form); $ipAddress = $this->factory->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, array('mauticError', 'mauticMessage')); $submission->setReferer($referer); $fields = $form->getFields(); $fieldArray = array(); $results = array(); $tokens = array(); $leadFieldMatches = array(); $validationErrors = array(); foreach ($fields as $f) { $id = $f->getId(); $type = $f->getType(); $alias = $f->getAlias(); $value = isset($post[$alias]) ? $post[$alias] : ''; $fieldArray[$id] = array('id' => $id, 'type' => $type, 'alias' => $alias); if (in_array($type, array('button', 'freetext'))) { //don't save items that don't have a value associated with it continue; } elseif ($type == 'captcha') { $captcha = $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)) { //somehow the user got passed the JS validation $msg = $f->getValidationMessage(); if (empty($msg)) { $msg = $this->translator->trans('mautic.form.field.generic.validationfailed', array('%label%' => $f->getLabel()), 'validators'); } $validationErrors[$alias] = $msg; continue; } //clean and validate the input if ($f->isCustom()) { $params = $f->getCustomParameters(); if (!empty($value)) { if (isset($params['valueFilter'])) { if (is_string($params['inputFilter'] && method_exists('\\Mautic\\CoreBundle\\Helper\\InputHelper', $params['valueFilter']))) { $value = InputHelper::_($value, $params['valueFilter']); } elseif (is_callable($params['valueFilter'])) { $value = call_user_func_array($params['valueFilter'], array($f, $value)); } else { $value = InputHelper::_($value, 'clean'); } } else { $value = InputHelper::_($value, 'clean'); } } if (isset($params['valueConstraints']) && is_callable($params['valueConstraints'])) { $customErrors = call_user_func_array($params['valueConstraints'], array($f, $value)); if (!empty($customErrors)) { $validationErrors[$alias] = is_array($customErrors) ? implode('<br />', $customErrors) : $customErrors; } } } elseif (!empty($value)) { $filter = $fieldHelper->getFieldFilter($type); $value = InputHelper::_($value, $filter); $validation = $fieldHelper->validateFieldValue($type, $value); if (!empty($validation)) { $validationErrors[$alias] = is_array($validation) ? implode('<br />', $validation) : $validation; } } //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; } $leadField = $f->getLeadField(); if (!empty($leadField)) { $leadFieldMatches[$leadField] = $value; } } $submission->setResults($results); //execute submit actions $actions = $form->getActions(); //get post submit actions to make sure it still exists $components = $this->factory->getModel('form')->getCustomComponents(); $availableActions = $components['actions']; $args = array('post' => $post, 'server' => $server, 'factory' => $this->factory, 'submission' => $submission, 'fields' => $fieldArray, 'form' => $form, 'tokens' => $tokens); foreach ($actions as $action) { $key = $action->getType(); if (!isset($availableActions[$key])) { continue; } $settings = $availableActions[$key]; $args['action'] = $action; $args['config'] = $action->getProperties(); if (array_key_exists('validator', $settings)) { $callback = $settings['validator']; if (is_callable($callback)) { if (is_array($callback)) { $reflection = new \ReflectionMethod($callback[0], $callback[1]); } elseif (strpos($callback, '::') !== false) { $parts = explode('::', $callback); $reflection = new \ReflectionMethod($parts[0], $parts[1]); } else { $reflection = new \ReflectionMethod(null, $callback); } $pass = array(); foreach ($reflection->getParameters() as $param) { if (isset($args[$param->getName()])) { $pass[] = $args[$param->getName()]; } else { $pass[] = null; } } list($validated, $validatedMessage) = $reflection->invokeArgs($this, $pass); if (!$validated) { $validationErrors[$alias] = $validatedMessage; } } } } //return errors if (!empty($validationErrors)) { return array('errors' => $validationErrors); } //set the landing page the form was submitted from if applicable if (!empty($post['mauticpage'])) { $page = $this->factory->getModel('page.page')->getEntity((int) $post['mauticpage']); if ($page != null) { $submission->setPage($page); } } // Add a feedback parameter $args['feedback'] = array(); /** @var \Mautic\LeadBundle\Model\LeadModel $leadModel */ $leadModel = $this->factory->getModel('lead'); // Create/update lead if (!empty($leadFieldMatches)) { $this->createLeadFromSubmit($form, $leadFieldMatches); } if ($form->isStandalone()) { // Now handle post submission actions foreach ($actions as $action) { $key = $action->getType(); if (!isset($availableActions[$key])) { continue; } $settings = $availableActions[$key]; $args['action'] = $action; $args['config'] = $action->getProperties(); // Set the lead each time in case an action updates it $args['lead'] = $leadModel->getCurrentLead(); $callback = $settings['callback']; if (is_callable($callback)) { if (is_array($callback)) { $reflection = new \ReflectionMethod($callback[0], $callback[1]); } elseif (strpos($callback, '::') !== false) { $parts = explode('::', $callback); $reflection = new \ReflectionMethod($parts[0], $parts[1]); } else { $reflection = new \ReflectionMethod(null, $callback); } $pass = array(); foreach ($reflection->getParameters() as $param) { if (isset($args[$param->getName()])) { $pass[] = $args[$param->getName()]; } else { $pass[] = null; } } $returned = $reflection->invokeArgs($this, $pass); $args['feedback'][$key] = $returned; } } } // Get updated lead with tracking ID if ($form->isInKioskMode()) { $lead = $leadModel->getCurrentLead(); } else { list($lead, $trackingId, $generated) = $leadModel->getCurrentLead(true); //set tracking ID for stats purposes to determine unique hits $submission->setTrackingId($trackingId); } $submission->setLead($lead); if (!$form->isStandalone()) { // Find and add the lead to the associated campaigns /** @var \Mautic\CampaignBundle\Model\CampaignModel $campaignModel */ $campaignModel = $this->factory->getModel('campaign'); $campaigns = $campaignModel->getCampaignsByForm($form); if (!empty($campaigns)) { foreach ($campaigns as $campaign) { $campaignModel->addLead($campaign, $lead); } } } //save entity after the form submission events are fired in case a new lead is created $this->saveEntity($submission); if ($this->dispatcher->hasListeners(FormEvents::FORM_ON_SUBMIT)) { $event = new SubmissionEvent($submission, $post, $server); $this->dispatcher->dispatch(FormEvents::FORM_ON_SUBMIT, $event); } //last round of callback commands from the submit actions; first come first serve foreach ($args['feedback'] as $k => $data) { if (!empty($data['callback'])) { return array('callback' => $data); } } //made it to the end so return false that there was not an error return false; }
$ignoreNumericalKeys = true; break; case 'country' == $leadFieldType: $list = \Mautic\LeadBundle\Helper\FormFieldHelper::getCountryChoices(); break; case 'region' == $leadFieldType: $list = \Mautic\LeadBundle\Helper\FormFieldHelper::getRegionChoices(); break; case 'timezone' == $leadFieldType: $list = \Mautic\LeadBundle\Helper\FormFieldHelper::getTimezonesChoices(); break; case 'locale': $list = \Mautic\LeadBundle\Helper\FormFieldHelper::getLocaleChoices(); break; } } if (empty($parseList)) { if (isset($list)) { $parseList = $list; } elseif (!empty($properties['list'])) { $parseList = $properties['list']; } elseif (!empty($properties['optionlist'])) { $parseList = $properties['optionlist']; } if (isset($parseList['list'])) { $parseList = $parseList['list']; } } $list = \Mautic\FormBundle\Helper\FormFieldHelper::parseList($parseList, false, $ignoreNumericalKeys); $firstListValue = reset($list); }