|
1 <?php |
|
2 /* |
|
3 Plugin Name: RSS Frontend |
|
4 Plugin URI: http://enanocms.org/Feed_me |
|
5 Description: Provides the page Special:RSS, which is used to generate RSS feeds of site and page content changes. |
|
6 Author: Dan Fuhry |
|
7 Version: 1.0 |
|
8 Author URI: http://enanocms.org/ |
|
9 */ |
|
10 |
|
11 /* |
|
12 * Enano - an open-source CMS capable of wiki functions, Drupal-like sidebar blocks, and everything in between |
|
13 * Version 1.0 release candidate 2 |
|
14 * Copyright (C) 2006-2007 Dan Fuhry |
|
15 * |
|
16 * This program is Free Software; you can redistribute and/or modify it under the terms of the GNU General Public License |
|
17 * as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. |
|
18 * |
|
19 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied |
|
20 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. |
|
21 */ |
|
22 |
|
23 global $db, $session, $paths, $template, $plugins; // Common objects |
|
24 |
|
25 $plugins->attachHook('base_classes_initted', ' |
|
26 $paths->add_page(Array( |
|
27 \'name\'=>\'RSS Feed - Latest changes\', |
|
28 \'urlname\'=>\'RSS\', |
|
29 \'namespace\'=>\'Special\', |
|
30 \'special\'=>0,\'visible\'=>0,\'comments_on\'=>0,\'protected\'=>1,\'delvotes\'=>0,\'delvote_ips\'=>\'\', |
|
31 )); |
|
32 '); |
|
33 |
|
34 $plugins->attachHook('session_started', '__enanoRSSAttachHTMLHeaders();'); |
|
35 |
|
36 function __enanoRSSAttachHTMLHeaders() |
|
37 { |
|
38 global $db, $session, $paths, $template, $plugins; // Common objects |
|
39 $template->add_header('<link rel="alternate" title="'.getConfig('site_name').' Changes feed" href="'.makeUrlNS('Special', 'RSS/recent', null, true).'" type="application/rss+xml" />'); |
|
40 $template->add_header('<link rel="alternate" title="'.getConfig('site_name').' Comments feed" href="'.makeUrlNS('Special', 'RSS/comments', null, true).'" type="application/rss+xml" />'); |
|
41 } |
|
42 |
|
43 define('ENANO_FEEDBURNER_INCLUDED', true); |
|
44 |
|
45 /** |
|
46 * Class for easily generating RSS feeds. |
|
47 * @package Enano |
|
48 * @subpackage Feed Me |
|
49 * @license GNU General Public License <http://www.gnu.org/licenses/gpl.html> |
|
50 */ |
|
51 |
|
52 class RSS |
|
53 { |
|
54 |
|
55 /** |
|
56 * List of channels contained in this feed. |
|
57 * @var array |
|
58 */ |
|
59 |
|
60 var $channels = Array(); |
|
61 |
|
62 /** |
|
63 * The channel that's currently being operated on |
|
64 * @var array |
|
65 */ |
|
66 |
|
67 var $this_channel = Array(); |
|
68 |
|
69 /** |
|
70 * GUID of the current channel |
|
71 * @var string |
|
72 */ |
|
73 |
|
74 var $this_guid = ''; |
|
75 |
|
76 /** |
|
77 * List of fully XML-formatted feed entries. |
|
78 * @var array |
|
79 */ |
|
80 |
|
81 var $items = Array(); |
|
82 |
|
83 /** |
|
84 * Constructor. |
|
85 * @param string Feed title |
|
86 * @param string Feed description |
|
87 * @param string Linkback |
|
88 * @param string Generator |
|
89 * @param string E-mail of webmaster |
|
90 */ |
|
91 |
|
92 function __construct($title = false, $desc = false, $link = false, $gen = false, $email = false) |
|
93 { |
|
94 $this->create_channel($title, $desc, $link, $gen, $email); |
|
95 } |
|
96 |
|
97 /** |
|
98 * PHP 4 constructor. |
|
99 */ |
|
100 |
|
101 function RSS($title = false, $desc = false, $link = false, $gen = false, $email = false) |
|
102 { |
|
103 $this->__construct($title, $desc, $link, $gen, $email); |
|
104 } |
|
105 |
|
106 /** |
|
107 * Creates a new channel. |
|
108 */ |
|
109 |
|
110 function create_channel($title = false, $desc = false, $link = false, $gen = false, $email = false) |
|
111 { |
|
112 if ( empty($title) ) |
|
113 $title = 'Untitled feed'; |
|
114 else |
|
115 $title = htmlspecialchars($title); |
|
116 if ( empty($desc) ) |
|
117 $desc = 'Test feed'; |
|
118 else |
|
119 $desc = htmlspecialchars($desc); |
|
120 if ( empty($link) ) |
|
121 $link = 'http' . ( isset($_SERVER['HTTPS']) ? 's' : '' ) . '://'.$_SERVER['HTTP_HOST'] . scriptPath . '/'; |
|
122 else |
|
123 $link = htmlspecialchars($link); |
|
124 if ( !empty($gen) ) |
|
125 $gen = htmlspecialchars($gen); |
|
126 else |
|
127 $gen = 'Enano CMS ' . enano_version(); |
|
128 if ( !empty($email) ) |
|
129 $email = htmlspecialchars($email); |
|
130 else |
|
131 $email = getConfig('contact_email'); |
|
132 |
|
133 $this->channels = Array(); |
|
134 $guid = md5(microtime() . mt_rand()); |
|
135 $this->channels[$guid] = Array( |
|
136 'title' => $title, |
|
137 'desc' => $desc, |
|
138 'link' => $link, |
|
139 'gen' => $gen, |
|
140 'email' => $email, |
|
141 'lang' => 'en-us', |
|
142 'items' => Array() |
|
143 ); |
|
144 $this->this_channel =& $this->channels[$guid]; |
|
145 $this->this_guid = $guid; |
|
146 return $guid; |
|
147 } |
|
148 |
|
149 /** |
|
150 * Selects a specific channel to add items to |
|
151 * @param string The GUID of the channel |
|
152 */ |
|
153 |
|
154 function select_channel($guid) |
|
155 { |
|
156 if ( isset($this->channels[$guid]) ) |
|
157 { |
|
158 $this->this_channel =& $this->channels[$guid]; |
|
159 $this->guid = $guid; |
|
160 } |
|
161 } |
|
162 |
|
163 /** |
|
164 * Adds a news item. |
|
165 * @param string Title of the feed entry |
|
166 * @param string Link to where more information can be found |
|
167 * @param string Short description or content area |
|
168 * @param string Date the item was published. If this is a UNIX timestamp it will be formatted with date(). |
|
169 * @param string A Globally-Unique Identifier (GUID) for this item. Doesn't have to be a 128-bit hash - usually it's a link. If one is not provided, one is generated based on the first three parameters. |
|
170 */ |
|
171 |
|
172 function add_item($title, $link, $desc, $pubdate, $guid = false) |
|
173 { |
|
174 $title = htmlspecialchars($title); |
|
175 $link = htmlspecialchars($link); |
|
176 $desc = '<![CDATA[ ' . str_replace(']]>', ']]>', $desc) . ']]>'; |
|
177 if ( is_int($pubdate) || ( !is_int($pub_date) && preg_match('/^([0-9]+)$/', $pubdate) ) ) |
|
178 { |
|
179 $pubdate = date('D, d M Y H:i:s T', intval($pubdate)); |
|
180 } |
|
181 if ( !$guid ) |
|
182 { |
|
183 $guid = md5 ( $title . $link . $desc . $pubdate ); |
|
184 $sec1 = substr($guid, 0, 8); |
|
185 $sec2 = substr($guid, 8, 4); |
|
186 $sec3 = substr($guid, 12, 4); |
|
187 $sec4 = substr($guid, 16, 4); |
|
188 $sec5 = substr($guid, 20, 12); |
|
189 $guid = sprintf('%s-%s-%s-%s-%s', $sec1, $sec2, $sec3, $sec4, $sec5); |
|
190 } |
|
191 $xml = " <item> |
|
192 <title>$title</title> |
|
193 <link>$link</link> |
|
194 <description> |
|
195 $desc |
|
196 </description> |
|
197 <pubDate>$pubdate</pubDate> |
|
198 <guid>$link</guid> |
|
199 </item>"; |
|
200 $this->this_channel['items'][] = $xml; |
|
201 } |
|
202 |
|
203 /** |
|
204 * Converts everything into the final RSS feed. |
|
205 * @param bool If true, XML headers ("<?xml version="1.0" encoding="utf-8" ?>") are included. Defaults to true. |
|
206 * @return string |
|
207 */ |
|
208 |
|
209 function render($headers = true) |
|
210 { |
|
211 $xml = ''; |
|
212 if ( $headers ) |
|
213 // The weird quotes are because of a jEdit syntax highlighting bug |
|
214 $xml .= "<?xml version=".'"'."1.0".'"'." encoding=".'"'."utf-8".'"'." ?>\n"; |
|
215 $xml .= "<rss version=".'"'."2.0".'"'.">\n"; |
|
216 foreach ( $this->channels as $channel ) |
|
217 { |
|
218 $xml .= " <channel> |
|
219 <title>{$channel['title']}</title> |
|
220 <link>{$channel['link']}</link> |
|
221 <description>{$channel['desc']}</description> |
|
222 <generator>{$channel['gen']}</generator> |
|
223 <webMaster>{$channel['email']}</webMaster> |
|
224 <language>{$channel['lang']}</language> |
|
225 |
|
226 "; |
|
227 $content = implode("\n", $channel['items']) . "\n"; |
|
228 $xml .= $content; |
|
229 $xml .= " </channel>"; |
|
230 $xml .= " |
|
231 </rss>"; |
|
232 } |
|
233 return $xml; |
|
234 } |
|
235 |
|
236 } |
|
237 |
|
238 // function names are IMPORTANT!!! The name pattern is: page_<namespace ID>_<page URLname, without namespace> |
|
239 |
|
240 function page_Special_RSS() |
|
241 { |
|
242 global $db, $session, $paths, $template, $plugins; // Common objects |
|
243 header('Content-type: text/xml; charset=utf-8'); //application/rss+xml'); |
|
244 global $aggressive_optimize_html; |
|
245 $aggressive_optimize_html = false; |
|
246 $session->sid_super = false; |
|
247 if ( $session->auth_level > USER_LEVEL_MEMBER ) |
|
248 $session->auth_level = USER_LEVEL_MEMBER; |
|
249 $mode = $paths->getParam(0); |
|
250 $n = $paths->getParam(1); |
|
251 if(!preg_match('#^([0-9]+)$#', $n) || (int)$n > 50) $n = 20; |
|
252 else $n = (int)$n; |
|
253 switch($mode) |
|
254 { |
|
255 case "recent": |
|
256 $title = getConfig('site_name') . ' Recent Changes'; |
|
257 $desc = getConfig('site_desc'); |
|
258 |
|
259 $rss = new RSS($title, $desc); |
|
260 |
|
261 $q = $db->sql_query('SELECT * FROM '.table_prefix.'logs WHERE log_type=\'page\' ORDER BY time_id DESC LIMIT '.$n.';'); |
|
262 if(!$q) |
|
263 { |
|
264 $rss->add_item('ERROR', '', 'Error selecting log data: ' . mysql_error() . '', time()); |
|
265 } |
|
266 else |
|
267 { |
|
268 while($row = $db->fetchrow()) |
|
269 { |
|
270 $link = makeUrlComplete($row['namespace'], $row['page_id'], "oldid={$row['time_id']}"); // makeUrlComplete($row['namespace'], $row['page_id']); |
|
271 $title = $paths->pages[$paths->nslist[$row['namespace']].$row['page_id']]['name']; |
|
272 $desc = "Change by {$row['author']}:<br />"; |
|
273 $desc .= ( $row['edit_summary'] != '' ) ? $row['edit_summary'] : 'No edit summary given.'; |
|
274 $date = $row['time_id']; |
|
275 $guid = false; |
|
276 |
|
277 $rss->add_item($title, $link, $desc, $date, $guid); |
|
278 } |
|
279 } |
|
280 |
|
281 echo $rss->render(); |
|
282 break; |
|
283 case "comments": |
|
284 $title = getConfig('site_name') . ' Latest Comments'; |
|
285 $desc = getConfig('site_desc'); |
|
286 |
|
287 $rss = new RSS($title, $desc); |
|
288 |
|
289 $q = $db->sql_query('SELECT * FROM '.table_prefix.'comments ORDER BY time DESC LIMIT '.$n.';'); |
|
290 |
|
291 if(!$q) |
|
292 { |
|
293 $rss->add_item('ERROR', '', 'Error selecting log data: ' . mysql_error() . '', time()); |
|
294 } |
|
295 else |
|
296 { |
|
297 $n = $db->numrows(); |
|
298 //echo '<!-- Number of rows: '.$n.' -->'; // ."\n<!-- SQL backtrace: \n\n".$db->sql_backtrace().' -->'; |
|
299 for ( $j = 0; $j < $n; $j++ ) |
|
300 { |
|
301 $row = $db->fetchrow($q); |
|
302 if(!is_array($row)) die(__FILE__.':'.__LINE__.' $row is not an array'); |
|
303 $link = 'http' . ( isset($_SERVER['HTTPS']) ? 's' : '' ) . '://'.$_SERVER['HTTP_HOST'] . makeUrlNS($row['namespace'], $row['page_id']).'#comments'; |
|
304 $page = ( isset($paths->pages[$paths->nslist[$row['namespace']].$row['page_id']]) ) ? $paths->pages[$paths->nslist[$row['namespace']].$row['page_id']]['name'] : $paths->nslist[$row['namespace']].$row['page_id']; |
|
305 $title = $row['subject'] . ': Posted on page "'.$page.'" by user '.$row['name']; |
|
306 $desc = RenderMan::render($row['comment_data']); |
|
307 $date = $row['time']; |
|
308 $guid = 'http' . ( isset($_SERVER['HTTPS']) ? 's' : '' ) . '://'.$_SERVER['HTTP_HOST'] . makeUrlNS($row['namespace'], $row['page_id']).'?do=comments&comment='.$row['time']; |
|
309 |
|
310 $rss->add_item($title, $link, $desc, $date, $guid); |
|
311 } |
|
312 } |
|
313 echo $rss->render(); |
|
314 break; |
|
315 default: |
|
316 $code = $plugins->setHook('feed_me_request'); |
|
317 foreach ( $code as $cmd ) |
|
318 { |
|
319 eval($cmd); |
|
320 } |
|
321 break; |
|
322 } |
|
323 } |
|
324 |
|
325 ?> |