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