[TASK] Have eIDs with PSR-7 without ControllerInterface
[Packages/TYPO3.CMS.git] / typo3 / sysext / rtehtmlarea / Classes / Controller / SpellCheckingController.php
1 <?php
2 namespace TYPO3\CMS\Rtehtmlarea\Controller;
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 Psr\Http\Message\ResponseInterface;
18 use Psr\Http\Message\ServerRequestInterface;
19 use TYPO3\CMS\Core\Http\Response;
20 use TYPO3\CMS\Core\Utility\GeneralUtility;
21
22 /**
23 * Spell checking plugin 'tx_rtehtmlarea_pi1' for the htmlArea RTE extension.
24 */
25 class SpellCheckingController {
26
27 /**
28 * @var \TYPO3\CMS\Core\Charset\CharsetConverter
29 */
30 protected $csConvObj;
31
32 // The extension key
33 /**
34 * @var string
35 */
36 public $extKey = 'rtehtmlarea';
37
38 /**
39 * @var string
40 */
41 public $siteUrl;
42
43 /**
44 * @var string
45 */
46 public $charset = 'utf-8';
47
48 /**
49 * @var string
50 */
51 public $parserCharset = 'utf-8';
52
53 /**
54 * @var string
55 */
56 public $defaultAspellEncoding = 'utf-8';
57
58 /**
59 * @var string
60 */
61 public $aspellEncoding;
62
63 /**
64 * @var string
65 */
66 public $result;
67
68 /**
69 * @var string
70 */
71 public $text;
72
73 /**
74 * @var array
75 */
76 public $misspelled = array();
77
78 /**
79 * @var array
80 */
81 public $suggestedWords;
82
83 /**
84 * @var int
85 */
86 public $wordCount = 0;
87
88 /**
89 * @var int
90 */
91 public $suggestionCount = 0;
92
93 /**
94 * @var int
95 */
96 public $suggestedWordCount = 0;
97
98 /**
99 * @var int
100 */
101 public $pspell_link;
102
103 /**
104 * @var string
105 */
106 public $pspellMode = 'normal';
107
108 /**
109 * @var string
110 */
111 public $dictionary;
112
113 /**
114 * @var string
115 */
116 public $AspellDirectory;
117
118 /**
119 * @var bool
120 */
121 public $pspell_is_available;
122
123 /**
124 * @var bool
125 */
126 public $forceCommandMode = 0;
127
128 /**
129 * @var string
130 */
131 public $filePrefix = 'rtehtmlarea_';
132
133 // Pre-FAL backward compatibility
134 protected $uploadFolder = 'uploads/tx_rtehtmlarea/';
135
136 // Path to main dictionary
137 protected $mainDictionaryPath;
138
139 // Path to personal dictionary
140 protected $personalDictionaryPath;
141
142 /**
143 * @var string
144 */
145 public $xmlCharacterData = '';
146
147
148 /**
149 * AJAX entry point
150 *
151 * @param array $ajaxParams
152 * @return void
153 * @throws \UnexpectedValueException
154 */
155 public function main(array $ajaxParams) {
156 /** @var Response $response */
157 $response = GeneralUtility::makeInstance(Response::class);
158 $this->processRequest($ajaxParams['request'], $response);
159 header('Content-Type: text/html; charset=' . strtoupper($this->parserCharset));
160 echo $this->result;
161 }
162
163 /**
164 * Main class of Spell Checker plugin
165 *
166 * @param ServerRequestInterface $request
167 * @param ResponseInterface $response
168 * @return ResponseInterface
169 * @throws \InvalidArgumentException
170 * @throws \UnexpectedValueException
171 */
172 public function processRequest(ServerRequestInterface $request, ResponseInterface $response) {
173 $this->csConvObj = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Charset\CharsetConverter::class);
174 // Setting start time
175 $time_start = microtime(TRUE);
176 $this->pspell_is_available = in_array('pspell', get_loaded_extensions());
177 $this->AspellDirectory = trim($GLOBALS['TYPO3_CONF_VARS']['EXTCONF'][$this->extKey]['plugins']['SpellChecker']['AspellDirectory']) ?: '/usr/bin/aspell';
178 // Setting command mode if requested and available
179 $this->forceCommandMode = trim($GLOBALS['TYPO3_CONF_VARS']['EXTCONF'][$this->extKey]['plugins']['SpellChecker']['forceCommandMode']) ?: 0;
180 if (!$this->pspell_is_available || $this->forceCommandMode) {
181 $AspellVersionString = explode('Aspell', shell_exec($this->AspellDirectory . ' -v'));
182 $AspellVersion = substr($AspellVersionString[1], 0, 4);
183 if (doubleval($AspellVersion) < doubleval('0.5') && (!$this->pspell_is_available || $this->forceCommandMode)) {
184 echo 'Configuration problem: Aspell version ' . $AspellVersion . ' too old. Spell checking cannot be performed in command mode.';
185 }
186 $this->defaultAspellEncoding = trim(shell_exec($this->AspellDirectory . ' config encoding'));
187 }
188 // Setting the list of dictionaries
189 $dictionaryList = shell_exec($this->AspellDirectory . ' dump dicts');
190 $dictionaryList = implode(',', GeneralUtility::trimExplode(LF, $dictionaryList, TRUE));
191 $dictionaryArray = GeneralUtility::trimExplode(',', $dictionaryList, TRUE);
192 $restrictToDictionaries = GeneralUtility::_POST('restrictToDictionaries');
193 if ($restrictToDictionaries) {
194 $dictionaryArray = array_intersect($dictionaryArray, GeneralUtility::trimExplode(',', $restrictToDictionaries, 1));
195 }
196 if (empty($dictionaryArray)) {
197 $dictionaryArray[] = 'en';
198 }
199 $this->dictionary = GeneralUtility::_POST('dictionary');
200 $defaultDictionary = $this->dictionary;
201 if (!$defaultDictionary || !in_array($defaultDictionary, $dictionaryArray)) {
202 $defaultDictionary = 'en';
203 }
204 uasort($dictionaryArray, 'strcoll');
205 $dictionaryList = implode(',', $dictionaryArray);
206 // Setting the dictionary
207 if (empty($this->dictionary) || !in_array($this->dictionary, $dictionaryArray)) {
208 $this->dictionary = 'en';
209 }
210 // Setting the pspell suggestion mode
211 $this->pspellMode = GeneralUtility::_POST('pspell_mode') ? GeneralUtility::_POST('pspell_mode') : $this->pspellMode;
212 // Now sanitize $this->pspellMode
213 $this->pspellMode = GeneralUtility::inList('ultra,fast,normal,bad-spellers', $this->pspellMode) ? $this->pspellMode : 'normal';
214 switch ($this->pspellMode) {
215 case 'ultra':
216
217 case 'fast':
218 $pspellModeFlag = PSPELL_FAST;
219 break;
220 case 'bad-spellers':
221 $pspellModeFlag = PSPELL_BAD_SPELLERS;
222 break;
223 case 'normal':
224
225 default:
226 $pspellModeFlag = PSPELL_NORMAL;
227 }
228 // Setting the charset
229 if (GeneralUtility::_POST('pspell_charset')) {
230 $this->charset = trim(GeneralUtility::_POST('pspell_charset'));
231 }
232 if (strtolower($this->charset) == 'iso-8859-1') {
233 $this->parserCharset = strtolower($this->charset);
234 }
235 // In some configurations, Aspell uses 'iso8859-1' instead of 'iso-8859-1'
236 $this->aspellEncoding = $this->parserCharset;
237 if ($this->parserCharset == 'iso-8859-1' && strstr($this->defaultAspellEncoding, '8859-1')) {
238 $this->aspellEncoding = $this->defaultAspellEncoding;
239 }
240 // However, we are going to work only in the parser charset
241 if ($this->pspell_is_available && !$this->forceCommandMode) {
242 $this->pspell_link = pspell_new($this->dictionary, '', '', $this->parserCharset, $pspellModeFlag);
243 }
244 // Setting the path to main dictionary
245 $this->setMainDictionaryPath();
246 // Setting the path to user personal dictionary, if any
247 $this->setPersonalDictionaryPath();
248 $this->fixPersonalDictionaryCharacterSet();
249 $cmd = GeneralUtility::_POST('cmd');
250 if ($cmd == 'learn') {
251 // Only availble for BE_USERS, die silently if someone has gotten here by accident
252 if (TYPO3_MODE !== 'BE' || !is_object($GLOBALS['BE_USER'])) {
253 die('');
254 }
255 // Updating the personal word list
256 $to_p_dict = GeneralUtility::_POST('to_p_dict');
257 $to_p_dict = $to_p_dict ? $to_p_dict : array();
258 $to_r_list = GeneralUtility::_POST('to_r_list');
259 $to_r_list = $to_r_list ? $to_r_list : array();
260 header('Content-Type: text/plain; charset=' . strtoupper($this->parserCharset));
261 header('Pragma: no-cache');
262 if ($to_p_dict || $to_r_list) {
263 $tmpFileName = GeneralUtility::tempnam($this->filePrefix);
264 $filehandle = fopen($tmpFileName, 'wb');
265 if ($filehandle) {
266 // Get the character set of the main dictionary
267 // We need to convert the input into the character set of the main dictionary
268 $mainDictionaryCharacterSet = $this->getMainDictionaryCharacterSet();
269 // Write the personal words addition commands to the temporary file
270 foreach ($to_p_dict as $personal_word) {
271 $cmd = '&' . $this->csConvObj->conv($personal_word, $this->parserCharset, $mainDictionaryCharacterSet) . LF;
272 fwrite($filehandle, $cmd, strlen($cmd));
273 }
274 // Write the replacent pairs addition commands to the temporary file
275 foreach ($to_r_list as $replace_pair) {
276 $cmd = '$$ra ' . $this->csConvObj->conv($replace_pair[0], $this->parserCharset, $mainDictionaryCharacterSet) . ' , ' . $this->csConvObj->conv($replace_pair[1], $this->parserCharset, $mainDictionaryCharacterSet) . LF;
277 fwrite($filehandle, $cmd, strlen($cmd));
278 }
279 $cmd = '#' . LF;
280 $result = fwrite($filehandle, $cmd, strlen($cmd));
281 if ($result === FALSE) {
282 GeneralUtility::sysLog('SpellChecker tempfile write error: ' . $tmpFileName, $this->extKey, GeneralUtility::SYSLOG_SEVERITY_ERROR);
283 } else {
284 // Assemble the Aspell command
285 $aspellCommand = ((TYPO3_OS === 'WIN') ? 'type ' : 'cat ') . escapeshellarg($tmpFileName) . ' | '
286 . $this->AspellDirectory
287 . ' -a --mode=none'
288 . ($this->personalDictionaryPath ? ' --home-dir=' . escapeshellarg($this->personalDictionaryPath) : '')
289 . ' --lang=' . escapeshellarg($this->dictionary)
290 . ' --encoding=' . escapeshellarg($mainDictionaryCharacterSet)
291 . ' 2>&1';
292 $aspellResult = shell_exec($aspellCommand);
293 // Close and delete the temporary file
294 fclose($filehandle);
295 GeneralUtility::unlink_tempfile($tmpFileName);
296 }
297 } else {
298 GeneralUtility::sysLog('SpellChecker tempfile open error: ' . $tmpFileName, $this->extKey, GeneralUtility::SYSLOG_SEVERITY_ERROR);
299 }
300 }
301 flush();
302 die;
303 } else {
304 // Check spelling content
305 // Initialize output
306 $this->result = '<?xml version="1.0" encoding="' . $this->parserCharset . '"?>
307 <!DOCTYPE html
308 PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
309 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
310 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="' . substr($this->dictionary, 0, 2) . '" lang="' . substr($this->dictionary, 0, 2) . '">
311 <head>
312 <meta http-equiv="Content-Type" content="text/html; charset=' . $this->parserCharset . '" />
313 <link rel="stylesheet" type="text/css" media="all" href="' . (TYPO3_MODE == 'BE' ? '../' : '') . \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::siteRelPath($this->extKey) . '/Resources/Public/Css/Skin/Plugins/spell-checker-iframe.css" />
314 <script type="text/javascript">
315 /*<![CDATA[*/
316 <!--
317 ';
318 // Getting the input content
319 $content = GeneralUtility::_POST('content');
320 // Parsing the input HTML
321 $parser = xml_parser_create(strtoupper($this->parserCharset));
322 xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
323 xml_set_object($parser, $this);
324 if (!xml_set_element_handler($parser, 'startHandler', 'endHandler')) {
325 echo 'Bad xml handler setting';
326 }
327 if (!xml_set_character_data_handler($parser, 'collectDataHandler')) {
328 echo 'Bad xml handler setting';
329 }
330 if (!xml_set_default_handler($parser, 'defaultHandler')) {
331 echo 'Bad xml handler setting';
332 }
333 if (!xml_parse($parser, ('<?xml version="1.0" encoding="' . $this->parserCharset . '"?><spellchecker> ' . preg_replace(('/&nbsp;/' . ($this->parserCharset == 'utf-8' ? 'u' : '')), ' ', $content) . ' </spellchecker>'))) {
334 echo 'Bad parsing';
335 }
336 if (xml_get_error_code($parser)) {
337 throw new \UnexpectedValueException('Line ' . xml_get_current_line_number($parser) . ': ' . xml_error_string(xml_get_error_code($parser)), 1294585788);
338 }
339 xml_parser_free($parser);
340 if ($this->pspell_is_available && !$this->forceCommandMode) {
341 pspell_clear_session($this->pspell_link);
342 }
343 $this->result .= 'var suggestedWords = {' . $this->suggestedWords . '};
344 var dictionaries = "' . $dictionaryList . '";
345 var selectedDictionary = "' . $this->dictionary . '";
346 ';
347 // Calculating parsing and spell checkting time
348 $time = number_format(microtime(TRUE) - $time_start, 2, ',', ' ');
349 // Insert spellcheck info
350 $this->result .= 'var spellcheckInfo = { "Total words":"' . $this->wordCount . '","Misspelled words":"' . sizeof($this->misspelled) . '","Total suggestions":"' . $this->suggestionCount . '","Total words suggested":"' . $this->suggestedWordCount . '","Spelling checked in":"' . $time . '" };
351 // -->
352 /*]]>*/
353 </script>
354 </head>
355 ';
356 $this->result .= '<body onload="window.parent.RTEarea[\'' . GeneralUtility::_POST('editorId') . '\'].editor.getPlugin(\'SpellChecker\').spellCheckComplete();">';
357 $this->result .= preg_replace('/' . preg_quote('<?xml') . '.*' . preg_quote('?>') . '[' . preg_quote((LF . CR . chr(32))) . ']*/' . ($this->parserCharset == 'utf-8' ? 'u' : ''), '', $this->text);
358 $this->result .= '<div style="display: none;">' . $dictionaries . '</div>';
359 // Closing
360 $this->result .= '
361 </body></html>';
362 // Outputting
363 $response = $response->withHeader('Content-Type', 'text/html; charset=' . strtoupper($this->parserCharset));
364 $response->getBody()->write($this->result);
365 return $response;
366 }
367 }
368
369 /**
370 * Sets the path to the main dictionary
371 *
372 * @return string path to the main dictionary
373 */
374 protected function setMainDictionaryPath() {
375 $this->mainDictionaryPath = '';
376 $aspellCommand = $this->AspellDirectory . ' config dict-dir';
377 $aspellResult = shell_exec($aspellCommand);
378 if ($aspellResult) {
379 $this->mainDictionaryPath = trim($aspellResult);
380 }
381 if (!$aspellResult || !$this->mainDictionaryPath) {
382 GeneralUtility::sysLog('SpellChecker main dictionary path retrieval error: ' . $aspellCommand, $this->extKey, GeneralUtility::SYSLOG_SEVERITY_ERROR);
383 }
384 return $this->mainDictionaryPath;
385 }
386
387 /**
388 * Gets the character set the main dictionary
389 *
390 * @return string character set the main dictionary
391 */
392 protected function getMainDictionaryCharacterSet() {
393 $characterSet = '';
394 if ($this->mainDictionaryPath) {
395 // Keep only the first part of the dictionary name
396 $mainDictionary = preg_split('/[-_]/', $this->dictionary, 2);
397 // Read the options of the dictionary
398 $dictionaryFileName = $this->mainDictionaryPath . '/' . $mainDictionary[0] . '.dat';
399 $dictionaryHandle = fopen($dictionaryFileName, 'rb');
400 if (!$dictionaryHandle) {
401 GeneralUtility::sysLog('SpellChecker main dictionary open error: ' . $dictionaryFileName, $this->extKey, GeneralUtility::SYSLOG_SEVERITY_ERROR);
402 } else {
403 $dictionaryContent = fread($dictionaryHandle, 500);
404 if ($dictionaryContent === FALSE) {
405 GeneralUtility::sysLog('SpellChecker main dictionary read error: ' . $dictionaryFileName, $this->extKey, GeneralUtility::SYSLOG_SEVERITY_ERROR);
406 } else {
407 fclose($dictionaryHandle);
408 // Get the line that contains the character set option
409 $dictionaryContent = preg_split('/charset\s*/', $dictionaryContent, 2);
410 if ($dictionaryContent[1]) {
411 // Isolate the character set
412 $dictionaryContent = GeneralUtility::trimExplode(LF, $dictionaryContent[1]);
413 // Fix Aspell character set oddity (i.e. iso8859-1)
414 $characterSet = str_replace(
415 array('iso', '--'),
416 array('iso-', '-'),
417 $dictionaryContent[0]
418 );
419 }
420 if (!$characterSet) {
421 GeneralUtility::sysLog('SpellChecker main dictionary character set retrieval error: ' . $dictionaryContent[1], $this->extKey, GeneralUtility::SYSLOG_SEVERITY_ERROR);
422 }
423 }
424 }
425 }
426 return $characterSet;
427 }
428
429 /**
430 * Sets the path to the personal dictionary
431 *
432 * @return string path to the personal dictionary
433 */
434 protected function setPersonalDictionaryPath() {
435 $this->personalDictionaryPath = '';
436 if (GeneralUtility::_POST('enablePersonalDicts') == 'true' && TYPO3_MODE == 'BE' && is_object($GLOBALS['BE_USER'])) {
437 if ($GLOBALS['BE_USER']->user['uid']) {
438 $personalDictionaryFolderName = 'BE_' . $GLOBALS['BE_USER']->user['uid'];
439 // Check for pre-FAL personal dictionary folder
440 try {
441 $personalDictionaryFolder = \TYPO3\CMS\Core\Resource\ResourceFactory::getInstance()->getFolderObjectFromCombinedIdentifier(PATH_site . $this->uploadFolder . $personalDictionaryFolderName);
442 } catch (\Exception $e) {
443 $personalDictionaryFolder = FALSE;
444 }
445 // The personal dictionary folder is created in the user's default upload folder and named BE_(uid)_personaldictionary
446 if (!$personalDictionaryFolder) {
447 $personalDictionaryFolderName .= '_personaldictionary';
448 $backendUserDefaultFolder = $GLOBALS['BE_USER']->getDefaultUploadFolder();
449 if ($backendUserDefaultFolder->hasFolder($personalDictionaryFolderName)) {
450 $personalDictionaryFolder = $backendUserDefaultFolder->getSubfolder($personalDictionaryFolderName);
451 } else {
452 $personalDictionaryFolder = $backendUserDefaultFolder->createFolder($personalDictionaryFolderName);
453 }
454 }
455 $this->personalDictionaryPath = PATH_site . rtrim($personalDictionaryFolder->getPublicUrl(), '/');
456 }
457 }
458 return $this->personalDictionaryPath;
459 }
460
461 /**
462 * Ensures that the personal dictionary is utf-8 encoded
463 *
464 * @return void
465 */
466 protected function fixPersonalDictionaryCharacterSet() {
467 if ($this->personalDictionaryPath) {
468 // Fix the options of the personl word list and of the replacement pairs files
469 // Aspell creates such files only for the main dictionary
470 $fileNames = array();
471 $mainDictionary = preg_split('/[-_]/', $this->dictionary, 2);
472 $fileNames[0] = $this->personalDictionaryPath . '/' . '.aspell.' . $mainDictionary[0] . '.pws';
473 $fileNames[1] = $this->personalDictionaryPath . '/' . '.aspell.' . $mainDictionary[0] . '.prepl';
474 foreach ($fileNames as $fileName) {
475 if (file_exists($fileName)) {
476 $fileContent = file_get_contents($fileName);
477 if ($fileContent === FALSE) {
478 GeneralUtility::sysLog('SpellChecker personal word list read error: ' . $fileName, $this->extKey, GeneralUtility::SYSLOG_SEVERITY_ERROR);
479 } else {
480 $fileContent = explode(LF, $fileContent);
481 if (strpos($fileContent[0], 'utf-8') === FALSE) {
482 $fileContent[0] .= ' utf-8';
483 $fileContent = implode(LF, $fileContent);
484 $result = file_put_contents($fileName, $fileContent);
485 if ($result === FALSE) {
486 GeneralUtility::sysLog('SpellChecker personal word list write error: ' . $fileName, $this->extKey, GeneralUtility::SYSLOG_SEVERITY_ERROR);
487 }
488 }
489 }
490 }
491 }
492 }
493 }
494
495 /**
496 * Handler for the opening of a tag
497 */
498 public function startHandler($xml_parser, $tag, $attributes) {
499 if ((string)$this->xmlCharacterData !== '') {
500 $this->spellCheckHandler($xml_parser, $this->xmlCharacterData);
501 $this->xmlCharacterData = '';
502 }
503 switch ($tag) {
504 case 'spellchecker':
505 break;
506 case 'br':
507
508 case 'BR':
509
510 case 'img':
511
512 case 'IMG':
513
514 case 'hr':
515
516 case 'HR':
517
518 case 'area':
519
520 case 'AREA':
521 $this->text .= '<' . $this->csConvObj->conv_case($this->parserCharset, $tag, 'toLower') . ' ';
522 foreach ($attributes as $key => $val) {
523 $this->text .= $key . '="' . $val . '" ';
524 }
525 $this->text .= ' />';
526 break;
527 default:
528 $this->text .= '<' . $this->csConvObj->conv_case($this->parserCharset, $tag, 'toLower') . ' ';
529 foreach ($attributes as $key => $val) {
530 $this->text .= $key . '="' . $val . '" ';
531 }
532 $this->text .= '>';
533 }
534 }
535
536 /**
537 * Handler for the closing of a tag
538 */
539 public function endHandler($xml_parser, $tag) {
540 if ((string)$this->xmlCharacterData !== '') {
541 $this->spellCheckHandler($xml_parser, $this->xmlCharacterData);
542 $this->xmlCharacterData = '';
543 }
544 switch ($tag) {
545 case 'spellchecker':
546
547 case 'br':
548
549 case 'BR':
550
551 case 'img':
552
553 case 'IMG':
554
555 case 'hr':
556
557 case 'HR':
558
559 case 'input':
560
561 case 'INPUT':
562
563 case 'area':
564
565 case 'AREA':
566 break;
567 default:
568 $this->text .= '</' . $tag . '>';
569 }
570 }
571
572 /**
573 * Handler for the content of a tag
574 */
575 public function spellCheckHandler($xml_parser, $string) {
576 $incurrent = array();
577 $stringText = $string;
578 $words = preg_split($this->parserCharset == 'utf-8' ? '/\\P{L}+/u' : '/\\W+/', $stringText);
579 foreach ($words as $word) {
580 $word = preg_replace('/ /' . ($this->parserCharset == 'utf-8' ? 'u' : ''), '', $word);
581 if ($word && !is_numeric($word)) {
582 if ($this->pspell_is_available && !$this->forceCommandMode) {
583 if (!pspell_check($this->pspell_link, $word)) {
584 if (!in_array($word, $this->misspelled)) {
585 if (sizeof($this->misspelled) != 0) {
586 $this->suggestedWords .= ',';
587 }
588 $suggest = array();
589 $suggest = pspell_suggest($this->pspell_link, $word);
590 if (sizeof($suggest) != 0) {
591 $this->suggestionCount++;
592 $this->suggestedWordCount += sizeof($suggest);
593 }
594 $this->suggestedWords .= '"' . $word . '":"' . implode(',', $suggest) . '"';
595 $this->misspelled[] = $word;
596 unset($suggest);
597 }
598 if (!in_array($word, $incurrent)) {
599 $stringText = preg_replace('/\\b' . $word . '\\b/' . ($this->parserCharset == 'utf-8' ? 'u' : ''), '<span class="htmlarea-spellcheck-error">' . $word . '</span>', $stringText);
600 $incurrent[] = $word;
601 }
602 }
603 } else {
604 $tmpFileName = GeneralUtility::tempnam($this->filePrefix);
605 if (!($filehandle = fopen($tmpFileName, 'wb'))) {
606 echo 'SpellChecker tempfile open error';
607 }
608 if (!fwrite($filehandle, $word)) {
609 echo 'SpellChecker tempfile write error';
610 }
611 if (!fclose($filehandle)) {
612 echo 'SpellChecker tempfile close error';
613 }
614 $catCommand = TYPO3_OS === 'WIN' ? 'type' : 'cat';
615 $AspellCommand = $catCommand . ' ' . escapeshellarg($tmpFileName) . ' | '
616 . $this->AspellDirectory
617 . ' -a check'
618 . ' --mode=none'
619 . ' --sug-mode=' . escapeshellarg($this->pspellMode)
620 . ($this->personalDictionaryPath ? ' --home-dir=' . escapeshellarg($this->personalDictionaryPath) : '')
621 . ' --lang=' . escapeshellarg($this->dictionary)
622 . ' --encoding=' . escapeshellarg($this->aspellEncoding)
623 . ' 2>&1';
624 $AspellAnswer = shell_exec($AspellCommand);
625 $AspellResultLines = array();
626 $AspellResultLines = GeneralUtility::trimExplode(LF, $AspellAnswer, TRUE);
627 if (substr($AspellResultLines[0], 0, 6) == 'Error:') {
628 echo '{' . $AspellAnswer . '}';
629 }
630 GeneralUtility::unlink_tempfile($tmpFileName);
631 if ($AspellResultLines['1'][0] !== '*') {
632 if (!in_array($word, $this->misspelled)) {
633 if (sizeof($this->misspelled) != 0) {
634 $this->suggestedWords .= ',';
635 }
636 $suggest = array();
637 $suggestions = array();
638 if ($AspellResultLines['1'][0] === '&') {
639 $suggestions = GeneralUtility::trimExplode(':', $AspellResultLines['1'], TRUE);
640 $suggest = GeneralUtility::trimExplode(',', $suggestions['1'], TRUE);
641 }
642 if (sizeof($suggest) != 0) {
643 $this->suggestionCount++;
644 $this->suggestedWordCount += sizeof($suggest);
645 }
646 $this->suggestedWords .= '"' . $word . '":"' . implode(',', $suggest) . '"';
647 $this->misspelled[] = $word;
648 unset($suggest);
649 unset($suggestions);
650 }
651 if (!in_array($word, $incurrent)) {
652 $stringText = preg_replace('/\\b' . $word . '\\b/' . ($this->parserCharset == 'utf-8' ? 'u' : ''), '<span class="htmlarea-spellcheck-error">' . $word . '</span>', $stringText);
653 $incurrent[] = $word;
654 }
655 }
656 unset($AspellResultLines);
657 }
658 $this->wordCount++;
659 }
660 }
661 $this->text .= $stringText;
662 unset($incurrent);
663 }
664
665 /**
666 * Handler for collecting data within a tag
667 */
668 public function collectDataHandler($xml_parser, $string) {
669 $this->xmlCharacterData .= $string;
670 }
671
672 /**
673 * Default handler for the xml parser
674 */
675 public function defaultHandler($xml_parser, $string) {
676 $this->text .= $string;
677 }
678
679 }