Version Description
Download this release
Release Info
Developer | radgeek |
Plugin | FeedWordPress |
Version | 0.8 |
Comparing to | |
See all releases |
Version 0.8
- OPTIONAL/wp-includes/rss-functions.php +1203 -0
- README.text +248 -0
- wp-content/plugins/feedwordpress.php +1492 -0
- wp-content/update-feeds.php +110 -0
OPTIONAL/wp-includes/rss-functions.php
ADDED
@@ -0,0 +1,1203 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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.7wp
|
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 a modification of 0.7. In addition to improved handling of character
|
15 |
+
* encoding and other updates, this branch of MagpieRSS 0.7 also supports
|
16 |
+
* multiple categorization of posts (using <dc:subject> or <category>). The
|
17 |
+
* file is, therefore, derived from four sources: (1) Kellan's MagpieRSS 0.51,
|
18 |
+
* (2) the WordPress development team's modifications to MagpieRSS 0.51,
|
19 |
+
* (3) Kellan's MagpieRSS 0.7, and (4) Charles Johnson's modifications to
|
20 |
+
* MagpieRSS 0.7. All possible because of the GPL. Yay for free software!
|
21 |
+
*
|
22 |
+
* Differences from the main branch of MagpieRSS:
|
23 |
+
*
|
24 |
+
* 1. Everything in rss_parse.inc, rss_fetch.inc, rss_cache.inc, and
|
25 |
+
* rss_utils.inc is included in one file.
|
26 |
+
*
|
27 |
+
* 2. MagpieRSS returns the WordPress version as the user agent, rather than
|
28 |
+
* Magpie
|
29 |
+
*
|
30 |
+
* 3. class RSSCache is a modified version by WordPress developers, which
|
31 |
+
* caches feeds in the WordPress database (in the options table), rather
|
32 |
+
* than writing external files directly.
|
33 |
+
*
|
34 |
+
* 4. There are two WordPress-specific functions, get_rss() and wp_rss()
|
35 |
+
*
|
36 |
+
* 5. New cases added to MagpieRSS::feed_start_element(),
|
37 |
+
* MagpieRSS::feed_end_element(), and MagpieRSS::normalize() to handle
|
38 |
+
* multiple categories correctly.
|
39 |
+
*/
|
40 |
+
|
41 |
+
define('RSS', 'RSS');
|
42 |
+
define('ATOM', 'Atom');
|
43 |
+
define('MAGPIE_USER_AGENT', 'WordPress/' . $wp_version);
|
44 |
+
|
45 |
+
# UPDATED: rss_parse.inc: class MagpieRSS, function map_attrs
|
46 |
+
# --- cut here ---
|
47 |
+
/**
|
48 |
+
* Hybrid parser, and object, takes RSS as a string and returns a simple object.
|
49 |
+
*
|
50 |
+
* see: rss_fetch.inc for a simpler interface with integrated caching support
|
51 |
+
*
|
52 |
+
*/
|
53 |
+
class MagpieRSS {
|
54 |
+
var $parser;
|
55 |
+
|
56 |
+
var $current_item = array(); // item currently being parsed
|
57 |
+
var $items = array(); // collection of parsed items
|
58 |
+
var $channel = array(); // hash of channel fields
|
59 |
+
var $textinput = array();
|
60 |
+
var $image = array();
|
61 |
+
var $feed_type;
|
62 |
+
var $feed_version;
|
63 |
+
var $encoding = ''; // output encoding of parsed rss
|
64 |
+
|
65 |
+
var $_source_encoding = ''; // only set if we have to parse xml prolog
|
66 |
+
|
67 |
+
var $ERROR = "";
|
68 |
+
var $WARNING = "";
|
69 |
+
|
70 |
+
// define some constants
|
71 |
+
|
72 |
+
var $_CONTENT_CONSTRUCTS = array('content', 'summary', 'info', 'title', 'tagline', 'copyright');
|
73 |
+
var $_KNOWN_ENCODINGS = array('UTF-8', 'US-ASCII', 'ISO-8859-1');
|
74 |
+
|
75 |
+
// parser variables, useless if you're not a parser, treat as private
|
76 |
+
var $stack = array(); // parser stack
|
77 |
+
var $inchannel = false;
|
78 |
+
var $initem = false;
|
79 |
+
var $incontent = false; // if in Atom <content mode="xml"> field
|
80 |
+
var $intextinput = false;
|
81 |
+
var $inimage = false;
|
82 |
+
var $current_namespace = false;
|
83 |
+
|
84 |
+
var $incategory = false;
|
85 |
+
var $current_category = 0;
|
86 |
+
|
87 |
+
/**
|
88 |
+
* Set up XML parser, parse source, and return populated RSS object..
|
89 |
+
*
|
90 |
+
* @param string $source string containing the RSS to be parsed
|
91 |
+
*
|
92 |
+
* NOTE: Probably a good idea to leave the encoding options alone unless
|
93 |
+
* you know what you're doing as PHP's character set support is
|
94 |
+
* a little weird.
|
95 |
+
*
|
96 |
+
* NOTE: A lot of this is unnecessary but harmless with PHP5
|
97 |
+
*
|
98 |
+
*
|
99 |
+
* @param string $output_encoding output the parsed RSS in this character
|
100 |
+
* set defaults to ISO-8859-1 as this is PHP's
|
101 |
+
* default.
|
102 |
+
*
|
103 |
+
* NOTE: might be changed to UTF-8 in future
|
104 |
+
* versions.
|
105 |
+
*
|
106 |
+
* @param string $input_encoding the character set of the incoming RSS source.
|
107 |
+
* Leave blank and Magpie will try to figure it
|
108 |
+
* out.
|
109 |
+
*
|
110 |
+
*
|
111 |
+
* @param bool $detect_encoding if false Magpie won't attempt to detect
|
112 |
+
* source encoding. (caveat emptor)
|
113 |
+
*
|
114 |
+
*/
|
115 |
+
function MagpieRSS ($source, $output_encoding='ISO-8859-1',
|
116 |
+
$input_encoding=null, $detect_encoding=true)
|
117 |
+
{
|
118 |
+
# if PHP xml isn't compiled in, die
|
119 |
+
#
|
120 |
+
if (!function_exists('xml_parser_create')) {
|
121 |
+
$this->error( "Failed to load PHP's XML Extension. " .
|
122 |
+
"http://www.php.net/manual/en/ref.xml.php",
|
123 |
+
E_USER_ERROR );
|
124 |
+
}
|
125 |
+
|
126 |
+
list($parser, $source) = $this->create_parser($source,
|
127 |
+
$output_encoding, $input_encoding, $detect_encoding);
|
128 |
+
|
129 |
+
|
130 |
+
if (!is_resource($parser)) {
|
131 |
+
$this->error( "Failed to create an instance of PHP's XML parser. " .
|
132 |
+
"http://www.php.net/manual/en/ref.xml.php",
|
133 |
+
E_USER_ERROR );
|
134 |
+
}
|
135 |
+
|
136 |
+
|
137 |
+
$this->parser = $parser;
|
138 |
+
|
139 |
+
# pass in parser, and a reference to this object
|
140 |
+
# setup handlers
|
141 |
+
#
|
142 |
+
xml_set_object( $this->parser, $this );
|
143 |
+
xml_set_element_handler($this->parser,
|
144 |
+
'feed_start_element', 'feed_end_element' );
|
145 |
+
|
146 |
+
xml_set_character_data_handler( $this->parser, 'feed_cdata' );
|
147 |
+
|
148 |
+
$status = xml_parse( $this->parser, $source );
|
149 |
+
|
150 |
+
if (! $status ) {
|
151 |
+
$errorcode = xml_get_error_code( $this->parser );
|
152 |
+
if ( $errorcode != XML_ERROR_NONE ) {
|
153 |
+
$xml_error = xml_error_string( $errorcode );
|
154 |
+
$error_line = xml_get_current_line_number($this->parser);
|
155 |
+
$error_col = xml_get_current_column_number($this->parser);
|
156 |
+
$errormsg = "$xml_error at line $error_line, column $error_col";
|
157 |
+
|
158 |
+
$this->error( $errormsg );
|
159 |
+
}
|
160 |
+
}
|
161 |
+
|
162 |
+
xml_parser_free( $this->parser );
|
163 |
+
|
164 |
+
$this->normalize();
|
165 |
+
}
|
166 |
+
|
167 |
+
function feed_start_element($p, $element, &$attrs) {
|
168 |
+
$el = $element = strtolower($element);
|
169 |
+
$attrs = array_change_key_case($attrs, CASE_LOWER);
|
170 |
+
|
171 |
+
// check for a namespace, and split if found
|
172 |
+
$ns = false;
|
173 |
+
if ( strpos( $element, ':' ) ) {
|
174 |
+
list($ns, $el) = split( ':', $element, 2);
|
175 |
+
}
|
176 |
+
if ( $ns and $ns != 'rdf' ) {
|
177 |
+
$this->current_namespace = $ns;
|
178 |
+
}
|
179 |
+
|
180 |
+
# if feed type isn't set, then this is first element of feed
|
181 |
+
# identify feed from root element
|
182 |
+
#
|
183 |
+
if (!isset($this->feed_type) ) {
|
184 |
+
if ( $el == 'rdf' ) {
|
185 |
+
$this->feed_type = RSS;
|
186 |
+
$this->feed_version = '1.0';
|
187 |
+
}
|
188 |
+
elseif ( $el == 'rss' ) {
|
189 |
+
$this->feed_type = RSS;
|
190 |
+
$this->feed_version = $attrs['version'];
|
191 |
+
}
|
192 |
+
elseif ( $el == 'feed' ) {
|
193 |
+
$this->feed_type = ATOM;
|
194 |
+
$this->feed_version = $attrs['version'];
|
195 |
+
$this->inchannel = true;
|
196 |
+
}
|
197 |
+
return;
|
198 |
+
}
|
199 |
+
|
200 |
+
if ( $el == 'channel' )
|
201 |
+
{
|
202 |
+
$this->inchannel = true;
|
203 |
+
}
|
204 |
+
elseif ($el == 'item' or $el == 'entry' )
|
205 |
+
{
|
206 |
+
$this->initem = true;
|
207 |
+
if ( isset($attrs['rdf:about']) ) {
|
208 |
+
$this->current_item['about'] = $attrs['rdf:about'];
|
209 |
+
}
|
210 |
+
}
|
211 |
+
|
212 |
+
elseif ($this->initem and ($el == 'category' or ($this->current_namespace == 'dc' and $el == 'subject'))) {
|
213 |
+
$this->incategory = true;
|
214 |
+
array_unshift( $this->stack, $el );
|
215 |
+
}
|
216 |
+
|
217 |
+
// if we're in the default namespace of an RSS feed,
|
218 |
+
// record textinput or image fields
|
219 |
+
elseif (
|
220 |
+
$this->feed_type == RSS and
|
221 |
+
$this->current_namespace == '' and
|
222 |
+
$el == 'textinput' )
|
223 |
+
{
|
224 |
+
$this->intextinput = true;
|
225 |
+
}
|
226 |
+
|
227 |
+
elseif (
|
228 |
+
$this->feed_type == RSS and
|
229 |
+
$this->current_namespace == '' and
|
230 |
+
$el == 'image' )
|
231 |
+
{
|
232 |
+
$this->inimage = true;
|
233 |
+
}
|
234 |
+
|
235 |
+
# handle atom content constructs
|
236 |
+
elseif ( $this->feed_type == ATOM and in_array($el, $this->_CONTENT_CONSTRUCTS) )
|
237 |
+
{
|
238 |
+
// avoid clashing w/ RSS mod_content
|
239 |
+
if ($el == 'content' ) {
|
240 |
+
$el = 'atom_content';
|
241 |
+
}
|
242 |
+
|
243 |
+
$this->incontent = $el;
|
244 |
+
|
245 |
+
|
246 |
+
}
|
247 |
+
|
248 |
+
// if inside an Atom content construct (e.g. content or summary) field treat tags as text
|
249 |
+
elseif ($this->feed_type == ATOM and $this->incontent )
|
250 |
+
{
|
251 |
+
// if tags are inlined, then flatten
|
252 |
+
$attrs_str = join(' ',
|
253 |
+
array_map('map_attrs',
|
254 |
+
array_keys($attrs),
|
255 |
+
array_values($attrs) ) );
|
256 |
+
|
257 |
+
$this->append_content( "<$element $attrs_str>" );
|
258 |
+
|
259 |
+
array_unshift( $this->stack, $el );
|
260 |
+
}
|
261 |
+
|
262 |
+
// Atom support many links per containging element.
|
263 |
+
// Magpie treats link elements of type rel='alternate'
|
264 |
+
// as being equivalent to RSS's simple link element.
|
265 |
+
//
|
266 |
+
elseif ($this->feed_type == ATOM and $el == 'link' )
|
267 |
+
{
|
268 |
+
if ( isset($attrs['rel']) and $attrs['rel'] == 'alternate' )
|
269 |
+
{
|
270 |
+
$link_el = 'link';
|
271 |
+
}
|
272 |
+
else {
|
273 |
+
$link_el = 'link_' . $attrs['rel'];
|
274 |
+
}
|
275 |
+
|
276 |
+
$this->append($link_el, $attrs['href']);
|
277 |
+
}
|
278 |
+
|
279 |
+
// set stack[0] to current element
|
280 |
+
else {
|
281 |
+
array_unshift($this->stack, $el);
|
282 |
+
}
|
283 |
+
}
|
284 |
+
|
285 |
+
|
286 |
+
|
287 |
+
function feed_cdata ($p, $text) {
|
288 |
+
if ($this->feed_type == ATOM and $this->incontent)
|
289 |
+
{
|
290 |
+
$this->append_content( $text );
|
291 |
+
}
|
292 |
+
else {
|
293 |
+
$current_el = join('_', array_reverse($this->stack));
|
294 |
+
$this->append($current_el, $text);
|
295 |
+
}
|
296 |
+
}
|
297 |
+
|
298 |
+
function feed_end_element ($p, $el) {
|
299 |
+
$el = strtolower($el);
|
300 |
+
|
301 |
+
if ( $el == 'item' or $el == 'entry' )
|
302 |
+
{
|
303 |
+
$this->items[] = $this->current_item;
|
304 |
+
$this->current_item = array();
|
305 |
+
$this->initem = false;
|
306 |
+
|
307 |
+
$this->current_category = 0;
|
308 |
+
}
|
309 |
+
elseif ($this->initem and ($el == 'category' or $el == 'dc:subject')) {
|
310 |
+
$this->incategory = false;
|
311 |
+
$this->current_category = $this->current_category + 1;
|
312 |
+
array_shift( $this->stack );
|
313 |
+
}
|
314 |
+
elseif ($this->feed_type == RSS and $this->current_namespace == '' and $el == 'textinput' )
|
315 |
+
{
|
316 |
+
$this->intextinput = false;
|
317 |
+
}
|
318 |
+
elseif ($this->feed_type == RSS and $this->current_namespace == '' and $el == 'image' )
|
319 |
+
{
|
320 |
+
$this->inimage = false;
|
321 |
+
}
|
322 |
+
elseif ($this->feed_type == ATOM and in_array($el, $this->_CONTENT_CONSTRUCTS) )
|
323 |
+
{
|
324 |
+
$this->incontent = false;
|
325 |
+
}
|
326 |
+
elseif ($el == 'channel' or $el == 'feed' )
|
327 |
+
{
|
328 |
+
$this->inchannel = false;
|
329 |
+
}
|
330 |
+
elseif ($this->feed_type == ATOM and $this->incontent ) {
|
331 |
+
// balance tags properly
|
332 |
+
// note: i don't think this is actually neccessary
|
333 |
+
if ( $this->stack[0] == $el )
|
334 |
+
{
|
335 |
+
$this->append_content("</$el>");
|
336 |
+
}
|
337 |
+
else {
|
338 |
+
$this->append_content("<$el />");
|
339 |
+
}
|
340 |
+
|
341 |
+
array_shift( $this->stack );
|
342 |
+
}
|
343 |
+
else {
|
344 |
+
array_shift( $this->stack );
|
345 |
+
}
|
346 |
+
|
347 |
+
$this->current_namespace = false;
|
348 |
+
}
|
349 |
+
|
350 |
+
function concat (&$str1, $str2="") {
|
351 |
+
if (!isset($str1) ) {
|
352 |
+
$str1="";
|
353 |
+
}
|
354 |
+
$str1 .= $str2;
|
355 |
+
}
|
356 |
+
|
357 |
+
|
358 |
+
|
359 |
+
function append_content($text) {
|
360 |
+
if ( $this->initem ) {
|
361 |
+
$this->concat( $this->current_item[ $this->incontent ], $text );
|
362 |
+
}
|
363 |
+
elseif ( $this->inchannel ) {
|
364 |
+
$this->concat( $this->channel[ $this->incontent ], $text );
|
365 |
+
}
|
366 |
+
}
|
367 |
+
|
368 |
+
// smart append - field and namespace aware
|
369 |
+
function append($el, $text) {
|
370 |
+
if (!$el) {
|
371 |
+
return;
|
372 |
+
}
|
373 |
+
if ( $this->current_namespace )
|
374 |
+
{
|
375 |
+
if ( $this->incategory ) {
|
376 |
+
$this->concat( $this->current_item['categories'][$this->current_category], $text );
|
377 |
+
}
|
378 |
+
elseif ( $this->initem ) {
|
379 |
+
$this->concat(
|
380 |
+
$this->current_item[ $this->current_namespace ][ $el ], $text );
|
381 |
+
}
|
382 |
+
elseif ($this->inchannel) {
|
383 |
+
$this->concat(
|
384 |
+
$this->channel[ $this->current_namespace][ $el ], $text );
|
385 |
+
}
|
386 |
+
elseif ($this->intextinput) {
|
387 |
+
$this->concat(
|
388 |
+
$this->textinput[ $this->current_namespace][ $el ], $text );
|
389 |
+
}
|
390 |
+
elseif ($this->inimage) {
|
391 |
+
$this->concat(
|
392 |
+
$this->image[ $this->current_namespace ][ $el ], $text );
|
393 |
+
}
|
394 |
+
}
|
395 |
+
else {
|
396 |
+
if ( $this->incategory ) {
|
397 |
+
$this->concat( $this->current_item['categories'][$this->current_category], $text );
|
398 |
+
}
|
399 |
+
elseif ( $this->initem ) {
|
400 |
+
$this->concat(
|
401 |
+
$this->current_item[ $el ], $text);
|
402 |
+
}
|
403 |
+
elseif ($this->intextinput) {
|
404 |
+
$this->concat(
|
405 |
+
$this->textinput[ $el ], $text );
|
406 |
+
}
|
407 |
+
elseif ($this->inimage) {
|
408 |
+
$this->concat(
|
409 |
+
$this->image[ $el ], $text );
|
410 |
+
}
|
411 |
+
elseif ($this->inchannel) {
|
412 |
+
$this->concat(
|
413 |
+
$this->channel[ $el ], $text );
|
414 |
+
}
|
415 |
+
|
416 |
+
}
|
417 |
+
}
|
418 |
+
|
419 |
+
function normalize () {
|
420 |
+
// if atom populate rss fields
|
421 |
+
if ( $this->is_atom() ) {
|
422 |
+
$this->channel['description'] = $this->channel['tagline'];
|
423 |
+
for ( $i = 0; $i < count($this->items); $i++) {
|
424 |
+
$item = $this->items[$i];
|
425 |
+
if ( isset($item['summary']) )
|
426 |
+
$item['description'] = $item['summary'];
|
427 |
+
if ( isset($item['atom_content']))
|
428 |
+
$item['content']['encoded'] = $item['atom_content'];
|
429 |
+
|
430 |
+
$atom_date = (isset($item['issued']) ) ? $item['issued'] : $item['modified'];
|
431 |
+
if ( $atom_date ) {
|
432 |
+
$epoch = @parse_w3cdtf($item['modified']);
|
433 |
+
if ($epoch and $epoch > 0) {
|
434 |
+
$item['date_timestamp'] = $epoch;
|
435 |
+
}
|
436 |
+
}
|
437 |
+
|
438 |
+
if ( is_array($item['categories']) ) {
|
439 |
+
$item['category'] = $item['categories'][0];
|
440 |
+
$item['dc']['subjects'] = $item['categories'];
|
441 |
+
$item['dc']['subject'] = $item['category'];
|
442 |
+
}
|
443 |
+
|
444 |
+
$this->items[$i] = $item;
|
445 |
+
}
|
446 |
+
}
|
447 |
+
elseif ( $this->is_rss() ) {
|
448 |
+
$this->channel['tagline'] = $this->channel['description'];
|
449 |
+
for ( $i = 0; $i < count($this->items); $i++) {
|
450 |
+
$item = $this->items[$i];
|
451 |
+
if ( isset($item['description']))
|
452 |
+
$item['summary'] = $item['description'];
|
453 |
+
if ( isset($item['content']['encoded'] ) )
|
454 |
+
$item['atom_content'] = $item['content']['encoded'];
|
455 |
+
|
456 |
+
if ( $this->is_rss() == '1.0' and isset($item['dc']['date']) ) {
|
457 |
+
$epoch = @parse_w3cdtf($item['dc']['date']);
|
458 |
+
if ($epoch and $epoch > 0) {
|
459 |
+
$item['date_timestamp'] = $epoch;
|
460 |
+
}
|
461 |
+
}
|
462 |
+
elseif ( isset($item['pubdate']) ) {
|
463 |
+
$epoch = @strtotime($item['pubdate']);
|
464 |
+
if ($epoch > 0) {
|
465 |
+
$item['date_timestamp'] = $epoch;
|
466 |
+
}
|
467 |
+
}
|
468 |
+
|
469 |
+
if ( is_array($item['categories']) ) {
|
470 |
+
$item['category'] = $item['categories'][0];
|
471 |
+
$item['dc']['subjects'] = $item['categories'];
|
472 |
+
$item['dc']['subject'] = $item['category'];
|
473 |
+
}
|
474 |
+
|
475 |
+
$this->items[$i] = $item;
|
476 |
+
}
|
477 |
+
}
|
478 |
+
}
|
479 |
+
|
480 |
+
|
481 |
+
function is_rss () {
|
482 |
+
if ( $this->feed_type == RSS ) {
|
483 |
+
return $this->feed_version;
|
484 |
+
}
|
485 |
+
else {
|
486 |
+
return false;
|
487 |
+
}
|
488 |
+
}
|
489 |
+
|
490 |
+
function is_atom() {
|
491 |
+
if ( $this->feed_type == ATOM ) {
|
492 |
+
return $this->feed_version;
|
493 |
+
}
|
494 |
+
else {
|
495 |
+
return false;
|
496 |
+
}
|
497 |
+
}
|
498 |
+
|
499 |
+
/**
|
500 |
+
* return XML parser, and possibly re-encoded source
|
501 |
+
*
|
502 |
+
*/
|
503 |
+
function create_parser($source, $out_enc, $in_enc, $detect) {
|
504 |
+
if ( substr(phpversion(),0,1) == 5) {
|
505 |
+
$parser = $this->php5_create_parser($in_enc, $detect);
|
506 |
+
}
|
507 |
+
else {
|
508 |
+
list($parser, $source) = $this->php4_create_parser($source, $in_enc, $detect);
|
509 |
+
}
|
510 |
+
if ($out_enc) {
|
511 |
+
$this->encoding = $out_enc;
|
512 |
+
xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $out_enc);
|
513 |
+
}
|
514 |
+
|
515 |
+
return array($parser, $source);
|
516 |
+
}
|
517 |
+
|
518 |
+
/**
|
519 |
+
* Instantiate an XML parser under PHP5
|
520 |
+
*
|
521 |
+
* PHP5 will do a fine job of detecting input encoding
|
522 |
+
* if passed an empty string as the encoding.
|
523 |
+
*
|
524 |
+
* All hail libxml2!
|
525 |
+
*
|
526 |
+
*/
|
527 |
+
function php5_create_parser($in_enc, $detect) {
|
528 |
+
// by default php5 does a fine job of detecting input encodings
|
529 |
+
if(!$detect && $in_enc) {
|
530 |
+
return xml_parser_create($in_enc);
|
531 |
+
}
|
532 |
+
else {
|
533 |
+
return xml_parser_create('');
|
534 |
+
}
|
535 |
+
}
|
536 |
+
|
537 |
+
/**
|
538 |
+
* Instaniate an XML parser under PHP4
|
539 |
+
*
|
540 |
+
* Unfortunately PHP4's support for character encodings
|
541 |
+
* and especially XML and character encodings sucks. As
|
542 |
+
* long as the documents you parse only contain characters
|
543 |
+
* from the ISO-8859-1 character set (a superset of ASCII,
|
544 |
+
* and a subset of UTF-8) you're fine. However once you
|
545 |
+
* step out of that comfy little world things get mad, bad,
|
546 |
+
* and dangerous to know.
|
547 |
+
*
|
548 |
+
* The following code is based on SJM's work with FoF
|
549 |
+
* @see http://minutillo.com/steve/weblog/2004/6/17/php-xml-and-character-encodings-a-tale-of-sadness-rage-and-data-loss
|
550 |
+
*
|
551 |
+
*/
|
552 |
+
function php4_create_parser($source, $in_enc, $detect) {
|
553 |
+
if ( !$detect ) {
|
554 |
+
return array(xml_parser_create($in_enc), $source);
|
555 |
+
}
|
556 |
+
|
557 |
+
if (!$in_enc) {
|
558 |
+
if (preg_match('/<?xml.*encoding=[\'"](.*?)[\'"].*?>/m', $source, $m)) {
|
559 |
+
$in_enc = strtoupper($m[1]);
|
560 |
+
$this->source_encoding = $in_enc;
|
561 |
+
}
|
562 |
+
else {
|
563 |
+
$in_enc = 'UTF-8';
|
564 |
+
}
|
565 |
+
}
|
566 |
+
|
567 |
+
if ($this->known_encoding($in_enc)) {
|
568 |
+
return array(xml_parser_create($in_enc), $source);
|
569 |
+
}
|
570 |
+
|
571 |
+
// the dectected encoding is not one of the simple encodings PHP knows
|
572 |
+
|
573 |
+
// attempt to use the iconv extension to
|
574 |
+
// cast the XML to a known encoding
|
575 |
+
// @see http://php.net/iconv
|
576 |
+
|
577 |
+
if (function_exists('iconv')) {
|
578 |
+
$encoded_source = iconv($in_enc,'UTF-8', $source);
|
579 |
+
if ($encoded_source) {
|
580 |
+
return array(xml_parser_create('UTF-8'), $encoded_source);
|
581 |
+
}
|
582 |
+
}
|
583 |
+
|
584 |
+
// iconv didn't work, try mb_convert_encoding
|
585 |
+
// @see http://php.net/mbstring
|
586 |
+
if(function_exists('mb_convert_encoding')) {
|
587 |
+
$encoded_source = mb_convert_encoding($source, 'UTF-8', $in_enc );
|
588 |
+
if ($encoded_source) {
|
589 |
+
return array(xml_parser_create('UTF-8'), $encoded_source);
|
590 |
+
}
|
591 |
+
}
|
592 |
+
|
593 |
+
// else
|
594 |
+
$this->error("Feed is in an unsupported character encoding. ($in_enc) " .
|
595 |
+
"You may see strange artifacts, and mangled characters.",
|
596 |
+
E_USER_NOTICE);
|
597 |
+
|
598 |
+
return array(xml_parser_create(), $source);
|
599 |
+
}
|
600 |
+
|
601 |
+
function known_encoding($enc) {
|
602 |
+
$enc = strtoupper($enc);
|
603 |
+
if ( in_array($enc, $this->_KNOWN_ENCODINGS) ) {
|
604 |
+
return $enc;
|
605 |
+
}
|
606 |
+
else {
|
607 |
+
return false;
|
608 |
+
}
|
609 |
+
}
|
610 |
+
|
611 |
+
function error ($errormsg, $lvl=E_USER_WARNING) {
|
612 |
+
// append PHP's error message if track_errors enabled
|
613 |
+
if ( $php_errormsg ) {
|
614 |
+
$errormsg .= " ($php_errormsg)";
|
615 |
+
}
|
616 |
+
if ( MAGPIE_DEBUG ) {
|
617 |
+
trigger_error( $errormsg, $lvl);
|
618 |
+
}
|
619 |
+
else {
|
620 |
+
error_log( $errormsg, 0);
|
621 |
+
}
|
622 |
+
|
623 |
+
$notices = E_USER_NOTICE|E_NOTICE;
|
624 |
+
if ( $lvl&$notices ) {
|
625 |
+
$this->WARNING = $errormsg;
|
626 |
+
} else {
|
627 |
+
$this->ERROR = $errormsg;
|
628 |
+
}
|
629 |
+
}
|
630 |
+
} // end class RSS
|
631 |
+
|
632 |
+
function map_attrs($k, $v) {
|
633 |
+
return "$k=\"$v\"";
|
634 |
+
}
|
635 |
+
# ---- cut here ----
|
636 |
+
|
637 |
+
require_once( dirname(__FILE__) . '/class-snoopy.php');
|
638 |
+
|
639 |
+
# -- UPDATED from rss_fetch.inc: fetch_rss, error, debug, magpie_error
|
640 |
+
# --- cut here ---
|
641 |
+
function fetch_rss ($url) {
|
642 |
+
// initialize constants
|
643 |
+
init();
|
644 |
+
|
645 |
+
if ( !isset($url) ) {
|
646 |
+
error("fetch_rss called without a url");
|
647 |
+
return false;
|
648 |
+
}
|
649 |
+
|
650 |
+
// if cache is disabled
|
651 |
+
if ( !MAGPIE_CACHE_ON ) {
|
652 |
+
// fetch file, and parse it
|
653 |
+
$resp = _fetch_remote_file( $url );
|
654 |
+
if ( is_success( $resp->status ) ) {
|
655 |
+
return _response_to_rss( $resp );
|
656 |
+
}
|
657 |
+
else {
|
658 |
+
error("Failed to fetch $url and cache is off");
|
659 |
+
return false;
|
660 |
+
}
|
661 |
+
}
|
662 |
+
// else cache is ON
|
663 |
+
else {
|
664 |
+
// Flow
|
665 |
+
// 1. check cache
|
666 |
+
// 2. if there is a hit, make sure its fresh
|
667 |
+
// 3. if cached obj fails freshness check, fetch remote
|
668 |
+
// 4. if remote fails, return stale object, or error
|
669 |
+
|
670 |
+
$cache = new RSSCache( MAGPIE_CACHE_DIR, MAGPIE_CACHE_AGE );
|
671 |
+
|
672 |
+
if (MAGPIE_DEBUG and $cache->ERROR) {
|
673 |
+
debug($cache->ERROR, E_USER_WARNING);
|
674 |
+
}
|
675 |
+
|
676 |
+
|
677 |
+
$cache_status = 0; // response of check_cache
|
678 |
+
$request_headers = array(); // HTTP headers to send with fetch
|
679 |
+
$rss = 0; // parsed RSS object
|
680 |
+
$errormsg = 0; // errors, if any
|
681 |
+
|
682 |
+
// store parsed XML by desired output encoding
|
683 |
+
// as character munging happens at parse time
|
684 |
+
$cache_key = $url . MAGPIE_OUTPUT_ENCODING;
|
685 |
+
|
686 |
+
if (!$cache->ERROR) {
|
687 |
+
// return cache HIT, MISS, or STALE
|
688 |
+
$cache_status = $cache->check_cache( $cache_key);
|
689 |
+
}
|
690 |
+
|
691 |
+
// if object cached, and cache is fresh, return cached obj
|
692 |
+
if ( $cache_status == 'HIT' ) {
|
693 |
+
$rss = $cache->get( $cache_key );
|
694 |
+
if ( isset($rss) and $rss ) {
|
695 |
+
// should be cache age
|
696 |
+
$rss->from_cache = 1;
|
697 |
+
if ( MAGPIE_DEBUG > 1) {
|
698 |
+
debug("MagpieRSS: Cache HIT", E_USER_NOTICE);
|
699 |
+
}
|
700 |
+
return $rss;
|
701 |
+
}
|
702 |
+
}
|
703 |
+
|
704 |
+
// else attempt a conditional get
|
705 |
+
|
706 |
+
// setup headers
|
707 |
+
if ( $cache_status == 'STALE' ) {
|
708 |
+
$rss = $cache->get( $cache_key );
|
709 |
+
if ( $rss and $rss->etag and $rss->last_modified ) {
|
710 |
+
$request_headers['If-None-Match'] = $rss->etag;
|
711 |
+
$request_headers['If-Last-Modified'] = $rss->last_modified;
|
712 |
+
}
|
713 |
+
}
|
714 |
+
|
715 |
+
$resp = _fetch_remote_file( $url, $request_headers );
|
716 |
+
|
717 |
+
if (isset($resp) and $resp) {
|
718 |
+
if ($resp->status == '304' ) {
|
719 |
+
// we have the most current copy
|
720 |
+
if ( MAGPIE_DEBUG > 1) {
|
721 |
+
debug("Got 304 for $url");
|
722 |
+
}
|
723 |
+
// reset cache on 304 (at minutillo insistent prodding)
|
724 |
+
$cache->set($cache_key, $rss);
|
725 |
+
return $rss;
|
726 |
+
}
|
727 |
+
elseif ( is_success( $resp->status ) ) {
|
728 |
+
$rss = _response_to_rss( $resp );
|
729 |
+
if ( $rss ) {
|
730 |
+
if (MAGPIE_DEBUG > 1) {
|
731 |
+
debug("Fetch successful");
|
732 |
+
}
|
733 |
+
// add object to cache
|
734 |
+
$cache->set( $cache_key, $rss );
|
735 |
+
return $rss;
|
736 |
+
}
|
737 |
+
}
|
738 |
+
else {
|
739 |
+
$errormsg = "Failed to fetch $url ";
|
740 |
+
if ( $resp->status == '-100' ) {
|
741 |
+
$errormsg .= "(Request timed out after " . MAGPIE_FETCH_TIME_OUT . " seconds)";
|
742 |
+
}
|
743 |
+
elseif ( $resp->error ) {
|
744 |
+
# compensate for Snoopy's annoying habbit to tacking
|
745 |
+
# on '\n'
|
746 |
+
$http_error = substr($resp->error, 0, -2);
|
747 |
+
$errormsg .= "(HTTP Error: $http_error)";
|
748 |
+
}
|
749 |
+
else {
|
750 |
+
$errormsg .= "(HTTP Response: " . $resp->response_code .')';
|
751 |
+
}
|
752 |
+
}
|
753 |
+
}
|
754 |
+
else {
|
755 |
+
$errormsg = "Unable to retrieve RSS file for unknown reasons.";
|
756 |
+
}
|
757 |
+
|
758 |
+
// else fetch failed
|
759 |
+
|
760 |
+
// attempt to return cached object
|
761 |
+
if ($rss) {
|
762 |
+
if ( MAGPIE_DEBUG ) {
|
763 |
+
debug("Returning STALE object for $url");
|
764 |
+
}
|
765 |
+
return $rss;
|
766 |
+
}
|
767 |
+
|
768 |
+
// else we totally failed
|
769 |
+
error( $errormsg );
|
770 |
+
|
771 |
+
return false;
|
772 |
+
|
773 |
+
} // end if ( !MAGPIE_CACHE_ON ) {
|
774 |
+
} // end fetch_rss()
|
775 |
+
|
776 |
+
/*=======================================================================*\
|
777 |
+
Function: error
|
778 |
+
Purpose: set MAGPIE_ERROR, and trigger error
|
779 |
+
\*=======================================================================*/
|
780 |
+
|
781 |
+
function error ($errormsg, $lvl=E_USER_WARNING) {
|
782 |
+
global $MAGPIE_ERROR;
|
783 |
+
|
784 |
+
// append PHP's error message if track_errors enabled
|
785 |
+
if ( isset($php_errormsg) ) {
|
786 |
+
$errormsg .= " ($php_errormsg)";
|
787 |
+
}
|
788 |
+
if ( $errormsg ) {
|
789 |
+
$errormsg = "MagpieRSS: $errormsg";
|
790 |
+
$MAGPIE_ERROR = $errormsg;
|
791 |
+
trigger_error( $errormsg, $lvl);
|
792 |
+
}
|
793 |
+
}
|
794 |
+
|
795 |
+
function debug ($debugmsg, $lvl=E_USER_NOTICE) {
|
796 |
+
trigger_error("MagpieRSS [debug] $debugmsg", $lvl);
|
797 |
+
}
|
798 |
+
|
799 |
+
/*=======================================================================*\
|
800 |
+
Function: magpie_error
|
801 |
+
Purpose: accessor for the magpie error variable
|
802 |
+
\*=======================================================================*/
|
803 |
+
function magpie_error ($errormsg="") {
|
804 |
+
global $MAGPIE_ERROR;
|
805 |
+
|
806 |
+
if ( isset($errormsg) and $errormsg ) {
|
807 |
+
$MAGPIE_ERROR = $errormsg;
|
808 |
+
}
|
809 |
+
|
810 |
+
return $MAGPIE_ERROR;
|
811 |
+
}
|
812 |
+
# --- cut here ---
|
813 |
+
|
814 |
+
# UPDATED FROM: rss_fetch.inc: _fetch_remote_file, _response_to_rss, init
|
815 |
+
# --- cut here ---
|
816 |
+
/*=======================================================================*\
|
817 |
+
Function: _fetch_remote_file
|
818 |
+
Purpose: retrieve an arbitrary remote file
|
819 |
+
Input: url of the remote file
|
820 |
+
headers to send along with the request (optional)
|
821 |
+
Output: an HTTP response object (see Snoopy.class.inc)
|
822 |
+
\*=======================================================================*/
|
823 |
+
function _fetch_remote_file ($url, $headers = "" ) {
|
824 |
+
// Snoopy is an HTTP client in PHP
|
825 |
+
$client = new Snoopy();
|
826 |
+
$client->agent = MAGPIE_USER_AGENT;
|
827 |
+
$client->read_timeout = MAGPIE_FETCH_TIME_OUT;
|
828 |
+
$client->use_gzip = MAGPIE_USE_GZIP;
|
829 |
+
if (is_array($headers) ) {
|
830 |
+
$client->rawheaders = $headers;
|
831 |
+
}
|
832 |
+
|
833 |
+
@$client->fetch($url);
|
834 |
+
return $client;
|
835 |
+
|
836 |
+
}
|
837 |
+
|
838 |
+
/*=======================================================================*\
|
839 |
+
Function: _response_to_rss
|
840 |
+
Purpose: parse an HTTP response object into an RSS object
|
841 |
+
Input: an HTTP response object (see Snoopy)
|
842 |
+
Output: parsed RSS object (see rss_parse)
|
843 |
+
\*=======================================================================*/
|
844 |
+
function _response_to_rss ($resp) {
|
845 |
+
$rss = new MagpieRSS( $resp->results, MAGPIE_OUTPUT_ENCODING, MAGPIE_INPUT_ENCODING, MAGPIE_DETECT_ENCODING );
|
846 |
+
|
847 |
+
// if RSS parsed successfully
|
848 |
+
if ( $rss and !$rss->ERROR) {
|
849 |
+
|
850 |
+
// find Etag, and Last-Modified
|
851 |
+
foreach($resp->headers as $h) {
|
852 |
+
// 2003-03-02 - Nicola Asuni (www.tecnick.com) - fixed bug "Undefined offset: 1"
|
853 |
+
if (strpos($h, ": ")) {
|
854 |
+
list($field, $val) = explode(": ", $h, 2);
|
855 |
+
}
|
856 |
+
else {
|
857 |
+
$field = $h;
|
858 |
+
$val = "";
|
859 |
+
}
|
860 |
+
|
861 |
+
if ( $field == 'ETag' ) {
|
862 |
+
$rss->etag = $val;
|
863 |
+
}
|
864 |
+
|
865 |
+
if ( $field == 'Last-Modified' ) {
|
866 |
+
$rss->last_modified = $val;
|
867 |
+
}
|
868 |
+
}
|
869 |
+
|
870 |
+
return $rss;
|
871 |
+
} // else construct error message
|
872 |
+
else {
|
873 |
+
$errormsg = "Failed to parse RSS file.";
|
874 |
+
|
875 |
+
if ($rss) {
|
876 |
+
$errormsg .= " (" . $rss->ERROR . ")";
|
877 |
+
}
|
878 |
+
error($errormsg);
|
879 |
+
|
880 |
+
return false;
|
881 |
+
} // end if ($rss and !$rss->error)
|
882 |
+
}
|
883 |
+
|
884 |
+
/*=======================================================================*\
|
885 |
+
Function: init
|
886 |
+
Purpose: setup constants with default values
|
887 |
+
check for user overrides
|
888 |
+
\*=======================================================================*/
|
889 |
+
function init () {
|
890 |
+
if ( defined('MAGPIE_INITALIZED') ) {
|
891 |
+
return;
|
892 |
+
}
|
893 |
+
else {
|
894 |
+
define('MAGPIE_INITALIZED', true);
|
895 |
+
}
|
896 |
+
|
897 |
+
if ( !defined('MAGPIE_CACHE_ON') ) {
|
898 |
+
define('MAGPIE_CACHE_ON', true);
|
899 |
+
}
|
900 |
+
|
901 |
+
if ( !defined('MAGPIE_CACHE_DIR') ) {
|
902 |
+
define('MAGPIE_CACHE_DIR', './cache');
|
903 |
+
}
|
904 |
+
|
905 |
+
if ( !defined('MAGPIE_CACHE_AGE') ) {
|
906 |
+
define('MAGPIE_CACHE_AGE', 60*60); // one hour
|
907 |
+
}
|
908 |
+
|
909 |
+
if ( !defined('MAGPIE_CACHE_FRESH_ONLY') ) {
|
910 |
+
define('MAGPIE_CACHE_FRESH_ONLY', false);
|
911 |
+
}
|
912 |
+
|
913 |
+
if ( !defined('MAGPIE_OUTPUT_ENCODING') ) {
|
914 |
+
define('MAGPIE_OUTPUT_ENCODING', 'ISO-8859-1');
|
915 |
+
}
|
916 |
+
|
917 |
+
if ( !defined('MAGPIE_INPUT_ENCODING') ) {
|
918 |
+
define('MAGPIE_INPUT_ENCODING', null);
|
919 |
+
}
|
920 |
+
|
921 |
+
if ( !defined('MAGPIE_DETECT_ENCODING') ) {
|
922 |
+
define('MAGPIE_DETECT_ENCODING', true);
|
923 |
+
}
|
924 |
+
|
925 |
+
if ( !defined('MAGPIE_DEBUG') ) {
|
926 |
+
define('MAGPIE_DEBUG', 0);
|
927 |
+
}
|
928 |
+
|
929 |
+
if ( !defined('MAGPIE_USER_AGENT') ) {
|
930 |
+
# WORDPRESS MODIFICATION: send WordPress as user-agent
|
931 |
+
# --- cut here ---
|
932 |
+
$ua = 'WordPress/'. $wp_version . ' (+http://www.wordpress.org';
|
933 |
+
# --- cut here ---
|
934 |
+
|
935 |
+
if ( MAGPIE_CACHE_ON ) {
|
936 |
+
$ua = $ua . ')';
|
937 |
+
}
|
938 |
+
else {
|
939 |
+
$ua = $ua . '; No cache)';
|
940 |
+
}
|
941 |
+
|
942 |
+
define('MAGPIE_USER_AGENT', $ua);
|
943 |
+
}
|
944 |
+
|
945 |
+
if ( !defined('MAGPIE_FETCH_TIME_OUT') ) {
|
946 |
+
define('MAGPIE_FETCH_TIME_OUT', 5); // 5 second timeout
|
947 |
+
}
|
948 |
+
|
949 |
+
// use gzip encoding to fetch rss files if supported?
|
950 |
+
if ( !defined('MAGPIE_USE_GZIP') ) {
|
951 |
+
define('MAGPIE_USE_GZIP', true);
|
952 |
+
}
|
953 |
+
}
|
954 |
+
# --- cut here ---
|
955 |
+
|
956 |
+
function is_info ($sc) {
|
957 |
+
return $sc >= 100 && $sc < 200;
|
958 |
+
}
|
959 |
+
|
960 |
+
function is_success ($sc) {
|
961 |
+
return $sc >= 200 && $sc < 300;
|
962 |
+
}
|
963 |
+
|
964 |
+
function is_redirect ($sc) {
|
965 |
+
return $sc >= 300 && $sc < 400;
|
966 |
+
}
|
967 |
+
|
968 |
+
function is_error ($sc) {
|
969 |
+
return $sc >= 400 && $sc < 600;
|
970 |
+
}
|
971 |
+
|
972 |
+
function is_client_error ($sc) {
|
973 |
+
return $sc >= 400 && $sc < 500;
|
974 |
+
}
|
975 |
+
|
976 |
+
function is_server_error ($sc) {
|
977 |
+
return $sc >= 500 && $sc < 600;
|
978 |
+
}
|
979 |
+
|
980 |
+
# WORDPRESS-SPECIFIC: class RSSCache (modified to use WP database)
|
981 |
+
# --- cut here ---
|
982 |
+
class RSSCache {
|
983 |
+
var $BASE_CACHE = 'wp-content/cache'; // where the cache files are stored
|
984 |
+
var $MAX_AGE = 43200; // when are files stale, default twelve hours
|
985 |
+
var $ERROR = ''; // accumulate error messages
|
986 |
+
|
987 |
+
function RSSCache ($base='', $age='') {
|
988 |
+
if ( $base ) {
|
989 |
+
$this->BASE_CACHE = $base;
|
990 |
+
}
|
991 |
+
if ( $age ) {
|
992 |
+
$this->MAX_AGE = $age;
|
993 |
+
}
|
994 |
+
|
995 |
+
}
|
996 |
+
|
997 |
+
/*=======================================================================*\
|
998 |
+
Function: set
|
999 |
+
Purpose: add an item to the cache, keyed on url
|
1000 |
+
Input: url from wich the rss file was fetched
|
1001 |
+
Output: true on sucess
|
1002 |
+
\*=======================================================================*/
|
1003 |
+
function set ($url, $rss) {
|
1004 |
+
global $wpdb;
|
1005 |
+
$cache_option = 'rss_' . $this->file_name( $url );
|
1006 |
+
$cache_timestamp = 'rss_' . $this->file_name( $url ) . '_ts';
|
1007 |
+
|
1008 |
+
if ( !$wpdb->get_var("SELECT option_name FROM $wpdb->options WHERE option_name = '$cache_option'") )
|
1009 |
+
add_option($cache_option, '', '', 'no');
|
1010 |
+
if ( !$wpdb->get_var("SELECT option_name FROM $wpdb->options WHERE option_name = '$cache_timestamp'") )
|
1011 |
+
add_option($cache_timestamp, '', '', 'no');
|
1012 |
+
|
1013 |
+
update_option($cache_option, $rss);
|
1014 |
+
update_option($cache_timestamp, time() );
|
1015 |
+
|
1016 |
+
return $cache_option;
|
1017 |
+
}
|
1018 |
+
|
1019 |
+
/*=======================================================================*\
|
1020 |
+
Function: get
|
1021 |
+
Purpose: fetch an item from the cache
|
1022 |
+
Input: url from wich the rss file was fetched
|
1023 |
+
Output: cached object on HIT, false on MISS
|
1024 |
+
\*=======================================================================*/
|
1025 |
+
function get ($url) {
|
1026 |
+
$this->ERROR = "";
|
1027 |
+
$cache_option = 'rss_' . $this->file_name( $url );
|
1028 |
+
|
1029 |
+
if ( ! get_option( $cache_option ) ) {
|
1030 |
+
$this->debug(
|
1031 |
+
"Cache doesn't contain: $url (cache option: $cache_option)"
|
1032 |
+
);
|
1033 |
+
return 0;
|
1034 |
+
}
|
1035 |
+
|
1036 |
+
$rss = get_option( $cache_option );
|
1037 |
+
|
1038 |
+
return $rss;
|
1039 |
+
}
|
1040 |
+
|
1041 |
+
/*=======================================================================*\
|
1042 |
+
Function: check_cache
|
1043 |
+
Purpose: check a url for membership in the cache
|
1044 |
+
and whether the object is older then MAX_AGE (ie. STALE)
|
1045 |
+
Input: url from wich the rss file was fetched
|
1046 |
+
Output: cached object on HIT, false on MISS
|
1047 |
+
\*=======================================================================*/
|
1048 |
+
function check_cache ( $url ) {
|
1049 |
+
$this->ERROR = "";
|
1050 |
+
$cache_option = $this->file_name( $url );
|
1051 |
+
$cache_timestamp = 'rss_' . $this->file_name( $url ) . '_ts';
|
1052 |
+
|
1053 |
+
if ( $mtime = get_option($cache_timestamp) ) {
|
1054 |
+
// find how long ago the file was added to the cache
|
1055 |
+
// and whether that is longer then MAX_AGE
|
1056 |
+
$age = time() - $mtime;
|
1057 |
+
if ( $this->MAX_AGE > $age ) {
|
1058 |
+
// object exists and is current
|
1059 |
+
return 'HIT';
|
1060 |
+
}
|
1061 |
+
else {
|
1062 |
+
// object exists but is old
|
1063 |
+
return 'STALE';
|
1064 |
+
}
|
1065 |
+
}
|
1066 |
+
else {
|
1067 |
+
// object does not exist
|
1068 |
+
return 'MISS';
|
1069 |
+
}
|
1070 |
+
}
|
1071 |
+
|
1072 |
+
/*=======================================================================*\
|
1073 |
+
Function: serialize
|
1074 |
+
\*=======================================================================*/
|
1075 |
+
function serialize ( $rss ) {
|
1076 |
+
return serialize( $rss );
|
1077 |
+
}
|
1078 |
+
|
1079 |
+
/*=======================================================================*\
|
1080 |
+
Function: unserialize
|
1081 |
+
\*=======================================================================*/
|
1082 |
+
function unserialize ( $data ) {
|
1083 |
+
return unserialize( $data );
|
1084 |
+
}
|
1085 |
+
|
1086 |
+
/*=======================================================================*\
|
1087 |
+
Function: file_name
|
1088 |
+
Purpose: map url to location in cache
|
1089 |
+
Input: url from wich the rss file was fetched
|
1090 |
+
Output: a file name
|
1091 |
+
\*=======================================================================*/
|
1092 |
+
function file_name ($url) {
|
1093 |
+
return md5( $url );
|
1094 |
+
}
|
1095 |
+
|
1096 |
+
/*=======================================================================*\
|
1097 |
+
Function: error
|
1098 |
+
Purpose: register error
|
1099 |
+
\*=======================================================================*/
|
1100 |
+
function error ($errormsg, $lvl=E_USER_WARNING) {
|
1101 |
+
// append PHP's error message if track_errors enabled
|
1102 |
+
if ( isset($php_errormsg) ) {
|
1103 |
+
$errormsg .= " ($php_errormsg)";
|
1104 |
+
}
|
1105 |
+
$this->ERROR = $errormsg;
|
1106 |
+
if ( MAGPIE_DEBUG ) {
|
1107 |
+
trigger_error( $errormsg, $lvl);
|
1108 |
+
}
|
1109 |
+
else {
|
1110 |
+
error_log( $errormsg, 0);
|
1111 |
+
}
|
1112 |
+
}
|
1113 |
+
function debug ($debugmsg, $lvl=E_USER_NOTICE) {
|
1114 |
+
if ( MAGPIE_DEBUG ) {
|
1115 |
+
$this->error("MagpieRSS [debug] $debugmsg", $lvl);
|
1116 |
+
}
|
1117 |
+
}
|
1118 |
+
}
|
1119 |
+
# --- cut here ---
|
1120 |
+
|
1121 |
+
function parse_w3cdtf ( $date_str ) {
|
1122 |
+
|
1123 |
+
# regex to match wc3dtf
|
1124 |
+
$pat = "/(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(:(\d{2}))?(?:([-+])(\d{2}):?(\d{2})|(Z))?/";
|
1125 |
+
|
1126 |
+
if ( preg_match( $pat, $date_str, $match ) ) {
|
1127 |
+
list( $year, $month, $day, $hours, $minutes, $seconds) =
|
1128 |
+
array( $match[1], $match[2], $match[3], $match[4], $match[5], $match[6]);
|
1129 |
+
|
1130 |
+
# calc epoch for current date assuming GMT
|
1131 |
+
$epoch = gmmktime( $hours, $minutes, $seconds, $month, $day, $year);
|
1132 |
+
|
1133 |
+
$offset = 0;
|
1134 |
+
if ( $match[10] == 'Z' ) {
|
1135 |
+
# zulu time, aka GMT
|
1136 |
+
}
|
1137 |
+
else {
|
1138 |
+
list( $tz_mod, $tz_hour, $tz_min ) =
|
1139 |
+
array( $match[8], $match[9], $match[10]);
|
1140 |
+
|
1141 |
+
# zero out the variables
|
1142 |
+
if ( ! $tz_hour ) { $tz_hour = 0; }
|
1143 |
+
if ( ! $tz_min ) { $tz_min = 0; }
|
1144 |
+
|
1145 |
+
$offset_secs = (($tz_hour*60)+$tz_min)*60;
|
1146 |
+
|
1147 |
+
# is timezone ahead of GMT? then subtract offset
|
1148 |
+
#
|
1149 |
+
if ( $tz_mod == '+' ) {
|
1150 |
+
$offset_secs = $offset_secs * -1;
|
1151 |
+
}
|
1152 |
+
|
1153 |
+
$offset = $offset_secs;
|
1154 |
+
}
|
1155 |
+
$epoch = $epoch + $offset;
|
1156 |
+
return $epoch;
|
1157 |
+
}
|
1158 |
+
else {
|
1159 |
+
return -1;
|
1160 |
+
}
|
1161 |
+
}
|
1162 |
+
|
1163 |
+
# WORDPRESS-SPECIFIC: wp_rss (), get_rss ()
|
1164 |
+
# --- cut here ---
|
1165 |
+
function wp_rss ($url, $num) {
|
1166 |
+
//ini_set("display_errors", false); uncomment to suppress php errors thrown if the feed is not returned.
|
1167 |
+
$num_items = $num;
|
1168 |
+
$rss = fetch_rss($url);
|
1169 |
+
if ( $rss ) {
|
1170 |
+
echo "<ul>";
|
1171 |
+
$rss->items = array_slice($rss->items, 0, $num_items);
|
1172 |
+
foreach ($rss->items as $item ) {
|
1173 |
+
echo "<li>\n";
|
1174 |
+
echo "<a href='$item[link]' title='$item[description]'>";
|
1175 |
+
echo htmlentities($item['title']);
|
1176 |
+
echo "</a><br />\n";
|
1177 |
+
echo "</li>\n";
|
1178 |
+
}
|
1179 |
+
echo "</ul>";
|
1180 |
+
}
|
1181 |
+
else {
|
1182 |
+
echo "an error has occured the feed is probably down, try again later.";
|
1183 |
+
}
|
1184 |
+
}
|
1185 |
+
|
1186 |
+
function get_rss ($uri, $num = 5) { // Like get posts, but for RSS
|
1187 |
+
$rss = fetch_rss($url);
|
1188 |
+
if ( $rss ) {
|
1189 |
+
$rss->items = array_slice($rss->items, 0, $num_items);
|
1190 |
+
foreach ($rss->items as $item ) {
|
1191 |
+
echo "<li>\n";
|
1192 |
+
echo "<a href='$item[link]' title='$item[description]'>";
|
1193 |
+
echo htmlentities($item['title']);
|
1194 |
+
echo "</a><br />\n";
|
1195 |
+
echo "</li>\n";
|
1196 |
+
}
|
1197 |
+
return $posts;
|
1198 |
+
} else {
|
1199 |
+
return false;
|
1200 |
+
}
|
1201 |
+
}
|
1202 |
+
# --- cut here ---
|
1203 |
+
?>
|
README.text
ADDED
@@ -0,0 +1,248 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
FeedWordPress
|
2 |
+
=============
|
3 |
+
|
4 |
+
* Author: [Charles Johnson](http://www.radgeek.com/contact)
|
5 |
+
* Version: 0.8
|
6 |
+
* Project URI: <http://projects.radgeek.com/feedwordpress>
|
7 |
+
* License: GPL. See License below for copyright jots and tittles.
|
8 |
+
|
9 |
+
Introduction
|
10 |
+
------------
|
11 |
+
FeedWordPress is an Atom/RSS aggregator for WordPress. It syndicates content
|
12 |
+
from newsfeeds that you select; if you syndicate several newsfeeds then you can
|
13 |
+
WordPress's posts database and templating engine as the back-end of an
|
14 |
+
aggregation ("planet") website.
|
15 |
+
|
16 |
+
FeedWordPress is similar in conception to software such as [Planet][]--in fact,
|
17 |
+
I started developing it because I needed a more flexible replacement for
|
18 |
+
Planet at to run [Feminist Blogs](http://www.feministblogs.org/). Since it
|
19 |
+
works on top of WordPress's database and templating system, however, it boasts
|
20 |
+
far more flexibility than many other aggregators. It is also designed with ease
|
21 |
+
of configuration and use in mind.
|
22 |
+
|
23 |
+
You'll need a working installation of [WordPress 1.5][] and FTP or SFTP access
|
24 |
+
to your web host. The ability to create cron jobs on your web host would be very
|
25 |
+
helpful but it's not absolutely necessary. You *don't* need to tweak any
|
26 |
+
plain-text configuration files and you *don't* need shell access to your web
|
27 |
+
host to make it work. (Although, I should point out, web hosts that *don't*
|
28 |
+
offer shell access are *bad web hosts*.)
|
29 |
+
|
30 |
+
[WordPress 1.5]: http://wordpress.org/development/2005/02/strayhorn/
|
31 |
+
[Planet]: http://www.planetplanet.org/ "Planet Planet"
|
32 |
+
|
33 |
+
Installation & Requirements
|
34 |
+
---------------------------
|
35 |
+
You'll need a website with WordPress 1.5 installed and configured and FTP or
|
36 |
+
SFTP access to your web space. You'll probably also want to have either (1)
|
37 |
+
the ability to create cron jobs on your web host, or (2) a computer of your
|
38 |
+
own that has always-on Internet access.
|
39 |
+
|
40 |
+
1. Install `feedwordpress.php` in your WordPress `plugins` directory and
|
41 |
+
`update.php` in your WordPress `wp-content` directory.
|
42 |
+
|
43 |
+
2. (Optional) Upgrade the copy of MagpieRSS packaged with WordPress by
|
44 |
+
installing the new `rss-functions.php` (archived in
|
45 |
+
`OPTIONAL/wp-includes`) into your WordPress `wp-includes` directory.
|
46 |
+
|
47 |
+
3. Log in to the WordPress Dashboard and activate the FeedWordPress plugin.
|
48 |
+
Go to Options --> Syndication to set up initial settings for the
|
49 |
+
syndication link category ("Contributors") by default and the RPC secret
|
50 |
+
word (blank by default, but you should probably set it to something.)
|
51 |
+
|
52 |
+
4. Set up links for syndication from the WordPress Dashboard using
|
53 |
+
Links --> Syndicated or Links --> Import.
|
54 |
+
|
55 |
+
5. FeedWordPress is now *ready* to feed syndicated content into WordPress.
|
56 |
+
In order for it to actually receive that content, either (1) have your
|
57 |
+
contributors add WordPress's XML-RPC URI to their blog's list of URIs
|
58 |
+
to ping when they update posts, (2) set up a cron job to check in on all
|
59 |
+
the feeds on a regular basis, or (3) both. If you do (2), you can either
|
60 |
+
set up a job to run `php update-feeds.php` on your web host, or set one
|
61 |
+
up on any computer with always-on Internet access to request
|
62 |
+
`update-feeds.php` over the web.
|
63 |
+
|
64 |
+
If your copy of WordPress is installed at <http://www.zyx.com/blog>, and
|
65 |
+
you set the secret word for XML-RPC pings to "foo", then your XML-RPC
|
66 |
+
URI will be <http://www.zyx.com/blog/xmlrpc.php>, and the URI to request
|
67 |
+
for `update-feeds.php` to update all feeds will be
|
68 |
+
<http://www.zyx.com/blog/wp-content/update-feeds.php?shibboleth=foo>
|
69 |
+
|
70 |
+
For detailed installation instructions, point your web browser to
|
71 |
+
<http://projects.radgeek.com/feedwordpress/install>.
|
72 |
+
|
73 |
+
Feed Settings
|
74 |
+
-------------
|
75 |
+
### Feed Settings ###
|
76 |
+
|
77 |
+
Once you have your links configured and regular feed updates scheduled, you can
|
78 |
+
mostly leave FeedWordPress to run on its own. If you need to add, remove, or
|
79 |
+
change information for any contributors, you can do so from the WordPress
|
80 |
+
Dashboard under Links --> Syndicated. If you want to distribute the labor of
|
81 |
+
adding, updating, and managing feeds, you can use the WordPress login and access
|
82 |
+
privileges system. Users with an access level of 5 or greater can add or modify
|
83 |
+
syndicated links, and change syndication options.
|
84 |
+
|
85 |
+
All the information for a syndicated feed is managed through the WordPress Links
|
86 |
+
database. Feeds in the category to be syndicated (by default, "Contributors")
|
87 |
+
use several fields of the standard WordPress Link record:
|
88 |
+
|
89 |
+
- The Link URI is used to store a URI to the front page (*not* the feed!)
|
90 |
+
of the syndicated website.
|
91 |
+
|
92 |
+
- The Link Name is used to store the title of the syndicated website.
|
93 |
+
|
94 |
+
- The Short Description is used to store the tagline of the syndicated
|
95 |
+
website.
|
96 |
+
|
97 |
+
- The RSS URI is used to store the URI for the feed to be syndicated.
|
98 |
+
|
99 |
+
- The Link Notes are used to store a collection of manually-encoded and
|
100 |
+
automatically-generated settings that apply to this feed. The format of
|
101 |
+
settings in Link Notes is:
|
102 |
+
|
103 |
+
key1: value1
|
104 |
+
key2: value2
|
105 |
+
key3: value3
|
106 |
+
feed/key1: value1
|
107 |
+
feed/key2: value2
|
108 |
+
|
109 |
+
And so on. Values that are prefixed by 'feed/' are automatically
|
110 |
+
generated from feed data every time the feed syndicated by this link is
|
111 |
+
checked for updates. Values without the prefix are set manually by the
|
112 |
+
user.
|
113 |
+
|
114 |
+
Most settings in the Link Notes have no effect on FeedWordPress, but can be
|
115 |
+
accessed from templates using the ``get_feed_meta()`` template function in a
|
116 |
+
post context. For example, many aggregator sites use a "face" image for each
|
117 |
+
feed to visually distinguish posts from different feeds. To implement a face
|
118 |
+
feature, you could add a line like this to each feed's Link Notes section:
|
119 |
+
|
120 |
+
face: http://www.zyx.com/mugs/ugly
|
121 |
+
|
122 |
+
The URI should be changed out for each feed to point to the appropriate image,
|
123 |
+
of course. Then, to use the setting from within a template:
|
124 |
+
|
125 |
+
// In a post context
|
126 |
+
<?php $img = get_feed_meta('face'); if strlen($img) > 0): ?>
|
127 |
+
<img src="<?=$img?>" alt="" />
|
128 |
+
<?php endif; ?>
|
129 |
+
|
130 |
+
... which will display the image, if any, whose URI is set in the "face" setting
|
131 |
+
for the feed that post comes from. If there is no "face" setting for a
|
132 |
+
particular feed, ``get_feed_meta()`` will return an empty string and no image
|
133 |
+
will be displayed.
|
134 |
+
|
135 |
+
Not all feed settings are only for templates. Some affect how FeedWordPress
|
136 |
+
processes posts from that feed. Currently, the special settings are:
|
137 |
+
|
138 |
+
- `cats:` a colon-separated list of default categories for any post coming
|
139 |
+
from this feed. So, for example, a this line in its Notes section:
|
140 |
+
|
141 |
+
cats: computers:web
|
142 |
+
|
143 |
+
... will make FeedWordPress place any posts syndicated from that feed in
|
144 |
+
the "computers" and "web" categories (*in addition to*, not *instead of*
|
145 |
+
any categories that are applied to the post in the feed)
|
146 |
+
|
147 |
+
- `hardcode name: (yes|no)`
|
148 |
+
|
149 |
+
A yes/no setting. By default, FeedWordPress updates the value of the
|
150 |
+
Link Name field automatically to reflect the title that is reported by a
|
151 |
+
syndicated feed. (So, for example, if one of your contributors changes
|
152 |
+
the title of her weblog, the change will be reflected on your
|
153 |
+
Contributors list after the next update.) To override that behavior for
|
154 |
+
a particular feed (e.g. to force WordPress to use an abbreviated form of
|
155 |
+
the site's title for reasons of space), add a line like this to the Link
|
156 |
+
Notes section:
|
157 |
+
|
158 |
+
hardcode name: yes
|
159 |
+
|
160 |
+
If `hardcode name` is absent, or set to a value other than `yes`,
|
161 |
+
FeedWordPress will take that as a 'no' and follow the default behavior.
|
162 |
+
|
163 |
+
- `post status:` sets the default post status for posts from this feed
|
164 |
+
This can be 'publish', 'draft', or 'private'. By default, it is set to
|
165 |
+
'publish' (syndicated posts go online immediately).
|
166 |
+
|
167 |
+
- `comment status:` sets the default status for comments on posts
|
168 |
+
syndicated from this feed. By default, all comments on syndicated posts
|
169 |
+
are closed, but you can set it to 'open', 'closed', or 'registered_only'
|
170 |
+
for particular feeds.
|
171 |
+
|
172 |
+
- `ping status:` sets the default status for receiving TrackBack and
|
173 |
+
PingBack pings on posts syndicated from this feed. By default,
|
174 |
+
syndicated posts are closed to pings, but you can set this to 'open' or
|
175 |
+
'closed' for particular feeds.
|
176 |
+
|
177 |
+
Template API
|
178 |
+
------------
|
179 |
+
When activated, FeedWordPress makes the following functions available for use by
|
180 |
+
themes/templates:
|
181 |
+
|
182 |
+
* ``is_syndicated()``: in a post context, returns ``TRUE`` if the post was
|
183 |
+
syndicated from another website, or ``FALSE`` if it was originally
|
184 |
+
posted here
|
185 |
+
|
186 |
+
* ``get_syndication_permalink()``: in a post context, returns the URI of
|
187 |
+
the permalink for this post *on the website it was syndicated from*
|
188 |
+
|
189 |
+
* ``the_syndication_permalink()``: in a post context, outputs the value
|
190 |
+
returned by ``get_syndication_permalink()``
|
191 |
+
|
192 |
+
* ``get_syndication_source_link()``: in a post context, returns the URI of
|
193 |
+
the front page (*not* the feed) of the website this post was syndicated
|
194 |
+
from
|
195 |
+
|
196 |
+
* ``the_syndication_source_link()``: in a post context, outputs the URI
|
197 |
+
returned by ``get_syndication_source_link()``
|
198 |
+
|
199 |
+
* ``get_syndication_source()``: in a post context, returns the
|
200 |
+
human-readable title of the website that a syndicated post was
|
201 |
+
syndicated from
|
202 |
+
|
203 |
+
* ``the_syndication_source()``: in a post context, outputs the value
|
204 |
+
returned by ``get_syndication_source()``
|
205 |
+
|
206 |
+
* ``get_syndication_feed():`` in a post context, returns the URI of the
|
207 |
+
feed (*not* the front page) that this post was syndicated from
|
208 |
+
|
209 |
+
* ``the_syndication_feed()``: in a post context, outputs the value
|
210 |
+
returned by ``get_syndication_feed()``
|
211 |
+
|
212 |
+
* ``get_feed_meta($key)``: in a post context, returns the value, if any,
|
213 |
+
of the feed setting ``$key`` for the feed that this post was syndicated
|
214 |
+
from
|
215 |
+
|
216 |
+
By default, FeedWordPress also places a filter on the standard functions
|
217 |
+
``get_permalink()`` and ``the_permalink()`` that substitutes the URI returned by
|
218 |
+
``get_syndication_permalink()`` for the URI generated by WordPress. This means
|
219 |
+
that by default the permalinks listed on your website and in your newsfeed will
|
220 |
+
link to the location of the posts on the source website, *not* to their location
|
221 |
+
on your website. You can switch this behavior on or off at Options -->
|
222 |
+
Syndication in the WordPress Dashboard.
|
223 |
+
|
224 |
+
License
|
225 |
+
-------
|
226 |
+
The FeedWordPress plugin is copyright (c) 2005 by Charles Johnson. It uses code
|
227 |
+
derived or translated from:
|
228 |
+
|
229 |
+
- [wp-rss-aggregate.php][] by [Kellan Elliot-McCrea](kellan@protest.net)
|
230 |
+
- [HTTP Navigator 2][] by [Keyvan Minoukadeh](keyvan@k1m.com)
|
231 |
+
- [Ultra-Liberal Feed Finder][] by [Mark Pilgrim](mark@diveintomark.org)
|
232 |
+
|
233 |
+
according to the terms of the [GNU General Public License][].
|
234 |
+
|
235 |
+
This program is free software; you can redistribute it and/or modify it under
|
236 |
+
the terms of the [GNU General Public License][] as published by the Free Software
|
237 |
+
Foundation; either version 2 of the License, or (at your option) any later
|
238 |
+
version.
|
239 |
+
|
240 |
+
This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
241 |
+
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
242 |
+
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
243 |
+
|
244 |
+
[wp-rss-aggregate.php]: http://laughingmeme.org/archives/002203.html
|
245 |
+
[HTTP Navigator 2]: http://www.keyvan.net/2004/11/16/http-navigator/
|
246 |
+
[Ultra-Liberal Feed Finder]: http://diveintomark.org/projects/feed_finder/
|
247 |
+
[GNU General Public License]: http://www.gnu.org/copyleft/gpl.html
|
248 |
+
|
wp-content/plugins/feedwordpress.php
ADDED
@@ -0,0 +1,1492 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/*
|
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.8
|
7 |
+
Author: Charles Johnson
|
8 |
+
Author URI: http://www.radgeek.com/
|
9 |
+
*/
|
10 |
+
|
11 |
+
# Author: Charles Johnson <technophilia@radgeek.com>
|
12 |
+
# License: GPL
|
13 |
+
# Last modified: 2005-03-21
|
14 |
+
#
|
15 |
+
# This uses code derived from:
|
16 |
+
# - wp-rss-aggregate.php by Kellan Elliot-McCrea <kellan@protest.net>
|
17 |
+
# - HTTP Navigator 2 by Keyvan Minoukadeh <keyvan@k1m.com>
|
18 |
+
# - Ultra-Liberal Feed Finder by Mark Pilgrim <mark@diveintomark.org>
|
19 |
+
# according to the terms of the GNU General Public License.
|
20 |
+
#
|
21 |
+
# INSTALLATION: see README.text or <http://projects.radgeek.com/install>
|
22 |
+
#
|
23 |
+
# USAGE: once FeedWordPress is installed, you manage just about everything from
|
24 |
+
# the WordPress Dashboard, under Options --> Syndication or Links --> Syndicated
|
25 |
+
# To ensure that fresh content is added as it becomes available, get your
|
26 |
+
# contributors to put your XML-RPC URI (if WordPress is installed at
|
27 |
+
# <http://www.zyx.com/blog>, XML-RPC requests should be sent to
|
28 |
+
# <http://www.zyx.com/blog/xmlrpc.php>), or see `update-feeds.php`
|
29 |
+
|
30 |
+
# -- Change these as you please
|
31 |
+
define ('FEEDWORDPRESS_LOG_UPDATES', true); // Make false if you hate status updates sent to error_log()
|
32 |
+
|
33 |
+
# -- Don't change these unless you know what you're doing...
|
34 |
+
define ('RPC_MAGIC', 'tag:radgeek.com/projects/feedwordpress/');
|
35 |
+
define ('FEEDWORDPRESS_VERSION', '0.8');
|
36 |
+
define ('DEFAULT_SYNDICATION_CATEGORY', 'Contributors');
|
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 |
+
// Is this being loaded from within WordPress?
|
44 |
+
if (!isset($wp_version)):
|
45 |
+
echo "FeedWordPress/".FEEDWORDPRESS_VERSION.": an Atom/RSS aggregator plugin for WordPress 1.5\n";
|
46 |
+
exit;
|
47 |
+
endif;
|
48 |
+
|
49 |
+
# Remove default WordPress auto-paragraph filter.
|
50 |
+
remove_filter('the_content', 'wpautop');
|
51 |
+
remove_filter('the_excerpt', 'wpautop');
|
52 |
+
remove_filter('comment_text', 'wpautop');
|
53 |
+
|
54 |
+
# What really should happen here is that we create our own ueber-filter,
|
55 |
+
# to run with the highest possible priority, which would intercept
|
56 |
+
# and check whether or not the post comes from off the wire, and
|
57 |
+
# pre-empty any further formatting filters if it does. Then we could
|
58 |
+
# leave wpautop in peace and not worry about Markdown, Textile, etc.
|
59 |
+
# Sadly, WordPress 1.5 gives you no way to pre-empt downstream filters
|
60 |
+
# (and no way for the furthest downstream filter to recover the original
|
61 |
+
# content, either.)
|
62 |
+
|
63 |
+
# add_filter('the_content', 'feedwordpress_preempt', 10);
|
64 |
+
# add_filter('the_excerpt', 'feedwordpress_preempt', 10);
|
65 |
+
# add_filter('comment_text', 'feedwordpress_preempt', 30);
|
66 |
+
|
67 |
+
add_filter('post_link', 'syndication_permalink', 1);
|
68 |
+
|
69 |
+
# Admin menu
|
70 |
+
add_action('admin_menu', 'fwp_add_pages');
|
71 |
+
|
72 |
+
# Inbound XML-RPC update methods
|
73 |
+
add_filter('xmlrpc_methods', 'feedwordpress_xmlrpc_hook');
|
74 |
+
|
75 |
+
# Outbound XML-RPC reform
|
76 |
+
remove_action('publish_post', 'generic_ping');
|
77 |
+
add_action('publish_post', 'fwp_catch_ping');
|
78 |
+
|
79 |
+
# -- Template functions for syndication sites
|
80 |
+
function is_syndicated () { return (strlen(get_syndication_feed()) > 0); }
|
81 |
+
|
82 |
+
function the_syndication_source_link () { echo get_syndication_source_link(); }
|
83 |
+
function get_syndication_source_link () { list($n) = get_post_custom_values('syndication_source_uri'); return $n; }
|
84 |
+
|
85 |
+
function get_syndication_source () { list($n) = get_post_custom_values('syndication_source'); return $n; }
|
86 |
+
function the_syndication_source () { echo get_syndication_source(); }
|
87 |
+
|
88 |
+
function get_syndication_feed () { list($u) = get_post_custom_values('syndication_feed'); return $u; }
|
89 |
+
function the_syndication_feed () { echo get_syndication_feed (); }
|
90 |
+
|
91 |
+
function get_feed_meta ($key) {
|
92 |
+
global $wpdb;
|
93 |
+
$feed = get_syndication_feed();
|
94 |
+
|
95 |
+
$ret = NULL;
|
96 |
+
if (strlen($feed) > 0):
|
97 |
+
$result = $wpdb->get_var("
|
98 |
+
SELECT link_notes FROM $wpdb->links
|
99 |
+
WHERE link_rss = '".$wpdb->escape($feed)."'"
|
100 |
+
);
|
101 |
+
|
102 |
+
$notes = explode("\n", $result);
|
103 |
+
foreach ($notes as $note):
|
104 |
+
list($k, $v) = explode(': ', $note, 2);
|
105 |
+
$meta[$k] = $v;
|
106 |
+
endforeach;
|
107 |
+
$ret = $meta[$key];
|
108 |
+
endif; /* if */
|
109 |
+
return $ret;
|
110 |
+
}
|
111 |
+
|
112 |
+
function get_syndication_permalink () {
|
113 |
+
list($u) = get_post_custom_values('syndication_permalink'); return $u;
|
114 |
+
}
|
115 |
+
function the_syndication_permalink () {
|
116 |
+
echo get_syndication_permalink();
|
117 |
+
}
|
118 |
+
|
119 |
+
# -- Filters for templates and feeds
|
120 |
+
function syndication_permalink ($permalink = '') {
|
121 |
+
if (get_settings('feedwordpress_munge_permalink') != 'no'):
|
122 |
+
$uri = get_syndication_permalink();
|
123 |
+
return ((strlen($uri) > 0) ? $uri : $permalink);
|
124 |
+
else:
|
125 |
+
return $permalink;
|
126 |
+
endif;
|
127 |
+
} // function syndication_permalink ()
|
128 |
+
|
129 |
+
# -- Admin menu add-ons
|
130 |
+
function fwp_add_pages () {
|
131 |
+
add_submenu_page('link-manager.php', 'Syndicated Sites', 'Syndicated', 5, __FILE__, 'fwp_syndication_manage_page');
|
132 |
+
add_options_page('Syndication', 'Syndication', 5, __FILE__, 'fwp_syndication_options_page');
|
133 |
+
} // function fwp_add_pages () */
|
134 |
+
|
135 |
+
function fwp_syndication_options_page () {
|
136 |
+
global $wpdb, $user_level;
|
137 |
+
|
138 |
+
$caption = 'Save Changes';
|
139 |
+
if (isset($_REQUEST['action']) and $_REQUEST['action']=$caption):
|
140 |
+
check_admin_referer();
|
141 |
+
|
142 |
+
if ($user_level < 5):
|
143 |
+
die (__("Cheatin' uh ?"));
|
144 |
+
else:
|
145 |
+
update_option('feedwordpress_rpc_secret', $_REQUEST['rpc_secret']);
|
146 |
+
update_option('feedwordpress_cat_id', $_REQUEST['syndication_category']);
|
147 |
+
update_option('feedwordpress_munge_permalink', $_REQUEST['munge_permalink']);
|
148 |
+
?>
|
149 |
+
<div class="updated">
|
150 |
+
<p><?php _e('Options saved.')?></p>
|
151 |
+
</div>
|
152 |
+
<?php
|
153 |
+
endif;
|
154 |
+
endif;
|
155 |
+
|
156 |
+
$cat_id = FeedWordPress::link_category_id();
|
157 |
+
$rpc_secret = FeedWordPress::rpc_secret();
|
158 |
+
$munge_permalink = get_settings('feedwordpress_munge_permalink');
|
159 |
+
$results = $wpdb->get_results("SELECT cat_id, cat_name, auto_toggle FROM $wpdb->linkcategories ORDER BY cat_id");
|
160 |
+
?>
|
161 |
+
<div class="wrap">
|
162 |
+
<h2>Syndication Options</h2>
|
163 |
+
<form action="" method="post">
|
164 |
+
<fieldset class="options">
|
165 |
+
<legend>Template Options</legend>
|
166 |
+
<table class="editform" width="100%" cellspacing="2" cellpadding="5">
|
167 |
+
<tr>
|
168 |
+
<th width="33%" scope="row">Permalinks for syndicated posts point to:</th>
|
169 |
+
<td width="67%"><select name="munge_permalink" size="1">
|
170 |
+
<option value="yes"<?=($munge_permalink=='yes')?' selected="selected"':''?>>source website</option>
|
171 |
+
<option value="no"<?=($munge_permalink=='no')?' selected="selected"':''?>>this website</option>
|
172 |
+
</select></td>
|
173 |
+
</tr>
|
174 |
+
</table>
|
175 |
+
<div class="submit"><input type="submit" name="action" value="<?=$caption?>" /></div>
|
176 |
+
</fieldset>
|
177 |
+
|
178 |
+
<fieldset class="options">
|
179 |
+
<legend>Syndication Options</legend>
|
180 |
+
<table class="editform" width="100%" cellspacing="2" cellpadding="5">
|
181 |
+
<tr>
|
182 |
+
<th width="33%" scope="row">Syndicate links in category:</th>
|
183 |
+
<td width="67%"><?php
|
184 |
+
echo "\n<select name=\"syndication_category\" size=\"1\">";
|
185 |
+
foreach ($results as $row) {
|
186 |
+
echo "\n\t<option value=\"$row->cat_id\"";
|
187 |
+
if ($row->cat_id == $cat_id)
|
188 |
+
echo " selected='selected'";
|
189 |
+
echo ">$row->cat_id: ".wp_specialchars($row->cat_name);
|
190 |
+
if ('Y' == $row->auto_toggle)
|
191 |
+
echo ' (auto toggle)';
|
192 |
+
echo "</option>\n";
|
193 |
+
}
|
194 |
+
echo "\n</select>\n";
|
195 |
+
?></td>
|
196 |
+
</tr>
|
197 |
+
</table>
|
198 |
+
<div class="submit"><input type="submit" name="action" value="<?=$caption?>" /></div>
|
199 |
+
</fieldset>
|
200 |
+
|
201 |
+
<fieldset class="options">
|
202 |
+
<legend>Back-end Options</legend>
|
203 |
+
<table class="editform" width="100%" cellspacing="2" cellpadding="5">
|
204 |
+
<tr>
|
205 |
+
<th width="33%" scope="row">XML-RPC update secret word:</th>
|
206 |
+
<td width="67%"><input id="rpc_secret" name="rpc_secret" value="<?=$rpc_secret?>" />
|
207 |
+
</td>
|
208 |
+
</table>
|
209 |
+
<div class="submit"><input type="submit" name="action" value="<?=$caption?>" /></div>
|
210 |
+
</fieldset>
|
211 |
+
</form>
|
212 |
+
</div>
|
213 |
+
<?php
|
214 |
+
}
|
215 |
+
|
216 |
+
function fwp_syndication_manage_page () {
|
217 |
+
global $user_level, $wpdb;
|
218 |
+
?>
|
219 |
+
<?php $cont = true;
|
220 |
+
if (isset($_REQUEST['action'])):
|
221 |
+
//die("ACTION: '".$_REQUEST['action']."'");
|
222 |
+
if ($_REQUEST['action'] == 'feedfinder'): $cont = fwp_feedfinder_page();
|
223 |
+
elseif ($_REQUEST['action'] == 'switchfeed'): $cont = fwp_switchfeed_page();
|
224 |
+
elseif ($_REQUEST['action'] == 'Delete Checked'): $cont = fwp_multidelete_page();
|
225 |
+
endif;
|
226 |
+
endif;
|
227 |
+
|
228 |
+
if ($cont):
|
229 |
+
?>
|
230 |
+
<?php
|
231 |
+
$links = get_linkobjects(FeedWordPress::link_category_id());
|
232 |
+
?>
|
233 |
+
<div class="wrap">
|
234 |
+
<form action="link-manager.php?page=feedwordpress.php" method="post">
|
235 |
+
<h2>Syndicate a new site:</h2>
|
236 |
+
<div>
|
237 |
+
<label for="add-uri">Website or newsfeed:</label>
|
238 |
+
<input type="text" name="lookup" id="add-uri" value="URI" size="64" />
|
239 |
+
<input type="hidden" name="action" value="feedfinder" />
|
240 |
+
</div>
|
241 |
+
<div class="submit"><input type="submit" value="Syndicate »" /></div>
|
242 |
+
</form>
|
243 |
+
</div>
|
244 |
+
|
245 |
+
<form action="link-manager.php?page=feedwordpress.php" method="post">
|
246 |
+
<div class="wrap">
|
247 |
+
<h2>Syndicated Sites</h2>
|
248 |
+
<?php $alt_row = true;
|
249 |
+
if ($links): ?>
|
250 |
+
|
251 |
+
<table width="100%" cellpadding="3" cellspacing="3">
|
252 |
+
<tr>
|
253 |
+
<th width="20%"><?php _e('Name'); ?></th>
|
254 |
+
<th width="50%"><?php _e('Feed'); ?></th>
|
255 |
+
<th colspan="4"><?php _e('Action'); ?></th>
|
256 |
+
</tr>
|
257 |
+
|
258 |
+
<?php foreach ($links as $link):
|
259 |
+
$alt_row = !$alt_row; ?>
|
260 |
+
<tr<?=($alt_row?' class="alternate"':'')?>>
|
261 |
+
<td><a href="<?=wp_specialchars($link->link_url)?>"><?=wp_specialchars($link->link_name)?></a></td>
|
262 |
+
<?php if (strlen($link->link_rss) > 0): $caption='Switch Feed'; ?>
|
263 |
+
<td style="font-size:smaller;text-align:center">
|
264 |
+
<strong><a href="<?=$link->link_rss?>"><?=wp_specialchars($link->link_rss)?></a></strong>
|
265 |
+
<br/><em>check validity</em> <a style="vertical-align:middle"
|
266 |
+
title="Check feed <<?=wp_specialchars($link->link_rss)?>> for validity"
|
267 |
+
href="http://feedvalidator.org/check.cgi?url=<?=urlencode($link->link_rss)?>"><img
|
268 |
+
src="../wp-images/smilies/icon_arrow.gif" alt="→" /></a></td>
|
269 |
+
<?php else: $caption='Find Feed'; ?>
|
270 |
+
<td style="background-color:#FFFFD0"><p><strong>no
|
271 |
+
feed assigned</strong></p></td>
|
272 |
+
<? endif; ?>
|
273 |
+
<?php if (($link->user_level <= $user_level)): ?>
|
274 |
+
<td><a href="link-manager.php?page=feedwordpress.php&link_id=<?=$link->link_id?>&action=feedfinder" class="edit"><?=$caption?></a></div></td>
|
275 |
+
<td><a href="link-manager.php?link_id=<?=$link->link_id?>&action=linkedit" class="edit"><?php _e('Edit')?></a></td>
|
276 |
+
<td><a href="link-manager.php?link_id=<?=$link->link_id?>&action=Delete" onclick="return confirm('You are about to delete this link.\\n \'Cancel\' to stop, \'OK\' to delete.');" class="delete"><?php _e('Delete'); ?></a></td>
|
277 |
+
<td><input type="checkbox" name="linkcheck[]" value="<?=$link->link_id?>" /></td>
|
278 |
+
<?php else:
|
279 |
+
echo "<td> </td><td> </td><td> </td><td> </td>\n";
|
280 |
+
endif;
|
281 |
+
echo "\n\t</tr>";
|
282 |
+
?>
|
283 |
+
</tr>
|
284 |
+
<?php
|
285 |
+
endforeach;
|
286 |
+
else: ?>
|
287 |
+
|
288 |
+
<p>There are no websites currently listed for syndication.</p>
|
289 |
+
|
290 |
+
<?php endif; ?>
|
291 |
+
</table>
|
292 |
+
</div>
|
293 |
+
|
294 |
+
<div class="wrap">
|
295 |
+
<h2>Manage Multiple Links</h2>
|
296 |
+
<div class="submit">
|
297 |
+
<input type="submit" class="delete" name="action" value="Delete Checked" />
|
298 |
+
</div>
|
299 |
+
</div>
|
300 |
+
</form>
|
301 |
+
<?php
|
302 |
+
endif;
|
303 |
+
}
|
304 |
+
|
305 |
+
function fwp_feedfinder_page () {
|
306 |
+
global $user_level, $wpdb;
|
307 |
+
|
308 |
+
$lookup = (isset($_REQUEST['lookup'])?$_REQUEST['lookup']:NULL);
|
309 |
+
|
310 |
+
if (isset($_REQUEST['link_id']) and ($_REQUEST['link_id']!=0)):
|
311 |
+
$link_id = $_REQUEST['link_id'];
|
312 |
+
$link = $wpdb->get_row("SELECT * FROM $wpdb->links WHERE link_id='".$wpdb->escape($link_id)."'");
|
313 |
+
if (is_object($link)):
|
314 |
+
if (is_null($lookup)) $lookup = $link->link_url;
|
315 |
+
$name = wp_specialchars($link->link_name);
|
316 |
+
else:
|
317 |
+
die (__("Cheatin' uh ?"));
|
318 |
+
endif;
|
319 |
+
else:
|
320 |
+
$name = "New Syndicated Feed";
|
321 |
+
$link_id = 0;
|
322 |
+
endif;
|
323 |
+
?>
|
324 |
+
<div class="wrap">
|
325 |
+
<h2>Feed Finder: <?=$name?></h2>
|
326 |
+
<?php $f =& new FeedFinder($lookup);
|
327 |
+
$feeds = $f->find();
|
328 |
+
if (count($feeds) > 0):
|
329 |
+
foreach ($feeds as $key => $f):
|
330 |
+
$rss = fetch_rss($f);
|
331 |
+
if ($rss):
|
332 |
+
$feed_title = isset($rss->channel['title'])?$rss->channel['title']:$rss->channel['link'];
|
333 |
+
$feed_link = isset($rss->channel['link'])?$rss->channel['link']:'';
|
334 |
+
?>
|
335 |
+
<form action="link-manager.php?page=feedwordpress.php" method="post">
|
336 |
+
<fieldset style="clear: both">
|
337 |
+
<legend><?=$rss->feed_type?> <?=$rss->feed_version?> feed</legend>
|
338 |
+
|
339 |
+
<?php if ($link_id===0): ?>
|
340 |
+
<input type="hidden" name="feed_title" value="<?=wp_specialchars($feed_title)?>" />
|
341 |
+
<input type="hidden" name="feed_link" value="<?=wp_specialchars($feed_link)?>" />
|
342 |
+
<?php endif; ?>
|
343 |
+
|
344 |
+
<input type="hidden" name="link_id" value="<?=$link_id?>" />
|
345 |
+
<input type="hidden" name="feed" value="<?=wp_specialchars($f)?>" />
|
346 |
+
<input type="hidden" name="action" value="switchfeed" />
|
347 |
+
|
348 |
+
<div>
|
349 |
+
<div style="float:right; background-color:#D0D0D0; color: black; width:45%; font-size:70%; border-left: 1px dotted #A0A0A0; padding-left: 0.5em; margin-left: 1.0em">
|
350 |
+
<?php if (count($rss->items) > 0): ?>
|
351 |
+
<?php $item = $rss->items[0]; ?>
|
352 |
+
<h3>Sample Item</h3>
|
353 |
+
<ul>
|
354 |
+
<li><strong>Title:</strong> <a href="<?=$item['link']?>"><?=$item['title']?></a></li>
|
355 |
+
<li><strong>Date:</strong> <?=isset($item['date_timestamp']) ? date('d-M-y g:i:s a', $item['date_timestamp']) : 'unknown'?></li>
|
356 |
+
</ul>
|
357 |
+
<div class="entry">
|
358 |
+
<?=(isset($item['content']['encoded'])?$item['content']['encoded']:$item['description'])?>
|
359 |
+
</div>
|
360 |
+
<?php else: ?>
|
361 |
+
<h3>No Items</h3>
|
362 |
+
<p>FeedWordPress found no posts on this feed.</p>
|
363 |
+
<?php endif; ?>
|
364 |
+
</div>
|
365 |
+
|
366 |
+
<div>
|
367 |
+
<h3>Feed Information</h3>
|
368 |
+
<ul>
|
369 |
+
<li><strong>Website:</strong> <a href="<?=$feed_link?>"><?=is_null($feed_title)?'<em>Unknown</em>':$feed_title?></a></li>
|
370 |
+
<li><strong>Feed URI:</strong> <a href="<?=wp_specialchars($f)?>"><?=wp_specialchars($f)?></a> <a title="Check feed <<?=wp_specialchars($f)?>> for validity" href="http://feedvalidator.org/check.cgi?url=<?=urlencode($f)?>"><img src="../wp-images/smilies/icon_arrow.gif" alt="(→)" /></a></li>
|
371 |
+
<li><strong>Encoding:</strong> <?=isset($rss->encoding)?wp_specialchars($rss->encoding):"<em>Unknown</em>"?></li>
|
372 |
+
<li><strong>Description:</strong> <?=isset($rss->channel['description'])?wp_specialchars($rss->channel['description']):"<em>Unknown</em>"?></li>
|
373 |
+
</ul>
|
374 |
+
<div class="submit"><input type="submit" name="Use" value="« Use this feed" /></div>
|
375 |
+
<div class="submit"><input type="submit" name="Cancel" value="« Cancel" /></div>
|
376 |
+
</div>
|
377 |
+
</div>
|
378 |
+
</fieldset>
|
379 |
+
</form>
|
380 |
+
<?php endif;
|
381 |
+
endforeach;
|
382 |
+
else:
|
383 |
+
echo "<p><strong>no feed found</strong></p>";
|
384 |
+
endif;
|
385 |
+
?>
|
386 |
+
</div>
|
387 |
+
|
388 |
+
<form action="link-manager.php?page=feedwordpress.php" method="post">
|
389 |
+
<div class="wrap">
|
390 |
+
<h2>Use another feed</h2>
|
391 |
+
<div><label>Feed:</label>
|
392 |
+
<input type="text" name="lookup" value="URI" />
|
393 |
+
<input type="hidden" name="link_id" value="<?=$link_id?>" />
|
394 |
+
<input type="hidden" name="action" value="feedfinder" /></div>
|
395 |
+
<div class="submit"><input type="submit" value="Use this feed »" /></div>
|
396 |
+
</div>
|
397 |
+
</form>
|
398 |
+
<?php
|
399 |
+
return false; // Don't continue
|
400 |
+
}
|
401 |
+
|
402 |
+
function fwp_switchfeed_page () {
|
403 |
+
global $wpdb, $user_level;
|
404 |
+
|
405 |
+
check_admin_referer();
|
406 |
+
if (!isset($_REQUEST['Cancel'])):
|
407 |
+
if ($user_level < 5):
|
408 |
+
die (__("Cheatin' uh ?"));
|
409 |
+
elseif (isset($_REQUEST['link_id']) and ($_REQUEST['link_id']==0)):
|
410 |
+
// Get the category ID#
|
411 |
+
$cat_id = FeedWordPress::link_category_id();
|
412 |
+
$result = $wpdb->query("
|
413 |
+
INSERT INTO $wpdb->links
|
414 |
+
SET
|
415 |
+
link_name = '".$wpdb->escape($_REQUEST['feed_title'])."',
|
416 |
+
link_url = '".$wpdb->escape($_REQUEST['feed_link'])."',
|
417 |
+
link_category = '".$wpdb->escape($cat_id)."',
|
418 |
+
link_rss = '".$wpdb->escape($_REQUEST['feed'])."'
|
419 |
+
");
|
420 |
+
|
421 |
+
if ($result): ?>
|
422 |
+
<div class="updated"><p><a href="<?=$_REQUEST['feed_link']?>"><?=wp_specialchars($_REQUEST['feed_title'])?></a>
|
423 |
+
has been added as a contributing site, using the newfeed at <<a href="<?=$_REQUEST['feed']?>"><?=wp_specialchars($_REQUEST['feed'])?></a>>.</p></div>
|
424 |
+
<?php else: ?>
|
425 |
+
<div class="updated"><p>There was a problem adding the newsfeed. [SQL: <?=wp_specialchars(mysql_error())?>]</p></div>
|
426 |
+
<?php endif;
|
427 |
+
elseif (isset($_REQUEST['link_id'])):
|
428 |
+
// Update link_rss
|
429 |
+
$result = $wpdb->query("
|
430 |
+
UPDATE $wpdb->links
|
431 |
+
SET
|
432 |
+
link_rss = '".$wpdb->escape($_REQUEST['feed'])."'
|
433 |
+
WHERE link_id = '".$wpdb->escape($_REQUEST['link_id'])."'
|
434 |
+
");
|
435 |
+
|
436 |
+
if ($result):
|
437 |
+
$result = $wpdb->get_row("
|
438 |
+
SELECT link_name, link_url FROM $wpdb->links
|
439 |
+
WHERE link_id = '".$wpdb->escape($_REQUEST['link_id'])."'
|
440 |
+
");
|
441 |
+
?>
|
442 |
+
<div class="updated"><p>Feed for <a href="<?=$result->link_url?>"><?=wp_specialchars($result->link_name)?></a>
|
443 |
+
updated to <<a href="<?=$_REQUEST['feed']?>"><?=wp_specialchars($_REQUEST['feed'])?></a>>.</p></div>
|
444 |
+
<?php else: ?>
|
445 |
+
<div class="updated"><p>Nothing was changed.</p></div>
|
446 |
+
<?php endif;
|
447 |
+
endif;
|
448 |
+
endif;
|
449 |
+
return true; // Continue
|
450 |
+
}
|
451 |
+
|
452 |
+
function fwp_multidelete_page () {
|
453 |
+
global $wpdb, $user_level;
|
454 |
+
check_admin_referer();
|
455 |
+
if ($user_level < 5):
|
456 |
+
die (__("Cheatin' uh ?"));
|
457 |
+
else:
|
458 |
+
// Update link_rss
|
459 |
+
$result = $wpdb->query("
|
460 |
+
DELETE FROM $wpdb->links
|
461 |
+
WHERE link_id IN (".implode(',',$_REQUEST['linkcheck']).")
|
462 |
+
");
|
463 |
+
|
464 |
+
if ($result):
|
465 |
+
$mesg = "Sites deleted from syndication list.";
|
466 |
+
else:
|
467 |
+
$mesg = "There was a problem deleting the sites from the syndication list. [SQL: ".mysql_error()."]";
|
468 |
+
endif;
|
469 |
+
echo "<div class=\"updated\">$mesg</div>\n";
|
470 |
+
endif;
|
471 |
+
return true;
|
472 |
+
}
|
473 |
+
|
474 |
+
# -- Outbound XML-RPC ping reform
|
475 |
+
# 'coz it's rude to send 500 pings the first time your aggregator runs
|
476 |
+
$fwp_held_ping = NULL; // NULL: not holding pings yet
|
477 |
+
|
478 |
+
function fwp_hold_pings () {
|
479 |
+
global $fwp_held_ping;
|
480 |
+
if (is_null($fwp_held_ping)):
|
481 |
+
$fwp_held_ping = 0; // 0: ready to hold pings; none yet received
|
482 |
+
endif;
|
483 |
+
}
|
484 |
+
|
485 |
+
function fwp_release_pings () {
|
486 |
+
global $fwp_held_ping;
|
487 |
+
if ($fwp_held_ping):
|
488 |
+
generic_ping($fwp_held_ping);
|
489 |
+
endif;
|
490 |
+
$fwp_held_ping = NULL; // NULL: not holding pings anymore
|
491 |
+
}
|
492 |
+
|
493 |
+
function fwp_catch_ping ($post_id = 0) {
|
494 |
+
global $fwp_held_ping;
|
495 |
+
if (!is_null($fwp_held_ping) and $post_id):
|
496 |
+
$fwp_held_ping = $post_id;
|
497 |
+
else:
|
498 |
+
generic_ping($fwp_held_ping);
|
499 |
+
endif;
|
500 |
+
}
|
501 |
+
|
502 |
+
// class FeedWordPress: handle the updating of the feeds and plug in to the
|
503 |
+
// XML-RPC interface
|
504 |
+
class FeedWordPress {
|
505 |
+
var $strip_attrs = array (
|
506 |
+
array('[a-z]+', 'style'),
|
507 |
+
array('[a-z]+', 'target'),
|
508 |
+
);
|
509 |
+
var $uri_attrs = array (
|
510 |
+
array('a', 'href'),
|
511 |
+
array('applet', 'codebase'),
|
512 |
+
array('area', 'href'),
|
513 |
+
array('blockquote', 'cite'),
|
514 |
+
array('body', 'background'),
|
515 |
+
array('del', 'cite'),
|
516 |
+
array('form', 'action'),
|
517 |
+
array('frame', 'longdesc'),
|
518 |
+
array('frame', 'src'),
|
519 |
+
array('iframe', 'longdesc'),
|
520 |
+
array('iframe', 'src'),
|
521 |
+
array('head', 'profile'),
|
522 |
+
array('img', 'longdesc'),
|
523 |
+
array('img', 'src'),
|
524 |
+
array('img', 'usemap'),
|
525 |
+
array('input', 'src'),
|
526 |
+
array('input', 'usemap'),
|
527 |
+
array('ins', 'cite'),
|
528 |
+
array('link', 'href'),
|
529 |
+
array('object', 'classid'),
|
530 |
+
array('object', 'codebase'),
|
531 |
+
array('object', 'data'),
|
532 |
+
array('object', 'usemap'),
|
533 |
+
array('q', 'cite'),
|
534 |
+
array('script', 'src')
|
535 |
+
);
|
536 |
+
|
537 |
+
var $_base = NULL;
|
538 |
+
var $feeds = NULL;
|
539 |
+
|
540 |
+
# function FeedWordPress (): Contructor; gain list of feeds
|
541 |
+
#
|
542 |
+
# To keep things compact and editable from within WordPress, we use a
|
543 |
+
# category of the WordPress "Links" for our list of feeds to syndicate
|
544 |
+
# By default, we use all the links in the "Contributors" category.
|
545 |
+
# Fields used are:
|
546 |
+
#
|
547 |
+
# * link_rss: the URI of the Atom/RSS feed to syndicate
|
548 |
+
#
|
549 |
+
# * link_notes: user-configurable options, with keys and values
|
550 |
+
# like so:
|
551 |
+
#
|
552 |
+
# key: value
|
553 |
+
# cats: computers:web
|
554 |
+
# feed/key: value
|
555 |
+
#
|
556 |
+
# Keys that start with "feed/" are gleaned from the data supplied
|
557 |
+
# by the feed itself, and will be overwritten with each update.
|
558 |
+
#
|
559 |
+
# The value of `cats` is used as a colon-separated (:) list of
|
560 |
+
# default categories for any post coming from a particular feed.
|
561 |
+
# (In the example above, any posts from this feed will be placed
|
562 |
+
# in the "computers" and "web" categories--*in addition to* any
|
563 |
+
# categories that may already be applied to the posts.)
|
564 |
+
#
|
565 |
+
# Values of keys in link_notes are accessible from templates using
|
566 |
+
# the function `get_feed_meta($key)` if this plugin is activated.
|
567 |
+
|
568 |
+
function FeedWordPress () {
|
569 |
+
$result = get_linkobjects(FeedWordPress::link_category_id());
|
570 |
+
|
571 |
+
$feeds = array ();
|
572 |
+
if ($result): foreach ($result as $link):
|
573 |
+
$sec = array ();
|
574 |
+
|
575 |
+
if (strlen($link->link_rss) > 0):
|
576 |
+
$notes = explode("\n", $link->link_notes);
|
577 |
+
foreach ($notes as $note):
|
578 |
+
list($key, $value) = explode(": ", $note, 2);
|
579 |
+
if (strlen($key) > 0) $sec[$key] = $value;
|
580 |
+
endforeach;
|
581 |
+
|
582 |
+
$sec['url'] = $link->link_rss;
|
583 |
+
$sec['name'] = $link->link_name;
|
584 |
+
|
585 |
+
if (isset($sec['cats'])):
|
586 |
+
$sec['cats'] = explode(':',$sec['cats']);
|
587 |
+
endif;
|
588 |
+
$sec['link_id'] = $link->link_id;
|
589 |
+
|
590 |
+
$feeds[] = $sec;
|
591 |
+
endif;
|
592 |
+
endforeach; endif;
|
593 |
+
|
594 |
+
$this->feeds = $feeds;
|
595 |
+
} // function acquire_feeds ()
|
596 |
+
|
597 |
+
function update ($uri) {
|
598 |
+
if (FEEDWORDPRESS_LOG_UPDATES) error_log("[".date('Y-m-d H:i:s')."][feedwordpress] update('$uri')");
|
599 |
+
|
600 |
+
global $wpdb;
|
601 |
+
|
602 |
+
// Secret voodoo tag: URI for updating *everything*.
|
603 |
+
$secret = RPC_MAGIC.FeedWordPress::rpc_secret();
|
604 |
+
|
605 |
+
fwp_hold_pings();
|
606 |
+
|
607 |
+
// Loop through and check for new posts
|
608 |
+
$delta = NULL;
|
609 |
+
foreach ($this->feeds as $feed) {
|
610 |
+
if (($uri === $secret)
|
611 |
+
or ($uri === $feed['url'])
|
612 |
+
or ($uri === $feed['feed/link'])) {
|
613 |
+
if (is_null($delta)) $delta = array('new' => 0, 'updated' => 0);
|
614 |
+
if (FEEDWORDPRESS_LOG_UPDATES) error_log("[".date('Y-m-d H:i:s')."][feedwordpress] Examining $feed[name] <$feed[url]>");
|
615 |
+
$added = $this->feed2wp($wpdb, $feed);
|
616 |
+
if (isset($added['new'])) $delta['new'] += $added['new'];
|
617 |
+
if (isset($added['updated'])) $delta['updated'] += $added['updated'];
|
618 |
+
} /* if */
|
619 |
+
} /* foreach */
|
620 |
+
|
621 |
+
fwp_release_pings();
|
622 |
+
if (FEEDWORDPRESS_LOG_UPDATES):
|
623 |
+
$mesg = array();
|
624 |
+
if (isset($delta['new'])) { $mesg[] = 'added '.$delta['new'].' new posts'; }
|
625 |
+
if (isset($delta['updated'])) { $mesg[] = 'updated '.$delta['updated'].' existing posts'; }
|
626 |
+
if (empty($mesg)) { $mesg[] = 'nothing changed'; }
|
627 |
+
|
628 |
+
error_log("[".date('Y-m-d H:i:s')."][feedwordpress] "
|
629 |
+
.(is_null($delta) ? "I don't syndicate <$uri>"
|
630 |
+
: implode(' and ', $mesg)));
|
631 |
+
endif;
|
632 |
+
return $delta;
|
633 |
+
}
|
634 |
+
|
635 |
+
function feed2wp ($wpdb, $f) {
|
636 |
+
$feed = fetch_rss($f['url']);
|
637 |
+
$new_count = array('new' => 0, 'updated' => 0);
|
638 |
+
|
639 |
+
$this->update_feed($wpdb, $feed->channel, $f);
|
640 |
+
|
641 |
+
if (is_array($feed->items)) {
|
642 |
+
foreach ($feed->items as $item) {
|
643 |
+
$post = $this->item_to_post($wpdb, $item, $feed->channel, $f);
|
644 |
+
$new = $this->add_post($wpdb, $post);
|
645 |
+
if ( $new !== false ) { $new_count[$new]++; }
|
646 |
+
} // foreach
|
647 |
+
} // if
|
648 |
+
return $new_count;
|
649 |
+
} // function feed2wp ()
|
650 |
+
|
651 |
+
// FeedWordPress::flatten_array (): flatten an array. Useful for
|
652 |
+
// hierarchical and namespaced elements.
|
653 |
+
//
|
654 |
+
// Given an array which may contain array or object elements in it,
|
655 |
+
// return a "flattened" array: a one-dimensional array of scalars
|
656 |
+
// containing each of the scalar elements contained within the array
|
657 |
+
// structure. Thus, for example, if $a['b']['c']['d'] == 'e', then the
|
658 |
+
// returned array for FeedWordPress::flatten_array($a) will contain a key
|
659 |
+
// $a['feed/b/c/d'] with value 'e'.
|
660 |
+
function flatten_array ($arr, $prefix = 'feed/', $separator = '/') {
|
661 |
+
$ret = array ();
|
662 |
+
if (is_array($arr)):
|
663 |
+
foreach ($arr as $key => $value) {
|
664 |
+
if (is_scalar($value)) {
|
665 |
+
$ret[$prefix.$key] = $value;
|
666 |
+
} else {
|
667 |
+
$ret = array_merge($ret, $this->flatten_array($value, $prefix.$key.$separator, $separator));
|
668 |
+
} /* if */
|
669 |
+
} /* foreach */
|
670 |
+
endif;
|
671 |
+
return $ret;
|
672 |
+
} // function FeedWordPress::flatten_array ()
|
673 |
+
|
674 |
+
function resolve_relative_uri ($matches) {
|
675 |
+
return $matches[1].Relative_URI::resolve($matches[2], $this->_base).$matches[3];
|
676 |
+
} // function FeedWordPress::resolve_relative_uri ()
|
677 |
+
|
678 |
+
function update_feed ($wpdb, $channel, $f) {
|
679 |
+
$affirmo = array ('y', 'yes', 't', 'true', 1);
|
680 |
+
|
681 |
+
$link_id = $f['link_id'];
|
682 |
+
|
683 |
+
if (!isset($channel['id'])) {
|
684 |
+
$channel['id'] = $f['url'];
|
685 |
+
}
|
686 |
+
|
687 |
+
$update = array();
|
688 |
+
if (isset($channel['link'])) {
|
689 |
+
$update[] = "link_url = '".$wpdb->escape($channel['link'])."'";
|
690 |
+
}
|
691 |
+
if (isset($channel['title']) and (!isset($f['hardcode name'])
|
692 |
+
or in_array(trim(strtolower($f['hardcode name'])), $affirmo))) {
|
693 |
+
$update[] = "link_name = '".$wpdb->escape($channel['title'])."'";
|
694 |
+
}
|
695 |
+
|
696 |
+
if (isset($channel['tagline'])) {
|
697 |
+
$update[] = "link_description = '".$wpdb->escape($channel['tagline'])."'";
|
698 |
+
} elseif (isset($channel['description'])) {
|
699 |
+
$update[] = "link_description = '".$wpdb->escape($channel['description'])."'";
|
700 |
+
}
|
701 |
+
|
702 |
+
if (is_array($f['cats'])) {
|
703 |
+
$f['cats'] = implode(':',$f['cats']);
|
704 |
+
} /* if */
|
705 |
+
|
706 |
+
$f = array_merge($f, $this->flatten_array($channel));
|
707 |
+
|
708 |
+
# -- A few things we don't want to save in the notes
|
709 |
+
unset($f['link_id']); unset($f['uri']); unset($f['url']);
|
710 |
+
|
711 |
+
$notes = '';
|
712 |
+
foreach ($f as $key => $value) {
|
713 |
+
$notes .= "${key}: $value\n";
|
714 |
+
}
|
715 |
+
$update[] = "link_notes = '".$wpdb->escape($notes)."'";
|
716 |
+
|
717 |
+
$update_set = implode(',', $update);
|
718 |
+
|
719 |
+
// if we've already have this feed, update
|
720 |
+
$result = $wpdb->query("
|
721 |
+
UPDATE $wpdb->links
|
722 |
+
SET $update_set
|
723 |
+
WHERE link_id='$link_id'
|
724 |
+
");
|
725 |
+
} // function FeedWordPress::update_feed ()
|
726 |
+
|
727 |
+
function item_to_post($wpdb, $item, $channel, $f) {
|
728 |
+
$post = array();
|
729 |
+
$post['post_title'] = $wpdb->escape($item['title']);
|
730 |
+
|
731 |
+
$author = array ();
|
732 |
+
if (isset($item['dc']['creator'])):
|
733 |
+
$author['name'] = $item['dc']['creator'];
|
734 |
+
elseif (isset($item['dc']['creator'])):
|
735 |
+
$author['name'] = $item['dc']['contributor'];
|
736 |
+
elseif (isset($item['author_name'])):
|
737 |
+
$author['name'] = $item['author_name'];
|
738 |
+
else:
|
739 |
+
$author['name'] = $channel['title'];
|
740 |
+
endif;
|
741 |
+
|
742 |
+
if (isset($item['author_email'])):
|
743 |
+
$author['email'] = $item['author_email'];
|
744 |
+
endif;
|
745 |
+
|
746 |
+
if (isset($item['author_url'])):
|
747 |
+
$author['url'] = $item['author_url'];
|
748 |
+
else:
|
749 |
+
$author['url'] = $channel['link'];
|
750 |
+
endif;
|
751 |
+
|
752 |
+
$post['post_author'] = $this->author_to_id($wpdb, $author['name'], $author['email'], $author['url']);
|
753 |
+
|
754 |
+
# Identify content and sanitize it.
|
755 |
+
# ---------------------------------
|
756 |
+
if (isset($item['content']['encoded']) and $item['content']['encoded']):
|
757 |
+
$content = $item['content']['encoded'];
|
758 |
+
else:
|
759 |
+
$content = $item['description'];
|
760 |
+
endif;
|
761 |
+
|
762 |
+
# Resolve relative URIs in post content
|
763 |
+
#
|
764 |
+
# N.B.: We *might* get screwed over by xml:base. But I don't see
|
765 |
+
# any way to get that information out of MagpieRSS if it's
|
766 |
+
# in the feed, and if it's in the content itself we'd have
|
767 |
+
# to do yet more XML parsing to do things right. For now
|
768 |
+
# this will have to do.
|
769 |
+
|
770 |
+
$this->_base = $item['link']; // Reset the base for resolving relative URIs
|
771 |
+
foreach ($this->uri_attrs as $pair):
|
772 |
+
list($tag,$attr) = $pair;
|
773 |
+
$content = preg_replace_callback (
|
774 |
+
":(<$tag [^>]*$attr=\")([^\">]*)(\"[^>]*>):i",
|
775 |
+
array(&$this,'resolve_relative_uri'),
|
776 |
+
$content
|
777 |
+
);
|
778 |
+
endforeach;
|
779 |
+
|
780 |
+
# Sanitize problematic attributes
|
781 |
+
foreach ($this->strip_attrs as $pair):
|
782 |
+
list($tag,$attr) = $pair;
|
783 |
+
$content = preg_replace (
|
784 |
+
":(<$tag [^>]*)($attr=(\"[^\">]*\"|[^>\\s]+))([^>]*>):i",
|
785 |
+
"\\1\\4",
|
786 |
+
$content
|
787 |
+
);
|
788 |
+
endforeach;
|
789 |
+
|
790 |
+
$post['post_content'] = $wpdb->escape($content);
|
791 |
+
|
792 |
+
$post['post_name'] = sanitize_title($post['post_title']);
|
793 |
+
|
794 |
+
# RSS is a fucking mess. Figure out whether we have a date in
|
795 |
+
# dc:date, <issued>, <pubDate>, etc., and get it into Unix epoch
|
796 |
+
# format for reformatting. If you can't find anything, use the
|
797 |
+
# current time.
|
798 |
+
if (isset($item['dc']['date'])):
|
799 |
+
$post['epoch']['issued'] = parse_w3cdtf($item['dc']['date']);
|
800 |
+
elseif (isset($item['issued'])):
|
801 |
+
$post['epoch']['issued'] = parse_w3cdtf($item['issued']);
|
802 |
+
elseif (isset($item['pubdate'])):
|
803 |
+
$post['epoch']['issued'] = strtotime($item['pubdate']);
|
804 |
+
else:
|
805 |
+
$post['epoch']['issued'] = time();
|
806 |
+
endif;
|
807 |
+
|
808 |
+
# As far as I know, only atom currently has a reliable way to
|
809 |
+
# specify when something was *modified* last
|
810 |
+
if (isset($item['modified'])):
|
811 |
+
$post['epoch']['modified'] = parse_w3cdtf($item['modified']);
|
812 |
+
else:
|
813 |
+
$post['epoch']['modified'] = $post['epoch']['issued'];
|
814 |
+
endif;
|
815 |
+
|
816 |
+
$post['post_date'] = date('Y-m-d H:i:s', $post['epoch']['issued']);
|
817 |
+
$post['post_modified'] = date('Y-m-d H:i:s', $post['epoch']['modified']);
|
818 |
+
$post['post_date_gmt'] = gmdate('Y-m-d H:i:s', $post['epoch']['issued']);
|
819 |
+
$post['post_modified_gmt'] = gmdate('Y-m-d H:i:s', $post['epoch']['modified']);
|
820 |
+
|
821 |
+
# Use feed-level preferences or a sensible default.
|
822 |
+
$post['post_status'] = (isset($f['post status']) ? $wpdb->escape(trim(strtolower($f['post status']))) : 'publish');
|
823 |
+
$post['comment_status'] = (isset($f['comment status']) ? $wpdb->escape(trim(strtolower($f['comment status']))) : 'closed');
|
824 |
+
$post['ping_status'] = (isset($f['ping status']) ? $wpdb->escape(trim(strtolower($f['ping status']))) : 'closed');
|
825 |
+
|
826 |
+
// Unique ID (hopefully a unique tag: URI); failing that, the permalink
|
827 |
+
if (isset($item['id'])):
|
828 |
+
$post['guid'] = $wpdb->escape($item['id']);
|
829 |
+
else:
|
830 |
+
$post['guid'] = $wpdb->escape($item['link']);
|
831 |
+
endif;
|
832 |
+
|
833 |
+
if (isset($channel['title'])) $post['syndication_source'] = $channel['title'];
|
834 |
+
if (isset($channel['link'])) $post['syndication_source_uri'] = $channel['link'];
|
835 |
+
$post['syndication_feed'] = $f['url'];
|
836 |
+
|
837 |
+
// In case you want to know the external permalink...
|
838 |
+
$post['syndication_permalink'] = $item['link'];
|
839 |
+
|
840 |
+
// Categories: start with default categories
|
841 |
+
$item_cats = $f['cats'];
|
842 |
+
|
843 |
+
// Now add categories from the post, if we have 'em
|
844 |
+
if (is_array($item['categories'])):
|
845 |
+
foreach ($item['categories'] as $cat):
|
846 |
+
if ( strpos($f['url'], 'del.icio.us') !== false ):
|
847 |
+
$item_cats = array_merge($item_cats, explode(' ', $cat));
|
848 |
+
else:
|
849 |
+
$item_cats[] = $cat;
|
850 |
+
endif;
|
851 |
+
endforeach;
|
852 |
+
endif;
|
853 |
+
$post['post_category'] = $this->lookup_categories($wpdb, $item_cats);
|
854 |
+
|
855 |
+
return $post;
|
856 |
+
} // function FeedWordPress::item_to_post ()
|
857 |
+
|
858 |
+
function add_post ($wpdb, $post) {
|
859 |
+
$guid = $post['guid'];
|
860 |
+
$result = $wpdb->get_row("
|
861 |
+
SELECT id, guid, UNIX_TIMESTAMP(post_modified) AS modified
|
862 |
+
FROM $wpdb->posts WHERE guid='$guid'
|
863 |
+
");
|
864 |
+
|
865 |
+
if (!$result):
|
866 |
+
// The item has not yet been added.
|
867 |
+
# The right way to do this would be to use:
|
868 |
+
#
|
869 |
+
# $postId = wp_insert_post($post);
|
870 |
+
# $result = $wpdb->query("
|
871 |
+
# UPDATE $wpdb->posts
|
872 |
+
# SET
|
873 |
+
# guid='$guid'
|
874 |
+
# WHERE post_id='$postId'
|
875 |
+
# ");
|
876 |
+
#
|
877 |
+
# in place of everything in the cut below. Alas,
|
878 |
+
# wp_insert_post seems to be a memory hog; using it
|
879 |
+
# to insert several posts in one session makes php
|
880 |
+
# segfault after inserting 50-100 posts. This can get
|
881 |
+
# pretty annoying, especially if you are trying to
|
882 |
+
# update your feeds for the first time.
|
883 |
+
#
|
884 |
+
# --- cut here ---
|
885 |
+
$result = $wpdb->query("
|
886 |
+
INSERT INTO $wpdb->posts
|
887 |
+
SET
|
888 |
+
guid = '$guid',
|
889 |
+
post_author = '".$post['post_author']."',
|
890 |
+
post_date = '".$post['post_date']."',
|
891 |
+
post_date_gmt = '".$post['post_date_gmt']."',
|
892 |
+
post_content = '".$post['post_content']."',
|
893 |
+
post_title = '".$post['post_title']."',
|
894 |
+
post_name = '".$post['post_name']."',
|
895 |
+
post_modified = '".$post['post_modified']."',
|
896 |
+
post_modified_gmt = '".$post['post_modified_gmt']."',
|
897 |
+
comment_status = '".$post['comment_status']."',
|
898 |
+
ping_status = '".$post['ping_status']."',
|
899 |
+
post_status = '".$post['post_status']."'
|
900 |
+
");
|
901 |
+
$postId = $wpdb->insert_id;
|
902 |
+
$this->add_to_category($wpdb, $postId, $post['post_category']);
|
903 |
+
|
904 |
+
// Since we are not going through official channels, we need to
|
905 |
+
// manually tell WordPress that we've published a new post.
|
906 |
+
// We need to make sure to do this in order for FeedWordPress
|
907 |
+
// to play well with the staticize-reloaded plugin (something
|
908 |
+
// that a large aggregator website is going to *want* to be
|
909 |
+
// able to use).
|
910 |
+
do_action('publish_post', $postId);
|
911 |
+
# --- cut here ---
|
912 |
+
|
913 |
+
if (FEEDWORDPRESS_LOG_UPDATES) error_log("[".date('Y-m-d H:i:s')."][feedwordpress] posted '".$post['post_title']."' (".$post['post_date'].") from '".$post['syndication_source']."'");
|
914 |
+
$this->add_rss_meta($wpdb, $postId, $post);
|
915 |
+
$ret = 'new';
|
916 |
+
elseif ($post['epoch']['modified'] > $result->modified):
|
917 |
+
$postId = $result->id; $modified = $result->modified;
|
918 |
+
|
919 |
+
$result = $wpdb->query("
|
920 |
+
UPDATE $wpdb->posts
|
921 |
+
SET
|
922 |
+
post_author = '".$post['post_author']."',
|
923 |
+
post_content = '".$post['post_content']."',
|
924 |
+
post_title = '".$post['post_title']."',
|
925 |
+
post_name = '".$post['post_name']."',
|
926 |
+
post_modified = '".$post['post_modified']."',
|
927 |
+
post_modified_gmt = '".$post['post_modified_gmt']."'
|
928 |
+
WHERE guid='$guid'
|
929 |
+
");
|
930 |
+
$this->add_to_category($wpdb, $postId, $post['post_category']);
|
931 |
+
|
932 |
+
// Since we are not going through official channels, we need to
|
933 |
+
// manually tell WordPress that we've published a new post.
|
934 |
+
// We need to make sure to do this in order for FeedWordPress
|
935 |
+
// to play well with the staticize-reloaded plugin (something
|
936 |
+
// that a large aggregator website is going to *want* to be
|
937 |
+
// able to use).
|
938 |
+
do_action('edit_post', $postId);
|
939 |
+
|
940 |
+
if (FEEDWORDPRESS_LOG_UPDATES):
|
941 |
+
error_log("[".date('Y-m-d H:i:s')
|
942 |
+
."][feedwordpress] updated '"
|
943 |
+
.$post['post_title']
|
944 |
+
."' (".$post['post_date']
|
945 |
+
.") from '".$post['syndication_source']
|
946 |
+
."' (".date('Y-m-d H:i:s', $modified)
|
947 |
+
." ==> "
|
948 |
+
.date('Y-m-d H:i:s', $post['epoch']['modified'])
|
949 |
+
.')');
|
950 |
+
endif;
|
951 |
+
$this->add_rss_meta($wpdb, $postId, $post);
|
952 |
+
$ret = 'updated';
|
953 |
+
else:
|
954 |
+
$ret = false;
|
955 |
+
endif;
|
956 |
+
|
957 |
+
return $ret;
|
958 |
+
} // function FeedWordPress::add_post ()
|
959 |
+
|
960 |
+
# function FeedWordPress::add_to_category ()
|
961 |
+
#
|
962 |
+
# If there is a way to properly hook in to wp_insert_post, then this
|
963 |
+
# function will no longer be needed. In the meantime, here it is.
|
964 |
+
# --- cut here ---
|
965 |
+
function add_to_category($wpdb, $postId, $post_categories) {
|
966 |
+
// Default to category 1 ("Uncategorized"), if nothing else
|
967 |
+
if (!$post_categories) $post_categories[] = 1;
|
968 |
+
|
969 |
+
// Clean the slate (in case we're updating)
|
970 |
+
$results = $wpdb->query("
|
971 |
+
DELETE FROM $wpdb->post2cat
|
972 |
+
WHERE post_id = $postId
|
973 |
+
");
|
974 |
+
|
975 |
+
foreach ($post_categories as $post_category):
|
976 |
+
$results = $wpdb->query("
|
977 |
+
INSERT INTO $wpdb->post2cat
|
978 |
+
SET
|
979 |
+
post_id = $postId,
|
980 |
+
category_id = $post_category
|
981 |
+
");
|
982 |
+
endforeach;
|
983 |
+
} // function FeedWordPress::add_to_category ()
|
984 |
+
# --- cut here ---
|
985 |
+
|
986 |
+
// FeedWordPress::add_rss_meta: adds feed meta-data to user-defined keys
|
987 |
+
// for each entry. Interesting feed meta-data is tagged in the $post
|
988 |
+
// array using the prefix 'syndication_'. This should be used for
|
989 |
+
// anything that the WordPress user might want to access about a post's
|
990 |
+
// original source that isn't provided for by standard WP meta-data
|
991 |
+
// (i.e., beyond author, title, timestamp, and categories)
|
992 |
+
function add_rss_meta ($wpdb, $postId, $post) {
|
993 |
+
foreach ($post as $key => $value):
|
994 |
+
if (strpos($key, "syndication_") === 0):
|
995 |
+
$value = $wpdb->escape($value);
|
996 |
+
|
997 |
+
$result = $wpdb->query("
|
998 |
+
DELETE FROM $wpdb->postmeta
|
999 |
+
WHERE post_id='$postId' AND meta_key='$key'
|
1000 |
+
");
|
1001 |
+
|
1002 |
+
$result = $wpdb->query("
|
1003 |
+
INSERT INTO $wpdb->postmeta
|
1004 |
+
SET
|
1005 |
+
post_id='$postId',
|
1006 |
+
meta_key='$key',
|
1007 |
+
meta_value='$value'
|
1008 |
+
");
|
1009 |
+
endif;
|
1010 |
+
endforeach;
|
1011 |
+
} /* FeedWordPress::add_rss_meta () */
|
1012 |
+
|
1013 |
+
// FeedWordPress::author_to_id (): get the ID for an author name from
|
1014 |
+
// the feed. Create the author if necessary.
|
1015 |
+
function author_to_id ($wpdb, $author, $email, $url) {
|
1016 |
+
// Never can be too careful...
|
1017 |
+
$nice_author = sanitize_title($author);
|
1018 |
+
$author = $wpdb->escape($author);
|
1019 |
+
$email = $wpdb->escape($email);
|
1020 |
+
$url = $wpdb->escape($url);
|
1021 |
+
|
1022 |
+
$id = $wpdb->get_var(
|
1023 |
+
"SELECT ID from $wpdb->users
|
1024 |
+
WHERE
|
1025 |
+
user_login = '$author' OR
|
1026 |
+
user_firstname = '$author' OR
|
1027 |
+
user_nickname = '$author' OR
|
1028 |
+
user_description = '$author' OR
|
1029 |
+
user_nicename = '$nice_author'");
|
1030 |
+
|
1031 |
+
if (is_null($id)):
|
1032 |
+
$wpdb->query (
|
1033 |
+
"INSERT INTO $wpdb->users
|
1034 |
+
SET
|
1035 |
+
ID='0',
|
1036 |
+
user_login='$author',
|
1037 |
+
user_firstname='$author',
|
1038 |
+
user_nickname='$author',
|
1039 |
+
user_nicename='$nice_author',
|
1040 |
+
user_description='$author',
|
1041 |
+
user_email='$email',
|
1042 |
+
user_url='$url'");
|
1043 |
+
$id = $wpdb->insert_id;
|
1044 |
+
endif;
|
1045 |
+
return $id;
|
1046 |
+
} // function FeedWordPress::author_to_id ()
|
1047 |
+
|
1048 |
+
// look up (and create) category ids from a list of categories
|
1049 |
+
function lookup_categories ($wpdb, $cats) {
|
1050 |
+
if ( !count($cats) ) return array();
|
1051 |
+
|
1052 |
+
# i'd kill for a decent map function in PHP
|
1053 |
+
# but that would require functiosn to be first class object, or at least
|
1054 |
+
# coderef support
|
1055 |
+
$cat_strs = array();
|
1056 |
+
foreach ( $cats as $c ) {
|
1057 |
+
$c = $wpdb->escape($c); $c = "'$c'";
|
1058 |
+
$cat_strs[] = $c;
|
1059 |
+
}
|
1060 |
+
|
1061 |
+
$cat_sql = join(',', $cat_strs);
|
1062 |
+
$sql = "SELECT cat_ID,cat_name from $wpdb->categories WHERE cat_name IN ($cat_sql)";
|
1063 |
+
$results = $wpdb->get_results($sql);
|
1064 |
+
|
1065 |
+
$cat_ids = array();
|
1066 |
+
$cat_found = array();
|
1067 |
+
|
1068 |
+
if (!is_null($results)):
|
1069 |
+
foreach ( $results as $row ) {
|
1070 |
+
$cat_ids[] = $row->cat_ID;
|
1071 |
+
$cat_found[] = strtolower($row->cat_name); // Normalize to avoid case problems
|
1072 |
+
}
|
1073 |
+
endif;
|
1074 |
+
|
1075 |
+
foreach ($cats as $new_cat):
|
1076 |
+
$sql = "INSERT INTO $wpdb->categories (cat_name, category_nicename)
|
1077 |
+
VALUES ('%s', '%s')";
|
1078 |
+
if (!in_array(strtolower($new_cat), $cat_found)):
|
1079 |
+
$nice_cat = sanitize_title($new_cat);
|
1080 |
+
$wpdb->query(sprintf($sql, $wpdb->escape($new_cat), $nice_cat));
|
1081 |
+
$cat_ids[] = $wpdb->insert_id;
|
1082 |
+
endif;
|
1083 |
+
endforeach;
|
1084 |
+
return $cat_ids;
|
1085 |
+
} // function FeedWordPress::lookup_categories ()
|
1086 |
+
|
1087 |
+
function rpc_secret () {
|
1088 |
+
return get_settings('feedwordpress_rpc_secret');
|
1089 |
+
} // function FeedWordPress::rpc_secret ()
|
1090 |
+
|
1091 |
+
function link_category_id () {
|
1092 |
+
global $wpdb;
|
1093 |
+
|
1094 |
+
$cat_id = get_settings('feedwordpress_cat_id');
|
1095 |
+
|
1096 |
+
// If we don't yet *have* the category, we'll have to create it
|
1097 |
+
if ($cat_id === false) {
|
1098 |
+
$cat = $wpdb->escape(DEFAULT_SYNDICATION_CATEGORY);
|
1099 |
+
|
1100 |
+
// Look for something with the right name...
|
1101 |
+
$cat_id = $wpdb->get_var("
|
1102 |
+
SELECT cat_id FROM $wpdb->linkcategories
|
1103 |
+
WHERE cat_name='$cat'
|
1104 |
+
");
|
1105 |
+
|
1106 |
+
// If you still can't find anything, make it for yourself.
|
1107 |
+
if (!$cat_id) {
|
1108 |
+
$result = $wpdb->query("
|
1109 |
+
INSERT INTO $wpdb->linkcategories
|
1110 |
+
SET
|
1111 |
+
cat_id = 0,
|
1112 |
+
cat_name='$cat',
|
1113 |
+
show_images='N',
|
1114 |
+
show_description='N',
|
1115 |
+
show_rating='N',
|
1116 |
+
show_updated='N',
|
1117 |
+
sort_order='name'
|
1118 |
+
");
|
1119 |
+
$cat_id = $wpdb->insert_id;
|
1120 |
+
}
|
1121 |
+
|
1122 |
+
update_option('feedwordpress_cat_id', $cat_id);
|
1123 |
+
}
|
1124 |
+
return $cat_id;
|
1125 |
+
}
|
1126 |
+
|
1127 |
+
function link_category () {
|
1128 |
+
global $wpdb;
|
1129 |
+
|
1130 |
+
$cat_id = FeedWordPress::link_category_id();
|
1131 |
+
|
1132 |
+
// Get the ID# for the category name...
|
1133 |
+
$cat_name = $wpdb->get_var("
|
1134 |
+
SELECT cat_name FROM $wpdb->linkcategories
|
1135 |
+
WHERE cat_id='$cat_id'
|
1136 |
+
");
|
1137 |
+
return $cat_name;
|
1138 |
+
}
|
1139 |
+
} // class FeedWordPress
|
1140 |
+
|
1141 |
+
# -- Inbound XML-RPC plugin interface
|
1142 |
+
function feedwordpress_xmlrpc_hook ($args = array ()) {
|
1143 |
+
$args['weblogUpdates.ping'] = 'feedwordpress_pong';
|
1144 |
+
return $args;
|
1145 |
+
}
|
1146 |
+
|
1147 |
+
function feedwordpress_pong ($args) {
|
1148 |
+
$feedwordpress =& new FeedWordPress;
|
1149 |
+
$delta = @$feedwordpress->update($args[1]);
|
1150 |
+
if (is_null($delta)):
|
1151 |
+
return array('flerror' => true, 'message' => "Sorry. I don't syndicate <$args[1]>.");
|
1152 |
+
else:
|
1153 |
+
$mesg = array();
|
1154 |
+
if (isset($delta['new'])) { $mesg[] = ' '.$delta['new'].' new posts were syndicated'; }
|
1155 |
+
if (isset($delta['updated'])) { $mesg[] = ' '.$delta['updated'].' existing posts were updated'; }
|
1156 |
+
|
1157 |
+
return array('flerror' => false, 'message' => "Thanks for the ping.".implode(' and', $mesg));
|
1158 |
+
endif;
|
1159 |
+
}
|
1160 |
+
|
1161 |
+
class FeedFinder {
|
1162 |
+
var $uri = NULL;
|
1163 |
+
var $_cache_uri = NULL;
|
1164 |
+
|
1165 |
+
var $verify = FALSE;
|
1166 |
+
|
1167 |
+
var $_data = NULL;
|
1168 |
+
var $_head = NULL;
|
1169 |
+
|
1170 |
+
# -- Recognition patterns
|
1171 |
+
var $_feed_types = array(
|
1172 |
+
'application/rss+xml',
|
1173 |
+
'text/xml',
|
1174 |
+
'application/atom+xml',
|
1175 |
+
'application/x.atom+xml',
|
1176 |
+
'application/x-atom+xml'
|
1177 |
+
);
|
1178 |
+
var $_feed_markers = array('\\<feed', '\\<rss', 'xmlns="http://purl.org/rss/1.0');
|
1179 |
+
var $_html_markers = array('\\<html');
|
1180 |
+
var $_obvious_feed_url = array('[./]rss', '[./]rdf', '[./]atom', '[./]feed', '\.xml');
|
1181 |
+
var $_maybe_feed_url = array ('rss', 'rdf', 'atom', 'feed', 'xml');
|
1182 |
+
|
1183 |
+
function FeedFinder ($uri = NULL, $verify = TRUE) {
|
1184 |
+
$this->uri = $uri; $this->verify = $verify;
|
1185 |
+
} /* FeedFinder::FeedFinder () */
|
1186 |
+
|
1187 |
+
function find ($uri = NULL) {
|
1188 |
+
$ret = array ();
|
1189 |
+
if (!is_null($this->data($uri))) {
|
1190 |
+
if ($this->is_feed($uri)) {
|
1191 |
+
$ret = array($this->uri);
|
1192 |
+
} else {
|
1193 |
+
// Assume that we have HTML or XHTML (even if we don't, who's it gonna hurt?)
|
1194 |
+
|
1195 |
+
// Autodiscovery is the preferred method
|
1196 |
+
$href = $this->_link_rel_feeds();
|
1197 |
+
|
1198 |
+
// ... but we'll also take the little orange buttons
|
1199 |
+
$href = array_merge($href, $this->_a_href_feeds(TRUE));
|
1200 |
+
|
1201 |
+
// If all that failed, look harder
|
1202 |
+
if (count($href) == 0) $href = $this->_a_href_feeds(FALSE);
|
1203 |
+
|
1204 |
+
// Verify feeds and resolve relative URIs
|
1205 |
+
foreach ($href as $u) {
|
1206 |
+
$the_uri = Relative_URI::resolve($u, $this->uri);
|
1207 |
+
if ($this->verify) {
|
1208 |
+
$feed =& new FeedFinder($the_uri);
|
1209 |
+
if ($feed->is_feed()) $ret[] = $the_uri;
|
1210 |
+
$feed = NULL;
|
1211 |
+
} else {
|
1212 |
+
$ret[] = $the_uri;
|
1213 |
+
}
|
1214 |
+
} /* foreach */
|
1215 |
+
} /* if */
|
1216 |
+
} /* if */
|
1217 |
+
return array_unique($ret);
|
1218 |
+
} /* FeedFinder::find () */
|
1219 |
+
|
1220 |
+
function data ($uri = NULL) {
|
1221 |
+
$this->_get($uri);
|
1222 |
+
return $this->_data;
|
1223 |
+
}
|
1224 |
+
|
1225 |
+
function is_feed ($uri = NULL) {
|
1226 |
+
$data = $this->data($uri);
|
1227 |
+
return (
|
1228 |
+
preg_match (
|
1229 |
+
"\007(".implode('|',$this->_feed_markers).")\007i",
|
1230 |
+
$data
|
1231 |
+
) and !preg_match (
|
1232 |
+
"\007(".implode('|',$this->_html_markers).")\007i",
|
1233 |
+
$data
|
1234 |
+
)
|
1235 |
+
);
|
1236 |
+
} /* FeedFinder::is_feed () */
|
1237 |
+
|
1238 |
+
# --- Private methods ---
|
1239 |
+
function _get ($uri = NULL) {
|
1240 |
+
if ($uri) $this->uri = $uri;
|
1241 |
+
|
1242 |
+
// Is the result not yet cached?
|
1243 |
+
if ($this->_cache_uri !== $this->uri) {
|
1244 |
+
// Retrieve, with headers, using cURL
|
1245 |
+
$ch = curl_init($this->uri);
|
1246 |
+
curl_setopt($ch, CURLOPT_HEADER, false);
|
1247 |
+
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
|
1248 |
+
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Connection: close'));
|
1249 |
+
curl_setopt($ch, CURLOPT_HTTPHEADER, array('User-Agent: feedfinder/1.2 (compatible; PHP FeedFinder) +http://projects.radgeek.com/feedwordpress'));
|
1250 |
+
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
1251 |
+
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
|
1252 |
+
$response = curl_exec($ch);
|
1253 |
+
curl_close($ch);
|
1254 |
+
|
1255 |
+
// Split into headers and content
|
1256 |
+
$this->_data = $response;
|
1257 |
+
|
1258 |
+
// Kilroy was here
|
1259 |
+
$this->_cache_uri = $this->uri;
|
1260 |
+
} /* if */
|
1261 |
+
} /* FeedFinder::_get () */
|
1262 |
+
|
1263 |
+
function _link_rel_feeds () {
|
1264 |
+
$links = $this->_tags('link');
|
1265 |
+
$link_count = count($links);
|
1266 |
+
|
1267 |
+
// now figure out which one points to the RSS file
|
1268 |
+
$href = array ();
|
1269 |
+
for ($n=0; $n<$link_count; $n++) {
|
1270 |
+
if (strtolower($links[$n]['rel']) == 'alternate') {
|
1271 |
+
if (in_array(strtolower($links[$n]['type']), $this->_feed_types)) {
|
1272 |
+
$href[] = $links[$n]['href'];
|
1273 |
+
} /* if */
|
1274 |
+
} /* if */
|
1275 |
+
} /* for */
|
1276 |
+
return $href;
|
1277 |
+
}
|
1278 |
+
|
1279 |
+
function _a_href_feeds ($obvious = TRUE) {
|
1280 |
+
$pattern = ($obvious ? $this->_obvious_feed_url : $this->_maybe_feed_url);
|
1281 |
+
|
1282 |
+
$links = $this->_tags('a');
|
1283 |
+
$link_count = count($links);
|
1284 |
+
|
1285 |
+
// now figure out which one points to the RSS file
|
1286 |
+
$href = array ();
|
1287 |
+
for ($n=0; $n<$link_count; $n++) {
|
1288 |
+
if (preg_match("\007(".implode('|',$pattern).")\007i", $links[$n]['href'])) {
|
1289 |
+
$href[] = $links[$n]['href'];
|
1290 |
+
} /* if */
|
1291 |
+
} /* for */
|
1292 |
+
return $href;
|
1293 |
+
}
|
1294 |
+
|
1295 |
+
function _tags ($tag) {
|
1296 |
+
$html = $this->data();
|
1297 |
+
|
1298 |
+
// search through the HTML, save all <link> tags
|
1299 |
+
// and store each link's attributes in an associative array
|
1300 |
+
preg_match_all('/<'.$tag.'\s+(.*?)\s*\/?>/si', $html, $matches);
|
1301 |
+
|
1302 |
+
$links = $matches[1];
|
1303 |
+
$ret = array();
|
1304 |
+
$link_count = count($links);
|
1305 |
+
for ($n=0; $n<$link_count; $n++) {
|
1306 |
+
$attributes = preg_split('/\s+/s', $links[$n]);
|
1307 |
+
foreach($attributes as $attribute) {
|
1308 |
+
$att = preg_split('/\s*=\s*/s', $attribute, 2);
|
1309 |
+
if (isset($att[1])) {
|
1310 |
+
$att[1] = preg_replace('/([\'"]?)(.*)\1/', '$2', $att[1]);
|
1311 |
+
$final_link[strtolower($att[0])] = $att[1];
|
1312 |
+
} /* if */
|
1313 |
+
} /* foreach */
|
1314 |
+
$ret[$n] = $final_link;
|
1315 |
+
} /* for */
|
1316 |
+
return $ret;
|
1317 |
+
}
|
1318 |
+
} /* class FeedFinder */
|
1319 |
+
|
1320 |
+
# Relative URI static class: PHP class for resolving relative URLs
|
1321 |
+
#
|
1322 |
+
# This class is derived (under the terms of the GPL) from URL Class 0.3 by
|
1323 |
+
# Keyvan Minoukadeh <keyvan@k1m.com>, which is great but more than we need
|
1324 |
+
# for FeedWordPress's purposes. The class has been stripped down to a single
|
1325 |
+
# public method: Relative_URI::resolve($url, $base), which resolves the URI in
|
1326 |
+
# $url relative to the URI in $base
|
1327 |
+
|
1328 |
+
class Relative_URI
|
1329 |
+
{
|
1330 |
+
// Resolve relative URI in $url against the base URI in $base. If $base
|
1331 |
+
// is not supplied, then we use the REQUEST_URI of this script.
|
1332 |
+
//
|
1333 |
+
// I'm hoping this method reflects RFC 2396 Section 5.2
|
1334 |
+
function resolve ($url, $base = NULL)
|
1335 |
+
{
|
1336 |
+
if (is_null($base)):
|
1337 |
+
$base = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
|
1338 |
+
endif;
|
1339 |
+
|
1340 |
+
$base = Relative_URI::_encode(trim($base));
|
1341 |
+
$uri_parts = Relative_URI::_parse_url($base);
|
1342 |
+
|
1343 |
+
$url = Relative_URI::_encode(trim($url));
|
1344 |
+
$parts = Relative_URI::_parse_url($url);
|
1345 |
+
|
1346 |
+
$uri_parts['fragment'] = (isset($parts['fragment']) ? $parts['fragment'] : null);
|
1347 |
+
$uri_parts['query'] = $parts['query'];
|
1348 |
+
|
1349 |
+
// if path is empty, and scheme, host, and query are undefined,
|
1350 |
+
// the URL is referring the base URL
|
1351 |
+
|
1352 |
+
if (($parts['path'] == '') && !isset($parts['scheme']) && !isset($parts['host']) && !isset($parts['query'])) {
|
1353 |
+
// If the URI is empty or only a fragment, return the base URI
|
1354 |
+
return $base . (isset($parts['fragment']) ? '#'.$parts['fragment'] : '');
|
1355 |
+
} elseif (isset($parts['scheme'])) {
|
1356 |
+
// If the scheme is set, then the URI is absolute.
|
1357 |
+
return $url;
|
1358 |
+
} elseif (isset($parts['host'])) {
|
1359 |
+
$uri_parts['host'] = $parts['host'];
|
1360 |
+
$uri_parts['path'] = $parts['path'];
|
1361 |
+
} else {
|
1362 |
+
// We have a relative path but not a host.
|
1363 |
+
|
1364 |
+
// start ugly fix:
|
1365 |
+
// prepend slash to path if base host is set, base path is not set, and url path is not absolute
|
1366 |
+
if ($uri_parts['host'] && ($uri_parts['path'] == '')
|
1367 |
+
&& (strlen($parts['path']) > 0)
|
1368 |
+
&& (substr($parts['path'], 0, 1) != '/')) {
|
1369 |
+
$parts['path'] = '/'.$parts['path'];
|
1370 |
+
} // end ugly fix
|
1371 |
+
|
1372 |
+
if (substr($parts['path'], 0, 1) == '/') {
|
1373 |
+
$uri_parts['path'] = $parts['path'];
|
1374 |
+
} else {
|
1375 |
+
// copy base path excluding any characters after the last (right-most) slash character
|
1376 |
+
$buffer = substr($uri_parts['path'], 0, (int)strrpos($uri_parts['path'], '/')+1);
|
1377 |
+
// append relative path
|
1378 |
+
$buffer .= $parts['path'];
|
1379 |
+
// remove "./" where "." is a complete path segment.
|
1380 |
+
$buffer = str_replace('/./', '/', $buffer);
|
1381 |
+
if (substr($buffer, 0, 2) == './') {
|
1382 |
+
$buffer = substr($buffer, 2);
|
1383 |
+
}
|
1384 |
+
// if buffer ends with "." as a complete path segment, remove it
|
1385 |
+
if (substr($buffer, -2) == '/.') {
|
1386 |
+
$buffer = substr($buffer, 0, -1);
|
1387 |
+
}
|
1388 |
+
// remove "<segment>/../" where <segment> is a complete path segment not equal to ".."
|
1389 |
+
$search_finished = false;
|
1390 |
+
$segment = explode('/', $buffer);
|
1391 |
+
while (!$search_finished) {
|
1392 |
+
for ($x=0; $x+1 < count($segment);) {
|
1393 |
+
if (($segment[$x] != '') && ($segment[$x] != '..') && ($segment[$x+1] == '..')) {
|
1394 |
+
if ($x+2 == count($segment)) $segment[] = '';
|
1395 |
+
unset($segment[$x], $segment[$x+1]);
|
1396 |
+
$segment = array_values($segment);
|
1397 |
+
continue 2;
|
1398 |
+
} else {
|
1399 |
+
$x++;
|
1400 |
+
}
|
1401 |
+
}
|
1402 |
+
$search_finished = true;
|
1403 |
+
}
|
1404 |
+
$buffer = (count($segment) == 1) ? '/' : implode('/', $segment);
|
1405 |
+
$uri_parts['path'] = $buffer;
|
1406 |
+
|
1407 |
+
}
|
1408 |
+
}
|
1409 |
+
|
1410 |
+
// If we've gotten to this point, we can try to put the pieces
|
1411 |
+
// back together.
|
1412 |
+
$ret = '';
|
1413 |
+
if (isset($uri_parts['scheme'])) $ret .= $uri_parts['scheme'].':';
|
1414 |
+
if (isset($uri_parts['user'])) {
|
1415 |
+
$ret .= $uri_parts['user'];
|
1416 |
+
if (isset($uri_parts['pass'])) $ret .= ':'.$uri_parts['parts'];
|
1417 |
+
$ret .= '@';
|
1418 |
+
}
|
1419 |
+
if (isset($uri_parts['host'])) {
|
1420 |
+
$ret .= '//'.$uri_parts['host'];
|
1421 |
+
if (isset($uri_parts['port'])) $ret .= ':'.$uri_parts['port'];
|
1422 |
+
}
|
1423 |
+
$ret .= $uri_parts['path'];
|
1424 |
+
if (isset($uri_parts['query'])) $ret .= '?'.$uri_parts['query'];
|
1425 |
+
if (isset($uri_parts['fragment'])) $ret .= '#'.$uri_parts['fragment'];
|
1426 |
+
|
1427 |
+
return $ret;
|
1428 |
+
}
|
1429 |
+
|
1430 |
+
/**
|
1431 |
+
* Parse URL
|
1432 |
+
*
|
1433 |
+
* Regular expression grabbed from RFC 2396 Appendix B.
|
1434 |
+
* This is a replacement for PHPs builtin parse_url().
|
1435 |
+
* @param string $url
|
1436 |
+
* @access private
|
1437 |
+
* @return array
|
1438 |
+
*/
|
1439 |
+
function _parse_url($url)
|
1440 |
+
{
|
1441 |
+
// I'm using this pattern instead of parse_url() as there's a few strings where parse_url()
|
1442 |
+
// generates a warning.
|
1443 |
+
if (preg_match('!^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?!', $url, $match)) {
|
1444 |
+
$parts = array();
|
1445 |
+
if ($match[1] != '') $parts['scheme'] = $match[2];
|
1446 |
+
if ($match[3] != '') $parts['auth'] = $match[4];
|
1447 |
+
// parse auth
|
1448 |
+
if (isset($parts['auth'])) {
|
1449 |
+
// store user info
|
1450 |
+
if (($at_pos = strpos($parts['auth'], '@')) !== false) {
|
1451 |
+
$userinfo = explode(':', substr($parts['auth'], 0, $at_pos), 2);
|
1452 |
+
$parts['user'] = $userinfo[0];
|
1453 |
+
if (isset($userinfo[1])) $parts['pass'] = $userinfo[1];
|
1454 |
+
$parts['auth'] = substr($parts['auth'], $at_pos+1);
|
1455 |
+
}
|
1456 |
+
// get port number
|
1457 |
+
if ($port_pos = strrpos($parts['auth'], ':')) {
|
1458 |
+
$parts['host'] = substr($parts['auth'], 0, $port_pos);
|
1459 |
+
$parts['port'] = (int)substr($parts['auth'], $port_pos+1);
|
1460 |
+
if ($parts['port'] < 1) $parts['port'] = null;
|
1461 |
+
} else {
|
1462 |
+
$parts['host'] = $parts['auth'];
|
1463 |
+
}
|
1464 |
+
}
|
1465 |
+
unset($parts['auth']);
|
1466 |
+
$parts['path'] = $match[5];
|
1467 |
+
if (isset($match[6]) && ($match[6] != '')) $parts['query'] = $match[7];
|
1468 |
+
if (isset($match[8]) && ($match[8] != '')) $parts['fragment'] = $match[9];
|
1469 |
+
return $parts;
|
1470 |
+
}
|
1471 |
+
// shouldn't reach here
|
1472 |
+
return array('path'=>'');
|
1473 |
+
}
|
1474 |
+
|
1475 |
+
function _encode($string)
|
1476 |
+
{
|
1477 |
+
static $replace = array();
|
1478 |
+
if (!count($replace)) {
|
1479 |
+
$find = array(32, 34, 60, 62, 123, 124, 125, 91, 92, 93, 94, 96, 127);
|
1480 |
+
$find = array_merge(range(0, 31), $find);
|
1481 |
+
$find = array_map('chr', $find);
|
1482 |
+
foreach ($find as $char) {
|
1483 |
+
$replace[$char] = '%'.bin2hex($char);
|
1484 |
+
}
|
1485 |
+
}
|
1486 |
+
// escape control characters and a few other characters
|
1487 |
+
$encoded = strtr($string, $replace);
|
1488 |
+
// remove any character outside the hex range: 21 - 7E (see www.asciitable.com)
|
1489 |
+
return preg_replace('/[^\x21-\x7e]/', '', $encoded);
|
1490 |
+
}
|
1491 |
+
}
|
1492 |
+
?>
|
wp-content/update-feeds.php
ADDED
@@ -0,0 +1,110 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
# update-feeds.php: Instruct FeedWordPress to scan for fresh content
|
3 |
+
#
|
4 |
+
# Project: FeedWordPress
|
5 |
+
# URI: <http://projects.radgeek.com/feedwordpress>
|
6 |
+
# Author: Charles Johnson <technophilia@radgeek.com>
|
7 |
+
# License: GPL
|
8 |
+
# Version: 0.8
|
9 |
+
# Last modified: 2005-03-21
|
10 |
+
#
|
11 |
+
# USAGE
|
12 |
+
# -----
|
13 |
+
# update-feeds.php is a useful script for instructing the FeedWordPress plugin
|
14 |
+
# to scan for fresh content on the feeds that it syndicates. This is handy if
|
15 |
+
# you want your syndication site to actually syndicate content, and you can't
|
16 |
+
# rely on all your contributors to send you an XML-RPC ping when they update.
|
17 |
+
#
|
18 |
+
# 1. Install FeedWordPress and activate the FeedWordPress plugin. (See
|
19 |
+
# <http://projects.radgeek.com/feedwordpress/install> if you need a guide
|
20 |
+
# for the perplexed.)
|
21 |
+
#
|
22 |
+
# 2. Set up a cron job to run update-feeds.php locally:
|
23 |
+
#
|
24 |
+
# cd <your-wordpress>/wp-content ; php update-feeds.php
|
25 |
+
#
|
26 |
+
# or to send an HTTP GET request to the appropriate URI:
|
27 |
+
#
|
28 |
+
# curl http://xyz.com/wp-content/update-feeds.php?shibboleth=foo
|
29 |
+
#
|
30 |
+
# 4. If you want to update *one* of the feeds rather than *all* of them, then
|
31 |
+
# pass the URI and title as command-line arguments:
|
32 |
+
#
|
33 |
+
# $ php update-feeds.php http://www.radgeek.com "Geekery Today"
|
34 |
+
#
|
35 |
+
# or in the GET query:
|
36 |
+
#
|
37 |
+
# $ curl http://www.xyz.com/wp-content/update-feeds.php?uri=http://www.radgeek.com\&title=Geekery+Today\&shibboleth=yourshibboleth
|
38 |
+
#
|
39 |
+
|
40 |
+
require_once ('../wp-config.php');
|
41 |
+
require_once (ABSPATH . WPINC . '/class-IXR.php');
|
42 |
+
|
43 |
+
# -- CHANGE THESE TO REFLECT YOUR SITE SETTINGS!
|
44 |
+
define ('RPC_URI', NULL); // Change this setting to ping a URI of your own devising
|
45 |
+
|
46 |
+
# -- Don't change these unless you know what you're doing...
|
47 |
+
define ('RPC_MAGIC', 'tag:radgeek.com/projects/feedwordpress/'); // update all
|
48 |
+
|
49 |
+
if (is_null(RPC_URI)):
|
50 |
+
$rpc_uri = get_settings('siteurl');
|
51 |
+
if (substr($rpc_uri,-1)!='/') $rpc_uri .= '/';
|
52 |
+
$rpc_uri .= 'xmlrpc.php';
|
53 |
+
else:
|
54 |
+
$rpc_uri = RPC_URI;
|
55 |
+
endif;
|
56 |
+
|
57 |
+
# -- Are we running from an HTTP GET or from the command line?
|
58 |
+
if (isset($_SERVER['REQUEST_URI'])) {
|
59 |
+
$rpc_secret = (isset($_REQUEST['shibboleth'])?$_REQUEST['shibboleth']:'');
|
60 |
+
$uri = (isset($_REQUEST['uri']) ? $_REQUEST['uri'] : RPC_MAGIC.$rpc_secret);
|
61 |
+
$blog = (isset($_REQUEST['title']) ? $_REQUEST['title'] : 'Refresh');
|
62 |
+
|
63 |
+
echo <<< EOHTML
|
64 |
+
<html>
|
65 |
+
<head>
|
66 |
+
<title>update-feeds :: FeedWordPress</title>
|
67 |
+
</head>
|
68 |
+
|
69 |
+
<body>
|
70 |
+
<h1>update-feeds: instruct FeedWordPress to look for new syndicated content</h1>
|
71 |
+
|
72 |
+
<p>Sending ping to <$rpc_uri>...</p>
|
73 |
+
|
74 |
+
<p>
|
75 |
+
EOHTML;
|
76 |
+
} else {
|
77 |
+
// Query secret word from database
|
78 |
+
$rpc_secret = get_settings('feedwordpress_rpc_secret');
|
79 |
+
|
80 |
+
$uri = (isset($_SERVER['argv'][1]) ? $_SERVER['argv'][1] : RPC_MAGIC.$rpc_secret);
|
81 |
+
$blog = (isset($_SERVER['argv'][2]) ? $_SERVER['argv'][2] : 'Refresh');
|
82 |
+
}
|
83 |
+
|
84 |
+
$client =& new IXR_Client($rpc_uri);
|
85 |
+
$ret = $client->query('weblogUpdates.ping', $blog, $uri);
|
86 |
+
|
87 |
+
if (!$ret):
|
88 |
+
if (!isset($_SERVER['REQUEST_URI'])) echo "[".date('Y-m-d H:i:s')."][update-feeds] ";
|
89 |
+
echo "The XML-RPC ping failed (local): ".wp_specialchars($client->getErrorMessage())."\n";
|
90 |
+
else:
|
91 |
+
$response = $client->getResponse();
|
92 |
+
if ($response['flerror']):
|
93 |
+
if (!isset($_SERVER['REQUEST_URI'])) echo "[".date('Y-m-d H:i:s')."][update-feeds] ";
|
94 |
+
echo "The XML-RPC ping failed (remote): ".wp_specialchars($response['message'])."\n";
|
95 |
+
elseif (isset($_SERVER['REQUEST_URI'])):
|
96 |
+
if (!isset($_SERVER['REQUEST_URI'])) echo "[".date('Y-m-d H:i:s')."][update-feeds] ";
|
97 |
+
echo "The XML-RPC ping succeeded: ".wp_specialchars($response['message'])."\n";
|
98 |
+
endif;
|
99 |
+
endif;
|
100 |
+
|
101 |
+
if (isset($_SERVER['REQUEST_URI'])) {
|
102 |
+
echo <<<EOHTML
|
103 |
+
</p>
|
104 |
+
|
105 |
+
<p><a href="../wp-admin">← Return to WordPress Dashboard</a></p>
|
106 |
+
</body>
|
107 |
+
</html>
|
108 |
+
EOHTML;
|
109 |
+
}
|
110 |
+
?>
|