7 * LICENSE: This source file is licensed under the terms of the GNU General Public License.
9 * @package Magic3 Framework
10 * @author 平田直毅(Naoki Hirata) <naoki@aplo.co.jp>
11 * @copyright Copyright 2006-2010 Magic3 Project.
12 * @license http://www.gnu.org/copyleft/gpl.html GPL License
13 * @version SVN: $Id: showrss.inc.php 3474 2010-08-13 10:36:48Z fishbone $
14 * @link http://www.magic3.org
17 // 2002-2006 PukiWiki Developers Team
18 // 2002 PANDA <panda@arino.jp>
19 // (Original)hiro_do3ob@yahoo.co.jp
20 // License: GPL, same as PukiWiki
22 // Show RSS (of remote site) plugin
24 // * This plugin needs 'PHP xml extension'
25 // * Cache data will be stored as CACHE_DIR/*.tmp
27 define('PLUGIN_SHOWRSS_USAGE', '#showrss(URI-to-RSS[,default|menubar|recent[,Cache-lifetime[,Show-timestamp]]])');
29 // Show related extensions are found or not
30 function plugin_showrss_action()
32 if (PKWK_SAFE_MODE) die_message('PKWK_SAFE_MODE prohibit this');
35 foreach(array('xml', 'mbstring') as $extension){
36 $$extension = extension_loaded($extension) ?
37 '&color(green){Found};' :
38 '&color(red){Not found};';
39 $body .= '| ' . $extension . ' extension | ' . $$extension . ' |' . "\n";
41 return array('msg' => 'showrss_info', 'body' => convert_html($body));
44 function plugin_showrss_convert()
48 if (! isset($_xml)) $_xml = extension_loaded('xml');
49 if (! $_xml) return '#showrss: xml extension is not found<br />' . "\n";
51 $num = func_num_args();
52 if ($num == 0) return PLUGIN_SHOWRSS_USAGE . '<br />' . "\n";
54 $argv = func_get_args();
57 $template = $uri = '';
59 case 4: $timestamp = (trim($argv[3]) == '1'); /*FALLTHROUGH*/
60 case 3: $cachehour = trim($argv[2]); /*FALLTHROUGH*/
61 case 2: $template = strtolower(trim($argv[1]));/*FALLTHROUGH*/
62 case 1: $uri = trim($argv[0]);
65 $class = ($template == '' || $template == 'default') ? 'ShowRSS_html' : 'ShowRSS_html_' . $template;
66 if (! is_numeric($cachehour))
67 return '#showrss: Cache-lifetime seems not numeric: ' . htmlspecialchars($cachehour) . '<br />' . "\n";
68 if (! class_exists($class))
69 return '#showrss: Template not found: ' . htmlspecialchars($template) . '<br />' . "\n";
71 return '#showrss: Seems not URI: ' . htmlspecialchars($uri) . '<br />' . "\n";
73 list($rss, $time) = plugin_showrss_get_rss($uri, $cachehour);
74 if ($rss === FALSE) return '#showrss: Failed fetching RSS from the server<br />' . "\n";
78 $time = '<p style="font-size:10px; font-weight:bold">Last-Modified:' .
79 get_date('Y/m/d H:i:s', $time) . '</p>';
82 $obj = new $class($rss);
83 return $obj->toString($time);
86 // Create HTML from RSS array()
92 function ShowRSS_html($rss)
94 foreach ($rss as $date=>$items) {
95 foreach ($items as $item) {
96 $link = $item['LINK'];
97 $title = $item['TITLE'];
98 $passage = get_passage($item['_TIMESTAMP']);
99 $link = '<a href="' . $link . '" title="' . $title . ' ' .
100 $passage . '" rel="nofollow">' . $title . '</a>';
101 $this->items[$date][] = $this->format_link($link);
106 function format_link($link)
108 return $link . '<br />' . "\n";
111 function format_list($date, $str)
116 function format_body($str)
121 function toString($timestamp)
124 foreach ($this->items as $date=>$items)
125 $retval .= $this->format_list($date, join('', $items));
126 $retval = $this->format_body($retval);
135 class ShowRSS_html_menubar extends ShowRSS_html
137 var $class = ' class="small"';
139 function format_link($link) {
140 return '<li>' . $link . '</li>' . "\n";
143 function format_body($str) {
144 return '<ul class="recent_list">' . "\n" . $str . '</ul>' . "\n";
148 class ShowRSS_html_recent extends ShowRSS_html
150 var $class = ' class="small"';
152 function format_link($link) {
153 return '<li>' . $link . '</li>' . "\n";
156 function format_list($date, $str) {
157 return '<strong>' . $date . '</strong>' . "\n" .
158 '<ul class="recent_list">' . "\n" . $str . '</ul>' . "\n";
163 function plugin_showrss_get_rss($target, $cachehour)
168 // Remove expired cache
169 plugin_showrss_cache_expire($cachehour);
171 // Get the cache not expired
172 $filename = CACHE_DIR . encode($target) . '.tmp';
173 if (is_readable($filename)) {
174 $buf = join('', file($filename));
175 $time = filemtime($filename) - LOCALZONE;
179 if ($time === NULL) {
181 $data = http_request($target);
182 if ($data['rc'] !== 200)
183 return array(FALSE, 0);
185 $buf = $data['data'];
188 // Save RSS into cache
190 $fp = fopen($filename, 'w');
197 $obj = new ShowRSS_XML();
198 return array($obj->parse($buf),$time);
201 // Remove cache if expired limit exeed
202 function plugin_showrss_cache_expire($cachehour)
204 $expire = $cachehour * 60 * 60; // Hour
205 $dh = dir(CACHE_DIR);
206 while (($file = $dh->read()) !== FALSE) {
207 if (substr($file, -4) != '.tmp') continue;
208 $file = CACHE_DIR . $file;
209 $last = time() - filemtime($file);
210 if ($last > $expire) unlink($file);
215 // Get RSS and array() them
226 $this->items = array();
227 $this->item = array();
228 $this->is_item = FALSE;
233 if(preg_match('/<\?xml [^>]*\bencoding="([a-z0-9-_]+)"/i', $buf, $matches)) {
234 $this->encoding = $matches[1];
236 $this->encoding = mb_detect_encoding($buf);
239 // Normalize to UTF-8 / ASCII
240 if (! in_array(strtolower($this->encoding), array('us-ascii', 'iso-8859-1', 'utf-8'))) {
241 $buf = mb_convert_encoding($buf, 'utf-8', $this->encoding);
242 $this->encoding = 'utf-8';
246 $xml_parser = xml_parser_create($this->encoding);
247 xml_set_element_handler($xml_parser, array($this, 'start_element'), array($this, 'end_element'));
248 xml_set_character_data_handler($xml_parser, array($this, 'character_data'));
249 if (! xml_parse($xml_parser, $buf, 1)) {
250 return(sprintf('XML error: %s at line %d in %s',
251 xml_error_string(xml_get_error_code($xml_parser)),
252 xml_get_current_line_number($xml_parser), $buf));
254 xml_parser_free($xml_parser);
259 function escape($str)
261 // Unescape already-escaped chars (<, >, &, ...) in RSS body before htmlspecialchars()
262 $str = strtr($str, array_flip(get_html_translation_table(ENT_COMPAT)));
264 $str = htmlspecialchars($str);
265 // Encoding conversion
266 $str = mb_convert_encoding($str, SOURCE_ENCODING, $this->encoding);
271 function start_element($parser, $name, $attrs)
273 if ($this->is_item) {
275 } else if ($name == 'ITEM') {
276 $this->is_item = TRUE;
281 function end_element($parser, $name)
283 if (! $this->is_item || $name != 'ITEM') return;
285 $item = array_map(array($this, 'escape'), $this->item);
286 $this->item = array();
288 if (isset($item['DC:DATE'])) {
289 $time = plugin_showrss_get_timestamp($item['DC:DATE']);
291 } else if (isset($item['PUBDATE'])) {
292 $time = plugin_showrss_get_timestamp($item['PUBDATE']);
294 } else if (isset($item['DESCRIPTION']) &&
295 ($description = trim($item['DESCRIPTION'])) != '' &&
296 ($time = strtotime($description)) != -1) {
300 $time = time() - LOCALZONE;
302 $item['_TIMESTAMP'] = $time;
303 $date = get_date('Y-m-d', $item['_TIMESTAMP']);
305 $this->items[$date][] = $item;
306 $this->is_item = FALSE;
309 function character_data($parser, $data)
311 if (! $this->is_item) return;
312 if (! isset($this->item[$this->tag])) $this->item[$this->tag] = '';
313 $this->item[$this->tag] .= $data;
317 function plugin_showrss_get_timestamp($str)
320 if ($str == '') return UTIME;
323 if (preg_match('/(\d{4}-\d{2}-\d{2})T(\d{2}:\d{2}:\d{2})(([+-])(\d{2}):(\d{2}))?/', $str, $matches)) {
324 $time = strtotime($matches[1] . ' ' . $matches[2]);
327 } else if ($matches[3]) {
328 $diff = ($matches[5] * 60 + $matches[6]) * 60;
329 $time += ($matches[4] == '-' ? $diff : -$diff);
333 $time = strtotime($str);
334 return ($time == -1) ? UTIME : $time - LOCALZONE;