Initial commit: CloudOps infrastructure platform

This commit is contained in:
root
2026-04-09 19:58:57 +02:00
commit 1166a52f26
7762 changed files with 839452 additions and 0 deletions

View File

@@ -0,0 +1,323 @@
<?php
namespace Mautic\FormBundle\Controller;
use Mautic\CoreBundle\Controller\FormController as CommonFormController;
use Mautic\FormBundle\Entity\Action;
use Mautic\FormBundle\Form\Type\ActionType;
use Mautic\FormBundle\Model\FormModel;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class ActionController extends CommonFormController
{
/**
* 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) {
$formAction = $request->request->all()['formaction'] ?? [];
$actionType = $formAction['type'];
$formId = $formAction['formId'];
} else {
$actionType = $request->query->get('type');
$formId = $request->query->get('formId');
$formAction = [
'type' => $actionType,
'formId' => $formId,
];
}
// ajax only for form fields
if (!$actionType
|| !$this->security->isGranted(['form:forms:editown', 'form:forms:editother', 'form:forms:create'], 'MATCH_ONE')
) {
return $this->modalAccessDenied();
}
// fire the form builder event
$formModel = $this->getModel('form.form');
\assert($formModel instanceof FormModel);
$customComponents = $formModel->getCustomComponents();
$form = $this->formFactory->create(ActionType::class, $formAction, [
'action' => $this->generateUrl('mautic_formaction_action', ['objectAction' => 'new']),
'settings' => $customComponents['actions'][$actionType],
'formId' => $formId,
]);
$form->get('formId')->setData($formId);
$formAction['settings'] = $customComponents['actions'][$actionType];
// 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
$actions = $session->get('mautic.form.'.$formId.'.actions.modified', []);
$formData = $form->getData();
$formAction = array_merge($formAction, $formData);
$formAction['id'] = $keyId;
if (empty($formAction['name'])) {
// set it to the event default
$formAction['name'] = $this->translator->trans($formAction['settings']['label']);
}
$actions[$keyId] = $formAction;
$session->set('mautic.form.'.$formId.'.actions.modified', $actions);
} else {
$success = 0;
}
}
}
$viewParams = ['type' => $actionType];
if ($cancelled || $valid) {
$closeModal = true;
} else {
$closeModal = false;
$viewParams['tmpl'] = 'action';
$viewParams['form'] = $form->createView();
$header = $formAction['settings']['label'];
$viewParams['actionHeader'] = $this->translator->trans($header);
if (isset($formAction['settings']['formTheme'])) {
$viewParams['formTheme'] = $formAction['settings']['formTheme'];
}
}
$passthroughVars = [
'mauticContent' => 'formAction',
'success' => $success,
'route' => false,
];
if (!empty($keyId)) {
// prevent undefined errors
$entity = new Action();
$blank = $entity->convertToArray();
$formAction = array_merge($blank, $formAction);
$template = (!empty($formAction['settings']['template'])) ? $formAction['settings']['template'] :
'@MauticForm/Action/base_form_action.html.twig';
$passthroughVars['actionId'] = $keyId;
$passthroughVars['actionHtml'] = $this->renderView($template, [
'inForm' => true,
'action' => $formAction,
'id' => $keyId,
'formId' => $formId,
]);
}
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();
$formaction = $request->request->all()['formaction'] ?? [];
$formId = 'POST' === $method ? ($formaction['formId'] ?? '') : $request->query->get('formId');
$actions = $session->get('mautic.form.'.$formId.'.actions.modified', []);
$success = 0;
$valid = $cancelled = false;
$formAction = array_key_exists($objectId, $actions) ? $actions[$objectId] : null;
if (null !== $formAction) {
$formModel = $this->getModel('form.form');
\assert($formModel instanceof FormModel);
$actionType = $formAction['type'];
$customComponents = $formModel->getCustomComponents();
$formAction['settings'] = $customComponents['actions'][$actionType];
// ajax only for form fields
if (!$actionType
|| !$request->isXmlHttpRequest()
|| !$this->security->isGranted(['form:forms:editown', 'form:forms:editother', 'form:forms:create'], 'MATCH_ONE')
) {
return $this->modalAccessDenied();
}
$form = $this->formFactory->create(ActionType::class, $formAction, [
'action' => $this->generateUrl('mautic_formaction_action', ['objectAction' => 'edit', 'objectId' => $objectId]),
'settings' => $formAction['settings'],
'formId' => $formId,
]);
$form->get('formId')->setData($formId);
// 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();
$actions = $session->get('mautic.form.'.$formId.'.actions.modified');
$formData = $form->getData();
// overwrite with updated data
$formAction = array_merge($actions[$objectId], $formData);
if (empty($formAction['name'])) {
// set it to the event default
$formAction['name'] = $this->translator->trans($formAction['settings']['label']);
}
$actions[$objectId] = $formAction;
$session->set('mautic.form.'.$formId.'.actions.modified', $actions);
// generate HTML for the field
$keyId = $objectId;
// take note if this is a submit button or not
if ('button' == $actionType) {
$submits = $session->get('mautic.formactions.submits', []);
if ('submit' == $formAction['properties']['type'] && !in_array($keyId, $submits)) {
// button type updated to submit
$submits[] = $keyId;
$session->set('mautic.formactions.submits', $submits);
} elseif ('submit' != $formAction['properties']['type'] && in_array($keyId, $submits)) {
// button type updated to something other than submit
$key = array_search($keyId, $submits);
unset($submits[$key]);
$session->set('mautic.formactions.submits', $submits);
}
}
}
}
}
$viewParams = ['type' => $actionType];
if ($cancelled || $valid) {
$closeModal = true;
} else {
$closeModal = false;
$viewParams['tmpl'] = 'action';
$viewParams['form'] = $form->createView();
$viewParams['actionHeader'] = $this->translator->trans($formAction['settings']['label']);
if (isset($formAction['settings']['formTheme'])) {
$viewParams['formTheme'] = $formAction['settings']['formTheme'];
}
}
$passthroughVars = [
'mauticContent' => 'formAction',
'success' => $success,
'route' => false,
];
if (!empty($keyId)) {
$passthroughVars['actionId'] = $keyId;
// prevent undefined errors
$entity = new Action();
$blank = $entity->convertToArray();
$formAction = array_merge($blank, $formAction);
$template = (!empty($formAction['settings']['template'])) ? $formAction['settings']['template'] :
'@MauticForm/Action/base_form_action.html.twig';
$passthroughVars['actionHtml'] = $this->renderView($template, [
'inForm' => true,
'action' => $formAction,
'id' => $keyId,
'formId' => $formId,
]);
}
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.
*
* @return JsonResponse
*/
public function deleteAction(Request $request, $objectId)
{
$session = $request->getSession();
$formId = $request->query->get('formId');
$actions = $session->get('mautic.form.'.$formId.'.actions.modified', []);
$delete = $session->get('mautic.form.'.$formId.'.actions.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();
}
$formAction = (array_key_exists($objectId, $actions)) ? $actions[$objectId] : null;
if ('POST' == $request->getMethod() && null !== $formAction) {
// add the field to the delete list
if (!in_array($objectId, $delete)) {
$delete[] = $objectId;
$session->set('mautic.form.'.$formId.'.actions.deleted', $delete);
}
// take note if this is a submit button or not
if ('button' == $formAction['type']) {
$submits = $session->get('mautic.formactions.submits', []);
$properties = $formAction['properties'];
if ('submit' == $properties['type'] && in_array($objectId, $submits)) {
$key = array_search($objectId, $submits);
unset($submits[$key]);
$session->set('mautic.formactions.submits', $submits);
}
}
$dataArray = [
'mauticContent' => 'formAction',
'success' => 1,
'route' => false,
];
} else {
$dataArray = ['success' => 0];
}
return new JsonResponse($dataArray);
}
}

