xref: /webtrees/app/Http/RequestHandlers/RenumberTreeAction.php (revision afa67798854828b1edc33dd077960ec2b18e6140)
1<?php
2
3/**
4 * webtrees: online genealogy
5 * Copyright (C) 2021 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\Http\RequestHandlers;
21
22use Fisharebest\Webtrees\Contracts\UserInterface;
23use Fisharebest\Webtrees\Family;
24use Fisharebest\Webtrees\FlashMessages;
25use Fisharebest\Webtrees\I18N;
26use Fisharebest\Webtrees\Individual;
27use Fisharebest\Webtrees\Media;
28use Fisharebest\Webtrees\Note;
29use Fisharebest\Webtrees\Registry;
30use Fisharebest\Webtrees\Repository;
31use Fisharebest\Webtrees\Services\AdminService;
32use Fisharebest\Webtrees\Services\TimeoutService;
33use Fisharebest\Webtrees\Source;
34use Fisharebest\Webtrees\Tree;
35use Illuminate\Database\Capsule\Manager as DB;
36use Illuminate\Database\Query\Expression;
37use Illuminate\Database\Query\JoinClause;
38use Psr\Http\Message\ResponseInterface;
39use Psr\Http\Message\ServerRequestInterface;
40use Psr\Http\Server\RequestHandlerInterface;
41
42use function assert;
43use function redirect;
44use function route;
45
46/**
47 * Renumber the XREFs in a family tree.
48 */
49class RenumberTreeAction implements RequestHandlerInterface
50{
51    /** @var AdminService */
52    private $admin_service;
53
54    /** @var TimeoutService */
55    private $timeout_service;
56
57    /**
58     * @param AdminService   $admin_service
59     * @param TimeoutService $timeout_service
60     */
61    public function __construct(AdminService $admin_service, TimeoutService $timeout_service)
62    {
63        $this->admin_service   = $admin_service;
64        $this->timeout_service = $timeout_service;
65    }
66
67    /**
68     * @param ServerRequestInterface $request
69     *
70     * @return ResponseInterface
71     */
72    public function handle(ServerRequestInterface $request): ResponseInterface
73    {
74        $tree = $request->getAttribute('tree');
75        assert($tree instanceof Tree);
76
77        $xrefs = $this->admin_service->duplicateXrefs($tree);
78
79        foreach ($xrefs as $old_xref => $type) {
80            $new_xref = Registry::xrefFactory()->make($type);
81            switch ($type) {
82                case Individual::RECORD_TYPE:
83                    DB::table('individuals')
84                        ->where('i_file', '=', $tree->id())
85                        ->where('i_id', '=', $old_xref)
86                        ->update([
87                            'i_id'     => $new_xref,
88                            'i_gedcom' => new Expression("REPLACE(i_gedcom, '0 @$old_xref@ INDI', '0 @$new_xref@ INDI')"),
89                        ]);
90
91                    DB::table('families')
92                        ->where('f_husb', '=', $old_xref)
93                        ->where('f_file', '=', $tree->id())
94                        ->update([
95                            'f_husb'   => $new_xref,
96                            'f_gedcom' => new Expression("REPLACE(f_gedcom, ' HUSB @$old_xref@', ' HUSB @$new_xref@')"),
97                        ]);
98
99                    DB::table('families')
100                        ->where('f_wife', '=', $old_xref)
101                        ->where('f_file', '=', $tree->id())
102                        ->update([
103                            'f_wife'   => $new_xref,
104                            'f_gedcom' => new Expression("REPLACE(f_gedcom, ' WIFE @$old_xref@', ' WIFE @$new_xref@')"),
105                        ]);
106
107                    // Other links from families to individuals
108                    foreach (['CHIL', 'ASSO', '_ASSO'] as $tag) {
109                        DB::table('families')
110                            ->join('link', static function (JoinClause $join): void {
111                                $join
112                                    ->on('l_file', '=', 'f_file')
113                                    ->on('l_from', '=', 'f_id');
114                            })
115                            ->where('l_to', '=', $old_xref)
116                            ->where('l_type', '=', $tag)
117                            ->where('f_file', '=', $tree->id())
118                            ->update([
119                                'f_gedcom' => new Expression("REPLACE(f_gedcom, ' $tag @$old_xref@', ' $tag @$new_xref@')"),
120                            ]);
121                    }
122
123                    // Links from individuals to individuals
124                    foreach (['ALIA', 'ASSO', '_ASSO'] as $tag) {
125                        DB::table('individuals')
126                            ->join('link', static function (JoinClause $join): void {
127                                $join
128                                    ->on('l_file', '=', 'i_file')
129                                    ->on('l_from', '=', 'i_id');
130                            })
131                            ->where('link.l_to', '=', $old_xref)
132                            ->where('link.l_type', '=', $tag)
133                            ->where('i_file', '=', $tree->id())
134                            ->update([
135                                'i_gedcom' => new Expression("REPLACE(i_gedcom, ' $tag @$old_xref@', ' $tag @$new_xref@')"),
136                            ]);
137                    }
138
139                    DB::table('placelinks')
140                        ->where('pl_file', '=', $tree->id())
141                        ->where('pl_gid', '=', $old_xref)
142                        ->update([
143                            'pl_gid' => $new_xref,
144                        ]);
145
146                    DB::table('dates')
147                        ->where('d_file', '=', $tree->id())
148                        ->where('d_gid', '=', $old_xref)
149                        ->update([
150                            'd_gid' => $new_xref,
151                        ]);
152
153                    DB::table('user_gedcom_setting')
154                        ->where('gedcom_id', '=', $tree->id())
155                        ->where('setting_value', '=', $old_xref)
156                        ->whereIn('setting_name', [UserInterface::PREF_TREE_ACCOUNT_XREF, UserInterface::PREF_TREE_DEFAULT_XREF])
157                        ->update([
158                            'setting_value' => $new_xref,
159                        ]);
160                    break;
161
162                case Family::RECORD_TYPE:
163                    DB::table('families')
164                        ->where('f_file', '=', $tree->id())
165                        ->where('f_id', '=', $old_xref)
166                        ->update([
167                            'f_id'     => $new_xref,
168                            'f_gedcom' => new Expression("REPLACE(f_gedcom, '0 @$old_xref@ FAM', '0 @$new_xref@ FAM')"),
169                        ]);
170
171                    // Links from individuals to families
172                    foreach (['FAMC', 'FAMS'] as $tag) {
173                        DB::table('individuals')
174                            ->join('link', static function (JoinClause $join): void {
175                                $join
176                                    ->on('l_file', '=', 'i_file')
177                                    ->on('l_from', '=', 'i_id');
178                            })
179                            ->where('l_to', '=', $old_xref)
180                            ->where('l_type', '=', $tag)
181                            ->where('i_file', '=', $tree->id())
182                            ->update([
183                                'i_gedcom' => new Expression("REPLACE(i_gedcom, ' $tag @$old_xref@', ' $tag @$new_xref@')"),
184                            ]);
185                    }
186
187                    DB::table('placelinks')
188                        ->where('pl_file', '=', $tree->id())
189                        ->where('pl_gid', '=', $old_xref)
190                        ->update([
191                            'pl_gid' => $new_xref,
192                        ]);
193
194                    DB::table('dates')
195                        ->where('d_file', '=', $tree->id())
196                        ->where('d_gid', '=', $old_xref)
197                        ->update([
198                            'd_gid' => $new_xref,
199                        ]);
200                    break;
201
202                case Source::RECORD_TYPE:
203                    DB::table('sources')
204                        ->where('s_file', '=', $tree->id())
205                        ->where('s_id', '=', $old_xref)
206                        ->update([
207                            's_id'     => $new_xref,
208                            's_gedcom' => new Expression("REPLACE(s_gedcom, '0 @$old_xref@ SOUR', '0 @$new_xref@ SOUR')"),
209                        ]);
210
211                    DB::table('individuals')
212                        ->join('link', static function (JoinClause $join): void {
213                            $join
214                                ->on('l_file', '=', 'i_file')
215                                ->on('l_from', '=', 'i_id');
216                        })
217                        ->where('l_to', '=', $old_xref)
218                        ->where('l_type', '=', 'SOUR')
219                        ->where('i_file', '=', $tree->id())
220                        ->update([
221                            'i_gedcom' => new Expression("REPLACE(i_gedcom, ' SOUR @$old_xref@', ' SOUR @$new_xref@')"),
222                        ]);
223
224                    DB::table('families')
225                        ->join('link', static function (JoinClause $join): void {
226                            $join
227                                ->on('l_file', '=', 'f_file')
228                                ->on('l_from', '=', 'f_id');
229                        })
230                        ->where('l_to', '=', $old_xref)
231                        ->where('l_type', '=', 'SOUR')
232                        ->where('f_file', '=', $tree->id())
233                        ->update([
234                            'f_gedcom' => new Expression("REPLACE(f_gedcom, ' SOUR @$old_xref@', ' SOUR @$new_xref@')"),
235                        ]);
236
237                    DB::table('media')
238                        ->join('link', static function (JoinClause $join): void {
239                            $join
240                                ->on('l_file', '=', 'm_file')
241                                ->on('l_from', '=', 'm_id');
242                        })
243                        ->where('l_to', '=', $old_xref)
244                        ->where('l_type', '=', 'SOUR')
245                        ->where('m_file', '=', $tree->id())
246                        ->update([
247                            'm_gedcom' => new Expression("REPLACE(m_gedcom, ' SOUR @$old_xref@', ' SOUR @$new_xref@')"),
248                        ]);
249
250                    DB::table('other')
251                        ->join('link', static function (JoinClause $join): void {
252                            $join
253                                ->on('l_file', '=', 'o_file')
254                                ->on('l_from', '=', 'o_id');
255                        })
256                        ->where('l_to', '=', $old_xref)
257                        ->where('l_type', '=', 'SOUR')
258                        ->where('o_file', '=', $tree->id())
259                        ->update([
260                            'o_gedcom' => new Expression("REPLACE(o_gedcom, ' SOUR @$old_xref@', ' SOUR @$new_xref@')"),
261                        ]);
262                    break;
263
264                case Repository::RECORD_TYPE:
265                    DB::table('other')
266                        ->where('o_file', '=', $tree->id())
267                        ->where('o_id', '=', $old_xref)
268                        ->where('o_type', '=', 'REPO')
269                        ->update([
270                            'o_id'     => $new_xref,
271                            'o_gedcom' => new Expression("REPLACE(o_gedcom, '0 @$old_xref@ REPO', '0 @$new_xref@ REPO')"),
272                        ]);
273
274                    DB::table('sources')
275                        ->join('link', static function (JoinClause $join): void {
276                            $join
277                                ->on('l_file', '=', 's_file')
278                                ->on('l_from', '=', 's_id');
279                        })
280                        ->where('l_to', '=', $old_xref)
281                        ->where('l_type', '=', 'REPO')
282                        ->where('s_file', '=', $tree->id())
283                        ->update([
284                            's_gedcom' => new Expression("REPLACE(s_gedcom, ' REPO @$old_xref@', ' REPO @$new_xref@')"),
285                        ]);
286                    break;
287
288                case Note::RECORD_TYPE:
289                    DB::table('other')
290                        ->where('o_file', '=', $tree->id())
291                        ->where('o_id', '=', $old_xref)
292                        ->where('o_type', '=', 'NOTE')
293                        ->update([
294                            'o_id'     => $new_xref,
295                            'o_gedcom' => new Expression("REPLACE(o_gedcom, '0 @$old_xref@ NOTE', '0 @$new_xref@ NOTE')"),
296                        ]);
297
298                    DB::table('individuals')
299                        ->join('link', static function (JoinClause $join): void {
300                            $join
301                                ->on('l_file', '=', 'i_file')
302                                ->on('l_from', '=', 'i_id');
303                        })
304                        ->where('l_to', '=', $old_xref)
305                        ->where('l_type', '=', 'NOTE')
306                        ->where('i_file', '=', $tree->id())
307                        ->update([
308                            'i_gedcom' => new Expression("REPLACE(i_gedcom, ' NOTE @$old_xref@', ' NOTE @$new_xref@')"),
309                        ]);
310
311                    DB::table('families')
312                        ->join('link', static function (JoinClause $join): void {
313                            $join
314                                ->on('l_file', '=', 'f_file')
315                                ->on('l_from', '=', 'f_id');
316                        })
317                        ->where('l_to', '=', $old_xref)
318                        ->where('l_type', '=', 'NOTE')
319                        ->where('f_file', '=', $tree->id())
320                        ->update([
321                            'f_gedcom' => new Expression("REPLACE(f_gedcom, ' NOTE @$old_xref@', ' NOTE @$new_xref@')"),
322                        ]);
323
324                    DB::table('media')
325                        ->join('link', static function (JoinClause $join): void {
326                            $join
327                                ->on('l_file', '=', 'm_file')
328                                ->on('l_from', '=', 'm_id');
329                        })
330                        ->where('l_to', '=', $old_xref)
331                        ->where('l_type', '=', 'NOTE')
332                        ->where('m_file', '=', $tree->id())
333                        ->update([
334                            'm_gedcom' => new Expression("REPLACE(m_gedcom, ' NOTE @$old_xref@', ' NOTE @$new_xref@')"),
335                        ]);
336
337                    DB::table('sources')
338                        ->join('link', static function (JoinClause $join): void {
339                            $join
340                                ->on('l_file', '=', 's_file')
341                                ->on('l_from', '=', 's_id');
342                        })
343                        ->where('l_to', '=', $old_xref)
344                        ->where('l_type', '=', 'NOTE')
345                        ->where('s_file', '=', $tree->id())
346                        ->update([
347                            's_gedcom' => new Expression("REPLACE(s_gedcom, ' NOTE @$old_xref@', ' NOTE @$new_xref@')"),
348                        ]);
349
350                    DB::table('other')
351                        ->join('link', static function (JoinClause $join): void {
352                            $join
353                                ->on('l_file', '=', 'o_file')
354                                ->on('l_from', '=', 'o_id');
355                        })
356                        ->where('l_to', '=', $old_xref)
357                        ->where('l_type', '=', 'NOTE')
358                        ->where('o_file', '=', $tree->id())
359                        ->update([
360                            'o_gedcom' => new Expression("REPLACE(o_gedcom, ' NOTE @$old_xref@', ' NOTE @$new_xref@')"),
361                        ]);
362                    break;
363
364                case Media::RECORD_TYPE:
365                    DB::table('media')
366                        ->where('m_file', '=', $tree->id())
367                        ->where('m_id', '=', $old_xref)
368                        ->update([
369                            'm_id'     => $new_xref,
370                            'm_gedcom' => new Expression("REPLACE(m_gedcom, '0 @$old_xref@ OBJE', '0 @$new_xref@ OBJE')"),
371                        ]);
372
373                    DB::table('media_file')
374                        ->where('m_file', '=', $tree->id())
375                        ->where('m_id', '=', $old_xref)
376                        ->update([
377                            'm_id' => $new_xref,
378                        ]);
379
380                    DB::table('individuals')
381                        ->join('link', static function (JoinClause $join): void {
382                            $join
383                                ->on('l_file', '=', 'i_file')
384                                ->on('l_from', '=', 'i_id');
385                        })
386                        ->where('l_to', '=', $old_xref)
387                        ->where('l_type', '=', 'OBJE')
388                        ->where('i_file', '=', $tree->id())
389                        ->update([
390                            'i_gedcom' => new Expression("REPLACE(i_gedcom, ' OBJE @$old_xref@', ' OBJE @$new_xref@')"),
391                        ]);
392
393                    DB::table('families')
394                        ->join('link', static function (JoinClause $join): void {
395                            $join
396                                ->on('l_file', '=', 'f_file')
397                                ->on('l_from', '=', 'f_id');
398                        })
399                        ->where('l_to', '=', $old_xref)
400                        ->where('l_type', '=', 'OBJE')
401                        ->where('f_file', '=', $tree->id())
402                        ->update([
403                            'f_gedcom' => new Expression("REPLACE(f_gedcom, ' OBJE @$old_xref@', ' OBJE @$new_xref@')"),
404                        ]);
405
406                    DB::table('sources')
407                        ->join('link', static function (JoinClause $join): void {
408                            $join
409                                ->on('l_file', '=', 's_file')
410                                ->on('l_from', '=', 's_id');
411                        })
412                        ->where('l_to', '=', $old_xref)
413                        ->where('l_type', '=', 'OBJE')
414                        ->where('s_file', '=', $tree->id())
415                        ->update([
416                            's_gedcom' => new Expression("REPLACE(s_gedcom, ' OBJE @$old_xref@', ' OBJE @$new_xref@')"),
417                        ]);
418
419                    DB::table('other')
420                        ->join('link', static function (JoinClause $join): void {
421                            $join
422                                ->on('l_file', '=', 'o_file')
423                                ->on('l_from', '=', 'o_id');
424                        })
425                        ->where('l_to', '=', $old_xref)
426                        ->where('l_type', '=', 'OBJE')
427                        ->where('o_file', '=', $tree->id())
428                        ->update([
429                            'o_gedcom' => new Expression("REPLACE(o_gedcom, ' OBJE @$old_xref@', ' OBJE @$new_xref@')"),
430                        ]);
431                    break;
432
433                default:
434                    DB::table('other')
435                        ->where('o_file', '=', $tree->id())
436                        ->where('o_id', '=', $old_xref)
437                        ->where('o_type', '=', $type)
438                        ->update([
439                            'o_id'     => $new_xref,
440                            'o_gedcom' => new Expression("REPLACE(o_gedcom, '0 @$old_xref@ $type', '0 @$new_xref@ $type')"),
441                        ]);
442
443                    DB::table('individuals')
444                        ->join('link', static function (JoinClause $join): void {
445                            $join
446                                ->on('l_file', '=', 'i_file')
447                                ->on('l_from', '=', 'i_id');
448                        })
449                        ->where('l_to', '=', $old_xref)
450                        ->where('l_type', '=', $type)
451                        ->where('i_file', '=', $tree->id())
452                        ->update([
453                            'i_gedcom' => new Expression("REPLACE(i_gedcom, ' $type @$old_xref@', ' $type @$new_xref@')"),
454                        ]);
455
456                    DB::table('families')
457                        ->join('link', static function (JoinClause $join): void {
458                            $join
459                                ->on('l_file', '=', 'f_file')
460                                ->on('l_from', '=', 'f_id');
461                        })
462                        ->where('l_to', '=', $old_xref)
463                        ->where('l_type', '=', $type)
464                        ->where('f_file', '=', $tree->id())
465                        ->update([
466                            'f_gedcom' => new Expression("REPLACE(f_gedcom, ' $type @$old_xref@', ' $type @$new_xref@')"),
467                        ]);
468
469                    DB::table('media')
470                        ->join('link', static function (JoinClause $join): void {
471                            $join
472                                ->on('l_file', '=', 'm_file')
473                                ->on('l_from', '=', 'm_id');
474                        })
475                        ->where('l_to', '=', $old_xref)
476                        ->where('l_type', '=', $type)
477                        ->where('m_file', '=', $tree->id())
478                        ->update([
479                            'm_gedcom' => new Expression("REPLACE(m_gedcom, ' $type @$old_xref@', ' $type @$new_xref@')"),
480                        ]);
481
482                    DB::table('sources')
483                        ->join('link', static function (JoinClause $join): void {
484                            $join
485                                ->on('l_file', '=', 's_file')
486                                ->on('l_from', '=', 's_id');
487                        })
488                        ->where('l_to', '=', $old_xref)
489                        ->where('l_type', '=', $type)
490                        ->where('s_file', '=', $tree->id())
491                        ->update([
492                            's_gedcom' => new Expression("REPLACE(s_gedcom, ' $type @$old_xref@', ' $type @$new_xref@')"),
493                        ]);
494
495                    DB::table('other')
496                        ->join('link', static function (JoinClause $join): void {
497                            $join
498                                ->on('l_file', '=', 'o_file')
499                                ->on('l_from', '=', 'o_id');
500                        })
501                        ->where('l_to', '=', $old_xref)
502                        ->where('l_type', '=', $type)
503                        ->where('o_file', '=', $tree->id())
504                        ->update([
505                            'o_gedcom' => new Expression("REPLACE(o_gedcom, ' $type @$old_xref@', ' $type @$new_xref@')"),
506                        ]);
507                    break;
508            }
509
510            DB::table('name')
511                ->where('n_file', '=', $tree->id())
512                ->where('n_id', '=', $old_xref)
513                ->update([
514                    'n_id' => $new_xref,
515                ]);
516
517            DB::table('default_resn')
518                ->where('gedcom_id', '=', $tree->id())
519                ->where('xref', '=', $old_xref)
520                ->update([
521                    'xref' => $new_xref,
522                ]);
523
524            DB::table('hit_counter')
525                ->where('gedcom_id', '=', $tree->id())
526                ->where('page_parameter', '=', $old_xref)
527                ->update([
528                    'page_parameter' => $new_xref,
529                ]);
530
531            DB::table('link')
532                ->where('l_file', '=', $tree->id())
533                ->where('l_from', '=', $old_xref)
534                ->update([
535                    'l_from' => $new_xref,
536                ]);
537
538            DB::table('link')
539                ->where('l_file', '=', $tree->id())
540                ->where('l_to', '=', $old_xref)
541                ->update([
542                    'l_to' => $new_xref,
543                ]);
544
545            DB::table('favorite')
546                ->where('gedcom_id', '=', $tree->id())
547                ->where('xref', '=', $old_xref)
548                ->update([
549                    'xref' => $new_xref,
550                ]);
551
552            unset($xrefs[$old_xref]);
553
554            // How much time do we have left?
555            if ($this->timeout_service->isTimeNearlyUp()) {
556                FlashMessages::addMessage(I18N::translate('The server’s time limit has been reached.'), 'warning');
557                break;
558            }
559        }
560
561        $url = route(RenumberTreePage::class, ['tree' => $tree->name()]);
562
563        return redirect($url);
564    }
565}
566