OSDN Git Service

fe9d4363f59c42099f0cd88f9ed9aa09847de50d
[nucleus-jp/nucleus-plugins.git] / trunk / NP_SEOSitemaps / NP_SEOSitemaps.php
1 <?php
2
3 /** ============================================================================
4   * GoogleSitemap for Nucleus
5   *
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.
12   *
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
16   * more details.
17   *
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   * ============================================================================
23   **/
24
25 /**
26   * History
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!
42   **/
43
44 class NP_SEOSitemaps extends NucleusPlugin
45 {
46
47         function getName()
48         {
49                 return 'SearchenginesSitemapsGenerator';
50         }
51
52         function getAuthor()
53         {
54                 return 'Niels Leenheer + shizuki';
55         }
56
57         function getURL()
58         {
59                 return 'http://japan.nucleuscms.org/wiki/plugins:searchenginesitemapgenerator';
60         }
61
62         function getVersion()
63         {
64                 return '1.1';
65         }
66
67         function getDescription()
68         {
69                 return _G_SITEMAP_DESC;
70         }
71         
72         function getEventList()
73         {
74                 return array(
75                                          'PostAddItem',
76                                          'PreSendContentType'
77                                         );
78         }
79         
80         function supportsFeature($feature)
81         {
82         switch($feature) {
83                 case 'SqlTablePrefix':
84                         return 1;
85                 default:
86                         return 0;
87                 }
88         }
89
90         function event_PreSendContentType($data)
91         {
92                 global $CONF, $manager, $blogid;
93
94                 $mcategories = $this->pluginCheck('MultipleCategories');
95                 if ($mcategories) {
96                         if (method_exists($mcategories, 'getRequestName')) {
97                                 $subReq = $mcategories->getRequestName();
98                         } else {
99                                 $subReq = 'subcatid';
100                         }
101                 }
102                 $npUpdateTime = $this->pluginCheck('UpdateTime');
103
104                 if (!$blogid) {
105                         $blogid = $CONF['DefaultBlog'];
106                 } else {
107                         if (is_numeric($blogid)) {
108                                 $blogid = intval($blogid);
109                         } else {
110                                 $blogid = intval(getBlogIDFromName($blogid));
111                         }
112                 }
113
114                 $b       =& $manager->getBlog($blogid);
115                 $BlogURL = $b->getURL();
116                 if (!$BlogURL) {
117                         $BlogURL = $CONF['IndexURL'];
118                 }
119
120                 if ( substr($BlogURL, -1) != '/'
121                   && substr($BlogURL, -4) != '.php') {
122                         $BlogURL .= '/';
123                 }
124
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'));
129                 } else {
130                         return;
131                 }
132
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) ) {
139                         $sitemap = array();
140                         if ( $this->getOption('AllBlogMap') == 'yes'
141                           && $blogid == $CONF['DefaultBlog']) {
142                                 $blogQuery  = 'SELECT * '
143                                                         . 'FROM %s '
144                                                         . 'ORDER BY bnumber';
145                                 $blogQuery  = sprintf($blogQuery, sql_table('blog'));
146                                 $blogResult = sql_query($blogQuery);
147                         } else {
148                                 $blogQuery   = 'SELECT * '
149                                                          . 'FROM %s '
150                                                          . 'WHERE bnumber = %d';
151                                 $blogQuery   = sprintf($blogQuery, sql_table('blog'), $blogid);
152                                 $blogResult  = sql_query($blogQuery);
153                                 $currentBlog = TRUE;
154                         }
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();
161                                         $SelfURL =  $TempURL;
162
163                                         $URLMode = $CONF['URLMode'];
164                                         if (substr($TempURL, -4) == '.php') {
165                                                 $CONF['URLMode'] = 'normal';
166                                         }
167
168                                         $usePathInfo = ($CONF['URLMode'] == 'pathinfo');
169
170                                         if (substr($SelfURL, -1) == '/') {
171
172                                                 if ($usePathInfo) {
173                                                         $SelfURL = substr($SelfURL, 0, -1);
174                                                 } else {
175                                                         $SelfURL = $SelfURL . 'index.php';
176                                                 }
177
178                                         } elseif (substr($SelfURL, -4) != '.php') {
179
180                                                 if ($usePathInfo) {
181                                                         $SelfURL = $SelfURL;
182                                                 } else {
183                                                         $SelfURL = $SelfURL . '/index.php';
184                                                 }
185
186                                         }
187
188                                         $CONF['ItemURL']     = $SelfURL;
189                                         $CONF['CategoryURL'] = $SelfURL;
190
191                                         if ( substr($TempURL, -1) != '/'
192                                           && substr($TempURL, -4) != '.php') {
193                                                 $TempURL .= '/';
194                                         }
195
196                                         $patternURL = '/^' . preg_replace('/\//', '\/', $BlogURL) . '/';
197
198                                         if (preg_match($patternURL, $TempURL)) {
199
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"
205                                                                                   . "     <item>\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"
210                                                                                   . "     </item>\n";
211                                                 } else {
212                                                         $bPriority = intval($this->getBlogOption($blog_id, 'blogPriority'));
213                                                         if ($bPriority > 10) $bPriority = 10;
214                                                         $bPriority = $bPriority / 10;
215                                                         $sitemap[] = array(
216                                                                 'loc'        => $this->_prepareLink($SelfURL, $TempURL),
217                                                                 'priority'   => $bPriority,
218                                                                 'changefreq' => 'daily'
219                                                         );
220                                                 }
221
222                                                 $catQuery  = 'SELECT * '
223                                                                    . 'FROM %s '
224                                                                    . 'WHERE cblog = %d '
225                                                                    . 'ORDER BY catid';
226                                                 $catTable  = sql_table('category');
227                                                 $catQuery  = sprintf($catQuery, $catTable, $blog_id);
228                                                 $catResult = sql_query($catQuery);
229
230                                                 while ($cat = mysql_fetch_array($catResult)) {
231
232                                                         $cat_id = intval($cat['catid']);
233                                                         $Link   = createCategoryLink($cat_id);
234                                                         $catLoc =$this->_prepareLink($SelfURL, $Link);
235
236                                                         if (end($path_arr) != 'ror.xml') {
237                                                                 $cPriority = intval($this->getCategoryOption($cat_id, 'catPriority'));
238                                                                 if ($cPriority > 10) $priority = 10;
239                                                                 $sPriority = ($cPriority - 1) / 10;
240                                                                 $cPriority  = $cPriority / 10;
241                                                                 $sitemap[] = array(
242                                                                         'loc'        => $catLoc,
243                                                                         'priority'   => $cPriority,
244                                                                         'changefreq' => 'daily'
245                                                                 );
246                                                         }
247
248                                                         if ($mcategories) {
249                                                                 $scatQuery  = 'SELECT * '
250                                                                                         . 'FROM %s '
251                                                                                         . 'WHERE catid = %d '
252 //                                                                                      . 'ORDER BY scatid';
253                                                                                         . 'ORDER BY ordid';
254                                                                 $scatTable  = sql_table('plug_multiple_categories_sub');
255                                                                 $scatQuery  = sprintf($scatQuery, $scatTable, $cat_id);
256                                                                 $scatResult = sql_query($scatQuery);
257
258                                                                 while ($scat = mysql_fetch_array($scatResult)) {
259
260                                                                         $scat_id = intval($scat['scatid']);
261                                                                         $params  = array($subReq => $scat_id);
262                                                                         $Link    = createCategoryLink($cat_id, $params);
263                                                                         $scatLoc = $this->_prepareLink($SelfURL, $Link);
264
265                                                                         if (end($path_arr) != 'ror.xml') {
266                                                                                 $sitemap[] = array(
267                                                                                         'loc'        => $scatLoc,
268                                                                                         'priority'   => $sPriority,
269                                                                                         'changefreq' => 'daily'
270                                                                                 );
271                                                                         }
272
273                                                                 }
274
275                                                         }
276
277                                                 }
278
279                                                 $itemQuery  = 'SELECT *, '
280                                                                         . '       UNIX_TIMESTAMP(itime) AS timestamp '
281                                                                         . 'FROM %s '
282                                                                         . 'WHERE iblog  = %d '
283                                                                         . 'AND   idraft = 0 '
284                                                                         . 'ORDER BY itime DESC';
285                                                 $itemTable  = sql_table('item');
286                                                 $itemQuery  = sprintf($itemQuery, $itemTable, $blog_id);
287                                                 $itemResult = sql_query($itemQuery);
288                                                 while ($item = mysql_fetch_array($itemResult)) {
289
290                                                         $item_id  = intval($item['inumber']);
291                                                         $Link     = createItemLink($item_id);
292                                                         $tz       = date('O', $item['timestamp']);
293                                                         $tz       = substr($tz, 0, 3) . ':' . substr($tz, 3, 2);
294                                                         $itemLoc  = $this->_prepareLink($SelfURL, $Link);
295
296                                                         $mdQuery  = 'SELECT'
297                                                                           . '   UNIX_TIMESTAMP(ctime) AS timestamp'
298                                                                           . ' FROM '
299                                                                           .     sql_table('comment')
300                                                                           . ' WHERE'
301                                                                           . '   citem = ' . $item_id
302                                                                           . ' ORDER BY'
303                                                                           . '   ctime DESC'
304                                                                           . ' LIMIT'
305                                                                           . '   1';
306                                                         $modTime  = sql_query($mdQuery);
307                                                         $itemTime = $item['timestamp'];
308                                                         if (mysql_num_rows($modTime) > 0) {
309                                                                 $lastMod  = mysql_fetch_object($modTime);
310                                                                 $itemTime = $lastMod->timestamp;
311                                                         } elseif ($npUpdateTime) { // NP_UpdateTime exists
312                                                                 $mdQuery = 'SELECT'
313                                                                                  . '   UNIX_TIMESTAMP(updatetime) AS timestamp'
314                                                                                  . ' FROM '
315                                                                                  .     sql_table('plugin_rectime')
316                                                                                  . ' WHERE'
317                                                                                  . '   up_id = ' . $item_id;
318                                                                 $modTime = sql_query($mdQuery);
319                                                                 if (mysql_num_rows($modTime) > 0) { 
320                                                                         $lastMod  = mysql_fetch_object($modTime);
321                                                                         $itemTime = $lastMod->timestamp;
322                                                                 }
323                                                         }
324
325 /*                                                      if (time() - $itemTime < 86400 * 2) {
326                                                                 $fq = 'hourly';
327                                                         } elseif (time() - $itemTime < 86400 * 14) {
328                                                                 $fq = 'daily'; 
329                                                         } elseif (time() - $itemTime < 86400 * 62) {
330                                                                 $fq = 'weekly';
331                                                         } else {
332                                                                 $fq = 'monthly';
333                                                         }*/
334                                                         if ($itemTime < strtotime('-1 month')) {
335                                                                 $fq = 'monthly';
336                                                         } elseif ($itemTime < strtotime('-1 week')) {
337                                                                 $fq = 'weekly';
338                                                         } elseif ($itemTime < strtotime('-1 day')) {
339                                                                 $fq = 'daily'; 
340                                                         } else {
341                                                                 $fq = 'hourly';
342                                                         }
343                                                         $lastmod = gmdate('Y-m-d\TH:i:s', $itemTime) . $tz;
344
345                                                         if (end($path_arr) != 'ror.xml') {
346                                                                 $iPriority = intval($this->getItemOption($item_id, 'itemPriority'));
347                                                                 if ($iPriority > 10) $iPriority = 10;
348                                                                 $iPriority = $iPriority / 10;
349                                                                 $sitemap[] = array(
350                                                                         'loc'        => $itemLoc,
351                                                                         'lastmod'    => $lastmod,
352                                                                         'priority'   => $iPriority,
353                                                                         'changefreq' => $fq
354                                                                 );
355                                                         } else {
356                                                                 $iTitle = $item['ititle'];
357                                                                 if (_CHARSET != 'UTF-8') {
358                                                                         $iTitle = mb_conbert_encoding($iTitle, 'UTF-8', _CHARSET);
359                                                                 }
360                                                                 $sitemap[] = array(
361                                                                         'title'            => $iTitle,
362                                                                         'link'             => $itemLoc,
363                                                                         'ror:updated'      => $lastmod,
364                                                                         'ror:updatePeriod' => 'day',
365                                                                         'ror:sortOrder'    => '0',
366                                                                         'ror:resourceOf'   => 'sitemap',
367                                                                 );
368                                                         }
369
370                                                 }
371
372                                         }
373
374                                 }
375
376                                 if ($CONF['URLMode'] != $URLMode) {
377                                         $CONF['URLMode'] = $URLMode;
378                                 }
379
380                         }
381
382                         $manager->notify('SiteMap', array ('sitemap' => & $sitemap));
383
384                         header ("Content-type: application/xml");
385
386                         if (end($path_arr) == 'ror.xml') {
387
388                         // ror sitemap feed
389                         $sitemapHeader ="<" . "?xml version='1.0' encoding='UTF-8'?" . ">\n\n"
390                                                    . "<!--  This file is a ROR Sitemap for describing this website to the search engines. "
391                                                    . "For details about the ROR format, go to www.rorweb.com.   -->\n"
392                                                    . '<rss version="2.0" xmlns:ror="http://rorweb.com/0.1/" >' . "\n"
393                                                    . "<channel>\n";
394
395                         } else {
396
397                         // old Google sitemap protocol ver.0.84
398 //                      $sitemapHeader  = "<" . "?xml version='1.0' encoding='UTF-8'?" . ">\n\n";
399 //                                                      . "\t<urlset" . ' xmlns="http://www.google.com/schemas/sitemap/0.84"' . "\n";
400 //                                                      . "\t" . 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"' . "\n";
401 //                                                      . "\t" . 'xsi:schemaLocation="http://www.google.com/schemas/sitemap/0.84' . "\n";
402 //                                                      . "\t" . '        http://www.google.com/schemas/sitemap/0.84/sitemap.xsd">' . "\n";
403
404                         // new sitemap common protocol ver 0.9
405                         $sitemapHeader  = "<" . "?xml version='1.0' encoding='UTF-8'?" . ">\n\n"
406                                                         . '<urlset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"' . "\n"
407                                                         . '         xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9' . "\n"
408                                                         . '         http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"' . "\n"
409                                                         . '         xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"';
410                         // uncomment and edit next line when you need "example_schema"
411 //                      $sitemapHeader .= '         xmlns:example="http://www.example.com/schemas/example_schema"';
412                         $sitemapHeader .= '>';
413
414                         }
415
416                         echo $sitemapHeader;
417                         if (end($path_arr) == 'ror.xml') {
418                                 echo $sitemapTitle;
419                         }
420
421                         while (list(, $url) = each($sitemap)) {
422
423                                 if (end($path_arr) == 'ror.xml') {
424                                         echo "\t<item>\n";
425                                 } else {
426                                         echo "\t<url>\n";
427                                 }
428
429                                 while (list($key, $value) = each($url)) {
430                                         if ($key == 'loc') {
431                                                 $value = preg_replace('|[^a-zA-Z0-9-~+_.?#=&;,/:@%]|i', '', $value);
432                                                 $data  = "\t\t<" . $key . ">"
433                                                            . htmlspecialchars($value, ENT_QUOTES, _CHARSET)
434                                                            . "</" . $key . ">\n";
435                                         } else {
436                                                 $data  = "\t\t<" . $key . ">"
437                                                            . htmlspecialchars($value, ENT_QUOTES, _CHARSET)
438                                                            . "</" . $key . ">\n";
439                                         }
440                                         echo $data;
441                                 }
442
443                                 if (end($path_arr) == 'ror.xml') {
444                                         echo "\t</item>\n";
445                                 } else {
446                                         echo "\t</url>\n";
447                                 }
448
449                         }
450
451                         if (end($path_arr) == 'ror.xml') {
452                                 echo "</channel>\n</rss>\n";
453                         } else {
454                                 echo "</urlset>\n";
455                         }
456 //                      echo "</urlset>\n";
457                         exit;
458
459                 }
460         }
461
462         function pluginCheck($pluginName)
463         {
464                 global $manager;
465                 if (!$manager->pluginInstalled('NP_' . $pluginName)) {
466                         return;
467                 } else {
468                         $plugin =& $manager->getPlugin('NP_' . $pluginName);
469                         return $plugin;
470                 }
471         }
472
473         function _prepareLink($base, $url) {
474                 if (substr($url, 0, 7) == 'http://') {
475                         return $url;
476                 } else {
477                         return $base . $url;
478                 }
479         }
480
481         function event_PostAddItem(&$data)
482         {
483                 global $manager, $CONF;
484
485                 $item_id =  intval($data['itemid']);
486                 $blog_id =  intval(getBlogIDFromItemID($item_id));
487                 $b       =& $manager->getBlog($blog_id);
488                 $b_url   =  $b->getURL();
489
490                 if (substr($b_url, -4) == '.php') $CONF['URLMode'] = 'normal';
491                 $usePathInfo = ($CONF['URLMode'] == 'pathinfo');
492
493                 if (substr($b_url, -1) == '/') {
494                         if (!$usePathInfo) {
495                                 $b_url .= 'index.php?virtualpath=';
496                         }
497                 } elseif (substr($b_url, -4) == '.php') {
498                         $b_url .= '?virtualpath=';
499                 } else {
500                         if ($usePathInfo) {
501                                 $b_url = $b_url . '/';
502                         } else {
503                                 $b_url = $b_url . '/index.php?virtualpath=';
504                         }
505                 }
506                 $siteMap = $this->getBlogOption($blog_id, 'PcSitemap');
507
508                 if ($this->getBlogOption($blog_id, 'PingGoogle') == 'yes') {
509                         $baseURL = 'http://www.google.com/webmasters/sitemaps/ping?sitemap=';
510                         $utl     = $baseURL . urlencode($b_url . $siteMap);
511                         $url     = preg_replace('|[^a-zA-Z0-9-~+_.?#=&;,/:@%]|i', '', $url);
512                         $fp      = @fopen($url, 'r');
513                         @fclose($fp);
514                         $MobileMap = $this->getBlogOption($blog_id, 'MobileSitemap');
515                         if (!empty($MobileMap)) {
516                                 $url = $baseURL . urlencode($b_url . $MobileMap);
517                                 $url = preg_replace('|[^a-zA-Z0-9-~+_.?#=&;,/:@%]|i', '', $url);
518                                 $fp  = @fopen($url, 'r');
519                                 @fclose($fp);
520                         }
521                 }
522
523                 if ($this->getBlogOption($blog_id, 'PingYahoo') == 'yes' &&
524                         $this->getBlogOption($blog_id, 'YahooAPID') != '') {
525                         $baseURL = 'http://search.yahooapis.com/SiteExplorerService/V1/updateNotification?appid='
526                                          . $this->getBlogOption($blog_id, 'YahooAPID')
527                                          . '&url=';
528                         $utl     = $baseURL . urlencode($b_url . $siteMap);
529                         $url     = preg_replace('|[^a-zA-Z0-9-~+_.?#=&;,/:@%]|i', '', $url);
530                         $fp      = @fopen($url, 'r');
531                         @fclose($fp);
532                         $MobileMap = $this->getBlogOption($blog_id, 'MobileSitemap');
533                         if (!empty($MobileMap)) {
534                                 $url = $baseURL . urlencode($b_url . $MobileMap);
535                                 $url = preg_replace('|[^a-zA-Z0-9-~+_.?#=&;,/:@%]|i', '', $url);
536                                 $fp  = @fopen($url, 'r');
537                                 @fclose($fp);
538                         }
539                 }
540
541         }
542
543         function init()
544         {
545                 global $admin;
546                 $language = ereg_replace( '[\\|/]', '', getLanguageName());
547                 if (file_exists($this->getDirectory() . $language.'.php')) {
548                         include_once($this->getDirectory() . $language.'.php');
549                 }else {
550                         include_once($this->getDirectory() . 'english.php');
551                 }
552         }
553
554         function install()
555         {
556                 $this->createOption('AllBlogMap',         _G_SITEMAP_ALLB,   'yesno', 'yes');
557                 $this->createBlogOption('IncludeSitemap', _G_SITEMAP_INC,    'yesno', 'yes');
558                 $this->createBlogOption('PingGoogle',     _G_SITEMAP_PING_G, 'yesno', 'yes');
559                 $this->createBlogOption('PingYahoo',      _G_SITEMAP_PING_Y, 'yesno', 'no');
560                 $this->createBlogOption('YahooAPID',      _G_SITEMAP_YAPID,  'text',  '');
561                 $this->createBlogOption('PcSitemap',      _G_SITEMAP_PCSM,   'text',  'sitemap.xml');
562                 $this->createBlogOption('MobileSitemap',  _G_SITEMAP_MBSM,   'text',  '');
563                 $this->createBlogOption('blogPriority',   _G_SITEMAP_BPRI,   'text',  '10', 'datatype=numerical');
564                 $this->createCategoryOption('catPriority', _G_SITEMAP_CPRI,  'text',  '9', 'datatype=numerical');
565                 $this->createItemOption('itemPriority',    _G_SITEMAP_IPRI,   'text',  '10', 'datatype=numerical');
566         }
567 }