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; 21 22use Fisharebest\Webtrees\Elements\XrefMedia; 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<int,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 123 if ($names === []) { 124 foreach ($this->mediaFiles() as $media_file) { 125 $names[] = $media_file->filename(); 126 } 127 } 128 129 $names = array_filter(array_unique($names)); 130 131 if ($names === []) { 132 $names[] = $this->getFallBackName(); 133 } 134 135 foreach ($names as $name) { 136 $this->addName(static::RECORD_TYPE, $name, ''); 137 } 138 } 139 140 /** 141 * This function should be redefined in derived classes to show any major 142 * identifying characteristics of this record. 143 * 144 * @return string 145 */ 146 public function formatListDetails(): string 147 { 148 return (new XrefMedia(I18N::translate('Media'))) 149 ->labelValue('@' . $this->xref . '@', $this->tree()); 150 } 151 152 /** 153 * Display an image-thumbnail or a media-icon, and add markup for image viewers such as colorbox. 154 * 155 * @param int $width Pixels 156 * @param int $height Pixels 157 * @param string $fit "crop" or "contain" 158 * @param array<string,string> $attributes Additional HTML attributes 159 * 160 * @return string 161 */ 162 public function displayImage(int $width, int $height, string $fit, array $attributes): string 163 { 164 // Display the first image 165 foreach ($this->mediaFiles() as $media_file) { 166 if ($media_file->isImage()) { 167 return $media_file->displayImage($width, $height, $fit, $attributes); 168 } 169 } 170 171 // Display the first file of any type 172 $media_file = $this->mediaFiles()->first(); 173 174 if ($media_file instanceof MediaFile) { 175 return $media_file->displayImage($width, $height, $fit, $attributes); 176 } 177 178 // No image? 179 return ''; 180 } 181 182 /** 183 * Lock the database row, to prevent concurrent edits. 184 */ 185 public function lock(): void 186 { 187 DB::table('media') 188 ->where('m_file', '=', $this->tree->id()) 189 ->where('m_id', '=', $this->xref()) 190 ->lockForUpdate() 191 ->get(); 192 } 193} 194