-
Notifications
You must be signed in to change notification settings - Fork 5
/
plugin.php
525 lines (449 loc) · 16.1 KB
/
plugin.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
<?php
// ======================================================================================
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
// ======================================================================================
// @author John Godley (http://urbangiraffe.com)
// @version 0.1.25
// @copyright Copyright © 2007 John Godley, All Rights Reserved
// ======================================================================================
// 0.1.6 - Corrected WP locale functions
// 0.1.7 - Add phpdoc comments
// 0.1.8 - Support for Admin SSL
// 0.1.9 - URL encoding, defer localization until init
// 0.1.10 - Better URL encoding
// 0.1.11 - Make work in WP 2.0, fix HTTPS issue on IIS
// 0.1.12 - Activation/deactivation actions that take into account the directory
// 0.1.13 - Add realpath function
// 0.1.14 - Add select/checked functions, fix locale loader
// 0.1.15 - Remove dependency on prototype
// 0.1.16 - Add support for homedir in realpath
// 0.1.17 - Added widget class
// 0.1.18 - Expand checked function
// 0.1.19 - Make url() cope with sites with no trailing slash
// 0.1.20 - Change init function to prevent overloading
// 0.1.21 - Make widget work for WP 2.1
// 0.1.22 - Make select work with option groups, RSS compatability fix
// 0.1.23 - Make widget count work better, fix widgets in K2
// 0.1.24 - Make realpath better
// 0.1.25 - Support for new WP2.6 config location
// ======================================================================================
/**
* Wraps up several useful functions for WordPress plugins and provides a method to separate
* display HTML from PHP code.
*
* <h4>Display Rendering</h4>
* The class uses a similar technique to Ruby On Rails views, whereby the display HTML is kept
* in a separate directory and file from the main code. A display is 'rendered' (sent to the browser)
* or 'captured' (returned to the calling function).
*
* Template files are separated into two areas: admin and user. Admin templates are only for display in
* the WordPress admin interface, while user templates are typically for display on the site (although neither
* of these are enforced). All templates are PHP code, but are referred to without .php extension.
*
* The reason for this separation is that one golden rule of plugin creation is that someone will always want to change
* the formatting and style of your output. Rather than forcing them to modify the plugin (bad), or modify files within
* the plugin (equally bad), the class allows user templates to be overridden with files contained within the theme.
*
* 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
* many locations)
*
* Template files are located within the 'view' subdirectory of the plugins base (specified when registering the plugin):
*
* <pre>myplugin/view/admin
* myplugin/view/myplugin</pre>
*
* Admin templates are contained within 'admin', and user templates are contained within a directory of the same name as the plugin.
*
* User files can be overridden within the theme by creating a similar directory structure:
*
* <pre>/themes/mytheme/view/myplugin</pre>
*
* The class will first look in the theme and then defaults to the plugin. A plugin should always provide default templates.
*
* <h4>Display Parameters</h4>
* Also similar to Ruby On Rails, when you display a template you must supply the parameters that the template has access to. This tries
* to ensure a very clean separation between code and display. Parameters are supplied as an associative array mapping variable name to variable value.
*
* For example,
*
* array ('message' => 'Your data was processed', 'items' => 103);
*
* <h4>How it works in practice</h4>
* You create a template file to display how many items have been processed. You store this in 'view/admin/processed.php':
*
* <pre><p>You processed <?php echo $items ?> items</p></pre>
*
* When you want to display this in your plugin you use:
*
* <pre> $this->render_admin ('processed', array ('items' => 100));
*
* @package WordPress base library
* @author John Godley
* @copyright Copyright (C) John Godley
**/
class TwitterTracker_Plugin
{
/**
* Plugin name
* @var string
**/
var $plugin_name;
/**
* Plugin 'view' directory
* @var string Directory
**/
var $plugin_base;
/**
* Register your plugin with a name and base directory. This <strong>must</strong> be called once.
*
* @param string $name Name of your plugin. Is used to determine the plugin locale domain
* @param string $base Directory containing the plugin's 'view' files.
* @return void
**/
function register_plugin ($name, $base)
{
$this->plugin_base = rtrim (dirname ($base), '/');
$this->plugin_name = $name;
$this->add_action ('init', 'load_locale');
}
function load_locale ()
{
// Here we manually fudge the plugin locale as WP doesnt allow many options
$locale = get_locale ();
if ( empty($locale) )
$locale = 'en_US';
$mofile = dirname (__FILE__)."/locale/$locale.mo";
load_textdomain ($this->plugin_name, $mofile);
}
/**
* Register a WordPress action and map it back to the calling object
*
* @param string $action Name of the action
* @param string $function Function name (optional)
* @param int $priority WordPress priority (optional)
* @param int $accepted_args Number of arguments the function accepts (optional)
* @return void
**/
function add_action ($action, $function = '', $priority = 10, $accepted_args = 1)
{
add_action ($action, array (&$this, $function == '' ? $action : $function), $priority, $accepted_args);
}
/**
* Register a WordPress filter and map it back to the calling object
*
* @param string $action Name of the action
* @param string $function Function name (optional)
* @param int $priority WordPress priority (optional)
* @param int $accepted_args Number of arguments the function accepts (optional)
* @return void
**/
function add_filter ($filter, $function = '', $priority = 10, $accepted_args = 1)
{
add_filter ($filter, array (&$this, $function == '' ? $filter : $function), $priority, $accepted_args);
}
function add_dashboard_widget( $widget_id, $widget_name, $callback = '', $control_callback = null )
{
if ( ! function_exists( "wp_add_dashboard_widget" ) ) throw new exception( "This function must be called within the wp_dashboard_setup action." );
wp_add_dashboard_widget( $widget_id, $widget_name, array ( & $this, $function == '' ? $widget_id : $callback ), $control_callback );
}
/**
* Register a WordPress meta box
*
* @param string $id ID for the box, also used as a function name if none is given
* @param string $title Title for the box
* @param int $page WordPress priority (optional)
* @param string $function Function name (optional)
* @param string $context e.g. 'advanced' or 'core' (optional)
* @param int $priority Priority, rough effect on the ordering (optional)
* @return void
**/
function add_meta_box($id, $title, $function = '', $page, $context = 'advanced', $priority = 'default')
{
require_once( ABSPATH . 'wp-admin/includes/template.php' );
add_meta_box( $id, $title, array( &$this, $function == '' ? $id : $function ), $page, $context, $priority );
}
/**
* Special activation function that takes into account the plugin directory
*
* @param string $pluginfile The plugin file location (i.e. __FILE__)
* @param string $function Optional function name, or default to 'activate'
* @return void
**/
function register_activation ($pluginfile, $function = '')
{
add_action ('activate_'.basename (dirname ($pluginfile)).'/'.basename ($pluginfile), array (&$this, $function == '' ? 'activate' : $function));
}
/**
* Special deactivation function that takes into account the plugin directory
*
* @param string $pluginfile The plugin file location (i.e. __FILE__)
* @param string $function Optional function name, or default to 'deactivate'
* @return void
**/
function register_deactivation ($pluginfile, $function = '')
{
add_action ('deactivate_'.basename (dirname ($pluginfile)).'/'.basename ($pluginfile), array (&$this, $function == '' ? 'deactivate' : $function));
}
/**
* Renders an admin section of display code
*
* @param string $ug_name Name of the admin file (without extension)
* @param string $array Array of variable name=>value that is available to the display code (optional)
* @return void
**/
function render_admin ($ug_name, $ug_vars = array ())
{
global $plugin_base;
foreach ($ug_vars AS $key => $val)
$$key = $val;
if (file_exists ("{$this->plugin_base}/view/admin/$ug_name.php"))
include ("{$this->plugin_base}/view/admin/$ug_name.php");
else
echo "<p>Rendering of admin template {$this->plugin_base}/view/admin/$ug_name.php failed</p>";
}
/**
* Renders a section of user display code. The code is first checked for in the current theme display directory
* before defaulting to the plugin
*
* @param string $ug_name Name of the admin file (without extension)
* @param string $array Array of variable name=>value that is available to the display code (optional)
* @return void
**/
function render ($ug_name, $ug_vars = array ())
{
foreach ($ug_vars AS $key => $val)
$$key = $val;
if (file_exists (TEMPLATEPATH."/view/{$this->plugin_name}/$ug_name.php"))
include (TEMPLATEPATH."/view/{$this->plugin_name}/$ug_name.php");
else if (file_exists ("{$this->plugin_base}/view/{$this->plugin_name}/$ug_name.php"))
include ("{$this->plugin_base}/view/{$this->plugin_name}/$ug_name.php");
else
echo "<p>Rendering of template $ug_name.php failed</p>";
}
/**
* Renders a section of user display code. The code is first checked for in the current theme display directory
* before defaulting to the plugin
*
* @param string $ug_name Name of the admin file (without extension)
* @param string $array Array of variable name=>value that is available to the display code (optional)
* @return void
**/
function capture ($ug_name, $ug_vars = array ())
{
ob_start ();
$this->render ($ug_name, $ug_vars);
$output = ob_get_contents ();
ob_end_clean ();
return $output;
}
/**
* Captures an admin section of display code
*
* @param string $ug_name Name of the admin file (without extension)
* @param string $array Array of variable name=>value that is available to the display code (optional)
* @return string Captured code
**/
function capture_admin ($ug_name, $ug_vars = array ())
{
ob_start ();
$this->render_admin ($ug_name, $ug_vars);
$output = ob_get_contents ();
ob_end_clean ();
return $output;
}
/**
* Display a standard error message (using CSS ID 'message' and classes 'fade' and 'error)
*
* @param string $message Message to display
* @return void
**/
function render_error ($message)
{
?>
<div class="fade error" id="message">
<p><?php echo $message ?></p>
</div>
<?php
}
/**
* Display a standard notice (using CSS ID 'message' and class 'updated').
* Note that the notice can be made to automatically disappear, and can be removed
* by clicking on it.
*
* @param string $message Message to display
* @param int $timeout Number of seconds to automatically remove the message (optional)
* @return void
**/
function render_message ($message, $timeout = 0)
{
?>
<div class="updated" id="message" onclick="this.parentNode.removeChild (this)">
<p><?php echo $message ?></p>
</div>
<?php
}
/**
* Get the plugin's base directory
*
* @return string Base directory
**/
function dir ()
{
return $this->plugin_base;
}
/**
* Get a URL to the plugin. Useful for specifying JS and CSS files
*
* For example, <img src="<?php echo $this->url () ?>/myimage.png"/>
*
* @return string URL
**/
function url ($url = '')
{
if ($url)
return str_replace ('\\', urlencode ('\\'), str_replace ('&amp', '&', str_replace ('&', '&', $url)));
else
{
$root = ABSPATH;
if (defined ('WP_PLUGIN_DIR'))
$root = WP_PLUGIN_DIR;
$url = substr ($this->plugin_base, strlen ($this->realpath ($root)));
if (DIRECTORY_SEPARATOR != '/')
$url = str_replace (DIRECTORY_SEPARATOR, '/', $url);
if (defined ('WP_PLUGIN_URL'))
$url = WP_PLUGIN_URL.'/'.ltrim ($url, '/');
else
$url = get_bloginfo ('wpurl').'/'.ltrim ($url, '/');
// Do an SSL check - only works on Apache
global $is_IIS;
if (isset ($_SERVER['HTTPS']) && !$is_IIS)
$url = str_replace ('http://', 'https://', $url);
}
return $url;
}
/**
* Get the filemtime for a path within this plugin
*
* @param string $path A path within this plugin directory
* @return int The filemtime
**/
function filemtime( $path )
{
if ( ! file_exists( $this->plugin_base . "/$path" ) )
return null;
return filemtime( $this->plugin_base . "/$path" );
}
/**
* Performs a version update check using an RSS feed. The function ensures that the feed is only
* hit once every given number of days, and the data is cached using the WordPress Magpie library
*
* @param string $url URL of the RSS feed
* @param int $days Number of days before next check
* @return string Text to display
**/
function version_update ($url, $days = 7)
{
if (!function_exists ('fetch_rss'))
{
if (!file_exists (ABSPATH.'wp-includes/rss.php'))
return '';
include (ABSPATH.'wp-includes/rss.php');
}
$now = time ();
$checked = get_option ('plugin_urbangiraffe_rss');
// Use built-in Magpie caching
if (function_exists ('fetch_rss') && (!isset ($checked[$this->plugin_name]) || $now > $checked[$this->plugin_name] + ($days * 24 * 60 * 60)))
{
$rss = fetch_rss ($url);
if (count ($rss->items) > 0)
{
foreach ($rss->items AS $pos => $item)
{
if (isset ($checked[$this->plugin_name]) && strtotime ($item['pubdate']) < $checked[$this->plugin_name])
unset ($rss->items[$pos]);
}
}
$checked[$this->plugin_name] = $now;
update_option ('plugin_urbangiraffe_rss', $checked);
return $rss;
}
}
/**
* Version of realpath that will work on systems without realpath
*
* @param string $path The path to canonicalize
* @return string Canonicalized path
**/
function realpath ($path)
{
if (function_exists ('realpath'))
return realpath ($path);
else if (DIRECTORY_SEPARATOR == '/')
{
$path = preg_replace ('/^~/', $_SERVER['DOCUMENT_ROOT'], $path);
// canonicalize
$path = explode (DIRECTORY_SEPARATOR, $path);
$newpath = array ();
for ($i = 0; $i < sizeof ($path); $i++)
{
if ($path[$i] === '' || $path[$i] === '.')
continue;
if ($path[$i] === '..')
{
array_pop ($newpath);
continue;
}
array_push ($newpath, $path[$i]);
}
$finalpath = DIRECTORY_SEPARATOR.implode (DIRECTORY_SEPARATOR, $newpath);
return $finalpath;
}
return $path;
}
function checked ($item, $field = '')
{
if ($field && is_array ($item))
{
if (isset ($item[$field]) && $item[$field])
echo ' checked="checked"';
}
else if (!empty ($item))
echo ' checked="checked"';
}
function select ($items, $default = '')
{
if (count ($items) > 0)
{
foreach ($items AS $key => $value)
{
if (is_array ($value))
{
echo '<optgroup label="'.$key.'">';
foreach ($value AS $sub => $subvalue)
echo '<option value="'.$sub.'"'.($sub == $default ? ' selected="selected"' : '').'>'.$subvalue.'</option>';
echo '</optgroup>';
}
else
echo '<option value="'.$key.'"'.($key == $default ? ' selected="selected"' : '').'>'.$value.'</option>';
}
}
}
}
if (!function_exists ('pr'))
{
function pr ($thing)
{
echo '<pre>';
print_r ($thing);
echo '</pre>';
}
}
?>