View File

@@ -0,0 +1,168 @@
<?php
namespace Mautic\FormBundle\Controller;
use Doctrine\Persistence\ManagerRegistry;
use Mautic\CoreBundle\Controller\AjaxController as CommonAjaxController;
use Mautic\CoreBundle\Factory\ModelFactory;
use Mautic\CoreBundle\Helper\CoreParametersHelper;
use Mautic\CoreBundle\Helper\InputHelper;
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\FieldCollectorInterface;
use Mautic\FormBundle\Crate\FieldCrate;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
class AjaxController extends CommonAjaxController
{
public function __construct(
private FieldCollectorInterface $fieldCollector,
private AlreadyMappedFieldCollectorInterface $mappedFieldCollector,
ManagerRegistry $doctrine,
ModelFactory $modelFactory,
UserHelper $userHelper,
CoreParametersHelper $coreParametersHelper,
EventDispatcherInterface $dispatcher,
Translator $translator,
FlashBag $flashBag,
RequestStack $requestStack,
CorePermissions $security,
) {
parent::__construct($doctrine, $modelFactory, $userHelper, $coreParametersHelper, $dispatcher, $translator, $flashBag, $requestStack, $security);
}
public function reorderFieldsAction(Request $request, string $name = 'fields'): JsonResponse
{
if ('form' === $name) {
$name = 'fields';
}
$dataArray = ['success' => 0];
$sessionId = InputHelper::clean($request->request->get('formId'));
$sessionName = 'mautic.form.'.$sessionId.'.'.$name.'.modified';
$session = $request->getSession();
$orderName = ('fields' == $name) ? 'mauticform' : 'mauticform_action';
$order = InputHelper::clean($request->request->all()[$orderName]);
$components = $session->get($sessionName);
if (!empty($order) && !empty($components)) {
$components = array_replace(array_flip($order), $components);
$session->set($sessionName, $components);
$dataArray['success'] = 1;
}
return $this->sendJsonResponse($dataArray);
}
public function getFieldsForObjectAction(Request $request): JsonResponse
{
$formId = $request->get('formId');
$mappedObject = $request->get('mappedObject');
$mappedField = $request->get('mappedField');
$mappedFields = $this->mappedFieldCollector->getFields($formId, $mappedObject);
$fields = $this->fieldCollector->getFields($mappedObject);
$fields = $fields->removeFieldsWithKeys($mappedFields, $mappedField);
return $this->sendJsonResponse(
[
'fields' => array_map(
fn (FieldCrate $field): array => [
'label' => $field->getName(),
'value' => $field->getKey(),
'isListType' => $field->isListType(),
],
$fields->getArrayCopy()
),
]
);
}
public function reorderActionsAction(Request $request): JsonResponse
{
return $this->reorderFieldsAction($request, 'actions');
}
public function updateFormFieldsAction(Request $request): JsonResponse
{
$formId = (int) $request->request->get('formId');
$dataArray = ['success' => 0];
$model = $this->getModel('form');
$entity = $model->getEntity($formId);
$formFields = empty($entity) ? [] : $entity->getFields();
$fields = [];
foreach ($formFields as $field) {
if ('button' != $field->getType()) {
$properties = $field->getProperties();
$options = [];
if (!empty($properties['list']['list'])) {
// If the field is a SELECT field then the data gets stored in [list][list]
$optionList = $properties['list']['list'];
} elseif (!empty($properties['optionlist']['list'])) {
// If the field is a radio or a checkbox then it will be stored in [optionlist][list]
$optionList = $properties['optionlist']['list'];
}
if (!empty($optionList)) {
foreach ($optionList as $listItem) {
if (is_array($listItem) && isset($listItem['value']) && isset($listItem['label'])) {
// The select box needs values to be [value] => label format so make sure we have that style then put it in
$options[$listItem['value']] = $listItem['label'];
} elseif (!is_array($listItem)) {
// Keeping for BC
$options[] = $listItem;
}
}
}
$fields[] = [
'id' => $field->getId(),
'label' => $field->getLabel(),
'alias' => $field->getAlias(),
'type' => $field->getType(),
'options' => $options,
];
// Be sure to not pollute the symbol table.
unset($optionList);
}
}
$dataArray['fields'] = $fields;
$dataArray['success'] = 1;
return $this->sendJsonResponse($dataArray);
}
/**
* Ajax submit for forms.
*/
public function submitAction(Request $request): JsonResponse
{
$response = $this->forwardWithPost('Mautic\FormBundle\Controller\PublicController::submitAction', $request->request->all(), [], ['ajax' => true]);
$responseData = json_decode($response->getContent(), true);
$success = (!in_array($response->getStatusCode(), [404, 500]) && empty($responseData['errorMessage'])
&& empty($responseData['validationErrors']));
$message = '';
$type = '';
if (isset($responseData['successMessage'])) {
$message = $responseData['successMessage'];
$type = 'notice';
} elseif (isset($responseData['errorMessage'])) {
$message = $responseData['errorMessage'];
$type = 'error';
}
$data = is_array($responseData)
? array_merge($responseData, ['message' => $message, 'type' => $type, 'success' => $success])
: [];
return $this->sendJsonResponse($data);
}
}

View File

