PluginProbe
FeedWordPress / 2011.0211
FeedWordPress v2011.0211
trunk 0.8 0.9 0.91 0.95 0.96 0.97 0.98 0.981 0.99 0.991 0.992 0.993 2008.1030 2008.1101 2008.1105 2008.1214 2009.0612 2009.0613 2009.0618 2009.0707 2009.1111 2009.1112 2010.0127 2010.0528 All 65 releases
← All changes | syndicatedpost.class.php +946 -1323 trunk2011.0211 View file →
@@ -1,8 +1,6 @@
1 1 <?php
2 -require_once dirname(__FILE__) . '/feedtime.class.php';
3 -require_once dirname(__FILE__) . '/syndicatedpostterm.class.php';
4 -require_once dirname(__FILE__) . '/syndicatedpostxpathquery.class.php';
2 +require_once(dirname(__FILE__).'/feedtime.class.php');
5 3
6 4 /**
7 5 * class SyndicatedPost: FeedWordPress uses to manage the conversion of
8 6 * incoming items from the feed parser into posts for the WordPress
@@ -11,31 +9,28 @@
11 9 * as some utility methods for extracting useful data from many
12 10 * different feed formats, which may be useful to FeedWordPress users
13 11 * who make use of feed data in PHP add-ons and filters.
14 12 *
15 - * @version 2017.1018
13 + * @version 2010.0905
16 14 */
17 15 class SyndicatedPost {
18 - /** @var MagpieRSS|null MagpieRSS representation. */
19 - var $item = null;
20 - /** @var SimplePie_Item|null SimplePie_Item representation. */
21 - var $entry = null;
16 + var $item = null; // MagpieRSS representation
17 + var $entry = null; // SimplePie_Item representation
22 18
23 19 var $link = null;
24 20 var $feed = null;
25 21 var $feedmeta = null;
22 +
23 + var $xmlns = array ();
26 24
27 - var $xmlns = array();
25 + var $post = array ();
28 26
29 - var $post = array();
30 -
31 - var $named = array();
32 - var $preset_terms = array();
33 - var $feed_terms = array();
34 -
27 + var $named = array ();
28 + var $preset_terms = array ();
29 + var $feed_terms = array ();
30 +
35 31 var $_freshness = null;
36 32 var $_wp_id = null;
37 - var $_wp_post = null;
38 33
39 34 /**
40 35 * SyndicatedPost constructor: Given a feed item and the source from
41 36 * which it was taken, prepare a post that can be inserted into the
@@ -44,68 +39,81 @@
44 39 *
45 40 * @param array $item The item syndicated from the feed.
46 41 * @param SyndicatedLink $source The feed it was syndicated from.
47 42 */
48 - public function __construct( $item, $source ) {
49 - if ( empty( $item ) and empty( $source ) )
50 - return;
43 + function SyndicatedPost ($item, &$source) {
44 + global $wpdb;
51 45
52 - if ( is_array( $item )
53 - and isset( $item['simplepie'] )
54 - and isset( $item['magpie'] ) ) :
46 + if (is_array($item)
47 + and isset($item['simplepie'])
48 + and isset($item['magpie'])) :
55 49 $this->entry = $item['simplepie'];
56 50 $this->item = $item['magpie'];
57 51 $item = $item['magpie'];
58 - elseif ( is_a( $item, 'SimplePie_Item' ) ) :
52 + elseif (is_a($item, 'SimplePie_Item')) :
59 53 $this->entry = $item;
60 -
54 +
61 55 // convert to Magpie for compat purposes
62 - $mpie = new MagpieFromSimplePie( $source->simplepie, $this->entry );
63 - $this->item = $mpie->get_item();
64 -
56 + $mp = new MagpieFromSimplePie($source->simplepie, $this->entry);
57 + $this->item = $mp->get_item();
58 +
65 59 // done with conversion object
66 - $mpie = NULL; unset( $mpie );
60 + $mp = NULL; unset($mp);
67 61 else :
68 62 $this->item = $item;
69 63 endif;
70 64
71 - $this->link = $source;
65 + $this->link =& $source;
72 66 $this->feed = $source->magpie;
73 67 $this->feedmeta = $source->settings;
74 68
75 - FeedWordPress::diagnostic( 'feed_items', 'Considering item [' . $this->guid() . '] "' . $this->entry->get_title().'"');
69 + FeedWordPress::diagnostic('feed_items', 'Considering item ['.$this->guid().'] "'.$this->entry->get_title().'"');
76 70
77 71 # Dealing with namespaces can get so fucking fucked.
78 72 $this->xmlns['forward'] = $source->magpie->_XMLNS_FAMILIAR;
79 73 $this->xmlns['reverse'] = array();
80 - foreach ( $this->xmlns['forward'] as $url => $ns ) :
81 - if ( ! isset( $this->xmlns['reverse'][ $ns ] ) ) :
82 - $this->xmlns['reverse'][ $ns ] = array();
74 + foreach ($this->xmlns['forward'] as $url => $ns) :
75 + if (!isset($this->xmlns['reverse'][$ns])) :
76 + $this->xmlns['reverse'][$ns] = array();
83 77 endif;
84 - $this->xmlns['reverse'][ $ns ][] = $url;
78 + $this->xmlns['reverse'][$ns][] = $url;
85 79 endforeach;
86 -
80 +
87 81 // Fucking SimplePie.
88 82 $this->xmlns['reverse']['rss'][] = '';
89 83
84 + # These globals were originally an ugly kludge around a bug in
85 + # apply_filters from WordPress 1.5. The bug was fixed in 1.5.1,
86 + # and I sure hope at this point that nobody writing filters for
87 + # FeedWordPress is still relying on them.
88 + #
89 + # Anyway, I hereby declare them DEPRECATED as of 8 February
90 + # 2010. I'll probably remove the globals within 1-2 releases in
91 + # the interests of code hygiene and memory usage. If you
92 + # currently use them in your filters, I advise you switch off to
93 + # accessing the public members SyndicatedPost::feed and
94 + # SyndicatedPost::feedmeta.
95 +
96 + global $fwp_channel, $fwp_feedmeta;
97 + $fwp_channel = $this->feed; $fwp_feedmeta = $this->feedmeta;
98 +
90 99 // Trigger global syndicated_item filter.
91 - $changed = apply_filters( 'syndicated_item', $this->item, $this );
100 + $changed = apply_filters('syndicated_item', $this->item, $this);
92 101 $this->item = $changed;
93 -
102 +
94 103 // Allow for feed-specific syndicated_item filters.
95 104 $changed = apply_filters(
96 - "syndicated_item_" . $source->uri(),
105 + "syndicated_item_".$source->uri(),
97 106 $this->item,
98 107 $this
99 108 );
100 109 $this->item = $changed;
101 -
110 +
102 111 # Filters can halt further processing by returning NULL
103 - if ( is_null( $this->item ) ) :
112 + if (is_null($this->item)) :
104 113 $this->post = NULL;
105 114 else :
106 -
107 - # Note that nothing is run through esc_sql() here.
115 + # Note that nothing is run through $wpdb->escape() here.
108 116 # That's deliberate. The escaping is done at the point
109 117 # of insertion, not here, to avoid double-escaping and
110 118 # to avoid screwing with syndicated_post filters
111 119
@@ -110,16 +118,14 @@
110 118 # to avoid screwing with syndicated_post filters
111 119
112 120 $this->post['post_title'] = apply_filters(
113 121 'syndicated_item_title',
114 - $this->entry->get_title(),
115 - $this
122 + $this->entry->get_title(), $this
116 123 );
117 124
118 125 $this->named['author'] = apply_filters(
119 126 'syndicated_item_author',
120 - $this->author(),
121 - $this
127 + $this->author(), $this
122 128 );
123 129 // This just gives us an alphanumeric name for the author.
124 130 // We look up (or create) the numeric ID for the author
125 131 // in SyndicatedPost::add().
@@ -125,68 +131,95 @@
125 131 // in SyndicatedPost::add().
126 132
127 133 $this->post['post_content'] = apply_filters(
128 134 'syndicated_item_content',
129 - $this->content(),
130 - $this
135 + $this->content(), $this
131 136 );
132 -
133 - $excerpt = apply_filters( 'syndicated_item_excerpt', $this->excerpt(), $this );
134 -
135 - if ( !empty( $excerpt ) ):
137 +
138 + $excerpt = apply_filters('syndicated_item_excerpt', $this->excerpt(), $this);
139 + if (!empty($excerpt)):
136 140 $this->post['post_excerpt'] = $excerpt;
137 141 endif;
138 142
139 143 // Dealing with timestamps in WordPress is so fucking fucked.
140 - $offset = (int) get_option( 'gmt_offset' ) * 60 * 60;
141 - $post_date_gmt = $this->published( array( 'default' => -1 ) );
142 - $post_modified_gmt = $this->updated( array( 'default' => -1 ) );
144 + $offset = (int) get_option('gmt_offset') * 60 * 60;
145 + $post_date_gmt = $this->published(array('default' => -1));
146 + $post_modified_gmt = $this->updated(array('default' => -1));
143 147
144 - $this->post['post_date_gmt'] = gmdate( 'Y-m-d H:i:s', $post_date_gmt );
145 - $this->post['post_date'] = gmdate( 'Y-m-d H:i:s', $post_date_gmt + $offset );
146 - $this->post['post_modified_gmt'] = gmdate( 'Y-m-d H:i:s', $post_modified_gmt );
147 - $this->post['post_modified'] = gmdate( 'Y-m-d H:i:s', $post_modified_gmt + $offset );
148 + $this->post['post_date_gmt'] = gmdate('Y-m-d H:i:s', $post_date_gmt);
149 + $this->post['post_date'] = gmdate('Y-m-d H:i:s', $post_date_gmt + $offset);
150 + $this->post['post_modified_gmt'] = gmdate('Y-m-d H:i:s', $post_modified_gmt);
151 + $this->post['post_modified'] = gmdate('Y-m-d H:i:s', $post_modified_gmt + $offset);
148 152
149 153 // Use feed-level preferences or the global default.
150 - $this->post['post_status'] = $this->link->syndicated_status( 'post', 'publish' );
151 - $this->post['comment_status'] = $this->link->syndicated_status( 'comment', 'closed' );
152 - $this->post['ping_status'] = $this->link->syndicated_status( 'ping', 'closed' );
154 + $this->post['post_status'] = $this->link->syndicated_status('post', 'publish');
155 + $this->post['comment_status'] = $this->link->syndicated_status('comment', 'closed');
156 + $this->post['ping_status'] = $this->link->syndicated_status('ping', 'closed');
153 157
154 158 // Unique ID (hopefully a unique tag: URI); failing that, the permalink
155 - $this->post['guid'] = apply_filters( 'syndicated_item_guid', $this->guid(), $this );
159 + $this->post['guid'] = apply_filters('syndicated_item_guid', $this->guid(), $this);
156 160
157 - // User-supplied custom settings to apply to each post.
158 - // Do first so that FWP-generated custom settings will
159 - // overwrite if necessary; thus preventing any munging.
160 - $postMetaIn = $this->link->postmeta( array( "parsed" => true ) );
161 + // 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
162 + $default_custom_settings = get_option('feedwordpress_custom_settings');
163 + if ($default_custom_settings and !is_array($default_custom_settings)) :
164 + $default_custom_settings = unserialize($default_custom_settings);
165 + endif;
166 + if (!is_array($default_custom_settings)) :
167 + $default_custom_settings = array();
168 + endif;
169 +
170 + $custom_settings = (isset($this->link->settings['postmeta']) ? $this->link->settings['postmeta'] : null);
171 + if ($custom_settings and !is_array($custom_settings)) :
172 + $custom_settings = unserialize($custom_settings);
173 + endif;
174 + if (!is_array($custom_settings)) :
175 + $custom_settings = array();
176 + endif;
177 +
178 + $postMetaIn = array_merge($default_custom_settings, $custom_settings);
161 179 $postMetaOut = array();
162 180
163 - foreach ( $postMetaIn as $key => $meta ) :
164 - $postMetaOut[ $key ] = $meta->do_substitutions( $this );
181 + // Big ugly fuckin loop to do any element substitutions
182 + // that we may need.
183 + foreach ($postMetaIn as $key => $values) :
184 + if (is_string($values)) : $values = array($values); endif;
185 +
186 + $postMetaOut[$key] = array();
187 + foreach ($values as $value) :
188 + if (preg_match('/\$\( ([^)]+) \)/x', $value, $ref)) :
189 + $elements = $this->query($ref[1]);
190 + foreach ($elements as $element) :
191 + $postMetaOut[$key][] = str_replace(
192 + $ref[0],
193 + $element,
194 + $value
195 + );
196 + endforeach;
197 + else :
198 + $postMetaOut[$key][] = $value;
199 + endif;
200 + endforeach;
165 201 endforeach;
166 202
167 - foreach ( $postMetaOut as $key => $values ) :
168 - if ( is_null( $values ) ) { // have chosen to replace value with empty string
169 - $values = [ '' ];
170 - }
171 - $this->post['meta'][ $key ] = array();
172 - foreach ( $values as $value ) :
173 - $this->post['meta'][ $key ][] = apply_filters( "syndicated_post_meta_{$key}", $value, $this );
203 + foreach ($postMetaOut as $key => $values) :
204 + $this->post['meta'][$key] = array();
205 + foreach ($values as $value) :
206 + $this->post['meta'][$key][] = apply_filters("syndicated_post_meta_{$key}", $value, $this);
174 207 endforeach;
175 208 endforeach;
176 -
209 +
177 210 // RSS 2.0 / Atom 1.0 enclosure support
178 211 $enclosures = $this->entry->get_enclosures();
179 - if ( is_array( $enclosures ) ) : foreach ( $enclosures as $enclosure ) :
212 + if (is_array($enclosures)) : foreach ($enclosures as $enclosure) :
180 213 $this->post['meta']['enclosure'][] =
181 - apply_filters( 'syndicated_item_enclosure_url', $enclosure->get_link(), $this ) . "\n".
182 - apply_filters( 'syndicated_item_enclosure_length', $enclosure->get_length(), $this ) . "\n".
183 - apply_filters( 'syndicated_item_enclosure_type', $enclosure->get_type(), $this );
214 + apply_filters('syndicated_item_enclosure_url', $enclosure->get_link(), $this)."\n".
215 + apply_filters('syndicated_item_enclosure_length', $enclosure->get_length(), $this)."\n".
216 + apply_filters('syndicated_item_enclosure_type', $enclosure->get_type(), $this);
184 217 endforeach; endif;
185 218
186 219 // In case you want to point back to the blog this was
187 220 // syndicated from.
188 -
221 +
189 222 $sourcemeta['syndication_source'] = apply_filters(
190 223 'syndicated_item_source_title',
191 224 $this->link->name(),
192 225 $this
@@ -200,21 +233,21 @@
200 233 'syndicated_item_source_id',
201 234 $this->link->guid(),
202 235 $this
203 236 );
204 -
237 +
205 238 // Make use of atom:source data, if present in an aggregated feed
206 239 $entry_source = $this->source();
207 - if ( ! is_null( $entry_source ) ) :
208 - foreach ( $entry_source as $what => $value ) :
209 - if ( ! is_null( $value ) ) :
210 - if ( 'title' == $what ) : $key = 'syndication_source';
211 - elseif ( 'feed' == $what ) : $key = 'syndication_feed';
212 - else : $key = "syndication_source_{$what}";
240 + if (!is_null($entry_source)) :
241 + foreach ($entry_source as $what => $value) :
242 + if (!is_null($value)) :
243 + if ($what=='title') : $key = 'syndication_source';
244 + elseif ($what=='feed') : $key = 'syndication_feed';
245 + else : $key = "syndication_source_${what}";
213 246 endif;
214 -
215 - $sourcemeta["{$key}_original"] = apply_filters(
216 - 'syndicated_item_original_source_' . $what,
247 +
248 + $sourcemeta["${key}_original"] = apply_filters(
249 + 'syndicated_item_original_source_'.$what,
217 250 $value,
218 251 $this
219 252 );
220 253 endif;
@@ -219,79 +252,136 @@
219 252 );
220 253 endif;
221 254 endforeach;
222 255 endif;
223 -
224 - foreach ( $sourcemeta as $meta_key => $value ) :
225 - if ( !is_null( $value ) ) :
226 - $this->post['meta'][ $meta_key ] = $value;
256 +
257 + foreach ($sourcemeta as $meta_key => $value) :
258 + if (!is_null($value)) :
259 + $this->post['meta'][$meta_key] = $value;
227 260 endif;
228 261 endforeach;
229 262
230 263 // Store information on human-readable and machine-readable comment URIs
231 -
264 +
232 265 // Human-readable comment URI
233 - $commentLink = apply_filters( 'syndicated_item_comments', $this->comment_link(), $this );
234 - if ( ! is_null( $commentLink) ) : $this->post['meta']['rss:comments'] = $commentLink; endif;
266 + $commentLink = apply_filters('syndicated_item_comments', $this->comment_link(), $this);
267 + if (!is_null($commentLink)) : $this->post['meta']['rss:comments'] = $commentLink; endif;
235 268
236 269 // Machine-readable content feed URI
237 - $commentFeed = apply_filters( 'syndicated_item_commentrss', $this->comment_feed(), $this );
238 - if ( ! is_null( $commentFeed ) ) : $this->post['meta']['wfw:commentRSS'] = $commentFeed; endif;
270 + $commentFeed = apply_filters('syndicated_item_commentrss', $this->comment_feed(), $this);
271 + if (!is_null($commentFeed)) : $this->post['meta']['wfw:commentRSS'] = $commentFeed; endif;
239 272 // Yeah, yeah, now I know that it's supposed to be
240 273 // wfw:commentRss. Oh well. Path dependence, sucka.
241 274
242 275 // Store information to identify the feed that this came from
243 - if ( isset( $this->feedmeta['link/uri'] ) ) :
276 + if (isset($this->feedmeta['link/uri'])) :
244 277 $this->post['meta']['syndication_feed'] = $this->feedmeta['link/uri'];
245 278 endif;
246 - if ( isset( $this->feedmeta['link/id'] ) ) :
279 + if (isset($this->feedmeta['link/id'])) :
247 280 $this->post['meta']['syndication_feed_id'] = $this->feedmeta['link/id'];
248 281 endif;
249 282
250 - if ( isset( $this->item['source_link_self'] ) ) :
283 + if (isset($this->item['source_link_self'])) :
251 284 $this->post['meta']['syndication_feed_original'] = $this->item['source_link_self'];
252 285 endif;
253 286
254 287 // In case you want to know the external permalink...
255 - $this->post['meta']['syndication_permalink'] = apply_filters( 'syndicated_item_link', $this->permalink() );
288 + $this->post['meta']['syndication_permalink'] = apply_filters('syndicated_item_link', $this->permalink());
256 289
257 290 // Store a hash of the post content for checking whether something needs to be updated
258 291 $this->post['meta']['syndication_item_hash'] = $this->update_hash();
259 292
260 - // Categories, Tags, and other Terms: from settings assignments (global settings, subscription settings),
261 - // and from feed assignments (item metadata, post content)
262 - $this->preset_terms = apply_filters( 'syndicated_item_preset_terms', $this->get_terms_from_settings(), $this );
263 - $this->feed_terms = apply_filters( 'syndicated_item_feed_terms', $this->get_terms_from_feeds(), $this );
293 + // Categories: start with default categories, if any.
294 + $cats = array();
295 + if ('no' != $this->link->setting('add/category', NULL, 'yes')) :
296 + $fc = get_option("feedwordpress_syndication_cats");
297 + if ($fc) :
298 + $cats = array_merge($cats, explode("\n", $fc));
299 + endif;
300 + endif;
264 301
265 - $this->post['post_type'] = apply_filters(
266 - 'syndicated_post_type',
267 - $this->link->setting( 'syndicated post type', 'syndicated_post_type', 'post' ),
268 - $this
269 - );
302 + $fc = $this->link->setting('cats', NULL, array());
303 + if (is_array($fc)) :
304 + $cats = array_merge($cats, $fc);
305 + endif;
306 + $this->preset_terms['category'] = $cats;
307 +
308 + // Now add categories from the post, if we have 'em
309 + $cats = array();
310 + $post_cats = $this->entry->get_categories();
311 + if (is_array($post_cats)) : foreach ($post_cats as $cat) :
312 + $cat_name = $cat->get_term();
313 + if (!$cat_name) : $cat_name = $cat->get_label(); endif;
314 +
315 + if ($this->link->setting('cat_split', NULL, NULL)) :
316 + $pcre = "\007".$this->feedmeta['cat_split']."\007";
317 + $cats = array_merge(
318 + $cats,
319 + preg_split(
320 + $pcre,
321 + $cat_name,
322 + -1 /*=no limit*/,
323 + PREG_SPLIT_NO_EMPTY
324 + )
325 + );
326 + else :
327 + $cats[] = $cat_name;
328 + endif;
329 + endforeach; endif;
330 +
331 + $this->feed_terms['category'] = apply_filters('syndicated_item_categories', $cats, $this);
332 +
333 + // Tags: start with default tags, if any
334 + $tags = array();
335 + if ('no' != $this->link->setting('add/post_tag', NULL, 'yes')) :
336 + $ft = get_option("feedwordpress_syndication_tags", NULL);
337 + $tags = (is_null($ft) ? array() : explode(FEEDWORDPRESS_CAT_SEPARATOR, $ft));
338 + endif;
339 +
340 + $ft = $this->link->setting('tags', NULL, array());
341 + if (is_array($ft)) :
342 + $tags = array_merge($tags, $ft);
343 + endif;
344 + $this->preset_terms['post_tag'] = $tags;
345 +
346 + // Scan post for /a[@rel='tag'] and use as tags if present
347 + $tags = $this->inline_tags();
348 + $this->feed_terms['post_tag'] = apply_filters('syndicated_item_tags', $tags, $this);
349 +
350 + $taxonomies = $this->link->taxonomies();
351 + $feedTerms = $this->link->setting('terms', NULL, array());
352 + $globalTerms = get_option('feedwordpress_syndication_terms', array());
353 +
354 + $specials = array('category' => 'cats', 'post_tag' => 'tags');
355 + foreach ($taxonomies as $tax) :
356 + if (!isset($specials[$tax])) :
357 + $terms = array();
358 +
359 + // See if we should get the globals
360 + if ('no' != $this->link->setting("add/$tax", NULL, 'yes')) :
361 + if (isset($globalTerms[$tax])) :
362 + $terms = $globalTerms[$tax];
363 + endif;
364 + endif;
365 +
366 + // Now merge in the locals
367 + if (isset($feedTerms[$tax])) :
368 + $terms = array_merge($terms, $feedTerms[$tax]);
369 + endif;
370 +
371 + // That's all, folks.
372 + $this->preset_terms[$tax] = $terms;
373 + endif;
374 + endforeach;
375 +
376 + $this->post['post_type'] = apply_filters('syndicated_post_type', $this->link->setting('syndicated post type', 'syndicated_post_type', 'post'), $this);
270 377 endif;
271 - } /* SyndicatedPost::__construct() */
378 + } /* SyndicatedPost::SyndicatedPost() */
272 379
273 380 #####################################
274 381 #### EXTRACT DATA FROM FEED ITEM ####
275 382 #####################################
276 -
277 - function substitution_function ($name) {
278 - $ret = NULL;
279 -
280 - switch ($name) :
281 - // Allowed PHP string functions
282 - case 'trim':
283 - case 'ltrim':
284 - case 'rtrim':
285 - case 'strtoupper':
286 - case 'strtolower':
287 - case 'urlencode':
288 - case 'urldecode':
289 - $ret = $name;
290 - endswitch;
291 - return $ret;
292 - }
293 -
383 +
294 384 /**
295 385 * SyndicatedPost::query uses an XPath-like syntax to query arbitrary
296 386 * elements within the syndicated item.
297 387 *
@@ -298,29 +388,100 @@
298 388 * @param string $path
299 389 * @returns array of string values representing contents of matching
300 390 * elements or attributes
301 391 */
302 - public function query ($path) {
303 - $xq = new SyndicatedPostXPathQuery(array("path" => $path));
392 + function query ($path) {
393 + $urlHash = array();
304 394
305 - $feedChannel = array_merge(
306 - $this->get_feed_root_element(),
307 - $this->get_feed_channel_elements()
308 - );
395 + // Allow {url} notation for namespaces. URLs will contain : and /, so...
396 + preg_match_all('/{([^}]+)}/', $path, $match, PREG_SET_ORDER);
397 + foreach ($match as $ref) :
398 + $urlHash[md5($ref[1])] = $ref[1];
399 + endforeach;
400 +
401 + foreach ($urlHash as $hash => $url) :
402 + $path = str_replace('{'.$url.'}', '{#'.$hash.'}', $path);
403 + endforeach;
309 404
310 - $matches = $xq->match(array(
311 - "type" => $this->link->simplepie->get_type(),
312 - "xmlns" => $this->xmlns,
313 - "map" => array(
314 - "/" => array($this->entry->data),
315 - "item" => array($this->entry->data),
316 - "feed" => $feedChannel,
317 - "channel" => $feedChannel
318 - ),
319 - "context" => $this->entry->data,
320 - "parent" => $feedChannel,
321 - ));
405 + $path = explode('/', $path);
406 + foreach ($path as $index => $node) :
407 + if (preg_match('/{#([^}]+)}/', $node, $ref)) :
408 + if (isset($urlHash[$ref[1]])) :
409 + $path[$index] = str_replace(
410 + '{#'.$ref[1].'}',
411 + '{'.$urlHash[$ref[1]].'}',
412 + $node
413 + );
414 + endif;
415 + endif;
416 + endforeach;
322 417
418 + // Start out with a get_item_tags query.
419 + $node = '';
420 + while (strlen($node)==0 and !is_null($node)) :
421 + $node = array_shift($path);
422 + endwhile;
423 +
424 + switch ($node) :
425 + case 'feed' :
426 + case 'channel' :
427 + $node = array_shift($path);
428 + $data = $this->get_feed_root_element();
429 + $data = array_merge($data, $this->get_feed_channel_elements());
430 + break;
431 + case 'item' :
432 + $node = array_shift($path);
433 + default :
434 + $data = array($this->entry->data);
435 + $method = NULL;
436 + endswitch;
437 +
438 + while (!is_null($node)) :
439 + if (strlen($node) > 0) :
440 + $matches = array();
441 +
442 + list($axis, $element) = $this->xpath_name_and_axis($node);
443 +
444 + foreach ($data as $datum) :
445 + if (!is_string($datum) and isset($datum[$axis])) :
446 + foreach ($datum[$axis] as $ns => $elements) :
447 + if (isset($elements[$element])) :
448 + // Potential match.
449 + // Check namespace.
450 + if (is_string($elements[$element])) : // Attribute
451 + $addenda = array($elements[$element]);
452 + $contexts = array($datum);
453 + else : // Element
454 + $addenda = $elements[$element];
455 + $contexts = $elements[$element];
456 + endif;
457 +
458 + foreach ($addenda as $index => $addendum) :
459 + $context = $contexts[$index];
460 +
461 + $namespaces = $this->xpath_possible_namespaces($node, $context);
462 + if (in_array($ns, $namespaces)) :
463 + $matches[] = $addendum;
464 + endif;
465 + endforeach;
466 + endif;
467 + endforeach;
468 + endif;
469 + endforeach;
470 +
471 + $data = $matches;
472 + endif;
473 + $node = array_shift($path);
474 + endwhile;
475 +
476 + $matches = array();
477 + foreach ($data as $datum) :
478 + if (is_string($datum)) :
479 + $matches[] = $datum;
480 + elseif (isset($datum['data'])) :
481 + $matches[] = $datum['data'];
482 + endif;
483 + endforeach;
323 484 return $matches;
324 485 } /* SyndicatedPost::query() */
325 486
326 487 function get_feed_root_element () {
@@ -339,13 +500,13 @@
339 500 SIMPLEPIE_NAMESPACE_RSS_10,
340 501 'http://backend.userland.com/RSS2',
341 502 SIMPLEPIE_NAMESPACE_RSS_20,
342 503 );
343 -
504 +
344 505 $matches = array();
345 506 foreach ($rss as $ns) :
346 507 $data = $this->link->simplepie->get_feed_tags($ns, 'channel');
347 - if ( !is_null($data)) :
508 + if (!is_null($data)) :
348 509 $matches = array_merge($matches, $data);
349 510 endif;
350 511 endforeach;
351 512 return $matches;
@@ -350,116 +511,139 @@
350 511 endforeach;
351 512 return $matches;
352 513 } /* SyndicatedPost::get_feed_channel_elements() */
353 514
354 - public function get_categories ($params = array()) {
355 - return $this->entry->get_categories();
356 - }
515 + function xpath_default_namespace () {
516 + // Get the default namespace.
517 + $type = $this->link->simplepie->get_type();
518 + if ($type & SIMPLEPIE_TYPE_ATOM_10) :
519 + $defaultNS = SIMPLEPIE_NAMESPACE_ATOM_10;
520 + elseif ($type & SIMPLEPIE_TYPE_ATOM_03) :
521 + $defaultNS = SIMPLEPIE_NAMESPACE_ATOM_03;
522 + elseif ($type & SIMPLEPIE_TYPE_RSS_090) :
523 + $defaultNS = SIMPLEPIE_NAMESPACE_RSS_090;
524 + elseif ($type & SIMPLEPIE_TYPE_RSS_10) :
525 + $defaultNS = SIMPLEPIE_NAMESPACE_RSS_10;
526 + elseif ($type & SIMPLEPIE_TYPE_RSS_20) :
527 + $defaultNS = SIMPLEPIE_NAMESPACE_RSS_20;
528 + else :
529 + $defaultNS = SIMPLEPIE_NAMESPACE_RSS_20;
530 + endif;
531 + return $defaultNS;
532 + } /* SyndicatedPost::xpath_default_namespace() */
357 533
358 - public function title ($params = array()) {
359 - return $this->entry->get_title();
360 - } /* SyndicatedPost::title () */
534 + function xpath_name_and_axis ($node) {
535 + $ns = NULL; $element = NULL;
361 536
362 - public function content ($params = array())
363 - {
364 - $params = wp_parse_args($params, array(
365 - "full only" => false,
366 - ));
537 + if (substr($node, 0, 1)=='@') :
538 + $axis = 'attribs'; $node = substr($node, 1);
539 + else :
540 + $axis = 'child';
541 + endif;
367 542
368 - $content = NULL;
543 + if (preg_match('/^{([^}]*)}(.*)$/', $node, $ref)) :
544 + $element = $ref[2];
545 + elseif (strpos($node, ':') !== FALSE) :
546 + list($xmlns, $element) = explode(':', $node, 2);
547 + else :
548 + $element = $node;
549 + endif;
550 + return array($axis, $element);
551 + } /* SyndicatedPost::xpath_local_name () */
369 552
370 - // FIXME: This is one of the main places in the code still using
371 - // the outmoded SimplePie - to - Magpie construction. We could
372 - // replace using SimplePie_Item::get_tags() here. (Or if really
373 - // ambitious we could attempt to just use
374 - // SimplePie_Item::get_content() with content-only set to TRUE
375 - // and some sanitization in effect. -CJ 1jul14
553 + function xpath_possible_namespaces ($node, $datum = array()) {
554 + $ns = NULL; $element = NULL;
376 555
377 - // atom:content, standard method of providing full HTML content
378 - // in Atom feeds.
556 + if (substr($node, 0, 1)=='@') :
557 + $attr = '@'; $node = substr($node, 1);
558 + else :
559 + $attr = '';
560 + endif;
561 +
562 + if (preg_match('/^{([^}]*)}(.*)$/', $node, $ref)) :
563 + $ns = array($ref[1]);
564 + elseif (strpos($node, ':') !== FALSE) :
565 + list($xmlns, $element) = explode(':', $node, 2);
566 +
567 + if (isset($this->xmlns['reverse'][$xmlns])) :
568 + $ns = $this->xmlns['reverse'][$xmlns];
569 + else :
570 + $ns = array($xmlns);
571 + endif;
572 +
573 + // Fucking SimplePie. For attributes in default xmlns.
574 + $defaultNS = $this->xpath_default_namespace();
575 + if (isset($this->xmlns['forward'][$defaultNS])
576 + and ($xmlns==$this->xmlns['forward'][$defaultNS])) :
577 + $ns[] = '';
578 + endif;
579 +
580 + if (isset($datum['xmlns'])) :
581 + if (isset($datum['xmlns'][$xmlns])) :
582 + $ns[] = $datum['xmlns'][$xmlns];
583 + endif;
584 + endif;
585 + else :
586 + // Often in SimplePie, the default namespace gets stored
587 + // as an empty string rather than a URL.
588 + $ns = array($this->xpath_default_namespace(), '');
589 + endif;
590 + return array_unique($ns);
591 + } /* SyndicatedPost::xpath_possible_namespaces() */
592 +
593 + function content () {
594 + $content = NULL;
379 595 if (isset($this->item['atom_content'])) :
380 596 $content = $this->item['atom_content'];
381 - elseif (isset($this->item['atom']['atom_content'])) :
382 - $content = $this->item['atom']['atom_content'];
383 -
384 - // Some exotics: back in the day, before widespread convergence
385 - // on content:encoding, some RSS feeds took advantage of XML
386 - // namespaces to use an inline xhtml:body or xhtml:div element
387 - // for full-content HTML. (E.g. Sam Ruby's feed, IIRC.)
388 597 elseif (isset($this->item['xhtml']['body'])) :
389 598 $content = $this->item['xhtml']['body'];
390 599 elseif (isset($this->item['xhtml']['div'])) :
391 600 $content = $this->item['xhtml']['div'];
392 -
393 - // content:encoded, most common method of providing full HTML in
394 - // RSS 2.0 feeds.
395 601 elseif (isset($this->item['content']['encoded']) and $this->item['content']['encoded']):
396 602 $content = $this->item['content']['encoded'];
397 -
398 - // Fall back on elements that sometimes may contain full HTML
399 - // but sometimes not.
400 - elseif ( ! $params['full only']) :
401 -
402 - // description element is sometimes used for full HTML
403 - // sometimes for summary text in RSS. (By the letter of
404 - // the standard, it should just be for summary text.)
405 - if (isset($this->item['description'])) :
406 - $content = $this->item['description'];
407 - endif;
408 -
603 + elseif (isset($this->item['description'])) :
604 + $content = $this->item['description'];
409 605 endif;
410 -
411 606 return $content;
412 607 } /* SyndicatedPost::content() */
413 608
414 - public function excerpt () {
609 + function excerpt () {
415 610 # Identify and sanitize excerpt: atom:summary, or rss:description
416 611 $excerpt = $this->entry->get_description();
417 -
612 +
418 613 # Many RSS feeds use rss:description, inadvisably, to
419 614 # carry the entire post (typically with escaped HTML).
420 615 # If that's what happened, we don't want the full
421 616 # content for the excerpt.
422 617 $content = $this->content();
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)));
423 622
424 - // Ignore whitespace, case, and tag cruft.
425 - $theExcerpt = preg_replace('/\s+/', '', strtolower(strip_tags(html_entity_decode($excerpt))));
426 - $theContent = preg_replace('/\s+/', '', strtolower(strip_tags(html_entity_decode($content))));
427 623 if ( empty($excerpt) or $theExcerpt == $theContent ) :
428 624 # If content is available, generate an excerpt.
429 625 if ( strlen(trim($content)) > 0 ) :
430 626 $excerpt = strip_tags($content);
431 627 if (strlen($excerpt) > 255) :
432 - if (is_object($this->link) and is_object($this->link->simplepie)) :
433 - $encoding = $this->link->simplepie->get_encoding();
434 - else :
435 - $encoding = get_option('blog_charset', 'utf8');
436 - endif;
437 - $excerpt = mb_substr($excerpt,0,252,$encoding).'...';
628 + $excerpt = substr($excerpt,0,252).'...';
438 629 endif;
439 630 endif;
440 631 endif;
441 -
442 632 return $excerpt;
443 633 } /* SyndicatedPost::excerpt() */
444 634
445 - /**
446 - * SyndicatedPost::permalink: returns the permalink for the post, as provided by the
447 - * source feed.
448 - *
449 - * @return string The URL of the original permalink for this syndicated post
450 - */
451 - public function permalink () {
635 + function permalink () {
452 636 // Handles explicit <link> elements and also RSS 2.0 cases with
453 637 // <guid isPermaLink="true">, etc. Hooray!
454 638 $permalink = $this->entry->get_link();
455 639 return $permalink;
456 - } /* SyndicatedPost::permalink () */
640 + }
457 641
458 - public function created ($params = array()) {
459 - $unfiltered = false; // $default = NULL; // seems to be unused on this function (gwyneth 20230916)
642 + function created ($params = array()) {
643 + $unfiltered = false; $default = NULL;
460 644 extract($params);
461 -
645 +
462 646 $date = '';
463 647 if (isset($this->item['dc']['created'])) :
464 648 $date = $this->item['dc']['created'];
465 649 elseif (isset($this->item['dcterms']['created'])) :
@@ -468,25 +652,25 @@
468 652 $date = $this->item['created'];
469 653 endif;
470 654
471 655 $time = new FeedTime($date);
472 - $tstamp = $time->timestamp();
473 - if ( ! $unfiltered) :
474 - apply_filters('syndicated_item_created', $tstamp, $this);
656 + $ts = $time->timestamp();
657 + if (!$unfiltered) :
658 + apply_filters('syndicated_item_created', $ts, $this);
475 659 endif;
476 - return $tstamp;
660 + return $ts;
477 661 } /* SyndicatedPost::created() */
478 662
479 - public function published ($params = array(), $default = NULL) {
663 + function published ($params = array(), $default = NULL) {
480 664 $fallback = true; $unfiltered = false;
481 - if ( !is_array($params)) : // Old style
665 + if (!is_array($params)) : // Old style
482 666 $fallback = $params;
483 667 else : // New style
484 668 extract($params);
485 669 endif;
486 -
670 +
487 671 $date = '';
488 - $tstamp = null;
672 + $ts = null;
489 673
490 674 # RSS is a fucking mess. Figure out whether we have a date in
491 675 # <dc:date>, <issued>, <pubDate>, etc., and get it into Unix
492 676 # epoch format for reformatting. If we can't find anything,
@@ -501,34 +685,34 @@
501 685 $date = $this->item['issued'];
502 686 elseif (isset($this->item['pubdate'])): // RSS 2.0
503 687 $date = $this->item['pubdate'];
504 688 endif;
505 -
689 +
506 690 if (strlen($date) > 0) :
507 691 $time = new FeedTime($date);
508 - $tstamp = $time->timestamp();
692 + $ts = $time->timestamp();
509 693 elseif ($fallback) : // Fall back to <updated> / <modified> if present
510 - $tstamp = $this->updated(/*fallback=*/ false, /*default=*/ $default);
694 + $ts = $this->updated(/*fallback=*/ false, /*default=*/ $default);
511 695 endif;
512 -
696 +
513 697 # If everything failed, then default to the current time.
514 - if (is_null($tstamp)) :
698 + if (is_null($ts)) :
515 699 if (-1 == $default) :
516 - $tstamp = time();
700 + $ts = time();
517 701 else :
518 - $tstamp = $default;
702 + $ts = $default;
519 703 endif;
520 704 endif;
521 -
522 - if ( ! $unfiltered) :
523 - $tstamp = apply_filters('syndicated_item_published', $tstamp, $this);
705 +
706 + if (!$unfiltered) :
707 + $ts = apply_filters('syndicated_item_published', $ts, $this);
524 708 endif;
525 - return $tstamp;
709 + return $ts;
526 710 } /* SyndicatedPost::published() */
527 711
528 - public function updated ($params = array(), $default = -1) {
712 + function updated ($params = array(), $default = -1) {
529 713 $fallback = true; $unfiltered = false;
530 - if ( !is_array($params)) : // Old style
714 + if (!is_array($params)) : // Old style
531 715 $fallback = $params;
532 716 else : // New style
533 717 extract($params);
534 718 endif;
@@ -533,9 +717,9 @@
533 717 extract($params);
534 718 endif;
535 719
536 720 $date = '';
537 - $tstamp = null;
721 + $ts = null;
538 722
539 723 # As far as I know, only dcterms and Atom have reliable ways to
540 724 # specify when something was *modified* last. If neither is
541 725 # available, then we'll try to get the time of publication.
@@ -547,130 +731,52 @@
547 731 $date = $this->item['modified'];
548 732 elseif (isset($this->item['updated'])): // Atom 1.0
549 733 $date = $this->item['updated'];
550 734 endif;
551 -
735 +
552 736 if (strlen($date) > 0) :
553 737 $time = new FeedTime($date);
554 - $tstamp = $time->timestamp();
738 + $ts = $time->timestamp();
555 739 elseif ($fallback) : // Fall back to issued / dc:date
556 - $tstamp = $this->published(/*fallback=*/ false, /*default=*/ $default);
740 + $ts = $this->published(/*fallback=*/ false, /*default=*/ $default);
557 741 endif;
558 -
742 +
559 743 # If everything failed, then default to the current time.
560 - if (is_null($tstamp)) :
744 + if (is_null($ts)) :
561 745 if (-1 == $default) :
562 - $tstamp = time();
746 + $ts = time();
563 747 else :
564 - $tstamp = $default;
748 + $ts = $default;
565 749 endif;
566 750 endif;
567 751
568 - if ( ! $unfiltered) :
569 - $tstamp = apply_filters('syndicated_item_updated', $tstamp, $this);
752 + if (!$unfiltered) :
753 + apply_filters('syndicated_item_updated', $ts, $this);
570 754 endif;
571 - return $tstamp;
755 + return $ts;
572 756 } /* SyndicatedPost::updated() */
573 757
574 - var $_hashes = array();
575 - function stored_hashes ($id = NULL) {
576 - if (is_null($id)) :
577 - $id = $this->wp_id();
578 - endif;
579 -
580 - if ( !isset($this->_hashes[$id])) :
581 - $this->_hashes[$id] = get_post_custom_values(
582 - 'syndication_item_hash', $id
583 - );
584 - if (is_null($this->_hashes[$id])) :
585 - $this->_hashes[$id] = array();
586 - endif;
587 - endif;
588 - return $this->_hashes[$id];
589 - }
590 -
591 - function update_hash ($hashed = true) {
592 - // Basis for tracking possible changes to item.
593 - $hash = array(
594 - "title" => $this->entry->get_title(),
595 - "link" => $this->permalink(),
596 - "content" => $this->content(),
597 - "excerpt" => $this->excerpt(),
598 - );
599 -
600 - if ($hashed) :
601 - $hash = md5(serialize($hash));
602 - endif;
603 -
604 - return $hash;
758 + function update_hash () {
759 + return md5(serialize($this->item));
605 760 } /* SyndicatedPost::update_hash() */
606 761
607 - /**
608 - * SyndicatedPost::normalize_guid_prefix(): generates a normalized URL
609 - * prefix (including scheme, authority, full path, and the beginning of
610 - * a query string) for creating guids that conform to WordPress's
611 - * internal constraints on the URL space for valid guids. To create a
612 - * normalized guid, just concatenate a valid URL query parameter value
613 - * to the returned URL.
614 - *
615 - * @return string The URL prefix generated.
616 - *
617 - * @uses trailingslashit()
618 - * @uses home_url()
619 - * @uses apply_filters()
620 - */
621 - static function normalize_guid_prefix () {
622 - $scheme = 'http';
623 - $url = trailingslashit(home_url(/*path=*/ '', $scheme));
624 - return apply_filters('syndicated_item_guid_normalized_prefix', $url . '?guid=', $scheme);
625 - } /* SyndicatedPost::normalize_guid_prefix() */
626 -
627 - static function normalize_guid ($guid) {
628 - $guid = trim($guid);
629 - if (preg_match('/^[0-9a-z]{32}$/i', $guid)) : // MD5
630 - $guid = SyndicatedPost::normalize_guid_prefix().strtolower($guid);
631 - elseif ((strlen(esc_url($guid)) == 0) or (esc_url($guid) != $guid)) :
632 - $guid = SyndicatedPost::normalize_guid_prefix().md5($guid);
633 - endif;
634 - $guid = trim($guid);
635 -
636 - return $guid;
637 - } /* SyndicatedPost::normalize_guid() */
638 -
639 - static function alternative_guid_prefix () {
640 - $scheme = 'https';
641 - $url = trailingslashit(home_url(/*path=*/ '', $scheme));
642 - return apply_filters('syndicated_item_guid_normalized_prefix', $url . '?guid=', $scheme);
643 - }
644 - static function alternative_guid ($guid) {
645 - $guid = trim($guid);
646 - if (preg_match('/^[0-9a-z]{32}$/i', $guid)) : // MD5
647 - $guid = SyndicatedPost::alternative_guid_prefix().strtolower($guid);
648 - elseif ((strlen(esc_url($guid)) == 0) or (esc_url($guid) != $guid)) :
649 - $guid = SyndicatedPost::alternative_guid_prefix().md5($guid);
650 - endif;
651 - $guid = trim($guid);
652 -
653 - return $guid;
654 - } /* SyndicatedPost::normalize_guid() */
655 -
656 - public function guid () {
762 + function guid () {
657 763 $guid = null;
658 - if (isset($this->item['id'])): // Atom 0.3 / 1.0
764 + if (isset($this->item['id'])): // Atom 0.3 / 1.0
659 765 $guid = $this->item['id'];
660 766 elseif (isset($this->item['atom']['id'])) : // Namespaced Atom
661 767 $guid = $this->item['atom']['id'];
662 - elseif (isset($this->item['guid'])) : // RSS 2.0
768 + elseif (isset($this->item['guid'])) : // RSS 2.0
663 769 $guid = $this->item['guid'];
664 770 elseif (isset($this->item['dc']['identifier'])) : // yeah, right
665 771 $guid = $this->item['dc']['identifier'];
666 772 endif;
667 -
773 +
668 774 // Un-set or too long to use as-is. Generate a tag: URI.
669 775 if (is_null($guid) or strlen($guid) > 250) :
670 776 // In case we need to check this again
671 777 $original_guid = $guid;
672 -
778 +
673 779 // The feed does not seem to have provided us with a
674 780 // usable unique identifier, so we'll have to cobble
675 781 // together a tag: URI that might work for us. The base
676 782 // of the URI will be the host name of the feed source ...
@@ -684,19 +790,19 @@
684 790 // gets clipped by MySQL, uniqueness tests will fail
685 791 // forever after and the post will be endlessly
686 792 // reduplicated. So, instead, Guids Of A Certain Length
687 793 // are hashed down into a nice, manageable tag: URI.
688 - if ( !is_null($original_guid)) :
794 + if (!is_null($original_guid)) :
689 795 $guid .= ',2010-12-03:id.'.md5($original_guid);
690 -
796 +
691 797 // If we have a date of creation, then we can use that
692 798 // to uniquely identify the item. (On the other hand, if
693 799 // the feed producer was consicentious enough to
694 800 // generate dates of creation, she probably also was
695 801 // conscientious enough to generate unique identifiers.)
696 - elseif ( !is_null($this->created())) :
802 + elseif (!is_null($this->created())) :
697 803 $guid .= '://post.'.date('YmdHis', $this->created());
698 -
804 +
699 805 // Otherwise, use both the URI of the item, *and* the
700 806 // item's title. We have to use both because titles are
701 807 // often not unique, and sometimes links aren't unique
702 808 // either (e.g. Bitch (S)HITLIST, Mozilla Dot Org news,
@@ -705,86 +811,61 @@
705 811 // this is about the best we can do.
706 812 else :
707 813 $link = $this->permalink();
708 814 if (is_null($link)) : $link = $this->link->uri(); endif;
709 - $guid .= '://'.md5($link.'/'.$this->title());
815 + $guid .= '://'.md5($link.'/'.$this->item['title']);
710 816 endif;
711 817 endif;
712 818 return $guid;
713 819 } /* SyndicatedPost::guid() */
714 -
715 - public function author () {
820 +
821 + function author () {
716 822 $author = array ();
717 -
718 - $aa = $this->entry->get_authors();
719 - if (is_countable($aa) and count($aa) > 0) :
720 - $a = reset($aa);
721 -
722 - $author = array(
723 - 'name' => $a->get_name(),
724 - 'email' => $a->get_email(),
725 - 'uri' => $a->get_link(),
726 - );
727 - endif;
728 -
729 - if (FEEDWORDPRESS_COMPATIBILITY) :
730 - // Search through the MagpieRSS elements: Atom, Dublin Core, RSS
731 - if (isset($this->item['author_name'])):
732 - $author['name'] = $this->item['author_name'];
733 - elseif (isset($this->item['dc']['creator'])):
734 - $author['name'] = $this->item['dc']['creator'];
735 - elseif (isset($this->item['dc']['contributor'])):
736 - $author['name'] = $this->item['dc']['contributor'];
737 - elseif (isset($this->feed->channel['dc']['creator'])) :
738 - $author['name'] = $this->feed->channel['dc']['creator'];
739 - elseif (isset($this->feed->channel['dc']['contributor'])) :
740 - $author['name'] = $this->feed->channel['dc']['contributor'];
741 - elseif (isset($this->feed->channel['author_name'])) :
742 - $author['name'] = $this->feed->channel['author_name'];
743 - elseif ($this->feed->is_rss() and isset($this->item['author'])) :
744 - // The author element in RSS is allegedly an
745 - // e-mail address, but lots of people don't use
746 - // it that way. So let's make of it what we can.
747 - $author = parse_email_with_realname($this->item['author']);
748 -
749 - if ( !isset($author['name'])) :
750 - if (isset($author['email'])) :
751 - $author['name'] = $author['email'];
752 - else :
753 - $author['name'] = $this->feed->channel['title'];
754 - endif;
823 +
824 + if (isset($this->item['author_name'])):
825 + $author['name'] = $this->item['author_name'];
826 + elseif (isset($this->item['dc']['creator'])):
827 + $author['name'] = $this->item['dc']['creator'];
828 + elseif (isset($this->item['dc']['contributor'])):
829 + $author['name'] = $this->item['dc']['contributor'];
830 + elseif (isset($this->feed->channel['dc']['creator'])) :
831 + $author['name'] = $this->feed->channel['dc']['creator'];
832 + elseif (isset($this->feed->channel['dc']['contributor'])) :
833 + $author['name'] = $this->feed->channel['dc']['contributor'];
834 + elseif (isset($this->feed->channel['author_name'])) :
835 + $author['name'] = $this->feed->channel['author_name'];
836 + elseif ($this->feed->is_rss() and isset($this->item['author'])) :
837 + // The author element in RSS is allegedly an
838 + // e-mail address, but lots of people don't use
839 + // it that way. So let's make of it what we can.
840 + $author = parse_email_with_realname($this->item['author']);
841 +
842 + if (!isset($author['name'])) :
843 + if (isset($author['email'])) :
844 + $author['name'] = $author['email'];
845 + else :
846 + $author['name'] = $this->feed->channel['title'];
755 847 endif;
756 848 endif;
849 + elseif ($this->link->name()) :
850 + $author['name'] = $this->link->name();
851 + else :
852 + $url = parse_url($this->link->uri());
853 + $author['name'] = $url['host'];
757 854 endif;
758 -
759 - if ( !isset($author['name']) or is_null($author['name'])) :
760 - // Nothing found. Try some crappy defaults.
761 - if ($this->link->name()) :
762 - $author['name'] = $this->link->name();
763 - else :
764 - $url = parse_url($this->link->uri());
765 - $author['name'] = $url['host'];
766 - endif;
855 +
856 + if (isset($this->item['author_email'])):
857 + $author['email'] = $this->item['author_email'];
858 + elseif (isset($this->feed->channel['author_email'])) :
859 + $author['email'] = $this->feed->channel['author_email'];
767 860 endif;
768 -
769 - if (FEEDWORDPRESS_COMPATIBILITY) :
770 - if (isset($this->item['author_email'])):
771 - $author['email'] = $this->item['author_email'];
772 - elseif (isset($this->feed->channel['author_email'])) :
773 - $author['email'] = $this->feed->channel['author_email'];
774 - endif;
775 -
776 - if (isset($this->item['author_uri'])):
777 - $author['uri'] = $this->item['author_uri'];
778 - elseif (isset($this->item['author_url'])):
779 - $author['uri'] = $this->item['author_url'];
780 - elseif (isset($this->feed->channel['author_uri'])) :
781 - $author['uri'] = $this->item['author_uri'];
782 - elseif (isset($this->feed->channel['author_url'])) :
783 - $author['uri'] = $this->item['author_url'];
784 - elseif (isset($this->feed->channel['link'])) :
785 - $author['uri'] = $this->feed->channel['link'];
786 - endif;
861 +
862 + if (isset($this->item['author_url'])):
863 + $author['uri'] = $this->item['author_url'];
864 + elseif (isset($this->feed->channel['author_url'])) :
865 + $author['uri'] = $this->item['author_url'];
866 + elseif (isset($this->feed->channel['link'])) :
867 + $author['uri'] = $this->feed->channel['link'];
787 868 endif;
788 869
789 870 return $author;
790 871 } /* SyndicatedPost::author() */
@@ -789,121 +870,8 @@
789 870 return $author;
790 871 } /* SyndicatedPost::author() */
791 872
792 873 /**
793 - * SyndicatedPost::get_terms_from_settings(): Return an array of terms to associate with the incoming
794 - * post based on the Categories, Tags, and other terms associated with each new post by the user's
795 - * settings (global and feed-specific).
796 - *
797 - * @since 2016.0331
798 - * @return array of lists, each element has the taxonomy for a key ('category', 'post_tag', etc.),
799 - * and a list of term codes (either alphanumeric names, or ID numbers encoded in a format that
800 - * SyndicatedLink::category_ids() can understand) within that taxonomy
801 - *
802 - */
803 - public function get_terms_from_settings () {
804 - // Categories: start with default categories, if any.
805 - $cats = array();
806 - if ('no' != $this->link->setting('add/category', NULL, 'yes')) :
807 - $fc = get_option("feedwordpress_syndication_cats");
808 - if ($fc) :
809 - $cats = array_merge($cats, explode("\n", $fc));
810 - endif;
811 - endif;
812 -
813 - $fc = $this->link->setting('cats',NULL, array());
814 - if (is_array($fc)) :
815 - $cats = array_merge($cats, $fc);
816 - endif;
817 - $preset_terms['category'] = $cats;
818 -
819 - // Tags: start with default tags, if any
820 - $tags = array();
821 - if ('no' != $this->link->setting('add/post_tag', NULL, 'yes')) :
822 - $ft = get_option("feedwordpress_syndication_tags", NULL);
823 - $tags = (is_null($ft) ? array() : explode(FEEDWORDPRESS_CAT_SEPARATOR, $ft));
824 - endif;
825 -
826 - $ft = $this->link->setting('tags', NULL, array());
827 - if (is_array($ft)) :
828 - $tags = array_merge($tags, $ft);
829 - endif;
830 - $preset_terms['post_tag'] = $tags;
831 -
832 - $taxonomies = $this->link->taxonomies();
833 - $feedTerms = $this->link->setting('terms', NULL, array());
834 - $globalTerms = get_option('feedwordpress_syndication_terms', array());
835 - $specials = array('category' => 'cats', 'post_tag' => 'tags');
836 -
837 - foreach ($taxonomies as $tax) :
838 - // category and tag settings have already previously been handled
839 - // but if this is from another taxonomy, then...
840 - if ( !isset($specials[$tax])) :
841 - $terms = array();
842 -
843 - // See if we should get the globals
844 - if ('no' != $this->link->setting("add/$tax", NULL, 'yes')) :
845 - if (isset($globalTerms[$tax])) :
846 - $terms = $globalTerms[$tax];
847 - endif;
848 - endif;
849 -
850 - // Now merge in the locals
851 - if (isset($feedTerms[$tax])) :
852 - $terms = array_merge($terms, $feedTerms[$tax]);
853 - endif;
854 -
855 - // That's all, folks.
856 - $preset_terms[$tax] = $terms;
857 - endif;
858 - endforeach;
859 -
860 - return $preset_terms;
861 - } /* SyndicatedPost::get_terms_from_settings () */
862 -
863 - /**
864 - * SyndicatedPost::get_terms_from_feeds(): Return an array of terms to associate with the incoming
865 - * post based on the contents of the subscribed feed (atom:category and rss:category elements, dc:subject
866 - * elements, tags embedded using microformats in the post content, etc.)
867 - *
868 - * @since 2016.0331
869 - * @return array of lists, each element has the taxonomy for a key ('category', 'post_tag', etc.),
870 - * and a list of alphanumeric term names
871 - */
872 - public function get_terms_from_feeds () {
873 - // Now add categories from the post, if we have 'em
874 - $cats = array();
875 - $post_cats = $this->entry->get_categories();
876 - if (is_array($post_cats)) : foreach ($post_cats as $cat) :
877 - $cat_name = $cat->get_term();
878 - if ( ! $cat_name) : $cat_name = $cat->get_label(); endif;
879 -
880 - if ($this->link->setting('cat_split', NULL, NULL)) :
881 - $pcre = "\007".$this->feedmeta['cat_split']."\007";
882 - $cats = array_merge(
883 - $cats,
884 - preg_split(
885 - $pcre,
886 - $cat_name,
887 - -1 /*=no limit*/,
888 - PREG_SPLIT_NO_EMPTY
889 - )
890 - );
891 - else :
892 - $cats[] = $cat_name;
893 - endif;
894 - endforeach; endif;
895 -
896 - $feed_terms['category'] = apply_filters('syndicated_item_categories', $cats, $this);
897 -
898 - // Scan post for /a[@rel='tag'] and use as tags if present
899 - $tags = $this->inline_tags();
900 - $feed_terms['post_tag'] = apply_filters('syndicated_item_tags', $tags, $this);
901 -
902 - return $feed_terms;
903 - } /* SyndicatedPost::get_terms_from_feeds () */
904 -
905 - /**
906 874 * SyndicatedPost::inline_tags: Return a list of all the tags embedded
907 875 * in post content using the a[@rel="tag"] microformat.
908 876 *
909 877 * @since 2010.0630
@@ -913,9 +881,9 @@
913 881 $tags = array();
914 882 $content = $this->content();
915 883 $pattern = FeedWordPressHTML::tagWithAttributeRegex('a', 'rel', 'tag');
916 884 preg_match_all($pattern, $content, $refs, PREG_SET_ORDER);
917 - if (is_countable($refs) and count($refs) > 0) :
885 + if (count($refs) > 0) :
918 886 foreach ($refs as $ref) :
919 887 $tag = FeedWordPressHTML::tagWithAttributeMatch($ref);
920 888 $tags[] = $tag['content'];
921 889 endforeach;
@@ -921,9 +889,9 @@
921 889 endforeach;
922 890 endif;
923 891 return $tags;
924 892 }
925 -
893 +
926 894 /**
927 895 * SyndicatedPost::isTaggedAs: Test whether a feed item is
928 896 * tagged / categorized with a given string. Case and leading and
929 897 * trailing whitespace are ignored.
@@ -929,9 +897,9 @@
929 897 * trailing whitespace are ignored.
930 898 *
931 899 * @param string $tag Tag to check for
932 900 *
933 - * @return bool Whether or not at least one of the categories / tags on
901 + * @return bool Whether or not at least one of the categories / tags on
934 902 * $this->item is set to $tag (modulo case and leading and trailing
935 903 * whitespace)
936 904 */
937 905 function isTaggedAs ($tag) {
@@ -944,9 +912,9 @@
944 912 // If we have the new MagpieRSS, the number of category elements
945 913 // on this item is stored under index "category#".
946 914 if (isset($this->item['category#'])) :
947 915 $numberOfCategories = (int) $this->item['category#'];
948 -
916 +
949 917 // We REALLY shouldn't have the old and busted MagpieRSS, but in
950 918 // case we do, it doesn't support multiple categories, but there
951 919 // might still be a single value under the "category" index.
952 920 elseif (isset($this->item['category'])) :
@@ -985,27 +953,27 @@
985 953 * that will be returned to only those with a MIME type which
986 954 * matches this regular expression.
987 955 * @return array
988 956 */
989 - function enclosures( $type = '/.*/' ) {
957 + function enclosures ($type = '/.*/') {
990 958 $enclosures = array();
991 959
992 - if ( isset( $this->item['enclosure#'] ) ) :
960 + if (isset($this->item['enclosure#'])) :
993 961 // Loop through enclosure, enclosure#2, enclosure#3, ....
994 - for ( $i = 1; $i <= $this->item['enclosure#']; $i++ ) :
995 - $eid = ( ( $i > 1 ) ? "#{$i}" : "" ); // this was set as #{$id} — was that a typo? (gwyneth 20230919)
962 + for ($i = 1; $i <= $this->item['enclosure#']; $i++) :
963 + $eid = (($i > 1) ? "#{$id}" : "");
996 964
997 965 // Does it match the type we want?
998 - if ( preg_match($type, $this->item["enclosure{$eid}@type"] ) ) :
966 + if (preg_match($type, $this->item["enclosure{$eid}@type"])) :
999 967 $enclosures[] = array(
1000 - "url" => $this->item["enclosure{$eid}@url"],
1001 - "type" => $this->item["enclosure{$eid}@type"],
968 + "url" => $this->item["enclosure{$eid}@url"],
969 + "type" => $this->item["enclosure{$eid}@type"],
1002 970 "length" => $this->item["enclosure{$eid}@length"],
1003 971 );
1004 972 endif;
1005 973 endfor;
1006 974 endif;
1007 - return $enclosures;
975 + return $enclosures;
1008 976 } /* SyndicatedPost::enclosures() */
1009 977
1010 978 function source ($what = NULL) {
1011 979 $ret = NULL;
@@ -1014,9 +982,9 @@
1014 982 $ret = array();
1015 983 $ret['title'] = $source->get_title();
1016 984 $ret['uri'] = $source->get_link();
1017 985 $ret['feed'] = $source->get_link(0, 'self');
1018 -
986 +
1019 987 if ($id_tags = $source->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'id')) :
1020 988 $ret['id'] = $id_tags[0]['data'];
1021 989 elseif ($id_tags = $source->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'id')) :
1022 990 $ret['id'] = $id_tags[0]['data'];
@@ -1027,18 +995,18 @@
1027 995 elseif ($id_tags = $source->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'guid')) :
1028 996 $ret['id'] = $id_tags[0]['data'];
1029 997 endif;
1030 998 endif;
1031 -
1032 - if ( !is_null($what) and is_scalar($what)) :
999 +
1000 + if (!is_null($what) and is_scalar($what)) :
1033 1001 $ret = $ret[$what];
1034 1002 endif;
1035 1003 return $ret;
1036 1004 }
1037 -
1005 +
1038 1006 function comment_link () {
1039 1007 $url = null;
1040 -
1008 +
1041 1009 // RSS 2.0 has a standard <comments> element:
1042 1010 // "<comments> is an optional sub-element of <item>. If present,
1043 1011 // it is the url of the comments page for the item."
1044 1012 // <http://cyber.law.harvard.edu/rss/rss.html#ltcommentsgtSubelementOfLtitemgt>
@@ -1049,15 +1017,15 @@
1049 1017 // The convention in Atom feeds is to use a standard <link>
1050 1018 // element with @rel="replies" and @type="text/html".
1051 1019 // Unfortunately, SimplePie_Item::get_links() allows us to filter
1052 1020 // by the value of @rel, but not by the value of @type. *sigh*
1053 -
1021 +
1054 1022 // Try Atom 1.0 first
1055 1023 $linkElements = $this->entry->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link');
1056 -
1024 +
1057 1025 // Fall back and try Atom 0.3
1058 1026 if (is_null($linkElements)) : $linkElements = $this->entry->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link'); endif;
1059 -
1027 +
1060 1028 // Now loop through the elements, screening by @rel and @type
1061 1029 if (is_array($linkElements)) : foreach ($linkElements as $link) :
1062 1030 $rel = (isset($link['attribs']['']['rel']) ? $link['attribs']['']['rel'] : 'alternate');
1063 1031 $type = (isset($link['attribs']['']['type']) ? $link['attribs']['']['type'] : NULL);
@@ -1143,15 +1111,14 @@
1143 1111
1144 1112 function resolve_single_relative_uri ($refs) {
1145 1113 $tag = FeedWordPressHTML::attributeMatch($refs);
1146 1114 $url = SimplePie_Misc::absolutize_url($tag['value'], $this->_base);
1147 -
1148 1115 return $tag['prefix'] . $url . $tag['suffix'];
1149 1116 } /* function SyndicatedPost::resolve_single_relative_uri() */
1150 1117
1151 - static function resolve_relative_uris ($content, $obj) {
1118 + function resolve_relative_uris ($content, $obj) {
1152 1119 $set = $obj->link->setting('resolve relative', 'resolve_relative', 'yes');
1153 - if ($set and $set != 'no') :
1120 + if ($set and $set != 'no') :
1154 1121 // Fallback: if we don't have anything better, use the
1155 1122 // item link from the feed
1156 1123 $obj->_base = $obj->permalink(); // Reset the base for resolving relative URIs
1157 1124
@@ -1164,22 +1131,16 @@
1164 1131
1165 1132 foreach ($obj->uri_attrs as $pair) :
1166 1133 list($tag, $attr) = $pair;
1167 1134 $pattern = FeedWordPressHTML::attributeRegex($tag, $attr);
1168 -
1169 - // FIXME: Encountered issue while testing an extremely long (= 88827 characters) item
1170 - // Relying on preg_replace_callback() here can cause a PHP seg fault on my development
1171 - // server. preg_match_all() causes a similar problem. Apparently this is a PCRE issue
1172 - // Cf. discussion of similar issue <https://bugs.php.net/bug.php?id=65009>
1173 1135 $content = preg_replace_callback (
1174 1136 $pattern,
1175 - array($obj, 'resolve_single_relative_uri'),
1137 + array(&$obj, 'resolve_single_relative_uri'),
1176 1138 $content
1177 1139 );
1178 -
1179 1140 endforeach;
1180 1141 endif;
1181 -
1142 +
1182 1143 return $content;
1183 1144 } /* function SyndicatedPost::resolve_relative_uris () */
1184 1145
1185 1146 var $strip_attrs = array (
@@ -1192,9 +1153,9 @@
1192 1153 $tag = FeedWordPressHTML::attributeMatch($refs);
1193 1154 return $tag['before_attribute'].$tag['after_attribute'];
1194 1155 }
1195 1156
1196 - static function sanitize_content ($content, $obj) {
1157 + function sanitize_content ($content, $obj) {
1197 1158 # This kind of sucks. I intend to replace it with
1198 1159 # lib_filter sometime soon.
1199 1160 foreach ($obj->strip_attrs as $pair):
1200 1161 list($tag,$attr) = $pair;
@@ -1201,9 +1162,9 @@
1201 1162 $pattern = FeedWordPressHTML::attributeRegex($tag, $attr);
1202 1163
1203 1164 $content = preg_replace_callback (
1204 1165 $pattern,
1205 - array($obj, 'strip_attribute_from_tag'),
1166 + array(&$obj, 'strip_attribute_from_tag'),
1206 1167 $content
1207 1168 );
1208 1169 endforeach;
1209 1170 return $content;
@@ -1229,235 +1190,75 @@
1229 1190 * match the latest revision, or a previously syndicated post that is
1230 1191 * still up-to-date.
1231 1192 *
1232 1193 * @return int A status code representing the freshness of the post
1233 - * -1 = post already syndicated; has a revision that needs to be stored, but not updated to
1234 1194 * 0 = post already syndicated; no update needed
1235 1195 * 1 = post already syndicated, but needs to be updated to latest
1236 1196 * 2 = post has not yet been syndicated; needs to be created
1237 1197 */
1238 - function freshness ($format = 'number') {
1198 + function freshness () {
1199 + global $wpdb;
1239 1200
1240 1201 if ($this->filtered()) : // This should never happen.
1241 - FeedWordPressDiagnostic::critical_bug('SyndicatedPost', $this, __LINE__, __FILE__);
1202 + FeedWordPress::critical_bug('SyndicatedPost', $this, __LINE__);
1242 1203 endif;
1204 +
1205 + if (is_null($this->_freshness)) :
1206 + $guid = $wpdb->escape($this->guid());
1243 1207
1244 - if (is_null($this->_freshness)) : // Not yet checked and cached.
1245 - $guid = $this->post['guid'];
1246 - $eguid = esc_sql($this->post['guid']);
1208 + $result = $wpdb->get_row("
1209 + SELECT id, guid, post_modified_gmt
1210 + FROM $wpdb->posts WHERE guid='$guid'
1211 + ");
1247 1212
1248 - $q = new WP_Query(array(
1249 - 'fields' => '_synfresh', // id, guid, post_modified_gmt
1250 - 'ignore_sticky_posts' => true,
1251 - 'guid' => $eguid, // it's always better to use the escaped version, right? (gwyneth 20230915)
1252 - ));
1253 -
1254 - $old_post = NULL;
1255 - if ($q->have_posts()) :
1256 - while ($q->have_posts()) : $q->the_post();
1257 - if (get_post_type($q->post->ID) == $this->post['post_type']):
1258 - $old_post = $q->post;
1259 - endif;
1260 - endwhile;
1261 - endif;
1262 -
1263 - if (is_null($old_post)) : // No post with this guid
1264 - FeedWordPress::diagnostic('feed_items:freshness', 'Item ['.$guid.'] "'.$this->entry->get_title().'" is a NEW POST.');
1213 + if (!$result) :
1265 1214 $this->_wp_id = NULL;
1266 1215 $this->_freshness = 2; // New content
1267 - else :
1268 - // Presume there is nothing new until we find
1269 - // something new.
1270 - $updated = false;
1271 - $live = false;
1216 + else:
1217 + preg_match('/([0-9]+)-([0-9]+)-([0-9]+) ([0-9]+):([0-9]+):([0-9]+)/', $result->post_modified_gmt, $backref);
1272 1218
1273 - // Pull the list of existing revisions to get
1274 - // timestamps.
1275 - $revisions = wp_get_post_revisions($old_post->ID);
1276 - foreach ($revisions as $rev) :
1277 - $revisions_ts[] = mysql2date('G', $rev->post_modified_gmt);
1278 - endforeach;
1279 -
1280 - $revisions_ts[] = mysql2date('G', $old_post->post_modified_gmt);
1281 - $last_rev_ts = end($revisions_ts);
1219 + $last_rev_ts = gmmktime($backref[4], $backref[5], $backref[6], $backref[2], $backref[3], $backref[1]);
1282 1220 $updated_ts = $this->updated(/*fallback=*/ true, /*default=*/ NULL);
1283 -
1284 - // If we have an explicit updated timestamp,
1285 - // check that against existing stamps.
1286 - if ( !is_null($updated_ts)) :
1287 - $updated = !in_array($updated_ts, $revisions_ts);
1288 -
1289 - // If this a newer revision, make it go
1290 - // live. If an older one, just record
1291 - // the contents.
1292 - $live = ($updated and ($updated_ts > $last_rev_ts));
1293 - endif;
1294 -
1295 - // This is a revision we haven't seen before, judging by the date.
1296 -
1297 - $updatedReason = NULL;
1298 - if ($updated) :
1299 - $updatedReason = preg_replace(
1300 - "/\s+/", " ",
1301 - 'has been marked with a new timestamp ('
1302 - .date('Y-m-d H:i:s', $updated_ts)
1303 - ." > "
1304 - .date('Y-m-d H:i:s', $last_rev_ts)
1305 - .')'
1306 - );
1307 -
1308 - // The date does not indicate a new revision, so
1309 - // let's check the hash.
1310 - else :
1221 +
1222 + // Check timestamps...
1223 + $updated = (
1224 + !is_null($updated_ts)
1225 + and ($updated_ts > $last_rev_ts)
1226 + );
1227 +
1228 + if (!$updated) :
1311 1229 // Or the hash...
1312 - $hash = $this->update_hash();
1313 - $seen = $this->stored_hashes($old_post->ID);
1314 - if (is_countable($seen) and count($seen) > 0) :
1315 - $updated = !in_array($hash, $seen); // Not seen yet?
1230 + $stored_update_hashes = get_post_custom_values('syndication_item_hash', $result->id);
1231 + if (count($stored_update_hashes) > 0) :
1232 + $stored_update_hash = $stored_update_hashes[0];
1233 + $updated = ($stored_update_hash != $this->update_hash());
1316 1234 else :
1317 1235 $updated = true; // Can't find syndication meta-data
1318 1236 endif;
1319 -
1320 - if ($updated and FeedWordPressDiagnostic::is_on('feed_items:freshness:reasons')) :
1321 - // In the absence of definitive
1322 - // timestamp information, we
1323 - // just have to assume that a
1324 - // hash we haven't seen before
1325 - // is a newer version.
1326 - $live = true;
1327 -
1328 - $updatedReason = ' has a not-yet-seen update hash: '
1329 - .MyPHP::val($hash)
1330 - .' not in {'
1331 - .implode(", ", array_map(array('FeedWordPress', 'val'), $seen))
1332 - .'}. Basis: '
1333 - .MyPHP::val(array_keys($this->update_hash(false)));
1334 - endif;
1335 1237 endif;
1336 -
1238 +
1337 1239 $frozen = false;
1338 1240 if ($updated) : // Ignore if the post is frozen
1339 1241 $frozen = ('yes' == $this->link->setting('freeze updates', 'freeze_updates', NULL));
1340 - if ( ! $frozen) :
1341 - $frozen_value = get_post_meta($old_post->ID, '_syndication_freeze_updates', /*single=*/ true);
1342 - $frozen = ( !is_null($frozen_value) and ('yes' == $frozen_value));
1343 -
1344 - if ($frozen) :
1345 - $updatedReason = ' IS BLOCKED FROM BEING UPDATED BY A UPDATE LOCK ON THIS POST, EVEN THOUGH IT '.$updatedReason;
1346 - endif;
1347 - else :
1348 - $updatedReason = ' IS BLOCKED FROM BEING UPDATED BY A FEEDWORDPRESS UPDATE LOCK, EVEN THOUGH IT '.$updatedReason;
1242 + if (!$frozen) :
1243 + $frozen_values = get_post_custom_values('_syndication_freeze_updates', $result->id);
1244 + $frozen = (count($frozen_values) > 0 and 'yes' == $frozen_values[0]);
1349 1245 endif;
1350 1246 endif;
1351 - $live = ($live and ! $frozen);
1247 + $updated = ($updated and !$frozen);
1352 1248
1353 1249 if ($updated) :
1354 - FeedWordPress::diagnostic('feed_items:freshness', 'Item ['.$guid.'] "'.$this->entry->get_title().'" is an update of an existing post.');
1355 - if ( !is_null($updatedReason)) :
1356 - $updatedReason = preg_replace('/\s+/', ' ', $updatedReason);
1357 - FeedWordPress::diagnostic('feed_items:freshness:reasons', 'Item ['.$guid.'] "'.$this->entry->get_title().'" '.$updatedReason);
1358 - endif;
1359 -
1360 - $this->_freshness = apply_filters('syndicated_item_freshness', ($live ? 1 : -1), $updated, $frozen, $updated_ts, $last_rev_ts, $this);
1361 -
1362 - $this->_wp_id = $old_post->ID;
1363 - $this->_wp_post = $old_post;
1364 -
1365 - // We want this to keep a running list of all the
1366 - // processed update hashes.
1367 - $this->post['meta']['syndication_item_hash'] = array_merge(
1368 - $this->stored_hashes(),
1369 - array($this->update_hash())
1370 - );
1250 + $this->_freshness = 1; // Updated content
1251 + $this->_wp_id = $result->id;
1371 1252 else :
1372 - FeedWordPress::diagnostic('feed_items:freshness', 'Item ['.$guid.'] "'.$this->entry->get_title().'" is a duplicate of an existing post.');
1373 1253 $this->_freshness = 0; // Same old, same old
1374 - $this->_wp_id = $old_post->ID;
1254 + $this->_wp_id = $result->id;
1375 1255 endif;
1376 1256 endif;
1377 1257 endif;
1378 -
1379 - switch ($format) :
1380 - case 'status' :
1381 - switch ($this->_freshness) :
1382 - case -1:
1383 - $ret = 'stored';
1384 - break;
1385 - case 0:
1386 - $ret = NULL;
1387 - break;
1388 - case 1:
1389 - $ret = 'updated';
1390 - break;
1391 - case 2:
1392 - default:
1393 - $ret = 'new';
1394 - break;
1395 - endswitch;
1396 - break;
1397 - case 'number' :
1398 - default :
1399 - $ret = $this->_freshness;
1400 - endswitch;
1401 -
1402 -
1403 - return $ret;
1404 - } /* SyndicatedPost::freshness () */
1405 -
1406 - function has_fresh_content () {
1407 - return ( ! $this->filtered() and $this->freshness() != 0 );
1408 - } /* SyndicatedPost::has_fresh_content () */
1409 -
1410 - function this_revision_needs_original_post ($freshness = NULL) {
1411 - if (is_null($freshness)) :
1412 - $freshness = $this->freshness();
1413 - endif;
1414 - return ( $freshness >= 2 );
1258 + return $this->_freshness;
1415 1259 }
1416 1260
1417 - function this_revision_is_current ($freshness = NULL) {
1418 - if (is_null($freshness)) :
1419 - $freshness = $this->freshness();
1420 - endif;
1421 - return ( $freshness >= 1 );
1422 - } /* SyndicatedPost::this_revision_is_current () */
1423 -
1424 - function fresh_content_is_update () {
1425 - return ($this->freshness() < 2);
1426 - } /* SyndicatedPost::fresh_content_is_update () */
1427 -
1428 - function fresh_storage_diagnostic () {
1429 - $ret = NULL;
1430 - switch ($this->freshness()) :
1431 - case -1 :
1432 - $ret = 'Storing alternate revision of existing post # '.$this->wp_id().', "'.$this->post['post_title'].'"';
1433 - break;
1434 - case 1 :
1435 - $ret = 'Updating existing post # '.$this->wp_id().', "'.$this->post['post_title'].'"';
1436 - break;
1437 - case 2 :
1438 - default :
1439 - $ret = 'Inserting new post "'.$this->post['post_title'].'"';
1440 - break;
1441 - endswitch;
1442 - return $ret;
1443 - } /* SyndicatedPost::fresh_storage_diagnostic() */
1444 -
1445 - function fresh_storage_hook () {
1446 - $ret = NULL;
1447 - switch ($this->freshness()) :
1448 - case -1 :
1449 - case 1 :
1450 - $ret = 'update_syndicated_item';
1451 - break;
1452 - case 2 :
1453 - default :
1454 - $ret = 'post_syndicated_item';
1455 - break;
1456 - endswitch;
1457 - return $ret;
1458 - } /* SyndicatedPost::fresh_storage_hook () */
1459 -
1460 1261 #################################################
1461 1262 #### INTERNAL STORAGE AND MANAGEMENT METHODS ####
1462 1263 #################################################
1463 1264
@@ -1462,11 +1263,11 @@
1462 1263 #################################################
1463 1264
1464 1265 function wp_id () {
1465 1266 if ($this->filtered()) : // This should never happen.
1466 - FeedWordPressDiagnostic::critical_bug('SyndicatedPost', $this, __LINE__, __FILE__);
1267 + FeedWordPress::critical_bug('SyndicatedPost', $this, __LINE__);
1467 1268 endif;
1468 -
1269 +
1469 1270 if (is_null($this->_wp_id) and is_null($this->_freshness)) :
1470 1271 $fresh = $this->freshness(); // sets WP DB id in the process
1471 1272 endif;
1472 1273 return $this->_wp_id;
@@ -1471,183 +1272,113 @@
1471 1272 endif;
1472 1273 return $this->_wp_id;
1473 1274 }
1474 1275
1475 - /**
1476 - * SyndicatedPost::secure_author_id(). Look up, or create, a numeric ID
1477 - * for the author of the incoming post.
1478 - *
1479 - * side effect: int|NULL stored in $this->post['post_author']
1480 - * side effect: IF no valid author is found, NULL stored in $this->post
1481 - * side effect: diagnostic output in case item is rejected with NULL author
1482 - *
1483 - * @used-by SyndicatedPost::store
1484 - *
1485 - * @uses SyndicatedPost::post
1486 - * @uses SyndicatedPost::author_id
1487 - * @uses SyndicatedLink::setting
1488 - * @uses FeedWordPress::diagnostic
1489 - */
1490 - protected function secure_author_id () {
1491 - # -- Look up, or create, numeric ID for author
1492 - $this->post['post_author'] = $this->author_id (
1493 - $this->link->setting('unfamiliar author', 'unfamiliar_author', 'create')
1494 - );
1276 + function store () {
1277 + global $wpdb;
1495 1278
1496 - if (is_null($this->post['post_author'])) :
1497 - FeedWordPress::diagnostic('feed_items:rejected', 'Filtered out item ['.$this->guid().'] without syndication: no author available');
1498 - $this->post = NULL;
1279 + if ($this->filtered()) : // This should never happen.
1280 + FeedWordPress::critical_bug('SyndicatedPost', $this, __LINE__);
1499 1281 endif;
1500 - } /* SyndicatedPost::secure_author_id() */
1282 +
1283 + $freshness = $this->freshness();
1284 + if ($freshness > 0) :
1285 + # -- Look up, or create, numeric ID for author
1286 + $this->post['post_author'] = $this->author_id (
1287 + $this->link->setting('unfamiliar author', 'unfamiliar_author', 'create')
1288 + );
1501 1289
1502 - /**
1503 - * SyndicatedPost::secure_term_ids(). Look up, or create, numeric IDs
1504 - * for the terms (categories, tags, etc.) assigned to the incoming post,
1505 - * whether by global settings, feed settings, or by the tags on the feed.
1506 - *
1507 - * side effect: array of term ids stored in $this->post['tax_input']
1508 - * side effect: IF settings or filters determine post should be filtered out,
1509 - * NULL stored in $this->post
1510 - *
1511 - * @used-by SyndicatedPost::store
1512 - *
1513 - * @uses apply_filters
1514 - * @uses SyndicatedLink::setting
1515 - * @uses SyndicatedPost::category_ids
1516 - * @uses SyndicatedPost::preset_terms
1517 - * @uses SyndicatedPost::post
1518 - */
1519 - protected function secure_term_ids () {
1520 - $mapping = apply_filters('syndicated_post_terms_mapping', array(
1521 - 'category' => array('abbr' => 'cats', 'unfamiliar' => 'category', 'domain' => array('category', 'post_tag')),
1522 - 'post_tag' => array('abbr' => 'tags', 'unfamiliar' => 'post_tag', 'domain' => array('post_tag')),
1523 - ), $this);
1524 -
1525 - $termSet = array(); $valid = null;
1526 - foreach ($this->feed_terms as $what => $anTerms) :
1527 - // Default to using the inclusive procedures (for cats) rather than exclusive (for inline tags)
1528 - $taxes = (isset($mapping[$what]) ? $mapping[$what] : $mapping['category']);
1529 - $unfamiliar = $taxes['unfamiliar'];
1530 -
1531 - if ( !is_null($this->post)) : // Not filtered out yet
1532 - # -- Look up, or create, numeric ID for categories
1533 - $taxonomies = $this->link->setting("match/".$taxes['abbr'], 'match_'.$taxes['abbr'], $taxes['domain']);
1534 -
1535 - // Eliminate dummy variables
1536 - $taxonomies = array_filter( $taxonomies, 'remove_dummy_zero' );
1537 -
1538 - // Allow FWP add-on filters to control the taxonomies we use to search for a term
1539 - $taxonomies = apply_filters("syndicated_post_terms_match", $taxonomies, $what, $this);
1540 - $taxonomies = apply_filters("syndicated_post_terms_match_{$what}", $taxonomies, $this);
1541 -
1542 - // Allow FWP add-on filters to control with greater precision what happens on unmatched
1543 - $unmatched = apply_filters("syndicated_post_terms_unfamiliar",
1544 - $this->link->setting(
1545 - "unfamiliar {$unfamiliar}",
1546 - "unfamiliar_{$unfamiliar}",
1547 - 'create:'.$unfamiliar
1548 - ),
1549 - $what,
1550 - $this
1551 - );
1552 -
1553 - $terms = $this->category_ids (
1554 - $anTerms,
1555 - $unmatched,
1556 - /*taxonomies=*/ $taxonomies,
1557 - array(
1558 - 'singleton' => false, // I don't like surprises
1559 - 'filters' => true,
1560 - )
1561 - );
1562 -
1563 - if (is_null($terms) or is_null($termSet)) :
1564 - // filtered out -- no matches
1565 - else :
1566 - $valid = true;
1567 -
1568 - // filter mode off, or at least one match
1569 - foreach ($terms as $tax => $term_ids) :
1570 - if ( !isset($termSet[$tax])) :
1571 - $termSet[$tax] = array();
1572 - endif;
1573 - $termSet[$tax] = array_merge($termSet[$tax], $term_ids);
1574 - endforeach;
1575 - endif;
1290 + if (is_null($this->post['post_author'])) :
1291 + $this->post = NULL;
1576 1292 endif;
1577 - endforeach;
1293 + endif;
1294 +
1295 + if (!$this->filtered() and $freshness > 0) :
1296 + $consider = array(
1297 + 'category' => array('abbr' => 'cats', 'domain' => array('category', 'post_tag')),
1298 + 'post_tag' => array('abbr' => 'tags', 'domain' => array('post_tag')),
1299 + );
1578 1300
1579 - if (is_null($valid)) : // Plonked
1580 - $this->post = NULL;
1581 - else : // We can proceed
1582 - $this->post['tax_input'] = array();
1583 - foreach ($termSet as $tax => $term_ids) :
1584 - if ( !isset($this->post['tax_input'][$tax])) :
1585 - $this->post['tax_input'][$tax] = array();
1586 - endif;
1587 - $this->post['tax_input'][$tax] = array_merge(
1588 - $this->post['tax_input'][$tax],
1589 - $term_ids
1590 - );
1591 - endforeach;
1301 + $termSet = array(); $valid = null;
1302 + foreach ($consider as $what => $taxes) :
1303 + if (!is_null($this->post)) : // Not filtered out yet
1304 + # -- Look up, or create, numeric ID for categories
1305 + $taxonomies = $this->link->setting("match/".$taxes['abbr'], 'match_'.$taxes['abbr'], $taxes['domain']);
1306 +
1307 + // Eliminate dummy variables
1308 + $taxonomies = array_filter($taxonomies, 'remove_dummy_zero');
1309 +
1310 + $terms = $this->category_ids (
1311 + $this->feed_terms[$what],
1312 + $this->link->setting("unfamiliar {$what}", "unfamiliar_{$what}", 'create:'.$what),
1313 + /*taxonomies=*/ $taxonomies,
1314 + array(
1315 + 'singleton' => false, // I don't like surprises
1316 + 'filters' => true,
1317 + )
1318 + );
1319 +
1320 + if (is_null($terms) or is_null($termSet)) :
1321 + // filtered out -- no matches
1322 + else :
1323 + $valid = true;
1592 1324
1593 - // Now let's add on the feed and global presets
1594 - foreach ($this->preset_terms as $tax => $term_ids) :
1595 - if ( !isset($this->post['tax_input'][$tax])) :
1596 - $this->post['tax_input'][$tax] = array();
1325 + // filter mode off, or at least one match
1326 + foreach ($terms as $tax => $term_ids) :
1327 + if (!isset($termSet[$tax])) :
1328 + $termSet[$tax] = array();
1329 + endif;
1330 + $termSet[$tax] = array_merge($termSet[$tax], $term_ids);
1331 + endforeach;
1332 + endif;
1597 1333 endif;
1598 -
1599 - $this->post['tax_input'][$tax] = array_merge (
1600 - $this->post['tax_input'][$tax],
1601 - $this->category_ids (
1602 - /*terms=*/ $term_ids,
1603 - /*unfamiliar=*/ 'create:'.$tax, // These are presets; for those added in a tagbox editor, the tag may not yet exist
1604 - /*taxonomies=*/ array($tax),
1605 - array(
1606 - 'singleton' => true,
1607 - ))
1608 - );
1609 1334 endforeach;
1610 - endif;
1611 - } /* SyndicatedPost::secure_term_ids() */
1612 1335
1613 - /**
1614 - * SyndicatedPost::store
1615 - *
1616 - * @uses SyndicatedPost::secure_author_id
1617 - */
1618 - public function store () {
1619 - if ($this->filtered()) : // This should never happen.
1620 - FeedWordPressDiagnostic::critical_bug('SyndicatedPost', $this, __LINE__, __FILE__);
1621 - endif;
1336 + if (is_null($valid)) : // Plonked
1337 + $this->post = NULL;
1338 + else : // We can proceed
1339 + $this->post['tax_input'] = array();
1340 + foreach ($termSet as $tax => $term_ids) :
1341 + if (!isset($this->post['tax_input'][$tax])) :
1342 + $this->post['tax_input'][$tax] = array();
1343 + endif;
1344 + $this->post['tax_input'][$tax] = array_merge(
1345 + $this->post['tax_input'][$tax],
1346 + $term_ids
1347 + );
1348 + endforeach;
1622 1349
1623 - $freshness = $this->freshness(); // see above: this sets WP DB id in the process (gwyneth 20230919)
1624 - if ($this->has_fresh_content()) :
1625 - $this->secure_author_id();
1350 + // Now let's add on the feed and global presets
1351 + foreach ($this->preset_terms as $tax => $term_ids) :
1352 + if (!isset($this->post['tax_input'][$tax])) :
1353 + $this->post['tax_input'][$tax] = array();
1354 + endif;
1355 +
1356 + $this->post['tax_input'][$tax] = array_merge (
1357 + $this->post['tax_input'][$tax],
1358 + $this->category_ids (
1359 + /*terms=*/ $term_ids,
1360 + /*unfamiliar=*/ 'create:'.$tax, // These are presets; for those added in a tagbox editor, the tag may not yet exist
1361 + /*taxonomies=*/ array($tax),
1362 + array(
1363 + 'singleton' => true,
1364 + ))
1365 + );
1366 + endforeach;
1367 + endif;
1626 1368 endif;
1627 -
1628 - if ($this->has_fresh_content()) : // Was this filtered during author_id lookup?
1629 - $this->secure_term_ids();
1630 - endif;
1631 -
1632 - // We have to check again in case the post has been filtered
1633 - // during the category/tags/taxonomy terms lookup
1634 - if ($this->has_fresh_content()) :
1369 +
1370 + if (!$this->filtered() and $freshness > 0) :
1635 1371 // Filter some individual fields
1636 -
1637 - // If there already is a post slug (from syndication or by manual
1638 - // editing) don't cause WP to overwrite it by sending in a NULL
1639 - // post_name. Props Chris Fritz 2012-11-28.
1640 - $post_name = (is_null($this->_wp_post) ? NULL : $this->_wp_post->post_name);
1641 -
1372 +
1642 1373 // Allow filters to set post slug. Props niska.
1643 - $post_name = apply_filters('syndicated_post_slug', $post_name, $this);
1644 - if ( !empty($post_name)) :
1374 + $post_name = apply_filters('syndicated_post_slug', NULL, $this);
1375 + if (!empty($post_name)) :
1645 1376 $this->post['post_name'] = $post_name;
1646 1377 endif;
1647 -
1378 +
1648 1379 $this->post = apply_filters('syndicated_post', $this->post, $this);
1649 -
1380 +
1650 1381 // Allow for feed-specific syndicated_post filters.
1651 1382 $this->post = apply_filters(
1652 1383 "syndicated_post_".$this->link->uri(),
1653 1384 $this->post,
@@ -1653,56 +1384,39 @@
1653 1384 $this->post,
1654 1385 $this
1655 1386 );
1656 1387 endif;
1657 -
1388 +
1658 1389 // Hook in early to make sure these get inserted if at all possible
1659 1390 add_action(
1660 1391 /*hook=*/ 'transition_post_status',
1661 - /*callback=*/ array($this, 'add_rss_meta'),
1392 + /*callback=*/ array(&$this, 'add_rss_meta'),
1662 1393 /*priority=*/ -10000, /* very early */
1663 1394 /*arguments=*/ 3
1664 1395 );
1665 1396
1397 + $retval = array(1 => 'updated', 2 => 'new');
1398 +
1666 1399 $ret = false;
1667 - if ($this->has_fresh_content()) :
1668 - $diag = $this->fresh_storage_diagnostic();
1669 - if ( !is_null($diag)) :
1670 - FeedWordPress::diagnostic('syndicated_posts', $diag);
1671 - endif;
1400 + if (!$this->filtered() and isset($retval[$freshness])) :
1401 + $diag = array(
1402 + 1 => 'Updating existing post # '.$this->wp_id().', "'.$this->post['post_title'].'"',
1403 + 2 => 'Inserting new post "'.$this->post['post_title'].'"',
1404 + );
1405 + FeedWordPress::diagnostic('syndicated_posts', $diag[$freshness]);
1672 1406
1673 - $this->insert_post(/*update=*/ $this->fresh_content_is_update(), $this->freshness());
1407 + $this->insert_post(/*update=*/ ($freshness == 1));
1408 +
1409 + $hook = array( 1 => 'update_syndicated_item', 2 => 'post_syndicated_item' );
1410 + do_action($hook[$freshness], $this->wp_id(), $this);
1674 1411
1675 - $hook = $this->fresh_storage_hook();
1676 - if ( !is_null($hook)) :
1677 - do_action($hook, $this->wp_id(), $this);
1678 - endif;
1679 -
1680 - $ret = $this->freshness('status');
1412 + $ret = $retval[$freshness];
1681 1413 endif;
1682 1414
1683 - // If this is a legit, non-filtered post, tag it as found on the
1684 - // feed regardless of fresh or stale status
1685 - if ( ! $this->filtered()) :
1686 - $key = '_feedwordpress_retire_me_' . $this->link->id;
1687 - delete_post_meta($this->wp_id(), $key);
1688 -
1689 - $status = get_post_field('post_status', $this->wp_id());
1690 - if ('fwpretired'==$status and $this->link->is_non_incremental()) :
1691 - FeedWordPress::diagnostic('syndicated_posts', "Un-retiring previously retired post # ".$this->wp_id()." due to re-appearance on non-incremental feed.");
1692 - set_post_field('post_status', $this->post['post_status'], $this->wp_id());
1693 - wp_transition_post_status($this->post['post_status'], $status, $this->post);
1694 - elseif ('fwpzapped'==$status) :
1695 - // Set this new revision up to be
1696 - // blanked on the next update.
1697 - add_post_meta($this->wp_id(), '_feedwordpress_zapped_blank_me', 2, /*single=*/ true);
1698 - endif;
1699 - endif;
1700 -
1701 1415 // Remove add_rss_meta hook
1702 1416 remove_action(
1703 1417 /*hook=*/ 'transition_post_status',
1704 - /*callback=*/ array($this, 'add_rss_meta'),
1418 + /*callback=*/ array(&$this, 'add_rss_meta'),
1705 1419 /*priority=*/ -10000, /* very early */
1706 1420 /*arguments=*/ 3
1707 1421 );
1708 1422
@@ -1707,56 +1421,48 @@
1707 1421 );
1708 1422
1709 1423 return $ret;
1710 1424 } /* function SyndicatedPost::store () */
1425 +
1426 + function insert_post ($update = false) {
1427 + global $wpdb;
1711 1428
1712 - function insert_post ($update = false, $freshness = 2) {
1713 1429 $dbpost = $this->normalize_post(/*new=*/ true);
1714 -
1715 - $ret = null;
1716 -
1717 - if ( !is_null($dbpost)) :
1430 + if (!is_null($dbpost)) :
1718 1431 $dbpost['post_pingback'] = false; // Tell WP 2.1 and 2.2 not to process for pingbacks
1719 -
1432 +
1720 1433 // This is a ridiculous fucking kludge necessitated by WordPress 2.6 munging authorship meta-data
1721 1434 add_action('_wp_put_post_revision', array($this, 'fix_revision_meta'));
1722 -
1435 +
1723 1436 // Kludge to prevent kses filters from stripping the
1724 1437 // content of posts when updating without a logged in
1725 1438 // user who has `unfiltered_html` capability.
1726 - $mungers = array('wp_filter_kses', 'wp_filter_post_kses');
1727 - $removed = array();
1728 - foreach ($mungers as $munger) :
1729 - if (has_filter('content_save_pre', $munger)) :
1730 - remove_filter('content_save_pre', $munger);
1731 - $removed[] = $munger;
1732 - endif;
1733 - endforeach;
1734 -
1439 + add_filter('content_save_pre', array($this, 'avoid_kses_munge'), 11);
1440 +
1735 1441 if ($update and function_exists('get_post_field')) :
1736 1442 // Don't munge status fields that the user may
1737 1443 // have reset manually
1738 1444 $doNotMunge = array('post_status', 'comment_status', 'ping_status');
1739 -
1445 +
1740 1446 foreach ($doNotMunge as $field) :
1741 1447 $dbpost[$field] = get_post_field($field, $this->wp_id());
1742 1448 endforeach;
1743 1449 endif;
1744 -
1450 +
1745 1451 // WP3's wp_insert_post scans current_user_can() for the
1746 1452 // tax_input, with no apparent way to override. Ugh.
1747 1453 add_action(
1748 1454 /*hook=*/ 'transition_post_status',
1749 - /*callback=*/ array($this, 'add_terms'),
1455 + /*callback=*/ array(&$this, 'add_terms'),
1750 1456 /*priority=*/ -10001, /* very early */
1751 1457 /*arguments=*/ 3
1752 1458 );
1753 -
1459 +
1754 1460 // WP3 appears to override whatever you give it for
1755 1461 // post_modified. Ugh.
1756 1462 add_action(
1757 1463 /*hook=*/ 'transition_post_status',
1758 - /*callback=*/ array($this, 'fix_post_modified_ts'),
1464 + /*callback=*/ array(&$this, 'fix_post_modified_ts'),
1759 1465 /*priority=*/ -10000, /* very early */
1760 1466 /*arguments=*/ 3
1761 1467 );
1762 1468
@@ -1763,57 +1469,13 @@
1763 1469 if ($update) :
1764 1470 $this->post['ID'] = $this->wp_id();
1765 1471 $dbpost['ID'] = $this->post['ID'];
1766 1472 endif;
1473 + $this->_wp_id = wp_insert_post($dbpost);
1767 1474
1768 - // O.K., is this a new post? If so, we need to create
1769 - // the basic post record before we do anything else.
1770 - if ($this->this_revision_needs_original_post()) :
1771 - // *sigh*, for handling inconsistent slash expectations < 3.6
1772 - $sdbpost = $this->db_sanitize_post($dbpost);
1773 -
1774 - // Go ahead and insert the first post record to
1775 - // anchor the revision history.
1776 -
1777 - $this->_wp_id = wp_insert_post($sdbpost, /*return wp_error=*/ true);
1778 -
1779 - $dbpost['ID'] = $this->_wp_id;
1780 - endif;
1781 -
1782 - // Sanity check: if the attempt to insert post
1783 - // returned an error, then feeding that error
1784 - // object in to _wp_put_post_revision() would
1785 - // cause a fatal error. Better to break out.
1786 - if ( !is_wp_error($this->_wp_id)) :
1787 - // Now that we've made sure the original exists, insert
1788 - // this version here as a revision.
1789 - $revision_id = _wp_put_post_revision($dbpost, /*autosave=*/ false);
1790 -
1791 - if ( ! $this->this_revision_needs_original_post()) :
1792 -
1793 - if ($this->this_revision_is_current()) :
1794 -
1795 - wp_restore_post_revision($revision_id);
1796 -
1797 - else :
1798 -
1799 - // If we do not activate this revision, then the
1800 - // add_rss_meta will not be called, which is
1801 - // more or less as it should be, but that means
1802 - // we have to actively record this revision's
1803 - // update hash from here.
1804 - $postId = $this->post['ID'];
1805 - $key = 'syndication_item_hash';
1806 - $hash = $this->update_hash();
1807 - FeedWordPress::diagnostic('syndicated_posts:meta_data', "Adding post meta-datum to post [$postId]: [$key] = ".FeedWordPress::val($hash, /*no newlines=*/ true));
1808 - add_post_meta( $postId, $key, $hash, /*unique=*/ false );
1809 - endif;
1810 - endif;
1811 - endif;
1812 -
1813 1475 remove_action(
1814 1476 /*hook=*/ 'transition_post_status',
1815 - /*callback=*/ array($this, 'add_terms'),
1477 + /*callback=*/ array(&$this, 'add_terms'),
1816 1478 /*priority=*/ -10001, /* very early */
1817 1479 /*arguments=*/ 3
1818 1480 );
1819 1481
@@ -1818,9 +1480,9 @@
1818 1480 );
1819 1481
1820 1482 remove_action(
1821 1483 /*hook=*/ 'transition_post_status',
1822 - /*callback=*/ array($this, 'fix_post_modified_ts'),
1484 + /*callback=*/ array(&$this, 'fix_post_modified_ts'),
1823 1485 /*priority=*/ -10000, /* very early */
1824 1486 /*arguments=*/ 3
1825 1487 );
1826 1488
@@ -1825,48 +1487,42 @@
1825 1487 );
1826 1488
1827 1489 // Turn off ridiculous fucking kludges #1 and #2
1828 1490 remove_action('_wp_put_post_revision', array($this, 'fix_revision_meta'));
1829 - foreach ($removed as $filter) :
1830 - add_filter('content_save_pre', $filter);
1831 - endforeach;
1832 -
1833 - $this->validate_post_id($dbpost, $update, array(__CLASS__, __FUNCTION__));
1834 -
1835 - $ret = $this->_wp_id;
1491 + remove_filter('content_save_pre', array($this, 'avoid_kses_munge'), 11);
1492 +
1493 + $this->validate_post_id($dbpost, array(__CLASS__, __FUNCTION__));
1836 1494 endif;
1837 - return $ret;
1838 1495 } /* function SyndicatedPost::insert_post () */
1839 -
1840 - /**
1841 - * SyndicatedPost::insert_new(). Uses the data collected in this post object to insert
1842 - * a new post into the wp_posts table.
1843 - *
1844 - * @uses SyndicatedPost::insert_post
1845 - */
1496 +
1846 1497 function insert_new () {
1847 - $this->insert_post(/*update=*/ false, 1);
1498 + $this->insert_post(/*update=*/ false);
1848 1499 } /* SyndicatedPost::insert_new() */
1849 1500
1850 - /**
1851 - * SyndicatedPost::insert_new(). Uses the data collected in this post object to update
1852 - * an existing post in the wp_posts table.
1853 - *
1854 - * @uses SyndicatedPost::insert_post
1855 - */
1856 1501 function update_existing () {
1857 - $this->insert_post(/*update=*/ true, 2);
1502 + $this->insert_post(/*update=*/ true);
1858 1503 } /* SyndicatedPost::update_existing() */
1859 1504
1860 1505 /**
1861 1506 * SyndicatedPost::normalize_post()
1862 1507 *
1863 - * @param bool $new If true, this post is to be inserted anew. If false, it is an update of an existing post. (Unused)
1508 + * @param bool $new If true, this post is to be inserted anew. If false, it is an update of an existing post.
1864 1509 * @return array A normalized representation of the post ready to be inserted into the database or sent to the WordPress API functions
1865 1510 */
1866 - function normalize_post( $new = true ) {
1867 - $out = $this->post;
1511 + function normalize_post ($new = true) {
1512 + global $wpdb;
1868 1513
1514 + $out = array();
1515 +
1516 + // Why the fuck doesn't wp_insert_post already do this?
1517 + foreach ($this->post as $key => $value) :
1518 + if (is_string($value)) :
1519 + $out[$key] = $wpdb->escape($value);
1520 + else :
1521 + $out[$key] = $value;
1522 + endif;
1523 + endforeach;
1524 +
1869 1525 $fullPost = $out['post_title'].$out['post_content'];
1870 1526 $fullPost .= (isset($out['post_excerpt']) ? $out['post_excerpt'] : '');
1871 1527 if (strlen($fullPost) < 1) :
1872 1528 // FIXME: Option for filtering out empty posts
@@ -1878,89 +1534,17 @@
1878 1534 else :
1879 1535 $feed_url = parse_url($this->post['meta']['syndication_feed']);
1880 1536 $source_title = $feed_url['host'];
1881 1537 endif;
1882 -
1538 +
1883 1539 $out['post_title'] = $source_title
1884 1540 .' '.gmdate('Y-m-d H:i:s', $this->published() + $offset);
1885 1541 // FIXME: Option for what to fill a blank title with...
1886 1542 endif;
1887 1543
1888 - // Normalize the guid if necessary.
1889 - $out['guid'] = SyndicatedPost::normalize_guid($out['guid']);
1890 -
1891 1544 return $out;
1892 1545 }
1893 1546
1894 - public function db_sanitize_post_check_encoding ($out) {
1895 - // Check encoding recursively: every string field needs to be checked
1896 - // for character encoding issues. This is a bit problematic because we
1897 - // *should* be using DB_CHARSET, but DB_CHARSET sometimes has values
1898 - // that work for MySQL but not for PHP mb_check_encoding. So instead
1899 - // we must rely on WordPress setting blog_charset and hope that the user
1900 - // has got their database encoding set up to roughly match
1901 - $charset = get_option('blog_charset', 'utf8');
1902 -
1903 - foreach ($out as $key => $value) :
1904 - if (is_string($value)) :
1905 -
1906 - if ( !function_exists('mb_check_encoding') or mb_check_encoding($value, $charset)) :
1907 - $out[$key] = $value;
1908 - else :
1909 - $fromCharset = mb_detect_encoding($value, mb_detect_order(), /*strict=*/ true);
1910 - $out[$key] = mb_convert_encoding($value, $charset, $fromCharset);
1911 - endif;
1912 -
1913 - elseif (is_array($value)) :
1914 - $out[$key] = $this->db_sanitize_post_check_encoding($value);
1915 -
1916 - else :
1917 - $out[$key] = $value;
1918 - endif;
1919 -
1920 - endforeach;
1921 -
1922 - return $out;
1923 - } /* SyndicatedPost::db_sanitize_post_check_encoding () */
1924 -
1925 - function db_sanitize_post ($out) {
1926 - global $wp_db_version;
1927 -
1928 - $out = $this->db_sanitize_post_check_encoding($out);
1929 -
1930 - // < 3.6. Core API, including `wp_insert_post()`, expects
1931 - // properly slashed data. If `wp_slash()` exists, then
1932 - // this is after the big change-over in how data slashing
1933 - // was handled.
1934 - if ( !function_exists('wp_slash')) :
1935 -
1936 - foreach ($out as $key => $value) :
1937 - if (is_string($value)) :
1938 - $out[$key] = esc_sql($value);
1939 - else :
1940 - $out[$key] = $value;
1941 - endif;
1942 - endforeach;
1943 -
1944 - // For revisions [@23416,@23554), core API expects
1945 - // unslashed data. Cf. <https://core.trac.wordpress.org/browser/trunk/wp-includes/post.php?rev=23416>
1946 - // NOOP for those revisions.
1947 -
1948 - // In revisions @23554 to present, `wp_insert_post()`
1949 - // expects slashed data once again.
1950 - // Cf. <https://core.trac.wordpress.org/changeset/23554/trunk/wp-includes/post.php?contextall=1>
1951 - // But at least now we can use the wp_slash API function to do that.
1952 - // Hooray.
1953 -
1954 - elseif ($wp_db_version >= 23524) :
1955 -
1956 - $out = wp_slash($out);
1957 -
1958 - endif;
1959 -
1960 - return $out;
1961 - }
1962 -
1963 1547 /**
1964 1548 * SyndicatedPost::validate_post_id()
1965 1549 *
1966 1550 * @param array $dbpost An array representing the post we attempted to insert or update
@@ -1965,57 +1549,35 @@
1965 1549 *
1966 1550 * @param array $dbpost An array representing the post we attempted to insert or update
1967 1551 * @param mixed $ns A string or array representing the namespace (class, method) whence this method was called.
1968 1552 */
1969 - function validate_post_id ($dbpost, $is_update, $ns) {
1553 + function validate_post_id ($dbpost, $ns) {
1970 1554 if (is_array($ns)) : $ns = implode('::', $ns);
1971 1555 else : $ns = (string) $ns; endif;
1972 -
1556 +
1973 1557 // This should never happen.
1974 - if ( !is_numeric($this->_wp_id) or ($this->_wp_id == 0)) :
1975 - $verb = ($is_update ? 'update existing' : 'insert new');
1976 - $guid = $this->guid();
1977 - $url = $this->permalink();
1978 - $feed = $this->link->uri(array('add_params' => true));
1979 -
1980 - // wp_insert_post failed. Diagnostics, or barf up a critical bug
1981 - // notice if we are in debug mode.
1982 - $mesg = "Failed to $verb item [$guid]. WordPress API returned no valid post ID.\n"
1983 - ."\t\tID = ".serialize($this->_wp_id)."\n"
1984 - ."\t\tURL = ".MyPHP::val($url)
1985 - ."\t\tFeed = ".MyPHP::val($feed);
1986 -
1987 - FeedWordPress::diagnostic('updated_feeds:errors', "WordPress API error: $mesg");
1988 - FeedWordPress::diagnostic('feed_items:rejected', $mesg);
1989 -
1990 - $mesg = <<<EOM
1991 -The WordPress API returned an invalid post ID
1992 - when FeedWordPress tried to $verb item $guid
1993 - [URL: $url]
1994 - from the feed at $feed
1995 -
1996 -$ns::_wp_id
1997 -EOM;
1998 - FeedWordPressDiagnostic::noncritical_bug(
1999 - /*message=*/ $mesg,
1558 + if (!is_numeric($this->_wp_id) or ($this->_wp_id == 0)) :
1559 + FeedWordPress::critical_bug(
1560 + /*name=*/ $ns.'::_wp_id',
2000 1561 /*var =*/ array(
2001 1562 "\$this->_wp_id" => $this->_wp_id,
2002 1563 "\$dbpost" => $dbpost,
1564 + "\$this" => $this
2003 1565 ),
2004 - /*line # =*/ __LINE__, /*filename=*/ __FILE__
1566 + /*line # =*/ __LINE__
2005 1567 );
2006 1568 endif;
2007 1569 } /* SyndicatedPost::validate_post_id() */
2008 -
1570 +
2009 1571 /**
2010 - * SyndicatedPost::fix_revision_meta() - Ensures that we get the meta
2011 - * data (authorship, guid, etc.) that we want when storing revisions of
2012 - * a syndicated post.
1572 + * SyndicatedPost::fix_revision_meta() - Fixes the way WP 2.6+ fucks up
1573 + * meta-data (authorship, etc.) when storing revisions of an updated
1574 + * syndicated post.
2013 1575 *
2014 - * In their infinite wisdom, the WordPress coders seem to have made it
2015 - * completely impossible for a plugin that uses wp_insert_post() to set
2016 - * certain meta-data (such as the author) when you store an old revision
2017 - * of an updated post. Instead, it uses the WordPress defaults (= cur.
1576 + * In their infinite wisdom, the WordPress coders have made it completely
1577 + * impossible for a plugin that uses wp_insert_post() to set certain
1578 + * meta-data (such as the author) when you store an old revision of an
1579 + * updated post. Instead, it uses the WordPress defaults (= currently
2018 1580 * active user ID if the process is running with a user logged in, or
2019 1581 * = #0 if there is no user logged in). This results in bogus authorship
2020 1582 * data for revisions that are syndicated from off the feed, unless we
2021 1583 * use a ridiculous kludge like this to end-run the munging of meta-data
@@ -2022,25 +1584,47 @@
2022 1584 * by _wp_put_post_revision.
2023 1585 *
2024 1586 * @param int $revision_id The revision ID to fix up meta-data
2025 1587 */
2026 - function fix_revision_meta( $revision_id ) {
2027 - $post_author = (int) $this->post['post_author']; // is this a global? And if it is, where is it defined? (gwyneth 20230919)
2028 -
1588 + function fix_revision_meta ($revision_id) {
1589 + global $wpdb;
1590 +
1591 + $post_author = (int) $this->post['post_author'];
1592 +
2029 1593 $revision_id = (int) $revision_id;
2030 -
2031 - // Let's fix the author.
2032 - set_post_field( 'post_author', $this->post['post_author'], $revision_id );
2033 -
2034 - // Let's fix the GUID to a dummy URL with the update hash.
2035 - set_post_field( 'guid', 'http://feedwordpress.radgeek.com/?rev=' . $this->update_hash(), $revision_id );
2036 -
2037 - // Let's fire an event for add-ons and filters
2038 - do_action( 'syndicated_post_fix_revision_meta', $revision_id, $this );
2039 -
1594 + $wpdb->query("
1595 + UPDATE $wpdb->posts
1596 + SET post_author={$this->post['post_author']}
1597 + WHERE post_type = 'revision' AND ID='$revision_id'
1598 + ");
2040 1599 } /* SyndicatedPost::fix_revision_meta () */
2041 1600
2042 1601 /**
1602 + * SyndicatedPost::avoid_kses_munge() -- If FeedWordPress is processing
1603 + * an automatic update, that generally means that wp_insert_post() is
1604 + * being called under the user credentials of whoever is viewing the
1605 + * blog at the time -- usually meaning no user at all. But if WordPress
1606 + * gets a wp_insert_post() when current_user_can('unfiltered_html') is
1607 + * false, it will run the content of the post through a kses function
1608 + * that strips out lots of HTML tags -- notably <object> and some others.
1609 + * This causes problems for syndicating (for example) feeds that contain
1610 + * YouTube videos. It also produces an unexpected asymmetry between
1611 + * automatically-initiated updates and updates initiated manually from
1612 + * the WordPress Dashboard (which are usually initiated under the
1613 + * credentials of a logged-in admin, and so don't get run through the
1614 + * kses function). So, to avoid the whole mess, what we do here is
1615 + * just forcibly disable the kses munging for a single syndicated post,
1616 + * by restoring the contents of the `post_content` field.
1617 + *
1618 + * @param string $content The content of the post, after other filters have gotten to it
1619 + * @return string The original content of the post, before other filters had a chance to munge it.
1620 + */
1621 + function avoid_kses_munge ($content) {
1622 + global $wpdb;
1623 + return $wpdb->escape($this->post['post_content']);
1624 + }
1625 +
1626 + /**
2043 1627 * SyndicatedPost::add_terms() -- if FeedWordPress is processing an
2044 1628 * automatic update, that generally means that wp_insert_post() is being
2045 1629 * called under the user credentials of whoever is viewing the blog at
2046 1630 * the time -- which usually means no user at all. But wp_insert_post()
@@ -2056,37 +1640,19 @@
2056 1640 * @param string $old_status Unused action parameter.
2057 1641 * @param object $post The database record for the post just inserted.
2058 1642 */
2059 1643 function add_terms ($new_status, $old_status, $post) {
2060 -
2061 - if ($new_status!='inherit') : // Bail if we are creating a revision.
2062 - if ( is_array($this->post) and isset($this->post['tax_input']) and is_array($this->post['tax_input']) ) :
2063 - foreach ($this->post['tax_input'] as $taxonomy => $terms) :
2064 - if (is_array($terms)) :
2065 - $terms = array_filter($terms); // strip out empties
2066 - endif;
2067 -
2068 - $res = wp_set_post_terms(
2069 - /*post_id=*/ $post->ID,
2070 - /*terms=*/ $terms,
2071 - /*taxonomy=*/ $taxonomy
2072 - );
2073 -
2074 - FeedWordPress::diagnostic(
2075 - 'syndicated_posts:categories',
2076 - 'Category: post('.json_encode($post->ID).') '.$taxonomy
2077 - .' := '
2078 - .json_encode($terms)
2079 - .' / result: '
2080 - .json_encode($res)
2081 - );
2082 -
2083 - endforeach;
2084 - endif;
1644 + if ( is_array($this->post) and isset($this->post['tax_input']) and is_array($this->post['tax_input']) ) :
1645 + foreach ($this->post['tax_input'] as $taxonomy => $terms) :
1646 + if (is_array($terms)) :
1647 + $terms = array_filter($terms); // strip out empties
1648 + endif;
1649 +
1650 + wp_set_post_terms($post->ID, $terms, $taxonomy);
1651 + endforeach;
2085 1652 endif;
2086 -
2087 1653 } /* SyndicatedPost::add_terms () */
2088 -
1654 +
2089 1655 /**
2090 1656 * SyndicatedPost::fix_post_modified_ts() -- We would like to set
2091 1657 * post_modified and post_modified_gmt to reflect the value of
2092 1658 * <atom:updated> or equivalent elements on the feed. Unfortunately,
@@ -2102,16 +1668,14 @@
2102 1668 * @param object $post The database record for the post just inserted.
2103 1669 */
2104 1670 function fix_post_modified_ts ($new_status, $old_status, $post) {
2105 1671 global $wpdb;
2106 - if ($new_status!='inherit') : // Bail if we are creating a revision.
2107 - $wpdb->update( $wpdb->posts, /*data=*/ array(
2108 - 'post_modified' => $this->post['post_modified'],
2109 - 'post_modified_gmt' => $this->post['post_modified_gmt'],
2110 - ), /*where=*/ array('ID' => $post->ID) );
2111 - endif;
1672 + $wpdb->update( $wpdb->posts, /*data=*/ array(
1673 + 'post_modified' => $this->post['post_modified'],
1674 + 'post_modified_gmt' => $this->post['post_modified_gmt'],
1675 + ), /*where=*/ array('ID' => $post->ID) );
2112 1676 } /* SyndicatedPost::fix_post_modified_ts () */
2113 -
1677 +
2114 1678 /**
2115 1679 * SyndicatedPost::add_rss_meta: adds interesting meta-data to each entry
2116 1680 * using the space for custom keys. The set of keys and values to add is
2117 1681 * specified by the keys and values of $post['meta']. This is used to
@@ -2125,48 +1689,41 @@
2125 1689 * @param string $new_status Unused action parameter.
2126 1690 * @param string $old_status Unused action parameter.
2127 1691 * @param object $post The database record for the post just inserted.
2128 1692 */
2129 - function add_rss_meta($new_status, $old_status, $post) {
1693 + function add_rss_meta ($new_status, $old_status, $post) {
1694 + FeedWordPress::diagnostic('syndicated_posts:meta_data', 'Adding post meta-data: {'.implode(", ", array_keys($this->post['meta'])).'}');
1695 +
2130 1696 global $wpdb;
2131 - if ($new_status!='inherit') : // Bail if we are creating a revision.
2132 - FeedWordPress::diagnostic('syndicated_posts:meta_data', 'Adding post meta-data: {'.implode(", ", array_keys($this->post['meta'])).'}');
1697 + if ( is_array($this->post) and isset($this->post['meta']) and is_array($this->post['meta']) ) :
1698 + $postId = $post->ID;
1699 +
1700 + // Aggregated posts should NOT send out pingbacks.
1701 + // WordPress 2.1-2.2 claim you can tell them not to
1702 + // using $post_pingback, but they don't listen, so we
1703 + // make sure here.
1704 + $result = $wpdb->query("
1705 + DELETE FROM $wpdb->postmeta
1706 + WHERE post_id='$postId' AND meta_key='_pingme'
1707 + ");
2133 1708
2134 - if ( is_array($this->post) and isset($this->post['meta']) and is_array($this->post['meta']) ) :
2135 - $postId = $post->ID;
1709 + foreach ( $this->post['meta'] as $key => $values ) :
1710 + $eKey = $wpdb->escape($key);
2136 1711
2137 - // Aggregated posts should NOT send out pingbacks.
2138 - // WordPress 2.1-2.2 claim you can tell them not to
2139 - // using $post_pingback, but they don't listen, so we
2140 - // make sure here.
1712 + // If this is an update, clear out the old
1713 + // values to avoid duplication.
2141 1714 $result = $wpdb->query("
2142 1715 DELETE FROM $wpdb->postmeta
2143 - WHERE post_id='$postId' AND meta_key='_pingme'
1716 + WHERE post_id='$postId' AND meta_key='$eKey'
2144 1717 ");
2145 1718
2146 - foreach ( $this->post['meta'] as $key => $values ) :
2147 - $eKey = esc_sql($key);
2148 -
2149 - // If this is an update, clear out the old
2150 - // values to avoid duplication.
2151 - $result = $wpdb->query("
2152 - DELETE FROM $wpdb->postmeta
2153 - WHERE post_id='$postId' AND meta_key='$eKey'
2154 - ");
2155 -
2156 - // Allow for either a single value or an array
2157 - if ( !is_array($values)) $values = array($values);
2158 - foreach ( $values as $value ) :
2159 - FeedWordPress::diagnostic('syndicated_posts:meta_data', "Adding post meta-datum to post [$postId]: [$key] = ".MyPHP::val($value, /*no newlines=*/ true));
2160 - add_post_meta($postId, $key, $value, /*unique=*/ false);
2161 - endforeach;
1719 + // Allow for either a single value or an array
1720 + if (!is_array($values)) $values = array($values);
1721 + foreach ( $values as $value ) :
1722 + FeedWordPress::diagnostic('syndicated_posts:meta_data', "Adding post meta-datum to post [$postId]: [$key] = ".FeedWordPress::val($value, /*no newlines=*/ true));
1723 + add_post_meta($postId, $key, $value, /*unique=*/ false);
2162 1724 endforeach;
2163 -
2164 - if ( $result === false)
2165 - {
2166 - error_log( "delete query failed: " . $wpdb->last_error );
2167 - }
2168 - endif;
1725 + endforeach;
2169 1726 endif;
2170 1727 } /* SyndicatedPost::add_rss_meta () */
2171 1728
2172 1729 /**
@@ -2181,45 +1738,35 @@
2181 1738 function author_id ($unfamiliar_author = 'create') {
2182 1739 global $wpdb;
2183 1740
2184 1741 $a = $this->named['author'];
2185 -
1742 +
2186 1743 $source = $this->source();
2187 1744 $forbidden = apply_filters('feedwordpress_forbidden_author_names',
2188 1745 array('admin', 'administrator', 'www', 'root'));
2189 -
2190 - // Prepare the list of candidates to try for author name: name from
2191 - // feed, original source title (if any), immediate source title live
2192 - // from feed, subscription title, prettied version of feed homepage URL,
2193 - // prettied version of feed URL, or, failing all, use "unknown author"
2194 - // as last resort
2195 -
1746 +
2196 1747 $candidates = array();
2197 1748 $candidates[] = $a['name'];
2198 - if ( !is_null($source)) : $candidates[] = $source['title']; endif;
1749 + if (!is_null($source)) : $candidates[] = $source['title']; endif;
2199 1750 $candidates[] = $this->link->name(/*fromFeed=*/ true);
2200 1751 $candidates[] = $this->link->name(/*fromFeed=*/ false);
2201 1752 if (strlen($this->link->homepage()) > 0) : $candidates[] = feedwordpress_display_url($this->link->homepage()); endif;
2202 1753 $candidates[] = feedwordpress_display_url($this->link->uri());
2203 1754 $candidates[] = 'unknown author';
2204 -
2205 - // Pick the first one that works from the list, screening against empty
2206 - // or forbidden names.
2207 -
1755 +
2208 1756 $author = NULL;
2209 - foreach ($candidates as $candidate) {
2210 - if ( !is_null($candidate)
2211 - and (strlen(trim($candidate)) > 0)
2212 - and !in_array(strtolower(trim($candidate)), $forbidden)) :
2213 - $author = $candidate;
2214 - break;
1757 + while (is_null($author) and ($candidate = each($candidates))) :
1758 + if (!is_null($candidate['value'])
1759 + and (strlen(trim($candidate['value'])) > 0)
1760 + and !in_array(strtolower(trim($candidate['value'])), $forbidden)) :
1761 + $author = $candidate['value'];
2215 1762 endif;
2216 - }
2217 -
1763 + endwhile;
1764 +
2218 1765 $email = (isset($a['email']) ? $a['email'] : NULL);
2219 1766 $authorUrl = (isset($a['uri']) ? $a['uri'] : NULL);
2220 1767
2221 -
1768 +
2222 1769 $hostUrl = $this->link->homepage();
2223 1770 if (is_null($hostUrl) or (strlen($hostUrl) < 0)) :
2224 1771 $hostUrl = $this->link->uri();
2225 1772 endif;
@@ -2244,9 +1791,9 @@
2244 1791 //
2245 1792 // Uniqueness will be guaranteed below if necessary.
2246 1793
2247 1794 $url = parse_url($hostUrl);
2248 -
1795 +
2249 1796 $login = sanitize_user($url['host'], /*strict=*/ true);
2250 1797 if (strlen($login) < 1) :
2251 1798 // This isn't working. Frak it.
2252 1799 $login = 'syndicated';
@@ -2257,13 +1804,13 @@
2257 1804
2258 1805 $nice_author = sanitize_title($author);
2259 1806 $nice_author = apply_filters('pre_user_nicename', $nice_author);
2260 1807
2261 - $reg_author = esc_sql(preg_quote($author ?: "(unknown)"));
2262 - $author = esc_sql($author);
2263 - $email = esc_sql($email);
2264 - $test_email = esc_sql($test_email);
2265 - $authorUrl = esc_sql($authorUrl);
1808 + $reg_author = $wpdb->escape(preg_quote($author));
1809 + $author = $wpdb->escape($author);
1810 + $email = $wpdb->escape($email);
1811 + $test_email = $wpdb->escape($test_email);
1812 + $authorUrl = $wpdb->escape($authorUrl);
2266 1813
2267 1814 // Check for an existing author rule....
2268 1815 if (isset($this->link->settings['map authors']['name']['*'])) :
2269 1816 $author_rule = $this->link->settings['map authors']['name']['*'];
@@ -2273,17 +1820,15 @@
2273 1820 $author_rule = NULL;
2274 1821 endif;
2275 1822
2276 1823 // User name is mapped to a particular author. If that author ID exists, use it.
2277 - // Note: get_userdata() is a pluggable function, so it might not be available yet;
2278 - // check if it exists before using it, or WP will throw a fatal error. (gwyneth 20220223)
2279 - if (is_numeric($author_rule) and function_exists('get_userdata') and get_userdata((int) $author_rule)) :
1824 + if (is_numeric($author_rule) and get_userdata((int) $author_rule)) :
2280 1825 $id = (int) $author_rule;
2281 1826
2282 1827 // User name is filtered out
2283 1828 elseif ('filter' == $author_rule) :
2284 1829 $id = NULL;
2285 -
1830 +
2286 1831 else :
2287 1832 // Check the database for an existing author record that might fit
2288 1833
2289 1834 // First try the user core data table.
@@ -2289,14 +1834,13 @@
2289 1834 // First try the user core data table.
2290 1835 $id = $wpdb->get_var(
2291 1836 "SELECT ID FROM $wpdb->users
2292 1837 WHERE TRIM(LCASE(display_name)) = TRIM(LCASE('$author'))
2293 - OR TRIM(LCASE(user_login)) = TRIM(LCASE('$author'))
2294 1838 OR (
2295 1839 LENGTH(TRIM(LCASE(user_email))) > 0
2296 1840 AND TRIM(LCASE(user_email)) = TRIM(LCASE('$test_email'))
2297 1841 )");
2298 -
1842 +
2299 1843 // If that fails, look for aliases in the user meta data table
2300 1844 if (is_null($id)) :
2301 1845 $id = $wpdb->get_var(
2302 1846 "SELECT user_id FROM $wpdb->usermeta
@@ -2318,11 +1862,11 @@
2318 1862 if (is_null($id)) :
2319 1863 if ($unfamiliar_author === 'create') :
2320 1864 $userdata = array();
2321 1865
2322 - #-- we need *something* for the email here or WordPress
2323 - #-- is liable to pitch a fit. So, make something up if
2324 - #-- necessary. (Ugh.)
1866 + // WordPress 3 is going to pitch a fit if we attempt to register
1867 + // more than one user account with an empty e-mail address, so we
1868 + // need *something* here. Ugh.
2325 1869 if (strlen($email) == 0 or FeedWordPress::is_null_email($email)) :
2326 1870 $url = parse_url($hostUrl);
2327 1871 $email = $nice_author.'@'.$url['host'];
2328 1872 endif;
@@ -2333,21 +1877,12 @@
2333 1877 $userdata['user_nicename'] = $nice_author;
2334 1878 $userdata['user_pass'] = substr(md5(uniqid(microtime())), 0, 6); // just something random to lock it up
2335 1879 $userdata['user_email'] = $email;
2336 1880 $userdata['user_url'] = $authorUrl;
2337 - $userdata['nickname'] = $author;
2338 -
2339 - $parts = preg_split('/\s+/', trim($author), 2);
2340 - if (isset($parts[0])) : $userdata['first_name'] = $parts[0]; endif;
2341 - if (isset($parts[1])) : $userdata['last_name'] = $parts[1]; endif;
2342 -
2343 1881 $userdata['display_name'] = $author;
2344 1882 $userdata['role'] = 'contributor';
2345 -
2346 - #-- loop. Keep trying to add the user until you get it
2347 - #-- right. Or until PHP crashes, I guess.
2348 - $insanity = 0;
2349 - do {
1883 +
1884 + do { // Keep trying until you get it right. Or until PHP crashes, I guess.
2350 1885 $id = wp_insert_user($userdata);
2351 1886 if (is_wp_error($id)) :
2352 1887 $codes = $id->get_error_code();
2353 1888 switch ($codes) :
@@ -2352,62 +1887,27 @@
2352 1887 $codes = $id->get_error_code();
2353 1888 switch ($codes) :
2354 1889 case 'empty_user_login' :
2355 1890 case 'existing_user_login' :
2356 - case 'invalid_username' :
2357 1891 // Add a random disambiguator
2358 1892 $userdata['user_login'] .= substr(md5(uniqid(microtime())), 0, 6);
2359 1893 break;
2360 - case 'user_login_too_long' :
2361 - // Limit length to 53 characters; if we end up needing a random disambiguator,
2362 - // we should still have space to add it.
2363 - $userdata['user_login'] = mb_substr( $userdata['user_login'], 0, 53 );
2364 - break;
2365 - case 'user_nicename_too_long' :
2366 - // Add a limited 50 characters user_nicename based on user_login
2367 - $userdata['user_nicename'] = mb_substr( $userdata['user_login'], 0, 50 );
2368 - break;
2369 1894 case 'existing_user_email' :
2370 - // Disassemble email for username, host
1895 + // No disassemble!
2371 1896 $parts = explode('@', $userdata['user_email'], 2);
2372 -
1897 +
2373 1898 // Add a random disambiguator as a gmail-style username extension
2374 1899 $parts[0] .= '+'.substr(md5(uniqid(microtime())), 0, 6);
2375 -
1900 +
2376 1901 // Reassemble
2377 1902 $userdata['user_email'] = $parts[0].'@'.$parts[1];
2378 1903 break;
2379 - default :
2380 - if ( $insanity > 10 ) :
2381 - // Try some settings that are unlikely to cause complaint...
2382 - $url = parse_url($hostUrl);
2383 -
2384 - $userdata['user_login'] = substr(md5(uniqid(microtime())), 0, 6);
2385 - $userdata['user_nicename'] = $userdata['user_login'];
2386 - $userdata['user_email'] = 'noreply@' . $url['host'];
2387 - elseif ( $insanity > 50 ) :
2388 - // Stop doing the same thing and expecting a different result
2389 - break;
2390 - endif;
2391 1904 endswitch;
2392 1905 endif;
2393 - $insanity = $insanity + 1;
2394 1906 } while (is_wp_error($id));
2395 -
2396 - // $id should now contain the numeric ID of a newly minted
2397 - // user account. Let's mark them as having been generated
2398 - // by FeedWordPress in the usermeta table, as per the
2399 - // suggestion of @boonebgorges, in case we need to process,
2400 - // winnow, filter, or merge syndicated author accounts, &c.
2401 - if ( !is_wp_error($id)) :
2402 - add_user_meta($id, 'feedwordpress_generated', 1);
2403 - else :
2404 - $id = null;
2405 - endif;
2406 - // also see comment above regarding get_userdata(). (gwyneth 20220223)
2407 - elseif ( is_numeric( $unfamiliar_author ) and function_exists( 'get_userdata ') and get_userdata((int) $unfamiliar_author ) ) :
1907 + elseif (is_numeric($unfamiliar_author) and get_userdata((int) $unfamiliar_author)) :
2408 1908 $id = (int) $unfamiliar_author;
2409 - elseif ( $unfamiliar_author === 'default' ) :
1909 + elseif ($unfamiliar_author === 'default') :
2410 1910 $id = 1;
2411 1911 endif;
2412 1912 endif;
2413 1913 endif;
@@ -2413,9 +1913,9 @@
2413 1913 endif;
2414 1914
2415 1915 if ($id) :
2416 1916 $this->link->settings['map authors']['name'][strtolower(trim($author))] = $id;
2417 -
1917 +
2418 1918 // Multisite: Check whether the author has been recorded
2419 1919 // on *this* blog before. If not, put her down as a
2420 1920 // Contributor for *this* blog.
2421 1921 $user = new WP_User((int) $id);
@@ -2422,21 +1922,144 @@
2422 1922 if (empty($user->roles)) :
2423 1923 $user->add_role('contributor');
2424 1924 endif;
2425 1925 endif;
2426 - return $id;
2427 - } /* function SyndicatedPost::author_id () */
1926 + return $id;
1927 + } // function SyndicatedPost::author_id ()
2428 1928
2429 1929 /**
2430 - * category_ids: look up (and create) category ids from a list of
2431 - * categories
1930 + * category_ids: look up (and create) category ids from a list of categories
2432 1931 *
2433 1932 * @param array $cats
2434 1933 * @param string $unfamiliar_category
2435 1934 * @param array|null $taxonomies
2436 1935 * @return array
2437 - */
1936 + */
2438 1937 function category_ids ($cats, $unfamiliar_category = 'create', $taxonomies = NULL, $params = array()) {
2439 - return $this->link->category_ids($this, $cats, $unfamiliar_category, $taxonomies, $params);
2440 - } /* SyndicatedPost::category_ids () */
1938 + $singleton = (isset($params['singleton']) ? $params['singleton'] : true);
1939 + $allowFilters = (isset($params['filters']) ? $params['filters'] : false);
1940 +
1941 + $catTax = 'category';
2441 1942
1943 + if (is_null($taxonomies)) :
1944 + $taxonomies = array('category');
1945 + endif;
1946 +
1947 + // We need to normalize whitespace because (1) trailing
1948 + // whitespace can cause PHP and MySQL not to see eye to eye on
1949 + // VARCHAR comparisons for some versions of MySQL (cf.
1950 + // <http://dev.mysql.com/doc/mysql/en/char.html>), and (2)
1951 + // because I doubt most people want to make a semantic
1952 + // distinction between 'Computers' and 'Computers '
1953 + $cats = array_map('trim', $cats);
1954 +
1955 + $terms = array();
1956 + foreach ($taxonomies as $tax) :
1957 + $terms[$tax] = array();
1958 + endforeach;
1959 +
1960 + foreach ($cats as $cat_name) :
1961 + if (preg_match('/^{([^#}]*)#([0-9]+)}$/', $cat_name, $backref)) :
1962 + $cat_id = (int) $backref[2];
1963 + $tax = $backref[1];
1964 + if (strlen($tax) < 1) :
1965 + $tax = $catTax;
1966 + endif;
1967 +
1968 + $term = term_exists($cat_id, $tax);
1969 + if (!is_wp_error($term) and !!$term) :
1970 + if (!isset($terms[$tax])) :
1971 + $terms[$tax] = array();
1972 + endif;
1973 + $terms[$tax][] = $cat_id;
1974 + endif;
1975 + elseif (strlen($cat_name) > 0) :
1976 + $familiar = false;
1977 + foreach ($taxonomies as $tax) :
1978 + if ($tax!='category' or strtolower($cat_name)!='uncategorized') :
1979 + $term = term_exists($cat_name, $tax);
1980 + if (!is_wp_error($term) and !!$term) :
1981 + $familiar = true;
1982 +
1983 + if (is_array($term)) :
1984 + $term_id = (int) $term['term_id'];
1985 + else :
1986 + $term_id = (int) $term;
1987 + endif;
1988 +
1989 + if (!isset($terms[$tax])) :
1990 + $terms[$tax] = array();
1991 + endif;
1992 + $terms[$tax][] = $term_id;
1993 + break; // We're done here.
1994 + endif;
1995 + endif;
1996 + endforeach;
1997 +
1998 + if (!$familiar) :
1999 + if ('tag'==$unfamiliar_category) :
2000 + $unfamiliar_category = 'create:post_tag';
2001 + endif;
2002 +
2003 + if (preg_match('/^create(:(.*))?$/i', $unfamiliar_category, $ref)) :
2004 + $tax = $catTax; // Default
2005 + if (isset($ref[2]) and strlen($ref[2]) > 2) :
2006 + $tax = $ref[2];
2007 + endif;
2008 + $term = wp_insert_term($cat_name, $tax);
2009 + if (is_wp_error($term)) :
2010 + FeedWordPress::noncritical_bug('term insertion problem', array('cat_name' => $cat_name, 'term' => $term, 'this' => $this), __LINE__);
2011 + else :
2012 + if (!isset($terms[$tax])) :
2013 + $terms[$tax] = array();
2014 + endif;
2015 + $terms[$tax][] = (int) $term['term_id'];
2016 + endif;
2017 + endif;
2018 + endif;
2019 + endif;
2020 + endforeach;
2021 +
2022 + $filtersOn = $allowFilters;
2023 + if ($allowFilters) :
2024 + $filters = array_filter(
2025 + $this->link->setting('match/filter', 'match_filter', array()),
2026 + 'remove_dummy_zero'
2027 + );
2028 + $filtersOn = ($filtersOn and is_array($filters) and (count($filters) > 0));
2029 + endif;
2030 +
2031 + // Check for filter conditions
2032 + foreach ($terms as $tax => $term_ids) :
2033 + if ($filtersOn
2034 + and (count($term_ids)==0)
2035 + and in_array($tax, $filters)) :
2036 + $terms = NULL; // Drop the post
2037 + break;
2038 + else :
2039 + $terms[$tax] = array_unique($term_ids);
2040 + endif;
2041 + endforeach;
2042 +
2043 + if ($singleton and count($terms)==1) : // If we only searched one, just return the term IDs
2044 + $terms = end($terms);
2045 + endif;
2046 + return $terms;
2047 + } // function SyndicatedPost::category_ids ()
2048 +
2049 + function use_api ($tag) {
2050 + global $wp_db_version;
2051 + switch ($tag) :
2052 + case 'wp_insert_post':
2053 + // Before 2.2, wp_insert_post does too much of the wrong stuff to use it
2054 + // In 1.5 it was such a resource hog it would make PHP segfault on big updates
2055 + $ret = (isset($wp_db_version) and $wp_db_version > FWP_SCHEMA_21);
2056 + break;
2057 + case 'post_status_pending':
2058 + $ret = (isset($wp_db_version) and $wp_db_version > FWP_SCHEMA_23);
2059 + break;
2060 + endswitch;
2061 + return $ret;
2062 + } // function SyndicatedPost::use_api ()
2063 +
2442 2064 } /* class SyndicatedPost */
2065 +