Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 68
0.00% covered (danger)
0.00%
0 / 23
CRAP
0.00% covered (danger)
0.00%
0 / 1
ImageGalleryBase
0.00% covered (danger)
0.00%
0 / 68
0.00% covered (danger)
0.00%
0 / 23
1332
0.00% covered (danger)
0.00%
0 / 1
 factory
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
20
 loadModes
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
6
 __construct
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
6
 setParser
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setHideBadImages
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setCaption
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setCaptionHtml
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setPerRow
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 setWidths
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
12
 setHeights
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
12
 setAdditionalOptions
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 add
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 insert
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 getImages
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isEmpty
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setShowDimensions
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setShowBytes
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setShowFilename
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setAttributes
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 toHTML
n/a
0 / 0
n/a
0 / 0
0
 count
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setContextTitle
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getContextTitle
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getRenderLang
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2/**
3 * Image gallery.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
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 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * http://www.gnu.org/copyleft/gpl.html
19 *
20 * @file
21 */
22
23use MediaWiki\Context\ContextSource;
24use MediaWiki\Context\IContextSource;
25use MediaWiki\Context\RequestContext;
26use MediaWiki\HookContainer\HookRunner;
27use MediaWiki\MainConfigNames;
28use MediaWiki\MediaWikiServices;
29use MediaWiki\Parser\Parser;
30use MediaWiki\Title\Title;
31
32/**
33 * Image gallery
34 *
35 * Add images to the gallery using add(), then render that list to HTML using toHTML().
36 * @stable to extend
37 * @ingroup Media
38 */
39abstract class ImageGalleryBase extends ContextSource {
40    public const LOADING_DEFAULT = 1;
41    public const LOADING_LAZY = 2;
42
43    /**
44     * @var array[] Gallery images
45     * @phan-var array<int,array{0:Title,1:string,2:string,3:string,4:array,5:int}>
46     */
47    protected $mImages;
48
49    /**
50     * @var bool Whether to show the filesize in bytes in categories
51     */
52    protected $mShowBytes;
53
54    /**
55     * @var bool Whether to show the dimensions in categories
56     */
57    protected $mShowDimensions;
58
59    /**
60     * @var bool Whether to show the filename. Default: true
61     */
62    protected $mShowFilename;
63
64    /**
65     * @var string Gallery mode. Default: traditional
66     */
67    protected $mMode;
68
69    /**
70     * @var string|false Gallery caption. Default: false
71     */
72    protected $mCaption = false;
73
74    /**
75     * Length to truncate filename to in caption when using "showfilename".
76     * A value of 'true' will truncate the filename to one line using CSS
77     * and will be the behaviour after deprecation.
78     *
79     * @var bool|int
80     */
81    protected $mCaptionLength = true;
82
83    /**
84     * @var bool Hide bad images?
85     */
86    protected $mHideBadImages;
87
88    /**
89     * @var Parser|false Registered parser object for output callbacks
90     */
91    public $mParser;
92
93    /**
94     * @var Title|null Contextual title, used when images are being screened against
95     *   the bad image list
96     */
97    protected $contextTitle = null;
98
99    /** @var array */
100    protected $mAttribs = [];
101
102    /** @var int */
103    protected $mPerRow;
104
105    /** @var int */
106    protected $mWidths;
107
108    /** @var int */
109    protected $mHeights;
110
111    /** @var array */
112    private static $modeMapping;
113
114    /**
115     * Get a new image gallery. This is the method other callers
116     * should use to get a gallery.
117     *
118     * @param string|false $mode Mode to use. False to use the default
119     * @param IContextSource|null $context
120     * @return ImageGalleryBase
121     * @throws ImageGalleryClassNotFoundException
122     */
123    public static function factory( $mode = false, IContextSource $context = null ) {
124        self::loadModes();
125        if ( !$context ) {
126            $context = RequestContext::getMainAndWarn( __METHOD__ );
127        }
128        if ( !$mode ) {
129            $galleryOptions = $context->getConfig()->get( MainConfigNames::GalleryOptions );
130            $mode = $galleryOptions['mode'];
131        }
132
133        $mode = MediaWikiServices::getInstance()->getContentLanguage()->lc( $mode );
134
135        if ( isset( self::$modeMapping[$mode] ) ) {
136            $class = self::$modeMapping[$mode];
137            return new $class( $mode, $context );
138        } else {
139            throw new ImageGalleryClassNotFoundException( "No gallery class registered for mode $mode" );
140        }
141    }
142
143    private static function loadModes() {
144        if ( self::$modeMapping === null ) {
145            self::$modeMapping = [
146                'traditional' => TraditionalImageGallery::class,
147                'nolines' => NolinesImageGallery::class,
148                'packed' => PackedImageGallery::class,
149                'packed-hover' => PackedHoverImageGallery::class,
150                'packed-overlay' => PackedOverlayImageGallery::class,
151                'slideshow' => SlideshowImageGallery::class,
152            ];
153            // Allow extensions to make a new gallery format.
154            ( new HookRunner( MediaWikiServices::getInstance()->getHookContainer() ) )
155                ->onGalleryGetModes( self::$modeMapping );
156        }
157    }
158
159    /**
160     * Create a new image gallery object.
161     *
162     * You should not call this directly, but instead use
163     * ImageGalleryBase::factory().
164     *
165     * @stable to call
166     * @note constructors of subclasses must have a compatible signature
167     *       for use by the factory() method.
168     *
169     * @param string $mode
170     * @param IContextSource|null $context
171     */
172    public function __construct( $mode = 'traditional', IContextSource $context = null ) {
173        if ( $context ) {
174            $this->setContext( $context );
175        }
176
177        $galleryOptions = $this->getConfig()->get( MainConfigNames::GalleryOptions );
178        $this->mImages = [];
179        $this->mShowBytes = $galleryOptions['showBytes'];
180        $this->mShowDimensions = $galleryOptions['showDimensions'];
181        $this->mShowFilename = true;
182        $this->mParser = false;
183        $this->mHideBadImages = false;
184        $this->mPerRow = $galleryOptions['imagesPerRow'];
185        $this->mWidths = $galleryOptions['imageWidth'];
186        $this->mHeights = $galleryOptions['imageHeight'];
187        $this->mCaptionLength = $galleryOptions['captionLength'];
188        $this->mMode = $mode;
189    }
190
191    /**
192     * Register a parser object. If you do not set this
193     * and the output of this gallery ends up in parser
194     * cache, the javascript will break!
195     *
196     * @note This also triggers using the page's target
197     *  language instead of the user language.
198     *
199     * @param Parser $parser
200     */
201    public function setParser( $parser ) {
202        $this->mParser = $parser;
203    }
204
205    /**
206     * @param bool $flag
207     */
208    public function setHideBadImages( $flag = true ) {
209        $this->mHideBadImages = $flag;
210    }
211
212    /**
213     * Set the caption (as plain text)
214     *
215     * @param string $caption
216     */
217    public function setCaption( $caption ) {
218        $this->mCaption = htmlspecialchars( $caption );
219    }
220
221    /**
222     * Set the caption (as HTML)
223     *
224     * @param string $caption
225     */
226    public function setCaptionHtml( $caption ) {
227        $this->mCaption = $caption;
228    }
229
230    /**
231     * Set how many images will be displayed per row.
232     *
233     * @param int $num Integer >= 0; If perrow=0 the gallery layout will adapt
234     *   to screensize invalid numbers will be rejected
235     */
236    public function setPerRow( $num ) {
237        if ( $num >= 0 ) {
238            $this->mPerRow = (int)$num;
239        }
240    }
241
242    /**
243     * Set how wide each image will be, in pixels.
244     *
245     * @param string $num Number. Unit other than 'px is invalid. Invalid numbers
246     *   and those below 0 are ignored.
247     */
248    public function setWidths( $num ) {
249        $parsed = Parser::parseWidthParam( $num, false );
250        if ( isset( $parsed['width'] ) && $parsed['width'] > 0 ) {
251            $this->mWidths = $parsed['width'];
252        }
253    }
254
255    /**
256     * Set how high each image will be, in pixels.
257     *
258     * @param string $num Number. Unit other than 'px is invalid. Invalid numbers
259     *   and those below 0 are ignored.
260     */
261    public function setHeights( $num ) {
262        $parsed = Parser::parseWidthParam( $num, false );
263        if ( isset( $parsed['width'] ) && $parsed['width'] > 0 ) {
264            $this->mHeights = $parsed['width'];
265        }
266    }
267
268    /**
269     * Allow setting additional options. This is meant
270     * to allow extensions to add additional parameters to
271     * <gallery> parser tag.
272     *
273     * @stable to override
274     *
275     * @param array $options Attributes of gallery tag
276     */
277    public function setAdditionalOptions( $options ) {
278    }
279
280    /**
281     * Add an image to the gallery.
282     *
283     * @param Title $title Title object of the image that is added to the gallery
284     * @param string $html Additional HTML text to be shown. The name and size
285     *   of the image are always shown.
286     * @param string|null $alt Alt text for the image, or null to omit
287     * @param string $link Override image link (optional)
288     * @param array $handlerOpts Array of options for image handler (aka page number)
289     * @param int $loading Sets loading attribute of the underlying <img> (optional)
290     * @param ?array $imageOptions To supercede the $link param
291     */
292    public function add(
293            $title,
294            $html = '',
295            $alt = '',
296            $link = '',
297            $handlerOpts = [],
298            $loading = self::LOADING_DEFAULT,
299            ?array $imageOptions = null
300        ) {
301        if ( $title instanceof File ) {
302            // Old calling convention
303            $title = $title->getTitle();
304        }
305        $this->mImages[] = [ $title, $html, $alt, $link, $handlerOpts, $loading, $imageOptions ];
306        wfDebug( 'ImageGallery::add ' . $title->getText() );
307    }
308
309    /**
310     * Add an image at the beginning of the gallery.
311     *
312     * @param Title $title Title object of the image that is added to the gallery
313     * @param string $html Additional HTML text to be shown. The name and size
314     *   of the image are always shown.
315     * @param string $alt Alt text for the image
316     * @param string $link Override image link (optional)
317     * @param array $handlerOpts Array of options for image handler (aka page number)
318     * @param int $loading Sets loading attribute of the underlying <img> (optional)
319     * @param ?array $imageOptions To supercede the $link param
320     */
321    public function insert(
322            $title,
323            $html = '',
324            $alt = '',
325            $link = '',
326            $handlerOpts = [],
327            $loading = self::LOADING_DEFAULT,
328            ?array $imageOptions = null
329        ) {
330        if ( $title instanceof File ) {
331            // Old calling convention
332            $title = $title->getTitle();
333        }
334        array_unshift( $this->mImages, [ &$title, $html, $alt, $link, $handlerOpts, $loading, $imageOptions ] );
335    }
336
337    /**
338     * Returns the list of images this gallery contains
339     * @return array[]
340     * @phan-return array<int,array{0:Title,1:string,2:string,3:string,4:array}>
341     */
342    public function getImages() {
343        return $this->mImages;
344    }
345
346    /**
347     * isEmpty() returns true if the gallery contains no images
348     * @return bool
349     */
350    public function isEmpty() {
351        return $this->mImages === [];
352    }
353
354    /**
355     * Enable/Disable showing of the dimensions of an image in the gallery.
356     * Enabled by default.
357     *
358     * @param bool $f Set to false to disable
359     */
360    public function setShowDimensions( $f ) {
361        $this->mShowDimensions = (bool)$f;
362    }
363
364    /**
365     * Enable/Disable showing of the file size of an image in the gallery.
366     * Enabled by default.
367     *
368     * @param bool $f Set to false to disable
369     */
370    public function setShowBytes( $f ) {
371        $this->mShowBytes = (bool)$f;
372    }
373
374    /**
375     * Enable/Disable showing of the filename of an image in the gallery.
376     * Enabled by default.
377     *
378     * @param bool $f Set to false to disable
379     */
380    public function setShowFilename( $f ) {
381        $this->mShowFilename = (bool)$f;
382    }
383
384    /**
385     * Set arbitrary attributes to go on the HTML gallery output element.
386     * Should be suitable for a <ul> element.
387     *
388     * Note -- if taking from user input, you should probably run through
389     * Sanitizer::validateAttributes() first.
390     *
391     * @param array $attribs Array of HTML attribute pairs
392     */
393    public function setAttributes( $attribs ) {
394        $this->mAttribs = $attribs;
395    }
396
397    /**
398     * Display an html representation of the gallery
399     *
400     * @return string The html
401     */
402    abstract public function toHTML();
403
404    /**
405     * @return int Number of images in the gallery
406     */
407    public function count() {
408        return count( $this->mImages );
409    }
410
411    /**
412     * Set the contextual title
413     *
414     * @param Title|null $title Contextual title
415     */
416    public function setContextTitle( $title ) {
417        $this->contextTitle = $title;
418    }
419
420    /**
421     * Get the contextual title, if applicable
422     *
423     * @return Title|null
424     */
425    public function getContextTitle() {
426        return $this->contextTitle;
427    }
428
429    /**
430     * Determines the correct language to be used for this image gallery
431     * @return Language
432     */
433    protected function getRenderLang() {
434        return $this->mParser
435            ? $this->mParser->getTargetLanguage()
436            : $this->getLanguage();
437    }
438}