@@ -0,0 +1,366 @@
<?php
namespace Mautic\FormBundle\Controller\Api;
use Doctrine\Persistence\ManagerRegistry;
use Mautic\ApiBundle\Controller\CommonApiController;
use Mautic\ApiBundle\Helper\EntityResultHelper;
use Mautic\CoreBundle\Entity\CommonEntity;
use Mautic\CoreBundle\Factory\ModelFactory;
use Mautic\CoreBundle\Helper\AppVersion;
use Mautic\CoreBundle\Helper\CoreParametersHelper;
use Mautic\CoreBundle\Security\Permissions\CorePermissions;
use Mautic\CoreBundle\Translation\Translator;
use Mautic\FormBundle\Entity\Action;
use Mautic\FormBundle\Entity\Field;
use Mautic\FormBundle\Entity\Form;
use Mautic\FormBundle\Model\ActionModel;
use Mautic\FormBundle\Model\FieldModel;
use Mautic\FormBundle\Model\FormModel;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\RouterInterface;
/**
* @extends CommonApiController<Form>
*/
class FormApiController extends CommonApiController
{
/**
* @var FormModel|null
*/
protected $model;
public function __construct(
CorePermissions $security,
Translator $translator,
EntityResultHelper $entityResultHelper,
RouterInterface $router,
FormFactoryInterface $formFactory,
AppVersion $appVersion,
RequestStack $requestStack,
ManagerRegistry $doctrine,
ModelFactory $modelFactory,
EventDispatcherInterface $dispatcher,
CoreParametersHelper $coreParametersHelper,
) {
$formModel = $modelFactory->getModel('form');
\assert($formModel instanceof FormModel);
$this->model = $formModel;
$this->entityClass = Form::class;
$this->entityNameOne = 'form';
$this->entityNameMulti = 'forms';
$this->serializerGroups = ['formDetails', 'categoryList', 'publishDetails'];
$this->dataInputMasks = [
'text' => 'html',
'message' => 'html',
];
parent::__construct($security, $translator, $entityResultHelper, $router, $formFactory, $appVersion, $requestStack, $doctrine, $modelFactory, $dispatcher, $coreParametersHelper);
}
/**
* Delete fields from a form.
*
* @return Response
*/
public function deleteFieldsAction(Request $request, $formId)
{
if (!$this->security->isGranted(['form:forms:editown', 'form:forms:editother'], 'MATCH_ONE')) {
return $this->accessDenied();
}
$entity = $this->model->getEntity($formId);
if (null === $entity) {
return $this->notFound();
}
$fieldsToDelete = $request->query->all()['fields'] ?? $request->request->all()['fields'] ?? [];
if (!is_array($fieldsToDelete)) {
return $this->badRequest('The fields attribute must be array.');
}
$this->model->deleteFields($entity, $fieldsToDelete);
$view = $this->view([$this->entityNameOne => $entity]);
return $this->handleView($view);
}
/**
* Delete fields from a form.
*
* @return Response
*/
public function deleteActionsAction(Request $request, $formId)
{
if (!$this->security->isGranted(['form:forms:editown', 'form:forms:editother'], 'MATCH_ONE')) {
return $this->accessDenied();
}
$entity = $this->model->getEntity($formId);
if (null === $entity) {
return $this->notFound();
}
$actionsToDelete = $request->query->all()['actions'] ?? $request->request->all()['actions'] ?? [];
if (!is_array($actionsToDelete)) {
return $this->badRequest('The actions attribute must be array.');
}
$this->model->deleteActions($entity, $actionsToDelete);
$view = $this->view([$this->entityNameOne => $entity]);
return $this->handleView($view);
}
protected function preSaveEntity(&$entity, $form, $parameters, $action = 'edit')
{
$fieldModel = $this->getModel('form.field');
\assert($fieldModel instanceof FieldModel);
$actionModel = $this->getModel('form.action');
\assert($actionModel instanceof ActionModel);
$method = $this->getCurrentRequest()->getMethod();
$isNew = false;
$alias = $entity->getAlias();
if (empty($alias)) {
// Set clean alias to prevent SQL errors
$alias = $this->model->cleanAlias($entity->getName(), '', 10);
$entity->setAlias($alias);
}
// Set timestamps
$this->model->setTimestamps($entity, true, false);
if (!$entity->getId()) {
$isNew = true;
// Save the form first to get the form ID.
// Using the repository function to not trigger the listeners twice.
$this->model->getRepository()->saveEntity($entity);
}
$formId = $entity->getId();
$requestFieldIds = [];
$requestActionIds = [];
$requestUsedAliases = [];
$currentFields = $entity->getFields();
$currentActions = $entity->getActions();
// Add fields from the request
if (!empty($parameters['fields']) && is_array($parameters['fields'])) {
$aliases = $entity->getFieldAliases();
foreach ($parameters['fields'] as &$fieldParams) {
if (empty($fieldParams['id'])) {
// Create an unique ID if not set - the following code requires one
$fieldParams['id'] = 'new'.hash('sha1', uniqid(mt_rand()));
/** @var ?Field $fieldEntity */
$fieldEntity = $fieldModel->getEntity();
} else {
/** @var ?Field $fieldEntity */
$fieldEntity = $fieldModel->getEntity($fieldParams['id']);
$requestFieldIds[] = $fieldParams['id'];
}
if (is_null($fieldEntity)) {
$msg = $this->translator->trans(
'mautic.core.error.entity.not.found',
[
'%entity%' => $this->translator->trans('mautic.form.field'),
'%id%' => $fieldParams['id'],
],
'flashes'
);
return $this->returnError($msg, Response::HTTP_NOT_FOUND);
}
$fieldEntityArray = $fieldEntity->convertToArray();
$fieldEntityArray['formId'] = $formId;
$fieldEntityArray['mappedObject'] = $fieldParams['mappedObject'] ?? null;
if (!empty($fieldParams['alias'])) {
$fieldParams['alias'] = $fieldModel->cleanAlias($fieldParams['alias'], 'f_', 25);
if (!in_array($fieldParams['alias'], $aliases)) {
$fieldEntityArray['alias'] = $fieldParams['alias'];
}
}
if (empty($fieldEntityArray['alias'])) {
$fieldEntityArray['alias'] = $fieldParams['alias'] = $fieldModel->generateAlias($fieldEntityArray['label'] ?? '', $aliases);
}
// Check that the alias is not already in use by another field
if (in_array($fieldEntityArray['alias'], $requestUsedAliases)) {
$msg = $this->translator->trans('mautic.form.field.alias.unique', ['%alias%' => $fieldEntityArray['alias']], 'validators');
return $this->returnError($msg, Response::HTTP_BAD_REQUEST);
} else {
$requestUsedAliases[] = $fieldEntityArray['alias'];
}
$fieldForm = $this->createFieldEntityForm($fieldEntityArray);
$fieldForm->submit($fieldParams, 'PATCH' !== $method);
if (!$fieldForm->isValid()) {
$formErrors = $this->getFormErrorMessages($fieldForm);
$msg = $this->getFormErrorMessage($formErrors);
return $this->returnError($msg, Response::HTTP_BAD_REQUEST);
}
}
$this->model->setFields($entity, $parameters['fields']);
}
// Remove fields which weren't in the PUT request
if (!$isNew && 'PUT' === $method) {
$fieldsToDelete = [];
foreach ($currentFields as $currentField) {
if (!in_array($currentField->getId(), $requestFieldIds)) {
$fieldsToDelete[] = $currentField->getId();
}
}
if ($fieldsToDelete) {
$this->model->deleteFields($entity, $fieldsToDelete);
}
}
// Add actions from the request
if (!empty($parameters['actions']) && is_array($parameters['actions'])) {
$actions = [];
foreach ($parameters['actions'] as &$actionParams) {
if (empty($actionParams['id'])) {
$actionParams['id'] = 'new'.hash('sha1', uniqid(mt_rand()));
$actionEntity = $actionModel->getEntity();
} else {
$actionEntity = $actionModel->getEntity($actionParams['id']);
$requestActionIds[] = $actionParams['id'];
}
$actionEntity->setForm($entity);
$actionForm = $this->createActionEntityForm($actionEntity, $actionParams);
$actionForm->submit($actionParams, 'PATCH' !== $method);
if (!$actionForm->isValid()) {
$formErrors = $this->getFormErrorMessages($actionForm);
$msg = $this->getFormErrorMessage($formErrors);
return $this->returnError($msg, Response::HTTP_BAD_REQUEST);
}
$actions[] = $actionForm->getNormData();
}
// Save the form first and new actions so that new fields are available to actions.
// Using the repository function to not trigger the listeners twice.
$this->model->getRepository()->saveEntity($entity);
$this->model->setActions($entity, $actions);
}
// Remove actions which weren't in the PUT request
if (!$isNew && 'PUT' === $method) {
$actionsToDelete = [];
foreach ($currentActions as $currentAction) {
if (!in_array($currentAction->getId(), $requestActionIds)) {
$actionsToDelete[] = $currentAction->getId();
}
}
if ($actionsToDelete) {
$this->model->deleteActions($entity, $actionsToDelete);
}
}
}
/**
* Creates the form instance.
*
* @return FormInterface<mixed>
*/
protected function createActionEntityForm(Action $entity, array $action)
{
/** @var FormModel $formModel */
$formModel = $this->getModel('form');
$components = $formModel->getCustomComponents();
$type = $action['type'] ?? $entity->getType();
$formActionModel = $this->getModel('form.action');
\assert($formActionModel instanceof ActionModel);
return $formActionModel->createForm(
$entity,
$this->formFactory,
null,
[
'csrf_protection' => false,
'allow_extra_fields' => true,
'settings' => $components['actions'][$type],
]
);
}
/**
* Creates the form instance.
*
* @return FormInterface<mixed>
*/
protected function createFieldEntityForm($entity)
{
$formFieldModel = $this->getModel('form.field');
\assert($formFieldModel instanceof FieldModel);
return $formFieldModel->createForm(
$entity,
$this->formFactory,
null,
[
'csrf_protection' => false,
'allow_extra_fields' => true,
]
);
}
/**
* @param CommonEntity $entity
* @param array<mixed> $parameters
*
* @return mixed
*/
protected function processForm(Request $request, $entity, $parameters = null, $method = 'PUT')
{
if (!isset($parameters['postAction'])) {
$parameters['postAction'] = 'return';
}
return parent::processForm($request, $entity, $parameters, $method);
}
public function newEntityAction(Request $request): Response
{
$parameters = $request->request->all();
if (!isset($parameters['postAction'])) {
$request->request->add(['postAction' => 'return']);
}
return parent::newEntityAction($request);
}
}

