3 /** ============================================================================
4 * GoogleSitemap for Nucleus
6 * Copyright 2005 by Niels Leenheer
7 * ============================================================================
8 * This program is free software and open source software; you can redistribute
9 * it and/or modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of the License,
11 * or (at your option) any later version.
13 * This program is distributed in the hope that it will be useful, but WITHOUT
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA or visit
21 * http://www.gnu.org/licenses/gpl.html
22 * ============================================================================
27 * 0.7 modified release by shizuki
28 * Generate URL modified from
29 * 'http://example.com/action.php?action=plugin&name=Sitemap' to
30 * 'http://example.com/sitemap.xml' and,or
31 * 'http://example.com/index.php?virtualpath=sitemap.xml'
32 * Add 'lastmod' attribute
33 * 0.9 SitemapProtocol updated release
34 * SitemapProtocol ver.0.9 as common for Google, Yahoo! and MSN(Live! Search)
35 * 1.0 Add Sitemap type and chage 'lastmod' generate
36 * Add 'ROR Sitemap' format
37 * For details about the ROR format, go to www.rorweb.com
38 * Modify 'lastmod' attribute
39 * item posted time or comment posted time or item update time
40 * item update time generate by NP_UpdateTime
41 * 1.1 Send Sitemaps to Yahoo!
44 class NP_SearchenginesSitemapsGenerator extends NucleusPlugin
49 return 'SearchenginesSitemapsGenerator';
54 return 'Niels Leenheer + shizuki';
59 return 'http://japan.nucleuscms.org/wiki/plugins:searchenginesitemapgenerator';
67 function getDescription()
69 return _G_SITEMAP_DESC;
72 function getEventList()
80 function supportsFeature($feature)
83 case 'SqlTablePrefix':
90 function event_PreSendContentType($data)
92 global $CONF, $manager, $blogid;
94 $mcategories = $this->pluginCheck('MultipleCategories');
96 if (method_exists($mcategories, 'getRequestName')) {
97 $subReq = $mcategories->getRequestName();
102 $npUpdateTime = $this->pluginCheck('UpdateTime');
105 $blogid = $CONF['DefaultBlog'];
107 if (is_numeric($blogid)) {
108 $blogid = intval($blogid);
110 $blogid = intval(getBlogIDFromName($blogid));
114 $b =& $manager->getBlog($blogid);
115 $BlogURL = $b->getURL();
117 $BlogURL = $CONF['IndexURL'];
120 if ( substr($BlogURL, -1) != '/'
121 && substr($BlogURL, -4) != '.php') {
125 if (getVar('virtualpath')) {
126 $info = preg_replace('|[^a-zA-Z0-9-~+_.?#=&;,/:@%]|i', '', getVar('virtualpath'));
127 } elseif (serverVar('PATH_INFO')) {
128 $info = preg_replace('|[^a-zA-Z0-9-~+_.?#=&;,/:@%]|i', '', serverVar('PATH_INFO'));
133 $path_arr = explode('/', $info);
134 $PcMap = $this->getBlogOption($blogid, 'PcSitemap');
135 $MobileMap = $this->getBlogOption($blogid, 'MobileSitemap');
136 if ( end($path_arr) == $PcMap
137 || end($path_arr) == 'ror.xml'
138 || (!empty($MobileMap) && end($path_arr) == $MobileMap) ) {
140 if ( $this->getOption('AllBlogMap') == 'yes'
141 && $blogid == $CONF['DefaultBlog']) {
142 $blogQuery = 'SELECT * '
144 . 'ORDER BY bnumber';
145 $blogQuery = sprintf($blogQuery, sql_table('blog'));
146 $blogResult = sql_query($blogQuery);
148 $blogQuery = 'SELECT * '
150 . 'WHERE bnumber = %d';
151 $blogQuery = sprintf($blogQuery, sql_table('blog'), $blogid);
152 $blogResult = sql_query($blogQuery);
155 while ($blogs = mysql_fetch_array($blogResult)) {
156 $blog_id = intval($blogs['bnumber']);
157 if ( $this->getBlogOption($blog_id, 'IncludeSitemap') == 'yes'
158 || !empty($currentBlog)) {
159 $temp_b =& $manager->getBlog($blog_id);
160 $TempURL = $temp_b->getURL();
163 $URLMode = $CONF['URLMode'];
164 if (substr($TempURL, -4) == '.php') {
165 $CONF['URLMode'] = 'normal';
168 $usePathInfo = ($CONF['URLMode'] == 'pathinfo');
170 if (substr($SelfURL, -1) == '/') {
173 $SelfURL = substr($SelfURL, 0, -1);
175 $SelfURL = $SelfURL . 'index.php';
178 } elseif (substr($SelfURL, -4) != '.php') {
183 $SelfURL = $SelfURL . '/index.php';
188 $CONF['ItemURL'] = $SelfURL;
189 $CONF['CategoryURL'] = $SelfURL;
191 if ( substr($TempURL, -1) != '/'
192 && substr($TempURL, -4) != '.php') {
196 $patternURL = '/^' . preg_replace('/\//', '\/', $BlogURL) . '/';
198 if (preg_match($patternURL, $TempURL)) {
200 if (end($path_arr) == 'ror.xml') {
201 $rorTitleURL = $this->_prepareLink($SelfURL, $TempURL);
202 $rooTitleURL = htmlspecialchars($rooTitleURL, ENT_QUOTES, _CHARSET);
203 $sitemapTitle = " <title>ROR Sitemap for " . $rorTitleURL . "</title>\n"
204 . " <link>" . $rorTitleURL . "</link>\n"
206 . " <title>ROR Sitemap for " . $rorTitleURL . "</title>\n"
207 . " <link>" . $rorTitleURL . "</link>\n"
208 . " <ror:about>sitemap</ror:about>\n"
209 . " <ror:type>SiteMap</ror:type>\n"
213 'loc' => $this->_prepareLink($SelfURL, $TempURL),
215 'changefreq' => 'daily'
219 $catQuery = 'SELECT * '
221 . 'WHERE cblog = %d '
223 $catTable = sql_table('category');
224 $catQuery = sprintf($catQuery, $catTable, $blog_id);
225 $catResult = sql_query($catQuery);
227 while ($cat = mysql_fetch_array($catResult)) {
229 $cat_id = intval($cat['catid']);
230 $Link = createCategoryLink($cat_id);
231 $catLoc =$this->_prepareLink($SelfURL, $Link);
233 if (end($path_arr) != 'ror.xml') {
237 'changefreq' => 'daily'
242 $scatQuery = 'SELECT * '
244 . 'WHERE catid = %d '
245 // . 'ORDER BY scatid';
247 $scatTable = sql_table('plug_multiple_categories_sub');
248 $scatQuery = sprintf($scatQuery, $scatTable, $cat_id);
249 $scatResult = sql_query($scatQuery);
251 while ($scat = mysql_fetch_array($scatResult)) {
253 $scat_id = intval($scat['scatid']);
254 $params = array($subReq => $scat_id);
255 $Link = createCategoryLink($cat_id, $params);
256 $scatLoc = $this->_prepareLink($SelfURL, $Link);
258 if (end($path_arr) != 'ror.xml') {
262 'changefreq' => 'daily'
272 $itemQuery = 'SELECT *, '
273 . ' UNIX_TIMESTAMP(itime) AS timestamp '
275 . 'WHERE iblog = %d '
277 . 'ORDER BY itime DESC';
278 $itemTable = sql_table('item');
279 $itemQuery = sprintf($itemQuery, $itemTable, $blog_id);
280 $itemResult = sql_query($itemQuery);
281 while ($item = mysql_fetch_array($itemResult)) {
283 $item_id = intval($item['inumber']);
284 $Link = createItemLink($item_id);
285 $tz = date('O', $item['timestamp']);
286 $tz = substr($tz, 0, 3) . ':' . substr($tz, 3, 2);
287 $itemLoc = $this->_prepareLink($SelfURL, $Link);
290 . ' UNIX_TIMESTAMP(ctime) AS timestamp'
292 . sql_table('comment')
294 . ' citem = ' . $item_id
299 $modTime = sql_query($mdQuery);
300 $itemTime = $item['timestamp'];
301 if (mysql_num_rows($modTime) > 0) {
302 $lastMod = mysql_fetch_object($modTime);
303 $itemTime = $lastMod->timestamp;
304 } elseif ($npUpdateTime) { // NP_UpdateTime exists
306 . ' UNIX_TIMESTAMP(updatetime) AS timestamp'
308 . sql_table('plugin_rectime')
310 . ' up_id = ' . $item_id;
311 $modTime = sql_query($mdQuery);
312 if (mysql_num_rows($modTime) > 0) {
313 $lastMod = mysql_fetch_object($modTime);
314 $itemTime = $lastMod->timestamp;
318 /* if (time() - $itemTime < 86400 * 2) {
320 } elseif (time() - $itemTime < 86400 * 14) {
322 } elseif (time() - $itemTime < 86400 * 62) {
327 if ($itemTime < strtotime('-1 month')) {
329 } elseif ($itemTime < strtotime('-1 week')) {
331 } elseif ($itemTime < strtotime('-1 day')) {
336 $lastmod = gmdate('Y-m-d\TH:i:s', $itemTime) . $tz;
338 if (end($path_arr) != 'ror.xml') {
341 'lastmod' => $lastmod,
346 $iTitle = $item['ititle'];
347 if (_CHARSET != 'UTF-8') {
348 $iTitle = mb_conbert_encoding($iTitle, 'UTF-8', _CHARSET);
353 'ror:updated' => $lastmod,
354 'ror:updatePeriod' => 'day',
355 'ror:sortOrder' => '0',
356 'ror:resourceOf' => 'sitemap',
366 if ($CONF['URLMode'] != $URLMode) {
367 $CONF['URLMode'] = $URLMode;
372 $manager->notify('SiteMap', array ('sitemap' => & $sitemap));
374 header ("Content-type: application/xml");
376 if (end($path_arr) == 'ror.xml') {
379 $sitemapHeader ="<" . "?xml version='1.0' encoding='UTF-8'?" . ">\n\n"
380 . "<!-- This file is a ROR Sitemap for describing this website to the search engines. "
381 . "For details about the ROR format, go to www.rorweb.com. -->\n"
382 . '<rss version="2.0" xmlns:ror="http://rorweb.com/0.1/" >' . "\n"
387 // old Google sitemap protocol ver.0.84
388 // $sitemapHeader = "<" . "?xml version='1.0' encoding='UTF-8'?" . ">\n\n";
389 // . "\t<urlset" . ' xmlns="http://www.google.com/schemas/sitemap/0.84"' . "\n";
390 // . "\t" . 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"' . "\n";
391 // . "\t" . 'xsi:schemaLocation="http://www.google.com/schemas/sitemap/0.84' . "\n";
392 // . "\t" . ' http://www.google.com/schemas/sitemap/0.84/sitemap.xsd">' . "\n";
394 // new sitemap common protocol ver 0.9
395 $sitemapHeader = "<" . "?xml version='1.0' encoding='UTF-8'?" . ">\n\n"
396 . '<urlset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"' . "\n"
397 . ' xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9' . "\n"
398 . ' http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"' . "\n"
399 . ' xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"';
400 // uncomment and edit next line when you need "example_schema"
401 // $sitemapHeader .= ' xmlns:example="http://www.example.com/schemas/example_schema"';
402 $sitemapHeader .= '>';
407 if (end($path_arr) == 'ror.xml') {
411 while (list(, $url) = each($sitemap)) {
413 if (end($path_arr) == 'ror.xml') {
419 while (list($key, $value) = each($url)) {
421 $value = preg_replace('|[^a-zA-Z0-9-~+_.?#=&;,/:@%]|i', '', $value);
422 $data = "\t\t<" . $key . ">"
423 . htmlspecialchars($value, ENT_QUOTES, _CHARSET)
424 . "</" . $key . ">\n";
426 $data = "\t\t<" . $key . ">"
427 . htmlspecialchars($value, ENT_QUOTES, _CHARSET)
428 . "</" . $key . ">\n";
433 if (end($path_arr) == 'ror.xml') {
441 if (end($path_arr) == 'ror.xml') {
442 echo "</channel>\n</rss>\n";
446 // echo "</urlset>\n";
452 function pluginCheck($pluginName)
455 if (!$manager->pluginInstalled('NP_' . $pluginName)) {
458 $plugin =& $manager->getPlugin('NP_' . $pluginName);
463 function _prepareLink($base, $url) {
464 if (substr($url, 0, 7) == 'http://') {
471 function event_PostAddItem(&$data)
473 global $manager, $CONF;
475 $item_id = intval($data['itemid']);
476 $blog_id = intval(getBlogIDFromItemID($item_id));
477 $b =& $manager->getBlog($blog_id);
478 $b_url = $b->getURL();
480 if (substr($b_url, -4) == '.php') $CONF['URLMode'] = 'normal';
481 $usePathInfo = ($CONF['URLMode'] == 'pathinfo');
483 if (substr($b_url, -1) == '/') {
485 $b_url .= 'index.php?virtualpath=';
487 } elseif (substr($b_url, -4) == '.php') {
488 $b_url .= '?virtualpath=';
491 $b_url = $b_url . '/';
493 $b_url = $b_url . '/index.php?virtualpath=';
496 $siteMap = $this->getBlogOption($blog_id, 'PcSitemap');
498 if ($this->getBlogOption($blog_id, 'PingGoogle') == 'yes') {
499 $baseURL = 'http://www.google.com/webmasters/sitemaps/ping?sitemap=';
500 $utl = $baseURL . urlencode($b_url . $siteMap);
501 $url = preg_replace('|[^a-zA-Z0-9-~+_.?#=&;,/:@%]|i', '', $url);
502 $fp = @fopen($url, 'r');
504 $MobileMap = $this->getBlogOption($blog_id, 'MobileSitemap');
505 if (!empty($MobileMap)) {
506 $url = $baseURL . urlencode($b_url . $MobileMap);
507 $url = preg_replace('|[^a-zA-Z0-9-~+_.?#=&;,/:@%]|i', '', $url);
508 $fp = @fopen($url, 'r');
513 if ($this->getBlogOption($blog_id, 'PingYahoo') == 'yes' &&
514 $this->getBlogOption($blog_id, 'YahooAPID') != '') {
515 $baseURL = 'http://search.yahooapis.com/SiteExplorerService/V1/updateNotification?appid='
516 . $this->getBlogOption($blog_id, 'YahooAPID')
518 $utl = $baseURL . urlencode($b_url . $siteMap);
519 $url = preg_replace('|[^a-zA-Z0-9-~+_.?#=&;,/:@%]|i', '', $url);
520 $fp = @fopen($url, 'r');
522 $MobileMap = $this->getBlogOption($blog_id, 'MobileSitemap');
523 if (!empty($MobileMap)) {
524 $url = $baseURL . urlencode($b_url . $MobileMap);
525 $url = preg_replace('|[^a-zA-Z0-9-~+_.?#=&;,/:@%]|i', '', $url);
526 $fp = @fopen($url, 'r');
536 $language = ereg_replace( '[\\|/]', '', getLanguageName());
537 if (file_exists($this->getDirectory() . $language.'.php')) {
538 include_once($this->getDirectory() . $language.'.php');
540 include_once($this->getDirectory() . 'english.php');
546 $this->createOption('AllBlogMap', _G_SITEMAP_ALLB, 'yesno', 'yes');
547 $this->createBlogOption('IncludeSitemap', _G_SITEMAP_INC, 'yesno', 'yes');
548 $this->createBlogOption('PingGoogle', _G_SITEMAP_PING_G, 'yesno', 'yes');
549 $this->createBlogOption('PingYahoo', _G_SITEMAP_PING_Y, 'yesno', 'no');
550 $this->createBlogOption('YahooAPID', _G_SITEMAP_YAPID, 'text', '');
551 $this->createBlogOption('PcSitemap', _G_SITEMAP_PCSM, 'text', 'sitemap.xml');
552 $this->createBlogOption('MobileSitemap', _G_SITEMAP_MBSM, 'text', '');