18c2e8227SGreg Roach<?php 28c2e8227SGreg Roach/** 38c2e8227SGreg Roach * webtrees: online genealogy 46bdf7674SGreg Roach * Copyright (C) 2017 webtrees development team 58c2e8227SGreg Roach * This program is free software: you can redistribute it and/or modify 68c2e8227SGreg Roach * it under the terms of the GNU General Public License as published by 78c2e8227SGreg Roach * the Free Software Foundation, either version 3 of the License, or 88c2e8227SGreg Roach * (at your option) any later version. 98c2e8227SGreg Roach * This program is distributed in the hope that it will be useful, 108c2e8227SGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of 118c2e8227SGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 128c2e8227SGreg Roach * GNU General Public License for more details. 138c2e8227SGreg Roach * You should have received a copy of the GNU General Public License 148c2e8227SGreg Roach * along with this program. If not, see <http://www.gnu.org/licenses/>. 158c2e8227SGreg Roach */ 1676692c8bSGreg Roachnamespace Fisharebest\Webtrees\Module; 1776692c8bSGreg Roach 180e62c4b8SGreg Roachuse Fisharebest\Webtrees\Auth; 1915d603e7SGreg Roachuse Fisharebest\Webtrees\Bootstrap4; 200e62c4b8SGreg Roachuse Fisharebest\Webtrees\Controller\PageController; 210e62c4b8SGreg Roachuse Fisharebest\Webtrees\Database; 220e62c4b8SGreg Roachuse Fisharebest\Webtrees\Filter; 23b1b85189SGreg Roachuse Fisharebest\Webtrees\Html; 240e62c4b8SGreg Roachuse Fisharebest\Webtrees\I18N; 250e62c4b8SGreg Roachuse Fisharebest\Webtrees\Individual; 260e62c4b8SGreg Roachuse Fisharebest\Webtrees\Media; 270e62c4b8SGreg Roachuse Fisharebest\Webtrees\Note; 280e62c4b8SGreg Roachuse Fisharebest\Webtrees\Repository; 290e62c4b8SGreg Roachuse Fisharebest\Webtrees\Source; 300e62c4b8SGreg Roachuse Fisharebest\Webtrees\Tree; 318c2e8227SGreg Roach 328c2e8227SGreg Roach/** 338c2e8227SGreg Roach * Class SiteMapModule 348c2e8227SGreg Roach */ 35e2a378d3SGreg Roachclass SiteMapModule extends AbstractModule implements ModuleConfigInterface { 368c2e8227SGreg Roach const RECORDS_PER_VOLUME = 500; // Keep sitemap files small, for memory, CPU and max_allowed_packet limits. 378c2e8227SGreg Roach const CACHE_LIFE = 1209600; // Two weeks 388c2e8227SGreg Roach 398c2e8227SGreg Roach /** {@inheritdoc} */ 408c2e8227SGreg Roach public function getTitle() { 418c2e8227SGreg Roach return /* I18N: Name of a module - see http://en.wikipedia.org/wiki/Sitemaps */ I18N::translate('Sitemaps'); 428c2e8227SGreg Roach } 438c2e8227SGreg Roach 448c2e8227SGreg Roach /** {@inheritdoc} */ 458c2e8227SGreg Roach public function getDescription() { 468c2e8227SGreg Roach return /* I18N: Description of the “Sitemaps” module */ I18N::translate('Generate sitemap files for search engines.'); 478c2e8227SGreg Roach } 488c2e8227SGreg Roach 4976692c8bSGreg Roach /** 5076692c8bSGreg Roach * This is a general purpose hook, allowing modules to respond to routes 5176692c8bSGreg Roach * of the form module.php?mod=FOO&mod_action=BAR 5276692c8bSGreg Roach * 5376692c8bSGreg Roach * @param string $mod_action 5476692c8bSGreg Roach */ 558c2e8227SGreg Roach public function modAction($mod_action) { 568c2e8227SGreg Roach switch ($mod_action) { 578c2e8227SGreg Roach case 'admin': 588c2e8227SGreg Roach $this->admin(); 598c2e8227SGreg Roach break; 608c2e8227SGreg Roach case 'generate': 618c2e8227SGreg Roach $this->generate(Filter::get('file')); 628c2e8227SGreg Roach break; 638c2e8227SGreg Roach default: 648c2e8227SGreg Roach http_response_code(404); 658c2e8227SGreg Roach } 668c2e8227SGreg Roach } 678c2e8227SGreg Roach 688c2e8227SGreg Roach /** 6976692c8bSGreg Roach * Generate an XML file. 7076692c8bSGreg Roach * 718c2e8227SGreg Roach * @param string $file 728c2e8227SGreg Roach */ 738c2e8227SGreg Roach private function generate($file) { 748c2e8227SGreg Roach if ($file == 'sitemap.xml') { 758c2e8227SGreg Roach $this->generateIndex(); 768c2e8227SGreg Roach } elseif (preg_match('/^sitemap-(\d+)-([isrmn])-(\d+).xml$/', $file, $match)) { 778c2e8227SGreg Roach $this->generateFile($match[1], $match[2], $match[3]); 788c2e8227SGreg Roach } else { 798c2e8227SGreg Roach http_response_code(404); 808c2e8227SGreg Roach } 818c2e8227SGreg Roach } 828c2e8227SGreg Roach 838c2e8227SGreg Roach /** 848c2e8227SGreg Roach * The index file contains references to all the other files. 858c2e8227SGreg Roach * These files are the same for visitors/users/admins. 868c2e8227SGreg Roach */ 878c2e8227SGreg Roach private function generateIndex() { 888c2e8227SGreg Roach // Check the cache 8915d603e7SGreg Roach $timestamp = (int) $this->getPreference('sitemap.timestamp'); 908c2e8227SGreg Roach if ($timestamp > WT_TIMESTAMP - self::CACHE_LIFE) { 9115d603e7SGreg Roach $data = $this->getPreference('sitemap.xml'); 928c2e8227SGreg Roach } else { 938c2e8227SGreg Roach $data = ''; 948c2e8227SGreg Roach $lastmod = '<lastmod>' . date('Y-m-d') . '</lastmod>'; 958c2e8227SGreg Roach foreach (Tree::getAll() as $tree) { 968c2e8227SGreg Roach if ($tree->getPreference('include_in_sitemap')) { 978c2e8227SGreg Roach $n = Database::prepare( 988c2e8227SGreg Roach "SELECT COUNT(*) FROM `##individuals` WHERE i_file = :tree_id" 9913abd6f3SGreg Roach )->execute(['tree_id' => $tree->getTreeId()])->fetchOne(); 1008c2e8227SGreg Roach for ($i = 0; $i <= $n / self::RECORDS_PER_VOLUME; ++$i) { 1018c2e8227SGreg Roach $data .= '<sitemap><loc>' . WT_BASE_URL . 'module.php?mod=' . $this->getName() . '&mod_action=generate&file=sitemap-' . $tree->getTreeId() . '-i-' . $i . '.xml</loc>' . $lastmod . '</sitemap>' . PHP_EOL; 1028c2e8227SGreg Roach } 1038c2e8227SGreg Roach $n = Database::prepare( 1048c2e8227SGreg Roach "SELECT COUNT(*) FROM `##sources` WHERE s_file = :tree_id" 10513abd6f3SGreg Roach )->execute(['tree_id' => $tree->getTreeId()])->fetchOne(); 1068c2e8227SGreg Roach if ($n) { 1078c2e8227SGreg Roach for ($i = 0; $i <= $n / self::RECORDS_PER_VOLUME; ++$i) { 1088c2e8227SGreg Roach $data .= '<sitemap><loc>' . WT_BASE_URL . 'module.php?mod=' . $this->getName() . '&mod_action=generate&file=sitemap-' . $tree->getTreeId() . '-s-' . $i . '.xml</loc>' . $lastmod . '</sitemap>' . PHP_EOL; 1098c2e8227SGreg Roach } 1108c2e8227SGreg Roach } 1118c2e8227SGreg Roach $n = Database::prepare( 1128c2e8227SGreg Roach "SELECT COUNT(*) FROM `##other` WHERE o_file = :tree_id AND o_type = 'REPO'" 11313abd6f3SGreg Roach )->execute(['tree_id' => $tree->getTreeId()])->fetchOne(); 1148c2e8227SGreg Roach if ($n) { 1158c2e8227SGreg Roach for ($i = 0; $i <= $n / self::RECORDS_PER_VOLUME; ++$i) { 1168c2e8227SGreg Roach $data .= '<sitemap><loc>' . WT_BASE_URL . 'module.php?mod=' . $this->getName() . '&mod_action=generate&file=sitemap-' . $tree->getTreeId() . '-r-' . $i . '.xml</loc>' . $lastmod . '</sitemap>' . PHP_EOL; 1178c2e8227SGreg Roach } 1188c2e8227SGreg Roach } 1198c2e8227SGreg Roach $n = Database::prepare( 1208c2e8227SGreg Roach "SELECT COUNT(*) FROM `##other` WHERE o_file = :tree_id AND o_type = 'NOTE'" 12113abd6f3SGreg Roach )->execute(['tree_id' => $tree->getTreeId()])->fetchOne(); 1228c2e8227SGreg Roach if ($n) { 1238c2e8227SGreg Roach for ($i = 0; $i <= $n / self::RECORDS_PER_VOLUME; ++$i) { 1248c2e8227SGreg Roach $data .= '<sitemap><loc>' . WT_BASE_URL . 'module.php?mod=' . $this->getName() . '&mod_action=generate&file=sitemap-' . $tree->getTreeId() . '-n-' . $i . '.xml</loc>' . $lastmod . '</sitemap>' . PHP_EOL; 1258c2e8227SGreg Roach } 1268c2e8227SGreg Roach } 1278c2e8227SGreg Roach $n = Database::prepare( 1288c2e8227SGreg Roach "SELECT COUNT(*) FROM `##media` WHERE m_file = :tree_id" 12913abd6f3SGreg Roach )->execute(['tree_id' => $tree->getTreeId()])->fetchOne(); 1308c2e8227SGreg Roach if ($n) { 1318c2e8227SGreg Roach for ($i = 0; $i <= $n / self::RECORDS_PER_VOLUME; ++$i) { 1328c2e8227SGreg Roach $data .= '<sitemap><loc>' . WT_BASE_URL . 'module.php?mod=' . $this->getName() . '&mod_action=generate&file=sitemap-' . $tree->getTreeId() . '-m-' . $i . '.xml</loc>' . $lastmod . '</sitemap>' . PHP_EOL; 1338c2e8227SGreg Roach } 1348c2e8227SGreg Roach } 1358c2e8227SGreg Roach } 1368c2e8227SGreg Roach } 1378c2e8227SGreg Roach $data = '<' . '?xml version="1.0" encoding="UTF-8" ?' . '>' . PHP_EOL . '<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' . PHP_EOL . $data . '</sitemapindex>' . PHP_EOL; 1388c2e8227SGreg Roach // Cache this data. 13915d603e7SGreg Roach $this->setPreference('sitemap.xml', $data); 14015d603e7SGreg Roach $this->setPreference('sitemap.timestamp', WT_TIMESTAMP); 1418c2e8227SGreg Roach } 1428c2e8227SGreg Roach header('Content-Type: application/xml'); 1438c2e8227SGreg Roach header('Content-Length: ' . strlen($data)); 1448c2e8227SGreg Roach echo $data; 1458c2e8227SGreg Roach } 1468c2e8227SGreg Roach 1478c2e8227SGreg Roach /** 1488c2e8227SGreg Roach * A separate file for each family tree and each record type. 1498c2e8227SGreg Roach * These files depend on access levels, so only cache for visitors. 1508c2e8227SGreg Roach * 151cbc1590aSGreg Roach * @param int $ged_id 1528c2e8227SGreg Roach * @param string $rec_type 1538c2e8227SGreg Roach * @param string $volume 1548c2e8227SGreg Roach */ 1558c2e8227SGreg Roach private function generateFile($ged_id, $rec_type, $volume) { 15624ec66ceSGreg Roach $tree = Tree::findById($ged_id); 1578c2e8227SGreg Roach // Check the cache 15815d603e7SGreg Roach $timestamp = (int) $this->getPreference('sitemap-' . $ged_id . '-' . $rec_type . '-' . $volume . '.timestamp'); 1598c2e8227SGreg Roach if ($timestamp > WT_TIMESTAMP - self::CACHE_LIFE && !Auth::check()) { 16015d603e7SGreg Roach $data = $this->getPreference('sitemap-' . $ged_id . '-' . $rec_type . '-' . $volume . '.xml'); 1618c2e8227SGreg Roach } else { 1628c2e8227SGreg Roach $data = '<url><loc>' . WT_BASE_URL . 'index.php?ctype=gedcom&ged=' . $tree->getNameUrl() . '</loc></url>' . PHP_EOL; 16313abd6f3SGreg Roach $records = []; 1648c2e8227SGreg Roach switch ($rec_type) { 1658c2e8227SGreg Roach case 'i': 1668c2e8227SGreg Roach $rows = Database::prepare( 16724ec66ceSGreg Roach "SELECT i_id AS xref, i_gedcom AS gedcom" . 1688c2e8227SGreg Roach " FROM `##individuals`" . 1698c2e8227SGreg Roach " WHERE i_file = :tree_id" . 1708c2e8227SGreg Roach " ORDER BY i_id" . 1718c2e8227SGreg Roach " LIMIT :limit OFFSET :offset" 17213abd6f3SGreg Roach )->execute([ 1738c2e8227SGreg Roach 'tree_id' => $ged_id, 1748c2e8227SGreg Roach 'limit' => self::RECORDS_PER_VOLUME, 1758c2e8227SGreg Roach 'offset' => self::RECORDS_PER_VOLUME * $volume, 17613abd6f3SGreg Roach ])->fetchAll(); 1778c2e8227SGreg Roach foreach ($rows as $row) { 17824ec66ceSGreg Roach $records[] = Individual::getInstance($row->xref, $tree, $row->gedcom); 1798c2e8227SGreg Roach } 1808c2e8227SGreg Roach break; 1818c2e8227SGreg Roach case 's': 1828c2e8227SGreg Roach $rows = Database::prepare( 18324ec66ceSGreg Roach "SELECT s_id AS xref, s_gedcom AS gedcom" . 1848c2e8227SGreg Roach " FROM `##sources`" . 1858c2e8227SGreg Roach " WHERE s_file = :tree_id" . 1868c2e8227SGreg Roach " ORDER BY s_id" . 1878c2e8227SGreg Roach " LIMIT :limit OFFSET :offset" 18813abd6f3SGreg Roach )->execute([ 1898c2e8227SGreg Roach 'tree_id' => $ged_id, 1908c2e8227SGreg Roach 'limit' => self::RECORDS_PER_VOLUME, 1918c2e8227SGreg Roach 'offset' => self::RECORDS_PER_VOLUME * $volume, 19213abd6f3SGreg Roach ])->fetchAll(); 1938c2e8227SGreg Roach foreach ($rows as $row) { 19424ec66ceSGreg Roach $records[] = Source::getInstance($row->xref, $tree, $row->gedcom); 1958c2e8227SGreg Roach } 1968c2e8227SGreg Roach break; 1978c2e8227SGreg Roach case 'r': 1988c2e8227SGreg Roach $rows = Database::prepare( 19924ec66ceSGreg Roach "SELECT o_id AS xref, o_gedcom AS gedcom" . 2008c2e8227SGreg Roach " FROM `##other`" . 2018c2e8227SGreg Roach " WHERE o_file = :tree_id AND o_type = 'REPO'" . 2028c2e8227SGreg Roach " ORDER BY o_id" . 2038c2e8227SGreg Roach " LIMIT :limit OFFSET :offset" 20413abd6f3SGreg Roach )->execute([ 2058c2e8227SGreg Roach 'tree_id' => $ged_id, 2068c2e8227SGreg Roach 'limit' => self::RECORDS_PER_VOLUME, 2078c2e8227SGreg Roach 'offset' => self::RECORDS_PER_VOLUME * $volume, 20813abd6f3SGreg Roach ])->fetchAll(); 2098c2e8227SGreg Roach foreach ($rows as $row) { 21024ec66ceSGreg Roach $records[] = Repository::getInstance($row->xref, $tree, $row->gedcom); 2118c2e8227SGreg Roach } 2128c2e8227SGreg Roach break; 2138c2e8227SGreg Roach case 'n': 2148c2e8227SGreg Roach $rows = Database::prepare( 21524ec66ceSGreg Roach "SELECT o_id AS xref, o_gedcom AS gedcom" . 2168c2e8227SGreg Roach " FROM `##other`" . 2178c2e8227SGreg Roach " WHERE o_file = :tree_id AND o_type = 'NOTE'" . 2188c2e8227SGreg Roach " ORDER BY o_id" . 2198c2e8227SGreg Roach " LIMIT :limit OFFSET :offset" 22013abd6f3SGreg Roach )->execute([ 2218c2e8227SGreg Roach 'tree_id' => $ged_id, 2228c2e8227SGreg Roach 'limit' => self::RECORDS_PER_VOLUME, 2238c2e8227SGreg Roach 'offset' => self::RECORDS_PER_VOLUME * $volume, 22413abd6f3SGreg Roach ])->fetchAll(); 2258c2e8227SGreg Roach foreach ($rows as $row) { 22624ec66ceSGreg Roach $records[] = Note::getInstance($row->xref, $tree, $row->gedcom); 2278c2e8227SGreg Roach } 2288c2e8227SGreg Roach break; 2298c2e8227SGreg Roach case 'm': 2308c2e8227SGreg Roach $rows = Database::prepare( 23124ec66ceSGreg Roach "SELECT m_id AS xref, m_gedcom AS gedcom" . 2328c2e8227SGreg Roach " FROM `##media`" . 2338c2e8227SGreg Roach " WHERE m_file = :tree_id" . 2348c2e8227SGreg Roach " ORDER BY m_id" . 2358c2e8227SGreg Roach " LIMIT :limit OFFSET :offset" 23613abd6f3SGreg Roach )->execute([ 2378c2e8227SGreg Roach 'tree_id' => $ged_id, 2388c2e8227SGreg Roach 'limit' => self::RECORDS_PER_VOLUME, 2398c2e8227SGreg Roach 'offset' => self::RECORDS_PER_VOLUME * $volume, 24013abd6f3SGreg Roach ])->fetchAll(); 2418c2e8227SGreg Roach foreach ($rows as $row) { 24224ec66ceSGreg Roach $records[] = Media::getInstance($row->xref, $tree, $row->gedcom); 2438c2e8227SGreg Roach } 2448c2e8227SGreg Roach break; 2458c2e8227SGreg Roach } 2468c2e8227SGreg Roach foreach ($records as $record) { 2478c2e8227SGreg Roach if ($record->canShowName()) { 2488c2e8227SGreg Roach $data .= '<url>'; 2498c2e8227SGreg Roach $data .= '<loc>' . WT_BASE_URL . $record->getHtmlUrl() . '</loc>'; 2508c2e8227SGreg Roach $chan = $record->getFirstFact('CHAN'); 2518c2e8227SGreg Roach if ($chan) { 2528c2e8227SGreg Roach $date = $chan->getDate(); 2538c2e8227SGreg Roach if ($date->isOK()) { 2542a05b1e4SGreg Roach $data .= '<lastmod>' . $date->minimumDate()->Format('%Y-%m-%d') . '</lastmod>'; 2558c2e8227SGreg Roach } 2568c2e8227SGreg Roach } 2578c2e8227SGreg Roach $data .= '</url>' . PHP_EOL; 2588c2e8227SGreg Roach } 2598c2e8227SGreg Roach } 2608c2e8227SGreg Roach $data = '<' . '?xml version="1.0" encoding="UTF-8" ?' . '>' . PHP_EOL . '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">' . PHP_EOL . $data . '</urlset>' . PHP_EOL; 2618c2e8227SGreg Roach // Cache this data - but only for visitors, as we don’t want 262cdc90107SGreg Roach // visitors to see data created by signed-in users. 2638c2e8227SGreg Roach if (!Auth::check()) { 26415d603e7SGreg Roach $this->setPreference('sitemap-' . $ged_id . '-' . $rec_type . '-' . $volume . '.xml', $data); 26515d603e7SGreg Roach $this->setPreference('sitemap-' . $ged_id . '-' . $rec_type . '-' . $volume . '.timestamp', WT_TIMESTAMP); 2668c2e8227SGreg Roach } 2678c2e8227SGreg Roach } 2688c2e8227SGreg Roach header('Content-Type: application/xml'); 2698c2e8227SGreg Roach header('Content-Length: ' . strlen($data)); 2708c2e8227SGreg Roach echo $data; 2718c2e8227SGreg Roach } 2728c2e8227SGreg Roach 2738c2e8227SGreg Roach /** 2748c2e8227SGreg Roach * Edit the configuration 2758c2e8227SGreg Roach */ 2768c2e8227SGreg Roach private function admin() { 2778c2e8227SGreg Roach $controller = new PageController; 2788c2e8227SGreg Roach $controller 2798c2e8227SGreg Roach ->restrictAccess(Auth::isAdmin()) 2808c2e8227SGreg Roach ->setPageTitle($this->getTitle()) 2818c2e8227SGreg Roach ->pageHeader(); 2828c2e8227SGreg Roach 2838c2e8227SGreg Roach // Save the updated preferences 2848c2e8227SGreg Roach if (Filter::post('action') == 'save') { 2858c2e8227SGreg Roach foreach (Tree::getAll() as $tree) { 2868c2e8227SGreg Roach $tree->setPreference('include_in_sitemap', Filter::postBool('include' . $tree->getTreeId())); 2878c2e8227SGreg Roach } 2888c2e8227SGreg Roach // Clear cache and force files to be regenerated 2898c2e8227SGreg Roach Database::prepare( 2908c2e8227SGreg Roach "DELETE FROM `##module_setting` WHERE setting_name LIKE 'sitemap%'" 2918c2e8227SGreg Roach )->execute(); 2928c2e8227SGreg Roach } 2938c2e8227SGreg Roach 2948c2e8227SGreg Roach $include_any = false; 2958c2e8227SGreg Roach 29615d603e7SGreg Roach echo Bootstrap4::breadcrumbs([ 2971f3fb95cSGreg Roach route('admin-control-panel') => I18N::translate('Control panel'), 2981f3fb95cSGreg Roach route('admin-modules') => I18N::translate('Module administration'), 29915d603e7SGreg Roach ], $controller->getPageTitle()); 3008c2e8227SGreg Roach ?> 30115d603e7SGreg Roach 30215d603e7SGreg Roach <h1><?= $controller->getPageTitle() ?></h1> 3038c2e8227SGreg Roach <?php 3048c2e8227SGreg Roach 3058c2e8227SGreg Roach echo 3068c2e8227SGreg Roach '<p>', 3078c2e8227SGreg Roach /* I18N: The www.sitemaps.org site is translated into many languages (e.g. http://www.sitemaps.org/fr/) - choose an appropriate URL. */ 3088c2e8227SGreg Roach I18N::translate('Sitemaps are a way for webmasters to tell search engines about the pages on a website that are available for crawling. All major search engines support sitemaps. For more information, see <a href="http://www.sitemaps.org/">www.sitemaps.org</a>.') . 3098c2e8227SGreg Roach '</p>', 310a4d5a9c2SGreg Roach '<p>', /* I18N: Label for a configuration option */ I18N::translate('Which family trees should be included in the sitemaps'), '</p>', 3118c2e8227SGreg Roach '<form method="post" action="module.php?mod=' . $this->getName() . '&mod_action=admin">', 3128c2e8227SGreg Roach '<input type="hidden" name="action" value="save">'; 3138c2e8227SGreg Roach foreach (Tree::getAll() as $tree) { 314*fff20713SGreg Roach echo '<div class="form-check"><label><input type="checkbox" name="include', $tree->getTreeId(), '" '; 3158c2e8227SGreg Roach if ($tree->getPreference('include_in_sitemap')) { 3168c2e8227SGreg Roach echo 'checked'; 3178c2e8227SGreg Roach $include_any = true; 3188c2e8227SGreg Roach } 319adffb21dSGreg Roach echo '>', $tree->getTitleHtml(), '</label></div>'; 3208c2e8227SGreg Roach } 3218c2e8227SGreg Roach echo 3228c2e8227SGreg Roach '<input type="submit" value="', I18N::translate('save'), '">', 3238c2e8227SGreg Roach '</form>', 3248c2e8227SGreg Roach '<hr>'; 3258c2e8227SGreg Roach 3268c2e8227SGreg Roach if ($include_any) { 3278c2e8227SGreg Roach $site_map_url1 = WT_BASE_URL . 'module.php?mod=' . $this->getName() . '&mod_action=generate&file=sitemap.xml'; 3288c2e8227SGreg Roach $site_map_url2 = rawurlencode(WT_BASE_URL . 'module.php?mod=' . $this->getName() . '&mod_action=generate&file=sitemap.xml'); 3298c2e8227SGreg Roach echo 3308c2e8227SGreg Roach '<p>', I18N::translate('To tell search engines that sitemaps are available, you should add the following line to your robots.txt file.'), '</p>', 3318c2e8227SGreg Roach '<pre>Sitemap: ', $site_map_url1, '</pre>', 3328c2e8227SGreg Roach '<hr>', 3338c2e8227SGreg Roach '<p>', I18N::translate('To tell search engines that sitemaps are available, you can use the following links.'), '</p>', 3348c2e8227SGreg Roach '<ul>', 3358c2e8227SGreg Roach // This list comes from http://en.wikipedia.org/wiki/Sitemaps 3362032bcddSRobert Scheck '<li><a href="https://www.bing.com/webmaster/ping.aspx?siteMap=' . $site_map_url2 . '">Bing</a></li>', 3372032bcddSRobert Scheck '<li><a href="https://www.google.com/webmasters/tools/ping?sitemap=' . $site_map_url2 . '">Google</a></li>', 3388c2e8227SGreg Roach '</ul>'; 3398c2e8227SGreg Roach } 3408c2e8227SGreg Roach } 3418c2e8227SGreg Roach 3428c2e8227SGreg Roach /** {@inheritdoc} */ 3438c2e8227SGreg Roach public function getConfigLink() { 344b1b85189SGreg Roach return Html::url('module.php', [ 345b1b85189SGreg Roach 'mod' => $this->getName(), 346b1b85189SGreg Roach 'mod_action' => 'admin', 347b1b85189SGreg Roach ]); 3488c2e8227SGreg Roach } 3498c2e8227SGreg Roach} 350