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