PluginProbe
FeedWordPress / 2011.0721
FeedWordPress v2011.0721
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 2011.0721, at feedwordpress.php

1,977 lines 66.0 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: 2011.0721
7 Author: Charles Johnson
8 Author URI: http://radgeek.com/
9 License: GPL
10 */
11
12 /**
13 * @package FeedWordPress
14 * @version 2011.0721
15 */
16
17 # This uses code derived from:
18 # - wp-rss-aggregate.php by Kellan Elliot-McCrea <kellan@protest.net>
19 # - MagpieRSS by Kellan Elliot-McCrea <kellan@protest.net>
20 # - Ultra-Liberal Feed Finder by Mark Pilgrim <mark@diveintomark.org>
21 # - WordPress Blog Tool and Publishing Platform <http://wordpress.org/>
22 # according to the terms of the GNU General Public License.
23 #
24 # INSTALLATION: see readme.txt or <http://projects.radgeek.com/install>
25 #
26 # USAGE: once FeedWordPress is installed, you manage just about everything from
27 # the WordPress Dashboard, under the Syndication menu. To ensure that fresh
28 # content is added as it becomes available, you can convince your contributors
29 # to put your XML-RPC URI (if WordPress is installed at
30 # <http://www.zyx.com/blog>, XML-RPC requests should be sent to
31 # <http://www.zyx.com/blog/xmlrpc.php>), or update manually under the
32 # Syndication menu, or set up automatic updates under Syndication --> Settings,
33 # or use a cron job.
34
35 # -- Don't change these unless you know what you're doing...
36
37 define ('FEEDWORDPRESS_VERSION', '2011.0721');
38 define ('FEEDWORDPRESS_AUTHOR_CONTACT', 'http://radgeek.com/contact');
39
40 if (!defined('FEEDWORDPRESS_BLEG')) :
41 define ('FEEDWORDPRESS_BLEG', true);
42 endif;
43
44 // Defaults
45 define ('DEFAULT_SYNDICATION_CATEGORY', 'Contributors');
46 define ('DEFAULT_UPDATE_PERIOD', 60); // value in minutes
47
48 if (isset($_REQUEST['feedwordpress_debug'])) :
49 $feedwordpress_debug = $_REQUEST['feedwordpress_debug'];
50 else :
51 $feedwordpress_debug = get_option('feedwordpress_debug');
52 if (is_string($feedwordpress_debug)) :
53 $feedwordpress_debug = ($feedwordpress_debug == 'yes');
54 endif;
55 endif;
56 define ('FEEDWORDPRESS_DEBUG', $feedwordpress_debug);
57
58 define ('FEEDWORDPRESS_CAT_SEPARATOR_PATTERN', '/[:\n]/');
59 define ('FEEDWORDPRESS_CAT_SEPARATOR', "\n");
60
61 define ('FEEDVALIDATOR_URI', 'http://feedvalidator.org/check.cgi');
62
63 define ('FEEDWORDPRESS_FRESHNESS_INTERVAL', 10*60); // Every ten minutes
64
65 define ('FWP_SCHEMA_HAS_USERMETA', 2966);
66 define ('FWP_SCHEMA_USES_ARGS_TAXONOMY', 12694); // Revision # for using $args['taxonomy'] to get link categories
67 define ('FWP_SCHEMA_20', 3308); // Database schema # for WP 2.0
68 define ('FWP_SCHEMA_21', 4772); // Database schema # for WP 2.1
69 define ('FWP_SCHEMA_23', 5495); // Database schema # for WP 2.3
70 define ('FWP_SCHEMA_25', 7558); // Database schema # for WP 2.5
71 define ('FWP_SCHEMA_26', 8201); // Database schema # for WP 2.6
72 define ('FWP_SCHEMA_27', 9872); // Database schema # for WP 2.7
73 define ('FWP_SCHEMA_28', 11548); // Database schema # for WP 2.8
74 define ('FWP_SCHEMA_29', 12329); // Database schema # for WP 2.9
75 define ('FWP_SCHEMA_30', 12694); // Database schema # for WP 3.0
76
77 if (FEEDWORDPRESS_DEBUG) :
78 // Help us to pick out errors, if any.
79 ini_set('error_reporting', E_ALL & ~E_NOTICE);
80 ini_set('display_errors', true);
81
82 // When testing we don't want cache issues to interfere. But this is
83 // a VERY BAD SETTING for a production server. Webmasters will eat your
84 // face for breakfast if you use it, and the baby Jesus will cry. So
85 // make sure FEEDWORDPRESS_DEBUG is FALSE for any site that will be
86 // used for more than testing purposes!
87 define('FEEDWORDPRESS_CACHE_AGE', 1);
88 define('FEEDWORDPRESS_CACHE_LIFETIME', 1);
89 define('FEEDWORDPRESS_FETCH_TIMEOUT_DEFAULT', 60);
90 else :
91 // Hold onto data all day for conditional GET purposes,
92 // but consider it stale after 1 min (requiring a conditional GET)
93 define('FEEDWORDPRESS_CACHE_LIFETIME', 24*60*60);
94 define('FEEDWORDPRESS_CACHE_AGE', 1*60);
95 define('FEEDWORDPRESS_FETCH_TIMEOUT_DEFAULT', 20);
96 endif;
97
98 // Use our the cache settings that we want.
99 add_filter('wp_feed_cache_transient_lifetime', array('FeedWordPress', 'cache_lifetime'));
100
101 // Ensure that we have SimplePie loaded up and ready to go.
102 // We no longer need a MagpieRSS upgrade module. Hallelujah!
103 if (!class_exists('SimplePie')) :
104 require_once(ABSPATH . WPINC . '/class-simplepie.php');
105 endif;
106 require_once(ABSPATH . WPINC . '/class-feed.php');
107
108 if (!function_exists('wp_insert_user')) :
109 require_once (ABSPATH . WPINC . '/registration.php'); // for wp_insert_user
110 endif;
111
112 $dir = dirname(__FILE__);
113 require_once("${dir}/admin-ui.php");
114 require_once("${dir}/feedwordpresssyndicationpage.class.php");
115 require_once("${dir}/compatability.php"); // Legacy API
116 require_once("${dir}/syndicatedpost.class.php");
117 require_once("${dir}/syndicatedlink.class.php");
118 require_once("${dir}/feedwordpresshtml.class.php");
119 require_once("${dir}/feedwordpress-content-type-sniffer.class.php");
120 require_once("${dir}/inspectpostmeta.class.php");
121 require_once("${dir}/syndicationdataqueries.class.php");
122 require_once("${dir}/feedwordpress_file.class.php");
123 require_once("${dir}/feedwordpress_parser.class.php");
124 require_once("${dir}/feedwordpressrpc.class.php");
125
126 // Magic quotes are just about the stupidest thing ever.
127 if (is_array($_POST)) :
128 $fwp_post = $_POST;
129 if (get_magic_quotes_gpc()) :
130 $fwp_post = stripslashes_deep($fwp_post);
131 endif;
132 endif;
133
134 // Get the path relative to the plugins directory in which FWP is stored
135 preg_match (
136 '|'.preg_quote(WP_PLUGIN_DIR).'/(.+)$|',
137 dirname(__FILE__),
138 $ref
139 );
140
141 if (isset($ref[1])) :
142 $fwp_path = $ref[1];
143 else : // Something went wrong. Let's just guess.
144 $fwp_path = 'feedwordpress';
145 endif;
146
147 // If this is a FeedWordPress admin page, queue up scripts for AJAX functions that FWP uses
148 // If it is a display page or a non-FeedWordPress admin page, don't.
149 wp_register_style('feedwordpress-elements', WP_PLUGIN_URL.'/'.$fwp_path.'/feedwordpress-elements.css');
150 if (FeedWordPressSettingsUI::is_admin()) :
151 // For JavaScript that needs to be generated dynamically
152 add_action('admin_print_scripts', array('FeedWordPressSettingsUI', 'admin_scripts'));
153
154 // For CSS that needs to be generated dynamically.
155 add_action('admin_print_styles', array('FeedWordPressSettingsUI', 'admin_styles'));
156
157 wp_enqueue_style('dashboard');
158 wp_enqueue_style('feedwordpress-elements');
159
160 if (function_exists('wp_admin_css')) :
161 wp_admin_css('css/dashboard');
162 endif;
163 endif;
164
165 if (!FeedWordPress::needs_upgrade()) : // only work if the conditions are safe!
166
167 # Syndicated items are generally received in output-ready (X)HTML and
168 # should not be folded, crumpled, mutilated, or spindled by WordPress
169 # formatting filters. But we don't want to interfere with filters for
170 # any locally-authored posts, either.
171 #
172 # What WordPress should really have is a way for upstream filters to
173 # stop downstream filters from running at all. Since it doesn't, and
174 # since a downstream filter can't access the original copy of the text
175 # that is being filtered, what we will do here is (1) save a copy of the
176 # original text upstream, before any other filters run, and then (2)
177 # retrieve that copy downstream, after all the other filters run, *if*
178 # this is a syndicated post
179
180 add_filter('the_content', 'feedwordpress_preserve_syndicated_content', -10000);
181 add_filter('the_content', 'feedwordpress_restore_syndicated_content', 10000);
182
183 add_action('atom_entry', 'feedwordpress_item_feed_data');
184
185 # Filter in original permalinks if the user wants that
186 add_filter('post_link', 'syndication_permalink', /*priority=*/ 1, /*arguments=*/ 3);
187
188 # When foreign URLs are used for permalinks in feeds or display
189 # contexts, they need to be escaped properly.
190 add_filter('the_permalink', 'syndication_permalink_escaped');
191 add_filter('the_permalink_rss', 'syndication_permalink_escaped');
192
193 add_filter('post_comments_feed_link', 'syndication_comments_feed_link');
194
195 # WTF? By default, wp_insert_link runs incoming link_url and link_rss
196 # URIs through default filters that include `wp_kses()`. But `wp_kses()`
197 # just happens to escape any occurrence of & to &amp; -- which just
198 # happens to fuck up any URI with a & to separate GET parameters.
199 remove_filter('pre_link_rss', 'wp_filter_kses');
200 remove_filter('pre_link_url', 'wp_filter_kses');
201
202 # Admin menu
203 add_action('admin_menu', 'fwp_add_pages');
204 add_action('admin_notices', 'fwp_check_debug');
205
206 add_action('admin_menu', 'feedwordpress_add_post_edit_controls');
207 add_action('save_post', 'feedwordpress_save_post_edit_controls');
208
209 add_action('admin_footer', array('FeedWordPress', 'admin_footer'));
210
211 # Inbound XML-RPC update methods
212 $feedwordpressRPC = new FeedWordPressRPC;
213
214 # Outbound XML-RPC ping reform
215 remove_action('publish_post', 'generic_ping'); // WP 1.5.x
216 remove_action('do_pings', 'do_all_pings', 10, 1); // WP 2.1, 2.2
217 remove_action('publish_post', '_publish_post_hook', 5, 1); // WP 2.3
218
219 add_action('publish_post', 'fwp_publish_post_hook', 5, 1);
220 add_action('do_pings', 'fwp_do_pings', 10, 1);
221 add_action('feedwordpress_update', 'fwp_hold_pings');
222 add_action('feedwordpress_update_complete', 'fwp_release_pings');
223
224 add_action('syndicated_feed_error', array('FeedWordPressDiagnostic', 'feed_error'), 100, 3);
225
226 add_action('wp_footer', 'debug_out_feedwordpress_footer', -100);
227 add_action('admin_footer', 'debug_out_feedwordpress_footer', -100);
228
229 $feedwordpress = new FeedWordPress;
230
231 # Cron-less auto-update. Hooray!
232 $autoUpdateHook = $feedwordpress->automatic_update_hook();
233 if (!is_null($autoUpdateHook)) :
234 add_action($autoUpdateHook, array(&$feedwordpress, 'auto_update'));
235 endif;
236
237 add_action('init', array(&$feedwordpress, 'init'));
238 add_action('shutdown', array(&$feedwordpress, 'email_diagnostic_log'));
239 add_action('wp_dashboard_setup', array(&$feedwordpress, 'dashboard_setup'));
240
241 # Default sanitizers
242 add_filter('syndicated_item_content', array('SyndicatedPost', 'resolve_relative_uris'), 0, 2);
243 add_filter('syndicated_item_content', array('SyndicatedPost', 'sanitize_content'), 0, 2);
244
245 else :
246 # Hook in the menus, which will just point to the upgrade interface
247 add_action('admin_menu', 'fwp_add_pages');
248 endif; // if (!FeedWordPress::needs_upgrade())
249
250 ################################################################################
251 ## LOGGING FUNCTIONS: log status updates to error_log if you want it ###########
252 ################################################################################
253
254 class FeedWordPressDiagnostic {
255 function feed_error ($error, $old, $link) {
256 $wpError = $error['object'];
257 $url = $link->uri();
258
259 $mesgs = $wpError->get_error_messages();
260 foreach ($mesgs as $mesg) :
261 $mesg = esc_html($mesg);
262 FeedWordPress::diagnostic(
263 'updated_feeds:errors',
264 "Feed Error: [${url}] update returned error: $mesg"
265 );
266
267 $hours = get_option('feedwordpress_diagnostics_persistent_errors_hours', 2);
268 $span = ($error['ts'] - $error['since']);
269
270 if ($span >= ($hours * 60 * 60)) :
271 $since = date('r', $error['since']);
272 $mostRecent = date('r', $error['ts']);
273 FeedWordPress::diagnostic(
274 'updated_feeds:errors:persistent',
275 "Feed Update Error: [${url}] returning errors"
276 ." since ${since}:<br/><code>$mesg</code>",
277 $url, $error['since'], $error['ts']
278 );
279 endif;
280 endforeach;
281 }
282
283 function admin_emails ($id = '') {
284 $users = get_users_of_blog($id);
285 $recipients = array();
286 foreach ($users as $user) :
287 $user_id = (isset($user->user_id) ? $user->user_id : $user->ID);
288 $dude = new WP_User($user_id);
289 if ($dude->has_cap('administrator')) :
290 if ($dude->user_email) :
291 $recipients[] = $dude->user_email;
292 endif;
293 endif;
294 endforeach;
295 return $recipients;
296 }
297 } /* class FeedWordPressDiagnostic */
298
299 function debug_out_human_readable_bytes ($quantity) {
300 $quantity = (int) $quantity;
301 $magnitude = 'B';
302 $orders = array('KB', 'MB', 'GB', 'TB');
303 while (($quantity > (1024*100)) and (count($orders) > 0)) :
304 $quantity = floor($quantity / 1024);
305 $magnitude = array_shift($orders);
306 endwhile;
307 return "${quantity} ${magnitude}";
308 }
309
310 function debug_out_feedwordpress_footer () {
311 if (FeedWordPress::diagnostic_on('memory_usage')) :
312 if (function_exists('memory_get_usage')) :
313 FeedWordPress::diagnostic ('memory_usage', "Memory: Current usage: ".debug_out_human_readable_bytes(memory_get_usage()));
314 endif;
315 if (function_exists('memory_get_peak_usage')) :
316 FeedWordPress::diagnostic ('memory_usage', "Memory: Peak usage: ".debug_out_human_readable_bytes(memory_get_peak_usage()));
317 endif;
318 endif;
319 } /* debug_out_feedwordpress_footer() */
320
321 ################################################################################
322 ## TEMPLATE API: functions to make your templates syndication-aware ############
323 ################################################################################
324
325 /**
326 * is_syndicated: Tests whether the current post in a Loop context, or a post
327 * given by ID number, was syndicated by FeedWordPress. Useful for templates
328 * to determine whether or not to retrieve syndication-related meta-data in
329 * displaying a post.
330 *
331 * @param int $id The post to check for syndicated status. Defaults to the current post in a Loop context.
332 * @return bool TRUE if the post's meta-data indicates it was syndicated; FALSE otherwise
333 */
334 function is_syndicated ($id = NULL) {
335 return (strlen(get_syndication_feed_id($id)) > 0);
336 } /* function is_syndicated() */
337
338 function feedwordpress_display_url ($url, $before = 60, $after = 0) {
339 $bits = parse_url($url);
340
341 // Strip out crufty subdomains
342 if (isset($bits['host'])) :
343 $bits['host'] = preg_replace('/^www[0-9]*\./i', '', $bits['host']);
344 endif;
345
346 // Reassemble bit-by-bit with minimum of crufty elements
347 $url = (isset($bits['user'])?$bits['user'].'@':'')
348 .(isset($bits['host'])?$bits['host']:'')
349 .(isset($bits['path'])?$bits['path']:'')
350 .(isset($uri_bits['port'])?':'.$uri_bits['port']:'')
351 .(isset($bits['query'])?'?'.$bits['query']:'');
352
353 if (strlen($url) > ($before+$after)) :
354 $url = substr($url, 0, $before).'…'.substr($url, 0 - $after, $after);
355 endif;
356
357 return $url;
358 } /* feedwordpress_display_url () */
359
360 function get_syndication_source_property ($original, $id, $local, $remote = NULL) {
361 $ret = NULL;
362 if (is_null($original)) :
363 $original = FeedWordPress::use_aggregator_source_data();
364 endif;
365
366 if (is_null($remote)) :
367 $remote = $local . '_original';
368 endif;
369
370 $vals = ($original ? get_post_custom_values($remote, $id) : array());
371 if (count($vals) < 1) :
372 $vals = get_post_custom_values($local, $id);
373 endif;
374
375 if (count($vals) > 0) :
376 $ret = $vals[0];
377 endif;
378 return $ret;
379 } /* function get_syndication_source_property () */
380
381 function the_syndication_source_link ($original = NULL, $id = NULL) { echo get_syndication_source_link($original, $id); }
382 function get_syndication_source_link ($original = NULL, $id = NULL) {
383 return get_syndication_source_property($original, $id, 'syndication_source_uri');
384 } /* function get_syndication_source_link() */
385
386 function the_syndication_source ($original = NULL, $id = NULL) { echo get_syndication_source($original, $id); }
387 function get_syndication_source ($original = NULL, $id = NULL) {
388 $ret = get_syndication_source_property($original, $id, 'syndication_source');
389 if (is_null($ret) or strlen(trim($ret)) == 0) : // Fall back to URL of blog
390 $ret = feedwordpress_display_url(get_syndication_source_link());
391 endif;
392 return $ret;
393 } /* function get_syndication_source() */
394
395 function the_syndication_feed ($original = NULL, $id = NULL) { echo get_syndication_feed($original, $id); }
396 function get_syndication_feed ($original = NULL, $id = NULL) {
397 return get_syndication_source_property($original, $id, 'syndication_feed');
398 } /* function get_syndication_feed() */
399
400 function the_syndication_feed_guid ($original = NULL, $id = NULL) { echo get_syndication_feed_guid($original, $id); }
401 function get_syndication_feed_guid ($original = NULL, $id = NULL) {
402 $ret = get_syndication_source_property($original, $id, 'syndication_source_id');
403 if (is_null($ret) or strlen(trim($ret))==0) : // Fall back to URL of feed
404 $ret = get_syndication_feed();
405 endif;
406 return $ret;
407 } /* function get_syndication_feed_guid () */
408
409 function get_syndication_feed_id ($id = NULL) { list($u) = get_post_custom_values('syndication_feed_id', $id); return $u; }
410 function the_syndication_feed_id ($id = NULL) { echo get_syndication_feed_id($id); }
411
412 $feedwordpress_linkcache = array (); // only load links from database once
413 function get_syndication_feed_object ($id = NULL) {
414 global $feedwordpress_linkcache;
415
416 $link = NULL;
417
418 $feed_id = get_syndication_feed_id($id);
419 if (strlen($feed_id) > 0):
420 if (isset($feedwordpress_linkcache[$feed_id])) :
421 $link = $feedwordpress_linkcache[$feed_id];
422 else :
423 $link = new SyndicatedLink($feed_id);
424 $feedwordpress_linkcache[$feed_id] = $link;
425 endif;
426 endif;
427 return $link;
428 }
429
430 function get_feed_meta ($key, $id = NULL) {
431 $ret = NULL;
432
433 $link = get_syndication_feed_object($id);
434 if (is_object($link) and isset($link->settings[$key])) :
435 $ret = $link->settings[$key];
436 endif;
437 return $ret;
438 } /* get_feed_meta() */
439
440 function get_syndication_permalink ($id = NULL) {
441 list($u) = get_post_custom_values('syndication_permalink', $id); return $u;
442 }
443 function the_syndication_permalink ($id = NULL) {
444 echo get_syndication_permalink($id);
445 }
446
447 /**
448 * get_local_permalink: returns a string containing the internal permalink
449 * for a post (whether syndicated or not) on your local WordPress installation.
450 * This may be useful if you want permalinks to point to the original source of
451 * an article for most purposes, but want to retrieve a URL for the local
452 * representation of the post for one or two limited purposes (for example,
453 * linking to a comments page on your local aggregator site).
454 *
455 * @param $id The numerical ID of the post to get the permalink for. If empty,
456 * defaults to the current post in a Loop context.
457 * @return string The URL of the local permalink for this post.
458 *
459 * @uses get_permalink()
460 * @global $feedwordpress_the_original_permalink
461 *
462 * @since 2010.0217
463 */
464 function get_local_permalink ($id = NULL) {
465 global $feedwordpress_the_original_permalink;
466
467 // get permalink, and thus activate filter and force global to be filled
468 // with original URL.
469 $url = get_permalink($id);
470 return $feedwordpress_the_original_permalink;
471 } /* get_local_permalink() */
472
473 /**
474 * the_original_permalink: displays the contents of get_original_permalink()
475 *
476 * @param $id The numerical ID of the post to get the permalink for. If empty,
477 * defaults to the current post in a Loop context.
478 *
479 * @uses get_local_permalinks()
480 * @uses apply_filters
481 *
482 * @since 2010.0217
483 */
484 function the_local_permalink ($id = NULL) {
485 print apply_filters('the_permalink', get_local_permalink($id));
486 } /* function the_local_permalink() */
487
488 ################################################################################
489 ## FILTERS: syndication-aware handling of post data for templates and feeds ####
490 ################################################################################
491
492 $feedwordpress_the_syndicated_content = NULL;
493 $feedwordpress_the_original_permalink = NULL;
494
495 function feedwordpress_preserve_syndicated_content ($text) {
496 global $feedwordpress_the_syndicated_content;
497
498 $globalExpose = (get_option('feedwordpress_formatting_filters') == 'yes');
499 $localExpose = get_post_custom_values('_feedwordpress_formatting_filters');
500 $expose = ($globalExpose or ((count($localExpose) > 0) and $localExpose[0]));
501
502 if ( is_syndicated() and !$expose ) :
503 $feedwordpress_the_syndicated_content = $text;
504 else :
505 $feedwordpress_the_syndicated_content = NULL;
506 endif;
507 return $text;
508 }
509
510 function feedwordpress_restore_syndicated_content ($text) {
511 global $feedwordpress_the_syndicated_content;
512
513 if ( !is_null($feedwordpress_the_syndicated_content) ) :
514 $text = $feedwordpress_the_syndicated_content;
515 endif;
516
517 return $text;
518 }
519
520 function feedwordpress_item_feed_data () {
521 // In a post context....
522 if (is_syndicated()) :
523 ?>
524 <source>
525 <title><?php print htmlspecialchars(get_syndication_source()); ?></title>
526 <link rel="alternate" type="text/html" href="<?php print htmlspecialchars(get_syndication_source_link()); ?>" />
527 <link rel="self" href="<?php print htmlspecialchars(get_syndication_feed()); ?>" />
528 <?php
529 $id = get_syndication_feed_guid();
530 if (strlen($id) > 0) :
531 ?>
532 <id><?php print htmlspecialchars($id); ?></id>
533 <?php
534 endif;
535 $updated = get_feed_meta('feed/updated');
536 if (strlen($updated) > 0) : ?>
537 <updated><?php print $updated; ?></updated>
538 <?php
539 endif;
540 ?>
541 </source>
542 <?php
543 endif;
544 }
545
546 /**
547 * syndication_permalink: Allow WordPress to use the original remote URL of
548 * syndicated posts as their permalink. Can be turned on or off by by setting in
549 * Syndication => Posts & Links. Saves the old internal permalink in a global
550 * variable for later use.
551 *
552 * @param string $permalink The internal permalink
553 * @return string The new permalink. Same as the old if the post is not
554 * syndicated, or if FWP is set to use internal permalinks, or if the post
555 * was syndicated, but didn't have a proper permalink recorded.
556 *
557 * @uses SyndicatedLink::setting()
558 * @uses get_syndication_permalink()
559 * @uses get_syndication_feed_object()
560 * @uses url_to_postid()
561 * @global $id
562 * @global $feedwordpress_the_original_permalink
563 */
564 function syndication_permalink ($permalink = '', $post = null, $leavename = false) {
565 global $id;
566 global $feedwordpress_the_original_permalink;
567
568 // Save the local permalink in case we need to retrieve it later.
569 $feedwordpress_the_original_permalink = $permalink;
570
571 if (is_object($post) and isset($post->ID) and !empty($post->ID)) :
572 // Use the post ID we've been provided with.
573 $postId = $post->ID;
574 elseif (is_string($permalink) and strlen($permalink) > 0) :
575 // Map this permalink to a post ID so we can get the correct
576 // permalink even outside of the Post Loop. Props Björn.
577 $postId = url_to_postid($permalink);
578 else :
579 // If the permalink string is empty but Post Loop context
580 // provides an id.
581 $postId = $id;
582 endif;
583
584 $munge = false;
585 $link = get_syndication_feed_object($postId);
586 if (is_object($link)) :
587 $munge = ($link->setting('munge permalink', 'munge_permalink', 'yes') != 'no');
588 endif;
589
590 if ($munge):
591 $uri = get_syndication_permalink($postId);
592 $permalink = ((strlen($uri) > 0) ? $uri : $permalink);
593 endif;
594 return $permalink;
595 } /* function syndication_permalink () */
596
597 /**
598 * syndication_permalink_escaped: Escape XML special characters in syndicated
599 * permalinks when used in feed contexts and HTML display contexts.
600 *
601 * @param string $permalink
602 * @return string
603 *
604 * @uses is_syndicated()
605 * @uses FeedWordPress::munge_permalinks()
606 *
607 */
608 function syndication_permalink_escaped ($permalink) {
609 /* FIXME: This should review link settings not just global settings */
610 if (is_syndicated() and FeedWordPress::munge_permalinks()) :
611 // This is a foreign link; WordPress can't vouch for its not
612 // having any entities that need to be &-escaped. So we'll do
613 // it here.
614 $permalink = esc_html($permalink);
615 endif;
616 return $permalink;
617 } /* function syndication_permalink_escaped() */
618
619 /**
620 * syndication_comments_feed_link: Escape XML special characters in comments
621 * feed links
622 *
623 * @param string $link
624 * @return string
625 *
626 * @uses is_syndicated()
627 * @uses FeedWordPress::munge_permalinks()
628 */
629 function syndication_comments_feed_link ($link) {
630 global $feedwordpress_the_original_permalink, $id;
631
632 if (is_syndicated() and FeedWordPress::munge_permalinks()) :
633 // If the source post provided a comment feed URL using
634 // wfw:commentRss or atom:link/@rel="replies" we can make use of
635 // that value here.
636 $source = get_syndication_feed_object();
637 $replacement = NULL;
638 if ($source->setting('munge comments feed links', 'munge_comments_feed_links', 'yes') != 'no') :
639 $commentFeeds = get_post_custom_values('wfw:commentRSS');
640 if (
641 is_array($commentFeeds)
642 and (count($commentFeeds) > 0)
643 and (strlen($commentFeeds[0]) > 0)
644 ) :
645 $replacement = $commentFeeds[0];
646
647 // This is a foreign link; WordPress can't vouch for its not
648 // having any entities that need to be &-escaped. So we'll do it
649 // here.
650 $replacement = esc_html($replacement);
651 endif;
652 endif;
653
654 if (is_null($replacement)) :
655 // Q: How can we get the proper feed format, since the
656 // format is, stupidly, not passed to the filter?
657 // A: Kludge kludge kludge kludge!
658 $fancy_permalinks = ('' != get_option('permalink_structure'));
659 if ($fancy_permalinks) :
660 preg_match('|/feed(/([^/]+))?/?$|', $link, $ref);
661
662 $format = (isset($ref[2]) ? $ref[2] : '');
663 if (strlen($format) == 0) : $format = get_default_feed(); endif;
664
665 $replacement = trailingslashit($feedwordpress_the_original_permalink) . 'feed';
666 if ($format != get_default_feed()) :
667 $replacement .= '/'.$format;
668 endif;
669 $replacement = user_trailingslashit($replacement, 'single_feed');
670 else :
671 // No fancy permalinks = no problem
672 // WordPress doesn't call get_permalink() to
673 // generate the comment feed URL, so the
674 // comments feed link is never munged by FWP.
675 endif;
676 endif;
677
678 if (!is_null($replacement)) : $link = $replacement; endif;
679 endif;
680 return $link;
681 } /* function syndication_comments_feed_link() */
682
683 ################################################################################
684 ## ADMIN MENU ADD-ONS: register Dashboard management pages #####################
685 ################################################################################
686
687 function fwp_add_pages () {
688 $menu_cap = FeedWordPress::menu_cap();
689 $settings_cap = FeedWordPress::menu_cap(/*sub=*/ true);
690 $syndicationMenu = FeedWordPress::path('syndication.php');
691
692 add_menu_page(
693 'Syndicated Sites', 'Syndication',
694 $menu_cap,
695 $syndicationMenu,
696 NULL,
697 WP_PLUGIN_URL.'/'.FeedWordPress::path('feedwordpress-tiny.png')
698 );
699
700 do_action('feedwordpress_admin_menu_pre_feeds', $menu_cap, $settings_cap);
701 add_submenu_page(
702 $syndicationMenu, 'Syndicated Feeds & Updates', 'Feeds & Updates',
703 $settings_cap, FeedWordPress::path('feeds-page.php')
704 );
705
706 do_action('feedwordpress_admin_menu_pre_posts', $menu_cap, $settings_cap);
707 add_submenu_page(
708 $syndicationMenu, 'Syndicated Posts & Links', 'Posts & Links',
709 $settings_cap, FeedWordPress::path('posts-page.php')
710 );
711
712 do_action('feedwordpress_admin_menu_pre_authors', $menu_cap, $settings_cap);
713 add_submenu_page(
714 $syndicationMenu, 'Syndicated Authors', 'Authors',
715 $settings_cap, FeedWordPress::path('authors-page.php')
716 );
717
718 do_action('feedwordpress_admin_menu_pre_categories', $menu_cap, $settings_cap);
719 add_submenu_page(
720 $syndicationMenu, 'Categories'.FEEDWORDPRESS_AND_TAGS, 'Categories'.FEEDWORDPRESS_AND_TAGS,
721 $settings_cap, FeedWordPress::path('categories-page.php')
722 );
723
724 do_action('feedwordpress_admin_menu_pre_performance', $menu_cap, $settings_cap);
725 add_submenu_page(
726 $syndicationMenu, 'FeedWordPress Performance', 'Performance',
727 $settings_cap, FeedWordPress::path('performance-page.php')
728 );
729
730 do_action('feedwordpress_admin_menu_pre_diagnostics', $menu_cap, $settings_cap);
731 add_submenu_page(
732 $syndicationMenu, 'FeedWordPress Diagnostics', 'Diagnostics',
733 $settings_cap, FeedWordPress::path('diagnostics-page.php')
734 );
735 } /* function fwp_add_pages () */
736
737 function fwp_check_debug () {
738 // This is a horrible fucking kludge that I have to do because the
739 // admin notice code is triggered before the code that updates the
740 // setting.
741 if (isset($_POST['feedwordpress_debug'])) :
742 $feedwordpress_debug = $_POST['feedwordpress_debug'];
743 else :
744 $feedwordpress_debug = get_option('feedwordpress_debug');
745 endif;
746 if ($feedwordpress_debug==='yes') :
747 ?>
748 <div class="error">
749 <p><strong>FeedWordPress warning.</strong> Debugging mode is <strong>ON</strong>.
750 While it remains on, FeedWordPress displays many diagnostic error messages,
751 warnings, and notices that are ordinarily suppressed, and also turns off all
752 caching of feeds. Use with caution: this setting is absolutely inappropriate
753 for a production server.</p>
754 </div>
755 <?php
756 endif;
757 } /* function fwp_check_debug () */
758
759 ################################################################################
760 ## fwp_hold_pings() and fwp_release_pings(): Outbound XML-RPC ping reform ####
761 ## ... 'coz it's rude to send 500 pings the first time your aggregator runs ####
762 ################################################################################
763
764 $fwp_held_ping = NULL; // NULL: not holding pings yet
765
766 function fwp_hold_pings () {
767 global $fwp_held_ping;
768 if (is_null($fwp_held_ping)):
769 $fwp_held_ping = 0; // 0: ready to hold pings; none yet received
770 endif;
771 }
772
773 function fwp_release_pings () {
774 global $fwp_held_ping;
775 if ($fwp_held_ping):
776 if (function_exists('wp_schedule_single_event')) :
777 wp_schedule_single_event(time(), 'do_pings');
778 else :
779 generic_ping($fwp_held_ping);
780 endif;
781 endif;
782 $fwp_held_ping = NULL; // NULL: not holding pings anymore
783 }
784
785 function fwp_do_pings () {
786 if (!is_null($fwp_held_ping) and $post_id) : // Defer until we're done updating
787 $fwp_held_ping = $post_id;
788 elseif (function_exists('do_all_pings')) :
789 do_all_pings();
790 else :
791 generic_ping($fwp_held_ping);
792 endif;
793 }
794
795 function fwp_publish_post_hook ($post_id) {
796 global $fwp_held_ping;
797
798 if (!is_null($fwp_held_ping)) : // Syndicated post. Don't mark with _pingme
799 if ( defined('XMLRPC_REQUEST') )
800 do_action('xmlrpc_publish_post', $post_id);
801 if ( defined('APP_REQUEST') )
802 do_action('app_publish_post', $post_id);
803
804 if ( defined('WP_IMPORTING') )
805 return;
806
807 // Defer sending out pings until we finish updating
808 $fwp_held_ping = $post_id;
809 else :
810 if (function_exists('_publish_post_hook')) : // WordPress 2.3
811 _publish_post_hook($post_id);
812 endif;
813 endif;
814 }
815
816 function feedwordpress_add_post_edit_controls () {
817 add_meta_box('feedwordpress-post-controls', __('Syndication'), 'feedwordpress_post_edit_controls', 'post', 'side', 'high');
818 if (FeedWordPress::diagnostic_on('syndicated_posts:static_meta_data')) :
819 $GLOBALS['inspectPostMeta'] = new InspectPostMeta;
820 endif;
821 } // function FeedWordPress::postEditControls
822
823 function feedwordpress_post_edit_controls () {
824 global $post;
825
826 $frozen_values = get_post_custom_values('_syndication_freeze_updates', $post->ID);
827 $frozen_post = (count($frozen_values) > 0 and 'yes' == $frozen_values[0]);
828
829 if (is_syndicated($post->ID)) :
830 ?>
831 <p>This is a syndicated post, which originally appeared at
832 <cite><?php print esc_html(get_syndication_source(NULL, $post->ID)); ?></cite>.
833 <a href="<?php print esc_html(get_syndication_permalink($post->ID)); ?>">View original post</a>.</p>
834
835 <p><input type="hidden" name="feedwordpress_noncename" id="feedwordpress_noncename" value="<?php print wp_create_nonce(plugin_basename(__FILE__)); ?>" />
836 <label><input type="checkbox" name="freeze_updates" value="yes" <?php if ($frozen_post) : ?>checked="checked"<?php endif; ?> /> <strong>Manual editing.</strong>
837 If set, FeedWordPress will not overwrite the changes you make manually
838 to this post, if the syndicated content is updated on the
839 feed.</label></p>
840 <?php
841 else :
842 ?>
843 <p>This post was created locally at this website.</p>
844 <?php
845 endif;
846 } // function feedwordpress_post_edit_controls () */
847
848 function feedwordpress_save_post_edit_controls ( $post_id ) {
849 global $post;
850
851 if (!isset($_POST['feedwordpress_noncename']) or !wp_verify_nonce($_POST['feedwordpress_noncename'], plugin_basename(__FILE__))) :
852 return $post_id;
853 endif;
854
855 // Verify if this is an auto save routine. If it is our form has
856 // not been submitted, so we don't want to do anything.
857 if ( defined('DOING_AUTOSAVE') and DOING_AUTOSAVE ) :
858 return $post_id;
859 endif;
860
861 // Check permissions
862 if ( !current_user_can( 'edit_'.$_POST['post_type'], $post_id) ) :
863 return $post_id;
864 endif;
865
866 // OK, we're golden. Now let's save some data.
867 if (isset($_POST['freeze_updates'])) :
868 update_post_meta($post_id, '_syndication_freeze_updates', $_POST['freeze_updates']);
869 $ret = $_POST['freeze_updates'];
870 else :
871 delete_post_meta($post_id, '_syndication_freeze_updates');
872 $ret = NULL;
873 endif;
874
875 return $ret;
876 } // function feedwordpress_save_edit_controls
877
878 ################################################################################
879 ## class FeedWordPress #########################################################
880 ################################################################################
881
882 // class FeedWordPress: handles feed updates and plugs in to the XML-RPC interface
883 class FeedWordPress {
884 var $strip_attrs = array (
885 array('[a-z]+', 'style'),
886 array('[a-z]+', 'target'),
887 );
888 var $uri_attrs = array (
889 array('a', 'href'),
890 array('applet', 'codebase'),
891 array('area', 'href'),
892 array('blockquote', 'cite'),
893 array('body', 'background'),
894 array('del', 'cite'),
895 array('form', 'action'),
896 array('frame', 'longdesc'),
897 array('frame', 'src'),
898 array('iframe', 'longdesc'),
899 array('iframe', 'src'),
900 array('head', 'profile'),
901 array('img', 'longdesc'),
902 array('img', 'src'),
903 array('img', 'usemap'),
904 array('input', 'src'),
905 array('input', 'usemap'),
906 array('ins', 'cite'),
907 array('link', 'href'),
908 array('object', 'classid'),
909 array('object', 'codebase'),
910 array('object', 'data'),
911 array('object', 'usemap'),
912 array('q', 'cite'),
913 array('script', 'src')
914 );
915
916 var $feeds = NULL;
917
918 # function FeedWordPress (): Contructor; retrieve a list of feeds
919 function FeedWordPress () {
920 $this->feeds = array ();
921 $links = FeedWordPress::syndicated_links();
922 if ($links): foreach ($links as $link):
923 $this->feeds[] = new SyndicatedLink($link);
924 endforeach; endif;
925 } // FeedWordPress::FeedWordPress ()
926
927 # function update (): polls for updates on one or more Contributor feeds
928 #
929 # Arguments:
930 # ----------
931 # * $uri (string): either the URI of the feed to poll, the URI of the
932 # (human-readable) website whose feed you want to poll, or NULL.
933 #
934 # If $uri is NULL, then FeedWordPress will poll any feeds that are
935 # ready for polling. It will not poll feeds that are marked as
936 # "Invisible" Links (signifying that the subscription has been
937 # de-activated), or feeds that are not yet stale according to their
938 # TTL setting (which is either set in the feed, or else set
939 # randomly within a window of 30 minutes - 2 hours).
940 #
941 # Returns:
942 # --------
943 # * Normally returns an associative array, with 'new' => the number
944 # of new posts added during the update, and 'updated' => the number
945 # of old posts that were updated during the update. If both numbers
946 # are zero, there was no change since the last poll on that URI.
947 #
948 # * Returns NULL if URI it was passed was not a URI that this
949 # installation of FeedWordPress syndicates.
950 #
951 # Effects:
952 # --------
953 # * One or more feeds are polled for updates
954 #
955 # * If the feed Link does not have a hardcoded name set, its Link
956 # Name is synchronized with the feed's title element
957 #
958 # * If the feed Link does not have a hardcoded URI set, its Link URI
959 # is synchronized with the feed's human-readable link element
960 #
961 # * If the feed Link does not have a hardcoded description set, its
962 # Link Description is synchronized with the feed's description,
963 # tagline, or subtitle element.
964 #
965 # * The time of polling is recorded in the feed's settings, and the
966 # TTL (time until the feed is next available for polling) is set
967 # either from the feed (if it is supplied in the ttl or syndication
968 # module elements) or else from a randomly-generated time window
969 # (between 30 minutes and 2 hours).
970 #
971 # * New posts from the polled feed are added to the WordPress store.
972 #
973 # * Updates to existing posts since the last poll are mirrored in the
974 # WordPress store.
975 #
976 function update ($uri = null, $crash_ts = null) {
977 global $wpdb;
978
979 if (FeedWordPress::needs_upgrade()) : // Will make duplicate posts if we don't hold off
980 return NULL;
981 endif;
982
983 if (!is_null($uri) and $uri != '*') :
984 $uri = trim($uri);
985 else : // Update all
986 if ($this->update_hooked) :
987 $diag = $this->update_hooked;
988 else :
989 $diag = 'Initiating a MANUAL check-in on the update schedule at '.date('r', time());
990 endif;
991 $this->diagnostic('update_schedule:check', $diag);
992
993 update_option('feedwordpress_last_update_all', time());
994 endif;
995
996 do_action('feedwordpress_update', $uri);
997
998 if (is_null($crash_ts)) :
999 $crash_ts = $this->crash_ts();
1000 endif;
1001
1002 $max_polls = apply_filters('feedwordpress_polls_per_update', get_option('feedwordpress_polls_per_update', 10), $uri);
1003
1004 // Randomize order for load balancing purposes
1005 $feed_set = $this->feeds;
1006 shuffle($feed_set);
1007
1008 $feed_set = apply_filters('feedwordpress_update_feeds', $feed_set, $uri);
1009
1010 // Loop through and check for new posts
1011 $delta = NULL; $remaining = $max_polls;
1012 foreach ($feed_set as $feed) :
1013
1014 // Has this process overstayed its welcome?
1015 if (
1016 // Over time limit?
1017 (!is_null($crash_ts) and (time() > $crash_ts))
1018
1019 // Over feed count?
1020 or ($remaining == 0)
1021 ) :
1022 break;
1023 endif;
1024
1025 $pinged_that = (is_null($uri) or ($uri=='*') or in_array($uri, array($feed->uri(), $feed->homepage())));
1026
1027 if (!is_null($uri)) : // A site-specific ping always updates
1028 $timely = true;
1029 else :
1030 $timely = $feed->stale();
1031 endif;
1032
1033 if ($pinged_that and is_null($delta)) : // If at least one feed was hit for updating...
1034 $delta = array('new' => 0, 'updated' => 0); // ... don't return error condition
1035 endif;
1036
1037 if ($pinged_that and $timely) :
1038 $remaining = $remaining - 1;
1039
1040 do_action('feedwordpress_check_feed', $feed->settings);
1041 $start_ts = time();
1042 $added = $feed->poll($crash_ts);
1043 do_action('feedwordpress_check_feed_complete', $feed->settings, $added, time() - $start_ts);
1044
1045 if (is_array($added)) : // Success
1046 if (isset($added['new'])) : $delta['new'] += $added['new']; endif;
1047 if (isset($added['updated'])) : $delta['updated'] += $added['updated']; endif;
1048 endif;
1049 endif;
1050 endforeach;
1051
1052 do_action('feedwordpress_update_complete', $delta);
1053
1054 return $delta;
1055 }
1056
1057 function crash_ts ($default = NULL) {
1058 $crash_dt = (int) get_option('feedwordpress_update_time_limit', 0);
1059 if ($crash_dt > 0) :
1060 $crash_ts = time() + $crash_dt;
1061 else :
1062 $crash_ts = $default;
1063 endif;
1064 return $crash_ts;
1065 }
1066
1067 function secret_key () {
1068 $secret = get_option('feedwordpress_secret_key', false);
1069 if (!$secret) : // Generate random key.
1070 $secret = substr(md5(uniqid(microtime())), 0, 6);
1071 update_option('feedwordpress_secret_key', $secret);
1072 endif;
1073 return $secret;
1074 }
1075
1076 function has_secret () {
1077 return ($this->by_request('feedwordpress_key', $this->secret_key()));
1078 }
1079
1080 // Utility function so I don't have to repeat myself w/ 1,000,003 isset()'s
1081 function by_request ($param, $eq = NULL) {
1082 $match = false;
1083 if (isset($_REQUEST[$param])) :
1084 $match = (is_null($eq) ? $_REQUEST[$param] : ($eq==$_REQUEST[$param]));
1085 endif;
1086 return $match;
1087 }
1088
1089 var $update_hooked = NULL;
1090 function automatic_update_hook ($params = array()) {
1091 $params = wp_parse_args($params, array( // Defaults
1092 'setting only' => false,
1093 ));
1094 $hook = get_option('feedwordpress_automatic_updates', NULL);
1095 $method = 'FeedWordPress option';
1096
1097 // Allow for forced behavior in testing.
1098 if (
1099 !$params['setting only']
1100 and $this->has_secret()
1101 and $this->by_request('automatic_update')
1102 ) :
1103 $hook = $_REQUEST['automatic_update'];
1104 $method = 'URL parameter';
1105 endif;
1106
1107 $exact = $hook; // Before munging
1108
1109 if (!!$hook) :
1110 if ($hook != 'init') : // Constrain values.
1111 $hook = 'shutdown';
1112 endif;
1113 endif;
1114
1115 if ($hook) :
1116 $this->update_hooked = "Initiating an AUTOMATIC CHECK FOR UPDATES ON PAGE LOAD ".$hook." due to ".$method." = ".trim($this->val($exact));
1117 endif;
1118
1119 return $hook;
1120 }
1121 function last_update_all () {
1122 $last = get_option('feedwordpress_last_update_all');
1123 if ($this->has_secret() and $this->by_request('automatic_update')) :
1124 $last = 1; // A long, long time ago.
1125 elseif ($this->has_secret() and $this->by_request('last_update_all')) :
1126 $last = $_REQUEST['last_update_all'];
1127 endif;
1128 return $last;
1129 }
1130 function force_update_all () {
1131 return ($this->has_secret() and $this->by_request('force_update_feeds'));
1132 }
1133
1134 function stale () {
1135 if (!is_null($this->automatic_update_hook())) :
1136 // Do our best to avoid possible simultaneous
1137 // updates by getting up-to-the-minute settings.
1138
1139 $last = $this->last_update_all();
1140
1141 // If we haven't updated all yet, give it a time window
1142 if (false === $last) :
1143 $ret = false;
1144 update_option('feedwordpress_last_update_all', time());
1145
1146 // Otherwise, check against freshness interval
1147 elseif (is_numeric($last)) : // Expect a timestamp
1148 $freshness = get_option('feedwordpress_freshness');
1149 if (false === $freshness) : // Use default
1150 $freshness = FEEDWORDPRESS_FRESHNESS_INTERVAL;
1151 endif;
1152 $ret = ( (time() - $last) > $freshness );
1153
1154 // This should never happen.
1155 else :
1156 FeedWordPress::critical_bug('FeedWordPress::stale::last', $last, __LINE__, __FILE__);
1157 endif;
1158
1159 else :
1160 $ret = false;
1161 endif;
1162 return $ret;
1163 } // FeedWordPress::stale()
1164
1165 function init () {
1166 $this->clear_cache_magic_url();
1167 $this->update_magic_url();
1168 } /* FeedWordPress::init() */
1169
1170 function dashboard_setup () {
1171 $see_it = FeedWordPress::menu_cap();
1172
1173 if (current_user_can($see_it)) :
1174 // Get the stylesheet
1175 wp_enqueue_style('feedwordpress-elements');
1176
1177 $widget_id = 'feedwordpress_dashboard';
1178 $widget_name = __('Syndicated Sources');
1179 $column = 'side';
1180 $priority = 'core';
1181
1182 // I would love to use wp_add_dashboard_widget() here and save
1183 // myself some trouble. But WP 3 does not yet have any way to
1184 // push a dashboard widget onto the side, or to give it a default
1185 // location.
1186 add_meta_box(
1187 /*id=*/ $widget_id,
1188 /*title=*/ $widget_name,
1189 /*callback=*/ array(&$this, 'dashboard'),
1190 /*page=*/ 'dashboard',
1191 /*context=*/ $column,
1192 /*priority=*/ $priority
1193 );
1194 /*control_callback= array(&$this, 'dashboard_control') */
1195
1196 // This is kind of rude, I know, but the dashboard widget isn't
1197 // worth much if users don't know that it exists, and I don't
1198 // know of any better way to reorder the boxen.
1199 //
1200 // Gleefully ripped off of codex.wordpress.org/Dashboard_Widgets_API
1201
1202 // Globalize the metaboxes array, this holds all the widgets for wp-admin
1203 global $wp_meta_boxes;
1204
1205 // Get the regular dashboard widgets array
1206 // (which has our new widget already but at the end)
1207
1208 $normal_dashboard = $wp_meta_boxes['dashboard'][$column][$priority];
1209
1210 // Backup and delete our new dashbaord widget from the end of the array
1211 if (isset($normal_dashboard[$widget_id])) :
1212 $backup = array();
1213 $backup[$widget_id] = $normal_dashboard[$widget_id];
1214 unset($normal_dashboard[$widget_id]);
1215
1216 // Merge the two arrays together so our widget is at the
1217 // beginning
1218 $sorted_dashboard = array_merge($backup, $normal_dashboard);
1219
1220 // Save the sorted array back into the original metaboxes
1221 $wp_meta_boxes['dashboard'][$column][$priority] = $sorted_dashboard;
1222 endif;
1223 endif;
1224 } /* FeedWordPress::dashboard_setup () */
1225
1226 function dashboard () {
1227 $syndicationPage = new FeedWordPressSyndicationPage(dirname(__FILE__).'/syndication.php');
1228 $syndicationPage->dashboard_box($syndicationPage);
1229 } /* FeedWordPress::dashboard () */
1230
1231 function update_magic_url () {
1232 global $wpdb;
1233
1234 // Explicit update request in the HTTP request (e.g. from a cron job)
1235 if ($this->update_requested()) :
1236 $this->update_hooked = "Initiating a CRON JOB CHECK-IN ON UPDATE SCHEDULE due to URL parameter = ".trim($this->val($_REQUEST['update_feedwordpress']));
1237
1238 $this->update($this->update_requested_url());
1239
1240 if (FEEDWORDPRESS_DEBUG and count($wpdb->queries) > 0) :
1241 $mysqlTime = 0.0;
1242 $byTime = array();
1243 foreach ($wpdb->queries as $query) :
1244 $time = $query[1] * 1000000.0;
1245 $mysqlTime += $query[1];
1246 if (!isset($byTime[$time])) : $byTime[$time] = array(); endif;
1247 $byTime[$time][] = $query[0]. ' // STACK: ' . $query[2];
1248 endforeach;
1249 krsort($byTime);
1250
1251 foreach ($byTime as $time => $querySet) :
1252 foreach ($querySet as $query) :
1253 print "[".(sprintf('%4.4f', $time/1000.0)) . "ms] $query\n";
1254 endforeach;
1255 endforeach;
1256 echo $this->log_prefix()."$wpdb->num_queries queries. $mysqlTime seconds in MySQL. Total of "; timer_stop(1); print " seconds.";
1257 endif;
1258
1259 debug_out_feedwordpress_footer();
1260
1261 // Magic URL should return nothing but a 200 OK header packet
1262 // when successful.
1263 exit;
1264 endif;
1265 } /* FeedWordPress::magic_update_url () */
1266
1267 function clear_cache_magic_url () {
1268 if ($this->clear_cache_requested()) :
1269 $this->clear_cache();
1270 endif;
1271 } /* FeedWordPress::clear_cache_magic_url() */
1272
1273 function clear_cache_requested () {
1274 return (
1275 isset($_GET['clear_cache'])
1276 and $_GET['clear_cache']
1277 );
1278 } /* FeedWordPress::clear_cache_requested() */
1279
1280 function update_requested () {
1281 return FeedWordPress::by_request('update_feedwordpress');
1282 } // FeedWordPress::update_requested()
1283
1284 function update_requested_url () {
1285 $ret = null;
1286
1287 if (($_REQUEST['update_feedwordpress']=='*')
1288 or (preg_match('|^http://.*|i', $_REQUEST['update_feedwordpress']))) :
1289 $ret = $_REQUEST['update_feedwordpress'];
1290 endif;
1291
1292 return $ret;
1293 } // FeedWordPress::update_requested_url()
1294
1295 function auto_update () {
1296 if ($this->stale()) :
1297 $this->update();
1298 endif;
1299 } /* FeedWordPress::auto_update () */
1300
1301 function find_link ($uri, $field = 'link_rss') {
1302 global $wpdb;
1303
1304 $unslashed = untrailingslashit($uri);
1305 $slashed = trailingslashit($uri);
1306 $link_id = $wpdb->get_var($wpdb->prepare("
1307 SELECT link_id FROM $wpdb->links WHERE $field IN ('%s', '%s')
1308 LIMIT 1", $unslashed, $slashed
1309 ));
1310
1311 return $link_id;
1312 } /* FeedWordPress::find_link () */
1313
1314 function syndicate_link ($name, $uri, $rss) {
1315 // Get the category ID#
1316 $cat_id = FeedWordPress::link_category_id();
1317 if (!is_wp_error($cat_id)) :
1318 $link_category = array($cat_id);
1319 else :
1320 $link_category = array();
1321 endif;
1322
1323 // WordPress gets cranky if there's no homepage URI
1324 if (!is_string($uri) or strlen($uri)<1) : $uri = $rss; endif;
1325
1326 // Check if this feed URL is already being syndicated.
1327 $link_id = wp_insert_link(array(
1328 "link_id" => FeedWordPress::find_link($rss), // insert if nothing was found; else update
1329 "link_rss" => $rss,
1330 "link_name" => $name,
1331 "link_url" => $uri,
1332 "link_category" => $link_category,
1333 "link_visible" => 'Y', // reactivate if inactivated
1334 ));
1335
1336 return $link_id;
1337 } /* function FeedWordPress::syndicate_link() */
1338
1339 /*static*/ function syndicated_status ($what, $default) {
1340 $ret = get_option("feedwordpress_syndicated_{$what}_status");
1341 if (!$ret) :
1342 $ret = $default;
1343 endif;
1344 return $ret;
1345 } /* FeedWordPress::syndicated_status() */
1346
1347 function on_unfamiliar ($what = 'author') {
1348 switch ($what) :
1349 case 'category' : $suffix = ':category'; break;
1350 case 'post_tag' : $suffix = ':post_tag'; break;
1351 default: $suffix = '';
1352 endswitch;
1353
1354 return get_option('feedwordpress_unfamiliar_'.$what, 'create'.$suffix);
1355 } // function FeedWordPress::on_unfamiliar()
1356
1357 function null_email_set () {
1358 $base = get_option('feedwordpress_null_email_set');
1359
1360 if ($base===false) :
1361 $ret = array('noreply@blogger.com'); // default
1362 else :
1363 $ret = array_map('strtolower',
1364 array_map('trim', explode("\n", $base)));
1365 endif;
1366 $ret = apply_filters('syndicated_item_author_null_email_set', $ret);
1367 return $ret;
1368
1369 } /* FeedWordPress::null_email_set () */
1370
1371 function is_null_email ($email) {
1372 $ret = in_array(strtolower(trim($email)), FeedWordPress::null_email_set());
1373 $ret = apply_filters('syndicated_item_author_is_null_email', $ret, $email);
1374 return $ret;
1375 } /* FeedWordPress::is_null_email () */
1376
1377 function use_aggregator_source_data () {
1378 $ret = get_option('feedwordpress_use_aggregator_source_data');
1379 return apply_filters('syndicated_post_use_aggregator_source_data', ($ret=='yes'));
1380 }
1381
1382 /**
1383 * FeedWordPress::munge_permalinks: check whether or not FeedWordPress
1384 * should rewrite permalinks for syndicated items to reflect their
1385 * original location.
1386 *
1387 * @return bool TRUE if FeedWordPress SHOULD rewrite permalinks; FALSE otherwise
1388 */
1389 /*static*/ function munge_permalinks () {
1390 return (get_option('feedwordpress_munge_permalink', /*default=*/ 'yes') != 'no');
1391 } /* FeedWordPress::munge_permalinks() */
1392
1393 function syndicated_links ($args = array()) {
1394 $contributors = FeedWordPress::link_category_id();
1395 if (!is_wp_error($contributors)) :
1396 $links = get_bookmarks(array_merge(
1397 array("category" => $contributors),
1398 $args
1399 ));
1400 else :
1401 $links = array();
1402 endif;
1403 return $links;
1404 } // function FeedWordPress::syndicated_links()
1405
1406 function link_category_id () {
1407 global $wpdb, $wp_db_version;
1408
1409 $cat_id = get_option('feedwordpress_cat_id');
1410
1411 // If we don't yet have the category ID stored, search by name
1412 if (!$cat_id) :
1413 $cat_id = FeedWordPressCompatibility::link_category_id(DEFAULT_SYNDICATION_CATEGORY);
1414
1415 if ($cat_id) :
1416 // We found it; let's stamp it.
1417 update_option('feedwordpress_cat_id', $cat_id);
1418 endif;
1419
1420 // If we *do* have the category ID stored, verify that it exists
1421 else :
1422 $cat_id = FeedWordPressCompatibility::link_category_id((int) $cat_id, 'cat_id');
1423 endif;
1424
1425 // If we could not find an appropriate link category,
1426 // make a new one for ourselves.
1427 if (!$cat_id) :
1428 $cat_id = FeedWordPressCompatibility::insert_link_category(DEFAULT_SYNDICATION_CATEGORY);
1429 if (!is_wp_error($cat_id)) :
1430 // Stamp it
1431 update_option('feedwordpress_cat_id', $cat_id);
1432 endif;
1433 endif;
1434
1435 return $cat_id;
1436 } // function FeedWordPress::link_category_id()
1437
1438 # Upgrades and maintenance...
1439 function needs_upgrade () {
1440 global $wpdb;
1441 $fwp_db_version = get_option('feedwordpress_version');
1442 $ret = false; // innocent until proven guilty
1443 if (!$fwp_db_version or $fwp_db_version < FEEDWORDPRESS_VERSION) :
1444 // This is an older version or a fresh install. Does it
1445 // require a database upgrade or database initialization?
1446 if ($fwp_db_version <= 0.96) :
1447 // Yes. Check to see whether this is a fresh install or an upgrade.
1448 $syn = $wpdb->get_col("
1449 SELECT post_id
1450 FROM $wpdb->postmeta
1451 WHERE meta_key = 'syndication_feed'
1452 ");
1453 if (count($syn) > 0) : // contains at least one syndicated post
1454 $ret = true;
1455 else : // fresh install; brand it as ours
1456 update_option('feedwordpress_version', FEEDWORDPRESS_VERSION);
1457 endif;
1458 elseif ($fwp_db_version < 2009.0707) :
1459 // We need to clear out any busted AJAX crap
1460 $wpdb->query("
1461 DELETE FROM $wpdb->usermeta
1462 WHERE LOCATE('feedwordpress', meta_key)
1463 AND LOCATE('box', meta_key);
1464 ");
1465
1466 update_option('feedwordpress_version', FEEDWORDPRESS_VERSION);
1467 elseif ($fwp_db_version < 2010.0814) :
1468 // Change in terminology.
1469 if (get_option('feedwordpress_unfamiliar_category', 'create')=='default') :
1470 update_option('feedwordpress_unfamiliar_category', 'null');
1471 endif;
1472 foreach (FeedWordPress::syndicated_links() as $link) :
1473 $sub = new SyndicatedLink($link);
1474
1475 $remap_uf = array(
1476 'default' => 'null',
1477 'filter' => 'null',
1478 'create' => 'create:category',
1479 'tag' => 'create:post_tag'
1480 );
1481 if (isset($sub->settings['unfamiliar category'])) :
1482 if ($sub->settings['unfamiliar category']=='filter') :
1483 $sub->settings['match/filter'] = array('category');
1484 endif;
1485 foreach ($remap_uf as $from => $to) :
1486 if ($sub->settings['unfamiliar category']==$from) :
1487 $sub->settings['unfamiliar category'] = $to;
1488 endif;
1489 endforeach;
1490 endif;
1491
1492 if (isset($sub->settings['add global categories'])) :
1493 $sub->settings['add/category'] = $sub->settings['add global categories'];
1494 unset($sub->settings['add global categories']);
1495 endif;
1496
1497 $sub->save_settings(/*reload=*/ true);
1498 endforeach;
1499 update_option('feedwordpress_version', FEEDWORDPRESS_VERSION);
1500 else :
1501 // No. Just brand it with the new version.
1502 update_option('feedwordpress_version', FEEDWORDPRESS_VERSION);
1503 endif;
1504 endif;
1505 return $ret;
1506 }
1507
1508 function upgrade_database ($from = NULL) {
1509 global $wpdb;
1510
1511 if (is_null($from) or $from <= 0.96) : $from = 0.96; endif;
1512
1513 switch ($from) :
1514 case 0.96:
1515 // Dropping legacy upgrade code. If anyone is still
1516 // using 0.96 and just now decided to upgrade, well, I'm
1517 // sorry about that. You'll just have to cope with a few
1518 // duplicate posts.
1519
1520 // Mark the upgrade as successful.
1521 update_option('feedwordpress_version', FEEDWORDPRESS_VERSION);
1522 endswitch;
1523 echo "<p>Upgrade complete. FeedWordPress is now ready to use again.</p>";
1524 } /* FeedWordPress::upgrade_database() */
1525
1526 function has_guid_index () {
1527 global $wpdb;
1528
1529 $found = false; // Guilty until proven innocent.
1530
1531 $results = $wpdb->get_results("
1532 SHOW INDEXES FROM {$wpdb->posts}
1533 ");
1534 if ($results) :
1535 foreach ($results as $index) :
1536 if (isset($index->Column_name)
1537 and ('guid' == $index->Column_name)) :
1538 $found = true;
1539 endif;
1540 endforeach;
1541 endif;
1542 return $found;
1543 } /* FeedWordPress::has_guid_index () */
1544
1545 function create_guid_index () {
1546 global $wpdb;
1547
1548 $wpdb->query("
1549 CREATE INDEX {$wpdb->posts}_guid_idx ON {$wpdb->posts}(guid)
1550 ");
1551 } /* FeedWordPress::create_guid_index () */
1552
1553 function remove_guid_index () {
1554 global $wpdb;
1555
1556 $wpdb->query("
1557 DROP INDEX {$wpdb->posts}_guid_idx ON {$wpdb->posts}
1558 ");
1559 }
1560
1561 /*static*/ function fetch_timeout () {
1562 return apply_filters(
1563 'feedwordpress_fetch_timeout',
1564 intval(get_option('feedwordpress_fetch_timeout', FEEDWORDPRESS_FETCH_TIMEOUT_DEFAULT))
1565 );
1566 }
1567
1568 /*static*/ function fetch ($url, $params = array()) {
1569 $force_feed = true; // Default
1570
1571 // Allow user to change default feed-fetch timeout with a global setting. Props Erigami Scholey-Fuller <http://www.piepalace.ca/blog/2010/11/feedwordpress-broke-my-heart.html> 'timeout' =>
1572 $timeout = FeedWordPress::fetch_timeout();
1573
1574 if (!is_array($params)) :
1575 $force_feed = $params;
1576 else : // Parameter array
1577 $args = shortcode_atts(array(
1578 'force_feed' => $force_feed,
1579 'timeout' => $timeout
1580 ), $params);
1581
1582 extract($args);
1583 endif;
1584 $timeout = intval($timeout);
1585
1586 $pie_class = apply_filters('feedwordpress_simplepie_class', 'SimplePie');
1587 $cache_class = apply_filters('feedwordpress_cache_class', 'WP_Feed_Cache');
1588 $file_class = apply_filters('feedwordpress_file_class', 'FeedWordPress_File');
1589 $parser_class = apply_filters('feedwordpress_parser_class', 'FeedWordPress_Parser');
1590
1591 $sniffer_class = apply_filters('feedwordpress_sniffer_class', 'FeedWordPress_Content_Type_Sniffer');
1592
1593 $feed = new $pie_class;
1594 $feed->set_feed_url($url);
1595 $feed->set_cache_class($cache_class);
1596 $feed->set_timeout($timeout);
1597
1598 $feed->set_content_type_sniffer_class($sniffer_class);
1599 $feed->set_file_class($file_class);
1600 $feed->set_parser_class($parser_class);
1601 $feed->force_feed($force_feed);
1602 $feed->set_cache_duration(FeedWordPress::cache_duration());
1603 $feed->init();
1604 $feed->handle_content_type();
1605
1606 if ($feed->error()) :
1607 $ret = new WP_Error('simplepie-error', $feed->error());
1608 else :
1609 $ret = $feed;
1610 endif;
1611 return $ret;
1612 } /* FeedWordPress::fetch () */
1613
1614 function clear_cache () {
1615 global $wpdb;
1616
1617 // Just in case, clear out any old MagpieRSS cache records.
1618 $magpies = $wpdb->query("
1619 DELETE FROM {$wpdb->options}
1620 WHERE option_name LIKE 'rss_%' AND LENGTH(option_name) > 32
1621 ");
1622
1623 // The WordPress SimplePie module stores its cached feeds as
1624 // transient records in the options table. The data itself is
1625 // stored in `_transient_feed_{md5 of url}` and the last-modified
1626 // timestamp in `_transient_feed_mod_{md5 of url}`. Timeouts for
1627 // these records are stored in `_transient_timeout_feed_{md5}`.
1628 // Since the md5 is always 32 characters in length, the
1629 // option_name is always over 32 characters.
1630 $simplepies = $wpdb->query("
1631 DELETE FROM {$wpdb->options}
1632 WHERE option_name LIKE '_transient%_feed_%' AND LENGTH(option_name) > 32
1633 ");
1634 $simplepies = (int) ($simplepies / 4); // Each transient has 4 rows: the data, the modified timestamp; and the timeouts for each
1635
1636 return ($magpies + $simplepies);
1637 } /* FeedWordPress::clear_cache () */
1638
1639 function cache_duration () {
1640 $duration = NULL;
1641 if (defined('FEEDWORDPRESS_CACHE_AGE')) :
1642 $duration = FEEDWORDPRESS_CACHE_AGE;
1643 endif;
1644 return $duration;
1645 }
1646 function cache_lifetime ($duration) {
1647 // Check for explicit setting of a lifetime duration
1648 if (defined('FEEDWORDPRESS_CACHE_LIFETIME')) :
1649 $duration = FEEDWORDPRESS_CACHE_LIFETIME;
1650
1651 // Fall back to the cache freshness duration
1652 elseif (defined('FEEDWORDPRESS_CACHE_AGE')) :
1653 $duration = FEEDWORDPRESS_CACHE_AGE;
1654 endif;
1655
1656 // Fall back to WordPress default
1657 return $duration;
1658 } /* FeedWordPress::cache_lifetime () */
1659
1660 # Utility functions for handling text settings
1661 function negative ($f, $setting) {
1662 $nego = array ('n', 'no', 'f', 'false');
1663 return (isset($f[$setting]) and in_array(strtolower($f[$setting]), $nego));
1664 }
1665
1666 function affirmative ($f, $setting) {
1667 $affirmo = array ('y', 'yes', 't', 'true', 1);
1668 return (isset($f[$setting]) and in_array(strtolower($f[$setting]), $affirmo));
1669 }
1670
1671 # Internal debugging functions
1672 function critical_bug ($varname, $var, $line, $file = NULL) {
1673 global $wp_version;
1674
1675 if (!is_null($file)) :
1676 $location = "line # ${line} of ".basename($file);
1677 else :
1678 $location = "line # ${line}";
1679 endif;
1680
1681 print '<p><strong>Critical error:</strong> 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>';
1682 print "\n<plaintext>";
1683 print "Triggered at ${location}\n";
1684 print "FeedWordPress: ".FEEDWORDPRESS_VERSION."\n";
1685 print "WordPress: {$wp_version}\n";
1686 print "PHP: ".phpversion()."\n";
1687 print "Error data: ";
1688 print $varname.": "; var_dump($var); echo "\n";
1689 die;
1690 }
1691
1692 function noncritical_bug ($varname, $var, $line, $file = NULL) {
1693 if (FEEDWORDPRESS_DEBUG) : // halt only when we are doing debugging
1694 FeedWordPress::critical_bug($varname, $var, $line, $file);
1695 endif;
1696 }
1697
1698 function val ($v, $no_newlines = false) {
1699 ob_start();
1700 var_dump($v);
1701 $out = ob_get_contents(); ob_end_clean();
1702
1703 if ($no_newlines) :
1704 $out = preg_replace('/\s+/', " ", $out);
1705 endif;
1706 return $out;
1707 } /* FeedWordPress::val () */
1708
1709 function diagnostic_on ($level) {
1710 $show = get_option('feedwordpress_diagnostics_show', array());
1711 return (in_array($level, $show));
1712 } /* FeedWordPress::diagnostic_on () */
1713
1714 function diagnostic ($level, $out, $persist = NULL, $since = NULL, $mostRecent = NULL) {
1715 global $feedwordpress_admin_footer;
1716
1717 $output = get_option('feedwordpress_diagnostics_output', array());
1718 $dlog = get_option('feedwordpress_diagnostics_log', array());
1719
1720 $diagnostic_nesting = count(explode(":", $level));
1721
1722 if (FeedWordPress::diagnostic_on($level)) :
1723 foreach ($output as $method) :
1724 switch ($method) :
1725 case 'echo' :
1726 if (!FeedWordPress::update_requested()) :
1727 echo "<div><pre><strong>Diag".str_repeat('====', $diagnostic_nesting-1).'|</strong> '.$out."</pre></div>";
1728 endif;
1729 break;
1730 case 'echo_in_cronjob' :
1731 if (FeedWordPress::update_requested()) :
1732 echo FeedWordPress::log_prefix()." ".$out."\n";
1733 endif;
1734 break;
1735 case 'admin_footer' :
1736 $feedwordpress_admin_footer[] = $out;
1737 break;
1738 case 'error_log' :
1739 error_log(FeedWordPress::log_prefix().' '.$out);
1740 break;
1741 case 'email' :
1742 if (is_null($persist)) :
1743 $sect = 'occurrent';
1744 $hook = (isset($dlog['mesg'][$sect]) ? count($dlog['mesg'][$sect]) : 0);
1745 $line = array("Time" => time(), "Message" => $out);
1746 else :
1747 $sect = 'persistent';
1748 $hook = md5($level."\n".$persist);
1749 $line = array("Since" => $since, "Message" => $out, "Most Recent" => $mostRecent);
1750 endif;
1751
1752 if (!isset($dlog['mesg'])) : $dlog['mesg'] = array(); endif;
1753 if (!isset($dlog['mesg'][$sect])) : $dlog['mesg'][$sect] = array(); endif;
1754
1755 $dlog['mesg'][$sect][$hook] = $line;
1756 endswitch;
1757 endforeach;
1758 endif;
1759
1760 update_option('feedwordpress_diagnostics_log', $dlog);
1761 } /* FeedWordPress::diagnostic () */
1762
1763 function email_diagnostics_override () {
1764 return ($this->has_secret() and isset($_REQUEST['feedwordpress_email_diagnostics']) and !!$_REQUEST['feedwordpress_email_diagnostics']);
1765 }
1766 function has_emailed_diagnostics ($dlog) {
1767 $ret = false;
1768 if ($this->email_diagnostics_override()
1769 or (isset($dlog['schedule']) and isset($dlog['schedule']['last']))) :
1770 $ret = true;
1771 endif;
1772 return $ret;
1773 }
1774 function ready_to_email_diagnostics ($dlog) {
1775 $ret = false;
1776 if ($this->email_diagnostics_override()
1777 or (time() > ($dlog['schedule']['last'] + $dlog['schedule']['freq']))) :
1778 $ret = true;
1779 endif;
1780 return $ret;
1781 }
1782
1783 function email_diagnostic_log () {
1784 $dlog = get_option('feedwordpress_diagnostics_log', array());
1785
1786 if ($this->has_emailed_diagnostics($dlog)) :
1787 if ($this->ready_to_email_diagnostics($dlog)) :
1788 // No news is good news; only send if
1789 // there are some messages to send.
1790 $body = NULL;
1791 if (!isset($dlog['mesg'])) : $dlog['mesg'] = array(); endif;
1792
1793 foreach ($dlog['mesg'] as $sect => $mesgs) :
1794 if (count($mesgs) > 0) :
1795 if (is_null($body)) : $body = ''; endif;
1796
1797 $paradigm = reset($mesgs);
1798 $body .= "<h2>".ucfirst($sect)." issues</h2>\n"
1799 ."<table>\n"
1800 ."<thead><tr>\n";
1801 foreach ($paradigm as $col => $value) :
1802 $body .= '<th scope="col">'.$col."</th>\n";
1803 endforeach;
1804 $body .= "</tr></thead>\n"
1805 ."<tbody>\n";
1806
1807 foreach ($mesgs as $line) :
1808 $body .= "<tr>\n";
1809 foreach ($line as $col => $cell) :
1810 if (is_numeric($cell)) :
1811 $cell = date('j-M-y, h:i a', $cell);
1812 endif;
1813 $class = strtolower(preg_replace('/\s+/', '-', $col));
1814 $body .= "<td class=\"$class\">${cell}</td>";
1815 endforeach;
1816 $body .= "</tr>\n";
1817 endforeach;
1818
1819 $body .= "</tbody>\n</table>\n\n";
1820 endif;
1821 endforeach;
1822
1823 $body = apply_filters('feedwordpress_diagnostic_email_body', $body, $dlog);
1824 if (!is_null($body)) :
1825 $home = feedwordpress_display_url(get_bloginfo('url'));
1826 $subj = apply_filters('feedwordpress_diagnostic_email_subject', $home . " syndication issues", $dlog);
1827 $agent = 'FeedWordPress '.FEEDWORDPRESS_VERSION;
1828 $body = <<<EOMAIL
1829 <html>
1830 <head>
1831 <title>$subj</title>
1832 <style type="text/css">
1833 body { background-color: white; color: black; }
1834 table { width: 100%; border: 1px solid black; }
1835 table thead tr th { background-color: #ff7700; color: white; border-bottom: 1px solid black; }
1836 table thead tr { border-bottom: 1px solid black; }
1837 table tr { vertical-align: top; }
1838 table .since { width: 20%; }
1839 table .time { width: 20%; }
1840 table .most-recently { width: 20%; }
1841 table .message { width: auto; }
1842 </style>
1843 </head>
1844 <body>
1845 <h1>Syndication Issues encountered by $agent on $home</h1>
1846 $body
1847 </body>
1848 </html>
1849
1850 EOMAIL;
1851
1852 $ded = get_option('feedwordpress_diagnostics_email_destination', 'admins');
1853
1854 // e-mail address
1855 if (preg_match('/^mailto:(.*)$/', $ded, $ref)) :
1856 $recipients = array($ref[1]);
1857
1858 // userid
1859 elseif (preg_match('/^user:(.*)$/', $ded, $ref)) :
1860 $userdata = get_userdata((int) $ref[1]);
1861 $recipients = array($userdata->user_email);
1862
1863 // admins
1864 else :
1865 $recipients = FeedWordPressDiagnostic::admin_emails();
1866 endif;
1867
1868 foreach ($recipients as $email) :
1869 add_filter('wp_mail_content_type', array('FeedWordPress', 'allow_html_mail'));
1870 wp_mail($email, $subj, $body);
1871 remove_filter('wp_mail_content_type', array('FeedWordPress', 'allow_html_mail'));
1872 endforeach;
1873 endif;
1874
1875 // Clear the logs
1876 $dlog['mesg']['persistent'] = array();
1877 $dlog['mesg']['occurrent'] = array();
1878
1879 // Set schedule for next update
1880 $dlog['schedule']['last'] = time();
1881 endif;
1882 else :
1883 $dlog['schedule'] = array(
1884 'freq' => 24 /*hr*/ * 60 /*min*/ * 60 /*s*/,
1885 'last' => time(),
1886 );
1887 endif;
1888
1889 update_option('feedwordpress_diagnostics_log', $dlog);
1890 } /* FeedWordPress::email_diagnostic_log () */
1891
1892 function allow_html_mail () {
1893 return 'text/html';
1894 } /* FeedWordPress::allow_html_mail () */
1895
1896 function admin_footer () {
1897 global $feedwordpress_admin_footer;
1898 foreach ($feedwordpress_admin_footer as $line) :
1899 echo '<div><pre>'.$line.'</pre></div>';
1900 endforeach;
1901 } /* FeedWordPress::admin_footer () */
1902
1903 function log_prefix ($date = false) {
1904 $home = get_bloginfo('url');
1905 $prefix = '['.feedwordpress_display_url($home).'] [feedwordpress] ';
1906 if ($date) :
1907 $prefix = "[".date('Y-m-d H:i:s')."]".$prefix;
1908 endif;
1909 return $prefix;
1910 } /* FeedWordPress::log_prefix () */
1911
1912 function menu_cap ($sub = false) {
1913 if ($sub) :
1914 $cap = apply_filters('feedwordpress_menu_settings_capacity', 'manage_options');
1915 else :
1916 $cap = apply_filters('feedwordpress_menu_main_capacity', 'manage_links');
1917 endif;
1918 return $cap;
1919 } /* FeedWordPress::menu_cap () */
1920
1921 function path ($filename = '') {
1922 global $fwp_path;
1923
1924 $path = $fwp_path;
1925 if (strlen($filename) > 0) :
1926 $path .= '/'.$filename;
1927 endif;
1928 return $path;
1929 }
1930
1931 function param ($key, $type = 'REQUEST', $default = NULL) {
1932 $where = '_'.strtoupper($type);
1933 $ret = $default;
1934 if (isset($GLOBALS[$where]) and is_array($GLOBALS[$where])) :
1935 if (isset($GLOBALS[$where][$key])) :
1936 $ret = $GLOBALS[$where][$key];
1937 if (get_magic_quotes_gpc()) :
1938 $ret = stripslashes_deep($ret);
1939 endif;
1940 endif;
1941 endif;
1942 return $ret;
1943 }
1944 function post ($key, $default = NULL) {
1945 return FeedWordPress::param($key, 'POST');
1946 }
1947 } // class FeedWordPress
1948
1949 $feedwordpress_admin_footer = array();
1950
1951 // take your best guess at the realname and e-mail, given a string
1952 define('FWP_REGEX_EMAIL_ADDY', '([^@"(<\s]+@[^"@(<\s]+\.[^"@(<\s]+)');
1953 define('FWP_REGEX_EMAIL_NAME', '("([^"]*)"|([^"<(]+\S))');
1954 define('FWP_REGEX_EMAIL_POSTFIX_NAME', '/^\s*'.FWP_REGEX_EMAIL_ADDY."\s+\(".FWP_REGEX_EMAIL_NAME.'\)\s*$/');
1955 define('FWP_REGEX_EMAIL_PREFIX_NAME', '/^\s*'.FWP_REGEX_EMAIL_NAME.'\s*<'.FWP_REGEX_EMAIL_ADDY.'>\s*$/');
1956 define('FWP_REGEX_EMAIL_JUST_ADDY', '/^\s*'.FWP_REGEX_EMAIL_ADDY.'\s*$/');
1957 define('FWP_REGEX_EMAIL_JUST_NAME', '/^\s*'.FWP_REGEX_EMAIL_NAME.'\s*$/');
1958
1959 function parse_email_with_realname ($email) {
1960 if (preg_match(FWP_REGEX_EMAIL_POSTFIX_NAME, $email, $matches)) :
1961 ($ret['name'] = $matches[3]) or ($ret['name'] = $matches[2]);
1962 $ret['email'] = $matches[1];
1963 elseif (preg_match(FWP_REGEX_EMAIL_PREFIX_NAME, $email, $matches)) :
1964 ($ret['name'] = $matches[2]) or ($ret['name'] = $matches[3]);
1965 $ret['email'] = $matches[4];
1966 elseif (preg_match(FWP_REGEX_EMAIL_JUST_ADDY, $email, $matches)) :
1967 $ret['name'] = NULL; $ret['email'] = $matches[1];
1968 elseif (preg_match(FWP_REGEX_EMAIL_JUST_NAME, $email, $matches)) :
1969 $ret['email'] = NULL;
1970 ($ret['name'] = $matches[2]) or ($ret['name'] = $matches[3]);
1971 else :
1972 $ret['name'] = NULL; $ret['email'] = NULL;
1973 endif;
1974 return $ret;
1975 }
1976
1977