[TASK] EXT:form - Set proper default values in form wizard
[Packages/TYPO3.CMS.git] / typo3 / sysext / form / Classes / Domain / Factory / JsonToTypoScript.php
1 <?php
2 namespace TYPO3\CMS\Form\Domain\Factory;
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 /**
18 * Json to Typoscript converter
19 *
20 * Takes the incoming Json and converts it to Typoscipt
21 */
22 class JsonToTypoScript
23 {
24 /**
25 * Internal counter for the elements
26 *
27 * @var int
28 */
29 protected $elementId = 0;
30
31 /**
32 * Storage for the validation rules
33 * In TypoScript they are set in the form, in the wizard on the elements
34 *
35 * @var array
36 */
37 protected $validationRules = array();
38
39 /**
40 * Internal counter for the validation rules
41 *
42 * @var int
43 */
44 protected $validationRulesCounter = 1;
45
46 /**
47 * Convert JSON to TypoScript
48 *
49 * First a TypoScript array is constructed,
50 * which will be converted to a formatted string
51 *
52 * @param string $json Json containing all configuration for the form
53 * @return string The typoscript for the form
54 */
55 public function convert($json)
56 {
57 $elements = json_decode((string)$json, true);
58 $typoscriptArray = array();
59 $typoscript = null;
60 $this->convertToTyposcriptArray($elements, $typoscriptArray);
61 if ($typoscriptArray['10.'] && is_array($typoscriptArray['10.']) && !empty($typoscriptArray['10.'])) {
62 $typoscript = $this->typoscriptArrayToString($typoscriptArray['10.']);
63 }
64 return $typoscript;
65 }
66
67 /**
68 * Converts the JSON array for each element to a TypoScript array
69 * and adds this Typoscript array to the parent
70 *
71 * @param array $elements The JSON array
72 * @param array $parent The parent element
73 * @param bool $childrenWithParentName Indicates if the children use the parent name
74 * @return void
75 */
76 protected function convertToTyposcriptArray(array $elements, array &$parent, $childrenWithParentName = false)
77 {
78 if (is_array($elements)) {
79 $elementCounter = 10;
80 foreach ($elements as $element) {
81 if ($element['xtype']) {
82 $this->elementId++;
83 switch ($element['xtype']) {
84 case 'typo3-form-wizard-elements-basic-button':
85
86 case 'typo3-form-wizard-elements-basic-checkbox':
87
88 case 'typo3-form-wizard-elements-basic-fileupload':
89
90 case 'typo3-form-wizard-elements-basic-hidden':
91
92 case 'typo3-form-wizard-elements-basic-password':
93
94 case 'typo3-form-wizard-elements-basic-radio':
95
96 case 'typo3-form-wizard-elements-basic-reset':
97
98 case 'typo3-form-wizard-elements-basic-select':
99
100 case 'typo3-form-wizard-elements-basic-submit':
101
102 case 'typo3-form-wizard-elements-basic-textarea':
103
104 case 'typo3-form-wizard-elements-basic-textline':
105
106 case 'typo3-form-wizard-elements-predefined-email':
107
108 case 'typo3-form-wizard-elements-content-header':
109
110 case 'typo3-form-wizard-elements-content-textblock':
111 $this->getDefaultElementSetup($element, $parent, $elementCounter, $childrenWithParentName);
112 break;
113 case 'typo3-form-wizard-elements-basic-fieldset':
114
115 case 'typo3-form-wizard-elements-predefined-name':
116 $this->getDefaultElementSetup($element, $parent, $elementCounter);
117 $this->getContainer($element, $parent, $elementCounter);
118 break;
119 case 'typo3-form-wizard-elements-predefined-checkboxgroup':
120
121 case 'typo3-form-wizard-elements-predefined-radiogroup':
122 $this->getDefaultElementSetup($element, $parent, $elementCounter);
123 $this->getContainer($element, $parent, $elementCounter, true);
124 break;
125 case 'typo3-form-wizard-elements-basic-form':
126 $this->getDefaultElementSetup($element, $parent, $elementCounter);
127 $this->getContainer($element, $parent, $elementCounter);
128 $this->getForm($element, $parent, $elementCounter);
129 break;
130 default:
131
132 }
133 }
134 $elementCounter = $elementCounter + 10;
135 }
136 }
137 }
138
139 /**
140 * Called for elements are a container for other elements like FORM and FIELDSET
141 *
142 * @param array $element The JSON array for this element
143 * @param array $parent The parent element
144 * @param bool $childrenWithParentName Indicates if the children use the parent name
145 * @param int $elementCounter The element counter
146 * @return void
147 */
148 protected function getContainer(array $element, array &$parent, $elementCounter, $childrenWithParentName = false)
149 {
150 if ($element['elementContainer'] && $element['elementContainer']['items']) {
151 $this->convertToTyposcriptArray($element['elementContainer']['items'], $parent[$elementCounter . '.'], $childrenWithParentName);
152 }
153 }
154
155 /**
156 * Only called for the type FORM
157 *
158 * Adds the validation rules to the form. In the wizard they are added to
159 * each element. In this script the validation rules are stored in a
160 * separate array to add them to the form at a later point.
161 *
162 * @param array $element The JSON array for this element
163 * @param array $parent The parent element
164 * @param int $elementCounter The element counter
165 * @return void
166 */
167 protected function getForm(array $element, array &$parent, $elementCounter)
168 {
169 // @todo Put at the top of the form
170 if (!empty($this->validationRules)) {
171 $parent[$elementCounter . '.']['rules'] = $this->validationRules;
172 }
173 }
174
175 /**
176 * Called for each element
177 *
178 * Adds the content object type to the parent array and starts adding the
179 * configuration for the element
180 *
181 * @param array $element The JSON array for this element
182 * @param array $parent The parent element
183 * @param int $elementCounter The element counter
184 * @param bool $childrenWithParentName Indicates if the children use the parent name
185 * @return void
186 */
187 protected function getDefaultElementSetup(array $element, array &$parent, $elementCounter, $childrenWithParentName = false)
188 {
189 $contentObjectType = $this->getContentObjectType($element);
190 if (is_null($contentObjectType) === false) {
191 $parent[$elementCounter] = $contentObjectType;
192 $parent[$elementCounter . '.'] = array();
193 if ($element['configuration']) {
194 $this->setConfiguration($element, $parent, $elementCounter, $childrenWithParentName);
195 }
196 }
197 }
198
199 /**
200 * Returns the content object type which is related to the ExtJS xtype
201 *
202 * @param array $element The JSON array for this element
203 * @return string The Content Object Type
204 */
205 protected function getContentObjectType(array $element)
206 {
207 $contentObjectType = null;
208 $shortXType = str_replace('typo3-form-wizard-elements-', '', $element['xtype']);
209 list($category, $type) = explode('-', $shortXType);
210 switch ($category) {
211 case 'basic':
212 $contentObjectType = strtoupper($type);
213 break;
214 case 'predefined':
215 switch ($type) {
216 case 'checkboxgroup':
217
218 case 'radiogroup':
219 $contentObjectType = strtoupper($type);
220 break;
221 case 'email':
222 $contentObjectType = 'TEXTLINE';
223 break;
224 case 'name':
225 $contentObjectType = 'FIELDSET';
226 }
227 break;
228 case 'content':
229 switch ($type) {
230 case 'header':
231
232 case 'textblock':
233 $contentObjectType = strtoupper($type);
234 }
235 default:
236
237 }
238 return $contentObjectType;
239 }
240
241 /**
242 * Iterates over the various configuration settings and calls the
243 * appropriate function for each setting
244 *
245 * @param array $element The JSON array for this element
246 * @param array $parent The parent element
247 * @param int $elementCounter The element counter
248 * @param bool $childrenWithParentName Indicates if the children use the parent name
249 * @return void
250 */
251 protected function setConfiguration(array $element, array &$parent, $elementCounter, $childrenWithParentName = false)
252 {
253 foreach ($element['configuration'] as $key => $value) {
254 switch ($key) {
255 case 'attributes':
256 $this->setAttributes($value, $parent, $elementCounter, $childrenWithParentName);
257 break;
258 case 'confirmation':
259 $this->setConfirmation($value, $parent, $elementCounter);
260 break;
261 case 'filters':
262 $this->setFilters($value, $parent, $elementCounter);
263 break;
264 case 'label':
265 $this->setLabel($value, $parent, $elementCounter);
266 break;
267 case 'layout':
268 $this->setLayout($element, $value, $parent, $elementCounter);
269 break;
270 case 'legend':
271 $this->setLegend($value, $parent, $elementCounter);
272 break;
273 case 'options':
274 $this->setOptions($element, $value, $parent, $elementCounter);
275 break;
276 case 'postProcessor':
277 $this->setPostProcessor($value, $parent, $elementCounter);
278 break;
279 case 'prefix':
280 $this->setPrefix($value, $parent, $elementCounter);
281 break;
282 case 'validation':
283 $this->setValidationRules($element, $value);
284 break;
285 case 'various':
286 $this->setVarious($element, $value, $parent, $elementCounter);
287 break;
288 default:
289
290 }
291 }
292 }
293
294 /**
295 * Set the attributes for the element
296 *
297 * @param array $attributes The JSON array for the attributes of this element
298 * @param array $parent The parent element
299 * @param int $elementCounter The element counter
300 * @param bool $childrenWithParentName Indicates if the children use the parent name
301 * @return void
302 */
303 protected function setAttributes(array $attributes, array &$parent, $elementCounter, $childrenWithParentName = false)
304 {
305 foreach ($attributes as $key => $value) {
306 if ($key === 'name' && $value === '' && !$childrenWithParentName) {
307 $value = $this->elementId;
308 }
309 if ($value != '') {
310 $parent[$elementCounter . '.'][$key] = $value;
311 }
312 }
313 }
314
315 /**
316 * Set the confirmation for the element FORM
317 *
318 * The confirmation indicates a confirmation screen has to be displayed
319 *
320 * @param bool $confirmation TRUE when confirmation screen
321 * @param array $parent The parent element
322 * @param int $elementCounter The element counter
323 * @return void
324 */
325 protected function setConfirmation($confirmation, array &$parent, $elementCounter)
326 {
327 $parent[$elementCounter . '.']['confirmation'] = $confirmation;
328 }
329
330 /**
331 * Set the filters for the element
332 *
333 * @param array $filters The JSON array for the filters of this element
334 * @param array $parent The parent element
335 * @param int $elementCounter The element counter
336 * @return void
337 */
338 protected function setFilters(array $filters, array &$parent, $elementCounter)
339 {
340 if (!empty($filters)) {
341 $parent[$elementCounter . '.']['filters'] = array();
342 $filterCounter = 1;
343 foreach ($filters as $name => $filterConfiguration) {
344 $parent[$elementCounter . '.']['filters'][$filterCounter] = $name;
345 $parent[$elementCounter . '.']['filters'][$filterCounter . '.'] = $filterConfiguration;
346 $filterCounter++;
347 }
348 }
349 }
350
351 /**
352 * Set the label for the element
353 *
354 * @param array $label The JSON array for the label of this element
355 * @param array $parent The parent element
356 * @param int $elementCounter The element counter
357 * @return void
358 */
359 protected function setLabel(array $label, array &$parent, $elementCounter)
360 {
361 if ($label['value'] != '') {
362 $parent[$elementCounter . '.']['label.']['value'] = $label['value'];
363 }
364 }
365
366 /**
367 * Changes the layout of some elements when this has been set in the wizard
368 *
369 * The wizard only uses 'back' or 'front' to set the layout. The TypoScript
370 * of the form uses a XML notation for the position of the label to the
371 * field.
372 *
373 * @param array $element The JSON array for this element
374 * @param string $value The layout setting, back or front
375 * @param array $parent The parent element
376 * @param int $elementCounter The element counter
377 * @return void
378 */
379 protected function setLayout(array $element, $value, array &$parent, $elementCounter)
380 {
381 switch ($element['xtype']) {
382 case 'typo3-form-wizard-elements-basic-button':
383
384 case 'typo3-form-wizard-elements-basic-fileupload':
385
386 case 'typo3-form-wizard-elements-basic-password':
387
388 case 'typo3-form-wizard-elements-basic-reset':
389
390 case 'typo3-form-wizard-elements-basic-submit':
391
392 case 'typo3-form-wizard-elements-basic-textline':
393 if ($value === 'back') {
394 $parent[$elementCounter . '.']['layout'] = '<input />' . LF . '<label />';
395 }
396 break;
397 case 'typo3-form-wizard-elements-basic-checkbox':
398
399 case 'typo3-form-wizard-elements-basic-radio':
400 if ($value === 'front') {
401 $parent[$elementCounter . '.']['layout'] = '<label />' . LF . '<input />';
402 }
403 break;
404 case 'typo3-form-wizard-elements-basic-select':
405 if ($value === 'back') {
406 $parent[$elementCounter . '.']['layout'] = '<select>' . LF . '<elements />' . LF . '</select>' . LF . '<label />';
407 }
408 break;
409 case 'typo3-form-wizard-elements-basic-textarea':
410 if ($value === 'back') {
411 $parent[$elementCounter . '.']['layout'] = '<textarea />' . LF . '<label />';
412 }
413 break;
414 default:
415
416 }
417 }
418
419 /**
420 * Set the legend for the element
421 *
422 * @param array $legend The JSON array for the legend of this element
423 * @param array $parent The parent element
424 * @param int $elementCounter The element counter
425 * @return void
426 */
427 protected function setLegend(array $legend, array &$parent, $elementCounter)
428 {
429 if ($legend['value'] != '') {
430 $parent[$elementCounter . '.']['legend.']['value'] = $legend['value'];
431 }
432 }
433
434 /**
435 * Set the options for a SELECT
436 *
437 * Although other elements like CHECKBOXGROUP and RADIOGROUP are using the
438 * option setting as well, they act like containers and are handled
439 * differently
440 *
441 * @param array $element The JSON array for this element
442 * @param array $options The JSON array for the options of this element
443 * @param array $parent The parent element
444 * @param int $elementCounter The element counter
445 * @return void
446 */
447 protected function setOptions(array $element, array $options, array &$parent, $elementCounter)
448 {
449 if (is_array($options) && $element['xtype'] === 'typo3-form-wizard-elements-basic-select') {
450 $optionCounter = 10;
451 foreach ($options as $option) {
452 $parent[$elementCounter . '.'][$optionCounter] = 'OPTION';
453 $parent[$elementCounter . '.'][$optionCounter . '.']['data'] = $option['data'];
454 if (isset($option['attributes'])) {
455 $parent[$elementCounter . '.'][$optionCounter . '.'] += $option['attributes'];
456 }
457 $optionCounter = $optionCounter + 10;
458 }
459 }
460 }
461
462 /**
463 * Set the post processors for the element
464 *
465 * @param array $postProcessors The JSON array for the post processors of this element
466 * @param array $parent The parent element
467 * @param int $elementCounter The element counter
468 * @return void
469 */
470 protected function setPostProcessor(array $postProcessors, array &$parent, $elementCounter)
471 {
472 if (!empty($postProcessors)) {
473 $parent[$elementCounter . '.']['postProcessor'] = array();
474 $postProcessorCounter = 1;
475 foreach ($postProcessors as $name => $postProcessorConfiguration) {
476 $parent[$elementCounter . '.']['postProcessor'][$postProcessorCounter] = $name;
477 $parent[$elementCounter . '.']['postProcessor'][$postProcessorCounter . '.'] = $postProcessorConfiguration;
478 $postProcessorCounter++;
479 }
480 }
481 }
482
483 /**
484 * Set the prefix for the element FORM
485 *
486 * The prefix will be used in the names of all elements in the form
487 *
488 * @param string $prefix The prefix for all element names
489 * @param array $parent The parent element
490 * @param int $elementCounter The element counter
491 * @return void
492 */
493 protected function setPrefix($prefix, array &$parent, $elementCounter)
494 {
495 $parent[$elementCounter . '.']['prefix'] = $prefix;
496 }
497
498 /**
499 * Stores the validation rules, set to the elements, in a temporary array
500 *
501 * In the wizard the validation rules are added to the element,
502 * in TypoScript they are added to the form.
503 *
504 * @param array $element The JSON array for this element
505 * @param array $validationRules The temporary storage array for the rules
506 * @return void
507 */
508 protected function setValidationRules(array $element, array $validationRules)
509 {
510 foreach ($validationRules as $name => $ruleConfiguration) {
511 if (isset($element['configuration']['attributes']['name']) && $element['configuration']['attributes']['name'] != '') {
512 $ruleConfiguration['element'] = $element['configuration']['attributes']['name'];
513 } elseif (isset($element['configuration']['various']['name']) && $element['configuration']['various']['name'] != '') {
514 $ruleConfiguration['element'] = $element['configuration']['various']['name'];
515 } else {
516 $ruleConfiguration['element'] = $this->elementId;
517 }
518 $this->validationRules[$this->validationRulesCounter] = $name;
519 $this->validationRules[$this->validationRulesCounter . '.'] = $ruleConfiguration;
520 $this->validationRulesCounter++;
521 }
522 }
523
524 /**
525 * Set the various configuration of an element
526 *
527 * @param array $element The JSON array for this element
528 * @param array $various The JSON array for the various options of this element
529 * @param array $parent The parent element
530 * @param int $elementCounter The element counter
531 * @return void
532 */
533 protected function setVarious(array $element, array $various, array &$parent, $elementCounter)
534 {
535 foreach ($various as $key => $value) {
536 switch ($key) {
537 case 'headingSize':
538
539 case 'content':
540
541 case 'name':
542 $parent[$elementCounter . '.'][$key] = (string)$value;
543 break;
544 }
545 }
546 }
547
548 /**
549 * Converts a TypoScript array to a formatted string
550 *
551 * Takes care of indentation, curly brackets and parentheses
552 *
553 * @param array $typoscriptArray The TypoScript array
554 * @param string $addKey Key which has underlying configuration
555 * @param int $tabCount The amount of tabs for indentation
556 * @return string The formatted TypoScript string
557 */
558 protected function typoscriptArrayToString(array $typoscriptArray, $addKey = '', $tabCount = -1)
559 {
560 $typoscript = '';
561 if ($addKey != '') {
562 $typoscript .= str_repeat(TAB, $tabCount) . str_replace('.', '', $addKey) . ' {' . LF;
563 }
564 $tabCount++;
565 foreach ($typoscriptArray as $key => $value) {
566 if (!is_array($value)) {
567 if (strstr($value, LF)) {
568 $typoscript .= str_repeat(TAB, $tabCount) . $key . ' (' . LF;
569 $value = str_replace(LF, LF . str_repeat(TAB, ($tabCount + 1)), $value);
570 $typoscript .= str_repeat(TAB, ($tabCount + 1)) . $value . LF;
571 $typoscript .= str_repeat(TAB, $tabCount) . ')' . LF;
572 } else {
573 $typoscript .= str_repeat(TAB, $tabCount) . $key . ' = ' . $value . LF;
574 }
575 } else {
576 $typoscript .= $this->typoscriptArrayToString($value, $key, $tabCount);
577 }
578 }
579 if ($addKey != '') {
580 $tabCount--;
581 $typoscript .= str_repeat(TAB, $tabCount) . '}' . LF;
582 }
583 return $typoscript;
584 }
585 }