FeedWordPress - Version 0.981

Version Description

Download this release

Release Info

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

Code changes from version 0.98 to 0.981

ChangeLog.text CHANGED
@@ -1,6 +1,52 @@
1
  FeedWordPress Change Log
2
  ========================
3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
  Changes from 0.97 to 0.98
5
  --------------------------
6
 
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.
10
+
11
+ * WORDPRESS 2.1 COMPATIBILITY: FeedWordPress is now compatible with
12
+ WordPress 2.1, as well as retaining its existing support for WordPress
13
+ 2.0 and 1.5. Incompatibilities that resulted in database warnings, fatal
14
+ errors, and which prevented FeedWordPress from syndicating new posts,
15
+ have been eliminated.
16
+
17
+ * RSS-FUNCTIONS.PHP RENAMED TO RSS.PHP: if you use the upgraded MagpieRSS
18
+ replacement that's included with FeedWordPress, be sure to note that
19
+ there are now *two* files to upload from the `OPTIONAL/wp-includes`
20
+ subdirectory in order to carry out the upgrade: rss-functions.php and
21
+ rss.php. **It is necessary to upload both files**, due to a change in
22
+ the file naming scheme in WordPress 2.1, and it is necessary to do so
23
+ whether you are using WordPress 2.1 or not. If you only upload the
24
+ `rss-functions.php` file as in previous installations you will not have
25
+ a working copy of MagpieRSS; the rss.php file contains the actual code.
26
+
27
+ * DATE BUG AFFECTING SOME PHP INSTALLATIONS RESOLVED: due to a subtle bug
28
+ in parse_w3cdtf(), some installations of PHP encountered problems with
29
+ FeedWordPress's attempt to date posts, which would cause some new posts
30
+ on Atom feeds to be dated as if they had apppeared in 1969 or 1970
31
+ (thus, effectively, never appearing on front apge at all). This bug in
32
+ the date handling should now be fixed.
33
+
34
+ * PHP <?=...?> SHORT FORM ELIMINATED: some installations of PHP do not
35
+ allow the <?=...?> short form for printing PHP values, which was used
36
+ extensively in the FeedWordPress interface code. Since this could cause
37
+ fatal errors for users with the wrong installation of PHP, the short
38
+ form has been replaced with full PHP echo statements, and is no longer
39
+ used in FeedWordPress.
40
+
41
+ * BETTER USER INTERFACE INTEGRATION WITH WORDPRESS 2.x: Some minor changes
42
+ have been made to help the FeedWordPress interface pages blend in better
43
+ with the user interface when running under WordPress 2.x.
44
+
45
+ * GLOBAL CATEGORIES BUG RESOLVED: a bug that prevented some users from
46
+ setting one or more categories to apply to syndicated posts from all
47
+ feeds (using the checkbox interface under Options --> Syndication) has
48
+ been resolved.
49
+
50
  Changes from 0.97 to 0.98
51
  --------------------------
52
 
OPTIONAL/wp-includes/rss-functions.php CHANGED
@@ -1,1635 +1,4 @@
1
  <?php
