1ce42304aSGreg Roach<?php 2ce42304aSGreg Roach 3ce42304aSGreg Roach/** 4ce42304aSGreg Roach * webtrees: online genealogy 5d11be702SGreg Roach * Copyright (C) 2023 webtrees development team 6ce42304aSGreg Roach * This program is free software: you can redistribute it and/or modify 7ce42304aSGreg Roach * it under the terms of the GNU General Public License as published by 8ce42304aSGreg Roach * the Free Software Foundation, either version 3 of the License, or 9ce42304aSGreg Roach * (at your option) any later version. 10ce42304aSGreg Roach * This program is distributed in the hope that it will be useful, 11ce42304aSGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of 12ce42304aSGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13ce42304aSGreg Roach * GNU General Public License for more details. 14ce42304aSGreg 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/>. 16ce42304aSGreg Roach */ 17ce42304aSGreg Roach 18ce42304aSGreg Roachdeclare(strict_types=1); 19ce42304aSGreg Roach 20ce42304aSGreg Roachnamespace Fisharebest\Webtrees\Module; 21ce42304aSGreg Roach 22*6f4ec3caSGreg Roachuse Fisharebest\Webtrees\DB; 23ce42304aSGreg Roachuse Fisharebest\Webtrees\Family; 24ce42304aSGreg Roachuse Fisharebest\Webtrees\GedcomRecord; 25ce42304aSGreg Roachuse Fisharebest\Webtrees\Individual; 267684867eSGreg Roachuse Fisharebest\Webtrees\Location; 27ce42304aSGreg Roachuse Fisharebest\Webtrees\Media; 28ce42304aSGreg Roachuse Fisharebest\Webtrees\Note; 29ce42304aSGreg Roachuse Fisharebest\Webtrees\Repository; 30ce42304aSGreg Roachuse Fisharebest\Webtrees\Source; 31ce42304aSGreg Roachuse Fisharebest\Webtrees\Submitter; 32ce42304aSGreg Roachuse Fisharebest\Webtrees\Tree; 33ce42304aSGreg Roachuse Illuminate\Database\Query\Builder; 34ce42304aSGreg Roachuse Illuminate\Support\Collection; 35ce42304aSGreg Roach 36ce42304aSGreg Roach/** 37ce42304aSGreg Roach * Trait ModuleDataFixTrait - default implementation of ModuleDataFixTrait 38ce42304aSGreg Roach */ 39ce42304aSGreg Roachtrait ModuleDataFixTrait 40ce42304aSGreg Roach{ 41ce42304aSGreg Roach /** 42ce42304aSGreg Roach * Options form. 43ce42304aSGreg Roach * 44ce42304aSGreg Roach * @param Tree $tree 45ce42304aSGreg Roach * 46ce42304aSGreg Roach * @return string 47ce42304aSGreg Roach */ 4849528f2bSGreg Roach public function fixOptions(Tree $tree): string 49ce42304aSGreg Roach { 50ce42304aSGreg Roach return ''; 51ce42304aSGreg Roach } 52ce42304aSGreg Roach 53ce42304aSGreg Roach /** 54ce42304aSGreg Roach * A list of all records that need examining. This may include records 55ce42304aSGreg Roach * that do not need updating, if we can't detect this quickly using SQL. 56ce42304aSGreg Roach * 57ce42304aSGreg Roach * @param Tree $tree 58ce42304aSGreg Roach * @param array<string,string> $params 59ce42304aSGreg Roach * 6036779af1SGreg Roach * @return Collection<int,object> 61ce42304aSGreg Roach */ 62ce42304aSGreg Roach public function recordsToFix(Tree $tree, array $params): Collection 63ce42304aSGreg Roach { 64ce42304aSGreg Roach $families = $this->familiesToFix($tree, $params); 65ce42304aSGreg Roach $individuals = $this->individualsToFix($tree, $params); 667684867eSGreg Roach $locations = $this->locationsToFix($tree, $params); 67ce42304aSGreg Roach $media = $this->mediaToFix($tree, $params); 68ce42304aSGreg Roach $notes = $this->notesToFix($tree, $params); 69ce42304aSGreg Roach $repositories = $this->repositoriesToFix($tree, $params); 70ce42304aSGreg Roach $sources = $this->sourcesToFix($tree, $params); 71ce42304aSGreg Roach $submitters = $this->submittersToFix($tree, $params); 72ce42304aSGreg Roach 73ce42304aSGreg Roach $records = new Collection(); 74ce42304aSGreg Roach 75ce42304aSGreg Roach if ($families !== null) { 76ce42304aSGreg Roach $records = $records->concat($this->mergePendingRecords($families, $tree, Family::RECORD_TYPE)); 77ce42304aSGreg Roach } 78ce42304aSGreg Roach 79ce42304aSGreg Roach if ($individuals !== null) { 80ce42304aSGreg Roach $records = $records->concat($this->mergePendingRecords($individuals, $tree, Individual::RECORD_TYPE)); 81ce42304aSGreg Roach } 82ce42304aSGreg Roach 837684867eSGreg Roach if ($locations !== null) { 847684867eSGreg Roach $records = $records->concat($this->mergePendingRecords($locations, $tree, Location::RECORD_TYPE)); 857684867eSGreg Roach } 867684867eSGreg Roach 87ce42304aSGreg Roach if ($media !== null) { 88ce42304aSGreg Roach $records = $records->concat($this->mergePendingRecords($media, $tree, Media::RECORD_TYPE)); 89ce42304aSGreg Roach } 90ce42304aSGreg Roach 91ce42304aSGreg Roach if ($notes !== null) { 92ce42304aSGreg Roach $records = $records->concat($this->mergePendingRecords($notes, $tree, Note::RECORD_TYPE)); 93ce42304aSGreg Roach } 94ce42304aSGreg Roach 95ce42304aSGreg Roach if ($repositories !== null) { 96ce42304aSGreg Roach $records = $records->concat($this->mergePendingRecords($repositories, $tree, Repository::RECORD_TYPE)); 97ce42304aSGreg Roach } 98ce42304aSGreg Roach 99ce42304aSGreg Roach if ($sources !== null) { 100ce42304aSGreg Roach $records = $records->concat($this->mergePendingRecords($sources, $tree, Source::RECORD_TYPE)); 101ce42304aSGreg Roach } 102ce42304aSGreg Roach 103ce42304aSGreg Roach if ($submitters !== null) { 104ce42304aSGreg Roach $records = $records->concat($this->mergePendingRecords($submitters, $tree, Submitter::RECORD_TYPE)); 105ce42304aSGreg Roach } 106ce42304aSGreg Roach 107ce42304aSGreg Roach return $records 108ce42304aSGreg Roach ->unique() 109f70bcff5SGreg Roach ->sort(static function (object $x, object $y) { 110ce42304aSGreg Roach return $x->xref <=> $y->xref; 111ce42304aSGreg Roach }); 112ce42304aSGreg Roach } 113ce42304aSGreg Roach 114ce42304aSGreg Roach /** 115ce42304aSGreg Roach * Does a record need updating? 116ce42304aSGreg Roach * 117ce42304aSGreg Roach * @param GedcomRecord $record 118ce42304aSGreg Roach * @param array<string,string> $params 119ce42304aSGreg Roach * 120ce42304aSGreg Roach * @return bool 121ce42304aSGreg Roach */ 12249528f2bSGreg Roach public function doesRecordNeedUpdate(GedcomRecord $record, array $params): bool 123ce42304aSGreg Roach { 124ce42304aSGreg Roach return false; 125ce42304aSGreg Roach } 126ce42304aSGreg Roach 127ce42304aSGreg Roach /** 128ce42304aSGreg Roach * Show the changes we would make 129ce42304aSGreg Roach * 130ce42304aSGreg Roach * @param GedcomRecord $record 131ce42304aSGreg Roach * @param array<string,string> $params 132ce42304aSGreg Roach * 133ce42304aSGreg Roach * @return string 134ce42304aSGreg Roach */ 13549528f2bSGreg Roach public function previewUpdate(GedcomRecord $record, array $params): string 136ce42304aSGreg Roach { 137ce42304aSGreg Roach return $record->fullName(); 138ce42304aSGreg Roach } 139ce42304aSGreg Roach 140ce42304aSGreg Roach /** 141ce42304aSGreg Roach * Fix a record 142ce42304aSGreg Roach * 143ce42304aSGreg Roach * @param GedcomRecord $record 144ce42304aSGreg Roach * @param array<string,string> $params 145ce42304aSGreg Roach * 146ce42304aSGreg Roach * @return void 147ce42304aSGreg Roach */ 14849528f2bSGreg Roach public function updateRecord(GedcomRecord $record, array $params): void 149ce42304aSGreg Roach { 150ce42304aSGreg Roach } 151ce42304aSGreg Roach 152ce42304aSGreg Roach /** 153ce42304aSGreg Roach * XREFs of family records that might need fixing. 154ce42304aSGreg Roach * 155ce42304aSGreg Roach * @param Tree $tree 156ce42304aSGreg Roach * @param array<string,string> $params 157ce42304aSGreg Roach * 15836779af1SGreg Roach * @return Collection<int,string>|null 159ce42304aSGreg Roach */ 16049528f2bSGreg Roach protected function familiesToFix(Tree $tree, array $params): ?Collection 161ce42304aSGreg Roach { 162ce42304aSGreg Roach return null; 163ce42304aSGreg Roach } 164ce42304aSGreg Roach 165ce42304aSGreg Roach /** 1667684867eSGreg Roach * @param Tree $tree 1677684867eSGreg Roach * @param array<string,string> $params 1687684867eSGreg Roach * 1697684867eSGreg Roach * @return Builder 1707684867eSGreg Roach */ 1717684867eSGreg Roach protected function familiesToFixQuery(Tree $tree, array $params): Builder 1727684867eSGreg Roach { 1737684867eSGreg Roach $query = DB::table('families') 1745062b1caSJonathan Jaubart ->where('f_file', '=', $tree->id()); 1757684867eSGreg Roach 1767684867eSGreg Roach if (isset($params['start'], $params['end'])) { 1775062b1caSJonathan Jaubart $query->whereBetween('f_id', [$params['start'], $params['end']]); 1787684867eSGreg Roach } 1797684867eSGreg Roach 1807684867eSGreg Roach return $query; 1817684867eSGreg Roach } 1827684867eSGreg Roach 1837684867eSGreg Roach /** 184ce42304aSGreg Roach * XREFs of individual records that might need fixing. 185ce42304aSGreg Roach * 186ce42304aSGreg Roach * @param Tree $tree 187ce42304aSGreg Roach * @param array<string,string> $params 188ce42304aSGreg Roach * 18936779af1SGreg Roach * @return Collection<int,string>|null 190ce42304aSGreg Roach */ 19149528f2bSGreg Roach protected function individualsToFix(Tree $tree, array $params): ?Collection 192ce42304aSGreg Roach { 193ce42304aSGreg Roach return null; 194ce42304aSGreg Roach } 195ce42304aSGreg Roach 196ce42304aSGreg Roach /** 1977684867eSGreg Roach * @param Tree $tree 1987684867eSGreg Roach * @param array<string,string> $params 1997684867eSGreg Roach * 2007684867eSGreg Roach * @return Builder 2017684867eSGreg Roach */ 2027684867eSGreg Roach protected function individualsToFixQuery(Tree $tree, array $params): Builder 2037684867eSGreg Roach { 2047684867eSGreg Roach $query = DB::table('individuals') 2057684867eSGreg Roach ->where('i_file', '=', $tree->id()); 2067684867eSGreg Roach 2077684867eSGreg Roach if (isset($params['start'], $params['end'])) { 2087684867eSGreg Roach $query->whereBetween('i_id', [$params['start'], $params['end']]); 2097684867eSGreg Roach } 2107684867eSGreg Roach 2117684867eSGreg Roach return $query; 2127684867eSGreg Roach } 2137684867eSGreg Roach 2147684867eSGreg Roach /** 2157684867eSGreg Roach * XREFs of location records that might need fixing. 2167684867eSGreg Roach * 2177684867eSGreg Roach * @param Tree $tree 2187684867eSGreg Roach * @param array<string,string> $params 2197684867eSGreg Roach * 22036779af1SGreg Roach * @return Collection<int,string>|null 2217684867eSGreg Roach */ 22249528f2bSGreg Roach protected function locationsToFix(Tree $tree, array $params): ?Collection 2237684867eSGreg Roach { 2247684867eSGreg Roach return null; 2257684867eSGreg Roach } 2267684867eSGreg Roach 2277684867eSGreg Roach /** 2287684867eSGreg Roach * @param Tree $tree 2297684867eSGreg Roach * @param array<string,string> $params 2307684867eSGreg Roach * 2317684867eSGreg Roach * @return Builder 2327684867eSGreg Roach */ 2337684867eSGreg Roach protected function locationsToFixQuery(Tree $tree, array $params): Builder 2347684867eSGreg Roach { 2357684867eSGreg Roach $query = DB::table('other') 2367684867eSGreg Roach ->where('o_type', '=', Location::RECORD_TYPE) 2377684867eSGreg Roach ->where('o_file', '=', $tree->id()); 2387684867eSGreg Roach 2397684867eSGreg Roach if (isset($params['start'], $params['end'])) { 2407684867eSGreg Roach $query->whereBetween('o_id', [$params['start'], $params['end']]); 2417684867eSGreg Roach } 2427684867eSGreg Roach 2437684867eSGreg Roach return $query; 2447684867eSGreg Roach } 2457684867eSGreg Roach 2467684867eSGreg Roach /** 247ce42304aSGreg Roach * XREFs of media records that might need fixing. 248ce42304aSGreg Roach * 249ce42304aSGreg Roach * @param Tree $tree 250ce42304aSGreg Roach * @param array<string,string> $params 251ce42304aSGreg Roach * 25236779af1SGreg Roach * @return Collection<int,string>|null 253ce42304aSGreg Roach */ 25449528f2bSGreg Roach protected function mediaToFix(Tree $tree, array $params): ?Collection 255ce42304aSGreg Roach { 256ce42304aSGreg Roach return null; 257ce42304aSGreg Roach } 258ce42304aSGreg Roach 259ce42304aSGreg Roach /** 2607684867eSGreg Roach * @param Tree $tree 2617684867eSGreg Roach * @param array<string,string> $params 2627684867eSGreg Roach * 2637684867eSGreg Roach * @return Builder 2647684867eSGreg Roach */ 2657684867eSGreg Roach protected function mediaToFixQuery(Tree $tree, array $params): Builder 2667684867eSGreg Roach { 2677684867eSGreg Roach $query = DB::table('media') 2687684867eSGreg Roach ->where('m_file', '=', $tree->id()); 2697684867eSGreg Roach 2707684867eSGreg Roach if (isset($params['start'], $params['end'])) { 2717684867eSGreg Roach $query->whereBetween('m_id', [$params['start'], $params['end']]); 2727684867eSGreg Roach } 2737684867eSGreg Roach 2747684867eSGreg Roach return $query; 2757684867eSGreg Roach } 2767684867eSGreg Roach 2777684867eSGreg Roach /** 278ce42304aSGreg Roach * XREFs of note records that might need fixing. 279ce42304aSGreg Roach * 280ce42304aSGreg Roach * @param Tree $tree 281ce42304aSGreg Roach * @param array<string,string> $params 282ce42304aSGreg Roach * 28336779af1SGreg Roach * @return Collection<int,string>|null 284ce42304aSGreg Roach */ 28549528f2bSGreg Roach protected function notesToFix(Tree $tree, array $params): ?Collection 286ce42304aSGreg Roach { 287ce42304aSGreg Roach return null; 288ce42304aSGreg Roach } 289ce42304aSGreg Roach 290ce42304aSGreg Roach /** 2917684867eSGreg Roach * @param Tree $tree 2927684867eSGreg Roach * @param array<string,string> $params 2937684867eSGreg Roach * 2947684867eSGreg Roach * @return Builder 2957684867eSGreg Roach */ 2967684867eSGreg Roach protected function notesToFixQuery(Tree $tree, array $params): Builder 2977684867eSGreg Roach { 2987684867eSGreg Roach $query = DB::table('other') 2997684867eSGreg Roach ->where('o_type', '=', Note::RECORD_TYPE) 3007684867eSGreg Roach ->where('o_file', '=', $tree->id()); 3017684867eSGreg Roach 3027684867eSGreg Roach if (isset($params['start'], $params['end'])) { 3037684867eSGreg Roach $query->whereBetween('o_id', [$params['start'], $params['end']]); 3047684867eSGreg Roach } 3057684867eSGreg Roach 3067684867eSGreg Roach return $query; 3077684867eSGreg Roach } 3087684867eSGreg Roach 3097684867eSGreg Roach /** 310ce42304aSGreg Roach * XREFs of repository records that might need fixing. 311ce42304aSGreg Roach * 312ce42304aSGreg Roach * @param Tree $tree 313ce42304aSGreg Roach * @param array<string,string> $params 314ce42304aSGreg Roach * 31536779af1SGreg Roach * @return Collection<int,string>|null 316ce42304aSGreg Roach */ 31749528f2bSGreg Roach protected function repositoriesToFix(Tree $tree, array $params): ?Collection 318ce42304aSGreg Roach { 319ce42304aSGreg Roach return null; 320ce42304aSGreg Roach } 321ce42304aSGreg Roach 322ce42304aSGreg Roach /** 3237684867eSGreg Roach * @param Tree $tree 3247684867eSGreg Roach * @param array<string,string> $params 3257684867eSGreg Roach * 3267684867eSGreg Roach * @return Builder 3277684867eSGreg Roach */ 3287684867eSGreg Roach protected function repositoriesToFixQuery(Tree $tree, array $params): Builder 3297684867eSGreg Roach { 3307684867eSGreg Roach $query = DB::table('other') 3317684867eSGreg Roach ->where('o_type', '=', Repository::RECORD_TYPE) 3327684867eSGreg Roach ->where('o_file', '=', $tree->id()); 3337684867eSGreg Roach 3347684867eSGreg Roach if (isset($params['start'], $params['end'])) { 3357684867eSGreg Roach $query->whereBetween('o_id', [$params['start'], $params['end']]); 3367684867eSGreg Roach } 3377684867eSGreg Roach 3387684867eSGreg Roach return $query; 3397684867eSGreg Roach } 3407684867eSGreg Roach 3417684867eSGreg Roach /** 342ce42304aSGreg Roach * XREFs of source records that might need fixing. 343ce42304aSGreg Roach * 344ce42304aSGreg Roach * @param Tree $tree 345ce42304aSGreg Roach * @param array<string,string> $params 346ce42304aSGreg Roach * 34736779af1SGreg Roach * @return Collection<int,string>|null 348ce42304aSGreg Roach */ 34949528f2bSGreg Roach protected function sourcesToFix(Tree $tree, array $params): ?Collection 350ce42304aSGreg Roach { 351ce42304aSGreg Roach return null; 352ce42304aSGreg Roach } 353ce42304aSGreg Roach 354ce42304aSGreg Roach /** 3557684867eSGreg Roach * @param Tree $tree 3567684867eSGreg Roach * @param array<string,string> $params 3577684867eSGreg Roach * 3587684867eSGreg Roach * @return Builder 3597684867eSGreg Roach */ 3607684867eSGreg Roach protected function sourcesToFixQuery(Tree $tree, array $params): Builder 3617684867eSGreg Roach { 3627684867eSGreg Roach $query = DB::table('sources') 3637684867eSGreg Roach ->where('s_file', '=', $tree->id()); 3647684867eSGreg Roach 3657684867eSGreg Roach if (isset($params['start'], $params['end'])) { 3667684867eSGreg Roach $query->whereBetween('s_id', [$params['start'], $params['end']]); 3677684867eSGreg Roach } 3687684867eSGreg Roach 3697684867eSGreg Roach return $query; 3707684867eSGreg Roach } 3717684867eSGreg Roach 3727684867eSGreg Roach /** 373ce42304aSGreg Roach * XREFs of submitter records that might need fixing. 374ce42304aSGreg Roach * 375ce42304aSGreg Roach * @param Tree $tree 376ce42304aSGreg Roach * @param array<string,string> $params 377ce42304aSGreg Roach * 37836779af1SGreg Roach * @return Collection<int,string>|null 379ce42304aSGreg Roach */ 38049528f2bSGreg Roach protected function submittersToFix(Tree $tree, array $params): ?Collection 381ce42304aSGreg Roach { 382ce42304aSGreg Roach return null; 383ce42304aSGreg Roach } 384ce42304aSGreg Roach 385ce42304aSGreg Roach /** 3867684867eSGreg Roach * @param Tree $tree 3877684867eSGreg Roach * @param array<string,string> $params 3887684867eSGreg Roach * 3897684867eSGreg Roach * @return Builder 3907684867eSGreg Roach */ 3917684867eSGreg Roach protected function submittersToFixQuery(Tree $tree, array $params): Builder 3927684867eSGreg Roach { 3937684867eSGreg Roach $query = DB::table('other') 3947684867eSGreg Roach ->where('o_type', '=', Submitter::RECORD_TYPE) 3957684867eSGreg Roach ->where('o_file', '=', $tree->id()); 3967684867eSGreg Roach 3977684867eSGreg Roach if (isset($params['start'], $params['end'])) { 3987684867eSGreg Roach $query->whereBetween('o_id', [$params['start'], $params['end']]); 3997684867eSGreg Roach } 4007684867eSGreg Roach 4017684867eSGreg Roach return $query; 4027684867eSGreg Roach } 4037684867eSGreg Roach 4047684867eSGreg Roach /** 405ce42304aSGreg Roach * Merge pending changes of a given type. We need to check all pending records. 406ce42304aSGreg Roach * 40736779af1SGreg Roach * @param Collection<int,string> $records 408ce42304aSGreg Roach * @param Tree $tree 409ce42304aSGreg Roach * @param string $type 410ce42304aSGreg Roach * 411e93aa0bdSGreg Roach * @return Collection<int,object> 412ce42304aSGreg Roach */ 413ce42304aSGreg Roach private function mergePendingRecords(Collection $records, Tree $tree, string $type): Collection 414ce42304aSGreg Roach { 415ce42304aSGreg Roach $pending = DB::table('change') 416ce42304aSGreg Roach ->where('gedcom_id', '=', $tree->id()) 417ce42304aSGreg Roach ->where('status', '=', 'pending') 418ce42304aSGreg Roach ->where(static function (Builder $query) use ($type): void { 419ce42304aSGreg Roach $query 420e93aa0bdSGreg Roach ->where('old_gedcom', 'LIKE', '%@ ' . $type . '\n%') 421e93aa0bdSGreg Roach ->orWhere('new_gedcom', 'LIKE', '%@ ' . $type . '\n%'); 422ce42304aSGreg Roach }) 423ce42304aSGreg Roach ->pluck('xref'); 424ce42304aSGreg Roach 425ce42304aSGreg Roach return $records 426ce42304aSGreg Roach ->concat($pending) 427f70bcff5SGreg Roach ->map(static function (string $xref) use ($type): object { 428ce42304aSGreg Roach return (object) ['xref' => $xref, 'type' => $type]; 429ce42304aSGreg Roach }); 430ce42304aSGreg Roach } 431ce42304aSGreg Roach} 432