| | 1 | <?php |
| | 2 | /** |
| | 3 | * This File contains functions used to handle Ajax spellchecking requests. |
| | 4 | * |
| | 5 | * @package WordPress |
| | 6 | * @author Benedikt Forchhammer <b.forchhammer@mind2.de> |
| | 7 | * @since 2.9.0 |
| | 8 | */ |
| | 9 | |
| | 10 | /** |
| | 11 | * Allows for the tinymce SpellChecker plugin directory to be moved from the default location. |
| | 12 | * |
| | 13 | * @since 2.9.0 |
| | 14 | */ |
| | 15 | if ( !defined('MCE_SPELLCHECKER_DIR') ) |
| | 16 | define( 'MCE_SPELLCHECKER_DIR', ABSPATH . WPINC . '/js/tinymce/plugins/spellchecker'); |
| | 17 | |
| | 18 | add_action('wp_ajax_spellcheck', 'wp_spellcheck_ajax'); |
| | 19 | add_filter('wp_spellcheck_engine_config','wp_spellcheck_engine_replacements'); |
| | 20 | |
| | 21 | /** |
| | 22 | * Changes config to use WP_GoogleSpell instead of GoogleSpell. |
| | 23 | * |
| | 24 | * @param array $config Config array for SpellChecker |
| | 25 | * @since 2.9.0 |
| | 26 | * @return array |
| | 27 | */ |
| | 28 | function wp_spellcheck_engine_replacements($config) { |
| | 29 | // replace GoogleSpell with WP_GoogleSpell |
| | 30 | if ($config['general.engine'] == 'GoogleSpell') { |
| | 31 | $config['general.engine'] = 'WP_GoogleSpell'; |
| | 32 | $config['general.engine.path'] = ABSPATH . 'wp-admin/includes/class-wp-googlespell.php'; |
| | 33 | } |
| | 34 | return $config; |
| | 35 | } |
| | 36 | |
| | 37 | /** |
| | 38 | * Handle ajax request for "spellcheck" action. |
| | 39 | * |
| | 40 | * This function is called by the action hook "wp_ajax_spellcheck". |
| | 41 | * |
| | 42 | * @uses apply_filters wp_spellcheck_rpc_request |
| | 43 | * @uses do_action wp_spellcheck_rpc_result |
| | 44 | * @since 2.9.0 |
| | 45 | * @return void |
| | 46 | */ |
| | 47 | function wp_spellcheck_ajax() { |
| | 48 | |
| | 49 | // set headers at this point so that they are set fatal error responses as well. |
| | 50 | wp_spellcheck_rpc_response_headers(); |
| | 51 | |
| | 52 | // get request data |
| | 53 | $request = wp_spellcheck_rpc_get_request(); |
| | 54 | if (is_wp_error($request)) { |
| | 55 | wp_spellcheck_rpc_error($request->get_error_message()); |
| | 56 | } |
| | 57 | $request = apply_filters('wp_spellcheck_rpc_request', $request); |
| | 58 | |
| | 59 | // execute and return spellcheck |
| | 60 | $result = wp_spellcheck_do_spellcheck($request['method'], $request['params']); |
| | 61 | if (is_wp_error($result)) { |
| | 62 | wp_spellcheck_rpc_error($result->get_error_message()); |
| | 63 | } |
| | 64 | do_action_ref_array('wp_spellcheck_rpc_result', array( $request, &$result )); |
| | 65 | |
| | 66 | wp_spellcheck_rpc_send_response($request['id'], $result); |
| | 67 | } |
| | 68 | |
| | 69 | /** |
| | 70 | * Calls a method on the SpellChecking Engine and returns the result. |
| | 71 | * |
| | 72 | * This function calls the method specified in $request['method'] |
| | 73 | * with the parameters specified in $request['params'] on the configured |
| | 74 | * SpellChecker Engine. |
| | 75 | * |
| | 76 | * The two methods which are available by default are: |
| | 77 | * |
| | 78 | * <ul> |
| | 79 | * <li><strong>checkWords<strong>: checks an array of words for misspellings and returns array of misspelled words</li> |
| | 80 | * <li><strong>getSuggestions</strong>: retrieves a list of suggestions for a given misspelled word</li> |
| | 81 | * </ul> |
| | 82 | * |
| | 83 | * @param string $method name of method |
| | 84 | * @param array $params parameters |
| | 85 | * @since 2.9.0 |
| | 86 | * @return mixed The spellchecking result or WP_Error object |
| | 87 | */ |
| | 88 | function wp_spellcheck_do_spellcheck ($method, $params = array()) { |
| | 89 | $spellchecker =& wp_spellcheck_get_engine(); |
| | 90 | |
| | 91 | if (is_wp_error($spellchecker)) { |
| | 92 | return $spellchecker; |
| | 93 | } |
| | 94 | |
| | 95 | if (!is_object($spellchecker)) { |
| | 96 | return new WP_Error('wp-spellcheck-engine-null', __('Failed to get SpellChecker engine instance.')); |
| | 97 | } |
| | 98 | |
| | 99 | if (!method_exists($spellchecker, $method)) { |
| | 100 | // Either invalid method name or incomplete spellchecker class |
| | 101 | return new WP_Error('wp-spellcheck-engine-method-notfound', __('Failed to call method on spellchecking engine. ')); |
| | 102 | } |
| | 103 | |
| | 104 | if (!is_array($params)) { |
| | 105 | $params = array($params); |
| | 106 | } |
| | 107 | |
| | 108 | $result = call_user_func_array(array($spellchecker, $method), $params); |
| | 109 | |
| | 110 | return $result; |
| | 111 | } |
| | 112 | |
| | 113 | /** |
| | 114 | * Get spellcheck request data. |
| | 115 | * |
| | 116 | * @since 2.9.0 |
| | 117 | * @return mixed |
| | 118 | */ |
| | 119 | function wp_spellcheck_rpc_get_request() { |
| | 120 | |
| | 121 | // try to get raw data |
| | 122 | $raw = wp_spellcheck_rpc_raw_input(); |
| | 123 | if (empty($raw)) { |
| | 124 | return new WP_Error("wp-spellcheck-request-empty", __("Could not get raw post data.")); |
| | 125 | } |
| | 126 | |
| | 127 | // try to parse data as json |
| | 128 | $request = wp_spellcheck_json_decode($raw); |
| | 129 | |
| | 130 | // check we have good request data |
| | 131 | if (!is_array($request)) { |
| | 132 | return new WP_Error("wp-spellcheck-request-malformed", __("Failed parsing request as JSON: ") . print_r($request, 1) ); |
| | 133 | } |
| | 134 | if (empty($request['method'])) { |
| | 135 | return new WP_Error("wp-spellcheck-request-malformed", __("Malformed JSON: missing \"method\" parameter.")); |
| | 136 | } |
| | 137 | if (empty($request['params'])) { |
| | 138 | $request['params'] = array(); |
| | 139 | } |
| | 140 | |
| | 141 | return $request; |
| | 142 | } |
| | 143 | |
| | 144 | /** |
| | 145 | * Retrieves the raw request data for spellchecking request. |
| | 146 | * |
| | 147 | * The function tries the following sources and returns the |
| | 148 | * first non-empty one found: |
| | 149 | * |
| | 150 | * <ul> |
| | 151 | * <li>$_POST[json_data]</li> |
| | 152 | * <li>$_GLOBALS[HTTP_RAW_POST_DATA]</li> |
| | 153 | * <li>$HTTP_RAW_POST_DATA</li> |
| | 154 | * <li>file_get_contents("php://input")</li> |
| | 155 | * <li>fopen("php://input", "r");</li> |
| | 156 | * </ul> |
| | 157 | * |
| | 158 | * @link http://core.trac.wordpress.org/browser/trunk/wp-includes/js/tinymce/plugins/spellchecker/rpc.php Roughly based on respective code in this file. |
| | 159 | * @since 2.9.0 |
| | 160 | * @return string Raw request data (json encoded) |
| | 161 | */ |
| | 162 | function wp_spellcheck_rpc_raw_input() { |
| | 163 | $raw = ""; |
| | 164 | |
| | 165 | // Try param |
| | 166 | if (isset($_POST["json_data"])) { |
| | 167 | $raw = $_POST["json_data"]; |
| | 168 | } |
| | 169 | |
| | 170 | // Try globals array |
| | 171 | if (!$raw && isset($_GLOBALS) && isset($_GLOBALS["HTTP_RAW_POST_DATA"])) { |
| | 172 | $raw = $_GLOBALS["HTTP_RAW_POST_DATA"]; |
| | 173 | } |
| | 174 | |
| | 175 | // Try globals variable |
| | 176 | if (!$raw && isset($HTTP_RAW_POST_DATA)) { |
| | 177 | $raw = $HTTP_RAW_POST_DATA; |
| | 178 | } |
| | 179 | |
| | 180 | // Try stream |
| | 181 | if (!$raw) { |
| | 182 | if (!function_exists('file_get_contents')) { |
| | 183 | $fp = fopen("php://input", "r"); |
| | 184 | if ($fp) { |
| | 185 | $raw = ""; |
| | 186 | |
| | 187 | while (!feof($fp)) $raw = fread($fp, 1024); |
| | 188 | fclose($fp); |
| | 189 | } |
| | 190 | } |
| | 191 | else { |
| | 192 | $raw = "" . file_get_contents("php://input"); |
| | 193 | } |
| | 194 | } |
| | 195 | |
| | 196 | return $raw; |
| | 197 | } |
| | 198 | |
| | 199 | /** |
| | 200 | * Send the given result to client. This will stop all execution. |
| | 201 | * |
| | 202 | * @param string $id Request id which was passed to the server in the request. |
| | 203 | * @param array $result Result of the request |
| | 204 | * @since 2.9.0 |
| | 205 | * @return void |
| | 206 | */ |
| | 207 | function wp_spellcheck_rpc_send_response($request_id, $result) { |
| | 208 | |
| | 209 | // Request and response id should always be the same |
| | 210 | $output = array( |
| | 211 | "id" => $request_id, |
| | 212 | "result" => $result, |
| | 213 | "error" => null |
| | 214 | ); |
| | 215 | |
| | 216 | // Return JSON encoded string |
| | 217 | echo wp_spellcheck_json_encode($output); |
| | 218 | exit(); |
| | 219 | } |
| | 220 | |
| | 221 | /** |
| | 222 | * Set headers for the spellcheck rpc response |
| | 223 | * |
| | 224 | * @uses do_action wp_spellcheck_rpc_headers |
| | 225 | * @since 2.9.0 |
| | 226 | * @return void |
| | 227 | */ |
| | 228 | function wp_spellcheck_rpc_response_headers() { |
| | 229 | header('Content-Type: text/plain'); |
| | 230 | header('Content-Encoding: UTF-8'); |
| | 231 | header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); |
| | 232 | header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); |
| | 233 | header("Cache-Control: no-store, no-cache, must-revalidate"); |
| | 234 | header("Cache-Control: post-check=0, pre-check=0", false); |
| | 235 | header("Pragma: no-cache"); |
| | 236 | |
| | 237 | do_action('wp_spellcheck_rpc_headers'); |
| | 238 | } |
| | 239 | |
| | 240 | /** |
| | 241 | * Send json encoded error message to client. This will stop all execution. |
| | 242 | * |
| | 243 | * Note: this is a clone of TinyMCE's throwError() function in the |
| | 244 | * SpellChecker class (spellchecker plugin). |
| | 245 | * |
| | 246 | * @param string $msg Message to send back to user. |
| | 247 | * @since 2.9.0 |
| | 248 | * @return void |
| | 249 | */ |
| | 250 | function wp_spellcheck_rpc_error($msg) { |
| | 251 | die('{"result":null,"id":null,"error":{"errstr":"' . addslashes($msg) . '","errfile":"","errline":null,"errcontext":"","level":"FATAL"}}'); |
| | 252 | } |
| | 253 | |
| | 254 | /** |
| | 255 | * Get the SpellChecker Engine Object |
| | 256 | * |
| | 257 | * @uses apply_filters wp_spellcheck_engine_class |
| | 258 | * @uses apply_filters wp_spellcheck_engine_path |
| | 259 | * @since 2.9.0 |
| | 260 | * @return SpellChecker spell checking engine |
| | 261 | */ |
| | 262 | function &wp_spellcheck_get_engine() { |
| | 263 | static $spellchecker = null; |
| | 264 | |
| | 265 | if ($spellchecker === null) { |
| | 266 | // load base class for all spellcheckers |
| | 267 | if ( !class_exists('SpellChecker') ) { |
| | 268 | require_once( MCE_SPELLCHECKER_DIR . "/classes/SpellChecker.php" ); |
| | 269 | } |
| | 270 | |
| | 271 | $config = wp_spellcheck_get_engine_config(); |
| | 272 | |
| | 273 | // get engine class name |
| | 274 | $engine_class = apply_filters('wp_spellcheck_engine_class', $config['general.engine']); |
| | 275 | |
| | 276 | // try to load engine class if necessary |
| | 277 | if ( !class_exists($engine_class) ) { |
| | 278 | |
| | 279 | // determine engine file path |
| | 280 | $engine_path = $config['general.engine.path']; |
| | 281 | if (empty($engine_path)) { |
| | 282 | $engine_path = MCE_SPELLCHECKER_DIR . "/classes/$engine_class.php"; |
| | 283 | } |
| | 284 | $engine_path = apply_filters('wp_spellcheck_engine_path', $engine_path, $engine_class); |
| | 285 | |
| | 286 | if ( !file_exists($engine_path) ) { |
| | 287 | return new WP_Error('wp-spellcheck-engine-not-found', __('Cannot load SpellChecker Engine; class file not found: ') . $engine_path ); |
| | 288 | } |
| | 289 | require_once( $engine_path ); |
| | 290 | |
| | 291 | if (!class_exists($engine_class)) { |
| | 292 | return new WP_Error('wp-spellcheck-engine-not-found', __('Cannot load SpellChecker Engine; class not found: ') . $engine_class ); |
| | 293 | } |
| | 294 | } |
| | 295 | |
| | 296 | $spellchecker = new $engine_class($config); |
| | 297 | } |
| | 298 | |
| | 299 | return $spellchecker; |
| | 300 | } |
| | 301 | |
| | 302 | /** |
| | 303 | * Get configuration values for the SpellChecker Engine. |
| | 304 | * |
| | 305 | * @uses apply_filters wp_spellcheck_engine_config |
| | 306 | * @since 2.9.0 |
| | 307 | * @return Array array of config values |
| | 308 | */ |
| | 309 | function wp_spellcheck_get_engine_config() { |
| | 310 | $config = array(); |
| | 311 | $config['general.engine'] = 'GoogleSpell'; |
| | 312 | |
| | 313 | // load default tinymce config |
| | 314 | require_once( MCE_SPELLCHECKER_DIR . "/config.php" ); |
| | 315 | |
| | 316 | return apply_filters('wp_spellcheck_engine_config', $config); |
| | 317 | } |
| | 318 | |
| | 319 | /** |
| | 320 | * Encodes a php object into json |
| | 321 | * |
| | 322 | * @param mixed the thing to encode |
| | 323 | * @since 2.9.0 |
| | 324 | * @return string json string |
| | 325 | */ |
| | 326 | function wp_spellcheck_json_encode($thing) { |
| | 327 | $json =& wp_spellcheck_json_object(); |
| | 328 | return $json->encode($thing); |
| | 329 | } |
| | 330 | |
| | 331 | /** |
| | 332 | * Decodes a json string into the respective php object |
| | 333 | * |
| | 334 | * @param string json string |
| | 335 | * @since 2.9.0 |
| | 336 | * @return mixed json decoded thing |
| | 337 | */ |
| | 338 | function wp_spellcheck_json_decode($string) { |
| | 339 | $json =& wp_spellcheck_json_object(); |
| | 340 | return $json->decode($string); |
| | 341 | } |
| | 342 | |
| | 343 | /** |
| | 344 | * Returns an object of the moxiecode json encoder/transcoder class. |
| | 345 | * |
| | 346 | * @link http://core.trac.wordpress.org/browser/trunk/wp-includes/js/tinymce/plugins/spellchecker/classes/utils/JSON.php Moxiecode JSON Class |
| | 347 | * @since 2.9.0 |
| | 348 | * @return Moxiecode_JSON JSON Encoder/Transcoder Object |
| | 349 | */ |
| | 350 | function &wp_spellcheck_json_object() { |
| | 351 | static $json = null; |
| | 352 | if ($json == null) { |
| | 353 | require_once( MCE_SPELLCHECKER_DIR . "/classes/utils/JSON.php" ); |
| | 354 | $json = new Moxiecode_JSON(); |
| | 355 | } |
| | 356 | return $json; |
| | 357 | } |
| | 358 | |
| | 359 | ?> |
| | 360 | No newline at end of file |