Issues (2560)

app/Module/FixDuplicateLinks.php (2 issues)

Labels
Severity
1
<?php
2
3
/**
4
 * webtrees: online genealogy
5
 * Copyright (C) 2025 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
18
declare(strict_types=1);
19
20
namespace Fisharebest\Webtrees\Module;
21
22
use Fisharebest\Webtrees\GedcomRecord;
0 ignored issues
show
The type Fisharebest\Webtrees\GedcomRecord was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
23
use Fisharebest\Webtrees\I18N;
0 ignored issues
show
The type Fisharebest\Webtrees\I18N was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
24
use Fisharebest\Webtrees\Services\DataFixService;
25
use Fisharebest\Webtrees\Tree;
26
use Illuminate\Support\Collection;
27
28
use function preg_match;
29
use function preg_replace;
30
31
class FixDuplicateLinks extends AbstractModule implements ModuleDataFixInterface
32
{
33
    use ModuleDataFixTrait;
34
35
    private DataFixService $data_fix_service;
36
37
    /**
38
     * @param DataFixService $data_fix_service
39
     */
40
    public function __construct(DataFixService $data_fix_service)
41
    {
42
        $this->data_fix_service = $data_fix_service;
43
    }
44
45
    public function title(): string
46
    {
47
        /* I18N: Name of a module */
48
        return I18N::translate('Remove duplicate links');
49
    }
50
51
    public function description(): string
52
    {
53
        /* I18N: Description of a “Data fix” module */
54
        return I18N::translate('A common error is to have multiple links to the same record, for example listing the same child more than once in a family record.');
55
    }
56
57
    /**
58
     * A list of all records that need examining.  This may include records
59
     * that do not need updating, if we can't detect this quickly using SQL.
60
     *
61
     * @param Tree          $tree
62
     * @param array<string> $params
63
     *
64
     * @return Collection<int,string>
65
     */
66
    protected function familiesToFix(Tree $tree, array $params): Collection
67
    {
68
        // No DB querying possible?  Select all.
69
        return $this->familiesToFixQuery($tree, $params)
70
            ->pluck('f_id');
71
    }
72
73
    /**
74
     * A list of all records that need examining.  This may include records
75
     * that do not need updating, if we can't detect this quickly using SQL.
76
     *
77
     * @param Tree                 $tree
78
     * @param array<string,string> $params
79
     *
80
     * @return Collection<int,string>|null
81
     */
82
    protected function individualsToFix(Tree $tree, array $params): Collection|null
83
    {
84
        // No DB querying possible?  Select all.
85
        return $this->individualsToFixQuery($tree, $params)
86
            ->pluck('i_id');
87
    }
88
89
    /**
90
     * A list of all records that need examining.  This may include records
91
     * that do not need updating, if we can't detect this quickly using SQL.
92
     *
93
     * @param Tree                 $tree
94
     * @param array<string,string> $params
95
     *
96
     * @return Collection<int,string>
97
     */
98
    protected function mediaToFix(Tree $tree, array $params): Collection
99
    {
100
        // No DB querying possible?  Select all.
101
        return $this->mediaToFixQuery($tree, $params)
102
            ->pluck('m_id');
103
    }
104
105
    /**
106
     * A list of all records that need examining.  This may include records
107
     * that do not need updating, if we can't detect this quickly using SQL.
108
     *
109
     * @param Tree                 $tree
110
     * @param array<string,string> $params
111
     *
112
     * @return Collection<int,string>
113
     */
114
    protected function notesToFix(Tree $tree, array $params): Collection
115
    {
116
        // No DB querying possible?  Select all.
117
        return $this->notesToFixQuery($tree, $params)
118
            ->pluck('o_id');
119
    }
120
121
    /**
122
     * A list of all records that need examining.  This may include records
123
     * that do not need updating, if we can't detect this quickly using SQL.
124
     *
125
     * @param Tree                 $tree
126
     * @param array<string,string> $params
127
     *
128
     * @return Collection<int,string>
129
     */
