diff --git a/wp-content/plugins/feedwordpress/admin-ui.php b/wp-content/plugins/feedwordpress/admin-ui.php index d9eee616d283643e4ec5e95f9f7e0c611ae01f50..8256a9450edad44fab9001379747f73bad771cee 100644 --- a/wp-content/plugins/feedwordpress/admin-ui.php +++ b/wp-content/plugins/feedwordpress/admin-ui.php @@ -50,7 +50,7 @@ class FeedWordPressAdminPage { elseif ($this->save_requested_in($post)) : // User mashed Save Changes $this->save_settings($post); endif; - do_action($this->dispatch.'_post', &$post, &$this); + do_action($this->dispatch.'_post', $post, $this); } function update_feed () { @@ -81,7 +81,7 @@ class FeedWordPressAdminPage { } function save_settings ($post) { - do_action($this->dispatch.'_save', &$post, &$this); + do_action($this->dispatch.'_save', $post, $this); if ($this->for_feed_settings()) : // Save settings @@ -163,12 +163,12 @@ class FeedWordPressAdminPage { ); foreach ($submit_buttons as $field) : if (isset($fwp_post[$field])) : - $link_id = $_REQUEST['save_link_id']; + $link_id = MyPHP::request('save_link_id'); endif; endforeach; if (is_null($link_id) and isset($_REQUEST['link_id'])) : - $link_id = $_REQUEST['link_id']; + $link_id = MyPHP::request('link_id'); endif; return $link_id; @@ -187,7 +187,7 @@ class FeedWordPressAdminPage { function stamp_link_id ($field = null) { if (is_null($field)) : $field = 'save_link_id'; endif; ?> - <input type="hidden" name="<?php print esc_html($field); ?>" value="<?php print ($this->for_feed_settings() ? $this->link->id : '*'); ?>" /> + <input type="hidden" name="<?php print esc_attr($field); ?>" value="<?php print ($this->for_feed_settings() ? $this->link->id : '*'); ?>" /> <?php } /* FeedWordPressAdminPage::stamp_link_id () */ @@ -257,7 +257,7 @@ class FeedWordPressAdminPage { "Feed" => array('page' => 'feeds-page.php', 'long' => 'Feeds & Updates'), "Posts" => array('page' => 'posts-page.php', 'long' => 'Posts & Links'), "Authors" => array('page' => 'authors-page.php', 'long' => 'Authors'), - 'Categories' => array('page' => 'categories-page.php', 'long' => 'Categories'.FEEDWORDPRESS_AND_TAGS), + 'Categories' => array('page' => 'categories-page.php', 'long' => 'Categories & Tags'), ); $hrefPrefix = 'admin.php?'; @@ -444,7 +444,7 @@ class FeedWordPressAdminPage { add_meta_box( /*id=*/ $id, /*title=*/ $title, - /*callback=*/ array(&$this, $method), + /*callback=*/ array($this, $method), /*page=*/ $this->meta_box_context(), /*context=*/ $this->meta_box_context() ); @@ -732,17 +732,10 @@ function fwp_option_box_opener ($legend, $id, $class = "stuffbox") { } function fwp_option_box_closer () { - global $wp_db_version; - if (isset($wp_db_version) and $wp_db_version >= FWP_SCHEMA_25) : ?> </div> <!-- class="inside" --> </div> <!-- class="stuffbox" --> <?php - else : -?> - </div> <!-- class="wrap" --> -<?php - endif; } function fwp_tags_box ($tags, $object, $params = array()) { @@ -853,11 +846,6 @@ function fwp_category_box ($checked, $object, $tags = array(), $params = array() <?php $newcat = 'new'.$taxonomy; - // Well, thank God they added "egory" before WP 3.0 came out. - if ('newcategory'==$newcat - and !FeedWordPressCompatibility::test_version(FWP_SCHEMA_30)) : - $newcat = 'newcat'; - endif; ?> <label class="screen-reader-text" for="<?php print $idPrefix; ?>new<?php print $taxonomy; ?>"><?php _e('Add New Category'); ?></label> <input @@ -880,7 +868,7 @@ function fwp_category_box ($checked, $object, $tags = array(), $params = array() 'show_option_none' => __('Parent category'), 'tab_index' => 3, ) ); ?> - <input type="button" id="<?php print $idPrefix; ?><?php print $taxonomy; ?>-add-sumbit" class="add:<?php print $idPrefix; ?><?php print $taxonomy; ?>checklist:<?php print $taxonomy; ?>-add add-categorychecklist-category-add button category-add-submit" value="<?php _e( 'Add' ); ?>" tabindex="3" /> + <input type="button" id="<?php print $idPrefix; ?><?php print $taxonomy; ?>-add-sumbit" class="add:<?php print $idPrefix; ?><?php print $taxonomy; ?>checklist:<?php print $idPrefix.$taxonomy; ?>-add add-categorychecklist-category-add button category-add-submit" value="<?php _e( 'Add' ); ?>" tabindex="3" /> <?php /* wp_nonce_field currently doesn't let us set an id different from name, but we need a non-unique name and a unique id */ ?> <input type="hidden" id="_ajax_nonce<?php print esc_html($idSuffix); ?>" name="_ajax_nonce" value="<?php print wp_create_nonce('add-'.$taxonomy); ?>" /> <input type="hidden" id="_ajax_nonce-add-<?php print $taxonomy; ?><?php print esc_html($idSuffix); ?>" name="_ajax_nonce-add-<?php print $taxonomy; ?>" value="<?php print wp_create_nonce('add-'.$taxonomy); ?>" /> @@ -945,9 +933,6 @@ class FeedWordPressSettingsUI { global $fwp_path; wp_enqueue_script('post'); // for magic tag and category boxes - if (!FeedWordPressCompatibility::test_version(FWP_SCHEMA_29)) : // < 2.9 - wp_enqueue_script('thickbox'); // for fold-up boxes - endif; wp_enqueue_script('admin-forms'); // for checkbox selection wp_register_script('feedwordpress-elements', WP_PLUGIN_URL.'/'.$fwp_path.'/feedwordpress-elements.js'); @@ -1118,8 +1103,17 @@ function fwp_syndication_manage_page_links_table_rows ($links, $page, $visible = endif; // Prep: get last error timestamp, if any + $fileSizeLines = array(); if (is_null($sLink->setting('update/error'))) : $errorsSince = ''; + if (!is_null($sLink->setting('link/item count'))) : + $N = $sLink->setting('link/item count'); + $fileSizeLines[] = sprintf((($N==1) ? __('%d item') : __('%d items')), $N); + endif; + + if (!is_null($sLink->setting('link/filesize'))) : + $fileSizeLines[] = size_format($sLink->setting('link/filesize')). ' total'; + endif; else : $trClass[] = 'feed-error'; @@ -1173,6 +1167,11 @@ function fwp_syndication_manage_page_links_table_rows ($links, $page, $visible = endif; $nextUpdate .= "</div></div>"; + $fileSize = ''; + if (count($fileSizeLines) > 0) : + $fileSize = '<div>'.implode(" / ", $fileSizeLines)."</div>"; + endif; + unset($sLink); $alt_row = !$alt_row; @@ -1221,6 +1220,7 @@ function fwp_syndication_manage_page_links_table_rows ($links, $page, $visible = <input type="submit" class="button" name="update_uri[<?php print esc_html($link->link_rss); ?>]" value="<?php _e('Update Now'); ?>" /> </div> <?php print $lastUpdated; ?> + <?php print $fileSize; ?> <?php print $errorsSince; ?> <?php print $nextUpdate; ?> </td> diff --git a/wp-content/plugins/feedwordpress/categories-page.php b/wp-content/plugins/feedwordpress/categories-page.php index 823713ccb9c29d35b774aff21d65eaa3cc88ee33..528a81ef8d306ce77fcd1e992091e6674afc4203 100644 --- a/wp-content/plugins/feedwordpress/categories-page.php +++ b/wp-content/plugins/feedwordpress/categories-page.php @@ -10,9 +10,9 @@ class FeedWordPressCategoriesPage extends FeedWordPressAdminPage { FeedWordPressAdminPage::FeedWordPressAdminPage('feedwordpresscategories', $link); $this->dispatch = 'feedwordpress_admin_page_categories'; $this->pagenames = array( - 'default' => 'Categories'.FEEDWORDPRESS_AND_TAGS, - 'settings-update' => 'Syndicated Categories'.FEEDWORDPRESS_AND_TAGS, - 'open-sheet' => 'Categories'.FEEDWORDPRESS_AND_TAGS, + 'default' => 'Categories & Tags', + 'settings-update' => 'Syndicated Categories & Tags', + 'open-sheet' => 'Categories & Tags', ); $this->filename = __FILE__; } @@ -599,12 +599,9 @@ blank.</p></td> //////////////////////////////////////////////// $this->boxes_by_methods = array( - 'feed_categories_box' => __('Feed Categories'.FEEDWORDPRESS_AND_TAGS), + 'feed_categories_box' => __('Feed Categories & Tags'), 'categories_box' => array('title' => __('Categories'), 'id' => 'categorydiv'), ); - if (!FeedWordPressCompatibility::post_tags()) : - unset($this->boxes_by_methods['tags_box']); - endif; parent::display(); } diff --git a/wp-content/plugins/feedwordpress/compatability.php b/wp-content/plugins/feedwordpress/compatability.php index 0099488a14581ed2c9a88317473912c94ccd1b90..6b62752f2e104146073898ab5f52fbae469bb642 100644 --- a/wp-content/plugins/feedwordpress/compatability.php +++ b/wp-content/plugins/feedwordpress/compatability.php @@ -69,10 +69,6 @@ class FeedWordPressCompatibility { return $cat_id; } /* FeedWordPressCompatibility::link_category_id () */ - /*static*/ function post_tags () { - return FeedWordPressCompatibility::test_version(FWP_SCHEMA_23); - } /* FeedWordPressCompatibility::post_tags () */ - /*static*/ function validate_http_request ($action = -1, $capability = null) { // Only worry about this if we're using a method with significant side-effects if (strtoupper($_SERVER['REQUEST_METHOD']) == 'POST') : @@ -102,47 +98,11 @@ class FeedWordPressCompatibility { /*static*/ function bottom_script_hook ($filename) { global $fwp_path; - $hook = 'admin_footer'; - if (FeedWordPressCompatibility::test_version(FWP_SCHEMA_28)) : // WordPress 2.8+ - $hook = $hook . '-' . $fwp_path . '/' . basename($filename); - endif; + $hook = 'admin_footer-'.$fwp_path.'/'.basename($filename); return $hook; } /* FeedWordPressCompatibility::bottom_script_hook() */ } /* class FeedWordPressCompatibility */ -define('FEEDWORDPRESS_AND_TAGS', (FeedWordPressCompatibility::post_tags() ? ' & Tags' : '')); - -if (!function_exists('stripslashes_deep')) { - function stripslashes_deep($value) { - $value = is_array($value) ? array_map('stripslashes_deep', $value) : stripslashes($value); - return $value; - } -} - -if (!function_exists('sanitize_user')) { - function sanitize_user ($text, $strict = false) { - return $text; // Don't munge it if it wasn't munged going in... - } -} - -if (!function_exists('disabled')) { - /** - * Outputs the html disabled attribute. - * - * Compares the first two arguments and if identical marks as disabled - * - * @since 3.0.0 - * - * @param mixed $disabled One of the values to compare - * @param mixed $current (true) The other value to compare if not just true - * @param bool $echo Whether to echo or just return the string - * @return string html attribute or empty string - */ - function disabled( $disabled, $current = true, $echo = true ) { - return __checked_selected_helper( $disabled, $current, $echo, 'disabled' ); - } -} /* if */ - // Compat if (!function_exists('set_post_field')) { @@ -175,13 +135,6 @@ if (!function_exists('set_post_field')) { } /* if */ -if (!function_exists('term_exists')) { - // Fucking WordPress 3.0 wordsmithing. - function term_exists ( $term, $taxonomy = '', $parent = 0 ) { - return is_term($term, $taxonomy, $parent); - } -} /* if */ - require_once(dirname(__FILE__).'/feedwordpress-walker-category-checklist.class.php'); function fwp_category_checklist ($post_id = 0, $descendents_and_self = 0, $selected_cats = false, $params = array()) { diff --git a/wp-content/plugins/feedwordpress/diagnostics-page.php b/wp-content/plugins/feedwordpress/diagnostics-page.php index d062b00f3290822cf9eedf988c20ef246ddc1574..ec74b2ec39f2fede1cf9c653111c54047ce5e3eb 100644 --- a/wp-content/plugins/feedwordpress/diagnostics-page.php +++ b/wp-content/plugins/feedwordpress/diagnostics-page.php @@ -7,6 +7,9 @@ class FeedWordPressDiagnosticsPage extends FeedWordPressAdminPage { FeedWordPressAdminPage::FeedWordPressAdminPage('feedwordpressdiagnosticspage'); $this->dispatch = 'feedwordpress_diagnostics'; $this->filename = __FILE__; + + $this->test_html = array(); + add_action('feedwordpress_diagnostics_do_http_test', array($this, 'do_http_test'), 10, 1); } function has_link () { return false; } @@ -42,6 +45,7 @@ class FeedWordPressDiagnosticsPage extends FeedWordPressAdminPage { 'info_box' => __('Diagnostic Information'), 'diagnostics_box' => __('Display Diagnostics'), 'updates_box' => __('Updates'), + 'tests_box' => __('Diagnostic Tests'), ); foreach ($boxes_by_methods as $method => $title) : @@ -68,7 +72,8 @@ class FeedWordPressDiagnosticsPage extends FeedWordPressAdminPage { function accept_POST ($post) { if (isset($post['submit']) - or isset($post['save'])) : + or isset($post['save']) + or isset($post['feedwordpress_diagnostics_do'])) : update_option('feedwordpress_debug', $post['feedwordpress_debug']); update_option('feedwordpress_secret_key', $post['feedwordpress_secret_key']); @@ -102,12 +107,19 @@ class FeedWordPressDiagnosticsPage extends FeedWordPressAdminPage { delete_option('feedwordpress_diagnostics_email_destination'); endif; + if (isset($post['feedwordpress_diagnostics_do'])) : + foreach ($post['feedwordpress_diagnostics_do'] as $do => $value) : + do_action('feedwordpress_diagnostics_do_'.$do, $post); + endforeach; + endif; + $this->updated = true; // Default update message endif; } /* FeedWordPressDiagnosticsPage::accept_POST () */ function info_box ($page, $box = NULL) { global $feedwordpress; + global $wp_version; $link_category_id = FeedWordPress::link_category_id(); ?> <table class="edit-form narrow"> @@ -122,6 +134,20 @@ class FeedWordPressDiagnosticsPage extends FeedWordPressAdminPage { <td>You are using FeedWordPress version <strong><?php print FEEDWORDPRESS_VERSION; ?></strong>.</td> </tr> + <tr> + <th scope="row">Hosting Environment:</th> + <td><ul style="margin-top: 0; padding-top: 0;"> + <li><em>WordPress:</em> version <?php print $wp_version; ?></li> + <?php if (function_exists('phpversion')) : ?> + <li><em>PHP:</em> version <?php print phpversion(); ?></li> + <?php endif; ?> + <?php if (function_exists('apache_get_version')) : ?> + <li><sem>Web Server:</em> <?php print apache_get_version(); ?></li> + <?php endif; ?> + </ul> + </td> + </tr> + <tr> <th scope="row">Link Category:</th> <td><?php if (!is_wp_error($link_category_id)) : @@ -242,6 +268,7 @@ testing but absolutely inappropriate for a production server.</p> 'updated_feeds' => 'as each feed is checked for updates', 'updated_feeds:errors:persistent' => 'when attempts to update a feed have resulted in errors</label> <label>for at least <input type="number" min="1" max="360" step="1" name="diagnostics_persistent_error_hours" value="'.$hours.'" /> hours', 'updated_feeds:errors' => 'any time FeedWordPress encounters any errors while checking a feed for updates', + 'updated_feeds:http' => "displaying the raw HTTP data passed to and from the feed being checked for updates", 'syndicated_posts' => 'as each syndicated post is added to the database', 'feed_items' => 'as each syndicated item is considered on the feed', 'memory_usage' => 'indicating how much memory was used', @@ -252,6 +279,7 @@ testing but absolutely inappropriate for a production server.</p> 'syndicated_posts:meta_data' => 'as syndication meta-data is added on the post', ), 'Advanced Diagnostics' => array( + 'feed_items:freshness:reasons' => 'explaining the reason that a post was treated as an update to an existing post', 'feed_items:freshness:sql' => 'when FeedWordPress issues the SQL query it uses to decide whether to treat items as new, updates, or duplicates', 'syndicated_posts:static_meta_data' => 'providing meta-data about syndicated posts in the Edit Posts interface', ), @@ -289,6 +317,101 @@ testing but absolutely inappropriate for a production server.</p> </table> <?php } /* FeedWordPressDiagnosticsPage::updates_box () */ + + function tests_box ($page, $box = NULL) { +?> +<script type="text/javascript"> +function clone_http_test_args_keyvalue_prototype () { + var next = jQuery('#http-test-args').find('.http-test-args-keyvalue').length; + var newRow = jQuery('#http-test-args-keyvalue-prototype').clone().attr('id', 'http-test-args-keyvalue-' + next); + newRow.find('.http_test_args_key').attr('name', 'http_test_args_key['+next+']').val(''); + newRow.find('.http_test_args_value').attr('name', 'http_test_args_value['+next+']').val(''); + + newRow.appendTo('#http-test-args'); + return false; +} +</script> + +<table class="edit-form"> + <tr> + <th scope="row">HTTP:</th> + <td><div><input type="url" name="http_test_url" value="" placeholder="http://www.example.com/" size="127" style="width: 80%; max-width: 80.0em;" /> + <input type="submit" name="feedwordpress_diagnostics_do[http_test]" value="Test »" /></div> + <div><select name="http_test_method" size="1"> + <option value="wp_remote_request">wp_remote_request</option> + <option value="FeedWordPress_File">FeedWordPress_File</option> + </select></div> + <table> + <tr> + <td> + <div id="http-test-args"> + <div id="http-test-args-keyvalue-prototype" class="http-test-args-keyvalue"><label>Args: + <input type="text" class='http_test_args_key' name="http_test_args_key[0]" value="" placeholder="key" /></label> + <label>= <input type="text" class='http_test_args_value' name="http_test_args_value[0]" value="" placeholder="value" /></label> + </div> + </div> + </td> + <td><a href="#http-test-args" onclick="return clone_http_test_args_keyvalue_prototype();">+ Add</a></td> + </tr> + </table> + + <?php if (isset($page->test_html['http_test'])) : ?> + <div style="position: relative"> + <div style="width: 100%; overflow: scroll; background-color: #eed"> + <pre style="white-space: pre-wrap; white-space: -moz-pre-wrap; white-space: -o-pre-wrap;"><?php print $page->test_html['http_test']; ?></pre> + </div> + </div> + <?php endif; ?> + </td> + </tr> +</table> + +<?php + } /* FeedWordPressDiagnosticsPage::tests_box () */ + + var $test_html; + function do_http_test ($post) { + if (isset($post['http_test_url']) and isset($post['http_test_method'])) : + $url = $post['http_test_url']; + + $args = array(); + if (isset($post['http_test_args_key'])) : + foreach ($post['http_test_args_key'] as $idx => $name) : + $name = trim($name); + if (strlen($name) > 0) : + $value = NULL; + if (isset($post['http_test_args_value']) + and isset($post['http_test_args_value'][$idx])) : + $value = $post['http_test_args_value'][$idx]; + endif; + + if (preg_match('/^javascript:(.*)$/i', $value, $refs)) : + if (function_exists('json_decode')) : + $json_value = json_decode($refs[1]); + if (!is_null($json_value)) : + $value = $json_value; + endif; + endif; + endif; + + $args[$name] = $value; + endif; + endforeach; + endif; + + switch ($post['http_test_method']) : + case 'wp_remote_request' : + $out = wp_remote_request($url, $args); + break; + case 'FeedWordPress_File' : + $out = new FeedWordPress_File($url); + break; + endswitch; + + $this->test_html['http_test'] = esc_html(FeedWordPress::val($out)); + endif; + } /* FeedWordPressDiagnosticsPage::do_http_test () */ + } /* class FeedWordPressDiagnosticsPage */ $diagnosticsPage = new FeedWordPressDiagnosticsPage; diff --git a/wp-content/plugins/feedwordpress/feeds-page.php b/wp-content/plugins/feedwordpress/feeds-page.php index ac62d18f88d41c3d7fd9cfb3f0df1ca843d55deb..491c93d4e0f9c3778180d6df9f17885d30a72b3a 100644 --- a/wp-content/plugins/feedwordpress/feeds-page.php +++ b/wp-content/plugins/feedwordpress/feeds-page.php @@ -111,7 +111,7 @@ class FeedWordPressFeedsPage extends FeedWordPressAdminPage { 'global_feeds_box' => __('Update Scheduling'), 'updated_posts_box' => __('Updated Posts'), 'custom_settings_box' => __('Custom Feed Settings (for use in templates)'), - 'fetch_settings_box' => __('Settings for Fetching Feeds (Advanced)'), + 'advanced_settings_box' => __('Advanced Settings'), ); if ($this->for_default_settings()) : unset($this->boxes_by_methods['custom_settings_box']); @@ -265,13 +265,13 @@ class FeedWordPressFeedsPage extends FeedWordPressAdminPage { $this->setting_radio_control( 'update/window', 'update_window', - array(&$this, 'update_window_edit_box'), + array($this, 'update_window_edit_box'), array( 'global-setting-default' => DEFAULT_UPDATE_PERIOD, 'default-input-name' => 'use_default_update_window', 'default-input-id' => 'use-default-update-window-yes', 'default-input-id-no' => 'use-default-update-window-no', - 'labels' => array(&$this, 'update_window_currently'), + 'labels' => array($this, 'update_window_currently'), ) ); ?></td> @@ -363,22 +363,63 @@ class FeedWordPressFeedsPage extends FeedWordPressAdminPage { print number_format(intval($setting)) . " " . (($setting==1) ? "second" : "seconds"); } - function fetch_settings_box ($page, $box = NULL) { + function advanced_settings_box ($page, $box = NULL) { + ?> + <table class="edit-form"> + <tr> + <th>Fetch Timeout:</th> + <td> + <?php $this->setting_radio_control( 'fetch timeout', 'fetch_timeout', - array(&$this, 'fetch_timeout_setting'), + array($this, 'fetch_timeout_setting'), array( 'global-setting-default' => FEEDWORDPRESS_FETCH_TIMEOUT_DEFAULT, 'input-name' => 'fetch_timeout', 'default-input-name' => 'fetch_timeout_default', - 'labels' => array(&$this, 'fetch_timeout_setting_value'), + 'labels' => array($this, 'fetch_timeout_setting_value'), ) ); - } /* FeedWordPressFeedsPage::fetch_settings_box () */ + ?> + </td> + </tr> + <tr> + <th>Feed Update Type:</th> + <td><?php + $this->setting_radio_control('update_incremental', 'update_incremental', + /*options=*/ array( + 'incremental' => '<strong>Incremental.</strong> When items no longer appear on the feed, keep them in the WordPress posts table.', + 'complete' => '<strong>Complete.</strong> When items no longer appear on the feed, they are obsolete; retire them from the WordPress posts table.', + ), + /*params=*/ array( + 'setting-default' => NULL, + 'global-setting-default' => 'incremental', + 'default-input-value' => 'default', + ) + ); ?></td> + </tr> + <tr> + <th>Allow Feeds to Delete Posts:</th> + <td><?php + $this->setting_radio_control('tombstones', 'tombstones', + /*options=*/ array( + 'yes' => 'Yes. If a feed indicates that one of its posts has been deleted, delete the local copy syndicated to this website.', + 'no' => 'No. Even if a feed indicates that one of its posts has been deleted, retain the local copy on this website.', + ), + /*params=*/ array( + 'setting-default' => NULL, + 'global-setting-default' => 'yes', + 'default-input-value' => 'default', + ) + ); ?></td> + </tr> + </table> + <?php + } /* FeedWordPressFeedsPage::advanced_settings_box () */ function display_authentication_credentials_box ($params = array()) { static $count = 0; - + $params = wp_parse_args($params, array( 'username' => NULL, 'password' => NULL, @@ -502,7 +543,7 @@ class FeedWordPressFeedsPage extends FeedWordPressAdminPage { global $feedwordpress; - if (is_null($auth)) : + if (is_null($auth) or (strlen($auth)==0)) : $auth = '-'; $hideAuth = true; endif; @@ -734,9 +775,9 @@ class FeedWordPressFeedsPage extends FeedWordPressAdminPage { $lookup = (isset($_REQUEST['lookup']) ? $_REQUEST['lookup'] : NULL); - $auth = FeedWordPress::param('link_rss_auth_method'); - $username = FeedWordPress::param('link_rss_username'); - $password = FeedWordPress::param('link_rss_password'); + $auth = MyPHP::request('link_rss_auth_method'); + $username = MyPHP::request('link_rss_username'); + $password = MyPHP::request('link_rss_password'); $credentials = array( "authentication" => $auth, "username" => $username, @@ -789,7 +830,21 @@ class FeedWordPressFeedsPage extends FeedWordPressAdminPage { $feeds = array_values( // Renumber from 0..(N-1) $feeds ); - + + // Allow for some simple FeedFinder results filtering + $feeds = apply_filters( + 'feedwordpress_feedfinder_results', + $feeds, + array( + "url" => $lookup, + "auth" => $auth, + "username" => $username, + "password" => $password, + "finder" => $finder, + ), + $this + ); + if (count($feeds) > 0): if ($feedSwitch) : ?> @@ -815,7 +870,7 @@ class FeedWordPressFeedsPage extends FeedWordPressAdminPage { foreach ($feeds as $key => $f): $ofc = $fwp_credentials; $fwp_credentials = $credentials; // Set - $pie = FeedWordPress::fetch($f); + $pie = FeedWordPress::fetch($f, array("cache" => false)); $fwp_credentials = $ofc; // Re-Set $rss = (is_wp_error($pie) ? $pie : new MagpieFromSimplePie($pie)); @@ -1161,6 +1216,14 @@ class FeedWordPressFeedsPage extends FeedWordPressAdminPage { endif; $this->update_setting('fetch timeout', $timeout); endif; + + if (isset($post['update_incremental'])) : + $this->update_setting('update_incremental', $post['update_incremental']); + endif; + + if (isset($post['tombstones'])) : + $this->update_setting('tombstones', $post['tombstones']); + endif; if (isset($post['update_minimum'])) : $this->update_setting('update/minimum', $post['update_minimum']); diff --git a/wp-content/plugins/feedwordpress/feedtime.class.php b/wp-content/plugins/feedwordpress/feedtime.class.php index 5cfc3a26a081a69fcde0a68d279242f697cfe64b..067c584300ec8339b4695e1f7ec8578a098c5b46 100644 --- a/wp-content/plugins/feedwordpress/feedtime.class.php +++ b/wp-content/plugins/feedwordpress/feedtime.class.php @@ -1,7 +1,24 @@ <?php /** * class FeedTime: handle common date-time formats used in feeds. + * We will try to be as tolerant as possible of different representations, since + * RSS Is A Fucking Mess, broken dates seem to be especially pervasive, etc. + * Supports anything that your version of PHP's strtotime() can handle; also has + * a built-in parser for W3C DTF, in case your PHP doesn't have that. * + * Tries to parse a W3C DTF first; then falls back to strtotime(). If strtotime + * fails due to a mystery timezone, then we will try again on local time, with + * the timezone stripped out. + * + * To parse a string representation of a date-time from a feed, instantiate and + * then pull the timestamp: + * + * $time = new FeedTime($s); + * if (!$time->failed()) : + * $ts = $time->timestamp(); + * else : + * // ... + * endif; */ class FeedTime { var $rep; @@ -21,10 +38,10 @@ class FeedTime { $this->ts = $this->parse_w3cdtf(); if ($this->failed()) : - // In some versions of PHP, strtotime() does not support - // the UT timezone. Since UT is by definition within 1 - // second of UTC, we'll just convert it preemptively to avoid - // problems. + // In some versions of PHP, strtotime() does not + // support the UT timezone. But since UT is by + // definition within 1 second of UTC, we'll just + // convert it preemptively to avoid problems. $time = preg_replace( '/(\s)UT$/', '$1UTC', diff --git a/wp-content/plugins/feedwordpress/feedwordpie.class.php b/wp-content/plugins/feedwordpress/feedwordpie.class.php new file mode 100644 index 0000000000000000000000000000000000000000..49bf11195d081bfc21f1865a3be7c7ef5848a472 --- /dev/null +++ b/wp-content/plugins/feedwordpress/feedwordpie.class.php @@ -0,0 +1,73 @@ +<?php +define('FEEDWORDPIE_TYPE_CUSTOM_XML', ~SIMPLEPIE_TYPE_NONE & ~SIMPLEPIE_TYPE_ALL); + +class FeedWordPie extends SimplePie { + var $subscription = NULL; + + function set_feed_url ($url) { + global $fwp_oLinks; + if ($url InstanceOf SyndicatedLink) : + + // Get URL with relevant parameters attached. + // Credentials will be handled further down. + $new_url = $url->uri(array('add_params' => true)); + + // Store for reference. + $this->subscription = $url->id(); + + // Pass it along down the line. + $url = $new_url; + + else : + $this->subscription = NULL; + endif; + + // OK, let's go. + return parent::set_feed_url($url); + } /* class SimplePie */ + + function get_type () { + // Allow filters to pre-empt a type determination from SimplePie + $ret = apply_filters( + 'feedwordpie_get_type', + NULL, + $this + ); + + // If not preempted by a filter, fall back on SimplePie + if (is_null($ret)) : + $ret = parent::get_type(); + endif; + + return $ret; + } + + function get_feed_tags ($namespace, $tag) { + // Allow filters to filter SimplePie handling + return apply_filters( + 'feedwordpie_get_feed_tags', + parent::get_feed_tags($namespace, $tag), + $namespace, + $tag, + $this + ); + } + + function get_items ($start = 0, $end = 0) { + // Allow filters to set up for, or pre-empt, SimplePie handling + $ret = apply_filters( + 'feedwordpie_get_items', + NULL, + $start, + $end, + $this + ); + + if (is_null($ret)) : + $ret = parent::get_items(); + endif; + return $ret; + } + +} /* class FeedWordPie */ + diff --git a/wp-content/plugins/feedwordpress/feedwordpie_item.class.php b/wp-content/plugins/feedwordpress/feedwordpie_item.class.php new file mode 100644 index 0000000000000000000000000000000000000000..8e3697cce12e2bafbb439273c039a302e83edd05 --- /dev/null +++ b/wp-content/plugins/feedwordpress/feedwordpie_item.class.php @@ -0,0 +1,55 @@ +<?php +class FeedWordPie_Item extends SimplePie_Item { + + function get_id ($hash = false) { + return apply_filters('feedwordpie_item_get_id', parent::get_id($hash), $hash, $this); + } + + function get_title () { + return apply_filters('feedwordpie_item_get_title', parent::get_title(), $this); + } + + function get_description ($description_only = false) { + return apply_filters('feedwordpie_item_get_description', parent::get_description($description_only), $description_only, $this); + } + + function get_content ($content_only = false) { + return apply_filters('feedwordpie_item_get_content', parent::get_content($content_only), $content_only, $this); + } + + function get_categories () { + return apply_filters('feedwordpie_item_get_categories', parent::get_categories(), $this); + } + + function get_authors () { + return apply_filters('feedwordpie_item_get_authors', parent::get_authors(), $this); + } + function get_contributors () { + return apply_filters('feedwordpie_item_get_contributors', parent::get_contributors(), $this); + } + function get_copyright () { + return apply_filters('feedwordpie_item_get_copyright', parent::get_copyright(), $this); + } + function get_date ($date_format = 'j F Y, g:i a') { + return apply_filters('feedwordpie_item_get_date', parent::get_date($date_format), $date_format, $this); + } + function get_local_date ($date_format = '%c') { + return apply_filters('feedwordpie_item_get_local_date', parent::get_local_date($date_format), $date_format, $this); + } + function get_links ($rel = 'alternate') { + return apply_filters('feedwordpie_item_get_links', parent::get_links($rel), $rel, $this); + } + function get_enclosures () { + return apply_filters('feedwordpie_item_get_enclosures', parent::get_enclosures(), $this); + } + function get_latitude () { + return apply_filters('feedwordpie_item_get_lattitude', parent::get_lattitude(), $this); + } + function get_longitude () { + return apply_filters('feedwordpie_item_get_longitude', parent::get_longtidue(), $this); + } + function get_source () { + return apply_filters('feedwordpie_item_get_source', parent::get_source(), $this); + } +} /* class FeedWordPie_Item */ + diff --git a/wp-content/plugins/feedwordpress/feedwordpress-content-type-sniffer.class.php b/wp-content/plugins/feedwordpress/feedwordpress-content-type-sniffer.class.php index 02cb19e854821d7843e948c3bd0637930049b096..15bab37423c7df2d9dc6071fb26acab4a54c80f8 100644 --- a/wp-content/plugins/feedwordpress/feedwordpress-content-type-sniffer.class.php +++ b/wp-content/plugins/feedwordpress/feedwordpress-content-type-sniffer.class.php @@ -17,7 +17,7 @@ class FeedWordPress_Content_Type_Sniffer extends SimplePie_Content_Type_Sniffer endif; foreach ($this->file->headers['content-type'] as $type) : - $parts = array_map('trim', split(";", $type, 2)); + $parts = array_map('trim', explode(";", $type, 2)); if (isset($parts[1])) : $type = $parts[0]; $charset = $parts[1]; diff --git a/wp-content/plugins/feedwordpress/feedwordpress-elements.css b/wp-content/plugins/feedwordpress/feedwordpress-elements.css index 9e92b099cf0230f3fd952d02dfcc07ca82e27f5a..11fcf2903d0e8a4e1a9941a3ae32abe37dfcfea5 100644 --- a/wp-content/plugins/feedwordpress/feedwordpress-elements.css +++ b/wp-content/plugins/feedwordpress/feedwordpress-elements.css @@ -13,8 +13,7 @@ width: 120px; } .feedwordpress-category-div ul.category-tabs li.tabs { - -moz-border-radius: 3px 0 0 3px; - -webkit-border-radius: 3px 0 0 3px; + display: block; border-radius: 3px 0 0 3px; border-color: #DFDFDF; border-style: solid none solid solid; @@ -430,3 +429,43 @@ table.twofer td.secondary { padding-left: 10px; width: 30%; } #feedwordpress-admin-feeds #link-rss-params td { width: auto !important; } +/* Feed Contents picker & XPath test results widgets */ + +.fwp-xpath-test-results { + position: absolute; + top: auto; left: auto; + z-index: 9999999; + overflow: auto; + max-height: 100px; max-width: 400px; + background-color: #ccffcc; + padding: 10px; +} +.fwp-xpath-test-results-close { + position: absolute; + top: 10px; right: 10px; + +} +.fwp-xpath-test-setting { + font-size: smaller; +} + +.fwp-feed-contents-picker h4, .fwp-xpath-test-results h4 { + margin-top: 0; + margin-bottom: 5px; + border-bottom: 1px dotted black; +} +.fwp-feed-contents-picker-close { + position: absolute; + top: 10px; right: 10px; +} +.fwp-feed-contents-picker, .fwp-feeds-picker { + position: absolute; + top: auto; left: auto; + z-index: 9999999; + overflow: auto; + max-height: 100px; max-width: 400px; + background-color: #ccffcc; + padding: 10px; +} + + diff --git a/wp-content/plugins/feedwordpress/feedwordpress-elements.js b/wp-content/plugins/feedwordpress/feedwordpress-elements.js index fa793373b25a1e888d1b6b9202c35778cd2b97e5..c3d799a767db6a9a631cb5613dbf27bab84015e0 100644 --- a/wp-content/plugins/feedwordpress/feedwordpress-elements.js +++ b/wp-content/plugins/feedwordpress/feedwordpress-elements.js @@ -249,7 +249,7 @@ fwpList = { el = el.get(0); var elem = el, color, rgbaTrans = new RegExp( "rgba\\(\\s*0,\\s*0,\\s*0,\\s*0\\s*\\)", "i" ); do { - color = jQuery.curCSS(elem, 'backgroundColor'); + color = jQuery(elem).css('backgroundColor'); if ( color != '' && color != 'transparent' && !color.match(rgbaTrans) || jQuery.nodeName(elem, "body") ) break; } while ( elem = elem.parentNode ); @@ -299,7 +299,6 @@ fwpList = { color = fwpList.getColor( e ); e.css( 'backgroundColor', s.addColor ).animate( { backgroundColor: color }, { complete: function() { $(this).css( 'backgroundColor', '' ); } } ); } - list.each( function() { this.fwpList.process( e ); } ); return e; }, @@ -394,14 +393,6 @@ function feedAuthenticationMethod (params) { var elMethod = elTable.find('.link-rss-auth-method'); var elLink = elDiv.find('.link-rss-userpass-use'); - console.log('--- ---'); - console.log(s.node); - console.log(elDiv); - console.log(elTable); - console.log(elMethod); - console.log(elLink); - console.log(elMethod.val()); - // Set. if (s.value != null) { elMethod.val(s.value); @@ -495,7 +486,252 @@ jQuery(document).ready( function($) { } ); /* $('.feedwordpress-category-div').each() */ } ); /* jQuery(document).ready() */ + +function fwp_feedspiper () { + var data = { + action: 'fwp_feeds' + }; + + return jQuery.ajax({ + type: "POST", + url: ajaxurl, + data: data + }); +} + +function fwp_feedcontentspiper (feed_id, callbackOK, callbackFail) { + var data = { + action: 'fwp_feedcontents', + feed_id: feed_id + }; + + jQuery.ajax({ + type: "POST", + url: ajaxurl, + data: data + }) + .done(function (response) { callbackOK(response); }) + .fail(function (response) { callbackFail(response); }); +} + +function fwp_feedcontentspicker (feed_id, destination, pickCallback, closeCallback) { + var picker_id = 'fwp-feed-contents-picker-' + feed_id; + + jQuery('<div class="fwp-feed-contents-picker" style="display: none;" id="'+picker_id+'"><p>Loading...</p></div>').insertAfter(destination); + jQuery('#'+picker_id).show(500); + + fwp_feedcontentspiper(feed_id, function (response) { + var ul = '<h4>Using post...</h4><ul>'; + for (var i=0; i < response.length; i++) { + ul += '<li><a class="fwp-feed-contents-picker-pick-'+feed_id+'" href="' + +response[i].guid + +'">' + response[i].post_title + '</a></li>'; + } + ul += '</ul>'; + ul += '<a class="fwp-feed-contents-picker-close" href="#" id="fwp-feed-contents-picker-' + feed_id + '-close">x</a>'; + + jQuery('#fwp-feed-contents-picker-' + feed_id).html(ul); + + // Set up event handlers. + jQuery('#fwp-feed-contents-picker-' + feed_id + '-close').click(function (e) { + jQuery('#fwp-feed-contents-picker-' + feed_id).hide(500, function () { jQuery('#fwp-feed-contents-picker-' + feed_id).remove(); }); + if (typeof(closeCallback)=='function') { + closeCallback(feed_id); + } + e.preventDefault(); + return false; + }); + jQuery('.fwp-feed-contents-picker-pick-' + feed_id).click(function (e) { + jQuery('#fwp-feed-contents-picker-' + feed_id).hide(500, function () { jQuery('#fwp-feed-contents-picker-' + feed_id).remove(); }); + if (typeof(pickCallback)=='function') { + pickCallback(feed_id, jQuery(this).attr('href')); + } + e.preventDefault(); + return false; + }); + }, + function (response) { + jQuery('#' + picker_id).addClass('error').html('There was a problem getting a listing of the feed. Sorry!').delay(5000).hide(500, function () { jQuery(this).remove(); }); + }); +} + +function fwp_feedspicker (destination, pickCallback, closeCallback) { + var dabber = jQuery(destination).attr('id'); + var picker_id = 'fwp-feeds-picker-' + dabber; + + jQuery('<div class="fwp-feeds-picker" style="display: none;" id="'+picker_id+'"><p>Loading...</p></div>').insertAfter(destination); + jQuery('#'+picker_id).show(500); + + fwp_feedspiper() + .done(function (response) { + var ul = '<h4>Using subscription...</h4><ul>'; + for (var i=0; i < response.length; i++) { + ul += '<li><a class="fwp-feeds-picker-pick-'+dabber+'" href="#feed-' + +response[i].id + +'">' + response[i].name + '</a></li>'; + } + ul += '</ul>'; + ul += '<a class="fwp-feeds-picker-close" href="#" id="fwp-feeds-picker-' + dabber + '-close">x</a>'; + + jQuery('#fwp-feeds-picker-' + dabber).html(ul);; + + // Set up event handlers. + jQuery('#fwp-feeds-picker-' + dabber + '-close').click(function (e) { + jQuery('#fwp-feeds-picker-' + dabber).hide(500, function () { jQuery('#fwp-feeds-picker-' + dabber).remove(); }); + if (typeof(closeCallback)=='function') { + closeCallback(destination); + } + e.preventDefault(); + return false; + }); + jQuery('.fwp-feeds-picker-pick-' + dabber).click(function (e) { + jQuery('#fwp-feeds-picker-' + dabber).hide(500, function () { + jQuery('#fwp-feeds-picker-' + dabber).remove(); + }); + + var feed_id = jQuery(this).attr('href').replace(/^#feed-/, ''); + + if (typeof(pickCallback)=='function') { + pickCallback(feed_id, jQuery(this)); + } + e.preventDefault(); + return false; + }); + }) + .fail(function (response) { + jQuery('#' + picker_id).addClass('error').html('There was a problem getting a listing of your subscriptions. Sorry!').delay(5000).hide(500, function () { jQuery(this).remove(); }); + }); +} + +function fwp_xpathtest_ajax (expression, feed_id, post_id) { + var data = { + action: 'fwp_xpathtest', + xpath: expression, + feed_id: feed_id, + post_id: post_id + }; + + return jQuery.ajax({ + type: "GET", + url: ajaxurl, + data: data + }); +} + +function fwp_xpathtest_fail (response, result_id, destination) { + jQuery('<div class="fwp-xpath-test-results error" style="display: none;" id="'+result_id+'">There was a problem communicating with the server.<p>result = <code>'+response+'</code></p></div>').insertAfter(destination); + jQuery('#'+result_id).show(500).delay(3000).hide(500, function () { jQuery(this).remove(); }); +} + +function fwp_xpathtest_ok (response, result_id, destination) { + var dabber = jQuery(destination).attr('id'); + var resultsHtml = '<ul>'; + + if (response.results instanceof Array) { + for (var i = 0; i < response.results.length; i++) { + resultsHtml += '<li>result['+i.toString()+'] = <code>'+response.results[i]+'</code></li>'; + } + } else { + resultsHtml += '<li>result = <code>' + response.results + '</code></li>'; + } /* if */ + resultsHtml += '</ul>'; + + jQuery('<div class="fwp-xpath-test-results" style="display: none;" id="'+result_id+'"><h4>'+response.expression+'</h4>'+resultsHtml+'</code> <a class="fwp-xpath-test-results-close">x</a></div>').insertAfter(destination); + + var link_id = 'fwp-xpath-test-results-post-'+dabber; + if (jQuery('#'+link_id).length > 0) { + jQuery('#'+link_id).attr('href', response.guid).html(response.post_title); + } else { + jQuery('<div id="contain-'+link_id+'" class="fwp-xpath-test-results-post fwp-xpath-test-setting">Using post: <a id="'+link_id+'" href="'+response.guid+'"> '+response.post_title+'</a> (<a href="#" class="fwp-xpath-test-results-post-change">reset</a>)</div>').insertAfter('#'+result_id); + } /* if */ + + jQuery('#'+result_id).find('.fwp-xpath-test-results-close').click(function (e) { + e.preventDefault(); + + jQuery('#'+result_id).hide(500, function () { jQuery(this).remove(); }); + return false; + }); + jQuery('#contain-'+link_id).find('.fwp-xpath-test-results-post-change').click(function (e) { + e.preventDefault(); + jQuery('#contain-'+link_id).remove(); + + return false; + }); + jQuery('#'+result_id).show(500); +} + +function fwp_xpathtest (expression, destination, feed_id) { + var dabber = jQuery(destination).attr('id'); + var result_id = 'fwp-xpath-test-results-'+dabber; + var preset_post_id = 'fwp-xpath-test-results-post-'+dabber; + var post_id = jQuery('#'+preset_post_id).attr('href'); + + // Clear out any previous results. + jQuery('#'+result_id).remove(); + + if (jQuery('#xpath-test-feed-id-'+dabber).length > 0) { + feed_id = jQuery('#xpath-test-feed-id-'+dabber).val(); + } + + if ('*' == feed_id) { + + fwp_feedspicker(destination, function (feed_id, a) { + var href = a.attr('href'); + var text = a.text(); + + jQuery('<div class="fwp-xpath-test-feed-id fwp-xpath-test-setting" id="contain-xpath-test-feed-id-'+dabber+'">Using sub: <a href="'+href+'">'+text+'</a><input type="hidden" id="xpath-test-feed-id-'+dabber+'" name="xpath_test_feed_id" value="'+feed_id+'" /> (<a href="#" class="fwp-xpath-test-feed-id-change">reset</a>)</div>').insertAfter(destination); + + jQuery('#contain-xpath-test-feed-id-'+dabber).find('.fwp-xpath-test-feed-id-change').click(function (e) { + e.preventDefault(); + + // If there is a post set, we need to reset that + console.log(('#contain-fwp-xpath-test-results-post-'+dabber), jQuery('#contain-fwp-xpath-test-results-post-'+dabber)); + jQuery('#contain-fwp-xpath-test-results-post-'+dabber).remove(); + + // Show yourself out. + jQuery('#contain-xpath-test-feed-id-'+dabber).remove(); + return false; + }); + + // Now recursively call the function in order to force + // a post-picker. + fwp_xpathtest(expression, destination, feed_id); + }); + } + + // Check for a pre-selected post GUID. + else if (post_id) { + fwp_xpathtest_ajax(expression, feed_id, post_id) + .done( function (response) { fwp_xpathtest_ok(response, result_id, destination); } ) + .fail( function (response) { fwp_xpathtest_fail(response, result_id, destination); } ); + } + else { + // Pop up the feed content picker + fwp_feedcontentspicker(feed_id, destination, function (feed_id, post_id) { + fwp_xpathtest_ajax(expression, feed_id, post_id) + .done( function (response) { fwp_xpathtest_ok(response, result_id, destination); } ) + .fail( function (response) { fwp_xpathtest_fail(response, result_id, destination); } ); + }); + } + +} + jQuery(document).ready(function($){ + if ( $('.xpath-test').length ) { + $('.xpath-test').click ( function (e) { + e.preventDefault(); + + // Pull the local expression from the text box + var expr = jQuery(this).closest('tr').find('textarea').val(); + + // Check to see if we are on a Feed settings page or + // on the global settings page; + var feed_id = jQuery('input[name="save_link_id"]').val(); + + fwp_xpathtest(expr, jQuery(this), feed_id); + return false; + }); + } if ( $('.jaxtag').length ) { tagBox.init(); } diff --git a/wp-content/plugins/feedwordpress/feedwordpress.php b/wp-content/plugins/feedwordpress/feedwordpress.php index fbccc1c65961df747cf244d8ff1b7f5ecbc43dfe..ca1311090db739f140acfcb407a5b6f5e10a902b 100644 --- a/wp-content/plugins/feedwordpress/feedwordpress.php +++ b/wp-content/plugins/feedwordpress/feedwordpress.php @@ -3,7 +3,7 @@ Plugin Name: FeedWordPress Plugin URI: http://feedwordpress.radgeek.com/ Description: simple and flexible Atom/RSS syndication for WordPress -Version: 2011.1019 +Version: 2013.0502 Author: Charles Johnson Author URI: http://radgeek.com/ License: GPL @@ -11,30 +11,28 @@ License: GPL /** * @package FeedWordPress - * @version 2011.1019 + * @version 2013.0502 */ # This uses code derived from: # - wp-rss-aggregate.php by Kellan Elliot-McCrea <kellan@protest.net> -# - MagpieRSS by Kellan Elliot-McCrea <kellan@protest.net> +# - SimplePie feed parser by Ryan Parman, Geoffrey Sneddon, Ryan McCue, et al. +# - MagpieRSS feed parser by Kellan Elliot-McCrea <kellan@protest.net> # - Ultra-Liberal Feed Finder by Mark Pilgrim <mark@diveintomark.org> # - WordPress Blog Tool and Publishing Platform <http://wordpress.org/> # according to the terms of the GNU General Public License. # -# INSTALLATION: see readme.txt or <http://projects.radgeek.com/install> +# INSTALLATION: see readme.txt or <http://feedwordpress.radgeek.com/install> # # USAGE: once FeedWordPress is installed, you manage just about everything from -# the WordPress Dashboard, under the Syndication menu. To ensure that fresh -# content is added as it becomes available, you can convince your contributors -# to put your XML-RPC URI (if WordPress is installed at -# <http://www.zyx.com/blog>, XML-RPC requests should be sent to -# <http://www.zyx.com/blog/xmlrpc.php>), or update manually under the -# Syndication menu, or set up automatic updates under Syndication --> Settings, -# or use a cron job. +# the WordPress Dashboard, under the Syndication menu. To keep fresh content +# coming in as it becomes available, you'll have to either check for updates +# manually, or set up one of the automatically-scheduled update methods. See +# <http://feedwordpress.radgeek.com/wiki/quick-start/> for some details. # -- Don't change these unless you know what you're doing... -define ('FEEDWORDPRESS_VERSION', '2011.1019'); +define ('FEEDWORDPRESS_VERSION', '2013.0502'); define ('FEEDWORDPRESS_AUTHOR_CONTACT', 'http://radgeek.com/contact'); if (!defined('FEEDWORDPRESS_BLEG')) : @@ -54,6 +52,8 @@ else : endif; endif; define ('FEEDWORDPRESS_DEBUG', $feedwordpress_debug); +$feedwordpress_compatibility = true; +define ('FEEDWORDPRESS_COMPATIBILITY', $feedwordpress_compatibility); define ('FEEDWORDPRESS_CAT_SEPARATOR_PATTERN', '/[:\n]/'); define ('FEEDWORDPRESS_CAT_SEPARATOR', "\n"); @@ -64,14 +64,6 @@ define ('FEEDWORDPRESS_FRESHNESS_INTERVAL', 10*60); // Every ten minutes define ('FWP_SCHEMA_HAS_USERMETA', 2966); define ('FWP_SCHEMA_USES_ARGS_TAXONOMY', 12694); // Revision # for using $args['taxonomy'] to get link categories -define ('FWP_SCHEMA_20', 3308); // Database schema # for WP 2.0 -define ('FWP_SCHEMA_21', 4772); // Database schema # for WP 2.1 -define ('FWP_SCHEMA_23', 5495); // Database schema # for WP 2.3 -define ('FWP_SCHEMA_25', 7558); // Database schema # for WP 2.5 -define ('FWP_SCHEMA_26', 8201); // Database schema # for WP 2.6 -define ('FWP_SCHEMA_27', 9872); // Database schema # for WP 2.7 -define ('FWP_SCHEMA_28', 11548); // Database schema # for WP 2.8 -define ('FWP_SCHEMA_29', 12329); // Database schema # for WP 2.9 define ('FWP_SCHEMA_30', 12694); // Database schema # for WP 3.0 if (FEEDWORDPRESS_DEBUG) : @@ -110,6 +102,7 @@ if (!function_exists('wp_insert_user')) : endif; $dir = dirname(__FILE__); +require_once("${dir}/externals/myphp/myphp.class.php"); require_once("${dir}/admin-ui.php"); require_once("${dir}/feedwordpresssyndicationpage.class.php"); require_once("${dir}/compatability.php"); // Legacy API @@ -119,10 +112,13 @@ require_once("${dir}/feedwordpresshtml.class.php"); require_once("${dir}/feedwordpress-content-type-sniffer.class.php"); require_once("${dir}/inspectpostmeta.class.php"); require_once("${dir}/syndicationdataqueries.class.php"); +require_once("${dir}/feedwordpie.class.php"); +require_once("${dir}/feedwordpie_item.class.php"); require_once("${dir}/feedwordpress_file.class.php"); require_once("${dir}/feedwordpress_parser.class.php"); require_once("${dir}/feedwordpressrpc.class.php"); require_once("${dir}/feedwordpresshttpauthenticator.class.php"); +require_once("${dir}/feedwordpresslocalpost.class.php"); // Magic quotes are just about the stupidest thing ever. if (is_array($_POST)) : @@ -167,7 +163,8 @@ if (!FeedWordPress::needs_upgrade()) : // only work if the conditions are safe! # Filter in original permalinks if the user wants that add_filter('post_link', 'syndication_permalink', /*priority=*/ 1, /*arguments=*/ 3); - + add_filter('post_type_link', 'syndication_permalink', /*priority=*/ 1, /*arguments=*/ 4); + # When foreign URLs are used for permalinks in feeds or display # contexts, they need to be escaped properly. add_filter('the_permalink', 'syndication_permalink_escaped'); @@ -183,6 +180,7 @@ if (!FeedWordPress::needs_upgrade()) : // only work if the conditions are safe! remove_filter('pre_link_url', 'wp_filter_kses'); # Admin menu + add_action('admin_init', array('feedwordpress', 'admin_init')); add_action('admin_menu', 'fwp_add_pages'); add_action('admin_notices', 'fwp_check_debug'); @@ -214,17 +212,18 @@ if (!FeedWordPress::needs_upgrade()) : // only work if the conditions are safe! # Cron-less auto-update. Hooray! $autoUpdateHook = $feedwordpress->automatic_update_hook(); if (!is_null($autoUpdateHook)) : - add_action($autoUpdateHook, array(&$feedwordpress, 'auto_update')); + add_action($autoUpdateHook, array($feedwordpress, 'auto_update')); endif; - add_action('init', array(&$feedwordpress, 'init')); - add_action('shutdown', array(&$feedwordpress, 'email_diagnostic_log')); - add_action('wp_dashboard_setup', array(&$feedwordpress, 'dashboard_setup')); + add_action('init', array($feedwordpress, 'init')); + add_action('shutdown', array($feedwordpress, 'email_diagnostic_log')); + add_action('wp_dashboard_setup', array($feedwordpress, 'dashboard_setup')); # Default sanitizers add_filter('syndicated_item_content', array('SyndicatedPost', 'resolve_relative_uris'), 0, 2); add_filter('syndicated_item_content', array('SyndicatedPost', 'sanitize_content'), 0, 2); + add_action('plugins_loaded', array('FeedWordPress', 'admin_api')); else : # Hook in the menus, which will just point to the upgrade interface add_action('admin_menu', 'fwp_add_pages'); @@ -315,7 +314,8 @@ function debug_out_feedwordpress_footer () { * @return bool TRUE if the post's meta-data indicates it was syndicated; FALSE otherwise */ function is_syndicated ($id = NULL) { - return (strlen(get_syndication_feed_id($id)) > 0); + $p = new FeedWordPressLocalPost($id); + return $p->is_syndicated(); } /* function is_syndicated() */ function feedwordpress_display_url ($url, $before = 60, $after = 0) { @@ -341,74 +341,59 @@ function feedwordpress_display_url ($url, $before = 60, $after = 0) { } /* feedwordpress_display_url () */ function get_syndication_source_property ($original, $id, $local, $remote = NULL) { - $ret = NULL; - if (is_null($original)) : - $original = FeedWordPress::use_aggregator_source_data(); - endif; - - if (is_null($remote)) : - $remote = $local . '_original'; - endif; - - $vals = ($original ? get_post_custom_values($remote, $id) : array()); - if (count($vals) < 1) : - $vals = get_post_custom_values($local, $id); - endif; - - if (count($vals) > 0) : - $ret = $vals[0]; - endif; - return $ret; + $p = new FeedWordPressLocalPost($id); + return $p->meta($local, array("unproxy" => $original, "unproxied setting" => $remote)); } /* function get_syndication_source_property () */ -function the_syndication_source_link ($original = NULL, $id = NULL) { echo get_syndication_source_link($original, $id); } function get_syndication_source_link ($original = NULL, $id = NULL) { - return get_syndication_source_property($original, $id, 'syndication_source_uri'); + $p = new FeedWordPressLocalPost($id); + return $p->syndication_source_link($original); } /* function get_syndication_source_link() */ -function the_syndication_source ($original = NULL, $id = NULL) { echo get_syndication_source($original, $id); } +function the_syndication_source_link ($original = NULL, $id = NULL) { + echo get_syndication_source_link($original, $id); +} /* function the_syndication_source_link() */ + function get_syndication_source ($original = NULL, $id = NULL) { - $ret = get_syndication_source_property($original, $id, 'syndication_source'); - if (is_null($ret) or strlen(trim($ret)) == 0) : // Fall back to URL of blog - $ret = feedwordpress_display_url(get_syndication_source_link()); - endif; - return $ret; + $p = new FeedWordPressLocalPost($id); + return $p->syndication_source($original); } /* function get_syndication_source() */ -function the_syndication_feed ($original = NULL, $id = NULL) { echo get_syndication_feed($original, $id); } +function the_syndication_source ($original = NULL, $id = NULL) { + echo get_syndication_source($original, $id); +} /* function the_syndication_source () */ + function get_syndication_feed ($original = NULL, $id = NULL) { - return get_syndication_source_property($original, $id, 'syndication_feed'); + $p = new FeedWordPressLocalPost($id); + return $p->syndication_feed($original); } /* function get_syndication_feed() */ -function the_syndication_feed_guid ($original = NULL, $id = NULL) { echo get_syndication_feed_guid($original, $id); } +function the_syndication_feed ($original = NULL, $id = NULL) { + echo get_syndication_feed($original, $id); +} /* function the_syndication_feed() */ + function get_syndication_feed_guid ($original = NULL, $id = NULL) { - $ret = get_syndication_source_property($original, $id, 'syndication_source_id'); - if (is_null($ret) or strlen(trim($ret))==0) : // Fall back to URL of feed - $ret = get_syndication_feed(); - endif; - return $ret; + $p = new FeedWordPressLocalPost($id); + return $p->syndication_feed_guid($original); } /* function get_syndication_feed_guid () */ -function get_syndication_feed_id ($id = NULL) { list($u) = get_post_custom_values('syndication_feed_id', $id); return $u; } -function the_syndication_feed_id ($id = NULL) { echo get_syndication_feed_id($id); } +function the_syndication_feed_guid ($original = NULL, $id = NULL) { + echo get_syndication_feed_guid($original, $id); +} /* function the_syndication_feed_guid () */ -$feedwordpress_linkcache = array (); // only load links from database once -function get_syndication_feed_object ($id = NULL) { - global $feedwordpress_linkcache; +function get_syndication_feed_id ($id = NULL) { + $p = new FeedWordPressLocalPost($id); + return $p->feed_id(); +} /* function get_syndication_feed_id () */ - $link = NULL; +function the_syndication_feed_id ($id = NULL) { + echo get_syndication_feed_id($id); +} /* function the_syndication_feed_id () */ - $feed_id = get_syndication_feed_id($id); - if (strlen($feed_id) > 0): - if (isset($feedwordpress_linkcache[$feed_id])) : - $link = $feedwordpress_linkcache[$feed_id]; - else : - $link = new SyndicatedLink($feed_id); - $feedwordpress_linkcache[$feed_id] = $link; - endif; - endif; - return $link; -} +function get_syndication_feed_object ($id = NULL) { + $p = new FeedWordPressLocalPost($id); + return $p->feed(); +} /* function get_syndication_feed_object() */ function get_feed_meta ($key, $id = NULL) { $ret = NULL; @@ -418,14 +403,16 @@ function get_feed_meta ($key, $id = NULL) { $ret = $link->settings[$key]; endif; return $ret; -} /* get_feed_meta() */ +} /* function get_feed_meta() */ function get_syndication_permalink ($id = NULL) { - list($u) = get_post_custom_values('syndication_permalink', $id); return $u; -} + $p = new FeedWordPressLocalPost($id); + return $p->syndication_permalink(); +} /* function get_syndication_permalink () */ + function the_syndication_permalink ($id = NULL) { echo get_syndication_permalink($id); -} +} /* function the_syndication_permalink () */ /** * get_local_permalink: returns a string containing the internal permalink @@ -478,11 +465,8 @@ $feedwordpress_the_original_permalink = NULL; function feedwordpress_preserve_syndicated_content ($text) { global $feedwordpress_the_syndicated_content; - $globalExpose = (get_option('feedwordpress_formatting_filters') == 'yes'); - $localExpose = get_post_custom_values('_feedwordpress_formatting_filters'); - $expose = ($globalExpose or ((count($localExpose) > 0) and $localExpose[0])); - - if ( is_syndicated() and !$expose ) : + $p = new FeedWordPressLocalPost; + if (!$p->is_exposed_to_formatting_filters()) : $feedwordpress_the_syndicated_content = $text; else : $feedwordpress_the_syndicated_content = NULL; @@ -544,7 +528,7 @@ function feedwordpress_item_feed_data () { * @global $id * @global $feedwordpress_the_original_permalink */ -function syndication_permalink ($permalink = '', $post = null, $leavename = false) { +function syndication_permalink ($permalink = '', $post = null, $leavename = false, $sample = false) { global $id; global $feedwordpress_the_original_permalink; @@ -618,6 +602,7 @@ function syndication_comments_feed_link ($link) { // that value here. $source = get_syndication_feed_object(); $replacement = NULL; + if ($source->setting('munge comments feed links', 'munge_comments_feed_links', 'yes') != 'no') : $commentFeeds = get_post_custom_values('wfw:commentRSS'); if ( @@ -668,6 +653,8 @@ function syndication_comments_feed_link ($link) { ################################################################################ function fwp_add_pages () { + global $feedwordpress; + $menu_cap = FeedWordPress::menu_cap(); $settings_cap = FeedWordPress::menu_cap(/*sub=*/ true); $syndicationMenu = FeedWordPress::path('syndication.php'); @@ -680,6 +667,12 @@ function fwp_add_pages () { WP_PLUGIN_URL.'/'.FeedWordPress::path('feedwordpress-tiny.png') ); + do_action('feedwordpress_admin_menu_pre_feeds', $menu_cap, $settings_cap); + add_submenu_page( + $syndicationMenu, 'Syndicated Sites', 'Syndicated Sites', + $settings_cap, $syndicationMenu + ); + do_action('feedwordpress_admin_menu_pre_feeds', $menu_cap, $settings_cap); add_submenu_page( $syndicationMenu, 'Syndicated Feeds & Updates', 'Feeds & Updates', @@ -700,7 +693,7 @@ function fwp_add_pages () { do_action('feedwordpress_admin_menu_pre_categories', $menu_cap, $settings_cap); add_submenu_page( - $syndicationMenu, 'Categories'.FEEDWORDPRESS_AND_TAGS, 'Categories'.FEEDWORDPRESS_AND_TAGS, + $syndicationMenu, 'Categories & Tags', 'Categories & Tags', $settings_cap, FeedWordPress::path('categories-page.php') ); @@ -715,6 +708,9 @@ function fwp_add_pages () { $syndicationMenu, 'FeedWordPress Diagnostics', 'Diagnostics', $settings_cap, FeedWordPress::path('diagnostics-page.php') ); + + add_filter('page_row_actions', array($feedwordpress, 'row_actions'), 10, 2); + add_filter('post_row_actions', array($feedwordpress, 'row_actions'), 10, 2); } /* function fwp_add_pages () */ function fwp_check_debug () { @@ -797,7 +793,13 @@ function fwp_publish_post_hook ($post_id) { } function feedwordpress_add_post_edit_controls () { + global $feedwordpress; + + // Put in Manual Editing checkbox add_meta_box('feedwordpress-post-controls', __('Syndication'), 'feedwordpress_post_edit_controls', 'post', 'side', 'high'); + + add_filter('user_can_richedit', array($feedwordpress, 'user_can_richedit'), 1000, 1); + if (FeedWordPress::diagnostic_on('syndicated_posts:static_meta_data')) : $GLOBALS['inspectPostMeta'] = new InspectPostMeta; endif; @@ -897,19 +899,60 @@ class FeedWordPress { ); var $feeds = NULL; - + var $feedurls = NULL; var $httpauth = NULL; + # function FeedWordPress (): Contructor; retrieve a list of feeds function FeedWordPress () { $this->feeds = array (); + $this->feedurls = array(); $links = FeedWordPress::syndicated_links(); if ($links): foreach ($links as $link): - $this->feeds[] = new SyndicatedLink($link); + $id = intval($link->link_id); + $url = $link->link_rss; + + // Store for later reference. + $this->feeds[$id] = $id; + if (strlen($url) > 0) : + $this->feedurls[$url] = $id; + endif; endforeach; endif; - + $this->httpauth = new FeedWordPressHTTPAuthenticator; } // FeedWordPress::FeedWordPress () + function subscribed ($id) { + return (isset($this->feedurls[$id]) or isset($this->feeds[$id])); + } /* FeedWordPress::subscribed () */ + + function subscription ($which) { + $sub = NULL; + + if (is_string($which) and isset($this->feedurls[$which])) : + $which = $this->feedurls[$which]; + endif; + + if (isset($this->feeds[$which])) : + $sub = $this->feeds[$which]; + endif; + + // If it's not in the in-memory cache already, try to load it from DB. + // This is necessary to fill requests for subscriptions that we don't + // cache in memory, e.g. for deactivated feeds. + if (is_null($sub)) : + $sub = get_bookmark($which); + endif; + + // Load 'er up if you haven't already. + if (!is_null($sub) and !($sub InstanceOf SyndicatedLink)) : + $link = new SyndicatedLink($sub); + $this->feeds[$which] = $link; + $sub = $link; + endif; + + return $sub; + } /* FeedWordPress::subscription () */ + # function update (): polls for updates on one or more Contributor feeds # # Arguments: @@ -986,7 +1029,7 @@ class FeedWordPress { endif; // Randomize order for load balancing purposes - $feed_set = $this->feeds; + $feed_set = array_keys($this->feeds); shuffle($feed_set); $updateWindow = (int) get_option('feedwordpress_update_window', DEFAULT_UPDATE_PERIOD) * 60 /* sec/min */; @@ -1001,10 +1044,13 @@ class FeedWordPress { ), $uri); $feed_set = apply_filters('feedwordpress_update_feeds', $feed_set, $uri); - + + // Loop through and check for new posts $delta = NULL; $remaining = $max_polls; - foreach ($feed_set as $feed) : + foreach ($feed_set as $feed_id) : + + $feed = $this->subscription($feed_id); // Has this process overstayed its welcome? if ( @@ -1157,6 +1203,38 @@ class FeedWordPress { return $ret; } // FeedWordPress::stale() + static function admin_init () { + // WordPress 3.5+ compat: the WP devs are in the midst of removing Links from the WordPress core. Eventually we'll have to deal + // with the possible disappearance of the wp_links table as a whole; but in the meantime, we just need to turn on the interface + // element to avoid problems with user capabilities that presume the presence of the Links Manager in the admin UI. + if (!intval(get_option('link_manager_enabled', false))) : + update_option('link_manager_enabled', true); + endif; + } /* FeedWordPress::admin_init() */ + + function admin_api () { + // This sucks, but WordPress doesn't give us any other way to + // easily invoke a permanent-delete from a plugged in post + // actions link. So we create a magic parameter, and when this + // magic parameter is activated, the WordPress trashcan is + // temporarily de-activated. + + if (MyPHP::request('fwp_post_delete')=='nuke') : + // Get post ID # + $post_id = MyPHP::request('post'); + if (!$post_id) : + $post_id = MyPHP::request('post_ID'); + endif; + + // Make sure we've got the right nonce and all that. + check_admin_referer('delete-post_' . $post_id); + + // If so, disable the trashcan. + define('EMPTY_TRASH_DAYS', 0); + endif; + + } + function init () { global $fwp_path; @@ -1174,15 +1252,185 @@ class FeedWordPress { wp_enqueue_style('dashboard'); wp_enqueue_style('feedwordpress-elements'); - if (function_exists('wp_admin_css')) : + /*if (function_exists('wp_admin_css')) : wp_admin_css('css/dashboard'); - endif; + endif;*/ endif; + // This is a special post status for hiding posts that have expired + register_post_status('fwpretired', array( + 'label' => _x('Retired', 'post'), + 'label_count' => _n_noop('Retired <span class="count">(%s)</span>', 'Retired <span class="count">(%s)</span>'), + 'exclude_from_search' => true, + 'public' => false, + 'publicly_queryable' => false, + 'show_in_admin_all_list' => false, + 'show_in_admin_status_list' => true, + )); + add_action( + /*hook=*/ 'template_redirect', + /*function=*/ array($this, 'redirect_retired'), + /*priority=*/ -100 + ); + + add_action('wp_ajax_fwp_feeds', array($this, 'fwp_feeds')); + add_action('wp_ajax_fwp_feedcontents', array($this, 'fwp_feedcontents')); + add_action('wp_ajax_fwp_xpathtest', array($this, 'fwp_xpathtest')); + $this->clear_cache_magic_url(); $this->update_magic_url(); } /* FeedWordPress::init() */ + function fwp_feeds () { + $feeds = array(); + $feed_ids = $this->feeds; + + foreach ($feed_ids as $id) : + $sub = $this->subscription($id); + $feeds[] = array( + "id" => $id, + "url" => $sub->uri(), + "name" => $sub->name(/*fromFeed=*/ false), + ); + endforeach; + + header("Content-Type: application/json"); + echo json_encode($feeds); + exit; + } + + function fwp_feedcontents () { + $feed_id = MyPHP::request('feed_id'); + + // Let's load up some data from the feed . . . + $feed = $this->subscription($feed_id); + $posts = $feed->live_posts(); + + if (is_wp_error($posts)) : + header("HTTP/1.1 502 Bad Gateway"); + $result = $posts; + else : + $result = array(); + + foreach ($posts as $post) : + $p = new SyndicatedPost($post, $feed); + + $result[] = array( + "post_title" => $p->entry->get_title(), + "post_link" => $p->permalink(), + "guid" => $p->guid(), + "post_date" => $p->published(), + ); + endforeach; + endif; + + header("Content-Type: application/json"); + + echo json_encode($result); + + // This is an AJAX request, so close it out thus. + die; + } /* FeedWordPress::fwp_feedcontents () */ + + function fwp_xpathtest () { + $xpath = MyPHP::request('xpath'); + $feed_id = MyPHP::request('feed_id'); + $post_id = MyPHP::request('post_id'); + + $expr = new FeedWordPressParsedPostMeta($xpath); + + // Let's load up some data from the feed . . . + $feed = $this->subscription($feed_id); + $posts = $feed->live_posts(); + + if (!is_wp_error($posts)) : + if (strlen($post_id) == 0) : + $post = $posts[0]; + else : + $post = null; + + foreach ($posts as $p) : + if ($p->get_id() == $post_id) : + $post = $p; + endif; + endforeach; + endif; + + $post = new SyndicatedPost($post, $feed); + $meta = $expr->do_substitutions($post); + + $result = array( + "post_title" => $post->entry->get_title(), + "post_link" => $post->permalink(), + "guid" => $post->guid(), + "expression" => $xpath, + "results" => $meta + ); + else : + $result = array( + "expression" => $xpath, + "feed_id" => $feed_id, + "post_id" => $post_id, + "results" => $posts + ); + + header("HTTP/1.1 503 Bad Gateway"); + endif; + + header("Content-Type: application/json"); + + echo json_encode($result); + + // This is an AJAX request, so close it out thus. + die; + } /* FeedWordPress::fwp_xpathtest () */ + + function redirect_retired () { + global $wp_query; + if (is_singular()) : + if ('fwpretired'==$wp_query->post->post_status) : + do_action('feedwordpress_redirect_retired', $wp_query->post); + + if (!($template = get_404_template())) : + $template = get_index_template(); + endif; + if ($template = apply_filters('template_include', $template)) : + header("HTTP/1.1 410 Gone"); + include($template); + endif; + exit; + endif; + endif; + } + + function row_actions ($actions, $post) { + if (is_syndicated($post->ID)) : + $link = get_delete_post_link($post->ID, '', true); + $link = MyPHP::url($link, array("fwp_post_delete" => "nuke")); + + $caption = 'Erase the record of this post (will be re-syndicated if it still appears on the feed).'; + $linktext = 'Erase/Resyndicate'; + + $keys = array_keys($actions); + $links = array(); + foreach ($keys as $key) : + $links[$key] = $actions[$key]; + + if ('trash'==$key) : + $links[$key] = "<a class='submitdelete' title='" . esc_attr( __( 'Move this item to the Trash (will NOT be re-syndicated)' ) ) . "' href='" . get_delete_post_link( $post->ID ) . "'>" . __( 'Trash/Don’t Resyndicate' ) . "</a>"; + + // Placeholder. + $links['delete'] = ''; + endif; + endforeach; + + $links['delete'] = '<a class="submitdelete" title="'.esc_attr(__($caption)).'" href="' . $link . '">' . __($linktext) . '</a>'; + + $actions = $links; + endif; + return $actions; + } + function dashboard_setup () { $see_it = FeedWordPress::menu_cap(); @@ -1202,12 +1450,12 @@ class FeedWordPress { add_meta_box( /*id=*/ $widget_id, /*title=*/ $widget_name, - /*callback=*/ array(&$this, 'dashboard'), + /*callback=*/ array($this, 'dashboard'), /*page=*/ 'dashboard', /*context=*/ $column, /*priority=*/ $priority ); - /*control_callback= array(&$this, 'dashboard_control') */ + /*control_callback= array($this, 'dashboard_control') */ // This is kind of rude, I know, but the dashboard widget isn't // worth much if users don't know that it exists, and I don't @@ -1244,6 +1492,20 @@ class FeedWordPress { $syndicationPage->dashboard_box($syndicationPage); } /* FeedWordPress::dashboard () */ + function user_can_richedit ($rich_edit) { + + $post = new FeedWordPressLocalPost; + + if (!$post->is_exposed_to_formatting_filters()) : + // Disable visual editor and only allow operations + // directly on HTML if post is bypassing fmt filters + $rich_edit = false; + endif; + + return $rich_edit; + + } /* FeedWordPress::user_can_richedit () */ + function update_magic_url () { global $wpdb; @@ -1287,10 +1549,7 @@ class FeedWordPress { } /* FeedWordPress::clear_cache_magic_url() */ function clear_cache_requested () { - return ( - isset($_GET['clear_cache']) - and $_GET['clear_cache'] - ); + return MyPHP::request('clear_cache'); } /* FeedWordPress::clear_cache_requested() */ function update_requested () { @@ -1416,6 +1675,7 @@ class FeedWordPress { else : $links = array(); endif; + return $links; } // function FeedWordPress::syndicated_links() @@ -1604,11 +1864,11 @@ class FeedWordPress { endif; $timeout = intval($timeout); - $pie_class = apply_filters('feedwordpress_simplepie_class', 'SimplePie'); + $pie_class = apply_filters('feedwordpress_simplepie_class', 'FeedWordPie'); $cache_class = apply_filters('feedwordpress_cache_class', 'WP_Feed_Cache'); $file_class = apply_filters('feedwordpress_file_class', 'FeedWordPress_File'); $parser_class = apply_filters('feedwordpress_parser_class', 'FeedWordPress_Parser'); - + $item_class = apply_filters('feedwordpress_item_class', 'FeedWordPie_Item'); $sniffer_class = apply_filters('feedwordpress_sniffer_class', 'FeedWordPress_Content_Type_Sniffer'); $feed = new $pie_class; @@ -1619,8 +1879,9 @@ class FeedWordPress { $feed->set_content_type_sniffer_class($sniffer_class); $feed->set_file_class($file_class); $feed->set_parser_class($parser_class); + $feed->set_item_class($item_class); $feed->force_feed($force_feed); - $feed->set_cache_duration(FeedWordPress::cache_duration()); + $feed->set_cache_duration(FeedWordPress::cache_duration($params)); $feed->init(); $feed->handle_content_type(); @@ -1657,9 +1918,15 @@ class FeedWordPress { return ($magpies + $simplepies); } /* FeedWordPress::clear_cache () */ - function cache_duration () { + function cache_duration ($params = array()) { + $params = wp_parse_args($params, array( + "cache" => true, + )); + $duration = NULL; - if (defined('FEEDWORDPRESS_CACHE_AGE')) : + if (!$params['cache']) : + $duration = 0; + elseif (defined('FEEDWORDPRESS_CACHE_AGE')) : $duration = FEEDWORDPRESS_CACHE_AGE; endif; return $duration; @@ -1760,6 +2027,7 @@ class FeedWordPress { error_log(FeedWordPress::log_prefix().' '.$out); break; case 'email' : + if (is_null($persist)) : $sect = 'occurrent'; $hook = (isset($dlog['mesg'][$sect]) ? count($dlog['mesg'][$sect]) : 0); @@ -1801,7 +2069,11 @@ class FeedWordPress { return $ret; } - function email_diagnostic_log () { + function email_diagnostic_log ($params = array()) { + $params = wp_parse_args($params, array( + "force" => false, + )); + $dlog = get_option('feedwordpress_diagnostics_log', array()); if ($this->has_emailed_diagnostics($dlog)) : @@ -1886,9 +2158,26 @@ EOMAIL; $recipients = FeedWordPressDiagnostic::admin_emails(); endif; + $mesgId = 'feedwordpress+'.time().'@'.$home; + $parentId = get_option('feedwordpress_diagnostics_email_root_message_id', NULL); + + $head = array("Message-ID: <$mesgId>"); + if (!is_null($parentId)) : + // We've already sent off a diagnostic message in the past. Let's do some + // magic to help with threading, in the hopes that all diagnostic messages + // get threaded together. + $head[] = "References: <$parentId>"; + $head[] = "In-Reply-To: <$parentId>"; + $subj = "Re: ".$subj; + else : + // This is the first of its kind. Let's mark it as such. + update_option('feedwordpress_diagnostics_email_root_message_id', $mesgId); + endif; + $head = apply_filters('feedwordpress_diagnostic_email_headers', $head); + foreach ($recipients as $email) : add_filter('wp_mail_content_type', array('FeedWordPress', 'allow_html_mail')); - wp_mail($email, $subj, $body); + wp_mail($email, $subj, $body, $head); remove_filter('wp_mail_content_type', array('FeedWordPress', 'allow_html_mail')); endforeach; endif; @@ -1949,21 +2238,13 @@ EOMAIL; return $path; } + // These are superceded by MyPHP::param/post/get/request, but kept + // here for backward compatibility. function param ($key, $type = 'REQUEST', $default = NULL) { - $where = '_'.strtoupper($type); - $ret = $default; - if (isset($GLOBALS[$where]) and is_array($GLOBALS[$where])) : - if (isset($GLOBALS[$where][$key])) : - $ret = $GLOBALS[$where][$key]; - if (get_magic_quotes_gpc()) : - $ret = stripslashes_deep($ret); - endif; - endif; - endif; - return $ret; + return MyPHP::param($key, $default, $type); } function post ($key, $default = NULL) { - return FeedWordPress::param($key, 'POST'); + return MyPHP::post($key, $default); } } // class FeedWordPress diff --git a/wp-content/plugins/feedwordpress/feedwordpress_file.class.php b/wp-content/plugins/feedwordpress/feedwordpress_file.class.php index c78bbbd6329ee9425ef796de9cc6b6a49125405d..9130a710bb1092fa00d69c72bc657035f0143a45 100644 --- a/wp-content/plugins/feedwordpress/feedwordpress_file.class.php +++ b/wp-content/plugins/feedwordpress/feedwordpress_file.class.php @@ -7,25 +7,44 @@ class FeedWordPress_File extends WP_SimplePie_File { } function __construct ($url, $timeout = 10, $redirects = 5, $headers = null, $useragent = null, $force_fsockopen = false) { + global $feedwordpress; + global $wp_version; + + $source = NULL; + if ($feedwordpress->subscribed($url)) : + $source = $feedwordpress->subscription($url); + endif; + $this->url = $url; $this->timeout = $timeout; $this->redirects = $redirects; $this->headers = $headers; $this->useragent = $useragent; - + $this->method = SIMPLEPIE_FILE_SOURCE_REMOTE; global $wpdb; global $fwp_credentials; - if ( preg_match('/^http(s)?:\/\//i', $url) ) { + if ( preg_match('/^http(s)?:\/\//i', $url) ) : $args = array( 'timeout' => $this->timeout, 'redirection' => $this->redirects); if ( !empty($this->headers) ) $args['headers'] = $this->headers; - if ( SIMPLEPIE_USERAGENT != $this->useragent ) //Use default WP user agent unless custom has been specified + // Use default FWP user agent unless custom has been specified + if ( SIMPLEPIE_USERAGENT != $this->useragent ) : $args['user-agent'] = $this->useragent; + else : + $args['user-agent'] = apply_filters('feedwordpress_user_agent', + 'FeedWordPress/'.FEEDWORDPRESS_VERSION + .' (aggregator:feedwordpress; WordPress/'.$wp_version + .' + '.SIMPLEPIE_NAME.'/'.SIMPLEPIE_VERSION + .'; Allow like Gecko; +http://feedwordpress.radgeek.com/) at ' + . feedwordpress_display_url(get_bloginfo('url')), + $this + ); + endif; // This is ugly as hell, but communicating up and down the chain // in any other way is difficult. @@ -36,21 +55,17 @@ class FeedWordPress_File extends WP_SimplePie_File { $args['username'] = $fwp_credentials['username']; $args['password'] = $fwp_credentials['password']; - else : - - $links = $wpdb->get_results( - $wpdb->prepare("SELECT * FROM {$wpdb->links} WHERE link_rss = '%s'", $url) - ); - if ($links) : - $source = new SyndicatedLink($links[0]); - $args['authentication'] = $source->authentication_method(); - $args['username'] = $source->username(); - $args['password'] = $source->password(); - endif; + elseif ($source InstanceOf SyndicatedLink) : + + $args['authentication'] = $source->authentication_method(); + $args['username'] = $source->username(); + $args['password'] = $source->password(); endif; - + + FeedWordPress::diagnostic('updated_feeds:http', "HTTP [$url] ⇜ ".esc_html(FeedWordPress::val($args))); $res = wp_remote_request($url, $args); + FeedWordPress::diagnostic('updated_feeds:http', "HTTP [$url] ⇝ ".esc_html(FeedWordPress::val($res))); if ( is_wp_error($res) ) { $this->error = 'WP HTTP Error: ' . $res->get_error_message(); @@ -60,12 +75,22 @@ class FeedWordPress_File extends WP_SimplePie_File { $this->body = wp_remote_retrieve_body( $res ); $this->status_code = wp_remote_retrieve_response_code( $res ); } - } else { - if ( ! $this->body = file_get_contents($url) ) { - $this->error = 'file_get_contents could not read the file'; - $this->success = false; - } - } + + if ($source InstanceOf SyndicatedLink) : + $source->update_setting('link/filesize', strlen($this->body)); + $source->update_setting('link/http status', $this->status_code); + $source->save_settings(/*reload=*/ true); + endif; + + // Do not allow schemes other than http(s)? for the time being. + // They are unlikely to be used; and unrestricted use of schemes + // allows for user to use an unrestricted file:/// scheme, which + // may result in exploits by WordPress users against the web + // hosting environment. + else : + $this->error = 'FeedWordPress only allows http or https URLs'; + $this->success = false; + endif; // SimplePie makes a strongly typed check against integers with // this, but WordPress puts a string in. Which causes caching diff --git a/wp-content/plugins/feedwordpress/feedwordpress_parser.class.php b/wp-content/plugins/feedwordpress/feedwordpress_parser.class.php index 49c2239bffcdb543c2ff7ee4a571422624f8a418..971bb6c4cded9bcd0ba623ac3509e35981d17c48 100644 --- a/wp-content/plugins/feedwordpress/feedwordpress_parser.class.php +++ b/wp-content/plugins/feedwordpress/feedwordpress_parser.class.php @@ -1,6 +1,20 @@ <?php class FeedWordPress_Parser extends SimplePie_Parser { + function reset_parser (&$xml) { + xml_parser_free($xml); + + $xml = xml_parser_create_ns($this->encoding, $this->separator); + xml_parser_set_option($xml, XML_OPTION_SKIP_WHITE, 1); + xml_parser_set_option($xml, XML_OPTION_CASE_FOLDING, 0); + xml_set_object($xml, $this); + xml_set_character_data_handler($xml, 'cdata'); + xml_set_element_handler($xml, 'tag_open', 'tag_close'); + xml_set_start_namespace_decl_handler($xml, 'start_xmlns'); + } + function parse (&$data, $encoding) { + $data = apply_filters('feedwordpress_parser_parse', $data, $encoding, $this); + // Use UTF-8 if we get passed US-ASCII, as every US-ASCII character is a UTF-8 character if (strtoupper($encoding) === 'US-ASCII') { @@ -40,7 +54,7 @@ class FeedWordPress_Parser extends SimplePie_Parser { if (substr($data, 0, 5) === '<?xml' && strspn(substr($data, 5, 1), "\x09\x0A\x0D\x20") && ($pos = strpos($data, '?>')) !== false) { - $declaration =& new SimplePie_XML_Declaration_Parser(substr($data, 5, $pos - 5)); + $declaration = new SimplePie_XML_Declaration_Parser(substr($data, 5, $pos - 5)); if ($declaration->parse()) { $data = substr($data, $pos + 2); @@ -76,10 +90,37 @@ class FeedWordPress_Parser extends SimplePie_Parser { xml_set_start_namespace_decl_handler($xml, 'start_xmlns'); // Parse! - if (!xml_parse($xml, $data, true)) + $parseResults = xml_parse($xml, $data, true); + + $endOfJunk = strpos($data, '<?xml'); + if (!$parseResults and $endOfJunk > 0) : + // There is some junk before the feed prolog. Try to get rid of it. + $newData = substr($data, $endOfJunk); + $newData = trim($newData); + $this->reset_parser($xml); + + $parseResults = xml_parse($xml, $newData, true); + endif; + + if (!$parseResults) { + if (class_exists('DOMDocument')) : + libxml_use_internal_errors(true); + $doc = new DOMDocument; + $doc->loadHTML('<html>'.$data.'</html>'); + ///*DBG*/ echo "<plaintext>"; + ///*DBG*/ $dd = $doc->getElementsByTagName('html'); + ///*DBG*/ for ($i = 0; $i < $dd->length; $i++) : + ///*DBG*/ var_dump($dd->item($i)->nodeName); + ///*DBG*/ endfor; + ///*DBG*/ var_dump($doc); + libxml_use_internal_errors(false); + ///*DBG*/ die; + endif; + $this->error_code = xml_get_error_code($xml); $this->error_string = xml_error_string($this->error_code); + ///*DBG*/ echo "WOOGA WOOGA"; var_dump($this->error_string); die; $return = false; } @@ -93,7 +134,7 @@ class FeedWordPress_Parser extends SimplePie_Parser { else { libxml_clear_errors(); - $xml =& new XMLReader(); + $xml = new XMLReader(); $xml->xml($data); while (@$xml->read()) { diff --git a/wp-content/plugins/feedwordpress/feedwordpresshtml.class.php b/wp-content/plugins/feedwordpress/feedwordpresshtml.class.php index 1cefd5a5f5f8ec8566257ee2910584daa94d3312..91cb5ed9030abc99343c134beca4d324c5d9934f 100644 --- a/wp-content/plugins/feedwordpress/feedwordpresshtml.class.php +++ b/wp-content/plugins/feedwordpress/feedwordpresshtml.class.php @@ -38,7 +38,7 @@ class FeedWordPressHTML { ); } /* function FeedWordPressHTML::attributeMatch () */ - function tagWithAttributeRegex ($tag, $attr, $value) { + function tagWithAttributeRegex ($tag, $attr, $value, $closing = true) { return ":( (<($tag)\s+[^>]*) ($attr)= @@ -50,13 +50,14 @@ class FeedWordPressHTML { | \s*((?!/>)($value)) ([^>]*>) - ) + )".($closing ? " (((?!</($tag)>).)*) (</($tag)>) + " : "")." :ix"; } /* FeedWordPressHTML::tagWithAttributeRegex () */ - function tagWithAttributeMatch ($matches) { + function tagWithAttributeMatch ($matches, $closing = true) { for ($i = 0; $i <= 21; $i++) : if (!isset($matches[$i])) : $matches[$i] = ''; @@ -77,8 +78,8 @@ class FeedWordPressHTML { "before_attribute" => $matches[2], "after_attribute" => $suffix, "open_tag" => $matches[1].$matches[6].$value.$matches[6].$suffix, - "content" => $matches[17], - "close_tag" => $matches[20], + "content" => ($closing ? $matches[17] : NULL), + "close_tag" => ($closing ? $matches[20] : NULL), ); } /* FeedWordPressHTML::tagWithAttributeMatch () */ diff --git a/wp-content/plugins/feedwordpress/feedwordpresshttpauthenticator.class.php b/wp-content/plugins/feedwordpress/feedwordpresshttpauthenticator.class.php index 7434f1e6250fad1b94527b670d762c81340b8891..0a1bb42dbdcf8ebf8b19530627b3f66098d88b57 100644 --- a/wp-content/plugins/feedwordpress/feedwordpresshttpauthenticator.class.php +++ b/wp-content/plugins/feedwordpress/feedwordpresshttpauthenticator.class.php @@ -2,18 +2,14 @@ class FeedWordPressHTTPAuthenticator { var $args = array(); - function FeedWordPressHTTPAuthenticator () { - self::__construct(); - } - function __construct () { - add_filter('use_curl_transport', array(&$this, 'digest_do_it'), 2, 1000); + add_filter('use_curl_transport', array($this, 'digest_do_it'), 2, 1000); foreach (array('fsockopen', 'fopen', 'streams', 'http_extension') as $transport) : - add_filter("use_{$transport}_transport", array(&$this, 'digest_dont'), 2, 1000); + add_filter("use_{$transport}_transport", array($this, 'digest_dont'), 2, 1000); endforeach; - add_filter('pre_http_request', array(&$this, 'pre_http_request'), 10, 3); - add_action('http_api_curl', array(&$this, 'set_auth_options'), 1000, 1); + add_filter('pre_http_request', array($this, 'pre_http_request'), 10, 3); + add_action('http_api_curl', array($this, 'set_auth_options'), 1000, 1); } /* FeedWordPressHTTPAuthenticator::__construct () */ function methods_available () { @@ -45,70 +41,74 @@ class FeedWordPressHTTPAuthenticator { )); // Ruh roh... - if (!is_null($this->args['authentication'])) : - switch ($this->args['authentication']) : - case '-' : - // No HTTP Auth method. Remove this stuff. - $this->args['authentication'] = NULL; - $this->args['username'] = NULL; - $this->args['password'] = NULL; + $auth = $this->args['authentication']; + if (is_null($auth) or (strlen($auth) == 0)) : + $this->args['authentication'] = '-'; + endif; + + switch ($this->args['authentication']) : + case '-' : + // No HTTP Auth method. Remove this stuff. + $this->args['authentication'] = NULL; + $this->args['username'] = NULL; + $this->args['password'] = NULL; + break; + case 'basic' : + if ($this->have_curl($args, $url)) : + // Don't need to do anything. http_api_curl hook takes care + // of it. break; - case 'basic' : - if ($this->have_curl($args, $url)) : - // Don't need to do anything. http_api_curl hook takes care - // of it. - break; - elseif ($this->have_streams($args, $url)) : - // curl has a nice native way to jam in the username and - // passwd but streams and fsockopen do not. So we have to - // make a recursive call with the credentials in the URL. - // Wee ha! - $method = $this->args['authentication']; - $credentials = $this->args['username']; - if (!is_null($this->args['password'])) : - $credentials .= ':'.$args['password']; - endif; - - // Remove these so we don't recurse all the way down - unset($this->args['authentication']); - unset($this->args['username']); - unset($this->args['password']); - - $url = preg_replace('!(https?://)!', '$1'.$credentials.'@', $url); - - // Subsidiary request - $pre = wp_remote_request($url, $this->args); - break; - endif; - case 'digest' : - if ($this->have_curl($args, $url)) : - // Don't need to do anything. http_api_curl hook takes care - // of it. - break; - endif; - default : - if (is_callable('WP_Http', '_get_first_available_transport')) : - $trans = WP_Http::_get_first_available_transport($args, $url); - if (!$trans) : - $trans = WP_Http::_get_first_available_transport(array(), $url); - endif; - elseif (is_callable('WP_Http', '_getTransport')) : - $transports = WP_Http::_getTransport($args); - $trans = get_class(reset($transports)); - else : - $trans = 'HTTP'; + elseif ($this->have_streams($args, $url)) : + // curl has a nice native way to jam in the username and + // passwd but streams and fsockopen do not. So we have to + // make a recursive call with the credentials in the URL. + // Wee ha! + $method = $this->args['authentication']; + $credentials = $this->args['username']; + if (!is_null($this->args['password'])) : + $credentials .= ':'.$args['password']; endif; - $pre = new WP_Error('http_request_failed', - sprintf( - __('%s cannot use %s authentication with the %s transport.'), - __CLASS__, - $args['authentication'], - $trans - ) - ); - endswitch; - endif; + // Remove these so we don't recurse all the way down + unset($this->args['authentication']); + unset($this->args['username']); + unset($this->args['password']); + + $url = preg_replace('!(https?://)!', '$1'.$credentials.'@', $url); + + // Subsidiary request + $pre = wp_remote_request($url, $this->args); + break; + endif; + case 'digest' : + if ($this->have_curl($args, $url)) : + // Don't need to do anything. http_api_curl hook takes care + // of it. + break; + endif; + default : + if (is_callable('WP_Http', '_get_first_available_transport')) : + $trans = WP_Http::_get_first_available_transport($args, $url); + if (!$trans) : + $trans = WP_Http::_get_first_available_transport(array(), $url); + endif; + elseif (is_callable('WP_Http', '_getTransport')) : + $transports = WP_Http::_getTransport($args); + $trans = get_class(reset($transports)); + else : + $trans = 'HTTP'; + endif; + + $pre = new WP_Error('http_request_failed', + sprintf( + __('%s cannot use %s authentication with the %s transport.'), + __CLASS__, + $args['authentication'], + $trans + ) + ); + endswitch; + return $pre; } /* FeedWordPressHTTPAuthenticator::pre_http_request () */ diff --git a/wp-content/plugins/feedwordpress/feedwordpresslocalpost.class.php b/wp-content/plugins/feedwordpress/feedwordpresslocalpost.class.php new file mode 100644 index 0000000000000000000000000000000000000000..6ff8af497a06b5392fa1a99b13bb4a690a28277a --- /dev/null +++ b/wp-content/plugins/feedwordpress/feedwordpresslocalpost.class.php @@ -0,0 +1,149 @@ +<?php + +class FeedWordPressLocalPost { + private $post; + + public function __construct ($p = NULL) { + global $post; + + if (is_null($p)) : + $this->post = $post; // current post in loop + elseif (is_object($p)) : + $this->post = $p; + else : + $this->post = get_post($p); + endif; + } + + public function id () { + return $this->post->ID; + } + + public function meta ($what, $params = array()) { + + // -=-=-= 1. INITIAL SETUP. =-=-=- + $params = wp_parse_args($params, array( + "single" => true, + "default" => NULL, + "global" => NULL, + "unproxied setting" => NULL, + "unproxy" => false, + )); + + // This is a little weird, just bear with me here. + $results = array(); + + // Has this been left up to the admin setting? + if (is_null($params['unproxy'])) : + $params['unproxy'] = FeedWordPress::use_aggregator_source_data(); + endif; + + // -=-=-= 2. GET DATA FROM THE PROXIMATE OR THE ULTIMATE SOURCE. =-=-=- + + // Now if we are supposed to look for ultimate source data (e.g. from + // <atom:source> ... </atom:source> elements), do so here. + if ($params['unproxy']) : + if (!is_string($params['unproxied setting'])) : + // Default pattern for unproxied settings: {$name}_original + $params['unproxied setting'] = $what . '_original'; + endif; + + // Now see if there's anything in postmeta from our ultimate source. + // If so, then we can cut out the middle man here. + $results = get_post_meta($this->post->ID, /*key=*/ $params['unproxied setting'], /*single=*/ false); + endif; + + // If we weren't looking for ultimate source data, or if there wasn't + // any recorded, then grab this from the data for the proximate source. + if (empty($results)) : + $results = get_post_meta($this->post->ID, /*key=*/ $what, /*single=*/ false); + endif; + + // -=-=-= 3. DEAL WITH THE RESULTS, IF ANY, OR FALLBACK VALUES. =-=-=- + + // If we have results now, cool. Just pass them back. + if (!empty($results)) : + $ret = ($params['single'] ? $results[0] : $results); + + // If we got no results but we have a fallback global setting, cool. Use + // that. Jam it into a singleton array for queries expecting an array of + // results instead of a scalar result. + elseif (is_string($params['global']) and strlen($params['global']) > 0) : + $opt = get_option($params['global'], $params['default']); + $ret = ($params['single'] ? $opt : array($opt)); + + // If we got no results and we have no fallback global setting, pass + // back a default value for single-result queries, or an empty array for + // multiple-result queries. + else : + $ret = ($params['single'] ? $params['default'] : array()); + endif; + + return $ret; + } + + public function is_syndicated () { + return (!is_null($this->feed_id(/*single=*/ false))); + } + + public function syndication_permalink () { + return $this->meta('syndication_permalink'); + } + + public function feed () { + return $GLOBALS['feedwordpress']->subscription($this->feed_id()); + } + + public function feed_id () { + return $this->meta('syndication_feed_id'); + } + + public function syndication_feed ($original = NULL) { + return $this->meta('syndication_feed', array("unproxy" => $original)); + } + + public function syndication_feed_guid ($original = NULL) { + $ret = $this->meta('syndication_source_id', array("unproxy" => $original)); + + // If this is blank, fall back to the full URL of the feed + if (is_null($ret) or strlen(trim($ret))==0) : + $ret = get_syndication_feed(); + endif; + + return $ret; + } + + public function syndication_source ($original = NULL) { + $ret = $this->meta('syndication_source', array("unproxy" => $original)); + + // If this is blank, fall back to a prettified URL for the blog. + if (is_null($ret) or strlen(trim($ret)) == 0) : + $ret = feedwordpress_display_url($this->syndication_source_link()); + endif; + + return $ret; + } + + public function syndication_source_link ($original = NULL) { + return $this->meta('syndication_source_uri', array("unproxy" => $original)); + } + + public function is_exposed_to_formatting_filters () { + + return ( + !$this->is_syndicated() + or ( + 'yes' == $this->meta( + '_feedwordpress_formatting_filters', + array( + 'global' => 'feedwordpress_formatting_filters', + 'default' => 'no', + ) + ) + ) + ); + + } /* FeedWordPressLocalPost::is_exposed_to_formatting_filters () */ + +} /* class FeedWordPressLocalPost */ + diff --git a/wp-content/plugins/feedwordpress/feedwordpressparsedpostmeta.class.php b/wp-content/plugins/feedwordpress/feedwordpressparsedpostmeta.class.php new file mode 100644 index 0000000000000000000000000000000000000000..c3c909926711b7ff03c5377107367073d5c18c15 --- /dev/null +++ b/wp-content/plugins/feedwordpress/feedwordpressparsedpostmeta.class.php @@ -0,0 +1,308 @@ +<?php +class FeedWordPressParsedPostMeta { + var $s, $ptr, $ptrEOS; + var $delims, $delimCount, $delimPtr; + var $paren; + + var $parsed = NULL; + + function __construct ($s) { + $this->s = $s; + $this->reset(); + } + + function reset () { + $this->parsed = NULL; + + $this->ptr = 0; + $this->paren = 0; + $this->ptrEOS = strlen($this->s); + + $this->delimCount['CDATA'] = preg_match_all('/\$/', $this->s, $this->delims['CDATA'], PREG_OFFSET_CAPTURE); + $this->delimPtr['CDATA'] = 0; + + $this->delimCount['EXPR'] = preg_match_all('/[()\s]/', $this->s, $this->delims['EXPR'], PREG_OFFSET_CAPTURE); + $this->delimPtr['EXPR'] = 0; + } + + function look () { return $this->s[$this->ptr]; } + function drop () { $this->anchor = $this->ptr; } + function take () { return substr($this->s, $this->anchor, ($this->ptr - $this->anchor)); } + function EOS () { return ($this->ptr >= $this->ptrEOS); } + + function nextDelim ($which) { + $N = $this->delimCount[$which]; $ptr = $this->delimPtr[$which]; + + $next = -1; + while ( ($ptr < $N) and ($next < $this->ptr) ) : + $next = $this->delims[$which][0][$ptr][1]; + $ptr++; + endwhile; + $this->delimPtr[$which] = $ptr; + + if ($next < $this->ptr) : + $next = $this->ptrEOS; + endif; + return $next; + } + + function substitute_terms ($post, $piece, $values = NULL) { + $terms = array(); + if ('EXPR'==$piece[0]) : + // Parameter stored in $piece[1] + $param = $piece[1]; + if (is_string($param)) : + if (!isset($values[$param])) : + $values[$param] = $post->query($param); + endif; + $term = $param; + $results = $values[$param]; + else : + list($results, $term, $values) = $this->substitute_terms($post, $piece[1], $values); + endif; + + // Filtering function, if any, stored in $piece[2] + if (isset($piece[2])) : + $filter = $post->substitution_function(trim(strtolower($piece[2]))); + if (!is_null($filter)) : + foreach ($results as $key => $result) : + $results[$key] = $filter($result); + endforeach; + else : + $results = array( + "[[ERR: No such function (".$piece[2].")]]", + ); + endif; + endif; + + elseif ('CDATA'==$piece[0]) : + // Literal string stored in $piece[1] + $results = array($piece[1]); + $term = NULL; + endif; + return array($results, $term, $values); + } + + function do_substitutions ($post, $in = NULL, $scratchpad = NULL) { + if (is_null($in)) : + $in = $this->get(); + endif; + + if (count($in) > 0) : + $out = array(); + + // Init. results set if not already initialized. + if (is_null($scratchpad)) : + $scratchpad = array(array('', array())); + endif; + + // Grab the first + $piece = array_shift($in); + + foreach ($scratchpad as $key => $scratch) : + $line = $scratch[0]; + $element_values = $scratch[1]; + + switch ($piece[0]) : + case 'CDATA' : + $subs = array($piece[1]); + $term = NULL; + break; + case 'EXPR' : + list($subs, $term, $element_values) = $this->substitute_terms($post, $piece, $element_values); + break; + endswitch; + + $constrained_values = $element_values; + foreach ($subs as $sub) : + + if (isset($term)) : + $constrained_values[$term] = array($sub); + endif; + + $out[] = array($line . $sub, $constrained_values); + endforeach; + + endforeach; + + if (count($in) > 0) : + $out = $this->do_substitutions($post, $in, $out); + endif; + else : + $out = NULL; + endif; + + // Now that we are done, strip out the namespace elements. + if (is_array($out)) : + foreach ($out as $idx => $line) : + if (is_array($line)) : + $out[$idx] = $line[0]; + endif; + endforeach; + endif; + + return $out; + } + + function get ($idx = NULL) { + $ret = $this->parse(); + if ($idx) : $ret = $ret[$idx]; endif; + + return $ret; + } + + function parse () { + if (is_null($this->parsed)) : + $out = array(); + + $this->reset(); + while (!$this->EOS()) : + switch ($this->look()) : + case '$' : + $this->ptr++; + $out[] = $this->EXPR(); + break; + default : + $out[] = $this->CDATA(); + endswitch; + endwhile; + + $this->parsed = $out; + endif; + return $this->parsed; + } + + function CDATA () { + $this->drop(); + $this->ptr = $this->nextDelim('CDATA'); + return array(__FUNCTION__, $this->take()); + } /* FeedWordPressParsedPostMeta::CDATA() */ + + function EXPR () { + $ret = array(__FUNCTION__); + $paren0 = $this->paren; + $this->drop(); + + $ptr0 = $this->ptr; + + $complete = false; + while (!$this->EOS() and !$complete) : + $tok = $this->look(); + switch ($tok) : + case '(' : + + $fun = $this->take(); + + // We're at the open paren; skip ahead past that. + $this->ptr++; + + // And indent us one level in. + $this->paren++; + + $delta = $this->EXPR(); + if (isset($delta[2])) : + $ret[1] = $delta; + else : + $ret[1] = $delta[1]; + endif; + + if (strlen(trim($fun)) > 0) : + $ret[2] = trim($fun); + endif; + + // We should be stopped on either a close paren or on EOS. + $this->ptr++; + + // A top level expression terminates + // immediately with the closing of its + // parens. Lower-level expressions may + // have whitespace wrapper, etc. + $complete = ($this->paren <= 0); + + $this->drop(); + break; + + case ')' : + + if ($this->paren > 0) : + $this->paren--; + + $complete = ($this->paren <= $paren0); + + $fun = $this->take(); + if (strlen(trim($fun)) > 0) : + $ret[1] = trim($fun); + endif; + + $this->drop(); + else : + $this->ptr++; + endif; + break; + + case '$' : + if ($ptr0 == $this->ptr) : + // This is an escaped literal $ + $this->ptr++; + $ret = array('CDATA', $tok); + $this->drop(); + $complete = true; + break; + endif; + + default : + if (ctype_space($tok) and ($this->paren <= 0)) : + // A loose $ should be treated as a literal $ + if ($ptr0 == $this->ptr) : + $ret = array('CDATA', '$'); + $this->drop(); + endif; + $complete = true; + else : + $next = $this->nextDelim('EXPR'); + + // Skip ahead to the next potentially interesting character. + if (!is_null($next)) : + $this->ptr = $next; + else : + $this->ptr = $this->ptrEOS; + endif; + + endif; + endswitch; + endwhile; + + $var = ''; + if ($this->anchor < $this->ptr) : + $var = trim($this->take()); + endif; + if (strlen($var) > 0) : + $ret[1] = $var; + endif; + return $ret; + } /* FeedWordPressParsedPostMeta::EXPR () */ +} /* class FeedWordPressParsedPostMeta */ + +if (basename($_SERVER['SCRIPT_FILENAME'])==basename(__FILE__)) : + $argv = $_SERVER['argv']; + array_shift($argv); + $N = reset($argv); + if (is_numeric($N)) : + array_shift($argv); + else : + $N = 1; + endif; + + $t0 = microtime(/*float=*/ true); + for ($i = 0; $i < $N; $i++) : + $parse = new FeedWordPressParsedPostMeta(implode(" ", $argv)); + $voo = ($parse->parse()); + unset($parse); + endfor; + $t1 = microtime(/*float=*/ true); + + echo "RESULT: "; var_dump($voo); + echo "ELAPSED TIME: "; print number_format(1000.0 * ($t1 - $t0)) . "ms\n"; + echo "CONSUMED MEMORY: "; print number_format(memory_get_peak_usage() / 1024) . "KB\n"; +endif; + diff --git a/wp-content/plugins/feedwordpress/feedwordpressrpc.class.php b/wp-content/plugins/feedwordpress/feedwordpressrpc.class.php index ead601c558a73075fe0b31037beb2477fbe0ff56..040e74aa8113f1920b63a1190062e51fdb5d1c2c 100644 --- a/wp-content/plugins/feedwordpress/feedwordpressrpc.class.php +++ b/wp-content/plugins/feedwordpress/feedwordpressrpc.class.php @@ -5,15 +5,15 @@ class FeedWordPressRPC { function FeedWordPressRPC () { - add_filter('xmlrpc_methods', array(&$this, 'xmlrpc_methods')); + add_filter('xmlrpc_methods', array($this, 'xmlrpc_methods')); } function xmlrpc_methods ($args = array()) { - $args['weblogUpdates.ping'] = array(&$this, 'ping'); - $args['feedwordpress.subscribe'] = array(&$this, 'subscribe'); - $args['feedwordpress.deactivate'] = array(&$this, 'deactivate'); - $args['feedwordpress.delete'] = array(&$this, 'delete'); - $args['feedwordpress.nuke'] = array(&$this, 'nuke'); + $args['weblogUpdates.ping'] = array($this, 'ping'); + $args['feedwordpress.subscribe'] = array($this, 'subscribe'); + $args['feedwordpress.deactivate'] = array($this, 'deactivate'); + $args['feedwordpress.delete'] = array($this, 'delete'); + $args['feedwordpress.nuke'] = array($this, 'nuke'); return $args; } diff --git a/wp-content/plugins/feedwordpress/feedwordpresssyndicationpage.class.php b/wp-content/plugins/feedwordpress/feedwordpresssyndicationpage.class.php index 3a08b094258922e7cbbfdbdc93d2c5111cb82bed..e60e704e6d655612aa4087220c5842662142598e 100644 --- a/wp-content/plugins/feedwordpress/feedwordpresssyndicationpage.class.php +++ b/wp-content/plugins/feedwordpress/feedwordpresssyndicationpage.class.php @@ -82,8 +82,8 @@ class FeedWordPressSyndicationPage extends FeedWordPressAdminPage { $update_set = array(); if ($fwp_update_invoke != 'get') : - if (is_array(FeedWordPress::post('link_ids')) - and (FeedWordPress::post('action')==FWP_UPDATE_CHECKED)) : + if (is_array(MyPHP::post('link_ids')) + and (MyPHP::post('action')==FWP_UPDATE_CHECKED)) : $targets = $wpdb->get_results(" SELECT * FROM $wpdb->links WHERE link_id IN (".implode(",",$_POST['link_ids']).") @@ -95,8 +95,8 @@ class FeedWordPressSyndicationPage extends FeedWordPressAdminPage { else : // This should never happen FeedWordPress::critical_bug('fwp_syndication_manage_page::targets', $targets, __LINE__, __FILE__); endif; - elseif (!is_null(FeedWordPress::post('update_uri'))) : - $targets = FeedWordPress::post('update_uri'); + elseif (!is_null(MyPHP::post('update_uri'))) : + $targets = MyPHP::post('update_uri'); if (!is_array($targets)) : $targets = array($targets); endif; @@ -340,8 +340,6 @@ class FeedWordPressSyndicationPage extends FeedWordPressAdminPage { return; endif; - ?> - <?php $cont = true; $dispatcher = array( "feedfinder" => 'fwp_feedfinder_page', @@ -352,8 +350,10 @@ class FeedWordPressSyndicationPage extends FeedWordPressAdminPage { 'Unsubscribe' => 'multidelete_page', FWP_RESUB_CHECKED => 'multiundelete_page', ); - if (isset($_REQUEST['action']) and isset($dispatcher[$_REQUEST['action']])) : - $method = $dispatcher[$_REQUEST['action']]; + + $act = MyPHP::request('action'); + if (isset($dispatcher[$act])) : + $method = $dispatcher[$act]; if (method_exists($this, $method)) : $cont = $this->{$method}(); else : @@ -384,7 +384,7 @@ class FeedWordPressSyndicationPage extends FeedWordPressAdminPage { add_meta_box( /*id=*/ 'feedwordpress_feeds_box', /*title=*/ __('Syndicated sources'), - /*callback=*/ array(&$this, 'syndicated_sources_box'), + /*callback=*/ array($this, 'syndicated_sources_box'), /*page=*/ $this->meta_box_context(), /*context =*/ $this->meta_box_context() ); @@ -431,10 +431,11 @@ class FeedWordPressSyndicationPage extends FeedWordPressAdminPage { // Hey ho, let's go... ?> + <div style="float: left; background: #F5F5F5; padding-top: 5px; padding-right: 5px;"><a href="<?php print $this->form_action(); ?>"><img src="<?php print esc_html(WP_PLUGIN_URL.'/'.$GLOBALS['fwp_path'].'/feedwordpress.png'); ?>" alt="" /></a></div> + <p class="info" style="margin-bottom: 0px; border-bottom: 1px dotted black;">Managed by <a href="http://feedwordpress.radgeek.com/">FeedWordPress</a> <?php print FEEDWORDPRESS_VERSION; ?>.</p> <?php if (FEEDWORDPRESS_BLEG) : ?> - <div style="float: left; background: white; margin-top: 5px; margin-right: 5px;"><a href="<?php print $this->form_action(); ?>"><img src="<?php print esc_html(WP_PLUGIN_URL.'/'.$GLOBALS['fwp_path'].'/feedwordpress.png'); ?>" alt="" /></a></div> <p class="info" style="margin-top: 0px; font-style: italic; font-size: 75%; color: #666;">If you find this tool useful for your daily work, you can contribute to ongoing support and development with <a href="http://feedwordpress.radgeek.com/donate/">a modest donation</a>.</p> @@ -488,8 +489,8 @@ class FeedWordPressSyndicationPage extends FeedWordPressAdminPage { value="Source URL" style="width: 55%;" /></label> <?php FeedWordPressSettingsUI::magic_input_tip_js('add-uri'); ?> - - <input style="vertical-align: middle;" type="image" src="<?php print WP_PLUGIN_URL.'/'.$fwp_path; ?>/plus.png" alt="<?php print FWP_SYNDICATE_NEW; ?>" name="action" value="<?php print FWP_SYNDICATE_NEW; ?>" /></div> + <input type="hidden" name="action" value="<?php print FWP_SYNDICATE_NEW; ?>" /> + <input style="vertical-align: middle;" type="image" src="<?php print WP_PLUGIN_URL.'/'.$fwp_path; ?>/plus.png" alt="<?php print FWP_SYNDICATE_NEW; ?>" /></div> </form> </div> <!-- id="add-single-uri" --> @@ -733,16 +734,12 @@ regular donation</a>) using an existing PayPal account or any major credit card. // ... and kill them all if (count($post_ids) > 0) : foreach ($post_ids as $post_id) : - if (FeedWordPressCompatibility::test_version(FWP_SCHEMA_29)) : - // Force scrubbing of deleted post - // rather than sending to Trashcan - wp_delete_post( - /*postid=*/ $post_id, - /*force_delete=*/ true - ); - else : - wp_delete_post($post_id); - endif; + // Force scrubbing of deleted post + // rather than sending to Trashcan + wp_delete_post( + /*postid=*/ $post_id, + /*force_delete=*/ true + ); endforeach; endif; @@ -1144,14 +1141,14 @@ updated to <<a href="<?php echo esc_html($fwp_post['feed']); ?>"><?php echo e endif; if (isset($existingLink)) : - $auth = FeedWordPress::post('link_rss_auth_method'); - if (!is_null($auth) and $auth != '-') : + $auth = MyPHP::post('link_rss_auth_method'); + if (!is_null($auth) and (strlen($auth) > 0) and ($auth != '-')) : $existingLink->update_setting('http auth method', $auth); $existingLink->update_setting('http username', - FeedWordPress::post('link_rss_username') + MyPHP::post('link_rss_username') ); $existingLink->update_setting('http password', - FeedWordPress::post('link_rss_password') + MyPHP::post('link_rss_password') ); else : $existingLink->update_setting('http auth method', NULL); diff --git a/wp-content/plugins/feedwordpress/magpiefromsimplepie.class.php b/wp-content/plugins/feedwordpress/magpiefromsimplepie.class.php index f20041bda80aa63bc2743fdbd57739db3330bb7c..40c8d7f251a183161af73b139323962bdbcc86d3 100644 --- a/wp-content/plugins/feedwordpress/magpiefromsimplepie.class.php +++ b/wp-content/plugins/feedwordpress/magpiefromsimplepie.class.php @@ -155,7 +155,8 @@ class MagpieFromSimplePie { endforeach; endforeach; endforeach; endif; - return $ret; + + return apply_filters('feedwordpress_magpiefromsimplepie_processfeeddata', $ret, $data, $this); } /* MagpieFromSimplePie::processFeedData() */ /** @@ -183,7 +184,7 @@ class MagpieFromSimplePie { + $this->handleChildren($data, $path, 'processChannelData') + $ret; - return $ret; + return apply_filters('feedwordpress_magpiefromsimplepie_processchanneldata', $ret, $data, $path, $this); } /* MagpieFromSimplePie::processChannelData() */ /** @@ -213,7 +214,7 @@ class MagpieFromSimplePie { + $this->handleChildren($data, $path, 'processItemData') + $ret; - return $ret; + return apply_filters('feedwordpress_magpiefromsimplepie_processitemdata', $ret, $data, $path, $this); } /* MagpieFromSimplePie::processItemData() */ /** @@ -250,7 +251,8 @@ class MagpieFromSimplePie { endif; endforeach; endforeach; endif; - return $ret; + + return apply_filters('feedwordpress_magpiefromsimplepie_handleattributes', $ret, $data, $path, $this); } /* MagpieFromSimplePie::handleAttributes() */ var $inImage = false; @@ -346,7 +348,8 @@ class MagpieFromSimplePie { endforeach; endforeach; endforeach; endif; - return $ret; + + return apply_filters('feedwordpress_magpiefromsimplepie_handlechildren', $ret, $data, $path, $method, $this); } /* MagpieFromSimplePie::handleChildren() */ /** @@ -386,6 +389,8 @@ class MagpieFromSimplePie { * @uses FeedTime::timestamp */ function normalize () { + do_action('feedwordpress_magpiefromsimplepie_normalize_pre', $this); + if (!is_null($this->channel)) : // Normalize channel data if ( $this->is_atom() ) : @@ -497,6 +502,8 @@ class MagpieFromSimplePie { $this->items[$i] = $item; endfor; endif; + + do_action('feedwordpress_magpiefromsimplepie_normalize_post', $this); } /* MagpieFromSimplePie::normalize() */ /** diff --git a/wp-content/plugins/feedwordpress/performance-page.php b/wp-content/plugins/feedwordpress/performance-page.php index 0762318f3359af73a99a963a4960d2ef7e235f3b..e1928d1fc6472cd2b521f88c8b616c3f9c645208 100644 --- a/wp-content/plugins/feedwordpress/performance-page.php +++ b/wp-content/plugins/feedwordpress/performance-page.php @@ -79,9 +79,19 @@ class FeedWordPressPerformancePage extends FeedWordPressAdminPage { $feeds = (($N == 1) ? __("feed") : __("feeds")); $this->updated = sprintf(__("Cleared %d cached %s from WordPress database."), $N, $feeds); endif; + + if (isset($post['optimize_in'])) : + update_option('feedwordpress_optimize_in_clauses', true); + $this->updated = sprintf(__("Enabled optimizing inefficient IN clauses in SQL queries.")); + elseif (isset($post['optimize_out'])) : + update_option('feedwordpress_optimize_in_clauses', false); + $this->updated = sprintf(__("Disabled optimizing inefficient IN clauses in SQL queries.")); + endif; } /* FeedWordPressPerformancePage::accept_POST () */ /*static*/ function performance_box ($page, $box = NULL) { + $optimize_in = get_option('feedwordpress_optimize_in_clauses', false); + // Hey ho, let's go... ?> <table class="editform" width="100%" cellspacing="2" cellpadding="5"> @@ -106,6 +116,34 @@ table. If you'd like to remove the index for any reason, you can do so here.</p> <?php endif; ?> +<tr style="vertical-align: top"> +<th width="33%" scope="row">Optimize IN clauses:</th> +<td width="67%"><?php if (!$optimize_in) : ?> +<input class="button" type="submit" name="optimize_in" value="Optimize inefficient IN clauses in SQL queries" /> + +<p><strong>Advanced setting.</strong> As of releases up to 3.3.2, WordPress +still generates many SQL queries with an extremely inefficient use of the IN +operator (for example, <code>SELECT user_id, meta_key, meta_value FROM +wp_usermeta WHERE user_id IN (1)</code>). When there is only one item in the +set, the IN operator is unnecessary; and inefficient, because it prevents SQL +from making use of indexes on the table being queried. Activating this setting +will cause these queries to get rewritten to use a simple equality operator when +there is only one item in the set (for example, the example query above would be +rewritten as <code>SELECT user_id, meta_key, meta_value FROM wp_usermeta WHERE +user_id = 1</code>).</p> + +<p><strong>Note.</strong> This is an advanced setting, which affects WordPress's +database queries at a very low level. The change should be harmless, but +proceed with caution, and only if you are confident in your ability to restore +your WordPress installation from backups if something important should stop +working.</p> + +<?php else : ?> +<input class="button" type="submit" name="optimize_out" value="Disable optimizing inefficient IN clauses" /> +<p>You can use this setting to disable any attempts by FeedWordPress to optimize +or rewrite WordPress's SQL queries.</p> +<?php endif; ?></td> +</tr> </td> </tr> diff --git a/wp-content/plugins/feedwordpress/posts-page.php b/wp-content/plugins/feedwordpress/posts-page.php index 4d0bf9e7d93cab037819c6d9177a34cf00882691..c7e09ccfd208119db2f58de7063e43cfa867f3b0 100644 --- a/wp-content/plugins/feedwordpress/posts-page.php +++ b/wp-content/plugins/feedwordpress/posts-page.php @@ -133,7 +133,6 @@ class FeedWordPressPostsPage extends FeedWordPressAdminPage { * @uses FeedWordPressPostsPage::these_posts_phrase() * @uses FeedWordPress::syndicated_status() * @uses SyndicatedLink::syndicated_status() - * @uses SyndicatedPost::use_api() */ /*static*/ function publication_box ($page, $box = NULL) { $thesePosts = $page->these_posts_phrase(); @@ -419,12 +418,16 @@ class FeedWordPressPostsPage extends FeedWordPressAdminPage { <?php $i = 0; - foreach ($custom_settings as $key => $value) : + $testerButton = '<br/><button id="xpath-test-%d"' + .'class="xpath-test"' + .'>test expression</button>'; + foreach ($custom_settings as $key => $value) : ?> <tr style="vertical-align:top"> <th width="30%" scope="row"><input type="hidden" name="notes[<?php echo $i; ?>][key0]" value="<?php echo esc_html($key); ?>" /> <input id="notes-<?php echo $i; ?>-key" name="notes[<?php echo $i; ?>][key1]" value="<?php echo esc_html($key); ?>" /></th> - <td width="60%"><textarea rows="2" cols="40" id="notes-<?php echo $i; ?>-value" name="notes[<?php echo $i; ?>][value]"><?php echo esc_html($value); ?></textarea></td> + <td width="60%"><textarea rows="2" cols="40" id="notes-<?php echo $i; ?>-value" name="notes[<?php echo $i; ?>][value]"><?php echo esc_html($value); ?></textarea> + <?php print sprintf($testerButton, $i); ?></td> <td width="10%"><select name="notes[<?php echo $i; ?>][action]"> <option value="update">save changes</option> <option value="delete">delete this setting</option> @@ -438,7 +441,7 @@ class FeedWordPressPostsPage extends FeedWordPressAdminPage { <tr style="vertical-align: top"> <th scope="row"><input type="text" size="10" name="notes[<?php echo $i; ?>][key1]" value="" /></th> - <td><textarea name="notes[<?php echo $i; ?>][value]" rows="2" cols="40"></textarea> + <td><textarea name="notes[<?php echo $i; ?>][value]" rows="2" cols="40"></textarea><?php print sprintf($testerButton, $i); ?> <p>Enter a text value, or a path to a data element from the syndicated item.<br/> For data elements, you can use an XPath-like syntax wrapped in <code>$( ... )</code>.<br/> <code>hello</code> = the text value <code><span style="background-color: #30FFA0;">hello</span></code><br/> diff --git a/wp-content/plugins/feedwordpress/readme.txt b/wp-content/plugins/feedwordpress/readme.txt index d4bb3f2b635754a3c01a6d21cafb4780f5524644..e711e099617e5d9254f93a49af40f8a749d4b431 100644 --- a/wp-content/plugins/feedwordpress/readme.txt +++ b/wp-content/plugins/feedwordpress/readme.txt @@ -3,15 +3,15 @@ Contributors: Charles Johnson Donate link: http://feedwordpress.radgeek.com/ Tags: syndication, aggregation, feed, atom, rss Requires at least: 3.0 -Tested up to: 3.2.1 -Stable tag: 2011.1019 +Tested up to: 3.5.1 +Stable tag: 2013.0503 FeedWordPress syndicates content from feeds you choose into your WordPress weblog. == Description == * Author: [Charles Johnson](http://radgeek.com/contact) -* Project URI: <http://projects.radgeek.com/feedwordpress> +* Project URI: <http://feedwordpress.radgeek.com/> * License: GPL 2. See License below for copyright jots and tittles. FeedWordPress is an Atom/RSS aggregator for WordPress. It syndicates content @@ -94,6 +94,120 @@ outs, see the documentation at the [FeedWordPress project homepage][]. == Changelog == += 2012.0503 = + +* BUGFIX: Works properly again with WordPress installations that use a + MySQL table name prefix other than the default `wp_` prefix. + +* BUGFIX: Includes a couple of significant PHP 5.4 compatibility fixes. + Now that PHP 5.4 is more widely deployed, Diagnostics will now also + show PHP version and some other potentially useful troubleshooting + information. + +* ADMIN UI: Better indicates your options when deleting a syndicated post + so as to let you know whether it will be Trashed (and thus not + resyndicated) or Erased (and thus potentially resyndicated) + +* ADMIN UI: Adds an AJAXy Test Expression button to allow live testing of + sample results from expressions in Custom Post Settings. + += 2012.1218 = + +* WORDPRESS VISUAL EDITOR FIXED. There was an unlisted change in the + 2012.1212 release which had the effect of disabling the WordPress Visual + Editor for all posts syndicated by FeedWordPress. Many users reported + this as a bug. It was actually a deliberate decision -- a crappy way to + try to deal with a crappy situation. (Many users had previously reported + a "bug" in which all the paragraph or line breaks seemed to be stripped + out of their syndicated posts; the issue turned out to be that the + Visual Editor was stripping out `<p>` and `<br/>` tags on the assumption + that the resulting post would be sent through standard WordPress + formatting filters. But under default settings, posts syndicated by FWP + deliberately bypass WordPress formatting filters.) In any case, this + version adopts a more flexible compromise. *If* FeedWordPress is set up + to bypass WordPress formatting filters (as it is by default), *then* + the Visual Editor will be disabled for syndicated posts (since using it + would produce incorrect results). If on the other hand FeedWordPress is + set up to expose syndicated posts to WordPress formatting filters (as it + usually is for those using the Visual Editor to manually edit posts), + then the Visual Editor tab will be re-enabled for syndicated posts. + +* BUG FIX: PERMALINKS REWRITTEN FOR CUSTOM POST TYPES AS WELL AS NORMAL + WORDPRESS POSTS. If you had WordPress set up to syndicate incoming posts + to a custom post type (under Syndication > Posts & Links), and asked + FeedWordPress to make "permalinks point to the original site", then + previous versions of FeedWordPress would fail to do the rewriting -- + permalinks would only be rewritten to point to the original source for + normal WordPress posts, not for custom post types. In 2012.1218 this bug + has been fixed: all post types will now have permalinks rewritten unless + you request for permalinks to point to the local copy on your aggregator + site. + +* BUG FIX: ELIMINATES "PHP Fatal error: Call to a member function + setting() on a non-object...." Some changes to the in-memory caching of + information about feed subscriptions could result in a fatal PHP error + in cases where you have de-activated one of your subscriptions, but + posts from that subscription were still in the archive. This would + normally show up through half-completed feeds or half-completed pages + that suddenly broke off in the middle, and displayed or logged an error + message like: "PHP Fatal error: Call to a member function setting() on a + non-object in {...}/wp-content/plugins/feedwordpress/feedwordpress.php + on line 615". This bug has been eliminated, so affected feeds and pages + should now render correctly, and the error message should no longer + appear. + +* BUG FIX: CATEGORY BOXES IN SYNDICATION > CATEGORIES & TAGS. Some minor + bugs in the appearance and animation of category checkboxes (for + example, the checkbox used to select categories for syndicated posts on + the Syndication > Categories & Tags settings page) have been fixed. + += 2012.1212 = + +* WORDPRESS 3.5 COMPATIBILITY: This release has been tested for compatibility + with new releases of WordPress, up to version 3.5, and any documented + compatibility issues have been cleared -- in particular, if you were seeing + error pages stating that you don't have permission to access the + FeedWordPress Syndication page within the WordPress admin interface, then + upgrading to this release should fix the problem. + + As always, if you encounter any compatibility problems after upgrading your + version of WordPress and your version of FeedWordPress to the most recent + versions, please contact me with as detailed a description as possible of + the issue you are encountering, the circumstances you're encountering it + under, what you expect to see happening, and what is happening instead. + +* PHP 5.4 COMPATIBILITY: This release has been audited to fix potential + problems with deprecation notices or fatal errors under recent versions + of PHP. In particular, all uses of run-time pass-by-reference have been + eliminated from the code; if you were seeing a fatal error reading + "Call-time pass-by-reference has been removed ..." then upgrading to + this release should fix the problem. + +* CUSTOMIZATION FRAMEWORK: A great deal of work has been done to make the + underlying framework more flexible, so that PHP add-ons can be written + to adapt FeedWordPress to handle custom XML vocabularies, expiration of + posts under specified conditions, and other custom behavior. + +* BUGFIX: MANUALLY EDITED POST SLUGS NOT OVERWRITTEN. Thanks to a report + by Chris Fritz, I've identified some code that causes post slugs for the + posts generated by FWP to be rewritten with every update, even if the + user has manually updated the slug from within the WordPress editing + interface. This has been fixed: FWP will continue to generate new slugs + for syndicated posts, but when syndicated posts are updated, they will + retain the slug that they had at the time of the update; any manual + changes to the post slug should be preserved. + +* USER-AGENT STRING: FeedWordPress now sends a distinctive User-Agent + string identifying itself, and noting that it is a feed aggregator. + +* MISCELLANEOUS PERFORMANCE IMPROVEMENTS: A number of changes have been + made to try to reduce the intensity and expense in terms of both + database performance and web server memory consumption. + +* DIAGNOSTICS IMPROVEMENTS: A number of new and improved diagnostics have + been added which should aid in understanding and troubleshooting issues + that may arise. + = 2011.1019 = * BUGFIX: "THERE ARE NO HTTP TRANSPORTS AVAILABLE" ERROR FIXED: The initial @@ -1842,7 +1956,8 @@ The FeedWordPress plugin is copyright © 2005-2010 by Charles Johnson. It uses code derived or translated from: - [wp-rss-aggregate.php][] by [Kellan Elliot-McCrea](kellan@protest.net) -- [MagpieRSS][] by [Kellan Elliot-McCrea](kellan@protest.net) +- [SimplePie][] feed parser by Ryan Parman, Geoffrey Sneddon, Ryan McCue, et al. +- [MagpieRSS][] feed parser by [Kellan Elliot-McCrea](kellan@protest.net) - [Ultra-Liberal Feed Finder][] by [Mark Pilgrim](mark@diveintomark.org) - [WordPress Blog Tool and Publishing Platform](http://wordpress.org/) @@ -1858,8 +1973,8 @@ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. [wp-rss-aggregate.php]: http://laughingmeme.org/archives/002203.html + [SimplePie]: http://www.simplepie.org/ [MagpieRSS]: http://magpierss.sourceforge.net/ - [HTTP Navigator 2]: http://www.keyvan.net/2004/11/16/http-navigator/ [Ultra-Liberal Feed Finder]: http://diveintomark.org/projects/feed_finder/ [GNU General Public License]: http://www.gnu.org/copyleft/gpl.html diff --git a/wp-content/plugins/feedwordpress/syndicatedlink.class.php b/wp-content/plugins/feedwordpress/syndicatedlink.class.php index 93b809c428bcee2a06604b2e5bdae28990f449df..0e8904044cd85226bc4f52536ec3cb69d85fa45a 100644 --- a/wp-content/plugins/feedwordpress/syndicatedlink.class.php +++ b/wp-content/plugins/feedwordpress/syndicatedlink.class.php @@ -34,6 +34,7 @@ # the function `get_feed_meta($key)` if this plugin is activated. require_once(dirname(__FILE__).'/magpiefromsimplepie.class.php'); +require_once(dirname(__FILE__).'/feedwordpressparsedpostmeta.class.php'); class SyndicatedLink { var $id = null; @@ -49,121 +50,25 @@ class SyndicatedLink { $this->link = $link; $this->id = $link->link_id; else : - $this->id = $link; - if (function_exists('get_bookmark')) : // WP 2.1+ - $this->link = get_bookmark($link); - else : - $this->link = $wpdb->get_row(" - SELECT * FROM $wpdb->links - WHERE (link_id = '".$wpdb->escape($link)."')" - ); - endif; + $this->id = $link; + $this->link = get_bookmark($link); endif; if (strlen($this->link->link_rss) > 0) : - // Read off feed settings from link_notes - $notes = explode("\n", $this->link->link_notes); - foreach ($notes as $note): - $pair = explode(": ", $note, 2); - $key = (isset($pair[0]) ? $pair[0] : null); - $value = (isset($pair[1]) ? $pair[1] : null); - if (!is_null($key) and !is_null($value)) : - // Unescape and trim() off the whitespace. - // Thanks to Ray Lischner for pointing out the - // need to trim off whitespace. - $this->settings[$key] = stripcslashes (trim($value)); - endif; - endforeach; - - // "Magic" feed settings - $this->settings['link/uri'] = $this->link->link_rss; - $this->settings['link/name'] = $this->link->link_name; - $this->settings['link/id'] = $this->link->link_id; - - // `hardcode categories` and `unfamiliar categories` are deprecated in favor of `unfamiliar category` - if ( - isset($this->settings['unfamiliar categories']) - and !isset($this->settings['unfamiliar category']) - ) : - $this->settings['unfamiliar category'] = $this->settings['unfamiliar categories']; - endif; - if ( - FeedWordPress::affirmative($this->settings, 'hardcode categories') - and !isset($this->settings['unfamiliar category']) - ) : - $this->settings['unfamiliar category'] = 'default'; - endif; - - // Set this up automagically for del.icio.us - $bits = parse_url($this->link->link_rss); - $tagspacers = array('del.icio.us', 'feeds.delicious.com'); - if (!isset($this->settings['cat_split']) and in_array($bits['host'], $tagspacers)) : - $this->settings['cat_split'] = '\s'; // Whitespace separates multiple tags in del.icio.us RSS feeds - endif; - - // Simple lists - foreach ($this->imploded_settings() as $what) : - if (isset($this->settings[$what])): - $this->settings[$what] = explode( - FEEDWORDPRESS_CAT_SEPARATOR, - $this->settings[$what] - ); - endif; - endforeach; - - if (isset($this->settings['terms'])) : - // Look for new format - $this->settings['terms'] = maybe_unserialize($this->settings['terms']); - - if (!is_array($this->settings['terms'])) : - // Deal with old format instead. Ugh. - - // Split on two *or more* consecutive breaks - // because in the old format, a taxonomy - // without any associated terms would - // produce tax_name#1\n\n\ntax_name#2\nterm, - // and the naive split on the first \n\n - // would screw up the tax_name#2 list. - // - // Props to David Morris for pointing this - // out. - - $this->settings['terms'] = preg_split( - "/".FEEDWORDPRESS_CAT_SEPARATOR."{2,}/", - $this->settings['terms'] - ); - $terms = array(); - foreach ($this->settings['terms'] as $line) : - $line = explode(FEEDWORDPRESS_CAT_SEPARATOR, $line); - $tax = array_shift($line); - $terms[$tax] = $line; - endforeach; - $this->settings['terms'] = $terms; - endif; - endif; - - if (isset($this->settings['map authors'])) : - $author_rules = explode("\n\n", $this->settings['map authors']); - $ma = array(); - foreach ($author_rules as $rule) : - list($rule_type, $author_name, $author_action) = explode("\n", $rule); - - // Normalize for case and whitespace - $rule_type = strtolower(trim($rule_type)); - $author_name = strtolower(trim($author_name)); - $author_action = strtolower(trim($author_action)); - - $ma[$rule_type][$author_name] = $author_action; - endforeach; - $this->settings['map authors'] = $ma; - endif; + $this->get_settings_from_notes(); endif; + + add_filter('feedwordpress_update_complete', array($this, 'process_retirements'), 1000, 1); } /* SyndicatedLink::SyndicatedLink () */ function found () { return is_object($this->link) and !is_wp_error($this->link); } /* SyndicatedLink::found () */ + function id () { + return (is_object($this->link) ? $this->link->link_id : NULL); + } + function stale () { global $feedwordpress; @@ -187,33 +92,53 @@ class SyndicatedLink { return $stale; } /* SyndicatedLink::stale () */ - function poll ($crash_ts = NULL) { - global $wpdb; - - $url = $this->uri(array('add_params' => true)); - FeedWordPress::diagnostic('updated_feeds', 'Polling feed ['.$url.']'); - + function fetch () { $timeout = $this->setting('fetch timeout', 'feedwordpress_fetch_timeout', FEEDWORDPRESS_FETCH_TIMEOUT_DEFAULT); $this->simplepie = apply_filters( 'syndicated_feed', - FeedWordPress::fetch($url, array('timeout' => $timeout)), + FeedWordPress::fetch($this, array('timeout' => $timeout)), $this ); - + // Filter compatibility mode if (is_wp_error($this->simplepie)) : $this->magpie = $this->simplepie; else : $this->magpie = new MagpieFromSimplePie($this->simplepie, NULL); endif; + } + function live_posts () { + if (!is_object($this->simplepie)) : + $this->fetch(); + endif; + + if (is_object($this->simplepie) and method_exists($this->simplepie, 'get_items')) : + $ret = apply_filters( + 'syndicated_feed_items', + $this->simplepie->get_items(), + $this + ); + else : + $ret = $this->simplepie; + endif; + return $ret; + } + + function poll ($crash_ts = NULL) { + global $wpdb; + + $url = $this->uri(array('add_params' => true)); + FeedWordPress::diagnostic('updated_feeds', 'Polling feed ['.$url.']'); + + $this->fetch(); $new_count = NULL; - $resume = FeedWordPress::affirmative($this->settings, 'update/unfinished'); + $resume = ('yes'==$this->setting('update/unfinished')); if ($resume) : // pick up where we left off - $processed = array_map('trim', explode("\n", $this->settings['update/processed'])); + $processed = array_map('trim', explode("\n", $this->setting('update/processed'))); else : // begin at the beginning $processed = array(); @@ -236,26 +161,25 @@ class SyndicatedLink { // Copy over the in-error-since timestamp $theError['since'] = $oldError['since']; - // If this is a repeat error, then we should take - // a step back before we try to fetch it again. - $this->settings['update/last'] = time(); - $this->settings['update/ttl'] = $this->automatic_ttl(); - $this->settings['update/ttl'] = apply_filters('syndicated_feed_ttl', $this->settings['update/ttl'], $this); - $this->settings['update/ttl'] = apply_filters('syndicated_feed_ttl_from_error', $this->settings['update/ttl'], $this); - - $this->settings['update/timed'] = 'automatically'; + // If this is a repeat error, then we should + // take a step back before we try to fetch it + // again. + $this->update_setting('update/last', time(), NULL); + $ttl = $this->automatic_ttl(); + $ttl = apply_filters('syndicated_feed_ttl', $ttl, $this); + $ttl = apply_filters('syndicated_feed_ttl_from_error', $ttl, $this); + $this->update_setting('update/ttl', $ttl, $this); + $this->update_setting('update/timed', 'automatically'); endif; do_action('syndicated_feed_error', $theError, $oldError, $this); - $this->settings['update/error'] = serialize($theError); + $this->update_setting('update/error', serialize($theError)); $this->save_settings(/*reload=*/ true); elseif (is_object($this->simplepie)) : // Success; clear out error setting, if any. - if (isset($this->settings['update/error'])) : - unset($this->settings['update/error']); - endif; + $this->update_setting('update/error', NULL); $new_count = array('new' => 0, 'updated' => 0); @@ -282,30 +206,34 @@ class SyndicatedLink { $update[] = "link_description = '".$wpdb->escape($channel['description'])."'"; endif; endif; - - $this->settings = array_merge($this->settings, $this->flatten_array($channel)); - $this->settings['update/last'] = time(); + $this->merge_settings($channel, 'feed/'); + + $this->update_setting('update/last', time()); list($ttl, $xml) = $this->ttl(/*return element=*/ true); if (!is_null($ttl)) : - $this->settings['update/ttl'] = $ttl; - $this->settings['update/xml'] = $xml; - $this->settings['update/timed'] = 'feed'; + $this->update_setting('update/ttl', $ttl); + $this->update_setting('update/xml', $xml); + $this->update_setting('update/timed', 'feed'); else : $ttl = $this->automatic_ttl(); - $this->settings['update/ttl'] = $ttl; - $this->settings['update/xml'] = NULL; - $this->settings['update/timed'] = 'automatically'; + $this->update_setting('update/ttl', $ttl); + $this->update_setting('update/xml', NULL); + $this->update_setting('update/timed', 'automatically'); endif; - $this->settings['update/fudge'] = rand(0, ($ttl/3))*60; - $this->settings['update/ttl'] = apply_filters('syndicated_feed_ttl', $this->setting('update/ttl'), $this); + $this->update_setting('update/fudge', rand(0, ($ttl/3))*60); + $this->update_setting('update/ttl', apply_filters( + 'syndicated_feed_ttl', + $this->setting('update/ttl'), + $this + )); if (!$this->setting('update/hold') != 'ping') : - $this->settings['update/hold'] = 'scheduled'; + $this->update_setting('update/hold', 'scheduled'); endif; - $this->settings['update/unfinished'] = 'yes'; + $this->update_setting('update/unfinished', 'yes'); $update[] = "link_notes = '".$wpdb->escape($this->settings_to_notes())."'"; @@ -322,14 +250,25 @@ class SyndicatedLink { # -- Add new posts from feed and update any updated posts $crashed = false; - $posts = apply_filters( - 'syndicated_feed_items', - $this->simplepie->get_items(), - &$this - ); + $posts = $this->live_posts(); $this->magpie->originals = $posts; + // If this is a complete feed, rather than an incremental feed, we + // need to prepare to mark everything for presumptive retirement. + if ($this->is_incremental()) : + $q = new WP_Query(array( + 'fields' => '_synfrom', + 'post_status__not' => 'fwpretired', + 'ignore_sticky_posts' => true, + 'meta_key' => 'syndication_feed_id', + 'meta_value' => $this->id, + )); + foreach ($q->posts as $p) : + update_post_meta($p->ID, '_feedwordpress_retire_me_'.$this->id, '1'); + endforeach; + endif; + if (is_array($posts)) : foreach ($posts as $key => $item) : $post = new SyndicatedPost($item, $this); @@ -349,18 +288,51 @@ class SyndicatedLink { unset($post); endforeach; endif; + + if ('yes'==$this->setting('tombstones', 'tombstones', 'yes')) : + // Check for use of Atom tombstones. Spec: + // <http://tools.ietf.org/html/draft-snell-atompub-tombstones-18> + $tombstones = $this->simplepie->get_feed_tags('http://purl.org/atompub/tombstones/1.0', 'deleted-entry'); + if (count($tombstones) > 0) : + foreach ($tombstones as $tombstone) : + $ref = NULL; + foreach (array('', 'http://purl.org/atompub/tombstones/1.0') as $ns) : + if (isset($tombstone['attribs'][$ns]) + and isset($tombstone['attribs'][$ns]['ref'])) : + $ref = $tombstone['attribs'][$ns]['ref']; + endif; + endforeach; + + $q = new WP_Query(array( + 'ignore_sticky_posts' => true, + 'guid' => $ref, + 'meta_key' => 'syndication_feed_id', + 'meta_value' => $this->id, // Only allow a feed to tombstone its own entries. + )); + + foreach ($q->posts as $p) : + $old_status = $p->post_status; + FeedWordPress::diagnostic('syndicated_posts', 'Retiring existing post # '.$p->ID.' "'.$p->post_title.'" due to Atom tombstone element in feed.'); + set_post_field('post_status', 'fwpretired', $p->ID); + wp_transition_post_status('fwpretired', $old_status, $p); + endforeach; + + endforeach; + endif; + endif; + $suffix = ($crashed ? 'crashed' : 'completed'); do_action('update_syndicated_feed_items', $this->id, $this); do_action("update_syndicated_feed_items_${suffix}", $this->id, $this); - // Copy back any changes to feed settings made in the course of updating (e.g. new author rules) - $to_notes = $this->settings; - - $this->settings['update/processed'] = $processed; + $this->update_setting('update/processed', $processed); if (!$crashed) : - $this->settings['update/unfinished'] = 'no'; + $this->update_setting('update/unfinished', 'no'); endif; + $this->update_setting('link/item count', count($posts)); + // Copy back any changes to feed settings made in the + // course of updating (e.g. new author rules) $update_set = "link_notes = '".$wpdb->escape($this->settings_to_notes())."'"; // Update the properties of the link from the feed information @@ -386,6 +358,28 @@ class SyndicatedLink { return $new_count; } /* SyndicatedLink::poll() */ + function process_retirements ($delta) { + global $post; + + $q = new WP_Query(array( + 'fields' => '_synfrom', + 'post_status__not' => 'fwpretired', + 'ignore_sticky_posts' => true, + 'meta_key' => '_feedwordpress_retire_me_'.$this->id, + 'meta_value' => '1', + )); + if ($q->have_posts()) : + foreach ($q->posts as $p) : + $old_status = $p->post_status; + FeedWordPress::diagnostic('syndicated_posts', 'Retiring existing post # '.$p->ID.' "'.$p->post_title.'" due to absence from a non-incremental feed.'); + set_post_field('post_status', 'fwpretired', $p->ID); + wp_transition_post_status('fwpretired', $old_status, $p); + delete_post_meta($p->ID, '_feedwordpress_retire_me_'.$this->id); + endforeach; + endif; + return $delta; + } + /** * Updates the URL for the feed syndicated by this link. * @@ -466,9 +460,11 @@ class SyndicatedLink { $newuser_id = fwp_insert_new_user($newuser_name); if (is_numeric($newuser_id)) : if (is_null($name)) : // Unfamiliar author - $this->settings['unfamiliar author'] = $newuser_id; + $this->update_setting('unfamiliar author', $newuser_id); else : - $this->settings['map authors']['name'][$name] = $newuser_id; + $map = $this->setting('map authors'); + $map['name'][$name] = $newuser_id; + $this->update_setting('map authors', $map); endif; else : // TODO: Add some error detection and reporting @@ -481,6 +477,108 @@ class SyndicatedLink { function imploded_settings () { return array('cats', 'tags', 'match/cats', 'match/tags', 'match/filter'); } + + function get_settings_from_notes () { + // Read off feed settings from link_notes + $notes = explode("\n", $this->link->link_notes); + foreach ($notes as $note): + $pair = explode(": ", $note, 2); + $key = (isset($pair[0]) ? $pair[0] : null); + $value = (isset($pair[1]) ? $pair[1] : null); + if (!is_null($key) and !is_null($value)) : + // Unescape and trim() off the whitespace. + // Thanks to Ray Lischner for pointing out the + // need to trim off whitespace. + $this->settings[$key] = stripcslashes (trim($value)); + endif; + endforeach; + + // "Magic" feed settings + $this->settings['link/uri'] = $this->link->link_rss; + $this->settings['link/name'] = $this->link->link_name; + $this->settings['link/id'] = $this->link->link_id; + + // `hardcode categories` and `unfamiliar categories` are + // deprecated in favor of `unfamiliar category` + if ( + isset($this->settings['unfamiliar categories']) + and !isset($this->settings['unfamiliar category']) + ) : + $this->settings['unfamiliar category'] = $this->settings['unfamiliar categories']; + endif; + if ( + FeedWordPress::affirmative($this->settings, 'hardcode categories') + and !isset($this->settings['unfamiliar category']) + ) : + $this->settings['unfamiliar category'] = 'default'; + endif; + + // Set this up automagically for del.icio.us + $bits = parse_url($this->link->link_rss); + $tagspacers = array('del.icio.us', 'feeds.delicious.com'); + if (!isset($this->settings['cat_split']) and in_array($bits['host'], $tagspacers)) : + $this->settings['cat_split'] = '\s'; // Whitespace separates multiple tags in del.icio.us RSS feeds + endif; + + // Simple lists + foreach ($this->imploded_settings() as $what) : + if (isset($this->settings[$what])): + $this->settings[$what] = explode( + FEEDWORDPRESS_CAT_SEPARATOR, + $this->settings[$what] + ); + endif; + endforeach; + + if (isset($this->settings['terms'])) : + // Look for new format + $this->settings['terms'] = maybe_unserialize($this->settings['terms']); + + if (!is_array($this->settings['terms'])) : + // Deal with old format instead. Ugh. + + // Split on two *or more* consecutive breaks + // because in the old format, a taxonomy + // without any associated terms would + // produce tax_name#1\n\n\ntax_name#2\nterm, + // and the naive split on the first \n\n + // would screw up the tax_name#2 list. + // + // Props to David Morris for pointing this + // out. + + $this->settings['terms'] = preg_split( + "/".FEEDWORDPRESS_CAT_SEPARATOR."{2,}/", + $this->settings['terms'] + ); + $terms = array(); + foreach ($this->settings['terms'] as $line) : + $line = explode(FEEDWORDPRESS_CAT_SEPARATOR, $line); + $tax = array_shift($line); + $terms[$tax] = $line; + endforeach; + $this->settings['terms'] = $terms; + endif; + endif; + + if (isset($this->settings['map authors'])) : + $author_rules = explode("\n\n", $this->settings['map authors']); + $ma = array(); + foreach ($author_rules as $rule) : + list($rule_type, $author_name, $author_action) = explode("\n", $rule); + + // Normalize for case and whitespace + $rule_type = strtolower(trim($rule_type)); + $author_name = strtolower(trim($author_name)); + $author_action = strtolower(trim($author_action)); + + $ma[$rule_type][$author_name] = $author_action; + endforeach; + $this->settings['map authors'] = $ma; + endif; + + } /* SyndicatedLink::get_settings_from_notes () */ + function settings_to_notes () { $to_notes = $this->settings; @@ -593,6 +691,11 @@ class SyndicatedLink { return $ret; } /* SyndicatedLink::setting () */ + function merge_settings ($data, $prefix, $separator = '/') { + $dd = $this->flatten_array($data, $prefix, $separator); + $this->settings = array_merge($this->settings, $dd); + } /* SyndicatedLink::merge_settings () */ + function update_setting ($name, $value, $default = 'default') { if (!is_null($value) and $value != $default) : $this->settings[$name] = $value; @@ -601,10 +704,14 @@ class SyndicatedLink { endif; } /* SyndicatedLink::update_setting () */ + function is_incremental () { + return ('complete'==$this->setting('update_incremental', 'update_incremental', 'incremental')); + } /* SyndicatedLink::is_incremental () */ + function uri ($params = array()) { - $params = shortcode_atts(array( + $params = wp_parse_args($params, array( 'add_params' => false, - ), $params); + )); $uri = (is_object($this->link) ? $this->link->link_rss : NULL); if (!is_null($uri) and strlen($uri) > 0 and $params['add_params']) : @@ -620,11 +727,11 @@ class SyndicatedLink { endforeach; // Are we appending to a URI that already has params? - $sep = ((strpos('?', $uri)===false) ? '?' : '&'); + $sep = ((strpos($uri, "?")===false) ? '?' : '&'); // Tack it on $uri .= $sep . implode("&", $q); - endif; + endif; endif; return $uri; @@ -645,15 +752,61 @@ class SyndicatedLink { endif; return $auth; } /* SyndicatedLink::authentication_method () */ + + var $postmeta = array(); + function postmeta ($params = array()) { + $params = wp_parse_args($params, /*defaults=*/ array( + "field" => NULL, + "parsed" => false, + "force" => false, + )); + + if ($params['force'] or !isset($this->postmeta[/*parsed = */ false])) : + // First, get the global settings. + $default_custom_settings = get_option('feedwordpress_custom_settings'); + if ($default_custom_settings and !is_array($default_custom_settings)) : + $default_custom_settings = unserialize($default_custom_settings); + endif; + if (!is_array($default_custom_settings)) : + $default_custom_settings = array(); + endif; + + // Next, get the settings for this particular feed. + $custom_settings = $this->setting('postmeta', NULL, NULL); + if ($custom_settings and !is_array($custom_settings)) : + $custom_settings = unserialize($custom_settings); + endif; + if (!is_array($custom_settings)) : + $custom_settings = array(); + endif; + + $this->postmeta[/*parsed=*/ false] = array_merge($default_custom_settings, $custom_settings); + $this->postmeta[/*parsed=*/ true] = array(); + + // Now, run through and parse them all. + foreach ($this->postmeta[/*parsed=*/ false] as $key => $meta) : + $meta = apply_filters("syndicated_link_post_meta_${key}_pre", $meta, $this); + $this->postmeta[/*parsed=*/ false][$key] = $meta; + $this->postmeta[/*parsed=*/ true][$key] = new FeedWordPressParsedPostMeta($meta); + endforeach; + endif; + + $ret = $this->postmeta[!!$params['parsed']]; + if (is_string($params['field'])) : + $ret = $ret[$params['field']]; + endif; + return $ret; + } /* SyndicatedLink::postmeta () */ - function property_cascade ($fromFeed, $link_field, $setting, $simplepie_method) { + function property_cascade ($fromFeed, $link_field, $setting, $method) { $value = NULL; if ($fromFeed) : - if (isset($this->settings[$setting])) : - $value = $this->settings[$setting]; - elseif (is_object($this->simplepie) - and method_exists($this->simplepie, $simplepie_method)) : - $value = $this->simplepie->{$simplepie_method}(); + $value = $this->setting($setting, NULL, NULL, NULL); + + $s = $this->simplepie; + $callable = (is_object($s) and method_exists($s, $method)); + if (is_null($value) and $callable) : + $fallback = $s->{$method}(); endif; else : $value = $this->link->{$link_field}; @@ -789,17 +942,13 @@ class SyndicatedLink { } /* SyndicatedLink::flatten_array () */ function hardcode ($what) { - $default = get_option("feedwordpress_hardcode_$what"); - if ( $default === 'yes' ) : - // If the default is to hardcode, then we want the - // negation of negative(): TRUE by default and FALSE if - // the setting is explicitly "no" - $ret = !FeedWordPress::negative($this->settings, "hardcode $what"); + + $ret = $this->setting('hardcode '.$what, 'hardcode_'.$what, NULL); + + if ('yes' == $ret) : + $ret = true; else : - // If the default is NOT to hardcode, then we want - // affirmative(): FALSE by default and TRUE if the - // setting is explicitly "yes" - $ret = FeedWordPress::affirmative($this->settings, "hardcode $what"); + $ret = false; endif; return $ret; } /* SyndicatedLink::hardcode () */ @@ -807,19 +956,8 @@ class SyndicatedLink { function syndicated_status ($what, $default, $fallback = true) { global $wpdb; - // Use local setting if we have it - if ( isset($this->settings["$what status"]) ) : - $ret = $this->settings["$what status"]; - - // Or fall back to global default if we can - elseif ($fallback) : - $ret = FeedWordPress::syndicated_status($what, $default); - - // Or use default value if we can't. - else : - $ret = $default; - - endif; + $g_set = ($fallback ? 'syndicated_' . $what . '_status' : NULL); + $ret = $this->setting($what.' status', $g_set, $default); return $wpdb->escape(trim(strtolower($ret))); } /* SyndicatedLink:syndicated_status () */ diff --git a/wp-content/plugins/feedwordpress/syndicatedpost.class.php b/wp-content/plugins/feedwordpress/syndicatedpost.class.php index 2c46841023429f6a99476d16521775364a4c4d3e..aa51a61349016b658a394de08d964e2e3977f902 100644 --- a/wp-content/plugins/feedwordpress/syndicatedpost.class.php +++ b/wp-content/plugins/feedwordpress/syndicatedpost.class.php @@ -30,6 +30,7 @@ class SyndicatedPost { var $_freshness = null; var $_wp_id = null; + var $_wp_post = null; /** * SyndicatedPost constructor: Given a feed item and the source from @@ -158,48 +159,16 @@ class SyndicatedPost { // Unique ID (hopefully a unique tag: URI); failing that, the permalink $this->post['guid'] = apply_filters('syndicated_item_guid', $this->guid(), $this); - // User-supplied custom settings to apply to each post. Do first so that FWP-generated custom settings will overwrite if necessary; thus preventing any munging - $default_custom_settings = get_option('feedwordpress_custom_settings'); - if ($default_custom_settings and !is_array($default_custom_settings)) : - $default_custom_settings = unserialize($default_custom_settings); - endif; - if (!is_array($default_custom_settings)) : - $default_custom_settings = array(); - endif; - - $custom_settings = (isset($this->link->settings['postmeta']) ? $this->link->settings['postmeta'] : null); - if ($custom_settings and !is_array($custom_settings)) : - $custom_settings = unserialize($custom_settings); - endif; - if (!is_array($custom_settings)) : - $custom_settings = array(); - endif; - - $postMetaIn = array_merge($default_custom_settings, $custom_settings); + // User-supplied custom settings to apply to each post. + // Do first so that FWP-generated custom settings will + // overwrite if necessary; thus preventing any munging. + $postMetaIn = $this->link->postmeta(array("parsed" => true)); $postMetaOut = array(); - - // Big ugly fuckin loop to do any element substitutions - // that we may need. - foreach ($postMetaIn as $key => $values) : - if (is_string($values)) : $values = array($values); endif; - - $postMetaOut[$key] = array(); - foreach ($values as $value) : - if (preg_match('/\$\( ([^)]+) \)/x', $value, $ref)) : - $elements = $this->query($ref[1]); - foreach ($elements as $element) : - $postMetaOut[$key][] = str_replace( - $ref[0], - $element, - $value - ); - endforeach; - else : - $postMetaOut[$key][] = $value; - endif; - endforeach; + + foreach ($postMetaIn as $key => $meta) : + $postMetaOut[$key] = $meta->do_substitutions($this); endforeach; - + foreach ($postMetaOut as $key => $values) : $this->post['meta'][$key] = array(); foreach ($values as $value) : @@ -380,6 +349,23 @@ class SyndicatedPost { ##################################### #### EXTRACT DATA FROM FEED ITEM #### ##################################### + + function substitution_function ($name) { + $ret = NULL; + + switch ($name) : + // Allowed PHP string functions + case 'trim': + case 'ltrim': + case 'rtrim': + case 'strtoupper': + case 'strtolower': + case 'urlencode': + case 'urldecode': + $ret = $name; + endswitch; + return $ret; + } /** * SyndicatedPost::query uses an XPath-like syntax to query arbitrary @@ -772,8 +758,20 @@ class SyndicatedPost { return $this->_hashes[$id]; } - function update_hash () { - return md5(serialize($this->item)); + function update_hash ($hashed = true) { + // Basis for tracking possible changes to item. + $hash = array( + "title" => $this->entry->get_title(), + "link" => $this->permalink(), + "content" => $this->content(), + "excerpt" => $this->excerpt(), + ); + + if ($hashed) : + $hash = md5(serialize($hash)); + endif; + + return $hash; } /* SyndicatedPost::update_hash() */ /*static*/ function normalize_guid_prefix () { @@ -853,56 +851,77 @@ class SyndicatedPost { function author () { $author = array (); - if (isset($this->item['author_name'])): - $author['name'] = $this->item['author_name']; - elseif (isset($this->item['dc']['creator'])): - $author['name'] = $this->item['dc']['creator']; - elseif (isset($this->item['dc']['contributor'])): - $author['name'] = $this->item['dc']['contributor']; - elseif (isset($this->feed->channel['dc']['creator'])) : - $author['name'] = $this->feed->channel['dc']['creator']; - elseif (isset($this->feed->channel['dc']['contributor'])) : - $author['name'] = $this->feed->channel['dc']['contributor']; - elseif (isset($this->feed->channel['author_name'])) : - $author['name'] = $this->feed->channel['author_name']; - elseif ($this->feed->is_rss() and isset($this->item['author'])) : - // The author element in RSS is allegedly an - // e-mail address, but lots of people don't use - // it that way. So let's make of it what we can. - $author = parse_email_with_realname($this->item['author']); - - if (!isset($author['name'])) : - if (isset($author['email'])) : - $author['name'] = $author['email']; - else : - $author['name'] = $this->feed->channel['title']; + $aa = $this->entry->get_authors(); + if (count($aa) > 0) : + $a = reset($aa); + + $author = array( + 'name' => $a->get_name(), + 'email' => $a->get_email(), + 'uri' => $a->get_link(), + ); + endif; + + if (FEEDWORDPRESS_COMPATIBILITY) : + // Search through the MagpieRSS elements: Atom, Dublin Core, RSS + if (isset($this->item['author_name'])): + $author['name'] = $this->item['author_name']; + elseif (isset($this->item['dc']['creator'])): + $author['name'] = $this->item['dc']['creator']; + elseif (isset($this->item['dc']['contributor'])): + $author['name'] = $this->item['dc']['contributor']; + elseif (isset($this->feed->channel['dc']['creator'])) : + $author['name'] = $this->feed->channel['dc']['creator']; + elseif (isset($this->feed->channel['dc']['contributor'])) : + $author['name'] = $this->feed->channel['dc']['contributor']; + elseif (isset($this->feed->channel['author_name'])) : + $author['name'] = $this->feed->channel['author_name']; + elseif ($this->feed->is_rss() and isset($this->item['author'])) : + // The author element in RSS is allegedly an + // e-mail address, but lots of people don't use + // it that way. So let's make of it what we can. + $author = parse_email_with_realname($this->item['author']); + + if (!isset($author['name'])) : + if (isset($author['email'])) : + $author['name'] = $author['email']; + else : + $author['name'] = $this->feed->channel['title']; + endif; endif; endif; - elseif ($this->link->name()) : - $author['name'] = $this->link->name(); - else : - $url = parse_url($this->link->uri()); - $author['name'] = $url['host']; endif; - if (isset($this->item['author_email'])): - $author['email'] = $this->item['author_email']; - elseif (isset($this->feed->channel['author_email'])) : - $author['email'] = $this->feed->channel['author_email']; + if (!isset($author['name']) or is_null($author['name'])) : + // Nothing found. Try some crappy defaults. + if ($this->link->name()) : + $author['name'] = $this->link->name(); + else : + $url = parse_url($this->link->uri()); + $author['name'] = $url['host']; + endif; endif; - if (isset($this->item['author_uri'])): - $author['uri'] = $this->item['author_uri']; - elseif (isset($this->item['author_url'])): - $author['uri'] = $this->item['author_url']; - elseif (isset($this->feed->channel['author_uri'])) : - $author['uri'] = $this->item['author_uri']; - elseif (isset($this->feed->channel['author_url'])) : - $author['uri'] = $this->item['author_url']; - elseif (isset($this->feed->channel['link'])) : - $author['uri'] = $this->feed->channel['link']; + if (FEEDWORDPRESS_COMPATIBILITY) : + if (isset($this->item['author_email'])): + $author['email'] = $this->item['author_email']; + elseif (isset($this->feed->channel['author_email'])) : + $author['email'] = $this->feed->channel['author_email']; + endif; + + if (isset($this->item['author_uri'])): + $author['uri'] = $this->item['author_uri']; + elseif (isset($this->item['author_url'])): + $author['uri'] = $this->item['author_url']; + elseif (isset($this->feed->channel['author_uri'])) : + $author['uri'] = $this->item['author_uri']; + elseif (isset($this->feed->channel['author_url'])) : + $author['uri'] = $this->item['author_url']; + elseif (isset($this->feed->channel['link'])) : + $author['uri'] = $this->feed->channel['link']; + endif; endif; - + return $author; } /* SyndicatedPost::author() */ @@ -1170,7 +1189,7 @@ class SyndicatedPost { $pattern = FeedWordPressHTML::attributeRegex($tag, $attr); $content = preg_replace_callback ( $pattern, - array(&$obj, 'resolve_single_relative_uri'), + array($obj, 'resolve_single_relative_uri'), $content ); endforeach; @@ -1199,7 +1218,7 @@ class SyndicatedPost { $content = preg_replace_callback ( $pattern, - array(&$obj, 'strip_attribute_from_tag'), + array($obj, 'strip_attribute_from_tag'), $content ); endforeach; @@ -1270,9 +1289,18 @@ class SyndicatedPost { !is_null($updated_ts) and ($updated_ts > $last_rev_ts) ); - - - if (!$updated) : + + $updatedReason = NULL; + if ($updated) : + $updatedReason = preg_replace( + "/\s+/", " ", + 'has been marked with a new timestamp (' + .date('Y-m-d H:i:s', $updated_ts) + ." > " + .date('Y-m-d H:i:s', $last_rev_ts) + .')' + ); + else : // Or the hash... $hash = $this->update_hash(); $seen = $this->stored_hashes($old_post->ID); @@ -1281,6 +1309,15 @@ class SyndicatedPost { else : $updated = true; // Can't find syndication meta-data endif; + + if ($updated and FeedWordPress::diagnostic_on('feed_items:freshness:reasons')) : + $updatedReason = ' has a not-yet-seen update hash: ' + .FeedWordPress::val($hash) + .' not in {' + .implode(", ", array_map(array('FeedWordPress', 'val'), $seen)) + .'}. Basis: ' + .FeedWordPress::val(array_keys($this->update_hash(false))); + endif; endif; $frozen = false; @@ -1289,15 +1326,26 @@ class SyndicatedPost { if (!$frozen) : $frozen_values = get_post_custom_values('_syndication_freeze_updates', $old_post->ID); $frozen = (count($frozen_values) > 0 and 'yes' == $frozen_values[0]); + + if ($frozen) : + $updatedReason = ' IS BLOCKED FROM BEING UPDATED BY A UPDATE LOCK ON THIS POST, EVEN THOUGH IT '.$updatedReason; + endif; + else : + $updatedReason = ' IS BLOCKED FROM BEING UPDATED BY A FEEDWORDPRESS UPDATE LOCK, EVEN THOUGH IT '.$updatedReason; endif; endif; $updated = ($updated and !$frozen); if ($updated) : FeedWordPress::diagnostic('feed_items:freshness', 'Item ['.$guid.'] "'.$this->entry->get_title().'" is an update of an existing post.'); + if (!is_null($updatedReason)) : + $updatedReason = preg_replace('/\s+/', ' ', $updatedReason); + FeedWordPress::diagnostic('feed_items:freshness:reasons', 'Item ['.$guid.'] "'.$this->entry->get_title().'" '.$updatedReason); + endif; $this->_freshness = 1; // Updated content $this->_wp_id = $old_post->ID; - + $this->_wp_post = $old_post; + // We want this to keep a running list of all the // processed update hashes. $this->post['meta']['syndication_item_hash'] = array_merge( @@ -1427,8 +1475,13 @@ class SyndicatedPost { if (!$this->filtered() and $freshness > 0) : // Filter some individual fields + // If there already is a post slug (from syndication or by manual + // editing) don't cause WP to overwrite it by sending in a NULL + // post_name. Props Chris Fritz 2012-11-28. + $post_name = (is_null($this->_wp_post) ? NULL : $this->_wp_post->post_name); + // Allow filters to set post slug. Props niska. - $post_name = apply_filters('syndicated_post_slug', NULL, $this); + $post_name = apply_filters('syndicated_post_slug', $post_name, $this); if (!empty($post_name)) : $this->post['post_name'] = $post_name; endif; @@ -1446,7 +1499,7 @@ class SyndicatedPost { // Hook in early to make sure these get inserted if at all possible add_action( /*hook=*/ 'transition_post_status', - /*callback=*/ array(&$this, 'add_rss_meta'), + /*callback=*/ array($this, 'add_rss_meta'), /*priority=*/ -10000, /* very early */ /*arguments=*/ 3 ); @@ -1469,10 +1522,24 @@ class SyndicatedPost { $ret = $retval[$freshness]; endif; + // If this is a legit, non-filtered post, tag it as found on the feed + // regardless of fresh or stale status + if (!$this->filtered()) : + $key = '_feedwordpress_retire_me_' . $this->link->id; + delete_post_meta($this->wp_id(), $key); + + $status = get_post_field('post_status', $this->wp_id()); + if ('fwpretired'==$status and $this->link->is_incremental()) : + FeedWordPress::diagnostic('syndicated_posts', "Un-retiring previously retired post # ".$this->wp_id()." due to re-appearance on non-incremental feed."); + set_post_field('post_status', $this->post['post_status'], $this->wp_id()); + wp_transition_post_status($this->post['post_status'], $status, $old_status, $this->post); + endif; + endif; + // Remove add_rss_meta hook remove_action( /*hook=*/ 'transition_post_status', - /*callback=*/ array(&$this, 'add_rss_meta'), + /*callback=*/ array($this, 'add_rss_meta'), /*priority=*/ -10000, /* very early */ /*arguments=*/ 3 ); @@ -1509,14 +1576,14 @@ class SyndicatedPost { foreach ($doNotMunge as $field) : $dbpost[$field] = get_post_field($field, $this->wp_id()); - endforeach; + endforeach; endif; // WP3's wp_insert_post scans current_user_can() for the // tax_input, with no apparent way to override. Ugh. add_action( /*hook=*/ 'transition_post_status', - /*callback=*/ array(&$this, 'add_terms'), + /*callback=*/ array($this, 'add_terms'), /*priority=*/ -10001, /* very early */ /*arguments=*/ 3 ); @@ -1525,7 +1592,7 @@ class SyndicatedPost { // post_modified. Ugh. add_action( /*hook=*/ 'transition_post_status', - /*callback=*/ array(&$this, 'fix_post_modified_ts'), + /*callback=*/ array($this, 'fix_post_modified_ts'), /*priority=*/ -10000, /* very early */ /*arguments=*/ 3 ); @@ -1539,14 +1606,14 @@ class SyndicatedPost { remove_action( /*hook=*/ 'transition_post_status', - /*callback=*/ array(&$this, 'add_terms'), + /*callback=*/ array($this, 'add_terms'), /*priority=*/ -10001, /* very early */ /*arguments=*/ 3 ); remove_action( /*hook=*/ 'transition_post_status', - /*callback=*/ array(&$this, 'fix_post_modified_ts'), + /*callback=*/ array($this, 'fix_post_modified_ts'), /*priority=*/ -10000, /* very early */ /*arguments=*/ 3 ); @@ -1554,7 +1621,7 @@ class SyndicatedPost { // Turn off ridiculous fucking kludges #1 and #2 remove_action('_wp_put_post_revision', array($this, 'fix_revision_meta')); foreach ($removed as $filter) : - add_filter('content_save_pre', $removed); + add_filter('content_save_pre', $filter); endforeach; $this->validate_post_id($dbpost, $update, array(__CLASS__, __FUNCTION__)); @@ -2131,20 +2198,5 @@ EOM; return $terms; } // function SyndicatedPost::category_ids () - function use_api ($tag) { - global $wp_db_version; - switch ($tag) : - case 'wp_insert_post': - // Before 2.2, wp_insert_post does too much of the wrong stuff to use it - // In 1.5 it was such a resource hog it would make PHP segfault on big updates - $ret = (isset($wp_db_version) and $wp_db_version > FWP_SCHEMA_21); - break; - case 'post_status_pending': - $ret = (isset($wp_db_version) and $wp_db_version > FWP_SCHEMA_23); - break; - endswitch; - return $ret; - } // function SyndicatedPost::use_api () - } /* class SyndicatedPost */ diff --git a/wp-content/plugins/feedwordpress/syndicationdataqueries.class.php b/wp-content/plugins/feedwordpress/syndicationdataqueries.class.php index 90ff6553179a02db09694324663f555c32946d41..fdb046cc537bb8d68da5f39c28bc4e1c18a159e2 100644 --- a/wp-content/plugins/feedwordpress/syndicationdataqueries.class.php +++ b/wp-content/plugins/feedwordpress/syndicationdataqueries.class.php @@ -1,12 +1,15 @@ <?php +define('FEEDWORDPRESS_OPTIMIZE_IN_CLAUSES', get_option('feedwordpress_optimize_in_clauses', false)); + class SyndicationDataQueries { function SyndicationDataQueries () { - add_action('init', array(&$this, 'init')); - add_action('parse_query', array(&$this, 'parse_query'), 10, 1); - add_filter('posts_search', array(&$this, 'posts_search'), 10, 2); - add_filter('posts_where', array(&$this, 'posts_where'), 10, 2); - add_filter('posts_fields', array(&$this, 'posts_fields'), 10, 2); - add_filter('posts_request', array(&$this, 'posts_request'), 10, 2); + add_action('init', array($this, 'init')); + add_filter('query', array($this, 'optimize_in_clauses')); + add_action('parse_query', array($this, 'parse_query'), 10, 1); + add_filter('posts_search', array($this, 'posts_search'), 10, 2); + add_filter('posts_where', array($this, 'posts_where'), 10, 2); + add_filter('posts_fields', array($this, 'posts_fields'), 10, 2); + add_filter('posts_request', array($this, 'posts_request'), 10, 2); } function init () { @@ -14,13 +17,36 @@ class SyndicationDataQueries { $wp->add_query_var('guid'); } + function optimize_in_clauses ($q) { + // This is kind of a dicey, low-level thing to do, and Christ, + // this is something WordPress should be doing on its own, + // so it's disabled by default. But you can enable it in + // Performance --> Optimize IN clauses + if (FEEDWORDPRESS_OPTIMIZE_IN_CLAUSES) : + if (preg_match_all('/ \s+ IN \s* \((\s*([0-9]+)\s*)\)/x', $q, $r, PREG_OFFSET_CAPTURE)) : + $from = 0; $nq = ''; + foreach ($r[0] as $idx => $ref) : + $len = $ref[1] - $from; + $nq .= substr($q, $from, $len); + $nq .= ' = ' . $r[1][$idx][0]; + $from = $ref[1] + strlen($ref[0]); + endforeach; + + $q = $nq; + endif; + endif; + + return $q; + } + function parse_query (&$q) { if ($q->get('guid')) : - $q->is_single = false; // Causes nasty side-effects. + $q->is_single = false; // Causes nasty side-effects. $q->is_singular = true; // Doesn't? endif; - if ($q->get('fields') == '_synfresh') : + $ff = $q->get('fields'); + if ($ff == '_synfresh' or $ff == '_synfrom') : $q->query_vars['cache_results'] = false; // Not suitable. endif; } /* SyndicationDataQueries::parse_query () */ @@ -35,7 +61,7 @@ class SyndicationDataQueries { endif; return $sql; } - + function posts_search ($search, &$query) { global $wpdb; if ($guid = $query->get('guid')) : @@ -67,17 +93,26 @@ class SyndicationDataQueries { // Ugly hack to ensure we ONLY check by guid in syndicated freshness // checks -- for reasons of both performance and correctness. Pitch: $search .= " -- '"; + elseif ($query->get('fields')=='_synfrom') : + $search .= " AND ({$wpdb->postmeta}.meta_key = '".$query->get('meta_key')."' AND {$wpdb->postmeta}.meta_value = '".$query->get('meta_value')."') -- '"; endif; return $search; } /* SyndicationDataQueries::posts_search () */ function posts_where ($where, &$q) { + global $wpdb; + // Ugly hack to ensure we ONLY check by guid in syndicated freshness // checks -- for reasons of both performance and correctness. Catch: if (strpos($where, " -- '") !== false) : $bits = explode(" -- '", $where, 2); $where = $bits[0]; endif; + + if ($psn = $q->get('post_status__not')) : + $where .= " AND ({$wpdb->posts}.post_status <> '".$wpdb->escape($psn)."')"; + endif; + return $where; } /* SyndicationDataQueries::post_where () */ @@ -86,7 +121,10 @@ class SyndicationDataQueries { if ($f = $query->get('fields')) : switch ($f) : case '_synfresh' : - $fields = "{$wpdb->posts}.ID, {$wpdb->posts}.guid, {$wpdb->posts}.post_modified_gmt"; + $fields = "{$wpdb->posts}.ID, {$wpdb->posts}.guid, {$wpdb->posts}.post_modified_gmt, {$wpdb->posts}.post_name"; + break; + case '_synfrom' : + $fields = "{$wpdb->posts}.ID, {$wpdb->posts}.guid, {$wpdb->posts}.post_title, {$wpdb->postmeta}.meta_value"; break; default : // Do nothing.