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