[TASK] Unify element- and linkbrowser styling
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Form / FormResultCompiler.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\Utility\BackendUtility;
18 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
19 use TYPO3\CMS\Core\Page\PageRenderer;
20 use TYPO3\CMS\Core\Utility\ArrayUtility;
21 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
22 use TYPO3\CMS\Core\Utility\GeneralUtility;
23
24 /**
25 * This is form engine - Class for creating the backend editing forms.
26 *
27 * @internal This class and its exposed method and method signatures will change
28 */
29 class FormResultCompiler
30 {
31 /**
32 * @var array HTML of additional hidden fields rendered by sub containers
33 */
34 protected $hiddenFieldAccum = [];
35
36 /**
37 * Can be set to point to a field name in the form which will be set to '1' when the form
38 * is submitted with a *save* button. This way the recipient script can determine that
39 * the form was submitted for save and not "close" for example.
40 *
41 * @var string
42 */
43 protected $doSaveFieldName = '';
44
45 /**
46 * @var array Data array from IRRE pushed to frontend as json array
47 */
48 protected $inlineData = [];
49
50 /**
51 * List of additional style sheet files to load
52 *
53 * @var array
54 */
55 protected $stylesheetFiles = [];
56
57 /**
58 * Additional JavaScript printed after the form
59 *
60 * @var array
61 */
62 protected $additionalJavaScriptPost = [];
63
64 /**
65 * Additional JavaScript executed on submit; If you set "OK" variable it will raise an error
66 * about RTEs not being loaded and offer to block further submission.
67 *
68 * @var array
69 */
70 protected $additionalJavaScriptSubmit = [];
71
72 /**
73 * Additional language label files to include.
74 *
75 * @var array
76 */
77 protected $additionalInlineLanguageLabelFiles = [];
78
79 /**
80 * Array with requireJS modules, use module name as key, the value could be callback code.
81 * Use NULL as value if no callback is used.
82 *
83 * @var array
84 */
85 protected $requireJsModules = [];
86
87 /**
88 * @var PageRenderer
89 */
90 protected $pageRenderer = null;
91
92 /**
93 * Merge existing data with the given result array
94 *
95 * @param array $resultArray Array returned by child
96 * @internal Temporary method to use FormEngine class as final data merger
97 */
98 public function mergeResult(array $resultArray)
99 {
100 $this->doSaveFieldName = $resultArray['doSaveFieldName'];
101 foreach ($resultArray['additionalJavaScriptPost'] as $element) {
102 $this->additionalJavaScriptPost[] = $element;
103 }
104 foreach ($resultArray['additionalJavaScriptSubmit'] as $element) {
105 $this->additionalJavaScriptSubmit[] = $element;
106 }
107 if (!empty($resultArray['requireJsModules'])) {
108 foreach ($resultArray['requireJsModules'] as $module) {
109 $moduleName = null;
110 $callback = null;
111 if (is_string($module)) {
112 // if $module is a string, no callback
113 $moduleName = $module;
114 $callback = null;
115 } elseif (is_array($module)) {
116 // if $module is an array, callback is possible
117 foreach ($module as $key => $value) {
118 $moduleName = $key;
119 $callback = $value;
120 break;
121 }
122 }
123 if ($moduleName !== null) {
124 if (!empty($this->requireJsModules[$moduleName]) && $callback !== null) {
125 $existingValue = $this->requireJsModules[$moduleName];
126 if (!is_array($existingValue)) {
127 $existingValue = [$existingValue];
128 }
129 $existingValue[] = $callback;
130 $this->requireJsModules[$moduleName] = $existingValue;
131 } else {
132 $this->requireJsModules[$moduleName] = $callback;
133 }
134 }
135 }
136 }
137
138 foreach ($resultArray['additionalHiddenFields'] as $element) {
139 $this->hiddenFieldAccum[] = $element;
140 }
141 foreach ($resultArray['stylesheetFiles'] as $stylesheetFile) {
142 if (!in_array($stylesheetFile, $this->stylesheetFiles)) {
143 $this->stylesheetFiles[] = $stylesheetFile;
144 }
145 }
146
147 if (!empty($resultArray['inlineData'])) {
148 $resultArrayInlineData = $this->inlineData;
149 $resultInlineData = $resultArray['inlineData'];
150 ArrayUtility::mergeRecursiveWithOverrule($resultArrayInlineData, $resultInlineData);
151 $this->inlineData = $resultArrayInlineData;
152 }
153
154 if (!empty($resultArray['additionalInlineLanguageLabelFiles'])) {
155 foreach ($resultArray['additionalInlineLanguageLabelFiles'] as $additionalInlineLanguageLabelFile) {
156 $this->additionalInlineLanguageLabelFiles[] = $additionalInlineLanguageLabelFile;
157 }
158 }
159 }
160
161 /**
162 * JavaScript code added BEFORE the form is drawn:
163 *
164 * @return string
165 * @deprecated since TYPO3 v8, will be removed in TYPO3 v9
166 */
167 public function JStop()
168 {
169 GeneralUtility::logDeprecatedFunction();
170 return $this->addCssFiles();
171 }
172
173 /**
174 * Adds CSS files BEFORE the form is drawn
175 *
176 * @return string
177 */
178 public function addCssFiles()
179 {
180 $pageRenderer = $this->getPageRenderer();
181 foreach ($this->stylesheetFiles as $stylesheetFile) {
182 $pageRenderer->addCssFile($stylesheetFile);
183 }
184 return '';
185 }
186
187 /**
188 * Prints necessary JavaScript for TCEforms (after the form HTML).
189 * currently this is used to transform page-specific options in the TYPO3.Settings array for JS
190 * so the JS module can access these values
191 *
192 * @return string
193 */
194 public function printNeededJSFunctions()
195 {
196 // set variables to be accessible for JS
197 $pageRenderer = $this->getPageRenderer();
198 $pageRenderer->addInlineSetting('FormEngine', 'formName', 'editform');
199
200 return $this->JSbottom();
201 }
202
203 /**
204 * JavaScript bottom code
205 *
206 * @return string A section with JavaScript - if $update is FALSE, embedded in <script></script>
207 */
208 protected function JSbottom()
209 {
210 $pageRenderer = $this->getPageRenderer();
211
212 // @todo: this is messy here - "additional hidden fields" should be handled elsewhere
213 $html = implode(LF, $this->hiddenFieldAccum);
214 $pageRenderer->addJsFile('EXT:backend/Resources/Public/JavaScript/md5.js');
215 // load the main module for FormEngine with all important JS functions
216 $this->requireJsModules['TYPO3/CMS/Backend/FormEngine'] = 'function(FormEngine) {
217 FormEngine.initialize(
218 ' . GeneralUtility::quoteJSvalue(BackendUtility::getModuleUrl('wizard_element_browser')) . ',
219 ' . ($GLOBALS['TYPO3_CONF_VARS']['SYS']['USdateFormat'] ? '1' : '0') . '
220 );
221 }';
222 $this->requireJsModules['TYPO3/CMS/Backend/FormEngineReview'] = null;
223
224 foreach ($this->requireJsModules as $moduleName => $callbacks) {
225 if (!is_array($callbacks)) {
226 $callbacks = [$callbacks];
227 }
228 foreach ($callbacks as $callback) {
229 $pageRenderer->loadRequireJsModule($moduleName, $callback);
230 }
231 }
232 $pageRenderer->loadJquery();
233 $beUserAuth = $this->getBackendUserAuthentication();
234
235 // define the window size of the element browser etc.
236 $popupWindowWidth = 800;
237 $popupWindowHeight = 600;
238 $popupWindowSize = trim($beUserAuth->getTSConfigVal('options.popupWindowSize'));
239 if (!empty($popupWindowSize)) {
240 list($popupWindowWidth, $popupWindowHeight) = GeneralUtility::intExplode('x', $popupWindowSize);
241 }
242
243 // define the window size of the popups within the RTE
244 $rtePopupWindowSize = trim($beUserAuth->getTSConfigVal('options.rte.popupWindowSize'));
245 if (!empty($rtePopupWindowSize)) {
246 list($rtePopupWindowWidth, $rtePopupWindowHeight) = GeneralUtility::trimExplode('x', $rtePopupWindowSize);
247 }
248 $rtePopupWindowWidth = !empty($rtePopupWindowWidth) ? (int)$rtePopupWindowWidth : ($popupWindowWidth);
249 $rtePopupWindowHeight = !empty($rtePopupWindowHeight) ? (int)$rtePopupWindowHeight : ($popupWindowHeight);
250
251 // Make textareas resizable and flexible ("autogrow" in height)
252 $textareaSettings = [
253 'autosize' => (bool)$beUserAuth->uc['resizeTextareas_Flexible'],
254 'RTEPopupWindow' => [
255 'width' => $rtePopupWindowWidth,
256 'height' => $rtePopupWindowHeight
257 ]
258 ];
259 $pageRenderer->addInlineSettingArray('Textarea', $textareaSettings);
260
261 $popupSettings = [
262 'PopupWindow' => [
263 'width' => $popupWindowWidth,
264 'height' => $popupWindowHeight
265 ]
266 ];
267 $pageRenderer->addInlineSettingArray('Popup', $popupSettings);
268
269 $pageRenderer->addJsFile('EXT:backend/Resources/Public/JavaScript/jsfunc.tbe_editor.js');
270 // Needed for FormEngine manipulation (date picker)
271 $dateFormat = ($GLOBALS['TYPO3_CONF_VARS']['SYS']['USdateFormat'] ? ['MM-DD-YYYY', 'HH:mm MM-DD-YYYY'] : ['DD-MM-YYYY', 'HH:mm DD-MM-YYYY']);
272 $pageRenderer->addInlineSetting('DateTimePicker', 'DateFormat', $dateFormat);
273
274 $pageRenderer->loadRequireJsModule('TYPO3/CMS/Filelist/FileListLocalisation');
275
276 $pageRenderer->addInlineLanguageLabelFile('EXT:lang/Resources/Private/Language/locallang_core.xlf', 'file_upload');
277 if (!empty($this->additionalInlineLanguageLabelFiles)) {
278 foreach ($this->additionalInlineLanguageLabelFiles as $additionalInlineLanguageLabelFile) {
279 $pageRenderer->addInlineLanguageLabelFile($additionalInlineLanguageLabelFile);
280 }
281 }
282 // Load codemirror for T3Editor
283 if (ExtensionManagementUtility::isLoaded('t3editor')) {
284 $pageRenderer->addJsFile('EXT:t3editor/Resources/Public/JavaScript/Contrib/codemirror/js/codemirror.js');
285 }
286 // We want to load jQuery-ui inside our js. Enable this using requirejs.
287 $pageRenderer->addJsFile('EXT:backend/Resources/Public/JavaScript/jsfunc.inline.js');
288
289 // todo: change these things in JS
290 $pageRenderer->addInlineLanguageLabelArray([
291 'FormEngine.noRecordTitle' => 'LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.no_title',
292 'FormEngine.fieldsChanged' => 'LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.fieldsChanged',
293 'FormEngine.fieldsMissing' => 'LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.fieldsMissing',
294 'FormEngine.maxItemsAllowed' => 'LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.maxItemsAllowed',
295 'FormEngine.refreshRequiredTitle' => 'LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:mess.refreshRequired.title',
296 'FormEngine.refreshRequiredContent' => 'LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:mess.refreshRequired.content',
297 'FormEngine.remainingCharacters' => 'LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.remainingCharacters',
298 ], true);
299
300 $out = LF . 'TBE_EDITOR.doSaveFieldName = "' . ($this->doSaveFieldName ? addslashes($this->doSaveFieldName) : '') . '";';
301
302 // Add JS required for inline fields
303 if (!empty($this->inlineData)) {
304 $out .= LF . 'inline.addToDataArray(' . json_encode($this->inlineData) . ');';
305 }
306 // $this->additionalJS_submit:
307 if ($this->additionalJavaScriptSubmit) {
308 $additionalJS_submit = implode('', $this->additionalJavaScriptSubmit);
309 $additionalJS_submit = str_replace([CR, LF], '', $additionalJS_submit);
310 $out .= 'TBE_EDITOR.addActionChecks("submit", "' . addslashes($additionalJS_submit) . '");';
311 }
312 $out .= LF . implode(LF, $this->additionalJavaScriptPost);
313
314 return $html . LF . TAB . GeneralUtility::wrapJS($out);
315 }
316
317 /**
318 * @return BackendUserAuthentication
319 */
320 protected function getBackendUserAuthentication()
321 {
322 return $GLOBALS['BE_USER'];
323 }
324
325 /**
326 * Wrapper for access to the current page renderer object
327 *
328 * @return \TYPO3\CMS\Core\Page\PageRenderer
329 */
330 protected function getPageRenderer()
331 {
332 if ($this->pageRenderer === null) {
333 $this->pageRenderer = GeneralUtility::makeInstance(PageRenderer::class);
334 }
335 return $this->pageRenderer;
336 }
337 }