2 // vim: tabstop=2:shiftwidth=2
\r
5 * NP_MetaTags ($Revision: 1.118 $)
\r
6 * by hsur ( http://blog.cles.jp/np_cles )
\r
10 * Copyright (C) 2005-2010 CLES. All rights reserved.
\r
12 * This program is free software; you can redistribute it and/or
\r
13 * modify it under the terms of the GNU General Public License
\r
14 * as published by the Free Software Foundation; either version 2
\r
15 * of the License, or (at your option) any later version.
\r
17 * This program is distributed in the hope that it will be useful,
\r
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
20 * GNU General Public License for more details.
\r
22 * You should have received a copy of the GNU General Public License
\r
23 * along with this program; if not, write to the Free Software
\r
24 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
\r
26 * In addition, as a special exception, cles( http://blog.cles.jp/np_cles ) gives
\r
27 * permission to link the code of this program with those files in the PEAR
\r
28 * library that are licensed under the PHP License (or with modified versions
\r
29 * of those files that use the same license as those files), and distribute
\r
30 * linked combinations including the two. You must obey the GNU General Public
\r
31 * License in all respects for all of the code used other than those files in
\r
32 * the PEAR library that are licensed under the PHP License. If you modify
\r
33 * this file, you may extend this exception to your version of the file,
\r
34 * but you are not obligated to do so. If you do not wish to do so, delete
\r
35 * this exception statement from your version.
\r
38 require_once(dirname(__FILE__).'/sharedlibs/sharedlibs.php');
\r
39 require_once("cles/AsyncHTTP.php");
\r
41 class NP_MetaTags extends NucleusPlugin {
\r
42 function getName() {
\r
45 function getAuthor() {
\r
49 return 'http://blog.cles.jp/np_cles/category/31/subcatid/4';
\r
51 function getVersion() {
\r
54 function getDescription() {
\r
55 return '[$Revision: 1.118 $]<br />This plug-in This plug-in inserts a <META> tag (robots, description, keywords), by using <%MetaTags%>';
\r
57 function getMinNucleusVersion() {
\r
60 function supportsFeature($what) {
\r
62 case 'SqlTablePrefix' :
\r
69 function hasAdminArea() {
\r
73 function getEventList() {
\r
74 return array ('PostPluginOptionsUpdate');
\r
77 function install() {
\r
78 $this->createOption('yahooAppId', 'Y!API AppId', 'text', '');
\r
79 $this->createOption('DescMaxLength', 'Description Max Length', 'text', '80');
\r
80 $this->createOption('MaxKeywords', 'Num of Max Keywords', 'text', '6');
\r
81 $this->createOption('isRefresh', 'Refresh data ?', 'yesno', 'no');
\r
82 $this->createOption('isForceRefresh', 'Force Refresh ?', 'yesno', 'no');
\r
84 $this->createItemOption('robots', 'META Robots', 'select', 'INDEX,FOLLOW', 'INDEX,FOLLOW|INDEX,FOLLOW|NOINDEX,FOLLOW|NOINDEX,FOLLOW|INDEX,NOFOLLOW|INDEX,NOFOLLOW|NOINDEX,NOFOLLOW|NOINDEX,NOFOLLOW');
\r
85 $this->createItemOption('description', 'META description', 'textarea', '');
\r
86 $this->createItemOption('keywords', 'META keywords', 'textarea', '');
\r
89 function doSkinVar($skinType, $mode = 'tags') {
\r
90 switch( $skinType ){
\r
93 $description = $this->getItemOption($itemid, 'description');
\r
94 $keywords = $this->getItemOption($itemid, 'keywords');
\r
95 $robots = $this->getItemOption($itemid, 'robots');
\r
100 if (!empty ($robots))
\r
101 echo "<meta name=\"robots\" content=\"".htmlspecialchars($robots, ENT_QUOTES)."\" />\n";
\r
102 if (!empty ($description))
\r
103 echo "<meta name=\"description\" content=\"".htmlspecialchars($description, ENT_QUOTES)."\" />\n";
\r
104 if (!empty ($keywords)){
\r
105 echo "<meta name=\"keywords\" content=\"".htmlspecialchars($keywords, ENT_QUOTES)."\" />\n";
\r
110 if (!empty ($robots))
\r
111 echo "<meta name=\"robots\" content=\"".htmlspecialchars($robots, ENT_QUOTES)."\" />\n";
\r
113 case 'description':
\r
114 if (!empty ($description))
\r
115 echo "<meta name=\"description\" content=\"".htmlspecialchars($description, ENT_QUOTES)."\" />\n";
\r
118 if (!empty ($keywords))
\r
119 echo "<meta name=\"keywords\" content=\"".htmlspecialchars($keywords, ENT_QUOTES)."\" />\n";
\r
128 global $archive, $archivetype;
\r
131 if( $archivetype == _ARCHIVETYPE_DAY ){
\r
132 sscanf($archive, '%d-%d-%d', $y, $m, $d);
\r
133 $t = mktime(0, 0, 0, $m, $d, $y);
\r
134 } elseif ( $archivetype == _ARCHIVETYPE_MONTH ) {
\r
135 sscanf($archive, '%d-%d', $y, $m);
\r
136 $t = mktime(0, 0, 0, $m, 1, $y);
\r
139 //TODO: remove hard coding.
\r
140 if( $t > $now || $t < mktime(0, 0, 0, 2, 1, 2004) )
\r
141 echo '<meta name="robots" content="NOINDEX,NOFOLLOW" />'."\n";
\r
143 echo '<meta name="robots" content="INDEX,FOLLOW" />'."\n";
\r
151 function event_PostPluginOptionsUpdate(& $data) {
\r
154 switch($data['context']){
\r
156 $affected = $this->_refreshData();
\r
159 // var_dump($data);
\r
160 // core hack needed.
\r
161 if( $data['item']['draft'] ) break;
\r
163 $item = $data['item'];
\r
164 $item['itemid'] = $data['itemid'];
\r
165 $this->_setData($item);
\r
172 function _refreshData(){
\r
173 if( $this->getOption('isRefresh') == yes ){
\r
174 ACTIONLOG :: add(INFO, 'MetaTags: Invoked refreshData()');
\r
176 $isForce = ($this->getOption('isForceRefresh') == 'yes')? true : false;
\r
177 $this->setOption('isRefresh','no');
\r
178 $this->setOption('isForceRefresh','no');
\r
181 $query = 'SELECT inumber, ibody, imore FROM '.sql_table('item').' order by inumber';
\r
182 $res = sql_query($query);
\r
183 if (! @mysql_num_rows($res) )
\r
185 while ($assoc = mysql_fetch_assoc($res)) {
\r
188 $description = $this->getItemOption($assoc['inumber'], 'description');
\r
189 if (empty($description)) {
\r
190 $description = $this->_makeDescription($assoc['ibody'].$assoc['imore']);
\r
191 $this->setItemOption($assoc['inumber'], 'description', $description);
\r
194 $description = $this->_makeDescription($assoc['ibody'].$assoc['imore']);
\r
195 $this->setItemOption($assoc['inumber'], 'description', $description);
\r
199 $keywords = $this->getItemOption($assoc['inumber'], 'keywords');
\r
200 if (empty($keywords)) {
\r
201 $keywords = $this->_getKeywords($assoc['ibody'].$assoc['imore']);
\r
202 $this->setItemOption($assoc['inumber'], 'keywords', $keywords);
\r
205 $keywords = $this->_getKeywords($assoc['ibody'].$assoc['imore']);
\r
206 $this->setItemOption($assoc['inumber'], 'keywords', $keywords);
\r
210 mysql_free_result($res);
\r
211 ACTIONLOG :: add(INFO, "MetaTags: Finished refreshData(): affected items [$affected]");
\r
216 function _setData(& $data) {
\r
217 $itemid = intval($data['itemid']);
\r
219 $this->setItemOption($itemid, 'lastupdate', time());
\r
221 $description = $this->getItemOption($itemid, 'description');
\r
222 if (empty ($description)) {
\r
223 $description = $this->_makeDescription($data['body'].$data['more']);
\r
224 $this->setItemOption($itemid, 'description', $description);
\r
227 $keywords = $this->getItemOption($itemid, 'keywords');
\r
228 if( strlen($keywords) > 100 ) $keywords = null;
\r
229 if (empty ($keywords)) {
\r
230 $keywords = $this->_getKeywords($data['body'].$data['more']);
\r
231 $this->setItemOption($itemid, 'keywords', $keywords);
\r
235 function _getKeywords($text) {
\r
236 if ( ! $this->getOption('yahooAppId') )
\r
238 $maxKeywords = $this->getOption('MaxKeywords');
\r
239 $appid = $this->getOption('yahooAppId');
\r
242 if( _CHARSET != 'UTF-8' )
\r
243 $string = mb_convert_encoding($string, 'UTF-8', _CHARSET);
\r
245 $text = strip_tags($text);
\r
246 $text = str_replace("\n", "", $text);
\r
247 $text = str_replace("\r", "", $text);
\r
249 $postData = array();
\r
250 $postData['appid'] = $appid;
\r
251 $postData['results'] = 'uniq';
\r
252 $postData['filter'] = '9';
\r
253 $postData['response'] = 'surface';
\r
254 $postData['sentence'] = $text;
\r
256 $ahttp = new cles_AsyncHTTP();
\r
257 $ahttp->asyncMode = false;
\r
258 $ahttp->userAgent = "NP_MetaTags/".$this->getVersion();
\r
259 $ahttp->setRequest('http://jlp.yahooapis.jp/MAService/V1/parse', 'POST', '', $postData);
\r
260 list($data) = $ahttp->getResponses();
\r
262 ACTIONLOG :: add(WARNING, 'NP_MetaTags: AsyncHTTP Error['.$ahttp->getErrorNo(0).']'.$ahttp->getError(0));
\r
265 $p =& new NP_MetaTags_MA_XMLParser();
\r
266 $words = $p->parse($data);
\r
268 ACTIONLOG :: add(WARNING, 'NP_MetaTags: Y!API Error( '. (isset($rawtokens[0]) ? $rawtokens[0] : 'Unknown Error -> '.$data) . ' )');
\r
276 $words = array_slice($words, 0, 20);
\r
278 $postData = array();
\r
279 $postData['appid'] = $appid;
\r
280 $postData['results'] = '1';
\r
281 $postData['adult_ok'] = '1';
\r
282 $postData['similar_ok'] = '1';
\r
283 $ahttp = new cles_AsyncHTTP();
\r
284 $ahttp->userAgent = "NP_MetaTags/".$this->getVersion();
\r
286 $requests = array();
\r
287 foreach ($words as $word => $count) {
\r
288 $postData['query'] = $word;
\r
291 foreach($postData as $k => $v){
\r
292 $qs[] = $k."=".urlencode($v);
\r
294 $u = 'http://search.yahooapis.jp/WebSearchService/V2/webSearch?'.implode("&", $qs);
\r
295 $id = $ahttp->setRequest($u, 'GET');
\r
297 $requests[$id] = $word;
\r
300 $responses = $ahttp->getResponses();
\r
302 foreach( $requests as $id => $word ){
\r
303 if( $respXml = $responses[$id] ){
\r
304 $p =& new NP_MetaTags_WS_XMLParser();
\r
305 list($totalResultsAvailable) = $p->parse($respXml);
\r
307 ACTIONLOG :: add(WARNING, 'NP_MetaTags: Y!API Error( '. (isset($totalResultsAvailable) ? $totalResultsAvailable : 'Unknown Error') . ' )');
\r
308 $totalResultsAvailable = 0;
\r
313 if( $totalResultsAvailable ){
\r
314 $tfidf[$word] = $words[$word] * log10(30000000000/$totalResultsAvailable);
\r
317 ACTIONLOG :: add(WARNING, $this->getName().': AsyncHTTP Error['.$ahttp->getErrorNo($id).']'.$ahttp->getError($id));
\r
320 //var_dump($ahttp);
\r
323 //var_dump($tfidf);
\r
326 $tfidf = array_slice($tfidf, 0, $maxKeywords);
\r
328 foreach( $tfidf as $word => $score ){
\r
329 $result .= $word.',';
\r
332 if( _CHARSET != 'UTF-8' )
\r
333 $result = mb_convert_encoding($result, _CHARSET, 'UTF-8');
\r
335 return mb_substr($result, 0, -1);
\r
338 function _makeDescription($description) {
\r
339 $maxLength = $this->getOption('DescMaxLength');
\r
340 $description = strip_tags($description);
\r
341 $description = Str_Replace("\n", "", $description);
\r
342 $description = Str_Replace("\r", "", $description);
\r
343 return htmlspecialchars(shorten($description, $maxLength, ''));
\r
347 class NP_MetaTags_Base_XMLParser {
\r
349 $this->parser = xml_parser_create('UTF-8');
\r
350 xml_set_object($this->parser, $this);
\r
351 xml_set_element_handler($this->parser, "_open", "_close");
\r
352 xml_set_character_data_handler($this->parser, "_cdata");
\r
354 $this->isError = false;
\r
355 $this->inTarget = false;
\r
358 function parse($data){
\r
359 $this->words = array();
\r
360 xml_parse($this->parser, $data);
\r
361 $errcode = xml_get_error_code($this->parser);
\r
362 if ( $errcode != XML_ERROR_NONE ) {
\r
363 $this->isError = true;
\r
364 $this->words = array();
\r
365 $this->words[] = 'XML Parse Error: ' . xml_error_string($errcode) . ' in '. xml_get_current_line_number($this->parser);
\r
367 return $this->words;
\r
371 xml_parser_free($this->parser);
\r
372 $this->words = null;
\r
376 class NP_MetaTags_MA_XMLParser extends NP_MetaTags_Base_XMLParser {
\r
377 function NP_MetaTags_MA_XMLParser(){
\r
381 function _open($parser, $name, $attribute){
\r
384 $this->tmp = array();
\r
387 $this->inTarget = 'SURFACE';
\r
390 $this->inTarget = 'COUNT';
\r
393 $this->inTarget = 'MESSAGE';
\r
396 $this->isError = true;
\r
401 function _close($parser, $name){
\r
404 if( $this->tmp['SURFACE'] && $this->tmp['COUNT'] && !is_numeric($this->tmp['SURFACE']) && mb_strlen($this->tmp['SURFACE']) > 1 ){
\r
405 $this->words[$this->tmp['SURFACE']] = $this->tmp['COUNT'];
\r
409 $this->words = array();
\r
410 $this->words[] = $this->tmp['MESSAGE'];
\r
413 if( $name == $this->inTarget ) $this->inTarget = false;
\r
416 function _cdata($parser, $data){
\r
417 if( $this->inTarget ){
\r
418 $this->tmp[$this->inTarget] = trim($data);
\r
423 class NP_MetaTags_WS_XMLParser extends NP_MetaTags_Base_XMLParser {
\r
424 function NP_MetaTags_WS_XMLParser(){
\r
428 function _open($parser, $name, $attribute){
\r
431 $this->words[] = $attribute['TOTALRESULTSAVAILABLE'];
\r
436 function _close($parser, $name){}
\r
437 function _cdata($parser, $data){}
\r