d445736d1f41bb1ce6264da33387f74c0f7b1434
[Packages/TYPO3.CMS.git] / typo3 / sysext / t3editor / Classes / Form / Element / T3editorElement.php
1 <?php
2 namespace TYPO3\CMS\T3editor\Form\Element;
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\Form\Element\AbstractFormElement;
18 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
19 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
20 use TYPO3\CMS\Core\Utility\GeneralUtility;
21 use TYPO3\CMS\Core\Utility\MathUtility;
22 use TYPO3\CMS\Lang\LanguageService;
23 use TYPO3\CMS\T3editor\T3editor;
24
25 /**
26 * t3editor FormEngine widget
27 */
28 class T3editorElement extends AbstractFormElement
29 {
30 const MODE_CSS = 'css';
31 const MODE_HTML = 'html';
32 const MODE_JAVASCRIPT = 'javascript';
33 const MODE_MIXED = 'mixed';
34 const MODE_PHP = 'php';
35 const MODE_SPARQL = 'sparql';
36 const MODE_TYPOSCRIPT = 'typoscript';
37 const MODE_XML = 'xml';
38
39 /**
40 * @var array
41 */
42 protected $allowedModes = [
43 self::MODE_CSS,
44 self::MODE_HTML,
45 self::MODE_JAVASCRIPT,
46 self::MODE_MIXED,
47 self::MODE_PHP,
48 self::MODE_SPARQL,
49 self::MODE_TYPOSCRIPT,
50 self::MODE_XML,
51 ];
52
53 /**
54 * @var array
55 */
56 protected $resultArray;
57
58 /**
59 * @var string
60 */
61 protected $mode = '';
62
63 /**
64 * Counts the editors on the current page
65 *
66 * @var int
67 */
68 protected $editorCounter = 0;
69
70 /**
71 * Relative path to EXT:t3editor
72 *
73 * @var string
74 */
75 protected $relExtPath = '';
76
77 /**
78 * @var string
79 */
80 protected $codemirrorPath = 'Resources/Public/JavaScript/Contrib/codemirror/js/';
81
82 /**
83 * RequireJS modules loaded for code completion
84 *
85 * @var array
86 */
87 protected $codeCompletionComponents = ['TsRef', 'CompletionResult', 'TsParser', 'TsCodeCompletion'];
88
89 /**
90 * @var string
91 */
92 protected $ajaxSaveType = '';
93
94 /**
95 * Render t3editor element
96 *
97 * @return array As defined in initializeResultArray() of AbstractNode
98 */
99 public function render()
100 {
101 $this->getLanguageService()->includeLLFile('EXT:t3editor/Resources/Private/Language/locallang.xlf');
102 $this->relExtPath = ExtensionManagementUtility::extRelPath('t3editor');
103 $this->codemirrorPath = $this->relExtPath . $this->codemirrorPath;
104
105 $this->resultArray = $this->initializeResultArray();
106
107 $parameterArray = $this->data['parameterArray'];
108
109 $rows = MathUtility::forceIntegerInRange($parameterArray['fieldConf']['config']['rows'] ?: 10, 1, 40);
110 $this->setMode($parameterArray['fieldConf']['config']['format'] ?? T3editor::MODE_MIXED);
111 $this->ajaxSaveType = $parameterArray['fieldConf']['config']['ajaxSaveType'] ?? '';
112
113 $attributes = [];
114 $attributes['rows'] = $rows;
115 $attributes['wrap'] = 'off';
116 $attributes['style'] = 'width:100%;';
117 $attributes['onchange'] = GeneralUtility::quoteJSvalue($parameterArray['fieldChangeFunc']['TBE_EDITOR_fieldChanged']);
118
119 $attributeString = '';
120 foreach ($attributes as $param => $value) {
121 $attributeString .= $param . '="' . htmlspecialchars($value) . '" ';
122 }
123
124 $this->resultArray['html'] = $this->getHTMLCodeForEditor(
125 $parameterArray['itemFormElName'],
126 'text-monospace enable-tab',
127 $parameterArray['itemFormElValue'],
128 $attributeString,
129 $this->data['tableName'] . ' > ' . $this->data['fieldName'],
130 [
131 'target' => 0,
132 'id' => (int)$this->data['databaseRow']['pageId']
133 ]
134 );
135 $this->resultArray['additionalJavaScriptPost'][] = 'require(["TYPO3/CMS/T3editor/T3editor"], function(T3editor) {T3editor.findAndInitializeEditors();});';
136
137 $this->initJavascriptCode();
138 return $this->resultArray;
139 }
140
141 /**
142 * Sets the type of code to edit, use one of the predefined constants
143 *
144 * @param string $mode Expects one of the predefined constants
145 * @throws \InvalidArgumentException
146 */
147 public function setMode($mode)
148 {
149 if (!in_array($mode, $this->allowedModes, true)) {
150 throw new \InvalidArgumentException($mode . 'is not allowed', 1438352574);
151 }
152 $this->mode = $mode;
153 }
154
155 /**
156 * Get mode
157 *
158 * @return string
159 */
160 public function getMode()
161 {
162 return $this->mode;
163 }
164
165 /**
166 * Init the JavaScript code (header part) for editor
167 */
168 protected function initJavascriptCode()
169 {
170 $this->resultArray['stylesheetFiles'][] = 'EXT:t3editor/Resources/Public/Css/t3editor.css';
171 if ($this->mode === self::MODE_TYPOSCRIPT) {
172 foreach ($this->codeCompletionComponents as $codeCompletionComponent) {
173 $this->resultArray['requireJsModules'][] = 'TYPO3/CMS/T3editor/Plugins/CodeCompletion/' . $codeCompletionComponent;
174 }
175 }
176 }
177
178 /**
179 * Generates HTML with code editor
180 *
181 * @param string $name Name attribute of HTML tag
182 * @param string $class Class attribute of HTML tag
183 * @param string $content Content of the editor
184 * @param string $additionalParams Any additional editor parameters
185 * @param string $alt Alt attribute
186 * @param array $hiddenfields
187 * @return string Generated HTML code for editor
188 */
189 protected function getHTMLCodeForEditor($name, $class = '', $content = '', $additionalParams = '', $alt = '', array $hiddenfields = [])
190 {
191 $code = [];
192 $attributes = [];
193 $attributes['class'] = $class . ' t3editor';
194 $attributes['alt'] = $alt;
195 $attributes['id'] = 't3editor_' . $this->editorCounter;
196 $attributes['name'] = $name;
197 $attributes['data-labels'] = json_encode($this->getLanguageService()->getLabelsWithPrefix('js.', 'label_'));
198 $attributes['data-instance-number'] = $this->editorCounter;
199 $attributes['data-editor-path'] = $this->relExtPath;
200 $attributes['data-codemirror-path'] = $this->codemirrorPath;
201 $attributes['data-ajaxsavetype'] = htmlspecialchars($this->ajaxSaveType);
202 $attributes['data-parserfile'] = $this->getParserfileByMode($this->mode);
203 $attributes['data-stylesheet'] = $this->getStylesheetByMode($this->mode);
204
205 $attributesString = '';
206 foreach ($attributes as $attribute => $value) {
207 $attributesString .= $attribute . '="' . htmlspecialchars($value) . '" ';
208 }
209 $attributesString .= $additionalParams;
210
211 $code[] = '<div class="t3editor">';
212 $code[] = ' <div class="t3e_wrap">';
213 $code[] = str_replace([CR, LF], '', file_get_contents(GeneralUtility::getFileAbsFileName('EXT:t3editor/Resources/Private/Templates/t3editor.html')));
214 $code[] = ' </div>';
215 $code[] = ' <textarea ' . $attributesString . '>' . htmlspecialchars($content) . '</textarea>';
216 $code[] = '</div>';
217
218 if (!empty($hiddenfields)) {
219 foreach ($hiddenfields as $name => $value) {
220 $code[] = '<input type="hidden" name="' . htmlspecialchars($name) . '" value="' . htmlspecialchars($value) . '" />';
221 }
222 }
223 $this->editorCounter++;
224 return implode(LF, $code);
225 }
226
227 /**
228 * Determine the correct parser js file for given mode
229 *
230 * @param string $mode
231 * @return string Parser file name
232 */
233 protected function getParserfileByMode($mode)
234 {
235 $parserfile = [];
236 switch ($mode) {
237 case self::MODE_TYPOSCRIPT:
238 $relPath = '../../../parse_typoscript/';
239 $parserfile = [$relPath . 'tokenizetyposcript.js', $relPath . 'parsetyposcript.js'];
240 break;
241 case self::MODE_JAVASCRIPT:
242 $parserfile = ['tokenizejavascript.js', 'parsejavascript.js'];
243 break;
244 case self::MODE_CSS:
245 $parserfile = ['parsecss.js'];
246 break;
247 case self::MODE_XML:
248 $parserfile = ['parsexml.js'];
249 break;
250 case self::MODE_SPARQL:
251 $parserfile = ['parsesparql.js'];
252 break;
253 case self::MODE_HTML:
254 $parserfile = ['tokenizejavascript.js', 'parsejavascript.js', 'parsecss.js', 'parsexml.js', 'parsehtmlmixed.js'];
255 break;
256 case self::MODE_PHP:
257 case self::MODE_MIXED:
258 $parserfile = ['tokenizejavascript.js', 'parsejavascript.js', 'parsecss.js', 'parsexml.js', '../contrib/php/js/tokenizephp.js', '../contrib/php/js/parsephp.js', '../contrib/php/js/parsephphtmlmixed.js'];
259 break;
260 }
261 return json_encode($parserfile);
262 }
263
264 /**
265 * Determine the correct css file for given mode
266 *
267 * @param string $mode
268 * @return string css file name
269 */
270 protected function getStylesheetByMode($mode)
271 {
272 switch ($mode) {
273 case self::MODE_TYPOSCRIPT:
274 $stylesheet = [$this->relExtPath . 'Resources/Public/Css/t3editor_typoscript_colors.css'];
275 break;
276 case self::MODE_JAVASCRIPT:
277 $stylesheet = [$this->codemirrorPath . '../css/jscolors.css'];
278 break;
279 case self::MODE_CSS:
280 $stylesheet = [$this->codemirrorPath . '../css/csscolors.css'];
281 break;
282 case self::MODE_XML:
283 $stylesheet = [$this->codemirrorPath . '../css/xmlcolors.css'];
284 break;
285 case self::MODE_HTML:
286 $stylesheet = [$this->codemirrorPath . '../css/xmlcolors.css', $this->codemirrorPath . '../css/jscolors.css', $this->codemirrorPath . '../css/csscolors.css'];
287 break;
288 case self::MODE_SPARQL:
289 $stylesheet = [$this->codemirrorPath . '../css/sparqlcolors.css'];
290 break;
291 case self::MODE_PHP:
292 $stylesheet = [$this->codemirrorPath . '../contrib/php/css/phpcolors.css'];
293 break;
294 case self::MODE_MIXED:
295 $stylesheet = [$this->codemirrorPath . '../css/xmlcolors.css', $this->codemirrorPath . '../css/jscolors.css', $this->codemirrorPath . '../css/csscolors.css', $this->codemirrorPath . '../contrib/php/css/phpcolors.css'];
296 break;
297 default:
298 $stylesheet = [];
299 }
300 $stylesheet[] = $this->relExtPath . 'Resources/Public/Css/t3editor_inner.css';
301 return json_encode($stylesheet);
302 }
303
304 /**
305 * @return LanguageService
306 */
307 protected function getLanguageService()
308 {
309 return $GLOBALS['LANG'];
310 }
311
312 /**
313 * @return BackendUserAuthentication
314 */
315 protected function getBackendUserAuthentication()
316 {
317 return $GLOBALS['BE_USER'];
318 }
319 }