PluginProbe
FeedWordPress / 2010.0531
FeedWordPress v2010.0531
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.0531, at feedwordpress.php

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