18add1155SRico Sonntag<?php 23976b470SGreg Roach 38add1155SRico Sonntag/** 48add1155SRico Sonntag * webtrees: online genealogy 5d11be702SGreg Roach * Copyright (C) 2023 webtrees development team 68add1155SRico Sonntag * This program is free software: you can redistribute it and/or modify 78add1155SRico Sonntag * it under the terms of the GNU General Public License as published by 88add1155SRico Sonntag * the Free Software Foundation, either version 3 of the License, or 98add1155SRico Sonntag * (at your option) any later version. 108add1155SRico Sonntag * This program is distributed in the hope that it will be useful, 118add1155SRico Sonntag * but WITHOUT ANY WARRANTY; without even the implied warranty of 128add1155SRico Sonntag * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 138add1155SRico Sonntag * GNU General Public License for more details. 148add1155SRico Sonntag * 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/>. 168add1155SRico Sonntag */ 17fcfa147eSGreg Roach 188add1155SRico Sonntagdeclare(strict_types=1); 198add1155SRico Sonntag 208add1155SRico Sonntagnamespace Fisharebest\Webtrees\Statistics\Repository; 218add1155SRico Sonntag 220d047a8cSGreg Roachuse Exception; 236f4ec3caSGreg Roachuse Fisharebest\Webtrees\DB; 24820b62dfSGreg Roachuse Fisharebest\Webtrees\Fact; 251635452cSGreg Roachuse Fisharebest\Webtrees\Header; 26f0c88a96SGreg Roachuse Fisharebest\Webtrees\Registry; 278add1155SRico Sonntaguse Fisharebest\Webtrees\Statistics\Repository\Interfaces\GedcomRepositoryInterface; 288add1155SRico Sonntaguse Fisharebest\Webtrees\Tree; 29*dd750643SGreg Roachuse InvalidArgumentException; 30*dd750643SGreg Roach 314c78e066SGreg Roachuse function e; 32dec352c1SGreg Roachuse function str_contains; 334c78e066SGreg Roachuse function strpos; 344c78e066SGreg Roachuse function substr; 35dec352c1SGreg Roach 368add1155SRico Sonntag/** 378add1155SRico Sonntag * A repository providing methods for GEDCOM related statistics. 388add1155SRico Sonntag */ 398add1155SRico Sonntagclass GedcomRepository implements GedcomRepositoryInterface 408add1155SRico Sonntag{ 414c78e066SGreg Roach private Tree $tree; 428add1155SRico Sonntag 438add1155SRico Sonntag /** 448add1155SRico Sonntag * @param Tree $tree 458add1155SRico Sonntag */ 468add1155SRico Sonntag public function __construct(Tree $tree) 478add1155SRico Sonntag { 488add1155SRico Sonntag $this->tree = $tree; 498add1155SRico Sonntag } 508add1155SRico Sonntag 518add1155SRico Sonntag /** 528add1155SRico Sonntag * Get information from the GEDCOM's HEAD record. 538add1155SRico Sonntag * 5424f2a3afSGreg Roach * @return array<string> 558add1155SRico Sonntag */ 568add1155SRico Sonntag private function gedcomHead(): array 578add1155SRico Sonntag { 588add1155SRico Sonntag $title = ''; 598add1155SRico Sonntag $version = ''; 608add1155SRico Sonntag $source = ''; 618add1155SRico Sonntag 626b9cb339SGreg Roach $head = Registry::headerFactory()->make('HEAD', $this->tree); 638add1155SRico Sonntag 641635452cSGreg Roach if ($head instanceof Header) { 65820b62dfSGreg Roach $sour = $head->facts(['SOUR'])->first(); 668add1155SRico Sonntag 67820b62dfSGreg Roach if ($sour instanceof Fact) { 688add1155SRico Sonntag $source = $sour->value(); 698add1155SRico Sonntag $title = $sour->attribute('NAME'); 708add1155SRico Sonntag $version = $sour->attribute('VERS'); 718add1155SRico Sonntag } 728add1155SRico Sonntag } 738add1155SRico Sonntag 748add1155SRico Sonntag return [ 758add1155SRico Sonntag $title, 768add1155SRico Sonntag $version, 778add1155SRico Sonntag $source, 788add1155SRico Sonntag ]; 798add1155SRico Sonntag } 808add1155SRico Sonntag 818add1155SRico Sonntag /** 820dcd9387SGreg Roach * @return string 838add1155SRico Sonntag */ 848add1155SRico Sonntag public function gedcomFilename(): string 858add1155SRico Sonntag { 868add1155SRico Sonntag return $this->tree->name(); 878add1155SRico Sonntag } 888add1155SRico Sonntag 898add1155SRico Sonntag /** 900dcd9387SGreg Roach * @return int 918add1155SRico Sonntag */ 928add1155SRico Sonntag public function gedcomId(): int 938add1155SRico Sonntag { 948add1155SRico Sonntag return $this->tree->id(); 958add1155SRico Sonntag } 968add1155SRico Sonntag 978add1155SRico Sonntag /** 980dcd9387SGreg Roach * @return string 998add1155SRico Sonntag */ 1008add1155SRico Sonntag public function gedcomTitle(): string 1018add1155SRico Sonntag { 1028add1155SRico Sonntag return e($this->tree->title()); 1038add1155SRico Sonntag } 1048add1155SRico Sonntag 1058add1155SRico Sonntag /** 1060dcd9387SGreg Roach * @return string 1078add1155SRico Sonntag */ 1088add1155SRico Sonntag public function gedcomCreatedSoftware(): string 1098add1155SRico Sonntag { 1108add1155SRico Sonntag return $this->gedcomHead()[0]; 1118add1155SRico Sonntag } 1128add1155SRico Sonntag 1138add1155SRico Sonntag /** 1140dcd9387SGreg Roach * @return string 1158add1155SRico Sonntag */ 1168add1155SRico Sonntag public function gedcomCreatedVersion(): string 1178add1155SRico Sonntag { 1188add1155SRico Sonntag $head = $this->gedcomHead(); 1198add1155SRico Sonntag 1208add1155SRico Sonntag // Fix broken version string in Family Tree Maker 121dec352c1SGreg Roach if (str_contains($head[1], 'Family Tree Maker ')) { 1228add1155SRico Sonntag $p = strpos($head[1], '(') + 1; 1238add1155SRico Sonntag $p2 = strpos($head[1], ')'); 1248add1155SRico Sonntag $head[1] = substr($head[1], $p, $p2 - $p); 1258add1155SRico Sonntag } 1268add1155SRico Sonntag 1278add1155SRico Sonntag // Fix EasyTree version 1288add1155SRico Sonntag if ($head[2] === 'EasyTree') { 1298add1155SRico Sonntag $head[1] = substr($head[1], 1); 1308add1155SRico Sonntag } 1318add1155SRico Sonntag 1328add1155SRico Sonntag return $head[1]; 1338add1155SRico Sonntag } 1348add1155SRico Sonntag 1358add1155SRico Sonntag /** 1360dcd9387SGreg Roach * @return string 1370d047a8cSGreg Roach * @throws Exception 1388add1155SRico Sonntag */ 1398add1155SRico Sonntag public function gedcomDate(): string 1408add1155SRico Sonntag { 1416b9cb339SGreg Roach $head = Registry::headerFactory()->make('HEAD', $this->tree); 1428add1155SRico Sonntag 1431635452cSGreg Roach if ($head instanceof Header) { 144820b62dfSGreg Roach $fact = $head->facts(['DATE'])->first(); 1458add1155SRico Sonntag 146820b62dfSGreg Roach if ($fact instanceof Fact) { 147*dd750643SGreg Roach try { 148d97083feSGreg Roach return Registry::timestampFactory()->fromString($fact->value(), 'j M Y')->isoFormat('LL'); 149*dd750643SGreg Roach } catch (InvalidArgumentException) { 150*dd750643SGreg Roach // HEAD:DATE invalid. 151*dd750643SGreg Roach } 1528add1155SRico Sonntag } 1538add1155SRico Sonntag } 1548add1155SRico Sonntag 1558add1155SRico Sonntag return ''; 1568add1155SRico Sonntag } 1578add1155SRico Sonntag 1588add1155SRico Sonntag /** 1590dcd9387SGreg Roach * @return string 1608add1155SRico Sonntag */ 1618add1155SRico Sonntag public function gedcomUpdated(): string 1628add1155SRico Sonntag { 1638fd23e11SGreg Roach $row = DB::table('change') 1648fd23e11SGreg Roach ->where('gedcom_id', '=', $this->tree->id()) 1658fd23e11SGreg Roach ->where('status', '=', 'accepted') 1668fd23e11SGreg Roach ->orderBy('change_id', 'DESC') 1678fd23e11SGreg Roach ->select(['change_time']) 1688fd23e11SGreg Roach ->first(); 1698fd23e11SGreg Roach 1708fd23e11SGreg Roach if ($row === null) { 1718fd23e11SGreg Roach return $this->gedcomDate(); 1728add1155SRico Sonntag } 1738add1155SRico Sonntag 174d97083feSGreg Roach return Registry::timestampFactory()->fromString($row->change_time)->isoFormat('LL'); 1758add1155SRico Sonntag } 1768add1155SRico Sonntag 1778add1155SRico Sonntag /** 1780dcd9387SGreg Roach * @return string 1798add1155SRico Sonntag */ 1808add1155SRico Sonntag public function gedcomRootId(): string 1818add1155SRico Sonntag { 1828add1155SRico Sonntag return $this->tree->getPreference('PEDIGREE_ROOT_ID'); 1838add1155SRico Sonntag } 1848add1155SRico Sonntag} 185