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