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