vendor/sulu/form-bundle/Form/Builder.php line 160

Open in your IDE?
  1. <?php
  2. /*
  3. * This file is part of Sulu.
  4. *
  5. * (c) Sulu GmbH
  6. *
  7. * This source file is subject to the MIT license that is bundled
  8. * with this source code in the file LICENSE.
  9. */
  10. namespace Sulu\Bundle\FormBundle\Form;
  11. use Sulu\Bundle\FormBundle\Csrf\DisabledCsrfTokenManager;
  12. use Sulu\Bundle\FormBundle\Dynamic\Checksum;
  13. use Sulu\Bundle\FormBundle\Dynamic\FormFieldTypePool;
  14. use Sulu\Bundle\FormBundle\Entity\Dynamic;
  15. use Sulu\Bundle\FormBundle\Entity\Form;
  16. use Sulu\Bundle\FormBundle\Form\Type\DynamicFormType;
  17. use Sulu\Bundle\FormBundle\Repository\FormRepository;
  18. use Sulu\Bundle\FormBundle\TitleProvider\TitleProviderPoolInterface;
  19. use Symfony\Component\Form\FormFactory;
  20. use Symfony\Component\Form\FormInterface;
  21. use Symfony\Component\HttpFoundation\Request;
  22. use Symfony\Component\HttpFoundation\RequestStack;
  23. use Symfony\Component\HttpKernel\Exception\HttpException;
  24. use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
  25. /**
  26. * Builds a dynamic form.
  27. */
  28. class Builder implements BuilderInterface
  29. {
  30. /**
  31. * @var FormInterface[]
  32. */
  33. private $cache = [];
  34. /**
  35. * @var RequestStack
  36. */
  37. protected $requestStack;
  38. /**
  39. * @var FormFieldTypePool
  40. */
  41. protected $formFieldTypePool;
  42. /**
  43. * @var TitleProviderPoolInterface
  44. */
  45. protected $titleProviderPool;
  46. /**
  47. * @var FormRepository
  48. */
  49. protected $formRepository;
  50. /**
  51. * @var FormFactory
  52. */
  53. protected $formFactory;
  54. /**
  55. * @var Checksum
  56. */
  57. protected $checksum;
  58. /**
  59. * @var CsrfTokenManagerInterface
  60. */
  61. private $csrfTokenManager;
  62. /**
  63. * @var bool
  64. */
  65. private $csrfProtection;
  66. public function __construct(
  67. RequestStack $requestStack,
  68. FormFieldTypePool $formFieldTypePool,
  69. TitleProviderPoolInterface $titleProviderPool,
  70. FormRepository $formRepository,
  71. FormFactory $formFactory,
  72. Checksum $checksum,
  73. CsrfTokenManagerInterface $csrfTokenManager,
  74. bool $csrfProtection = false
  75. ) {
  76. $this->requestStack = $requestStack;
  77. $this->formFieldTypePool = $formFieldTypePool;
  78. $this->titleProviderPool = $titleProviderPool;
  79. $this->formRepository = $formRepository;
  80. $this->formFactory = $formFactory;
  81. $this->checksum = $checksum;
  82. $this->csrfTokenManager = $csrfTokenManager;
  83. $this->csrfProtection = $csrfProtection;
  84. }
  85. public function buildByRequest(Request $request): ?FormInterface
  86. {
  87. foreach ($request->request->all() as $key => $parameters) {
  88. if (0 === \strpos($key, 'dynamic_')) {
  89. if (!\is_array($parameters)
  90. || !\is_string($parameters['checksum'] ?? null)
  91. || !\is_string($parameters['type'] ?? null)
  92. || !\is_string($parameters['formId'] ?? null)
  93. || !\is_string($parameters['formName'] ?? null)
  94. || !\is_string($parameters['typeId'] ?? null)
  95. ) {
  96. continue;
  97. }
  98. $formNameParts = \explode('dynamic_', $key, 2);
  99. $checksumCheck = $this->checksum->check(
  100. $parameters['checksum'],
  101. $parameters['type'],
  102. $parameters['typeId'],
  103. (int) $parameters['formId'],
  104. $parameters['formName']
  105. );
  106. if (!isset($formNameParts[1])) {
  107. continue;
  108. }
  109. if (!$checksumCheck) {
  110. throw new HttpException(400, 'SuluFormBundle: Checksum not valid!');
  111. }
  112. $locale = $request->getLocale();
  113. if (isset($parameters['locale']) && \is_string($parameters['locale'])) {
  114. $locale = $parameters['locale'];
  115. }
  116. return $this->build(
  117. (int) $parameters['formId'],
  118. $parameters['type'],
  119. $parameters['typeId'],
  120. $locale,
  121. $parameters['formName']
  122. );
  123. }
  124. }
  125. return null;
  126. }
  127. public function build(int $id, string $type, string $typeId, ?string $locale = null, string $name = 'form'): ?FormInterface
  128. {
  129. $request = $this->requestStack->getCurrentRequest();
  130. if (!$locale) {
  131. $locale = $request->getLocale();
  132. }
  133. // Check if form was builded before and return the cached form.
  134. $key = $this->getKey($id, $type, $typeId, $locale, $name);
  135. if (!isset($this->cache[$key])) {
  136. $this->cache[$key] = $this->buildForm($id, $type, $typeId, $locale, $name);
  137. }
  138. return $this->cache[$key];
  139. }
  140. /**
  141. * Returns formType and the built form.
  142. */
  143. protected function buildForm(int $id, string $type, string $typeId, string $locale, string $name): ?FormInterface
  144. {
  145. $request = $this->requestStack->getCurrentRequest();
  146. // Load Form entity
  147. $formEntity = $this->loadFormEntity($id, $locale);
  148. if (!$formEntity) {
  149. return null;
  150. }
  151. $webspaceKey = $this->getWebspaceKey();
  152. // Create Form
  153. $form = $this->createForm(
  154. $name,
  155. $type,
  156. $typeId,
  157. $locale,
  158. $formEntity,
  159. $webspaceKey
  160. );
  161. // Handle request
  162. $form->handleRequest($request);
  163. return $form;
  164. }
  165. private function getKey(int $id, string $type, string $typeId, string $locale, string $name): string
  166. {
  167. return \implode('__', \func_get_args());
  168. }
  169. private function createForm(string $name, string $type, string $typeId, string $locale, Form $formEntity, string $webspaceKey): FormInterface
  170. {
  171. $defaults = $this->getDefaults($formEntity, $locale);
  172. $typeName = $this->titleProviderPool->get($type)->getTitle($typeId, $locale);
  173. $recaptchaFields = $formEntity->getFieldsByType('recaptcha');
  174. $csrfTokenProtection = $this->csrfProtection;
  175. if (\count($recaptchaFields)) {
  176. $csrfTokenProtection = false;
  177. }
  178. return $this->formFactory->createNamed(
  179. 'dynamic_' . $name . $formEntity->getId(),
  180. DynamicFormType::class,
  181. new Dynamic($type, $typeId, $locale, $formEntity, $defaults, $webspaceKey, $typeName),
  182. [
  183. 'formEntity' => $formEntity,
  184. 'locale' => $locale,
  185. 'type' => $type,
  186. 'typeId' => $typeId,
  187. 'csrf_protection' => $csrfTokenProtection,
  188. 'name' => $name,
  189. 'block_name' => 'dynamic_' . $name,
  190. 'csrf_token_manager' => new DisabledCsrfTokenManager($this->csrfTokenManager),
  191. ]
  192. );
  193. }
  194. /**
  195. * Load Form entity.
  196. */
  197. private function loadFormEntity(int $id, string $locale): ?Form
  198. {
  199. $formEntity = $this->formRepository->loadById($id, $locale);
  200. if (!$formEntity) {
  201. return null;
  202. }
  203. $translation = $formEntity->getTranslation($locale);
  204. if (!$translation) {
  205. // No translation for this locale exists
  206. return null;
  207. }
  208. return $formEntity;
  209. }
  210. /**
  211. * Get defaults.
  212. *
  213. * @return mixed[]
  214. */
  215. private function getDefaults(Form $formEntity, string $locale): array
  216. {
  217. // set Defaults
  218. $defaults = [];
  219. foreach ($formEntity->getFields() as $field) {
  220. $fieldTranslation = $field->getTranslation($locale);
  221. if ($fieldTranslation && $fieldTranslation->getDefaultValue()) {
  222. $value = $this->formFieldTypePool->get($field->getType())->getDefaultValue($field, $locale);
  223. $defaults[$field->getKey()] = $value;
  224. }
  225. }
  226. return $defaults;
  227. }
  228. private function getWebspaceKey(): ?string
  229. {
  230. $request = $this->requestStack->getCurrentRequest();
  231. $webspaceKey = null;
  232. if ($request->get('_sulu')) {
  233. if ($request->get('_sulu')->getAttribute('webspace')) {
  234. $webspaceKey = $request->get('_sulu')->getAttribute('webspace')->getKey();
  235. }
  236. }
  237. return $webspaceKey;
  238. }
  239. }