PluginProbe
FeedWordPress / 2010.0528
FeedWordPress v2010.0528
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
← All changes | feedwordpress.php +428 -238 2009.11112010.0528 View file →
@@ -2,14 +2,19 @@
2 2 /*
3 3 Plugin Name: FeedWordPress
4 4 Plugin URI: http://feedwordpress.radgeek.com/
5 5 Description: simple and flexible Atom/RSS syndication for WordPress
6 -Version: 2009.1111
6 +Version: 2010.0528
7 7 Author: Charles Johnson
8 8 Author URI: http://radgeek.com/
9 9 License: GPL
10 10 */
11 11
12 +/**
13 + * @package FeedWordPress
14 + * @version 2010.0528
15 + */
16 +
12 17 # This uses code derived from:
13 18 # - wp-rss-aggregate.php by Kellan Elliot-McCrea <kellan@protest.net>
14 19 # - HTTP Navigator 2 by Keyvan Minoukadeh <keyvan@k1m.com>
15 20 # - Ultra-Liberal Feed Finder by Mark Pilgrim <mark@diveintomark.org>
@@ -27,9 +32,9 @@
27 32 # or use a cron job.
28 33
29 34 # -- Don't change these unless you know what you're doing...
30 35
31 -define ('FEEDWORDPRESS_VERSION', '2009.0803');
36 +define ('FEEDWORDPRESS_VERSION', '2010.0528');
32 37 define ('FEEDWORDPRESS_AUTHOR_CONTACT', 'http://radgeek.com/contact');
33 38
34 39 // Defaults
35 40 define ('DEFAULT_SYNDICATION_CATEGORY', 'Contributors');
@@ -59,14 +64,14 @@
59 64 define ('FWP_SCHEMA_25', 7558); // Database schema # for WP 2.5
60 65 define ('FWP_SCHEMA_26', 8201); // Database schema # for WP 2.6
61 66 define ('FWP_SCHEMA_27', 9872); // Database schema # for WP 2.7
62 67 define ('FWP_SCHEMA_28', 11548); // Database schema # for WP 2.8
68 +define ('FWP_SCHEMA_29', 12329); // Database schema # for WP 2.9
63 69
64 70 if (FEEDWORDPRESS_DEBUG) :
65 71 // Help us to pick out errors, if any.
66 72 ini_set('error_reporting', E_ALL & ~E_NOTICE);
67 73 ini_set('display_errors', true);
68 - define('MAGPIE_DEBUG', true);
69 74
70 75 // When testing we don't want cache issues to interfere. But this is
71 76 // a VERY BAD SETTING for a production server. Webmasters will eat your
72 77 // face for breakfast if you use it, and the baby Jesus will cry. So
@@ -71,24 +76,25 @@
71 76 // a VERY BAD SETTING for a production server. Webmasters will eat your
72 77 // face for breakfast if you use it, and the baby Jesus will cry. So
73 78 // make sure FEEDWORDPRESS_DEBUG is FALSE for any site that will be
74 79 // used for more than testing purposes!
75 - define('MAGPIE_CACHE_AGE', 1);
80 + define('FEEDWORDPRESS_CACHE_AGE', 1);
81 + define('FEEDWORDPRESS_CACHE_LIFETIME', 1);
82 + define('FEEDWORDPRESS_FETCH_TIME_OUT', 60);
76 83 else :
77 - define('MAGPIE_DEBUG', false);
78 -
79 - define('MAGPIE_CACHE_AGE', 1*60);
84 + // Hold onto data all day for conditional GET purposes,
85 + // but consider it stale after 1 min (requiring a conditional GET)
86 + define('FEEDWORDPRESS_CACHE_LIFETIME', 24*60*60);
87 + define('FEEDWORDPRESS_CACHE_AGE', 1*60);
88 + define('FEEDWORDPRESS_FETCH_TIME_OUT', 10);
80 89 endif;
81 90
82 -// Note that the rss-functions.php that comes prepackaged with WordPress is
83 -// old & busted. For the new hotness, drop a copy of rss.php from
84 -// this archive into wp-includes/rss.php
91 +// Use our the cache settings that we want.
92 +add_filter('wp_feed_cache_transient_lifetime', array('FeedWordPress', 'cache_lifetime'));
85 93
86 -if (is_readable(ABSPATH . WPINC . '/rss.php')) :
87 - require_once (ABSPATH . WPINC . '/rss.php');
88 -else :
89 - require_once (ABSPATH . WPINC . '/rss-functions.php');
90 -endif;
94 +// Ensure that we have SimplePie loaded up and ready to go.
95 +// We no longer need a MagpieRSS upgrade module. Hallelujah!
96 +require_once(ABSPATH . WPINC . '/feed.php');
91 97
92 98 if (isset($wp_db_version)) :
93 99 if ($wp_db_version >= FWP_SCHEMA_23) :
94 100 require_once (ABSPATH . WPINC . '/registration.php'); // for wp_insert_user
@@ -121,13 +127,20 @@
121 127 else : // Something went wrong. Let's just guess.
122 128 $fwp_path = 'feedwordpress';
123 129 endif;
124 130
131 +function feedwordpress_admin_scripts () {
132 + wp_enqueue_script('post'); // for magic tag and category boxes
133 + wp_enqueue_script('admin-forms'); // for checkbox selection
134 +}
135 +
125 136 // If this is a FeedWordPress admin page, queue up scripts for AJAX functions that FWP uses
126 137 // If it is a display page or a non-FeedWordPress admin page, don't.
127 138 if (is_admin() and isset($_REQUEST['page']) and preg_match("|^{$fwp_path}/|", $_REQUEST['page'])) :
128 139 if (function_exists('wp_enqueue_script')) :
129 - if (isset($wp_db_version) and $wp_db_version >= FWP_SCHEMA_25) :
140 + if (FeedWordPressCompatibility::test_version(FWP_SCHEMA_29)) :
141 + add_action('admin_print_scripts', 'feedwordpress_admin_scripts');
142 + elseif (FeedWordPressCompatibility::test_version(FWP_SCHEMA_25)) :
130 143 wp_enqueue_script('post'); // for magic tag and category boxes
131 144 wp_enqueue_script('thickbox'); // for fold-up boxes
132 145 wp_enqueue_script('admin-forms'); // for checkbox selection
133 146 else :
@@ -168,8 +181,15 @@
168 181
169 182 # Filter in original permalinks if the user wants that
170 183 add_filter('post_link', 'syndication_permalink', 1);
171 184
185 + # When foreign URLs are used for permalinks in feeds or display
186 + # contexts, they need to be escaped properly.
187 + add_filter('the_permalink', 'syndication_permalink_escaped');
188 + add_filter('the_permalink_rss', 'syndication_permalink_escaped');
189 +
190 + add_filter('post_comments_feed_link', 'syndication_comments_feed_link');
191 +
172 192 # WTF? By default, wp_insert_link runs incoming link_url and link_rss
173 193 # URIs through default filters that include `wp_kses()`. But `wp_kses()`
174 194 # just happens to escape any occurrence of & to &amp; -- which just
175 195 # happens to fuck up any URI with a & to separate GET parameters.
@@ -178,11 +198,12 @@
178 198
179 199 # Admin menu
180 200 add_action('admin_menu', 'fwp_add_pages');
181 201 add_action('admin_notices', 'fwp_check_debug');
182 - add_action('admin_notices', 'fwp_check_magpie');
183 - add_action('init', 'feedwordpress_check_for_magpie_fix');
184 202
203 + add_action('admin_menu', 'feedwordpress_add_post_edit_controls');
204 + add_action('save_post', 'feedwordpress_save_post_edit_controls');
205 +
185 206 # Inbound XML-RPC update methods
186 207 add_filter('xmlrpc_methods', 'feedwordpress_xmlrpc_hook');
187 208
188 209 # Outbound XML-RPC ping reform
@@ -204,14 +225,18 @@
204 225 add_action('feedwordpress_check_feed', 'log_feedwordpress_check_feed', 100);
205 226 add_action('feedwordpress_update_complete', 'log_feedwordpress_update_complete', 100);
206 227 endif;
207 228
208 - if (FeedWordPress::update_requested() and FEEDWORDPRESS_DEBUG) :
209 - add_action('post_syndicated_item', 'debug_out_feedwordpress_post', 100);
210 - add_action('update_syndicated_item', 'debug_out_feedwordpress_update_post', 100);
211 - add_action('feedwordpress_update', 'debug_out_feedwordpress_update_feeds', 100);
212 - add_action('feedwordpress_check_feed', 'debug_out_feedwordpress_check_feed', 100);
213 - add_action('feedwordpress_update_complete', 'debug_out_feedwordpress_update_complete', 100);
229 + if (FeedWordPress::update_requested()) :
230 + if (FEEDWORDPRESS_DEBUG) :
231 + add_action('post_syndicated_item', 'debug_out_feedwordpress_post', 100);
232 + add_action('update_syndicated_item', 'debug_out_feedwordpress_update_post', 100);
233 + add_action('feedwordpress_update', 'debug_out_feedwordpress_update_feeds', 100);
234 + add_action('feedwordpress_check_feed', 'debug_out_feedwordpress_check_feed', 100);
235 + add_action('feedwordpress_update_complete', 'debug_out_feedwordpress_update_complete', 100);
236 + endif;
237 +
238 + add_action('feedwordpress_check_feed_complete', 'debug_out_feedwordpress_feed_error', 100, 3);
214 239 endif;
215 240
216 241 # Cron-less auto-update. Hooray!
217 242 $autoUpdateHook = get_option('feedwordpress_automatic_updates');
@@ -230,55 +255,44 @@
230 255 # Hook in the menus, which will just point to the upgrade interface
231 256 add_action('admin_menu', 'fwp_add_pages');
232 257 endif; // if (!FeedWordPress::needs_upgrade())
233 258
234 -function feedwordpress_check_for_magpie_fix () {
235 - if (isset($_POST['action']) and $_POST['action']=='fix_magpie_version') :
236 - FeedWordPressCompatibility::validate_http_request(/*action=*/ 'feedwordpress_fix_magpie', /*capability=*/ 'edit_files');
237 -
238 - $back_to = $_SERVER['REQUEST_URI'];
239 - if (isset($_POST['ignore'])) :
240 - // kill error message by telling it we ignored the upgrade request for this version
241 - update_option('feedwordpress_magpie_ignored_upgrade_to', EXPECTED_MAGPIE_VERSION);
242 - $ret = 'ignored';
243 - elseif (isset($_POST['upgrade'])) :
244 - $source = dirname(__FILE__)."/MagpieRSS-upgrade/rss.php";
245 - $destination = ABSPATH . WPINC . '/rss.php';
246 - $success = @copy($source, $destination);
247 -
248 - // Copy over rss-functions.php, too, to avoid collisions
249 - // on pre-lapsarian versions of WordPress.
250 - if ($success) :
251 - $source = dirname(__FILE__)."/MagpieRSS-upgrade/rss-functions.php";
252 - $destination = ABSPATH . WPINC . '/rss-functions.php';
253 - $success = @copy($source, $destination);
254 - endif;
255 - $ret = (int) $success;
256 - endif;
257 -
258 - if (strpos($back_to, '?')===false) : $sep = '?';
259 - else : $sep = '&';
260 - endif;
261 -
262 - header("Location: {$back_to}{$sep}feedwordpress_magpie_fix=".$ret);
263 - exit;
264 - endif;
265 -} /* feedwordpress_check_for_magpie_fix() */
266 -
267 259 function feedwordpress_auto_update () {
268 260 if (FeedWordPress::stale()) :
269 - $feedwordpress =& new FeedWordPress;
261 + $feedwordpress = new FeedWordPress;
270 262 $feedwordpress->update();
271 263 endif;
272 264 } /* feedwordpress_auto_update () */
273 265
274 266 function feedwordpress_update_magic_url () {
267 + global $wpdb;
268 +
275 269 // Explicit update request in the HTTP request (e.g. from a cron job)
276 270 if (FeedWordPress::update_requested()) :
277 - $feedwordpress =& new FeedWordPress;
271 + $feedwordpress = new FeedWordPress;
278 272 $feedwordpress->update(FeedWordPress::update_requested_url());
279 273
280 - // Magic URL should return nothing but a 200 OK header packet
274 + if (FEEDWORDPRESS_DEBUG and count($wpdb->queries) > 0) :
275 + $mysqlTime = 0.0;
276 + $byTime = array();
277 + foreach ($wpdb->queries as $query) :
278 + $time = $query[1] * 1000000.0;
279 + $mysqlTime += $query[1];
280 + if (!isset($byTime[$time])) : $byTime[$time] = array(); endif;
281 + $byTime[$time][] = $query[0]. ' // STACK: ' . $query[2];
282 + endforeach;
283 + krsort($byTime);
284 +
285 + foreach ($byTime as $time => $querySet) :
286 + foreach ($querySet as $query) :
287 + print "[".(sprintf('%4.4f', $time/1000.0)) . "ms] $query\n";
288 + endforeach;
289 + endforeach;
290 + echo "[feedwordpress] $wpdb->num_queries queries. $mysqlTime seconds in MySQL. Total of "; timer_stop(1); print " seconds.";
291 + endif;
292 +
293 +
294 + // Magic URL should return nothing but a 200 OK header packet
281 295 // when successful.
282 296 exit;
283 297 endif;
284 298 } /* feedwordpress_magic_update_url () */
@@ -352,13 +366,33 @@
352 366 .(is_null($delta) ? "Error: I don't syndicate that URI"
353 367 : implode(' and ', $mesg))."\n");
354 368 }
355 369
370 +function debug_out_feedwordpress_feed_error ($feed, $added, $dt) {
371 + if (is_wp_error($added)) :
372 + $mesgs = $added->get_error_messages();
373 + foreach ($mesgs as $mesg) :
374 + echo "[feedwordpress] Error updating [{$feed['link/uri']}]: $mesg\n";
375 + endforeach;
376 + endif;
377 +}
378 +
356 379 ################################################################################
357 380 ## TEMPLATE API: functions to make your templates syndication-aware ############
358 381 ################################################################################
359 382
360 -function is_syndicated ($id = NULL) { return (strlen(get_syndication_feed_id($id)) > 0); }
383 +/**
384 + * is_syndicated: Tests whether the current post in a Loop context, or a post
385 + * given by ID number, was syndicated by FeedWordPress. Useful for templates
386 + * to determine whether or not to retrieve syndication-related meta-data in
387 + * displaying a post.
388 + *
389 + * @param int $id The post to check for syndicated status. Defaults to the current post in a Loop context.
390 + * @return bool TRUE if the post's meta-data indicates it was syndicated; FALSE otherwise
391 + */
392 +function is_syndicated ($id = NULL) {
393 + return (strlen(get_syndication_feed_id($id)) > 0);
394 +} /* function is_syndicated() */
361 395
362 396 function get_syndication_source_link ($original = NULL, $id = NULL) {
363 397 if (is_null($original)) : $original = FeedWordPress::use_aggregator_source_data();
364 398 endif;
@@ -388,8 +422,9 @@
388 422 // Reassemble bit-by-bit with minimum of crufty elements
389 423 $url = (isset($bits['user'])?$bits['user'].'@':'')
390 424 .(isset($bits['host'])?$bits['host']:'')
391 425 .(isset($bits['path'])?$bits['path']:'')
426 + .(isset($uri_bits['port'])?':'.$uri_bits['port']:'')
392 427 .(isset($bits['query'])?'?'.$bits['query']:'');
393 428
394 429 if (strlen($url) > ($before+$after)) :
395 430 $url = substr($url, 0, $before).'…'.substr($url, 0 - $after, $after);
@@ -468,22 +503,30 @@
468 503 function get_syndication_feed_id ($id = NULL) { list($u) = get_post_custom_values('syndication_feed_id', $id); return $u; }
469 504 function the_syndication_feed_id ($id = NULL) { echo get_syndication_feed_id($id); }
470 505
471 506 $feedwordpress_linkcache = array (); // only load links from database once
507 +function get_syndication_feed_object ($id = NULL) {
508 + global $feedwordpress_linkcache;
472 509
473 -function get_feed_meta ($key, $id = NULL) {
474 - global $wpdb, $feedwordpress_linkcache;
510 + $link = NULL;
511 +
475 512 $feed_id = get_syndication_feed_id($id);
476 -
477 - $ret = NULL;
478 513 if (strlen($feed_id) > 0):
479 514 if (isset($feedwordpress_linkcache[$feed_id])) :
480 515 $link = $feedwordpress_linkcache[$feed_id];
481 516 else :
482 - $link =& new SyndicatedLink($feed_id);
517 + $link = new SyndicatedLink($feed_id);
483 518 $feedwordpress_linkcache[$feed_id] = $link;
484 519 endif;
520 + endif;
521 + return $link;
522 +}
485 523
524 +function get_feed_meta ($key, $id = NULL) {
525 + $ret = NULL;
526 +
527 + $link = get_syndication_feed_object($id);
528 + if (is_object($link) and isset($link->settings[$key])) :
486 529 $ret = $link->settings[$key];
487 530 endif;
488 531 return $ret;
489 532 } /* get_feed_meta() */
@@ -494,13 +537,55 @@
494 537 function the_syndication_permalink ($id = NULL) {
495 538 echo get_syndication_permalink($id);
496 539 }
497 540
541 +/**
542 + * get_local_permalink: returns a string containing the internal permalink
543 + * for a post (whether syndicated or not) on your local WordPress installation.
544 + * This may be useful if you want permalinks to point to the original source of
545 + * an article for most purposes, but want to retrieve a URL for the local
546 + * representation of the post for one or two limited purposes (for example,
547 + * linking to a comments page on your local aggregator site).
548 + *
549 + * @param $id The numerical ID of the post to get the permalink for. If empty,
550 + * defaults to the current post in a Loop context.
551 + * @return string The URL of the local permalink for this post.
552 + *
553 + * @uses get_permalink()
554 + * @global $feedwordpress_the_original_permalink
555 + *
556 + * @since 2010.0217
557 + */
558 +function get_local_permalink ($id = NULL) {
559 + global $feedwordpress_the_original_permalink;
560 +
561 + // get permalink, and thus activate filter and force global to be filled
562 + // with original URL.
563 + $url = get_permalink($id);
564 + return $feedwordpress_the_original_permalink;
565 +} /* get_local_permalink() */
566 +
567 +/**
568 + * the_original_permalink: displays the contents of get_original_permalink()
569 + *
570 + * @param $id The numerical ID of the post to get the permalink for. If empty,
571 + * defaults to the current post in a Loop context.
572 + *
573 + * @uses get_local_permalinks()
574 + * @uses apply_filters
575 + *
576 + * @since 2010.0217
577 + */
578 +function the_local_permalink ($id = NULL) {
579 + print apply_filters('the_permalink', get_local_permalink($id));
580 +} /* function the_local_permalink() */
581 +
498 582 ################################################################################
499 583 ## FILTERS: syndication-aware handling of post data for templates and feeds ####
500 584 ################################################################################
501 585
502 586 $feedwordpress_the_syndicated_content = NULL;
587 +$feedwordpress_the_original_permalink = NULL;
503 588
504 589 function feedwordpress_preserve_syndicated_content ($text) {
505 590 global $feedwordpress_the_syndicated_content;
506 591
@@ -530,16 +615,16 @@
530 615 // In a post context....
531 616 if (is_syndicated()) :
532 617 ?>
533 618 <source>
534 - <title><?php the_syndication_source(); ?></title>
535 - <link rel="alternate" type="text/html" href="<?php the_syndication_source_link(); ?>" />
536 - <link rel="self" href="<?php the_syndication_feed(); ?>" />
619 + <title><?php print htmlspecialchars(get_syndication_source()); ?></title>
620 + <link rel="alternate" type="text/html" href="<?php print htmlspecialchars(get_syndication_source_link()); ?>" />
621 + <link rel="self" href="<?php print htmlspecialchars(get_syndication_feed()); ?>" />
537 622 <?php
538 623 $id = get_syndication_feed_guid();
539 624 if (strlen($id) > 0) :
540 625 ?>
541 - <id><?php print $id; ?></id>
626 + <id><?php print htmlspecialchars($id); ?></id>
542 627 <?php
543 628 endif;
544 629 $updated = get_feed_meta('feed/updated');
545 630 if (strlen($updated) > 0) : ?>
@@ -551,17 +636,131 @@
551 636 <?php
552 637 endif;
553 638 }
554 639
640 +/**
641 + * syndication_permalink: Allow WordPress to use the original remote URL of
642 + * syndicated posts as their permalink. Can be turned on or off by by setting in
643 + * Syndication => Posts & Links. Saves the old internal permalink in a global
644 + * variable for later use.
645 + *
646 + * @param string $permalink The internal permalink
647 + * @return string The new permalink. Same as the old if the post is not
648 + * syndicated, or if FWP is set to use internal permalinks, or if the post
649 + * was syndicated, but didn't have a proper permalink recorded.
650 + *
651 + * @uses FeedWordPress::munge_permalinks()
652 + * @uses get_syndication_permalink()
653 + * @global $feedwordpress_the_original_permalink
654 + */
555 655 function syndication_permalink ($permalink = '') {
556 - if (get_option('feedwordpress_munge_permalink') != 'no'):
557 - $uri = get_syndication_permalink();
558 - return ((strlen($uri) > 0) ? $uri : $permalink);
559 - else:
560 - return $permalink;
656 + global $feedwordpress_the_original_permalink;
657 +
658 + // Save the local permalink in case we need to retrieve it later.
659 + $feedwordpress_the_original_permalink = $permalink;
660 +
661 + // Map this permalink to a post ID so we can get the correct permalink
662 + // even outside of the Post Loop. Props Björn.
663 + $id = url_to_postid($permalink);
664 +
665 + $munge = false;
666 + $link = get_syndication_feed_object($id);
667 + if (is_object($link)) :
668 + $munge = ($link->setting('munge permalink', 'munge_permalink', 'yes') != 'no');
561 669 endif;
562 -} // function syndication_permalink ()
563 670
671 + if ($munge):
672 + $uri = get_syndication_permalink($id);
673 + $permalink = ((strlen($uri) > 0) ? $uri : $permalink);
674 + endif;
675 + return $permalink;
676 +} /* function syndication_permalink () */
677 +
678 +/**
679 + * syndication_permalink_escaped: Escape XML special characters in syndicated
680 + * permalinks when used in feed contexts and HTML display contexts.
681 + *
682 + * @param string $permalink
683 + * @return string
684 + *
685 + * @uses is_syndicated()
686 + * @uses FeedWordPress::munge_permalinks()
687 + *
688 + */
689 +function syndication_permalink_escaped ($permalink) {
690 + if (is_syndicated() and FeedWordPress::munge_permalinks()) :
691 + // This is a foreign link; WordPress can't vouch for its not
692 + // having any entities that need to be &-escaped. So we'll do
693 + // it here.
694 + $permalink = esc_html($permalink);
695 + endif;
696 + return $permalink;
697 +} /* function syndication_permalink_escaped() */
698 +
699 +/**
700 + * syndication_comments_feed_link: Escape XML special characters in comments
701 + * feed links
702 + *
703 + * @param string $link
704 + * @return string
705 + *
706 + * @uses is_syndicated()
707 + * @uses FeedWordPress::munge_permalinks()
708 + */
709 +function syndication_comments_feed_link ($link) {
710 + global $feedwordpress_the_original_permalink, $id;
711 +
712 + if (is_syndicated() and FeedWordPress::munge_permalinks()) :
713 + // If the source post provided a comment feed URL using
714 + // wfw:commentRss or atom:link/@rel="replies" we can make use of
715 + // that value here.
716 + $source = get_syndication_feed_object();
717 + $replacement = NULL;
718 + if ($source->setting('munge comments feed links', 'munge_comments_feed_links', 'yes') != 'no') :
719 + $commentFeeds = get_post_custom_values('wfw:commentRSS');
720 + if (
721 + is_array($commentFeeds)
722 + and (count($commentFeeds) > 0)
723 + and (strlen($commentFeeds[0]) > 0)
724 + ) :
725 + $replacement = $commentFeeds[0];
726 +
727 + // This is a foreign link; WordPress can't vouch for its not
728 + // having any entities that need to be &-escaped. So we'll do it
729 + // here.
730 + $replacement = esc_html($replacement);
731 + endif;
732 + endif;
733 +
734 + if (is_null($replacement)) :
735 + // Q: How can we get the proper feed format, since the
736 + // format is, stupidly, not passed to the filter?
737 + // A: Kludge kludge kludge kludge!
738 + $fancy_permalinks = ('' != get_option('permalink_structure'));
739 + if ($fancy_permalinks) :
740 + preg_match('|/feed(/([^/]+))?/?$|', $link, $ref);
741 +
742 + $format = (isset($ref[2]) ? $ref[2] : '');
743 + if (strlen($format) == 0) : $format = get_default_feed(); endif;
744 +
745 + $replacement = trailingslashit($feedwordpress_the_original_permalink) . 'feed';
746 + if ($format != get_default_feed()) :
747 + $replacement .= '/'.$format;
748 + endif;
749 + $replacement = user_trailingslashit($replacement, 'single_feed');
750 + else :
751 + // No fancy permalinks = no problem
752 + // WordPress doesn't call get_permalink() to
753 + // generate the comment feed URL, so the
754 + // comments feed link is never munged by FWP.
755 + endif;
756 + endif;
757 +
758 + if (!is_null($replacement)) : $link = $replacement; endif;
759 + endif;
760 + return $link;
761 +} /* function syndication_comments_feed_link() */
762 +
564 763 ################################################################################
565 764 ## ADMIN MENU ADD-ONS: register Dashboard management pages #####################
566 765 ################################################################################
567 766
@@ -604,97 +803,8 @@
604 803 <?php
605 804 endif;
606 805 } /* function fwp_check_debug () */
607 806
608 -define('EXPECTED_MAGPIE_VERSION', '2009.0725');
609 -function fwp_check_magpie () {
610 - if (isset($_REQUEST['feedwordpress_magpie_fix'])) :
611 - if ($_REQUEST['feedwordpress_magpie_fix']=='ignored') :
612 -?>
613 -<div class="updated fade">
614 -<p>O.K., we'll ignore the problem for now. FeedWordPress will not display any
615 -more error messages.</p>
616 -</div>
617 -<?php
618 - elseif ((bool) $_REQUEST['feedwordpress_magpie_fix']) :
619 -?>
620 -<div class="updated fade">
621 -<p>Congratulations! Your MagpieRSS has been successfully upgraded to the version
622 -shipped with FeedWordPress.</p>
623 -</div>
624 -<?php
625 - else :
626 - $source = dirname(__FILE__)."/MagpieRSS-upgrade/rss.php";
627 - $destination = ABSPATH . WPINC . '/rss.php';
628 - $cmd = "cp '".htmlspecialchars(addslashes($source))."' '".htmlspecialchars(addslashes($destination))."'";
629 - $cmd = wordwrap($cmd, /*width=*/ 75, /*break=*/ " \\\n\t");
630 -
631 -?>
632 -<div class="error">
633 -<p><strong>FeedWordPress was unable to automatically upgrade your copy of MagpieRSS.</strong></p>
634 -<p>It's likely that you need to change the file permissions on <code><?php print htmlspecialchars($source); ?></code>
635 -to allow FeedWordPress to overwrite it.</p>
636 -<p><strong>To perform the upgrade manually,</strong> you can
637 -use a SFTP or FTP client to upload a copy of <code>rss.php</code> from the
638 -<code>MagpieRSS-upgrades/</code> directory of your FeedWordPress archive so that
639 -it overwrites <code><?php print htmlspecialchars($destination); ?></code>. Or,
640 -if your web host provides shell access, you can issue the following command from
641 -a command prompt to perform the upgrade:</p>
642 -<pre>
643 -<samp>$</samp> <kbd><?php print $cmd; ?></kbd>
644 -</pre>
645 -<p><strong>If you've fixed the file permissions,</strong> you can try the
646 -automatic upgrade again.</p>
647 -
648 -<?php feedwordpress_upgrade_old_and_busted_buttons(); ?>
649 -</div>
650 -<?php
651 - endif;
652 - else :
653 - $magpie_version = FeedWordPress::magpie_version();
654 -
655 - $ignored = get_option('feedwordpress_magpie_ignored_upgrade_to');
656 - if (EXPECTED_MAGPIE_VERSION != $magpie_version and EXPECTED_MAGPIE_VERSION != $ignored) :
657 - if (current_user_can('edit_files')) :
658 - $youAre = 'you are';
659 - $itIsRecommendedThatYou = 'It is <strong>strongly recommended</strong> that you';
660 - else :
661 - $youAre = 'this site is';
662 - $itIsRecommendedThatYou = 'You may want to contact the administrator of the site; it is <strong>strongly recommended</strong> that they';
663 - endif;
664 - print '<div class="error">';
665 -?>
666 -<p style="font-style: italic"><strong>FeedWordPress has detected that <?php print $youAre; ?> currently using a version of
667 -MagpieRSS other than the upgraded version that ships with this version of FeedWordPress.</strong></p>
668 -<ul>
669 -<li><strong>Currently running:</strong> MagpieRSS <?php print $magpie_version; ?></li>
670 -<li><strong>Version included with FeedWordPress <?php print FEEDWORDPRESS_VERSION; ?>:</strong> MagpieRSS <?php print EXPECTED_MAGPIE_VERSION; ?></li>
671 -</ul>
672 -<p><?php print $itIsRecommendedThatYou; ?> install the upgraded
673 -version of MagpieRSS supplied with FeedWordPress. The version of
674 -MagpieRSS that ships with WordPress is very old and buggy, and
675 -encounters a number of errors when trying to parse modern Atom
676 -and RSS feeds.</p>
677 -<?php
678 - feedwordpress_upgrade_old_and_busted_buttons();
679 - print '</div>';
680 - endif;
681 - endif;
682 -}
683 -
684 -function feedwordpress_upgrade_old_and_busted_buttons() {
685 - if (current_user_can('edit_files')) :
686 -?>
687 -<form action="" method="post"><div>
688 -<?php FeedWordPressCompatibility::stamp_nonce('feedwordpress_fix_magpie'); ?>
689 -<input type="hidden" name="action" value="fix_magpie_version" />
690 -<input class="button-secondary" type="submit" name="ignore" value="<?php _e('Ignore this problem'); ?>" />
691 -<input class="button-primary" type="submit" name="upgrade" value="<?php _e('Upgrade'); ?>" />
692 -</div></form>
693 -<?php
694 - endif;
695 -}
696 -
697 807 ################################################################################
698 808 ## fwp_hold_pings() and fwp_release_pings(): Outbound XML-RPC ping reform ####
699 809 ## ... 'coz it's rude to send 500 pings the first time your aggregator runs ####
700 810 ################################################################################
@@ -750,8 +860,65 @@
750 860 endif;
751 861 endif;
752 862 }
753 863
864 + function feedwordpress_add_post_edit_controls () {
865 + add_meta_box('feedwordpress-post-controls', __('Syndication'), 'feedwordpress_post_edit_controls', 'post', 'side', 'high');
866 + } // function FeedWordPress::postEditControls
867 +
868 + function feedwordpress_post_edit_controls () {
869 + global $post;
870 +
871 + $frozen_values = get_post_custom_values('_syndication_freeze_updates', $post->ID);
872 + $frozen_post = (count($frozen_values) > 0 and 'yes' == $frozen_values[0]);
873 +
874 + if (is_syndicated($post->ID)) :
875 + ?>
876 + <p>This is a syndicated post, which originally appeared at
877 + <cite><?php print esc_html(get_syndication_source(NULL, $post->ID)); ?></cite>.
878 + <a href="<?php print esc_html(get_syndication_permalink($post->ID)); ?>">View original post</a>.</p>
879 +
880 + <p><input type="hidden" name="feedwordpress_noncename" id="feedwordpress_noncename" value="<?php print wp_create_nonce(plugin_basename(__FILE__)); ?>" />
881 + <label><input type="checkbox" name="freeze_updates" value="yes" <?php if ($frozen_post) : ?>checked="checked"<?php endif; ?> /> <strong>Manual editing.</strong>
882 + If set, FeedWordPress will not overwrite the changes you make manually
883 + to this post, if the syndicated content is updated on the
884 + feed.</label></p>
885 + <?php
886 + else :
887 + ?>
888 + <p>This post was created locally at this website.</p>
889 + <?php
890 + endif;
891 + } // function feedwordpress_post_edit_controls () */
892 +
893 + function feedwordpress_save_post_edit_controls ( $post_id ) {
894 + global $post;
895 +
896 + if (!isset($_POST['feedwordpress_noncename']) or !wp_verify_nonce($_POST['feedwordpress_noncename'], plugin_basename(__FILE__))) :
897 + return $post_id;
898 + endif;
899 +
900 + // Verify if this is an auto save routine. If it is our form has
901 + // not been submitted, so we don't want to do anything.
902 + if ( defined('DOING_AUTOSAVE') and DOING_AUTOSAVE ) :
903 + return $post_id;
904 + endif;
905 +
906 + // Check permissions
907 + if ( !current_user_can( 'edit_'.$_POST['post_type'], $post_id) ) :
908 + return $post_id;
909 + endif;
910 +
911 + // OK, we're golden. Now let's save some data.
912 + if (isset($_POST['freeze_updates'])) :
913 + update_post_meta($post_id, '_syndication_freeze_updates', $_POST['freeze_updates']);
914 + else :
915 + delete_post_meta($post_id, '_syndication_freeze_updates');
916 + endif;
917 +
918 + return $_POST['freeze_updates'];
919 + } // function feedwordpress_save_edit_controls
920 +
754 921 ################################################################################
755 922 ## class FeedWordPress #########################################################
756 923 ################################################################################
757 924
@@ -795,9 +962,9 @@
795 962 function FeedWordPress () {
796 963 $this->feeds = array ();
797 964 $links = FeedWordPress::syndicated_links();
798 965 if ($links): foreach ($links as $link):
799 - $this->feeds[] =& new SyndicatedLink($link);
966 + $this->feeds[] = new SyndicatedLink($link);
800 967 endforeach; endif;
801 968 } // FeedWordPress::FeedWordPress ()
802 969
803 970 # function update (): polls for updates on one or more Contributor feeds
@@ -901,10 +1068,12 @@
901 1068 $start_ts = time();
902 1069 $added = $feed->poll($crash_ts);
903 1070 do_action('feedwordpress_check_feed_complete', $feed->settings, $added, time() - $start_ts);
904 1071
905 - if (isset($added['new'])) : $delta['new'] += $added['new']; endif;
906 - if (isset($added['updated'])) : $delta['updated'] += $added['updated']; endif;
1072 + if (is_array($added)) : // Success
1073 + if (isset($added['new'])) : $delta['new'] += $added['new']; endif;
1074 + if (isset($added['updated'])) : $delta['updated'] += $added['updated']; endif;
1075 + endif;
907 1076 endif;
908 1077 endforeach;
909 1078
910 1079 do_action('feedwordpress_update_complete', $delta);
@@ -1019,8 +1188,10 @@
1019 1188 );
1020 1189
1021 1190 if (is_string($override)) :
1022 1191 $ret = strtolower($override);
1192 + else :
1193 + $ret = NULL;
1023 1194 endif;
1024 1195
1025 1196 if (!is_numeric($override) and !in_array($ret, $set[$what])) :
1026 1197 $ret = get_option('feedwordpress_unfamiliar_'.$what);
@@ -1056,8 +1227,19 @@
1056 1227 $ret = get_option('feedwordpress_use_aggregator_source_data');
1057 1228 return apply_filters('syndicated_post_use_aggregator_source_data', ($ret=='yes'));
1058 1229 }
1059 1230
1231 + /**
1232 + * FeedWordPress::munge_permalinks: check whether or not FeedWordPress
1233 + * should rewrite permalinks for syndicated items to reflect their
1234 + * original location.
1235 + *
1236 + * @return bool TRUE if FeedWordPress SHOULD rewrite permalinks; FALSE otherwise
1237 + */
1238 + /*static*/ function munge_permalinks () {
1239 + return (get_option('feedwordpress_munge_permalink', /*default=*/ 'yes') != 'no');
1240 + } /* FeedWordPress::munge_permalinks() */
1241 +
1060 1242 function syndicated_links () {
1061 1243 $contributors = FeedWordPress::link_category_id();
1062 1244 if (function_exists('get_bookmarks')) :
1063 1245 $links = get_bookmarks(array("category" => $contributors));
@@ -1141,58 +1323,14 @@
1141 1323
1142 1324 if (is_null($from) or $from <= 0.96) : $from = 0.96; endif;
1143 1325
1144 1326 switch ($from) :
1145 - case 0.96: // account for changes to syndication custom values and guid
1146 - echo "<p>Upgrading database from {$from} to ".FEEDWORDPRESS_VERSION."...</p>\n";
1327 + case 0.96:
1328 + // Dropping legacy upgrade code. If anyone is still
1329 + // using 0.96 and just now decided to upgrade, well, I'm
1330 + // sorry about that. You'll just have to cope with a few
1331 + // duplicate posts.
1147 1332
1148 - $cat_id = FeedWordPress::link_category_id();
1149 -
1150 - // Avoid duplicates
1151 - $wpdb->query("DELETE FROM `{$wpdb->postmeta}` WHERE meta_key = 'syndication_feed_id'");
1152 -
1153 - // Look up all the link IDs
1154 - $wpdb->query("
1155 - CREATE TEMPORARY TABLE tmp_custom_values
1156 - SELECT
1157 - NULL AS meta_id,
1158 - post_id,
1159 - 'syndication_feed_id' AS meta_key,
1160 - link_id AS meta_value
1161 - FROM `{$wpdb->postmeta}`, `{$wpdb->links}`
1162 - WHERE
1163 - meta_key='syndication_feed'
1164 - AND meta_value=link_rss
1165 - AND link_category = {$cat_id}
1166 - ");
1167 -
1168 - // Now attach them to their posts
1169 - $wpdb->query("INSERT INTO `{$wpdb->postmeta}` SELECT * FROM tmp_custom_values");
1170 -
1171 - // And clean up after ourselves.
1172 - $wpdb->query("DROP TABLE tmp_custom_values");
1173 -
1174 - // Now fix the guids to avoid duplicate posts
1175 - echo "<ul>";
1176 - foreach ($this->feeds as $syndicatedLink) :
1177 - $feed = $syndicatedLink->settings;
1178 - echo "<li>Fixing post meta-data for <cite>".$feed['link/name']."</cite> &#8230; "; flush();
1179 - $rss = @fetch_rss($feed['link/uri']);
1180 - if (is_array($rss->items)) :
1181 - foreach ($rss->items as $item) :
1182 - $post = new SyndicatedPost($item, $syndicatedLink);
1183 - $guid = $wpdb->escape($post->guid()); // new GUID algorithm
1184 - $link = $wpdb->escape($item['link']);
1185 -
1186 - $wpdb->query("
1187 - UPDATE `{$wpdb->posts}` SET guid='{$guid}' WHERE guid='{$link}'
1188 - ");
1189 - endforeach;
1190 - endif;
1191 - echo "<strong>complete.</strong></li>\n";
1192 - endforeach;
1193 - echo "</ul>\n";
1194 -
1195 1333 // Mark the upgrade as successful.
1196 1334 update_option('feedwordpress_version', FEEDWORDPRESS_VERSION);
1197 1335 endswitch;
1198 1336 echo "<p>Upgrade complete. FeedWordPress is now ready to use again.</p>";
@@ -1197,8 +1335,27 @@
1197 1335 endswitch;
1198 1336 echo "<p>Upgrade complete. FeedWordPress is now ready to use again.</p>";
1199 1337 } /* FeedWordPress::upgrade_database() */
1200 1338
1339 + function has_guid_index () {
1340 + global $wpdb;
1341 +
1342 + $found = false; // Guilty until proven innocent.
1343 +
1344 + $results = $wpdb->get_results("
1345 + SHOW INDEXES FROM {$wpdb->posts}
1346 + ");
1347 + if ($results) :
1348 + foreach ($results as $index) :
1349 + if (isset($index->Column_name)
1350 + and ('guid' == $index->Column_name)) :
1351 + $found = true;
1352 + endif;
1353 + endforeach;
1354 + endif;
1355 + return $found;
1356 + } /* FeedWordPress::has_guid_index () */
1357 +
1201 1358 function create_guid_index () {
1202 1359 global $wpdb;
1203 1360
1204 1361 $wpdb->query("
@@ -1205,29 +1362,71 @@
1205 1362 CREATE INDEX {$wpdb->posts}_guid_idx ON {$wpdb->posts}(guid)
1206 1363 ");
1207 1364 } /* FeedWordPress::create_guid_index () */
1208 1365
1366 + function remove_guid_index () {
1367 + global $wpdb;
1368 +
1369 + $wpdb->query("
1370 + DROP INDEX {$wpdb->posts}_guid_idx ON {$wpdb->posts}
1371 + ");
1372 + }
1373 +
1374 + /*static*/ function fetch ($url) {
1375 + require_once (ABSPATH . WPINC . '/class-feed.php');
1376 + $feed = new SimplePie();
1377 + $feed->set_feed_url($url);
1378 + $feed->set_cache_class('WP_Feed_Cache');
1379 + $feed->set_file_class('WP_SimplePie_File');
1380 + $feed->set_cache_duration(FeedWordPress::cache_duration());
1381 + $feed->init();
1382 + $feed->handle_content_type();
1383 +
1384 + if ($feed->error()) :
1385 + $ret = new WP_Error('simplepie-error', $feed->error());
1386 + else :
1387 + $ret = $feed;
1388 + endif;
1389 + return $ret;
1390 + } /* FeedWordPress::fetch () */
1391 +
1209 1392 function clear_cache () {
1210 1393 global $wpdb;
1211 1394
1212 - // MagpieRSS stores its cached feeds in options table rows with
1213 - // name = `rss_{md5 of url}` and timestamps for cached feeds in
1214 - // table rows with name = `rss_{md5 of url}_ts`. The md5 is
1215 - // always 32 characters in length, so the total option_name is
1216 - // always over 32 characters.
1395 + // The WordPress SimplePie module stores its cached feeds as
1396 + // transient records in the options table. The data itself is
1397 + // stored in `_transient_feed_{md5 of url}` and the last-modified
1398 + // timestamp in `_transient_feed_mod_{md5 of url}`. Timeouts for
1399 + // these records are stored in `_transient_timeout_feed_{md5}`.
1400 + // Since the md5 is always 32 characters in length, the
1401 + // option_name is always over 32 characters.
1217 1402 $wpdb->query("
1218 1403 DELETE FROM {$wpdb->options}
1219 - WHERE LOCATE('rss_', option_name) AND LENGTH(option_name) > 32
1404 + WHERE option_name LIKE '_transient%_feed_%' AND LENGTH(option_name) > 32
1220 1405 ");
1221 1406 } /* FeedWordPress::clear_cache () */
1222 1407
1223 - function magpie_version () {
1224 - if (!defined('MAGPIE_VERSION')) : $magpie_version = $GLOBALS['wp_version'].'-default';
1225 - else : $magpie_version = MAGPIE_VERSION;
1408 + function cache_duration () {
1409 + $duration = NULL;
1410 + if (defined('FEEDWORDPRESS_CACHE_AGE')) :
1411 + $duration = FEEDWORDPRESS_CACHE_AGE;
1226 1412 endif;
1227 - return $magpie_version;
1413 + return $duration;
1228 1414 }
1415 + function cache_lifetime ($duration) {
1416 + // Check for explicit setting of a lifetime duration
1417 + if (defined('FEEDWORDPRESS_CACHE_LIFETIME')) :
1418 + $duration = FEEDWORDPRESS_CACHE_LIFETIME;
1229 1419
1420 + // Fall back to the cache freshness duration
1421 + elseif (defined('FEEDWORDPRESS_CACHE_AGE')) :
1422 + $duration = FEEDWORDPRESS_CACHE_AGE;
1423 + endif;
1424 +
1425 + // Fall back to WordPress default
1426 + return $duration;
1427 + } /* FeedWordPress::cache_lifetime () */
1428 +
1230 1429 # Utility functions for handling text settings
1231 1430 function negative ($f, $setting) {
1232 1431 $nego = array ('n', 'no', 'f', 'false');
1233 1432 return (isset($f[$setting]) and in_array(strtolower($f[$setting]), $nego));
@@ -1242,17 +1441,12 @@
1242 1441 # Internal debugging functions
1243 1442 function critical_bug ($varname, $var, $line) {
1244 1443 global $wp_version;
1245 1444
1246 - if (defined('MAGPIE_VERSION')) : $mv = MAGPIE_VERSION;
1247 - else : $mv = 'WordPress '.$wp_version.' default.';
1248 - endif;
1249 -
1250 1445 echo '<p>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>';
1251 1446 echo "\n<plaintext>";
1252 1447 echo "Triggered at line # ".$line."\n";
1253 1448 echo "FeedWordPress version: ".FEEDWORDPRESS_VERSION."\n";
1254 - echo "MagpieRSS version: {$mv}\n";
1255 1449 echo "WordPress version: {$wp_version}\n";
1256 1450 echo "PHP version: ".phpversion()."\n";
1257 1451 echo "\n";
1258 1452 echo $varname.": "; var_dump($var); echo "\n";
@@ -1278,9 +1472,9 @@
1278 1472 return $args;
1279 1473 }
1280 1474
1281 1475 function feedwordpress_pong ($args) {
1282 - $feedwordpress =& new FeedWordPress;
1476 + $feedwordpress = new FeedWordPress;
1283 1477 $delta = @$feedwordpress->update($args[1]);
1284 1478 if (is_null($delta)):
1285 1479 return array('flerror' => true, 'message' => "Sorry. I don't syndicate <$args[1]>.");
1286 1480 else:
@@ -1291,13 +1485,9 @@
1291 1485 return array('flerror' => false, 'message' => "Thanks for the ping.".implode(' and', $mesg));
1292 1486 endif;
1293 1487 }
1294 1488
1295 -# The upgraded MagpieRSS also uses this class. So if we have it loaded
1296 -# in, don't load it again.
1297 -if (!class_exists('Relative_URI')) {
1298 - require_once(dirname(__FILE__) . '/relative_uri.class.php');
1299 -}
1489 +require_once(dirname(__FILE__) . '/relative_uri.class.php');
1300 1490
1301 1491 // take your best guess at the realname and e-mail, given a string
1302 1492 define('FWP_REGEX_EMAIL_ADDY', '([^@"(<\s]+@[^"@(<\s]+\.[^"@(<\s]+)');
1303 1493 define('FWP_REGEX_EMAIL_NAME', '("([^"]*)"|([^"<(]+\S))');