130
    protected function repositoriesToFix(Tree $tree, array $params): Collection
131
    {
132
        // No DB querying possible?  Select all.
133
        return $this->repositoriesToFixQuery($tree, $params)
134
            ->pluck('o_id');
135
    }
136
137
    /**
138
     * A list of all records that need examining.  This may include records
139
     * that do not need updating, if we can't detect this quickly using SQL.
140
     *
141
     * @param Tree                 $tree
142
     * @param array<string,string> $params
143
     *
144
     * @return Collection<int,string>
145
     */
146
    protected function sourcesToFix(Tree $tree, array $params): Collection
147
    {
148
        // No DB querying possible?  Select all.
149
        return $this->sourcesToFixQuery($tree, $params)
150
            ->pluck('s_id');
151
    }
152
153
    /**
154
     * A list of all records that need examining.  This may include records
155
     * that do not need updating, if we can't detect this quickly using SQL.
156
     *
157
     * @param Tree                 $tree
158
     * @param array<string,string> $params
159
     *
160
     * @return Collection<int,string>
161
     */
162
    protected function submittersToFix(Tree $tree, array $params): Collection
163
    {
164
        // No DB querying possible?  Select all.
165
        return $this->submittersToFixQuery($tree, $params)
166
            ->pluck('o_id');
167
    }
168
169
    /**
170
     * Does a record need updating?
171
     *
172
     * @param GedcomRecord         $record
173
     * @param array<string,string> $params
174
     *
175
     * @return bool
176
     */
177
    public function doesRecordNeedUpdate(GedcomRecord $record, array $params): bool
178
    {
179
        $gedcom = $record->gedcom();
180
181
        return
182
            preg_match('/(\n1.*@.+@.*(?:\n[2-9].*)*)(?:\n1.*(?:\n[2-9].*)*)*\1/', $gedcom) ||
183
            preg_match('/(\n2.*@.+@.*(?:\n[3-9].*)*)(?:\n2.*(?:\n[3-9].*)*)*\1/', $gedcom) ||
184
            preg_match('/(\n3.*@.+@.*(?:\n[4-9].*)*)(?:\n3.*(?:\n[4-9].*)*)*\1/', $gedcom);
185
    }
186
187
    /**
188
     * Show the changes we would make
189
     *
190
     * @param GedcomRecord         $record
191
     * @param array<string,string> $params
192
     *
193
     * @return string
194
     */
195
    public function previewUpdate(GedcomRecord $record, array $params): string
196
    {
197
        $old = $record->gedcom();
198
        $new = $this->updateGedcom($record);
199
200
        return $this->data_fix_service->gedcomDiff($record->tree(), $old, $new);
201
    }
202
203
    /**
204
     * Fix a record
205
     *
206
     * @param GedcomRecord         $record
207
     * @param array<string,string> $params
208
     *
209
     * @return void
210
     */
211
    public function updateRecord(GedcomRecord $record, array $params): void
212
    {
213
        $record->updateRecord($this->updateGedcom($record), false);
214
    }
215
216
    /**
217
     * @param GedcomRecord $record
218
     *
219
     * @return string
220
     */
221
    private function updateGedcom(GedcomRecord $record): string
222
    {
223
        $gedcom = $record->gedcom();
224
        $gedcom = preg_replace('/(\n1.*@.+@.*(?:\n[2-9].*)*)((?:\n1.*(?:\n[2-9].*)*)*\1)/', '$2', $gedcom);
225
        $gedcom = preg_replace('/(\n2.*@.+@.*(?:\n[3-9].*)*)((?:\n2.*(?:\n[3-9].*)*)*\1)/', '$2', $gedcom);
226
227
        return preg_replace('/(\n3.*@.+@.*(?:\n[4-9].*)*)((?:\n3.*(?:\n[4-9].*)*)*\1)/', '$2', $gedcom);
228
    }
229
}
230