diff options
| -rw-r--r-- | app/Contracts/IdFactoryInterface.php | 52 | ||||
| -rw-r--r-- | app/Elements/EventsRecorded.php | 3 | ||||
| -rw-r--r-- | app/Elements/NoteStructure.php | 2 | ||||
| -rw-r--r-- | app/Elements/PafUid.php | 34 | ||||
| -rw-r--r-- | app/Elements/Uid.php | 17 | ||||
| -rw-r--r-- | app/Factories/IdFactory.php | 99 | ||||
| -rw-r--r-- | app/Registry.php | 19 | ||||
| -rw-r--r-- | app/Services/CaptchaService.php | 8 | ||||
| -rw-r--r-- | app/Services/UpgradeService.php | 3 | ||||
| -rw-r--r-- | app/Webtrees.php | 2 | ||||
| -rw-r--r-- | resources/views/chart-box.phtml | 4 | ||||
| -rw-r--r-- | resources/views/edit/edit-gedcom-fields.phtml | 3 | ||||
| -rw-r--r-- | resources/views/fact-gedcom-fields.phtml | 3 | ||||
| -rw-r--r-- | resources/views/lists/families-table.phtml | 3 | ||||
| -rw-r--r-- | resources/views/lists/individuals-table.phtml | 3 |
15 files changed, 193 insertions, 62 deletions
diff --git a/app/Contracts/IdFactoryInterface.php b/app/Contracts/IdFactoryInterface.php new file mode 100644 index 0000000000..7551ffe808 --- /dev/null +++ b/app/Contracts/IdFactoryInterface.php @@ -0,0 +1,52 @@ +<?php + +/** + * webtrees: online genealogy + * Copyright (C) 2022 webtrees development team + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +declare(strict_types=1); + +namespace Fisharebest\Webtrees\Contracts; + +/** + * Create a unique identifier. + */ +interface IdFactoryInterface +{ + /** + * @return string + */ + public function uuid(): string; + + /** + * An identifier for use in CSS/HTML + * + * @return string + */ + public function id(string $prefix = 'id-'): string; + + /** + * A value for _UID fields, as created by PAF + * + * @return string + */ + public function pafUid(): string; + + /** + * @param string $uid - exactly 32 hex characters + * + * @return string + */ + public function pafUidChecksum(string $uid): string; +} diff --git a/app/Elements/EventsRecorded.php b/app/Elements/EventsRecorded.php index d897fb3c77..b1bab54d08 100644 --- a/app/Elements/EventsRecorded.php +++ b/app/Elements/EventsRecorded.php @@ -23,7 +23,6 @@ use Fisharebest\Webtrees\I18N; use Fisharebest\Webtrees\Registry; use Fisharebest\Webtrees\Tree; use Illuminate\Support\Collection; -use Ramsey\Uuid\Uuid; use function array_map; use function explode; @@ -132,7 +131,7 @@ class EventsRecorded extends AbstractElement ->sort() ->all(); - $id2 = Uuid::uuid4()->toString(); + $id2 = Registry::idFactory()->id(); // Our form element name contains "[]", and multiple selections would create multiple values. $hidden = '<input type="hidden" id="' . e($id) . '" name="' . e($name) . '" value="' . e($value) . '" />'; diff --git a/app/Elements/NoteStructure.php b/app/Elements/NoteStructure.php index 21026105e7..c22342d123 100644 --- a/app/Elements/NoteStructure.php +++ b/app/Elements/NoteStructure.php @@ -107,7 +107,7 @@ class NoteStructure extends SubmitterText */ public function labelValue(string $value, Tree $tree): string { - $id = 'collapse-' . Uuid::uuid4()->toString(); + $id = Registry::idFactory()->id(); $expanded = $tree->getPreference('EXPAND_NOTES') === '1'; // A note structure can contain an inline note or a linked to a shared note. diff --git a/app/Elements/PafUid.php b/app/Elements/PafUid.php index c7330a1046..5dfb2b0946 100644 --- a/app/Elements/PafUid.php +++ b/app/Elements/PafUid.php @@ -19,15 +19,8 @@ declare(strict_types=1); namespace Fisharebest\Webtrees\Elements; +use Fisharebest\Webtrees\Registry; use Fisharebest\Webtrees\Tree; -use Ramsey\Uuid\Exception\RandomSourceException; -use Ramsey\Uuid\Uuid; - -use function dechex; -use function hexdec; -use function strtoupper; -use function strtr; -use function substr; /** * _UID fields, as created by PAF and other applications @@ -45,29 +38,10 @@ class PafUid extends AbstractElement */ public function default(Tree $tree): string { - if ($tree->getPreference('GENERATE_UIDS') !== '1') { - return ''; - } - - try { - $uid = strtr(Uuid::uuid4()->toString(), ['-' => '']); - } catch (RandomSourceException $ex) { - // uuid4() can fail if there is insufficient entropy in the system. - return ''; + if ($tree->getPreference('GENERATE_UIDS') === '1') { + return Registry::idFactory()->pafUid(); } - $checksum_a = 0; // a sum of the bytes - $checksum_b = 0; // a sum of the incremental values of $checksum_a - - // Compute checksums - for ($i = 0; $i < 32; $i += 2) { - $checksum_a += hexdec(substr($uid, $i, 2)); - $checksum_b += $checksum_a & 0xff; - } - - $uid .= substr('0' . dechex($checksum_a), -2); - $uid .= substr('0' . dechex($checksum_b), -2); - - return strtoupper($uid); + return ''; } } diff --git a/app/Elements/Uid.php b/app/Elements/Uid.php index 5f5d9329ff..0c7c2ac2ed 100644 --- a/app/Elements/Uid.php +++ b/app/Elements/Uid.php @@ -19,16 +19,8 @@ declare(strict_types=1); namespace Fisharebest\Webtrees\Elements; +use Fisharebest\Webtrees\Registry; use Fisharebest\Webtrees\Tree; -use Ramsey\Uuid\Exception\RandomSourceException; -use Ramsey\Uuid\Uuid; - -use function dechex; -use function hexdec; -use function strtolower; -use function strtoupper; -use function strtr; -use function substr; /** * UID fields @@ -46,11 +38,8 @@ class Uid extends AbstractElement */ public function default(Tree $tree): string { - try { - if ($tree->getPreference('GENERATE_UIDS') === '1') { - return strtolower(Uuid::uuid4()->toString()); - } - } catch (RandomSourceException $ex) { + if ($tree->getPreference('GENERATE_UIDS') === '1') { + return Registry::idFactory()->uuid(); } return ''; diff --git a/app/Factories/IdFactory.php b/app/Factories/IdFactory.php new file mode 100644 index 0000000000..cfba19673c --- /dev/null +++ b/app/Factories/IdFactory.php @@ -0,0 +1,99 @@ +<?php + +/** + * webtrees: online genealogy + * Copyright (C) 2022 webtrees development team + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +declare(strict_types=1); + +namespace Fisharebest\Webtrees\Factories; + +use Fisharebest\Webtrees\Contracts\IdFactoryInterface; + +use Ramsey\Uuid\Exception\RandomSourceException; +use Ramsey\Uuid\Uuid; + +use function dechex; +use function hexdec; +use function str_pad; +use function strtoupper; +use function substr; + +use const STR_PAD_LEFT; + +/** + * Create a unique identifier. + */ +class IdFactory implements IdFactoryInterface +{ + /** + * @return string + */ + public function uuid(): string + { + try { + return strtolower(strtr(Uuid::uuid4()->toString(), ['-' => ''])); + } catch (RandomSourceException $ex) { + // uuid4() can fail if there is insufficient entropy in the system. + return ''; + } + } + + /** + * An identifier for use in CSS/HTML + * + * @return string + */ + public function id(string $prefix = 'id-'): string + { + return $prefix . $this->uuid(); + } + + /** + * A value for _UID fields, as created by PAF + * + * @return string + */ + public function pafUid(): string + { + $uid = strtoupper(strtr($this->uuid(), ['-' => ''])); + + if ($uid === '') { + return ''; + } + + return $uid . $this->pafUidChecksum($uid); + } + + /** + * @param string $uid - exactly 32 hex characters + * + * @return string + */ + public function pafUidChecksum(string $uid): string + { + $checksum_a = 0; // a sum of the bytes + $checksum_b = 0; // a sum of the incremental values of $checksum_a + + for ($i = 0; $i < 32; $i += 2) { + $checksum_a += hexdec(substr($uid, $i, 2)); + $checksum_b += $checksum_a & 0xff; + } + + $digit1 = str_pad(dechex($checksum_a), 2, '0', STR_PAD_LEFT); + $digit2 = str_pad(dechex($checksum_b), 2, '0', STR_PAD_LEFT); + + return strtoupper($digit1 . $digit2); + } +} diff --git a/app/Registry.php b/app/Registry.php index 3a8ea31bb0..4f24ea9c32 100644 --- a/app/Registry.php +++ b/app/Registry.php @@ -43,6 +43,7 @@ use Fisharebest\Webtrees\Contracts\SubmissionFactoryInterface; use Fisharebest\Webtrees\Contracts\SubmitterFactoryInterface; use Fisharebest\Webtrees\Contracts\SurnameTraditionFactoryInterface; use Fisharebest\Webtrees\Contracts\TimestampFactoryInterface; +use Fisharebest\Webtrees\Contracts\IdFactoryInterface; use Fisharebest\Webtrees\Contracts\XrefFactoryInterface; /** @@ -66,6 +67,8 @@ class Registry private static HeaderFactoryInterface $header_factory; + private static IdFactoryInterface $id_factory; + private static ImageFactoryInterface $image_factory; private static IndividualFactoryInterface $individual_factory; @@ -231,6 +234,22 @@ class Registry /** * Store or retrieve a factory object. * + * @param IdFactoryInterface|null $factory + * + * @return IdFactoryInterface + */ + public static function idFactory(IdFactoryInterface $factory = null): IdFactoryInterface + { + if ($factory instanceof IdFactoryInterface) { + self::$id_factory = $factory; + } + + return self::$id_factory; + } + + /** + * Store or retrieve a factory object. + * * @param ImageFactoryInterface|null $factory * * @return ImageFactoryInterface diff --git a/app/Services/CaptchaService.php b/app/Services/CaptchaService.php index ef04ed3ced..ae0d78ea2c 100644 --- a/app/Services/CaptchaService.php +++ b/app/Services/CaptchaService.php @@ -19,10 +19,10 @@ declare(strict_types=1); namespace Fisharebest\Webtrees\Services; +use Fisharebest\Webtrees\Registry; use Fisharebest\Webtrees\Session; use Fisharebest\Webtrees\Validator; use Psr\Http\Message\ServerRequestInterface; -use Ramsey\Uuid\Uuid; use function view; @@ -41,9 +41,9 @@ class CaptchaService */ public function createCaptcha(): string { - $x = Uuid::uuid4()->toString(); - $y = Uuid::uuid4()->toString(); - $z = Uuid::uuid4()->toString(); + $x = Registry::idFactory()->uuid(); + $y = Registry::idFactory()->uuid(); + $z = Registry::idFactory()->uuid(); Session::put('captcha-t', microtime(true)); Session::put('captcha-x', $x); diff --git a/app/Services/UpgradeService.php b/app/Services/UpgradeService.php index 86168a8abb..fa94782571 100644 --- a/app/Services/UpgradeService.php +++ b/app/Services/UpgradeService.php @@ -22,6 +22,7 @@ namespace Fisharebest\Webtrees\Services; use Fig\Http\Message\StatusCodeInterface; use Fisharebest\Webtrees\Http\Exceptions\HttpServerErrorException; use Fisharebest\Webtrees\I18N; +use Fisharebest\Webtrees\Registry; use Fisharebest\Webtrees\Site; use Fisharebest\Webtrees\Webtrees; use GuzzleHttp\Client; @@ -350,7 +351,7 @@ class UpgradeService $site_uuid = Site::getPreference('SITE_UUID'); if ($site_uuid === '') { - $site_uuid = Uuid::uuid4()->toString(); + $site_uuid = Registry::idFactory()->uuid(); Site::setPreference('SITE_UUID', $site_uuid); } diff --git a/app/Webtrees.php b/app/Webtrees.php index a812783f0b..e8cf51551a 100644 --- a/app/Webtrees.php +++ b/app/Webtrees.php @@ -45,6 +45,7 @@ use Fisharebest\Webtrees\Factories\SubmissionFactory; use Fisharebest\Webtrees\Factories\SubmitterFactory; use Fisharebest\Webtrees\Factories\SurnameTraditionFactory; use Fisharebest\Webtrees\Factories\TimestampFactory; +use Fisharebest\Webtrees\Factories\IdFactory; use Fisharebest\Webtrees\Factories\XrefFactory; use Fisharebest\Webtrees\GedcomFilters\GedcomEncodingFilter; use Fisharebest\Webtrees\Http\Middleware\BadBotBlocker; @@ -204,6 +205,7 @@ class Webtrees Registry::filesystem(new FilesystemFactory()); Registry::gedcomRecordFactory(new GedcomRecordFactory()); Registry::headerFactory(new HeaderFactory()); + Registry::idFactory(new IdFactory()); Registry::imageFactory(new ImageFactory()); Registry::individualFactory(new IndividualFactory()); Registry::locationFactory(new LocationFactory()); diff --git a/resources/views/chart-box.phtml b/resources/views/chart-box.phtml index e53cae5a02..b47fbe9e2e 100644 --- a/resources/views/chart-box.phtml +++ b/resources/views/chart-box.phtml @@ -9,9 +9,9 @@ use Fisharebest\Webtrees\I18N; use Fisharebest\Webtrees\Individual; use Fisharebest\Webtrees\Menu; use Fisharebest\Webtrees\Module\ModuleChartInterface; +use Fisharebest\Webtrees\Registry; use Fisharebest\Webtrees\Services\ModuleService; use Illuminate\Support\Collection; -use Ramsey\Uuid\Uuid; /** * @var Individual|null $individual @@ -94,7 +94,7 @@ $all_facts = $all_facts->filter(static function (Fact $fact) use ($exclude): boo $all_facts = Fact::sortFacts($all_facts); -$id = Uuid::uuid4()->toString(); +$id = Registry::idFactory()->id(); ?> <div class="wt-chart-box wt-chart-box-<?= strtolower($individual->sex()) ?> <?= $individual->isPendingAddition() ? 'wt-new' : '' ?> <?= $individual->isPendingDeletion() ? 'wt-old' : '' ?> overflow-hidden" data-wt-chart-xref="<?= e($individual->xref()) ?>" data-tree="<?= e($individual->tree()->name()) ?>"> diff --git a/resources/views/edit/edit-gedcom-fields.phtml b/resources/views/edit/edit-gedcom-fields.phtml index b60669f315..cc5507cfd0 100644 --- a/resources/views/edit/edit-gedcom-fields.phtml +++ b/resources/views/edit/edit-gedcom-fields.phtml @@ -3,7 +3,6 @@ use Fisharebest\Webtrees\Elements\EmptyElement; use Fisharebest\Webtrees\Registry; use Fisharebest\Webtrees\Tree; -use Ramsey\Uuid\Uuid; /** * @var string $gedcom @@ -25,7 +24,7 @@ foreach ($keys as $num => $key) { $hierarchy[$levels[$key]] = $tags[$key]; $full_tag = implode(':', array_slice($hierarchy, 0, 1 + $levels[$key])); $elements[$key] = Registry::elementFactory()->make($full_tag); - $ids[$key] = Uuid::uuid4()->toString() . '-' . $full_tag; + $ids[$key] = Registry::idFactory()->id() . '-' . $full_tag; // Does this element have any children? $has_subtags = ($levels[$key + 1] ?? 0) > $levels[$key]; diff --git a/resources/views/fact-gedcom-fields.phtml b/resources/views/fact-gedcom-fields.phtml index 9e7041dec8..9e3c0e2fd9 100644 --- a/resources/views/fact-gedcom-fields.phtml +++ b/resources/views/fact-gedcom-fields.phtml @@ -8,7 +8,6 @@ use Fisharebest\Webtrees\I18N; use Fisharebest\Webtrees\Registry; use Fisharebest\Webtrees\Source; use Fisharebest\Webtrees\Tree; -use Ramsey\Uuid\Uuid; /** * @var string $gedcom @@ -54,7 +53,7 @@ foreach ($keys as $key) { <?php if ($elements[0] instanceof XrefSource && preg_match('/^@(' . Gedcom::REGEX_XREF . ')@$/', $values[0], $match) === 1) : ?> <?php $source = Registry::sourceFactory()->make($match[1], $tree) ?> <?php if ($source instanceof Source) : ?> - <?php $id = 'collapse-' . Uuid::uuid4()->toString() ?> + <?php $id = Registry::idFactory()->id() ?> <?php $expanded = $tree->getPreference('EXPAND_SOURCES') === '1' ?> <div class="fact_SOUR"> <a href="#<?= e($id) ?>" role="button" data-bs-toggle="collapse" aria-controls="<?= e($id) ?>" aria-expanded="<?= $expanded ? 'true' : 'false' ?>"> diff --git a/resources/views/lists/families-table.phtml b/resources/views/lists/families-table.phtml index 23e8732050..2913a1122c 100644 --- a/resources/views/lists/families-table.phtml +++ b/resources/views/lists/families-table.phtml @@ -11,9 +11,8 @@ use Fisharebest\Webtrees\I18N; use Fisharebest\Webtrees\Tree; use Fisharebest\Webtrees\View; use Illuminate\Support\Collection; -use Ramsey\Uuid\Uuid; -$table_id = 'table-fam-' . Uuid::uuid4()->toString(); // lists requires a unique ID in case there are multiple lists per page +$table_id = Registry::idFactory()->id(); // lists requires a unique ID in case there are multiple lists per page $today = new Date(strtoupper(date('d M Y'))); $today_jd = Registry::timestampFactory()->now()->julianDay(); diff --git a/resources/views/lists/individuals-table.phtml b/resources/views/lists/individuals-table.phtml index 2a1a371a04..0da8b51d5d 100644 --- a/resources/views/lists/individuals-table.phtml +++ b/resources/views/lists/individuals-table.phtml @@ -15,7 +15,6 @@ use Fisharebest\Webtrees\Services\ModuleService; use Fisharebest\Webtrees\Tree; use Fisharebest\Webtrees\View; use Illuminate\Support\Collection; -use Ramsey\Uuid\Uuid; /** * @var Collection<int,Individual> $individuals @@ -24,7 +23,7 @@ use Ramsey\Uuid\Uuid; */ // lists requires a unique ID in case there are multiple lists per page -$table_id = 'table-indi-' . Uuid::uuid4()->toString(); +$table_id = Registry::idFactory()->id(); $today_jd = Registry::timestampFactory()->now()->julianDay(); $hundred_years_ago = Registry::timestampFactory()->now()->subtractYears(100)->julianDay(); |
