PluginProbe
FeedWordPress / 2011.0512
FeedWordPress v2011.0512
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 +1506 -518 2009.07072011.0512 View file →
@@ -1,19 +1,25 @@
1 1 <?php
2 2 /*
3 3 Plugin Name: FeedWordPress
4 -Plugin URI: http://projects.radgeek.com/feedwordpress
4 +Plugin URI: http://feedwordpress.radgeek.com/
5 5 Description: simple and flexible Atom/RSS syndication for WordPress
6 -Version: 2009.0707
6 +Version: 2011.0512
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.0512
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,15 +33,26 @@
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.0707');
37 +define ('FEEDWORDPRESS_VERSION', '2011.0512');
32 38 define ('FEEDWORDPRESS_AUTHOR_CONTACT', 'http://radgeek.com/contact');
39 +
40 +if (!defined('FEEDWORDPRESS_BLEG')) :
41 + define ('FEEDWORDPRESS_BLEG', true);
42 +endif;
43 +
44 +// Defaults
33 45 define ('DEFAULT_SYNDICATION_CATEGORY', 'Contributors');
46 +define ('DEFAULT_UPDATE_PERIOD', 60); // value in minutes
34 47
35 -$feedwordpress_debug = get_option('feedwordpress_debug');
36 -if (is_string($feedwordpress_debug)) :
37 - $feedwordpress_debug = ($feedwordpress_debug == 'yes');
48 +if (isset($_REQUEST['feedwordpress_debug'])) :
49 + $feedwordpress_debug = $_REQUEST['feedwordpress_debug'];
50 +else :
51 + $feedwordpress_debug = get_option('feedwordpress_debug');
52 + if (is_string($feedwordpress_debug)) :
53 + $feedwordpress_debug = ($feedwordpress_debug == 'yes');
54 + endif;
38 55 endif;
39 56 define ('FEEDWORDPRESS_DEBUG', $feedwordpress_debug);
40 57
41 58 define ('FEEDWORDPRESS_CAT_SEPARATOR_PATTERN', '/[:\n]/');
@@ -45,8 +62,9 @@
45 62
46 63 define ('FEEDWORDPRESS_FRESHNESS_INTERVAL', 10*60); // Every ten minutes
47 64
48 65 define ('FWP_SCHEMA_HAS_USERMETA', 2966);
66 +define ('FWP_SCHEMA_USES_ARGS_TAXONOMY', 12694); // Revision # for using $args['taxonomy'] to get link categories
49 67 define ('FWP_SCHEMA_20', 3308); // Database schema # for WP 2.0
50 68 define ('FWP_SCHEMA_21', 4772); // Database schema # for WP 2.1
51 69 define ('FWP_SCHEMA_23', 5495); // Database schema # for WP 2.3
52 70 define ('FWP_SCHEMA_25', 7558); // Database schema # for WP 2.5
@@ -52,14 +70,15 @@
52 70 define ('FWP_SCHEMA_25', 7558); // Database schema # for WP 2.5
53 71 define ('FWP_SCHEMA_26', 8201); // Database schema # for WP 2.6
54 72 define ('FWP_SCHEMA_27', 9872); // Database schema # for WP 2.7
55 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
56 76
57 77 if (FEEDWORDPRESS_DEBUG) :
58 78 // Help us to pick out errors, if any.
59 79 ini_set('error_reporting', E_ALL & ~E_NOTICE);
60 80 ini_set('display_errors', true);
61 - define('MAGPIE_DEBUG', true);
62 81
63 82 // When testing we don't want cache issues to interfere. But this is
64 83 // a VERY BAD SETTING for a production server. Webmasters will eat your
65 84 // face for breakfast if you use it, and the baby Jesus will cry. So
@@ -64,46 +83,53 @@
64 83 // a VERY BAD SETTING for a production server. Webmasters will eat your
65 84 // face for breakfast if you use it, and the baby Jesus will cry. So
66 85 // make sure FEEDWORDPRESS_DEBUG is FALSE for any site that will be
67 86 // used for more than testing purposes!
68 - define('MAGPIE_CACHE_AGE', 1);
87 + define('FEEDWORDPRESS_CACHE_AGE', 1);
88 + define('FEEDWORDPRESS_CACHE_LIFETIME', 1);
89 + define('FEEDWORDPRESS_FETCH_TIMEOUT_DEFAULT', 60);
69 90 else :
70 - define('MAGPIE_DEBUG', false);
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);
71 96 endif;
72 97
73 -// Note that the rss-functions.php that comes prepackaged with WordPress is
74 -// old & busted. For the new hotness, drop a copy of rss.php from
75 -// 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'));
76 100
77 -if (is_readable(ABSPATH . WPINC . '/rss.php')) :
78 - require_once (ABSPATH . WPINC . '/rss.php');
79 -else :
80 - 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');
81 105 endif;
106 +require_once(ABSPATH . WPINC . '/class-feed.php');
82 107
83 -if (isset($wp_db_version)) :
84 - if ($wp_db_version >= FWP_SCHEMA_23) :
85 - require_once (ABSPATH . WPINC . '/registration.php'); // for wp_insert_user
86 - elseif ($wp_db_version >= FWP_SCHEMA_21) : // WordPress 2.1 and 2.2, but not 2.3
87 - require_once (ABSPATH . WPINC . '/registration.php'); // for wp_insert_user
88 - require_once (ABSPATH . 'wp-admin/admin-db.php'); // for wp_insert_category
89 - elseif ($wp_db_version >= FWP_SCHEMA_20) : // WordPress 2.0
90 - require_once (ABSPATH . WPINC . '/registration-functions.php'); // for wp_insert_user
91 - require_once (ABSPATH . 'wp-admin/admin-db.php'); // for wp_insert_category
92 - endif;
108 +if (!function_exists('wp_insert_user')) :
109 + require_once (ABSPATH . WPINC . '/registration.php'); // for wp_insert_user
93 110 endif;
94 111
112 +require_once(dirname(__FILE__) . '/admin-ui.php');
113 +require_once(dirname(__FILE__) . '/feedwordpresssyndicationpage.class.php');
95 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');
96 118 require_once(dirname(__FILE__) . '/feedwordpresshtml.class.php');
119 +require_once(dirname(__FILE__) . '/feedwordpress-content-type-sniffer.class.php');
97 120
98 121 // Magic quotes are just about the stupidest thing ever.
99 122 if (is_array($_POST)) :
100 - $fwp_post = stripslashes_deep($_POST);
123 + $fwp_post = $_POST;
124 + if (get_magic_quotes_gpc()) :
125 + $fwp_post = stripslashes_deep($fwp_post);
126 + endif;
101 127 endif;
102 128
103 129 // Get the path relative to the plugins directory in which FWP is stored
104 130 preg_match (
105 - '|/wp-content/plugins/(.+)$|',
131 + '|'.preg_quote(WP_PLUGIN_DIR).'/(.+)$|',
106 132 dirname(__FILE__),
107 133 $ref
108 134 );
109 135
@@ -114,27 +140,21 @@
114 140 endif;
115 141
116 142 // If this is a FeedWordPress admin page, queue up scripts for AJAX functions that FWP uses
117 143 // If it is a display page or a non-FeedWordPress admin page, don't.
118 -if (is_admin() and isset($_REQUEST['page']) and preg_match("|^{$fwp_path}/|", $_REQUEST['page'])) :
119 - if (function_exists('wp_enqueue_script')) :
120 - if (isset($wp_db_version) and $wp_db_version >= FWP_SCHEMA_25) :
121 - wp_enqueue_script('post'); // for magic tag and category boxes
122 - wp_enqueue_script('thickbox'); // for fold-up boxes
123 - wp_enqueue_script('admin-forms'); // for checkbox selection
124 - else :
125 - wp_enqueue_script( 'ajaxcat' ); // Provides the handy-dandy new category text box
126 - endif;
127 - endif;
128 - if (function_exists('wp_enqueue_style')) :
129 - if (fwp_test_wp_version(FWP_SCHEMA_25)) :
130 - wp_enqueue_style('dashboard');
131 - endif;
132 - 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 +
133 155 if (function_exists('wp_admin_css')) :
134 - if (fwp_test_wp_version(FWP_SCHEMA_25)) :
135 - wp_admin_css('css/dashboard');
136 - endif;
156 + wp_admin_css('css/dashboard');
137 157 endif;
138 158 endif;
139 159
140 160 if (!FeedWordPress::needs_upgrade()) : // only work if the conditions are safe!
@@ -157,10 +177,17 @@
157 177
158 178 add_action('atom_entry', 'feedwordpress_item_feed_data');
159 179
160 180 # Filter in original permalinks if the user wants that
161 - add_filter('post_link', 'syndication_permalink', 1);
181 + add_filter('post_link', 'syndication_permalink', /*priority=*/ 1, /*arguments=*/ 3);
162 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 +
163 190 # WTF? By default, wp_insert_link runs incoming link_url and link_rss
164 191 # URIs through default filters that include `wp_kses()`. But `wp_kses()`
165 192 # just happens to escape any occurrence of & to &amp; -- which just
166 193 # happens to fuck up any URI with a & to separate GET parameters.
@@ -169,13 +196,17 @@
169 196
170 197 # Admin menu
171 198 add_action('admin_menu', 'fwp_add_pages');
172 199 add_action('admin_notices', 'fwp_check_debug');
173 - add_action('admin_notices', 'fwp_check_magpie');
174 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 +
175 206 # Inbound XML-RPC update methods
176 - add_filter('xmlrpc_methods', 'feedwordpress_xmlrpc_hook');
177 -
207 + $feedwordpressRPC = new FeedWordPressRPC;
208 +
178 209 # Outbound XML-RPC ping reform
179 210 remove_action('publish_post', 'generic_ping'); // WP 1.5.x
180 211 remove_action('do_pings', 'do_all_pings', 10, 1); // WP 2.1, 2.2
181 212 remove_action('publish_post', '_publish_post_hook', 5, 1); // WP 2.3
@@ -184,22 +215,25 @@
184 215 add_action('do_pings', 'fwp_do_pings', 10, 1);
185 216 add_action('feedwordpress_update', 'fwp_hold_pings');
186 217 add_action('feedwordpress_update_complete', 'fwp_release_pings');
187 218
188 - # Hook in logging functions only if the logging option is ON
189 - $update_logging = get_option('feedwordpress_update_logging');
190 - if ($update_logging == 'yes') :
191 - add_action('post_syndicated_item', 'log_feedwordpress_post', 100);
192 - add_action('update_syndicated_item', 'log_feedwordpress_update_post', 100);
193 - add_action('feedwordpress_update', 'log_feedwordpress_update_feeds', 100);
194 - add_action('feedwordpress_check_feed', 'log_feedwordpress_check_feed', 100);
195 - add_action('feedwordpress_update_complete', 'log_feedwordpress_update_complete', 100);
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);
223 +
224 + $feedwordpress = new FeedWordPress;
225 +
226 + # Cron-less auto-update. Hooray!
227 + $autoUpdateHook = get_option('feedwordpress_automatic_updates');
228 + if ($autoUpdateHook != 'init') :
229 + $autoUpdateHook = 'shutdown';
196 230 endif;
197 -
198 - # Cron-less auto-update. Hooray!
199 - add_action('init', 'feedwordpress_auto_update');
200 - add_action('init', 'feedwordpress_check_for_magpie_fix');
201 -
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'));
235 +
202 236 # Default sanitizers
203 237 add_filter('syndicated_item_content', array('SyndicatedPost', 'resolve_relative_uris'), 0, 2);
204 238 add_filter('syndicated_item_content', array('SyndicatedPost', 'sanitize_content'), 0, 2);
205 239
@@ -207,194 +241,252 @@
207 241 # Hook in the menus, which will just point to the upgrade interface
208 242 add_action('admin_menu', 'fwp_add_pages');
209 243 endif; // if (!FeedWordPress::needs_upgrade())
210 244
211 -function feedwordpress_check_for_magpie_fix () {
212 - if (isset($_POST['action']) and $_POST['action']=='fix_magpie_version') :
213 - FeedWordPressCompatibility::validate_http_request(/*action=*/ 'feedwordpress_fix_magpie', /*capability=*/ 'edit_files');
214 -
215 - $back_to = $_SERVER['REQUEST_URI'];
216 - if (isset($_POST['ignore'])) :
217 - // kill error message by telling it we ignored the upgrade request for this version
218 - update_option('feedwordpress_magpie_ignored_upgrade_to', EXPECTED_MAGPIE_VERSION);
219 - $ret = 'ignored';
220 - elseif (isset($_POST['upgrade'])) :
221 - $source = dirname(__FILE__)."/MagpieRSS-upgrade/rss.php";
222 - $destination = ABSPATH . WPINC . '/rss.php';
223 - $success = @copy($source, $destination);
224 - $ret = (int) $success;
225 - endif;
226 -
227 - if (strpos($back_to, '?')===false) : $sep = '?';
228 - else : $sep = '&';
229 - endif;
230 -
231 - header("Location: {$back_to}{$sep}feedwordpress_magpie_fix=".$ret);
232 - exit;
233 - endif;
234 -} /* feedwordpress_check_for_magpie_fix() */
235 -
236 -function feedwordpress_auto_update () {
237 - if (FeedWordPress::stale()) :
238 - $feedwordpress =& new FeedWordPress;
239 - $feedwordpress->update();
240 - endif;
241 -
242 - if (FeedWordPress::update_requested()) :
243 - exit;
244 - endif;
245 -} /* feedwordpress_auto_update () */
246 -
247 245 ################################################################################
248 246 ## LOGGING FUNCTIONS: log status updates to error_log if you want it ###########
249 247 ################################################################################
250 248
251 -function log_feedwordpress_post ($id) {
252 - $post = wp_get_single_post($id);
253 - error_log("[".date('Y-m-d H:i:s')."][feedwordpress] posted "
254 - ."'{$post->post_title}' ({$post->post_date})");
255 -}
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']);
256 264
257 -function log_feedwordpress_update_post ($id) {
258 - $post = wp_get_single_post($id);
259 - error_log("[".date('Y-m-d H:i:s')."][feedwordpress] updated "
260 - ."'{$post->post_title}' ({$post->post_date})"
261 - ." (as of {$post->post_modified})");
262 -}
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 + }
263 277
264 -function log_feedwordpress_update_feeds ($uri) {
265 - error_log("[".date('Y-m-d H:i:s')."][feedwordpress] update('$uri')");
266 -}
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 */
267 293
268 -function log_feedwordpress_check_feed ($feed) {
269 - $uri = $feed['link/uri']; $name = $feed['link/name'];
270 - 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}";
271 303 }
272 304
273 -function log_feedwordpress_update_complete ($delta) {
274 - $mesg = array();
275 - if (isset($delta['new'])) $mesg[] = 'added '.$delta['new'].' new posts';
276 - if (isset($delta['updated'])) $mesg[] = 'updated '.$delta['updated'].' existing posts';
277 - 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() */
278 315
279 - error_log("[".date('Y-m-d H:i:s')."][feedwordpress] "
280 - .(is_null($delta) ? "Error: I don't syndicate that URI"
281 - : implode(' and ', $mesg)));
282 -}
283 -
284 316 ################################################################################
285 317 ## TEMPLATE API: functions to make your templates syndication-aware ############
286 318 ################################################################################
287 319
288 -function is_syndicated () { return (strlen(get_syndication_feed_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() */
289 332
290 -function get_syndication_source_link ($original = NULL) {
291 - if (is_null($original)) : $original = FeedWordPress::use_aggregator_source_data();
333 +function feedwordpress_display_url ($url, $before = 60, $after = 0) {
334 + $bits = parse_url($url);
335 +
336 + // Strip out crufty subdomains
337 + if (isset($bits['host'])) :
338 + $bits['host'] = preg_replace('/^www[0-9]*\./i', '', $bits['host']);
339 + endif;
340 +
341 + // Reassemble bit-by-bit with minimum of crufty elements
342 + $url = (isset($bits['user'])?$bits['user'].'@':'')
343 + .(isset($bits['host'])?$bits['host']:'')
344 + .(isset($bits['path'])?$bits['path']:'')
345 + .(isset($uri_bits['port'])?':'.$uri_bits['port']:'')
346 + .(isset($bits['query'])?'?'.$bits['query']:'');
347 +
348 + if (strlen($url) > ($before+$after)) :
349 + $url = substr($url, 0, $before).'…'.substr($url, 0 - $after, $after);
292 350 endif;
293 351
294 - if ($original) : $vals = get_post_custom_values('syndication_source_uri_original');
295 - else : $vals = array();
352 + return $url;
353 +} /* feedwordpress_display_url () */
354 +
355 +function get_syndication_source_property ($original, $id, $local, $remote = NULL) {
356 + $ret = NULL;
357 + if (is_null($original)) :
358 + $original = FeedWordPress::use_aggregator_source_data();
296 359 endif;
297 360
298 - if (count($vals) == 0) : $vals = get_post_custom_values('syndication_source_uri');
361 + if (is_null($remote)) :
362 + $remote = $local . '_original';
299 363 endif;
300 364
301 - if (count($vals) > 0) : $ret = $vals[0]; else : $ret = NULL; endif;
302 -
303 - return $ret;
304 -} /* function get_syndication_source_link() */
305 -
306 -function the_syndication_source_link ($original = NULL) { echo get_syndication_source_link($original); }
307 -
308 -function get_syndication_source ($original = NULL) {
309 - if (is_null($original)) : $original = FeedWordPress::use_aggregator_source_data();
365 + $vals = ($original ? get_post_custom_values($remote, $id) : array());
366 + if (count($vals) < 1) :
367 + $vals = get_post_custom_values($local, $id);
310 368 endif;
311 -
312 - if ($original) : $vals = get_post_custom_values('syndication_source_original');
313 - else : $vals = array();
314 - endif;
315 369
316 - if (count($vals) == 0) : $vals = get_post_custom_values('syndication_source');
370 + if (count($vals) > 0) :
371 + $ret = $vals[0];
317 372 endif;
318 -
319 - if (count($vals) > 0) : $ret = $vals[0]; else : $ret = NULL; endif;
320 -
321 373 return $ret;
322 -} /* function get_syndication_source() */
374 +} /* function get_syndication_source_property () */
323 375
324 -function the_syndication_source ($original = NULL) { echo get_syndication_source($original); }
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() */
325 380
326 -function get_syndication_feed ($original = NULL) {
327 - if (is_null($original)) : $original = FeedWordPress::use_aggregator_source_data();
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
385 + $ret = feedwordpress_display_url(get_syndication_source_link());
328 386 endif;
387 + return $ret;
388 +} /* function get_syndication_source() */
329 389
330 - if ($original) : $vals = get_post_custom_values('syndication_feed_original');
331 - else : $vals = array();
332 - endif;
333 -
334 - if (count($vals) == 0) : $vals = get_post_custom_values('syndication_feed');
335 - endif;
336 -
337 - if (count($vals) > 0) : $ret = $vals[0]; else : $ret = NULL; endif;
338 -
339 - return $ret;
390 +function the_syndication_feed ($original = NULL, $id = NULL) { echo get_syndication_feed($original, $id); }
391 +function get_syndication_feed ($original = NULL, $id = NULL) {
392 + return get_syndication_source_property($original, $id, 'syndication_feed');
340 393 } /* function get_syndication_feed() */
341 394
342 -function the_syndication_feed ($original = NULL) { echo get_syndication_feed ($original); }
343 -
344 -function get_syndication_feed_guid ($original = NULL) {
345 - if (is_null($original)) : $original = FeedWordPress::use_aggregator_source_data();
395 +function the_syndication_feed_guid ($original = NULL, $id = NULL) { echo get_syndication_feed_guid($original, $id); }
396 +function get_syndication_feed_guid ($original = NULL, $id = NULL) {
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();
346 400 endif;
347 -
348 - if ($original) : $vals = get_post_custom_values('syndication_source_id_original');
349 - else : $vals = array();
350 - endif;
351 -
352 - if (count($vals) == 0) : $vals = array(get_feed_meta('feed/id'));
353 - endif;
354 -
355 - if (count($vals) > 0) : $ret = $vals[0]; else : $ret = NULL; endif;
356 -
357 401 return $ret;
358 402 } /* function get_syndication_feed_guid () */
359 403
360 -function the_syndication_feed_guid ($original = NULL) { echo get_syndication_feed_guid($original); }
404 +function get_syndication_feed_id ($id = NULL) { list($u) = get_post_custom_values('syndication_feed_id', $id); return $u; }
405 +function the_syndication_feed_id ($id = NULL) { echo get_syndication_feed_id($id); }
361 406
362 -function get_syndication_feed_id () { list($u) = get_post_custom_values('syndication_feed_id'); return $u; }
363 -function the_syndication_feed_id () { echo get_syndication_feed_id(); }
364 -
365 407 $feedwordpress_linkcache = array (); // only load links from database once
408 +function get_syndication_feed_object ($id = NULL) {
409 + global $feedwordpress_linkcache;
366 410
367 -function get_feed_meta ($key) {
368 - global $wpdb, $feedwordpress_linkcache;
369 - $feed_id = get_syndication_feed_id();
411 + $link = NULL;
370 412
371 - $ret = NULL;
413 + $feed_id = get_syndication_feed_id($id);
372 414 if (strlen($feed_id) > 0):
373 415 if (isset($feedwordpress_linkcache[$feed_id])) :
374 416 $link = $feedwordpress_linkcache[$feed_id];
375 417 else :
376 - $link =& new SyndicatedLink($feed_id);
418 + $link = new SyndicatedLink($feed_id);
377 419 $feedwordpress_linkcache[$feed_id] = $link;
378 420 endif;
421 + endif;
422 + return $link;
423 +}
379 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])) :
380 430 $ret = $link->settings[$key];
381 431 endif;
382 432 return $ret;
383 433 } /* get_feed_meta() */
384 434
385 -function get_syndication_permalink () {
386 - list($u) = get_post_custom_values('syndication_permalink'); return $u;
435 +function get_syndication_permalink ($id = NULL) {
436 + list($u) = get_post_custom_values('syndication_permalink', $id); return $u;
387 437 }
388 -function the_syndication_permalink () {
389 - echo get_syndication_permalink();
438 +function the_syndication_permalink ($id = NULL) {
439 + echo get_syndication_permalink($id);
390 440 }
391 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 +
392 483 ################################################################################
393 484 ## FILTERS: syndication-aware handling of post data for templates and feeds ####
394 485 ################################################################################
395 486
396 487 $feedwordpress_the_syndicated_content = NULL;
488 +$feedwordpress_the_original_permalink = NULL;
397 489
398 490 function feedwordpress_preserve_syndicated_content ($text) {
399 491 global $feedwordpress_the_syndicated_content;
400 492
@@ -424,16 +516,16 @@
424 516 // In a post context....
425 517 if (is_syndicated()) :
426 518 ?>
427 519 <source>
428 - <title><?php the_syndication_source(); ?></title>
429 - <link rel="alternate" type="text/html" href="<?php the_syndication_source_link(); ?>" />
430 - <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()); ?>" />
431 523 <?php
432 524 $id = get_syndication_feed_guid();
433 525 if (strlen($id) > 0) :
434 526 ?>
435 - <id><?php print $id; ?></id>
527 + <id><?php print htmlspecialchars($id); ?></id>
436 528 <?php
437 529 endif;
438 530 $updated = get_feed_meta('feed/updated');
439 531 if (strlen($updated) > 0) : ?>
@@ -445,46 +537,197 @@
445 537 <?php
446 538 endif;
447 539 }
448 540
449 -function syndication_permalink ($permalink = '') {
450 - if (get_option('feedwordpress_munge_permalink') != 'no'):
451 - $uri = get_syndication_permalink();
452 - return ((strlen($uri) > 0) ? $uri : $permalink);
453 - else:
454 - 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;
455 577 endif;
456 -} // function syndication_permalink ()
457 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 +
458 678 ################################################################################
459 679 ## ADMIN MENU ADD-ONS: register Dashboard management pages #####################
460 680 ################################################################################
461 681
462 682 function fwp_add_pages () {
463 - global $fwp_capability;
464 - 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 + );
465 694
466 - $menu = array('Syndicated Sites', 'Syndication', $fwp_capability['manage_links'], $fwp_path.'/syndication.php', NULL);
467 - if (fwp_test_wp_version(FWP_SCHEMA_27)) :
468 - // add icon parameter
469 - $menu[] = WP_PLUGIN_URL.'/'.$fwp_path.'/feedwordpress-tiny.png';
470 - 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 + );
471 700
472 - if (fwp_test_wp_version(FWP_SCHEMA_26)) :
473 - $options = __('Settings');
474 - $longoptions = __('Syndication Settings');
475 - else :
476 - $options = __('Options');
477 - $longoptions = __('Syndication Options');
478 - endif;
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 + );
479 706
480 - call_user_func_array('add_menu_page', $menu);
481 - add_submenu_page($fwp_path.'/syndication.php', 'Syndicated Posts', 'Posts', $fwp_capability['manage_options'], $fwp_path.'/posts-page.php');
482 - add_submenu_page($fwp_path.'/syndication.php', 'Syndicated Authors', 'Authors', $fwp_capability['manage_options'], $fwp_path.'/authors-page.php');
483 - add_submenu_page($fwp_path.'/syndication.php', 'Categories & Tags', 'Categories & Tags', $fwp_capability['manage_options'], $fwp_path.'/categories-page.php');
484 - add_submenu_page($fwp_path.'/syndication.php', $longoptions, $options, $fwp_capability['manage_options'], $fwp_path.'/syndication-options.php');
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 + );
485 712
486 - add_options_page($longoptions, 'Syndication', $fwp_capability['manage_options'], $fwp_path.'/syndication-options.php');
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 + );
487 730 } /* function fwp_add_pages () */
488 731
489 732 function fwp_check_debug () {
490 733 // This is a horrible fucking kludge that I have to do because the
@@ -507,97 +750,8 @@
507 750 <?php
508 751 endif;
509 752 } /* function fwp_check_debug () */
510 753
511 -define('EXPECTED_MAGPIE_VERSION', '2009.0618');
512 -function fwp_check_magpie () {
513 - if (isset($_REQUEST['feedwordpress_magpie_fix'])) :
514 - if ($_REQUEST['feedwordpress_magpie_fix']=='ignored') :
515 -?>
516 -<div class="updated fade">
517 -<p>O.K., we'll ignore the problem for now. FeedWordPress will not display any
518 -more error messages.</p>
519 -</div>
520 -<?php
521 - elseif ((bool) $_REQUEST['feedwordpress_magpie_fix']) :
522 -?>
523 -<div class="updated fade">
524 -<p>Congratulations! Your MagpieRSS has been successfully upgraded to the version
525 -shipped with FeedWordPress.</p>
526 -</div>
527 -<?php
528 - else :
529 - $source = dirname(__FILE__)."/MagpieRSS-upgrade/rss.php";
530 - $destination = ABSPATH . WPINC . '/rss.php';
531 - $cmd = "cp '".htmlspecialchars(addslashes($source))."' '".htmlspecialchars(addslashes($destination))."'";
532 - $cmd = wordwrap($cmd, /*width=*/ 75, /*break=*/ " \\\n\t");
533 -
534 -?>
535 -<div class="error">
536 -<p><strong>FeedWordPress was unable to automatically upgrade your copy of MagpieRSS.</strong></p>
537 -<p>It's likely that you need to change the file permissions on <code><?php print htmlspecialchars($source); ?></code>
538 -to allow FeedWordPress to overwrite it.</p>
539 -<p><strong>To perform the upgrade manually,</strong> you can
540 -use a SFTP or FTP client to upload a copy of <code>rss.php</code> from the
541 -<code>MagpieRSS-upgrades/</code> directory of your FeedWordPress archive so that
542 -it overwrites <code><?php print htmlspecialchars($destination); ?></code>. Or,
543 -if your web host provides shell access, you can issue the following command from
544 -a command prompt to perform the upgrade:</p>
545 -<pre>
546 -<samp>$</samp> <kbd><?php print $cmd; ?></kbd>
547 -</pre>
548 -<p><strong>If you've fixed the file permissions,</strong> you can try the
549 -automatic upgrade again.</p>
550 -
551 -<?php feedwordpress_upgrade_old_and_busted_buttons(); ?>
552 -</div>
553 -<?php
554 - endif;
555 - else :
556 - $magpie_version = FeedWordPress::magpie_version();
557 -
558 - $ignored = get_option('feedwordpress_magpie_ignored_upgrade_to');
559 - if (EXPECTED_MAGPIE_VERSION != $magpie_version and EXPECTED_MAGPIE_VERSION != $ignored) :
560 - if (current_user_can('edit_files')) :
561 - $youAre = 'you are';
562 - $itIsRecommendedThatYou = 'It is <strong>strongly recommended</strong> that you';
563 - else :
564 - $youAre = 'this site is';
565 - $itIsRecommendedThatYou = 'You may want to contact the administrator of the site; it is <strong>strongly recommended</strong> that they';
566 - endif;
567 - print '<div class="error">';
568 -?>
569 -<p style="font-style: italic"><strong>FeedWordPress has detected that <?php print $youAre; ?> currently using a version of
570 -MagpieRSS other than the upgraded version that ships with this version of FeedWordPress.</strong></p>
571 -<ul>
572 -<li><strong>Currently running:</strong> MagpieRSS <?php print $magpie_version; ?></li>
573 -<li><strong>Version included with FeedWordPress <?php print FEEDWORDPRESS_VERSION; ?>:</strong> MagpieRSS <?php print EXPECTED_MAGPIE_VERSION; ?></li>
574 -</ul>
575 -<p><?php print $itIsRecommendedThatYou; ?> install the upgraded
576 -version of MagpieRSS supplied with FeedWordPress. The version of
577 -MagpieRSS that ships with WordPress is very old and buggy, and
578 -encounters a number of errors when trying to parse modern Atom
579 -and RSS feeds.</p>
580 -<?php
581 - feedwordpress_upgrade_old_and_busted_buttons();
582 - print '</div>';
583 - endif;
584 - endif;
585 -}
586 -
587 -function feedwordpress_upgrade_old_and_busted_buttons() {
588 - if (current_user_can('edit_files')) :
589 -?>
590 -<form action="" method="post"><div>
591 -<?php FeedWordPressCompatibility::stamp_nonce('feedwordpress_fix_magpie'); ?>
592 -<input type="hidden" name="action" value="fix_magpie_version" />
593 -<input class="button-secondary" type="submit" name="ignore" value="<?php _e('Ignore this problem'); ?>" />
594 -<input class="button-primary" type="submit" name="upgrade" value="<?php _e('Upgrade'); ?>" />
595 -</div></form>
596 -<?php
597 - endif;
598 -}
599 -
600 754 ################################################################################
601 755 ## fwp_hold_pings() and fwp_release_pings(): Outbound XML-RPC ping reform ####
602 756 ## ... 'coz it's rude to send 500 pings the first time your aggregator runs ####
603 757 ################################################################################
@@ -653,8 +807,67 @@
653 807 endif;
654 808 endif;
655 809 }
656 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 +
657 870 ################################################################################
658 871 ## class FeedWordPress #########################################################
659 872 ################################################################################
660 873
@@ -698,9 +911,9 @@
698 911 function FeedWordPress () {
699 912 $this->feeds = array ();
700 913 $links = FeedWordPress::syndicated_links();
701 914 if ($links): foreach ($links as $link):
702 - $this->feeds[] =& new SyndicatedLink($link);
915 + $this->feeds[] = new SyndicatedLink($link);
703 916 endforeach; endif;
704 917 } // FeedWordPress::FeedWordPress ()
705 918
706 919 # function update (): polls for updates on one or more Contributor feeds
@@ -758,9 +971,9 @@
758 971 if (FeedWordPress::needs_upgrade()) : // Will make duplicate posts if we don't hold off
759 972 return NULL;
760 973 endif;
761 974
762 - if (!is_null($uri)) :
975 + if (!is_null($uri) and $uri != '*') :
763 976 $uri = trim($uri);
764 977 else : // Update all
765 978 update_option('feedwordpress_last_update_all', time());
766 979 endif;
@@ -767,20 +980,17 @@
767 980
768 981 do_action('feedwordpress_update', $uri);
769 982
770 983 if (is_null($crash_ts)) :
771 - $crash_dt = (int) get_option('feedwordpress_update_time_limit');
772 - if ($crash_dt > 0) :
773 - $crash_ts = time() + $crash_dt;
774 - else :
775 - $crash_ts = NULL;
776 - endif;
984 + $crash_ts = $this->crash_ts();
777 985 endif;
778 -
986 +
779 987 // Randomize order for load balancing purposes
780 988 $feed_set = $this->feeds;
781 989 shuffle($feed_set);
782 990
991 + $feed_set = apply_filters('feedwordpress_update_feeds', $feed_set, $uri);
992 +
783 993 // Loop through and check for new posts
784 994 $delta = NULL;
785 995 foreach ($feed_set as $feed) :
786 996 if (!is_null($crash_ts) and (time() > $crash_ts)) : // Check whether we've exceeded the time limit
@@ -786,9 +996,9 @@
786 996 if (!is_null($crash_ts) and (time() > $crash_ts)) : // Check whether we've exceeded the time limit
787 997 break;
788 998 endif;
789 999
790 - $pinged_that = (is_null($uri) or in_array($uri, array($feed->uri(), $feed->homepage())));
1000 + $pinged_that = (is_null($uri) or ($uri=='*') or in_array($uri, array($feed->uri(), $feed->homepage())));
791 1001
792 1002 if (!is_null($uri)) : // A site-specific ping always updates
793 1003 $timely = true;
794 1004 else :
@@ -794,9 +1004,9 @@
794 1004 else :
795 1005 $timely = $feed->stale();
796 1006 endif;
797 1007
798 - 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...
799 1009 $delta = array('new' => 0, 'updated' => 0); // ... don't return error condition
800 1010 endif;
801 1011
802 1012 if ($pinged_that and $timely) :
@@ -804,10 +1014,12 @@
804 1014 $start_ts = time();
805 1015 $added = $feed->poll($crash_ts);
806 1016 do_action('feedwordpress_check_feed_complete', $feed->settings, $added, time() - $start_ts);
807 1017
808 - if (isset($added['new'])) : $delta['new'] += $added['new']; endif;
809 - 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;
810 1022 endif;
811 1023 endforeach;
812 1024
813 1025 do_action('feedwordpress_update_complete', $delta);
@@ -814,10 +1026,23 @@
814 1026
815 1027 return $delta;
816 1028 }
817 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 +
818 1040 function stale () {
819 1041 if (get_option('feedwordpress_automatic_updates')) :
1042 + // Do our best to avoid possible simultaneous
1043 + // updates by getting up-to-the-minute settings.
1044 +
820 1045 $last = get_option('feedwordpress_last_update_all');
821 1046
822 1047 // If we haven't updated all yet, give it a time window
823 1048 if (false === $last) :
@@ -833,73 +1058,208 @@
833 1058 $ret = ( (time() - $last) > $freshness);
834 1059
835 1060 // This should never happen.
836 1061 else :
837 - FeedWordPress::critical_bug('FeedWordPress::stale::last', $last, __LINE__);
1062 + FeedWordPress::critical_bug('FeedWordPress::stale::last', $last, __LINE__, __FILE__);
838 1063 endif;
839 1064
840 - // Explicit request for an update (e.g. from a cron job).
841 - elseif (FeedWordPress::update_requested()) :
842 - $ret = true;
843 -
844 1065 else :
845 1066 $ret = false;
846 1067 endif;
847 1068 return $ret;
848 1069 } // FeedWordPress::stale()
1070 +
1071 + function init () {
1072 + $this->clear_cache_magic_url();
1073 + $this->update_magic_url();
1074 + } /* FeedWordPress::init() */
849 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 +
850 1184 function update_requested () {
851 - return (isset($_REQUEST['update_feedwordpress']) and $_REQUEST['update_feedwordpress']);
1185 + return (
1186 + isset($_REQUEST['update_feedwordpress'])
1187 + and $_REQUEST['update_feedwordpress']
1188 + );
852 1189 } // FeedWordPress::update_requested()
853 1190
854 - function syndicate_link ($name, $uri, $rss) {
1191 + function update_requested_url () {
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;
1198 +
1199 + return $ret;
1200 + } // FeedWordPress::update_requested_url()
1201 +
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') {
855 1209 global $wpdb;
856 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) {
857 1222 // Get the category ID#
858 1223 $cat_id = FeedWordPress::link_category_id();
859 -
1224 + if (!is_wp_error($cat_id)) :
1225 + $link_category = array($cat_id);
1226 + else :
1227 + $link_category = array();
1228 + endif;
1229 +
860 1230 // WordPress gets cranky if there's no homepage URI
861 - if (!isset($uri) or strlen($uri)<1) : $uri = $rss; endif;
1231 + if (!is_string($uri) or strlen($uri)<1) : $uri = $rss; endif;
862 1232
863 - if (function_exists('wp_insert_link')) { // WordPress 2.x
864 - $link_id = wp_insert_link(array(
865 - "link_name" => $name,
866 - "link_url" => $uri,
867 - "link_category" => (fwp_test_wp_version(0, FWP_SCHEMA_21) ? $cat_id : array($cat_id)),
868 - "link_rss" => $rss
869 - ));
870 - } else { // WordPress 1.5.x
871 - $result = $wpdb->query("
872 - INSERT INTO $wpdb->links
873 - SET
874 - link_name = '".$wpdb->escape($name)."',
875 - link_url = '".$wpdb->escape($uri)."',
876 - link_category = '".$wpdb->escape($cat_id)."',
877 - link_rss = '".$wpdb->escape($rss)."'
878 - ");
879 - $link_id = $wpdb->insert_id;
880 - } // if
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 + ));
1242 +
881 1243 return $link_id;
882 - } // function FeedWordPress::syndicate_link()
1244 + } /* function FeedWordPress::syndicate_link() */
883 1245
884 - function on_unfamiliar ($what = 'author', $override = NULL) {
885 - $set = array(
886 - 'author' => array('create', 'default', 'filter'),
887 - 'category' => array('create', 'tag', 'default', 'filter'),
888 - );
889 -
890 - if (is_string($override)) :
891 - $ret = strtolower($override);
1246 + /*static*/ function syndicated_status ($what, $default) {
1247 + $ret = get_option("feedwordpress_syndicated_{$what}_status");
1248 + if (!$ret) :
1249 + $ret = $default;
892 1250 endif;
1251 + return $ret;
1252 + } /* FeedWordPress::syndicated_status() */
893 1253
894 - if (!is_numeric($override) and !in_array($ret, $set[$what])) :
895 - $ret = get_option('feedwordpress_unfamiliar_'.$what);
896 - if (!is_numeric($ret) and !in_array($ret, $set[$what])) :
897 - $ret = 'create';
898 - endif;
899 - endif;
900 -
901 - return $ret;
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;
1260 +
1261 + return get_option('feedwordpress_unfamiliar_'.$what, 'create'.$suffix);
902 1262 } // function FeedWordPress::on_unfamiliar()
903 1263
904 1264 function null_email_set () {
905 1265 $base = get_option('feedwordpress_null_email_set');
@@ -925,14 +1285,28 @@
925 1285 $ret = get_option('feedwordpress_use_aggregator_source_data');
926 1286 return apply_filters('syndicated_post_use_aggregator_source_data', ($ret=='yes'));
927 1287 }
928 1288
929 - 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()) {
930 1301 $contributors = FeedWordPress::link_category_id();
931 - if (function_exists('get_bookmarks')) :
932 - $links = get_bookmarks(array("category" => $contributors));
933 - else:
934 - $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();
935 1309 endif;
936 1310 return $links;
937 1311 } // function FeedWordPress::syndicated_links()
938 1312
@@ -940,61 +1314,32 @@
940 1314 global $wpdb, $wp_db_version;
941 1315
942 1316 $cat_id = get_option('feedwordpress_cat_id');
943 1317
944 - // If we don't yet *have* the category, we'll have to create it
945 - if ($cat_id === false) :
946 - $cat = $wpdb->escape(DEFAULT_SYNDICATION_CATEGORY);
947 -
948 - // Look for something with the right name...
949 - // -----------------------------------------
1318 + // If we don't yet have the category ID stored, search by name
1319 + if (!$cat_id) :
1320 + $cat_id = FeedWordPressCompatibility::link_category_id(DEFAULT_SYNDICATION_CATEGORY);
950 1321
951 - // WordPress 2.3 introduces a new taxonomy/term API
952 - if (function_exists('is_term')) :
953 - $cat_id = is_term($cat, 'link_category');
954 - // WordPress 2.1 and 2.2 use a common table for both link and post categories
955 - elseif (fwp_test_wp_version(FWP_SCHEMA_21, FWP_SCHEMA_23)) :
956 - $cat_id = $wpdb->get_var("SELECT cat_id FROM {$wpdb->categories} WHERE cat_name='$cat'");
957 - // WordPress 1.5 and 2.0.x have a separate table for link categories
958 - elseif (fwp_test_wp_version(0, FWP_SCHEMA_21)):
959 - $cat_id = $wpdb->get_var("SELECT cat_id FROM {$wpdb->linkcategories} WHERE cat_name='$cat'");
960 - // This should never happen.
961 - else :
962 - FeedWordPress::critical_bug('FeedWordPress::link_category_id::wp_db_version', $wp_db_version, __LINE__);
1322 + if ($cat_id) :
1323 + // We found it; let's stamp it.
1324 + update_option('feedwordpress_cat_id', $cat_id);
963 1325 endif;
964 1326
965 - // If you still can't find anything, make it for yourself.
966 - // -------------------------------------------------------
967 - if (!$cat_id) :
968 - // WordPress 2.3+ term/taxonomy API
969 - if (function_exists('wp_insert_term')) :
970 - $term = wp_insert_term($cat, 'link_category');
971 - $cat_id = $term['term_id'];
972 - // WordPress 2.1, 2.2 category API. By the way, why the fuck is this API function only available in a wp-admin module?
973 - elseif (function_exists('wp_insert_category') and !fwp_test_wp_version(FWP_SCHEMA_20, FWP_SCHEMA_21)) :
974 - $cat_id = wp_insert_category(array('cat_name' => $cat));
975 - // WordPress 1.5 and 2.0.x
976 - elseif (fwp_test_wp_version(0, FWP_SCHEMA_21)) :
977 - $result = $wpdb->query("
978 - INSERT INTO $wpdb->linkcategories
979 - SET
980 - cat_id = 0,
981 - cat_name='$cat',
982 - show_images='N',
983 - show_description='N',
984 - show_rating='N',
985 - show_updated='N',
986 - sort_order='name'
987 - ");
988 - $cat_id = $wpdb->insert_id;
989 - // This should never happen.
990 - else :
991 - FeedWordPress::critical_bug('FeedWordPress::link_category_id::wp_db_version', $wp_db_version, __LINE__);
992 - endif;
1327 + // If we *do* have the category ID stored, verify that it exists
1328 + else :
1329 + $cat_id = FeedWordPressCompatibility::link_category_id((int) $cat_id, 'cat_id');
1330 + endif;
1331 +
1332 + // If we could not find an appropriate link category,
1333 + // make a new one for ourselves.
1334 + if (!$cat_id) :
1335 + $cat_id = FeedWordPressCompatibility::insert_link_category(DEFAULT_SYNDICATION_CATEGORY);
1336 + if (!is_wp_error($cat_id)) :
1337 + // Stamp it
1338 + update_option('feedwordpress_cat_id', $cat_id);
993 1339 endif;
1340 + endif;
994 1341
995 - update_option('feedwordpress_cat_id', $cat_id);
996 - endif;
997 1342 return $cat_id;
998 1343 } // function FeedWordPress::link_category_id()
999 1344
1000 1345 # Upgrades and maintenance...
@@ -1018,15 +1363,47 @@
1018 1363 update_option('feedwordpress_version', FEEDWORDPRESS_VERSION);
1019 1364 endif;
1020 1365 elseif ($fwp_db_version < 2009.0707) :
1021 1366 // We need to clear out any busted AJAX crap
1022 - if (fwp_test_wp_version(FWP_SCHEMA_HAS_USERMETA)) :
1023 - $wpdb->query("
1024 - DELETE FROM $wpdb->usermeta
1025 - WHERE LOCATE('feedwordpress', meta_key)
1026 - AND LOCATE('box', meta_key);
1027 - ");
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');
1028 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;
1029 1406 update_option('feedwordpress_version', FEEDWORDPRESS_VERSION);
1030 1407 else :
1031 1408 // No. Just brand it with the new version.
1032 1409 update_option('feedwordpress_version', FEEDWORDPRESS_VERSION);
@@ -1040,56 +1417,14 @@
1040 1417
1041 1418 if (is_null($from) or $from <= 0.96) : $from = 0.96; endif;
1042 1419
1043 1420 switch ($from) :
1044 - case 0.96: // account for changes to syndication custom values and guid
1045 - 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.
1046 1426
1047 - $cat_id = FeedWordPress::link_category_id();
1048 -
1049 - // Avoid duplicates
1050 - $wpdb->query("DELETE FROM `{$wpdb->postmeta}` WHERE meta_key = 'syndication_feed_id'");
1051 -
1052 - // Look up all the link IDs
1053 - $wpdb->query("
1054 - CREATE TEMPORARY TABLE tmp_custom_values
1055 - SELECT
1056 - NULL AS meta_id,
1057 - post_id,
1058 - 'syndication_feed_id' AS meta_key,
1059 - link_id AS meta_value
1060 - FROM `{$wpdb->postmeta}`, `{$wpdb->links}`
1061 - WHERE
1062 - meta_key='syndication_feed'
1063 - AND meta_value=link_rss
1064 - AND link_category = {$cat_id}
1065 - ");
1066 -
1067 - // Now attach them to their posts
1068 - $wpdb->query("INSERT INTO `{$wpdb->postmeta}` SELECT * FROM tmp_custom_values");
1069 -
1070 - // And clean up after ourselves.
1071 - $wpdb->query("DROP TABLE tmp_custom_values");
1072 -
1073 - // Now fix the guids to avoid duplicate posts
1074 - echo "<ul>";
1075 - foreach ($this->feeds as $feed) :
1076 - echo "<li>Fixing post meta-data for <cite>".$feed['link/name']."</cite> &#8230; "; flush();
1077 - $rss = @fetch_rss($feed['link/uri']);
1078 - if (is_array($rss->items)) :
1079 - foreach ($rss->items as $item) :
1080 - $guid = $wpdb->escape(FeedWordPress::guid($item, $feed)); // new GUID algorithm
1081 - $link = $wpdb->escape($item['link']);
1082 -
1083 - $wpdb->query("
1084 - UPDATE `{$wpdb->posts}` SET guid='{$guid}' WHERE guid='{$link}'
1085 - ");
1086 - endforeach;
1087 - endif;
1088 - echo "<strong>complete.</strong></li>\n";
1089 - endforeach;
1090 - echo "</ul>\n";
1091 -
1092 1427 // Mark the upgrade as successful.
1093 1428 update_option('feedwordpress_version', FEEDWORDPRESS_VERSION);
1094 1429 endswitch;
1095 1430 echo "<p>Upgrade complete. FeedWordPress is now ready to use again.</p>";
@@ -1094,8 +1429,27 @@
1094 1429 endswitch;
1095 1430 echo "<p>Upgrade complete. FeedWordPress is now ready to use again.</p>";
1096 1431 } /* FeedWordPress::upgrade_database() */
1097 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 +
1098 1452 function create_guid_index () {
1099 1453 global $wpdb;
1100 1454
1101 1455 $wpdb->query("
@@ -1102,15 +1456,116 @@
1102 1456 CREATE INDEX {$wpdb->posts}_guid_idx ON {$wpdb->posts}(guid)
1103 1457 ");
1104 1458 } /* FeedWordPress::create_guid_index () */
1105 1459
1106 - function magpie_version () {
1107 - if (!defined('MAGPIE_VERSION')) : $magpie_version = $GLOBALS['wp_version'].'-default';
1108 - else : $magpie_version = MAGPIE_VERSION;
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);
1109 1490 endif;
1110 - return $magpie_version;
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 +
1522 + function clear_cache () {
1523 + global $wpdb;
1524 +
1525 + // Just in case, clear out any old MagpieRSS cache records.
1526 + $magpies = $wpdb->query("
1527 + DELETE FROM {$wpdb->options}
1528 + WHERE option_name LIKE 'rss_%' AND LENGTH(option_name) > 32
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);
1545 + } /* FeedWordPress::clear_cache () */
1546 +
1547 + function cache_duration () {
1548 + $duration = NULL;
1549 + if (defined('FEEDWORDPRESS_CACHE_AGE')) :
1550 + $duration = FEEDWORDPRESS_CACHE_AGE;
1551 + endif;
1552 + return $duration;
1111 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;
1112 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 +
1113 1568 # Utility functions for handling text settings
1114 1569 function negative ($f, $setting) {
1115 1570 $nego = array ('n', 'no', 'f', 'false');
1116 1571 return (isset($f[$setting]) and in_array(strtolower($f[$setting]), $nego));
@@ -1120,67 +1575,600 @@
1120 1575 $affirmo = array ('y', 'yes', 't', 'true', 1);
1121 1576 return (isset($f[$setting]) and in_array(strtolower($f[$setting]), $affirmo));
1122 1577 }
1123 1578
1124 -
1125 1579 # Internal debugging functions
1126 - function critical_bug ($varname, $var, $line) {
1580 + function critical_bug ($varname, $var, $line, $file = NULL) {
1127 1581 global $wp_version;
1582 +
1583 + if (!is_null($file)) :
1584 + $location = "line # ${line} of ".basename($file);
1585 + else :
1586 + $location = "line # ${line}";
1587 + endif;
1588 +
1589 + print '<p><strong>Critical error:</strong> There may be a bug in FeedWordPress. Please <a href="'.FEEDWORDPRESS_AUTHOR_CONTACT.'">contact the author</a> and paste the following information into your e-mail:</p>';
1590 + print "\n<plaintext>";
1591 + print "Triggered at ${location}\n";
1592 + print "FeedWordPress: ".FEEDWORDPRESS_VERSION."\n";
1593 + print "WordPress: {$wp_version}\n";
1594 + print "PHP: ".phpversion()."\n";
1595 + print "Error data: ";
1596 + print $varname.": "; var_dump($var); echo "\n";
1597 + die;
1598 + }
1599 +
1600 + function noncritical_bug ($varname, $var, $line, $file = NULL) {
1601 + if (FEEDWORDPRESS_DEBUG) : // halt only when we are doing debugging
1602 + FeedWordPress::critical_bug($varname, $var, $line, $file);
1603 + endif;
1604 + }
1605 +
1606 + function val ($v, $no_newlines = false) {
1607 + ob_start();
1608 + var_dump($v);
1609 + $out = ob_get_contents(); ob_end_clean();
1610 +
1611 + if ($no_newlines) :
1612 + $out = preg_replace('/\s+/', " ", $out);
1613 + endif;
1614 + return $out;
1615 + } /* FeedWordPress::val () */
1128 1616
1129 - if (defined('MAGPIE_VERSION')) : $mv = MAGPIE_VERSION;
1130 - else : $mv = 'WordPress '.$wp_version.' default.';
1617 + function diagnostic_on ($level) {
1618 + $show = get_option('feedwordpress_diagnostics_show', array());
1619 + return (in_array($level, $show));
1620 + } /* FeedWordPress::diagnostic_on () */
1621 +
1622 + function diagnostic ($level, $out, $persist = NULL, $since = NULL, $mostRecent = NULL) {
1623 + global $feedwordpress_admin_footer;
1624 +
1625 + $output = get_option('feedwordpress_diagnostics_output', array());
1626 + $dlog = get_option('feedwordpress_diagnostics_log', array());
1627 +
1628 + $diagnostic_nesting = count(explode(":", $level));
1629 +
1630 + if (FeedWordPress::diagnostic_on($level)) :
1631 + foreach ($output as $method) :
1632 + switch ($method) :
1633 + case 'echo' :
1634 + if (!FeedWordPress::update_requested()) :
1635 + echo "<div><pre><strong>Diag".str_repeat('====', $diagnostic_nesting-1).'|</strong> '.$out."</pre></div>";
1636 + endif;
1637 + break;
1638 + case 'echo_in_cronjob' :
1639 + if (FeedWordPress::update_requested()) :
1640 + echo FeedWordPress::log_prefix()." ".$out."\n";
1641 + endif;
1642 + break;
1643 + case 'admin_footer' :
1644 + $feedwordpress_admin_footer[] = $out;
1645 + break;
1646 + case 'error_log' :
1647 + error_log(FeedWordPress::log_prefix().' '.$out);
1648 + break;
1649 + case 'email' :
1650 + if (is_null($persist)) :
1651 + $sect = 'occurrent';
1652 + $hook = (isset($dlog['mesg'][$sect]) ? count($dlog['mesg'][$sect]) : 0);
1653 + $line = array("Time" => time(), "Message" => $out);
1654 + else :
1655 + $sect = 'persistent';
1656 + $hook = md5($level."\n".$persist);
1657 + $line = array("Since" => $since, "Message" => $out, "Most Recent" => $mostRecent);
1658 + endif;
1659 +
1660 + if (!isset($dlog['mesg'])) : $dlog['mesg'] = array(); endif;
1661 + if (!isset($dlog['mesg'][$sect])) : $dlog['mesg'][$sect] = array(); endif;
1662 +
1663 + $dlog['mesg'][$sect][$hook] = $line;
1664 + endswitch;
1665 + endforeach;
1131 1666 endif;
1667 +
1668 + update_option('feedwordpress_diagnostics_log', $dlog);
1669 + } /* FeedWordPress::diagnostic () */
1670 +
1671 + function email_diagnostic_log () {
1672 + $dlog = get_option('feedwordpress_diagnostics_log', array());
1673 +
1674 + if (isset($dlog['schedule']) and isset($dlog['schedule']['last'])) :
1675 + if (time() > ($dlog['schedule']['last'] + $dlog['schedule']['freq'])) :
1676 + // No news is good news; only send if
1677 + // there are some messages to send.
1678 + $body = NULL;
1679 + foreach ($dlog['mesg'] as $sect => $mesgs) :
1680 + if (count($mesgs) > 0) :
1681 + if (is_null($body)) : $body = ''; endif;
1682 +
1683 + $paradigm = reset($mesgs);
1684 + $body .= "<h2>".ucfirst($sect)." issues</h2>\n"
1685 + ."<table>\n"
1686 + ."<thead><tr>\n";
1687 + foreach ($paradigm as $col => $value) :
1688 + $body .= '<th scope="col">'.$col."</th>\n";
1689 + endforeach;
1690 + $body .= "</tr></thead>\n"
1691 + ."<tbody>\n";
1692 +
1693 + foreach ($mesgs as $line) :
1694 + $body .= "<tr>\n";
1695 + foreach ($line as $col => $cell) :
1696 + if (is_numeric($cell)) :
1697 + $cell = date('j-M-y, h:i a', $cell);
1698 + endif;
1699 + $class = strtolower(preg_replace('/\s+/', '-', $col));
1700 + $body .= "<td class=\"$class\">${cell}</td>";
1701 + endforeach;
1702 + $body .= "</tr>\n";
1703 + endforeach;
1704 +
1705 + $body .= "</tbody>\n</table>\n\n";
1706 + endif;
1707 + endforeach;
1708 +
1709 + if (!is_null($body)) :
1710 + $home = feedwordpress_display_url(get_bloginfo('url'));
1711 + $subj = $home . " syndication issues for ".date('j-M-y', time());
1712 + $agent = 'FeedWordPress '.FEEDWORDPRESS_VERSION;
1713 + $body = <<<EOMAIL
1714 +<html>
1715 +<head>
1716 +<title>$subj</title>
1717 +<style type="text/css">
1718 + body { background-color: white; color: black; }
1719 + table { width: 100%; border: 1px solid black; }
1720 + table thead tr th { background-color: #ff7700; color: white; border-bottom: 1px solid black; }
1721 + table thead tr { border-bottom: 1px solid black; }
1722 + table tr { vertical-align: top; }
1723 + table .since { width: 20%; }
1724 + table .time { width: 20%; }
1725 + table .most-recently { width: 20%; }
1726 + table .message { width: auto; }
1727 +</style>
1728 +</head>
1729 +<body>
1730 +<h1>Syndication Issues encountered by $agent on $home</h1>
1731 +$body
1732 +</body>
1733 +</html>
1132 1734
1133 - 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>';
1134 - echo "\n<plaintext>";
1135 - echo "Triggered at line # ".$line."\n";
1136 - echo "FeedWordPress version: ".FEEDWORDPRESS_VERSION."\n";
1137 - echo "MagpieRSS version: {$mv}\n";
1138 - echo "WordPress version: {$wp_version}\n";
1139 - echo "PHP version: ".phpversion()."\n";
1140 - echo "\n";
1141 - echo $varname.": "; var_dump($var); echo "\n";
1142 - die;
1735 +EOMAIL;
1736 +
1737 + $ded = get_option('feedwordpress_diagnostics_email_destination', 'admins');
1738 +
1739 + // e-mail address
1740 + if (preg_match('/^mailto:(.*)$/', $ded, $ref)) :
1741 + $recipients = array($ref[1]);
1742 +
1743 + // userid
1744 + elseif (preg_match('/^user:(.*)$/', $ded, $ref)) :
1745 + $userdata = get_userdata((int) $ref[1]);
1746 + $recipients = array($userdata->user_email);
1747 +
1748 + // admins
1749 + else :
1750 + $recipients = FeedWordPressDiagnostic::admin_emails();
1751 + endif;
1752 +
1753 + foreach ($recipients as $email) :
1754 + add_filter('wp_mail_content_type', array('FeedWordPress', 'allow_html_mail'));
1755 + wp_mail($email, $subj, $body);
1756 + remove_filter('wp_mail_content_type', array('FeedWordPress', 'allow_html_mail'));
1757 + endforeach;
1758 + endif;
1759 +
1760 + // Clear the logs
1761 + $dlog['mesg']['persistent'] = array();
1762 + $dlog['mesg']['occurrent'] = array();
1763 +
1764 + // Set schedule for next update
1765 + $dlog['schedule']['last'] = time();
1766 + endif;
1767 + else :
1768 + $dlog['schedule'] = array(
1769 + 'freq' => 24 /*hr*/ * 60 /*min*/ * 60 /*s*/,
1770 + 'last' => time(),
1771 + );
1772 + endif;
1773 +
1774 + update_option('feedwordpress_diagnostics_log', $dlog);
1775 + } /* FeedWordPress::email_diagnostic_log () */
1776 +
1777 + function allow_html_mail () {
1778 + return 'text/html';
1779 + } /* FeedWordPress::allow_html_mail () */
1780 +
1781 + function admin_footer () {
1782 + global $feedwordpress_admin_footer;
1783 + foreach ($feedwordpress_admin_footer as $line) :
1784 + echo '<div><pre>'.$line.'</pre></div>';
1785 + endforeach;
1786 + } /* FeedWordPress::admin_footer () */
1787 +
1788 + function log_prefix ($date = false) {
1789 + $home = get_bloginfo('url');
1790 + $prefix = '['.feedwordpress_display_url($home).'] [feedwordpress] ';
1791 + if ($date) :
1792 + $prefix = "[".date('Y-m-d H:i:s')."]".$prefix;
1793 + endif;
1794 + return $prefix;
1795 + } /* FeedWordPress::log_prefix () */
1796 +
1797 + function menu_cap ($sub = false) {
1798 + if ($sub) :
1799 + $cap = apply_filters('feedwordpress_menu_settings_capacity', 'manage_options');
1800 + else :
1801 + $cap = apply_filters('feedwordpress_menu_main_capacity', 'manage_links');
1802 + endif;
1803 + return $cap;
1804 + } /* FeedWordPress::menu_cap () */
1805 +
1806 + function path ($filename = '') {
1807 + global $fwp_path;
1808 +
1809 + $path = $fwp_path;
1810 + if (strlen($filename) > 0) :
1811 + $path .= '/'.$filename;
1812 + endif;
1813 + return $path;
1143 1814 }
1144 1815
1145 - function noncritical_bug ($varname, $var, $line) {
1146 - if (FEEDWORDPRESS_DEBUG) : // halt only when we are doing debugging
1147 - FeedWordPress::critical_bug($varname, $var, $line);
1816 + function param ($key, $type = 'REQUEST', $default = NULL) {
1817 + $where = '_'.strtoupper($type);
1818 + $ret = $default;
1819 + if (isset($GLOBALS[$where]) and is_array($GLOBALS[$where])) :
1820 + if (isset($GLOBALS[$where][$key])) :
1821 + $ret = $GLOBALS[$where][$key];
1822 + if (get_magic_quotes_gpc()) :
1823 + $ret = stripslashes_deep($ret);
1824 + endif;
1825 + endif;
1148 1826 endif;
1827 + return $ret;
1149 1828 }
1829 + function post ($key, $default = NULL) {
1830 + return FeedWordPress::param($key, 'POST');
1831 + }
1150 1832 } // class FeedWordPress
1151 1833
1152 -require_once(dirname(__FILE__) . '/syndicatedpost.class.php');
1153 -require_once(dirname(__FILE__) . '/syndicatedlink.class.php');
1834 +class FeedWordPress_File extends WP_SimplePie_File {
1835 + function FeedWordPress_File ($url, $timeout = 10, $redirects = 5, $headers = null, $useragent = null, $force_fsockopen = false) {
1836 + WP_SimplePie_File::WP_SimplePie_File($url, $timeout, $redirects, $headers, $useragent, $force_fsockopen);
1154 1837
1838 + // SimplePie makes a strongly typed check against integers with
1839 + // this, but WordPress puts a string in. Which causes caching
1840 + // to break and fall on its ass when SimplePie is getting a 304,
1841 + // but doesn't realize it because this member is "304" instead.
1842 + $this->status_code = (int) $this->status_code;
1843 + }
1844 +} /* class FeedWordPress_File () */
1845 +
1846 +class FeedWordPress_Parser extends SimplePie_Parser {
1847 + function parse (&$data, $encoding) {
1848 + // Use UTF-8 if we get passed US-ASCII, as every US-ASCII character is a UTF-8 character
1849 + if (strtoupper($encoding) === 'US-ASCII')
1850 + {
1851 + $this->encoding = 'UTF-8';
1852 + }
1853 + else
1854 + {
1855 + $this->encoding = $encoding;
1856 + }
1857 +
1858 + // Strip BOM:
1859 + // UTF-32 Big Endian BOM
1860 + if (substr($data, 0, 4) === "\x00\x00\xFE\xFF")
1861 + {
1862 + $data = substr($data, 4);
1863 + }
1864 + // UTF-32 Little Endian BOM
1865 + elseif (substr($data, 0, 4) === "\xFF\xFE\x00\x00")
1866 + {
1867 + $data = substr($data, 4);
1868 + }
1869 + // UTF-16 Big Endian BOM
1870 + elseif (substr($data, 0, 2) === "\xFE\xFF")
1871 + {
1872 + $data = substr($data, 2);
1873 + }
1874 + // UTF-16 Little Endian BOM
1875 + elseif (substr($data, 0, 2) === "\xFF\xFE")
1876 + {
1877 + $data = substr($data, 2);
1878 + }
1879 + // UTF-8 BOM
1880 + elseif (substr($data, 0, 3) === "\xEF\xBB\xBF")
1881 + {
1882 + $data = substr($data, 3);
1883 + }
1884 +
1885 + if (substr($data, 0, 5) === '<?xml' && strspn(substr($data, 5, 1), "\x09\x0A\x0D\x20") && ($pos = strpos($data, '?>')) !== false)
1886 + {
1887 + $declaration =& new SimplePie_XML_Declaration_Parser(substr($data, 5, $pos - 5));
1888 + if ($declaration->parse())
1889 + {
1890 + $data = substr($data, $pos + 2);
1891 + $data = '<?xml version="' . $declaration->version . '" encoding="' . $encoding . '" standalone="' . (($declaration->standalone) ? 'yes' : 'no') . '"?>' . $data;
1892 + }
1893 + else
1894 + {
1895 + $this->error_string = 'SimplePie bug! Please report this!';
1896 + return false;
1897 + }
1898 + }
1899 +
1900 + $return = true;
1901 +
1902 + static $xml_is_sane = null;
1903 + if ($xml_is_sane === null)
1904 + {
1905 + $parser_check = xml_parser_create();
1906 + xml_parse_into_struct($parser_check, '<foo>&amp;</foo>', $values);
1907 + xml_parser_free($parser_check);
1908 + $xml_is_sane = isset($values[0]['value']);
1909 + }
1910 +
1911 + // Create the parser
1912 + if ($xml_is_sane)
1913 + {
1914 + $xml = xml_parser_create_ns($this->encoding, $this->separator);
1915 + xml_parser_set_option($xml, XML_OPTION_SKIP_WHITE, 1);
1916 + xml_parser_set_option($xml, XML_OPTION_CASE_FOLDING, 0);
1917 + xml_set_object($xml, $this);
1918 + xml_set_character_data_handler($xml, 'cdata');
1919 + xml_set_element_handler($xml, 'tag_open', 'tag_close');
1920 + xml_set_start_namespace_decl_handler($xml, 'start_xmlns');
1921 +
1922 + // Parse!
1923 + if (!xml_parse($xml, $data, true))
1924 + {
1925 + $this->error_code = xml_get_error_code($xml);
1926 + $this->error_string = xml_error_string($this->error_code);
1927 + $return = false;
1928 + }
1929 +
1930 + $this->current_line = xml_get_current_line_number($xml);
1931 + $this->current_column = xml_get_current_column_number($xml);
1932 + $this->current_byte = xml_get_current_byte_index($xml);
1933 + xml_parser_free($xml);
1934 +
1935 + return $return;
1936 + }
1937 + else
1938 + {
1939 + libxml_clear_errors();
1940 + $xml =& new XMLReader();
1941 + $xml->xml($data);
1942 + while (@$xml->read())
1943 + {
1944 + switch ($xml->nodeType)
1945 + {
1946 +
1947 + case constant('XMLReader::END_ELEMENT'):
1948 + if ($xml->namespaceURI !== '')
1949 + {
1950 + $tagName = "{$xml->namespaceURI}{$this->separator}{$xml->localName}";
1951 + }
1952 + else
1953 + {
1954 + $tagName = $xml->localName;
1955 + }
1956 + $this->tag_close(null, $tagName);
1957 + break;
1958 + case constant('XMLReader::ELEMENT'):
1959 + $empty = $xml->isEmptyElement;
1960 + if ($xml->namespaceURI !== '')
1961 + {
1962 + $tagName = "{$xml->namespaceURI}{$this->separator}{$xml->localName}";
1963 + }
1964 + else
1965 + {
1966 + $tagName = $xml->localName;
1967 + }
1968 + $attributes = array();
1969 + while ($xml->moveToNextAttribute())
1970 + {
1971 + if ($xml->namespaceURI !== '')
1972 + {
1973 + $attrName = "{$xml->namespaceURI}{$this->separator}{$xml->localName}";
1974 + }
1975 + else
1976 + {
1977 + $attrName = $xml->localName;
1978 + }
1979 + $attributes[$attrName] = $xml->value;
1980 + }
1981 +
1982 + foreach ($attributes as $attr => $value) :
1983 + list($ns, $local) = $this->split_ns($attr);
1984 + if ($ns=='http://www.w3.org/2000/xmlns/') :
1985 + if ('xmlns' == $local) : $local = false; endif;
1986 + $this->start_xmlns(null, $local, $value);
1987 + endif;
1988 + endforeach;
1989 +
1990 + $this->tag_open(null, $tagName, $attributes);
1991 + if ($empty)
1992 + {
1993 + $this->tag_close(null, $tagName);
1994 + }
1995 + break;
1996 + case constant('XMLReader::TEXT'):
1997 +
1998 + case constant('XMLReader::CDATA'):
1999 + $this->cdata(null, $xml->value);
2000 + break;
2001 + }
2002 + }
2003 + if ($error = libxml_get_last_error())
2004 + {
2005 + $this->error_code = $error->code;
2006 + $this->error_string = $error->message;
2007 + $this->current_line = $error->line;
2008 + $this->current_column = $error->column;
2009 + return false;
2010 + }
2011 + else
2012 + {
2013 + return true;
2014 + }
2015 + }
2016 + } /* FeedWordPress_Parser::parse() */
2017 +
2018 + var $xmlns_stack = array();
2019 + var $xmlns_current = array();
2020 + function tag_open ($parser, $tag, $attributes) {
2021 + $ret = parent::tag_open($parser, $tag, $attributes);
2022 + if ($this->current_xhtml_construct < 0) :
2023 + $this->data['xmlns'] = $this->xmlns_current;
2024 + $this->xmlns_stack[] = $this->xmlns_current;
2025 + endif;
2026 + return $ret;
2027 + }
2028 +
2029 + function tag_close($parser, $tag) {
2030 + if ($this->current_xhtml_construct < 0) :
2031 + $this->xmlns_current = array_pop($this->xmlns_stack);
2032 + endif;
2033 + $ret = parent::tag_close($parser, $tag);
2034 + return $ret;
2035 + }
2036 +
2037 + function start_xmlns ($parser, $prefix, $uri) {
2038 + if (!$prefix) :
2039 + $prefix = '';
2040 + endif;
2041 + if ($this->current_xhtml_construct < 0) :
2042 + $this->xmlns_current[$prefix] = $uri;
2043 + endif;
2044 + return true;
2045 + } /* FeedWordPress_Parser::start_xmlns() */
2046 +}
2047 +
2048 +$feedwordpress_admin_footer = array();
2049 +
1155 2050 ################################################################################
1156 2051 ## XML-RPC HOOKS: accept XML-RPC update pings from Contributors ################
1157 2052 ################################################################################
1158 2053
1159 -function feedwordpress_xmlrpc_hook ($args = array ()) {
1160 - $args['weblogUpdates.ping'] = 'feedwordpress_pong';
1161 - return $args;
1162 -}
2054 +class FeedWordPressRPC {
2055 + function FeedWordPressRPC () {
2056 + add_filter('xmlrpc_methods', array(&$this, 'xmlrpc_methods'));
2057 + }
2058 +
2059 + function xmlrpc_methods ($args = array()) {
2060 + $args['weblogUpdates.ping'] = array(&$this, 'ping');
2061 + $args['feedwordpress.subscribe'] = array(&$this, 'subscribe');
2062 + $args['feedwordpress.deactivate'] = array(&$this, 'deactivate');
2063 + $args['feedwordpress.delete'] = array(&$this, 'delete');
2064 + $args['feedwordpress.nuke'] = array(&$this, 'nuke');
2065 + return $args;
2066 + }
2067 +
2068 + function ping ($args) {
2069 + global $feedwordpress;
2070 +
2071 + $delta = @$feedwordpress->update($args[1]);
2072 + if (is_null($delta)):
2073 + return array('flerror' => true, 'message' => "Sorry. I don't syndicate <$args[1]>.");
2074 + else:
2075 + $mesg = array();
2076 + if (isset($delta['new'])) { $mesg[] = ' '.$delta['new'].' new posts were syndicated'; }
2077 + if (isset($delta['updated'])) { $mesg[] = ' '.$delta['updated'].' existing posts were updated'; }
2078 +
2079 + return array('flerror' => false, 'message' => "Thanks for the ping.".implode(' and', $mesg));
2080 + endif;
2081 + }
2082 +
2083 + function validate (&$args) {
2084 + global $wp_xmlrpc_server;
1163 2085
1164 -function feedwordpress_pong ($args) {
1165 - $feedwordpress =& new FeedWordPress;
1166 - $delta = @$feedwordpress->update($args[1]);
1167 - if (is_null($delta)):
1168 - return array('flerror' => true, 'message' => "Sorry. I don't syndicate <$args[1]>.");
1169 - else:
1170 - $mesg = array();
1171 - if (isset($delta['new'])) { $mesg[] = ' '.$delta['new'].' new posts were syndicated'; }
1172 - if (isset($delta['updated'])) { $mesg[] = ' '.$delta['updated'].' existing posts were updated'; }
2086 + // First two params are username/password
2087 + $username = $wp_xmlrpc_server->escape(array_shift($args));
2088 + $password = $wp_xmlrpc_server->escape(array_shift($args));
1173 2089
1174 - return array('flerror' => false, 'message' => "Thanks for the ping.".implode(' and', $mesg));
1175 - endif;
1176 -}
2090 + $ret = array();
2091 + if ( !$user = $wp_xmlrpc_server->login($username, $password) ) :
2092 + $ret = $wp_xmlrpc_server->error;
2093 + elseif (!current_user_can('manage_links')) :
2094 + $ret = new IXR_Error(401, 'Sorry, you cannot change the subscription list.');
2095 + endif;
2096 + return $ret;
2097 + }
1177 2098
1178 -# The upgraded MagpieRSS also uses this class. So if we have it loaded
1179 -# in, don't load it again.
1180 -if (!class_exists('Relative_URI')) {
1181 - require_once(dirname(__FILE__) . '/relative_uri.class.php');
1182 -}
2099 + function subscribe ($args) {
2100 + $ret = $this->validate($args);
2101 + if (is_array($ret)) : // Success
2102 + // The remaining params are feed URLs
2103 + foreach ($args as $arg) :
2104 + $finder = new FeedFinder($arg, /*verify=*/ false, /*fallbacks=*/ 1);
2105 + $feeds = array_values(array_unique($finder->find()));
2106 +
2107 + if (count($feeds) > 0) :
2108 + $link_id = FeedWordPress::syndicate_link(
2109 + /*title=*/ feedwordpress_display_url($feeds[0]),
2110 + /*homepage=*/ $feeds[0],
2111 + /*feed=*/ $feeds[0]
2112 + );
2113 + $ret[] = array(
2114 + 'added',
2115 + $feeds[0],
2116 + $arg,
2117 + );
2118 + else :
2119 + $ret[] = array(
2120 + 'error',
2121 + $arg
2122 + );
2123 + endif;
2124 + endforeach;
2125 + endif;
2126 + return $ret;
2127 + } /* FeedWordPressRPC::subscribe () */
2128 +
2129 + function unsubscribe ($method, $args) {
2130 + $ret = $this->validate($args);
2131 + if (is_array($ret)) : // Success
2132 + // The remaining params are feed URLs
2133 + foreach ($args as $arg) :
2134 + $link_id = FeedWordPress::find_link($arg);
2135 +
2136 + if (!$link_id) :
2137 + $link_id = FeedWordPress::find_link($arg, 'link_url');
2138 + endif;
2139 +
2140 + if ($link_id) :
2141 + $link = new SyndicatedLink($link_id);
2142 +
2143 + $link->{$method}();
2144 + $ret[] = array(
2145 + 'deactivated',
2146 + $arg,
2147 + );
2148 + else :
2149 + $ret[] = array(
2150 + 'error',
2151 + $arg,
2152 + );
2153 + endif;
2154 + endforeach;
2155 + endif;
2156 + return $ret;
2157 + } /* FeedWordPress::unsubscribe () */
2158 +
2159 + function deactivate ($args) {
2160 + return $this->unsubscribe('deactivate', $args);
2161 + } /* FeedWordPressRPC::deactivate () */
2162 +
2163 + function delete ($args) {
2164 + return $this->unsubscribe('delete', $args);
2165 + } /* FeedWordPressRPC::delete () */
2166 +
2167 + function nuke ($args) {
2168 + return $this->unsubscribe('nuke', $args);
2169 + } /* FeedWordPressRPC::nuke () */
2170 +} /* class FeedWordPressRPC */
1183 2171
1184 2172 // take your best guess at the realname and e-mail, given a string
1185 2173 define('FWP_REGEX_EMAIL_ADDY', '([^@"(<\s]+@[^"@(<\s]+\.[^"@(<\s]+)');
1186 2174 define('FWP_REGEX_EMAIL_NAME', '("([^"]*)"|([^"<(]+\S))');