[BUGFIX] EXT:form - Fix setting of additional f:form arguments via TS
[Packages/TYPO3.CMS.git] / typo3 / sysext / form / Classes / Domain / Builder / ElementBuilder.php
1 <?php
2 namespace TYPO3\CMS\Form\Domain\Builder;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Utility\MathUtility;
18 use TYPO3\CMS\Form\Domain\Model\Element;
19
20 /**
21 * Builder for Element domain models.
22 */
23 class ElementBuilder
24 {
25 /**
26 * @param FormBuilder $formBuilder
27 * @param Element $element
28 * @param array $userDefinedTypoScript
29 * @return ElementBuilder
30 */
31 public static function create(FormBuilder $formBuilder, Element $element, array $userDefinedTypoScript)
32 {
33 /** @var ElementBuilder $elementBuilder */
34 $elementBuilder = \TYPO3\CMS\Form\Utility\FormUtility::getObjectManager()->get(ElementBuilder::class);
35 $elementBuilder->setFormBuilder($formBuilder);
36 $elementBuilder->setElement($element);
37 $elementBuilder->setUserConfiguredElementTyposcript($userDefinedTypoScript);
38 return $elementBuilder;
39 }
40
41 /**
42 * @var \TYPO3\CMS\Form\Domain\Repository\TypoScriptRepository
43 */
44 protected $typoScriptRepository;
45
46 /**
47 * @var \TYPO3\CMS\Extbase\Service\TypoScriptService
48 */
49 protected $typoScriptService;
50
51 /**
52 * @var array
53 */
54 protected $userConfiguredElementTyposcript = array();
55
56 /**
57 * @var array
58 */
59 protected $htmlAttributes = array();
60
61 /**
62 * @var array
63 */
64 protected $additionalArguments = array();
65
66 /**
67 * @var array
68 */
69 protected $wildcardPrefixes = array();
70
71 /**
72 * @var FormBuilder
73 */
74 protected $formBuilder;
75
76 /**
77 * @var Element
78 */
79 protected $element;
80
81 /**
82 * @param \TYPO3\CMS\Form\Domain\Repository\TypoScriptRepository $typoScriptRepository
83 * @return void
84 */
85 public function injectTypoScriptRepository(\TYPO3\CMS\Form\Domain\Repository\TypoScriptRepository $typoScriptRepository)
86 {
87 $this->typoScriptRepository = $typoScriptRepository;
88 }
89
90 /**
91 * @param \TYPO3\CMS\Extbase\Service\TypoScriptService $typoScriptService
92 * @return void
93 */
94 public function injectTypoScriptService(\TYPO3\CMS\Extbase\Service\TypoScriptService $typoScriptService)
95 {
96 $this->typoScriptService = $typoScriptService;
97 }
98
99 /**
100 * @param FormBuilder $formBuilder
101 */
102 public function setFormBuilder(FormBuilder $formBuilder)
103 {
104 $this->formBuilder = $formBuilder;
105 }
106
107 /**
108 * @param Element $element
109 */
110 public function setElement(Element $element)
111 {
112 $this->element = $element;
113 }
114
115 /**
116 * Set the fluid partial path to the element
117 *
118 * @return void
119 */
120 public function setPartialPaths()
121 {
122 $this->setElementPartialPath();
123 }
124
125 /**
126 * Set the fluid partial path to the element
127 *
128 * @return void
129 */
130 protected function setElementPartialPath()
131 {
132 if (!isset($this->userConfiguredElementTyposcript['partialPath'])) {
133 $partialPath = $this->typoScriptRepository->getDefaultFluidTemplate($this->element->getElementType());
134 } else {
135 $partialPath = $this->userConfiguredElementTyposcript['partialPath'];
136 unset($this->userConfiguredElementTyposcript['partialPath']);
137 }
138 $this->element->setPartialPath($partialPath);
139 }
140
141 /**
142 * Set the fluid partial path to the element
143 *
144 * @return void
145 */
146 public function setVisibility()
147 {
148 $visibility = false;
149 if ($this->formBuilder->getControllerAction() === 'show') {
150 if (!isset($this->userConfiguredElementTyposcript['visibleInShowAction'])) {
151 $visibility = (bool)$this->typoScriptRepository->getModelConfigurationByScope($this->element->getElementType(), 'visibleInShowAction');
152 } else {
153 $visibility = (bool)$this->userConfiguredElementTyposcript['visibleInShowAction'];
154 }
155 } elseif ($this->formBuilder->getControllerAction() === 'confirmation') {
156 if (!isset($this->userConfiguredElementTyposcript['visibleInConfirmationAction'])) {
157 $visibility = (bool)$this->typoScriptRepository->getModelConfigurationByScope($this->element->getElementType(), 'visibleInConfirmationAction');
158 } else {
159 $visibility = (bool)$this->userConfiguredElementTyposcript['visibleInConfirmationAction'];
160 }
161 } elseif ($this->formBuilder->getControllerAction() === 'process') {
162 if (!isset($this->userConfiguredElementTyposcript['visibleInMail'])) {
163 $visibility = (bool)$this->typoScriptRepository->getModelConfigurationByScope($this->element->getElementType(), 'visibleInMail');
164 } else {
165 $visibility = (bool)$this->userConfiguredElementTyposcript['visibleInMail'];
166 }
167 }
168 $this->element->setShowElement($visibility);
169 }
170
171 /**
172 * Find all prefix-* attributes and return the
173 * found prefixs. Than delete them from the htmlAttributes array
174 *
175 * @return void
176 */
177 public function setHtmlAttributeWildcards()
178 {
179 foreach ($this->htmlAttributes as $attributeName => $attributeValue) {
180 if (strpos($attributeName, '-*') > 0) {
181 $prefix = substr($attributeName, 0, -1);
182 $this->wildcardPrefixes[] = $prefix;
183 unset($this->htmlAttributes[$attributeName]);
184 }
185 }
186 }
187
188 /**
189 * Overlay user defined html attribute values
190 * To determine whats a html attribute, the htmlAttributes
191 * array is used. If a html attribute value is found in userConfiguredElementTyposcript
192 * this value is set to htmlAttributes and removed from userConfiguredElementTyposcript.
193 *
194 * @return void
195 */
196 public function overlayUserdefinedHtmlAttributeValues()
197 {
198 foreach ($this->htmlAttributes as $attributeName => $attributeValue) {
199 $attributeNameWithoutDot = rtrim($attributeName, '.');
200 $attributeNameToSet = $attributeNameWithoutDot;
201 $rendered = false;
202 /* If the attribute exists in the user configured typoscript */
203 if ($this->arrayKeyExists($attributeName, $this->userConfiguredElementTyposcript)) {
204 if ($this->formBuilder->getConfiguration()->getCompatibility()) {
205 $newAttributeName = $this->formBuilder->getCompatibilityService()->getNewAttributeName(
206 $this->element->getElementType(),
207 $attributeNameWithoutDot
208 );
209 /* Should the attribute be renamed? */
210 if ($newAttributeName !== $attributeNameWithoutDot) {
211 $attributeNameToSet = $newAttributeName;
212 /* If the renamed attribute already exists in the user configured typoscript */
213 if ($this->arrayKeyExists($newAttributeName, $this->userConfiguredElementTyposcript)) {
214 $attributeValue = $this->formBuilder->getFormUtility()->renderItem(
215 $this->userConfiguredElementTyposcript[$newAttributeName . '.'],
216 $this->userConfiguredElementTyposcript[$newAttributeName]
217 );
218 /* set renamed attribute name with the value of the renamed attribute */
219 $this->htmlAttributes[$newAttributeName] = $attributeValue;
220 /* unset the renamed attribute */
221 unset($this->userConfiguredElementTyposcript[$newAttributeName . '.']);
222 unset($this->userConfiguredElementTyposcript[$newAttributeName]);
223 $rendered = true;
224 }
225 }
226 }
227 }
228 if ($rendered === false) {
229 if ($this->arrayKeyExists($attributeNameWithoutDot, $this->userConfiguredElementTyposcript)) {
230 $attributeValue = $this->formBuilder->getFormUtility()->renderItem(
231 $this->userConfiguredElementTyposcript[$attributeNameWithoutDot . '.'],
232 $this->userConfiguredElementTyposcript[$attributeNameWithoutDot]
233 );
234 $this->htmlAttributes[$attributeNameToSet] = $attributeValue;
235 }
236 }
237 unset($this->userConfiguredElementTyposcript[$attributeNameWithoutDot . '.']);
238 unset($this->userConfiguredElementTyposcript[$attributeNameWithoutDot]);
239 }
240
241 // the prefix-* magic
242 $ignoreKeys = array();
243 foreach ($this->userConfiguredElementTyposcript as $attributeName => $attributeValue) {
244 // ignore child elements
245 if (
246 MathUtility::canBeInterpretedAsInteger($attributeName)
247 || isset($ignoreKeys[$attributeName])
248 ) {
249 $ignoreKeys[$attributeName . '.'] = true;
250 continue;
251 }
252
253 foreach ($this->wildcardPrefixes as $wildcardPrefix) {
254 if (strpos($attributeName, $wildcardPrefix) !== 0) {
255 continue;
256 }
257 $attributeNameWithoutDot = rtrim($attributeName, '.');
258 $attributeValue = $this->formBuilder->getFormUtility()->renderItem(
259 $this->userConfiguredElementTyposcript[$attributeNameWithoutDot . '.'],
260 $this->userConfiguredElementTyposcript[$attributeNameWithoutDot]
261 );
262 $this->htmlAttributes[$attributeNameWithoutDot] = $attributeValue;
263 $ignoreKeys[$attributeNameWithoutDot . '.'] = true;
264 unset($this->userConfiguredElementTyposcript[$attributeNameWithoutDot . '.']);
265 unset($this->userConfiguredElementTyposcript[$attributeNameWithoutDot]);
266 break;
267 }
268 }
269 }
270
271 /**
272 * If fixedHtmlAttributeValues are defined for this element
273 * then overwrite the html attribute value
274 *
275 * @return void
276 */
277 public function overlayFixedHtmlAttributeValues()
278 {
279 $fixedHtmlAttributeValues = $this->typoScriptRepository->getModelConfigurationByScope($this->element->getElementType(), 'fixedHtmlAttributeValues.');
280 if (is_array($fixedHtmlAttributeValues)) {
281 foreach ($fixedHtmlAttributeValues as $attributeName => $attributeValue) {
282 $this->htmlAttributes[$attributeName] = $attributeValue;
283 }
284 }
285 }
286
287 /**
288 * Move htmlAttributes to additionalArguments that must be passed
289 * as a view helper argument
290 *
291 * @return void
292 */
293 public function moveHtmlAttributesToAdditionalArguments()
294 {
295 $htmlAttributesUsedByTheViewHelperDirectly = $this->typoScriptRepository->getModelConfigurationByScope($this->element->getElementType(), 'htmlAttributesUsedByTheViewHelperDirectly.');
296 if (is_array($htmlAttributesUsedByTheViewHelperDirectly)) {
297 foreach ($htmlAttributesUsedByTheViewHelperDirectly as $attributeName) {
298 if (array_key_exists($attributeName, $this->htmlAttributes)) {
299 $this->additionalArguments[$attributeName] = $this->htmlAttributes[$attributeName];
300 unset($this->htmlAttributes[$attributeName]);
301 }
302 }
303 }
304 }
305
306 /**
307 * Set the viewhelper default arguments in the additionalArguments array
308 *
309 * @return void
310 */
311 public function setViewHelperDefaulArgumentsToAdditionalArguments()
312 {
313 $viewHelperDefaultArguments = $this->typoScriptRepository->getModelConfigurationByScope($this->element->getElementType(), 'viewHelperDefaultArguments.');
314 if (is_array($viewHelperDefaultArguments)) {
315 foreach ($viewHelperDefaultArguments as $viewHelperDefaulArgumentName => $viewHelperDefaulArgumentValue) {
316 $viewHelperDefaulArgumentNameWithoutDot = rtrim($viewHelperDefaulArgumentName, '.');
317 $this->additionalArguments[$viewHelperDefaulArgumentNameWithoutDot] = $viewHelperDefaulArgumentValue;
318 }
319 }
320 unset($this->userConfiguredElementTyposcript['viewHelperDefaultArguments']);
321 }
322
323 /**
324 * Move all userdefined properties to the additionalArguments
325 * array. Ignore the child elements
326 *
327 * @return void
328 */
329 public function moveAllOtherUserdefinedPropertiesToAdditionalArguments()
330 {
331 $viewHelperDefaultArguments = $this->typoScriptRepository->getModelConfigurationByScope($this->element->getElementType(), 'viewHelperDefaultArguments.');
332 $ignoreKeys = array();
333
334 foreach ($this->userConfiguredElementTyposcript as $attributeName => $attributeValue) {
335 // ignore child elements
336 if (
337 MathUtility::canBeInterpretedAsInteger($attributeName)
338 || isset($ignoreKeys[$attributeName])
339 || $attributeName == 'postProcessor.'
340 || $attributeName == 'rules.'
341 || $attributeName == 'filters.'
342 || $attributeName == 'layout'
343 ) {
344 $ignoreKeys[$attributeName . '.'] = true;
345 continue;
346 }
347 $attributeNameWithoutDot = rtrim($attributeName, '.');
348 $attributeNameToSet = $attributeNameWithoutDot;
349 $rendered = false;
350 if ($this->formBuilder->getConfiguration()->getCompatibility()) {
351 $newAttributeName = $this->formBuilder->getCompatibilityService()->getNewAttributeName(
352 $this->element->getElementType(),
353 $attributeNameWithoutDot
354 );
355 /* Should the attribute be renamed? */
356 if ($newAttributeName !== $attributeNameWithoutDot) {
357 $attributeNameToSet = $newAttributeName;
358 /* If the renamed attribute already exists in the user configured typoscript */
359 if ($this->arrayKeyExists($newAttributeName, $this->userConfiguredElementTyposcript)) {
360 $attributeValue = $this->formBuilder->getFormUtility()->renderItem(
361 $this->userConfiguredElementTyposcript[$newAttributeName . '.'],
362 $this->userConfiguredElementTyposcript[$newAttributeName]
363 );
364 /* set renamed attribute name with the value of the renamed attribute */
365 $this->additionalArguments[$newAttributeName] = $attributeValue;
366 /* unset the renamed attribute */
367 $ignoreKeys[$newAttributeName . '.'] = true;
368 $ignoreKeys[$newAttributeName] = true;
369 unset($this->userConfiguredElementTyposcript[$newAttributeName . '.']);
370 unset($this->userConfiguredElementTyposcript[$newAttributeName]);
371 $rendered = true;
372 }
373 }
374 }
375 if ($rendered === false) {
376 if (
377 isset($viewHelperDefaultArguments[$attributeName])
378 || isset($viewHelperDefaultArguments[$attributeNameWithoutDot])
379 ) {
380 $this->formBuilder->getFormUtility()->renderArrayItems($attributeValue);
381 $attributeValue = $this->typoScriptService->convertTypoScriptArrayToPlainArray($attributeValue);
382 } else {
383 $attributeValue = $this->formBuilder->getFormUtility()->renderItem(
384 $this->userConfiguredElementTyposcript[$attributeNameWithoutDot . '.'],
385 $this->userConfiguredElementTyposcript[$attributeNameWithoutDot]
386 );
387 }
388 $this->additionalArguments[$attributeNameToSet] = $attributeValue;
389 $ignoreKeys[$attributeNameToSet . '.'] = true;
390 $ignoreKeys[$attributeNameToSet] = true;
391 }
392 unset($this->userConfiguredElementTyposcript[$attributeNameWithoutDot . '.']);
393 unset($this->userConfiguredElementTyposcript[$attributeNameWithoutDot]);
394 }
395 // remove "stdWrap." from "additionalArguments" on
396 // the FORM Element
397 if (
398 !$this->formBuilder->getConfiguration()->getContentElementRendering()
399 && $this->element->getElementType() == 'FORM'
400 ) {
401 unset($this->additionalArguments['stdWrap']);
402 unset($this->additionalArguments['stdWrap.']);
403 }
404 }
405
406 /**
407 * Set the name and id attribute
408 *
409 * @return array
410 */
411 public function setNameAndId()
412 {
413 if (
414 $this->element->getParentElement()
415 && (int)$this->typoScriptRepository->getModelConfigurationByScope($this->element->getParentElement()->getElementType(), 'childrenInheritName') == 1
416 ) {
417 $this->htmlAttributes['name'] = $this->element->getParentElement()->getName();
418 $this->additionalArguments['multiple'] = '1';
419 $name = $this->sanitizeNameAttribute($this->userConfiguredElementTyposcript['name']);
420 $this->element->setName($name);
421 } else {
422 $this->htmlAttributes['name'] = $this->sanitizeNameAttribute($this->htmlAttributes['name']);
423 $this->element->setName($this->htmlAttributes['name']);
424 }
425 $this->htmlAttributes['id'] = $this->sanitizeIdAttribute($this->htmlAttributes['id']);
426 $this->element->setId($this->htmlAttributes['id']);
427 }
428
429 /**
430 * If the name is not defined it is automatically generated
431 * using the following syntax: id-{element_counter}
432 * The name attribute will be transformed if it contains some
433 * non allowed characters:
434 * - spaces are changed into hyphens
435 * - remove all characters except a-z A-Z 0-9 _ -
436 *
437 * @param string $name
438 * @return string
439 */
440 public function sanitizeNameAttribute($name)
441 {
442 $name = $this->formBuilder->getFormUtility()->sanitizeNameAttribute($name);
443 if (empty($name)) {
444 $name = 'id-' . $this->element->getElementCounter();
445 }
446 return $name;
447 }
448
449 /**
450 * If the id is not defined it is automatically generated
451 * using the following syntax: field-{element_counter}
452 * The id attribute will be transformed if it contains some
453 * non allowed characters:
454 * - spaces are changed into hyphens
455 * - if the id start with a integer then transform it to field-{integer}
456 * - remove all characters expect a-z A-Z 0-9 _ - : .
457 *
458 * @param string $id
459 * @return string
460 */
461 protected function sanitizeIdAttribute($id)
462 {
463 $id = $this->formBuilder->getFormUtility()->sanitizeIdAttribute($id);
464 if (empty($id)) {
465 $id = 'field-' . $this->element->getElementCounter();
466 }
467 return $id;
468 }
469
470 /**
471 * Check if a needle exists in a array.
472 *
473 * @param string $needle
474 * @param array $haystack
475 * @return bool TRUE if found
476 */
477 protected function arrayKeyExists($needle, array $haystack = array())
478 {
479 return (
480 isset($haystack[$needle]) || isset($haystack[$needle . '.'])
481 );
482 }
483
484 /**
485 * Get the current html attributes
486 *
487 * @return array
488 */
489 public function getHtmlAttributes()
490 {
491 return $this->htmlAttributes;
492 }
493
494 /**
495 * Set the current html attributes
496 *
497 * @param array $htmlAttributes
498 */
499 public function setHtmlAttributes(array $htmlAttributes)
500 {
501 $this->htmlAttributes = $htmlAttributes;
502 }
503
504 /**
505 * Get the current additional arguments
506 *
507 * @return array
508 */
509 public function getAdditionalArguments()
510 {
511 return $this->additionalArguments;
512 }
513
514 /**
515 * Set the current additional arguments
516 *
517 * @param array $additionalArguments
518 */
519 public function setAdditionalArguments(array $additionalArguments)
520 {
521 $this->additionalArguments = $additionalArguments;
522 }
523
524 /**
525 * Get the current wildcard prefixes
526 *
527 * @return array
528 */
529 public function getWildcardPrefixes()
530 {
531 return $this->wildcardPrefixes;
532 }
533
534 /**
535 * Set the current wildcard prefixes
536 *
537 * @param array $wildcardPrefixes
538 */
539 public function setWildcardPrefixes(array $wildcardPrefixes)
540 {
541 $this->wildcardPrefixes = $wildcardPrefixes;
542 }
543
544 /**
545 * Get the current Element
546 *
547 * @return array
548 */
549 public function getUserConfiguredElementTyposcript()
550 {
551 return $this->userConfiguredElementTyposcript;
552 }
553
554 /**
555 * Set the current Element
556 *
557 * @param array $userConfiguredElementTyposcript
558 */
559 public function setUserConfiguredElementTyposcript(array $userConfiguredElementTyposcript)
560 {
561 $this->userConfiguredElementTyposcript = $userConfiguredElementTyposcript;
562 }
563 }