PluginProbe
FeedWordPress / 2009.0618
FeedWordPress v2009.0618
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 +684 -780 2008.10302009.0618 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.0618
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 we ignored the upgrade request for this version
217 + update_option('feedwordpress_magpie_ignored_upgrade_to', EXPECTED_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,142 @@
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('EXPECTED_MAGPIE_VERSION', '2009.0618');
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 + $ignored = get_option('feedwordpress_magpie_ignored_upgrade_to');
558 + if (EXPECTED_MAGPIE_VERSION != $magpie_version and EXPECTED_MAGPIE_VERSION != $ignored) :
559 + if (current_user_can('edit_files')) :
560 + $youAre = 'you are';
561 + $itIsRecommendedThatYou = 'It is <strong>strongly recommended</strong> that you';
562 + else :
563 + $youAre = 'this site is';
564 + $itIsRecommendedThatYou = 'You may want to contact the administrator of the site; it is <strong>strongly recommended</strong> that they';
565 + endif;
566 + print '<div class="error">';
567 +?>
568 +<p style="font-style: italic"><strong>FeedWordPress has detected that <?php print $youAre; ?> currently using a version of
569 +MagpieRSS other than the upgraded version that ships with this version of FeedWordPress.</strong></p>
570 +<ul>
571 +<li><strong>Currently running:</strong> MagpieRSS <?php print $magpie_version; ?></li>
572 +<li><strong>Version included with FeedWordPress <?php print FEEDWORDPRESS_VERSION; ?>:</strong> MagpieRSS <?php print EXPECTED_MAGPIE_VERSION; ?></li>
573 +</ul>
574 +<p><?php print $itIsRecommendedThatYou; ?> install the upgraded
575 +version of MagpieRSS supplied with FeedWordPress. The version of
576 +MagpieRSS that ships with WordPress is very old and buggy, and
577 +encounters a number of errors when trying to parse modern Atom
578 +and RSS feeds.</p>
579 +<?php
580 + feedwordpress_upgrade_old_and_busted_buttons();
581 + print '</div>';
582 + endif;
583 + endif;
584 +}
585 +
586 +function feedwordpress_upgrade_old_and_busted_buttons() {
587 + if (current_user_can('edit_files')) :
588 +?>
589 +<form action="" method="post"><div>
590 +<?php FeedWordPressCompatibility::stamp_nonce('feedwordpress_fix_magpie'); ?>
591 +<input type="hidden" name="action" value="fix_magpie_version" />
592 +<input class="button-secondary" type="submit" name="ignore" value="<?php _e('Ignore this problem'); ?>" />
593 +<input class="button-primary" type="submit" name="upgrade" value="<?php _e('Upgrade'); ?>" />
594 +</div></form>
595 +<?php
596 + endif;
597 +}
598 +
366 599 ################################################################################
367 600 ## fwp_hold_pings() and fwp_release_pings(): Outbound XML-RPC ping reform ####
368 601 ## ... 'coz it's rude to send 500 pings the first time your aggregator runs ####
369 602 ################################################################################
@@ -517,9 +750,9 @@
517 750 #
518 751 # * Updates to existing posts since the last poll are mirrored in the
519 752 # WordPress store.
520 753 #
521 - function update ($uri = null) {
754 + function update ($uri = null, $crash_ts = null) {
522 755 global $wpdb;
523 756
524 757 if (FeedWordPress::needs_upgrade()) : // Will make duplicate posts if we don't hold off
525 758 return NULL;
@@ -532,11 +765,28 @@
532 765 endif;
533 766
534 767 do_action('feedwordpress_update', $uri);
535 768
769 + if (is_null($crash_ts)) :
770 + $crash_dt = (int) get_option('feedwordpress_update_time_limit');
771 + if ($crash_dt > 0) :
772 + $crash_ts = time() + $crash_dt;
773 + else :
774 + $crash_ts = NULL;
775 + endif;
776 + endif;
777 +
778 + // Randomize order for load balancing purposes
779 + $feed_set = $this->feeds;
780 + shuffle($feed_set);
781 +
536 782 // Loop through and check for new posts
537 783 $delta = NULL;
538 - foreach ($this->feeds as $feed) :
784 + foreach ($feed_set as $feed) :
785 + if (!is_null($crash_ts) and (time() > $crash_ts)) : // Check whether we've exceeded the time limit
786 + break;
787 + endif;
788 +
539 789 $pinged_that = (is_null($uri) or in_array($uri, array($feed->uri(), $feed->homepage())));
540 790
541 791 if (!is_null($uri)) : // A site-specific ping always updates
542 792 $timely = true;
@@ -549,9 +799,12 @@
549 799 endif;
550 800
551 801 if ($pinged_that and $timely) :
552 802 do_action('feedwordpress_check_feed', $feed->settings);
553 - $added = $feed->poll();
803 + $start_ts = time();
804 + $added = $feed->poll($crash_ts);
805 + do_action('feedwordpress_check_feed_complete', $feed->settings, $added, time() - $start_ts);
806 +
554 807 if (isset($added['new'])) : $delta['new'] += $added['new']; endif;
555 808 if (isset($added['updated'])) : $delta['updated'] += $added['updated']; endif;
556 809 endif;
557 810 endforeach;
@@ -609,9 +862,9 @@
609 862 if (function_exists('wp_insert_link')) { // WordPress 2.x
610 863 $link_id = wp_insert_link(array(
611 864 "link_name" => $name,
612 865 "link_url" => $uri,
613 - "link_category" => array($cat_id),
866 + "link_category" => (fwp_test_wp_version(0, FWP_SCHEMA_21) ? $cat_id : array($cat_id)),
614 867 "link_rss" => $rss
615 868 ));
616 869 } else { // WordPress 1.5.x
617 870 $result = $wpdb->query("
@@ -646,8 +899,33 @@
646 899
647 900 return $ret;
648 901 } // function FeedWordPress::on_unfamiliar()
649 902
903 + function null_email_set () {
904 + $base = get_option('feedwordpress_null_email_set');
905 +
906 + if ($base===false) :
907 + $ret = array('noreply@blogger.com'); // default
908 + else :
909 + $ret = array_map('strtolower',
910 + array_map('trim', explode("\n", $base)));
911 + endif;
912 + $ret = apply_filters('syndicated_item_author_null_email_set', $ret);
913 + return $ret;
914 +
915 + } /* FeedWordPress::null_email_set () */
916 +
917 + function is_null_email ($email) {
918 + $ret = in_array(strtolower(trim($email)), FeedWordPress::null_email_set());
919 + $ret = apply_filters('syndicated_item_author_is_null_email', $ret, $email);
920 + return $ret;
921 + } /* FeedWordPress::is_null_email () */
922 +
923 + function use_aggregator_source_data () {
924 + $ret = get_option('feedwordpress_use_aggregator_source_data');
925 + return apply_filters('syndicated_post_use_aggregator_source_data', ($ret=='yes'));
926 + }
927 +
650 928 function syndicated_links () {
651 929 $contributors = FeedWordPress::link_category_id();
652 930 if (function_exists('get_bookmarks')) :
653 931 $links = get_bookmarks(array("category" => $contributors));
@@ -672,12 +950,12 @@
672 950 // WordPress 2.3 introduces a new taxonomy/term API
673 951 if (function_exists('is_term')) :
674 952 $cat_id = is_term($cat, 'link_category');
675 953 // 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) ) :
954 + elseif (fwp_test_wp_version(FWP_SCHEMA_21, FWP_SCHEMA_23)) :
677 955 $cat_id = $wpdb->get_var("SELECT cat_id FROM {$wpdb->categories} WHERE cat_name='$cat'");
678 956 // 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) :
957 + elseif (fwp_test_wp_version(0, FWP_SCHEMA_21)):
680 958 $cat_id = $wpdb->get_var("SELECT cat_id FROM {$wpdb->linkcategories} WHERE cat_name='$cat'");
681 959 // This should never happen.
682 960 else :
683 961 FeedWordPress::critical_bug('FeedWordPress::link_category_id::wp_db_version', $wp_db_version, __LINE__);
@@ -690,12 +968,12 @@
690 968 if (function_exists('wp_insert_term')) :
691 969 $term = wp_insert_term($cat, 'link_category');
692 970 $cat_id = $term['term_id'];
693 971 // 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')) :
972 + elseif (function_exists('wp_insert_category') and !fwp_test_wp_version(FWP_SCHEMA_20, FWP_SCHEMA_21)) :
695 973 $cat_id = wp_insert_category(array('cat_name' => $cat));
696 974 // WordPress 1.5 and 2.0.x
697 - elseif (!isset($wp_db_version) or $wp_db_version < FWP_SCHEMA_21) :
975 + elseif (fwp_test_wp_version(0, FWP_SCHEMA_21)) :
698 976 $result = $wpdb->query("
699 977 INSERT INTO $wpdb->linkcategories
700 978 SET
701 979 cat_id = 0,
@@ -805,8 +1083,23 @@
805 1083 endswitch;
806 1084 echo "<p>Upgrade complete. FeedWordPress is now ready to use again.</p>";
807 1085 } /* FeedWordPress::upgrade_database() */
808 1086
1087 + function create_guid_index () {
1088 + global $wpdb;
1089 +
1090 + $wpdb->query("
1091 + CREATE INDEX {$wpdb->posts}_guid_idx ON {$wpdb->posts}(guid)
1092 + ");
1093 + } /* FeedWordPress::create_guid_index () */
1094 +
1095 + function magpie_version () {
1096 + if (!defined('MAGPIE_VERSION')) : $magpie_version = $GLOBALS['wp_version'].'-default';
1097 + else : $magpie_version = MAGPIE_VERSION;
1098 + endif;
1099 + return $magpie_version;
1100 + }
1101 +
809 1102 # Utility functions for handling text settings
810 1103 function negative ($f, $setting) {
811 1104 $nego = array ('n', 'no', 'f', 'false');
812 1105 return (isset($f[$setting]) and in_array(strtolower($f[$setting]), $nego));
@@ -821,18 +1114,29 @@
821 1114 # Internal debugging functions
822 1115 function critical_bug ($varname, $var, $line) {
823 1116 global $wp_version;
824 1117
1118 + if (defined('MAGPIE_VERSION')) : $mv = MAGPIE_VERSION;
1119 + else : $mv = 'WordPress '.$wp_version.' default.';
1120 + endif;
1121 +
825 1122 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 1123 echo "\n<plaintext>";
827 1124 echo "Triggered at line # ".$line."\n";
828 1125 echo "FeedWordPress version: ".FEEDWORDPRESS_VERSION."\n";
829 - echo "WordPress version: $wp_version\n";
1126 + echo "MagpieRSS version: {$mv}\n";
1127 + echo "WordPress version: {$wp_version}\n";
830 1128 echo "PHP version: ".phpversion()."\n";
831 1129 echo "\n";
832 1130 echo $varname.": "; var_dump($var); echo "\n";
833 1131 die;
834 1132 }
1133 +
1134 + function noncritical_bug ($varname, $var, $line) {
1135 + if (FEEDWORDPRESS_DEBUG) : // halt only when we are doing debugging
1136 + FeedWordPress::critical_bug($varname, $var, $line);
1137 + endif;
1138 + }
835 1139 } // class FeedWordPress
836 1140
837 1141 class SyndicatedPost {
838 1142 var $item = null;
@@ -841,9 +1145,8 @@
841 1145 var $feed = null;
842 1146 var $feedmeta = null;
843 1147
844 1148 var $post = array ();
845 - var $_base = null;
846 1149
847 1150 var $_freshness = null;
848 1151 var $_wp_id = null;
849 1152
@@ -887,9 +1190,11 @@
887 1190 $this->post['named']['author'] = apply_filters('syndicated_item_author', $this->author(), $this);
888 1191
889 1192 # Identify content and sanitize it.
890 1193 # ---------------------------------
891 - if (isset($this->item['xhtml']['body'])) :
1194 + if (isset($this->item['atom_content'])) :
1195 + $content = $this->item['atom_content'];
1196 + elseif (isset($this->item['xhtml']['body'])) :
892 1197 $content = $this->item['xhtml']['body'];
893 1198 elseif (isset($this->item['xhtml']['div'])) :
894 1199 $content = $this->item['xhtml']['div'];
895 1200 elseif (isset($this->item['content']['encoded']) and $this->item['content']['encoded']):
@@ -938,8 +1243,26 @@
938 1243
939 1244 // Unique ID (hopefully a unique tag: URI); failing that, the permalink
940 1245 $this->post['guid'] = apply_filters('syndicated_item_guid', $this->guid(), $this);
941 1246
1247 + // 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
1248 + $default_custom_settings = get_option('feedwordpress_custom_settings');
1249 + if ($default_custom_settings) :
1250 + $default_custom_settings = unserialize($default_custom_settings);
1251 + endif;
1252 + if (!is_array($default_custom_settings)) :
1253 + $default_custom_settings = array();
1254 + endif;
1255 +
1256 + $custom_settings = (isset($this->link->settings['postmeta']) ? $this->link->settings['postmeta'] : null);
1257 + if ($custom_settings) :
1258 + $custom_settings = unserialize($custom_settings);
1259 + endif;
1260 + if (!is_array($custom_settings)) :
1261 + $custom_settings = array();
1262 + endif;
1263 + $this->post['meta'] = array_merge($default_custom_settings, $custom_settings);
1264 +
942 1265 // RSS 2.0 / Atom 1.0 enclosure support
943 1266 if ( isset($this->item['enclosure#']) ) :
944 1267 for ($i = 1; $i <= $this->item['enclosure#']; $i++) :
945 1268 $eid = (($i > 1) ? "#{$id}" : "");
@@ -953,12 +1276,26 @@
953 1276 // In case you want to point back to the blog this was syndicated from
954 1277 if (isset($this->feed->channel['title'])) :
955 1278 $this->post['meta']['syndication_source'] = apply_filters('syndicated_item_source_title', $this->feed->channel['title'], $this);
956 1279 endif;
1280 +
957 1281 if (isset($this->feed->channel['link'])) :
958 1282 $this->post['meta']['syndication_source_uri'] = apply_filters('syndicated_item_source_link', $this->feed->channel['link'], $this);
959 1283 endif;
960 1284
1285 + // Make use of atom:source data, if present in an aggregated feed
1286 + if (isset($this->item['source_title'])) :
1287 + $this->post['meta']['syndication_source_original'] = $this->item['source_title'];
1288 + endif;
1289 +
1290 + if (isset($this->item['source_link'])) :
1291 + $this->post['meta']['syndication_source_uri_original'] = $this->item['source_link'];
1292 + endif;
1293 +
1294 + if (isset($this->item['source_id'])) :
1295 + $this->post['meta']['syndication_source_id_original'] = $this->item['source_id'];
1296 + endif;
1297 +
961 1298 // Store information on human-readable and machine-readable comment URIs
962 1299 if (isset($this->item['comments'])) :
963 1300 $this->post['meta']['rss:comments'] = apply_filters('syndicated_item_comments', $this->item['comments']);
964 1301 endif;
@@ -969,8 +1306,12 @@
969 1306 // Store information to identify the feed that this came from
970 1307 $this->post['meta']['syndication_feed'] = $this->feedmeta['link/uri'];
971 1308 $this->post['meta']['syndication_feed_id'] = $this->feedmeta['link/id'];
972 1309
1310 + if (isset($this->item['source_link_self'])) :
1311 + $this->post['meta']['syndication_feed_original'] = $this->item['source_link_self'];
1312 + endif;
1313 +
973 1314 // In case you want to know the external permalink...
974 1315 $this->post['meta']['syndication_permalink'] = apply_filters('syndicated_item_link', $this->item['link']);
975 1316
976 1317 // Store a hash of the post content for checking whether something needs to be updated
@@ -976,10 +1317,10 @@
976 1317 // Store a hash of the post content for checking whether something needs to be updated
977 1318 $this->post['meta']['syndication_item_hash'] = $this->update_hash();
978 1319
979 1320 // Feed-by-feed options for author and category creation
980 - $this->post['named']['unfamiliar']['author'] = $this->feedmeta['unfamiliar author'];
981 - $this->post['named']['unfamiliar']['category'] = $this->feedmeta['unfamiliar category'];
1321 + $this->post['named']['unfamiliar']['author'] = (isset($this->feedmeta['unfamiliar author']) ? $this->feedmeta['unfamiliar author'] : null);
1322 + $this->post['named']['unfamiliar']['category'] = (isset($this->feedmeta['unfamiliar category']) ? $this->feedmeta['unfamiliar category'] : null);
982 1323
983 1324 // Categories: start with default categories, if any
984 1325 $fc = get_option("feedwordpress_syndication_cats");
985 1326 if ($fc) :
@@ -987,9 +1328,9 @@
987 1328 else :
988 1329 $this->post['named']['preset/category'] = array();
989 1330 endif;
990 1331
991 - if (is_array($this->feedmeta['cats'])) :
1332 + if (isset($this->feedmeta['cats']) and is_array($this->feedmeta['cats'])) :
992 1333 $this->post['named']['preset/category'] = array_merge($this->post['named']['preset/category'], $this->feedmeta['cats']);
993 1334 endif;
994 1335
995 1336 // Now add categories from the post, if we have 'em
@@ -1011,14 +1352,14 @@
1011 1352
1012 1353 // Tags: start with default tags, if any
1013 1354 $ft = get_option("feedwordpress_syndication_tags");
1014 1355 if ($ft) :
1015 - $this->post['tags_input'] = explode('FEEDWORDPRESS_CAT_SEPARATOR', $ft);
1356 + $this->post['tags_input'] = explode(FEEDWORDPRESS_CAT_SEPARATOR, $ft);
1016 1357 else :
1017 1358 $this->post['tags_input'] = array();
1018 1359 endif;
1019 1360
1020 - if (is_array($this->feedmeta['tags'])) :
1361 + if (isset($this->feedmeta['tags']) and is_array($this->feedmeta['tags'])) :
1021 1362 $this->post['tags_input'] = array_merge($this->post['tags_input'], $this->feedmeta['tags']);
1022 1363 endif;
1023 1364
1024 1365 endif;
@@ -1162,187 +1503,223 @@
1162 1503 } // function SyndicatedPost::store ()
1163 1504
1164 1505 function insert_new () {
1165 1506 global $wpdb, $wp_db_version;
1507 +
1508 + $dbpost = $this->normalize_post(/*new=*/ true);
1509 + if (!is_null($dbpost)) :
1510 + if ($this->use_api('wp_insert_post')) :
1511 + $dbpost['post_pingback'] = false; // Tell WP 2.1 and 2.2 not to process for pingbacks
1512 +
1513 + // This is a ridiculous fucking kludge necessitated by WordPress 2.6 munging authorship meta-data
1514 + add_action('_wp_put_post_revision', array($this, 'fix_revision_meta'));
1515 +
1516 + // Kludge to prevent kses filters from stripping the
1517 + // content of posts when updating without a logged in
1518 + // user who has `unfiltered_html` capability.
1519 + add_filter('content_save_pre', array($this, 'avoid_kses_munge'), 11);
1520 +
1521 + $this->_wp_id = wp_insert_post($dbpost);
1522 +
1523 + // Turn off ridiculous fucking kludges #1 and #2
1524 + remove_action('_wp_put_post_revision', array($this, 'fix_revision_meta'));
1525 + remove_filter('content_save_pre', array($this, 'avoid_kses_munge'), 11);
1526 +
1527 + $this->validate_post_id($dbpost, array(__CLASS__, __FUNCTION__));
1528 +
1529 + // Unfortunately, as of WordPress 2.3, wp_insert_post()
1530 + // *still* offers no way to use a guid of your choice,
1531 + // and munges your post modified timestamp, too.
1532 + $result = $wpdb->query("
1533 + UPDATE $wpdb->posts
1534 + SET
1535 + guid='{$dbpost['guid']}',
1536 + post_modified='{$dbpost['post_modified']}',
1537 + post_modified_gmt='{$dbpost['post_modified_gmt']}'
1538 + WHERE ID='{$this->_wp_id}'
1539 + ");
1540 + else :
1541 + # The right way to do this is the above. But, alas,
1542 + # in earlier versions of WordPress, wp_insert_post has
1543 + # too much behavior (mainly related to pings) that can't
1544 + # be overridden. In WordPress 1.5, it's enough of a
1545 + # resource hog to make PHP segfault after inserting
1546 + # 50-100 posts. This can get pretty annoying, especially
1547 + # if you are trying to update your feeds for the first
1548 + # time.
1549 +
1550 + $result = $wpdb->query("
1551 + INSERT INTO $wpdb->posts
1552 + SET
1553 + guid = '{$dbpost['guid']}',
1554 + post_author = '{$dbpost['post_author']}',
1555 + post_date = '{$dbpost['post_date']}',
1556 + post_date_gmt = '{$dbpost['post_date_gmt']}',
1557 + post_content = '{$dbpost['post_content']}',"
1558 + .(isset($dbpost['post_excerpt']) ? "post_excerpt = '{$dbpost['post_excerpt']}'," : "")."
1559 + post_title = '{$dbpost['post_title']}',
1560 + post_name = '{$dbpost['post_name']}',
1561 + post_modified = '{$dbpost['post_modified']}',
1562 + post_modified_gmt = '{$dbpost['post_modified_gmt']}',
1563 + comment_status = '{$dbpost['comment_status']}',
1564 + ping_status = '{$dbpost['ping_status']}',
1565 + post_status = '{$dbpost['post_status']}'
1566 + ");
1567 + $this->_wp_id = $wpdb->insert_id;
1568 +
1569 + $this->validate_post_id($dbpost, array(__CLASS__, __FUNCTION__));
1570 +
1571 + // WordPress 1.5.x - 2.0.x
1572 + wp_set_post_cats('1', $this->wp_id(), $this->post['post_category']);
1166 1573
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;
1574 + // Since we are not going through official channels, we need to
1575 + // manually tell WordPress that we've published a new post.
1576 + // We need to make sure to do this in order for FeedWordPress
1577 + // to play well with the staticize-reloaded plugin (something
1578 + // that a large aggregator website is going to *want* to be
1579 + // able to use).
1580 + do_action('publish_post', $this->_wp_id);
1173 1581 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 1582 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;
1583 + } /* SyndicatedPost::insert_new() */
1184 1584
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
1585 + function update_existing () {
1586 + global $wpdb;
1187 1587
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);
1588 + // Why the fuck doesn't wp_insert_post already do this?
1589 + $dbpost = $this->normalize_post(/*new=*/ false);
1590 + if (!is_null($dbpost)) :
1591 + if ($this->use_api('wp_insert_post')) :
1592 + $dbpost['post_pingback'] = false; // Tell WP 2.1 and 2.2 not to process for pingbacks
1593 +
1594 + // This is a ridiculous fucking kludge necessitated by WordPress 2.6 munging authorship meta-data
1595 + add_action('_wp_put_post_revision', array($this, 'fix_revision_meta'));
1596 +
1597 + // Kludge to prevent kses filters from stripping the
1598 + // content of posts when updating without a logged in
1599 + // user who has `unfiltered_html` capability.
1600 + add_filter('content_save_pre', array($this, 'avoid_kses_munge'), 11);
1197 1601
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);
1602 + // Don't munge status fields that the user may have reset manually
1603 + if (function_exists('get_post_field')) :
1604 + $doNotMunge = array('post_status', 'comment_status', 'ping_status');
1605 + foreach ($doNotMunge as $field) :
1606 + $dbpost[$field] = get_post_field($field, $this->wp_id());
1607 + endforeach;
1608 + endif;
1201 1609
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;
1610 + $this->_wp_id = wp_insert_post($dbpost);
1206 1611
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("
1612 + // Turn off ridiculous fucking kludges #1 and #2
1613 + remove_action('_wp_put_post_revision', array($this, 'fix_revision_meta'));
1614 + remove_filter('content_save_pre', array($this, 'avoid_kses_munge'), 11);
1615 +
1616 + $this->validate_post_id($dbpost, array(__CLASS__, __FUNCTION__));
1617 +
1618 + // Unfortunately, as of WordPress 2.3, wp_insert_post()
1619 + // munges your post modified timestamp.
1620 + $result = $wpdb->query("
1621 + UPDATE $wpdb->posts
1622 + SET
1623 + post_modified='{$dbpost['post_modified']}',
1624 + post_modified_gmt='{$dbpost['post_modified_gmt']}'
1625 + WHERE ID='{$this->_wp_id}'
1626 + ");
1627 + else :
1628 +
1629 + $result = $wpdb->query("
1211 1630 UPDATE $wpdb->posts
1212 1631 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__);
1632 + post_author = '{$dbpost['post_author']}',
1633 + post_content = '{$dbpost['post_content']}',"
1634 + .(isset($dbpost['post_excerpt']) ? "post_excerpt = '{$dbpost['post_excerpt']}'," : "")."
1635 + post_title = '{$dbpost['post_title']}',
1636 + post_name = '{$dbpost['post_name']}',
1637 + post_modified = '{$dbpost['post_modified']}',
1638 + post_modified_gmt = '{$dbpost['post_modified_gmt']}'
1639 + WHERE guid='{$dbpost['guid']}'
1640 + ");
1641 +
1642 + // WordPress 2.1.x and up
1643 + if (function_exists('wp_set_post_categories')) :
1644 + wp_set_post_categories($this->wp_id(), $this->post['post_category']);
1645 + // WordPress 1.5.x - 2.0.x
1646 + elseif (function_exists('wp_set_post_cats')) :
1647 + wp_set_post_cats('1', $this->wp_id(), $this->post['post_category']);
1648 + // This should never happen.
1649 + else :
1650 + FeedWordPress::critical_bug(__CLASS__.'::'.__FUNCTION.'(): no post categorizing function', array("dbpost" => $dbpost, "this" => $this), __LINE__);
1651 + endif;
1652 +
1653 + // Since we are not going through official channels, we need to
1654 + // manually tell WordPress that we've published a new post.
1655 + // We need to make sure to do this in order for FeedWordPress
1656 + // to play well with the staticize-reloaded plugin (something
1657 + // that a large aggregator website is going to *want* to be
1658 + // able to use).
1659 + do_action('edit_post', $this->post['ID']);
1250 1660 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 1661 endif;
1263 - } /* SyndicatedPost::insert_new() */
1662 + } /* SyndicatedPost::update_existing() */
1264 1663
1265 - function update_existing () {
1664 + /**
1665 + * SyndicatedPost::normalize_post()
1666 + *
1667 + * @param bool $new If true, this post is to be inserted anew. If false, it is an update of an existing post.
1668 + * @return array A normalized representation of the post ready to be inserted into the database or sent to the WordPress API functions
1669 + */
1670 + function normalize_post ($new = true) {
1266 1671 global $wpdb;
1267 1672
1673 + $out = array();
1674 +
1268 1675 // Why the fuck doesn't wp_insert_post already do this?
1269 - $dbpost = array();
1270 1676 foreach ($this->post as $key => $value) :
1271 1677 if (is_string($value)) :
1272 - $dbpost[$key] = $wpdb->escape($value);
1678 + $out[$key] = $wpdb->escape($value);
1273 1679 else :
1274 - $dbpost[$key] = $value;
1680 + $out[$key] = $value;
1275 1681 endif;
1276 1682 endforeach;
1277 1683
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
1684 + if (strlen($out['post_title'].$out['post_content'].$out['post_excerpt']) == 0) :
1685 + // FIXME: Option for filtering out empty posts
1686 + endif;
1687 + if (strlen($out['post_title'])==0) :
1688 + $offset = (int) get_option('gmt_offset') * 60 * 60;
1689 + $out['post_title'] =
1690 + $this->post['meta']['syndication_source']
1691 + .' '.gmdate('Y-m-d H:i:s', $this->published() + $offset);
1692 + // FIXME: Option for what to fill a blank title with...
1693 + endif;
1280 1694
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'));
1695 + return $out;
1696 + }
1283 1697
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 - ");
1698 + /**
1699 + * SyndicatedPost::validate_post_id()
1700 + *
1701 + * @param array $dbpost An array representing the post we attempted to insert or update
1702 + * @param mixed $ns A string or array representing the namespace (class, method) whence this method was called.
1703 + */
1704 + function validate_post_id ($dbpost, $ns) {
1705 + if (is_array($ns)) : $ns = implode('::', $ns);
1706 + else : $ns = (string) $ns; endif;
1707 +
1708 + // This should never happen.
1709 + if (!is_numeric($this->_wp_id) or ($this->_wp_id == 0)) :
1710 + FeedWordPress::critical_bug(
1711 + /*name=*/ $ns.'::_wp_id',
1712 + /*var =*/ array(
1713 + "\$this->_wp_id" => $this->_wp_id,
1714 + "\$dbpost" => $dbpost,
1715 + "\$this" => $this
1716 + ),
1717 + /*line # =*/ __LINE__
1718 + );
1719 + endif;
1720 + } /* SyndicatedPost::validate_post_id() */
1323 1721
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 1722 /**
1346 1723 * SyndicatedPost::fix_revision_meta() - Fixes the way WP 2.6+ fucks up
1347 1724 * meta-data (authorship, etc.) when storing revisions of an updated
1348 1725 * syndicated post.
@@ -1449,9 +1826,9 @@
1449 1826
1450 1827 // SyndicatedPost::author_id (): get the ID for an author name from
1451 1828 // the feed. Create the author if necessary.
1452 1829 function author_id ($unfamiliar_author = 'create') {
1453 - global $wpdb, $wp_db_version; // test for WordPress 2.0 database schema
1830 + global $wpdb;
1454 1831
1455 1832 $a = $this->author();
1456 1833 $author = $a['name'];
1457 1834 $email = $a['email'];
@@ -1456,8 +1833,15 @@
1456 1833 $author = $a['name'];
1457 1834 $email = $a['email'];
1458 1835 $url = $a['uri'];
1459 1836
1837 + $match_author_by_email = !('yes' == get_option("feedwordpress_do_not_match_author_by_email"));
1838 + if ($match_author_by_email and !FeedWordPress::is_null_email($email)) :
1839 + $test_email = $email;
1840 + else :
1841 + $test_email = NULL;
1842 + endif;
1843 +
1460 1844 // Never can be too careful...
1461 1845 $login = sanitize_user($author, /*strict=*/ true);
1462 1846 $login = apply_filters('pre_user_login', $login);
1463 1847
@@ -1466,8 +1850,9 @@
1466 1850
1467 1851 $reg_author = $wpdb->escape(preg_quote($author));
1468 1852 $author = $wpdb->escape($author);
1469 1853 $email = $wpdb->escape($email);
1854 + $test_email = $wpdb->escape($test_email);
1470 1855 $url = $wpdb->escape($url);
1471 1856
1472 1857 // Check for an existing author rule....
1473 1858 if (isset($this->link->settings['map authors']['name'][strtolower(trim($author))])) :
@@ -1486,34 +1871,10 @@
1486 1871
1487 1872 else :
1488 1873 // Check the database for an existing author record that might fit
1489 1874
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 1875 #-- WordPress 2.0+
1515 - elseif ($wp_db_version >= 2966) :
1876 + if (fwp_test_wp_version(FWP_SCHEMA_HAS_USERMETA)) :
1516 1877
1517 1878 // First try the user core data table.
1518 1879 $id = $wpdb->get_var(
1519 1880 "SELECT ID FROM $wpdb->users
@@ -1520,9 +1881,9 @@
1520 1881 WHERE
1521 1882 TRIM(LCASE(user_login)) = TRIM(LCASE('$login'))
1522 1883 OR (
1523 1884 LENGTH(TRIM(LCASE(user_email))) > 0
1524 - AND TRIM(LCASE(user_email)) = TRIM(LCASE('$email'))
1885 + AND TRIM(LCASE(user_email)) = TRIM(LCASE('$test_email'))
1525 1886 )
1526 1887 OR TRIM(LCASE(user_nicename)) = TRIM(LCASE('$nice_author'))
1527 1888 ");
1528 1889
@@ -1542,8 +1903,33 @@
1542 1903 )
1543 1904 )
1544 1905 ");
1545 1906 endif;
1907 +
1908 + #-- WordPress 1.5.x
1909 + else :
1910 + $id = $wpdb->get_var(
1911 + "SELECT ID from $wpdb->users
1912 + WHERE
1913 + TRIM(LCASE(user_login)) = TRIM(LCASE('$login')) OR
1914 + (
1915 + LENGTH(TRIM(LCASE(user_email))) > 0
1916 + AND TRIM(LCASE(user_email)) = TRIM(LCASE('$test_email'))
1917 + ) OR
1918 + TRIM(LCASE(user_firstname)) = TRIM(LCASE('$author')) OR
1919 + TRIM(LCASE(user_nickname)) = TRIM(LCASE('$author')) OR
1920 + TRIM(LCASE(user_nicename)) = TRIM(LCASE('$nice_author')) OR
1921 + TRIM(LCASE(user_description)) = TRIM(LCASE('$author')) OR
1922 + (
1923 + LOWER(user_description)
1924 + RLIKE CONCAT(
1925 + '(^|\\n)a\\.?k\\.?a\\.?( |\\t)*:?( |\\t)*',
1926 + LCASE('$reg_author'),
1927 + '( |\\t|\\r)*(\\n|\$)'
1928 + )
1929 + )
1930 + ");
1931 +
1546 1932 endif;
1547 1933
1548 1934 // ... if you don't find one, then do what you need to do
1549 1935 if (is_null($id)) :
@@ -1596,9 +1982,9 @@
1596 1982 $cat_ids[] = $cat_id;
1597 1983 elseif (get_category($cat_id)) :
1598 1984 $cat_ids[] = $cat_id;
1599 1985 endif;
1600 - else :
1986 + elseif (strlen($cat_name) > 0) :
1601 1987 $esc = $wpdb->escape($cat_name);
1602 1988 $resc = $wpdb->escape(preg_quote($cat_name));
1603 1989
1604 1990 // WordPress 2.3+
@@ -1620,9 +2006,13 @@
1620 2006 elseif ('tag'==$unfamiliar_category) :
1621 2007 $tags[] = $cat_name;
1622 2008 elseif ('create'===$unfamiliar_category) :
1623 2009 $term = wp_insert_term($cat_name, 'category');
1624 - $cat_ids[] = $term['term_id'];
2010 + if (is_wp_error($term)) :
2011 + FeedWordPress::noncritical_bug('term insertion problem', array('cat_name' => $cat_name, 'term' => $term, 'this' => $this), __LINE__);
2012 + else :
2013 + $cat_ids[] = $term['term_id'];
2014 + endif;
1625 2015 endif;
1626 2016
1627 2017 // WordPress 1.5.x - 2.2.x
1628 2018 else :
@@ -1854,25 +2244,87 @@
1854 2244
1855 2245 return $author;
1856 2246 } // SyndicatedPost::author()
1857 2247
2248 + var $uri_attrs = array (
2249 + array('a', 'href'),
2250 + array('applet', 'codebase'),
2251 + array('area', 'href'),
2252 + array('blockquote', 'cite'),
2253 + array('body', 'background'),
2254 + array('del', 'cite'),
2255 + array('form', 'action'),
2256 + array('frame', 'longdesc'),
2257 + array('frame', 'src'),
2258 + array('iframe', 'longdesc'),
2259 + array('iframe', 'src'),
2260 + array('head', 'profile'),
2261 + array('img', 'longdesc'),
2262 + array('img', 'src'),
2263 + array('img', 'usemap'),
2264 + array('input', 'src'),
2265 + array('input', 'usemap'),
2266 + array('ins', 'cite'),
2267 + array('link', 'href'),
2268 + array('object', 'classid'),
2269 + array('object', 'codebase'),
2270 + array('object', 'data'),
2271 + array('object', 'usemap'),
2272 + array('q', 'cite'),
2273 + array('script', 'src')
2274 + ); /* var SyndicatedPost::$uri_attrs */
2275 +
2276 + var $_base = null;
2277 +
2278 + function resolve_single_relative_uri ($refs) {
2279 + $tag = FeedWordPressHTML::attributeMatch($refs);
2280 + $url = Relative_URI::resolve($tag['value'], $this->_base);
2281 + return $tag['prefix'] . $url . $tag['suffix'];
2282 + } /* function SyndicatedPost::resolve_single_relative_uri() */
2283 +
2284 + function resolve_relative_uris ($content, $obj) {
2285 + # The MagpieRSS upgrade has some `xml:base` support baked in.
2286 + # However, sometimes people do silly things, like putting
2287 + # relative URIs out on a production RSS 2.0 feed or other feeds
2288 + # with no good support for `xml:base`. So we'll do our best to
2289 + # try to catch any remaining relative URIs and resolve them as
2290 + # best we can.
2291 + $obj->_base = $obj->item['link']; // Reset the base for resolving relative URIs
2292 +
2293 + foreach ($obj->uri_attrs as $pair) :
2294 + list($tag, $attr) = $pair;
2295 + $pattern = FeedWordPressHTML::attributeRegex($tag, $attr);
2296 + $content = preg_replace_callback (
2297 + $pattern,
2298 + array(&$obj, 'resolve_single_relative_uri'),
2299 + $content
2300 + );
2301 + endforeach;
2302 +
2303 + return $content;
2304 + } /* function SyndicatedPost::resolve_relative_uris () */
2305 +
1858 2306 var $strip_attrs = array (
1859 - array('[a-z]+', 'style'),
1860 - array('[a-z]+', 'target'),
2307 + array('[a-z]+', 'target'),
2308 +// array('[a-z]+', 'style'),
2309 +// array('[a-z]+', 'on[a-z]+'),
1861 2310 );
2311 +
2312 + function strip_attribute_from_tag ($refs) {
2313 + $tag = FeedWordPressHTML::attributeMatch($refs);
2314 + return $tag['before_attribute'].$tag['after_attribute'];
2315 + }
2316 +
1862 2317 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 2318 # This kind of sucks. I intend to replace it with
1869 2319 # lib_filter sometime soon.
1870 2320 foreach ($obj->strip_attrs as $pair):
1871 2321 list($tag,$attr) = $pair;
1872 - $content = preg_replace (
1873 - ":(<$tag [^>]*)($attr=(\"[^\">]*\"|[^>\\s]+))([^>]*>):i",
1874 - "\\1\\4",
2322 + $pattern = FeedWordPressHTML::attributeRegex($tag, $attr);
2323 +
2324 + $content = preg_replace_callback (
2325 + $pattern,
2326 + array(&$obj, 'strip_attribute_from_tag'),
1875 2327 $content
1876 2328 );
1877 2329 endforeach;
1878 2330 return $content;
@@ -1878,397 +2330,10 @@
1878 2330 return $content;
1879 2331 }
1880 2332 } // class SyndicatedPost
1881 2333
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.
2334 +require_once(dirname(__FILE__) . '/syndicatedlink.class.php');
1915 2335
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 2336 ################################################################################
2272 2337 ## XML-RPC HOOKS: accept XML-RPC update pings from Contributors ################
2273 2338 ################################################################################
2274 2339
@@ -2289,169 +2354,8 @@
2289 2354
2290 2355 return array('flerror' => false, 'message' => "Thanks for the ping.".implode(' and', $mesg));
2291 2356 endif;
2292 2357 }
2293 -
2294 -################################################################################
2295 -## class FeedFinder: find likely feeds using autodetection and/or guesswork ####
2296 -################################################################################
2297 -
2298 -class FeedFinder {
2299 - var $uri = NULL;
2300 - var $_cache_uri = NULL;
2301 -
2302 - var $verify = FALSE;
2303 -
2304 - var $_data = NULL;
2305 - var $_head = NULL;
2306 -
2307 - # -- Recognition patterns
2308 - var $_feed_types = array(
2309 - 'application/rss+xml',
2310 - 'text/xml',
2311 - 'application/atom+xml',
2312 - 'application/x.atom+xml',
2313 - 'application/x-atom+xml'
2314 - );
2315 - var $_feed_markers = array('\\<feed', '\\<rss', 'xmlns="http://purl.org/rss/1.0');
2316 - var $_html_markers = array('\\<html');
2317 - var $_obvious_feed_url = array('[./]rss', '[./]rdf', '[./]atom', '[./]feed', '\.xml');
2318 - var $_maybe_feed_url = array ('rss', 'rdf', 'atom', 'feed', 'xml');
2319 -
2320 - function FeedFinder ($uri = NULL, $verify = TRUE) {
2321 - $this->uri = $uri; $this->verify = $verify;
2322 - } /* FeedFinder::FeedFinder () */
2323 -
2324 - function find ($uri = NULL) {
2325 - $ret = array ();
2326 - if (!is_null($this->data($uri))) {
2327 - if ($this->is_feed($uri)) {
2328 - $ret = array($this->uri);
2329 - } else {
2330 - // Assume that we have HTML or XHTML (even if we don't, who's it gonna hurt?)
2331 - // Autodiscovery is the preferred method
2332 - $href = $this->_link_rel_feeds();
2333 -
2334 - // ... but we'll also take the little orange buttons
2335 - $href = array_merge($href, $this->_a_href_feeds(TRUE));
2336 -
2337 - // If all that failed, look harder
2338 - if (count($href) == 0) $href = $this->_a_href_feeds(FALSE);
2339 -
2340 - // Verify feeds and resolve relative URIs
2341 - foreach ($href as $u) {
2342 - $the_uri = Relative_URI::resolve($u, $this->uri);
2343 - if ($this->verify) {
2344 - $feed =& new FeedFinder($the_uri);
2345 - if ($feed->is_feed()) $ret[] = $the_uri;
2346 - $feed = NULL;
2347 - } else {
2348 - $ret[] = $the_uri;
2349 - }
2350 - } /* foreach */
2351 - } /* if */
2352 - } /* if */
2353 - return array_unique($ret);
2354 - } /* FeedFinder::find () */
2355 -
2356 - function data ($uri = NULL) {
2357 - $this->_get($uri);
2358 - return $this->_data;
2359 - }
2360 -
2361 - function is_feed ($uri = NULL) {
2362 - $data = $this->data($uri);
2363 -
2364 - return (
2365 - preg_match (
2366 - "\007(".implode('|',$this->_feed_markers).")\007i",
2367 - $data
2368 - ) and !preg_match (
2369 - "\007(".implode('|',$this->_html_markers).")\007i",
2370 - $data
2371 - )
2372 - );
2373 - } /* FeedFinder::is_feed () */
2374 -
2375 - # --- Private methods ---
2376 - function _get ($uri = NULL) {
2377 - if ($uri) $this->uri = $uri;
2378 -
2379 - // Is the result not yet cached?
2380 - 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;
2393 -
2394 - // Kilroy was here
2395 - $this->_cache_uri = $this->uri;
2396 - endif;
2397 - } /* FeedFinder::_get () */
2398 -
2399 - function _link_rel_feeds () {
2400 - $links = $this->_tags('link');
2401 - $link_count = count($links);
2402 -
2403 - // now figure out which one points to the RSS file
2404 - $href = array ();
2405 - for ($n=0; $n<$link_count; $n++) {
2406 - if (strtolower($links[$n]['rel']) == 'alternate') {
2407 - if (in_array(strtolower($links[$n]['type']), $this->_feed_types)) {
2408 - $href[] = $links[$n]['href'];
2409 - } /* if */
2410 - } /* if */
2411 - } /* for */
2412 - return $href;
2413 - }
2414 -
2415 - function _a_href_feeds ($obvious = TRUE) {
2416 - $pattern = ($obvious ? $this->_obvious_feed_url : $this->_maybe_feed_url);
2417 -
2418 - $links = $this->_tags('a');
2419 - $link_count = count($links);
2420 -
2421 - // now figure out which one points to the RSS file
2422 - $href = array ();
2423 - for ($n=0; $n<$link_count; $n++) {
2424 - if (preg_match("\007(".implode('|',$pattern).")\007i", $links[$n]['href'])) {
2425 - $href[] = $links[$n]['href'];
2426 - } /* if */
2427 - } /* for */
2428 - return $href;
2429 - }
2430 -
2431 - function _tags ($tag) {
2432 - $html = $this->data();
2433 -
2434 - // search through the HTML, save all <link> tags
2435 - // and store each link's attributes in an associative array
2436 - preg_match_all('/<'.$tag.'\s+(.*?)\s*\/?>/si', $html, $matches);
2437 - $links = $matches[1];
2438 - $ret = array();
2439 - $link_count = count($links);
2440 - for ($n=0; $n<$link_count; $n++) {
2441 - $attributes = preg_split('/\s+/s', $links[$n]);
2442 - foreach($attributes as $attribute) {
2443 - $att = preg_split('/\s*=\s*/s', $attribute, 2);
2444 - if (isset($att[1])) {
2445 - $att[1] = preg_replace('/([\'"]?)(.*)\1/', '$2', $att[1]);
2446 - $final_link[strtolower($att[0])] = $att[1];
2447 - } /* if */
2448 - } /* foreach */
2449 - $ret[$n] = $final_link;
2450 - } /* for */
2451 - return $ret;
2452 - }
2453 -} /* class FeedFinder */
2454 2358
2455 2359 # Relative URI static class: PHP class for resolving relative URLs
2456 2360 #
2457 2361 # This class is derived (under the terms of the GPL) from URL Class 0.3 by