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