. */ declare(strict_types=1); namespace Fisharebest\Webtrees\Http\RequestHandlers; use Fisharebest\Webtrees\Auth; use Fisharebest\Webtrees\FlashMessages; use Fisharebest\Webtrees\I18N; use Fisharebest\Webtrees\Registry; use Fisharebest\Webtrees\Tree; use Fisharebest\Webtrees\User; use Illuminate\Database\Capsule\Manager as DB; use Illuminate\Database\Query\Expression; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\RequestHandlerInterface; use function assert; use function e; use function in_array; use function preg_replace; use function redirect; use function route; use function str_replace; /** * Merge records */ class MergeFactsAction implements RequestHandlerInterface { /** * @param ServerRequestInterface $request * * @return ResponseInterface */ public function handle(ServerRequestInterface $request): ResponseInterface { $tree = $request->getAttribute('tree'); assert($tree instanceof Tree); $params = (array) $request->getParsedBody(); $xref1 = $params['xref1'] ?? ''; $xref2 = $params['xref2'] ?? ''; $keep1 = $params['keep1'] ?? []; $keep2 = $params['keep2'] ?? []; // Merge record2 into record1 $record1 = Registry::gedcomRecordFactory()->make($xref1, $tree); $record2 = Registry::gedcomRecordFactory()->make($xref2, $tree); if ( $record1 === null || $record2 === null || $record1 === $record2 || $record1->tag() !== $record2->tag() || $record1->isPendingDeletion() || $record2->isPendingDeletion() ) { return redirect(route(MergeRecordsPage::class, [ 'tree' => $tree->name(), 'xref1' => $xref1, 'xref2' => $xref2, ])); } // If we are not auto-accepting, then we can show a link to the pending deletion if (Auth::user()->getPreference(User::PREF_AUTO_ACCEPT_EDITS) === '1') { $record2_name = $record2->fullName(); } else { $record2_name = '' . $record2->fullName() . ''; } // Update records that link to the one we will be removing. $linking_records = $record2->linkingRecords(); foreach ($linking_records as $record) { if (!$record->isPendingDeletion()) { /* I18N: The placeholders are the names of individuals, sources, etc. */ FlashMessages::addMessage(I18N::translate( 'The link from “%1$s” to “%2$s” has been updated.', '' . $record->fullName() . '', $record2_name ), 'info'); $gedcom = str_replace('@' . $xref2 . '@', '@' . $xref1 . '@', $record->gedcom()); $gedcom = preg_replace( '/(\n1.*@.+@.*(?:(?:\n[2-9].*)*))((?:\n1.*(?:\n[2-9].*)*)*\1)/', '$2', $gedcom ); $record->updateRecord($gedcom, true); } } // Update any linked user-accounts DB::table('user_gedcom_setting') ->where('gedcom_id', '=', $tree->id()) ->whereIn('setting_name', [User::PREF_TREE_ACCOUNT_XREF, User::PREF_TREE_DEFAULT_XREF]) ->where('setting_value', '=', $xref2) ->update(['setting_value' => $xref1]); // Merge hit counters $hits = DB::table('hit_counter') ->where('gedcom_id', '=', $tree->id()) ->whereIn('page_parameter', [$xref1, $xref2]) ->groupBy(['page_name']) ->pluck(new Expression('SUM(page_count)'), 'page_name'); foreach ($hits as $page_name => $page_count) { DB::table('hit_counter') ->where('gedcom_id', '=', $tree->id()) ->where('page_name', '=', $page_name) ->where('page_parameter', '=', $xref1) ->update(['page_count' => $page_count]); } DB::table('hit_counter') ->where('gedcom_id', '=', $tree->id()) ->where('page_parameter', '=', $xref2) ->delete(); $gedcom = '0 @' . $record1->xref() . '@ ' . $record1->tag(); foreach ($record1->facts() as $fact) { if (in_array($fact->id(), $keep1, true)) { $gedcom .= "\n" . $fact->gedcom(); } } foreach ($record2->facts() as $fact) { if (in_array($fact->id(), $keep2, true)) { $gedcom .= "\n" . $fact->gedcom(); } } DB::table('favorite') ->where('gedcom_id', '=', $tree->id()) ->where('xref', '=', $xref2) ->update(['xref' => $xref1]); $record1->updateRecord($gedcom, true); $record2->deleteRecord(); /* I18N: Records are individuals, sources, etc. */ FlashMessages::addMessage(I18N::translate( 'The records “%1$s” and “%2$s” have been merged.', '' . $record1->fullName() . '', $record2_name ), 'success'); return redirect(route(MergeRecordsPage::class, ['tree' => $tree->name()])); } }