FeedWordPress - Version 2011.0531

Version Description

  • WORDPRSS 3.1.3 COMPATIBILITY: DUPLICATE POSTS ISSUE FIXED. Due to internal changes in the way that WordPress handles post guids in the most recent release (3.1.3), many users experienced problems with many duplicate posts appearing in rapid succession. (Specifically, this would happen with any posts using tag: URL guids -- such as all the posts coming from Blogger feeds or feeds from other Google services.) This compatibility release of FeedWordPress eliminates the issue by working around the new restrictions on tag: URLs.

  • NEW AND IMPROVED DIAGNOSTICS: Syndication --> Diagnostics now contains some new diagnostics settings useful for debugging problems with duplicate posts (allowing you to easily view the guid of posts in the WordPress posts database and allowing you to track the SQL used to check for existing versions of a syndicated post).

Download this release

Release Info

Developer radgeek
Plugin Icon wp plugin FeedWordPress
Version 2011.0531
Comparing to
See all releases

Code changes from version 2011.0512 to 2011.0531

diagnostics-page.php CHANGED
@@ -240,6 +240,10 @@ testing but absolutely inappropriate for a production server.</p>
240
  'feed_items:rejected' => 'when FeedWordPress rejects a post without syndicating it',
241
  'syndicated_posts:meta_data' => 'as syndication meta-data is added on the post',
242
  ),
 
 
 
 
243
  ), $page);
244
 
245
  foreach ($fields as $section => $items) :
240
  'feed_items:rejected' => 'when FeedWordPress rejects a post without syndicating it',
241
  'syndicated_posts:meta_data' => 'as syndication meta-data is added on the post',
242
  ),
243
+ 'Advanced Diagnostics' => array(
244
+ 'feed_items:freshness:sql' => 'when FeedWordPress issues the SQL query it uses to decide whether to treat items as new, updates, or duplicates',
245
+ 'syndicated_posts:static_meta_data' => 'providing meta-data about syndicated posts in the Edit Posts interface',
246
+ ),
247
  ), $page);
248
 
249
  foreach ($fields as $section => $items) :
feedwordpress.php CHANGED
@@ -3,7 +3,7 @@
3
  Plugin Name: FeedWordPress
4
  Plugin URI: http://feedwordpress.radgeek.com/
5
  Description: simple and flexible Atom/RSS syndication for WordPress
6
- Version: 2011.0512
7
  Author: Charles Johnson
8
  Author URI: http://radgeek.com/
9
  License: GPL
@@ -11,7 +11,7 @@ License: GPL
11
 
12
  /**
13
  * @package FeedWordPress
14
- * @version 2011.0512
15
  */
16
 
17
  # This uses code derived from:
@@ -34,7 +34,7 @@ License: GPL
34
 
35
  # -- Don't change these unless you know what you're doing...
36
 
37
- define ('FEEDWORDPRESS_VERSION', '2011.0512');
38
  define ('FEEDWORDPRESS_AUTHOR_CONTACT', 'http://radgeek.com/contact');
39
 
40
  if (!defined('FEEDWORDPRESS_BLEG')) :
@@ -109,14 +109,16 @@ if (!function_exists('wp_insert_user')) :
109
  require_once (ABSPATH . WPINC . '/registration.php'); // for wp_insert_user
110
  endif;
111
 
112
- require_once(dirname(__FILE__) . '/admin-ui.php');
113
- require_once(dirname(__FILE__) . '/feedwordpresssyndicationpage.class.php');
114
- require_once(dirname(__FILE__) . '/compatability.php'); // LEGACY API: Replicate or mock up functions for legacy support purposes
115
-
116
- require_once(dirname(__FILE__) . '/syndicatedpost.class.php');
117
- require_once(dirname(__FILE__) . '/syndicatedlink.class.php');
118
- require_once(dirname(__FILE__) . '/feedwordpresshtml.class.php');
119
- require_once(dirname(__FILE__) . '/feedwordpress-content-type-sniffer.class.php');
 
 
120
 
