PluginProbe
FeedWordPress / 2009.0612
FeedWordPress v2009.0612
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 +707 -629 2008.10302009.0612 View file →
@@ -2,13 +2,12 @@
2 2 /*
3 3 Plugin Name: FeedWordPress
4 4 Plugin URI: http://projects.radgeek.com/feedwordpress
5 5 Description: simple and flexible Atom/RSS syndication for WordPress
6 -Version: 2008.1030
6 +Version: 2009.0612
7 7 Author: Charles Johnson
8 8 Author URI: http://radgeek.com/
9 9 License: GPL
10 -Last modified: 2008-10-30 4:14pm PDT
11 10 */
12 11
13 12 # This uses code derived from:
14 13 # - wp-rss-aggregate.php by Kellan Elliot-McCrea <kellan@protest.net>
@@ -15,24 +14,30 @@
15 14 # - HTTP Navigator 2 by Keyvan Minoukadeh <keyvan@k1m.com>
16 15 # - Ultra-Liberal Feed Finder by Mark Pilgrim <mark@diveintomark.org>
17 16 # according to the terms of the GNU General Public License.
18 17 #
19 -# INSTALLATION: see README.text or <http://projects.radgeek.com/install>
18 +# INSTALLATION: see readme.txt or <http://projects.radgeek.com/install>
20 19 #
21 20 # USAGE: once FeedWordPress is installed, you manage just about everything from
22 -# the WordPress Dashboard, under Options --> Syndication or Links --> Syndicated
23 -# To ensure that fresh content is added as it becomes available, get your
24 -# contributors to put your XML-RPC URI (if WordPress is installed at
21 +# the WordPress Dashboard, under the Syndication menu. To ensure that fresh
22 +# content is added as it becomes available, you can convince your contributors
23 +# to put your XML-RPC URI (if WordPress is installed at
25 24 # <http://www.zyx.com/blog>, XML-RPC requests should be sent to
26 -# <http://www.zyx.com/blog/xmlrpc.php>), or see `update-feeds.php`
25 +# <http://www.zyx.com/blog/xmlrpc.php>), or update manually under the
26 +# Syndication menu, or set up automatic updates under Syndication --> Settings,
27 +# or use a cron job.
27 28
28 29 # -- Don't change these unless you know what you're doing...
29 30
30 -define ('FEEDWORDPRESS_VERSION', '2008.1030');
31 +define ('FEEDWORDPRESS_VERSION', '2009.0612');
31 32 define ('FEEDWORDPRESS_AUTHOR_CONTACT', 'http://radgeek.com/contact');
32 33 define ('DEFAULT_SYNDICATION_CATEGORY', 'Contributors');
33 34
34 -define ('FEEDWORDPRESS_DEBUG', false);
35 +$feedwordpress_debug = get_option('feedwordpress_debug');
36 +if (is_string($feedwordpress_debug)) :
37 + $feedwordpress_debug = ($feedwordpress_debug == 'yes');
38 +endif;
39 +define ('FEEDWORDPRESS_DEBUG', $feedwordpress_debug);
35 40
36 41 define ('FEEDWORDPRESS_CAT_SEPARATOR_PATTERN', '/[:\n]/');
37 42 define ('FEEDWORDPRESS_CAT_SEPARATOR', "\n");
38 43
@@ -39,13 +44,15 @@
39 44 define ('FEEDVALIDATOR_URI', 'http://feedvalidator.org/check.cgi');
40 45
41 46 define ('FEEDWORDPRESS_FRESHNESS_INTERVAL', 10*60); // Every ten minutes
42 47
48 +define ('FWP_SCHEMA_HAS_USERMETA', 2966);
43 49 define ('FWP_SCHEMA_20', 3308); // Database schema # for WP 2.0
44 50 define ('FWP_SCHEMA_21', 4772); // Database schema # for WP 2.1
45 51 define ('FWP_SCHEMA_23', 5495); // Database schema # for WP 2.3
46 52 define ('FWP_SCHEMA_25', 7558); // Database schema # for WP 2.5
47 53 define ('FWP_SCHEMA_26', 8201); // Database schema # for WP 2.6
54 +define ('FWP_SCHEMA_27', 9872); // Database schema # for WP 2.7
48 55
49 56 if (FEEDWORDPRESS_DEBUG) :
50 57 // Help us to pick out errors, if any.
51 58 ini_set('error_reporting', E_ALL & ~E_NOTICE);
@@ -84,8 +91,9 @@
84 91 endif;
85 92 endif;
86 93
87 94 require_once(dirname(__FILE__) . '/compatability.php'); // LEGACY API: Replicate or mock up functions for legacy support purposes
95 +require_once(dirname(__FILE__) . '/feedwordpresshtml.class.php');
88 96
89 97 // Magic quotes are just about the stupidest thing ever.
90 98 if (is_array($_POST)) :
91 99 $fwp_post = stripslashes_deep($_POST);
@@ -159,9 +167,11 @@
159 167 remove_filter('pre_link_url', 'wp_filter_kses');
160 168
161 169 # Admin menu
162 170 add_action('admin_menu', 'fwp_add_pages');
163 -
171 + add_action('admin_notices', 'fwp_check_debug');
172 + add_action('admin_notices', 'fwp_check_magpie');
173 +
164 174 # Inbound XML-RPC update methods
165 175 add_filter('xmlrpc_methods', 'feedwordpress_xmlrpc_hook');
166 176
167 177 # Outbound XML-RPC ping reform
@@ -185,10 +195,12 @@
185 195 endif;
186 196
187 197 # Cron-less auto-update. Hooray!
188 198 add_action('init', 'feedwordpress_auto_update');
199 + add_action('init', 'feedwordpress_check_for_magpie_fix');
189 200
190 201 # Default sanitizers
202 + add_filter('syndicated_item_content', array('SyndicatedPost', 'resolve_relative_uris'), 0, 2);
191 203 add_filter('syndicated_item_content', array('SyndicatedPost', 'sanitize_content'), 0, 2);
192 204
193 205 else :
194 206 # Hook in the menus, which will just point to the upgrade interface
@@ -194,8 +206,33 @@
194 206 # Hook in the menus, which will just point to the upgrade interface
195 207 add_action('admin_menu', 'fwp_add_pages');
196 208 endif; // if (!FeedWordPress::needs_upgrade())
197 209
210 +function feedwordpress_check_for_magpie_fix () {
211 + if (isset($_POST['action']) and $_POST['action']=='fix_magpie_version') :
212 + FeedWordPressCompatibility::validate_http_request(/*action=*/ 'feedwordpress_fix_magpie', /*capability=*/ 'edit_files');
213 +
214 + $back_to = $_SERVER['REQUEST_URI'];
215 + if (isset($_POST['ignore'])) :
216 + // kill error message by telling it to expect whatever we've got
217 + update_option('feedwordpress_expected_magpie', FeedWordPress::magpie_version());
218 + $ret = 'ignored';
219 + elseif (isset($_POST['upgrade'])) :
220 + $source = dirname(__FILE__)."/MagpieRSS-upgrade/rss.php";
221 + $destination = ABSPATH . WPINC . '/rss.php';
222 + $success = @copy($source, $destination);
223 + $ret = (int) $success;
224 + endif;
225 +
226 + if (strpos($back_to, '?')===false) : $sep = '?';
227 + else : $sep = '&';
228 + endif;
229 +
230 + header("Location: {$back_to}{$sep}feedwordpress_magpie_fix=".$ret);
231 + exit;
232 + endif;
233 +} /* feedwordpress_check_for_magpie_fix() */
234 +
198 235 function feedwordpress_auto_update () {
199 236 if (FeedWordPress::stale()) :
200 237 $feedwordpress =& new FeedWordPress;
201 238 $feedwordpress->update();
@@ -248,17 +285,80 @@
248 285 ################################################################################
249 286
250 287 function is_syndicated () { return (strlen(get_syndication_feed_id()) > 0); }
251 288
252 -function the_syndication_source_link () { echo get_syndication_source_link(); }
253 -function get_syndication_source_link () { list($n) = get_post_custom_values('syndication_source_uri'); return $n; }
289 +function get_syndication_source_link ($original = NULL) {
290 + if (is_null($original)) : $original = FeedWordPress::use_aggregator_source_data();
291 + endif;
254 292
255 -function get_syndication_source () { list($n) = get_post_custom_values('syndication_source'); return $n; }
256 -function the_syndication_source () { echo get_syndication_source(); }
293 + if ($original) : $vals = get_post_custom_values('syndication_source_uri_original');
294 + else : $vals = array();
295 + endif;
296 +
297 + if (count($vals) == 0) : $vals = get_post_custom_values('syndication_source_uri');
298 + endif;
299 +
300 + if (count($vals) > 0) : $ret = $vals[0]; else : $ret = NULL; endif;
257 301
258 -function get_syndication_feed () { list($u) = get_post_custom_values('syndication_feed'); return $u; }
259 -function the_syndication_feed () { echo get_syndication_feed (); }
302 + return $ret;
303 +} /* function get_syndication_source_link() */
260 304
305 +function the_syndication_source_link ($original = NULL) { echo get_syndication_source_link($original); }
306 +
307 +function get_syndication_source ($original = NULL) {
308 + if (is_null($original)) : $original = FeedWordPress::use_aggregator_source_data();
309 + endif;
310 +
311 + if ($original) : $vals = get_post_custom_values('syndication_source_original');
312 + else : $vals = array();
313 + endif;
314 +
315 + if (count($vals) == 0) : $vals = get_post_custom_values('syndication_source');
316 + endif;
317 +
318 + if (count($vals) > 0) : $ret = $vals[0]; else : $ret = NULL; endif;
319 +
320 + return $ret;
321 +} /* function get_syndication_source() */
322 +
323 +function the_syndication_source ($original = NULL) { echo get_syndication_source($original); }
324 +
325 +function get_syndication_feed ($original = NULL) {
326 + if (is_null($original)) : $original = FeedWordPress::use_aggregator_source_data();
327 + endif;
328 +
329 + if ($original) : $vals = get_post_custom_values('syndication_feed_original');
330 + else : $vals = array();
331 + endif;
332 +
333 + if (count($vals) == 0) : $vals = get_post_custom_values('syndication_feed');
334 + endif;
335 +
336 + if (count($vals) > 0) : $ret = $vals[0]; else : $ret = NULL; endif;
337 +
338 + return $ret;
339 +} /* function get_syndication_feed() */
340 +
341 +function the_syndication_feed ($original = NULL) { echo get_syndication_feed ($original); }
342 +
343 +function get_syndication_feed_guid ($original = NULL) {
344 + if (is_null($original)) : $original = FeedWordPress::use_aggregator_source_data();
345 + endif;
346 +
347 + if ($original) : $vals = get_post_custom_values('syndication_source_id_original');
348 + else : $vals = array();
349 + endif;
350 +
351 + if (count($vals) == 0) : $vals = array(get_feed_meta('feed/id'));
352 + endif;
353 +
354 + if (count($vals) > 0) : $ret = $vals[0]; else : $ret = NULL; endif;
355 +
356 + return $ret;
357 +} /* function get_syndication_feed_guid () */
358 +
359 +function the_syndication_feed_guid ($original = NULL) { echo get_syndication_feed_guid($original); }
360 +
261 361 function get_syndication_feed_id () { list($u) = get_post_custom_values('syndication_feed_id'); return $u; }
262 362 function the_syndication_feed_id () { echo get_syndication_feed_id(); }
263 363
264 364 $feedwordpress_linkcache = array (); // only load links from database once
@@ -296,9 +396,13 @@
296 396
297 397 function feedwordpress_preserve_syndicated_content ($text) {
298 398 global $feedwordpress_the_syndicated_content;
299 399
300 - if ( is_syndicated() and get_option('feedwordpress_formatting_filters') != 'yes' ) :
400 + $globalExpose = (get_option('feedwordpress_formatting_filters') == 'yes');
401 + $localExpose = get_post_custom_values('_feedwordpress_formatting_filters');
402 + $expose = ($globalExpose or ((count($localExpose) > 0) and $localExpose[0]));
403 +
404 + if ( is_syndicated() and !$expose ) :
301 405 $feedwordpress_the_syndicated_content = $text;
302 406 else :
303 407 $feedwordpress_the_syndicated_content = NULL;
304 408 endif;
@@ -323,9 +427,9 @@
323 427 <title><?php the_syndication_source(); ?></title>
324 428 <link rel="alternate" type="text/html" href="<?php the_syndication_source_link(); ?>" />
325 429 <link rel="self" href="<?php the_syndication_feed(); ?>" />
326 430 <?php
327 - $id = get_feed_meta('feed/id');
431 + $id = get_syndication_feed_guid();
328 432 if (strlen($id) > 0) :
329 433 ?>
330 434 <id><?php print $id; ?></id>
331 435 <?php
@@ -357,13 +461,146 @@
357 461 function fwp_add_pages () {
358 462 global $fwp_capability;
359 463 global $fwp_path;
360 464
361 - add_menu_page('Syndicated Sites', 'Syndication', $fwp_capability['manage_links'], $fwp_path.'/syndication.php');
362 - add_submenu_page($fwp_path.'/syndication.php', 'Syndication Options', 'Options', $fwp_capability['manage_options'], $fwp_path.'/syndication-options.php');
363 - add_options_page('Syndication Options', 'Syndication', $fwp_capability['manage_options'], $fwp_path.'/syndication-options.php');
364 -} // function fwp_add_pages () */
465 + $menu = array('Syndicated Sites', 'Syndication', $fwp_capability['manage_links'], $fwp_path.'/syndication.php', NULL);
466 + if (fwp_test_wp_version(FWP_SCHEMA_27)) :
467 + // add icon parameter
468 + $menu[] = WP_PLUGIN_URL.'/'.$fwp_path.'/feedwordpress-tiny.png';
469 + endif;
365 470
471 + if (fwp_test_wp_version(FWP_SCHEMA_26)) :
472 + $options = __('Settings');
473 + $longoptions = __('Syndication Settings');
474 + else :
475 + $options = __('Options');
476 + $longoptions = __('Syndication Options');
477 + endif;
478 +
479 + call_user_func_array('add_menu_page', $menu);
480 + add_submenu_page($fwp_path.'/syndication.php', 'Syndicated Posts', 'Posts', $fwp_capability['manage_options'], $fwp_path.'/posts-page.php');
481 + add_submenu_page($fwp_path.'/syndication.php', 'Syndicated Authors', 'Authors', $fwp_capability['manage_options'], $fwp_path.'/authors-page.php');
482 + add_submenu_page($fwp_path.'/syndication.php', 'Categories & Tags', 'Categories & Tags', $fwp_capability['manage_options'], $fwp_path.'/categories-page.php');
483 + add_submenu_page($fwp_path.'/syndication.php', $longoptions, $options, $fwp_capability['manage_options'], $fwp_path.'/syndication-options.php');
484 +
485 + add_options_page($longoptions, 'Syndication', $fwp_capability['manage_options'], $fwp_path.'/syndication-options.php');
486 +} /* function fwp_add_pages () */
487 +
488 +function fwp_check_debug () {
489 + // This is a horrible fucking kludge that I have to do because the
490 + // admin notice code is triggered before the code that updates the
491 + // setting.
492 + if (isset($_POST['feedwordpress_debug'])) :
493 + $feedwordpress_debug = $_POST['feedwordpress_debug'];
494 + else :
495 + $feedwordpress_debug = get_option('feedwordpress_debug');
496 + endif;
497 + if ($feedwordpress_debug==='yes') :
498 +?>
499 + <div class="error">
500 +<p><strong>FeedWordPress warning.</strong> Debugging mode is <strong>ON</strong>.
501 +While it remains on, FeedWordPress displays many diagnostic error messages,
502 +warnings, and notices that are ordinarily suppressed, and also turns off all
503 +caching of feeds. Use with caution: this setting is absolutely inappropriate
504 +for a production server.</p>
505 + </div>
506 +<?php
507 + endif;
508 +} /* function fwp_check_debug () */
509 +
510 +define('DEFAULT_EXPECTED_MAGPIE_VERSION', '0.85');
511 +function fwp_check_magpie () {
512 + if (isset($_REQUEST['feedwordpress_magpie_fix'])) :
513 + if ($_REQUEST['feedwordpress_magpie_fix']=='ignored') :
514 +?>
515 +<div class="updated fade">
516 +<p>O.K., we'll ignore the problem for now. FeedWordPress will not display any
517 +more error messages.</p>
518 +</div>
519 +<?php
520 + elseif ((bool) $_REQUEST['feedwordpress_magpie_fix']) :
521 +?>
522 +<div class="updated fade">
523 +<p>Congratulations! Your MagpieRSS has been successfully upgraded to the version
524 +shipped with FeedWordPress.</p>
525 +</div>
526 +<?php
527 + else :
528 + $source = dirname(__FILE__)."/MagpieRSS-upgrade/rss.php";
529 + $destination = ABSPATH . WPINC . '/rss.php';
530 + $cmd = "cp '".htmlspecialchars(addslashes($source))."' '".htmlspecialchars(addslashes($destination))."'";
531 + $cmd = wordwrap($cmd, /*width=*/ 75, /*break=*/ " \\\n\t");
532 +
533 +?>
534 +<div class="error">
535 +<p><strong>FeedWordPress was unable to automatically upgrade your copy of MagpieRSS.</strong></p>
536 +<p>It's likely that you need to change the file permissions on <code><?php print htmlspecialchars($source); ?></code>
537 +to allow FeedWordPress to overwrite it.</p>
538 +<p><strong>To perform the upgrade manually,</strong> you can
539 +use a SFTP or FTP client to upload a copy of <code>rss.php</code> from the
540 +<code>MagpieRSS-upgrades/</code> directory of your FeedWordPress archive so that
541 +it overwrites <code><?php print htmlspecialchars($destination); ?></code>. Or,
542 +if your web host provides shell access, you can issue the following command from
543 +a command prompt to perform the upgrade:</p>
544 +<pre>
545 +<samp>$</samp> <kbd><?php print $cmd; ?></kbd>
546 +</pre>
547 +<p><strong>If you've fixed the file permissions,</strong> you can try the
548 +automatic upgrade again.</p>
549 +
550 +<?php feedwordpress_upgrade_old_and_busted_buttons(); ?>
551 +</div>
552 +<?php
553 + endif;
554 + else :
555 + $magpie_version = FeedWordPress::magpie_version();
556 +
557 + $exp = get_option('feedwordpress_expected_magpie');
558 + if ($exp) : $expected = array($exp, DEFAULT_EXPECTED_MAGPIE_VERSION);
559 + else : $expected = array(DEFAULT_EXPECTED_MAGPIE_VERSION);
560 + endif;
561 +
562 + if (in_array($magpie_version, $expected)) :
563 + if ($magpie_version != $exp) :
564 + update_option('feedwordpress_expected_magpie', $magpie_version);
565 + endif;
566 + else :
567 + if (current_user_can('edit_files')) :
568 + $youAre = 'you are';
569 + $itIsRecommendedThatYou = 'It is <strong>strongly recommended</strong> that you';
570 + else :
571 + $youAre = 'this site is';
572 + $itIsRecommendedThatYou = 'You may want to contact the administrator of the site; it is <strong>strongly recommended</strong> that they';
573 + endif;
574 + print '<div class="error">';
575 +?>
576 +<p style="font-style: italic"><strong>FeedWordPress has detected that <?php print $youAre; ?> currently using a version of
577 +MagpieRSS other than the upgraded version that ships with FeedWordPress.</strong></p>
578 +<p><?php print $itIsRecommendedThatYou; ?> install the upgraded
579 +version of MagpieRSS supplied with FeedWordPress. The version of
580 +MagpieRSS that ships with WordPress is very old and buggy, and
581 +encounters a number of errors when trying to parse modern Atom
582 +and RSS feeds.</p>
583 +<?php
584 + feedwordpress_upgrade_old_and_busted_buttons();
585 + print '</div>';
586 + endif;
587 + endif;
588 +}
589 +
590 +function feedwordpress_upgrade_old_and_busted_buttons() {
591 + if (current_user_can('edit_files')) :
592 +?>
593 +<form action="" method="post"><div>
594 +<?php FeedWordPressCompatibility::stamp_nonce('feedwordpress_fix_magpie'); ?>
595 +<input type="hidden" name="action" value="fix_magpie_version" />
596 +<input class="button-secondary" type="submit" name="ignore" value="<?php _e('Ignore this problem'); ?>" />
597 +<input class="button-primary" type="submit" name="upgrade" value="<?php _e('Upgrade'); ?>" />
598 +</div></form>
599 +<?php
600 + endif;
601 +}
602 +
366 603 ################################################################################
367 604 ## fwp_hold_pings() and fwp_release_pings(): Outbound XML-RPC ping reform ####
368 605 ## ... 'coz it's rude to send 500 pings the first time your aggregator runs ####
369 606 ################################################################################
@@ -517,9 +754,9 @@
517 754 #
518 755 # * Updates to existing posts since the last poll are mirrored in the
519 756 # WordPress store.
520 757 #
521 - function update ($uri = null) {
758 + function update ($uri = null, $crash_ts = null) {
522 759 global $wpdb;
523 760
524 761 if (FeedWordPress::needs_upgrade()) : // Will make duplicate posts if we don't hold off
525 762 return NULL;
@@ -532,11 +769,28 @@
532 769 endif;
533 770
534 771 do_action('feedwordpress_update', $uri);
535 772
773 + if (is_null($crash_ts)) :
774 + $crash_dt = (int) get_option('feedwordpress_update_time_limit');
775 + if ($crash_dt > 0) :
776 + $crash_ts = time() + $crash_dt;
777 + else :
778 + $crash_ts = NULL;
779 + endif;
780 + endif;
781 +
782 + // Randomize order for load balancing purposes
783 + $feed_set = $this->feeds;
784 + shuffle($feed_set);
785 +
536 786 // Loop through and check for new posts
537 787 $delta = NULL;
538 - foreach ($this->feeds as $feed) :
788 + foreach ($feed_set as $feed) :
789 + if (!is_null($crash_ts) and (time() > $crash_ts)) : // Check whether we've exceeded the time limit
790 + break;
791 + endif;
792 +
539 793 $pinged_that = (is_null($uri) or in_array($uri, array($feed->uri(), $feed->homepage())));
540 794
541 795 if (!is_null($uri)) : // A site-specific ping always updates
542 796 $timely = true;
@@ -549,9 +803,12 @@
549 803 endif;
550 804
551 805 if ($pinged_that and $timely) :
552 806 do_action('feedwordpress_check_feed', $feed->settings);
553 - $added = $feed->poll();
807 + $start_ts = time();
808 + $added = $feed->poll($crash_ts);
809 + do_action('feedwordpress_check_feed_complete', $feed->settings, $added, time() - $start_ts);
810 +
554 811 if (isset($added['new'])) : $delta['new'] += $added['new']; endif;
555 812 if (isset($added['updated'])) : $delta['updated'] += $added['updated']; endif;
556 813 endif;
557 814 endforeach;
@@ -609,9 +866,9 @@
609 866 if (function_exists('wp_insert_link')) { // WordPress 2.x
610 867 $link_id = wp_insert_link(array(
611 868 "link_name" => $name,
612 869 "link_url" => $uri,
613 - "link_category" => array($cat_id),
870 + "link_category" => (fwp_test_wp_version(0, FWP_SCHEMA_21) ? $cat_id : array($cat_id)),
614 871 "link_rss" => $rss
615 872 ));
616 873 } else { // WordPress 1.5.x
617 874 $result = $wpdb->query("
@@ -646,8 +903,33 @@
646 903
647 904 return $ret;
648 905 } // function FeedWordPress::on_unfamiliar()
649 906
907 + function null_email_set () {
908 + $base = get_option('feedwordpress_null_email_set');
909 +
910 + if ($base===false) :
911 + $ret = array('noreply@blogger.com'); // default
912 + else :
913 + $ret = array_map('strtolower',
914 + array_map('trim', explode("\n", $base)));
915 + endif;
916 + $ret = apply_filters('syndicated_item_author_null_email_set', $ret);
917 + return $ret;
918 +
919 + } /* FeedWordPress::null_email_set () */
920 +
921 + function is_null_email ($email) {
922 + $ret = in_array(strtolower(trim($email)), FeedWordPress::null_email_set());
923 + $ret = apply_filters('syndicated_item_author_is_null_email', $ret, $email);
924 + return $ret;
925 + } /* FeedWordPress::is_null_email () */
926 +
927 + function use_aggregator_source_data () {
928 + $ret = get_option('feedwordpress_use_aggregator_source_data');
929 + return apply_filters('syndicated_post_use_aggregator_source_data', ($ret=='yes'));
930 + }
931 +
650 932 function syndicated_links () {
651 933 $contributors = FeedWordPress::link_category_id();
652 934 if (function_exists('get_bookmarks')) :
653 935 $links = get_bookmarks(array("category" => $contributors));
@@ -672,12 +954,12 @@
672 954 // WordPress 2.3 introduces a new taxonomy/term API
673 955 if (function_exists('is_term')) :
674 956 $cat_id = is_term($cat, 'link_category');
675 957 // WordPress 2.1 and 2.2 use a common table for both link and post categories
676 - elseif (isset($wp_db_version) and ($wp_db_version >= FWP_SCHEMA_21 and $wp_db_version < FWP_SCHEMA_23) ) :
958 + elseif (fwp_test_wp_version(FWP_SCHEMA_21, FWP_SCHEMA_23)) :
677 959 $cat_id = $wpdb->get_var("SELECT cat_id FROM {$wpdb->categories} WHERE cat_name='$cat'");
678 960 // WordPress 1.5 and 2.0.x have a separate table for link categories
679 - elseif (!isset($wp_db_version) or $wp_db_version < FWP_SCHEMA_21) :
961 + elseif (fwp_test_wp_version(0, FWP_SCHEMA_21)):
680 962 $cat_id = $wpdb->get_var("SELECT cat_id FROM {$wpdb->linkcategories} WHERE cat_name='$cat'");
681 963 // This should never happen.
682 964 else :
683 965 FeedWordPress::critical_bug('FeedWordPress::link_category_id::wp_db_version', $wp_db_version, __LINE__);
@@ -690,12 +972,12 @@
690 972 if (function_exists('wp_insert_term')) :
691 973 $term = wp_insert_term($cat, 'link_category');
692 974 $cat_id = $term['term_id'];
693 975 // WordPress 2.1, 2.2 category API. By the way, why the fuck is this API function only available in a wp-admin module?
694 - elseif (function_exists('wp_insert_category')) :
976 + elseif (function_exists('wp_insert_category') and !fwp_test_wp_version(FWP_SCHEMA_20, FWP_SCHEMA_21)) :
695 977 $cat_id = wp_insert_category(array('cat_name' => $cat));
696 978 // WordPress 1.5 and 2.0.x
697 - elseif (!isset($wp_db_version) or $wp_db_version < FWP_SCHEMA_21) :
979 + elseif (fwp_test_wp_version(0, FWP_SCHEMA_21)) :
698 980 $result = $wpdb->query("
699 981 INSERT INTO $wpdb->linkcategories
700 982 SET
701 983 cat_id = 0,
@@ -805,8 +1087,23 @@
805 1087 endswitch;
806 1088 echo "<p>Upgrade complete. FeedWordPress is now ready to use again.</p>";
807 1089 } /* FeedWordPress::upgrade_database() */
808 1090
1091 + function create_guid_index () {
1092 + global $wpdb;
1093 +
1094 + $wpdb->query("
1095 + CREATE INDEX {$wpdb->posts}_guid_idx ON {$wpdb->posts}(guid)
1096 + ");
1097 + } /* FeedWordPress::create_guid_index () */
1098 +
1099 + function magpie_version () {
1100 + if (!defined('MAGPIE_VERSION')) : $magpie_version = $GLOBALS['wp_version'].'-default';
1101 + else : $magpie_version = MAGPIE_VERSION;
1102 + endif;
1103 + return $magpie_version;
1104 + }
1105 +
809 1106 # Utility functions for handling text settings
810 1107 function negative ($f, $setting) {
811 1108 $nego = array ('n', 'no', 'f', 'false');
812 1109 return (isset($f[$setting]) and in_array(strtolower($f[$setting]), $nego));
@@ -821,18 +1118,29 @@
821 1118 # Internal debugging functions
822 1119 function critical_bug ($varname, $var, $line) {
823 1120 global $wp_version;
824 1121
1122 + if (defined('MAGPIE_VERSION')) : $mv = MAGPIE_VERSION;
1123 + else : $mv = 'WordPress '.$wp_version.' default.';
1124 + endif;
1125 +
825 1126 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>';
826 1127 echo "\n<plaintext>";
827 1128 echo "Triggered at line # ".$line."\n";
828 1129 echo "FeedWordPress version: ".FEEDWORDPRESS_VERSION."\n";
829 - echo "WordPress version: $wp_version\n";
1130 + echo "MagpieRSS version: {$mv}\n";
1131 + echo "WordPress version: {$wp_version}\n";
830 1132 echo "PHP version: ".phpversion()."\n";
831 1133 echo "\n";
832 1134 echo $varname.": "; var_dump($var); echo "\n";
833 1135 die;
834 1136 }
1137 +
1138 + function noncritical_bug ($varname, $var, $line) {
1139 + if (FEEDWORDPRESS_DEBUG) : // halt only when we are doing debugging
1140 + FeedWordPress::critical_bug($varname, $var, $line);
1141 + endif;
1142 + }
835 1143 } // class FeedWordPress
836 1144
837 1145 class SyndicatedPost {
838 1146 var $item = null;
@@ -841,9 +1149,8 @@
841 1149 var $feed = null;
842 1150 var $feedmeta = null;
843 1151
844 1152 var $post = array ();
845 - var $_base = null;
846 1153
847 1154 var $_freshness = null;
848 1155 var $_wp_id = null;
849 1156
@@ -887,9 +1194,11 @@
887 1194 $this->post['named']['author'] = apply_filters('syndicated_item_author', $this->author(), $this);
888 1195
889 1196 # Identify content and sanitize it.
890 1197 # ---------------------------------
891 - if (isset($this->item['xhtml']['body'])) :
1198 + if (isset($this->item['atom_content'])) :
1199 + $content = $this->item['atom_content'];
1200 + elseif (isset($this->item['xhtml']['body'])) :
892 1201 $content = $this->item['xhtml']['body'];
893 1202 elseif (isset($this->item['xhtml']['div'])) :
894 1203 $content = $this->item['xhtml']['div'];
895 1204 elseif (isset($this->item['content']['encoded']) and $this->item['content']['encoded']):
@@ -938,8 +1247,26 @@
938 1247
939 1248 // Unique ID (hopefully a unique tag: URI); failing that, the permalink
940 1249 $this->post['guid'] = apply_filters('syndicated_item_guid', $this->guid(), $this);
941 1250
1251 + // User-supplied custom settings to apply to each post. Do first so that FWP-generated custom settings will overwrite if necessary; thus preventing any munging
1252 + $default_custom_settings = get_option('feedwordpress_custom_settings');
1253 + if ($default_custom_settings) :
1254 + $default_custom_settings = unserialize($default_custom_settings);
1255 + endif;
1256 + if (!is_array($default_custom_settings)) :
1257 + $default_custom_settings = array();
1258 + endif;
1259 +
1260 + $custom_settings = $this->link->settings['postmeta'];
1261 + if ($custom_settings) :
1262 + $custom_settings = unserialize($custom_settings);
1263 + endif;
1264 + if (!is_array($custom_settings)) :
1265 + $custom_settings = array();
1266 + endif;
1267 + $this->post['meta'] = array_merge($default_custom_settings, $custom_settings);
1268 +
942 1269 // RSS 2.0 / Atom 1.0 enclosure support
943 1270 if ( isset($this->item['enclosure#']) ) :
944 1271 for ($i = 1; $i <= $this->item['enclosure#']; $i++) :
945 1272 $eid = (($i > 1) ? "#{$id}" : "");
@@ -953,12 +1280,26 @@
953 1280 // In case you want to point back to the blog this was syndicated from
954 1281 if (isset($this->feed->channel['title'])) :
955 1282 $this->post['meta']['syndication_source'] = apply_filters('syndicated_item_source_title', $this->feed->channel['title'], $this);
956 1283 endif;
1284 +
957 1285 if (isset($this->feed->channel['link'])) :
958 1286 $this->post['meta']['syndication_source_uri'] = apply_filters('syndicated_item_source_link', $this->feed->channel['link'], $this);
959 1287 endif;
960 1288
1289 + // Make use of atom:source data, if present in an aggregated feed
1290 + if (isset($this->item['source_title'])) :
1291 + $this->post['meta']['syndication_source_original'] = $this->item['source_title'];
1292 + endif;
1293 +
1294 + if (isset($this->item['source_link'])) :
1295 + $this->post['meta']['syndication_source_uri_original'] = $this->item['source_link'];
1296 + endif;
1297 +
1298 + if (isset($this->item['source_id'])) :
1299 + $this->post['meta']['syndication_source_id_original'] = $this->item['source_id'];
1300 + endif;
1301 +
961 1302 // Store information on human-readable and machine-readable comment URIs
962 1303 if (isset($this->item['comments'])) :
963 1304 $this->post['meta']['rss:comments'] = apply_filters('syndicated_item_comments', $this->item['comments']);
964 1305 endif;
@@ -969,8 +1310,12 @@
969 1310 // Store information to identify the feed that this came from
970 1311 $this->post['meta']['syndication_feed'] = $this->feedmeta['link/uri'];
971 1312 $this->post['meta']['syndication_feed_id'] = $this->feedmeta['link/id'];
972 1313
1314 + if (isset($this->item['source_link_self'])) :
1315 + $this->post['meta']['syndication_feed_original'] = $this->item['source_link_self'];
1316 + endif;
1317 +
973 1318 // In case you want to know the external permalink...
974 1319 $this->post['meta']['syndication_permalink'] = apply_filters('syndicated_item_link', $this->item['link']);
975 1320
976 1321 // Store a hash of the post content for checking whether something needs to be updated
@@ -1011,9 +1356,9 @@
1011 1356
1012 1357 // Tags: start with default tags, if any
1013 1358 $ft = get_option("feedwordpress_syndication_tags");
1014 1359 if ($ft) :
1015 - $this->post['tags_input'] = explode('FEEDWORDPRESS_CAT_SEPARATOR', $ft);
1360 + $this->post['tags_input'] = explode(FEEDWORDPRESS_CAT_SEPARATOR, $ft);
1016 1361 else :
1017 1362 $this->post['tags_input'] = array();
1018 1363 endif;
1019 1364
@@ -1162,187 +1507,223 @@
1162 1507 } // function SyndicatedPost::store ()
1163 1508
1164 1509 function insert_new () {
1165 1510 global $wpdb, $wp_db_version;
1511 +
1512 + $dbpost = $this->normalize_post(/*new=*/ true);
1513 + if (!is_null($dbpost)) :
1514 + if ($this->use_api('wp_insert_post')) :
1515 + $dbpost['post_pingback'] = false; // Tell WP 2.1 and 2.2 not to process for pingbacks
1516 +
1517 + // This is a ridiculous fucking kludge necessitated by WordPress 2.6 munging authorship meta-data
1518 + add_action('_wp_put_post_revision', array($this, 'fix_revision_meta'));
1519 +
1520 + // Kludge to prevent kses filters from stripping the
1521 + // content of posts when updating without a logged in
1522 + // user who has `unfiltered_html` capability.
1523 + add_filter('content_save_pre', array($this, 'avoid_kses_munge'), 11);
1524 +
1525 + $this->_wp_id = wp_insert_post($dbpost);
1526 +
1527 + // Turn off ridiculous fucking kludges #1 and #2
1528 + remove_action('_wp_put_post_revision', array($this, 'fix_revision_meta'));
1529 + remove_filter('content_save_pre', array($this, 'avoid_kses_munge'), 11);
1530 +
1531 + $this->validate_post_id($dbpost, array(__CLASS__, __FUNCTION__));
1532 +
1533 + // Unfortunately, as of WordPress 2.3, wp_insert_post()
1534 + // *still* offers no way to use a guid of your choice,
1535 + // and munges your post modified timestamp, too.
1536 + $result = $wpdb->query("
1537 + UPDATE $wpdb->posts
1538 + SET
1539 + guid='{$dbpost['guid']}',
1540 + post_modified='{$dbpost['post_modified']}',
1541 + post_modified_gmt='{$dbpost['post_modified_gmt']}'
1542 + WHERE ID='{$this->_wp_id}'
1543 + ");
1544 + else :
1545 + # The right way to do this is the above. But, alas,
1546 + # in earlier versions of WordPress, wp_insert_post has
1547 + # too much behavior (mainly related to pings) that can't
1548 + # be overridden. In WordPress 1.5, it's enough of a
1549 + # resource hog to make PHP segfault after inserting
1550 + # 50-100 posts. This can get pretty annoying, especially
1551 + # if you are trying to update your feeds for the first
1552 + # time.
1553 +
1554 + $result = $wpdb->query("
1555 + INSERT INTO $wpdb->posts
1556 + SET
1557 + guid = '{$dbpost['guid']}',
1558 + post_author = '{$dbpost['post_author']}',
1559 + post_date = '{$dbpost['post_date']}',
1560 + post_date_gmt = '{$dbpost['post_date_gmt']}',
1561 + post_content = '{$dbpost['post_content']}',"
1562 + .(isset($dbpost['post_excerpt']) ? "post_excerpt = '{$dbpost['post_excerpt']}'," : "")."
1563 + post_title = '{$dbpost['post_title']}',
1564 + post_name = '{$dbpost['post_name']}',
1565 + post_modified = '{$dbpost['post_modified']}',
1566 + post_modified_gmt = '{$dbpost['post_modified_gmt']}',
1567 + comment_status = '{$dbpost['comment_status']}',
1568 + ping_status = '{$dbpost['ping_status']}',
1569 + post_status = '{$dbpost['post_status']}'
1570 + ");
1571 + $this->_wp_id = $wpdb->insert_id;
1572 +
1573 + $this->validate_post_id($dbpost, array(__CLASS__, __FUNCTION__));
1574 +
1575 + // WordPress 1.5.x - 2.0.x
1576 + wp_set_post_cats('1', $this->wp_id(), $this->post['post_category']);
1166 1577
1167 - // Why the fuck doesn't wp_insert_post already do this?
1168 - foreach ($this->post as $key => $value) :
1169 - if (is_string($value)) :
1170 - $dbpost[$key] = $wpdb->escape($value);
1171 - else :
1172 - $dbpost[$key] = $value;
1578 + // Since we are not going through official channels, we need to
1579 + // manually tell WordPress that we've published a new post.
1580 + // We need to make sure to do this in order for FeedWordPress
1581 + // to play well with the staticize-reloaded plugin (something
1582 + // that a large aggregator website is going to *want* to be
1583 + // able to use).
1584 + do_action('publish_post', $this->_wp_id);
1173 1585 endif;
1174 - endforeach;
1175 -
1176 - if (strlen($dbpost['post_title'].$dbpost['post_content'].$dbpost['post_excerpt']) == 0) :
1177 - // FIXME: Option for filtering out empty posts
1178 1586 endif;
1179 - if (strlen($dbpost['post_title'])==0) :
1180 - $dbpost['post_title'] = $this->post['meta']['syndication_source']
1181 - .' '.gmdate('Y-m-d H:i:s', $this->published() + $offset);
1182 - // FIXME: Option for what to fill a blank title with...
1183 - endif;
1587 + } /* SyndicatedPost::insert_new() */
1184 1588
1185 - if ($this->use_api('wp_insert_post')) :
1186 - $dbpost['post_pingback'] = false; // Tell WP 2.1 and 2.2 not to process for pingbacks
1589 + function update_existing () {
1590 + global $wpdb;
1187 1591
1188 - // This is a ridiculous fucking kludge necessitated by WordPress 2.6 munging authorship meta-data
1189 - add_action('_wp_put_post_revision', array($this, 'fix_revision_meta'));
1190 -
1191 - // Kludge to prevent kses filters from stripping the
1192 - // content of posts when updating without a logged in
1193 - // user who has `unfiltered_html` capability.
1194 - add_filter('content_save_pre', array($this, 'avoid_kses_munge'), 11);
1195 -
1196 - $this->_wp_id = wp_insert_post($dbpost);
1592 + // Why the fuck doesn't wp_insert_post already do this?
1593 + $dbpost = $this->normalize_post(/*new=*/ false);
1594 + if (!is_null($dbpost)) :
1595 + if ($this->use_api('wp_insert_post')) :
1596 + $dbpost['post_pingback'] = false; // Tell WP 2.1 and 2.2 not to process for pingbacks
1597 +
1598 + // This is a ridiculous fucking kludge necessitated by WordPress 2.6 munging authorship meta-data
1599 + add_action('_wp_put_post_revision', array($this, 'fix_revision_meta'));
1600 +
1601 + // Kludge to prevent kses filters from stripping the
1602 + // content of posts when updating without a logged in
1603 + // user who has `unfiltered_html` capability.
1604 + add_filter('content_save_pre', array($this, 'avoid_kses_munge'), 11);
1197 1605
1198 - // Turn off ridiculous fucking kludges #1 and #2
1199 - remove_action('_wp_put_post_revision', array($this, 'fix_revision_meta'));
1200 - remove_filter('content_save_pre', array($this, 'avoid_kses_munge'), 11);
1606 + // Don't munge status fields that the user may have reset manually
1607 + if (function_exists('get_post_field')) :
1608 + $doNotMunge = array('post_status', 'comment_status', 'ping_status');
1609 + foreach ($doNotMunge as $field) :
1610 + $dbpost[$field] = get_post_field($field, $this->wp_id());
1611 + endforeach;
1612 + endif;
1201 1613
1202 - // This should never happen.
1203 - if (!is_numeric($this->_wp_id) or ($this->_wp_id == 0)) :
1204 - FeedWordPress::critical_bug('SyndicatedPost (_wp_id problem)', array("dbpost" => $dbpost, "this" => $this), __LINE__);
1205 - endif;
1614 + $this->_wp_id = wp_insert_post($dbpost);
1206 1615
1207 - // Unfortunately, as of WordPress 2.3, wp_insert_post()
1208 - // *still* offers no way to use a guid of your choice,
1209 - // and munges your post modified timestamp, too.
1210 - $result = $wpdb->query("
1616 + // Turn off ridiculous fucking kludges #1 and #2
1617 + remove_action('_wp_put_post_revision', array($this, 'fix_revision_meta'));
1618 + remove_filter('content_save_pre', array($this, 'avoid_kses_munge'), 11);
1619 +
1620 + $this->validate_post_id($dbpost, array(__CLASS__, __FUNCTION__));
1621 +
1622 + // Unfortunately, as of WordPress 2.3, wp_insert_post()
1623 + // munges your post modified timestamp.
1624 + $result = $wpdb->query("
1625 + UPDATE $wpdb->posts
1626 + SET
1627 + post_modified='{$dbpost['post_modified']}',
1628 + post_modified_gmt='{$dbpost['post_modified_gmt']}'
1629 + WHERE ID='{$this->_wp_id}'
1630 + ");
1631 + else :
1632 +
1633 + $result = $wpdb->query("
1211 1634 UPDATE $wpdb->posts
1212 1635 SET
1213 - guid='{$dbpost['guid']}',
1214 - post_modified='{$dbpost['post_modified']}',
1215 - post_modified_gmt='{$dbpost['post_modified_gmt']}'
1216 - WHERE ID='{$this->_wp_id}'
1217 - ");
1218 - else :
1219 - # The right way to do this is the above. But, alas,
1220 - # in earlier versions of WordPress, wp_insert_post has
1221 - # too much behavior (mainly related to pings) that can't
1222 - # be overridden. In WordPress 1.5, it's enough of a
1223 - # resource hog to make PHP segfault after inserting
1224 - # 50-100 posts. This can get pretty annoying, especially
1225 - # if you are trying to update your feeds for the first
1226 - # time.
1227 -
1228 - $result = $wpdb->query("
1229 - INSERT INTO $wpdb->posts
1230 - SET
1231 - guid = '{$dbpost['guid']}',
1232 - post_author = '{$dbpost['post_author']}',
1233 - post_date = '{$dbpost['post_date']}',
1234 - post_date_gmt = '{$dbpost['post_date_gmt']}',
1235 - post_content = '{$dbpost['post_content']}',"
1236 - .(isset($dbpost['post_excerpt']) ? "post_excerpt = '{$dbpost['post_excerpt']}'," : "")."
1237 - post_title = '{$dbpost['post_title']}',
1238 - post_name = '{$dbpost['post_name']}',
1239 - post_modified = '{$dbpost['post_modified']}',
1240 - post_modified_gmt = '{$dbpost['post_modified_gmt']}',
1241 - comment_status = '{$dbpost['comment_status']}',
1242 - ping_status = '{$dbpost['ping_status']}',
1243 - post_status = '{$dbpost['post_status']}'
1244 - ");
1245 - $this->_wp_id = $wpdb->insert_id;
1246 -
1247 - // This should never happen.
1248 - if (!is_numeric($this->_wp_id) or ($this->_wp_id == 0)) :
1249 - FeedWordPress::critical_bug('SyndicatedPost (_wp_id problem)', array("dbpost" => $dbpost, "this" => $this), __LINE__);
1636 + post_author = '{$dbpost['post_author']}',
1637 + post_content = '{$dbpost['post_content']}',"
1638 + .(isset($dbpost['post_excerpt']) ? "post_excerpt = '{$dbpost['post_excerpt']}'," : "")."
1639 + post_title = '{$dbpost['post_title']}',
1640 + post_name = '{$dbpost['post_name']}',
1641 + post_modified = '{$dbpost['post_modified']}',
1642 + post_modified_gmt = '{$dbpost['post_modified_gmt']}'
1643 + WHERE guid='{$dbpost['guid']}'
1644 + ");
1645 +
1646 + // WordPress 2.1.x and up
1647 + if (function_exists('wp_set_post_categories')) :
1648 + wp_set_post_categories($this->wp_id(), $this->post['post_category']);
1649 + // WordPress 1.5.x - 2.0.x
1650 + elseif (function_exists('wp_set_post_cats')) :
1651 + wp_set_post_cats('1', $this->wp_id(), $this->post['post_category']);
1652 + // This should never happen.
1653 + else :
1654 + FeedWordPress::critical_bug(__CLASS__.'::'.__FUNCTION.'(): no post categorizing function', array("dbpost" => $dbpost, "this" => $this), __LINE__);
1655 + endif;
1656 +
1657 + // Since we are not going through official channels, we need to
1658 + // manually tell WordPress that we've published a new post.
1659 + // We need to make sure to do this in order for FeedWordPress
1660 + // to play well with the staticize-reloaded plugin (something
1661 + // that a large aggregator website is going to *want* to be
1662 + // able to use).
1663 + do_action('edit_post', $this->post['ID']);
1250 1664 endif;
1251 -
1252 - // WordPress 1.5.x - 2.0.x
1253 - wp_set_post_cats('1', $this->wp_id(), $this->post['post_category']);
1254 -
1255 - // Since we are not going through official channels, we need to
1256 - // manually tell WordPress that we've published a new post.
1257 - // We need to make sure to do this in order for FeedWordPress
1258 - // to play well with the staticize-reloaded plugin (something
1259 - // that a large aggregator website is going to *want* to be
1260 - // able to use).
1261 - do_action('publish_post', $this->_wp_id);
1262 1665 endif;
1263 - } /* SyndicatedPost::insert_new() */
1666 + } /* SyndicatedPost::update_existing() */
1264 1667
1265 - function update_existing () {
1668 + /**
1669 + * SyndicatedPost::normalize_post()
1670 + *
1671 + * @param bool $new If true, this post is to be inserted anew. If false, it is an update of an existing post.
1672 + * @return array A normalized representation of the post ready to be inserted into the database or sent to the WordPress API functions
1673 + */
1674 + function normalize_post ($new = true) {
1266 1675 global $wpdb;
1267 1676
1677 + $out = array();
1678 +
1268 1679 // Why the fuck doesn't wp_insert_post already do this?
1269 - $dbpost = array();
1270 1680 foreach ($this->post as $key => $value) :
1271 1681 if (is_string($value)) :
1272 - $dbpost[$key] = $wpdb->escape($value);
1682 + $out[$key] = $wpdb->escape($value);
1273 1683 else :
1274 - $dbpost[$key] = $value;
1684 + $out[$key] = $value;
1275 1685 endif;
1276 1686 endforeach;
1277 1687
1278 - if ($this->use_api('wp_insert_post')) :
1279 - $dbpost['post_pingback'] = false; // Tell WP 2.1 and 2.2 not to process for pingbacks
1688 + if (strlen($out['post_title'].$out['post_content'].$out['post_excerpt']) == 0) :
1689 + // FIXME: Option for filtering out empty posts
1690 + endif;
1691 + if (strlen($out['post_title'])==0) :
1692 + $offset = (int) get_option('gmt_offset') * 60 * 60;
1693 + $out['post_title'] =
1694 + $this->post['meta']['syndication_source']
1695 + .' '.gmdate('Y-m-d H:i:s', $this->published() + $offset);
1696 + // FIXME: Option for what to fill a blank title with...
1697 + endif;
1280 1698
1281 - // This is a ridiculous fucking kludge necessitated by WordPress 2.6 munging authorship meta-data
1282 - add_action('_wp_put_post_revision', array($this, 'fix_revision_meta'));
1699 + return $out;
1700 + }
1283 1701
1284 - // Kludge to prevent kses filters from stripping the
1285 - // content of posts when updating without a logged in
1286 - // user who has `unfiltered_html` capability.
1287 - add_filter('content_save_pre', array($this, 'avoid_kses_munge'), 11);
1288 -
1289 - $this->_wp_id = wp_insert_post($dbpost);
1290 -
1291 - // Turn off ridiculous fucking kludges #1 and #2
1292 - remove_action('_wp_put_post_revision', array($this, 'fix_revision_meta'));
1293 - remove_filter('content_save_pre', array($this, 'avoid_kses_munge'), 11);
1294 -
1295 - // This should never happen.
1296 - if (!is_numeric($this->_wp_id) or ($this->_wp_id == 0)) :
1297 - FeedWordPress::critical_bug('SyndicatedPost (_wp_id problem)', array("dbpost" => $dbpost, "this" => $this), __LINE__);
1298 - endif;
1299 -
1300 - // Unfortunately, as of WordPress 2.3, wp_insert_post()
1301 - // munges your post modified timestamp.
1302 - $result = $wpdb->query("
1303 - UPDATE $wpdb->posts
1304 - SET
1305 - post_modified='{$dbpost['post_modified']}',
1306 - post_modified_gmt='{$dbpost['post_modified_gmt']}'
1307 - WHERE ID='{$this->_wp_id}'
1308 - ");
1309 - else :
1310 -
1311 - $result = $wpdb->query("
1312 - UPDATE $wpdb->posts
1313 - SET
1314 - post_author = '{$dbpost['post_author']}',
1315 - post_content = '{$dbpost['post_content']}',"
1316 - .(isset($dbpost['post_excerpt']) ? "post_excerpt = '{$dbpost['post_excerpt']}'," : "")."
1317 - post_title = '{$dbpost['post_title']}',
1318 - post_name = '{$dbpost['post_name']}',
1319 - post_modified = '{$dbpost['post_modified']}',
1320 - post_modified_gmt = '{$dbpost['post_modified_gmt']}'
1321 - WHERE guid='{$dbpost['guid']}'
1322 - ");
1702 + /**
1703 + * SyndicatedPost::validate_post_id()
1704 + *
1705 + * @param array $dbpost An array representing the post we attempted to insert or update
1706 + * @param mixed $ns A string or array representing the namespace (class, method) whence this method was called.
1707 + */
1708 + function validate_post_id ($dbpost, $ns) {
1709 + if (is_array($ns)) : $ns = implode('::', $ns);
1710 + else : $ns = (string) $ns; endif;
1711 +
1712 + // This should never happen.
1713 + if (!is_numeric($this->_wp_id) or ($this->_wp_id == 0)) :
1714 + FeedWordPress::critical_bug(
1715 + /*name=*/ $ns.'::_wp_id',
1716 + /*var =*/ array(
1717 + "\$this->_wp_id" => $this->_wp_id,
1718 + "\$dbpost" => $dbpost,
1719 + "\$this" => $this
1720 + ),
1721 + /*line # =*/ __LINE__
1722 + );
1723 + endif;
1724 + } /* SyndicatedPost::validate_post_id() */
1323 1725
1324 - // WordPress 2.1.x and up
1325 - if (function_exists('wp_set_post_categories')) :
1326 - wp_set_post_categories($this->wp_id(), $this->post['post_category']);
1327 - // WordPress 1.5.x - 2.0.x
1328 - elseif (function_exists('wp_set_post_cats')) :
1329 - wp_set_post_cats('1', $this->wp_id(), $this->post['post_category']);
1330 - // This should never happen.
1331 - else :
1332 - FeedWordPress::critical_bug('SyndicatedPost (_wp_id problem)', array("dbpost" => $dbpost, "this" => $this), __LINE__);
1333 - endif;
1334 -
1335 - // Since we are not going through official channels, we need to
1336 - // manually tell WordPress that we've published a new post.
1337 - // We need to make sure to do this in order for FeedWordPress
1338 - // to play well with the staticize-reloaded plugin (something
1339 - // that a large aggregator website is going to *want* to be
1340 - // able to use).
1341 - do_action('edit_post', $this->post['ID']);
1342 - endif;
1343 - } /* SyndicatedPost::update_existing() */
1344 -
1345 1726 /**
1346 1727 * SyndicatedPost::fix_revision_meta() - Fixes the way WP 2.6+ fucks up
1347 1728 * meta-data (authorship, etc.) when storing revisions of an updated
1348 1729 * syndicated post.
@@ -1449,9 +1830,9 @@
1449 1830
1450 1831 // SyndicatedPost::author_id (): get the ID for an author name from
1451 1832 // the feed. Create the author if necessary.
1452 1833 function author_id ($unfamiliar_author = 'create') {
1453 - global $wpdb, $wp_db_version; // test for WordPress 2.0 database schema
1834 + global $wpdb;
1454 1835
1455 1836 $a = $this->author();
1456 1837 $author = $a['name'];
1457 1838 $email = $a['email'];
@@ -1456,8 +1837,15 @@
1456 1837 $author = $a['name'];
1457 1838 $email = $a['email'];
1458 1839 $url = $a['uri'];
1459 1840
1841 + $match_author_by_email = !('yes' == get_option("feedwordpress_do_not_match_author_by_email"));
1842 + if ($match_author_by_email and !FeedWordPress::is_null_email($email)) :
1843 + $test_email = $email;
1844 + else :
1845 + $test_email = NULL;
1846 + endif;
1847 +
1460 1848 // Never can be too careful...
1461 1849 $login = sanitize_user($author, /*strict=*/ true);
1462 1850 $login = apply_filters('pre_user_login', $login);
1463 1851
@@ -1466,8 +1854,9 @@
1466 1854
1467 1855 $reg_author = $wpdb->escape(preg_quote($author));
1468 1856 $author = $wpdb->escape($author);
1469 1857 $email = $wpdb->escape($email);
1858 + $test_email = $wpdb->escape($test_email);
1470 1859 $url = $wpdb->escape($url);
1471 1860
1472 1861 // Check for an existing author rule....
1473 1862 if (isset($this->link->settings['map authors']['name'][strtolower(trim($author))])) :
@@ -1486,34 +1875,10 @@
1486 1875
1487 1876 else :
1488 1877 // Check the database for an existing author record that might fit
1489 1878
1490 - #-- WordPress 1.5.x
1491 - if (!isset($wp_db_version)) :
1492 - $id = $wpdb->get_var(
1493 - "SELECT ID from $wpdb->users
1494 - WHERE
1495 - TRIM(LCASE(user_login)) = TRIM(LCASE('$login')) OR
1496 - (
1497 - LENGTH(TRIM(LCASE(user_email))) > 0
1498 - AND TRIM(LCASE(user_email)) = TRIM(LCASE('$email'))
1499 - ) OR
1500 - TRIM(LCASE(user_firstname)) = TRIM(LCASE('$author')) OR
1501 - TRIM(LCASE(user_nickname)) = TRIM(LCASE('$author')) OR
1502 - TRIM(LCASE(user_nicename)) = TRIM(LCASE('$nice_author')) OR
1503 - TRIM(LCASE(user_description)) = TRIM(LCASE('$author')) OR
1504 - (
1505 - LOWER(user_description)
1506 - RLIKE CONCAT(
1507 - '(^|\\n)a\\.?k\\.?a\\.?( |\\t)*:?( |\\t)*',
1508 - LCASE('$reg_author'),
1509 - '( |\\t|\\r)*(\\n|\$)'
1510 - )
1511 - )
1512 - ");
1513 -
1514 1879 #-- WordPress 2.0+
1515 - elseif ($wp_db_version >= 2966) :
1880 + if (fwp_test_wp_version(FWP_SCHEMA_HAS_USERMETA)) :
1516 1881
1517 1882 // First try the user core data table.
1518 1883 $id = $wpdb->get_var(
1519 1884 "SELECT ID FROM $wpdb->users
@@ -1520,9 +1885,9 @@
1520 1885 WHERE
1521 1886 TRIM(LCASE(user_login)) = TRIM(LCASE('$login'))
1522 1887 OR (
1523 1888 LENGTH(TRIM(LCASE(user_email))) > 0
1524 - AND TRIM(LCASE(user_email)) = TRIM(LCASE('$email'))
1889 + AND TRIM(LCASE(user_email)) = TRIM(LCASE('$test_email'))
1525 1890 )
1526 1891 OR TRIM(LCASE(user_nicename)) = TRIM(LCASE('$nice_author'))
1527 1892 ");
1528 1893
@@ -1542,8 +1907,33 @@
1542 1907 )
1543 1908 )
1544 1909 ");
1545 1910 endif;
1911 +
1912 + #-- WordPress 1.5.x
1913 + else :
1914 + $id = $wpdb->get_var(
1915 + "SELECT ID from $wpdb->users
1916 + WHERE
1917 + TRIM(LCASE(user_login)) = TRIM(LCASE('$login')) OR
1918 + (
1919 + LENGTH(TRIM(LCASE(user_email))) > 0
1920 + AND TRIM(LCASE(user_email)) = TRIM(LCASE('$test_email'))
1921 + ) OR
1922 + TRIM(LCASE(user_firstname)) = TRIM(LCASE('$author')) OR
1923 + TRIM(LCASE(user_nickname)) = TRIM(LCASE('$author')) OR
1924 + TRIM(LCASE(user_nicename)) = TRIM(LCASE('$nice_author')) OR
1925 + TRIM(LCASE(user_description)) = TRIM(LCASE('$author')) OR
1926 + (
1927 + LOWER(user_description)
1928 + RLIKE CONCAT(
1929 + '(^|\\n)a\\.?k\\.?a\\.?( |\\t)*:?( |\\t)*',
1930 + LCASE('$reg_author'),
1931 + '( |\\t|\\r)*(\\n|\$)'
1932 + )
1933 + )
1934 + ");
1935 +
1546 1936 endif;
1547 1937
1548 1938 // ... if you don't find one, then do what you need to do
1549 1939 if (is_null($id)) :
@@ -1596,9 +1986,9 @@
1596 1986 $cat_ids[] = $cat_id;
1597 1987 elseif (get_category($cat_id)) :
1598 1988 $cat_ids[] = $cat_id;
1599 1989 endif;
1600 - else :
1990 + elseif (strlen($cat_name) > 0) :
1601 1991 $esc = $wpdb->escape($cat_name);
1602 1992 $resc = $wpdb->escape(preg_quote($cat_name));
1603 1993
1604 1994 // WordPress 2.3+
@@ -1620,9 +2010,13 @@
1620 2010 elseif ('tag'==$unfamiliar_category) :
1621 2011 $tags[] = $cat_name;
1622 2012 elseif ('create'===$unfamiliar_category) :
1623 2013 $term = wp_insert_term($cat_name, 'category');
1624 - $cat_ids[] = $term['term_id'];
2014 + if (is_wp_error($term)) :
2015 + FeedWordPress::noncritical_bug('term insertion problem', array('cat_name' => $cat_name, 'term' => $term, 'this' => $this), __LINE__);
2016 + else :
2017 + $cat_ids[] = $term['term_id'];
2018 + endif;
1625 2019 endif;
1626 2020
1627 2021 // WordPress 1.5.x - 2.2.x
1628 2022 else :
@@ -1854,25 +2248,87 @@
1854 2248
1855 2249 return $author;
1856 2250 } // SyndicatedPost::author()
1857 2251
2252 + var $uri_attrs = array (
2253 + array('a', 'href'),
2254 + array('applet', 'codebase'),
2255 + array('area', 'href'),
2256 + array('blockquote', 'cite'),
2257 + array('body', 'background'),
2258 + array('del', 'cite'),
2259 + array('form', 'action'),
2260 + array('frame', 'longdesc'),
2261 + array('frame', 'src'),
2262 + array('iframe', 'longdesc'),
2263 + array('iframe', 'src'),
2264 + array('head', 'profile'),
2265 + array('img', 'longdesc'),
2266 + array('img', 'src'),
2267 + array('img', 'usemap'),
2268 + array('input', 'src'),
2269 + array('input', 'usemap'),
2270 + array('ins', 'cite'),
2271 + array('link', 'href'),
2272 + array('object', 'classid'),
2273 + array('object', 'codebase'),
2274 + array('object', 'data'),
2275 + array('object', 'usemap'),
2276 + array('q', 'cite'),
2277 + array('script', 'src')
2278 + ); /* var SyndicatedPost::$uri_attrs */
2279 +
2280 + var $_base = null;
2281 +
2282 + function resolve_single_relative_uri ($refs) {
2283 + $tag = FeedWordPressHTML::attributeMatch($refs);
2284 + $url = Relative_URI::resolve($tag['value'], $this->_base);
2285 + return $tag['prefix'] . $url . $tag['suffix'];
2286 + } /* function SyndicatedPost::resolve_single_relative_uri() */
2287 +
2288 + function resolve_relative_uris ($content, $obj) {
2289 + # The MagpieRSS upgrade has some `xml:base` support baked in.
2290 + # However, sometimes people do silly things, like putting
2291 + # relative URIs out on a production RSS 2.0 feed or other feeds
2292 + # with no good support for `xml:base`. So we'll do our best to
2293 + # try to catch any remaining relative URIs and resolve them as
2294 + # best we can.
2295 + $obj->_base = $obj->item['link']; // Reset the base for resolving relative URIs
2296 +
2297 + foreach ($obj->uri_attrs as $pair) :
2298 + list($tag, $attr) = $pair;
2299 + $pattern = FeedWordPressHTML::attributeRegex($tag, $attr);
2300 + $content = preg_replace_callback (
2301 + $pattern,
2302 + array(&$obj, 'resolve_single_relative_uri'),
2303 + $content
2304 + );
2305 + endforeach;
2306 +
2307 + return $content;
2308 + } /* function SyndicatedPost::resolve_relative_uris () */
2309 +
1858 2310 var $strip_attrs = array (
1859 - array('[a-z]+', 'style'),
1860 - array('[a-z]+', 'target'),
2311 + array('[a-z]+', 'target'),
2312 +// array('[a-z]+', 'style'),
2313 +// array('[a-z]+', 'on[a-z]+'),
1861 2314 );
2315 +
2316 + function strip_attribute_from_tag ($refs) {
2317 + $tag = FeedWordPressHTML::attributeMatch($refs);
2318 + return $tag['before_attribute'].$tag['after_attribute'];
2319 + }
2320 +
1862 2321 function sanitize_content ($content, $obj) {
1863 - # FeedWordPress used to resolve URIs relative to the
1864 - # feed URI. It now relies on the xml:base support
1865 - # baked in to the MagpieRSS upgrade. So all we do here
1866 - # now is to sanitize problematic attributes.
1867 - #
1868 2322 # This kind of sucks. I intend to replace it with
1869 2323 # lib_filter sometime soon.
1870 2324 foreach ($obj->strip_attrs as $pair):
1871 2325 list($tag,$attr) = $pair;
1872 - $content = preg_replace (
1873 - ":(<$tag [^>]*)($attr=(\"[^\">]*\"|[^>\\s]+))([^>]*>):i",
1874 - "\\1\\4",
2326 + $pattern = FeedWordPressHTML::attributeRegex($tag, $attr);
2327 +
2328 + $content = preg_replace_callback (
2329 + $pattern,
2330 + array(&$obj, 'strip_attribute_from_tag'),
1875 2331 $content
1876 2332 );
1877 2333 endforeach;
1878 2334 return $content;
@@ -1878,397 +2334,10 @@
1878 2334 return $content;
1879 2335 }
1880 2336 } // class SyndicatedPost
1881 2337
1882 -# class SyndicatedLink: represents a syndication feed stored within the
1883 -# WordPress database
1884 -#
1885 -# To keep things compact and editable from within WordPress, we use all the
1886 -# links under a particular category in the WordPress "Blogroll" for the list of
1887 -# feeds to syndicate. "Contributors" is the category used by default; you can
1888 -# configure that under Options --> Syndication.
1889 -#
1890 -# Fields used are:
1891 -#
1892 -# * link_rss: the URI of the Atom/RSS feed to syndicate
1893 -#
1894 -# * link_notes: user-configurable options, with keys and values
1895 -# like so:
1896 -#
1897 -# key: value
1898 -# cats: computers\nweb
1899 -# feed/key: value
1900 -#
1901 -# Keys that start with "feed/" are gleaned from the data supplied
1902 -# by the feed itself, and will be overwritten with each update.
1903 -#
1904 -# Values have linebreak characters escaped with C-style
1905 -# backslashes (so, for example, a newline becomes "\n").
1906 -#
1907 -# The value of `cats` is used as a newline-separated list of
1908 -# default categories for any post coming from a particular feed.
1909 -# (In the example above, any posts from this feed will be placed
1910 -# in the "computers" and "web" categories--*in addition to* any
1911 -# categories that may already be applied to the posts.)
1912 -#
1913 -# Values of keys in link_notes are accessible from templates using
1914 -# the function `get_feed_meta($key)` if this plugin is activated.
2338 +require_once(dirname(__FILE__) . '/syndicatedlink.class.php');
1915 2339
1916 -class SyndicatedLink {
1917 - var $id = null;
1918 - var $link = null;
1919 - var $settings = array ();
1920 - var $magpie = null;
1921 -
1922 - function SyndicatedLink ($link) {
1923 - global $wpdb;
1924 -
1925 - if (is_object($link)) :
1926 - $this->link = $link;
1927 - $this->id = $link->link_id;
1928 - else :
1929 - $this->id = $link;
1930 - if (function_exists('get_bookmark')) : // WP 2.1+
1931 - $this->link = get_bookmark($link);
1932 - else :
1933 - $this->link = $wpdb->get_row("
1934 - SELECT * FROM $wpdb->links
1935 - WHERE (link_id = '".$wpdb->escape($link)."')"
1936 - );
1937 - endif;
1938 - endif;
1939 -
1940 - if (strlen($this->link->link_rss) > 0) :
1941 - // Read off feed settings from link_notes
1942 - $notes = explode("\n", $this->link->link_notes);
1943 - foreach ($notes as $note):
1944 - list($key, $value) = explode(": ", $note, 2);
1945 -
1946 - if (strlen($key) > 0) :
1947 - // Unescape and trim() off the whitespace.
1948 - // Thanks to Ray Lischner for pointing out the
1949 - // need to trim off whitespace.
1950 - $this->settings[$key] = stripcslashes (trim($value));
1951 - endif;
1952 - endforeach;
1953 -
1954 - // "Magic" feed settings
1955 - $this->settings['link/uri'] = $this->link->link_rss;
1956 - $this->settings['link/name'] = $this->link->link_name;
1957 - $this->settings['link/id'] = $this->link->link_id;
1958 -
1959 - // `hardcode categories` and `unfamiliar categories` are deprecated in favor of `unfamiliar category`
1960 - if (
1961 - isset($this->settings['unfamiliar categories'])
1962 - and !isset($this->settings['unfamiliar category'])
1963 - ) :
1964 - $this->settings['unfamiliar category'] = $this->settings['unfamiliar categories'];
1965 - endif;
1966 - if (
1967 - FeedWordPress::affirmative($this->settings, 'hardcode categories')
1968 - and !isset($this->settings['unfamiliar category'])
1969 - ) :
1970 - $this->settings['unfamiliar category'] = 'default';
1971 - endif;
1972 -
1973 - // Set this up automagically for del.icio.us
1974 - $bits = parse_url($this->link->link_rss);
1975 - $tagspacers = array('del.icio.us', 'feeds.delicious.com');
1976 - if (!isset($this->settings['cat_split']) and in_array($bits['host'], $tagspacers)) :
1977 - $this->settings['cat_split'] = '\s'; // Whitespace separates multiple tags in del.icio.us RSS feeds
1978 - endif;
1979 -
1980 - if (isset($this->settings['cats'])):
1981 - $this->settings['cats'] = preg_split(FEEDWORDPRESS_CAT_SEPARATOR_PATTERN, $this->settings['cats']);
1982 - endif;
1983 - if (isset($this->settings['tags'])):
1984 - $this->settings['tags'] = preg_split(FEEDWORDPRESS_CAT_SEPARATOR_PATTERN, $this->settings['tags']);
1985 - endif;
1986 -
1987 - if (isset($this->settings['map authors'])) :
1988 - $author_rules = explode("\n\n", $this->settings['map authors']);
1989 - $ma = array();
1990 - foreach ($author_rules as $rule) :
1991 - list($rule_type, $author_name, $author_action) = explode("\n", $rule);
1992 -
1993 - // Normalize for case and whitespace
1994 - $rule_type = strtolower(trim($rule_type));
1995 - $author_name = strtolower(trim($author_name));
1996 - $author_action = strtolower(trim($author_action));
1997 -
1998 - $ma[$rule_type][$author_name] = $author_action;
1999 - endforeach;
2000 - $this->settings['map authors'] = $ma;
2001 - endif;
2002 - endif;
2003 - } // SyndicatedLink::SyndicatedLink ()
2004 -
2005 - function found () {
2006 - return is_object($this->link);
2007 - }
2008 -
2009 - function stale () {
2010 - $stale = true;
2011 - if (isset($this->settings['update/hold']) and ($this->settings['update/hold']=='ping')) :
2012 - $stale = false; // don't update on any timed updates; pings only
2013 - elseif (isset($this->settings['update/hold']) and ($this->settings['update/hold']=='next')) :
2014 - $stale = true; // update on the next timed update
2015 - elseif (!isset($this->settings['update/ttl']) or !isset($this->settings['update/last'])) :
2016 - $stale = true; // initial update
2017 - else :
2018 - $after = ((int) $this->settings['update/last'])
2019 - +((int) $this->settings['update/ttl'] * 60);
2020 - $stale = (time() >= $after);
2021 - endif;
2022 - return $stale;
2023 - }
2024 -
2025 - function poll () {
2026 - global $wpdb;
2027 -
2028 - $this->magpie = fetch_rss($this->link->link_rss);
2029 - $new_count = NULL;
2030 -
2031 - if (is_object($this->magpie)) :
2032 - $new_count = array('new' => 0, 'updated' => 0);
2033 -
2034 - # -- Update Link metadata live from feed
2035 - $channel = $this->magpie->channel;
2036 -
2037 - if (!isset($channel['id'])) :
2038 - $channel['id'] = $this->link->link_rss;
2039 - endif;
2040 -
2041 - $update = array();
2042 - if (!$this->hardcode('url') and isset($channel['link'])) :
2043 - $update[] = "link_url = '".$wpdb->escape($channel['link'])."'";
2044 - endif;
2045 -
2046 - if (!$this->hardcode('name') and isset($channel['title'])) :
2047 - $update[] = "link_name = '".$wpdb->escape($channel['title'])."'";
2048 - endif;
2049 -
2050 - if (!$this->hardcode('description')) :
2051 - if (isset($channel['tagline'])) :
2052 - $update[] = "link_description = '".$wpdb->escape($channel['tagline'])."'";
2053 - elseif (isset($channel['description'])) :
2054 - $update[] = "link_description = '".$wpdb->escape($channel['description'])."'";
2055 - endif;
2056 - endif;
2057 -
2058 - $this->settings = array_merge($this->settings, $this->flatten_array($channel));
2059 -
2060 - $this->settings['update/last'] = time(); $ttl = $this->ttl();
2061 - if (!is_null($ttl)) :
2062 - $this->settings['update/ttl'] = $ttl;
2063 - $this->settings['update/timed'] = 'feed';
2064 - else :
2065 - $this->settings['update/ttl'] = rand(30, 120); // spread over time interval for staggered updates
2066 - $this->settings['update/timed'] = 'automatically';
2067 - endif;
2068 -
2069 - if (!isset($this->settings['update/hold']) or $this->settings['update/hold']!='ping') :
2070 - $this->settings['update/hold'] = 'scheduled';
2071 - endif;
2072 -
2073 - // Copy back without a few things that we don't want to save in the notes
2074 - $to_notes = $this->settings;
2075 -
2076 - if (is_array($to_notes['cats'])) :
2077 - $to_notes['cats'] = implode(FEEDWORDPRESS_CAT_SEPARATOR, $to_notes['cats']);
2078 - endif;
2079 - if (is_array($to_notes['tags'])) :
2080 - $to_notes['tags'] = implode(FEEDWORDPRESS_CAT_SEPARATOR, $to_notes['tags']);
2081 - endif;
2082 -
2083 - if (isset($to_notes['map authors'])) :
2084 - $ma = array();
2085 - foreach ($to_notes['map authors'] as $rule_type => $author_rules) :
2086 - foreach ($author_rules as $author_name => $author_action) :
2087 - $ma[] = $rule_type."\n".$author_name."\n".$author_action;
2088 - endforeach;
2089 - endforeach;
2090 - $to_notes['map authors'] = implode("\n\n", $ma);
2091 - endif;
2092 -
2093 - unset($to_notes['link/id']); unset($to_notes['link/uri']);
2094 - unset($to_notes['link/name']);
2095 - unset($to_notes['hardcode categories']); // Deprecated
2096 - unset($to_notes['unfamiliar categories']); // Deprecated
2097 -
2098 - $notes = '';
2099 - foreach ($to_notes as $key => $value) :
2100 - $notes .= $key . ": ". addcslashes($value, "\0..\37".'\\') . "\n";
2101 - endforeach;
2102 - $update[] = "link_notes = '".$wpdb->escape($notes)."'";
2103 -
2104 - $update_set = implode(',', $update);
2105 -
2106 - // Update the properties of the link from the feed information
2107 - $result = $wpdb->query("
2108 - UPDATE $wpdb->links
2109 - SET $update_set
2110 - WHERE link_id='$this->id'
2111 - ");
2112 -
2113 - # -- Add new posts from feed and update any updated posts
2114 - if (is_array($this->magpie->items)) :
2115 - foreach ($this->magpie->items as $item) :
2116 - $post =& new SyndicatedPost($item, $this);
2117 - if (!$post->filtered()) :
2118 - $new = $post->store();
2119 - if ( $new !== false ) $new_count[$new]++;
2120 - endif;
2121 - endforeach;
2122 - endif;
2123 -
2124 - // Copy back any changes to feed settings made in the course of updating (e.g. new author rules)
2125 - $to_notes = $this->settings;
2126 -
2127 - if (is_array($to_notes['cats'])) :
2128 - $to_notes['cats'] = implode(FEEDWORDPRESS_CAT_SEPARATOR, $to_notes['cats']);
2129 - endif;
2130 - if (is_array($to_notes['tags'])) :
2131 - $to_notes['tags'] = implode(FEEDWORDPRESS_CAT_SEPARATOR, $to_notes['tags']);
2132 - endif;
2133 -
2134 - if (isset($to_notes['map authors'])) :
2135 - $ma = array();
2136 - foreach ($to_notes['map authors'] as $rule_type => $author_rules) :
2137 - foreach ($author_rules as $author_name => $author_action) :
2138 - $ma[] = $rule_type."\n".$author_name."\n".$author_action;
2139 - endforeach;
2140 - endforeach;
2141 - $to_notes['map authors'] = implode("\n\n", $ma);
2142 - endif;
2143 -
2144 - unset($to_notes['link/id']); unset($to_notes['link/uri']);
2145 - unset($to_notes['link/name']);
2146 - unset($to_notes['hardcode categories']); // Deprecated
2147 - unset($to_notes['unfamiliar categories']); // Deprecated
2148 -
2149 - $notes = '';
2150 - foreach ($to_notes as $key => $value) :
2151 - $notes .= $key . ": ". addcslashes($value, "\0..\37".'\\') . "\n";
2152 - endforeach;
2153 -
2154 - $update_set = "link_notes = '".$wpdb->escape($notes)."'";
2155 -
2156 - // Update the properties of the link from the feed information
2157 - $result = $wpdb->query("
2158 - UPDATE $wpdb->links
2159 - SET $update_set
2160 - WHERE link_id='$this->id'
2161 - ");
2162 - endif;
2163 - return $new_count;
2164 - } /* SyndicatedLink::poll() */
2165 -
2166 - function uri () {
2167 - return (is_object($this->link) ? $this->link->link_rss : NULL);
2168 - }
2169 - function homepage () {
2170 - return (isset($this->settings['feed/link']) ? $this->settings['feed/link'] : NULL);
2171 - }
2172 -
2173 - function ttl () {
2174 - if (is_object($this->magpie)) :
2175 - $channel = $this->magpie->channel;
2176 - else :
2177 - $channel = array();
2178 - endif;
2179 -
2180 - if (isset($channel['ttl'])) :
2181 - // "ttl stands for time to live. It's a number of
2182 - // minutes that indicates how long a channel can be
2183 - // cached before refreshing from the source."
2184 - // <http://blogs.law.harvard.edu/tech/rss#ltttlgtSubelementOfLtchannelgt>
2185 - $ret = $channel['ttl'];
2186 - elseif (isset($channel['sy']['updatefrequency']) or isset($channel['sy']['updateperiod'])) :
2187 - $period_minutes = array (
2188 - 'hourly' => 60, /* minutes in an hour */
2189 - 'daily' => 1440, /* minutes in a day */
2190 - 'weekly' => 10080, /* minutes in a week */
2191 - 'monthly' => 43200, /* minutes in a month */
2192 - 'yearly' => 525600, /* minutes in a year */
2193 - );
2194 -
2195 - // "sy:updatePeriod: Describes the period over which the
2196 - // channel format is updated. Acceptable values are:
2197 - // hourly, daily, weekly, monthly, yearly. If omitted,
2198 - // daily is assumed." <http://web.resource.org/rss/1.0/modules/syndication/>
2199 - if (isset($channel['sy']['updateperiod'])) : $period = $channel['sy']['updateperiod'];
2200 - else : $period = 'daily';
2201 - endif;
2202 -
2203 - // "sy:updateFrequency: Used to describe the frequency
2204 - // of updates in relation to the update period. A
2205 - // positive integer indicates how many times in that
2206 - // period the channel is updated. ... If omitted a value
2207 - // of 1 is assumed." <http://web.resource.org/rss/1.0/modules/syndication/>
2208 - if (isset($channel['sy']['updatefrequency'])) : $freq = (int) $channel['sy']['updatefrequency'];
2209 - else : $freq = 1;
2210 - endif;
2211 -
2212 - $ret = (int) ($period_minutes[$period] / $freq);
2213 - else :
2214 - $ret = NULL;
2215 - endif;
2216 - return $ret;
2217 - } /* SyndicatedLink::ttl() */
2218 -
2219 - // SyndicatedLink::flatten_array (): flatten an array. Useful for
2220 - // hierarchical and namespaced elements.
2221 - //
2222 - // Given an array which may contain array or object elements in it,
2223 - // return a "flattened" array: a one-dimensional array of scalars
2224 - // containing each of the scalar elements contained within the array
2225 - // structure. Thus, for example, if $a['b']['c']['d'] == 'e', then the
2226 - // returned array for FeedWordPress::flatten_array($a) will contain a key
2227 - // $a['feed/b/c/d'] with value 'e'.
2228 - function flatten_array ($arr, $prefix = 'feed/', $separator = '/') {
2229 - $ret = array ();
2230 - if (is_array($arr)) :
2231 - foreach ($arr as $key => $value) :
2232 - if (is_scalar($value)) :
2233 - $ret[$prefix.$key] = $value;
2234 - else :
2235 - $ret = array_merge($ret, $this->flatten_array($value, $prefix.$key.$separator, $separator));
2236 - endif;
2237 - endforeach;
2238 - endif;
2239 - return $ret;
2240 - } // function SyndicatedLink::flatten_array ()
2241 -
2242 - function hardcode ($what) {
2243 - $default = get_option("feedwordpress_hardcode_$what");
2244 - if ( $default === 'yes' ) :
2245 - // If the default is to hardcode, then we want the
2246 - // negation of negative(): TRUE by default and FALSE if
2247 - // the setting is explicitly "no"
2248 - $ret = !FeedWordPress::negative($this->settings, "hardcode $what");
2249 - else :
2250 - // If the default is NOT to hardcode, then we want
2251 - // affirmative(): FALSE by default and TRUE if the
2252 - // setting is explicitly "yes"
2253 - $ret = FeedWordPress::affirmative($this->settings, "hardcode $what");
2254 - endif;
2255 - return $ret;
2256 - } // function SyndicatedLink::hardcode ()
2257 -
2258 - function syndicated_status ($what, $default) {
2259 - global $wpdb;
2260 -
2261 - $ret = get_option("feedwordpress_syndicated_{$what}_status");
2262 - if ( isset($this->settings["$what status"]) ) :
2263 - $ret = $this->settings["$what status"];
2264 - elseif (!$ret) :
2265 - $ret = $default;
2266 - endif;
2267 - return $wpdb->escape(trim(strtolower($ret)));
2268 - } // function SyndicatedLink:syndicated_status ()
2269 -} // class SyndicatedLink
2270 -
2271 2340 ################################################################################
2272 2341 ## XML-RPC HOOKS: accept XML-RPC update pings from Contributors ################
2273 2342 ################################################################################
2274 2343
@@ -2301,8 +2370,9 @@
2301 2370
2302 2371 var $verify = FALSE;
2303 2372
2304 2373 var $_data = NULL;
2374 + var $_error = NULL;
2305 2375 var $_head = NULL;
2306 2376
2307 2377 # -- Recognition patterns
2308 2378 var $_feed_types = array(
@@ -2336,8 +2406,12 @@
2336 2406
2337 2407 // If all that failed, look harder
2338 2408 if (count($href) == 0) $href = $this->_a_href_feeds(FALSE);
2339 2409
2410 + // Our search may turn up duplicate URIs. We only need to do any given URI once.
2411 + // Props to Camilo <http://projects.radgeek.com/2008/12/14/feedwordpress-20081214/#comment-20090122160414>
2412 + $href = array_unique($href);
2413 +
2340 2414 // Verify feeds and resolve relative URIs
2341 2415 foreach ($href as $u) {
2342 2416 $the_uri = Relative_URI::resolve($u, $this->uri);
2343 2417 if ($this->verify) {
@@ -2357,8 +2431,12 @@
2357 2431 $this->_get($uri);
2358 2432 return $this->_data;
2359 2433 }
2360 2434
2435 + function error () {
2436 + return $this->_error;
2437 + }
2438 +
2361 2439 function is_feed ($uri = NULL) {
2362 2440 $data = $this->data($uri);
2363 2441
2364 2442 return (
@@ -2377,23 +2455,23 @@
2377 2455 if ($uri) $this->uri = $uri;
2378 2456
2379 2457 // Is the result not yet cached?
2380 2458 if ($this->_cache_uri !== $this->uri) :
2381 - // Snoopy is an HTTP client in PHP
2382 - $client = new Snoopy();
2383 -
2384 - // Prepare headers and internal settings
2385 - $client->rawheaders['Connection'] = 'close';
2386 - $client->accept = 'application/atom+xml application/rdf+xml application/rss+xml application/xml text/html */*';
2387 - $client->agent = 'feedfinder/1.2 (compatible; PHP FeedFinder) +http://projects.radgeek.com/feedwordpress';
2388 - $client->read_timeout = 25;
2389 -
2390 - // Fetch the HTML or feed
2391 - @$client->fetch($this->uri);
2392 - $this->_data = $client->results;
2459 + $headers['Connection'] = 'close';
2460 + $headers['Accept'] = 'application/atom+xml application/rdf+xml application/rss+xml application/xml text/html */*';
2461 + $headers['User-Agent'] = 'feedfinder/1.2 (compatible; PHP FeedFinder) +http://projects.radgeek.com/feedwordpress';
2393 2462
2394 - // Kilroy was here
2395 - $this->_cache_uri = $this->uri;
2463 + // Use function provided by MagpieRSS package
2464 + $client = _fetch_remote_file($this->uri, $headers);
2465 + if (isset($client->error)) :
2466 + $this->_error = $client->error;
2467 + else :
2468 + $this->_error = NULL;
2469 + endif;
2470 + $this->_data = $client->results;
2471 +
2472 + // Kilroy was here
2473 + $this->_cache_uri = $this->uri;
2396 2474 endif;
2397 2475 } /* FeedFinder::_get () */
2398 2476
2399 2477 function _link_rel_feeds () {