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

1,927 lines 64.6 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.0706
7 Author: Charles Johnson
8 Author URI: http://radgeek.com/
9 License: GPL
10 */
11
12 /**
13 * @package FeedWordPress
14 * @version 2011.0706
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.0706');
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 update_option('feedwordpress_last_update_all', time());
987 endif;
988
989 do_action('feedwordpress_update', $uri);
990
991 if (is_null($crash_ts)) :
992 $crash_ts = $this->crash_ts();
993 endif;
994
995 // Randomize order for load balancing purposes
996 $feed_set = $this->feeds;
997 shuffle($feed_set);
998
999 $feed_set = apply_filters('feedwordpress_update_feeds', $feed_set, $uri);
1000
1001 // Loop through and check for new posts
1002 $delta = NULL;
1003 foreach ($feed_set as $feed) :
1004 if (!is_null($crash_ts) and (time() > $crash_ts)) : // Check whether we've exceeded the time limit
1005 break;
1006 endif;
1007
1008 $pinged_that = (is_null($uri) or ($uri=='*') or in_array($uri, array($feed->uri(), $feed->homepage())));
1009
1010 if (!is_null($uri)) : // A site-specific ping always updates
1011 $timely = true;
1012 else :
1013 $timely = $feed->stale();
1014 endif;
1015
1016 if ($pinged_that and is_null($delta)) : // If at least one feed was hit for updating...
1017 $delta = array('new' => 0, 'updated' => 0); // ... don't return error condition
1018 endif;
1019
1020 if ($pinged_that and $timely) :
1021 do_action('feedwordpress_check_feed', $feed->settings);
1022 $start_ts = time();
1023 $added = $feed->poll($crash_ts);
1024 do_action('feedwordpress_check_feed_complete', $feed->settings, $added, time() - $start_ts);
1025
1026 if (is_array($added)) : // Success
1027 if (isset($added['new'])) : $delta['new'] += $added['new']; endif;
1028 if (isset($added['updated'])) : $delta['updated'] += $added['updated']; endif;
1029 endif;
1030 endif;
1031 endforeach;
1032
1033 do_action('feedwordpress_update_complete', $delta);
1034
1035 return $delta;
1036 }
1037
1038 function crash_ts ($default = NULL) {
1039 $crash_dt = (int) get_option('feedwordpress_update_time_limit', 0);
1040 if ($crash_dt > 0) :
1041 $crash_ts = time() + $crash_dt;
1042 else :
1043 $crash_ts = $default;
1044 endif;
1045 return $crash_ts;
1046 }
1047
1048 function secret_key () {
1049 $secret = get_option('feedwordpress_secret_key', false);
1050 if (!$secret) : // Generate random key.
1051 $secret = substr(md5(uniqid(microtime())), 0, 6);
1052 update_option('feedwordpress_secret_key', $secret);
1053 endif;
1054 return $secret;
1055 }
1056
1057 function has_secret () {
1058 return (isset($_REQUEST['feedwordpress_key']) and ($_REQUEST['feedwordpress_key']==$this->secret_key()));
1059 }
1060
1061 function automatic_update_hook () {
1062 $hook = get_option('feedwordpress_automatic_updates', NULL);
1063 if ($this->has_secret() and isset($_REQUEST['automatic_update'])) : // For forced behavior in testing.
1064 $hook = $_REQUEST['automatic_update'];
1065 endif;
1066
1067 if (!is_null($hook)) :
1068 if ($hook != 'init') : // Constrain values.
1069 $hook = 'shutdown';
1070 endif;
1071 endif;
1072 return $hook;
1073 }
1074 function last_update_all () {
1075 $last = get_option('feedwordpress_last_update_all');
1076 if ($this->has_secret() and isset($_REQUEST['automatic_update']) and ((strlen($_REQUEST['automatic_update']) > 0))) :
1077 $last = 1; // A long, long time ago.
1078 endif;
1079 return $last;
1080 }
1081 function force_update_all () {
1082 return ($this->has_secret() and isset($_REQUEST['force_update_feeds']) and !!$_REQUEST['force_update_feeds']);
1083 }
1084
1085 function stale () {
1086 if (!is_null($this->automatic_update_hook())) :
1087 // Do our best to avoid possible simultaneous
1088 // updates by getting up-to-the-minute settings.
1089
1090 $last = $this->last_update_all();
1091
1092 // If we haven't updated all yet, give it a time window
1093 if (false === $last) :
1094 $ret = false;
1095 update_option('feedwordpress_last_update_all', time());
1096
1097 // Otherwise, check against freshness interval
1098 elseif (is_numeric($last)) : // Expect a timestamp
1099 $freshness = get_option('feedwordpress_freshness');
1100 if (false === $freshness) : // Use default
1101 $freshness = FEEDWORDPRESS_FRESHNESS_INTERVAL;
1102 endif;
1103 $ret = ( (time() - $last) > $freshness );
1104
1105 // This should never happen.
1106 else :
1107 FeedWordPress::critical_bug('FeedWordPress::stale::last', $last, __LINE__, __FILE__);
1108 endif;
1109
1110 else :
1111 $ret = false;
1112 endif;
1113 return $ret;
1114 } // FeedWordPress::stale()
1115
1116 function init () {
1117 $this->clear_cache_magic_url();
1118 $this->update_magic_url();
1119 } /* FeedWordPress::init() */
1120
1121 function dashboard_setup () {
1122 $see_it = FeedWordPress::menu_cap();
1123
1124 if (current_user_can($see_it)) :
1125 // Get the stylesheet
1126 wp_enqueue_style('feedwordpress-elements');
1127
1128 $widget_id = 'feedwordpress_dashboard';
1129 $widget_name = __('Syndicated Sources');
1130 $column = 'side';
1131 $priority = 'core';
1132
1133 // I would love to use wp_add_dashboard_widget() here and save
1134 // myself some trouble. But WP 3 does not yet have any way to
1135 // push a dashboard widget onto the side, or to give it a default
1136 // location.
1137 add_meta_box(
1138 /*id=*/ $widget_id,
1139 /*title=*/ $widget_name,
1140 /*callback=*/ array(&$this, 'dashboard'),
1141 /*page=*/ 'dashboard',
1142 /*context=*/ $column,
1143 /*priority=*/ $priority
1144 );
1145 /*control_callback= array(&$this, 'dashboard_control') */
1146
1147 // This is kind of rude, I know, but the dashboard widget isn't
1148 // worth much if users don't know that it exists, and I don't
1149 // know of any better way to reorder the boxen.
1150 //
1151 // Gleefully ripped off of codex.wordpress.org/Dashboard_Widgets_API
1152
1153 // Globalize the metaboxes array, this holds all the widgets for wp-admin
1154 global $wp_meta_boxes;
1155
1156 // Get the regular dashboard widgets array
1157 // (which has our new widget already but at the end)
1158
1159 $normal_dashboard = $wp_meta_boxes['dashboard'][$column][$priority];
1160
1161 // Backup and delete our new dashbaord widget from the end of the array
1162 if (isset($normal_dashboard[$widget_id])) :
1163 $backup = array();
1164 $backup[$widget_id] = $normal_dashboard[$widget_id];
1165 unset($normal_dashboard[$widget_id]);
1166
1167 // Merge the two arrays together so our widget is at the
1168 // beginning
1169 $sorted_dashboard = array_merge($backup, $normal_dashboard);
1170
1171 // Save the sorted array back into the original metaboxes
1172 $wp_meta_boxes['dashboard'][$column][$priority] = $sorted_dashboard;
1173 endif;
1174 endif;
1175 } /* FeedWordPress::dashboard_setup () */
1176
1177 function dashboard () {
1178 $syndicationPage = new FeedWordPressSyndicationPage(dirname(__FILE__).'/syndication.php');
1179 $syndicationPage->dashboard_box($syndicationPage);
1180 } /* FeedWordPress::dashboard () */
1181
1182 function update_magic_url () {
1183 global $wpdb;
1184
1185 // Explicit update request in the HTTP request (e.g. from a cron job)
1186 if ($this->update_requested()) :
1187 $this->update($this->update_requested_url());
1188
1189 if (FEEDWORDPRESS_DEBUG and count($wpdb->queries) > 0) :
1190 $mysqlTime = 0.0;
1191 $byTime = array();
1192 foreach ($wpdb->queries as $query) :
1193 $time = $query[1] * 1000000.0;
1194 $mysqlTime += $query[1];
1195 if (!isset($byTime[$time])) : $byTime[$time] = array(); endif;
1196 $byTime[$time][] = $query[0]. ' // STACK: ' . $query[2];
1197 endforeach;
1198 krsort($byTime);
1199
1200 foreach ($byTime as $time => $querySet) :
1201 foreach ($querySet as $query) :
1202 print "[".(sprintf('%4.4f', $time/1000.0)) . "ms] $query\n";
1203 endforeach;
1204 endforeach;
1205 echo $this->log_prefix()."$wpdb->num_queries queries. $mysqlTime seconds in MySQL. Total of "; timer_stop(1); print " seconds.";
1206 endif;
1207
1208 debug_out_feedwordpress_footer();
1209
1210 // Magic URL should return nothing but a 200 OK header packet
1211 // when successful.
1212 exit;
1213 endif;
1214 } /* FeedWordPress::magic_update_url () */
1215
1216 function clear_cache_magic_url () {
1217 if ($this->clear_cache_requested()) :
1218 $this->clear_cache();
1219 endif;
1220 } /* FeedWordPress::clear_cache_magic_url() */
1221
1222 function clear_cache_requested () {
1223 return (
1224 isset($_GET['clear_cache'])
1225 and $_GET['clear_cache']
1226 );
1227 } /* FeedWordPress::clear_cache_requested() */
1228
1229 function update_requested () {
1230 return (
1231 isset($_REQUEST['update_feedwordpress'])
1232 and $_REQUEST['update_feedwordpress']
1233 );
1234 } // FeedWordPress::update_requested()
1235
1236 function update_requested_url () {
1237 $ret = null;
1238
1239 if (($_REQUEST['update_feedwordpress']=='*')
1240 or (preg_match('|^http://.*|i', $_REQUEST['update_feedwordpress']))) :
1241 $ret = $_REQUEST['update_feedwordpress'];
1242 endif;
1243
1244 return $ret;
1245 } // FeedWordPress::update_requested_url()
1246
1247 function auto_update () {
1248 if ($this->stale()) :
1249 $this->update();
1250 endif;
1251 } /* FeedWordPress::auto_update () */
1252
1253 function find_link ($uri, $field = 'link_rss') {
1254 global $wpdb;
1255
1256 $unslashed = untrailingslashit($uri);
1257 $slashed = trailingslashit($uri);
1258 $link_id = $wpdb->get_var($wpdb->prepare("
1259 SELECT link_id FROM $wpdb->links WHERE $field IN ('%s', '%s')
1260 LIMIT 1", $unslashed, $slashed
1261 ));
1262
1263 return $link_id;
1264 } /* FeedWordPress::find_link () */
1265
1266 function syndicate_link ($name, $uri, $rss) {
1267 // Get the category ID#
1268 $cat_id = FeedWordPress::link_category_id();
1269 if (!is_wp_error($cat_id)) :
1270 $link_category = array($cat_id);
1271 else :
1272 $link_category = array();
1273 endif;
1274
1275 // WordPress gets cranky if there's no homepage URI
1276 if (!is_string($uri) or strlen($uri)<1) : $uri = $rss; endif;
1277
1278 // Check if this feed URL is already being syndicated.
1279 $link_id = wp_insert_link(array(
1280 "link_id" => FeedWordPress::find_link($rss), // insert if nothing was found; else update
1281 "link_rss" => $rss,
1282 "link_name" => $name,
1283 "link_url" => $uri,
1284 "link_category" => $link_category,
1285 "link_visible" => 'Y', // reactivate if inactivated
1286 ));
1287
1288 return $link_id;
1289 } /* function FeedWordPress::syndicate_link() */
1290
1291 /*static*/ function syndicated_status ($what, $default) {
1292 $ret = get_option("feedwordpress_syndicated_{$what}_status");
1293 if (!$ret) :
1294 $ret = $default;
1295 endif;
1296 return $ret;
1297 } /* FeedWordPress::syndicated_status() */
1298
1299 function on_unfamiliar ($what = 'author') {
1300 switch ($what) :
1301 case 'category' : $suffix = ':category'; break;
1302 case 'post_tag' : $suffix = ':post_tag'; break;
1303 default: $suffix = '';
1304 endswitch;
1305
1306 return get_option('feedwordpress_unfamiliar_'.$what, 'create'.$suffix);
1307 } // function FeedWordPress::on_unfamiliar()
1308
1309 function null_email_set () {
1310 $base = get_option('feedwordpress_null_email_set');
1311
1312 if ($base===false) :
1313 $ret = array('noreply@blogger.com'); // default
1314 else :
1315 $ret = array_map('strtolower',
1316 array_map('trim', explode("\n", $base)));
1317 endif;
1318 $ret = apply_filters('syndicated_item_author_null_email_set', $ret);
1319 return $ret;
1320
1321 } /* FeedWordPress::null_email_set () */
1322
1323 function is_null_email ($email) {
1324 $ret = in_array(strtolower(trim($email)), FeedWordPress::null_email_set());
1325 $ret = apply_filters('syndicated_item_author_is_null_email', $ret, $email);
1326 return $ret;
1327 } /* FeedWordPress::is_null_email () */
1328
1329 function use_aggregator_source_data () {
1330 $ret = get_option('feedwordpress_use_aggregator_source_data');
1331 return apply_filters('syndicated_post_use_aggregator_source_data', ($ret=='yes'));
1332 }
1333
1334 /**
1335 * FeedWordPress::munge_permalinks: check whether or not FeedWordPress
1336 * should rewrite permalinks for syndicated items to reflect their
1337 * original location.
1338 *
1339 * @return bool TRUE if FeedWordPress SHOULD rewrite permalinks; FALSE otherwise
1340 */
1341 /*static*/ function munge_permalinks () {
1342 return (get_option('feedwordpress_munge_permalink', /*default=*/ 'yes') != 'no');
1343 } /* FeedWordPress::munge_permalinks() */
1344
1345 function syndicated_links ($args = array()) {
1346 $contributors = FeedWordPress::link_category_id();
1347 if (!is_wp_error($contributors)) :
1348 $links = get_bookmarks(array_merge(
1349 array("category" => $contributors),
1350 $args
1351 ));
1352 else :
1353 $links = array();
1354 endif;
1355 return $links;
1356 } // function FeedWordPress::syndicated_links()
1357
1358 function link_category_id () {
1359 global $wpdb, $wp_db_version;
1360
1361 $cat_id = get_option('feedwordpress_cat_id');
1362
1363 // If we don't yet have the category ID stored, search by name
1364 if (!$cat_id) :
1365 $cat_id = FeedWordPressCompatibility::link_category_id(DEFAULT_SYNDICATION_CATEGORY);
1366
1367 if ($cat_id) :
1368 // We found it; let's stamp it.
1369 update_option('feedwordpress_cat_id', $cat_id);
1370 endif;
1371
1372 // If we *do* have the category ID stored, verify that it exists
1373 else :
1374 $cat_id = FeedWordPressCompatibility::link_category_id((int) $cat_id, 'cat_id');
1375 endif;
1376
1377 // If we could not find an appropriate link category,
1378 // make a new one for ourselves.
1379 if (!$cat_id) :
1380 $cat_id = FeedWordPressCompatibility::insert_link_category(DEFAULT_SYNDICATION_CATEGORY);
1381 if (!is_wp_error($cat_id)) :
1382 // Stamp it
1383 update_option('feedwordpress_cat_id', $cat_id);
1384 endif;
1385 endif;
1386
1387 return $cat_id;
1388 } // function FeedWordPress::link_category_id()
1389
1390 # Upgrades and maintenance...
1391 function needs_upgrade () {
1392 global $wpdb;
1393 $fwp_db_version = get_option('feedwordpress_version');
1394 $ret = false; // innocent until proven guilty
1395 if (!$fwp_db_version or $fwp_db_version < FEEDWORDPRESS_VERSION) :
1396 // This is an older version or a fresh install. Does it
1397 // require a database upgrade or database initialization?
1398 if ($fwp_db_version <= 0.96) :
1399 // Yes. Check to see whether this is a fresh install or an upgrade.
1400 $syn = $wpdb->get_col("
1401 SELECT post_id
1402 FROM $wpdb->postmeta
1403 WHERE meta_key = 'syndication_feed'
1404 ");
1405 if (count($syn) > 0) : // contains at least one syndicated post
1406 $ret = true;
1407 else : // fresh install; brand it as ours
1408 update_option('feedwordpress_version', FEEDWORDPRESS_VERSION);
1409 endif;
1410 elseif ($fwp_db_version < 2009.0707) :
1411 // We need to clear out any busted AJAX crap
1412 $wpdb->query("
1413 DELETE FROM $wpdb->usermeta
1414 WHERE LOCATE('feedwordpress', meta_key)
1415 AND LOCATE('box', meta_key);
1416 ");
1417
1418 update_option('feedwordpress_version', FEEDWORDPRESS_VERSION);
1419 elseif ($fwp_db_version < 2010.0814) :
1420 // Change in terminology.
1421 if (get_option('feedwordpress_unfamiliar_category', 'create')=='default') :
1422 update_option('feedwordpress_unfamiliar_category', 'null');
1423 endif;
1424 foreach (FeedWordPress::syndicated_links() as $link) :
1425 $sub = new SyndicatedLink($link);
1426
1427 $remap_uf = array(
1428 'default' => 'null',
1429 'filter' => 'null',
1430 'create' => 'create:category',
1431 'tag' => 'create:post_tag'
1432 );
1433 if (isset($sub->settings['unfamiliar category'])) :
1434 if ($sub->settings['unfamiliar category']=='filter') :
1435 $sub->settings['match/filter'] = array('category');
1436 endif;
1437 foreach ($remap_uf as $from => $to) :
1438 if ($sub->settings['unfamiliar category']==$from) :
1439 $sub->settings['unfamiliar category'] = $to;
1440 endif;
1441 endforeach;
1442 endif;
1443
1444 if (isset($sub->settings['add global categories'])) :
1445 $sub->settings['add/category'] = $sub->settings['add global categories'];
1446 unset($sub->settings['add global categories']);
1447 endif;
1448
1449 $sub->save_settings(/*reload=*/ true);
1450 endforeach;
1451 update_option('feedwordpress_version', FEEDWORDPRESS_VERSION);
1452 else :
1453 // No. Just brand it with the new version.
1454 update_option('feedwordpress_version', FEEDWORDPRESS_VERSION);
1455 endif;
1456 endif;
1457 return $ret;
1458 }
1459
1460 function upgrade_database ($from = NULL) {
1461 global $wpdb;
1462
1463 if (is_null($from) or $from <= 0.96) : $from = 0.96; endif;
1464
1465 switch ($from) :
1466 case 0.96:
1467 // Dropping legacy upgrade code. If anyone is still
1468 // using 0.96 and just now decided to upgrade, well, I'm
1469 // sorry about that. You'll just have to cope with a few
1470 // duplicate posts.
1471
1472 // Mark the upgrade as successful.
1473 update_option('feedwordpress_version', FEEDWORDPRESS_VERSION);
1474 endswitch;
1475 echo "<p>Upgrade complete. FeedWordPress is now ready to use again.</p>";
1476 } /* FeedWordPress::upgrade_database() */
1477
1478 function has_guid_index () {
1479 global $wpdb;
1480
1481 $found = false; // Guilty until proven innocent.
1482
1483 $results = $wpdb->get_results("
1484 SHOW INDEXES FROM {$wpdb->posts}
1485 ");
1486 if ($results) :
1487 foreach ($results as $index) :
1488 if (isset($index->Column_name)
1489 and ('guid' == $index->Column_name)) :
1490 $found = true;
1491 endif;
1492 endforeach;
1493 endif;
1494 return $found;
1495 } /* FeedWordPress::has_guid_index () */
1496
1497 function create_guid_index () {
1498 global $wpdb;
1499
1500 $wpdb->query("
1501 CREATE INDEX {$wpdb->posts}_guid_idx ON {$wpdb->posts}(guid)
1502 ");
1503 } /* FeedWordPress::create_guid_index () */
1504
1505 function remove_guid_index () {
1506 global $wpdb;
1507
1508 $wpdb->query("
1509 DROP INDEX {$wpdb->posts}_guid_idx ON {$wpdb->posts}
1510 ");
1511 }
1512
1513 /*static*/ function fetch_timeout () {
1514 return apply_filters(
1515 'feedwordpress_fetch_timeout',
1516 intval(get_option('feedwordpress_fetch_timeout', FEEDWORDPRESS_FETCH_TIMEOUT_DEFAULT))
1517 );
1518 }
1519
1520 /*static*/ function fetch ($url, $params = array()) {
1521 $force_feed = true; // Default
1522
1523 // 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' =>
1524 $timeout = FeedWordPress::fetch_timeout();
1525
1526 if (!is_array($params)) :
1527 $force_feed = $params;
1528 else : // Parameter array
1529 $args = shortcode_atts(array(
1530 'force_feed' => $force_feed,
1531 'timeout' => $timeout
1532 ), $params);
1533
1534 extract($args);
1535 endif;
1536 $timeout = intval($timeout);
1537
1538 $pie_class = apply_filters('feedwordpress_simplepie_class', 'SimplePie');
1539 $cache_class = apply_filters('feedwordpress_cache_class', 'WP_Feed_Cache');
1540 $file_class = apply_filters('feedwordpress_file_class', 'FeedWordPress_File');
1541 $parser_class = apply_filters('feedwordpress_parser_class', 'FeedWordPress_Parser');
1542
1543 $sniffer_class = apply_filters('feedwordpress_sniffer_class', 'FeedWordPress_Content_Type_Sniffer');
1544
1545 $feed = new $pie_class;
1546 $feed->set_feed_url($url);
1547 $feed->set_cache_class($cache_class);
1548 $feed->set_timeout($timeout);
1549
1550 $feed->set_content_type_sniffer_class($sniffer_class);
1551 $feed->set_file_class($file_class);
1552 $feed->set_parser_class($parser_class);
1553 $feed->force_feed($force_feed);
1554 $feed->set_cache_duration(FeedWordPress::cache_duration());
1555 $feed->init();
1556 $feed->handle_content_type();
1557
1558 if ($feed->error()) :
1559 $ret = new WP_Error('simplepie-error', $feed->error());
1560 else :
1561 $ret = $feed;
1562 endif;
1563 return $ret;
1564 } /* FeedWordPress::fetch () */
1565
1566 function clear_cache () {
1567 global $wpdb;
1568
1569 // Just in case, clear out any old MagpieRSS cache records.
1570 $magpies = $wpdb->query("
1571 DELETE FROM {$wpdb->options}
1572 WHERE option_name LIKE 'rss_%' AND LENGTH(option_name) > 32
1573 ");
1574
1575 // The WordPress SimplePie module stores its cached feeds as
1576 // transient records in the options table. The data itself is
1577 // stored in `_transient_feed_{md5 of url}` and the last-modified
1578 // timestamp in `_transient_feed_mod_{md5 of url}`. Timeouts for
1579 // these records are stored in `_transient_timeout_feed_{md5}`.
1580 // Since the md5 is always 32 characters in length, the
1581 // option_name is always over 32 characters.
1582 $simplepies = $wpdb->query("
1583 DELETE FROM {$wpdb->options}
1584 WHERE option_name LIKE '_transient%_feed_%' AND LENGTH(option_name) > 32
1585 ");
1586 $simplepies = (int) ($simplepies / 4); // Each transient has 4 rows: the data, the modified timestamp; and the timeouts for each
1587
1588 return ($magpies + $simplepies);
1589 } /* FeedWordPress::clear_cache () */
1590
1591 function cache_duration () {
1592 $duration = NULL;
1593 if (defined('FEEDWORDPRESS_CACHE_AGE')) :
1594 $duration = FEEDWORDPRESS_CACHE_AGE;
1595 endif;
1596 return $duration;
1597 }
1598 function cache_lifetime ($duration) {
1599 // Check for explicit setting of a lifetime duration
1600 if (defined('FEEDWORDPRESS_CACHE_LIFETIME')) :
1601 $duration = FEEDWORDPRESS_CACHE_LIFETIME;
1602
1603 // Fall back to the cache freshness duration
1604 elseif (defined('FEEDWORDPRESS_CACHE_AGE')) :
1605 $duration = FEEDWORDPRESS_CACHE_AGE;
1606 endif;
1607
1608 // Fall back to WordPress default
1609 return $duration;
1610 } /* FeedWordPress::cache_lifetime () */
1611
1612 # Utility functions for handling text settings
1613 function negative ($f, $setting) {
1614 $nego = array ('n', 'no', 'f', 'false');
1615 return (isset($f[$setting]) and in_array(strtolower($f[$setting]), $nego));
1616 }
1617
1618 function affirmative ($f, $setting) {
1619 $affirmo = array ('y', 'yes', 't', 'true', 1);
1620 return (isset($f[$setting]) and in_array(strtolower($f[$setting]), $affirmo));
1621 }
1622
1623 # Internal debugging functions
1624 function critical_bug ($varname, $var, $line, $file = NULL) {
1625 global $wp_version;
1626
1627 if (!is_null($file)) :
1628 $location = "line # ${line} of ".basename($file);
1629 else :
1630 $location = "line # ${line}";
1631 endif;
1632
1633 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>';
1634 print "\n<plaintext>";
1635 print "Triggered at ${location}\n";
1636 print "FeedWordPress: ".FEEDWORDPRESS_VERSION."\n";
1637 print "WordPress: {$wp_version}\n";
1638 print "PHP: ".phpversion()."\n";
1639 print "Error data: ";
1640 print $varname.": "; var_dump($var); echo "\n";
1641 die;
1642 }
1643
1644 function noncritical_bug ($varname, $var, $line, $file = NULL) {
1645 if (FEEDWORDPRESS_DEBUG) : // halt only when we are doing debugging
1646 FeedWordPress::critical_bug($varname, $var, $line, $file);
1647 endif;
1648 }
1649
1650 function val ($v, $no_newlines = false) {
1651 ob_start();
1652 var_dump($v);
1653 $out = ob_get_contents(); ob_end_clean();
1654
1655 if ($no_newlines) :
1656 $out = preg_replace('/\s+/', " ", $out);
1657 endif;
1658 return $out;
1659 } /* FeedWordPress::val () */
1660
1661 function diagnostic_on ($level) {
1662 $show = get_option('feedwordpress_diagnostics_show', array());
1663 return (in_array($level, $show));
1664 } /* FeedWordPress::diagnostic_on () */
1665
1666 function diagnostic ($level, $out, $persist = NULL, $since = NULL, $mostRecent = NULL) {
1667 global $feedwordpress_admin_footer;
1668
1669 $output = get_option('feedwordpress_diagnostics_output', array());
1670 $dlog = get_option('feedwordpress_diagnostics_log', array());
1671
1672 $diagnostic_nesting = count(explode(":", $level));
1673
1674 if (FeedWordPress::diagnostic_on($level)) :
1675 foreach ($output as $method) :
1676 switch ($method) :
1677 case 'echo' :
1678 if (!FeedWordPress::update_requested()) :
1679 echo "<div><pre><strong>Diag".str_repeat('====', $diagnostic_nesting-1).'|</strong> '.$out."</pre></div>";
1680 endif;
1681 break;
1682 case 'echo_in_cronjob' :
1683 if (FeedWordPress::update_requested()) :
1684 echo FeedWordPress::log_prefix()." ".$out."\n";
1685 endif;
1686 break;
1687 case 'admin_footer' :
1688 $feedwordpress_admin_footer[] = $out;
1689 break;
1690 case 'error_log' :
1691 error_log(FeedWordPress::log_prefix().' '.$out);
1692 break;
1693 case 'email' :
1694 if (is_null($persist)) :
1695 $sect = 'occurrent';
1696 $hook = (isset($dlog['mesg'][$sect]) ? count($dlog['mesg'][$sect]) : 0);
1697 $line = array("Time" => time(), "Message" => $out);
1698 else :
1699 $sect = 'persistent';
1700 $hook = md5($level."\n".$persist);
1701 $line = array("Since" => $since, "Message" => $out, "Most Recent" => $mostRecent);
1702 endif;
1703
1704 if (!isset($dlog['mesg'])) : $dlog['mesg'] = array(); endif;
1705 if (!isset($dlog['mesg'][$sect])) : $dlog['mesg'][$sect] = array(); endif;
1706
1707 $dlog['mesg'][$sect][$hook] = $line;
1708 endswitch;
1709 endforeach;
1710 endif;
1711
1712 update_option('feedwordpress_diagnostics_log', $dlog);
1713 } /* FeedWordPress::diagnostic () */
1714
1715 function email_diagnostics_override () {
1716 return ($this->has_secret() and isset($_REQUEST['feedwordpress_email_diagnostics']) and !!$_REQUEST['feedwordpress_email_diagnostics']);
1717 }
1718 function has_emailed_diagnostics ($dlog) {
1719 $ret = false;
1720 if ($this->email_diagnostics_override()
1721 or (isset($dlog['schedule']) and isset($dlog['schedule']['last']))) :
1722 $ret = true;
1723 endif;
1724 return $ret;
1725 }
1726 function ready_to_email_diagnostics ($dlog) {
1727 $ret = false;
1728 if ($this->email_diagnostics_override()
1729 or (time() > ($dlog['schedule']['last'] + $dlog['schedule']['freq']))) :
1730 $ret = true;
1731 endif;
1732 return $ret;
1733 }
1734
1735 function email_diagnostic_log () {
1736 $dlog = get_option('feedwordpress_diagnostics_log', array());
1737
1738 if ($this->has_emailed_diagnostics($dlog)) :
1739 if ($this->ready_to_email_diagnostics($dlog)) :
1740 // No news is good news; only send if
1741 // there are some messages to send.
1742 $body = NULL;
1743 foreach ($dlog['mesg'] as $sect => $mesgs) :
1744 if (count($mesgs) > 0) :
1745 if (is_null($body)) : $body = ''; endif;
1746
1747 $paradigm = reset($mesgs);
1748 $body .= "<h2>".ucfirst($sect)." issues</h2>\n"
1749 ."<table>\n"
1750 ."<thead><tr>\n";
1751 foreach ($paradigm as $col => $value) :
1752 $body .= '<th scope="col">'.$col."</th>\n";
1753 endforeach;
1754 $body .= "</tr></thead>\n"
1755 ."<tbody>\n";
1756
1757 foreach ($mesgs as $line) :
1758 $body .= "<tr>\n";
1759 foreach ($line as $col => $cell) :
1760 if (is_numeric($cell)) :
1761 $cell = date('j-M-y, h:i a', $cell);
1762 endif;
1763 $class = strtolower(preg_replace('/\s+/', '-', $col));
1764 $body .= "<td class=\"$class\">${cell}</td>";
1765 endforeach;
1766 $body .= "</tr>\n";
1767 endforeach;
1768
1769 $body .= "</tbody>\n</table>\n\n";
1770 endif;
1771 endforeach;
1772
1773 $body = apply_filters('feedwordpress_diagnostic_email_body', $body, $dlog);
1774 if (!is_null($body)) :
1775 $home = feedwordpress_display_url(get_bloginfo('url'));
1776 $subj = apply_filters('feedwordpress_diagnostic_email_subject', $home . " syndication issues", $dlog);
1777 $agent = 'FeedWordPress '.FEEDWORDPRESS_VERSION;
1778 $body = <<<EOMAIL
1779 <html>
1780 <head>
1781 <title>$subj</title>
1782 <style type="text/css">
1783 body { background-color: white; color: black; }
1784 table { width: 100%; border: 1px solid black; }
1785 table thead tr th { background-color: #ff7700; color: white; border-bottom: 1px solid black; }
1786 table thead tr { border-bottom: 1px solid black; }
1787 table tr { vertical-align: top; }
1788 table .since { width: 20%; }
1789 table .time { width: 20%; }
1790 table .most-recently { width: 20%; }
1791 table .message { width: auto; }
1792 </style>
1793 </head>
1794 <body>
1795 <h1>Syndication Issues encountered by $agent on $home</h1>
1796 $body
1797 </body>
1798 </html>
1799
1800 EOMAIL;
1801
1802 $ded = get_option('feedwordpress_diagnostics_email_destination', 'admins');
1803
1804 // e-mail address
1805 if (preg_match('/^mailto:(.*)$/', $ded, $ref)) :
1806 $recipients = array($ref[1]);
1807
1808 // userid
1809 elseif (preg_match('/^user:(.*)$/', $ded, $ref)) :
1810 $userdata = get_userdata((int) $ref[1]);
1811 $recipients = array($userdata->user_email);
1812
1813 // admins
1814 else :
1815 $recipients = FeedWordPressDiagnostic::admin_emails();
1816 endif;
1817
1818 foreach ($recipients as $email) :
1819 add_filter('wp_mail_content_type', array('FeedWordPress', 'allow_html_mail'));
1820 wp_mail($email, $subj, $body);
1821 remove_filter('wp_mail_content_type', array('FeedWordPress', 'allow_html_mail'));
1822 endforeach;
1823 endif;
1824
1825 // Clear the logs
1826 $dlog['mesg']['persistent'] = array();
1827 $dlog['mesg']['occurrent'] = array();
1828
1829 // Set schedule for next update
1830 $dlog['schedule']['last'] = time();
1831 endif;
1832 else :
1833 $dlog['schedule'] = array(
1834 'freq' => 24 /*hr*/ * 60 /*min*/ * 60 /*s*/,
1835 'last' => time(),
1836 );
1837 endif;
1838
1839 update_option('feedwordpress_diagnostics_log', $dlog);
1840 } /* FeedWordPress::email_diagnostic_log () */
1841
1842 function allow_html_mail () {
1843 return 'text/html';
1844 } /* FeedWordPress::allow_html_mail () */
1845
1846 function admin_footer () {
1847 global $feedwordpress_admin_footer;
1848 foreach ($feedwordpress_admin_footer as $line) :
1849 echo '<div><pre>'.$line.'</pre></div>';
1850 endforeach;
1851 } /* FeedWordPress::admin_footer () */
1852
1853 function log_prefix ($date = false) {
1854 $home = get_bloginfo('url');
1855 $prefix = '['.feedwordpress_display_url($home).'] [feedwordpress] ';
1856 if ($date) :
1857 $prefix = "[".date('Y-m-d H:i:s')."]".$prefix;
1858 endif;
1859 return $prefix;
1860 } /* FeedWordPress::log_prefix () */
1861
1862 function menu_cap ($sub = false) {
1863 if ($sub) :
1864 $cap = apply_filters('feedwordpress_menu_settings_capacity', 'manage_options');
1865 else :
1866 $cap = apply_filters('feedwordpress_menu_main_capacity', 'manage_links');
1867 endif;
1868 return $cap;
1869 } /* FeedWordPress::menu_cap () */
1870
1871 function path ($filename = '') {
1872 global $fwp_path;
1873
1874 $path = $fwp_path;
1875 if (strlen($filename) > 0) :
1876 $path .= '/'.$filename;
1877 endif;
1878 return $path;
1879 }
1880
1881 function param ($key, $type = 'REQUEST', $default = NULL) {
1882 $where = '_'.strtoupper($type);
1883 $ret = $default;
1884 if (isset($GLOBALS[$where]) and is_array($GLOBALS[$where])) :
1885 if (isset($GLOBALS[$where][$key])) :
1886 $ret = $GLOBALS[$where][$key];
1887 if (get_magic_quotes_gpc()) :
1888 $ret = stripslashes_deep($ret);
1889 endif;
1890 endif;
1891 endif;
1892 return $ret;
1893 }
1894 function post ($key, $default = NULL) {
1895 return FeedWordPress::param($key, 'POST');
1896 }
1897 } // class FeedWordPress
1898
1899 $feedwordpress_admin_footer = array();
1900
1901 // take your best guess at the realname and e-mail, given a string
1902 define('FWP_REGEX_EMAIL_ADDY', '([^@"(<\s]+@[^"@(<\s]+\.[^"@(<\s]+)');
1903 define('FWP_REGEX_EMAIL_NAME', '("([^"]*)"|([^"<(]+\S))');
1904 define('FWP_REGEX_EMAIL_POSTFIX_NAME', '/^\s*'.FWP_REGEX_EMAIL_ADDY."\s+\(".FWP_REGEX_EMAIL_NAME.'\)\s*$/');
1905 define('FWP_REGEX_EMAIL_PREFIX_NAME', '/^\s*'.FWP_REGEX_EMAIL_NAME.'\s*<'.FWP_REGEX_EMAIL_ADDY.'>\s*$/');
1906 define('FWP_REGEX_EMAIL_JUST_ADDY', '/^\s*'.FWP_REGEX_EMAIL_ADDY.'\s*$/');
1907 define('FWP_REGEX_EMAIL_JUST_NAME', '/^\s*'.FWP_REGEX_EMAIL_NAME.'\s*$/');
1908
1909 function parse_email_with_realname ($email) {
1910 if (preg_match(FWP_REGEX_EMAIL_POSTFIX_NAME, $email, $matches)) :
1911 ($ret['name'] = $matches[3]) or ($ret['name'] = $matches[2]);
1912 $ret['email'] = $matches[1];
1913 elseif (preg_match(FWP_REGEX_EMAIL_PREFIX_NAME, $email, $matches)) :
1914 ($ret['name'] = $matches[2]) or ($ret['name'] = $matches[3]);
1915 $ret['email'] = $matches[4];
1916 elseif (preg_match(FWP_REGEX_EMAIL_JUST_ADDY, $email, $matches)) :
1917 $ret['name'] = NULL; $ret['email'] = $matches[1];
1918 elseif (preg_match(FWP_REGEX_EMAIL_JUST_NAME, $email, $matches)) :
1919 $ret['email'] = NULL;
1920 ($ret['name'] = $matches[2]) or ($ret['name'] = $matches[3]);
1921 else :
1922 $ret['name'] = NULL; $ret['email'] = NULL;
1923 endif;
1924 return $ret;
1925 }
1926
1927