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