OSDN Git Service

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