121
  // Magic quotes are just about the stupidest thing ever.
122
  if (is_array($_POST)) :
@@ -810,6 +812,9 @@ function fwp_publish_post_hook ($post_id) {
810
 
811
  function feedwordpress_add_post_edit_controls () {
812
  add_meta_box('feedwordpress-post-controls', __('Syndication'), 'feedwordpress_post_edit_controls', 'post', 'side', 'high');
 
 
 
813
  } // function FeedWordPress::postEditControls
814
 
815
  function feedwordpress_post_edit_controls () {
3
  Plugin Name: FeedWordPress
4
  Plugin URI: http://feedwordpress.radgeek.com/
5
  Description: simple and flexible Atom/RSS syndication for WordPress
6
+ Version: 2011.0531
7
  Author: Charles Johnson
8
  Author URI: http://radgeek.com/
9
  License: GPL
11
 
12
  /**
13
  * @package FeedWordPress
14
+ * @version 2011.0531
15
  */
16
 
17
  # This uses code derived from:
34
 
35
  # -- Don't change these unless you know what you're doing...
36
 
37
+ define ('FEEDWORDPRESS_VERSION', '2011.0531');
38
  define ('FEEDWORDPRESS_AUTHOR_CONTACT', 'http://radgeek.com/contact');
39
 
40
  if (!defined('FEEDWORDPRESS_BLEG')) :
109
  require_once (ABSPATH . WPINC . '/registration.php'); // for wp_insert_user
110
  endif;
111
 
112
+ $dir = dirname(__FILE__);
113
+ require_once("${dir}/admin-ui.php");
114
+ require_once("${dir}/feedwordpresssyndicationpage.class.php");
115
+ require_once("${dir}/compatability.php"); // Legacy API
116
+ require_once("${dir}/syndicatedpost.class.php");
117
+ require_once("${dir}/syndicatedlink.class.php");
118
+ require_once("${dir}/feedwordpresshtml.class.php");
119
+ require_once("${dir}/feedwordpress-content-type-sniffer.class.php");
120
+ require_once("${dir}/inspectpostmeta.class.php");
121
+ require_once("${dir}/syndicationdataqueries.class.php");
122
 
123
  // Magic quotes are just about the stupidest thing ever.
124
  if (is_array($_POST)) :
812
 
813
  function feedwordpress_add_post_edit_controls () {
814
  add_meta_box('feedwordpress-post-controls', __('Syndication'), 'feedwordpress_post_edit_controls', 'post', 'side', 'high');
815
+ if (FeedWordPress::diagnostic_on('syndicated_posts:static_meta_data')) :
816
+ $GLOBALS['inspectPostMeta'] = new InspectPostMeta;
817
+ endif;
818
  } // function FeedWordPress::postEditControls
819
 
820
  function feedwordpress_post_edit_controls () {
inspectpostmeta.class.php ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ * Inspect Post Meta
4
+ * Creates a post widget to help you inspect some meta fields for posts
5
+ * Version: 2010.1104
6
+ * Author: Charles Johnson
7
+ * Author URI: http://projects.radgeek.com
8
+ */
9
+
10
+ class InspectPostMeta {
11
+ function InspectPostMeta ($in_hook = true) {
12
+ if (!$in_hook) :
13
+ add_action('admin_menu', array($this, 'add_meta_box'));
14
+ else :
15
+ $this->add_meta_box();
16
+ endif;
17
+ }
18
+
19
+ function add_meta_box () {
20
+ add_meta_box(
21
+ /*id=*/ 'inspect_post_guid_box',
22
+ /*title=*/ 'Post GUID and Meta Data',
23
+ /*callback=*/ array($this, 'meta_box'),
24
+ /*page=*/ 'post',
25
+ /*context=*/ 'normal',
26
+ /*priority=*/ 'default'
27
+ );
28
+ }
29
+
30
+ function meta_box () {
31
+ global $post;
32
+ ?>
33
+ <table>
34
+ <tbody>
35
+ <tr>
36
+ <th style="text-align: left" scope="row">ID:</th>
37
+ <td><code><?php print esc_html($post->ID); ?></code></td>
38
+ </tr>
39
+
40
+ <tr>
41
+ <th style="text-align: left" scope="row">GUID:</th>
42
+ <td><code><?php print esc_html($post->guid); ?></code></td>
43
+ </tr>
44
+
45
+ <tr>
46
+ <th colspan="2" style="text-align: center"><h4>Custom Fields</h4></th>
47
+ </tr>
48
+
49
+ <?php
50
+ $custom = get_post_custom($post->ID);
51
+ if (count($custom) > 0) :
52
+ foreach ($custom as $key => $values) :
53
+ $idx = 1;
54
+ foreach ($values as $value) :
55
+ print "<tr><th style='text-align: left' scope='row'>".esc_html($key);
56
+ if ($idx > 1) :
57
+ print "[$idx]";
58
+ endif;
59
+ print ":</th> ";
60
+ print "<td><pre><code>".esc_html($value)."</code></pre>";
61
+ print "</td></tr>\n";
62
+ $idx++;
63
+ endforeach;
64
+ endforeach;
65
+ else :
66
+ print "<tr><td colspan='2'><p><em>No custom fields for this post.</em></p></td></tr>\n";
67
+ endif;
68
+ print "</table>\n";
69
+ } /* InspectPostMeta::meta_box() */
70
+ } /* class InspectPostMeta */
71
+
readme.txt CHANGED
@@ -3,8 +3,8 @@ Contributors: Charles Johnson
3
  Donate link: http://feedwordpress.radgeek.com/
4
  Tags: syndication, aggregation, feed, atom, rss
5
  Requires at least: 3.0
6
- Tested up to: 3.1.2
7
- Stable tag: 2011.0512
8
 
9
  FeedWordPress syndicates content from feeds you choose into your WordPress weblog.
10
 
@@ -93,6 +93,23 @@ outs, see the documentation at the [FeedWordPress project homepage][].
93
 
94
  == Changelog ==
95
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
  = 2011.0512 =
97
 
98
  * DIAGNOSTICS IMPROVEMENTS; "THERE MAY BE A BUG IN FEEDWORDPRESS" CRITICAL
3
  Donate link: http://feedwordpress.radgeek.com/
4
  Tags: syndication, aggregation, feed, atom, rss
5
  Requires at least: 3.0
6
+ Tested up to: 3.1.3
7
+ Stable tag: 2011.0531
8
 
9
  FeedWordPress syndicates content from feeds you choose into your WordPress weblog.
10
 
93
 
94
  == Changelog ==
95
 
96
+ = 2011.0531 =
97
+
98
+ * WORDPRSS 3.1.3 COMPATIBILITY: DUPLICATE POSTS ISSUE FIXED. Due to internal
99
+ changes in the way that WordPress handles post guids in the most recent
100
+ release (3.1.3), many users experienced problems with many duplicate posts
101
+ appearing in rapid succession. (Specifically, this would happen with any
102
+ posts using tag: URL guids -- such as all the posts coming from Blogger
103
+ feeds or feeds from other Google services.) This compatibility release of
104
+ FeedWordPress eliminates the issue by working around the new restrictions
105
+ on tag: URLs.
106
+
107
+ * NEW AND IMPROVED DIAGNOSTICS: Syndication --> Diagnostics now contains some
108
+ new diagnostics settings useful for debugging problems with duplicate posts
109
+ (allowing you to easily view the guid of posts in the WordPress posts
110
+ database and allowing you to track the SQL used to check for existing
111
+ versions of a syndicated post).
112
+
113
  = 2011.0512 =
114
 
115
  * DIAGNOSTICS IMPROVEMENTS; "THERE MAY BE A BUG IN FEEDWORDPRESS" CRITICAL
syndicatedpost.class.php CHANGED
@@ -618,7 +618,7 @@ class SyndicatedPost {
618
 
619
  // Ignore whitespace, case, and tag cruft.
620
  $theExcerpt = preg_replace('/\s+/', '', strtolower(strip_tags($excerpt)));
621
- $theContent = preg_replace('/\s+/', '', strtolower(strip_Tags($content)));
622
 
623
  if ( empty($excerpt) or $theExcerpt == $theContent ) :
624
  # If content is available, generate an excerpt.
@@ -773,13 +773,27 @@ class SyndicatedPost {
773
  return md5(serialize($this->item));
774
  } /* SyndicatedPost::update_hash() */
775
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
776
  function guid () {
777
  $guid = null;
778
- if (isset($this->item['id'])): // Atom 0.3 / 1.0
779
  $guid = $this->item['id'];
780
  elseif (isset($this->item['atom']['id'])) : // Namespaced Atom
781
  $guid = $this->item['atom']['id'];
782
- elseif (isset($this->item['guid'])) : // RSS 2.0
783
  $guid = $this->item['guid'];
784
  elseif (isset($this->item['dc']['identifier'])) : // yeah, right
785
  $guid = $this->item['dc']['identifier'];
@@ -1223,17 +1237,24 @@ class SyndicatedPost {
1223
  if (is_null($this->_freshness)) : // Not yet checked and cached.
1224
  $guid = $wpdb->escape($this->guid());
1225
 
1226
- $result = $wpdb->get_row("
1227
- SELECT id, guid, post_modified_gmt
1228
- FROM $wpdb->posts WHERE guid='$guid'
1229
- ");
1230
 
1231
- if (!$result) : // No post with this guid
 
 
 
 
 
 
 
1232
  FeedWordPress::diagnostic('feed_items:freshness', 'Item ['.$this->guid().'] "'.$this->entry->get_title().'" is a NEW POST.');
1233
  $this->_wp_id = NULL;
1234
  $this->_freshness = 2; // New content
1235
  else :
1236
- preg_match('/([0-9]+)-([0-9]+)-([0-9]+) ([0-9]+):([0-9]+):([0-9]+)/', $result->post_modified_gmt, $backref);
1237
 
1238
  $last_rev_ts = gmmktime($backref[4], $backref[5], $backref[6], $backref[2], $backref[3], $backref[1]);
1239
  $updated_ts = $this->updated(/*fallback=*/ true, /*default=*/ NULL);
@@ -1248,7 +1269,7 @@ class SyndicatedPost {
1248
  if (!$updated) :
1249
  // Or the hash...
1250
  $hash = $this->update_hash();
1251
- $seen = $this->stored_hashes($result->id);
1252
  if (count($seen) > 0) :
1253
  $updated = !in_array($hash, $seen); // Not seen yet?
1254
  else :
@@ -1260,7 +1281,7 @@ class SyndicatedPost {
1260
  if ($updated) : // Ignore if the post is frozen
1261
  $frozen = ('yes' == $this->link->setting('freeze updates', 'freeze_updates', NULL));
1262
  if (!$frozen) :
1263
- $frozen_values = get_post_custom_values('_syndication_freeze_updates', $result->id);
1264
  $frozen = (count($frozen_values) > 0 and 'yes' == $frozen_values[0]);
1265
  endif;
1266
  endif;
@@ -1269,7 +1290,7 @@ class SyndicatedPost {
1269
  if ($updated) :
1270
  FeedWordPress::diagnostic('feed_items:freshness', 'Item ['.$this->guid().'] "'.$this->entry->get_title().'" is an update of an existing post.');
1271
  $this->_freshness = 1; // Updated content
1272
- $this->_wp_id = $result->id;
1273
 
1274
  // We want this to keep a running list of all the
1275
  // processed update hashes.
@@ -1281,7 +1302,7 @@ class SyndicatedPost {
1281
  else :
1282
  FeedWordPress::diagnostic('feed_items:freshness', 'Item ['.$this->guid().'] "'.$this->entry->get_title().'" is a duplicate of an existing post.');
1283
  $this->_freshness = 0; // Same old, same old
1284
- $this->_wp_id = $result->id;
1285
  endif;
1286
  endif;
1287
  endif;
@@ -1581,6 +1602,8 @@ class SyndicatedPost {
1581
  // FIXME: Option for what to fill a blank title with...
1582
  endif;
1583
 
 
 
1584
  return $out;
1585
  }
1586
 
618
 
619
  // Ignore whitespace, case, and tag cruft.
620
  $theExcerpt = preg_replace('/\s+/', '', strtolower(strip_tags($excerpt)));
621
+ $theContent = preg_replace('/\s+/', '', strtolower(strip_tags($content)));
622
 
623
  if ( empty($excerpt) or $theExcerpt == $theContent ) :
624
  # If content is available, generate an excerpt.
773
  return md5(serialize($this->item));
774
  } /* SyndicatedPost::update_hash() */
775
 
776
+ /*static*/ function normalize_guid_prefix () {
777
+ return trailingslashit(get_bloginfo('url')).'?guid=';
778
+ }
779
+
780
+ /*static*/ function normalize_guid ($guid) {
781
+ if (preg_match('/^[0-9a-z]{32}$/i', $guid)) : // MD5
782
+ $guid = SyndicatedPost::normalize_Guid_prefix().strtolower($guid);
783
+ elseif (strlen(esc_url($guid)) == 0) :
784
+ $guid = SyndicatedPost::normalize_guid_prefix().md5($guid);
785
+ endif;
786
+
787
+ return $guid;
788
+ } /* SyndicatedPost::normalize_guid() */
789
+
790
  function guid () {
791
  $guid = null;
792
+ if (isset($this->item['id'])): // Atom 0.3 / 1.0
793
  $guid = $this->item['id'];
794
  elseif (isset($this->item['atom']['id'])) : // Namespaced Atom
795
  $guid = $this->item['atom']['id'];
796
+ elseif (isset($this->item['guid'])) : // RSS 2.0
797
  $guid = $this->item['guid'];
798
  elseif (isset($this->item['dc']['identifier'])) : // yeah, right
799
  $guid = $this->item['dc']['identifier'];
1237
  if (is_null($this->_freshness)) : // Not yet checked and cached.
1238
  $guid = $wpdb->escape($this->guid());
1239
 
1240
+ $q = new WP_Query(array(
1241
+ 'fields' => '_synfresh', // id, guid, post_modified_gmt
1242
+ 'guid' => $this->guid(),
1243
+ ));
1244
 
1245
+ $old_post = NULL;
1246
+ if ($q->have_posts()) :
1247
+ while ($q->have_posts()) : $q->the_post();
1248
+ $old_post = $q->post;
1249
+ endwhile;
1250
+ endif;
1251
+
1252
+ if (is_null($old_post)) : // No post with this guid
1253
  FeedWordPress::diagnostic('feed_items:freshness', 'Item ['.$this->guid().'] "'.$this->entry->get_title().'" is a NEW POST.');
1254
  $this->_wp_id = NULL;
1255
  $this->_freshness = 2; // New content
1256
  else :
1257
+ preg_match('/([0-9]+)-([0-9]+)-([0-9]+) ([0-9]+):([0-9]+):([0-9]+)/', $old_post->post_modified_gmt, $backref);
1258
 
1259
  $last_rev_ts = gmmktime($backref[4], $backref[5], $backref[6], $backref[2], $backref[3], $backref[1]);
1260
  $updated_ts = $this->updated(/*fallback=*/ true, /*default=*/ NULL);
1269
  if (!$updated) :
1270
  // Or the hash...
1271
  $hash = $this->update_hash();
1272
+ $seen = $this->stored_hashes($old_post->ID);
1273
  if (count($seen) > 0) :
1274
  $updated = !in_array($hash, $seen); // Not seen yet?
1275
  else :
1281
  if ($updated) : // Ignore if the post is frozen
1282
  $frozen = ('yes' == $this->link->setting('freeze updates', 'freeze_updates', NULL));
1283
  if (!$frozen) :
1284
+ $frozen_values = get_post_custom_values('_syndication_freeze_updates', $old_post->ID);
1285
  $frozen = (count($frozen_values) > 0 and 'yes' == $frozen_values[0]);
1286
  endif;
1287
  endif;
1290
  if ($updated) :
1291
  FeedWordPress::diagnostic('feed_items:freshness', 'Item ['.$this->guid().'] "'.$this->entry->get_title().'" is an update of an existing post.');
1292
  $this->_freshness = 1; // Updated content
1293
+ $this->_wp_id = $old_post->id;
1294
 
1295
  // We want this to keep a running list of all the
1296
  // processed update hashes.
1302
  else :
1303
  FeedWordPress::diagnostic('feed_items:freshness', 'Item ['.$this->guid().'] "'.$this->entry->get_title().'" is a duplicate of an existing post.');
1304
  $this->_freshness = 0; // Same old, same old
1305
+ $this->_wp_id = $old_post->id;
1306
  endif;
1307
  endif;
1308
  endif;
1602
  // FIXME: Option for what to fill a blank title with...
1603
  endif;
1604
 
1605
+ // Normalize the guid if necessary.
1606
+ $out['guid'] = SyndicatedPost::normalize_guid($out['guid']);
1607
  return $out;
1608
  }
1609
 
syndicationdataqueries.class.php ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class SyndicationDataQueries {
3
+ function SyndicationDataQueries () {
4
+ add_action('init', array(&$this, 'init'));
5
+ add_filter('posts_search', array(&$this, 'posts_search'), 10, 2);
6
+ add_filter('posts_fields', array(&$this, 'posts_fields'), 10, 2);
7
+ add_filter('posts_request', array(&$this, 'posts_request'), 10, 2);
8
+ }
9
+
10
+ function init () {
11
+ global $wp;
12
+ $wp->add_query_var('guid');
13
+ }
14
+
15
+ function posts_request ($sql, &$query) {
16
+ if ($query->get('fields') == '_synfresh') :
17
+ FeedWordPress::diagnostic('feed_items:freshness:sql', "SQL: ".$sql);
18
+ endif;
19
+ return $sql;
20
+ }
21
+
22
+ function posts_search ($search, &$query) {
23
+ global $wpdb;
24
+ if ($guid = $query->get('guid')) :
25
+ if (strlen(trim($guid)) > 0) :
26
+ $seek = array($guid);
27
+ $md5Seek = array();
28
+
29
+ // MD5 hashes
30
+ if (preg_match('/^[0-9a-f]{32}$/i', $guid)) :
31
+ $md5Seek = array($guid);
32
+ $seek[] = SyndicatedPost::normalize_guid_prefix().$guid;
33
+ endif;
34
+
35
+ // URLs that are invalid, or that WordPress just doesn't like
36
+ $nGuid = SyndicatedPost::normalize_guid($guid);
37
+ if ($guid != $nGuid) :
38
+ $seek[] = $nGuid;
39
+ endif;
40
+
41
+ // Assemble
42
+ $guidMatch = "(guid = '".implode("') OR (guid = '", $seek)."')";
43
+ if (count($md5Seek) > 0) :
44
+ $guidMatch .= " OR (MD5(guid) = '".implode("') OR (MD5(guid) = '", $md5Seek)."')";
45
+ endif;
46
+
47
+ $search .= $wpdb->prepare(" AND ($guidMatch)");
48
+ endif;
49
+ endif;
50
+ return $search;
51
+ } /* SyndicationDataQueries::posts_where () */
52
+
53
+ function posts_fields ($fields, &$query) {
54
+ global $wpdb;
55
+ if ($f = $query->get('fields')) :
56
+ switch ($f) :
57
+ case '_synfresh' :
58
+ $fields = "{$wpdb->posts}.ID, {$wpdb->posts}.guid, {$wpdb->posts}.post_modified_gmt";
59
+ break;
60
+ default :
61
+ // Do nothing.
62
+ endswitch;
63
+ endif;
64
+ return $fields;
65
+ } /* SyndicationDataQueries::posts_fields () */
66
+ }
67
+
68
+ $SDQ = new SyndicationDataQueries;
69
+