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