Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
15.29% covered (danger)
15.29%
13 / 85
0.00% covered (danger)
0.00%
0 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
ThumbnailImage
15.29% covered (danger)
15.29%
13 / 85
0.00% covered (danger)
0.00%
0 / 2
540.14
0.00% covered (danger)
0.00%
0 / 1
 __construct
65.00% covered (warning)
65.00%
13 / 20
0.00% covered (danger)
0.00%
0 / 1
4.69
 toHtml
0.00% covered (danger)
0.00%
0 / 65
0.00% covered (danger)
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
25use MediaWiki\HookContainer\HookRunner;
26use MediaWiki\Html\Html;
27use MediaWiki\MainConfigNames;
28use MediaWiki\MediaWikiServices;
29use MediaWiki\Title\Title;
30use MediaWiki\Xml\Xml;
31
32/**
33 * Media transform output for images
34 *
35 * @ingroup Media
36 */
37class 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}