PluginProbe
FeedWordPress / 2017.1004
FeedWordPress v2017.1004
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 -177 2009.06182017.1004 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,120 +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 - $pair = explode(": ", $note, 2);
65 - $key = (isset($pair[0]) ? $pair[0] : null);
66 - $value = (isset($pair[1]) ? $pair[1] : null);
67 - if (!is_null($key) and !is_null($value)) :
68 - // Unescape and trim() off the whitespace.
69 - // Thanks to Ray Lischner for pointing out the
70 - // need to trim off whitespace.
71 - $this->settings[$key] = stripcslashes (trim($value));
72 - endif;
73 - endforeach;
63 + add_filter('feedwordpress_update_complete', array($this, 'process_retirements'), 1000, 1);
64 + } /* SyndicatedLink::__construct () */
74 65
75 - // "Magic" feed settings
76 - $this->settings['link/uri'] = $this->link->link_rss;
77 - $this->settings['link/name'] = $this->link->link_name;
78 - $this->settings['link/id'] = $this->link->link_id;
79 -
80 - // `hardcode categories` and `unfamiliar categories` are deprecated in favor of `unfamiliar category`
81 - if (
82 - isset($this->settings['unfamiliar categories'])
83 - and !isset($this->settings['unfamiliar category'])
84 - ) :
85 - $this->settings['unfamiliar category'] = $this->settings['unfamiliar categories'];
86 - endif;
87 - if (
88 - FeedWordPress::affirmative($this->settings, 'hardcode categories')
89 - and !isset($this->settings['unfamiliar category'])
90 - ) :
91 - $this->settings['unfamiliar category'] = 'default';
92 - endif;
66 + public function found () {
67 + return is_object($this->link) and !is_wp_error($this->link);
68 + } /* SyndicatedLink::found () */
93 69
94 - // Set this up automagically for del.icio.us
95 - $bits = parse_url($this->link->link_rss);
96 - $tagspacers = array('del.icio.us', 'feeds.delicious.com');
97 - if (!isset($this->settings['cat_split']) and in_array($bits['host'], $tagspacers)) :
98 - $this->settings['cat_split'] = '\s'; // Whitespace separates multiple tags in del.icio.us RSS feeds
99 - endif;
70 + public function id () {
71 + return (is_object($this->link) ? $this->link->link_id : NULL);
72 + }
100 73
101 - if (isset($this->settings['cats'])):
102 - $this->settings['cats'] = preg_split(FEEDWORDPRESS_CAT_SEPARATOR_PATTERN, $this->settings['cats']);
103 - endif;
104 - if (isset($this->settings['tags'])):
105 - $this->settings['tags'] = preg_split(FEEDWORDPRESS_CAT_SEPARATOR_PATTERN, $this->settings['tags']);
106 - endif;
107 -
108 - if (isset($this->settings['map authors'])) :
109 - $author_rules = explode("\n\n", $this->settings['map authors']);
110 - $ma = array();
111 - foreach ($author_rules as $rule) :
112 - list($rule_type, $author_name, $author_action) = explode("\n", $rule);
113 -
114 - // Normalize for case and whitespace
115 - $rule_type = strtolower(trim($rule_type));
116 - $author_name = strtolower(trim($author_name));
117 - $author_action = strtolower(trim($author_action));
118 -
119 - $ma[$rule_type][$author_name] = $author_action;
120 - endforeach;
121 - $this->settings['map authors'] = $ma;
122 - endif;
123 - endif;
124 - } /* SyndicatedLink::SyndicatedLink () */
125 -
126 - function found () {
127 - return is_object($this->link);
128 - } /* SyndicatedLink::found () */
74 + public function stale () {
75 + global $feedwordpress;
129 76
130 - function stale () {
131 77 $stale = true;
132 - if (isset($this->settings['update/hold']) and ($this->settings['update/hold']=='ping')) :
78 + if ($this->setting('update/hold')=='ping') :
133 79 $stale = false; // don't update on any timed updates; pings only
134 - elseif (isset($this->settings['update/hold']) and ($this->settings['update/hold']=='next')) :
80 + elseif ($this->setting('update/hold')=='next') :
135 81 $stale = true; // update on the next timed update
136 - elseif (!isset($this->settings['update/ttl']) or !isset($this->settings['update/last'])) :
82 + elseif ( !$this->setting('update/last') ) :
137 83 $stale = true; // initial update
84 + elseif ($feedwordpress->force_update_all()) :
85 + $stale = true; // forced general updating
138 86 else :
139 - $after = ((int) $this->settings['update/last'])
140 - +((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 + );
141 92 $stale = (time() >= $after);
142 93 endif;
143 94 return $stale;
144 95 } /* SyndicatedLink::stale () */
145 96
146 - 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) {
147 132 global $wpdb;
148 133
149 - $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 +
150 139 $new_count = NULL;
151 140
152 - $resume = FeedWordPress::affirmative($this->settings, 'update/unfinished');
141 + $resume = ('yes'==$this->setting('update/unfinished'));
153 142 if ($resume) :
154 143 // pick up where we left off
155 - $processed = array_map('trim', explode("\n", $this->settings['update/processed']));
144 + $processed = array_map('trim', explode("\n", $this->setting('update/processed')));
156 145 else :
157 146 // begin at the beginning
158 147 $processed = array();
159 148 endif;
160 149
161 - 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 +
162 188 $new_count = array('new' => 0, 'updated' => 0);
163 189
164 190 # -- Update Link metadata live from feed
165 191 $channel = $this->magpie->channel;
@@ -166,47 +192,58 @@
166 192
167 193 if (!isset($channel['id'])) :
168 194 $channel['id'] = $this->link->link_rss;
169 195 endif;
170 -
196 +
171 197 $update = array();
172 198 if (!$this->hardcode('url') and isset($channel['link'])) :
173 - $update[] = "link_url = '".$wpdb->escape($channel['link'])."'";
199 + $update[] = "link_url = '".esc_sql($channel['link'])."'";
174 200 endif;
175 -
201 +
176 202 if (!$this->hardcode('name') and isset($channel['title'])) :
177 - $update[] = "link_name = '".$wpdb->escape($channel['title'])."'";
203 + $update[] = "link_name = '".esc_sql($channel['title'])."'";
178 204 endif;
179 -
205 +
180 206 if (!$this->hardcode('description')) :
181 207 if (isset($channel['tagline'])) :
182 - $update[] = "link_description = '".$wpdb->escape($channel['tagline'])."'";
208 + $update[] = "link_description = '".esc_sql($channel['tagline'])."'";
183 209 elseif (isset($channel['description'])) :
184 - $update[] = "link_description = '".$wpdb->escape($channel['description'])."'";
210 + $update[] = "link_description = '".esc_sql($channel['description'])."'";
185 211 endif;
186 212 endif;
187 -
188 - $this->settings = array_merge($this->settings, $this->flatten_array($channel));
189 213
190 - $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 +
191 219 if (!is_null($ttl)) :
192 - $this->settings['update/ttl'] = $ttl;
193 - $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');
194 223 else :
195 - $this->settings['update/ttl'] = rand(30, 120); // spread over time interval for staggered updates
196 - $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');
197 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 + ));
198 235
199 - if (!isset($this->settings['update/hold']) or $this->settings['update/hold']!='ping') :
200 - $this->settings['update/hold'] = 'scheduled';
236 + if (!$this->setting('update/hold') != 'ping') :
237 + $this->update_setting('update/hold', 'scheduled');
201 238 endif;
202 239
203 - $this->settings['update/unfinished'] = 'yes';
240 + $this->update_setting('update/unfinished', 'yes');
204 241
205 - $update[] = "link_notes = '".$wpdb->escape($this->settings_to_notes())."'";
242 + $update[] = "link_notes = '".esc_sql($this->settings_to_notes())."'";
206 243
207 244 $update_set = implode(',', $update);
208 -
245 +
209 246 // Update the properties of the link from the feed information
210 247 $result = $wpdb->query("
211 248 UPDATE $wpdb->links
212 249 SET $update_set
@@ -211,15 +248,36 @@
211 248 UPDATE $wpdb->links
212 249 SET $update_set
213 250 WHERE link_id='$this->id'
214 251 ");
252 + do_action('update_syndicated_feed', $this->id, $this);
215 253
216 254 # -- Add new posts from feed and update any updated posts
217 255 $crashed = false;
218 256
219 - if (is_array($this->magpie->items)) :
220 - foreach ($this->magpie->items as $item) :
221 - $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 +
222 280 if (!$resume or !in_array(trim($post->guid()), $processed)) :
223 281 $processed[] = $post->guid();
224 282 if (!$post->filtered()) :
225 283 $new = $post->store();
@@ -230,21 +288,60 @@
230 288 $crashed = true;
231 289 break;
232 290 endif;
233 291 endif;
292 +
293 + unset($post);
294 +
234 295 endforeach;
235 296 endif;
236 -
237 - // Copy back any changes to feed settings made in the course of updating (e.g. new author rules)
238 - $to_notes = $this->settings;
239 297
240 - $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);
241 335 if (!$crashed) :
242 - $this->settings['update/unfinished'] = 'no';
336 + $this->update_setting('update/unfinished', 'no');
243 337 endif;
338 + $this->update_setting('link/item count', count($posts));
244 339
245 - $update_set = "link_notes = '".$wpdb->escape($this->settings_to_notes())."'";
246 -
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 +
247 344 // Update the properties of the link from the feed information
248 345 $result = $wpdb->query("
249 346 UPDATE $wpdb->links
250 347 SET $update_set
@@ -249,34 +346,153 @@
249 346 UPDATE $wpdb->links
250 347 SET $update_set
251 348 WHERE link_id='$this->id'
252 349 ");
350 +
351 + do_action("update_syndicated_feed_completed", $this->id, $this);
253 352 endif;
254 -
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 +
255 364 return $new_count;
256 365 } /* SyndicatedLink::poll() */
257 366
258 - 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) {
259 397 global $wpdb;
260 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 +
261 486 if (strlen($newuser_name) > 0) :
262 - $userdata = array();
263 - $userdata['ID'] = NULL;
264 -
265 - $userdata['user_login'] = sanitize_user($newuser_name);
266 - $userdata['user_login'] = apply_filters('pre_user_login', $userdata['user_login']);
267 -
268 - $userdata['user_nicename'] = sanitize_title($newuser_name);
269 - $userdata['user_nicename'] = apply_filters('pre_user_nicename', $userdata['user_nicename']);
270 -
271 - $userdata['display_name'] = $wpdb->escape($newuser_name);
272 -
273 - $newuser_id = wp_insert_user($userdata);
487 + $newuser_id = fwp_insert_new_user($newuser_name);
274 488 if (is_numeric($newuser_id)) :
275 489 if (is_null($name)) : // Unfamiliar author
276 - $this->settings['unfamiliar author'] = $newuser_id;
490 + $this->update_setting('unfamiliar author', $newuser_id);
277 491 else :
278 - $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);
279 495 endif;
280 496 else :
281 497 // TODO: Add some error detection and reporting
282 498 endif;
@@ -284,9 +500,114 @@
284 500 // TODO: Add some error reporting
285 501 endif;
286 502 } /* SyndicatedLink::map_name_to_new_user () */
287 503
288 - 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 () {
289 610 $to_notes = $this->settings;
290 611
291 612 unset($to_notes['link/id']); // Magic setting; don't save
292 613 unset($to_notes['link/uri']); // Magic setting; don't save
@@ -298,14 +619,21 @@
298 619 if (isset($to_notes['update/processed']) and (is_array($to_notes['update/processed']))) :
299 620 $to_notes['update/processed'] = implode("\n", $to_notes['update/processed']);
300 621 endif;
301 622
302 - if (isset($to_notes['cats']) and is_array($to_notes['cats'])) :
303 - $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']);
304 635 endif;
305 - if (isset($to_notes['tags']) and is_array($to_notes['tags'])) :
306 - $to_notes['tags'] = implode(FEEDWORDPRESS_CAT_SEPARATOR, $to_notes['tags']);
307 - endif;
308 636
309 637 // Collapse the author mapping rule structure back into a flat string
310 638 if (isset($to_notes['map authors'])) :
311 639 $ma = array();
@@ -323,17 +651,305 @@
323 651 endforeach;
324 652 return $notes;
325 653 } /* SyndicatedLink::settings_to_notes () */
326 654
327 - function uri () {
328 - 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;
329 783 } /* SyndicatedLink::uri () */
330 784
331 - function homepage () {
332 - 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');
333 864 } /* SyndicatedLink::homepage () */
334 865
335 - 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) {
336 952 if (is_object($this->magpie)) :
337 953 $channel = $this->magpie->channel;
338 954 else :
339 955 $channel = array();
@@ -342,9 +958,10 @@
342 958 if (isset($channel['ttl'])) :
343 959 // "ttl stands for time to live. It's a number of
344 960 // minutes that indicates how long a channel can be
345 961 // cached before refreshing from the source."
346 - // <http://blogs.law.harvard.edu/tech/rss#ltttlgtSubelementOfLtchannelgt>
962 + // <http://blogs.law.harvard.edu/tech/rss#ltttlgtSubelementOfLtchannelgt>
963 + $xml = 'rss:ttl';
347 964 $ret = $channel['ttl'];
348 965 elseif (isset($channel['sy']['updatefrequency']) or isset($channel['sy']['updateperiod'])) :
349 966 $period_minutes = array (
350 967 'hourly' => 60, /* minutes in an hour */
@@ -360,10 +977,10 @@
360 977 // daily is assumed." <http://web.resource.org/rss/1.0/modules/syndication/>
361 978 if (isset($channel['sy']['updateperiod'])) : $period = $channel['sy']['updateperiod'];
362 979 else : $period = 'daily';
363 980 endif;
364 -
365 - // "sy:updateFrequency: Used to describe the frequency
981 +
982 + // "sy:updateFrequency: Used to describe the frequency
366 983 // of updates in relation to the update period. A
367 984 // positive integer indicates how many times in that
368 985 // period the channel is updated. ... If omitted a value
369 986 // of 1 is assumed." <http://web.resource.org/rss/1.0/modules/syndication/>
@@ -369,26 +986,56 @@
369 986 // of 1 is assumed." <http://web.resource.org/rss/1.0/modules/syndication/>
370 987 if (isset($channel['sy']['updatefrequency'])) : $freq = (int) $channel['sy']['updatefrequency'];
371 988 else : $freq = 1;
372 989 endif;
373 -
990 +
991 + $xml = 'sy:updateFrequency';
374 992 $ret = (int) ($period_minutes[$period] / $freq);
375 993 else :
994 + $xml = NULL;
376 995 $ret = NULL;
377 996 endif;
378 - 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);
379 1006 } /* SyndicatedLink::ttl() */
380 1007
381 - // SyndicatedLink::flatten_array (): flatten an array. Useful for
382 - // hierarchical and namespaced elements.
383 - //
384 - // Given an array which may contain array or object elements in it,
385 - // return a "flattened" array: a one-dimensional array of scalars
386 - // containing each of the scalar elements contained within the array
387 - // structure. Thus, for example, if $a['b']['c']['d'] == 'e', then the
388 - // returned array for FeedWordPress::flatten_array($a) will contain a key
389 - // $a['feed/b/c/d'] with value 'e'.
390 - 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 = '/') {
391 1038 $ret = array ();
392 1039 if (is_array($arr)) :
393 1040 foreach ($arr as $key => $value) :
394 1041 if (is_scalar($value)) :
@@ -400,33 +1047,138 @@
400 1047 endif;
401 1048 return $ret;
402 1049 } /* SyndicatedLink::flatten_array () */
403 1050
404 - function hardcode ($what) {
405 - $default = get_option("feedwordpress_hardcode_$what");
406 - if ( $default === 'yes' ) :
407 - // If the default is to hardcode, then we want the
408 - // negation of negative(): TRUE by default and FALSE if
409 - // the setting is explicitly "no"
410 - $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;
411 1057 else :
412 - // If the default is NOT to hardcode, then we want
413 - // affirmative(): FALSE by default and TRUE if the
414 - // setting is explicitly "yes"
415 - $ret = FeedWordPress::affirmative($this->settings, "hardcode $what");
1058 + $ret = false;
416 1059 endif;
417 1060 return $ret;
418 1061 } /* SyndicatedLink::hardcode () */
419 1062
420 - function syndicated_status ($what, $default) {
1063 + public function syndicated_status ($what, $default, $fallback = true) {
421 1064 global $wpdb;
422 1065
423 - $ret = get_option("feedwordpress_syndicated_{$what}_status");
424 - if ( isset($this->settings["$what status"]) ) :
425 - $ret = $this->settings["$what status"];
426 - elseif (!$ret) :
427 - $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');
428 1094 endif;
429 - return $wpdb->escape(trim(strtolower($ret)));
430 - } /* SyndicatedLink:syndicated_status () */
431 -} // 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 */
432 1184