Spaces:
No application file
No application file
namespace Mautic\FormBundle\Controller; | |
use Doctrine\Persistence\ManagerRegistry; | |
use Mautic\CoreBundle\Controller\FormController as CommonFormController; | |
use Mautic\CoreBundle\Factory\MauticFactory; | |
use Mautic\CoreBundle\Factory\ModelFactory; | |
use Mautic\CoreBundle\Helper\CoreParametersHelper; | |
use Mautic\CoreBundle\Helper\UserHelper; | |
use Mautic\CoreBundle\Security\Permissions\CorePermissions; | |
use Mautic\CoreBundle\Service\FlashBag; | |
use Mautic\CoreBundle\Translation\Translator; | |
use Mautic\FormBundle\Collector\AlreadyMappedFieldCollectorInterface; | |
use Mautic\FormBundle\Collector\MappedObjectCollectorInterface; | |
use Mautic\FormBundle\Entity\Field; | |
use Mautic\FormBundle\Event\FormBuilderEvent; | |
use Mautic\FormBundle\FormEvents; | |
use Mautic\FormBundle\Helper\FormFieldHelper; | |
use Mautic\FormBundle\Model\FieldModel; | |
use Mautic\FormBundle\Model\FormModel; | |
use Symfony\Component\EventDispatcher\EventDispatcherInterface; | |
use Symfony\Component\Form\FormFactoryInterface; | |
use Symfony\Component\HttpFoundation\JsonResponse; | |
use Symfony\Component\HttpFoundation\Request; | |
use Symfony\Component\HttpFoundation\RequestStack; | |
use Symfony\Component\HttpFoundation\Response; | |
class FieldController extends CommonFormController | |
{ | |
public function __construct( | |
private FormModel $formModel, | |
private FieldModel $formFieldModel, | |
FormFieldHelper $fieldHelper, | |
FormFactoryInterface $formFactory, | |
private MappedObjectCollectorInterface $mappedObjectCollector, | |
private AlreadyMappedFieldCollectorInterface $alreadyMappedFieldCollector, | |
ManagerRegistry $doctrine, | |
MauticFactory $factory, | |
ModelFactory $modelFactory, | |
UserHelper $userHelper, | |
CoreParametersHelper $coreParametersHelper, | |
EventDispatcherInterface $dispatcher, | |
Translator $translator, | |
FlashBag $flashBag, | |
RequestStack $requestStack, | |
CorePermissions $security | |
) { | |
$this->fieldHelper = $fieldHelper; | |
$this->formFactory = $formFactory; | |
parent::__construct($formFactory, $fieldHelper, $doctrine, $factory, $modelFactory, $userHelper, $coreParametersHelper, $dispatcher, $translator, $flashBag, $requestStack, $security); | |
} | |
/** | |
* Generates new form and processes post data. | |
* | |
* @return Response | |
*/ | |
public function newAction(Request $request) | |
{ | |
$success = 0; | |
$valid = $cancelled = false; | |
$method = $request->getMethod(); | |
$session = $request->getSession(); | |
if ('POST' == $method) { | |
$formField = $request->request->all()['formfield'] ?? []; | |
$fieldType = $formField['type']; | |
$formId = $formField['formId']; | |
} else { | |
$fieldType = $request->query->get('type'); | |
$formId = $request->query->get('formId'); | |
$formField = [ | |
'type' => $fieldType, | |
'formId' => $formId, | |
'parent' => $request->query->get('parent'), | |
]; | |
} | |
$customComponents = $this->formModel->getCustomComponents(); | |
$customParams = $customComponents['fields'][$fieldType] ?? false; | |
// ajax only for form fields | |
if (!$fieldType | |
|| !$request->isXmlHttpRequest() | |
|| !$this->security->isGranted(['form:forms:editown', 'form:forms:editother', 'form:forms:create'], 'MATCH_ONE') | |
) { | |
return $this->modalAccessDenied(); | |
} | |
// Generate the form | |
$form = $this->getFieldForm($formId, $formField); | |
if (!empty($customParams)) { | |
$formField['isCustom'] = true; | |
$formField['customParameters'] = $customParams; | |
} | |
// Check for a submitted form and process it | |
if ('POST' == $method) { | |
if (!$cancelled = $this->isFormCancelled($form)) { | |
if ($valid = $this->isFormValid($form)) { | |
$success = 1; | |
// form is valid so process the data | |
$keyId = 'new'.hash('sha1', uniqid(mt_rand())); | |
// save the properties to session | |
$fields = $session->get('mautic.form.'.$formId.'.fields.modified', []); | |
$formData = $form->getData(); | |
$formField = array_merge($formField, $formData); | |
$formField['id'] = $keyId; | |
// Get aliases in order to generate a new one for the new field | |
$aliases = []; | |
foreach ($fields as $f) { | |
$aliases[] = $f['alias']; | |
} | |
// Generate or ensure a unique alias | |
$alias = empty($formField['alias']) ? $formField['label'] : $formField['alias']; | |
$formFieldModel = $this->getModel('form.field'); | |
\assert($formFieldModel instanceof FieldModel); | |
$formField['alias'] = $formFieldModel->generateAlias($alias, $aliases); | |
// Force required for captcha if not a honeypot | |
if ('captcha' == $formField['type']) { | |
$formField['isRequired'] = !empty($formField['properties']['captcha']); | |
} | |
// Add it to the next to last assuming the last is the submit button | |
if (count($fields)) { | |
$lastField = end($fields); | |
$lastKey = key($fields); | |
array_pop($fields); | |
$fields[$keyId] = $formField; | |
$fields[$lastKey] = $lastField; | |
} else { | |
$fields[$keyId] = $formField; | |
} | |
$session->set('mautic.form.'.$formId.'.fields.modified', $fields); | |
// Keep track of used lead fields | |
if (!empty($formField['mappedObject']) && !empty($formField['mappedField']) && empty($formData['parent'])) { | |
$this->alreadyMappedFieldCollector->addField($formId, $formField['mappedObject'], $formField['mappedField']); | |
} | |
} else { | |
$success = 0; | |
} | |
} | |
} | |
$viewParams = ['type' => $fieldType]; | |
if ($cancelled || $valid) { | |
$closeModal = true; | |
} else { | |
$closeModal = false; | |
$viewParams['tmpl'] = 'field'; | |
$viewParams['form'] = (isset($customParams['formTheme'])) ? $this->setFormTheme($form, '@MauticForm/Builder/field.html.twig', $customParams['formTheme']) : $form->createView(); | |
$viewParams['fieldHeader'] = (!empty($customParams)) ? $this->translator->trans($customParams['label']) : $this->translator->transConditional('mautic.core.type.'.$fieldType, 'mautic.form.field.type.'.$fieldType); | |
} | |
$passthroughVars = [ | |
'mauticContent' => 'formField', | |
'success' => $success, | |
'route' => false, | |
]; | |
if (!empty($keyId)) { | |
$entity = new Field(); | |
$blank = $entity->convertToArray(); | |
$formField = array_merge($blank, $formField); | |
$formEntity = $this->formModel->getEntity($formId); | |
$passthroughVars['parent'] = $formField['parent']; | |
$passthroughVars['fieldId'] = $keyId; | |
$template = (!empty($customParams)) ? $customParams['template'] : '@MauticForm/Field/'.$fieldType.'.html.twig'; | |
$leadFieldModel = $this->getModel('lead.field'); | |
\assert($leadFieldModel instanceof \Mautic\LeadBundle\Model\FieldModel); | |
$passthroughVars['fieldHtml'] = $this->renderView( | |
'@MauticForm/Builder/_field_wrapper.html.twig', | |
[ | |
'isConditional' => !empty($formField['parent']), | |
'template' => $template, | |
'inForm' => true, | |
'field' => $formField, | |
'id' => $keyId, | |
'formId' => $formId, | |
'formName' => null === $formEntity ? 'newform' : $formEntity->generateFormName(), | |
'mappedFields' => $this->mappedObjectCollector->buildCollection((string) $formField['mappedObject']), | |
'inBuilder' => true, | |
'fields' => $this->fieldHelper->getChoiceList($customComponents['fields']), | |
'viewOnlyFields' => $customComponents['viewOnlyFields'], | |
'formFields' => $fields, | |
] | |
); | |
} | |
if ($closeModal) { | |
// just close the modal | |
$passthroughVars['closeModal'] = 1; | |
return new JsonResponse($passthroughVars); | |
} | |
return $this->ajaxAction($request, [ | |
'contentTemplate' => '@MauticForm/Builder/'.$viewParams['tmpl'].'.html.twig', | |
'viewParameters' => $viewParams, | |
'passthroughVars' => $passthroughVars, | |
]); | |
} | |
/** | |
* Generates edit form and processes post data. | |
* | |
* @param int $objectId | |
* | |
* @return Response | |
*/ | |
public function editAction(Request $request, $objectId) | |
{ | |
$session = $request->getSession(); | |
$method = $request->getMethod(); | |
$formfield = $request->request->get('formfield') ?? []; | |
$formId = 'POST' === $method ? ($formfield['formId'] ?? '') : $request->query->get('formId'); | |
$fields = $session->get('mautic.form.'.$formId.'.fields.modified', []); | |
$success = 0; | |
$valid = $cancelled = false; | |
$formField = array_key_exists($objectId, $fields) ? $fields[$objectId] : []; | |
if ($formField) { | |
$fieldType = $formField['type']; | |
// ajax only for form fields | |
if (!$fieldType | |
|| !$request->isXmlHttpRequest() | |
|| !$this->security->isGranted(['form:forms:editown', 'form:forms:editother', 'form:forms:create'], 'MATCH_ONE') | |
) { | |
return $this->modalAccessDenied(); | |
} | |
// Generate the form | |
$form = $this->getFieldForm($formId, $formField); | |
// Check for a submitted form and process it | |
if ('POST' == $method) { | |
if (!$cancelled = $this->isFormCancelled($form)) { | |
if ($valid = $this->isFormValid($form)) { | |
$success = 1; | |
// form is valid so process the data | |
// save the properties to session | |
$session = $request->getSession(); | |
$fields = $session->get('mautic.form.'.$formId.'.fields.modified'); | |
$formData = $form->getData(); | |
// overwrite with updated data | |
$formField = array_merge($fields[$objectId], $formData); | |
if (str_contains((string) $objectId, 'new')) { | |
// Get aliases in order to generate update for this one | |
$aliases = []; | |
foreach ($fields as $k => $f) { | |
if ($k != $objectId) { | |
$aliases[] = $f['alias']; | |
} | |
} | |
$formField['alias'] = $this->formFieldModel->generateAlias( | |
$formField['alias'] ?? $formField['label'] ?? '', | |
$aliases | |
); | |
} | |
// Force required for captcha if not a honeypot | |
if ('captcha' == $formField['type']) { | |
$formField['isRequired'] = !empty($formField['properties']['captcha']); | |
} | |
$fields[$objectId] = $formField; | |
$session->set('mautic.form.'.$formId.'.fields.modified', $fields); | |
// Keep track of used lead fields | |
if (!empty($formField['mappedObject']) && !empty($formField['mappedField']) && empty($formData['parent'])) { | |
$this->alreadyMappedFieldCollector->addField($formId, $formField['mappedObject'], $formField['mappedField']); | |
} | |
} | |
} | |
} | |
$viewParams = ['type' => $fieldType]; | |
$customComponents = $this->formModel->getCustomComponents(); | |
$customParams = $customComponents['fields'][$fieldType] ?? false; | |
if ($cancelled || $valid) { | |
$closeModal = true; | |
} else { | |
$closeModal = false; | |
$viewParams['tmpl'] = 'field'; | |
$viewParams['form'] = (isset($customParams['formTheme'])) ? $this->setFormTheme( | |
$form, | |
'@MauticForm/Builder/field.html.twig', | |
$customParams['formTheme'] | |
) : $form->createView(); | |
$viewParams['fieldHeader'] = (!empty($customParams)) | |
? $this->translator->trans($customParams['label']) | |
: $this->translator->transConditional('mautic.core.type.'.$fieldType, 'mautic.form.field.type.'.$fieldType); | |
} | |
$passthroughVars = [ | |
'mauticContent' => 'formField', | |
'success' => $success, | |
'route' => false, | |
]; | |
$passthroughVars['fieldId'] = $objectId; | |
$template = (!empty($customParams)) ? $customParams['template'] : '@MauticForm/Field/'.$fieldType.'.html.twig'; | |
// prevent undefined errors | |
$entity = new Field(); | |
$blank = $entity->convertToArray(); | |
$formField = array_merge($blank, $formField); | |
$leadFieldModel = $this->getModel('lead.field'); | |
\assert($leadFieldModel instanceof \Mautic\LeadBundle\Model\FieldModel); | |
$passthroughVars['fieldHtml'] = $this->renderView( | |
'@MauticForm/Builder/_field_wrapper.html.twig', | |
[ | |
'isConditional' => !empty($formField['parent']), | |
'template' => $template, | |
'inForm' => true, | |
'field' => $formField, | |
'id' => $objectId, | |
'formId' => $formId, | |
'mappedFields' => $this->mappedObjectCollector->buildCollection((string) $formField['mappedObject']), | |
'inBuilder' => true, | |
'fields' => $this->fieldHelper->getChoiceList($customComponents['fields']), | |
'formFields' => $fields, | |
'viewOnlyFields' => $customComponents['viewOnlyFields'], | |
] | |
); | |
if ($closeModal) { | |
// just close the modal | |
$passthroughVars['closeModal'] = 1; | |
return new JsonResponse($passthroughVars); | |
} | |
return $this->ajaxAction( | |
$request, | |
[ | |
'contentTemplate' => '@MauticForm/Builder/'.$viewParams['tmpl'].'.html.twig', | |
'viewParameters' => $viewParams, | |
'passthroughVars' => $passthroughVars, | |
] | |
); | |
} | |
return new JsonResponse(['success' => 0]); | |
} | |
/** | |
* Deletes the entity. | |
* | |
* @param int $objectId | |
* | |
* @return JsonResponse | |
*/ | |
public function deleteAction(Request $request, $objectId) | |
{ | |
$session = $request->getSession(); | |
$formId = $request->query->get('formId'); | |
$fields = $session->get('mautic.form.'.$formId.'.fields.modified', []); | |
$delete = $session->get('mautic.form.'.$formId.'.fields.deleted', []); | |
// ajax only for form fields | |
if (!$request->isXmlHttpRequest() | |
|| !$this->security->isGranted(['form:forms:editown', 'form:forms:editother', 'form:forms:create'], 'MATCH_ONE') | |
) { | |
return $this->accessDenied(); | |
} | |
$formField = (array_key_exists($objectId, $fields)) ? $fields[$objectId] : null; | |
if ('POST' === $request->getMethod() && null !== $formField) { | |
if ($formField['mappedObject'] && $formField['mappedField']) { | |
// Allow to select the lead field from the delete field again | |
$this->alreadyMappedFieldCollector->removeField($formId, $formField['mappedObject'], $formField['mappedField']); | |
} | |
// add the field to the delete list | |
if (!in_array($objectId, $delete)) { | |
$delete[] = $objectId; | |
$session->set('mautic.form.'.$formId.'.fields.deleted', $delete); | |
} | |
$dataArray = [ | |
'mauticContent' => 'formField', | |
'success' => 1, | |
'route' => false, | |
]; | |
} else { | |
$dataArray = ['success' => 0]; | |
} | |
return new JsonResponse($dataArray); | |
} | |
/** | |
* @param int $formId | |
* @param mixed[] $formField | |
* | |
* @return mixed | |
*/ | |
private function getFieldForm($formId, array $formField) | |
{ | |
// fire the form builder event | |
$formModel = $this->getModel('form.form'); | |
\assert($formModel instanceof FormModel); | |
$customComponents = $this->formModel->getCustomComponents(); | |
$customParams = $customComponents['fields'][$formField['type']] ?? false; | |
$formFieldModel = $this->getModel('form.field'); | |
\assert($formFieldModel instanceof FieldModel); | |
$form = $formFieldModel->createForm( | |
$formField, | |
$this->formFactory, | |
(!empty($formField['id'])) ? | |
$this->generateUrl('mautic_formfield_action', ['objectAction' => 'edit', 'objectId' => $formField['id']]) | |
: $this->generateUrl('mautic_formfield_action', ['objectAction' => 'new']), | |
['customParameters' => $customParams] | |
); | |
$form->get('formId')->setData($formId); | |
$event = new FormBuilderEvent($this->translator); | |
$this->dispatcher->dispatch($event, FormEvents::FORM_ON_BUILD); | |
$event->addValidatorsToBuilder($form); | |
return $form; | |
} | |
} | |