e6b731647cef233b785c6e594dc002f2fc33c663
[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\NodeFactory;
23 use TYPO3\CMS\Backend\Form\Utility\FormEngineUtility;
24 use TYPO3\CMS\Backend\Utility\BackendUtility;
25
26 /**
27 * The container handles single elements.
28 *
29 * This one is called by FlexFormTabsContainer, FlexFormNoTabsContainer or FlexFormContainerContainer.
30 * For single fields, the code is similar to SingleFieldContainer, processing will end up in single
31 * element classes depending on specific type of an element. Additionally, it determines if a
32 * section is handled and hands over to FlexFormSectionContainer in this case.
33 */
34 class FlexFormElementContainer extends AbstractContainer {
35
36 /**
37 * Entry method
38 *
39 * @return array As defined in initializeResultArray() of AbstractNode
40 */
41 public function render() {
42 $table = $this->globalOptions['table'];
43 $row = $this->globalOptions['databaseRow'];
44 $fieldName = $this->globalOptions['fieldName'];
45 $flexFormDataStructureArray = $this->globalOptions['flexFormDataStructureArray'];
46 $flexFormRowData = $this->globalOptions['flexFormRowData'];
47 $flexFormCurrentLanguage = $this->globalOptions['flexFormCurrentLanguage'];
48 $flexFormNoEditDefaultLanguage = $this->globalOptions['flexFormNoEditDefaultLanguage'];
49 $flexFormFormPrefix = $this->globalOptions['flexFormFormPrefix'];
50 $parameterArray = $this->globalOptions['parameterArray'];
51
52 $languageService = $this->getLanguageService();
53 $resultArray = $this->initializeResultArray();
54 foreach ($flexFormDataStructureArray as $flexFormFieldName => $flexFormFieldArray) {
55 if (
56 // No item array found at all
57 !is_array($flexFormFieldArray)
58 // Not a section or container and not a list of single items
59 || (!isset($flexFormFieldArray['type']) && !is_array($flexFormFieldArray['TCEforms']['config']))
60 ) {
61 continue;
62 }
63
64 if ($flexFormFieldArray['type'] === 'array') {
65 // Section
66 if (empty($flexFormFieldArray['section'])) {
67 $resultArray['html'] = LF . 'Section expected at ' . $flexFormFieldName . ' but not found';
68 continue;
69 }
70
71 $sectionTitle = '';
72 if (!empty($flexFormFieldArray['title'])) {
73 $sectionTitle = $languageService->sL($flexFormFieldArray['title']);
74 }
75
76 $options = $this->globalOptions;
77 $options['flexFormDataStructureArray'] = $flexFormFieldArray['el'];
78 $options['flexFormRowData'] = is_array($flexFormRowData[$flexFormFieldName]['el']) ? $flexFormRowData[$flexFormFieldName]['el'] : array();
79 $options['flexFormSectionType'] = $flexFormFieldName;
80 $options['flexFormSectionTitle'] = $sectionTitle;
81 /** @var FlexFormSectionContainer $sectionContainer */
82 $sectionContainer = GeneralUtility::makeInstance(FlexFormSectionContainer::class);
83 $sectionContainerResult = $sectionContainer->setGlobalOptions($options)->render();
84 $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $sectionContainerResult);
85 } else {
86 // Single element
87 $vDEFkey = 'vDEF';
88
89 $displayConditionResult = TRUE;
90 if (!empty($flexFormFieldArray['TCEforms']['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['TCEforms']['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['TCEforms']['label'])),
105 'config' => $flexFormFieldArray['TCEforms']['config'],
106 'defaultExtras' => $flexFormFieldArray['TCEforms']['defaultExtras'],
107 'onChange' => $flexFormFieldArray['TCEforms']['onChange'],
108 ),
109 );
110
111 // Force a none field if default language can not be edited
112 if ($flexFormNoEditDefaultLanguage && $flexFormCurrentLanguage === 'lDEF') {
113 $fakeParameterArray['fieldConf']['config'] = array(
114 'type' => 'none',
115 'rows' => 2
116 );
117 }
118
119 $alertMsgOnChange = '';
120 if (
121 $fakeParameterArray['fieldConf']['onChange'] === 'reload'
122 || !empty($GLOBALS['TCA'][$table]['ctrl']['type']) && $GLOBALS['TCA'][$table]['ctrl']['type'] === $flexFormFieldName
123 || !empty($GLOBALS['TCA'][$table]['ctrl']['requestUpdate']) && GeneralUtility::inList($GLOBALS['TCA'][$table]['ctrl']['requestUpdate'], $flexFormFieldName)
124 ) {
125 if ($this->getBackendUserAuthentication()->jsConfirmation(JsConfirmation::TYPE_CHANGE)) {
126 $alertMsgOnChange = 'if (confirm(TBE_EDITOR.labels.onChangeAlert) && TBE_EDITOR.checkSubmit(-1)){ TBE_EDITOR.submitForm() };';
127 } else {
128 $alertMsgOnChange = 'if (TBE_EDITOR.checkSubmit(-1)){ TBE_EDITOR.submitForm();}';
129 }
130 }
131 $fakeParameterArray['fieldChangeFunc'] = $parameterArray['fieldChangeFunc'];
132 if ($alertMsgOnChange) {
133 $fakeParameterArray['fieldChangeFunc']['alert'] = $alertMsgOnChange;
134 }
135
136 $fakeParameterArray['onFocus'] = $parameterArray['onFocus'];
137 $fakeParameterArray['label'] = $parameterArray['label'];
138 $fakeParameterArray['itemFormElName'] = $parameterArray['itemFormElName'] . $flexFormFormPrefix . '[' . $flexFormFieldName . '][' . $vDEFkey . ']';
139 $fakeParameterArray['itemFormElName_file'] = $parameterArray['itemFormElName_file'] . $flexFormFormPrefix . '[' . $flexFormFieldName . '][' . $vDEFkey . ']';
140 $fakeParameterArray['itemFormElID'] = $fakeParameterArray['itemFormElName'];
141 if (isset($flexFormRowData[$flexFormFieldName][$vDEFkey])) {
142 $fakeParameterArray['itemFormElValue'] = $flexFormRowData[$flexFormFieldName][$vDEFkey];
143 } else {
144 $fakeParameterArray['itemFormElValue'] = $fakeParameterArray['fieldConf']['config']['default'];
145 }
146
147 $options = $this->globalOptions;
148 $options['parameterArray'] = $fakeParameterArray;
149 /** @var NodeFactory $nodeFactory */
150 $nodeFactory = GeneralUtility::makeInstance(NodeFactory::class);
151 $child = $nodeFactory->create($flexFormFieldArray['TCEforms']['config']['type']);
152 $childResult = $child->setGlobalOptions($options)->render();
153
154 $theTitle = htmlspecialchars($fakeParameterArray['fieldConf']['label']);
155 $defInfo = array();
156 if (!$flexFormNoEditDefaultLanguage) {
157 $previewLanguages = $this->globalOptions['additionalPreviewLanguages'];
158 foreach ($previewLanguages as $previewLanguage) {
159 $defInfo[] = '<div class="t3-form-original-language">';
160 $defInfo[] = FormEngineUtility::getLanguageIcon($table, $row, ('v' . $previewLanguage['ISOcode']));
161 $defInfo[] = $this->previewFieldValue($flexFormRowData[$flexFormFieldName][('v' . $previewLanguage['ISOcode'])], $fakeParameterArray['fieldConf'], $fieldName);
162 $defInfo[] = '</div>';
163 }
164 }
165
166 $languageIcon = '';
167 if ($vDEFkey !== 'vDEF') {
168 $languageIcon = FormEngineUtility::getLanguageIcon($table, $row, $vDEFkey);
169 }
170 // Possible line breaks in the label through xml: \n => <br/>, usage of nl2br() not possible, so it's done through str_replace (?!)
171 $processedTitle = str_replace('\\n', '<br />', $theTitle);
172 // @todo: Similar to the processing within SingleElementContainer ... use it from there?!
173 $html = array();
174 $html[] = '<div class="form-section">';
175 $html[] = '<div class="form-group t3js-formengine-palette-field">';
176 $html[] = '<label class="t3js-formengine-label">';
177 $html[] = $languageIcon;
178 $html[] = BackendUtility::wrapInHelp($parameterArray['_cshKey'], $flexFormFieldName, $processedTitle);
179 $html[] = '</label>';
180 $html[] = '<div class="t3js-formengine-field-item">';
181 $html[] = $childResult['html'];
182 $html[] = implode(LF, $defInfo);
183 $html[] = $this->renderVDEFDiff($flexFormRowData[$flexFormFieldName], $vDEFkey);
184 $html[] = '</div>';
185 $html[] = '</div>';
186 $html[] = '</div>';
187
188 $resultArray['html'] .= implode(LF, $html);
189 $childResult['html'] = '';
190 $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $childResult);
191 }
192 }
193
194 return $resultArray;
195 }
196
197 /**
198 * Renders the diff-view of vDEF fields in flex forms
199 *
200 * @param array $vArray Record array of the record being edited
201 * @param string $vDEFkey HTML of the form field. This is what we add the content to.
202 * @return string Item string returned again, possibly with the original value added to.
203 */
204 protected function renderVDEFDiff($vArray, $vDEFkey) {
205 $item = NULL;
206 if (
207 $GLOBALS['TYPO3_CONF_VARS']['BE']['flexFormXMLincludeDiffBase'] && isset($vArray[$vDEFkey . '.vDEFbase'])
208 && (string)$vArray[$vDEFkey . '.vDEFbase'] !== (string)$vArray['vDEF']
209 ) {
210 // Create diff-result:
211 $diffUtility = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Utility\DiffUtility::class);
212 $diffres = $diffUtility->makeDiffDisplay($vArray[$vDEFkey . '.vDEFbase'], $vArray['vDEF']);
213 $item = '<div class="typo3-TCEforms-diffBox">' . '<div class="typo3-TCEforms-diffBox-header">'
214 . htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.changeInOrig')) . ':</div>' . $diffres . '</div>';
215 }
216 return $item;
217 }
218
219 /**
220 * @return LanguageService
221 */
222 protected function getLanguageService() {
223 return $GLOBALS['LANG'];
224 }
225
226 /**
227 * @return BackendUserAuthentication
228 */
229 protected function getBackendUserAuthentication() {
230 return $GLOBALS['BE_USER'];
231 }
232
233 }