104938e1a4f0349868c027bb2f47ce1f3c9f318b
[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\GeneralUtility;
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 = null;
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
199 // @todo: this is messy here - "additional hidden fields" should be handled elsewhere
200 $html = implode(LF, $this->hiddenFieldAccum);
201 $pageRenderer->addJsFile('EXT:backend/Resources/Public/JavaScript/md5.js');
202 // load the main module for FormEngine with all important JS functions
203 $this->requireJsModules['TYPO3/CMS/Backend/FormEngine'] = 'function(FormEngine) {
204 FormEngine.initialize(
205 ' . GeneralUtility::quoteJSvalue(BackendUtility::getModuleUrl('wizard_element_browser')) . ',
206 ' . ($GLOBALS['TYPO3_CONF_VARS']['SYS']['USdateFormat'] ? '1' : '0') . '
207 );
208 }';
209 $this->requireJsModules['TYPO3/CMS/Backend/FormEngineReview'] = null;
210
211 foreach ($this->requireJsModules as $moduleName => $callbacks) {
212 if (!is_array($callbacks)) {
213 $callbacks = [$callbacks];
214 }
215 foreach ($callbacks as $callback) {
216 $pageRenderer->loadRequireJsModule($moduleName, $callback);
217 }
218 }
219 $pageRenderer->loadJquery();
220 $beUserAuth = $this->getBackendUserAuthentication();
221
222 // define the window size of the element browser etc.
223 $popupWindowWidth = 800;
224 $popupWindowHeight = 600;
225 $popupWindowSize = trim($beUserAuth->getTSConfigVal('options.popupWindowSize'));
226 if (!empty($popupWindowSize)) {
227 list($popupWindowWidth, $popupWindowHeight) = GeneralUtility::intExplode('x', $popupWindowSize);
228 }
229
230 // define the window size of the popups within the RTE
231 $rtePopupWindowSize = trim($beUserAuth->getTSConfigVal('options.rte.popupWindowSize'));
232 if (!empty($rtePopupWindowSize)) {
233 list($rtePopupWindowWidth, $rtePopupWindowHeight) = GeneralUtility::trimExplode('x', $rtePopupWindowSize);
234 }
235 $rtePopupWindowWidth = !empty($rtePopupWindowWidth) ? (int)$rtePopupWindowWidth : ($popupWindowWidth);
236 $rtePopupWindowHeight = !empty($rtePopupWindowHeight) ? (int)$rtePopupWindowHeight : ($popupWindowHeight);
237
238 // Make textareas resizable and flexible ("autogrow" in height)
239 $textareaSettings = [
240 'autosize' => (bool)$beUserAuth->uc['resizeTextareas_Flexible'],
241 'RTEPopupWindow' => [
242 'width' => $rtePopupWindowWidth,
243 'height' => $rtePopupWindowHeight
244 ]
245 ];
246 $pageRenderer->addInlineSettingArray('Textarea', $textareaSettings);
247
248 $popupSettings = [
249 'PopupWindow' => [
250 'width' => $popupWindowWidth,
251 'height' => $popupWindowHeight
252 ]
253 ];
254 $pageRenderer->addInlineSettingArray('Popup', $popupSettings);
255
256 $pageRenderer->addJsFile('EXT:backend/Resources/Public/JavaScript/jsfunc.tbe_editor.js');
257 // Needed for FormEngine manipulation (date picker)
258 $dateFormat = ($GLOBALS['TYPO3_CONF_VARS']['SYS']['USdateFormat'] ? ['MM-DD-YYYY', 'HH:mm MM-DD-YYYY'] : ['DD-MM-YYYY', 'HH:mm DD-MM-YYYY']);
259 $pageRenderer->addInlineSetting('DateTimePicker', 'DateFormat', $dateFormat);
260
261 $pageRenderer->loadRequireJsModule('TYPO3/CMS/Filelist/FileListLocalisation');
262
263 $pageRenderer->addInlineLanguageLabelFile('EXT:lang/Resources/Private/Language/locallang_core.xlf', 'file_upload');
264 if (!empty($this->additionalInlineLanguageLabelFiles)) {
265 foreach ($this->additionalInlineLanguageLabelFiles as $additionalInlineLanguageLabelFile) {
266 $pageRenderer->addInlineLanguageLabelFile($additionalInlineLanguageLabelFile);
267 }
268 }
269 // We want to load jQuery-ui inside our js. Enable this using requirejs.
270 $pageRenderer->addJsFile('EXT:backend/Resources/Public/JavaScript/jsfunc.inline.js');
271
272 // todo: change these things in JS
273 $pageRenderer->addInlineLanguageLabelArray([
274 'FormEngine.noRecordTitle' => 'LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.no_title',
275 'FormEngine.fieldsChanged' => 'LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.fieldsChanged',
276 'FormEngine.fieldsMissing' => 'LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.fieldsMissing',
277 'FormEngine.maxItemsAllowed' => 'LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.maxItemsAllowed',
278 'FormEngine.refreshRequiredTitle' => 'LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:mess.refreshRequired.title',
279 'FormEngine.refreshRequiredContent' => 'LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:mess.refreshRequired.content',
280 'FormEngine.remainingCharacters' => 'LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.remainingCharacters',
281 ], true);
282
283 $out = LF . 'TBE_EDITOR.doSaveFieldName = "' . ($this->doSaveFieldName ? addslashes($this->doSaveFieldName) : '') . '";';
284
285 // Add JS required for inline fields
286 if (!empty($this->inlineData)) {
287 $out .= LF . 'inline.addToDataArray(' . json_encode($this->inlineData) . ');';
288 }
289 // $this->additionalJS_submit:
290 if ($this->additionalJavaScriptSubmit) {
291 $additionalJS_submit = implode('', $this->additionalJavaScriptSubmit);
292 $additionalJS_submit = str_replace([CR, LF], '', $additionalJS_submit);
293 $out .= 'TBE_EDITOR.addActionChecks("submit", "' . addslashes($additionalJS_submit) . '");';
294 }
295 $out .= LF . implode(LF, $this->additionalJavaScriptPost);
296
297 return $html . LF . TAB . GeneralUtility::wrapJS($out);
298 }
299
300 /**
301 * @return BackendUserAuthentication
302 */
303 protected function getBackendUserAuthentication()
304 {
305 return $GLOBALS['BE_USER'];
306 }
307
308 /**
309 * Wrapper for access to the current page renderer object
310 *
311 * @return \TYPO3\CMS\Core\Page\PageRenderer
312 */
313 protected function getPageRenderer()
314 {
315 if ($this->pageRenderer === null) {
316 $this->pageRenderer = GeneralUtility::makeInstance(PageRenderer::class);
317 }
318 return $this->pageRenderer;
319 }
320 }