View File

@@ -0,0 +1,140 @@
<?php
namespace Mautic\FormBundle\Controller\Api;
use Doctrine\Persistence\ManagerRegistry;
use Mautic\ApiBundle\Controller\CommonApiController;
use Mautic\ApiBundle\Helper\EntityResultHelper;
use Mautic\CoreBundle\Factory\ModelFactory;
use Mautic\CoreBundle\Helper\AppVersion;
use Mautic\CoreBundle\Helper\CoreParametersHelper;
use Mautic\CoreBundle\Helper\UserHelper;
use Mautic\CoreBundle\Security\Permissions\CorePermissions;
use Mautic\CoreBundle\Translation\Translator;
use Mautic\FormBundle\Entity\Form;
use Mautic\FormBundle\Entity\Submission;
use Mautic\FormBundle\Model\SubmissionModel;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\RouterInterface;
/**
* @extends CommonApiController<Submission>
*/
class SubmissionApiController extends CommonApiController
{
public function __construct(CorePermissions $security, Translator $translator, EntityResultHelper $entityResultHelper, RouterInterface $router, FormFactoryInterface $formFactory, AppVersion $appVersion, RequestStack $requestStack, ManagerRegistry $doctrine, ModelFactory $modelFactory, EventDispatcherInterface $dispatcher, CoreParametersHelper $coreParametersHelper)
{
$formSubmissionModel = $modelFactory->getModel('form.submission');
\assert($formSubmissionModel instanceof SubmissionModel);
$this->model = $formSubmissionModel;
$this->entityClass = Submission::class;
$this->entityNameOne = 'submission';
$this->entityNameMulti = 'submissions';
$this->permissionBase = 'form:forms';
$this->serializerGroups = ['submissionDetails', 'formList', 'ipAddressList', 'leadBasicList', 'pageList'];
parent::__construct($security, $translator, $entityResultHelper, $router, $formFactory, $appVersion, $requestStack, $doctrine, $modelFactory, $dispatcher, $coreParametersHelper);
}
/**
* Obtains a list of entities as defined by the API URL.
*
* @param int $formId
*
* @return Response
*/
public function getEntitiesAction(Request $request, UserHelper $userHelper, $formId = null)
{
$form = $this->getFormOrResponseWithError($formId);
if ($form instanceof Response) {
return $form;
}
$this->extraGetEntitiesArguments = array_merge(
$this->extraGetEntitiesArguments,
[
'form' => $form,
'flatten_results' => true,
'return_entities' => true,
]
);
return parent::getEntitiesAction($request, $userHelper);
}
/**
* Obtains a list of entities for specific form and contact.
*
* @param int $formId
* @param int $contactId
*
* @return Response
*/
public function getEntitiesForContactAction(Request $request, UserHelper $userHelper, $formId, $contactId)
{
$filter = [
'filter' => [
'where' => [
[
'col' => 's.lead_id',
'expr' => 'eq',
'val' => (int) $contactId,
],
],
],
];
$this->extraGetEntitiesArguments = array_merge($this->extraGetEntitiesArguments, $filter);
return $this->getEntitiesAction($request, $userHelper, $formId);
}
/**
* Obtains a specific entity as defined by the API URL.
*
* @return Response
*/
public function getEntityAction(Request $request, $formId = null, $submissionId = null)
{
$form = $this->getFormOrResponseWithError($formId);
if ($form instanceof Response) {
return $form;
}
return parent::getEntityAction($request, $submissionId);
}
/**
* Tries to fetch the form and returns Response if
* - Form not found
* - User doesn't have permission to view it.
*
* Returns Form on success
*
* @param int $formId
*
* @return Response|Form
*/
protected function getFormOrResponseWithError($formId)
{
$formModel = $this->getModel('form');
$form = $formModel->getEntity($formId);
if (!$form) {
return $this->notFound();
}
if (!$this->checkEntityAccess($form)) {
return $this->accessDenied();
}
return $form;
}
}

View File

@@ -0,0 +1,443 @@
<?php
namespace Mautic\FormBundle\Controller;
use Doctrine\Persistence\ManagerRegistry;
use Mautic\CoreBundle\Controller\FormController as CommonFormController;
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;
use Twig\Environment;
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,
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, $modelFactory, $userHelper, $coreParametersHelper, $dispatcher, $translator, $flashBag, $requestStack, $security);
}
/**
* Generates new form and processes post data.
*
* @return Response
*/
public function newAction(Request $request, Environment $twig)
{
$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, $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, Environment $twig, $objectId)
{
$session = $request->getSession();
$method = $request->getMethod();
$formfield = $request->request->all()['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,
$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;
}
}

View File

