18c2e8227SGreg Roach<?php 23976b470SGreg Roach 38c2e8227SGreg Roach/** 48c2e8227SGreg Roach * webtrees: online genealogy 5d11be702SGreg 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; 246f4ec3caSGreg Roachuse Fisharebest\Webtrees\DB; 25c5a1ffe6SGreg Roachuse Fisharebest\Webtrees\Family; 26a5f7ed67SGreg Roachuse Fisharebest\Webtrees\FlashMessages; 27a5f7ed67SGreg Roachuse Fisharebest\Webtrees\GedcomRecord; 28b1b85189SGreg Roachuse Fisharebest\Webtrees\Html; 2981b729d3SGreg Roachuse Fisharebest\Webtrees\Http\Exceptions\HttpNotFoundException; 300e62c4b8SGreg Roachuse Fisharebest\Webtrees\I18N; 310e62c4b8SGreg Roachuse Fisharebest\Webtrees\Individual; 320e62c4b8SGreg Roachuse Fisharebest\Webtrees\Media; 330e62c4b8SGreg Roachuse Fisharebest\Webtrees\Note; 3481b729d3SGreg Roachuse Fisharebest\Webtrees\Registry; 350e62c4b8SGreg Roachuse Fisharebest\Webtrees\Repository; 363df1e584SGreg Roachuse Fisharebest\Webtrees\Services\TreeService; 370e62c4b8SGreg Roachuse Fisharebest\Webtrees\Source; 38c5a1ffe6SGreg Roachuse Fisharebest\Webtrees\Submitter; 390e62c4b8SGreg Roachuse Fisharebest\Webtrees\Tree; 40b55cbc6bSGreg Roachuse Fisharebest\Webtrees\Validator; 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 * @param TreeService $tree_service 773df1e584SGreg Roach */ 783df1e584SGreg Roach public function __construct(TreeService $tree_service) 793df1e584SGreg Roach { 803df1e584SGreg Roach $this->tree_service = $tree_service; 813df1e584SGreg Roach } 823df1e584SGreg Roach 83a5f7ed67SGreg Roach /** 8416a40a66SGreg Roach * Initialization. 8516a40a66SGreg Roach * 8616a40a66SGreg Roach * @return void 8716a40a66SGreg Roach */ 8816a40a66SGreg Roach public function boot(): void 8916a40a66SGreg Roach { 90158900c2SGreg Roach Registry::routeFactory()->routeMap() 9114aabe72SGreg Roach ->get('sitemap-style', '/sitemap.xsl', $this); 9214aabe72SGreg Roach 93158900c2SGreg Roach Registry::routeFactory()->routeMap() 9416a40a66SGreg Roach ->get('sitemap-index', '/sitemap.xml', $this); 9516a40a66SGreg Roach 96158900c2SGreg Roach Registry::routeFactory()->routeMap() 97c5a1ffe6SGreg Roach ->get('sitemap-file', '/sitemap-{tree}-{type}-{page}.xml', $this); 9816a40a66SGreg Roach } 9916a40a66SGreg Roach 10049a243cbSGreg Roach public function description(): string 101c1010edaSGreg Roach { 102bbb76c12SGreg Roach /* I18N: Description of the “Sitemaps” module */ 103bbb76c12SGreg Roach return I18N::translate('Generate sitemap files for search engines.'); 1048c2e8227SGreg Roach } 1058c2e8227SGreg Roach 10676692c8bSGreg Roach /** 107abafa13cSGreg Roach * Should this module be enabled when it is first installed? 108abafa13cSGreg Roach * 109abafa13cSGreg Roach * @return bool 110abafa13cSGreg Roach */ 111abafa13cSGreg Roach public function isEnabledByDefault(): bool 112abafa13cSGreg Roach { 113abafa13cSGreg Roach return false; 114abafa13cSGreg Roach } 115abafa13cSGreg Roach 116abafa13cSGreg Roach /** 11757ab2231SGreg Roach * @param ServerRequestInterface $request 11857ab2231SGreg Roach * 1196ccdf4f0SGreg Roach * @return ResponseInterface 1208c2e8227SGreg Roach */ 12149528f2bSGreg Roach public function getAdminAction(ServerRequestInterface $request): ResponseInterface 122c1010edaSGreg Roach { 123a5f7ed67SGreg Roach $this->layout = 'layouts/administration'; 124a5f7ed67SGreg Roach 12516a40a66SGreg Roach $sitemap_url = route('sitemap-index'); 126a5f7ed67SGreg Roach 127291c1b19SGreg Roach return $this->viewResponse('modules/sitemap/config', [ 1283df1e584SGreg Roach 'all_trees' => $this->tree_service->all(), 129a5f7ed67SGreg Roach 'sitemap_url' => $sitemap_url, 13049a243cbSGreg Roach 'title' => $this->title(), 131a5f7ed67SGreg Roach ]); 1328c2e8227SGreg Roach } 1338c2e8227SGreg Roach 1348c2e8227SGreg Roach /** 1356ccdf4f0SGreg Roach * How should this module be identified in the control panel, etc.? 136a5f7ed67SGreg Roach * 1376ccdf4f0SGreg Roach * @return string 1388c2e8227SGreg Roach */ 1396ccdf4f0SGreg Roach public function title(): string 1406ccdf4f0SGreg Roach { 141ad3143ccSGreg Roach /* I18N: Name of a module - see https://en.wikipedia.org/wiki/Sitemaps */ 1426ccdf4f0SGreg Roach return I18N::translate('Sitemaps'); 1436ccdf4f0SGreg Roach } 1446ccdf4f0SGreg Roach 1456ccdf4f0SGreg Roach /** 1466ccdf4f0SGreg Roach * @param ServerRequestInterface $request 1476ccdf4f0SGreg Roach * 1486ccdf4f0SGreg Roach * @return ResponseInterface 1496ccdf4f0SGreg Roach */ 1506ccdf4f0SGreg Roach public function postAdminAction(ServerRequestInterface $request): ResponseInterface 151c1010edaSGreg Roach { 1523df1e584SGreg Roach foreach ($this->tree_service->all() as $tree) { 153748dbe15SGreg Roach $include_in_sitemap = Validator::parsedBody($request)->boolean('sitemap' . $tree->id(), false); 154a5f7ed67SGreg Roach $tree->setPreference('include_in_sitemap', (string) $include_in_sitemap); 1558c2e8227SGreg Roach } 156a5f7ed67SGreg Roach 15749a243cbSGreg Roach FlashMessages::addMessage(I18N::translate('The preferences for the module “%s” have been updated.', $this->title()), 'success'); 158a5f7ed67SGreg Roach 1596ccdf4f0SGreg Roach return redirect($this->getConfigLink()); 1608c2e8227SGreg Roach } 1618c2e8227SGreg Roach 1628c2e8227SGreg Roach /** 16357ab2231SGreg Roach * @param ServerRequestInterface $request 16457ab2231SGreg Roach * 1656ccdf4f0SGreg Roach * @return ResponseInterface 1668c2e8227SGreg Roach */ 16716a40a66SGreg Roach public function handle(ServerRequestInterface $request): ResponseInterface 168c1010edaSGreg Roach { 169b55cbc6bSGreg Roach $route = Validator::attributes($request)->route(); 170a5f7ed67SGreg Roach 17114aabe72SGreg Roach if ($route->name === 'sitemap-style') { 17214aabe72SGreg Roach $content = view('modules/sitemap/sitemap-xsl'); 17314aabe72SGreg Roach 17414aabe72SGreg Roach return response($content, StatusCodeInterface::STATUS_OK, [ 1756172e7f6SGreg Roach 'content-type' => 'application/xml', 17614aabe72SGreg Roach ]); 17714aabe72SGreg Roach } 17814aabe72SGreg Roach 17916a40a66SGreg Roach if ($route->name === 'sitemap-index') { 18016a40a66SGreg Roach return $this->siteMapIndex($request); 18116a40a66SGreg Roach } 18216a40a66SGreg Roach 18316a40a66SGreg Roach return $this->siteMapFile($request); 18416a40a66SGreg Roach } 18516a40a66SGreg Roach 18616a40a66SGreg Roach /** 18716a40a66SGreg Roach * @param ServerRequestInterface $request 18816a40a66SGreg Roach * 18916a40a66SGreg Roach * @return ResponseInterface 19016a40a66SGreg Roach */ 19149528f2bSGreg Roach private function siteMapIndex(ServerRequestInterface $request): ResponseInterface 19216a40a66SGreg Roach { 1936b9cb339SGreg Roach $content = Registry::cache()->file()->remember('sitemap.xml', function (): string { 194659aa375SGreg Roach // Which trees have sitemaps enabled? 195*f25fc0f9SGreg Roach $tree_ids = $this->tree_service->all() 196*f25fc0f9SGreg Roach ->filter(static fn (Tree $tree): bool => $tree->getPreference('include_in_sitemap') === '1') 197*f25fc0f9SGreg Roach ->map(static fn (Tree $tree): int => $tree->id()); 198659aa375SGreg Roach 199c5a1ffe6SGreg Roach $count_families = DB::table('families') 200c5a1ffe6SGreg Roach ->join('gedcom', 'f_file', '=', 'gedcom_id') 201c5a1ffe6SGreg Roach ->whereIn('gedcom_id', $tree_ids) 202c5a1ffe6SGreg Roach ->groupBy(['gedcom_id']) 203059898c9SGreg Roach ->pluck(new Expression('COUNT(*) AS total'), 'gedcom_name'); 204c5a1ffe6SGreg Roach 205fa17fb66SGreg Roach $count_individuals = DB::table('individuals') 20616a40a66SGreg Roach ->join('gedcom', 'i_file', '=', 'gedcom_id') 20716a40a66SGreg Roach ->whereIn('gedcom_id', $tree_ids) 20816a40a66SGreg Roach ->groupBy(['gedcom_id']) 209059898c9SGreg Roach ->pluck(new Expression('COUNT(*) AS total'), 'gedcom_name'); 210a5f7ed67SGreg Roach 211fa17fb66SGreg Roach $count_media = DB::table('media') 21216a40a66SGreg Roach ->join('gedcom', 'm_file', '=', 'gedcom_id') 21316a40a66SGreg Roach ->whereIn('gedcom_id', $tree_ids) 21416a40a66SGreg Roach ->groupBy(['gedcom_id']) 215059898c9SGreg Roach ->pluck(new Expression('COUNT(*) AS total'), 'gedcom_name'); 216a5f7ed67SGreg Roach 217fa17fb66SGreg Roach $count_notes = DB::table('other') 21816a40a66SGreg Roach ->join('gedcom', 'o_file', '=', 'gedcom_id') 21916a40a66SGreg Roach ->whereIn('gedcom_id', $tree_ids) 220c5a1ffe6SGreg Roach ->where('o_type', '=', Note::RECORD_TYPE) 22116a40a66SGreg Roach ->groupBy(['gedcom_id']) 222059898c9SGreg Roach ->pluck(new Expression('COUNT(*) AS total'), 'gedcom_name'); 223a5f7ed67SGreg Roach 224fa17fb66SGreg Roach $count_repositories = DB::table('other') 22516a40a66SGreg Roach ->join('gedcom', 'o_file', '=', 'gedcom_id') 22616a40a66SGreg Roach ->whereIn('gedcom_id', $tree_ids) 227c5a1ffe6SGreg Roach ->where('o_type', '=', Repository::RECORD_TYPE) 22816a40a66SGreg Roach ->groupBy(['gedcom_id']) 229059898c9SGreg Roach ->pluck(new Expression('COUNT(*) AS total'), 'gedcom_name'); 230a5f7ed67SGreg Roach 231fa17fb66SGreg Roach $count_sources = DB::table('sources') 23216a40a66SGreg Roach ->join('gedcom', 's_file', '=', 'gedcom_id') 23316a40a66SGreg Roach ->whereIn('gedcom_id', $tree_ids) 23416a40a66SGreg Roach ->groupBy(['gedcom_id']) 235059898c9SGreg Roach ->pluck(new Expression('COUNT(*) AS total'), 'gedcom_name'); 236a5f7ed67SGreg Roach 237c5a1ffe6SGreg Roach $count_submitters = DB::table('other') 238c5a1ffe6SGreg Roach ->join('gedcom', 'o_file', '=', 'gedcom_id') 239c5a1ffe6SGreg Roach ->whereIn('gedcom_id', $tree_ids) 240c5a1ffe6SGreg Roach ->where('o_type', '=', Submitter::RECORD_TYPE) 241c5a1ffe6SGreg Roach ->groupBy(['gedcom_id']) 242059898c9SGreg Roach ->pluck(new Expression('COUNT(*) AS total'), 'gedcom_name'); 243c5a1ffe6SGreg Roach 24416a40a66SGreg Roach // Versions 2.0.1 and earlier of this module stored large amounts of data in the settings. 24516a40a66SGreg Roach DB::table('module_setting') 24616a40a66SGreg Roach ->where('module_name', '=', $this->name()) 24716a40a66SGreg Roach ->delete(); 24816a40a66SGreg Roach 24914aabe72SGreg Roach return view('modules/sitemap/sitemap-index-xml', [ 2503df1e584SGreg Roach 'all_trees' => $this->tree_service->all(), 251c5a1ffe6SGreg Roach 'count_families' => $count_families, 252a5f7ed67SGreg Roach 'count_individuals' => $count_individuals, 253a5f7ed67SGreg Roach 'count_media' => $count_media, 254a5f7ed67SGreg Roach 'count_notes' => $count_notes, 255a5f7ed67SGreg Roach 'count_repositories' => $count_repositories, 256a5f7ed67SGreg Roach 'count_sources' => $count_sources, 257c5a1ffe6SGreg Roach 'count_submitters' => $count_submitters, 258a5f7ed67SGreg Roach 'last_mod' => date('Y-m-d'), 259a5f7ed67SGreg Roach 'records_per_volume' => self::RECORDS_PER_VOLUME, 26014aabe72SGreg Roach 'sitemap_xsl' => route('sitemap-style'), 261a5f7ed67SGreg Roach ]); 26216a40a66SGreg Roach }, self::CACHE_LIFE); 263a5f7ed67SGreg Roach 2646ccdf4f0SGreg Roach return response($content, StatusCodeInterface::STATUS_OK, [ 2656172e7f6SGreg Roach 'content-type' => 'application/xml', 266a5f7ed67SGreg Roach ]); 267a5f7ed67SGreg Roach } 268a5f7ed67SGreg Roach 269a5f7ed67SGreg Roach /** 2706ccdf4f0SGreg Roach * @param ServerRequestInterface $request 271a5f7ed67SGreg Roach * 2726ccdf4f0SGreg Roach * @return ResponseInterface 273a5f7ed67SGreg Roach */ 27416a40a66SGreg Roach private function siteMapFile(ServerRequestInterface $request): ResponseInterface 275c1010edaSGreg Roach { 276b55cbc6bSGreg Roach $tree = Validator::attributes($request)->tree('tree'); 277b55cbc6bSGreg Roach $type = Validator::attributes($request)->string('type'); 278b55cbc6bSGreg Roach $page = Validator::attributes($request)->integer('page'); 279a5f7ed67SGreg Roach 280659aa375SGreg Roach if ($tree->getPreference('include_in_sitemap') !== '1') { 281659aa375SGreg Roach throw new HttpNotFoundException(); 282659aa375SGreg Roach } 283659aa375SGreg Roach 284c5a1ffe6SGreg Roach $cache_key = 'sitemap/' . $tree->id() . '/' . $type . '/' . $page . '.xml'; 28516a40a66SGreg Roach 2866b9cb339SGreg Roach $content = Registry::cache()->file()->remember($cache_key, function () use ($tree, $type, $page): string { 287c5a1ffe6SGreg Roach $records = $this->sitemapRecords($tree, $type, self::RECORDS_PER_VOLUME, self::RECORDS_PER_VOLUME * $page); 28816a40a66SGreg Roach 28914aabe72SGreg Roach return view('modules/sitemap/sitemap-file-xml', [ 290c5a1ffe6SGreg Roach 'priority' => self::PRIORITY[$type], 2919e3c7a99SGreg Roach 'records' => $records, 29214aabe72SGreg Roach 'sitemap_xsl' => route('sitemap-style'), 2939e3c7a99SGreg Roach 'tree' => $tree, 2949e3c7a99SGreg Roach ]); 29516a40a66SGreg Roach }, self::CACHE_LIFE); 296a5f7ed67SGreg Roach 2976ccdf4f0SGreg Roach return response($content, StatusCodeInterface::STATUS_OK, [ 2986172e7f6SGreg Roach 'content-type' => 'application/xml', 299a5f7ed67SGreg Roach ]); 300a5f7ed67SGreg Roach } 301a5f7ed67SGreg Roach 302a5f7ed67SGreg Roach /** 303a5f7ed67SGreg Roach * @param Tree $tree 304a5f7ed67SGreg Roach * @param string $type 305a5f7ed67SGreg Roach * @param int $limit 306a5f7ed67SGreg Roach * @param int $offset 307a5f7ed67SGreg Roach * 30836779af1SGreg Roach * @return Collection<int,GedcomRecord> 309a5f7ed67SGreg Roach */ 310886b77daSGreg Roach private function sitemapRecords(Tree $tree, string $type, int $limit, int $offset): Collection 311c1010edaSGreg Roach { 312a5f7ed67SGreg Roach switch ($type) { 313c5a1ffe6SGreg Roach case Family::RECORD_TYPE: 314c5a1ffe6SGreg Roach $records = $this->sitemapFamilies($tree, $limit, $offset); 315c5a1ffe6SGreg Roach break; 316c5a1ffe6SGreg Roach 31716a40a66SGreg Roach case Individual::RECORD_TYPE: 318a5f7ed67SGreg Roach $records = $this->sitemapIndividuals($tree, $limit, $offset); 319a5f7ed67SGreg Roach break; 320a5f7ed67SGreg Roach 32116a40a66SGreg Roach case Media::RECORD_TYPE: 322a5f7ed67SGreg Roach $records = $this->sitemapMedia($tree, $limit, $offset); 323a5f7ed67SGreg Roach break; 324a5f7ed67SGreg Roach 32516a40a66SGreg Roach case Note::RECORD_TYPE: 326a5f7ed67SGreg Roach $records = $this->sitemapNotes($tree, $limit, $offset); 327a5f7ed67SGreg Roach break; 328a5f7ed67SGreg Roach 32916a40a66SGreg Roach case Repository::RECORD_TYPE: 330a5f7ed67SGreg Roach $records = $this->sitemapRepositories($tree, $limit, $offset); 331a5f7ed67SGreg Roach break; 332a5f7ed67SGreg Roach 33316a40a66SGreg Roach case Source::RECORD_TYPE: 334a5f7ed67SGreg Roach $records = $this->sitemapSources($tree, $limit, $offset); 335a5f7ed67SGreg Roach break; 336a5f7ed67SGreg Roach 337c5a1ffe6SGreg Roach case Submitter::RECORD_TYPE: 338c5a1ffe6SGreg Roach $records = $this->sitemapSubmitters($tree, $limit, $offset); 339c5a1ffe6SGreg Roach break; 340c5a1ffe6SGreg Roach 341a5f7ed67SGreg Roach default: 342d501c45dSGreg Roach throw new HttpNotFoundException('Invalid record type: ' . $type); 343a5f7ed67SGreg Roach } 344a5f7ed67SGreg Roach 345a5f7ed67SGreg Roach // Skip private records. 346*f25fc0f9SGreg Roach $records = $records->filter(static fn (GedcomRecord $record): bool => $record->canShow(Auth::PRIV_PRIVATE)); 347a5f7ed67SGreg Roach 348a5f7ed67SGreg Roach return $records; 349a5f7ed67SGreg Roach } 350a5f7ed67SGreg Roach 351a5f7ed67SGreg Roach /** 352a5f7ed67SGreg Roach * @param Tree $tree 353a5f7ed67SGreg Roach * @param int $limit 354a5f7ed67SGreg Roach * @param int $offset 355a5f7ed67SGreg Roach * 35636779af1SGreg Roach * @return Collection<int,Family> 357c5a1ffe6SGreg Roach */ 358c5a1ffe6SGreg Roach private function sitemapFamilies(Tree $tree, int $limit, int $offset): Collection 359c5a1ffe6SGreg Roach { 360c5a1ffe6SGreg Roach return DB::table('families') 361c5a1ffe6SGreg Roach ->where('f_file', '=', $tree->id()) 362c5a1ffe6SGreg Roach ->orderBy('f_id') 363c5a1ffe6SGreg Roach ->skip($offset) 364c5a1ffe6SGreg Roach ->take($limit) 365c5a1ffe6SGreg Roach ->get() 3666b9cb339SGreg Roach ->map(Registry::familyFactory()->mapper($tree)); 367c5a1ffe6SGreg Roach } 368c5a1ffe6SGreg Roach 369c5a1ffe6SGreg Roach /** 370c5a1ffe6SGreg Roach * @param Tree $tree 371c5a1ffe6SGreg Roach * @param int $limit 372c5a1ffe6SGreg Roach * @param int $offset 373c5a1ffe6SGreg Roach * 37436779af1SGreg Roach * @return Collection<int,Individual> 375a5f7ed67SGreg Roach */ 376886b77daSGreg Roach private function sitemapIndividuals(Tree $tree, int $limit, int $offset): Collection 377c1010edaSGreg Roach { 378886b77daSGreg Roach return DB::table('individuals') 379fa17fb66SGreg Roach ->where('i_file', '=', $tree->id()) 380fa17fb66SGreg Roach ->orderBy('i_id') 381fa17fb66SGreg Roach ->skip($offset) 382fa17fb66SGreg Roach ->take($limit) 383886b77daSGreg Roach ->get() 3846b9cb339SGreg Roach ->map(Registry::individualFactory()->mapper($tree)); 3858c2e8227SGreg Roach } 386a5f7ed67SGreg Roach 387a5f7ed67SGreg Roach /** 388a5f7ed67SGreg Roach * @param Tree $tree 389a5f7ed67SGreg Roach * @param int $limit 390a5f7ed67SGreg Roach * @param int $offset 391a5f7ed67SGreg Roach * 39236779af1SGreg Roach * @return Collection<int,Media> 393a5f7ed67SGreg Roach */ 394886b77daSGreg Roach private function sitemapMedia(Tree $tree, int $limit, int $offset): Collection 395c1010edaSGreg Roach { 396886b77daSGreg Roach return DB::table('media') 397fa17fb66SGreg Roach ->where('m_file', '=', $tree->id()) 398fa17fb66SGreg Roach ->orderBy('m_id') 399fa17fb66SGreg Roach ->skip($offset) 400fa17fb66SGreg Roach ->take($limit) 401886b77daSGreg Roach ->get() 4026b9cb339SGreg Roach ->map(Registry::mediaFactory()->mapper($tree)); 4038c2e8227SGreg Roach } 4048c2e8227SGreg Roach 4058c2e8227SGreg Roach /** 406a5f7ed67SGreg Roach * @param Tree $tree 407a5f7ed67SGreg Roach * @param int $limit 408a5f7ed67SGreg Roach * @param int $offset 409a5f7ed67SGreg Roach * 41036779af1SGreg Roach * @return Collection<int,Note> 4118c2e8227SGreg Roach */ 412886b77daSGreg Roach private function sitemapNotes(Tree $tree, int $limit, int $offset): Collection 413c1010edaSGreg Roach { 414886b77daSGreg Roach return DB::table('other') 415fa17fb66SGreg Roach ->where('o_file', '=', $tree->id()) 416c5a1ffe6SGreg Roach ->where('o_type', '=', Note::RECORD_TYPE) 417fa17fb66SGreg Roach ->orderBy('o_id') 418fa17fb66SGreg Roach ->skip($offset) 419fa17fb66SGreg Roach ->take($limit) 420886b77daSGreg Roach ->get() 4216b9cb339SGreg Roach ->map(Registry::noteFactory()->mapper($tree)); 4228c2e8227SGreg Roach } 4238c2e8227SGreg Roach 424a5f7ed67SGreg Roach /** 425a5f7ed67SGreg Roach * @param Tree $tree 426a5f7ed67SGreg Roach * @param int $limit 427a5f7ed67SGreg Roach * @param int $offset 428a5f7ed67SGreg Roach * 42936779af1SGreg Roach * @return Collection<int,Repository> 430a5f7ed67SGreg Roach */ 431886b77daSGreg Roach private function sitemapRepositories(Tree $tree, int $limit, int $offset): Collection 432c1010edaSGreg Roach { 433886b77daSGreg Roach return DB::table('other') 434fa17fb66SGreg Roach ->where('o_file', '=', $tree->id()) 435c5a1ffe6SGreg Roach ->where('o_type', '=', Repository::RECORD_TYPE) 436fa17fb66SGreg Roach ->orderBy('o_id') 437fa17fb66SGreg Roach ->skip($offset) 438fa17fb66SGreg Roach ->take($limit) 439886b77daSGreg Roach ->get() 4406b9cb339SGreg Roach ->map(Registry::repositoryFactory()->mapper($tree)); 441a5f7ed67SGreg Roach } 442a5f7ed67SGreg Roach 443a5f7ed67SGreg Roach /** 444a5f7ed67SGreg Roach * @param Tree $tree 445a5f7ed67SGreg Roach * @param int $limit 446a5f7ed67SGreg Roach * @param int $offset 447a5f7ed67SGreg Roach * 44836779af1SGreg Roach * @return Collection<int,Source> 449a5f7ed67SGreg Roach */ 450886b77daSGreg Roach private function sitemapSources(Tree $tree, int $limit, int $offset): Collection 451c1010edaSGreg Roach { 452886b77daSGreg Roach return DB::table('sources') 453fa17fb66SGreg Roach ->where('s_file', '=', $tree->id()) 454fa17fb66SGreg Roach ->orderBy('s_id') 455fa17fb66SGreg Roach ->skip($offset) 456fa17fb66SGreg Roach ->take($limit) 457886b77daSGreg Roach ->get() 4586b9cb339SGreg Roach ->map(Registry::sourceFactory()->mapper($tree)); 4598c2e8227SGreg Roach } 460c5a1ffe6SGreg Roach 461c5a1ffe6SGreg Roach /** 462c5a1ffe6SGreg Roach * @param Tree $tree 463c5a1ffe6SGreg Roach * @param int $limit 464c5a1ffe6SGreg Roach * @param int $offset 465c5a1ffe6SGreg Roach * 46636779af1SGreg Roach * @return Collection<int,Submitter> 467c5a1ffe6SGreg Roach */ 468c5a1ffe6SGreg Roach private function sitemapSubmitters(Tree $tree, int $limit, int $offset): Collection 469c5a1ffe6SGreg Roach { 470c5a1ffe6SGreg Roach return DB::table('other') 471c5a1ffe6SGreg Roach ->where('o_file', '=', $tree->id()) 472c5a1ffe6SGreg Roach ->where('o_type', '=', Submitter::RECORD_TYPE) 473c5a1ffe6SGreg Roach ->orderBy('o_id') 474c5a1ffe6SGreg Roach ->skip($offset) 475c5a1ffe6SGreg Roach ->take($limit) 476c5a1ffe6SGreg Roach ->get() 4776b9cb339SGreg Roach ->map(Registry::submitterFactory()->mapper($tree)); 478c5a1ffe6SGreg Roach } 4798c2e8227SGreg Roach} 480