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