OSDN Git Service

NP_MetaTags v1.8
[nucleus-jp/nucleus-plugins.git] / trunk / NP_MetaTags / NP_MetaTags.php
1 <?php\r
2 // vim: tabstop=2:shiftwidth=2\r
3 \r
4 /**\r
5   * NP_MetaTags ($Revision: 1.118 $)\r
6   * by hsur ( http://blog.cles.jp/np_cles )\r
7 */\r
8 \r
9 /*\r
10   * Copyright (C) 2005-2010 CLES. All rights reserved.\r
11   *\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
16   * \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
21   * \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
25   * \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
36 */\r
37 \r
38 require_once(dirname(__FILE__).'/sharedlibs/sharedlibs.php');\r
39 require_once("cles/AsyncHTTP.php");\r
40 \r
41 class NP_MetaTags extends NucleusPlugin {\r
42         function getName() {\r
43                 return 'MetaTags';\r
44         }\r
45         function getAuthor() {\r
46                 return 'hsur';\r
47         }\r
48         function getURL() {\r
49                 return 'http://blog.cles.jp/np_cles/category/31/subcatid/4';\r
50         }\r
51         function getVersion() {\r
52                 return '1.8';\r
53         }\r
54         function getDescription() {\r
55                 return '[$Revision: 1.118 $]<br />This plug-in This plug-in inserts a &lt;META&gt; tag (robots, description, keywords), by using &lt;%MetaTags%&gt;';\r
56         }\r
57         function getMinNucleusVersion() {\r
58                 return 320;\r
59         }\r
60         function supportsFeature($what) {\r
61                 switch ($what) {\r
62                         case 'SqlTablePrefix' :\r
63                                 return 1;\r
64                         default :\r
65                                 return 0;\r
66                 }\r
67         }\r
68 \r
69         function hasAdminArea() {\r
70                 return 1;\r
71         }\r
72         \r
73         function getEventList() {\r
74                 return array ('PostPluginOptionsUpdate');\r
75         }\r
76 \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
83 \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
87         }\r
88 \r
89         function doSkinVar($skinType, $mode = 'tags') {\r
90                 switch( $skinType ){\r
91                         case 'item':\r
92                                 global $itemid;\r
93                                 $description = $this->getItemOption($itemid, 'description');\r
94                                 $keywords = $this->getItemOption($itemid, 'keywords');\r
95                                 $robots = $this->getItemOption($itemid, 'robots');\r
96                                 \r
97                                 switch( $mode ){\r
98                                         case '':\r
99                                         case 'tags':\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
106                                                 }\r
107                                                 break;\r
108                                                 \r
109                                         case 'robots':\r
110                                                 if (!empty ($robots))\r
111                                                         echo "<meta name=\"robots\" content=\"".htmlspecialchars($robots, ENT_QUOTES)."\" />\n";\r
112                                                 break;\r
113                                         case 'description':\r
114                                                 if (!empty ($description))\r
115                                                         echo "<meta name=\"description\" content=\"".htmlspecialchars($description, ENT_QUOTES)."\" />\n";\r
116                                                 break;\r
117                                         case 'keywords':\r
118                                                 if (!empty ($keywords))\r
119                                                         echo "<meta name=\"keywords\" content=\"".htmlspecialchars($keywords, ENT_QUOTES)."\" />\n";\r
120                                                 break;\r
121                                                 \r
122                                         default :\r
123                                                 break;\r
124                                 }\r
125                                 break;\r
126                                 \r
127                         case 'archive':\r
128                                 global $archive, $archivetype;\r
129                                 $now = time();\r
130                                 $t = 0;\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
137                                 }\r
138                                 \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
142                                 else\r
143                                         echo '<meta name="robots" content="INDEX,FOLLOW" />'."\n";\r
144                                 break;\r
145                                 \r
146                         default:\r
147                                 break;\r
148                 }\r
149         }\r
150         \r
151         function event_PostPluginOptionsUpdate(& $data) {\r
152                 global $manager;\r
153                 \r
154                 switch($data['context']){\r
155                         case 'global':\r
156                                 $affected = $this->_refreshData();\r
157                                 break;\r
158                         case 'item':\r
159                                 // var_dump($data);\r
160                                 // core hack needed.\r
161                                 if( $data['item']['draft'] ) break;\r
162 \r
163                                 $item = $data['item'];\r
164                                 $item['itemid'] = $data['itemid'];\r
165                                 $this->_setData($item);\r
166                                 break;\r
167                         default:\r
168                                 // nothing      \r
169                 }\r
170         }\r
171         \r
172         function _refreshData(){\r
173                 if( $this->getOption('isRefresh') == yes ){\r
174                         ACTIONLOG :: add(INFO, 'MetaTags: Invoked refreshData()');\r
175 \r
176                         $isForce = ($this->getOption('isForceRefresh') == 'yes')? true : false;                 \r
177                         $this->setOption('isRefresh','no');\r
178                         $this->setOption('isForceRefresh','no');\r
179                         \r
180                         $affected = 0;\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
184                                 return;\r
185                         while ($assoc = mysql_fetch_assoc($res)) {\r
186                                 // description\r
187                                 if( !$isForce ){\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
192                                         }\r
193                                 } else {\r
194                                         $description = $this->_makeDescription($assoc['ibody'].$assoc['imore']);\r
195                                         $this->setItemOption($assoc['inumber'], 'description', $description);\r
196                                 }\r
197                                 // keywords\r
198                                 if( !$isForce ){\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
203                                         }\r
204                                 } else {\r
205                                         $keywords = $this->_getKeywords($assoc['ibody'].$assoc['imore']);\r
206                                         $this->setItemOption($assoc['inumber'], 'keywords', $keywords);\r
207                                 }\r
208                                 $affected += 1;\r
209                         }\r
210                         mysql_free_result($res);\r
211                         ACTIONLOG :: add(INFO, "MetaTags: Finished refreshData(): affected items [$affected]");\r
212                         return $affected;\r
213                 }               \r
214         }\r
215 \r
216         function _setData(& $data) {\r
217                 $itemid = intval($data['itemid']);\r
218                 \r
219                 $this->setItemOption($itemid, 'lastupdate', time());\r
220                 \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
225                 }\r
226 \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
232                 }\r
233         }\r
234 \r
235         function _getKeywords($text) {\r
236                 if ( ! $this->getOption('yahooAppId') )\r
237                         return '';\r
238                 $maxKeywords = $this->getOption('MaxKeywords');\r
239                 $appid = $this->getOption('yahooAppId');\r
240                 $tfidf = array();\r
241                 \r
242                 if( _CHARSET != 'UTF-8' )\r
243                         $string = mb_convert_encoding($string, 'UTF-8', _CHARSET);\r
244                 \r
245                 $text = strip_tags($text);\r
246                 $text = str_replace("\n", "", $text);\r
247                 $text = str_replace("\r", "", $text);\r
248                 \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
255 \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
261                 if( !$data )\r
262                         ACTIONLOG :: add(WARNING, 'NP_MetaTags: AsyncHTTP Error['.$ahttp->getErrorNo(0).']'.$ahttp->getError(0));\r
263                 \r
264                 if( $data ){\r
265                         $p =& new NP_MetaTags_MA_XMLParser();\r
266                         $words = $p->parse($data);\r
267                         if( $p->isError ){\r
268                                 ACTIONLOG :: add(WARNING, 'NP_MetaTags: Y!API Error( '. (isset($rawtokens[0]) ? $rawtokens[0] : 'Unknown Error -> '.$data) . ' )');\r
269                                 $words = array();\r
270                         }\r
271                         $p->free();\r
272                         $p = null;\r
273                         \r
274                         if( $words ){\r
275                                 arsort($words);\r
276                                 $words = array_slice($words, 0, 20);\r
277                                 \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
285                                 \r
286                                 $requests = array();\r
287                                 foreach ($words as $word => $count) {\r
288                                         $postData['query'] = $word;\r
289                                         \r
290                                         $qs = array();\r
291                                         foreach($postData as $k => $v){\r
292                                                 $qs[] = $k."=".urlencode($v);   \r
293                                         }\r
294                                         $u = 'http://search.yahooapis.jp/WebSearchService/V2/webSearch?'.implode("&", $qs);\r
295                                         $id = $ahttp->setRequest($u, 'GET');\r
296                                         \r
297                                         $requests[$id] = $word;\r
298                                 }\r
299                                 \r
300                                 $responses = $ahttp->getResponses();\r
301                                 \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
306                                                 if( $p->isError ){\r
307                                                         ACTIONLOG :: add(WARNING, 'NP_MetaTags: Y!API Error( '. (isset($totalResultsAvailable) ? $totalResultsAvailable : 'Unknown Error') . ' )');\r
308                                                         $totalResultsAvailable = 0;\r
309                                                 }\r
310                                                 $p->free();\r
311                                                 $p = null;\r
312                                                 \r
313                                                 if( $totalResultsAvailable ){\r
314                                                         $tfidf[$word] = $words[$word] * log10(30000000000/$totalResultsAvailable);\r
315                                                 }\r
316                                         } else {\r
317                                                 ACTIONLOG :: add(WARNING, $this->getName().': AsyncHTTP Error['.$ahttp->getErrorNo($id).']'.$ahttp->getError($id));\r
318                                         }\r
319                                 }\r
320                                 //var_dump($ahttp);\r
321                         }\r
322                 }\r
323                 //var_dump($tfidf);     \r
324 \r
325                 arsort($tfidf);\r
326                 $tfidf = array_slice($tfidf, 0, $maxKeywords);\r
327                 $result = "";\r
328                 foreach( $tfidf as $word => $score ){\r
329                         $result .= $word.',';\r
330                 }\r
331                 \r
332                 if( _CHARSET != 'UTF-8' )\r
333                         $result = mb_convert_encoding($result, _CHARSET, 'UTF-8');\r
334                 \r
335                 return mb_substr($result, 0, -1);\r
336         }\r
337 \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
344         }\r
345 }\r
346 \r
347 class NP_MetaTags_Base_XMLParser {\r
348         function init(){\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
353                 \r
354                 $this->isError = false;\r
355                 $this->inTarget = false;\r
356         }\r
357 \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
366             }\r
367                 return $this->words;\r
368         }\r
369 \r
370         function free(){\r
371                 xml_parser_free($this->parser);\r
372                 $this->words = null;\r
373         }\r
374 }\r
375 \r
376 class NP_MetaTags_MA_XMLParser extends NP_MetaTags_Base_XMLParser {\r
377         function NP_MetaTags_MA_XMLParser(){\r
378                 $this->init();\r
379         }\r
380         \r
381         function _open($parser, $name, $attribute){\r
382                 switch( $name ){\r
383                         case 'WORD':\r
384                                 $this->tmp = array();\r
385                                 break;\r
386                         case 'SURFACE':\r
387                                 $this->inTarget = 'SURFACE';\r
388                                 break;\r
389                         case 'COUNT':\r
390                                 $this->inTarget = 'COUNT';\r
391                                 break;                  \r
392                         case 'MESSAGE':\r
393                                 $this->inTarget = 'MESSAGE';\r
394                                 break;\r
395                         case 'ERROR':\r
396                                 $this->isError = true;\r
397                                 break;\r
398                 }\r
399         }\r
400 \r
401         function _close($parser, $name){\r
402                 switch( $name ){\r
403                         case 'WORD':\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
406                                 }\r
407                                 break;\r
408                         case 'MESSAGE':\r
409                                 $this->words = array();\r
410                                 $this->words[] = $this->tmp['MESSAGE'];\r
411                                 break;\r
412                 }\r
413                 if( $name == $this->inTarget ) $this->inTarget = false;\r
414         }\r
415 \r
416         function _cdata($parser, $data){\r
417                 if( $this->inTarget ){\r
418                         $this->tmp[$this->inTarget] = trim($data);\r
419                 }\r
420         }\r
421 }\r
422 \r
423 class NP_MetaTags_WS_XMLParser extends NP_MetaTags_Base_XMLParser {\r
424         function NP_MetaTags_WS_XMLParser(){\r
425                 $this->init();\r
426         }\r
427         \r
428         function _open($parser, $name, $attribute){\r
429                 switch( $name ){\r
430                         case 'RESULTSET':\r
431                                 $this->words[] = $attribute['TOTALRESULTSAVAILABLE'];\r
432                                 break;\r
433                 }\r
434         }\r
435         \r
436         function _close($parser, $name){}\r
437         function _cdata($parser, $data){}\r
438 }\r