2
- /* Project: MagpieRSS: a simple RSS integration tool
3
- * File: A compiled file for RSS syndication
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:
11
- *
12
- * This is a drop-in replacement for the `rss-functions.php` provided with the
13
- * WordPress 1.5 distribution, which upgrades the version of MagpieRSS from 0.51
14
- * to 0.8a. The update improves handling of character encoding, supports
15
- * multiple categories for posts (using <dc:subject> or <category>), supports
16
- * Atom 1.0, and implements many other useful features. The file is derived from
17
- * a combination of (1) the WordPress development team's modifications to
18
- * MagpieRSS 0.51 and (2) the latest bleeding-edge updates to the "official"
19
- * MagpieRSS software, including Kellan's original work and some substantial
20
- * updates by Charles Johnson. All possible through the magic of the GPL. Yay
21
- * for free software!
22
- *
23
- * Differences from the main branch of MagpieRSS:
24
- *
25
- * 1. Everything in rss_parse.inc, rss_fetch.inc, rss_cache.inc, and
26
- * rss_utils.inc is included in one file.
27
- *
28
- * 2. MagpieRSS returns the WordPress version as the user agent, rather than
29
- * Magpie
30
- *
31
- * 3. class RSSCache is a modified version by WordPress developers, which
32
- * caches feeds in the WordPress database (in the options table), rather
33
- * than writing external files directly.
34
- *
35
- * 4. There are two WordPress-specific functions, get_rss() and wp_rss()
36
- *
37
- * Differences from the version of MagpieRSS packaged with WordPress:
38
- *
39
- * 1. Support for translation between multiple character encodings. Under
40
- * PHP 5 this is very nicely handled by the XML parsing library. Under PHP
41
- * 4 we need to do a little bit of work ourselves, using either iconv or
42
- * mb_convert_encoding if it is not one of the (extremely limited) number
43
- * of character sets that PHP 4's XML module can handle natively.
44
- *
45
- * 2. Numerous bug fixes.
46
- *
47
- * 3. The parser class MagpieRSS has been substantially revised to better
48
- * support popular features such as enclosures and multiple categories,
49
- * and to support the new Atom 1.0 IETF standard. (Atom feeds are
50
- * normalized so as to make the data available using terminology from
51
- * either Atom 0.3 or Atom 1.0. Atom 0.3 backward-compatibility is provided
52
- * to allow existing software to easily begin accepting Atom 1.0 data; new
53
- * software SHOULD NOT depend on the 0.3 terminology, but rather use the
54
- * normalization as a convenient way to keep supporting 0.3 feeds while
55
- * they linger in the world.)
56
- *
57
- * The upgraded MagpieRSS can also now handle some content constructs that
58
- * had not been handled well by previous versions of Magpie (such as the
59
- * use of namespaced XHTML in <xhtml:body> or <xhtml:div> elements to
60
- * provide the full content of posts in RSS 2.0 feeds).
61
- *
62
- * Unlike previous versions of MagpieRSS, this version can parse multiple
63
- * instances of the same child element in item/entry and channel/feed
64
- * containers. This is done using simple counters next to the element
65
- * names: the first <category> element on an RSS item, for example, can be
66
- * found in $item['category'] (thus preserving backward compatibility); the
67
- * second in $item['category#2'], the third in $item['category#3'], and so
68
- * on. The number of categories applied to the item can be found in
69
- * $item['category#']
70
- *
71
- * Also unlike previous versions of MagpieRSS, this version allows you to
72
- * access the values of elements' attributes as well as the content they
73
- * contain. This can be done using a simple syntax inspired by XPath: to
74
- * access the type attribute of an RSS 2.0 enclosure, for example, you
75
- * need only access `$item['enclosure@type']`. A comma-separated list of
76
- * attributes for the enclosure element is stored in `$item['enclosure@']`.
77
- * (This syntax interacts easily with the syntax for multiple categories;
78
- * for example, the value of the `scheme` attribute for the fourth category
79
- * element on a particular item is stored in `$item['category#4@scheme']`.)
80
- *
81
- * Note also that this implementation IS NOT backward-compatible with the
82
- * kludges that were used to hack in support for multiple categories and
83
- * for enclosures in upgraded versions of MagpieRSS distributed with
84
- * previous versions of FeedWordPress. If your hacks or filter plugins
85
- * depended on the old way of doing things... well, I warned you that they
86
- * might not be permanent. Sorry!
87
- */
88
-
89
- define('RSS', 'RSS');
90
- define('ATOM', 'Atom');
91
-
92
- ################################################################################
93
- ## WordPress: make some settings WordPress-appropriate #########################
94
- ################################################################################
95
-
96
- define('MAGPIE_USER_AGENT', 'WordPress/' . $wp_version . '(+http://www.wordpress.org)');
97
-
98
- $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
- /**
106
- * Hybrid parser, and object, takes RSS as a string and returns a simple object.
107
- *
108
- * see: rss_fetch.inc for a simpler interface with integrated caching support
109
- *
110
- */
111
- class MagpieRSS {
112
- var $parser;
113
-
114
- var $current_item = array(); // item currently being parsed
115
- var $items = array(); // collection of parsed items
116
- var $channel = array(); // hash of channel fields
117
- var $textinput = array();
118
- var $image = array();
119
- var $feed_type;
120
- var $feed_version;
121
- var $encoding = ''; // output encoding of parsed rss
122
-
123
- var $_source_encoding = ''; // only set if we have to parse xml prolog
124
-
125
- var $ERROR = "";
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
- *
153
- * @param string $source string containing the RSS to be parsed
154
- *
155
- * NOTE: Probably a good idea to leave the encoding options alone unless
156
- * you know what you're doing as PHP's character set support is
157
- * a little weird.
158
- *
159
- * NOTE: A lot of this is unnecessary but harmless with PHP5
160
- *
161
- *
162
- * @param string $output_encoding output the parsed RSS in this character
163
- * set defaults to ISO-8859-1 as this is PHP's
164
- * default.
165
- *
166
- * NOTE: might be changed to UTF-8 in future
167
- * versions.
168
- *
169
- * @param string $input_encoding the character set of the incoming RSS source.
170
- * Leave blank and Magpie will try to figure it
171
- * out.
172
- *
173
- *
174
- * @param bool $detect_encoding if false Magpie won't attempt to detect
175
- * source encoding. (caveat emptor)
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
- #
183
- if (!function_exists('xml_parser_create')) {
184
- $this->error( "Failed to load PHP's XML Extension. " .
185
- "http://www.php.net/manual/en/ref.xml.php",
186
- E_USER_ERROR );
187
- }
188
-
189
- list($parser, $source) = $this->create_parser($source,
190
- $output_encoding, $input_encoding, $detect_encoding);
191
-
192
-
193
- if (!is_resource($parser)) {
194
- $this->error( "Failed to create an instance of PHP's XML parser. " .
195
- "http://www.php.net/manual/en/ref.xml.php",
196
- E_USER_ERROR );
197
- }
198
-
199
-
200
- $this->parser = $parser;
201
-
202
- # pass in parser, and a reference to this object
203
- # setup handlers
204
- #
205
- xml_set_object( $this->parser, $this );
206
- xml_set_element_handler($this->parser,
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 ) {
214
- $errorcode = xml_get_error_code( $this->parser );
215
- if ( $errorcode != XML_ERROR_NONE ) {
216
- $xml_error = xml_error_string( $errorcode );
217
- $error_line = xml_get_current_line_number($this->parser);
218
- $error_col = xml_get_current_column_number($this->parser);
219
- $errormsg = "$xml_error at line $error_line, column $error_col";
220
-
221
- $this->error( $errormsg );
222
- }
223
- }
224
-
225
- xml_parser_free( $this->parser );
226
-
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;
294
- if ( isset($attrs['rdf:about']) ) {
295
- $this->current_item['about'] = $attrs['rdf:about'];
296
- }
297
- }
298
-
299
- // if we're in the default namespace of an RSS feed,
300
- // record textinput or image fields
301
- elseif (
302
- $this->feed_type == RSS and
303
- $this->current_namespace == '' and
304
- $el == 'textinput' )
305
- {
306
- $this->intextinput = true;
307
- }
308
-
309
- elseif (
310
- $this->feed_type == RSS and
311
- $this->current_namespace == '' and
312
- $el == 'image' )
313
- {
314
- $this->inimage = true;
315
- }
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
644
- // each of multiple elements is normalized properly. If you need to mess
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
765
- $this->normalize_element($this->channel, 'description', $this->channel, 'tagline'); // Atom 0.3
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
-
804
-
805
- function is_rss () {
806
- if ( $this->feed_type == RSS ) {
807
- return $this->feed_version;
808
- }
809
- else {
810
- return false;
811
- }
812
- }
813
-
814
- function is_atom() {
815
- if ( $this->feed_type == ATOM ) {
816
- return $this->feed_version;
817
- }
818
- else {
819
- return false;
820
- }
821
- }
822
-
823
- /**
824
- * return XML parser, and possibly re-encoded source
825
- *
826
- */
827
- function create_parser($source, $out_enc, $in_enc, $detect) {
828
- if ( substr(phpversion(),0,1) == 5) {
829
- $parser = $this->php5_create_parser($in_enc, $detect);
830
- }
831
- else {
832
- list($parser, $source) = $this->php4_create_parser($source, $in_enc, $detect);
833
- }
834
- if ($out_enc) {
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
-
842
- /**
843
- * Instantiate an XML parser under PHP5
844
- *
845
- * PHP5 will do a fine job of detecting input encoding
846
- * if passed an empty string as the encoding.
847
- *
848
- * All hail libxml2!
849
- *
850
- */
851
- function php5_create_parser($in_enc, $detect) {
852
- // by default php5 does a fine job of detecting input encodings
853
- if(!$detect && $in_enc) {
854
- return xml_parser_create($in_enc);
855
- }
856
- else {
857
- return xml_parser_create('');
858
- }
859
- }
860
-
861
- /**
862
- * Instaniate an XML parser under PHP4
863
- *
864
- * Unfortunately PHP4's support for character encodings
865
- * and especially XML and character encodings sucks. As
866
- * long as the documents you parse only contain characters
867
- * from the ISO-8859-1 character set (a superset of ASCII,
868
- * and a subset of UTF-8) you're fine. However once you
869
- * step out of that comfy little world things get mad, bad,
870
- * and dangerous to know.
871
- *
872
- * The following code is based on SJM's work with FoF
873
- * @see http://minutillo.com/steve/weblog/2004/6/17/php-xml-and-character-encodings-a-tale-of-sadness-rage-and-data-loss
874
- *
875
- */
876
- function php4_create_parser($source, $in_enc, $detect) {
877
- if ( !$detect ) {
878
- return array(xml_parser_create($in_enc), $source);
879
- }
880
-
881
- if (!$in_enc) {
882
- if (preg_match('/<?xml.*encoding=[\'"](.*?)[\'"].*?>/m', $source, $m)) {
883
- $in_enc = strtoupper($m[1]);
884
- $this->source_encoding = $in_enc;
885
- }
886
- else {
887
- $in_enc = 'UTF-8';
888
- }
889
- }
890
-
891
- if ($this->known_encoding($in_enc)) {
892
- return array(xml_parser_create($in_enc), $source);
893
- }
894
-
895
- // the dectected encoding is not one of the simple encodings PHP knows
896
-
897
- // attempt to use the iconv extension to
898
- // cast the XML to a known encoding
899
- // @see http://php.net/iconv
900
-
901
- if (function_exists('iconv')) {
902
- $encoded_source = iconv($in_enc,'UTF-8', $source);
903
- if ($encoded_source) {
904
- return array(xml_parser_create('UTF-8'), $encoded_source);
905
- }
906
- }
907
-
908
- // iconv didn't work, try mb_convert_encoding
909
- // @see http://php.net/mbstring
910
- if(function_exists('mb_convert_encoding')) {
911
- $encoded_source = mb_convert_encoding($source, 'UTF-8', $in_enc );
912
- if ($encoded_source) {
913
- return array(xml_parser_create('UTF-8'), $encoded_source);
914
- }
915
- }
916
-
917
- // else
918
- $this->error("Feed is in an unsupported character encoding. ($in_enc) " .
919
- "You may see strange artifacts, and mangled characters.",
920
- E_USER_NOTICE);
921
-
922
- return array(xml_parser_create(), $source);
923
- }
924
-
925
- function known_encoding($enc) {
926
- $enc = strtoupper($enc);
927
- if ( in_array($enc, $this->_KNOWN_ENCODINGS) ) {
928
- return $enc;
929
- }
930
- else {
931
- return false;
932
- }
933
- }
934
-
935
- function error ($errormsg, $lvl=E_USER_WARNING) {
936
- // append PHP's error message if track_errors enabled
937
- if ( isset($php_errormsg) ) {
938
- $errormsg .= " ($php_errormsg)";
939
- }
940
- if ( MAGPIE_DEBUG ) {
941
- trigger_error( $errormsg, $lvl);
942
- }
943
- else {
944
- error_log( $errormsg, 0);
945
- }
946
-
947
- $notices = E_USER_NOTICE|E_NOTICE;
948
- if ( $lvl&$notices ) {
949
- $this->WARNING = $errormsg;
950
- } else {
951
- $this->ERROR = $errormsg;
952
- }
953
- }
954
-
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
-
985
- ################################################################################
986
- ## WordPress: Load in Snoopy from wp-includes ##################################
987
- ################################################################################
988
-
989
- require_once( dirname(__FILE__) . '/class-snoopy.php');
990
-
991
- ################################################################################
992
- ## rss_fetch.inc: from MagpieRSS 0.8a ##########################################
993
- ################################################################################
994
-
995
- /*=======================================================================*\
996
- Function: fetch_rss:
997
- Purpose: return RSS object for the give url
998
- maintain the cache
999
- Input: url of RSS file
1000
- Output: parsed RSS object (see rss_parse.inc)
1001
-
1002
- NOTES ON CACHEING:
1003
- If caching is on (MAGPIE_CACHE_ON) fetch_rss will first check the cache.
1004
-
1005
- NOTES ON RETRIEVING REMOTE FILES:
1006
- If conditional gets are on (MAGPIE_CONDITIONAL_GET_ON) fetch_rss will
1007
- return a cached object, and touch the cache object upon recieving a
1008
- 304.
1009
-
1010
- NOTES ON FAILED REQUESTS:
1011
- If there is an HTTP error while fetching an RSS object, the cached
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
-
1019
- function fetch_rss ($url) {
1020
- // initialize constants
1021
- init();
1022
-
1023
- if ( !isset($url) ) {
1024
- error("fetch_rss called without a url");
1025
- return false;
1026
- }
1027
-
1028
- // if cache is disabled
1029
- if ( !MAGPIE_CACHE_ON ) {
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");
1037
- return false;
1038
- }
1039
- }
1040
- // else cache is ON
1041
- else {
1042
- // Flow
1043
- // 1. check cache
1044
- // 2. if there is a hit, make sure its fresh
1045
- // 3. if cached obj fails freshness check, fetch remote
1046
- // 4. if remote fails, return stale object, or error
1047
-
1048
- $cache = new RSSCache( MAGPIE_CACHE_DIR, MAGPIE_CACHE_AGE );
1049
-
1050
- if (MAGPIE_DEBUG and $cache->ERROR) {
1051
- debug($cache->ERROR, E_USER_WARNING);
1052
- }
1053
-
1054
-
1055
- $cache_status = 0; // response of check_cache
1056
- $request_headers = array(); // HTTP headers to send with fetch
1057
- $rss = 0; // parsed RSS object
1058
- $errormsg = 0; // errors, if any
1059
-
1060
- // store parsed XML by desired output encoding
1061
- // as character munging happens at parse time
1062
- $cache_key = $url . MAGPIE_OUTPUT_ENCODING;
1063
-
1064
- if (!$cache->ERROR) {
1065
- // return cache HIT, MISS, or STALE
1066
- $cache_status = $cache->check_cache( $cache_key);
1067
- }
1068
-
1069
- // if object cached, and cache is fresh, return cached obj
1070
- if ( $cache_status == 'HIT' ) {
1071
- $rss = $cache->get( $cache_key );
1072
- if ( isset($rss) and $rss ) {
1073
- // should be cache age
1074
- $rss->from_cache = 1;
1075
- if ( MAGPIE_DEBUG > 1) {
1076
- debug("MagpieRSS: Cache HIT", E_USER_NOTICE);
1077
- }
1078
- return $rss;
1079
- }
1080
- }
1081
-
1082
- // else attempt a conditional get
1083
-
1084
- // setup headers
1085
- if ( $cache_status == 'STALE' ) {
1086
- $rss = $cache->get( $cache_key );
1087
- if ( $rss and $rss->etag and $rss->last_modified ) {
1088
- $request_headers['If-None-Match'] = $rss->etag;
1089
- $request_headers['If-Last-Modified'] = $rss->last_modified;
1090
- }
1091
- }
1092
-
1093
- $resp = _fetch_remote_file( $url, $request_headers );
1094
-
1095
- if (isset($resp) and $resp) {
1096
- if ($resp->status == '304' ) {
1097
- // we have the most current copy
1098
- if ( MAGPIE_DEBUG > 1) {
1099
- debug("Got 304 for $url");
1100
- }
1101
- // reset cache on 304 (at minutillo insistent prodding)
1102
- $cache->set($cache_key, $rss);
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");
1110
- }
1111
- // add object to cache
1112
- $cache->set( $cache_key, $rss );
1113
- return $rss;
1114
- }
1115
- }
1116
- else {
1117
- $errormsg = "Failed to fetch $url ";
1118
- if ( $resp->status == '-100' ) {
1119
- $errormsg .= "(Request timed out after " . MAGPIE_FETCH_TIME_OUT . " seconds)";
1120
- }
1121
- elseif ( $resp->error ) {
1122
- # compensate for Snoopy's annoying habbit to tacking
1123
- # on '\n'
1124
- $http_error = substr($resp->error, 0, -2);
1125
- $errormsg .= "(HTTP Error: $http_error)";
1126
- }
1127
- else {
1128
- $errormsg .= "(HTTP Response: " . $resp->response_code .')';
1129
- }
1130
- }
1131
- }
1132
- else {
1133
- $errormsg = "Unable to retrieve RSS file for unknown reasons.";
1134
- }
1135
-
1136
- // else fetch failed
1137
-
1138
- // attempt to return cached object
1139
- if ($rss) {
1140
- if ( MAGPIE_DEBUG ) {
1141
- debug("Returning STALE object for $url");
1142
- }
1143
- return $rss;
1144
- }
1145
-
1146
- // else we totally failed
1147
- error( $errormsg );
1148
-
1149
- return false;
1150
-
1151
- } // end if ( !MAGPIE_CACHE_ON ) {
1152
- } // end fetch_rss()
1153
-
1154
- /*=======================================================================*\
1155
- Function: error
1156
- Purpose: set MAGPIE_ERROR, and trigger error
1157
- \*=======================================================================*/
1158
-
1159
- function error ($errormsg, $lvl=E_USER_WARNING) {
1160
- global $MAGPIE_ERROR;
1161
-
1162
- // append PHP's error message if track_errors enabled
1163
- if ( isset($php_errormsg) ) {
1164
- $errormsg .= " ($php_errormsg)";
1165
- }
1166
- if ( $errormsg ) {
1167
- $errormsg = "MagpieRSS: $errormsg";
1168
- $MAGPIE_ERROR = $errormsg;
1169
- trigger_error( $errormsg, $lvl);
1170
- }
1171
- }
1172
-
1173
- function debug ($debugmsg, $lvl=E_USER_NOTICE) {
1174
- trigger_error("MagpieRSS [debug] $debugmsg", $lvl);
1175
- }
1176
-
1177
- /*=======================================================================*\
1178
- Function: magpie_error
1179
- Purpose: accessor for the magpie error variable
1180
- \*=======================================================================*/
1181
- function magpie_error ($errormsg="") {
1182
- global $MAGPIE_ERROR;
1183
-
1184
- if ( isset($errormsg) and $errormsg ) {
1185
- $MAGPIE_ERROR = $errormsg;
1186
- }
1187
-
1188
- return $MAGPIE_ERROR;
1189
- }
1190
-
1191
- /*=======================================================================*\
1192
- Function: _fetch_remote_file
1193
- Purpose: retrieve an arbitrary remote file
1194
- Input: url of the remote file
1195
- headers to send along with the request (optional)
1196
- Output: an HTTP response object (see Snoopy.class.inc)
1197
- \*=======================================================================*/
1198
- function _fetch_remote_file ($url, $headers = "" ) {
1199
- // Snoopy is an HTTP client in PHP
1200
- $client = new Snoopy();
1201
- $client->agent = MAGPIE_USER_AGENT;
1202
- $client->read_timeout = MAGPIE_FETCH_TIME_OUT;
1203
- $client->use_gzip = MAGPIE_USE_GZIP;
1204
- if (is_array($headers) ) {
1205
- $client->rawheaders = $headers;
1206
- }
1207
-
1208
- @$client->fetch($url);
1209
- return $client;
1210
-
1211
- }
1212
-
1213
- /*=======================================================================*\
1214
- Function: _response_to_rss
1215
- Purpose: parse an HTTP response object into an RSS object
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"
1228
- if (strpos($h, ": ")) {
1229
- list($field, $val) = explode(": ", $h, 2);
1230
- }
1231
- else {
1232
- $field = $h;
1233
- $val = "";
1234
- }
1235
-
1236
- if ( $field == 'ETag' ) {
1237
- $rss->etag = $val;
1238
- }
1239
-
1240
- if ( $field == 'Last-Modified' ) {
1241
- $rss->last_modified = $val;
1242
- }
1243
- }
1244
-
1245
- return $rss;
1246
- } // else construct error message
1247
- else {
1248
- $errormsg = "Failed to parse RSS file.";
1249
-
1250
- if ($rss) {
1251
- $errormsg .= " (" . $rss->ERROR . ")";
1252
- }
1253
- error($errormsg);
1254
-
1255
- return false;
1256
- } // end if ($rss and !$rss->error)
1257
- }
1258
-
1259
- /*=======================================================================*\
1260
- Function: init
1261
- Purpose: setup constants with default values
1262
- check for user overrides
1263
- \*=======================================================================*/
1264
- function init () {
1265
- if ( defined('MAGPIE_INITALIZED') ) {
1266
- return;
1267
- }
1268
- else {
1269
- define('MAGPIE_INITALIZED', true);
1270
- }
1271
-
1272
- if ( !defined('MAGPIE_CACHE_ON') ) {
1273
- define('MAGPIE_CACHE_ON', true);
1274
- }
1275
-
1276
- if ( !defined('MAGPIE_CACHE_DIR') ) {
1277
- define('MAGPIE_CACHE_DIR', './cache');
1278
- }
1279
-
1280
- if ( !defined('MAGPIE_CACHE_AGE') ) {
1281
- define('MAGPIE_CACHE_AGE', 60*60); // one hour
1282
- }
1283
-
1284
- if ( !defined('MAGPIE_CACHE_FRESH_ONLY') ) {
1285
- define('MAGPIE_CACHE_FRESH_ONLY', false);
1286
- }
1287
-
1288
- if ( !defined('MAGPIE_OUTPUT_ENCODING') ) {
1289
- define('MAGPIE_OUTPUT_ENCODING', 'ISO-8859-1');
1290
- }
1291
-
1292
- if ( !defined('MAGPIE_INPUT_ENCODING') ) {
1293
- define('MAGPIE_INPUT_ENCODING', null);
1294
- }
1295
-
1296
- if ( !defined('MAGPIE_DETECT_ENCODING') ) {
1297
- define('MAGPIE_DETECT_ENCODING', true);
1298
- }
1299
-
1300
- if ( !defined('MAGPIE_DEBUG') ) {
1301
- define('MAGPIE_DEBUG', 0);
1302
- }
1303
-
1304
- if ( !defined('MAGPIE_USER_AGENT') ) {
1305
- $ua = 'MagpieRSS/'. MAGPIE_VERSION . ' (+http://magpierss.sf.net';
1306
-
1307
- if ( MAGPIE_CACHE_ON ) {
1308
- $ua = $ua . ')';
1309
- }
1310
- else {
1311
- $ua = $ua . '; No cache)';
1312
- }
1313
-
1314
- define('MAGPIE_USER_AGENT', $ua);
1315
- }
1316
-
1317
- if ( !defined('MAGPIE_FETCH_TIME_OUT') ) {
1318
- define('MAGPIE_FETCH_TIME_OUT', 5); // 5 second timeout
1319
- }
1320
-
1321
- // use gzip encoding to fetch rss files if supported?
1322
- if ( !defined('MAGPIE_USE_GZIP') ) {
1323
- define('MAGPIE_USE_GZIP', true);
1324
- }
1325
- }
1326
-
1327
- // NOTE: the following code should really be in Snoopy, or at least
1328
- // somewhere other then rss_fetch!
1329
-
1330
- /*=======================================================================*\
1331
- HTTP STATUS CODE PREDICATES
1332
- These functions attempt to classify an HTTP status code
1333
- based on RFC 2616 and RFC 2518.
1334
-
1335
- All of them take an HTTP status code as input, and return true or false
1336
-
1337
- All this code is adapted from LWP's HTTP::Status.
1338
- \*=======================================================================*/
1339
-
1340
-
1341
- /*=======================================================================*\
1342
- Function: is_info
1343
- Purpose: return true if Informational status code
1344
- \*=======================================================================*/
1345
- function is_info ($sc) {
1346
- return $sc >= 100 && $sc < 200;
1347
- }
1348
-
1349
- /*=======================================================================*\
1350
- Function: is_success
1351
- Purpose: return true if Successful status code
1352
- \*=======================================================================*/
1353
- function is_success ($sc) {
1354
- return $sc >= 200 && $sc < 300;
1355
- }
1356
-
1357
- /*=======================================================================*\
1358
- Function: is_redirect
1359
- Purpose: return true if Redirection status code
1360
- \*=======================================================================*/
1361
- function is_redirect ($sc) {
1362
- return $sc >= 300 && $sc < 400;
1363
- }
1364
-
1365
- /*=======================================================================*\
1366
- Function: is_error
1367
- Purpose: return true if Error status code
1368
- \*=======================================================================*/
1369
- function is_error ($sc) {
1370
- return $sc >= 400 && $sc < 600;
1371
- }
1372
-
1373
- /*=======================================================================*\
1374
- Function: is_client_error
1375
- Purpose: return true if Error status code, and its a client error
1376
- \*=======================================================================*/
1377
- function is_client_error ($sc) {
1378
- return $sc >= 400 && $sc < 500;
1379
- }
1380
-
1381
- /*=======================================================================*\
1382
- Function: is_client_error
1383
- Purpose: return true if Error status code, and its a server error
1384
- \*=======================================================================*/
1385
- function is_server_error ($sc) {
1386
- return $sc >= 500 && $sc < 600;
1387
- }
1388
-
1389
- ################################################################################
1390
- ## rss_cache.inc: from WordPress 1.5 ###########################################
1391
- ################################################################################
1392
-
1393
- class RSSCache {
1394
- var $BASE_CACHE = 'wp-content/cache'; // where the cache files are stored
1395
- var $MAX_AGE = 43200; // when are files stale, default twelve hours
1396
- var $ERROR = ''; // accumulate error messages
1397
-
1398
- function RSSCache ($base='', $age='') {
1399
- if ( $base ) {
1400
- $this->BASE_CACHE = $base;
1401
- }
1402
- if ( $age ) {
1403
- $this->MAX_AGE = $age;
1404
- }
1405
-
1406
- }
1407
-
1408
- /*=======================================================================*\
1409
- Function: set
1410
- Purpose: add an item to the cache, keyed on url
1411
- Input: url from wich the rss file was fetched
1412
- Output: true on sucess
1413
- \*=======================================================================*/
1414
- function set ($url, $rss) {
1415
- global $wpdb;
1416
- $cache_option = 'rss_' . $this->file_name( $url );
1417
- $cache_timestamp = 'rss_' . $this->file_name( $url ) . '_ts';
1418
-
1419
- if ( !$wpdb->get_var("SELECT option_name FROM $wpdb->options WHERE option_name = '$cache_option'") )
1420
- add_option($cache_option, '', '', 'no');
1421
- if ( !$wpdb->get_var("SELECT option_name FROM $wpdb->options WHERE option_name = '$cache_timestamp'") )
1422
- add_option($cache_timestamp, '', '', 'no');
1423
-
1424
- update_option($cache_option, $rss);
1425
- update_option($cache_timestamp, time() );
1426
-
1427
- return $cache_option;
1428
- }
1429
-
1430
- /*=======================================================================*\
1431
- Function: get
1432
- Purpose: fetch an item from the cache
1433
- Input: url from wich the rss file was fetched
1434
- Output: cached object on HIT, false on MISS
1435
- \*=======================================================================*/
1436
- function get ($url) {
1437
- $this->ERROR = "";
1438
- $cache_option = 'rss_' . $this->file_name( $url );
1439
-
1440
- if ( ! get_option( $cache_option ) ) {
1441
- $this->debug(
1442
- "Cache doesn't contain: $url (cache option: $cache_option)"
1443
- );
1444
- return 0;
1445
- }
1446
-
1447
- $rss = get_option( $cache_option );
1448
-
1449
- return $rss;
1450
- }
1451
-
1452
- /*=======================================================================*\
1453
- Function: check_cache
1454
- Purpose: check a url for membership in the cache
1455
- and whether the object is older then MAX_AGE (ie. STALE)
1456
- Input: url from wich the rss file was fetched
1457
- Output: cached object on HIT, false on MISS
1458
- \*=======================================================================*/
1459
- function check_cache ( $url ) {
1460
- $this->ERROR = "";
1461
- $cache_option = $this->file_name( $url );
1462
- $cache_timestamp = 'rss_' . $this->file_name( $url ) . '_ts';
1463
-
1464
- if ( $mtime = get_option($cache_timestamp) ) {
1465
- // find how long ago the file was added to the cache
1466
- // and whether that is longer then MAX_AGE
1467
- $age = time() - $mtime;
1468
- if ( $this->MAX_AGE > $age ) {
1469
- // object exists and is current
1470
- return 'HIT';
1471
- }
1472
- else {
1473
- // object exists but is old
1474
- return 'STALE';
1475
- }
1476
- }
1477
- else {
1478
- // object does not exist
1479
- return 'MISS';
1480
- }
1481
- }
1482
-
1483
- /*=======================================================================*\
1484
- Function: serialize
1485
- \*=======================================================================*/
1486
- function serialize ( $rss ) {
1487
- return serialize( $rss );
1488
- }
1489
-
1490
- /*=======================================================================*\
1491
- Function: unserialize
1492
- \*=======================================================================*/
1493
- function unserialize ( $data ) {
1494
- return unserialize( $data );
1495
- }
1496
-
1497
- /*=======================================================================*\
1498
- Function: file_name
1499
- Purpose: map url to location in cache
1500
- Input: url from wich the rss file was fetched
1501
- Output: a file name
1502
- \*=======================================================================*/
1503
- function file_name ($url) {
1504
- return md5( $url );
1505
- }
1506
-
1507
- /*=======================================================================*\
1508
- Function: error
1509
- Purpose: register error
1510
- \*=======================================================================*/
1511
- function error ($errormsg, $lvl=E_USER_WARNING) {
1512
- // append PHP's error message if track_errors enabled
1513
- if ( isset($php_errormsg) ) {
1514
- $errormsg .= " ($php_errormsg)";
1515
- }
1516
- $this->ERROR = $errormsg;
1517
- if ( MAGPIE_DEBUG ) {
1518
- trigger_error( $errormsg, $lvl);
1519
- }
1520
- else {
1521
- error_log( $errormsg, 0);
1522
- }
1523
- }
1524
- function debug ($debugmsg, $lvl=E_USER_NOTICE) {
1525
- if ( MAGPIE_DEBUG ) {
1526
- $this->error("MagpieRSS [debug] $debugmsg", $lvl);
1527
- }
1528
- }
1529
- }
1530
-
1531
- ################################################################################
1532
- ## rss_utils.inc: from MagpieRSS 0.8a ##########################################
1533
- ################################################################################
1534
-
1535
- /*======================================================================*\
1536
- Function: parse_w3cdtf
1537
- Purpose: parse a W3CDTF date into unix epoch
1538
-
1539
- NOTE: http://www.w3.org/TR/NOTE-datetime
1540
- \*======================================================================*/
1541
-
1542
- function parse_w3cdtf ( $date_str ) {
1543
-
1544
- # regex to match wc3dtf
1545
- $pat = "/^\s*(\d{4})(-(\d{2})(-(\d{2})(T(\d{2}):(\d{2})(:(\d{2})(\.\d+)?)?(?:([-+])(\d{2}):?(\d{2})|(Z))?)?)?)?\s*\$/";
1546
-
1547
- if ( preg_match( $pat, $date_str, $match ) ) {
1548
- list( $year, $month, $day, $hours, $minutes, $seconds) =
1549
- array( $match[1], $match[3], $match[5], $match[7], $match[8], $match[9]);
1550
-
1551
- # W3C dates can omit the time, the day of the month, or even the month.
1552
- # Fill in any blanks using information from the present moment. --CWJ
1553
- $default['hr'] = (int) gmdate('H');
1554
- $default['day'] = (int) gmdate('d');
1555
- $default['month'] = (int) gmdate('m');
1556
-
1557
- if (is_null($hours)) : $hours = $default['hr']; $minutes = 0; $seconds = 0; endif;
1558
- if (is_null($day)) : $day = $default['day']; endif;
1559
- if (is_null($month)) : $month = $default['month']; endif;
1560
-
1561
- # calc epoch for current date assuming GMT
1562
- $epoch = gmmktime( $hours, $minutes, $seconds, $month, $day, $year);
1563
-
1564
- $offset = 0;
1565
- if ( $match[14] == 'Z' ) {
1566
- # zulu time, aka GMT
1567
- }
1568
- else {
1569
- list( $tz_mod, $tz_hour, $tz_min ) =
1570
- array( $match[12], $match[13], $match[14]);
1571
-
1572
- # zero out the variables
1573
- if ( ! $tz_hour ) { $tz_hour = 0; }
1574
- if ( ! $tz_min ) { $tz_min = 0; }
1575
-
1576
- $offset_secs = (($tz_hour*60)+$tz_min)*60;
1577
-
1578
- # is timezone ahead of GMT? then subtract offset
1579
- #
1580
- if ( $tz_mod == '+' ) {
1581
- $offset_secs = $offset_secs * -1;
1582
- }
1583
-
1584
- $offset = $offset_secs;
1585
- }
1586
- $epoch = $epoch + $offset;
1587
- return $epoch;
1588
- }
1589
- else {
1590
- return -1;
1591
- }
1592
- }
1593
-
1594
- ################################################################################
1595
- ## WordPress: wp_rss(), get_rss() ##############################################
1596
- ################################################################################
1597
-
1598
- function wp_rss ($url, $num) {
1599
- //ini_set("display_errors", false); uncomment to suppress php errors thrown if the feed is not returned.
1600
- $num_items = $num;
1601
- $rss = fetch_rss($url);
1602
- if ( $rss ) {
1603
- echo "<ul>";
1604
- $rss->items = array_slice($rss->items, 0, $num_items);
1605
- foreach ($rss->items as $item ) {
1606
- echo "<li>\n";
1607
- echo "<a href='$item[link]' title='$item[description]'>";
1608
- echo htmlentities($item['title']);
1609
- echo "</a><br />\n";
1610
- echo "</li>\n";
1611
- }
1612
- echo "</ul>";
1613
- }
1614
- else {
1615
- echo "an error has occured the feed is probably down, try again later.";
1616
- }
1617
- }
1618
-
1619
- function get_rss ($uri, $num = 5) { // Like get posts, but for RSS
1620
- $rss = fetch_rss($url);
1621
- if ( $rss ) {
1622
- $rss->items = array_slice($rss->items, 0, $num_items);
1623
- foreach ($rss->items as $item ) {
1624
- echo "<li>\n";
1625
- echo "<a href='$item[link]' title='$item[description]'>";
1626
- echo htmlentities($item['title']);
1627
- echo "</a><br />\n";
1628
- echo "</li>\n";
1629
- }
1630
- return $posts;
1631
- } else {
1632
- return false;
1633
- }
1634
- }
1635
- ?>
1
  <?php
2
+ // Deprecated. Use rss.php instead.
3
+ require_once (ABSPATH . WPINC . '/rss.php');
4
+ ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
OPTIONAL/wp-includes/rss.php ADDED
@@ -0,0 +1,1635 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /* Project: MagpieRSS: a simple RSS integration tool
3
+ * File: A compiled file for RSS syndication
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:
11
+ *
12
+ * This is a drop-in replacement for the `rss-functions.php` provided with the
13
+ * WordPress 1.5 distribution, which upgrades the version of MagpieRSS from 0.51
14
+ * to 0.8a. The update improves handling of character encoding, supports
15
+ * multiple categories for posts (using <dc:subject> or <category>), supports
16
+ * Atom 1.0, and implements many other useful features. The file is derived from
17
+ * a combination of (1) the WordPress development team's modifications to
18
+ * MagpieRSS 0.51 and (2) the latest bleeding-edge updates to the "official"
19
+ * MagpieRSS software, including Kellan's original work and some substantial
20
+ * updates by Charles Johnson. All possible through the magic of the GPL. Yay
21
+ * for free software!
22
+ *
23
+ * Differences from the main branch of MagpieRSS:
24
+ *
25
+ * 1. Everything in rss_parse.inc, rss_fetch.inc, rss_cache.inc, and
26
+ * rss_utils.inc is included in one file.
27
+ *
28
+ * 2. MagpieRSS returns the WordPress version as the user agent, rather than
29
+ * Magpie
30
+ *
31
+ * 3. class RSSCache is a modified version by WordPress developers, which
32
+ * caches feeds in the WordPress database (in the options table), rather
33
+ * than writing external files directly.
34
+ *
35
+ * 4. There are two WordPress-specific functions, get_rss() and wp_rss()
36
+ *
37
+ * Differences from the version of MagpieRSS packaged with WordPress:
38
+ *
39
+ * 1. Support for translation between multiple character encodings. Under
40
+ * PHP 5 this is very nicely handled by the XML parsing library. Under PHP
41
+ * 4 we need to do a little bit of work ourselves, using either iconv or
42
+ * mb_convert_encoding if it is not one of the (extremely limited) number
43
+ * of character sets that PHP 4's XML module can handle natively.
44
+ *
45
+ * 2. Numerous bug fixes.
46
+ *
47
+ * 3. The parser class MagpieRSS has been substantially revised to better
48
+ * support popular features such as enclosures and multiple categories,
49
+ * and to support the new Atom 1.0 IETF standard. (Atom feeds are
50
+ * normalized so as to make the data available using terminology from
51
+ * either Atom 0.3 or Atom 1.0. Atom 0.3 backward-compatibility is provided
52
+ * to allow existing software to easily begin accepting Atom 1.0 data; new
53
+ * software SHOULD NOT depend on the 0.3 terminology, but rather use the
54
+ * normalization as a convenient way to keep supporting 0.3 feeds while
55
+ * they linger in the world.)
56
+ *
57
+ * The upgraded MagpieRSS can also now handle some content constructs that
58
+ * had not been handled well by previous versions of Magpie (such as the
59
+ * use of namespaced XHTML in <xhtml:body> or <xhtml:div> elements to
60
+ * provide the full content of posts in RSS 2.0 feeds).
61
+ *
62
+ * Unlike previous versions of MagpieRSS, this version can parse multiple
63
+ * instances of the same child element in item/entry and channel/feed
64
+ * containers. This is done using simple counters next to the element
65
+ * names: the first <category> element on an RSS item, for example, can be
66
+ * found in $item['category'] (thus preserving backward compatibility); the
67
+ * second in $item['category#2'], the third in $item['category#3'], and so
68
+ * on. The number of categories applied to the item can be found in
69
+ * $item['category#']
70
+ *
71
+ * Also unlike previous versions of MagpieRSS, this version allows you to
72
+ * access the values of elements' attributes as well as the content they
73
+ * contain. This can be done using a simple syntax inspired by XPath: to
74
+ * access the type attribute of an RSS 2.0 enclosure, for example, you
75
+ * need only access `$item['enclosure@type']`. A comma-separated list of
76
+ * attributes for the enclosure element is stored in `$item['enclosure@']`.
77
+ * (This syntax interacts easily with the syntax for multiple categories;
78
+ * for example, the value of the `scheme` attribute for the fourth category
79
+ * element on a particular item is stored in `$item['category#4@scheme']`.)
80
+ *
81
+ * Note also that this implementation IS NOT backward-compatible with the
82
+ * kludges that were used to hack in support for multiple categories and
83
+ * for enclosures in upgraded versions of MagpieRSS distributed with
84
+ * previous versions of FeedWordPress. If your hacks or filter plugins
85
+ * depended on the old way of doing things... well, I warned you that they
86
+ * might not be permanent. Sorry!
87
+ */
88
+
89
+ define('RSS', 'RSS');
90
+ define('ATOM', 'Atom');
91
+
92
+ ################################################################################
93
+ ## WordPress: make some settings WordPress-appropriate #########################
94
+ ################################################################################
95
+
96
+ define('MAGPIE_USER_AGENT', 'WordPress/' . $wp_version . '(+http://www.wordpress.org)');
97
+
98
+ $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
+ /**
106
+ * Hybrid parser, and object, takes RSS as a string and returns a simple object.
107
+ *
108
+ * see: rss_fetch.inc for a simpler interface with integrated caching support
109
+ *
110
+ */
111
+ class MagpieRSS {
112
+ var $parser;
113
+
114
+ var $current_item = array(); // item currently being parsed
115
+ var $items = array(); // collection of parsed items
116
+ var $channel = array(); // hash of channel fields
117
+ var $textinput = array();
118
+ var $image = array();
119
+ var $feed_type;
120
+ var $feed_version;
121
+ var $encoding = ''; // output encoding of parsed rss
122
+
123
+ var $_source_encoding = ''; // only set if we have to parse xml prolog
124
+
125
+ var $ERROR = "";
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
+ *
153
+ * @param string $source string containing the RSS to be parsed
154
+ *
155
+ * NOTE: Probably a good idea to leave the encoding options alone unless
156
+ * you know what you're doing as PHP's character set support is
157
+ * a little weird.
158
+ *
159
+ * NOTE: A lot of this is unnecessary but harmless with PHP5
160
+ *
161
+ *
162
+ * @param string $output_encoding output the parsed RSS in this character
163
+ * set defaults to ISO-8859-1 as this is PHP's
164
+ * default.
165
+ *
166
+ * NOTE: might be changed to UTF-8 in future
167
+ * versions.
168
+ *
169
+ * @param string $input_encoding the character set of the incoming RSS source.
170
+ * Leave blank and Magpie will try to figure it
171
+ * out.
172
+ *
173
+ *
174
+ * @param bool $detect_encoding if false Magpie won't attempt to detect
175
+ * source encoding. (caveat emptor)
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
+ #
183
+ if (!function_exists('xml_parser_create')) {
184
+ $this->error( "Failed to load PHP's XML Extension. " .
185
+ "http://www.php.net/manual/en/ref.xml.php",
186
+ E_USER_ERROR );
187
+ }
188
+
189
+ list($parser, $source) = $this->create_parser($source,
190
+ $output_encoding, $input_encoding, $detect_encoding);
191
+
192
+
193
+ if (!is_resource($parser)) {
194
+ $this->error( "Failed to create an instance of PHP's XML parser. " .
195
+ "http://www.php.net/manual/en/ref.xml.php",
196
+ E_USER_ERROR );
197
+ }
198
+
199
+
200
+ $this->parser = $parser;
201
+
202
+ # pass in parser, and a reference to this object
203
+ # setup handlers
204
+ #
205
+ xml_set_object( $this->parser, $this );
206
+ xml_set_element_handler($this->parser,
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 ) {
214
+ $errorcode = xml_get_error_code( $this->parser );
215
+ if ( $errorcode != XML_ERROR_NONE ) {
216
+ $xml_error = xml_error_string( $errorcode );
217
+ $error_line = xml_get_current_line_number($this->parser);
218
+ $error_col = xml_get_current_column_number($this->parser);
219
+ $errormsg = "$xml_error at line $error_line, column $error_col";
220
+
221
+ $this->error( $errormsg );
222
+ }
223
+ }
224
+
225
+ xml_parser_free( $this->parser );
226
+
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;
294
+ if ( isset($attrs['rdf:about']) ) {
295
+ $this->current_item['about'] = $attrs['rdf:about'];
296
+ }
297
+ }
298
+
299
+ // if we're in the default namespace of an RSS feed,
300
+ // record textinput or image fields
301
+ elseif (
302
+ $this->feed_type == RSS and
303
+ $this->current_namespace == '' and
304
+ $el == 'textinput' )
305
+ {
306
+ $this->intextinput = true;
307
+ }
308
+
309
+ elseif (
310
+ $this->feed_type == RSS and
311
+ $this->current_namespace == '' and
312
+ $el == 'image' )
313
+ {
314
+ $this->inimage = true;
315
+ }
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
644
+ // each of multiple elements is normalized properly. If you need to mess
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
765
+ $this->normalize_element($this->channel, 'description', $this->channel, 'tagline'); // Atom 0.3
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
+
804
+
805
+ function is_rss () {
806
+ if ( $this->feed_type == RSS ) {
807
+ return $this->feed_version;
808
+ }
809
+ else {
810
+ return false;
811
+ }
812
+ }
813
+
814
+ function is_atom() {
815
+ if ( $this->feed_type == ATOM ) {
816
+ return $this->feed_version;
817
+ }
818
+ else {
819
+ return false;
820
+ }
821
+ }
822
+
823
+ /**
824
+ * return XML parser, and possibly re-encoded source
825
+ *
826
+ */
827
+ function create_parser($source, $out_enc, $in_enc, $detect) {
828
+ if ( substr(phpversion(),0,1) == 5) {
829
+ $parser = $this->php5_create_parser($in_enc, $detect);
830
+ }
831
+ else {
832
+ list($parser, $source) = $this->php4_create_parser($source, $in_enc, $detect);
833
+ }
834
+ if ($out_enc) {
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
+
842
+ /**
843
+ * Instantiate an XML parser under PHP5
844
+ *
845
+ * PHP5 will do a fine job of detecting input encoding
846
+ * if passed an empty string as the encoding.
847
+ *
848
+ * All hail libxml2!
849
+ *
850
+ */
851
+ function php5_create_parser($in_enc, $detect) {
852
+ // by default php5 does a fine job of detecting input encodings
853
+ if(!$detect && $in_enc) {
854
+ return xml_parser_create($in_enc);
855
+ }
856
+ else {
857
+ return xml_parser_create('');
858
+ }
859
+ }
860
+
861
+ /**
862
+ * Instaniate an XML parser under PHP4
863
+ *
864
+ * Unfortunately PHP4's support for character encodings
865
+ * and especially XML and character encodings sucks. As
866
+ * long as the documents you parse only contain characters
867
+ * from the ISO-8859-1 character set (a superset of ASCII,
868
+ * and a subset of UTF-8) you're fine. However once you
869
+ * step out of that comfy little world things get mad, bad,
870
+ * and dangerous to know.
871
+ *
872
+ * The following code is based on SJM's work with FoF
873
+ * @see http://minutillo.com/steve/weblog/2004/6/17/php-xml-and-character-encodings-a-tale-of-sadness-rage-and-data-loss
874
+ *
875
+ */
876
+ function php4_create_parser($source, $in_enc, $detect) {
877
+ if ( !$detect ) {
878
+ return array(xml_parser_create($in_enc), $source);
879
+ }
880
+
881
+ if (!$in_enc) {
882
+ if (preg_match('/<?xml.*encoding=[\'"](.*?)[\'"].*?>/m', $source, $m)) {
883
+ $in_enc = strtoupper($m[1]);
884
+ $this->source_encoding = $in_enc;
885
+ }
886
+ else {
887
+ $in_enc = 'UTF-8';
888
+ }
889
+ }
890
+
891
+ if ($this->known_encoding($in_enc)) {
892
+ return array(xml_parser_create($in_enc), $source);
893
+ }
894
+
895
+ // the dectected encoding is not one of the simple encodings PHP knows
896
+
897
+ // attempt to use the iconv extension to
898
+ // cast the XML to a known encoding
899
+ // @see http://php.net/iconv
900
+
901
+ if (function_exists('iconv')) {
902
+ $encoded_source = iconv($in_enc,'UTF-8', $source);
903
+ if ($encoded_source) {
904
+ return array(xml_parser_create('UTF-8'), $encoded_source);
905
+ }
906
+ }
907
+
908
+ // iconv didn't work, try mb_convert_encoding
909
+ // @see http://php.net/mbstring
910
+ if(function_exists('mb_convert_encoding')) {
911
+ $encoded_source = mb_convert_encoding($source, 'UTF-8', $in_enc );
912
+ if ($encoded_source) {
913
+ return array(xml_parser_create('UTF-8'), $encoded_source);
914
+ }
915
+ }
916
+
917
+ // else
918
+ $this->error("Feed is in an unsupported character encoding. ($in_enc) " .
919
+ "You may see strange artifacts, and mangled characters.",
920
+ E_USER_NOTICE);
921
+
922
+ return array(xml_parser_create(), $source);
923
+ }
924
+
925
+ function known_encoding($enc) {
926
+ $enc = strtoupper($enc);
927
+ if ( in_array($enc, $this->_KNOWN_ENCODINGS) ) {
928
+ return $enc;
929
+ }
930
+ else {
931
+ return false;
932
+ }
933
+ }
934
+
935
+ function error ($errormsg, $lvl=E_USER_WARNING) {
936
+ // append PHP's error message if track_errors enabled
937
+ if ( isset($php_errormsg) ) {
938
+ $errormsg .= " ($php_errormsg)";
939
+ }
940
+ if ( MAGPIE_DEBUG ) {
941
+ trigger_error( $errormsg, $lvl);
942
+ }
943
+ else {
944
+ error_log( $errormsg, 0);
945
+ }
946
+
947
+ $notices = E_USER_NOTICE|E_NOTICE;
948
+ if ( $lvl&$notices ) {
949
+ $this->WARNING = $errormsg;
950
+ } else {
951
+ $this->ERROR = $errormsg;
952
+ }
953
+ }
954
+
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
+
985
+ ################################################################################
986
+ ## WordPress: Load in Snoopy from wp-includes ##################################
987
+ ################################################################################
988
+
989
+ require_once( dirname(__FILE__) . '/class-snoopy.php');
990
+
991
+ ################################################################################
992
+ ## rss_fetch.inc: from MagpieRSS 0.8a ##########################################
993
+ ################################################################################
994
+
995
+ /*=======================================================================*\
996
+ Function: fetch_rss:
997
+ Purpose: return RSS object for the give url
998
+ maintain the cache
999
+ Input: url of RSS file
1000
+ Output: parsed RSS object (see rss_parse.inc)
1001
+
1002
+ NOTES ON CACHEING:
1003
+ If caching is on (MAGPIE_CACHE_ON) fetch_rss will first check the cache.
1004
+
1005
+ NOTES ON RETRIEVING REMOTE FILES:
1006
+ If conditional gets are on (MAGPIE_CONDITIONAL_GET_ON) fetch_rss will
1007
+ return a cached object, and touch the cache object upon recieving a
1008
+ 304.
1009
+
1010
+ NOTES ON FAILED REQUESTS:
1011
+ If there is an HTTP error while fetching an RSS object, the cached
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
+
1019
+ function fetch_rss ($url) {
1020
+ // initialize constants
1021
+ init();
1022
+
1023
+ if ( !isset($url) ) {
1024
+ error("fetch_rss called without a url");
1025
+ return false;
1026
+ }
1027
+
1028
+ // if cache is disabled
1029
+ if ( !MAGPIE_CACHE_ON ) {
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");
1037
+ return false;
1038
+ }
1039
+ }
1040
+ // else cache is ON
1041
+ else {
1042
+ // Flow
1043
+ // 1. check cache
1044
+ // 2. if there is a hit, make sure its fresh
1045
+ // 3. if cached obj fails freshness check, fetch remote
1046
+ // 4. if remote fails, return stale object, or error
1047
+
1048
+ $cache = new RSSCache( MAGPIE_CACHE_DIR, MAGPIE_CACHE_AGE );
1049
+
1050
+ if (MAGPIE_DEBUG and $cache->ERROR) {
1051
+ debug($cache->ERROR, E_USER_WARNING);
1052
+ }
1053
+
1054
+
1055
+ $cache_status = 0; // response of check_cache
1056
+ $request_headers = array(); // HTTP headers to send with fetch
1057
+ $rss = 0; // parsed RSS object
1058
+ $errormsg = 0; // errors, if any
1059
+
1060
+ // store parsed XML by desired output encoding
1061
+ // as character munging happens at parse time
1062
+ $cache_key = $url . MAGPIE_OUTPUT_ENCODING;
1063
+
1064
+ if (!$cache->ERROR) {
1065
+ // return cache HIT, MISS, or STALE
1066
+ $cache_status = $cache->check_cache( $cache_key);
1067
+ }
1068
+
1069
+ // if object cached, and cache is fresh, return cached obj
1070
+ if ( $cache_status == 'HIT' ) {
1071
+ $rss = $cache->get( $cache_key );
1072
+ if ( isset($rss) and $rss ) {
1073
+ // should be cache age
1074
+ $rss->from_cache = 1;
1075
+ if ( MAGPIE_DEBUG > 1) {
1076
+ debug("MagpieRSS: Cache HIT", E_USER_NOTICE);
1077
+ }
1078
+ return $rss;
1079
+ }
1080
+ }
1081
+
1082
+ // else attempt a conditional get
1083
+
1084
+ // setup headers
1085
+ if ( $cache_status == 'STALE' ) {
1086
+ $rss = $cache->get( $cache_key );
1087
+ if ( $rss and $rss->etag and $rss->last_modified ) {
1088
+ $request_headers['If-None-Match'] = $rss->etag;
1089
+ $request_headers['If-Last-Modified'] = $rss->last_modified;
1090
+ }
1091
+ }
1092
+
1093
+ $resp = _fetch_remote_file( $url, $request_headers );
1094
+
1095
+ if (isset($resp) and $resp) {
1096
+ if ($resp->status == '304' ) {
1097
+ // we have the most current copy
1098
+ if ( MAGPIE_DEBUG > 1) {
1099
+ debug("Got 304 for $url");
1100
+ }
1101
+ // reset cache on 304 (at minutillo insistent prodding)
1102
+ $cache->set($cache_key, $rss);
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");
1110
+ }
1111
+ // add object to cache
1112
+ $cache->set( $cache_key, $rss );
1113
+ return $rss;
1114
+ }
1115
+ }
1116
+ else {
1117
+ $errormsg = "Failed to fetch $url ";
1118
+ if ( $resp->status == '-100' ) {
1119
+ $errormsg .= "(Request timed out after " . MAGPIE_FETCH_TIME_OUT . " seconds)";
1120
+ }
1121
+ elseif ( $resp->error ) {
1122
+ # compensate for Snoopy's annoying habbit to tacking
1123
+ # on '\n'
1124
+ $http_error = substr($resp->error, 0, -2);
1125
+ $errormsg .= "(HTTP Error: $http_error)";
1126
+ }
1127
+ else {
1128
+ $errormsg .= "(HTTP Response: " . $resp->response_code .')';
1129
+ }
1130
+ }
1131
+ }
1132
+ else {
1133
+ $errormsg = "Unable to retrieve RSS file for unknown reasons.";
1134
+ }
1135
+
1136
+ // else fetch failed
1137
+
1138
+ // attempt to return cached object
1139
+ if ($rss) {
1140
+ if ( MAGPIE_DEBUG ) {
1141
+ debug("Returning STALE object for $url");
1142
+ }
1143
+ return $rss;
1144
+ }
1145
+
1146
+ // else we totally failed
1147
+ error( $errormsg );
1148
+
1149
+ return false;
1150
+
1151
+ } // end if ( !MAGPIE_CACHE_ON ) {
1152
+ } // end fetch_rss()
1153
+
1154
+ /*=======================================================================*\
1155
+ Function: error
1156
+ Purpose: set MAGPIE_ERROR, and trigger error
1157
+ \*=======================================================================*/
1158
+
1159
+ function error ($errormsg, $lvl=E_USER_WARNING) {
1160
+ global $MAGPIE_ERROR;
1161
+
1162
+ // append PHP's error message if track_errors enabled
1163
+ if ( isset($php_errormsg) ) {
1164
+ $errormsg .= " ($php_errormsg)";
1165
+ }
1166
+ if ( $errormsg ) {
1167
+ $errormsg = "MagpieRSS: $errormsg";
1168
+ $MAGPIE_ERROR = $errormsg;
1169
+ trigger_error( $errormsg, $lvl);
1170
+ }
1171
+ }
1172
+
1173
+ function debug ($debugmsg, $lvl=E_USER_NOTICE) {
1174
+ trigger_error("MagpieRSS [debug] $debugmsg", $lvl);
1175
+ }
1176
+
1177
+ /*=======================================================================*\
1178
+ Function: magpie_error
1179
+ Purpose: accessor for the magpie error variable
1180
+ \*=======================================================================*/
1181
+ function magpie_error ($errormsg="") {
1182
+ global $MAGPIE_ERROR;
1183
+
1184
+ if ( isset($errormsg) and $errormsg ) {
1185
+ $MAGPIE_ERROR = $errormsg;
1186
+ }
1187
+
1188
+ return $MAGPIE_ERROR;
1189
+ }
1190
+
1191
+ /*=======================================================================*\
1192
+ Function: _fetch_remote_file
1193
+ Purpose: retrieve an arbitrary remote file
1194
+ Input: url of the remote file
1195
+ headers to send along with the request (optional)
1196
+ Output: an HTTP response object (see Snoopy.class.inc)
1197
+ \*=======================================================================*/
1198
+ function _fetch_remote_file ($url, $headers = "" ) {
1199
+ // Snoopy is an HTTP client in PHP
1200
+ $client = new Snoopy();
1201
+ $client->agent = MAGPIE_USER_AGENT;
1202
+ $client->read_timeout = MAGPIE_FETCH_TIME_OUT;
1203
+ $client->use_gzip = MAGPIE_USE_GZIP;
1204
+ if (is_array($headers) ) {
1205
+ $client->rawheaders = $headers;
1206
+ }
1207
+
1208
+ @$client->fetch($url);
1209
+ return $client;
1210
+
1211
+ }
1212
+
1213
+ /*=======================================================================*\
1214
+ Function: _response_to_rss
1215
+ Purpose: parse an HTTP response object into an RSS object
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"
1228
+ if (strpos($h, ": ")) {
1229
+ list($field, $val) = explode(": ", $h, 2);
1230
+ }
1231
+ else {
1232
+ $field = $h;
1233
+ $val = "";
1234
+ }
1235
+
1236
+ if ( $field == 'ETag' ) {
1237
+ $rss->etag = $val;
1238
+ }
1239
+
1240
+ if ( $field == 'Last-Modified' ) {
1241
+ $rss->last_modified = $val;
1242
+ }
1243
+ }
1244
+
1245
+ return $rss;
1246
+ } // else construct error message
1247
+ else {
1248
+ $errormsg = "Failed to parse RSS file.";
1249
+
1250
+ if ($rss) {
1251
+ $errormsg .= " (" . $rss->ERROR . ")";
1252
+ }
1253
+ error($errormsg);
1254
+
1255
+ return false;
1256
+ } // end if ($rss and !$rss->error)
1257
+ }
1258
+
1259
+ /*=======================================================================*\
1260
+ Function: init
1261
+ Purpose: setup constants with default values
1262
+ check for user overrides
1263
+ \*=======================================================================*/
1264
+ function init () {
1265
+ if ( defined('MAGPIE_INITALIZED') ) {
1266
+ return;
1267
+ }
1268
+ else {
1269
+ define('MAGPIE_INITALIZED', true);
1270
+ }
1271
+
1272
+ if ( !defined('MAGPIE_CACHE_ON') ) {
1273
+ define('MAGPIE_CACHE_ON', true);
1274
+ }
1275
+
1276
+ if ( !defined('MAGPIE_CACHE_DIR') ) {
1277
+ define('MAGPIE_CACHE_DIR', './cache');
1278
+ }
1279
+
1280
+ if ( !defined('MAGPIE_CACHE_AGE') ) {
1281
+ define('MAGPIE_CACHE_AGE', 60*60); // one hour
1282
+ }
1283
+
1284
+ if ( !defined('MAGPIE_CACHE_FRESH_ONLY') ) {
1285
+ define('MAGPIE_CACHE_FRESH_ONLY', false);
1286
+ }
1287
+
1288
+ if ( !defined('MAGPIE_OUTPUT_ENCODING') ) {
1289
+ define('MAGPIE_OUTPUT_ENCODING', 'ISO-8859-1');
1290
+ }
1291
+
1292
+ if ( !defined('MAGPIE_INPUT_ENCODING') ) {
1293
+ define('MAGPIE_INPUT_ENCODING', null);
1294
+ }
1295
+
1296
+ if ( !defined('MAGPIE_DETECT_ENCODING') ) {
1297
+ define('MAGPIE_DETECT_ENCODING', true);
1298
+ }
1299
+
1300
+ if ( !defined('MAGPIE_DEBUG') ) {
1301
+ define('MAGPIE_DEBUG', 0);
1302
+ }
1303
+
1304
+ if ( !defined('MAGPIE_USER_AGENT') ) {
1305
+ $ua = 'MagpieRSS/'. MAGPIE_VERSION . ' (+http://magpierss.sf.net';
1306
+
1307
+ if ( MAGPIE_CACHE_ON ) {
1308
+ $ua = $ua . ')';
1309
+ }
1310
+ else {
1311
+ $ua = $ua . '; No cache)';
1312
+ }
1313
+
1314
+ define('MAGPIE_USER_AGENT', $ua);
1315
+ }
1316
+
1317
+ if ( !defined('MAGPIE_FETCH_TIME_OUT') ) {
1318
+ define('MAGPIE_FETCH_TIME_OUT', 5); // 5 second timeout
1319
+ }
1320
+
1321
+ // use gzip encoding to fetch rss files if supported?
1322
+ if ( !defined('MAGPIE_USE_GZIP') ) {
1323
+ define('MAGPIE_USE_GZIP', true);
1324
+ }
1325
+ }
1326
+
1327
+ // NOTE: the following code should really be in Snoopy, or at least
1328
+ // somewhere other then rss_fetch!
1329
+
1330
+ /*=======================================================================*\
1331
+ HTTP STATUS CODE PREDICATES
1332
+ These functions attempt to classify an HTTP status code
1333
+ based on RFC 2616 and RFC 2518.
1334
+
1335
+ All of them take an HTTP status code as input, and return true or false
1336
+
1337
+ All this code is adapted from LWP's HTTP::Status.
1338
+ \*=======================================================================*/
1339
+
1340
+
1341
+ /*=======================================================================*\
1342
+ Function: is_info
1343
+ Purpose: return true if Informational status code
1344
+ \*=======================================================================*/
1345
+ function is_info ($sc) {
1346
+ return $sc >= 100 && $sc < 200;
1347
+ }
1348
+
1349
+ /*=======================================================================*\
1350
+ Function: is_success
1351
+ Purpose: return true if Successful status code
1352
+ \*=======================================================================*/
1353
+ function is_success ($sc) {
1354
+ return $sc >= 200 && $sc < 300;
1355
+ }
1356
+
1357
+ /*=======================================================================*\
1358
+ Function: is_redirect
1359
+ Purpose: return true if Redirection status code
1360
+ \*=======================================================================*/
1361
+ function is_redirect ($sc) {
1362
+ return $sc >= 300 && $sc < 400;
1363
+ }
1364
+
1365
+ /*=======================================================================*\
1366
+ Function: is_error
1367
+ Purpose: return true if Error status code
1368
+ \*=======================================================================*/
1369
+ function is_error ($sc) {
1370
+ return $sc >= 400 && $sc < 600;
1371
+ }
1372
+
1373
+ /*=======================================================================*\
1374
+ Function: is_client_error
1375
+ Purpose: return true if Error status code, and its a client error
1376
+ \*=======================================================================*/
1377
+ function is_client_error ($sc) {
1378
+ return $sc >= 400 && $sc < 500;
1379
+ }
1380
+
1381
+ /*=======================================================================*\
1382
+ Function: is_client_error
1383
+ Purpose: return true if Error status code, and its a server error
1384
+ \*=======================================================================*/
1385
+ function is_server_error ($sc) {
1386
+ return $sc >= 500 && $sc < 600;
1387
+ }
1388
+
1389
+ ################################################################################
1390
+ ## rss_cache.inc: from WordPress 1.5 ###########################################
1391
+ ################################################################################
1392
+
1393
+ class RSSCache {
1394
+ var $BASE_CACHE = 'wp-content/cache'; // where the cache files are stored
1395
+ var $MAX_AGE = 43200; // when are files stale, default twelve hours
1396
+ var $ERROR = ''; // accumulate error messages
1397
+
1398
+ function RSSCache ($base='', $age='') {
1399
+ if ( $base ) {
1400
+ $this->BASE_CACHE = $base;
1401
+ }
1402
+ if ( $age ) {
1403
+ $this->MAX_AGE = $age;
1404
+ }
1405
+
1406
+ }
1407
+
1408
+ /*=======================================================================*\
1409
+ Function: set
1410
+ Purpose: add an item to the cache, keyed on url
1411
+ Input: url from wich the rss file was fetched
1412
+ Output: true on sucess
1413
+ \*=======================================================================*/
1414
+ function set ($url, $rss) {
1415
+ global $wpdb;
1416
+ $cache_option = 'rss_' . $this->file_name( $url );
1417
+ $cache_timestamp = 'rss_' . $this->file_name( $url ) . '_ts';
1418
+
1419
+ if ( !$wpdb->get_var("SELECT option_name FROM $wpdb->options WHERE option_name = '$cache_option'") )
1420
+ add_option($cache_option, '', '', 'no');
1421
+ if ( !$wpdb->get_var("SELECT option_name FROM $wpdb->options WHERE option_name = '$cache_timestamp'") )
1422
+ add_option($cache_timestamp, '', '', 'no');
1423
+
1424
+ update_option($cache_option, $rss);
1425
+ update_option($cache_timestamp, time() );
1426
+
1427
+ return $cache_option;
1428
+ }
1429
+
1430
+ /*=======================================================================*\
1431
+ Function: get
1432
+ Purpose: fetch an item from the cache
1433
+ Input: url from wich the rss file was fetched
1434
+ Output: cached object on HIT, false on MISS
1435
+ \*=======================================================================*/
1436
+ function get ($url) {
1437
+ $this->ERROR = "";
1438
+ $cache_option = 'rss_' . $this->file_name( $url );
1439
+
1440
+ if ( ! get_option( $cache_option ) ) {
1441
+ $this->debug(
1442
+ "Cache doesn't contain: $url (cache option: $cache_option)"
1443
+ );
1444
+ return 0;
1445
+ }
1446
+
1447
+ $rss = get_option( $cache_option );
1448
+
1449
+ return $rss;
1450
+ }
1451
+
1452
+ /*=======================================================================*\
1453
+ Function: check_cache
1454
+ Purpose: check a url for membership in the cache
1455
+ and whether the object is older then MAX_AGE (ie. STALE)
1456
+ Input: url from wich the rss file was fetched
1457
+ Output: cached object on HIT, false on MISS
1458
+ \*=======================================================================*/
1459
+ function check_cache ( $url ) {
1460
+ $this->ERROR = "";
1461
+ $cache_option = $this->file_name( $url );
1462
+ $cache_timestamp = 'rss_' . $this->file_name( $url ) . '_ts';
1463
+
1464
+ if ( $mtime = get_option($cache_timestamp) ) {
1465
+ // find how long ago the file was added to the cache
1466
+ // and whether that is longer then MAX_AGE
1467
+ $age = time() - $mtime;
1468
+ if ( $this->MAX_AGE > $age ) {
1469
+ // object exists and is current
1470
+ return 'HIT';
1471
+ }
1472
+ else {
1473
+ // object exists but is old
1474
+ return 'STALE';
1475
+ }
1476
+ }
1477
+ else {
1478
+ // object does not exist
1479
+ return 'MISS';
1480
+ }
1481
+ }
1482
+
1483
+ /*=======================================================================*\
1484
+ Function: serialize
1485
+ \*=======================================================================*/
1486
+ function serialize ( $rss ) {
1487
+ return serialize( $rss );
1488
+ }
1489
+
1490
+ /*=======================================================================*\
1491
+ Function: unserialize
1492
+ \*=======================================================================*/
1493
+ function unserialize ( $data ) {
1494
+ return unserialize( $data );
1495
+ }
1496
+
1497
+ /*=======================================================================*\
1498
+ Function: file_name
1499
+ Purpose: map url to location in cache
1500
+ Input: url from wich the rss file was fetched
1501
+ Output: a file name
1502
+ \*=======================================================================*/
1503
+ function file_name ($url) {
1504
+ return md5( $url );
1505
+ }
1506
+
1507
+ /*=======================================================================*\
1508
+ Function: error
1509
+ Purpose: register error
1510
+ \*=======================================================================*/
1511
+ function error ($errormsg, $lvl=E_USER_WARNING) {
1512
+ // append PHP's error message if track_errors enabled
1513
+ if ( isset($php_errormsg) ) {
1514
+ $errormsg .= " ($php_errormsg)";
1515
+ }
1516
+ $this->ERROR = $errormsg;
1517
+ if ( MAGPIE_DEBUG ) {
1518
+ trigger_error( $errormsg, $lvl);
1519
+ }
1520
+ else {
1521
+ error_log( $errormsg, 0);
1522
+ }
1523
+ }
1524
+ function debug ($debugmsg, $lvl=E_USER_NOTICE) {
1525
+ if ( MAGPIE_DEBUG ) {
1526
+ $this->error("MagpieRSS [debug] $debugmsg", $lvl);
1527
+ }
1528
+ }
1529
+ }
1530
+
1531
+ ################################################################################
1532
+ ## rss_utils.inc: from MagpieRSS 0.8a ##########################################
1533
+ ################################################################################
1534
+
1535
+ /*======================================================================*\
1536
+ Function: parse_w3cdtf
1537
+ Purpose: parse a W3CDTF date into unix epoch
1538
+
1539
+ NOTE: http://www.w3.org/TR/NOTE-datetime
1540
+ \*======================================================================*/
1541
+
1542
+ function parse_w3cdtf ( $date_str ) {
1543
+
1544
+ # regex to match wc3dtf
1545
+ $pat = "/^\s*(\d{4})(-(\d{2})(-(\d{2})(T(\d{2}):(\d{2})(:(\d{2})(\.\d+)?)?(?:([-+])(\d{2}):?(\d{2})|(Z))?)?)?)?\s*\$/";
1546
+
1547
+ if ( preg_match( $pat, $date_str, $match ) ) {
1548
+ list( $year, $month, $day, $hours, $minutes, $seconds) =
1549
+ array( $match[1], $match[3], $match[5], $match[7], $match[8], $match[10]);
1550
+
1551
+ # W3C dates can omit the time, the day of the month, or even the month.
1552
+ # Fill in any blanks using information from the present moment. --CWJ
1553
+ $default['hr'] = (int) gmdate('H');
1554
+ $default['day'] = (int) gmdate('d');
1555
+ $default['month'] = (int) gmdate('m');
1556
+
1557
+ if (is_null($hours)) : $hours = $default['hr']; $minutes = 0; $seconds = 0; endif;
1558
+ if (is_null($day)) : $day = $default['day']; endif;
1559
+ if (is_null($month)) : $month = $default['month']; endif;
1560
+
1561
+ # calc epoch for current date assuming GMT
1562
+ $epoch = gmmktime( $hours, $minutes, $seconds, $month, $day, $year);
1563
+
1564
+ $offset = 0;
1565
+ if ( $match[14] == 'Z' ) {
1566
+ # zulu time, aka GMT
1567
+ }
1568
+ else {
1569
+ list( $tz_mod, $tz_hour, $tz_min ) =
1570
+ array( $match[12], $match[13], $match[14]);
1571
+
1572
+ # zero out the variables
1573
+ if ( ! $tz_hour ) { $tz_hour = 0; }
1574
+ if ( ! $tz_min ) { $tz_min = 0; }
1575
+
1576
+ $offset_secs = (($tz_hour*60)+$tz_min)*60;
1577
+
1578
+ # is timezone ahead of GMT? then subtract offset
1579
+ #
1580
+ if ( $tz_mod == '+' ) {
1581
+ $offset_secs = $offset_secs * -1;
1582
+ }
1583
+
1584
+ $offset = $offset_secs;
1585
+ }
1586
+ $epoch = $epoch + $offset;
1587
+ return $epoch;
1588
+ }
1589
+ else {
1590
+ return -1;
1591
+ }
1592
+ }
1593
+
1594
+ ################################################################################
1595
+ ## WordPress: wp_rss(), get_rss() ##############################################
1596
+ ################################################################################
1597
+
1598
+ function wp_rss ($url, $num) {
1599
+ //ini_set("display_errors", false); uncomment to suppress php errors thrown if the feed is not returned.
1600
+ $num_items = $num;
1601
+ $rss = fetch_rss($url);
1602
+ if ( $rss ) {
1603
+ echo "<ul>";
1604
+ $rss->items = array_slice($rss->items, 0, $num_items);
1605
+ foreach ($rss->items as $item ) {
1606
+ echo "<li>\n";
1607
+ echo "<a href='$item[link]' title='$item[description]'>";
1608
+ echo htmlentities($item['title']);
1609
+ echo "</a><br />\n";
1610
+ echo "</li>\n";
1611
+ }
1612
+ echo "</ul>";
1613
+ }
1614
+ else {
1615
+ echo "an error has occured the feed is probably down, try again later.";
1616
+ }
1617
+ }
1618
+
1619
+ function get_rss ($uri, $num = 5) { // Like get posts, but for RSS
1620
+ $rss = fetch_rss($url);
1621
+ if ( $rss ) {
1622
+ $rss->items = array_slice($rss->items, 0, $num_items);
1623
+ foreach ($rss->items as $item ) {
1624
+ echo "<li>\n";
1625
+ echo "<a href='$item[link]' title='$item[description]'>";
1626
+ echo htmlentities($item['title']);
1627
+ echo "</a><br />\n";
1628
+ echo "</li>\n";
1629
+ }
1630
+ return $posts;
1631
+ } else {
1632
+ return false;
1633
+ }
1634
+ }
1635
+ ?>
README.text CHANGED
@@ -2,7 +2,7 @@ FeedWordPress
2
  =============
3
 
4
  * Author: [Charles Johnson](http://radgeek.com/contact)
5
- * Version: 0.98
6
  * Project URI: <http://projects.radgeek.com/feedwordpress>
7
  * License: GPL 2. See License below for copyright jots and tittles.
8
 
@@ -20,13 +20,14 @@ 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.0][] or [1.5][]), and also FTP or SFTP access to your web host. The ability
24
- 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.0]: http://wordpress.org/development/2005/12/wp2/
31
  [1.5]: http://wordpress.org/development/2005/02/strayhorn/
32
 
@@ -34,10 +35,11 @@ Installation
34
  ------------
35
  ### Requirements ###
36
 
37
- To use version 0.98 of FeedWordPress, you will need:
38
 
39
- 1. an installed configured copy of WordPress 2.0.x or 1.5.x. (FeedWordPress
40
- *will not work* with WP 1.2 or WP MU development builds.)
 
41
 
42
  2. FTP or SFTP access to your web host
43
 
@@ -51,13 +53,13 @@ And you'll probably also want to have either:
51
 
52
  #### Upgrades ####
53
 
54
- To *upgrade* an existing installation of FeedWordPress to version 0.98:
55
 
56
  1. Download the FeedWordPress archive in zip or gzipped tar format and
57
  extract the files on your computer. Replace your existing FeedWordPress
58
- files with the new files. Be sure to upgrade `rss-functions.php` if you
59
- use the optional MagpieRSS upgrade, or don't use it yet but do want to
60
- syndicate Atom 1.0 feeds.
61
 
62
  2. If you are upgrading from version 0.96 or earlier, **immediately** log
63
  in to the WordPress Dashboard, and go to Options --> Syndicated. Follow
@@ -88,7 +90,7 @@ To *upgrade* an existing installation of FeedWordPress to version 0.98:
88
  and `update-feeds.php` in your WordPress `wp-content` directory.
89
 
90
  2. (Optional) Upgrade the copy of MagpieRSS packaged with WordPress by
91
- installing the new `rss-functions.php` (archived in
92
  `OPTIONAL/wp-includes`) into your WordPress `wp-includes` directory.
93
  Upgrading MagpieRSS is necessary if you want to take advantage of
94
  support for Atom 1.0, multiple post categories, RSS enclosures, and
@@ -109,11 +111,11 @@ To *upgrade* an existing installation of FeedWordPress to version 0.98:
109
  provides some light security by keeping passing ruffians from saying
110
  "Update all the feeds" at will to your FeedWordPress installation.
111
 
112
- 5. Go to Links --> Syndicated to set up the list of sites that you want
113
- FeedWordPress to syndicate onto your blog. (If you have the feeds you
114
- want to aggregate in a service such as Bloglines, you may prefer to
115
- export them to an OPML file and use WordPress's Links --> Import to
116
- import them into the contributors category.)
117
 
118
  #### Setting Up Feed Updates ####
119
 
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
 
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
 
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
 
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
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
96
  support for Atom 1.0, multiple post categories, RSS enclosures, and
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
 
wp-content/plugins/feedwordpress.php CHANGED
@@ -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.98
7
  Author: Charles Johnson
8
  Author URI: http://radgeek.com/
9
  License: GPL
10
- Last modified: 2005-09-28 4:40pm EDT
11
  */
12
 
13
  # This uses code derived from:
@@ -27,7 +27,7 @@ Last modified: 2005-09-28 4:40pm EDT
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.98');
31
  define ('DEFAULT_SYNDICATION_CATEGORY', 'Contributors');
32
 
33
  define ('FEEDWORDPRESS_CAT_SEPARATOR_PATTERN', '/[:\n]/');
@@ -36,37 +36,24 @@ define ('FEEDWORDPRESS_CAT_SEPARATOR', "\n");
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-functions.php from
40
- // this archive into wp-includes/rss-functions.php
41
- require_once (ABSPATH . WPINC . '/rss-functions.php');
42
 
43
- if (isset($wp_db_version) and $wp_db_version >= 2966) :
 
 
 
 
 
 
44
  require_once (ABSPATH . WPINC . '/registration-functions.php');
 
45
  endif;
46
 
47
  // Is this being loaded from within WordPress 1.5 or later?
48
  if (isset($wp_version) and $wp_version >= 1.5):
49
 
50
- $fwp_db_version = get_settings('feedwordpress_version');
51
- $feedwordpress_needs_upgrade = false; // innocent until proven guilty
52
- if (!$fwp_db_version or $fwp_db_version < FEEDWORDPRESS_VERSION) :
53
- // check to see whether this is a fresh install or an upgrade
54
- $syn = $wpdb->get_col("
55
- SELECT post_id
56
- FROM $wpdb->postmeta
57
- WHERE meta_key = 'syndication_feed'
58
- ");
59
- if (count($syn) > 0) : // contains at least one syndicated post
60
- $feedwordpress_needs_upgrade = true;
61
- else : // fresh install; brand it as ours
62
- update_option('feedwordpress_version', FEEDWORDPRESS_VERSION);
63
- if (!get_settings('feedwordpress_rpc_secret')) :
64
- update_option('feedwordpress_rpc_secret', substr(md5(uniqid(microtime())), 0, 6));
65
- endif;
66
- endif;
67
- endif;
68
-
69
- if (!$feedwordpress_needs_upgrade) : // only work if the conditions are safe!
70
 
71
  # Syndicated items are generally received in output-ready (X)HTML and
72
  # should not be folded, crumpled, mutilated, or spindled by WordPress
@@ -109,7 +96,7 @@ if (isset($wp_version) and $wp_version >= 1.5):
109
  else :
110
  # Hook in the menus, which will just point to the upgrade interface
111
  add_action('admin_menu', 'fwp_add_pages');
112
- endif; // if (!$feedwordpress_needs_upgrade)
113
  endif;
114
 
115
  ################################################################################
@@ -149,6 +136,34 @@ function log_feedwordpress_update_complete ($delta) {
149
  : implode(' and ', $mesg)));
150
  }
151
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
152
  ################################################################################
153
  ## TEMPLATE API: functions to make your templates syndication-aware ############
154
  ################################################################################
@@ -262,9 +277,9 @@ function fwp_upgrade_page () {
262
  <h2>Upgrade FeedWordPress</h2>
263
 
264
  <p>It appears that you have installed FeedWordPress
265
- <?=FEEDWORDPRESS_VERSION?> as an upgrade to an existing installation of
266
  FeedWordPress. That's no problem, but you will need to take a minute out first
267
- to upgrade your database: some necessarily changes in how the software keeps
268
  track of posts and feeds will cause problems such as duplicate posts and broken
269
  templates if we were to continue without the upgrade.</p>
270
 
@@ -288,14 +303,26 @@ like me you may want to back up your database before you proceed.</p>
288
  ################################################################################
289
 
290
  function fwp_add_pages () {
291
- add_submenu_page('link-manager.php', 'Syndicated Sites', 'Syndicated', 5, basename(__FILE__), 'fwp_syndication_manage_page');
292
- add_options_page('Syndication Options', 'Syndication', 6, basename(__FILE__), 'fwp_syndication_options_page');
 
 
 
 
 
 
 
 
 
 
 
 
293
  } // function fwp_add_pages () */
294
 
295
  function fwp_syndication_options_page () {
296
- global $wpdb, $user_level;
297
 
298
- if ($GLOBALS['feedwordpress_needs_upgrade']) :
299
  fwp_upgrade_page();
300
  return;
301
  endif;
@@ -304,7 +331,7 @@ function fwp_syndication_options_page () {
304
  if (isset($_POST['action']) and $_POST['action']==$caption):
305
  check_admin_referer();
306
 
307
- if ($user_level < 6):
308
  die (__("Cheatin' uh ?"));
309
  else:
310
  update_option('feedwordpress_rpc_secret', $_REQUEST['rpc_secret']);
@@ -392,7 +419,12 @@ function fwp_syndication_options_page () {
392
  if (is_string($uc) and array_key_exists($uc, $unfamiliar_category)) :
393
  $unfamiliar_category[$uc] = ' checked="checked"';
394
  endif;
395
- $results = $wpdb->get_results("SELECT cat_id, cat_name, auto_toggle FROM $wpdb->linkcategories ORDER BY cat_id");
 
 
 
 
 
396
 
397
  $cats = get_settings('feedwordpress_syndication_cats');
398
  $dogs = get_nested_categories(-1, 0);
@@ -419,6 +451,8 @@ function fwp_syndication_options_page () {
419
  <td width="67%"><?php
420
  echo "\n<select name=\"syndication_category\" size=\"1\">";
421
  foreach ($results as $row) {
 
 
422
  echo "\n\t<option value=\"$row->cat_id\"";
423
  if ($row->cat_id == $cat_id)
424
  echo " selected='selected'";
@@ -433,9 +467,9 @@ function fwp_syndication_options_page () {
433
 
434
  <tr><th width="33%" scope="row" style="vertical-align:top">Update live from feed:</th>
435
  <td width="67%"><ul style="margin:0;list-style:none">
436
- <li><input type="checkbox" name="hardcode_name" value="no"<?=(($hardcode_name=='yes')?'':' checked="checked"')?>/> Contributor name (feed title)</li>
437
- <li><input type="checkbox" name="hardcode_description" value="no"<?=(($hardcode_description=='yes')?'':' checked="checked"')?>/> Contributor description (feed tagline)</li>
438
- <li><input type="checkbox" name="hardcode_url" value="no"<?=(($hardcode_url=='yes')?'':' checked="checked"')?>/> Homepage (feed link)</li>
439
  </ul></td></tr>
440
  </table>
441
  </fieldset>
@@ -443,53 +477,48 @@ function fwp_syndication_options_page () {
443
  <fieldset class="options">
444
  <legend>Syndicated Posts</legend>
445
 
446
- <fieldset id="categorydiv" style="width: 20%; margin-right: 2em">
447
- <legend>Categories</legend>
448
- <p style="font-size:smaller;font-style:bold;margin:0">Place <em>all syndicated
449
- posts</em> under...</p>
450
- <div style="height: 20em"><?php write_nested_categories($dogs); ?></div>
451
- </fieldset>
452
 
453
  <table class="editform" width="75%" cellspacing="2" cellpadding="5">
454
  <tr style="vertical-align: top"><th width="33%" scope="row">Publication:</th>
455
  <td width="67%"><ul style="margin: 0; padding: 0; list-style:none">
456
- <li><label><input type="radio" name="post_status" value="publish"<?=($post_status=='publish')?' checked="checked"':''?> /> Publish syndicated posts immediately</label></li>
457
- <li><label><input type="radio" name="post_status" value="draft"<?=($post_status=='draft')?' checked="checked"':''?> /> Hold syndicated posts as drafts</label></li>
458
- <li><label><input type="radio" name="post_status" value="private"<?=($post_status=='private')?' checked="checked"':''?> /> Hold syndicated posts as private posts</label></li>
459
  </ul></td></tr>
460
 
461
  <tr style="vertical-align: top"><th width="33%" scope="row">Comments:</th>
462
  <td width="67%"><ul style="margin: 0; padding: 0; list-style:none">
463
- <li><label><input type="radio" name="comment_status" value="open"<?=($comment_status=='open')?' checked="checked"':''?> /> Allow comments on syndicated posts</label></li>
464
- <li><label><input type="radio" name="comment_status" value="closed"<?=($comment_status!='open')?' checked="checked"':''?> /> Don't allow comments on syndicated posts</label></li>
465
  </ul></td></tr>
466
 
467
  <tr style="vertical-align: top"><th width="33%" scope="row">Trackback and Pingback:</th>
468
  <td width="67%"><ul style="margin:0; padding: 0; list-style:none">
469
- <li><label><input type="radio" name="ping_status" value="open"<?=($ping_status=='open')?' checked="checked"':''?> /> Accept pings on syndicated posts</label></li>
470
- <li><label><input type="radio" name="ping_status" value="closed"<?=($ping_status!='open')?' checked="checked"':''?> /> Don't accept pings on syndicated posts</label></li>
471
  </ul></td></tr>
472
 
473
  <tr style="vertical-align: top"><th width="33%" scope="row" style="vertical-align:top">Unfamiliar authors:</th>
474
  <td width="67%"><ul style="margin: 0; padding: 0; list-style:none">
475
- <li><label><input type="radio" name="unfamiliar_author" value="create"<?=$unfamiliar_author['create']?>/> create a new author account</label></li>
476
- <li><label><input type="radio" name="unfamiliar_author" value="default"<?=$unfamiliar_author['default']?> /> attribute the post to the default author</label></li>
477
- <li><label><input type="radio" name="unfamiliar_author" value="filter"<?=$unfamiliar_author['filter']?> /> don't syndicate the post</label></li>
478
  </ul></td></tr>
479
  <tr style="vertical-align: top"><th width="33%" scope="row" style="vertical-align:top">Unfamiliar categories:</th>
480
  <td width="67%"><ul style="margin: 0; padding:0; list-style:none">
481
- <li><label><input type="radio" name="unfamiliar_category" value="create"<?=$unfamiliar_category['create']?>/> create any categories the post is in</label></li>
482
- <li><label><input type="radio" name="unfamiliar_category" value="default"<?=$unfamiliar_category['default']?>/> don't create new categories</li>
483
- <li><label><input type="radio" name="unfamiliar_category" value="filter"<?=$unfamiliar_category['filter']?>/> don't create new categories and don't syndicate posts unless they match at least one familiar category</label></li>
484
  </ul></td></tr>
485
 
486
  <tr style="vertical-align: top"><th width="33%" scope="row">Permalinks point to:</th>
487
  <td width="67%"><select name="munge_permalink" size="1">
488
- <option value="yes"<?=($munge_permalink=='yes')?' selected="selected"':''?>>original website</option>
489
- <option value="no"<?=($munge_permalink=='no')?' selected="selected"':''?>>this website</option>
490
  </select></td></tr>
491
  </table>
492
- <div class="submit"><input type="submit" name="action" value="<?=$caption?>" /></div>
493
  </fieldset>
494
 
495
  <fieldset class="options">
@@ -497,28 +526,59 @@ posts</em> under...</p>
497
  <table class="editform" width="100%" cellspacing="2" cellpadding="5">
498
  <tr>
499
  <th width="33%" scope="row">XML-RPC update secret word:</th>
500
- <td width="67%"><input id="rpc_secret" name="rpc_secret" value="<?=$rpc_secret?>" />
501
  </td>
502
  </tr>
503
  <tr>
504
  <th scope="row">Write update notices to PHP logs:</th>
505
  <td><select name="update_logging" size="1">
506
- <option value="yes"<?=(($update_logging=='yes')?' selected="selected"':'')?>>yes</option>
507
- <option value="no"<?=(($update_logging!='yes')?' selected="selected"':'')?>>no</option>
508
  </select></td>
509
  </tr>
510
  </table>
511
- <div class="submit"><input type="submit" name="action" value="<?=$caption?>" /></div>
512
  </fieldset>
513
  </form>
514
  </div>
515
  <?php
516
  }
517
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
518
  function fwp_syndication_manage_page () {
519
- global $user_level, $wpdb;
520
 
521
- if ($GLOBALS['feedwordpress_needs_upgrade']) :
522
  fwp_upgrade_page();
523
  return;
524
  endif;
@@ -536,10 +596,10 @@ endif;
536
  if ($cont):
537
  ?>
538
  <?php
539
- $links = get_linkobjects(FeedWordPress::link_category_id());
540
  ?>
541
  <div class="wrap">
542
- <form action="link-manager.php?page=<?=basename(__FILE__)?>" method="post">
543
  <h2>Syndicate a new site:</h2>
544
  <div>
545
  <label for="add-uri">Website or newsfeed:</label>
@@ -550,7 +610,7 @@ if ($cont):
550
  </form>
551
  </div>
552
 
553
- <form action="link-manager.php?page=<?=basename(__FILE__)?>" method="post">
554
  <div class="wrap">
555
  <h2>Syndicated Sites</h2>
556
  <?php $alt_row = true;
@@ -565,8 +625,8 @@ if ($cont):
565
 
566
  <?php foreach ($links as $link):
567
  $alt_row = !$alt_row; ?>
568
- <tr<?=($alt_row?' class="alternate"':'')?>>
569
- <td><a href="<?=wp_specialchars($link->link_url)?>"><?=wp_specialchars($link->link_name)?></a></td>
570
  <?php
571
  if (strlen($link->link_rss) > 0):
572
  $caption='Switch Feed';
@@ -581,7 +641,7 @@ if ($cont):
581
  if (strlen($display_uri) > 32) : $display_uri = substr($display_uri, 0, 32).'&#8230;'; endif;
582
  ?>
583
  <td>
584
- <strong><a href="<?=$link->link_rss?>"><?=wp_specialchars($display_uri)?></a></strong></td>
585
  <?php
586
  else:
587
  $caption='Find Feed';
@@ -590,17 +650,12 @@ if ($cont):
590
  feed assigned</strong></p></td>
591
  <?php
592
  endif;
593
-
594
- if (($link->user_level <= $user_level)):
595
  ?>
596
- <td><a href="link-manager.php?page=<?=basename(__FILE__)?>&amp;link_id=<?=$link->link_id?>&amp;action=linkedit" class="edit"><?php _e('Edit')?></a></td>
597
- <td><a href="link-manager.php?page=<?=basename(__FILE__)?>&amp;link_id=<?=$link->link_id?>&amp;action=feedfinder" class="edit"><?=$caption?></a></td>
598
- <td><a href="link-manager.php?page=<?=basename(__FILE__)?>&amp;link_id=<?=$link->link_id?>&amp;action=Unsubscribe" class="delete"><?php _e('Unsubscribe'); ?></a></td>
599
- <td><input type="checkbox" name="link_ids[]" value="<?=$link->link_id?>" /></td>
600
  <?php
601
- else:
602
- echo "<td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td>\n";
603
- endif;
604
  echo "\n\t</tr>";
605
  endforeach;
606
  else:
@@ -624,7 +679,7 @@ endif;
624
  }
625
 
626
  function fwp_feedfinder_page () {
627
- global $user_level, $wpdb;
628
 
629
  $lookup = (isset($_REQUEST['lookup'])?$_REQUEST['lookup']:NULL);
630
 
@@ -643,8 +698,9 @@ function fwp_feedfinder_page () {
643
  endif;
644
  ?>
645
  <div class="wrap">
646
- <h2>Feed Finder: <?=$name?></h2>
647
- <?php $f =& new FeedFinder($lookup);
 
648
  $feeds = $f->find();
649
  if (count($feeds) > 0):
650
  foreach ($feeds as $key => $f):
@@ -653,17 +709,17 @@ function fwp_feedfinder_page () {
653
  $feed_title = isset($rss->channel['title'])?$rss->channel['title']:$rss->channel['link'];
654
  $feed_link = isset($rss->channel['link'])?$rss->channel['link']:'';
655
  ?>
656
- <form action="link-manager.php?page=<?=basename(__FILE__)?>" method="post">
657
  <fieldset style="clear: both">
658
- <legend><?=$rss->feed_type?> <?=$rss->feed_version?> feed</legend>
659
 
660
  <?php if ($link_id===0): ?>
661
- <input type="hidden" name="feed_title" value="<?=wp_specialchars($feed_title)?>" />
662
- <input type="hidden" name="feed_link" value="<?=wp_specialchars($feed_link)?>" />
663
  <?php endif; ?>
664
 
665
- <input type="hidden" name="link_id" value="<?=$link_id?>" />
666
- <input type="hidden" name="feed" value="<?=wp_specialchars($f)?>" />
667
  <input type="hidden" name="action" value="switchfeed" />
668
 
669
  <div>
@@ -672,11 +728,11 @@ function fwp_feedfinder_page () {
672
  <?php $item = $rss->items[0]; ?>
673
  <h3>Sample Item</h3>
674
  <ul>
675
- <li><strong>Title:</strong> <a href="<?=$item['link']?>"><?=$item['title']?></a></li>
676
- <li><strong>Date:</strong> <?=isset($item['date_timestamp']) ? date('d-M-y g:i:s a', $item['date_timestamp']) : 'unknown'?></li>
677
  </ul>
678
  <div class="entry">
679
- <?=(isset($item['content']['encoded'])?$item['content']['encoded']:$item['description'])?>
680
  </div>
681
  <?php else: ?>
682
  <h3>No Items</h3>
@@ -687,10 +743,10 @@ function fwp_feedfinder_page () {
687
  <div>
688
  <h3>Feed Information</h3>
689
  <ul>
690
- <li><strong>Website:</strong> <a href="<?=$feed_link?>"><?=is_null($feed_title)?'<em>Unknown</em>':$feed_title?></a></li>
691
- <li><strong>Feed URI:</strong> <a href="<?=wp_specialchars($f)?>"><?=wp_specialchars($f)?></a> <a title="Check feed &lt;<?=wp_specialchars($f)?>&gt; for validity" href="http://feedvalidator.org/check.cgi?url=<?=urlencode($f)?>"><img src="../wp-images/smilies/icon_arrow.gif" alt="(&rarr;)" /></a></li>
692
- <li><strong>Encoding:</strong> <?=isset($rss->encoding)?wp_specialchars($rss->encoding):"<em>Unknown</em>"?></li>
693
- <li><strong>Description:</strong> <?=isset($rss->channel['description'])?wp_specialchars($rss->channel['description']):"<em>Unknown</em>"?></li>
694
  </ul>
695
  <div class="submit"><input type="submit" name="Use" value="&laquo; Use this feed" /></div>
696
  <div class="submit"><input type="submit" name="Cancel" value="&laquo; Cancel" /></div>
@@ -706,12 +762,12 @@ function fwp_feedfinder_page () {
706
  ?>
707
  </div>
708
 
709
- <form action="link-manager.php?page=<?=basename(__FILE__)?>" method="post">
710
  <div class="wrap">
711
  <h2>Use another feed</h2>
712
  <div><label>Feed:</label>
713
  <input type="text" name="lookup" value="URI" />
714
- <input type="hidden" name="link_id" value="<?=$link_id?>" />
715
  <input type="hidden" name="action" value="feedfinder" /></div>
716
  <div class="submit"><input type="submit" value="Use this feed &raquo;" /></div>
717
  </div>
@@ -721,29 +777,19 @@ function fwp_feedfinder_page () {
721
  }
722
 
723
  function fwp_switchfeed_page () {
724
- global $wpdb, $user_level;
725
 
726
  check_admin_referer();
727
  if (!isset($_REQUEST['Cancel'])):
728
- if ($user_level < 5):
729
  die (__("Cheatin' uh ?"));
730
  elseif (isset($_REQUEST['link_id']) and ($_REQUEST['link_id']==0)):
731
- // Get the category ID#
732
- $cat_id = FeedWordPress::link_category_id();
733
- $result = $wpdb->query("
734
- INSERT INTO $wpdb->links
735
- SET
736
- link_name = '".$wpdb->escape($_REQUEST['feed_title'])."',
737
- link_url = '".$wpdb->escape($_REQUEST['feed_link'])."',
738
- link_category = '".$wpdb->escape($cat_id)."',
739
- link_rss = '".$wpdb->escape($_REQUEST['feed'])."'
740
- ");
741
-
742
- if ($result): ?>
743
- <div class="updated"><p><a href="<?=$_REQUEST['feed_link']?>"><?=wp_specialchars($_REQUEST['feed_title'])?></a>
744
- has been added as a contributing site, using the newsfeed at &lt;<a href="<?=$_REQUEST['feed']?>"><?=wp_specialchars($_REQUEST['feed'])?></a>&gt;.</p></div>
745
  <?php else: ?>
746
- <div class="updated"><p>There was a problem adding the newsfeed. [SQL: <?=wp_specialchars(mysql_error())?>]</p></div>
747
  <?php endif;
748
  elseif (isset($_REQUEST['link_id'])):
749
  // Update link_rss
@@ -760,8 +806,8 @@ has been added as a contributing site, using the newsfeed at &lt;<a href="<?=$_R
760
  WHERE link_id = '".$wpdb->escape($_REQUEST['link_id'])."'
761
  ");
762
  ?>
763
- <div class="updated"><p>Feed for <a href="<?=$result->link_url?>"><?=wp_specialchars($result->link_name)?></a>
764
- updated to &lt;<a href="<?=$_REQUEST['feed']?>"><?=wp_specialchars($_REQUEST['feed'])?></a>&gt;.</p></div>
765
  <?php else: ?>
766
  <div class="updated"><p>Nothing was changed.</p></div>
767
  <?php endif;
@@ -771,7 +817,7 @@ updated to &lt;<a href="<?=$_REQUEST['feed']?>"><?=wp_specialchars($_REQUEST['fe
771
  }
772
 
773
  function fwp_linkedit_page () {
774
- global $wpdb, $user_level;
775
 
776
  check_admin_referer(); // Make sure we arrived here from the Dashboard
777
 
@@ -791,7 +837,7 @@ function fwp_linkedit_page () {
791
  'link/.*',
792
  );
793
 
794
- if ($user_level < 5) :
795
  die (__("Cheatin' uh ?"));
796
  elseif (isset($_REQUEST['feedfinder'])) :
797
  return fwp_feedfinder_page(); // re-route to Feed Finder page
@@ -993,9 +1039,9 @@ function fwp_linkedit_page () {
993
  <div class="updated"><p>Syndicated feed settings updated.</p></div>
994
  <?php endif; ?>
995
 
996
- <form action="link-manager.php?page=<?=basename(__FILE__)?>" method="post">
997
  <div class="wrap">
998
- <input type="hidden" name="link_id" value="<?=$link_id?>" />
999
  <input type="hidden" name="action" value="linkedit" />
1000
  <input type="hidden" name="save" value="link" />
1001
 
@@ -1004,9 +1050,9 @@ function fwp_linkedit_page () {
1004
  <table class="editform" width="100%" cellspacing="2" cellpadding="5">
1005
  <tr>
1006
  <th scope="row" width="20%"><?php _e('Feed URI:') ?></th>
1007
- <td width="60%"><a href="<?=wp_specialchars($link_rss_uri)?>"><?=$link_rss_uri?></a>
1008
- <a href="<?=FEEDVALIDATOR_URI?>?url=<?=urlencode($link_rss_uri)?>"
1009
- title="Check feed &lt;<?=wp_specialchars($link_rss_uri)?>&gt; for validity"><img src="../wp-images/smilies/icon_arrow.gif" alt="&rarr;" /></a>
1010
  </td>
1011
  <td width="20%"><input type="submit" name="feedfinder" value="switch &rarr;" style="font-size:smaller" /></td>
1012
  </tr>
@@ -1014,12 +1060,12 @@ title="Check feed &lt;<?=wp_specialchars($link_rss_uri)?>&gt; for validity"><img
1014
  <th scope="row" width="20%"><?php _e('Link Name:') ?></th>
1015
  <td width="60%"><input type="text" id="basics-name-edit" name="name"
1016
  value="<?php echo $link_name; ?>" style="width: 95%" />
1017
- <span id="basics-name-view"><strong><?=$link_name?></strong></span>
1018
  </td>
1019
  <td>
1020
  <select id="basics-hardcode-name" onchange="flip_hardcode('name')" name="hardcode_name">
1021
- <option value="no" <?=FeedWordPress::hardcode('name', $meta)?'':'selected="selected"'?>>update automatically</option>
1022
- <option value="yes" <?=FeedWordPress::hardcode('name', $meta)?'selected="selected"':''?>>edit manually</option>
1023
  </select>
1024
  </td>
1025
  </tr>
@@ -1027,24 +1073,24 @@ value="<?php echo $link_name; ?>" style="width: 95%" />
1027
  <th scope="row" width="20%"><?php _e('Short description:') ?></th>
1028
  <td width="60%">
1029
  <input id="basics-description-edit" type="text" name="description" value="<?php echo $link_description; ?>" style="width: 95%" />
1030
- <span id="basics-description-view"><strong><?=$link_description?></strong></span>
1031
  </td>
1032
  <td>
1033
  <select id="basics-hardcode-description" onchange="flip_hardcode('description')"
1034
  name="hardcode_description">
1035
- <option value="no" <?=FeedWordPress::hardcode('description', $meta)?'':'selected="selected"'?>>update automatically</option>
1036
- <option value="yes" <?=FeedWordPress::hardcode('description', $meta)?'selected="selected"':''?>>edit manually</option>
1037
  </select></td>
1038
  </tr>
1039
  <tr>
1040
  <th width="20%" scope="row"><?php _e('Homepage:') ?></th>
1041
  <td width="60%">
1042
  <input id="basics-url-edit" type="text" name="linkurl" value="<?php echo $link_url; ?>" style="width: 95%;" />
1043
- <a id="basics-url-view" href="<?=$link_url?>"><?=$link_url?></a></td>
1044
  <td>
1045
  <select id="basics-hardcode-url" onchange="flip_hardcode('url')" name="hardcode_url">
1046
- <option value="no"<?=FeedWordPress::hardcode('url', $meta)?'':' selected="selected"'?>>update live from feed</option>
1047
- <option value="yes"<?=FeedWordPress::hardcode('url', $meta)?' selected="selected"':''?>>edit manually</option>
1048
  </select></td></tr>
1049
 
1050
  <tr>
@@ -1061,7 +1107,7 @@ name="hardcode_description">
1061
  $holdem = (isset($meta['update/hold']) ? $meta['update/hold'] : 'scheduled');
1062
  ?>
1063
  <select name="update_schedule">
1064
- <option value="scheduled"<?=($holdem=='scheduled')?' selected="selected"':''?>>update on schedule <?php
1065
  echo " (";
1066
  if (isset($meta['update/ttl']) and is_numeric($meta['update/ttl'])) :
1067
  if (isset($meta['update/timed']) and $meta['update/timed']=='automatically') :
@@ -1079,8 +1125,8 @@ name="hardcode_description">
1079
  endif;
1080
  echo ")";
1081
  ?></option>
1082
- <option value="next"<?=($holdem=='next')?' selected="selected"':''?>>update ASAP</option>
1083
- <option value="ping"<?=($holdem=='ping')?' selected="selected"':''?>>update only when pinged</option>
1084
  </select></tr>
1085
  </table>
1086
  </fieldset>
@@ -1098,49 +1144,44 @@ flip_hardcode('url');
1098
  <fieldset>
1099
  <legend>Syndicated Posts</legend>
1100
 
1101
- <fieldset id="categorydiv" style="width: 20%; margin-right: 2em">
1102
- <legend>Categories</legend>
1103
- <p style="font-size:smaller;font-style:bold;margin:0">Place all syndicated posts from this feed
1104
- under...</p>
1105
- <div style="height: 16em"><?php write_nested_categories($dogs); ?></div>
1106
- </fieldset>
1107
 
1108
  <table class="editform" width="80%" cellspacing="2" cellpadding="5">
1109
  <tr><th width="20%" scope="row" style="vertical-align:top">Publication:</th>
1110
  <td width="80%" style="vertical-align:top"><ul style="margin:0; list-style:none">
1111
  <li><label><input type="radio" name="feed_post_status" value="site-default"
1112
- <?=$status['post']['site-default']?> /> Use site-wide setting from <a href="options-general.php?page=<?=basename(__FILE__)?>">Syndication Options</a>
1113
- (currently: <strong><?=FeedWordPress::syndicated_status('post', array(), 'publish')?></strong>)</label></li>
1114
  <li><label><input type="radio" name="feed_post_status" value="publish"
1115
- <?=$status['post']['publish']?> /> Publish posts from this feed immediately</label></li>
1116
  <li><label><input type="radio" name="feed_post_status" value="private"
1117
- <?=$status['post']['private']?> /> Hold posts from this feed as private posts</label></li>
1118
  <li><label><input type="radio" name="feed_post_status" value="draft"
1119
- <?=$status['post']['draft']?> /> Hold posts from this feed as drafts</label></li>
1120
  </ul></td>
1121
  </tr>
1122
 
1123
  <tr><th width="20%" scope="row" style="vertical-align:top">Comments:</th>
1124
  <td width="80%"><ul style="margin:0; list-style:none">
1125
  <li><label><input type="radio" name="feed_comment_status" value="site-default"
1126
- <?=$status['comment']['site-default']?> /> Use site-wide setting from <a href="options-general.php?page=<?=basename(__FILE__)?>">Syndication Options</a>
1127
- (currently: <strong><?=FeedWordPress::syndicated_status('comment', array(), 'closed')?>)</strong></label></li>
1128
  <li><label><input type="radio" name="feed_comment_status" value="open"
1129
- <?=$status['comment']['open']?> /> Allow comments on syndicated posts from this feed</label></li>
1130
  <li><label><input type="radio" name="feed_comment_status" value="closed"
1131
- <?=$status['comment']['closed']?> /> Don't allow comments on syndicated posts from this feed</label></li>
1132
  </ul></td>
1133
  </tr>
1134
 
1135
  <tr><th width="20%" scope="row" style="vertical-align:top">Trackback and Pingback:</th>
1136
  <td width="80%"><ul style="margin:0; list-style:none">
1137
  <li><label><input type="radio" name="feed_ping_status" value="site-default"
1138
- <?=$status['ping']['site-default']?> /> Use site-wide setting from <a href="options-general.php?page=<?=basename(__FILE__)?>">Syndication Options</a>
1139
- (currently: <strong><?=FeedWordPress::syndicated_status('ping', array(), 'closed')?>)</strong></label></li>
1140
  <li><label><input type="radio" name="feed_ping_status" value="open"
1141
- <?=$status['ping']['open']?> /> Accept pings on syndicated posts from this feed</label></li>
1142
  <li><label><input type="radio" name="feed_ping_status" value="closed"
1143
- <?=$status['ping']['closed']?> /> Don't accept pings on syndicated posts from this feed</label></li>
1144
  </ul></td>
1145
  </tr>
1146
  </table>
@@ -1156,22 +1197,22 @@ under...</p>
1156
  <tr>
1157
  <th width="20%" scope="row" style="vertical-align:top">Unfamiliar authors:</th>
1158
  <td width="80%"><ul style="margin: 0; list-style:none">
1159
- <li><label><input type="radio" name="unfamiliar_author" value="site-default"<?=$unfamiliar['author']['site-default']?> /> use site-wide setting from <a href="options-general.php?page=<?=basename(__FILE__)?>">Syndication Options</a>
1160
- (currently <strong><?=FeedWordPress::on_unfamiliar('author');?></strong>)</label></li>
1161
- <li><label><input type="radio" name="unfamiliar_author" value="create"<?=$unfamiliar['author']['create']?>/> create a new author account</label></li>
1162
- <li><label><input type="radio" name="unfamiliar_author" value="default"<?=$unfamiliar['author']['default']?> /> attribute the post to the default author</label></li>
1163
- <li><label><input type="radio" name="unfamiliar_author" value="filter"<?=$unfamiliar['author']['filter']?> /> don't syndicate the post</label></li>
1164
  </ul></td>
1165
  </tr>
1166
 
1167
  <tr>
1168
  <th width="20%" scope="row" style="vertical-align:top">Unfamiliar categories:</th>
1169
  <td width="80%"><ul style="margin: 0; list-style:none">
1170
- <li><label><input type="radio" name="unfamiliar_category" value="site-default"<?=$unfamiliar['category']['site-default']?> /> use site-wide setting from <a href="options-general.php?page=<?=basename(__FILE__)?>">Syndication Options</a>
1171
- (currently <strong><?=FeedWordPress::on_unfamiliar('category');?></strong>)</label></li>
1172
- <li><label><input type="radio" name="unfamiliar_category" value="create"<?=$unfamiliar['category']['create']?> /> create any categories the post is in</label></li>
1173
- <li><label><input type="radio" name="unfamiliar_category" value="default"<?=$unfamiliar['category']['default']?> /> don't create new categories</label></li>
1174
- <li><label><input type="radio" name="unfamiliar_category" value="filter"<?=$unfamiliar['category']['filter']?> /> don't create new categories and don't syndicate posts unless they match at least one familiar category</label></li>
1175
  </ul></td>
1176
  </tr></table>
1177
  </fieldset>
@@ -1196,10 +1237,10 @@ under...</p>
1196
  if (!preg_match("\007^((".implode(')|(', $special_settings)."))$\007i", $key)) :
1197
  ?>
1198
  <tr style="vertical-align:top">
1199
- <th width="30%" scope="row"><input type="hidden" name="notes[<?=$i?>][key0]" value="<?=wp_specialchars($key)?>" />
1200
- <input id="notes-<?=$i?>-key" name="notes[<?=$i?>][key1]" value="<?=wp_specialchars($key)?>" /></th>
1201
- <td width="60%"><textarea rows="2" cols="40" id="notes-<?=$i?>-value" name="notes[<?=$i?>][value]"><?=wp_specialchars($value)?></textarea></td>
1202
- <td width="10%"><select name="notes[<?=$i?>][action]">
1203
  <option value="update">save changes</option>
1204
  <option value="delete">delete this setting</option>
1205
  </select></td>
@@ -1210,9 +1251,9 @@ under...</p>
1210
  endforeach;
1211
  ?>
1212
  <tr>
1213
- <th scope="row"><input type="text" size="10" name="notes[<?=$i?>][key1]" value="" /></th>
1214
- <td><textarea name="notes[<?=$i?>][value]" rows="2" cols="40"></textarea></td>
1215
- <td><em>add new setting...</em><input type="hidden" name="notes[<?=$i?>][action]" value="update" /></td>
1216
  </tr>
1217
  </table>
1218
  </fieldset>
@@ -1228,14 +1269,14 @@ under...</p>
1228
  }
1229
 
1230
  function fwp_multidelete_page () {
1231
- global $wpdb, $user_level;
1232
 
1233
  check_admin_referer(); // Make sure the referers are kosher
1234
 
1235
  $link_ids = (isset($_REQUEST['link_ids']) ? $_REQUEST['link_ids'] : array());
1236
  if (isset($_REQUEST['link_id'])) : array_push($link_ids, $_REQUEST['link_id']); endif;
1237
 
1238
- if ($user_level < 5):
1239
  die (__("Cheatin' uh ?"));
1240
  elseif (isset($_POST['confirm']) and $_POST['confirm']=='Delete'):
1241
  foreach ($_POST['link_action'] as $link_id => $what) :
@@ -1318,7 +1359,7 @@ function fwp_multidelete_page () {
1318
  WHERE link_id IN (".implode(",",$link_ids).")
1319
  ");
1320
  ?>
1321
- <form action="link-manager.php?page=<?=basename(__FILE__)?>" method="post">
1322
  <div class="wrap">
1323
  <input type="hidden" name="action" value="Unsubscribe" />
1324
  <input type="hidden" name="confirm" value="Delete" />
@@ -1332,34 +1373,34 @@ function fwp_multidelete_page () {
1332
  $meta = FeedWordPress::notes_to_settings($link->link_notes);
1333
  ?>
1334
  <fieldset>
1335
- <legend><?=$link_name?></legend>
1336
  <table class="editform" width="100%" cellspacing="2" cellpadding="5">
1337
  <tr><th scope="row" width="20%"><?php _e('Feed URI:') ?></th>
1338
- <td width="80%"><a href="<?=$link_rss?>"><?=$link_rss?></a></td></tr>
1339
  <tr><th scope="row" width="20%"><?php _e('Short description:') ?></th>
1340
- <td width="80%"><?=$link_description?></span></td></tr>
1341
  <tr><th width="20%" scope="row"><?php _e('Homepage:') ?></th>
1342
- <td width="80%"><a href="<?=$link_url?>"><?=$link_url?></a></td></tr>
1343
  <tr style="vertical-align:top"><th width="20%" scope="row">Subscription <?php _e('Options') ?>:</th>
1344
  <td width="80%"><ul style="margin:0; padding: 0; list-style: none">
1345
- <li><input type="radio" id="hide-<?=$link->link_id?>"
1346
- name="link_action[<?=$link->link_id?>]" value="hide" />
1347
- <label for="hide-<?=$link->link_id?>">Turn off the subscription for this
1348
  syndicated link<br/><span style="font-size:smaller">(Keep the feed information
1349
  and all the posts from this feed in the database, but don't syndicate any
1350
  new posts from the feed.)</span></label></li>
1351
- <li><input type="radio" id="nuke-<?=$link->link_id?>"
1352
- name="link_action[<?=$link->link_id?>]" value="nuke" />
1353
- <label for="nuke-<?=$link->link_id?>">Delete this syndicated link and all the
1354
  posts that were syndicated from it</label></li>
1355
- <li><input type="radio" id="delete-<?=$link->link_id?>"
1356
- name="link_action[<?=$link->link_id?>]" value="delete" />
1357
- <label for="delete-<?=$link->link_id?>">Delete this syndicated link, but
1358
  <em>keep</em> posts that were syndicated from it (as if they were authored
1359
  locally).</label></li>
1360
- <li><input type="radio" id="nothing-<?=$link->link_id?>"
1361
- name="link_action[<?=$link->link_id?>]" value="nothing" />
1362
- <label for="nothing-<?=$link->link_id?>">Keep this feed as it is. I changed
1363
  my mind.</label></li>
1364
  </ul>
1365
  </table>
@@ -1479,7 +1520,7 @@ class FeedWordPress {
1479
  # the function `get_feed_meta($key)` if this plugin is activated.
1480
 
1481
  function FeedWordPress () {
1482
- $result = get_linkobjects(FeedWordPress::link_category_id());
1483
 
1484
  $feeds = array ();
1485
  if ($result): foreach ($result as $link):
@@ -1601,7 +1642,7 @@ class FeedWordPress {
1601
 
1602
  $uri = trim($uri);
1603
 
1604
- if ($GLOBALS['feedwordpress_needs_upgrade']) : // Will make duplicate posts if we don't hold off
1605
  return NULL;
1606
  endif;
1607
 
@@ -1636,14 +1677,14 @@ class FeedWordPress {
1636
  endif;
1637
 
1638
  if ($pinged_that and $timely) :
1639
- do_action('feedwordpress_check_feed', array($feed));
1640
  $added = $this->feed2wp($wpdb, $feed);
1641
  if (isset($added['new'])) : $delta['new'] += $added['new']; endif;
1642
  if (isset($added['updated'])) : $delta['updated'] += $added['updated']; endif;
1643
  endif;
1644
  endforeach;
1645
 
1646
- do_action('feedwordpress_update_complete', array($delta));
1647
  fwp_release_pings(); // Now that we're done, send the one ping
1648
 
1649
  return $delta;
@@ -1905,7 +1946,7 @@ class FeedWordPress {
1905
  $channel = $rss->channel;
1906
 
1907
  $post = array();
1908
-
1909
  // This is ugly as all hell. I'd like to use apply_filters()'s
1910
  // alleged support for a variable argument count, but this seems
1911
  // to have been broken in WordPress 1.5. It'll be fixed somehow
@@ -2051,7 +2092,7 @@ class FeedWordPress {
2051
  elseif (isset($item['pubdate'])): // RSS 2.0
2052
  $post['epoch']['issued'] = strtotime($item['pubdate']);
2053
  else:
2054
- $post['epoch']['issued'] = time();
2055
  endif;
2056
 
2057
  # And again, for the created date
@@ -2067,13 +2108,15 @@ class FeedWordPress {
2067
  $post['epoch']['modified'] = @parse_w3cdtf($item['modified']);
2068
  elseif (isset($item['updated'])): // Atom 1.0
2069
  $post['epoch']['modified'] = @parse_w3cdtf($item['updated']);
2070
- else : // Fall back to issued / dc:date
2071
  $post['epoch']['modified'] = $post['epoch']['issued'];
 
 
2072
  endif;
2073
 
2074
- $post['post_date'] = date('Y-m-d H:i:s', $post['epoch']['issued']);
2075
  $post['post_modified'] = date('Y-m-d H:i:s', $post['epoch']['modified']);
2076
- $post['post_date_gmt'] = gmdate('Y-m-d H:i:s', $post['epoch']['issued']);
2077
  $post['post_modified_gmt'] = gmdate('Y-m-d H:i:s', $post['epoch']['modified']);
2078
 
2079
  # Use feed-level preferences or the global default.
@@ -2119,7 +2162,9 @@ class FeedWordPress {
2119
  if ($fc) : $post['named']['preset/category'] = explode("\n", $fc);
2120
  else : $post['named']['preset/category'] = array();
2121
  endif;
2122
- $post['named']['preset/category'] = array_merge($post['named']['preset/category'], $f['cats']);
 
 
2123
 
2124
  // Now add categories from the post, if we have 'em
2125
  $post['named']['category'] = array();
@@ -2573,55 +2618,151 @@ class FeedWordPress {
2573
  return $ret;
2574
  } // function FeedWordPress::on_unfamiliar()
2575
 
2576
- function link_category_id () {
 
 
 
 
 
 
 
 
 
 
2577
  global $wpdb;
2578
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2579
  $cat_id = get_settings('feedwordpress_cat_id');
2580
 
2581
  // If we don't yet *have* the category, we'll have to create it
2582
  if ($cat_id === false) {
2583
  $cat = $wpdb->escape(DEFAULT_SYNDICATION_CATEGORY);
2584
-
2585
- // Look for something with the right name...
2586
- $cat_id = $wpdb->get_var("
2587
- SELECT cat_id FROM $wpdb->linkcategories
2588
- WHERE cat_name='$cat'
2589
- ");
2590
 
 
 
 
2591
  // If you still can't find anything, make it for yourself.
2592
  if (!$cat_id) {
2593
- $result = $wpdb->query("
2594
- INSERT INTO $wpdb->linkcategories
2595
- SET
2596
- cat_id = 0,
2597
- cat_name='$cat',
2598
- show_images='N',
2599
- show_description='N',
2600
- show_rating='N',
2601
- show_updated='N',
2602
- sort_order='name'
2603
- ");
2604
- $cat_id = $wpdb->insert_id;
2605
  }
2606
-
2607
  update_option('feedwordpress_cat_id', $cat_id);
2608
  }
2609
  return $cat_id;
2610
  }
2611
 
2612
  function link_category () {
2613
- global $wpdb;
2614
 
2615
  $cat_id = FeedWordPress::link_category_id();
2616
 
2617
  // Get the ID# for the category name...
2618
- $cat_name = $wpdb->get_var("
2619
- SELECT cat_name FROM $wpdb->linkcategories
2620
- WHERE cat_id='$cat_id'
2621
- ");
 
 
 
 
 
 
 
2622
  return $cat_name;
2623
  }
2624
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2625
  function upgrade_database ($from = NULL) {
2626
  global $wpdb;
2627
 
@@ -2629,7 +2770,7 @@ class FeedWordPress {
2629
 
2630
  switch ($from) :
2631
  case 0.96: // account for changes to syndication custom values and guid
2632
- echo "<p>Upgrading database from {$from} to 0.97+...</p>\n";
2633
 
2634
  $cat_id = FeedWordPress::link_category_id();
2635
 
@@ -2801,7 +2942,7 @@ class FeedFinder {
2801
  $client->rawheaders['Connection'] = 'close';
2802
  $client->accept = 'application/atom+xml application/rdf+xml application/rss+xml application/xml text/html */*';
2803
  $client->agent = 'feedfinder/1.2 (compatible; PHP FeedFinder) +http://projects.radgeek.com/feedwordpress';
2804
- $client->read_timeout = 5;
2805
 
2806
  // Fetch the HTML or feed
2807
  @$client->fetch($this->uri);
@@ -2896,7 +3037,7 @@ class Relative_URI
2896
  $parts = Relative_URI::_parse_url($url);
2897
 
2898
  $uri_parts['fragment'] = (isset($parts['fragment']) ? $parts['fragment'] : null);
2899
- $uri_parts['query'] = $parts['query'];
2900
 
2901
  // if path is empty, and scheme, host, and query are undefined,
2902
  // the URL is referring the base URL
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:
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]/');
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
 
41
 
42
+ if (is_readable(ABSPATH . WPINC . '/rss.php')) :
43
+ require_once (ABSPATH . WPINC . '/rss.php');
44
+ 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?
54
  if (isset($wp_version) and $wp_version >= 1.5):
55
 
56
+ if (!FeedWordPress::needs_upgrade()) : // only work if the conditions are safe!
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
 
58
  # Syndicated items are generally received in output-ready (X)HTML and
59
  # should not be folded, crumpled, mutilated, or spindled by WordPress
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
  ################################################################################
136
  : implode(' and ', $mesg)));
137
  }
138
 
139
+ ################################################################################
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) {
146
+ global $user_level;
147
+
148
+ $can = false;
149
+
150
+ // This is **not** a full replacement for current_user_can. It
151
+ // is only for checking the capabilities we care about via the
152
+ // WordPress 1.5 user levels.
153
+ switch ($task) {
154
+ case 'manage_options':
155
+ $can = ($user_level >= 6);
156
+ break;
157
+ case 'manage_links':
158
+ $can = ($user_level >= 5);
159
+ break;
160
+ } /* switch */
161
+ return $can;
162
+ }
163
+ } else {
164
+ $legacy_capability_hack = false;
165
+ }
166
+
167
  ################################################################################
168
  ## TEMPLATE API: functions to make your templates syndication-aware ############
169
  ################################################################################
277
  <h2>Upgrade FeedWordPress</h2>
278
 
279
  <p>It appears that you have installed FeedWordPress
280
+ <?php echo FEEDWORDPRESS_VERSION; ?> as an upgrade to an existing installation of
281
  FeedWordPress. That's no problem, but you will need to take a minute out first
282
+ to upgrade your database: some necessary changes in how the software keeps
283
  track of posts and feeds will cause problems such as duplicate posts and broken
284
  templates if we were to continue without the upgrade.</p>
285
 
303
  ################################################################################
304
 
305
  function fwp_add_pages () {
306
+ global $legacy_capability_hack;
307
+
308
+ if ($legacy_capability_hack) :
309
+ // old & busted: numeric user levels
310
+ $manage_links = 5;
311
+ $manage_options = 6;
312
+ else :
313
+ // new hotness: named capabilities
314
+ $manage_links = 'manage_links';
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;
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']);
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);
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'";
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>
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">
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
+
550
+ if (isset($wp_db_version) and $wp_db_version >= 3308) : // WordPress 2.x
551
+ ?>
552
+ <div id="poststuff">
553
+
554
+ <div id="moremeta">
555
+ <div id="grabit" class="dbx-group">
556
+ <fieldset id="categorydiv" class="dbx-box">
557
+ <h3 class="dbx-handle"><?php _e('Categories') ?></h3>
558
+ <div class="dbx-content">
559
+ <p style="font-size:smaller;font-style:bold;margin:0">Place <?php print $object; ?> under...</p>
560
+ <p id="jaxcat"></p>
561
+ <ul id="categorychecklist"><?php write_nested_categories($checked); ?></ul></div>
562
+ </fieldset>
563
+ </div>
564
+ </div>
565
+ </div>
566
+ <?php
567
+ else : // WordPress 1.5
568
+ ?>
569
+ <fieldset id="categorydiv" style="width: 20%; margin-right: 2em">
570
+ <legend><?php _e('Categories') ?></legend>
571
+ <p style="font-size:smaller;font-style:bold;margin:0">Place <?php print $object; ?> under...</p>
572
+ <div style="height: 20em"><?php write_nested_categories($checked); ?></div>
573
+ </fieldset>
574
+ <?php
575
+ endif;
576
+ }
577
+
578
  function fwp_syndication_manage_page () {
579
+ global $wpdb;
580
 
581
+ if (FeedWordPress::needs_upgrade()) :
582
  fwp_upgrade_page();
583
  return;
584
  endif;
596
  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>
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;
625
 
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
  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';
650
  feed assigned</strong></p></td>
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>";
660
  endforeach;
661
  else:
679
  }
680
 
681
  function fwp_feedfinder_page () {
682
+ global $wpdb;
683
 
684
  $lookup = (isset($_REQUEST['lookup'])?$_REQUEST['lookup']:NULL);
685
 
698
  endif;
699
  ?>
700
  <div class="wrap">
701
+ <h2>Feed Finder: <?php echo $name; ?></h2>
702
+ <?php
703
+ $f =& new FeedFinder($lookup);
704
  $feeds = $f->find();
705
  if (count($feeds) > 0):
706
  foreach ($feeds as $key => $f):
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>
728
  <?php $item = $rss->items[0]; ?>
729
  <h3>Sample Item</h3>
730
  <ul>
731
+ <li><strong>Title:</strong> <a href="<?php echo $item['link']; ?>"><?php echo $item['title']; ?></a></li>
732
+ <li><strong>Date:</strong> <?php echo isset($item['date_timestamp']) ? date('d-M-y g:i:s a', $item['date_timestamp']) : 'unknown'; ?></li>
733
  </ul>
734
  <div class="entry">
735
+ <?php echo (isset($item['content']['encoded'])?$item['content']['encoded']:$item['description']); ?>
736
  </div>
737
  <?php else: ?>
738
  <h3>No Items</h3>
743
  <div>
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
  ?>
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>
769
  <input type="text" name="lookup" value="URI" />
770
+ <input type="hidden" name="link_id" value="<?php echo $link_id; ?>" />
771
  <input type="hidden" name="action" value="feedfinder" /></div>
772
  <div class="submit"><input type="submit" value="Use this feed &raquo;" /></div>
773
  </div>
777
  }
778
 
779
  function fwp_switchfeed_page () {
780
+ global $wpdb, $wp_db_version;
781
 
782
  check_admin_referer();
783
  if (!isset($_REQUEST['Cancel'])):
784
+ if (!current_user_can('manage_links')):
785
  die (__("Cheatin' uh ?"));
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
  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;
817
  }
818
 
819
  function fwp_linkedit_page () {
820
+ global $wpdb;
821
 
822
  check_admin_referer(); // Make sure we arrived here from the Dashboard
823
 
837
  'link/.*',
838
  );
839
 
840
+ if (!current_user_can('manage_links')) :
841
  die (__("Cheatin' uh ?"));
842
  elseif (isset($_REQUEST['feedfinder'])) :
843
  return fwp_feedfinder_page(); // re-route to Feed Finder 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" />
1046
  <input type="hidden" name="save" value="link" />
1047
 
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>
1060
  <th scope="row" width="20%"><?php _e('Link Name:') ?></th>
1061
  <td width="60%"><input type="text" id="basics-name-edit" name="name"
1062
  value="<?php echo $link_name; ?>" style="width: 95%" />
1063
+ <span id="basics-name-view"><strong><?php echo $link_name; ?></strong></span>
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>
1073
  <th scope="row" width="20%"><?php _e('Short description:') ?></th>
1074
  <td width="60%">
1075
  <input id="basics-description-edit" type="text" name="description" value="<?php echo $link_description; ?>" style="width: 95%" />
1076
+ <span id="basics-description-view"><strong><?php echo $link_description; ?></strong></span>
1077
  </td>
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>
1086
  <th width="20%" scope="row"><?php _e('Homepage:') ?></th>
1087
  <td width="60%">
1088
  <input id="basics-url-edit" type="text" name="linkurl" value="<?php echo $link_url; ?>" style="width: 95%;" />
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>
1107
  $holdem = (isset($meta['update/hold']) ? $meta['update/hold'] : 'scheduled');
1108
  ?>
1109
  <select name="update_schedule">
1110
+ <option value="scheduled"<?php echo ($holdem=='scheduled')?' selected="selected"':''; ?>>update on schedule <?php
1111
  echo " (";
1112
  if (isset($meta['update/ttl']) and is_numeric($meta['update/ttl'])) :
1113
  if (isset($meta['update/timed']) and $meta['update/timed']=='automatically') :
1125
  endif;
1126
  echo ")";
1127
  ?></option>
1128
+ <option value="next"<?php echo ($holdem=='next')?' selected="selected"':''; ?>>update ASAP</option>
1129
+ <option value="ping"<?php echo ($holdem=='ping')?' selected="selected"':''; ?>>update only when pinged</option>
1130
  </select></tr>
1131
  </table>
1132
  </fieldset>
1144
  <fieldset>
1145
  <legend>Syndicated Posts</legend>
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"
1158
+ <?php echo $status['post']['private']; ?> /> Hold posts from this feed as private posts</label></li>
1159
  <li><label><input type="radio" name="feed_post_status" value="draft"
1160
+ <?php echo $status['post']['draft']; ?> /> Hold posts from this feed as drafts</label></li>
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"
1172
+ <?php echo $status['comment']['closed']; ?> /> Don't allow comments on syndicated posts from this feed</label></li>
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"
1184
+ <?php echo $status['ping']['closed']; ?> /> Don't accept pings on syndicated posts from this feed</label></li>
1185
  </ul></td>
1186
  </tr>
1187
  </table>
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>
1204
+ <li><label><input type="radio" name="unfamiliar_author" value="filter"<?php echo $unfamiliar['author']['filter']; ?> /> don't syndicate the post</label></li>
1205
  </ul></td>
1206
  </tr>
1207
 
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>
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>
1246
  </select></td>
1251
  endforeach;
1252
  ?>
1253
  <tr>
1254
+ <th scope="row"><input type="text" size="10" name="notes[<?php echo $i; ?>][key1]" value="" /></th>
1255
+ <td><textarea name="notes[<?php echo $i; ?>][value]" rows="2" cols="40"></textarea></td>
1256
+ <td><em>add new setting...</em><input type="hidden" name="notes[<?php echo $i; ?>][action]" value="update" /></td>
1257
  </tr>
1258
  </table>
1259
  </fieldset>
1269
  }
1270
 
1271
  function fwp_multidelete_page () {
1272
+ global $wpdb;
1273
 
1274
  check_admin_referer(); // Make sure the referers are kosher
1275
 
1276
  $link_ids = (isset($_REQUEST['link_ids']) ? $_REQUEST['link_ids'] : array());
1277
  if (isset($_REQUEST['link_id'])) : array_push($link_ids, $_REQUEST['link_id']); endif;
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) :
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" />
1373
  $meta = FeedWordPress::notes_to_settings($link->link_notes);
1374
  ?>
1375
  <fieldset>
1376
+ <legend><?php echo $link_name; ?></legend>
1377
  <table class="editform" width="100%" cellspacing="2" cellpadding="5">
1378
  <tr><th scope="row" width="20%"><?php _e('Feed URI:') ?></th>
1379
+ <td width="80%"><a href="<?php echo $link_rss; ?>"><?php echo $link_rss; ?></a></td></tr>
1380
  <tr><th scope="row" width="20%"><?php _e('Short description:') ?></th>
1381
+ <td width="80%"><?php echo $link_description; ?></span></td></tr>
1382
  <tr><th width="20%" scope="row"><?php _e('Homepage:') ?></th>
1383
+ <td width="80%"><a href="<?php echo $link_url; ?>"><?php echo $link_url; ?></a></td></tr>
1384
  <tr style="vertical-align:top"><th width="20%" scope="row">Subscription <?php _e('Options') ?>:</th>
1385
  <td width="80%"><ul style="margin:0; padding: 0; list-style: none">
1386
+ <li><input type="radio" id="hide-<?php echo $link->link_id; ?>"
1387
+ name="link_action[<?php echo $link->link_id; ?>]" value="hide" />
1388
+ <label for="hide-<?php echo $link->link_id; ?>">Turn off the subscription for this
1389
  syndicated link<br/><span style="font-size:smaller">(Keep the feed information
1390
  and all the posts from this feed in the database, but don't syndicate any
1391
  new posts from the feed.)</span></label></li>
1392
+ <li><input type="radio" id="nuke-<?php echo $link->link_id; ?>"
1393
+ name="link_action[<?php echo $link->link_id; ?>]" value="nuke" />
1394
+ <label for="nuke-<?php echo $link->link_id; ?>">Delete this syndicated link and all the
1395
  posts that were syndicated from it</label></li>
1396
+ <li><input type="radio" id="delete-<?php echo $link->link_id; ?>"
1397
+ name="link_action[<?php echo $link->link_id; ?>]" value="delete" />
1398
+ <label for="delete-<?php echo $link->link_id; ?>">Delete this syndicated link, but
1399
  <em>keep</em> posts that were syndicated from it (as if they were authored
1400
  locally).</label></li>
1401
+ <li><input type="radio" id="nothing-<?php echo $link->link_id; ?>"
1402
+ name="link_action[<?php echo $link->link_id; ?>]" value="nothing" />
1403
+ <label for="nothing-<?php echo $link->link_id; ?>">Keep this feed as it is. I changed
1404
  my mind.</label></li>
1405
  </ul>
1406
  </table>
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):
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
 
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;
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
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
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.
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();
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
 
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
 
2942
  $client->rawheaders['Connection'] = 'close';
2943
  $client->accept = 'application/atom+xml application/rdf+xml application/rss+xml application/xml text/html */*';
2944
  $client->agent = 'feedfinder/1.2 (compatible; PHP FeedFinder) +http://projects.radgeek.com/feedwordpress';
2945
+ $client->read_timeout = 25;
2946
 
2947
  // Fetch the HTML or feed
2948
  @$client->fetch($this->uri);
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