Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
15.38% |
14 / 91 |
|
0.00% |
0 / 2 |
CRAP | |
0.00% |
0 / 1 |
| ThumbnailImage | |
15.56% |
14 / 90 |
|
0.00% |
0 / 2 |
571.95 | |
0.00% |
0 / 1 |
| __construct | |
60.87% |
14 / 23 |
|
0.00% |
0 / 1 |
6.50 | |||
| toHtml | |
0.00% |
0 / 67 |
|
0.00% |
0 / 1 |
650 | |||
| 1 | <?php |
| 2 | /** |
| 3 | * Base class for the output of file transformation methods. |
| 4 | * |
| 5 | * @license GPL-2.0-or-later |
| 6 | * @file |
| 7 | * @ingroup Media |
| 8 | */ |
| 9 | |
| 10 | namespace MediaWiki\Media; |
| 11 | |
| 12 | use InvalidArgumentException; |
| 13 | use MediaWiki\FileRepo\File\File; |
| 14 | use MediaWiki\HookContainer\HookRunner; |
| 15 | use MediaWiki\Html\Html; |
| 16 | use MediaWiki\MainConfigNames; |
| 17 | use MediaWiki\MediaWikiServices; |
| 18 | use MediaWiki\Title\Title; |
| 19 | |
| 20 | /** |
| 21 | * Media transform output for images |
| 22 | * |
| 23 | * @ingroup Media |
| 24 | */ |
| 25 | class ThumbnailImage extends MediaTransformOutput { |
| 26 | /** |
| 27 | * Get a thumbnail object from a file and parameters. |
| 28 | * |
| 29 | * @param File $file |
| 30 | * @param string $url URL path to the thumb |
| 31 | * @param string|null|false $path Filesystem path to the thumb |
| 32 | * If $path is set to null, the output file is treated as a source copy. |
| 33 | * If $path is set to false, no output file will be created. |
| 34 | * @param array $parameters Associative array of parameters |
| 35 | * - 'width' and 'height': Required. |
| 36 | * - 'physicalWidth' and 'physicalHeight': Required, usually set by |
| 37 | * ImageHandler::normaliseParams or a subclass override. |
| 38 | * - 'page': Optional, for multipage files. |
| 39 | * - 'usePhysicalSize': Optional, used by ApiQueryImageInfo. |
| 40 | */ |
| 41 | public function __construct( $file, $url, $path = false, $parameters = [] ) { |
| 42 | // Previous parameters: |
| 43 | // $file, $url, $width, $height, $path = false, $page = false |
| 44 | |
| 45 | $defaults = [ |
| 46 | 'page' => false, |
| 47 | 'lang' => false |
| 48 | ]; |
| 49 | |
| 50 | if ( is_array( $parameters ) ) { |
| 51 | $actualParams = $parameters + $defaults; |
| 52 | } else { |
| 53 | // Using old format, should convert. Later a warning could be added here. |
| 54 | $numArgs = func_num_args(); |
| 55 | $actualParams = [ |
| 56 | 'width' => $path, |
| 57 | 'height' => $parameters, |
| 58 | 'page' => ( $numArgs > 5 ) ? func_get_arg( 5 ) : false |
| 59 | ] + $defaults; |
| 60 | $path = ( $numArgs > 4 ) ? func_get_arg( 4 ) : false; |
| 61 | } |
| 62 | |
| 63 | if ( $actualParams['usePhysicalSize'] ?? false ) { |
| 64 | $actualParams['width'] = $actualParams['physicalWidth']; |
| 65 | $actualParams['height'] = $actualParams['physicalHeight']; |
| 66 | } |
| 67 | |
| 68 | $this->file = $file; |
| 69 | $this->url = $url; |
| 70 | $this->path = $path; |
| 71 | |
| 72 | // These should be integers when they get here. |
| 73 | // If not, there's a bug somewhere. But let's at |
| 74 | // least produce valid HTML code regardless. |
| 75 | $this->width = (int)round( $actualParams['width'] ); |
| 76 | $this->height = (int)round( $actualParams['height'] ); |
| 77 | |
| 78 | $this->page = $actualParams['page']; |
| 79 | $this->lang = $actualParams['lang']; |
| 80 | } |
| 81 | |
| 82 | /** |
| 83 | * Return HTML `<img ... />` tag for the thumbnail, will include |
| 84 | * width and height attributes and a blank alt text (as required). |
| 85 | * |
| 86 | * @param array $options Associative array of options. Boolean options |
| 87 | * should be indicated with a value of true for true, and false or |
| 88 | * absent for false. |
| 89 | * |
| 90 | * - alt : HTML alt attribute |
| 91 | * - title : HTML title attribute |
| 92 | * - desc-link : Boolean, show a description link |
| 93 | * - file-link : Boolean, show a file download link |
| 94 | * - valign : vertical-align property, if the output is an inline element |
| 95 | * - img-class : Class applied to the `<img>` tag, if there is such a tag |
| 96 | * - loading : Specify an explicit browser loading strategy for images and iframes. |
| 97 | * - desc-query : String, description link query params |
| 98 | * - override-width : Override width attribute. Should generally not set |
| 99 | * - override-height : Override height attribute. Should generally not set |
| 100 | * - no-dimensions : Boolean, skip width and height attributes (useful if |
| 101 | * : set in CSS) |
| 102 | * - custom-url-link : Custom URL to link to |
| 103 | * - custom-title-link : Custom Title object to link to |
| 104 | * - custom-title-link-query : Querystring parameters array, for custom-title-link |
| 105 | * - custom-target-link : Value of the target attribute, for custom-url-link |
| 106 | * - parser-extlink-* : Attributes added by parser for external links: |
| 107 | * - parser-extlink-rel: add rel="nofollow" |
| 108 | * - parser-extlink-target: link target, but overridden by custom-target-link |
| 109 | * - magnify-resource : To set the HTML resource attribute, when necessary |
| 110 | * |
| 111 | * For images, desc-link and file-link are implemented as a click-through. For |
| 112 | * sounds and videos, they may be displayed in other ways. |
| 113 | * |
| 114 | * @return string |
| 115 | */ |
| 116 | public function toHtml( $options = [] ) { |
| 117 | $services = MediaWikiServices::getInstance(); |
| 118 | $mainConfig = $services->getMainConfig(); |
| 119 | $nativeImageLazyLoading = $mainConfig->get( MainConfigNames::NativeImageLazyLoading ); |
| 120 | |
| 121 | if ( func_num_args() === 2 ) { |
| 122 | throw new InvalidArgumentException( __METHOD__ . ' called in the old style' ); |
| 123 | } |
| 124 | |
| 125 | $query = $options['desc-query'] ?? ''; |
| 126 | |
| 127 | $attribs = []; |
| 128 | |
| 129 | // An empty alt indicates an image is not a key part of the content and |
| 130 | // that non-visual browsers may omit it from rendering. Only set the |
| 131 | // parameter if it's explicitly requested. |
| 132 | if ( isset( $options['alt'] ) ) { |
| 133 | $attribs['alt'] = $options['alt']; |
| 134 | } |
| 135 | |
| 136 | // Description links get the mw-file-description class and link |
| 137 | // to the file description page, making the resource redundant |
| 138 | if ( |
| 139 | isset( $options['magnify-resource'] ) && |
| 140 | !( $options['desc-link'] ?? false ) |
| 141 | ) { |
| 142 | $attribs['resource'] = $options['magnify-resource']; |
| 143 | } |
| 144 | |
| 145 | $attribs += [ |
| 146 | 'src' => $this->getUrl(), |
| 147 | 'decoding' => 'async', |
| 148 | ]; |
| 149 | |
| 150 | if ( $options['loading'] ?? $nativeImageLazyLoading ) { |
| 151 | $attribs['loading'] = $options['loading'] ?? 'lazy'; |
| 152 | } |
| 153 | |
| 154 | if ( !empty( $options['custom-url-link'] ) ) { |
| 155 | $linkAttribs = [ 'href' => $options['custom-url-link'] ]; |
| 156 | if ( !empty( $options['title'] ) ) { |
| 157 | $linkAttribs['title'] = $options['title']; |
| 158 | } |
| 159 | if ( !empty( $options['custom-target-link'] ) ) { |
| 160 | $linkAttribs['target'] = $options['custom-target-link']; |
| 161 | } elseif ( !empty( $options['parser-extlink-target'] ) ) { |
| 162 | $linkAttribs['target'] = $options['parser-extlink-target']; |
| 163 | } |
| 164 | if ( !empty( $options['parser-extlink-rel'] ) ) { |
| 165 | $linkAttribs['rel'] = $options['parser-extlink-rel']; |
| 166 | } |
| 167 | } elseif ( !empty( $options['custom-title-link'] ) ) { |
| 168 | /** @var Title $title */ |
| 169 | $title = $options['custom-title-link']; |
| 170 | $linkAttribs = [ |
| 171 | 'href' => $title->getLinkURL( $options['custom-title-link-query'] ?? null ), |
| 172 | 'title' => empty( $options['title'] ) ? $title->getPrefixedText() : $options['title'] |
| 173 | ]; |
| 174 | } elseif ( !empty( $options['desc-link'] ) ) { |
| 175 | $linkAttribs = $this->getDescLinkAttribs( |
| 176 | empty( $options['title'] ) ? null : $options['title'], |
| 177 | $query |
| 178 | ); |
| 179 | } elseif ( !empty( $options['file-link'] ) ) { |
| 180 | $linkAttribs = [ 'href' => $this->file->getUrl() ]; |
| 181 | } else { |
| 182 | $linkAttribs = false; |
| 183 | if ( !empty( $options['title'] ) ) { |
| 184 | $linkAttribs = [ 'title' => $options['title'] ]; |
| 185 | } |
| 186 | } |
| 187 | |
| 188 | if ( empty( $options['no-dimensions'] ) ) { |
| 189 | $attribs['width'] = $this->width; |
| 190 | $attribs['height'] = $this->height; |
| 191 | } |
| 192 | $style = ''; |
| 193 | if ( !empty( $options['valign'] ) ) { |
| 194 | $style .= "vertical-align: {$options['valign']};"; |
| 195 | } |
| 196 | if ( !empty( $options['style'] ) ) { |
| 197 | $style .= $options['style']; |
| 198 | } |
| 199 | $style = trim( $style ); |
| 200 | if ( $style ) { |
| 201 | $attribs['style'] = $style; |
| 202 | } |
| 203 | if ( !empty( $options['img-class'] ) ) { |
| 204 | $attribs['class'] = $options['img-class']; |
| 205 | } |
| 206 | if ( isset( $options['override-height'] ) ) { |
| 207 | $attribs['height'] = $options['override-height']; |
| 208 | } |
| 209 | if ( isset( $options['override-width'] ) ) { |
| 210 | $attribs['width'] = $options['override-width']; |
| 211 | } |
| 212 | |
| 213 | // Additional densities for responsive images, if specified. |
| 214 | // If any of these urls is the same as src url, it'll be excluded. |
| 215 | $responsiveUrls = array_diff( $this->responsiveUrls, [ $this->getUrl() ] ); |
| 216 | if ( $responsiveUrls ) { |
| 217 | $attribs['srcset'] = Html::srcSet( $responsiveUrls ); |
| 218 | } |
| 219 | |
| 220 | ( new HookRunner( $services->getHookContainer() ) ) |
| 221 | ->onThumbnailBeforeProduceHTML( $this, $attribs, $linkAttribs ); |
| 222 | |
| 223 | return $this->linkWrap( $linkAttribs, Html::element( 'img', $attribs ) ); |
| 224 | } |
| 225 | } |
| 226 | |
| 227 | /** @deprecated class alias since 1.46 */ |
| 228 | class_alias( ThumbnailImage::class, 'ThumbnailImage' ); |