18c2e8227SGreg Roach<?php 23976b470SGreg Roach 38c2e8227SGreg Roach/** 48c2e8227SGreg Roach * webtrees: online genealogy 55bfc6897SGreg Roach * Copyright (C) 2022 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 1589f7189bSGreg 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; 26c5a1ffe6SGreg Roachuse Fisharebest\Webtrees\Family; 27a5f7ed67SGreg Roachuse Fisharebest\Webtrees\FlashMessages; 28a5f7ed67SGreg Roachuse Fisharebest\Webtrees\GedcomRecord; 29b1b85189SGreg Roachuse Fisharebest\Webtrees\Html; 3081b729d3SGreg Roachuse Fisharebest\Webtrees\Http\Exceptions\HttpNotFoundException; 310e62c4b8SGreg Roachuse Fisharebest\Webtrees\I18N; 320e62c4b8SGreg Roachuse Fisharebest\Webtrees\Individual; 330e62c4b8SGreg Roachuse Fisharebest\Webtrees\Media; 340e62c4b8SGreg Roachuse Fisharebest\Webtrees\Note; 3581b729d3SGreg Roachuse Fisharebest\Webtrees\Registry; 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; 41b55cbc6bSGreg Roachuse Fisharebest\Webtrees\Validator; 42fa17fb66SGreg Roachuse Illuminate\Database\Capsule\Manager as DB; 43a69f5655SGreg Roachuse Illuminate\Database\Query\Expression; 44886b77daSGreg Roachuse Illuminate\Support\Collection; 456ccdf4f0SGreg Roachuse Psr\Http\Message\ResponseInterface; 466ccdf4f0SGreg Roachuse Psr\Http\Message\ServerRequestInterface; 4716a40a66SGreg Roachuse Psr\Http\Server\RequestHandlerInterface; 48d519210eSGreg Roach 4916a40a66SGreg Roachuse function app; 5016a40a66SGreg Roachuse function assert; 5116a40a66SGreg Roachuse function date; 523df1e584SGreg Roachuse function redirect; 5316a40a66SGreg Roachuse function response; 5416a40a66SGreg Roachuse function route; 553df1e584SGreg Roachuse function view; 563df1e584SGreg Roach 578c2e8227SGreg Roach/** 588c2e8227SGreg Roach * Class SiteMapModule 598c2e8227SGreg Roach */ 6016a40a66SGreg Roachclass SiteMapModule extends AbstractModule implements ModuleConfigInterface, RequestHandlerInterface 61c1010edaSGreg Roach{ 6249a243cbSGreg Roach use ModuleConfigTrait; 6349a243cbSGreg Roach 6416d6367aSGreg Roach private const RECORDS_PER_VOLUME = 500; // Keep sitemap files small, for memory, CPU and max_allowed_packet limits. 650daf451eSGreg Roach private const CACHE_LIFE = 209600; // Two weeks 668c2e8227SGreg Roach 67c5a1ffe6SGreg Roach private const PRIORITY = [ 68c5a1ffe6SGreg Roach Family::RECORD_TYPE => 0.7, 69c5a1ffe6SGreg Roach Individual::RECORD_TYPE => 0.9, 70c5a1ffe6SGreg Roach Media::RECORD_TYPE => 0.5, 71c5a1ffe6SGreg Roach Note::RECORD_TYPE => 0.3, 72c5a1ffe6SGreg Roach Repository::RECORD_TYPE => 0.5, 73c5a1ffe6SGreg Roach Source::RECORD_TYPE => 0.5, 74c5a1ffe6SGreg Roach Submitter::RECORD_TYPE => 0.3, 75c5a1ffe6SGreg Roach ]; 76c5a1ffe6SGreg Roach 7743f2f523SGreg Roach private TreeService $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 { 96158900c2SGreg Roach Registry::routeFactory()->routeMap() 9714aabe72SGreg Roach ->get('sitemap-style', '/sitemap.xsl', $this); 9814aabe72SGreg Roach 99158900c2SGreg Roach Registry::routeFactory()->routeMap() 10016a40a66SGreg Roach ->get('sitemap-index', '/sitemap.xml', $this); 10116a40a66SGreg Roach 102158900c2SGreg Roach Registry::routeFactory()->routeMap() 103c5a1ffe6SGreg Roach ->get('sitemap-file', '/sitemap-{tree}-{type}-{page}.xml', $this); 10416a40a66SGreg Roach } 10516a40a66SGreg Roach 10616a40a66SGreg Roach /** 107a5f7ed67SGreg Roach * A sentence describing what this module does. 108a5f7ed67SGreg Roach * 109a5f7ed67SGreg Roach * @return string 110a5f7ed67SGreg Roach */ 11149a243cbSGreg Roach public function description(): string 112c1010edaSGreg Roach { 113bbb76c12SGreg Roach /* I18N: Description of the “Sitemaps” module */ 114bbb76c12SGreg Roach return I18N::translate('Generate sitemap files for search engines.'); 1158c2e8227SGreg Roach } 1168c2e8227SGreg Roach 11776692c8bSGreg Roach /** 118abafa13cSGreg Roach * Should this module be enabled when it is first installed? 119abafa13cSGreg Roach * 120abafa13cSGreg Roach * @return bool 121abafa13cSGreg Roach */ 122abafa13cSGreg Roach public function isEnabledByDefault(): bool 123abafa13cSGreg Roach { 124abafa13cSGreg Roach return false; 125abafa13cSGreg Roach } 126abafa13cSGreg Roach 127abafa13cSGreg Roach /** 12857ab2231SGreg Roach * @param ServerRequestInterface $request 12957ab2231SGreg Roach * 1306ccdf4f0SGreg Roach * @return ResponseInterface 1318c2e8227SGreg Roach */ 132a09ea7ccSGreg Roach public function getAdminAction(/** @scrutinizer ignore-unused */ ServerRequestInterface $request): ResponseInterface 133c1010edaSGreg Roach { 134a5f7ed67SGreg Roach $this->layout = 'layouts/administration'; 135a5f7ed67SGreg Roach 13616a40a66SGreg Roach $sitemap_url = route('sitemap-index'); 137a5f7ed67SGreg Roach 138ce42304aSGreg Roach // This list comes from https://en.wikipedia.org/wiki/Sitemaps 139a5f7ed67SGreg Roach $submit_urls = [ 140a5f7ed67SGreg Roach 'Bing/Yahoo' => Html::url('https://www.bing.com/webmaster/ping.aspx', ['siteMap' => $sitemap_url]), 141a5f7ed67SGreg Roach 'Google' => Html::url('https://www.google.com/webmasters/tools/ping', ['sitemap' => $sitemap_url]), 142a5f7ed67SGreg Roach ]; 143a5f7ed67SGreg Roach 144291c1b19SGreg Roach return $this->viewResponse('modules/sitemap/config', [ 1453df1e584SGreg Roach 'all_trees' => $this->tree_service->all(), 146a5f7ed67SGreg Roach 'sitemap_url' => $sitemap_url, 147a5f7ed67SGreg Roach 'submit_urls' => $submit_urls, 14849a243cbSGreg Roach 'title' => $this->title(), 149a5f7ed67SGreg Roach ]); 1508c2e8227SGreg Roach } 1518c2e8227SGreg Roach 1528c2e8227SGreg Roach /** 1536ccdf4f0SGreg Roach * How should this module be identified in the control panel, etc.? 154a5f7ed67SGreg Roach * 1556ccdf4f0SGreg Roach * @return string 1568c2e8227SGreg Roach */ 1576ccdf4f0SGreg Roach public function title(): string 1586ccdf4f0SGreg Roach { 159ad3143ccSGreg Roach /* I18N: Name of a module - see https://en.wikipedia.org/wiki/Sitemaps */ 1606ccdf4f0SGreg Roach return I18N::translate('Sitemaps'); 1616ccdf4f0SGreg Roach } 1626ccdf4f0SGreg Roach 1636ccdf4f0SGreg Roach /** 1646ccdf4f0SGreg Roach * @param ServerRequestInterface $request 1656ccdf4f0SGreg Roach * 1666ccdf4f0SGreg Roach * @return ResponseInterface 1676ccdf4f0SGreg Roach */ 1686ccdf4f0SGreg Roach public function postAdminAction(ServerRequestInterface $request): ResponseInterface 169c1010edaSGreg Roach { 170b46c87bdSGreg Roach $params = (array) $request->getParsedBody(); 171b6b9dcc9SGreg Roach 1723df1e584SGreg Roach foreach ($this->tree_service->all() as $tree) { 173b6b9dcc9SGreg Roach $include_in_sitemap = (bool) ($params['sitemap' . $tree->id()] ?? false); 174a5f7ed67SGreg Roach $tree->setPreference('include_in_sitemap', (string) $include_in_sitemap); 1758c2e8227SGreg Roach } 176a5f7ed67SGreg Roach 17749a243cbSGreg Roach FlashMessages::addMessage(I18N::translate('The preferences for the module “%s” have been updated.', $this->title()), 'success'); 178a5f7ed67SGreg Roach 1796ccdf4f0SGreg Roach return redirect($this->getConfigLink()); 1808c2e8227SGreg Roach } 1818c2e8227SGreg Roach 1828c2e8227SGreg Roach /** 18357ab2231SGreg Roach * @param ServerRequestInterface $request 18457ab2231SGreg Roach * 1856ccdf4f0SGreg Roach * @return ResponseInterface 1868c2e8227SGreg Roach */ 18716a40a66SGreg Roach public function handle(ServerRequestInterface $request): ResponseInterface 188c1010edaSGreg Roach { 189b55cbc6bSGreg Roach $route = Validator::attributes($request)->route(); 190a5f7ed67SGreg Roach 19114aabe72SGreg Roach if ($route->name === 'sitemap-style') { 19214aabe72SGreg Roach $content = view('modules/sitemap/sitemap-xsl'); 19314aabe72SGreg Roach 19414aabe72SGreg Roach return response($content, StatusCodeInterface::STATUS_OK, [ 195*6172e7f6SGreg Roach 'content-type' => 'application/xml', 19614aabe72SGreg Roach ]); 19714aabe72SGreg Roach } 19814aabe72SGreg Roach 19916a40a66SGreg Roach if ($route->name === 'sitemap-index') { 20016a40a66SGreg Roach return $this->siteMapIndex($request); 20116a40a66SGreg Roach } 20216a40a66SGreg Roach 20316a40a66SGreg Roach return $this->siteMapFile($request); 20416a40a66SGreg Roach } 20516a40a66SGreg Roach 20616a40a66SGreg Roach /** 20716a40a66SGreg Roach * @param ServerRequestInterface $request 20816a40a66SGreg Roach * 20916a40a66SGreg Roach * @return ResponseInterface 21016a40a66SGreg Roach */ 211a09ea7ccSGreg Roach private function siteMapIndex(/** @scrutinizer ignore-unused */ ServerRequestInterface $request): ResponseInterface 21216a40a66SGreg Roach { 2136b9cb339SGreg Roach $content = Registry::cache()->file()->remember('sitemap.xml', function (): string { 214659aa375SGreg Roach // Which trees have sitemaps enabled? 215659aa375SGreg Roach $tree_ids = $this->tree_service->all()->filter(static function (Tree $tree): bool { 216659aa375SGreg Roach return $tree->getPreference('include_in_sitemap') === '1'; 217659aa375SGreg Roach })->map(static function (Tree $tree): int { 218659aa375SGreg Roach return $tree->id(); 219659aa375SGreg Roach }); 220659aa375SGreg Roach 221c5a1ffe6SGreg Roach $count_families = DB::table('families') 222c5a1ffe6SGreg Roach ->join('gedcom', 'f_file', '=', 'gedcom_id') 223c5a1ffe6SGreg Roach ->whereIn('gedcom_id', $tree_ids) 224c5a1ffe6SGreg Roach ->groupBy(['gedcom_id']) 225c5a1ffe6SGreg Roach ->select([new Expression('COUNT(*) AS total'), 'gedcom_name']) 226c5a1ffe6SGreg Roach ->pluck('total', 'gedcom_name'); 227c5a1ffe6SGreg Roach 228fa17fb66SGreg Roach $count_individuals = DB::table('individuals') 22916a40a66SGreg Roach ->join('gedcom', 'i_file', '=', 'gedcom_id') 23016a40a66SGreg Roach ->whereIn('gedcom_id', $tree_ids) 23116a40a66SGreg Roach ->groupBy(['gedcom_id']) 23216a40a66SGreg Roach ->select([new Expression('COUNT(*) AS total'), 'gedcom_name']) 23316a40a66SGreg Roach ->pluck('total', 'gedcom_name'); 234a5f7ed67SGreg Roach 235fa17fb66SGreg Roach $count_media = DB::table('media') 23616a40a66SGreg Roach ->join('gedcom', 'm_file', '=', 'gedcom_id') 23716a40a66SGreg Roach ->whereIn('gedcom_id', $tree_ids) 23816a40a66SGreg Roach ->groupBy(['gedcom_id']) 23916a40a66SGreg Roach ->select([new Expression('COUNT(*) AS total'), 'gedcom_name']) 24016a40a66SGreg Roach ->pluck('total', 'gedcom_name'); 241a5f7ed67SGreg Roach 242fa17fb66SGreg Roach $count_notes = DB::table('other') 24316a40a66SGreg Roach ->join('gedcom', 'o_file', '=', 'gedcom_id') 24416a40a66SGreg Roach ->whereIn('gedcom_id', $tree_ids) 245c5a1ffe6SGreg Roach ->where('o_type', '=', Note::RECORD_TYPE) 24616a40a66SGreg Roach ->groupBy(['gedcom_id']) 24716a40a66SGreg Roach ->select([new Expression('COUNT(*) AS total'), 'gedcom_name']) 24816a40a66SGreg Roach ->pluck('total', 'gedcom_name'); 249a5f7ed67SGreg Roach 250fa17fb66SGreg Roach $count_repositories = DB::table('other') 25116a40a66SGreg Roach ->join('gedcom', 'o_file', '=', 'gedcom_id') 25216a40a66SGreg Roach ->whereIn('gedcom_id', $tree_ids) 253c5a1ffe6SGreg Roach ->where('o_type', '=', Repository::RECORD_TYPE) 25416a40a66SGreg Roach ->groupBy(['gedcom_id']) 25516a40a66SGreg Roach ->select([new Expression('COUNT(*) AS total'), 'gedcom_name']) 25616a40a66SGreg Roach ->pluck('total', 'gedcom_name'); 257a5f7ed67SGreg Roach 258fa17fb66SGreg Roach $count_sources = DB::table('sources') 25916a40a66SGreg Roach ->join('gedcom', 's_file', '=', 'gedcom_id') 26016a40a66SGreg Roach ->whereIn('gedcom_id', $tree_ids) 26116a40a66SGreg Roach ->groupBy(['gedcom_id']) 26216a40a66SGreg Roach ->select([new Expression('COUNT(*) AS total'), 'gedcom_name']) 26316a40a66SGreg Roach ->pluck('total', 'gedcom_name'); 264a5f7ed67SGreg Roach 265c5a1ffe6SGreg Roach $count_submitters = DB::table('other') 266c5a1ffe6SGreg Roach ->join('gedcom', 'o_file', '=', 'gedcom_id') 267c5a1ffe6SGreg Roach ->whereIn('gedcom_id', $tree_ids) 268c5a1ffe6SGreg Roach ->where('o_type', '=', Submitter::RECORD_TYPE) 269c5a1ffe6SGreg Roach ->groupBy(['gedcom_id']) 270c5a1ffe6SGreg Roach ->select([new Expression('COUNT(*) AS total'), 'gedcom_name']) 271c5a1ffe6SGreg Roach ->pluck('total', 'gedcom_name'); 272c5a1ffe6SGreg Roach 27316a40a66SGreg Roach // Versions 2.0.1 and earlier of this module stored large amounts of data in the settings. 27416a40a66SGreg Roach DB::table('module_setting') 27516a40a66SGreg Roach ->where('module_name', '=', $this->name()) 27616a40a66SGreg Roach ->delete(); 27716a40a66SGreg Roach 27814aabe72SGreg Roach return view('modules/sitemap/sitemap-index-xml', [ 2793df1e584SGreg Roach 'all_trees' => $this->tree_service->all(), 280c5a1ffe6SGreg Roach 'count_families' => $count_families, 281a5f7ed67SGreg Roach 'count_individuals' => $count_individuals, 282a5f7ed67SGreg Roach 'count_media' => $count_media, 283a5f7ed67SGreg Roach 'count_notes' => $count_notes, 284a5f7ed67SGreg Roach 'count_repositories' => $count_repositories, 285a5f7ed67SGreg Roach 'count_sources' => $count_sources, 286c5a1ffe6SGreg Roach 'count_submitters' => $count_submitters, 287a5f7ed67SGreg Roach 'last_mod' => date('Y-m-d'), 288a5f7ed67SGreg Roach 'records_per_volume' => self::RECORDS_PER_VOLUME, 28914aabe72SGreg Roach 'sitemap_xsl' => route('sitemap-style'), 290a5f7ed67SGreg Roach ]); 29116a40a66SGreg Roach }, self::CACHE_LIFE); 292a5f7ed67SGreg Roach 2936ccdf4f0SGreg Roach return response($content, StatusCodeInterface::STATUS_OK, [ 294*6172e7f6SGreg Roach 'content-type' => 'application/xml', 295a5f7ed67SGreg Roach ]); 296a5f7ed67SGreg Roach } 297a5f7ed67SGreg Roach 298a5f7ed67SGreg Roach /** 2996ccdf4f0SGreg Roach * @param ServerRequestInterface $request 300a5f7ed67SGreg Roach * 3016ccdf4f0SGreg Roach * @return ResponseInterface 302a5f7ed67SGreg Roach */ 30316a40a66SGreg Roach private function siteMapFile(ServerRequestInterface $request): ResponseInterface 304c1010edaSGreg Roach { 305b55cbc6bSGreg Roach $tree = Validator::attributes($request)->tree('tree'); 306b55cbc6bSGreg Roach $type = Validator::attributes($request)->string('type'); 307b55cbc6bSGreg Roach $page = Validator::attributes($request)->integer('page'); 308a5f7ed67SGreg Roach 309659aa375SGreg Roach if ($tree->getPreference('include_in_sitemap') !== '1') { 310659aa375SGreg Roach throw new HttpNotFoundException(); 311659aa375SGreg Roach } 312659aa375SGreg Roach 313c5a1ffe6SGreg Roach $cache_key = 'sitemap/' . $tree->id() . '/' . $type . '/' . $page . '.xml'; 31416a40a66SGreg Roach 3156b9cb339SGreg Roach $content = Registry::cache()->file()->remember($cache_key, function () use ($tree, $type, $page): string { 316c5a1ffe6SGreg Roach $records = $this->sitemapRecords($tree, $type, self::RECORDS_PER_VOLUME, self::RECORDS_PER_VOLUME * $page); 31716a40a66SGreg Roach 31814aabe72SGreg Roach return view('modules/sitemap/sitemap-file-xml', [ 319c5a1ffe6SGreg Roach 'priority' => self::PRIORITY[$type], 3209e3c7a99SGreg Roach 'records' => $records, 32114aabe72SGreg Roach 'sitemap_xsl' => route('sitemap-style'), 3229e3c7a99SGreg Roach 'tree' => $tree, 3239e3c7a99SGreg Roach ]); 32416a40a66SGreg Roach }, self::CACHE_LIFE); 325a5f7ed67SGreg Roach 3266ccdf4f0SGreg Roach return response($content, StatusCodeInterface::STATUS_OK, [ 327*6172e7f6SGreg Roach 'content-type' => 'application/xml', 328a5f7ed67SGreg Roach ]); 329a5f7ed67SGreg Roach } 330a5f7ed67SGreg Roach 331a5f7ed67SGreg Roach /** 332a5f7ed67SGreg Roach * @param Tree $tree 333a5f7ed67SGreg Roach * @param string $type 334a5f7ed67SGreg Roach * @param int $limit 335a5f7ed67SGreg Roach * @param int $offset 336a5f7ed67SGreg Roach * 33736779af1SGreg Roach * @return Collection<int,GedcomRecord> 338a5f7ed67SGreg Roach */ 339886b77daSGreg Roach private function sitemapRecords(Tree $tree, string $type, int $limit, int $offset): Collection 340c1010edaSGreg Roach { 341a5f7ed67SGreg Roach switch ($type) { 342c5a1ffe6SGreg Roach case Family::RECORD_TYPE: 343c5a1ffe6SGreg Roach $records = $this->sitemapFamilies($tree, $limit, $offset); 344c5a1ffe6SGreg Roach break; 345c5a1ffe6SGreg Roach 34616a40a66SGreg Roach case Individual::RECORD_TYPE: 347a5f7ed67SGreg Roach $records = $this->sitemapIndividuals($tree, $limit, $offset); 348a5f7ed67SGreg Roach break; 349a5f7ed67SGreg Roach 35016a40a66SGreg Roach case Media::RECORD_TYPE: 351a5f7ed67SGreg Roach $records = $this->sitemapMedia($tree, $limit, $offset); 352a5f7ed67SGreg Roach break; 353a5f7ed67SGreg Roach 35416a40a66SGreg Roach case Note::RECORD_TYPE: 355a5f7ed67SGreg Roach $records = $this->sitemapNotes($tree, $limit, $offset); 356a5f7ed67SGreg Roach break; 357a5f7ed67SGreg Roach 35816a40a66SGreg Roach case Repository::RECORD_TYPE: 359a5f7ed67SGreg Roach $records = $this->sitemapRepositories($tree, $limit, $offset); 360a5f7ed67SGreg Roach break; 361a5f7ed67SGreg Roach 36216a40a66SGreg Roach case Source::RECORD_TYPE: 363a5f7ed67SGreg Roach $records = $this->sitemapSources($tree, $limit, $offset); 364a5f7ed67SGreg Roach break; 365a5f7ed67SGreg Roach 366c5a1ffe6SGreg Roach case Submitter::RECORD_TYPE: 367c5a1ffe6SGreg Roach $records = $this->sitemapSubmitters($tree, $limit, $offset); 368c5a1ffe6SGreg Roach break; 369c5a1ffe6SGreg Roach 370a5f7ed67SGreg Roach default: 371d501c45dSGreg Roach throw new HttpNotFoundException('Invalid record type: ' . $type); 372a5f7ed67SGreg Roach } 373a5f7ed67SGreg Roach 374a5f7ed67SGreg Roach // Skip private records. 375659aa375SGreg Roach $records = $records->filter(static function (GedcomRecord $record): bool { 376659aa375SGreg Roach return $record->canShow(Auth::PRIV_PRIVATE); 377659aa375SGreg Roach }); 378a5f7ed67SGreg Roach 379a5f7ed67SGreg Roach return $records; 380a5f7ed67SGreg Roach } 381a5f7ed67SGreg Roach 382a5f7ed67SGreg Roach /** 383a5f7ed67SGreg Roach * @param Tree $tree 384a5f7ed67SGreg Roach * @param int $limit 385a5f7ed67SGreg Roach * @param int $offset 386a5f7ed67SGreg Roach * 38736779af1SGreg Roach * @return Collection<int,Family> 388c5a1ffe6SGreg Roach */ 389c5a1ffe6SGreg Roach private function sitemapFamilies(Tree $tree, int $limit, int $offset): Collection 390c5a1ffe6SGreg Roach { 391c5a1ffe6SGreg Roach return DB::table('families') 392c5a1ffe6SGreg Roach ->where('f_file', '=', $tree->id()) 393c5a1ffe6SGreg Roach ->orderBy('f_id') 394c5a1ffe6SGreg Roach ->skip($offset) 395c5a1ffe6SGreg Roach ->take($limit) 396c5a1ffe6SGreg Roach ->get() 3976b9cb339SGreg Roach ->map(Registry::familyFactory()->mapper($tree)); 398c5a1ffe6SGreg Roach } 399c5a1ffe6SGreg Roach 400c5a1ffe6SGreg Roach /** 401c5a1ffe6SGreg Roach * @param Tree $tree 402c5a1ffe6SGreg Roach * @param int $limit 403c5a1ffe6SGreg Roach * @param int $offset 404c5a1ffe6SGreg Roach * 40536779af1SGreg Roach * @return Collection<int,Individual> 406a5f7ed67SGreg Roach */ 407886b77daSGreg Roach private function sitemapIndividuals(Tree $tree, int $limit, int $offset): Collection 408c1010edaSGreg Roach { 409886b77daSGreg Roach return DB::table('individuals') 410fa17fb66SGreg Roach ->where('i_file', '=', $tree->id()) 411fa17fb66SGreg Roach ->orderBy('i_id') 412fa17fb66SGreg Roach ->skip($offset) 413fa17fb66SGreg Roach ->take($limit) 414886b77daSGreg Roach ->get() 4156b9cb339SGreg Roach ->map(Registry::individualFactory()->mapper($tree)); 4168c2e8227SGreg Roach } 417a5f7ed67SGreg Roach 418a5f7ed67SGreg Roach /** 419a5f7ed67SGreg Roach * @param Tree $tree 420a5f7ed67SGreg Roach * @param int $limit 421a5f7ed67SGreg Roach * @param int $offset 422a5f7ed67SGreg Roach * 42336779af1SGreg Roach * @return Collection<int,Media> 424a5f7ed67SGreg Roach */ 425886b77daSGreg Roach private function sitemapMedia(Tree $tree, int $limit, int $offset): Collection 426c1010edaSGreg Roach { 427886b77daSGreg Roach return DB::table('media') 428fa17fb66SGreg Roach ->where('m_file', '=', $tree->id()) 429fa17fb66SGreg Roach ->orderBy('m_id') 430fa17fb66SGreg Roach ->skip($offset) 431fa17fb66SGreg Roach ->take($limit) 432886b77daSGreg Roach ->get() 4336b9cb339SGreg Roach ->map(Registry::mediaFactory()->mapper($tree)); 4348c2e8227SGreg Roach } 4358c2e8227SGreg Roach 4368c2e8227SGreg Roach /** 437a5f7ed67SGreg Roach * @param Tree $tree 438a5f7ed67SGreg Roach * @param int $limit 439a5f7ed67SGreg Roach * @param int $offset 440a5f7ed67SGreg Roach * 44136779af1SGreg Roach * @return Collection<int,Note> 4428c2e8227SGreg Roach */ 443886b77daSGreg Roach private function sitemapNotes(Tree $tree, int $limit, int $offset): Collection 444c1010edaSGreg Roach { 445886b77daSGreg Roach return DB::table('other') 446fa17fb66SGreg Roach ->where('o_file', '=', $tree->id()) 447c5a1ffe6SGreg Roach ->where('o_type', '=', Note::RECORD_TYPE) 448fa17fb66SGreg Roach ->orderBy('o_id') 449fa17fb66SGreg Roach ->skip($offset) 450fa17fb66SGreg Roach ->take($limit) 451886b77daSGreg Roach ->get() 4526b9cb339SGreg Roach ->map(Registry::noteFactory()->mapper($tree)); 4538c2e8227SGreg Roach } 4548c2e8227SGreg Roach 455a5f7ed67SGreg Roach /** 456a5f7ed67SGreg Roach * @param Tree $tree 457a5f7ed67SGreg Roach * @param int $limit 458a5f7ed67SGreg Roach * @param int $offset 459a5f7ed67SGreg Roach * 46036779af1SGreg Roach * @return Collection<int,Repository> 461a5f7ed67SGreg Roach */ 462886b77daSGreg Roach private function sitemapRepositories(Tree $tree, int $limit, int $offset): Collection 463c1010edaSGreg Roach { 464886b77daSGreg Roach return DB::table('other') 465fa17fb66SGreg Roach ->where('o_file', '=', $tree->id()) 466c5a1ffe6SGreg Roach ->where('o_type', '=', Repository::RECORD_TYPE) 467fa17fb66SGreg Roach ->orderBy('o_id') 468fa17fb66SGreg Roach ->skip($offset) 469fa17fb66SGreg Roach ->take($limit) 470886b77daSGreg Roach ->get() 4716b9cb339SGreg Roach ->map(Registry::repositoryFactory()->mapper($tree)); 472a5f7ed67SGreg Roach } 473a5f7ed67SGreg Roach 474a5f7ed67SGreg Roach /** 475a5f7ed67SGreg Roach * @param Tree $tree 476a5f7ed67SGreg Roach * @param int $limit 477a5f7ed67SGreg Roach * @param int $offset 478a5f7ed67SGreg Roach * 47936779af1SGreg Roach * @return Collection<int,Source> 480a5f7ed67SGreg Roach */ 481886b77daSGreg Roach private function sitemapSources(Tree $tree, int $limit, int $offset): Collection 482c1010edaSGreg Roach { 483886b77daSGreg Roach return DB::table('sources') 484fa17fb66SGreg Roach ->where('s_file', '=', $tree->id()) 485fa17fb66SGreg Roach ->orderBy('s_id') 486fa17fb66SGreg Roach ->skip($offset) 487fa17fb66SGreg Roach ->take($limit) 488886b77daSGreg Roach ->get() 4896b9cb339SGreg Roach ->map(Registry::sourceFactory()->mapper($tree)); 4908c2e8227SGreg Roach } 491c5a1ffe6SGreg Roach 492c5a1ffe6SGreg Roach /** 493c5a1ffe6SGreg Roach * @param Tree $tree 494c5a1ffe6SGreg Roach * @param int $limit 495c5a1ffe6SGreg Roach * @param int $offset 496c5a1ffe6SGreg Roach * 49736779af1SGreg Roach * @return Collection<int,Submitter> 498c5a1ffe6SGreg Roach */ 499c5a1ffe6SGreg Roach private function sitemapSubmitters(Tree $tree, int $limit, int $offset): Collection 500c5a1ffe6SGreg Roach { 501c5a1ffe6SGreg Roach return DB::table('other') 502c5a1ffe6SGreg Roach ->where('o_file', '=', $tree->id()) 503c5a1ffe6SGreg Roach ->where('o_type', '=', Submitter::RECORD_TYPE) 504c5a1ffe6SGreg Roach ->orderBy('o_id') 505c5a1ffe6SGreg Roach ->skip($offset) 506c5a1ffe6SGreg Roach ->take($limit) 507c5a1ffe6SGreg Roach ->get() 5086b9cb339SGreg Roach ->map(Registry::submitterFactory()->mapper($tree)); 509c5a1ffe6SGreg Roach } 5108c2e8227SGreg Roach} 511