Created
January 1, 2011 23:15
-
-
Save masao/762100 to your computer and use it in GitHub Desktop.
PukiWiki plugin for J-STAGE API
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/* | |
* J-STAGE API plugin | |
*/ | |
define('PLUGIN_JSTAGE_USAGE', '#jstage(issn=xxxx-xxxx)'); | |
define('JSTAGE_API_BASEURL', 'http://api.jstage.jst.go.jp/searchapi/do'); | |
define('DEFAULT_HEADING_LEVEL', 2); | |
function plugin_jstage_convert() | |
{ | |
static $_xml; | |
if (! isset($_xml)) $_xml = extension_loaded('xml'); | |
if (! $_xml) return '#jstage: xml extension is not found<br />' . "\n"; | |
$num = func_num_args(); | |
if ($num == 0) return PLUGIN_JSTAGE_USAGE .'<br />' . "\n"; | |
$argv = func_get_args(); | |
$params = trim($argv[0]); | |
if (strlen($params) == 0) return ''. PLUGIN_JSTAGE_USAGE . '<br />' . "\n"; | |
//return encode($params); | |
$timestamp = FALSE; | |
$cachehour = 24; // cache expires after a day (default) | |
$level = DEFAULT_HEADING_LEVEL; | |
if ($num > 1 && intval($argv[1]) > 0) { | |
$level = intval($argv[1]); | |
} | |
list($xml, $time) = plugin_jstage_get_xml($params, $cachehour); | |
$time = ''; | |
if ($timestamp > 0) { | |
$time = '<p style="font-size:10px; font-weight:bold">Last-Modified:' . | |
get_date('Y/m/d H:i:s', $time) . '</p>'; | |
} | |
$obj = new JSTAGE_TOC_html($xml, $level); | |
return $obj->toString($time); | |
} | |
class JSTAGE_TOC_html | |
{ | |
var $items = array(); | |
var $class = ''; | |
var $level; | |
function JSTAGE_TOC_html($entry, $level) | |
{ | |
$this->level = $level; | |
foreach ($entry as $item) { | |
$link = $item['ID']; | |
$title = $item['TITLE']; | |
$volume = $item['PRISM:VOLUME']; | |
$number = $item['PRISM:NUMBER']; | |
$spage = $item['PRISM:STARTINGPAGE']; | |
$link = '<a href="'. $link .'" title="'. $title .'" rel="nofollow">'. "$title</a> - $spage<br />\n"; | |
if (! is_array($this->items[$volume])) | |
$this->items[$volume] = array(); | |
if (! is_array($this->items[$volume][$number])) | |
$this->items[$volume][$number] = array(); | |
$this->items[$volume][$number][$spage] = $link; | |
} | |
} | |
function toString($timestamp) | |
{ | |
$retval = ''; | |
$volumes = array_keys($this->items); | |
rsort($volumes); | |
foreach ($volumes as $volume) { | |
$retval .= "<h". $this->level .">Vol.$volume</h". $this->level .">\n"; | |
$numbers = array_keys($this->items[$volume]); | |
sort($numbers); | |
$next_level = $this->level + 1; | |
foreach ($numbers as $number) { | |
$retval .= "<h$next_level>"; | |
$retval .= "No.$number"; | |
$retval .= "</h$next_level>"; | |
$spages = array_keys($this->items[$volume][$number]); | |
sort( $spages ); | |
foreach ($spages as $spage) { | |
$retval .= $this->items[$volume][$number][$spage]; | |
} | |
} | |
} | |
return <<<EOD | |
<div{$this->class}> | |
$retval$timestamp | |
</div> | |
EOD; | |
} | |
} | |
// Get and save J-STAGE XML | |
function plugin_jstage_get_xml($params, $cachehour) | |
{ | |
$buf = ''; | |
$time = NULL; | |
$filename = CACHE_DIR . "jstage" . encode($params) . '.tmp'; | |
if ($cachehour) { | |
// Remove expired cache | |
plugin_jstage_cache_expire($cachehour); | |
// Get the cache not expired | |
if (is_readable($filename)) { | |
$buf = join('', file($filename)); | |
$time = filemtime($filename) - LOCALZONE; | |
} | |
} | |
if ($time === NULL) { | |
// Newly get XML | |
$url = PLUGIN_JSTAGE_BASEURL .'?service=3&'. $params; | |
$url = 'http://api.jstage.jst.go.jp/searchapi/do?service=3&'. $params; | |
//return array($cachehour,0); | |
$data = http_request($url); | |
if ($data['rc'] !== 200) | |
return array(FALSE, 0); | |
$buf = $data['data']; | |
//return array($buf,0); | |
$time = UTIME; | |
// Save XML into cache | |
if ($cachehour) { | |
$fp = fopen($filename, 'w'); | |
fwrite($fp, $buf); | |
fclose($fp); | |
} | |
} | |
// Parse | |
$obj = new JSTAGE_TOC_XML(); | |
return array($obj->parse($buf),$time); | |
} | |
// Remove cache if expired limit exeed | |
function plugin_jstage_cache_expire($cachehour) | |
{ | |
$expire = $cachehour * 60 * 60; // Hour | |
$dh = dir(CACHE_DIR); | |
while (($file = $dh->read()) !== FALSE) { | |
if (substr($file, -4) != '.tmp') continue; | |
$file = CACHE_DIR . $file; | |
$last = time() - filemtime($file); | |
if ($last > $expire) unlink($file); | |
} | |
$dh->close(); | |
} | |
// Get RSS and array() them | |
class JSTAGE_TOC_XML | |
{ | |
var $items; | |
var $item; | |
var $is_item; | |
var $tag; | |
var $encoding; | |
function parse($buf) | |
{ | |
$this->items = array(); | |
$this->item = array(); | |
$this->is_item = FALSE; | |
$this->tag = ''; | |
// 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'; | |
} | |
// Parsing | |
$xml_parser = xml_parser_create($this->encoding); | |
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', | |
xml_error_string(xml_get_error_code($xml_parser)), | |
xml_get_current_line_number($xml_parser), $buf)); | |
} | |
xml_parser_free($xml_parser); | |
return $this->items; | |
} | |
function escape($str) | |
{ | |
// Unescape already-escaped chars (<, >, &, ...) in RSS body before htmlspecialchars() | |
$str = strtr($str, array_flip(get_html_translation_table(ENT_COMPAT))); | |
// Escape | |
$str = htmlspecialchars($str); | |
// 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 == 'ENTRY') { | |
$this->is_item = TRUE; | |
} | |
} | |
// Tag end | |
function end_element($parser, $name) | |
{ | |
if (! $this->is_item || $name != 'ENTRY') return; | |
$item = array_map(array(& $this, 'escape'), $this->item); | |
$this->item = array(); | |
if (isset($item['UPDATED'])) { | |
$time = plugin_jstage_get_timestamp($item['UPDATED']); | |
} else if (isset($item['PUBDATE'])) { | |
$time = plugin_jstage_get_timestamp($item['PUBDATE']); | |
} else if (isset($item['DESCRIPTION']) && | |
($description = trim($item['DESCRIPTION'])) != '' && | |
($time = strtotime($description)) != -1) { | |
$time -= LOCALZONE; | |
} else { | |
$time = time() - LOCALZONE; | |
} | |
$item['_TIMESTAMP'] = $time; | |
$date = get_date('Y-m-d', $item['_TIMESTAMP']); | |
$this->items[] = $item; | |
$this->is_item = FALSE; | |
} | |
function character_data($parser, $data) | |
{ | |
if (! $this->is_item) return; | |
if (! isset($this->item[$this->tag])) $this->item[$this->tag] = ''; | |
$this->item[$this->tag] .= $data; | |
} | |
} | |
function plugin_jstage_get_timestamp($str) | |
{ | |
$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]) { | |
$diff = ($matches[5] * 60 + $matches[6]) * 60; | |
$time += ($matches[4] == '-' ? $diff : -$diff); | |
} | |
return $time; | |
} else { | |
$time = strtotime($str); | |
return ($time == -1) ? UTIME : $time - LOCALZONE; | |
} | |
} | |
?> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment