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

feedwordpress.php in FeedWordPress 2010.0127, at feedwordpress.php

1,419 lines 49.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /*
3 Plugin Name: FeedWordPress
4 Plugin URI: http://feedwordpress.radgeek.com/
5 Description: simple and flexible Atom/RSS syndication for WordPress
6 Version: 2010.0127
7 Author: Charles Johnson
8 Author URI: http://radgeek.com/
9 License: GPL
10 */
11
12 # This uses code derived from:
13 # - wp-rss-aggregate.php by Kellan Elliot-McCrea <kellan@protest.net>
14 # - HTTP Navigator 2 by Keyvan Minoukadeh <keyvan@k1m.com>
15 # - Ultra-Liberal Feed Finder by Mark Pilgrim <mark@diveintomark.org>
16 # according to the terms of the GNU General Public License.
17 #
18 # INSTALLATION: see readme.txt or <http://projects.radgeek.com/install>
19 #
20 # USAGE: once FeedWordPress is installed, you manage just about everything from
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
24 # <http://www.zyx.com/blog>, XML-RPC requests should be sent to
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.
28
29 # -- Don't change these unless you know what you're doing...
30
31 define ('FEEDWORDPRESS_VERSION', '2010.0127');
32 define ('FEEDWORDPRESS_AUTHOR_CONTACT', 'http://radgeek.com/contact');
33
34 // Defaults
35 define ('DEFAULT_SYNDICATION_CATEGORY', 'Contributors');
36 define ('DEFAULT_UPDATE_PERIOD', 60); // value in minutes
37
38 if (isset($_REQUEST['feedwordpress_debug'])) :
39 $feedwordpress_debug = $_REQUEST['feedwordpress_debug'];
40 else :
41 $feedwordpress_debug = get_option('feedwordpress_debug');
42 if (is_string($feedwordpress_debug)) :
43 $feedwordpress_debug = ($feedwordpress_debug == 'yes');
44 endif;
45 endif;
46 define ('FEEDWORDPRESS_DEBUG', $feedwordpress_debug);
47
48 define ('FEEDWORDPRESS_CAT_SEPARATOR_PATTERN', '/[:\n]/');
49 define ('FEEDWORDPRESS_CAT_SEPARATOR', "\n");
50
51 define ('FEEDVALIDATOR_URI', 'http://feedvalidator.org/check.cgi');
52
53 define ('FEEDWORDPRESS_FRESHNESS_INTERVAL', 10*60); // Every ten minutes
54
55 define ('FWP_SCHEMA_HAS_USERMETA', 2966);
56 define ('FWP_SCHEMA_20', 3308); // Database schema # for WP 2.0
57 define ('FWP_SCHEMA_21', 4772); // Database schema # for WP 2.1
58 define ('FWP_SCHEMA_23', 5495); // Database schema # for WP 2.3
59 define ('FWP_SCHEMA_25', 7558); // Database schema # for WP 2.5
60 define ('FWP_SCHEMA_26', 8201); // Database schema # for WP 2.6
61 define ('FWP_SCHEMA_27', 9872); // Database schema # for WP 2.7
62 define ('FWP_SCHEMA_28', 11548); // Database schema # for WP 2.8
63 define ('FWP_SCHEMA_29', 12329); // Database schema # for WP 2.9
64
65 if (FEEDWORDPRESS_DEBUG) :
66 // Help us to pick out errors, if any.
67 ini_set('error_reporting', E_ALL & ~E_NOTICE);
68 ini_set('display_errors', true);
69 define('MAGPIE_DEBUG', true);
70
71 // When testing we don't want cache issues to interfere. But this is
72 // a VERY BAD SETTING for a production server. Webmasters will eat your
73 // face for breakfast if you use it, and the baby Jesus will cry. So
74 // make sure FEEDWORDPRESS_DEBUG is FALSE for any site that will be
75 // used for more than testing purposes!
76 define('MAGPIE_CACHE_AGE', 1);
77 else :
78 define('MAGPIE_DEBUG', false);
79
80 define('MAGPIE_CACHE_AGE', 1*60);
81 endif;
82
83 // Note that the rss-functions.php that comes prepackaged with WordPress is
84 // old & busted. For the new hotness, drop a copy of rss.php from
85 // this archive into wp-includes/rss.php
86
87 if (is_readable(ABSPATH . WPINC . '/rss.php')) :
88 require_once (ABSPATH . WPINC . '/rss.php');
89 else :
90 require_once (ABSPATH . WPINC . '/rss-functions.php');
91 endif;
92
93 if (isset($wp_db_version)) :
94 if ($wp_db_version >= FWP_SCHEMA_23) :
95 require_once (ABSPATH . WPINC . '/registration.php'); // for wp_insert_user
96 elseif ($wp_db_version >= FWP_SCHEMA_21) : // WordPress 2.1 and 2.2, but not 2.3
97 require_once (ABSPATH . WPINC . '/registration.php'); // for wp_insert_user
98 require_once (ABSPATH . 'wp-admin/admin-db.php'); // for wp_insert_category
99 elseif ($wp_db_version >= FWP_SCHEMA_20) : // WordPress 2.0
100 require_once (ABSPATH . WPINC . '/registration-functions.php'); // for wp_insert_user
101 require_once (ABSPATH . 'wp-admin/admin-db.php'); // for wp_insert_category
102 endif;
103 endif;
104
105 require_once(dirname(__FILE__) . '/compatability.php'); // LEGACY API: Replicate or mock up functions for legacy support purposes
106 require_once(dirname(__FILE__) . '/feedwordpresshtml.class.php');
107
108 // Magic quotes are just about the stupidest thing ever.
109 if (is_array($_POST)) :
110 $fwp_post = stripslashes_deep($_POST);
111 endif;
112
113 // Get the path relative to the plugins directory in which FWP is stored
114 preg_match (
115 '|/wp-content/plugins/(.+)$|',
116 dirname(__FILE__),
117 $ref
118 );
119
120 if (isset($ref[1])) :
121 $fwp_path = $ref[1];
122 else : // Something went wrong. Let's just guess.
123 $fwp_path = 'feedwordpress';
124 endif;
125
126 function feedwordpress_admin_scripts () {
127 wp_enqueue_script('post'); // for magic tag and category boxes
128 wp_enqueue_script('admin-forms'); // for checkbox selection
129 }
130
131 // If this is a FeedWordPress admin page, queue up scripts for AJAX functions that FWP uses
132 // If it is a display page or a non-FeedWordPress admin page, don't.
133 if (is_admin() and isset($_REQUEST['page']) and preg_match("|^{$fwp_path}/|", $_REQUEST['page'])) :
134 if (function_exists('wp_enqueue_script')) :
135 if (FeedWordPressCompatibility::test_version(FWP_SCHEMA_29)) :
136 add_action('admin_print_scripts', 'feedwordpress_admin_scripts');
137 elseif (FeedWordPressCompatibility::test_version(FWP_SCHEMA_25)) :
138 wp_enqueue_script('post'); // for magic tag and category boxes
139 wp_enqueue_script('thickbox'); // for fold-up boxes
140 wp_enqueue_script('admin-forms'); // for checkbox selection
141 else :
142 wp_enqueue_script( 'ajaxcat' ); // Provides the handy-dandy new category text box
143 endif;
144 endif;
145 if (function_exists('wp_enqueue_style')) :
146 if (fwp_test_wp_version(FWP_SCHEMA_25)) :
147 wp_enqueue_style('dashboard');
148 endif;
149 endif;
150 if (function_exists('wp_admin_css')) :
151 if (fwp_test_wp_version(FWP_SCHEMA_25)) :
152 wp_admin_css('css/dashboard');
153 endif;
154 endif;
155 endif;
156
157 if (!FeedWordPress::needs_upgrade()) : // only work if the conditions are safe!
158
159 # Syndicated items are generally received in output-ready (X)HTML and
160 # should not be folded, crumpled, mutilated, or spindled by WordPress
161 # formatting filters. But we don't want to interfere with filters for
162 # any locally-authored posts, either.
163 #
164 # What WordPress should really have is a way for upstream filters to
165 # stop downstream filters from running at all. Since it doesn't, and
166 # since a downstream filter can't access the original copy of the text
167 # that is being filtered, what we will do here is (1) save a copy of the
168 # original text upstream, before any other filters run, and then (2)
169 # retrieve that copy downstream, after all the other filters run, *if*
170 # this is a syndicated post
171
172 add_filter('the_content', 'feedwordpress_preserve_syndicated_content', -10000);
173 add_filter('the_content', 'feedwordpress_restore_syndicated_content', 10000);
174
175 add_action('atom_entry', 'feedwordpress_item_feed_data');
176
177 # Filter in original permalinks if the user wants that
178 add_filter('post_link', 'syndication_permalink', 1);
179
180 # WTF? By default, wp_insert_link runs incoming link_url and link_rss
181 # URIs through default filters that include `wp_kses()`. But `wp_kses()`
182 # just happens to escape any occurrence of & to &amp; -- which just
183 # happens to fuck up any URI with a & to separate GET parameters.
184 remove_filter('pre_link_rss', 'wp_filter_kses');
185 remove_filter('pre_link_url', 'wp_filter_kses');
186
187 # Admin menu
188 add_action('admin_menu', 'fwp_add_pages');
189 add_action('admin_notices', 'fwp_check_debug');
190 add_action('admin_notices', 'fwp_check_magpie');
191 add_action('init', 'feedwordpress_check_for_magpie_fix');
192
193 add_action('admin_menu', 'feedwordpress_add_post_edit_controls');
194 add_action('save_post', 'feedwordpress_save_post_edit_controls');
195
196 # Inbound XML-RPC update methods
197 add_filter('xmlrpc_methods', 'feedwordpress_xmlrpc_hook');
198
199 # Outbound XML-RPC ping reform
200 remove_action('publish_post', 'generic_ping'); // WP 1.5.x
201 remove_action('do_pings', 'do_all_pings', 10, 1); // WP 2.1, 2.2
202 remove_action('publish_post', '_publish_post_hook', 5, 1); // WP 2.3
203
204 add_action('publish_post', 'fwp_publish_post_hook', 5, 1);
205 add_action('do_pings', 'fwp_do_pings', 10, 1);
206 add_action('feedwordpress_update', 'fwp_hold_pings');
207 add_action('feedwordpress_update_complete', 'fwp_release_pings');
208
209 # Hook in logging functions only if the logging option is ON
210 $update_logging = get_option('feedwordpress_update_logging');
211 if ($update_logging == 'yes') :
212 add_action('post_syndicated_item', 'log_feedwordpress_post', 100);
213 add_action('update_syndicated_item', 'log_feedwordpress_update_post', 100);
214 add_action('feedwordpress_update', 'log_feedwordpress_update_feeds', 100);
215 add_action('feedwordpress_check_feed', 'log_feedwordpress_check_feed', 100);
216 add_action('feedwordpress_update_complete', 'log_feedwordpress_update_complete', 100);
217 endif;
218
219 if (FeedWordPress::update_requested() and FEEDWORDPRESS_DEBUG) :
220 add_action('post_syndicated_item', 'debug_out_feedwordpress_post', 100);
221 add_action('update_syndicated_item', 'debug_out_feedwordpress_update_post', 100);
222 add_action('feedwordpress_update', 'debug_out_feedwordpress_update_feeds', 100);
223 add_action('feedwordpress_check_feed', 'debug_out_feedwordpress_check_feed', 100);
224 add_action('feedwordpress_update_complete', 'debug_out_feedwordpress_update_complete', 100);
225 endif;
226
227 # Cron-less auto-update. Hooray!
228 $autoUpdateHook = get_option('feedwordpress_automatic_updates');
229 if ($autoUpdateHook != 'init') :
230 $autoUpdateHook = 'shutdown';
231 endif;
232 add_action($autoUpdateHook, 'feedwordpress_auto_update');
233
234 add_action('init', 'feedwordpress_update_magic_url');
235
236 # Default sanitizers
237 add_filter('syndicated_item_content', array('SyndicatedPost', 'resolve_relative_uris'), 0, 2);
238 add_filter('syndicated_item_content', array('SyndicatedPost', 'sanitize_content'), 0, 2);
239
240 else :
241 # Hook in the menus, which will just point to the upgrade interface
242 add_action('admin_menu', 'fwp_add_pages');
243 endif; // if (!FeedWordPress::needs_upgrade())
244
245 function feedwordpress_check_for_magpie_fix () {
246 if (isset($_POST['action']) and $_POST['action']=='fix_magpie_version') :
247 FeedWordPressCompatibility::validate_http_request(/*action=*/ 'feedwordpress_fix_magpie', /*capability=*/ 'edit_files');
248
249 $back_to = $_SERVER['REQUEST_URI'];
250 if (isset($_POST['ignore'])) :
251 // kill error message by telling it we ignored the upgrade request for this version
252 update_option('feedwordpress_magpie_ignored_upgrade_to', EXPECTED_MAGPIE_VERSION);
253 $ret = 'ignored';
254 elseif (isset($_POST['upgrade'])) :
255 $source = dirname(__FILE__)."/MagpieRSS-upgrade/rss.php";
256 $destination = ABSPATH . WPINC . '/rss.php';
257 $success = @copy($source, $destination);
258
259 // Copy over rss-functions.php, too, to avoid collisions
260 // on pre-lapsarian versions of WordPress.
261 if ($success) :
262 $source = dirname(__FILE__)."/MagpieRSS-upgrade/rss-functions.php";
263 $destination = ABSPATH . WPINC . '/rss-functions.php';
264 $success = @copy($source, $destination);
265 endif;
266 $ret = (int) $success;
267 endif;
268
269 if (strpos($back_to, '?')===false) : $sep = '?';
270 else : $sep = '&';
271 endif;
272
273 header("Location: {$back_to}{$sep}feedwordpress_magpie_fix=".$ret);
274 exit;
275 endif;
276 } /* feedwordpress_check_for_magpie_fix() */
277
278 function feedwordpress_auto_update () {
279 if (FeedWordPress::stale()) :
280 $feedwordpress =& new FeedWordPress;
281 $feedwordpress->update();
282 endif;
283 } /* feedwordpress_auto_update () */
284
285 function feedwordpress_update_magic_url () {
286 global $wpdb;
287
288 // Explicit update request in the HTTP request (e.g. from a cron job)
289 if (FeedWordPress::update_requested()) :
290 $feedwordpress =& new FeedWordPress;
291 $feedwordpress->update(FeedWordPress::update_requested_url());
292
293 if (FEEDWORDPRESS_DEBUG and count($wpdb->queries) > 0) :
294 $mysqlTime = 0.0;
295 $byTime = array();
296 foreach ($wpdb->queries as $query) :
297 $time = $query[1] * 1000000.0;
298 $mysqlTime += $query[1];
299 if (!isset($byTime[$time])) : $byTime[$time] = array(); endif;
300 $byTime[$time][] = $query[0]. ' // STACK: ' . $query[2];
301 endforeach;
302 krsort($byTime);
303
304 foreach ($byTime as $time => $querySet) :
305 foreach ($querySet as $query) :
306 print "[".(sprintf('%4.4f', $time/1000.0)) . "ms] $query\n";
307 endforeach;
308 endforeach;
309 echo "[feedwordpress] $wpdb->num_queries queries. $mysqlTime seconds in MySQL. Total of "; timer_stop(1); print " seconds.";
310 endif;
311
312
313 // Magic URL should return nothing but a 200 OK header packet
314 // when successful.
315 exit;
316 endif;
317 } /* feedwordpress_magic_update_url () */
318
319 ################################################################################
320 ## LOGGING FUNCTIONS: log status updates to error_log if you want it ###########
321 ################################################################################
322
323 function log_feedwordpress_post ($id) {
324 $post = wp_get_single_post($id);
325 error_log("[".date('Y-m-d H:i:s')."][feedwordpress] posted "
326 ."'{$post->post_title}' ({$post->post_date})");
327 }
328
329 function log_feedwordpress_update_post ($id) {
330 $post = wp_get_single_post($id);
331 error_log("[".date('Y-m-d H:i:s')."][feedwordpress] updated "
332 ."'{$post->post_title}' ({$post->post_date})"
333 ." (as of {$post->post_modified})");
334 }
335
336 function log_feedwordpress_update_feeds ($uri) {
337 error_log("[".date('Y-m-d H:i:s')."][feedwordpress] update('$uri')");
338 }
339
340 function log_feedwordpress_check_feed ($feed) {
341 $uri = $feed['link/uri']; $name = $feed['link/name'];
342 error_log("[".date('Y-m-d H:i:s')."][feedwordpress] Examining $name <$uri>");
343 }
344
345 function log_feedwordpress_update_complete ($delta) {
346 $mesg = array();
347 if (isset($delta['new'])) $mesg[] = 'added '.$delta['new'].' new posts';
348 if (isset($delta['updated'])) $mesg[] = 'updated '.$delta['updated'].' existing posts';
349 if (empty($mesg)) $mesg[] = 'nothing changed';
350
351 error_log("[".date('Y-m-d H:i:s')."][feedwordpress] "
352 .(is_null($delta) ? "Error: I don't syndicate that URI"
353 : implode(' and ', $mesg)));
354 }
355
356 function debug_out_feedwordpress_post ($id) {
357 $post = wp_get_single_post($id);
358 print ("[".date('Y-m-d H:i:s')."][feedwordpress] posted "
359 ."'{$post->post_title}' ({$post->post_date})\n");
360 }
361
362 function debug_out_feedwordpress_update_post ($id) {
363 $post = wp_get_single_post($id);
364 print ("[".date('Y-m-d H:i:s')."][feedwordpress] updated "
365 ."'{$post->post_title}' ({$post->post_date})"
366 ." (as of {$post->post_modified})\n");
367 }
368
369 function debug_out_feedwordpress_update_feeds ($uri) {
370 print ("[".date('Y-m-d H:i:s')."][feedwordpress] update('$uri')\n");
371 }
372
373 function debug_out_feedwordpress_check_feed ($feed) {
374 $uri = $feed['link/uri']; $name = $feed['link/name'];
375 print ("[".date('Y-m-d H:i:s')."][feedwordpress] Examining $name <$uri>\n");
376 }
377
378 function debug_out_feedwordpress_update_complete ($delta) {
379 $mesg = array();
380 if (isset($delta['new'])) $mesg[] = 'added '.$delta['new'].' new posts';
381 if (isset($delta['updated'])) $mesg[] = 'updated '.$delta['updated'].' existing posts';
382 if (empty($mesg)) $mesg[] = 'nothing changed';
383
384 print ("[".date('Y-m-d H:i:s')."][feedwordpress] "
385 .(is_null($delta) ? "Error: I don't syndicate that URI"
386 : implode(' and ', $mesg))."\n");
387 }
388
389 ################################################################################
390 ## TEMPLATE API: functions to make your templates syndication-aware ############
391 ################################################################################
392
393 function is_syndicated ($id = NULL) { return (strlen(get_syndication_feed_id($id)) > 0); }
394
395 function get_syndication_source_link ($original = NULL, $id = NULL) {
396 if (is_null($original)) : $original = FeedWordPress::use_aggregator_source_data();
397 endif;
398
399 if ($original) : $vals = get_post_custom_values('syndication_source_uri_original', $id);
400 else : $vals = array();
401 endif;
402
403 if (count($vals) == 0) : $vals = get_post_custom_values('syndication_source_uri', $id);
404 endif;
405
406 if (count($vals) > 0) : $ret = $vals[0]; else : $ret = NULL; endif;
407
408 return $ret;
409 } /* function get_syndication_source_link() */
410
411 function the_syndication_source_link ($original = NULL, $id = NULL) {
412 echo get_syndication_source_link($original, $id);
413 }
414
415 function feedwordpress_display_url ($url, $before = 60, $after = 0) {
416 $bits = parse_url($url);
417
418 // Strip out crufty subdomains
419 $bits['host'] = preg_replace('/^www[0-9]*\./i', '', $bits['host']);
420
421 // Reassemble bit-by-bit with minimum of crufty elements
422 $url = (isset($bits['user'])?$bits['user'].'@':'')
423 .(isset($bits['host'])?$bits['host']:'')
424 .(isset($bits['path'])?$bits['path']:'')
425 .(isset($bits['query'])?'?'.$bits['query']:'');
426
427 if (strlen($url) > ($before+$after)) :
428 $url = substr($url, 0, $before).'…'.substr($url, 0 - $after, $after);
429 endif;
430
431 return $url;
432 }
433
434 function get_syndication_source ($original = NULL, $id = NULL) {
435 if (is_null($original)) :
436 $original = FeedWordPress::use_aggregator_source_data();
437 endif;
438
439 if ($original) :
440 $vals = get_post_custom_values('syndication_source_original', $id);
441 else :
442 $vals = array();
443 endif;
444
445 if (count($vals) == 0) :
446 $vals = get_post_custom_values('syndication_source', $id);
447 endif;
448
449 if (count($vals) > 0) :
450 $ret = $vals[0];
451 else :
452 $ret = NULL;
453 endif;
454
455 if (is_null($ret) or strlen(trim($ret)) == 0) :
456 // Fall back to URL of blog
457 $ret = feedwordpress_display_url(get_syndication_source_link());
458 endif;
459
460 return $ret;
461 } /* function get_syndication_source() */
462
463 function the_syndication_source ($original = NULL, $id = NULL) { echo get_syndication_source($original, $id); }
464
465 function get_syndication_feed ($original = NULL, $id = NULL) {
466 if (is_null($original)) : $original = FeedWordPress::use_aggregator_source_data();
467 endif;
468
469 if ($original) : $vals = get_post_custom_values('syndication_feed_original', $id);
470 else : $vals = array();
471 endif;
472
473 if (count($vals) == 0) : $vals = get_post_custom_values('syndication_feed', $id);
474 endif;
475
476 if (count($vals) > 0) : $ret = $vals[0]; else : $ret = NULL; endif;
477
478 return $ret;
479 } /* function get_syndication_feed() */
480
481 function the_syndication_feed ($original = NULL, $id = NULL) { echo get_syndication_feed($original, $id); }
482
483 function get_syndication_feed_guid ($original = NULL, $id = NULL) {
484 if (is_null($original)) : $original = FeedWordPress::use_aggregator_source_data();
485 endif;
486
487 if ($original) : $vals = get_post_custom_values('syndication_source_id_original', $id);
488 else : $vals = array();
489 endif;
490
491 if (count($vals) == 0) : $vals = array(get_feed_meta('feed/id', $id));
492 endif;
493
494 if (count($vals) > 0) : $ret = $vals[0]; else : $ret = NULL; endif;
495
496 return $ret;
497 } /* function get_syndication_feed_guid () */
498
499 function the_syndication_feed_guid ($original = NULL, $id = NULL) { echo get_syndication_feed_guid($original, $id); }
500
501 function get_syndication_feed_id ($id = NULL) { list($u) = get_post_custom_values('syndication_feed_id', $id); return $u; }
502 function the_syndication_feed_id ($id = NULL) { echo get_syndication_feed_id($id); }
503
504 $feedwordpress_linkcache = array (); // only load links from database once
505
506 function get_feed_meta ($key, $id = NULL) {
507 global $wpdb, $feedwordpress_linkcache;
508 $feed_id = get_syndication_feed_id($id);
509
510 $ret = NULL;
511 if (strlen($feed_id) > 0):
512 if (isset($feedwordpress_linkcache[$feed_id])) :
513 $link = $feedwordpress_linkcache[$feed_id];
514 else :
515 $link =& new SyndicatedLink($feed_id);
516 $feedwordpress_linkcache[$feed_id] = $link;
517 endif;
518
519 $ret = $link->settings[$key];
520 endif;
521 return $ret;
522 } /* get_feed_meta() */
523
524 function get_syndication_permalink ($id = NULL) {
525 list($u) = get_post_custom_values('syndication_permalink', $id); return $u;
526 }
527 function the_syndication_permalink ($id = NULL) {
528 echo get_syndication_permalink($id);
529 }
530
531 ################################################################################
532 ## FILTERS: syndication-aware handling of post data for templates and feeds ####
533 ################################################################################
534
535 $feedwordpress_the_syndicated_content = NULL;
536
537 function feedwordpress_preserve_syndicated_content ($text) {
538 global $feedwordpress_the_syndicated_content;
539
540 $globalExpose = (get_option('feedwordpress_formatting_filters') == 'yes');
541 $localExpose = get_post_custom_values('_feedwordpress_formatting_filters');
542 $expose = ($globalExpose or ((count($localExpose) > 0) and $localExpose[0]));
543
544 if ( is_syndicated() and !$expose ) :
545 $feedwordpress_the_syndicated_content = $text;
546 else :
547 $feedwordpress_the_syndicated_content = NULL;
548 endif;
549 return $text;
550 }
551
552 function feedwordpress_restore_syndicated_content ($text) {
553 global $feedwordpress_the_syndicated_content;
554
555 if ( !is_null($feedwordpress_the_syndicated_content) ) :
556 $text = $feedwordpress_the_syndicated_content;
557 endif;
558
559 return $text;
560 }
561
562 function feedwordpress_item_feed_data () {
563 // In a post context....
564 if (is_syndicated()) :
565 ?>
566 <source>
567 <title><?php print htmlspecialchars(get_syndication_source()); ?></title>
568 <link rel="alternate" type="text/html" href="<?php print htmlspecialchars(get_syndication_source_link()); ?>" />
569 <link rel="self" href="<?php print htmlspecialchars(get_syndication_feed()); ?>" />
570 <?php
571 $id = get_syndication_feed_guid();
572 if (strlen($id) > 0) :
573 ?>
574 <id><?php print htmlspecialchars($id); ?></id>
575 <?php
576 endif;
577 $updated = get_feed_meta('feed/updated');
578 if (strlen($updated) > 0) : ?>
579 <updated><?php print $updated; ?></updated>
580 <?php
581 endif;
582 ?>
583 </source>
584 <?php
585 endif;
586 }
587
588 function syndication_permalink ($permalink = '') {
589 if (get_option('feedwordpress_munge_permalink') != 'no'):
590 $uri = get_syndication_permalink();
591 return ((strlen($uri) > 0) ? $uri : $permalink);
592 else:
593 return $permalink;
594 endif;
595 } // function syndication_permalink ()
596
597 ################################################################################
598 ## ADMIN MENU ADD-ONS: register Dashboard management pages #####################
599 ################################################################################
600
601 function fwp_add_pages () {
602 global $fwp_capability;
603 global $fwp_path;
604
605 $menu = array('Syndicated Sites', 'Syndication', $fwp_capability['manage_links'], $fwp_path.'/syndication.php', NULL);
606 if (fwp_test_wp_version(FWP_SCHEMA_27)) :
607 // add icon parameter
608 $menu[] = WP_PLUGIN_URL.'/'.$fwp_path.'/feedwordpress-tiny.png';
609 endif;
610
611 call_user_func_array('add_menu_page', $menu);
612 add_submenu_page($fwp_path.'/syndication.php', 'Syndicated Feeds & Updates', 'Feeds & Updates', $fwp_capability['manage_options'], $fwp_path.'/feeds-page.php');
613 add_submenu_page($fwp_path.'/syndication.php', 'Syndicated Posts & Links', 'Posts & Links', $fwp_capability['manage_options'], $fwp_path.'/posts-page.php');
614 add_submenu_page($fwp_path.'/syndication.php', 'Syndicated Authors', 'Authors', $fwp_capability['manage_options'], $fwp_path.'/authors-page.php');
615 add_submenu_page($fwp_path.'/syndication.php', 'Categories'.FEEDWORDPRESS_AND_TAGS, 'Categories'.FEEDWORDPRESS_AND_TAGS, $fwp_capability['manage_options'], $fwp_path.'/categories-page.php');
616 add_submenu_page($fwp_path.'/syndication.php', 'FeedWordPress Back End', 'Back End', $fwp_capability['manage_options'], $fwp_path.'/backend-page.php');
617 } /* function fwp_add_pages () */
618
619 function fwp_check_debug () {
620 // This is a horrible fucking kludge that I have to do because the
621 // admin notice code is triggered before the code that updates the
622 // setting.
623 if (isset($_POST['feedwordpress_debug'])) :
624 $feedwordpress_debug = $_POST['feedwordpress_debug'];
625 else :
626 $feedwordpress_debug = get_option('feedwordpress_debug');
627 endif;
628 if ($feedwordpress_debug==='yes') :
629 ?>
630 <div class="error">
631 <p><strong>FeedWordPress warning.</strong> Debugging mode is <strong>ON</strong>.
632 While it remains on, FeedWordPress displays many diagnostic error messages,
633 warnings, and notices that are ordinarily suppressed, and also turns off all
634 caching of feeds. Use with caution: this setting is absolutely inappropriate
635 for a production server.</p>
636 </div>
637 <?php
638 endif;
639 } /* function fwp_check_debug () */
640
641 define('EXPECTED_MAGPIE_VERSION', '2010.0122');
642 function fwp_check_magpie () {
643 if (isset($_REQUEST['feedwordpress_magpie_fix'])) :
644 if ($_REQUEST['feedwordpress_magpie_fix']=='ignored') :
645 ?>
646 <div class="updated fade">
647 <p>O.K., we'll ignore the problem for now. FeedWordPress will not display any
648 more error messages.</p>
649 </div>
650 <?php
651 elseif ((bool) $_REQUEST['feedwordpress_magpie_fix']) :
652 ?>
653 <div class="updated fade">
654 <p>Congratulations! Your MagpieRSS has been successfully upgraded to the version
655 shipped with FeedWordPress.</p>
656 </div>
657 <?php
658 else :
659 $source = dirname(__FILE__)."/MagpieRSS-upgrade/rss.php";
660 $destination = ABSPATH . WPINC . '/rss.php';
661 $cmd = "cp '".htmlspecialchars(addslashes($source))."' '".htmlspecialchars(addslashes($destination))."'";
662 $cmd = wordwrap($cmd, /*width=*/ 75, /*break=*/ " \\\n\t");
663
664 ?>
665 <div class="error">
666 <p><strong>FeedWordPress was unable to automatically upgrade your copy of MagpieRSS.</strong></p>
667 <p>It's likely that you need to change the file permissions on <code><?php print htmlspecialchars($source); ?></code>
668 to allow FeedWordPress to overwrite it.</p>
669 <p><strong>To perform the upgrade manually,</strong> you can
670 use a SFTP or FTP client to upload a copy of <code>rss.php</code> from the
671 <code>MagpieRSS-upgrades/</code> directory of your FeedWordPress archive so that
672 it overwrites <code><?php print htmlspecialchars($destination); ?></code>. Or,
673 if your web host provides shell access, you can issue the following command from
674 a command prompt to perform the upgrade:</p>
675 <pre>
676 <samp>$</samp> <kbd><?php print $cmd; ?></kbd>
677 </pre>
678 <p><strong>If you've fixed the file permissions,</strong> you can try the
679 automatic upgrade again.</p>
680
681 <?php feedwordpress_upgrade_old_and_busted_buttons(); ?>
682 </div>
683 <?php
684 endif;
685 else :
686 $magpie_version = FeedWordPress::magpie_version();
687
688 $ignored = get_option('feedwordpress_magpie_ignored_upgrade_to');
689 if (EXPECTED_MAGPIE_VERSION != $magpie_version and EXPECTED_MAGPIE_VERSION != $ignored) :
690 if (current_user_can('edit_files')) :
691 $youAre = 'you are';
692 $itIsRecommendedThatYou = 'It is <strong>strongly recommended</strong> that you';
693 else :
694 $youAre = 'this site is';
695 $itIsRecommendedThatYou = 'You may want to contact the administrator of the site; it is <strong>strongly recommended</strong> that they';
696 endif;
697 print '<div class="error">';
698 ?>
699 <p style="font-style: italic"><strong>FeedWordPress has detected that <?php print $youAre; ?> currently using a version of
700 MagpieRSS other than the upgraded version that ships with this version of FeedWordPress.</strong></p>
701 <ul>
702 <li><strong>Currently running:</strong> MagpieRSS <?php print $magpie_version; ?></li>
703 <li><strong>Version included with FeedWordPress <?php print FEEDWORDPRESS_VERSION; ?>:</strong> MagpieRSS <?php print EXPECTED_MAGPIE_VERSION; ?></li>
704 </ul>
705 <p><?php print $itIsRecommendedThatYou; ?> install the upgraded
706 version of MagpieRSS supplied with FeedWordPress. The version of
707 MagpieRSS that ships with WordPress is very old and buggy, and
708 encounters a number of errors when trying to parse modern Atom
709 and RSS feeds.</p>
710 <?php
711 feedwordpress_upgrade_old_and_busted_buttons();
712 print '</div>';
713 endif;
714 endif;
715 }
716
717 function feedwordpress_upgrade_old_and_busted_buttons() {
718 if (current_user_can('edit_files')) :
719 ?>
720 <form action="" method="post"><div>
721 <?php FeedWordPressCompatibility::stamp_nonce('feedwordpress_fix_magpie'); ?>
722 <input type="hidden" name="action" value="fix_magpie_version" />
723 <input class="button-secondary" type="submit" name="ignore" value="<?php _e('Ignore this problem'); ?>" />
724 <input class="button-primary" type="submit" name="upgrade" value="<?php _e('Upgrade'); ?>" />
725 </div></form>
726 <?php
727 endif;
728 }
729
730 ################################################################################
731 ## fwp_hold_pings() and fwp_release_pings(): Outbound XML-RPC ping reform ####
732 ## ... 'coz it's rude to send 500 pings the first time your aggregator runs ####
733 ################################################################################
734
735 $fwp_held_ping = NULL; // NULL: not holding pings yet
736
737 function fwp_hold_pings () {
738 global $fwp_held_ping;
739 if (is_null($fwp_held_ping)):
740 $fwp_held_ping = 0; // 0: ready to hold pings; none yet received
741 endif;
742 }
743
744 function fwp_release_pings () {
745 global $fwp_held_ping;
746 if ($fwp_held_ping):
747 if (function_exists('wp_schedule_single_event')) :
748 wp_schedule_single_event(time(), 'do_pings');
749 else :
750 generic_ping($fwp_held_ping);
751 endif;
752 endif;
753 $fwp_held_ping = NULL; // NULL: not holding pings anymore
754 }
755
756 function fwp_do_pings () {
757 if (!is_null($fwp_held_ping) and $post_id) : // Defer until we're done updating
758 $fwp_held_ping = $post_id;
759 elseif (function_exists('do_all_pings')) :
760 do_all_pings();
761 else :
762 generic_ping($fwp_held_ping);
763 endif;
764 }
765
766 function fwp_publish_post_hook ($post_id) {
767 global $fwp_held_ping;
768
769 if (!is_null($fwp_held_ping)) : // Syndicated post. Don't mark with _pingme
770 if ( defined('XMLRPC_REQUEST') )
771 do_action('xmlrpc_publish_post', $post_id);
772 if ( defined('APP_REQUEST') )
773 do_action('app_publish_post', $post_id);
774
775 if ( defined('WP_IMPORTING') )
776 return;
777
778 // Defer sending out pings until we finish updating
779 $fwp_held_ping = $post_id;
780 else :
781 if (function_exists('_publish_post_hook')) : // WordPress 2.3
782 _publish_post_hook($post_id);
783 endif;
784 endif;
785 }
786
787 function feedwordpress_add_post_edit_controls () {
788 add_meta_box('feedwordpress-post-controls', __('Syndication'), 'feedwordpress_post_edit_controls', 'post', 'side', 'high');
789 } // function FeedWordPress::postEditControls
790
791 function feedwordpress_post_edit_controls () {
792 global $post;
793
794 $frozen_values = get_post_custom_values('_syndication_freeze_updates', $post->ID);
795 $frozen_post = (count($frozen_values) > 0 and 'yes' == $frozen_values[0]);
796
797 if (is_syndicated($post->ID)) :
798 ?>
799 <p>This is a syndicated post, which originally appeared at
800 <cite><?php print wp_specialchars(get_syndication_source(NULL, $post->ID)); ?></cite>.
801 <a href="<?php print wp_specialchars(get_syndication_permalink($post->ID)); ?>">View original post</a>.</p>
802
803 <p><input type="hidden" name="feedwordpress_noncename" id="feedwordpress_noncename" value="<?php print wp_create_nonce(plugin_basename(__FILE__)); ?>" />
804 <label><input type="checkbox" name="freeze_updates" value="yes" <?php if ($frozen_post) : ?>checked="checked"<?php endif; ?> /> <strong>Manual editing.</strong>
805 If set, FeedWordPress will not overwrite the changes you make manually
806 to this post, if the syndicated content is updated on the
807 feed.</label></p>
808 <?php
809 else :
810 ?>
811 <p>This post was created locally at this website.</p>
812 <?php
813 endif;
814 } // function feedwordpress_post_edit_controls () */
815
816 function feedwordpress_save_post_edit_controls ( $post_id ) {
817 global $post;
818
819 if (!isset($_POST['feedwordpress_noncename']) or !wp_verify_nonce($_POST['feedwordpress_noncename'], plugin_basename(__FILE__))) :
820 return $post_id;
821 endif;
822
823 // Verify if this is an auto save routine. If it is our form has
824 // not been submitted, so we don't want to do anything.
825 if ( defined('DOING_AUTOSAVE') and DOING_AUTOSAVE ) :
826 return $post_id;
827 endif;
828
829 // Check permissions
830 if ( !current_user_can( 'edit_'.$_POST['post_type'], $post_id) ) :
831 return $post_id;
832 endif;
833
834 // OK, we're golden. Now let's save some data.
835 if (isset($_POST['freeze_updates'])) :
836 update_post_meta($post_id, '_syndication_freeze_updates', $_POST['freeze_updates']);
837 else :
838 delete_post_meta($post_id, '_syndication_freeze_updates');
839 endif;
840
841 return $_POST['freeze_updates'];
842 } // function feedwordpress_save_edit_controls
843
844 ################################################################################
845 ## class FeedWordPress #########################################################
846 ################################################################################
847
848 // class FeedWordPress: handles feed updates and plugs in to the XML-RPC interface
849 class FeedWordPress {
850 var $strip_attrs = array (
851 array('[a-z]+', 'style'),
852 array('[a-z]+', 'target'),
853 );
854 var $uri_attrs = array (
855 array('a', 'href'),
856 array('applet', 'codebase'),
857 array('area', 'href'),
858 array('blockquote', 'cite'),
859 array('body', 'background'),
860 array('del', 'cite'),
861 array('form', 'action'),
862 array('frame', 'longdesc'),
863 array('frame', 'src'),
864 array('iframe', 'longdesc'),
865 array('iframe', 'src'),
866 array('head', 'profile'),
867 array('img', 'longdesc'),
868 array('img', 'src'),
869 array('img', 'usemap'),
870 array('input', 'src'),
871 array('input', 'usemap'),
872 array('ins', 'cite'),
873 array('link', 'href'),
874 array('object', 'classid'),
875 array('object', 'codebase'),
876 array('object', 'data'),
877 array('object', 'usemap'),
878 array('q', 'cite'),
879 array('script', 'src')
880 );
881
882 var $feeds = NULL;
883
884 # function FeedWordPress (): Contructor; retrieve a list of feeds
885 function FeedWordPress () {
886 $this->feeds = array ();
887 $links = FeedWordPress::syndicated_links();
888 if ($links): foreach ($links as $link):
889 $this->feeds[] =& new SyndicatedLink($link);
890 endforeach; endif;
891 } // FeedWordPress::FeedWordPress ()
892
893 # function update (): polls for updates on one or more Contributor feeds
894 #
895 # Arguments:
896 # ----------
897 # * $uri (string): either the URI of the feed to poll, the URI of the
898 # (human-readable) website whose feed you want to poll, or NULL.
899 #
900 # If $uri is NULL, then FeedWordPress will poll any feeds that are
901 # ready for polling. It will not poll feeds that are marked as
902 # "Invisible" Links (signifying that the subscription has been
903 # de-activated), or feeds that are not yet stale according to their
904 # TTL setting (which is either set in the feed, or else set
905 # randomly within a window of 30 minutes - 2 hours).
906 #
907 # Returns:
908 # --------
909 # * Normally returns an associative array, with 'new' => the number
910 # of new posts added during the update, and 'updated' => the number
911 # of old posts that were updated during the update. If both numbers
912 # are zero, there was no change since the last poll on that URI.
913 #
914 # * Returns NULL if URI it was passed was not a URI that this
915 # installation of FeedWordPress syndicates.
916 #
917 # Effects:
918 # --------
919 # * One or more feeds are polled for updates
920 #
921 # * If the feed Link does not have a hardcoded name set, its Link
922 # Name is synchronized with the feed's title element
923 #
924 # * If the feed Link does not have a hardcoded URI set, its Link URI
925 # is synchronized with the feed's human-readable link element
926 #
927 # * If the feed Link does not have a hardcoded description set, its
928 # Link Description is synchronized with the feed's description,
929 # tagline, or subtitle element.
930 #
931 # * The time of polling is recorded in the feed's settings, and the
932 # TTL (time until the feed is next available for polling) is set
933 # either from the feed (if it is supplied in the ttl or syndication
934 # module elements) or else from a randomly-generated time window
935 # (between 30 minutes and 2 hours).
936 #
937 # * New posts from the polled feed are added to the WordPress store.
938 #
939 # * Updates to existing posts since the last poll are mirrored in the
940 # WordPress store.
941 #
942 function update ($uri = null, $crash_ts = null) {
943 global $wpdb;
944
945 if (FeedWordPress::needs_upgrade()) : // Will make duplicate posts if we don't hold off
946 return NULL;
947 endif;
948
949 if (!is_null($uri)) :
950 $uri = trim($uri);
951 else : // Update all
952 update_option('feedwordpress_last_update_all', time());
953 endif;
954
955 do_action('feedwordpress_update', $uri);
956
957 if (is_null($crash_ts)) :
958 $crash_dt = (int) get_option('feedwordpress_update_time_limit');
959 if ($crash_dt > 0) :
960 $crash_ts = time() + $crash_dt;
961 else :
962 $crash_ts = NULL;
963 endif;
964 endif;
965
966 // Randomize order for load balancing purposes
967 $feed_set = $this->feeds;
968 shuffle($feed_set);
969
970 // Loop through and check for new posts
971 $delta = NULL;
972 foreach ($feed_set as $feed) :
973 if (!is_null($crash_ts) and (time() > $crash_ts)) : // Check whether we've exceeded the time limit
974 break;
975 endif;
976
977 $pinged_that = (is_null($uri) or ($uri=='*') or in_array($uri, array($feed->uri(), $feed->homepage())));
978
979 if (!is_null($uri)) : // A site-specific ping always updates
980 $timely = true;
981 else :
982 $timely = $feed->stale();
983 endif;
984
985 if ($pinged_that and is_null($delta)) : // If at least one feed was hit for updating...
986 $delta = array('new' => 0, 'updated' => 0); // ... don't return error condition
987 endif;
988
989 if ($pinged_that and $timely) :
990 do_action('feedwordpress_check_feed', $feed->settings);
991 $start_ts = time();
992 $added = $feed->poll($crash_ts);
993 do_action('feedwordpress_check_feed_complete', $feed->settings, $added, time() - $start_ts);
994
995 if (isset($added['new'])) : $delta['new'] += $added['new']; endif;
996 if (isset($added['updated'])) : $delta['updated'] += $added['updated']; endif;
997 endif;
998 endforeach;
999
1000 do_action('feedwordpress_update_complete', $delta);
1001
1002 return $delta;
1003 }
1004
1005 function stale () {
1006 if (get_option('feedwordpress_automatic_updates')) :
1007 // Do our best to avoid possible simultaneous
1008 // updates by getting up-to-the-minute settings.
1009
1010 $last = get_option('feedwordpress_last_update_all');
1011
1012 // If we haven't updated all yet, give it a time window
1013 if (false === $last) :
1014 $ret = false;
1015 update_option('feedwordpress_last_update_all', time());
1016
1017 // Otherwise, check against freshness interval
1018 elseif (is_numeric($last)) : // Expect a timestamp
1019 $freshness = get_option('feedwordpress_freshness');
1020 if (false === $freshness) : // Use default
1021 $freshness = FEEDWORDPRESS_FRESHNESS_INTERVAL;
1022 endif;
1023 $ret = ( (time() - $last) > $freshness);
1024
1025 // This should never happen.
1026 else :
1027 FeedWordPress::critical_bug('FeedWordPress::stale::last', $last, __LINE__);
1028 endif;
1029
1030 else :
1031 $ret = false;
1032 endif;
1033 return $ret;
1034 } // FeedWordPress::stale()
1035
1036 function update_requested () {
1037 return (
1038 isset($_REQUEST['update_feedwordpress'])
1039 and $_REQUEST['update_feedwordpress']
1040 );
1041 } // FeedWordPress::update_requested()
1042
1043 function update_requested_url () {
1044 $ret = null;
1045
1046 if (($_REQUEST['update_feedwordpress']=='*')
1047 or (preg_match('|^http://.*|i', $_REQUEST['update_feedwordpress']))) :
1048 $ret = $_REQUEST['update_feedwordpress'];
1049 endif;
1050
1051 return $ret;
1052 } // FeedWordPress::update_requested_url()
1053
1054 function syndicate_link ($name, $uri, $rss) {
1055 global $wpdb;
1056
1057 // Get the category ID#
1058 $cat_id = FeedWordPress::link_category_id();
1059
1060 // WordPress gets cranky if there's no homepage URI
1061 if (!isset($uri) or strlen($uri)<1) : $uri = $rss; endif;
1062
1063 if (function_exists('wp_insert_link')) : // WordPress 2.x
1064 if (FeedWordPressCompatibility::test_version(0, FWP_SCHEMA_21)) :
1065 // Morons.
1066 $name = $wpdb->escape($name);
1067 $uri = $wpdb->escape($uri);
1068 $rss = $wpdb->escape($rss);
1069
1070 // Comes in as a single category
1071 $linkCats = $cat_id;
1072 else :
1073 // Comes in as an array of categories
1074 $linkCats = array($cat_id);
1075 endif;
1076
1077 $link_id = wp_insert_link(array(
1078 "link_name" => $name,
1079 "link_url" => $uri,
1080 "link_category" => $linkCats,
1081 "link_rss" => $rss
1082 ));
1083 else : // WordPress 1.5.x
1084 $result = $wpdb->query("
1085 INSERT INTO $wpdb->links
1086 SET
1087 link_name = '".$wpdb->escape($name)."',
1088 link_url = '".$wpdb->escape($uri)."',
1089 link_category = '".$wpdb->escape($cat_id)."',
1090 link_rss = '".$wpdb->escape($rss)."'
1091 ");
1092 $link_id = $wpdb->insert_id;
1093 endif;
1094 return $link_id;
1095 } // function FeedWordPress::syndicate_link()
1096
1097 /*static*/ function syndicated_status ($what, $default) {
1098 $ret = get_option("feedwordpress_syndicated_{$what}_status");
1099 if (!$ret) :
1100 $ret = $default;
1101 endif;
1102 return $ret;
1103 } /* FeedWordPress::syndicated_status() */
1104
1105 function on_unfamiliar ($what = 'author', $override = NULL) {
1106 $set = array(
1107 'author' => array('create', 'default', 'filter'),
1108 'category' => array('create', 'tag', 'default', 'filter'),
1109 );
1110
1111 if (is_string($override)) :
1112 $ret = strtolower($override);
1113 else :
1114 $ret = NULL;
1115 endif;
1116
1117 if (!is_numeric($override) and !in_array($ret, $set[$what])) :
1118 $ret = get_option('feedwordpress_unfamiliar_'.$what);
1119 if (!is_numeric($ret) and !in_array($ret, $set[$what])) :
1120 $ret = 'create';
1121 endif;
1122 endif;
1123
1124 return $ret;
1125 } // function FeedWordPress::on_unfamiliar()
1126
1127 function null_email_set () {
1128 $base = get_option('feedwordpress_null_email_set');
1129
1130 if ($base===false) :
1131 $ret = array('noreply@blogger.com'); // default
1132 else :
1133 $ret = array_map('strtolower',
1134 array_map('trim', explode("\n", $base)));
1135 endif;
1136 $ret = apply_filters('syndicated_item_author_null_email_set', $ret);
1137 return $ret;
1138
1139 } /* FeedWordPress::null_email_set () */
1140
1141 function is_null_email ($email) {
1142 $ret = in_array(strtolower(trim($email)), FeedWordPress::null_email_set());
1143 $ret = apply_filters('syndicated_item_author_is_null_email', $ret, $email);
1144 return $ret;
1145 } /* FeedWordPress::is_null_email () */
1146
1147 function use_aggregator_source_data () {
1148 $ret = get_option('feedwordpress_use_aggregator_source_data');
1149 return apply_filters('syndicated_post_use_aggregator_source_data', ($ret=='yes'));
1150 }
1151
1152 function syndicated_links () {
1153 $contributors = FeedWordPress::link_category_id();
1154 if (function_exists('get_bookmarks')) :
1155 $links = get_bookmarks(array("category" => $contributors));
1156 else:
1157 $links = get_linkobjects($contributors); // deprecated as of WP 2.1
1158 endif;
1159 return $links;
1160 } // function FeedWordPress::syndicated_links()
1161
1162 function link_category_id () {
1163 global $wpdb, $wp_db_version;
1164
1165 $cat_id = get_option('feedwordpress_cat_id');
1166
1167 // If we don't yet have the category ID stored, search by name
1168 if (!$cat_id) :
1169 $cat_id = FeedWordPressCompatibility::link_category_id(DEFAULT_SYNDICATION_CATEGORY);
1170
1171 if ($cat_id) :
1172 // We found it; let's stamp it.
1173 update_option('feedwordpress_cat_id', $cat_id);
1174 endif;
1175
1176 // If we *do* have the category ID stored, verify that it exists
1177 else :
1178 $cat_id = FeedWordPressCompatibility::link_category_id((int) $cat_id, 'cat_id');
1179 endif;
1180
1181 // If we could not find an appropriate link category,
1182 // make a new one for ourselves.
1183 if (!$cat_id) :
1184 $cat_id = FeedWordPressCompatibility::insert_link_category(DEFAULT_SYNDICATION_CATEGORY);
1185
1186 // Stamp it
1187 update_option('feedwordpress_cat_id', $cat_id);
1188 endif;
1189
1190 return $cat_id;
1191 } // function FeedWordPress::link_category_id()
1192
1193 # Upgrades and maintenance...
1194 function needs_upgrade () {
1195 global $wpdb;
1196 $fwp_db_version = get_option('feedwordpress_version');
1197 $ret = false; // innocent until proven guilty
1198 if (!$fwp_db_version or $fwp_db_version < FEEDWORDPRESS_VERSION) :
1199 // This is an older version or a fresh install. Does it
1200 // require a database upgrade or database initialization?
1201 if ($fwp_db_version <= 0.96) :
1202 // Yes. Check to see whether this is a fresh install or an upgrade.
1203 $syn = $wpdb->get_col("
1204 SELECT post_id
1205 FROM $wpdb->postmeta
1206 WHERE meta_key = 'syndication_feed'
1207 ");
1208 if (count($syn) > 0) : // contains at least one syndicated post
1209 $ret = true;
1210 else : // fresh install; brand it as ours
1211 update_option('feedwordpress_version', FEEDWORDPRESS_VERSION);
1212 endif;
1213 elseif ($fwp_db_version < 2009.0707) :
1214 // We need to clear out any busted AJAX crap
1215 if (fwp_test_wp_version(FWP_SCHEMA_HAS_USERMETA)) :
1216 $wpdb->query("
1217 DELETE FROM $wpdb->usermeta
1218 WHERE LOCATE('feedwordpress', meta_key)
1219 AND LOCATE('box', meta_key);
1220 ");
1221 endif;
1222 update_option('feedwordpress_version', FEEDWORDPRESS_VERSION);
1223 else :
1224 // No. Just brand it with the new version.
1225 update_option('feedwordpress_version', FEEDWORDPRESS_VERSION);
1226 endif;
1227 endif;
1228 return $ret;
1229 }
1230
1231 function upgrade_database ($from = NULL) {
1232 global $wpdb;
1233
1234 if (is_null($from) or $from <= 0.96) : $from = 0.96; endif;
1235
1236 switch ($from) :
1237 case 0.96: // account for changes to syndication custom values and guid
1238 echo "<p>Upgrading database from {$from} to ".FEEDWORDPRESS_VERSION."...</p>\n";
1239
1240 $cat_id = FeedWordPress::link_category_id();
1241
1242 // Avoid duplicates
1243 $wpdb->query("DELETE FROM `{$wpdb->postmeta}` WHERE meta_key = 'syndication_feed_id'");
1244
1245 // Look up all the link IDs
1246 $wpdb->query("
1247 CREATE TEMPORARY TABLE tmp_custom_values
1248 SELECT
1249 NULL AS meta_id,
1250 post_id,
1251 'syndication_feed_id' AS meta_key,
1252 link_id AS meta_value
1253 FROM `{$wpdb->postmeta}`, `{$wpdb->links}`
1254 WHERE
1255 meta_key='syndication_feed'
1256 AND meta_value=link_rss
1257 AND link_category = {$cat_id}
1258 ");
1259
1260 // Now attach them to their posts
1261 $wpdb->query("INSERT INTO `{$wpdb->postmeta}` SELECT * FROM tmp_custom_values");
1262
1263 // And clean up after ourselves.
1264 $wpdb->query("DROP TABLE tmp_custom_values");
1265
1266 // Now fix the guids to avoid duplicate posts
1267 echo "<ul>";
1268 foreach ($this->feeds as $syndicatedLink) :
1269 $feed = $syndicatedLink->settings;
1270 echo "<li>Fixing post meta-data for <cite>".$feed['link/name']."</cite> &#8230; "; flush();
1271 $rss = @fetch_rss($feed['link/uri']);
1272 if (is_array($rss->items)) :
1273 foreach ($rss->items as $item) :
1274 $post = new SyndicatedPost($item, $syndicatedLink);
1275 $guid = $wpdb->escape($post->guid()); // new GUID algorithm
1276 $link = $wpdb->escape($item['link']);
1277
1278 $wpdb->query("
1279 UPDATE `{$wpdb->posts}` SET guid='{$guid}' WHERE guid='{$link}'
1280 ");
1281 endforeach;
1282 endif;
1283 echo "<strong>complete.</strong></li>\n";
1284 endforeach;
1285 echo "</ul>\n";
1286
1287 // Mark the upgrade as successful.
1288 update_option('feedwordpress_version', FEEDWORDPRESS_VERSION);
1289 endswitch;
1290 echo "<p>Upgrade complete. FeedWordPress is now ready to use again.</p>";
1291 } /* FeedWordPress::upgrade_database() */
1292
1293 function create_guid_index () {
1294 global $wpdb;
1295
1296 $wpdb->query("
1297 CREATE INDEX {$wpdb->posts}_guid_idx ON {$wpdb->posts}(guid)
1298 ");
1299 } /* FeedWordPress::create_guid_index () */
1300
1301 function clear_cache () {
1302 global $wpdb;
1303
1304 // MagpieRSS stores its cached feeds in options table rows with
1305 // name = `rss_{md5 of url}` and timestamps for cached feeds in
1306 // table rows with name = `rss_{md5 of url}_ts`. The md5 is
1307 // always 32 characters in length, so the total option_name is
1308 // always over 32 characters.
1309 $wpdb->query("
1310 DELETE FROM {$wpdb->options}
1311 WHERE LOCATE('rss_', option_name) AND LENGTH(option_name) > 32
1312 ");
1313 } /* FeedWordPress::clear_cache () */
1314
1315 function magpie_version () {
1316 if (!defined('MAGPIE_VERSION')) : $magpie_version = $GLOBALS['wp_version'].'-default';
1317 else : $magpie_version = MAGPIE_VERSION;
1318 endif;
1319 return $magpie_version;
1320 }
1321
1322 # Utility functions for handling text settings
1323 function negative ($f, $setting) {
1324 $nego = array ('n', 'no', 'f', 'false');
1325 return (isset($f[$setting]) and in_array(strtolower($f[$setting]), $nego));
1326 }
1327
1328 function affirmative ($f, $setting) {
1329 $affirmo = array ('y', 'yes', 't', 'true', 1);
1330 return (isset($f[$setting]) and in_array(strtolower($f[$setting]), $affirmo));
1331 }
1332
1333
1334 # Internal debugging functions
1335 function critical_bug ($varname, $var, $line) {
1336 global $wp_version;
1337
1338 if (defined('MAGPIE_VERSION')) : $mv = MAGPIE_VERSION;
1339 else : $mv = 'WordPress '.$wp_version.' default.';
1340 endif;
1341
1342 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>';
1343 echo "\n<plaintext>";
1344 echo "Triggered at line # ".$line."\n";
1345 echo "FeedWordPress version: ".FEEDWORDPRESS_VERSION."\n";
1346 echo "MagpieRSS version: {$mv}\n";
1347 echo "WordPress version: {$wp_version}\n";
1348 echo "PHP version: ".phpversion()."\n";
1349 echo "\n";
1350 echo $varname.": "; var_dump($var); echo "\n";
1351 die;
1352 }
1353
1354 function noncritical_bug ($varname, $var, $line) {
1355 if (FEEDWORDPRESS_DEBUG) : // halt only when we are doing debugging
1356 FeedWordPress::critical_bug($varname, $var, $line);
1357 endif;
1358 }
1359 } // class FeedWordPress
1360
1361 require_once(dirname(__FILE__) . '/syndicatedpost.class.php');
1362 require_once(dirname(__FILE__) . '/syndicatedlink.class.php');
1363
1364 ################################################################################
1365 ## XML-RPC HOOKS: accept XML-RPC update pings from Contributors ################
1366 ################################################################################
1367
1368 function feedwordpress_xmlrpc_hook ($args = array ()) {
1369 $args['weblogUpdates.ping'] = 'feedwordpress_pong';
1370 return $args;
1371 }
1372
1373 function feedwordpress_pong ($args) {
1374 $feedwordpress =& new FeedWordPress;
1375 $delta = @$feedwordpress->update($args[1]);
1376 if (is_null($delta)):
1377 return array('flerror' => true, 'message' => "Sorry. I don't syndicate <$args[1]>.");
1378 else:
1379 $mesg = array();
1380 if (isset($delta['new'])) { $mesg[] = ' '.$delta['new'].' new posts were syndicated'; }
1381 if (isset($delta['updated'])) { $mesg[] = ' '.$delta['updated'].' existing posts were updated'; }
1382
1383 return array('flerror' => false, 'message' => "Thanks for the ping.".implode(' and', $mesg));
1384 endif;
1385 }
1386
1387 # The upgraded MagpieRSS also uses this class. So if we have it loaded
1388 # in, don't load it again.
1389 if (!class_exists('Relative_URI')) {
1390 require_once(dirname(__FILE__) . '/relative_uri.class.php');
1391 }
1392
1393 // take your best guess at the realname and e-mail, given a string
1394 define('FWP_REGEX_EMAIL_ADDY', '([^@"(<\s]+@[^"@(<\s]+\.[^"@(<\s]+)');
1395 define('FWP_REGEX_EMAIL_NAME', '("([^"]*)"|([^"<(]+\S))');
1396 define('FWP_REGEX_EMAIL_POSTFIX_NAME', '/^\s*'.FWP_REGEX_EMAIL_ADDY."\s+\(".FWP_REGEX_EMAIL_NAME.'\)\s*$/');
1397 define('FWP_REGEX_EMAIL_PREFIX_NAME', '/^\s*'.FWP_REGEX_EMAIL_NAME.'\s*<'.FWP_REGEX_EMAIL_ADDY.'>\s*$/');
1398 define('FWP_REGEX_EMAIL_JUST_ADDY', '/^\s*'.FWP_REGEX_EMAIL_ADDY.'\s*$/');
1399 define('FWP_REGEX_EMAIL_JUST_NAME', '/^\s*'.FWP_REGEX_EMAIL_NAME.'\s*$/');
1400
1401 function parse_email_with_realname ($email) {
1402 if (preg_match(FWP_REGEX_EMAIL_POSTFIX_NAME, $email, $matches)) :
1403 ($ret['name'] = $matches[3]) or ($ret['name'] = $matches[2]);
1404 $ret['email'] = $matches[1];
1405 elseif (preg_match(FWP_REGEX_EMAIL_PREFIX_NAME, $email, $matches)) :
1406 ($ret['name'] = $matches[2]) or ($ret['name'] = $matches[3]);
1407 $ret['email'] = $matches[4];
1408 elseif (preg_match(FWP_REGEX_EMAIL_JUST_ADDY, $email, $matches)) :
1409 $ret['name'] = NULL; $ret['email'] = $matches[1];
1410 elseif (preg_match(FWP_REGEX_EMAIL_JUST_NAME, $email, $matches)) :
1411 $ret['email'] = NULL;
1412 ($ret['name'] = $matches[2]) or ($ret['name'] = $matches[3]);
1413 else :
1414 $ret['name'] = NULL; $ret['email'] = NULL;
1415 endif;
1416 return $ret;
1417 }
1418
1419