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