00a94fab5e408a7152d2de453e09f0ef0474b769
[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 $this->data = $data;
46 }
47
48 /**
49 * Handler for single nodes
50 *
51 * @return array As defined in initializeResultArray() of AbstractNode
52 */
53 abstract public function render();
54
55 /**
56 * Initialize the array that is returned to parent after calling. This structure
57 * is identical for *all* nodes. Parent will merge the return of a child with its
58 * own stuff and in itself return an array of the same structure.
59 *
60 * @return array
61 */
62 protected function initializeResultArray() {
63 return array(
64 'additionalJavaScriptPost' => array(),
65 'additionalJavaScriptSubmit' => array(),
66 'additionalHiddenFields' => array(),
67 'stylesheetFiles' => array(),
68 // can hold strings or arrays, string = requireJS module, array = requireJS module + callback e.g. array('TYPO3/Foo/Bar', 'function() {}')
69 'requireJsModules' => array(),
70 'extJSCODE' => '',
71 'inlineData' => array(),
72 'html' => '',
73 );
74 }
75
76 /**
77 * Merge existing data with a child return array
78 *
79 * @param array $existing Currently merged array
80 * @param array $childReturn Array returned by child
81 * @return array Result array
82 */
83 protected function mergeChildReturnIntoExistingResult(array $existing, array $childReturn) {
84 if (!empty($childReturn['html'])) {
85 $existing['html'] .= LF . $childReturn['html'];
86 }
87 if (!empty($childReturn['extJSCODE'])) {
88 $existing['extJSCODE'] .= LF . $childReturn['extJSCODE'];
89 }
90 foreach ($childReturn['additionalJavaScriptPost'] as $value) {
91 $existing['additionalJavaScriptPost'][] = $value;
92 }
93 foreach ($childReturn['additionalJavaScriptSubmit'] as $value) {
94 $existing['additionalJavaScriptSubmit'][] = $value;
95 }
96 foreach ($childReturn['additionalHiddenFields'] as $value) {
97 $existing['additionalHiddenFields'][] = $value;
98 }
99 foreach ($childReturn['stylesheetFiles'] as $value) {
100 $existing['stylesheetFiles'][] = $value;
101 }
102 if (!empty($childReturn['requireJsModules'])) {
103 foreach ($childReturn['requireJsModules'] as $module) {
104 $existing['requireJsModules'][] = $module;
105 }
106 }
107 if (!empty($childReturn['inlineData'])) {
108 $existingInlineData = $existing['inlineData'];
109 $childInlineData = $childReturn['inlineData'];
110 ArrayUtility::mergeRecursiveWithOverrule($existingInlineData, $childInlineData);
111 $existing['inlineData'] = $existingInlineData;
112 }
113 return $existing;
114 }
115
116 /**
117 * Determine and get the value for the placeholder for an input field.
118 * Typically used in an inline relation where values from fields down the record chain
119 * are used as "default" values for fields.
120 *
121 * @param string $table
122 * @param array $config
123 * @param array $row
124 * @return mixed
125 */
126 protected function getPlaceholderValue($table, array $config, array $row) {
127 $value = trim($config['placeholder']);
128 if (!$value) {
129 return '';
130 }
131 // Check if we have a reference to another field value from the current record
132 if (substr($value, 0, 6) === '__row|') {
133 /** @var FormDataTraverser $traverser */
134 $traverseFields = GeneralUtility::trimExplode('|', substr($value, 6));
135 $traverser = GeneralUtility::makeInstance(FormDataTraverser::class);
136 $value = $traverser->getTraversedFieldValue($traverseFields, $table, $row, $this->data['inlineFirstPid']);
137 }
138
139 return $value;
140 }
141
142 /**
143 * Build JSON string for validations rules and return it
144 * as data attribute for HTML elements.
145 *
146 * @param array $config
147 * @return string
148 */
149 protected function getValidationDataAsDataAttribute(array $config) {
150 return sprintf(' data-formengine-validation-rules="%s" ', htmlspecialchars($this->getValidationDataAsJsonString($config)));
151 }
152
153 /**
154 * Build JSON string for validations rules.
155 *
156 * @param array $config
157 * @return string
158 */
159 protected function getValidationDataAsJsonString(array $config) {
160 $validationRules = array();
161 if (!empty($config['eval'])) {
162 $evalList = GeneralUtility::trimExplode(',', $config['eval'], TRUE);
163 unset($config['eval']);
164 foreach ($evalList as $evalType) {
165 $validationRules[] = array(
166 'type' => $evalType,
167 'config' => $config
168 );
169 }
170 }
171 if (!empty($config['range'])) {
172 $validationRules[] = array(
173 'type' => 'range',
174 'config' => $config['range']
175 );
176 }
177 if (!empty($config['maxitems']) || !empty($config['minitems'])) {
178 $minItems = (isset($config['minitems'])) ? (int)$config['minitems'] : 0;
179 $maxItems = (isset($config['maxitems'])) ? (int)$config['maxitems'] : 10000;
180 $type = ($config['type']) ?: 'range';
181 if ($config['renderMode'] !== 'tree' && $maxItems <= 1 && $minItems > 0) {
182 $validationRules[] = array(
183 'type' => $type,
184 'minItems' => 1,
185 'maxItems' => 100000
186 );
187 } else {
188 $validationRules[] = array(
189 'type' => $type,
190 'minItems' => $minItems,
191 'maxItems' => $maxItems
192 );
193 }
194 }
195 if (!empty($config['required'])) {
196 $validationRules[] = array('type' => 'required');
197 }
198 return json_encode($validationRules);
199 }
200
201 }