18c2e8227SGreg Roach<?php 23976b470SGreg Roach 38c2e8227SGreg Roach/** 48c2e8227SGreg Roach * webtrees: online genealogy 5*d11be702SGreg Roach * Copyright (C) 2023 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 226ccdf4f0SGreg Roachuse Fig\Http\Message\StatusCodeInterface; 23659aa375SGreg Roachuse Fisharebest\Webtrees\Auth; 24c5a1ffe6SGreg Roachuse Fisharebest\Webtrees\Family; 25a5f7ed67SGreg Roachuse Fisharebest\Webtrees\FlashMessages; 26a5f7ed67SGreg Roachuse Fisharebest\Webtrees\GedcomRecord; 27b1b85189SGreg Roachuse Fisharebest\Webtrees\Html; 2881b729d3SGreg Roachuse Fisharebest\Webtrees\Http\Exceptions\HttpNotFoundException; 290e62c4b8SGreg Roachuse Fisharebest\Webtrees\I18N; 300e62c4b8SGreg Roachuse Fisharebest\Webtrees\Individual; 310e62c4b8SGreg Roachuse Fisharebest\Webtrees\Media; 320e62c4b8SGreg Roachuse Fisharebest\Webtrees\Note; 3381b729d3SGreg Roachuse Fisharebest\Webtrees\Registry; 340e62c4b8SGreg Roachuse Fisharebest\Webtrees\Repository; 353df1e584SGreg Roachuse Fisharebest\Webtrees\Services\TreeService; 360e62c4b8SGreg Roachuse Fisharebest\Webtrees\Source; 37c5a1ffe6SGreg Roachuse Fisharebest\Webtrees\Submitter; 380e62c4b8SGreg Roachuse Fisharebest\Webtrees\Tree; 39b55cbc6bSGreg Roachuse Fisharebest\Webtrees\Validator; 40fa17fb66SGreg Roachuse Illuminate\Database\Capsule\Manager as DB; 41a69f5655SGreg Roachuse Illuminate\Database\Query\Expression; 42886b77daSGreg Roachuse Illuminate\Support\Collection; 436ccdf4f0SGreg Roachuse Psr\Http\Message\ResponseInterface; 446ccdf4f0SGreg Roachuse Psr\Http\Message\ServerRequestInterface; 4516a40a66SGreg Roachuse Psr\Http\Server\RequestHandlerInterface; 46d519210eSGreg Roach 4716a40a66SGreg Roachuse function date; 483df1e584SGreg Roachuse function redirect; 4916a40a66SGreg Roachuse function response; 5016a40a66SGreg Roachuse function route; 513df1e584SGreg Roachuse function view; 523df1e584SGreg Roach 538c2e8227SGreg Roach/** 548c2e8227SGreg Roach * Class SiteMapModule 558c2e8227SGreg Roach */ 5616a40a66SGreg Roachclass SiteMapModule extends AbstractModule implements ModuleConfigInterface, RequestHandlerInterface 57c1010edaSGreg Roach{ 5849a243cbSGreg Roach use ModuleConfigTrait; 5949a243cbSGreg Roach 6016d6367aSGreg Roach private const RECORDS_PER_VOLUME = 500; // Keep sitemap files small, for memory, CPU and max_allowed_packet limits. 610daf451eSGreg Roach private const CACHE_LIFE = 209600; // Two weeks 628c2e8227SGreg Roach 63c5a1ffe6SGreg Roach private const PRIORITY = [ 64c5a1ffe6SGreg Roach Family::RECORD_TYPE => 0.7, 65c5a1ffe6SGreg Roach Individual::RECORD_TYPE => 0.9, 66c5a1ffe6SGreg Roach Media::RECORD_TYPE => 0.5, 67c5a1ffe6SGreg Roach Note::RECORD_TYPE => 0.3, 68c5a1ffe6SGreg Roach Repository::RECORD_TYPE => 0.5, 69c5a1ffe6SGreg Roach Source::RECORD_TYPE => 0.5, 70c5a1ffe6SGreg Roach Submitter::RECORD_TYPE => 0.3, 71c5a1ffe6SGreg Roach ]; 72c5a1ffe6SGreg Roach 7343f2f523SGreg Roach private TreeService $tree_service; 743df1e584SGreg Roach 753df1e584SGreg Roach /** 763df1e584SGreg Roach * TreesMenuModule constructor. 773df1e584SGreg Roach * 783df1e584SGreg Roach * @param TreeService $tree_service 793df1e584SGreg Roach */ 803df1e584SGreg Roach public function __construct(TreeService $tree_service) 813df1e584SGreg Roach { 823df1e584SGreg Roach $this->tree_service = $tree_service; 833df1e584SGreg Roach } 843df1e584SGreg Roach 85a5f7ed67SGreg Roach /** 8616a40a66SGreg Roach * Initialization. 8716a40a66SGreg Roach * 8816a40a66SGreg Roach * @return void 8916a40a66SGreg Roach */ 9016a40a66SGreg Roach public function boot(): void 9116a40a66SGreg Roach { 92158900c2SGreg Roach Registry::routeFactory()->routeMap() 9314aabe72SGreg Roach ->get('sitemap-style', '/sitemap.xsl', $this); 9414aabe72SGreg Roach 95158900c2SGreg Roach Registry::routeFactory()->routeMap() 9616a40a66SGreg Roach ->get('sitemap-index', '/sitemap.xml', $this); 9716a40a66SGreg Roach 98158900c2SGreg Roach Registry::routeFactory()->routeMap() 99c5a1ffe6SGreg Roach ->get('sitemap-file', '/sitemap-{tree}-{type}-{page}.xml', $this); 10016a40a66SGreg Roach } 10116a40a66SGreg Roach 10216a40a66SGreg Roach /** 103a5f7ed67SGreg Roach * A sentence describing what this module does. 104a5f7ed67SGreg Roach * 105a5f7ed67SGreg Roach * @return string 106a5f7ed67SGreg Roach */ 10749a243cbSGreg Roach public function description(): string 108c1010edaSGreg Roach { 109bbb76c12SGreg Roach /* I18N: Description of the “Sitemaps” module */ 110bbb76c12SGreg Roach return I18N::translate('Generate sitemap files for search engines.'); 1118c2e8227SGreg Roach } 1128c2e8227SGreg Roach 11376692c8bSGreg Roach /** 114abafa13cSGreg Roach * Should this module be enabled when it is first installed? 115abafa13cSGreg Roach * 116abafa13cSGreg Roach * @return bool 117abafa13cSGreg Roach */ 118abafa13cSGreg Roach public function isEnabledByDefault(): bool 119abafa13cSGreg Roach { 120abafa13cSGreg Roach return false; 121abafa13cSGreg Roach } 122abafa13cSGreg Roach 123abafa13cSGreg Roach /** 12457ab2231SGreg Roach * @param ServerRequestInterface $request 12557ab2231SGreg Roach * 1266ccdf4f0SGreg Roach * @return ResponseInterface 1278c2e8227SGreg Roach */ 12849528f2bSGreg Roach public function getAdminAction(ServerRequestInterface $request): ResponseInterface 129c1010edaSGreg Roach { 130a5f7ed67SGreg Roach $this->layout = 'layouts/administration'; 131a5f7ed67SGreg Roach 13216a40a66SGreg Roach $sitemap_url = route('sitemap-index'); 133a5f7ed67SGreg Roach 134ce42304aSGreg Roach // This list comes from https://en.wikipedia.org/wiki/Sitemaps 135a5f7ed67SGreg Roach $submit_urls = [ 136a5f7ed67SGreg Roach 'Bing/Yahoo' => Html::url('https://www.bing.com/webmaster/ping.aspx', ['siteMap' => $sitemap_url]), 137a5f7ed67SGreg Roach 'Google' => Html::url('https://www.google.com/webmasters/tools/ping', ['sitemap' => $sitemap_url]), 138a5f7ed67SGreg Roach ]; 139a5f7ed67SGreg Roach 140291c1b19SGreg Roach return $this->viewResponse('modules/sitemap/config', [ 1413df1e584SGreg Roach 'all_trees' => $this->tree_service->all(), 142a5f7ed67SGreg Roach 'sitemap_url' => $sitemap_url, 143a5f7ed67SGreg Roach 'submit_urls' => $submit_urls, 14449a243cbSGreg Roach 'title' => $this->title(), 145a5f7ed67SGreg Roach ]); 1468c2e8227SGreg Roach } 1478c2e8227SGreg Roach 1488c2e8227SGreg Roach /** 1496ccdf4f0SGreg Roach * How should this module be identified in the control panel, etc.? 150a5f7ed67SGreg Roach * 1516ccdf4f0SGreg Roach * @return string 1528c2e8227SGreg Roach */ 1536ccdf4f0SGreg Roach public function title(): string 1546ccdf4f0SGreg Roach { 155ad3143ccSGreg Roach /* I18N: Name of a module - see https://en.wikipedia.org/wiki/Sitemaps */ 1566ccdf4f0SGreg Roach return I18N::translate('Sitemaps'); 1576ccdf4f0SGreg Roach } 1586ccdf4f0SGreg Roach 1596ccdf4f0SGreg Roach /** 1606ccdf4f0SGreg Roach * @param ServerRequestInterface $request 1616ccdf4f0SGreg Roach * 1626ccdf4f0SGreg Roach * @return ResponseInterface 1636ccdf4f0SGreg Roach */ 1646ccdf4f0SGreg Roach public function postAdminAction(ServerRequestInterface $request): ResponseInterface 165c1010edaSGreg Roach { 1663df1e584SGreg Roach foreach ($this->tree_service->all() as $tree) { 167748dbe15SGreg Roach $include_in_sitemap = Validator::parsedBody($request)->boolean('sitemap' . $tree->id(), false); 168a5f7ed67SGreg Roach $tree->setPreference('include_in_sitemap', (string) $include_in_sitemap); 1698c2e8227SGreg Roach } 170a5f7ed67SGreg Roach 17149a243cbSGreg Roach FlashMessages::addMessage(I18N::translate('The preferences for the module “%s” have been updated.', $this->title()), 'success'); 172a5f7ed67SGreg Roach 1736ccdf4f0SGreg Roach return redirect($this->getConfigLink()); 1748c2e8227SGreg Roach } 1758c2e8227SGreg Roach 1768c2e8227SGreg Roach /** 17757ab2231SGreg Roach * @param ServerRequestInterface $request 17857ab2231SGreg Roach * 1796ccdf4f0SGreg Roach * @return ResponseInterface 1808c2e8227SGreg Roach */ 18116a40a66SGreg Roach public function handle(ServerRequestInterface $request): ResponseInterface 182c1010edaSGreg Roach { 183b55cbc6bSGreg Roach $route = Validator::attributes($request)->route(); 184a5f7ed67SGreg Roach 18514aabe72SGreg Roach if ($route->name === 'sitemap-style') { 18614aabe72SGreg Roach $content = view('modules/sitemap/sitemap-xsl'); 18714aabe72SGreg Roach 18814aabe72SGreg Roach return response($content, StatusCodeInterface::STATUS_OK, [ 1896172e7f6SGreg Roach 'content-type' => 'application/xml', 19014aabe72SGreg Roach ]); 19114aabe72SGreg Roach } 19214aabe72SGreg Roach 19316a40a66SGreg Roach if ($route->name === 'sitemap-index') { 19416a40a66SGreg Roach return $this->siteMapIndex($request); 19516a40a66SGreg Roach } 19616a40a66SGreg Roach 19716a40a66SGreg Roach return $this->siteMapFile($request); 19816a40a66SGreg Roach } 19916a40a66SGreg Roach 20016a40a66SGreg Roach /** 20116a40a66SGreg Roach * @param ServerRequestInterface $request 20216a40a66SGreg Roach * 20316a40a66SGreg Roach * @return ResponseInterface 20416a40a66SGreg Roach */ 20549528f2bSGreg Roach private function siteMapIndex(ServerRequestInterface $request): ResponseInterface 20616a40a66SGreg Roach { 2076b9cb339SGreg Roach $content = Registry::cache()->file()->remember('sitemap.xml', function (): string { 208659aa375SGreg Roach // Which trees have sitemaps enabled? 209659aa375SGreg Roach $tree_ids = $this->tree_service->all()->filter(static function (Tree $tree): bool { 210659aa375SGreg Roach return $tree->getPreference('include_in_sitemap') === '1'; 211659aa375SGreg Roach })->map(static function (Tree $tree): int { 212659aa375SGreg Roach return $tree->id(); 213659aa375SGreg Roach }); 214659aa375SGreg Roach 215c5a1ffe6SGreg Roach $count_families = DB::table('families') 216c5a1ffe6SGreg Roach ->join('gedcom', 'f_file', '=', 'gedcom_id') 217c5a1ffe6SGreg Roach ->whereIn('gedcom_id', $tree_ids) 218c5a1ffe6SGreg Roach ->groupBy(['gedcom_id']) 219c5a1ffe6SGreg Roach ->select([new Expression('COUNT(*) AS total'), 'gedcom_name']) 220c5a1ffe6SGreg Roach ->pluck('total', 'gedcom_name'); 221c5a1ffe6SGreg Roach 222fa17fb66SGreg Roach $count_individuals = DB::table('individuals') 22316a40a66SGreg Roach ->join('gedcom', 'i_file', '=', 'gedcom_id') 22416a40a66SGreg Roach ->whereIn('gedcom_id', $tree_ids) 22516a40a66SGreg Roach ->groupBy(['gedcom_id']) 22616a40a66SGreg Roach ->select([new Expression('COUNT(*) AS total'), 'gedcom_name']) 22716a40a66SGreg Roach ->pluck('total', 'gedcom_name'); 228a5f7ed67SGreg Roach 229fa17fb66SGreg Roach $count_media = DB::table('media') 23016a40a66SGreg Roach ->join('gedcom', 'm_file', '=', 'gedcom_id') 23116a40a66SGreg Roach ->whereIn('gedcom_id', $tree_ids) 23216a40a66SGreg Roach ->groupBy(['gedcom_id']) 23316a40a66SGreg Roach ->select([new Expression('COUNT(*) AS total'), 'gedcom_name']) 23416a40a66SGreg Roach ->pluck('total', 'gedcom_name'); 235a5f7ed67SGreg Roach 236fa17fb66SGreg Roach $count_notes = DB::table('other') 23716a40a66SGreg Roach ->join('gedcom', 'o_file', '=', 'gedcom_id') 23816a40a66SGreg Roach ->whereIn('gedcom_id', $tree_ids) 239c5a1ffe6SGreg Roach ->where('o_type', '=', Note::RECORD_TYPE) 24016a40a66SGreg Roach ->groupBy(['gedcom_id']) 24116a40a66SGreg Roach ->select([new Expression('COUNT(*) AS total'), 'gedcom_name']) 24216a40a66SGreg Roach ->pluck('total', 'gedcom_name'); 243a5f7ed67SGreg Roach 244fa17fb66SGreg Roach $count_repositories = DB::table('other') 24516a40a66SGreg Roach ->join('gedcom', 'o_file', '=', 'gedcom_id') 24616a40a66SGreg Roach ->whereIn('gedcom_id', $tree_ids) 247c5a1ffe6SGreg Roach ->where('o_type', '=', Repository::RECORD_TYPE) 24816a40a66SGreg Roach ->groupBy(['gedcom_id']) 24916a40a66SGreg Roach ->select([new Expression('COUNT(*) AS total'), 'gedcom_name']) 25016a40a66SGreg Roach ->pluck('total', 'gedcom_name'); 251a5f7ed67SGreg Roach 252fa17fb66SGreg Roach $count_sources = DB::table('sources') 25316a40a66SGreg Roach ->join('gedcom', 's_file', '=', 'gedcom_id') 25416a40a66SGreg Roach ->whereIn('gedcom_id', $tree_ids) 25516a40a66SGreg Roach ->groupBy(['gedcom_id']) 25616a40a66SGreg Roach ->select([new Expression('COUNT(*) AS total'), 'gedcom_name']) 25716a40a66SGreg Roach ->pluck('total', 'gedcom_name'); 258a5f7ed67SGreg Roach 259c5a1ffe6SGreg Roach $count_submitters = DB::table('other') 260c5a1ffe6SGreg Roach ->join('gedcom', 'o_file', '=', 'gedcom_id') 261c5a1ffe6SGreg Roach ->whereIn('gedcom_id', $tree_ids) 262c5a1ffe6SGreg Roach ->where('o_type', '=', Submitter::RECORD_TYPE) 263c5a1ffe6SGreg Roach ->groupBy(['gedcom_id']) 264c5a1ffe6SGreg Roach ->select([new Expression('COUNT(*) AS total'), 'gedcom_name']) 265c5a1ffe6SGreg Roach ->pluck('total', 'gedcom_name'); 266c5a1ffe6SGreg Roach 26716a40a66SGreg Roach // Versions 2.0.1 and earlier of this module stored large amounts of data in the settings. 26816a40a66SGreg Roach DB::table('module_setting') 26916a40a66SGreg Roach ->where('module_name', '=', $this->name()) 27016a40a66SGreg Roach ->delete(); 27116a40a66SGreg Roach 27214aabe72SGreg Roach return view('modules/sitemap/sitemap-index-xml', [ 2733df1e584SGreg Roach 'all_trees' => $this->tree_service->all(), 274c5a1ffe6SGreg Roach 'count_families' => $count_families, 275a5f7ed67SGreg Roach 'count_individuals' => $count_individuals, 276a5f7ed67SGreg Roach 'count_media' => $count_media, 277a5f7ed67SGreg Roach 'count_notes' => $count_notes, 278a5f7ed67SGreg Roach 'count_repositories' => $count_repositories, 279a5f7ed67SGreg Roach 'count_sources' => $count_sources, 280c5a1ffe6SGreg Roach 'count_submitters' => $count_submitters, 281a5f7ed67SGreg Roach 'last_mod' => date('Y-m-d'), 282a5f7ed67SGreg Roach 'records_per_volume' => self::RECORDS_PER_VOLUME, 28314aabe72SGreg Roach 'sitemap_xsl' => route('sitemap-style'), 284a5f7ed67SGreg Roach ]); 28516a40a66SGreg Roach }, self::CACHE_LIFE); 286a5f7ed67SGreg Roach 2876ccdf4f0SGreg Roach return response($content, StatusCodeInterface::STATUS_OK, [ 2886172e7f6SGreg Roach 'content-type' => 'application/xml', 289a5f7ed67SGreg Roach ]); 290a5f7ed67SGreg Roach } 291a5f7ed67SGreg Roach 292a5f7ed67SGreg Roach /** 2936ccdf4f0SGreg Roach * @param ServerRequestInterface $request 294a5f7ed67SGreg Roach * 2956ccdf4f0SGreg Roach * @return ResponseInterface 296a5f7ed67SGreg Roach */ 29716a40a66SGreg Roach private function siteMapFile(ServerRequestInterface $request): ResponseInterface 298c1010edaSGreg Roach { 299b55cbc6bSGreg Roach $tree = Validator::attributes($request)->tree('tree'); 300b55cbc6bSGreg Roach $type = Validator::attributes($request)->string('type'); 301b55cbc6bSGreg Roach $page = Validator::attributes($request)->integer('page'); 302a5f7ed67SGreg Roach 303659aa375SGreg Roach if ($tree->getPreference('include_in_sitemap') !== '1') { 304659aa375SGreg Roach throw new HttpNotFoundException(); 305659aa375SGreg Roach } 306659aa375SGreg Roach 307c5a1ffe6SGreg Roach $cache_key = 'sitemap/' . $tree->id() . '/' . $type . '/' . $page . '.xml'; 30816a40a66SGreg Roach 3096b9cb339SGreg Roach $content = Registry::cache()->file()->remember($cache_key, function () use ($tree, $type, $page): string { 310c5a1ffe6SGreg Roach $records = $this->sitemapRecords($tree, $type, self::RECORDS_PER_VOLUME, self::RECORDS_PER_VOLUME * $page); 31116a40a66SGreg Roach 31214aabe72SGreg Roach return view('modules/sitemap/sitemap-file-xml', [ 313c5a1ffe6SGreg Roach 'priority' => self::PRIORITY[$type], 3149e3c7a99SGreg Roach 'records' => $records, 31514aabe72SGreg Roach 'sitemap_xsl' => route('sitemap-style'), 3169e3c7a99SGreg Roach 'tree' => $tree, 3179e3c7a99SGreg Roach ]); 31816a40a66SGreg Roach }, self::CACHE_LIFE); 319a5f7ed67SGreg Roach 3206ccdf4f0SGreg Roach return response($content, StatusCodeInterface::STATUS_OK, [ 3216172e7f6SGreg Roach 'content-type' => 'application/xml', 322a5f7ed67SGreg Roach ]); 323a5f7ed67SGreg Roach } 324a5f7ed67SGreg Roach 325a5f7ed67SGreg Roach /** 326a5f7ed67SGreg Roach * @param Tree $tree 327a5f7ed67SGreg Roach * @param string $type 328a5f7ed67SGreg Roach * @param int $limit 329a5f7ed67SGreg Roach * @param int $offset 330a5f7ed67SGreg Roach * 33136779af1SGreg Roach * @return Collection<int,GedcomRecord> 332a5f7ed67SGreg Roach */ 333886b77daSGreg Roach private function sitemapRecords(Tree $tree, string $type, int $limit, int $offset): Collection 334c1010edaSGreg Roach { 335a5f7ed67SGreg Roach switch ($type) { 336c5a1ffe6SGreg Roach case Family::RECORD_TYPE: 337c5a1ffe6SGreg Roach $records = $this->sitemapFamilies($tree, $limit, $offset); 338c5a1ffe6SGreg Roach break; 339c5a1ffe6SGreg Roach 34016a40a66SGreg Roach case Individual::RECORD_TYPE: 341a5f7ed67SGreg Roach $records = $this->sitemapIndividuals($tree, $limit, $offset); 342a5f7ed67SGreg Roach break; 343a5f7ed67SGreg Roach 34416a40a66SGreg Roach case Media::RECORD_TYPE: 345a5f7ed67SGreg Roach $records = $this->sitemapMedia($tree, $limit, $offset); 346a5f7ed67SGreg Roach break; 347a5f7ed67SGreg Roach 34816a40a66SGreg Roach case Note::RECORD_TYPE: 349a5f7ed67SGreg Roach $records = $this->sitemapNotes($tree, $limit, $offset); 350a5f7ed67SGreg Roach break; 351a5f7ed67SGreg Roach 35216a40a66SGreg Roach case Repository::RECORD_TYPE: 353a5f7ed67SGreg Roach $records = $this->sitemapRepositories($tree, $limit, $offset); 354a5f7ed67SGreg Roach break; 355a5f7ed67SGreg Roach 35616a40a66SGreg Roach case Source::RECORD_TYPE: 357a5f7ed67SGreg Roach $records = $this->sitemapSources($tree, $limit, $offset); 358a5f7ed67SGreg Roach break; 359a5f7ed67SGreg Roach 360c5a1ffe6SGreg Roach case Submitter::RECORD_TYPE: 361c5a1ffe6SGreg Roach $records = $this->sitemapSubmitters($tree, $limit, $offset); 362c5a1ffe6SGreg Roach break; 363c5a1ffe6SGreg Roach 364a5f7ed67SGreg Roach default: 365d501c45dSGreg Roach throw new HttpNotFoundException('Invalid record type: ' . $type); 366a5f7ed67SGreg Roach } 367a5f7ed67SGreg Roach 368a5f7ed67SGreg Roach // Skip private records. 369659aa375SGreg Roach $records = $records->filter(static function (GedcomRecord $record): bool { 370659aa375SGreg Roach return $record->canShow(Auth::PRIV_PRIVATE); 371659aa375SGreg Roach }); 372a5f7ed67SGreg Roach 373a5f7ed67SGreg Roach return $records; 374a5f7ed67SGreg Roach } 375a5f7ed67SGreg Roach 376a5f7ed67SGreg Roach /** 377a5f7ed67SGreg Roach * @param Tree $tree 378a5f7ed67SGreg Roach * @param int $limit 379a5f7ed67SGreg Roach * @param int $offset 380a5f7ed67SGreg Roach * 38136779af1SGreg Roach * @return Collection<int,Family> 382c5a1ffe6SGreg Roach */ 383c5a1ffe6SGreg Roach private function sitemapFamilies(Tree $tree, int $limit, int $offset): Collection 384c5a1ffe6SGreg Roach { 385c5a1ffe6SGreg Roach return DB::table('families') 386c5a1ffe6SGreg Roach ->where('f_file', '=', $tree->id()) 387c5a1ffe6SGreg Roach ->orderBy('f_id') 388c5a1ffe6SGreg Roach ->skip($offset) 389c5a1ffe6SGreg Roach ->take($limit) 390c5a1ffe6SGreg Roach ->get() 3916b9cb339SGreg Roach ->map(Registry::familyFactory()->mapper($tree)); 392c5a1ffe6SGreg Roach } 393c5a1ffe6SGreg Roach 394c5a1ffe6SGreg Roach /** 395c5a1ffe6SGreg Roach * @param Tree $tree 396c5a1ffe6SGreg Roach * @param int $limit 397c5a1ffe6SGreg Roach * @param int $offset 398c5a1ffe6SGreg Roach * 39936779af1SGreg Roach * @return Collection<int,Individual> 400a5f7ed67SGreg Roach */ 401886b77daSGreg Roach private function sitemapIndividuals(Tree $tree, int $limit, int $offset): Collection 402c1010edaSGreg Roach { 403886b77daSGreg Roach return DB::table('individuals') 404fa17fb66SGreg Roach ->where('i_file', '=', $tree->id()) 405fa17fb66SGreg Roach ->orderBy('i_id') 406fa17fb66SGreg Roach ->skip($offset) 407fa17fb66SGreg Roach ->take($limit) 408886b77daSGreg Roach ->get() 4096b9cb339SGreg Roach ->map(Registry::individualFactory()->mapper($tree)); 4108c2e8227SGreg Roach } 411a5f7ed67SGreg Roach 412a5f7ed67SGreg Roach /** 413a5f7ed67SGreg Roach * @param Tree $tree 414a5f7ed67SGreg Roach * @param int $limit 415a5f7ed67SGreg Roach * @param int $offset 416a5f7ed67SGreg Roach * 41736779af1SGreg Roach * @return Collection<int,Media> 418a5f7ed67SGreg Roach */ 419886b77daSGreg Roach private function sitemapMedia(Tree $tree, int $limit, int $offset): Collection 420c1010edaSGreg Roach { 421886b77daSGreg Roach return DB::table('media') 422fa17fb66SGreg Roach ->where('m_file', '=', $tree->id()) 423fa17fb66SGreg Roach ->orderBy('m_id') 424fa17fb66SGreg Roach ->skip($offset) 425fa17fb66SGreg Roach ->take($limit) 426886b77daSGreg Roach ->get() 4276b9cb339SGreg Roach ->map(Registry::mediaFactory()->mapper($tree)); 4288c2e8227SGreg Roach } 4298c2e8227SGreg Roach 4308c2e8227SGreg Roach /** 431a5f7ed67SGreg Roach * @param Tree $tree 432a5f7ed67SGreg Roach * @param int $limit 433a5f7ed67SGreg Roach * @param int $offset 434a5f7ed67SGreg Roach * 43536779af1SGreg Roach * @return Collection<int,Note> 4368c2e8227SGreg Roach */ 437886b77daSGreg Roach private function sitemapNotes(Tree $tree, int $limit, int $offset): Collection 438c1010edaSGreg Roach { 439886b77daSGreg Roach return DB::table('other') 440fa17fb66SGreg Roach ->where('o_file', '=', $tree->id()) 441c5a1ffe6SGreg Roach ->where('o_type', '=', Note::RECORD_TYPE) 442fa17fb66SGreg Roach ->orderBy('o_id') 443fa17fb66SGreg Roach ->skip($offset) 444fa17fb66SGreg Roach ->take($limit) 445886b77daSGreg Roach ->get() 4466b9cb339SGreg Roach ->map(Registry::noteFactory()->mapper($tree)); 4478c2e8227SGreg Roach } 4488c2e8227SGreg Roach 449a5f7ed67SGreg Roach /** 450a5f7ed67SGreg Roach * @param Tree $tree 451a5f7ed67SGreg Roach * @param int $limit 452a5f7ed67SGreg Roach * @param int $offset 453a5f7ed67SGreg Roach * 45436779af1SGreg Roach * @return Collection<int,Repository> 455a5f7ed67SGreg Roach */ 456886b77daSGreg Roach private function sitemapRepositories(Tree $tree, int $limit, int $offset): Collection 457c1010edaSGreg Roach { 458886b77daSGreg Roach return DB::table('other') 459fa17fb66SGreg Roach ->where('o_file', '=', $tree->id()) 460c5a1ffe6SGreg Roach ->where('o_type', '=', Repository::RECORD_TYPE) 461fa17fb66SGreg Roach ->orderBy('o_id') 462fa17fb66SGreg Roach ->skip($offset) 463fa17fb66SGreg Roach ->take($limit) 464886b77daSGreg Roach ->get() 4656b9cb339SGreg Roach ->map(Registry::repositoryFactory()->mapper($tree)); 466a5f7ed67SGreg Roach } 467a5f7ed67SGreg Roach 468a5f7ed67SGreg Roach /** 469a5f7ed67SGreg Roach * @param Tree $tree 470a5f7ed67SGreg Roach * @param int $limit 471a5f7ed67SGreg Roach * @param int $offset 472a5f7ed67SGreg Roach * 47336779af1SGreg Roach * @return Collection<int,Source> 474a5f7ed67SGreg Roach */ 475886b77daSGreg Roach private function sitemapSources(Tree $tree, int $limit, int $offset): Collection 476c1010edaSGreg Roach { 477886b77daSGreg Roach return DB::table('sources') 478fa17fb66SGreg Roach ->where('s_file', '=', $tree->id()) 479fa17fb66SGreg Roach ->orderBy('s_id') 480fa17fb66SGreg Roach ->skip($offset) 481fa17fb66SGreg Roach ->take($limit) 482886b77daSGreg Roach ->get() 4836b9cb339SGreg Roach ->map(Registry::sourceFactory()->mapper($tree)); 4848c2e8227SGreg Roach } 485c5a1ffe6SGreg Roach 486c5a1ffe6SGreg Roach /** 487c5a1ffe6SGreg Roach * @param Tree $tree 488c5a1ffe6SGreg Roach * @param int $limit 489c5a1ffe6SGreg Roach * @param int $offset 490c5a1ffe6SGreg Roach * 49136779af1SGreg Roach * @return Collection<int,Submitter> 492c5a1ffe6SGreg Roach */ 493c5a1ffe6SGreg Roach private function sitemapSubmitters(Tree $tree, int $limit, int $offset): Collection 494c5a1ffe6SGreg Roach { 495c5a1ffe6SGreg Roach return DB::table('other') 496c5a1ffe6SGreg Roach ->where('o_file', '=', $tree->id()) 497c5a1ffe6SGreg Roach ->where('o_type', '=', Submitter::RECORD_TYPE) 498c5a1ffe6SGreg Roach ->orderBy('o_id') 499c5a1ffe6SGreg Roach ->skip($offset) 500c5a1ffe6SGreg Roach ->take($limit) 501c5a1ffe6SGreg Roach ->get() 5026b9cb339SGreg Roach ->map(Registry::submitterFactory()->mapper($tree)); 503c5a1ffe6SGreg Roach } 5048c2e8227SGreg Roach} 505