xref: /webtrees/app/Http/RequestHandlers/MergeFactsAction.php (revision 11225d5e3f5cfe234635c3e83127a4f88451294f)
15bbfbb82SGreg Roach<?php
25bbfbb82SGreg Roach
35bbfbb82SGreg Roach/**
45bbfbb82SGreg Roach * webtrees: online genealogy
5d11be702SGreg Roach * Copyright (C) 2023 webtrees development team
65bbfbb82SGreg Roach * This program is free software: you can redistribute it and/or modify
75bbfbb82SGreg Roach * it under the terms of the GNU General Public License as published by
85bbfbb82SGreg Roach * the Free Software Foundation, either version 3 of the License, or
95bbfbb82SGreg Roach * (at your option) any later version.
105bbfbb82SGreg Roach * This program is distributed in the hope that it will be useful,
115bbfbb82SGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of
125bbfbb82SGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
135bbfbb82SGreg Roach * GNU General Public License for more details.
145bbfbb82SGreg 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/>.
165bbfbb82SGreg Roach */
175bbfbb82SGreg Roach
185bbfbb82SGreg Roachdeclare(strict_types=1);
195bbfbb82SGreg Roach
205bbfbb82SGreg Roachnamespace Fisharebest\Webtrees\Http\RequestHandlers;
215bbfbb82SGreg Roach
225bbfbb82SGreg Roachuse Fisharebest\Webtrees\Auth;
231fe542e9SGreg Roachuse Fisharebest\Webtrees\Contracts\UserInterface;
246f4ec3caSGreg Roachuse Fisharebest\Webtrees\DB;
255bbfbb82SGreg Roachuse Fisharebest\Webtrees\FlashMessages;
265bbfbb82SGreg Roachuse Fisharebest\Webtrees\I18N;
276b9cb339SGreg Roachuse Fisharebest\Webtrees\Registry;
284991f205SGreg Roachuse Fisharebest\Webtrees\Services\LinkedRecordService;
29b55cbc6bSGreg Roachuse Fisharebest\Webtrees\Validator;
305bbfbb82SGreg Roachuse Illuminate\Database\Query\Expression;
315bbfbb82SGreg Roachuse Psr\Http\Message\ResponseInterface;
325bbfbb82SGreg Roachuse Psr\Http\Message\ServerRequestInterface;
335bbfbb82SGreg Roachuse Psr\Http\Server\RequestHandlerInterface;
345bbfbb82SGreg Roach
355bbfbb82SGreg Roachuse function e;
365bbfbb82SGreg Roachuse function in_array;
375bbfbb82SGreg Roachuse function preg_replace;
385bbfbb82SGreg Roachuse function redirect;
395bbfbb82SGreg Roachuse function route;
405bbfbb82SGreg Roachuse function str_replace;
415bbfbb82SGreg Roach
425bbfbb82SGreg Roach/**
435bbfbb82SGreg Roach * Merge records
445bbfbb82SGreg Roach */
455bbfbb82SGreg Roachclass MergeFactsAction implements RequestHandlerInterface
465bbfbb82SGreg Roach{
474991f205SGreg Roach    private LinkedRecordService $linked_record_service;
484991f205SGreg Roach
494991f205SGreg Roach    /**
504991f205SGreg Roach     * @param LinkedRecordService $linked_record_service
514991f205SGreg Roach     */
524991f205SGreg Roach    public function __construct(LinkedRecordService $linked_record_service)
534991f205SGreg Roach    {
544991f205SGreg Roach        $this->linked_record_service = $linked_record_service;
554991f205SGreg Roach    }
564991f205SGreg Roach
575bbfbb82SGreg Roach    /**
585bbfbb82SGreg Roach     * @param ServerRequestInterface $request
595bbfbb82SGreg Roach     *
605bbfbb82SGreg Roach     * @return ResponseInterface
615bbfbb82SGreg Roach     */
625bbfbb82SGreg Roach    public function handle(ServerRequestInterface $request): ResponseInterface
635bbfbb82SGreg Roach    {
64b55cbc6bSGreg Roach        $tree  = Validator::attributes($request)->tree();
65748dbe15SGreg Roach        $xref1 = Validator::parsedBody($request)->isXref()->string('xref1');
66748dbe15SGreg Roach        $xref2 = Validator::parsedBody($request)->isXref()->string('xref2');
67748dbe15SGreg Roach        $keep1 = Validator::parsedBody($request)->array('keep1');
68748dbe15SGreg Roach        $keep2 = Validator::parsedBody($request)->array('keep2');
695bbfbb82SGreg Roach
705bbfbb82SGreg Roach        // Merge record2 into record1
716b9cb339SGreg Roach        $record1 = Registry::gedcomRecordFactory()->make($xref1, $tree);
726b9cb339SGreg Roach        $record2 = Registry::gedcomRecordFactory()->make($xref2, $tree);
735bbfbb82SGreg Roach
745bbfbb82SGreg Roach        if (
755bbfbb82SGreg Roach            $record1 === null ||
765bbfbb82SGreg Roach            $record2 === null ||
775bbfbb82SGreg Roach            $record1 === $record2 ||
7802467d32SGreg Roach            $record1->tag() !== $record2->tag() ||
795bbfbb82SGreg Roach            $record1->isPendingDeletion() ||
805bbfbb82SGreg Roach            $record2->isPendingDeletion()
815bbfbb82SGreg Roach        ) {
825bbfbb82SGreg Roach            return redirect(route(MergeRecordsPage::class, [
835bbfbb82SGreg Roach                'tree'  => $tree->name(),
845bbfbb82SGreg Roach                'xref1' => $xref1,
855bbfbb82SGreg Roach                'xref2' => $xref2,
865bbfbb82SGreg Roach            ]));
875bbfbb82SGreg Roach        }
885bbfbb82SGreg Roach
895bbfbb82SGreg Roach        // If we are not auto-accepting, then we can show a link to the pending deletion
901fe542e9SGreg Roach        if (Auth::user()->getPreference(UserInterface::PREF_AUTO_ACCEPT_EDITS) === '1') {
915bbfbb82SGreg Roach            $record2_name = $record2->fullName();
925bbfbb82SGreg Roach        } else {
935bbfbb82SGreg Roach            $record2_name = '<a class="alert-link" href="' . e($record2->url()) . '">' . $record2->fullName() . '</a>';
945bbfbb82SGreg Roach        }
955bbfbb82SGreg Roach
965bbfbb82SGreg Roach        // Update records that link to the one we will be removing.
974991f205SGreg Roach        $linking_records = $this->linked_record_service->allLinkedRecords($record2);
985bbfbb82SGreg Roach
995bbfbb82SGreg Roach        foreach ($linking_records as $record) {
1005bbfbb82SGreg Roach            if (!$record->isPendingDeletion()) {
1015bbfbb82SGreg Roach                /* I18N: The placeholders are the names of individuals, sources, etc. */
1025bbfbb82SGreg Roach                FlashMessages::addMessage(I18N::translate(
1035bbfbb82SGreg Roach                    'The link from “%1$s” to “%2$s” has been updated.',
1045bbfbb82SGreg Roach                    '<a class="alert-link" href="' . e($record->url()) . '">' . $record->fullName() . '</a>',
1055bbfbb82SGreg Roach                    $record2_name
1065bbfbb82SGreg Roach                ), 'info');
1075bbfbb82SGreg Roach                $gedcom = str_replace('@' . $xref2 . '@', '@' . $xref1 . '@', $record->gedcom());
1085bbfbb82SGreg Roach                $gedcom = preg_replace(
109ecc5b3c4SGreg Roach                    '/(\n1.*@.+@.*(?:\n[2-9].*)*)((?:\n1.*(?:\n[2-9].*)*)*\1)/',
1105bbfbb82SGreg Roach                    '$2',
1115bbfbb82SGreg Roach                    $gedcom
1125bbfbb82SGreg Roach                );
1135bbfbb82SGreg Roach                $record->updateRecord($gedcom, true);
1145bbfbb82SGreg Roach            }
1155bbfbb82SGreg Roach        }
1165bbfbb82SGreg Roach
1175bbfbb82SGreg Roach        // Update any linked user-accounts
1185bbfbb82SGreg Roach        DB::table('user_gedcom_setting')
1195bbfbb82SGreg Roach            ->where('gedcom_id', '=', $tree->id())
1201fe542e9SGreg Roach            ->whereIn('setting_name', [UserInterface::PREF_TREE_ACCOUNT_XREF, UserInterface::PREF_TREE_DEFAULT_XREF])
1215bbfbb82SGreg Roach            ->where('setting_value', '=', $xref2)
1225bbfbb82SGreg Roach            ->update(['setting_value' => $xref1]);
1235bbfbb82SGreg Roach
124ecc5b3c4SGreg Roach        // Merge stories, etc.
125ecc5b3c4SGreg Roach        DB::table('block')
126ecc5b3c4SGreg Roach            ->where('gedcom_id', '=', $tree->id())
127ecc5b3c4SGreg Roach            ->where('xref', '=', $xref2)
128ecc5b3c4SGreg Roach            ->update(['xref' => $xref1]);
129ecc5b3c4SGreg Roach
1305bbfbb82SGreg Roach        // Merge hit counters
1315bbfbb82SGreg Roach        $hits = DB::table('hit_counter')
1325bbfbb82SGreg Roach            ->where('gedcom_id', '=', $tree->id())
1335bbfbb82SGreg Roach            ->whereIn('page_parameter', [$xref1, $xref2])
1345bbfbb82SGreg Roach            ->groupBy(['page_name'])
135*11225d5eSGreg Roach            ->pluck(new Expression('SUM(page_count) AS total'), 'page_name');
1365bbfbb82SGreg Roach
1375bbfbb82SGreg Roach        foreach ($hits as $page_name => $page_count) {
1385bbfbb82SGreg Roach            DB::table('hit_counter')
1395bbfbb82SGreg Roach                ->where('gedcom_id', '=', $tree->id())
1405bbfbb82SGreg Roach                ->where('page_name', '=', $page_name)
1414d74848fSGreg Roach                ->where('page_parameter', '=', $xref1)
1425bbfbb82SGreg Roach                ->update(['page_count' => $page_count]);
1435bbfbb82SGreg Roach        }
1445bbfbb82SGreg Roach
1455bbfbb82SGreg Roach        DB::table('hit_counter')
1465bbfbb82SGreg Roach            ->where('gedcom_id', '=', $tree->id())
1475bbfbb82SGreg Roach            ->where('page_parameter', '=', $xref2)
1485bbfbb82SGreg Roach            ->delete();
1495bbfbb82SGreg Roach
15002467d32SGreg Roach        $gedcom = '0 @' . $record1->xref() . '@ ' . $record1->tag();
1515bbfbb82SGreg Roach
1525bbfbb82SGreg Roach        foreach ($record1->facts() as $fact) {
1535bbfbb82SGreg Roach            if (in_array($fact->id(), $keep1, true)) {
1545bbfbb82SGreg Roach                $gedcom .= "\n" . $fact->gedcom();
1555bbfbb82SGreg Roach            }
1565bbfbb82SGreg Roach        }
1575bbfbb82SGreg Roach
1585bbfbb82SGreg Roach        foreach ($record2->facts() as $fact) {
1595bbfbb82SGreg Roach            if (in_array($fact->id(), $keep2, true)) {
1605bbfbb82SGreg Roach                $gedcom .= "\n" . $fact->gedcom();
1615bbfbb82SGreg Roach            }
1625bbfbb82SGreg Roach        }
1635bbfbb82SGreg Roach
1645bbfbb82SGreg Roach        DB::table('favorite')
1655bbfbb82SGreg Roach            ->where('gedcom_id', '=', $tree->id())
1665bbfbb82SGreg Roach            ->where('xref', '=', $xref2)
1675bbfbb82SGreg Roach            ->update(['xref' => $xref1]);
1685bbfbb82SGreg Roach
1695bbfbb82SGreg Roach        $record1->updateRecord($gedcom, true);
1705bbfbb82SGreg Roach        $record2->deleteRecord();
1715bbfbb82SGreg Roach
1725bbfbb82SGreg Roach        /* I18N: Records are individuals, sources, etc. */
1735bbfbb82SGreg Roach        FlashMessages::addMessage(I18N::translate(
1745bbfbb82SGreg Roach            'The records “%1$s” and “%2$s” have been merged.',
1755bbfbb82SGreg Roach            '<a class="alert-link" href="' . e($record1->url()) . '">' . $record1->fullName() . '</a>',
1765bbfbb82SGreg Roach            $record2_name
1775bbfbb82SGreg Roach        ), 'success');
1785bbfbb82SGreg Roach
17941413128SGreg Roach        return redirect(route(ManageTrees::class, ['tree' => $tree->name()]));
1805bbfbb82SGreg Roach    }
1815bbfbb82SGreg Roach}
182