18c2e8227SGreg Roach<?php 23976b470SGreg Roach 38c2e8227SGreg Roach/** 48c2e8227SGreg Roach * webtrees: online genealogy 5*89f7189bSGreg Roach * Copyright (C) 2021 webtrees development team 68c2e8227SGreg Roach * This program is free software: you can redistribute it and/or modify 78c2e8227SGreg Roach * it under the terms of the GNU General Public License as published by 88c2e8227SGreg Roach * the Free Software Foundation, either version 3 of the License, or 98c2e8227SGreg Roach * (at your option) any later version. 108c2e8227SGreg Roach * This program is distributed in the hope that it will be useful, 118c2e8227SGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of 128c2e8227SGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 138c2e8227SGreg Roach * GNU General Public License for more details. 148c2e8227SGreg Roach * You should have received a copy of the GNU General Public License 15*89f7189bSGreg Roach * along with this program. If not, see <https://www.gnu.org/licenses/>. 168c2e8227SGreg Roach */ 17fcfa147eSGreg Roach 18e7f56f2aSGreg Roachdeclare(strict_types=1); 19e7f56f2aSGreg Roach 2076692c8bSGreg Roachnamespace Fisharebest\Webtrees\Module; 2176692c8bSGreg Roach 2216a40a66SGreg Roachuse Aura\Router\Route; 2316a40a66SGreg Roachuse Aura\Router\RouterContainer; 246ccdf4f0SGreg Roachuse Fig\Http\Message\StatusCodeInterface; 25659aa375SGreg Roachuse Fisharebest\Webtrees\Auth; 26d501c45dSGreg Roachuse Fisharebest\Webtrees\Exceptions\HttpNotFoundException; 276b9cb339SGreg Roachuse Fisharebest\Webtrees\Registry; 28c5a1ffe6SGreg Roachuse Fisharebest\Webtrees\Family; 29a5f7ed67SGreg Roachuse Fisharebest\Webtrees\FlashMessages; 30a5f7ed67SGreg Roachuse Fisharebest\Webtrees\GedcomRecord; 31b1b85189SGreg Roachuse Fisharebest\Webtrees\Html; 320e62c4b8SGreg Roachuse Fisharebest\Webtrees\I18N; 330e62c4b8SGreg Roachuse Fisharebest\Webtrees\Individual; 340e62c4b8SGreg Roachuse Fisharebest\Webtrees\Media; 350e62c4b8SGreg Roachuse Fisharebest\Webtrees\Note; 360e62c4b8SGreg Roachuse Fisharebest\Webtrees\Repository; 373df1e584SGreg Roachuse Fisharebest\Webtrees\Services\TreeService; 380e62c4b8SGreg Roachuse Fisharebest\Webtrees\Source; 39c5a1ffe6SGreg Roachuse Fisharebest\Webtrees\Submitter; 400e62c4b8SGreg Roachuse Fisharebest\Webtrees\Tree; 41fa17fb66SGreg Roachuse Illuminate\Database\Capsule\Manager as DB; 42a69f5655SGreg Roachuse Illuminate\Database\Query\Expression; 43886b77daSGreg Roachuse Illuminate\Support\Collection; 446ccdf4f0SGreg Roachuse Psr\Http\Message\ResponseInterface; 456ccdf4f0SGreg Roachuse Psr\Http\Message\ServerRequestInterface; 4616a40a66SGreg Roachuse Psr\Http\Server\RequestHandlerInterface; 47d519210eSGreg Roach 4816a40a66SGreg Roachuse function app; 4916a40a66SGreg Roachuse function assert; 5016a40a66SGreg Roachuse function date; 513df1e584SGreg Roachuse function redirect; 5216a40a66SGreg Roachuse function response; 5316a40a66SGreg Roachuse function route; 543df1e584SGreg Roachuse function view; 553df1e584SGreg Roach 568c2e8227SGreg Roach/** 578c2e8227SGreg Roach * Class SiteMapModule 588c2e8227SGreg Roach */ 5916a40a66SGreg Roachclass SiteMapModule extends AbstractModule implements ModuleConfigInterface, RequestHandlerInterface 60c1010edaSGreg Roach{ 6149a243cbSGreg Roach use ModuleConfigTrait; 6249a243cbSGreg Roach 6316d6367aSGreg Roach private const RECORDS_PER_VOLUME = 500; // Keep sitemap files small, for memory, CPU and max_allowed_packet limits. 640daf451eSGreg Roach private const CACHE_LIFE = 209600; // Two weeks 658c2e8227SGreg Roach 66c5a1ffe6SGreg Roach private const PRIORITY = [ 67c5a1ffe6SGreg Roach Family::RECORD_TYPE => 0.7, 68c5a1ffe6SGreg Roach Individual::RECORD_TYPE => 0.9, 69c5a1ffe6SGreg Roach Media::RECORD_TYPE => 0.5, 70c5a1ffe6SGreg Roach Note::RECORD_TYPE => 0.3, 71c5a1ffe6SGreg Roach Repository::RECORD_TYPE => 0.5, 72c5a1ffe6SGreg Roach Source::RECORD_TYPE => 0.5, 73c5a1ffe6SGreg Roach Submitter::RECORD_TYPE => 0.3, 74c5a1ffe6SGreg Roach ]; 75c5a1ffe6SGreg Roach 763df1e584SGreg Roach /** @var TreeService */ 773df1e584SGreg Roach private $tree_service; 783df1e584SGreg Roach 793df1e584SGreg Roach /** 803df1e584SGreg Roach * TreesMenuModule constructor. 813df1e584SGreg Roach * 823df1e584SGreg Roach * @param TreeService $tree_service 833df1e584SGreg Roach */ 843df1e584SGreg Roach public function __construct(TreeService $tree_service) 853df1e584SGreg Roach { 863df1e584SGreg Roach $this->tree_service = $tree_service; 873df1e584SGreg Roach } 883df1e584SGreg Roach 89a5f7ed67SGreg Roach /** 9016a40a66SGreg Roach * Initialization. 9116a40a66SGreg Roach * 9216a40a66SGreg Roach * @return void 9316a40a66SGreg Roach */ 9416a40a66SGreg Roach public function boot(): void 9516a40a66SGreg Roach { 9616a40a66SGreg Roach $router_container = app(RouterContainer::class); 9716a40a66SGreg Roach assert($router_container instanceof RouterContainer); 9816a40a66SGreg Roach 9916a40a66SGreg Roach $router_container->getMap() 10014aabe72SGreg Roach ->get('sitemap-style', '/sitemap.xsl', $this); 10114aabe72SGreg Roach 10214aabe72SGreg Roach $router_container->getMap() 10316a40a66SGreg Roach ->get('sitemap-index', '/sitemap.xml', $this); 10416a40a66SGreg Roach 10516a40a66SGreg Roach $router_container->getMap() 106c5a1ffe6SGreg Roach ->get('sitemap-file', '/sitemap-{tree}-{type}-{page}.xml', $this); 10716a40a66SGreg Roach } 10816a40a66SGreg Roach 10916a40a66SGreg Roach /** 110a5f7ed67SGreg Roach * A sentence describing what this module does. 111a5f7ed67SGreg Roach * 112a5f7ed67SGreg Roach * @return string 113a5f7ed67SGreg Roach */ 11449a243cbSGreg Roach public function description(): string 115c1010edaSGreg Roach { 116bbb76c12SGreg Roach /* I18N: Description of the “Sitemaps” module */ 117bbb76c12SGreg Roach return I18N::translate('Generate sitemap files for search engines.'); 1188c2e8227SGreg Roach } 1198c2e8227SGreg Roach 12076692c8bSGreg Roach /** 121abafa13cSGreg Roach * Should this module be enabled when it is first installed? 122abafa13cSGreg Roach * 123abafa13cSGreg Roach * @return bool 124abafa13cSGreg Roach */ 125abafa13cSGreg Roach public function isEnabledByDefault(): bool 126abafa13cSGreg Roach { 127abafa13cSGreg Roach return false; 128abafa13cSGreg Roach } 129abafa13cSGreg Roach 130abafa13cSGreg Roach /** 13157ab2231SGreg Roach * @param ServerRequestInterface $request 13257ab2231SGreg Roach * 1336ccdf4f0SGreg Roach * @return ResponseInterface 1348c2e8227SGreg Roach */ 13557ab2231SGreg Roach public function getAdminAction(ServerRequestInterface $request): ResponseInterface 136c1010edaSGreg Roach { 137a5f7ed67SGreg Roach $this->layout = 'layouts/administration'; 138a5f7ed67SGreg Roach 13916a40a66SGreg Roach $sitemap_url = route('sitemap-index'); 140a5f7ed67SGreg Roach 141ce42304aSGreg Roach // This list comes from https://en.wikipedia.org/wiki/Sitemaps 142a5f7ed67SGreg Roach $submit_urls = [ 143a5f7ed67SGreg Roach 'Bing/Yahoo' => Html::url('https://www.bing.com/webmaster/ping.aspx', ['siteMap' => $sitemap_url]), 144a5f7ed67SGreg Roach 'Google' => Html::url('https://www.google.com/webmasters/tools/ping', ['sitemap' => $sitemap_url]), 145a5f7ed67SGreg Roach ]; 146a5f7ed67SGreg Roach 147291c1b19SGreg Roach return $this->viewResponse('modules/sitemap/config', [ 1483df1e584SGreg Roach 'all_trees' => $this->tree_service->all(), 149a5f7ed67SGreg Roach 'sitemap_url' => $sitemap_url, 150a5f7ed67SGreg Roach 'submit_urls' => $submit_urls, 15149a243cbSGreg Roach 'title' => $this->title(), 152a5f7ed67SGreg Roach ]); 1538c2e8227SGreg Roach } 1548c2e8227SGreg Roach 1558c2e8227SGreg Roach /** 1566ccdf4f0SGreg Roach * How should this module be identified in the control panel, etc.? 157a5f7ed67SGreg Roach * 1586ccdf4f0SGreg Roach * @return string 1598c2e8227SGreg Roach */ 1606ccdf4f0SGreg Roach public function title(): string 1616ccdf4f0SGreg Roach { 1626ccdf4f0SGreg Roach /* I18N: Name of a module - see http://en.wikipedia.org/wiki/Sitemaps */ 1636ccdf4f0SGreg Roach return I18N::translate('Sitemaps'); 1646ccdf4f0SGreg Roach } 1656ccdf4f0SGreg Roach 1666ccdf4f0SGreg Roach /** 1676ccdf4f0SGreg Roach * @param ServerRequestInterface $request 1686ccdf4f0SGreg Roach * 1696ccdf4f0SGreg Roach * @return ResponseInterface 1706ccdf4f0SGreg Roach */ 1716ccdf4f0SGreg Roach public function postAdminAction(ServerRequestInterface $request): ResponseInterface 172c1010edaSGreg Roach { 173b46c87bdSGreg Roach $params = (array) $request->getParsedBody(); 174b6b9dcc9SGreg Roach 1753df1e584SGreg Roach foreach ($this->tree_service->all() as $tree) { 176b6b9dcc9SGreg Roach $include_in_sitemap = (bool) ($params['sitemap' . $tree->id()] ?? false); 177a5f7ed67SGreg Roach $tree->setPreference('include_in_sitemap', (string) $include_in_sitemap); 1788c2e8227SGreg Roach } 179a5f7ed67SGreg Roach 18049a243cbSGreg Roach FlashMessages::addMessage(I18N::translate('The preferences for the module “%s” have been updated.', $this->title()), 'success'); 181a5f7ed67SGreg Roach 1826ccdf4f0SGreg Roach return redirect($this->getConfigLink()); 1838c2e8227SGreg Roach } 1848c2e8227SGreg Roach 1858c2e8227SGreg Roach /** 18657ab2231SGreg Roach * @param ServerRequestInterface $request 18757ab2231SGreg Roach * 1886ccdf4f0SGreg Roach * @return ResponseInterface 1898c2e8227SGreg Roach */ 19016a40a66SGreg Roach public function handle(ServerRequestInterface $request): ResponseInterface 191c1010edaSGreg Roach { 19216a40a66SGreg Roach $route = $request->getAttribute('route'); 19316a40a66SGreg Roach assert($route instanceof Route); 194a5f7ed67SGreg Roach 19514aabe72SGreg Roach if ($route->name === 'sitemap-style') { 19614aabe72SGreg Roach $content = view('modules/sitemap/sitemap-xsl'); 19714aabe72SGreg Roach 19814aabe72SGreg Roach return response($content, StatusCodeInterface::STATUS_OK, [ 19914aabe72SGreg Roach 'Content-Type' => 'application/xml', 20014aabe72SGreg Roach ]); 20114aabe72SGreg Roach } 20214aabe72SGreg Roach 20316a40a66SGreg Roach if ($route->name === 'sitemap-index') { 20416a40a66SGreg Roach return $this->siteMapIndex($request); 20516a40a66SGreg Roach } 20616a40a66SGreg Roach 20716a40a66SGreg Roach return $this->siteMapFile($request); 20816a40a66SGreg Roach } 20916a40a66SGreg Roach 21016a40a66SGreg Roach /** 21116a40a66SGreg Roach * @param ServerRequestInterface $request 21216a40a66SGreg Roach * 21316a40a66SGreg Roach * @return ResponseInterface 21416a40a66SGreg Roach */ 21516a40a66SGreg Roach private function siteMapIndex(ServerRequestInterface $request): ResponseInterface 21616a40a66SGreg Roach { 2176b9cb339SGreg Roach $content = Registry::cache()->file()->remember('sitemap.xml', function (): string { 218659aa375SGreg Roach // Which trees have sitemaps enabled? 219659aa375SGreg Roach $tree_ids = $this->tree_service->all()->filter(static function (Tree $tree): bool { 220659aa375SGreg Roach return $tree->getPreference('include_in_sitemap') === '1'; 221659aa375SGreg Roach })->map(static function (Tree $tree): int { 222659aa375SGreg Roach return $tree->id(); 223659aa375SGreg Roach }); 224659aa375SGreg Roach 225c5a1ffe6SGreg Roach $count_families = DB::table('families') 226c5a1ffe6SGreg Roach ->join('gedcom', 'f_file', '=', 'gedcom_id') 227c5a1ffe6SGreg Roach ->whereIn('gedcom_id', $tree_ids) 228c5a1ffe6SGreg Roach ->groupBy(['gedcom_id']) 229c5a1ffe6SGreg Roach ->select([new Expression('COUNT(*) AS total'), 'gedcom_name']) 230c5a1ffe6SGreg Roach ->pluck('total', 'gedcom_name'); 231c5a1ffe6SGreg Roach 232fa17fb66SGreg Roach $count_individuals = DB::table('individuals') 23316a40a66SGreg Roach ->join('gedcom', 'i_file', '=', 'gedcom_id') 23416a40a66SGreg Roach ->whereIn('gedcom_id', $tree_ids) 23516a40a66SGreg Roach ->groupBy(['gedcom_id']) 23616a40a66SGreg Roach ->select([new Expression('COUNT(*) AS total'), 'gedcom_name']) 23716a40a66SGreg Roach ->pluck('total', 'gedcom_name'); 238a5f7ed67SGreg Roach 239fa17fb66SGreg Roach $count_media = DB::table('media') 24016a40a66SGreg Roach ->join('gedcom', 'm_file', '=', 'gedcom_id') 24116a40a66SGreg Roach ->whereIn('gedcom_id', $tree_ids) 24216a40a66SGreg Roach ->groupBy(['gedcom_id']) 24316a40a66SGreg Roach ->select([new Expression('COUNT(*) AS total'), 'gedcom_name']) 24416a40a66SGreg Roach ->pluck('total', 'gedcom_name'); 245a5f7ed67SGreg Roach 246fa17fb66SGreg Roach $count_notes = DB::table('other') 24716a40a66SGreg Roach ->join('gedcom', 'o_file', '=', 'gedcom_id') 24816a40a66SGreg Roach ->whereIn('gedcom_id', $tree_ids) 249c5a1ffe6SGreg Roach ->where('o_type', '=', Note::RECORD_TYPE) 25016a40a66SGreg Roach ->groupBy(['gedcom_id']) 25116a40a66SGreg Roach ->select([new Expression('COUNT(*) AS total'), 'gedcom_name']) 25216a40a66SGreg Roach ->pluck('total', 'gedcom_name'); 253a5f7ed67SGreg Roach 254fa17fb66SGreg Roach $count_repositories = DB::table('other') 25516a40a66SGreg Roach ->join('gedcom', 'o_file', '=', 'gedcom_id') 25616a40a66SGreg Roach ->whereIn('gedcom_id', $tree_ids) 257c5a1ffe6SGreg Roach ->where('o_type', '=', Repository::RECORD_TYPE) 25816a40a66SGreg Roach ->groupBy(['gedcom_id']) 25916a40a66SGreg Roach ->select([new Expression('COUNT(*) AS total'), 'gedcom_name']) 26016a40a66SGreg Roach ->pluck('total', 'gedcom_name'); 261a5f7ed67SGreg Roach 262fa17fb66SGreg Roach $count_sources = DB::table('sources') 26316a40a66SGreg Roach ->join('gedcom', 's_file', '=', 'gedcom_id') 26416a40a66SGreg Roach ->whereIn('gedcom_id', $tree_ids) 26516a40a66SGreg Roach ->groupBy(['gedcom_id']) 26616a40a66SGreg Roach ->select([new Expression('COUNT(*) AS total'), 'gedcom_name']) 26716a40a66SGreg Roach ->pluck('total', 'gedcom_name'); 268a5f7ed67SGreg Roach 269c5a1ffe6SGreg Roach $count_submitters = DB::table('other') 270c5a1ffe6SGreg Roach ->join('gedcom', 'o_file', '=', 'gedcom_id') 271c5a1ffe6SGreg Roach ->whereIn('gedcom_id', $tree_ids) 272c5a1ffe6SGreg Roach ->where('o_type', '=', Submitter::RECORD_TYPE) 273c5a1ffe6SGreg Roach ->groupBy(['gedcom_id']) 274c5a1ffe6SGreg Roach ->select([new Expression('COUNT(*) AS total'), 'gedcom_name']) 275c5a1ffe6SGreg Roach ->pluck('total', 'gedcom_name'); 276c5a1ffe6SGreg Roach 27716a40a66SGreg Roach // Versions 2.0.1 and earlier of this module stored large amounts of data in the settings. 27816a40a66SGreg Roach DB::table('module_setting') 27916a40a66SGreg Roach ->where('module_name', '=', $this->name()) 28016a40a66SGreg Roach ->delete(); 28116a40a66SGreg Roach 28214aabe72SGreg Roach return view('modules/sitemap/sitemap-index-xml', [ 2833df1e584SGreg Roach 'all_trees' => $this->tree_service->all(), 284c5a1ffe6SGreg Roach 'count_families' => $count_families, 285a5f7ed67SGreg Roach 'count_individuals' => $count_individuals, 286a5f7ed67SGreg Roach 'count_media' => $count_media, 287a5f7ed67SGreg Roach 'count_notes' => $count_notes, 288a5f7ed67SGreg Roach 'count_repositories' => $count_repositories, 289a5f7ed67SGreg Roach 'count_sources' => $count_sources, 290c5a1ffe6SGreg Roach 'count_submitters' => $count_submitters, 291a5f7ed67SGreg Roach 'last_mod' => date('Y-m-d'), 292a5f7ed67SGreg Roach 'records_per_volume' => self::RECORDS_PER_VOLUME, 29314aabe72SGreg Roach 'sitemap_xsl' => route('sitemap-style'), 294a5f7ed67SGreg Roach ]); 29516a40a66SGreg Roach }, self::CACHE_LIFE); 296a5f7ed67SGreg Roach 2976ccdf4f0SGreg Roach return response($content, StatusCodeInterface::STATUS_OK, [ 298a5f7ed67SGreg Roach 'Content-Type' => 'application/xml', 299a5f7ed67SGreg Roach ]); 300a5f7ed67SGreg Roach } 301a5f7ed67SGreg Roach 302a5f7ed67SGreg Roach /** 3036ccdf4f0SGreg Roach * @param ServerRequestInterface $request 304a5f7ed67SGreg Roach * 3056ccdf4f0SGreg Roach * @return ResponseInterface 306a5f7ed67SGreg Roach */ 30716a40a66SGreg Roach private function siteMapFile(ServerRequestInterface $request): ResponseInterface 308c1010edaSGreg Roach { 30916a40a66SGreg Roach $tree = $request->getAttribute('tree'); 31016a40a66SGreg Roach assert($tree instanceof Tree); 311a5f7ed67SGreg Roach 312c5a1ffe6SGreg Roach $type = $request->getAttribute('type'); 313c5a1ffe6SGreg Roach $page = (int) $request->getAttribute('page'); 314a5f7ed67SGreg Roach 315659aa375SGreg Roach if ($tree->getPreference('include_in_sitemap') !== '1') { 316659aa375SGreg Roach throw new HttpNotFoundException(); 317659aa375SGreg Roach } 318659aa375SGreg Roach 319c5a1ffe6SGreg Roach $cache_key = 'sitemap/' . $tree->id() . '/' . $type . '/' . $page . '.xml'; 32016a40a66SGreg Roach 3216b9cb339SGreg Roach $content = Registry::cache()->file()->remember($cache_key, function () use ($tree, $type, $page): string { 322c5a1ffe6SGreg Roach $records = $this->sitemapRecords($tree, $type, self::RECORDS_PER_VOLUME, self::RECORDS_PER_VOLUME * $page); 32316a40a66SGreg Roach 32414aabe72SGreg Roach return view('modules/sitemap/sitemap-file-xml', [ 325c5a1ffe6SGreg Roach 'priority' => self::PRIORITY[$type], 3269e3c7a99SGreg Roach 'records' => $records, 32714aabe72SGreg Roach 'sitemap_xsl' => route('sitemap-style'), 3289e3c7a99SGreg Roach 'tree' => $tree, 3299e3c7a99SGreg Roach ]); 33016a40a66SGreg Roach }, self::CACHE_LIFE); 331a5f7ed67SGreg Roach 3326ccdf4f0SGreg Roach return response($content, StatusCodeInterface::STATUS_OK, [ 333a5f7ed67SGreg Roach 'Content-Type' => 'application/xml', 334a5f7ed67SGreg Roach ]); 335a5f7ed67SGreg Roach } 336a5f7ed67SGreg Roach 337a5f7ed67SGreg Roach /** 338a5f7ed67SGreg Roach * @param Tree $tree 339a5f7ed67SGreg Roach * @param string $type 340a5f7ed67SGreg Roach * @param int $limit 341a5f7ed67SGreg Roach * @param int $offset 342a5f7ed67SGreg Roach * 343b5c8fd7eSGreg Roach * @return Collection<GedcomRecord> 344a5f7ed67SGreg Roach */ 345886b77daSGreg Roach private function sitemapRecords(Tree $tree, string $type, int $limit, int $offset): Collection 346c1010edaSGreg Roach { 347a5f7ed67SGreg Roach switch ($type) { 348c5a1ffe6SGreg Roach case Family::RECORD_TYPE: 349c5a1ffe6SGreg Roach $records = $this->sitemapFamilies($tree, $limit, $offset); 350c5a1ffe6SGreg Roach break; 351c5a1ffe6SGreg Roach 35216a40a66SGreg Roach case Individual::RECORD_TYPE: 353a5f7ed67SGreg Roach $records = $this->sitemapIndividuals($tree, $limit, $offset); 354a5f7ed67SGreg Roach break; 355a5f7ed67SGreg Roach 35616a40a66SGreg Roach case Media::RECORD_TYPE: 357a5f7ed67SGreg Roach $records = $this->sitemapMedia($tree, $limit, $offset); 358a5f7ed67SGreg Roach break; 359a5f7ed67SGreg Roach 36016a40a66SGreg Roach case Note::RECORD_TYPE: 361a5f7ed67SGreg Roach $records = $this->sitemapNotes($tree, $limit, $offset); 362a5f7ed67SGreg Roach break; 363a5f7ed67SGreg Roach 36416a40a66SGreg Roach case Repository::RECORD_TYPE: 365a5f7ed67SGreg Roach $records = $this->sitemapRepositories($tree, $limit, $offset); 366a5f7ed67SGreg Roach break; 367a5f7ed67SGreg Roach 36816a40a66SGreg Roach case Source::RECORD_TYPE: 369a5f7ed67SGreg Roach $records = $this->sitemapSources($tree, $limit, $offset); 370a5f7ed67SGreg Roach break; 371a5f7ed67SGreg Roach 372c5a1ffe6SGreg Roach case Submitter::RECORD_TYPE: 373c5a1ffe6SGreg Roach $records = $this->sitemapSubmitters($tree, $limit, $offset); 374c5a1ffe6SGreg Roach break; 375c5a1ffe6SGreg Roach 376a5f7ed67SGreg Roach default: 377d501c45dSGreg Roach throw new HttpNotFoundException('Invalid record type: ' . $type); 378a5f7ed67SGreg Roach } 379a5f7ed67SGreg Roach 380a5f7ed67SGreg Roach // Skip private records. 381659aa375SGreg Roach $records = $records->filter(static function (GedcomRecord $record): bool { 382659aa375SGreg Roach return $record->canShow(Auth::PRIV_PRIVATE); 383659aa375SGreg Roach }); 384a5f7ed67SGreg Roach 385a5f7ed67SGreg Roach return $records; 386a5f7ed67SGreg Roach } 387a5f7ed67SGreg Roach 388a5f7ed67SGreg Roach /** 389a5f7ed67SGreg Roach * @param Tree $tree 390a5f7ed67SGreg Roach * @param int $limit 391a5f7ed67SGreg Roach * @param int $offset 392a5f7ed67SGreg Roach * 393c5a1ffe6SGreg Roach * @return Collection<Family> 394c5a1ffe6SGreg Roach */ 395c5a1ffe6SGreg Roach private function sitemapFamilies(Tree $tree, int $limit, int $offset): Collection 396c5a1ffe6SGreg Roach { 397c5a1ffe6SGreg Roach return DB::table('families') 398c5a1ffe6SGreg Roach ->where('f_file', '=', $tree->id()) 399c5a1ffe6SGreg Roach ->orderBy('f_id') 400c5a1ffe6SGreg Roach ->skip($offset) 401c5a1ffe6SGreg Roach ->take($limit) 402c5a1ffe6SGreg Roach ->get() 4036b9cb339SGreg Roach ->map(Registry::familyFactory()->mapper($tree)); 404c5a1ffe6SGreg Roach } 405c5a1ffe6SGreg Roach 406c5a1ffe6SGreg Roach /** 407c5a1ffe6SGreg Roach * @param Tree $tree 408c5a1ffe6SGreg Roach * @param int $limit 409c5a1ffe6SGreg Roach * @param int $offset 410c5a1ffe6SGreg Roach * 411b5c8fd7eSGreg Roach * @return Collection<Individual> 412a5f7ed67SGreg Roach */ 413886b77daSGreg Roach private function sitemapIndividuals(Tree $tree, int $limit, int $offset): Collection 414c1010edaSGreg Roach { 415886b77daSGreg Roach return DB::table('individuals') 416fa17fb66SGreg Roach ->where('i_file', '=', $tree->id()) 417fa17fb66SGreg Roach ->orderBy('i_id') 418fa17fb66SGreg Roach ->skip($offset) 419fa17fb66SGreg Roach ->take($limit) 420886b77daSGreg Roach ->get() 4216b9cb339SGreg Roach ->map(Registry::individualFactory()->mapper($tree)); 4228c2e8227SGreg Roach } 423a5f7ed67SGreg Roach 424a5f7ed67SGreg Roach /** 425a5f7ed67SGreg Roach * @param Tree $tree 426a5f7ed67SGreg Roach * @param int $limit 427a5f7ed67SGreg Roach * @param int $offset 428a5f7ed67SGreg Roach * 429b5c8fd7eSGreg Roach * @return Collection<Media> 430a5f7ed67SGreg Roach */ 431886b77daSGreg Roach private function sitemapMedia(Tree $tree, int $limit, int $offset): Collection 432c1010edaSGreg Roach { 433886b77daSGreg Roach return DB::table('media') 434fa17fb66SGreg Roach ->where('m_file', '=', $tree->id()) 435fa17fb66SGreg Roach ->orderBy('m_id') 436fa17fb66SGreg Roach ->skip($offset) 437fa17fb66SGreg Roach ->take($limit) 438886b77daSGreg Roach ->get() 4396b9cb339SGreg Roach ->map(Registry::mediaFactory()->mapper($tree)); 4408c2e8227SGreg Roach } 4418c2e8227SGreg Roach 4428c2e8227SGreg Roach /** 443a5f7ed67SGreg Roach * @param Tree $tree 444a5f7ed67SGreg Roach * @param int $limit 445a5f7ed67SGreg Roach * @param int $offset 446a5f7ed67SGreg Roach * 447b5c8fd7eSGreg Roach * @return Collection<Note> 4488c2e8227SGreg Roach */ 449886b77daSGreg Roach private function sitemapNotes(Tree $tree, int $limit, int $offset): Collection 450c1010edaSGreg Roach { 451886b77daSGreg Roach return DB::table('other') 452fa17fb66SGreg Roach ->where('o_file', '=', $tree->id()) 453c5a1ffe6SGreg Roach ->where('o_type', '=', Note::RECORD_TYPE) 454fa17fb66SGreg Roach ->orderBy('o_id') 455fa17fb66SGreg Roach ->skip($offset) 456fa17fb66SGreg Roach ->take($limit) 457886b77daSGreg Roach ->get() 4586b9cb339SGreg Roach ->map(Registry::noteFactory()->mapper($tree)); 4598c2e8227SGreg Roach } 4608c2e8227SGreg Roach 461a5f7ed67SGreg Roach /** 462a5f7ed67SGreg Roach * @param Tree $tree 463a5f7ed67SGreg Roach * @param int $limit 464a5f7ed67SGreg Roach * @param int $offset 465a5f7ed67SGreg Roach * 466b5c8fd7eSGreg Roach * @return Collection<Repository> 467a5f7ed67SGreg Roach */ 468886b77daSGreg Roach private function sitemapRepositories(Tree $tree, int $limit, int $offset): Collection 469c1010edaSGreg Roach { 470886b77daSGreg Roach return DB::table('other') 471fa17fb66SGreg Roach ->where('o_file', '=', $tree->id()) 472c5a1ffe6SGreg Roach ->where('o_type', '=', Repository::RECORD_TYPE) 473fa17fb66SGreg Roach ->orderBy('o_id') 474fa17fb66SGreg Roach ->skip($offset) 475fa17fb66SGreg Roach ->take($limit) 476886b77daSGreg Roach ->get() 4776b9cb339SGreg Roach ->map(Registry::repositoryFactory()->mapper($tree)); 478a5f7ed67SGreg Roach } 479a5f7ed67SGreg Roach 480a5f7ed67SGreg Roach /** 481a5f7ed67SGreg Roach * @param Tree $tree 482a5f7ed67SGreg Roach * @param int $limit 483a5f7ed67SGreg Roach * @param int $offset 484a5f7ed67SGreg Roach * 485b5c8fd7eSGreg Roach * @return Collection<Source> 486a5f7ed67SGreg Roach */ 487886b77daSGreg Roach private function sitemapSources(Tree $tree, int $limit, int $offset): Collection 488c1010edaSGreg Roach { 489886b77daSGreg Roach return DB::table('sources') 490fa17fb66SGreg Roach ->where('s_file', '=', $tree->id()) 491fa17fb66SGreg Roach ->orderBy('s_id') 492fa17fb66SGreg Roach ->skip($offset) 493fa17fb66SGreg Roach ->take($limit) 494886b77daSGreg Roach ->get() 4956b9cb339SGreg Roach ->map(Registry::sourceFactory()->mapper($tree)); 4968c2e8227SGreg Roach } 497c5a1ffe6SGreg Roach 498c5a1ffe6SGreg Roach /** 499c5a1ffe6SGreg Roach * @param Tree $tree 500c5a1ffe6SGreg Roach * @param int $limit 501c5a1ffe6SGreg Roach * @param int $offset 502c5a1ffe6SGreg Roach * 503c5a1ffe6SGreg Roach * @return Collection<Submitter> 504c5a1ffe6SGreg Roach */ 505c5a1ffe6SGreg Roach private function sitemapSubmitters(Tree $tree, int $limit, int $offset): Collection 506c5a1ffe6SGreg Roach { 507c5a1ffe6SGreg Roach return DB::table('other') 508c5a1ffe6SGreg Roach ->where('o_file', '=', $tree->id()) 509c5a1ffe6SGreg Roach ->where('o_type', '=', Submitter::RECORD_TYPE) 510c5a1ffe6SGreg Roach ->orderBy('o_id') 511c5a1ffe6SGreg Roach ->skip($offset) 512c5a1ffe6SGreg Roach ->take($limit) 513c5a1ffe6SGreg Roach ->get() 5146b9cb339SGreg Roach ->map(Registry::submitterFactory()->mapper($tree)); 515c5a1ffe6SGreg Roach } 5168c2e8227SGreg Roach} 517