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; 21 22use Fisharebest\Webtrees\Functions\FunctionsPrintFacts; 23use Fisharebest\Webtrees\Http\RequestHandlers\MediaPage; 24use Illuminate\Database\Capsule\Manager as DB; 25use Illuminate\Support\Collection; 26 27/** 28 * A GEDCOM media (OBJE) object. 29 */ 30class Media extends GedcomRecord 31{ 32 public const RECORD_TYPE = 'OBJE'; 33 34 protected const ROUTE_NAME = MediaPage::class; 35 36 /** 37 * Each object type may have its own special rules, and re-implement this function. 38 * 39 * @param int $access_level 40 * 41 * @return bool 42 */ 43 protected function canShowByType(int $access_level): bool 44 { 45 // Hide media objects if they are attached to private records 46 $linked_ids = DB::table('link') 47 ->where('l_file', '=', $this->tree->id()) 48 ->where('l_to', '=', $this->xref) 49 ->pluck('l_from'); 50 51 foreach ($linked_ids as $linked_id) { 52 $linked_record = Registry::gedcomRecordFactory()->make($linked_id, $this->tree); 53 if ($linked_record instanceof GedcomRecord && !$linked_record->canShow($access_level)) { 54 return false; 55 } 56 } 57 58 // ... otherwise apply default behavior 59 return parent::canShowByType($access_level); 60 } 61 62 /** 63 * Get the media files for this media object 64 * 65 * @return Collection<MediaFile> 66 */ 67 public function mediaFiles(): Collection 68 { 69 return $this->facts(['FILE']) 70 ->map(function (Fact $fact): MediaFile { 71 return new MediaFile($fact->gedcom(), $this); 72 }); 73 } 74 75 /** 76 * Get the first media file that contains an image. 77 * 78 * @return MediaFile|null 79 */ 80 public function firstImageFile(): ?MediaFile 81 { 82 return $this->mediaFiles() 83 ->first(static function (MediaFile $media_file): bool { 84 return $media_file->isImage() && !$media_file->isExternal(); 85 }); 86 } 87 88 /** 89 * Get the first note attached to this media object 90 * 91 * @return string 92 */ 93 public function getNote(): string 94 { 95 $fact = $this->facts(['NOTE'])->first(); 96 97 if ($fact instanceof Fact) { 98 // Link to note object 99 $note = $fact->target(); 100 if ($note instanceof Note) { 101 return $note->getNote(); 102 } 103 104 // Inline note 105 return $fact->value(); 106 } 107 108 return ''; 109 } 110 111 /** 112 * Extract names from the GEDCOM record. 113 * 114 * @return void 115 */ 116 public function extractNames(): void 117 { 118 $names = []; 119 foreach ($this->mediaFiles() as $media_file) { 120 $names[] = $media_file->title(); 121 } 122 foreach ($this->mediaFiles() as $media_file) { 123 $names[] = $media_file->filename(); 124 } 125 $names = array_filter(array_unique($names)); 126 127 if ($names === []) { 128 $names[] = $this->getFallBackName(); 129 } 130 131 foreach ($names as $name) { 132 $this->addName(static::RECORD_TYPE, $name, ''); 133 } 134 } 135 136 /** 137 * This function should be redefined in derived classes to show any major 138 * identifying characteristics of this record. 139 * 140 * @return string 141 */ 142 public function formatListDetails(): string 143 { 144 ob_start(); 145 FunctionsPrintFacts::printMediaLinks($this->tree(), '1 OBJE @' . $this->xref() . '@', 1); 146 147 return ob_get_clean(); 148 } 149 150 /** 151 * Display an image-thumbnail or a media-icon, and add markup for image viewers such as colorbox. 152 * 153 * @param int $width Pixels 154 * @param int $height Pixels 155 * @param string $fit "crop" or "contain" 156 * @param array<string,string> $attributes Additional HTML attributes 157 * 158 * @return string 159 */ 160 public function displayImage(int $width, int $height, string $fit, array $attributes = []): string 161 { 162 // Display the first image 163 foreach ($this->mediaFiles() as $media_file) { 164 if ($media_file->isImage()) { 165 return $media_file->displayImage($width, $height, $fit, $attributes); 166 } 167 } 168 169 // Display the first file of any type 170 $media_file = $this->mediaFiles()->first(); 171 172 if ($media_file instanceof MediaFile) { 173 return $media_file->displayImage($width, $height, $fit, $attributes); 174 } 175 176 // No image? 177 return ''; 178 } 179 180 /** 181 * Lock the database row, to prevent concurrent edits. 182 */ 183 public function lock(): void 184 { 185 DB::table('media') 186 ->where('m_file', '=', $this->tree->id()) 187 ->where('m_id', '=', $this->xref()) 188 ->lockForUpdate() 189 ->get(); 190 } 191} 192