(bug 22227) Special:Listfiles no longer throws an error on bogus file entries
[lhc/web/wiklou.git] / includes / ImageGallery.php
1 <?php
2 if ( ! defined( 'MEDIAWIKI' ) )
3 die( 1 );
4
5 /**
6 * Image gallery
7 *
8 * Add images to the gallery using add(), then render that list to HTML using toHTML().
9 *
10 * @ingroup Media
11 */
12 class ImageGallery
13 {
14 var $mImages, $mShowBytes, $mShowFilename;
15 var $mCaption = false;
16
17 /**
18 * Hide blacklisted images?
19 */
20 var $mHideBadImages;
21
22 /**
23 * Registered parser object for output callbacks
24 * @var Parser
25 */
26 var $mParser;
27
28 /**
29 * Contextual title, used when images are being screened
30 * against the bad image list
31 */
32 private $contextTitle = false;
33
34 private $mAttribs = array();
35
36 /**
37 * Fixed margins
38 */
39 const THUMB_PADDING = 30;
40 const GB_PADDING = 5;
41 //2px borders on each side + 2px implied padding on each side
42 const GB_BORDERS = 8;
43
44 /**
45 * Create a new image gallery object.
46 */
47 function __construct( ) {
48 global $wgGalleryOptions;
49 $this->mImages = array();
50 $this->mShowBytes = $wgGalleryOptions['showBytes'];
51 $this->mShowFilename = true;
52 $this->mParser = false;
53 $this->mHideBadImages = false;
54 $this->mPerRow = $wgGalleryOptions['imagesPerRow'];
55 $this->mWidths = $wgGalleryOptions['imageWidth'];
56 $this->mHeights = $wgGalleryOptions['imageHeight'];
57 $this->mCaptionLength = $wgGalleryOptions['captionLength'];
58 }
59
60 /**
61 * Register a parser object
62 */
63 function setParser( $parser ) {
64 $this->mParser = $parser;
65 }
66
67 /**
68 * Set bad image flag
69 */
70 function setHideBadImages( $flag = true ) {
71 $this->mHideBadImages = $flag;
72 }
73
74 /**
75 * Set the caption (as plain text)
76 *
77 * @param $caption Caption
78 */
79 function setCaption( $caption ) {
80 $this->mCaption = htmlspecialchars( $caption );
81 }
82
83 /**
84 * Set the caption (as HTML)
85 *
86 * @param $caption String: Caption
87 */
88 public function setCaptionHtml( $caption ) {
89 $this->mCaption = $caption;
90 }
91
92 /**
93 * Set how many images will be displayed per row.
94 *
95 * @param $num Integer >= 0; If perrow=0 the gallery layout will adapt to screensize
96 * invalid numbers will be rejected
97 */
98 public function setPerRow( $num ) {
99 if ($num >= 0) {
100 $this->mPerRow = (int)$num;
101 }
102 }
103
104 /**
105 * Set how wide each image will be, in pixels.
106 *
107 * @param $num Integer > 0; invalid numbers will be ignored
108 */
109 public function setWidths( $num ) {
110 if ($num > 0) {
111 $this->mWidths = (int)$num;
112 }
113 }
114
115 /**
116 * Set how high each image will be, in pixels.
117 *
118 * @param $num Integer > 0; invalid numbers will be ignored
119 */
120 public function setHeights( $num ) {
121 if ($num > 0) {
122 $this->mHeights = (int)$num;
123 }
124 }
125
126 /**
127 * Instruct the class to use a specific skin for rendering
128 *
129 * @param $skin Skin object
130 * @deprecated Not used anymore
131 */
132 function useSkin( $skin ) {
133 /* no op */
134 }
135
136 /**
137 * Add an image to the gallery.
138 *
139 * @param $title Title object of the image that is added to the gallery
140 * @param $html String: Additional HTML text to be shown. The name and size of the image are always shown.
141 * @param $alt String: Alt text for the image
142 */
143 function add( $title, $html = '', $alt = '' ) {
144 if ( $title instanceof File ) {
145 // Old calling convention
146 $title = $title->getTitle();
147 }
148 $this->mImages[] = array( $title, $html, $alt );
149 wfDebug( "ImageGallery::add " . $title->getText() . "\n" );
150 }
151
152 /**
153 * Add an image at the beginning of the gallery.
154 *
155 * @param $title Title object of the image that is added to the gallery
156 * @param $html String: Additional HTML text to be shown. The name and size of the image are always shown.
157 * @param $alt String: Alt text for the image
158 */
159 function insert( $title, $html='', $alt='' ) {
160 if ( $title instanceof File ) {
161 // Old calling convention
162 $title = $title->getTitle();
163 }
164 array_unshift( $this->mImages, array( &$title, $html, $alt ) );
165 }
166
167
168 /**
169 * isEmpty() returns true if the gallery contains no images
170 */
171 function isEmpty() {
172 return empty( $this->mImages );
173 }
174
175 /**
176 * Enable/Disable showing of the file size of an image in the gallery.
177 * Enabled by default.
178 *
179 * @param $f Boolean: set to false to disable.
180 */
181 function setShowBytes( $f ) {
182 $this->mShowBytes = (bool)$f;
183 }
184
185 /**
186 * Enable/Disable showing of the filename of an image in the gallery.
187 * Enabled by default.
188 *
189 * @param $f Boolean: set to false to disable.
190 */
191 function setShowFilename( $f ) {
192 $this->mShowFilename = (bool)$f;
193 }
194
195 /**
196 * Set arbitrary attributes to go on the HTML gallery output element.
197 * Should be suitable for a <ul> element.
198 *
199 * Note -- if taking from user input, you should probably run through
200 * Sanitizer::validateAttributes() first.
201 *
202 * @param $attribs Array of HTML attribute pairs
203 */
204 function setAttributes( $attribs ) {
205 $this->mAttribs = $attribs;
206 }
207
208 /**
209 * Return a HTML representation of the image gallery
210 *
211 * For each image in the gallery, display
212 * - a thumbnail
213 * - the image name
214 * - the additional text provided when adding the image
215 * - the size of the image
216 *
217 */
218 function toHTML() {
219 global $wgLang;
220
221 if ( $this->mPerRow > 0 ) {
222 $maxwidth = $this->mPerRow * ( $this->mWidths + self::THUMB_PADDING + self::GB_PADDING + self::GB_BORDERS );
223 $oldStyle = isset( $this->mAttribs['style'] ) ? $this->mAttribs['style'] : "";
224 # _width is ignored by any sane browser. IE6 doesn't know max-width so it uses _width instead
225 $this->mAttribs['style'] = "max-width: {$maxwidth}px;_width: {$maxwidth}px;" . $oldStyle;
226 }
227
228 $attribs = Sanitizer::mergeAttributes(
229 array( 'class' => 'gallery' ), $this->mAttribs );
230
231 $output = Xml::openElement( 'ul', $attribs );
232 if ( $this->mCaption ) {
233 $output .= "\n\t<li class='gallerycaption'>{$this->mCaption}</li>";
234 }
235
236 $params = array( 'width' => $this->mWidths, 'height' => $this->mHeights );
237 # Output each image...
238 foreach ( $this->mImages as $pair ) {
239 $nt = $pair[0];
240 $text = $pair[1]; # "text" means "caption" here
241 $alt = $pair[2];
242
243 $descQuery = false;
244 if ( $nt->getNamespace() == NS_FILE ) {
245 # Get the file...
246 if ( $this->mParser instanceof Parser ) {
247 # Give extensions a chance to select the file revision for us
248 $time = $sha1 = false;
249 wfRunHooks( 'BeforeParserFetchFileAndTitle',
250 array( $this->mParser, $nt, &$time, &$sha1, &$descQuery ) );
251 # Fetch and register the file (file title may be different via hooks)
252 list( $img, $nt ) = $this->mParser->fetchFileAndTitle( $nt, $time, $sha1 );
253 } else {
254 $img = wfFindFile( $nt );
255 }
256 } else {
257 $img = false;
258 }
259
260 if( !$img ) {
261 # We're dealing with a non-image, spit out the name and be done with it.
262 $thumbhtml = "\n\t\t\t".'<div style="height: '.(self::THUMB_PADDING + $this->mHeights).'px;">'
263 . htmlspecialchars( $nt->getText() ) . '</div>';
264 } elseif( $this->mHideBadImages && wfIsBadImage( $nt->getDBkey(), $this->getContextTitle() ) ) {
265 # The image is blacklisted, just show it as a text link.
266 $thumbhtml = "\n\t\t\t".'<div style="height: '.(self::THUMB_PADDING + $this->mHeights).'px;">' .
267 Linker::link(
268 $nt,
269 htmlspecialchars( $nt->getText() ),
270 array(),
271 array(),
272 array( 'known', 'noclasses' )
273 ) .
274 '</div>';
275 } elseif( !( $thumb = $img->transform( $params ) ) ) {
276 # Error generating thumbnail.
277 $thumbhtml = "\n\t\t\t".'<div style="height: '.(self::THUMB_PADDING + $this->mHeights).'px;">'
278 . htmlspecialchars( $img->getLastError() ) . '</div>';
279 } else {
280 # We get layout problems with the margin, if the image is smaller
281 # than the line-height (17), so we add less margin in these cases.
282 $minThumbHeight = $thumb->height > 17 ? $thumb->height : 17;
283 $vpad = floor(( self::THUMB_PADDING + $this->mHeights - $minThumbHeight ) /2);
284
285
286 $imageParameters = array(
287 'desc-link' => true,
288 'desc-query' => $descQuery,
289 'alt' => $alt,
290 );
291 # In the absence of both alt text and caption, fall back on providing screen readers with the filename as alt text
292 if ( $alt == '' && $text == '' ) {
293 $imageParameters['alt'] = $nt->getText();
294 }
295
296 # Set both fixed width and min-height.
297 $thumbhtml = "\n\t\t\t".
298 '<div class="thumb" style="width: ' .($this->mWidths + self::THUMB_PADDING).'px;">'
299 # Auto-margin centering for block-level elements. Needed now that we have video
300 # handlers since they may emit block-level elements as opposed to simple <img> tags.
301 # ref http://css-discuss.incutio.com/?page=CenteringBlockElement
302 . '<div style="margin:'.$vpad.'px auto;">'
303 . $thumb->toHtml( $imageParameters ) . '</div></div>';
304
305 // Call parser transform hook
306 if ( $this->mParser && $img->getHandler() ) {
307 $img->getHandler()->parserTransformHook( $this->mParser, $img );
308 }
309 }
310
311 //TODO
312 // $linkTarget = Title::newFromText( $wgContLang->getNsText( MWNamespace::getUser() ) . ":{$ut}" );
313 // $ul = Linker::link( $linkTarget, $ut );
314
315 if( $this->mShowBytes ) {
316 if( $img ) {
317 $fileSize = wfMsgExt( 'nbytes', array( 'parsemag', 'escape'),
318 $wgLang->formatNum( $img->getSize() ) );
319 } else {
320 $fileSize = wfMsgHtml( 'filemissing' );
321 }
322 $fileSize = "$fileSize<br />\n";
323 } else {
324 $fileSize = '';
325 }
326
327 $textlink = $this->mShowFilename ?
328 Linker::link(
329 $nt,
330 htmlspecialchars( $wgLang->truncate( $nt->getText(), $this->mCaptionLength ) ),
331 array(),
332 array(),
333 array( 'known', 'noclasses' )
334 ) . "<br />\n" :
335 '' ;
336
337 # ATTENTION: The newline after <div class="gallerytext"> is needed to accommodate htmltidy which
338 # in version 4.8.6 generated crackpot html in its absence, see:
339 # http://bugzilla.wikimedia.org/show_bug.cgi?id=1765 -Ævar
340
341 # Weird double wrapping (the extra div inside the li) needed due to FF2 bug
342 # Can be safely removed if FF2 falls completely out of existance
343 $output .=
344 "\n\t\t" . '<li class="gallerybox" style="width: ' . ( $this->mWidths + self::THUMB_PADDING + self::GB_PADDING ) . 'px">'
345 . '<div style="width: ' . ( $this->mWidths + self::THUMB_PADDING + self::GB_PADDING ) . 'px">'
346 . $thumbhtml
347 . "\n\t\t\t" . '<div class="gallerytext">' . "\n"
348 . $textlink . $text . $fileSize
349 . "\n\t\t\t</div>"
350 . "\n\t\t</div></li>";
351 }
352 $output .= "\n</ul>";
353
354 return $output;
355 }
356
357 /**
358 * @return Integer: number of images in the gallery
359 */
360 public function count() {
361 return count( $this->mImages );
362 }
363
364 /**
365 * Set the contextual title
366 *
367 * @param $title Title: contextual title
368 */
369 public function setContextTitle( $title ) {
370 $this->contextTitle = $title;
371 }
372
373 /**
374 * Get the contextual title, if applicable
375 *
376 * @return mixed Title or false
377 */
378 public function getContextTitle() {
379 return is_object( $this->contextTitle ) && $this->contextTitle instanceof Title
380 ? $this->contextTitle
381 : false;
382 }
383
384 } //class