]> scripts.mit.edu Git - autoinstalls/mediawiki.git/blob - includes/ImagePage.php
MediaWiki 1.11.0
[autoinstalls/mediawiki.git] / includes / ImagePage.php
1 <?php
2 /**
3  */
4
5 /**
6  *
7  */
8 if( !defined( 'MEDIAWIKI' ) )
9         die( 1 );
10
11 /**
12  * Special handling for image description pages
13  *
14  * @addtogroup Media
15  */
16 class ImagePage extends Article {
17
18         /* private */ var $img;  // Image object this page is shown for
19         /* private */ var $repo;
20         var $mExtraDescription = false;
21
22         function __construct( $title ) {
23                 parent::__construct( $title );
24                 $this->img = wfFindFile( $this->mTitle );
25                 if ( !$this->img ) {
26                         $this->img = wfLocalFile( $this->mTitle );
27                 }
28                 $this->repo = $this->img->repo;
29         }
30
31         /**
32          * Handler for action=render
33          * Include body text only; none of the image extras
34          */
35         function render() {
36                 global $wgOut;
37                 $wgOut->setArticleBodyOnly( true );
38                 $wgOut->addSecondaryWikitext( $this->getContent() );
39         }
40
41         function view() {
42                 global $wgOut, $wgShowEXIF, $wgRequest, $wgUser;
43
44                 $diff = $wgRequest->getVal( 'diff' );
45                 $diffOnly = $wgRequest->getBool( 'diffonly', $wgUser->getOption( 'diffonly' ) );
46
47                 if ( $this->mTitle->getNamespace() != NS_IMAGE || ( isset( $diff ) && $diffOnly ) )
48                         return Article::view();
49
50                 if ($wgShowEXIF && $this->img->exists()) {
51                         // FIXME: bad interface, see note on MediaHandler::formatMetadata(). 
52                         $formattedMetadata = $this->img->formatMetadata();
53                         $showmeta = $formattedMetadata !== false;
54                 } else {
55                         $showmeta = false;
56                 }
57
58                 if ($this->img->exists())
59                         $wgOut->addHTML($this->showTOC($showmeta));
60
61                 $this->openShowImage();
62
63                 # No need to display noarticletext, we use our own message, output in openShowImage()
64                 if ( $this->getID() ) {
65                         Article::view();
66                 } else {
67                         # Just need to set the right headers
68                         $wgOut->setArticleFlag( true );
69                         $wgOut->setRobotpolicy( 'index,follow' );
70                         $wgOut->setPageTitle( $this->mTitle->getPrefixedText() );
71                         $this->viewUpdates();
72                 }
73
74                 # Show shared description, if needed
75                 if ( $this->mExtraDescription ) {
76                         $fol = wfMsg( 'shareddescriptionfollows' );
77                         if( $fol != '-' && !wfEmptyMsg( 'shareddescriptionfollows', $fol ) ) {
78                                 $wgOut->addWikiText( $fol );
79                         }
80                         $wgOut->addHTML( '<div id="shared-image-desc">' . $this->mExtraDescription . '</div>' );
81                 }
82
83                 $this->closeShowImage();
84                 $this->imageHistory();
85                 $this->imageLinks();
86
87                 if ( $showmeta ) {
88                         global $wgStylePath, $wgStyleVersion;
89                         $expand = htmlspecialchars( wfEscapeJsString( wfMsg( 'metadata-expand' ) ) );
90                         $collapse = htmlspecialchars( wfEscapeJsString( wfMsg( 'metadata-collapse' ) ) );
91                         $wgOut->addHTML( Xml::element( 'h2', array( 'id' => 'metadata' ), wfMsg( 'metadata' ) ). "\n" );
92                         $wgOut->addWikiText( $this->makeMetadataTable( $formattedMetadata ) );
93                         $wgOut->addHTML(
94                                 "<script type=\"text/javascript\" src=\"$wgStylePath/common/metadata.js?$wgStyleVersion\"></script>\n" .
95                                 "<script type=\"text/javascript\">attachMetadataToggle('mw_metadata', '$expand', '$collapse');</script>\n" );
96                 }
97         }
98
99         /**
100          * Create the TOC
101          *
102          * @access private
103          *
104          * @param bool $metadata Whether or not to show the metadata link
105          * @return string
106          */
107         function showTOC( $metadata ) {
108                 global $wgLang;
109                 $r = '<ul id="filetoc">
110                         <li><a href="#file">' . $wgLang->getNsText( NS_IMAGE ) . '</a></li>
111                         <li><a href="#filehistory">' . wfMsgHtml( 'filehist' ) . '</a></li>
112                         <li><a href="#filelinks">' . wfMsgHtml( 'imagelinks' ) . '</a></li>' .
113                         ($metadata ? ' <li><a href="#metadata">' . wfMsgHtml( 'metadata' ) . '</a></li>' : '') . '
114                 </ul>';
115                 return $r;
116         }
117
118         /**
119          * Make a table with metadata to be shown in the output page.
120          *
121          * FIXME: bad interface, see note on MediaHandler::formatMetadata(). 
122          *
123          * @access private
124          *
125          * @param array $exif The array containing the EXIF data
126          * @return string
127          */
128         function makeMetadataTable( $metadata ) {
129                 $r = wfMsg( 'metadata-help' ) . "\n\n";
130                 $r .= "{| id=mw_metadata class=mw_metadata\n";
131                 foreach ( $metadata as $type => $stuff ) {
132                         foreach ( $stuff as $v ) {
133                                 $class = Sanitizer::escapeId( $v['id'] );
134                                 if( $type == 'collapsed' ) {
135                                         $class .= ' collapsable';
136                                 }
137                                 $r .= "|- class=\"$class\"\n";
138                                 $r .= "!| {$v['name']}\n";
139                                 $r .= "|| {$v['value']}\n";
140                         }
141                 }
142                 $r .= '|}';
143                 return $r;
144         }
145
146         /**
147          * Overloading Article's getContent method.
148          * 
149          * Omit noarticletext if sharedupload; text will be fetched from the
150          * shared upload server if possible.
151          */
152         function getContent() {
153                 if( $this->img && !$this->img->isLocal() && 0 == $this->getID() ) {
154                         return '';
155                 }
156                 return Article::getContent();
157         }
158
159         function openShowImage() {
160                 global $wgOut, $wgUser, $wgImageLimits, $wgRequest, $wgLang;
161
162                 $full_url  = $this->img->getURL();
163                 $linkAttribs = false;
164                 $sizeSel = intval( $wgUser->getOption( 'imagesize') );
165                 if( !isset( $wgImageLimits[$sizeSel] ) ) {
166                         $sizeSel = User::getDefaultOption( 'imagesize' );
167
168                         // The user offset might still be incorrect, specially if
169                         // $wgImageLimits got changed (see bug #8858).
170                         if( !isset( $wgImageLimits[$sizeSel] ) ) {
171                                 // Default to the first offset in $wgImageLimits
172                                 $sizeSel = 0;
173                         }
174                 }
175                 $max = $wgImageLimits[$sizeSel];
176                 $maxWidth = $max[0];
177                 $maxHeight = $max[1];
178                 $sk = $wgUser->getSkin();
179
180                 if ( $this->img->exists() ) {
181                         # image
182                         $page = $wgRequest->getIntOrNull( 'page' );
183                         if ( is_null( $page ) ) {
184                                 $params = array();
185                                 $page = 1;
186                         } else {
187                                 $params = array( 'page' => $page );
188                         }
189                         $width_orig = $this->img->getWidth();
190                         $width = $width_orig;
191                         $height_orig = $this->img->getHeight();
192                         $height = $height_orig;
193                         $mime = $this->img->getMimeType();
194                         $showLink = false;
195                         $linkAttribs = array( 'href' => $full_url );
196                         $longDesc = $this->img->getLongDesc();
197
198                         wfRunHooks( 'ImageOpenShowImageInlineBefore', array( &$this , &$wgOut ) )       ;
199
200                         if ( $this->img->allowInlineDisplay() ) {
201                                 # image
202
203                                 # "Download high res version" link below the image
204                                 #$msgsize = wfMsgHtml('file-info-size', $width_orig, $height_orig, $sk->formatSize( $this->img->getSize() ), $mime );
205                                 # We'll show a thumbnail of this image
206                                 if ( $width > $maxWidth || $height > $maxHeight ) {
207                                         # Calculate the thumbnail size.
208                                         # First case, the limiting factor is the width, not the height.
209                                         if ( $width / $height >= $maxWidth / $maxHeight ) {
210                                                 $height = round( $height * $maxWidth / $width);
211                                                 $width = $maxWidth;
212                                                 # Note that $height <= $maxHeight now.
213                                         } else {
214                                                 $newwidth = floor( $width * $maxHeight / $height);
215                                                 $height = round( $height * $newwidth / $width );
216                                                 $width = $newwidth;
217                                                 # Note that $height <= $maxHeight now, but might not be identical
218                                                 # because of rounding.
219                                         }
220                                         $msgbig  = wfMsgHtml( 'show-big-image' );
221                                         $msgsmall = wfMsgExt( 'show-big-image-thumb',
222                                                 array( 'parseinline' ), $width, $height );
223                                 } else {
224                                         # Image is small enough to show full size on image page
225                                         $msgbig = htmlspecialchars( $this->img->getName() );
226                                         $msgsmall = wfMsgExt( 'file-nohires', array( 'parseinline' ) );
227                                 }
228
229                                 $params['width'] = $width;
230                                 $thumbnail = $this->img->transform( $params );
231
232                                 $anchorclose = "<br />";
233                                 if( $this->img->mustRender() ) {
234                                         $showLink = true;
235                                 } else {
236                                         $anchorclose .= 
237                                                 $msgsmall .
238                                                 '<br />' . Xml::tags( 'a', $linkAttribs,  $msgbig ) . ' ' . $longDesc;
239                                 }
240
241                                 if ( $this->img->isMultipage() ) {
242                                         $wgOut->addHTML( '<table class="multipageimage"><tr><td>' );
243                                 }
244
245                                 if ( $thumbnail ) {
246                                         $options = array( 
247                                                 'alt' => $this->img->getTitle()->getPrefixedText(),
248                                                 'file-link' => true,
249                                         );
250                                         $wgOut->addHTML( '<div class="fullImageLink" id="file">' . 
251                                                 $thumbnail->toHtml( $options ) .
252                                                 $anchorclose . '</div>' );
253                                 }
254
255                                 if ( $this->img->isMultipage() ) {
256                                         $count = $this->img->pageCount();
257
258                                         if ( $page > 1 ) {
259                                                 $label = $wgOut->parse( wfMsg( 'imgmultipageprev' ), false );
260                                                 $link = $sk->makeKnownLinkObj( $this->mTitle, $label, 'page='. ($page-1) );
261                                                 $thumb1 = $sk->makeThumbLinkObj( $this->mTitle, $this->img, $link, $label, 'none', 
262                                                         array( 'page' => $page - 1 ) );
263                                         } else {
264                                                 $thumb1 = '';
265                                         }
266
267                                         if ( $page < $count ) {
268                                                 $label = wfMsg( 'imgmultipagenext' );
269                                                 $link = $sk->makeKnownLinkObj( $this->mTitle, $label, 'page='. ($page+1) );
270                                                 $thumb2 = $sk->makeThumbLinkObj( $this->mTitle, $this->img, $link, $label, 'none', 
271                                                         array( 'page' => $page + 1 ) );
272                                         } else {
273                                                 $thumb2 = '';
274                                         }
275
276                                         global $wgScript;
277                                         $select = '<form name="pageselector" action="' . 
278                                                 htmlspecialchars( $wgScript ) .
279                                                 '" method="get" onchange="document.pageselector.submit();">' .
280                                                 Xml::hidden( 'title', $this->getTitle()->getPrefixedDbKey() );
281                                         $select .= $wgOut->parse( wfMsg( 'imgmultigotopre' ), false ) .
282                                                 ' <select id="pageselector" name="page">';
283                                         for ( $i=1; $i <= $count; $i++ ) {
284                                                 $select .= Xml::option( $wgLang->formatNum( $i ), $i,
285                                                         $i == $page );
286                                         }
287                                         $select .= '</select>' . $wgOut->parse( wfMsg( 'imgmultigotopost' ), false ) .
288                                                 '<input type="submit" value="' .
289                                                 htmlspecialchars( wfMsg( 'imgmultigo' ) ) . '"></form>';
290
291                                         $wgOut->addHTML( '</td><td><div class="multipageimagenavbox">' .
292                                                 "$select<hr />$thumb1\n$thumb2<br clear=\"all\" /></div></td></tr></table>" );
293                                 }
294                         } else {
295                                 #if direct link is allowed but it's not a renderable image, show an icon.
296                                 if ($this->img->isSafeFile()) {
297                                         $icon= $this->img->iconThumb();
298
299                                         $wgOut->addHTML( '<div class="fullImageLink" id="file">' .
300                                         $icon->toHtml( array( 'desc-link' => true ) ) .
301                                         '</div>' );
302                                 }
303
304                                 $showLink = true;
305                         }
306
307
308                         if ($showLink) {
309                                 $filename = wfEscapeWikiText( $this->img->getName() );
310
311                                 global $wgContLang;
312                                 $dirmark = $wgContLang->getDirMark();
313                                 if (!$this->img->isSafeFile()) {
314                                         $warning = wfMsg( 'mediawarning' );
315                                         $wgOut->addWikiText( <<<EOT
316 <div class="fullMedia">
317 <span class="dangerousLink">[[Media:$filename|$filename]]</span>$dirmark
318 <span class="fileInfo"> $longDesc</span>
319 </div>
320
321 <div class="mediaWarning">$warning</div>
322 EOT
323                                                 );
324                                 } else {
325                                         $wgOut->addWikiText( <<<EOT
326 <div class="fullMedia">
327 [[Media:$filename|$filename]]$dirmark <span class="fileInfo"> $longDesc</span>
328 </div>
329 EOT
330                                                 );
331                                 }
332                         }
333
334                         if(!$this->img->isLocal()) {
335                                 $this->printSharedImageText();
336                         }
337                 } else {
338                         # Image does not exist
339
340                         $title = SpecialPage::getTitleFor( 'Upload' );
341                         $link = $sk->makeKnownLinkObj($title, wfMsgHtml('noimage-linktext'),
342                                 'wpDestFile=' . urlencode( $this->img->getName() ) );
343                         $wgOut->addHTML( wfMsgWikiHtml( 'noimage', $link ) );
344                 }
345         }
346
347         function printSharedImageText() {
348                 global $wgOut, $wgUser;
349
350                 $descUrl = $this->img->getDescriptionUrl();
351                 $descText = $this->img->getDescriptionText();
352                 $s = "<div class='sharedUploadNotice'>" . wfMsgWikiHtml("sharedupload");
353                 if ( $descUrl && !$descText) {
354                         $sk = $wgUser->getSkin();
355                         $link = $sk->makeExternalLink( $descUrl, wfMsg('shareduploadwiki-linktext') );
356                         $s .= " " . wfMsgWikiHtml('shareduploadwiki', $link);
357                 }
358                 $s .= "</div>";
359                 $wgOut->addHTML($s);
360
361                 if ( $descText ) {
362                         $this->mExtraDescription = $descText;
363                 }
364         }
365
366         function getUploadUrl() {
367                 global $wgServer;
368                 $uploadTitle = SpecialPage::getTitleFor( 'Upload' );
369                 return $wgServer . $uploadTitle->getLocalUrl( 'wpDestFile=' . urlencode( $this->img->getName() ) );
370         }
371
372         /**
373          * Print out the various links at the bottom of the image page, e.g. reupload,
374          * external editing (and instructions link) etc.
375          */
376         function uploadLinksBox() {
377                 global $wgUser, $wgOut;
378
379                 if( !$this->img->isLocal() )
380                         return;
381
382                 $sk = $wgUser->getSkin();
383                 
384                 $wgOut->addHtml( '<br /><ul>' );
385                 
386                 # "Upload a new version of this file" link
387                 if( UploadForm::userCanReUpload($wgUser,$this->img->name) ) {
388                         $ulink = $sk->makeExternalLink( $this->getUploadUrl(), wfMsg( 'uploadnewversion-linktext' ) );
389                         $wgOut->addHtml( "<li><div class='plainlinks'>{$ulink}</div></li>" );
390                 }
391                 
392                 # External editing link
393                 $elink = $sk->makeKnownLinkObj( $this->mTitle, wfMsgHtml( 'edit-externally' ), 'action=edit&externaledit=true&mode=file' );
394                 $wgOut->addHtml( '<li>' . $elink . '<div>' . wfMsgWikiHtml( 'edit-externally-help' ) . '</div></li>' );
395                 
396                 $wgOut->addHtml( '</ul>' );
397         }
398
399         function closeShowImage()
400         {
401                 # For overloading
402
403         }
404
405         /**
406          * If the page we've just displayed is in the "Image" namespace,
407          * we follow it with an upload history of the image and its usage.
408          */
409         function imageHistory()
410         {
411                 global $wgUser, $wgOut, $wgUseExternalEditor;
412
413                 $sk = $wgUser->getSkin();
414
415                 $line = $this->img->nextHistoryLine();
416
417                 if ( $line ) {
418                         $list = new ImageHistoryList( $sk, $this->img );
419                         $file = $this->repo->newFileFromRow( $line );
420                         $dims = $file->getDimensionsString();
421                         $s = $list->beginImageHistoryList() .
422                                 $list->imageHistoryLine( true, wfTimestamp(TS_MW, $line->img_timestamp),
423                                         $this->mTitle->getDBkey(),  $line->img_user,
424                                         $line->img_user_text, $line->img_size, $line->img_description,
425                                         $dims
426                                 );
427
428                         while ( $line = $this->img->nextHistoryLine() ) {
429                                 $file = $this->repo->newFileFromRow( $line );
430                                 $dims = $file->getDimensionsString();
431                                 $s .= $list->imageHistoryLine( false, $line->oi_timestamp,
432                                         $line->oi_archive_name, $line->oi_user,
433                                         $line->oi_user_text, $line->oi_size, $line->oi_description,
434                                         $dims
435                                 );
436                         }
437                         $s .= $list->endImageHistoryList();
438                 } else { $s=''; }
439                 $wgOut->addHTML( $s );
440
441                 $this->img->resetHistory();     // free db resources
442
443                 # Exist check because we don't want to show this on pages where an image
444                 # doesn't exist along with the noimage message, that would suck. -ævar
445                 if( $wgUseExternalEditor && $this->img->exists() ) {
446                         $this->uploadLinksBox();
447                 }
448
449         }
450
451         function imageLinks()
452         {
453                 global $wgUser, $wgOut;
454
455                 $wgOut->addHTML( Xml::element( 'h2', array( 'id' => 'filelinks' ), wfMsg( 'imagelinks' ) ) . "\n" );
456
457                 $dbr = wfGetDB( DB_SLAVE );
458                 $page = $dbr->tableName( 'page' );
459                 $imagelinks = $dbr->tableName( 'imagelinks' );
460
461                 $sql = "SELECT page_namespace,page_title FROM $imagelinks,$page WHERE il_to=" .
462                   $dbr->addQuotes( $this->mTitle->getDBkey() ) . " AND il_from=page_id";
463                 $sql = $dbr->limitResult($sql, 500, 0);
464                 $res = $dbr->query( $sql, "ImagePage::imageLinks" );
465
466                 if ( 0 == $dbr->numRows( $res ) ) {
467                         $wgOut->addHtml( '<p>' . wfMsg( "nolinkstoimage" ) . "</p>\n" );
468                         return;
469                 }
470                 $wgOut->addHTML( '<p>' . wfMsg( 'linkstoimage' ) .  "</p>\n<ul>" );
471
472                 $sk = $wgUser->getSkin();
473                 while ( $s = $dbr->fetchObject( $res ) ) {
474                         $name = Title::MakeTitle( $s->page_namespace, $s->page_title );
475                         $link = $sk->makeKnownLinkObj( $name, "" );
476                         $wgOut->addHTML( "<li>{$link}</li>\n" );
477                 }
478                 $wgOut->addHTML( "</ul>\n" );
479         }
480
481         /**
482          * Delete the file, or an earlier version of it
483          */
484         public function delete() {
485                 if( !$this->img->exists() || !$this->img->isLocal() ) {
486                         // Standard article deletion
487                         Article::delete();
488                         return;
489                 }
490                 $deleter = new FileDeleteForm( $this->img );
491                 $deleter->execute();
492         }
493
494         /**
495          * Revert the file to an earlier version
496          */
497         public function revert() {
498                 $reverter = new FileRevertForm( $this->img );
499                 $reverter->execute();
500         }
501         
502         /**
503          * Override handling of action=purge
504          */
505         function doPurge() {
506                 if( $this->img->exists() ) {
507                         wfDebug( "ImagePage::doPurge purging " . $this->img->getName() . "\n" );
508                         $update = new HTMLCacheUpdate( $this->mTitle, 'imagelinks' );
509                         $update->doUpdate();
510                         $this->img->upgradeRow();
511                         $this->img->purgeCache();
512                 } else {
513                         wfDebug( "ImagePage::doPurge no image\n" );
514                 }
515                 parent::doPurge();
516         }
517
518         /**
519          * Display an error with a wikitext description
520          */
521         function showError( $description ) {
522                 global $wgOut;
523                 $wgOut->setPageTitle( wfMsg( "internalerror" ) );
524                 $wgOut->setRobotpolicy( "noindex,nofollow" );
525                 $wgOut->setArticleRelated( false );
526                 $wgOut->enableClientCache( false );
527                 $wgOut->addWikiText( $description );
528         }
529
530 }
531
532 /**
533  * Builds the image revision log shown on image pages
534  *
535  * @addtogroup Media
536  */
537 class ImageHistoryList {
538
539         protected $img, $skin, $title, $repo;
540
541         public function __construct( $skin, $img ) {
542                 $this->skin = $skin;
543                 $this->img = $img;
544                 $this->title = $img->getTitle();
545         }
546
547         public function beginImageHistoryList() {
548                 global $wgOut, $wgUser;
549                 return Xml::element( 'h2', array( 'id' => 'filehistory' ), wfMsg( 'filehist' ) )
550                         . $wgOut->parse( wfMsgNoTrans( 'filehist-help' ) )
551                         . Xml::openElement( 'table', array( 'class' => 'filehistory' ) ) . "\n"
552                         . '<tr><td></td>'
553                         . ( $this->img->isLocal() && $wgUser->isAllowed( 'delete' ) ? '<td></td>' : '' )
554                         . '<th>' . wfMsgHtml( 'filehist-datetime' ) . '</th>'
555                         . '<th>' . wfMsgHtml( 'filehist-user' ) . '</th>'
556                         . '<th>' . wfMsgHtml( 'filehist-dimensions' ) . '</th>'
557                         . '<th class="mw-imagepage-filesize">' . wfMsgHtml( 'filehist-filesize' ) . '</th>'
558                         . '<th>' . wfMsgHtml( 'filehist-comment' ) . '</th>'
559                         . "</tr>\n";
560         }
561
562         public function endImageHistoryList() {
563                 return "</table>\n";
564         }
565
566         public function imageHistoryLine( $iscur, $timestamp, $img, $user, $usertext, $size, $description, $dims ) {
567                 global $wgUser, $wgLang, $wgContLang;
568                 $local = $this->img->isLocal();
569                 $row = '';
570
571                 // Deletion link
572                 if( $local && $wgUser->isAllowed( 'delete' ) ) {
573                         $row .= '<td>';
574                         $q = array();
575                         $q[] = 'action=delete';
576                         if( !$iscur )
577                                 $q[] = 'oldimage=' . urlencode( $img );
578                         $row .= '(' . $this->skin->makeKnownLinkObj(
579                                 $this->title,
580                                 wfMsgHtml( $iscur ? 'filehist-deleteall' : 'filehist-deleteone' ),
581                                 implode( '&', $q )
582                         ) . ')';
583                         $row .= '</td>';
584                 }
585
586                 // Reversion link/current indicator
587                 $row .= '<td>';
588                 if( $iscur ) {
589                         $row .= '(' . wfMsgHtml( 'filehist-current' ) . ')';
590                 } elseif( $local && $wgUser->isLoggedIn() && $this->title->userCan( 'edit' ) ) {
591                         $q = array();
592                         $q[] = 'action=revert';
593                         $q[] = 'oldimage=' . urlencode( $img );
594                         $q[] = 'wpEditToken=' . urlencode( $wgUser->editToken( $img ) );
595                         $row .= '(' . $this->skin->makeKnownLinkObj(
596                                 $this->title,
597                                 wfMsgHtml( 'filehist-revert' ),
598                                 implode( '&', $q )
599                         ) . ')';
600                 }
601                 $row .= '</td>';
602
603                 // Date/time and image link
604                 $row .= '<td>';
605                 $url = $iscur ? $this->img->getUrl() : $this->img->getArchiveUrl( $img );
606                 $row .= Xml::element(
607                         'a',
608                         array( 'href' => $url ),
609                         $wgLang->timeAndDate( $timestamp, true )
610                 );
611                 $row .= '</td>';
612
613                 // Uploading user
614                 $row .= '<td>';
615                 if( $local ) {
616                         $row .= $this->skin->userLink( $user, $usertext ) . $this->skin->userToolLinks( $user, $usertext );
617                 } else {
618                         $row .= htmlspecialchars( $usertext );
619                 }
620                 $row .= '</td>';
621
622                 // Image dimensions
623                 $row .= '<td>' . htmlspecialchars( $dims ) . '</td>';
624
625                 // File size
626                 $row .= '<td class="mw-imagepage-filesize">' . $this->skin->formatSize( $size ) . '</td>';
627
628                 // Comment
629                 $row .= '<td>' . $this->skin->formatComment( $description, $this->title ) . '</td>';
630
631                 return "<tr>{$row}</tr>\n";
632         }
633
634 }