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