@@ -0,0 +1,454 @@
<?php
namespace Mautic\FormBundle\Controller;
use Mautic\CoreBundle\Controller\FormController as CommonFormController;
use Mautic\CoreBundle\Helper\InputHelper;
use Mautic\CoreBundle\Helper\ThemeHelper;
use Mautic\CoreBundle\Twig\Helper\AnalyticsHelper;
use Mautic\CoreBundle\Twig\Helper\AssetsHelper;
use Mautic\CoreBundle\Twig\Helper\DateHelper;
use Mautic\FormBundle\Event\SubmissionEvent;
use Mautic\FormBundle\Model\FieldModel;
use Mautic\FormBundle\Model\FormModel;
use Mautic\FormBundle\Model\SubmissionModel;
use Mautic\LeadBundle\Helper\TokenHelper;
use Mautic\LeadBundle\Model\CompanyModel;
use Mautic\PageBundle\Helper\TokenHelper as PageTokenHelper;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class PublicController extends CommonFormController
{
private array $tokens = [];
/**
* @return RedirectResponse|Response
*/
public function submitAction(Request $request, DateHelper $dateTemplateHelper, PageTokenHelper $pageTokenHelper)
{
if ('POST' !== $request->getMethod()) {
return $this->accessDenied();
}
$isAjax = $request->query->get('ajax');
$form = null;
$post = $request->request->all()['mauticform'] ?? [];
$messengerMode = (!empty($post['messenger']));
$server = $request->server->all();
$return = $post['return'] ?? false;
if (empty($return)) {
// try to get it from the HTTP_REFERER
$return = $server['HTTP_REFERER'] ?? false;
}
if (!empty($return)) {
// remove mauticError and mauticMessage from the referer so it doesn't get sent back
$return = InputHelper::url($return, null, null, null, ['mauticError', 'mauticMessage'], true);
$query = (!str_contains($return, '?')) ? '?' : '&';
}
$translator = $this->translator;
// check to ensure there is a formId
if (!isset($post['formId'])) {
$error = $translator->trans('mautic.form.submit.error.unavailable', [], 'flashes');
} else {
$formModel = $this->getModel('form.form');
$form = $formModel->getEntity($post['formId']);
// check to see that the form was found
if (null === $form) {
$error = $translator->trans('mautic.form.submit.error.unavailable', [], 'flashes');
} else {
// get what to do immediately after successful post
$postAction = $form->getPostAction();
$postActionProperty = $form->getPostActionProperty();
// check to ensure the form is published
$status = $form->getPublishStatus();
if ('pending' == $status) {
$error = $translator->trans(
'mautic.form.submit.error.pending',
[
'%date%' => $dateTemplateHelper->toFull($form->getPublishUp()),
],
'flashes'
);
} elseif ('expired' == $status) {
$error = $translator->trans(
'mautic.form.submit.error.expired',
[
'%date%' => $dateTemplateHelper->toFull($form->getPublishDown()),
],
'flashes'
);
} elseif ('published' != $status) {
$error = $translator->trans('mautic.form.submit.error.unavailable', [], 'flashes');
} else {
$formSubmissionModel = $this->getModel('form.submission');
\assert($formSubmissionModel instanceof SubmissionModel);
$result = $formSubmissionModel->saveSubmission($post, $server, $form, $request, true);
if (!empty($result['errors'])) {
if ($messengerMode || $isAjax) {
$error = $result['errors'];
} else {
$error = ($result['errors']) ?
$this->translator->trans('mautic.form.submission.errors').'<br /><ol><li>'.
implode('</li><li>', $result['errors']).'</li></ol>' : false;
}
} elseif (!empty($result['callback'])) {
/** @var SubmissionEvent $submissionEvent */
$submissionEvent = $result['callback'];
$callbackResponses = $submissionEvent->getPostSubmitCallbackResponse();
// These submit actions have requested a callback after all is said and done
$callbacksRequested = $submissionEvent->getPostSubmitCallback();
foreach ($callbacksRequested as $key => $callbackRequested) {
$callbackRequested['messengerMode'] = $messengerMode;
$callbackRequested['ajaxMode'] = $isAjax;
if (isset($callbackRequested['eventName'])) {
$submissionEvent->setPostSubmitCallback($key, $callbackRequested);
$submissionEvent->setContext($key);
$this->dispatcher->dispatch($submissionEvent, $callbackRequested['eventName']);
}
if ($submissionEvent->isPropagationStopped() && $submissionEvent->hasPostSubmitResponse()) {
if ($messengerMode) {
$callbackResponses[$key] = $submissionEvent->getPostSubmitResponse();
} else {
return $submissionEvent->getPostSubmitResponse();
}
}
}
} elseif (isset($result['submission'])) {
/** @var SubmissionEvent $submissionEvent */
$submissionEvent = $result['submission'];
}
}
}
}
if (isset($submissionEvent) && !empty($postActionProperty)) {
// Replace post action property with tokens to support custom redirects, etc
$postActionProperty = $this->replacePostSubmitTokens($postActionProperty, $submissionEvent, $pageTokenHelper);
}
if ($messengerMode || $isAjax) {
// Return the call via postMessage API
$data = ['success' => 1];
if (!empty($error)) {
if (is_array($error)) {
$data['validationErrors'] = $error;
} else {
$data['errorMessage'] = $error;
}
$data['success'] = 0;
} else {
// Include results in ajax response for JS callback use
if (isset($submissionEvent)) {
$data['results'] = $submissionEvent->getResults();
}
switch ($postAction) {
case 'redirect':
$data['redirect'] = $postActionProperty;
break;
case 'hideform':
$data['hideform'] = true;
// no break
default:
if (!empty($postActionProperty)) {
$data['successMessage'] = [$postActionProperty];
}
break;
}
if (!empty($callbackResponses)) {
foreach ($callbackResponses as $response) {
// Convert the responses to something useful for a JS response
if ($response instanceof RedirectResponse && !isset($data['redirect'])) {
$data['redirect'] = $response->getTargetUrl();
} elseif ($response instanceof Response) {
if (!isset($data['successMessage'])) {
$data['successMessage'] = [];
}
$data['successMessage'][] = $response->getContent();
} elseif (is_array($response)) {
$data = array_merge($data, $response);
} elseif (is_string($response)) {
if (!isset($data['successMessage'])) {
$data['successMessage'] = [];
}
$data['successMessage'][] = $response;
} // ignore anything else
}
}
// Combine all messages into one
if (isset($data['successMessage'])) {
$data['successMessage'] = implode('<br /><br />', $data['successMessage']);
}
}
if (isset($post['formName'])) {
$data['formName'] = $post['formName'];
}
if ($isAjax) {
// Post via ajax so return a json response
return new JsonResponse($data);
} else {
$response = json_encode($data);
return $this->render('@MauticForm/messenger.html.twig', ['response' => $response]);
}
} else {
if (!empty($error)) {
if ($return) {
$hash = (null !== $form) ? '#'.strtolower($form->getAlias()) : '';
return $this->redirect($return.$query.'mauticError='.rawurlencode($error).$hash);
} else {
$msg = $error;
$msgType = 'error';
}
} elseif ('redirect' == $postAction) {
return $this->redirect($postActionProperty);
} elseif ('return' == $postAction) {
if (!empty($return)) {
if (!empty($postActionProperty)) {
$return .= $query.'mauticMessage='.rawurlencode($postActionProperty);
}
return $this->redirect($return);
} else {
$msg = $this->translator->trans('mautic.form.submission.thankyou');
}
} else {
$msg = $postActionProperty;
}
$session = $request->getSession();
$session->set(
'mautic.emailbundle.message',
[
'message' => $msg,
'type' => (empty($msgType)) ? 'notice' : $msgType,
]
);
return $this->redirectToRoute('mautic_form_postmessage');
}
}
/**
* Displays a message.
*/
public function messageAction(Request $request, AnalyticsHelper $analyticsHelper, AssetsHelper $assetsHelper, ThemeHelper $themeHelper): Response
{
$session = $request->getSession();
$message = $session->get('mautic.emailbundle.message', []);
$msg = (!empty($message['message'])) ? $message['message'] : '';
$msgType = (!empty($message['type'])) ? $message['type'] : 'notice';
$analytics = $analyticsHelper->getCode();
if (!empty($analytics)) {
$assetsHelper->addCustomDeclaration($analytics);
}
$logicalName = $themeHelper->checkForTwigTemplate('@themes/'.$this->coreParametersHelper->get('theme').'/html/message.html.twig');
return $this->render($logicalName, [
'message' => $msg,
'type' => $msgType,
'template' => $this->coreParametersHelper->get('theme'),
]);
}
/**
* Gives a preview of the form.
*
* @return Response
*
* @throws \Exception
* @throws \Mautic\CoreBundle\Exception\FileNotFoundException
*/
public function previewAction(Request $request, AnalyticsHelper $analyticsHelper, AssetsHelper $assetsHelper, ThemeHelper $themeHelper, int $id = 0)
{
$model = $this->getModel('form.form');
\assert($model instanceof FormModel);
$objectId = (empty($id)) ? (int) $request->get('id') : $id;
$css = InputHelper::string((string) $request->get('css'));
$form = $model->getEntity($objectId);
$customStylesheets = (!empty($css)) ? explode(',', $css) : [];
$template = null;
if (null === $form || !$form->isPublished()) {
return $this->notFound();
} else {
$html = $model->getContent($form);
$model->populateValuesWithGetParameters($form, $html);
$viewParams = [
'content' => $html,
'stylesheets' => $customStylesheets,
'name' => $form->getName(),
'metaRobots' => '<meta name="robots" content="index">',
];
if ($form->getNoIndex()) {
$viewParams['metaRobots'] = '<meta name="robots" content="noindex">';
}
// Use form specific template or system-wide default theme
$template = $form->getTemplate() ?? $this->coreParametersHelper->get('theme');
if (!empty($template)) {
$theme = $themeHelper->getTheme($template);
if ($theme->getTheme() != $template) {
$config = $theme->getConfig();
if (in_array('form', $config['features'])) {
$template = $theme->getTheme();
} else {
$template = null;
}
}
}
}
$viewParams['template'] = $template;
if (!empty($template)) {
$logicalName = $themeHelper->checkForTwigTemplate('@themes/'.$template.'/html/form.html.twig');
$analytics = $analyticsHelper->getCode();
foreach ($customStylesheets as $css) {
$assetsHelper->addStylesheet($css);
}
if (!empty($analytics)) {
$assetsHelper->addCustomDeclaration($analytics);
}
if ($form->getNoIndex()) {
$assetsHelper->addCustomDeclaration('<meta name="robots" content="noindex">');
}
return $this->render($logicalName, $viewParams);
}
return $this->render('@MauticForm/form.html.twig', $viewParams);
}
/**
* Generates JS file for automatic form generation.
*/
public function generateAction(Request $request): Response
{
// Don't store a visitor with this request
defined('MAUTIC_NON_TRACKABLE_REQUEST') || define('MAUTIC_NON_TRACKABLE_REQUEST', 1);
$formId = (int) $request->get('id');
$model = $this->getModel('form.form');
\assert($model instanceof FormModel);
$form = $model->getEntity($formId);
$js = '';
if (null !== $form) {
$status = $form->getPublishStatus();
if ('published' === $status) {
$js = $model->getAutomaticJavascript($form);
}
}
$response = new Response();
$response->setContent($js);
$response->setStatusCode(Response::HTTP_OK);
$response->headers->set('Content-Type', 'text/javascript');
return $response;
}
/**
* @return Response
*/
public function embedAction(Request $request)
{
$formId = (int) $request->get('id');
/** @var FormModel $model */
$model = $this->getModel('form');
$form = $model->getEntity($formId);
if (null !== $form) {
$status = $form->getPublishStatus();
if ('published' === $status) {
if ($request->get('video')) {
return $this->render(
'@MauticForm/Public/videoembed.html.twig',
['form' => $form, 'fieldSettings' => $model->getCustomComponents()['fields']]
);
}
$content = $model->getContent($form, false, true);
return new Response($content);
}
}
return new Response('', Response::HTTP_NOT_FOUND);
}
/**
* @return string|string[]
*/
private function replacePostSubmitTokens($string, SubmissionEvent $submissionEvent, PageTokenHelper $pageTokenHelper): string|array
{
if (count($this->tokens)) {
return $this->tokens;
}
if ($lead = $submissionEvent->getLead()) {
$this->tokens = array_merge(
$submissionEvent->getTokens(),
TokenHelper::findLeadTokens(
$string,
$lead->getProfileFields()
)
);
}
$this->tokens = array_merge(
$this->tokens,
$pageTokenHelper->findPageTokens($string)
);
return str_replace(array_keys($this->tokens), array_values($this->tokens), $string);
}
public function lookupCompanyAction(Request $request, FieldModel $fieldModel, CompanyModel $companyModel): JsonResponse
{
$parameters = json_decode($request->getContent(), true);
$search = InputHelper::clean($parameters['search'] ?? '');
$formId = (int) ($parameters['formId'] ?? 0);
// Intentionally vague message as the JS takes care of this.
// Make it hard to abuse this public endpoint.
$vagueErrorMessage = ['error' => 'Invalid request param'];
if (mb_strlen($search) < 3 || !$formId) {
return new JsonResponse($vagueErrorMessage, JsonResponse::HTTP_BAD_REQUEST);
}
if (!$fieldModel->getRepository()->fieldExistsByFormAndType($formId, 'companyLookup')) {
return new JsonResponse($vagueErrorMessage, JsonResponse::HTTP_BAD_REQUEST);
}
return new JsonResponse($companyModel->getRepository()->getCompanyLookupData($search));
}
}

