1<?php 2 3/** 4 * webtrees: online genealogy 5 * Copyright (C) 2022 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\Fact; 23use Fisharebest\Webtrees\Gedcom; 24use Fisharebest\Webtrees\Registry; 25use Fisharebest\Webtrees\Services\DatatablesService; 26use Fisharebest\Webtrees\Services\TreeService; 27use Illuminate\Database\Capsule\Manager as DB; 28use Illuminate\Database\Query\Expression; 29use Illuminate\Database\Query\JoinClause; 30use Illuminate\Support\Collection; 31use Psr\Http\Message\ResponseInterface; 32use Psr\Http\Message\ServerRequestInterface; 33use Psr\Http\Server\RequestHandlerInterface; 34 35use function addcslashes; 36use function e; 37use function in_array; 38use function preg_match; 39use function view; 40 41/** 42 * Move media links from records to facts. 43 */ 44class FixLevel0MediaData implements RequestHandlerInterface 45{ 46 private DatatablesService $datatables_service; 47 48 private TreeService $tree_service; 49 50 /** 51 * FixLevel0MediaController constructor. 52 * 53 * @param DatatablesService $datatables_service 54 * @param TreeService $tree_service 55 */ 56 public function __construct(DatatablesService $datatables_service, TreeService $tree_service) 57 { 58 $this->datatables_service = $datatables_service; 59 $this->tree_service = $tree_service; 60 } 61 62 /** 63 * If media objects are wronly linked to top-level records, reattach them 64 * to facts/events. 65 * 66 * @param ServerRequestInterface $request 67 * 68 * @return ResponseInterface 69 */ 70 public function handle(ServerRequestInterface $request): ResponseInterface 71 { 72 $ignore_facts = [ 73 'INDI:NAME', 74 'INDI:SEX', 75 'INDI:CHAN', 76 'INDI:NOTE', 77 'INDI:SOUR', 78 'INDI:SUBM', 79 'INDI:RESN', 80 ]; 81 82 $prefix = DB::connection()->getTablePrefix(); 83 84 $query = DB::table('media') 85 ->join('media_file', static function (JoinClause $join): void { 86 $join 87 ->on('media_file.m_file', '=', 'media.m_file') 88 ->on('media_file.m_id', '=', 'media.m_id'); 89 }) 90 ->join('link', static function (JoinClause $join): void { 91 $join 92 ->on('link.l_file', '=', 'media.m_file') 93 ->on('link.l_to', '=', 'media.m_id') 94 ->where('link.l_type', '=', 'OBJE'); 95 }) 96 ->join('individuals', static function (JoinClause $join): void { 97 $join 98 ->on('individuals.i_file', '=', 'link.l_file') 99 ->on('individuals.i_id', '=', 'link.l_from'); 100 }) 101 ->where('i_gedcom', 'LIKE', new Expression("('%\n1 OBJE @' || " . $prefix . "media.m_id || '@%')")) 102 ->orderBy('individuals.i_file') 103 ->orderBy('individuals.i_id') 104 ->orderBy('media.m_id') 105 ->where('descriptive_title', 'LIKE', '%' . addcslashes($request->getQueryParams()['search']['value'] ?? '', '\\%_') . '%') 106 ->select(['media.m_file', 'media.m_id', 'media.m_gedcom', 'individuals.i_id', 'individuals.i_gedcom']); 107 108 return $this->datatables_service->handleQuery($request, $query, [], [], function (object $datum) use ($ignore_facts): array { 109 $tree = $this->tree_service->find((int) $datum->m_file); 110 $media = Registry::mediaFactory()->make($datum->m_id, $tree, $datum->m_gedcom); 111 $individual = Registry::individualFactory()->make($datum->i_id, $tree, $datum->i_gedcom); 112 113 $facts = $individual->facts([], true) 114 ->filter(static function (Fact $fact) use ($ignore_facts): bool { 115 return 116 !$fact->isPendingDeletion() && 117 !preg_match('/^@' . Gedcom::REGEX_XREF . '@$/', $fact->value()) && 118 !in_array($fact->tag(), $ignore_facts, true); 119 }); 120 121 // The link to the media object may have been deleted in a pending change. 122 $deleted = true; 123 foreach ($individual->facts(['OBJE']) as $fact) { 124 if ($fact->target() === $media && !$fact->isPendingDeletion()) { 125 $deleted = false; 126 } 127 } 128 if ($deleted) { 129 $facts = new Collection(); 130 } 131 132 $facts = $facts->map(static function (Fact $fact) use ($individual, $media): string { 133 return view('admin/fix-level-0-media-action', [ 134 'fact' => $fact, 135 'individual' => $individual, 136 'media' => $media, 137 ]); 138 }); 139 140 return [ 141 $tree->name(), 142 $media->displayImage(100, 100, 'contain', ['class' => 'img-thumbnail']), 143 '<a href="' . e($media->url()) . '">' . $media->fullName() . '</a>', 144 '<a href="' . e($individual->url()) . '">' . $individual->fullName() . '</a>', 145 $facts->implode(' '), 146 ]; 147 }); 148 } 149} 150