// PukiWiki - Yet another WikiWikiWeb clone
// showrss.inc.php
// Copyright:
-// 2002-2016 PukiWiki Development Team
+// 2002-2017 PukiWiki Development Team
// 2002 PANDA <panda@arino.jp>
// (Original)hiro_do3ob@yahoo.co.jp
// License: GPL, same as PukiWiki
}
$class = ($template == '' || $template == 'default') ? 'ShowRSS_html' : 'ShowRSS_html_' . $template;
+ if (! class_exists($class)) $class = 'ShowRSS_html';
+
if (! is_numeric($cachehour))
return '#showrss: Cache-lifetime seems not numeric: ' . htmlsc($cachehour) . '<br />' . "\n";
- if (! class_exists($class))
- return '#showrss: Template not found: ' . htmlsc($template) . '<br />' . "\n";
if (! is_url($uri))
return '#showrss: Seems not URI: ' . htmlsc($uri) . '<br />' . "\n";
+ // Remove old caches in 5% rate
+ if (mt_rand(1, 20) === 1) {
+ plugin_showrss_cache_expire(24);
+ }
list($rss, $time) = plugin_showrss_get_rss($uri, $cachehour);
if ($rss === FALSE) return '#showrss: Failed fetching RSS from the server<br />' . "\n";
-
- $time = '';
+ if (! is_array($rss)) {
+ // Show XML error message
+ return '#showrss: Error - ' . htmlsc($rss) . '<br />' . "\n";
+ }
+ $time_display = '';
if ($timestamp > 0) {
- $time = '<p style="font-size:10px; font-weight:bold">Last-Modified:' .
+ $time_display = '<p style="font-size:10px; font-weight:bold">Last-Modified:' .
get_date('Y/m/d H:i:s', $time) . '</p>';
}
$obj = new $class($rss);
- return $obj->toString($time);
+ return $obj->toString($time_display);
}
// Create HTML from RSS array()
foreach ($items as $item) {
$link = $item['LINK'];
$title = $item['TITLE'];
- $passage = get_passage($item['_TIMESTAMP']);
- $link = '<a href="' . $link . '" title="' . $title . ' ' .
- $passage . '" rel="nofollow">' . $title . '</a>';
+ $date = get_date_atom($item['_TIMESTAMP'] + LOCALZONE);
+ $link = '<a href="' . $link . '" data-mtime="' .
+ $date . '" class="' . get_link_passage_class() .
+ '" rel="nofollow">' . $title . '</a>';
$this->items[$date][] = $this->format_link($link);
}
}
$buf = '';
$time = NULL;
if ($cachehour) {
+ $filename = CACHE_DIR . encode($target) . '.tmp';
// Remove expired cache
- plugin_showrss_cache_expire($cachehour);
-
+ plugin_showrss_cache_expire_file($filename, $cachehour);
// Get the cache not expired
- $filename = CACHE_DIR . encode($target) . '.tmp';
if (is_readable($filename)) {
$buf = join('', file($filename));
$time = filemtime($filename) - LOCALZONE;
}
}
- if ($time === NULL) {
+ if (is_null($time)) {
// Newly get RSS
$data = pkwk_http_request($target);
- if ($data['rc'] !== 200)
+ if ($data['rc'] !== 200) {
return array(FALSE, 0);
-
+ }
$buf = $data['data'];
$time = UTIME;
fclose($fp);
}
}
-
// Parse
$obj = new ShowRSS_XML();
+ $obj->modified_date = (is_null($time) ? UTIME : $time);
return array($obj->parse($buf),$time);
}
$dh->close();
}
+/**
+ * Remove single file cache if expired limit exeed
+ * @param $filename
+ * @param $cachehour
+ */
+function plugin_showrss_cache_expire_file($filename, $cachehour)
+{
+ $expire = $cachehour * 60 * 60; // Hour
+ $last = time() - filemtime($filename);
+ if ($last > $expire) {
+ unlink($filename);
+ }
+}
+
// Get RSS and array() them
class ShowRSS_XML
{
var $is_item;
var $tag;
var $encoding;
+ var $modified_date;
function parse($buf)
{
$this->item = array();
$this->is_item = FALSE;
$this->tag = '';
-
+ $utf8 = 'UTF-8';
+ $this->encoding = $utf8;
// Detect encoding
$matches = array();
if(preg_match('/<\?xml [^>]*\bencoding="([a-z0-9-_]+)"/i', $buf, $matches)) {
- $this->encoding = $matches[1];
- } else {
- $this->encoding = mb_detect_encoding($buf);
- }
-
- // Normalize to UTF-8 / ASCII
- if (! in_array(strtolower($this->encoding), array('us-ascii', 'iso-8859-1', 'utf-8'))) {
- $buf = mb_convert_encoding($buf, 'utf-8', $this->encoding);
- $this->encoding = 'utf-8';
+ $encoding = $matches[1];
+ if (strtoupper($encoding) !== $utf8) {
+ // xml_parse() fails on non UTF-8 encoding attr in XML decLaration
+ $buf = preg_replace('/<\?xml ([^>]*)\bencoding="[a-z0-9-_]+"/i', '<?xml $1', $buf);
+ // xml_parse() requires UTF-8 compatible encoding
+ $buf = mb_convert_encoding($buf, $utf8, $encoding);
+ }
}
-
// Parsing
- $xml_parser = xml_parser_create($this->encoding);
+ $xml_parser = xml_parser_create($utf8);
xml_set_element_handler($xml_parser, array(& $this, 'start_element'), array(& $this, 'end_element'));
xml_set_character_data_handler($xml_parser, array(& $this, 'character_data'));
if (! xml_parse($xml_parser, $buf, 1)) {
- return(sprintf('XML error: %s at line %d in %s',
+ return sprintf('XML error: %s at line %d in %s',
xml_error_string(xml_get_error_code($xml_parser)),
- xml_get_current_line_number($xml_parser), $buf));
+ xml_get_current_line_number($xml_parser),
+ (strlen($buf) < 500 ? $buf : substr($buf, 0, 500) . '...'));
}
xml_parser_free($xml_parser);
-
return $this->items;
}
// Unescape already-escaped chars (<, >, &, ...) in RSS body before htmlsc()
$str = strtr($str, array_flip(get_html_translation_table(ENT_COMPAT)));
// Escape
- $str = htmlsc($str);
+ $str = htmlsc($str, ENT_COMPAT, $this->encoding);
// Encoding conversion
$str = mb_convert_encoding($str, SOURCE_ENCODING, $this->encoding);
return trim($str);
// Tag start
function start_element($parser, $name, $attrs)
{
- if ($this->is_item) {
- $this->tag = $name;
- } else if ($name == 'ITEM') {
- $this->is_item = TRUE;
+ if ($this->is_item !== FALSE) {
+ $this->tag = $name;
+ if ($this->is_item === 'ENTRY' && $name === 'LINK' && isset($attrs['HREF'])) {
+ if (! isset($this->item[$name])) $this->item[$name] = '';
+ $this->item[$name] .= $attrs['HREF'];
+ }
+ } else if ($name === 'ITEM') {
+ $this->is_item = 'ITEM';
+ } else if ($name === 'ENTRY') {
+ $this->is_item = 'ENTRY';
}
}
// Tag end
function end_element($parser, $name)
{
- if (! $this->is_item || $name != 'ITEM') return;
+ if ($this->is_item === FALSE || $name !== $this->is_item) return;
$item = array_map(array(& $this, 'escape'), $this->item);
$this->item = array();
if (isset($item['DC:DATE'])) {
- $time = plugin_showrss_get_timestamp($item['DC:DATE']);
-
+ $time = plugin_showrss_get_timestamp($item['DC:DATE'], $this->modified_date);
} else if (isset($item['PUBDATE'])) {
- $time = plugin_showrss_get_timestamp($item['PUBDATE']);
-
- } else if (isset($item['DESCRIPTION']) &&
- ($description = trim($item['DESCRIPTION'])) != '' &&
- ($time = strtotime($description)) != -1) {
- $time -= LOCALZONE;
-
+ $time = plugin_showrss_get_timestamp($item['PUBDATE'], $this->modified_date);
+ } else if (isset($item['UPDATED'])) {
+ $time = plugin_showrss_get_timestamp($item['UPDATED'], $this->time);
} else {
- $time = time() - LOCALZONE;
+ $time_from_desc = FALSE;
+ if (isset($item['DESCRIPTION']) &&
+ (($description = trim($item['DESCRIPTION'])) != '')) {
+ $time_from_desc = strtotime($description);
+ }
+ if ($time_from_desc !== FALSE && $time_from_desc !== -1) {
+ $time = $time_from_desc - LOCALZONE;
+ } else {
+ $time = time() - LOCALZONE;
+ }
}
$item['_TIMESTAMP'] = $time;
$date = get_date('Y-m-d', $item['_TIMESTAMP']);
function character_data($parser, $data)
{
- if (! $this->is_item) return;
+ if ($this->is_item === FALSE) return;
if (! isset($this->item[$this->tag])) $this->item[$this->tag] = '';
$this->item[$this->tag] .= $data;
}
}
-function plugin_showrss_get_timestamp($str)
+function plugin_showrss_get_timestamp($str, $default_date)
{
$str = trim($str);
if ($str == '') return UTIME;
$matches = array();
if (preg_match('/(\d{4}-\d{2}-\d{2})T(\d{2}:\d{2}:\d{2})(([+-])(\d{2}):(\d{2}))?/', $str, $matches)) {
$time = strtotime($matches[1] . ' ' . $matches[2]);
- if ($time == -1) {
- $time = UTIME;
- } else if ($matches[3]) {
+ if ($time === FALSE || $time === -1) {
+ $time = $default_date;
+ } else if (isset($matches[3])) {
$diff = ($matches[5] * 60 + $matches[6]) * 60;
$time += ($matches[4] == '-' ? $diff : -$diff);
}
return $time;
} else {
$time = strtotime($str);
- return ($time == -1) ? UTIME : $time - LOCALZONE;
+ return ($time === FALSE || $time === -1) ? $default_date : $time - LOCALZONE;
}
}