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 +526 -634 2008.11052009.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.1105
6 +Version: 2009.0612
7 7 Author: Charles Johnson
8 8 Author URI: http://radgeek.com/
9 9 License: GPL
10 -Last modified: 2008-11-05 4:35pm PST
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.1105');
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
@@ -45,8 +50,9 @@
45 50 define ('FWP_SCHEMA_21', 4772); // Database schema # for WP 2.1
46 51 define ('FWP_SCHEMA_23', 5495); // Database schema # for WP 2.3
47 52 define ('FWP_SCHEMA_25', 7558); // Database schema # for WP 2.5
48 53 define ('FWP_SCHEMA_26', 8201); // Database schema # for WP 2.6
54 +define ('FWP_SCHEMA_27', 9872); // Database schema # for WP 2.7
49 55
50 56 if (FEEDWORDPRESS_DEBUG) :
51 57 // Help us to pick out errors, if any.
52 58 ini_set('error_reporting', E_ALL & ~E_NOTICE);
@@ -85,8 +91,9 @@
85 91 endif;
86 92 endif;
87 93
88 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');
89 96
90 97 // Magic quotes are just about the stupidest thing ever.
91 98 if (is_array($_POST)) :
92 99 $fwp_post = stripslashes_deep($_POST);
@@ -160,9 +167,11 @@
160 167 remove_filter('pre_link_url', 'wp_filter_kses');
161 168
162 169 # Admin menu
163 170 add_action('admin_menu', 'fwp_add_pages');
164 -
171 + add_action('admin_notices', 'fwp_check_debug');
172 + add_action('admin_notices', 'fwp_check_magpie');
173 +
165 174 # Inbound XML-RPC update methods
166 175 add_filter('xmlrpc_methods', 'feedwordpress_xmlrpc_hook');
167 176
168 177 # Outbound XML-RPC ping reform
@@ -186,10 +195,12 @@
186 195 endif;
187 196
188 197 # Cron-less auto-update. Hooray!
189 198 add_action('init', 'feedwordpress_auto_update');
199 + add_action('init', 'feedwordpress_check_for_magpie_fix');
190 200
191 201 # Default sanitizers
202 + add_filter('syndicated_item_content', array('SyndicatedPost', 'resolve_relative_uris'), 0, 2);
192 203 add_filter('syndicated_item_content', array('SyndicatedPost', 'sanitize_content'), 0, 2);
193 204
194 205 else :
195 206 # Hook in the menus, which will just point to the upgrade interface
@@ -195,8 +206,33 @@
195 206 # Hook in the menus, which will just point to the upgrade interface
196 207 add_action('admin_menu', 'fwp_add_pages');
197 208 endif; // if (!FeedWordPress::needs_upgrade())
198 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 +
199 235 function feedwordpress_auto_update () {
200 236 if (FeedWordPress::stale()) :
201 237 $feedwordpress =& new FeedWordPress;
202 238 $feedwordpress->update();
@@ -360,9 +396,13 @@
360 396
361 397 function feedwordpress_preserve_syndicated_content ($text) {
362 398 global $feedwordpress_the_syndicated_content;
363 399
364 - 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 ) :
365 405 $feedwordpress_the_syndicated_content = $text;
366 406 else :
367 407 $feedwordpress_the_syndicated_content = NULL;
368 408 endif;
@@ -421,14 +461,146 @@
421 461 function fwp_add_pages () {
422 462 global $fwp_capability;
423 463 global $fwp_path;
424 464
425 - add_menu_page('Syndicated Sites', 'Syndication', $fwp_capability['manage_links'], $fwp_path.'/syndication.php');
426 - add_submenu_page($fwp_path.'/syndication.php', 'Syndicated Authors', 'Authors', $fwp_capability['manage_options'], $fwp_path.'/authors.php');
427 - add_submenu_page($fwp_path.'/syndication.php', 'Syndication Options', 'Options', $fwp_capability['manage_options'], $fwp_path.'/syndication-options.php');
428 - add_options_page('Syndication Options', 'Syndication', $fwp_capability['manage_options'], $fwp_path.'/syndication-options.php');
429 -} // 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;
430 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 +
431 603 ################################################################################
432 604 ## fwp_hold_pings() and fwp_release_pings(): Outbound XML-RPC ping reform ####
433 605 ## ... 'coz it's rude to send 500 pings the first time your aggregator runs ####
434 606 ################################################################################
@@ -694,9 +866,9 @@
694 866 if (function_exists('wp_insert_link')) { // WordPress 2.x
695 867 $link_id = wp_insert_link(array(
696 868 "link_name" => $name,
697 869 "link_url" => $uri,
698 - "link_category" => array($cat_id),
870 + "link_category" => (fwp_test_wp_version(0, FWP_SCHEMA_21) ? $cat_id : array($cat_id)),
699 871 "link_rss" => $rss
700 872 ));
701 873 } else { // WordPress 1.5.x
702 874 $result = $wpdb->query("
@@ -782,12 +954,12 @@
782 954 // WordPress 2.3 introduces a new taxonomy/term API
783 955 if (function_exists('is_term')) :
784 956 $cat_id = is_term($cat, 'link_category');
785 957 // WordPress 2.1 and 2.2 use a common table for both link and post categories
786 - 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)) :
787 959 $cat_id = $wpdb->get_var("SELECT cat_id FROM {$wpdb->categories} WHERE cat_name='$cat'");
788 960 // WordPress 1.5 and 2.0.x have a separate table for link categories
789 - elseif (!isset($wp_db_version) or $wp_db_version < FWP_SCHEMA_21) :
961 + elseif (fwp_test_wp_version(0, FWP_SCHEMA_21)):
790 962 $cat_id = $wpdb->get_var("SELECT cat_id FROM {$wpdb->linkcategories} WHERE cat_name='$cat'");
791 963 // This should never happen.
792 964 else :
793 965 FeedWordPress::critical_bug('FeedWordPress::link_category_id::wp_db_version', $wp_db_version, __LINE__);
@@ -800,12 +972,12 @@
800 972 if (function_exists('wp_insert_term')) :
801 973 $term = wp_insert_term($cat, 'link_category');
802 974 $cat_id = $term['term_id'];
803 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?
804 - elseif (function_exists('wp_insert_category')) :
976 + elseif (function_exists('wp_insert_category') and !fwp_test_wp_version(FWP_SCHEMA_20, FWP_SCHEMA_21)) :
805 977 $cat_id = wp_insert_category(array('cat_name' => $cat));
806 978 // WordPress 1.5 and 2.0.x
807 - elseif (!isset($wp_db_version) or $wp_db_version < FWP_SCHEMA_21) :
979 + elseif (fwp_test_wp_version(0, FWP_SCHEMA_21)) :
808 980 $result = $wpdb->query("
809 981 INSERT INTO $wpdb->linkcategories
810 982 SET
811 983 cat_id = 0,
@@ -923,8 +1095,15 @@
923 1095 CREATE INDEX {$wpdb->posts}_guid_idx ON {$wpdb->posts}(guid)
924 1096 ");
925 1097 } /* FeedWordPress::create_guid_index () */
926 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 +
927 1106 # Utility functions for handling text settings
928 1107 function negative ($f, $setting) {
929 1108 $nego = array ('n', 'no', 'f', 'false');
930 1109 return (isset($f[$setting]) and in_array(strtolower($f[$setting]), $nego));
@@ -939,18 +1118,29 @@
939 1118 # Internal debugging functions
940 1119 function critical_bug ($varname, $var, $line) {
941 1120 global $wp_version;
942 1121
1122 + if (defined('MAGPIE_VERSION')) : $mv = MAGPIE_VERSION;
1123 + else : $mv = 'WordPress '.$wp_version.' default.';
1124 + endif;
1125 +
943 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>';
944 1127 echo "\n<plaintext>";
945 1128 echo "Triggered at line # ".$line."\n";
946 1129 echo "FeedWordPress version: ".FEEDWORDPRESS_VERSION."\n";
947 - echo "WordPress version: $wp_version\n";
1130 + echo "MagpieRSS version: {$mv}\n";
1131 + echo "WordPress version: {$wp_version}\n";
948 1132 echo "PHP version: ".phpversion()."\n";
949 1133 echo "\n";
950 1134 echo $varname.": "; var_dump($var); echo "\n";
951 1135 die;
952 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 + }
953 1143 } // class FeedWordPress
954 1144
955 1145 class SyndicatedPost {
956 1146 var $item = null;
@@ -959,9 +1149,8 @@
959 1149 var $feed = null;
960 1150 var $feedmeta = null;
961 1151
962 1152 var $post = array ();
963 - var $_base = null;
964 1153
965 1154 var $_freshness = null;
966 1155 var $_wp_id = null;
967 1156
@@ -1005,9 +1194,11 @@
1005 1194 $this->post['named']['author'] = apply_filters('syndicated_item_author', $this->author(), $this);
1006 1195
1007 1196 # Identify content and sanitize it.
1008 1197 # ---------------------------------
1009 - 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'])) :
1010 1201 $content = $this->item['xhtml']['body'];
1011 1202 elseif (isset($this->item['xhtml']['div'])) :
1012 1203 $content = $this->item['xhtml']['div'];
1013 1204 elseif (isset($this->item['content']['encoded']) and $this->item['content']['encoded']):
@@ -1056,8 +1247,26 @@
1056 1247
1057 1248 // Unique ID (hopefully a unique tag: URI); failing that, the permalink
1058 1249 $this->post['guid'] = apply_filters('syndicated_item_guid', $this->guid(), $this);
1059 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 +
1060 1269 // RSS 2.0 / Atom 1.0 enclosure support
1061 1270 if ( isset($this->item['enclosure#']) ) :
1062 1271 for ($i = 1; $i <= $this->item['enclosure#']; $i++) :
1063 1272 $eid = (($i > 1) ? "#{$id}" : "");
@@ -1147,9 +1356,9 @@
1147 1356
1148 1357 // Tags: start with default tags, if any
1149 1358 $ft = get_option("feedwordpress_syndication_tags");
1150 1359 if ($ft) :
1151 - $this->post['tags_input'] = explode('FEEDWORDPRESS_CAT_SEPARATOR', $ft);
1360 + $this->post['tags_input'] = explode(FEEDWORDPRESS_CAT_SEPARATOR, $ft);
1152 1361 else :
1153 1362 $this->post['tags_input'] = array();
1154 1363 endif;
1155 1364
@@ -1298,187 +1507,223 @@
1298 1507 } // function SyndicatedPost::store ()
1299 1508
1300 1509 function insert_new () {
1301 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']);
1302 1577
1303 - // Why the fuck doesn't wp_insert_post already do this?
1304 - foreach ($this->post as $key => $value) :
1305 - if (is_string($value)) :
1306 - $dbpost[$key] = $wpdb->escape($value);
1307 - else :
1308 - $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);
1309 1585 endif;
1310 - endforeach;
1311 -
1312 - if (strlen($dbpost['post_title'].$dbpost['post_content'].$dbpost['post_excerpt']) == 0) :
1313 - // FIXME: Option for filtering out empty posts
1314 1586 endif;
1315 - if (strlen($dbpost['post_title'])==0) :
1316 - $dbpost['post_title'] = $this->post['meta']['syndication_source']
1317 - .' '.gmdate('Y-m-d H:i:s', $this->published() + $offset);
1318 - // FIXME: Option for what to fill a blank title with...
1319 - endif;
1587 + } /* SyndicatedPost::insert_new() */
1320 1588
1321 - if ($this->use_api('wp_insert_post')) :
1322 - $dbpost['post_pingback'] = false; // Tell WP 2.1 and 2.2 not to process for pingbacks
1589 + function update_existing () {
1590 + global $wpdb;
1323 1591
1324 - // This is a ridiculous fucking kludge necessitated by WordPress 2.6 munging authorship meta-data
1325 - add_action('_wp_put_post_revision', array($this, 'fix_revision_meta'));
1326 -
1327 - // Kludge to prevent kses filters from stripping the
1328 - // content of posts when updating without a logged in
1329 - // user who has `unfiltered_html` capability.
1330 - add_filter('content_save_pre', array($this, 'avoid_kses_munge'), 11);
1331 -
1332 - $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);
1333 1605
1334 - // Turn off ridiculous fucking kludges #1 and #2
1335 - remove_action('_wp_put_post_revision', array($this, 'fix_revision_meta'));
1336 - 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;
1337 1613
1338 - // This should never happen.
1339 - if (!is_numeric($this->_wp_id) or ($this->_wp_id == 0)) :
1340 - FeedWordPress::critical_bug('SyndicatedPost (_wp_id problem)', array("dbpost" => $dbpost, "this" => $this), __LINE__);
1341 - endif;
1614 + $this->_wp_id = wp_insert_post($dbpost);
1342 1615
1343 - // Unfortunately, as of WordPress 2.3, wp_insert_post()
1344 - // *still* offers no way to use a guid of your choice,
1345 - // and munges your post modified timestamp, too.
1346 - $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("
1347 1634 UPDATE $wpdb->posts
1348 1635 SET
1349 - guid='{$dbpost['guid']}',
1350 - post_modified='{$dbpost['post_modified']}',
1351 - post_modified_gmt='{$dbpost['post_modified_gmt']}'
1352 - WHERE ID='{$this->_wp_id}'
1353 - ");
1354 - else :
1355 - # The right way to do this is the above. But, alas,
1356 - # in earlier versions of WordPress, wp_insert_post has
1357 - # too much behavior (mainly related to pings) that can't
1358 - # be overridden. In WordPress 1.5, it's enough of a
1359 - # resource hog to make PHP segfault after inserting
1360 - # 50-100 posts. This can get pretty annoying, especially
1361 - # if you are trying to update your feeds for the first
1362 - # time.
1363 -
1364 - $result = $wpdb->query("
1365 - INSERT INTO $wpdb->posts
1366 - SET
1367 - guid = '{$dbpost['guid']}',
1368 - post_author = '{$dbpost['post_author']}',
1369 - post_date = '{$dbpost['post_date']}',
1370 - post_date_gmt = '{$dbpost['post_date_gmt']}',
1371 - post_content = '{$dbpost['post_content']}',"
1372 - .(isset($dbpost['post_excerpt']) ? "post_excerpt = '{$dbpost['post_excerpt']}'," : "")."
1373 - post_title = '{$dbpost['post_title']}',
1374 - post_name = '{$dbpost['post_name']}',
1375 - post_modified = '{$dbpost['post_modified']}',
1376 - post_modified_gmt = '{$dbpost['post_modified_gmt']}',
1377 - comment_status = '{$dbpost['comment_status']}',
1378 - ping_status = '{$dbpost['ping_status']}',
1379 - post_status = '{$dbpost['post_status']}'
1380 - ");
1381 - $this->_wp_id = $wpdb->insert_id;
1382 -
1383 - // This should never happen.
1384 - if (!is_numeric($this->_wp_id) or ($this->_wp_id == 0)) :
1385 - 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']);
1386 1664 endif;
1387 -
1388 - // WordPress 1.5.x - 2.0.x
1389 - wp_set_post_cats('1', $this->wp_id(), $this->post['post_category']);
1390 -
1391 - // Since we are not going through official channels, we need to
1392 - // manually tell WordPress that we've published a new post.
1393 - // We need to make sure to do this in order for FeedWordPress
1394 - // to play well with the staticize-reloaded plugin (something
1395 - // that a large aggregator website is going to *want* to be
1396 - // able to use).
1397 - do_action('publish_post', $this->_wp_id);
1398 1665 endif;
1399 - } /* SyndicatedPost::insert_new() */
1666 + } /* SyndicatedPost::update_existing() */
1400 1667
1401 - 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) {
1402 1675 global $wpdb;
1403 1676
1677 + $out = array();
1678 +
1404 1679 // Why the fuck doesn't wp_insert_post already do this?
1405 - $dbpost = array();
1406 1680 foreach ($this->post as $key => $value) :
1407 1681 if (is_string($value)) :
1408 - $dbpost[$key] = $wpdb->escape($value);
1682 + $out[$key] = $wpdb->escape($value);
1409 1683 else :
1410 - $dbpost[$key] = $value;
1684 + $out[$key] = $value;
1411 1685 endif;
1412 1686 endforeach;
1413 1687
1414 - if ($this->use_api('wp_insert_post')) :
1415 - $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;
1416 1698
1417 - // This is a ridiculous fucking kludge necessitated by WordPress 2.6 munging authorship meta-data
1418 - add_action('_wp_put_post_revision', array($this, 'fix_revision_meta'));
1699 + return $out;
1700 + }
1419 1701
1420 - // Kludge to prevent kses filters from stripping the
1421 - // content of posts when updating without a logged in
1422 - // user who has `unfiltered_html` capability.
1423 - add_filter('content_save_pre', array($this, 'avoid_kses_munge'), 11);
1424 -
1425 - $this->_wp_id = wp_insert_post($dbpost);
1426 -
1427 - // Turn off ridiculous fucking kludges #1 and #2
1428 - remove_action('_wp_put_post_revision', array($this, 'fix_revision_meta'));
1429 - remove_filter('content_save_pre', array($this, 'avoid_kses_munge'), 11);
1430 -
1431 - // This should never happen.
1432 - if (!is_numeric($this->_wp_id) or ($this->_wp_id == 0)) :
1433 - FeedWordPress::critical_bug('SyndicatedPost (_wp_id problem)', array("dbpost" => $dbpost, "this" => $this), __LINE__);
1434 - endif;
1435 -
1436 - // Unfortunately, as of WordPress 2.3, wp_insert_post()
1437 - // munges your post modified timestamp.
1438 - $result = $wpdb->query("
1439 - UPDATE $wpdb->posts
1440 - SET
1441 - post_modified='{$dbpost['post_modified']}',
1442 - post_modified_gmt='{$dbpost['post_modified_gmt']}'
1443 - WHERE ID='{$this->_wp_id}'
1444 - ");
1445 - else :
1446 -
1447 - $result = $wpdb->query("
1448 - UPDATE $wpdb->posts
1449 - SET
1450 - post_author = '{$dbpost['post_author']}',
1451 - post_content = '{$dbpost['post_content']}',"
1452 - .(isset($dbpost['post_excerpt']) ? "post_excerpt = '{$dbpost['post_excerpt']}'," : "")."
1453 - post_title = '{$dbpost['post_title']}',
1454 - post_name = '{$dbpost['post_name']}',
1455 - post_modified = '{$dbpost['post_modified']}',
1456 - post_modified_gmt = '{$dbpost['post_modified_gmt']}'
1457 - WHERE guid='{$dbpost['guid']}'
1458 - ");
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() */
1459 1725
1460 - // WordPress 2.1.x and up
1461 - if (function_exists('wp_set_post_categories')) :
1462 - wp_set_post_categories($this->wp_id(), $this->post['post_category']);
1463 - // WordPress 1.5.x - 2.0.x
1464 - elseif (function_exists('wp_set_post_cats')) :
1465 - wp_set_post_cats('1', $this->wp_id(), $this->post['post_category']);
1466 - // This should never happen.
1467 - else :
1468 - FeedWordPress::critical_bug('SyndicatedPost (_wp_id problem)', array("dbpost" => $dbpost, "this" => $this), __LINE__);
1469 - endif;
1470 -
1471 - // Since we are not going through official channels, we need to
1472 - // manually tell WordPress that we've published a new post.
1473 - // We need to make sure to do this in order for FeedWordPress
1474 - // to play well with the staticize-reloaded plugin (something
1475 - // that a large aggregator website is going to *want* to be
1476 - // able to use).
1477 - do_action('edit_post', $this->post['ID']);
1478 - endif;
1479 - } /* SyndicatedPost::update_existing() */
1480 -
1481 1726 /**
1482 1727 * SyndicatedPost::fix_revision_meta() - Fixes the way WP 2.6+ fucks up
1483 1728 * meta-data (authorship, etc.) when storing revisions of an updated
1484 1729 * syndicated post.
@@ -1741,9 +1986,9 @@
1741 1986 $cat_ids[] = $cat_id;
1742 1987 elseif (get_category($cat_id)) :
1743 1988 $cat_ids[] = $cat_id;
1744 1989 endif;
1745 - else :
1990 + elseif (strlen($cat_name) > 0) :
1746 1991 $esc = $wpdb->escape($cat_name);
1747 1992 $resc = $wpdb->escape(preg_quote($cat_name));
1748 1993
1749 1994 // WordPress 2.3+
@@ -1765,9 +2010,13 @@
1765 2010 elseif ('tag'==$unfamiliar_category) :
1766 2011 $tags[] = $cat_name;
1767 2012 elseif ('create'===$unfamiliar_category) :
1768 2013 $term = wp_insert_term($cat_name, 'category');
1769 - $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;
1770 2019 endif;
1771 2020
1772 2021 // WordPress 1.5.x - 2.2.x
1773 2022 else :
@@ -1999,25 +2248,87 @@
1999 2248
2000 2249 return $author;
2001 2250 } // SyndicatedPost::author()
2002 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 +
2003 2310 var $strip_attrs = array (
2004 - array('[a-z]+', 'style'),
2005 - array('[a-z]+', 'target'),
2311 + array('[a-z]+', 'target'),
2312 +// array('[a-z]+', 'style'),
2313 +// array('[a-z]+', 'on[a-z]+'),
2006 2314 );
2315 +
2316 + function strip_attribute_from_tag ($refs) {
2317 + $tag = FeedWordPressHTML::attributeMatch($refs);
2318 + return $tag['before_attribute'].$tag['after_attribute'];
2319 + }
2320 +
2007 2321 function sanitize_content ($content, $obj) {
2008 - # FeedWordPress used to resolve URIs relative to the
2009 - # feed URI. It now relies on the xml:base support
2010 - # baked in to the MagpieRSS upgrade. So all we do here
2011 - # now is to sanitize problematic attributes.
2012 - #
2013 2322 # This kind of sucks. I intend to replace it with
2014 2323 # lib_filter sometime soon.
2015 2324 foreach ($obj->strip_attrs as $pair):
2016 2325 list($tag,$attr) = $pair;
2017 - $content = preg_replace (
2018 - ":(<$tag [^>]*)($attr=(\"[^\">]*\"|[^>\\s]+))([^>]*>):i",
2019 - "\\1\\4",
2326 + $pattern = FeedWordPressHTML::attributeRegex($tag, $attr);
2327 +
2328 + $content = preg_replace_callback (
2329 + $pattern,
2330 + array(&$obj, 'strip_attribute_from_tag'),
2020 2331 $content
2021 2332 );
2022 2333 endforeach;
2023 2334 return $content;
@@ -2023,438 +2334,10 @@
2023 2334 return $content;
2024 2335 }
2025 2336 } // class SyndicatedPost
2026 2337
2027 -# class SyndicatedLink: represents a syndication feed stored within the
2028 -# WordPress database
2029 -#
2030 -# To keep things compact and editable from within WordPress, we use all the
2031 -# links under a particular category in the WordPress "Blogroll" for the list of
2032 -# feeds to syndicate. "Contributors" is the category used by default; you can
2033 -# configure that under Options --> Syndication.
2034 -#
2035 -# Fields used are:
2036 -#
2037 -# * link_rss: the URI of the Atom/RSS feed to syndicate
2038 -#
2039 -# * link_notes: user-configurable options, with keys and values
2040 -# like so:
2041 -#
2042 -# key: value
2043 -# cats: computers\nweb
2044 -# feed/key: value
2045 -#
2046 -# Keys that start with "feed/" are gleaned from the data supplied
2047 -# by the feed itself, and will be overwritten with each update.
2048 -#
2049 -# Values have linebreak characters escaped with C-style
2050 -# backslashes (so, for example, a newline becomes "\n").
2051 -#
2052 -# The value of `cats` is used as a newline-separated list of
2053 -# default categories for any post coming from a particular feed.
2054 -# (In the example above, any posts from this feed will be placed
2055 -# in the "computers" and "web" categories--*in addition to* any
2056 -# categories that may already be applied to the posts.)
2057 -#
2058 -# Values of keys in link_notes are accessible from templates using
2059 -# the function `get_feed_meta($key)` if this plugin is activated.
2338 +require_once(dirname(__FILE__) . '/syndicatedlink.class.php');
2060 2339
2061 -class SyndicatedLink {
2062 - var $id = null;
2063 - var $link = null;
2064 - var $settings = array ();
2065 - var $magpie = null;
2066 -
2067 - function SyndicatedLink ($link) {
2068 - global $wpdb;
2069 -
2070 - if (is_object($link)) :
2071 - $this->link = $link;
2072 - $this->id = $link->link_id;
2073 - else :
2074 - $this->id = $link;
2075 - if (function_exists('get_bookmark')) : // WP 2.1+
2076 - $this->link = get_bookmark($link);
2077 - else :
2078 - $this->link = $wpdb->get_row("
2079 - SELECT * FROM $wpdb->links
2080 - WHERE (link_id = '".$wpdb->escape($link)."')"
2081 - );
2082 - endif;
2083 - endif;
2084 -
2085 - if (strlen($this->link->link_rss) > 0) :
2086 - // Read off feed settings from link_notes
2087 - $notes = explode("\n", $this->link->link_notes);
2088 - foreach ($notes as $note):
2089 - list($key, $value) = explode(": ", $note, 2);
2090 -
2091 - if (strlen($key) > 0) :
2092 - // Unescape and trim() off the whitespace.
2093 - // Thanks to Ray Lischner for pointing out the
2094 - // need to trim off whitespace.
2095 - $this->settings[$key] = stripcslashes (trim($value));
2096 - endif;
2097 - endforeach;
2098 -
2099 - // "Magic" feed settings
2100 - $this->settings['link/uri'] = $this->link->link_rss;
2101 - $this->settings['link/name'] = $this->link->link_name;
2102 - $this->settings['link/id'] = $this->link->link_id;
2103 -
2104 - // `hardcode categories` and `unfamiliar categories` are deprecated in favor of `unfamiliar category`
2105 - if (
2106 - isset($this->settings['unfamiliar categories'])
2107 - and !isset($this->settings['unfamiliar category'])
2108 - ) :
2109 - $this->settings['unfamiliar category'] = $this->settings['unfamiliar categories'];
2110 - endif;
2111 - if (
2112 - FeedWordPress::affirmative($this->settings, 'hardcode categories')
2113 - and !isset($this->settings['unfamiliar category'])
2114 - ) :
2115 - $this->settings['unfamiliar category'] = 'default';
2116 - endif;
2117 -
2118 - // Set this up automagically for del.icio.us
2119 - $bits = parse_url($this->link->link_rss);
2120 - $tagspacers = array('del.icio.us', 'feeds.delicious.com');
2121 - if (!isset($this->settings['cat_split']) and in_array($bits['host'], $tagspacers)) :
2122 - $this->settings['cat_split'] = '\s'; // Whitespace separates multiple tags in del.icio.us RSS feeds
2123 - endif;
2124 -
2125 - if (isset($this->settings['cats'])):
2126 - $this->settings['cats'] = preg_split(FEEDWORDPRESS_CAT_SEPARATOR_PATTERN, $this->settings['cats']);
2127 - endif;
2128 - if (isset($this->settings['tags'])):
2129 - $this->settings['tags'] = preg_split(FEEDWORDPRESS_CAT_SEPARATOR_PATTERN, $this->settings['tags']);
2130 - endif;
2131 -
2132 - if (isset($this->settings['map authors'])) :
2133 - $author_rules = explode("\n\n", $this->settings['map authors']);
2134 - $ma = array();
2135 - foreach ($author_rules as $rule) :
2136 - list($rule_type, $author_name, $author_action) = explode("\n", $rule);
2137 -
2138 - // Normalize for case and whitespace
2139 - $rule_type = strtolower(trim($rule_type));
2140 - $author_name = strtolower(trim($author_name));
2141 - $author_action = strtolower(trim($author_action));
2142 -
2143 - $ma[$rule_type][$author_name] = $author_action;
2144 - endforeach;
2145 - $this->settings['map authors'] = $ma;
2146 - endif;
2147 - endif;
2148 - } /* SyndicatedLink::SyndicatedLink () */
2149 -
2150 - function found () {
2151 - return is_object($this->link);
2152 - } /* SyndicatedLink::found () */
2153 -
2154 - function stale () {
2155 - $stale = true;
2156 - if (isset($this->settings['update/hold']) and ($this->settings['update/hold']=='ping')) :
2157 - $stale = false; // don't update on any timed updates; pings only
2158 - elseif (isset($this->settings['update/hold']) and ($this->settings['update/hold']=='next')) :
2159 - $stale = true; // update on the next timed update
2160 - elseif (!isset($this->settings['update/ttl']) or !isset($this->settings['update/last'])) :
2161 - $stale = true; // initial update
2162 - else :
2163 - $after = ((int) $this->settings['update/last'])
2164 - +((int) $this->settings['update/ttl'] * 60);
2165 - $stale = (time() >= $after);
2166 - endif;
2167 - return $stale;
2168 - } /* SyndicatedLink::stale () */
2169 -
2170 - function poll ($crash_ts = NULL) {
2171 - global $wpdb;
2172 -
2173 - $this->magpie = fetch_rss($this->link->link_rss);
2174 - $new_count = NULL;
2175 -
2176 - $resume = FeedWordPress::affirmative($this->settings, 'update/unfinished');
2177 - if ($resume) :
2178 - // pick up where we left off
2179 - $processed = array_map('trim', explode("\n", $this->settings['update/processed']));
2180 - else :
2181 - // begin at the beginning
2182 - $processed = array();
2183 - endif;
2184 -
2185 - if (is_object($this->magpie)) :
2186 - $new_count = array('new' => 0, 'updated' => 0);
2187 -
2188 - # -- Update Link metadata live from feed
2189 - $channel = $this->magpie->channel;
2190 -
2191 - if (!isset($channel['id'])) :
2192 - $channel['id'] = $this->link->link_rss;
2193 - endif;
2194 -
2195 - $update = array();
2196 - if (!$this->hardcode('url') and isset($channel['link'])) :
2197 - $update[] = "link_url = '".$wpdb->escape($channel['link'])."'";
2198 - endif;
2199 -
2200 - if (!$this->hardcode('name') and isset($channel['title'])) :
2201 - $update[] = "link_name = '".$wpdb->escape($channel['title'])."'";
2202 - endif;
2203 -
2204 - if (!$this->hardcode('description')) :
2205 - if (isset($channel['tagline'])) :
2206 - $update[] = "link_description = '".$wpdb->escape($channel['tagline'])."'";
2207 - elseif (isset($channel['description'])) :
2208 - $update[] = "link_description = '".$wpdb->escape($channel['description'])."'";
2209 - endif;
2210 - endif;
2211 -
2212 - $this->settings = array_merge($this->settings, $this->flatten_array($channel));
2213 -
2214 - $this->settings['update/last'] = time(); $ttl = $this->ttl();
2215 - if (!is_null($ttl)) :
2216 - $this->settings['update/ttl'] = $ttl;
2217 - $this->settings['update/timed'] = 'feed';
2218 - else :
2219 - $this->settings['update/ttl'] = rand(30, 120); // spread over time interval for staggered updates
2220 - $this->settings['update/timed'] = 'automatically';
2221 - endif;
2222 -
2223 - if (!isset($this->settings['update/hold']) or $this->settings['update/hold']!='ping') :
2224 - $this->settings['update/hold'] = 'scheduled';
2225 - endif;
2226 -
2227 - $this->settings['update/unfinished'] = 'yes';
2228 -
2229 - $update[] = "link_notes = '".$wpdb->escape($this->settings_to_notes())."'";
2230 -
2231 - $update_set = implode(',', $update);
2232 -
2233 - // Update the properties of the link from the feed information
2234 - $result = $wpdb->query("
2235 - UPDATE $wpdb->links
2236 - SET $update_set
2237 - WHERE link_id='$this->id'
2238 - ");
2239 -
2240 - # -- Add new posts from feed and update any updated posts
2241 - $crashed = false;
2242 -
2243 - if (is_array($this->magpie->items)) :
2244 - foreach ($this->magpie->items as $item) :
2245 - $post =& new SyndicatedPost($item, $this);
2246 - if (!$resume or !in_array(trim($post->guid()), $processed)) :
2247 - $processed[] = $post->guid();
2248 - if (!$post->filtered()) :
2249 - $new = $post->store();
2250 - if ( $new !== false ) $new_count[$new]++;
2251 - endif;
2252 -
2253 - if (!is_null($crash_ts) and (time() > $crash_ts)) :
2254 - $crashed = true;
2255 - break;
2256 - endif;
2257 - endif;
2258 - endforeach;
2259 - endif;
2260 -
2261 - // Copy back any changes to feed settings made in the course of updating (e.g. new author rules)
2262 - $to_notes = $this->settings;
2263 -
2264 - $this->settings['update/processed'] = $processed;
2265 - if (!$crashed) :
2266 - $this->settings['update/unfinished'] = 'no';
2267 - endif;
2268 -
2269 - $update_set = "link_notes = '".$wpdb->escape($this->settings_to_notes())."'";
2270 -
2271 - // Update the properties of the link from the feed information
2272 - $result = $wpdb->query("
2273 - UPDATE $wpdb->links
2274 - SET $update_set
2275 - WHERE link_id='$this->id'
2276 - ");
2277 - endif;
2278 -
2279 - return $new_count;
2280 - } /* SyndicatedLink::poll() */
2281 -
2282 - function map_name_to_new_user ($name, $newuser_name) {
2283 - global $wpdb;
2284 -
2285 - if (strlen($newuser_name) > 0) :
2286 - $userdata = array();
2287 - $userdata['ID'] = NULL;
2288 -
2289 - $userdata['user_login'] = sanitize_user($newuser_name);
2290 - $userdata['user_login'] = apply_filters('pre_user_login', $userdata['user_login']);
2291 -
2292 - $userdata['user_nicename'] = sanitize_title($newuser_name);
2293 - $userdata['user_nicename'] = apply_filters('pre_user_nicename', $userdata['user_nicename']);
2294 -
2295 - $userdata['display_name'] = $wpdb->escape($newuser_name);
2296 -
2297 - $newuser_id = wp_insert_user($userdata);
2298 - if (is_numeric($newuser_id)) :
2299 - if (is_null($name)) : // Unfamiliar author
2300 - $this->settings['unfamiliar author'] = $newuser_id;
2301 - else :
2302 - $this->settings['map authors']['name'][$name] = $newuser_id;
2303 - endif;
2304 - else :
2305 - // TODO: Add some error detection and reporting
2306 - endif;
2307 - else :
2308 - // TODO: Add some error reporting
2309 - endif;
2310 - } /* SyndicatedLink::map_name_to_new_user () */
2311 -
2312 - function settings_to_notes () {
2313 - $to_notes = $this->settings;
2314 -
2315 - unset($to_notes['link/id']); // Magic setting; don't save
2316 - unset($to_notes['link/uri']); // Magic setting; don't save
2317 - unset($to_notes['link/name']); // Magic setting; don't save
2318 - unset($to_notes['hardcode categories']); // Deprecated
2319 - unset($to_notes['unfamiliar categories']); // Deprecated
2320 -
2321 - // Collapse array settings
2322 - if (isset($to_notes['update/processed']) and (is_array($to_notes['update/processed']))) :
2323 - $to_notes['update/processed'] = implode("\n", $to_notes['update/processed']);
2324 - endif;
2325 -
2326 - if (is_array($to_notes['cats'])) :
2327 - $to_notes['cats'] = implode(FEEDWORDPRESS_CAT_SEPARATOR, $to_notes['cats']);
2328 - endif;
2329 - if (is_array($to_notes['tags'])) :
2330 - $to_notes['tags'] = implode(FEEDWORDPRESS_CAT_SEPARATOR, $to_notes['tags']);
2331 - endif;
2332 -
2333 - // Collapse the author mapping rule structure back into a flat string
2334 - if (isset($to_notes['map authors'])) :
2335 - $ma = array();
2336 - foreach ($to_notes['map authors'] as $rule_type => $author_rules) :
2337 - foreach ($author_rules as $author_name => $author_action) :
2338 - $ma[] = $rule_type."\n".$author_name."\n".$author_action;
2339 - endforeach;
2340 - endforeach;
2341 - $to_notes['map authors'] = implode("\n\n", $ma);
2342 - endif;
2343 -
2344 - $notes = '';
2345 - foreach ($to_notes as $key => $value) :
2346 - $notes .= $key . ": ". addcslashes($value, "\0..\37".'\\') . "\n";
2347 - endforeach;
2348 - return $notes;
2349 - } /* SyndicatedLink::settings_to_notes () */
2350 -
2351 - function uri () {
2352 - return (is_object($this->link) ? $this->link->link_rss : NULL);
2353 - } /* SyndicatedLink::uri () */
2354 -
2355 - function homepage () {
2356 - return (isset($this->settings['feed/link']) ? $this->settings['feed/link'] : NULL);
2357 - } /* SyndicatedLink::homepage () */
2358 -
2359 - function ttl () {
2360 - if (is_object($this->magpie)) :
2361 - $channel = $this->magpie->channel;
2362 - else :
2363 - $channel = array();
2364 - endif;
2365 -
2366 - if (isset($channel['ttl'])) :
2367 - // "ttl stands for time to live. It's a number of
2368 - // minutes that indicates how long a channel can be
2369 - // cached before refreshing from the source."
2370 - // <http://blogs.law.harvard.edu/tech/rss#ltttlgtSubelementOfLtchannelgt>
2371 - $ret = $channel['ttl'];
2372 - elseif (isset($channel['sy']['updatefrequency']) or isset($channel['sy']['updateperiod'])) :
2373 - $period_minutes = array (
2374 - 'hourly' => 60, /* minutes in an hour */
2375 - 'daily' => 1440, /* minutes in a day */
2376 - 'weekly' => 10080, /* minutes in a week */
2377 - 'monthly' => 43200, /* minutes in a month */
2378 - 'yearly' => 525600, /* minutes in a year */
2379 - );
2380 -
2381 - // "sy:updatePeriod: Describes the period over which the
2382 - // channel format is updated. Acceptable values are:
2383 - // hourly, daily, weekly, monthly, yearly. If omitted,
2384 - // daily is assumed." <http://web.resource.org/rss/1.0/modules/syndication/>
2385 - if (isset($channel['sy']['updateperiod'])) : $period = $channel['sy']['updateperiod'];
2386 - else : $period = 'daily';
2387 - endif;
2388 -
2389 - // "sy:updateFrequency: Used to describe the frequency
2390 - // of updates in relation to the update period. A
2391 - // positive integer indicates how many times in that
2392 - // period the channel is updated. ... If omitted a value
2393 - // of 1 is assumed." <http://web.resource.org/rss/1.0/modules/syndication/>
2394 - if (isset($channel['sy']['updatefrequency'])) : $freq = (int) $channel['sy']['updatefrequency'];
2395 - else : $freq = 1;
2396 - endif;
2397 -
2398 - $ret = (int) ($period_minutes[$period] / $freq);
2399 - else :
2400 - $ret = NULL;
2401 - endif;
2402 - return $ret;
2403 - } /* SyndicatedLink::ttl() */
2404 -
2405 - // SyndicatedLink::flatten_array (): flatten an array. Useful for
2406 - // hierarchical and namespaced elements.
2407 - //
2408 - // Given an array which may contain array or object elements in it,
2409 - // return a "flattened" array: a one-dimensional array of scalars
2410 - // containing each of the scalar elements contained within the array
2411 - // structure. Thus, for example, if $a['b']['c']['d'] == 'e', then the
2412 - // returned array for FeedWordPress::flatten_array($a) will contain a key
2413 - // $a['feed/b/c/d'] with value 'e'.
2414 - function flatten_array ($arr, $prefix = 'feed/', $separator = '/') {
2415 - $ret = array ();
2416 - if (is_array($arr)) :
2417 - foreach ($arr as $key => $value) :
2418 - if (is_scalar($value)) :
2419 - $ret[$prefix.$key] = $value;
2420 - else :
2421 - $ret = array_merge($ret, $this->flatten_array($value, $prefix.$key.$separator, $separator));
2422 - endif;
2423 - endforeach;
2424 - endif;
2425 - return $ret;
2426 - } /* SyndicatedLink::flatten_array () */
2427 -
2428 - function hardcode ($what) {
2429 - $default = get_option("feedwordpress_hardcode_$what");
2430 - if ( $default === 'yes' ) :
2431 - // If the default is to hardcode, then we want the
2432 - // negation of negative(): TRUE by default and FALSE if
2433 - // the setting is explicitly "no"
2434 - $ret = !FeedWordPress::negative($this->settings, "hardcode $what");
2435 - else :
2436 - // If the default is NOT to hardcode, then we want
2437 - // affirmative(): FALSE by default and TRUE if the
2438 - // setting is explicitly "yes"
2439 - $ret = FeedWordPress::affirmative($this->settings, "hardcode $what");
2440 - endif;
2441 - return $ret;
2442 - } /* SyndicatedLink::hardcode () */
2443 -
2444 - function syndicated_status ($what, $default) {
2445 - global $wpdb;
2446 -
2447 - $ret = get_option("feedwordpress_syndicated_{$what}_status");
2448 - if ( isset($this->settings["$what status"]) ) :
2449 - $ret = $this->settings["$what status"];
2450 - elseif (!$ret) :
2451 - $ret = $default;
2452 - endif;
2453 - return $wpdb->escape(trim(strtolower($ret)));
2454 - } /* SyndicatedLink:syndicated_status () */
2455 -} // class SyndicatedLink
2456 -
2457 2340 ################################################################################
2458 2341 ## XML-RPC HOOKS: accept XML-RPC update pings from Contributors ################
2459 2342 ################################################################################
2460 2343
@@ -2487,8 +2370,9 @@
2487 2370
2488 2371 var $verify = FALSE;
2489 2372
2490 2373 var $_data = NULL;
2374 + var $_error = NULL;
2491 2375 var $_head = NULL;
2492 2376
2493 2377 # -- Recognition patterns
2494 2378 var $_feed_types = array(
@@ -2522,8 +2406,12 @@
2522 2406
2523 2407 // If all that failed, look harder
2524 2408 if (count($href) == 0) $href = $this->_a_href_feeds(FALSE);
2525 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 +
2526 2414 // Verify feeds and resolve relative URIs
2527 2415 foreach ($href as $u) {
2528 2416 $the_uri = Relative_URI::resolve($u, $this->uri);
2529 2417 if ($this->verify) {
@@ -2543,8 +2431,12 @@
2543 2431 $this->_get($uri);
2544 2432 return $this->_data;
2545 2433 }
2546 2434
2435 + function error () {
2436 + return $this->_error;
2437 + }
2438 +
2547 2439 function is_feed ($uri = NULL) {
2548 2440 $data = $this->data($uri);
2549 2441
2550 2442 return (
@@ -2563,23 +2455,23 @@
2563 2455 if ($uri) $this->uri = $uri;
2564 2456
2565 2457 // Is the result not yet cached?
2566 2458 if ($this->_cache_uri !== $this->uri) :
2567 - // Snoopy is an HTTP client in PHP
2568 - $client = new Snoopy();
2569 -
2570 - // Prepare headers and internal settings
2571 - $client->rawheaders['Connection'] = 'close';
2572 - $client->accept = 'application/atom+xml application/rdf+xml application/rss+xml application/xml text/html */*';
2573 - $client->agent = 'feedfinder/1.2 (compatible; PHP FeedFinder) +http://projects.radgeek.com/feedwordpress';
2574 - $client->read_timeout = 25;
2575 -
2576 - // Fetch the HTML or feed
2577 - @$client->fetch($this->uri);
2578 - $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';
2579 2462
2580 - // Kilroy was here
2581 - $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;
2582 2474 endif;
2583 2475 } /* FeedFinder::_get () */
2584 2476
2585 2477 function _link_rel_feeds () {