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