1ab1364d85246c660641010e3d7b17d7c5177f83
[Packages/TYPO3.CMS.git] / typo3 / sysext / t3editor / Classes / T3Editor.php
1 <?php
2 namespace TYPO3\CMS\T3Editor;
3
4 /**
5 * Provides a javascript-driven code editor with syntax highlighting for TS, HTML, CSS and more
6 *
7 * @author Tobias Liebig <mail_typo3@etobi.de>
8 */
9 class T3Editor implements \TYPO3\CMS\Core\SingletonInterface {
10
11 const MODE_TYPOSCRIPT = 'typoscript';
12 const MODE_JAVASCRIPT = 'javascript';
13 const MODE_CSS = 'css';
14 const MODE_XML = 'xml';
15 const MODE_HTML = 'html';
16 const MODE_PHP = 'php';
17 const MODE_SPARQL = 'sparql';
18 const MODE_MIXED = 'mixed';
19 /**
20 * @var string
21 */
22 protected $mode = '';
23
24 /**
25 * @var string
26 */
27 protected $ajaxSaveType = '';
28
29 /**
30 * Counts the editors on the current page
31 *
32 * @var integer
33 */
34 protected $editorCounter = 0;
35
36 /**
37 * Flag to enable the t3editor
38 *
39 * @var boolean
40 */
41 protected $_isEnabled = TRUE;
42
43 /**
44 * sets the type of code to edit (::MODE_TYPOSCRIPT, ::MODE_JAVASCRIPT)
45 *
46 * @param $mode string Expects one of the predefined constants
47 * @return \TYPO3\CMS\T3Editor\T3Editor
48 */
49 public function setMode($mode) {
50 $this->mode = $mode;
51 return $this;
52 }
53
54 /**
55 * Set the AJAX save type
56 *
57 * @param string $ajaxSaveType
58 * @return \TYPO3\CMS\T3Editor\T3Editor
59 */
60 public function setAjaxSaveType($ajaxSaveType) {
61 $this->ajaxSaveType = $ajaxSaveType;
62 return $this;
63 }
64
65 /**
66 * Set mode by file
67 *
68 * @param string $file
69 * @return string
70 */
71 public function setModeByFile($file) {
72 $fileInfo = \TYPO3\CMS\Core\Utility\GeneralUtility::split_fileref($file);
73 return $this->setModeByType($fileInfo['fileext']);
74 }
75
76 /**
77 * Set mode by type
78 *
79 * @param string $type
80 * @return void
81 */
82 public function setModeByType($type) {
83 switch ($type) {
84 case 'html':
85
86 case 'htm':
87
88 case 'tmpl':
89 $mode = self::MODE_HTML;
90 break;
91 case 'js':
92 $mode = self::MODE_JAVASCRIPT;
93 break;
94 case 'xml':
95
96 case 'svg':
97 $mode = self::MODE_XML;
98 break;
99 case 'css':
100 $mode = self::MODE_CSS;
101 break;
102 case 'ts':
103 $mode = self::MODE_TYPOSCRIPT;
104 break;
105 case 'sparql':
106 $mode = self::MODE_SPARQL;
107 break;
108 case 'php':
109
110 case 'phpsh':
111
112 case 'inc':
113 $mode = self::MODE_PHP;
114 break;
115 default:
116 $mode = self::MODE_MIXED;
117 }
118 $this->setMode($mode);
119 }
120
121 /**
122 * Get mode
123 *
124 * @return string
125 */
126 public function getMode() {
127 return $this->mode;
128 }
129
130 /**
131 * @return boolean TRUE if the t3editor is enabled
132 */
133 public function isEnabled() {
134 return $this->_isEnabled;
135 }
136
137 /**
138 * Creates a new instance of the class
139 */
140 public function __construct() {
141 // Disable pmktextarea to avoid conflicts (thanks Peter Klein for this suggestion)
142 $GLOBALS['BE_USER']->uc['disablePMKTextarea'] = 1;
143 }
144
145 /**
146 * Retrieves JavaScript code (header part) for editor
147 *
148 * @param \TYPO3\CMS\Backend\Template\DocumentTemplate $doc
149 * @return string JavaScript code
150 */
151 public function getJavascriptCode($doc) {
152 $content = '';
153 if ($this->isEnabled()) {
154 $path_t3e = \t3lib_extmgm::extRelPath('t3editor');
155 $path_codemirror = 'contrib/codemirror/js/';
156 // Include needed javascript-frameworks
157 $pageRenderer = $doc->getPageRenderer();
158 /** @var $pageRenderer \TYPO3\CMS\Core\Page\PageRenderer */
159 $pageRenderer->loadPrototype();
160 $pageRenderer->loadScriptaculous();
161 // Include editor-css
162 $content .= '<link href="' . \TYPO3\CMS\Core\Utility\GeneralUtility::createVersionNumberedFilename(($GLOBALS['BACK_PATH'] . \t3lib_extmgm::extRelPath('t3editor') . 'res/css/t3editor.css')) . '" type="text/css" rel="stylesheet" />';
163 // Include editor-js-lib
164 $doc->loadJavascriptLib($path_codemirror . 'codemirror.js');
165 $doc->loadJavascriptLib($path_t3e . 'res/jslib/t3editor.js');
166
167 $content .= \TYPO3\CMS\Core\Utility\GeneralUtility::wrapJS(
168 'T3editor = T3editor || {};' .
169 'T3editor.lang = ' . json_encode($this->getJavaScriptLabels()) . ';' . LF .
170 'T3editor.PATH_t3e = "' . $GLOBALS['BACK_PATH'] . $path_t3e . '"; ' . LF .
171 'T3editor.PATH_codemirror = "' . $GLOBALS['BACK_PATH'] . $path_codemirror . '"; ' . LF .
172 'T3editor.URL_typo3 = "' . htmlspecialchars(\TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('TYPO3_SITE_URL') . TYPO3_mainDir) . '"; ' . LF .
173 'T3editor.template = ' . $this->getPreparedTemplate() . ';' . LF .
174 'T3editor.ajaxSavetype = "' . $this->ajaxSaveType . '";' . LF
175 );
176 $content .= $this->getModeSpecificJavascriptCode();
177 }
178 return $content;
179 }
180
181 /**
182 * Get mode specific JavaScript code
183 *
184 * @return string
185 */
186 public function getModeSpecificJavascriptCode() {
187 if (empty($this->mode)) {
188 return '';
189 }
190 $path_t3e = $GLOBALS['BACK_PATH'] . \t3lib_extmgm::extRelPath('t3editor');
191 $content = '';
192 if ($this->mode === self::MODE_TYPOSCRIPT) {
193 $content .= '<script type="text/javascript" src="' . $path_t3e . 'res/jslib/ts_codecompletion/tsref.js' . '"></script>';
194 $content .= '<script type="text/javascript" src="' . $path_t3e . 'res/jslib/ts_codecompletion/completionresult.js' . '"></script>';
195 $content .= '<script type="text/javascript" src="' . $path_t3e . 'res/jslib/ts_codecompletion/tsparser.js' . '"></script>';
196 $content .= '<script type="text/javascript" src="' . $path_t3e . 'res/jslib/ts_codecompletion/tscodecompletion.js' . '"></script>';
197 }
198 $content .= \TYPO3\CMS\Core\Utility\GeneralUtility::wrapJS('T3editor.parserfile = ' . $this->getParserfileByMode($this->mode) . ';' . LF . 'T3editor.stylesheet = ' . $this->getStylesheetByMode($this->mode) . ';');
199 return $content;
200 }
201
202 /**
203 * Get the template code, prepared for javascript (no line breaks, quoted in single quotes)
204 *
205 * @return string The template code, prepared to use in javascript
206 */
207 protected function getPreparedTemplate() {
208 $T3Editor_template = \TYPO3\CMS\Core\Utility\GeneralUtility::getUrl(\TYPO3\CMS\Core\Utility\GeneralUtility::getFileAbsFileName('EXT:t3editor/res/templates/t3editor.html'));
209 $T3Editor_template = addslashes($T3Editor_template);
210 $T3Editor_template = str_replace(array(CR, LF), array('', '\' + \''), $T3Editor_template);
211 return '\'' . $T3Editor_template . '\'';
212 }
213
214 /**
215 * Determine the correct parser js file for given mode
216 *
217 * @param string $mode
218 * @return string Parser file name
219 */
220 protected function getParserfileByMode($mode) {
221 switch ($mode) {
222 case self::MODE_TYPOSCRIPT:
223 $relPath = ($GLOBALS['BACK_PATH'] ? $GLOBALS['BACK_PATH'] : '../../../') . \t3lib_extmgm::extRelPath('t3editor') . 'res/jslib/parse_typoscript/';
224 $parserfile = '["' . $relPath . 'tokenizetyposcript.js", "' . $relPath . 'parsetyposcript.js"]';
225 break;
226 case self::MODE_JAVASCRIPT:
227 $parserfile = '["tokenizejavascript.js", "parsejavascript.js"]';
228 break;
229 case self::MODE_CSS:
230 $parserfile = '"parsecss.js"';
231 break;
232 case self::MODE_XML:
233 $parserfile = '"parsexml.js"';
234 break;
235 case self::MODE_SPARQL:
236 $parserfile = '"parsesparql.js"';
237 break;
238 case self::MODE_HTML:
239 $parserfile = '["tokenizejavascript.js", "parsejavascript.js", "parsecss.js", "parsexml.js", "parsehtmlmixed.js"]';
240 break;
241 case self::MODE_PHP:
242
243 case self::MODE_MIXED:
244 $parserfile = '[' . '"tokenizejavascript.js", ' . '"parsejavascript.js", ' . '"parsecss.js", ' . '"parsexml.js", ' . '"../contrib/php/js/tokenizephp.js", ' . '"../contrib/php/js/parsephp.js", ' . '"../contrib/php/js/parsephphtmlmixed.js"' . ']';
245 break;
246 }
247 return $parserfile;
248 }
249
250 /**
251 * Determine the correct css file for given mode
252 *
253 * @param string $mode
254 * @return string css file name
255 */
256 protected function getStylesheetByMode($mode) {
257 switch ($mode) {
258 case self::MODE_TYPOSCRIPT:
259 $stylesheet = 'T3editor.PATH_t3e + "res/css/typoscriptcolors.css"';
260 break;
261 case self::MODE_JAVASCRIPT:
262 $stylesheet = 'T3editor.PATH_codemirror + "../css/jscolors.css"';
263 break;
264 case self::MODE_CSS:
265 $stylesheet = 'T3editor.PATH_codemirror + "../css/csscolors.css"';
266 break;
267 case self::MODE_XML:
268 $stylesheet = 'T3editor.PATH_codemirror + "../css/xmlcolors.css"';
269 break;
270 case self::MODE_HTML:
271 $stylesheet = 'T3editor.PATH_codemirror + "../css/xmlcolors.css", ' . 'T3editor.PATH_codemirror + "../css/jscolors.css", ' . 'T3editor.PATH_codemirror + "../css/csscolors.css"';
272 break;
273 case self::MODE_SPARQL:
274 $stylesheet = 'T3editor.PATH_codemirror + "../css/sparqlcolors.css"';
275 break;
276 case self::MODE_PHP:
277 $stylesheet = 'T3editor.PATH_codemirror + "../contrib/php/css/phpcolors.css"';
278 break;
279 case self::MODE_MIXED:
280 $stylesheet = 'T3editor.PATH_codemirror + "../css/xmlcolors.css", ' . 'T3editor.PATH_codemirror + "../css/jscolors.css", ' . 'T3editor.PATH_codemirror + "../css/csscolors.css", ' . 'T3editor.PATH_codemirror + "../contrib/php/css/phpcolors.css"';
281 break;
282 }
283 if ($stylesheet != '') {
284 $stylesheet = '' . $stylesheet . ', ';
285 }
286 return '[' . $stylesheet . 'T3editor.PATH_t3e + "res/css/t3editor_inner.css"]';
287 }
288
289 /**
290 * Gets the labels to be used in JavaScript in the Ext JS interface.
291 * TODO this method is copied from EXT:Recycler, maybe this should be refactored into a helper class
292 *
293 * @return array The labels to be used in JavaScript
294 */
295 protected function getJavaScriptLabels() {
296 $coreLabels = array();
297 $extensionLabels = $this->getJavaScriptLabelsFromLocallang('js.', 'label_');
298 return array_merge($coreLabels, $extensionLabels);
299 }
300
301 /**
302 * Gets labels to be used in JavaScript fetched from the current locallang file.
303 * TODO this method is copied from EXT:Recycler, maybe this should be refactored into a helper class
304 *
305 * @param string $selectionPrefix Prefix to select the correct labels (default: 'js.')
306 * @param string $stripFromSelectionName Sub-prefix to be removed from label names in the result (default: '')
307 * @return array Lables to be used in JavaScript of the current locallang file
308 * @todo Check, whether this method can be moved in a generic way to $GLOBALS['LANG']
309 */
310 protected function getJavaScriptLabelsFromLocallang($selectionPrefix = 'js.', $stripFromSelectionName = '') {
311 $extraction = array();
312 $labels = array_merge((array) $GLOBALS['LOCAL_LANG']['default'], (array) $GLOBALS['LOCAL_LANG'][$GLOBALS['LANG']->lang]);
313 // Regular expression to strip the selection prefix and possibly something from the label name:
314 $labelPattern = '#^' . preg_quote($selectionPrefix, '#') . '(' . preg_quote($stripFromSelectionName, '#') . ')?#';
315 // Iterate throuh all locallang lables:
316 foreach ($labels as $label => $value) {
317 if (strpos($label, $selectionPrefix) === 0) {
318 $key = preg_replace($labelPattern, '', $label);
319 $extraction[$key] = $value;
320 }
321 }
322 return $extraction;
323 }
324
325 /**
326 * Generates HTML with code editor
327 *
328 * @param string $name Name attribute of HTML tag
329 * @param string $class Class attribute of HTML tag
330 * @param string $content Content of the editor
331 * @param string $additionalParams Any additional editor parameters
332 * @param string $alt Alt attribute
333 * @param array $hiddenfields
334 * @return string Generated HTML code for editor
335 */
336 public function getCodeEditor($name, $class = '', $content = '', $additionalParams = '', $alt = '', array $hiddenfields = array()) {
337 $code = '';
338 if ($this->isEnabled()) {
339 $this->editorCounter++;
340 $class .= ' t3editor';
341 $alt = htmlspecialchars($alt);
342 if (!empty($alt)) {
343 $alt = ' alt="' . $alt . '"';
344 }
345 $code .= '<div>' . '<textarea id="t3editor_' . $this->editorCounter . '" ' . 'name="' . $name . '" ' . 'class="' . $class . '" ' . $additionalParams . ' ' . $alt . '>' . $content . '</textarea></div>';
346 $checked = $GLOBALS['BE_USER']->uc['disableT3Editor'] ? 'checked="checked"' : '';
347 $code .= '<br /><br />' . '<input type="checkbox" ' . 'class="checkbox t3editor_disableEditor" ' . 'onclick="T3editor.toggleEditor(this);" ' . 'name="t3editor_disableEditor" ' . 'value="true" ' . 'id="t3editor_disableEditor_' . $this->editorCounter . '_checkbox" ' . $checked . ' />&nbsp;' . '<label for="t3editor_disableEditor_' . $this->editorCounter . '_checkbox">' . $GLOBALS['LANG']->getLL('deactivate') . '</label>' . '<br /><br />';
348 if (count($hiddenfields)) {
349 foreach ($hiddenfields as $name => $value) {
350 $code .= '<input type="hidden" ' . 'name="' . $name . '" ' . 'value="' . $value . '" />';
351 }
352 }
353 } else {
354 // Fallback
355 if (!empty($class)) {
356 $class = 'class="' . $class . '" ';
357 }
358 $code .= '<textarea name="' . $name . '" ' . $class . $additionalParams . '>' . $content . '</textarea>';
359 }
360 return $code;
361 }
362
363 /**
364 * Save the content from t3editor retrieved via Ajax
365 *
366 * new Ajax.Request('/dev/t3e/dummy/typo3/ajax.php', {
367 * parameters: {
368 * ajaxID: 'T3Editor::saveCode::saveCode',
369 * t3editor_savetype: 'tx_tstemplateinfo'
370 * }
371 * });
372 *
373 * @param array params Parameters (not used yet)
374 * @param \TYPO3\CMS\Core\Http\AjaxRequestHandler ajaxObj AjaxObject to handle response
375 */
376 public function ajaxSaveCode($params, $ajaxObj) {
377 // cancel if its not an Ajax request
378 if (TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_AJAX) {
379 $ajaxObj->setContentFormat('json');
380 $codeType = \TYPO3\CMS\Core\Utility\GeneralUtility::_GP('t3editor_savetype');
381 $savingsuccess = FALSE;
382 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/t3editor/classes/class.tx_t3editor.php']['ajaxSaveCode'])) {
383 $_params = array(
384 'pObj' => &$this,
385 'type' => $codeType,
386 'ajaxObj' => &$ajaxObj
387 );
388 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/t3editor/classes/class.tx_t3editor.php']['ajaxSaveCode'] as $key => $_funcRef) {
389 $savingsuccess = \TYPO3\CMS\Core\Utility\GeneralUtility::callUserFunction($_funcRef, $_params, $this) || $savingsuccess;
390 }
391 }
392 $ajaxObj->setContent(array('result' => $savingsuccess));
393 }
394 }
395
396 /**
397 * Gets plugins that are defined at $TYPO3_CONF_VARS['EXTCONF']['t3editor']['plugins']
398 * (called by typo3/ajax.php)
399 *
400 * @param array $params additional parameters (not used here)
401 * @param TYPO3AJAX &$ajaxObj: the TYPO3AJAX object of this request
402 * @return void
403 * @author Oliver Hader <oliver@typo3.org>
404 */
405 public function getPlugins($params, \TYPO3\CMS\Core\Http\AjaxRequestHandler &$ajaxObj) {
406 $result = array();
407 $plugins =& $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['t3editor']['plugins'];
408 if (is_array($plugins)) {
409 $result = array_values($plugins);
410 }
411 $ajaxObj->setContent($result);
412 $ajaxObj->setContentFormat('jsonbody');
413 }
414
415 }
416
417
418 ?>