2 namespace TYPO3\CMS\Rtehtmlarea\Controller;
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';
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.';
184 $this->defaultAspellEncoding = trim(shell_exec($this->AspellDirectory .
' config encoding'));
187 $dictionaryList = shell_exec($this->AspellDirectory .
' dump dicts');
191 if ($restrictToDictionaries) {
194 if (empty($dictionaryArray)) {
195 $dictionaryArray[] =
'en';
199 if (!$defaultDictionary || !in_array($defaultDictionary, $dictionaryArray)) {
200 $defaultDictionary =
'en';
202 uasort($dictionaryArray,
'strcoll');
203 $dictionaryList = implode(
',', $dictionaryArray);
205 if (empty($this->dictionary) || !in_array($this->dictionary, $dictionaryArray)) {
206 $this->dictionary =
'en';
211 $this->pspellMode =
GeneralUtility::inList(
'ultra,fast,normal,bad-spellers', $this->pspellMode) ? $this->pspellMode :
'normal';
212 switch ($this->pspellMode) {
216 $pspellModeFlag = PSPELL_FAST;
219 $pspellModeFlag = PSPELL_BAD_SPELLERS;
224 $pspellModeFlag = PSPELL_NORMAL;
230 if (strtolower($this->charset) ==
'iso-8859-1') {
231 $this->parserCharset = strtolower($this->charset);
235 if ($this->parserCharset ==
'iso-8859-1' && strstr($this->defaultAspellEncoding,
'8859-1')) {
239 if ($this->pspell_is_available && !$this->forceCommandMode) {
240 $this->pspell_link = pspell_new($this->dictionary,
'',
'', $this->parserCharset, $pspellModeFlag);
248 if ($cmd ==
'learn') {
250 if (TYPO3_MODE !==
'BE' || !is_object(
$GLOBALS[
'BE_USER'])) {
255 $to_p_dict = $to_p_dict ? $to_p_dict : array();
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) {
262 $filehandle = fopen($tmpFileName,
'wb');
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));
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));
278 $result = fwrite($filehandle, $cmd, strlen($cmd));
283 $aspellCommand = ((TYPO3_OS ===
'WIN') ?
'type ' :
'cat ') . escapeshellarg($tmpFileName) .
' | '
284 . $this->AspellDirectory
286 . ($this->personalDictionaryPath ?
' --home-dir=' . escapeshellarg($this->personalDictionaryPath) :
'')
287 .
' --lang=' . escapeshellarg($this->dictionary)
288 .
' --encoding=' . escapeshellarg($mainDictionaryCharacterSet)
290 $aspellResult = shell_exec($aspellCommand);
304 $this->result =
'<?xml version="1.0" encoding="' . $this->parserCharset .
'"?>
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) .
'">
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">
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';
325 if (!xml_set_character_data_handler($parser,
'collectDataHandler')) {
326 echo
'Bad xml handler setting';
328 if (!xml_set_default_handler($parser,
'defaultHandler')) {
329 echo
'Bad xml handler setting';
331 if (!xml_parse($parser, (
'<?xml version="1.0" encoding="' . $this->parserCharset .
'"?><spellchecker> ' . preg_replace((
'/ /' . ($this->parserCharset ==
'utf-8' ?
'u' :
'')),
' ', $content) .
' </spellchecker>'))) {
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);
337 xml_parser_free($parser);
338 if ($this->pspell_is_available && !$this->forceCommandMode) {
339 pspell_clear_session($this->pspell_link);
341 $this->result .=
'var suggestedWords = {' . $this->suggestedWords .
'};
342 var dictionaries = "' . $dictionaryList .
'";
343 var selectedDictionary = "' . $this->dictionary .
'";
346 $time = number_format(microtime(
true) - $time_start, 2,
',',
' ');
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 .
'" };
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>';
361 $response = $response->
withHeader(
'Content-Type',
'text/html; charset=' . strtoupper($this->parserCharset));
362 $response->
getBody()->write($this->result);
374 $this->mainDictionaryPath =
'';
375 $aspellCommand = $this->AspellDirectory .
' config dict-dir';
376 $aspellResult = shell_exec($aspellCommand);
378 $this->mainDictionaryPath = trim($aspellResult);
380 if (!$aspellResult || !$this->mainDictionaryPath) {
394 if ($this->mainDictionaryPath) {
396 $mainDictionary = preg_split(
'/[-_]/', $this->dictionary, 2);
398 $dictionaryFileName = $this->mainDictionaryPath .
'/' . $mainDictionary[0] .
'.dat';
399 $dictionaryHandle = fopen($dictionaryFileName,
'rb');
400 if (!$dictionaryHandle) {
403 $dictionaryContent = fread($dictionaryHandle, 500);
404 if ($dictionaryContent ===
false) {
407 fclose($dictionaryHandle);
409 $dictionaryContent = preg_split(
'/charset\s*/', $dictionaryContent, 2);
410 if ($dictionaryContent[1]) {
414 $characterSet = str_replace(
417 $dictionaryContent[0]
420 if (!$characterSet) {
426 return $characterSet;
436 $this->personalDictionaryPath =
'';
438 if (
$GLOBALS[
'BE_USER']->user[
'uid']) {
439 $personalDictionaryFolderName =
'BE_' .
$GLOBALS[
'BE_USER']->user[
'uid'];
442 $personalDictionaryFolder = \TYPO3\CMS\Core\Resource\ResourceFactory::getInstance()->getFolderObjectFromCombinedIdentifier(PATH_site . $this->uploadFolder . $personalDictionaryFolderName);
444 $personalDictionaryFolder =
false;
447 if (!$personalDictionaryFolder) {
448 $personalDictionaryFolderName .=
'_personaldictionary';
449 $backendUserDefaultFolder =
$GLOBALS[
'BE_USER']->getDefaultUploadFolder();
450 if ($backendUserDefaultFolder->hasFolder($personalDictionaryFolderName)) {
451 $personalDictionaryFolder = $backendUserDefaultFolder->getSubfolder($personalDictionaryFolderName);
453 $personalDictionaryFolder = $backendUserDefaultFolder->createFolder($personalDictionaryFolderName);
456 $this->personalDictionaryPath = PATH_site . rtrim($personalDictionaryFolder->getPublicUrl(),
'/');
469 if ($this->personalDictionaryPath) {
472 $fileNames = array();
473 $mainDictionary = preg_split(
'/[-_]/', $this->dictionary, 2);
474 $fileNames[0] = $this->personalDictionaryPath .
'/' .
'.aspell.' . $mainDictionary[0] .
'.pws';
475 $fileNames[1] = $this->personalDictionaryPath .
'/' .
'.aspell.' . $mainDictionary[0] .
'.prepl';
476 foreach ($fileNames as $fileName) {
477 if (file_exists($fileName)) {
478 $fileContent = file_get_contents($fileName);
479 if ($fileContent ===
false) {
482 $fileContent = explode(LF, $fileContent);
483 if (strpos($fileContent[0],
'utf-8') ===
false) {
484 $fileContent[0] .=
' utf-8';
485 $fileContent = implode(LF, $fileContent);
486 $result = file_put_contents($fileName, $fileContent);
502 if ((
string)$this->xmlCharacterData !==
'') {
504 $this->xmlCharacterData =
'';
524 $this->text .=
'<' . $this->csConvObj->conv_case($this->parserCharset, $tag,
'toLower') .
' ';
525 foreach ($attributes as $key => $val) {
526 $this->text .= $key .
'="' . $val .
'" ';
528 $this->text .=
' />';
531 $this->text .=
'<' . $this->csConvObj->conv_case($this->parserCharset, $tag,
'toLower') .
' ';
532 foreach ($attributes as $key => $val) {
533 $this->text .= $key .
'="' . $val .
'" ';
544 if ((
string)$this->xmlCharacterData !==
'') {
546 $this->xmlCharacterData =
'';
572 $this->text .=
'</' . $tag .
'>';
581 $incurrent = array();
582 $stringText = $string;
583 $words = preg_split($this->parserCharset ==
'utf-8' ?
'/\\P{L}+/u' :
'/\\W+/', $stringText);
584 foreach ($words as $word) {
585 $word = preg_replace(
'/ /' . ($this->parserCharset ==
'utf-8' ?
'u' :
''),
'', $word);
586 if ($word && !is_numeric($word)) {
587 if ($this->pspell_is_available && !$this->forceCommandMode) {
588 if (!pspell_check($this->pspell_link, $word)) {
589 if (!in_array($word, $this->misspelled)) {
590 if (
sizeof($this->misspelled) != 0) {
591 $this->suggestedWords .=
',';
594 $suggest = pspell_suggest($this->pspell_link, $word);
595 if (
sizeof($suggest) != 0) {
596 $this->suggestionCount++;
597 $this->suggestedWordCount +=
sizeof($suggest);
599 $this->suggestedWords .=
'"' . $word .
'":"' . implode(
',', $suggest) .
'"';
600 $this->misspelled[] = $word;
603 if (!in_array($word, $incurrent)) {
604 $stringText = preg_replace(
'/\\b' . $word .
'\\b/' . ($this->parserCharset ==
'utf-8' ?
'u' :
''),
'<span class="htmlarea-spellcheck-error">' . $word .
'</span>', $stringText);
605 $incurrent[] = $word;
610 if (!($filehandle = fopen($tmpFileName,
'wb'))) {
611 echo
'SpellChecker tempfile open error';
613 if (!fwrite($filehandle, $word)) {
614 echo
'SpellChecker tempfile write error';
616 if (!fclose($filehandle)) {
617 echo
'SpellChecker tempfile close error';
619 $catCommand = TYPO3_OS ===
'WIN' ?
'type' :
'cat';
620 $AspellCommand = $catCommand .
' ' . escapeshellarg($tmpFileName) .
' | '
621 . $this->AspellDirectory
624 .
' --sug-mode=' . escapeshellarg($this->pspellMode)
625 . ($this->personalDictionaryPath ?
' --home-dir=' . escapeshellarg($this->personalDictionaryPath) :
'')
626 .
' --lang=' . escapeshellarg($this->dictionary)
627 .
' --encoding=' . escapeshellarg($this->aspellEncoding)
629 $AspellAnswer = shell_exec($AspellCommand);
630 $AspellResultLines = array();
632 if (substr($AspellResultLines[0], 0, 6) ==
'Error:') {
633 echo
'{' . $AspellAnswer .
'}';
636 if ($AspellResultLines[
'1'][0] !==
'*') {
637 if (!in_array($word, $this->misspelled)) {
638 if (
sizeof($this->misspelled) != 0) {
639 $this->suggestedWords .=
',';
642 $suggestions = array();
643 if ($AspellResultLines[
'1'][0] ===
'&') {
647 if (
sizeof($suggest) != 0) {
648 $this->suggestionCount++;
649 $this->suggestedWordCount +=
sizeof($suggest);
651 $this->suggestedWords .=
'"' . $word .
'":"' . implode(
',', $suggest) .
'"';
652 $this->misspelled[] = $word;
656 if (!in_array($word, $incurrent)) {
657 $stringText = preg_replace(
'/\\b' . $word .
'\\b/' . ($this->parserCharset ==
'utf-8' ?
'u' :
''),
'<span class="htmlarea-spellcheck-error">' . $word .
'</span>', $stringText);
658 $incurrent[] = $word;
661 unset($AspellResultLines);
666 $this->text .= $stringText;
675 $this->xmlCharacterData .= $string;
683 $this->text .= $string;