PluginProbe
FeedWordPress / 2011.0211.2
FeedWordPress v2011.0211.2
trunk 0.8 0.9 0.91 0.95 0.96 0.97 0.98 0.981 0.99 0.991 0.992 0.993 2008.1030 2008.1101 2008.1105 2008.1214 2009.0612 2009.0613 2009.0618 2009.0707 2009.1111 2009.1112 2010.0127 2010.0528 All 65 releases
← All changes | feedwordpress.php +1402 -539 2009.11112011.0211.2 View file →
@@ -2,18 +2,24 @@
2 2 /*
3 3 Plugin Name: FeedWordPress
4 4 Plugin URI: http://feedwordpress.radgeek.com/
5 5 Description: simple and flexible Atom/RSS syndication for WordPress
6 -Version: 2009.1111
6 +Version: 2011.0211.2
7 7 Author: Charles Johnson
8 8 Author URI: http://radgeek.com/
9 9 License: GPL
10 10 */
11 11
12 +/**
13 + * @package FeedWordPress
14 + * @version 2011.0211.2
15 + */
16 +
12 17 # This uses code derived from:
13 18 # - wp-rss-aggregate.php by Kellan Elliot-McCrea <kellan@protest.net>
14 -# - HTTP Navigator 2 by Keyvan Minoukadeh <keyvan@k1m.com>
19 +# - MagpieRSS by Kellan Elliot-McCrea <kellan@protest.net>
15 20 # - Ultra-Liberal Feed Finder by Mark Pilgrim <mark@diveintomark.org>
21 +# - WordPress Blog Tool and Publishing Platform <http://wordpress.org/>
16 22 # according to the terms of the GNU General Public License.
17 23 #
18 24 # INSTALLATION: see readme.txt or <http://projects.radgeek.com/install>
19 25 #
@@ -27,11 +33,15 @@
27 33 # or use a cron job.
28 34
29 35 # -- Don't change these unless you know what you're doing...
30 36
31 -define ('FEEDWORDPRESS_VERSION', '2009.0803');
37 +define ('FEEDWORDPRESS_VERSION', '2011.0211.2');
32 38 define ('FEEDWORDPRESS_AUTHOR_CONTACT', 'http://radgeek.com/contact');
33 39
40 +if (!defined('FEEDWORDPRESS_BLEG')) :
41 + define ('FEEDWORDPRESS_BLEG', true);
42 +endif;
43 +
34 44 // Defaults
35 45 define ('DEFAULT_SYNDICATION_CATEGORY', 'Contributors');
36 46 define ('DEFAULT_UPDATE_PERIOD', 60); // value in minutes
37 47
@@ -52,8 +62,9 @@
52 62
53 63 define ('FEEDWORDPRESS_FRESHNESS_INTERVAL', 10*60); // Every ten minutes
54 64
55 65 define ('FWP_SCHEMA_HAS_USERMETA', 2966);
66 +define ('FWP_SCHEMA_USES_ARGS_TAXONOMY', 12694); // Revision # for using $args['taxonomy'] to get link categories
56 67 define ('FWP_SCHEMA_20', 3308); // Database schema # for WP 2.0
57 68 define ('FWP_SCHEMA_21', 4772); // Database schema # for WP 2.1
58 69 define ('FWP_SCHEMA_23', 5495); // Database schema # for WP 2.3
59 70 define ('FWP_SCHEMA_25', 7558); // Database schema # for WP 2.5
@@ -59,14 +70,15 @@
59 70 define ('FWP_SCHEMA_25', 7558); // Database schema # for WP 2.5
60 71 define ('FWP_SCHEMA_26', 8201); // Database schema # for WP 2.6
61 72 define ('FWP_SCHEMA_27', 9872); // Database schema # for WP 2.7
62 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
63 76
64 77 if (FEEDWORDPRESS_DEBUG) :
65 78 // Help us to pick out errors, if any.
66 79 ini_set('error_reporting', E_ALL & ~E_NOTICE);
67 80 ini_set('display_errors', true);
68 - define('MAGPIE_DEBUG', true);
69 81
70 82 // When testing we don't want cache issues to interfere. But this is
71 83 // a VERY BAD SETTING for a production server. Webmasters will eat your
72 84 // face for breakfast if you use it, and the baby Jesus will cry. So
@@ -71,48 +83,53 @@
71 83 // a VERY BAD SETTING for a production server. Webmasters will eat your
72 84 // face for breakfast if you use it, and the baby Jesus will cry. So
73 85 // make sure FEEDWORDPRESS_DEBUG is FALSE for any site that will be
74 86 // used for more than testing purposes!
75 - define('MAGPIE_CACHE_AGE', 1);
87 + define('FEEDWORDPRESS_CACHE_AGE', 1);
88 + define('FEEDWORDPRESS_CACHE_LIFETIME', 1);
89 + define('FEEDWORDPRESS_FETCH_TIMEOUT_DEFAULT', 60);
76 90 else :
77 - define('MAGPIE_DEBUG', false);
78 -
79 - define('MAGPIE_CACHE_AGE', 1*60);
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);
80 96 endif;
81 97
82 -// Note that the rss-functions.php that comes prepackaged with WordPress is
83 -// old & busted. For the new hotness, drop a copy of rss.php from
84 -// this archive into wp-includes/rss.php
98 +// Use our the cache settings that we want.
99 +add_filter('wp_feed_cache_transient_lifetime', array('FeedWordPress', 'cache_lifetime'));
85 100
86 -if (is_readable(ABSPATH . WPINC . '/rss.php')) :
87 - require_once (ABSPATH . WPINC . '/rss.php');
88 -else :
89 - require_once (ABSPATH . WPINC . '/rss-functions.php');
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');
90 105 endif;
106 +require_once(ABSPATH . WPINC . '/class-feed.php');
91 107
92 -if (isset($wp_db_version)) :
93 - if ($wp_db_version >= FWP_SCHEMA_23) :
94 - require_once (ABSPATH . WPINC . '/registration.php'); // for wp_insert_user
95 - elseif ($wp_db_version >= FWP_SCHEMA_21) : // WordPress 2.1 and 2.2, but not 2.3
96 - require_once (ABSPATH . WPINC . '/registration.php'); // for wp_insert_user
97 - require_once (ABSPATH . 'wp-admin/admin-db.php'); // for wp_insert_category
98 - elseif ($wp_db_version >= FWP_SCHEMA_20) : // WordPress 2.0
99 - require_once (ABSPATH . WPINC . '/registration-functions.php'); // for wp_insert_user
100 - require_once (ABSPATH . 'wp-admin/admin-db.php'); // for wp_insert_category
101 - endif;
108 +if (!function_exists('wp_insert_user')) :
109 + require_once (ABSPATH . WPINC . '/registration.php'); // for wp_insert_user
102 110 endif;
103 111
112 +require_once(dirname(__FILE__) . '/admin-ui.php');
113 +require_once(dirname(__FILE__) . '/feedwordpresssyndicationpage.class.php');
104 114 require_once(dirname(__FILE__) . '/compatability.php'); // LEGACY API: Replicate or mock up functions for legacy support purposes
115 +
116 +require_once(dirname(__FILE__) . '/syndicatedpost.class.php');
117 +require_once(dirname(__FILE__) . '/syndicatedlink.class.php');
105 118 require_once(dirname(__FILE__) . '/feedwordpresshtml.class.php');
119 +require_once(dirname(__FILE__) . '/feedwordpress-content-type-sniffer.class.php');
106 120
107 121 // Magic quotes are just about the stupidest thing ever.
108 122 if (is_array($_POST)) :
109 - $fwp_post = stripslashes_deep($_POST);
123 + $fwp_post = $_POST;
124 + if (get_magic_quotes_gpc()) :
125 + $fwp_post = stripslashes_deep($fwp_post);
126 + endif;
110 127 endif;
111 128
112 129 // Get the path relative to the plugins directory in which FWP is stored
113 130 preg_match (
114 - '|/wp-content/plugins/(.+)$|',
131 + '|'.preg_quote(WP_PLUGIN_DIR).'/(.+)$|',
115 132 dirname(__FILE__),
116 133 $ref
117 134 );
118 135
@@ -123,27 +140,21 @@
123 140 endif;
124 141
125 142 // If this is a FeedWordPress admin page, queue up scripts for AJAX functions that FWP uses
126 143 // If it is a display page or a non-FeedWordPress admin page, don't.
127 -if (is_admin() and isset($_REQUEST['page']) and preg_match("|^{$fwp_path}/|", $_REQUEST['page'])) :
128 - if (function_exists('wp_enqueue_script')) :
129 - if (isset($wp_db_version) and $wp_db_version >= FWP_SCHEMA_25) :
130 - wp_enqueue_script('post'); // for magic tag and category boxes
131 - wp_enqueue_script('thickbox'); // for fold-up boxes
132 - wp_enqueue_script('admin-forms'); // for checkbox selection
133 - else :
134 - wp_enqueue_script( 'ajaxcat' ); // Provides the handy-dandy new category text box
135 - endif;
136 - endif;
137 - if (function_exists('wp_enqueue_style')) :
138 - if (fwp_test_wp_version(FWP_SCHEMA_25)) :
139 - wp_enqueue_style('dashboard');
140 - endif;
141 - endif;
144 +wp_register_style('feedwordpress-elements', WP_PLUGIN_URL.'/'.$fwp_path.'/feedwordpress-elements.css');
145 +if (FeedWordPressSettingsUI::is_admin()) :
146 + // For JavaScript that needs to be generated dynamically
147 + add_action('admin_print_scripts', array('FeedWordPressSettingsUI', 'admin_scripts'));
148 +
149 + // For CSS that needs to be generated dynamically.
150 + add_action('admin_print_styles', array('FeedWordPressSettingsUI', 'admin_styles'));
151 +
152 + wp_enqueue_style('dashboard');
153 + wp_enqueue_style('feedwordpress-elements');
154 +
142 155 if (function_exists('wp_admin_css')) :
143 - if (fwp_test_wp_version(FWP_SCHEMA_25)) :
144 - wp_admin_css('css/dashboard');
145 - endif;
156 + wp_admin_css('css/dashboard');
146 157 endif;
147 158 endif;
148 159
149 160 if (!FeedWordPress::needs_upgrade()) : // only work if the conditions are safe!
@@ -166,10 +177,17 @@
166 177
167 178 add_action('atom_entry', 'feedwordpress_item_feed_data');
168 179
169 180 # Filter in original permalinks if the user wants that
170 - add_filter('post_link', 'syndication_permalink', 1);
181 + add_filter('post_link', 'syndication_permalink', /*priority=*/ 1, /*arguments=*/ 3);
171 182
183 + # When foreign URLs are used for permalinks in feeds or display
184 + # contexts, they need to be escaped properly.
185 + add_filter('the_permalink', 'syndication_permalink_escaped');
186 + add_filter('the_permalink_rss', 'syndication_permalink_escaped');
187 +
188 + add_filter('post_comments_feed_link', 'syndication_comments_feed_link');
189 +
172 190 # WTF? By default, wp_insert_link runs incoming link_url and link_rss
173 191 # URIs through default filters that include `wp_kses()`. But `wp_kses()`
174 192 # just happens to escape any occurrence of & to &amp; -- which just
175 193 # happens to fuck up any URI with a & to separate GET parameters.
@@ -178,14 +196,17 @@
178 196
179 197 # Admin menu
180 198 add_action('admin_menu', 'fwp_add_pages');
181 199 add_action('admin_notices', 'fwp_check_debug');
182 - add_action('admin_notices', 'fwp_check_magpie');
183 - add_action('init', 'feedwordpress_check_for_magpie_fix');
184 200
201 + add_action('admin_menu', 'feedwordpress_add_post_edit_controls');
202 + add_action('save_post', 'feedwordpress_save_post_edit_controls');
203 +
204 + add_action('admin_footer', array('FeedWordPress', 'admin_footer'));
205 +
185 206 # Inbound XML-RPC update methods
186 - add_filter('xmlrpc_methods', 'feedwordpress_xmlrpc_hook');
187 -
207 + $feedwordpressRPC = new FeedWordPressRPC;
208 +
188 209 # Outbound XML-RPC ping reform
189 210 remove_action('publish_post', 'generic_ping'); // WP 1.5.x
190 211 remove_action('do_pings', 'do_all_pings', 10, 1); // WP 2.1, 2.2
191 212 remove_action('publish_post', '_publish_post_hook', 5, 1); // WP 2.3
@@ -194,25 +215,14 @@
194 215 add_action('do_pings', 'fwp_do_pings', 10, 1);
195 216 add_action('feedwordpress_update', 'fwp_hold_pings');
196 217 add_action('feedwordpress_update_complete', 'fwp_release_pings');
197 218
198 - # Hook in logging functions only if the logging option is ON
199 - $update_logging = get_option('feedwordpress_update_logging');
200 - if ($update_logging == 'yes') :
201 - add_action('post_syndicated_item', 'log_feedwordpress_post', 100);
202 - add_action('update_syndicated_item', 'log_feedwordpress_update_post', 100);
203 - add_action('feedwordpress_update', 'log_feedwordpress_update_feeds', 100);
204 - add_action('feedwordpress_check_feed', 'log_feedwordpress_check_feed', 100);
205 - add_action('feedwordpress_update_complete', 'log_feedwordpress_update_complete', 100);
206 - endif;
219 + add_action('syndicated_feed_error', array('FeedWordPressDiagnostic', 'feed_error'), 100, 3);
220 +
221 + add_action('wp_footer', 'debug_out_feedwordpress_footer', -100);
222 + add_action('admin_footer', 'debug_out_feedwordpress_footer', -100);
207 223
208 - if (FeedWordPress::update_requested() and FEEDWORDPRESS_DEBUG) :
209 - add_action('post_syndicated_item', 'debug_out_feedwordpress_post', 100);
210 - add_action('update_syndicated_item', 'debug_out_feedwordpress_update_post', 100);
211 - add_action('feedwordpress_update', 'debug_out_feedwordpress_update_feeds', 100);
212 - add_action('feedwordpress_check_feed', 'debug_out_feedwordpress_check_feed', 100);
213 - add_action('feedwordpress_update_complete', 'debug_out_feedwordpress_update_complete', 100);
214 - endif;
224 + $feedwordpress = new FeedWordPress;
215 225
216 226 # Cron-less auto-update. Hooray!
217 227 $autoUpdateHook = get_option('feedwordpress_automatic_updates');
218 228 if ($autoUpdateHook != 'init') :
@@ -217,12 +227,13 @@
217 227 $autoUpdateHook = get_option('feedwordpress_automatic_updates');
218 228 if ($autoUpdateHook != 'init') :
219 229 $autoUpdateHook = 'shutdown';
220 230 endif;
221 - add_action($autoUpdateHook, 'feedwordpress_auto_update');
231 + add_action($autoUpdateHook, array(&$feedwordpress, 'auto_update'));
232 + add_action('init', array(&$feedwordpress, 'init'));
233 + add_action('shutdown', array(&$feedwordpress, 'email_diagnostic_log'));
234 + add_action('wp_dashboard_setup', array(&$feedwordpress, 'dashboard_setup'));
222 235
223 - add_action('init', 'feedwordpress_update_magic_url');
224 -
225 236 # Default sanitizers
226 237 add_filter('syndicated_item_content', array('SyndicatedPost', 'resolve_relative_uris'), 0, 2);
227 238 add_filter('syndicated_item_content', array('SyndicatedPost', 'sanitize_content'), 0, 2);
228 239
@@ -230,166 +241,109 @@
230 241 # Hook in the menus, which will just point to the upgrade interface
231 242 add_action('admin_menu', 'fwp_add_pages');
232 243 endif; // if (!FeedWordPress::needs_upgrade())
233 244
234 -function feedwordpress_check_for_magpie_fix () {
235 - if (isset($_POST['action']) and $_POST['action']=='fix_magpie_version') :
236 - FeedWordPressCompatibility::validate_http_request(/*action=*/ 'feedwordpress_fix_magpie', /*capability=*/ 'edit_files');
237 -
238 - $back_to = $_SERVER['REQUEST_URI'];
239 - if (isset($_POST['ignore'])) :
240 - // kill error message by telling it we ignored the upgrade request for this version
241 - update_option('feedwordpress_magpie_ignored_upgrade_to', EXPECTED_MAGPIE_VERSION);
242 - $ret = 'ignored';
243 - elseif (isset($_POST['upgrade'])) :
244 - $source = dirname(__FILE__)."/MagpieRSS-upgrade/rss.php";
245 - $destination = ABSPATH . WPINC . '/rss.php';
246 - $success = @copy($source, $destination);
247 -
248 - // Copy over rss-functions.php, too, to avoid collisions
249 - // on pre-lapsarian versions of WordPress.
250 - if ($success) :
251 - $source = dirname(__FILE__)."/MagpieRSS-upgrade/rss-functions.php";
252 - $destination = ABSPATH . WPINC . '/rss-functions.php';
253 - $success = @copy($source, $destination);
254 - endif;
255 - $ret = (int) $success;
256 - endif;
257 -
258 - if (strpos($back_to, '?')===false) : $sep = '?';
259 - else : $sep = '&';
260 - endif;
261 -
262 - header("Location: {$back_to}{$sep}feedwordpress_magpie_fix=".$ret);
263 - exit;
264 - endif;
265 -} /* feedwordpress_check_for_magpie_fix() */
266 -
267 -function feedwordpress_auto_update () {
268 - if (FeedWordPress::stale()) :
269 - $feedwordpress =& new FeedWordPress;
270 - $feedwordpress->update();
271 - endif;
272 -} /* feedwordpress_auto_update () */
273 -
274 -function feedwordpress_update_magic_url () {
275 - // Explicit update request in the HTTP request (e.g. from a cron job)
276 - if (FeedWordPress::update_requested()) :
277 - $feedwordpress =& new FeedWordPress;
278 - $feedwordpress->update(FeedWordPress::update_requested_url());
279 -
280 - // Magic URL should return nothing but a 200 OK header packet
281 - // when successful.
282 - exit;
283 - endif;
284 -} /* feedwordpress_magic_update_url () */
285 -
286 245 ################################################################################
287 246 ## LOGGING FUNCTIONS: log status updates to error_log if you want it ###########
288 247 ################################################################################
289 248
290 -function log_feedwordpress_post ($id) {
291 - $post = wp_get_single_post($id);
292 - error_log("[".date('Y-m-d H:i:s')."][feedwordpress] posted "
293 - ."'{$post->post_title}' ({$post->post_date})");
294 -}
249 +class FeedWordPressDiagnostic {
250 + function feed_error ($error, $old, $link) {
251 + $wpError = $error['object'];
252 + $url = $link->uri();
253 +
254 + $mesgs = $wpError->get_error_messages();
255 + foreach ($mesgs as $mesg) :
256 + $mesg = esc_html($mesg);
257 + FeedWordPress::diagnostic(
258 + 'updated_feeds:errors',
259 + "Feed Error: [${url}] update returned error: $mesg"
260 + );
261 +
262 + $hours = get_option('feedwordpress_diagnostics_persistent_errors_hours', 2);
263 + $span = ($error['ts'] - $error['since']);
295 264
296 -function log_feedwordpress_update_post ($id) {
297 - $post = wp_get_single_post($id);
298 - error_log("[".date('Y-m-d H:i:s')."][feedwordpress] updated "
299 - ."'{$post->post_title}' ({$post->post_date})"
300 - ." (as of {$post->post_modified})");
301 -}
265 + if ($span >= ($hours * 60 * 60)) :
266 + $since = date('r', $error['since']);
267 + $mostRecent = date('r', $error['ts']);
268 + FeedWordPress::diagnostic(
269 + 'updated_feeds:errors:persistent',
270 + "Feed Update Error: [${url}] returning errors"
271 + ." since ${since}:<br/><code>$mesg</code>",
272 + $url, $error['since'], $error['ts']
273 + );
274 + endif;
275 + endforeach;
276 + }
302 277
303 -function log_feedwordpress_update_feeds ($uri) {
304 - error_log("[".date('Y-m-d H:i:s')."][feedwordpress] update('$uri')");
305 -}
278 + function admin_emails ($id = '') {
279 + $users = get_users_of_blog($id);
280 + $recipients = array();
281 + foreach ($users as $user) :
282 + $user_id = (isset($user->user_id) ? $user->user_id : $user->ID);
283 + $dude = new WP_User($user_id);
284 + if ($dude->has_cap('administrator')) :
285 + if ($dude->user_email) :
286 + $recipients[] = $dude->user_email;
287 + endif;
288 + endif;
289 + endforeach;
290 + return $recipients;
291 + }
292 +} /* class FeedWordPressDiagnostic */
306 293
307 -function log_feedwordpress_check_feed ($feed) {
308 - $uri = $feed['link/uri']; $name = $feed['link/name'];
309 - error_log("[".date('Y-m-d H:i:s')."][feedwordpress] Examining $name <$uri>");
294 +function debug_out_human_readable_bytes ($quantity) {
295 + $quantity = (int) $quantity;
296 + $magnitude = 'B';
297 + $orders = array('KB', 'MB', 'GB', 'TB');
298 + while (($quantity > (1024*100)) and (count($orders) > 0)) :
299 + $quantity = floor($quantity / 1024);
300 + $magnitude = array_shift($orders);
301 + endwhile;
302 + return "${quantity} ${magnitude}";
310 303 }
311 304
312 -function log_feedwordpress_update_complete ($delta) {
313 - $mesg = array();
314 - if (isset($delta['new'])) $mesg[] = 'added '.$delta['new'].' new posts';
315 - if (isset($delta['updated'])) $mesg[] = 'updated '.$delta['updated'].' existing posts';
316 - if (empty($mesg)) $mesg[] = 'nothing changed';
305 +function debug_out_feedwordpress_footer () {
306 + if (FeedWordPress::diagnostic_on('memory_usage')) :
307 + if (function_exists('memory_get_usage')) :
308 + FeedWordPress::diagnostic ('memory_usage', "Memory: Current usage: ".debug_out_human_readable_bytes(memory_get_usage()));
309 + endif;
310 + if (function_exists('memory_get_peak_usage')) :
311 + FeedWordPress::diagnostic ('memory_usage', "Memory: Peak usage: ".debug_out_human_readable_bytes(memory_get_peak_usage()));
312 + endif;
313 + endif;
314 +} /* debug_out_feedwordpress_footer() */
317 315
318 - error_log("[".date('Y-m-d H:i:s')."][feedwordpress] "
319 - .(is_null($delta) ? "Error: I don't syndicate that URI"
320 - : implode(' and ', $mesg)));
321 -}
322 -
323 -function debug_out_feedwordpress_post ($id) {
324 - $post = wp_get_single_post($id);
325 - print ("[".date('Y-m-d H:i:s')."][feedwordpress] posted "
326 - ."'{$post->post_title}' ({$post->post_date})\n");
327 -}
328 -
329 -function debug_out_feedwordpress_update_post ($id) {
330 - $post = wp_get_single_post($id);
331 - print ("[".date('Y-m-d H:i:s')."][feedwordpress] updated "
332 - ."'{$post->post_title}' ({$post->post_date})"
333 - ." (as of {$post->post_modified})\n");
334 -}
335 -
336 -function debug_out_feedwordpress_update_feeds ($uri) {
337 - print ("[".date('Y-m-d H:i:s')."][feedwordpress] update('$uri')\n");
338 -}
339 -
340 -function debug_out_feedwordpress_check_feed ($feed) {
341 - $uri = $feed['link/uri']; $name = $feed['link/name'];
342 - print ("[".date('Y-m-d H:i:s')."][feedwordpress] Examining $name <$uri>\n");
343 -}
344 -
345 -function debug_out_feedwordpress_update_complete ($delta) {
346 - $mesg = array();
347 - if (isset($delta['new'])) $mesg[] = 'added '.$delta['new'].' new posts';
348 - if (isset($delta['updated'])) $mesg[] = 'updated '.$delta['updated'].' existing posts';
349 - if (empty($mesg)) $mesg[] = 'nothing changed';
350 -
351 - print ("[".date('Y-m-d H:i:s')."][feedwordpress] "
352 - .(is_null($delta) ? "Error: I don't syndicate that URI"
353 - : implode(' and ', $mesg))."\n");
354 -}
355 -
356 316 ################################################################################
357 317 ## TEMPLATE API: functions to make your templates syndication-aware ############
358 318 ################################################################################
359 319
360 -function is_syndicated ($id = NULL) { return (strlen(get_syndication_feed_id($id)) > 0); }
320 +/**
321 + * is_syndicated: Tests whether the current post in a Loop context, or a post
322 + * given by ID number, was syndicated by FeedWordPress. Useful for templates
323 + * to determine whether or not to retrieve syndication-related meta-data in
324 + * displaying a post.
325 + *
326 + * @param int $id The post to check for syndicated status. Defaults to the current post in a Loop context.
327 + * @return bool TRUE if the post's meta-data indicates it was syndicated; FALSE otherwise
328 + */
329 +function is_syndicated ($id = NULL) {
330 + return (strlen(get_syndication_feed_id($id)) > 0);
331 +} /* function is_syndicated() */
361 332
362 -function get_syndication_source_link ($original = NULL, $id = NULL) {
363 - if (is_null($original)) : $original = FeedWordPress::use_aggregator_source_data();
364 - endif;
365 -
366 - if ($original) : $vals = get_post_custom_values('syndication_source_uri_original', $id);
367 - else : $vals = array();
368 - endif;
369 -
370 - if (count($vals) == 0) : $vals = get_post_custom_values('syndication_source_uri', $id);
371 - endif;
372 -
373 - if (count($vals) > 0) : $ret = $vals[0]; else : $ret = NULL; endif;
374 -
375 - return $ret;
376 -} /* function get_syndication_source_link() */
377 -
378 -function the_syndication_source_link ($original = NULL, $id = NULL) {
379 - echo get_syndication_source_link($original, $id);
380 -}
381 -
382 333 function feedwordpress_display_url ($url, $before = 60, $after = 0) {
383 334 $bits = parse_url($url);
384 335
385 336 // Strip out crufty subdomains
386 - $bits['host'] = preg_replace('/^www[0-9]*\./i', '', $bits['host']);
387 -
337 + if (isset($bits['host'])) :
338 + $bits['host'] = preg_replace('/^www[0-9]*\./i', '', $bits['host']);
339 + endif;
340 +
388 341 // Reassemble bit-by-bit with minimum of crufty elements
389 342 $url = (isset($bits['user'])?$bits['user'].'@':'')
390 343 .(isset($bits['host'])?$bits['host']:'')
391 344 .(isset($bits['path'])?$bits['path']:'')
345 + .(isset($uri_bits['port'])?':'.$uri_bits['port']:'')
392 346 .(isset($bits['query'])?'?'.$bits['query']:'');
393 347
394 348 if (strlen($url) > ($before+$after)) :
395 349 $url = substr($url, 0, $before).'…'.substr($url, 0 - $after, $after);
@@ -395,95 +349,85 @@
395 349 $url = substr($url, 0, $before).'…'.substr($url, 0 - $after, $after);
396 350 endif;
397 351
398 352 return $url;
399 -}
353 +} /* feedwordpress_display_url () */
400 354
401 -function get_syndication_source ($original = NULL, $id = NULL) {
355 +function get_syndication_source_property ($original, $id, $local, $remote = NULL) {
356 + $ret = NULL;
402 357 if (is_null($original)) :
403 358 $original = FeedWordPress::use_aggregator_source_data();
404 359 endif;
405 -
406 - if ($original) :
407 - $vals = get_post_custom_values('syndication_source_original', $id);
408 - else :
409 - $vals = array();
360 +
361 + if (is_null($remote)) :
362 + $remote = $local . '_original';
410 363 endif;
411 364
412 - if (count($vals) == 0) :
413 - $vals = get_post_custom_values('syndication_source', $id);
365 + $vals = ($original ? get_post_custom_values($remote, $id) : array());
366 + if (count($vals) < 1) :
367 + $vals = get_post_custom_values($local, $id);
414 368 endif;
415 369
416 370 if (count($vals) > 0) :
417 371 $ret = $vals[0];
418 - else :
419 - $ret = NULL;
420 372 endif;
373 + return $ret;
374 +} /* function get_syndication_source_property () */
421 375
422 - if (is_null($ret) or strlen(trim($ret)) == 0) :
423 - // Fall back to URL of blog
376 +function the_syndication_source_link ($original = NULL, $id = NULL) { echo get_syndication_source_link($original, $id); }
377 +function get_syndication_source_link ($original = NULL, $id = NULL) {
378 + return get_syndication_source_property($original, $id, 'syndication_source_uri');
379 +} /* function get_syndication_source_link() */
380 +
381 +function the_syndication_source ($original = NULL, $id = NULL) { echo get_syndication_source($original, $id); }
382 +function get_syndication_source ($original = NULL, $id = NULL) {
383 + $ret = get_syndication_source_property($original, $id, 'syndication_source');
384 + if (is_null($ret) or strlen(trim($ret)) == 0) : // Fall back to URL of blog
424 385 $ret = feedwordpress_display_url(get_syndication_source_link());
425 386 endif;
426 -
427 387 return $ret;
428 388 } /* function get_syndication_source() */
429 389
430 -function the_syndication_source ($original = NULL, $id = NULL) { echo get_syndication_source($original, $id); }
431 -
390 +function the_syndication_feed ($original = NULL, $id = NULL) { echo get_syndication_feed($original, $id); }
432 391 function get_syndication_feed ($original = NULL, $id = NULL) {
433 - if (is_null($original)) : $original = FeedWordPress::use_aggregator_source_data();
434 - endif;
435 -
436 - if ($original) : $vals = get_post_custom_values('syndication_feed_original', $id);
437 - else : $vals = array();
438 - endif;
439 -
440 - if (count($vals) == 0) : $vals = get_post_custom_values('syndication_feed', $id);
441 - endif;
442 -
443 - if (count($vals) > 0) : $ret = $vals[0]; else : $ret = NULL; endif;
444 -
445 - return $ret;
392 + return get_syndication_source_property($original, $id, 'syndication_feed');
446 393 } /* function get_syndication_feed() */
447 394
448 -function the_syndication_feed ($original = NULL, $id = NULL) { echo get_syndication_feed($original, $id); }
449 -
395 +function the_syndication_feed_guid ($original = NULL, $id = NULL) { echo get_syndication_feed_guid($original, $id); }
450 396 function get_syndication_feed_guid ($original = NULL, $id = NULL) {
451 - if (is_null($original)) : $original = FeedWordPress::use_aggregator_source_data();
397 + $ret = get_syndication_source_property($original, $id, 'syndication_source_id');
398 + if (is_null($ret) or strlen(trim($ret))==0) : // Fall back to URL of feed
399 + $ret = get_syndication_feed();
452 400 endif;
453 -
454 - if ($original) : $vals = get_post_custom_values('syndication_source_id_original', $id);
455 - else : $vals = array();
456 - endif;
457 -
458 - if (count($vals) == 0) : $vals = array(get_feed_meta('feed/id', $id));
459 - endif;
460 -
461 - if (count($vals) > 0) : $ret = $vals[0]; else : $ret = NULL; endif;
462 -
463 401 return $ret;
464 402 } /* function get_syndication_feed_guid () */
465 403
466 -function the_syndication_feed_guid ($original = NULL, $id = NULL) { echo get_syndication_feed_guid($original, $id); }
467 -
468 404 function get_syndication_feed_id ($id = NULL) { list($u) = get_post_custom_values('syndication_feed_id', $id); return $u; }
469 405 function the_syndication_feed_id ($id = NULL) { echo get_syndication_feed_id($id); }
470 406
471 407 $feedwordpress_linkcache = array (); // only load links from database once
408 +function get_syndication_feed_object ($id = NULL) {
409 + global $feedwordpress_linkcache;
472 410
473 -function get_feed_meta ($key, $id = NULL) {
474 - global $wpdb, $feedwordpress_linkcache;
411 + $link = NULL;
412 +
475 413 $feed_id = get_syndication_feed_id($id);
476 -
477 - $ret = NULL;
478 414 if (strlen($feed_id) > 0):
479 415 if (isset($feedwordpress_linkcache[$feed_id])) :
480 416 $link = $feedwordpress_linkcache[$feed_id];
481 417 else :
482 - $link =& new SyndicatedLink($feed_id);
418 + $link = new SyndicatedLink($feed_id);
483 419 $feedwordpress_linkcache[$feed_id] = $link;
484 420 endif;
421 + endif;
422 + return $link;
423 +}
485 424
425 +function get_feed_meta ($key, $id = NULL) {
426 + $ret = NULL;
427 +
428 + $link = get_syndication_feed_object($id);
429 + if (is_object($link) and isset($link->settings[$key])) :
486 430 $ret = $link->settings[$key];
487 431 endif;
488 432 return $ret;
489 433 } /* get_feed_meta() */
@@ -494,13 +438,55 @@
494 438 function the_syndication_permalink ($id = NULL) {
495 439 echo get_syndication_permalink($id);
496 440 }
497 441
442 +/**
443 + * get_local_permalink: returns a string containing the internal permalink
444 + * for a post (whether syndicated or not) on your local WordPress installation.
445 + * This may be useful if you want permalinks to point to the original source of
446 + * an article for most purposes, but want to retrieve a URL for the local
447 + * representation of the post for one or two limited purposes (for example,
448 + * linking to a comments page on your local aggregator site).
449 + *
450 + * @param $id The numerical ID of the post to get the permalink for. If empty,
451 + * defaults to the current post in a Loop context.
452 + * @return string The URL of the local permalink for this post.
453 + *
454 + * @uses get_permalink()
455 + * @global $feedwordpress_the_original_permalink
456 + *
457 + * @since 2010.0217
458 + */
459 +function get_local_permalink ($id = NULL) {
460 + global $feedwordpress_the_original_permalink;
461 +
462 + // get permalink, and thus activate filter and force global to be filled
463 + // with original URL.
464 + $url = get_permalink($id);
465 + return $feedwordpress_the_original_permalink;
466 +} /* get_local_permalink() */
467 +
468 +/**
469 + * the_original_permalink: displays the contents of get_original_permalink()
470 + *
471 + * @param $id The numerical ID of the post to get the permalink for. If empty,
472 + * defaults to the current post in a Loop context.
473 + *
474 + * @uses get_local_permalinks()
475 + * @uses apply_filters
476 + *
477 + * @since 2010.0217
478 + */
479 +function the_local_permalink ($id = NULL) {
480 + print apply_filters('the_permalink', get_local_permalink($id));
481 +} /* function the_local_permalink() */
482 +
498 483 ################################################################################
499 484 ## FILTERS: syndication-aware handling of post data for templates and feeds ####
500 485 ################################################################################
501 486
502 487 $feedwordpress_the_syndicated_content = NULL;
488 +$feedwordpress_the_original_permalink = NULL;
503 489
504 490 function feedwordpress_preserve_syndicated_content ($text) {
505 491 global $feedwordpress_the_syndicated_content;
506 492
@@ -530,16 +516,16 @@
530 516 // In a post context....
531 517 if (is_syndicated()) :
532 518 ?>
533 519 <source>
534 - <title><?php the_syndication_source(); ?></title>
535 - <link rel="alternate" type="text/html" href="<?php the_syndication_source_link(); ?>" />
536 - <link rel="self" href="<?php the_syndication_feed(); ?>" />
520 + <title><?php print htmlspecialchars(get_syndication_source()); ?></title>
521 + <link rel="alternate" type="text/html" href="<?php print htmlspecialchars(get_syndication_source_link()); ?>" />
522 + <link rel="self" href="<?php print htmlspecialchars(get_syndication_feed()); ?>" />
537 523 <?php
538 524 $id = get_syndication_feed_guid();
539 525 if (strlen($id) > 0) :
540 526 ?>
541 - <id><?php print $id; ?></id>
527 + <id><?php print htmlspecialchars($id); ?></id>
542 528 <?php
543 529 endif;
544 530 $updated = get_feed_meta('feed/updated');
545 531 if (strlen($updated) > 0) : ?>
@@ -551,37 +537,197 @@
551 537 <?php
552 538 endif;
553 539 }
554 540
555 -function syndication_permalink ($permalink = '') {
556 - if (get_option('feedwordpress_munge_permalink') != 'no'):
557 - $uri = get_syndication_permalink();
558 - return ((strlen($uri) > 0) ? $uri : $permalink);
559 - else:
560 - return $permalink;
541 +/**
542 + * syndication_permalink: Allow WordPress to use the original remote URL of
543 + * syndicated posts as their permalink. Can be turned on or off by by setting in
544 + * Syndication => Posts & Links. Saves the old internal permalink in a global
545 + * variable for later use.
546 + *
547 + * @param string $permalink The internal permalink
548 + * @return string The new permalink. Same as the old if the post is not
549 + * syndicated, or if FWP is set to use internal permalinks, or if the post
550 + * was syndicated, but didn't have a proper permalink recorded.
551 + *
552 + * @uses SyndicatedLink::setting()
553 + * @uses get_syndication_permalink()
554 + * @uses get_syndication_feed_object()
555 + * @uses url_to_postid()
556 + * @global $id
557 + * @global $feedwordpress_the_original_permalink
558 + */
559 +function syndication_permalink ($permalink = '', $post = null, $leavename = false) {
560 + global $id;
561 + global $feedwordpress_the_original_permalink;
562 +
563 + // Save the local permalink in case we need to retrieve it later.
564 + $feedwordpress_the_original_permalink = $permalink;
565 +
566 + if (is_object($post) and isset($post->ID) and !empty($post->ID)) :
567 + // Use the post ID we've been provided with.
568 + $postId = $post->ID;
569 + elseif (is_string($permalink) and strlen($permalink) > 0) :
570 + // Map this permalink to a post ID so we can get the correct
571 + // permalink even outside of the Post Loop. Props Björn.
572 + $postId = url_to_postid($permalink);
573 + else :
574 + // If the permalink string is empty but Post Loop context
575 + // provides an id.
576 + $postId = $id;
561 577 endif;
562 -} // function syndication_permalink ()
563 578
579 + $munge = false;
580 + $link = get_syndication_feed_object($postId);
581 + if (is_object($link)) :
582 + $munge = ($link->setting('munge permalink', 'munge_permalink', 'yes') != 'no');
583 + endif;
584 +
585 + if ($munge):
586 + $uri = get_syndication_permalink($postId);
587 + $permalink = ((strlen($uri) > 0) ? $uri : $permalink);
588 + endif;
589 + return $permalink;
590 +} /* function syndication_permalink () */
591 +
592 +/**
593 + * syndication_permalink_escaped: Escape XML special characters in syndicated
594 + * permalinks when used in feed contexts and HTML display contexts.
595 + *
596 + * @param string $permalink
597 + * @return string
598 + *
599 + * @uses is_syndicated()
600 + * @uses FeedWordPress::munge_permalinks()
601 + *
602 + */
603 +function syndication_permalink_escaped ($permalink) {
604 + /* FIXME: This should review link settings not just global settings */
605 + if (is_syndicated() and FeedWordPress::munge_permalinks()) :
606 + // This is a foreign link; WordPress can't vouch for its not
607 + // having any entities that need to be &-escaped. So we'll do
608 + // it here.
609 + $permalink = esc_html($permalink);
610 + endif;
611 + return $permalink;
612 +} /* function syndication_permalink_escaped() */
613 +
614 +/**
615 + * syndication_comments_feed_link: Escape XML special characters in comments
616 + * feed links
617 + *
618 + * @param string $link
619 + * @return string
620 + *
621 + * @uses is_syndicated()
622 + * @uses FeedWordPress::munge_permalinks()
623 + */
624 +function syndication_comments_feed_link ($link) {
625 + global $feedwordpress_the_original_permalink, $id;
626 +
627 + if (is_syndicated() and FeedWordPress::munge_permalinks()) :
628 + // If the source post provided a comment feed URL using
629 + // wfw:commentRss or atom:link/@rel="replies" we can make use of
630 + // that value here.
631 + $source = get_syndication_feed_object();
632 + $replacement = NULL;
633 + if ($source->setting('munge comments feed links', 'munge_comments_feed_links', 'yes') != 'no') :
634 + $commentFeeds = get_post_custom_values('wfw:commentRSS');
635 + if (
636 + is_array($commentFeeds)
637 + and (count($commentFeeds) > 0)
638 + and (strlen($commentFeeds[0]) > 0)
639 + ) :
640 + $replacement = $commentFeeds[0];
641 +
642 + // This is a foreign link; WordPress can't vouch for its not
643 + // having any entities that need to be &-escaped. So we'll do it
644 + // here.
645 + $replacement = esc_html($replacement);
646 + endif;
647 + endif;
648 +
649 + if (is_null($replacement)) :
650 + // Q: How can we get the proper feed format, since the
651 + // format is, stupidly, not passed to the filter?
652 + // A: Kludge kludge kludge kludge!
653 + $fancy_permalinks = ('' != get_option('permalink_structure'));
654 + if ($fancy_permalinks) :
655 + preg_match('|/feed(/([^/]+))?/?$|', $link, $ref);
656 +
657 + $format = (isset($ref[2]) ? $ref[2] : '');
658 + if (strlen($format) == 0) : $format = get_default_feed(); endif;
659 +
660 + $replacement = trailingslashit($feedwordpress_the_original_permalink) . 'feed';
661 + if ($format != get_default_feed()) :
662 + $replacement .= '/'.$format;
663 + endif;
664 + $replacement = user_trailingslashit($replacement, 'single_feed');
665 + else :
666 + // No fancy permalinks = no problem
667 + // WordPress doesn't call get_permalink() to
668 + // generate the comment feed URL, so the
669 + // comments feed link is never munged by FWP.
670 + endif;
671 + endif;
672 +
673 + if (!is_null($replacement)) : $link = $replacement; endif;
674 + endif;
675 + return $link;
676 +} /* function syndication_comments_feed_link() */
677 +
564 678 ################################################################################
565 679 ## ADMIN MENU ADD-ONS: register Dashboard management pages #####################
566 680 ################################################################################
567 681
568 682 function fwp_add_pages () {
569 - global $fwp_capability;
570 - global $fwp_path;
683 + $menu_cap = FeedWordPress::menu_cap();
684 + $settings_cap = FeedWordPress::menu_cap(/*sub=*/ true);
685 + $syndicationMenu = FeedWordPress::path('syndication.php');
686 +
687 + add_menu_page(
688 + 'Syndicated Sites', 'Syndication',
689 + $menu_cap,
690 + $syndicationMenu,
691 + NULL,
692 + WP_PLUGIN_URL.'/'.FeedWordPress::path('feedwordpress-tiny.png')
693 + );
571 694
572 - $menu = array('Syndicated Sites', 'Syndication', $fwp_capability['manage_links'], $fwp_path.'/syndication.php', NULL);
573 - if (fwp_test_wp_version(FWP_SCHEMA_27)) :
574 - // add icon parameter
575 - $menu[] = WP_PLUGIN_URL.'/'.$fwp_path.'/feedwordpress-tiny.png';
576 - endif;
695 + do_action('feedwordpress_admin_menu_pre_feeds', $menu_cap, $settings_cap);
696 + add_submenu_page(
697 + $syndicationMenu, 'Syndicated Feeds & Updates', 'Feeds & Updates',
698 + $settings_cap, FeedWordPress::path('feeds-page.php')
699 + );
577 700
578 - call_user_func_array('add_menu_page', $menu);
579 - add_submenu_page($fwp_path.'/syndication.php', 'Syndicated Feeds & Updates', 'Feeds & Updates', $fwp_capability['manage_options'], $fwp_path.'/feeds-page.php');
580 - add_submenu_page($fwp_path.'/syndication.php', 'Syndicated Posts & Links', 'Posts & Links', $fwp_capability['manage_options'], $fwp_path.'/posts-page.php');
581 - add_submenu_page($fwp_path.'/syndication.php', 'Syndicated Authors', 'Authors', $fwp_capability['manage_options'], $fwp_path.'/authors-page.php');
582 - add_submenu_page($fwp_path.'/syndication.php', 'Categories'.FEEDWORDPRESS_AND_TAGS, 'Categories'.FEEDWORDPRESS_AND_TAGS, $fwp_capability['manage_options'], $fwp_path.'/categories-page.php');
583 - add_submenu_page($fwp_path.'/syndication.php', 'FeedWordPress Back End', 'Back End', $fwp_capability['manage_options'], $fwp_path.'/backend-page.php');
701 + do_action('feedwordpress_admin_menu_pre_posts', $menu_cap, $settings_cap);
702 + add_submenu_page(
703 + $syndicationMenu, 'Syndicated Posts & Links', 'Posts & Links',
704 + $settings_cap, FeedWordPress::path('posts-page.php')
705 + );
706 +
707 + do_action('feedwordpress_admin_menu_pre_authors', $menu_cap, $settings_cap);
708 + add_submenu_page(
709 + $syndicationMenu, 'Syndicated Authors', 'Authors',
710 + $settings_cap, FeedWordPress::path('authors-page.php')
711 + );
712 +
713 + do_action('feedwordpress_admin_menu_pre_categories', $menu_cap, $settings_cap);
714 + add_submenu_page(
715 + $syndicationMenu, 'Categories'.FEEDWORDPRESS_AND_TAGS, 'Categories'.FEEDWORDPRESS_AND_TAGS,
716 + $settings_cap, FeedWordPress::path('categories-page.php')
717 + );
718 +
719 + do_action('feedwordpress_admin_menu_pre_performance', $menu_cap, $settings_cap);
720 + add_submenu_page(
721 + $syndicationMenu, 'FeedWordPress Performance', 'Performance',
722 + $settings_cap, FeedWordPress::path('performance-page.php')
723 + );
724 +
725 + do_action('feedwordpress_admin_menu_pre_diagnostics', $menu_cap, $settings_cap);
726 + add_submenu_page(
727 + $syndicationMenu, 'FeedWordPress Diagnostics', 'Diagnostics',
728 + $settings_cap, FeedWordPress::path('diagnostics-page.php')
729 + );
584 730 } /* function fwp_add_pages () */
585 731
586 732 function fwp_check_debug () {
587 733 // This is a horrible fucking kludge that I have to do because the
@@ -604,97 +750,8 @@
604 750 <?php
605 751 endif;
606 752 } /* function fwp_check_debug () */
607 753
608 -define('EXPECTED_MAGPIE_VERSION', '2009.0725');
609 -function fwp_check_magpie () {
610 - if (isset($_REQUEST['feedwordpress_magpie_fix'])) :
611 - if ($_REQUEST['feedwordpress_magpie_fix']=='ignored') :
612 -?>
613 -<div class="updated fade">
614 -<p>O.K., we'll ignore the problem for now. FeedWordPress will not display any
615 -more error messages.</p>
616 -</div>
617 -<?php
618 - elseif ((bool) $_REQUEST['feedwordpress_magpie_fix']) :
619 -?>
620 -<div class="updated fade">
621 -<p>Congratulations! Your MagpieRSS has been successfully upgraded to the version
622 -shipped with FeedWordPress.</p>
623 -</div>
624 -<?php
625 - else :
626 - $source = dirname(__FILE__)."/MagpieRSS-upgrade/rss.php";
627 - $destination = ABSPATH . WPINC . '/rss.php';
628 - $cmd = "cp '".htmlspecialchars(addslashes($source))."' '".htmlspecialchars(addslashes($destination))."'";
629 - $cmd = wordwrap($cmd, /*width=*/ 75, /*break=*/ " \\\n\t");
630 -
631 -?>
632 -<div class="error">
633 -<p><strong>FeedWordPress was unable to automatically upgrade your copy of MagpieRSS.</strong></p>
634 -<p>It's likely that you need to change the file permissions on <code><?php print htmlspecialchars($source); ?></code>
635 -to allow FeedWordPress to overwrite it.</p>
636 -<p><strong>To perform the upgrade manually,</strong> you can
637 -use a SFTP or FTP client to upload a copy of <code>rss.php</code> from the
638 -<code>MagpieRSS-upgrades/</code> directory of your FeedWordPress archive so that
639 -it overwrites <code><?php print htmlspecialchars($destination); ?></code>. Or,
640 -if your web host provides shell access, you can issue the following command from
641 -a command prompt to perform the upgrade:</p>
642 -<pre>
643 -<samp>$</samp> <kbd><?php print $cmd; ?></kbd>
644 -</pre>
645 -<p><strong>If you've fixed the file permissions,</strong> you can try the
646 -automatic upgrade again.</p>
647 -
648 -<?php feedwordpress_upgrade_old_and_busted_buttons(); ?>
649 -</div>
650 -<?php
651 - endif;
652 - else :
653 - $magpie_version = FeedWordPress::magpie_version();
654 -
655 - $ignored = get_option('feedwordpress_magpie_ignored_upgrade_to');
656 - if (EXPECTED_MAGPIE_VERSION != $magpie_version and EXPECTED_MAGPIE_VERSION != $ignored) :
657 - if (current_user_can('edit_files')) :
658 - $youAre = 'you are';
659 - $itIsRecommendedThatYou = 'It is <strong>strongly recommended</strong> that you';
660 - else :
661 - $youAre = 'this site is';
662 - $itIsRecommendedThatYou = 'You may want to contact the administrator of the site; it is <strong>strongly recommended</strong> that they';
663 - endif;
664 - print '<div class="error">';
665 -?>
666 -<p style="font-style: italic"><strong>FeedWordPress has detected that <?php print $youAre; ?> currently using a version of
667 -MagpieRSS other than the upgraded version that ships with this version of FeedWordPress.</strong></p>
668 -<ul>
669 -<li><strong>Currently running:</strong> MagpieRSS <?php print $magpie_version; ?></li>
670 -<li><strong>Version included with FeedWordPress <?php print FEEDWORDPRESS_VERSION; ?>:</strong> MagpieRSS <?php print EXPECTED_MAGPIE_VERSION; ?></li>
671 -</ul>
672 -<p><?php print $itIsRecommendedThatYou; ?> install the upgraded
673 -version of MagpieRSS supplied with FeedWordPress. The version of
674 -MagpieRSS that ships with WordPress is very old and buggy, and
675 -encounters a number of errors when trying to parse modern Atom
676 -and RSS feeds.</p>
677 -<?php
678 - feedwordpress_upgrade_old_and_busted_buttons();
679 - print '</div>';
680 - endif;
681 - endif;
682 -}
683 -
684 -function feedwordpress_upgrade_old_and_busted_buttons() {
685 - if (current_user_can('edit_files')) :
686 -?>
687 -<form action="" method="post"><div>
688 -<?php FeedWordPressCompatibility::stamp_nonce('feedwordpress_fix_magpie'); ?>
689 -<input type="hidden" name="action" value="fix_magpie_version" />
690 -<input class="button-secondary" type="submit" name="ignore" value="<?php _e('Ignore this problem'); ?>" />
691 -<input class="button-primary" type="submit" name="upgrade" value="<?php _e('Upgrade'); ?>" />
692 -</div></form>
693 -<?php
694 - endif;
695 -}
696 -
697 754 ################################################################################
698 755 ## fwp_hold_pings() and fwp_release_pings(): Outbound XML-RPC ping reform ####
699 756 ## ... 'coz it's rude to send 500 pings the first time your aggregator runs ####
700 757 ################################################################################
@@ -750,8 +807,67 @@
750 807 endif;
751 808 endif;
752 809 }
753 810
811 + function feedwordpress_add_post_edit_controls () {
812 + add_meta_box('feedwordpress-post-controls', __('Syndication'), 'feedwordpress_post_edit_controls', 'post', 'side', 'high');
813 + } // function FeedWordPress::postEditControls
814 +
815 + function feedwordpress_post_edit_controls () {
816 + global $post;
817 +
818 + $frozen_values = get_post_custom_values('_syndication_freeze_updates', $post->ID);
819 + $frozen_post = (count($frozen_values) > 0 and 'yes' == $frozen_values[0]);
820 +
821 + if (is_syndicated($post->ID)) :
822 + ?>
823 + <p>This is a syndicated post, which originally appeared at
824 + <cite><?php print esc_html(get_syndication_source(NULL, $post->ID)); ?></cite>.
825 + <a href="<?php print esc_html(get_syndication_permalink($post->ID)); ?>">View original post</a>.</p>
826 +
827 + <p><input type="hidden" name="feedwordpress_noncename" id="feedwordpress_noncename" value="<?php print wp_create_nonce(plugin_basename(__FILE__)); ?>" />
828 + <label><input type="checkbox" name="freeze_updates" value="yes" <?php if ($frozen_post) : ?>checked="checked"<?php endif; ?> /> <strong>Manual editing.</strong>
829 + If set, FeedWordPress will not overwrite the changes you make manually
830 + to this post, if the syndicated content is updated on the
831 + feed.</label></p>
832 + <?php
833 + else :
834 + ?>
835 + <p>This post was created locally at this website.</p>
836 + <?php
837 + endif;
838 + } // function feedwordpress_post_edit_controls () */
839 +
840 + function feedwordpress_save_post_edit_controls ( $post_id ) {
841 + global $post;
842 +
843 + if (!isset($_POST['feedwordpress_noncename']) or !wp_verify_nonce($_POST['feedwordpress_noncename'], plugin_basename(__FILE__))) :
844 + return $post_id;
845 + endif;
846 +
847 + // Verify if this is an auto save routine. If it is our form has
848 + // not been submitted, so we don't want to do anything.
849 + if ( defined('DOING_AUTOSAVE') and DOING_AUTOSAVE ) :
850 + return $post_id;
851 + endif;
852 +
853 + // Check permissions
854 + if ( !current_user_can( 'edit_'.$_POST['post_type'], $post_id) ) :
855 + return $post_id;
856 + endif;
857 +
858 + // OK, we're golden. Now let's save some data.
859 + if (isset($_POST['freeze_updates'])) :
860 + update_post_meta($post_id, '_syndication_freeze_updates', $_POST['freeze_updates']);
861 + $ret = $_POST['freeze_updates'];
862 + else :
863 + delete_post_meta($post_id, '_syndication_freeze_updates');
864 + $ret = NULL;
865 + endif;
866 +
867 + return $ret;
868 + } // function feedwordpress_save_edit_controls
869 +
754 870 ################################################################################
755 871 ## class FeedWordPress #########################################################
756 872 ################################################################################
757 873
@@ -795,9 +911,9 @@
795 911 function FeedWordPress () {
796 912 $this->feeds = array ();
797 913 $links = FeedWordPress::syndicated_links();
798 914 if ($links): foreach ($links as $link):
799 - $this->feeds[] =& new SyndicatedLink($link);
915 + $this->feeds[] = new SyndicatedLink($link);
800 916 endforeach; endif;
801 917 } // FeedWordPress::FeedWordPress ()
802 918
803 919 # function update (): polls for updates on one or more Contributor feeds
@@ -855,9 +971,9 @@
855 971 if (FeedWordPress::needs_upgrade()) : // Will make duplicate posts if we don't hold off
856 972 return NULL;
857 973 endif;
858 974
859 - if (!is_null($uri)) :
975 + if (!is_null($uri) and $uri != '*') :
860 976 $uri = trim($uri);
861 977 else : // Update all
862 978 update_option('feedwordpress_last_update_all', time());
863 979 endif;
@@ -864,20 +980,17 @@
864 980
865 981 do_action('feedwordpress_update', $uri);
866 982
867 983 if (is_null($crash_ts)) :
868 - $crash_dt = (int) get_option('feedwordpress_update_time_limit');
869 - if ($crash_dt > 0) :
870 - $crash_ts = time() + $crash_dt;
871 - else :
872 - $crash_ts = NULL;
873 - endif;
984 + $crash_ts = $this->crash_ts();
874 985 endif;
875 -
986 +
876 987 // Randomize order for load balancing purposes
877 988 $feed_set = $this->feeds;
878 989 shuffle($feed_set);
879 990
991 + $feed_set = apply_filters('feedwordpress_update_feeds', $feed_set, $uri);
992 +
880 993 // Loop through and check for new posts
881 994 $delta = NULL;
882 995 foreach ($feed_set as $feed) :
883 996 if (!is_null($crash_ts) and (time() > $crash_ts)) : // Check whether we've exceeded the time limit
@@ -891,9 +1004,9 @@
891 1004 else :
892 1005 $timely = $feed->stale();
893 1006 endif;
894 1007
895 - if ($pinged_that and is_null($delta)) : // If at least one feed was hit for updating...
1008 + if ($pinged_that and is_null($delta)) : // If at least one feed was hit for updating...
896 1009 $delta = array('new' => 0, 'updated' => 0); // ... don't return error condition
897 1010 endif;
898 1011
899 1012 if ($pinged_that and $timely) :
@@ -901,10 +1014,12 @@
901 1014 $start_ts = time();
902 1015 $added = $feed->poll($crash_ts);
903 1016 do_action('feedwordpress_check_feed_complete', $feed->settings, $added, time() - $start_ts);
904 1017
905 - if (isset($added['new'])) : $delta['new'] += $added['new']; endif;
906 - if (isset($added['updated'])) : $delta['updated'] += $added['updated']; endif;
1018 + if (is_array($added)) : // Success
1019 + if (isset($added['new'])) : $delta['new'] += $added['new']; endif;
1020 + if (isset($added['updated'])) : $delta['updated'] += $added['updated']; endif;
1021 + endif;
907 1022 endif;
908 1023 endforeach;
909 1024
910 1025 do_action('feedwordpress_update_complete', $delta);
@@ -911,8 +1026,18 @@
911 1026
912 1027 return $delta;
913 1028 }
914 1029
1030 + function crash_ts ($default = NULL) {
1031 + $crash_dt = (int) get_option('feedwordpress_update_time_limit', 0);
1032 + if ($crash_dt > 0) :
1033 + $crash_ts = time() + $crash_dt;
1034 + else :
1035 + $crash_ts = $default;
1036 + endif;
1037 + return $crash_ts;
1038 + }
1039 +
915 1040 function stale () {
916 1041 if (get_option('feedwordpress_automatic_updates')) :
917 1042 // Do our best to avoid possible simultaneous
918 1043 // updates by getting up-to-the-minute settings.
@@ -941,9 +1066,122 @@
941 1066 $ret = false;
942 1067 endif;
943 1068 return $ret;
944 1069 } // FeedWordPress::stale()
1070 +
1071 + function init () {
1072 + $this->clear_cache_magic_url();
1073 + $this->update_magic_url();
1074 + } /* FeedWordPress::init() */
945 1075
1076 + function dashboard_setup () {
1077 + $see_it = FeedWordPress::menu_cap();
1078 +
1079 + if (current_user_can($see_it)) :
1080 + // Get the stylesheet
1081 + wp_enqueue_style('feedwordpress-elements');
1082 +
1083 + $widget_id = 'feedwordpress_dashboard';
1084 + $widget_name = __('Syndicated Sources');
1085 + $column = 'side';
1086 + $priority = 'core';
1087 +
1088 + // I would love to use wp_add_dashboard_widget() here and save
1089 + // myself some trouble. But WP 3 does not yet have any way to
1090 + // push a dashboard widget onto the side, or to give it a default
1091 + // location.
1092 + add_meta_box(
1093 + /*id=*/ $widget_id,
1094 + /*title=*/ $widget_name,
1095 + /*callback=*/ array(&$this, 'dashboard'),
1096 + /*page=*/ 'dashboard',
1097 + /*context=*/ $column,
1098 + /*priority=*/ $priority
1099 + );
1100 + /*control_callback= array(&$this, 'dashboard_control') */
1101 +
1102 + // This is kind of rude, I know, but the dashboard widget isn't
1103 + // worth much if users don't know that it exists, and I don't
1104 + // know of any better way to reorder the boxen.
1105 + //
1106 + // Gleefully ripped off of codex.wordpress.org/Dashboard_Widgets_API
1107 +
1108 + // Globalize the metaboxes array, this holds all the widgets for wp-admin
1109 + global $wp_meta_boxes;
1110 +
1111 + // Get the regular dashboard widgets array
1112 + // (which has our new widget already but at the end)
1113 +
1114 + $normal_dashboard = $wp_meta_boxes['dashboard'][$column][$priority];
1115 +
1116 + // Backup and delete our new dashbaord widget from the end of the array
1117 + if (isset($normal_dashboard[$widget_id])) :
1118 + $backup = array();
1119 + $backup[$widget_id] = $normal_dashboard[$widget_id];
1120 + unset($normal_dashboard[$widget_id]);
1121 +
1122 + // Merge the two arrays together so our widget is at the
1123 + // beginning
1124 + $sorted_dashboard = array_merge($backup, $normal_dashboard);
1125 +
1126 + // Save the sorted array back into the original metaboxes
1127 + $wp_meta_boxes['dashboard'][$column][$priority] = $sorted_dashboard;
1128 + endif;
1129 + endif;
1130 + } /* FeedWordPress::dashboard_setup () */
1131 +
1132 + function dashboard () {
1133 + $syndicationPage = new FeedWordPressSyndicationPage(dirname(__FILE__).'/syndication.php');
1134 + $syndicationPage->dashboard_box($syndicationPage);
1135 + } /* FeedWordPress::dashboard () */
1136 +
1137 + function update_magic_url () {
1138 + global $wpdb;
1139 +
1140 + // Explicit update request in the HTTP request (e.g. from a cron job)
1141 + if ($this->update_requested()) :
1142 + $this->update($this->update_requested_url());
1143 +
1144 + if (FEEDWORDPRESS_DEBUG and count($wpdb->queries) > 0) :
1145 + $mysqlTime = 0.0;
1146 + $byTime = array();
1147 + foreach ($wpdb->queries as $query) :
1148 + $time = $query[1] * 1000000.0;
1149 + $mysqlTime += $query[1];
1150 + if (!isset($byTime[$time])) : $byTime[$time] = array(); endif;
1151 + $byTime[$time][] = $query[0]. ' // STACK: ' . $query[2];
1152 + endforeach;
1153 + krsort($byTime);
1154 +
1155 + foreach ($byTime as $time => $querySet) :
1156 + foreach ($querySet as $query) :
1157 + print "[".(sprintf('%4.4f', $time/1000.0)) . "ms] $query\n";
1158 + endforeach;
1159 + endforeach;
1160 + echo $this->log_prefix()."$wpdb->num_queries queries. $mysqlTime seconds in MySQL. Total of "; timer_stop(1); print " seconds.";
1161 + endif;
1162 +
1163 + debug_out_feedwordpress_footer();
1164 +
1165 + // Magic URL should return nothing but a 200 OK header packet
1166 + // when successful.
1167 + exit;
1168 + endif;
1169 + } /* FeedWordPress::magic_update_url () */
1170 +
1171 + function clear_cache_magic_url () {
1172 + if ($this->clear_cache_requested()) :
1173 + $this->clear_cache();
1174 + endif;
1175 + } /* FeedWordPress::clear_cache_magic_url() */
1176 +
1177 + function clear_cache_requested () {
1178 + return (
1179 + isset($_GET['clear_cache'])
1180 + and $_GET['clear_cache']
1181 + );
1182 + } /* FeedWordPress::clear_cache_requested() */
1183 +
946 1184 function update_requested () {
947 1185 return (
948 1186 isset($_REQUEST['update_feedwordpress'])
949 1187 and $_REQUEST['update_feedwordpress']
@@ -950,60 +1188,61 @@
950 1188 );
951 1189 } // FeedWordPress::update_requested()
952 1190
953 1191 function update_requested_url () {
954 - $ret = null;
955 -
956 - if (($_REQUEST['update_feedwordpress']=='*')
957 - or (preg_match('|^http://.*|i', $_REQUEST['update_feedwordpress']))) :
958 - $ret = $_REQUEST['update_feedwordpress'];
959 - endif;
1192 + $ret = null;
1193 +
1194 + if (($_REQUEST['update_feedwordpress']=='*')
1195 + or (preg_match('|^http://.*|i', $_REQUEST['update_feedwordpress']))) :
1196 + $ret = $_REQUEST['update_feedwordpress'];
1197 + endif;
960 1198
961 - return $ret;
1199 + return $ret;
962 1200 } // FeedWordPress::update_requested_url()
963 1201
964 - function syndicate_link ($name, $uri, $rss) {
1202 + function auto_update () {
1203 + if ($this->stale()) :
1204 + $this->update();
1205 + endif;
1206 + } /* FeedWordPress::auto_update () */
1207 +
1208 + function find_link ($uri, $field = 'link_rss') {
965 1209 global $wpdb;
966 1210
1211 + $unslashed = untrailingslashit($uri);
1212 + $slashed = trailingslashit($uri);
1213 + $link_id = $wpdb->get_var($wpdb->prepare("
1214 + SELECT link_id FROM $wpdb->links WHERE $field IN ('%s', '%s')
1215 + LIMIT 1", $unslashed, $slashed
1216 + ));
1217 +
1218 + return $link_id;
1219 + } /* FeedWordPress::find_link () */
1220 +
1221 + function syndicate_link ($name, $uri, $rss) {
967 1222 // Get the category ID#
968 1223 $cat_id = FeedWordPress::link_category_id();
969 -
1224 + if (!is_wp_error($cat_id)) :
1225 + $link_category = array($cat_id);
1226 + else :
1227 + $link_category = array();
1228 + endif;
1229 +
970 1230 // WordPress gets cranky if there's no homepage URI
971 - if (!isset($uri) or strlen($uri)<1) : $uri = $rss; endif;
1231 + if (!is_string($uri) or strlen($uri)<1) : $uri = $rss; endif;
972 1232
973 - if (function_exists('wp_insert_link')) : // WordPress 2.x
974 - if (FeedWordPressCompatibility::test_version(0, FWP_SCHEMA_21)) :
975 - // Morons.
976 - $name = $wpdb->escape($name);
977 - $uri = $wpdb->escape($uri);
978 - $rss = $wpdb->escape($rss);
979 -
980 - // Comes in as a single category
981 - $linkCats = $cat_id;
982 - else :
983 - // Comes in as an array of categories
984 - $linkCats = array($cat_id);
985 - endif;
1233 + // Check if this feed URL is already being syndicated.
1234 + $link_id = wp_insert_link(array(
1235 + "link_id" => FeedWordPress::find_link($rss), // insert if nothing was found; else update
1236 + "link_rss" => $rss,
1237 + "link_name" => $name,
1238 + "link_url" => $uri,
1239 + "link_category" => $link_category,
1240 + "link_visible" => 'Y', // reactivate if inactivated
1241 + ));
986 1242
987 - $link_id = wp_insert_link(array(
988 - "link_name" => $name,
989 - "link_url" => $uri,
990 - "link_category" => $linkCats,
991 - "link_rss" => $rss
992 - ));
993 - else : // WordPress 1.5.x
994 - $result = $wpdb->query("
995 - INSERT INTO $wpdb->links
996 - SET
997 - link_name = '".$wpdb->escape($name)."',
998 - link_url = '".$wpdb->escape($uri)."',
999 - link_category = '".$wpdb->escape($cat_id)."',
1000 - link_rss = '".$wpdb->escape($rss)."'
1001 - ");
1002 - $link_id = $wpdb->insert_id;
1003 - endif;
1004 1243 return $link_id;
1005 - } // function FeedWordPress::syndicate_link()
1244 + } /* function FeedWordPress::syndicate_link() */
1006 1245
1007 1246 /*static*/ function syndicated_status ($what, $default) {
1008 1247 $ret = get_option("feedwordpress_syndicated_{$what}_status");
1009 1248 if (!$ret) :
@@ -1011,26 +1250,16 @@
1011 1250 endif;
1012 1251 return $ret;
1013 1252 } /* FeedWordPress::syndicated_status() */
1014 1253
1015 - function on_unfamiliar ($what = 'author', $override = NULL) {
1016 - $set = array(
1017 - 'author' => array('create', 'default', 'filter'),
1018 - 'category' => array('create', 'tag', 'default', 'filter'),
1019 - );
1254 + function on_unfamiliar ($what = 'author') {
1255 + switch ($what) :
1256 + case 'category' : $suffix = ':category'; break;
1257 + case 'post_tag' : $suffix = ':post_tag'; break;
1258 + default: $suffix = '';
1259 + endswitch;
1020 1260
1021 - if (is_string($override)) :
1022 - $ret = strtolower($override);
1023 - endif;
1024 -
1025 - if (!is_numeric($override) and !in_array($ret, $set[$what])) :
1026 - $ret = get_option('feedwordpress_unfamiliar_'.$what);
1027 - if (!is_numeric($ret) and !in_array($ret, $set[$what])) :
1028 - $ret = 'create';
1029 - endif;
1030 - endif;
1031 -
1032 - return $ret;
1261 + return get_option('feedwordpress_unfamiliar_'.$what, 'create'.$suffix);
1033 1262 } // function FeedWordPress::on_unfamiliar()
1034 1263
1035 1264 function null_email_set () {
1036 1265 $base = get_option('feedwordpress_null_email_set');
@@ -1056,14 +1285,28 @@
1056 1285 $ret = get_option('feedwordpress_use_aggregator_source_data');
1057 1286 return apply_filters('syndicated_post_use_aggregator_source_data', ($ret=='yes'));
1058 1287 }
1059 1288
1060 - function syndicated_links () {
1289 + /**
1290 + * FeedWordPress::munge_permalinks: check whether or not FeedWordPress
1291 + * should rewrite permalinks for syndicated items to reflect their
1292 + * original location.
1293 + *
1294 + * @return bool TRUE if FeedWordPress SHOULD rewrite permalinks; FALSE otherwise
1295 + */
1296 + /*static*/ function munge_permalinks () {
1297 + return (get_option('feedwordpress_munge_permalink', /*default=*/ 'yes') != 'no');
1298 + } /* FeedWordPress::munge_permalinks() */
1299 +
1300 + function syndicated_links ($args = array()) {
1061 1301 $contributors = FeedWordPress::link_category_id();
1062 - if (function_exists('get_bookmarks')) :
1063 - $links = get_bookmarks(array("category" => $contributors));
1064 - else:
1065 - $links = get_linkobjects($contributors); // deprecated as of WP 2.1
1302 + if (!is_wp_error($contributors)) :
1303 + $links = get_bookmarks(array_merge(
1304 + array("category" => $contributors),
1305 + $args
1306 + ));
1307 + else :
1308 + $links = array();
1066 1309 endif;
1067 1310 return $links;
1068 1311 } // function FeedWordPress::syndicated_links()
1069 1312
@@ -1089,11 +1332,12 @@
1089 1332 // If we could not find an appropriate link category,
1090 1333 // make a new one for ourselves.
1091 1334 if (!$cat_id) :
1092 1335 $cat_id = FeedWordPressCompatibility::insert_link_category(DEFAULT_SYNDICATION_CATEGORY);
1093 -
1094 - // Stamp it
1095 - update_option('feedwordpress_cat_id', $cat_id);
1336 + if (!is_wp_error($cat_id)) :
1337 + // Stamp it
1338 + update_option('feedwordpress_cat_id', $cat_id);
1339 + endif;
1096 1340 endif;
1097 1341
1098 1342 return $cat_id;
1099 1343 } // function FeedWordPress::link_category_id()
@@ -1119,15 +1363,47 @@
1119 1363 update_option('feedwordpress_version', FEEDWORDPRESS_VERSION);
1120 1364 endif;
1121 1365 elseif ($fwp_db_version < 2009.0707) :
1122 1366 // We need to clear out any busted AJAX crap
1123 - if (fwp_test_wp_version(FWP_SCHEMA_HAS_USERMETA)) :
1124 - $wpdb->query("
1125 - DELETE FROM $wpdb->usermeta
1126 - WHERE LOCATE('feedwordpress', meta_key)
1127 - AND LOCATE('box', meta_key);
1128 - ");
1367 + $wpdb->query("
1368 + DELETE FROM $wpdb->usermeta
1369 + WHERE LOCATE('feedwordpress', meta_key)
1370 + AND LOCATE('box', meta_key);
1371 + ");
1372 +
1373 + update_option('feedwordpress_version', FEEDWORDPRESS_VERSION);
1374 + elseif ($fwp_db_version < 2010.0814) :
1375 + // Change in terminology.
1376 + if (get_option('feedwordpress_unfamiliar_category', 'create')=='default') :
1377 + update_option('feedwordpress_unfamiliar_category', 'null');
1129 1378 endif;
1379 + foreach (FeedWordPress::syndicated_links() as $link) :
1380 + $sub = new SyndicatedLink($link);
1381 +
1382 + $remap_uf = array(
1383 + 'default' => 'null',
1384 + 'filter' => 'null',
1385 + 'create' => 'create:category',
1386 + 'tag' => 'create:post_tag'
1387 + );
1388 + if (isset($sub->settings['unfamiliar category'])) :
1389 + if ($sub->settings['unfamiliar category']=='filter') :
1390 + $sub->settings['match/filter'] = array('category');
1391 + endif;
1392 + foreach ($remap_uf as $from => $to) :
1393 + if ($sub->settings['unfamiliar category']==$from) :
1394 + $sub->settings['unfamiliar category'] = $to;
1395 + endif;
1396 + endforeach;
1397 + endif;
1398 +
1399 + if (isset($sub->settings['add global categories'])) :
1400 + $sub->settings['add/category'] = $sub->settings['add global categories'];
1401 + unset($sub->settings['add global categories']);
1402 + endif;
1403 +
1404 + $sub->save_settings(/*reload=*/ true);
1405 + endforeach;
1130 1406 update_option('feedwordpress_version', FEEDWORDPRESS_VERSION);
1131 1407 else :
1132 1408 // No. Just brand it with the new version.
1133 1409 update_option('feedwordpress_version', FEEDWORDPRESS_VERSION);
@@ -1141,58 +1417,14 @@
1141 1417
1142 1418 if (is_null($from) or $from <= 0.96) : $from = 0.96; endif;
1143 1419
1144 1420 switch ($from) :
1145 - case 0.96: // account for changes to syndication custom values and guid
1146 - echo "<p>Upgrading database from {$from} to ".FEEDWORDPRESS_VERSION."...</p>\n";
1421 + case 0.96:
1422 + // Dropping legacy upgrade code. If anyone is still
1423 + // using 0.96 and just now decided to upgrade, well, I'm
1424 + // sorry about that. You'll just have to cope with a few
1425 + // duplicate posts.
1147 1426
1148 - $cat_id = FeedWordPress::link_category_id();
1149 -
1150 - // Avoid duplicates
1151 - $wpdb->query("DELETE FROM `{$wpdb->postmeta}` WHERE meta_key = 'syndication_feed_id'");
1152 -
1153 - // Look up all the link IDs
1154 - $wpdb->query("
1155 - CREATE TEMPORARY TABLE tmp_custom_values
1156 - SELECT
1157 - NULL AS meta_id,
1158 - post_id,
1159 - 'syndication_feed_id' AS meta_key,
1160 - link_id AS meta_value
1161 - FROM `{$wpdb->postmeta}`, `{$wpdb->links}`
1162 - WHERE
1163 - meta_key='syndication_feed'
1164 - AND meta_value=link_rss
1165 - AND link_category = {$cat_id}
1166 - ");
1167 -
1168 - // Now attach them to their posts
1169 - $wpdb->query("INSERT INTO `{$wpdb->postmeta}` SELECT * FROM tmp_custom_values");
1170 -
1171 - // And clean up after ourselves.
1172 - $wpdb->query("DROP TABLE tmp_custom_values");
1173 -
1174 - // Now fix the guids to avoid duplicate posts
1175 - echo "<ul>";
1176 - foreach ($this->feeds as $syndicatedLink) :
1177 - $feed = $syndicatedLink->settings;
1178 - echo "<li>Fixing post meta-data for <cite>".$feed['link/name']."</cite> &#8230; "; flush();
1179 - $rss = @fetch_rss($feed['link/uri']);
1180 - if (is_array($rss->items)) :
1181 - foreach ($rss->items as $item) :
1182 - $post = new SyndicatedPost($item, $syndicatedLink);
1183 - $guid = $wpdb->escape($post->guid()); // new GUID algorithm
1184 - $link = $wpdb->escape($item['link']);
1185 -
1186 - $wpdb->query("
1187 - UPDATE `{$wpdb->posts}` SET guid='{$guid}' WHERE guid='{$link}'
1188 - ");
1189 - endforeach;
1190 - endif;
1191 - echo "<strong>complete.</strong></li>\n";
1192 - endforeach;
1193 - echo "</ul>\n";
1194 -
1195 1427 // Mark the upgrade as successful.
1196 1428 update_option('feedwordpress_version', FEEDWORDPRESS_VERSION);
1197 1429 endswitch;
1198 1430 echo "<p>Upgrade complete. FeedWordPress is now ready to use again.</p>";
@@ -1197,8 +1429,27 @@
1197 1429 endswitch;
1198 1430 echo "<p>Upgrade complete. FeedWordPress is now ready to use again.</p>";
1199 1431 } /* FeedWordPress::upgrade_database() */
1200 1432
1433 + function has_guid_index () {
1434 + global $wpdb;
1435 +
1436 + $found = false; // Guilty until proven innocent.
1437 +
1438 + $results = $wpdb->get_results("
1439 + SHOW INDEXES FROM {$wpdb->posts}
1440 + ");
1441 + if ($results) :
1442 + foreach ($results as $index) :
1443 + if (isset($index->Column_name)
1444 + and ('guid' == $index->Column_name)) :
1445 + $found = true;
1446 + endif;
1447 + endforeach;
1448 + endif;
1449 + return $found;
1450 + } /* FeedWordPress::has_guid_index () */
1451 +
1201 1452 function create_guid_index () {
1202 1453 global $wpdb;
1203 1454
1204 1455 $wpdb->query("
@@ -1205,29 +1456,116 @@
1205 1456 CREATE INDEX {$wpdb->posts}_guid_idx ON {$wpdb->posts}(guid)
1206 1457 ");
1207 1458 } /* FeedWordPress::create_guid_index () */
1208 1459
1460 + function remove_guid_index () {
1461 + global $wpdb;
1462 +
1463 + $wpdb->query("
1464 + DROP INDEX {$wpdb->posts}_guid_idx ON {$wpdb->posts}
1465 + ");
1466 + }
1467 +
1468 + /*static*/ function fetch_timeout () {
1469 + return apply_filters(
1470 + 'feedwordpress_fetch_timeout',
1471 + intval(get_option('feedwordpress_fetch_timeout', FEEDWORDPRESS_FETCH_TIMEOUT_DEFAULT))
1472 + );
1473 + }
1474 +
1475 + /*static*/ function fetch ($url, $params = array()) {
1476 + $force_feed = true; // Default
1477 +
1478 + // 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' =>
1479 + $timeout = FeedWordPress::fetch_timeout();
1480 +
1481 + if (!is_array($params)) :
1482 + $force_feed = $params;
1483 + else : // Parameter array
1484 + $args = shortcode_atts(array(
1485 + 'force_feed' => $force_feed,
1486 + 'timeout' => $timeout
1487 + ), $params);
1488 +
1489 + extract($args);
1490 + endif;
1491 + $timeout = intval($timeout);
1492 +
1493 + $pie_class = apply_filters('feedwordpress_simplepie_class', 'SimplePie');
1494 + $cache_class = apply_filters('feedwordpress_cache_class', 'WP_Feed_Cache');
1495 + $file_class = apply_filters('feedwordpress_file_class', 'FeedWordPress_File');
1496 + $parser_class = apply_filters('feedwordpress_parser_class', 'FeedWordPress_Parser');
1497 +
1498 + $sniffer_class = apply_filters('feedwordpress_sniffer_class', 'FeedWordPress_Content_Type_Sniffer');
1499 +
1500 + $feed = new $pie_class;
1501 + $feed->set_feed_url($url);
1502 + $feed->set_cache_class($cache_class);
1503 + $feed->set_timeout($timeout);
1504 +
1505 + //$feed->set_file_class('WP_SimplePie_File');
1506 + $feed->set_content_type_sniffer_class($sniffer_class);
1507 + $feed->set_file_class($file_class);
1508 + $feed->set_parser_class($parser_class);
1509 + $feed->force_feed($force_feed);
1510 + $feed->set_cache_duration(FeedWordPress::cache_duration());
1511 + $feed->init();
1512 + $feed->handle_content_type();
1513 +
1514 + if ($feed->error()) :
1515 + $ret = new WP_Error('simplepie-error', $feed->error());
1516 + else :
1517 + $ret = $feed;
1518 + endif;
1519 + return $ret;
1520 + } /* FeedWordPress::fetch () */
1521 +
1209 1522 function clear_cache () {
1210 1523 global $wpdb;
1211 1524
1212 - // MagpieRSS stores its cached feeds in options table rows with
1213 - // name = `rss_{md5 of url}` and timestamps for cached feeds in
1214 - // table rows with name = `rss_{md5 of url}_ts`. The md5 is
1215 - // always 32 characters in length, so the total option_name is
1216 - // always over 32 characters.
1217 - $wpdb->query("
1525 + // Just in case, clear out any old MagpieRSS cache records.
1526 + $magpies = $wpdb->query("
1218 1527 DELETE FROM {$wpdb->options}
1219 - WHERE LOCATE('rss_', option_name) AND LENGTH(option_name) > 32
1528 + WHERE option_name LIKE 'rss_%' AND LENGTH(option_name) > 32
1220 1529 ");
1530 +
1531 + // The WordPress SimplePie module stores its cached feeds as
1532 + // transient records in the options table. The data itself is
1533 + // stored in `_transient_feed_{md5 of url}` and the last-modified
1534 + // timestamp in `_transient_feed_mod_{md5 of url}`. Timeouts for
1535 + // these records are stored in `_transient_timeout_feed_{md5}`.
1536 + // Since the md5 is always 32 characters in length, the
1537 + // option_name is always over 32 characters.
1538 + $simplepies = $wpdb->query("
1539 + DELETE FROM {$wpdb->options}
1540 + WHERE option_name LIKE '_transient%_feed_%' AND LENGTH(option_name) > 32
1541 + ");
1542 + $simplepies = (int) ($simplepies / 4); // Each transient has 4 rows: the data, the modified timestamp; and the timeouts for each
1543 +
1544 + return ($magpies + $simplepies);
1221 1545 } /* FeedWordPress::clear_cache () */
1222 1546
1223 - function magpie_version () {
1224 - if (!defined('MAGPIE_VERSION')) : $magpie_version = $GLOBALS['wp_version'].'-default';
1225 - else : $magpie_version = MAGPIE_VERSION;
1547 + function cache_duration () {
1548 + $duration = NULL;
1549 + if (defined('FEEDWORDPRESS_CACHE_AGE')) :
1550 + $duration = FEEDWORDPRESS_CACHE_AGE;
1226 1551 endif;
1227 - return $magpie_version;
1552 + return $duration;
1228 1553 }
1554 + function cache_lifetime ($duration) {
1555 + // Check for explicit setting of a lifetime duration
1556 + if (defined('FEEDWORDPRESS_CACHE_LIFETIME')) :
1557 + $duration = FEEDWORDPRESS_CACHE_LIFETIME;
1229 1558
1559 + // Fall back to the cache freshness duration
1560 + elseif (defined('FEEDWORDPRESS_CACHE_AGE')) :
1561 + $duration = FEEDWORDPRESS_CACHE_AGE;
1562 + endif;
1563 +
1564 + // Fall back to WordPress default
1565 + return $duration;
1566 + } /* FeedWordPress::cache_lifetime () */
1567 +
1230 1568 # Utility functions for handling text settings
1231 1569 function negative ($f, $setting) {
1232 1570 $nego = array ('n', 'no', 'f', 'false');
1233 1571 return (isset($f[$setting]) and in_array(strtolower($f[$setting]), $nego));
@@ -1237,22 +1575,16 @@
1237 1575 $affirmo = array ('y', 'yes', 't', 'true', 1);
1238 1576 return (isset($f[$setting]) and in_array(strtolower($f[$setting]), $affirmo));
1239 1577 }
1240 1578
1241 -
1242 1579 # Internal debugging functions
1243 1580 function critical_bug ($varname, $var, $line) {
1244 1581 global $wp_version;
1245 1582
1246 - if (defined('MAGPIE_VERSION')) : $mv = MAGPIE_VERSION;
1247 - else : $mv = 'WordPress '.$wp_version.' default.';
1248 - endif;
1249 -
1250 1583 echo '<p>There may be a bug in FeedWordPress. Please <a href="'.FEEDWORDPRESS_AUTHOR_CONTACT.'">contact the author</a> and paste the following information into your e-mail:</p>';
1251 1584 echo "\n<plaintext>";
1252 1585 echo "Triggered at line # ".$line."\n";
1253 1586 echo "FeedWordPress version: ".FEEDWORDPRESS_VERSION."\n";
1254 - echo "MagpieRSS version: {$mv}\n";
1255 1587 echo "WordPress version: {$wp_version}\n";
1256 1588 echo "PHP version: ".phpversion()."\n";
1257 1589 echo "\n";
1258 1590 echo $varname.": "; var_dump($var); echo "\n";
@@ -1263,41 +1595,572 @@
1263 1595 if (FEEDWORDPRESS_DEBUG) : // halt only when we are doing debugging
1264 1596 FeedWordPress::critical_bug($varname, $var, $line);
1265 1597 endif;
1266 1598 }
1599 +
1600 + function val ($v, $no_newlines = false) {
1601 + ob_start();
1602 + var_dump($v);
1603 + $out = ob_get_contents(); ob_end_clean();
1604 +
1605 + if ($no_newlines) :
1606 + $out = preg_replace('/\s+/', " ", $out);
1607 + endif;
1608 + return $out;
1609 + } /* FeedWordPress::val () */
1610 +
1611 + function diagnostic_on ($level) {
1612 + $show = get_option('feedwordpress_diagnostics_show', array());
1613 + return (in_array($level, $show));
1614 + } /* FeedWordPress::diagnostic_on () */
1615 +
1616 + function diagnostic ($level, $out, $persist = NULL, $since = NULL, $mostRecent = NULL) {
1617 + global $feedwordpress_admin_footer;
1618 +
1619 + $output = get_option('feedwordpress_diagnostics_output', array());
1620 + $dlog = get_option('feedwordpress_diagnostics_log', array());
1621 +
1622 + $diagnostic_nesting = count(explode(":", $level));
1623 +
1624 + if (FeedWordPress::diagnostic_on($level)) :
1625 + foreach ($output as $method) :
1626 + switch ($method) :
1627 + case 'echo' :
1628 + echo "<div><pre><strong>Diag".str_repeat('====', $diagnostic_nesting-1).'|</strong> '.$out."</pre></div>";
1629 + break;
1630 + case 'echo_in_cronjob' :
1631 + if (FeedWordPress::update_requested()) :
1632 + echo FeedWordPress::log_prefix()." ".$out."\n";
1633 + endif;
1634 + break;
1635 + case 'admin_footer' :
1636 + $feedwordpress_admin_footer[] = $out;
1637 + break;
1638 + case 'error_log' :
1639 + error_log(FeedWordPress::log_prefix().' '.$out);
1640 + break;
1641 + case 'email' :
1642 + if (is_null($persist)) :
1643 + $sect = 'occurrent';
1644 + $hook = (isset($dlog['mesg'][$sect]) ? count($dlog['mesg'][$sect]) : 0);
1645 + $line = array("Time" => time(), "Message" => $out);
1646 + else :
1647 + $sect = 'persistent';
1648 + $hook = md5($level."\n".$persist);
1649 + $line = array("Since" => $since, "Message" => $out, "Most Recent" => $mostRecent);
1650 + endif;
1651 +
1652 + if (!isset($dlog['mesg'])) : $dlog['mesg'] = array(); endif;
1653 + if (!isset($dlog['mesg'][$sect])) : $dlog['mesg'][$sect] = array(); endif;
1654 +
1655 + $dlog['mesg'][$sect][$hook] = $line;
1656 + endswitch;
1657 + endforeach;
1658 + endif;
1659 +
1660 + update_option('feedwordpress_diagnostics_log', $dlog);
1661 + } /* FeedWordPress::diagnostic () */
1662 +
1663 + function email_diagnostic_log () {
1664 + $dlog = get_option('feedwordpress_diagnostics_log', array());
1665 +
1666 + if (isset($dlog['schedule']) and isset($dlog['schedule']['last'])) :
1667 + if (time() > ($dlog['schedule']['last'] + $dlog['schedule']['freq'])) :
1668 + // No news is good news; only send if
1669 + // there are some messages to send.
1670 + $body = NULL;
1671 + foreach ($dlog['mesg'] as $sect => $mesgs) :
1672 + if (count($mesgs) > 0) :
1673 + if (is_null($body)) : $body = ''; endif;
1674 +
1675 + $paradigm = reset($mesgs);
1676 + $body .= "<h2>".ucfirst($sect)." issues</h2>\n"
1677 + ."<table>\n"
1678 + ."<thead><tr>\n";
1679 + foreach ($paradigm as $col => $value) :
1680 + $body .= '<th scope="col">'.$col."</th>\n";
1681 + endforeach;
1682 + $body .= "</tr></thead>\n"
1683 + ."<tbody>\n";
1684 +
1685 + foreach ($mesgs as $line) :
1686 + $body .= "<tr>\n";
1687 + foreach ($line as $col => $cell) :
1688 + if (is_numeric($cell)) :
1689 + $cell = date('j-M-y, h:i a', $cell);
1690 + endif;
1691 + $class = strtolower(preg_replace('/\s+/', '-', $col));
1692 + $body .= "<td class=\"$class\">${cell}</td>";
1693 + endforeach;
1694 + $body .= "</tr>\n";
1695 + endforeach;
1696 +
1697 + $body .= "</tbody>\n</table>\n\n";
1698 + endif;
1699 + endforeach;
1700 +
1701 + if (!is_null($body)) :
1702 + $home = feedwordpress_display_url(get_bloginfo('url'));
1703 + $subj = $home . " syndication issues for ".date('j-M-y', time());
1704 + $agent = 'FeedWordPress '.FEEDWORDPRESS_VERSION;
1705 + $body = <<<EOMAIL
1706 +<html>
1707 +<head>
1708 +<title>$subj</title>
1709 +<style type="text/css">
1710 + body { background-color: white; color: black; }
1711 + table { width: 100%; border: 1px solid black; }
1712 + table thead tr th { background-color: #ff7700; color: white; border-bottom: 1px solid black; }
1713 + table thead tr { border-bottom: 1px solid black; }
1714 + table tr { vertical-align: top; }
1715 + table .since { width: 20%; }
1716 + table .time { width: 20%; }
1717 + table .most-recently { width: 20%; }
1718 + table .message { width: auto; }
1719 +</style>
1720 +</head>
1721 +<body>
1722 +<h1>Syndication Issues encountered by $agent on $home</h1>
1723 +$body
1724 +</body>
1725 +</html>
1726 +
1727 +EOMAIL;
1728 +
1729 + $ded = get_option('feedwordpress_diagnostics_email_destination', 'admins');
1730 +
1731 + // e-mail address
1732 + if (preg_match('/^mailto:(.*)$/', $ded, $ref)) :
1733 + $recipients = array($ref[1]);
1734 +
1735 + // userid
1736 + elseif (preg_match('/^user:(.*)$/', $ded, $ref)) :
1737 + $userdata = get_userdata((int) $ref[1]);
1738 + $recipients = array($userdata->user_email);
1739 +
1740 + // admins
1741 + else :
1742 + $recipients = FeedWordPressDiagnostic::admin_emails();
1743 + endif;
1744 +
1745 + foreach ($recipients as $email) :
1746 + add_filter('wp_mail_content_type', array('FeedWordPress', 'allow_html_mail'));
1747 + wp_mail($email, $subj, $body);
1748 + remove_filter('wp_mail_content_type', array('FeedWordPress', 'allow_html_mail'));
1749 + endforeach;
1750 + endif;
1751 +
1752 + // Clear the logs
1753 + $dlog['mesg']['persistent'] = array();
1754 + $dlog['mesg']['occurrent'] = array();
1755 +
1756 + // Set schedule for next update
1757 + $dlog['schedule']['last'] = time();
1758 + endif;
1759 + else :
1760 + $dlog['schedule'] = array(
1761 + 'freq' => 24 /*hr*/ * 60 /*min*/ * 60 /*s*/,
1762 + 'last' => time(),
1763 + );
1764 + endif;
1765 +
1766 + update_option('feedwordpress_diagnostics_log', $dlog);
1767 + } /* FeedWordPress::email_diagnostic_log () */
1768 +
1769 + function allow_html_mail () {
1770 + return 'text/html';
1771 + } /* FeedWordPress::allow_html_mail () */
1772 +
1773 + function admin_footer () {
1774 + global $feedwordpress_admin_footer;
1775 + foreach ($feedwordpress_admin_footer as $line) :
1776 + echo '<div><pre>'.$line.'</pre></div>';
1777 + endforeach;
1778 + } /* FeedWordPress::admin_footer () */
1779 +
1780 + function log_prefix ($date = false) {
1781 + $home = get_bloginfo('url');
1782 + $prefix = '['.feedwordpress_display_url($home).'] [feedwordpress] ';
1783 + if ($date) :
1784 + $prefix = "[".date('Y-m-d H:i:s')."]".$prefix;
1785 + endif;
1786 + return $prefix;
1787 + } /* FeedWordPress::log_prefix () */
1788 +
1789 + function menu_cap ($sub = false) {
1790 + if ($sub) :
1791 + $cap = apply_filters('feedwordpress_menu_settings_capacity', 'manage_options');
1792 + else :
1793 + $cap = apply_filters('feedwordpress_menu_main_capacity', 'manage_links');
1794 + endif;
1795 + return $cap;
1796 + } /* FeedWordPress::menu_cap () */
1797 +
1798 + function path ($filename = '') {
1799 + global $fwp_path;
1800 +
1801 + $path = $fwp_path;
1802 + if (strlen($filename) > 0) :
1803 + $path .= '/'.$filename;
1804 + endif;
1805 + return $path;
1806 + }
1807 +
1808 + function param ($key, $type = 'REQUEST', $default = NULL) {
1809 + $where = '_'.strtoupper($type);
1810 + $ret = $default;
1811 + if (isset($GLOBALS[$where]) and is_array($GLOBALS[$where])) :
1812 + if (isset($GLOBALS[$where][$key])) :
1813 + $ret = $GLOBALS[$where][$key];
1814 + if (get_magic_quotes_gpc()) :
1815 + $ret = stripslashes_deep($ret);
1816 + endif;
1817 + endif;
1818 + endif;
1819 + return $ret;
1820 + }
1821 + function post ($key, $default = NULL) {
1822 + return FeedWordPress::param($key, 'POST');
1823 + }
1267 1824 } // class FeedWordPress
1268 1825
1269 -require_once(dirname(__FILE__) . '/syndicatedpost.class.php');
1270 -require_once(dirname(__FILE__) . '/syndicatedlink.class.php');
1826 +class FeedWordPress_File extends WP_SimplePie_File {
1827 + function FeedWordPress_File ($url, $timeout = 10, $redirects = 5, $headers = null, $useragent = null, $force_fsockopen = false) {
1828 + WP_SimplePie_File::WP_SimplePie_File($url, $timeout, $redirects, $headers, $useragent, $force_fsockopen);
1271 1829
1830 + // SimplePie makes a strongly typed check against integers with
1831 + // this, but WordPress puts a string in. Which causes caching
1832 + // to break and fall on its ass when SimplePie is getting a 304,
1833 + // but doesn't realize it because this member is "304" instead.
1834 + $this->status_code = (int) $this->status_code;
1835 + }
1836 +} /* class FeedWordPress_File () */
1837 +
1838 +class FeedWordPress_Parser extends SimplePie_Parser {
1839 + function parse (&$data, $encoding) {
1840 + // Use UTF-8 if we get passed US-ASCII, as every US-ASCII character is a UTF-8 character
1841 + if (strtoupper($encoding) === 'US-ASCII')
1842 + {
1843 + $this->encoding = 'UTF-8';
1844 + }
1845 + else
1846 + {
1847 + $this->encoding = $encoding;
1848 + }
1849 +
1850 + // Strip BOM:
1851 + // UTF-32 Big Endian BOM
1852 + if (substr($data, 0, 4) === "\x00\x00\xFE\xFF")
1853 + {
1854 + $data = substr($data, 4);
1855 + }
1856 + // UTF-32 Little Endian BOM
1857 + elseif (substr($data, 0, 4) === "\xFF\xFE\x00\x00")
1858 + {
1859 + $data = substr($data, 4);
1860 + }
1861 + // UTF-16 Big Endian BOM
1862 + elseif (substr($data, 0, 2) === "\xFE\xFF")
1863 + {
1864 + $data = substr($data, 2);
1865 + }
1866 + // UTF-16 Little Endian BOM
1867 + elseif (substr($data, 0, 2) === "\xFF\xFE")
1868 + {
1869 + $data = substr($data, 2);
1870 + }
1871 + // UTF-8 BOM
1872 + elseif (substr($data, 0, 3) === "\xEF\xBB\xBF")
1873 + {
1874 + $data = substr($data, 3);
1875 + }
1876 +
1877 + if (substr($data, 0, 5) === '<?xml' && strspn(substr($data, 5, 1), "\x09\x0A\x0D\x20") && ($pos = strpos($data, '?>')) !== false)
1878 + {
1879 + $declaration =& new SimplePie_XML_Declaration_Parser(substr($data, 5, $pos - 5));
1880 + if ($declaration->parse())
1881 + {
1882 + $data = substr($data, $pos + 2);
1883 + $data = '<?xml version="' . $declaration->version . '" encoding="' . $encoding . '" standalone="' . (($declaration->standalone) ? 'yes' : 'no') . '"?>' . $data;
1884 + }
1885 + else
1886 + {
1887 + $this->error_string = 'SimplePie bug! Please report this!';
1888 + return false;
1889 + }
1890 + }
1891 +
1892 + $return = true;
1893 +
1894 + static $xml_is_sane = null;
1895 + if ($xml_is_sane === null)
1896 + {
1897 + $parser_check = xml_parser_create();
1898 + xml_parse_into_struct($parser_check, '<foo>&amp;</foo>', $values);
1899 + xml_parser_free($parser_check);
1900 + $xml_is_sane = isset($values[0]['value']);
1901 + }
1902 +
1903 + // Create the parser
1904 + if ($xml_is_sane)
1905 + {
1906 + $xml = xml_parser_create_ns($this->encoding, $this->separator);
1907 + xml_parser_set_option($xml, XML_OPTION_SKIP_WHITE, 1);
1908 + xml_parser_set_option($xml, XML_OPTION_CASE_FOLDING, 0);
1909 + xml_set_object($xml, $this);
1910 + xml_set_character_data_handler($xml, 'cdata');
1911 + xml_set_element_handler($xml, 'tag_open', 'tag_close');
1912 + xml_set_start_namespace_decl_handler($xml, 'start_xmlns');
1913 +
1914 + // Parse!
1915 + if (!xml_parse($xml, $data, true))
1916 + {
1917 + $this->error_code = xml_get_error_code($xml);
1918 + $this->error_string = xml_error_string($this->error_code);
1919 + $return = false;
1920 + }
1921 +
1922 + $this->current_line = xml_get_current_line_number($xml);
1923 + $this->current_column = xml_get_current_column_number($xml);
1924 + $this->current_byte = xml_get_current_byte_index($xml);
1925 + xml_parser_free($xml);
1926 +
1927 + return $return;
1928 + }
1929 + else
1930 + {
1931 + libxml_clear_errors();
1932 + $xml =& new XMLReader();
1933 + $xml->xml($data);
1934 + while (@$xml->read())
1935 + {
1936 + switch ($xml->nodeType)
1937 + {
1938 +
1939 + case constant('XMLReader::END_ELEMENT'):
1940 + if ($xml->namespaceURI !== '')
1941 + {
1942 + $tagName = "{$xml->namespaceURI}{$this->separator}{$xml->localName}";
1943 + }
1944 + else
1945 + {
1946 + $tagName = $xml->localName;
1947 + }
1948 + $this->tag_close(null, $tagName);
1949 + break;
1950 + case constant('XMLReader::ELEMENT'):
1951 + $empty = $xml->isEmptyElement;
1952 + if ($xml->namespaceURI !== '')
1953 + {
1954 + $tagName = "{$xml->namespaceURI}{$this->separator}{$xml->localName}";
1955 + }
1956 + else
1957 + {
1958 + $tagName = $xml->localName;
1959 + }
1960 + $attributes = array();
1961 + while ($xml->moveToNextAttribute())
1962 + {
1963 + if ($xml->namespaceURI !== '')
1964 + {
1965 + $attrName = "{$xml->namespaceURI}{$this->separator}{$xml->localName}";
1966 + }
1967 + else
1968 + {
1969 + $attrName = $xml->localName;
1970 + }
1971 + $attributes[$attrName] = $xml->value;
1972 + }
1973 +
1974 + foreach ($attributes as $attr => $value) :
1975 + list($ns, $local) = $this->split_ns($attr);
1976 + if ($ns=='http://www.w3.org/2000/xmlns/') :
1977 + if ('xmlns' == $local) : $local = false; endif;
1978 + $this->start_xmlns(null, $local, $value);
1979 + endif;
1980 + endforeach;
1981 +
1982 + $this->tag_open(null, $tagName, $attributes);
1983 + if ($empty)
1984 + {
1985 + $this->tag_close(null, $tagName);
1986 + }
1987 + break;
1988 + case constant('XMLReader::TEXT'):
1989 +
1990 + case constant('XMLReader::CDATA'):
1991 + $this->cdata(null, $xml->value);
1992 + break;
1993 + }
1994 + }
1995 + if ($error = libxml_get_last_error())
1996 + {
1997 + $this->error_code = $error->code;
1998 + $this->error_string = $error->message;
1999 + $this->current_line = $error->line;
2000 + $this->current_column = $error->column;
2001 + return false;
2002 + }
2003 + else
2004 + {
2005 + return true;
2006 + }
2007 + }
2008 + } /* FeedWordPress_Parser::parse() */
2009 +
2010 + var $xmlns_stack = array();
2011 + var $xmlns_current = array();
2012 + function tag_open ($parser, $tag, $attributes) {
2013 + $ret = parent::tag_open($parser, $tag, $attributes);
2014 + if ($this->current_xhtml_construct < 0) :
2015 + $this->data['xmlns'] = $this->xmlns_current;
2016 + $this->xmlns_stack[] = $this->xmlns_current;
2017 + endif;
2018 + return $ret;
2019 + }
2020 +
2021 + function tag_close($parser, $tag) {
2022 + if ($this->current_xhtml_construct < 0) :
2023 + $this->xmlns_current = array_pop($this->xmlns_stack);
2024 + endif;
2025 + $ret = parent::tag_close($parser, $tag);
2026 + return $ret;
2027 + }
2028 +
2029 + function start_xmlns ($parser, $prefix, $uri) {
2030 + if (!$prefix) :
2031 + $prefix = '';
2032 + endif;
2033 + if ($this->current_xhtml_construct < 0) :
2034 + $this->xmlns_current[$prefix] = $uri;
2035 + endif;
2036 + return true;
2037 + } /* FeedWordPress_Parser::start_xmlns() */
2038 +}
2039 +
2040 +$feedwordpress_admin_footer = array();
2041 +
1272 2042 ################################################################################
1273 2043 ## XML-RPC HOOKS: accept XML-RPC update pings from Contributors ################
1274 2044 ################################################################################
1275 2045
1276 -function feedwordpress_xmlrpc_hook ($args = array ()) {
1277 - $args['weblogUpdates.ping'] = 'feedwordpress_pong';
1278 - return $args;
1279 -}
2046 +class FeedWordPressRPC {
2047 + function FeedWordPressRPC () {
2048 + add_filter('xmlrpc_methods', array(&$this, 'xmlrpc_methods'));
2049 + }
2050 +
2051 + function xmlrpc_methods ($args = array()) {
2052 + $args['weblogUpdates.ping'] = array(&$this, 'ping');
2053 + $args['feedwordpress.subscribe'] = array(&$this, 'subscribe');
2054 + $args['feedwordpress.deactivate'] = array(&$this, 'deactivate');
2055 + $args['feedwordpress.delete'] = array(&$this, 'delete');
2056 + $args['feedwordpress.nuke'] = array(&$this, 'nuke');
2057 + return $args;
2058 + }
2059 +
2060 + function ping ($args) {
2061 + global $feedwordpress;
2062 +
2063 + $delta = @$feedwordpress->update($args[1]);
2064 + if (is_null($delta)):
2065 + return array('flerror' => true, 'message' => "Sorry. I don't syndicate <$args[1]>.");
2066 + else:
2067 + $mesg = array();
2068 + if (isset($delta['new'])) { $mesg[] = ' '.$delta['new'].' new posts were syndicated'; }
2069 + if (isset($delta['updated'])) { $mesg[] = ' '.$delta['updated'].' existing posts were updated'; }
2070 +
2071 + return array('flerror' => false, 'message' => "Thanks for the ping.".implode(' and', $mesg));
2072 + endif;
2073 + }
2074 +
2075 + function validate (&$args) {
2076 + global $wp_xmlrpc_server;
1280 2077
1281 -function feedwordpress_pong ($args) {
1282 - $feedwordpress =& new FeedWordPress;
1283 - $delta = @$feedwordpress->update($args[1]);
1284 - if (is_null($delta)):
1285 - return array('flerror' => true, 'message' => "Sorry. I don't syndicate <$args[1]>.");
1286 - else:
1287 - $mesg = array();
1288 - if (isset($delta['new'])) { $mesg[] = ' '.$delta['new'].' new posts were syndicated'; }
1289 - if (isset($delta['updated'])) { $mesg[] = ' '.$delta['updated'].' existing posts were updated'; }
2078 + // First two params are username/password
2079 + $username = $wp_xmlrpc_server->escape(array_shift($args));
2080 + $password = $wp_xmlrpc_server->escape(array_shift($args));
1290 2081
1291 - return array('flerror' => false, 'message' => "Thanks for the ping.".implode(' and', $mesg));
1292 - endif;
1293 -}
2082 + $ret = array();
2083 + if ( !$user = $wp_xmlrpc_server->login($username, $password) ) :
2084 + $ret = $wp_xmlrpc_server->error;
2085 + elseif (!current_user_can('manage_links')) :
2086 + $ret = new IXR_Error(401, 'Sorry, you cannot change the subscription list.');
2087 + endif;
2088 + return $ret;
2089 + }
1294 2090
1295 -# The upgraded MagpieRSS also uses this class. So if we have it loaded
1296 -# in, don't load it again.
1297 -if (!class_exists('Relative_URI')) {
1298 - require_once(dirname(__FILE__) . '/relative_uri.class.php');
1299 -}
2091 + function subscribe ($args) {
2092 + $ret = $this->validate($args);
2093 + if (is_array($ret)) : // Success
2094 + // The remaining params are feed URLs
2095 + foreach ($args as $arg) :
2096 + $finder = new FeedFinder($arg, /*verify=*/ false, /*fallbacks=*/ 1);
2097 + $feeds = array_values(array_unique($finder->find()));
2098 +
2099 + if (count($feeds) > 0) :
2100 + $link_id = FeedWordPress::syndicate_link(
2101 + /*title=*/ feedwordpress_display_url($feeds[0]),
2102 + /*homepage=*/ $feeds[0],
2103 + /*feed=*/ $feeds[0]
2104 + );
2105 + $ret[] = array(
2106 + 'added',
2107 + $feeds[0],
2108 + $arg,
2109 + );
2110 + else :
2111 + $ret[] = array(
2112 + 'error',
2113 + $arg
2114 + );
2115 + endif;
2116 + endforeach;
2117 + endif;
2118 + return $ret;
2119 + } /* FeedWordPressRPC::subscribe () */
2120 +
2121 + function unsubscribe ($method, $args) {
2122 + $ret = $this->validate($args);
2123 + if (is_array($ret)) : // Success
2124 + // The remaining params are feed URLs
2125 + foreach ($args as $arg) :
2126 + $link_id = FeedWordPress::find_link($arg);
2127 +
2128 + if (!$link_id) :
2129 + $link_id = FeedWordPress::find_link($arg, 'link_url');
2130 + endif;
2131 +
2132 + if ($link_id) :
2133 + $link = new SyndicatedLink($link_id);
2134 +
2135 + $link->{$method}();
2136 + $ret[] = array(
2137 + 'deactivated',
2138 + $arg,
2139 + );
2140 + else :
2141 + $ret[] = array(
2142 + 'error',
2143 + $arg,
2144 + );
2145 + endif;
2146 + endforeach;
2147 + endif;
2148 + return $ret;
2149 + } /* FeedWordPress::unsubscribe () */
2150 +
2151 + function deactivate ($args) {
2152 + return $this->unsubscribe('deactivate', $args);
2153 + } /* FeedWordPressRPC::deactivate () */
2154 +
2155 + function delete ($args) {
2156 + return $this->unsubscribe('delete', $args);
2157 + } /* FeedWordPressRPC::delete () */
2158 +
2159 + function nuke ($args) {
2160 + return $this->unsubscribe('nuke', $args);
2161 + } /* FeedWordPressRPC::nuke () */
2162 +} /* class FeedWordPressRPC */
1300 2163
1301 2164 // take your best guess at the realname and e-mail, given a string
1302 2165 define('FWP_REGEX_EMAIL_ADDY', '([^@"(<\s]+@[^"@(<\s]+\.[^"@(<\s]+)');
1303 2166 define('FWP_REGEX_EMAIL_NAME', '("([^"]*)"|([^"<(]+\S))');