PluginProbe
FeedWordPress / 2016.1211
FeedWordPress v2016.1211
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 | syndicatedlink.class.php +885 -158 2009.06122016.1211 View file →
@@ -24,9 +24,9 @@
24 24 # Values have linebreak characters escaped with C-style
25 25 # backslashes (so, for example, a newline becomes "\n").
26 26 #
27 27 # The value of `cats` is used as a newline-separated list of
28 -# default categories for any post coming from a particular feed.
28 +# default categories for any post coming from a particular feed.
29 29 # (In the example above, any posts from this feed will be placed
30 30 # in the "computers" and "web" categories--*in addition to* any
31 31 # categories that may already be applied to the posts.)
32 32 #
@@ -32,15 +32,19 @@
32 32 #
33 33 # Values of keys in link_notes are accessible from templates using
34 34 # the function `get_feed_meta($key)` if this plugin is activated.
35 35
36 +require_once(dirname(__FILE__).'/magpiefromsimplepie.class.php');
37 +require_once(dirname(__FILE__).'/feedwordpressparsedpostmeta.class.php');
38 +
36 39 class SyndicatedLink {
37 40 var $id = null;
38 41 var $link = null;
39 42 var $settings = array ();
43 + public $simplepie = null;
40 44 var $magpie = null;
41 45
42 - function SyndicatedLink ($link) {
46 + function __construct( $link ) {
43 47 global $wpdb;
44 48
45 49 if (is_object($link)) :
46 50 $this->link = $link;
@@ -46,119 +50,143 @@
46 50 $this->link = $link;
47 51 $this->id = $link->link_id;
48 52 else :
49 53 $this->id = $link;
50 - if (function_exists('get_bookmark')) : // WP 2.1+
51 - $this->link = get_bookmark($link);
52 - else :
53 - $this->link = $wpdb->get_row("
54 - SELECT * FROM $wpdb->links
55 - WHERE (link_id = '".$wpdb->escape($link)."')"
56 - );
57 - endif;
54 + $this->link = get_bookmark($link);
58 55 endif;
59 56
60 57 if (strlen($this->link->link_rss) > 0) :
61 - // Read off feed settings from link_notes
62 - $notes = explode("\n", $this->link->link_notes);
63 - foreach ($notes as $note):
64 - list($key, $value) = explode(": ", $note, 2);
65 -
66 - if (strlen($key) > 0) :
67 - // Unescape and trim() off the whitespace.
68 - // Thanks to Ray Lischner for pointing out the
69 - // need to trim off whitespace.
70 - $this->settings[$key] = stripcslashes (trim($value));
71 - endif;
72 - endforeach;
58 + $this->get_settings_from_notes();
59 + endif;
73 60
74 - // "Magic" feed settings
75 - $this->settings['link/uri'] = $this->link->link_rss;
76 - $this->settings['link/name'] = $this->link->link_name;
77 - $this->settings['link/id'] = $this->link->link_id;
78 -
79 - // `hardcode categories` and `unfamiliar categories` are deprecated in favor of `unfamiliar category`
80 - if (
81 - isset($this->settings['unfamiliar categories'])
82 - and !isset($this->settings['unfamiliar category'])
83 - ) :
84 - $this->settings['unfamiliar category'] = $this->settings['unfamiliar categories'];
85 - endif;
86 - if (
87 - FeedWordPress::affirmative($this->settings, 'hardcode categories')
88 - and !isset($this->settings['unfamiliar category'])
89 - ) :
90 - $this->settings['unfamiliar category'] = 'default';
91 - endif;
61 + add_filter('feedwordpress_update_complete', array($this, 'process_retirements'), 1000, 1);
62 + } /* SyndicatedLink::SyndicatedLink () */
92 63
93 - // Set this up automagically for del.icio.us
94 - $bits = parse_url($this->link->link_rss);
95 - $tagspacers = array('del.icio.us', 'feeds.delicious.com');
96 - if (!isset($this->settings['cat_split']) and in_array($bits['host'], $tagspacers)) :
97 - $this->settings['cat_split'] = '\s'; // Whitespace separates multiple tags in del.icio.us RSS feeds
98 - endif;
64 + function SyndicatedLink( $link ) {
65 + self::__construct( $link );
66 + }
99 67
100 - if (isset($this->settings['cats'])):
101 - $this->settings['cats'] = preg_split(FEEDWORDPRESS_CAT_SEPARATOR_PATTERN, $this->settings['cats']);
102 - endif;
103 - if (isset($this->settings['tags'])):
104 - $this->settings['tags'] = preg_split(FEEDWORDPRESS_CAT_SEPARATOR_PATTERN, $this->settings['tags']);
105 - endif;
106 -
107 - if (isset($this->settings['map authors'])) :
108 - $author_rules = explode("\n\n", $this->settings['map authors']);
109 - $ma = array();
110 - foreach ($author_rules as $rule) :
111 - list($rule_type, $author_name, $author_action) = explode("\n", $rule);
112 -
113 - // Normalize for case and whitespace
114 - $rule_type = strtolower(trim($rule_type));
115 - $author_name = strtolower(trim($author_name));
116 - $author_action = strtolower(trim($author_action));
117 -
118 - $ma[$rule_type][$author_name] = $author_action;
119 - endforeach;
120 - $this->settings['map authors'] = $ma;
121 - endif;
122 - endif;
123 - } /* SyndicatedLink::SyndicatedLink () */
124 -
125 68 function found () {
126 - return is_object($this->link);
69 + return is_object($this->link) and !is_wp_error($this->link);
127 70 } /* SyndicatedLink::found () */
128 71
72 + function id () {
73 + return (is_object($this->link) ? $this->link->link_id : NULL);
74 + }
75 +
129 76 function stale () {
77 + global $feedwordpress;
78 +
130 79 $stale = true;
131 - if (isset($this->settings['update/hold']) and ($this->settings['update/hold']=='ping')) :
80 + if ($this->setting('update/hold')=='ping') :
132 81 $stale = false; // don't update on any timed updates; pings only
133 - elseif (isset($this->settings['update/hold']) and ($this->settings['update/hold']=='next')) :
82 + elseif ($this->setting('update/hold')=='next') :
134 83 $stale = true; // update on the next timed update
135 - elseif (!isset($this->settings['update/ttl']) or !isset($this->settings['update/last'])) :
84 + elseif ( !$this->setting('update/last') ) :
136 85 $stale = true; // initial update
86 + elseif ($feedwordpress->force_update_all()) :
87 + $stale = true; // forced general updating
137 88 else :
138 - $after = ((int) $this->settings['update/last'])
139 - +((int) $this->settings['update/ttl'] * 60);
89 + $after = (
90 + (int) $this->setting('update/last')
91 + + (int) $this->setting('update/fudge')
92 + + ((int) $this->setting('update/ttl') * 60)
93 + );
140 94 $stale = (time() >= $after);
141 95 endif;
142 96 return $stale;
143 97 } /* SyndicatedLink::stale () */
144 98
99 + function fetch () {
100 + $timeout = $this->setting('fetch timeout', 'feedwordpress_fetch_timeout', FEEDWORDPRESS_FETCH_TIMEOUT_DEFAULT);
101 +
102 + $this->simplepie = apply_filters(
103 + 'syndicated_feed',
104 + FeedWordPress::fetch($this, array('timeout' => $timeout)),
105 + $this
106 + );
107 +
108 + // Filter compatibility mode
109 + if (is_wp_error($this->simplepie)) :
110 + $this->magpie = $this->simplepie;
111 + else :
112 + $this->magpie = new MagpieFromSimplePie($this->simplepie, NULL);
113 + endif;
114 + }
115 + function live_posts () {
116 + if (!is_object($this->simplepie)) :
117 + $this->fetch();
118 + endif;
119 +
120 + if (is_object($this->simplepie) and method_exists($this->simplepie, 'get_items')) :
121 + $ret = apply_filters(
122 + 'syndicated_feed_items',
123 + $this->simplepie->get_items(),
124 + $this
125 + );
126 + else :
127 + $ret = $this->simplepie;
128 + endif;
129 + return $ret;
130 + }
131 +
145 132 function poll ($crash_ts = NULL) {
146 133 global $wpdb;
147 134
148 - $this->magpie = fetch_rss($this->link->link_rss);
135 + $url = $this->uri(array('add_params' => true, 'fetch' => true));
136 + FeedWordPress::diagnostic('updated_feeds', 'Polling feed ['.$url.']');
137 +
138 + $this->fetch();
139 +
149 140 $new_count = NULL;
150 141
151 - $resume = FeedWordPress::affirmative($this->settings, 'update/unfinished');
142 + $resume = ('yes'==$this->setting('update/unfinished'));
152 143 if ($resume) :
153 144 // pick up where we left off
154 - $processed = array_map('trim', explode("\n", $this->settings['update/processed']));
145 + $processed = array_map('trim', explode("\n", $this->setting('update/processed')));
155 146 else :
156 147 // begin at the beginning
157 148 $processed = array();
158 149 endif;
159 150
160 - if (is_object($this->magpie)) :
151 + if (is_wp_error($this->simplepie)) :
152 + $new_count = $this->simplepie;
153 + // Error; establish an error setting.
154 + $theError = array();
155 + $theError['ts'] = time();
156 + $theError['since'] = time();
157 + $theError['object'] = $this->simplepie;
158 +
159 + $oldError = $this->setting('update/error', NULL, NULL);
160 + if (is_string($oldError)) :
161 + $oldError = unserialize($oldError);
162 + endif;
163 +
164 + if (!is_null($oldError)) :
165 + // Copy over the in-error-since timestamp
166 + $theError['since'] = $oldError['since'];
167 +
168 + // If this is a repeat error, then we should
169 + // take a step back before we try to fetch it
170 + // again.
171 + $this->update_setting('update/last', time(), NULL);
172 + $ttl = $this->automatic_ttl();
173 + $ttl = apply_filters('syndicated_feed_ttl', $ttl, $this);
174 + $ttl = apply_filters('syndicated_feed_ttl_from_error', $ttl, $this);
175 + $this->update_setting('update/ttl', $ttl, $this);
176 + $this->update_setting('update/timed', 'automatically');
177 + endif;
178 +
179 + do_action('syndicated_feed_error', $theError, $oldError, $this);
180 +
181 + $this->update_setting('update/error', serialize($theError));
182 + $this->save_settings(/*reload=*/ true);
183 +
184 + elseif (is_object($this->simplepie)) :
185 +
186 + // Success; clear out error setting, if any.
187 + $this->update_setting('update/error', NULL);
188 +
161 189 $new_count = array('new' => 0, 'updated' => 0);
162 190
163 191 # -- Update Link metadata live from feed
164 192 $channel = $this->magpie->channel;
@@ -165,47 +193,58 @@
165 193
166 194 if (!isset($channel['id'])) :
167 195 $channel['id'] = $this->link->link_rss;
168 196 endif;
169 -
197 +
170 198 $update = array();
171 199 if (!$this->hardcode('url') and isset($channel['link'])) :
172 - $update[] = "link_url = '".$wpdb->escape($channel['link'])."'";
200 + $update[] = "link_url = '".esc_sql($channel['link'])."'";
173 201 endif;
174 -
202 +
175 203 if (!$this->hardcode('name') and isset($channel['title'])) :
176 - $update[] = "link_name = '".$wpdb->escape($channel['title'])."'";
204 + $update[] = "link_name = '".esc_sql($channel['title'])."'";
177 205 endif;
178 -
206 +
179 207 if (!$this->hardcode('description')) :
180 208 if (isset($channel['tagline'])) :
181 - $update[] = "link_description = '".$wpdb->escape($channel['tagline'])."'";
209 + $update[] = "link_description = '".esc_sql($channel['tagline'])."'";
182 210 elseif (isset($channel['description'])) :
183 - $update[] = "link_description = '".$wpdb->escape($channel['description'])."'";
211 + $update[] = "link_description = '".esc_sql($channel['description'])."'";
184 212 endif;
185 213 endif;
186 -
187 - $this->settings = array_merge($this->settings, $this->flatten_array($channel));
188 214
189 - $this->settings['update/last'] = time(); $ttl = $this->ttl();
215 + $this->merge_settings($channel, 'feed/');
216 +
217 + $this->update_setting('update/last', time());
218 + list($ttl, $xml) = $this->ttl(/*return element=*/ true);
219 +
190 220 if (!is_null($ttl)) :
191 - $this->settings['update/ttl'] = $ttl;
192 - $this->settings['update/timed'] = 'feed';
221 + $this->update_setting('update/ttl', $ttl);
222 + $this->update_setting('update/xml', $xml);
223 + $this->update_setting('update/timed', 'feed');
193 224 else :
194 - $this->settings['update/ttl'] = rand(30, 120); // spread over time interval for staggered updates
195 - $this->settings['update/timed'] = 'automatically';
225 + $ttl = $this->automatic_ttl();
226 + $this->update_setting('update/ttl', $ttl);
227 + $this->update_setting('update/xml', NULL);
228 + $this->update_setting('update/timed', 'automatically');
196 229 endif;
230 + $this->update_setting('update/fudge', rand(0, ($ttl/3))*60);
231 + $this->update_setting('update/ttl', apply_filters(
232 + 'syndicated_feed_ttl',
233 + $this->setting('update/ttl'),
234 + $this
235 + ));
197 236
198 - if (!isset($this->settings['update/hold']) or $this->settings['update/hold']!='ping') :
199 - $this->settings['update/hold'] = 'scheduled';
237 + if (!$this->setting('update/hold') != 'ping') :
238 + $this->update_setting('update/hold', 'scheduled');
200 239 endif;
201 240
202 - $this->settings['update/unfinished'] = 'yes';
241 + $this->update_setting('update/unfinished', 'yes');
203 242
204 - $update[] = "link_notes = '".$wpdb->escape($this->settings_to_notes())."'";
243 + $update[] = "link_notes = '".esc_sql($this->settings_to_notes())."'";
205 244
206 245 $update_set = implode(',', $update);
207 -
246 +
208 247 // Update the properties of the link from the feed information
209 248 $result = $wpdb->query("
210 249 UPDATE $wpdb->links
211 250 SET $update_set
@@ -210,15 +249,36 @@
210 249 UPDATE $wpdb->links
211 250 SET $update_set
212 251 WHERE link_id='$this->id'
213 252 ");
253 + do_action('update_syndicated_feed', $this->id, $this);
214 254
215 255 # -- Add new posts from feed and update any updated posts
216 256 $crashed = false;
217 257
218 - if (is_array($this->magpie->items)) :
219 - foreach ($this->magpie->items as $item) :
220 - $post =& new SyndicatedPost($item, $this);
258 + $posts = $this->live_posts();
259 +
260 + $this->magpie->originals = $posts;
261 +
262 + // If this is a complete feed, rather than an incremental feed, we
263 + // need to prepare to mark everything for presumptive retirement.
264 + if ($this->is_non_incremental()) :
265 + $q = new WP_Query(array(
266 + 'fields' => '_synfrom',
267 + 'post_status__not' => 'fwpretired',
268 + 'ignore_sticky_posts' => true,
269 + 'meta_key' => 'syndication_feed_id',
270 + 'meta_value' => $this->id,
271 + ));
272 + foreach ($q->posts as $p) :
273 + update_post_meta($p->ID, '_feedwordpress_retire_me_'.$this->id, '1');
274 + endforeach;
275 + endif;
276 +
277 + if (is_array($posts)) :
278 + foreach ($posts as $key => $item) :
279 + $post = new SyndicatedPost($item, $this);
280 +
221 281 if (!$resume or !in_array(trim($post->guid()), $processed)) :
222 282 $processed[] = $post->guid();
223 283 if (!$post->filtered()) :
224 284 $new = $post->store();
@@ -229,21 +289,60 @@
229 289 $crashed = true;
230 290 break;
231 291 endif;
232 292 endif;
293 +
294 + unset($post);
295 +
233 296 endforeach;
234 297 endif;
235 -
236 - // Copy back any changes to feed settings made in the course of updating (e.g. new author rules)
237 - $to_notes = $this->settings;
238 298
239 - $this->settings['update/processed'] = $processed;
299 + if ('yes'==$this->setting('tombstones', 'tombstones', 'yes')) :
300 + // Check for use of Atom tombstones. Spec:
301 + // <http://tools.ietf.org/html/draft-snell-atompub-tombstones-18>
302 + $tombstones = $this->simplepie->get_feed_tags('http://purl.org/atompub/tombstones/1.0', 'deleted-entry');
303 + if (count($tombstones) > 0) :
304 + foreach ($tombstones as $tombstone) :
305 + $ref = NULL;
306 + foreach (array('', 'http://purl.org/atompub/tombstones/1.0') as $ns) :
307 + if (isset($tombstone['attribs'][$ns])
308 + and isset($tombstone['attribs'][$ns]['ref'])) :
309 + $ref = $tombstone['attribs'][$ns]['ref'];
310 + endif;
311 + endforeach;
312 +
313 + $q = new WP_Query(array(
314 + 'ignore_sticky_posts' => true,
315 + 'guid' => $ref,
316 + 'meta_key' => 'syndication_feed_id',
317 + 'meta_value' => $this->id, // Only allow a feed to tombstone its own entries.
318 + ));
319 +
320 + foreach ($q->posts as $p) :
321 + $old_status = $p->post_status;
322 + FeedWordPress::diagnostic('syndicated_posts', 'Retiring existing post # '.$p->ID.' "'.$p->post_title.'" due to Atom tombstone element in feed.');
323 + set_post_field('post_status', 'fwpretired', $p->ID);
324 + wp_transition_post_status('fwpretired', $old_status, $p);
325 + endforeach;
326 +
327 + endforeach;
328 + endif;
329 + endif;
330 +
331 + $suffix = ($crashed ? 'crashed' : 'completed');
332 + do_action('update_syndicated_feed_items', $this->id, $this);
333 + do_action("update_syndicated_feed_items_${suffix}", $this->id, $this);
334 +
335 + $this->update_setting('update/processed', $processed);
240 336 if (!$crashed) :
241 - $this->settings['update/unfinished'] = 'no';
337 + $this->update_setting('update/unfinished', 'no');
242 338 endif;
339 + $this->update_setting('link/item count', count($posts));
243 340
244 - $update_set = "link_notes = '".$wpdb->escape($this->settings_to_notes())."'";
245 -
341 + // Copy back any changes to feed settings made in the
342 + // course of updating (e.g. new author rules)
343 + $update_set = "link_notes = '".esc_sql($this->settings_to_notes())."'";
344 +
246 345 // Update the properties of the link from the feed information
247 346 $result = $wpdb->query("
248 347 UPDATE $wpdb->links
249 348 SET $update_set
@@ -248,34 +347,133 @@
248 347 UPDATE $wpdb->links
249 348 SET $update_set
250 349 WHERE link_id='$this->id'
251 350 ");
351 +
352 + do_action("update_syndicated_feed_completed", $this->id, $this);
252 353 endif;
253 -
354 +
355 + // All done; let's clean up.
356 + $this->magpie = NULL;
357 +
358 + // Avoid circular-reference memory leak in PHP < 5.3.
359 + // Cf. <http://simplepie.org/wiki/faq/i_m_getting_memory_leaks>
360 + if (method_exists($this->simplepie, '__destruct')) :
361 + $this->simplepie->__destruct();
362 + endif;
363 + $this->simplepie = NULL;
364 +
254 365 return $new_count;
255 366 } /* SyndicatedLink::poll() */
256 367
368 + function process_retirements ($delta) {
369 + global $post;
370 +
371 + $q = new WP_Query(array(
372 + 'fields' => '_synfrom',
373 + 'post_status__not' => 'fwpretired',
374 + 'ignore_sticky_posts' => true,
375 + 'meta_key' => '_feedwordpress_retire_me_'.$this->id,
376 + 'meta_value' => '1',
377 + ));
378 + if ($q->have_posts()) :
379 + foreach ($q->posts as $p) :
380 + $old_status = $p->post_status;
381 + FeedWordPress::diagnostic('syndicated_posts', 'Retiring existing post # '.$p->ID.' "'.$p->post_title.'" due to absence from a non-incremental feed.');
382 + set_post_field('post_status', 'fwpretired', $p->ID);
383 + wp_transition_post_status('fwpretired', $old_status, $p);
384 + delete_post_meta($p->ID, '_feedwordpress_retire_me_'.$this->id);
385 + endforeach;
386 + endif;
387 +
388 + return $delta;
389 + }
390 +
391 + /**
392 + * Updates the URL for the feed syndicated by this link.
393 + *
394 + * @param string $url The new feed URL to use for this source.
395 + * @return bool TRUE on success, FALSE on failure.
396 + */
397 + function set_uri ($url) {
398 + global $wpdb;
399 +
400 + if ($this->found()) :
401 + // Update link_rss
402 + $result = $wpdb->query("
403 + UPDATE $wpdb->links
404 + SET
405 + link_rss = '".esc_sql($url)."'
406 + WHERE link_id = '".esc_sql($this->id)."'
407 + ");
408 +
409 + $ret = ($result ? true : false);
410 + else :
411 + $ret = false;
412 + endif;
413 + return $ret;
414 + } /* SyndicatedLink::set_uri () */
415 +
416 + function deactivate () {
417 + global $wpdb;
418 +
419 + $wpdb->query($wpdb->prepare("
420 + UPDATE $wpdb->links SET link_visible = 'N' WHERE link_id = %d
421 + ", (int) $this->id));
422 + } /* SyndicatedLink::deactivate () */
423 +
424 + function delete () {
425 + global $wpdb;
426 +
427 + $wpdb->query($wpdb->prepare("
428 + DELETE FROM $wpdb->postmeta WHERE meta_key='syndication_feed_id'
429 + AND meta_value = '%s'
430 + ", $this->id));
431 +
432 + $wpdb->query($wpdb->prepare("
433 + DELETE FROM $wpdb->links WHERE link_id = %d
434 + ", (int) $this->id));
435 +
436 + $this->id = NULL;
437 + } /* SyndicatedLink::delete () */
438 +
439 + function nuke () {
440 + global $wpdb;
441 +
442 + // Make a list of the items syndicated from this feed...
443 + $post_ids = $wpdb->get_col($wpdb->prepare("
444 + SELECT post_id FROM $wpdb->postmeta
445 + WHERE meta_key = 'syndication_feed_id'
446 + AND meta_value = '%s'
447 + ", $this->id));
448 +
449 + // ... and kill them all
450 + if (count($post_ids) > 0) :
451 + foreach ($post_ids as $post_id) :
452 + // Force scrubbing of deleted post
453 + // rather than sending to Trashcan
454 + wp_delete_post(
455 + /*postid=*/ $post_id,
456 + /*force_delete=*/ true
457 + );
458 + endforeach;
459 + endif;
460 +
461 + $this->delete();
462 + } /* SyndicatedLink::nuke () */
463 +
257 464 function map_name_to_new_user ($name, $newuser_name) {
258 465 global $wpdb;
259 466
260 467 if (strlen($newuser_name) > 0) :
261 - $userdata = array();
262 - $userdata['ID'] = NULL;
263 -
264 - $userdata['user_login'] = sanitize_user($newuser_name);
265 - $userdata['user_login'] = apply_filters('pre_user_login', $userdata['user_login']);
266 -
267 - $userdata['user_nicename'] = sanitize_title($newuser_name);
268 - $userdata['user_nicename'] = apply_filters('pre_user_nicename', $userdata['user_nicename']);
269 -
270 - $userdata['display_name'] = $wpdb->escape($newuser_name);
271 -
272 - $newuser_id = wp_insert_user($userdata);
468 + $newuser_id = fwp_insert_new_user($newuser_name);
273 469 if (is_numeric($newuser_id)) :
274 470 if (is_null($name)) : // Unfamiliar author
275 - $this->settings['unfamiliar author'] = $newuser_id;
471 + $this->update_setting('unfamiliar author', $newuser_id);
276 472 else :
277 - $this->settings['map authors']['name'][$name] = $newuser_id;
473 + $map = $this->setting('map authors');
474 + $map['name'][$name] = $newuser_id;
475 + $this->update_setting('map authors', $map);
278 476 endif;
279 477 else :
280 478 // TODO: Add some error detection and reporting
281 479 endif;
@@ -283,8 +481,113 @@
283 481 // TODO: Add some error reporting
284 482 endif;
285 483 } /* SyndicatedLink::map_name_to_new_user () */
286 484
485 + function imploded_settings () {
486 + return array('cats', 'tags', 'match/cats', 'match/tags', 'match/filter');
487 + }
488 +
489 + function get_settings_from_notes () {
490 + // Read off feed settings from link_notes
491 + $notes = explode("\n", $this->link->link_notes);
492 + foreach ($notes as $note):
493 + $pair = explode(": ", $note, 2);
494 + $key = (isset($pair[0]) ? $pair[0] : null);
495 + $value = (isset($pair[1]) ? $pair[1] : null);
496 + if (!is_null($key) and !is_null($value)) :
497 + // Unescape and trim() off the whitespace.
498 + // Thanks to Ray Lischner for pointing out the
499 + // need to trim off whitespace.
500 + $this->settings[$key] = stripcslashes (trim($value));
501 + endif;
502 + endforeach;
503 +
504 + // "Magic" feed settings
505 + $this->settings['link/uri'] = $this->link->link_rss;
506 + $this->settings['link/name'] = $this->link->link_name;
507 + $this->settings['link/id'] = $this->link->link_id;
508 +
509 + // `hardcode categories` and `unfamiliar categories` are
510 + // deprecated in favor of `unfamiliar category`
511 + if (
512 + isset($this->settings['unfamiliar categories'])
513 + and !isset($this->settings['unfamiliar category'])
514 + ) :
515 + $this->settings['unfamiliar category'] = $this->settings['unfamiliar categories'];
516 + endif;
517 + if (
518 + FeedWordPress::affirmative($this->settings, 'hardcode categories')
519 + and !isset($this->settings['unfamiliar category'])
520 + ) :
521 + $this->settings['unfamiliar category'] = 'default';
522 + endif;
523 +
524 + // Set this up automagically for del.icio.us
525 + $bits = parse_url($this->link->link_rss);
526 + $tagspacers = array('del.icio.us', 'feeds.delicious.com');
527 + if (!isset($this->settings['cat_split']) and in_array($bits['host'], $tagspacers)) :
528 + $this->settings['cat_split'] = '\s'; // Whitespace separates multiple tags in del.icio.us RSS feeds
529 + endif;
530 +
531 + // Simple lists
532 + foreach ($this->imploded_settings() as $what) :
533 + if (isset($this->settings[$what])):
534 + $this->settings[$what] = explode(
535 + FEEDWORDPRESS_CAT_SEPARATOR,
536 + $this->settings[$what]
537 + );
538 + endif;
539 + endforeach;
540 +
541 + if (isset($this->settings['terms'])) :
542 + // Look for new format
543 + $this->settings['terms'] = maybe_unserialize($this->settings['terms']);
544 +
545 + if (!is_array($this->settings['terms'])) :
546 + // Deal with old format instead. Ugh.
547 +
548 + // Split on two *or more* consecutive breaks
549 + // because in the old format, a taxonomy
550 + // without any associated terms would
551 + // produce tax_name#1\n\n\ntax_name#2\nterm,
552 + // and the naive split on the first \n\n
553 + // would screw up the tax_name#2 list.
554 + //
555 + // Props to David Morris for pointing this
556 + // out.
557 +
558 + $this->settings['terms'] = preg_split(
559 + "/".FEEDWORDPRESS_CAT_SEPARATOR."{2,}/",
560 + $this->settings['terms']
561 + );
562 + $terms = array();
563 + foreach ($this->settings['terms'] as $line) :
564 + $line = explode(FEEDWORDPRESS_CAT_SEPARATOR, $line);
565 + $tax = array_shift($line);
566 + $terms[$tax] = $line;
567 + endforeach;
568 + $this->settings['terms'] = $terms;
569 + endif;
570 + endif;
571 +
572 + if (isset($this->settings['map authors'])) :
573 + $author_rules = explode("\n\n", $this->settings['map authors']);
574 + $ma = array();
575 + foreach ($author_rules as $rule) :
576 + list($rule_type, $author_name, $author_action) = explode("\n", $rule);
577 +
578 + // Normalize for case and whitespace
579 + $rule_type = strtolower(trim($rule_type));
580 + $author_name = strtolower(trim($author_name));
581 + $author_action = strtolower(trim($author_action));
582 +
583 + $ma[$rule_type][$author_name] = $author_action;
584 + endforeach;
585 + $this->settings['map authors'] = $ma;
586 + endif;
587 +
588 + } /* SyndicatedLink::get_settings_from_notes () */
589 +
287 590 function settings_to_notes () {
288 591 $to_notes = $this->settings;
289 592
290 593 unset($to_notes['link/id']); // Magic setting; don't save
@@ -297,14 +600,21 @@
297 600 if (isset($to_notes['update/processed']) and (is_array($to_notes['update/processed']))) :
298 601 $to_notes['update/processed'] = implode("\n", $to_notes['update/processed']);
299 602 endif;
300 603
301 - if (is_array($to_notes['cats'])) :
302 - $to_notes['cats'] = implode(FEEDWORDPRESS_CAT_SEPARATOR, $to_notes['cats']);
604 + foreach ($this->imploded_settings() as $what) :
605 + if (isset($to_notes[$what]) and is_array($to_notes[$what])) :
606 + $to_notes[$what] = implode(
607 + FEEDWORDPRESS_CAT_SEPARATOR,
608 + $to_notes[$what]
609 + );
610 + endif;
611 + endforeach;
612 +
613 + if (isset($to_notes['terms']) and is_array($to_notes['terms'])) :
614 + // Serialize it.
615 + $to_notes['terms'] = serialize($to_notes['terms']);
303 616 endif;
304 - if (is_array($to_notes['tags'])) :
305 - $to_notes['tags'] = implode(FEEDWORDPRESS_CAT_SEPARATOR, $to_notes['tags']);
306 - endif;
307 617
308 618 // Collapse the author mapping rule structure back into a flat string
309 619 if (isset($to_notes['map authors'])) :
310 620 $ma = array();
@@ -322,17 +632,305 @@
322 632 endforeach;
323 633 return $notes;
324 634 } /* SyndicatedLink::settings_to_notes () */
325 635
326 - function uri () {
327 - return (is_object($this->link) ? $this->link->link_rss : NULL);
636 + function save_settings ($reload = false) {
637 + global $wpdb;
638 +
639 + // Save channel-level meta-data
640 + foreach (array('link_name', 'link_description', 'link_url') as $what) :
641 + $alter[] = "{$what} = '".esc_sql($this->link->{$what})."'";
642 + endforeach;
643 +
644 + // Save settings to the notes field
645 + $alter[] = "link_notes = '".esc_sql($this->settings_to_notes())."'";
646 +
647 + // Update the properties of the link from settings changes, etc.
648 + $update_set = implode(", ", $alter);
649 +
650 + $result = $wpdb->query("
651 + UPDATE $wpdb->links
652 + SET $update_set
653 + WHERE link_id='$this->id'
654 + ");
655 +
656 + if ($reload) :
657 + // force reload of link information from DB
658 + if (function_exists('clean_bookmark_cache')) :
659 + clean_bookmark_cache($this->id);
660 + endif;
661 + endif;
662 + } /* SyndicatedLink::save_settings () */
663 +
664 + /**
665 + * Retrieves the value of a setting, allowing for a global setting to be
666 + * used as a fallback, or a constant value, or both.
667 + *
668 + * @param string $name The link setting key
669 + * @param mixed $fallback_global If the link setting is nonexistent or marked as a use-default value, fall back to the value of this global setting.
670 + * @param mixed $fallback_value If the link setting and the global setting are nonexistent or marked as a use-default value, fall back to this constant value.
671 + * @return bool TRUE on success, FALSE on failure.
672 + */
673 + function setting ($name, $fallback_global = NULL, $fallback_value = NULL, $default = 'default') {
674 + $ret = NULL;
675 + if (isset($this->settings[$name])) :
676 + $ret = $this->settings[$name];
677 + endif;
678 +
679 + $no_value = (
680 + is_null($ret)
681 + or (is_string($ret) and strtolower($ret)==$default)
682 + );
683 +
684 + if ($no_value and !is_null($fallback_global)) :
685 + // Avoid duplication of this correction
686 + $fallback_global = preg_replace('/^feedwordpress_/', '', $fallback_global);
687 +
688 + // Occasionally we'll get an array back. Convert it to a string
689 + if ( is_array($fallback_global) && sizeof($fallback_global) )
690 + $fallback_global = reset($fallback_global);
691 +
692 + if ( !empty($fallback_global) )
693 + $ret = get_option('feedwordpress_'.$fallback_global, /*default=*/ NULL);
694 + endif;
695 +
696 + $no_value = (
697 + is_null($ret)
698 + or (is_string($ret) and strtolower($ret)==$default)
699 + );
700 +
701 + if ($no_value and !is_null($fallback_value)) :
702 + $ret = $fallback_value;
703 + endif;
704 + return $ret;
705 + } /* SyndicatedLink::setting () */
706 +
707 + function merge_settings ($data, $prefix, $separator = '/') {
708 + $dd = $this->flatten_array($data, $prefix, $separator);
709 + $this->settings = array_merge($this->settings, $dd);
710 + } /* SyndicatedLink::merge_settings () */
711 +
712 + function update_setting ($name, $value, $default = 'default') {
713 + if (!is_null($value) and $value != $default) :
714 + $this->settings[$name] = $value;
715 + else : // Zap it.
716 + unset($this->settings[$name]);
717 + endif;
718 + } /* SyndicatedLink::update_setting () */
719 +
720 + function is_non_incremental () {
721 + return ('complete'==$this->setting('update_incremental', 'update_incremental', 'incremental'));
722 + } /* SyndicatedLink::is_non_incremental () */
723 +
724 + function uri ($params = array()) {
725 + $params = wp_parse_args($params, array(
726 + 'add_params' => false,
727 + 'fetch' => false,
728 + ));
729 +
730 + // Initialize $qp (= array for added query parameters, if any)
731 + $qp = array();
732 +
733 + $link_rss = (is_object($this->link) ? $this->link->link_rss : NULL);
734 +
735 + // $link_rss stores the URI for the subscription as stored in the feed's record.
736 + // $uri stores the effective URI of the request including any/all added query parameters
737 + $uri = $link_rss;
738 + if (!is_null($uri) and strlen($uri) > 0 and $params['add_params']) :
739 + $qp = maybe_unserialize($this->setting('query parameters', array()));
740 +
741 + // For high-tech HTTP feed request kung fu
742 + $qp = apply_filters('syndicated_feed_parameters', $qp, $uri, $this);
743 +
744 + // $qp is an array of key-value pairs stored as arrays of format [$key, $value]
745 + $q = array();
746 + if (is_array($qp) and count($qp) > 0) :
747 + foreach ($qp as $pair) :
748 + $q[] = urlencode($pair[0]).'='.urlencode($pair[1]);
749 + endforeach;
750 +
751 + // Are we appending to a URI that already has params?
752 + $sep = ((strpos($uri, "?")===false) ? '?' : '&');
753 +
754 + // Tack it on
755 + $uri .= $sep . implode("&", $q);
756 + endif;
757 + endif;
758 +
759 + // Do we have any filters that apply here?
760 + $uri = apply_filters('syndicated_link_uri', $uri, $link_rss, $qp, $params, $this);
761 +
762 + // Return the filtered link URI.
763 + return $uri;
328 764 } /* SyndicatedLink::uri () */
329 765
330 - function homepage () {
331 - return (isset($this->settings['feed/link']) ? $this->settings['feed/link'] : NULL);
766 + function username () {
767 + return $this->setting('http username', 'http_username', NULL);
768 + } /* SyndicatedLink::username () */
769 +
770 + function password () {
771 + return $this->setting('http password', 'http_password', NULL);
772 + } /* SyndicatedLink::password () */
773 +
774 + function authentication_method () {
775 + $auth = $this->setting('http auth method', NULL);
776 + if (('-' == $auth) or (strlen($auth)==0)) :
777 + $auth = NULL;
778 + endif;
779 + return $auth;
780 + } /* SyndicatedLink::authentication_method () */
781 +
782 + var $postmeta = array();
783 + function postmeta ($params = array()) {
784 + $params = wp_parse_args($params, /*defaults=*/ array(
785 + "field" => NULL,
786 + "parsed" => false,
787 + "force" => false,
788 + ));
789 +
790 + if ($params['force'] or !isset($this->postmeta[/*parsed = */ false])) :
791 + // First, get the global settings.
792 + $default_custom_settings = get_option('feedwordpress_custom_settings');
793 + if ($default_custom_settings and !is_array($default_custom_settings)) :
794 + $default_custom_settings = unserialize($default_custom_settings);
795 + endif;
796 + if (!is_array($default_custom_settings)) :
797 + $default_custom_settings = array();
798 + endif;
799 +
800 + // Next, get the settings for this particular feed.
801 + $custom_settings = $this->setting('postmeta', NULL, NULL);
802 + if ($custom_settings and !is_array($custom_settings)) :
803 + $custom_settings = unserialize($custom_settings);
804 + endif;
805 + if (!is_array($custom_settings)) :
806 + $custom_settings = array();
807 + endif;
808 +
809 + $this->postmeta[/*parsed=*/ false] = array_merge($default_custom_settings, $custom_settings);
810 + $this->postmeta[/*parsed=*/ true] = array();
811 +
812 + // Now, run through and parse them all.
813 + foreach ($this->postmeta[/*parsed=*/ false] as $key => $meta) :
814 + $meta = apply_filters("syndicated_link_post_meta_${key}_pre", $meta, $this);
815 + $this->postmeta[/*parsed=*/ false][$key] = $meta;
816 + $this->postmeta[/*parsed=*/ true][$key] = new FeedWordPressParsedPostMeta($meta);
817 + endforeach;
818 + endif;
819 +
820 + $ret = $this->postmeta[!!$params['parsed']];
821 + if (is_string($params['field'])) :
822 + $ret = $ret[$params['field']];
823 + endif;
824 + return $ret;
825 + } /* SyndicatedLink::postmeta () */
826 +
827 + function property_cascade ($fromFeed, $link_field, $setting, $method) {
828 + $value = NULL;
829 + if ($fromFeed) :
830 + $value = $this->setting($setting, NULL, NULL, NULL);
831 +
832 + $s = $this->simplepie;
833 + $callable = (is_object($s) and method_exists($s, $method));
834 + if (is_null($value) and $callable) :
835 + $fallback = $s->{$method}();
836 + endif;
837 + else :
838 + $value = $this->link->{$link_field};
839 + endif;
840 + return $value;
841 + } /* SyndicatedLink::property_cascade () */
842 +
843 + function homepage ($fromFeed = true) {
844 + return $this->property_cascade($fromFeed, 'link_url', 'feed/link', 'get_link');
332 845 } /* SyndicatedLink::homepage () */
333 846
334 - function ttl () {
847 + function name ($fromFeed = true) {
848 + return $this->property_cascade($fromFeed, 'link_name', 'feed/title', 'get_title');
849 + } /* SyndicatedLink::name () */
850 +
851 + function guid () {
852 + $ret = $this->setting('feed/id', NULL, $this->uri());
853 +
854 + // If we can get it live from the feed, do so.
855 + if (is_object($this->simplepie)) :
856 + $search = array(
857 + array('', 'id'),
858 + array(SIMPLEPIE_NAMESPACE_ATOM_10, 'id'),
859 + array(SIMPLEPIE_NAMESPACE_ATOM_03, 'id'),
860 + array(SIMPLEPIE_NAMESPACE_RSS_20, 'guid'),
861 + array(SIMPLEPIE_NAMESPACE_DC_11, 'identifier'),
862 + array(SIMPLEPIE_NAMESPACE_DC_10, 'identifier'),
863 + );
864 +
865 + foreach ($search as $pair) :
866 + if ($id_tags = $this->simplepie->get_feed_tags($pair[0], $pair[1])) :
867 + $ret = $id_tags[0]['data'];
868 + break;
869 + elseif ($id_tags = $this->simplepie->get_channel_tags($pair[0], $pair[1])) :
870 + $ret = $id_tags[0]['data'];
871 + break;
872 + endif;
873 + endforeach;
874 + endif;
875 + return $ret;
876 + }
877 +
878 + function links ($params = array()) {
879 + $params = wp_parse_args($params, array(
880 + "rel" => NULL,
881 + ));
882 +
883 + $fLinks = array();
884 + $search = array(
885 + array('', 'link'),
886 + array(SIMPLEPIE_NAMESPACE_ATOM_10, 'link'),
887 + array(SIMPLEPIE_NAMESPACE_ATOM_03, 'link'),
888 + );
889 +
890 + foreach ($search as $pair) :
891 + if ($link_tags = $this->simplepie->get_feed_tags($pair[0], $pair[1])) :
892 + $fLinks = array_merge($fLinks, $link_tags);
893 + endif;
894 + if ($link_tags = $this->simplepie->get_channel_tags($pair[0], $pair[1])) :
895 + $fLinks = array_merge($fLinks, $link_tags);
896 + endif;
897 + endforeach;
898 +
899 + $ret = array();
900 + foreach ($fLinks as $link) :
901 + $filter = false;
902 + if (!is_null($params['rel'])) :
903 + $filter = true;
904 +
905 + if (isset($link['attribs'])) :
906 + // Get a list of NSes from the search
907 + foreach ($search as $pair) :
908 + $ns = $pair[0];
909 +
910 + if (isset($link['attribs'][$ns])
911 + and isset($link['attribs'][$ns]['rel'])
912 + ) :
913 + $rel = strtolower(trim($link['attribs'][$ns]['rel']));
914 + $fRel = strtolower(trim($params['rel']));
915 +
916 + if ($rel == $fRel) :
917 + $filter = false;
918 + endif;
919 + endif;
920 + endforeach;
921 + endif;
922 + endif;
923 +
924 + if (!$filter) :
925 + $ret[] = $link;
926 + endif;
927 + endforeach;
928 +
929 + return $ret;
930 + }
931 +
932 + function ttl ($return_element = false) {
335 933 if (is_object($this->magpie)) :
336 934 $channel = $this->magpie->channel;
337 935 else :
338 936 $channel = array();
@@ -341,9 +939,10 @@
341 939 if (isset($channel['ttl'])) :
342 940 // "ttl stands for time to live. It's a number of
343 941 // minutes that indicates how long a channel can be
344 942 // cached before refreshing from the source."
345 - // <http://blogs.law.harvard.edu/tech/rss#ltttlgtSubelementOfLtchannelgt>
943 + // <http://blogs.law.harvard.edu/tech/rss#ltttlgtSubelementOfLtchannelgt>
944 + $xml = 'rss:ttl';
346 945 $ret = $channel['ttl'];
347 946 elseif (isset($channel['sy']['updatefrequency']) or isset($channel['sy']['updateperiod'])) :
348 947 $period_minutes = array (
349 948 'hourly' => 60, /* minutes in an hour */
@@ -359,10 +958,10 @@
359 958 // daily is assumed." <http://web.resource.org/rss/1.0/modules/syndication/>
360 959 if (isset($channel['sy']['updateperiod'])) : $period = $channel['sy']['updateperiod'];
361 960 else : $period = 'daily';
362 961 endif;
363 -
364 - // "sy:updateFrequency: Used to describe the frequency
962 +
963 + // "sy:updateFrequency: Used to describe the frequency
365 964 // of updates in relation to the update period. A
366 965 // positive integer indicates how many times in that
367 966 // period the channel is updated. ... If omitted a value
368 967 // of 1 is assumed." <http://web.resource.org/rss/1.0/modules/syndication/>
@@ -368,16 +967,39 @@
368 967 // of 1 is assumed." <http://web.resource.org/rss/1.0/modules/syndication/>
369 968 if (isset($channel['sy']['updatefrequency'])) : $freq = (int) $channel['sy']['updatefrequency'];
370 969 else : $freq = 1;
371 970 endif;
372 -
971 +
972 + $xml = 'sy:updateFrequency';
373 973 $ret = (int) ($period_minutes[$period] / $freq);
374 974 else :
975 + $xml = NULL;
375 976 $ret = NULL;
376 977 endif;
377 - return $ret;
978 +
979 + if ('yes'==$this->setting('update/minimum', 'update_minimum', 'no')) :
980 + $min = (int) $this->setting('update/window', 'update_window', DEFAULT_UPDATE_PERIOD);
981 +
982 + if ($min > $ret) :
983 + $ret = NULL;
984 + endif;
985 + endif;
986 + return ($return_element ? array($ret, $xml) : $ret);
378 987 } /* SyndicatedLink::ttl() */
379 988
989 + function automatic_ttl () {
990 + // spread out over a time interval for staggered updates
991 + $updateWindow = $this->setting('update/window', 'update_window', DEFAULT_UPDATE_PERIOD);
992 + if (!is_numeric($updateWindow) or ($updateWindow < 1)) :
993 + $updateWindow = DEFAULT_UPDATE_PERIOD;
994 + endif;
995 +
996 + // We get a fudge of 1/3 of window from elsewhere. We'll do some more
997 + // fudging here.
998 + $fudgedInterval = $updateWindow+rand(-($updateWindow/6), 5*($updateWindow/12));
999 + return apply_filters('syndicated_feed_automatic_ttl', $fudgedInterval, $this);
1000 + } /* SyndicatedLink::automatic_ttl () */
1001 +
380 1002 // SyndicatedLink::flatten_array (): flatten an array. Useful for
381 1003 // hierarchical and namespaced elements.
382 1004 //
383 1005 // Given an array which may contain array or object elements in it,
@@ -400,32 +1022,137 @@
400 1022 return $ret;
401 1023 } /* SyndicatedLink::flatten_array () */
402 1024
403 1025 function hardcode ($what) {
404 - $default = get_option("feedwordpress_hardcode_$what");
405 - if ( $default === 'yes' ) :
406 - // If the default is to hardcode, then we want the
407 - // negation of negative(): TRUE by default and FALSE if
408 - // the setting is explicitly "no"
409 - $ret = !FeedWordPress::negative($this->settings, "hardcode $what");
1026 +
1027 + $ret = $this->setting('hardcode '.$what, 'hardcode_'.$what, NULL);
1028 +
1029 + if ('yes' == $ret) :
1030 + $ret = true;
410 1031 else :
411 - // If the default is NOT to hardcode, then we want
412 - // affirmative(): FALSE by default and TRUE if the
413 - // setting is explicitly "yes"
414 - $ret = FeedWordPress::affirmative($this->settings, "hardcode $what");
1032 + $ret = false;
415 1033 endif;
416 1034 return $ret;
417 1035 } /* SyndicatedLink::hardcode () */
418 1036
419 - function syndicated_status ($what, $default) {
1037 + function syndicated_status ($what, $default, $fallback = true) {
420 1038 global $wpdb;
421 1039
422 - $ret = get_option("feedwordpress_syndicated_{$what}_status");
423 - if ( isset($this->settings["$what status"]) ) :
424 - $ret = $this->settings["$what status"];
425 - elseif (!$ret) :
426 - $ret = $default;
1040 + $g_set = ($fallback ? 'syndicated_' . $what . '_status' : NULL);
1041 + $ret = $this->setting($what.' status', $g_set, $default);
1042 +
1043 + return esc_sql(trim(strtolower($ret)));
1044 + } /* SyndicatedLink:syndicated_status () */
1045 +
1046 + function taxonomies () {
1047 + $post_type = $this->setting('syndicated post type', 'syndicated_post_type', 'post');
1048 + return get_object_taxonomies(array('object_type' => $post_type), 'names');
1049 + } /* SyndicatedLink::taxonomies () */
1050 +
1051 + /**
1052 + * category_ids: look up (and create) category ids from a list of
1053 + * categories
1054 + *
1055 + * @param array $cats
1056 + * @param string $unfamiliar_category
1057 + * @param array|null $taxonomies
1058 + * @return array
1059 + */
1060 + function category_ids ($post, $cats, $unfamiliar_category = 'create', $taxonomies = NULL, $params = array()) {
1061 + $singleton = (isset($params['singleton']) ? $params['singleton'] : true);
1062 + $allowFilters = (isset($params['filters']) ? $params['filters'] : false);
1063 +
1064 + $catTax = 'category';
1065 +
1066 + if (is_null($taxonomies)) :
1067 + $taxonomies = array('category');
427 1068 endif;
428 - return $wpdb->escape(trim(strtolower($ret)));
429 - } /* SyndicatedLink:syndicated_status () */
1069 +
1070 + // We need to normalize whitespace because (1) trailing
1071 + // whitespace can cause PHP and MySQL not to see eye to eye on
1072 + // VARCHAR comparisons for some versions of MySQL (cf.
1073 + // <http://dev.mysql.com/doc/mysql/en/char.html>), and (2)
1074 + // because I doubt most people want to make a semantic
1075 + // distinction between 'Computers' and 'Computers '
1076 + $cats = array_map('trim', $cats);
1077 +
1078 + $terms = array();
1079 + foreach ($taxonomies as $tax) :
1080 + $terms[$tax] = array();
1081 + endforeach;
1082 +
1083 + foreach ($cats as $cat_name) :
1084 + if (strlen(trim($cat_name)) < 1) :
1085 + continue;
1086 + endif;
1087 +
1088 + $oTerm = new SyndicatedPostTerm($cat_name, $taxonomies, $post);
1089 +
1090 + if ($oTerm->is_familiar()) :
1091 +
1092 + $tax = $oTerm->taxonomy();
1093 + if (!isset($terms[$tax])) :
1094 + $terms[$tax] = array();
1095 + endif;
1096 + $terms[$tax][] = $oTerm->id();
1097 +
1098 + else :
1099 +
1100 + if ('tag'==$unfamiliar_category) :
1101 + $unfamiliar_category = 'create:post_tag';
1102 + endif;
1103 +
1104 + if (preg_match('/^create(:(.*))?$/i', $unfamiliar_category, $ref)) :
1105 + $tax = $catTax; // Default
1106 +
1107 + if (isset($ref[2])
1108 + and strlen($ref[2]) > 2) :
1109 + $tax = $ref[2];
1110 + endif;
1111 +
1112 + $inserted = $oTerm->insert($tax);
1113 + if (!is_null($inserted)) :
1114 + if (!isset($terms[$tax])) :
1115 + $terms[$tax] = array();
1116 + endif;
1117 + $terms[$tax][] = $inserted;
1118 + else :
1119 +
1120 + endif; // !is_null($inserted)
1121 + endif; // preg_match(...)
1122 +
1123 + endif; /* ($oTerm->is_familiar()) */
1124 + endforeach;
1125 +
1126 + $filtersOn = $allowFilters;
1127 + if ($allowFilters) :
1128 + $filters = array_filter(
1129 + $this->setting('match/filter', 'match_filter', array()),
1130 + 'remove_dummy_zero'
1131 + );
1132 + $filtersOn = ($filtersOn and is_array($filters) and (count($filters) > 0));
1133 + endif;
1134 +
1135 + // Check for filter conditions
1136 + foreach ($terms as $tax => $term_ids) :
1137 + if ($filtersOn
1138 + and (count($term_ids)==0)
1139 + and in_array($tax, $filters)) :
1140 + $terms = NULL; // Drop the post
1141 + break;
1142 + else :
1143 + $terms[$tax] = array_unique($term_ids);
1144 + endif;
1145 + endforeach;
1146 +
1147 + if ($singleton and count($terms)==1) : // If we only searched one, just return the term IDs
1148 + $terms = end($terms);
1149 + endif;
1150 +
1151 + FeedWordPress::diagnostic(
1152 + 'syndicated_posts:categories',
1153 + 'Category: MAPPED term names '.json_encode($cats).' to IDs: '.json_encode($terms)
1154 + );
1155 + return $terms;
1156 + } /* SyndicatedLink::category_ids () */
430 1157 } // class SyndicatedLink
431 1158