NP_Headerを改造してみた
NP_Headerを導入してみたものの・・・
- bulkfeeds の APIを使った形態素解析が動かない
- plugin_headerのレコードが記事にアクセスされるたび、REPLACEクエリで削除→登録されるオーバーヘッドが発生する
などの問題があったので、プログラムを改造してみました。
久しぶりにPHPのコードを触ったので苦労しましたが、期待した結果を得られることができました。
形態素解析にはYahooのWEB APIを使いました。
※利用するにはアプリケーションIDの登録が必要です。
<?php /** * NP_Header - Sends some HTTP headers, and outputs meta elements for Nucleus CMS * * Copyright (c) 2004-2008, kalm * All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * @package NP_Header * @author kalm * @copyright 2004-2008 kalm, 2009 admin * @license http://opensource.org/licenses/gpl-license.php GNU General Public License * @version 2.0 * @require PHP 4.3.0 and higher, Nucleus CMS 2.5 and higher */ /** * @ignore */ defined('PHP_EOL') || define('PHP_EOL', "n"); /** * NP_Header class. */ class NP_Header extends NucleusPlugin { /** * Returns the name of the plugin. * * @access public * @return string */ function getName() { return 'Header'; } /** * Returns the name of author of the plugin. * * @access public * @return string */ function getAuthor() { return 'kalm'; } /** * Returns the URL of the site where the plugin can be downloaded. * * @access public * @return string */ function getURL() { return 'http://funktor.org/'; } /** * Returns a longer description of the plugin. * * @access public * @return string */ function getDescription() { return 'Sends some HTTP headers, and outputs meta elements.'; } /** * Returns the current version of the plugin. * * @access public * @return string */ function getVersion() { return '2.03'; } /** * Returns the minimum required Nucleus CMS version. * * @access public * @return string */ function getMinNucleusVersion() { return '250'; } /** * Checks if a plugin supports a certain feature. * * @access public * @param string $feature name of the feature * @return int 1 if the feature is reported; 0 otherwise */ function supportsFeature($what) { switch ($what) { case 'SqlTablePrefix': return 1; default: return 0; } } /** * This method should return an array of database tables that the plugin has created. * * @access public * @return array */ function getTableList() { return array(sql_table('plugin_header')); } /** * Plugins can subscribe to events. * * @access public * @return array */ function getEventList() { return array('PreSendContentType', 'PostAddItem', 'PreUpdateItem', 'PostAddComment', 'PostPluginOptionsUpdate', 'AddItemFormExtras', 'EditItemFormExtras', 'PreDeleteItem', 'PostDeleteCategory', 'PostDeleteBlog', 'PostDeleteComment', 'PreDeletePlugin', 'PostAddCategory', 'PostAddPlugin', 'PostMoveItem', 'PostMoveCategory'); } /** * This method gets called on the moment the plugin is installed. * * @access public */ function install() { $this->createOption('use_sitename', 'Use site name instead of blog name to Title?', 'yesno', 'no'); $this->createOption('no_cache', 'No cache', 'yesno', 'no'); $this->createOption('use_yahoo_webapi', 'Acquire keywords automatically, with using Yahoo WEB API', 'yesno', 'no'); $this->createOption('max_keywords', 'Max keywords acquired with Yahoo WEB API', 'text', '8'); $this->createBlogOption('tpl_http', 'HTTP headers template', 'textarea', 'Content-Type: text/html; charset=<%charset%>' . PHP_EOL . 'Content-Script-Type: text/javascript' . PHP_EOL . 'Content-Style-Type: text/css' . PHP_EOL . '<%if(language)%>Content-Language: <%language%><%endif%>' . PHP_EOL . 'Last-Modified: <%last_modified%>' . PHP_EOL . 'X-Generator: <%version%>'); $this->createBlogOption('tpl_html', 'HTML template (e.g. meta elements)', 'textarea', '<%if(title)%><meta name="Title" content="<%title%>" /><%endif%>' . PHP_EOL . '<%if(description)%><meta name="Description" content="<%description%>" /><%endif%>' . PHP_EOL . '<%if(keywords)%><meta name="Keywords" content="<%keywords%>" /><%endif%>' . PHP_EOL . '<meta name="Classification" content="General" />' . PHP_EOL . '<meta name="Rating" content="General" />' . PHP_EOL . '<meta name="Doc-Class" content="Living Document" />' . PHP_EOL . '<meta name="Revisit-After" content="7 days" />' . PHP_EOL . '<%if(robots)%><meta name="Robots" content="<%robots%>" /><%endif%>' . PHP_EOL . '<meta name="Generator" content="<%version%>" />'); $this->createBlogOption('language', 'Default language (ISO 639 language codes)', 'text', 'ja'); $this->createBlogOption('keywords', 'Default keywords (comma-separated)', 'text'); $this->createBlogOption('robots', 'Default robots control value', 'text', 'ALL'); $this->createBlogOption('yahoo_appid', 'Yahoo WEB APP ID', 'text', ''); $this->createOption('del_uninstall', 'Delete a table on uninstall?', 'yesno', 'no'); sql_query('CREATE TABLE IF NOT EXISTS ' . sql_table('plugin_header') . " ( `itemid` int(11) NOT NULL, `description` varchar(255) NOT NULL default '', `keywords` varchar(255) NOT NULL default '', `language` varchar(255) NOT NULL default '', `last_modified` datetime NOT NULL default '0000-00-00 00:00:00', `modifier` varchar(255) NOT NULL default '', PRIMARY KEY (itemid)) "); /* for compatibility with NP_Header 1.21 and lower */ if (mysql_num_rows(sql_query('SHOW TABLES LIKE '' . sql_table('plugin_meta_keywords') . ''')) > 0) { $result = sql_query('SELECT * FROM ' . sql_table('plugin_meta_keywords')); while (($row = mysql_fetch_object($result)) !== FALSE) { $this->_setItemKeywords($row->itemid, $row->keywords); } sql_query('DROP TABLE ' . sql_table('plugin_meta_keywords')); } } /** * Called when the plugin is uninstalled. * * @access public */ function unInstall() { if ($this->getOption('del_uninstall') == 'yes') { sql_query('DROP TABLE IF EXISTS ' . sql_table('plugin_header')); } $this->deleteOption('del_uninstall'); $this->deleteBlogOption('tpl_http'); $this->deleteBlogOption('tpl_html'); $this->deleteBlogOption('language'); $this->deleteBlogOption('keywords'); $this->deleteBlogOption('robots'); $this->deleteBlogOption('yahoo_appid'); $this->deleteOption('use_sitename'); $this->deleteOption('no_cache'); $this->deleteOption('use_yahoo_webapi'); $this->deleteOption('max_keywords'); } /** * When a plugin wants to allow user interaction, it can allow actions through action.php. * * @access public * @param string $type an optional message type */ function doAction($type) { if (($type == 'lirs') || ($type == 'lirs_gzip')) { /** * Outputs LIRS (Last modified Information Relaying Specification) * @see http://aniki.haun.org/natsu/LIRS.html */ global $manager, $nucleus; $time = 0; $localzone = date('Z'); $utime = time() - $localzone; $length = 0; $uri = ''; $title = ''; $modifier = ''; $result = sql_query('SELECT * FROM ' . sql_table('plugin_header') . ' ORDER BY last_modified DESC LIMIT 1'); if (mysql_num_rows($result) > 0) { $row = mysql_fetch_object($result); $itemid = $row->itemid; $time = strtotime($row->last_modified); $modifier = $row->modifier; $blog =& $manager->getBlog(getBlogIDFromItemID($itemid)); if ($blog) { $uri = $blog->getURL(); $length = strlen(@file_get_contents($uri)); $title = $blog->getName(); } } $content = 'LIRS,' . $time . ',' . $utime . ',' . $localzone . ',' . $length . ',' . $uri . ',' . $title . ',' . $modifier . ',' . $uri . ',Nucleus CMS ' . $nucleus['version'] . ",n"; $content = mb_convert_encoding($content, 'EUC-JP', _CHARSET); if ($type == 'lirs_gzip') { $content = gzcompress($content); } echo $content; } } /** * When plugins are called using the <%plugin(...)%>-skinvar, this method will be called. * * @access public * @param string $skinType the type of skin */ function doSkinVar($skinType) { global $CONF, $manager, $blog, $itemid; if (!empty($blog)) { $b =& $blog; } else { $b =& $manager->getBlog($CONF['DefaultBlog']); } $blogid = $b->getID(); $language = $this->getBlogOption($blogid, 'language'); $last_modified = $this->_getLastModified(); if (($this->getOption('use_sitename') == 'yes') && isset($CONF['SiteName'])) { $title = $CONF['SiteName']; } else { $title = $b->getName(); } switch ($skinType) { case 'item': $this->_checkItemData($itemid); $language = $this->_getItemLanguage($itemid); $description = $this->_getItemDescription($itemid); $keywords = $this->_getItemKeywords($itemid); break; case 'search': case 'error': case 'imagepopup': $description = ''; $keywords = 'DONOTSEARCH'; break; default: $description = $b->getDescription(); $keywords = $this->getBlogOption($blogid, 'keywords'); break; } $robots = $this->getBlogOption($blogid, 'robots'); if ($keywords == 'DONOTSEARCH') { $keywords = ''; $robots = 'NOINDEX'; } $template = $this->getBlogOption($blogid, 'tpl_html'); $trans = array( '<%language%>' => $language, '<%last_modified%>' => $last_modified, '<%title%>' => $title, '<%description%>' => $description, '<%keywords%>' => $keywords, '<%robots%>' => $robots, ); echo $this->_parseTemplate($template, $trans); } /** * After adding a comment to the database. * * @access public * @param array $data comment data */ function event_PostAddComment($data) { $c = $data['comment']; $this->_setItemLastModified($c['itemid'], $c['timestamp'], $c['user']); } /** * After an item has been added to the database. * * @access public * @param array $data item data */ function event_PostAddItem($data) { global $manager; $itemid = $data['itemid']; $item =& $manager->getItem($itemid, 0, 0); if ($item) { $author =& MEMBER::createFromID($item['authorid']); $this->_setItemData($itemid, requestVar('item_description'), requestVar('item_keywords'), requestVar('item_language'), $item['timestamp'], $author->getRealName()); } } /** * Immediately before an item gets updates in the database. * * @access public * @param array $data item data */ function event_PreUpdateItem($data) { global $manager; $itemid = $data['itemid']; $item =& $manager->getItem($itemid, 0, 0); if ($item) { $author =& MEMBER::createFromID($item['authorid']); $this->_setItemData($itemid, requestVar('item_description'), requestVar('item_keywords'), requestVar('item_language'), time(), $author->getRealName()); } } /** * Somewhere inside the add item page or bookmarklet. * * @access public * @param array $data data */ function event_AddItemFormExtras($data) { echo '<h3>' . $this->getName() . '</h3> <p><label for="item_description">Item description</label>:<br /> <input type="text" name="item_description" id="item_description" size="60" maxlength="255" value="" /></p> <p><label for="item_keywords">Item keywords (comma-separated), or "DONOTSEARCH"</label>:<br /> <input type="text" name="item_keywords" id="item_keywords" size="60" maxlength="255" value="" /></p> <p><label for="item_language">Item language (ISO 639 language codes; when differing from blog)</label>:<br /> <input type="text" name="item_language" id="item_language" size="10" maxlength="5" value="" /></p>'; } /** * Somewhere inside the edit item page or bookmarklet. * * @access public * @param array $data data */ function event_EditItemFormExtras($data) { echo '<h3>' . $this->getName() . '</h3> <p><label for="item_description">Item description</label>:<br /> <input type="text" name="item_description" id="item_description" size="60" maxlength="255" value="' . $this->_getItemDescription($data['itemid']) . '" /></p> <p><label for="item_keywords">Item keywords (comma-separated), or "DONOTSEARCH"</label>:<br /> <input type="text" name="item_keywords" id="item_keywords" size="60" maxlength="255" value="' . $this->_getItemKeywords($data['itemid']) . '" /></p> <p><label for="item_language">Item language (ISO 639 language codes; when differing from blog)</label>:<br /> <input type="text" name="item_language" id="item_language" size="10" maxlength="5" value="' . $this->_getItemLanguage($data['itemid']) . '" /></p>'; } /** * After the options for a plugin have been updated. * * @access public * @param array $data plugin options data */ function event_PostPluginOptionsUpdate($data) { $this->_setLastModified(); } /** * Immediately before an item gets deleted in the database. * * @access public * @param int $itemid itemid */ function event_PreDeleteItem($itemid) { $this->_setLastModified(); } /** * Immediately after a category has been deleted from the database. * * @access public * @param int $catid catid */ function event_PostDeleteCategory($catid) { $this->_setLastModified(); } /** * Immediately after a blog has been deleted from the database. * * @access public * @param int $blogid blogid */ function event_PostDeleteBlog($blogid) { $this->_setLastModified(); } /** * Immediately after a comment has been deleted from the database. * * @access public * @param int $commentid commentid */ function event_PostDeleteComment($commentid) { $this->_setLastModified(); } /** * Immediately after a plugin has been deleted from the database. * * @access public * @param int $plugid plugid */ function event_PreDeletePlugin($plugid) { $this->_setLastModified(); } /** * Immediately after a new category has been created in the database. * * @access public * @param array $data category data */ function event_PostAddCategory($data) { $this->_setLastModified(); } /** * Immediately after a plugin has been added. * * @access public * @param array $data plugin data */ function event_PostAddPlugin($data) { $this->_setLastModified(); } /** * Immediately after an item has been moved to another blog/category. * * @access public * @param array $data item data */ function event_PostMoveItem($data) { $this->_setLastModified(); } /** * Immediately after a catgeory has been moved to another blog. * * @access public * @param array $data catgeory data */ function event_PostMoveCategory($data) { $this->_setLastModified(); } /** * Immediately before a content type is being set in the HTTP header. * * @access public * @param array $data data */ function event_PreSendContentType(&$data) { if (headers_sent() || ($data['pageType'] != 'skin') || (strtolower(substr(php_sapi_name(), 0, 3)) == 'cgi')) { return; } $template = $this->getBlogOption($blogid, 'tpl_http'); if (!empty($template)) { global $CONF, $manager, $blog, $itemid; if (!empty($blog)) { $b =& $blog; } else { $b =& $manager->getBlog($CONF['DefaultBlog']); } $blogid = $b->getID(); $language = $this->getBlogOption($blogid, 'language'); $last_modified = $this->_getLastModified(); if ($itemid) { $language = $this->_getItemLanguage($itemid); } $trans = array( '<%language%>' => $language, '<%last_modified%>' => $last_modified, ); $template = $this->_parseTemplate($template, $trans); $template = preg_split('/r?n/', $template, -1, PREG_SPLIT_NO_EMPTY); foreach ($template as $h) header($h); } if ($this->getOption('no_cache') == 'yes') { $last_modified = gmdate('D, d M Y H:i:s'); header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); header('Last-Modified:' . $last_modified); header('ETag: ' . md5($last_modified)); header('Cache-Control: no-store, no-cache, must-revalidate'); header('Cache-Control: post-check=0, pre-check=0', FALSE); header('Pragma: no-cache'); } else { global $itemid; if (!empty($itemid)) { $last_modified = $this->_getItemLastModified($itemid); } else { $last_modified = $this->_getLastModified(); } $this->_doConditionalGet($last_modified); } } /** * Check item data Records. * * @access private * @param int $itemid itemid */ function _checkItemData($itemid) { global $manager; $result = sql_query('SELECT itemid FROM ' . sql_table('plugin_header') . ' WHERE (itemid = '' . ((int)$itemid) . '')'); if (!mysql_num_rows($result)) { $item =& $manager->getItem($itemid, 0, 0); sql_query('INSERT INTO ' . sql_table('plugin_header') . ' (itemid, last_modified) VALUES ('' . ((int)$itemid) . '', '' . mysqldate($item['timestamp']) . '')'); } } /** * Sets item data. * * @access private * @param int $itemid itemid * @param string $description description * @param string $keywords keywords * @param string $language language * @param int $last_modified last_modified */ function _setItemData($itemid, $description, $keywords, $language, $last_modified, $modifier) { sql_query('REPLACE INTO ' . sql_table('plugin_header') . ' (itemid, description, keywords, language, last_modified, modifier) VALUES ('' . ((int)$itemid) . '', '' . addslashes($description) . '', '' . addslashes($keywords) . '', '' . addslashes($language) . '', ' . mysqldate($last_modified) . ', '' . addslashes($modifier) . '')'); } /** * Sets item description. * * @access private * @param int $itemid itemid * @param string $description description */ function _setItemDescription($itemid, $description) { sql_query('UPDATE ' . sql_table('plugin_header') . ' SET description = '' . addslashes($description) . ''' . ' WHERE (itemid = '' . ((int)$itemid) . '')'); } /** * Gets item description. * * @access private * @param int $itemid itemid * @return string */ function _getItemDescription($itemid) { global $manager, $blog; $description = ''; $result = sql_query('SELECT description FROM ' . sql_table('plugin_header') . ' WHERE (itemid = '' . ((int)$itemid) . '')'); if (mysql_num_rows($result) > 0) { $row = mysql_fetch_object($result); $description = $row->description; } if (!empty($description)) { return $description; } $item =& $manager->getItem($itemid, 0, 1); if (!empty($item)) { $description = strip_tags($item['body']); $description = preg_replace('/[rnt�40]+/', ' ', $description); $description = shorten($description, 250, ' ...'); $description = htmlspecialchars($description); $this->_setItemDescription($itemid, $description); return $description; } if (!empty($blog)) { $b =& $blog; } else { $b =& $manager->getBlog($CONF['DefaultBlog']); } return $b->getDescription(); } /** * Sets item keywords. * * @access private * @param int $itemid itemid * @param string $keywords keywords */ function _setItemKeywords($itemid, $keywords) { sql_query('UPDATE ' . sql_table('plugin_header') . ' SET keywords = '' . addslashes($keywords) . ''' . ' WHERE (itemid = '' . ((int)$itemid) . '')'); } /** * Gets item keywords. * * @access private * @param int $itemid itemid * @return string */ function _getItemKeywords($itemid) { global $CONF, $manager, $blog; $keywords = ''; $result = sql_query('SELECT keywords FROM ' . sql_table('plugin_header') . ' WHERE (itemid = '' . ((int)$itemid) . '')'); if (mysql_num_rows($result) > 0) { $row = mysql_fetch_object($result); $keywords = $row->keywords; } if (!empty($keywords)) return $keywords; if ($this->getOption('use_yahoo_webapi') == 'yes') { if (!empty($blog)) { $b =& $blog; } else { $b =& $manager->getBlog($CONF['DefaultBlog']); } $blogid = $b->getID(); $item =& $manager->getItem($itemid, 0, 1); if (!empty($item)) { $url = 'http://jlp.yahooapis.jp/MAService/V1/parse'; $appid = $this->getBlogOption($blogid, 'yahoo_appid'); $sentence = strip_tags($item['body'] . $item['more']); $data = array( 'appid' => $appid, 'results' => 'uniq', 'uniq_filter' => '9', 'sentence' => $sentence, ); $options = array('http' => array( 'method' => 'POST', 'header' => 'Content-Type: application/x-www-form-urlencoded', 'content' => http_build_query($data) )); $result = file_get_contents($url, false, stream_context_create($options)); if ( $ResultSet = simplexml_load_string($result) ) { foreach ($ResultSet->uniq_result->word_list->word as $val) { $surface = (string)$val->surface; $items[$surface] = (int)$val->count; } arsort($items); for($i=0; $i < $this->getOption('max_keywords') && current($items); $i++) { $words[] = key($items); next($items); } if (count($words) > 0) { $keywords = implode(', ', $words); $this->_setItemKeywords($itemid, $keywords); return $keywords; } } } } return $this->getBlogOption(getBlogIDFromItemID($itemid), 'keywords'); } /** * Sets item language. * * @access private * @param int $itemid itemid * @param string $language language */ function _setItemLanguage($itemid, $language) { sql_query('UPDATE ' . sql_table('plugin_header') . ' SET language = '' . addslashes($language) . ''' . ' WHERE (itemid = '' . ((int)$itemid) . '')'); } /** * Gets item language. * * @access private * @param int $itemid itemid * @return string */ function _getItemLanguage($itemid) { global $CONF, $manager, $blog; $language = ''; $result = sql_query('SELECT language FROM ' . sql_table('plugin_header') . ' WHERE (itemid = '' . ((int)$itemid) . '')'); if (mysql_num_rows($result) > 0) { $row = mysql_fetch_object($result); $language = $row->language; } if (empty($language)) { if (!empty($blog)) { $b =& $blog; } else { $b =& $manager->getBlog($CONF['DefaultBlog']); } $blogid = $b->getID(); $language = $this->getBlogOption($blogid, 'language'); if ( $language ) $this->_setItemLanguage($itemid, $language); } return $language; } /** * Sets item last modified. * * @access private * @param int $itemid itemid * @param int $time time * @param string $modifier modifier */ function _setItemLastModified($itemid, $time = -1, $modifier) { if ($time == -1) $time = time(); sql_query('UPDATE ' . sql_table('plugin_header') . ' SET last_modified = ' . mysqldate($time) . ', modifier = '' . addslashes($modifier) . ''' . ' WHERE (itemid = '' . ((int)$itemid) . '')'); } /** * Gets item last modified. * * @access private * @param int $itemid itemid * @return int */ function _getItemLastModified($itemid) { $result = sql_query('SELECT * FROM ' . sql_table('plugin_header') . ' WHERE (itemid = '' . ((int)$itemid) . '')'); if (mysql_num_rows($result) > 0) { $row = mysql_fetch_object($result); return strtotime($row->last_modified); } return time(); } /** * Sets last modified information. * * @access public * @param int $time time * @param string $modifier modifier */ function _setLastModified($time = -1, $modifier = '') { if ($time == -1) $time = time(); $query = 'UPDATE ' . sql_table('plugin_header') . ' SET last_modified = ' . mysqldate($time); if (!empty($modifier)) { $query .= ', modifier = '' . addslashes($modifier) . '''; } sql_query($query); } /** * Gets last modified information. * * @access public * @return int timestamp */ function _getLastModified() { $result = sql_query('SELECT * FROM ' . sql_table('plugin_header') . ' ORDER BY last_modified DESC LIMIT 1'); if (mysql_num_rows($result) > 0) { $row = mysql_fetch_object($result); return strtotime($row->last_modified); } return time(); } /** * A PHP implementation of conditional get, see * @link http://fishbowl.pastiche.org/archives/001132.html * * @access private * @param int $last_modified last modified */ function _doConditionalGet($last_modified) { $last_modified = gmdate('D, d M Y H:i:s', $last_modified); $etag = '"' . md5($last_modified) . '"'; header('Last-Modified: ' . $last_modified); header('ETag: ' . $etag); $if_modified_since = stripslashes(serverVar('HTTP_IF_MODIFIED_SINCE')); $if_none_match = stripslashes(serverVar('HTTP_IF_NONE_MATCH')); if (empty($if_modified_since) && empty($if_none_match)) return; if (!empty($if_none_match) && ($if_none_match != $etag)) return; if (!empty($if_modified_since) && ($if_modified_since != $last_modified)) return; header('HTTP/1.1 304 Not Modified'); exit; } /** * Parses header template. * * @access private * @param string $template template * @param array $vars additional variables * @return string parsed template */ function _parseTemplate($template, $vars) { global $nucleus; $trans = array_merge(array( '<%charset%>' => _CHARSET, '<%version%>' => 'Nucleus CMS ' . $nucleus['version'] ), $vars); $template = preg_replace('/<%if((w+))%>n?((?:(?>(?:(?!(<%if(w+)%>|<%endif%>)).)*)|(?R))*)<%endif%>n?/se', 'str_replace('\\"','"',(!isset($trans['<%\1%>'])||empty($trans['<%\1%>']))?'':'\2')', $template); return strtr($template, $trans); } } ?>