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