[BUGFIX] Placeholder attribute without useOrOverridePlaceholder
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Form / FormEngine.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\Backend\Form\Element\InlineElement;
18 use TYPO3\CMS\Backend\Template\DocumentTemplate;
19 use TYPO3\CMS\Backend\Utility\BackendUtility;
20 use TYPO3\CMS\Backend\Utility\IconUtility;
21 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
22 use TYPO3\CMS\Core\Database\DatabaseConnection;
23 use TYPO3\CMS\Core\Html\HtmlParser;
24 use TYPO3\CMS\Core\Messaging\FlashMessage;
25 use TYPO3\CMS\Core\Messaging\FlashMessageQueue;
26 use TYPO3\CMS\Core\Messaging\FlashMessageService;
27 use TYPO3\CMS\Core\Utility\ArrayUtility;
28 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
29 use TYPO3\CMS\Core\Utility\GeneralUtility;
30 use TYPO3\CMS\Core\Utility\MathUtility;
31 use TYPO3\CMS\Core\Utility\PathUtility;
32 use TYPO3\CMS\Lang\LanguageService;
33
34 /**
35 * 'TCEforms' - Class for creating the backend editing forms.
36 *
37 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
38 * @coauthor René Fritz <r.fritz@colorcube.de>
39 */
40 class FormEngine {
41
42 /**
43 * @var array
44 */
45 public $palFieldArr = array();
46
47 /**
48 * @var bool
49 */
50 public $disableWizards = FALSE;
51
52 /**
53 * @var bool
54 */
55 public $isPalettedoc = FALSE;
56
57 /**
58 * @var int
59 */
60 public $paletteMargin = 1;
61
62 /**
63 * @var string
64 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8
65 */
66 public $defStyle = '';
67
68 /**
69 * @var array
70 */
71 public $cachedTSconfig = array();
72
73 /**
74 * @var array
75 */
76 public $cachedTSconfig_fieldLevel = array();
77
78 /**
79 * @var array
80 */
81 public $cachedLanguageFlag = array();
82
83 /**
84 * @var array|NULL
85 */
86 public $cachedAdditionalPreviewLanguages = NULL;
87
88 /**
89 * Cache for the real PID of a record. The array key consists for a combined string "<table>:<uid>:<pid>".
90 * The value is an array with two values: first is the real PID of a record, second is the PID value for TSconfig.
91 *
92 * @var array
93 */
94 protected $cache_getTSCpid = array();
95
96 /**
97 * @var array
98 */
99 public $transformedRow = array();
100
101 /**
102 * @var string
103 */
104 public $extJSCODE = '';
105
106 /**
107 * @var array
108 */
109 public $printNeededJS = array();
110
111 /**
112 * @var array
113 */
114 public $hiddenFieldAccum = array();
115
116 /**
117 * @var string
118 */
119 public $TBE_EDITOR_fieldChanged_func = '';
120
121 /**
122 * @var bool
123 */
124 public $loadMD5_JS = TRUE;
125
126 /**
127 * Array where records in the default language is stored. (processed by transferdata)
128 *
129 * @var array
130 */
131 public $defaultLanguageData = array();
132
133 /**
134 * Array where records in the default language is stored (raw without any processing. used for making diff)
135 *
136 * @var array
137 */
138 public $defaultLanguageData_diff = array();
139
140 /**
141 * @var array
142 */
143 public $additionalPreviewLanguageData = array();
144
145 /**
146 * Set this to the 'backPath' pointing back to the typo3 admin directory
147 * from the script where this form is displayed.
148 *
149 * @var string
150 */
151 public $backPath = '';
152
153 /**
154 * Alternative return URL path (default is \TYPO3\CMS\Core\Utility\GeneralUtility::linkThisScript())
155 *
156 * @var string
157 */
158 public $returnUrl = '';
159
160 /**
161 * Can be set to point to a field name in the form which will be set to '1' when the form
162 * is submitted with a *save* button. This way the recipient script can determine that
163 * the form was submitted for save and not "close" for example.
164 *
165 * @var string
166 */
167 public $doSaveFieldName = '';
168
169 /**
170 * Can be set TRUE/FALSE to whether palettes (secondary options) are in the topframe or in form.
171 * TRUE means they are NOT IN-form. So a collapsed palette is one, which is shown in the top frame, not in the page.
172 *
173 * @var bool
174 */
175 public $palettesCollapsed = FALSE;
176
177 /**
178 * If set, the RTE is disabled (from form display, eg. by checkbox in the bottom of the page!)
179 *
180 * @var bool
181 */
182 public $disableRTE = FALSE;
183
184 /**
185 * If FALSE, then all CSH will be disabled, regardless of settings in $this->edit_showFieldHelp
186 *
187 * @var bool
188 */
189 public $globalShowHelp = TRUE;
190
191 /**
192 * If this evaluates to TRUE, the forms are rendering only localization relevant fields of the records.
193 *
194 * @var string
195 */
196 public $localizationMode = '';
197
198 /**
199 * Overrule the field order set in TCA[types][showitem], eg for tt_content this value,
200 * 'bodytext,image', would make first the 'bodytext' field, then the 'image' field (if set for display)...
201 * and then the rest in the old order.
202 *
203 * @var string
204 */
205 public $fieldOrder = '';
206
207 /**
208 * If set to FALSE, palettes will NEVER be rendered.
209 *
210 * @var bool
211 */
212 public $doPrintPalette = TRUE;
213
214 /**
215 * Set to initialized clipboard object;
216 * Then the element browser will offer a link to paste in records from clipboard.
217 *
218 * @var \TYPO3\CMS\Backend\Clipboard\Clipboard|NULL
219 */
220 public $clipObj = NULL;
221
222 /**
223 * Enable click menu on reference icons.
224 *
225 * @var bool
226 */
227 public $enableClickMenu = FALSE;
228
229 /**
230 * @var bool
231 */
232 public $enableTabMenu = FALSE;
233
234 /**
235 * When enabled all fields are rendered non-editable
236 *
237 * @var bool
238 */
239 public $renderReadonly = FALSE;
240
241 /**
242 * Form field width compensation: Factor of "size=12" to "style="width: 12*9.58px"
243 * for form field widths of style-aware browsers
244 *
245 * @var float
246 */
247 public $form_rowsToStylewidth = 9.58;
248
249 /**
250 * Value that gets added for style="width: ...px" for textareas compared to input fields.
251 *
252 * @var int
253 */
254 protected $form_additionalTextareaStyleWidth = 23;
255
256 /**
257 * Form field width compensation: Compensation for large documents, doc-tab (editing)
258 *
259 * @var float
260 */
261 public $form_largeComp = 1.33;
262
263 /**
264 * The number of chars expected per row when the height of a text area field is
265 * automatically calculated based on the number of characters found in the field content.
266 *
267 * @var int
268 */
269 public $charsPerRow = 40;
270
271 /**
272 * The maximum abstract value for textareas
273 *
274 * @var int
275 */
276 public $maxTextareaWidth = 48;
277
278 /**
279 * The maximum abstract value for input fields
280 *
281 * @var int
282 */
283 public $maxInputWidth = 48;
284
285 /**
286 * Default style for the selector boxes used for multiple items in "select" and "group" types.
287 *
288 * @var string
289 */
290 public $defaultMultipleSelectorStyle = 'width:310px;';
291
292 // INTERNAL, static
293 /**
294 * The string to prepend formfield names with.
295 *
296 * @var string
297 */
298 public $prependFormFieldNames = 'data';
299
300 /**
301 * The string to prepend commands for tcemain::process_cmdmap with
302 *
303 * @var string
304 */
305 public $prependCmdFieldNames = 'cmd';
306
307 /**
308 * The string to prepend FILE form field names with
309 *
310 * @var string
311 */
312 public $prependFormFieldNames_file = 'data_files';
313
314 /**
315 * The string to prepend form field names that are active (not NULL)
316 *
317 * @var string
318 */
319 protected $prependFormFieldNamesActive = 'control[active]';
320
321 /**
322 * The name attribute of the form
323 *
324 * @var string
325 */
326 public $formName = 'editform';
327
328 /**
329 * Whitelist that allows TCA field configuration to be overridden by TSconfig
330 *
331 * @see overrideFieldConf()
332 * @var array
333 */
334 public $allowOverrideMatrix = array();
335
336 /**
337 * Set by readPerms() (caching)
338 *
339 * @var string
340 */
341 public $perms_clause = '';
342
343 /**
344 * Set by readPerms() (caching-flag)
345 *
346 * @var bool
347 */
348 public $perms_clause_set = FALSE;
349
350 /**
351 * Used to indicate the mode of CSH (Context Sensitive Help),
352 * whether it should be icons-only ('icon') or not at all (blank).
353 *
354 * @var bool
355 */
356 public $edit_showFieldHelp = FALSE;
357
358 /**
359 * @var bool
360 */
361 public $edit_docModuleUpload = FALSE;
362
363 /**
364 * Loaded with info about the browser when class is instantiated
365 *
366 * @var array
367 */
368 public $clientInfo = array();
369
370 /**
371 * TRUE, if RTE is possible for the current user (based on result from BE_USER->isRTE())
372 *
373 * @var bool
374 */
375 public $RTEenabled = FALSE;
376
377 /**
378 * If $this->RTEenabled was FALSE, you can find the reasons listed in this array
379 * which is filled with reasons why the RTE could not be loaded)
380 *
381 * @var string
382 */
383 public $RTEenabled_notReasons = '';
384
385 /**
386 * Counter that is incremented before an RTE is created. Can be used for unique ids etc.
387 *
388 * @var int
389 */
390 public $RTEcounter = 0;
391
392 /**
393 * Contains current color scheme
394 *
395 * @var array
396 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8
397 */
398 public $colorScheme = array();
399
400 /**
401 * Contains current class scheme
402 *
403 * @var array
404 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8
405 */
406 public $classScheme = array();
407
408 /**
409 * Contains the default color scheme
410 *
411 * @var array
412 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8
413 */
414 public $defColorScheme = array();
415
416 /**
417 * Contains the default class scheme
418 *
419 * @var array
420 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8
421 */
422 public $defClassScheme = array();
423
424 /**
425 * Contains field style values
426 *
427 * @var array|NULL
428 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8
429 */
430 public $fieldStyle = NULL;
431
432 /**
433 * Contains border style values
434 *
435 * @var array|NULL
436 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8
437 */
438 public $borderStyle = NULL;
439
440 /**
441 * An accumulation of messages from the class
442 *
443 * @var array
444 */
445 public $commentMessages = array();
446
447 /**
448 * Total wrapping for the table rows
449 *
450 * @var string
451 */
452 public $totalWrap = '<hr />|<hr />';
453
454 /**
455 * Field template
456 *
457 * @var string
458 */
459 public $fieldTemplate = '<strong>###FIELD_NAME###</strong><br />###FIELD_ITEM###<hr />';
460
461 /**
462 * Template subpart for palette fields
463 *
464 * @var string
465 */
466 protected $paletteFieldTemplate = '';
467
468 /**
469 * Wrapping template code for a section
470 *
471 * @var string
472 * @deprecatd since TYPO3 CMS 7, will be removed in CMS 8
473 */
474 public $sectionWrap = '';
475
476 /**
477 * Template for palette headers
478 *
479 * @var string
480 */
481 public $palFieldTemplateHeader = '';
482
483 /**
484 * Template for palettes
485 *
486 * @var string
487 */
488 public $palFieldTemplate = '';
489
490 /**
491 * Set to the fields NOT to display, if any
492 *
493 * @var array|NULL
494 */
495 public $excludeElements = NULL;
496
497 /**
498 * During rendering of forms this will keep track of which palettes
499 * has already been rendered (so they are not rendered twice by mistake)
500 *
501 * @var array
502 */
503 public $palettesRendered = array();
504
505 /**
506 * This array of fields will be set as hidden-fields instead of rendered normally!
507 * For instance palette fields edited in the top frame are set as hidden fields
508 * since the main form has to submit the values.
509 * The top frame actually just sets the value in the main form!
510 *
511 * @var array
512 */
513 public $hiddenFieldListArr = array();
514
515 /**
516 * Used to register input-field names, which are required. (Done during rendering of the fields).
517 * This information is then used later when the JavaScript is made.
518 *
519 * @var array
520 */
521 public $requiredFields = array();
522
523 /**
524 * Used to register input-field names, which are required an have additional requirements.
525 * (e.g. like a date/time must be positive integer)
526 * The information of this array is merged with $this->requiredFields later.
527 *
528 * @var array
529 */
530 public $requiredAdditional = array();
531
532 /**
533 * Used to register the min and max number of elements
534 * for selector boxes where that apply (in the "group" type for instance)
535 *
536 * @var array
537 */
538 public $requiredElements = array();
539
540 /**
541 * Used to determine where $requiredFields or $requiredElements are nested (in Tabs or IRRE)
542 *
543 * @var array
544 */
545 public $requiredNested = array();
546
547 /**
548 * Keeps track of the rendering depth of nested records
549 *
550 * @var int
551 */
552 public $renderDepth = 0;
553
554 /**
555 * Color scheme buffer
556 *
557 * @var array
558 */
559 public $savedSchemes = array();
560
561 /**
562 * holds the path an element is nested in (e.g. required for RTEhtmlarea)
563 *
564 * @var array
565 */
566 public $dynNestedStack = array();
567
568 // Internal, registers for user defined functions etc.
569 /**
570 * Additional HTML code, printed before the form
571 *
572 * @var array
573 */
574 public $additionalCode_pre = array();
575
576 /**
577 * Additional JavaScript, printed before the form
578 *
579 * @var array
580 */
581 public $additionalJS_pre = array();
582
583 /**
584 * Additional JavaScript printed after the form
585 *
586 * @var array
587 */
588 public $additionalJS_post = array();
589
590 /**
591 * Additional JavaScript executed on submit; If you set "OK" variable it will raise an error
592 * about RTEs not being loaded and offer to block further submission.
593 *
594 * @var array
595 */
596 public $additionalJS_submit = array();
597
598 /**
599 * Additional JavaScript executed when section element is deleted.
600 * This is necessary, for example, to correctly clean up HTMLArea RTE (bug #8232)
601 *
602 * @var array
603 */
604 public $additionalJS_delete = array();
605
606 /**
607 * @var \TYPO3\CMS\Backend\Form\Element\InlineElement
608 */
609 public $inline;
610
611 /**
612 * Array containing hook class instances called once for a form
613 *
614 * @var array
615 */
616 public $hookObjectsMainFields = array();
617
618 /**
619 * Array containing hook class instances called for each field
620 *
621 * @var array
622 */
623 public $hookObjectsSingleField = array();
624
625 /**
626 * Rows getting inserted into the alt_doc headers (when called from alt_doc.php)
627 *
628 * @var array
629 */
630 public $extraFormHeaders = array();
631
632 /**
633 * Form template, relative to typo3 directory
634 *
635 * @var string
636 */
637 public $templateFile = '';
638
639 /**
640 * @var \TYPO3\CMS\Backend\Form\Element\SuggestElement
641 */
642 protected $suggest;
643
644 /**
645 * Constructor function, setting internal variables, loading the styles used.
646 *
647 */
648 public function __construct() {
649 $this->clientInfo = GeneralUtility::clientInfo();
650 $this->RTEenabled = $this->getBackendUserAuthentication()->isRTE();
651 if (!$this->RTEenabled) {
652 $this->RTEenabled_notReasons = implode(LF, $this->getBackendUserAuthentication()->RTE_errors);
653 $this->commentMessages[] = 'RTE NOT ENABLED IN SYSTEM due to:' . LF . $this->RTEenabled_notReasons;
654 }
655 // Define whitelist that allows TCA field configuration to be overridden by TSconfig, @see overrideFieldConf():
656 $this->allowOverrideMatrix = array(
657 'input' => array('size', 'max', 'readOnly'),
658 'text' => array('cols', 'rows', 'wrap', 'readOnly'),
659 'check' => array('cols', 'showIfRTE', 'readOnly'),
660 'select' => array('size', 'autoSizeMax', 'maxitems', 'minitems', 'readOnly', 'treeConfig'),
661 'group' => array('size', 'autoSizeMax', 'max_size', 'show_thumbs', 'maxitems', 'minitems', 'disable_controls', 'readOnly'),
662 'inline' => array('appearance', 'behaviour', 'foreign_label', 'foreign_selector', 'foreign_unique', 'maxitems', 'minitems', 'size', 'autoSizeMax', 'symmetric_label', 'readOnly')
663 );
664 // Create instance of InlineElement only if this a non-IRRE-AJAX call:
665 if (!isset($GLOBALS['ajaxID']) || strpos($GLOBALS['ajaxID'], \TYPO3\CMS\Backend\Form\Element\InlineElement::class . '::') !== 0) {
666 $this->inline = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Form\Element\InlineElement::class);
667 }
668 // Create instance of \TYPO3\CMS\Backend\Form\Element\SuggestElement only if this a non-Suggest-AJAX call:
669 if (!isset($GLOBALS['ajaxID']) || strpos($GLOBALS['ajaxID'], \TYPO3\CMS\Backend\Form\Element\SuggestElement::class . '::') !== 0) {
670 $this->suggest = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Form\Element\SuggestElement::class);
671 }
672 // Prepare user defined objects (if any) for hooks which extend this function:
673 $this->hookObjectsMainFields = array();
674 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tceforms.php']['getMainFieldsClass'])) {
675 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tceforms.php']['getMainFieldsClass'] as $classRef) {
676 $this->hookObjectsMainFields[] = GeneralUtility::getUserObj($classRef);
677 }
678 }
679 $this->hookObjectsSingleField = array();
680 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tceforms.php']['getSingleFieldClass'])) {
681 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tceforms.php']['getSingleFieldClass'] as $classRef) {
682 $this->hookObjectsSingleField[] = GeneralUtility::getUserObj($classRef);
683 }
684 }
685 $this->templateFile = 'sysext/backend/Resources/Private/Templates/FormEngine.html';
686 }
687
688 /**
689 * Initialize various internal variables.
690 *
691 * @return void
692 */
693 public function initDefaultBEmode() {
694 $this->prependFormFieldNames = 'data';
695 $this->formName = 'editform';
696 $this->setNewBEDesign();
697 $this->edit_showFieldHelp = (bool)$this->getBackendUserAuthentication()->uc['edit_showFieldHelp'];
698 $this->edit_docModuleUpload = (bool)$this->getBackendUserAuthentication()->uc['edit_docModuleUpload'];
699 $this->inline->init($this);
700 $this->suggest->init($this);
701 }
702
703 /*******************************************************
704 *
705 * Rendering the forms, fields etc
706 *
707 *******************************************************/
708 /**
709 * Will return the TCEform element for just a single field from a record.
710 * The field must be listed in the currently displayed fields (as found in [types][showitem]) for the record.
711 * This also means that the $table/$row supplied must be complete so the list of fields to show can be found correctly
712 *
713 * @param string $table The table name
714 * @param array $row The record from the table for which to render a field.
715 * @param string $theFieldToReturn The field name to return the TCEform element for.
716 * @return string HTML output
717 * @see getMainFields()
718 */
719 public function getSoloField($table, $row, $theFieldToReturn) {
720 if (!isset($GLOBALS['TCA'][$table])) {
721 return '';
722 }
723 $typeNum = $this->getRTypeNum($table, $row);
724 if (isset($GLOBALS['TCA'][$table]['types'][$typeNum])) {
725 $itemList = $GLOBALS['TCA'][$table]['types'][$typeNum]['showitem'];
726 if ($itemList) {
727 $fields = GeneralUtility::trimExplode(',', $itemList, TRUE);
728 $excludeElements = ($this->excludeElements = $this->getExcludeElements($table, $row, $typeNum));
729 foreach ($fields as $fieldInfo) {
730 $parts = explode(';', $fieldInfo);
731 $theField = trim($parts[0]);
732 if (!in_array($theField, $excludeElements) && (string)$theField === (string)$theFieldToReturn) {
733 if ($GLOBALS['TCA'][$table]['columns'][$theField]) {
734 $sField = $this->getSingleField($table, $theField, $row, $parts[1], 1, $parts[3], $parts[2]);
735 return $sField['ITEM'];
736 }
737 }
738 }
739 }
740 }
741 return '';
742 }
743
744 /**
745 * Based on the $table and $row of content, this displays the complete TCEform for the record.
746 * The input-$row is required to be preprocessed if necessary by eg.
747 * the \TYPO3\CMS\Backend\Form\DataPreprocessor class. For instance the RTE content
748 * should be transformed through this class first.
749 *
750 * @param string $table The table name
751 * @param array $row The record from the table for which to render a field.
752 * @param int $depth Depth level
753 * @param array $overruleTypesArray Overrule types array. Can be used to override the showitem etc. configuration for the TCA types of the table. Can contain all settings which are possible in the TCA 'types' section. See e.g. $TCA['tt_content']['types'].
754 * @return string HTML output
755 * @see getSoloField()
756 */
757 public function getMainFields($table, array $row, $depth = 0, array $overruleTypesArray = array()) {
758 $this->renderDepth = $depth;
759 // Init vars:
760 $out_array = array(array());
761 $out_array_meta = array(
762 array(
763 'title' => $this->getLL('l_generalTab')
764 )
765 );
766 $out_pointer = 0;
767 $out_sheet = 0;
768 $this->palettesRendered = array();
769 $this->palettesRendered[$this->renderDepth][$table] = array();
770 // Hook: getMainFields_preProcess (requested by Thomas Hempel for use with the "dynaflex" extension)
771 foreach ($this->hookObjectsMainFields as $hookObj) {
772 if (method_exists($hookObj, 'getMainFields_preProcess')) {
773 $hookObj->getMainFields_preProcess($table, $row, $this);
774 }
775 }
776 $tabIdentString = '';
777 $tabIdentStringMD5 = '';
778 if ($GLOBALS['TCA'][$table]) {
779 // Load the description content for the table.
780 if ($this->edit_showFieldHelp || $this->doLoadTableDescr($table)) {
781 $this->getLanguageService()->loadSingleTableDescription($table);
782 }
783 // Get the current "type" value for the record.
784 $typeNum = $this->getRTypeNum($table, $row);
785 // Find the list of fields to display:
786 if ($GLOBALS['TCA'][$table]['types'][$typeNum]) {
787 $itemList = $GLOBALS['TCA'][$table]['types'][$typeNum]['showitem'];
788 if (is_array($overruleTypesArray) && isset($overruleTypesArray[$typeNum]['showitem'])) {
789 $itemList = $overruleTypesArray[$typeNum]['showitem'];
790 }
791 // If such a list existed...
792 if ($itemList) {
793 // Explode the field list and possibly rearrange the order of the fields, if configured for
794 $fields = GeneralUtility::trimExplode(',', $itemList, TRUE);
795 if ($this->fieldOrder) {
796 $fields = $this->rearrange($fields);
797 }
798 // Get excluded fields, added fiels and put it together:
799 $excludeElements = ($this->excludeElements = $this->getExcludeElements($table, $row, $typeNum));
800 $fields = $this->mergeFieldsWithAddedFields($fields, $this->getFieldsToAdd($table, $row, $typeNum), $table);
801 // If TCEforms will render a tab menu in the next step, push the name to the tab stack:
802 if (strstr($itemList, '--div--') !== FALSE && $this->enableTabMenu) {
803 $tabIdentString = 'TCEforms:' . $table . ':' . $row['uid'];
804 $tabIdentStringMD5 = $this->getDocumentTemplate()->getDynTabMenuId($tabIdentString);
805 // Remember that were currently working on the general tab:
806 if (isset($fields[0]) && strpos($fields[0], '--div--') !== 0) {
807 $this->pushToDynNestedStack('tab', $tabIdentStringMD5 . '-1');
808 }
809 }
810 // Traverse the fields to render:
811 $cc = 0;
812 foreach ($fields as $fieldInfo) {
813 // Exploding subparts of the field configuration:
814 // this is documented as this:
815 // fieldname;fieldlabel;paletteidorlinebreaktodisplay;extradata;colorscheme
816 // fieldname can also be "--div--" or "--palette--"
817 // the last option colorscheme was dropped with TYPO3 CMS 7
818
819 list($theField, $fieldLabel, $additionalPalette, $extraFieldProcessingData) = explode(';', $fieldInfo);
820
821 // Render the field:
822 if (!in_array($theField, $excludeElements)) {
823 if ($GLOBALS['TCA'][$table]['columns'][$theField]) {
824 $sFieldPal = '';
825 if ($additionalPalette && !isset($this->palettesRendered[$this->renderDepth][$table][$additionalPalette])) {
826 $sFieldPal = $this->getPaletteFields($table, $row, $additionalPalette);
827 $this->palettesRendered[$this->renderDepth][$table][$additionalPalette] = 1;
828 }
829 $sField = $this->getSingleField($table, $theField, $row, $fieldLabel, 0, $extraFieldProcessingData, $additionalPalette);
830 if ($sField) {
831 $sField .= $sFieldPal;
832 }
833 $out_array[$out_sheet][$out_pointer] .= $sField;
834 } elseif ($theField == '--div--') {
835 if ($cc > 0) {
836 if ($this->enableTabMenu) {
837 // Remove last tab entry from the dynNestedStack:
838 $out_sheet++;
839 // Remove the previous sheet from stack (if any):
840 $this->popFromDynNestedStack('tab', $tabIdentStringMD5 . '-' . $out_sheet);
841 // Remember on which sheet we're currently working:
842 $this->pushToDynNestedStack('tab', $tabIdentStringMD5 . '-' . ($out_sheet + 1));
843 $out_array[$out_sheet] = array();
844 $out_array_meta[$out_sheet]['title'] = $this->sL($fieldLabel);
845 // Register newline for Tab
846 $out_array_meta[$out_sheet]['newline'] = $additionalPalette == 'newline';
847 }
848 } else {
849 // Setting alternative title for "General" tab if "--div--" is the very first element.
850 $out_array_meta[$out_sheet]['title'] = $this->sL($fieldLabel);
851 // Only add the first tab to the dynNestedStack if there are more tabs:
852 if ($tabIdentString && strpos($itemList, '--div--', strlen($fieldInfo))) {
853 $this->pushToDynNestedStack('tab', $tabIdentStringMD5 . '-1');
854 }
855 }
856 } elseif ($theField == '--palette--') {
857 if ($additionalPalette && !isset($this->palettesRendered[$this->renderDepth][$table][$additionalPalette])) {
858 // Render a 'header' if not collapsed
859 if ($GLOBALS['TCA'][$table]['palettes'][$additionalPalette]['canNotCollapse'] && $fieldLabel) {
860 $out_array[$out_sheet][$out_pointer] .= $this->getPaletteFields($table, $row, $additionalPalette, $this->sL($fieldLabel));
861 } else {
862 $out_array[$out_sheet][$out_pointer] .= $this->getPaletteFields($table, $row, $additionalPalette, '', '', $this->sL($fieldLabel));
863 }
864 $this->palettesRendered[$this->renderDepth][$table][$additionalPalette] = 1;
865 }
866 }
867 }
868 $cc++;
869 }
870 }
871 }
872 }
873 // Hook: getMainFields_postProcess (requested by Thomas Hempel for use with the "dynaflex" extension)
874 foreach ($this->hookObjectsMainFields as $hookObj) {
875 if (method_exists($hookObj, 'getMainFields_postProcess')) {
876 $hookObj->getMainFields_postProcess($table, $row, $this);
877 }
878 }
879 // Rendering Main palettes, if any
880 $mParr = GeneralUtility::trimExplode(',', $GLOBALS['TCA'][$table]['ctrl']['mainpalette']);
881 $i = 0;
882 if (count($mParr)) {
883 foreach ($mParr as $mP) {
884 if (!isset($this->palettesRendered[$this->renderDepth][$table][$mP])) {
885 $temp_palettesCollapsed = $this->palettesCollapsed;
886 $this->palettesCollapsed = FALSE;
887 $label = $i == 0 ? $this->getLL('l_generalOptions') : $this->getLL('l_generalOptions_more');
888 $out_array[$out_sheet][$out_pointer] .= $this->getPaletteFields($table, $row, $mP, $label);
889 $this->palettesCollapsed = $temp_palettesCollapsed;
890 $this->palettesRendered[$this->renderDepth][$table][$mP] = 1;
891 }
892 $i++;
893 if ($this->renderDepth) {
894 $this->renderDepth--;
895 }
896 }
897 }
898 // Return the imploded $out_array:
899 // There were --div-- dividers around...
900 if ($out_sheet > 0) {
901 // Create parts array for the tab menu:
902 $parts = array();
903 foreach ($out_array as $idx => $sheetContent) {
904 $content = implode('', $sheetContent);
905 if ($content) {
906 // Wrap content (row) with table-tag, otherwise tab/sheet will be disabled (see getdynTabMenu() )
907 $content = '<table border="0" cellspacing="0" cellpadding="0" width="100%">' . $content . '</table>';
908 }
909 $parts[$idx] = array(
910 'label' => $out_array_meta[$idx]['title'],
911 'content' => $content,
912 'newline' => $out_array_meta[$idx]['newline']
913 );
914 }
915 if (count($parts) > 1) {
916 // Unset the current level of tab menus:
917 $this->popFromDynNestedStack('tab', $tabIdentStringMD5 . '-' . ($out_sheet + 1));
918 $output = $this->getDynTabMenu($parts, $tabIdentString);
919 } else {
920 // If there is only one tab/part there is no need to wrap it into the dynTab code
921 $output = isset($parts[0]) ? trim($parts[0]['content']) : '';
922 }
923 $output = '
924 <tr>
925 <td colspan="2">
926 ' . $output . '
927 </td>
928 </tr>';
929 } else {
930 // Only one tab, so just implode and wrap the background image (= tab container) around:
931 $output = implode('', $out_array[$out_sheet]);
932 $output = '<div class="typo3-dyntabmenu-divs">' . $output . '</div>';
933 }
934
935 return $output;
936 }
937
938 /**
939 * Will return the TCEform elements for a pre-defined list of fields.
940 * Notice that this will STILL use the configuration found in the list [types][showitem] for those fields which are found there. So ideally the list of fields given as argument to this function should also be in the current [types][showitem] list of the record.
941 * Used for displaying forms for the frontend edit icons for instance.
942 *
943 * @param string $table The table name
944 * @param array $row The record array.
945 * @param string $list Commalist of fields from the table. These will be shown in the specified order in a form.
946 * @return string TCEform elements in a string.
947 */
948 public function getListedFields($table, $row, $list) {
949 if ($this->edit_showFieldHelp || $this->doLoadTableDescr($table)) {
950 $this->getLanguageService()->loadSingleTableDescription($table);
951 }
952 $out = '';
953 $types_fieldConfig = BackendUtility::getTCAtypes($table, $row, 1);
954 $editFieldList = array_unique(GeneralUtility::trimExplode(',', $list, TRUE));
955 foreach ($editFieldList as $theFieldC) {
956 list($theField, $palFields) = preg_split('/\\[|\\]/', $theFieldC);
957 $theField = trim($theField);
958 $palFields = trim($palFields);
959 if ($GLOBALS['TCA'][$table]['columns'][$theField]) {
960 $parts = GeneralUtility::trimExplode(';', $types_fieldConfig[$theField]['origString']);
961 // Don't sent palette pointer - there are no options anyways for a field-list.
962 $sField = $this->getSingleField($table, $theField, $row, $parts[1], 0, $parts[3], 0);
963 $out .= $sField;
964 }
965 if ($palFields) {
966 $out .= $this->getPaletteFields($table, $row, '', '', implode(',', GeneralUtility::trimExplode('|', $palFields, TRUE)));
967 }
968 }
969 return $out;
970 }
971
972 /**
973 * Creates a palette (collection of secondary options).
974 *
975 * @param string $table The table name
976 * @param array $row The row array
977 * @param string $palette The palette number/pointer
978 * @param string $header Header string for the palette (used when in-form). If not set, no header item is made.
979 * @param string $itemList Optional alternative list of fields for the palette
980 * @param string $collapsedHeader Optional Link text for activating a palette (when palettes does not have another form element to belong to).
981 * @return string HTML code.
982 */
983 public function getPaletteFields($table, $row, $palette, $header = '', $itemList = '', $collapsedHeader = NULL) {
984 if (!$this->doPrintPalette) {
985 return '';
986 }
987 $out = '';
988 $parts = $this->loadPaletteElements($table, $row, $palette, $itemList);
989 // Put palette together if there are fields in it:
990 if (count($parts)) {
991 $realFields = 0;
992 foreach ($parts as $part) {
993 if ($part['NAME'] !== '--linebreak--') {
994 $realFields++;
995 }
996 }
997 if ($realFields > 0) {
998 if ($header) {
999 $out .= $this->intoTemplate(array('HEADER' => htmlspecialchars($header)), $this->palFieldTemplateHeader);
1000 }
1001 $collapsed = $this->isPalettesCollapsed($table, $palette);
1002 // Check if the palette is a hidden palette
1003 $isHiddenPalette = !empty($GLOBALS['TCA'][$table]['palettes'][$palette]['isHiddenPalette']);
1004 $thePalIcon = '';
1005 if ($collapsed && $collapsedHeader !== NULL && !$isHiddenPalette) {
1006 list($thePalIcon, ) = $this->wrapOpenPalette(IconUtility::getSpriteIcon('actions-system-options-view', array('title' => htmlspecialchars($this->getLL('l_moreOptions')))), $table, $row, $palette, 1);
1007 $thePalIcon = '<span style="margin-left: 20px;">' . $thePalIcon . $collapsedHeader . '</span>';
1008 }
1009 $paletteHtml = $this->wrapPaletteField($this->printPalette($parts), $table, $row, $palette, $collapsed);
1010 $out .= $this->intoTemplate(array('PALETTE' => $thePalIcon . $paletteHtml), $this->palFieldTemplate);
1011 }
1012 }
1013 return $out;
1014 }
1015
1016 /**
1017 * Returns the form HTML code for a database table field.
1018 *
1019 * @param string $table The table name
1020 * @param string $field The field name
1021 * @param array $row The record to edit from the database table.
1022 * @param string $altName Alternative field name label to show.
1023 * @param bool $palette Set this if the field is on a palette (in top frame), otherwise not. (if set, field will render as a hidden field).
1024 * @param string $extra The "extra" options from "Part 4" of the field configurations found in the "types" "showitem" list. Typically parsed by $this->getSpecConfFromString() in order to get the options as an associative array.
1025 * @param int $pal The palette pointer.
1026 * @return mixed String (normal) or array (palettes)
1027 */
1028 public function getSingleField($table, $field, $row, $altName = '', $palette = FALSE, $extra = '', $pal = 0) {
1029 // Hook: getSingleField_preProcess
1030 foreach ($this->hookObjectsSingleField as $hookObj) {
1031 if (method_exists($hookObj, 'getSingleField_preProcess')) {
1032 $hookObj->getSingleField_preProcess($table, $field, $row, $altName, $palette, $extra, $pal, $this);
1033 }
1034 }
1035 $out = '';
1036 $PA = array();
1037 $PA['altName'] = $altName;
1038 $PA['palette'] = $palette;
1039 $PA['extra'] = $extra;
1040 $PA['pal'] = $pal;
1041 // Get the TCA configuration for the current field:
1042 $PA['fieldConf'] = $GLOBALS['TCA'][$table]['columns'][$field];
1043 $PA['fieldConf']['config']['form_type'] = $PA['fieldConf']['config']['form_type'] ?: $PA['fieldConf']['config']['type'];
1044
1045 // Using "form_type" locally in this script
1046 $skipThisField = $this->inline->skipField($table, $field, $row, $PA['fieldConf']['config']);
1047
1048 // Evaluate display condition
1049 $displayConditionResult = TRUE;
1050 if (is_array($PA['fieldConf']) && $PA['fieldConf']['displayCond'] && is_array($row)) {
1051 /** @var $elementConditionMatcher \TYPO3\CMS\Backend\Form\ElementConditionMatcher */
1052 $elementConditionMatcher = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Form\ElementConditionMatcher::class);
1053 $displayConditionResult = $elementConditionMatcher->match($PA['fieldConf']['displayCond'], $row);
1054 }
1055 // Check if this field is configured and editable (according to excludefields + other configuration)
1056 if (
1057 is_array($PA['fieldConf'])
1058 && !$skipThisField
1059 && (!$PA['fieldConf']['exclude'] || $this->getBackendUserAuthentication()->check('non_exclude_fields', $table . ':' . $field))
1060 && $PA['fieldConf']['config']['form_type'] != 'passthrough'
1061 && ($this->RTEenabled || !$PA['fieldConf']['config']['showIfRTE'])
1062 && $displayConditionResult
1063 && (!$GLOBALS['TCA'][$table]['ctrl']['languageField'] || $PA['fieldConf']['l10n_display'] || ($PA['fieldConf']['l10n_mode'] !== 'exclude') || $row[$GLOBALS['TCA'][$table]['ctrl']['languageField']] <= 0)
1064 && (!$GLOBALS['TCA'][$table]['ctrl']['languageField'] || !$this->localizationMode || $this->localizationMode === $PA['fieldConf']['l10n_cat'])
1065 ) {
1066 // Fetching the TSconfig for the current table/field. This includes the $row which means that
1067 $PA['fieldTSConfig'] = $this->setTSconfig($table, $row, $field);
1068 // If the field is NOT disabled from TSconfig (which it could have been) then render it
1069 if (!$PA['fieldTSConfig']['disabled']) {
1070 // Override fieldConf by fieldTSconfig:
1071 $PA['fieldConf']['config'] = $this->overrideFieldConf($PA['fieldConf']['config'], $PA['fieldTSConfig']);
1072 // Init variables:
1073 $PA['itemFormElName'] = $this->prependFormFieldNames . '[' . $table . '][' . $row['uid'] . '][' . $field . ']';
1074 // Form field name, in case of file uploads
1075 $PA['itemFormElName_file'] = $this->prependFormFieldNames_file . '[' . $table . '][' . $row['uid'] . '][' . $field . ']';
1076 // Form field name, to activate elements
1077 // If the "eval" list contains "null", elements can be deactivated which results in storing NULL to database
1078 $PA['itemFormElNameActive'] = $this->prependFormFieldNamesActive . '[' . $table . '][' . $row['uid'] . '][' . $field . ']';
1079 // The value to show in the form field.
1080 $PA['itemFormElValue'] = $row[$field];
1081 $PA['itemFormElID'] = $this->prependFormFieldNames . '_' . $table . '_' . $row['uid'] . '_' . $field;
1082 // Set field to read-only if configured for translated records to show default language content as readonly
1083 if ($PA['fieldConf']['l10n_display'] && GeneralUtility::inList($PA['fieldConf']['l10n_display'], 'defaultAsReadonly') && $row[$GLOBALS['TCA'][$table]['ctrl']['languageField']] > 0) {
1084 $PA['fieldConf']['config']['readOnly'] = TRUE;
1085 $PA['itemFormElValue'] = $this->defaultLanguageData[$table . ':' . $row['uid']][$field];
1086 }
1087 if (strpos($GLOBALS['TCA'][$table]['ctrl']['type'], ':') === FALSE) {
1088 $typeField = $GLOBALS['TCA'][$table]['ctrl']['type'];
1089 } else {
1090 $typeField = substr($GLOBALS['TCA'][$table]['ctrl']['type'], 0, strpos($GLOBALS['TCA'][$table]['ctrl']['type'], ':'));
1091 }
1092 // Create a JavaScript code line which will ask the user to save/update the form due to changing the element. This is used for eg. "type" fields and others configured with "requestUpdate"
1093 if (
1094 !empty($GLOBALS['TCA'][$table]['ctrl']['type'])
1095 && $field === $typeField
1096 || !empty($GLOBALS['TCA'][$table]['ctrl']['requestUpdate'])
1097 && GeneralUtility::inList(str_replace(' ', '', $GLOBALS['TCA'][$table]['ctrl']['requestUpdate']), $field)
1098 ) {
1099 if ($this->getBackendUserAuthentication()->jsConfirmation(1)) {
1100 $alertMsgOnChange = 'if (confirm(TBE_EDITOR.labels.onChangeAlert) && TBE_EDITOR.checkSubmit(-1)){ TBE_EDITOR.submitForm() };';
1101 } else {
1102 $alertMsgOnChange = 'if (TBE_EDITOR.checkSubmit(-1)){ TBE_EDITOR.submitForm() };';
1103 }
1104 } else {
1105 $alertMsgOnChange = '';
1106 }
1107 // Render as a hidden field?
1108 if (in_array($field, $this->hiddenFieldListArr)) {
1109 $this->hiddenFieldAccum[] = '<input type="hidden" name="' . $PA['itemFormElName'] . '" value="' . htmlspecialchars($PA['itemFormElValue']) . '" />';
1110 } else {
1111 // Render as a normal field:
1112 // If the field is NOT a palette field, then we might create an icon which links to a palette for the field, if one exists.
1113 $palJSfunc = '';
1114 $thePalIcon = '';
1115 if (!$PA['palette']) {
1116 $paletteFields = $this->loadPaletteElements($table, $row, $PA['pal']);
1117 if ($PA['pal'] && $this->isPalettesCollapsed($table, $PA['pal']) && count($paletteFields)) {
1118 list($thePalIcon, $palJSfunc) = $this->wrapOpenPalette(IconUtility::getSpriteIcon('actions-system-options-view', array('title' => htmlspecialchars($this->getLL('l_moreOptions')))), $table, $row, $PA['pal'], 1);
1119 }
1120 }
1121 // onFocus attribute to add to the field:
1122 $PA['onFocus'] = $palJSfunc && !$this->getBackendUserAuthentication()->uc['dontShowPalettesOnFocusInAB'] ? ' onfocus="' . htmlspecialchars($palJSfunc) . '"' : '';
1123 $PA['label'] = $PA['altName'] ?: $PA['fieldConf']['label'];
1124 $PA['label'] = $PA['fieldTSConfig']['label'] ?: $PA['label'];
1125 $PA['label'] = $PA['fieldTSConfig']['label.'][$this->getLanguageService()->lang] ?: $PA['label'];
1126 $PA['label'] = $this->sL($PA['label']);
1127 // JavaScript code for event handlers:
1128 $PA['fieldChangeFunc'] = array();
1129 $PA['fieldChangeFunc']['TBE_EDITOR_fieldChanged'] = 'TBE_EDITOR.fieldChanged(\'' . $table . '\',\'' . $row['uid'] . '\',\'' . $field . '\',\'' . $PA['itemFormElName'] . '\');';
1130 $PA['fieldChangeFunc']['alert'] = $alertMsgOnChange;
1131 // If this is the child of an inline type and it is the field creating the label
1132 if ($this->inline->isInlineChildAndLabelField($table, $field)) {
1133 $inlineObjectId = implode(InlineElement::Structure_Separator, array(
1134 $this->inline->inlineNames['object'],
1135 $table,
1136 $row['uid']
1137 ));
1138 $PA['fieldChangeFunc']['inline'] = 'inline.handleChangedField(\'' . $PA['itemFormElName'] . '\',\'' . $inlineObjectId . '\');';
1139 }
1140 // Based on the type of the item, call a render function:
1141 $item = $this->getSingleField_SW($table, $field, $row, $PA);
1142 // Add language + diff
1143 if ($PA['fieldConf']['l10n_display'] && (GeneralUtility::inList($PA['fieldConf']['l10n_display'], 'hideDiff') || GeneralUtility::inList($PA['fieldConf']['l10n_display'], 'defaultAsReadonly'))) {
1144 $renderLanguageDiff = FALSE;
1145 } else {
1146 $renderLanguageDiff = TRUE;
1147 }
1148 if ($renderLanguageDiff) {
1149 $item = $this->renderDefaultLanguageContent($table, $field, $row, $item);
1150 $item = $this->renderDefaultLanguageDiff($table, $field, $row, $item);
1151 }
1152 // If the record has been saved and the "linkTitleToSelf" is set, we make the field name into a link, which will load ONLY this field in alt_doc.php
1153 $label = htmlspecialchars($PA['label'], ENT_COMPAT, 'UTF-8', FALSE);
1154 if (MathUtility::canBeInterpretedAsInteger($row['uid']) && $PA['fieldTSConfig']['linkTitleToSelf'] && !GeneralUtility::_GP('columnsOnly')) {
1155 $lTTS_url = $this->backPath . 'alt_doc.php?edit[' . $table . '][' . $row['uid'] . ']=edit&columnsOnly=' . $field . '&returnUrl=' . rawurlencode($this->thisReturnUrl());
1156 $label = '<a href="' . htmlspecialchars($lTTS_url) . '">' . $label . '</a>';
1157 }
1158
1159 if (isset($PA['fieldConf']['config']['mode']) && $PA['fieldConf']['config']['mode'] == 'useOrOverridePlaceholder') {
1160 $placeholder = $this->getPlaceholderValue($table, $field, $PA['fieldConf']['config'], $row);
1161 $onChange = 'typo3form.fieldTogglePlaceholder(' . GeneralUtility::quoteJSvalue($PA['itemFormElName']) . ', !this.checked)';
1162 $checked = $PA['itemFormElValue'] === NULL ? '' : ' checked="checked"';
1163
1164 $this->additionalJS_post[] = 'typo3form.fieldTogglePlaceholder('
1165 . GeneralUtility::quoteJSvalue($PA['itemFormElName']) . ', ' . ($checked ? 'false' : 'true') . ');';
1166
1167 $item = '<div class="t3-form-field-placeholder-override">'
1168 . '<span class="t3-tceforms-placeholder-override-checkbox">' .
1169 '<input type="hidden" name="' . htmlspecialchars($PA['itemFormElNameActive']) . '" value="0" />' .
1170 '<input type="checkbox" name="' . htmlspecialchars($PA['itemFormElNameActive']) . '" value="1" id="tce-forms-textfield-use-override-' . $field . '-' . $row['uid'] . '" onchange="' . htmlspecialchars($onChange) . '"' . $checked . ' />' .
1171 '<label for="tce-forms-textfield-use-override-' . $field . '-' . $row['uid'] . '">' .
1172 sprintf($this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.placeholder.override'),
1173 BackendUtility::getRecordTitlePrep($placeholder, 20)) . '</label>' .
1174 '</span>'
1175 . '<div class="t3-form-placeholder-placeholder">' . $this->getSingleField_typeNone_render(
1176 $PA['fieldConf']['config'], GeneralUtility::fixed_lgd_cs($placeholder, 30)
1177 ) . '</div>'
1178 . '<div class="t3-form-placeholder-formfield">' . $item . '</div>'
1179 . '</div>';
1180 }
1181
1182 // Wrap the label with help text
1183 $PA['label'] = ($label = BackendUtility::wrapInHelp($table, $field, $label));
1184 // Create output value:
1185 if ($PA['fieldConf']['config']['form_type'] == 'user' && $PA['fieldConf']['config']['noTableWrapping']) {
1186 $out = $item;
1187 } elseif ($PA['palette']) {
1188 // Array:
1189 $out = array(
1190 'NAME' => $label,
1191 'ID' => $row['uid'],
1192 'FIELD' => $field,
1193 'TABLE' => $table,
1194 'ITEM' => $item,
1195 'ITEM_DISABLED' => ($this->isDisabledNullValueField($table, $field, $row, $PA) ? ' disabled' : ''),
1196 'ITEM_NULLVALUE' => $this->renderNullValueWidget($table, $field, $row, $PA),
1197 );
1198 $out = $this->addUserTemplateMarkers($out, $table, $field, $row, $PA);
1199 } else {
1200 // String:
1201 $out = array(
1202 'NAME' => $label,
1203 'ITEM' => $item,
1204 'TABLE' => $table,
1205 'ID' => $row['uid'],
1206 'PAL_LINK_ICON' => $thePalIcon,
1207 'FIELD' => $field,
1208 'ITEM_DISABLED' => ($this->isDisabledNullValueField($table, $field, $row, $PA) ? ' disabled' : ''),
1209 'ITEM_NULLVALUE' => $this->renderNullValueWidget($table, $field, $row, $PA),
1210 );
1211 $out = $this->addUserTemplateMarkers($out, $table, $field, $row, $PA);
1212 // String:
1213 $out = $this->intoTemplate($out);
1214 }
1215 }
1216 } else {
1217 $this->commentMessages[] = $this->prependFormFieldNames . '[' . $table . '][' . $row['uid'] . '][' . $field . ']: Disabled by TSconfig';
1218 }
1219 }
1220 // Hook: getSingleField_postProcess
1221 foreach ($this->hookObjectsSingleField as $hookObj) {
1222 if (method_exists($hookObj, 'getSingleField_postProcess')) {
1223 $hookObj->getSingleField_postProcess($table, $field, $row, $out, $PA, $this);
1224 }
1225 }
1226 // Return value (string or array)
1227 return $out;
1228 }
1229
1230 /**
1231 * Rendering a single item for the form
1232 *
1233 * @param string $table Table name of record
1234 * @param string $field Fieldname to render
1235 * @param array $row The record
1236 * @param array $PA Parameters array containing a lot of stuff. Value by Reference!
1237 * @return string Returns the item as HTML code to insert
1238 * @access private
1239 * @see getSingleField(), getSingleField_typeFlex_draw()
1240 */
1241 public function getSingleField_SW($table, $field, $row, &$PA) {
1242 $PA['fieldConf']['config']['form_type'] = $PA['fieldConf']['config']['form_type'] ?: $PA['fieldConf']['config']['type'];
1243 // Using "form_type" locally in this script
1244 // Hook: getSingleField_beforeRender
1245 foreach ($this->hookObjectsSingleField as $hookObject) {
1246 if (method_exists($hookObject, 'getSingleField_beforeRender')) {
1247 $hookObject->getSingleField_beforeRender($table, $field, $row, $PA);
1248 }
1249 }
1250 $type = $PA['fieldConf']['config']['form_type'];
1251 if ($type === 'inline') {
1252 $item = $this->inline->getSingleField_typeInline($table, $field, $row, $PA);
1253 } else {
1254 $typeClassNameMapping = array(
1255 'input' => 'InputElement',
1256 'text' => 'TextElement',
1257 'check' => 'CheckboxElement',
1258 'radio' => 'RadioElement',
1259 'select' => 'SelectElement',
1260 'group' => 'GroupElement',
1261 'none' => 'NoneElement',
1262 'user' => 'UserElement',
1263 'flex' => 'FlexElement',
1264 'unknown' => 'UnknownElement',
1265 );
1266 if (!isset($typeClassNameMapping[$type])) {
1267 $type = 'unknown';
1268 }
1269 $item = GeneralUtility::makeInstance('TYPO3\\CMS\\Backend\\Form\\Element\\' . $typeClassNameMapping[$type], $this)
1270 ->render($table, $field, $row, $PA);
1271 }
1272 return $item;
1273 }
1274
1275 /**********************************************************
1276 *
1277 * Rendering of each TCEform field type
1278 *
1279 ************************************************************/
1280 /**
1281 * Generation of TCEform elements of the type "input"
1282 * This will render a single-line input form field, possibly with various control/validation features
1283 *
1284 * @param string $table The table name of the record
1285 * @param string $field The field name which this element is supposed to edit
1286 * @param array $row The record data array where the value(s) for the field can be found
1287 * @param array $PA An array with additional configuration options.
1288 * @return string The HTML code for the TCEform field
1289 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8. Use \TYPO3\CMS\Backend\Form\Element\InputElement
1290 */
1291 public function getSingleField_typeInput($table, $field, $row, &$PA) {
1292 GeneralUtility::logDeprecatedFunction();
1293 return $item = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Form\Element\InputElement::class, $this)
1294 ->render($table, $field, $row, $PA);
1295 }
1296
1297 /**
1298 * Renders a view widget to handle and activate NULL values.
1299 * The widget is enabled by using 'null' in the 'eval' TCA definition.
1300 *
1301 * @param string $table Name of the table
1302 * @param string $field Name of the field
1303 * @param array $row Accordant data of the record row
1304 * @param array $PA Parameters array with rendering instructions
1305 * @return string Widget (if any).
1306 */
1307 protected function renderNullValueWidget($table, $field, array $row, array $PA) {
1308 $widget = '';
1309
1310 $config = $PA['fieldConf']['config'];
1311 if (
1312 !empty($config['eval']) && GeneralUtility::inList($config['eval'], 'null')
1313 && (empty($config['mode']) || $config['mode'] !== 'useOrOverridePlaceholder')
1314 ) {
1315 $checked = $PA['itemFormElValue'] === NULL ? '' : ' checked="checked"';
1316 $onChange = htmlspecialchars(
1317 'typo3form.fieldSetNull(\'' . $PA['itemFormElName'] . '\', !this.checked)'
1318 );
1319
1320 $widget = '<span class="t3-tceforms-widget-null-wrapper">' .
1321 '<input type="hidden" name="' . $PA['itemFormElNameActive'] . '" value="0" />' .
1322 '<input type="checkbox" name="' . $PA['itemFormElNameActive'] . '" value="1" onchange="' . $onChange . '"' . $checked . ' />' .
1323 '</span>';
1324 }
1325
1326 return $widget;
1327 }
1328
1329 /**
1330 * Determines whether the current field value is considered as NULL value.
1331 * Using NULL values is enabled by using 'null' in the 'eval' TCA definition.
1332 *
1333 * @param string $table Name of the table
1334 * @param string $field Name of the field
1335 * @param array $row Accordant data
1336 * @param array $PA Parameters array with rendering instructions
1337 * @return bool
1338 */
1339 protected function isDisabledNullValueField($table, $field, array $row, array $PA) {
1340 $result = FALSE;
1341
1342 $config = $PA['fieldConf']['config'];
1343 if ($PA['itemFormElValue'] === NULL && !empty($config['eval'])
1344 && GeneralUtility::inList($config['eval'], 'null')
1345 && (empty($config['mode']) || $config['mode'] !== 'useOrOverridePlaceholder')) {
1346
1347 $result = TRUE;
1348 }
1349
1350 return $result;
1351 }
1352
1353 /**
1354 * Generation of TCEform elements of the type "text"
1355 * This will render a <textarea> OR RTE area form field, possibly with various control/validation features
1356 *
1357 * @param string $table The table name of the record
1358 * @param string $field The field name which this element is supposed to edit
1359 * @param array $row The record data array where the value(s) for the field can be found
1360 * @param array $PA An array with additional configuration options.
1361 * @return string The HTML code for the TCEform field
1362 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8. Use \TYPO3\CMS\Backend\Form\Element\TextElement
1363 */
1364 public function getSingleField_typeText($table, $field, $row, &$PA) {
1365 GeneralUtility::logDeprecatedFunction();
1366 return $item = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Form\Element\TextElement::class, $this)
1367 ->render($table, $field, $row, $PA);
1368 }
1369
1370 /**
1371 * Generation of TCEform elements of the type "check"
1372 * This will render a check-box OR an array of checkboxes
1373 *
1374 * @param string $table The table name of the record
1375 * @param string $field The field name which this element is supposed to edit
1376 * @param array $row The record data array where the value(s) for the field can be found
1377 * @param array $PA An array with additional configuration options.
1378 * @return string The HTML code for the TCEform field
1379 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8. Use \TYPO3\CMS\Backend\Form\Element\CheckboxElement
1380 */
1381 public function getSingleField_typeCheck($table, $field, $row, &$PA) {
1382 GeneralUtility::logDeprecatedFunction();
1383 return $item = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Form\Element\CheckboxElement::class, $this)
1384 ->render($table, $field, $row, $PA);
1385 }
1386
1387 /**
1388 * Generation of TCEform elements of the type "radio"
1389 * This will render a series of radio buttons.
1390 *
1391 * @param string $table The table name of the record
1392 * @param string $field The field name which this element is supposed to edit
1393 * @param array $row The record data array where the value(s) for the field can be found
1394 * @param array $PA An array with additional configuration options.
1395 * @return string The HTML code for the TCEform field
1396 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8. Use \TYPO3\CMS\Backend\Form\Element\RadioElement
1397 */
1398 public function getSingleField_typeRadio($table, $field, $row, &$PA) {
1399 GeneralUtility::logDeprecatedFunction();
1400 return $item = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Form\Element\RadioElement::class, $this)
1401 ->render($table, $field, $row, $PA);
1402 }
1403
1404 /**
1405 * Generation of TCEform elements of the type "select"
1406 * This will render a selector box element, or possibly a special construction with two selector boxes.
1407 * That depends on configuration.
1408 *
1409 * @param string $table The table name of the record
1410 * @param string $field The field name which this element is supposed to edit
1411 * @param array $row The record data array where the value(s) for the field can be found
1412 * @param array $PA An array with additional configuration options.
1413 * @return string The HTML code for the TCEform field
1414 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8. Use \TYPO3\CMS\Backend\Form\Element\SelectElement
1415 */
1416 public function getSingleField_typeSelect($table, $field, $row, &$PA) {
1417 GeneralUtility::logDeprecatedFunction();
1418 return $item = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Form\Element\SelectElement::class, $this)
1419 ->render($table, $field, $row, $PA);
1420 }
1421
1422 /**
1423 * Generation of TCEform elements of the type "group"
1424 * This will render a selectorbox into which elements from either the file system or database can be inserted. Relations.
1425 *
1426 * @param string $table The table name of the record
1427 * @param string $field The field name which this element is supposed to edit
1428 * @param array $row The record data array where the value(s) for the field can be found
1429 * @param array $PA An array with additional configuration options.
1430 * @return string The HTML code for the TCEform field
1431 * @deprecated since 7.0 - will be removed two versions later; Use \TYPO3\CMS\Backend\Form\Element\GroupElement
1432 */
1433 public function getSingleField_typeGroup($table, $field, $row, &$PA) {
1434 GeneralUtility::logDeprecatedFunction();
1435 return $item = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Form\Element\GroupElement::class, $this)
1436 ->render($table, $field, $row, $PA);
1437 }
1438
1439 /**
1440 * Generation of TCEform elements of the type "none"
1441 * This will render a non-editable display of the content of the field.
1442 *
1443 * @param string $table The table name of the record
1444 * @param string $field The field name which this element is supposed to edit
1445 * @param array $row The record data array where the value(s) for the field can be found
1446 * @param array $PA An array with additional configuration options.
1447 * @return string The HTML code for the TCEform field
1448 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8. Use \TYPO3\CMS\Backend\Form\Element\NoneElement
1449 */
1450 public function getSingleField_typeNone($table, $field, $row, &$PA) {
1451 GeneralUtility::logDeprecatedFunction();
1452 return $item = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Form\Element\NoneElement::class, $this)
1453 ->render($table, $field, $row, $PA);
1454 }
1455
1456 /**
1457 * HTML rendering of a value which is not editable.
1458 *
1459 * @param array $config Configuration for the display
1460 * @param string $itemValue The value to display
1461 * @return string The HTML code for the display
1462 * @see getSingleField_typeNone();
1463 */
1464 public function getSingleField_typeNone_render($config, $itemValue) {
1465 if ($config['format']) {
1466 $itemValue = $this->formatValue($config, $itemValue);
1467 }
1468 $rows = (int)$config['rows'];
1469 if ($rows > 1) {
1470 if (!$config['pass_content']) {
1471 $itemValue = nl2br(htmlspecialchars($itemValue));
1472 }
1473 // Like textarea
1474 $cols = MathUtility::forceIntegerInRange($config['cols'] ? $config['cols'] : 30, 5, $this->maxTextareaWidth);
1475 if (!$config['fixedRows']) {
1476 $origRows = ($rows = MathUtility::forceIntegerInRange($rows, 1, 20));
1477 if (strlen($itemValue) > $this->charsPerRow * 2) {
1478 $cols = $this->maxTextareaWidth;
1479 $rows = MathUtility::forceIntegerInRange(round(strlen($itemValue) / $this->charsPerRow), count(explode(LF, $itemValue)), 20);
1480 if ($rows < $origRows) {
1481 $rows = $origRows;
1482 }
1483 }
1484 }
1485
1486 $cols = round($cols * $this->form_largeComp);
1487 $width = ceil($cols * $this->form_rowsToStylewidth);
1488 // Hardcoded: 12 is the height of the font
1489 $height = $rows * 12;
1490 $item = '
1491 <div style="overflow:auto; height:' . $height . 'px; width:' . $width . 'px;" class="t3-tceforms-fieldReadOnly">'
1492 . $itemValue . IconUtility::getSpriteIcon('status-status-readonly') . '</div>';
1493 } else {
1494 if (!$config['pass_content']) {
1495 $itemValue = htmlspecialchars($itemValue);
1496 }
1497 $cols = $config['cols'] ? $config['cols'] : ($config['size'] ? $config['size'] : $this->maxInputWidth);
1498 $cols = round($cols * $this->form_largeComp);
1499 $width = ceil($cols * $this->form_rowsToStylewidth);
1500 // Overflow:auto crashes mozilla here. Title tag is useful when text is longer than the div box (overflow:hidden).
1501 $item = '
1502 <div style="overflow:hidden; width:' . $width . 'px;" class="t3-tceforms-fieldReadOnly" title="' . $itemValue . '">'
1503 . '<span class="nobr">' . ((string)$itemValue !== '' ? $itemValue : '&nbsp;') . '</span>'
1504 . IconUtility::getSpriteIcon('status-status-readonly') . '</div>';
1505 }
1506 return $item;
1507 }
1508
1509 /**
1510 * Handler for Flex Forms
1511 *
1512 * @param string $table The table name of the record
1513 * @param string $field The field name which this element is supposed to edit
1514 * @param array $row The record data array where the value(s) for the field can be found
1515 * @param array $PA An array with additional configuration options.
1516 * @return string The HTML code for the TCEform field
1517 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8. Use \TYPO3\CMS\Backend\Form\Element\FlexElement
1518 */
1519 public function getSingleField_typeFlex($table, $field, $row, &$PA) {
1520 GeneralUtility::logDeprecatedFunction();
1521 return $item = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Form\Element\FlexElement::class, $this)
1522 ->render($table, $field, $row, $PA);
1523 }
1524
1525 /**
1526 * Creates the language menu for FlexForms:
1527 *
1528 * @param array $languages
1529 * @param string $elName
1530 * @param array $selectedLanguage
1531 * @param bool $multi
1532 * @return string HTML for menu
1533 */
1534 public function getSingleField_typeFlex_langMenu($languages, $elName, $selectedLanguage, $multi = TRUE) {
1535 $opt = array();
1536 foreach ($languages as $lArr) {
1537 $opt[] = '<option value="' . htmlspecialchars($lArr['ISOcode']) . '"'
1538 . (in_array($lArr['ISOcode'], $selectedLanguage) ? ' selected="selected"' : '') . '>'
1539 . htmlspecialchars($lArr['title']) . '</option>';
1540 }
1541 $output = '<select id="' . str_replace('.', '', uniqid('tceforms-multiselect-', TRUE))
1542 . ' class="tceforms-select tceforms-multiselect tceforms-flexlangmenu" name="' . $elName . '[]"'
1543 . ($multi ? ' multiple="multiple" size="' . count($languages) . '"' : '') . '>' . implode('', $opt)
1544 . '</select>';
1545 return $output;
1546 }
1547
1548 /**
1549 * Creates the menu for selection of the sheets:
1550 *
1551 * @param array $sArr Sheet array for which to render the menu
1552 * @param string $elName Form element name of the field containing the sheet pointer
1553 * @param string $sheetKey Current sheet key
1554 * @return string HTML for menu
1555 */
1556 public function getSingleField_typeFlex_sheetMenu($sArr, $elName, $sheetKey) {
1557 $tCells = array();
1558 $pct = round(100 / count($sArr));
1559 foreach ($sArr as $sKey => $sheetCfg) {
1560 if ($this->getBackendUserAuthentication()->jsConfirmation(1)) {
1561 $onClick = 'if (confirm(TBE_EDITOR.labels.onChangeAlert) && TBE_EDITOR.checkSubmit(-1)){'
1562 . $this->elName($elName) . '.value=\'' . $sKey . '\'; TBE_EDITOR.submitForm()};';
1563 } else {
1564 $onClick = 'if(TBE_EDITOR.checkSubmit(-1)){ ' . $this->elName($elName) . '.value=\'' . $sKey . '\'; TBE_EDITOR.submitForm();}';
1565 }
1566 $tCells[] = '<td width="' . $pct . '%" style="'
1567 . ($sKey == $sheetKey ? 'background-color: #9999cc; font-weight: bold;' : 'background-color: #aaaaaa;')
1568 . ' cursor: hand;" onclick="' . htmlspecialchars($onClick) . '" align="center">'
1569 . ($sheetCfg['ROOT']['TCEforms']['sheetTitle'] ? $this->sL($sheetCfg['ROOT']['TCEforms']['sheetTitle']) : $sKey)
1570 . '</td>';
1571 }
1572 return '<table border="0" cellpadding="0" cellspacing="2" class="typo3-TCEforms-flexForm-sheetMenu"><tr>' . implode('', $tCells) . '</tr></table>';
1573 }
1574
1575 /**
1576 * Handler for unknown types.
1577 *
1578 * @param string $table The table name of the record
1579 * @param string $field The field name which this element is supposed to edit
1580 * @param array $row The record data array where the value(s) for the field can be found
1581 * @param array $PA An array with additional configuration options.
1582 * @return string The HTML code for the TCEform field
1583 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8. Use \TYPO3\CMS\Backend\Form\Element\UnknownElement
1584 */
1585 public function getSingleField_typeUnknown($table, $field, $row, &$PA) {
1586 GeneralUtility::logDeprecatedFunction();
1587 return $item = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Form\Element\UnknownElement::class, $this)
1588 ->render($table, $field, $row, $PA);
1589 }
1590
1591 /**
1592 * User defined field type
1593 *
1594 * @param string $table The table name of the record
1595 * @param string $field The field name which this element is supposed to edit
1596 * @param array $row The record data array where the value(s) for the field can be found
1597 * @param array $PA An array with additional configuration options.
1598 * @return string The HTML code for the TCEform field
1599 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8. Use \TYPO3\CMS\Backend\Form\Element\UserElement
1600 */
1601 public function getSingleField_typeUser($table, $field, $row, &$PA) {
1602 GeneralUtility::logDeprecatedFunction();
1603 return $item = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Form\Element\UserElement::class, $this)
1604 ->render($table, $field, $row, $PA);
1605 }
1606
1607 /************************************************************
1608 *
1609 * Field content processing
1610 *
1611 ************************************************************/
1612 /**
1613 * Format field content of various types if $config['format'] is set to date, filesize, ..., user
1614 * This is primarily for the field type none but can be used for user field types for example
1615 *
1616 * @param array $config Configuration for the display
1617 * @param string $itemValue The value to display
1618 * @return string Formatted Field content
1619 */
1620 public function formatValue($config, $itemValue) {
1621 $format = trim($config['format']);
1622 switch ($format) {
1623 case 'date':
1624 if ($itemValue) {
1625 $option = trim($config['format.']['option']);
1626 if ($option) {
1627 if ($config['format.']['strftime']) {
1628 $value = strftime($option, $itemValue);
1629 } else {
1630 $value = date($option, $itemValue);
1631 }
1632 } else {
1633 $value = date('d-m-Y', $itemValue);
1634 }
1635 } else {
1636 $value = '';
1637 }
1638 if ($config['format.']['appendAge']) {
1639 $age = BackendUtility::calcAge(
1640 $GLOBALS['EXEC_TIME'] - $itemValue,
1641 $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.minutesHoursDaysYears')
1642 );
1643 $value .= ' (' . $age . ')';
1644 }
1645 $itemValue = $value;
1646 break;
1647 case 'datetime':
1648 // compatibility with "eval" (type "input")
1649 if ($itemValue !== '') {
1650 $itemValue = date('H:i d-m-Y', (int)$itemValue);
1651 }
1652 break;
1653 case 'time':
1654 // compatibility with "eval" (type "input")
1655 if ($itemValue !== '') {
1656 $itemValue = date('H:i', (int)$itemValue);
1657 }
1658 break;
1659 case 'timesec':
1660 // compatibility with "eval" (type "input")
1661 if ($itemValue !== '') {
1662 $itemValue = date('H:i:s', (int)$itemValue);
1663 }
1664 break;
1665 case 'year':
1666 // compatibility with "eval" (type "input")
1667 if ($itemValue !== '') {
1668 $itemValue = date('Y', (int)$itemValue);
1669 }
1670 break;
1671 case 'int':
1672 $baseArr = array('dec' => 'd', 'hex' => 'x', 'HEX' => 'X', 'oct' => 'o', 'bin' => 'b');
1673 $base = trim($config['format.']['base']);
1674 $format = $baseArr[$base] ?: 'd';
1675 $itemValue = sprintf('%' . $format, $itemValue);
1676 break;
1677 case 'float':
1678 $precision = MathUtility::forceIntegerInRange($config['format.']['precision'], 1, 10, 2);
1679 $itemValue = sprintf('%.' . $precision . 'f', $itemValue);
1680 break;
1681 case 'number':
1682 $format = trim($config['format.']['option']);
1683 $itemValue = sprintf('%' . $format, $itemValue);
1684 break;
1685 case 'md5':
1686 $itemValue = md5($itemValue);
1687 break;
1688 case 'filesize':
1689 // We need to cast to int here, otherwise empty values result in empty output,
1690 // but we expect zero.
1691 $value = GeneralUtility::formatSize((int)$itemValue);
1692 if ($config['format.']['appendByteSize']) {
1693 $value .= ' (' . $itemValue . ')';
1694 }
1695 $itemValue = $value;
1696 break;
1697 case 'user':
1698 $func = trim($config['format.']['userFunc']);
1699 if ($func) {
1700 $params = array(
1701 'value' => $itemValue,
1702 'args' => $config['format.']['userFunc'],
1703 'config' => $config,
1704 'pObj' => &$this
1705 );
1706 $itemValue = GeneralUtility::callUserFunction($func, $params, $this);
1707 }
1708 break;
1709 default:
1710 // Do nothing e.g. when $format === ''
1711 }
1712 return $itemValue;
1713 }
1714
1715 /************************************************************
1716 *
1717 * "Configuration" fetching/processing functions
1718 *
1719 ************************************************************/
1720 /**
1721 * Calculate and return the current "types" pointer value for a record
1722 *
1723 * @param string $table The table name. MUST be in $GLOBALS['TCA']
1724 * @param array $row The row from the table, should contain at least the "type" field, if applicable.
1725 * @return string Return the "type" value for this record, ready to pick a "types" configuration from the $GLOBALS['TCA'] array.
1726 * @throws \RuntimeException
1727 */
1728 public function getRTypeNum($table, $row) {
1729 $typeNum = 0;
1730 $field = $GLOBALS['TCA'][$table]['ctrl']['type'];
1731 if ($field) {
1732 if (strpos($field, ':') !== FALSE) {
1733 list($pointerField, $foreignTypeField) = explode(':', $field);
1734 $fieldConfig = $GLOBALS['TCA'][$table]['columns'][$pointerField]['config'];
1735 $relationType = $fieldConfig['type'];
1736 if ($relationType === 'select') {
1737 $foreignUid = $row[$pointerField];
1738 $foreignTable = $fieldConfig['foreign_table'];
1739 } elseif ($relationType === 'group') {
1740 $values = $this->extractValuesOnlyFromValueLabelList($row[$pointerField]);
1741 list(, $foreignUid) = GeneralUtility::revExplode('_', $values[0], 2);
1742 $allowedTables = explode(',', $fieldConfig['allowed']);
1743 // Always take the first configured table.
1744 $foreignTable = $allowedTables[0];
1745 } else {
1746 throw new \RuntimeException('TCA Foreign field pointer fields are only allowed to be used with group or select field types.', 1325861239);
1747 }
1748 if ($foreignUid) {
1749 $foreignRow = BackendUtility::getRecord($foreignTable, $foreignUid, $foreignTypeField);
1750 $this->registerDefaultLanguageData($foreignTable, $foreignRow);
1751 if ($foreignRow[$foreignTypeField]) {
1752 $foreignTypeFieldConfig = $GLOBALS['TCA'][$table]['columns'][$field];
1753 $typeNum = $this->getLanguageOverlayRawValue($foreignTable, $foreignRow, $foreignTypeField, $foreignTypeFieldConfig);
1754 }
1755 }
1756 } else {
1757 $typeFieldConfig = $GLOBALS['TCA'][$table]['columns'][$field];
1758 $typeNum = $this->getLanguageOverlayRawValue($table, $row, $field, $typeFieldConfig);
1759 }
1760 }
1761 if (empty($typeNum)) {
1762 // If that value is an empty string, set it to "0" (zero)
1763 $typeNum = 0;
1764 }
1765 // If current typeNum doesn't exist, set it to 0 (or to 1 for historical reasons, if 0 doesn't exist)
1766 if (!$GLOBALS['TCA'][$table]['types'][$typeNum]) {
1767 $typeNum = $GLOBALS['TCA'][$table]['types']['0'] ? 0 : 1;
1768 }
1769 // Force to string. Necessary for eg '-1' to be recognized as a type value.
1770 $typeNum = (string)$typeNum;
1771 return $typeNum;
1772 }
1773
1774 /**
1775 * Used to adhoc-rearrange the field order normally set in the [types][showitem] list
1776 *
1777 * @param array $fields A [types][showitem] list of fields, exploded by ",
1778 * @return array Returns rearranged version (keys are changed around as well.)
1779 * @see getMainFields()
1780 */
1781 public function rearrange($fields) {
1782 $fO = array_flip(GeneralUtility::trimExplode(',', $this->fieldOrder, TRUE));
1783 $newFields = array();
1784 foreach ($fields as $cc => $content) {
1785 $cP = GeneralUtility::trimExplode(';', $content);
1786 if (isset($fO[$cP[0]])) {
1787 $newFields[$fO[$cP[0]]] = $content;
1788 unset($fields[$cc]);
1789 }
1790 }
1791 ksort($newFields);
1792 // Candidate for GeneralUtility::array_merge() if integer-keys will some day make trouble...
1793 $fields = array_merge($newFields, $fields);
1794 return $fields;
1795 }
1796
1797 /**
1798 * Producing an array of field names NOT to display in the form,
1799 * based on settings from subtype_value_field, bitmask_excludelist_bits etc.
1800 * Notice, this list is in NO way related to the "excludeField" flag
1801 *
1802 * @param string $table Table name, MUST be in $GLOBALS['TCA']
1803 * @param array $row A record from table.
1804 * @param string $typeNum A "type" pointer value, probably the one calculated based on the record array.
1805 * @return array Array with fieldnames as values. The fieldnames are those which should NOT be displayed "anyways
1806 * @see getMainFields()
1807 */
1808 public function getExcludeElements($table, $row, $typeNum) {
1809 // Init:
1810 $excludeElements = array();
1811 // If a subtype field is defined for the type
1812 if ($GLOBALS['TCA'][$table]['types'][$typeNum]['subtype_value_field']) {
1813 $sTfield = $GLOBALS['TCA'][$table]['types'][$typeNum]['subtype_value_field'];
1814 if (trim($GLOBALS['TCA'][$table]['types'][$typeNum]['subtypes_excludelist'][$row[$sTfield]])) {
1815 $excludeElements = GeneralUtility::trimExplode(',', $GLOBALS['TCA'][$table]['types'][$typeNum]['subtypes_excludelist'][$row[$sTfield]], TRUE);
1816 }
1817 }
1818 // If a bitmask-value field has been configured, then find possible fields to exclude based on that:
1819 if ($GLOBALS['TCA'][$table]['types'][$typeNum]['bitmask_value_field']) {
1820 $sTfield = $GLOBALS['TCA'][$table]['types'][$typeNum]['bitmask_value_field'];
1821 $sTValue = MathUtility::forceIntegerInRange($row[$sTfield], 0);
1822 if (is_array($GLOBALS['TCA'][$table]['types'][$typeNum]['bitmask_excludelist_bits'])) {
1823 foreach ($GLOBALS['TCA'][$table]['types'][$typeNum]['bitmask_excludelist_bits'] as $bitKey => $eList) {
1824 $bit = substr($bitKey, 1);
1825 if (MathUtility::canBeInterpretedAsInteger($bit)) {
1826 $bit = MathUtility::forceIntegerInRange($bit, 0, 30);
1827 if ($bitKey[0] === '-' && !($sTValue & pow(2, $bit)) || $bitKey[0] === '+' && $sTValue & pow(2, $bit)) {
1828 $excludeElements = array_merge($excludeElements, GeneralUtility::trimExplode(',', $eList, TRUE));
1829 }
1830 }
1831 }
1832 }
1833 }
1834 // Return the array of elements:
1835 return $excludeElements;
1836 }
1837
1838 /**
1839 * Finds possible field to add to the form, based on subtype fields.
1840 *
1841 * @param string $table Table name, MUST be in $GLOBALS['TCA']
1842 * @param array $row A record from table.
1843 * @param string $typeNum A "type" pointer value, probably the one calculated based on the record array.
1844 * @return array An array containing two values: 1) Another array containing field names to add and 2) the subtype value field.
1845 * @see getMainFields()
1846 */
1847 public function getFieldsToAdd($table, $row, $typeNum) {
1848 // Init:
1849 $addElements = array();
1850 // If a subtype field is defined for the type
1851 $sTfield = '';
1852 if ($GLOBALS['TCA'][$table]['types'][$typeNum]['subtype_value_field']) {
1853 $sTfield = $GLOBALS['TCA'][$table]['types'][$typeNum]['subtype_value_field'];
1854 if (trim($GLOBALS['TCA'][$table]['types'][$typeNum]['subtypes_addlist'][$row[$sTfield]])) {
1855 $addElements = GeneralUtility::trimExplode(',', $GLOBALS['TCA'][$table]['types'][$typeNum]['subtypes_addlist'][$row[$sTfield]], TRUE);
1856 }
1857 }
1858 // Return the return
1859 return array($addElements, $sTfield);
1860 }
1861
1862 /**
1863 * Merges the current [types][showitem] array with the array of fields to add for the current subtype field of the "type" value.
1864 *
1865 * @param array $fields A [types][showitem] list of fields, exploded by ",
1866 * @param array $fieldsToAdd The output from getFieldsToAdd()
1867 * @param string $table The table name, if we want to consider it's palettes when positioning the new elements
1868 * @return array Return the modified $fields array.
1869 * @see getMainFields(),getFieldsToAdd()
1870 */
1871 public function mergeFieldsWithAddedFields($fields, $fieldsToAdd, $table = '') {
1872 if (count($fieldsToAdd[0])) {
1873 $c = 0;
1874 $found = FALSE;
1875 foreach ($fields as $fieldInfo) {
1876 list($fieldName, $label, $paletteName) = GeneralUtility::trimExplode(';', $fieldInfo);
1877 if ($fieldName === $fieldsToAdd[1]) {
1878 $found = TRUE;
1879 } elseif ($fieldName === '--palette--' && $paletteName && $table !== '') {
1880 // Look inside the palette
1881 if (is_array($GLOBALS['TCA'][$table]['palettes'][$paletteName])) {
1882 $itemList = $GLOBALS['TCA'][$table]['palettes'][$paletteName]['showitem'];
1883 if ($itemList) {
1884 $paletteFields = GeneralUtility::trimExplode(',', $itemList, TRUE);
1885 foreach ($paletteFields as $info) {
1886 $fieldParts = GeneralUtility::trimExplode(';', $info);
1887 $theField = $fieldParts[0];
1888 if ($theField === $fieldsToAdd[1]) {
1889 $found = TRUE;
1890 break 1;
1891 }
1892 }
1893 }
1894 }
1895 }
1896 if ($found) {
1897 array_splice($fields, $c + 1, 0, $fieldsToAdd[0]);
1898 break;
1899 }
1900 $c++;
1901 }
1902 }
1903 return $fields;
1904 }
1905
1906 /**
1907 * Returns TSconfig for table/row
1908 * Multiple requests to this function will return cached content so there is no performance loss in calling
1909 * this many times since the information is looked up only once.
1910 *
1911 * @param string $table The table name
1912 * @param array $row The table row (Should at least contain the "uid" value, even if "NEW..." string. The "pid" field is important as well, and negative values will be intepreted as pointing to a record from the same table.)
1913 * @param string $field Optionally you can specify the field name as well. In that case the TSconfig for the field is returned.
1914 * @return mixed The TSconfig values (probably in an array)
1915 * @see BackendUtility::getTCEFORM_TSconfig()
1916 */
1917 public function setTSconfig($table, $row, $field = '') {
1918 $mainKey = $table . ':' . $row['uid'];
1919 if (!isset($this->cachedTSconfig[$mainKey])) {
1920 $this->cachedTSconfig[$mainKey] = BackendUtility::getTCEFORM_TSconfig($table, $row);
1921 }
1922 if ($field) {
1923 return $this->cachedTSconfig[$mainKey][$field];
1924 } else {
1925 return $this->cachedTSconfig[$mainKey];
1926 }
1927 }
1928
1929 /**
1930 * Overrides the TCA field configuration by TSconfig settings.
1931 *
1932 * Example TSconfig: TCEform.<table>.<field>.config.appearance.useSortable = 1
1933 * This overrides the setting in $GLOBALS['TCA'][<table>]['columns'][<field>]['config']['appearance']['useSortable'].
1934 *
1935 * @param array $fieldConfig $GLOBALS['TCA'] field configuration
1936 * @param array $TSconfig TSconfig
1937 * @return array Changed TCA field configuration
1938 */
1939 public function overrideFieldConf($fieldConfig, $TSconfig) {
1940 if (is_array($TSconfig)) {
1941 $TSconfig = GeneralUtility::removeDotsFromTS($TSconfig);
1942 $type = $fieldConfig['type'];
1943 if (is_array($TSconfig['config']) && is_array($this->allowOverrideMatrix[$type])) {
1944 // Check if the keys in TSconfig['config'] are allowed to override TCA field config:
1945 foreach (array_keys($TSconfig['config']) as $key) {
1946 if (!in_array($key, $this->allowOverrideMatrix[$type], TRUE)) {
1947 unset($TSconfig['config'][$key]);
1948 }
1949 }
1950 // Override $GLOBALS['TCA'] field config by remaining TSconfig['config']:
1951 if (count($TSconfig['config'])) {
1952 ArrayUtility::mergeRecursiveWithOverrule($fieldConfig, $TSconfig['config']);
1953 }
1954 }
1955 }
1956 return $fieldConfig;
1957 }
1958
1959 /**
1960 * Returns the "special" configuration (from the "types" "showitem" list) for a fieldname based on input table/record
1961 * (Not used anywhere...?)
1962 *
1963 * @param string $table The table name
1964 * @param array $row The table row (Should at least contain the "uid" value, even if "NEW..." string. The "pid" field is important as well, and negative values will be intepreted as pointing to a record from the same table.)
1965 * @param string $field Specify the field name.
1966 * @return array|NULL
1967 * @see getSpecConfFromString(), BackendUtility::getTCAtypes()
1968 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8
1969 */
1970 public function getSpecConfForField($table, $row, $field) {
1971 GeneralUtility::logDeprecatedFunction();
1972 // Finds the current "types" configuration for the table/row:
1973 $types_fieldConfig = BackendUtility::getTCAtypes($table, $row);
1974 // If this is an array, then traverse it:
1975 if (is_array($types_fieldConfig)) {
1976 foreach ($types_fieldConfig as $vconf) {
1977 // If the input field name matches one found in the 'types' list, then return the 'special' configuration.
1978 if ($vconf['field'] == $field) {
1979 return $vconf['spec'];
1980 }
1981 }
1982 }
1983 return NULL;
1984 }
1985
1986 /**
1987 * Returns the "special" configuration of an "extra" string (non-parsed)
1988 *
1989 * @param string $extraString The "Part 4" of the fields configuration in "types" "showitem" lists.
1990 * @param string $defaultExtras The ['defaultExtras'] value from field configuration
1991 * @return array An array with the special options in.
1992 * @see getSpecConfForField(), BackendUtility::getSpecConfParts()
1993 */
1994 public function getSpecConfFromString($extraString, $defaultExtras) {
1995 return BackendUtility::getSpecConfParts($extraString, $defaultExtras);
1996 }
1997
1998 /**
1999 * Loads the elements of a palette (collection of secondary options) in an array.
2000 *
2001 * @param string $table The table name
2002 * @param array $row The row array
2003 * @param string $palette The palette number/pointer
2004 * @param string $itemList Optional alternative list of fields for the palette
2005 * @return array The palette elements
2006 */
2007 public function loadPaletteElements($table, $row, $palette, $itemList = '') {
2008 $parts = array();
2009 // Getting excludeElements, if any.
2010 if (!is_array($this->excludeElements)) {
2011 $this->excludeElements = $this->getExcludeElements($table, $row, $this->getRTypeNum($table, $row));
2012 }
2013 // Load the palette TCEform elements
2014 if ($GLOBALS['TCA'][$table] && (is_array($GLOBALS['TCA'][$table]['palettes'][$palette]) || $itemList)) {
2015 $itemList = $itemList ? $itemList : $GLOBALS['TCA'][$table]['palettes'][$palette]['showitem'];
2016 if ($itemList) {
2017 $fields = GeneralUtility::trimExplode(',', $itemList, TRUE);
2018 foreach ($fields as $info) {
2019 $fieldParts = GeneralUtility::trimExplode(';', $info);
2020 $theField = $fieldParts[0];
2021 if ($theField === '--linebreak--') {
2022 $parts[]['NAME'] = '--linebreak--';
2023 } elseif (!in_array($theField, $this->excludeElements) && $GLOBALS['TCA'][$table]['columns'][$theField]) {
2024 $this->palFieldArr[$palette][] = $theField;
2025 $elem = $this->getSingleField($table, $theField, $row, $fieldParts[1], 1, '', $fieldParts[2]);
2026 if (is_array($elem)) {
2027 $parts[] = $elem;
2028 }
2029 }
2030 }
2031 }
2032 }
2033 return $parts;
2034 }
2035
2036 /************************************************************
2037 *
2038 * Display of localized content etc.
2039 *
2040 ************************************************************/
2041 /**
2042 * Will register data from original language records if the current record is a translation of another.
2043 * The original data is shown with the edited record in the form.
2044 * The information also includes possibly diff-views of what changed in the original record.
2045 * Function called from outside (see alt_doc.php + quick edit) before rendering a form for a record
2046 *
2047 * @param string $table Table name of the record being edited
2048 * @param array $rec Record array of the record being edited
2049 * @return void
2050 */
2051 public function registerDefaultLanguageData($table, $rec) {
2052 // Add default language:
2053 if (
2054 $GLOBALS['TCA'][$table]['ctrl']['languageField'] && $rec[$GLOBALS['TCA'][$table]['ctrl']['languageField']] > 0
2055 && $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']
2056 && (int)$rec[$GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']] > 0
2057 ) {
2058 $lookUpTable = $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerTable']
2059 ? $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerTable']
2060 : $table;
2061 // Get data formatted:
2062 $this->defaultLanguageData[$table . ':' . $rec['uid']] = BackendUtility::getRecordWSOL(
2063 $lookUpTable,
2064 (int)$rec[$GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']]
2065 );
2066 // Get data for diff:
2067 if ($GLOBALS['TCA'][$table]['ctrl']['transOrigDiffSourceField']) {
2068 $this->defaultLanguageData_diff[$table . ':' . $rec['uid']] = unserialize($rec[$GLOBALS['TCA'][$table]['ctrl']['transOrigDiffSourceField']]);
2069 }
2070 // If there are additional preview languages, load information for them also:
2071 $prLang = $this->getAdditionalPreviewLanguages();
2072 foreach ($prLang as $prL) {
2073 /** @var $t8Tools \TYPO3\CMS\Backend\Configuration\TranslationConfigurationProvider */
2074 $t8Tools = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Configuration\TranslationConfigurationProvider::class);
2075 $tInfo = $t8Tools->translationInfo($lookUpTable, (int)$rec[$GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']], $prL['uid']);
2076 if (is_array($tInfo['translations']) && is_array($tInfo['translations'][$prL['uid']])) {
2077 $this->additionalPreviewLanguageData[$table . ':' . $rec['uid']][$prL['uid']] = BackendUtility::getRecordWSOL($table, (int)$tInfo['translations'][$prL['uid']]['uid']);
2078 }
2079 }
2080 }
2081 }
2082
2083 /**
2084 * Creates language-overlay for a field value
2085 * This means the requested field value will be overridden with the data from the default language.
2086 * Can be used to render read only fields for example.
2087 *
2088 * @param string $table Table name of the record being edited
2089 * @param array $row Record array of the record being edited in current language
2090 * @param string $field Field name represented by $item
2091 * @param array $fieldConf Content of $PA['fieldConf']
2092 * @return string Unprocessed field value merged with default language data if needed
2093 */
2094 public function getLanguageOverlayRawValue($table, $row, $field, $fieldConf) {
2095 $value = $row[$field];
2096 if (is_array($this->defaultLanguageData[$table . ':' . $row['uid']])) {
2097 if (
2098 $fieldConf['l10n_mode'] == 'exclude'
2099 || $fieldConf['l10n_mode'] == 'mergeIfNotBlank' && trim($this->defaultLanguageData[$table . ':' . $row['uid']][$field]) !== ''
2100 ) {
2101 $value = $this->defaultLanguageData[$table . ':' . $row['uid']][$field];
2102 }
2103 }
2104 return $value;
2105 }
2106
2107 /**
2108 * Renders the display of default language record content around current field.
2109 * Will render content if any is found in the internal array, $this->defaultLanguageData,
2110 * depending on registerDefaultLanguageData() being called prior to this.
2111 *
2112 * @param string $table Table name of the record being edited
2113 * @param string $field Field name represented by $item
2114 * @param array $row Record array of the record being edited
2115 * @param string $item HTML of the form field. This is what we add the content to.
2116 * @return string Item string returned again, possibly with the original value added to.
2117 * @see getSingleField(), registerDefaultLanguageData()
2118 */
2119 public function renderDefaultLanguageContent($table, $field, $row, $item) {
2120 if (is_array($this->defaultLanguageData[$table . ':' . $row['uid']])) {
2121 $defaultLanguageValue = BackendUtility::getProcessedValue($table, $field, $this->defaultLanguageData[$table . ':' . $row['uid']][$field], 0, 1);
2122 $fieldConfig = $GLOBALS['TCA'][$table]['columns'][$field];
2123 // Don't show content if it's for IRRE child records:
2124 if ($fieldConfig['config']['type'] != 'inline') {
2125 if ($defaultLanguageValue !== '') {
2126 $item .= '<div class="t3-form-original-language">' . $this->getLanguageIcon($table, $row, 0)
2127 . $this->getMergeBehaviourIcon($fieldConfig['l10n_mode'])
2128 . $this->previewFieldValue($defaultLanguageValue, $fieldConfig, $field) . '</div>';
2129 }
2130 $previewLanguages = $this->getAdditionalPreviewLanguages();
2131 foreach ($previewLanguages as $previewLanguage) {
2132 $defaultLanguageValue = BackendUtility::getProcessedValue($table, $field, $this->additionalPreviewLanguageData[$table . ':' . $row['uid']][$previewLanguage['uid']][$field], 0, 1);
2133 if ($defaultLanguageValue !== '') {
2134 $item .= '<div class="t3-form-original-language">'
2135 . $this->getLanguageIcon($table, $row, ('v' . $previewLanguage['ISOcode']))
2136 . $this->getMergeBehaviourIcon($fieldConfig['l10n_mode'])
2137 . $this->previewFieldValue($defaultLanguageValue, $fieldConfig, $field) . '</div>';
2138 }
2139 }
2140 }
2141 }
2142 return $item;
2143 }
2144
2145 /**
2146 * Renders the diff-view of default language record content compared with what the record was originally translated from.
2147 * Will render content if any is found in the internal array, $this->defaultLanguageData,
2148 * depending on registerDefaultLanguageData() being called prior to this.
2149 *
2150 * @param string $table Table name of the record being edited
2151 * @param string $field Field name represented by $item
2152 * @param array $row Record array of the record being edited
2153 * @param string $item HTML of the form field. This is what we add the content to.
2154 * @return string Item string returned again, possibly with the original value added to.
2155 * @see getSingleField(), registerDefaultLanguageData()
2156 */
2157 public function renderDefaultLanguageDiff($table, $field, $row, $item) {
2158 if (is_array($this->defaultLanguageData_diff[$table . ':' . $row['uid']])) {
2159 // Initialize:
2160 $dLVal = array(
2161 'old' => $this->defaultLanguageData_diff[$table . ':' . $row['uid']],
2162 'new' => $this->defaultLanguageData[$table . ':' . $row['uid']]
2163 );
2164 // There must be diff-data:
2165 if (isset($dLVal['old'][$field])) {
2166 if ((string)$dLVal['old'][$field] !== (string)$dLVal['new'][$field]) {
2167 // Create diff-result:
2168 $t3lib_diff_Obj = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Utility\DiffUtility::class);
2169 $diffres = $t3lib_diff_Obj->makeDiffDisplay(
2170 BackendUtility::getProcessedValue($table, $field, $dLVal['old'][$field], 0, 1),
2171 BackendUtility::getProcessedValue($table, $field, $dLVal['new'][$field], 0, 1)
2172 );
2173 $item .= '<div class="t3-form-original-language-diff">
2174 <div class="t3-form-original-language-diffheader">' . htmlspecialchars($this->getLL('l_changeInOrig')) . '</div>
2175 <div class="t3-form-original-language-diffcontent">' . $diffres . '</div>
2176 </div>';
2177 }
2178 }
2179 }
2180 return $item;
2181 }
2182
2183 /**
2184 * Renders the diff-view of vDEF fields in flexforms
2185 *
2186 * @param array $vArray Record array of the record being edited
2187 * @param string $vDEFkey HTML of the form field. This is what we add the content to.
2188 * @return string Item string returned again, possibly with the original value added to.
2189 * @see getSingleField(), registerDefaultLanguageData()
2190 */
2191 public function renderVDEFDiff($vArray, $vDEFkey) {
2192 $item = NULL;
2193 if (
2194 $GLOBALS['TYPO3_CONF_VARS']['BE']['flexFormXMLincludeDiffBase'] && isset($vArray[$vDEFkey . '.vDEFbase'])
2195 && (string)$vArray[$vDEFkey . '.vDEFbase'] !== (string)$vArray['vDEF']
2196 ) {
2197 // Create diff-result:
2198 $t3lib_diff_Obj = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Utility\DiffUtility::class);
2199 $diffres = $t3lib_diff_Obj->makeDiffDisplay($vArray[$vDEFkey . '.vDEFbase'], $vArray['vDEF']);
2200 $item = '<div class="typo3-TCEforms-diffBox">' . '<div class="typo3-TCEforms-diffBox-header">'
2201 . htmlspecialchars($this->getLL('l_changeInOrig')) . ':</div>' . $diffres . '</div>';
2202 }
2203 return $item;
2204 }
2205
2206 /************************************************************
2207 *
2208 * Form element helper functions
2209 *
2210 ************************************************************/
2211 /**
2212 * Prints the selector box form-field for the db/file/select elements (multiple)
2213 *
2214 * @param string $fName Form element name
2215 * @param string $mode Mode "db", "file" (internal_type for the "group" type) OR blank (then for the "select" type)
2216 * @param string $allowed Commalist of "allowed
2217 * @param array $itemArray The array of items. For "select" and "group"/"file" this is just a set of value. For "db" its an array of arrays with table/uid pairs.
2218 * @param string $selector Alternative selector box.
2219 * @param array $params An array of additional parameters, eg: "size", "info", "headers" (array with "selector" and "items"), "noBrowser", "thumbnails
2220 * @param string $onFocus On focus attribute string
2221 * @param string $table (optional) Table name processing for
2222 * @param string $field (optional) Field of table name processing for
2223 * @param string $uid (optional) uid of table record processing for
2224 * @param array $config (optional) The TCA field config
2225 * @return string The form fields for the selection.
2226 * @throws \UnexpectedValueException
2227 */
2228 public function dbFileIcons($fName, $mode, $allowed, $itemArray, $selector = '', $params = array(), $onFocus = '', $table = '', $field = '', $uid = '', $config = array()) {
2229 $disabled = '';
2230 if ($this->renderReadonly || $params['readOnly']) {
2231 $disabled = ' disabled="disabled"';
2232 }
2233 // Sets a flag which means some JavaScript is included on the page to support this element.
2234 $this->printNeededJS['dbFileIcons'] = 1;
2235 // INIT
2236 $uidList = array();
2237 $opt = array();
2238 $itemArrayC = 0;
2239 // Creating <option> elements:
2240 if (is_array($itemArray)) {
2241 $itemArrayC = count($itemArray);
2242 switch ($mode) {
2243 case 'db':
2244 foreach ($itemArray as $pp) {
2245 $pRec = BackendUtility::getRecordWSOL($pp['table'], $pp['id']);
2246 if (is_array($pRec)) {
2247 $pTitle = BackendUtility::getRecordTitle($pp['table'], $pRec, FALSE, TRUE);
2248 $pUid = $pp['table'] . '_' . $pp['id'];
2249 $uidList[] = $pUid;
2250 $title = htmlspecialchars($pTitle);
2251 $opt[] = '<option value="' . htmlspecialchars($pUid) . '" title="' . $title . '">' . $title . '</option>';
2252 }
2253 }
2254 break;
2255 case 'file_reference':
2256
2257 case 'file':
2258 foreach ($itemArray as $item) {
2259 $itemParts = explode('|', $item);
2260 $uidList[] = ($pUid = ($pTitle = $itemParts[0]));
2261 $title = htmlspecialchars(rawurldecode($itemParts[1]));
2262 $opt[] = '<option value="' . htmlspecialchars(rawurldecode($itemParts[0])) . '" title="' . $title . '">' . $title . '</option>';
2263 }
2264 break;
2265 case 'folder':
2266 foreach ($itemArray as $pp) {
2267 $pParts = explode('|', $pp);
2268 $uidList[] = ($pUid = ($pTitle = $pParts[0]));
2269 $title = htmlspecialchars(rawurldecode($pParts[0]));
2270 $opt[] = '<option value="' . htmlspecialchars(rawurldecode($pParts[0])) . '" title="' . $title . '">' . $title . '</option>';
2271 }
2272 break;
2273 default:
2274 foreach ($itemArray as $pp) {
2275 $pParts = explode('|', $pp, 2);
2276 $uidList[] = ($pUid = $pParts[0]);
2277 $pTitle = $pParts[1];
2278 $title = htmlspecialchars(rawurldecode($pTitle));
2279 $opt[] = '<option value="' . htmlspecialchars(rawurldecode($pUid)) . '" title="' . $title . '">' . $title . '</option>';
2280 }
2281 }
2282 }
2283 // Create selector box of the options
2284 $sSize = $params['autoSizeMax']
2285 ? MathUtility::forceIntegerInRange($itemArrayC + 1, MathUtility::forceIntegerInRange($params['size'], 1), $params['autoSizeMax'])
2286 : $params['size'];
2287 if (!$selector) {
2288 $isMultiple = $params['maxitems'] != 1 && $params['size'] != 1;
2289 $selector = '<select id="' . str_replace('.', '', uniqid('tceforms-multiselect-', TRUE)) . '" '
2290 . ($params['noList'] ? 'style="display: none"' : 'size="' . $sSize . '" ' . $this->insertDefStyle('group', 'tceforms-multiselect'))
2291 . ($isMultiple ? ' multiple="multiple"' : '')
2292 . ' name="' . $fName . '_list" ' . $onFocus . $params['style'] . $disabled . '>' . implode('', $opt)
2293 . '</select>';
2294 }
2295 $icons = array(
2296 'L' => array(),
2297 'R' => array()
2298 );
2299 $rOnClickInline = '';
2300 if (!$params['readOnly'] && !$params['noList']) {
2301 if (!$params['noBrowser']) {
2302 // Check against inline uniqueness
2303 $inlineParent = $this->inline->getStructureLevel(-1);
2304 $aOnClickInline = '';
2305 if (is_array($inlineParent) && $inlineParent['uid']) {
2306 if ($inlineParent['config']['foreign_table'] == $table && $inlineParent['config']['foreign_unique'] == $field) {
2307 $objectPrefix = $this->inline->inlineNames['object'] . InlineElement::Structure_Separator . $table;
2308 $aOnClickInline = $objectPrefix . '|inline.checkUniqueElement|inline.setUniqueElement';
2309 $rOnClickInline = 'inline.revertUnique(\'' . $objectPrefix . '\',null,\'' . $uid . '\');';
2310 }
2311 }
2312 if (is_array($config['appearance']) && isset($config['appearance']['elementBrowserType'])) {
2313 $elementBrowserType = $config['appearance']['elementBrowserType'];
2314 } else {
2315 $elementBrowserType = $mode;
2316 }
2317 if (is_array($config['appearance']) && isset($config['appearance']['elementBrowserAllowed'])) {
2318 $elementBrowserAllowed = $config['appearance']['elementBrowserAllowed'];
2319 } else {
2320 $elementBrowserAllowed = $allowed;
2321 }
2322 $aOnClick = 'setFormValueOpenBrowser(\'' . $elementBrowserType . '\',\''
2323 . ($fName . '|||' . $elementBrowserAllowed . '|' . $aOnClickInline) . '\'); return false;';
2324 $spriteIcon = IconUtility::getSpriteIcon('actions-insert-record', array(
2325 'title' => htmlspecialchars($this->getLL('l_browse_' . ($mode == 'db' ? 'db' : 'file')))
2326 ));
2327 $icons['R'][] = '<a href="#" onclick="' . htmlspecialchars($aOnClick) . '" class="btn btn-default">' . $spriteIcon . '</a>';
2328 }
2329 if (!$params['dontShowMoveIcons']) {
2330 if ($sSize >= 5) {
2331 $icons['L'][] = IconUtility::getSpriteIcon('actions-move-to-top', array(
2332 'data-fieldname' => $fName,
2333 'class' => 't3-btn t3-btn-moveoption-top',
2334 'title' => htmlspecialchars($this->getLL('l_move_to_top'))
2335 ));
2336 }
2337 $icons['L'][] = IconUtility::getSpriteIcon('actions-move-up', array(
2338 'data-fieldname' => $fName,
2339 'class' => 't3-btn t3-btn-moveoption-up',
2340 'title' => htmlspecialchars($this->getLL('l_move_up'))
2341 ));
2342 $icons['L'][] = IconUtility::getSpriteIcon('actions-move-down', array(
2343 'data-fieldname' => $fName,
2344 'class' => 't3-btn t3-btn-moveoption-down',
2345 'title' => htmlspecialchars($this->getLL('l_move_down'))
2346 ));
2347 if ($sSize >= 5) {
2348 $icons['L'][] = IconUtility::getSpriteIcon('actions-move-to-bottom', array(
2349 'data-fieldname' => $fName,
2350 'class' => 't3-btn t3-btn-moveoption-bottom',
2351 'title' => htmlspecialchars($this->getLL('l_move_to_bottom'))
2352 ));
2353 }
2354 }
2355 $clipElements = $this->getClipboardElements($allowed, $mode);
2356 if (count($clipElements)) {
2357 $aOnClick = '';
2358 foreach ($clipElements as $elValue) {
2359 if ($mode == 'db') {
2360 list($itemTable, $itemUid) = explode('|', $elValue);
2361 $recordTitle = BackendUtility::getRecordTitle($itemTable, BackendUtility::getRecordWSOL($itemTable, $itemUid));
2362 $itemTitle = GeneralUtility::quoteJSvalue($recordTitle);
2363 $elValue = $itemTable . '_' . $itemUid;
2364 } else {
2365 // 'file', 'file_reference' and 'folder' mode
2366 $itemTitle = 'unescape(\'' . rawurlencode(basename($elValue)) . '\')';
2367 }
2368 $aOnClick .= 'setFormValueFromBrowseWin(\'' . $fName . '\',unescape(\''
2369 . rawurlencode(str_replace('%20', ' ', $elValue)) . '\'),' . $itemTitle . ',' . $itemTitle . ');';
2370 }
2371 $aOnClick .= 'return false;';
2372 $spriteIcon1 = IconUtility::getSpriteIcon('actions-document-paste-into', array(
2373 'title' => htmlspecialchars(sprintf($this->getLL('l_clipInsert_' . ($mode == 'db' ? 'db' : 'file')), count($clipElements)))
2374 ));
2375 $icons['R'][] = '<a href="#" onclick="' . htmlspecialchars($aOnClick) . '">' . $spriteIcon1 . '</a>';
2376 }
2377 }
2378 if (!$params['readOnly'] && !$params['noDelete']) {
2379 $icons['L'][] = IconUtility::getSpriteIcon('actions-selection-delete', array(
2380 'onclick' => $rOnClickInline,
2381 'data-fieldname' => $fName,
2382 'class' => 't3-btn t3-btn-removeoption',
2383 'title' => htmlspecialchars($this->getLL('l_remove_selected'))
2384
2385 ));
2386 }
2387 $imagesOnly = FALSE;
2388 if ($params['thumbnails'] && $params['info']) {
2389 // In case we have thumbnails, check if only images are allowed.
2390 // In this case, render them below the field, instead of to the right
2391 $allowedExtensionList = GeneralUtility::trimExplode(' ', strtolower($params['info']), TRUE);
2392 $imageExtensionList = GeneralUtility::trimExplode(',', strtolower($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext']), TRUE);
2393 $imagesOnly = TRUE;
2394 foreach ($allowedExtensionList as $allowedExtension) {
2395 if (!GeneralUtility::inArray($imageExtensionList, $allowedExtension)) {
2396 $imagesOnly = FALSE;
2397 break;
2398 }
2399 }
2400 }
2401 if ($imagesOnly) {
2402 $rightbox = '';
2403 $thumbnails = '<div class="imagethumbs">' . $params['thumbnails'] . '</div>';
2404 } else {
2405 $rightbox = $params['thumbnails'];
2406 $thumbnails = '';
2407 }
2408 // Hook: dbFileIcons_postProcess (requested by FAL-team for use with the "fal" extension)
2409 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tceforms.php']['dbFileIcons'])) {
2410 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tceforms.php']['dbFileIcons'] as $classRef) {
2411 $hookObject = GeneralUtility::getUserObj($classRef);
2412 if (!$hookObject instanceof DatabaseFileIconsHookInterface) {
2413 throw new \UnexpectedValueException('$hookObject must implement interface ' . \TYPO3\CMS\Backend\Form\DatabaseFileIconsHookInterface::class, 1290167704);
2414 }
2415 $additionalParams = array(
2416 'mode' => $mode,
2417 'allowed' => $allowed,
2418 'itemArray' => $itemArray,
2419 'onFocus' => $onFocus,
2420 'table' => $table,
2421 'field' => $field,
2422 'uid' => $uid,
2423 'config' => $GLOBALS['TCA'][$table]['columns'][$field]
2424 );
2425 $hookObject->dbFileIcons_postProcess($params, $selector, $thumbnails, $icons, $rightbox, $fName, $uidList, $additionalParams, $this);
2426 }
2427 }
2428 $str = '<table border="0" cellpadding="0" cellspacing="0" width="1" class="t3-form-field-group-file">
2429 ' . ($params['headers'] ? '
2430 <tr>
2431 <td>' . $params['headers']['selector'] . '</td>
2432 <td></td>
2433 <td></td>
2434 <td>' . ($params['thumbnails'] ? $params['headers']['items'] : '') . '</td>
2435 </tr>' : '') . '
2436 <tr>
2437 <td>' . $selector . $thumbnails;
2438 if (!$params['noList'] && $params['info'] !== '') {
2439 $str .= '<span class="filetypes">' . $params['info'] . '</span>';
2440 }
2441 $str .= '</td>
2442 <td class="icons">' . implode('<br />', $icons['L']) . '</td>
2443 <td class="icons">' . implode('<br />', $icons['R']) . '</td>
2444 <td>' . $rightbox . '</td>
2445 </tr>
2446 </table>';
2447 // Creating the hidden field which contains the actual value as a comma list.
2448 $str .= '<input type="hidden" name="' . $fName . '" value="' . htmlspecialchars(implode(',', $uidList)) . '" />';
2449 return $str;
2450 }
2451
2452 /**
2453 * Returns array of elements from clipboard to insert into GROUP element box.
2454 *
2455 * @param string $allowed Allowed elements, Eg "pages,tt_content", "gif,jpg,jpeg,png
2456 * @param string $mode Mode of relations: "db" or "file
2457 * @return array Array of elements in values (keys are insignificant), if none found, empty array.
2458 */
2459 public function getClipboardElements($allowed, $mode) {
2460 $output = array();
2461 if (is_object($this->clipObj)) {
2462 switch ($mode) {
2463 case 'file_reference':
2464
2465 case 'file':
2466 $elFromTable = $this->clipObj->elFromTable('_FILE');
2467 $allowedExts = GeneralUtility::trimExplode(',', $allowed, TRUE);
2468 // If there are a set of allowed extensions, filter the content:
2469 if ($allowedExts) {
2470 foreach ($elFromTable as $elValue) {
2471 $pI = pathinfo($elValue);
2472 $ext = strtolower($pI['extension']);
2473 if (in_array($ext, $allowedExts)) {
2474 $output[] = $elValue;
2475 }
2476 }
2477 } else {
2478 // If all is allowed, insert all: (This does NOT respect any disallowed extensions,
2479 // but those will be filtered away by the backend TCEmain)
2480 $output = $elFromTable;
2481 }
2482 break;
2483 case 'db':
2484 $allowedTables = GeneralUtility::trimExplode(',', $allowed, TRUE);
2485 // All tables allowed for relation:
2486 if (trim($allowedTables[0]) === '*') {
2487 $output = $this->clipObj->elFromTable('');
2488 } else {
2489 // Only some tables, filter them:
2490 foreach ($allowedTables as $tablename) {
2491 $elFromTable = $this->clipObj->elFromTable($tablename);
2492 $output = array_merge($output, $elFromTable);
2493 }
2494 }
2495 $output = array_keys($output);
2496 break;
2497 }
2498 }
2499 return $output;
2500 }
2501
2502 /**
2503 * Wraps the icon of a relation item (database record or file) in a link opening the context menu for the item.
2504 * Icons will be wrapped only if $this->enableClickMenu is set. This must be done only if a global SOBE object
2505 * exists and if the necessary JavaScript for displaying the context menus has been added to the page properties.
2506 *
2507 * @param string $str The icon HTML to wrap
2508 * @param string $table Table name (eg. "pages" or "tt_content") OR the absolute path to the file
2509 * @param int $uid The uid of the record OR if file, just blank value.
2510 * @return string HTML
2511 */
2512 public function getClickMenu($str, $table, $uid = 0) {
2513 if ($this->enableClickMenu) {
2514 return $this->getControllerDocumentTemplate()->wrapClickMenuOnIcon($str, $table, $uid, 1, '', '+copy,info,edit,view');
2515 }
2516 return '';
2517 }
2518
2519 /**
2520 * Rendering wizards for form fields.
2521 *
2522 * @param array $itemKinds Array with the real item in the first value, and an alternative item in the second value.
2523 * @param array $wizConf The "wizard" key from the config array for the field (from TCA)
2524 * @param string $table Table name
2525 * @param array $row The record array
2526 * @param string $field The field name
2527 * @param array $PA Additional configuration array. (passed by reference!)
2528 * @param string $itemName The field name
2529 * @param array $specConf Special configuration if available.
2530 * @param bool $RTE Whether the RTE could have been loaded.
2531 * @return string The new item value.
2532 */
2533 public function renderWizards($itemKinds, $wizConf, $table, $row, $field, &$PA, $itemName, $specConf, $RTE = FALSE) {
2534 // Init:
2535 $fieldChangeFunc = $PA['fieldChangeFunc'];
2536 $item = $itemKinds[0];
2537 $outArr = array();
2538 $colorBoxLinks = array();
2539 $fName = '[' . $table . '][' . $row['uid'] . '][' . $field . ']';
2540 $md5ID = 'ID' . GeneralUtility::shortmd5($itemName);
2541 $listFlag = '_list';
2542 $fieldConfig = $PA['fieldConf']['config'];
2543 $prefixOfFormElName = 'data[' . $table . '][' . $row['uid'] . '][' . $field . ']';
2544 $flexFormPath = '';
2545 if (GeneralUtility::isFirstPartOfStr($PA['itemFormElName'], $prefixOfFormElName)) {
2546 $flexFormPath = str_replace('][', '/', substr($PA['itemFormElName'], strlen($prefixOfFormElName) + 1, -1));
2547 }
2548 // Manipulate the field name (to be the TRUE form field name) and remove
2549 // a suffix-value if the item is a selector box with renderMode "singlebox":
2550 if ($PA['fieldConf']['config']['form_type'] == 'select') {
2551 // Single select situation:
2552 if ($PA['fieldConf']['config']['maxitems'] <= 1) {
2553 $listFlag = '';
2554 } elseif ($PA['fieldConf']['config']['renderMode'] == 'singlebox') {
2555 $itemName .= '[]';
2556 $listFlag = '';
2557 }
2558 }
2559 // Traverse wizards:
2560 if (is_array($wizConf) && !$this->disableWizards) {
2561 $parametersOfWizards = &$specConf['wizards']['parameters'];
2562 foreach ($wizConf as $wid => $wConf) {
2563 if (
2564 $wid[0] !== '_' && (!$wConf['enableByTypeConfig']
2565 || is_array($parametersOfWizards) && in_array($wid, $parametersOfWizards)) && ($RTE || !$wConf['RTEonly'])
2566 ) {
2567 // Title / icon:
2568 $iTitle = htmlspecialchars($this->sL($wConf['title']));
2569 if ($wConf['icon']) {
2570 $icon = $this->getIconHtml($wConf['icon'], $iTitle, $iTitle);
2571 } else {
2572 $icon = $iTitle;
2573 }
2574 switch ((string)$wConf['type']) {
2575 case 'userFunc':
2576
2577 case 'script':
2578
2579 case 'popup':
2580
2581 case 'colorbox':
2582
2583 case 'slider':
2584 if (!$wConf['notNewRecords'] || MathUtility::canBeInterpretedAsInteger($row['uid'])) {
2585 // Setting &P array contents:
2586 $params = array();
2587 // Including the full fieldConfig from TCA may produce too long an URL
2588 if ($wid != 'RTE') {
2589 $params['fieldConfig'] = $fieldConfig;
2590 }
2591 $params['params'] = $wConf['params'];
2592 $params['exampleImg'] = $wConf['exampleImg'];
2593 $params['table'] = $table;
2594 $params['uid'] = $row['uid'];
2595 $params['pid'] = $row['pid'];
2596 $params['field'] = $field;
2597 $params['flexFormPath'] = $flexFormPath;
2598 $params['md5ID'] = $md5ID;
2599 $params['returnUrl'] = $this->thisReturnUrl();
2600
2601 $wScript = '';
2602 // Resolving script filename and setting URL.
2603 if (isset($wConf['module']['name'])) {
2604 $urlParameters = array();
2605 if (isset($wConf['module']['urlParameters']) && is_array($wConf['module']['urlParameters'])) {
2606 $urlParameters = $wConf['module']['urlParameters'];
2607 }
2608 $wScript = BackendUtility::getModuleUrl($wConf['module']['name'], $urlParameters, $this->backPath);
2609 } elseif (in_array($wConf['type'], array('script', 'colorbox', 'popup'), TRUE)) {
2610 // Illegal configuration, fail silently
2611 break;
2612 }
2613 $url = ($wScript ?: $this->backPath) . (strstr($wScript, '?') ? '' : '?');
2614 // If "script" type, create the links around the icon:
2615 if ((string)$wConf['type'] === 'script') {
2616 $aUrl = $url . GeneralUtility::implodeArrayForUrl('', array('P' => $params));
2617 $outArr[] = '<a href="' . htmlspecialchars($aUrl) . '" onclick="this.blur(); return !TBE_EDITOR.isFormChanged();">' . $icon . '</a>';
2618 } else {
2619 // ... else types "popup", "colorbox" and "userFunc" will need additional parameters:
2620 $params['formName'] = $this->formName;
2621 $params['itemName'] = $itemName;
2622 $params['hmac'] = GeneralUtility::hmac($params['formName'] . $params['itemName'], 'wizard_js');
2623 $params['fieldChangeFunc'] = $fieldChangeFunc;
2624 $params['fieldChangeFuncHash'] = GeneralUtility::hmac(serialize($fieldChangeFunc));
2625 switch ((string)$wConf['type']) {
2626 case 'popup':
2627 case 'colorbox':
2628 // Current form value is passed as P[currentValue]!
2629 $addJS = $wConf['popup_onlyOpenIfSelected']
2630 ? 'if (!TBE_EDITOR.curSelected(\'' . $itemName . $listFlag . '\')){alert('
2631 . GeneralUtility::quoteJSvalue($this->getLL('m_noSelItemForEdit'))
2632 . '); return false;}'
2633 : '';
2634 $curSelectedValues = '+\'&P[currentSelectedValues]=\'+TBE_EDITOR.curSelected(\'' . $itemName . $listFlag . '\')';
2635 $aOnClick = 'this.blur();' . $addJS . 'vHWin=window.open(\'' . $url
2636 . GeneralUtility::implodeArrayForUrl('', array('P' => $params))
2637 . '\'+\'&P[currentValue]=\'+TBE_EDITOR.rawurlencode('
2638 . $this->elName($itemName) . '.value,200)' . $curSelectedValues
2639 . ',\'popUp' . $md5ID . '\',\'' . $wConf['JSopenParams'] . '\');'
2640 . 'vHWin.focus();return false;';
2641 // Setting "colorBoxLinks" - user LATER to wrap around the color box as well:
2642 $colorBoxLinks = array('<a href="#" onclick="' . htmlspecialchars($aOnClick) . '">', '</a>');
2643 if ((string)$wConf['type'] == 'popup') {
2644 $outArr[] = $colorBoxLinks[0] . $icon . $colorBoxLinks[1];
2645 }
2646 break;
2647 case 'userFunc':
2648 // Reference set!
2649 $params['item'] = &$item;
2650 $params['icon'] = $icon;
2651 $params['iTitle'] = $iTitle;
2652 $params['wConf'] = $wConf;
2653 $params['row'] = $row;
2654 $outArr[] = GeneralUtility::callUserFunction($wConf['userFunc'], $params, $this);
2655 break;
2656 case 'slider':
2657 // Reference set!
2658 $params['item'] = &$item;
2659 $params['icon'] = $icon;
2660 $params['iTitle'] = $iTitle;
2661 $params['wConf'] = $wConf;
2662 $params['row'] = $row;
2663 $wizard = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Form\Element\ValueSlider::class);
2664 $outArr[] = call_user_func_array(array(&$wizard, 'renderWizard'), array(&$params, &$this));
2665 break;
2666 }
2667 }
2668 // Hide the real form element?
2669 if (is_array($wConf['hideParent']) || $wConf['hideParent']) {
2670 // Setting the item to a hidden-field.
2671 $item = $itemKinds[1];
2672 if (is_array($wConf['hideParent'])) {
2673 $item .= $this->getSingleField_typeNone_render($wConf['hideParent'], $PA['itemFormElValue']);
2674 }
2675 }
2676 }
2677 break;
2678 case 'select':
2679 $fieldValue = array('config' => $wConf);
2680 $TSconfig = $this->setTSconfig($table, $row);
2681 $TSconfig[$field] = $TSconfig[$field]['wizards.'][$wid . '.'];
2682 $selItems = $this->addSelectOptionsToItemArray($this->initItemArray($fieldValue), $fieldValue, $TSconfig, $field);
2683 // Process items by a user function:
2684 if (!empty($wConf['itemsProcFunc'])) {
2685 $funcConfig = !empty($wConf['itemsProcFunc.']) ? $wConf['itemsProcFunc.'] : array();
2686 $selItems = $this->procItems($selItems, $funcConfig, $wConf, $table, $row, $field);
2687 }
2688 $opt = array();
2689 $opt[] = '<option>' . $iTitle . '</option>';
2690 foreach ($selItems as $p) {
2691 $opt[] = '<option value="' . htmlspecialchars($p[1]) . '">' . htmlspecialchars($p[0]) . '</option>';
2692 }
2693 if ($wConf['mode'] == 'append') {
2694 $assignValue = $this->elName($itemName) . '.value=\'\'+this.options[this.selectedIndex].value+' . $this->elName($itemName) . '.value';
2695 } elseif ($wConf['mode'] == 'prepend') {
2696 $assignValue = $this->elName($itemName) . '.value+=\'\'+this.options[this.selectedIndex].value';
2697 } else {
2698 $assignValue = $this->elName($itemName) . '.value=this.options[this.selectedIndex].value';
2699 }
2700 $sOnChange = $assignValue . ';this.blur();this.selectedIndex=0;' . implode('', $fieldChangeFunc);
2701 $outArr[] = '<select id="' . str_replace('.', '', uniqid('tceforms-select-', TRUE))
2702 . '" class="tceforms-select tceforms-wizardselect" name="_WIZARD' . $fName . '" onchange="'
2703 . htmlspecialchars($sOnChange) . '">' . implode('', $opt) . '</select>';
2704 break;
2705 case 'suggest':
2706 if (!empty($PA['fieldTSConfig']['suggest.']['default.']['hide'])) {
2707 break;
2708 }
2709 $outArr[] = $this->suggest->renderSuggestSelector($PA['itemFormElName'], $table, $field, $row, $PA);
2710 break;
2711 }
2712 // Color wizard colorbox:
2713 if ((string)$wConf['type'] === 'colorbox') {
2714 $dim = GeneralUtility::intExplode('x', $wConf['dim']);
2715 $dX = MathUtility::forceIntegerInRange($dim[0], 1, 200, 20);
2716 $dY = MathUtility::forceIntegerInRange($dim[1], 1, 200, 20);
2717 $color = $PA['itemFormElValue'] ? ' bgcolor="' . htmlspecialchars($PA['itemFormElValue']) . '"' : '';
2718 $skinImg = IconUtility::skinImg(
2719 $this->backPath,
2720 $color === '' ? 'gfx/colorpicker_empty.png' : 'gfx/colorpicker.png',
2721 'width="' . $dX . '" height="' . $dY . '"' . BackendUtility::titleAltAttrib(trim($iTitle . ' ' . $PA['itemFormElValue'])) . ' border="0"'
2722 );
2723 $outArr[] = '<table border="0" cellpadding="0" cellspacing="0" id="' . $md5ID . '"' . $color
2724 . ' style="' . htmlspecialchars($wConf['tableStyle']) . '">
2725 <tr>
2726 <td>' . $colorBoxLinks[0] . '<img ' . $skinImg . '>' . $colorBoxLinks[1] . '</td>
2727 </tr>
2728 </table>';
2729 }
2730 }
2731 }
2732 // For each rendered wizard, put them together around the item.
2733 if (count($outArr)) {
2734 if ($wizConf['_HIDDENFIELD']) {
2735 $item = $itemKinds[1];
2736 }
2737 $vAlign = $wizConf['_VALIGN'] ? ' style="vertical-align:' . $wizConf['_VALIGN'] . '"' : '';
2738 if (count($outArr) > 1 || $wizConf['_PADDING']) {
2739 $dist = (int)$wizConf['_DISTANCE'];
2740 if ($wizConf['_VERTICAL']) {
2741 $dist = $dist ? '<tr><td><img src="clear.gif" width="1" height="' . $dist . '" alt="" /></td></tr>' : '';
2742 $outStr = '<tr><td>' . implode(('</td></tr>' . $dist . '<tr><td>'), $outArr) . '</td></tr>';
2743 } else {
2744 $dist = $dist ? '<td><img src="clear.gif" height="1" width="' . $dist . '" alt="" /></td>' : '';
2745 $outStr = '<tr><td' . $vAlign . '>' . implode(('</td>' . $dist . '<td' . $vAlign . '>'), $outArr) . '</td></tr>';
2746 }
2747 $outStr = '<table border="0" cellpadding="' . (int)$wizConf['_PADDING'] . '" cellspacing="' . (int)$wizConf['_PADDING'] . '">' . $outStr . '</table>';
2748 } else {
2749 $outStr = implode('', $outArr);
2750 }
2751 if ($wizConf['_POSITION'] === 'left') {
2752 $outStr = '<tr><td' . $vAlign . '>' . $outStr . '</td><td' . $vAlign . '>' . $item . '</td></tr>';
2753 } elseif ($wizConf['_POSITION'] === 'top') {