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