View File

@@ -0,0 +1,557 @@
<?php
namespace Mautic\FormBundle\Controller;
use Doctrine\Persistence\ManagerRegistry;
use Mautic\CoreBundle\Controller\FormController as CommonFormController;
use Mautic\CoreBundle\Factory\ModelFactory;
use Mautic\CoreBundle\Factory\PageHelperFactoryInterface;
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\Helper\FormFieldHelper;
use Mautic\FormBundle\Helper\FormUploader;
use Mautic\FormBundle\Model\FieldModel;
use Mautic\FormBundle\Model\FormModel;
use Mautic\FormBundle\Model\SubmissionModel;
use Mautic\FormBundle\Model\SubmissionResultLoader;
use Mautic\LeadBundle\Form\Type\BatchType;
use Mautic\LeadBundle\Model\ListModel;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
class ResultController extends CommonFormController
{
public function __construct(FormFactoryInterface $formFactory, FormFieldHelper $fieldHelper, ManagerRegistry $doctrine, ModelFactory $modelFactory, UserHelper $userHelper, CoreParametersHelper $coreParametersHelper, EventDispatcherInterface $dispatcher, Translator $translator, FlashBag $flashBag, RequestStack $requestStack, CorePermissions $security)
{
$this->setStandardParameters(
'form.submission', // model name
'form:forms', // permission base
'mautic_form', // route base
'mautic.formresult', // session base
'mautic.form.result', // lang string base
'@MauticForm/Result', // template base
'mautic_form', // activeLink
'formresult' // mauticContent
);
parent::__construct($formFactory, $fieldHelper, $doctrine, $modelFactory, $userHelper, $coreParametersHelper, $dispatcher, $translator, $flashBag, $requestStack, $security);
}
/**
* @return \Symfony\Component\HttpFoundation\JsonResponse|\Symfony\Component\HttpFoundation\RedirectResponse|Response
*/
public function indexAction(Request $request, PageHelperFactoryInterface $pageHelperFacotry, int $objectId, int $page = 1)
{
/** @var FormModel $formModel */
$formModel = $this->getModel('form.form');
$form = $formModel->getEntity($objectId);
$session = $request->getSession();
$formPage = $session->get('mautic.form.page', 1);
$returnUrl = $this->generateUrl('mautic_form_index', ['page' => $formPage]);
$viewOnlyFields = $formModel->getCustomComponents()['viewOnlyFields'];
if (null === $form) {
// redirect back to form list
return $this->postActionRedirect(
[
'returnUrl' => $returnUrl,
'viewParameters' => ['page' => $formPage],
'contentTemplate' => 'Mautic\FormBundle\Controller\FormController::indexAction',
'passthroughVars' => [
'activeLink' => 'mautic_form_index',
'mauticContent' => 'form',
],
'flashes' => [
[
'type' => 'error',
'msg' => 'mautic.form.error.notfound',
'msgVars' => ['%id%' => $objectId],
],
],
]
);
} elseif (!$this->security->hasEntityAccess(
'form:forms:viewown',
'form:forms:viewother',
$form->getCreatedBy()
)
) {
return $this->accessDenied();
}
if ('POST' === $request->getMethod()) {
$this->setListFilters($request->query->get('name'));
}
$pageHelper = $pageHelperFacotry->make('mautic.formresult.'.$objectId, $page);
// set limits
$limit = $pageHelper->getLimit();
$start = $pageHelper->getStart();
// Set order direction to desc if not set
if (!$session->get('mautic.formresult.'.$objectId.'.orderbydir', null)) {
$session->set('mautic.formresult.'.$objectId.'.orderbydir', 'DESC');
}
$orderBy = $session->get('mautic.formresult.'.$objectId.'.orderby', 's.date_submitted');
$orderByDir = $session->get('mautic.formresult.'.$objectId.'.orderbydir', 'DESC');
$filters = $session->get('mautic.formresult.'.$objectId.'.filters', []);
$model = $this->getModel('form.submission');
if ($request->query->has('result')) {
// Force ID
$filters['s.id'] = ['column' => 's.id', 'expr' => 'like', 'value' => (int) $request->query->get('result'), 'strict' => false];
$session->set("mautic.formresult.$objectId.filters", $filters);
}
// get the results
$entities = $model->getEntities(
[
'start' => $start,
'limit' => $limit,
'filter' => ['force' => $filters],
'orderBy' => $orderBy,
'orderByDir' => $orderByDir,
'form' => $form,
'withTotalCount' => true,
'viewOnlyFields' => $viewOnlyFields,
'simpleResults' => true,
]
);
$count = $entities['count'];
$results = $entities['results'];
unset($entities);
if ($count && $count < ($start + 1)) {
// the number of entities are now less then the current page so redirect to the last page
$lastPage = $pageHelper->countPage($count);
$pageHelper->rememberPage($lastPage);
$returnUrl = $this->generateUrl('mautic_form_results', ['objectId' => $objectId, 'page' => $lastPage]);
return $this->postActionRedirect(
[
'returnUrl' => $returnUrl,
'viewParameters' => ['page' => $lastPage],
'contentTemplate' => 'Mautic\FormBundle\Controller\ResultController::indexAction',
'passthroughVars' => [
'activeLink' => 'mautic_form_index',
'mauticContent' => 'formresult',
],
]
);
}
// set what page currently on so that we can return here if need be
$pageHelper->rememberPage($page);
return $this->delegateView(
[
'viewParameters' => [
'items' => $results,
'filters' => $filters,
'form' => $form,
'viewOnlyFields' => $viewOnlyFields,
'page' => $page,
'totalCount' => $count,
'limit' => $limit,
'tmpl' => $request->isXmlHttpRequest() ? $request->get('tmpl', 'index') : 'index',
'canDelete' => $this->security->hasEntityAccess(
'form:forms:editown',
'form:forms:editother',
$form->getCreatedBy()
),
'enableExportPermission'=> $this->security->isAdmin() || $this->security->isGranted('form:export:enable', 'MATCH_ONE'),
],
'contentTemplate' => '@MauticForm/Result/list.html.twig',
'passthroughVars' => [
'activeLink' => 'mautic_form_index',
'mauticContent' => 'formresult',
'route' => $this->generateUrl(
'mautic_form_results',
[
'objectId' => $objectId,
'page' => $page,
]
),
],
]
);
}
/**
* @return BinaryFileResponse
*/
public function downloadFileAction(int $submissionId, string $field, FormUploader $formUploader)
{
/** @var SubmissionResultLoader $submissionResultLoader */
$submissionResultLoader = $this->getModel('form.submission_result_loader');
$submission = $submissionResultLoader->getSubmissionWithResult($submissionId);
if (!$submission) {
throw $this->createNotFoundException();
}
$results = $submission->getResults();
$fieldEntity = $submission->getFieldByAlias($field);
if (empty($results[$field]) || null === $fieldEntity) {
throw $this->createNotFoundException();
}
if (empty($fieldEntity->getProperties()['public']) && !$this->security->hasEntityAccess(
'form:forms:viewown',
'form:forms:viewother',
$submission->getForm()->getCreatedBy())
) {
return $this->accessDenied();
}
$fileName = $results[$field];
$file = $formUploader->getCompleteFilePath($fieldEntity, $fileName);
$fs = new Filesystem();
if (!$fs->exists($file)) {
throw $this->createNotFoundException();
}
$response = new BinaryFileResponse($file);
$response::trustXSendfileTypeHeader();
$response->setContentDisposition(
ResponseHeaderBag::DISPOSITION_ATTACHMENT,
$fileName
);
return $response;
}
public function downloadFileByFileNameAction(string $fieldId, string $fileName, FieldModel $fieldModel, FormUploader $formUploader): Response
{
$fieldEntity = $fieldModel->getEntity($fieldId);
if (empty($fieldEntity->getProperties()['public']) && !$this->security->hasEntityAccess(
'form:forms:viewown',
'form:forms:viewother',
$fieldEntity->getForm()->getCreatedBy())
) {
return $this->accessDenied();
}
$file = $formUploader->getCompleteFilePath($fieldEntity, $fileName);
$fs = new Filesystem();
if (!$fs->exists($file)) {
throw $this->createNotFoundException();
}
$response = new BinaryFileResponse($file);
$response::trustXSendfileTypeHeader();
$response->setContentDisposition(
ResponseHeaderBag::DISPOSITION_ATTACHMENT,
$fileName
);
return $response;
}
/**
* @param int $objectId
* @param string $format
*
* @return Response
*
* @throws \Exception
*/
public function exportAction(Request $request, $objectId, $format = 'csv')
{
$formModel = $this->getModel('form.form');
$form = $formModel->getEntity($objectId);
$session = $request->getSession();
$formPage = $session->get('mautic.form.page', 1);
$returnUrl = $this->generateUrl('mautic_form_index', ['page' => $formPage]);
if (!$this->security->isAdmin() && !$this->security->isGranted('form:export:enable', 'MATCH_ONE')) {
return $this->accessDenied();
}
if (null === $form) {
// redirect back to form list
return $this->postActionRedirect(
[
'returnUrl' => $returnUrl,
'viewParameters' => ['page' => $formPage],
'contentTemplate' => 'Mautic\FormBundle\Controller\FormController::indexAction',
'passthroughVars' => [
'activeLink' => 'mautic_form_index',
'mauticContent' => 'form',
],
'flashes' => [
[
'type' => 'error',
'msg' => 'mautic.form.error.notfound',
'msgVars' => ['%id%' => $objectId],
],
],
]
);
} elseif (!$this->security->hasEntityAccess(
'form:forms:viewown',
'form:forms:viewother',
$form->getCreatedBy()
)
) {
return $this->accessDenied();
}
$orderBy = $session->get('mautic.formresult.'.$objectId.'.orderby', 's.date_submitted');
$orderByDir = $session->get('mautic.formresult.'.$objectId.'.orderbydir', 'DESC');
$filters = $session->get('mautic.formresult.'.$objectId.'.filters', []);
$args = [
'limit' => false,
'filter' => ['force' => $filters],
'orderBy' => $orderBy,
'orderByDir' => $orderByDir,
'form' => $form,
];
/** @var SubmissionModel $model */
$model = $this->getModel('form.submission');
return $model->exportResults($format, $form, $args);
}
/**
* Delete a form result.
*
* @return array|Response
*/
public function deleteAction(Request $request)
{
$formId = $request->get('formId', 0);
$objectId = $request->get('objectId', 0);
$session = $request->getSession();
$page = $session->get('mautic.formresult.'.$formId.'.page', 1);
$flashes = [];
if (Request::METHOD_POST === $request->getMethod()) {
$model = $this->getModel('form.submission');
\assert($model instanceof SubmissionModel);
// Find the result
$entity = $model->getEntity($objectId);
if (null === $entity) {
$flashes[] = [
'type' => 'error',
'msg' => 'mautic.form.error.notfound',
'msgVars' => ['%id%' => $objectId],
];
} elseif (!$this->security->hasEntityAccess('form:forms:editown', 'form:forms:editother', $entity->getCreatedBy())) {
return $this->accessDenied();
} else {
$id = $entity->getId();
$model->deleteEntity($entity);
$flashes[] = [
'type' => 'notice',
'msg' => 'mautic.core.notice.deleted',
'msgVars' => [
'%name%' => '#'.$id,
],
];
}
} // else don't do anything
$viewParameters = [
'objectId' => $formId,
'page' => $page,
];
return $this->postActionRedirect(
[
'returnUrl' => $this->generateUrl('mautic_form_results', $viewParameters),
'viewParameters' => $viewParameters,
'contentTemplate' => 'Mautic\FormBundle\Controller\ResultController::indexAction',
'passthroughVars' => [
'mauticContent' => 'formresult',
],
'flashes' => $flashes,
]
);
}
/**
* @return \Symfony\Component\HttpFoundation\JsonResponse|\Symfony\Component\HttpFoundation\RedirectResponse
*/
public function batchDeleteAction(Request $request)
{
return $this->batchDeleteStandard($request);
}
protected function getModelName(): string
{
return 'form.submission';
}
protected function getIndexRoute(): string
{
return 'mautic_form_results';
}
protected function getActionRoute(): string
{
return 'mautic_form_results_action';
}
/**
* Set the main form ID as the objectId.
*/
protected function generateUrl(string $route, array $parameters = [], int $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH): string
{
$formId = $this->getFormIdFromRequest($parameters);
switch ($route) {
case 'mautic_form_results_action':
$parameters['formId'] = $formId;
break;
case 'mautic_form_results':
$parameters['objectId'] = $formId;
break;
}
return parent::generateUrl($route, $parameters, $referenceType);
}
public function getPostActionRedirectArguments(array $args, $action): array
{
switch ($action) {
case 'batchDelete':
$formId = $this->getFormIdFromRequest();
$args['viewParameters']['objectId'] = $formId;
break;
}
return $args;
}
/**
* @param array $parameters
*
* @return mixed
*/
protected function getFormIdFromRequest($parameters = [])
{
$request = $this->getCurrentRequest();
if ($request->attributes->has('formId')) {
$formId = $request->attributes->get('formId');
} elseif ($request->request->has('formId')) {
$formId = $request->request->get('formId');
} else {
$objectId = $parameters['objectId'] ?? 0;
$formId = $parameters['formId'] ?? $request->query->get('formId', $objectId);
}
return $formId;
}
public function addToSegmentAction(Request $request, int $objectId, FormModel $formModel, SubmissionModel $model, ListModel $segmentModel): Response
{
$form = $formModel->getEntity($objectId);
$session = $request->getSession();
$formPage = $session->get('mautic.form.page', 1);
$returnUrl = $this->generateUrl('mautic_form_index', ['page' => $formPage]);
if (null === $form) {
return $this->postActionRedirect([
'returnUrl' => $returnUrl,
'viewParameters' => ['page' => $formPage],
'contentTemplate' => 'Mautic\\FormBundle\\Controller\\FormController::indexAction',
'passthroughVars' => [
'activeLink' => 'mautic_form_index',
'mauticContent' => 'form',
],
'flashes' => [
[
'type' => 'error',
'msg' => 'mautic.form.error.notfound',
'msgVars' => ['%id%' => $objectId],
],
],
]);
} elseif (!$this->security->hasEntityAccess('form:forms:viewown', 'form:forms:viewother', $form->getCreatedBy())) {
return $this->accessDenied();
}
$orderBy = $session->get('mautic.formresult.'.$objectId.'.orderby', 's.date_submitted');
$orderByDir = $session->get('mautic.formresult.'.$objectId.'.orderbydir', 'DESC');
$filters = $session->get('mautic.formresult.'.$objectId.'.filters', []);
$viewOnlyFields = $formModel->getCustomComponents()['viewOnlyFields'];
$entities = $model->getEntities([
'limit' => false,
'filter' => ['force' => $filters],
'orderBy' => $orderBy,
'orderByDir' => $orderByDir,
'form' => $form,
'viewOnlyFields' => $viewOnlyFields,
'simpleResults' => true,
]);
if (isset($entities['results'])) {
$entities = $entities['results'];
}
$contactIds = [];
foreach ($entities as $result) {
if (!empty($result['leadId'])) {
$contactIds[] = (int) $result['leadId'];
}
}
$contactIds = array_values(array_unique($contactIds));
$lists = $segmentModel->getUserLists();
$items = [];
foreach ($lists as $list) {
$items[$list['name'].' ('.$list['id'].')'] = $list['id'];
}
$route = $this->generateUrl('mautic_segment_batch_contact_set');
$formView = $this->createForm(
BatchType::class,
['ids' => json_encode($contactIds)],
[
'items' => $items,
'action' => $route,
'attr' => [
'data-submit-callback' => 'formResultBatchSubmit',
],
]
)->createView();
return $this->delegateView([
'viewParameters' => [
'form' => $formView,
],
'contentTemplate' => '@MauticLead/Batch/form.html.twig',
'passthroughVars' => [
'activeLink' => 'mautic_form_index',
'mauticContent' => 'formresult',
'route' => $route,
],
]);
}
}