PluginProbe
FeedWordPress / 2011.1018
FeedWordPress v2011.1018
trunk 0.8 0.9 0.91 0.95 0.96 0.97 0.98 0.981 0.99 0.991 0.992 0.993 2008.1030 2008.1101 2008.1105 2008.1214 2009.0612 2009.0613 2009.0618 2009.0707 2009.1111 2009.1112 2010.0127 2010.0528 All 65 releases
feedwordpress / feedwordpress.php

feedwordpress.php in FeedWordPress 2011.1018, at feedwordpress.php

1,993 lines 66.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /*
3 Plugin Name: FeedWordPress
4 Plugin URI: http://feedwordpress.radgeek.com/
5 Description: simple and flexible Atom/RSS syndication for WordPress
6 Version: 2011.1018
7 Author: Charles Johnson
8 Author URI: http://radgeek.com/
9 License: GPL
10 */
11
12 /**
13 * @package FeedWordPress
14 * @version 2011.1018
15 */
16
17 # This uses code derived from:
18 # - wp-rss-aggregate.php by Kellan Elliot-McCrea <kellan@protest.net>
19 # - MagpieRSS by Kellan Elliot-McCrea <kellan@protest.net>
20 # - Ultra-Liberal Feed Finder by Mark Pilgrim <mark@diveintomark.org>
21 # - WordPress Blog Tool and Publishing Platform <http://wordpress.org/>
22 # according to the terms of the GNU General Public License.
23 #
24 # INSTALLATION: see readme.txt or <http://projects.radgeek.com/install>
25 #
26 # USAGE: once FeedWordPress is installed, you manage just about everything from
27 # the WordPress Dashboard, under the Syndication menu. To ensure that fresh
28 # content is added as it becomes available, you can convince your contributors
29 # to put your XML-RPC URI (if WordPress is installed at
30 # <http://www.zyx.com/blog>, XML-RPC requests should be sent to
31 # <http://www.zyx.com/blog/xmlrpc.php>), or update manually under the
32 # Syndication menu, or set up automatic updates under Syndication --> Settings,
33 # or use a cron job.
34
35 # -- Don't change these unless you know what you're doing...
36
37 define ('FEEDWORDPRESS_VERSION', '2011.1018');
38 define ('FEEDWORDPRESS_AUTHOR_CONTACT', 'http://radgeek.com/contact');
39
40 if (!defined('FEEDWORDPRESS_BLEG')) :
41 define ('FEEDWORDPRESS_BLEG', true);
42 endif;
43
44 // Defaults
45 define ('DEFAULT_SYNDICATION_CATEGORY', 'Contributors');
46 define ('DEFAULT_UPDATE_PERIOD', 60); // value in minutes
47
48 if (isset($_REQUEST['feedwordpress_debug'])) :
49 $feedwordpress_debug = $_REQUEST['feedwordpress_debug'];
50 else :
51 $feedwordpress_debug = get_option('feedwordpress_debug');
52 if (is_string($feedwordpress_debug)) :
53 $feedwordpress_debug = ($feedwordpress_debug == 'yes');
54 endif;
55 endif;
56 define ('FEEDWORDPRESS_DEBUG', $feedwordpress_debug);
57
58 define ('FEEDWORDPRESS_CAT_SEPARATOR_PATTERN', '/[:\n]/');
59 define ('FEEDWORDPRESS_CAT_SEPARATOR', "\n");
60
61 define ('FEEDVALIDATOR_URI', 'http://feedvalidator.org/check.cgi');
62
63 define ('FEEDWORDPRESS_FRESHNESS_INTERVAL', 10*60); // Every ten minutes
64
65 define ('FWP_SCHEMA_HAS_USERMETA', 2966);
66 define ('FWP_SCHEMA_USES_ARGS_TAXONOMY', 12694); // Revision # for using $args['taxonomy'] to get link categories
67 define ('FWP_SCHEMA_20', 3308); // Database schema # for WP 2.0
68 define ('FWP_SCHEMA_21', 4772); // Database schema # for WP 2.1
69 define ('FWP_SCHEMA_23', 5495); // Database schema # for WP 2.3
70 define ('FWP_SCHEMA_25', 7558); // Database schema # for WP 2.5
71 define ('FWP_SCHEMA_26', 8201); // Database schema # for WP 2.6
72 define ('FWP_SCHEMA_27', 9872); // Database schema # for WP 2.7
73 define ('FWP_SCHEMA_28', 11548); // Database schema # for WP 2.8
74 define ('FWP_SCHEMA_29', 12329); // Database schema # for WP 2.9
75 define ('FWP_SCHEMA_30', 12694); // Database schema # for WP 3.0
76
77 if (FEEDWORDPRESS_DEBUG) :
78 // Help us to pick out errors, if any.
79 ini_set('error_reporting', E_ALL & ~E_NOTICE);
80 ini_set('display_errors', true);
81
82 // When testing we don't want cache issues to interfere. But this is
83 // a VERY BAD SETTING for a production server. Webmasters will eat your
84 // face for breakfast if you use it, and the baby Jesus will cry. So
85 // make sure FEEDWORDPRESS_DEBUG is FALSE for any site that will be
86 // used for more than testing purposes!
87 define('FEEDWORDPRESS_CACHE_AGE', 1);
88 define('FEEDWORDPRESS_CACHE_LIFETIME', 1);
89 define('FEEDWORDPRESS_FETCH_TIMEOUT_DEFAULT', 60);
90 else :
91 // Hold onto data all day for conditional GET purposes,
92 // but consider it stale after 1 min (requiring a conditional GET)
93 define('FEEDWORDPRESS_CACHE_LIFETIME', 24*60*60);
94 define('FEEDWORDPRESS_CACHE_AGE', 1*60);
95 define('FEEDWORDPRESS_FETCH_TIMEOUT_DEFAULT', 20);
96 endif;
97
98 // Use our the cache settings that we want.
99 add_filter('wp_feed_cache_transient_lifetime', array('FeedWordPress', 'cache_lifetime'));
100
101 // Ensure that we have SimplePie loaded up and ready to go.
102 // We no longer need a MagpieRSS upgrade module. Hallelujah!
103 if (!class_exists('SimplePie')) :
104 require_once(ABSPATH . WPINC . '/class-simplepie.php');
105 endif;
106 require_once(ABSPATH . WPINC . '/class-feed.php');
107
108 if (!function_exists('wp_insert_user')) :
109 require_once (ABSPATH . WPINC . '/registration.php'); // for wp_insert_user
110 endif;
111
112 $dir = dirname(__FILE__);
113 require_once("${dir}/admin-ui.php");
114 require_once("${dir}/feedwordpresssyndicationpage.class.php");
115 require_once("${dir}/compatability.php"); // Legacy API
116 require_once("${dir}/syndicatedpost.class.php");
117 require_once("${dir}/syndicatedlink.class.php");
118 require_once("${dir}/feedwordpresshtml.class.php");
119 require_once("${dir}/feedwordpress-content-type-sniffer.class.php");
120 require_once("${dir}/inspectpostmeta.class.php");
121 require_once("${dir}/syndicationdataqueries.class.php");
122 require_once("${dir}/feedwordpress_file.class.php");
123 require_once("${dir}/feedwordpress_parser.class.php");
124 require_once("${dir}/feedwordpressrpc.class.php");
125 require_once("${dir}/feedwordpresshttpauthenticator.class.php");
126
127 // Magic quotes are just about the stupidest thing ever.
128 if (is_array($_POST)) :
129 $fwp_post = $_POST;
130 if (get_magic_quotes_gpc()) :
131 $fwp_post = stripslashes_deep($fwp_post);
132 endif;
133 endif;
134
135 // Get the path relative to the plugins directory in which FWP is stored
136 preg_match (
137 '|'.preg_quote(WP_PLUGIN_DIR).'/(.+)$|',
138 dirname(__FILE__),
139 $ref
140 );
141
142 if (isset($ref[1])) :
143 $fwp_path = $ref[1];
144 else : // Something went wrong. Let's just guess.
145 $fwp_path = 'feedwordpress';
146 endif;
147
148 if (!FeedWordPress::needs_upgrade()) : // only work if the conditions are safe!
149
150 # Syndicated items are generally received in output-ready (X)HTML and
151 # should not be folded, crumpled, mutilated, or spindled by WordPress
152 # formatting filters. But we don't want to interfere with filters for
153 # any locally-authored posts, either.
154 #
155 # What WordPress should really have is a way for upstream filters to
156 # stop downstream filters from running at all. Since it doesn't, and
157 # since a downstream filter can't access the original copy of the text
158 # that is being filtered, what we will do here is (1) save a copy of the
159 # original text upstream, before any other filters run, and then (2)
160 # retrieve that copy downstream, after all the other filters run, *if*
161 # this is a syndicated post
162
163 add_filter('the_content', 'feedwordpress_preserve_syndicated_content', -10000);
164 add_filter('the_content', 'feedwordpress_restore_syndicated_content', 10000);
165
166 add_action('atom_entry', 'feedwordpress_item_feed_data');
167
168 # Filter in original permalinks if the user wants that
169 add_filter('post_link', 'syndication_permalink', /*priority=*/ 1, /*arguments=*/ 3);
170
171 # When foreign URLs are used for permalinks in feeds or display
172 # contexts, they need to be escaped properly.
173 add_filter('the_permalink', 'syndication_permalink_escaped');
174 add_filter('the_permalink_rss', 'syndication_permalink_escaped');
175
176 add_filter('post_comments_feed_link', 'syndication_comments_feed_link');
177
178 # WTF? By default, wp_insert_link runs incoming link_url and link_rss
179 # URIs through default filters that include `wp_kses()`. But `wp_kses()`
180 # just happens to escape any occurrence of & to &amp; -- which just
181 # happens to fuck up any URI with a & to separate GET parameters.
182 remove_filter('pre_link_rss', 'wp_filter_kses');
183 remove_filter('pre_link_url', 'wp_filter_kses');
184
185 # Admin menu
186 add_action('admin_menu', 'fwp_add_pages');
187 add_action('admin_notices', 'fwp_check_debug');
188
189 add_action('admin_menu', 'feedwordpress_add_post_edit_controls');
190 add_action('save_post', 'feedwordpress_save_post_edit_controls');
191
192 add_action('admin_footer', array('FeedWordPress', 'admin_footer'));
193
194 # Inbound XML-RPC update methods
195 $feedwordpressRPC = new FeedWordPressRPC;
196
197 # Outbound XML-RPC ping reform
198 remove_action('publish_post', 'generic_ping'); // WP 1.5.x
199 remove_action('do_pings', 'do_all_pings', 10, 1); // WP 2.1, 2.2
200 remove_action('publish_post', '_publish_post_hook', 5, 1); // WP 2.3
201
202 add_action('publish_post', 'fwp_publish_post_hook', 5, 1);
203 add_action('do_pings', 'fwp_do_pings', 10, 1);
204 add_action('feedwordpress_update', 'fwp_hold_pings');
205 add_action('feedwordpress_update_complete', 'fwp_release_pings');
206
207 add_action('syndicated_feed_error', array('FeedWordPressDiagnostic', 'feed_error'), 100, 3);
208
209 add_action('wp_footer', 'debug_out_feedwordpress_footer', -100);
210 add_action('admin_footer', 'debug_out_feedwordpress_footer', -100);
211
212 $feedwordpress = new FeedWordPress;
213
214 # Cron-less auto-update. Hooray!
215 $autoUpdateHook = $feedwordpress->automatic_update_hook();
216 if (!is_null($autoUpdateHook)) :
217 add_action($autoUpdateHook, array(&$feedwordpress, 'auto_update'));
218 endif;
219
220 add_action('init', array(&$feedwordpress, 'init'));
221 add_action('shutdown', array(&$feedwordpress, 'email_diagnostic_log'));
222 add_action('wp_dashboard_setup', array(&$feedwordpress, 'dashboard_setup'));
223
224 # Default sanitizers
225 add_filter('syndicated_item_content', array('SyndicatedPost', 'resolve_relative_uris'), 0, 2);
226 add_filter('syndicated_item_content', array('SyndicatedPost', 'sanitize_content'), 0, 2);
227
228 else :
229 # Hook in the menus, which will just point to the upgrade interface
230 add_action('admin_menu', 'fwp_add_pages');
231 endif; // if (!FeedWordPress::needs_upgrade())
232
233 ################################################################################
234 ## LOGGING FUNCTIONS: log status updates to error_log if you want it ###########
235 ################################################################################
236
237 class FeedWordPressDiagnostic {
238 function feed_error ($error, $old, $link) {
239 $wpError = $error['object'];
240 $url = $link->uri();
241
242 $mesgs = $wpError->get_error_messages();
243 foreach ($mesgs as $mesg) :
244 $mesg = esc_html($mesg);
245 FeedWordPress::diagnostic(
246 'updated_feeds:errors',
247 "Feed Error: [${url}] update returned error: $mesg"
248 );
249
250 $hours = get_option('feedwordpress_diagnostics_persistent_errors_hours', 2);
251 $span = ($error['ts'] - $error['since']);
252
253 if ($span >= ($hours * 60 * 60)) :
254 $since = date('r', $error['since']);
255 $mostRecent = date('r', $error['ts']);
256 FeedWordPress::diagnostic(
257 'updated_feeds:errors:persistent',
258 "Feed Update Error: [${url}] returning errors"
259 ." since ${since}:<br/><code>$mesg</code>",
260 $url, $error['since'], $error['ts']
261 );
262 endif;
263 endforeach;
264 }
265
266 function admin_emails ($id = '') {
267 $users = get_users_of_blog($id);
268 $recipients = array();
269 foreach ($users as $user) :
270 $user_id = (isset($user->user_id) ? $user->user_id : $user->ID);
271 $dude = new WP_User($user_id);
272 if ($dude->has_cap('administrator')) :
273 if ($dude->user_email) :
274 $recipients[] = $dude->user_email;
275 endif;
276 endif;
277 endforeach;
278 return $recipients;
279 }
280 } /* class FeedWordPressDiagnostic */
281
282 function debug_out_human_readable_bytes ($quantity) {
283 $quantity = (int) $quantity;
284 $magnitude = 'B';
285 $orders = array('KB', 'MB', 'GB', 'TB');
286 while (($quantity > (1024*100)) and (count($orders) > 0)) :
287 $quantity = floor($quantity / 1024);
288 $magnitude = array_shift($orders);
289 endwhile;
290 return "${quantity} ${magnitude}";
291 }
292
293 function debug_out_feedwordpress_footer () {
294 if (FeedWordPress::diagnostic_on('memory_usage')) :
295 if (function_exists('memory_get_usage')) :
296 FeedWordPress::diagnostic ('memory_usage', "Memory: Current usage: ".debug_out_human_readable_bytes(memory_get_usage()));
297 endif;
298 if (function_exists('memory_get_peak_usage')) :
299 FeedWordPress::diagnostic ('memory_usage', "Memory: Peak usage: ".debug_out_human_readable_bytes(memory_get_peak_usage()));
300 endif;
301 endif;
302 } /* debug_out_feedwordpress_footer() */
303
304 ################################################################################
305 ## TEMPLATE API: functions to make your templates syndication-aware ############
306 ################################################################################
307
308 /**
309 * is_syndicated: Tests whether the current post in a Loop context, or a post
310 * given by ID number, was syndicated by FeedWordPress. Useful for templates
311 * to determine whether or not to retrieve syndication-related meta-data in
312 * displaying a post.
313 *
314 * @param int $id The post to check for syndicated status. Defaults to the current post in a Loop context.
315 * @return bool TRUE if the post's meta-data indicates it was syndicated; FALSE otherwise
316 */
317 function is_syndicated ($id = NULL) {
318 return (strlen(get_syndication_feed_id($id)) > 0);
319 } /* function is_syndicated() */
320
321 function feedwordpress_display_url ($url, $before = 60, $after = 0) {
322 $bits = parse_url($url);
323
324 // Strip out crufty subdomains
325 if (isset($bits['host'])) :
326 $bits['host'] = preg_replace('/^www[0-9]*\./i', '', $bits['host']);
327 endif;
328
329 // Reassemble bit-by-bit with minimum of crufty elements
330 $url = (isset($bits['user'])?$bits['user'].'@':'')
331 .(isset($bits['host'])?$bits['host']:'')
332 .(isset($bits['path'])?$bits['path']:'')
333 .(isset($uri_bits['port'])?':'.$uri_bits['port']:'')
334 .(isset($bits['query'])?'?'.$bits['query']:'');
335
336 if (strlen($url) > ($before+$after)) :
337 $url = substr($url, 0, $before).'…'.substr($url, 0 - $after, $after);
338 endif;
339
340 return $url;
341 } /* feedwordpress_display_url () */
342
343 function get_syndication_source_property ($original, $id, $local, $remote = NULL) {
344 $ret = NULL;
345 if (is_null($original)) :
346 $original = FeedWordPress::use_aggregator_source_data();
347 endif;
348
349 if (is_null($remote)) :
350 $remote = $local . '_original';
351 endif;
352
353 $vals = ($original ? get_post_custom_values($remote, $id) : array());
354 if (count($vals) < 1) :
355 $vals = get_post_custom_values($local, $id);
356 endif;
357
358 if (count($vals) > 0) :
359 $ret = $vals[0];
360 endif;
361 return $ret;
362 } /* function get_syndication_source_property () */
363
364 function the_syndication_source_link ($original = NULL, $id = NULL) { echo get_syndication_source_link($original, $id); }
365 function get_syndication_source_link ($original = NULL, $id = NULL) {
366 return get_syndication_source_property($original, $id, 'syndication_source_uri');
367 } /* function get_syndication_source_link() */
368
369 function the_syndication_source ($original = NULL, $id = NULL) { echo get_syndication_source($original, $id); }
370 function get_syndication_source ($original = NULL, $id = NULL) {
371 $ret = get_syndication_source_property($original, $id, 'syndication_source');
372 if (is_null($ret) or strlen(trim($ret)) == 0) : // Fall back to URL of blog
373 $ret = feedwordpress_display_url(get_syndication_source_link());
374 endif;
375 return $ret;
376 } /* function get_syndication_source() */
377
378 function the_syndication_feed ($original = NULL, $id = NULL) { echo get_syndication_feed($original, $id); }
379 function get_syndication_feed ($original = NULL, $id = NULL) {
380 return get_syndication_source_property($original, $id, 'syndication_feed');
381 } /* function get_syndication_feed() */
382
383 function the_syndication_feed_guid ($original = NULL, $id = NULL) { echo get_syndication_feed_guid($original, $id); }
384 function get_syndication_feed_guid ($original = NULL, $id = NULL) {
385 $ret = get_syndication_source_property($original, $id, 'syndication_source_id');
386 if (is_null($ret) or strlen(trim($ret))==0) : // Fall back to URL of feed
387 $ret = get_syndication_feed();
388 endif;
389 return $ret;
390 } /* function get_syndication_feed_guid () */
391
392 function get_syndication_feed_id ($id = NULL) { list($u) = get_post_custom_values('syndication_feed_id', $id); return $u; }
393 function the_syndication_feed_id ($id = NULL) { echo get_syndication_feed_id($id); }
394
395 $feedwordpress_linkcache = array (); // only load links from database once
396 function get_syndication_feed_object ($id = NULL) {
397 global $feedwordpress_linkcache;
398
399 $link = NULL;
400
401 $feed_id = get_syndication_feed_id($id);
402 if (strlen($feed_id) > 0):
403 if (isset($feedwordpress_linkcache[$feed_id])) :
404 $link = $feedwordpress_linkcache[$feed_id];
405 else :
406 $link = new SyndicatedLink($feed_id);
407 $feedwordpress_linkcache[$feed_id] = $link;
408 endif;
409 endif;
410 return $link;
411 }
412
413 function get_feed_meta ($key, $id = NULL) {
414 $ret = NULL;
415
416 $link = get_syndication_feed_object($id);
417 if (is_object($link) and isset($link->settings[$key])) :
418 $ret = $link->settings[$key];
419 endif;
420 return $ret;
421 } /* get_feed_meta() */
422
423 function get_syndication_permalink ($id = NULL) {
424 list($u) = get_post_custom_values('syndication_permalink', $id); return $u;
425 }
426 function the_syndication_permalink ($id = NULL) {
427 echo get_syndication_permalink($id);
428 }
429
430 /**
431 * get_local_permalink: returns a string containing the internal permalink
432 * for a post (whether syndicated or not) on your local WordPress installation.
433 * This may be useful if you want permalinks to point to the original source of
434 * an article for most purposes, but want to retrieve a URL for the local
435 * representation of the post for one or two limited purposes (for example,
436 * linking to a comments page on your local aggregator site).
437 *
438 * @param $id The numerical ID of the post to get the permalink for. If empty,
439 * defaults to the current post in a Loop context.
440 * @return string The URL of the local permalink for this post.
441 *
442 * @uses get_permalink()
443 * @global $feedwordpress_the_original_permalink
444 *
445 * @since 2010.0217
446 */
447 function get_local_permalink ($id = NULL) {
448 global $feedwordpress_the_original_permalink;
449
450 // get permalink, and thus activate filter and force global to be filled
451 // with original URL.
452 $url = get_permalink($id);
453 return $feedwordpress_the_original_permalink;
454 } /* get_local_permalink() */
455
456 /**
457 * the_original_permalink: displays the contents of get_original_permalink()
458 *
459 * @param $id The numerical ID of the post to get the permalink for. If empty,
460 * defaults to the current post in a Loop context.
461 *
462 * @uses get_local_permalinks()
463 * @uses apply_filters
464 *
465 * @since 2010.0217
466 */
467 function the_local_permalink ($id = NULL) {
468 print apply_filters('the_permalink', get_local_permalink($id));
469 } /* function the_local_permalink() */
470
471 ################################################################################
472 ## FILTERS: syndication-aware handling of post data for templates and feeds ####
473 ################################################################################
474
475 $feedwordpress_the_syndicated_content = NULL;
476 $feedwordpress_the_original_permalink = NULL;
477
478 function feedwordpress_preserve_syndicated_content ($text) {
479 global $feedwordpress_the_syndicated_content;
480
481 $globalExpose = (get_option('feedwordpress_formatting_filters') == 'yes');
482 $localExpose = get_post_custom_values('_feedwordpress_formatting_filters');
483 $expose = ($globalExpose or ((count($localExpose) > 0) and $localExpose[0]));
484
485 if ( is_syndicated() and !$expose ) :
486 $feedwordpress_the_syndicated_content = $text;
487 else :
488 $feedwordpress_the_syndicated_content = NULL;
489 endif;
490 return $text;
491 }
492
493 function feedwordpress_restore_syndicated_content ($text) {
494 global $feedwordpress_the_syndicated_content;
495
496 if ( !is_null($feedwordpress_the_syndicated_content) ) :
497 $text = $feedwordpress_the_syndicated_content;
498 endif;
499
500 return $text;
501 }
502
503 function feedwordpress_item_feed_data () {
504 // In a post context....
505 if (is_syndicated()) :
506 ?>
507 <source>
508 <title><?php print htmlspecialchars(get_syndication_source()); ?></title>
509 <link rel="alternate" type="text/html" href="<?php print htmlspecialchars(get_syndication_source_link()); ?>" />
510 <link rel="self" href="<?php print htmlspecialchars(get_syndication_feed()); ?>" />
511 <?php
512 $id = get_syndication_feed_guid();
513 if (strlen($id) > 0) :
514 ?>
515 <id><?php print htmlspecialchars($id); ?></id>
516 <?php
517 endif;
518 $updated = get_feed_meta('feed/updated');
519 if (strlen($updated) > 0) : ?>
520 <updated><?php print $updated; ?></updated>
521 <?php
522 endif;
523 ?>
524 </source>
525 <?php
526 endif;
527 }
528
529 /**
530 * syndication_permalink: Allow WordPress to use the original remote URL of
531 * syndicated posts as their permalink. Can be turned on or off by by setting in
532 * Syndication => Posts & Links. Saves the old internal permalink in a global
533 * variable for later use.
534 *
535 * @param string $permalink The internal permalink
536 * @return string The new permalink. Same as the old if the post is not
537 * syndicated, or if FWP is set to use internal permalinks, or if the post
538 * was syndicated, but didn't have a proper permalink recorded.
539 *
540 * @uses SyndicatedLink::setting()
541 * @uses get_syndication_permalink()
542 * @uses get_syndication_feed_object()
543 * @uses url_to_postid()
544 * @global $id
545 * @global $feedwordpress_the_original_permalink
546 */
547 function syndication_permalink ($permalink = '', $post = null, $leavename = false) {
548 global $id;
549 global $feedwordpress_the_original_permalink;
550
551 // Save the local permalink in case we need to retrieve it later.
552 $feedwordpress_the_original_permalink = $permalink;
553
554 if (is_object($post) and isset($post->ID) and !empty($post->ID)) :
555 // Use the post ID we've been provided with.
556 $postId = $post->ID;
557 elseif (is_string($permalink) and strlen($permalink) > 0) :
558 // Map this permalink to a post ID so we can get the correct
559 // permalink even outside of the Post Loop. Props Björn.
560 $postId = url_to_postid($permalink);
561 else :
562 // If the permalink string is empty but Post Loop context
563 // provides an id.
564 $postId = $id;
565 endif;
566
567 $munge = false;
568 $link = get_syndication_feed_object($postId);
569 if (is_object($link)) :
570 $munge = ($link->setting('munge permalink', 'munge_permalink', 'yes') != 'no');
571 endif;
572
573 if ($munge):
574 $uri = get_syndication_permalink($postId);
575 $permalink = ((strlen($uri) > 0) ? $uri : $permalink);
576 endif;
577 return $permalink;
578 } /* function syndication_permalink () */
579
580 /**
581 * syndication_permalink_escaped: Escape XML special characters in syndicated
582 * permalinks when used in feed contexts and HTML display contexts.
583 *
584 * @param string $permalink
585 * @return string
586 *
587 * @uses is_syndicated()
588 * @uses FeedWordPress::munge_permalinks()
589 *
590 */
591 function syndication_permalink_escaped ($permalink) {
592 /* FIXME: This should review link settings not just global settings */
593 if (is_syndicated() and FeedWordPress::munge_permalinks()) :
594 // This is a foreign link; WordPress can't vouch for its not
595 // having any entities that need to be &-escaped. So we'll do
596 // it here.
597 $permalink = esc_html($permalink);
598 endif;
599 return $permalink;
600 } /* function syndication_permalink_escaped() */
601
602 /**
603 * syndication_comments_feed_link: Escape XML special characters in comments
604 * feed links
605 *
606 * @param string $link
607 * @return string
608 *
609 * @uses is_syndicated()
610 * @uses FeedWordPress::munge_permalinks()
611 */
612 function syndication_comments_feed_link ($link) {
613 global $feedwordpress_the_original_permalink, $id;
614
615 if (is_syndicated() and FeedWordPress::munge_permalinks()) :
616 // If the source post provided a comment feed URL using
617 // wfw:commentRss or atom:link/@rel="replies" we can make use of
618 // that value here.
619 $source = get_syndication_feed_object();
620 $replacement = NULL;
621 if ($source->setting('munge comments feed links', 'munge_comments_feed_links', 'yes') != 'no') :
622 $commentFeeds = get_post_custom_values('wfw:commentRSS');
623 if (
624 is_array($commentFeeds)
625 and (count($commentFeeds) > 0)
626 and (strlen($commentFeeds[0]) > 0)
627 ) :
628 $replacement = $commentFeeds[0];
629
630 // This is a foreign link; WordPress can't vouch for its not
631 // having any entities that need to be &-escaped. So we'll do it
632 // here.
633 $replacement = esc_html($replacement);
634 endif;
635 endif;
636
637 if (is_null($replacement)) :
638 // Q: How can we get the proper feed format, since the
639 // format is, stupidly, not passed to the filter?
640 // A: Kludge kludge kludge kludge!
641 $fancy_permalinks = ('' != get_option('permalink_structure'));
642 if ($fancy_permalinks) :
643 preg_match('|/feed(/([^/]+))?/?$|', $link, $ref);
644
645 $format = (isset($ref[2]) ? $ref[2] : '');
646 if (strlen($format) == 0) : $format = get_default_feed(); endif;
647
648 $replacement = trailingslashit($feedwordpress_the_original_permalink) . 'feed';
649 if ($format != get_default_feed()) :
650 $replacement .= '/'.$format;
651 endif;
652 $replacement = user_trailingslashit($replacement, 'single_feed');
653 else :
654 // No fancy permalinks = no problem
655 // WordPress doesn't call get_permalink() to
656 // generate the comment feed URL, so the
657 // comments feed link is never munged by FWP.
658 endif;
659 endif;
660
661 if (!is_null($replacement)) : $link = $replacement; endif;
662 endif;
663 return $link;
664 } /* function syndication_comments_feed_link() */
665
666 ################################################################################
667 ## ADMIN MENU ADD-ONS: register Dashboard management pages #####################
668 ################################################################################
669
670 function fwp_add_pages () {
671 $menu_cap = FeedWordPress::menu_cap();
672 $settings_cap = FeedWordPress::menu_cap(/*sub=*/ true);
673 $syndicationMenu = FeedWordPress::path('syndication.php');
674
675 add_menu_page(
676 'Syndicated Sites', 'Syndication',
677 $menu_cap,
678 $syndicationMenu,
679 NULL,
680 WP_PLUGIN_URL.'/'.FeedWordPress::path('feedwordpress-tiny.png')
681 );
682
683 do_action('feedwordpress_admin_menu_pre_feeds', $menu_cap, $settings_cap);
684 add_submenu_page(
685 $syndicationMenu, 'Syndicated Feeds & Updates', 'Feeds & Updates',
686 $settings_cap, FeedWordPress::path('feeds-page.php')
687 );
688
689 do_action('feedwordpress_admin_menu_pre_posts', $menu_cap, $settings_cap);
690 add_submenu_page(
691 $syndicationMenu, 'Syndicated Posts & Links', 'Posts & Links',
692 $settings_cap, FeedWordPress::path('posts-page.php')
693 );
694
695 do_action('feedwordpress_admin_menu_pre_authors', $menu_cap, $settings_cap);
696 add_submenu_page(
697 $syndicationMenu, 'Syndicated Authors', 'Authors',
698 $settings_cap, FeedWordPress::path('authors-page.php')
699 );
700
701 do_action('feedwordpress_admin_menu_pre_categories', $menu_cap, $settings_cap);
702 add_submenu_page(
703 $syndicationMenu, 'Categories'.FEEDWORDPRESS_AND_TAGS, 'Categories'.FEEDWORDPRESS_AND_TAGS,
704 $settings_cap, FeedWordPress::path('categories-page.php')
705 );
706
707 do_action('feedwordpress_admin_menu_pre_performance', $menu_cap, $settings_cap);
708 add_submenu_page(
709 $syndicationMenu, 'FeedWordPress Performance', 'Performance',
710 $settings_cap, FeedWordPress::path('performance-page.php')
711 );
712
713 do_action('feedwordpress_admin_menu_pre_diagnostics', $menu_cap, $settings_cap);
714 add_submenu_page(
715 $syndicationMenu, 'FeedWordPress Diagnostics', 'Diagnostics',
716 $settings_cap, FeedWordPress::path('diagnostics-page.php')
717 );
718 } /* function fwp_add_pages () */
719
720 function fwp_check_debug () {
721 // This is a horrible fucking kludge that I have to do because the
722 // admin notice code is triggered before the code that updates the
723 // setting.
724 if (isset($_POST['feedwordpress_debug'])) :
725 $feedwordpress_debug = $_POST['feedwordpress_debug'];
726 else :
727 $feedwordpress_debug = get_option('feedwordpress_debug');
728 endif;
729 if ($feedwordpress_debug==='yes') :
730 ?>
731 <div class="error">
732 <p><strong>FeedWordPress warning.</strong> Debugging mode is <strong>ON</strong>.
733 While it remains on, FeedWordPress displays many diagnostic error messages,
734 warnings, and notices that are ordinarily suppressed, and also turns off all
735 caching of feeds. Use with caution: this setting is absolutely inappropriate
736 for a production server.</p>
737 </div>
738 <?php
739 endif;
740 } /* function fwp_check_debug () */
741
742 ################################################################################
743 ## fwp_hold_pings() and fwp_release_pings(): Outbound XML-RPC ping reform ####
744 ## ... 'coz it's rude to send 500 pings the first time your aggregator runs ####
745 ################################################################################
746
747 $fwp_held_ping = NULL; // NULL: not holding pings yet
748
749 function fwp_hold_pings () {
750 global $fwp_held_ping;
751 if (is_null($fwp_held_ping)):
752 $fwp_held_ping = 0; // 0: ready to hold pings; none yet received
753 endif;
754 }
755
756 function fwp_release_pings () {
757 global $fwp_held_ping;
758 if ($fwp_held_ping):
759 if (function_exists('wp_schedule_single_event')) :
760 wp_schedule_single_event(time(), 'do_pings');
761 else :
762 generic_ping($fwp_held_ping);
763 endif;
764 endif;
765 $fwp_held_ping = NULL; // NULL: not holding pings anymore
766 }
767
768 function fwp_do_pings () {
769 if (!is_null($fwp_held_ping) and $post_id) : // Defer until we're done updating
770 $fwp_held_ping = $post_id;
771 elseif (function_exists('do_all_pings')) :
772 do_all_pings();
773 else :
774 generic_ping($fwp_held_ping);
775 endif;
776 }
777
778 function fwp_publish_post_hook ($post_id) {
779 global $fwp_held_ping;
780
781 if (!is_null($fwp_held_ping)) : // Syndicated post. Don't mark with _pingme
782 if ( defined('XMLRPC_REQUEST') )
783 do_action('xmlrpc_publish_post', $post_id);
784 if ( defined('APP_REQUEST') )
785 do_action('app_publish_post', $post_id);
786
787 if ( defined('WP_IMPORTING') )
788 return;
789
790 // Defer sending out pings until we finish updating
791 $fwp_held_ping = $post_id;
792 else :
793 if (function_exists('_publish_post_hook')) : // WordPress 2.3
794 _publish_post_hook($post_id);
795 endif;
796 endif;
797 }
798
799 function feedwordpress_add_post_edit_controls () {
800 add_meta_box('feedwordpress-post-controls', __('Syndication'), 'feedwordpress_post_edit_controls', 'post', 'side', 'high');
801 if (FeedWordPress::diagnostic_on('syndicated_posts:static_meta_data')) :
802 $GLOBALS['inspectPostMeta'] = new InspectPostMeta;
803 endif;
804 } // function FeedWordPress::postEditControls
805
806 function feedwordpress_post_edit_controls () {
807 global $post;
808
809 $frozen_values = get_post_custom_values('_syndication_freeze_updates', $post->ID);
810 $frozen_post = (count($frozen_values) > 0 and 'yes' == $frozen_values[0]);
811
812 if (is_syndicated($post->ID)) :
813 ?>
814 <p>This is a syndicated post, which originally appeared at
815 <cite><?php print esc_html(get_syndication_source(NULL, $post->ID)); ?></cite>.
816 <a href="<?php print esc_html(get_syndication_permalink($post->ID)); ?>">View original post</a>.</p>
817
818 <p><input type="hidden" name="feedwordpress_noncename" id="feedwordpress_noncename" value="<?php print wp_create_nonce(plugin_basename(__FILE__)); ?>" />
819 <label><input type="checkbox" name="freeze_updates" value="yes" <?php if ($frozen_post) : ?>checked="checked"<?php endif; ?> /> <strong>Manual editing.</strong>
820 If set, FeedWordPress will not overwrite the changes you make manually
821 to this post, if the syndicated content is updated on the
822 feed.</label></p>
823 <?php
824 else :
825 ?>
826 <p>This post was created locally at this website.</p>
827 <?php
828 endif;
829 } // function feedwordpress_post_edit_controls () */
830
831 function feedwordpress_save_post_edit_controls ( $post_id ) {
832 global $post;
833
834 if (!isset($_POST['feedwordpress_noncename']) or !wp_verify_nonce($_POST['feedwordpress_noncename'], plugin_basename(__FILE__))) :
835 return $post_id;
836 endif;
837
838 // Verify if this is an auto save routine. If it is our form has
839 // not been submitted, so we don't want to do anything.
840 if ( defined('DOING_AUTOSAVE') and DOING_AUTOSAVE ) :
841 return $post_id;
842 endif;
843
844 // Check permissions
845 if ( !current_user_can( 'edit_'.$_POST['post_type'], $post_id) ) :
846 return $post_id;
847 endif;
848
849 // OK, we're golden. Now let's save some data.
850 if (isset($_POST['freeze_updates'])) :
851 update_post_meta($post_id, '_syndication_freeze_updates', $_POST['freeze_updates']);
852 $ret = $_POST['freeze_updates'];
853 else :
854 delete_post_meta($post_id, '_syndication_freeze_updates');
855 $ret = NULL;
856 endif;
857
858 return $ret;
859 } // function feedwordpress_save_edit_controls
860
861 ################################################################################
862 ## class FeedWordPress #########################################################
863 ################################################################################
864
865 // class FeedWordPress: handles feed updates and plugs in to the XML-RPC interface
866 class FeedWordPress {
867 var $strip_attrs = array (
868 array('[a-z]+', 'style'),
869 array('[a-z]+', 'target'),
870 );
871 var $uri_attrs = array (
872 array('a', 'href'),
873 array('applet', 'codebase'),
874 array('area', 'href'),
875 array('blockquote', 'cite'),
876 array('body', 'background'),
877 array('del', 'cite'),
878 array('form', 'action'),
879 array('frame', 'longdesc'),
880 array('frame', 'src'),
881 array('iframe', 'longdesc'),
882 array('iframe', 'src'),
883 array('head', 'profile'),
884 array('img', 'longdesc'),
885 array('img', 'src'),
886 array('img', 'usemap'),
887 array('input', 'src'),
888 array('input', 'usemap'),
889 array('ins', 'cite'),
890 array('link', 'href'),
891 array('object', 'classid'),
892 array('object', 'codebase'),
893 array('object', 'data'),
894 array('object', 'usemap'),
895 array('q', 'cite'),
896 array('script', 'src')
897 );
898
899 var $feeds = NULL;
900
901 var $httpauth = NULL;
902 # function FeedWordPress (): Contructor; retrieve a list of feeds
903 function FeedWordPress () {
904 $this->feeds = array ();
905 $links = FeedWordPress::syndicated_links();
906 if ($links): foreach ($links as $link):
907 $this->feeds[] = new SyndicatedLink($link);
908 endforeach; endif;
909
910 $this->httpauth = new FeedWordPressHTTPAuthenticator;
911 } // FeedWordPress::FeedWordPress ()
912
913 # function update (): polls for updates on one or more Contributor feeds
914 #
915 # Arguments:
916 # ----------
917 # * $uri (string): either the URI of the feed to poll, the URI of the
918 # (human-readable) website whose feed you want to poll, or NULL.
919 #
920 # If $uri is NULL, then FeedWordPress will poll any feeds that are
921 # ready for polling. It will not poll feeds that are marked as
922 # "Invisible" Links (signifying that the subscription has been
923 # de-activated), or feeds that are not yet stale according to their
924 # TTL setting (which is either set in the feed, or else set
925 # randomly within a window of 30 minutes - 2 hours).
926 #
927 # Returns:
928 # --------
929 # * Normally returns an associative array, with 'new' => the number
930 # of new posts added during the update, and 'updated' => the number
931 # of old posts that were updated during the update. If both numbers
932 # are zero, there was no change since the last poll on that URI.
933 #
934 # * Returns NULL if URI it was passed was not a URI that this
935 # installation of FeedWordPress syndicates.
936 #
937 # Effects:
938 # --------
939 # * One or more feeds are polled for updates
940 #
941 # * If the feed Link does not have a hardcoded name set, its Link
942 # Name is synchronized with the feed's title element
943 #
944 # * If the feed Link does not have a hardcoded URI set, its Link URI
945 # is synchronized with the feed's human-readable link element
946 #
947 # * If the feed Link does not have a hardcoded description set, its
948 # Link Description is synchronized with the feed's description,
949 # tagline, or subtitle element.
950 #
951 # * The time of polling is recorded in the feed's settings, and the
952 # TTL (time until the feed is next available for polling) is set
953 # either from the feed (if it is supplied in the ttl or syndication
954 # module elements) or else from a randomly-generated time window
955 # (between 30 minutes and 2 hours).
956 #
957 # * New posts from the polled feed are added to the WordPress store.
958 #
959 # * Updates to existing posts since the last poll are mirrored in the
960 # WordPress store.
961 #
962 function update ($uri = null, $crash_ts = null) {
963 global $wpdb;
964
965 if (FeedWordPress::needs_upgrade()) : // Will make duplicate posts if we don't hold off
966 return NULL;
967 endif;
968
969 if (!is_null($uri) and $uri != '*') :
970 $uri = trim($uri);
971 else : // Update all
972 if ($this->update_hooked) :
973 $diag = $this->update_hooked;
974 else :
975 $diag = 'Initiating a MANUAL check-in on the update schedule at '.date('r', time());
976 endif;
977 $this->diagnostic('update_schedule:check', $diag);
978
979 update_option('feedwordpress_last_update_all', time());
980 endif;
981
982 do_action('feedwordpress_update', $uri);
983
984 if (is_null($crash_ts)) :
985 $crash_ts = $this->crash_ts();
986 endif;
987
988 // Randomize order for load balancing purposes
989 $feed_set = $this->feeds;
990 shuffle($feed_set);
991
992 $updateWindow = (int) get_option('feedwordpress_update_window', DEFAULT_UPDATE_PERIOD) * 60 /* sec/min */;
993 $interval = (int) get_option('feedwordpress_freshness', FEEDWORDPRESS_FRESHNESS_INTERVAL);
994 $portion = max(
995 (int) ceil(count($feed_set) / ($updateWindow / $interval)),
996 10
997 );
998
999 $max_polls = apply_filters('feedwordpress_polls_per_update', get_option(
1000 'feedwordpress_polls_per_update', $portion
1001 ), $uri);
1002
1003 $feed_set = apply_filters('feedwordpress_update_feeds', $feed_set, $uri);
1004
1005 // Loop through and check for new posts
1006 $delta = NULL; $remaining = $max_polls;
1007 foreach ($feed_set as $feed) :
1008
1009 // Has this process overstayed its welcome?
1010 if (
1011 // Over time limit?
1012 (!is_null($crash_ts) and (time() > $crash_ts))
1013
1014 // Over feed count?
1015 or ($remaining == 0)
1016 ) :
1017 break;
1018 endif;
1019
1020 $pinged_that = (is_null($uri) or ($uri=='*') or in_array($uri, array($feed->uri(), $feed->homepage())));
1021
1022 if (!is_null($uri)) : // A site-specific ping always updates
1023 $timely = true;
1024 else :
1025 $timely = $feed->stale();
1026 endif;
1027
1028 if ($pinged_that and is_null($delta)) : // If at least one feed was hit for updating...
1029 $delta = array('new' => 0, 'updated' => 0); // ... don't return error condition
1030 endif;
1031
1032 if ($pinged_that and $timely) :
1033 $remaining = $remaining - 1;
1034
1035 do_action('feedwordpress_check_feed', $feed->settings);
1036 $start_ts = time();
1037 $added = $feed->poll($crash_ts);
1038 do_action('feedwordpress_check_feed_complete', $feed->settings, $added, time() - $start_ts);
1039
1040 if (is_array($added)) : // Success
1041 if (isset($added['new'])) : $delta['new'] += $added['new']; endif;
1042 if (isset($added['updated'])) : $delta['updated'] += $added['updated']; endif;
1043 endif;
1044 endif;
1045 endforeach;
1046
1047 do_action('feedwordpress_update_complete', $delta);
1048
1049 return $delta;
1050 }
1051
1052 function crash_ts ($default = NULL) {
1053 $crash_dt = (int) get_option('feedwordpress_update_time_limit', 0);
1054 if ($crash_dt > 0) :
1055 $crash_ts = time() + $crash_dt;
1056 else :
1057 $crash_ts = $default;
1058 endif;
1059 return $crash_ts;
1060 }
1061
1062 function secret_key () {
1063 $secret = get_option('feedwordpress_secret_key', false);
1064 if (!$secret) : // Generate random key.
1065 $secret = substr(md5(uniqid(microtime())), 0, 6);
1066 update_option('feedwordpress_secret_key', $secret);
1067 endif;
1068 return $secret;
1069 }
1070
1071 function has_secret () {
1072 return ($this->by_request('feedwordpress_key', $this->secret_key()));
1073 }
1074
1075 // Utility function so I don't have to repeat myself w/ 1,000,003 isset()'s
1076 function by_request ($param, $eq = NULL) {
1077 $match = false;
1078 if (isset($_REQUEST[$param])) :
1079 $match = (is_null($eq) ? $_REQUEST[$param] : ($eq==$_REQUEST[$param]));
1080 endif;
1081 return $match;
1082 }
1083
1084 var $update_hooked = NULL;
1085 function automatic_update_hook ($params = array()) {
1086 $params = wp_parse_args($params, array( // Defaults
1087 'setting only' => false,
1088 ));
1089 $hook = get_option('feedwordpress_automatic_updates', NULL);
1090 $method = 'FeedWordPress option';
1091
1092 // Allow for forced behavior in testing.
1093 if (
1094 !$params['setting only']
1095 and $this->has_secret()
1096 and $this->by_request('automatic_update')
1097 ) :
1098 $hook = $_REQUEST['automatic_update'];
1099 $method = 'URL parameter';
1100 endif;
1101
1102 $exact = $hook; // Before munging
1103
1104 if (!!$hook) :
1105 if ($hook != 'init') : // Constrain values.
1106 $hook = 'shutdown';
1107 endif;
1108 endif;
1109
1110 if ($hook) :
1111 $this->update_hooked = "Initiating an AUTOMATIC CHECK FOR UPDATES ON PAGE LOAD ".$hook." due to ".$method." = ".trim($this->val($exact));
1112 endif;
1113
1114 return $hook;
1115 }
1116 function last_update_all () {
1117 $last = get_option('feedwordpress_last_update_all');
1118 if ($this->has_secret() and $this->by_request('automatic_update')) :
1119 $last = 1; // A long, long time ago.
1120 elseif ($this->has_secret() and $this->by_request('last_update_all')) :
1121 $last = $_REQUEST['last_update_all'];
1122 endif;
1123 return $last;
1124 }
1125 function force_update_all () {
1126 return ($this->has_secret() and $this->by_request('force_update_feeds'));
1127 }
1128
1129 function stale () {
1130 if (!is_null($this->automatic_update_hook())) :
1131 // Do our best to avoid possible simultaneous
1132 // updates by getting up-to-the-minute settings.
1133
1134 $last = $this->last_update_all();
1135
1136 // If we haven't updated all yet, give it a time window
1137 if (false === $last) :
1138 $ret = false;
1139 update_option('feedwordpress_last_update_all', time());
1140
1141 // Otherwise, check against freshness interval
1142 elseif (is_numeric($last)) : // Expect a timestamp
1143 $freshness = get_option('feedwordpress_freshness');
1144 if (false === $freshness) : // Use default
1145 $freshness = FEEDWORDPRESS_FRESHNESS_INTERVAL;
1146 endif;
1147 $ret = ( (time() - $last) > $freshness );
1148
1149 // This should never happen.
1150 else :
1151 FeedWordPress::critical_bug('FeedWordPress::stale::last', $last, __LINE__, __FILE__);
1152 endif;
1153
1154 else :
1155 $ret = false;
1156 endif;
1157 return $ret;
1158 } // FeedWordPress::stale()
1159
1160 function init () {
1161 global $fwp_path;
1162
1163 // If this is a FeedWordPress admin page, queue up scripts for AJAX
1164 // functions that FWP uses. If it is a display page or a non-FWP admin
1165 // page, don't.
1166 wp_register_style('feedwordpress-elements', WP_PLUGIN_URL.'/'.$fwp_path.'/feedwordpress-elements.css');
1167 if (FeedWordPressSettingsUI::is_admin()) :
1168 // For JavaScript that needs to be generated dynamically
1169 add_action('admin_print_scripts', array('FeedWordPressSettingsUI', 'admin_scripts'));
1170
1171 // For CSS that needs to be generated dynamically.
1172 add_action('admin_print_styles', array('FeedWordPressSettingsUI', 'admin_styles'));
1173
1174 wp_enqueue_style('dashboard');
1175 wp_enqueue_style('feedwordpress-elements');
1176
1177 if (function_exists('wp_admin_css')) :
1178 wp_admin_css('css/dashboard');
1179 endif;
1180 endif;
1181
1182 $this->clear_cache_magic_url();
1183 $this->update_magic_url();
1184 } /* FeedWordPress::init() */
1185
1186 function dashboard_setup () {
1187 $see_it = FeedWordPress::menu_cap();
1188
1189 if (current_user_can($see_it)) :
1190 // Get the stylesheet
1191 wp_enqueue_style('feedwordpress-elements');
1192
1193 $widget_id = 'feedwordpress_dashboard';
1194 $widget_name = __('Syndicated Sources');
1195 $column = 'side';
1196 $priority = 'core';
1197
1198 // I would love to use wp_add_dashboard_widget() here and save
1199 // myself some trouble. But WP 3 does not yet have any way to
1200 // push a dashboard widget onto the side, or to give it a default
1201 // location.
1202 add_meta_box(
1203 /*id=*/ $widget_id,
1204 /*title=*/ $widget_name,
1205 /*callback=*/ array(&$this, 'dashboard'),
1206 /*page=*/ 'dashboard',
1207 /*context=*/ $column,
1208 /*priority=*/ $priority
1209 );
1210 /*control_callback= array(&$this, 'dashboard_control') */
1211
1212 // This is kind of rude, I know, but the dashboard widget isn't
1213 // worth much if users don't know that it exists, and I don't
1214 // know of any better way to reorder the boxen.
1215 //
1216 // Gleefully ripped off of codex.wordpress.org/Dashboard_Widgets_API
1217
1218 // Globalize the metaboxes array, this holds all the widgets for wp-admin
1219 global $wp_meta_boxes;
1220
1221 // Get the regular dashboard widgets array
1222 // (which has our new widget already but at the end)
1223
1224 $normal_dashboard = $wp_meta_boxes['dashboard'][$column][$priority];
1225
1226 // Backup and delete our new dashbaord widget from the end of the array
1227 if (isset($normal_dashboard[$widget_id])) :
1228 $backup = array();
1229 $backup[$widget_id] = $normal_dashboard[$widget_id];
1230 unset($normal_dashboard[$widget_id]);
1231
1232 // Merge the two arrays together so our widget is at the
1233 // beginning
1234 $sorted_dashboard = array_merge($backup, $normal_dashboard);
1235
1236 // Save the sorted array back into the original metaboxes
1237 $wp_meta_boxes['dashboard'][$column][$priority] = $sorted_dashboard;
1238 endif;
1239 endif;
1240 } /* FeedWordPress::dashboard_setup () */
1241
1242 function dashboard () {
1243 $syndicationPage = new FeedWordPressSyndicationPage(dirname(__FILE__).'/syndication.php');
1244 $syndicationPage->dashboard_box($syndicationPage);
1245 } /* FeedWordPress::dashboard () */
1246
1247 function update_magic_url () {
1248 global $wpdb;
1249
1250 // Explicit update request in the HTTP request (e.g. from a cron job)
1251 if ($this->update_requested()) :
1252 $this->update_hooked = "Initiating a CRON JOB CHECK-IN ON UPDATE SCHEDULE due to URL parameter = ".trim($this->val($_REQUEST['update_feedwordpress']));
1253
1254 $this->update($this->update_requested_url());
1255
1256 if (FEEDWORDPRESS_DEBUG and count($wpdb->queries) > 0) :
1257 $mysqlTime = 0.0;
1258 $byTime = array();
1259 foreach ($wpdb->queries as $query) :
1260 $time = $query[1] * 1000000.0;
1261 $mysqlTime += $query[1];
1262 if (!isset($byTime[$time])) : $byTime[$time] = array(); endif;
1263 $byTime[$time][] = $query[0]. ' // STACK: ' . $query[2];
1264 endforeach;
1265 krsort($byTime);
1266
1267 foreach ($byTime as $time => $querySet) :
1268 foreach ($querySet as $query) :
1269 print "[".(sprintf('%4.4f', $time/1000.0)) . "ms] $query\n";
1270 endforeach;
1271 endforeach;
1272 echo $this->log_prefix()."$wpdb->num_queries queries. $mysqlTime seconds in MySQL. Total of "; timer_stop(1); print " seconds.";
1273 endif;
1274
1275 debug_out_feedwordpress_footer();
1276
1277 // Magic URL should return nothing but a 200 OK header packet
1278 // when successful.
1279 exit;
1280 endif;
1281 } /* FeedWordPress::magic_update_url () */
1282
1283 function clear_cache_magic_url () {
1284 if ($this->clear_cache_requested()) :
1285 $this->clear_cache();
1286 endif;
1287 } /* FeedWordPress::clear_cache_magic_url() */
1288
1289 function clear_cache_requested () {
1290 return (
1291 isset($_GET['clear_cache'])
1292 and $_GET['clear_cache']
1293 );
1294 } /* FeedWordPress::clear_cache_requested() */
1295
1296 function update_requested () {
1297 return FeedWordPress::by_request('update_feedwordpress');
1298 } // FeedWordPress::update_requested()
1299
1300 function update_requested_url () {
1301 $ret = null;
1302
1303 if (($_REQUEST['update_feedwordpress']=='*')
1304 or (preg_match('|^http://.*|i', $_REQUEST['update_feedwordpress']))) :
1305 $ret = $_REQUEST['update_feedwordpress'];
1306 endif;
1307
1308 return $ret;
1309 } // FeedWordPress::update_requested_url()
1310
1311 function auto_update () {
1312 if ($this->stale()) :
1313 $this->update();
1314 endif;
1315 } /* FeedWordPress::auto_update () */
1316
1317 function find_link ($uri, $field = 'link_rss') {
1318 global $wpdb;
1319
1320 $unslashed = untrailingslashit($uri);
1321 $slashed = trailingslashit($uri);
1322 $link_id = $wpdb->get_var($wpdb->prepare("
1323 SELECT link_id FROM $wpdb->links WHERE $field IN ('%s', '%s')
1324 LIMIT 1", $unslashed, $slashed
1325 ));
1326
1327 return $link_id;
1328 } /* FeedWordPress::find_link () */
1329
1330 function syndicate_link ($name, $uri, $rss) {
1331 // Get the category ID#
1332 $cat_id = FeedWordPress::link_category_id();
1333 if (!is_wp_error($cat_id)) :
1334 $link_category = array($cat_id);
1335 else :
1336 $link_category = array();
1337 endif;
1338
1339 // WordPress gets cranky if there's no homepage URI
1340 if (!is_string($uri) or strlen($uri)<1) : $uri = $rss; endif;
1341
1342 // Check if this feed URL is already being syndicated.
1343 $link_id = wp_insert_link(array(
1344 "link_id" => FeedWordPress::find_link($rss), // insert if nothing was found; else update
1345 "link_rss" => $rss,
1346 "link_name" => $name,
1347 "link_url" => $uri,
1348 "link_category" => $link_category,
1349 "link_visible" => 'Y', // reactivate if inactivated
1350 ));
1351
1352 return $link_id;
1353 } /* function FeedWordPress::syndicate_link() */
1354
1355 /*static*/ function syndicated_status ($what, $default) {
1356 $ret = get_option("feedwordpress_syndicated_{$what}_status");
1357 if (!$ret) :
1358 $ret = $default;
1359 endif;
1360 return $ret;
1361 } /* FeedWordPress::syndicated_status() */
1362
1363 function on_unfamiliar ($what = 'author') {
1364 switch ($what) :
1365 case 'category' : $suffix = ':category'; break;
1366 case 'post_tag' : $suffix = ':post_tag'; break;
1367 default: $suffix = '';
1368 endswitch;
1369
1370 return get_option('feedwordpress_unfamiliar_'.$what, 'create'.$suffix);
1371 } // function FeedWordPress::on_unfamiliar()
1372
1373 function null_email_set () {
1374 $base = get_option('feedwordpress_null_email_set');
1375
1376 if ($base===false) :
1377 $ret = array('noreply@blogger.com'); // default
1378 else :
1379 $ret = array_map('strtolower',
1380 array_map('trim', explode("\n", $base)));
1381 endif;
1382 $ret = apply_filters('syndicated_item_author_null_email_set', $ret);
1383 return $ret;
1384
1385 } /* FeedWordPress::null_email_set () */
1386
1387 function is_null_email ($email) {
1388 $ret = in_array(strtolower(trim($email)), FeedWordPress::null_email_set());
1389 $ret = apply_filters('syndicated_item_author_is_null_email', $ret, $email);
1390 return $ret;
1391 } /* FeedWordPress::is_null_email () */
1392
1393 function use_aggregator_source_data () {
1394 $ret = get_option('feedwordpress_use_aggregator_source_data');
1395 return apply_filters('syndicated_post_use_aggregator_source_data', ($ret=='yes'));
1396 }
1397
1398 /**
1399 * FeedWordPress::munge_permalinks: check whether or not FeedWordPress
1400 * should rewrite permalinks for syndicated items to reflect their
1401 * original location.
1402 *
1403 * @return bool TRUE if FeedWordPress SHOULD rewrite permalinks; FALSE otherwise
1404 */
1405 /*static*/ function munge_permalinks () {
1406 return (get_option('feedwordpress_munge_permalink', /*default=*/ 'yes') != 'no');
1407 } /* FeedWordPress::munge_permalinks() */
1408
1409 function syndicated_links ($args = array()) {
1410 $contributors = FeedWordPress::link_category_id();
1411 if (!is_wp_error($contributors)) :
1412 $links = get_bookmarks(array_merge(
1413 array("category" => $contributors),
1414 $args
1415 ));
1416 else :
1417 $links = array();
1418 endif;
1419 return $links;
1420 } // function FeedWordPress::syndicated_links()
1421
1422 function link_category_id () {
1423 global $wpdb, $wp_db_version;
1424
1425 $cat_id = get_option('feedwordpress_cat_id');
1426
1427 // If we don't yet have the category ID stored, search by name
1428 if (!$cat_id) :
1429 $cat_id = FeedWordPressCompatibility::link_category_id(DEFAULT_SYNDICATION_CATEGORY);
1430
1431 if ($cat_id) :
1432 // We found it; let's stamp it.
1433 update_option('feedwordpress_cat_id', $cat_id);
1434 endif;
1435
1436 // If we *do* have the category ID stored, verify that it exists
1437 else :
1438 $cat_id = FeedWordPressCompatibility::link_category_id((int) $cat_id, 'cat_id');
1439 endif;
1440
1441 // If we could not find an appropriate link category,
1442 // make a new one for ourselves.
1443 if (!$cat_id) :
1444 $cat_id = FeedWordPressCompatibility::insert_link_category(DEFAULT_SYNDICATION_CATEGORY);
1445 if (!is_wp_error($cat_id)) :
1446 // Stamp it
1447 update_option('feedwordpress_cat_id', $cat_id);
1448 endif;
1449 endif;
1450
1451 return $cat_id;
1452 } // function FeedWordPress::link_category_id()
1453
1454 # Upgrades and maintenance...
1455 function needs_upgrade () {
1456 global $wpdb;
1457 $fwp_db_version = get_option('feedwordpress_version');
1458 $ret = false; // innocent until proven guilty
1459 if (!$fwp_db_version or $fwp_db_version < FEEDWORDPRESS_VERSION) :
1460 // This is an older version or a fresh install. Does it
1461 // require a database upgrade or database initialization?
1462 if ($fwp_db_version <= 0.96) :
1463 // Yes. Check to see whether this is a fresh install or an upgrade.
1464 $syn = $wpdb->get_col("
1465 SELECT post_id
1466 FROM $wpdb->postmeta
1467 WHERE meta_key = 'syndication_feed'
1468 ");
1469 if (count($syn) > 0) : // contains at least one syndicated post
1470 $ret = true;
1471 else : // fresh install; brand it as ours
1472 update_option('feedwordpress_version', FEEDWORDPRESS_VERSION);
1473 endif;
1474 elseif ($fwp_db_version < 2009.0707) :
1475 // We need to clear out any busted AJAX crap
1476 $wpdb->query("
1477 DELETE FROM $wpdb->usermeta
1478 WHERE LOCATE('feedwordpress', meta_key)
1479 AND LOCATE('box', meta_key);
1480 ");
1481
1482 update_option('feedwordpress_version', FEEDWORDPRESS_VERSION);
1483 elseif ($fwp_db_version < 2010.0814) :
1484 // Change in terminology.
1485 if (get_option('feedwordpress_unfamiliar_category', 'create')=='default') :
1486 update_option('feedwordpress_unfamiliar_category', 'null');
1487 endif;
1488 foreach (FeedWordPress::syndicated_links() as $link) :
1489 $sub = new SyndicatedLink($link);
1490
1491 $remap_uf = array(
1492 'default' => 'null',
1493 'filter' => 'null',
1494 'create' => 'create:category',
1495 'tag' => 'create:post_tag'
1496 );
1497 if (isset($sub->settings['unfamiliar category'])) :
1498 if ($sub->settings['unfamiliar category']=='filter') :
1499 $sub->settings['match/filter'] = array('category');
1500 endif;
1501 foreach ($remap_uf as $from => $to) :
1502 if ($sub->settings['unfamiliar category']==$from) :
1503 $sub->settings['unfamiliar category'] = $to;
1504 endif;
1505 endforeach;
1506 endif;
1507
1508 if (isset($sub->settings['add global categories'])) :
1509 $sub->settings['add/category'] = $sub->settings['add global categories'];
1510 unset($sub->settings['add global categories']);
1511 endif;
1512
1513 $sub->save_settings(/*reload=*/ true);
1514 endforeach;
1515 update_option('feedwordpress_version', FEEDWORDPRESS_VERSION);
1516 else :
1517 // No. Just brand it with the new version.
1518 update_option('feedwordpress_version', FEEDWORDPRESS_VERSION);
1519 endif;
1520 endif;
1521 return $ret;
1522 }
1523
1524 function upgrade_database ($from = NULL) {
1525 global $wpdb;
1526
1527 if (is_null($from) or $from <= 0.96) : $from = 0.96; endif;
1528
1529 switch ($from) :
1530 case 0.96:
1531 // Dropping legacy upgrade code. If anyone is still
1532 // using 0.96 and just now decided to upgrade, well, I'm
1533 // sorry about that. You'll just have to cope with a few
1534 // duplicate posts.
1535
1536 // Mark the upgrade as successful.
1537 update_option('feedwordpress_version', FEEDWORDPRESS_VERSION);
1538 endswitch;
1539 echo "<p>Upgrade complete. FeedWordPress is now ready to use again.</p>";
1540 } /* FeedWordPress::upgrade_database() */
1541
1542 function has_guid_index () {
1543 global $wpdb;
1544
1545 $found = false; // Guilty until proven innocent.
1546
1547 $results = $wpdb->get_results("
1548 SHOW INDEXES FROM {$wpdb->posts}
1549 ");
1550 if ($results) :
1551 foreach ($results as $index) :
1552 if (isset($index->Column_name)
1553 and ('guid' == $index->Column_name)) :
1554 $found = true;
1555 endif;
1556 endforeach;
1557 endif;
1558 return $found;
1559 } /* FeedWordPress::has_guid_index () */
1560
1561 function create_guid_index () {
1562 global $wpdb;
1563
1564 $wpdb->query("
1565 CREATE INDEX {$wpdb->posts}_guid_idx ON {$wpdb->posts}(guid)
1566 ");
1567 } /* FeedWordPress::create_guid_index () */
1568
1569 function remove_guid_index () {
1570 global $wpdb;
1571
1572 $wpdb->query("
1573 DROP INDEX {$wpdb->posts}_guid_idx ON {$wpdb->posts}
1574 ");
1575 }
1576
1577 /*static*/ function fetch_timeout () {
1578 return apply_filters(
1579 'feedwordpress_fetch_timeout',
1580 intval(get_option('feedwordpress_fetch_timeout', FEEDWORDPRESS_FETCH_TIMEOUT_DEFAULT))
1581 );
1582 }
1583
1584 /*static*/ function fetch ($url, $params = array()) {
1585 $force_feed = true; // Default
1586
1587 // Allow user to change default feed-fetch timeout with a global setting. Props Erigami Scholey-Fuller <http://www.piepalace.ca/blog/2010/11/feedwordpress-broke-my-heart.html> 'timeout' =>
1588 $timeout = FeedWordPress::fetch_timeout();
1589
1590 if (!is_array($params)) :
1591 $force_feed = $params;
1592 else : // Parameter array
1593 $args = wp_parse_args(array(
1594 'force_feed' => $force_feed,
1595 'timeout' => $timeout
1596 ), $params);
1597
1598 extract($args);
1599 endif;
1600 $timeout = intval($timeout);
1601
1602 $pie_class = apply_filters('feedwordpress_simplepie_class', 'SimplePie');
1603 $cache_class = apply_filters('feedwordpress_cache_class', 'WP_Feed_Cache');
1604 $file_class = apply_filters('feedwordpress_file_class', 'FeedWordPress_File');
1605 $parser_class = apply_filters('feedwordpress_parser_class', 'FeedWordPress_Parser');
1606
1607 $sniffer_class = apply_filters('feedwordpress_sniffer_class', 'FeedWordPress_Content_Type_Sniffer');
1608
1609 $feed = new $pie_class;
1610 $feed->set_feed_url($url);
1611 $feed->set_cache_class($cache_class);
1612 $feed->set_timeout($timeout);
1613
1614 $feed->set_content_type_sniffer_class($sniffer_class);
1615 $feed->set_file_class($file_class);
1616 $feed->set_parser_class($parser_class);
1617 $feed->force_feed($force_feed);
1618 $feed->set_cache_duration(FeedWordPress::cache_duration());
1619 $feed->init();
1620 $feed->handle_content_type();
1621
1622 if ($feed->error()) :
1623 $ret = new WP_Error('simplepie-error', $feed->error());
1624 else :
1625 $ret = $feed;
1626 endif;
1627 return $ret;
1628 } /* FeedWordPress::fetch () */
1629
1630 function clear_cache () {
1631 global $wpdb;
1632
1633 // Just in case, clear out any old MagpieRSS cache records.
1634 $magpies = $wpdb->query("
1635 DELETE FROM {$wpdb->options}
1636 WHERE option_name LIKE 'rss_%' AND LENGTH(option_name) > 32
1637 ");
1638
1639 // The WordPress SimplePie module stores its cached feeds as
1640 // transient records in the options table. The data itself is
1641 // stored in `_transient_feed_{md5 of url}` and the last-modified
1642 // timestamp in `_transient_feed_mod_{md5 of url}`. Timeouts for
1643 // these records are stored in `_transient_timeout_feed_{md5}`.
1644 // Since the md5 is always 32 characters in length, the
1645 // option_name is always over 32 characters.
1646 $simplepies = $wpdb->query("
1647 DELETE FROM {$wpdb->options}
1648 WHERE option_name LIKE '_transient%_feed_%' AND LENGTH(option_name) > 32
1649 ");
1650 $simplepies = (int) ($simplepies / 4); // Each transient has 4 rows: the data, the modified timestamp; and the timeouts for each
1651
1652 return ($magpies + $simplepies);
1653 } /* FeedWordPress::clear_cache () */
1654
1655 function cache_duration () {
1656 $duration = NULL;
1657 if (defined('FEEDWORDPRESS_CACHE_AGE')) :
1658 $duration = FEEDWORDPRESS_CACHE_AGE;
1659 endif;
1660 return $duration;
1661 }
1662 function cache_lifetime ($duration) {
1663 // Check for explicit setting of a lifetime duration
1664 if (defined('FEEDWORDPRESS_CACHE_LIFETIME')) :
1665 $duration = FEEDWORDPRESS_CACHE_LIFETIME;
1666
1667 // Fall back to the cache freshness duration
1668 elseif (defined('FEEDWORDPRESS_CACHE_AGE')) :
1669 $duration = FEEDWORDPRESS_CACHE_AGE;
1670 endif;
1671
1672 // Fall back to WordPress default
1673 return $duration;
1674 } /* FeedWordPress::cache_lifetime () */
1675
1676 # Utility functions for handling text settings
1677 function negative ($f, $setting) {
1678 $nego = array ('n', 'no', 'f', 'false');
1679 return (isset($f[$setting]) and in_array(strtolower($f[$setting]), $nego));
1680 }
1681
1682 function affirmative ($f, $setting) {
1683 $affirmo = array ('y', 'yes', 't', 'true', 1);
1684 return (isset($f[$setting]) and in_array(strtolower($f[$setting]), $affirmo));
1685 }
1686
1687 # Internal debugging functions
1688 function critical_bug ($varname, $var, $line, $file = NULL) {
1689 global $wp_version;
1690
1691 if (!is_null($file)) :
1692 $location = "line # ${line} of ".basename($file);
1693 else :
1694 $location = "line # ${line}";
1695 endif;
1696
1697 print '<p><strong>Critical error:</strong> There may be a bug in FeedWordPress. Please <a href="'.FEEDWORDPRESS_AUTHOR_CONTACT.'">contact the author</a> and paste the following information into your e-mail:</p>';
1698 print "\n<plaintext>";
1699 print "Triggered at ${location}\n";
1700 print "FeedWordPress: ".FEEDWORDPRESS_VERSION."\n";
1701 print "WordPress: {$wp_version}\n";
1702 print "PHP: ".phpversion()."\n";
1703 print "Error data: ";
1704 print $varname.": "; var_dump($var); echo "\n";
1705 die;
1706 }
1707
1708 function noncritical_bug ($varname, $var, $line, $file = NULL) {
1709 if (FEEDWORDPRESS_DEBUG) : // halt only when we are doing debugging
1710 FeedWordPress::critical_bug($varname, $var, $line, $file);
1711 endif;
1712 }
1713
1714 function val ($v, $no_newlines = false) {
1715 ob_start();
1716 var_dump($v);
1717 $out = ob_get_contents(); ob_end_clean();
1718
1719 if ($no_newlines) :
1720 $out = preg_replace('/\s+/', " ", $out);
1721 endif;
1722 return $out;
1723 } /* FeedWordPress::val () */
1724
1725 function diagnostic_on ($level) {
1726 $show = get_option('feedwordpress_diagnostics_show', array());
1727 return (in_array($level, $show));
1728 } /* FeedWordPress::diagnostic_on () */
1729
1730 function diagnostic ($level, $out, $persist = NULL, $since = NULL, $mostRecent = NULL) {
1731 global $feedwordpress_admin_footer;
1732
1733 $output = get_option('feedwordpress_diagnostics_output', array());
1734 $dlog = get_option('feedwordpress_diagnostics_log', array());
1735
1736 $diagnostic_nesting = count(explode(":", $level));
1737
1738 if (FeedWordPress::diagnostic_on($level)) :
1739 foreach ($output as $method) :
1740 switch ($method) :
1741 case 'echo' :
1742 if (!FeedWordPress::update_requested()) :
1743 echo "<div><pre><strong>Diag".str_repeat('====', $diagnostic_nesting-1).'|</strong> '.$out."</pre></div>";
1744 endif;
1745 break;
1746 case 'echo_in_cronjob' :
1747 if (FeedWordPress::update_requested()) :
1748 echo FeedWordPress::log_prefix()." ".$out."\n";
1749 endif;
1750 break;
1751 case 'admin_footer' :
1752 $feedwordpress_admin_footer[] = $out;
1753 break;
1754 case 'error_log' :
1755 error_log(FeedWordPress::log_prefix().' '.$out);
1756 break;
1757 case 'email' :
1758 if (is_null($persist)) :
1759 $sect = 'occurrent';
1760 $hook = (isset($dlog['mesg'][$sect]) ? count($dlog['mesg'][$sect]) : 0);
1761 $line = array("Time" => time(), "Message" => $out);
1762 else :
1763 $sect = 'persistent';
1764 $hook = md5($level."\n".$persist);
1765 $line = array("Since" => $since, "Message" => $out, "Most Recent" => $mostRecent);
1766 endif;
1767
1768 if (!isset($dlog['mesg'])) : $dlog['mesg'] = array(); endif;
1769 if (!isset($dlog['mesg'][$sect])) : $dlog['mesg'][$sect] = array(); endif;
1770
1771 $dlog['mesg'][$sect][$hook] = $line;
1772 endswitch;
1773 endforeach;
1774 endif;
1775
1776 update_option('feedwordpress_diagnostics_log', $dlog);
1777 } /* FeedWordPress::diagnostic () */
1778
1779 function email_diagnostics_override () {
1780 return ($this->has_secret() and isset($_REQUEST['feedwordpress_email_diagnostics']) and !!$_REQUEST['feedwordpress_email_diagnostics']);
1781 }
1782 function has_emailed_diagnostics ($dlog) {
1783 $ret = false;
1784 if ($this->email_diagnostics_override()
1785 or (isset($dlog['schedule']) and isset($dlog['schedule']['last']))) :
1786 $ret = true;
1787 endif;
1788 return $ret;
1789 }
1790 function ready_to_email_diagnostics ($dlog) {
1791 $ret = false;
1792 if ($this->email_diagnostics_override()
1793 or (time() > ($dlog['schedule']['last'] + $dlog['schedule']['freq']))) :
1794 $ret = true;
1795 endif;
1796 return $ret;
1797 }
1798
1799 function email_diagnostic_log () {
1800 $dlog = get_option('feedwordpress_diagnostics_log', array());
1801
1802 if ($this->has_emailed_diagnostics($dlog)) :
1803 if ($this->ready_to_email_diagnostics($dlog)) :
1804 // No news is good news; only send if
1805 // there are some messages to send.
1806 $body = NULL;
1807 if (!isset($dlog['mesg'])) : $dlog['mesg'] = array(); endif;
1808
1809 foreach ($dlog['mesg'] as $sect => $mesgs) :
1810 if (count($mesgs) > 0) :
1811 if (is_null($body)) : $body = ''; endif;
1812
1813 $paradigm = reset($mesgs);
1814 $body .= "<h2>".ucfirst($sect)." issues</h2>\n"
1815 ."<table>\n"
1816 ."<thead><tr>\n";
1817 foreach ($paradigm as $col => $value) :
1818 $body .= '<th scope="col">'.$col."</th>\n";
1819 endforeach;
1820 $body .= "</tr></thead>\n"
1821 ."<tbody>\n";
1822
1823 foreach ($mesgs as $line) :
1824 $body .= "<tr>\n";
1825 foreach ($line as $col => $cell) :
1826 if (is_numeric($cell)) :
1827 $cell = date('j-M-y, h:i a', $cell);
1828 endif;
1829 $class = strtolower(preg_replace('/\s+/', '-', $col));
1830 $body .= "<td class=\"$class\">${cell}</td>";
1831 endforeach;
1832 $body .= "</tr>\n";
1833 endforeach;
1834
1835 $body .= "</tbody>\n</table>\n\n";
1836 endif;
1837 endforeach;
1838
1839 $body = apply_filters('feedwordpress_diagnostic_email_body', $body, $dlog);
1840 if (!is_null($body)) :
1841 $home = feedwordpress_display_url(get_bloginfo('url'));
1842 $subj = apply_filters('feedwordpress_diagnostic_email_subject', $home . " syndication issues", $dlog);
1843 $agent = 'FeedWordPress '.FEEDWORDPRESS_VERSION;
1844 $body = <<<EOMAIL
1845 <html>
1846 <head>
1847 <title>$subj</title>
1848 <style type="text/css">
1849 body { background-color: white; color: black; }
1850 table { width: 100%; border: 1px solid black; }
1851 table thead tr th { background-color: #ff7700; color: white; border-bottom: 1px solid black; }
1852 table thead tr { border-bottom: 1px solid black; }
1853 table tr { vertical-align: top; }
1854 table .since { width: 20%; }
1855 table .time { width: 20%; }
1856 table .most-recently { width: 20%; }
1857 table .message { width: auto; }
1858 </style>
1859 </head>
1860 <body>
1861 <h1>Syndication Issues encountered by $agent on $home</h1>
1862 $body
1863 </body>
1864 </html>
1865
1866 EOMAIL;
1867
1868 $ded = get_option('feedwordpress_diagnostics_email_destination', 'admins');
1869
1870 // e-mail address
1871 if (preg_match('/^mailto:(.*)$/', $ded, $ref)) :
1872 $recipients = array($ref[1]);
1873
1874 // userid
1875 elseif (preg_match('/^user:(.*)$/', $ded, $ref)) :
1876 $userdata = get_userdata((int) $ref[1]);
1877 $recipients = array($userdata->user_email);
1878
1879 // admins
1880 else :
1881 $recipients = FeedWordPressDiagnostic::admin_emails();
1882 endif;
1883
1884 foreach ($recipients as $email) :
1885 add_filter('wp_mail_content_type', array('FeedWordPress', 'allow_html_mail'));
1886 wp_mail($email, $subj, $body);
1887 remove_filter('wp_mail_content_type', array('FeedWordPress', 'allow_html_mail'));
1888 endforeach;
1889 endif;
1890
1891 // Clear the logs
1892 $dlog['mesg']['persistent'] = array();
1893 $dlog['mesg']['occurrent'] = array();
1894
1895 // Set schedule for next update
1896 $dlog['schedule']['last'] = time();
1897 endif;
1898 else :
1899 $dlog['schedule'] = array(
1900 'freq' => 24 /*hr*/ * 60 /*min*/ * 60 /*s*/,
1901 'last' => time(),
1902 );
1903 endif;
1904
1905 update_option('feedwordpress_diagnostics_log', $dlog);
1906 } /* FeedWordPress::email_diagnostic_log () */
1907
1908 function allow_html_mail () {
1909 return 'text/html';
1910 } /* FeedWordPress::allow_html_mail () */
1911
1912 function admin_footer () {
1913 global $feedwordpress_admin_footer;
1914 foreach ($feedwordpress_admin_footer as $line) :
1915 echo '<div><pre>'.$line.'</pre></div>';
1916 endforeach;
1917 } /* FeedWordPress::admin_footer () */
1918
1919 function log_prefix ($date = false) {
1920 $home = get_bloginfo('url');
1921 $prefix = '['.feedwordpress_display_url($home).'] [feedwordpress] ';
1922 if ($date) :
1923 $prefix = "[".date('Y-m-d H:i:s')."]".$prefix;
1924 endif;
1925 return $prefix;
1926 } /* FeedWordPress::log_prefix () */
1927
1928 function menu_cap ($sub = false) {
1929 if ($sub) :
1930 $cap = apply_filters('feedwordpress_menu_settings_capacity', 'manage_options');
1931 else :
1932 $cap = apply_filters('feedwordpress_menu_main_capacity', 'manage_links');
1933 endif;
1934 return $cap;
1935 } /* FeedWordPress::menu_cap () */
1936
1937 function path ($filename = '') {
1938 global $fwp_path;
1939
1940 $path = $fwp_path;
1941 if (strlen($filename) > 0) :
1942 $path .= '/'.$filename;
1943 endif;
1944 return $path;
1945 }
1946
1947 function param ($key, $type = 'REQUEST', $default = NULL) {
1948 $where = '_'.strtoupper($type);
1949 $ret = $default;
1950 if (isset($GLOBALS[$where]) and is_array($GLOBALS[$where])) :
1951 if (isset($GLOBALS[$where][$key])) :
1952 $ret = $GLOBALS[$where][$key];
1953 if (get_magic_quotes_gpc()) :
1954 $ret = stripslashes_deep($ret);
1955 endif;
1956 endif;
1957 endif;
1958 return $ret;
1959 }
1960 function post ($key, $default = NULL) {
1961 return FeedWordPress::param($key, 'POST');
1962 }
1963 } // class FeedWordPress
1964
1965 $feedwordpress_admin_footer = array();
1966
1967 // take your best guess at the realname and e-mail, given a string
1968 define('FWP_REGEX_EMAIL_ADDY', '([^@"(<\s]+@[^"@(<\s]+\.[^"@(<\s]+)');
1969 define('FWP_REGEX_EMAIL_NAME', '("([^"]*)"|([^"<(]+\S))');
1970 define('FWP_REGEX_EMAIL_POSTFIX_NAME', '/^\s*'.FWP_REGEX_EMAIL_ADDY."\s+\(".FWP_REGEX_EMAIL_NAME.'\)\s*$/');
1971 define('FWP_REGEX_EMAIL_PREFIX_NAME', '/^\s*'.FWP_REGEX_EMAIL_NAME.'\s*<'.FWP_REGEX_EMAIL_ADDY.'>\s*$/');
1972 define('FWP_REGEX_EMAIL_JUST_ADDY', '/^\s*'.FWP_REGEX_EMAIL_ADDY.'\s*$/');
1973 define('FWP_REGEX_EMAIL_JUST_NAME', '/^\s*'.FWP_REGEX_EMAIL_NAME.'\s*$/');
1974
1975 function parse_email_with_realname ($email) {
1976 if (preg_match(FWP_REGEX_EMAIL_POSTFIX_NAME, $email, $matches)) :
1977 ($ret['name'] = $matches[3]) or ($ret['name'] = $matches[2]);
1978 $ret['email'] = $matches[1];
1979 elseif (preg_match(FWP_REGEX_EMAIL_PREFIX_NAME, $email, $matches)) :
1980 ($ret['name'] = $matches[2]) or ($ret['name'] = $matches[3]);
1981 $ret['email'] = $matches[4];
1982 elseif (preg_match(FWP_REGEX_EMAIL_JUST_ADDY, $email, $matches)) :
1983 $ret['name'] = NULL; $ret['email'] = $matches[1];
1984 elseif (preg_match(FWP_REGEX_EMAIL_JUST_NAME, $email, $matches)) :
1985 $ret['email'] = NULL;
1986 ($ret['name'] = $matches[2]) or ($ret['name'] = $matches[3]);
1987 else :
1988 $ret['name'] = NULL; $ret['email'] = NULL;
1989 endif;
1990 return $ret;
1991 }
1992
1993