OSDN Git Service

2eedec75680596a536ba3bce848a214c92499769
[pukiwiki/pukiwiki.git] / plugin / showrss.inc.php
1 <?php
2 // PukiWiki - Yet another WikiWikiWeb clone
3 // $Id: showrss.inc.php,v 1.19 2006/05/02 08:15:43 henoheno Exp $
4 //  Id:showrss.inc.php,v 1.40 2003/03/18 11:52:58 hiro Exp
5 // Copyright (C):
6 //     2002-2006 PukiWiki Developers Team
7 //     2002      PANDA <panda@arino.jp>
8 //     (Original)hiro_do3ob@yahoo.co.jp
9 // License: GPL, same as PukiWiki
10 //
11 // Show RSS (of remote site) plugin
12 // NOTE:
13 //    * This plugin needs 'PHP xml extension'
14 //    * Cache data will be stored as CACHE_DIR/*.tmp
15
16 define('PLUGIN_SHOWRSS_USAGE', '#showrss(URI-to-RSS[,default|menubar|recent[,Cache-lifetime[,Show-timestamp]]])');
17
18 // Show related extensions are found or not
19 function plugin_showrss_action()
20 {
21         if (PKWK_SAFE_MODE) die_message('PKWK_SAFE_MODE prohibit this');
22
23         $body = '';
24         foreach(array('xml', 'mbstring') as $extension){
25                 $$extension = extension_loaded($extension) ?
26                         '&color(green){Found};' :
27                         '&color(red){Not found};';
28                 $body .= '| ' . $extension . ' extension | ' . $$extension . ' |' . "\n";
29         }
30         return array('msg' => 'showrss_info', 'body' => convert_html($body));
31 }
32
33 function plugin_showrss_convert()
34 {
35         static $_xml;
36
37         if (! isset($_xml)) $_xml = extension_loaded('xml');
38         if (! $_xml) return '#showrss: xml extension is not found<br />' . "\n";
39
40         $num = func_num_args();
41         if ($num == 0) return PLUGIN_SHOWRSS_USAGE . '<br />' . "\n";
42
43         $argv = func_get_args();
44         $timestamp = FALSE;
45         $cachehour = 0;
46         $template = $uri = '';
47         switch ($num) {
48         case 4: $timestamp = (trim($argv[3]) == '1');   /*FALLTHROUGH*/
49         case 3: $cachehour = trim($argv[2]);            /*FALLTHROUGH*/
50         case 2: $template  = strtolower(trim($argv[1]));/*FALLTHROUGH*/
51         case 1: $uri       = trim($argv[0]);
52         }
53
54         $class = ($template == '' || $template == 'default') ? 'ShowRSS_html' : 'ShowRSS_html_' . $template;
55         if (! is_numeric($cachehour))
56                 return '#showrss: Cache-lifetime seems not numeric: ' . htmlspecialchars($cachehour) . '<br />' . "\n";
57         if (! class_exists($class))
58                 return '#showrss: Template not found: ' . htmlspecialchars($template) . '<br />' . "\n";
59         if (! is_url($uri))
60                 return '#showrss: Seems not URI: ' . htmlspecialchars($uri) . '<br />' . "\n";
61
62         list($rss, $time) = plugin_showrss_get_rss($uri, $cachehour);
63         if ($rss === FALSE) return '#showrss: Failed fetching RSS from the server<br />' . "\n";
64
65         $time = '';
66         if ($timestamp > 0) {
67                 $time = '<p style="font-size:10px; font-weight:bold">Last-Modified:' .
68                         get_date('Y/m/d H:i:s', $time) .  '</p>';
69         }
70
71         $obj = new $class($rss);
72         return $obj->toString($time);
73 }
74
75 // Create HTML from RSS array()
76 class ShowRSS_html
77 {
78         var $items = array();
79         var $class = '';
80
81         function ShowRSS_html($rss)
82         {
83                 foreach ($rss as $date=>$items) {
84                         foreach ($items as $item) {
85                                 $link  = $item['LINK'];
86                                 $title = $item['TITLE'];
87                                 $passage = get_passage($item['_TIMESTAMP']);
88                                 $link = '<a href="' . $link . '" title="' .  $title . ' ' .
89                                         $passage . '" rel="nofollow">' . $title . '</a>';
90                                 $this->items[$date][] = $this->format_link($link);
91                         }
92                 }
93         }
94
95         function format_link($link)
96         {
97                 return $link . '<br />' . "\n";
98         }
99
100         function format_list($date, $str)
101         {
102                 return $str;
103         }
104
105         function format_body($str)
106         {
107                 return $str;
108         }
109
110         function toString($timestamp)
111         {
112                 $retval = '';
113                 foreach ($this->items as $date=>$items)
114                         $retval .= $this->format_list($date, join('', $items));
115                 $retval = $this->format_body($retval);
116                 return <<<EOD
117 <div{$this->class}>
118 $retval$timestamp
119 </div>
120 EOD;
121         }
122 }
123
124 class ShowRSS_html_menubar extends ShowRSS_html
125 {
126         var $class = ' class="small"';
127
128         function format_link($link) {
129                 return '<li>' . $link . '</li>' . "\n";
130         }
131
132         function format_body($str) {
133                 return '<ul class="recent_list">' . "\n" . $str . '</ul>' . "\n";
134         }
135 }
136
137 class ShowRSS_html_recent extends ShowRSS_html
138 {
139         var $class = ' class="small"';
140
141         function format_link($link) {
142                 return '<li>' . $link . '</li>' . "\n";
143         }
144
145         function format_list($date, $str) {
146                 return '<strong>' . $date . '</strong>' . "\n" .
147                         '<ul class="recent_list">' . "\n" . $str . '</ul>' . "\n";
148         }
149 }
150
151 // Get and save RSS
152 function plugin_showrss_get_rss($target, $cachehour)
153 {
154         $buf  = '';
155         $time = NULL;
156         if ($cachehour) {
157                 // Remove expired cache
158                 plugin_showrss_cache_expire($cachehour);
159
160                 // Get the cache not expired
161                 $filename = CACHE_DIR . encode($target) . '.tmp';
162                 if (is_readable($filename)) {
163                         $buf  = join('', file($filename));
164                         $time = filemtime($filename) - LOCALZONE;
165                 }
166         }
167
168         if ($time === NULL) {
169                 // Newly get RSS
170                 $data = http_request($target);
171                 if ($data['rc'] !== 200)
172                         return array(FALSE, 0);
173
174                 $buf = $data['data'];
175                 $time = UTIME;
176
177                 // Save RSS into cache
178                 if ($cachehour) {
179                         $fp = fopen($filename, 'w');
180                         fwrite($fp, $buf);
181                         fclose($fp);
182                 }
183         }
184
185         // Parse
186         $obj = new ShowRSS_XML();
187         return array($obj->parse($buf),$time);
188 }
189
190 // Remove cache if expired limit exeed
191 function plugin_showrss_cache_expire($cachehour)
192 {
193         $expire = $cachehour * 60 * 60; // Hour
194         $dh = dir(CACHE_DIR);
195         while (($file = $dh->read()) !== FALSE) {
196                 if (substr($file, -4) != '.tmp') continue;
197                 $file = CACHE_DIR . $file;
198                 $last = time() - filemtime($file);
199                 if ($last > $expire) unlink($file);
200         }
201         $dh->close();
202 }
203
204 // Get RSS and array() them
205 class ShowRSS_XML
206 {
207         var $items;
208         var $item;
209         var $is_item;
210         var $tag;
211         var $encoding;
212
213         function parse($buf)
214         {
215                 $this->items   = array();
216                 $this->item    = array();
217                 $this->is_item = FALSE;
218                 $this->tag     = '';
219
220                 // Detect encoding
221                 $this->encoding = mb_detect_encoding($buf);
222                 if (! in_array(strtolower($this->encoding), array('us-ascii', 'iso-8859-1', 'utf-8'))) {
223                         $buf = mb_convert_encoding($buf, 'utf-8', $this->encoding);
224                         $this->encoding = 'utf-8';
225                 }
226
227                 // Parsing
228                 $xml_parser = xml_parser_create($this->encoding);
229                 xml_set_element_handler($xml_parser, array(& $this, 'start_element'), array(& $this, 'end_element'));
230                 xml_set_character_data_handler($xml_parser, array(& $this, 'character_data'));
231                 if (! xml_parse($xml_parser, $buf, 1)) {
232                         return(sprintf('XML error: %s at line %d in %s',
233                                 xml_error_string(xml_get_error_code($xml_parser)),
234                                 xml_get_current_line_number($xml_parser), $buf));
235                 }
236                 xml_parser_free($xml_parser);
237
238                 return $this->items;
239         }
240
241         function escape($str)
242         {
243                 // Unescape already-escaped chars (&lt;, &gt;, &amp;, ...) in RSS body before htmlspecialchars()
244                 $str = strtr($str, array_flip(get_html_translation_table(ENT_COMPAT)));
245                 // Escape
246                 $str = htmlspecialchars($str);
247                 // Encoding conversion
248                 $str = mb_convert_encoding($str, SOURCE_ENCODING, $this->encoding);
249                 return trim($str);
250         }
251
252         // Tag start
253         function start_element($parser, $name, $attrs)
254         {
255                 if ($this->is_item) {
256                         $this->tag     = $name;
257                 } else if ($name == 'ITEM') {
258                         $this->is_item = TRUE;
259                 }
260         }
261
262         // Tag end
263         function end_element($parser, $name)
264         {
265                 if (! $this->is_item || $name != 'ITEM') return;
266
267                 $item = array_map(array(& $this, 'escape'), $this->item);
268                 $this->item = array();
269
270                 if (isset($item['DC:DATE'])) {
271                         $time = plugin_showrss_get_timestamp($item['DC:DATE']);
272                         
273                 } else if (isset($item['PUBDATE'])) {
274                         $time = plugin_showrss_get_timestamp($item['PUBDATE']);
275                         
276                 } else if (isset($item['DESCRIPTION']) &&
277                         ($description = trim($item['DESCRIPTION'])) != '' &&
278                         ($time = strtotime($description)) != -1) {
279                                 $time -= LOCALZONE;
280
281                 } else {
282                         $time = time() - LOCALZONE;
283                 }
284                 $item['_TIMESTAMP'] = $time;
285                 $date = get_date('Y-m-d', $item['_TIMESTAMP']);
286
287                 $this->items[$date][] = $item;
288                 $this->is_item        = FALSE;
289         }
290
291         function character_data($parser, $data)
292         {
293                 if (! $this->is_item) return;
294                 if (! isset($this->item[$this->tag])) $this->item[$this->tag] = '';
295                 $this->item[$this->tag] .= $data;
296         }
297 }
298
299 function plugin_showrss_get_timestamp($str)
300 {
301         $str = trim($str);
302         if ($str == '') return UTIME;
303
304         $matches = array();
305         if (preg_match('/(\d{4}-\d{2}-\d{2})T(\d{2}:\d{2}:\d{2})(([+-])(\d{2}):(\d{2}))?/', $str, $matches)) {
306                 $time = strtotime($matches[1] . ' ' . $matches[2]);
307                 if ($time == -1) {
308                         $time = UTIME;
309                 } else if ($matches[3]) {
310                         $diff = ($matches[5] * 60 + $matches[6]) * 60;
311                         $time += ($matches[4] == '-' ? $diff : -$diff);
312                 }
313                 return $time;
314         } else {
315                 $time = strtotime($str);
316                 return ($time == -1) ? UTIME : $time - LOCALZONE;
317         }
318 }
319 ?>