06c2ef29f74de2899831bee1b4360673591c9efd
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Form / Container / FlexFormElementContainer.php
1 <?php
2 namespace TYPO3\CMS\Backend\Form\Container;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Backend\Form\ElementConditionMatcher;
18 use TYPO3\CMS\Core\Utility\GeneralUtility;
19 use TYPO3\CMS\Lang\LanguageService;
20 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
21 use TYPO3\CMS\Core\Type\Bitmask\JsConfirmation;
22 use TYPO3\CMS\Backend\Form\Utility\FormEngineUtility;
23 use TYPO3\CMS\Backend\Utility\BackendUtility;
24
25 /**
26 * The container handles single elements.
27 *
28 * This one is called by FlexFormTabsContainer, FlexFormNoTabsContainer or FlexFormContainerContainer.
29 * For single fields, the code is similar to SingleFieldContainer, processing will end up in single
30 * element classes depending on specific type of an element. Additionally, it determines if a
31 * section is handled and hands over to FlexFormSectionContainer in this case.
32 */
33 class FlexFormElementContainer extends AbstractContainer {
34
35 /**
36 * Entry method
37 *
38 * @return array As defined in initializeResultArray() of AbstractNode
39 */
40 public function render() {
41 $table = $this->data['tableName'];
42 $row = $this->data['databaseRow'];
43 $fieldName = $this->data['fieldName'];
44 $flexFormDataStructureArray = $this->data['flexFormDataStructureArray'];
45 $flexFormRowData = $this->data['flexFormRowData'];
46 $flexFormFormPrefix = $this->data['flexFormFormPrefix'];
47 $parameterArray = $this->data['parameterArray'];
48
49 $languageService = $this->getLanguageService();
50 $resultArray = $this->initializeResultArray();
51 foreach ($flexFormDataStructureArray as $flexFormFieldName => $flexFormFieldArray) {
52 if (
53 // No item array found at all
54 !is_array($flexFormFieldArray)
55 // Not a section or container and not a list of single items
56 || (!isset($flexFormFieldArray['type']) && !is_array($flexFormFieldArray['config']))
57 ) {
58 continue;
59 }
60
61 if ($flexFormFieldArray['type'] === 'array') {
62 // Section
63 if (empty($flexFormFieldArray['section'])) {
64 $resultArray['html'] = LF . 'Section expected at ' . $flexFormFieldName . ' but not found';
65 continue;
66 }
67
68 $sectionTitle = '';
69 if (!empty($flexFormFieldArray['title'])) {
70 $sectionTitle = $languageService->sL($flexFormFieldArray['title']);
71 }
72
73 $options = $this->data;
74 $options['flexFormDataStructureArray'] = $flexFormFieldArray['el'];
75 $options['flexFormRowData'] = is_array($flexFormRowData[$flexFormFieldName]['el']) ? $flexFormRowData[$flexFormFieldName]['el'] : array();
76 $options['flexFormSectionType'] = $flexFormFieldName;
77 $options['flexFormSectionTitle'] = $sectionTitle;
78 $options['renderType'] = 'flexFormSectionContainer';
79 $sectionContainerResult = $this->nodeFactory->create($options)->render();
80 $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $sectionContainerResult);
81 } else {
82 // Single element
83 // @todo: There is a bug in here - with langChildren = 1, "single" fields can be localized.
84 // @todo: This case is not handled correctly here, see for details method #2 of
85 // @todo: https://docs.typo3.org/typo3cms/TCAReference/Reference/Columns/Flex/Index.html#handling-languages-in-flexforms
86 // @todo: All data should be properly prepared by flex provider already
87 $vDEFkey = 'vDEF';
88
89 $displayConditionResult = TRUE;
90 if (!empty($flexFormFieldArray['displayCond'])) {
91 $conditionData = is_array($flexFormRowData) ? $flexFormRowData : array();
92 $conditionData['parentRec'] = $row;
93 /** @var $elementConditionMatcher ElementConditionMatcher */
94 $elementConditionMatcher = GeneralUtility::makeInstance(ElementConditionMatcher::class);
95 $displayConditionResult = $elementConditionMatcher->match($flexFormFieldArray['displayCond'], $conditionData, $vDEFkey);
96 }
97 if (!$displayConditionResult) {
98 continue;
99 }
100
101 // Set up options for single element
102 $fakeParameterArray = array(
103 'fieldConf' => array(
104 'label' => $languageService->sL(trim($flexFormFieldArray['label'])),
105 'config' => $flexFormFieldArray['config'],
106 'defaultExtras' => $flexFormFieldArray['defaultExtras'],
107 'onChange' => $flexFormFieldArray['onChange'],
108 ),
109 );
110
111 $alertMsgOnChange = '';
112 if (
113 $fakeParameterArray['fieldConf']['onChange'] === 'reload'
114 || !empty($GLOBALS['TCA'][$table]['ctrl']['type']) && $GLOBALS['TCA'][$table]['ctrl']['type'] === $flexFormFieldName
115 || !empty($GLOBALS['TCA'][$table]['ctrl']['requestUpdate']) && GeneralUtility::inList($GLOBALS['TCA'][$table]['ctrl']['requestUpdate'], $flexFormFieldName)
116 ) {
117 if ($this->getBackendUserAuthentication()->jsConfirmation(JsConfirmation::TYPE_CHANGE)) {
118 $alertMsgOnChange = 'if (confirm(TBE_EDITOR.labels.onChangeAlert) && TBE_EDITOR.checkSubmit(-1)){ TBE_EDITOR.submitForm() };';
119 } else {
120 $alertMsgOnChange = 'if (TBE_EDITOR.checkSubmit(-1)){ TBE_EDITOR.submitForm();}';
121 }
122 }
123 $fakeParameterArray['fieldChangeFunc'] = $parameterArray['fieldChangeFunc'];
124 if ($alertMsgOnChange) {
125 $fakeParameterArray['fieldChangeFunc']['alert'] = $alertMsgOnChange;
126 }
127
128 $fakeParameterArray['onFocus'] = $parameterArray['onFocus'];
129 $fakeParameterArray['label'] = $parameterArray['label'];
130 $fakeParameterArray['itemFormElName'] = $parameterArray['itemFormElName'] . $flexFormFormPrefix . '[' . $flexFormFieldName . '][' . $vDEFkey . ']';
131 $fakeParameterArray['itemFormElID'] = $fakeParameterArray['itemFormElName'];
132 if (isset($flexFormRowData[$flexFormFieldName][$vDEFkey])) {
133 $fakeParameterArray['itemFormElValue'] = $flexFormRowData[$flexFormFieldName][$vDEFkey];
134 } else {
135 $fakeParameterArray['itemFormElValue'] = $fakeParameterArray['fieldConf']['config']['default'];
136 }
137
138 $options = $this->data;
139 $options['parameterArray'] = $fakeParameterArray;
140 $options['elementBaseName'] = $this->data['elementBaseName'] . $flexFormFormPrefix . '[' . $flexFormFieldName . '][' . $vDEFkey . ']';
141
142 if (!empty($flexFormFieldArray['config']['renderType'])) {
143 $options['renderType'] = $flexFormFieldArray['config']['renderType'];
144 } else {
145 // Fallback to type if no renderType is given
146 $options['renderType'] = $flexFormFieldArray['config']['type'];
147 }
148 $childResult = $this->nodeFactory->create($options)->render();
149
150 $theTitle = htmlspecialchars($fakeParameterArray['fieldConf']['label']);
151 $defInfo = array();
152
153 $languageIcon = '';
154 if ($vDEFkey !== 'vDEF') {
155 $languageIcon = FormEngineUtility::getLanguageIcon($table, $row, $vDEFkey);
156 }
157 // Possible line breaks in the label through xml: \n => <br/>, usage of nl2br() not possible, so it's done through str_replace (?!)
158 $processedTitle = str_replace('\\n', '<br />', $theTitle);
159 // @todo: Similar to the processing within SingleElementContainer ... use it from there?!
160 $html = array();
161 $html[] = '<div class="form-section">';
162 $html[] = '<div class="form-group t3js-formengine-palette-field t3js-formengine-validation-marker">';
163 $html[] = '<label class="t3js-formengine-label">';
164 $html[] = $languageIcon;
165 $html[] = BackendUtility::wrapInHelp($parameterArray['_cshKey'], $flexFormFieldName, $processedTitle);
166 $html[] = '</label>';
167 $html[] = '<div class="t3js-formengine-field-item">';
168 $html[] = $childResult['html'];
169 $html[] = implode(LF, $defInfo);
170 $html[] = $this->renderVDEFDiff($flexFormRowData[$flexFormFieldName], $vDEFkey);
171 $html[] = '</div>';
172 $html[] = '</div>';
173 $html[] = '</div>';
174
175 $resultArray['html'] .= implode(LF, $html);
176 $childResult['html'] = '';
177 $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $childResult);
178 }
179 }
180
181 return $resultArray;
182 }
183
184 /**
185 * Renders the diff-view of vDEF fields in flex forms
186 *
187 * @param array $vArray Record array of the record being edited
188 * @param string $vDEFkey HTML of the form field. This is what we add the content to.
189 * @return string Item string returned again, possibly with the original value added to.
190 */
191 protected function renderVDEFDiff($vArray, $vDEFkey) {
192 $item = NULL;
193 if (
194 $GLOBALS['TYPO3_CONF_VARS']['BE']['flexFormXMLincludeDiffBase'] && isset($vArray[$vDEFkey . '.vDEFbase'])
195 && (string)$vArray[$vDEFkey . '.vDEFbase'] !== (string)$vArray['vDEF']
196 ) {
197 // Create diff-result:
198 $diffUtility = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Utility\DiffUtility::class);
199 $diffres = $diffUtility->makeDiffDisplay($vArray[$vDEFkey . '.vDEFbase'], $vArray['vDEF']);
200 $item = '<div class="typo3-TCEforms-diffBox">' . '<div class="typo3-TCEforms-diffBox-header">'
201 . htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.changeInOrig')) . ':</div>' . $diffres . '</div>';
202 }
203 return $item;
204 }
205
206 /**
207 * @return LanguageService
208 */
209 protected function getLanguageService() {
210 return $GLOBALS['LANG'];
211 }
212
213 /**
214 * @return BackendUserAuthentication
215 */
216 protected function getBackendUserAuthentication() {
217 return $GLOBALS['BE_USER'];
218 }
219
220 }