FeedWordPress - Version 0.99

Version Description

Download this release

Release Info

Developer radgeek
Plugin Icon wp plugin FeedWordPress
Version 0.99
Comparing to
See all releases

Code changes from version 0.981 to 0.99

ChangeLog.text CHANGED
@@ -1,9 +1,124 @@
1
  FeedWordPress Change Log
2
  ========================
3
 
4
- Changes from 0.98 to 0.981
5
  --------------------------
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
 
 
 
7
  Version 0.981 is a narrowly targeted bugfix and compatibility release, whose
8
  main purpose is to resolve a major outstanding problem: the incompatibility
9
  between version 0.98 of WordPress and the recently released WordPress 2.1.
1
  FeedWordPress Change Log
2
  ========================
3
 
4
+ Changes from 0.981 to 0.99
5
  --------------------------
6
+ Version 0.99 adds several significant new features, fixes some bugs, and
7
+ provides compatability with WordPress 2.2.x and 2.3.x.
8
+
9
+ * WORDPRESS 2.2 AND 2.3 COMPATIBILITY: FeedWordPress should now be
10
+ compatible with WordPress version 2.2 and the upcoming WordPress
11
+ version 2.3. In particular, it has been tested extensively against
12
+ WordPress 2.2.3 and WordPress 2.3 Release Candidate 1.
13
+
14
+ * AUTOMATIC UPDATES WITHOUT CRON: FeedWordPress now allows you to
15
+ automatically schedule checks for new posts without using external task
16
+ scheduling tools such as cron. In order to enable automatic updates, go
17
+ to **Syndication --> Options** and set "Check for new posts" to
18
+ "automatically." For details, see "Automatic Feed Updates" in
19
+ README.text.
20
+
21
+ An important side-effect of the changes to the update system is that if
22
+ you were previously using the cron job and the `update-feeds.php` script
23
+ to schedule updates, you need to change your cron set-up. The old
24
+ `update-feeds.php` script no longer exists. Instead, if you wish to use
25
+ a cron job to guarantee updates on a particular schedule, you should
26
+ have the cron job fetch the front page of your blog (for example, by
27
+ using `curl http://www.zyx.com/blog/ > /dev/null`) instead of activating
28
+ the `update-feeds.php` script. If automatic updates have been enabled,
29
+ fetching the front page will automatically trigger the update process.
30
+
31
+ * INTERFACE REORGANIZATION: All FeedWordPress functions are now located
32
+ under a top-level "Syndication" menu in the WordPress Dashboard. To
33
+ manage the list of syndicated sites, manually check for new posts on
34
+ one or more feeds, or syndicate a new site, you should use the main page
35
+ under **Syndication**. To change global settings for FeedWordPress,
36
+ you should use **Syndication --> Options**.
37
+
38
+ * FILE STRUCTURE REORGANIZATION: Due to a combination of changing styles
39
+ for FeedWordPress plugins and lingering bugs in the FeedWordPress admin
40
+ menu code, the code for FeedWordPress is now contained in two different
41
+ PHP files, which should be installed together in a subdirectory of your
42
+ plugins directory named `feedwordpress`. (See README.text for
43
+ installation and upgrade instructions relating to the change.)
44
+
45
+ * MULTIPLE CATEGORIES SETTING: Some feeds use non-standard methods to
46
+ indicate multiple categories within a single category element. (The most
47
+ popular site to do this is del.icio.us, which separates tags with a
48
+ space.) FeedWordPress now allows you to set an optional setting, for any
49
+ feed which does this, indicating the character or characters used to
50
+ divide multiple categories, using a Perl-compatible regular expression.
51
+ (In the case of del.icio.us feeds, FeedWordPress will automatically use
52
+ \s for the pattern without your having to do any further configuration.)
53
+ To turn this setting on, simply use the "Edit" link for the feed that
54
+ you want to turn it on for.
55
+
56
+ * REGULAR EXPRESSION BUG FIXED: Eliminated a minor bug in the regular
57
+ expressions for e-mail addresses (used in parsing RSS `author`
58
+ elements), which could produce unsightly error messages for some users
59
+ parsing RSS 2.0 feeds.
60
+
61
+ * DATE / UPDATE BUG FIXED: A bug in date handling was eliminated that may
62
+ have caused problems if any of (1) WordPress, or (2) PHP, or (3) your
63
+ web server, or (4) your MySQL server, has been set to use a different
64
+ time zone from the one that any of the others is set to use. If
65
+ FeedWordPress has not been properly updating updated posts, or has been
66
+ updating posts when there shouldn't be any changes for the update, this
67
+ release may solve that problem.
68
+
69
+ * GOOGLE READER BUGS FIXED: A couple of bugs that made it difficult for
70
+ FeedWordPress to interact with Google Reader public feeds have been
71
+ fixed. Firstly, if you encountered an error message reading "There was a
72
+ problem adding the newsfeed. [SQL: ]" when you tried to add the feed,
73
+ the cause of this error has been fixed. Secondly, if you succeeded in
74
+ getting FeedWordPress to check a Google Reader feed, only to find that
75
+ the title of posts had junk squashed on to the end of them, that bug
76
+ has been fixed too. To fix this bug, you must install the newest version
77
+ of the optional MagpieRSS upgrade.
78
+
79
+ * FILTER PARAMETERS: Due to an old, old bug in WordPress 1.5.0 (which was
80
+ what was available back when I first wrote the filter interface),
81
+ FeedWordPress has traditionally only passed one parameter to
82
+ syndicated_item and syndicated_post filters functions -- an array
83
+ containing either the Magpie representation of a syndicated item from
84
+ the feed, or the database representation of a post about to be inserted
85
+ into the WordPress database. If you needed information about the feed
86
+ that the item came from, this was accessible only through a pair of
87
+ global variables, $fwp_channel and $fwp_feedmeta.
88
+
89
+ Since it's been a pretty long time since WordPress 1.5.0 was in
90
+ widespread usage, I have gone ahead and added an optional second
91
+ parameter to the invocation of the syndicated_item and syndicated_post
92
+ filters. If you have written a filter for FeedWordPress that uses either
93
+ of these hooks, you can now register that filter to accept 2 parameters.
94
+ If you do so, the second parameter will be a SyndicatedPost object,
95
+ which, among other things, allows you to access information about the
96
+ feed from which an item is syndicated using the $post->feed and the
97
+ $post->feedmeta elements (where $post is the name of the second
98
+ parameter).
99
+
100
+ NOTE THAT THE OLD GLOBAL VARIABLES ARE STILL AVAILABLE, for the time
101
+ being at least, so existing filters will not break with the upgrade.
102
+ They should be considered deprecated, however, and may be eliminated in
103
+ the future.
104
+
105
+ * FILTER CHANGE / BUGFIX: the array that is passed as the first argument
106
+ syndicated_post filters no longer is no longer backslash-escaped for
107
+ MySQL when filters are called. This was originally a bug, or an
108
+ oversight; the contents of the array should only be escaped for the
109
+ database *after* they have gone through all filters. IF YOU HAVE WRITTEN
110
+ ANY syndicated_post FILTERS THAT PRESUME THE OLD BEHAVIOR OF PASSING IN
111
+ STRINGS THAT ARE ALREADY BACKSLASH-ESCAPED, UPDATE YOUR FILTERS
112
+ ACCORDINGLY.
113
+
114
+ * OTHER MINOR BUGFIXES AND INTERNAL CHANGES: The internal architecture of
115
+ FeedWordPress has been significantly changed to make the code more
116
+ modular and clean; hopefully this should help reduce the number of
117
+ compatibility updates that are needed, and make them easier and quicker
118
+ when they are needed.
119
 
120
+ Changes from 0.98 to 0.981
121
+ --------------------------
122
  Version 0.981 is a narrowly targeted bugfix and compatibility release, whose
123
  main purpose is to resolve a major outstanding problem: the incompatibility
124
  between version 0.98 of WordPress and the recently released WordPress 2.1.
OPTIONAL/wp-includes/rss.php CHANGED
@@ -4,7 +4,7 @@
4
  * Author: Kellan Elliot-McCrea <kellan@protest.net>
5
  * WordPress development team <http://www.wordpress.org/>
6
  * Charles Johnson <technophilia@radgeek.com>
7
- * Version: 0.8wp (2005.10.14)
8
  * License: GPL
9
  *
10
  * Provenance:
@@ -99,7 +99,7 @@ $wp_encoding = get_settings('blog_charset');
99
  define('MAGPIE_OUTPUT_ENCODING', ($wp_encoding?$wp_encoding:'ISO-8859-1'));
100
 
101
  ################################################################################
102
- ## rss_parse.inc: from MagpieRSS 0.8a ##########################################
103
  ################################################################################
104
 
105
  /**
@@ -126,27 +126,92 @@ class MagpieRSS {
126
  var $WARNING = "";
127
 
128
  // define some constants
129
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
  var $_ATOM_CONTENT_CONSTRUCTS = array(
131
- 'content', 'summary', 'title', /* common */
132
- 'info', 'tagline', 'copyright', /* Atom 0.3 */
133
- 'rights', 'subtitle', /* Atom 1.0 */
134
- );
135
  var $_XHTML_CONTENT_CONSTRUCTS = array('body', 'div');
136
  var $_KNOWN_ENCODINGS = array('UTF-8', 'US-ASCII', 'ISO-8859-1');
137
 
138
  // parser variables, useless if you're not a parser, treat as private
139
- var $stack = array(); // parser stack
 
140
  var $inchannel = false;
141
  var $initem = false;
142
-
143
  var $incontent = array(); // non-empty if in namespaced XML content field
144
- var $exclude_top = false; // true when Atom 1.0 type="xhtml"
 
145
 
146
  var $intextinput = false;
147
  var $inimage = false;
 
148
  var $current_namespace = false;
149
-
 
150
  /**
151
  * Set up XML parser, parse source, and return populated RSS object..
152
  *
@@ -176,7 +241,7 @@ class MagpieRSS {
176
  *
177
  */
178
  function MagpieRSS ($source, $output_encoding='ISO-8859-1',
179
- $input_encoding=null, $detect_encoding=true)
180
  {
181
  # if PHP xml isn't compiled in, die
182
  #
@@ -207,7 +272,9 @@ class MagpieRSS {
207
  'feed_start_element', 'feed_end_element' );
208
 
209
  xml_set_character_data_handler( $this->parser, 'feed_cdata' );
210
-
 
 
211
  $status = xml_parse( $this->parser, $source );
212
 
213
  if (! $status ) {
@@ -227,67 +294,114 @@ class MagpieRSS {
227
  $this->normalize();
228
  }
229
 
230
- function feed_start_element($p, $element, &$attrs) {
231
- $el = $element = strtolower($element);
232
- $attrs = array_change_key_case($attrs, CASE_LOWER);
233
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
234
  // check for a namespace, and split if found
235
- if ( empty($this->incontent) ) { // Don't munge content tags
236
- $ns = false;
237
- if ( strpos( $element, ':' ) ) {
238
- list($ns, $el) = split( ':', $element, 2);
 
 
 
 
 
 
 
239
  }
240
- if ( $ns and $ns != 'rdf' ) {
241
- $this->current_namespace = $ns;
 
 
242
  }
243
  }
244
 
 
 
245
  # if feed type isn't set, then this is first element of feed
246
  # identify feed from root element
247
  #
248
  if (!isset($this->feed_type) ) {
249
  if ( $el == 'rdf' ) {
250
  $this->feed_type = RSS;
 
251
  $this->feed_version = '1.0';
252
  }
253
  elseif ( $el == 'rss' ) {
254
  $this->feed_type = RSS;
 
255
  $this->feed_version = $attrs['version'];
256
  }
257
  elseif ( $el == 'feed' ) {
258
  $this->feed_type = ATOM;
259
- if ($attrs['xmlns'] == 'http://www.w3.org/2005/Atom') { // Atom 1.0
 
 
260
  $this->feed_version = '1.0';
261
- }
262
- else { // Atom 0.3, probably.
263
- $this->feed_version = $attrs['version'];
264
- }
265
  $this->inchannel = true;
266
  }
267
  return;
268
  }
269
-
270
  // if we're inside a namespaced content construct, treat tags as text
271
  if ( !empty($this->incontent) )
272
  {
273
- if ((count($this->incontent) > 1) or !$this->exclude_top) {
274
- // if tags are inlined, then flatten
275
- $attrs_str = join(' ',
276
- array_map('map_attrs',
277
- array_keys($attrs),
278
- array_values($attrs) ) );
279
- if (strlen($attrs_str) > 0) $attrs_str = ' '.$attrs_str;
280
-
281
- $this->append_content( "<{$element}{$attrs_str}>" );
282
- }
283
- array_push($this->incontent, $el); // stack for parsing content XML
 
 
 
 
 
 
 
 
 
 
 
 
 
284
  }
285
-
286
- elseif ( $el == 'channel' )
287
- {
288
  $this->inchannel = true;
289
  }
290
-
291
  elseif ($el == 'item' or $el == 'entry' )
292
  {
293
  $this->initem = true;
@@ -316,328 +430,378 @@ class MagpieRSS {
316
 
317
  // set stack[0] to current element
318
  else {
319
- // Atom support many links per containing element.
320
- // Magpie treats link elements of type rel='alternate'
321
- // as being equivalent to RSS's simple link element.
322
-
323
- $atom_link = false;
324
- if ($this->feed_type == ATOM and $el == 'link') {
325
- $atom_link = true;
326
- if (isset($attrs['rel']) and $attrs['rel'] != 'alternate') {
327
- $el = $el . "_" . $attrs['rel']; // pseudo-element names for Atom link elements
328
- }
329
- }
330
- # handle atom content constructs
331
- elseif ( $this->feed_type == ATOM and in_array($el, $this->_ATOM_CONTENT_CONSTRUCTS) )
332
- {
333
- // avoid clashing w/ RSS mod_content
334
- if ($el == 'content' ) {
335
- $el = 'atom_content';
336
- }
337
-
338
- // assume that everything accepts namespaced XML
339
- // (that will pass through some non-validating feeds;
340
- // but so what? this isn't a validating parser)
341
- $this->incontent = array();
342
- array_push($this->incontent, $el); // start a stack
343
-
344
- if (
345
- isset($attrs['type'])
346
- and trim(strtolower($attrs['type']))=='xhtml'
347
- ) {
348
- $this->exclude_top = true;
349
- } else {
350
- $this->exclude_top = false;
351
- }
352
- }
353
- # Handle inline XHTML body elements --CWJ
354
- elseif (
355
- ($this->current_namespace=='xhtml' or (isset($attrs['xmlns']) and $attrs['xmlns'] == 'http://www.w3.org/1999/xhtml'))
356
- and in_array($el, $this->_XHTML_CONTENT_CONSTRUCTS) )
357
- {
358
- $this->current_namespace = 'xhtml';
359
- $this->incontent = array();
360
- array_push($this->incontent, $el); // start a stack
361
- $this->exclude_top = false;
362
- }
363
 
364
- array_unshift($this->stack, $el);
365
- $elpath = join('_', array_reverse($this->stack));
366
-
367
- $n = $this->element_count($elpath);
368
- $this->element_count($elpath, $n+1);
369
 
370
- if ($n > 0) {
371
- array_shift($this->stack);
372
- array_unshift($this->stack, $el.'#'.($n+1));
373
- $elpath = join('_', array_reverse($this->stack));
374
- }
375
 
376
- // this makes the baby Jesus cry, but we can't do it in normalize()
377
- // because we've made the element name for Atom links unpredictable
378
- // by tacking on the relation to the end. -CWJ
379
- if ($atom_link and isset($attrs['href'])) {
380
- $this->append($elpath, $attrs['href']);
381
- }
382
-
383
- // add attributes
384
- if (count($attrs) > 0) {
385
- $this->append($elpath.'@', join(',', array_keys($attrs)));
386
- foreach ($attrs as $attr => $value) {
387
- $this->append($elpath.'@'.$attr, $value);
388
- }
389
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
390
  }
391
  }
392
-
393
 
394
-
395
  function feed_cdata ($p, $text) {
396
-
397
  if ($this->incontent) {
398
- $this->append_content( $text );
399
- }
400
- else {
401
- $current_el = join('_', array_reverse($this->stack));
402
  $this->append($current_el, $text);
403
  }
404
  }
405
 
406
  function feed_end_element ($p, $el) {
407
- $el = strtolower($el);
408
-
409
- if ( $this->incontent ) {
410
- $opener = array_pop($this->incontent);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
411
 
412
- // Don't get bamboozled by namespace voodoo
413
- if (strpos($el, ':')) { list($ns, $closer) = split(':', $el); }
414
- else { $ns = false; $closer = $el; }
 
 
 
 
 
 
415
 
416
- // Don't get bamboozled by our munging of <atom:content>, either
417
- if ($this->feed_type == ATOM and $closer == 'content') {
418
- $closer = 'atom_content';
 
419
  }
420
 
421
- // balance tags properly
422
- // note: i don't think this is actually neccessary
423
- if ($opener != $closer) {
424
- array_push($this->incontent, $opener);
425
- $this->append_content("<$el />");
426
- } elseif ($this->incontent) { // are we in the content construct still?
427
- if ((count($this->incontent) > 1) or !$this->exclude_top) {
428
- $this->append_content("</$el>");
 
 
 
 
429
  }
430
- } else { // shift the opening of the content construct off the normal stack
431
- array_shift( $this->stack );
432
- }
433
- }
434
- elseif ( $el == 'item' or $el == 'entry' )
435
- {
436
- $this->items[] = $this->current_item;
437
- $this->current_item = array();
438
- $this->initem = false;
439
 
440
- $this->current_category = 0;
441
- }
442
- elseif ($this->feed_type == RSS and $this->current_namespace == '' and $el == 'textinput' )
443
- {
444
- $this->intextinput = false;
445
- }
446
- elseif ($this->feed_type == RSS and $this->current_namespace == '' and $el == 'image' )
447
- {
448
- $this->inimage = false;
449
- }
450
- elseif ($el == 'channel' or $el == 'feed' )
451
- {
452
- $this->inchannel = false;
453
- }
454
- else {
455
- array_shift( $this->stack );
456
- }
457
-
458
- if ( !$this->incontent ) { // Don't munge the namespace after finishing with elements in namespaced content constructs -CWJ
459
- $this->current_namespace = false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
460
  }
461
- }
462
-
463
  function concat (&$str1, $str2="") {
464
  if (!isset($str1) ) {
465
  $str1="";
466
  }
467
  $str1 .= $str2;
468
  }
469
-
 
 
 
 
 
 
 
 
 
 
 
470
  function append_content($text) {
471
- if ( $this->initem ) {
472
- if ($this->current_namespace) {
473
- $this->concat( $this->current_item[$this->current_namespace][ reset($this->incontent) ], $text );
 
 
 
 
 
 
 
474
  } else {
475
- $this->concat( $this->current_item[ reset($this->incontent) ], $text );
476
  }
477
- }
478
- elseif ( $this->inchannel ) {
479
  if ($this->current_namespace) {
480
- $this->concat( $this->channel[$this->current_namespace][ reset($this->incontent) ], $text );
481
  } else {
482
- $this->concat( $this->channel[ reset($this->incontent) ], $text );
483
  }
484
- }
485
  }
486
 
487
  // smart append - field and namespace aware
488
  function append($el, $text) {
489
- if (!$el) {
490
- return;
491
- }
492
- if ( $this->current_namespace )
493
- {
494
- if ( $this->initem ) {
495
- $this->concat(
496
- $this->current_item[ $this->current_namespace ][ $el ], $text);
497
- }
498
- elseif ($this->inchannel) {
499
- $this->concat(
500
- $this->channel[ $this->current_namespace][ $el ], $text );
501
- }
502
- elseif ($this->intextinput) {
503
- $this->concat(
504
- $this->textinput[ $this->current_namespace][ $el ], $text );
505
- }
506
- elseif ($this->inimage) {
507
- $this->concat(
508
- $this->image[ $this->current_namespace ][ $el ], $text );
509
- }
510
- }
511
- else {
512
- if ( $this->initem ) {
513
- $this->concat(
514
- $this->current_item[ $el ], $text);
515
- }
516
- elseif ($this->intextinput) {
517
- $this->concat(
518
- $this->textinput[ $el ], $text );
519
- }
520
- elseif ($this->inimage) {
521
- $this->concat(
522
- $this->image[ $el ], $text );
523
- }
524
- elseif ($this->inchannel) {
525
- $this->concat(
526
- $this->channel[ $el ], $text );
527
- }
528
-
529
- }
530
  }
531
 
 
 
 
 
 
 
 
532
  // smart count - field and namespace aware
533
  function element_count ($el, $set = NULL) {
534
- if (!$el) {
535
- return;
536
- }
537
- if ( $this->current_namespace )
538
- {
539
- if ( $this->initem ) {
540
- if (!is_null($set)) { $this->current_item[ $this->current_namespace ][ $el.'#' ] = $set; }
541
- $ret = (isset($this->current_item[ $this->current_namespace ][ $el.'#' ]) ?
542
- $this->current_item[ $this->current_namespace ][ $el.'#' ] : 0);
543
- }
544
- elseif ($this->inchannel) {
545
- if (!is_null($set)) { $this->channel[ $this->current_namespace ][ $el.'#' ] = $set; }
546
- $ret = (isset($this->channel[ $this->current_namespace][ $el.'#' ]) ?
547
- $this->channel[ $this->current_namespace][ $el.'#' ] : 0);
548
- }
549
- }
550
- else {
551
- if ( $this->initem ) {
552
- if (!is_null($set)) { $this->current_item[ $el.'#' ] = $set; }
553
- $ret = (isset($this->current_item[ $el.'#' ]) ?
554
- $this->current_item[ $el.'#' ] : 0);
555
- }
556
- elseif ($this->inchannel) {
557
- if (!is_null($set)) {$this->channel[ $el.'#' ] = $set; }
558
- $ret = (isset($this->channel[ $el.'#' ]) ?
559
- $this->channel[ $el.'#' ] : 0);
560
- }
561
- }
562
- return $ret;
563
  }
564
 
565
  function normalize_enclosure (&$source, $from, &$dest, $to, $i) {
566
- $id_from = $this->element_id($from, $i);
567
- $id_to = $this->element_id($to, $i);
568
- if (isset($source["{$id_from}@"])) {
569
- foreach (explode(',', $source["{$id_from}@"]) as $attr) {
570
- if ($from=='link_enclosure' and $attr=='href') { // from Atom
571
- $dest["{$id_to}@url"] = $source["{$id_from}@{$attr}"];
572
- $dest["{$id_to}"] = $source["{$id_from}@{$attr}"];
573
- }
574
- elseif ($from=='enclosure' and $attr=='url') { // from RSS
575
- $dest["{$id_to}@href"] = $source["{$id_from}@{$attr}"];
576
- $dest["{$id_to}"] = $source["{$id_from}@{$attr}"];
577
- }
578
- else {
579
- $dest["{$id_to}@{$attr}"] = $source["{$id_from}@{$attr}"];
580
- }
581
- }
582
- }
583
  }
584
 
585
  function normalize_atom_person (&$source, $person, &$dest, $to, $i) {
586
- $id = $this->element_id($person, $i);
587
- $id_to = $this->element_id($to, $i);
588
 
589
- // Atom 0.3 <=> Atom 1.0
590
- if ($this->feed_version >= 1.0) { $used = 'uri'; $norm = 'url'; }
591
- else { $used = 'url'; $norm = 'uri'; }
592
 
593
- if (isset($source["{$id}_{$used}"])) {
594
- $dest["{$id_to}_{$norm}"] = $source["{$id}_{$used}"];
595
- }
596
 
597
- // Atom to RSS 2.0 and Dublin Core
598
- // RSS 2.0 person strings should be valid e-mail addresses if possible.
599
- if (isset($source["{$id}_email"])) {
600
- $rss_author = $source["{$id}_email"];
601
- }
602
- if (isset($source["{$id}_name"])) {
603
- $rss_author = $source["{$id}_name"]
604
- . (isset($rss_author) ? " <$rss_author>" : '');
605
- }
606
- if (isset($rss_author)) {
607
- $source[$id] = $rss_author; // goes to top-level author or contributor
608
- $dest[$id_to] = $rss_author; // goes to dc:creator or dc:contributor
609
- }
610
  }
611
 
612
  // Normalize Atom 1.0 and RSS 2.0 categories to Dublin Core...
613
  function normalize_category (&$source, $from, &$dest, $to, $i) {
614
- $cat_id = $this->element_id($from, $i);
615
- $dc_id = $this->element_id($to, $i);
616
-
617
- // first normalize category elements: Atom 1.0 <=> RSS 2.0
618
- if ( isset($source["{$cat_id}@term"]) ) { // category identifier
619
- $source[$cat_id] = $source["{$cat_id}@term"];
620
- } elseif ( $this->feed_type == RSS ) {
621
- $source["{$cat_id}@term"] = $source[$cat_id];
622
- }
623
-
624
- if ( isset($source["{$cat_id}@scheme"]) ) { // URI to taxonomy
625
- $source["{$cat_id}@domain"] = $source["{$cat_id}@scheme"];
626
- } elseif ( isset($source["{$cat_id}@domain"]) ) {
627
- $source["{$cat_id}@scheme"] = $source["{$cat_id}@domain"];
628
- }
629
 
630
- // Now put the identifier into dc:subject
631
- $dest[$dc_id] = $source[$cat_id];
 
 
 
 
 
 
 
 
 
 
 
 
 
632
  }
633
 
634
  // ... or vice versa
635
  function normalize_dc_subject (&$source, $from, &$dest, $to, $i) {
636
- $dc_id = $this->element_id($from, $i);
637
- $cat_id = $this->element_id($to, $i);
638
 
639
- $dest[$cat_id] = $source[$dc_id]; // RSS 2.0
640
- $dest["{$cat_id}@term"] = $source[$dc_id]; // Atom 1.0
641
  }
642
 
643
  // simplify the logic for normalize(). Makes sure that count of elements and
@@ -645,120 +809,124 @@ class MagpieRSS {
645
  // with things like attributes or change formats or the like, pass it a
646
  // callback to handle each element.
647
  function normalize_element (&$source, $from, &$dest, $to, $via = NULL) {
648
- if (isset($source[$from]) or isset($source["{$from}#"])) {
649
- if (isset($source["{$from}#"])) {
650
- $n = $source["{$from}#"];
651
- $dest["{$to}#"] = $source["{$from}#"];
652
- }
653
- else { $n = 1; }
654
 
655
- for ($i = 1; $i <= $n; $i++) {
656
- if (isset($via)) { // custom callback for ninja attacks
657
- $this->{$via}($source, $from, $dest, $to, $i);
658
- }
659
- else { // just make it the same
660
- $from_id = $this->element_id($from, $i);
661
- $to_id = $this->element_id($to, $i);
662
- $dest[$to_id] = $source[$from_id];
663
- }
664
- }
665
- }
666
  }
667
 
668
  function normalize () {
669
  // if atom populate rss fields and normalize 0.3 and 1.0 feeds
670
  if ( $this->is_atom() ) {
671
- // Atom 1.0 elements <=> Atom 0.3 elements (Thanks, o brilliant wordsmiths of the Atom 1.0 standard!)
672
- if ($this->feed_version < 1.0) {
673
- $this->normalize_element($this->channel, 'tagline', $this->channel, 'subtitle');
674
- $this->normalize_element($this->channel, 'copyright', $this->channel, 'rights');
675
- $this->normalize_element($this->channel, 'modified', $this->channel, 'updated');
676
- } else {
677
- $this->normalize_element($this->channel, 'subtitle', $this->channel, 'tagline');
678
- $this->normalize_element($this->channel, 'rights', $this->channel, 'copyright');
679
- $this->normalize_element($this->channel, 'updated', $this->channel, 'modified');
680
- }
681
- $this->normalize_element($this->channel, 'author', $this->channel['dc'], 'creator', 'normalize_atom_person');
682
- $this->normalize_element($this->channel, 'contributor', $this->channel['dc'], 'contributor', 'normalize_atom_person');
683
-
684
- // Atom elements to RSS elements
685
- $this->normalize_element($this->channel, 'subtitle', $this->channel, 'description');
686
-
687
- if ( isset($this->channel['logo']) ) {
688
- $this->normalize_element($this->channel, 'logo', $this->image, 'url');
689
- $this->normalize_element($this->channel, 'link', $this->image, 'link');
690
- $this->normalize_element($this->channel, 'title', $this->image, 'title');
691
- }
692
-
693
- for ( $i = 0; $i < count($this->items); $i++) {
694
- $item = $this->items[$i];
695
 
696
- // Atom 1.0 elements <=> Atom 0.3 elements
697
- if ($this->feed_version < 1.0) {
698
- $this->normalize_element($item, 'modified', $item, 'updated');
699
- $this->normalize_element($item, 'issued', $item, 'published');
700
- } else {
701
- $this->normalize_element($item, 'updated', $item, 'modified');
702
- $this->normalize_element($item, 'published', $item, 'issued');
703
- }
 
 
 
 
 
 
 
 
 
 
 
 
704
 
705
- // "If an atom:entry element does not contain
706
- // atom:author elements, then the atom:author elements
707
- // of the contained atom:source element are considered
708
- // to apply. In an Atom Feed Document, the atom:author
709
- // elements of the containing atom:feed element are
710
- // considered to apply to the entry if there are no
711
- // atom:author elements in the locations described
712
- // above." <http://atompub.org/2005/08/17/draft-ietf-atompub-format-11.html#rfc.section.4.2.1>
713
- if (!isset($item["author#"])) {
714
- if (isset($item["source_author#"])) { // from aggregation source
715
- $source = $item;
716
- $author = "source_author";
717
- } elseif (isset($this->channel["author#"])) { // from containing feed
718
- $source = $this->channel;
719
- $author = "author";
720
- }
 
 
721
 
722
- $item["author#"] = $source["{$author}#"];
723
- for ($au = 1; $au <= $item["author#"]; $au++) {
724
- $id_to = $this->element_id('author', $au);
725
- $id_from = $this->element_id($author, $au);
726
-
727
- $item[$id_to] = $source[$id_from];
728
- foreach (array('name', 'email', 'uri', 'url') as $what) {
729
- if (isset($source["{$id_from}_{$what}"])) {
730
- $item["{$id_to}_{$what}"] = $source["{$id_from}_{$what}"];
731
- }
732
- }
733
  }
 
734
  }
 
 
735
 
736
- // Atom elements to RSS elements
737
- $this->normalize_element($item, 'author', $item['dc'], 'creator', 'normalize_atom_person');
738
- $this->normalize_element($item, 'contributor', $item['dc'], 'contributor', 'normalize_atom_person');
739
- $this->normalize_element($item, 'summary', $item, 'description');
740
- $this->normalize_element($item, 'atom_content', $item['content'], 'encoded');
741
- $this->normalize_element($item, 'link_enclosure', $item, 'enclosure', 'normalize_enclosure');
742
 
743
- // Categories
744
- if ( isset($item['category#']) ) { // Atom 1.0 categories to dc:subject and RSS 2.0 categories
745
- $this->normalize_element($item, 'category', $item['dc'], 'subject', 'normalize_category');
746
- }
747
- elseif ( isset($item['dc']['subject#']) ) { // dc:subject to Atom 1.0 and RSS 2.0 categories
748
- $this->normalize_element($item['dc'], 'subject', $item, 'category', 'normalize_dc_subject');
749
- }
750
 
751
- // Normalized item timestamp
752
- $atom_date = (isset($item['published']) ) ? $item['published'] : $item['updated'];
753
- if ( $atom_date ) {
754
- $epoch = @parse_w3cdtf($atom_date);
755
- if ($epoch and $epoch > 0) {
756
- $item['date_timestamp'] = $epoch;
757
- }
758
- }
759
 
760
- $this->items[$i] = $item;
761
- }
762
  }
763
  elseif ( $this->is_rss() ) {
764
  // RSS elements to Atom elements
@@ -766,38 +934,37 @@ class MagpieRSS {
766
  $this->normalize_element($this->channel, 'description', $this->channel, 'subtitle'); // Atom 1.0 (yay wordsmithing!)
767
  $this->normalize_element($this->image, 'url', $this->channel, 'logo');
768
 
769
- for ( $i = 0; $i < count($this->items); $i++) {
770
- $item = $this->items[$i];
771
-
772
- // RSS elements to Atom elements
773
- $this->normalize_element($item, 'description', $item, 'summary');
774
- $this->normalize_element($item['content'], 'encoded', $item, 'atom_content');
775
- $this->normalize_element($item, 'enclosure', $item, 'link_enclosure', 'normalize_enclosure');
 
 
 
 
 
 
 
776
 
777
- // Categories
778
- if ( isset($item['category#']) ) { // RSS 2.0 categories to dc:subject and Atom 1.0 categories
779
- $this->normalize_element($item, 'category', $item['dc'], 'subject', 'normalize_category');
780
- }
781
- elseif ( isset($item['dc']['subject#']) ) { // dc:subject to Atom 1.0 and RSS 2.0 categories
782
- $this->normalize_element($item['dc'], 'subject', $item, 'category', 'normalize_dc_subject');
 
 
 
 
 
 
 
 
 
783
  }
784
-
785
- // Normalized item timestamp
786
- if ( $this->is_rss() == '1.0' and isset($item['dc']['date']) ) {
787
- $epoch = @parse_w3cdtf($item['dc']['date']);
788
- if ($epoch and $epoch > 0) {
789
- $item['date_timestamp'] = $epoch;
790
- }
791
- }
792
- elseif ( isset($item['pubdate']) ) {
793
- $epoch = @strtotime($item['pubdate']);
794
- if ($epoch > 0) {
795
- $item['date_timestamp'] = $epoch;
796
- }
797
- }
798
-
799
- $this->items[$i] = $item;
800
- }
801
  }
802
  }
803
 
@@ -835,7 +1002,7 @@ class MagpieRSS {
835
  $this->encoding = $out_enc;
836
  xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $out_enc);
837
  }
838
-
839
  return array($parser, $source);
840
  }
841
 
@@ -955,30 +1122,52 @@ class MagpieRSS {
955
  // magic ID function for multiple elemenets.
956
  // can be called as static MagpieRSS::element_id()
957
  function element_id ($el, $counter) {
958
- return $el . (($counter > 1) ? '#'.$counter : '');
959
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
960
  } // end class RSS
961
 
962
- function map_attrs($k, $v) {
963
- return "$k=\"$v\"";
964
- }
965
 
966
  // patch to support medieval versions of PHP4.1.x,
967
  // courtesy, Ryan Currie, ryan@digibliss.com
968
 
969
  if (!function_exists('array_change_key_case')) {
970
- define("CASE_UPPER",1);
971
- define("CASE_LOWER",0);
972
 
973
 
974
- function array_change_key_case($array,$case=CASE_LOWER) {
975
  if ($case==CASE_LOWER) $cmd='strtolower';
976
  elseif ($case==CASE_UPPER) $cmd='strtoupper';
977
  foreach($array as $key=>$value) {
978
  $output[$cmd($key)]=$value;
979
  }
980
  return $output;
981
- }
982
 
983
  }
984
 
@@ -1012,7 +1201,7 @@ require_once( dirname(__FILE__) . '/class-snoopy.php');
1012
  version will be return, if it exists (and if MAGPIE_CACHE_FRESH_ONLY is off)
1013
  \*=======================================================================*/
1014
 
1015
- define('MAGPIE_VERSION', '0.7');
1016
 
1017
  $MAGPIE_ERROR = "";
1018
 
@@ -1030,7 +1219,7 @@ function fetch_rss ($url) {
1030
  // fetch file, and parse it
1031
  $resp = _fetch_remote_file( $url );
1032
  if ( is_success( $resp->status ) ) {
1033
- return _response_to_rss( $resp );
1034
  }
1035
  else {
1036
  error("Failed to fetch $url and cache is off");
@@ -1103,7 +1292,7 @@ function fetch_rss ($url) {
1103
  return $rss;
1104
  }
1105
  elseif ( is_success( $resp->status ) ) {
1106
- $rss = _response_to_rss( $resp );
1107
  if ( $rss ) {
1108
  if (MAGPIE_DEBUG > 1) {
1109
  debug("Fetch successful");
@@ -1216,12 +1405,13 @@ function _fetch_remote_file ($url, $headers = "" ) {
1216
  Input: an HTTP response object (see Snoopy)
1217
  Output: parsed RSS object (see rss_parse)
1218
  \*=======================================================================*/
1219
- function _response_to_rss ($resp) {
1220
- $rss = new MagpieRSS( $resp->results, MAGPIE_OUTPUT_ENCODING, MAGPIE_INPUT_ENCODING, MAGPIE_DETECT_ENCODING );
1221
 
1222
  // if RSS parsed successfully
1223
  if ( $rss and !$rss->ERROR) {
1224
-
 
1225
  // find Etag, and Last-Modified
1226
  foreach($resp->headers as $h) {
1227
  // 2003-03-02 - Nicola Asuni (www.tecnick.com) - fixed bug "Undefined offset: 1"
@@ -1233,6 +1423,8 @@ function _response_to_rss ($resp) {
1233
  $val = "";
1234
  }
1235
 
 
 
1236
  if ( $field == 'ETag' ) {
1237
  $rss->etag = $val;
1238
  }
@@ -1591,6 +1783,185 @@ function parse_w3cdtf ( $date_str ) {
1591
  }
1592
  }
1593
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1594
  ################################################################################
1595
  ## WordPress: wp_rss(), get_rss() ##############################################
1596
  ################################################################################
4
  * Author: Kellan Elliot-McCrea <kellan@protest.net>
5
  * WordPress development team <http://www.wordpress.org/>
6
  * Charles Johnson <technophilia@radgeek.com>
7
+ * Version: 0.85wp (2007.09.24)
8
  * License: GPL
9
  *
10
  * Provenance:
99
  define('MAGPIE_OUTPUT_ENCODING', ($wp_encoding?$wp_encoding:'ISO-8859-1'));
100
 
101
  ################################################################################
102
+ ## rss_parse.inc: from MagpieRSS 0.85 ##########################################
103
  ################################################################################
104
 
105
  /**
126
  var $WARNING = "";
127
 
128
  // define some constants
129
+ var $_XMLNS_FAMILIAR = array (
130
+ 'http://www.w3.org/2005/Atom' => 'atom' /* 1.0 */,
131
+ 'http://purl.org/atom/ns#' => 'atom' /* pre-1.0 */,
132
+ 'http://purl.org/rss/1.0/' => 'rss' /* 1.0 */,
133
+ 'http://backend.userland.com/RSS2' => 'rss' /* 2.0 */,
134
+ 'http://www.w3.org/1999/02/22-rdf-syntax-ns#' => 'rdf',
135
+ 'http://www.w3.org/1999/xhtml' => 'xhtml',
136
+ 'http://purl.org/dc/elements/1.1/' => 'dc',
137
+ 'http://purl.org/dc/terms/' => 'dcterms',
138
+ 'http://purl.org/rss/1.0/modules/content/' => 'content',
139
+ 'http://purl.org/rss/1.0/modules/syndication/' => 'sy',
140
+ 'http://purl.org/rss/1.0/modules/taxonomy/' => 'taxo',
141
+ 'http://purl.org/rss/1.0/modules/dc/' => 'dc',
142
+ 'http://wellformedweb.org/CommentAPI/' => 'wfw',
143
+ 'http://webns.net/mvcb/' => 'admin',
144
+ 'http://purl.org/rss/1.0/modules/annotate/' => 'annotate',
145
+ 'http://xmlns.com/foaf/0.1/' => 'foaf',
146
+ 'http://madskills.com/public/xml/rss/module/trackback/' => 'trackback',
147
+ 'http://web.resource.org/cc/' => 'cc'
148
+ );
149
+
150
+ var $_XMLBASE_RESOLVE = array (
151
+ // Atom 0.3 and 1.0 xml:base support
152
+ 'atom' => array (
153
+ 'link' => array ('href' => true),
154
+ 'content' => array ('src' => true, '*xml' => true, '*html' => true),
155
+ 'summary' => array ('*xml' => true, '*html' => true),
156
+ 'title' => array ('*xml' => true, '*html' => true),
157
+ 'rights' => array ('*xml' => true, '*html' => true),
158
+ 'subtitle' => array ('*xml' => true, '*html' => true),
159
+ 'info' => array('*xml' => true, '*html' => true),
160
+ 'tagline' => array('*xml' => true, '*html' => true),
161
+ 'copyright' => array ('*xml' => true, '*html' => true),
162
+ 'generator' => array ('uri' => true, 'url' => true),
163
+ 'uri' => array ('*content' => true),
164
+ 'url' => array ('*content' => true),
165
+ 'icon' => array ('*content' => true),
166
+ 'logo' => array ('*content' => true),
167
+ ),
168
+
169
+ // for inline namespaced XHTML
170
+ 'xhtml' => array (
171
+ 'a' => array ('href' => true),
172
+ 'applet' => array('codebase' => true),
173
+ 'area' => array('href' => true),
174
+ 'blockquote' => array('cite' => true),
175
+ 'body' => array('background' => true),
176
+ 'del' => array('cite' => true),
177
+ 'form' => array('action' => true),
178
+ 'frame' => array('longdesc' => true, 'src' => true),
179
+ 'iframe' => array('longdesc' => true, 'iframe' => true, 'src' => true),
180
+ 'head' => array('profile' => true),
181
+ 'img' => array('longdesc' => true, 'src' => true, 'usemap' => true),
182
+ 'input' => array('src' => true, 'usemap' => true),
183
+ 'ins' => array('cite' => true),
184
+ 'link' => array('href' => true),
185
+ 'object' => array('classid' => true, 'codebase' => true, 'data' => true, 'usemap' => true),
186
+ 'q' => array('cite' => true),
187
+ 'script' => array('src' => true),
188
+ ),
189
+ );
190
+
191
  var $_ATOM_CONTENT_CONSTRUCTS = array(
192
+ 'content', 'summary', 'title', /* common */
193
+ 'info', 'tagline', 'copyright', /* Atom 0.3 */
194
+ 'rights', 'subtitle', /* Atom 1.0 */
195
+ );
196
  var $_XHTML_CONTENT_CONSTRUCTS = array('body', 'div');
197
  var $_KNOWN_ENCODINGS = array('UTF-8', 'US-ASCII', 'ISO-8859-1');
198
 
199
  // parser variables, useless if you're not a parser, treat as private
200
+ var $stack = array('element' => array (), 'xmlns' => array (), 'xml:base' => array ()); // stack of XML data
201
+
202
  var $inchannel = false;
203
  var $initem = false;
204
+
205
  var $incontent = array(); // non-empty if in namespaced XML content field
206
+ var $xml_escape = false; // true when accepting namespaced XML
207
+ var $exclude_top = false; // true when Atom 1.0 type="xhtml"
208
 
209
  var $intextinput = false;
210
  var $inimage = false;
211
+ var $root_namespaces = array();
212
  var $current_namespace = false;
213
+ var $working_namespace_table = array();
214
+
215
  /**
216
  * Set up XML parser, parse source, and return populated RSS object..
217
  *
241
  *
242
  */
243
  function MagpieRSS ($source, $output_encoding='ISO-8859-1',
244
+ $input_encoding=null, $detect_encoding=true, $base_uri=null)
245
  {
246
  # if PHP xml isn't compiled in, die
247
  #
272
  'feed_start_element', 'feed_end_element' );
273
 
274
  xml_set_character_data_handler( $this->parser, 'feed_cdata' );
275
+
276
+ $this->stack['xml:base'] = array($base_uri);
277
+
278
  $status = xml_parse( $this->parser, $source );
279
 
280
  if (! $status ) {
294
  $this->normalize();
295
  }
296
 
297
+ function feed_start_element($p, $element, &$attributes) {
298
+ $el = strtolower($element);
299
+
300
+ $namespaces = end($this->stack['xmlns']);
301
+ $baseuri = end($this->stack['xml:base']);
302
+
303
+ if (isset($attributes['xml:base'])) {
304
+ $baseuri = Relative_URI::resolve($attributes['xml:base'], $baseuri);
305
+ }
306
+ array_push($this->stack['xml:base'], $baseuri);
307
+
308
+ // scan for xml namespace declarations. ugly ugly ugly.
309
+ // theoretically we could use xml_set_start_namespace_decl_handler and
310
+ // xml_set_end_namespace_decl_handler to handle this more elegantly, but
311
+ // support for these is buggy
312
+ foreach ($attributes as $attr => $value) {
313
+ if ( preg_match('/^xmlns(\:([A-Z_a-z].*))?$/', $attr, $match) ) {
314
+ $ns = (isset($match[2]) ? $match[2] : '');
315
+ $namespaces[$ns] = $value;
316
+ }
317
+ }
318
+
319
+ array_push($this->stack['xmlns'], $namespaces);
320
+
321
  // check for a namespace, and split if found
322
+ // Don't munge content tags
323
+ $ns = $this->namespace($element);
324
+ if ( empty($this->incontent) ) {
325
+ $el = strtolower($ns['element']);
326
+ $this->current_namespace = $ns['effective'];
327
+ }
328
+
329
+ $nsc = $ns['canonical']; $nse = $ns['element'];
330
+ if ( isset($this->_XMLBASE_RESOLVE[$nsc][$nse]) ) {
331
+ if (isset($this->_XMLBASE_RESOLVE[$nsc][$nse]['*xml'])) {
332
+ $attributes['xml:base'] = $baseuri;
333
  }
334
+ foreach ($attributes as $key => $value) {
335
+ if (isset($this->_XMLBASE_RESOLVE[$nsc][$nse][strtolower($key)])) {
336
+ $attributes[$key] = Relative_URI::resolve($attributes[$key], $baseuri);
337
+ }
338
  }
339
  }
340
 
341
+ $attrs = array_change_key_case($attributes, CASE_LOWER);
342
+
343
  # if feed type isn't set, then this is first element of feed
344
  # identify feed from root element
345
  #
346
  if (!isset($this->feed_type) ) {
347
  if ( $el == 'rdf' ) {
348
  $this->feed_type = RSS;
349
+ $this->root_namespaces = array('rss', 'rdf');
350
  $this->feed_version = '1.0';
351
  }
352
  elseif ( $el == 'rss' ) {
353
  $this->feed_type = RSS;
354
+ $this->root_namespaces = array('rss');
355
  $this->feed_version = $attrs['version'];
356
  }
357
  elseif ( $el == 'feed' ) {
358
  $this->feed_type = ATOM;
359
+ $this->root_namespaces = array('atom');
360
+ if ($ns['uri'] == 'http://www.w3.org/2005/Atom') { // Atom 1.0
361
+ $this->root_namespaces = array('atom');
362
  $this->feed_version = '1.0';
363
+ }
364
+ else { // Atom 0.3, probably.
365
+ $this->feed_version = $attrs['version'];
366
+ }
367
  $this->inchannel = true;
368
  }
369
  return;
370
  }
371
+
372
  // if we're inside a namespaced content construct, treat tags as text
373
  if ( !empty($this->incontent) )
374
  {
375
+ if ((count($this->incontent) > 1) or !$this->exclude_top) {
376
+ if ($ns['effective']=='xhtml') {
377
+ $tag = $ns['element'];
378
+ }
379
+ else {
380
+ $tag = $element;
381
+ $xmlns = 'xmlns';
382
+ if (strlen($ns['prefix'])>0) {
383
+ $xmlns = $xmlns . ':' . $ns['prefix'];
384
+ }
385
+ $attributes[$xmlns] = $ns['uri']; // make sure it's visible
386
+ }
387
+
388
+ // if tags are inlined, then flatten
389
+ $attrs_str = join(' ',
390
+ array_map(array($this, 'map_attrs'),
391
+ array_keys($attributes),
392
+ array_values($attributes) )
393
+ );
394
+
395
+ if (strlen($attrs_str) > 0) { $attrs_str = ' '.$attrs_str; }
396
+ $this->append_content( "<{$tag}{$attrs_str}>" );
397
+ }
398
+ array_push($this->incontent, $ns); // stack for parsing content XML
399
  }
400
+
401
+ elseif ( $el == 'channel' ) {
 
402
  $this->inchannel = true;
403
  }
404
+
405
  elseif ($el == 'item' or $el == 'entry' )
406
  {
407
  $this->initem = true;
430
 
431
  // set stack[0] to current element
432
  else {
433
+ // Atom support many links per containing element.
434
+ // Magpie treats link elements of type rel='alternate'
435
+ // as being equivalent to RSS's simple link element.
436
+
437
+ $atom_link = false;
438
+ if ($this->feed_type == ATOM and $el == 'link') {
439
+ $atom_link = true;
440
+ if (isset($attrs['rel']) and $attrs['rel'] != 'alternate') {
441
+ $el = $el . "_" . $attrs['rel']; // pseudo-element names for Atom link elements
442
+ }
443
+ }
444
+ # handle atom content constructs
445
+ elseif ( $this->feed_type == ATOM and in_array($el, $this->_ATOM_CONTENT_CONSTRUCTS) )
446
+ {
447
+ // avoid clashing w/ RSS mod_content
448
+ if ($el == 'content' ) {
449
+ $el = 'atom_content';
450
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
451
 
452
+ // assume that everything accepts namespaced XML
453
+ // (that will pass through some non-validating feeds;
454
+ // but so what? this isn't a validating parser)
455
+ $this->incontent = array();
456
+ array_push($this->incontent, $ns); // start a stack
457
 
458
+ $this->xml_escape = $this->accepts_namespaced_xml($attrs);
 
 
 
 
459
 
460
+ if ( isset($attrs['type']) and trim(strtolower($attrs['type']))=='xhtml') {
461
+ $this->exclude_top = true;
462
+ } else {
463
+ $this->exclude_top = false;
464
+ }
465
+ }
466
+ # Handle inline XHTML body elements --CWJ
467
+ elseif ($ns['effective']=='xhtml' and in_array($el, $this->_XHTML_CONTENT_CONSTRUCTS)) {
468
+ $this->current_namespace = 'xhtml';
469
+ $this->incontent = array();
470
+ array_push($this->incontent, $ns); // start a stack
471
+
472
+ $this->xml_escape = true;
473
+ $this->exclude_top = false;
474
+ }
475
+
476
+ array_unshift($this->stack['element'], $el);
477
+ $elpath = join('_', array_reverse($this->stack['element']));
478
+
479
+ $n = $this->element_count($elpath);
480
+ $this->element_count($elpath, $n+1);
481
+
482
+ if ($n > 0) {
483
+ array_shift($this->stack['element']);
484
+ array_unshift($this->stack['element'], $el.'#'.($n+1));
485
+ $elpath = join('_', array_reverse($this->stack['element']));
486
+ }
487
+
488
+ // this makes the baby Jesus cry, but we can't do it in normalize()
489
+ // because we've made the element name for Atom links unpredictable
490
+ // by tacking on the relation to the end. -CWJ
491
+ if ($atom_link and isset($attrs['href'])) {
492
+ $this->append($elpath, $attrs['href']);
493
+ }
494
+
495
+ // add attributes
496
+ if (count($attrs) > 0) {
497
+ $this->append($elpath.'@', join(',', array_keys($attrs)));
498
+ foreach ($attrs as $attr => $value) {
499
+ $this->append($elpath.'@'.$attr, $value);
500
+ }
501
+ }
502
  }
503
  }
 
504
 
 
505
  function feed_cdata ($p, $text) {
 
506
  if ($this->incontent) {
507
+ if ($this->xml_escape) { $text = htmlspecialchars($text, ENT_COMPAT, $this->encoding); }
508
+ $this->append_content( $text );
509
+ } else {
510
+ $current_el = join('_', array_reverse($this->stack['element']));
511
  $this->append($current_el, $text);
512
  }
513
  }
514
 
515
  function feed_end_element ($p, $el) {
516
+ $closer = $this->namespace($el);
517
+
518
+ if ( $this->incontent ) {
519
+ $opener = array_pop($this->incontent);
520
+
521
+ // balance tags properly
522
+ // note: i don't think this is actually neccessary
523
+ if ($opener != $closer) {
524
+ array_push($this->incontent, $opener);
525
+ $this->append_content("<$el />");
526
+ } elseif ($this->incontent) { // are we in the content construct still?
527
+ if ((count($this->incontent) > 1) or !$this->exclude_top) {
528
+ if ($closer['effective']=='xhtml') {
529
+ $tag = $closer['element'];
530
+ }
531
+ else {
532
+ $tag = $el;
533
+ }
534
+ $this->append_content("</$tag>");
535
+ }
536
+ } else { // if we're done with the content construct, shift the opening of the content construct off the normal stack
537
+ array_shift( $this->stack['element'] );
538
+ }
539
+ }
540
+ elseif ($closer['effective'] == '') {
541
+ $el = strtolower($closer['element']);
542
+ if ( $el == 'item' or $el == 'entry' ) {
543
+ $this->items[] = $this->current_item;
544
+ $this->current_item = array();
545
+ $this->initem = false;
546
+ $this->current_category = 0;
547
+ }
548
+ elseif ($this->feed_type == RSS and $el == 'textinput' ) {
549
+ $this->intextinput = false;
550
+ }
551
+ elseif ($this->feed_type == RSS and $el == 'image' ) {
552
+ $this->inimage = false;
553
+ }
554
+ elseif ($el == 'channel' or $el == 'feed' ) {
555
+ $this->inchannel = false;
556
+ } else {
557
+ $nsc = $closer['canonical']; $nse = $closer['element'];
558
+ if (isset($this->_XMLBASE_RESOLVE[$nsc][$nse]['*content'])) {
559
+ // Resolve relative URI in content of tag
560
+ $this->dereference_current_element();
561
+ }
562
+ array_shift( $this->stack['element'] );
563
+ }
564
+ } else {
565
+ $nsc = $closer['canonical']; $nse = strtolower($closer['element']);
566
+ if (isset($this->_XMLBASE_RESOLVE[$nsc][$nse]['*content'])) {
567
+ // Resolve relative URI in content of tag
568
+ $this->dereference_current_element();
569
+ }
570
+ array_shift( $this->stack['element'] );
571
+ }
572
+
573
+ if ( !$this->incontent ) { // Don't munge the namespace after finishing with elements in namespaced content constructs -CWJ
574
+ $this->current_namespace = false;
575
+ }
576
+ array_pop($this->stack['xmlns']);
577
+ array_pop($this->stack['xml:base']);
578
+ }
579
+
580
+ // Namespace handling functions
581
+ function namespace ($element) {
582
+ $namespaces = end($this->stack['xmlns']);
583
+ $ns = '';
584
+ if ( strpos( $element, ':' ) ) {
585
+ list($ns, $element) = split( ':', $element, 2);
586
+ }
587
+
588
+ $uri = (isset($namespaces[$ns]) ? $namespaces[$ns] : null);
589
 
590
+ if (!is_null($uri)) {
591
+ $canonical = (
592
+ isset($this->_XMLNS_FAMILIAR[$uri])
593
+ ? $this->_XMLNS_FAMILIAR[$uri]
594
+ : $uri
595
+ );
596
+ } else {
597
+ $canonical = $ns;
598
+ }
599
 
600
+ if (in_array($canonical, $this->root_namespaces)) {
601
+ $effective = '';
602
+ } else {
603
+ $effective = $canonical;
604
  }
605
 
606
+ return array('effective' => $effective, 'canonical' => $canonical, 'prefix' => $ns, 'uri' => $uri, 'element' => $element);
607
+ }
608
+
609
+ // Utility functions for accessing data structure
610
+
611
+ // for smart, namespace-aware methods...
612
+ function magpie_data ($el, $method, $text = NULL) {
613
+ $ret = NULL;
614
+ if ($el) {
615
+ if (is_array($method)) {
616
+ $el = $this->{$method['key']}($el);
617
+ $method = $method['value'];
618
  }
 
 
 
 
 
 
 
 
 
619
 
620
+ if ( $this->current_namespace ) {
621
+ if ( $this->initem ) {
622
+ $ret = $this->{$method} (
623
+ $this->current_item[ $this->current_namespace ][ $el ],
624
+ $text
625
+ );
626
+ }
627
+ elseif ($this->inchannel) {
628
+ $ret = $this->{$method} (
629
+ $this->channel[ $this->current_namespace][ $el ],
630
+ $text
631
+ );
632
+ }
633
+ elseif ($this->intextinput) {
634
+ $ret = $this->{$method} (
635
+ $this->textinput[ $this->current_namespace][ $el ],
636
+ $text
637
+ );
638
+ }
639
+ elseif ($this->inimage) {
640
+ $ret = $this->{$method} (
641
+ $this->image[ $this->current_namespace ][ $el ], $text );
642
+ }
643
+ }
644
+ else {
645
+ if ( $this->initem ) {
646
+ $ret = $this->{$method} (
647
+ $this->current_item[ $el ], $text);
648
+ }
649
+ elseif ($this->intextinput) {
650
+ $ret = $this->{$method} (
651
+ $this->textinput[ $el ], $text );
652
+ }
653
+ elseif ($this->inimage) {
654
+ $ret = $this->{$method} (
655
+ $this->image[ $el ], $text );
656
+ }
657
+ elseif ($this->inchannel) {
658
+ $ret = $this->{$method} (
659
+ $this->channel[ $el ], $text );
660
+ }
661
+ }
662
+ }
663
+ return $ret;
664
  }
665
+
 
666
  function concat (&$str1, $str2="") {
667
  if (!isset($str1) ) {
668
  $str1="";
669
  }
670
  $str1 .= $str2;
671
  }
672
+
673
+ function retrieve_value (&$el, $text /*ignore*/) {
674
+ return $el;
675
+ }
676
+ function replace_value (&$el, $text) {
677
+ $el = $text;
678
+ }
679
+ function counter_key ($el) {
680
+ return $el.'#';
681
+ }
682
+
683
+
684
  function append_content($text) {
685
+ $construct = reset($this->incontent);
686
+ $ns = $construct['effective'];
687
+
688
+ // Keeping data about parent elements is necessary to
689
+ // properly handle atom:source and its children elements
690
+ $tag = join('_', array_reverse($this->stack['element']));
691
+
692
+ if ( $this->initem ) {
693
+ if ($ns) {
694
+ $this->concat( $this->current_item[$ns][$tag], $text );
695
  } else {
696
+ $this->concat( $this->current_item[$tag], $text );
697
  }
698
+ }
699
+ elseif ( $this->inchannel ) {
700
  if ($this->current_namespace) {
701
+ $this->concat( $this->channel[$ns][$tag], $text );
702
  } else {
703
+ $this->concat( $this->channel[$tag], $text );
704
  }
705
+ }
706
  }
707
 
708
  // smart append - field and namespace aware
709
  function append($el, $text) {
710
+ $this->magpie_data($el, 'concat', $text);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
711
  }
712
 
713
+ function dereference_current_element () {
714
+ $el = join('_', array_reverse($this->stack['element']));
715
+ $base = end($this->stack['xml:base']);
716
+ $uri = $this->magpie_data($el, 'retrieve_value');
717
+ $this->magpie_data($el, 'replace_value', Relative_URI::resolve($uri, $base));
718
+ }
719
+
720
  // smart count - field and namespace aware
721
  function element_count ($el, $set = NULL) {
722
+ if (!is_null($set)) {
723
+ $ret = $this->magpie_data($el, array('key' => 'counter_key', 'value' => 'replace_value'), $set);
724
+ }
725
+ $ret = $this->magpie_data($el, array('key' => 'counter_key', 'value' => 'retrieve_value'));
726
+ return ($ret ? $ret : 0);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
727
  }
728
 
729
  function normalize_enclosure (&$source, $from, &$dest, $to, $i) {
730
+ $id_from = $this->element_id($from, $i);
731
+ $id_to = $this->element_id($to, $i);
732
+ if (isset($source["{$id_from}@"])) {
733
+ foreach (explode(',', $source["{$id_from}@"]) as $attr) {
734
+ if ($from=='link_enclosure' and $attr=='href') { // from Atom
735
+ $dest["{$id_to}@url"] = $source["{$id_from}@{$attr}"];
736
+ $dest["{$id_to}"] = $source["{$id_from}@{$attr}"];
737
+ }
738
+ elseif ($from=='enclosure' and $attr=='url') { // from RSS
739
+ $dest["{$id_to}@href"] = $source["{$id_from}@{$attr}"];
740
+ $dest["{$id_to}"] = $source["{$id_from}@{$attr}"];
741
+ }
742
+ else {
743
+ $dest["{$id_to}@{$attr}"] = $source["{$id_from}@{$attr}"];
744
+ }
745
+ }
746
+ }
747
  }
748
 
749
  function normalize_atom_person (&$source, $person, &$dest, $to, $i) {
750
+ $id = $this->element_id($person, $i);
751
+ $id_to = $this->element_id($to, $i);
752
 
753
+ // Atom 0.3 <=> Atom 1.0
754
+ if ($this->feed_version >= 1.0) { $used = 'uri'; $norm = 'url'; }
755
+ else { $used = 'url'; $norm = 'uri'; }
756
 
757
+ if (isset($source["{$id}_{$used}"])) {
758
+ $dest["{$id_to}_{$norm}"] = $source["{$id}_{$used}"];
759
+ }
760
 
761
+ // Atom to RSS 2.0 and Dublin Core
762
+ // RSS 2.0 person strings should be valid e-mail addresses if possible.
763
+ if (isset($source["{$id}_email"])) {
764
+ $rss_author = $source["{$id}_email"];
765
+ }
766
+ if (isset($source["{$id}_name"])) {
767
+ $rss_author = $source["{$id}_name"]
768
+ . (isset($rss_author) ? " <$rss_author>" : '');
769
+ }
770
+ if (isset($rss_author)) {
771
+ $source[$id] = $rss_author; // goes to top-level author or contributor
772
+ $dest[$id_to] = $rss_author; // goes to dc:creator or dc:contributor
773
+ }
774
  }
775
 
776
  // Normalize Atom 1.0 and RSS 2.0 categories to Dublin Core...
777
  function normalize_category (&$source, $from, &$dest, $to, $i) {
778
+ $cat_id = $this->element_id($from, $i);
779
+ $dc_id = $this->element_id($to, $i);
 
 
 
 
 
 
 
 
 
 
 
 
 
780
 
781
+ // first normalize category elements: Atom 1.0 <=> RSS 2.0
782
+ if ( isset($source["{$cat_id}@term"]) ) { // category identifier
783
+ $source[$cat_id] = $source["{$cat_id}@term"];
784
+ } elseif ( $this->feed_type == RSS ) {
785
+ $source["{$cat_id}@term"] = $source[$cat_id];
786
+ }
787
+
788
+ if ( isset($source["{$cat_id}@scheme"]) ) { // URI to taxonomy
789
+ $source["{$cat_id}@domain"] = $source["{$cat_id}@scheme"];
790
+ } elseif ( isset($source["{$cat_id}@domain"]) ) {
791
+ $source["{$cat_id}@scheme"] = $source["{$cat_id}@domain"];
792
+ }
793
+
794
+ // Now put the identifier into dc:subject
795
+ $dest[$dc_id] = $source[$cat_id];
796
  }
797
 
798
  // ... or vice versa
799
  function normalize_dc_subject (&$source, $from, &$dest, $to, $i) {
800
+ $dc_id = $this->element_id($from, $i);
801
+ $cat_id = $this->element_id($to, $i);
802
 
803
+ $dest[$cat_id] = $source[$dc_id]; // RSS 2.0
804
+ $dest["{$cat_id}@term"] = $source[$dc_id]; // Atom 1.0
805
  }
806
 
807
  // simplify the logic for normalize(). Makes sure that count of elements and
809
  // with things like attributes or change formats or the like, pass it a
810
  // callback to handle each element.
811
  function normalize_element (&$source, $from, &$dest, $to, $via = NULL) {
812
+ if (isset($source[$from]) or isset($source["{$from}#"])) {
813
+ if (isset($source["{$from}#"])) {
814
+ $n = $source["{$from}#"];
815
+ $dest["{$to}#"] = $source["{$from}#"];
816
+ }
817
+ else { $n = 1; }
818
 
819
+ for ($i = 1; $i <= $n; $i++) {
820
+ if (isset($via)) { // custom callback for ninja attacks
821
+ $this->{$via}($source, $from, $dest, $to, $i);
822
+ }
823
+ else { // just make it the same
824
+ $from_id = $this->element_id($from, $i);
825
+ $to_id = $this->element_id($to, $i);
826
+ $dest[$to_id] = $source[$from_id];
827
+ }
828
+ }
829
+ }
830
  }
831
 
832
  function normalize () {
833
  // if atom populate rss fields and normalize 0.3 and 1.0 feeds
834
  if ( $this->is_atom() ) {
835
+ // Atom 1.0 elements <=> Atom 0.3 elements (Thanks, o brilliant wordsmiths of the Atom 1.0 standard!)
836
+ if ($this->feed_version < 1.0) {
837
+ $this->normalize_element($this->channel, 'tagline', $this->channel, 'subtitle');
838
+ $this->normalize_element($this->channel, 'copyright', $this->channel, 'rights');
839
+ $this->normalize_element($this->channel, 'modified', $this->channel, 'updated');
840
+ } else {
841
+ $this->normalize_element($this->channel, 'subtitle', $this->channel, 'tagline');
842
+ $this->normalize_element($this->channel, 'rights', $this->channel, 'copyright');
843
+ $this->normalize_element($this->channel, 'updated', $this->channel, 'modified');
844
+ }
845
+ $this->normalize_element($this->channel, 'author', $this->channel['dc'], 'creator', 'normalize_atom_person');
846
+ $this->normalize_element($this->channel, 'contributor', $this->channel['dc'], 'contributor', 'normalize_atom_person');
 
 
 
 
 
 
 
 
 
 
 
 
847
 
848
+ // Atom elements to RSS elements
849
+ $this->normalize_element($this->channel, 'subtitle', $this->channel, 'description');
850
+
851
+ if ( isset($this->channel['logo']) ) {
852
+ $this->normalize_element($this->channel, 'logo', $this->image, 'url');
853
+ $this->normalize_element($this->channel, 'link', $this->image, 'link');
854
+ $this->normalize_element($this->channel, 'title', $this->image, 'title');
855
+ }
856
+
857
+ for ( $i = 0; $i < count($this->items); $i++) {
858
+ $item = $this->items[$i];
859
+
860
+ // Atom 1.0 elements <=> Atom 0.3 elements
861
+ if ($this->feed_version < 1.0) {
862
+ $this->normalize_element($item, 'modified', $item, 'updated');
863
+ $this->normalize_element($item, 'issued', $item, 'published');
864
+ } else {
865
+ $this->normalize_element($item, 'updated', $item, 'modified');
866
+ $this->normalize_element($item, 'published', $item, 'issued');
867
+ }
868
 
869
+ // "If an atom:entry element does not contain
870
+ // atom:author elements, then the atom:author elements
871
+ // of the contained atom:source element are considered
872
+ // to apply. In an Atom Feed Document, the atom:author
873
+ // elements of the containing atom:feed element are
874
+ // considered to apply to the entry if there are no
875
+ // atom:author elements in the locations described
876
+ // above." <http://atompub.org/2005/08/17/draft-ietf-atompub-format-11.html#rfc.section.4.2.1>
877
+ if (!isset($item["author#"])) {
878
+ if (isset($item["source_author#"])) { // from aggregation source
879
+ $source = $item;
880
+ $author = "source_author";
881
+ } elseif (isset($this->channel["author#"])) { // from containing feed
882
+ $source = $this->channel;
883
+ $author = "author";
884
+ } else {
885
+ $author = null;
886
+ }
887
 
888
+ if (!is_null($author)) {
889
+ $item["author#"] = $source["{$author}#"];
890
+ for ($au = 1; $au <= $item["author#"]; $au++) {
891
+ $id_to = $this->element_id('author', $au);
892
+ $id_from = $this->element_id($author, $au);
893
+
894
+ $item[$id_to] = $source[$id_from];
895
+ foreach (array('name', 'email', 'uri', 'url') as $what) {
896
+ if (isset($source["{$id_from}_{$what}"])) {
897
+ $item["{$id_to}_{$what}"] = $source["{$id_from}_{$what}"];
 
898
  }
899
+ }
900
  }
901
+ }
902
+ }
903
 
904
+ // Atom elements to RSS elements
905
+ $this->normalize_element($item, 'author', $item['dc'], 'creator', 'normalize_atom_person');
906
+ $this->normalize_element($item, 'contributor', $item['dc'], 'contributor', 'normalize_atom_person');
907
+ $this->normalize_element($item, 'summary', $item, 'description');
908
+ $this->normalize_element($item, 'atom_content', $item['content'], 'encoded');
909
+ $this->normalize_element($item, 'link_enclosure', $item, 'enclosure', 'normalize_enclosure');
910
 
911
+ // Categories
912
+ if ( isset($item['category#']) ) { // Atom 1.0 categories to dc:subject and RSS 2.0 categories
913
+ $this->normalize_element($item, 'category', $item['dc'], 'subject', 'normalize_category');
914
+ }
915
+ elseif ( isset($item['dc']['subject#']) ) { // dc:subject to Atom 1.0 and RSS 2.0 categories
916
+ $this->normalize_element($item['dc'], 'subject', $item, 'category', 'normalize_dc_subject');
917
+ }
918
 
919
+ // Normalized item timestamp
920
+ $atom_date = (isset($item['published']) ) ? $item['published'] : $item['updated'];
921
+ if ( $atom_date ) {
922
+ $epoch = @parse_w3cdtf($atom_date);
923
+ if ($epoch and $epoch > 0) {
924
+ $item['date_timestamp'] = $epoch;
925
+ }
926
+ }
927
 
928
+ $this->items[$i] = $item;
929
+ }
930
  }
931
  elseif ( $this->is_rss() ) {
932
  // RSS elements to Atom elements
934
  $this->normalize_element($this->channel, 'description', $this->channel, 'subtitle'); // Atom 1.0 (yay wordsmithing!)
935
  $this->normalize_element($this->image, 'url', $this->channel, 'logo');
936
 
937
+ for ( $i = 0; $i < count($this->items); $i++) {
938
+ $item = $this->items[$i];
939
+
940
+ // RSS elements to Atom elements
941
+ $this->normalize_element($item, 'description', $item, 'summary');
942
+ $this->normalize_element($item, 'enclosure', $item, 'link_enclosure', 'normalize_enclosure');
943
+
944
+ // Categories
945
+ if ( isset($item['category#']) ) { // RSS 2.0 categories to dc:subject and Atom 1.0 categories
946
+ $this->normalize_element($item, 'category', $item['dc'], 'subject', 'normalize_category');
947
+ }
948
+ elseif ( isset($item['dc']['subject#']) ) { // dc:subject to Atom 1.0 and RSS 2.0 categories
949
+ $this->normalize_element($item['dc'], 'subject', $item, 'category', 'normalize_dc_subject');
950
+ }
951
 
952
+ // Normalized item timestamp
953
+ if ( $this->is_rss() == '1.0' and isset($item['dc']['date']) ) {
954
+ $epoch = @parse_w3cdtf($item['dc']['date']);
955
+ if ($epoch and $epoch > 0) {
956
+ $item['date_timestamp'] = $epoch;
957
+ }
958
+ }
959
+ elseif ( isset($item['pubdate']) ) {
960
+ $epoch = @strtotime($item['pubdate']);
961
+ if ($epoch > 0) {
962
+ $item['date_timestamp'] = $epoch;
963
+ }
964
+ }
965
+
966
+ $this->items[$i] = $item;
967
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
968
  }
969
  }
970
 
1002
  $this->encoding = $out_enc;
1003
  xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $out_enc);
1004
  }
1005
+ xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, false);
1006
  return array($parser, $source);
1007
  }
1008
 
1122
  // magic ID function for multiple elemenets.
1123
  // can be called as static MagpieRSS::element_id()
1124
  function element_id ($el, $counter) {
1125
+ return $el . (($counter > 1) ? '#'.$counter : '');
1126
  }
1127
+
1128
+ function map_attrs($k, $v) {
1129
+ return $k.'="'.htmlspecialchars($v, ENT_COMPAT, $this->encoding).'"';
1130
+ }
1131
+
1132
+ function accepts_namespaced_xml ($attrs) {
1133
+ $mode = (isset($attrs['mode']) ? trim(strtolower($attrs['mode'])) : 'xml');
1134
+ $type = (isset($attrs['type']) ? trim(strtolower($attrs['type'])) : null);
1135
+ if ($this->feed_type == ATOM and $this->feed_version < 1.0) {
1136
+ if ($mode=='xml' and preg_match(':[/+](html|xml)$:i', $type)) {
1137
+ $ret = true;
1138
+ } else {
1139
+ $ret = false;
1140
+ }
1141
+ } elseif ($this->feed_type == ATOM and $this->feed_version >= 1.0) {
1142
+ if ($type=='xhtml' or preg_match(':[/+]xml$:i', $type)) {
1143
+ $ret = true;
1144
+ } else {
1145
+ $ret = false;
1146
+ }
1147
+ } else {
1148
+ $ret = false; // Don't munge unless you're sure
1149
+ }
1150
+ return $ret;
1151
+ }
1152
  } // end class RSS
1153
 
 
 
 
1154
 
1155
  // patch to support medieval versions of PHP4.1.x,
1156
  // courtesy, Ryan Currie, ryan@digibliss.com
1157
 
1158
  if (!function_exists('array_change_key_case')) {
1159
+ define("CASE_UPPER",1);
1160
+ define("CASE_LOWER",0);
1161
 
1162
 
1163
+ function array_change_key_case($array,$case=CASE_LOWER) {
1164
  if ($case==CASE_LOWER) $cmd='strtolower';
1165
  elseif ($case==CASE_UPPER) $cmd='strtoupper';
1166
  foreach($array as $key=>$value) {
1167
  $output[$cmd($key)]=$value;
1168
  }
1169
  return $output;
1170
+ }
1171
 
1172
  }
1173
 
1201
  version will be return, if it exists (and if MAGPIE_CACHE_FRESH_ONLY is off)
1202
  \*=======================================================================*/
1203
 
1204
+ define('MAGPIE_VERSION', '0.85');
1205
 
1206
  $MAGPIE_ERROR = "";
1207
 
1219
  // fetch file, and parse it
1220
  $resp = _fetch_remote_file( $url );
1221
  if ( is_success( $resp->status ) ) {
1222
+ return _response_to_rss( $resp, $url );
1223
  }
1224
  else {
1225
  error("Failed to fetch $url and cache is off");
1292
  return $rss;
1293
  }
1294
  elseif ( is_success( $resp->status ) ) {
1295
+ $rss = _response_to_rss( $resp, $url );
1296
  if ( $rss ) {
1297
  if (MAGPIE_DEBUG > 1) {
1298
  debug("Fetch successful");
1405
  Input: an HTTP response object (see Snoopy)
1406
  Output: parsed RSS object (see rss_parse)
1407
  \*=======================================================================*/
1408
+ function _response_to_rss ($resp, $url = null) {
1409
+ $rss = new MagpieRSS( $resp->results, MAGPIE_OUTPUT_ENCODING, MAGPIE_INPUT_ENCODING, MAGPIE_DETECT_ENCODING, $url );
1410
 
1411
  // if RSS parsed successfully
1412
  if ( $rss and !$rss->ERROR) {
1413
+ $rss->http_status = $resp->status;
1414
+
1415
  // find Etag, and Last-Modified
1416
  foreach($resp->headers as $h) {
1417
  // 2003-03-02 - Nicola Asuni (www.tecnick.com) - fixed bug "Undefined offset: 1"
1423
  $val = "";
1424
  }
1425
 
1426
+ $rss->header[$field] = $val;
1427
+
1428
  if ( $field == 'ETag' ) {
1429
  $rss->etag = $val;
1430
  }
1783
  }
1784
  }
1785
 
1786
+ # Relative URI static class: PHP class for resolving relative URLs
1787
+ #
1788
+ # This class is derived (under the terms of the GPL) from URL Class 0.3 by
1789
+ # Keyvan Minoukadeh <keyvan@k1m.com>, which is great but more than we need
1790
+ # for MagpieRSS's purposes. The class has been stripped down to a single
1791
+ # public method: Relative_URI::resolve($url, $base), which resolves the URI in
1792
+ # $url relative to the URI in $base
1793
+ #
1794
+ # FeedWordPress also uses this class. So if we have it loaded in, don't load it
1795
+ # again.
1796
+ #
1797
+ # -- Charles Johnson <technophilia@radgeek.com>
1798
+ if (!class_exists('Relative_URI')) {
1799
+ class Relative_URI
1800
+ {
1801
+ // Resolve relative URI in $url against the base URI in $base. If $base
1802
+ // is not supplied, then we use the REQUEST_URI of this script.
1803
+ //
1804
+ // I'm hoping this method reflects RFC 2396 Section 5.2
1805
+ function resolve ($url, $base = NULL)
1806
+ {
1807
+ if (is_null($base)):
1808
+ $base = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
1809
+ endif;
1810
+
1811
+ $base = Relative_URI::_encode(trim($base));
1812
+ $uri_parts = Relative_URI::_parse_url($base);
1813
+
1814
+ $url = Relative_URI::_encode(trim($url));
1815
+ $parts = Relative_URI::_parse_url($url);
1816
+
1817
+ $uri_parts['fragment'] = (isset($parts['fragment']) ? $parts['fragment'] : null);
1818
+ $uri_parts['query'] = (isset($parts['query']) ? $parts['query'] : null);
1819
+
1820
+ // if path is empty, and scheme, host, and query are undefined,
1821
+ // the URL is referring the base URL
1822
+
1823
+ if (($parts['path'] == '') && !isset($parts['scheme']) && !isset($parts['host']) && !isset($parts['query'])) {
1824
+ // If the URI is empty or only a fragment, return the base URI
1825
+ return $base . (isset($parts['fragment']) ? '#'.$parts['fragment'] : '');
1826
+ } elseif (isset($parts['scheme'])) {
1827
+ // If the scheme is set, then the URI is absolute.
1828
+ return $url;
1829
+ } elseif (isset($parts['host'])) {
1830
+ $uri_parts['host'] = $parts['host'];
1831
+ $uri_parts['path'] = $parts['path'];
1832
+ } else {
1833
+ // We have a relative path but not a host.
1834
+
1835
+ // start ugly fix:
1836
+ // prepend slash to path if base host is set, base path is not set, and url path is not absolute
1837
+ if ($uri_parts['host'] && ($uri_parts['path'] == '')
1838
+ && (strlen($parts['path']) > 0)
1839
+ && (substr($parts['path'], 0, 1) != '/')) {
1840
+ $parts['path'] = '/'.$parts['path'];
1841
+ } // end ugly fix
1842
+
1843
+ if (substr($parts['path'], 0, 1) == '/') {
1844
+ $uri_parts['path'] = $parts['path'];
1845
+ } else {
1846
+ // copy base path excluding any characters after the last (right-most) slash character
1847
+ $buffer = substr($uri_parts['path'], 0, (int)strrpos($uri_parts['path'], '/')+1);
1848
+ // append relative path
1849
+ $buffer .= $parts['path'];
1850
+ // remove "./" where "." is a complete path segment.
1851
+ $buffer = str_replace('/./', '/', $buffer);
1852
+ if (substr($buffer, 0, 2) == './') {
1853
+ $buffer = substr($buffer, 2);
1854
+ }
1855
+ // if buffer ends with "." as a complete path segment, remove it
1856
+ if (substr($buffer, -2) == '/.') {
1857
+ $buffer = substr($buffer, 0, -1);
1858
+ }
1859
+ // remove "<segment>/../" where <segment> is a complete path segment not equal to ".."
1860
+ $search_finished = false;
1861
+ $segment = explode('/', $buffer);
1862
+ while (!$search_finished) {
1863
+ for ($x=0; $x+1 < count($segment);) {
1864
+ if (($segment[$x] != '') && ($segment[$x] != '..') && ($segment[$x+1] == '..')) {
1865
+ if ($x+2 == count($segment)) $segment[] = '';
1866
+ unset($segment[$x], $segment[$x+1]);
1867
+ $segment = array_values($segment);
1868
+ continue 2;
1869
+ } else {
1870
+ $x++;
1871
+ }
1872
+ }
1873
+ $search_finished = true;
1874
+ }
1875
+ $buffer = (count($segment) == 1) ? '/' : implode('/', $segment);
1876
+ $uri_parts['path'] = $buffer;
1877
+
1878
+ }
1879
+ }
1880
+
1881
+ // If we've gotten to this point, we can try to put the pieces
1882
+ // back together.
1883
+ $ret = '';
1884
+ if (isset($uri_parts['scheme'])) $ret .= $uri_parts['scheme'].':';
1885
+ if (isset($uri_parts['user'])) {
1886
+ $ret .= $uri_parts['user'];
1887
+ if (isset($uri_parts['pass'])) $ret .= ':'.$uri_parts['parts'];
1888
+ $ret .= '@';
1889
+ }
1890
+ if (isset($uri_parts['host'])) {
1891
+ $ret .= '//'.$uri_parts['host'];
1892
+ if (isset($uri_parts['port'])) $ret .= ':'.$uri_parts['port'];
1893
+ }
1894
+ $ret .= $uri_parts['path'];
1895
+ if (isset($uri_parts['query'])) $ret .= '?'.$uri_parts['query'];
1896
+ if (isset($uri_parts['fragment'])) $ret .= '#'.$uri_parts['fragment'];
1897
+
1898
+ return $ret;
1899
+ }
1900
+
1901
+ /**
1902
+ * Parse URL
1903
+ *
1904
+ * Regular expression grabbed from RFC 2396 Appendix B.
1905
+ * This is a replacement for PHPs builtin parse_url().
1906
+ * @param string $url
1907
+ * @access private
1908
+ * @return array
1909
+ */
1910
+ function _parse_url($url)
1911
+ {
1912
+ // I'm using this pattern instead of parse_url() as there's a few strings where parse_url()
1913
+ // generates a warning.
1914
+ if (preg_match('!^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?!', $url, $match)) {
1915
+ $parts = array();
1916
+ if ($match[1] != '') $parts['scheme'] = $match[2];
1917
+ if ($match[3] != '') $parts['auth'] = $match[4];
1918
+ // parse auth
1919
+ if (isset($parts['auth'])) {
1920
+ // store user info
1921
+ if (($at_pos = strpos($parts['auth'], '@')) !== false) {
1922
+ $userinfo = explode(':', substr($parts['auth'], 0, $at_pos), 2);
1923
+ $parts['user'] = $userinfo[0];
1924
+ if (isset($userinfo[1])) $parts['pass'] = $userinfo[1];
1925
+ $parts['auth'] = substr($parts['auth'], $at_pos+1);
1926
+ }
1927
+ // get port number
1928
+ if ($port_pos = strrpos($parts['auth'], ':')) {
1929
+ $parts['host'] = substr($parts['auth'], 0, $port_pos);
1930
+ $parts['port'] = (int)substr($parts['auth'], $port_pos+1);
1931
+ if ($parts['port'] < 1) $parts['port'] = null;
1932
+ } else {
1933
+ $parts['host'] = $parts['auth'];
1934
+ }
1935
+ }
1936
+ unset($parts['auth']);
1937
+ $parts['path'] = $match[5];
1938
+ if (isset($match[6]) && ($match[6] != '')) $parts['query'] = $match[7];
1939
+ if (isset($match[8]) && ($match[8] != '')) $parts['fragment'] = $match[9];
1940
+ return $parts;
1941
+ }
1942
+ // shouldn't reach here
1943
+ return array('path'=>'');
1944
+ }
1945
+
1946
+ function _encode($string)
1947
+ {
1948
+ static $replace = array();
1949
+ if (!count($replace)) {
1950
+ $find = array(32, 34, 60, 62, 123, 124, 125, 91, 92, 93, 94, 96, 127);
1951
+ $find = array_merge(range(0, 31), $find);
1952
+ $find = array_map('chr', $find);
1953
+ foreach ($find as $char) {
1954
+ $replace[$char] = '%'.bin2hex($char);
1955
+ }
1956
+ }
1957
+ // escape control characters and a few other characters
1958
+ $encoded = strtr($string, $replace);
1959
+ // remove any character outside the hex range: 21 - 7E (see www.asciitable.com)
1960
+ return preg_replace('/[^\x21-\x7e]/', '', $encoded);
1961
+ }
1962
+ } // class Relative_URI
1963
+ }
1964
+
1965
  ################################################################################
1966
  ## WordPress: wp_rss(), get_rss() ##############################################
1967
  ################################################################################
README.text CHANGED
@@ -2,7 +2,7 @@ FeedWordPress
2
  =============
3
 
4
  * Author: [Charles Johnson](http://radgeek.com/contact)
5
- * Version: 0.981
6
  * Project URI: <http://projects.radgeek.com/feedwordpress>
7
  * License: GPL 2. See License below for copyright jots and tittles.
8
 
@@ -20,76 +20,86 @@ originally developed it because I needed a more flexible replacement for
20
 
21
  FeedWordPress is designed with flexibility, ease of use, and ease of
22
  configuration in mind. You'll need a working installation of WordPress (version
23
- [2.1][], [2.0][] or [1.5][]), and also FTP or SFTP access to your web host. The
24
- ability to create cron jobs on your web host would be very helpful but it's not
25
- absolutely necessary. You *don't* need to tweak any plain-text configuration
26
- files and you *don't* need shell access to your web host to make it work.
27
- (Although, I should point out, web hosts that *don't* offer shell access are
28
- *bad web hosts*.)
29
-
30
- [2.1]: http://wordpress.org/development/2007/01/ella-21/
31
- [2.0]: http://wordpress.org/development/2005/12/wp2/
32
- [1.5]: http://wordpress.org/development/2005/02/strayhorn/
 
 
33
 
34
  Installation
35
  ------------
36
  ### Requirements ###
37
 
38
- To use version 0.981 of FeedWordPress, you will need:
39
 
40
- 1. an installed and configured copy of WordPress 2.1, 2.0.x, or 1.5.x.
41
- (FeedWordPress currently *will not work* with older versions of
42
- WordPress, or with WordPress MU.)
43
 
44
  2. FTP or SFTP access to your web host
45
 
46
- And you'll probably also want to have either:
47
-
48
- 1. the ability to create cron jobs on your web host, or at least
49
-
50
- 2. a computer of your own and always-on Internet access
51
-
52
  ### Installation ###
53
 
54
  #### Upgrades ####
55
 
56
- To *upgrade* an existing installation of FeedWordPress to version 0.981:
57
 
58
  1. Download the FeedWordPress archive in zip or gzipped tar format and
59
- extract the files on your computer. Replace your existing FeedWordPress
60
- files with the new files. Be sure to upgrade `rss.php` and
61
- `rss-functions.php` if you use the optional MagpieRSS upgrade, or don't
62
- use it yet but do want to syndicate Atom 1.0 feeds.
63
-
64
- 2. If you are upgrading from version 0.96 or earlier, **immediately** log
 
 
 
 
 
 
 
 
 
 
 
65
  in to the WordPress Dashboard, and go to Options --> Syndicated. Follow
66
  the directions to launch the database upgrade procedure. The new
67
  versions of FeedWordPress incorporate some long-needed improvements, but
68
  old meta-data needs to be updated to prevent duplicate posts and other
69
  possible maladies. If you're upgrading an existing installation, updates
70
  and FeedWordPress template functions *will not work* until you've done
71
- the upgrade.
 
 
72
 
73
- 3. Take a coffee break while the upgrade runs. It should, hopefully, finish
74
- within a few minutes even on relatively large databases.
75
-
76
- 4. `update-feeds.php` has been overhauled to improve performance and ease
77
- of use, and also to make errors easier to detect and eliminate. The
78
- overhaul doesn't require any changes to your set up *if* you used
79
- XML-RPC pings, or command-line PHP, to do scheduled updates. It *does*
80
- affect you if you used curl or some other tool to send HTTP requests to
81
- `update-feeds.php`: your old cron job will probably not work anymore.
82
- See [Setting Up Feed Updates][] below to get scheduled updates back on
83
- track.
84
 
85
  5. Enjoy your new installation of FeedWordPress.
86
 
87
  #### New Installations ####
88
 
89
- 1. Install `feedwordpress.php` in your WordPress `plugins` directory
90
- and `update-feeds.php` in your WordPress `wp-content` directory.
91
-
92
- 2. (Optional) Upgrade the copy of MagpieRSS packaged with WordPress by
 
 
 
 
 
 
93
  installing the new `rss.php` and `rss-functions.php` (archived in
94
  `OPTIONAL/wp-includes`) into your WordPress `wp-includes` directory.
95
  Upgrading MagpieRSS is necessary if you want to take advantage of
@@ -101,107 +111,91 @@ To *upgrade* an existing installation of FeedWordPress to version 0.981:
101
  your installation of PHP is up-to-date and that you keep a copy of
102
  the old MagpieRSS around to compare results.)
103
 
104
- 3. Log in to the WordPress Dashboard and activate the FeedWordPress
105
  plugin.
106
 
107
- 4. While you're at the Dashboard, once the plugin is activated, you can
108
- go to Options --> Syndication and set (1) the link category that
109
  FeedWordPress will syndicate links from (by default, "Contributors"),
110
- and (2) a "secret word" for your RPC-XML updating interface. This
111
- provides some light security by keeping passing ruffians from saying
112
- "Update all the feeds" at will to your FeedWordPress installation.
113
 
114
- 5. Go to Blogroll --> Syndicated or Links --> Syndicated to set up the list
115
- of sites that you want FeedWordPress to syndicate onto your blog. (If
116
- you have the feeds you want to aggregate in a service such as Bloglines,
117
- you may prefer to export them to an OPML file and use WordPress's Links
118
- --> Import to import them into the contributors category.)
 
119
 
120
  #### Setting Up Feed Updates ####
121
 
122
- FeedWordPress is now ready to accept posts from its syndication sources.
123
- Unfortunately, it doesn't yet know *when* to go get them. (**This may be true
124
- even if you are upgrading an existing installation of FeedWordPress:** your old
125
- cron job will still work if you used command-line PHP or blogging software pings
126
- to do updates, but it will need to be fixed if you used curl or another tool to
127
- send HTTP requests to `update-feeds.php`.)
128
-
129
- You can load in syndicated posts for the first time by pointing your web browser
130
- to `update-feeds.php`. If you have WordPress installed at, say,
131
- <http://www.zyx.com/blog> then you should point your browser to
132
- <http://www.zyx.com/blog/wp-content/update-feeds.php> and log in as any user in
133
- the user database. (You may want to create a new "dummy" user for doing
134
- scheduled updates, using **Users --> Authors & Users --> Add New User**. Tell
135
- FeedWordPress to update all feeds, and you'll get the first wave of posts
136
- imported into the database.
137
-
138
- Congratulations! You should now have an aggregator site full of delicious
139
- syndicated content hot off of the newswires. Now you just need a way to *keep*
140
- the content freshly updated. Unless you enjoy manually browsing to
141
- `update-feeds.php` every hour on the hour, you'll probably want to do this by
142
- setting up your site for automated updates.
143
-
144
- You can pull that off in one of two ways, or by a mixture of both:
145
-
146
- 1. **The Blogging Software Ping Method:** You can get all of your
147
- contributors to add you to the list of URIs that they notify of updates:
148
- while FeedWordPress is activated, it will accept XML-RPC "recently
149
- updated" pings in the standard format accepted by Weblogs.com,
150
- Ping-O-Matic, Technorati, and other services. Most blogging software
151
- allows users to add a URI to the list of URIs that get pinged on each
152
- update. (See, for example, Options --> Writing --> Update Services in
153
- WordPress, or Configuration --> Preferences --> Publicity / Remote
154
- Interfaces / TrackBack in Movable Type.)
155
-
156
- If you can get a contributor to add your XML-RPC URI to her
157
- services-to-ping list (if you have WordPress installed at
158
- <http://www.zyx.com/blog>, say, the URI to add should be
159
- <http://www.zyx.com/blog/xmlrpc.php>), then whenever she updates her
160
- blog, her blogging software will ping your FeedWordPress installation,
161
- and FeedWordPress will look up her feed to grab the new posts off of
162
- it.
163
-
164
- 2. **The Scheduled Update Job Method:** You may very well not be able to
165
- get all your contributors to add your site to their blogging software's
166
- ping list, and even if you do you may want to have a back-up option to
167
- catch updates later even if the ping fails to go through on one
168
- particular occasion. You'll need to create a scheduled job to
169
- periodically check for updates on *all* the feeds. You'll need either
170
- (a) the ability to create cron jobs on your web host or (b) access to
171
- another computer with a reliable, always-on Internet connection.
172
-
173
- If you *can* create a crontab on your web host, then the best thing to
174
- do is to create a cron job that will run update-feeds.php through the
175
- PHP command-line interface. For example, if you have WordPress installed
176
- in `~/www/wp` (where ~ is your home directory), you might insert the
177
- following line into your crontab:
178
-
179
- 25 * * * * cd $HOME/www/wp/wp-content ; php -q update-feeds.php
180
-
181
- If you *don't* have access to (a), you can still save the day using
182
- another computer with always-on Internet access that sends a POST
183
- request to the `update-feeds.php` URI on a regular schedule. So, for
184
- example, if you have WordPress installed at <http://www.zyx.com/blog>,
185
- and you have a dummy user in your WordPress database with the login name
186
- 'login' and the password 'pass', then you could add the following line
187
- to the crontab on a home Linux box:
188
-
189
- 25 * * * * curl --user login:pass http://www.zyx.com/blog/wp-content/update-feeds.php -d update=quiet
190
-
191
- The `-d update=quiet` switch ensures that (1) `update-feeds.php` will
192
- receive an HTTP POST request rather than an HTTP GET request (which
193
- is important, since it won't take any actions with side-effects -- such
194
- as checking for new posts -- unless it receives an HTTP POST); it also
195
- tells it to suppress the HTML output that it would generate for normal
196
- web browsers, and only to output text if it encounters errors (this will
197
- keep the number of e-mails you receive from the Cron Daemon to a
198
- minimum).
199
-
200
- If you are using Windows XP and have a version of curl (such as the
201
- version included in [Cygwin][]), you can create a Scheduled Task to
202
- similar effect.
203
-
204
- [Cygwin]: http://www.cygwin.com/
205
 
206
  Basic Concepts
207
  --------------
@@ -485,8 +479,8 @@ For more information, see <http://projects.radgeek.com/feedwordpress/api>.
485
 
486
  License
487
  -------
488
- The FeedWordPress plugin is copyright (c) 2005 by Charles Johnson. It uses code
489
- derived or translated from:
490
 
491
  - [wp-rss-aggregate.php][] by [Kellan Elliot-McCrea](kellan@protest.net)
492
  - [MagpieRSS][] by [Kellan Elliot-McCrea](kellan@protest.net)
2
  =============
3
 
4
  * Author: [Charles Johnson](http://radgeek.com/contact)
5
+ * Version: 0.99
6
  * Project URI: <http://projects.radgeek.com/feedwordpress>
7
  * License: GPL 2. See License below for copyright jots and tittles.
8
 
20
 
21
  FeedWordPress is designed with flexibility, ease of use, and ease of
22
  configuration in mind. You'll need a working installation of WordPress (version
23
+ [2.3][], [2.2][], [2.1][], [2.0][] or [1.5][]), and also FTP or SFTP access to
24
+ your web host. The ability to create cron jobs on your web host would be very
25
+ helpful but it's not absolutely necessary. You *don't* need to tweak any
26
+ plain-text configuration files and you *don't* need shell access to your web
27
+ host to make it work. (Although, I should point out, web hosts that *don't*
28
+ offer shell access are *bad web hosts*.)
29
+
30
+ [2.3]: http://codex.wordpress.org/Version_2.3
31
+ [2.2]: http://codex.wordpress.org/Version_2.2
32
+ [2.1]: http://codex.wordpress.org/Version_2.1
33
+ [2.0]: http://codex.wordpress.org/Version_2.0
34
+ [1.5]: http://codex.wordpress.org/Version_1.5
35
 
36
  Installation
37
  ------------
38
  ### Requirements ###
39
 
40
+ To use version 0.99 of FeedWordPress, you will need:
41
 
42
+ 1. an installed and configured copy of WordPress 2.3.x, 2.2.x, 2.1.x,
43
+ 2.0.x, or 1.5.x. (FeedWordPress currently *will not work* with older
44
+ versions of WordPress, or with WordPress MU.)
45
 
46
  2. FTP or SFTP access to your web host
47
 
 
 
 
 
 
 
48
  ### Installation ###
49
 
50
  #### Upgrades ####
51
 
52
+ To *upgrade* an existing installation of FeedWordPress to version 0.99:
53
 
54
  1. Download the FeedWordPress archive in zip or gzipped tar format and
55
+ extract the files on your computer.
56
+
57
+ 2. If you are upgrading from version 0.98 or earlier, then you need to
58
+ create a new directory named `feedwordpress` in the `wp-content/plugins`
59
+ directory of your WordPress installation, and you also need to *delete*
60
+ your existing `wp-content/update-feeds.php` and
61
+ `wp-content/plugins/feedwordpress.php` files. The file structure for
62
+ FeedWordPress has changed and the files from your old version will not
63
+ be overwritten, which could cause conflicts if you leave them in place.
64
+
65
+ 3. Upload the new PHP files to `wp-content/plugins/feedwordpress`,
66
+ overwriting any existing FeedWordPress files that are there. Also be
67
+ sure to upgrade `wp-includes/rss.php` and
68
+ `wp-includes/rss-functions.php` if you use the optional MagpieRSS
69
+ upgrade, or don't use it yet but do want to syndicate Atom 1.0 feeds.
70
+
71
+ 3. If you are upgrading from version 0.96 or earlier, **immediately** log
72
  in to the WordPress Dashboard, and go to Options --> Syndicated. Follow
73
  the directions to launch the database upgrade procedure. The new
74
  versions of FeedWordPress incorporate some long-needed improvements, but
75
  old meta-data needs to be updated to prevent duplicate posts and other
76
  possible maladies. If you're upgrading an existing installation, updates
77
  and FeedWordPress template functions *will not work* until you've done
78
+ the upgrade. Then take a coffee break while the upgrade runs. It should,
79
+ hopefully, finish within a few minutes even on relatively large
80
+ databases.
81
 
82
+ 4. If you are upgrading from version 0.98 or earlier, note that the old
83
+ `update-feeds.php` has been eliminated in favor of a (hopefully) more
84
+ humane method for automatic updating. If you used a cron job for
85
+ scheduled updates, it will not work anymore, but there is another,
86
+ simpler method which will. See [Setting Up Feed Updates][] below to get
87
+ scheduled updates back on track.
 
 
 
 
 
88
 
89
  5. Enjoy your new installation of FeedWordPress.
90
 
91
  #### New Installations ####
92
 
93
+ 1. Download the FeedWordPress archive in zip or gzipped tar format and
94
+ extract the files on your computer.
95
+
96
+ 2. Create a new directory named `feedwordpress` in the `wp-content/plugins`
97
+ directory of your WordPress installation. Use an FTP or SFTP client to
98
+ upload the contents of the `wp-content/plugins/feedwordpress` directory
99
+ in the FeedWordPress archive to the new directory that you just created
100
+ on your web host.
101
+
102
+ 3. (Optional) Upgrade the copy of MagpieRSS packaged with WordPress by
103
  installing the new `rss.php` and `rss-functions.php` (archived in
104
  `OPTIONAL/wp-includes`) into your WordPress `wp-includes` directory.
105
  Upgrading MagpieRSS is necessary if you want to take advantage of
111
  your installation of PHP is up-to-date and that you keep a copy of
112
  the old MagpieRSS around to compare results.)
113
 
114
+ 4. Log in to the WordPress Dashboard and activate the FeedWordPress
115
  plugin.
116
 
117
+ 5. While you're at the Dashboard, once the plugin is activated, you can
118
+ go to **Syndication --> Options** and set (1) the link category that
119
  FeedWordPress will syndicate links from (by default, "Contributors"),
120
+ and (2) whether FeedWordPress will use automatic updates or only
121
+ manual updates.
 
122
 
123
+ 5. Go to the main **Syndication** page to set up the list of sites that
124
+ you want FeedWordPress to syndicate onto your blog. (If you have the
125
+ feeds you want to aggregate in a service such as Bloglines,
126
+ you may prefer to export them to an OPML file and use WordPress's
127
+ **Blogroll --> Import Links** to import them into the contributors
128
+ category.)
129
 
130
  #### Setting Up Feed Updates ####
131
 
132
+ FeedWordPress is now ready to accept posts from its syndication sources. The
133
+ next thing to do is to make sure it knows *when* to go get them.
134
+
135
+ **N.B.:** If you are upgrading from version 0.981 or earlier of FeedWordPress,
136
+ the system for checking for new posts has been overhauled, hopefully making it
137
+ more humane, and also easier to use for people who do not have access to task
138
+ scheduling tools such as `cron`. You will need to re-read this section and
139
+ change your set-up accordingly.
140
+
141
+ FeedWordPress allows you to choose whether it will check for new posts
142
+ automatically, or only when you manually request for it to check. By default,
143
+ FeedWordPress opts for the **manual** option -- so that you can get your feeds
144
+ set up properly before FeedWordPress begins importing new posts. If you want
145
+ to use automatically scheduled updates, remember to enable them in
146
+ **Syndication --> Options** after you finish setting up FeedWordPress.
147
+
148
+ ##### Manual Feed Updates #####
149
+
150
+ To manually check for new posts, log in to the WordPress Dashboard and go to the
151
+ main page under **Syndication**. You can use the "Update feeds now" button to
152
+ check for new posts on feeds that are due for a scheduled update, or use the
153
+ checkboxes and "Update Checked Feeds" button to force FeedWordPress to check one
154
+ or more specific feeds for new posts. FeedWordPress will check the selected
155
+ feed or feeds for new posts, and import any new content available.
156
+
157
+ ##### Automatic Feed Updates #####
158
+
159
+ If you choose an automatic update schedule, then FeedWordPress will
160
+ automatically check for new posts based on a schedule you determine. When
161
+ automatic updates are enabled, FeedWordPress will check for new posts when
162
+ (1) at least ten minutes have passed since the last update, and (2) a viewer
163
+ visits your FeedWordPress-enabled blog. (If you want the interval of time to
164
+ be shorter or longer, you can change the interval in the Dashboard under
165
+ **Syndication --> Options**.)
166
+
167
+ Note that this is not quite the same thing as precisely scheduled updating.
168
+ If you get at least one viewer every ten minutes, then FeedWordPress will be
169
+ regularly checking for new posts on schedule; if not, not. But for a relatively
170
+ active aggregator blog this is probably close enough for government work.
171
+
172
+ However, if you want to ensure regular updates, and you have access to a
173
+ task-scheduling tool such as `cron`, you can use it to schedule regular
174
+ checks for updates on a fixed schedule. For example, using `cron`, you can
175
+ easily ensure that FeedWordPress checks for new posts regularly by adding the
176
+ following line to your crontab, substituting the actual address of your
177
+ WordPress installation for "http://www.zyx.com/blog/":
178
+
179
+ */15 * * * * curl http://www.zyx.com/blog/ > /dev/null
180
+
181
+ If you don't have direct access to `cron` or a similar scheduling tool, you
182
+ can use online tools such as [WebCron](http://www.webcron.org/?lang=en) to
183
+ schedule a regular fetch of your blog's front page to much the same effect.
184
+
185
+ ##### Feed Updates using XML-RPC #####
186
+
187
+ FeedWordPress also allows syndicated blogs to notify you of updates using the
188
+ XML-RPC "recently updated" pings (in the standard format accepted by
189
+ Weblogs.com, Ping-O-Matic, Technorati, and other blogging services). Most
190
+ blogging software allows users to add a URI to the list of URIs that get pinged
191
+ with each new update -- see, for example, **Options --> Writing --> Update
192
+ Services** in WordPress, or **Configuration --> Preferences --> Publicity /
193
+ Remote Interfaces / TrackBack** in Movable Type. If you can get a contributor
194
+ to add your XML-RPC URI to her list of update services to ping, then whenever
195
+ she updates her blog, her blogging software will notify your FeedWordPress
196
+ installation, and FeedWordPress will look up her feed to grab the new posts off
197
+ of it. (If you have WordPress installed at <http://www.zyx.com/blog>, say, the
198
+ URI for her to ping should be <http://www.zyx.com/blog/xmlrpc.php>).
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
199
 
200
  Basic Concepts
201
  --------------
479
 
480
  License
481
  -------
482
+ The FeedWordPress plugin is copyright (c) 2005-2007 by Charles Johnson. It uses
483
+ code derived or translated from:
484
 
485
  - [wp-rss-aggregate.php][] by [Kellan Elliot-McCrea](kellan@protest.net)
486
  - [MagpieRSS][] by [Kellan Elliot-McCrea](kellan@protest.net)
wp-content/plugins/{feedwordpress.php → feedwordpress/feedwordpress.php} RENAMED
@@ -3,11 +3,11 @@
3
  Plugin Name: FeedWordPress
4
  Plugin URI: http://projects.radgeek.com/feedwordpress
5
  Description: simple and flexible Atom/RSS syndication for WordPress
6
- Version: 0.981
7
  Author: Charles Johnson
8
  Author URI: http://radgeek.com/
9
  License: GPL
10
- Last modified: 2007-02-17 4:23pm EST
11
  */
12
 
13
  # This uses code derived from:
@@ -26,15 +26,38 @@ Last modified: 2007-02-17 4:23pm EST
26
  # <http://www.zyx.com/blog/xmlrpc.php>), or see `update-feeds.php`
27
 
28
  # -- Don't change these unless you know what you're doing...
29
- define ('RPC_MAGIC', 'tag:radgeek.com/projects/feedwordpress/');
30
- define ('FEEDWORDPRESS_VERSION', '0.981');
 
31
  define ('DEFAULT_SYNDICATION_CATEGORY', 'Contributors');
32
 
 
 
33
  define ('FEEDWORDPRESS_CAT_SEPARATOR_PATTERN', '/[:\n]/');
34
  define ('FEEDWORDPRESS_CAT_SEPARATOR', "\n");
35
 
36
  define ('FEEDVALIDATOR_URI', 'http://feedvalidator.org/check.cgi');
37
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  // Note that the rss-functions.php that comes prepackaged with WordPress is
39
  // old & busted. For the new hotness, drop a copy of rss.php from
40
  // this archive into wp-includes/rss.php
@@ -45,9 +68,25 @@ else :
45
  require_once (ABSPATH . WPINC . '/rss-functions.php');
46
  endif;
47
 
48
- if (isset($wp_db_version) and $wp_db_version >= 3308) :
49
- require_once (ABSPATH . WPINC . '/registration-functions.php');
50
- require_once (ABSPATH . 'wp-admin/admin-db.php');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
  endif;
52
 
53
  // Is this being loaded from within WordPress 1.5 or later?
@@ -81,11 +120,17 @@ if (isset($wp_version) and $wp_version >= 1.5):
81
  add_filter('xmlrpc_methods', 'feedwordpress_xmlrpc_hook');
82
 
83
  # Outbound XML-RPC ping reform
84
- remove_action('publish_post', 'generic_ping');
85
- add_action('publish_post', 'fwp_catch_ping');
86
-
 
 
 
 
 
 
87
  # Hook in logging functions only if the logging option is ON
88
- $update_logging = get_settings('feedwordpress_update_logging');
89
  if ($update_logging == 'yes') :
90
  add_action('post_syndicated_item', 'log_feedwordpress_post', 100);
91
  add_action('update_syndicated_item', 'log_feedwordpress_update_post', 100);
@@ -93,12 +138,22 @@ if (isset($wp_version) and $wp_version >= 1.5):
93
  add_action('feedwordpress_check_feed', 'log_feedwordpress_check_feed', 100);
94
  add_action('feedwordpress_update_complete', 'log_feedwordpress_update_complete', 100);
95
  endif;
 
 
 
96
  else :
97
  # Hook in the menus, which will just point to the upgrade interface
98
  add_action('admin_menu', 'fwp_add_pages');
99
  endif; // if (!FeedWordPress::needs_upgrade())
100
  endif;
101
 
 
 
 
 
 
 
 
102
  ################################################################################
103
  ## LOGGING FUNCTIONS: log status updates to error_log if you want it ###########
104
  ################################################################################
@@ -140,6 +195,11 @@ function log_feedwordpress_update_complete ($delta) {
140
  ## LEGACY API: Replicate or mock up functions for legacy support purposes ######
141
  ################################################################################
142
 
 
 
 
 
 
143
  if (!function_exists('current_user_can')) {
144
  $legacy_capability_hack = true;
145
  function current_user_can ($task) {
@@ -191,20 +251,16 @@ function get_feed_meta ($key) {
191
  $ret = NULL;
192
  if (strlen($feed_id) > 0):
193
  if (isset($feedwordpress_linkcache[$feed_id])) :
194
- $result = $feedwordpress_linkcache[$feed_id];
195
  else :
196
- $result = $wpdb->get_row("
197
- SELECT * FROM $wpdb->links
198
- WHERE (link_id = '".$wpdb->escape($feed_id)."')"
199
- );
200
- $feedwordpress_linkcache[$feed_id] = $result;
201
  endif;
202
 
203
- $meta = FeedWordPress::notes_to_settings($result->link_notes);
204
- $ret = $meta[$key];
205
- endif; /* if */
206
  return $ret;
207
- }
208
 
209
  function get_syndication_permalink () {
210
  list($u) = get_post_custom_values('syndication_permalink'); return $u;
@@ -241,7 +297,7 @@ function feedwordpress_restore_syndicated_content ($text) {
241
  }
242
 
243
  function syndication_permalink ($permalink = '') {
244
- if (get_settings('feedwordpress_munge_permalink') != 'no'):
245
  $uri = get_syndication_permalink();
246
  return ((strlen($uri) > 0) ? $uri : $permalink);
247
  else:
@@ -254,9 +310,9 @@ function syndication_permalink ($permalink = '') {
254
  ################################################################################
255
 
256
  function fwp_upgrade_page () {
257
- if (isset($_POST['action']) and $_POST['action']=='Upgrade') :
258
- $ver = get_settings('feedwordpress_version');
259
- if (get_settings('feedwordpress_version') != FEEDWORDPRESS_VERSION) :
260
  echo "<div class=\"wrap\">\n";
261
  echo "<h2>Upgrading FeedWordPress...</h2>";
262
 
@@ -315,235 +371,12 @@ function fwp_add_pages () {
315
  $manage_options = 'manage_options';
316
  endif;
317
 
318
- add_submenu_page('link-manager.php', 'Syndicated Sites', 'Syndicated', $manage_links, basename(__FILE__), 'fwp_syndication_manage_page');
319
- add_options_page('Syndication Options', 'Syndication', $manage_options, basename(__FILE__), 'fwp_syndication_options_page');
 
 
320
  } // function fwp_add_pages () */
321
 
322
- function fwp_syndication_options_page () {
323
- global $wpdb, $wp_db_version;
324
-
325
- if (FeedWordPress::needs_upgrade()) :
326
- fwp_upgrade_page();
327
- return;
328
- endif;
329
-
330
- $caption = 'Save Changes';
331
- if (isset($_POST['action']) and $_POST['action']==$caption):
332
- check_admin_referer();
333
-
334
- if (!current_user_can('manage_options')):
335
- die (__("Cheatin' uh ?"));
336
- else:
337
- update_option('feedwordpress_rpc_secret', $_REQUEST['rpc_secret']);
338
- update_option('feedwordpress_cat_id', $_REQUEST['syndication_category']);
339
- update_option('feedwordpress_munge_permalink', $_REQUEST['munge_permalink']);
340
- update_option('feedwordpress_update_logging', $_REQUEST['update_logging']);
341
- update_option('feedwordpress_unfamiliar_author', $_REQUEST['unfamiliar_author']);
342
- update_option('feedwordpress_unfamiliar_category', $_REQUEST['unfamiliar_category']);
343
- update_option('feedwordpress_syndicated_post_status', $_REQUEST['post_status']);
344
-
345
- // Categories
346
- $cats = array();
347
- if (isset($_POST['post_category'])) :
348
- $cat_set = "(".implode(",", $_POST['post_category']).")";
349
- $cats = $wpdb->get_col(
350
- "SELECT cat_name
351
- FROM $wpdb->categories
352
- WHERE cat_ID IN {$cat_set}
353
- ");
354
- endif;
355
-
356
- if (!empty($cats)) :
357
- update_option('feedwordpress_syndication_cats', implode("\n", $cats));
358
- else :
359
- delete_option('feedwordpress_syndication_cats');
360
- endif;
361
-
362
- if (isset($_REQUEST['comment_status']) and ($_REQUEST['comment_status'] == 'open')) :
363
- update_option('feedwordpress_syndicated_comment_status', 'open');
364
- else :
365
- update_option('feedwordpress_syndicated_comment_status', 'closed');
366
- endif;
367
-
368
- if (isset($_REQUEST['ping_status']) and ($_REQUEST['ping_status'] == 'open')) :
369
- update_option('feedwordpress_syndicated_ping_status', 'open');
370
- else :
371
- update_option('feedwordpress_syndicated_ping_status', 'closed');
372
- endif;
373
-
374
- if (isset($_REQUEST['hardcode_name']) and ($_REQUEST['hardcode_name'] == 'no')) :
375
- update_option('feedwordpress_hardcode_name', 'no');
376
- else :
377
- update_option('feedwordpress_hardcode_name', 'yes');
378
- endif;
379
-
380
- if (isset($_REQUEST['hardcode_description']) and ($_REQUEST['hardcode_description'] == 'no')) :
381
- update_option('feedwordpress_hardcode_description', 'no');
382
- else :
383
- update_option('feedwordpress_hardcode_description', 'yes');
384
- endif;
385
-
386
- if (isset($_REQUEST['hardcode_url']) and ($_REQUEST['hardcode_url'] == 'no')) :
387
- update_option('feedwordpress_hardcode_url', 'no');
388
- else :
389
- update_option('feedwordpress_hardcode_url', 'yes');
390
- endif;
391
- ?>
392
- <div class="updated">
393
- <p><?php _e('Options saved.')?></p>
394
- </div>
395
- <?php
396
- endif;
397
- endif;
398
-
399
- $cat_id = FeedWordPress::link_category_id();
400
- $rpc_secret = FeedWordPress::rpc_secret();
401
- $munge_permalink = get_settings('feedwordpress_munge_permalink');
402
- $update_logging = get_settings('feedwordpress_update_logging');
403
-
404
- $hardcode_name = get_settings('feedwordpress_hardcode_name');
405
- $hardcode_description = get_settings('feedwordpress_hardcode_description');
406
- $hardcode_url = get_settings('feedwordpress_hardcode_url');
407
-
408
- $post_status = FeedWordPress::syndicated_status('post', array(), 'publish');
409
- $comment_status = FeedWordPress::syndicated_status('comment', array(), 'closed');
410
- $ping_status = FeedWordPress::syndicated_status('ping', array(), 'closed');
411
-
412
- $unfamiliar_author = array ('create' => '','default' => '','filter' => '');
413
- $ua = FeedWordPress::on_unfamiliar('author');
414
- if (is_string($ua) and array_key_exists($ua, $unfamiliar_author)) :
415
- $unfamiliar_author[$ua] = ' checked="checked"';
416
- endif;
417
- $unfamiliar_category = array ('create'=>'','default'=>'','filter'=>'');
418
- $uc = FeedWordPress::on_unfamiliar('category');
419
- if (is_string($uc) and array_key_exists($uc, $unfamiliar_category)) :
420
- $unfamiliar_category[$uc] = ' checked="checked"';
421
- endif;
422
-
423
- if (isset($wp_db_version) and $wp_db_version >= 4772) :
424
- $results = get_categories('type=link');
425
- else :
426
- $results = $wpdb->get_results("SELECT cat_id, cat_name, auto_toggle FROM $wpdb->linkcategories ORDER BY cat_id");
427
- endif;
428
-
429
- $cats = get_settings('feedwordpress_syndication_cats');
430
- $dogs = get_nested_categories(-1, 0);
431
- $cats = array_map('strtolower',
432
- array_map('trim',
433
- preg_split(FEEDWORDPRESS_CAT_SEPARATOR_PATTERN, $cats)
434
- ));
435
-
436
- foreach ($dogs as $tag => $dog) :
437
- if (in_array(strtolower(trim($dog['cat_name'])), $cats)) :
438
- $dogs[$tag]['checked'] = true;
439
- endif;
440
- endforeach;
441
-
442
- ?>
443
- <div class="wrap">
444
- <h2>Syndication Options</h2>
445
- <form action="" method="post">
446
- <fieldset class="options">
447
- <legend>Syndicated Feeds</legend>
448
- <table class="editform" width="100%" cellspacing="2" cellpadding="5">
449
- <tr>
450
- <th width="33%" scope="row">Syndicate links in category:</th>
451
- <td width="67%"><?php
452
- echo "\n<select name=\"syndication_category\" size=\"1\">";
453
- foreach ($results as $row) {
454
- if (!isset($row->cat_id)) { $row->cat_id = $row->cat_ID; }
455
-
456
- echo "\n\t<option value=\"$row->cat_id\"";
457
- if ($row->cat_id == $cat_id)
458
- echo " selected='selected'";
459
- echo ">$row->cat_id: ".wp_specialchars($row->cat_name);
460
- if ('Y' == $row->auto_toggle)
461
- echo ' (auto toggle)';
462
- echo "</option>\n";
463
- }
464
- echo "\n</select>\n";
465
- ?></td>
466
- </tr>
467
-
468
- <tr><th width="33%" scope="row" style="vertical-align:top">Update live from feed:</th>
469
- <td width="67%"><ul style="margin:0;list-style:none">
470
- <li><input type="checkbox" name="hardcode_name" value="no"<?php echo (($hardcode_name=='yes')?'':' checked="checked"');?>/> Contributor name (feed title)</li>
471
- <li><input type="checkbox" name="hardcode_description" value="no"<?php echo (($hardcode_description=='yes')?'':' checked="checked"');?>/> Contributor description (feed tagline)</li>
472
- <li><input type="checkbox" name="hardcode_url" value="no"<?php echo (($hardcode_url=='yes')?'':' checked="checked"');?>/> Homepage (feed link)</li>
473
- </ul></td></tr>
474
- </table>
475
- </fieldset>
476
-
477
- <fieldset class="options">
478
- <legend>Syndicated Posts</legend>
479
-
480
- <?php fwp_category_box($dogs, '<em>all syndicated posts</em>'); ?>
481
-
482
- <table class="editform" width="75%" cellspacing="2" cellpadding="5">
483
- <tr style="vertical-align: top"><th width="33%" scope="row">Publication:</th>
484
- <td width="67%"><ul style="margin: 0; padding: 0; list-style:none">
485
- <li><label><input type="radio" name="post_status" value="publish"<?php echo ($post_status=='publish')?' checked="checked"':''; ?> /> Publish syndicated posts immediately</label></li>
486
- <li><label><input type="radio" name="post_status" value="draft"<?php echo ($post_status=='draft')?' checked="checked"':''; ?> /> Hold syndicated posts as drafts</label></li>
487
- <li><label><input type="radio" name="post_status" value="private"<?php echo ($post_status=='private')?' checked="checked"':''; ?> /> Hold syndicated posts as private posts</label></li>
488
- </ul></td></tr>
489
-
490
- <tr style="vertical-align: top"><th width="33%" scope="row">Comments:</th>
491
- <td width="67%"><ul style="margin: 0; padding: 0; list-style:none">
492
- <li><label><input type="radio" name="comment_status" value="open"<?php echo ($comment_status=='open')?' checked="checked"':''; ?> /> Allow comments on syndicated posts</label></li>
493
- <li><label><input type="radio" name="comment_status" value="closed"<?php echo ($comment_status!='open')?' checked="checked"':''; ?> /> Don't allow comments on syndicated posts</label></li>
494
- </ul></td></tr>
495
-
496
- <tr style="vertical-align: top"><th width="33%" scope="row">Trackback and Pingback:</th>
497
- <td width="67%"><ul style="margin:0; padding: 0; list-style:none">
498
- <li><label><input type="radio" name="ping_status" value="open"<?php echo ($ping_status=='open')?' checked="checked"':''; ?> /> Accept pings on syndicated posts</label></li>
499
- <li><label><input type="radio" name="ping_status" value="closed"<?php echo ($ping_status!='open')?' checked="checked"':''; ?> /> Don't accept pings on syndicated posts</label></li>
500
- </ul></td></tr>
501
-
502
- <tr style="vertical-align: top"><th width="33%" scope="row" style="vertical-align:top">Unfamiliar authors:</th>
503
- <td width="67%"><ul style="margin: 0; padding: 0; list-style:none">
504
- <li><label><input type="radio" name="unfamiliar_author" value="create"<?php echo $unfamiliar_author['create']; ?>/> create a new author account</label></li>
505
- <li><label><input type="radio" name="unfamiliar_author" value="default"<?php echo $unfamiliar_author['default']; ?> /> attribute the post to the default author</label></li>
506
- <li><label><input type="radio" name="unfamiliar_author" value="filter"<?php echo $unfamiliar_author['filter']; ?> /> don't syndicate the post</label></li>
507
- </ul></td></tr>
508
- <tr style="vertical-align: top"><th width="33%" scope="row" style="vertical-align:top">Unfamiliar categories:</th>
509
- <td width="67%"><ul style="margin: 0; padding:0; list-style:none">
510
- <li><label><input type="radio" name="unfamiliar_category" value="create"<?php echo $unfamiliar_category['create']; ?>/> create any categories the post is in</label></li>
511
- <li><label><input type="radio" name="unfamiliar_category" value="default"<?php echo $unfamiliar_category['default']; ?>/> don't create new categories</li>
512
- <li><label><input type="radio" name="unfamiliar_category" value="filter"<?php echo $unfamiliar_category['filter']; ?>/> don't create new categories and don't syndicate posts unless they match at least one familiar category</label></li>
513
- </ul></td></tr>
514
-
515
- <tr style="vertical-align: top"><th width="33%" scope="row">Permalinks point to:</th>
516
- <td width="67%"><select name="munge_permalink" size="1">
517
- <option value="yes"<?php echo ($munge_permalink=='yes')?' selected="selected"':''; ?>>original website</option>
518
- <option value="no"<?php echo ($munge_permalink=='no')?' selected="selected"':''; ?>>this website</option>
519
- </select></td></tr>
520
- </table>
521
- <div class="submit"><input type="submit" name="action" value="<?php echo $caption; ?>" /></div>
522
- </fieldset>
523
-
524
- <fieldset class="options">
525
- <legend>Back-end Options</legend>
526
- <table class="editform" width="100%" cellspacing="2" cellpadding="5">
527
- <tr>
528
- <th width="33%" scope="row">XML-RPC update secret word:</th>
529
- <td width="67%"><input id="rpc_secret" name="rpc_secret" value="<?php echo $rpc_secret; ?>" />
530
- </td>
531
- </tr>
532
- <tr>
533
- <th scope="row">Write update notices to PHP logs:</th>
534
- <td><select name="update_logging" size="1">
535
- <option value="yes"<?php echo (($update_logging=='yes')?' selected="selected"':''); ?>>yes</option>
536
- <option value="no"<?php echo (($update_logging!='yes')?' selected="selected"':''); ?>>no</option>
537
- </select></td>
538
- </tr>
539
- </table>
540
- <div class="submit"><input type="submit" name="action" value="<?php echo $caption; ?>" /></div>
541
- </fieldset>
542
- </form>
543
- </div>
544
- <?php
545
- }
546
-
547
  function fwp_category_box ($checked, $object) {
548
  global $wp_db_version;
549
 
@@ -575,6 +408,12 @@ function fwp_category_box ($checked, $object) {
575
  endif;
576
  }
577
 
 
 
 
 
 
 
578
  function fwp_syndication_manage_page () {
579
  global $wpdb;
580
 
@@ -589,7 +428,7 @@ if (isset($_REQUEST['action'])):
589
  if ($_REQUEST['action'] == 'feedfinder') : $cont = fwp_feedfinder_page();
590
  elseif ($_REQUEST['action'] == 'switchfeed') : $cont = fwp_switchfeed_page();
591
  elseif ($_REQUEST['action'] == 'linkedit') : $cont = fwp_linkedit_page();
592
- elseif ($_REQUEST['action'] == 'Unsubscribe from Checked' or $_REQUEST['action'] == 'Unsubscribe') : $cont = fwp_multidelete_page();
593
  endif;
594
  endif;
595
 
@@ -597,20 +436,74 @@ if ($cont):
597
  ?>
598
  <?php
599
  $links = FeedWordPress::syndicated_links();
600
- ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
601
  <div class="wrap">
602
- <form action="link-manager.php?page=<?php echo basename(__FILE__); ?>" method="post">
603
- <h2>Syndicate a new site:</h2>
604
- <div>
605
- <label for="add-uri">Website or newsfeed:</label>
606
- <input type="text" name="lookup" id="add-uri" value="URI" size="64" />
607
- <input type="hidden" name="action" value="feedfinder" />
608
- </div>
609
- <div class="submit"><input type="submit" value="Syndicate &raquo;" /></div>
 
 
 
 
 
 
610
  </form>
611
- </div>
612
 
613
- <form action="link-manager.php?page=<?php echo basename(__FILE__); ?>" method="post">
614
  <div class="wrap">
615
  <h2>Syndicated Sites</h2>
616
  <?php $alt_row = true;
@@ -626,7 +519,7 @@ if ($cont):
626
  <?php foreach ($links as $link):
627
  $alt_row = !$alt_row; ?>
628
  <tr<?php echo ($alt_row?' class="alternate"':''); ?>>
629
- <td><a href="<?php echo wp_specialchars($link->link_url); ?>"><?php echo wp_specialchars($link->link_name); ?></a></td>
630
  <?php
631
  if (strlen($link->link_rss) > 0):
632
  $caption='Switch Feed';
@@ -641,7 +534,7 @@ if ($cont):
641
  if (strlen($display_uri) > 32) : $display_uri = substr($display_uri, 0, 32).'&#8230;'; endif;
642
  ?>
643
  <td>
644
- <strong><a href="<?php echo $link->link_rss; ?>"><?php echo wp_specialchars($display_uri); ?></a></strong></td>
645
  <?php
646
  else:
647
  $caption='Find Feed';
@@ -651,9 +544,9 @@ if ($cont):
651
  <?php
652
  endif;
653
  ?>
654
- <td><a href="link-manager.php?page=<?php echo basename(__FILE__); ?>&amp;link_id=<?php echo $link->link_id; ?>&amp;action=linkedit" class="edit"><?php _e('Edit')?></a></td>
655
- <td><a href="link-manager.php?page=<?php echo basename(__FILE__); ?>&amp;link_id=<?php echo $link->link_id; ?>&amp;action=feedfinder" class="edit"><?php echo $caption; ?></a></td>
656
- <td><a href="link-manager.php?page=<?php echo basename(__FILE__); ?>&amp;link_id=<?php echo $link->link_id; ?>&amp;action=Unsubscribe" class="delete"><?php _e('Unsubscribe'); ?></a></td>
657
  <td><input type="checkbox" name="link_ids[]" value="<?php echo $link->link_id; ?>" /></td>
658
  <?php
659
  echo "\n\t</tr>";
@@ -665,15 +558,24 @@ if ($cont):
665
 
666
  <?php endif; ?>
667
  </table>
668
- </div>
669
-
 
 
 
 
 
670
  <div class="wrap">
671
- <h2>Manage Multiple Links</h2>
672
- <div class="submit">
673
- <input type="submit" class="delete" name="action" value="Unsubscribe from Checked" />
674
- </div>
 
 
675
  </div>
 
676
  </form>
 
677
  <?php
678
  endif;
679
  }
@@ -685,10 +587,12 @@ function fwp_feedfinder_page () {
685
 
686
  if (isset($_REQUEST['link_id']) and ($_REQUEST['link_id']!=0)):
687
  $link_id = $_REQUEST['link_id'];
 
 
688
  $link = $wpdb->get_row("SELECT * FROM $wpdb->links WHERE link_id='".$wpdb->escape($link_id)."'");
689
  if (is_object($link)):
690
  if (is_null($lookup)) $lookup = $link->link_url;
691
- $name = wp_specialchars($link->link_name);
692
  else:
693
  die (__("Cheatin' uh ?"));
694
  endif;
@@ -709,17 +613,17 @@ function fwp_feedfinder_page () {
709
  $feed_title = isset($rss->channel['title'])?$rss->channel['title']:$rss->channel['link'];
710
  $feed_link = isset($rss->channel['link'])?$rss->channel['link']:'';
711
  ?>
712
- <form action="link-manager.php?page=<?php echo basename(__FILE__); ?>" method="post">
713
  <fieldset style="clear: both">
714
  <legend><?php echo $rss->feed_type; ?> <?php echo $rss->feed_version; ?> feed</legend>
715
 
716
  <?php if ($link_id===0): ?>
717
- <input type="hidden" name="feed_title" value="<?php echo wp_specialchars($feed_title); ?>" />
718
- <input type="hidden" name="feed_link" value="<?php echo wp_specialchars($feed_link); ?>" />
719
  <?php endif; ?>
720
 
721
  <input type="hidden" name="link_id" value="<?php echo $link_id; ?>" />
722
- <input type="hidden" name="feed" value="<?php echo wp_specialchars($f); ?>" />
723
  <input type="hidden" name="action" value="switchfeed" />
724
 
725
  <div>
@@ -744,9 +648,9 @@ function fwp_feedfinder_page () {
744
  <h3>Feed Information</h3>
745
  <ul>
746
  <li><strong>Website:</strong> <a href="<?php echo $feed_link; ?>"><?php echo is_null($feed_title)?'<em>Unknown</em>':$feed_title; ?></a></li>
747
- <li><strong>Feed URI:</strong> <a href="<?php echo wp_specialchars($f); ?>"><?php echo wp_specialchars($f); ?></a> <a title="Check feed &lt;<?php echo wp_specialchars($f); ?>&gt; for validity" href="http://feedvalidator.org/check.cgi?url=<?php echo urlencode($f); ?>"><img src="../wp-images/smilies/icon_arrow.gif" alt="(&rarr;)" /></a></li>
748
- <li><strong>Encoding:</strong> <?php echo isset($rss->encoding)?wp_specialchars($rss->encoding):"<em>Unknown</em>"; ?></li>
749
- <li><strong>Description:</strong> <?php echo isset($rss->channel['description'])?wp_specialchars($rss->channel['description']):"<em>Unknown</em>"; ?></li>
750
  </ul>
751
  <div class="submit"><input type="submit" name="Use" value="&laquo; Use this feed" /></div>
752
  <div class="submit"><input type="submit" name="Cancel" value="&laquo; Cancel" /></div>
@@ -762,7 +666,7 @@ function fwp_feedfinder_page () {
762
  ?>
763
  </div>
764
 
765
- <form action="link-manager.php?page=<?php echo basename(__FILE__); ?>" method="post">
766
  <div class="wrap">
767
  <h2>Use another feed</h2>
768
  <div><label>Feed:</label>
@@ -786,10 +690,10 @@ function fwp_switchfeed_page () {
786
  elseif (isset($_REQUEST['link_id']) and ($_REQUEST['link_id']==0)):
787
  $link_id = FeedWordPress::syndicate_link($_REQUEST['feed_title'], $_REQUEST['feed_link'], $_REQUEST['feed']);
788
  if ($link_id): ?>
789
- <div class="updated"><p><a href="<?php echo $_REQUEST['feed_link']; ?>"><?php echo wp_specialchars($_REQUEST['feed_title']); ?></a>
790
- has been added as a contributing site, using the newsfeed at &lt;<a href="<?php echo $_REQUEST['feed']; ?>"><?php echo wp_specialchars($_REQUEST['feed']); ?></a>&gt;.</p></div>
791
  <?php else: ?>
792
- <div class="updated"><p>There was a problem adding the newsfeed. [SQL: <?php echo wp_specialchars(mysql_error()); ?>]</p></div>
793
  <?php endif;
794
  elseif (isset($_REQUEST['link_id'])):
795
  // Update link_rss
@@ -806,8 +710,8 @@ has been added as a contributing site, using the newsfeed at &lt;<a href="<?php
806
  WHERE link_id = '".$wpdb->escape($_REQUEST['link_id'])."'
807
  ");
808
  ?>
809
- <div class="updated"><p>Feed for <a href="<?php echo $result->link_url; ?>"><?php echo wp_specialchars($result->link_name); ?></a>
810
- updated to &lt;<a href="<?php echo $_REQUEST['feed']; ?>"><?php echo wp_specialchars($_REQUEST['feed']); ?></a>&gt;.</p></div>
811
  <?php else: ?>
812
  <div class="updated"><p>Nothing was changed.</p></div>
813
  <?php endif;
@@ -823,6 +727,7 @@ function fwp_linkedit_page () {
823
 
824
  $special_settings = array ( /* Regular expression syntax is OK here */
825
  'cats',
 
826
  'hardcode name',
827
  'hardcode url',
828
  'hardcode description',
@@ -843,21 +748,19 @@ function fwp_linkedit_page () {
843
  return fwp_feedfinder_page(); // re-route to Feed Finder page
844
  else :
845
  $link_id = (int) $_REQUEST['link_id'];
846
- $row = $wpdb->get_row("
847
- SELECT * FROM $wpdb->links WHERE link_id = $link_id
848
- ");
849
 
850
- if ($row) :
851
- if (isset($_POST['save'])) :
852
  $alter = array ();
853
 
854
- $meta = FeedWordPress::notes_to_settings($row->link_notes);
855
  if (isset($meta['cats'])):
856
  $meta['cats'] = preg_split(FEEDWORDPRESS_CAT_SEPARATOR_PATTERN, $meta['cats']);
857
  endif;
858
 
859
  // custom feed settings first
860
- foreach ($_POST['notes'] as $mn) :
861
  $mn['key0'] = trim($mn['key0']);
862
  $mn['key1'] = trim($mn['key1']);
863
  if (preg_match("\007^(("
@@ -878,41 +781,36 @@ function fwp_linkedit_page () {
878
 
879
  // now stuff through the web form
880
  // hardcoded feed info
881
- if (isset($_POST['hardcode_name'])) :
882
- $meta['hardcode name'] = $_POST['hardcode_name'];
883
  if (FeedWordPress::affirmative($meta, 'hardcode name')) :
884
- $alter[] = "link_name = '".$wpdb->escape($_POST['name'])."'";
885
  endif;
886
  endif;
887
- if (isset($_POST['hardcode_description'])) :
888
- $meta['hardcode description'] = $_POST['hardcode_description'];
889
  if (FeedWordPress::affirmative($meta, 'hardcode description')) :
890
- $alter[] = "link_description = '".$wpdb->escape($_POST['description'])."'";
891
  endif;
892
  endif;
893
- if (isset($_POST['hardcode_url'])) :
894
- $meta['hardcode url'] = $_POST['hardcode_url'];
895
  if (FeedWordPress::affirmative($meta, 'hardcode url')) :
896
- $alter[] = "link_url = '".$wpdb->escape($_POST['linkurl'])."'";
897
  endif;
898
  endif;
899
 
900
  // Update scheduling
901
- if (isset($_POST['update_schedule'])) :
902
- $meta['update/hold'] = $_POST['update_schedule'];
903
  endif;
904
 
905
  // Categories
906
- if (isset($_POST['post_category'])) :
907
- $cat_set = "(".implode(",", $_POST['post_category']).")";
908
- $meta['cats'] = $wpdb->get_col(
909
- "SELECT cat_name
910
- FROM $wpdb->categories
911
- WHERE cat_ID IN {$cat_set}
912
- ");
913
- if (count($meta['cats']) == 0) :
914
- unset($meta['cats']);
915
- endif;
916
  else :
917
  unset($meta['cats']);
918
  endif;
@@ -920,11 +818,11 @@ function fwp_linkedit_page () {
920
  // Post status, comment status, ping status
921
  foreach (array('post', 'comment', 'ping') as $what) :
922
  $sfield = "feed_{$what}_status";
923
- if (isset($_POST[$sfield])) :
924
- if ($_POST[$sfield]=='site-default') :
925
  unset($meta["{$what} status"]);
926
  else :
927
- $meta["{$what} status"] = $_POST[$sfield];
928
  endif;
929
  endif;
930
  endforeach;
@@ -932,22 +830,30 @@ function fwp_linkedit_page () {
932
  // Unfamiliar author, unfamiliar categories
933
  foreach (array("author", "category") as $what) :
934
  $sfield = "unfamiliar_{$what}";
935
- if (isset($_POST[$sfield])) :
936
- if ($_POST[$sfield]=='site-default') :
937
  unset($meta["unfamiliar {$what}"]);
938
  else :
939
- $meta["unfamiliar {$what}"] = $_POST[$sfield];
940
  endif;
941
  endif;
942
  endforeach;
943
 
 
 
 
 
 
 
 
 
944
  if (is_array($meta['cats'])) :
945
  $meta['cats'] = implode(FEEDWORDPRESS_CAT_SEPARATOR, $meta['cats']);
946
  endif;
947
 
948
  $notes = '';
949
  foreach ($meta as $key => $value) :
950
- $notes .= $key . ": ". addcslashes($value, "\0..\37") . "\n";
951
  endforeach;
952
  $alter[] = "link_notes = '".$wpdb->escape($notes)."'";
953
 
@@ -962,27 +868,23 @@ function fwp_linkedit_page () {
962
  $updated_link = true;
963
 
964
  // reload link information from DB
965
- $row = $wpdb->get_row("
966
- SELECT * FROM $wpdb->links WHERE link_id = $link_id
967
- ");
968
  else :
969
  $updated_link = false;
970
  endif;
971
 
972
- $link_url = wp_specialchars($row->link_url, 1);
973
- $link_name = wp_specialchars($row->link_name, 1);
974
- $link_image = $row->link_image;
975
- $link_target = $row->link_target;
976
- $link_category = $row->link_category;
977
- $link_description = wp_specialchars($row->link_description);
978
- $link_visible = $row->link_visible;
979
- $link_rating = $row->link_rating;
980
- $link_rel = $row->link_rel;
981
- $link_notes = wp_specialchars($row->link_notes);
982
- $link_rss_uri = wp_specialchars($row->link_rss);
983
 
984
- $meta = FeedWordPress::notes_to_settings($row->link_notes);
985
-
986
  $status['post'] = array('publish' => '', 'private' => '', 'draft' => '', 'site-default' => '');
987
  $status['comment'] = array('open' => '', 'closed' => '', 'site-default' => '');
988
  $status['ping'] = array('open' => '', 'closed' => '', 'site-default' => '');
@@ -1009,13 +911,22 @@ function fwp_linkedit_page () {
1009
  endforeach;
1010
 
1011
  $dogs = get_nested_categories(-1, 0);
1012
- $cats = array_map('strtolower',
1013
- array_map('trim',
1014
- preg_split(FEEDWORDPRESS_CAT_SEPARATOR_PATTERN, $meta['cats'])
1015
- ));
 
 
 
 
1016
 
1017
  foreach ($dogs as $tag => $dog) :
1018
- if (in_array(strtolower(trim($dog['cat_name'])), $cats)) :
 
 
 
 
 
1019
  $dogs[$tag]['checked'] = true;
1020
  endif;
1021
  endforeach;
@@ -1039,7 +950,7 @@ function fwp_linkedit_page () {
1039
  <div class="updated"><p>Syndicated feed settings updated.</p></div>
1040
  <?php endif; ?>
1041
 
1042
- <form action="link-manager.php?page=<?php echo basename(__FILE__); ?>" method="post">
1043
  <div class="wrap">
1044
  <input type="hidden" name="link_id" value="<?php echo $link_id; ?>" />
1045
  <input type="hidden" name="action" value="linkedit" />
@@ -1050,9 +961,9 @@ function fwp_linkedit_page () {
1050
  <table class="editform" width="100%" cellspacing="2" cellpadding="5">
1051
  <tr>
1052
  <th scope="row" width="20%"><?php _e('Feed URI:') ?></th>
1053
- <td width="60%"><a href="<?php echo wp_specialchars($link_rss_uri); ?>"><?php echo $link_rss_uri; ?></a>
1054
  <a href="<?php echo FEEDVALIDATOR_URI; ?>?url=<?php echo urlencode($link_rss_uri); ?>"
1055
- title="Check feed &lt;<?php echo wp_specialchars($link_rss_uri); ?>&gt; for validity"><img src="../wp-images/smilies/icon_arrow.gif" alt="&rarr;" /></a>
1056
  </td>
1057
  <td width="20%"><input type="submit" name="feedfinder" value="switch &rarr;" style="font-size:smaller" /></td>
1058
  </tr>
@@ -1064,8 +975,8 @@ value="<?php echo $link_name; ?>" style="width: 95%" />
1064
  </td>
1065
  <td>
1066
  <select id="basics-hardcode-name" onchange="flip_hardcode('name')" name="hardcode_name">
1067
- <option value="no" <?php echo FeedWordPress::hardcode('name', $meta)?'':'selected="selected"'; ?>>update automatically</option>
1068
- <option value="yes" <?php echo FeedWordPress::hardcode('name', $meta)?'selected="selected"':''; ?>>edit manually</option>
1069
  </select>
1070
  </td>
1071
  </tr>
@@ -1078,8 +989,8 @@ value="<?php echo $link_name; ?>" style="width: 95%" />
1078
  <td>
1079
  <select id="basics-hardcode-description" onchange="flip_hardcode('description')"
1080
  name="hardcode_description">
1081
- <option value="no" <?php echo FeedWordPress::hardcode('description', $meta)?'':'selected="selected"'; ?>>update automatically</option>
1082
- <option value="yes" <?php echo FeedWordPress::hardcode('description', $meta)?'selected="selected"':''; ?>>edit manually</option>
1083
  </select></td>
1084
  </tr>
1085
  <tr>
@@ -1089,8 +1000,8 @@ name="hardcode_description">
1089
  <a id="basics-url-view" href="<?php echo $link_url; ?>"><?php echo $link_url; ?></a></td>
1090
  <td>
1091
  <select id="basics-hardcode-url" onchange="flip_hardcode('url')" name="hardcode_url">
1092
- <option value="no"<?php echo FeedWordPress::hardcode('url', $meta)?'':' selected="selected"'; ?>>update live from feed</option>
1093
- <option value="yes"<?php echo FeedWordPress::hardcode('url', $meta)?' selected="selected"':''; ?>>edit manually</option>
1094
  </select></td></tr>
1095
 
1096
  <tr>
@@ -1146,12 +1057,12 @@ flip_hardcode('url');
1146
 
1147
  <?php fwp_category_box($dogs, 'all syndicated posts from this feed'); ?>
1148
 
1149
- <table class="editform" width="80%" cellspacing="2" cellpadding="5">
1150
- <tr><th width="20%" scope="row" style="vertical-align:top">Publication:</th>
1151
- <td width="80%" style="vertical-align:top"><ul style="margin:0; list-style:none">
1152
  <li><label><input type="radio" name="feed_post_status" value="site-default"
1153
- <?php echo $status['post']['site-default']; ?> /> Use site-wide setting from <a href="options-general.php?page=<?php echo basename(__FILE__); ?>">Syndication Options</a>
1154
- (currently: <strong><?php echo FeedWordPress::syndicated_status('post', array(), 'publish'); ?></strong>)</label></li>
1155
  <li><label><input type="radio" name="feed_post_status" value="publish"
1156
  <?php echo $status['post']['publish']; ?> /> Publish posts from this feed immediately</label></li>
1157
  <li><label><input type="radio" name="feed_post_status" value="private"
@@ -1161,11 +1072,11 @@ flip_hardcode('url');
1161
  </ul></td>
1162
  </tr>
1163
 
1164
- <tr><th width="20%" scope="row" style="vertical-align:top">Comments:</th>
1165
- <td width="80%"><ul style="margin:0; list-style:none">
1166
  <li><label><input type="radio" name="feed_comment_status" value="site-default"
1167
- <?php echo $status['comment']['site-default']; ?> /> Use site-wide setting from <a href="options-general.php?page=<?php echo basename(__FILE__); ?>">Syndication Options</a>
1168
- (currently: <strong><?php echo FeedWordPress::syndicated_status('comment', array(), 'closed'); ?>)</strong></label></li>
1169
  <li><label><input type="radio" name="feed_comment_status" value="open"
1170
  <?php echo $status['comment']['open']; ?> /> Allow comments on syndicated posts from this feed</label></li>
1171
  <li><label><input type="radio" name="feed_comment_status" value="closed"
@@ -1173,11 +1084,11 @@ flip_hardcode('url');
1173
  </ul></td>
1174
  </tr>
1175
 
1176
- <tr><th width="20%" scope="row" style="vertical-align:top">Trackback and Pingback:</th>
1177
- <td width="80%"><ul style="margin:0; list-style:none">
1178
  <li><label><input type="radio" name="feed_ping_status" value="site-default"
1179
- <?php echo $status['ping']['site-default']; ?> /> Use site-wide setting from <a href="options-general.php?page=<?php echo basename(__FILE__); ?>">Syndication Options</a>
1180
- (currently: <strong><?php echo FeedWordPress::syndicated_status('ping', array(), 'closed'); ?>)</strong></label></li>
1181
  <li><label><input type="radio" name="feed_ping_status" value="open"
1182
  <?php echo $status['ping']['open']; ?> /> Accept pings on syndicated posts from this feed</label></li>
1183
  <li><label><input type="radio" name="feed_ping_status" value="closed"
@@ -1197,7 +1108,7 @@ flip_hardcode('url');
1197
  <tr>
1198
  <th width="20%" scope="row" style="vertical-align:top">Unfamiliar authors:</th>
1199
  <td width="80%"><ul style="margin: 0; list-style:none">
1200
- <li><label><input type="radio" name="unfamiliar_author" value="site-default"<?php echo $unfamiliar['author']['site-default']; ?> /> use site-wide setting from <a href="options-general.php?page=<?php echo basename(__FILE__); ?>">Syndication Options</a>
1201
  (currently <strong><?php echo FeedWordPress::on_unfamiliar('author');; ?></strong>)</label></li>
1202
  <li><label><input type="radio" name="unfamiliar_author" value="create"<?php echo $unfamiliar['author']['create']; ?>/> create a new author account</label></li>
1203
  <li><label><input type="radio" name="unfamiliar_author" value="default"<?php echo $unfamiliar['author']['default']; ?> /> attribute the post to the default author</label></li>
@@ -1208,13 +1119,26 @@ flip_hardcode('url');
1208
  <tr>
1209
  <th width="20%" scope="row" style="vertical-align:top">Unfamiliar categories:</th>
1210
  <td width="80%"><ul style="margin: 0; list-style:none">
1211
- <li><label><input type="radio" name="unfamiliar_category" value="site-default"<?php echo $unfamiliar['category']['site-default']; ?> /> use site-wide setting from <a href="options-general.php?page=<?php echo basename(__FILE__); ?>">Syndication Options</a>
1212
  (currently <strong><?php echo FeedWordPress::on_unfamiliar('category');; ?></strong>)</label></li>
1213
  <li><label><input type="radio" name="unfamiliar_category" value="create"<?php echo $unfamiliar['category']['create']; ?> /> create any categories the post is in</label></li>
1214
  <li><label><input type="radio" name="unfamiliar_category" value="default"<?php echo $unfamiliar['category']['default']; ?> /> don't create new categories</label></li>
1215
  <li><label><input type="radio" name="unfamiliar_category" value="filter"<?php echo $unfamiliar['category']['filter']; ?> /> don't create new categories and don't syndicate posts unless they match at least one familiar category</label></li>
1216
  </ul></td>
1217
- </tr></table>
 
 
 
 
 
 
 
 
 
 
 
 
 
1218
  </fieldset>
1219
 
1220
  <p class="submit">
@@ -1237,9 +1161,9 @@ flip_hardcode('url');
1237
  if (!preg_match("\007^((".implode(')|(', $special_settings)."))$\007i", $key)) :
1238
  ?>
1239
  <tr style="vertical-align:top">
1240
- <th width="30%" scope="row"><input type="hidden" name="notes[<?php echo $i; ?>][key0]" value="<?php echo wp_specialchars($key); ?>" />
1241
- <input id="notes-<?php echo $i; ?>-key" name="notes[<?php echo $i; ?>][key1]" value="<?php echo wp_specialchars($key); ?>" /></th>
1242
- <td width="60%"><textarea rows="2" cols="40" id="notes-<?php echo $i; ?>-value" name="notes[<?php echo $i; ?>][value]"><?php echo wp_specialchars($value); ?></textarea></td>
1243
  <td width="10%"><select name="notes[<?php echo $i; ?>][action]">
1244
  <option value="update">save changes</option>
1245
  <option value="delete">delete this setting</option>
@@ -1278,8 +1202,8 @@ function fwp_multidelete_page () {
1278
 
1279
  if (!current_user_can('manage_links')):
1280
  die (__("Cheatin' uh ?"));
1281
- elseif (isset($_POST['confirm']) and $_POST['confirm']=='Delete'):
1282
- foreach ($_POST['link_action'] as $link_id => $what) :
1283
  $do_it[$what][] = $link_id;
1284
  endforeach;
1285
 
@@ -1359,7 +1283,7 @@ function fwp_multidelete_page () {
1359
  WHERE link_id IN (".implode(",",$link_ids).")
1360
  ");
1361
  ?>
1362
- <form action="link-manager.php?page=<?php echo basename(__FILE__); ?>" method="post">
1363
  <div class="wrap">
1364
  <input type="hidden" name="action" value="Unsubscribe" />
1365
  <input type="hidden" name="confirm" value="Delete" />
@@ -1368,9 +1292,8 @@ function fwp_multidelete_page () {
1368
  <?php foreach ($targets as $link) :
1369
  $link_url = wp_specialchars($link->link_url, 1);
1370
  $link_name = wp_specialchars($link->link_name, 1);
1371
- $link_description = wp_specialchars($link->link_description);
1372
- $link_rss = wp_specialchars($link->link_rss);
1373
- $meta = FeedWordPress::notes_to_settings($link->link_notes);
1374
  ?>
1375
  <fieldset>
1376
  <legend><?php echo $link_name; ?></legend>
@@ -1433,20 +1356,46 @@ function fwp_hold_pings () {
1433
  function fwp_release_pings () {
1434
  global $fwp_held_ping;
1435
  if ($fwp_held_ping):
1436
- generic_ping($fwp_held_ping);
 
 
 
 
1437
  endif;
1438
  $fwp_held_ping = NULL; // NULL: not holding pings anymore
1439
  }
1440
 
1441
- function fwp_catch_ping ($post_id = 0) {
1442
- global $fwp_held_ping;
1443
- if (!is_null($fwp_held_ping) and $post_id):
1444
  $fwp_held_ping = $post_id;
1445
- else:
 
 
1446
  generic_ping($fwp_held_ping);
1447
  endif;
1448
  }
1449
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1450
  ################################################################################
1451
  ## class FeedWordPress #########################################################
1452
  ################################################################################
@@ -1485,120 +1434,30 @@ class FeedWordPress {
1485
  array('script', 'src')
1486
  );
1487
 
1488
- var $_base = NULL;
1489
  var $feeds = NULL;
1490
 
1491
- # function FeedWordPress (): Contructor; gain list of feeds
1492
- #
1493
- # To keep things compact and editable from within WordPress, we use a
1494
- # category of the WordPress "Links" for our list of feeds to syndicate
1495
- # By default, we use all the links in the "Contributors" category.
1496
- # Fields used are:
1497
- #
1498
- # * link_rss: the URI of the Atom/RSS feed to syndicate
1499
- #
1500
- # * link_notes: user-configurable options, with keys and values
1501
- # like so:
1502
- #
1503
- # key: value
1504
- # cats: computers\nweb
1505
- # feed/key: value
1506
- #
1507
- # Keys that start with "feed/" are gleaned from the data supplied
1508
- # by the feed itself, and will be overwritten with each update.
1509
- #
1510
- # Values have linebreak characters escaped with C-style
1511
- # backslashes (so, for example, a newline becomes "\n").
1512
- #
1513
- # The value of `cats` is used as a newline-separated list of
1514
- # default categories for any post coming from a particular feed.
1515
- # (In the example above, any posts from this feed will be placed
1516
- # in the "computers" and "web" categories--*in addition to* any
1517
- # categories that may already be applied to the posts.)
1518
- #
1519
- # Values of keys in link_notes are accessible from templates using
1520
- # the function `get_feed_meta($key)` if this plugin is activated.
1521
-
1522
  function FeedWordPress () {
1523
- $result = FeedWordPress::syndicated_links();
1524
-
1525
- $feeds = array ();
1526
- if ($result): foreach ($result as $link):
1527
- if (strlen($link->link_rss) > 0):
1528
- $sec = FeedWordPress::notes_to_settings($link->link_notes);
1529
- $sec['link/uri'] = $link->link_rss;
1530
- $sec['link/name'] = $link->link_name;
1531
- $sec['link/id'] = $link->link_id;
1532
-
1533
- // `hardcode categories` is deprecated in favor
1534
- // of `unfamiliar categories`
1535
- if (
1536
- FeedWordPress::affirmative($sec, 'hardcode categories')
1537
- and !isset($sec['unfamiliar categories'])
1538
- ) :
1539
- $sec['unfamiliar categories'] = 'default';
1540
- endif;
1541
-
1542
- if (isset($sec['cats'])):
1543
- $sec['cats'] = preg_split(FEEDWORDPRESS_CAT_SEPARATOR_PATTERN, $sec['cats']);
1544
- endif;
1545
-
1546
- $feeds[] = $sec;
1547
- endif;
1548
  endforeach; endif;
1549
-
1550
- $this->feeds = $feeds;
1551
  } // FeedWordPress::FeedWordPress ()
1552
 
1553
- # function notes_to_settings (): Convert WordPress Link Notes to array
1554
- # of feed-level settings
1555
- #
1556
- # Arguments:
1557
- # ----------
1558
- # * $link_notes (string): the text from the Link Notes section of a link
1559
- #
1560
- # Returns:
1561
- # --------
1562
- # An associative array of settings stored in the Link Notes field. (For
1563
- # the `unfamiliar authors` setting, for example, simply look up the
1564
- # value of $meta['unfamiliar authors'], if $meta contains the value
1565
- # returned by `notes_to_settings()`.
1566
- #
1567
- # Values in FeedWordPress feed settings are escaped using C-style
1568
- # slashes. The escaped characters will already have been processed and
1569
- # converted in the returned array.
1570
- function notes_to_settings ($link_notes) {
1571
- $notes = explode("\n", $link_notes);
1572
-
1573
- $sec = array ();
1574
- foreach ($notes as $note):
1575
- list($key, $value) = explode(": ", $note, 2);
1576
-
1577
- if (strlen($key) > 0) :
1578
- // Unescape and trim() off the whitespace.
1579
- // Thanks to Ray Lischner for pointing out the
1580
- // need to trim off whitespace.
1581
- $sec[$key] = stripcslashes (trim($value));
1582
- endif;
1583
- endforeach;
1584
- return $sec;
1585
- } // FeedWordPress::notes_to_settings ()
1586
-
1587
  # function update (): polls for updates on one or more Contributor feeds
1588
  #
1589
  # Arguments:
1590
  # ----------
1591
  # * $uri (string): either the URI of the feed to poll, the URI of the
1592
- # website (human-readable link) whose feed you want to poll, or a
1593
- # "magic" tag: URI composed of the URI in the constant `RPC_MAGIC`
1594
- # and a "secret word" set in the FeedWordPress Options.
1595
  #
1596
- # If the "magic" URI is used, then FeedWordPress will poll any
1597
- # feeds that are ready for polling. It will not poll feeds that are
1598
- # marked as "Invisible" Links (signifying that the subscription has
1599
- # been de-activated), or feeds that are not yet stale according to
1600
- # their TTL setting (which is either set in the feed, or else
1601
- # set randomly within a window of 30 minutes - 2 hours).
1602
  #
1603
  # Returns:
1604
  # --------
@@ -1608,9 +1467,7 @@ class FeedWordPress {
1608
  # are zero, there was no change since the last poll on that URI.
1609
  #
1610
  # * Returns NULL if URI it was passed was not a URI that this
1611
- # installation of FeedWordPress syndicates (the most common cause
1612
- # of this error is attempts to poll all feeds, lacking, or using
1613
- # an incorrect, "secret word."
1614
  #
1615
  # Effects:
1616
  # --------
@@ -1637,39 +1494,30 @@ class FeedWordPress {
1637
  # * Updates to existing posts since the last poll are mirrored in the
1638
  # WordPress store.
1639
  #
1640
- function update ($uri) {
1641
  global $wpdb;
1642
 
1643
- $uri = trim($uri);
1644
-
1645
  if (FeedWordPress::needs_upgrade()) : // Will make duplicate posts if we don't hold off
1646
  return NULL;
1647
  endif;
 
 
 
 
 
 
1648
 
1649
  do_action('feedwordpress_update', $uri);
1650
 
1651
- // Secret voodoo tag: URI for updating *everything*.
1652
- $secret = RPC_MAGIC.FeedWordPress::rpc_secret();
1653
-
1654
- fwp_hold_pings(); // Only send out one ping for the whole to-do
1655
-
1656
  // Loop through and check for new posts
1657
- $delta = NULL;
1658
  foreach ($this->feeds as $feed) :
1659
- $pinged_that = in_array($uri, array($secret, $feed['link/uri'], $feed['feed/link']));
1660
 
1661
- if ($uri != $secret) : // A site-specific ping always updates
1662
- $timely = true;
1663
- elseif (isset($feed['update/hold']) and ($feed['update/hold']=='ping')) :
1664
- $timely = false;
1665
- elseif (isset($feed['update/hold']) and ($feed['update/hold']=='next')) :
1666
- $timely = true;
1667
- elseif (!isset($feed['update/ttl']) or !isset($feed['update/last'])) :
1668
  $timely = true;
1669
  else :
1670
- $after = ((int) $feed['update/last'])
1671
- +((int) $feed['update/ttl'] * 60);
1672
- $timely = (time() >= $after);
1673
  endif;
1674
 
1675
  if ($pinged_that and is_null($delta)) : // If at least one feed was hit for updating...
@@ -1677,375 +1525,352 @@ class FeedWordPress {
1677
  endif;
1678
 
1679
  if ($pinged_that and $timely) :
1680
- do_action('feedwordpress_check_feed', $feed);
1681
- $added = $this->feed2wp($wpdb, $feed);
1682
  if (isset($added['new'])) : $delta['new'] += $added['new']; endif;
1683
  if (isset($added['updated'])) : $delta['updated'] += $added['updated']; endif;
1684
  endif;
1685
  endforeach;
1686
 
1687
  do_action('feedwordpress_update_complete', $delta);
1688
- fwp_release_pings(); // Now that we're done, send the one ping
1689
 
1690
  return $delta;
1691
  }
1692
 
1693
- function feed2wp ($wpdb, $f) {
1694
- $feed = fetch_rss($f['link/uri']);
1695
- $new_count = array('new' => 0, 'updated' => 0);
1696
-
1697
- $this->update_feed($wpdb, $feed->channel, $f);
1698
-
1699
- if (is_array($feed->items)) :
1700
- foreach ($feed->items as $item) :
1701
- $post = $this->item_to_post($wpdb, $item, $feed, $f);
1702
- if (!is_null($post)) :
1703
- $new = $this->add_post($wpdb, $post);
1704
- if ( $new !== false ) $new_count[$new]++;
1705
- endif;
1706
- endforeach;
1707
- endif;
1708
- return $new_count;
1709
- } // function feed2wp ()
1710
 
1711
- // FeedWordPress::flatten_array (): flatten an array. Useful for
1712
- // hierarchical and namespaced elements.
1713
- //
1714
- // Given an array which may contain array or object elements in it,
1715
- // return a "flattened" array: a one-dimensional array of scalars
1716
- // containing each of the scalar elements contained within the array
1717
- // structure. Thus, for example, if $a['b']['c']['d'] == 'e', then the
1718
- // returned array for FeedWordPress::flatten_array($a) will contain a key
1719
- // $a['feed/b/c/d'] with value 'e'.
1720
- function flatten_array ($arr, $prefix = 'feed/', $separator = '/') {
1721
- $ret = array ();
1722
- if (is_array($arr)) :
1723
- foreach ($arr as $key => $value) :
1724
- if (is_scalar($value)) :
1725
- $ret[$prefix.$key] = $value;
1726
- else :
1727
- $ret = array_merge($ret, $this->flatten_array($value, $prefix.$key.$separator, $separator));
1728
  endif;
1729
- endforeach;
1730
- endif;
1731
- return $ret;
1732
- } // function FeedWordPress::flatten_array ()
1733
-
1734
- function resolve_relative_uri ($matches) {
1735
- return $matches[1].Relative_URI::resolve($matches[2], $this->_base).$matches[3];
1736
- } // function FeedWordPress::resolve_relative_uri ()
1737
-
1738
- function hardcode ($what, $f) {
1739
- $default = get_settings("feedwordpress_hardcode_$what");
1740
- if ( $default === 'yes' ) :
1741
- // If the default is to hardcode, then we want the
1742
- // negation of negative(): TRUE by default and FALSE if
1743
- // the setting is explicitly "no"
1744
- $ret = !FeedWordPress::negative($f, "hardcode $what");
1745
- else :
1746
- // If the default is NOT to hardcode, then we want
1747
- // affirmative(): FALSE by default and TRUE if the
1748
- // setting is explicitly "yes"
1749
- $ret = FeedWordPress::affirmative($f, "hardcode $what");
1750
  endif;
1751
  return $ret;
1752
  }
1753
 
1754
- function syndicated_status ($what, $f, $default) {
1755
  global $wpdb;
1756
 
1757
- $ret = get_settings("feedwordpress_syndicated_{$what}_status");
1758
- if ( isset($f["$what status"]) ) :
1759
- $ret = $f["$what status"];
1760
- elseif (!$ret) :
1761
- $ret = $default;
1762
- endif;
1763
- return $wpdb->escape(trim(strtolower($ret)));
1764
- }
1765
-
1766
- function negative ($f, $setting) {
1767
- $nego = array ('n', 'no', 'f', 'false');
1768
- return (isset($f[$setting]) and in_array(strtolower($f[$setting]), $nego));
1769
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
1770
 
1771
- function affirmative ($f, $setting) {
1772
- $affirmo = array ('y', 'yes', 't', 'true', 1);
1773
- return (isset($f[$setting]) and in_array(strtolower($f[$setting]), $affirmo));
1774
- }
1775
-
1776
- function feed_ttl ($channel) {
1777
- if (isset($channel['ttl'])) :
1778
- // "ttl stands for time to live. It's a number of
1779
- // minutes that indicates how long a channel can be
1780
- // cached before refreshing from the source."
1781
- // <http://blogs.law.harvard.edu/tech/rss#ltttlgtSubelementOfLtchannelgt>
1782
- $ret = $channel['ttl'];
1783
- elseif (isset($channel['sy']['updatefrequency']) or isset($channel['sy']['updateperiod'])) :
1784
- $period_minutes = array (
1785
- 'hourly' => 60, /* minutes in an hour */
1786
- 'daily' => 1440, /* minutes in a day */
1787
- 'weekly' => 10080, /* minutes in a week */
1788
- 'monthly' => 43200, /* minutes in a month */
1789
- 'yearly' => 525600, /* minutes in a year */
1790
- );
1791
 
1792
- // "sy:updatePeriod: Describes the period over which the
1793
- // channel format is updated. Acceptable values are:
1794
- // hourly, daily, weekly, monthly, yearly. If omitted,
1795
- // daily is assumed." <http://web.resource.org/rss/1.0/modules/syndication/>
1796
- if (isset($channel['sy']['updateperiod'])) : $period = $channel['sy']['updateperiod'];
1797
- else : $period = 'daily';
1798
- endif;
1799
-
1800
- // "sy:updateFrequency: Used to describe the frequency
1801
- // of updates in relation to the update period. A
1802
- // positive integer indicates how many times in that
1803
- // period the channel is updated. ... If omitted a value
1804
- // of 1 is assumed." <http://web.resource.org/rss/1.0/modules/syndication/>
1805
- if (isset($channel['sy']['updatefrequency'])) : $freq = (int) $channel['sy']['updatefrequency'];
1806
- else : $freq = 1;
1807
  endif;
1808
-
1809
- $ret = (int) ($period_minutes[$period] / $freq);
1810
- else :
1811
- $ret = NULL;
1812
  endif;
1813
- return $ret;
1814
- }
1815
 
1816
- function update_feed ($wpdb, $channel, $f) {
1817
- $link_id = $f['link/id'];
1818
 
1819
- if (!isset($channel['id'])) :
1820
- $channel['id'] = $f['link/uri'];
 
 
 
 
1821
  endif;
 
 
1822
 
1823
- $update = array();
1824
- if (!FeedWordPress::hardcode('url', $f) and isset($channel['link'])) :
1825
- $update[] = "link_url = '".$wpdb->escape($channel['link'])."'";
1826
- endif;
1827
 
1828
- if (!FeedWordPress::hardcode('name', $f) and isset($channel['title'])) :
1829
- $update[] = "link_name = '".$wpdb->escape($channel['title'])."'";
1830
- endif;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1831
 
1832
- if (!FeedWordPress::hardcode('description', $f)) :
1833
- if (isset($channel['tagline'])) :
1834
- $update[] = "link_description = '".$wpdb->escape($channel['tagline'])."'";
1835
- elseif (isset($channel['description'])) :
1836
- $update[] = "link_description = '".$wpdb->escape($channel['description'])."'";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1837
  endif;
1838
- endif;
1839
 
1840
- if (is_array($f['cats'])) :
1841
- $f['cats'] = implode(FEEDWORDPRESS_CAT_SEPARATOR, $f['cats']);
1842
  endif;
 
 
1843
 
1844
- $f = array_merge($f, $this->flatten_array($channel));
1845
-
1846
- $f['update/last'] = time();
1847
- $ttl = $this->feed_ttl($channel);
1848
- if (!is_null($ttl)) :
1849
- $f['update/ttl'] = $ttl;
1850
- $f['update/timed'] = 'feed';
1851
- else :
1852
- $f['update/ttl'] = rand(30, 120); // spread over time interval for staggered updates
1853
- $f['update/timed'] = 'automatically';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1854
  endif;
 
 
1855
 
1856
- if (!isset($f['update/hold']) or $f['update/hold']!='ping') :
1857
- $f['update/hold'] = 'scheduled';
1858
- endif;
1859
 
1860
- # -- A few things we don't want to save in the notes
1861
- unset($f['link/id']); unset($f['link/uri']);
1862
- unset($f['link/name']);
1863
- unset($f['hardcode categories']); // Deprecated
1864
 
1865
- $notes = '';
1866
- foreach ($f as $key => $value) :
1867
- $notes .= $key . ": ". addcslashes($value, "\0..\37") . "\n";
1868
- endforeach;
1869
- $update[] = "link_notes = '".$wpdb->escape($notes)."'";
1870
 
1871
- $update_set = implode(',', $update);
1872
-
1873
- // Update the properties of the link from the feed information
1874
- $result = $wpdb->query("
1875
- UPDATE $wpdb->links
1876
- SET $update_set
1877
- WHERE link_id='$link_id'
1878
- ");
1879
- } // function FeedWordPress::update_feed ()
1880
-
1881
- function date_created ($item) {
1882
- if (isset($item['dc']['created'])) :
1883
- $epoch = @parse_w3cdtf($item['dc']['created']);
1884
- elseif (isset($item['dcterms']['created'])) :
1885
- $epoch = @parse_w3cdtf($item['dcterms']['created']);
1886
- elseif (isset($item['created'])): // Atom 0.3
1887
- $epoch = @parse_w3cdtf($item['created']);
1888
- endif;
1889
- return $epoch;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1890
  }
1891
 
1892
- function guid ($item, $feed) {
1893
- if (isset($item['id'])): // Atom 0.3 / 1.0
1894
- $guid = $item['id'];
1895
- elseif (isset($item['atom']['id'])) : // Namespaced Atom
1896
- $guid = $item['atom']['id'];
1897
- elseif (isset($item['guid'])) : // RSS 2.0
1898
- $guid = $item['guid'];
1899
- elseif (isset($item['dc']['identifier'])) : // yeah, right
1900
- $guid = $item['dc']['identifier'];
1901
- else :
1902
- // The feed does not seem to have provided us with a
1903
- // unique identifier, so we'll have to cobble together
1904
- // a tag: URI that might work for us. The base of the
1905
- // URI will be the host name of the feed source ...
1906
- $bits = parse_url($feed['link/uri']);
1907
- $guid = 'tag:'.$bits['host'];
1908
 
1909
- // If we have a date of creation, then we can use that
1910
- // to uniquely identify the item. (On the other hand, if
1911
- // the feed producer was consicentious enough to
1912
- // generate dates of creation, she probably also was
1913
- // conscientious enough to generate unique identifiers.)
1914
- if (!is_null(FeedWordPress::date_created($item))) :
1915
- $guid .= '://post.'.date('YmdHis', FeedWordPress::date_created($item));
1916
-
1917
- // Otherwise, use both the URI of the item, *and* the
1918
- // item's title. We have to use both because titles are
1919
- // often not unique, and sometimes links aren't unique
1920
- // either (e.g. Bitch (S)HITLIST, Mozilla Dot Org news,
1921
- // some podcasts). But it's rare to have *both* the same
1922
- // title *and* the same link for two different items. So
1923
- // this is about the best we can do.
1924
- else :
1925
- $guid .= '://'.md5($item['link'].'/'.$item['title']);
1926
- endif;
1927
- endif;
1928
- return $guid;
1929
  }
 
1930
 
1931
- // item_to_post(): convert information from a single item from an
1932
- // Atom/RSS feed to a post for WordPress's database.
1933
- //
1934
- // item_to_post() invokes the syndicated_item filter on each item it
1935
- // receives. Filters should return either (a) the item unmodified,
1936
- // (b) the item modified according to the rules of the filter, or
1937
- // (c) NULL. A NULL item will not be posted into the database.
1938
- //
1939
- // N.B.: item_to_post and the syndicate_item filter really ought to have
1940
- // *no* side effects on the WordPress database (that's why, for example,
1941
- // we handle lookup/creation of numeric author and category IDs in
1942
- // add_post()). If you want plugins that have side effects on the posts
1943
- // database, you should probably hook into the action
1944
- // post_syndicated_item
1945
- function item_to_post($wpdb, $item, $rss, $f) {
1946
- $channel = $rss->channel;
1947
-
1948
- $post = array();
1949
-
1950
- // This is ugly as all hell. I'd like to use apply_filters()'s
1951
- // alleged support for a variable argument count, but this seems
1952
- // to have been broken in WordPress 1.5. It'll be fixed somehow
1953
- // in WP 1.5.1, but I'm aiming at WP 1.5 compatibility across
1954
- // the board here.
1955
- //
1956
- // Cf.: <http://mosquito.wordpress.org/view.php?id=901>
 
 
 
 
 
 
1957
  global $fwp_channel, $fwp_feedmeta;
1958
- $fwp_channel = $channel; $fwp_feedmeta = $f;
1959
-
1960
- $item = apply_filters('syndicated_item', $item);
1961
-
1962
- // Filters can halt further processing by returning NULL
1963
- if (is_null($item)) :
1964
- $post = NULL;
 
 
 
 
1965
  else :
1966
- $post['post_title'] = $wpdb->escape($item['title']);
 
 
 
1967
 
1968
- $post['named']['author'] = array ();
1969
-
1970
- if (isset($item['author_name'])):
1971
- $post['named']['author']['name'] = $item['author_name'];
1972
- elseif (isset($item['dc']['creator'])):
1973
- $post['named']['author']['name'] = $item['dc']['creator'];
1974
- elseif (isset($item['dc']['contributor'])):
1975
- $post['named']['author']['name'] = $item['dc']['contributor'];
1976
- elseif (isset($channel['dc']['creator'])) :
1977
- $post['named']['author']['name'] = $channel['dc']['creator'];
1978
- elseif (isset($channel['dc']['contributor'])) :
1979
- $post['named']['author']['name'] = $channel['dc']['contributor'];
1980
- elseif (isset($channel['author_name'])) :
1981
- $post['named']['author']['name'] = $channel['author_name'];
1982
- elseif ($rss->is_rss() and isset($item['author'])) :
1983
- // The author element in RSS is allegedly an
1984
- // e-mail address, but lots of people don't use
1985
- // it that way. So let's make of it what we can.
1986
- $post['named']['author'] = parse_email_with_realname($item['author']);
1987
-
1988
- if (!isset($post['named']['author']['name'])) :
1989
- if (isset($post['named']['author']['email'])) :
1990
- $post['named']['author']['name'] = $post['named']['author']['email'];
1991
- else :
1992
- $post['named']['author']['name'] = $channel['title'];
1993
- endif;
1994
- endif;
1995
- else :
1996
- $post['named']['author']['name'] = $channel['title'];
1997
- endif;
1998
-
1999
- if (isset($item['author_email'])):
2000
- $post['named']['author']['email'] = $item['author_email'];
2001
- elseif (isset($channel['author_email'])) :
2002
- $post['named']['author']['email'] = $channel['author_email'];
2003
- endif;
2004
-
2005
- if (isset($item['author_url'])):
2006
- $post['named']['author']['uri'] = $item['author_url'];
2007
- elseif (isset($channel['author_url'])) :
2008
- $post['named']['author']['uri'] = $item['author_url'];
2009
- else:
2010
- $post['named']['author']['uri'] = $channel['link'];
2011
- endif;
2012
 
2013
- // ... So far we just have an alphanumeric
2014
- // representation of the author. We will look up (or
2015
- // create) the numeric ID for the author in
2016
- // FeedWordPress::add_post()
2017
 
2018
  # Identify content and sanitize it.
2019
  # ---------------------------------
2020
- if (isset($item['xhtml']['body'])) :
2021
- $content = $item['xhtml']['body'];
2022
- elseif (isset($item['xhtml']['div'])) :
2023
- $content = $item['xhtml']['div'];
2024
- elseif (isset($item['content']['encoded']) and $item['content']['encoded']):
2025
- $content = $item['content']['encoded'];
2026
  else:
2027
- $content = $item['description'];
2028
  endif;
2029
 
2030
- # Resolve relative URIs in post content
 
 
 
2031
  #
2032
- # N.B.: We *might* get screwed over by xml:base. But I don't see
2033
- # any way to get that information out of MagpieRSS if it's
2034
- # in the feed, and if it's in the content itself we'd have
2035
- # to do yet more XML parsing to do things right. For now
2036
- # this will have to do.
2037
-
2038
- $this->_base = $item['link']; // Reset the base for resolving relative URIs
2039
- foreach ($this->uri_attrs as $pair):
2040
- list($tag,$attr) = $pair;
2041
- $content = preg_replace_callback (
2042
- ":(<$tag [^>]*$attr=\")([^\">]*)(\"[^>]*>):i",
2043
- array(&$this,'resolve_relative_uri'),
2044
- $content
2045
- );
2046
- endforeach;
2047
-
2048
- # Sanitize problematic attributes
2049
  foreach ($this->strip_attrs as $pair):
2050
  list($tag,$attr) = $pair;
2051
  $content = preg_replace (
@@ -2057,8 +1882,8 @@ class FeedWordPress {
2057
 
2058
  # Identify and sanitize excerpt
2059
  $excerpt = NULL;
2060
- if ( isset($item['description']) and $item['description'] ) :
2061
- $excerpt = $item['description'];
2062
  elseif ( isset($content) and $content ) :
2063
  $excerpt = strip_tags($content);
2064
  if (strlen($excerpt) > 255) :
@@ -2066,193 +1891,211 @@ class FeedWordPress {
2066
  endif;
2067
  endif;
2068
 
2069
- $post['post_content'] = $wpdb->escape($content);
2070
 
2071
  if (!is_null($excerpt)):
2072
- $post['post_excerpt'] = $wpdb->escape($excerpt);
2073
  endif;
2074
 
2075
- # This is unneeded if wp_insert_post can be used.
2076
- # --- cut here ---
2077
- $post['post_name'] = sanitize_title($post['post_title']);
2078
- # --- cut here ---
2079
-
2080
- # RSS is a fucking mess. Figure out whether we have a date in
2081
- # dc:date, <issued>, <pubDate>, etc., and get it into Unix epoch
2082
- # format for reformatting. If you can't find anything, use the
2083
- # current time.
2084
- if (isset($item['dc']['date'])):
2085
- $post['epoch']['issued'] = parse_w3cdtf($item['dc']['date']);
2086
- elseif (isset($item['dcterms']['issued'])) :
2087
- $post['epoch']['issued'] = parse_w3cdtf($item['dcterms']['issued']);
2088
- elseif (isset($item['published'])) : // Atom 1.0
2089
- $post['epoch']['issued'] = parse_w3cdtf($item['published']);
2090
- elseif (isset($item['issued'])): // Atom 0.3
2091
- $post['epoch']['issued'] = parse_w3cdtf($item['issued']);
2092
- elseif (isset($item['pubdate'])): // RSS 2.0
2093
- $post['epoch']['issued'] = strtotime($item['pubdate']);
2094
- else:
2095
- $post['epoch']['issued'] = null;
2096
  endif;
2097
 
2098
- # And again, for the created date
2099
- $post['epoch']['created'] = FeedWordPress::date_created($item);
2100
-
2101
- # As far as I know, only atom currently has a reliable way to
2102
- # specify when something was *modified* last
2103
- if (isset($item['dc']['modified'])) : // Not really correct
2104
- $post['epoch']['modified'] = @parse_w3cdtf($item['dc']['modified']);
2105
- elseif (isset($item['dcterms']['modified'])) : // Dublin Core extensions
2106
- $post['epoch']['modified'] = @parse_w3cdtf($item['dcterms']['modified']);
2107
- elseif (isset($item['modified'])): // Atom 0.3
2108
- $post['epoch']['modified'] = @parse_w3cdtf($item['modified']);
2109
- elseif (isset($item['updated'])): // Atom 1.0
2110
- $post['epoch']['modified'] = @parse_w3cdtf($item['updated']);
2111
- elseif (isset($post['epoch']['issued'])) : // Fall back to issued / dc:date
2112
- $post['epoch']['modified'] = $post['epoch']['issued'];
2113
- else :
2114
- $post['epoch']['modified'] = time();
2115
- endif;
2116
 
2117
- $post['post_date'] = date('Y-m-d H:i:s', (!is_null($post['epoch']['issued']) ? $post['epoch']['issued'] : $post['epoch']['modified']));
2118
- $post['post_modified'] = date('Y-m-d H:i:s', $post['epoch']['modified']);
2119
- $post['post_date_gmt'] = gmdate('Y-m-d H:i:s', (!is_null($post['epoch']['issued']) ? $post['epoch']['issued'] : $post['epoch']['modified']));
2120
- $post['post_modified_gmt'] = gmdate('Y-m-d H:i:s', $post['epoch']['modified']);
 
 
2121
 
2122
- # Use feed-level preferences or the global default.
2123
- $post['post_status'] = FeedWordPress::syndicated_status('post', $f, 'publish');
2124
- $post['comment_status'] = FeedWordPress::syndicated_status('comment', $f, 'closed');
2125
- $post['ping_status'] = FeedWordPress::syndicated_status('ping', $f, 'closed');
2126
 
2127
  // Unique ID (hopefully a unique tag: URI); failing that, the permalink
2128
- $post['guid'] = $wpdb->escape(FeedWordPress::guid($item, $f));
2129
 
2130
  // RSS 2.0 / Atom 1.0 enclosure support
2131
- if ( isset($item['enclosure#']) ) :
2132
- for ($i = 1; $i <= $item['enclosure#']; $i++) :
2133
  $eid = (($i > 1) ? "#{$id}" : "");
2134
- $post['meta']['enclosure'][] =
2135
- $item["enclosure{$eid}@url"]."\n".
2136
- $item["enclosure{$eid}@length"]."\n".
2137
- $item["enclosure{$eid}@type"];
2138
  endfor;
2139
  endif;
2140
 
2141
  // In case you want to point back to the blog this was syndicated from
2142
- if (isset($channel['title'])) $post['meta']['syndication_source'] = $channel['title'];
2143
- if (isset($channel['link'])) $post['meta']['syndication_source_uri'] = $channel['link'];
 
 
 
 
2144
 
2145
  // Store information on human-readable and machine-readable comment URIs
2146
- if (isset($item['comments'])) : $post['meta']['rss:comments'] = $item['comments']; endif;
2147
- if (isset($item['wfw']['commentrss'])) : $post['meta']['wfw:commentRSS'] = $item['wfw']['commentrss']; endif;
 
 
 
 
2148
 
2149
  // Store information to identify the feed that this came from
2150
- $post['meta']['syndication_feed'] = $f['link/uri'];
2151
- $post['meta']['syndication_feed_id'] = $f['link/id'];
2152
 
2153
  // In case you want to know the external permalink...
2154
- $post['meta']['syndication_permalink'] = $item['link'];
2155
 
2156
  // Feed-by-feed options for author and category creation
2157
- $post['named']['unfamiliar']['author'] = $f['unfamiliar author'];
2158
- $post['named']['unfamiliar']['category'] = $f['unfamiliar categories'];
2159
 
2160
- // Categories: start with default categories
2161
- $fc = get_settings("feedwordpress_syndication_cats");
2162
- if ($fc) : $post['named']['preset/category'] = explode("\n", $fc);
2163
- else : $post['named']['preset/category'] = array();
 
 
2164
  endif;
2165
- if (is_array($f['cats'])) :
2166
- $post['named']['preset/category'] = array_merge($post['named']['preset/category'], $f['cats']);
 
2167
  endif;
2168
 
2169
  // Now add categories from the post, if we have 'em
2170
- $post['named']['category'] = array();
2171
- if ( isset($item['category#']) ) :
2172
- for ($i = 1; $i <= $item['category#']; $i++) :
2173
  $cat_idx = (($i > 1) ? "#{$i}" : "");
2174
- $cat = $item["category{$cat_idx}"];
2175
 
2176
- if ( strpos($f['link/uri'], 'del.icio.us') !== false ):
2177
- $post['named']['category'] = array_merge($post['named']['category'], explode(' ', $cat));
2178
- else:
2179
- $post['named']['category'][] = $cat;
 
2180
  endif;
2181
  endfor;
2182
  endif;
2183
  endif;
2184
- return $post;
2185
- } // function FeedWordPress::item_to_post ()
2186
-
2187
- function add_post ($wpdb, $post) {
2188
- $guid = $post['guid'];
2189
- $result = $wpdb->get_row("
2190
- SELECT id, guid, UNIX_TIMESTAMP(post_modified) AS modified
2191
- FROM $wpdb->posts WHERE guid='$guid'
2192
- ");
2193
-
2194
- if (!$result) :
2195
- $freshness = 2; // New content
2196
- elseif ($post['epoch']['modified'] > $result->modified) :
2197
- $freshness = 1; // Updated content
2198
- else :
2199
- $freshness = 0;
2200
  endif;
 
 
 
 
 
 
 
 
2201
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2202
  if ($freshness > 0) :
2203
  # -- Look up, or create, numeric ID for author
2204
- $post['post_author'] = $this->author_to_id (
2205
- $wpdb,
2206
- $post['named']['author']['name'],
2207
- $post['named']['author']['email'],
2208
- $post['named']['author']['uri'],
2209
- FeedWordPress::on_unfamiliar('author', $post['named']['unfamiliar']['author'])
2210
  );
2211
 
2212
- if (is_null($post['post_author'])) :
2213
- $freshness = 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
2214
  else :
2215
- # -- Look up, or create, numeric ID for categories
2216
- $post['post_category'] = $this->lookup_categories (
2217
- $wpdb,
2218
- $post['named']['category'],
2219
- FeedWordPress::on_unfamiliar('category', $post['named']['unfamiliar']['category'])
 
 
2220
  );
2221
-
2222
- if (is_null($post['post_category'])) : // filter mode on, no matching categories; drop the post
2223
- $freshness = 0;
2224
- else : // filter mode off or at least one match; now add on the feed and global presets
2225
- $post['post_category'] = array_merge (
2226
- $post['post_category'],
2227
- $this->lookup_categories (
2228
- $wpdb,
2229
- $post['named']['preset/category'],
2230
- 'default'
2231
- )
2232
- );
2233
  endif;
2234
  endif;
2235
-
2236
- unset($post['named']);
2237
  endif;
2238
 
2239
- if ($freshness > 0) :
2240
- $post = apply_filters('syndicated_post', $post);
2241
- if (is_null($post)) $freshness = 0;
2242
  endif;
2243
 
2244
- if ($freshness == 2) :
2245
  // The item has not yet been added. So let's add it.
2246
- $postId = $this->insert_new_post($post);
2247
- $this->add_rss_meta($wpdb, $postId, $post);
2248
- do_action('post_syndicated_item', $postId);
2249
 
2250
  $ret = 'new';
2251
- elseif ($freshness == 1) :
2252
- $post['ID'] = $result->id; $modified = $result->modified;
2253
- $this->update_existing_post($post);
2254
- $this->add_rss_meta($wpdb, $post['ID'], $post);
2255
- do_action('update_syndicated_item', $post['ID']);
2256
 
2257
  $ret = 'updated';
2258
  else :
@@ -2260,108 +2103,155 @@ class FeedWordPress {
2260
  endif;
2261
 
2262
  return $ret;
2263
- } // function FeedWordPress::add_post ()
2264
-
2265
- function insert_new_post ($post) {
2266
- global $wpdb;
2267
 
2268
- $guid = $post['guid'];
 
 
 
 
 
 
 
2269
 
2270
- # The right way to do this would be to use:
2271
- #
2272
- # $postId = wp_insert_post($post);
2273
- # $result = $wpdb->query("
2274
- # UPDATE $wpdb->posts
2275
- # SET
2276
- # guid='$guid'
2277
- # WHERE post_id='$postId'
2278
- # ");
2279
- #
2280
- # in place of everything in the cut below. Alas,
2281
- # wp_insert_post seems to be a memory hog; using it
2282
- # to insert several posts in one session makes php
2283
- # segfault after inserting 50-100 posts. This can get
2284
- # pretty annoying, especially if you are trying to
2285
- # update your feeds for the first time.
2286
- #
2287
- # --- cut here ---
2288
- $result = $wpdb->query("
2289
- INSERT INTO $wpdb->posts
2290
- SET
2291
- guid = '$guid',
2292
- post_author = '".$post['post_author']."',
2293
- post_date = '".$post['post_date']."',
2294
- post_date_gmt = '".$post['post_date_gmt']."',
2295
- post_content = '".$post['post_content']."',"
2296
- .(isset($post['post_excerpt']) ? "post_excerpt = '".$post['post_excerpt']."'," : "")."
2297
- post_title = '".$post['post_title']."',
2298
- post_name = '".$post['post_name']."',
2299
- post_modified = '".$post['post_modified']."',
2300
- post_modified_gmt = '".$post['post_modified_gmt']."',
2301
- comment_status = '".$post['comment_status']."',
2302
- ping_status = '".$post['ping_status']."',
2303
- post_status = '".$post['post_status']."'
2304
- ");
2305
- $postId = $wpdb->insert_id;
2306
- $this->add_to_category($wpdb, $postId, $post['post_category']);
2307
-
2308
- // Since we are not going through official channels, we need to
2309
- // manually tell WordPress that we've published a new post.
2310
- // We need to make sure to do this in order for FeedWordPress
2311
- // to play well with the staticize-reloaded plugin (something
2312
- // that a large aggregator website is going to *want* to be
2313
- // able to use).
2314
- do_action('publish_post', $postId);
2315
- # --- cut here ---
2316
-
2317
- return $postId;
2318
- } /* FeedWordPress::insert_new_post() */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2319
 
2320
- function update_existing_post ($post) {
2321
  global $wpdb;
2322
-
2323
- $guid = $post['guid'];
2324
- $postId = $post['ID'];
2325
-
2326
- $result = $wpdb->query("
2327
- UPDATE $wpdb->posts
2328
- SET
2329
- post_author = '".$post['post_author']."',
2330
- post_content = '".$post['post_content']."',
2331
- post_title = '".$post['post_title']."',
2332
- post_name = '".$post['post_name']."',
2333
- post_modified = '".$post['post_modified']."',
2334
- post_modified_gmt = '".$post['post_modified_gmt']."'
2335
- WHERE guid='$guid'
2336
- ");
2337
- $this->add_to_category($wpdb, $postId, $post['post_category']);
2338
-
2339
- // Since we are not going through official channels, we need to
2340
- // manually tell WordPress that we've published a new post.
2341
- // We need to make sure to do this in order for FeedWordPress
2342
- // to play well with the staticize-reloaded plugin (something
2343
- // that a large aggregator website is going to *want* to be
2344
- // able to use).
2345
- do_action('edit_post', $postId);
2346
- } /* FeedWordPress::update_existing_post() */
2347
-
2348
- # function FeedWordPress::add_to_category ()
2349
- #
2350
- # If there is a way to properly hook in to wp_insert_post, then this
2351
- # function will no longer be needed. In the meantime, here it is.
2352
- # --- cut here ---
2353
- function add_to_category($wpdb, $postId, $post_categories) {
2354
- global $wp_db_version; // test for WordPress 2.0 database schema
2355
 
2356
- // Default to category 1 ("Uncategorized"), if nothing else
2357
- if (!$post_categories) $post_categories[] = 1;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2358
 
2359
- // Now pass the buck to the WordPress API...
2360
- wp_set_post_cats('', $postId, $post_categories);
2361
- } // function FeedWordPress::add_to_category ()
2362
- # --- cut here ---
 
 
 
 
 
 
2363
 
2364
- // FeedWordPress::add_rss_meta: adds interesting meta-data to each entry
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2365
  // using the space for custom keys. The set of keys and values to add is
2366
  // specified by the keys and values of $post['meta']. This is used to
2367
  // store anything that the WordPress user might want to access from a
@@ -2370,9 +2260,21 @@ class FeedWordPress {
2370
  // syndicated post other than author, title, timestamp, categories, and
2371
  // guid). It's also used to hook into WordPress's support for
2372
  // enclosures.
2373
- function add_rss_meta ($wpdb, $postId, $post) {
2374
- if ( is_array($post) and isset($post['meta']) and is_array($post['meta']) ) :
2375
- foreach ( $post['meta'] as $key => $values ) :
 
 
 
 
 
 
 
 
 
 
 
 
2376
 
2377
  $key = $wpdb->escape($key);
2378
 
@@ -2397,12 +2299,17 @@ class FeedWordPress {
2397
  endforeach;
2398
  endforeach;
2399
  endif;
2400
- } /* FeedWordPress::add_rss_meta () */
2401
 
2402
- // FeedWordPress::author_to_id (): get the ID for an author name from
2403
  // the feed. Create the author if necessary.
2404
- function author_to_id ($wpdb, $author, $email, $url, $unfamiliar_author = 'create') {
2405
- global $wp_db_version; // test for WordPress 2.0 database schema
 
 
 
 
 
2406
 
2407
  // Never can be too careful...
2408
  $nice_author = sanitize_title($author);
@@ -2429,7 +2336,7 @@ class FeedWordPress {
2429
  (
2430
  LOWER(user_description)
2431
  RLIKE CONCAT(
2432
- '(^|\\n)a.k.a.( |\\t)*:?( |\\t)*',
2433
  LCASE('$reg_author'),
2434
  '( |\\t|\\r)*(\\n|\$)'
2435
  )
@@ -2459,7 +2366,7 @@ class FeedWordPress {
2459
  meta_key = 'description'
2460
  AND LCASE(meta_value)
2461
  RLIKE CONCAT(
2462
- '(^|\\n)a.k.a.( |\\t)*:?( |\\t)*',
2463
  LCASE('$reg_author'),
2464
  '( |\\t|\\r)*(\\n|\$)'
2465
  )
@@ -2492,7 +2399,7 @@ class FeedWordPress {
2492
  #-- user table data
2493
  $userdata['ID'] = NULL; // new user
2494
  $userdata['user_login'] = $author;
2495
- $userdata['user_pass'] = FeedWordPress::rpc_secret();
2496
  $userdata['user_email'] = $email;
2497
  $userdata['user_url'] = $url;
2498
  $userdata['display_name'] = $author;
@@ -2504,326 +2411,571 @@ class FeedWordPress {
2504
  endif;
2505
  endif;
2506
  return $id;
2507
- } // function FeedWordPress::author_to_id ()
2508
 
2509
  // look up (and create) category ids from a list of categories
2510
- function lookup_categories ($wpdb, $cats, $unfamiliar_category = 'create') {
2511
- // Normalize whitespace because (1) trailing whitespace can
2512
- // cause PHP and MySQL not to see eye to eye on VARCHAR
2513
- // comparisons for some versions of MySQL (cf.
 
 
2514
  // <http://dev.mysql.com/doc/mysql/en/char.html>), and (2)
2515
  // because I doubt most people want to make a semantic
2516
  // distinction between 'Computers' and 'Computers '
2517
  $cats = array_map('trim', $cats);
2518
 
2519
  $cat_ids = array ();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2520
 
2521
- if ( count($cats) > 0 ) :
2522
- # i'd kill for a decent map function in PHP
2523
- # but that would require functions to be first class object,
2524
- # or at least coderef support
2525
- $cat_str = array ();
2526
- $cat_aka = array ();
2527
- foreach ( $cats as $c ) :
2528
- $resc = $wpdb->escape(preg_quote($c));
2529
- $esc = $wpdb->escape($c);
2530
- $cat_str[] = "'$esc'";
2531
-
2532
- $cat_aka[] = "(LOWER(category_description)
2533
- RLIKE CONCAT('(^|\n)a.k.a.( |\t)*:?( |\t)*', LOWER('{$resc}'), '( |\t|\r)*(\n|\$)'))";
2534
- endforeach;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2535
 
2536
- $match_cat_name = 'cat_name IN ('.join(',', $cat_str).')';
2537
- $match_cat_alias = join(' OR ', $cat_aka);
2538
 
2539
- $results = $wpdb->get_results(
2540
- "SELECT
2541
- cat_ID,
2542
- cat_name,
2543
- category_description
2544
- FROM $wpdb->categories
2545
- WHERE ($match_cat_name) OR ($match_cat_alias)"
2546
- );
2547
-
2548
- $cat_ids = array();
2549
- $found = array();
2550
-
2551
- if (!is_null($results)):
2552
- foreach ( $results as $row ) :
2553
- // Add existing ID to list of numerical
2554
- // IDs to eventually place post in
2555
- $cat_ids[] = $row->cat_ID;
2556
-
2557
- // Add name to list of categories not to
2558
- // create afresh. Normalizing case with
2559
- // strtolower() avoids mismatches in
2560
- // VARCHAR comparison between PHP (which
2561
- // has case-sensitive comparisons) and
2562
- // MySQL (which has case-insensitive
2563
- // comparisons for the field types used
2564
- // by WordPress)
2565
- $found[] = strtolower(trim($row->cat_name));
2566
-
2567
- // Add name of any aliases to list of
2568
- // categories not to create afresh.
2569
- if (preg_match_all('/^a.k.a. \s* :? \s* (.*\S) \s*$/mx',
2570
- $row->category_description, $aka,
2571
- PREG_PATTERN_ORDER)) :
2572
- $found = array_merge (
2573
- $found,
2574
- array_map('strtolower',
2575
- array_map('trim',
2576
- $aka[1]
2577
- ))
2578
- );
2579
- endif;
2580
- endforeach;
2581
- endif;
2582
 
2583
- foreach ($cats as $new_cat) :
2584
- if (($unfamiliar_category==='create') and !in_array(strtolower($new_cat), $found)) :
2585
- $nice_cat = sanitize_title($new_cat);
2586
- $wpdb->query(sprintf("
2587
- INSERT INTO $wpdb->categories
2588
- SET
2589
- cat_name='%s',
2590
- category_nicename='%s'
2591
- ", $wpdb->escape($new_cat), $nice_cat));
2592
- $cat_ids[] = $wpdb->insert_id;
2593
- endif;
2594
- endforeach;
2595
 
2596
- if ((count($cat_ids) == 0) and ($unfamiliar_category === 'filter')) :
2597
- $cat_ids = NULL; // Drop the post
 
 
 
 
 
 
 
2598
  endif;
2599
  endif;
2600
- return $cat_ids;
2601
- } // function FeedWordPress::lookup_categories ()
2602
 
2603
- function rpc_secret () {
2604
- return get_settings('feedwordpress_rpc_secret');
2605
- } // function FeedWordPress::rpc_secret ()
2606
-
2607
- function on_unfamiliar ($what = 'author', $override = NULL) {
2608
- $set = array('create', 'default', 'filter');
2609
-
2610
- $ret = strtolower($override);
2611
- if (!in_array($ret, $set)) :
2612
- $ret = get_settings('feedwordpress_unfamiliar_'.$what);
2613
- if (!in_array($ret, $set)) :
2614
- $ret = 'create';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2615
  endif;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2616
  endif;
2617
 
2618
- return $ret;
2619
- } // function FeedWordPress::on_unfamiliar()
2620
-
2621
- function syndicated_links () {
2622
- $contributors = FeedWordPress::link_category_id();
2623
- if (function_exists('get_bookmarks')) {
2624
- $links = get_bookmarks(array("category" => $contributors));
2625
- } else {
2626
- $links = get_linkobjects($contributors); // deprecated as of WP 2.1
2627
- } // if
2628
- return $links;
2629
- } // function syndicated_links()
2630
-
2631
- function syndicate_link ($name, $uri, $rss) {
2632
- global $wpdb;
2633
-
2634
- // Get the category ID#
2635
- $cat_id = FeedWordPress::link_category_id();
2636
 
2637
- if (function_exists('wp_insert_link')) { // WordPress 2.x
2638
- $link_id = wp_insert_link(array(
2639
- "link_name" => $name,
2640
- "link_url" => $uri,
2641
- "link_category" => array($cat_id),
2642
- "link_rss" => $rss
2643
- ));
2644
- } else { // WordPress 1.5.x
2645
- $result = $wpdb->query("
2646
- INSERT INTO $wpdb->links
2647
- SET
2648
- link_name = '".$wpdb->escape($name)."',
2649
- link_url = '".$wpdb->escape($uri)."',
2650
- link_category = '".$wpdb->escape($cat_id)."',
2651
- link_rss = '".$wpdb->escape($rss)."'
2652
- ");
2653
- $link_id = $wpdb->insert_id;
2654
- } // if
2655
- return $link_id;
2656
- } // function insert_syndicated_link()
 
 
 
 
 
 
 
 
 
 
 
 
 
2657
 
2658
- function link_category_named ($name) {
2659
- global $wp_db_version, $wpdb;
2660
-
2661
- if (!isset($wp_db_version) or $wp_db_version < 4772) {
2662
- // WordPress 1.5 and 2.0.x have segregated post and link categories
2663
- $ct = $wpdb->linkcategories;
2664
- } else {
2665
- // WordPress 2.1 has a unified category table for both
2666
- $ct = $wpdb->categories;
2667
- }
2668
- return $wpdb->get_var("SELECT cat_id FROM $ct WHERE cat_name='$name'");
2669
- }
2670
 
2671
- function create_link_category ($name) {
2672
- global $wp_db_version, $wpdb;
2673
 
2674
- if (!isset($wp_db_version) or $wp_db_version < 4772) {
2675
- $result = $wpdb->query("
2676
- INSERT INTO $wpdb->linkcategories
2677
- SET
2678
- cat_id = 0,
2679
- cat_name='$name',
2680
- show_images='N',
2681
- show_description='N',
2682
- show_rating='N',
2683
- show_updated='N',
2684
- sort_order='name'
2685
- ");
2686
- $cat_id = $wpdb->insert_id;
2687
- } else {
2688
- // Why the fuck is this API function only available in a wp-admin module?
2689
- $cat_id = wp_insert_category(array('cat_name' => $name));
2690
- }
2691
- }
2692
 
2693
- function link_category_id () {
2694
- global $wpdb, $wp_db_version;
 
 
 
 
 
 
 
 
 
 
 
2695
 
2696
- $cat_id = get_settings('feedwordpress_cat_id');
2697
-
2698
- // If we don't yet *have* the category, we'll have to create it
2699
- if ($cat_id === false) {
2700
- $cat = $wpdb->escape(DEFAULT_SYNDICATION_CATEGORY);
2701
 
2702
- // Look for something with the right name...
2703
- $cat_id = FeedWordPress::link_category_named($cat);
 
 
 
 
 
 
 
 
 
 
2704
 
2705
- // If you still can't find anything, make it for yourself.
2706
- if (!$cat_id) {
2707
- $cat_id = FeedWordPress::create_link_category($cat);
2708
- }
 
 
 
 
 
2709
 
2710
- update_option('feedwordpress_cat_id', $cat_id);
2711
- }
2712
- return $cat_id;
 
 
 
 
 
 
 
 
 
 
 
2713
  }
2714
 
2715
- function link_category () {
2716
- global $wpdb, $wp_db_version;
2717
 
2718
- $cat_id = FeedWordPress::link_category_id();
 
2719
 
2720
- // Get the ID# for the category name...
2721
- if (!isset($wp_db_version) or $wp_db_version < 4772) {
2722
- // WordPress 1.5 and 2.0.x
2723
- $cat_name = $wpdb->get_var("
2724
- SELECT cat_name FROM $wpdb->linkcategories
2725
- WHERE cat_id='$cat_id'
2726
- ");
2727
- } else {
2728
- // WordPress 2.1
2729
- $category = get_category($cat_id);
2730
- $cat_name = $category->cat_name;
2731
- }
2732
- return $cat_name;
2733
- }
2734
 
2735
- function needs_upgrade () {
2736
- global $wpdb;
2737
- $fwp_db_version = get_settings('feedwordpress_version');
2738
- $ret = false; // innocent until proven guilty
2739
- if (!$fwp_db_version or $fwp_db_version < FEEDWORDPRESS_VERSION) :
2740
- // This is an older version or a fresh install. Does it
2741
- // require a database upgrade or database initialization?
2742
- if ($fwp_db_version > 0.96) :
2743
- // No. Just brand it with the new version.
2744
- update_option('feedwordpress_version', FEEDWORDPRESS_VERSION);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2745
  else :
2746
- // Yes. Check to see whether this is a fresh install or an upgrade.
2747
- $syn = $wpdb->get_col("
2748
- SELECT post_id
2749
- FROM $wpdb->postmeta
2750
- WHERE meta_key = 'syndication_feed'
2751
- ");
2752
- if (count($syn) > 0) : // contains at least one syndicated post
2753
- $ret = true;
2754
- else : // fresh install; brand it as ours
2755
- update_option('feedwordpress_version', FEEDWORDPRESS_VERSION);
2756
- if (!get_settings('feedwordpress_rpc_secret')) :
2757
- update_option('feedwordpress_rpc_secret', substr(md5(uniqid(microtime())), 0, 6));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2758
  endif;
2759
- endif;
2760
  endif;
2761
  endif;
2762
- return $ret;
 
 
 
 
 
 
 
2763
  }
2764
 
 
 
 
 
 
 
2765
 
2766
- function upgrade_database ($from = NULL) {
2767
- global $wpdb;
2768
-
2769
- if (is_null($from) or $from <= 0.96) : $from = 0.96; endif;
2770
-
2771
- switch ($from) :
2772
- case 0.96: // account for changes to syndication custom values and guid
2773
- echo "<p>Upgrading database from {$from} to ".FEEDWORDPRESS_VERSION."...</p>\n";
 
 
 
 
 
 
2774
 
2775
- $cat_id = FeedWordPress::link_category_id();
2776
-
2777
- // Avoid duplicates
2778
- $wpdb->query("DELETE FROM `{$wpdb->postmeta}` WHERE meta_key = 'syndication_feed_id'");
2779
-
2780
- // Look up all the link IDs
2781
- $wpdb->query("
2782
- CREATE TEMPORARY TABLE tmp_custom_values
2783
- SELECT
2784
- NULL AS meta_id,
2785
- post_id,
2786
- 'syndication_feed_id' AS meta_key,
2787
- link_id AS meta_value
2788
- FROM `{$wpdb->postmeta}`, `{$wpdb->links}`
2789
- WHERE
2790
- meta_key='syndication_feed'
2791
- AND meta_value=link_rss
2792
- AND link_category = {$cat_id}
2793
- ");
2794
-
2795
- // Now attach them to their posts
2796
- $wpdb->query("INSERT INTO `{$wpdb->postmeta}` SELECT * FROM tmp_custom_values");
2797
 
2798
- // And clean up after ourselves.
2799
- $wpdb->query("DROP TABLE tmp_custom_values");
 
 
 
 
 
 
2800
 
2801
- // Now fix the guids to avoid duplicate posts
2802
- echo "<ul>";
2803
- foreach ($this->feeds as $feed) :
2804
- echo "<li>Fixing post meta-data for <cite>".$feed['link/name']."</cite> &#8230; "; flush();
2805
- $rss = @fetch_rss($feed['link/uri']);
2806
- if (is_array($rss->items)) :
2807
- foreach ($rss->items as $item) :
2808
- $guid = $wpdb->escape(FeedWordPress::guid($item, $feed)); // new GUID algorithm
2809
- $link = $wpdb->escape($item['link']);
2810
-
2811
- $wpdb->query("
2812
- UPDATE `{$wpdb->posts}` SET guid='{$guid}' WHERE guid='{$link}'
2813
- ");
2814
- endforeach;
 
 
 
 
 
 
 
 
 
 
2815
  endif;
2816
- echo "<strong>complete.</strong></li>\n";
2817
  endforeach;
2818
- echo "</ul>\n";
 
 
2819
 
2820
- // Mark the upgrade as successful.
2821
- update_option('feedwordpress_version', FEEDWORDPRESS_VERSION);
2822
- endswitch;
2823
- echo "<p>Upgrade complete. FeedWordPress is now ready to use again.</p>";
2824
- } /* FeedWordPress::upgrade_database() */
 
 
 
 
 
 
 
 
 
 
2825
 
2826
- } // class FeedWordPress
 
 
 
 
 
 
 
 
 
 
 
2827
 
2828
  ################################################################################
2829
  ## XML-RPC HOOKS: accept XML-RPC update pings from Contributors ################
@@ -2885,7 +3037,6 @@ class FeedFinder {
2885
  $ret = array($this->uri);
2886
  } else {
2887
  // Assume that we have HTML or XHTML (even if we don't, who's it gonna hurt?)
2888
-
2889
  // Autodiscovery is the preferred method
2890
  $href = $this->_link_rel_feeds();
2891
 
@@ -2918,6 +3069,7 @@ class FeedFinder {
2918
 
2919
  function is_feed ($uri = NULL) {
2920
  $data = $this->data($uri);
 
2921
  return (
2922
  preg_match (
2923
  "\007(".implode('|',$this->_feed_markers).")\007i",
@@ -2966,7 +3118,7 @@ class FeedFinder {
2966
  } /* if */
2967
  } /* if */
2968
  } /* for */
2969
- return $href;
2970
  }
2971
 
2972
  function _a_href_feeds ($obvious = TRUE) {
@@ -2991,7 +3143,6 @@ class FeedFinder {
2991
  // search through the HTML, save all <link> tags
2992
  // and store each link's attributes in an associative array
2993
  preg_match_all('/<'.$tag.'\s+(.*?)\s*\/?>/si', $html, $matches);
2994
-
2995
  $links = $matches[1];
2996
  $ret = array();
2997
  $link_count = count($links);
@@ -3017,179 +3168,184 @@ class FeedFinder {
3017
  # for FeedWordPress's purposes. The class has been stripped down to a single
3018
  # public method: Relative_URI::resolve($url, $base), which resolves the URI in
3019
  # $url relative to the URI in $base
 
 
 
 
3020
 
3021
- class Relative_URI
3022
- {
3023
- // Resolve relative URI in $url against the base URI in $base. If $base
3024
- // is not supplied, then we use the REQUEST_URI of this script.
3025
- //
3026
- // I'm hoping this method reflects RFC 2396 Section 5.2
3027
- function resolve ($url, $base = NULL)
3028
  {
3029
- if (is_null($base)):
3030
- $base = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
3031
- endif;
3032
-
3033
- $base = Relative_URI::_encode(trim($base));
3034
- $uri_parts = Relative_URI::_parse_url($base);
3035
-
3036
- $url = Relative_URI::_encode(trim($url));
3037
- $parts = Relative_URI::_parse_url($url);
3038
-
3039
- $uri_parts['fragment'] = (isset($parts['fragment']) ? $parts['fragment'] : null);
3040
- $uri_parts['query'] = (isset($parts['query']) ? $parts['query'] : null);
3041
-
3042
- // if path is empty, and scheme, host, and query are undefined,
3043
- // the URL is referring the base URL
3044
-
3045
- if (($parts['path'] == '') && !isset($parts['scheme']) && !isset($parts['host']) && !isset($parts['query'])) {
3046
- // If the URI is empty or only a fragment, return the base URI
3047
- return $base . (isset($parts['fragment']) ? '#'.$parts['fragment'] : '');
3048
- } elseif (isset($parts['scheme'])) {
3049
- // If the scheme is set, then the URI is absolute.
3050
- return $url;
3051
- } elseif (isset($parts['host'])) {
3052
- $uri_parts['host'] = $parts['host'];
3053
- $uri_parts['path'] = $parts['path'];
3054
- } else {
3055
- // We have a relative path but not a host.
3056
-
3057
- // start ugly fix:
3058
- // prepend slash to path if base host is set, base path is not set, and url path is not absolute
3059
- if ($uri_parts['host'] && ($uri_parts['path'] == '')
3060
- && (strlen($parts['path']) > 0)
3061
- && (substr($parts['path'], 0, 1) != '/')) {
3062
- $parts['path'] = '/'.$parts['path'];
3063
- } // end ugly fix
3064
 
3065
- if (substr($parts['path'], 0, 1) == '/') {
 
 
 
 
 
 
 
3066
  $uri_parts['path'] = $parts['path'];
3067
  } else {
3068
- // copy base path excluding any characters after the last (right-most) slash character
3069
- $buffer = substr($uri_parts['path'], 0, (int)strrpos($uri_parts['path'], '/')+1);
3070
- // append relative path
3071
- $buffer .= $parts['path'];
3072
- // remove "./" where "." is a complete path segment.
3073
- $buffer = str_replace('/./', '/', $buffer);
3074
- if (substr($buffer, 0, 2) == './') {
3075
- $buffer = substr($buffer, 2);
3076
- }
3077
- // if buffer ends with "." as a complete path segment, remove it
3078
- if (substr($buffer, -2) == '/.') {
3079
- $buffer = substr($buffer, 0, -1);
3080
- }
3081
- // remove "<segment>/../" where <segment> is a complete path segment not equal to ".."
3082
- $search_finished = false;
3083
- $segment = explode('/', $buffer);
3084
- while (!$search_finished) {
3085
- for ($x=0; $x+1 < count($segment);) {
3086
- if (($segment[$x] != '') && ($segment[$x] != '..') && ($segment[$x+1] == '..')) {
3087
- if ($x+2 == count($segment)) $segment[] = '';
3088
- unset($segment[$x], $segment[$x+1]);
3089
- $segment = array_values($segment);
3090
- continue 2;
3091
- } else {
3092
- $x++;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3093
  }
3094
- }
3095
- $search_finished = true;
 
3096
  }
3097
- $buffer = (count($segment) == 1) ? '/' : implode('/', $segment);
3098
- $uri_parts['path'] = $buffer;
3099
-
3100
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3101
  }
3102
-
3103
- // If we've gotten to this point, we can try to put the pieces
3104
- // back together.
3105
- $ret = '';
3106
- if (isset($uri_parts['scheme'])) $ret .= $uri_parts['scheme'].':';
3107
- if (isset($uri_parts['user'])) {
3108
- $ret .= $uri_parts['user'];
3109
- if (isset($uri_parts['pass'])) $ret .= ':'.$uri_parts['parts'];
3110
- $ret .= '@';
3111
- }
3112
- if (isset($uri_parts['host'])) {
3113
- $ret .= '//'.$uri_parts['host'];
3114
- if (isset($uri_parts['port'])) $ret .= ':'.$uri_parts['port'];
 
3115
  }
3116
- $ret .= $uri_parts['path'];
3117
- if (isset($uri_parts['query'])) $ret .= '?'.$uri_parts['query'];
3118
- if (isset($uri_parts['fragment'])) $ret .= '#'.$uri_parts['fragment'];
3119
-
3120
- return $ret;
3121
- }
3122
-
3123
- /**
3124
- * Parse URL
3125
- *
3126
- * Regular expression grabbed from RFC 2396 Appendix B.
3127
- * This is a replacement for PHPs builtin parse_url().
3128
- * @param string $url
3129
- * @access private
3130
- * @return array
3131
- */
3132
- function _parse_url($url)
3133
- {
3134
- // I'm using this pattern instead of parse_url() as there's a few strings where parse_url()
3135
- // generates a warning.
3136
- if (preg_match('!^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?!', $url, $match)) {
3137
- $parts = array();
3138
- if ($match[1] != '') $parts['scheme'] = $match[2];
3139
- if ($match[3] != '') $parts['auth'] = $match[4];
3140
- // parse auth
3141
- if (isset($parts['auth'])) {
3142
- // store user info
3143
- if (($at_pos = strpos($parts['auth'], '@')) !== false) {
3144
- $userinfo = explode(':', substr($parts['auth'], 0, $at_pos), 2);
3145
- $parts['user'] = $userinfo[0];
3146
- if (isset($userinfo[1])) $parts['pass'] = $userinfo[1];
3147
- $parts['auth'] = substr($parts['auth'], $at_pos+1);
3148
- }
3149
- // get port number
3150
- if ($port_pos = strrpos($parts['auth'], ':')) {
3151
- $parts['host'] = substr($parts['auth'], 0, $port_pos);
3152
- $parts['port'] = (int)substr($parts['auth'], $port_pos+1);
3153
- if ($parts['port'] < 1) $parts['port'] = null;
3154
- } else {
3155
- $parts['host'] = $parts['auth'];
3156
- }
3157
- }
3158
- unset($parts['auth']);
3159
- $parts['path'] = $match[5];
3160
- if (isset($match[6]) && ($match[6] != '')) $parts['query'] = $match[7];
3161
- if (isset($match[8]) && ($match[8] != '')) $parts['fragment'] = $match[9];
3162
- return $parts;
3163
- }
3164
- // shouldn't reach here
3165
- return array('path'=>'');
3166
- }
3167
-
3168
- function _encode($string)
3169
- {
3170
- static $replace = array();
3171
- if (!count($replace)) {
3172
- $find = array(32, 34, 60, 62, 123, 124, 125, 91, 92, 93, 94, 96, 127);
3173
- $find = array_merge(range(0, 31), $find);
3174
- $find = array_map('chr', $find);
3175
- foreach ($find as $char) {
3176
- $replace[$char] = '%'.bin2hex($char);
3177
- }
3178
- }
3179
- // escape control characters and a few other characters
3180
- $encoded = strtr($string, $replace);
3181
- // remove any character outside the hex range: 21 - 7E (see www.asciitable.com)
3182
- return preg_replace('/[^\x21-\x7e]/', '', $encoded);
3183
- }
3184
  }
3185
 
3186
  // take your best guess at the realname and e-mail, given a string
3187
  define('FWP_REGEX_EMAIL_ADDY', '([^@"(<\s]+@[^"@(<\s]+\.[^"@(<\s]+)');
3188
  define('FWP_REGEX_EMAIL_NAME', '("([^"]*)"|([^"<(]+\S))');
3189
- define('FWP_REGEX_EMAIL_POSTFIX_NAME', "/^\s*".FWP_REGEX_EMAIL_ADDY."\s+\(".FWP_REGEX_EMAIL_NAME."\)\s*$/");
3190
- define('FWP_REGEX_EMAIL_PREFIX_NAME', "/^\s*".FWP_REGEX_EMAIL_NAME."\s*<".FWP_REGEX_EMAIL_ADDY.">\s*$/");
3191
- define('FWP_REGEX_EMAIL_JUST_ADDY', "/^\s*".FWP_REGEX_EMAIL_ADDY."\s*$/");
3192
- define('FWP_REGEX_EMAIL_JUST_NAME', "/^\s*".FWP_REGEX_EMAIL_NAME."\s*$/");
3193
 
3194
  function parse_email_with_realname ($email) {
3195
  if (preg_match(FWP_REGEX_EMAIL_POSTFIX_NAME, $email, $matches)) :
@@ -3208,5 +3364,4 @@ function parse_email_with_realname ($email) {
3208
  endif;
3209
  return $ret;
3210
  }
3211
-
3212
  ?>
3
  Plugin Name: FeedWordPress
4
  Plugin URI: http://projects.radgeek.com/feedwordpress
5
  Description: simple and flexible Atom/RSS syndication for WordPress
6
+ Version: 0.99
7
  Author: Charles Johnson
8
  Author URI: http://radgeek.com/
9
  License: GPL
10
+ Last modified: 2007-09-16 11:13 PDT
11
  */
12
 
13
  # This uses code derived from:
26
  # <http://www.zyx.com/blog/xmlrpc.php>), or see `update-feeds.php`
27
 
28
  # -- Don't change these unless you know what you're doing...
29
+
30
+ define ('FEEDWORDPRESS_VERSION', '0.99');
31
+ define ('FEEDWORDPRESS_AUTHOR_CONTACT', 'http://radgeek.com/contact');
32
  define ('DEFAULT_SYNDICATION_CATEGORY', 'Contributors');
33
 
34
+ define ('FEEDWORDPRESS_DEBUG', false);
35
+
36
  define ('FEEDWORDPRESS_CAT_SEPARATOR_PATTERN', '/[:\n]/');
37
  define ('FEEDWORDPRESS_CAT_SEPARATOR', "\n");
38
 
39
  define ('FEEDVALIDATOR_URI', 'http://feedvalidator.org/check.cgi');
40
 
41
+ define ('FEEDWORDPRESS_FRESHNESS_INTERVAL', 10*60); // Every ten minutes
42
+
43
+ define ('FWP_SCHEMA_20', 3308); // Database schema # for WP 2.0
44
+ define ('FWP_SCHEMA_21', 4772); // Database schema # for WP 2.1
45
+ define ('FWP_SCHEMA_23', 5495); // Database schema # for WP 2.3
46
+
47
+ if (FEEDWORDPRESS_DEBUG) :
48
+ // Help us to pick out errors, if any.
49
+ ini_set('error_reporting', E_ALL & ~E_NOTICE);
50
+ ini_set('display_errors', true);
51
+ define('MAGPIE_DEBUG', true);
52
+
53
+ // When testing we don't want cache issues to interfere. But this is
54
+ // a VERY BAD SETTING for a production server. Webmasters will eat your
55
+ // face for breakfast if you use it, and the baby Jesus will cry. So
56
+ // make sure FEEDWORDPRESS_DEBUG is FALSE for any site that will be
57
+ // used for more than testing purposes!
58
+ define('MAGPIE_CACHE_AGE', 1);
59
+ endif;
60
+
61
  // Note that the rss-functions.php that comes prepackaged with WordPress is
62
  // old & busted. For the new hotness, drop a copy of rss.php from
63
  // this archive into wp-includes/rss.php
68
  require_once (ABSPATH . WPINC . '/rss-functions.php');
69
  endif;
70
 
71
+ if (isset($wp_db_version)) :
72
+ if ($wp_db_version >= FWP_SCHEMA_23) :
73
+ require_once (ABSPATH . WPINC . '/registration.php'); // for wp_insert_user
74
+ elseif ($wp_db_version >= FWP_SCHEMA_21) : // WordPress 2.1 and 2.2, but not 2.3
75
+ require_once (ABSPATH . WPINC . '/registration.php'); // for wp_insert_user
76
+ require_once (ABSPATH . 'wp-admin/admin-db.php'); // for wp_insert_category
77
+ elseif ($wp_db_version >= 3308) : // WordPress 2.0
78
+ require_once (ABSPATH . WPINC . '/registration-functions.php'); // for wp_insert_user
79
+ require_once (ABSPATH . 'wp-admin/admin-db.php'); // for wp_insert_category
80
+ endif;
81
+ endif;
82
+
83
+ if (function_exists('wp_enqueue_script')) :
84
+ wp_enqueue_script( 'ajaxcat' );
85
+ endif;
86
+
87
+ // Magic quotes are just about the stupidest thing ever.
88
+ if (is_array($_POST)) :
89
+ $fwp_post = stripslashes_deep($_POST);
90
  endif;
91
 
92
  // Is this being loaded from within WordPress 1.5 or later?
120
  add_filter('xmlrpc_methods', 'feedwordpress_xmlrpc_hook');
121
 
122
  # Outbound XML-RPC ping reform
123
+ remove_action('publish_post', 'generic_ping'); // WP 1.5.x
124
+ remove_action('do_pings', 'do_all_pings', 10, 1); // WP 2.1, 2.2
125
+ remove_action('publish_post', '_publish_post_hook', 5, 1); // WP 2.3
126
+
127
+ add_action('publish_post', 'fwp_publish_post_hook', 5, 1);
128
+ add_action('do_pings', 'fwp_do_pings', 10, 1);
129
+ add_action('feedwordpress_update', 'fwp_hold_pings');
130
+ add_action('feedwordpress_update_complete', 'fwp_release_pings');
131
+
132
  # Hook in logging functions only if the logging option is ON
133
+ $update_logging = get_option('feedwordpress_update_logging');
134
  if ($update_logging == 'yes') :
135
  add_action('post_syndicated_item', 'log_feedwordpress_post', 100);
136
  add_action('update_syndicated_item', 'log_feedwordpress_update_post', 100);
138
  add_action('feedwordpress_check_feed', 'log_feedwordpress_check_feed', 100);
139
  add_action('feedwordpress_update_complete', 'log_feedwordpress_update_complete', 100);
140
  endif;
141
+
142
+ # Cron-less auto-update. Hooray!
143
+ add_action('init', 'feedwordpress_auto_update');
144
  else :
145
  # Hook in the menus, which will just point to the upgrade interface
146
  add_action('admin_menu', 'fwp_add_pages');
147
  endif; // if (!FeedWordPress::needs_upgrade())
148
  endif;
149
 
150
+ function feedwordpress_auto_update () {
151
+ if (FeedWordPress::stale()) :
152
+ $feedwordpress =& new FeedWordPress;
153
+ $feedwordpress->update();
154
+ endif;
155
+ } /* feedwordpress_auto_update () */
156
+
157
  ################################################################################
158
  ## LOGGING FUNCTIONS: log status updates to error_log if you want it ###########
159
  ################################################################################
195
  ## LEGACY API: Replicate or mock up functions for legacy support purposes ######
196
  ################################################################################
197
 
198
+ if (!function_exists('get_option')) {
199
+ function get_option ($option) {
200
+ return get_settings($option);
201
+ }
202
+ }
203
  if (!function_exists('current_user_can')) {
204
  $legacy_capability_hack = true;
205
  function current_user_can ($task) {
251
  $ret = NULL;
252
  if (strlen($feed_id) > 0):
253
  if (isset($feedwordpress_linkcache[$feed_id])) :
254
+ $link = $feedwordpress_linkcache[$feed_id];
255
  else :
256
+ $link =& new SyndicatedLink($feed_id);
257
+ $feedwordpress_linkcache[$feed_id] = $link;
 
 
 
258
  endif;
259
 
260
+ $ret = $link->settings[$key];
261
+ endif;
 
262
  return $ret;
263
+ } /* get_feed_meta() */
264
 
265
  function get_syndication_permalink () {
266
  list($u) = get_post_custom_values('syndication_permalink'); return $u;
297
  }
298
 
299
  function syndication_permalink ($permalink = '') {
300
+ if (get_option('feedwordpress_munge_permalink') != 'no'):
301
  $uri = get_syndication_permalink();
302
  return ((strlen($uri) > 0) ? $uri : $permalink);
303
  else:
310
  ################################################################################
311
 
312
  function fwp_upgrade_page () {
313
+ if (isset($GLOBALS['fwp_post']['action']) and $GLOBALS['fwp_post']['action']=='Upgrade') :
314
+ $ver = get_option('feedwordpress_version');
315
+ if (get_option('feedwordpress_version') != FEEDWORDPRESS_VERSION) :
316
  echo "<div class=\"wrap\">\n";
317
  echo "<h2>Upgrading FeedWordPress...</h2>";
318
 
371
  $manage_options = 'manage_options';
372
  endif;
373
 
374
+ //add_submenu_page('plugins.php', 'Akismet Configuration', 'Akismet Configuration', 'manage_options', 'syndication-manage-page', 'fwp_syndication_manage_page');
375
+ add_menu_page('Syndicated Sites', 'Syndication', $manage_links, 'feedwordpress/'.basename(__FILE__), 'fwp_syndication_manage_page');
376
+ add_submenu_page('feedwordpress/'.basename(__FILE__), 'Syndication Options', 'Options', $manage_options, 'feedwordpress/syndication-options.php');
377
+ add_options_page('Syndication Options', 'Syndication', $manage_options, 'feedwordpress/syndication-options.php');
378
  } // function fwp_add_pages () */
379
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
380
  function fwp_category_box ($checked, $object) {
381
  global $wp_db_version;
382
 
408
  endif;
409
  }
410
 
411
+ function update_feeds_mention ($feed) {
412
+ echo "<li>Updating <cite>".$feed['link/name']."</cite> from &lt;<a href=\""
413
+ .$feed['link/uri']."\">".$feed['link/uri']."</a>&gt; ...</li>\n";
414
+ flush();
415
+ }
416
+
417
  function fwp_syndication_manage_page () {
418
  global $wpdb;
419
 
428
  if ($_REQUEST['action'] == 'feedfinder') : $cont = fwp_feedfinder_page();
429
  elseif ($_REQUEST['action'] == 'switchfeed') : $cont = fwp_switchfeed_page();
430
  elseif ($_REQUEST['action'] == 'linkedit') : $cont = fwp_linkedit_page();
431
+ elseif ($_REQUEST['action'] == 'Unsubscribe from Checked Links' or $_REQUEST['action'] == 'Unsubscribe') : $cont = fwp_multidelete_page();
432
  endif;
433
  endif;
434
 
436
  ?>
437
  <?php
438
  $links = FeedWordPress::syndicated_links();
439
+
440
+ if (isset($_POST['update']) or isset($_POST['action']) or isset($_POST['update_uri'])) :
441
+ $fwp_update_invoke = 'post';
442
+ else :
443
+ $fwp_update_invoke = 'get';
444
+ endif;
445
+
446
+ $update_set = array();
447
+ if (isset($_POST['link_ids']) and is_array($_POST['link_ids']) and ($_POST['action']=='Update Checked Links')) :
448
+ $targets = $wpdb->get_results("
449
+ SELECT * FROM $wpdb->links
450
+ WHERE link_id IN (".implode(",",$_POST['link_ids']).")
451
+ ");
452
+ if (is_array($targets)) :
453
+ foreach ($targets as $target) :
454
+ $update_set[] = $target->link_rss;
455
+ endforeach;
456
+ else : // This should never happen
457
+ FeedWordPress::critical_bug('fwp_syndication_manage_page::targets', $targets, __LINE__);
458
+ endif;
459
+ elseif (isset($_POST['update_uri'])) :
460
+ $update_set[] = $_POST['update_uri'];
461
+ endif;
462
+
463
+ if ($fwp_update_invoke != 'get' and count($update_set) > 0) : // Only do things with side-effects for HTTP POST or command line
464
+ $feedwordpress =& new FeedWordPress;
465
+ add_action('feedwordpress_check_feed', 'update_feeds_mention');
466
+
467
+ echo "<div class=\"updated\">\n";
468
+ echo "<ul>\n";
469
+ foreach ($update_set as $uri) :
470
+ if ($uri == '*') : $uri = NULL; endif;
471
+ $delta = $feedwordpress->update($uri);
472
+ endforeach;
473
+ echo "</ul>\n";
474
+
475
+ if (is_null($delta)) :
476
+ echo "<p><strong>Error:</strong> I don't syndicate <a href=\"$uri\">$uri</a></p>\n";
477
+ else :
478
+ $mesg = array();
479
+ if (isset($delta['new'])) : $mesg[] = ' '.$delta['new'].' new posts were syndicated'; endif;
480
+ if (isset($delta['updated'])) : $mesg[] = ' '.$delta['updated'].' existing posts were updated'; endif;
481
+ echo "<p>Update complete.".implode(' and', $mesg)."</p>";
482
+ echo "\n"; flush();
483
+ endif;
484
+ echo "</div> <!-- class=\"updated\" -->\n";
485
+ endif;
486
+
487
+ ?>
488
  <div class="wrap">
489
+ <form action="" method="POST">
490
+ <h2>Update feeds now</h2>
491
+ <p>Check currently scheduled feeds for new and updated posts.</p>
492
+
493
+ <?php if (!get_option('feedwordpress_automatic_updates')) : ?>
494
+ <p><strong>Note:</strong> Automatic updates are currently turned
495
+ <strong>off</strong>. New posts from your feeds will not be syndicated
496
+ until you manually check for them here. You can turn on automatic
497
+ updates under <a href="admin.php?page=feedwordpress/syndication-options.php">Syndication
498
+ Options</a>.</p>
499
+ <?php endif; ?>
500
+
501
+
502
+ <div class="submit"><input type="hidden" name="update_uri" value="*" /><input type="submit" name="update" value="Update" /></div>
503
  </form>
504
+ </div> <!-- class="wrap" -->
505
 
506
+ <form action="admin.php?page=feedwordpress/<?php echo basename(__FILE__); ?>" method="post">
507
  <div class="wrap">
508
  <h2>Syndicated Sites</h2>
509
  <?php $alt_row = true;
519
  <?php foreach ($links as $link):
520
  $alt_row = !$alt_row; ?>
521
  <tr<?php echo ($alt_row?' class="alternate"':''); ?>>
522
+ <td><a href="<?php echo wp_specialchars($link->link_url, 'both'); ?>"><?php echo wp_specialchars($link->link_name, 'both'); ?></a></td>
523
  <?php
524
  if (strlen($link->link_rss) > 0):
525
  $caption='Switch Feed';
534
  if (strlen($display_uri) > 32) : $display_uri = substr($display_uri, 0, 32).'&#8230;'; endif;
535
  ?>
536
  <td>
537
+ <strong><a href="<?php echo $link->link_rss; ?>"><?php echo wp_specialchars($display_uri, 'both'); ?></a></strong></td>
538
  <?php
539
  else:
540
  $caption='Find Feed';
544
  <?php
545
  endif;
546
  ?>
547
+ <td><a href="admin.php?page=feedwordpress/<?php echo basename(__FILE__); ?>&amp;link_id=<?php echo $link->link_id; ?>&amp;action=linkedit" class="edit"><?php _e('Edit')?></a></td>
548
+ <td><a href="admin.php?page=feedwordpress/<?php echo basename(__FILE__); ?>&amp;link_id=<?php echo $link->link_id; ?>&amp;action=feedfinder" class="edit"><?php echo $caption; ?></a></td>
549
+ <td><a href="admin.php?page=feedwordpress/<?php echo basename(__FILE__); ?>&amp;link_id=<?php echo $link->link_id; ?>&amp;action=Unsubscribe" class="delete"><?php _e('Unsubscribe'); ?></a></td>
550
  <td><input type="checkbox" name="link_ids[]" value="<?php echo $link->link_id; ?>" /></td>
551
  <?php
552
  echo "\n\t</tr>";
558
 
559
  <?php endif; ?>
560
  </table>
561
+
562
+ <br/><hr/>
563
+ <div class="submit"><input type="submit" class="delete" name="action" value="Unsubscribe from Checked Links" />
564
+ <input type="submit" name="action" value="Update Checked Links" /></div>
565
+ </div> <!-- class="wrap" -->
566
+ </form>
567
+
568
  <div class="wrap">
569
+ <form action="admin.php?page=feedwordpress/<?php echo basename(__FILE__); ?>" method="post">
570
+ <h2>Add a new syndicated site:</h2>
571
+ <div>
572
+ <label for="add-uri">Website or newsfeed:</label>
573
+ <input type="text" name="lookup" id="add-uri" value="URI" size="64" />
574
+ <input type="hidden" name="action" value="feedfinder" />
575
  </div>
576
+ <div class="submit"><input type="submit" value="Syndicate &raquo;" /></div>
577
  </form>
578
+ </div> <!-- class="wrap" -->
579
  <?php
580
  endif;
581
  }
587
 
588
  if (isset($_REQUEST['link_id']) and ($_REQUEST['link_id']!=0)):
589
  $link_id = $_REQUEST['link_id'];
590
+ if (!is_numeric($link_id)) : FeedWordPress::critical_bug('fwp_feedfinder_page::link_id', $link_id, __LINE__); endif;
591
+
592
  $link = $wpdb->get_row("SELECT * FROM $wpdb->links WHERE link_id='".$wpdb->escape($link_id)."'");
593
  if (is_object($link)):
594
  if (is_null($lookup)) $lookup = $link->link_url;
595
+ $name = wp_specialchars($link->link_name, 'both');
596
  else:
597
  die (__("Cheatin' uh ?"));
598
  endif;
613
  $feed_title = isset($rss->channel['title'])?$rss->channel['title']:$rss->channel['link'];
614
  $feed_link = isset($rss->channel['link'])?$rss->channel['link']:'';
615
  ?>
616
+ <form action="admin.php?page=feedwordpress/<?php echo basename(__FILE__); ?>" method="post">
617
  <fieldset style="clear: both">
618
  <legend><?php echo $rss->feed_type; ?> <?php echo $rss->feed_version; ?> feed</legend>
619
 
620
  <?php if ($link_id===0): ?>
621
+ <input type="hidden" name="feed_title" value="<?php echo wp_specialchars($feed_title, 'both'); ?>" />
622
+ <input type="hidden" name="feed_link" value="<?php echo wp_specialchars($feed_link, 'both'); ?>" />
623
  <?php endif; ?>
624
 
625
  <input type="hidden" name="link_id" value="<?php echo $link_id; ?>" />
626
+ <input type="hidden" name="feed" value="<?php echo wp_specialchars($f, 'both'); ?>" />
627
  <input type="hidden" name="action" value="switchfeed" />
628
 
629
  <div>
648
  <h3>Feed Information</h3>
649
  <ul>
650
  <li><strong>Website:</strong> <a href="<?php echo $feed_link; ?>"><?php echo is_null($feed_title)?'<em>Unknown</em>':$feed_title; ?></a></li>
651
+ <li><strong>Feed URI:</strong> <a href="<?php echo wp_specialchars($f, 'both'); ?>"><?php echo wp_specialchars($f, 'both'); ?></a> <a title="Check feed &lt;<?php echo wp_specialchars($f, 'both'); ?>&gt; for validity" href="http://feedvalidator.org/check.cgi?url=<?php echo urlencode($f); ?>"><img src="../wp-images/smilies/icon_arrow.gif" alt="(&rarr;)" /></a></li>
652
+ <li><strong>Encoding:</strong> <?php echo isset($rss->encoding)?wp_specialchars($rss->encoding, 'both'):"<em>Unknown</em>"; ?></li>
653
+ <li><strong>Description:</strong> <?php echo isset($rss->channel['description'])?wp_specialchars($rss->channel['description'], 'both'):"<em>Unknown</em>"; ?></li>
654
  </ul>
655
  <div class="submit"><input type="submit" name="Use" value="&laquo; Use this feed" /></div>
656
  <div class="submit"><input type="submit" name="Cancel" value="&laquo; Cancel" /></div>
666
  ?>
667
  </div>
668
 
669
+ <form action="admin.php?page=feedwordpress/<?php echo basename(__FILE__); ?>" method="post">
670
  <div class="wrap">
671
  <h2>Use another feed</h2>
672
  <div><label>Feed:</label>
690
  elseif (isset($_REQUEST['link_id']) and ($_REQUEST['link_id']==0)):
691
  $link_id = FeedWordPress::syndicate_link($_REQUEST['feed_title'], $_REQUEST['feed_link'], $_REQUEST['feed']);
692
  if ($link_id): ?>
693
+ <div class="updated"><p><a href="<?php echo $_REQUEST['feed_link']; ?>"><?php echo wp_specialchars($_REQUEST['feed_title'], 'both'); ?></a>
694
+ has been added as a contributing site, using the newsfeed at &lt;<a href="<?php echo $_REQUEST['feed']; ?>"><?php echo wp_specialchars($_REQUEST['feed'], 'both'); ?></a>&gt;.</p></div>
695
  <?php else: ?>
696
+ <div class="updated"><p>There was a problem adding the newsfeed. [SQL: <?php echo wp_specialchars(mysql_error(), 'both'); ?>]</p></div>
697
  <?php endif;
698
  elseif (isset($_REQUEST['link_id'])):
699
  // Update link_rss
710
  WHERE link_id = '".$wpdb->escape($_REQUEST['link_id'])."'
711
  ");
712
  ?>
713
+ <div class="updated"><p>Feed for <a href="<?php echo $result->link_url; ?>"><?php echo wp_specialchars($result->link_name, 'both'); ?></a>
714
+ updated to &lt;<a href="<?php echo $_REQUEST['feed']; ?>"><?php echo wp_specialchars($_REQUEST['feed'], 'both'); ?></a>&gt;.</p></div>
715
  <?php else: ?>
716
  <div class="updated"><p>Nothing was changed.</p></div>
717
  <?php endif;
727
 
728
  $special_settings = array ( /* Regular expression syntax is OK here */
729
  'cats',
730
+ 'cat_split',
731
  'hardcode name',
732
  'hardcode url',
733
  'hardcode description',
748
  return fwp_feedfinder_page(); // re-route to Feed Finder page
749
  else :
750
  $link_id = (int) $_REQUEST['link_id'];
751
+ $link =& new SyndicatedLink($link_id);
 
 
752
 
753
+ if ($link->found()) :
754
+ if (isset($GLOBALS['fwp_post']['save'])) :
755
  $alter = array ();
756
 
757
+ $meta = $link->settings;
758
  if (isset($meta['cats'])):
759
  $meta['cats'] = preg_split(FEEDWORDPRESS_CAT_SEPARATOR_PATTERN, $meta['cats']);
760
  endif;
761
 
762
  // custom feed settings first
763
+ foreach ($GLOBALS['fwp_post']['notes'] as $mn) :
764
  $mn['key0'] = trim($mn['key0']);
765
  $mn['key1'] = trim($mn['key1']);
766
  if (preg_match("\007^(("
781
 
782
  // now stuff through the web form
783
  // hardcoded feed info
784
+ if (isset($GLOBALS['fwp_post']['hardcode_name'])) :
785
+ $meta['hardcode name'] = $GLOBALS['fwp_post']['hardcode_name'];
786
  if (FeedWordPress::affirmative($meta, 'hardcode name')) :
787
+ $alter[] = "link_name = '".$wpdb->escape($GLOBALS['fwp_post']['name'])."'";
788
  endif;
789
  endif;
790
+ if (isset($GLOBALS['fwp_post']['hardcode_description'])) :
791
+ $meta['hardcode description'] = $GLOBALS['fwp_post']['hardcode_description'];
792
  if (FeedWordPress::affirmative($meta, 'hardcode description')) :
793
+ $alter[] = "link_description = '".$wpdb->escape($GLOBALS['fwp_post']['description'])."'";
794
  endif;
795
  endif;
796
+ if (isset($GLOBALS['fwp_post']['hardcode_url'])) :
797
+ $meta['hardcode url'] = $GLOBALS['fwp_post']['hardcode_url'];
798
  if (FeedWordPress::affirmative($meta, 'hardcode url')) :
799
+ $alter[] = "link_url = '".$wpdb->escape($GLOBALS['fwp_post']['linkurl'])."'";
800
  endif;
801
  endif;
802
 
803
  // Update scheduling
804
+ if (isset($GLOBALS['fwp_post']['update_schedule'])) :
805
+ $meta['update/hold'] = $GLOBALS['fwp_post']['update_schedule'];
806
  endif;
807
 
808
  // Categories
809
+ if (isset($GLOBALS['fwp_post']['post_category'])) :
810
+ $meta['cats'] = array();
811
+ foreach ($GLOBALS['fwp_post']['post_category'] as $cat_id) :
812
+ $meta['cats'][] = '{#'.$cat_id.'}';
813
+ endforeach;
 
 
 
 
 
814
  else :
815
  unset($meta['cats']);
816
  endif;
818
  // Post status, comment status, ping status
819
  foreach (array('post', 'comment', 'ping') as $what) :
820
  $sfield = "feed_{$what}_status";
821
+ if (isset($GLOBALS['fwp_post'][$sfield])) :
822
+ if ($GLOBALS['fwp_post'][$sfield]=='site-default') :
823
  unset($meta["{$what} status"]);
824
  else :
825
+ $meta["{$what} status"] = $GLOBALS['fwp_post'][$sfield];
826
  endif;
827
  endif;
828
  endforeach;
830
  // Unfamiliar author, unfamiliar categories
831
  foreach (array("author", "category") as $what) :
832
  $sfield = "unfamiliar_{$what}";
833
+ if (isset($GLOBALS['fwp_post'][$sfield])) :
834
+ if ($GLOBALS['fwp_post'][$sfield]=='site-default') :
835
  unset($meta["unfamiliar {$what}"]);
836
  else :
837
+ $meta["unfamiliar {$what}"] = $GLOBALS['fwp_post'][$sfield];
838
  endif;
839
  endif;
840
  endforeach;
841
 
842
+ if (isset($GLOBALS['fwp_post']['cat_split'])) :
843
+ if (strlen(trim($GLOBALS['fwp_post']['cat_split'])) > 0) :
844
+ $meta['cat_split'] = trim($GLOBALS['fwp_post']['cat_split']);
845
+ else :
846
+ unset($meta['cat_split']);
847
+ endif;
848
+ endif;
849
+
850
  if (is_array($meta['cats'])) :
851
  $meta['cats'] = implode(FEEDWORDPRESS_CAT_SEPARATOR, $meta['cats']);
852
  endif;
853
 
854
  $notes = '';
855
  foreach ($meta as $key => $value) :
856
+ $notes .= $key . ": ". addcslashes($value, "\0..\37".'\\') . "\n";
857
  endforeach;
858
  $alter[] = "link_notes = '".$wpdb->escape($notes)."'";
859
 
868
  $updated_link = true;
869
 
870
  // reload link information from DB
871
+ $link =& new SyndicatedLink($link_id);
 
 
872
  else :
873
  $updated_link = false;
874
  endif;
875
 
876
+ $db_link = $link->link;
877
+ $link_url = wp_specialchars($db_link->link_url, 1);
878
+ $link_name = wp_specialchars($db_link->link_name, 1);
879
+ $link_description = wp_specialchars($db_link->link_description, 'both');
880
+ $link_notes = wp_specialchars($db_link->link_notes, 'both');
881
+ $link_rss_uri = wp_specialchars($db_link->link_rss, 'both');
882
+
883
+ $meta = $link->settings;
884
+ $post_status_global = get_option('feedwordpress_syndicated_post_status');
885
+ $comment_status_global = get_option('feedwordpress_syndicated_comment_status');
886
+ $ping_status_global = get_option('feedwordpress_syndicated_ping_status');
887
 
 
 
888
  $status['post'] = array('publish' => '', 'private' => '', 'draft' => '', 'site-default' => '');
889
  $status['comment'] = array('open' => '', 'closed' => '', 'site-default' => '');
890
  $status['ping'] = array('open' => '', 'closed' => '', 'site-default' => '');
911
  endforeach;
912
 
913
  $dogs = get_nested_categories(-1, 0);
914
+ if (is_array($meta['cats'])) :
915
+ $cats = array_map('strtolower',
916
+ array_map('trim',
917
+ $meta['cats']
918
+ ));
919
+ else:
920
+ $cats = array();
921
+ endif;
922
 
923
  foreach ($dogs as $tag => $dog) :
924
+ $found_by_name = in_array(strtolower(trim($dog['cat_name'])), $cats);
925
+
926
+ if (isset($dog['cat_ID'])) : $dog['cat_id'] = $dog['cat_ID']; endif;
927
+ $found_by_id = in_array('{#'.trim($dog['cat_id']).'}', $cats);
928
+
929
+ if ($found_by_name or $found_by_id) :
930
  $dogs[$tag]['checked'] = true;
931
  endif;
932
  endforeach;
950
  <div class="updated"><p>Syndicated feed settings updated.</p></div>
951
  <?php endif; ?>
952
 
953
+ <form action="admin.php?page=feedwordpress/<?php echo basename(__FILE__); ?>" method="post">
954
  <div class="wrap">
955
  <input type="hidden" name="link_id" value="<?php echo $link_id; ?>" />
956
  <input type="hidden" name="action" value="linkedit" />
961
  <table class="editform" width="100%" cellspacing="2" cellpadding="5">
962
  <tr>
963
  <th scope="row" width="20%"><?php _e('Feed URI:') ?></th>
964
+ <td width="60%"><a href="<?php echo wp_specialchars($link_rss_uri, 'both'); ?>"><?php echo $link_rss_uri; ?></a>
965
  <a href="<?php echo FEEDVALIDATOR_URI; ?>?url=<?php echo urlencode($link_rss_uri); ?>"
966
+ title="Check feed &lt;<?php echo wp_specialchars($link_rss_uri, 'both'); ?>&gt; for validity"><img src="../wp-images/smilies/icon_arrow.gif" alt="&rarr;" /></a>
967
  </td>
968
  <td width="20%"><input type="submit" name="feedfinder" value="switch &rarr;" style="font-size:smaller" /></td>
969
  </tr>
975
  </td>
976
  <td>
977
  <select id="basics-hardcode-name" onchange="flip_hardcode('name')" name="hardcode_name">
978
+ <option value="no" <?php echo $link->hardcode('name')?'':'selected="selected"'; ?>>update automatically</option>
979
+ <option value="yes" <?php echo $link->hardcode('name')?'selected="selected"':''; ?>>edit manually</option>
980
  </select>
981
  </td>
982
  </tr>
989
  <td>
990
  <select id="basics-hardcode-description" onchange="flip_hardcode('description')"
991
  name="hardcode_description">
992
+ <option value="no" <?php echo $link->hardcode('description')?'':'selected="selected"'; ?>>update automatically</option>
993
+ <option value="yes" <?php echo $link->hardcode('description')?'selected="selected"':''; ?>>edit manually</option>
994
  </select></td>
995
  </tr>
996
  <tr>
1000
  <a id="basics-url-view" href="<?php echo $link_url; ?>"><?php echo $link_url; ?></a></td>
1001
  <td>
1002
  <select id="basics-hardcode-url" onchange="flip_hardcode('url')" name="hardcode_url">
1003
+ <option value="no"<?php echo $link->hardcode('url')?'':' selected="selected"'; ?>>update automatically</option>
1004
+ <option value="yes"<?php echo $link->hardcode('url')?' selected="selected"':''; ?>>edit manually</option>
1005
  </select></td></tr>
1006
 
1007
  <tr>
1057
 
1058
  <?php fwp_category_box($dogs, 'all syndicated posts from this feed'); ?>
1059
 
1060
+ <table class="editform" width="75%" cellspacing="2" cellpadding="5">
1061
+ <tr><th width="27%" scope="row" style="vertical-align:top">Publication:</th>
1062
+ <td width="73%" style="vertical-align:top"><ul style="margin:0; list-style:none">
1063
  <li><label><input type="radio" name="feed_post_status" value="site-default"
1064
+ <?php echo $status['post']['site-default']; ?> /> Use site-wide setting from <a href="admin.php?page=feedwordpress/syndication-options.php">Syndication Options</a>
1065
+ (currently: <strong><?php echo ($post_status_global ? $post_status_global : 'publish'); ?></strong>)</label></li>
1066
  <li><label><input type="radio" name="feed_post_status" value="publish"
1067
  <?php echo $status['post']['publish']; ?> /> Publish posts from this feed immediately</label></li>
1068
  <li><label><input type="radio" name="feed_post_status" value="private"
1072
  </ul></td>
1073
  </tr>
1074
 
1075
+ <tr><th width="27%" scope="row" style="vertical-align:top">Comments:</th>
1076
+ <td width="73%"><ul style="margin:0; list-style:none">
1077
  <li><label><input type="radio" name="feed_comment_status" value="site-default"
1078
+ <?php echo $status['comment']['site-default']; ?> /> Use site-wide setting from <a href="admin.php?page=feedwordpress/syndication-options.php">Syndication Options</a>
1079
+ (currently: <strong><?php echo ($comment_status_global ? $comment_status_global : 'closed'); ?>)</strong></label></li>
1080
  <li><label><input type="radio" name="feed_comment_status" value="open"
1081
  <?php echo $status['comment']['open']; ?> /> Allow comments on syndicated posts from this feed</label></li>
1082
  <li><label><input type="radio" name="feed_comment_status" value="closed"
1084
  </ul></td>
1085
  </tr>
1086
 
1087
+ <tr><th width="27%" scope="row" style="vertical-align:top">Trackback and Pingback:</th>
1088
+ <td width="73%"><ul style="margin:0; list-style:none">
1089
  <li><label><input type="radio" name="feed_ping_status" value="site-default"
1090
+ <?php echo $status['ping']['site-default']; ?> /> Use site-wide setting from <a href="admin.php?page=feedwordpress/syndication-options.php">Syndication Options</a>
1091
+ (currently: <strong><?php echo ($ping_status_global ? $ping_status_global : 'closed'); ?>)</strong></label></li>
1092
  <li><label><input type="radio" name="feed_ping_status" value="open"
1093
  <?php echo $status['ping']['open']; ?> /> Accept pings on syndicated posts from this feed</label></li>
1094
  <li><label><input type="radio" name="feed_ping_status" value="closed"
1108
  <tr>
1109
  <th width="20%" scope="row" style="vertical-align:top">Unfamiliar authors:</th>
1110
  <td width="80%"><ul style="margin: 0; list-style:none">
1111
+ <li><label><input type="radio" name="unfamiliar_author" value="site-default"<?php echo $unfamiliar['author']['site-default']; ?> /> use site-wide setting from <a href="admin.php?page=feedwordpress/syndication-options.php">Syndication Options</a>
1112
  (currently <strong><?php echo FeedWordPress::on_unfamiliar('author');; ?></strong>)</label></li>
1113
  <li><label><input type="radio" name="unfamiliar_author" value="create"<?php echo $unfamiliar['author']['create']; ?>/> create a new author account</label></li>
1114
  <li><label><input type="radio" name="unfamiliar_author" value="default"<?php echo $unfamiliar['author']['default']; ?> /> attribute the post to the default author</label></li>
1119
  <tr>
1120
  <th width="20%" scope="row" style="vertical-align:top">Unfamiliar categories:</th>
1121
  <td width="80%"><ul style="margin: 0; list-style:none">
1122
+ <li><label><input type="radio" name="unfamiliar_category" value="site-default"<?php echo $unfamiliar['category']['site-default']; ?> /> use site-wide setting from <a href="admin.php?page=feedwordpress/syndication-options.php">Syndication Options</a>
1123
  (currently <strong><?php echo FeedWordPress::on_unfamiliar('category');; ?></strong>)</label></li>
1124
  <li><label><input type="radio" name="unfamiliar_category" value="create"<?php echo $unfamiliar['category']['create']; ?> /> create any categories the post is in</label></li>
1125
  <li><label><input type="radio" name="unfamiliar_category" value="default"<?php echo $unfamiliar['category']['default']; ?> /> don't create new categories</label></li>
1126
  <li><label><input type="radio" name="unfamiliar_category" value="filter"<?php echo $unfamiliar['category']['filter']; ?> /> don't create new categories and don't syndicate posts unless they match at least one familiar category</label></li>
1127
  </ul></td>
1128
+ </tr>
1129
+
1130
+ <tr>
1131
+ <th width="20%" scope="row" style="vertical-align:top">Multiple categories:</th>
1132
+ <td width="80%">
1133
+ <input type="text" size="20" id="cat_split" name="cat_split" value="<?php if (isset($meta['cat_split'])) : echo htmlspecialchars($meta['cat_split']); endif; ?>" /><br/>
1134
+ Enter a <a href="http://us.php.net/manual/en/reference.pcre.pattern.syntax.php">Perl-compatible regular expression</a> here if the feed provides multiple
1135
+ categories in a single category element. The regular expression should match
1136
+ the characters used to separate one category from the next. If the feed uses
1137
+ spaces (like <a href="http://del.icio.us/">del.icio.us</a>), use the pattern "\s".
1138
+ If the feed does not provide multiple categories in a single element, leave this
1139
+ blank.</td>
1140
+ </tr>
1141
+ </table>
1142
  </fieldset>
1143
 
1144
  <p class="submit">
1161
  if (!preg_match("\007^((".implode(')|(', $special_settings)."))$\007i", $key)) :
1162
  ?>
1163
  <tr style="vertical-align:top">
1164
+ <th width="30%" scope="row"><input type="hidden" name="notes[<?php echo $i; ?>][key0]" value="<?php echo wp_specialchars($key, 'both'); ?>" />
1165
+ <input id="notes-<?php echo $i; ?>-key" name="notes[<?php echo $i; ?>][key1]" value="<?php echo wp_specialchars($key, 'both'); ?>" /></th>
1166
+ <td width="60%"><textarea rows="2" cols="40" id="notes-<?php echo $i; ?>-value" name="notes[<?php echo $i; ?>][value]"><?php echo wp_specialchars($value, 'both'); ?></textarea></td>
1167
  <td width="10%"><select name="notes[<?php echo $i; ?>][action]">
1168
  <option value="update">save changes</option>
1169
  <option value="delete">delete this setting</option>
1202
 
1203
  if (!current_user_can('manage_links')):
1204
  die (__("Cheatin' uh ?"));
1205
+ elseif (isset($GLOBALS['fwp_post']['confirm']) and $GLOBALS['fwp_post']['confirm']=='Delete'):
1206
+ foreach ($GLOBALS['fwp_post']['link_action'] as $link_id => $what) :
1207
  $do_it[$what][] = $link_id;
1208
  endforeach;
1209
 
1283
  WHERE link_id IN (".implode(",",$link_ids).")
1284
  ");
1285
  ?>
1286
+ <form action="admin.php?page=feedwordpress/<?php echo basename(__FILE__); ?>" method="post">
1287
  <div class="wrap">
1288
  <input type="hidden" name="action" value="Unsubscribe" />
1289
  <input type="hidden" name="confirm" value="Delete" />
1292
  <?php foreach ($targets as $link) :
1293
  $link_url = wp_specialchars($link->link_url, 1);
1294
  $link_name = wp_specialchars($link->link_name, 1);
1295
+ $link_description = wp_specialchars($link->link_description, 'both');
1296
+ $link_rss = wp_specialchars($link->link_rss, 'both');
 
1297
  ?>
1298
  <fieldset>
1299
  <legend><?php echo $link_name; ?></legend>
1356
  function fwp_release_pings () {
1357
  global $fwp_held_ping;
1358
  if ($fwp_held_ping):
1359
+ if (function_exists('wp_schedule_single_event')) :
1360
+ wp_schedule_single_event(time(), 'do_pings');
1361
+ else :
1362
+ generic_ping($fwp_held_ping);
1363
+ endif;
1364
  endif;
1365
  $fwp_held_ping = NULL; // NULL: not holding pings anymore
1366
  }
1367
 
1368
+ function fwp_do_pings () {
1369
+ if (!is_null($fwp_held_ping) and $post_id) : // Defer until we're done updating
 
1370
  $fwp_held_ping = $post_id;
1371
+ elseif (function_exists('do_all_pings')) :
1372
+ do_all_pings();
1373
+ else :
1374
  generic_ping($fwp_held_ping);
1375
  endif;
1376
  }
1377
 
1378
+ function fwp_publish_post_hook ($post_id) {
1379
+ global $fwp_held_ping;
1380
+
1381
+ if (!is_null($fwp_held_ping)) : // Syndicated post. Don't mark with _pingme
1382
+ if ( defined('XMLRPC_REQUEST') )
1383
+ do_action('xmlrpc_publish_post', $post_id);
1384
+ if ( defined('APP_REQUEST') )
1385
+ do_action('app_publish_post', $post_id);
1386
+
1387
+ if ( defined('WP_IMPORTING') )
1388
+ return;
1389
+
1390
+ // Defer sending out pings until we finish updating
1391
+ $fwp_held_ping = $post_id;
1392
+ else :
1393
+ if (function_exists('_publish_post_hook')) : // WordPress 2.3
1394
+ _publish_post_hook($post_id);
1395
+ endif;
1396
+ endif;
1397
+ }
1398
+
1399
  ################################################################################
1400
  ## class FeedWordPress #########################################################
1401
  ################################################################################
1434
  array('script', 'src')
1435
  );
1436
 
 
1437
  var $feeds = NULL;
1438
 
1439
+ # function FeedWordPress (): Contructor; retrieve a list of feeds
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1440
  function FeedWordPress () {
1441
+ $this->feeds = array ();
1442
+ $links = FeedWordPress::syndicated_links();
1443
+ if ($links): foreach ($links as $link):
1444
+ $this->feeds[] =& new SyndicatedLink($link);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1445
  endforeach; endif;
 
 
1446
  } // FeedWordPress::FeedWordPress ()
1447
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1448
  # function update (): polls for updates on one or more Contributor feeds
1449
  #
1450
  # Arguments:
1451
  # ----------
1452
  # * $uri (string): either the URI of the feed to poll, the URI of the
1453
+ # (human-readable) website whose feed you want to poll, or NULL.
 
 
1454
  #
1455
+ # If $uri is NULL, then FeedWordPress will poll any feeds that are
1456
+ # ready for polling. It will not poll feeds that are marked as
1457
+ # "Invisible" Links (signifying that the subscription has been
1458
+ # de-activated), or feeds that are not yet stale according to their
1459
+ # TTL setting (which is either set in the feed, or else set
1460
+ # randomly within a window of 30 minutes - 2 hours).
1461
  #
1462
  # Returns:
1463
  # --------
1467
  # are zero, there was no change since the last poll on that URI.
1468
  #
1469
  # * Returns NULL if URI it was passed was not a URI that this
1470
+ # installation of FeedWordPress syndicates.
 
 
1471
  #
1472
  # Effects:
1473
  # --------
1494
  # * Updates to existing posts since the last poll are mirrored in the
1495
  # WordPress store.
1496
  #
1497
+ function update ($uri = null) {
1498
  global $wpdb;
1499
 
 
 
1500
  if (FeedWordPress::needs_upgrade()) : // Will make duplicate posts if we don't hold off
1501
  return NULL;
1502
  endif;
1503
+
1504
+ if (!is_null($uri)) :
1505
+ $uri = trim($uri);
1506
+ else : // Update all
1507
+ update_option('feedwordpress_last_update_all', time());
1508
+ endif;
1509
 
1510
  do_action('feedwordpress_update', $uri);
1511
 
 
 
 
 
 
1512
  // Loop through and check for new posts
1513
+ $delta = NULL;
1514
  foreach ($this->feeds as $feed) :
1515
+ $pinged_that = (is_null($uri) or in_array($uri, array($feed->uri(), $feed->homepage())));
1516
 
1517
+ if (!is_null($uri)) : // A site-specific ping always updates
 
 
 
 
 
 
1518
  $timely = true;
1519
  else :
1520
+ $timely = $feed->stale();
 
 
1521
  endif;
1522
 
1523
  if ($pinged_that and is_null($delta)) : // If at least one feed was hit for updating...
1525
  endif;
1526
 
1527
  if ($pinged_that and $timely) :
1528
+ do_action('feedwordpress_check_feed', $feed->settings);
1529
+ $added = $feed->poll();
1530
  if (isset($added['new'])) : $delta['new'] += $added['new']; endif;
1531
  if (isset($added['updated'])) : $delta['updated'] += $added['updated']; endif;
1532
  endif;
1533
  endforeach;
1534
 
1535
  do_action('feedwordpress_update_complete', $delta);
 
1536
 
1537
  return $delta;
1538
  }
1539
 
1540
+ function stale () {
1541
+ if (get_option('feedwordpress_automatic_updates')) :
1542
+ $last = get_option('feedwordpress_last_update_all');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1543
 
1544
+ // If we haven't updated all yet, give it a time window
1545
+ if (false === $last) :
1546
+ $ret = false;
1547
+ update_option('feedwordpress_last_update_all', time());
1548
+
1549
+ // Otherwise, check against freshness interval
1550
+ elseif (is_numeric($last)) : // Expect a timestamp
1551
+ $freshness = get_option('feedwordpress_freshness');
1552
+ if (false === $freshness) : // Use default
1553
+ $freshness = FEEDWORDPRESS_FRESHNESS_INTERVAL;
 
 
 
 
 
 
 
1554
  endif;
1555
+ $ret = ( (time() - $last) > $freshness);
1556
+
1557
+ // This should never happen.
1558
+ else :
1559
+ FeedWordPress::critical_bug('FeedWordPress::stale::last', $last, __LINE__);
1560
+ endif;
1561
+ else :
1562
+ $ret = false;
 
 
 
 
 
 
 
 
 
 
 
 
 
1563
  endif;
1564
  return $ret;
1565
  }
1566
 
1567
+ function syndicate_link ($name, $uri, $rss) {
1568
  global $wpdb;
1569
 
1570
+ // Get the category ID#
1571
+ $cat_id = FeedWordPress::link_category_id();
1572
+
1573
+ // WordPress gets cranky if there's no homepage URI
1574
+ if (!isset($uri) or strlen($uri)<1) : $uri = $rss; endif;
1575
+
1576
+ if (function_exists('wp_insert_link')) { // WordPress 2.x
1577
+ $link_id = wp_insert_link(array(
1578
+ "link_name" => $name,
1579
+ "link_url" => $uri,
1580
+ "link_category" => array($cat_id),
1581
+ "link_rss" => $rss
1582
+ ));
1583
+ } else { // WordPress 1.5.x
1584
+ $result = $wpdb->query("
1585
+ INSERT INTO $wpdb->links
1586
+ SET
1587
+ link_name = '".$wpdb->escape($name)."',
1588
+ link_url = '".$wpdb->escape($uri)."',
1589
+ link_category = '".$wpdb->escape($cat_id)."',
1590
+ link_rss = '".$wpdb->escape($rss)."'
1591
+ ");
1592
+ $link_id = $wpdb->insert_id;
1593
+ } // if
1594
+ return $link_id;
1595
+ } // function FeedWordPress::syndicate_link()
1596
 
1597
+ function on_unfamiliar ($what = 'author', $override = NULL) {
1598
+ $set = array('create', 'default', 'filter');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1599
 
1600
+ $ret = strtolower($override);
1601
+ if (!in_array($ret, $set)) :
1602
+ $ret = get_option('feedwordpress_unfamiliar_'.$what);
1603
+ if (!in_array($ret, $set)) :
1604
+ $ret = 'create';
 
 
 
 
 
 
 
 
 
 
1605
  endif;
 
 
 
 
1606
  endif;
 
 
1607
 
1608
+ return $ret;
1609
+ } // function FeedWordPress::on_unfamiliar()
1610
 
1611
+ function syndicated_links () {
1612
+ $contributors = FeedWordPress::link_category_id();
1613
+ if (function_exists('get_bookmarks')) :
1614
+ $links = get_bookmarks(array("category" => $contributors));
1615
+ else:
1616
+ $links = get_linkobjects($contributors); // deprecated as of WP 2.1
1617
  endif;
1618
+ return $links;
1619
+ } // function FeedWordPress::syndicated_links()
1620
 
1621
+ function link_category_id () {
1622
+ global $wpdb, $wp_db_version;
 
 
1623
 
1624
+ $cat_id = get_option('feedwordpress_cat_id');
1625
+
1626
+ // If we don't yet *have* the category, we'll have to create it
1627
+ if ($cat_id === false) :
1628
+ $cat = $wpdb->escape(DEFAULT_SYNDICATION_CATEGORY);
1629
+
1630
+ // Look for something with the right name...
1631
+ // -----------------------------------------
1632
+
1633
+ // WordPress 2.3 introduces a new taxonomy/term API
1634
+ if (function_exists('is_term')) :
1635
+ $cat_id = is_term($cat, 'link_category');
1636
+ // WordPress 2.1 and 2.2 use a common table for both link and post categories
1637
+ elseif (isset($wp_db_version) and ($wp_db_version >= FWP_SCHEMA_21 and $wp_db_version < FWP_SCHEMA_23) ) :
1638
+ $cat_id = $wpdb->get_var("SELECT cat_id FROM {$wpdb->categories} WHERE cat_name='$cat'");
1639
+ // WordPress 1.5 and 2.0.x have a separate table for link categories
1640
+ elseif (!isset($wp_db_version) or $wp_db_version < FWP_SCHEMA_21) :
1641
+ $cat_id = $wpdb->get_var("SELECT cat_id FROM {$wpdb->linkcategories} WHERE cat_name='$cat'");
1642
+ // This should never happen.
1643
+ else :
1644
+ FeedWordPress::critical_bug('FeedWordPress::link_category_id::wp_db_version', $wp_db_version, __LINE__);
1645
+ endif;
1646
 
1647
+ // If you still can't find anything, make it for yourself.
1648
+ // -------------------------------------------------------
1649
+ if (!$cat_id) :
1650
+ // WordPress 2.3+ term/taxonomy API
1651
+ if (function_exists('wp_insert_term')) :
1652
+ $term = wp_insert_term($cat, 'link_category');
1653
+ $cat_id = $term['term_id'];
1654
+ // WordPress 2.1, 2.2 category API. By the way, why the fuck is this API function only available in a wp-admin module?
1655
+ elseif (function_exists('wp_insert_category')) :
1656
+ $cat_id = wp_insert_category(array('cat_name' => $cat));
1657
+ // WordPress 1.5 and 2.0.x
1658
+ elseif (!isset($wp_db_version) or $wp_db_version < FWP_SCHEMA_21) :
1659
+ $result = $wpdb->query("
1660
+ INSERT INTO $wpdb->linkcategories
1661
+ SET
1662
+ cat_id = 0,
1663
+ cat_name='$cat',
1664
+ show_images='N',
1665
+ show_description='N',
1666
+ show_rating='N',
1667
+ show_updated='N',
1668
+ sort_order='name'
1669
+ ");
1670
+ $cat_id = $wpdb->insert_id;
1671
+ // This should never happen.
1672
+ else :
1673
+ FeedWordPress::critical_bug('FeedWordPress::link_category_id::wp_db_version', $wp_db_version, __LINE__);
1674
+ endif;
1675
  endif;
 
1676
 
1677
+ update_option('feedwordpress_cat_id', $cat_id);
 
1678
  endif;
1679
+ return $cat_id;
1680
+ } // function FeedWordPress::link_category_id()
1681
 
1682
+ # Upgrades and maintenance...
1683
+ function needs_upgrade () {
1684
+ global $wpdb;
1685
+ $fwp_db_version = get_option('feedwordpress_version');
1686
+ $ret = false; // innocent until proven guilty
1687
+ if (!$fwp_db_version or $fwp_db_version < FEEDWORDPRESS_VERSION) :
1688
+ // This is an older version or a fresh install. Does it
1689
+ // require a database upgrade or database initialization?
1690
+ if ($fwp_db_version > 0.96) :
1691
+ // No. Just brand it with the new version.
1692
+ update_option('feedwordpress_version', FEEDWORDPRESS_VERSION);
1693
+ else :
1694
+ // Yes. Check to see whether this is a fresh install or an upgrade.
1695
+ $syn = $wpdb->get_col("
1696
+ SELECT post_id
1697
+ FROM $wpdb->postmeta
1698
+ WHERE meta_key = 'syndication_feed'
1699
+ ");
1700
+ if (count($syn) > 0) : // contains at least one syndicated post
1701
+ $ret = true;
1702
+ else : // fresh install; brand it as ours
1703
+ update_option('feedwordpress_version', FEEDWORDPRESS_VERSION);
1704
+ endif;
1705
+ endif;
1706
  endif;
1707
+ return $ret;
1708
+ }
1709
 
1710
+ function upgrade_database ($from = NULL) {
1711
+ global $wpdb;
 
1712
 
1713
+ if (is_null($from) or $from <= 0.96) : $from = 0.96; endif;
 
 
 
1714
 
1715
+ switch ($from) :
1716
+ case 0.96: // account for changes to syndication custom values and guid
1717
+ echo "<p>Upgrading database from {$from} to ".FEEDWORDPRESS_VERSION."...</p>\n";
 
 
1718
 
1719
+ $cat_id = FeedWordPress::link_category_id();
1720
+
1721
+ // Avoid duplicates
1722
+ $wpdb->query("DELETE FROM `{$wpdb->postmeta}` WHERE meta_key = 'syndication_feed_id'");
1723
+
1724
+ // Look up all the link IDs
1725
+ $wpdb->query("
1726
+ CREATE TEMPORARY TABLE tmp_custom_values
1727
+ SELECT
1728
+ NULL AS meta_id,
1729
+ post_id,
1730
+ 'syndication_feed_id' AS meta_key,
1731
+ link_id AS meta_value
1732
+ FROM `{$wpdb->postmeta}`, `{$wpdb->links}`
1733
+ WHERE
1734
+ meta_key='syndication_feed'
1735
+ AND meta_value=link_rss
1736
+ AND link_category = {$cat_id}
1737
+ ");
1738
+
1739
+ // Now attach them to their posts
1740
+ $wpdb->query("INSERT INTO `{$wpdb->postmeta}` SELECT * FROM tmp_custom_values");
1741
+
1742
+ // And clean up after ourselves.
1743
+ $wpdb->query("DROP TABLE tmp_custom_values");
1744
+
1745
+ // Now fix the guids to avoid duplicate posts
1746
+ echo "<ul>";
1747
+ foreach ($this->feeds as $feed) :
1748
+ echo "<li>Fixing post meta-data for <cite>".$feed['link/name']."</cite> &#8230; "; flush();
1749
+ $rss = @fetch_rss($feed['link/uri']);
1750
+ if (is_array($rss->items)) :
1751
+ foreach ($rss->items as $item) :
1752
+ $guid = $wpdb->escape(FeedWordPress::guid($item, $feed)); // new GUID algorithm
1753
+ $link = $wpdb->escape($item['link']);
1754
+
1755
+ $wpdb->query("
1756
+ UPDATE `{$wpdb->posts}` SET guid='{$guid}' WHERE guid='{$link}'
1757
+ ");
1758
+ endforeach;
1759
+ endif;
1760
+ echo "<strong>complete.</strong></li>\n";
1761
+ endforeach;
1762
+ echo "</ul>\n";
1763
+
1764
+ // Mark the upgrade as successful.
1765
+ update_option('feedwordpress_version', FEEDWORDPRESS_VERSION);
1766
+ endswitch;
1767
+ echo "<p>Upgrade complete. FeedWordPress is now ready to use again.</p>";
1768
+ } /* FeedWordPress::upgrade_database() */
1769
+
1770
+ # Utility functions for handling text settings
1771
+ function negative ($f, $setting) {
1772
+ $nego = array ('n', 'no', 'f', 'false');
1773
+ return (isset($f[$setting]) and in_array(strtolower($f[$setting]), $nego));
1774
  }
1775
 
1776
+ function affirmative ($f, $setting) {
1777
+ $affirmo = array ('y', 'yes', 't', 'true', 1);
1778
+ return (isset($f[$setting]) and in_array(strtolower($f[$setting]), $affirmo));
1779
+ }
 
 
 
 
 
 
 
 
 
 
 
 
1780
 
1781
+
1782
+ # Internal debugging functions
1783
+ function critical_bug ($varname, $var, $line) {
1784
+ global $wp_version;
1785
+
1786
+ echo '<p>There may be a bug in FeedWordPress. Please <a href="'.FEEDWORDPRESS_AUTHOR_CONTACT.'">contact the author</a> and paste the following information into your e-mail:</p>';
1787
+ echo "\n<plaintext>";
1788
+ echo "Triggered at line # ".$line."\n";
1789
+ echo "FeedWordPress version: ".FEEDWORDPRESS_VERSION."\n";
1790
+ echo "WordPress version: $wp_version\n";
1791
+ echo "PHP version: ".phpversion()."\n";
1792
+ echo "\n";
1793
+ echo $varname.": "; var_dump($var); echo "\n";
1794
+ die;
 
 
 
 
 
 
1795
  }
1796
+ } // class FeedWordPress
1797
 
1798
+ class SyndicatedPost {
1799
+ var $item = null;
1800
+
1801
+ var $link = null;
1802
+ var $feed = null;
1803
+ var $feedmeta = null;
1804
+
1805
+ var $post = array ();
1806
+ var $_base = null;
1807
+
1808
+ var $_freshness = null;
1809
+ var $_wp_id = null;
1810
+
1811
+ var $strip_attrs = array (
1812
+ array('[a-z]+', 'style'),
1813
+ array('[a-z]+', 'target'),
1814
+ );
1815
+
1816
+ function SyndicatedPost ($item, $link) {
1817
+ global $wpdb;
1818
+
1819
+ $this->link = $link;
1820
+ $feedmeta = $link->settings;
1821
+ $feed = $link->magpie;
1822
+
1823
+ # This is ugly as all hell. I'd like to use apply_filters()'s
1824
+ # alleged support for a variable argument count, but this seems
1825
+ # to have been broken in WordPress 1.5. It'll be fixed somehow
1826
+ # in WP 1.5.1, but I'm aiming at WP 1.5 compatibility across
1827
+ # the board here.
1828
+ #
1829
+ # Cf.: <http://mosquito.wordpress.org/view.php?id=901>
1830
  global $fwp_channel, $fwp_feedmeta;
1831
+ $fwp_channel = $feed; $fwp_feedmeta = $feedmeta;
1832
+
1833
+ $this->feed = $feed;
1834
+ $this->feedmeta = $feedmeta;
1835
+
1836
+ $this->item = $item;
1837
+ $this->item = apply_filters('syndicated_item', $this->item, $this);
1838
+
1839
+ # Filters can halt further processing by returning NULL
1840
+ if (is_null($this->item)) :
1841
+ $this->post = NULL;
1842
  else :
1843
+ # Note that nothing is run through $wpdb->escape() here.
1844
+ # That's deliberate. The escaping is done at the point
1845
+ # of insertion, not here, to avoid double-escaping and
1846
+ # to avoid screwing with syndicated_post filters
1847
 
1848
+ $this->post['post_title'] = $this->item['title'];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1849
 
1850
+ // This just gives usan alphanumeric representation of
1851
+ // the author. We will look up (or create) the numeric
1852
+ // ID for the author in SyndicatedPost::add()
1853
+ $this->post['named']['author'] = $this->author();
1854
 
1855
  # Identify content and sanitize it.
1856
  # ---------------------------------
1857
+ if (isset($this->item['xhtml']['body'])) :
1858
+ $content = $this->item['xhtml']['body'];
1859
+ elseif (isset($this->item['xhtml']['div'])) :
1860
+ $content = $this->item['xhtml']['div'];
1861
+ elseif (isset($this->item['content']['encoded']) and $this->item['content']['encoded']):
1862
+ $content = $this->item['content']['encoded'];
1863
  else:
1864
+ $content = $this->item['description'];
1865
  endif;
1866
 
1867
+ # FeedWordPress used to resolve URIs relative to the
1868
+ # feed URI. It now relies on the xml:base support
1869
+ # baked in to the MagpieRSS upgrade. So all we do here
1870
+ # now is to sanitize problematic attributes.
1871
  #
1872
+ # This kind of sucks. I intend to replace it with
1873
+ # lib_filter sometime soon.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1874
  foreach ($this->strip_attrs as $pair):
1875
  list($tag,$attr) = $pair;
1876
  $content = preg_replace (
1882
 
1883
  # Identify and sanitize excerpt
1884
  $excerpt = NULL;
1885
+ if ( isset($this->item['description']) and $this->item['description'] ) :
1886
+ $excerpt = $this->item['description'];
1887
  elseif ( isset($content) and $content ) :
1888
  $excerpt = strip_tags($content);
1889
  if (strlen($excerpt) > 255) :
1891
  endif;
1892
  endif;
1893
 
1894
+ $this->post['post_content'] = $content;
1895
 
1896
  if (!is_null($excerpt)):
1897
+ $this->post['post_excerpt'] = $excerpt;
1898
  endif;
1899
 
1900
+ // This is unnecessary if we use wp_insert_post
1901
+ if (!$this->use_api('wp_insert_post')) :
1902
+ $this->post['post_name'] = sanitize_title($this->post['post_title']);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1903
  endif;
1904
 
1905
+ $this->post['epoch']['issued'] = $this->published();
1906
+ $this->post['epoch']['created'] = $this->created();
1907
+ $this->post['epoch']['modified'] = $this->updated();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1908
 
1909
+ // Dealing with timestamps in WordPress is so fucking fucked.
1910
+ $offset = (int) get_option('gmt_offset') * 60 * 60;
1911
+ $this->post['post_date'] = gmdate('Y-m-d H:i:s', $this->published() + $offset);
1912
+ $this->post['post_modified'] = gmdate('Y-m-d H:i:s', $this->updated() + $offset);
1913
+ $this->post['post_date_gmt'] = gmdate('Y-m-d H:i:s', $this->published());
1914
+ $this->post['post_modified_gmt'] = gmdate('Y-m-d H:i:s', $this->updated());
1915
 
1916
+ // Use feed-level preferences or the global default.
1917
+ $this->post['post_status'] = $this->link->syndicated_status('post', 'publish');
1918
+ $this->post['comment_status'] = $this->link->syndicated_status('comment', 'closed');
1919
+ $this->post['ping_status'] = $this->link->syndicated_status('ping', 'closed');
1920
 
1921
  // Unique ID (hopefully a unique tag: URI); failing that, the permalink
1922
+ $this->post['guid'] = $this->guid();
1923
 
1924
  // RSS 2.0 / Atom 1.0 enclosure support
1925
+ if ( isset($this->item['enclosure#']) ) :
1926
+ for ($i = 1; $i <= $this->item['enclosure#']; $i++) :
1927
  $eid = (($i > 1) ? "#{$id}" : "");
1928
+ $this->post['meta']['enclosure'][] =
1929
+ $this->item["enclosure{$eid}@url"]."\n".
1930
+ $this->item["enclosure{$eid}@length"]."\n".
1931
+ $this->item["enclosure{$eid}@type"];
1932
  endfor;
1933
  endif;
1934
 
1935
  // In case you want to point back to the blog this was syndicated from
1936
+ if (isset($this->feed->channel['title'])) :
1937
+ $this->post['meta']['syndication_source'] = $this->feed->channel['title'];
1938
+ endif;
1939
+ if (isset($this->feed->channel['link'])) :
1940
+ $this->post['meta']['syndication_source_uri'] = $this->feed->channel['link'];
1941
+ endif;
1942
 
1943
  // Store information on human-readable and machine-readable comment URIs
1944
+ if (isset($this->item['comments'])) :
1945
+ $this->post['meta']['rss:comments'] = $this->item['comments'];
1946
+ endif;
1947
+ if (isset($this->item['wfw']['commentrss'])) :
1948
+ $this->post['meta']['wfw:commentRSS'] = $this->item['wfw']['commentrss'];
1949
+ endif;
1950
 
1951
  // Store information to identify the feed that this came from
1952
+ $this->post['meta']['syndication_feed'] = $this->feedmeta['link/uri'];
1953
+ $this->post['meta']['syndication_feed_id'] = $this->feedmeta['link/id'];
1954
 
1955
  // In case you want to know the external permalink...
1956
+ $this->post['meta']['syndication_permalink'] = $this->item['link'];
1957
 
1958
  // Feed-by-feed options for author and category creation
1959
+ $this->post['named']['unfamiliar']['author'] = $this->feedmeta['unfamiliar author'];
1960
+ $this->post['named']['unfamiliar']['category'] = $this->feedmeta['unfamiliar categories'];
1961
 
1962
+ // Categories: start with default categories, if any
1963
+ $fc = get_option("feedwordpress_syndication_cats");
1964
+ if ($fc) :
1965
+ $this->post['named']['preset/category'] = explode("\n", $fc);
1966
+ else :
1967
+ $this->post['named']['preset/category'] = array();
1968
  endif;
1969
+
1970
+ if (is_array($this->feedmeta['cats'])) :
1971
+ $this->post['named']['preset/category'] = array_merge($this->post['named']['preset/category'], $this->feedmeta['cats']);
1972
  endif;
1973
 
1974
  // Now add categories from the post, if we have 'em
1975
+ $this->post['named']['category'] = array();
1976
+ if ( isset($this->item['category#']) ) :
1977
+ for ($i = 1; $i <= $this->item['category#']; $i++) :
1978
  $cat_idx = (($i > 1) ? "#{$i}" : "");
1979
+ $cat = $this->item["category{$cat_idx}"];
1980
 
1981
+ if ( isset($this->feedmeta['cat_split']) and strlen($this->feedmeta['cat_split']) > 0) :
1982
+ $pcre = "\007".$this->feedmeta['cat_split']."\007";
1983
+ $this->post['named']['category'] = array_merge($this->post['named']['category'], preg_split($pcre, $cat, -1 /*=no limit*/, PREG_SPLIT_NO_EMPTY));
1984
+ else :
1985
+ $this->post['named']['category'][] = $cat;
1986
  endif;
1987
  endfor;
1988
  endif;
1989
  endif;
1990
+ } // SyndicatedPost::SyndicatedPost()
1991
+
1992
+ function filtered () {
1993
+ return is_null($this->post);
1994
+ }
1995
+
1996
+ function freshness () {
1997
+ global $wpdb;
1998
+
1999
+ if ($this->filtered()) : // This should never happen.
2000
+ FeedWordPress::critical_bug('SyndicatedPost', $this, __LINE__);
 
 
 
 
 
2001
  endif;
2002
+
2003
+ if (is_null($this->_freshness)) :
2004
+ $guid = $wpdb->escape($this->guid());
2005
+
2006
+ $result = $wpdb->get_row("
2007
+ SELECT id, guid, post_modified_gmt
2008
+ FROM $wpdb->posts WHERE guid='$guid'
2009
+ ");
2010
 
2011
+ preg_match('/([0-9]+)-([0-9]+)-([0-9]+) ([0-9]+):([0-9]+):([0-9]+)/', $result->post_modified_gmt, $backref);
2012
+ $updated = gmmktime($backref[4], $backref[5], $backref[6], $backref[2], $backref[3], $backref[1]);
2013
+ if (!$result) :
2014
+ $this->_freshness = 2; // New content
2015
+ elseif ($this->updated() > $updated) :
2016
+ $this->_freshness = 1; // Updated content
2017
+ $this->_wp_id = $result->id;
2018
+ else :
2019
+ $this->_freshness = 0; // Same old, same old
2020
+ $this->_wp_id = $result->id;
2021
+ endif;
2022
+ endif;
2023
+ return $this->_freshness;
2024
+ }
2025
+
2026
+ function wp_id () {
2027
+ if ($this->filtered()) : // This should never happen.
2028
+ FeedWordPress::critical_bug('SyndicatedPost', $this, __LINE__);
2029
+ endif;
2030
+
2031
+ if (is_null($this->_wp_id) and is_null($this->_freshness)) :
2032
+ $fresh = $this->freshness(); // sets WP DB id in the process
2033
+ endif;
2034
+ return $this->_wp_id;
2035
+ }
2036
+
2037
+ function store () {
2038
+ global $wpdb;
2039
+
2040
+ if ($this->filtered()) : // This should never happen.
2041
+ FeedWordPress::critical_bug('SyndicatedPost', $this, __LINE__);
2042
+ endif;
2043
+
2044
+ $freshness = $this->freshness();
2045
  if ($freshness > 0) :
2046
  # -- Look up, or create, numeric ID for author
2047
+ $this->post['post_author'] = $this->author_id (
2048
+ FeedWordPress::on_unfamiliar('author', $this->post['named']['unfamiliar']['author'])
 
 
 
 
2049
  );
2050
 
2051
+ if (is_null($this->post['post_author'])) :
2052
+ $this->post = NULL;
2053
+ endif;
2054
+ endif;
2055
+
2056
+ if (!$this->filtered() and $freshness > 0) :
2057
+ # -- Look up, or create, numeric ID for categories
2058
+ $this->post['post_category'] = $this->category_ids (
2059
+ $this->post['named']['category'],
2060
+ FeedWordPress::on_unfamiliar('category', $this->post['named']['unfamiliar']['category'])
2061
+ );
2062
+
2063
+ if (is_null($this->post['post_category'])) :
2064
+ // filter mode on, no matching categories; drop the post
2065
+ $this->post = NULL;
2066
  else :
2067
+ // filter mode off or at least one match; now add on the feed and global presets
2068
+ $this->post['post_category'] = array_merge (
2069
+ $this->post['post_category'],
2070
+ $this->category_ids (
2071
+ $this->post['named']['preset/category'],
2072
+ 'default'
2073
+ )
2074
  );
2075
+
2076
+ if (count($this->post['post_category']) < 1) :
2077
+ $this->post['post_category'][] = 1; // Default to category 1 ("Uncategorized" / "General") if nothing else
 
 
 
 
 
 
 
 
 
2078
  endif;
2079
  endif;
 
 
2080
  endif;
2081
 
2082
+ if (!$this->filtered() and $freshness > 0) :
2083
+ unset($this->post['named']);
2084
+ $this->post = apply_filters('syndicated_post', $this->post, $this);
2085
  endif;
2086
 
2087
+ if (!$this->filtered() and $freshness == 2) :
2088
  // The item has not yet been added. So let's add it.
2089
+ $this->insert_new();
2090
+ $this->add_rss_meta();
2091
+ do_action('post_syndicated_item', $this->wp_id());
2092
 
2093
  $ret = 'new';
2094
+ elseif (!$this->filtered() and $freshness == 1) :
2095
+ $this->post['ID'] = $this->wp_id();
2096
+ $this->update_existing();
2097
+ $this->add_rss_meta();
2098
+ do_action('update_syndicated_item', $this->wp_id());
2099
 
2100
  $ret = 'updated';
2101
  else :
2103
  endif;
2104
 
2105
  return $ret;
2106
+ } // function SyndicatedPost::store ()
2107
+
2108
+ function insert_new () {
2109
+ global $wpdb, $wp_db_version;
2110
 
2111
+ // Why the fuck doesn't wp_insert_post already do this?
2112
+ foreach ($this->post as $key => $value) :
2113
+ if (is_string($value)) :
2114
+ $dbpost[$key] = $wpdb->escape($value);
2115
+ else :
2116
+ $dbpost[$key] = $value;
2117
+ endif;
2118
+ endforeach;
2119
 
2120
+ if ($this->use_api('wp_insert_post')) :
2121
+ $dbpost['post_pingback'] = false; // Tell WP 2.1 and 2.2 not to process for pingbacks
2122
+ $this->_wp_id = wp_insert_post($dbpost);
2123
+
2124
+ // This should never happen.
2125
+ if (!is_numeric($this->_wp_id) or ($this->_wp_id == 0)) :
2126
+ FeedWordPress::critical_bug('SyndicatedPost::_wp_id', $this->_wp_id, __LINE__);
2127
+ endif;
2128
+
2129
+ // Unfortunately, as of WordPress 2.3, wp_insert_post()
2130
+ // *still* offers no way to use a guid of your choice,
2131
+ // and munges your post modified timestamp, too.
2132
+ $result = $wpdb->query("
2133
+ UPDATE $wpdb->posts
2134
+ SET
2135
+ guid='{$dbpost['guid']}',
2136
+ post_modified='{$dbpost['post_modified']}',
2137
+ post_modified_gmt='{$dbpost['post_modified_gmt']}'
2138
+ WHERE ID='{$this->_wp_id}'
2139
+ ");
2140
+ else :
2141
+ # The right way to do this is the above. But, alas,
2142
+ # in earlier versions of WordPress, wp_insert_post has
2143
+ # too much behavior (mainly related to pings) that can't
2144
+ # be overridden. In WordPress 1.5, it's enough of a
2145
+ # resource hog to make PHP segfault after inserting
2146
+ # 50-100 posts. This can get pretty annoying, especially
2147
+ # if you are trying to update your feeds for the first
2148
+ # time.
2149
+
2150
+ $result = $wpdb->query("
2151
+ INSERT INTO $wpdb->posts
2152
+ SET
2153
+ guid = '{$dbpost['guid']}',
2154
+ post_author = '{$dbpost['post_author']}',
2155
+ post_date = '{$dbpost['post_date']}',
2156
+ post_date_gmt = '{$dbpost['post_date_gmt']}',
2157
+ post_content = '{$dbpost['post_content']}',"
2158
+ .(isset($dbpost['post_excerpt']) ? "post_excerpt = '{$dbpost['post_excerpt']}'," : "")."
2159
+ post_title = '{$dbpost['post_title']}',
2160
+ post_name = '{$dbpost['post_name']}',
2161
+ post_modified = '{$dbpost['post_modified']}',
2162
+ post_modified_gmt = '{$dbpost['post_modified_gmt']}',
2163
+ comment_status = '{$dbpost['comment_status']}',
2164
+ ping_status = '{$dbpost['ping_status']}',
2165
+ post_status = '{$dbpost['post_status']}'
2166
+ ");
2167
+ $this->_wp_id = $wpdb->insert_id;
2168
+
2169
+ // This should never happen.
2170
+ if (!is_numeric($this->_wp_id) or ($this->_wp_id == 0)) :
2171
+ FeedWordPress::critical_bug('SyndicatedPost::_wp_id', $this->_wp_id, __LINE__);
2172
+ endif;
2173
+
2174
+ // WordPress 1.5.x - 2.0.x
2175
+ wp_set_post_cats('1', $this->wp_id(), $this->post['post_category']);
2176
+
2177
+ // Since we are not going through official channels, we need to
2178
+ // manually tell WordPress that we've published a new post.
2179
+ // We need to make sure to do this in order for FeedWordPress
2180
+ // to play well with the staticize-reloaded plugin (something
2181
+ // that a large aggregator website is going to *want* to be
2182
+ // able to use).
2183
+ do_action('publish_post', $this->_wp_id);
2184
+ endif;
2185
+ } /* SyndicatedPost::insert_new() */
2186
 
2187
+ function update_existing () {
2188
  global $wpdb;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2189
 
2190
+ // Why the fuck doesn't wp_insert_post already do this?
2191
+ $dbpost = array();
2192
+ foreach ($this->post as $key => $value) :
2193
+ if (is_string($value)) :
2194
+ $dbpost[$key] = $wpdb->escape($value);
2195
+ else :
2196
+ $dbpost[$key] = $value;
2197
+ endif;
2198
+ endforeach;
2199
+
2200
+ if ($this->use_api('wp_insert_post')) :
2201
+ $dbpost['post_pingback'] = false; // Tell WP 2.1 and 2.2 not to process for pingbacks
2202
+ $this->_wp_id = wp_insert_post($dbpost);
2203
+
2204
+ // This should never happen.
2205
+ if (!is_numeric($this->_wp_id) or ($this->_wp_id == 0)) :
2206
+ FeedWordPress::critical_bug('SyndicatedPost::_wp_id', $this->_wp_id, __LINE__);
2207
+ endif;
2208
 
2209
+ // Unfortunately, as of WordPress 2.3, wp_insert_post()
2210
+ // munges your post modified timestamp.
2211
+ $result = $wpdb->query("
2212
+ UPDATE $wpdb->posts
2213
+ SET
2214
+ post_modified='{$dbpost['post_modified']}',
2215
+ post_modified_gmt='{$dbpost['post_modified_gmt']}'
2216
+ WHERE ID='{$this->_wp_id}'
2217
+ ");
2218
+ else :
2219
 
2220
+ $result = $wpdb->query("
2221
+ UPDATE $wpdb->posts
2222
+ SET
2223
+ post_author = '{$dbpost['post_author']}',
2224
+ post_content = '{$dbpost['post_content']}',"
2225
+ .(isset($dbpost['post_excerpt']) ? "post_excerpt = '{$dbpost['post_excerpt']}'," : "")."
2226
+ post_title = '{$dbpost['post_title']}',
2227
+ post_name = '{$dbpost['post_name']}',
2228
+ post_modified = '{$dbpost['post_modified']}',
2229
+ post_modified_gmt = '{$dbpost['post_modified_gmt']}'
2230
+ WHERE guid='{$dbpost['guid']}'
2231
+ ");
2232
+
2233
+ // WordPress 2.1.x and up
2234
+ if (function_exists('wp_set_post_categories')) :
2235
+ wp_set_post_categories($this->wp_id(), $this->post['post_category']);
2236
+ // WordPress 1.5.x - 2.0.x
2237
+ elseif (function_exists('wp_set_post_cats')) :
2238
+ wp_set_post_cats('1', $this->wp_id(), $this->post['post_category']);
2239
+ // This should never happen.
2240
+ else :
2241
+ FeedWordPress::critical_bug('SyndicatedPost::insert_new::wp_version', $GLOBALS['wp_version'], __LINE__);
2242
+ endif;
2243
+
2244
+ // Since we are not going through official channels, we need to
2245
+ // manually tell WordPress that we've published a new post.
2246
+ // We need to make sure to do this in order for FeedWordPress
2247
+ // to play well with the staticize-reloaded plugin (something
2248
+ // that a large aggregator website is going to *want* to be
2249
+ // able to use).
2250
+ do_action('edit_post', $this->post['ID']);
2251
+ endif;
2252
+ } /* SyndicatedPost::update_existing() */
2253
+
2254
+ // SyndicatedPost::add_rss_meta: adds interesting meta-data to each entry
2255
  // using the space for custom keys. The set of keys and values to add is
2256
  // specified by the keys and values of $post['meta']. This is used to
2257
  // store anything that the WordPress user might want to access from a
2260
  // syndicated post other than author, title, timestamp, categories, and
2261
  // guid). It's also used to hook into WordPress's support for
2262
  // enclosures.
2263
+ function add_rss_meta () {
2264
+ global $wpdb;
2265
+ if ( is_array($this->post) and isset($this->post['meta']) and is_array($this->post['meta']) ) :
2266
+ $postId = $this->wp_id();
2267
+
2268
+ // Aggregated posts should NOT send out pingbacks.
2269
+ // WordPress 2.1-2.2 claim you can tell them not to
2270
+ // using $post_pingback, but they don't listen, so we
2271
+ // make sure here.
2272
+ $result = $wpdb->query("
2273
+ DELETE FROM $wpdb->postmeta
2274
+ WHERE post_id='$postId' AND meta_key='_pingme'
2275
+ ");
2276
+
2277
+ foreach ( $this->post['meta'] as $key => $values ) :
2278
 
2279
  $key = $wpdb->escape($key);
2280
 
2299
  endforeach;
2300
  endforeach;
2301
  endif;
2302
+ } /* SyndicatedPost::add_rss_meta () */
2303
 
2304
+ // SyndicatedPost::author_id (): get the ID for an author name from
2305
  // the feed. Create the author if necessary.
2306
+ function author_id ($unfamiliar_author = 'create') {
2307
+ global $wpdb, $wp_db_version; // test for WordPress 2.0 database schema
2308
+
2309
+ $a = $this->author();
2310
+ $author = $a['name'];
2311
+ $email = $a['email'];
2312
+ $url = $a['uri'];
2313
 
2314
  // Never can be too careful...
2315
  $nice_author = sanitize_title($author);
2336
  (
2337
  LOWER(user_description)
2338
  RLIKE CONCAT(
2339
+ '(^|\\n)a\\.?k\\.?a\\.?( |\\t)*:?( |\\t)*',
2340
  LCASE('$reg_author'),
2341
  '( |\\t|\\r)*(\\n|\$)'
2342
  )
2366
  meta_key = 'description'
2367
  AND LCASE(meta_value)
2368
  RLIKE CONCAT(
2369
+ '(^|\\n)a\\.?k\\.?a\\.?( |\\t)*:?( |\\t)*',
2370
  LCASE('$reg_author'),
2371
  '( |\\t|\\r)*(\\n|\$)'
2372
  )
2399
  #-- user table data
2400
  $userdata['ID'] = NULL; // new user
2401
  $userdata['user_login'] = $author;
2402
+ $userdata['user_pass'] = substr(md5(uniqid(microtime())), 0, 6); // just something random to lock it up
2403
  $userdata['user_email'] = $email;
2404
  $userdata['user_url'] = $url;
2405
  $userdata['display_name'] = $author;
2411
  endif;
2412
  endif;
2413
  return $id;
2414
+ } // function SyndicatedPost::author_id ()
2415
 
2416
  // look up (and create) category ids from a list of categories
2417
+ function category_ids ($cats, $unfamiliar_category = 'create') {
2418
+ global $wpdb;
2419
+
2420
+ // We need to normalize whitespace because (1) trailing
2421
+ // whitespace can cause PHP and MySQL not to see eye to eye on
2422
+ // VARCHAR comparisons for some versions of MySQL (cf.
2423
  // <http://dev.mysql.com/doc/mysql/en/char.html>), and (2)
2424
  // because I doubt most people want to make a semantic
2425
  // distinction between 'Computers' and 'Computers '
2426
  $cats = array_map('trim', $cats);
2427
 
2428
  $cat_ids = array ();
2429
+ foreach ($cats as $cat_name) :
2430
+ if (preg_match('/{#([0-9]+)}/', $cat_name, $backref)) :
2431
+ $cat_id = (int) $backref[1];
2432
+ if (function_exists('is_term') and is_term($cat_id, 'category')) :
2433
+ $cat_ids[] = $cat_id;
2434
+ elseif (get_category($cat_id)) :
2435
+ $cat_ids[] = $cat_id;
2436
+ endif;
2437
+ else :
2438
+ $esc = $wpdb->escape($cat_name);
2439
+ $resc = $wpdb->escape(preg_quote($cat_name));
2440
+
2441
+ // WordPress 2.3+
2442
+ if (function_exists('is_term')) :
2443
+ $cat_id = is_term($cat_name, 'category');
2444
+ if ($cat_id) :
2445
+ $cat_ids[] = $cat_id['term_id'];
2446
+ // There must be a better way to do this...
2447
+ elseif ($results = $wpdb->get_results(
2448
+ "SELECT term_id
2449
+ FROM $wpdb->term_taxonomy
2450
+ WHERE
2451
+ LOWER(description) RLIKE
2452
+ CONCAT('(^|\\n)a\\.?k\\.?a\\.?( |\\t)*:?( |\\t)*', LOWER('{$resc}'), '( |\\t|\\r)*(\\n|\$)')"
2453
+ )) :
2454
+ foreach ($results AS $term) :
2455
+ $cat_ids[] = (int) $term->term_id;
2456
+ endforeach;
2457
+ elseif ('create'===$unfamiliar_category) :
2458
+ $term = wp_insert_term($cat_name, 'category');
2459
+ $cat_ids[] = $term['term_id'];
2460
+ endif;
2461
+
2462
+ // WordPress 1.5.x - 2.2.x
2463
+ else :
2464
+ $results = $wpdb->get_results(
2465
+ "SELECT cat_ID
2466
+ FROM $wpdb->categories
2467
+ WHERE
2468
+ (LOWER(cat_name) = LOWER('$esc'))
2469
+ OR (LOWER(category_description)
2470
+ RLIKE CONCAT('(^|\\n)a\\.?k\\.?a\\.?( |\\t)*:?( |\\t)*', LOWER('{$resc}'), '( |\\t|\\r)*(\\n|\$)'))
2471
+ ");
2472
+ if ($results) :
2473
+ foreach ($results as $term) :
2474
+ $cat_ids[] = (int) $term->cat_ID;
2475
+ endforeach;
2476
+ elseif ('create'===$unfamiliar_category) :
2477
+ if (function_exists('wp_insert_category')) :
2478
+ $cat_id = wp_insert_category(array('cat_name' => $cat_name));
2479
+ // And into the database we go.
2480
+ else :
2481
+ $nice_kitty = sanitize_title($cat_name);
2482
+ $wpdb->query(sprintf("
2483
+ INSERT INTO $wpdb->categories
2484
+ SET
2485
+ cat_name='%s',
2486
+ category_nicename='%s'
2487
+ ", $wpdb->escape($cat_name), $nice_kitty
2488
+ ));
2489
+ $cat_id = $wpdb->insert_id;
2490
+ endif;
2491
+ $cat_ids[] = $cat_id;
2492
+ endif;
2493
+ endif;
2494
+ endif;
2495
+ endforeach;
2496
 
2497
+ if ((count($cat_ids) == 0) and ($unfamiliar_category === 'filter')) :
2498
+ $cat_ids = NULL; // Drop the post
2499
+ else :
2500
+ $cat_ids = array_unique($cat_ids);
2501
+ endif;
2502
+ return $cat_ids;
2503
+ } // function SyndicatedPost::category_ids ()
2504
+
2505
+ function use_api ($tag) {
2506
+ global $wp_db_version;
2507
+ if ('wp_insert_post'==$tag) :
2508
+ // Before 2.2, wp_insert_post does too much of the wrong stuff to use it
2509
+ // In 1.5 it was such a resource hog it would make PHP segfault on big updates
2510
+ $ret = (isset($wp_db_version) and $wp_db_version > FWP_SCHEMA_21);
2511
+ endif;
2512
+ return $ret;
2513
+ } // function SyndicatedPost::use_api ()
2514
+
2515
+ #### EXTRACT DATA FROM FEED ITEM ####
2516
+
2517
+ function created () {
2518
+ $epoch = null;
2519
+ if (isset($this->item['dc']['created'])) :
2520
+ $epoch = @parse_w3cdtf($this->item['dc']['created']);
2521
+ elseif (isset($this->item['dcterms']['created'])) :
2522
+ $epoch = @parse_w3cdtf($this->item['dcterms']['created']);
2523
+ elseif (isset($this->item['created'])): // Atom 0.3
2524
+ $epoch = @parse_w3cdtf($this->item['created']);
2525
+ endif;
2526
+ return $epoch;
2527
+ }
2528
+ function published ($fallback = true) {
2529
+ $epoch = null;
2530
+
2531
+ # RSS is a fucking mess. Figure out whether we have a date in
2532
+ # <dc:date>, <issued>, <pubDate>, etc., and get it into Unix
2533
+ # epoch format for reformatting. If we can't find anything,
2534
+ # we'll use the last-updated time.
2535
+ if (isset($this->item['dc']['date'])): // Dublin Core
2536
+ $epoch = @parse_w3cdtf($this->item['dc']['date']);
2537
+ elseif (isset($this->item['dcterms']['issued'])) : // Dublin Core extensions
2538
+ $epoch = @parse_w3cdtf($this->item['dcterms']['issued']);
2539
+ elseif (isset($this->item['published'])) : // Atom 1.0
2540
+ $epoch = @parse_w3cdtf($this->item['published']);
2541
+ elseif (isset($this->item['issued'])): // Atom 0.3
2542
+ $epoch = @parse_w3cdtf($this->item['issued']);
2543
+ elseif (isset($this->item['pubdate'])): // RSS 2.0
2544
+ $epoch = strtotime($this->item['pubdate']);
2545
+ elseif ($fallback) : // Fall back to <updated> / <modified> if present
2546
+ $epoch = $this->updated(/*fallback=*/ false);
2547
+ endif;
2548
+
2549
+ # If everything failed, then default to the current time.
2550
+ if (is_null($epoch)) :
2551
+ $epoch = time();
2552
+ endif;
2553
+
2554
+ return $epoch;
2555
+ }
2556
+ function updated ($fallback = true) {
2557
+ $epoch = null;
2558
+
2559
+ # As far as I know, only dcterms and Atom have reliable ways to
2560
+ # specify when something was *modified* last. If neither is
2561
+ # available, then we'll try to get the time of publication.
2562
+ if (isset($this->item['dc']['modified'])) : // Not really correct
2563
+ $epoch = @parse_w3cdtf($this->item['dc']['modified']);
2564
+ elseif (isset($this->item['dcterms']['modified'])) : // Dublin Core extensions
2565
+ $epoch = @parse_w3cdtf($this->item['dcterms']['modified']);
2566
+ elseif (isset($this->item['modified'])): // Atom 0.3
2567
+ $epoch = @parse_w3cdtf($this->item['modified']);
2568
+ elseif (isset($this->item['updated'])): // Atom 1.0
2569
+ $epoch = @parse_w3cdtf($this->item['updated']);
2570
+ elseif ($fallback) : // Fall back to issued / dc:date
2571
+ $epoch = $this->published(/*fallback=*/ false);
2572
+ endif;
2573
+
2574
+ # If everything failed, then default to the current time.
2575
+ if (is_null($epoch)) :
2576
+ $epoch = time();
2577
+ endif;
2578
 
2579
+ return $epoch;
2580
+ }
2581
 
2582
+ function guid () {
2583
+ $guid = null;
2584
+ if (isset($this->item['id'])): // Atom 0.3 / 1.0
2585
+ $guid = $this->item['id'];
2586
+ elseif (isset($this->item['atom']['id'])) : // Namespaced Atom
2587
+ $guid = $this->item['atom']['id'];
2588
+ elseif (isset($this->item['guid'])) : // RSS 2.0
2589
+ $guid = $this->item['guid'];
2590
+ elseif (isset($this->item['dc']['identifier'])) :// yeah, right
2591
+ $guid = $this->item['dc']['identifier'];
2592
+ else :
2593
+ // The feed does not seem to have provided us with a
2594
+ // unique identifier, so we'll have to cobble together
2595
+ // a tag: URI that might work for us. The base of the
2596
+ // URI will be the host name of the feed source ...
2597
+ $bits = parse_url($this->feedmeta['link/uri']);
2598
+ $guid = 'tag:'.$bits['host'];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2599
 
2600
+ // If we have a date of creation, then we can use that
2601
+ // to uniquely identify the item. (On the other hand, if
2602
+ // the feed producer was consicentious enough to
2603
+ // generate dates of creation, she probably also was
2604
+ // conscientious enough to generate unique identifiers.)
2605
+ if (!is_null($this->created())) :
2606
+ $guid .= '://post.'.date('YmdHis', $this->created());
 
 
 
 
 
2607
 
2608
+ // Otherwise, use both the URI of the item, *and* the
2609
+ // item's title. We have to use both because titles are
2610
+ // often not unique, and sometimes links aren't unique
2611
+ // either (e.g. Bitch (S)HITLIST, Mozilla Dot Org news,
2612
+ // some podcasts). But it's rare to have *both* the same
2613
+ // title *and* the same link for two different items. So
2614
+ // this is about the best we can do.
2615
+ else :
2616
+ $guid .= '://'.md5($this->item['link'].'/'.$this->item['title']);
2617
  endif;
2618
  endif;
2619
+ return $guid;
2620
+ }
2621
 
2622
+ function author () {
2623
+ $author = array ();
2624
+
2625
+ if (isset($this->item['author_name'])):
2626
+ $author['name'] = $this->item['author_name'];
2627
+ elseif (isset($this->item['dc']['creator'])):
2628
+ $author['name'] = $this->item['dc']['creator'];
2629
+ elseif (isset($this->item['dc']['contributor'])):
2630
+ $author['name'] = $this->item['dc']['contributor'];
2631
+ elseif (isset($this->feed->channel['dc']['creator'])) :
2632
+ $author['name'] = $this->feed->channel['dc']['creator'];
2633
+ elseif (isset($this->feed->channel['dc']['contributor'])) :
2634
+ $author['name'] = $this->feed->channel['dc']['contributor'];
2635
+ elseif (isset($this->feed->channel['author_name'])) :
2636
+ $author['name'] = $this->feed->channel['author_name'];
2637
+ elseif ($this->feed->is_rss() and isset($this->item['author'])) :
2638
+ // The author element in RSS is allegedly an
2639
+ // e-mail address, but lots of people don't use
2640
+ // it that way. So let's make of it what we can.
2641
+ $author = parse_email_with_realname($this->item['author']);
2642
+
2643
+ if (!isset($author['name'])) :
2644
+ if (isset($author['email'])) :
2645
+ $author['name'] = $author['email'];
2646
+ else :
2647
+ $author['name'] = $this->feed->channel['title'];
2648
+ endif;
2649
  endif;
2650
+ else :
2651
+ $author['name'] = $this->feed->channel['title'];
2652
+ endif;
2653
+
2654
+ if (isset($this->item['author_email'])):
2655
+ $author['email'] = $this->item['author_email'];
2656
+ elseif (isset($this->feed->channel['author_email'])) :
2657
+ $author['email'] = $this->feed->channel['author_email'];
2658
+ endif;
2659
+
2660
+ if (isset($this->item['author_url'])):
2661
+ $author['uri'] = $this->item['author_url'];
2662
+ elseif (isset($this->feed->channel['author_url'])) :
2663
+ $author['uri'] = $this->item['author_url'];
2664
+ else:
2665
+ $author['uri'] = $this->feed->channel['link'];
2666
  endif;
2667
 
2668
+ return $author;
2669
+ } // SyndicatedPost::author()
2670
+ } // class SyndicatedPost
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2671
 
2672
+ # class SyndicatedLink: represents a syndication feed stored within the
2673
+ # WordPress database
2674
+ #
2675
+ # To keep things compact and editable from within WordPress, we use all the
2676
+ # links under a particular category in the WordPress "Blogroll" for the list of
2677
+ # feeds to syndicate. "Contributors" is the category used by default; you can
2678
+ # configure that under Options --> Syndication.
2679
+ #
2680
+ # Fields used are:
2681
+ #
2682
+ # * link_rss: the URI of the Atom/RSS feed to syndicate
2683
+ #
2684
+ # * link_notes: user-configurable options, with keys and values
2685
+ # like so:
2686
+ #
2687
+ # key: value
2688
+ # cats: computers\nweb
2689
+ # feed/key: value
2690
+ #
2691
+ # Keys that start with "feed/" are gleaned from the data supplied
2692
+ # by the feed itself, and will be overwritten with each update.
2693
+ #
2694
+ # Values have linebreak characters escaped with C-style
2695
+ # backslashes (so, for example, a newline becomes "\n").
2696
+ #
2697
+ # The value of `cats` is used as a newline-separated list of
2698
+ # default categories for any post coming from a particular feed.
2699
+ # (In the example above, any posts from this feed will be placed
2700
+ # in the "computers" and "web" categories--*in addition to* any
2701
+ # categories that may already be applied to the posts.)
2702
+ #
2703
+ # Values of keys in link_notes are accessible from templates using
2704
+ # the function `get_feed_meta($key)` if this plugin is activated.
2705
 
2706
+ class SyndicatedLink {
2707
+ var $id = null;
2708
+ var $link = null;
2709
+ var $settings = array ();
2710
+ var $magpie = null;
 
 
 
 
 
 
 
2711
 
2712
+ function SyndicatedLink ($link) {
2713
+ global $wpdb;
2714
 
2715
+ if (is_object($link)) :
2716
+ $this->link = $link;
2717
+ $this->id = $link->link_id;
2718
+ else :
2719
+ $this->id = $link;
2720
+ if (function_exists('get_bookmark')) : // WP 2.1+
2721
+ $this->link = get_bookmark($link);
2722
+ else :
2723
+ $this->link = $wpdb->get_row("
2724
+ SELECT * FROM $wpdb->links
2725
+ WHERE (link_id = '".$wpdb->escape($link)."')"
2726
+ );
2727
+ endif;
2728
+ endif;
 
 
 
 
2729
 
2730
+ if (strlen($this->link->link_rss) > 0) :
2731
+ // Read off feed settings from link_notes
2732
+ $notes = explode("\n", $this->link->link_notes);
2733
+ foreach ($notes as $note):
2734
+ list($key, $value) = explode(": ", $note, 2);
2735
+
2736
+ if (strlen($key) > 0) :
2737
+ // Unescape and trim() off the whitespace.
2738
+ // Thanks to Ray Lischner for pointing out the
2739
+ // need to trim off whitespace.
2740
+ $this->settings[$key] = stripcslashes (trim($value));
2741
+ endif;
2742
+ endforeach;
2743
 
2744
+ // "Magic" feed settings
2745
+ $this->settings['link/uri'] = $this->link->link_rss;
2746
+ $this->settings['link/name'] = $this->link->link_name;
2747
+ $this->settings['link/id'] = $this->link->link_id;
 
2748
 
2749
+ // `hardcode categories` is deprecated in favor of `unfamiliar categories`
2750
+ if (
2751
+ FeedWordPress::affirmative($this->settings, 'hardcode categories')
2752
+ and !isset($this->settings['unfamiliar categories'])
2753
+ ) :
2754
+ $this->settings['unfamiliar categories'] = 'default';
2755
+ endif;
2756
+
2757
+ // Set this up automagically for del.icio.us
2758
+ if (!isset($this->settings['cat_split']) and false !== strpos($this->link->link_rss, 'del.icio.us')) :
2759
+ $this->settings['cat_split'] = '\s'; // Whitespace separates multiple tags in del.icio.us RSS feeds
2760
+ endif;
2761
 
2762
+ if (isset($this->settings['cats'])):
2763
+ $this->settings['cats'] = preg_split(FEEDWORDPRESS_CAT_SEPARATOR_PATTERN, $this->settings['cats']);
2764
+ endif;
2765
+ endif;
2766
+ } // SyndicatedLink::SyndicatedLink ()
2767
+
2768
+ function found () {
2769
+ return is_object($this->link);
2770
+ }
2771
 
2772
+ function stale () {
2773
+ $stale = true;
2774
+ if (isset($this->settings['update/hold']) and ($this->settings['update/hold']=='ping')) :
2775
+ $stale = false; // don't update on any timed updates; pings only
2776
+ elseif (isset($this->settings['update/hold']) and ($this->settings['update/hold']=='next')) :
2777
+ $stale = true; // update on the next timed update
2778
+ elseif (!isset($this->settings['update/ttl']) or !isset($this->settings['update/last'])) :
2779
+ $stale = true; // initial update
2780
+ else :
2781
+ $after = ((int) $this->settings['update/last'])
2782
+ +((int) $this->settings['update/ttl'] * 60);
2783
+ $stale = (time() >= $after);
2784
+ endif;
2785
+ return $stale;
2786
  }
2787
 
2788
+ function poll () {
2789
+ global $wpdb;
2790
 
2791
+ $this->magpie = fetch_rss($this->link->link_rss);
2792
+ $new_count = NULL;
2793
 
2794
+ if (is_object($this->magpie)) :
2795
+ $new_count = array('new' => 0, 'updated' => 0);
 
 
 
 
 
 
 
 
 
 
 
 
2796
 
2797
+ # -- Update Link metadata live from feed
2798
+ $channel = $this->magpie->channel;
2799
+
2800
+ if (!isset($channel['id'])) :
2801
+ $channel['id'] = $this->link->link_rss;
2802
+ endif;
2803
+
2804
+ $update = array();
2805
+ if (!$this->hardcode('url') and isset($channel['link'])) :
2806
+ $update[] = "link_url = '".$wpdb->escape($channel['link'])."'";
2807
+ endif;
2808
+
2809
+ if (!$this->hardcode('name') and isset($channel['title'])) :
2810
+ $update[] = "link_name = '".$wpdb->escape($channel['title'])."'";
2811
+ endif;
2812
+
2813
+ if (!$this->hardcode('description')) :
2814
+ if (isset($channel['tagline'])) :
2815
+ $update[] = "link_description = '".$wpdb->escape($channel['tagline'])."'";
2816
+ elseif (isset($channel['description'])) :
2817
+ $update[] = "link_description = '".$wpdb->escape($channel['description'])."'";
2818
+ endif;
2819
+ endif;
2820
+
2821
+ $this->settings = array_merge($this->settings, $this->flatten_array($channel));
2822
+
2823
+ $this->settings['update/last'] = time(); $ttl = $this->ttl();
2824
+ if (!is_null($ttl)) :
2825
+ $this->settings['update/ttl'] = $ttl;
2826
+ $this->settings['update/timed'] = 'feed';
2827
  else :
2828
+ $this->settings['update/ttl'] = rand(30, 120); // spread over time interval for staggered updates
2829
+ $this->settings['update/timed'] = 'automatically';
2830
+ endif;
2831
+
2832
+ if (!isset($this->settings['update/hold']) or $this->settings['update/hold']!='ping') :
2833
+ $this->settings['update/hold'] = 'scheduled';
2834
+ endif;
2835
+
2836
+ // Copy back without a few things that we don't want to save in the notes
2837
+ $to_notes = $this->settings;
2838
+
2839
+ if (is_array($to_notes['cats'])) :
2840
+ $to_notes['cats'] = implode(FEEDWORDPRESS_CAT_SEPARATOR, $to_notes['cats']);
2841
+ endif;
2842
+ unset($to_notes['link/id']); unset($to_notes['link/uri']);
2843
+ unset($to_notes['link/name']);
2844
+ unset($to_notes['hardcode categories']); // Deprecated
2845
+
2846
+ $notes = '';
2847
+ foreach ($to_notes as $key => $value) :
2848
+ $notes .= $key . ": ". addcslashes($value, "\0..\37".'\\') . "\n";
2849
+ endforeach;
2850
+ $update[] = "link_notes = '".$wpdb->escape($notes)."'";
2851
+
2852
+ $update_set = implode(',', $update);
2853
+
2854
+ // Update the properties of the link from the feed information
2855
+ $result = $wpdb->query("
2856
+ UPDATE $wpdb->links
2857
+ SET $update_set
2858
+ WHERE link_id='$this->id'
2859
+ ");
2860
+
2861
+ # -- Add new posts from feed and update any updated posts
2862
+ if (is_array($this->magpie->items)) :
2863
+ foreach ($this->magpie->items as $item) :
2864
+ $post =& new SyndicatedPost($item, $this);
2865
+ if (!$post->filtered()) :
2866
+ $new = $post->store();
2867
+ if ( $new !== false ) $new_count[$new]++;
2868
  endif;
2869
+ endforeach;
2870
  endif;
2871
  endif;
2872
+ return $new_count;
2873
+ } /* SyndicatedLink::poll() */
2874
+
2875
+ function uri () {
2876
+ return (is_object($this->link) ? $this->link->link_rss : NULL);
2877
+ }
2878
+ function homepage () {
2879
+ return (isset($this->settings['feed/link']) ? $this->settings['feed/link'] : NULL);
2880
  }
2881
 
2882
+ function ttl () {
2883
+ if (is_object($this->magpie)) :
2884
+ $channel = $this->magpie->channel;
2885
+ else :
2886
+ $channel = array();
2887
+ endif;
2888
 
2889
+ if (isset($channel['ttl'])) :
2890
+ // "ttl stands for time to live. It's a number of
2891
+ // minutes that indicates how long a channel can be
2892
+ // cached before refreshing from the source."
2893
+ // <http://blogs.law.harvard.edu/tech/rss#ltttlgtSubelementOfLtchannelgt>
2894
+ $ret = $channel['ttl'];
2895
+ elseif (isset($channel['sy']['updatefrequency']) or isset($channel['sy']['updateperiod'])) :
2896
+ $period_minutes = array (
2897
+ 'hourly' => 60, /* minutes in an hour */
2898
+ 'daily' => 1440, /* minutes in a day */
2899
+ 'weekly' => 10080, /* minutes in a week */
2900
+ 'monthly' => 43200, /* minutes in a month */
2901
+ 'yearly' => 525600, /* minutes in a year */
2902
+ );
2903
 
2904
+ // "sy:updatePeriod: Describes the period over which the
2905
+ // channel format is updated. Acceptable values are:
2906
+ // hourly, daily, weekly, monthly, yearly. If omitted,
2907
+ // daily is assumed." <http://web.resource.org/rss/1.0/modules/syndication/>
2908
+ if (isset($channel['sy']['updateperiod'])) : $period = $channel['sy']['updateperiod'];
2909
+ else : $period = 'daily';
2910
+ endif;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2911
 
2912
+ // "sy:updateFrequency: Used to describe the frequency
2913
+ // of updates in relation to the update period. A
2914
+ // positive integer indicates how many times in that
2915
+ // period the channel is updated. ... If omitted a value
2916
+ // of 1 is assumed." <http://web.resource.org/rss/1.0/modules/syndication/>
2917
+ if (isset($channel['sy']['updatefrequency'])) : $freq = (int) $channel['sy']['updatefrequency'];
2918
+ else : $freq = 1;
2919
+ endif;
2920
 
2921
+ $ret = (int) ($period_minutes[$period] / $freq);
2922
+ else :
2923
+ $ret = NULL;
2924
+ endif;
2925
+ return $ret;
2926
+ } /* SyndicatedLink::ttl() */
2927
+
2928
+ // SyndicatedLink:flatten_array (): flatten an array. Useful for
2929
+ // hierarchical and namespaced elements.
2930
+ //
2931
+ // Given an array which may contain array or object elements in it,
2932
+ // return a "flattened" array: a one-dimensional array of scalars
2933
+ // containing each of the scalar elements contained within the array
2934
+ // structure. Thus, for example, if $a['b']['c']['d'] == 'e', then the
2935
+ // returned array for FeedWordPress::flatten_array($a) will contain a key
2936
+ // $a['feed/b/c/d'] with value 'e'.
2937
+ function flatten_array ($arr, $prefix = 'feed/', $separator = '/') {
2938
+ $ret = array ();
2939
+ if (is_array($arr)) :
2940
+ foreach ($arr as $key => $value) :
2941
+ if (is_scalar($value)) :
2942
+ $ret[$prefix.$key] = $value;
2943
+ else :
2944
+ $ret = array_merge($ret, $this->flatten_array($value, $prefix.$key.$separator, $separator));
2945
  endif;
 
2946
  endforeach;
2947
+ endif;
2948
+ return $ret;
2949
+ } // function SyndicatedLink::flatten_array ()
2950
 
2951
+ function hardcode ($what) {
2952
+ $default = get_option("feedwordpress_hardcode_$what");
2953
+ if ( $default === 'yes' ) :
2954
+ // If the default is to hardcode, then we want the
2955
+ // negation of negative(): TRUE by default and FALSE if
2956
+ // the setting is explicitly "no"
2957
+ $ret = !FeedWordPress::negative($this->settings, "hardcode $what");
2958
+ else :
2959
+ // If the default is NOT to hardcode, then we want
2960
+ // affirmative(): FALSE by default and TRUE if the
2961
+ // setting is explicitly "yes"
2962
+ $ret = FeedWordPress::affirmative($this->settings, "hardcode $what");
2963
+ endif;
2964
+ return $ret;
2965
+ } // function SyndicatedLink::hardcode ()
2966
 
2967
+ function syndicated_status ($what, $default) {
2968
+ global $wpdb;
2969
+
2970
+ $ret = get_option("feedwordpress_syndicated_{$what}_status");
2971
+ if ( isset($this->settings["$what status"]) ) :
2972
+ $ret = $this->settings["$what status"];
2973
+ elseif (!$ret) :
2974
+ $ret = $default;
2975
+ endif;
2976
+ return $wpdb->escape(trim(strtolower($ret)));
2977
+ } // function SyndicatedLink:syndicated_status ()
2978
+ } // class SyndicatedLink
2979
 
2980
  ################################################################################
2981
  ## XML-RPC HOOKS: accept XML-RPC update pings from Contributors ################
3037
  $ret = array($this->uri);
3038
  } else {
3039
  // Assume that we have HTML or XHTML (even if we don't, who's it gonna hurt?)
 
3040
  // Autodiscovery is the preferred method
3041
  $href = $this->_link_rel_feeds();
3042
 
3069
 
3070
  function is_feed ($uri = NULL) {
3071
  $data = $this->data($uri);
3072
+
3073
  return (
3074
  preg_match (
3075
  "\007(".implode('|',$this->_feed_markers).")\007i",
3118
  } /* if */
3119
  } /* if */
3120
  } /* for */
3121
+ return $href;
3122
  }
3123
 
3124
  function _a_href_feeds ($obvious = TRUE) {
3143
  // search through the HTML, save all <link> tags
3144
  // and store each link's attributes in an associative array
3145
  preg_match_all('/<'.$tag.'\s+(.*?)\s*\/?>/si', $html, $matches);
 
3146
  $links = $matches[1];
3147
  $ret = array();
3148
  $link_count = count($links);
3168
  # for FeedWordPress's purposes. The class has been stripped down to a single
3169
  # public method: Relative_URI::resolve($url, $base), which resolves the URI in
3170
  # $url relative to the URI in $base
3171
+ #
3172
+ # The upgraded MagpieRSS also uses this class. So if we have it loaded
3173
+ # in, don't load it again.
3174
+ if (!class_exists('Relative_URI')) {
3175
 
3176
+ class Relative_URI
 
 
 
 
 
 
3177
  {
3178
+ // Resolve relative URI in $url against the base URI in $base. If $base
3179
+ // is not supplied, then we use the REQUEST_URI of this script.
3180
+ //
3181
+ // I'm hoping this method reflects RFC 2396 Section 5.2
3182
+ function resolve ($url, $base = NULL)
3183
+ {
3184
+ if (is_null($base)):
3185
+ $base = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
3186
+ endif;
3187
+
3188
+ $base = Relative_URI::_encode(trim($base));
3189
+ $uri_parts = Relative_URI::_parse_url($base);
3190
+
3191
+ $url = Relative_URI::_encode(trim($url));
3192
+ $parts = Relative_URI::_parse_url($url);
3193
+
3194
+ $uri_parts['fragment'] = (isset($parts['fragment']) ? $parts['fragment'] : null);
3195
+ $uri_parts['query'] = (isset($parts['query']) ? $parts['query'] : null);
3196
+
3197
+ // if path is empty, and scheme, host, and query are undefined,
3198
+ // the URL is referring the base URL
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3199
 
3200
+ if (($parts['path'] == '') && !isset($parts['scheme']) && !isset($parts['host']) && !isset($parts['query'])) {
3201
+ // If the URI is empty or only a fragment, return the base URI
3202
+ return $base . (isset($parts['fragment']) ? '#'.$parts['fragment'] : '');
3203
+ } elseif (isset($parts['scheme'])) {
3204
+ // If the scheme is set, then the URI is absolute.
3205
+ return $url;
3206
+ } elseif (isset($parts['host'])) {
3207
+ $uri_parts['host'] = $parts['host'];
3208
  $uri_parts['path'] = $parts['path'];
3209
  } else {
3210
+ // We have a relative path but not a host.
3211
+
3212
+ // start ugly fix:
3213
+ // prepend slash to path if base host is set, base path is not set, and url path is not absolute
3214
+ if ($uri_parts['host'] && ($uri_parts['path'] == '')
3215
+ && (strlen($parts['path']) > 0)
3216
+ && (substr($parts['path'], 0, 1) != '/')) {
3217
+ $parts['path'] = '/'.$parts['path'];
3218
+ } // end ugly fix
3219
+
3220
+ if (substr($parts['path'], 0, 1) == '/') {
3221
+ $uri_parts['path'] = $parts['path'];
3222
+ } else {
3223
+ // copy base path excluding any characters after the last (right-most) slash character
3224
+ $buffer = substr($uri_parts['path'], 0, (int)strrpos($uri_parts['path'], '/')+1);
3225
+ // append relative path
3226
+ $buffer .= $parts['path'];
3227
+ // remove "./" where "." is a complete path segment.
3228
+ $buffer = str_replace('/./', '/', $buffer);
3229
+ if (substr($buffer, 0, 2) == './') {
3230
+ $buffer = substr($buffer, 2);
3231
+ }
3232
+ // if buffer ends with "." as a complete path segment, remove it
3233
+ if (substr($buffer, -2) == '/.') {
3234
+ $buffer = substr($buffer, 0, -1);
3235
+ }
3236
+ // remove "<segment>/../" where <segment> is a complete path segment not equal to ".."
3237
+ $search_finished = false;
3238
+ $segment = explode('/', $buffer);
3239
+ while (!$search_finished) {
3240
+ for ($x=0; $x+1 < count($segment);) {
3241
+ if (($segment[$x] != '') && ($segment[$x] != '..') && ($segment[$x+1] == '..')) {
3242
+ if ($x+2 == count($segment)) $segment[] = '';
3243
+ unset($segment[$x], $segment[$x+1]);
3244
+ $segment = array_values($segment);
3245
+ continue 2;
3246
+ } else {
3247
+ $x++;
3248
+ }
3249
+ }
3250
+ $search_finished = true;
3251
  }
3252
+ $buffer = (count($segment) == 1) ? '/' : implode('/', $segment);
3253
+ $uri_parts['path'] = $buffer;
3254
+
3255
  }
 
 
 
3256
  }
3257
+
3258
+ // If we've gotten to this point, we can try to put the pieces
3259
+ // back together.
3260
+ $ret = '';
3261
+ if (isset($uri_parts['scheme'])) $ret .= $uri_parts['scheme'].':';
3262
+ if (isset($uri_parts['user'])) {
3263
+ $ret .= $uri_parts['user'];
3264
+ if (isset($uri_parts['pass'])) $ret .= ':'.$uri_parts['parts'];
3265
+ $ret .= '@';
3266
+ }
3267
+ if (isset($uri_parts['host'])) {
3268
+ $ret .= '//'.$uri_parts['host'];
3269
+ if (isset($uri_parts['port'])) $ret .= ':'.$uri_parts['port'];
3270
+ }
3271
+ $ret .= $uri_parts['path'];
3272
+ if (isset($uri_parts['query'])) $ret .= '?'.$uri_parts['query'];
3273
+ if (isset($uri_parts['fragment'])) $ret .= '#'.$uri_parts['fragment'];
3274
+
3275
+ return $ret;
3276
+ }
3277
+
3278
+ /**
3279
+ * Parse URL
3280
+ *
3281
+ * Regular expression grabbed from RFC 2396 Appendix B.
3282
+ * This is a replacement for PHPs builtin parse_url().
3283
+ * @param string $url
3284
+ * @access private
3285
+ * @return array
3286
+ */
3287
+ function _parse_url($url)
3288
+ {
3289
+ // I'm using this pattern instead of parse_url() as there's a few strings where parse_url()
3290
+ // generates a warning.
3291
+ if (preg_match('!^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?!', $url, $match)) {
3292
+ $parts = array();
3293
+ if ($match[1] != '') $parts['scheme'] = $match[2];
3294
+ if ($match[3] != '') $parts['auth'] = $match[4];
3295
+ // parse auth
3296
+ if (isset($parts['auth'])) {
3297
+ // store user info
3298
+ if (($at_pos = strpos($parts['auth'], '@')) !== false) {
3299
+ $userinfo = explode(':', substr($parts['auth'], 0, $at_pos), 2);
3300
+ $parts['user'] = $userinfo[0];
3301
+ if (isset($userinfo[1])) $parts['pass'] = $userinfo[1];
3302
+ $parts['auth'] = substr($parts['auth'], $at_pos+1);
3303
+ }
3304
+ // get port number
3305
+ if ($port_pos = strrpos($parts['auth'], ':')) {
3306
+ $parts['host'] = substr($parts['auth'], 0, $port_pos);
3307
+ $parts['port'] = (int)substr($parts['auth'], $port_pos+1);
3308
+ if ($parts['port'] < 1) $parts['port'] = null;
3309
+ } else {
3310
+ $parts['host'] = $parts['auth'];
3311
+ }
3312
+ }
3313
+ unset($parts['auth']);
3314
+ $parts['path'] = $match[5];
3315
+ if (isset($match[6]) && ($match[6] != '')) $parts['query'] = $match[7];
3316
+ if (isset($match[8]) && ($match[8] != '')) $parts['fragment'] = $match[9];
3317
+ return $parts;
3318
  }
3319
+ // shouldn't reach here
3320
+ return array('path'=>'');
3321
+ }
3322
+
3323
+ function _encode($string)
3324
+ {
3325
+ static $replace = array();
3326
+ if (!count($replace)) {
3327
+ $find = array(32, 34, 60, 62, 123, 124, 125, 91, 92, 93, 94, 96, 127);
3328
+ $find = array_merge(range(0, 31), $find);
3329
+ $find = array_map('chr', $find);
3330
+ foreach ($find as $char) {
3331
+ $replace[$char] = '%'.bin2hex($char);
3332
+ }
3333
  }
3334
+ // escape control characters and a few other characters
3335
+ $encoded = strtr($string, $replace);
3336
+ // remove any character outside the hex range: 21 - 7E (see www.asciitable.com)
3337
+ return preg_replace('/[^\x21-\x7e]/', '', $encoded);
3338
+ }
3339
+ } // class Relative_URI
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3340
  }
3341
 
3342
  // take your best guess at the realname and e-mail, given a string
3343
  define('FWP_REGEX_EMAIL_ADDY', '([^@"(<\s]+@[^"@(<\s]+\.[^"@(<\s]+)');
3344
  define('FWP_REGEX_EMAIL_NAME', '("([^"]*)"|([^"<(]+\S))');
3345
+ define('FWP_REGEX_EMAIL_POSTFIX_NAME', '/^\s*'.FWP_REGEX_EMAIL_ADDY."\s+\(".FWP_REGEX_EMAIL_NAME.'\)\s*$/');
3346
+ define('FWP_REGEX_EMAIL_PREFIX_NAME', '/^\s*'.FWP_REGEX_EMAIL_NAME.'\s*<'.FWP_REGEX_EMAIL_ADDY.'>\s*$/');
3347
+ define('FWP_REGEX_EMAIL_JUST_ADDY', '/^\s*'.FWP_REGEX_EMAIL_ADDY.'\s*$/');
3348
+ define('FWP_REGEX_EMAIL_JUST_NAME', '/^\s*'.FWP_REGEX_EMAIL_NAME.'\s*$/');
3349
 
3350
  function parse_email_with_realname ($email) {
3351
  if (preg_match(FWP_REGEX_EMAIL_POSTFIX_NAME, $email, $matches)) :
3364
  endif;
3365
  return $ret;
3366
  }
 
3367
  ?>
wp-content/plugins/feedwordpress/syndication-options.php ADDED
@@ -0,0 +1,252 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ function fwp_syndication_options_page () {
3
+ global $wpdb, $wp_db_version;
4
+
5
+ if (FeedWordPress::needs_upgrade()) :
6
+ fwp_upgrade_page();
7
+ return;
8
+ endif;
9
+
10
+ $caption = 'Save Changes';
11
+ if (isset($_POST['action']) and $_POST['action']==$caption):
12
+ check_admin_referer();
13
+
14
+ if (!current_user_can('manage_options')):
15
+ die (__("Cheatin' uh ?"));
16
+ else:
17
+ update_option('feedwordpress_cat_id', $_REQUEST['syndication_category']);
18
+ update_option('feedwordpress_munge_permalink', $_REQUEST['munge_permalink']);
19
+ update_option('feedwordpress_update_logging', $_REQUEST['update_logging']);
20
+ update_option('feedwordpress_unfamiliar_author', $_REQUEST['unfamiliar_author']);
21
+ update_option('feedwordpress_unfamiliar_category', $_REQUEST['unfamiliar_category']);
22
+ update_option('feedwordpress_syndicated_post_status', $_REQUEST['post_status']);
23
+ update_option('feedwordpress_automatic_updates', ($_POST['automatic_updates']=='yes'));
24
+ update_option('feedwordpress_freshness', ($_POST['freshness_interval']*60));
25
+
26
+ // Categories
27
+ $cats = array();
28
+ if (isset($_POST['post_category'])) :
29
+ $cats = array();
30
+ foreach ($_POST['post_category'] as $cat_id) :
31
+ $cats[] = '{#'.$cat_id.'}';
32
+ endforeach;
33
+ endif;
34
+
35
+ if (!empty($cats)) :
36
+ update_option('feedwordpress_syndication_cats', implode("\n", $cats));
37
+ else :
38
+ delete_option('feedwordpress_syndication_cats');
39
+ endif;
40
+
41
+ if (isset($_REQUEST['comment_status']) and ($_REQUEST['comment_status'] == 'open')) :
42
+ update_option('feedwordpress_syndicated_comment_status', 'open');
43
+ else :
44
+ update_option('feedwordpress_syndicated_comment_status', 'closed');
45
+ endif;
46
+
47
+ if (isset($_REQUEST['ping_status']) and ($_REQUEST['ping_status'] == 'open')) :
48
+ update_option('feedwordpress_syndicated_ping_status', 'open');
49
+ else :
50
+ update_option('feedwordpress_syndicated_ping_status', 'closed');
51
+ endif;
52
+
53
+ if (isset($_REQUEST['hardcode_name']) and ($_REQUEST['hardcode_name'] == 'no')) :
54
+ update_option('feedwordpress_hardcode_name', 'no');
55
+ else :
56
+ update_option('feedwordpress_hardcode_name', 'yes');
57
+ endif;
58
+
59
+ if (isset($_REQUEST['hardcode_description']) and ($_REQUEST['hardcode_description'] == 'no')) :
60
+ update_option('feedwordpress_hardcode_description', 'no');
61
+ else :
62
+ update_option('feedwordpress_hardcode_description', 'yes');
63
+ endif;
64
+
65
+ if (isset($_REQUEST['hardcode_url']) and ($_REQUEST['hardcode_url'] == 'no')) :
66
+ update_option('feedwordpress_hardcode_url', 'no');
67
+ else :
68
+ update_option('feedwordpress_hardcode_url', 'yes');
69
+ endif;
70
+ ?>
71
+ <div class="updated">
72
+ <p><?php _e('Options saved.')?></p>
73
+ </div>
74
+ <?php
75
+ endif;
76
+ endif;
77
+
78
+ $cat_id = FeedWordPress::link_category_id();
79
+ $munge_permalink = get_option('feedwordpress_munge_permalink');
80
+ $update_logging = get_option('feedwordpress_update_logging');
81
+
82
+ $automatic_updates = get_option('feedwordpress_automatic_updates');
83
+
84
+ $freshness_interval = get_option('feedwordpress_freshness');
85
+ if (false === $freshness_interval) :
86
+ $freshness_interval = FEEDWORDPRESS_FRESHNESS_INTERVAL;
87
+ endif;
88
+ $freshness_interval = $freshness_interval / 60; // convert to minutes
89
+
90
+ $hardcode_name = get_option('feedwordpress_hardcode_name');
91
+ $hardcode_description = get_option('feedwordpress_hardcode_description');
92
+ $hardcode_url = get_option('feedwordpress_hardcode_url');
93
+
94
+ $post_status = get_option("feedwordpress_syndicated_post_status"); // default="publish"
95
+ $comment_status = get_option("feedwordpress_syndicated_comment_status"); // default="closed"
96
+ $ping_status = get_option("feedwordpress_syndicated_ping_status"); // default="closed"
97
+
98
+ $unfamiliar_author = array ('create' => '','default' => '','filter' => '');
99
+ $ua = FeedWordPress::on_unfamiliar('author');
100
+ if (is_string($ua) and array_key_exists($ua, $unfamiliar_author)) :
101
+ $unfamiliar_author[$ua] = ' checked="checked"';
102
+ endif;
103
+ $unfamiliar_category = array ('create'=>'','default'=>'','filter'=>'');
104
+ $uc = FeedWordPress::on_unfamiliar('category');
105
+ if (is_string($uc) and array_key_exists($uc, $unfamiliar_category)) :
106
+ $unfamiliar_category[$uc] = ' checked="checked"';
107
+ endif;
108
+
109
+ if (isset($wp_db_version) and $wp_db_version >= 4772) :
110
+ $results = get_categories('type=link');
111
+
112
+ // Guarantee that the Contributors category will be in the drop-down chooser, even if it is empty.
113
+ $found_link_category_id = false;
114
+ foreach ($results as $row) :
115
+ if ($row->cat_id == $cat_id) : $found_link_category_id = true; endif;
116
+ endforeach;
117
+
118
+ if (!$found_link_category_id) : $results[] = get_category($cat_id); endif;
119
+ else :
120
+ $results = $wpdb->get_results("SELECT cat_id, cat_name, auto_toggle FROM $wpdb->linkcategories ORDER BY cat_id");
121
+ endif;
122
+
123
+ $cats = get_option('feedwordpress_syndication_cats');
124
+ $dogs = get_nested_categories(-1, 0);
125
+ $cats = array_map('strtolower',
126
+ array_map('trim',
127
+ preg_split(FEEDWORDPRESS_CAT_SEPARATOR_PATTERN, $cats)
128
+ ));
129
+
130
+ foreach ($dogs as $tag => $dog) :
131
+ $found_by_name = in_array(strtolower(trim($dog['cat_name'])), $cats);
132
+ if (isset($dog['cat_ID'])) : $dog['cat_id'] = $dog['cat_ID']; endif;
133
+ $found_by_id = in_array('{#'.trim($dog['cat_id']).'}', $cats);
134
+
135
+ if ($found_by_name or $found_by_id) :
136
+ $dogs[$tag]['checked'] = true;
137
+ endif;
138
+ endforeach;
139
+
140
+ ?>
141
+ <div class="wrap">
142
+ <h2>Syndication Options</h2>
143
+ <form action="" method="post">
144
+ <fieldset class="options">
145
+ <legend>Syndicated Feeds</legend>
146
+ <table class="editform" width="100%" cellspacing="2" cellpadding="5">
147
+ <tr>
148
+ <th width="33%" scope="row">Syndicated Link category:</th>
149
+ <td width="67%"><?php
150
+ echo "\n<select name=\"syndication_category\" size=\"1\">";
151
+ foreach ($results as $row) {
152
+ if (!isset($row->cat_id)) { $row->cat_id = $row->cat_ID; }
153
+
154
+ echo "\n\t<option value=\"$row->cat_id\"";
155
+ if ($row->cat_id == $cat_id)
156
+ echo " selected='selected'";
157
+ echo ">$row->cat_id: ".wp_specialchars($row->cat_name);
158
+ if ('Y' == $row->auto_toggle)
159
+ echo ' (auto toggle)';
160
+ echo "</option>\n";
161
+ }
162
+ echo "\n</select>\n";
163
+ ?></td>
164
+ </tr>
165
+
166
+ <tr>
167
+ <th width="33%" scope="row">Check for new posts:</th>
168
+ <td width="67%"><select name="automatic_updates" size="1" onchange="if (this.value=='yes') { disp = 'inline'; } else { disp = 'none'; }; el=document.getElementById('automatic-update-interval-span'); if (el) el.style.display=disp;">
169
+ <option value="yes"<?php echo ($automatic_updates)?' selected="selected"':''; ?>>automatically</option>
170
+ <option value="no"<?php echo (!$automatic_updates)?' selected="selected"':''; ?>>only when I request</option>
171
+ </select>
172
+ <span id="automatic-update-interval-span" style="display: <?php echo $automatic_updates?'inline':'none';?>"><label for="automatic-update-interval">every</label> <input id="automatic-update-interval" name="freshness_interval" value="<?php echo $freshness_interval; ?>" size="4" /> minutes.</span>
173
+ </td>
174
+ </tr>
175
+
176
+ <tr><th width="33%" scope="row" style="vertical-align:top">Feed information:</th>
177
+ <td width="67%"><ul style="margin:0;padding:0;list-style:none">
178
+ <li><input type="checkbox" name="hardcode_name" value="no"<?php echo (($hardcode_name=='yes')?'':' checked="checked"');?>/> Update the contributor title when the feed title changes</li>
179
+ <li><input type="checkbox" name="hardcode_description" value="no"<?php echo (($hardcode_description=='yes')?'':' checked="checked"');?>/> Update when contributor description if the feed tagline changes</li>
180
+ <li><input type="checkbox" name="hardcode_url" value="no"<?php echo (($hardcode_url=='yes')?'':' checked="checked"');?>/> Update the contributor homepage when the feed link changes</li>
181
+ </ul></td></tr>
182
+ </table>
183
+ <div class="submit"><input type="submit" name="action" value="<?php echo $caption; ?>" /></div>
184
+ </fieldset>
185
+
186
+ <fieldset class="options">
187
+ <legend>Syndicated Posts</legend>
188
+
189
+ <?php fwp_category_box($dogs, '<em>all syndicated posts</em>'); ?>
190
+
191
+ <table class="editform" width="75%" cellspacing="2" cellpadding="5">
192
+ <tr style="vertical-align: top"><th width="44%" scope="row">Publication:</th>
193
+ <td width="56%"><ul style="margin: 0; padding: 0; list-style:none">
194
+ <li><label><input type="radio" name="post_status" value="publish"<?php echo (!$post_status or $post_status=='publish')?' checked="checked"':''; ?> /> Publish syndicated posts immediately</label></li>
195
+ <li><label><input type="radio" name="post_status" value="draft"<?php echo ($post_status=='draft')?' checked="checked"':''; ?> /> Hold syndicated posts as drafts</label></li>
196
+ <li><label><input type="radio" name="post_status" value="private"<?php echo ($post_status=='private')?' checked="checked"':''; ?> /> Hold syndicated posts as private posts</label></li>
197
+ </ul></td></tr>
198
+
199
+ <tr style="vertical-align: top"><th width="44%" scope="row">Comments:</th>
200
+ <td width="56%"><ul style="margin: 0; padding: 0; list-style:none">
201
+ <li><label><input type="radio" name="comment_status" value="open"<?php echo ($comment_status=='open')?' checked="checked"':''; ?> /> Allow comments on syndicated posts</label></li>
202
+ <li><label><input type="radio" name="comment_status" value="closed"<?php echo ($comment_status!='open')?' checked="checked"':''; ?> /> Don't allow comments on syndicated posts</label></li>
203
+ </ul></td></tr>
204
+
205
+ <tr style="vertical-align: top"><th width="44%" scope="row">Trackback and Pingback:</th>
206
+ <td width="56%"><ul style="margin:0; padding: 0; list-style:none">
207
+ <li><label><input type="radio" name="ping_status" value="open"<?php echo ($ping_status=='open')?' checked="checked"':''; ?> /> Accept pings on syndicated posts</label></li>
208
+ <li><label><input type="radio" name="ping_status" value="closed"<?php echo ($ping_status!='open')?' checked="checked"':''; ?> /> Don't accept pings on syndicated posts</label></li>
209
+ </ul></td></tr>
210
+
211
+ <tr style="vertical-align: top"><th width="44%" scope="row" style="vertical-align:top">Unfamiliar authors:</th>
212
+ <td width="56%"><ul style="margin: 0; padding: 0; list-style:none">
213
+ <li><label><input type="radio" name="unfamiliar_author" value="create"<?php echo $unfamiliar_author['create']; ?>/> create a new author account</label></li>
214
+ <li><label><input type="radio" name="unfamiliar_author" value="default"<?php echo $unfamiliar_author['default']; ?> /> attribute the post to the default author</label></li>
215
+ <li><label><input type="radio" name="unfamiliar_author" value="filter"<?php echo $unfamiliar_author['filter']; ?> /> don't syndicate the post</label></li>
216
+ </ul></td></tr>
217
+
218
+ <tr style="vertical-align: top"><th width="44%" scope="row" style="vertical-align:top">Unfamiliar categories:</th>
219
+ <td width="56%"><ul style="margin: 0; padding:0; list-style:none">
220
+ <li><label><input type="radio" name="unfamiliar_category" value="create"<?php echo $unfamiliar_category['create']; ?>/> create any categories the post is in</label></li>
221
+ <li><label><input type="radio" name="unfamiliar_category" value="default"<?php echo $unfamiliar_category['default']; ?>/> don't create new categories</li>
222
+ <li><label><input type="radio" name="unfamiliar_category" value="filter"<?php echo $unfamiliar_category['filter']; ?>/> don't create new categories and don't syndicate posts unless they match at least one familiar category</label></li>
223
+ </ul></td></tr>
224
+
225
+ <tr style="vertical-align: top"><th width="44%" scope="row">Permalinks point to:</th>
226
+ <td width="56%"><select name="munge_permalink" size="1">
227
+ <option value="yes"<?php echo ($munge_permalink=='yes')?' selected="selected"':''; ?>>original website</option>
228
+ <option value="no"<?php echo ($munge_permalink=='no')?' selected="selected"':''; ?>>this website</option>
229
+ </select></td></tr>
230
+ </table>
231
+ <div class="submit"><input type="submit" name="action" value="<?php echo $caption; ?>" /></div>
232
+ </fieldset>
233
+
234
+ <fieldset class="options">
235
+ <legend>Back-end Options</legend>
236
+ <table class="editform" width="100%" cellspacing="2" cellpadding="5">
237
+ <th width="33%" scope="row">Write update notices to PHP logs:</th>
238
+ <td width="67%"><select name="update_logging" size="1">
239
+ <option value="yes"<?php echo (($update_logging=='yes')?' selected="selected"':''); ?>>yes</option>
240
+ <option value="no"<?php echo (($update_logging!='yes')?' selected="selected"':''); ?>>no</option>
241
+ </select></td>
242
+ </tr>
243
+ </table>
244
+ <div class="submit"><input type="submit" name="action" value="<?php echo $caption; ?>" /></div>
245
+ </fieldset>
246
+ </form>
247
+ </div>
248
+ <?php
249
+ }
250
+
251
+ fwp_syndication_options_page();
252
+ ?>
wp-content/update-feeds.php DELETED
@@ -1,205 +0,0 @@
1
- <?php
2
- # update-feeds.php: Instruct FeedWordPress to scan for fresh content
3
- #
4
- # Project: FeedWordPress
5
- # URI: <http://projects.radgeek.com/feedwordpress>
6
- # Author: Charles Johnson <technophilia@radgeek.com>
7
- # License: GPL
8
- # Version: 2005.11.06
9
- #
10
- # USAGE
11
- # -----
12
- # update-feeds.php is a useful script for instructing the FeedWordPress plugin
13
- # to scan for fresh content on the feeds that it syndicates. This is handy if
14
- # you want your syndication site to actually syndicate content, and you can't
15
- # rely on all your contributors to send you an XML-RPC ping when they update.
16
- #
17
- # 1. Install FeedWordPress and activate the FeedWordPress plugin. (See
18
- # <http://projects.radgeek.com/feedwordpress/install> if you need a guide
19
- # for the perplexed.)
20
- #
21
- # 2. If you want to manually update one or more of your feeds, you can do
22
- # so by pointing your web browser to the URI
23
- # <http://xyz.com/wp-content/update-feeds.php>, where `http://xyz.com/` is
24
- # replaced by the URI to your installation of WordPress. Log in as any
25
- # user in your WordPress database and use the form to update.
26
- #
27
- # 3. To keep content up-to-date automatically, set up a cron job to run
28
- # update-feeds.php locally:
29
- #
30
- # cd /path/to/wordpress/wp-content ; php update-feeds.php
31
- #
32
- # where `/path/to/wordpress` is replaced by the filesystem path to your
33
- # installation of WordPress; or, if you don't have, or don't want to use,
34
- # command-line PHP, you can send an HTTP POST request to the appropriate
35
- # URI:
36
- #
37
- # curl --user user:pass http://xyz.com/wp-content/update-feeds.php -d update=quiet
38
- #
39
- # `user` and `pass` should be replaced by the username and password of
40
- # a user in your WordPress database (you can create a dummy user for
41
- # updates if you want; that's what I do). `http://xyz.com/` should be
42
- # replaced by the URI to your installation of WordPress.
43
- #
44
- # Don't be afraid to run this cron job frequently. FeedWordPress staggers
45
- # updates over time rather than checking all of the feeds every time the
46
- # cron job runs, so even if the cron job runs every 10 minutes, each feed
47
- # will, on average only be polled for updates once an hour or so (or less
48
- # frequently if the feed author requests less frequent updates using
49
- # the RSS <ttl> element or the syndication module elements).
50
- #
51
- # 4. If you want to update *one* of the feeds rather than *all* of them, then
52
- # pass the URI and title as command-line arguments:
53
- #
54
- # $ php update-feeds.php http://radgeek.com
55
- #
56
- # or in the POST request:
57
- #
58
- # $ curl --user login:password http://xyz.com/wp-content/update-feeds.php -d uri=http://www.radgeek.com\&update=quiet
59
- #
60
- // Help us to pick out errors, if any.
61
- ini_set('error_reporting', E_ALL & ~E_NOTICE);
62
- ini_set('display_errors', true);
63
- define('MAGPIE_DEBUG', true);
64
-
65
- // Are we running from a web request or from the command line?
66
- if (!isset($_SERVER['REQUEST_URI'])) :
67
- $update_feeds_display = 'text/plain';
68
- $update_feeds_invoke = 'cmd';
69
- elseif (isset($_POST['update'])) :
70
- if ($_POST['update'] == 'quiet') :
71
- $update_feeds_display = 'text/plain';
72
- $update_feeds_invoke = 'post';
73
- $update_feeds_verbose = false;
74
- elseif ($_POST['update'] == 'verbose') :
75
- $update_feeds_display = 'text/plain';
76
- $update_feeds_invoke = 'post';
77
- $update_feeds_verbose = true;
78
- else :
79
- $update_feeds_display = 'text/html';
80
- $update_feeds_invoke = 'post';
81
- endif;
82
- else :
83
- $update_feeds_display = 'text/html';
84
- $update_feeds_invoke = 'get';
85
- endif;
86
-
87
- require_once ('../wp-blog-header.php');
88
-
89
- function update_feeds_mention ($feed) {
90
- global $update_feeds_display;
91
-
92
- if ($update_feeds_display=='text/html') :
93
- echo "<li>Updating <cite>".$feed['link/name']."</cite> from &lt;<a href=\""
94
- .$feed['link/uri']."\">".$feed['link/uri']."</a>&gt; ...</li>\n";
95
- else :
96
- echo "* Updating ".$feed['link/name']." from <".$feed['link/uri']."> ...\n";
97
- endif;
98
- flush();
99
- }
100
-
101
- # -- Don't change these unless you know what you're doing...
102
- define ('RPC_MAGIC', 'tag:radgeek.com/projects/feedwordpress/'); // update all
103
-
104
- // Query secret word from database
105
- $rpc_secret = get_settings('feedwordpress_rpc_secret');
106
-
107
- header("Content-Type: {$update_feeds_display}; charset=utf-8");
108
-
109
- # -- Are we running from an HTTP GET, HTTP POST, or from the command line?
110
- if ($update_feeds_invoke != 'cmd') : // We're acessing this from HTTP GET or HTTP POST
111
- // Authenticate the user, if possible ...
112
- if (isset($_SERVER['PHP_AUTH_USER']) and isset($_SERVER['PHP_AUTH_PW'])) : // try HTTP authentication
113
- $login = $_SERVER['PHP_AUTH_USER']; $pass = $_SERVER['PHP_AUTH_PW'];
114
- elseif (isset($_POST['log']) and isset($_POST['pwd'])) : // try POST data
115
- $login = $_REQUEST['log']; $pass = $_REQUEST['pwd'];
116
- endif;
117
-
118
- if (empty($login) or empty($pass) or !wp_login($login, $pass)) :
119
- if ($update_feeds_display=='text/html') :
120
- auth_redirect(); // try authentication cookies; if all else fails, redirect to wp-login.php
121
- else :
122
- echo "update-feeds (".date('Y-m-d H:i:s')."): ERROR: Could not log in as '$login' (password: '$pass')\n";
123
- die;
124
- endif;
125
- endif;
126
-
127
- // Henceforward, we can proceed on the assumption that we have an authenticated user
128
- $uri = (isset($_REQUEST['uri']) ? $_REQUEST['uri'] : RPC_MAGIC.$rpc_secret);
129
-
130
- if ($update_feeds_display=='text/html') :
131
- echo <<<EOHTML
132
- <?xml version="1.0" encoding="utf-8"?>
133
- <html>
134
- <head>
135
- <title>update-feeds :: FeedWordPress</title>
136
- </head>
137
-
138
- <body>
139
- <h1>update-feeds: make FeedWordPress check for new syndicated content</h1>
140
-
141
- EOHTML;
142
- endif;
143
- else :
144
- // update-feeds has been invoked from the command line; no further
145
- // authentication is necessary. (If PAM hasn't already done the
146
- // necessary screening for you, you have bigger problems than their
147
- // access to FeedWordPress...)
148
- $uri = (isset($_SERVER['argv'][1]) ? $_SERVER['argv'][1] : RPC_MAGIC.$rpc_secret);
149
- endif;
150
-
151
- $feedwordpress =& new FeedWordPress;
152
-
153
- if ($update_feeds_display=='text/html' or $update_feeds_verbose) :
154
- add_action('feedwordpress_check_feed', 'update_feeds_mention');
155
- endif;
156
-
157
- if ($update_feeds_display=='text/html') : // HTTP GET or HTTP POST: add some web niceties
158
-
159
- echo "<form action=\"\" method=\"POST\">\n";
160
- echo "<select name=\"uri\">\n";
161
- echo "<option value=\"".RPC_MAGIC.$rpc_secret."\">All feeds</option>\n";
162
- foreach ($feedwordpress->feeds as $feed) :
163
- echo "<option value=\"{$feed['link/uri']}\"";
164
- if ($feed['link/uri']==$_REQUEST['uri']) : echo ' selected="selected"'; endif;
165
- echo ">{$feed['link/name']}</option>\n";
166
- endforeach;
167
- echo "</select> ";
168
- echo "<input type=\"submit\" name=\"update\" value=\"Update\" />\n";
169
- echo "</form>\n";
170
- endif;
171
-
172
- if ($update_feeds_invoke != 'get') : // Only do things with side-effects for HTTP POST or command line
173
- if ($update_feeds_display == 'text/html') : echo "<ul>\n"; endif;
174
- $delta = @$feedwordpress->update($uri);
175
- if ($update_feeds_display == 'text/html') : echo "</ul>\n"; endif;
176
-
177
- if (is_null($delta)) :
178
- if ($update_feeds_invoke == 'cmd') :
179
- $stderr = fopen('php://stderr', 'w');
180
- fputs($stderr, "update-feeds (".date('Y-m-d H:i:s')."): ERROR: I don't syndicate <$uri>\n");
181
- elseif ($update_feeds_display == 'text/plain') :
182
- echo "update-feeds (".date('Y-m-d H:i:s')."): ERROR: I don't syndicate <$uri>\n";
183
- else :
184
- echo "<p><strong>Error:</strong> I don't syndicate <a href=\"$uri\">$uri</a></p>\n";
185
- endif;
186
- elseif ($update_feeds_display=='text/html' or $update_feeds_verbose) :
187
- $mesg = array();
188
- if (isset($delta['new'])) : $mesg[] = ' '.$delta['new'].' new posts were syndicated'; endif;
189
- if (isset($delta['updated'])) : $mesg[] = ' '.$delta['updated'].' existing posts were updated'; endif;
190
- if ($update_feeds_display=='text/html') : echo "<p>"; endif;
191
- echo "Update complete.".implode(' and', $mesg);
192
- if ($update_feeds_display=='text/html') : echo "</p>"; endif;
193
- echo "\n"; flush();
194
- endif;
195
- endif;
196
-
197
- if ($update_feeds_display=='text/html') : // HTTP GET or HTTP POST: close off web niceties
198
- echo <<<EOHTML
199
-
200
- <p><a href="../wp-admin">&larr; Return to WordPress Dashboard</a></p>
201
- </body>
202
- </html>
203
- EOHTML;
204
- endif;
205
- ?>