PluginProbe
FeedWordPress / 2011.0211.2
FeedWordPress v2011.0211.2
trunk 0.8 0.9 0.91 0.95 0.96 0.97 0.98 0.981 0.99 0.991 0.992 0.993 2008.1030 2008.1101 2008.1105 2008.1214 2009.0612 2009.0613 2009.0618 2009.0707 2009.1111 2009.1112 2010.0127 2010.0528 All 65 releases
← All changes | feedwordpress.php +1562 -2772 0.9912011.0211.2 View file →
@@ -1,38 +1,60 @@
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: 0.991
6 +Version: 2011.0211.2
7 7 Author: Charles Johnson
8 8 Author URI: http://radgeek.com/
9 9 License: GPL
10 -Last modified: 2007-09-25 12:48 PDT
11 10 */
12 11
12 +/**
13 + * @package FeedWordPress
14 + * @version 2011.0211.2
15 + */
16 +
13 17 # This uses code derived from:
14 18 # - wp-rss-aggregate.php by Kellan Elliot-McCrea <kellan@protest.net>
15 -# - HTTP Navigator 2 by Keyvan Minoukadeh <keyvan@k1m.com>
19 +# - MagpieRSS by Kellan Elliot-McCrea <kellan@protest.net>
16 20 # - Ultra-Liberal Feed Finder by Mark Pilgrim <mark@diveintomark.org>
21 +# - WordPress Blog Tool and Publishing Platform <http://wordpress.org/>
17 22 # according to the terms of the GNU General Public License.
18 23 #
19 -# INSTALLATION: see README.text or <http://projects.radgeek.com/install>
24 +# INSTALLATION: see readme.txt or <http://projects.radgeek.com/install>
20 25 #
21 26 # USAGE: once FeedWordPress is installed, you manage just about everything from
22 -# the WordPress Dashboard, under Options --> Syndication or Links --> Syndicated
23 -# To ensure that fresh content is added as it becomes available, get your
24 -# contributors to put your XML-RPC URI (if WordPress is installed at
27 +# the WordPress Dashboard, under the Syndication menu. To ensure that fresh
28 +# content is added as it becomes available, you can convince your contributors
29 +# to put your XML-RPC URI (if WordPress is installed at
25 30 # <http://www.zyx.com/blog>, XML-RPC requests should be sent to
26 -# <http://www.zyx.com/blog/xmlrpc.php>), or see `update-feeds.php`
31 +# <http://www.zyx.com/blog/xmlrpc.php>), or update manually under the
32 +# Syndication menu, or set up automatic updates under Syndication --> Settings,
33 +# or use a cron job.
27 34
28 35 # -- Don't change these unless you know what you're doing...
29 36
30 -define ('FEEDWORDPRESS_VERSION', '0.991');
37 +define ('FEEDWORDPRESS_VERSION', '2011.0211.2');
31 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
32 45 define ('DEFAULT_SYNDICATION_CATEGORY', 'Contributors');
46 +define ('DEFAULT_UPDATE_PERIOD', 60); // value in minutes
33 47
34 -define ('FEEDWORDPRESS_DEBUG', false);
48 +if (isset($_REQUEST['feedwordpress_debug'])) :
49 + $feedwordpress_debug = $_REQUEST['feedwordpress_debug'];
50 +else :
51 + $feedwordpress_debug = get_option('feedwordpress_debug');
52 + if (is_string($feedwordpress_debug)) :
53 + $feedwordpress_debug = ($feedwordpress_debug == 'yes');
54 + endif;
55 +endif;
56 +define ('FEEDWORDPRESS_DEBUG', $feedwordpress_debug);
35 57
36 58 define ('FEEDWORDPRESS_CAT_SEPARATOR_PATTERN', '/[:\n]/');
37 59 define ('FEEDWORDPRESS_CAT_SEPARATOR', "\n");
38 60
@@ -39,17 +61,24 @@
39 61 define ('FEEDVALIDATOR_URI', 'http://feedvalidator.org/check.cgi');
40 62
41 63 define ('FEEDWORDPRESS_FRESHNESS_INTERVAL', 10*60); // Every ten minutes
42 64
65 +define ('FWP_SCHEMA_HAS_USERMETA', 2966);
66 +define ('FWP_SCHEMA_USES_ARGS_TAXONOMY', 12694); // Revision # for using $args['taxonomy'] to get link categories
43 67 define ('FWP_SCHEMA_20', 3308); // Database schema # for WP 2.0
44 68 define ('FWP_SCHEMA_21', 4772); // Database schema # for WP 2.1
45 69 define ('FWP_SCHEMA_23', 5495); // Database schema # for WP 2.3
70 +define ('FWP_SCHEMA_25', 7558); // Database schema # for WP 2.5
71 +define ('FWP_SCHEMA_26', 8201); // Database schema # for WP 2.6
72 +define ('FWP_SCHEMA_27', 9872); // Database schema # for WP 2.7
73 +define ('FWP_SCHEMA_28', 11548); // Database schema # for WP 2.8
74 +define ('FWP_SCHEMA_29', 12329); // Database schema # for WP 2.9
75 +define ('FWP_SCHEMA_30', 12694); // Database schema # for WP 3.0
46 76
47 77 if (FEEDWORDPRESS_DEBUG) :
48 78 // Help us to pick out errors, if any.
49 79 ini_set('error_reporting', E_ALL & ~E_NOTICE);
50 80 ini_set('display_errors', true);
51 - define('MAGPIE_DEBUG', true);
52 81
53 82 // When testing we don't want cache issues to interfere. But this is
54 83 // a VERY BAD SETTING for a production server. Webmasters will eat your
55 84 // face for breakfast if you use it, and the baby Jesus will cry. So
@@ -54,45 +83,81 @@
54 83 // a VERY BAD SETTING for a production server. Webmasters will eat your
55 84 // face for breakfast if you use it, and the baby Jesus will cry. So
56 85 // make sure FEEDWORDPRESS_DEBUG is FALSE for any site that will be
57 86 // used for more than testing purposes!
58 - define('MAGPIE_CACHE_AGE', 1);
87 + define('FEEDWORDPRESS_CACHE_AGE', 1);
88 + define('FEEDWORDPRESS_CACHE_LIFETIME', 1);
89 + define('FEEDWORDPRESS_FETCH_TIMEOUT_DEFAULT', 60);
59 90 else :
60 - 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);
61 96 endif;
62 97
63 -// Note that the rss-functions.php that comes prepackaged with WordPress is
64 -// old & busted. For the new hotness, drop a copy of rss.php from
65 -// 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'));
66 100
67 -if (is_readable(ABSPATH . WPINC . '/rss.php')) :
68 - require_once (ABSPATH . WPINC . '/rss.php');
69 -else :
70 - 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');
71 105 endif;
106 +require_once(ABSPATH . WPINC . '/class-feed.php');
72 107
73 -if (isset($wp_db_version)) :
74 - if ($wp_db_version >= FWP_SCHEMA_23) :
75 - require_once (ABSPATH . WPINC . '/registration.php'); // for wp_insert_user
76 - elseif ($wp_db_version >= FWP_SCHEMA_21) : // WordPress 2.1 and 2.2, but not 2.3
77 - require_once (ABSPATH . WPINC . '/registration.php'); // for wp_insert_user
78 - require_once (ABSPATH . 'wp-admin/admin-db.php'); // for wp_insert_category
79 - elseif ($wp_db_version >= FWP_SCHEMA_20) : // WordPress 2.0
80 - require_once (ABSPATH . WPINC . '/registration-functions.php'); // for wp_insert_user
81 - require_once (ABSPATH . 'wp-admin/admin-db.php'); // for wp_insert_category
82 - endif;
108 +if (!function_exists('wp_insert_user')) :
109 + require_once (ABSPATH . WPINC . '/registration.php'); // for wp_insert_user
83 110 endif;
84 111
85 -if (function_exists('wp_enqueue_script')) :
86 - wp_enqueue_script( 'ajaxcat' ); // Provides the handy-dandy new category text box
87 -endif;
112 +require_once(dirname(__FILE__) . '/admin-ui.php');
113 +require_once(dirname(__FILE__) . '/feedwordpresssyndicationpage.class.php');
114 +require_once(dirname(__FILE__) . '/compatability.php'); // LEGACY API: Replicate or mock up functions for legacy support purposes
88 115
116 +require_once(dirname(__FILE__) . '/syndicatedpost.class.php');
117 +require_once(dirname(__FILE__) . '/syndicatedlink.class.php');
118 +require_once(dirname(__FILE__) . '/feedwordpresshtml.class.php');
119 +require_once(dirname(__FILE__) . '/feedwordpress-content-type-sniffer.class.php');
120 +
89 121 // Magic quotes are just about the stupidest thing ever.
90 122 if (is_array($_POST)) :
91 - $fwp_post = stripslashes_deep($_POST);
123 + $fwp_post = $_POST;
124 + if (get_magic_quotes_gpc()) :
125 + $fwp_post = stripslashes_deep($fwp_post);
126 + endif;
92 127 endif;
93 128
129 +// Get the path relative to the plugins directory in which FWP is stored
130 +preg_match (
131 + '|'.preg_quote(WP_PLUGIN_DIR).'/(.+)$|',
132 + dirname(__FILE__),
133 + $ref
134 +);
94 135
136 +if (isset($ref[1])) :
137 + $fwp_path = $ref[1];
138 +else : // Something went wrong. Let's just guess.
139 + $fwp_path = 'feedwordpress';
140 +endif;
141 +
142 +// If this is a FeedWordPress admin page, queue up scripts for AJAX functions that FWP uses
143 +// If it is a display page or a non-FeedWordPress admin page, don't.
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 +
155 + if (function_exists('wp_admin_css')) :
156 + wp_admin_css('css/dashboard');
157 + endif;
158 +endif;
159 +
95 160 if (!FeedWordPress::needs_upgrade()) : // only work if the conditions are safe!
96 161
97 162 # Syndicated items are generally received in output-ready (X)HTML and
98 163 # should not be folded, crumpled, mutilated, or spindled by WordPress
@@ -109,17 +174,39 @@
109 174
110 175 add_filter('the_content', 'feedwordpress_preserve_syndicated_content', -10000);
111 176 add_filter('the_content', 'feedwordpress_restore_syndicated_content', 10000);
112 177
178 + add_action('atom_entry', 'feedwordpress_item_feed_data');
179 +
113 180 # Filter in original permalinks if the user wants that
114 - add_filter('post_link', 'syndication_permalink', 1);
181 + add_filter('post_link', 'syndication_permalink', /*priority=*/ 1, /*arguments=*/ 3);
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');
115 187
188 + add_filter('post_comments_feed_link', 'syndication_comments_feed_link');
189 +
190 + # WTF? By default, wp_insert_link runs incoming link_url and link_rss
191 + # URIs through default filters that include `wp_kses()`. But `wp_kses()`
192 + # just happens to escape any occurrence of & to &amp; -- which just
193 + # happens to fuck up any URI with a & to separate GET parameters.
194 + remove_filter('pre_link_rss', 'wp_filter_kses');
195 + remove_filter('pre_link_url', 'wp_filter_kses');
196 +
116 197 # Admin menu
117 198 add_action('admin_menu', 'fwp_add_pages');
199 + add_action('admin_notices', 'fwp_check_debug');
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'));
118 205
119 206 # Inbound XML-RPC update methods
120 - add_filter('xmlrpc_methods', 'feedwordpress_xmlrpc_hook');
121 -
207 + $feedwordpressRPC = new FeedWordPressRPC;
208 +
122 209 # Outbound XML-RPC ping reform
123 210 remove_action('publish_post', 'generic_ping'); // WP 1.5.x
124 211 remove_action('do_pings', 'do_all_pings', 10, 1); // WP 2.1, 2.2
125 212 remove_action('publish_post', '_publish_post_hook', 5, 1); // WP 2.3
@@ -128,166 +215,287 @@
128 215 add_action('do_pings', 'fwp_do_pings', 10, 1);
129 216 add_action('feedwordpress_update', 'fwp_hold_pings');
130 217 add_action('feedwordpress_update_complete', 'fwp_release_pings');
131 218
132 - # Hook in logging functions only if the logging option is ON
133 - $update_logging = get_option('feedwordpress_update_logging');
134 - if ($update_logging == 'yes') :
135 - add_action('post_syndicated_item', 'log_feedwordpress_post', 100);
136 - add_action('update_syndicated_item', 'log_feedwordpress_update_post', 100);
137 - add_action('feedwordpress_update', 'log_feedwordpress_update_feeds', 100);
138 - add_action('feedwordpress_check_feed', 'log_feedwordpress_check_feed', 100);
139 - 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';
140 230 endif;
141 -
142 - # Cron-less auto-update. Hooray!
143 - add_action('init', 'feedwordpress_auto_update');
231 + add_action($autoUpdateHook, array(&$feedwordpress, 'auto_update'));
232 + add_action('init', array(&$feedwordpress, 'init'));
233 + add_action('shutdown', array(&$feedwordpress, 'email_diagnostic_log'));
234 + add_action('wp_dashboard_setup', array(&$feedwordpress, 'dashboard_setup'));
235 +
236 + # Default sanitizers
237 + add_filter('syndicated_item_content', array('SyndicatedPost', 'resolve_relative_uris'), 0, 2);
238 + add_filter('syndicated_item_content', array('SyndicatedPost', 'sanitize_content'), 0, 2);
239 +
144 240 else :
145 241 # Hook in the menus, which will just point to the upgrade interface
146 242 add_action('admin_menu', 'fwp_add_pages');
147 243 endif; // if (!FeedWordPress::needs_upgrade())
148 244
149 -function feedwordpress_auto_update () {
150 - if (FeedWordPress::stale()) :
151 - $feedwordpress =& new FeedWordPress;
152 - $feedwordpress->update();
153 - endif;
154 -
155 - if (FeedWordPress::update_requested()) :
156 - exit;
157 - endif;
158 -} /* feedwordpress_auto_update () */
159 -
160 245 ################################################################################
161 246 ## LOGGING FUNCTIONS: log status updates to error_log if you want it ###########
162 247 ################################################################################
163 248
164 -function log_feedwordpress_post ($id) {
165 - $post = wp_get_single_post($id);
166 - error_log("[".date('Y-m-d H:i:s')."][feedwordpress] posted "
167 - ."'{$post->post_title}' ({$post->post_date})");
168 -}
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']);
169 264
170 -function log_feedwordpress_update_post ($id) {
171 - $post = wp_get_single_post($id);
172 - error_log("[".date('Y-m-d H:i:s')."][feedwordpress] updated "
173 - ."'{$post->post_title}' ({$post->post_date})"
174 - ." (as of {$post->post_modified})");
175 -}
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 + }
176 277
177 -function log_feedwordpress_update_feeds ($uri) {
178 - error_log("[".date('Y-m-d H:i:s')."][feedwordpress] update('$uri')");
179 -}
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 */
180 293
181 -function log_feedwordpress_check_feed ($feed) {
182 - $uri = $feed['link/uri']; $name = $feed['link/name'];
183 - 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}";
184 303 }
185 304
186 -function log_feedwordpress_update_complete ($delta) {
187 - $mesg = array();
188 - if (isset($delta['new'])) $mesg[] = 'added '.$delta['new'].' new posts';
189 - if (isset($delta['updated'])) $mesg[] = 'updated '.$delta['updated'].' existing posts';
190 - 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() */
191 315
192 - error_log("[".date('Y-m-d H:i:s')."][feedwordpress] "
193 - .(is_null($delta) ? "Error: I don't syndicate that URI"
194 - : implode(' and ', $mesg)));
195 -}
196 -
197 316 ################################################################################
198 -## LEGACY API: Replicate or mock up functions for legacy support purposes ######
317 +## TEMPLATE API: functions to make your templates syndication-aware ############
199 318 ################################################################################
200 319
201 -if (!function_exists('get_option')) {
202 - function get_option ($option) {
203 - return get_settings($option);
204 - }
205 -}
206 -if (!function_exists('current_user_can')) {
207 - $legacy_capability_hack = true;
208 - function current_user_can ($task) {
209 - global $user_level;
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() */
210 332
211 - $can = false;
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']:'');
212 347
213 - // This is **not** a full replacement for current_user_can. It
214 - // is only for checking the capabilities we care about via the
215 - // WordPress 1.5 user levels.
216 - switch ($task) {
217 - case 'manage_options':
218 - $can = ($user_level >= 6);
219 - break;
220 - case 'manage_links':
221 - $can = ($user_level >= 5);
222 - break;
223 - } /* switch */
224 - return $can;
225 - }
226 -} else {
227 - $legacy_capability_hack = false;
228 -}
229 -if (!function_exists('sanitize_user')) {
230 - function sanitize_user ($text, $strict) {
231 - return $text; // Don't munge it if it wasn't munged going in...
232 - }
233 -}
348 + if (strlen($url) > ($before+$after)) :
349 + $url = substr($url, 0, $before).'…'.substr($url, 0 - $after, $after);
350 + endif;
234 351
235 -################################################################################
236 -## TEMPLATE API: functions to make your templates syndication-aware ############
237 -################################################################################
352 + return $url;
353 +} /* feedwordpress_display_url () */
238 354
239 -function is_syndicated () { return (strlen(get_syndication_feed_id()) > 0); }
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();
359 + endif;
360 +
361 + if (is_null($remote)) :
362 + $remote = $local . '_original';
363 + endif;
364 +
365 + $vals = ($original ? get_post_custom_values($remote, $id) : array());
366 + if (count($vals) < 1) :
367 + $vals = get_post_custom_values($local, $id);
368 + endif;
369 +
370 + if (count($vals) > 0) :
371 + $ret = $vals[0];
372 + endif;
373 + return $ret;
374 +} /* function get_syndication_source_property () */
240 375
241 -function the_syndication_source_link () { echo get_syndication_source_link(); }
242 -function get_syndication_source_link () { list($n) = get_post_custom_values('syndication_source_uri'); return $n; }
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() */
243 380
244 -function get_syndication_source () { list($n) = get_post_custom_values('syndication_source'); return $n; }
245 -function the_syndication_source () { echo get_syndication_source(); }
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());
386 + endif;
387 + return $ret;
388 +} /* function get_syndication_source() */
246 389
247 -function get_syndication_feed () { list($u) = get_post_custom_values('syndication_feed'); return $u; }
248 -function the_syndication_feed () { echo get_syndication_feed (); }
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');
393 +} /* function get_syndication_feed() */
249 394
250 -function get_syndication_feed_id () { list($u) = get_post_custom_values('syndication_feed_id'); return $u; }
251 -function the_syndication_feed_id () { echo get_syndication_feed_id(); }
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();
400 + endif;
401 + return $ret;
402 +} /* function get_syndication_feed_guid () */
252 403
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); }
406 +
253 407 $feedwordpress_linkcache = array (); // only load links from database once
408 +function get_syndication_feed_object ($id = NULL) {
409 + global $feedwordpress_linkcache;
254 410
255 -function get_feed_meta ($key) {
256 - global $wpdb, $feedwordpress_linkcache;
257 - $feed_id = get_syndication_feed_id();
411 + $link = NULL;
258 412
259 - $ret = NULL;
413 + $feed_id = get_syndication_feed_id($id);
260 414 if (strlen($feed_id) > 0):
261 415 if (isset($feedwordpress_linkcache[$feed_id])) :
262 416 $link = $feedwordpress_linkcache[$feed_id];
263 417 else :
264 - $link =& new SyndicatedLink($feed_id);
418 + $link = new SyndicatedLink($feed_id);
265 419 $feedwordpress_linkcache[$feed_id] = $link;
266 420 endif;
421 + endif;
422 + return $link;
423 +}
267 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])) :
268 430 $ret = $link->settings[$key];
269 431 endif;
270 432 return $ret;
271 433 } /* get_feed_meta() */
272 434
273 -function get_syndication_permalink () {
274 - 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;
275 437 }
276 -function the_syndication_permalink () {
277 - echo get_syndication_permalink();
438 +function the_syndication_permalink ($id = NULL) {
439 + echo get_syndication_permalink($id);
278 440 }
279 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 +
280 483 ################################################################################
281 484 ## FILTERS: syndication-aware handling of post data for templates and feeds ####
282 485 ################################################################################
283 486
284 487 $feedwordpress_the_syndicated_content = NULL;
488 +$feedwordpress_the_original_permalink = NULL;
285 489
286 490 function feedwordpress_preserve_syndicated_content ($text) {
287 491 global $feedwordpress_the_syndicated_content;
288 492
289 - if ( is_syndicated() ) :
493 + $globalExpose = (get_option('feedwordpress_formatting_filters') == 'yes');
494 + $localExpose = get_post_custom_values('_feedwordpress_formatting_filters');
495 + $expose = ($globalExpose or ((count($localExpose) > 0) and $localExpose[0]));
496 +
497 + if ( is_syndicated() and !$expose ) :
290 498 $feedwordpress_the_syndicated_content = $text;
291 499 else :
292 500 $feedwordpress_the_syndicated_content = NULL;
293 501 endif;
@@ -303,1059 +511,246 @@
303 511
304 512 return $text;
305 513 }
306 514
307 -function syndication_permalink ($permalink = '') {
308 - if (get_option('feedwordpress_munge_permalink') != 'no'):
309 - $uri = get_syndication_permalink();
310 - return ((strlen($uri) > 0) ? $uri : $permalink);
311 - else:
312 - return $permalink;
313 - endif;
314 -} // function syndication_permalink ()
315 -
316 -################################################################################
317 -## UPGRADE INTERFACE: Have users upgrade DB from older versions of FWP #########
318 -################################################################################
319 -
320 -function fwp_upgrade_page () {
321 - if (isset($GLOBALS['fwp_post']['action']) and $GLOBALS['fwp_post']['action']=='Upgrade') :
322 - $ver = get_option('feedwordpress_version');
323 - if (get_option('feedwordpress_version') != FEEDWORDPRESS_VERSION) :
324 - echo "<div class=\"wrap\">\n";
325 - echo "<h2>Upgrading FeedWordPress...</h2>";
326 -
327 - $feedwordpress =& new FeedWordPress;
328 - $feedwordpress->upgrade_database();
329 - echo "<p><strong>Done!</strong> Upgraded database to version ".FEEDWORDPRESS_VERSION.".</p>\n";
330 - echo "<form action=\"\" method=\"get\">\n";
331 - echo "<div class=\"submit\"><input type=\"hidden\" name=\"page\" value=\"".basename(__FILE__)."\" />";
332 - echo "<input type=\"submit\" value=\"Continue &raquo;\" /></form></div>\n";
333 - echo "</div>\n";
334 - return;
335 - else :
336 - echo "<div class=\"updated\"><p>Already at version ".FEEDWORDPRESS_VERSION."!</p></div>";
337 - endif;
338 - endif;
515 +function feedwordpress_item_feed_data () {
516 + // In a post context....
517 + if (is_syndicated()) :
339 518 ?>
340 -<div class="wrap">
341 -<h2>Upgrade FeedWordPress</h2>
342 -
343 -<p>It appears that you have installed FeedWordPress
344 -<?php echo FEEDWORDPRESS_VERSION; ?> as an upgrade to an existing installation of
345 -FeedWordPress. That's no problem, but you will need to take a minute out first
346 -to upgrade your database: some necessary changes in how the software keeps
347 -track of posts and feeds will cause problems such as duplicate posts and broken
348 -templates if we were to continue without the upgrade.</p>
349 -
350 -<p>Note that most of FeedWordPress's functionality is temporarily disabled
351 -until we have successfully completed the upgrade. Everything should begin
352 -working as normal again once the upgrade is complete. There's extraordinarily
353 -little chance of any damage as the result of the upgrade, but if you're paranoid
354 -like me you may want to back up your database before you proceed.</p>
355 -
356 -<p>This may take several minutes for a large installation.</p>
357 -
358 -<form action="" method="post">
359 -<div class="submit"><input type="submit" name="action" value="Upgrade" /></div>
360 -</form>
361 -</div>
519 +<source>
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()); ?>" />
362 523 <?php
363 -} // function fwp_upgrade_page ()
364 -
365 -################################################################################
366 -## ADMIN MENU ADD-ONS: implement Dashboard management pages ####################
367 -################################################################################
368 -
369 -function fwp_add_pages () {
370 - global $legacy_capability_hack;
371 -
372 - if ($legacy_capability_hack) :
373 - // old & busted: numeric user levels
374 - $manage_links = 5;
375 - $manage_options = 6;
376 - else :
377 - // new hotness: named capabilities
378 - $manage_links = 'manage_links';
379 - $manage_options = 'manage_options';
524 + $id = get_syndication_feed_guid();
525 + if (strlen($id) > 0) :
526 +?>
527 + <id><?php print htmlspecialchars($id); ?></id>
528 +<?php
380 529 endif;
381 -
382 - //add_submenu_page('plugins.php', 'Akismet Configuration', 'Akismet Configuration', 'manage_options', 'syndication-manage-page', 'fwp_syndication_manage_page');
383 - add_menu_page('Syndicated Sites', 'Syndication', $manage_links, 'feedwordpress/'.basename(__FILE__), 'fwp_syndication_manage_page');
384 - add_submenu_page('feedwordpress/'.basename(__FILE__), 'Syndication Options', 'Options', $manage_options, 'feedwordpress/syndication-options.php');
385 - add_options_page('Syndication Options', 'Syndication', $manage_options, 'feedwordpress/syndication-options.php');
386 -} // function fwp_add_pages () */
387 -
388 -function fwp_category_box ($checked, $object) {
389 - global $wp_db_version;
390 -
391 - if (isset($wp_db_version) and $wp_db_version >= FWP_SCHEMA_20) : // WordPress 2.x
392 -?>
393 - <div id="poststuff">
394 -
395 - <div id="moremeta">
396 - <div id="grabit" class="dbx-group">
397 - <fieldset id="categorydiv" class="dbx-box">
398 - <h3 class="dbx-handle"><?php _e('Categories') ?></h3>
399 - <div class="dbx-content">
400 - <p style="font-size:smaller;font-style:bold;margin:0">Place <?php print $object; ?> under...</p>
401 - <p id="jaxcat"></p>
402 - <ul id="categorychecklist"><?php write_nested_categories($checked); ?></ul></div>
403 - </fieldset>
404 - </div>
405 - </div>
406 - </div>
530 + $updated = get_feed_meta('feed/updated');
531 + if (strlen($updated) > 0) : ?>
532 + <updated><?php print $updated; ?></updated>
407 533 <?php
408 - else : // WordPress 1.5
534 + endif;
409 535 ?>
410 - <fieldset id="categorydiv" style="width: 20%; margin-right: 2em">
411 - <legend><?php _e('Categories') ?></legend>
412 - <p style="font-size:smaller;font-style:bold;margin:0">Place <?php print $object; ?> under...</p>
413 - <div style="height: 20em"><?php write_nested_categories($checked); ?></div>
414 - </fieldset>
536 +</source>
415 537 <?php
416 538 endif;
417 539 }
418 540
419 -function update_feeds_mention ($feed) {
420 - echo "<li>Updating <cite>".$feed['link/name']."</cite> from &lt;<a href=\""
421 - .$feed['link/uri']."\">".$feed['link/uri']."</a>&gt; ...</li>\n";
422 - flush();
423 -}
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;
424 565
425 -function fwp_syndication_manage_page () {
426 - global $wpdb;
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;
577 + endif;
427 578
428 - if (FeedWordPress::needs_upgrade()) :
429 - fwp_upgrade_page();
430 - return;
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');
431 583 endif;
432 584
433 -?>
434 -<?php $cont = true;
435 -if (isset($_REQUEST['action'])):
436 - if ($_REQUEST['action'] == 'feedfinder') : $cont = fwp_feedfinder_page();
437 - elseif ($_REQUEST['action'] == 'switchfeed') : $cont = fwp_switchfeed_page();
438 - elseif ($_REQUEST['action'] == 'linkedit') : $cont = fwp_linkedit_page();
439 - elseif ($_REQUEST['action'] == 'Unsubscribe from Checked Links' or $_REQUEST['action'] == 'Unsubscribe') : $cont = fwp_multidelete_page();
585 + if ($munge):
586 + $uri = get_syndication_permalink($postId);
587 + $permalink = ((strlen($uri) > 0) ? $uri : $permalink);
440 588 endif;
441 -endif;
589 + return $permalink;
590 +} /* function syndication_permalink () */
442 591
443 -if ($cont):
444 -?>
445 -<?php
446 - $links = FeedWordPress::syndicated_links();
447 -
448 - if (isset($_POST['update']) or isset($_POST['action']) or isset($_POST['update_uri'])) :
449 - $fwp_update_invoke = 'post';
450 - else :
451 - $fwp_update_invoke = 'get';
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);
452 610 endif;
611 + return $permalink;
612 +} /* function syndication_permalink_escaped() */
453 613
454 - $update_set = array();
455 - if (isset($_POST['link_ids']) and is_array($_POST['link_ids']) and ($_POST['action']=='Update Checked Links')) :
456 - $targets = $wpdb->get_results("
457 - SELECT * FROM $wpdb->links
458 - WHERE link_id IN (".implode(",",$_POST['link_ids']).")
459 - ");
460 - if (is_array($targets)) :
461 - foreach ($targets as $target) :
462 - $update_set[] = $target->link_rss;
463 - endforeach;
464 - else : // This should never happen
465 - FeedWordPress::critical_bug('fwp_syndication_manage_page::targets', $targets, __LINE__);
466 - endif;
467 - elseif (isset($_POST['update_uri'])) :
468 - $update_set[] = $_POST['update_uri'];
469 - endif;
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;
470 626
471 - if ($fwp_update_invoke != 'get' and count($update_set) > 0) : // Only do things with side-effects for HTTP POST or command line
472 - $feedwordpress =& new FeedWordPress;
473 - add_action('feedwordpress_check_feed', 'update_feeds_mention');
474 -
475 - echo "<div class=\"updated\">\n";
476 - echo "<ul>\n";
477 - $tdelta = NULL;
478 - foreach ($update_set as $uri) :
479 - if ($uri == '*') : $uri = NULL; endif;
480 - $delta = $feedwordpress->update($uri);
481 - if (!is_null($delta)) :
482 - if (is_null($tdelta)) :
483 - $tdelta = $delta;
484 - else :
485 - $tdelta['new'] += $delta['new'];
486 - $tdelta['updated'] += $delta['updated'];
487 - endif;
488 - else :
489 - echo "<li><p><strong>Error:</strong> There was a problem updating <a href=\"$uri\">$uri</a></p></li>\n";
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);
490 646 endif;
491 - endforeach;
492 - echo "</ul>\n";
493 -
494 - if (!is_null($tdelta)) :
495 - $mesg = array();
496 - if (isset($delta['new'])) : $mesg[] = ' '.$tdelta['new'].' new posts were syndicated'; endif;
497 - if (isset($delta['updated'])) : $mesg[] = ' '.$tdelta['updated'].' existing posts were updated'; endif;
498 - echo "<p>Update complete.".implode(' and', $mesg)."</p>";
499 - echo "\n"; flush();
500 647 endif;
501 - echo "</div> <!-- class=\"updated\" -->\n";
502 - endif;
503 -
504 - ?>
505 - <div class="wrap">
506 - <form action="" method="POST">
507 - <h2>Update feeds now</h2>
508 - <p>Check currently scheduled feeds for new and updated posts.</p>
509 -
510 -<?php if (!get_option('feedwordpress_automatic_updates')) : ?>
511 - <p><strong>Note:</strong> Automatic updates are currently turned
512 - <strong>off</strong>. New posts from your feeds will not be syndicated
513 - until you manually check for them here. You can turn on automatic
514 - updates under <a href="admin.php?page=feedwordpress/syndication-options.php">Syndication
515 - Options</a>.</p>
516 -<?php endif; ?>
517 -
518 -
519 - <div class="submit"><input type="hidden" name="update_uri" value="*" /><input type="submit" name="update" value="Update" /></div>
520 - </form>
521 - </div> <!-- class="wrap" -->
522 -
523 - <form action="admin.php?page=feedwordpress/<?php echo basename(__FILE__); ?>" method="post">
524 - <div class="wrap">
525 - <h2>Syndicated Sites</h2>
526 -<?php $alt_row = true;
527 - if ($links): ?>
528 -
529 -<table width="100%" cellpadding="3" cellspacing="3">
530 -<tr>
531 -<th width="20%"><?php _e('Name'); ?></th>
532 -<th width="40%"><?php _e('Feed'); ?></th>
533 -<th colspan="4"><?php _e('Action'); ?></th>
534 -</tr>
535 -
536 -<?php foreach ($links as $link):
537 - $alt_row = !$alt_row; ?>
538 -<tr<?php echo ($alt_row?' class="alternate"':''); ?>>
539 -<td><a href="<?php echo wp_specialchars($link->link_url, 'both'); ?>"><?php echo wp_specialchars($link->link_name, 'both'); ?></a></td>
540 -<?php
541 - if (strlen($link->link_rss) > 0):
542 - $caption='Switch Feed';
543 - $uri_bits = parse_url($link->link_rss);
544 - $uri_bits['host'] = preg_replace('/^www\./i', '', $uri_bits['host']);
545 - $display_uri =
546 - (isset($uri_bits['user'])?$uri_bits['user'].'@':'')
547 - .(isset($uri_bits['host'])?$uri_bits['host']:'')
548 - .(isset($uri_bits['port'])?':'.$uri_bits['port']:'')
549 - .(isset($uri_bits['path'])?$uri_bits['path']:'')
550 - .(isset($uri_bits['query'])?'?'.$uri_bits['query']:'');
551 - if (strlen($display_uri) > 32) : $display_uri = substr($display_uri, 0, 32).'&#8230;'; endif;
552 -?>
553 - <td>
554 - <strong><a href="<?php echo $link->link_rss; ?>"><?php echo wp_specialchars($display_uri, 'both'); ?></a></strong></td>
555 -<?php
556 - else:
557 - $caption='Find Feed';
558 -?>
559 - <td style="background-color:#FFFFD0"><p><strong>no
560 - feed assigned</strong></p></td>
561 -<?php
562 - endif;
563 -?>
564 - <td><a href="admin.php?page=feedwordpress/<?php echo basename(__FILE__); ?>&amp;link_id=<?php echo $link->link_id; ?>&amp;action=linkedit" class="edit"><?php _e('Edit')?></a></td>
565 - <td><a href="admin.php?page=feedwordpress/<?php echo basename(__FILE__); ?>&amp;link_id=<?php echo $link->link_id; ?>&amp;action=feedfinder" class="edit"><?php echo $caption; ?></a></td>
566 - <td><a href="admin.php?page=feedwordpress/<?php echo basename(__FILE__); ?>&amp;link_id=<?php echo $link->link_id; ?>&amp;action=Unsubscribe" class="delete"><?php _e('Unsubscribe'); ?></a></td>
567 - <td><input type="checkbox" name="link_ids[]" value="<?php echo $link->link_id; ?>" /></td>
568 -<?php
569 - echo "\n\t</tr>";
570 - endforeach;
571 - else:
572 -?>
573 -
574 -<p>There are no websites currently listed for syndication.</p>
575 -
576 -<?php endif; ?>
577 - </table>
578 -
579 - <br/><hr/>
580 - <div class="submit"><input type="submit" class="delete" name="action" value="Unsubscribe from Checked Links" />
581 - <input type="submit" name="action" value="Update Checked Links" /></div>
582 - </div> <!-- class="wrap" -->
583 - </form>
584 -
585 - <div class="wrap">
586 - <form action="admin.php?page=feedwordpress/<?php echo basename(__FILE__); ?>" method="post">
587 - <h2>Add a new syndicated site:</h2>
588 - <div>
589 - <label for="add-uri">Website or newsfeed:</label>
590 - <input type="text" name="lookup" id="add-uri" value="URI" size="64" />
591 - <input type="hidden" name="action" value="feedfinder" />
592 - </div>
593 - <div class="submit"><input type="submit" value="Syndicate &raquo;" /></div>
594 - </form>
595 - </div> <!-- class="wrap" -->
596 -<?php
597 -endif;
598 -}
599 -
600 -function fwp_feedfinder_page () {
601 - global $wpdb;
602 -
603 - $lookup = (isset($_REQUEST['lookup'])?$_REQUEST['lookup']:NULL);
604 -
605 - if (isset($_REQUEST['link_id']) and ($_REQUEST['link_id']!=0)):
606 - $link_id = $_REQUEST['link_id'];
607 - if (!is_numeric($link_id)) : FeedWordPress::critical_bug('fwp_feedfinder_page::link_id', $link_id, __LINE__); endif;
608 648
609 - $link = $wpdb->get_row("SELECT * FROM $wpdb->links WHERE link_id='".$wpdb->escape($link_id)."'");
610 - if (is_object($link)):
611 - if (is_null($lookup)) $lookup = $link->link_url;
612 - $name = wp_specialchars($link->link_name, 'both');
613 - else:
614 - die (__("Cheatin' uh ?"));
615 - endif;
616 - else:
617 - $name = "New Syndicated Feed";
618 - $link_id = 0;
619 - endif;
620 -?>
621 - <div class="wrap">
622 - <h2>Feed Finder: <?php echo $name; ?></h2>
623 -<?php
624 - $f =& new FeedFinder($lookup);
625 - $feeds = $f->find();
626 - if (count($feeds) > 0):
627 - foreach ($feeds as $key => $f):
628 - $rss = fetch_rss($f);
629 - if ($rss):
630 - $feed_title = isset($rss->channel['title'])?$rss->channel['title']:$rss->channel['link'];
631 - $feed_link = isset($rss->channel['link'])?$rss->channel['link']:'';
632 -?>
633 - <form action="admin.php?page=feedwordpress/<?php echo basename(__FILE__); ?>" method="post">
634 - <fieldset style="clear: both">
635 - <legend><?php echo $rss->feed_type; ?> <?php echo $rss->feed_version; ?> feed</legend>
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);
636 656
637 - <?php if ($link_id===0): ?>
638 - <input type="hidden" name="feed_title" value="<?php echo wp_specialchars($feed_title, 'both'); ?>" />
639 - <input type="hidden" name="feed_link" value="<?php echo wp_specialchars($feed_link, 'both'); ?>" />
640 - <?php endif; ?>
657 + $format = (isset($ref[2]) ? $ref[2] : '');
658 + if (strlen($format) == 0) : $format = get_default_feed(); endif;
641 659
642 - <input type="hidden" name="link_id" value="<?php echo $link_id; ?>" />
643 - <input type="hidden" name="feed" value="<?php echo wp_specialchars($f, 'both'); ?>" />
644 - <input type="hidden" name="action" value="switchfeed" />
645 -
646 - <div>
647 - <div style="float:right; background-color:#D0D0D0; color: black; width:45%; font-size:70%; border-left: 1px dotted #A0A0A0; padding-left: 0.5em; margin-left: 1.0em">
648 -<?php if (count($rss->items) > 0): ?>
649 - <?php $item = $rss->items[0]; ?>
650 - <h3>Sample Item</h3>
651 - <ul>
652 - <li><strong>Title:</strong> <a href="<?php echo $item['link']; ?>"><?php echo $item['title']; ?></a></li>
653 - <li><strong>Date:</strong> <?php echo isset($item['date_timestamp']) ? date('d-M-y g:i:s a', $item['date_timestamp']) : 'unknown'; ?></li>
654 - </ul>
655 - <div class="entry">
656 - <?php echo (isset($item['content']['encoded'])?$item['content']['encoded']:$item['description']); ?>
657 - </div>
658 -<?php else: ?>
659 - <h3>No Items</h3>
660 - <p>FeedWordPress found no posts on this feed.</p>
661 -<?php endif; ?>
662 - </div>
663 -
664 - <div>
665 - <h3>Feed Information</h3>
666 - <ul>
667 - <li><strong>Website:</strong> <a href="<?php echo $feed_link; ?>"><?php echo is_null($feed_title)?'<em>Unknown</em>':$feed_title; ?></a></li>
668 - <li><strong>Feed URI:</strong> <a href="<?php echo wp_specialchars($f, 'both'); ?>"><?php echo wp_specialchars($f, 'both'); ?></a> <a title="Check feed &lt;<?php echo wp_specialchars($f, 'both'); ?>&gt; for validity" href="http://feedvalidator.org/check.cgi?url=<?php echo urlencode($f); ?>"><img src="../wp-images/smilies/icon_arrow.gif" alt="(&rarr;)" /></a></li>
669 - <li><strong>Encoding:</strong> <?php echo isset($rss->encoding)?wp_specialchars($rss->encoding, 'both'):"<em>Unknown</em>"; ?></li>
670 - <li><strong>Description:</strong> <?php echo isset($rss->channel['description'])?wp_specialchars($rss->channel['description'], 'both'):"<em>Unknown</em>"; ?></li>
671 - </ul>
672 - <div class="submit"><input type="submit" name="Use" value="&laquo; Use this feed" /></div>
673 - <div class="submit"><input type="submit" name="Cancel" value="&laquo; Cancel" /></div>
674 - </div>
675 - </div>
676 - </fieldset>
677 - </form>
678 -<?php endif;
679 - endforeach;
680 - else:
681 - echo "<p><strong>no feed found</strong></p>";
682 - endif;
683 -?>
684 - </div>
685 -
686 - <form action="admin.php?page=feedwordpress/<?php echo basename(__FILE__); ?>" method="post">
687 - <div class="wrap">
688 - <h2>Use another feed</h2>
689 - <div><label>Feed:</label>
690 - <input type="text" name="lookup" value="URI" />
691 - <input type="hidden" name="link_id" value="<?php echo $link_id; ?>" />
692 - <input type="hidden" name="action" value="feedfinder" /></div>
693 - <div class="submit"><input type="submit" value="Use this feed &raquo;" /></div>
694 - </div>
695 - </form>
696 -<?php
697 - return false; // Don't continue
698 -}
699 -
700 -function fwp_switchfeed_page () {
701 - global $wpdb, $wp_db_version;
702 -
703 - check_admin_referer();
704 - if (!isset($_REQUEST['Cancel'])):
705 - if (!current_user_can('manage_links')):
706 - die (__("Cheatin' uh ?"));
707 - elseif (isset($_REQUEST['link_id']) and ($_REQUEST['link_id']==0)):
708 - $link_id = FeedWordPress::syndicate_link($_REQUEST['feed_title'], $_REQUEST['feed_link'], $_REQUEST['feed']);
709 - if ($link_id): ?>
710 -<div class="updated"><p><a href="<?php echo $_REQUEST['feed_link']; ?>"><?php echo wp_specialchars($_REQUEST['feed_title'], 'both'); ?></a>
711 -has been added as a contributing site, using the newsfeed at &lt;<a href="<?php echo $_REQUEST['feed']; ?>"><?php echo wp_specialchars($_REQUEST['feed'], 'both'); ?></a>&gt;.</p></div>
712 -<?php else: ?>
713 -<div class="updated"><p>There was a problem adding the newsfeed. [SQL: <?php echo wp_specialchars(mysql_error(), 'both'); ?>]</p></div>
714 -<?php endif;
715 - elseif (isset($_REQUEST['link_id'])):
716 - // Update link_rss
717 - $result = $wpdb->query("
718 - UPDATE $wpdb->links
719 - SET
720 - link_rss = '".$wpdb->escape($_REQUEST['feed'])."'
721 - WHERE link_id = '".$wpdb->escape($_REQUEST['link_id'])."'
722 - ");
723 -
724 - if ($result):
725 - $result = $wpdb->get_row("
726 - SELECT link_name, link_url FROM $wpdb->links
727 - WHERE link_id = '".$wpdb->escape($_REQUEST['link_id'])."'
728 - ");
729 - ?>
730 -<div class="updated"><p>Feed for <a href="<?php echo $result->link_url; ?>"><?php echo wp_specialchars($result->link_name, 'both'); ?></a>
731 -updated to &lt;<a href="<?php echo $_REQUEST['feed']; ?>"><?php echo wp_specialchars($_REQUEST['feed'], 'both'); ?></a>&gt;.</p></div>
732 - <?php else: ?>
733 -<div class="updated"><p>Nothing was changed.</p></div>
734 - <?php endif;
735 - endif;
736 - endif;
737 - return true; // Continue
738 -}
739 -
740 -function fwp_linkedit_page () {
741 - global $wpdb;
742 -
743 - check_admin_referer(); // Make sure we arrived here from the Dashboard
744 -
745 - $special_settings = array ( /* Regular expression syntax is OK here */
746 - 'cats',
747 - 'cat_split',
748 - 'hardcode name',
749 - 'hardcode url',
750 - 'hardcode description',
751 - 'hardcode categories', /* Deprecated */
752 - 'post status',
753 - 'comment status',
754 - 'ping status',
755 - 'unfamiliar author',
756 - 'unfamliar categories',
757 - 'update/.*',
758 - 'feed/.*',
759 - 'link/.*',
760 - );
761 -
762 - if (!current_user_can('manage_links')) :
763 - die (__("Cheatin' uh ?"));
764 - elseif (isset($_REQUEST['feedfinder'])) :
765 - return fwp_feedfinder_page(); // re-route to Feed Finder page
766 - else :
767 - $link_id = (int) $_REQUEST['link_id'];
768 - $link =& new SyndicatedLink($link_id);
769 -
770 - if ($link->found()) :
771 - if (isset($GLOBALS['fwp_post']['save'])) :
772 - $alter = array ();
773 -
774 - $meta = $link->settings;
775 - if (isset($meta['cats'])):
776 - $meta['cats'] = preg_split(FEEDWORDPRESS_CAT_SEPARATOR_PATTERN, $meta['cats']);
660 + $replacement = trailingslashit($feedwordpress_the_original_permalink) . 'feed';
661 + if ($format != get_default_feed()) :
662 + $replacement .= '/'.$format;
777 663 endif;
778 -
779 - // custom feed settings first
780 - foreach ($GLOBALS['fwp_post']['notes'] as $mn) :
781 - $mn['key0'] = trim($mn['key0']);
782 - $mn['key1'] = trim($mn['key1']);
783 - if (preg_match("\007^(("
784 - .implode(')|(',$special_settings)
785 - ."))$\007i",
786 - $mn['key1'])) :
787 - $mn['key1'] = 'user/'.$mn['key1'];
788 - endif;
789 -
790 - if (strlen($mn['key0']) > 0) :
791 - unset($meta[$mn['key0']]); // out with the old
792 - endif;
793 -
794 - if (($mn['action']=='update') and (strlen($mn['key1']) > 0)) :
795 - $meta[$mn['key1']] = $mn['value']; // in with the new
796 - endif;
797 - endforeach;
798 -
799 - // now stuff through the web form
800 - // hardcoded feed info
801 - if (isset($GLOBALS['fwp_post']['hardcode_name'])) :
802 - $meta['hardcode name'] = $GLOBALS['fwp_post']['hardcode_name'];
803 - if (FeedWordPress::affirmative($meta, 'hardcode name')) :
804 - $alter[] = "link_name = '".$wpdb->escape($GLOBALS['fwp_post']['name'])."'";
805 - endif;
806 - endif;
807 - if (isset($GLOBALS['fwp_post']['hardcode_description'])) :
808 - $meta['hardcode description'] = $GLOBALS['fwp_post']['hardcode_description'];
809 - if (FeedWordPress::affirmative($meta, 'hardcode description')) :
810 - $alter[] = "link_description = '".$wpdb->escape($GLOBALS['fwp_post']['description'])."'";
811 - endif;
812 - endif;
813 - if (isset($GLOBALS['fwp_post']['hardcode_url'])) :
814 - $meta['hardcode url'] = $GLOBALS['fwp_post']['hardcode_url'];
815 - if (FeedWordPress::affirmative($meta, 'hardcode url')) :
816 - $alter[] = "link_url = '".$wpdb->escape($GLOBALS['fwp_post']['linkurl'])."'";
817 - endif;
818 - endif;
819 -
820 - // Update scheduling
821 - if (isset($GLOBALS['fwp_post']['update_schedule'])) :
822 - $meta['update/hold'] = $GLOBALS['fwp_post']['update_schedule'];
823 - endif;
824 -
825 - // Categories
826 - if (isset($GLOBALS['fwp_post']['post_category'])) :
827 - $meta['cats'] = array();
828 - foreach ($GLOBALS['fwp_post']['post_category'] as $cat_id) :
829 - $meta['cats'][] = '{#'.$cat_id.'}';
830 - endforeach;
831 - else :
832 - unset($meta['cats']);
833 - endif;
834 -
835 - // Post status, comment status, ping status
836 - foreach (array('post', 'comment', 'ping') as $what) :
837 - $sfield = "feed_{$what}_status";
838 - if (isset($GLOBALS['fwp_post'][$sfield])) :
839 - if ($GLOBALS['fwp_post'][$sfield]=='site-default') :
840 - unset($meta["{$what} status"]);
841 - else :
842 - $meta["{$what} status"] = $GLOBALS['fwp_post'][$sfield];
843 - endif;
844 - endif;
845 - endforeach;
846 -
847 - // Unfamiliar author, unfamiliar categories
848 - foreach (array("author", "category") as $what) :
849 - $sfield = "unfamiliar_{$what}";
850 - if (isset($GLOBALS['fwp_post'][$sfield])) :
851 - if ($GLOBALS['fwp_post'][$sfield]=='site-default') :
852 - unset($meta["unfamiliar {$what}"]);
853 - else :
854 - $meta["unfamiliar {$what}"] = $GLOBALS['fwp_post'][$sfield];
855 - endif;
856 - endif;
857 - endforeach;
858 -
859 - if (isset($GLOBALS['fwp_post']['cat_split'])) :
860 - if (strlen(trim($GLOBALS['fwp_post']['cat_split'])) > 0) :
861 - $meta['cat_split'] = trim($GLOBALS['fwp_post']['cat_split']);
862 - else :
863 - unset($meta['cat_split']);
864 - endif;
865 - endif;
866 -
867 - if (is_array($meta['cats'])) :
868 - $meta['cats'] = implode(FEEDWORDPRESS_CAT_SEPARATOR, $meta['cats']);
869 - endif;
870 -
871 - $notes = '';
872 - foreach ($meta as $key => $value) :
873 - $notes .= $key . ": ". addcslashes($value, "\0..\37".'\\') . "\n";
874 - endforeach;
875 - $alter[] = "link_notes = '".$wpdb->escape($notes)."'";
876 -
877 - $alter_set = implode(", ", $alter);
878 -
879 - // issue update query
880 - $result = $wpdb->query("
881 - UPDATE $wpdb->links
882 - SET $alter_set
883 - WHERE link_id='$link_id'
884 - ");
885 - $updated_link = true;
886 -
887 - // reload link information from DB
888 - $link =& new SyndicatedLink($link_id);
664 + $replacement = user_trailingslashit($replacement, 'single_feed');
889 665 else :
890 - $updated_link = false;
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.
891 670 endif;
892 -
893 - $db_link = $link->link;
894 - $link_url = wp_specialchars($db_link->link_url, 1);
895 - $link_name = wp_specialchars($db_link->link_name, 1);
896 - $link_description = wp_specialchars($db_link->link_description, 'both');
897 - $link_notes = wp_specialchars($db_link->link_notes, 'both');
898 - $link_rss_uri = wp_specialchars($db_link->link_rss, 'both');
899 -
900 - $meta = $link->settings;
901 - $post_status_global = get_option('feedwordpress_syndicated_post_status');
902 - $comment_status_global = get_option('feedwordpress_syndicated_comment_status');
903 - $ping_status_global = get_option('feedwordpress_syndicated_ping_status');
904 -
905 - $status['post'] = array('publish' => '', 'private' => '', 'draft' => '', 'site-default' => '');
906 - $status['comment'] = array('open' => '', 'closed' => '', 'site-default' => '');
907 - $status['ping'] = array('open' => '', 'closed' => '', 'site-default' => '');
908 -
909 - foreach (array('post', 'comment', 'ping') as $what) :
910 - if (isset($meta["{$what} status"])) :
911 - $status[$what][$meta["{$what} status"]] = ' checked="checked"';
912 - else :
913 - $status[$what]['site-default'] = ' checked="checked"';
914 - endif;
915 - endforeach;
916 -
917 - $unfamiliar['author'] = array ('create' => '','default' => '','filter' => '');
918 - $unfamiliar['category'] = array ('create'=>'','default'=>'','filter'=>'');
919 -
920 - foreach (array('author', 'category') as $what) :
921 - if (is_string($meta["unfamiliar {$what}"]) and
922 - array_key_exists($meta["unfamiliar {$what}"], $unfamiliar[$what])) :
923 - $key = $meta["unfamiliar {$what}"];
924 - else:
925 - $key = 'site-default';
926 - endif;
927 - $unfamiliar[$what][$key] = ' checked="checked"';
928 - endforeach;
929 -
930 - $dogs = get_nested_categories(-1, 0);
931 - if (is_array($meta['cats'])) :
932 - $cats = array_map('strtolower',
933 - array_map('trim',
934 - $meta['cats']
935 - ));
936 - else:
937 - $cats = array();
938 - endif;
939 -
940 - foreach ($dogs as $tag => $dog) :
941 - $found_by_name = in_array(strtolower(trim($dog['cat_name'])), $cats);
942 -
943 - if (isset($dog['cat_ID'])) : $dog['cat_id'] = $dog['cat_ID']; endif;
944 - $found_by_id = in_array('{#'.trim($dog['cat_id']).'}', $cats);
945 -
946 - if ($found_by_name or $found_by_id) :
947 - $dogs[$tag]['checked'] = true;
948 - endif;
949 - endforeach;
950 - else :
951 - die( __('Link not found.') );
952 671 endif;
953 -
954 - ?>
955 -<script type="text/javascript">
956 - function flip_hardcode (item) {
957 - ed=document.getElementById('basics-'+item+'-edit');
958 - view=document.getElementById('basics-'+item+'-view');
959 672
960 - o = document.getElementById('basics-hardcode-'+item);
961 - if (o.value=='yes') { ed.style.display='inline'; view.style.display='none'; }
962 - else { ed.style.display='none'; view.style.display='inline'; }
963 - }
964 -</script>
965 -
966 -<?php if ($updated_link) : ?>
967 -<div class="updated"><p>Syndicated feed settings updated.</p></div>
968 -<?php endif; ?>
969 -
970 -<form action="admin.php?page=feedwordpress/<?php echo basename(__FILE__); ?>" method="post">
971 -<div class="wrap">
972 -<input type="hidden" name="link_id" value="<?php echo $link_id; ?>" />
973 -<input type="hidden" name="action" value="linkedit" />
974 -<input type="hidden" name="save" value="link" />
975 -
976 -<h2>Edit a syndicated feed:</h2>
977 -<fieldset><legend>Basics</legend>
978 -<table class="editform" width="100%" cellspacing="2" cellpadding="5">
979 -<tr>
980 -<th scope="row" width="20%"><?php _e('Feed URI:') ?></th>
981 -<td width="60%"><a href="<?php echo wp_specialchars($link_rss_uri, 'both'); ?>"><?php echo $link_rss_uri; ?></a>
982 -<a href="<?php echo FEEDVALIDATOR_URI; ?>?url=<?php echo urlencode($link_rss_uri); ?>"
983 -title="Check feed &lt;<?php echo wp_specialchars($link_rss_uri, 'both'); ?>&gt; for validity"><img src="../wp-images/smilies/icon_arrow.gif" alt="&rarr;" /></a>
984 -</td>
985 -<td width="20%"><input type="submit" name="feedfinder" value="switch &rarr;" style="font-size:smaller" /></td>
986 -</tr>
987 -<tr>
988 -<th scope="row" width="20%"><?php _e('Link Name:') ?></th>
989 -<td width="60%"><input type="text" id="basics-name-edit" name="name"
990 -value="<?php echo $link_name; ?>" style="width: 95%" />
991 -<span id="basics-name-view"><strong><?php echo $link_name; ?></strong></span>
992 -</td>
993 -<td>
994 -<select id="basics-hardcode-name" onchange="flip_hardcode('name')" name="hardcode_name">
995 -<option value="no" <?php echo $link->hardcode('name')?'':'selected="selected"'; ?>>update automatically</option>
996 -<option value="yes" <?php echo $link->hardcode('name')?'selected="selected"':''; ?>>edit manually</option>
997 -</select>
998 -</td>
999 -</tr>
1000 -<tr>
1001 -<th scope="row" width="20%"><?php _e('Short description:') ?></th>
1002 -<td width="60%">
1003 -<input id="basics-description-edit" type="text" name="description" value="<?php echo $link_description; ?>" style="width: 95%" />
1004 -<span id="basics-description-view"><strong><?php echo $link_description; ?></strong></span>
1005 -</td>
1006 -<td>
1007 -<select id="basics-hardcode-description" onchange="flip_hardcode('description')"
1008 -name="hardcode_description">
1009 -<option value="no" <?php echo $link->hardcode('description')?'':'selected="selected"'; ?>>update automatically</option>
1010 -<option value="yes" <?php echo $link->hardcode('description')?'selected="selected"':''; ?>>edit manually</option>
1011 -</select></td>
1012 -</tr>
1013 -<tr>
1014 -<th width="20%" scope="row"><?php _e('Homepage:') ?></th>
1015 -<td width="60%">
1016 -<input id="basics-url-edit" type="text" name="linkurl" value="<?php echo $link_url; ?>" style="width: 95%;" />
1017 -<a id="basics-url-view" href="<?php echo $link_url; ?>"><?php echo $link_url; ?></a></td>
1018 -<td>
1019 -<select id="basics-hardcode-url" onchange="flip_hardcode('url')" name="hardcode_url">
1020 -<option value="no"<?php echo $link->hardcode('url')?'':' selected="selected"'; ?>>update automatically</option>
1021 -<option value="yes"<?php echo $link->hardcode('url')?' selected="selected"':''; ?>>edit manually</option>
1022 -</select></td></tr>
1023 -
1024 -<tr>
1025 -<th width="20%"><?php _e('Last update') ?>:</th>
1026 -<td colspan="2"><?php
1027 - if (isset($meta['update/last'])) :
1028 - echo strftime('%x %X', $meta['update/last'])." ";
1029 - else :
1030 - echo " none yet";
673 + if (!is_null($replacement)) : $link = $replacement; endif;
1031 674 endif;
1032 -?></td></tr>
1033 -<tr><th width="20%">Next update:</th>
1034 -<td colspan="2"><?php
1035 - $holdem = (isset($meta['update/hold']) ? $meta['update/hold'] : 'scheduled');
1036 -?>
1037 -<select name="update_schedule">
1038 -<option value="scheduled"<?php echo ($holdem=='scheduled')?' selected="selected"':''; ?>>update on schedule <?php
1039 - echo " (";
1040 - if (isset($meta['update/ttl']) and is_numeric($meta['update/ttl'])) :
1041 - if (isset($meta['update/timed']) and $meta['update/timed']=='automatically') :
1042 - echo 'next: ';
1043 - $next = $meta['update/last'] + ((int) $meta['update/ttl'] * 60);
1044 - if (strftime('%x', time()) != strftime('%x', $next)) :
1045 - echo strftime('%x', $next)." ";
1046 - endif;
1047 - echo strftime('%X', $meta['update/last']+((int) $meta['update/ttl']*60));
1048 - else :
1049 - echo "every ".$meta['update/ttl']." minute".(($meta['update/ttl']!=1)?"s":"");
1050 - endif;
1051 - else:
1052 - echo "next scheduled update";
1053 - endif;
1054 - echo ")";
1055 -?></option>
1056 -<option value="next"<?php echo ($holdem=='next')?' selected="selected"':''; ?>>update ASAP</option>
1057 -<option value="ping"<?php echo ($holdem=='ping')?' selected="selected"':''; ?>>update only when pinged</option>
1058 -</select></tr>
1059 -</table>
1060 -</fieldset>
675 + return $link;
676 +} /* function syndication_comments_feed_link() */
1061 677
1062 -<script type="text/javascript">
1063 -flip_hardcode('name');
1064 -flip_hardcode('description');
1065 -flip_hardcode('url');
1066 -</script>
678 +################################################################################
679 +## ADMIN MENU ADD-ONS: register Dashboard management pages #####################
680 +################################################################################
1067 681
1068 -<p class="submit">
1069 -<input type="submit" name="submit" value="<?php _e('Save Changes &raquo;') ?>" />
1070 -</p>
682 +function fwp_add_pages () {
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 + );
1071 694
1072 -<fieldset>
1073 -<legend>Syndicated Posts</legend>
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 + );
1074 700
1075 -<?php fwp_category_box($dogs, 'all syndicated posts from this feed'); ?>
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 + );
1076 706
1077 -<table class="editform" width="75%" cellspacing="2" cellpadding="5">
1078 -<tr><th width="27%" scope="row" style="vertical-align:top">Publication:</th>
1079 -<td width="73%" style="vertical-align:top"><ul style="margin:0; list-style:none">
1080 -<li><label><input type="radio" name="feed_post_status" value="site-default"
1081 -<?php echo $status['post']['site-default']; ?> /> Use site-wide setting from <a href="admin.php?page=feedwordpress/syndication-options.php">Syndication Options</a>
1082 -(currently: <strong><?php echo ($post_status_global ? $post_status_global : 'publish'); ?></strong>)</label></li>
1083 -<li><label><input type="radio" name="feed_post_status" value="publish"
1084 -<?php echo $status['post']['publish']; ?> /> Publish posts from this feed immediately</label></li>
1085 -<li><label><input type="radio" name="feed_post_status" value="private"
1086 -<?php echo $status['post']['private']; ?> /> Hold posts from this feed as private posts</label></li>
1087 -<li><label><input type="radio" name="feed_post_status" value="draft"
1088 -<?php echo $status['post']['draft']; ?> /> Hold posts from this feed as drafts</label></li>
1089 -</ul></td>
1090 -</tr>
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 + );
1091 712
1092 -<tr><th width="27%" scope="row" style="vertical-align:top">Comments:</th>
1093 -<td width="73%"><ul style="margin:0; list-style:none">
1094 -<li><label><input type="radio" name="feed_comment_status" value="site-default"
1095 -<?php echo $status['comment']['site-default']; ?> /> Use site-wide setting from <a href="admin.php?page=feedwordpress/syndication-options.php">Syndication Options</a>
1096 -(currently: <strong><?php echo ($comment_status_global ? $comment_status_global : 'closed'); ?>)</strong></label></li>
1097 -<li><label><input type="radio" name="feed_comment_status" value="open"
1098 -<?php echo $status['comment']['open']; ?> /> Allow comments on syndicated posts from this feed</label></li>
1099 -<li><label><input type="radio" name="feed_comment_status" value="closed"
1100 -<?php echo $status['comment']['closed']; ?> /> Don't allow comments on syndicated posts from this feed</label></li>
1101 -</ul></td>
1102 -</tr>
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 + );
1103 718
1104 -<tr><th width="27%" scope="row" style="vertical-align:top">Trackback and Pingback:</th>
1105 -<td width="73%"><ul style="margin:0; list-style:none">
1106 -<li><label><input type="radio" name="feed_ping_status" value="site-default"
1107 -<?php echo $status['ping']['site-default']; ?> /> Use site-wide setting from <a href="admin.php?page=feedwordpress/syndication-options.php">Syndication Options</a>
1108 -(currently: <strong><?php echo ($ping_status_global ? $ping_status_global : 'closed'); ?>)</strong></label></li>
1109 -<li><label><input type="radio" name="feed_ping_status" value="open"
1110 -<?php echo $status['ping']['open']; ?> /> Accept pings on syndicated posts from this feed</label></li>
1111 -<li><label><input type="radio" name="feed_ping_status" value="closed"
1112 -<?php echo $status['ping']['closed']; ?> /> Don't accept pings on syndicated posts from this feed</label></li>
1113 -</ul></td>
1114 -</tr>
1115 -</table>
1116 -</fieldset>
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 + );
1117 724
1118 -<p class="submit">
1119 -<input type="submit" name="submit" value="<?php _e('Save Changes &raquo;') ?>" />
1120 -</p>
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 + );
730 +} /* function fwp_add_pages () */
1121 731
1122 -<fieldset>
1123 -<legend>Advanced Feed Options</legend>
1124 -<table class="editform" width="100%" cellspacing="2" cellpadding="5">
1125 -<tr>
1126 -<th width="20%" scope="row" style="vertical-align:top">Unfamiliar authors:</th>
1127 -<td width="80%"><ul style="margin: 0; list-style:none">
1128 -<li><label><input type="radio" name="unfamiliar_author" value="site-default"<?php echo $unfamiliar['author']['site-default']; ?> /> use site-wide setting from <a href="admin.php?page=feedwordpress/syndication-options.php">Syndication Options</a>
1129 -(currently <strong><?php echo FeedWordPress::on_unfamiliar('author');; ?></strong>)</label></li>
1130 -<li><label><input type="radio" name="unfamiliar_author" value="create"<?php echo $unfamiliar['author']['create']; ?>/> create a new author account</label></li>
1131 -<li><label><input type="radio" name="unfamiliar_author" value="default"<?php echo $unfamiliar['author']['default']; ?> /> attribute the post to the default author</label></li>
1132 -<li><label><input type="radio" name="unfamiliar_author" value="filter"<?php echo $unfamiliar['author']['filter']; ?> /> don't syndicate the post</label></li>
1133 -</ul></td>
1134 -</tr>
1135 -
1136 -<tr>
1137 -<th width="20%" scope="row" style="vertical-align:top">Unfamiliar categories:</th>
1138 -<td width="80%"><ul style="margin: 0; list-style:none">
1139 -<li><label><input type="radio" name="unfamiliar_category" value="site-default"<?php echo $unfamiliar['category']['site-default']; ?> /> use site-wide setting from <a href="admin.php?page=feedwordpress/syndication-options.php">Syndication Options</a>
1140 -(currently <strong><?php echo FeedWordPress::on_unfamiliar('category');; ?></strong>)</label></li>
1141 -<li><label><input type="radio" name="unfamiliar_category" value="create"<?php echo $unfamiliar['category']['create']; ?> /> create any categories the post is in</label></li>
1142 -<li><label><input type="radio" name="unfamiliar_category" value="default"<?php echo $unfamiliar['category']['default']; ?> /> don't create new categories</label></li>
1143 -<li><label><input type="radio" name="unfamiliar_category" value="filter"<?php echo $unfamiliar['category']['filter']; ?> /> don't create new categories and don't syndicate posts unless they match at least one familiar category</label></li>
1144 -</ul></td>
1145 -</tr>
1146 -
1147 -<tr>
1148 -<th width="20%" scope="row" style="vertical-align:top">Multiple categories:</th>
1149 -<td width="80%">
1150 -<input type="text" size="20" id="cat_split" name="cat_split" value="<?php if (isset($meta['cat_split'])) : echo htmlspecialchars($meta['cat_split']); endif; ?>" /><br/>
1151 -Enter a <a href="http://us.php.net/manual/en/reference.pcre.pattern.syntax.php">Perl-compatible regular expression</a> here if the feed provides multiple
1152 -categories in a single category element. The regular expression should match
1153 -the characters used to separate one category from the next. If the feed uses
1154 -spaces (like <a href="http://del.icio.us/">del.icio.us</a>), use the pattern "\s".
1155 -If the feed does not provide multiple categories in a single element, leave this
1156 -blank.</td>
1157 -</tr>
1158 -</table>
1159 -</fieldset>
1160 -
1161 -<p class="submit">
1162 -<input type="submit" name="submit" value="<?php _e('Save Changes &raquo;') ?>" />
1163 -</p>
1164 -
1165 -<fieldset id="postcustom">
1166 -<legend>Custom Settings (for use in templates)</legend>
1167 -<div id="postcustomstuff">
1168 -<table id="meta-list" cellpadding="3">
1169 - <tr>
1170 - <th>Key</th>
1171 - <th>Value</th>
1172 - <th>Action</th>
1173 - </tr>
1174 -
1175 -<?php
1176 - $i = 0;
1177 - foreach ($meta as $key => $value) :
1178 - if (!preg_match("\007^((".implode(')|(', $special_settings)."))$\007i", $key)) :
1179 -?>
1180 - <tr style="vertical-align:top">
1181 - <th width="30%" scope="row"><input type="hidden" name="notes[<?php echo $i; ?>][key0]" value="<?php echo wp_specialchars($key, 'both'); ?>" />
1182 - <input id="notes-<?php echo $i; ?>-key" name="notes[<?php echo $i; ?>][key1]" value="<?php echo wp_specialchars($key, 'both'); ?>" /></th>
1183 - <td width="60%"><textarea rows="2" cols="40" id="notes-<?php echo $i; ?>-value" name="notes[<?php echo $i; ?>][value]"><?php echo wp_specialchars($value, 'both'); ?></textarea></td>
1184 - <td width="10%"><select name="notes[<?php echo $i; ?>][action]">
1185 - <option value="update">save changes</option>
1186 - <option value="delete">delete this setting</option>
1187 - </select></td>
1188 - </tr>
1189 -<?php
1190 - $i++;
1191 - endif;
1192 - endforeach;
1193 -?>
1194 - <tr>
1195 - <th scope="row"><input type="text" size="10" name="notes[<?php echo $i; ?>][key1]" value="" /></th>
1196 - <td><textarea name="notes[<?php echo $i; ?>][value]" rows="2" cols="40"></textarea></td>
1197 - <td><em>add new setting...</em><input type="hidden" name="notes[<?php echo $i; ?>][action]" value="update" /></td>
1198 - </tr>
1199 -</table>
1200 -</fieldset>
1201 -
1202 -<p class="submit">
1203 -<input type="submit" name="submit" value="<?php _e('Save Changes &raquo;') ?>" />
1204 -</p>
1205 -
1206 -</div>
1207 - <?php
732 +function fwp_check_debug () {
733 + // This is a horrible fucking kludge that I have to do because the
734 + // admin notice code is triggered before the code that updates the
735 + // setting.
736 + if (isset($_POST['feedwordpress_debug'])) :
737 + $feedwordpress_debug = $_POST['feedwordpress_debug'];
738 + else :
739 + $feedwordpress_debug = get_option('feedwordpress_debug');
1208 740 endif;
1209 - return false; // Don't continue
1210 -}
1211 -
1212 -function fwp_multidelete_page () {
1213 - global $wpdb;
1214 -
1215 - check_admin_referer(); // Make sure the referers are kosher
1216 -
1217 - $link_ids = (isset($_REQUEST['link_ids']) ? $_REQUEST['link_ids'] : array());
1218 - if (isset($_REQUEST['link_id'])) : array_push($link_ids, $_REQUEST['link_id']); endif;
1219 -
1220 - if (!current_user_can('manage_links')):
1221 - die (__("Cheatin' uh ?"));
1222 - elseif (isset($GLOBALS['fwp_post']['confirm']) and $GLOBALS['fwp_post']['confirm']=='Delete'):
1223 - foreach ($GLOBALS['fwp_post']['link_action'] as $link_id => $what) :
1224 - $do_it[$what][] = $link_id;
1225 - endforeach;
1226 -
1227 - $alter = array();
1228 - if (count($do_it['hide']) > 0) :
1229 - $hidem = "(".implode(', ', $do_it['hide']).")";
1230 - $alter[] = "
1231 - UPDATE $wpdb->links
1232 - SET link_visible = 'N'
1233 - WHERE link_id IN {$hidem}
1234 - ";
1235 - endif;
1236 -
1237 - if (count($do_it['nuke']) > 0) :
1238 - $nukem = "(".implode(', ', $do_it['nuke']).")";
1239 -
1240 - // Make a list of the items syndicated from this feed...
1241 - $post_ids = $wpdb->get_col("
1242 - SELECT post_id FROM $wpdb->postmeta
1243 - WHERE meta_key = 'syndication_feed_id'
1244 - AND meta_value IN {$nukem}
1245 - ");
1246 -
1247 - // ... and kill them all
1248 - if (count($post_ids) > 0) :
1249 - foreach ($post_ids as $post_id) :
1250 - wp_delete_post($post_id);
1251 - endforeach;
1252 - endif;
1253 -
1254 - $alter[] = "
1255 - DELETE FROM $wpdb->links
1256 - WHERE link_id IN {$nukem}
1257 - ";
1258 - endif;
1259 -
1260 - if (count($do_it['delete']) > 0) :
1261 - $deletem = "(".implode(', ', $do_it['delete']).")";
1262 -
1263 - // Make the items syndicated from this feed appear to be locally-authored
1264 - $alter[] = "
1265 - DELETE FROM $wpdb->postmeta
1266 - WHERE meta_key = 'syndication_feed_id'
1267 - AND meta_value IN {$deletem}
1268 - ";
1269 -
1270 - // ... and delete the links themselves.
1271 - $alter[] = "
1272 - DELETE FROM $wpdb->links
1273 - WHERE link_id IN {$deletem}
1274 - ";
1275 - endif;
1276 -
1277 - $errs = array(); $success = array ();
1278 - foreach ($alter as $sql) :
1279 - $result = $wpdb->query($sql);
1280 - if (!$result):
1281 - $errs[] = mysql_error();
1282 - endif;
1283 - endforeach;
1284 -
1285 - if (count($alter) > 0) :
1286 - echo "<div class=\"updated\">\n";
1287 - if (count($errs) > 0) :
1288 - echo "There were some problems processing your ";
1289 - echo "unsubscribe request. [SQL: ".implode('; ', $errs)."]";
1290 - else :
1291 - echo "Your unsubscribe request(s) have been processed.";
1292 - endif;
1293 - echo "</div>\n";
1294 - endif;
1295 -
1296 - return true; // Continue on to Syndicated Sites listing
1297 - else :
1298 - $targets = $wpdb->get_results("
1299 - SELECT * FROM $wpdb->links
1300 - WHERE link_id IN (".implode(",",$link_ids).")
1301 - ");
741 + if ($feedwordpress_debug==='yes') :
1302 742 ?>
1303 -<form action="admin.php?page=feedwordpress/<?php echo basename(__FILE__); ?>" method="post">
1304 -<div class="wrap">
1305 -<input type="hidden" name="action" value="Unsubscribe" />
1306 -<input type="hidden" name="confirm" value="Delete" />
1307 -
1308 -<h2>Unsubscribe from Syndicated Links:</h2>
1309 -<?php foreach ($targets as $link) :
1310 - $link_url = wp_specialchars($link->link_url, 1);
1311 - $link_name = wp_specialchars($link->link_name, 1);
1312 - $link_description = wp_specialchars($link->link_description, 'both');
1313 - $link_rss = wp_specialchars($link->link_rss, 'both');
1314 -?>
1315 -<fieldset>
1316 -<legend><?php echo $link_name; ?></legend>
1317 -<table class="editform" width="100%" cellspacing="2" cellpadding="5">
1318 -<tr><th scope="row" width="20%"><?php _e('Feed URI:') ?></th>
1319 -<td width="80%"><a href="<?php echo $link_rss; ?>"><?php echo $link_rss; ?></a></td></tr>
1320 -<tr><th scope="row" width="20%"><?php _e('Short description:') ?></th>
1321 -<td width="80%"><?php echo $link_description; ?></span></td></tr>
1322 -<tr><th width="20%" scope="row"><?php _e('Homepage:') ?></th>
1323 -<td width="80%"><a href="<?php echo $link_url; ?>"><?php echo $link_url; ?></a></td></tr>
1324 -<tr style="vertical-align:top"><th width="20%" scope="row">Subscription <?php _e('Options') ?>:</th>
1325 -<td width="80%"><ul style="margin:0; padding: 0; list-style: none">
1326 -<li><input type="radio" id="hide-<?php echo $link->link_id; ?>"
1327 -name="link_action[<?php echo $link->link_id; ?>]" value="hide" />
1328 -<label for="hide-<?php echo $link->link_id; ?>">Turn off the subscription for this
1329 -syndicated link<br/><span style="font-size:smaller">(Keep the feed information
1330 -and all the posts from this feed in the database, but don't syndicate any
1331 -new posts from the feed.)</span></label></li>
1332 -<li><input type="radio" id="nuke-<?php echo $link->link_id; ?>"
1333 -name="link_action[<?php echo $link->link_id; ?>]" value="nuke" />
1334 -<label for="nuke-<?php echo $link->link_id; ?>">Delete this syndicated link and all the
1335 -posts that were syndicated from it</label></li>
1336 -<li><input type="radio" id="delete-<?php echo $link->link_id; ?>"
1337 -name="link_action[<?php echo $link->link_id; ?>]" value="delete" />
1338 -<label for="delete-<?php echo $link->link_id; ?>">Delete this syndicated link, but
1339 -<em>keep</em> posts that were syndicated from it (as if they were authored
1340 -locally).</label></li>
1341 -<li><input type="radio" id="nothing-<?php echo $link->link_id; ?>"
1342 -name="link_action[<?php echo $link->link_id; ?>]" value="nothing" />
1343 -<label for="nothing-<?php echo $link->link_id; ?>">Keep this feed as it is. I changed
1344 -my mind.</label></li>
1345 -</ul>
1346 -</table>
1347 -</fieldset>
1348 -<?php endforeach; ?>
1349 -
1350 -<div class="submit">
1351 -<input class="delete" type="submit" name="submit" value="<?php _e('Unsubscribe from selected feeds &raquo;') ?>" />
1352 -</div>
1353 -</div>
743 + <div class="error">
744 +<p><strong>FeedWordPress warning.</strong> Debugging mode is <strong>ON</strong>.
745 +While it remains on, FeedWordPress displays many diagnostic error messages,
746 +warnings, and notices that are ordinarily suppressed, and also turns off all
747 +caching of feeds. Use with caution: this setting is absolutely inappropriate
748 +for a production server.</p>
749 + </div>
1354 750 <?php
1355 - return false; // Don't continue on to Syndicated Sites listing
1356 751 endif;
1357 -}
752 +} /* function fwp_check_debug () */
1358 753
1359 754 ################################################################################
1360 755 ## fwp_hold_pings() and fwp_release_pings(): Outbound XML-RPC ping reform ####
1361 756 ## ... 'coz it's rude to send 500 pings the first time your aggregator runs ####
@@ -1412,8 +807,67 @@
1412 807 endif;
1413 808 endif;
1414 809 }
1415 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 +
1416 870 ################################################################################
1417 871 ## class FeedWordPress #########################################################
1418 872 ################################################################################
1419 873
@@ -1457,9 +911,9 @@
1457 911 function FeedWordPress () {
1458 912 $this->feeds = array ();
1459 913 $links = FeedWordPress::syndicated_links();
1460 914 if ($links): foreach ($links as $link):
1461 - $this->feeds[] =& new SyndicatedLink($link);
915 + $this->feeds[] = new SyndicatedLink($link);
1462 916 endforeach; endif;
1463 917 } // FeedWordPress::FeedWordPress ()
1464 918
1465 919 # function update (): polls for updates on one or more Contributor feeds
@@ -1510,9 +964,9 @@
1510 964 #
1511 965 # * Updates to existing posts since the last poll are mirrored in the
1512 966 # WordPress store.
1513 967 #
1514 - function update ($uri = null) {
968 + function update ($uri = null, $crash_ts = null) {
1515 969 global $wpdb;
1516 970
1517 971 if (FeedWordPress::needs_upgrade()) : // Will make duplicate posts if we don't hold off
1518 972 return NULL;
@@ -1517,9 +971,9 @@
1517 971 if (FeedWordPress::needs_upgrade()) : // Will make duplicate posts if we don't hold off
1518 972 return NULL;
1519 973 endif;
1520 974
1521 - if (!is_null($uri)) :
975 + if (!is_null($uri) and $uri != '*') :
1522 976 $uri = trim($uri);
1523 977 else : // Update all
1524 978 update_option('feedwordpress_last_update_all', time());
1525 979 endif;
@@ -1525,13 +979,27 @@
1525 979 endif;
1526 980
1527 981 do_action('feedwordpress_update', $uri);
1528 982
983 + if (is_null($crash_ts)) :
984 + $crash_ts = $this->crash_ts();
985 + endif;
986 +
987 + // Randomize order for load balancing purposes
988 + $feed_set = $this->feeds;
989 + shuffle($feed_set);
990 +
991 + $feed_set = apply_filters('feedwordpress_update_feeds', $feed_set, $uri);
992 +
1529 993 // Loop through and check for new posts
1530 994 $delta = NULL;
1531 - foreach ($this->feeds as $feed) :
1532 - $pinged_that = (is_null($uri) or in_array($uri, array($feed->uri(), $feed->homepage())));
995 + foreach ($feed_set as $feed) :
996 + if (!is_null($crash_ts) and (time() > $crash_ts)) : // Check whether we've exceeded the time limit
997 + break;
998 + endif;
1533 999
1000 + $pinged_that = (is_null($uri) or ($uri=='*') or in_array($uri, array($feed->uri(), $feed->homepage())));
1001 +
1534 1002 if (!is_null($uri)) : // A site-specific ping always updates
1535 1003 $timely = true;
1536 1004 else :
1537 1005 $timely = $feed->stale();
@@ -1536,17 +1004,22 @@
1536 1004 else :
1537 1005 $timely = $feed->stale();
1538 1006 endif;
1539 1007
1540 - 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...
1541 1009 $delta = array('new' => 0, 'updated' => 0); // ... don't return error condition
1542 1010 endif;
1543 1011
1544 1012 if ($pinged_that and $timely) :
1545 1013 do_action('feedwordpress_check_feed', $feed->settings);
1546 - $added = $feed->poll();
1547 - if (isset($added['new'])) : $delta['new'] += $added['new']; endif;
1548 - if (isset($added['updated'])) : $delta['updated'] += $added['updated']; endif;
1014 + $start_ts = time();
1015 + $added = $feed->poll($crash_ts);
1016 + do_action('feedwordpress_check_feed_complete', $feed->settings, $added, time() - $start_ts);
1017 +
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;
1549 1022 endif;
1550 1023 endforeach;
1551 1024
1552 1025 do_action('feedwordpress_update_complete', $delta);
@@ -1553,10 +1026,23 @@
1553 1026
1554 1027 return $delta;
1555 1028 }
1556 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 +
1557 1040 function stale () {
1558 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 +
1559 1045 $last = get_option('feedwordpress_last_update_all');
1560 1046
1561 1047 // If we haven't updated all yet, give it a time window
1562 1048 if (false === $last) :
@@ -1575,72 +1061,252 @@
1575 1061 else :
1576 1062 FeedWordPress::critical_bug('FeedWordPress::stale::last', $last, __LINE__);
1577 1063 endif;
1578 1064
1579 - // Explicit request for an update (e.g. from a cron job).
1580 - elseif (FeedWordPress::update_requested()) :
1581 - $ret = true;
1582 -
1583 1065 else :
1584 1066 $ret = false;
1585 1067 endif;
1586 1068 return $ret;
1587 1069 } // FeedWordPress::stale()
1070 +
1071 + function init () {
1072 + $this->clear_cache_magic_url();
1073 + $this->update_magic_url();
1074 + } /* FeedWordPress::init() */
1588 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 +
1589 1184 function update_requested () {
1590 - return (isset($_REQUEST['update_feedwordpress']) and $_REQUEST['update_feedwordpress']);
1185 + return (
1186 + isset($_REQUEST['update_feedwordpress'])
1187 + and $_REQUEST['update_feedwordpress']
1188 + );
1591 1189 } // FeedWordPress::update_requested()
1592 1190
1593 - 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') {
1594 1209 global $wpdb;
1595 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) {
1596 1222 // Get the category ID#
1597 1223 $cat_id = FeedWordPress::link_category_id();
1598 -
1224 + if (!is_wp_error($cat_id)) :
1225 + $link_category = array($cat_id);
1226 + else :
1227 + $link_category = array();
1228 + endif;
1229 +
1599 1230 // WordPress gets cranky if there's no homepage URI
1600 - if (!isset($uri) or strlen($uri)<1) : $uri = $rss; endif;
1231 + if (!is_string($uri) or strlen($uri)<1) : $uri = $rss; endif;
1601 1232
1602 - if (function_exists('wp_insert_link')) { // WordPress 2.x
1603 - $link_id = wp_insert_link(array(
1604 - "link_name" => $name,
1605 - "link_url" => $uri,
1606 - "link_category" => array($cat_id),
1607 - "link_rss" => $rss
1608 - ));
1609 - } else { // WordPress 1.5.x
1610 - $result = $wpdb->query("
1611 - INSERT INTO $wpdb->links
1612 - SET
1613 - link_name = '".$wpdb->escape($name)."',
1614 - link_url = '".$wpdb->escape($uri)."',
1615 - link_category = '".$wpdb->escape($cat_id)."',
1616 - link_rss = '".$wpdb->escape($rss)."'
1617 - ");
1618 - $link_id = $wpdb->insert_id;
1619 - } // 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 +
1620 1243 return $link_id;
1621 - } // function FeedWordPress::syndicate_link()
1244 + } /* function FeedWordPress::syndicate_link() */
1622 1245
1623 - function on_unfamiliar ($what = 'author', $override = NULL) {
1624 - $set = array('create', 'default', 'filter');
1246 + /*static*/ function syndicated_status ($what, $default) {
1247 + $ret = get_option("feedwordpress_syndicated_{$what}_status");
1248 + if (!$ret) :
1249 + $ret = $default;
1250 + endif;
1251 + return $ret;
1252 + } /* FeedWordPress::syndicated_status() */
1625 1253
1626 - $ret = strtolower($override);
1627 - if (!in_array($ret, $set)) :
1628 - $ret = get_option('feedwordpress_unfamiliar_'.$what);
1629 - if (!in_array($ret, $set)) :
1630 - $ret = 'create';
1631 - endif;
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);
1262 + } // function FeedWordPress::on_unfamiliar()
1263 +
1264 + function null_email_set () {
1265 + $base = get_option('feedwordpress_null_email_set');
1266 +
1267 + if ($base===false) :
1268 + $ret = array('noreply@blogger.com'); // default
1269 + else :
1270 + $ret = array_map('strtolower',
1271 + array_map('trim', explode("\n", $base)));
1632 1272 endif;
1273 + $ret = apply_filters('syndicated_item_author_null_email_set', $ret);
1274 + return $ret;
1633 1275
1276 + } /* FeedWordPress::null_email_set () */
1277 +
1278 + function is_null_email ($email) {
1279 + $ret = in_array(strtolower(trim($email)), FeedWordPress::null_email_set());
1280 + $ret = apply_filters('syndicated_item_author_is_null_email', $ret, $email);
1634 1281 return $ret;
1635 - } // function FeedWordPress::on_unfamiliar()
1282 + } /* FeedWordPress::is_null_email () */
1636 1283
1637 - function syndicated_links () {
1284 + function use_aggregator_source_data () {
1285 + $ret = get_option('feedwordpress_use_aggregator_source_data');
1286 + return apply_filters('syndicated_post_use_aggregator_source_data', ($ret=='yes'));
1287 + }
1288 +
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()) {
1638 1301 $contributors = FeedWordPress::link_category_id();
1639 - if (function_exists('get_bookmarks')) :
1640 - $links = get_bookmarks(array("category" => $contributors));
1641 - else:
1642 - $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();
1643 1309 endif;
1644 1310 return $links;
1645 1311 } // function FeedWordPress::syndicated_links()
1646 1312
@@ -1648,61 +1314,32 @@
1648 1314 global $wpdb, $wp_db_version;
1649 1315
1650 1316 $cat_id = get_option('feedwordpress_cat_id');
1651 1317
1652 - // If we don't yet *have* the category, we'll have to create it
1653 - if ($cat_id === false) :
1654 - $cat = $wpdb->escape(DEFAULT_SYNDICATION_CATEGORY);
1655 -
1656 - // Look for something with the right name...
1657 - // -----------------------------------------
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);
1658 1321
1659 - // WordPress 2.3 introduces a new taxonomy/term API
1660 - if (function_exists('is_term')) :
1661 - $cat_id = is_term($cat, 'link_category');
1662 - // WordPress 2.1 and 2.2 use a common table for both link and post categories
1663 - elseif (isset($wp_db_version) and ($wp_db_version >= FWP_SCHEMA_21 and $wp_db_version < FWP_SCHEMA_23) ) :
1664 - $cat_id = $wpdb->get_var("SELECT cat_id FROM {$wpdb->categories} WHERE cat_name='$cat'");
1665 - // WordPress 1.5 and 2.0.x have a separate table for link categories
1666 - elseif (!isset($wp_db_version) or $wp_db_version < FWP_SCHEMA_21) :
1667 - $cat_id = $wpdb->get_var("SELECT cat_id FROM {$wpdb->linkcategories} WHERE cat_name='$cat'");
1668 - // This should never happen.
1669 - else :
1670 - 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);
1671 1325 endif;
1672 1326
1673 - // If you still can't find anything, make it for yourself.
1674 - // -------------------------------------------------------
1675 - if (!$cat_id) :
1676 - // WordPress 2.3+ term/taxonomy API
1677 - if (function_exists('wp_insert_term')) :
1678 - $term = wp_insert_term($cat, 'link_category');
1679 - $cat_id = $term['term_id'];
1680 - // WordPress 2.1, 2.2 category API. By the way, why the fuck is this API function only available in a wp-admin module?
1681 - elseif (function_exists('wp_insert_category')) :
1682 - $cat_id = wp_insert_category(array('cat_name' => $cat));
1683 - // WordPress 1.5 and 2.0.x
1684 - elseif (!isset($wp_db_version) or $wp_db_version < FWP_SCHEMA_21) :
1685 - $result = $wpdb->query("
1686 - INSERT INTO $wpdb->linkcategories
1687 - SET
1688 - cat_id = 0,
1689 - cat_name='$cat',
1690 - show_images='N',
1691 - show_description='N',
1692 - show_rating='N',
1693 - show_updated='N',
1694 - sort_order='name'
1695 - ");
1696 - $cat_id = $wpdb->insert_id;
1697 - // This should never happen.
1698 - else :
1699 - FeedWordPress::critical_bug('FeedWordPress::link_category_id::wp_db_version', $wp_db_version, __LINE__);
1700 - 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);
1701 1339 endif;
1340 + endif;
1702 1341
1703 - update_option('feedwordpress_cat_id', $cat_id);
1704 - endif;
1705 1342 return $cat_id;
1706 1343 } // function FeedWordPress::link_category_id()
1707 1344
1708 1345 # Upgrades and maintenance...
@@ -1712,12 +1349,9 @@
1712 1349 $ret = false; // innocent until proven guilty
1713 1350 if (!$fwp_db_version or $fwp_db_version < FEEDWORDPRESS_VERSION) :
1714 1351 // This is an older version or a fresh install. Does it
1715 1352 // require a database upgrade or database initialization?
1716 - if ($fwp_db_version > 0.96) :
1717 - // No. Just brand it with the new version.
1718 - update_option('feedwordpress_version', FEEDWORDPRESS_VERSION);
1719 - else :
1353 + if ($fwp_db_version <= 0.96) :
1720 1354 // Yes. Check to see whether this is a fresh install or an upgrade.
1721 1355 $syn = $wpdb->get_col("
1722 1356 SELECT post_id
1723 1357 FROM $wpdb->postmeta
@@ -1727,8 +1361,53 @@
1727 1361 $ret = true;
1728 1362 else : // fresh install; brand it as ours
1729 1363 update_option('feedwordpress_version', FEEDWORDPRESS_VERSION);
1730 1364 endif;
1365 + elseif ($fwp_db_version < 2009.0707) :
1366 + // We need to clear out any busted AJAX crap
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');
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;
1406 + update_option('feedwordpress_version', FEEDWORDPRESS_VERSION);
1407 + else :
1408 + // No. Just brand it with the new version.
1409 + update_option('feedwordpress_version', FEEDWORDPRESS_VERSION);
1731 1410 endif;
1732 1411 endif;
1733 1412 return $ret;
1734 1413 }
@@ -1738,56 +1417,14 @@
1738 1417
1739 1418 if (is_null($from) or $from <= 0.96) : $from = 0.96; endif;
1740 1419
1741 1420 switch ($from) :
1742 - case 0.96: // account for changes to syndication custom values and guid
1743 - 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.
1744 1426
1745 - $cat_id = FeedWordPress::link_category_id();
1746 -
1747 - // Avoid duplicates
1748 - $wpdb->query("DELETE FROM `{$wpdb->postmeta}` WHERE meta_key = 'syndication_feed_id'");
1749 -
1750 - // Look up all the link IDs
1751 - $wpdb->query("
1752 - CREATE TEMPORARY TABLE tmp_custom_values
1753 - SELECT
1754 - NULL AS meta_id,
1755 - post_id,
1756 - 'syndication_feed_id' AS meta_key,
1757 - link_id AS meta_value
1758 - FROM `{$wpdb->postmeta}`, `{$wpdb->links}`
1759 - WHERE
1760 - meta_key='syndication_feed'
1761 - AND meta_value=link_rss
1762 - AND link_category = {$cat_id}
1763 - ");
1764 -
1765 - // Now attach them to their posts
1766 - $wpdb->query("INSERT INTO `{$wpdb->postmeta}` SELECT * FROM tmp_custom_values");
1767 -
1768 - // And clean up after ourselves.
1769 - $wpdb->query("DROP TABLE tmp_custom_values");
1770 -
1771 - // Now fix the guids to avoid duplicate posts
1772 - echo "<ul>";
1773 - foreach ($this->feeds as $feed) :
1774 - echo "<li>Fixing post meta-data for <cite>".$feed['link/name']."</cite> &#8230; "; flush();
1775 - $rss = @fetch_rss($feed['link/uri']);
1776 - if (is_array($rss->items)) :
1777 - foreach ($rss->items as $item) :
1778 - $guid = $wpdb->escape(FeedWordPress::guid($item, $feed)); // new GUID algorithm
1779 - $link = $wpdb->escape($item['link']);
1780 -
1781 - $wpdb->query("
1782 - UPDATE `{$wpdb->posts}` SET guid='{$guid}' WHERE guid='{$link}'
1783 - ");
1784 - endforeach;
1785 - endif;
1786 - echo "<strong>complete.</strong></li>\n";
1787 - endforeach;
1788 - echo "</ul>\n";
1789 -
1790 1427 // Mark the upgrade as successful.
1791 1428 update_option('feedwordpress_version', FEEDWORDPRESS_VERSION);
1792 1429 endswitch;
1793 1430 echo "<p>Upgrade complete. FeedWordPress is now ready to use again.</p>";
@@ -1792,1585 +1429,738 @@
1792 1429 endswitch;
1793 1430 echo "<p>Upgrade complete. FeedWordPress is now ready to use again.</p>";
1794 1431 } /* FeedWordPress::upgrade_database() */
1795 1432
1796 - # Utility functions for handling text settings
1797 - function negative ($f, $setting) {
1798 - $nego = array ('n', 'no', 'f', 'false');
1799 - return (isset($f[$setting]) and in_array(strtolower($f[$setting]), $nego));
1800 - }
1801 -
1802 - function affirmative ($f, $setting) {
1803 - $affirmo = array ('y', 'yes', 't', 'true', 1);
1804 - return (isset($f[$setting]) and in_array(strtolower($f[$setting]), $affirmo));
1805 - }
1806 -
1807 -
1808 - # Internal debugging functions
1809 - function critical_bug ($varname, $var, $line) {
1810 - global $wp_version;
1811 -
1812 - 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>';
1813 - echo "\n<plaintext>";
1814 - echo "Triggered at line # ".$line."\n";
1815 - echo "FeedWordPress version: ".FEEDWORDPRESS_VERSION."\n";
1816 - echo "WordPress version: $wp_version\n";
1817 - echo "PHP version: ".phpversion()."\n";
1818 - echo "\n";
1819 - echo $varname.": "; var_dump($var); echo "\n";
1820 - die;
1821 - }
1822 -} // class FeedWordPress
1823 -
1824 -class SyndicatedPost {
1825 - var $item = null;
1826 -
1827 - var $link = null;
1828 - var $feed = null;
1829 - var $feedmeta = null;
1830 -
1831 - var $post = array ();
1832 - var $_base = null;
1833 -
1834 - var $_freshness = null;
1835 - var $_wp_id = null;
1836 -
1837 - var $strip_attrs = array (
1838 - array('[a-z]+', 'style'),
1839 - array('[a-z]+', 'target'),
1840 - );
1841 -
1842 - function SyndicatedPost ($item, $link) {
1433 + function has_guid_index () {
1843 1434 global $wpdb;
1844 -
1845 - $this->link = $link;
1846 - $feedmeta = $link->settings;
1847 - $feed = $link->magpie;
1848 -
1849 - # This is ugly as all hell. I'd like to use apply_filters()'s
1850 - # alleged support for a variable argument count, but this seems
1851 - # to have been broken in WordPress 1.5. It'll be fixed somehow
1852 - # in WP 1.5.1, but I'm aiming at WP 1.5 compatibility across
1853 - # the board here.
1854 - #
1855 - # Cf.: <http://mosquito.wordpress.org/view.php?id=901>
1856 - global $fwp_channel, $fwp_feedmeta;
1857 - $fwp_channel = $feed; $fwp_feedmeta = $feedmeta;
1858 -
1859 - $this->feed = $feed;
1860 - $this->feedmeta = $feedmeta;
1861 -
1862 - $this->item = $item;
1863 - $this->item = apply_filters('syndicated_item', $this->item, $this);
1864 -
1865 - # Filters can halt further processing by returning NULL
1866 - if (is_null($this->item)) :
1867 - $this->post = NULL;
1868 - else :
1869 - # Note that nothing is run through $wpdb->escape() here.
1870 - # That's deliberate. The escaping is done at the point
1871 - # of insertion, not here, to avoid double-escaping and
1872 - # to avoid screwing with syndicated_post filters
1873 -
1874 - $this->post['post_title'] = $this->item['title'];
1875 -
1876 - // This just gives usan alphanumeric representation of
1877 - // the author. We will look up (or create) the numeric
1878 - // ID for the author in SyndicatedPost::add()
1879 - $this->post['named']['author'] = $this->author();
1880 -
1881 - # Identify content and sanitize it.
1882 - # ---------------------------------
1883 - if (isset($this->item['xhtml']['body'])) :
1884 - $content = $this->item['xhtml']['body'];
1885 - elseif (isset($this->item['xhtml']['div'])) :
1886 - $content = $this->item['xhtml']['div'];
1887 - elseif (isset($this->item['content']['encoded']) and $this->item['content']['encoded']):
1888 - $content = $this->item['content']['encoded'];
1889 - else:
1890 - $content = $this->item['description'];
1891 - endif;
1892 1435
1893 - # FeedWordPress used to resolve URIs relative to the
1894 - # feed URI. It now relies on the xml:base support
1895 - # baked in to the MagpieRSS upgrade. So all we do here
1896 - # now is to sanitize problematic attributes.
1897 - #
1898 - # This kind of sucks. I intend to replace it with
1899 - # lib_filter sometime soon.
1900 - foreach ($this->strip_attrs as $pair):
1901 - list($tag,$attr) = $pair;
1902 - $content = preg_replace (
1903 - ":(<$tag [^>]*)($attr=(\"[^\">]*\"|[^>\\s]+))([^>]*>):i",
1904 - "\\1\\4",
1905 - $content
1906 - );
1907 - endforeach;
1436 + $found = false; // Guilty until proven innocent.
1908 1437
1909 - # Identify and sanitize excerpt
1910 - $excerpt = NULL;
1911 - if ( isset($this->item['description']) and $this->item['description'] ) :
1912 - $excerpt = $this->item['description'];
1913 - elseif ( isset($content) and $content ) :
1914 - $excerpt = strip_tags($content);
1915 - if (strlen($excerpt) > 255) :
1916 - $excerpt = substr($excerpt,0,252).'...';
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;
1917 1446 endif;
1918 - endif;
1919 -
1920 - $this->post['post_content'] = $content;
1921 -
1922 - if (!is_null($excerpt)):
1923 - $this->post['post_excerpt'] = $excerpt;
1924 - endif;
1925 -
1926 - // This is unnecessary if we use wp_insert_post
1927 - if (!$this->use_api('wp_insert_post')) :
1928 - $this->post['post_name'] = sanitize_title($this->post['post_title']);
1929 - endif;
1930 -
1931 - $this->post['epoch']['issued'] = $this->published();
1932 - $this->post['epoch']['created'] = $this->created();
1933 - $this->post['epoch']['modified'] = $this->updated();
1934 -
1935 - // Dealing with timestamps in WordPress is so fucking fucked.
1936 - $offset = (int) get_option('gmt_offset') * 60 * 60;
1937 - $this->post['post_date'] = gmdate('Y-m-d H:i:s', $this->published() + $offset);
1938 - $this->post['post_modified'] = gmdate('Y-m-d H:i:s', $this->updated() + $offset);
1939 - $this->post['post_date_gmt'] = gmdate('Y-m-d H:i:s', $this->published());
1940 - $this->post['post_modified_gmt'] = gmdate('Y-m-d H:i:s', $this->updated());
1941 -
1942 - // Use feed-level preferences or the global default.
1943 - $this->post['post_status'] = $this->link->syndicated_status('post', 'publish');
1944 - $this->post['comment_status'] = $this->link->syndicated_status('comment', 'closed');
1945 - $this->post['ping_status'] = $this->link->syndicated_status('ping', 'closed');
1946 -
1947 - // Unique ID (hopefully a unique tag: URI); failing that, the permalink
1948 - $this->post['guid'] = $this->guid();
1949 -
1950 - // RSS 2.0 / Atom 1.0 enclosure support
1951 - if ( isset($this->item['enclosure#']) ) :
1952 - for ($i = 1; $i <= $this->item['enclosure#']; $i++) :
1953 - $eid = (($i > 1) ? "#{$id}" : "");
1954 - $this->post['meta']['enclosure'][] =
1955 - $this->item["enclosure{$eid}@url"]."\n".
1956 - $this->item["enclosure{$eid}@length"]."\n".
1957 - $this->item["enclosure{$eid}@type"];
1958 - endfor;
1959 - endif;
1960 -
1961 - // In case you want to point back to the blog this was syndicated from
1962 - if (isset($this->feed->channel['title'])) :
1963 - $this->post['meta']['syndication_source'] = $this->feed->channel['title'];
1964 - endif;
1965 - if (isset($this->feed->channel['link'])) :
1966 - $this->post['meta']['syndication_source_uri'] = $this->feed->channel['link'];
1967 - endif;
1968 -
1969 - // Store information on human-readable and machine-readable comment URIs
1970 - if (isset($this->item['comments'])) :
1971 - $this->post['meta']['rss:comments'] = $this->item['comments'];
1972 - endif;
1973 - if (isset($this->item['wfw']['commentrss'])) :
1974 - $this->post['meta']['wfw:commentRSS'] = $this->item['wfw']['commentrss'];
1975 - endif;
1976 -
1977 - // Store information to identify the feed that this came from
1978 - $this->post['meta']['syndication_feed'] = $this->feedmeta['link/uri'];
1979 - $this->post['meta']['syndication_feed_id'] = $this->feedmeta['link/id'];
1980 -
1981 - // In case you want to know the external permalink...
1982 - $this->post['meta']['syndication_permalink'] = $this->item['link'];
1983 -
1984 - // Feed-by-feed options for author and category creation
1985 - $this->post['named']['unfamiliar']['author'] = $this->feedmeta['unfamiliar author'];
1986 - $this->post['named']['unfamiliar']['category'] = $this->feedmeta['unfamiliar categories'];
1987 -
1988 - // Categories: start with default categories, if any
1989 - $fc = get_option("feedwordpress_syndication_cats");
1990 - if ($fc) :
1991 - $this->post['named']['preset/category'] = explode("\n", $fc);
1992 - else :
1993 - $this->post['named']['preset/category'] = array();
1994 - endif;
1995 -
1996 - if (is_array($this->feedmeta['cats'])) :
1997 - $this->post['named']['preset/category'] = array_merge($this->post['named']['preset/category'], $this->feedmeta['cats']);
1998 - endif;
1999 -
2000 - // Now add categories from the post, if we have 'em
2001 - $this->post['named']['category'] = array();
2002 - if ( isset($this->item['category#']) ) :
2003 - for ($i = 1; $i <= $this->item['category#']; $i++) :
2004 - $cat_idx = (($i > 1) ? "#{$i}" : "");
2005 - $cat = $this->item["category{$cat_idx}"];
2006 -
2007 - if ( isset($this->feedmeta['cat_split']) and strlen($this->feedmeta['cat_split']) > 0) :
2008 - $pcre = "\007".$this->feedmeta['cat_split']."\007";
2009 - $this->post['named']['category'] = array_merge($this->post['named']['category'], preg_split($pcre, $cat, -1 /*=no limit*/, PREG_SPLIT_NO_EMPTY));
2010 - else :
2011 - $this->post['named']['category'][] = $cat;
2012 - endif;
2013 - endfor;
2014 - endif;
1447 + endforeach;
2015 1448 endif;
2016 - } // SyndicatedPost::SyndicatedPost()
2017 -
2018 - function filtered () {
2019 - return is_null($this->post);
2020 - }
2021 -
2022 - function freshness () {
1449 + return $found;
1450 + } /* FeedWordPress::has_guid_index () */
1451 +
1452 + function create_guid_index () {
2023 1453 global $wpdb;
2024 -
2025 - if ($this->filtered()) : // This should never happen.
2026 - FeedWordPress::critical_bug('SyndicatedPost', $this, __LINE__);
2027 - endif;
2028 1454
2029 - if (is_null($this->_freshness)) :
2030 - $guid = $wpdb->escape($this->guid());
2031 -
2032 - $result = $wpdb->get_row("
2033 - SELECT id, guid, post_modified_gmt
2034 - FROM $wpdb->posts WHERE guid='$guid'
2035 - ");
2036 -
2037 - preg_match('/([0-9]+)-([0-9]+)-([0-9]+) ([0-9]+):([0-9]+):([0-9]+)/', $result->post_modified_gmt, $backref);
2038 - $updated = gmmktime($backref[4], $backref[5], $backref[6], $backref[2], $backref[3], $backref[1]);
2039 - if (!$result) :
2040 - $this->_freshness = 2; // New content
2041 - elseif ($this->updated() > $updated) :
2042 - $this->_freshness = 1; // Updated content
2043 - $this->_wp_id = $result->id;
2044 - else :
2045 - $this->_freshness = 0; // Same old, same old
2046 - $this->_wp_id = $result->id;
2047 - endif;
2048 - endif;
2049 - return $this->_freshness;
1455 + $wpdb->query("
1456 + CREATE INDEX {$wpdb->posts}_guid_idx ON {$wpdb->posts}(guid)
1457 + ");
1458 + } /* FeedWordPress::create_guid_index () */
1459 +
1460 + function remove_guid_index () {
1461 + global $wpdb;
1462 +
1463 + $wpdb->query("
1464 + DROP INDEX {$wpdb->posts}_guid_idx ON {$wpdb->posts}
1465 + ");
2050 1466 }
2051 1467
2052 - function wp_id () {
2053 - if ($this->filtered()) : // This should never happen.
2054 - FeedWordPress::critical_bug('SyndicatedPost', $this, __LINE__);
2055 - endif;
2056 -
2057 - if (is_null($this->_wp_id) and is_null($this->_freshness)) :
2058 - $fresh = $this->freshness(); // sets WP DB id in the process
2059 - endif;
2060 - return $this->_wp_id;
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 + );
2061 1473 }
1474 +
1475 + /*static*/ function fetch ($url, $params = array()) {
1476 + $force_feed = true; // Default
2062 1477
2063 - function store () {
2064 - global $wpdb;
2065 -
2066 - if ($this->filtered()) : // This should never happen.
2067 - FeedWordPress::critical_bug('SyndicatedPost', $this, __LINE__);
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);
2068 1490 endif;
1491 + $timeout = intval($timeout);
2069 1492
2070 - $freshness = $this->freshness();
2071 - if ($freshness > 0) :
2072 - # -- Look up, or create, numeric ID for author
2073 - $this->post['post_author'] = $this->author_id (
2074 - FeedWordPress::on_unfamiliar('author', $this->post['named']['unfamiliar']['author'])
2075 - );
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');
2076 1497
2077 - if (is_null($this->post['post_author'])) :
2078 - $this->post = NULL;
2079 - endif;
2080 - endif;
2081 -
2082 - if (!$this->filtered() and $freshness > 0) :
2083 - # -- Look up, or create, numeric ID for categories
2084 - $this->post['post_category'] = $this->category_ids (
2085 - $this->post['named']['category'],
2086 - FeedWordPress::on_unfamiliar('category', $this->post['named']['unfamiliar']['category'])
2087 - );
2088 -
2089 - if (is_null($this->post['post_category'])) :
2090 - // filter mode on, no matching categories; drop the post
2091 - $this->post = NULL;
2092 - else :
2093 - // filter mode off or at least one match; now add on the feed and global presets
2094 - $this->post['post_category'] = array_merge (
2095 - $this->post['post_category'],
2096 - $this->category_ids (
2097 - $this->post['named']['preset/category'],
2098 - 'default'
2099 - )
2100 - );
1498 + $sniffer_class = apply_filters('feedwordpress_sniffer_class', 'FeedWordPress_Content_Type_Sniffer');
2101 1499
2102 - if (count($this->post['post_category']) < 1) :
2103 - $this->post['post_category'][] = 1; // Default to category 1 ("Uncategorized" / "General") if nothing else
2104 - endif;
2105 - endif;
2106 - endif;
1500 + $feed = new $pie_class;
1501 + $feed->set_feed_url($url);
1502 + $feed->set_cache_class($cache_class);
1503 + $feed->set_timeout($timeout);
2107 1504
2108 - if (!$this->filtered() and $freshness > 0) :
2109 - unset($this->post['named']);
2110 - $this->post = apply_filters('syndicated_post', $this->post, $this);
2111 - endif;
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();
2112 1513
2113 - if (!$this->filtered() and $freshness == 2) :
2114 - // The item has not yet been added. So let's add it.
2115 - $this->insert_new();
2116 - $this->add_rss_meta();
2117 - do_action('post_syndicated_item', $this->wp_id());
2118 -
2119 - $ret = 'new';
2120 - elseif (!$this->filtered() and $freshness == 1) :
2121 - $this->post['ID'] = $this->wp_id();
2122 - $this->update_existing();
2123 - $this->add_rss_meta();
2124 - do_action('update_syndicated_item', $this->wp_id());
2125 -
2126 - $ret = 'updated';
1514 + if ($feed->error()) :
1515 + $ret = new WP_Error('simplepie-error', $feed->error());
2127 1516 else :
2128 - $ret = false;
1517 + $ret = $feed;
2129 1518 endif;
2130 -
2131 1519 return $ret;
2132 - } // function SyndicatedPost::store ()
1520 + } /* FeedWordPress::fetch () */
2133 1521
2134 - function insert_new () {
2135 - global $wpdb, $wp_db_version;
1522 + function clear_cache () {
1523 + global $wpdb;
2136 1524
2137 - // Why the fuck doesn't wp_insert_post already do this?
2138 - foreach ($this->post as $key => $value) :
2139 - if (is_string($value)) :
2140 - $dbpost[$key] = $wpdb->escape($value);
2141 - else :
2142 - $dbpost[$key] = $value;
2143 - endif;
2144 - endforeach;
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 + ");
2145 1530
2146 - if ($this->use_api('wp_insert_post')) :
2147 - $dbpost['post_pingback'] = false; // Tell WP 2.1 and 2.2 not to process for pingbacks
2148 - $this->_wp_id = wp_insert_post($dbpost);
2149 -
2150 - // This should never happen.
2151 - if (!is_numeric($this->_wp_id) or ($this->_wp_id == 0)) :
2152 - FeedWordPress::critical_bug('SyndicatedPost (_wp_id problem)', $this, __LINE__);
2153 - endif;
2154 -
2155 - // Unfortunately, as of WordPress 2.3, wp_insert_post()
2156 - // *still* offers no way to use a guid of your choice,
2157 - // and munges your post modified timestamp, too.
2158 - $result = $wpdb->query("
2159 - UPDATE $wpdb->posts
2160 - SET
2161 - guid='{$dbpost['guid']}',
2162 - post_modified='{$dbpost['post_modified']}',
2163 - post_modified_gmt='{$dbpost['post_modified_gmt']}'
2164 - WHERE ID='{$this->_wp_id}'
2165 - ");
2166 - else :
2167 - # The right way to do this is the above. But, alas,
2168 - # in earlier versions of WordPress, wp_insert_post has
2169 - # too much behavior (mainly related to pings) that can't
2170 - # be overridden. In WordPress 1.5, it's enough of a
2171 - # resource hog to make PHP segfault after inserting
2172 - # 50-100 posts. This can get pretty annoying, especially
2173 - # if you are trying to update your feeds for the first
2174 - # time.
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
2175 1543
2176 - $result = $wpdb->query("
2177 - INSERT INTO $wpdb->posts
2178 - SET
2179 - guid = '{$dbpost['guid']}',
2180 - post_author = '{$dbpost['post_author']}',
2181 - post_date = '{$dbpost['post_date']}',
2182 - post_date_gmt = '{$dbpost['post_date_gmt']}',
2183 - post_content = '{$dbpost['post_content']}',"
2184 - .(isset($dbpost['post_excerpt']) ? "post_excerpt = '{$dbpost['post_excerpt']}'," : "")."
2185 - post_title = '{$dbpost['post_title']}',
2186 - post_name = '{$dbpost['post_name']}',
2187 - post_modified = '{$dbpost['post_modified']}',
2188 - post_modified_gmt = '{$dbpost['post_modified_gmt']}',
2189 - comment_status = '{$dbpost['comment_status']}',
2190 - ping_status = '{$dbpost['ping_status']}',
2191 - post_status = '{$dbpost['post_status']}'
2192 - ");
2193 - $this->_wp_id = $wpdb->insert_id;
1544 + return ($magpies + $simplepies);
1545 + } /* FeedWordPress::clear_cache () */
2194 1546
2195 - // This should never happen.
2196 - if (!is_numeric($this->_wp_id) or ($this->_wp_id == 0)) :
2197 - FeedWordPress::critical_bug('SyndicatedPost::_wp_id', $this->_wp_id, __LINE__);
2198 - endif;
1547 + function cache_duration () {
1548 + $duration = NULL;
1549 + if (defined('FEEDWORDPRESS_CACHE_AGE')) :
1550 + $duration = FEEDWORDPRESS_CACHE_AGE;
1551 + endif;
1552 + return $duration;
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;
2199 1558
2200 - // WordPress 1.5.x - 2.0.x
2201 - wp_set_post_cats('1', $this->wp_id(), $this->post['post_category']);
2202 -
2203 - // Since we are not going through official channels, we need to
2204 - // manually tell WordPress that we've published a new post.
2205 - // We need to make sure to do this in order for FeedWordPress
2206 - // to play well with the staticize-reloaded plugin (something
2207 - // that a large aggregator website is going to *want* to be
2208 - // able to use).
2209 - do_action('publish_post', $this->_wp_id);
1559 + // Fall back to the cache freshness duration
1560 + elseif (defined('FEEDWORDPRESS_CACHE_AGE')) :
1561 + $duration = FEEDWORDPRESS_CACHE_AGE;
2210 1562 endif;
2211 - } /* SyndicatedPost::insert_new() */
1563 +
1564 + // Fall back to WordPress default
1565 + return $duration;
1566 + } /* FeedWordPress::cache_lifetime () */
2212 1567
2213 - function update_existing () {
2214 - global $wpdb;
1568 + # Utility functions for handling text settings
1569 + function negative ($f, $setting) {
1570 + $nego = array ('n', 'no', 'f', 'false');
1571 + return (isset($f[$setting]) and in_array(strtolower($f[$setting]), $nego));
1572 + }
2215 1573
2216 - // Why the fuck doesn't wp_insert_post already do this?
2217 - $dbpost = array();
2218 - foreach ($this->post as $key => $value) :
2219 - if (is_string($value)) :
2220 - $dbpost[$key] = $wpdb->escape($value);
2221 - else :
2222 - $dbpost[$key] = $value;
2223 - endif;
2224 - endforeach;
1574 + function affirmative ($f, $setting) {
1575 + $affirmo = array ('y', 'yes', 't', 'true', 1);
1576 + return (isset($f[$setting]) and in_array(strtolower($f[$setting]), $affirmo));
1577 + }
2225 1578
2226 - if ($this->use_api('wp_insert_post')) :
2227 - $dbpost['post_pingback'] = false; // Tell WP 2.1 and 2.2 not to process for pingbacks
2228 - $this->_wp_id = wp_insert_post($dbpost);
1579 + # Internal debugging functions
1580 + function critical_bug ($varname, $var, $line) {
1581 + global $wp_version;
2229 1582
2230 - // This should never happen.
2231 - if (!is_numeric($this->_wp_id) or ($this->_wp_id == 0)) :
2232 - FeedWordPress::critical_bug('SyndicatedPost::_wp_id', $this->_wp_id, __LINE__);
2233 - endif;
2234 -
2235 - // Unfortunately, as of WordPress 2.3, wp_insert_post()
2236 - // munges your post modified timestamp.
2237 - $result = $wpdb->query("
2238 - UPDATE $wpdb->posts
2239 - SET
2240 - post_modified='{$dbpost['post_modified']}',
2241 - post_modified_gmt='{$dbpost['post_modified_gmt']}'
2242 - WHERE ID='{$this->_wp_id}'
2243 - ");
2244 - else :
2245 -
2246 - $result = $wpdb->query("
2247 - UPDATE $wpdb->posts
2248 - SET
2249 - post_author = '{$dbpost['post_author']}',
2250 - post_content = '{$dbpost['post_content']}',"
2251 - .(isset($dbpost['post_excerpt']) ? "post_excerpt = '{$dbpost['post_excerpt']}'," : "")."
2252 - post_title = '{$dbpost['post_title']}',
2253 - post_name = '{$dbpost['post_name']}',
2254 - post_modified = '{$dbpost['post_modified']}',
2255 - post_modified_gmt = '{$dbpost['post_modified_gmt']}'
2256 - WHERE guid='{$dbpost['guid']}'
2257 - ");
1583 + echo '<p>There may be a bug in FeedWordPress. Please <a href="'.FEEDWORDPRESS_AUTHOR_CONTACT.'">contact the author</a> and paste the following information into your e-mail:</p>';
1584 + echo "\n<plaintext>";
1585 + echo "Triggered at line # ".$line."\n";
1586 + echo "FeedWordPress version: ".FEEDWORDPRESS_VERSION."\n";
1587 + echo "WordPress version: {$wp_version}\n";
1588 + echo "PHP version: ".phpversion()."\n";
1589 + echo "\n";
1590 + echo $varname.": "; var_dump($var); echo "\n";
1591 + die;
1592 + }
2258 1593
2259 - // WordPress 2.1.x and up
2260 - if (function_exists('wp_set_post_categories')) :
2261 - wp_set_post_categories($this->wp_id(), $this->post['post_category']);
2262 - // WordPress 1.5.x - 2.0.x
2263 - elseif (function_exists('wp_set_post_cats')) :
2264 - wp_set_post_cats('1', $this->wp_id(), $this->post['post_category']);
2265 - // This should never happen.
2266 - else :
2267 - FeedWordPress::critical_bug('SyndicatedPost::insert_new::wp_version', $GLOBALS['wp_version'], __LINE__);
2268 - endif;
1594 + function noncritical_bug ($varname, $var, $line) {
1595 + if (FEEDWORDPRESS_DEBUG) : // halt only when we are doing debugging
1596 + FeedWordPress::critical_bug($varname, $var, $line);
1597 + endif;
1598 + }
2269 1599
2270 - // Since we are not going through official channels, we need to
2271 - // manually tell WordPress that we've published a new post.
2272 - // We need to make sure to do this in order for FeedWordPress
2273 - // to play well with the staticize-reloaded plugin (something
2274 - // that a large aggregator website is going to *want* to be
2275 - // able to use).
2276 - do_action('edit_post', $this->post['ID']);
1600 + function val ($v, $no_newlines = false) {
1601 + ob_start();
1602 + var_dump($v);
1603 + $out = ob_get_contents(); ob_end_clean();
1604 +
1605 + if ($no_newlines) :
1606 + $out = preg_replace('/\s+/', " ", $out);
2277 1607 endif;
2278 - } /* SyndicatedPost::update_existing() */
1608 + return $out;
1609 + } /* FeedWordPress::val () */
2279 1610
2280 - // SyndicatedPost::add_rss_meta: adds interesting meta-data to each entry
2281 - // using the space for custom keys. The set of keys and values to add is
2282 - // specified by the keys and values of $post['meta']. This is used to
2283 - // store anything that the WordPress user might want to access from a
2284 - // template concerning the post's original source that isn't provided
2285 - // for by standard WP meta-data (i.e., any interesting data about the
2286 - // syndicated post other than author, title, timestamp, categories, and
2287 - // guid). It's also used to hook into WordPress's support for
2288 - // enclosures.
2289 - function add_rss_meta () {
2290 - global $wpdb;
2291 - if ( is_array($this->post) and isset($this->post['meta']) and is_array($this->post['meta']) ) :
2292 - $postId = $this->wp_id();
2293 -
2294 - // Aggregated posts should NOT send out pingbacks.
2295 - // WordPress 2.1-2.2 claim you can tell them not to
2296 - // using $post_pingback, but they don't listen, so we
2297 - // make sure here.
2298 - $result = $wpdb->query("
2299 - DELETE FROM $wpdb->postmeta
2300 - WHERE post_id='$postId' AND meta_key='_pingme'
2301 - ");
1611 + function diagnostic_on ($level) {
1612 + $show = get_option('feedwordpress_diagnostics_show', array());
1613 + return (in_array($level, $show));
1614 + } /* FeedWordPress::diagnostic_on () */
2302 1615
2303 - foreach ( $this->post['meta'] as $key => $values ) :
1616 + function diagnostic ($level, $out, $persist = NULL, $since = NULL, $mostRecent = NULL) {
1617 + global $feedwordpress_admin_footer;
2304 1618
2305 - $key = $wpdb->escape($key);
1619 + $output = get_option('feedwordpress_diagnostics_output', array());
1620 + $dlog = get_option('feedwordpress_diagnostics_log', array());
2306 1621
2307 - // If this is an update, clear out the old
2308 - // values to avoid duplication.
2309 - $result = $wpdb->query("
2310 - DELETE FROM $wpdb->postmeta
2311 - WHERE post_id='$postId' AND meta_key='$key'
2312 - ");
1622 + $diagnostic_nesting = count(explode(":", $level));
2313 1623
2314 - // Allow for either a single value or an array
2315 - if (!is_array($values)) $values = array($values);
2316 - foreach ( $values as $value ) :
2317 - $value = $wpdb->escape($value);
2318 - $result = $wpdb->query("
2319 - INSERT INTO $wpdb->postmeta
2320 - SET
2321 - post_id='$postId',
2322 - meta_key='$key',
2323 - meta_value='$value'
2324 - ");
2325 - endforeach;
1624 + if (FeedWordPress::diagnostic_on($level)) :
1625 + foreach ($output as $method) :
1626 + switch ($method) :
1627 + case 'echo' :
1628 + echo "<div><pre><strong>Diag".str_repeat('====', $diagnostic_nesting-1).'|</strong> '.$out."</pre></div>";
1629 + break;
1630 + case 'echo_in_cronjob' :
1631 + if (FeedWordPress::update_requested()) :
1632 + echo FeedWordPress::log_prefix()." ".$out."\n";
1633 + endif;
1634 + break;
1635 + case 'admin_footer' :
1636 + $feedwordpress_admin_footer[] = $out;
1637 + break;
1638 + case 'error_log' :
1639 + error_log(FeedWordPress::log_prefix().' '.$out);
1640 + break;
1641 + case 'email' :
1642 + if (is_null($persist)) :
1643 + $sect = 'occurrent';
1644 + $hook = (isset($dlog['mesg'][$sect]) ? count($dlog['mesg'][$sect]) : 0);
1645 + $line = array("Time" => time(), "Message" => $out);
1646 + else :
1647 + $sect = 'persistent';
1648 + $hook = md5($level."\n".$persist);
1649 + $line = array("Since" => $since, "Message" => $out, "Most Recent" => $mostRecent);
1650 + endif;
1651 +
1652 + if (!isset($dlog['mesg'])) : $dlog['mesg'] = array(); endif;
1653 + if (!isset($dlog['mesg'][$sect])) : $dlog['mesg'][$sect] = array(); endif;
1654 +
1655 + $dlog['mesg'][$sect][$hook] = $line;
1656 + endswitch;
2326 1657 endforeach;
2327 1658 endif;
2328 - } /* SyndicatedPost::add_rss_meta () */
1659 +
1660 + update_option('feedwordpress_diagnostics_log', $dlog);
1661 + } /* FeedWordPress::diagnostic () */
1662 +
1663 + function email_diagnostic_log () {
1664 + $dlog = get_option('feedwordpress_diagnostics_log', array());
1665 +
1666 + if (isset($dlog['schedule']) and isset($dlog['schedule']['last'])) :
1667 + if (time() > ($dlog['schedule']['last'] + $dlog['schedule']['freq'])) :
1668 + // No news is good news; only send if
1669 + // there are some messages to send.
1670 + $body = NULL;
1671 + foreach ($dlog['mesg'] as $sect => $mesgs) :
1672 + if (count($mesgs) > 0) :
1673 + if (is_null($body)) : $body = ''; endif;
1674 +
1675 + $paradigm = reset($mesgs);
1676 + $body .= "<h2>".ucfirst($sect)." issues</h2>\n"
1677 + ."<table>\n"
1678 + ."<thead><tr>\n";
1679 + foreach ($paradigm as $col => $value) :
1680 + $body .= '<th scope="col">'.$col."</th>\n";
1681 + endforeach;
1682 + $body .= "</tr></thead>\n"
1683 + ."<tbody>\n";
1684 +
1685 + foreach ($mesgs as $line) :
1686 + $body .= "<tr>\n";
1687 + foreach ($line as $col => $cell) :
1688 + if (is_numeric($cell)) :
1689 + $cell = date('j-M-y, h:i a', $cell);
1690 + endif;
1691 + $class = strtolower(preg_replace('/\s+/', '-', $col));
1692 + $body .= "<td class=\"$class\">${cell}</td>";
1693 + endforeach;
1694 + $body .= "</tr>\n";
1695 + endforeach;
1696 +
1697 + $body .= "</tbody>\n</table>\n\n";
1698 + endif;
1699 + endforeach;
1700 +
1701 + if (!is_null($body)) :
1702 + $home = feedwordpress_display_url(get_bloginfo('url'));
1703 + $subj = $home . " syndication issues for ".date('j-M-y', time());
1704 + $agent = 'FeedWordPress '.FEEDWORDPRESS_VERSION;
1705 + $body = <<<EOMAIL
1706 +<html>
1707 +<head>
1708 +<title>$subj</title>
1709 +<style type="text/css">
1710 + body { background-color: white; color: black; }
1711 + table { width: 100%; border: 1px solid black; }
1712 + table thead tr th { background-color: #ff7700; color: white; border-bottom: 1px solid black; }
1713 + table thead tr { border-bottom: 1px solid black; }
1714 + table tr { vertical-align: top; }
1715 + table .since { width: 20%; }
1716 + table .time { width: 20%; }
1717 + table .most-recently { width: 20%; }
1718 + table .message { width: auto; }
1719 +</style>
1720 +</head>
1721 +<body>
1722 +<h1>Syndication Issues encountered by $agent on $home</h1>
1723 +$body
1724 +</body>
1725 +</html>
2329 1726
2330 - // SyndicatedPost::author_id (): get the ID for an author name from
2331 - // the feed. Create the author if necessary.
2332 - function author_id ($unfamiliar_author = 'create') {
2333 - global $wpdb, $wp_db_version; // test for WordPress 2.0 database schema
1727 +EOMAIL;
2334 1728
2335 - $a = $this->author();
2336 - $author = $a['name'];
2337 - $email = $a['email'];
2338 - $url = $a['uri'];
1729 + $ded = get_option('feedwordpress_diagnostics_email_destination', 'admins');
2339 1730
2340 - // Never can be too careful...
2341 - $login = sanitize_user($author, /*strict=*/ true);
2342 - $login = apply_filters('pre_user_login', $login);
2343 -
2344 - $nice_author = sanitize_title($author);
2345 - $nice_author = apply_filters('pre_user_nicename', $nice_author);
2346 -
2347 - $reg_author = $wpdb->escape(preg_quote($author));
2348 - $author = $wpdb->escape($author);
2349 - $email = $wpdb->escape($email);
2350 - $url = $wpdb->escape($url);
2351 -
2352 - // Look for an existing author record that fits...
2353 - if (!isset($wp_db_version)) :
2354 - #-- WordPress 1.5.x
2355 - $id = $wpdb->get_var(
2356 - "SELECT ID from $wpdb->users
2357 - WHERE
2358 - TRIM(LCASE(user_login)) = TRIM(LCASE('$login')) OR
2359 - (
2360 - LENGTH(TRIM(LCASE(user_email))) > 0
2361 - AND TRIM(LCASE(user_email)) = TRIM(LCASE('$email'))
2362 - ) OR
2363 - TRIM(LCASE(user_firstname)) = TRIM(LCASE('$author')) OR
2364 - TRIM(LCASE(user_nickname)) = TRIM(LCASE('$author')) OR
2365 - TRIM(LCASE(user_nicename)) = TRIM(LCASE('$nice_author')) OR
2366 - TRIM(LCASE(user_description)) = TRIM(LCASE('$author')) OR
2367 - (
2368 - LOWER(user_description)
2369 - RLIKE CONCAT(
2370 - '(^|\\n)a\\.?k\\.?a\\.?( |\\t)*:?( |\\t)*',
2371 - LCASE('$reg_author'),
2372 - '( |\\t|\\r)*(\\n|\$)'
2373 - )
2374 - )
2375 - ");
2376 - elseif ($wp_db_version >= 2966) :
2377 - #-- WordPress 2.0+
2378 -
2379 - # First try the user core data table.
2380 - $id = $wpdb->get_var(
2381 - "SELECT ID FROM $wpdb->users
2382 - WHERE
2383 - TRIM(LCASE(user_login)) = TRIM(LCASE('$login'))
2384 - OR (
2385 - LENGTH(TRIM(LCASE(user_email))) > 0
2386 - AND TRIM(LCASE(user_email)) = TRIM(LCASE('$email'))
2387 - )
2388 - OR TRIM(LCASE(user_nicename)) = TRIM(LCASE('$nice_author'))
2389 - ");
2390 -
2391 - if (is_null($id)) : # Then look for aliases in the user meta data table
2392 - $id = $wpdb->get_var(
2393 - "SELECT user_id FROM $wpdb->usermeta
2394 - WHERE
2395 - (meta_key = 'description' AND TRIM(LCASE(meta_value)) = TRIM(LCASE('$author')))
2396 - OR (
2397 - meta_key = 'description'
2398 - AND TRIM(LCASE(meta_value))
2399 - RLIKE CONCAT(
2400 - '(^|\\n)a\\.?k\\.?a\\.?( |\\t)*:?( |\\t)*',
2401 - TRIM(LCASE('$reg_author')),
2402 - '( |\\t|\\r)*(\\n|\$)'
2403 - )
2404 - )
2405 - ");
2406 - endif;
2407 - endif;
2408 -
2409 - // ... if you don't find one, then do what you need to do
2410 - if (is_null($id)) :
2411 - if ($unfamiliar_author === 'create') :
2412 - if (!isset($wp_db_version)) :
2413 - #-- WordPress 1.5.x
2414 - $wpdb->query (
2415 - "INSERT INTO $wpdb->users
2416 - SET
2417 - ID='0',
2418 - user_login='$login',
2419 - user_firstname='$author',
2420 - user_nickname='$author',
2421 - user_nicename='$nice_author',
2422 - user_description='$author',
2423 - user_email='$email',
2424 - user_url='$url'");
2425 - $id = $wpdb->insert_id;
2426 - elseif ($wp_db_version >= 2966) :
2427 - #-- WordPress 2.0+
2428 - $userdata = array();
1731 + // e-mail address
1732 + if (preg_match('/^mailto:(.*)$/', $ded, $ref)) :
1733 + $recipients = array($ref[1]);
1734 +
1735 + // userid
1736 + elseif (preg_match('/^user:(.*)$/', $ded, $ref)) :
1737 + $userdata = get_userdata((int) $ref[1]);
1738 + $recipients = array($userdata->user_email);
2429 1739
2430 - #-- user table data
2431 - $userdata['ID'] = NULL; // new user
2432 - $userdata['user_login'] = $login;
2433 - $userdata['user_nicename'] = $nice_author;
2434 - $userdata['user_pass'] = substr(md5(uniqid(microtime())), 0, 6); // just something random to lock it up
2435 - $userdata['user_email'] = $email;
2436 - $userdata['user_url'] = $url;
2437 - $userdata['display_name'] = $author;
1740 + // admins
1741 + else :
1742 + $recipients = FeedWordPressDiagnostic::admin_emails();
1743 + endif;
2438 1744
2439 - $id = wp_insert_user($userdata);
1745 + foreach ($recipients as $email) :
1746 + add_filter('wp_mail_content_type', array('FeedWordPress', 'allow_html_mail'));
1747 + wp_mail($email, $subj, $body);
1748 + remove_filter('wp_mail_content_type', array('FeedWordPress', 'allow_html_mail'));
1749 + endforeach;
2440 1750 endif;
2441 - elseif ($unfamiliar_author === 'default') :
2442 - $id = 1;
2443 - endif;
2444 - endif;
2445 - return $id;
2446 - } // function SyndicatedPost::author_id ()
2447 -
2448 - // look up (and create) category ids from a list of categories
2449 - function category_ids ($cats, $unfamiliar_category = 'create') {
2450 - global $wpdb;
2451 -
2452 - // We need to normalize whitespace because (1) trailing
2453 - // whitespace can cause PHP and MySQL not to see eye to eye on
2454 - // VARCHAR comparisons for some versions of MySQL (cf.
2455 - // <http://dev.mysql.com/doc/mysql/en/char.html>), and (2)
2456 - // because I doubt most people want to make a semantic
2457 - // distinction between 'Computers' and 'Computers '
2458 - $cats = array_map('trim', $cats);
2459 -
2460 - $cat_ids = array ();
2461 - foreach ($cats as $cat_name) :
2462 - if (preg_match('/^{#([0-9]+)}$/', $cat_name, $backref)) :
2463 - $cat_id = (int) $backref[1];
2464 - if (function_exists('is_term') and is_term($cat_id, 'category')) :
2465 - $cat_ids[] = $cat_id;
2466 - elseif (get_category($cat_id)) :
2467 - $cat_ids[] = $cat_id;
2468 - endif;
2469 - else :
2470 - $esc = $wpdb->escape($cat_name);
2471 - $resc = $wpdb->escape(preg_quote($cat_name));
2472 1751
2473 - // WordPress 2.3+
2474 - if (function_exists('is_term')) :
2475 - $cat_id = is_term($cat_name, 'category');
2476 - if ($cat_id) :
2477 - $cat_ids[] = $cat_id['term_id'];
2478 - // There must be a better way to do this...
2479 - elseif ($results = $wpdb->get_results(
2480 - "SELECT term_id
2481 - FROM $wpdb->term_taxonomy
2482 - WHERE
2483 - LOWER(description) RLIKE
2484 - CONCAT('(^|\\n)a\\.?k\\.?a\\.?( |\\t)*:?( |\\t)*', LOWER('{$resc}'), '( |\\t|\\r)*(\\n|\$)')"
2485 - )) :
2486 - foreach ($results AS $term) :
2487 - $cat_ids[] = (int) $term->term_id;
2488 - endforeach;
2489 - elseif ('create'===$unfamiliar_category) :
2490 - $term = wp_insert_term($cat_name, 'category');
2491 - $cat_ids[] = $term['term_id'];
2492 - endif;
1752 + // Clear the logs
1753 + $dlog['mesg']['persistent'] = array();
1754 + $dlog['mesg']['occurrent'] = array();
2493 1755
2494 - // WordPress 1.5.x - 2.2.x
2495 - else :
2496 - $results = $wpdb->get_results(
2497 - "SELECT cat_ID
2498 - FROM $wpdb->categories
2499 - WHERE
2500 - (LOWER(cat_name) = LOWER('$esc'))
2501 - OR (LOWER(category_description)
2502 - RLIKE CONCAT('(^|\\n)a\\.?k\\.?a\\.?( |\\t)*:?( |\\t)*', LOWER('{$resc}'), '( |\\t|\\r)*(\\n|\$)'))
2503 - ");
2504 - if ($results) :
2505 - foreach ($results as $term) :
2506 - $cat_ids[] = (int) $term->cat_ID;
2507 - endforeach;
2508 - elseif ('create'===$unfamiliar_category) :
2509 - if (function_exists('wp_insert_category')) :
2510 - $cat_id = wp_insert_category(array('cat_name' => $cat_name));
2511 - // And into the database we go.
2512 - else :
2513 - $nice_kitty = sanitize_title($cat_name);
2514 - $wpdb->query(sprintf("
2515 - INSERT INTO $wpdb->categories
2516 - SET
2517 - cat_name='%s',
2518 - category_nicename='%s'
2519 - ", $wpdb->escape($cat_name), $nice_kitty
2520 - ));
2521 - $cat_id = $wpdb->insert_id;
2522 - endif;
2523 - $cat_ids[] = $cat_id;
2524 - endif;
2525 - endif;
1756 + // Set schedule for next update
1757 + $dlog['schedule']['last'] = time();
2526 1758 endif;
2527 - endforeach;
2528 -
2529 - if ((count($cat_ids) == 0) and ($unfamiliar_category === 'filter')) :
2530 - $cat_ids = NULL; // Drop the post
2531 1759 else :
2532 - $cat_ids = array_unique($cat_ids);
1760 + $dlog['schedule'] = array(
1761 + 'freq' => 24 /*hr*/ * 60 /*min*/ * 60 /*s*/,
1762 + 'last' => time(),
1763 + );
2533 1764 endif;
2534 - return $cat_ids;
2535 - } // function SyndicatedPost::category_ids ()
2536 -
2537 - function use_api ($tag) {
2538 - global $wp_db_version;
2539 - if ('wp_insert_post'==$tag) :
2540 - // Before 2.2, wp_insert_post does too much of the wrong stuff to use it
2541 - // In 1.5 it was such a resource hog it would make PHP segfault on big updates
2542 - $ret = (isset($wp_db_version) and $wp_db_version > FWP_SCHEMA_21);
2543 - endif;
2544 - return $ret;
2545 - } // function SyndicatedPost::use_api ()
2546 -
2547 - #### EXTRACT DATA FROM FEED ITEM ####
2548 -
2549 - function created () {
2550 - $epoch = null;
2551 - if (isset($this->item['dc']['created'])) :
2552 - $epoch = @parse_w3cdtf($this->item['dc']['created']);
2553 - elseif (isset($this->item['dcterms']['created'])) :
2554 - $epoch = @parse_w3cdtf($this->item['dcterms']['created']);
2555 - elseif (isset($this->item['created'])): // Atom 0.3
2556 - $epoch = @parse_w3cdtf($this->item['created']);
2557 - endif;
2558 - return $epoch;
2559 - }
2560 - function published ($fallback = true) {
2561 - $epoch = null;
2562 -
2563 - # RSS is a fucking mess. Figure out whether we have a date in
2564 - # <dc:date>, <issued>, <pubDate>, etc., and get it into Unix
2565 - # epoch format for reformatting. If we can't find anything,
2566 - # we'll use the last-updated time.
2567 - if (isset($this->item['dc']['date'])): // Dublin Core
2568 - $epoch = @parse_w3cdtf($this->item['dc']['date']);
2569 - elseif (isset($this->item['dcterms']['issued'])) : // Dublin Core extensions
2570 - $epoch = @parse_w3cdtf($this->item['dcterms']['issued']);
2571 - elseif (isset($this->item['published'])) : // Atom 1.0
2572 - $epoch = @parse_w3cdtf($this->item['published']);
2573 - elseif (isset($this->item['issued'])): // Atom 0.3
2574 - $epoch = @parse_w3cdtf($this->item['issued']);
2575 - elseif (isset($this->item['pubdate'])): // RSS 2.0
2576 - $epoch = strtotime($this->item['pubdate']);
2577 - elseif ($fallback) : // Fall back to <updated> / <modified> if present
2578 - $epoch = $this->updated(/*fallback=*/ false);
2579 - endif;
2580 1765
2581 - # If everything failed, then default to the current time.
2582 - if (is_null($epoch)) :
2583 - $epoch = time();
2584 - endif;
2585 -
2586 - return $epoch;
2587 - }
2588 - function updated ($fallback = true) {
2589 - $epoch = null;
1766 + update_option('feedwordpress_diagnostics_log', $dlog);
1767 + } /* FeedWordPress::email_diagnostic_log () */
1768 +
1769 + function allow_html_mail () {
1770 + return 'text/html';
1771 + } /* FeedWordPress::allow_html_mail () */
2590 1772
2591 - # As far as I know, only dcterms and Atom have reliable ways to
2592 - # specify when something was *modified* last. If neither is
2593 - # available, then we'll try to get the time of publication.
2594 - if (isset($this->item['dc']['modified'])) : // Not really correct
2595 - $epoch = @parse_w3cdtf($this->item['dc']['modified']);
2596 - elseif (isset($this->item['dcterms']['modified'])) : // Dublin Core extensions
2597 - $epoch = @parse_w3cdtf($this->item['dcterms']['modified']);
2598 - elseif (isset($this->item['modified'])): // Atom 0.3
2599 - $epoch = @parse_w3cdtf($this->item['modified']);
2600 - elseif (isset($this->item['updated'])): // Atom 1.0
2601 - $epoch = @parse_w3cdtf($this->item['updated']);
2602 - elseif ($fallback) : // Fall back to issued / dc:date
2603 - $epoch = $this->published(/*fallback=*/ false);
1773 + function admin_footer () {
1774 + global $feedwordpress_admin_footer;
1775 + foreach ($feedwordpress_admin_footer as $line) :
1776 + echo '<div><pre>'.$line.'</pre></div>';
1777 + endforeach;
1778 + } /* FeedWordPress::admin_footer () */
1779 +
1780 + function log_prefix ($date = false) {
1781 + $home = get_bloginfo('url');
1782 + $prefix = '['.feedwordpress_display_url($home).'] [feedwordpress] ';
1783 + if ($date) :
1784 + $prefix = "[".date('Y-m-d H:i:s')."]".$prefix;
2604 1785 endif;
2605 -
2606 - # If everything failed, then default to the current time.
2607 - if (is_null($epoch)) :
2608 - $epoch = time();
2609 - endif;
2610 -
2611 - return $epoch;
2612 - }
2613 -
2614 - function guid () {
2615 - $guid = null;
2616 - if (isset($this->item['id'])): // Atom 0.3 / 1.0
2617 - $guid = $this->item['id'];
2618 - elseif (isset($this->item['atom']['id'])) : // Namespaced Atom
2619 - $guid = $this->item['atom']['id'];
2620 - elseif (isset($this->item['guid'])) : // RSS 2.0
2621 - $guid = $this->item['guid'];
2622 - elseif (isset($this->item['dc']['identifier'])) :// yeah, right
2623 - $guid = $this->item['dc']['identifier'];
1786 + return $prefix;
1787 + } /* FeedWordPress::log_prefix () */
1788 +
1789 + function menu_cap ($sub = false) {
1790 + if ($sub) :
1791 + $cap = apply_filters('feedwordpress_menu_settings_capacity', 'manage_options');
2624 1792 else :
2625 - // The feed does not seem to have provided us with a
2626 - // unique identifier, so we'll have to cobble together
2627 - // a tag: URI that might work for us. The base of the
2628 - // URI will be the host name of the feed source ...
2629 - $bits = parse_url($this->feedmeta['link/uri']);
2630 - $guid = 'tag:'.$bits['host'];
2631 -
2632 - // If we have a date of creation, then we can use that
2633 - // to uniquely identify the item. (On the other hand, if
2634 - // the feed producer was consicentious enough to
2635 - // generate dates of creation, she probably also was
2636 - // conscientious enough to generate unique identifiers.)
2637 - if (!is_null($this->created())) :
2638 - $guid .= '://post.'.date('YmdHis', $this->created());
2639 -
2640 - // Otherwise, use both the URI of the item, *and* the
2641 - // item's title. We have to use both because titles are
2642 - // often not unique, and sometimes links aren't unique
2643 - // either (e.g. Bitch (S)HITLIST, Mozilla Dot Org news,
2644 - // some podcasts). But it's rare to have *both* the same
2645 - // title *and* the same link for two different items. So
2646 - // this is about the best we can do.
2647 - else :
2648 - $guid .= '://'.md5($this->item['link'].'/'.$this->item['title']);
2649 - endif;
1793 + $cap = apply_filters('feedwordpress_menu_main_capacity', 'manage_links');
2650 1794 endif;
2651 - return $guid;
2652 - }
1795 + return $cap;
1796 + } /* FeedWordPress::menu_cap () */
2653 1797
2654 - function author () {
2655 - $author = array ();
1798 + function path ($filename = '') {
1799 + global $fwp_path;
2656 1800
2657 - if (isset($this->item['author_name'])):
2658 - $author['name'] = $this->item['author_name'];
2659 - elseif (isset($this->item['dc']['creator'])):
2660 - $author['name'] = $this->item['dc']['creator'];
2661 - elseif (isset($this->item['dc']['contributor'])):
2662 - $author['name'] = $this->item['dc']['contributor'];
2663 - elseif (isset($this->feed->channel['dc']['creator'])) :
2664 - $author['name'] = $this->feed->channel['dc']['creator'];
2665 - elseif (isset($this->feed->channel['dc']['contributor'])) :
2666 - $author['name'] = $this->feed->channel['dc']['contributor'];
2667 - elseif (isset($this->feed->channel['author_name'])) :
2668 - $author['name'] = $this->feed->channel['author_name'];
2669 - elseif ($this->feed->is_rss() and isset($this->item['author'])) :
2670 - // The author element in RSS is allegedly an
2671 - // e-mail address, but lots of people don't use
2672 - // it that way. So let's make of it what we can.
2673 - $author = parse_email_with_realname($this->item['author']);
2674 -
2675 - if (!isset($author['name'])) :
2676 - if (isset($author['email'])) :
2677 - $author['name'] = $author['email'];
2678 - else :
2679 - $author['name'] = $this->feed->channel['title'];
2680 - endif;
2681 - endif;
2682 - else :
2683 - $author['name'] = $this->feed->channel['title'];
1801 + $path = $fwp_path;
1802 + if (strlen($filename) > 0) :
1803 + $path .= '/'.$filename;
2684 1804 endif;
2685 -
2686 - if (isset($this->item['author_email'])):
2687 - $author['email'] = $this->item['author_email'];
2688 - elseif (isset($this->feed->channel['author_email'])) :
2689 - $author['email'] = $this->feed->channel['author_email'];
2690 - endif;
2691 -
2692 - if (isset($this->item['author_url'])):
2693 - $author['uri'] = $this->item['author_url'];
2694 - elseif (isset($this->feed->channel['author_url'])) :
2695 - $author['uri'] = $this->item['author_url'];
2696 - else:
2697 - $author['uri'] = $this->feed->channel['link'];
2698 - endif;
2699 -
2700 - return $author;
2701 - } // SyndicatedPost::author()
2702 -} // class SyndicatedPost
2703 -
2704 -# class SyndicatedLink: represents a syndication feed stored within the
2705 -# WordPress database
2706 -#
2707 -# To keep things compact and editable from within WordPress, we use all the
2708 -# links under a particular category in the WordPress "Blogroll" for the list of
2709 -# feeds to syndicate. "Contributors" is the category used by default; you can
2710 -# configure that under Options --> Syndication.
2711 -#
2712 -# Fields used are:
2713 -#
2714 -# * link_rss: the URI of the Atom/RSS feed to syndicate
2715 -#
2716 -# * link_notes: user-configurable options, with keys and values
2717 -# like so:
2718 -#
2719 -# key: value
2720 -# cats: computers\nweb
2721 -# feed/key: value
2722 -#
2723 -# Keys that start with "feed/" are gleaned from the data supplied
2724 -# by the feed itself, and will be overwritten with each update.
2725 -#
2726 -# Values have linebreak characters escaped with C-style
2727 -# backslashes (so, for example, a newline becomes "\n").
2728 -#
2729 -# The value of `cats` is used as a newline-separated list of
2730 -# default categories for any post coming from a particular feed.
2731 -# (In the example above, any posts from this feed will be placed
2732 -# in the "computers" and "web" categories--*in addition to* any
2733 -# categories that may already be applied to the posts.)
2734 -#
2735 -# Values of keys in link_notes are accessible from templates using
2736 -# the function `get_feed_meta($key)` if this plugin is activated.
2737 -
2738 -class SyndicatedLink {
2739 - var $id = null;
2740 - var $link = null;
2741 - var $settings = array ();
2742 - var $magpie = null;
2743 -
2744 - function SyndicatedLink ($link) {
2745 - global $wpdb;
2746 -
2747 - if (is_object($link)) :
2748 - $this->link = $link;
2749 - $this->id = $link->link_id;
2750 - else :
2751 - $this->id = $link;
2752 - if (function_exists('get_bookmark')) : // WP 2.1+
2753 - $this->link = get_bookmark($link);
2754 - else :
2755 - $this->link = $wpdb->get_row("
2756 - SELECT * FROM $wpdb->links
2757 - WHERE (link_id = '".$wpdb->escape($link)."')"
2758 - );
2759 - endif;
2760 - endif;
2761 -
2762 - if (strlen($this->link->link_rss) > 0) :
2763 - // Read off feed settings from link_notes
2764 - $notes = explode("\n", $this->link->link_notes);
2765 - foreach ($notes as $note):
2766 - list($key, $value) = explode(": ", $note, 2);
1805 + return $path;
1806 + }
2767 1807
2768 - if (strlen($key) > 0) :
2769 - // Unescape and trim() off the whitespace.
2770 - // Thanks to Ray Lischner for pointing out the
2771 - // need to trim off whitespace.
2772 - $this->settings[$key] = stripcslashes (trim($value));
1808 + function param ($key, $type = 'REQUEST', $default = NULL) {
1809 + $where = '_'.strtoupper($type);
1810 + $ret = $default;
1811 + if (isset($GLOBALS[$where]) and is_array($GLOBALS[$where])) :
1812 + if (isset($GLOBALS[$where][$key])) :
1813 + $ret = $GLOBALS[$where][$key];
1814 + if (get_magic_quotes_gpc()) :
1815 + $ret = stripslashes_deep($ret);
2773 1816 endif;
2774 - endforeach;
2775 -
2776 - // "Magic" feed settings
2777 - $this->settings['link/uri'] = $this->link->link_rss;
2778 - $this->settings['link/name'] = $this->link->link_name;
2779 - $this->settings['link/id'] = $this->link->link_id;
2780 -
2781 - // `hardcode categories` is deprecated in favor of `unfamiliar categories`
2782 - if (
2783 - FeedWordPress::affirmative($this->settings, 'hardcode categories')
2784 - and !isset($this->settings['unfamiliar categories'])
2785 - ) :
2786 - $this->settings['unfamiliar categories'] = 'default';
2787 1817 endif;
2788 -
2789 - // Set this up automagically for del.icio.us
2790 - if (!isset($this->settings['cat_split']) and false !== strpos($this->link->link_rss, 'del.icio.us')) :
2791 - $this->settings['cat_split'] = '\s'; // Whitespace separates multiple tags in del.icio.us RSS feeds
2792 - endif;
2793 -
2794 - if (isset($this->settings['cats'])):
2795 - $this->settings['cats'] = preg_split(FEEDWORDPRESS_CAT_SEPARATOR_PATTERN, $this->settings['cats']);
2796 - endif;
2797 1818 endif;
2798 - } // SyndicatedLink::SyndicatedLink ()
2799 -
2800 - function found () {
2801 - return is_object($this->link);
1819 + return $ret;
2802 1820 }
1821 + function post ($key, $default = NULL) {
1822 + return FeedWordPress::param($key, 'POST');
1823 + }
1824 +} // class FeedWordPress
2803 1825
2804 - function stale () {
2805 - $stale = true;
2806 - if (isset($this->settings['update/hold']) and ($this->settings['update/hold']=='ping')) :
2807 - $stale = false; // don't update on any timed updates; pings only
2808 - elseif (isset($this->settings['update/hold']) and ($this->settings['update/hold']=='next')) :
2809 - $stale = true; // update on the next timed update
2810 - elseif (!isset($this->settings['update/ttl']) or !isset($this->settings['update/last'])) :
2811 - $stale = true; // initial update
2812 - else :
2813 - $after = ((int) $this->settings['update/last'])
2814 - +((int) $this->settings['update/ttl'] * 60);
2815 - $stale = (time() >= $after);
2816 - endif;
2817 - return $stale;
1826 +class FeedWordPress_File extends WP_SimplePie_File {
1827 + function FeedWordPress_File ($url, $timeout = 10, $redirects = 5, $headers = null, $useragent = null, $force_fsockopen = false) {
1828 + WP_SimplePie_File::WP_SimplePie_File($url, $timeout, $redirects, $headers, $useragent, $force_fsockopen);
1829 +
1830 + // SimplePie makes a strongly typed check against integers with
1831 + // this, but WordPress puts a string in. Which causes caching
1832 + // to break and fall on its ass when SimplePie is getting a 304,
1833 + // but doesn't realize it because this member is "304" instead.
1834 + $this->status_code = (int) $this->status_code;
2818 1835 }
1836 +} /* class FeedWordPress_File () */
2819 1837
2820 - function poll () {
2821 - global $wpdb;
1838 +class FeedWordPress_Parser extends SimplePie_Parser {
1839 + function parse (&$data, $encoding) {
1840 + // Use UTF-8 if we get passed US-ASCII, as every US-ASCII character is a UTF-8 character
1841 + if (strtoupper($encoding) === 'US-ASCII')
1842 + {
1843 + $this->encoding = 'UTF-8';
1844 + }
1845 + else
1846 + {
1847 + $this->encoding = $encoding;
1848 + }
2822 1849
2823 - $this->magpie = fetch_rss($this->link->link_rss);
2824 - $new_count = NULL;
1850 + // Strip BOM:
1851 + // UTF-32 Big Endian BOM
1852 + if (substr($data, 0, 4) === "\x00\x00\xFE\xFF")
1853 + {
1854 + $data = substr($data, 4);
1855 + }
1856 + // UTF-32 Little Endian BOM
1857 + elseif (substr($data, 0, 4) === "\xFF\xFE\x00\x00")
1858 + {
1859 + $data = substr($data, 4);
1860 + }
1861 + // UTF-16 Big Endian BOM
1862 + elseif (substr($data, 0, 2) === "\xFE\xFF")
1863 + {
1864 + $data = substr($data, 2);
1865 + }
1866 + // UTF-16 Little Endian BOM
1867 + elseif (substr($data, 0, 2) === "\xFF\xFE")
1868 + {
1869 + $data = substr($data, 2);
1870 + }
1871 + // UTF-8 BOM
1872 + elseif (substr($data, 0, 3) === "\xEF\xBB\xBF")
1873 + {
1874 + $data = substr($data, 3);
1875 + }
2825 1876
2826 - if (is_object($this->magpie)) :
2827 - $new_count = array('new' => 0, 'updated' => 0);
1877 + if (substr($data, 0, 5) === '<?xml' && strspn(substr($data, 5, 1), "\x09\x0A\x0D\x20") && ($pos = strpos($data, '?>')) !== false)
1878 + {
1879 + $declaration =& new SimplePie_XML_Declaration_Parser(substr($data, 5, $pos - 5));
1880 + if ($declaration->parse())
1881 + {
1882 + $data = substr($data, $pos + 2);
1883 + $data = '<?xml version="' . $declaration->version . '" encoding="' . $encoding . '" standalone="' . (($declaration->standalone) ? 'yes' : 'no') . '"?>' . $data;
1884 + }
1885 + else
1886 + {
1887 + $this->error_string = 'SimplePie bug! Please report this!';
1888 + return false;
1889 + }
1890 + }
2828 1891
2829 - # -- Update Link metadata live from feed
2830 - $channel = $this->magpie->channel;
1892 + $return = true;
2831 1893
2832 - if (!isset($channel['id'])) :
2833 - $channel['id'] = $this->link->link_rss;
2834 - endif;
2835 -
2836 - $update = array();
2837 - if (!$this->hardcode('url') and isset($channel['link'])) :
2838 - $update[] = "link_url = '".$wpdb->escape($channel['link'])."'";
2839 - endif;
2840 -
2841 - if (!$this->hardcode('name') and isset($channel['title'])) :
2842 - $update[] = "link_name = '".$wpdb->escape($channel['title'])."'";
2843 - endif;
2844 -
2845 - if (!$this->hardcode('description')) :
2846 - if (isset($channel['tagline'])) :
2847 - $update[] = "link_description = '".$wpdb->escape($channel['tagline'])."'";
2848 - elseif (isset($channel['description'])) :
2849 - $update[] = "link_description = '".$wpdb->escape($channel['description'])."'";
2850 - endif;
2851 - endif;
2852 -
2853 - $this->settings = array_merge($this->settings, $this->flatten_array($channel));
1894 + static $xml_is_sane = null;
1895 + if ($xml_is_sane === null)
1896 + {
1897 + $parser_check = xml_parser_create();
1898 + xml_parse_into_struct($parser_check, '<foo>&amp;</foo>', $values);
1899 + xml_parser_free($parser_check);
1900 + $xml_is_sane = isset($values[0]['value']);
1901 + }
2854 1902
2855 - $this->settings['update/last'] = time(); $ttl = $this->ttl();
2856 - if (!is_null($ttl)) :
2857 - $this->settings['update/ttl'] = $ttl;
2858 - $this->settings['update/timed'] = 'feed';
2859 - else :
2860 - $this->settings['update/ttl'] = rand(30, 120); // spread over time interval for staggered updates
2861 - $this->settings['update/timed'] = 'automatically';
2862 - endif;
2863 -
2864 - if (!isset($this->settings['update/hold']) or $this->settings['update/hold']!='ping') :
2865 - $this->settings['update/hold'] = 'scheduled';
2866 - endif;
2867 -
2868 - // Copy back without a few things that we don't want to save in the notes
2869 - $to_notes = $this->settings;
1903 + // Create the parser
1904 + if ($xml_is_sane)
1905 + {
1906 + $xml = xml_parser_create_ns($this->encoding, $this->separator);
1907 + xml_parser_set_option($xml, XML_OPTION_SKIP_WHITE, 1);
1908 + xml_parser_set_option($xml, XML_OPTION_CASE_FOLDING, 0);
1909 + xml_set_object($xml, $this);
1910 + xml_set_character_data_handler($xml, 'cdata');
1911 + xml_set_element_handler($xml, 'tag_open', 'tag_close');
1912 + xml_set_start_namespace_decl_handler($xml, 'start_xmlns');
2870 1913
2871 - if (is_array($to_notes['cats'])) :
2872 - $to_notes['cats'] = implode(FEEDWORDPRESS_CAT_SEPARATOR, $to_notes['cats']);
2873 - endif;
2874 - unset($to_notes['link/id']); unset($to_notes['link/uri']);
2875 - unset($to_notes['link/name']);
2876 - unset($to_notes['hardcode categories']); // Deprecated
2877 -
2878 - $notes = '';
2879 - foreach ($to_notes as $key => $value) :
2880 - $notes .= $key . ": ". addcslashes($value, "\0..\37".'\\') . "\n";
2881 - endforeach;
2882 - $update[] = "link_notes = '".$wpdb->escape($notes)."'";
2883 -
2884 - $update_set = implode(',', $update);
2885 -
2886 - // Update the properties of the link from the feed information
2887 - $result = $wpdb->query("
2888 - UPDATE $wpdb->links
2889 - SET $update_set
2890 - WHERE link_id='$this->id'
2891 - ");
1914 + // Parse!
1915 + if (!xml_parse($xml, $data, true))
1916 + {
1917 + $this->error_code = xml_get_error_code($xml);
1918 + $this->error_string = xml_error_string($this->error_code);
1919 + $return = false;
1920 + }
2892 1921
2893 - # -- Add new posts from feed and update any updated posts
2894 - if (is_array($this->magpie->items)) :
2895 - foreach ($this->magpie->items as $item) :
2896 - $post =& new SyndicatedPost($item, $this);
2897 - if (!$post->filtered()) :
2898 - $new = $post->store();
2899 - if ( $new !== false ) $new_count[$new]++;
2900 - endif;
2901 - endforeach;
2902 - endif;
2903 - endif;
2904 - return $new_count;
2905 - } /* SyndicatedLink::poll() */
2906 -
2907 - function uri () {
2908 - return (is_object($this->link) ? $this->link->link_rss : NULL);
2909 - }
2910 - function homepage () {
2911 - return (isset($this->settings['feed/link']) ? $this->settings['feed/link'] : NULL);
2912 - }
1922 + $this->current_line = xml_get_current_line_number($xml);
1923 + $this->current_column = xml_get_current_column_number($xml);
1924 + $this->current_byte = xml_get_current_byte_index($xml);
1925 + xml_parser_free($xml);
2913 1926
2914 - function ttl () {
2915 - if (is_object($this->magpie)) :
2916 - $channel = $this->magpie->channel;
2917 - else :
2918 - $channel = array();
2919 - endif;
1927 + return $return;
1928 + }
1929 + else
1930 + {
1931 + libxml_clear_errors();
1932 + $xml =& new XMLReader();
1933 + $xml->xml($data);
1934 + while (@$xml->read())
1935 + {
1936 + switch ($xml->nodeType)
1937 + {
2920 1938
2921 - if (isset($channel['ttl'])) :
2922 - // "ttl stands for time to live. It's a number of
2923 - // minutes that indicates how long a channel can be
2924 - // cached before refreshing from the source."
2925 - // <http://blogs.law.harvard.edu/tech/rss#ltttlgtSubelementOfLtchannelgt>
2926 - $ret = $channel['ttl'];
2927 - elseif (isset($channel['sy']['updatefrequency']) or isset($channel['sy']['updateperiod'])) :
2928 - $period_minutes = array (
2929 - 'hourly' => 60, /* minutes in an hour */
2930 - 'daily' => 1440, /* minutes in a day */
2931 - 'weekly' => 10080, /* minutes in a week */
2932 - 'monthly' => 43200, /* minutes in a month */
2933 - 'yearly' => 525600, /* minutes in a year */
2934 - );
1939 + case constant('XMLReader::END_ELEMENT'):
1940 + if ($xml->namespaceURI !== '')
1941 + {
1942 + $tagName = "{$xml->namespaceURI}{$this->separator}{$xml->localName}";
1943 + }
1944 + else
1945 + {
1946 + $tagName = $xml->localName;
1947 + }
1948 + $this->tag_close(null, $tagName);
1949 + break;
1950 + case constant('XMLReader::ELEMENT'):
1951 + $empty = $xml->isEmptyElement;
1952 + if ($xml->namespaceURI !== '')
1953 + {
1954 + $tagName = "{$xml->namespaceURI}{$this->separator}{$xml->localName}";
1955 + }
1956 + else
1957 + {
1958 + $tagName = $xml->localName;
1959 + }
1960 + $attributes = array();
1961 + while ($xml->moveToNextAttribute())
1962 + {
1963 + if ($xml->namespaceURI !== '')
1964 + {
1965 + $attrName = "{$xml->namespaceURI}{$this->separator}{$xml->localName}";
1966 + }
1967 + else
1968 + {
1969 + $attrName = $xml->localName;
1970 + }
1971 + $attributes[$attrName] = $xml->value;
1972 + }
1973 +
1974 + foreach ($attributes as $attr => $value) :
1975 + list($ns, $local) = $this->split_ns($attr);
1976 + if ($ns=='http://www.w3.org/2000/xmlns/') :
1977 + if ('xmlns' == $local) : $local = false; endif;
1978 + $this->start_xmlns(null, $local, $value);
1979 + endif;
1980 + endforeach;
1981 +
1982 + $this->tag_open(null, $tagName, $attributes);
1983 + if ($empty)
1984 + {
1985 + $this->tag_close(null, $tagName);
1986 + }
1987 + break;
1988 + case constant('XMLReader::TEXT'):
2935 1989
2936 - // "sy:updatePeriod: Describes the period over which the
2937 - // channel format is updated. Acceptable values are:
2938 - // hourly, daily, weekly, monthly, yearly. If omitted,
2939 - // daily is assumed." <http://web.resource.org/rss/1.0/modules/syndication/>
2940 - if (isset($channel['sy']['updateperiod'])) : $period = $channel['sy']['updateperiod'];
2941 - else : $period = 'daily';
2942 - endif;
2943 -
2944 - // "sy:updateFrequency: Used to describe the frequency
2945 - // of updates in relation to the update period. A
2946 - // positive integer indicates how many times in that
2947 - // period the channel is updated. ... If omitted a value
2948 - // of 1 is assumed." <http://web.resource.org/rss/1.0/modules/syndication/>
2949 - if (isset($channel['sy']['updatefrequency'])) : $freq = (int) $channel['sy']['updatefrequency'];
2950 - else : $freq = 1;
2951 - endif;
2952 -
2953 - $ret = (int) ($period_minutes[$period] / $freq);
2954 - else :
2955 - $ret = NULL;
1990 + case constant('XMLReader::CDATA'):
1991 + $this->cdata(null, $xml->value);
1992 + break;
1993 + }
1994 + }
1995 + if ($error = libxml_get_last_error())
1996 + {
1997 + $this->error_code = $error->code;
1998 + $this->error_string = $error->message;
1999 + $this->current_line = $error->line;
2000 + $this->current_column = $error->column;
2001 + return false;
2002 + }
2003 + else
2004 + {
2005 + return true;
2006 + }
2007 + }
2008 + } /* FeedWordPress_Parser::parse() */
2009 +
2010 + var $xmlns_stack = array();
2011 + var $xmlns_current = array();
2012 + function tag_open ($parser, $tag, $attributes) {
2013 + $ret = parent::tag_open($parser, $tag, $attributes);
2014 + if ($this->current_xhtml_construct < 0) :
2015 + $this->data['xmlns'] = $this->xmlns_current;
2016 + $this->xmlns_stack[] = $this->xmlns_current;
2956 2017 endif;
2957 2018 return $ret;
2958 - } /* SyndicatedLink::ttl() */
2959 -
2960 - // SyndicatedLink:flatten_array (): flatten an array. Useful for
2961 - // hierarchical and namespaced elements.
2962 - //
2963 - // Given an array which may contain array or object elements in it,
2964 - // return a "flattened" array: a one-dimensional array of scalars
2965 - // containing each of the scalar elements contained within the array
2966 - // structure. Thus, for example, if $a['b']['c']['d'] == 'e', then the
2967 - // returned array for FeedWordPress::flatten_array($a) will contain a key
2968 - // $a['feed/b/c/d'] with value 'e'.
2969 - function flatten_array ($arr, $prefix = 'feed/', $separator = '/') {
2970 - $ret = array ();
2971 - if (is_array($arr)) :
2972 - foreach ($arr as $key => $value) :
2973 - if (is_scalar($value)) :
2974 - $ret[$prefix.$key] = $value;
2975 - else :
2976 - $ret = array_merge($ret, $this->flatten_array($value, $prefix.$key.$separator, $separator));
2977 - endif;
2978 - endforeach;
2019 + }
2020 +
2021 + function tag_close($parser, $tag) {
2022 + if ($this->current_xhtml_construct < 0) :
2023 + $this->xmlns_current = array_pop($this->xmlns_stack);
2979 2024 endif;
2025 + $ret = parent::tag_close($parser, $tag);
2980 2026 return $ret;
2981 - } // function SyndicatedLink::flatten_array ()
2982 -
2983 - function hardcode ($what) {
2984 - $default = get_option("feedwordpress_hardcode_$what");
2985 - if ( $default === 'yes' ) :
2986 - // If the default is to hardcode, then we want the
2987 - // negation of negative(): TRUE by default and FALSE if
2988 - // the setting is explicitly "no"
2989 - $ret = !FeedWordPress::negative($this->settings, "hardcode $what");
2990 - else :
2991 - // If the default is NOT to hardcode, then we want
2992 - // affirmative(): FALSE by default and TRUE if the
2993 - // setting is explicitly "yes"
2994 - $ret = FeedWordPress::affirmative($this->settings, "hardcode $what");
2027 + }
2028 +
2029 + function start_xmlns ($parser, $prefix, $uri) {
2030 + if (!$prefix) :
2031 + $prefix = '';
2995 2032 endif;
2996 - return $ret;
2997 - } // function SyndicatedLink::hardcode ()
2998 -
2999 - function syndicated_status ($what, $default) {
3000 - global $wpdb;
3001 -
3002 - $ret = get_option("feedwordpress_syndicated_{$what}_status");
3003 - if ( isset($this->settings["$what status"]) ) :
3004 - $ret = $this->settings["$what status"];
3005 - elseif (!$ret) :
3006 - $ret = $default;
2033 + if ($this->current_xhtml_construct < 0) :
2034 + $this->xmlns_current[$prefix] = $uri;
3007 2035 endif;
3008 - return $wpdb->escape(trim(strtolower($ret)));
3009 - } // function SyndicatedLink:syndicated_status ()
3010 -} // class SyndicatedLink
3011 -
3012 -################################################################################
3013 -## XML-RPC HOOKS: accept XML-RPC update pings from Contributors ################
3014 -################################################################################
3015 -
3016 -function feedwordpress_xmlrpc_hook ($args = array ()) {
3017 - $args['weblogUpdates.ping'] = 'feedwordpress_pong';
3018 - return $args;
2036 + return true;
2037 + } /* FeedWordPress_Parser::start_xmlns() */
3019 2038 }
3020 2039
3021 -function feedwordpress_pong ($args) {
3022 - $feedwordpress =& new FeedWordPress;
3023 - $delta = @$feedwordpress->update($args[1]);
3024 - if (is_null($delta)):
3025 - return array('flerror' => true, 'message' => "Sorry. I don't syndicate <$args[1]>.");
3026 - else:
3027 - $mesg = array();
3028 - if (isset($delta['new'])) { $mesg[] = ' '.$delta['new'].' new posts were syndicated'; }
3029 - if (isset($delta['updated'])) { $mesg[] = ' '.$delta['updated'].' existing posts were updated'; }
2040 +$feedwordpress_admin_footer = array();
3030 2041
3031 - return array('flerror' => false, 'message' => "Thanks for the ping.".implode(' and', $mesg));
3032 - endif;
3033 -}
3034 -
3035 2042 ################################################################################
3036 -## class FeedFinder: find likely feeds using autodetection and/or guesswork ####
2043 +## XML-RPC HOOKS: accept XML-RPC update pings from Contributors ################
3037 2044 ################################################################################
3038 2045
3039 -class FeedFinder {
3040 - var $uri = NULL;
3041 - var $_cache_uri = NULL;
2046 +class FeedWordPressRPC {
2047 + function FeedWordPressRPC () {
2048 + add_filter('xmlrpc_methods', array(&$this, 'xmlrpc_methods'));
2049 + }
3042 2050
3043 - var $verify = FALSE;
2051 + function xmlrpc_methods ($args = array()) {
2052 + $args['weblogUpdates.ping'] = array(&$this, 'ping');
2053 + $args['feedwordpress.subscribe'] = array(&$this, 'subscribe');
2054 + $args['feedwordpress.deactivate'] = array(&$this, 'deactivate');
2055 + $args['feedwordpress.delete'] = array(&$this, 'delete');
2056 + $args['feedwordpress.nuke'] = array(&$this, 'nuke');
2057 + return $args;
2058 + }
3044 2059
3045 - var $_data = NULL;
3046 - var $_head = NULL;
3047 -
3048 - # -- Recognition patterns
3049 - var $_feed_types = array(
3050 - 'application/rss+xml',
3051 - 'text/xml',
3052 - 'application/atom+xml',
3053 - 'application/x.atom+xml',
3054 - 'application/x-atom+xml'
3055 - );
3056 - var $_feed_markers = array('\\<feed', '\\<rss', 'xmlns="http://purl.org/rss/1.0');
3057 - var $_html_markers = array('\\<html');
3058 - var $_obvious_feed_url = array('[./]rss', '[./]rdf', '[./]atom', '[./]feed', '\.xml');
3059 - var $_maybe_feed_url = array ('rss', 'rdf', 'atom', 'feed', 'xml');
3060 -
3061 - function FeedFinder ($uri = NULL, $verify = TRUE) {
3062 - $this->uri = $uri; $this->verify = $verify;
3063 - } /* FeedFinder::FeedFinder () */
3064 -
3065 - function find ($uri = NULL) {
3066 - $ret = array ();
3067 - if (!is_null($this->data($uri))) {
3068 - if ($this->is_feed($uri)) {
3069 - $ret = array($this->uri);
3070 - } else {
3071 - // Assume that we have HTML or XHTML (even if we don't, who's it gonna hurt?)
3072 - // Autodiscovery is the preferred method
3073 - $href = $this->_link_rel_feeds();
3074 -
3075 - // ... but we'll also take the little orange buttons
3076 - $href = array_merge($href, $this->_a_href_feeds(TRUE));
3077 -
3078 - // If all that failed, look harder
3079 - if (count($href) == 0) $href = $this->_a_href_feeds(FALSE);
3080 -
3081 - // Verify feeds and resolve relative URIs
3082 - foreach ($href as $u) {
3083 - $the_uri = Relative_URI::resolve($u, $this->uri);
3084 - if ($this->verify) {
3085 - $feed =& new FeedFinder($the_uri);
3086 - if ($feed->is_feed()) $ret[] = $the_uri;
3087 - $feed = NULL;
3088 - } else {
3089 - $ret[] = $the_uri;
3090 - }
3091 - } /* foreach */
3092 - } /* if */
3093 - } /* if */
3094 - return array_unique($ret);
3095 - } /* FeedFinder::find () */
3096 -
3097 - function data ($uri = NULL) {
3098 - $this->_get($uri);
3099 - return $this->_data;
3100 - }
3101 -
3102 - function is_feed ($uri = NULL) {
3103 - $data = $this->data($uri);
3104 -
3105 - return (
3106 - preg_match (
3107 - "\007(".implode('|',$this->_feed_markers).")\007i",
3108 - $data
3109 - ) and !preg_match (
3110 - "\007(".implode('|',$this->_html_markers).")\007i",
3111 - $data
3112 - )
3113 - );
3114 - } /* FeedFinder::is_feed () */
3115 -
3116 - # --- Private methods ---
3117 - function _get ($uri = NULL) {
3118 - if ($uri) $this->uri = $uri;
3119 -
3120 - // Is the result not yet cached?
3121 - if ($this->_cache_uri !== $this->uri) :
3122 - // Snoopy is an HTTP client in PHP
3123 - $client = new Snoopy();
3124 -
3125 - // Prepare headers and internal settings
3126 - $client->rawheaders['Connection'] = 'close';
3127 - $client->accept = 'application/atom+xml application/rdf+xml application/rss+xml application/xml text/html */*';
3128 - $client->agent = 'feedfinder/1.2 (compatible; PHP FeedFinder) +http://projects.radgeek.com/feedwordpress';
3129 - $client->read_timeout = 25;
3130 -
3131 - // Fetch the HTML or feed
3132 - @$client->fetch($this->uri);
3133 - $this->_data = $client->results;
3134 -
3135 - // Kilroy was here
3136 - $this->_cache_uri = $this->uri;
2060 + function ping ($args) {
2061 + global $feedwordpress;
2062 +
2063 + $delta = @$feedwordpress->update($args[1]);
2064 + if (is_null($delta)):
2065 + return array('flerror' => true, 'message' => "Sorry. I don't syndicate <$args[1]>.");
2066 + else:
2067 + $mesg = array();
2068 + if (isset($delta['new'])) { $mesg[] = ' '.$delta['new'].' new posts were syndicated'; }
2069 + if (isset($delta['updated'])) { $mesg[] = ' '.$delta['updated'].' existing posts were updated'; }
2070 +
2071 + return array('flerror' => false, 'message' => "Thanks for the ping.".implode(' and', $mesg));
3137 2072 endif;
3138 - } /* FeedFinder::_get () */
3139 -
3140 - function _link_rel_feeds () {
3141 - $links = $this->_tags('link');
3142 - $link_count = count($links);
3143 -
3144 - // now figure out which one points to the RSS file
3145 - $href = array ();
3146 - for ($n=0; $n<$link_count; $n++) {
3147 - if (strtolower($links[$n]['rel']) == 'alternate') {
3148 - if (in_array(strtolower($links[$n]['type']), $this->_feed_types)) {
3149 - $href[] = $links[$n]['href'];
3150 - } /* if */
3151 - } /* if */
3152 - } /* for */
3153 - return $href;
3154 2073 }
2074 +
2075 + function validate (&$args) {
2076 + global $wp_xmlrpc_server;
3155 2077
3156 - function _a_href_feeds ($obvious = TRUE) {
3157 - $pattern = ($obvious ? $this->_obvious_feed_url : $this->_maybe_feed_url);
2078 + // First two params are username/password
2079 + $username = $wp_xmlrpc_server->escape(array_shift($args));
2080 + $password = $wp_xmlrpc_server->escape(array_shift($args));
3158 2081
3159 - $links = $this->_tags('a');
3160 - $link_count = count($links);
3161 -
3162 - // now figure out which one points to the RSS file
3163 - $href = array ();
3164 - for ($n=0; $n<$link_count; $n++) {
3165 - if (preg_match("\007(".implode('|',$pattern).")\007i", $links[$n]['href'])) {
3166 - $href[] = $links[$n]['href'];
3167 - } /* if */
3168 - } /* for */
3169 - return $href;
3170 - }
3171 -
3172 - function _tags ($tag) {
3173 - $html = $this->data();
3174 -
3175 - // search through the HTML, save all <link> tags
3176 - // and store each link's attributes in an associative array
3177 - preg_match_all('/<'.$tag.'\s+(.*?)\s*\/?>/si', $html, $matches);
3178 - $links = $matches[1];
3179 2082 $ret = array();
3180 - $link_count = count($links);
3181 - for ($n=0; $n<$link_count; $n++) {
3182 - $attributes = preg_split('/\s+/s', $links[$n]);
3183 - foreach($attributes as $attribute) {
3184 - $att = preg_split('/\s*=\s*/s', $attribute, 2);
3185 - if (isset($att[1])) {
3186 - $att[1] = preg_replace('/([\'"]?)(.*)\1/', '$2', $att[1]);
3187 - $final_link[strtolower($att[0])] = $att[1];
3188 - } /* if */
3189 - } /* foreach */
3190 - $ret[$n] = $final_link;
3191 - } /* for */
2083 + if ( !$user = $wp_xmlrpc_server->login($username, $password) ) :
2084 + $ret = $wp_xmlrpc_server->error;
2085 + elseif (!current_user_can('manage_links')) :
2086 + $ret = new IXR_Error(401, 'Sorry, you cannot change the subscription list.');
2087 + endif;
3192 2088 return $ret;
3193 2089 }
3194 -} /* class FeedFinder */
3195 2090
3196 -# Relative URI static class: PHP class for resolving relative URLs
3197 -#
3198 -# This class is derived (under the terms of the GPL) from URL Class 0.3 by
3199 -# Keyvan Minoukadeh <keyvan@k1m.com>, which is great but more than we need
3200 -# for FeedWordPress's purposes. The class has been stripped down to a single
3201 -# public method: Relative_URI::resolve($url, $base), which resolves the URI in
3202 -# $url relative to the URI in $base
3203 -#
3204 -# The upgraded MagpieRSS also uses this class. So if we have it loaded
3205 -# in, don't load it again.
3206 -if (!class_exists('Relative_URI')) {
3207 -
3208 - class Relative_URI
3209 - {
3210 - // Resolve relative URI in $url against the base URI in $base. If $base
3211 - // is not supplied, then we use the REQUEST_URI of this script.
3212 - //
3213 - // I'm hoping this method reflects RFC 2396 Section 5.2
3214 - function resolve ($url, $base = NULL)
3215 - {
3216 - if (is_null($base)):
3217 - $base = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
3218 - endif;
2091 + function subscribe ($args) {
2092 + $ret = $this->validate($args);
2093 + if (is_array($ret)) : // Success
2094 + // The remaining params are feed URLs
2095 + foreach ($args as $arg) :
2096 + $finder = new FeedFinder($arg, /*verify=*/ false, /*fallbacks=*/ 1);
2097 + $feeds = array_values(array_unique($finder->find()));
2098 +
2099 + if (count($feeds) > 0) :
2100 + $link_id = FeedWordPress::syndicate_link(
2101 + /*title=*/ feedwordpress_display_url($feeds[0]),
2102 + /*homepage=*/ $feeds[0],
2103 + /*feed=*/ $feeds[0]
2104 + );
2105 + $ret[] = array(
2106 + 'added',
2107 + $feeds[0],
2108 + $arg,
2109 + );
2110 + else :
2111 + $ret[] = array(
2112 + 'error',
2113 + $arg
2114 + );
2115 + endif;
2116 + endforeach;
2117 + endif;
2118 + return $ret;
2119 + } /* FeedWordPressRPC::subscribe () */
3219 2120
3220 - $base = Relative_URI::_encode(trim($base));
3221 - $uri_parts = Relative_URI::_parse_url($base);
3222 -
3223 - $url = Relative_URI::_encode(trim($url));
3224 - $parts = Relative_URI::_parse_url($url);
3225 -
3226 - $uri_parts['fragment'] = (isset($parts['fragment']) ? $parts['fragment'] : null);
3227 - $uri_parts['query'] = (isset($parts['query']) ? $parts['query'] : null);
3228 -
3229 - // if path is empty, and scheme, host, and query are undefined,
3230 - // the URL is referring the base URL
3231 -
3232 - if (($parts['path'] == '') && !isset($parts['scheme']) && !isset($parts['host']) && !isset($parts['query'])) {
3233 - // If the URI is empty or only a fragment, return the base URI
3234 - return $base . (isset($parts['fragment']) ? '#'.$parts['fragment'] : '');
3235 - } elseif (isset($parts['scheme'])) {
3236 - // If the scheme is set, then the URI is absolute.
3237 - return $url;
3238 - } elseif (isset($parts['host'])) {
3239 - $uri_parts['host'] = $parts['host'];
3240 - $uri_parts['path'] = $parts['path'];
3241 - } else {
3242 - // We have a relative path but not a host.
3243 -
3244 - // start ugly fix:
3245 - // prepend slash to path if base host is set, base path is not set, and url path is not absolute
3246 - if ($uri_parts['host'] && ($uri_parts['path'] == '')
3247 - && (strlen($parts['path']) > 0)
3248 - && (substr($parts['path'], 0, 1) != '/')) {
3249 - $parts['path'] = '/'.$parts['path'];
3250 - } // end ugly fix
2121 + function unsubscribe ($method, $args) {
2122 + $ret = $this->validate($args);
2123 + if (is_array($ret)) : // Success
2124 + // The remaining params are feed URLs
2125 + foreach ($args as $arg) :
2126 + $link_id = FeedWordPress::find_link($arg);
3251 2127
3252 - if (substr($parts['path'], 0, 1) == '/') {
3253 - $uri_parts['path'] = $parts['path'];
3254 - } else {
3255 - // copy base path excluding any characters after the last (right-most) slash character
3256 - $buffer = substr($uri_parts['path'], 0, (int)strrpos($uri_parts['path'], '/')+1);
3257 - // append relative path
3258 - $buffer .= $parts['path'];
3259 - // remove "./" where "." is a complete path segment.
3260 - $buffer = str_replace('/./', '/', $buffer);
3261 - if (substr($buffer, 0, 2) == './') {
3262 - $buffer = substr($buffer, 2);
3263 - }
3264 - // if buffer ends with "." as a complete path segment, remove it
3265 - if (substr($buffer, -2) == '/.') {
3266 - $buffer = substr($buffer, 0, -1);
3267 - }
3268 - // remove "<segment>/../" where <segment> is a complete path segment not equal to ".."
3269 - $search_finished = false;
3270 - $segment = explode('/', $buffer);
3271 - while (!$search_finished) {
3272 - for ($x=0; $x+1 < count($segment);) {
3273 - if (($segment[$x] != '') && ($segment[$x] != '..') && ($segment[$x+1] == '..')) {
3274 - if ($x+2 == count($segment)) $segment[] = '';
3275 - unset($segment[$x], $segment[$x+1]);
3276 - $segment = array_values($segment);
3277 - continue 2;
3278 - } else {
3279 - $x++;
3280 - }
3281 - }
3282 - $search_finished = true;
3283 - }
3284 - $buffer = (count($segment) == 1) ? '/' : implode('/', $segment);
3285 - $uri_parts['path'] = $buffer;
2128 + if (!$link_id) :
2129 + $link_id = FeedWordPress::find_link($arg, 'link_url');
2130 + endif;
2131 +
2132 + if ($link_id) :
2133 + $link = new SyndicatedLink($link_id);
2134 +
2135 + $link->{$method}();
2136 + $ret[] = array(
2137 + 'deactivated',
2138 + $arg,
2139 + );
2140 + else :
2141 + $ret[] = array(
2142 + 'error',
2143 + $arg,
2144 + );
2145 + endif;
2146 + endforeach;
2147 + endif;
2148 + return $ret;
2149 + } /* FeedWordPress::unsubscribe () */
3286 2150
3287 - }
3288 - }
2151 + function deactivate ($args) {
2152 + return $this->unsubscribe('deactivate', $args);
2153 + } /* FeedWordPressRPC::deactivate () */
3289 2154
3290 - // If we've gotten to this point, we can try to put the pieces
3291 - // back together.
3292 - $ret = '';
3293 - if (isset($uri_parts['scheme'])) $ret .= $uri_parts['scheme'].':';
3294 - if (isset($uri_parts['user'])) {
3295 - $ret .= $uri_parts['user'];
3296 - if (isset($uri_parts['pass'])) $ret .= ':'.$uri_parts['parts'];
3297 - $ret .= '@';
3298 - }
3299 - if (isset($uri_parts['host'])) {
3300 - $ret .= '//'.$uri_parts['host'];
3301 - if (isset($uri_parts['port'])) $ret .= ':'.$uri_parts['port'];
3302 - }
3303 - $ret .= $uri_parts['path'];
3304 - if (isset($uri_parts['query'])) $ret .= '?'.$uri_parts['query'];
3305 - if (isset($uri_parts['fragment'])) $ret .= '#'.$uri_parts['fragment'];
2155 + function delete ($args) {
2156 + return $this->unsubscribe('delete', $args);
2157 + } /* FeedWordPressRPC::delete () */
3306 2158
3307 - return $ret;
3308 - }
3309 -
3310 - /**
3311 - * Parse URL
3312 - *
3313 - * Regular expression grabbed from RFC 2396 Appendix B.
3314 - * This is a replacement for PHPs builtin parse_url().
3315 - * @param string $url
3316 - * @access private
3317 - * @return array
3318 - */
3319 - function _parse_url($url)
3320 - {
3321 - // I'm using this pattern instead of parse_url() as there's a few strings where parse_url()
3322 - // generates a warning.
3323 - if (preg_match('!^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?!', $url, $match)) {
3324 - $parts = array();
3325 - if ($match[1] != '') $parts['scheme'] = $match[2];
3326 - if ($match[3] != '') $parts['auth'] = $match[4];
3327 - // parse auth
3328 - if (isset($parts['auth'])) {
3329 - // store user info
3330 - if (($at_pos = strpos($parts['auth'], '@')) !== false) {
3331 - $userinfo = explode(':', substr($parts['auth'], 0, $at_pos), 2);
3332 - $parts['user'] = $userinfo[0];
3333 - if (isset($userinfo[1])) $parts['pass'] = $userinfo[1];
3334 - $parts['auth'] = substr($parts['auth'], $at_pos+1);
3335 - }
3336 - // get port number
3337 - if ($port_pos = strrpos($parts['auth'], ':')) {
3338 - $parts['host'] = substr($parts['auth'], 0, $port_pos);
3339 - $parts['port'] = (int)substr($parts['auth'], $port_pos+1);
3340 - if ($parts['port'] < 1) $parts['port'] = null;
3341 - } else {
3342 - $parts['host'] = $parts['auth'];
3343 - }
3344 - }
3345 - unset($parts['auth']);
3346 - $parts['path'] = $match[5];
3347 - if (isset($match[6]) && ($match[6] != '')) $parts['query'] = $match[7];
3348 - if (isset($match[8]) && ($match[8] != '')) $parts['fragment'] = $match[9];
3349 - return $parts;
3350 - }
3351 - // shouldn't reach here
3352 - return array('path'=>'');
3353 - }
3354 -
3355 - function _encode($string)
3356 - {
3357 - static $replace = array();
3358 - if (!count($replace)) {
3359 - $find = array(32, 34, 60, 62, 123, 124, 125, 91, 92, 93, 94, 96, 127);
3360 - $find = array_merge(range(0, 31), $find);
3361 - $find = array_map('chr', $find);
3362 - foreach ($find as $char) {
3363 - $replace[$char] = '%'.bin2hex($char);
3364 - }
3365 - }
3366 - // escape control characters and a few other characters
3367 - $encoded = strtr($string, $replace);
3368 - // remove any character outside the hex range: 21 - 7E (see www.asciitable.com)
3369 - return preg_replace('/[^\x21-\x7e]/', '', $encoded);
3370 - }
3371 - } // class Relative_URI
3372 -}
2159 + function nuke ($args) {
2160 + return $this->unsubscribe('nuke', $args);
2161 + } /* FeedWordPressRPC::nuke () */
2162 +} /* class FeedWordPressRPC */
3373 2163
3374 2164 // take your best guess at the realname and e-mail, given a string
3375 2165 define('FWP_REGEX_EMAIL_ADDY', '([^@"(<\s]+@[^"@(<\s]+\.[^"@(<\s]+)');
3376 2166 define('FWP_REGEX_EMAIL_NAME', '("([^"]*)"|([^"<(]+\S))');
@@ -3395,5 +2185,5 @@
3395 2185 $ret['name'] = NULL; $ret['email'] = NULL;
3396 2186 endif;
3397 2187 return $ret;
3398 2188 }
3399 -?>
2189 +