PluginProbe
FeedWordPress / 2009.1111
FeedWordPress v2009.1111
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
feedwordpress / syndicatedpost.class.php

syndicatedpost.class.php in FeedWordPress 2009.1111, at syndicatedpost.class.php

1,279 lines 45.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 class SyndicatedPost {
3 var $item = null;
4
5 var $link = null;
6 var $feed = null;
7 var $feedmeta = null;
8
9 var $post = array ();
10
11 var $_freshness = null;
12 var $_wp_id = null;
13
14 function SyndicatedPost ($item, $link) {
15 global $wpdb;
16
17 $this->link = $link;
18 $feedmeta = $link->settings;
19 $feed = $link->magpie;
20
21 # This is ugly as all hell. I'd like to use apply_filters()'s
22 # alleged support for a variable argument count, but this seems
23 # to have been broken in WordPress 1.5. It'll be fixed somehow
24 # in WP 1.5.1, but I'm aiming at WP 1.5 compatibility across
25 # the board here.
26 #
27 # Cf.: <http://mosquito.wordpress.org/view.php?id=901>
28 global $fwp_channel, $fwp_feedmeta;
29 $fwp_channel = $feed; $fwp_feedmeta = $feedmeta;
30
31 $this->feed = $feed;
32 $this->feedmeta = $feedmeta;
33
34 $this->item = $item;
35 $this->item = apply_filters('syndicated_item', $this->item, $this);
36
37 # Filters can halt further processing by returning NULL
38 if (is_null($this->item)) :
39 $this->post = NULL;
40 else :
41 # Note that nothing is run through $wpdb->escape() here.
42 # That's deliberate. The escaping is done at the point
43 # of insertion, not here, to avoid double-escaping and
44 # to avoid screwing with syndicated_post filters
45
46 $this->post['post_title'] = apply_filters('syndicated_item_title', $this->item['title'], $this);
47
48 // This just gives us an alphanumeric representation of
49 // the author. We will look up (or create) the numeric
50 // ID for the author in SyndicatedPost::add()
51 $this->post['named']['author'] = apply_filters('syndicated_item_author', $this->author(), $this);
52
53 # Identify content and sanitize it.
54 # ---------------------------------
55 if (isset($this->item['atom_content'])) :
56 $content = $this->item['atom_content'];
57 elseif (isset($this->item['xhtml']['body'])) :
58 $content = $this->item['xhtml']['body'];
59 elseif (isset($this->item['xhtml']['div'])) :
60 $content = $this->item['xhtml']['div'];
61 elseif (isset($this->item['content']['encoded']) and $this->item['content']['encoded']):
62 $content = $this->item['content']['encoded'];
63 else:
64 $content = $this->item['description'];
65 endif;
66 $this->post['post_content'] = apply_filters('syndicated_item_content', $content, $this);
67
68 # Identify and sanitize excerpt
69 $excerpt = NULL;
70 if ( isset($this->item['description']) and $this->item['description'] ) :
71 $excerpt = $this->item['description'];
72 elseif ( isset($content) and $content ) :
73 $excerpt = strip_tags($content);
74 if (strlen($excerpt) > 255) :
75 $excerpt = substr($excerpt,0,252).'...';
76 endif;
77 endif;
78 $excerpt = apply_filters('syndicated_item_excerpt', $excerpt, $this);
79
80 if (!is_null($excerpt)):
81 $this->post['post_excerpt'] = $excerpt;
82 endif;
83
84 // This is unnecessary if we use wp_insert_post
85 if (!$this->use_api('wp_insert_post')) :
86 $this->post['post_name'] = sanitize_title($this->post['post_title']);
87 endif;
88
89 $this->post['epoch']['issued'] = apply_filters('syndicated_item_published', $this->published(), $this);
90 $this->post['epoch']['created'] = apply_filters('syndicated_item_created', $this->created(), $this);
91 $this->post['epoch']['modified'] = apply_filters('syndicated_item_updated', $this->updated(), $this);
92
93 // Dealing with timestamps in WordPress is so fucking fucked.
94 $offset = (int) get_option('gmt_offset') * 60 * 60;
95 $this->post['post_date'] = gmdate('Y-m-d H:i:s', $this->published() + $offset);
96 $this->post['post_modified'] = gmdate('Y-m-d H:i:s', $this->updated() + $offset);
97 $this->post['post_date_gmt'] = gmdate('Y-m-d H:i:s', $this->published());
98 $this->post['post_modified_gmt'] = gmdate('Y-m-d H:i:s', $this->updated());
99
100 // Use feed-level preferences or the global default.
101 $this->post['post_status'] = $this->link->syndicated_status('post', 'publish');
102 $this->post['comment_status'] = $this->link->syndicated_status('comment', 'closed');
103 $this->post['ping_status'] = $this->link->syndicated_status('ping', 'closed');
104
105 // Unique ID (hopefully a unique tag: URI); failing that, the permalink
106 $this->post['guid'] = apply_filters('syndicated_item_guid', $this->guid(), $this);
107
108 // 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
109 $default_custom_settings = get_option('feedwordpress_custom_settings');
110 if ($default_custom_settings and !is_array($default_custom_settings)) :
111 $default_custom_settings = unserialize($default_custom_settings);
112 endif;
113 if (!is_array($default_custom_settings)) :
114 $default_custom_settings = array();
115 endif;
116
117 $custom_settings = (isset($this->link->settings['postmeta']) ? $this->link->settings['postmeta'] : null);
118 if ($custom_settings and !is_array($custom_settings)) :
119 $custom_settings = unserialize($custom_settings);
120 endif;
121 if (!is_array($custom_settings)) :
122 $custom_settings = array();
123 endif;
124 $this->post['meta'] = array_merge($default_custom_settings, $custom_settings);
125
126 // RSS 2.0 / Atom 1.0 enclosure support
127 if ( isset($this->item['enclosure#']) ) :
128 for ($i = 1; $i <= $this->item['enclosure#']; $i++) :
129 $eid = (($i > 1) ? "#{$id}" : "");
130 $this->post['meta']['enclosure'][] =
131 apply_filters('syndicated_item_enclosure_url', $this->item["enclosure{$eid}@url"], $this)."\n".
132 apply_filters('syndicated_item_enclosure_length', $this->item["enclosure{$eid}@length"], $this)."\n".
133 apply_filters('syndicated_item_enclosure_type', $this->item["enclosure{$eid}@type"], $this);
134 endfor;
135 endif;
136
137 // In case you want to point back to the blog this was syndicated from
138 if (isset($this->feed->channel['title'])) :
139 $this->post['meta']['syndication_source'] = apply_filters('syndicated_item_source_title', $this->feed->channel['title'], $this);
140 endif;
141
142 if (isset($this->feed->channel['link'])) :
143 $this->post['meta']['syndication_source_uri'] = apply_filters('syndicated_item_source_link', $this->feed->channel['link'], $this);
144 endif;
145
146 // Make use of atom:source data, if present in an aggregated feed
147 if (isset($this->item['source_title'])) :
148 $this->post['meta']['syndication_source_original'] = $this->item['source_title'];
149 endif;
150
151 if (isset($this->item['source_link'])) :
152 $this->post['meta']['syndication_source_uri_original'] = $this->item['source_link'];
153 endif;
154
155 if (isset($this->item['source_id'])) :
156 $this->post['meta']['syndication_source_id_original'] = $this->item['source_id'];
157 endif;
158
159 // Store information on human-readable and machine-readable comment URIs
160 if (isset($this->item['comments'])) :
161 $this->post['meta']['rss:comments'] = apply_filters('syndicated_item_comments', $this->item['comments']);
162 endif;
163
164 // RSS 2.0 comment feeds extension
165 if (isset($this->item['wfw']['commentrss'])) :
166 $this->post['meta']['wfw:commentRSS'] = apply_filters('syndicated_item_commentrss', $this->item['wfw']['commentrss']);
167 endif;
168
169 // Atom 1.0 comment feeds link-rel
170 if (isset($this->item['link_replies'])) :
171 // There may be multiple <link rel="replies"> elements; feeds have a feed MIME type
172 $N = isset($this->item['link_replies#']) ? $this->item['link_replies#'] : 1;
173 for ($i = 1; $i <= $N; $i++) :
174 $currentElement = 'link_replies'.(($i > 1) ? '#'.$i : '');
175 if (isset($this->item[$currentElement.'@type'])
176 and preg_match("\007application/(atom|rss|rdf)\+xml\007i", $this->item[$currentElement.'@type'])) :
177 $this->post['meta']['wfw:commentRSS'] = apply_filters('syndicated_item_commentrss', $this->item[$currentElement]);
178 endif;
179 endfor;
180 endif;
181
182 // Store information to identify the feed that this came from
183 $this->post['meta']['syndication_feed'] = $this->feedmeta['link/uri'];
184 $this->post['meta']['syndication_feed_id'] = $this->feedmeta['link/id'];
185
186 if (isset($this->item['source_link_self'])) :
187 $this->post['meta']['syndication_feed_original'] = $this->item['source_link_self'];
188 endif;
189
190 // In case you want to know the external permalink...
191 if (isset($this->item['link'])) :
192 $permalink = $this->item['link'];
193
194 // No <link> element. See if this feed has <guid isPermalink="true"> ....
195 elseif (isset($this->item['guid'])) :
196 if (isset($this->item['guid@ispermalink']) and strtolower(trim($this->item['guid@ispermalink'])) != 'false') :
197 $permalink = $this->item['guid'];
198 endif;
199 endif;
200
201 $this->post['meta']['syndication_permalink'] = apply_filters('syndicated_item_link', $permalink);
202
203 // Store a hash of the post content for checking whether something needs to be updated
204 $this->post['meta']['syndication_item_hash'] = $this->update_hash();
205
206 // Feed-by-feed options for author and category creation
207 $this->post['named']['unfamiliar']['author'] = (isset($this->feedmeta['unfamiliar author']) ? $this->feedmeta['unfamiliar author'] : null);
208 $this->post['named']['unfamiliar']['category'] = (isset($this->feedmeta['unfamiliar category']) ? $this->feedmeta['unfamiliar category'] : null);
209
210 // Categories: start with default categories, if any
211 $fc = get_option("feedwordpress_syndication_cats");
212 if ($fc) :
213 $this->post['named']['preset/category'] = explode("\n", $fc);
214 else :
215 $this->post['named']['preset/category'] = array();
216 endif;
217
218 if (isset($this->feedmeta['cats']) and is_array($this->feedmeta['cats'])) :
219 $this->post['named']['preset/category'] = array_merge($this->post['named']['preset/category'], $this->feedmeta['cats']);
220 endif;
221
222 // Now add categories from the post, if we have 'em
223 $this->post['named']['category'] = array();
224 if ( isset($this->item['category#']) ) :
225 for ($i = 1; $i <= $this->item['category#']; $i++) :
226 $cat_idx = (($i > 1) ? "#{$i}" : "");
227 $cat = $this->item["category{$cat_idx}"];
228
229 if ( isset($this->feedmeta['cat_split']) and strlen($this->feedmeta['cat_split']) > 0) :
230 $pcre = "\007".$this->feedmeta['cat_split']."\007";
231 $this->post['named']['category'] = array_merge($this->post['named']['category'], preg_split($pcre, $cat, -1 /*=no limit*/, PREG_SPLIT_NO_EMPTY));
232 else :
233 $this->post['named']['category'][] = $cat;
234 endif;
235 endfor;
236 endif;
237 $this->post['named']['category'] = apply_filters('syndicated_item_categories', $this->post['named']['category'], $this);
238
239 // Tags: start with default tags, if any
240 $ft = get_option("feedwordpress_syndication_tags");
241 if ($ft) :
242 $this->post['tags_input'] = explode(FEEDWORDPRESS_CAT_SEPARATOR, $ft);
243 else :
244 $this->post['tags_input'] = array();
245 endif;
246
247 if (isset($this->feedmeta['tags']) and is_array($this->feedmeta['tags'])) :
248 $this->post['tags_input'] = array_merge($this->post['tags_input'], $this->feedmeta['tags']);
249 endif;
250 $this->post['tags_input'] = apply_filters('syndicated_item_tags', $this->post['tags_input'], $this);
251 endif;
252 } // SyndicatedPost::SyndicatedPost()
253
254 function filtered () {
255 return is_null($this->post);
256 }
257
258 function freshness () {
259 global $wpdb;
260
261 if ($this->filtered()) : // This should never happen.
262 FeedWordPress::critical_bug('SyndicatedPost', $this, __LINE__);
263 endif;
264
265 if (is_null($this->_freshness)) :
266 $guid = $wpdb->escape($this->guid());
267
268 $result = $wpdb->get_row("
269 SELECT id, guid, post_modified_gmt
270 FROM $wpdb->posts WHERE guid='$guid'
271 ");
272
273 if (!$result) :
274 $this->_freshness = 2; // New content
275 else:
276 $stored_update_hashes = get_post_custom_values('syndication_item_hash', $result->id);
277 if (count($stored_update_hashes) > 0) :
278 $stored_update_hash = $stored_update_hashes[0];
279 $update_hash_changed = ($stored_update_hash != $this->update_hash());
280 else :
281 $update_hash_changed = false;
282 endif;
283
284 preg_match('/([0-9]+)-([0-9]+)-([0-9]+) ([0-9]+):([0-9]+):([0-9]+)/', $result->post_modified_gmt, $backref);
285
286 $last_rev_ts = gmmktime($backref[4], $backref[5], $backref[6], $backref[2], $backref[3], $backref[1]);
287 $updated_ts = $this->updated(/*fallback=*/ true, /*default=*/ NULL);
288 $updated = ((
289 !is_null($updated_ts)
290 and ($updated_ts > $last_rev_ts)
291 ) or $update_hash_changed);
292
293 if ($updated) :
294 $this->_freshness = 1; // Updated content
295 $this->_wp_id = $result->id;
296 else :
297 $this->_freshness = 0; // Same old, same old
298 $this->_wp_id = $result->id;
299 endif;
300 endif;
301 endif;
302 return $this->_freshness;
303 }
304
305 function wp_id () {
306 if ($this->filtered()) : // This should never happen.
307 FeedWordPress::critical_bug('SyndicatedPost', $this, __LINE__);
308 endif;
309
310 if (is_null($this->_wp_id) and is_null($this->_freshness)) :
311 $fresh = $this->freshness(); // sets WP DB id in the process
312 endif;
313 return $this->_wp_id;
314 }
315
316 function store () {
317 global $wpdb;
318
319 if ($this->filtered()) : // This should never happen.
320 FeedWordPress::critical_bug('SyndicatedPost', $this, __LINE__);
321 endif;
322
323 $freshness = $this->freshness();
324 if ($freshness > 0) :
325 # -- Look up, or create, numeric ID for author
326 $this->post['post_author'] = $this->author_id (
327 FeedWordPress::on_unfamiliar('author', $this->post['named']['unfamiliar']['author'])
328 );
329
330 if (is_null($this->post['post_author'])) :
331 $this->post = NULL;
332 endif;
333 endif;
334
335 if (!$this->filtered() and $freshness > 0) :
336 # -- Look up, or create, numeric ID for categories
337 list($pcats, $ptags) = $this->category_ids (
338 $this->post['named']['category'],
339 FeedWordPress::on_unfamiliar('category', $this->post['named']['unfamiliar']['category']),
340 /*tags_too=*/ true
341 );
342
343 $this->post['post_category'] = $pcats;
344 $this->post['tags_input'] = array_merge($this->post['tags_input'], $ptags);
345
346 if (is_null($this->post['post_category'])) :
347 // filter mode on, no matching categories; drop the post
348 $this->post = NULL;
349 else :
350 // filter mode off or at least one match; now add on the feed and global presets
351 $this->post['post_category'] = array_merge (
352 $this->post['post_category'],
353 $this->category_ids (
354 $this->post['named']['preset/category'],
355 'default'
356 )
357 );
358
359 if (count($this->post['post_category']) < 1) :
360 $this->post['post_category'][] = 1; // Default to category 1 ("Uncategorized" / "General") if nothing else
361 endif;
362 endif;
363 endif;
364
365 if (!$this->filtered() and $freshness > 0) :
366 unset($this->post['named']);
367 $this->post = apply_filters('syndicated_post', $this->post, $this);
368 endif;
369
370 if (!$this->filtered() and $freshness == 2) :
371 // The item has not yet been added. So let's add it.
372 $this->insert_new();
373 $this->add_rss_meta();
374 do_action('post_syndicated_item', $this->wp_id());
375
376 $ret = 'new';
377 elseif (!$this->filtered() and $freshness == 1) :
378 $this->post['ID'] = $this->wp_id();
379 $this->update_existing();
380 $this->add_rss_meta();
381 do_action('update_syndicated_item', $this->wp_id());
382
383 $ret = 'updated';
384 else :
385 $ret = false;
386 endif;
387
388 return $ret;
389 } // function SyndicatedPost::store ()
390
391 function insert_new () {
392 global $wpdb, $wp_db_version;
393
394 $dbpost = $this->normalize_post(/*new=*/ true);
395 if (!is_null($dbpost)) :
396 if ($this->use_api('wp_insert_post')) :
397 $dbpost['post_pingback'] = false; // Tell WP 2.1 and 2.2 not to process for pingbacks
398
399 // This is a ridiculous fucking kludge necessitated by WordPress 2.6 munging authorship meta-data
400 add_action('_wp_put_post_revision', array($this, 'fix_revision_meta'));
401
402 // Kludge to prevent kses filters from stripping the
403 // content of posts when updating without a logged in
404 // user who has `unfiltered_html` capability.
405 add_filter('content_save_pre', array($this, 'avoid_kses_munge'), 11);
406
407 $this->_wp_id = wp_insert_post($dbpost);
408
409 // Turn off ridiculous fucking kludges #1 and #2
410 remove_action('_wp_put_post_revision', array($this, 'fix_revision_meta'));
411 remove_filter('content_save_pre', array($this, 'avoid_kses_munge'), 11);
412
413 $this->validate_post_id($dbpost, array(__CLASS__, __FUNCTION__));
414
415 // Unfortunately, as of WordPress 2.3, wp_insert_post()
416 // *still* offers no way to use a guid of your choice,
417 // and munges your post modified timestamp, too.
418 $result = $wpdb->query("
419 UPDATE $wpdb->posts
420 SET
421 guid='{$dbpost['guid']}',
422 post_modified='{$dbpost['post_modified']}',
423 post_modified_gmt='{$dbpost['post_modified_gmt']}'
424 WHERE ID='{$this->_wp_id}'
425 ");
426 else :
427 # The right way to do this is the above. But, alas,
428 # in earlier versions of WordPress, wp_insert_post has
429 # too much behavior (mainly related to pings) that can't
430 # be overridden. In WordPress 1.5, it's enough of a
431 # resource hog to make PHP segfault after inserting
432 # 50-100 posts. This can get pretty annoying, especially
433 # if you are trying to update your feeds for the first
434 # time.
435
436 $result = $wpdb->query("
437 INSERT INTO $wpdb->posts
438 SET
439 guid = '{$dbpost['guid']}',
440 post_author = '{$dbpost['post_author']}',
441 post_date = '{$dbpost['post_date']}',
442 post_date_gmt = '{$dbpost['post_date_gmt']}',
443 post_content = '{$dbpost['post_content']}',"
444 .(isset($dbpost['post_excerpt']) ? "post_excerpt = '{$dbpost['post_excerpt']}'," : "")."
445 post_title = '{$dbpost['post_title']}',
446 post_name = '{$dbpost['post_name']}',
447 post_modified = '{$dbpost['post_modified']}',
448 post_modified_gmt = '{$dbpost['post_modified_gmt']}',
449 comment_status = '{$dbpost['comment_status']}',
450 ping_status = '{$dbpost['ping_status']}',
451 post_status = '{$dbpost['post_status']}'
452 ");
453 $this->_wp_id = $wpdb->insert_id;
454
455 $this->validate_post_id($dbpost, array(__CLASS__, __FUNCTION__));
456
457 // WordPress 1.5.x - 2.0.x
458 wp_set_post_cats('1', $this->wp_id(), $this->post['post_category']);
459
460 // Since we are not going through official channels, we need to
461 // manually tell WordPress that we've published a new post.
462 // We need to make sure to do this in order for FeedWordPress
463 // to play well with the staticize-reloaded plugin (something
464 // that a large aggregator website is going to *want* to be
465 // able to use).
466 do_action('publish_post', $this->_wp_id);
467 endif;
468 endif;
469 } /* SyndicatedPost::insert_new() */
470
471 function update_existing () {
472 global $wpdb;
473
474 // Why the fuck doesn't wp_insert_post already do this?
475 $dbpost = $this->normalize_post(/*new=*/ false);
476 if (!is_null($dbpost)) :
477 if ($this->use_api('wp_insert_post')) :
478 $dbpost['post_pingback'] = false; // Tell WP 2.1 and 2.2 not to process for pingbacks
479
480 // This is a ridiculous fucking kludge necessitated by WordPress 2.6 munging authorship meta-data
481 add_action('_wp_put_post_revision', array($this, 'fix_revision_meta'));
482
483 // Kludge to prevent kses filters from stripping the
484 // content of posts when updating without a logged in
485 // user who has `unfiltered_html` capability.
486 add_filter('content_save_pre', array($this, 'avoid_kses_munge'), 11);
487
488 // Don't munge status fields that the user may have reset manually
489 if (function_exists('get_post_field')) :
490 $doNotMunge = array('post_status', 'comment_status', 'ping_status');
491 foreach ($doNotMunge as $field) :
492 $dbpost[$field] = get_post_field($field, $this->wp_id());
493 endforeach;
494 endif;
495
496 $this->_wp_id = wp_insert_post($dbpost);
497
498 // Turn off ridiculous fucking kludges #1 and #2
499 remove_action('_wp_put_post_revision', array($this, 'fix_revision_meta'));
500 remove_filter('content_save_pre', array($this, 'avoid_kses_munge'), 11);
501
502 $this->validate_post_id($dbpost, array(__CLASS__, __FUNCTION__));
503
504 // Unfortunately, as of WordPress 2.3, wp_insert_post()
505 // munges your post modified timestamp.
506 $result = $wpdb->query("
507 UPDATE $wpdb->posts
508 SET
509 post_modified='{$dbpost['post_modified']}',
510 post_modified_gmt='{$dbpost['post_modified_gmt']}'
511 WHERE ID='{$this->_wp_id}'
512 ");
513 else :
514
515 $result = $wpdb->query("
516 UPDATE $wpdb->posts
517 SET
518 post_author = '{$dbpost['post_author']}',
519 post_content = '{$dbpost['post_content']}',"
520 .(isset($dbpost['post_excerpt']) ? "post_excerpt = '{$dbpost['post_excerpt']}'," : "")."
521 post_title = '{$dbpost['post_title']}',
522 post_name = '{$dbpost['post_name']}',
523 post_modified = '{$dbpost['post_modified']}',
524 post_modified_gmt = '{$dbpost['post_modified_gmt']}'
525 WHERE guid='{$dbpost['guid']}'
526 ");
527
528 // WordPress 2.1.x and up
529 if (function_exists('wp_set_post_categories')) :
530 wp_set_post_categories($this->wp_id(), $this->post['post_category']);
531 // WordPress 1.5.x - 2.0.x
532 elseif (function_exists('wp_set_post_cats')) :
533 wp_set_post_cats('1', $this->wp_id(), $this->post['post_category']);
534 // This should never happen.
535 else :
536 FeedWordPress::critical_bug(__CLASS__.'::'.__FUNCTION.'(): no post categorizing function', array("dbpost" => $dbpost, "this" => $this), __LINE__);
537 endif;
538
539 // Since we are not going through official channels, we need to
540 // manually tell WordPress that we've published a new post.
541 // We need to make sure to do this in order for FeedWordPress
542 // to play well with the staticize-reloaded plugin (something
543 // that a large aggregator website is going to *want* to be
544 // able to use).
545 do_action('edit_post', $this->post['ID']);
546 endif;
547 endif;
548 } /* SyndicatedPost::update_existing() */
549
550 /**
551 * SyndicatedPost::normalize_post()
552 *
553 * @param bool $new If true, this post is to be inserted anew. If false, it is an update of an existing post.
554 * @return array A normalized representation of the post ready to be inserted into the database or sent to the WordPress API functions
555 */
556 function normalize_post ($new = true) {
557 global $wpdb;
558
559 $out = array();
560
561 // Why the fuck doesn't wp_insert_post already do this?
562 foreach ($this->post as $key => $value) :
563 if (is_string($value)) :
564 $out[$key] = $wpdb->escape($value);
565 else :
566 $out[$key] = $value;
567 endif;
568 endforeach;
569
570 if (strlen($out['post_title'].$out['post_content'].$out['post_excerpt']) == 0) :
571 // FIXME: Option for filtering out empty posts
572 endif;
573 if (strlen($out['post_title'])==0) :
574 $offset = (int) get_option('gmt_offset') * 60 * 60;
575 $out['post_title'] =
576 $this->post['meta']['syndication_source']
577 .' '.gmdate('Y-m-d H:i:s', $this->published() + $offset);
578 // FIXME: Option for what to fill a blank title with...
579 endif;
580
581 return $out;
582 }
583
584 /**
585 * SyndicatedPost::validate_post_id()
586 *
587 * @param array $dbpost An array representing the post we attempted to insert or update
588 * @param mixed $ns A string or array representing the namespace (class, method) whence this method was called.
589 */
590 function validate_post_id ($dbpost, $ns) {
591 if (is_array($ns)) : $ns = implode('::', $ns);
592 else : $ns = (string) $ns; endif;
593
594 // This should never happen.
595 if (!is_numeric($this->_wp_id) or ($this->_wp_id == 0)) :
596 FeedWordPress::critical_bug(
597 /*name=*/ $ns.'::_wp_id',
598 /*var =*/ array(
599 "\$this->_wp_id" => $this->_wp_id,
600 "\$dbpost" => $dbpost,
601 "\$this" => $this
602 ),
603 /*line # =*/ __LINE__
604 );
605 endif;
606 } /* SyndicatedPost::validate_post_id() */
607
608 /**
609 * SyndicatedPost::fix_revision_meta() - Fixes the way WP 2.6+ fucks up
610 * meta-data (authorship, etc.) when storing revisions of an updated
611 * syndicated post.
612 *
613 * In their infinite wisdom, the WordPress coders have made it completely
614 * impossible for a plugin that uses wp_insert_post() to set certain
615 * meta-data (such as the author) when you store an old revision of an
616 * updated post. Instead, it uses the WordPress defaults (= currently
617 * active user ID if the process is running with a user logged in, or
618 * = #0 if there is no user logged in). This results in bogus authorship
619 * data for revisions that are syndicated from off the feed, unless we
620 * use a ridiculous kludge like this to end-run the munging of meta-data
621 * by _wp_put_post_revision.
622 *
623 * @param int $revision_id The revision ID to fix up meta-data
624 */
625 function fix_revision_meta ($revision_id) {
626 global $wpdb;
627
628 $post_author = (int) $this->post['post_author'];
629
630 $revision_id = (int) $revision_id;
631 $wpdb->query("
632 UPDATE $wpdb->posts
633 SET post_author={$this->post['post_author']}
634 WHERE post_type = 'revision' AND ID='$revision_id'
635 ");
636 } /* SyndicatedPost::fix_revision_meta () */
637
638 /**
639 * SyndicatedPost::avoid_kses_munge() -- If FeedWordPress is processing
640 * an automatic update, that generally means that wp_insert_post() is
641 * being called under the user credentials of whoever is viewing the
642 * blog at the time -- usually meaning no user at all. But if WordPress
643 * gets a wp_insert_post() when current_user_can('unfiltered_html') is
644 * false, it will run the content of the post through a kses function
645 * that strips out lots of HTML tags -- notably <object> and some others.
646 * This causes problems for syndicating (for example) feeds that contain
647 * YouTube videos. It also produces an unexpected asymmetry between
648 * automatically-initiated updates and updates initiated manually from
649 * the WordPress Dashboard (which are usually initiated under the
650 * credentials of a logged-in admin, and so don't get run through the
651 * kses function). So, to avoid the whole mess, what we do here is
652 * just forcibly disable the kses munging for a single syndicated post,
653 * by restoring the contents of the `post_content` field.
654 *
655 * @param string $content The content of the post, after other filters have gotten to it
656 * @return string The original content of the post, before other filters had a chance to munge it.
657 */
658 function avoid_kses_munge ($content) {
659 global $wpdb;
660 return $wpdb->escape($this->post['post_content']);
661 }
662
663 // SyndicatedPost::add_rss_meta: adds interesting meta-data to each entry
664 // using the space for custom keys. The set of keys and values to add is
665 // specified by the keys and values of $post['meta']. This is used to
666 // store anything that the WordPress user might want to access from a
667 // template concerning the post's original source that isn't provided
668 // for by standard WP meta-data (i.e., any interesting data about the
669 // syndicated post other than author, title, timestamp, categories, and
670 // guid). It's also used to hook into WordPress's support for
671 // enclosures.
672 function add_rss_meta () {
673 global $wpdb;
674 if ( is_array($this->post) and isset($this->post['meta']) and is_array($this->post['meta']) ) :
675 $postId = $this->wp_id();
676
677 // Aggregated posts should NOT send out pingbacks.
678 // WordPress 2.1-2.2 claim you can tell them not to
679 // using $post_pingback, but they don't listen, so we
680 // make sure here.
681 $result = $wpdb->query("
682 DELETE FROM $wpdb->postmeta
683 WHERE post_id='$postId' AND meta_key='_pingme'
684 ");
685
686 foreach ( $this->post['meta'] as $key => $values ) :
687
688 $key = $wpdb->escape($key);
689
690 // If this is an update, clear out the old
691 // values to avoid duplication.
692 $result = $wpdb->query("
693 DELETE FROM $wpdb->postmeta
694 WHERE post_id='$postId' AND meta_key='$key'
695 ");
696
697 // Allow for either a single value or an array
698 if (!is_array($values)) $values = array($values);
699 foreach ( $values as $value ) :
700 $value = $wpdb->escape($value);
701 $result = $wpdb->query("
702 INSERT INTO $wpdb->postmeta
703 SET
704 post_id='$postId',
705 meta_key='$key',
706 meta_value='$value'
707 ");
708 if (!$result) :
709 $err = mysql_error();
710 if (FEEDWORDPRESS_DEBUG) :
711 echo "[DEBUG:".date('Y-m-d H:i:S')."][feedwordpress]: post metadata insertion FAILED for field '$key' := '$value': [$err]";
712 endif;
713 endif;
714 endforeach;
715 endforeach;
716 endif;
717 } /* SyndicatedPost::add_rss_meta () */
718
719 // SyndicatedPost::author_id (): get the ID for an author name from
720 // the feed. Create the author if necessary.
721 function author_id ($unfamiliar_author = 'create') {
722 global $wpdb;
723
724 $a = $this->author();
725 $author = $a['name'];
726 $email = $a['email'];
727 $url = $a['uri'];
728
729 $match_author_by_email = !('yes' == get_option("feedwordpress_do_not_match_author_by_email"));
730 if ($match_author_by_email and !FeedWordPress::is_null_email($email)) :
731 $test_email = $email;
732 else :
733 $test_email = NULL;
734 endif;
735
736 // Never can be too careful...
737 $login = sanitize_user($author, /*strict=*/ true);
738 $login = apply_filters('pre_user_login', $login);
739
740 $nice_author = sanitize_title($author);
741 $nice_author = apply_filters('pre_user_nicename', $nice_author);
742
743 $reg_author = $wpdb->escape(preg_quote($author));
744 $author = $wpdb->escape($author);
745 $email = $wpdb->escape($email);
746 $test_email = $wpdb->escape($test_email);
747 $url = $wpdb->escape($url);
748
749 // Check for an existing author rule....
750 if (isset($this->link->settings['map authors']['name'][strtolower(trim($author))])) :
751 $author_rule = $this->link->settings['map authors']['name'][strtolower(trim($author))];
752 else :
753 $author_rule = NULL;
754 endif;
755
756 // User name is mapped to a particular author. If that author ID exists, use it.
757 if (is_numeric($author_rule) and get_userdata((int) $author_rule)) :
758 $id = (int) $author_rule;
759
760 // User name is filtered out
761 elseif ('filter' == $author_rule) :
762 $id = NULL;
763
764 else :
765 // Check the database for an existing author record that might fit
766
767 #-- WordPress 2.0+
768 if (fwp_test_wp_version(FWP_SCHEMA_HAS_USERMETA)) :
769
770 // First try the user core data table.
771 $id = $wpdb->get_var(
772 "SELECT ID FROM $wpdb->users
773 WHERE
774 TRIM(LCASE(user_login)) = TRIM(LCASE('$login'))
775 OR (
776 LENGTH(TRIM(LCASE(user_email))) > 0
777 AND TRIM(LCASE(user_email)) = TRIM(LCASE('$test_email'))
778 )
779 OR TRIM(LCASE(user_nicename)) = TRIM(LCASE('$nice_author'))
780 ");
781
782 // If that fails, look for aliases in the user meta data table
783 if (is_null($id)) :
784 $id = $wpdb->get_var(
785 "SELECT user_id FROM $wpdb->usermeta
786 WHERE
787 (meta_key = 'description' AND TRIM(LCASE(meta_value)) = TRIM(LCASE('$author')))
788 OR (
789 meta_key = 'description'
790 AND TRIM(LCASE(meta_value))
791 RLIKE CONCAT(
792 '(^|\\n)a\\.?k\\.?a\\.?( |\\t)*:?( |\\t)*',
793 TRIM(LCASE('$reg_author')),
794 '( |\\t|\\r)*(\\n|\$)'
795 )
796 )
797 ");
798 endif;
799
800 #-- WordPress 1.5.x
801 else :
802 $id = $wpdb->get_var(
803 "SELECT ID from $wpdb->users
804 WHERE
805 TRIM(LCASE(user_login)) = TRIM(LCASE('$login')) OR
806 (
807 LENGTH(TRIM(LCASE(user_email))) > 0
808 AND TRIM(LCASE(user_email)) = TRIM(LCASE('$test_email'))
809 ) OR
810 TRIM(LCASE(user_firstname)) = TRIM(LCASE('$author')) OR
811 TRIM(LCASE(user_nickname)) = TRIM(LCASE('$author')) OR
812 TRIM(LCASE(user_nicename)) = TRIM(LCASE('$nice_author')) OR
813 TRIM(LCASE(user_description)) = TRIM(LCASE('$author')) OR
814 (
815 LOWER(user_description)
816 RLIKE CONCAT(
817 '(^|\\n)a\\.?k\\.?a\\.?( |\\t)*:?( |\\t)*',
818 LCASE('$reg_author'),
819 '( |\\t|\\r)*(\\n|\$)'
820 )
821 )
822 ");
823
824 endif;
825
826 // ... if you don't find one, then do what you need to do
827 if (is_null($id)) :
828 if ($unfamiliar_author === 'create') :
829 $userdata = array();
830
831 #-- user table data
832 $userdata['ID'] = NULL; // new user
833 $userdata['user_login'] = $login;
834 $userdata['user_nicename'] = $nice_author;
835 $userdata['user_pass'] = substr(md5(uniqid(microtime())), 0, 6); // just something random to lock it up
836 $userdata['user_email'] = $email;
837 $userdata['user_url'] = $url;
838 $userdata['display_name'] = $author;
839
840 $id = wp_insert_user($userdata);
841 elseif (is_numeric($unfamiliar_author) and get_userdata((int) $unfamiliar_author)) :
842 $id = (int) $unfamiliar_author;
843 elseif ($unfamiliar_author === 'default') :
844 $id = 1;
845 endif;
846 endif;
847 endif;
848
849 if ($id) :
850 $this->link->settings['map authors']['name'][strtolower(trim($author))] = $id;
851 endif;
852 return $id;
853 } // function SyndicatedPost::author_id ()
854
855 // look up (and create) category ids from a list of categories
856 function category_ids ($cats, $unfamiliar_category = 'create', $tags_too = false) {
857 global $wpdb;
858
859 // We need to normalize whitespace because (1) trailing
860 // whitespace can cause PHP and MySQL not to see eye to eye on
861 // VARCHAR comparisons for some versions of MySQL (cf.
862 // <http://dev.mysql.com/doc/mysql/en/char.html>), and (2)
863 // because I doubt most people want to make a semantic
864 // distinction between 'Computers' and 'Computers '
865 $cats = array_map('trim', $cats);
866
867 $tags = array();
868
869 $cat_ids = array ();
870 foreach ($cats as $cat_name) :
871 if (preg_match('/^{#([0-9]+)}$/', $cat_name, $backref)) :
872 $cat_id = (int) $backref[1];
873 if (function_exists('is_term') and is_term($cat_id, 'category')) :
874 $cat_ids[] = $cat_id;
875 elseif (get_category($cat_id)) :
876 $cat_ids[] = $cat_id;
877 endif;
878 elseif (strlen($cat_name) > 0) :
879 $esc = $wpdb->escape($cat_name);
880 $resc = $wpdb->escape(preg_quote($cat_name));
881
882 // WordPress 2.3+
883 if (function_exists('is_term')) :
884 $cat_id = is_term($cat_name, 'category');
885 if ($cat_id) :
886 $cat_ids[] = $cat_id['term_id'];
887 // There must be a better way to do this...
888 elseif ($results = $wpdb->get_results(
889 "SELECT term_id
890 FROM $wpdb->term_taxonomy
891 WHERE
892 LOWER(description) RLIKE
893 CONCAT('(^|\\n)a\\.?k\\.?a\\.?( |\\t)*:?( |\\t)*', LOWER('{$resc}'), '( |\\t|\\r)*(\\n|\$)')"
894 )) :
895 foreach ($results AS $term) :
896 $cat_ids[] = (int) $term->term_id;
897 endforeach;
898 elseif ('tag'==$unfamiliar_category) :
899 $tags[] = $cat_name;
900 elseif ('create'===$unfamiliar_category) :
901 $term = wp_insert_term($cat_name, 'category');
902 if (is_wp_error($term)) :
903 FeedWordPress::noncritical_bug('term insertion problem', array('cat_name' => $cat_name, 'term' => $term, 'this' => $this), __LINE__);
904 else :
905 $cat_ids[] = $term['term_id'];
906 endif;
907 endif;
908
909 // WordPress 1.5.x - 2.2.x
910 else :
911 $results = $wpdb->get_results(
912 "SELECT cat_ID
913 FROM $wpdb->categories
914 WHERE
915 (LOWER(cat_name) = LOWER('$esc'))
916 OR (LOWER(category_description)
917 RLIKE CONCAT('(^|\\n)a\\.?k\\.?a\\.?( |\\t)*:?( |\\t)*', LOWER('{$resc}'), '( |\\t|\\r)*(\\n|\$)'))
918 ");
919 if ($results) :
920 foreach ($results as $term) :
921 $cat_ids[] = (int) $term->cat_ID;
922 endforeach;
923 elseif ('create'===$unfamiliar_category) :
924 if (function_exists('wp_insert_category')) :
925 $cat_id = wp_insert_category(array('cat_name' => $esc));
926 // And into the database we go.
927 else :
928 $nice_kitty = sanitize_title($cat_name);
929 $wpdb->query(sprintf("
930 INSERT INTO $wpdb->categories
931 SET
932 cat_name='%s',
933 category_nicename='%s'
934 ", $esc, $nice_kitty
935 ));
936 $cat_id = $wpdb->insert_id;
937 endif;
938 $cat_ids[] = $cat_id;
939 endif;
940 endif;
941 endif;
942 endforeach;
943
944 if ((count($cat_ids) == 0) and ($unfamiliar_category === 'filter')) :
945 $cat_ids = NULL; // Drop the post
946 else :
947 $cat_ids = array_unique($cat_ids);
948 endif;
949
950 if ($tags_too) : $ret = array($cat_ids, $tags);
951 else : $ret = $cat_ids;
952 endif;
953
954 return $ret;
955 } // function SyndicatedPost::category_ids ()
956
957 function use_api ($tag) {
958 global $wp_db_version;
959 switch ($tag) :
960 case 'wp_insert_post':
961 // Before 2.2, wp_insert_post does too much of the wrong stuff to use it
962 // In 1.5 it was such a resource hog it would make PHP segfault on big updates
963 $ret = (isset($wp_db_version) and $wp_db_version > FWP_SCHEMA_21);
964 break;
965 case 'post_status_pending':
966 $ret = (isset($wp_db_version) and $wp_db_version > FWP_SCHEMA_23);
967 break;
968 endswitch;
969 return $ret;
970 } // function SyndicatedPost::use_api ()
971
972 #### EXTRACT DATA FROM FEED ITEM ####
973
974 function created () {
975 $epoch = null;
976 if (isset($this->item['dc']['created'])) :
977 $epoch = @parse_w3cdtf($this->item['dc']['created']);
978 elseif (isset($this->item['dcterms']['created'])) :
979 $epoch = @parse_w3cdtf($this->item['dcterms']['created']);
980 elseif (isset($this->item['created'])): // Atom 0.3
981 $epoch = @parse_w3cdtf($this->item['created']);
982 endif;
983 return $epoch;
984 }
985 function published ($fallback = true) {
986 $epoch = null;
987
988 # RSS is a fucking mess. Figure out whether we have a date in
989 # <dc:date>, <issued>, <pubDate>, etc., and get it into Unix
990 # epoch format for reformatting. If we can't find anything,
991 # we'll use the last-updated time.
992 if (isset($this->item['dc']['date'])): // Dublin Core
993 $epoch = @parse_w3cdtf($this->item['dc']['date']);
994 elseif (isset($this->item['dcterms']['issued'])) : // Dublin Core extensions
995 $epoch = @parse_w3cdtf($this->item['dcterms']['issued']);
996 elseif (isset($this->item['published'])) : // Atom 1.0
997 $epoch = @parse_w3cdtf($this->item['published']);
998 elseif (isset($this->item['issued'])): // Atom 0.3
999 $epoch = @parse_w3cdtf($this->item['issued']);
1000 elseif (isset($this->item['pubdate'])): // RSS 2.0
1001 $epoch = strtotime($this->item['pubdate']);
1002 elseif ($fallback) : // Fall back to <updated> / <modified> if present
1003 $epoch = $this->updated(/*fallback=*/ false);
1004 endif;
1005
1006 # If everything failed, then default to the current time.
1007 if (is_null($epoch)) :
1008 if (-1 == $default) :
1009 $epoch = time();
1010 else :
1011 $epoch = $default;
1012 endif;
1013 endif;
1014
1015 return $epoch;
1016 }
1017 function updated ($fallback = true, $default = -1) {
1018 $epoch = null;
1019
1020 # As far as I know, only dcterms and Atom have reliable ways to
1021 # specify when something was *modified* last. If neither is
1022 # available, then we'll try to get the time of publication.
1023 if (isset($this->item['dc']['modified'])) : // Not really correct
1024 $epoch = @parse_w3cdtf($this->item['dc']['modified']);
1025 elseif (isset($this->item['dcterms']['modified'])) : // Dublin Core extensions
1026 $epoch = @parse_w3cdtf($this->item['dcterms']['modified']);
1027 elseif (isset($this->item['modified'])): // Atom 0.3
1028 $epoch = @parse_w3cdtf($this->item['modified']);
1029 elseif (isset($this->item['updated'])): // Atom 1.0
1030 $epoch = @parse_w3cdtf($this->item['updated']);
1031 elseif ($fallback) : // Fall back to issued / dc:date
1032 $epoch = $this->published(/*fallback=*/ false, /*default=*/ $default);
1033 endif;
1034
1035 # If everything failed, then default to the current time.
1036 if (is_null($epoch)) :
1037 if (-1 == $default) :
1038 $epoch = time();
1039 else :
1040 $epoch = $default;
1041 endif;
1042 endif;
1043
1044 return $epoch;
1045 }
1046
1047 function update_hash () {
1048 return md5(serialize($this->item));
1049 }
1050
1051 function guid () {
1052 $guid = null;
1053 if (isset($this->item['id'])): // Atom 0.3 / 1.0
1054 $guid = $this->item['id'];
1055 elseif (isset($this->item['atom']['id'])) : // Namespaced Atom
1056 $guid = $this->item['atom']['id'];
1057 elseif (isset($this->item['guid'])) : // RSS 2.0
1058 $guid = $this->item['guid'];
1059 elseif (isset($this->item['dc']['identifier'])) :// yeah, right
1060 $guid = $this->item['dc']['identifier'];
1061 else :
1062 // The feed does not seem to have provided us with a
1063 // unique identifier, so we'll have to cobble together
1064 // a tag: URI that might work for us. The base of the
1065 // URI will be the host name of the feed source ...
1066 $bits = parse_url($this->feedmeta['link/uri']);
1067 $guid = 'tag:'.$bits['host'];
1068
1069 // If we have a date of creation, then we can use that
1070 // to uniquely identify the item. (On the other hand, if
1071 // the feed producer was consicentious enough to
1072 // generate dates of creation, she probably also was
1073 // conscientious enough to generate unique identifiers.)
1074 if (!is_null($this->created())) :
1075 $guid .= '://post.'.date('YmdHis', $this->created());
1076
1077 // Otherwise, use both the URI of the item, *and* the
1078 // item's title. We have to use both because titles are
1079 // often not unique, and sometimes links aren't unique
1080 // either (e.g. Bitch (S)HITLIST, Mozilla Dot Org news,
1081 // some podcasts). But it's rare to have *both* the same
1082 // title *and* the same link for two different items. So
1083 // this is about the best we can do.
1084 else :
1085 $guid .= '://'.md5($this->item['link'].'/'.$this->item['title']);
1086 endif;
1087 endif;
1088 return $guid;
1089 }
1090
1091 function author () {
1092 $author = array ();
1093
1094 if (isset($this->item['author_name'])):
1095 $author['name'] = $this->item['author_name'];
1096 elseif (isset($this->item['dc']['creator'])):
1097 $author['name'] = $this->item['dc']['creator'];
1098 elseif (isset($this->item['dc']['contributor'])):
1099 $author['name'] = $this->item['dc']['contributor'];
1100 elseif (isset($this->feed->channel['dc']['creator'])) :
1101 $author['name'] = $this->feed->channel['dc']['creator'];
1102 elseif (isset($this->feed->channel['dc']['contributor'])) :
1103 $author['name'] = $this->feed->channel['dc']['contributor'];
1104 elseif (isset($this->feed->channel['author_name'])) :
1105 $author['name'] = $this->feed->channel['author_name'];
1106 elseif ($this->feed->is_rss() and isset($this->item['author'])) :
1107 // The author element in RSS is allegedly an
1108 // e-mail address, but lots of people don't use
1109 // it that way. So let's make of it what we can.
1110 $author = parse_email_with_realname($this->item['author']);
1111
1112 if (!isset($author['name'])) :
1113 if (isset($author['email'])) :
1114 $author['name'] = $author['email'];
1115 else :
1116 $author['name'] = $this->feed->channel['title'];
1117 endif;
1118 endif;
1119 else :
1120 $author['name'] = $this->feed->channel['title'];
1121 endif;
1122
1123 if (isset($this->item['author_email'])):
1124 $author['email'] = $this->item['author_email'];
1125 elseif (isset($this->feed->channel['author_email'])) :
1126 $author['email'] = $this->feed->channel['author_email'];
1127 endif;
1128
1129 if (isset($this->item['author_url'])):
1130 $author['uri'] = $this->item['author_url'];
1131 elseif (isset($this->feed->channel['author_url'])) :
1132 $author['uri'] = $this->item['author_url'];
1133 else:
1134 $author['uri'] = $this->feed->channel['link'];
1135 endif;
1136
1137 return $author;
1138 } // SyndicatedPost::author()
1139
1140 /**
1141 * SyndicatedPost::isTaggedAs: Test whether a feed item is
1142 * tagged / categorized with a given string. Case and leading and
1143 * trailing whitespace are ignored.
1144 *
1145 * @param string $tag Tag to check for
1146 *
1147 * @return bool Whether or not at least one of the categories / tags on
1148 * $this->item is set to $tag (modulo case and leading and trailing
1149 * whitespace)
1150 */
1151 function isTaggedAs ($tag) {
1152 $desiredTag = strtolower(trim($tag)); // Normalize case and whitespace
1153
1154 // Check to see if this is tagged with $tag
1155 $currentCategory = 'category';
1156 $currentCategoryNumber = 1;
1157
1158 // If we have the new MagpieRSS, the number of category elements
1159 // on this item is stored under index "category#".
1160 if (isset($this->item['category#'])) :
1161 $numberOfCategories = (int) $this->item['category#'];
1162
1163 // We REALLY shouldn't have the old and busted MagpieRSS, but in
1164 // case we do, it doesn't support multiple categories, but there
1165 // might still be a single value under the "category" index.
1166 elseif (isset($this->item['category'])) :
1167 $numberOfCategories = 1;
1168
1169 // No standard category or tag elements on this feed item.
1170 else :
1171 $numberOfCategories = 0;
1172
1173 endif;
1174
1175 $isSoTagged = false; // Innocent until proven guilty
1176
1177 // Loop through category elements; if there are multiple
1178 // elements, they are indexed as category, category#2,
1179 // category#3, ... category#N
1180 while ($currentCategoryNumber <= $numberOfCategories) :
1181 if ($desiredTag == strtolower(trim($this->item[$currentCategory]))) :
1182 $isSoTagged = true; // Got it!
1183 break;
1184 endif;
1185
1186 $currentCategoryNumber += 1;
1187 $currentCategory = 'category#'.$currentCategoryNumber;
1188 endwhile;
1189
1190 return $isSoTagged;
1191 } /* SyndicatedPost::isTaggedAs() */
1192
1193 var $uri_attrs = array (
1194 array('a', 'href'),
1195 array('applet', 'codebase'),
1196 array('area', 'href'),
1197 array('blockquote', 'cite'),
1198 array('body', 'background'),
1199 array('del', 'cite'),
1200 array('form', 'action'),
1201 array('frame', 'longdesc'),
1202 array('frame', 'src'),
1203 array('iframe', 'longdesc'),
1204 array('iframe', 'src'),
1205 array('head', 'profile'),
1206 array('img', 'longdesc'),
1207 array('img', 'src'),
1208 array('img', 'usemap'),
1209 array('input', 'src'),
1210 array('input', 'usemap'),
1211 array('ins', 'cite'),
1212 array('link', 'href'),
1213 array('object', 'classid'),
1214 array('object', 'codebase'),
1215 array('object', 'data'),
1216 array('object', 'usemap'),
1217 array('q', 'cite'),
1218 array('script', 'src')
1219 ); /* var SyndicatedPost::$uri_attrs */
1220
1221 var $_base = null;
1222
1223 function resolve_single_relative_uri ($refs) {
1224 $tag = FeedWordPressHTML::attributeMatch($refs);
1225 $url = Relative_URI::resolve($tag['value'], $this->_base);
1226 return $tag['prefix'] . $url . $tag['suffix'];
1227 } /* function SyndicatedPost::resolve_single_relative_uri() */
1228
1229 function resolve_relative_uris ($content, $obj) {
1230 # The MagpieRSS upgrade has some `xml:base` support baked in.
1231 # However, sometimes people do silly things, like putting
1232 # relative URIs out on a production RSS 2.0 feed or other feeds
1233 # with no good support for `xml:base`. So we'll do our best to
1234 # try to catch any remaining relative URIs and resolve them as
1235 # best we can.
1236 $obj->_base = $obj->item['link']; // Reset the base for resolving relative URIs
1237
1238 foreach ($obj->uri_attrs as $pair) :
1239 list($tag, $attr) = $pair;
1240 $pattern = FeedWordPressHTML::attributeRegex($tag, $attr);
1241 $content = preg_replace_callback (
1242 $pattern,
1243 array(&$obj, 'resolve_single_relative_uri'),
1244 $content
1245 );
1246 endforeach;
1247
1248 return $content;
1249 } /* function SyndicatedPost::resolve_relative_uris () */
1250
1251 var $strip_attrs = array (
1252 array('[a-z]+', 'target'),
1253 // array('[a-z]+', 'style'),
1254 // array('[a-z]+', 'on[a-z]+'),
1255 );
1256
1257 function strip_attribute_from_tag ($refs) {
1258 $tag = FeedWordPressHTML::attributeMatch($refs);
1259 return $tag['before_attribute'].$tag['after_attribute'];
1260 }
1261
1262 function sanitize_content ($content, $obj) {
1263 # This kind of sucks. I intend to replace it with
1264 # lib_filter sometime soon.
1265 foreach ($obj->strip_attrs as $pair):
1266 list($tag,$attr) = $pair;
1267 $pattern = FeedWordPressHTML::attributeRegex($tag, $attr);
1268
1269 $content = preg_replace_callback (
1270 $pattern,
1271 array(&$obj, 'strip_attribute_from_tag'),
1272 $content
1273 );
1274 endforeach;
1275 return $content;
1276 }
1277 } // class SyndicatedPost
1278
1279