1<?php 2 3/** 4 * webtrees: online genealogy 5 * Copyright (C) 2023 webtrees development team 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation, either version 3 of the License, or 9 * (at your option) any later version. 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <https://www.gnu.org/licenses/>. 16 */ 17 18declare(strict_types=1); 19 20namespace Fisharebest\Webtrees\Statistics\Repository; 21 22use Exception; 23use Fisharebest\Webtrees\DB; 24use Fisharebest\Webtrees\Fact; 25use Fisharebest\Webtrees\Header; 26use Fisharebest\Webtrees\Registry; 27use Fisharebest\Webtrees\Statistics\Repository\Interfaces\GedcomRepositoryInterface; 28use Fisharebest\Webtrees\Tree; 29use InvalidArgumentException; 30 31use function e; 32use function str_contains; 33use function strpos; 34use function substr; 35 36/** 37 * A repository providing methods for GEDCOM related statistics. 38 */ 39class GedcomRepository implements GedcomRepositoryInterface 40{ 41 private Tree $tree; 42 43 /** 44 * @param Tree $tree 45 */ 46 public function __construct(Tree $tree) 47 { 48 $this->tree = $tree; 49 } 50 51 /** 52 * Get information from the GEDCOM's HEAD record. 53 * 54 * @return array<string> 55 */ 56 private function gedcomHead(): array 57 { 58 $title = ''; 59 $version = ''; 60 $source = ''; 61 62 $head = Registry::headerFactory()->make('HEAD', $this->tree); 63 64 if ($head instanceof Header) { 65 $sour = $head->facts(['SOUR'])->first(); 66 67 if ($sour instanceof Fact) { 68 $source = $sour->value(); 69 $title = $sour->attribute('NAME'); 70 $version = $sour->attribute('VERS'); 71 } 72 } 73 74 return [ 75 $title, 76 $version, 77 $source, 78 ]; 79 } 80 81 /** 82 * @return string 83 */ 84 public function gedcomFilename(): string 85 { 86 return $this->tree->name(); 87 } 88 89 /** 90 * @return int 91 */ 92 public function gedcomId(): int 93 { 94 return $this->tree->id(); 95 } 96 97 /** 98 * @return string 99 */ 100 public function gedcomTitle(): string 101 { 102 return e($this->tree->title()); 103 } 104 105 /** 106 * @return string 107 */ 108 public function gedcomCreatedSoftware(): string 109 { 110 return $this->gedcomHead()[0]; 111 } 112 113 /** 114 * @return string 115 */ 116 public function gedcomCreatedVersion(): string 117 { 118 $head = $this->gedcomHead(); 119 120 // Fix broken version string in Family Tree Maker 121 if (str_contains($head[1], 'Family Tree Maker ')) { 122 $p = strpos($head[1], '(') + 1; 123 $p2 = strpos($head[1], ')'); 124 $head[1] = substr($head[1], $p, $p2 - $p); 125 } 126 127 // Fix EasyTree version 128 if ($head[2] === 'EasyTree') { 129 $head[1] = substr($head[1], 1); 130 } 131 132 return $head[1]; 133 } 134 135 /** 136 * @return string 137 * @throws Exception 138 */ 139 public function gedcomDate(): string 140 { 141 $head = Registry::headerFactory()->make('HEAD', $this->tree); 142 143 if ($head instanceof Header) { 144 $fact = $head->facts(['DATE'])->first(); 145 146 if ($fact instanceof Fact) { 147 try { 148 return Registry::timestampFactory()->fromString($fact->value(), 'j M Y')->isoFormat('LL'); 149 } catch (InvalidArgumentException) { 150 // HEAD:DATE invalid. 151 } 152 } 153 } 154 155 return ''; 156 } 157 158 /** 159 * @return string 160 */ 161 public function gedcomUpdated(): string 162 { 163 $row = DB::table('change') 164 ->where('gedcom_id', '=', $this->tree->id()) 165 ->where('status', '=', 'accepted') 166 ->orderBy('change_id', 'DESC') 167 ->select(['change_time']) 168 ->first(); 169 170 if ($row === null) { 171 return $this->gedcomDate(); 172 } 173 174 return Registry::timestampFactory()->fromString($row->change_time)->isoFormat('LL'); 175 } 176 177 /** 178 * @return string 179 */ 180 public function gedcomRootId(): string 181 { 182 return $this->tree->getPreference('PEDIGREE_ROOT_ID'); 183 } 184} 185