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\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; 34use stdClass; 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::connection()->getTablePrefix(); 84 85 $query = DB::table('media') 86 ->join('media_file', static function (JoinClause $join): void { 87 $join 88 ->on('media_file.m_file', '=', 'media.m_file') 89 ->on('media_file.m_id', '=', 'media.m_id'); 90 }) 91 ->join('link', static function (JoinClause $join): void { 92 $join 93 ->on('link.l_file', '=', 'media.m_file') 94 ->on('link.l_to', '=', 'media.m_id') 95 ->where('link.l_type', '=', 'OBJE'); 96 }) 97 ->join('individuals', static function (JoinClause $join): void { 98 $join 99 ->on('individuals.i_file', '=', 'link.l_file') 100 ->on('individuals.i_id', '=', 'link.l_from'); 101 }) 102 ->where('i_gedcom', 'LIKE', new Expression("('%\n1 OBJE @' || " . $prefix . "media.m_id || '@%')")) 103 ->orderBy('individuals.i_file') 104 ->orderBy('individuals.i_id') 105 ->orderBy('media.m_id') 106 ->where('descriptive_title', 'LIKE', '%' . addcslashes($request->getQueryParams()['search']['value'] ?? '', '\\%_') . '%') 107 ->select(['media.m_file', 'media.m_id', 'media.m_gedcom', 'individuals.i_id', 'individuals.i_gedcom']); 108 109 return $this->datatables_service->handleQuery($request, $query, [], [], function (stdClass $datum) use ($ignore_facts): array { 110 $tree = $this->tree_service->find((int) $datum->m_file); 111 $media = Registry::mediaFactory()->make($datum->m_id, $tree, $datum->m_gedcom); 112 $individual = Registry::individualFactory()->make($datum->i_id, $tree, $datum->i_gedcom); 113 114 $facts = $individual->facts([], true) 115 ->filter(static function (Fact $fact) use ($ignore_facts): bool { 116 return 117 !$fact->isPendingDeletion() && 118 !preg_match('/^@' . Gedcom::REGEX_XREF . '@$/', $fact->value()) && 119 !in_array($fact->tag(), $ignore_facts, true); 120 }); 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 function (Fact $fact) use ($individual, $media): string { 134 return view('admin/fix-level-0-media-action', [ 135 'fact' => $fact, 136 'individual' => $individual, 137 'media' => $media, 138 ]); 139 }); 140 141 return [ 142 $tree->name(), 143 $media->displayImage(100, 100, 'contain', ['class' => 'img-thumbnail']), 144 '<a href="' . e($media->url()) . '">' . $media->fullName() . '</a>', 145 '<a href="' . e($individual->url()) . '">' . $individual->fullName() . '</a>', 146 $facts->implode(' '), 147 ]; 148 }); 149 } 150} 151