| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418 | 
							- <?php
 - 
 - /**
 -  +-----------------------------------------------------------------------+
 -  | This file is part of the Roundcube Webmail client                     |
 -  | Copyright (C) 2011-2013, Kolab Systems AG                             |
 -  | Copyright (C) 2008-2013, The Roundcube Dev Team                       |
 -  |                                                                       |
 -  | Licensed under the GNU General Public License version 3 or            |
 -  | any later version with exceptions for skins & plugins.                |
 -  | See the README file for a full license statement.                     |
 -  |                                                                       |
 -  | PURPOSE:                                                              |
 -  |   Spellchecking using different backends                              |
 -  +-----------------------------------------------------------------------+
 -  | Author: Aleksander Machniak <machniak@kolabsys.com>                   |
 -  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
 -  +-----------------------------------------------------------------------+
 - */
 - 
 - /**
 -  * Helper class for spellchecking with Googielspell and PSpell support.
 -  *
 -  * @package    Framework
 -  * @subpackage Utils
 -  */
 - class rcube_spellchecker
 - {
 -     private $matches = array();
 -     private $engine;
 -     private $backend;
 -     private $lang;
 -     private $rc;
 -     private $error;
 -     private $options = array();
 -     private $dict;
 -     private $have_dict;
 - 
 - 
 -     /**
 -      * Constructor
 -      *
 -      * @param string $lang Language code
 -      */
 -     function __construct($lang = 'en')
 -     {
 -         $this->rc     = rcube::get_instance();
 -         $this->engine = $this->rc->config->get('spellcheck_engine', 'googie');
 -         $this->lang   = $lang ?: 'en';
 - 
 -         $this->options = array(
 -             'ignore_syms' => $this->rc->config->get('spellcheck_ignore_syms'),
 -             'ignore_nums' => $this->rc->config->get('spellcheck_ignore_nums'),
 -             'ignore_caps' => $this->rc->config->get('spellcheck_ignore_caps'),
 -             'dictionary'  => $this->rc->config->get('spellcheck_dictionary'),
 -         );
 - 
 -         $cls = 'rcube_spellcheck_' . $this->engine;
 -         if (class_exists($cls)) {
 -             $this->backend = new $cls($this, $this->lang);
 -             $this->backend->options = $this->options;
 -         }
 -         else {
 -             $this->error = "Unknown spellcheck engine '$this->engine'";
 -         }
 -     }
 - 
 -     /**
 -      * Return a list of supported languages
 -      */
 -     function languages()
 -     {
 -         // trust configuration
 -         $configured = $this->rc->config->get('spellcheck_languages');
 -         if (!empty($configured) && is_array($configured) && !$configured[0]) {
 -             return $configured;
 -         }
 -         else if (!empty($configured)) {
 -             $langs = (array)$configured;
 -         }
 -         else if ($this->backend) {
 -             $langs = $this->backend->languages();
 -         }
 - 
 -         // load index
 -         @include(RCUBE_LOCALIZATION_DIR . 'index.inc');
 - 
 -         // add correct labels
 -         $languages = array();
 -         foreach ($langs as $lang) {
 -             $langc = strtolower(substr($lang, 0, 2));
 -             $alias = $rcube_language_aliases[$langc];
 -             if (!$alias) {
 -                 $alias = $langc.'_'.strtoupper($langc);
 -             }
 -             if ($rcube_languages[$lang]) {
 -                 $languages[$lang] = $rcube_languages[$lang];
 -             }
 -             else if ($rcube_languages[$alias]) {
 -                 $languages[$lang] = $rcube_languages[$alias];
 -             }
 -             else {
 -                 $languages[$lang] = ucfirst($lang);
 -             }
 -         }
 - 
 -         // remove possible duplicates (#1489395)
 -         $languages = array_unique($languages);
 - 
 -         asort($languages);
 - 
 -         return $languages;
 -     }
 - 
 -     /**
 -      * Set content and check spelling
 -      *
 -      * @param string $text    Text content for spellchecking
 -      * @param bool   $is_html Enables HTML-to-Text conversion
 -      *
 -      * @return bool True when no mispelling found, otherwise false
 -      */
 -     function check($text, $is_html = false)
 -     {
 -         // convert to plain text
 -         if ($is_html) {
 -             $this->content = $this->html2text($text);
 -         }
 -         else {
 -             $this->content = $text;
 -         }
 - 
 -         if ($this->backend) {
 -             $this->matches = $this->backend->check($this->content);
 -         }
 - 
 -         return $this->found() == 0;
 -     }
 - 
 -     /**
 -      * Number of mispellings found (after check)
 -      *
 -      * @return int Number of mispellings
 -      */
 -     function found()
 -     {
 -         return count($this->matches);
 -     }
 - 
 -     /**
 -      * Returns suggestions for the specified word
 -      *
 -      * @param string $word The word
 -      *
 -      * @return array Suggestions list
 -      */
 -     function get_suggestions($word)
 -     {
 -         if ($this->backend) {
 -             return $this->backend->get_suggestions($word);
 -         }
 - 
 -         return array();
 -     }
 - 
 -     /**
 -      * Returns misspelled words
 -      *
 -      * @param string $text The content for spellchecking. If empty content
 -      *                     used for check() method will be used.
 -      *
 -      * @return array List of misspelled words
 -      */
 -     function get_words($text = null, $is_html=false)
 -     {
 -         if ($is_html) {
 -             $text = $this->html2text($text);
 -         }
 - 
 -         if ($this->backend) {
 -             return $this->backend->get_words($text);
 -         }
 - 
 -         return array();
 -     }
 - 
 -     /**
 -      * Returns checking result in XML (Googiespell) format
 -      *
 -      * @return string XML content
 -      */
 -     function get_xml()
 -     {
 -         // send output
 -         $out = '<?xml version="1.0" encoding="'.RCUBE_CHARSET.'"?><spellresult charschecked="'.mb_strlen($this->content).'">';
 - 
 -         foreach ((array)$this->matches as $item) {
 -             $out .= '<c o="'.$item[1].'" l="'.$item[2].'">';
 -             $out .= is_array($item[4]) ? implode("\t", $item[4]) : $item[4];
 -             $out .= '</c>';
 -         }
 - 
 -         $out .= '</spellresult>';
 - 
 -         return $out;
 -     }
 - 
 -     /**
 -      * Returns checking result (misspelled words with suggestions)
 -      *
 -      * @return array Spellchecking result. An array indexed by word.
 -      */
 -     function get()
 -     {
 -         $result = array();
 - 
 -         foreach ((array)$this->matches as $item) {
 -             if ($this->engine == 'pspell') {
 -                 $word = $item[0];
 -             }
 -             else {
 -                 $word = mb_substr($this->content, $item[1], $item[2], RCUBE_CHARSET);
 -             }
 - 
 -             if (is_array($item[4])) {
 -                 $suggestions = $item[4];
 -             }
 -             else if (empty($item[4])) {
 -                 $suggestions = array();
 -             }
 -             else {
 -                 $suggestions = explode("\t", $item[4]);
 -             }
 - 
 -             $result[$word] = $suggestions;
 -         }
 - 
 -         return $result;
 -     }
 - 
 -     /**
 -      * Returns error message
 -      *
 -      * @return string Error message
 -      */
 -     function error()
 -     {
 -         return $this->error ?: ($this->backend ? $this->backend->error() : false);
 -     }
 - 
 -     private function html2text($text)
 -     {
 -         $h2t = new rcube_html2text($text, false, false, 0);
 -         return $h2t->get_text();
 -     }
 - 
 -     /**
 -      * Check if the specified word is an exception accoring to 
 -      * spellcheck options.
 -      *
 -      * @param string  $word  The word
 -      *
 -      * @return bool True if the word is an exception, False otherwise
 -      */
 -     public function is_exception($word)
 -     {
 -         // Contain only symbols (e.g. "+9,0", "2:2")
 -         if (!$word || preg_match('/^[0-9@#$%^&_+~*<>=:;?!,.-]+$/', $word))
 -             return true;
 - 
 -         // Contain symbols (e.g. "g@@gle"), all symbols excluding separators
 -         if (!empty($this->options['ignore_syms']) && preg_match('/[@#$%^&_+~*=-]/', $word))
 -             return true;
 - 
 -         // Contain numbers (e.g. "g00g13")
 -         if (!empty($this->options['ignore_nums']) && preg_match('/[0-9]/', $word))
 -             return true;
 - 
 -         // Blocked caps (e.g. "GOOGLE")
 -         if (!empty($this->options['ignore_caps']) && $word == mb_strtoupper($word))
 -             return true;
 - 
 -         // Use exceptions from dictionary
 -         if (!empty($this->options['dictionary'])) {
 -             $this->load_dict();
 - 
 -             // @TODO: should dictionary be case-insensitive?
 -             if (!empty($this->dict) && in_array($word, $this->dict))
 -                 return true;
 -         }
 - 
 -         return false;
 -     }
 - 
 -     /**
 -      * Add a word to dictionary
 -      *
 -      * @param string  $word  The word to add
 -      */
 -     public function add_word($word)
 -     {
 -         $this->load_dict();
 - 
 -         foreach (explode(' ', $word) as $word) {
 -             // sanity check
 -             if (strlen($word) < 512) {
 -                 $this->dict[] = $word;
 -                 $valid = true;
 -             }
 -         }
 - 
 -         if ($valid) {
 -             $this->dict = array_unique($this->dict);
 -             $this->update_dict();
 -         }
 -     }
 - 
 -     /**
 -      * Remove a word from dictionary
 -      *
 -      * @param string  $word  The word to remove
 -      */
 -     public function remove_word($word)
 -     {
 -         $this->load_dict();
 - 
 -         if (($key = array_search($word, $this->dict)) !== false) {
 -             unset($this->dict[$key]);
 -             $this->update_dict();
 -         }
 -     }
 - 
 -     /**
 -      * Update dictionary row in DB
 -      */
 -     private function update_dict()
 -     {
 -         if (strcasecmp($this->options['dictionary'], 'shared') != 0) {
 -             $userid = $this->rc->get_user_id();
 -         }
 - 
 -         $plugin = $this->rc->plugins->exec_hook('spell_dictionary_save', array(
 -             'userid' => $userid, 'language' => $this->lang, 'dictionary' => $this->dict));
 - 
 -         if (!empty($plugin['abort'])) {
 -             return;
 -         }
 - 
 -         if ($this->have_dict) {
 -             if (!empty($this->dict)) {
 -                 $this->rc->db->query(
 -                     "UPDATE " . $this->rc->db->table_name('dictionary', true)
 -                     ." SET `data` = ?"
 -                     ." WHERE `user_id` " . ($plugin['userid'] ? "= ".$this->rc->db->quote($plugin['userid']) : "IS NULL")
 -                         ." AND `language` = ?",
 -                     implode(' ', $plugin['dictionary']), $plugin['language']);
 -             }
 -             // don't store empty dict
 -             else {
 -                 $this->rc->db->query(
 -                     "DELETE FROM " . $this->rc->db->table_name('dictionary', true)
 -                     ." WHERE `user_id` " . ($plugin['userid'] ? "= ".$this->rc->db->quote($plugin['userid']) : "IS NULL")
 -                         ." AND `language` = ?",
 -                     $plugin['language']);
 -             }
 -         }
 -         else if (!empty($this->dict)) {
 -             $this->rc->db->query(
 -                 "INSERT INTO " . $this->rc->db->table_name('dictionary', true)
 -                 ." (`user_id`, `language`, `data`) VALUES (?, ?, ?)",
 -                 $plugin['userid'], $plugin['language'], implode(' ', $plugin['dictionary']));
 -         }
 -     }
 - 
 -     /**
 -      * Get dictionary from DB
 -      */
 -     private function load_dict()
 -     {
 -         if (is_array($this->dict)) {
 -             return $this->dict;
 -         }
 - 
 -         if (strcasecmp($this->options['dictionary'], 'shared') != 0) {
 -             $userid = $this->rc->get_user_id();
 -         }
 - 
 -         $plugin = $this->rc->plugins->exec_hook('spell_dictionary_get', array(
 -             'userid' => $userid, 'language' => $this->lang, 'dictionary' => array()));
 - 
 -         if (empty($plugin['abort'])) {
 -             $dict = array();
 -             $sql_result = $this->rc->db->query(
 -                 "SELECT `data` FROM " . $this->rc->db->table_name('dictionary', true)
 -                 ." WHERE `user_id` ". ($plugin['userid'] ? "= ".$this->rc->db->quote($plugin['userid']) : "IS NULL")
 -                     ." AND `language` = ?",
 -                 $plugin['language']);
 - 
 -             if ($sql_arr = $this->rc->db->fetch_assoc($sql_result)) {
 -                 $this->have_dict = true;
 -                 if (!empty($sql_arr['data'])) {
 -                     $dict = explode(' ', $sql_arr['data']);
 -                 }
 -             }
 - 
 -             $plugin['dictionary'] = array_merge((array)$plugin['dictionary'], $dict);
 -         }
 - 
 -         if (!empty($plugin['dictionary']) && is_array($plugin['dictionary'])) {
 -             $this->dict = $plugin['dictionary'];
 -         }
 -         else {
 -             $this->dict = array();
 -         }
 - 
 -         return $this->dict;
 -     }
 - }
 
 
  |