2 namespace TYPO3\CMS\Backend\Form\Element
;
5 * This file is part of the TYPO3 CMS project.
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.
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
14 * The TYPO3 project - inspiring people to share!
17 use TYPO3\CMS\Backend\Utility\BackendUtility
;
18 use TYPO3\CMS\Backend\Utility\IconUtility
;
19 use TYPO3\CMS\Core\Utility\GeneralUtility
;
22 * Generation of TCEform elements of the type "flexform"
24 class FlexElement
extends AbstractFormElement
{
27 * Handler for Flex Forms
29 * @param string $table The table name of the record
30 * @param string $field The field name which this element is supposed to edit
31 * @param array $row The record data array where the value(s) for the field can be found
32 * @param array $additionalInformation An array with additional configuration options.
33 * @return string The HTML code for the TCEform field
35 public function render($table, $field, $row, &$additionalInformation) {
37 $dataStructArray = BackendUtility
::getFlexFormDS($additionalInformation['fieldConf']['config'], $row, $table, $field);
39 // Manipulate Flexform DS via TSConfig and group access lists
40 if (is_array($dataStructArray)) {
41 $flexFormHelper = GeneralUtility
::makeInstance(\TYPO3\CMS\Backend\Form\FlexFormsHelper
::class);
42 $dataStructArray = $flexFormHelper->modifyFlexFormDS($dataStructArray, $table, $field, $row, $additionalInformation['fieldConf']);
43 unset($flexFormHelper);
45 // Get data structure:
46 if (is_array($dataStructArray)) {
48 $xmlData = $additionalInformation['itemFormElValue'];
49 $xmlHeaderAttributes = GeneralUtility
::xmlGetHeaderAttribs($xmlData);
50 $storeInCharset = strtolower($xmlHeaderAttributes['encoding']);
51 if ($storeInCharset) {
52 $currentCharset = $this->getLanguageService()->charSet
;
53 $xmlData = $this->getLanguageService()->csConvObj
->conv($xmlData, $storeInCharset, $currentCharset, 1);
55 $editData = GeneralUtility
::xml2array($xmlData);
56 // Must be XML parsing error...
57 if (!is_array($editData)) {
59 } elseif (!isset($editData['meta']) ||
!is_array($editData['meta'])) {
60 $editData['meta'] = array();
62 // Find the data structure if sheets are found:
63 $sheet = $editData['meta']['currentSheetId'] ?
$editData['meta']['currentSheetId'] : 'sDEF';
65 // Create language menu:
66 $langChildren = $dataStructArray['meta']['langChildren'] ?
1 : 0;
67 $langDisabled = $dataStructArray['meta']['langDisable'] ?
1 : 0;
68 $editData['meta']['currentLangId'] = array();
69 // Look up page overlays:
70 $checkPageLanguageOverlay = $this->getBackendUserAuthentication()->getTSConfigVal('options.checkPageLanguageOverlay') ?
TRUE : FALSE;
71 if ($checkPageLanguageOverlay) {
72 $where_clause = 'pid=' . (int)$row['pid'] . BackendUtility
::deleteClause('pages_language_overlay')
73 . BackendUtility
::versioningPlaceholderClause('pages_language_overlay');
74 $pageOverlays = $this->getDatabaseConnection()->exec_SELECTgetRows('*', 'pages_language_overlay', $where_clause, '', '', '', 'sys_language_uid');
76 $languages = $this->formEngine
->getAvailableLanguages();
77 foreach ($languages as $lInfo) {
79 $this->getBackendUserAuthentication()->checkLanguageAccess($lInfo['uid'])
80 && (!$checkPageLanguageOverlay ||
$lInfo['uid'] <= 0 ||
is_array($pageOverlays[$lInfo['uid']]))
82 $editData['meta']['currentLangId'][] = $lInfo['ISOcode'];
85 if (!is_array($editData['meta']['currentLangId']) ||
!count($editData['meta']['currentLangId'])) {
86 $editData['meta']['currentLangId'] = array('DEF');
88 $editData['meta']['currentLangId'] = array_unique($editData['meta']['currentLangId']);
89 $additionalInformation['_noEditDEF'] = FALSE;
90 if ($langChildren ||
$langDisabled) {
91 $rotateLang = array('DEF');
93 if (!in_array('DEF', $editData['meta']['currentLangId'])) {
94 array_unshift($editData['meta']['currentLangId'], 'DEF');
95 $additionalInformation['_noEditDEF'] = TRUE;
97 $rotateLang = $editData['meta']['currentLangId'];
100 if (is_array($dataStructArray['sheets'])) {
101 $tabsToTraverse = array_keys($dataStructArray['sheets']);
103 $tabsToTraverse = array($sheet);
106 /** @var $elementConditionMatcher \TYPO3\CMS\Backend\Form\ElementConditionMatcher */
107 $elementConditionMatcher = GeneralUtility
::makeInstance(\TYPO3\CMS\Backend\Form\ElementConditionMatcher
::class);
109 foreach ($rotateLang as $lKey) {
110 if (!$langChildren && !$langDisabled) {
111 $item .= '<strong>' . $this->formEngine
->getLanguageIcon($table, $row, ('v' . $lKey)) . $lKey . ':</strong>';
113 // Default language, other options are "lUK" or whatever country code (independent of system!!!)
117 foreach ($tabsToTraverse as $sheet) {
118 list($dataStruct, $sheet) = GeneralUtility
::resolveSheetDefInDS($dataStructArray, $sheet);
119 // If sheet has displayCond
120 if ($dataStruct['ROOT']['TCEforms']['displayCond']) {
121 $splitCondition = GeneralUtility
::trimExplode(':', $dataStruct['ROOT']['TCEforms']['displayCond']);
122 $skipCondition = FALSE;
124 switch ($splitCondition[0]) {
126 list($sheetName, $fieldName) = GeneralUtility
::trimExplode('.', $splitCondition[1]);
127 $fieldValue = $editData['data'][$sheetName][$lang][$fieldName];
128 $splitCondition[1] = $fieldName;
129 $dataStruct['ROOT']['TCEforms']['displayCond'] = join(':', $splitCondition);
130 $fakeRow = array($fieldName => $fieldValue);
132 case 'HIDE_FOR_NON_ADMINS':
136 case 'HIDE_L10N_SIBLINGS':
141 $fakeRow = array('uid' => $row['uid']);
144 $skipCondition = TRUE;
146 $displayConditionResult = TRUE;
147 if ($dataStruct['ROOT']['TCEforms']['displayCond']) {
148 $displayConditionResult = $elementConditionMatcher->match($dataStruct['ROOT']['TCEforms']['displayCond'], $fakeRow, 'vDEF');
150 // If sheets displayCond leads to false
151 if (!$skipCondition && !$displayConditionResult) {
152 // Don't create this sheet
157 if (is_array($dataStruct['ROOT']) && is_array($dataStruct['ROOT']['el'])) {
158 // Default language, other options are "lUK" or whatever country code (independent of system!!!)
159 $additionalInformation['_valLang'] = $langChildren && !$langDisabled ?
$editData['meta']['currentLangId'] : 'DEF';
160 $additionalInformation['_lang'] = $lang;
161 // Assemble key for loading the correct CSH file
162 $dsPointerFields = GeneralUtility
::trimExplode(',', $GLOBALS['TCA'][$table]['columns'][$field]['config']['ds_pointerField'], TRUE);
163 $additionalInformation['_cshKey'] = $table . '.' . $field;
164 foreach ($dsPointerFields as $key) {
165 $additionalInformation['_cshKey'] .= '.' . $row[$key];
167 // Push the sheet level tab to DynNestedStack
168 $tabIdentString = '';
169 if (is_array($dataStructArray['sheets'])) {
170 $tabIdentString = $this->getDocumentTemplate()->getDynTabMenuId('TCEFORMS:flexform:' . $additionalInformation['itemFormElName'] . $additionalInformation['_lang']);
171 $this->formEngine
->pushToDynNestedStack('tab', $tabIdentString . '-' . (count($tabParts) +
1));
174 $tRows = $this->getSingleField_typeFlex_draw($dataStruct['ROOT']['el'], $editData['data'][$sheet][$lang], $table, $field, $row, $additionalInformation, '[data][' . $sheet . '][' . $lang . ']');
175 $sheetContent = '<div class="typo3-TCEforms-flexForm">' . $tRows . '</div>';
176 // Pop the sheet level tab from DynNestedStack
177 if (is_array($dataStructArray['sheets'])) {
178 $this->formEngine
->popFromDynNestedStack('tab', $tabIdentString . '-' . (count($tabParts) +
1));
181 $sheetContent = 'Data Structure ERROR: No ROOT element found for sheet "' . $sheet . '".';
185 'label' => $dataStruct['ROOT']['TCEforms']['sheetTitle'] ?
$this->formEngine
->sL($dataStruct['ROOT']['TCEforms']['sheetTitle']) : $sheet,
186 'description' => $dataStruct['ROOT']['TCEforms']['sheetDescription'] ?
$this->formEngine
->sL($dataStruct['ROOT']['TCEforms']['sheetDescription']) : '',
187 'linkTitle' => $dataStruct['ROOT']['TCEforms']['sheetShortDescr'] ?
$this->formEngine
->sL($dataStruct['ROOT']['TCEforms']['sheetShortDescr']) : '',
188 'content' => $sheetContent
191 if (is_array($dataStructArray['sheets'])) {
192 $item .= $this->formEngine
->getDynTabMenu($tabParts, 'TCEFORMS:flexform:' . $additionalInformation['itemFormElName'] . $additionalInformation['_lang']);
194 $item .= $sheetContent;
198 $item = 'Data Structure ERROR: ' . $dataStructArray;
205 * Recursive rendering of flexforms
207 * @param array $dataStruct (part of) Data Structure for which to render. Keys on first level is flex-form fields
208 * @param array $editData (part of) Data array of flexform corresponding to the input DS. Keys on first level is flex-form field names
209 * @param string $table Table name, eg. tt_content
210 * @param string $field Field name, eg. tx_templavoila_flex
211 * @param array $row The particular record from $table in which the field $field is found
212 * @param array $PA Array of standard information for rendering of a form field in TCEforms, see other rendering functions too
213 * @param string $formPrefix Form field prefix, eg. "[data][sDEF][lDEF][...][...]
214 * @param int $level Indicates nesting level for the function call
215 * @param string $idPrefix Prefix for ID-values
216 * @param bool $toggleClosed Defines whether the next flexform level is open or closed. Comes from _TOGGLE pseudo field in FlexForm xml.
217 * @return string HTMl code for form.
219 public function getSingleField_typeFlex_draw($dataStruct, $editData, $table, $field, $row, &$PA, $formPrefix = '', $level = 0, $idPrefix = 'ID', $toggleClosed = FALSE) {
221 $mayRestructureFlexforms = $this->getBackendUserAuthentication()->checkLanguageAccess(0);
222 // Data Structure array must be ... and array of course...
223 if (is_array($dataStruct)) {
224 foreach ($dataStruct as $key => $value) {
225 // Traversing fields in structure:
226 if (is_array($value)) {
227 // The value of each entry must be an array.
228 // ********************
230 // ********************
232 // <title>LLL:EXT:cms/locallang_ttc.xml:media.sources</title>
233 $theTitle = $value['title'];
235 // If there is a title, check for LLL label
236 if (strlen($theTitle) > 0) {
237 $theTitle = htmlspecialchars(GeneralUtility
::fixed_lgd_cs($this->formEngine
->sL($theTitle),
238 (int)$this->getBackendUserAuthentication()->uc
['titleLen']));
240 // If it's a "section" or "container":
241 if ($value['type'] == 'array') {
242 // Creating IDs for form fields:
243 // It's important that the IDs "cascade" - otherwise we can't dynamically expand the flex form
244 // because this relies on simple string substitution of the first parts of the id values.
245 // This is a suffix used for forms on this level
246 $thisId = GeneralUtility
::shortMd5(uniqid('id', TRUE));
247 // $idPrefix is the prefix for elements on lower levels in the hierarchy and we combine this
248 // with the thisId value to form a new ID on this level.
249 $idTagPrefix = $idPrefix . '-' . $thisId;
250 // If it's a "section" containing other elements:
251 if ($value['section']) {
252 // Load script.aculo.us if flexform sections can be moved by drag'n'drop:
253 $this->getControllerDocumentTemplate()->getPageRenderer()->loadScriptaculous();
254 // Render header of section:
255 $output .= '<div class="t3-form-field-label-flexsection"><strong>' . $theTitle . '</strong></div>';
256 // Render elements in data array for section:
258 if (is_array($editData[$key]['el'])) {
259 foreach ($editData[$key]['el'] as $k3 => $v3) {
263 $theDat = $v3[$theType];
264 $newSectionEl = $value['el'][$theType];
265 if (is_array($newSectionEl)) {
266 $tRows[] = $this->getSingleField_typeFlex_draw(array($theType => $newSectionEl),
267 array($theType => $theDat), $table, $field, $row, $PA,
268 $formPrefix . '[' . $key . '][el][' . $cc . ']', $level +
1,
269 $idTagPrefix, $v3['_TOGGLE']);
274 // Now, we generate "templates" for new elements that could be added to this section
275 // by traversing all possible types of content inside the section:
276 // We have to handle the fact that requiredElements and such may be set during this
277 // rendering process and therefore we save and reset the state of some internal variables
278 // ... little crude, but works...
279 // Preserving internal variables we don't want to change:
280 $TEMP_requiredElements = $this->formEngine
->requiredElements
;
281 // Traversing possible types of new content in the section:
282 $newElementsLinks = array();
283 foreach ($value['el'] as $nnKey => $nCfg) {
284 $additionalJS_post_saved = $this->formEngine
->additionalJS_post
;
285 $this->formEngine
->additionalJS_post
= array();
286 $additionalJS_submit_saved = $this->formEngine
->additionalJS_submit
;
287 $this->formEngine
->additionalJS_submit
= array();
288 $newElementTemplate = $this->getSingleField_typeFlex_draw(array($nnKey => $nCfg),
289 array(), $table, $field, $row, $PA,
290 $formPrefix . '[' . $key . '][el][' . $idTagPrefix . '-form]', $level +
1,
292 // Makes a "Add new" link:
293 $var = str_replace('.', '', uniqid('idvar', TRUE));
294 $replace = 'replace(/' . $idTagPrefix . '-/g,"' . $idTagPrefix . '-"+' . $var . '+"-")';
295 $replace .= '.replace(/(tceforms-(datetime|date)field-)/g,"$1" + (new Date()).getTime())';
296 $onClickInsert = 'var ' . $var . ' = "' . 'idx"+(new Date()).getTime();'
297 // Do not replace $isTagPrefix in setActionStatus() because it needs section id!
298 . 'new Insertion.Bottom($("' . $idTagPrefix . '"), ' . json_encode($newElementTemplate)
299 . '.' . $replace . '); setActionStatus("' . $idTagPrefix . '");'
300 . 'eval(unescape("' . rawurlencode(implode(';', $this->formEngine
->additionalJS_post
)) . '").' . $replace . ');'
301 . 'TBE_EDITOR.addActionChecks("submit", unescape("'
302 . rawurlencode(implode(';', $this->formEngine
->additionalJS_submit
)) . '").' . $replace . ');'
303 . 'TYPO3.TCEFORMS.update();'
305 // Kasper's comment (kept for history):
306 // Maybe there is a better way to do this than store the HTML for the new element
307 // in rawurlencoded format - maybe it even breaks with certain charsets?
308 // But for now this works...
309 $this->formEngine
->additionalJS_post
= $additionalJS_post_saved;
310 $this->formEngine
->additionalJS_submit
= $additionalJS_submit_saved;
312 if (isset($nCfg['title'])) {
313 $title = $this->formEngine
->sL($nCfg['title']);
315 $newElementsLinks[] = '<a href="#" onclick="' . htmlspecialchars($onClickInsert) . '">'
316 . IconUtility
::getSpriteIcon('actions-document-new')
317 . htmlspecialchars(GeneralUtility
::fixed_lgd_cs($title, 30)) . '</a>';
319 // Reverting internal variables we don't want to change:
320 $this->formEngine
->requiredElements
= $TEMP_requiredElements;
321 // Adding the sections:
322 $toggleAll = $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.toggleall', TRUE);
324 <div class="t3-form-field-toggle-flexsection">
325 <a href="#" onclick="flexFormToggleSubs(\'' . htmlspecialchars($idTagPrefix) . '\'); return false;">'
326 . IconUtility
::getSpriteIcon('actions-move-right', array('title' => $toggleAll)) . $toggleAll . '
330 <div id="' . $idTagPrefix . '" class="t3-form-field-container-flexsection">' . implode('', $tRows) . '</div>';
331 $output .= $mayRestructureFlexforms ?
'<div class="t3-form-field-add-flexsection"><strong>'
332 . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.addnew', TRUE)
333 . ':</strong> ' . implode(' | ', $newElementsLinks) . '</div>' : '';
336 $toggleIcon_open = IconUtility
::getSpriteIcon('actions-move-down');
337 $toggleIcon_close = IconUtility
::getSpriteIcon('actions-move-right');
338 // Create on-click actions.
339 $onClickRemove = 'if (confirm("Are you sure?")){/*###REMOVE###*/;$("' . $idTagPrefix
340 . '").hide();setActionStatus("' . $idPrefix . '");} return false;';
341 $onClickToggle = 'flexFormToggle("' . $idTagPrefix . '"); return false;';
342 $onMove = 'flexFormSortable("' . $idPrefix . '")';
343 // Notice: Creating "new" elements after others seemed to be too difficult to do
344 // and since moving new elements created in the bottom is now so easy
345 // with drag'n'drop I didn't see the need.
346 // Putting together header of a section. Sections can be removed, copied, opened/closed, moved up and down:
347 // I didn't know how to make something right-aligned without a table, so I put it in a table.
348 // can be made into <div>'s if someone like to.
349 // Notice: The fact that I make a "Sortable.create" right onmousedown is that if we
350 // initialize this when rendering the form in PHP new and copied elements will not
351 // be possible to move as a sortable. But this way a new sortable is initialized every time
352 // someone tries to move and it will always work.
354 <table class="t3-form-field-header-flexsection" onmousedown="' . ($mayRestructureFlexforms ?
htmlspecialchars($onMove) : '') . '">
357 <a href="#" onclick="' . htmlspecialchars($onClickToggle) . '" id="' . $idTagPrefix . '-toggle">
358 ' . ($toggleClosed ?
$toggleIcon_close : $toggleIcon_open) . '
360 <strong>' . $theTitle . '</strong> <em><span id="' . $idTagPrefix . '-preview"></span></em>
363 . ($mayRestructureFlexforms ? IconUtility
::getSpriteIcon('actions-move-move', array('title' => 'Drag to Move')) : '')
364 . ($mayRestructureFlexforms ?
'<a href="#" onclick="' . htmlspecialchars($onClickRemove) . '">'
365 . IconUtility
::getSpriteIcon('actions-edit-delete', array('title' => 'Delete')) : '')
369 $s = GeneralUtility
::revExplode('[]', $formPrefix, 2);
370 $actionFieldName = '_ACTION_FLEX_FORM' . $PA['itemFormElName'] . $s[0] . '][_ACTION][' . $s[1];
371 // Push the container to DynNestedStack as it may be toggled
372 $this->formEngine
->pushToDynNestedStack('flex', $idTagPrefix);
373 // Putting together the container:
374 $this->formEngine
->additionalJS_delete
= array();
375 $singleField_typeFlex_draw = $this->getSingleField_typeFlex_draw($value['el'],
376 $editData[$key]['el'], $table, $field, $row, $PA,
377 ($formPrefix . '[' . $key . '][el]'), ($level +
1), $idTagPrefix);
379 <div id="' . $idTagPrefix . '" class="t3-form-field-container-flexsections">
380 <input id="' . $idTagPrefix . '-action" type="hidden" name="' . htmlspecialchars($actionFieldName) . '" value=""/>
383 <div class="t3-form-field-record-flexsection" id="' . $idTagPrefix . '-content"'
384 . ($toggleClosed ?
' style="display:none;"' : '') . '>' . $singleField_typeFlex_draw . '
386 <input id="' . $idTagPrefix . '-toggleClosed" type="hidden" name="'
387 . htmlspecialchars('data[' . $table . '][' . $row['uid'] . '][' . $field . ']' . $formPrefix . '[_TOGGLE]')
388 . '" value="' . ($toggleClosed ?
1 : 0) . '" />
390 $output = str_replace('/*###REMOVE###*/', GeneralUtility
::slashJS(htmlspecialchars(implode('', $this->formEngine
->additionalJS_delete
))), $output);
391 // NOTICE: We are saving the toggle-state directly in the flexForm XML and "unauthorized"
392 // according to the data structure. It means that flexform XML will report unclean and
393 // a cleaning operation will remove the recorded togglestates. This is not a fatal problem.
394 // Ideally we should save the toggle states in meta-data but it is much harder to do that.
395 // And this implementation was easy to make and with no really harmful impact.
396 // Pop the container from DynNestedStack
397 $this->formEngine
->popFromDynNestedStack('flex', $idTagPrefix);
399 } elseif (is_array($value['TCEforms']['config'])) {
400 // Rendering a single form element:
401 if (is_array($PA['_valLang'])) {
402 $rotateLang = $PA['_valLang'];
404 $rotateLang = array($PA['_valLang']);
406 $conditionData = is_array($editData) ?
$editData : array();
407 // Add current $row to data processed by \TYPO3\CMS\Backend\Form\ElementConditionMatcher
408 $conditionData['parentRec'] = $row;
411 /** @var $elementConditionMatcher \TYPO3\CMS\Backend\Form\ElementConditionMatcher */
412 $elementConditionMatcher = GeneralUtility
::makeInstance(\TYPO3\CMS\Backend\Form\ElementConditionMatcher
::class);
414 foreach ($rotateLang as $vDEFkey) {
415 $vDEFkey = 'v' . $vDEFkey;
416 $displayConditionResult = TRUE;
417 if ($value['TCEforms']['displayCond']) {
418 $displayConditionResult = $elementConditionMatcher->match($value['TCEforms']['displayCond'], $conditionData, $vDEFkey);
420 if ($displayConditionResult) {
422 $fakePA['fieldConf'] = array(
423 'label' => $this->formEngine
->sL(trim($value['TCEforms']['label'])),
424 'config' => $value['TCEforms']['config'],
425 'defaultExtras' => $value['TCEforms']['defaultExtras'],
426 'onChange' => $value['TCEforms']['onChange']
428 if ($PA['_noEditDEF'] && $PA['_lang'] === 'lDEF') {
429 $fakePA['fieldConf']['config'] = array(
435 $fakePA['fieldConf']['onChange'] === 'reload'
436 ||
!empty($GLOBALS['TCA'][$table]['ctrl']['type'])
437 && (string)$key === $GLOBALS['TCA'][$table]['ctrl']['type']
438 ||
!empty($GLOBALS['TCA'][$table]['ctrl']['requestUpdate'])
439 && GeneralUtility
::inList($GLOBALS['TCA'][$table]['ctrl']['requestUpdate'], $key)
441 if ($this->getBackendUserAuthentication()->jsConfirmation(1)) {
442 $alertMsgOnChange = 'if (confirm(TBE_EDITOR.labels.onChangeAlert) && TBE_EDITOR.checkSubmit(-1)){ TBE_EDITOR.submitForm() };';
444 $alertMsgOnChange = 'if(TBE_EDITOR.checkSubmit(-1)){ TBE_EDITOR.submitForm();}';
447 $alertMsgOnChange = '';
449 $fakePA['fieldChangeFunc'] = $PA['fieldChangeFunc'];
450 if (strlen($alertMsgOnChange)) {
451 $fakePA['fieldChangeFunc']['alert'] = $alertMsgOnChange;
453 $fakePA['onFocus'] = $PA['onFocus'];
454 $fakePA['label'] = $PA['label'];
455 $fakePA['itemFormElName'] = $PA['itemFormElName'] . $formPrefix . '[' . $key . '][' . $vDEFkey . ']';
456 $fakePA['itemFormElName_file'] = $PA['itemFormElName_file'] . $formPrefix . '[' . $key . '][' . $vDEFkey . ']';
457 $fakePA['itemFormElID'] = $fakePA['itemFormElName'];
458 if (isset($editData[$key][$vDEFkey])) {
459 $fakePA['itemFormElValue'] = $editData[$key][$vDEFkey];
461 $fakePA['itemFormElValue'] = $fakePA['fieldConf']['config']['default'];
463 $theFormEl = $this->formEngine
->getSingleField_SW($table, $field, $row, $fakePA);
464 $theTitle = htmlspecialchars($fakePA['fieldConf']['label']);
465 if (!in_array('DEF', $rotateLang)) {
466 $defInfo = '<div class="typo3-TCEforms-originalLanguageValue">'
467 . $this->formEngine
->getLanguageIcon($table, $row, 0)
468 . $this->formEngine
->previewFieldValue($editData[$key]['vDEF'], $fakePA['fieldConf'], $field)
473 if (!$PA['_noEditDEF']) {
474 $prLang = $this->formEngine
->getAdditionalPreviewLanguages();
475 foreach ($prLang as $prL) {
476 $defInfo .= '<div class="typo3-TCEforms-originalLanguageValue">'
477 . $this->formEngine
->getLanguageIcon($table, $row, ('v' . $prL['ISOcode']))
478 . $this->formEngine
->previewFieldValue($editData[$key][('v' . $prL['ISOcode'])], $fakePA['fieldConf'], $field)
483 if ($vDEFkey != 'vDEF') {
484 $languageIcon = $this->formEngine
->getLanguageIcon($table, $row, $vDEFkey);
487 // possible linebreaks in the label through xml: \n => <br/>, usage of nl2br()
488 // not possible, so it's done through str_replace
489 $processedTitle = str_replace('\\n', '<br />', $theTitle);
490 $tRows[] = '<div class="t3-form-field-container t3-form-field-container-flex">'
491 . '<div class="t3-form-field-label t3-form-field-label-flex">' . $languageIcon
492 . BackendUtility
::wrapInHelp($PA['_cshKey'], $key, $processedTitle) . '</div>
493 <div class="t3-form-field t3-form-field-flex">' . $theFormEl . $defInfo
494 . $this->formEngine
->renderVDEFDiff($editData[$key], $vDEFkey) . '</div>
499 $output .= implode('', $tRows);