[BUGFIX] Do not manipulate minitems/maxitems
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Form / AbstractNode.php
1 <?php
2 namespace TYPO3\CMS\Backend\Form;
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\ArrayUtility;
18 use TYPO3\CMS\Core\Utility\GeneralUtility;
19
20 /**
21 * Base class for container and single elements - their abstracts extend from here.
22 */
23 abstract class AbstractNode implements NodeInterface
24 {
25 /**
26 * Main data array to work on, given from parent to child elements
27 *
28 * @var array
29 */
30 protected $data = array();
31
32 /**
33 * Set data to data array.
34 *
35 * @todo: Should NOT set the nodeFactory instance, this is done by AbstractContainer only,
36 * @todo: but not done for Element classes: Elements are tree leaves, they MUST
37 * @todo: not create new nodes again.
38 * @todo: Currently, AbstractFormElement still does that, but do not rely on the fact that
39 * @todo: Element classes have an instance of NodeFactory at hand.
40 *
41 * @param NodeFactory $nodeFactory
42 * @param array $data
43 */
44 public function __construct(NodeFactory $nodeFactory, array $data)
45 {
46 $this->data = $data;
47 }
48
49 /**
50 * Handler for single nodes
51 *
52 * @return array As defined in initializeResultArray() of AbstractNode
53 */
54 abstract public function render();
55
56 /**
57 * Initialize the array that is returned to parent after calling. This structure
58 * is identical for *all* nodes. Parent will merge the return of a child with its
59 * own stuff and in itself return an array of the same structure.
60 *
61 * @return array
62 */
63 protected function initializeResultArray()
64 {
65 return array(
66 'additionalJavaScriptPost' => array(),
67 'additionalJavaScriptSubmit' => array(),
68 'additionalHiddenFields' => array(),
69 'additionalInlineLanguageLabelFiles' => array(),
70 'stylesheetFiles' => array(),
71 // can hold strings or arrays, string = requireJS module, array = requireJS module + callback e.g. array('TYPO3/Foo/Bar', 'function() {}')
72 'requireJsModules' => array(),
73 'extJSCODE' => '',
74 'inlineData' => array(),
75 'html' => '',
76 );
77 }
78
79 /**
80 * Merge existing data with a child return array
81 *
82 * @param array $existing Currently merged array
83 * @param array $childReturn Array returned by child
84 * @return array Result array
85 */
86 protected function mergeChildReturnIntoExistingResult(array $existing, array $childReturn)
87 {
88 if (!empty($childReturn['html'])) {
89 $existing['html'] .= LF . $childReturn['html'];
90 }
91 if (!empty($childReturn['extJSCODE'])) {
92 $existing['extJSCODE'] .= LF . $childReturn['extJSCODE'];
93 }
94 foreach ($childReturn['additionalJavaScriptPost'] as $value) {
95 $existing['additionalJavaScriptPost'][] = $value;
96 }
97 foreach ($childReturn['additionalJavaScriptSubmit'] as $value) {
98 $existing['additionalJavaScriptSubmit'][] = $value;
99 }
100 foreach ($childReturn['additionalHiddenFields'] as $value) {
101 $existing['additionalHiddenFields'][] = $value;
102 }
103 foreach ($childReturn['stylesheetFiles'] as $value) {
104 $existing['stylesheetFiles'][] = $value;
105 }
106 if (!empty($childReturn['requireJsModules'])) {
107 foreach ($childReturn['requireJsModules'] as $module) {
108 $existing['requireJsModules'][] = $module;
109 }
110 }
111 if (!empty($childReturn['additionalInlineLanguageLabelFiles'])) {
112 foreach ($childReturn['additionalInlineLanguageLabelFiles'] as $inlineLanguageLabelFile) {
113 $existing['additionalInlineLanguageLabelFiles'][] = $inlineLanguageLabelFile;
114 }
115 }
116 if (!empty($childReturn['inlineData'])) {
117 $existingInlineData = $existing['inlineData'];
118 $childInlineData = $childReturn['inlineData'];
119 ArrayUtility::mergeRecursiveWithOverrule($existingInlineData, $childInlineData);
120 $existing['inlineData'] = $existingInlineData;
121 }
122 return $existing;
123 }
124
125 /**
126 * Build JSON string for validations rules and return it
127 * as data attribute for HTML elements.
128 *
129 * @param array $config
130 * @return string
131 */
132 protected function getValidationDataAsDataAttribute(array $config)
133 {
134 return sprintf(' data-formengine-validation-rules="%s" ', htmlspecialchars($this->getValidationDataAsJsonString($config)));
135 }
136
137 /**
138 * Build JSON string for validations rules.
139 *
140 * @param array $config
141 * @return string
142 */
143 protected function getValidationDataAsJsonString(array $config)
144 {
145 $validationRules = array();
146 if (!empty($config['eval'])) {
147 $evalList = GeneralUtility::trimExplode(',', $config['eval'], true);
148 unset($config['eval']);
149 foreach ($evalList as $evalType) {
150 $validationRules[] = array(
151 'type' => $evalType,
152 'config' => $config
153 );
154 }
155 }
156 if (!empty($config['range'])) {
157 $validationRules[] = array(
158 'type' => 'range',
159 'config' => $config['range']
160 );
161 }
162 if (!empty($config['maxitems']) || !empty($config['minitems'])) {
163 $minItems = (isset($config['minitems'])) ? (int)$config['minitems'] : 0;
164 $maxItems = (isset($config['maxitems'])) ? (int)$config['maxitems'] : 10000;
165 $type = ($config['type']) ?: 'range';
166 if ($config['type'] === 'select' && $config['renderType'] !== 'selectTree' && $maxItems <= 1 && $minItems > 0) {
167 $validationRules[] = array(
168 'type' => $type,
169 'minItems' => 1,
170 'maxItems' => 100000
171 );
172 } else {
173 $validationRules[] = array(
174 'type' => $type,
175 'minItems' => $minItems,
176 'maxItems' => $maxItems
177 );
178 }
179 }
180 if (!empty($config['required'])) {
181 $validationRules[] = array('type' => 'required');
182 }
183 return json_encode($validationRules);
184 }
185 }