xref: /webtrees/app/Module/FixPrimaryTag.php (revision 7413816e6dd2d50e569034fb804f3dce7471bb94)
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
22ce42304aSGreg Roachuse Fisharebest\Webtrees\Fact;
23ce42304aSGreg Roachuse Fisharebest\Webtrees\GedcomRecord;
24ce42304aSGreg Roachuse Fisharebest\Webtrees\I18N;
25ce42304aSGreg Roachuse Fisharebest\Webtrees\Individual;
264991f205SGreg Roachuse Fisharebest\Webtrees\Services\LinkedRecordService;
27ce42304aSGreg Roachuse Fisharebest\Webtrees\Tree;
28ce42304aSGreg Roachuse Illuminate\Support\Collection;
29ce42304aSGreg Roach
30ce42304aSGreg Roachuse function e;
31dec352c1SGreg Roachuse function str_contains;
32ce42304aSGreg Roachuse function strtoupper;
33ce42304aSGreg Roach
34ce42304aSGreg Roach/**
35ce42304aSGreg Roach * Class FixPrimaryTag
36ce42304aSGreg Roach */
37ce42304aSGreg Roachclass FixPrimaryTag extends AbstractModule implements ModuleDataFixInterface
38ce42304aSGreg Roach{
39ce42304aSGreg Roach    use ModuleDataFixTrait;
40ce42304aSGreg Roach
414991f205SGreg Roach    private LinkedRecordService $linked_record_service;
424991f205SGreg Roach
434991f205SGreg Roach    /**
444991f205SGreg Roach     * @param LinkedRecordService $linked_record_service
454991f205SGreg Roach     */
464991f205SGreg Roach    public function __construct(LinkedRecordService $linked_record_service)
474991f205SGreg Roach    {
484991f205SGreg Roach        $this->linked_record_service = $linked_record_service;
494991f205SGreg Roach    }
504991f205SGreg Roach
51ce42304aSGreg Roach    /**
52ce42304aSGreg Roach     * How should this module be identified in the control panel, etc.?
53ce42304aSGreg Roach     *
54ce42304aSGreg Roach     * @return string
55ce42304aSGreg Roach     */
56ce42304aSGreg Roach    public function title(): string
57ce42304aSGreg Roach    {
58ce42304aSGreg Roach        /* I18N: Name of a module */
59e93aa0bdSGreg Roach        return I18N::translate('Convert %s tags to GEDCOM 5.5.1', 'OBJE:_PRIM');
60ce42304aSGreg Roach    }
61ce42304aSGreg Roach
62ce42304aSGreg Roach    public function description(): string
63ce42304aSGreg Roach    {
64ce42304aSGreg Roach        /* I18N: Description of a “Data fix” module */
65ce42304aSGreg Roach        return I18N::translate('“Highlighted image” (_PRIM) tags are used by some genealogy applications to indicate the preferred image for an individual. An alternative is to re-order the images so that the preferred one is listed first.');
66ce42304aSGreg Roach    }
67ce42304aSGreg Roach
68ce42304aSGreg Roach    /**
69ce42304aSGreg Roach     * XREFs of media records that might need fixing.
70ce42304aSGreg Roach     *
71ce42304aSGreg Roach     * @param Tree                 $tree
72ce42304aSGreg Roach     * @param array<string,string> $params
73ce42304aSGreg Roach     *
7436779af1SGreg Roach     * @return Collection<int,string>
75ce42304aSGreg Roach     */
76ce42304aSGreg Roach    public function mediaToFix(Tree $tree, array $params): Collection
77ce42304aSGreg Roach    {
787684867eSGreg Roach        return $this->mediaToFixQuery($tree, $params)
79ce42304aSGreg Roach            ->where('m_file', '=', $tree->id())
80ce42304aSGreg Roach            ->where('m_gedcom', 'LIKE', "%\n1 _PRIM %")
81ce42304aSGreg Roach            ->pluck('m_id');
82ce42304aSGreg Roach    }
83ce42304aSGreg Roach
84ce42304aSGreg Roach    /**
85ce42304aSGreg Roach     * Does a record need updating?
86ce42304aSGreg Roach     *
87ce42304aSGreg Roach     * @param GedcomRecord         $record
88ce42304aSGreg Roach     * @param array<string,string> $params
89ce42304aSGreg Roach     *
90ce42304aSGreg Roach     * @return bool
91ce42304aSGreg Roach     */
92ce42304aSGreg Roach    public function doesRecordNeedUpdate(GedcomRecord $record, array $params): bool
93ce42304aSGreg Roach    {
94dec352c1SGreg Roach        return str_contains($record->gedcom(), "\n1 _PRIM ");
95ce42304aSGreg Roach    }
96ce42304aSGreg Roach
97ce42304aSGreg Roach    /**
98ce42304aSGreg Roach     * Show the changes we would make
99ce42304aSGreg Roach     *
100ce42304aSGreg Roach     * @param GedcomRecord         $record
101ce42304aSGreg Roach     * @param array<string,string> $params
102ce42304aSGreg Roach     *
103ce42304aSGreg Roach     * @return string
104ce42304aSGreg Roach     */
105ce42304aSGreg Roach    public function previewUpdate(GedcomRecord $record, array $params): string
106ce42304aSGreg Roach    {
107ce42304aSGreg Roach        $html = '';
108ce42304aSGreg Roach        foreach ($record->facts(['_PRIM']) as $prim) {
109ce42304aSGreg Roach            $html = '<p>' . I18N::translate('Delete') . ' – <code>' . e($prim->gedcom()) . '</code></p>';
110ce42304aSGreg Roach        }
111ce42304aSGreg Roach
112ce42304aSGreg Roach        $html .= '<ul>';
1134991f205SGreg Roach        foreach ($this->linked_record_service->linkedIndividuals($record) as $individual) {
114ce42304aSGreg Roach            $html .= '<li>' . I18N::translate('Re-order media') . ' – <a href="' . e($individual->url()) . '">' . $individual->fullName() . '</a></li>';
115ce42304aSGreg Roach        }
116ce42304aSGreg Roach        $html .= '</ul>';
117ce42304aSGreg Roach
118ce42304aSGreg Roach        return $html;
119ce42304aSGreg Roach    }
120ce42304aSGreg Roach
121ce42304aSGreg Roach    /**
122ce42304aSGreg Roach     * Fix a record
123ce42304aSGreg Roach     *
124ce42304aSGreg Roach     * @param GedcomRecord         $record
125ce42304aSGreg Roach     * @param array<string,string> $params
126ce42304aSGreg Roach     *
127ce42304aSGreg Roach     * @return void
128ce42304aSGreg Roach     */
129ce42304aSGreg Roach    public function updateRecord(GedcomRecord $record, array $params): void
130ce42304aSGreg Roach    {
131*f25fc0f9SGreg Roach        $facts = $record->facts(['_PRIM'])->filter(static fn (Fact $fact): bool => !$fact->isPendingDeletion());
132ce42304aSGreg Roach
133ce42304aSGreg Roach        foreach ($facts as $fact) {
134ce42304aSGreg Roach            $primary = strtoupper($fact->value()) !== 'N';
135ce42304aSGreg Roach
1364991f205SGreg Roach            foreach ($this->linked_record_service->linkedIndividuals($record) as $individual) {
137ce42304aSGreg Roach                $this->updateMediaLinks($individual, $record->xref(), $primary);
138ce42304aSGreg Roach            }
139ce42304aSGreg Roach
140ce42304aSGreg Roach            $record->deleteFact($fact->id(), false);
141ce42304aSGreg Roach        }
142ce42304aSGreg Roach    }
143ce42304aSGreg Roach
144ce42304aSGreg Roach    /**
145ce42304aSGreg Roach     * @param Individual $individual
146ce42304aSGreg Roach     * @param string     $xref
147ce42304aSGreg Roach     * @param bool       $primary
148ce42304aSGreg Roach     */
149ce42304aSGreg Roach    private function updateMediaLinks(Individual $individual, string $xref, bool $primary): void
150ce42304aSGreg Roach    {
151042e1af7SGreg Roach        $facts = $individual->facts([], false, null, true);
152ce42304aSGreg Roach
153ce42304aSGreg Roach        $facts1 = new Collection();
154ce42304aSGreg Roach        $facts2 = new Collection();
155ce42304aSGreg Roach        $facts3 = new Collection();
156ce42304aSGreg Roach        $facts4 = new Collection();
157ce42304aSGreg Roach
158ce42304aSGreg Roach        foreach ($facts as $fact) {
159d0889c63SGreg Roach            if ($fact->tag() !== 'INDI:OBJE') {
160ce42304aSGreg Roach                $facts1->push($fact);
161ce42304aSGreg Roach            } elseif ($fact->value() !== '@' . $xref . '@') {
162ce42304aSGreg Roach                $facts3->push($fact);
163ce42304aSGreg Roach            } elseif ($primary) {
164ce42304aSGreg Roach                $facts2->push($fact);
165ce42304aSGreg Roach            } else {
166ce42304aSGreg Roach                $facts4->push($fact);
167ce42304aSGreg Roach            }
168ce42304aSGreg Roach        }
169ce42304aSGreg Roach
170ce42304aSGreg Roach        $sorted_facts = $facts1->concat($facts2)->concat($facts3)->concat($facts4);
171ce42304aSGreg Roach
172*f25fc0f9SGreg Roach        $gedcom = $sorted_facts->map(static fn (Fact $fact): string => "\n" . $fact->gedcom())->implode('');
173ce42304aSGreg Roach
174ce42304aSGreg Roach        $gedcom = '0 @' . $individual->xref() . '@ INDI' . $gedcom;
175ce42304aSGreg Roach
176ce42304aSGreg Roach        $individual->updateRecord($gedcom, false);
177ce42304aSGreg Roach    }
178ce42304aSGreg Roach}
179