18f5f5da8SGreg Roach<?php 28f5f5da8SGreg Roach/** 38f5f5da8SGreg Roach * webtrees: online genealogy 48f5f5da8SGreg Roach * Copyright (C) 2017 webtrees development team 58f5f5da8SGreg Roach * This program is free software: you can redistribute it and/or modify 68f5f5da8SGreg Roach * it under the terms of the GNU General Public License as published by 78f5f5da8SGreg Roach * the Free Software Foundation, either version 3 of the License, or 88f5f5da8SGreg Roach * (at your option) any later version. 98f5f5da8SGreg Roach * This program is distributed in the hope that it will be useful, 108f5f5da8SGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of 118f5f5da8SGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 128f5f5da8SGreg Roach * GNU General Public License for more details. 138f5f5da8SGreg Roach * You should have received a copy of the GNU General Public License 148f5f5da8SGreg Roach * along with this program. If not, see <http://www.gnu.org/licenses/>. 158f5f5da8SGreg Roach */ 168f5f5da8SGreg Roachnamespace Fisharebest\Webtrees; 178f5f5da8SGreg Roach 188f5f5da8SGreg Roachuse Fisharebest\Webtrees\Functions\FunctionsPrintFacts; 198f5f5da8SGreg Roachuse League\Glide\Urls\UrlBuilderFactory; 208f5f5da8SGreg Roach 218f5f5da8SGreg Roach/** 228f5f5da8SGreg Roach * A GEDCOM media file. A media object can contain many media files, 238f5f5da8SGreg Roach * such as scans of both sides of a document, the transcript of an audio 248f5f5da8SGreg Roach * recording, etc. 258f5f5da8SGreg Roach */ 268f5f5da8SGreg Roachclass MediaFile { 278f5f5da8SGreg Roach /** @var string The filename */ 288f5f5da8SGreg Roach private $multimedia_file_refn = ''; 298f5f5da8SGreg Roach 308f5f5da8SGreg Roach /** @var string The file extension; jpeg, txt, mp4, etc. */ 318f5f5da8SGreg Roach private $multimedia_format = ''; 328f5f5da8SGreg Roach 338f5f5da8SGreg Roach /** @var string The type of document; newspaper, microfiche, etc. */ 348f5f5da8SGreg Roach private $source_media_type = ''; 358f5f5da8SGreg Roach /** @var string The filename */ 368f5f5da8SGreg Roach 378f5f5da8SGreg Roach /** @var string The name of the document */ 388f5f5da8SGreg Roach private $descriptive_title = ''; 398f5f5da8SGreg Roach 408f5f5da8SGreg Roach /** @var Media $media The media object to which this file belongs */ 418f5f5da8SGreg Roach private $media; 428f5f5da8SGreg Roach 438f5f5da8SGreg Roach /** 448f5f5da8SGreg Roach * Create a MediaFile from raw GEDCOM data. 458f5f5da8SGreg Roach * 468f5f5da8SGreg Roach * @param string $gedcom 478f5f5da8SGreg Roach * @param Media $media 488f5f5da8SGreg Roach */ 498f5f5da8SGreg Roach public function __construct($gedcom, Media $media) { 508f5f5da8SGreg Roach $this->media = $media; 518f5f5da8SGreg Roach 528f5f5da8SGreg Roach if (preg_match('/^\d FILE (.+)/m', $gedcom, $match)) { 538f5f5da8SGreg Roach $this->multimedia_file_refn = $match[1]; 548f5f5da8SGreg Roach } 558f5f5da8SGreg Roach 568f5f5da8SGreg Roach if (preg_match('/^\d FORM (.+)/m', $gedcom, $match)) { 578f5f5da8SGreg Roach $this->multimedia_format = $match[1]; 588f5f5da8SGreg Roach } 598f5f5da8SGreg Roach 608f5f5da8SGreg Roach if (preg_match('/^\d TYPE (.+)/m', $gedcom, $match)) { 618f5f5da8SGreg Roach $this->source_media_type = $match[1]; 628f5f5da8SGreg Roach } 638f5f5da8SGreg Roach 648f5f5da8SGreg Roach if (preg_match('/^\d TITL (.+)/m', $gedcom, $match)) { 658f5f5da8SGreg Roach $this->descriptive_title = $match[1]; 668f5f5da8SGreg Roach } 678f5f5da8SGreg Roach } 688f5f5da8SGreg Roach 698f5f5da8SGreg Roach /** 708f5f5da8SGreg Roach * Get the filename 718f5f5da8SGreg Roach * 728f5f5da8SGreg Roach * @return string 738f5f5da8SGreg Roach */ 74*6e6a9947SGreg Roach public function filename(): string { 75*6e6a9947SGreg Roach return $this->multimedia_file_refn; 768f5f5da8SGreg Roach } 778f5f5da8SGreg Roach 788f5f5da8SGreg Roach /** 798f5f5da8SGreg Roach * Get the format 808f5f5da8SGreg Roach * 818f5f5da8SGreg Roach * @return string 828f5f5da8SGreg Roach */ 83*6e6a9947SGreg Roach public function format(): string { 848f5f5da8SGreg Roach return $this->multimedia_format; 858f5f5da8SGreg Roach } 868f5f5da8SGreg Roach 878f5f5da8SGreg Roach /** 888f5f5da8SGreg Roach * Get the type 898f5f5da8SGreg Roach * 908f5f5da8SGreg Roach * @return string 918f5f5da8SGreg Roach */ 92*6e6a9947SGreg Roach public function type(): string { 938f5f5da8SGreg Roach return $this->source_media_type; 948f5f5da8SGreg Roach } 958f5f5da8SGreg Roach 968f5f5da8SGreg Roach /** 978f5f5da8SGreg Roach * Get the title 988f5f5da8SGreg Roach * 998f5f5da8SGreg Roach * @return string 1008f5f5da8SGreg Roach */ 101*6e6a9947SGreg Roach public function title(): string { 1028f5f5da8SGreg Roach return $this->descriptive_title; 1038f5f5da8SGreg Roach } 1048f5f5da8SGreg Roach 1058f5f5da8SGreg Roach /** 1068f5f5da8SGreg Roach * Get the filename on the server - for those (very few!) functions which actually 1078f5f5da8SGreg Roach * need the filename, such as mediafirewall.php and the PDF reports. 1088f5f5da8SGreg Roach * 1098f5f5da8SGreg Roach * @return string 1108f5f5da8SGreg Roach */ 1118f5f5da8SGreg Roach public function getServerFilename() { 1128f5f5da8SGreg Roach $MEDIA_DIRECTORY = $this->media->getTree()->getPreference('MEDIA_DIRECTORY'); 1138f5f5da8SGreg Roach 1148f5f5da8SGreg Roach if ($this->isExternal() || !$this->file) { 1158f5f5da8SGreg Roach // External image, or (in the case of corrupt GEDCOM data) no image at all 1168f5f5da8SGreg Roach return $this->file; 1178f5f5da8SGreg Roach } else { 1188f5f5da8SGreg Roach // Main image 1198f5f5da8SGreg Roach return WT_DATA_DIR . $MEDIA_DIRECTORY . $this->file; 1208f5f5da8SGreg Roach } 1218f5f5da8SGreg Roach } 1228f5f5da8SGreg Roach 1238f5f5da8SGreg Roach /** 1248f5f5da8SGreg Roach * check if the file exists on this server 1258f5f5da8SGreg Roach * 1268f5f5da8SGreg Roach * @return bool 1278f5f5da8SGreg Roach */ 1288f5f5da8SGreg Roach public function fileExists() { 1298f5f5da8SGreg Roach return file_exists($this->getServerFilename()); 1308f5f5da8SGreg Roach } 1318f5f5da8SGreg Roach 1328f5f5da8SGreg Roach /** 1338f5f5da8SGreg Roach * Determine if the file is an external url 1348f5f5da8SGreg Roach * 1358f5f5da8SGreg Roach * @return bool 1368f5f5da8SGreg Roach */ 1378f5f5da8SGreg Roach public function isExternal() { 1388f5f5da8SGreg Roach return strpos($this->file, '://') !== false; 1398f5f5da8SGreg Roach } 1408f5f5da8SGreg Roach 1418f5f5da8SGreg Roach /** 1428f5f5da8SGreg Roach * get the media file size in KB 1438f5f5da8SGreg Roach * 1448f5f5da8SGreg Roach * @return string 1458f5f5da8SGreg Roach */ 1468f5f5da8SGreg Roach public function getFilesize() { 1478f5f5da8SGreg Roach $size = $this->getFilesizeraw(); 1488f5f5da8SGreg Roach // Round up to the nearest KB. 1498f5f5da8SGreg Roach $size = (int) (($size + 1023) / 1024); 1508f5f5da8SGreg Roach 1518f5f5da8SGreg Roach return /* I18N: size of file in KB */ 1528f5f5da8SGreg Roach I18N::translate('%s KB', I18N::number($size)); 1538f5f5da8SGreg Roach } 1548f5f5da8SGreg Roach 1558f5f5da8SGreg Roach /** 1568f5f5da8SGreg Roach * get the media file size, unformatted 1578f5f5da8SGreg Roach * 1588f5f5da8SGreg Roach * @return int 1598f5f5da8SGreg Roach */ 1608f5f5da8SGreg Roach public function getFilesizeraw() { 1618f5f5da8SGreg Roach try { 1628f5f5da8SGreg Roach return filesize($this->getServerFilename()); 1638f5f5da8SGreg Roach } catch (\ErrorException $ex) { 1648f5f5da8SGreg Roach DebugBar::addThrowable($ex); 1658f5f5da8SGreg Roach 1668f5f5da8SGreg Roach return 0; 1678f5f5da8SGreg Roach } 1688f5f5da8SGreg Roach } 1698f5f5da8SGreg Roach 1708f5f5da8SGreg Roach /** 1718f5f5da8SGreg Roach * Deprecated? This does not need to be a function here. 1728f5f5da8SGreg Roach * 1738f5f5da8SGreg Roach * @return string 1748f5f5da8SGreg Roach */ 1758f5f5da8SGreg Roach public function getMediaType() { 1768f5f5da8SGreg Roach if (preg_match('/\n\d TYPE (.+)/', $this->gedcom, $match)) { 1778f5f5da8SGreg Roach return strtolower($match[1]); 1788f5f5da8SGreg Roach } else { 1798f5f5da8SGreg Roach return ''; 1808f5f5da8SGreg Roach } 1818f5f5da8SGreg Roach } 1828f5f5da8SGreg Roach 1838f5f5da8SGreg Roach /** 1848f5f5da8SGreg Roach * get image properties 1858f5f5da8SGreg Roach * 1868f5f5da8SGreg Roach * @return array 1878f5f5da8SGreg Roach */ 1888f5f5da8SGreg Roach public function getImageAttributes() { 1898f5f5da8SGreg Roach $imgsize = []; 1908f5f5da8SGreg Roach if ($this->fileExists()) { 1918f5f5da8SGreg Roach try { 1928f5f5da8SGreg Roach $imgsize = getimagesize($this->getServerFilename()); 1938f5f5da8SGreg Roach if (is_array($imgsize) && !empty($imgsize['0'])) { 1948f5f5da8SGreg Roach // this is an image 1958f5f5da8SGreg Roach $imageTypes = ['', 'GIF', 'JPG', 'PNG', 'SWF', 'PSD', 'BMP', 'TIFF', 'TIFF', 'JPC', 'JP2', 'JPX', 'JB2', 'SWC', 'IFF', 'WBMP', 'XBM']; 1968f5f5da8SGreg Roach $imgsize['ext'] = $imageTypes[0 + $imgsize[2]]; 1978f5f5da8SGreg Roach // this is for display purposes, always show non-adjusted info 1988f5f5da8SGreg Roach $imgsize['WxH'] = /* I18N: image dimensions, width × height */ 1998f5f5da8SGreg Roach I18N::translate('%1$s × %2$s pixels', I18N::number($imgsize['0']), I18N::number($imgsize['1'])); 2008f5f5da8SGreg Roach } 2018f5f5da8SGreg Roach } catch (\ErrorException $ex) { 2028f5f5da8SGreg Roach DebugBar::addThrowable($ex); 2038f5f5da8SGreg Roach 2048f5f5da8SGreg Roach // Not an image, or not a valid image? 2058f5f5da8SGreg Roach $imgsize = false; 2068f5f5da8SGreg Roach } 2078f5f5da8SGreg Roach } 2088f5f5da8SGreg Roach 2098f5f5da8SGreg Roach if (!is_array($imgsize) || empty($imgsize['0'])) { 2108f5f5da8SGreg Roach // this is not an image, OR the file doesn’t exist OR it is a url 2118f5f5da8SGreg Roach $imgsize[0] = 0; 2128f5f5da8SGreg Roach $imgsize[1] = 0; 2138f5f5da8SGreg Roach $imgsize['ext'] = ''; 2148f5f5da8SGreg Roach $imgsize['mime'] = ''; 2158f5f5da8SGreg Roach $imgsize['WxH'] = ''; 2168f5f5da8SGreg Roach } 2178f5f5da8SGreg Roach 2188f5f5da8SGreg Roach if (empty($imgsize['mime'])) { 2198f5f5da8SGreg Roach // this is not an image, OR the file doesn’t exist OR it is a url 2208f5f5da8SGreg Roach // set file type equal to the file extension - can’t use parse_url because this may not be a full url 2218f5f5da8SGreg Roach $exp = explode('?', $this->file); 2228f5f5da8SGreg Roach $imgsize['ext'] = strtoupper(pathinfo($exp[0], PATHINFO_EXTENSION)); 2238f5f5da8SGreg Roach // all mimetypes we wish to serve with the media firewall must be added to this array. 2248f5f5da8SGreg Roach $mime = [ 2258f5f5da8SGreg Roach 'DOC' => 'application/msword', 2268f5f5da8SGreg Roach 'MOV' => 'video/quicktime', 2278f5f5da8SGreg Roach 'MP3' => 'audio/mpeg', 2288f5f5da8SGreg Roach 'PDF' => 'application/pdf', 2298f5f5da8SGreg Roach 'PPT' => 'application/vnd.ms-powerpoint', 2308f5f5da8SGreg Roach 'RTF' => 'text/rtf', 2318f5f5da8SGreg Roach 'SID' => 'image/x-mrsid', 2328f5f5da8SGreg Roach 'TXT' => 'text/plain', 2338f5f5da8SGreg Roach 'XLS' => 'application/vnd.ms-excel', 2348f5f5da8SGreg Roach 'WMV' => 'video/x-ms-wmv', 2358f5f5da8SGreg Roach ]; 2368f5f5da8SGreg Roach if (empty($mime[$imgsize['ext']])) { 2378f5f5da8SGreg Roach // if we don’t know what the mimetype is, use something ambiguous 2388f5f5da8SGreg Roach $imgsize['mime'] = 'application/octet-stream'; 2398f5f5da8SGreg Roach if ($this->fileExists()) { 2408f5f5da8SGreg Roach // alert the admin if we cannot determine the mime type of an existing file 2418f5f5da8SGreg Roach // as the media firewall will be unable to serve this file properly 2428f5f5da8SGreg Roach Log::addMediaLog('Media Firewall error: >Unknown Mimetype< for file >' . $this->file . '<'); 2438f5f5da8SGreg Roach } 2448f5f5da8SGreg Roach } else { 2458f5f5da8SGreg Roach $imgsize['mime'] = $mime[$imgsize['ext']]; 2468f5f5da8SGreg Roach } 2478f5f5da8SGreg Roach } 2488f5f5da8SGreg Roach 2498f5f5da8SGreg Roach return $imgsize; 2508f5f5da8SGreg Roach } 2518f5f5da8SGreg Roach 2528f5f5da8SGreg Roach /** 2538f5f5da8SGreg Roach * Generate a URL for an image. 2548f5f5da8SGreg Roach * 2558f5f5da8SGreg Roach * @param int $width Maximum width in pixels 2568f5f5da8SGreg Roach * @param int $height Maximum height in pixels 2578f5f5da8SGreg Roach * @param string $fit "crop" or "contain" 2588f5f5da8SGreg Roach * 2598f5f5da8SGreg Roach * @return string 2608f5f5da8SGreg Roach */ 2618f5f5da8SGreg Roach public function imageUrl($width, $height, $fit) { 2628f5f5da8SGreg Roach // Sign the URL, to protect against mass-resize attacks. 2638f5f5da8SGreg Roach $glide_key = Site::getPreference('glide-key'); 2648f5f5da8SGreg Roach if (empty($glide_key)) { 2658f5f5da8SGreg Roach $glide_key = bin2hex(random_bytes(128)); 2668f5f5da8SGreg Roach Site::setPreference('glide-key', $glide_key); 2678f5f5da8SGreg Roach } 2688f5f5da8SGreg Roach 2698f5f5da8SGreg Roach if (Auth::accessLevel($this->getTree()) > $this->getTree()->getPreference('SHOW_NO_WATERMARK')) { 2708f5f5da8SGreg Roach $mark = 'watermark.png'; 2718f5f5da8SGreg Roach } else { 2728f5f5da8SGreg Roach $mark = ''; 2738f5f5da8SGreg Roach } 2748f5f5da8SGreg Roach 2758f5f5da8SGreg Roach $url = UrlBuilderFactory::create(WT_BASE_URL, $glide_key) 2768f5f5da8SGreg Roach ->getUrl('mediafirewall.php', [ 2778f5f5da8SGreg Roach 'mid' => $this->getXref(), 2788f5f5da8SGreg Roach 'ged' => $this->tree->getName(), 2798f5f5da8SGreg Roach 'w' => $width, 2808f5f5da8SGreg Roach 'h' => $height, 2818f5f5da8SGreg Roach 'fit' => $fit, 2828f5f5da8SGreg Roach 'mark' => $mark, 2838f5f5da8SGreg Roach 'markh' => '100h', 2848f5f5da8SGreg Roach 'markw' => '100w', 2858f5f5da8SGreg Roach 'markalpha' => 25, 2868f5f5da8SGreg Roach 'or' => 0, // Intervention uses exif_read_data() which is very buggy. 2878f5f5da8SGreg Roach ]); 2888f5f5da8SGreg Roach 2898f5f5da8SGreg Roach return $url; 2908f5f5da8SGreg Roach } 2918f5f5da8SGreg Roach 2928f5f5da8SGreg Roach /** 2938f5f5da8SGreg Roach * What file extension is used by this file? 2948f5f5da8SGreg Roach * 2958f5f5da8SGreg Roach * @return string 2968f5f5da8SGreg Roach */ 2978f5f5da8SGreg Roach public function extension() { 2988f5f5da8SGreg Roach if (preg_match('/\.([a-zA-Z0-9]+)$/', $this->file, $match)) { 2998f5f5da8SGreg Roach return strtolower($match[1]); 3008f5f5da8SGreg Roach } else { 3018f5f5da8SGreg Roach return ''; 3028f5f5da8SGreg Roach } 3038f5f5da8SGreg Roach } 3048f5f5da8SGreg Roach 3058f5f5da8SGreg Roach /** 3068f5f5da8SGreg Roach * What is the mime-type of this object? 3078f5f5da8SGreg Roach * For simplicity and efficiency, use the extension, rather than the contents. 3088f5f5da8SGreg Roach * 3098f5f5da8SGreg Roach * @return string 3108f5f5da8SGreg Roach */ 3118f5f5da8SGreg Roach public function mimeType() { 3128f5f5da8SGreg Roach // Themes contain icon definitions for some/all of these mime-types 3138f5f5da8SGreg Roach switch ($this->extension()) { 3148f5f5da8SGreg Roach case 'bmp': 3158f5f5da8SGreg Roach return 'image/bmp'; 3168f5f5da8SGreg Roach case 'doc': 3178f5f5da8SGreg Roach return 'application/msword'; 3188f5f5da8SGreg Roach case 'docx': 3198f5f5da8SGreg Roach return 'application/msword'; 3208f5f5da8SGreg Roach case 'ged': 3218f5f5da8SGreg Roach return 'text/x-gedcom'; 3228f5f5da8SGreg Roach case 'gif': 3238f5f5da8SGreg Roach return 'image/gif'; 3248f5f5da8SGreg Roach case 'htm': 3258f5f5da8SGreg Roach return 'text/html'; 3268f5f5da8SGreg Roach case 'html': 3278f5f5da8SGreg Roach return 'text/html'; 3288f5f5da8SGreg Roach case 'jpeg': 3298f5f5da8SGreg Roach return 'image/jpeg'; 3308f5f5da8SGreg Roach case 'jpg': 3318f5f5da8SGreg Roach return 'image/jpeg'; 3328f5f5da8SGreg Roach case 'mov': 3338f5f5da8SGreg Roach return 'video/quicktime'; 3348f5f5da8SGreg Roach case 'mp3': 3358f5f5da8SGreg Roach return 'audio/mpeg'; 3368f5f5da8SGreg Roach case 'mp4': 3378f5f5da8SGreg Roach return 'video/mp4'; 3388f5f5da8SGreg Roach case 'ogv': 3398f5f5da8SGreg Roach return 'video/ogg'; 3408f5f5da8SGreg Roach case 'pdf': 3418f5f5da8SGreg Roach return 'application/pdf'; 3428f5f5da8SGreg Roach case 'png': 3438f5f5da8SGreg Roach return 'image/png'; 3448f5f5da8SGreg Roach case 'rar': 3458f5f5da8SGreg Roach return 'application/x-rar-compressed'; 3468f5f5da8SGreg Roach case 'swf': 3478f5f5da8SGreg Roach return 'application/x-shockwave-flash'; 3488f5f5da8SGreg Roach case 'svg': 3498f5f5da8SGreg Roach return 'image/svg'; 3508f5f5da8SGreg Roach case 'tif': 3518f5f5da8SGreg Roach return 'image/tiff'; 3528f5f5da8SGreg Roach case 'tiff': 3538f5f5da8SGreg Roach return 'image/tiff'; 3548f5f5da8SGreg Roach case 'xls': 3558f5f5da8SGreg Roach return 'application/vnd-ms-excel'; 3568f5f5da8SGreg Roach case 'xlsx': 3578f5f5da8SGreg Roach return 'application/vnd-ms-excel'; 3588f5f5da8SGreg Roach case 'wmv': 3598f5f5da8SGreg Roach return 'video/x-ms-wmv'; 3608f5f5da8SGreg Roach case 'zip': 3618f5f5da8SGreg Roach return 'application/zip'; 3628f5f5da8SGreg Roach default: 3638f5f5da8SGreg Roach return 'application/octet-stream'; 3648f5f5da8SGreg Roach } 3658f5f5da8SGreg Roach } 3668f5f5da8SGreg Roach 3678f5f5da8SGreg Roach /** 3688f5f5da8SGreg Roach * Display an image-thumbnail or a media-icon, and add markup for image viewers such as colorbox. 3698f5f5da8SGreg Roach * 3708f5f5da8SGreg Roach * @param int $width Pixels 3718f5f5da8SGreg Roach * @param int $height Pixels 3728f5f5da8SGreg Roach * @param string $fit "crop" or "contain" 3738f5f5da8SGreg Roach * @param string[] $attributes Additional HTML attributes 3748f5f5da8SGreg Roach * 3758f5f5da8SGreg Roach * @return string 3768f5f5da8SGreg Roach */ 3778f5f5da8SGreg Roach public function displayImage($width, $height, $fit, $attributes = []) { 3788f5f5da8SGreg Roach // Default image for external, missing or corrupt images. 3798f5f5da8SGreg Roach $image 3808f5f5da8SGreg Roach = '<i' . 3818f5f5da8SGreg Roach ' dir="auto"' . // For the tool-tip 3828f5f5da8SGreg Roach ' class="icon-mime-' . str_replace('/', '-', $this->mimeType()) . '"' . 3838f5f5da8SGreg Roach ' title="' . strip_tags($this->getFullName()) . '"' . 3848f5f5da8SGreg Roach '></i>'; 3858f5f5da8SGreg Roach 3868f5f5da8SGreg Roach // Use a thumbnail image. 3878f5f5da8SGreg Roach if ($this->isExternal()) { 3888f5f5da8SGreg Roach $src = $this->getFilename(); 3898f5f5da8SGreg Roach $srcset = []; 3908f5f5da8SGreg Roach } else { 3918f5f5da8SGreg Roach // Generate multiple images for displays with higher pixel densities. 3928f5f5da8SGreg Roach $src = $this->imageUrl($width, $height, $fit); 3938f5f5da8SGreg Roach $srcset = []; 3948f5f5da8SGreg Roach foreach ([2, 3, 4] as $x) { 3958f5f5da8SGreg Roach $srcset[] = $this->imageUrl($width * $x, $height * $x, $fit) . ' ' . $x . 'x'; 3968f5f5da8SGreg Roach } 3978f5f5da8SGreg Roach } 3988f5f5da8SGreg Roach 3998f5f5da8SGreg Roach $image = '<img ' . Html::attributes($attributes + [ 4008f5f5da8SGreg Roach 'dir' => 'auto', 4018f5f5da8SGreg Roach 'src' => $src, 4028f5f5da8SGreg Roach 'srcset' => implode(',', $srcset), 4038f5f5da8SGreg Roach 'alt' => strip_tags($this->getFullName()), 4048f5f5da8SGreg Roach ]) . '>'; 4058f5f5da8SGreg Roach 4068f5f5da8SGreg Roach $attributes = Html::attributes([ 4078f5f5da8SGreg Roach 'class' => 'gallery', 4088f5f5da8SGreg Roach 'type' => $this->mimeType(), 4098f5f5da8SGreg Roach 'href' => $this->imageUrl(0, 0, ''), 4108f5f5da8SGreg Roach ]); 4118f5f5da8SGreg Roach 4128f5f5da8SGreg Roach return '<a ' . $attributes . '>' . $image . '</a>'; 4138f5f5da8SGreg Roach } 4148f5f5da8SGreg Roach} 415