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