[BUGFIX] Use correct tokenizer for JavaScript in T3Editor
[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 = array(
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 = 'sysext/t3editor/Resources/Public/JavaScript/Contrib/codemirror/js/';
81
82 /**
83 * RequireJS modules loaded for code completion
84 *
85 * @var array
86 */
87 protected $codeCompletionComponents = array('TsRef', 'CompletionResult', 'TsParser', 'TsCodeCompletion');
88
89 /**
90 * Render t3editor element
91 *
92 * @return array As defined in initializeResultArray() of AbstractNode
93 */
94 public function render()
95 {
96 $this->getLanguageService()->includeLLFile('EXT:t3editor/Resources/Private/Language/locallang.xlf');
97 $this->relExtPath = ExtensionManagementUtility::extRelPath('t3editor');
98
99 $this->resultArray = $this->initializeResultArray();
100
101 $parameterArray = $this->data['parameterArray'];
102
103 $rows = MathUtility::forceIntegerInRange($parameterArray['fieldConf']['config']['rows'] ?: 10, 1, 40);
104 $this->setMode(isset($parameterArray['fieldConf']['config']['format']) ? $parameterArray['fieldConf']['config']['format'] : T3editor::MODE_MIXED);
105
106 $attributes = array();
107 $attributes['rows'] = $rows;
108 $attributes['wrap'] = 'off';
109 $attributes['style'] = 'width:100%;';
110 $attributes['onchange'] = GeneralUtility::quoteJSvalue($parameterArray['fieldChangeFunc']['TBE_EDITOR_fieldChanged']);
111
112 $attributeString = '';
113 foreach ($attributes as $param => $value) {
114 $attributeString .= $param . '="' . htmlspecialchars($value) . '" ';
115 }
116
117 $this->resultArray['html'] = $this->getHTMLCodeForEditor(
118 $parameterArray['itemFormElName'],
119 'text-monospace enable-tab',
120 $parameterArray['itemFormElValue'],
121 $attributeString,
122 $this->data['tableName'] . ' > ' . $this->data['fieldName'],
123 array('target' => 0)
124 );
125
126 $this->initJavascriptCode();
127 return $this->resultArray;
128 }
129
130 /**
131 * Sets the type of code to edit, use one of the predefined constants
132 *
133 * @param string $mode Expects one of the predefined constants
134 * @throws \InvalidArgumentException
135 */
136 public function setMode($mode)
137 {
138 if (!in_array($mode, $this->allowedModes, true)) {
139 throw new \InvalidArgumentException($mode . 'is not allowed', 1438352574);
140 }
141 $this->mode = $mode;
142 }
143
144 /**
145 * Get mode
146 *
147 * @return string
148 */
149 public function getMode()
150 {
151 return $this->mode;
152 }
153
154 /**
155 * Init the JavaScript code (header part) for editor
156 */
157 protected function initJavascriptCode()
158 {
159 $this->resultArray['stylesheetFiles'][] = $this->relExtPath . 'Resources/Public/Css/t3editor.css';
160 $this->resultArray['requireJsModules'][] = 'TYPO3/CMS/T3editor/T3editor';
161 if ($this->mode === self::MODE_TYPOSCRIPT) {
162 foreach ($this->codeCompletionComponents as $codeCompletionComponent) {
163 $this->resultArray['requireJsModules'][] = 'TYPO3/CMS/T3editor/Plugins/CodeCompletion/' . $codeCompletionComponent;
164 }
165 }
166 }
167
168 /**
169 * Generates HTML with code editor
170 *
171 * @param string $name Name attribute of HTML tag
172 * @param string $class Class attribute of HTML tag
173 * @param string $content Content of the editor
174 * @param string $additionalParams Any additional editor parameters
175 * @param string $alt Alt attribute
176 * @param array $hiddenfields
177 * @return string Generated HTML code for editor
178 */
179 protected function getHTMLCodeForEditor($name, $class = '', $content = '', $additionalParams = '', $alt = '', array $hiddenfields = array())
180 {
181 $code = array();
182 $attributes = array();
183 $attributes['class'] = $class . ' t3editor';
184 $attributes['alt'] = $alt;
185 $attributes['id'] = 't3editor_' . $this->editorCounter;
186 $attributes['name'] = $name;
187 $attributes['data-labels'] = json_encode($this->getLanguageService()->getLabelsWithPrefix('js.', 'label_'));
188 $attributes['data-instance-number'] = $this->editorCounter;
189 $attributes['data-editor-path'] = $this->relExtPath;
190 $attributes['data-codemirror-path'] = $this->codemirrorPath;
191 $attributes['data-ajaxsavetype'] = ''; // no ajax save in FormEngine at the moment
192 $attributes['data-parserfile'] = $this->getParserfileByMode($this->mode);
193 $attributes['data-stylesheet'] = $this->getStylesheetByMode($this->mode);
194
195 $attributesString = '';
196 foreach ($attributes as $attribute => $value) {
197 $attributesString .= $attribute . '="' . htmlspecialchars($value) . '" ';
198 }
199 $attributesString .= $additionalParams;
200
201 $code[] = '<div class="t3editor">';
202 $code[] = ' <div class="t3e_wrap">';
203 $code[] = str_replace(array(CR, LF), '', GeneralUtility::getUrl(GeneralUtility::getFileAbsFileName('EXT:t3editor/Resources/Private/Templates/t3editor.html')));
204 $code[] = ' </div>';
205 $code[] = ' <textarea ' . $attributesString . '>' . htmlspecialchars($content) . '</textarea>';
206 $code[] = '</div>';
207
208 if (!empty($hiddenfields)) {
209 foreach ($hiddenfields as $name => $value) {
210 $code[] = '<input type="hidden" name="' . htmlspecialchars($name) . '" value="' . htmlspecialchars($value) . '" />';
211 }
212 }
213 $this->editorCounter++;
214 return implode(LF, $code);
215 }
216
217 /**
218 * Determine the correct parser js file for given mode
219 *
220 * @param string $mode
221 * @return string Parser file name
222 */
223 protected function getParserfileByMode($mode)
224 {
225 $parserfile = array();
226 switch ($mode) {
227 case self::MODE_TYPOSCRIPT:
228 $relPath = '../../../parse_typoscript/';
229 $parserfile = array($relPath . 'tokenizetyposcript.js', $relPath . 'parsetyposcript.js');
230 break;
231 case self::MODE_JAVASCRIPT:
232 $parserfile = array('tokenizejavascript.js', 'parsejavascript.js');
233 break;
234 case self::MODE_CSS:
235 $parserfile = array('parsecss.js');
236 break;
237 case self::MODE_XML:
238 $parserfile = array('parsexml.js');
239 break;
240 case self::MODE_SPARQL:
241 $parserfile = array('parsesparql.js');
242 break;
243 case self::MODE_HTML:
244 $parserfile = array('tokenizejavascript.js', 'parsejavascript.js', 'parsecss.js', 'parsexml.js', 'parsehtmlmixed.js');
245 break;
246 case self::MODE_PHP:
247 case self::MODE_MIXED:
248 $parserfile = array('tokenizejavascript.js', 'parsejavascript.js', 'parsecss.js', 'parsexml.js', '../contrib/php/js/tokenizephp.js', '../contrib/php/js/parsephp.js', '../contrib/php/js/parsephphtmlmixed.js');
249 break;
250 }
251 return json_encode($parserfile);
252 }
253
254 /**
255 * Determine the correct css file for given mode
256 *
257 * @param string $mode
258 * @return string css file name
259 */
260 protected function getStylesheetByMode($mode)
261 {
262 switch ($mode) {
263 case self::MODE_TYPOSCRIPT:
264 $stylesheet = array($this->relExtPath . 'Resources/Public/Css/typoscriptcolors.css');
265 break;
266 case self::MODE_JAVASCRIPT:
267 $stylesheet = array($this->codemirrorPath . '../css/jscolors.css');
268 break;
269 case self::MODE_CSS:
270 $stylesheet = array($this->codemirrorPath . '../css/csscolors.css');
271 break;
272 case self::MODE_XML:
273 $stylesheet = array($this->codemirrorPath . '../css/xmlcolors.css');
274 break;
275 case self::MODE_HTML:
276 $stylesheet = array($this->codemirrorPath . '../css/xmlcolors.css', $this->codemirrorPath . '../css/jscolors.css', $this->codemirrorPath . '../css/csscolors.css');
277 break;
278 case self::MODE_SPARQL:
279 $stylesheet = array($this->codemirrorPath . '../css/sparqlcolors.css');
280 break;
281 case self::MODE_PHP:
282 $stylesheet = array($this->codemirrorPath . '../contrib/php/css/phpcolors.css');
283 break;
284 case self::MODE_MIXED:
285 $stylesheet = array($this->codemirrorPath . '../css/xmlcolors.css', $this->codemirrorPath . '../css/jscolors.css', $this->codemirrorPath . '../css/csscolors.css', $this->codemirrorPath . '../contrib/php/css/phpcolors.css');
286 break;
287 default:
288 $stylesheet = array();
289 }
290 $stylesheet[] = $this->relExtPath . 'Resources/Public/Css/t3editor_inner.css';
291 return json_encode($stylesheet);
292 }
293
294 /**
295 * @return LanguageService
296 */
297 protected function getLanguageService()
298 {
299 return $GLOBALS['LANG'];
300 }
301
302 /**
303 * @return BackendUserAuthentication
304 */
305 protected function getBackendUserAuthentication()
306 {
307 return $GLOBALS['BE_USER'];
308 }
309 }