PluginProbe
Redirection / 2.1.29
Redirection v2.1.29
5.10.0 5.9.0 5.8.1 5.8.0 3.7.2 3.7.3 4.0 4.0.1 4.1 4.1.1 4.2 4.2.1 4.2.2 4.2.3 4.3 4.3.1 4.3.2 4.3.3 4.4 4.4.1 4.4.2 4.5 4.5.1 4.6.2 4.7.1 All 130 releases
redirection / plugin.php

plugin.php in Redirection 2.1.29, at plugin.php

625 lines 20.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 // ======================================================================================
4 // This library is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU Lesser General Public
6 // License as published by the Free Software Foundation; either
7 // version 2.1 of the License, or(at your option) any later version.
8 //
9 // This library is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 // Lesser General Public License for more details.
13 // ======================================================================================
14 // @author John Godley(http://urbangiraffe.com)
15 // @version 0.2.7
16 // @copyright Copyright &copy; 2009 John Godley, All Rights Reserved
17 // ======================================================================================
18 // 0.1.6 - Corrected WP locale functions
19 // 0.1.7 - Add phpdoc comments
20 // 0.1.8 - Support for Admin SSL
21 // 0.1.9 - URL encoding, defer localization until init
22 // 0.1.10 - Better URL encoding
23 // 0.1.11 - Make work in WP 2.0, fix HTTPS issue on IIS
24 // 0.1.12 - Activation/deactivation actions that take into account the directory
25 // 0.1.13 - Add realpath function
26 // 0.1.14 - Add select/checked functions, fix locale loader
27 // 0.1.15 - Remove dependency on prototype
28 // 0.1.16 - Add support for homedir in realpath
29 // 0.1.17 - Added widget class
30 // 0.1.18 - Expand checked function
31 // 0.1.19 - Make url() cope with sites with no trailing slash
32 // 0.1.20 - Change init function to prevent overloading
33 // 0.1.21 - Make widget work for WP 2.1
34 // 0.1.22 - Make select work with option groups, RSS compatability fix
35 // 0.1.23 - Make widget count work better, fix widgets in K2
36 // 0.1.24 - Make realpath better
37 // 0.1.25 - Support for new WP2.6 config location
38 // 0.1.26 - Add description to widget class
39 // 0.1.27 - Realpath on windows again
40 // 0.1.28 - Plugin version information
41 // 0.1.29 - Plugin version for older WP
42 // 0.1.30 - Add htmlspecialchars for non-support charsets
43 // 0.2 - WP Coding style
44 // 0.2.1 - Better HTTPS detection
45 // 0.2.2 - Plugin settings, base function
46 // 0.2.3 - More HTTPS
47 // 0.2.4 - Ajax helper, more compatability functions
48 // 0.2.5 - _n helper
49 // 0.2.6 - Compatability functions js_esc
50 // 0.2.7 - Allow multiple hooks in add_action/add_filter
51 // ======================================================================================
52
53
54 /**
55 * Wraps up several useful functions for WordPress plugins and provides a method to separate
56 * display HTML from PHP code.
57 *
58 * <h4>Display Rendering</h4>
59 * The class uses a similar technique to Ruby On Rails views, whereby the display HTML is kept
60 * in a separate directory and file from the main code. A display is 'rendered'(sent to the browser)
61 * or 'captured'(returned to the calling function).
62 *
63 * Template files are separated into two areas: admin and user. Admin templates are only for display in
64 * the WordPress admin interface, while user templates are typically for display on the site(although neither
65 * of these are enforced). All templates are PHP code, but are referred to without .php extension.
66 *
67 * The reason for this separation is that one golden rule of plugin creation is that someone will always want to change
68 * the formatting and style of your output. Rather than forcing them to modify the plugin(bad), or modify files within
69 * the plugin(equally bad), the class allows user templates to be overridden with files contained within the theme.
70 *
71 * An additional benefit is that it leads to code re-use, especially with regards to Ajax(i.e. your display code can be called from
72 * many locations)
73 *
74 * Template files are located within the 'view' subdirectory of the plugins base(specified when registering the plugin):
75 *
76 * <pre>myplugin/view/admin
77 * myplugin/view/myplugin</pre>
78 *
79 * Admin templates are contained within 'admin', and user templates are contained within a directory of the same name as the plugin.
80 *
81 * User files can be overridden within the theme by creating a similar directory structure:
82 *
83 * <pre>/themes/mytheme/view/myplugin</pre>
84 *
85 * The class will first look in the theme and then defaults to the plugin. A plugin should always provide default templates.
86 *
87 * <h4>Display Parameters</h4>
88 * Also similar to Ruby On Rails, when you display a template you must supply the parameters that the template has access to. This tries
89 * to ensure a very clean separation between code and display. Parameters are supplied as an associative array mapping variable name to variable value.
90 *
91 * For example,
92 *
93 * array( 'message' => 'Your data was processed', 'items' => 103);
94 *
95 * <h4>How it works in practice</h4>
96 * You create a template file to display how many items have been processed. You store this in 'view/admin/processed.php':
97 *
98 * <pre>&lt;p&gt;You processed &lt;?php echo $items ?&gt; items&lt;/p&gt;</pre>
99 *
100 * When you want to display this in your plugin you use:
101 *
102 * <pre> $this->render_admin( 'processed', array( 'items' => 100));</pre>
103 *
104 * @package WordPress base library
105 * @author John Godley
106 * @copyright Copyright(C) John Godley
107 **/
108
109 class Redirection_Plugin {
110 /**
111 * Plugin name
112 * @var string
113 **/
114 var $plugin_name;
115
116 /**
117 * Plugin 'view' directory
118 * @var string Directory
119 **/
120 var $plugin_base;
121
122 /**
123 * Version URL(if enabled)
124 * @var string URL
125 **/
126 var $version_url;
127
128 /**
129 * Register your plugin with a name and base directory. This <strong>must</strong> be called once.
130 *
131 * @param string $name Name of your plugin. Is used to determine the plugin locale domain
132 * @param string $base Directory containing the plugin's 'view' files.
133 * @return void
134 **/
135 function register_plugin( $name, $base ) {
136 $this->plugin_base = rtrim( dirname( $base ), '/' );
137 $this->plugin_name = $name;
138
139 $this->add_action( 'init', 'load_locale' );
140
141 global $wp_version;
142
143 if ( version_compare( $wp_version, '2.8', '<' ) )
144 $this->add_action( 'admin_menu', 'compatibility_28' );
145
146 if ( version_compare( $wp_version, '2.7', '<' ) )
147 $this->add_action( 'admin_menu', 'compatibility_27' );
148
149 if ( version_compare( $wp_version, '2.6', '<' ) )
150 $this->add_action( 'admin_menu', 'compatibility_26' );
151
152 if ( version_compare( $wp_version, '2.5', '<' ) ) {
153 $this->add_action( 'admin_menu', 'compatibility_25' );
154
155 if ( !function_exists( 'is_front_page' ) ) {
156 function is_front_page ( ) {
157 return is_home ();
158 }
159 }
160 }
161 }
162
163 function compatibility_28() {
164 if ( !function_exists( 'esc_js' ) ) {
165 function esc_js( $text ) {
166 $safe_text = wp_specialchars( $safe_text, ENT_COMPAT );
167 $safe_text = preg_replace( '/&#(x)?0*(?(1)27|39);?/i', "'", stripslashes( $safe_text ) );
168 $safe_text = preg_replace( "/\r?\n/", "\\n", addslashes( $safe_text ) );
169 return apply_filters( 'js_escape', $safe_text, $text );
170 }
171 }
172 }
173
174 function compatibility_25() {
175 if ( !function_exists( 'check_ajax_referer' ) ) {
176 function check_ajax_referer( $action = -1, $query_arg = false, $die = true ) {
177 if ( $query_arg )
178 $nonce = $_REQUEST[$query_arg];
179 else
180 $nonce = $_REQUEST['_ajax_nonce'] ? $_REQUEST['_ajax_nonce'] : $_REQUEST['_wpnonce'];
181
182 $result = wp_verify_nonce( $nonce, $action );
183
184 if ( $die && false == $result )
185 die('-1');
186
187 do_action('check_ajax_referer', $action, $result);
188
189 return $result;
190 }
191 }
192 }
193
194 function compatibility_26() {
195 if ( !function_exists( 'admin_url' ) ) {
196 function admin_url() {
197 $url = site_url('wp-admin/', 'admin');
198
199 if ( !empty($path) && is_string($path) && strpos($path, '..') === false )
200 $url .= ltrim($path, '/');
201
202 return $url;
203 }
204 }
205
206 if ( !function_exists( 'is_ssl' ) ) {
207 function is_ssl() {
208 if ( isset($_SERVER['HTTPS']) ) {
209 if ( 'on' == strtolower($_SERVER['HTTPS']) )
210 return true;
211 if ( '1' == $_SERVER['HTTPS'] )
212 return true;
213 } elseif ( isset($_SERVER['SERVER_PORT']) && ( '443' == $_SERVER['SERVER_PORT'] ) ) {
214 return true;
215 }
216 return false;
217 }
218 }
219
220 if ( !function_exists( 'site_url' ) ) {
221 function site_url($path = '', $scheme = null) {
222 $scheme = ( is_ssl() ? 'https' : 'http' );
223
224 $url = str_replace( 'http://', "{$scheme}://", get_option('siteurl') );
225
226 if ( !empty($path) && is_string($path) && strpos($path, '..') === false )
227 $url .= '/' . ltrim($path, '/');
228
229 return apply_filters('site_url', $url, $path, $orig_scheme);
230 }
231 }
232 }
233
234 /**
235 * Backwards compatible admin functions
236 * @return void
237 **/
238 function compatibility_27() {
239 if ( !function_exists( 'screen_icon' ) ) {
240 function screen_icon() {
241 }
242 }
243
244 if ( !function_exists( 'add_meta_box' ) ) {
245 function add_meta_box ( $id, $title, $callback, $page, $context = 'advanced', $priority = 'default', $callback_args=null ) {
246 add_action( 'dbx_post_advanced', $callback );
247 }
248 }
249 }
250
251 /**
252 * Hook called to change the locale directory
253 * @return void
254 **/
255 function load_locale() {
256 // Here we manually fudge the plugin locale as WP doesnt allow many options
257 $locale = get_locale();
258 if( empty( $locale ) )
259 $locale = 'en_US';
260
261 $mofile = dirname( __FILE__ )."/locale/$locale.mo";
262 load_textdomain( $this->plugin_name, $mofile );
263 }
264
265
266 /**
267 * Register a WordPress action and map it back to the calling object
268 *
269 * @param mixed $action Name of the action (single string or array of strings)
270 * @param string $function Function name (optional, if an array is given for $action then first $action is used as function name)
271 * @param int $priority WordPress priority(optional)
272 * @param int $accepted_args Number of arguments the function accepts(optional)
273 * @return void
274 **/
275 function add_action( $actions, $function = '', $priority = 10, $accepted_args = 1 ) {
276 if ( !is_array( $actions ) )
277 $actions = array( $actions );
278
279 foreach ( $actions AS $action ) {
280 add_action( $action, array( &$this, $function == '' ? $actions[0] : $function ), $priority, $accepted_args );
281 }
282 }
283
284
285 /**
286 * Register a WordPress filter and map it back to the calling object
287 *
288 * @param mixed $action Name of the action (single string or array of strings)
289 * @param string $function Function name (optional, if an array is given for $action then first $action is used as function name)
290 * @param int $priority WordPress priority(optional)
291 * @param int $accepted_args Number of arguments the function accepts(optional)
292 * @return void
293 **/
294 function add_filter( $filters, $function = '', $priority = 10, $accepted_args = 1 ) {
295 if ( !is_array( $filters ) )
296 $filters = array( $filters );
297
298 foreach ( $filters AS $filter ) {
299 add_filter( $filter, array( &$this, $function == '' ? $filters[0] : $function ), $priority, $accepted_args );
300 }
301 }
302
303
304 /**
305 * Special activation function that takes into account the plugin directory
306 *
307 * @param string $pluginfile The plugin file location(i.e. __FILE__)
308 * @param string $function Optional function name, or default to 'activate'
309 * @return void
310 **/
311 function register_activation( $pluginfile, $function = '' ) {
312 add_action( 'activate_'.basename( dirname( $pluginfile ) ).'/'.basename( $pluginfile ), array( &$this, $function == '' ? 'activate' : $function ) );
313 }
314
315 function register_ajax( $action, $function = '', $priority = 10 ) {
316 add_action( 'wp_ajax_'.$action, array( &$this, $function == '' ? $action : $function ), $priority );
317 }
318
319 /**
320 * Special deactivation function that takes into account the plugin directory
321 *
322 * @param string $pluginfile The plugin file location(i.e. __FILE__)
323 * @param string $function Optional function name, or default to 'deactivate'
324 * @return void
325 **/
326 function register_deactivation( $pluginfile, $function = '' ) {
327 add_action( 'deactivate_'.basename( dirname( $pluginfile ) ).'/'.basename( $pluginfile ), array( &$this, $function == '' ? 'deactivate' : $function ) );
328 }
329
330 function register_plugin_settings( $pluginfile, $function = '' ) {
331 add_action( 'plugin_action_links_'.basename( dirname( $pluginfile ) ).'/'.basename( $pluginfile ), array( &$this, $function == '' ? 'plugin_settings' : $function ), 10, 4 );
332 }
333
334 /**
335 * Renders an admin section of display code
336 *
337 * @param string $ug_name Name of the admin file(without extension)
338 * @param string $array Array of variable name=>value that is available to the display code(optional)
339 * @return void
340 **/
341 function render_admin( $ug_name, $ug_vars = array() ) {
342 global $plugin_base;
343
344 foreach ( $ug_vars AS $key => $val ) {
345 $$key = $val;
346 }
347
348 if ( file_exists( "{$this->plugin_base}/view/admin/$ug_name.php" ) )
349 include "{$this->plugin_base}/view/admin/$ug_name.php";
350 else
351 echo "<p>Rendering of admin template {$this->plugin_base}/view/admin/$ug_name.php failed</p>";
352 }
353
354 /**
355 * Renders a section of user display code. The code is first checked for in the current theme display directory
356 * before defaulting to the plugin
357 *
358 * @param string $ug_name Name of the admin file(without extension)
359 * @param string $array Array of variable name=>value that is available to the display code(optional)
360 * @return void
361 **/
362 function render( $ug_name, $ug_vars = array() ) {
363 foreach ( $ug_vars AS $key => $val ) {
364 $$key = $val;
365 }
366
367 if ( file_exists( TEMPLATEPATH."/view/{$this->plugin_name}/$ug_name.php" ) )
368 include TEMPLATEPATH."/view/{$this->plugin_name}/$ug_name.php";
369 elseif ( file_exists( "{$this->plugin_base}/view/{$this->plugin_name}/$ug_name.php" ) )
370 include "{$this->plugin_base}/view/{$this->plugin_name}/$ug_name.php";
371 else
372 echo "<p>Rendering of template $ug_name.php failed</p>";
373 }
374
375 /**
376 * Renders a section of user display code. The code is first checked for in the current theme display directory
377 * before defaulting to the plugin
378 *
379 * @param string $ug_name Name of the admin file(without extension)
380 * @param string $array Array of variable name=>value that is available to the display code(optional)
381 * @return void
382 **/
383 function capture( $ug_name, $ug_vars = array() ) {
384 ob_start();
385
386 $this->render( $ug_name, $ug_vars );
387 $output = ob_get_contents();
388
389 ob_end_clean();
390 return $output;
391 }
392
393 /**
394 * Captures an admin section of display code
395 *
396 * @param string $ug_name Name of the admin file(without extension)
397 * @param string $array Array of variable name=>value that is available to the display code(optional)
398 * @return string Captured code
399 **/
400 function capture_admin( $ug_name, $ug_vars = array() ) {
401 ob_start();
402
403 $this->render_admin( $ug_name, $ug_vars );
404 $output = ob_get_contents();
405
406 ob_end_clean();
407 return $output;
408 }
409
410 /**
411 * Display a standard error message(using CSS ID 'message' and classes 'fade' and 'error)
412 *
413 * @param string $message Message to display
414 * @return void
415 **/
416 function render_error( $message ) {
417 ?>
418 <div class="fade error" id="message">
419 <p><?php echo $message ?></p>
420 </div>
421 <?php
422 }
423
424 /**
425 * Display a standard notice(using CSS ID 'message' and class 'updated' ).
426 * Note that the notice can be made to automatically disappear, and can be removed
427 * by clicking on it.
428 *
429 * @param string $message Message to display
430 * @param int $timeout Number of seconds to automatically remove the message(optional)
431 * @return void
432 **/
433 function render_message( $message, $timeout = 0 ) {
434 ?>
435 <div class="updated" id="message" onclick="this.parentNode.removeChild(this)">
436 <p><?php echo $message ?></p>
437 </div>
438 <?php
439 }
440
441 /**
442 * Get the plugin's base directory
443 *
444 * @return string Base directory
445 **/
446 function dir() {
447 return $this->plugin_base;
448 }
449
450 function base () {
451 $parts = explode( '?', basename( $_SERVER['REQUEST_URI'] ) );
452 return $parts[0];
453 }
454
455 /**
456 * Get a URL to the plugin. Useful for specifying JS and CSS files
457 *
458 * For example, <img src="<?php echo $this->url() ?>/myimage.png"/>
459 *
460 * @return string URL
461 **/
462 function url( $url = '' ) {
463 if ( $url )
464 return str_replace( '\\', urlencode( '\\' ), str_replace( '&amp;amp', '&amp;', str_replace( '&', '&amp;', $url ) ) );
465
466 $root = ABSPATH;
467 if ( defined( 'WP_PLUGIN_DIR' ) )
468 $root = WP_PLUGIN_DIR;
469
470 $url = substr( $this->plugin_base, strlen( $this->realpath( $root ) ) );
471 if ( DIRECTORY_SEPARATOR != '/' )
472 $url = str_replace( DIRECTORY_SEPARATOR, '/', $url );
473
474 if ( defined( 'WP_PLUGIN_URL' ) )
475 $url = WP_PLUGIN_URL.'/'.ltrim( $url, '/' );
476 else
477 $url = get_bloginfo( 'wpurl' ).'/'.ltrim( $url, '/' );
478
479 // Do an SSL check - only works on Apache
480 global $is_IIS;
481 if ( isset( $_SERVER['HTTPS'] ) && strtolower( $_SERVER['HTTPS'] ) == 'on' && $is_IIS === false )
482 $url = str_replace( 'http://', 'https://', $url );
483
484 return $url;
485 }
486
487 /**
488 * Version of realpath that will work on systems without realpath
489 *
490 * @param string $path The path to canonicalize
491 * @return string Canonicalized path
492 **/
493 function realpath( $path ) {
494 if ( function_exists( 'realpath' ) && DIRECTORY_SEPARATOR == '/' )
495 return realpath( $path );
496 elseif ( DIRECTORY_SEPARATOR == '/' )
497 {
498 $path = preg_replace( '/^~/', $_SERVER['DOCUMENT_ROOT'], $path );
499
500 // canonicalize
501 $path = explode( DIRECTORY_SEPARATOR, $path );
502 $newpath = array();
503
504 for ( $i = 0; $i < count( $path ); $i++ ) {
505 if ( $path[$i] === '' || $path[$i] === '.' )
506 continue;
507
508 if ( $path[$i] === '..' ) {
509 array_pop( $newpath );
510 continue;
511 }
512
513 array_push( $newpath, $path[$i] );
514 }
515
516 return DIRECTORY_SEPARATOR.implode( DIRECTORY_SEPARATOR, $newpath );
517 }
518
519 return $path;
520 }
521
522 /**
523 * Helper function to check a checkbox if the item has been checked
524 *
525 * @param mixed $item Checkbox value, or array of checkbox values: field => value
526 * @param string $field Fieldname, if array is given for $item
527 * @return void
528 **/
529 function checked( $item, $field = '' ) {
530 if ( $field && is_array( $item ) ) {
531 if ( isset( $item[$field] ) && $item[$field] )
532 echo ' checked="checked"';
533 }
534 elseif ( !empty( $item ) )
535 echo ' checked="checked"';
536 }
537
538 /**
539 * Helper function to display a dropdown select box
540 *
541 * @param array $items Associative array of: fieldname => label
542 * @param string $default Default fieldname to select
543 * @return void
544 **/
545 function select( $items, $default = '' ) {
546 if ( count( $items ) > 0 ) {
547 foreach ( $items AS $key => $value ) {
548 if ( is_array( $value ) ) {
549 echo '<optgroup label="'.$key.'">';
550
551 foreach ( $value AS $sub => $subvalue ) {
552 echo '<option value="'.$sub.'"'.( $sub == $default ? ' selected="selected"' : '' ).'>'.$subvalue.'</option>';
553 }
554
555 echo '</optgroup>';
556 }
557 else
558 echo '<option value="'.$key.'"'.( $key == $default ? ' selected="selected"' : '' ).'>'.$value.'</option>';
559 }
560 }
561 }
562
563 /**
564 * Expanded version of htmlspecialchars which detects the blog encoding and runs iconv on any encoding that is not supported by htmlspecialchars
565 *
566 * @param string $text Text to run htmlspecialchars on
567 * @return void
568 **/
569 function specialchars( $text ) {
570 $charset = get_option( 'blog_charset' );
571
572 if ( $charset != 'UTF-8' && function_exists( 'iconv' ) && !in_array( $charset, array( 'ISO-8859-1', 'ISO-8859-15', 'cp1251', 'cp1252', 'KOI8-R', 'BIG5', 'GB2312', 'Shift_JIS', 'EUC-JP' ) ) )
573 return iconv( 'UTF-8//IGNORE', $charset, htmlspecialchars( iconv( $charset, 'UTF-8//IGNORE', $text ) ) );
574 return htmlspecialchars( $text, ENT_COMPAT, $charset );
575 }
576
577 /**
578 * Special version of strlen that runs mb_strlen if blog encoding is not UTF-8
579 *
580 * @param string $name Name of your plugin. Is used to determine the plugin locale domain
581 * @param string $base Directory containing the plugin's 'view' files.
582 * @return void
583 **/
584 function strlen( $text ) {
585 $charset = get_option( 'blog_charset' );
586
587 if ( $charset != 'UTF-8' && function_exists( 'mb_strlen' ) )
588 return mb_strlen( $text );
589 return strlen( $text );
590 }
591
592 /**
593 * Returns version of plugin
594 *
595 * @return string Version
596 **/
597 function version() {
598 $plugin_data = implode( '', file( $this->plugin_base ) );
599
600 if ( preg_match( '|Version:(.*)|i', $plugin_data, $version ) )
601 return trim( $version[1] );
602 return '';
603 }
604 }
605
606 /**
607 * Debug helper, borrowed from CakePHP, that displays a print_r inside <pre></pre> tags
608 *
609 * @param string $name Name of your plugin. Is used to determine the plugin locale domain
610 * @param string $base Directory containing the plugin's 'view' files.
611 * @return void
612 **/
613 if ( !function_exists( 'pr' ) ) {
614 function pr( $thing ) {
615 echo '<pre>';
616 print_r( $thing );
617 echo '</pre>';
618 }
619 }
620
621 if ( !function_exists( '_n' ) ) {
622 function _n($single, $plural, $number, $domain = 'default') {
623 return __ngettext($single, $plural, $number, $domain = 'default');
624 }
625 }