MediaWiki  1.23.15
ImagePage.php
Go to the documentation of this file.
1 <?php
28 class ImagePage extends Article {
29 
33  private $displayImg;
37  private $repo;
38  private $fileLoaded;
39 
40  var $mExtraDescription = false;
41 
46  protected function newPage( Title $title ) {
47  // Overload mPage with a file-specific page
48  return new WikiFilePage( $title );
49  }
50 
56  public static function newFromID( $id ) {
57  $t = Title::newFromID( $id );
58  # @todo FIXME: Doesn't inherit right
59  return $t == null ? null : new self( $t );
60  # return $t == null ? null : new static( $t ); // PHP 5.3
61  }
62 
67  public function setFile( $file ) {
68  $this->mPage->setFile( $file );
69  $this->displayImg = $file;
70  $this->fileLoaded = true;
71  }
72 
73  protected function loadFile() {
74  if ( $this->fileLoaded ) {
75  return;
76  }
77  $this->fileLoaded = true;
78 
79  $this->displayImg = $img = false;
80  wfRunHooks( 'ImagePageFindFile', array( $this, &$img, &$this->displayImg ) );
81  if ( !$img ) { // not set by hook?
82  $img = wfFindFile( $this->getTitle() );
83  if ( !$img ) {
84  $img = wfLocalFile( $this->getTitle() );
85  }
86  }
87  $this->mPage->setFile( $img );
88  if ( !$this->displayImg ) { // not set by hook?
89  $this->displayImg = $img;
90  }
91  $this->repo = $img->getRepo();
92  }
93 
98  public function render() {
99  $this->getContext()->getOutput()->setArticleBodyOnly( true );
100  parent::view();
101  }
102 
103  public function view() {
104  global $wgShowEXIF;
105 
106  $out = $this->getContext()->getOutput();
107  $request = $this->getContext()->getRequest();
108  $diff = $request->getVal( 'diff' );
109  $diffOnly = $request->getBool( 'diffonly', $this->getContext()->getUser()->getOption( 'diffonly' ) );
110 
111  if ( $this->getTitle()->getNamespace() != NS_FILE || ( $diff !== null && $diffOnly ) ) {
112  parent::view();
113  return;
114  }
115 
116  $this->loadFile();
117 
118  if ( $this->getTitle()->getNamespace() == NS_FILE && $this->mPage->getFile()->getRedirected() ) {
119  if ( $this->getTitle()->getDBkey() == $this->mPage->getFile()->getName() || $diff !== null ) {
120  // mTitle is the same as the redirect target so ask Article
121  // to perform the redirect for us.
122  $request->setVal( 'diffonly', 'true' );
123  parent::view();
124  return;
125  } else {
126  // mTitle is not the same as the redirect target so it is
127  // probably the redirect page itself. Fake the redirect symbol
128  $out->setPageTitle( $this->getTitle()->getPrefixedText() );
129  $out->addHTML( $this->viewRedirect( Title::makeTitle( NS_FILE, $this->mPage->getFile()->getName() ),
130  /* $appendSubtitle */ true, /* $forceKnown */ true ) );
131  $this->mPage->doViewUpdates( $this->getContext()->getUser(), $this->getOldID() );
132  return;
133  }
134  }
135 
136  if ( $wgShowEXIF && $this->displayImg->exists() ) {
137  // @todo FIXME: Bad interface, see note on MediaHandler::formatMetadata().
138  $formattedMetadata = $this->displayImg->formatMetadata();
139  $showmeta = $formattedMetadata !== false;
140  } else {
141  $showmeta = false;
142  }
143 
144  if ( !$diff && $this->displayImg->exists() ) {
145  $out->addHTML( $this->showTOC( $showmeta ) );
146  }
147 
148  if ( !$diff ) {
149  $this->openShowImage();
150  }
151 
152  # No need to display noarticletext, we use our own message, output in openShowImage()
153  if ( $this->mPage->getID() ) {
154  # NS_FILE is in the user language, but this section (the actual wikitext)
155  # should be in page content language
156  $pageLang = $this->getTitle()->getPageViewLanguage();
157  $out->addHTML( Xml::openElement( 'div', array( 'id' => 'mw-imagepage-content',
158  'lang' => $pageLang->getHtmlCode(), 'dir' => $pageLang->getDir(),
159  'class' => 'mw-content-' . $pageLang->getDir() ) ) );
160 
161  parent::view();
162 
163  $out->addHTML( Xml::closeElement( 'div' ) );
164  } else {
165  # Just need to set the right headers
166  $out->setArticleFlag( true );
167  $out->setPageTitle( $this->getTitle()->getPrefixedText() );
168  $this->mPage->doViewUpdates( $this->getContext()->getUser(), $this->getOldID() );
169  }
170 
171  # Show shared description, if needed
172  if ( $this->mExtraDescription ) {
173  $fol = wfMessage( 'shareddescriptionfollows' );
174  if ( !$fol->isDisabled() ) {
175  $out->addWikiText( $fol->plain() );
176  }
177  $out->addHTML( '<div id="shared-image-desc">' . $this->mExtraDescription . "</div>\n" );
178  }
179 
180  $this->closeShowImage();
181  $this->imageHistory();
182  // TODO: Cleanup the following
183 
184  $out->addHTML( Xml::element( 'h2',
185  array( 'id' => 'filelinks' ),
186  wfMessage( 'imagelinks' )->text() ) . "\n" );
187  $this->imageDupes();
188  # @todo FIXME: For some freaky reason, we can't redirect to foreign images.
189  # Yet we return metadata about the target. Definitely an issue in the FileRepo
190  $this->imageLinks();
191 
192  # Allow extensions to add something after the image links
193  $html = '';
194  wfRunHooks( 'ImagePageAfterImageLinks', array( $this, &$html ) );
195  if ( $html ) {
196  $out->addHTML( $html );
197  }
198 
199  if ( $showmeta ) {
200  $out->addHTML( Xml::element(
201  'h2',
202  array( 'id' => 'metadata' ),
203  wfMessage( 'metadata' )->text() ) . "\n" );
204  $out->addWikiText( $this->makeMetadataTable( $formattedMetadata ) );
205  $out->addModules( array( 'mediawiki.action.view.metadata' ) );
206  }
207 
208  // Add remote Filepage.css
209  if ( !$this->repo->isLocal() ) {
210  $css = $this->repo->getDescriptionStylesheetUrl();
211  if ( $css ) {
212  $out->addStyle( $css );
213  }
214  }
215  // always show the local local Filepage.css, bug 29277
216  $out->addModuleStyles( 'filepage' );
217  }
218 
222  public function getDisplayedFile() {
223  $this->loadFile();
224  return $this->displayImg;
225  }
226 
233  protected function showTOC( $metadata ) {
234  $r = array(
235  '<li><a href="#file">' . wfMessage( 'file-anchor-link' )->escaped() . '</a></li>',
236  '<li><a href="#filehistory">' . wfMessage( 'filehist' )->escaped() . '</a></li>',
237  '<li><a href="#filelinks">' . wfMessage( 'imagelinks' )->escaped() . '</a></li>',
238  );
239  if ( $metadata ) {
240  $r[] = '<li><a href="#metadata">' . wfMessage( 'metadata' )->escaped() . '</a></li>';
241  }
242 
243  wfRunHooks( 'ImagePageShowTOC', array( $this, &$r ) );
244 
245  return '<ul id="filetoc">' . implode( "\n", $r ) . '</ul>';
246  }
247 
256  protected function makeMetadataTable( $metadata ) {
257  $r = "<div class=\"mw-imagepage-section-metadata\">";
258  $r .= wfMessage( 'metadata-help' )->plain();
259  $r .= "<table id=\"mw_metadata\" class=\"mw_metadata\">\n";
260  foreach ( $metadata as $type => $stuff ) {
261  foreach ( $stuff as $v ) {
262  # @todo FIXME: Why is this using escapeId for a class?!
263  $class = Sanitizer::escapeId( $v['id'] );
264  if ( $type == 'collapsed' ) {
265  // Handled by mediawiki.action.view.metadata module
266  // and skins/common/shared.css.
267  $class .= ' collapsable';
268  }
269  $r .= "<tr class=\"$class\">\n";
270  $r .= "<th>{$v['name']}</th>\n";
271  $r .= "<td>{$v['value']}</td>\n</tr>";
272  }
273  }
274  $r .= "</table>\n</div>\n";
275  return $r;
276  }
277 
285  public function getContentObject() {
286  $this->loadFile();
287  if ( $this->mPage->getFile() && !$this->mPage->getFile()->isLocal() && 0 == $this->getID() ) {
288  return null;
289  }
290  return parent::getContentObject();
291  }
292 
293  protected function openShowImage() {
294  global $wgImageLimits, $wgEnableUploads, $wgSend404Code;
295 
296  $this->loadFile();
297  $out = $this->getContext()->getOutput();
298  $user = $this->getContext()->getUser();
299  $lang = $this->getContext()->getLanguage();
300  $dirmark = $lang->getDirMarkEntity();
301  $request = $this->getContext()->getRequest();
302 
303  $max = $this->getImageLimitsFromOption( $user, 'imagesize' );
304  $maxWidth = $max[0];
305  $maxHeight = $max[1];
306 
307  if ( $this->displayImg->exists() ) {
308  # image
309  $page = $request->getIntOrNull( 'page' );
310  if ( is_null( $page ) ) {
311  $params = array();
312  $page = 1;
313  } else {
314  $params = array( 'page' => $page );
315  }
316 
317  $renderLang = $request->getVal( 'lang' );
318  if ( !is_null( $renderLang ) ) {
319  $handler = $this->displayImg->getHandler();
320  if ( $handler && $handler->validateParam( 'lang', $renderLang ) ) {
321  $params['lang'] = $renderLang;
322  } else {
323  $renderLang = null;
324  }
325  }
326 
327  $width_orig = $this->displayImg->getWidth( $page );
328  $width = $width_orig;
329  $height_orig = $this->displayImg->getHeight( $page );
330  $height = $height_orig;
331 
332  $filename = wfEscapeWikiText( $this->displayImg->getName() );
333  $linktext = $filename;
334 
335  wfRunHooks( 'ImageOpenShowImageInlineBefore', array( &$this, &$out ) );
336 
337  if ( $this->displayImg->allowInlineDisplay() ) {
338  # image
339 
340  # "Download high res version" link below the image
341  # $msgsize = wfMessage( 'file-info-size', $width_orig, $height_orig, Linker::formatSize( $this->displayImg->getSize() ), $mime )->escaped();
342  # We'll show a thumbnail of this image
343  if ( $width > $maxWidth || $height > $maxHeight ) {
344  # Calculate the thumbnail size.
345  # First case, the limiting factor is the width, not the height.
346  if ( $width / $height >= $maxWidth / $maxHeight ) { // FIXME: Possible division by 0. bug 36911
347  $height = round( $height * $maxWidth / $width ); // FIXME: Possible division by 0. bug 36911
348  $width = $maxWidth;
349  # Note that $height <= $maxHeight now.
350  } else {
351  $newwidth = floor( $width * $maxHeight / $height ); // FIXME: Possible division by 0. bug 36911
352  $height = round( $height * $newwidth / $width ); // FIXME: Possible division by 0. bug 36911
353  $width = $newwidth;
354  # Note that $height <= $maxHeight now, but might not be identical
355  # because of rounding.
356  }
357  $linktext = wfMessage( 'show-big-image' )->escaped();
358  if ( $this->displayImg->getRepo()->canTransformVia404() ) {
359  $thumbSizes = $wgImageLimits;
360  // Also include the full sized resolution in the list, so
361  // that users know they can get it. This will link to the
362  // original file asset if mustRender() === false. In the case
363  // that we mustRender, some users have indicated that they would
364  // find it useful to have the full size image in the rendered
365  // image format.
366  $thumbSizes[] = array( $width_orig, $height_orig );
367  } else {
368  # Creating thumb links triggers thumbnail generation.
369  # Just generate the thumb for the current users prefs.
370  $thumbSizes = array( $this->getImageLimitsFromOption( $user, 'thumbsize' ) );
371  if ( !$this->displayImg->mustRender() ) {
372  // We can safely include a link to the "full-size" preview,
373  // without actually rendering.
374  $thumbSizes[] = array( $width_orig, $height_orig );
375  }
376  }
377  # Generate thumbnails or thumbnail links as needed...
378  $otherSizes = array();
379  foreach ( $thumbSizes as $size ) {
380  // We include a thumbnail size in the list, if it is
381  // less than or equal to the original size of the image
382  // asset ($width_orig/$height_orig). We also exclude
383  // the current thumbnail's size ($width/$height)
384  // since that is added to the message separately, so
385  // it can be denoted as the current size being shown.
386  if ( $size[0] <= $width_orig && $size[1] <= $height_orig
387  && $size[0] != $width && $size[1] != $height
388  ) {
389  $sizeLink = $this->makeSizeLink( $params, $size[0], $size[1] );
390  if ( $sizeLink ) {
391  $otherSizes[] = $sizeLink;
392  }
393  }
394  }
395  $otherSizes = array_unique( $otherSizes );
396  $msgsmall = '';
397  $sizeLinkBigImagePreview = $this->makeSizeLink( $params, $width, $height );
398  if ( $sizeLinkBigImagePreview ) {
399  $msgsmall .= wfMessage( 'show-big-image-preview' )->
400  rawParams( $sizeLinkBigImagePreview )->
401  parse();
402  }
403  if ( count( $otherSizes ) ) {
404  $msgsmall .= ' ' .
405  Html::rawElement( 'span', array( 'class' => 'mw-filepage-other-resolutions' ),
406  wfMessage( 'show-big-image-other' )->rawParams( $lang->pipeList( $otherSizes ) )->
407  params( count( $otherSizes ) )->parse()
408  );
409  }
410  } elseif ( $width == 0 && $height == 0 ) {
411  # Some sort of audio file that doesn't have dimensions
412  # Don't output a no hi res message for such a file
413  $msgsmall = '';
414  } elseif ( $this->displayImg->isVectorized() ) {
415  # For vectorized images, full size is just the frame size
416  $msgsmall = '';
417  } else {
418  # Image is small enough to show full size on image page
419  $msgsmall = wfMessage( 'file-nohires' )->parse();
420  }
421 
422  $params['width'] = $width;
423  $params['height'] = $height;
424  $thumbnail = $this->displayImg->transform( $params );
425  Linker::processResponsiveImages( $this->displayImg, $thumbnail, $params );
426 
427  $anchorclose = Html::rawElement( 'div', array( 'class' => 'mw-filepage-resolutioninfo' ), $msgsmall );
428 
429  $isMulti = $this->displayImg->isMultipage() && $this->displayImg->pageCount() > 1;
430  if ( $isMulti ) {
431  $out->addModules( 'mediawiki.page.image.pagination' );
432  $out->addHTML( '<table class="multipageimage"><tr><td>' );
433  }
434 
435  if ( $thumbnail ) {
436  $options = array(
437  'alt' => $this->displayImg->getTitle()->getPrefixedText(),
438  'file-link' => true,
439  );
440  $out->addHTML( '<div class="fullImageLink" id="file">' .
441  $thumbnail->toHtml( $options ) .
442  $anchorclose . "</div>\n" );
443  }
444 
445  if ( $isMulti ) {
446  $count = $this->displayImg->pageCount();
447 
448  if ( $page > 1 ) {
449  $label = $out->parse( wfMessage( 'imgmultipageprev' )->text(), false );
450  // on the client side, this link is generated in ajaxifyPageNavigation()
451  // in the mediawiki.page.image.pagination module
453  $this->getTitle(),
454  $label,
455  array(),
456  array( 'page' => $page - 1 )
457  );
458  $thumb1 = Linker::makeThumbLinkObj( $this->getTitle(), $this->displayImg, $link, $label, 'none',
459  array( 'page' => $page - 1 ) );
460  } else {
461  $thumb1 = '';
462  }
463 
464  if ( $page < $count ) {
465  $label = wfMessage( 'imgmultipagenext' )->text();
467  $this->getTitle(),
468  $label,
469  array(),
470  array( 'page' => $page + 1 )
471  );
472  $thumb2 = Linker::makeThumbLinkObj( $this->getTitle(), $this->displayImg, $link, $label, 'none',
473  array( 'page' => $page + 1 ) );
474  } else {
475  $thumb2 = '';
476  }
477 
478  global $wgScript;
479 
480  $formParams = array(
481  'name' => 'pageselector',
482  'action' => $wgScript,
483  );
484  $options = array();
485  for ( $i = 1; $i <= $count; $i++ ) {
486  $options[] = Xml::option( $lang->formatNum( $i ), $i, $i == $page );
487  }
488  $select = Xml::tags( 'select',
489  array( 'id' => 'pageselector', 'name' => 'page' ),
490  implode( "\n", $options ) );
491 
492  $out->addHTML(
493  '</td><td><div class="multipageimagenavbox">' .
494  Xml::openElement( 'form', $formParams ) .
495  Html::hidden( 'title', $this->getTitle()->getPrefixedDBkey() ) .
496  wfMessage( 'imgmultigoto' )->rawParams( $select )->parse() .
497  Xml::submitButton( wfMessage( 'imgmultigo' )->text() ) .
498  Xml::closeElement( 'form' ) .
499  "<hr />$thumb1\n$thumb2<br style=\"clear: both\" /></div></td></tr></table>"
500  );
501  }
502  } elseif ( $this->displayImg->isSafeFile() ) {
503  # if direct link is allowed but it's not a renderable image, show an icon.
504  $icon = $this->displayImg->iconThumb();
505 
506  $out->addHTML( '<div class="fullImageLink" id="file">' .
507  $icon->toHtml( array( 'file-link' => true ) ) .
508  "</div>\n" );
509  }
510 
511  $longDesc = wfMessage( 'parentheses', $this->displayImg->getLongDesc() )->text();
512 
513  $medialink = "[[Media:$filename|$linktext]]";
514 
515  if ( !$this->displayImg->isSafeFile() ) {
516  $warning = wfMessage( 'mediawarning' )->plain();
517  // dirmark is needed here to separate the file name, which
518  // most likely ends in Latin characters, from the description,
519  // which may begin with the file type. In RTL environment
520  // this will get messy.
521  // The dirmark, however, must not be immediately adjacent
522  // to the filename, because it can get copied with it.
523  // See bug 25277.
524  $out->addWikiText( <<<EOT
525 <div class="fullMedia"><span class="dangerousLink">{$medialink}</span> $dirmark<span class="fileInfo">$longDesc</span></div>
526 <div class="mediaWarning">$warning</div>
527 EOT
528  );
529  } else {
530  $out->addWikiText( <<<EOT
531 <div class="fullMedia">{$medialink} {$dirmark}<span class="fileInfo">$longDesc</span>
532 </div>
533 EOT
534  );
535  }
536 
537  $renderLangOptions = $this->displayImg->getAvailableLanguages();
538  if ( count( $renderLangOptions ) >= 1 ) {
539  $currentLanguage = $renderLang;
540  $defaultLang = $this->displayImg->getDefaultRenderLanguage();
541  if ( is_null( $currentLanguage ) ) {
542  $currentLanguage = $defaultLang;
543  }
544  $out->addHtml( $this->doRenderLangOpt( $renderLangOptions, $currentLanguage, $defaultLang ) );
545  }
546 
547  // Add cannot animate thumbnail warning
548  if ( !$this->displayImg->canAnimateThumbIfAppropriate() ) {
549  // Include the extension so wiki admins can
550  // customize it on a per file-type basis
551  // (aka say things like use format X instead).
552  // additionally have a specific message for
553  // file-no-thumb-animation-gif
554  $ext = $this->displayImg->getExtension();
555  $noAnimMesg = wfMessageFallback(
556  'file-no-thumb-animation-' . $ext,
557  'file-no-thumb-animation'
558  )->plain();
559 
560  $out->addWikiText( <<<EOT
561 <div class="mw-noanimatethumb">{$noAnimMesg}</div>
562 EOT
563  );
564  }
565 
566  if ( !$this->displayImg->isLocal() ) {
567  $this->printSharedImageText();
568  }
569  } else {
570  # Image does not exist
571  if ( !$this->getID() ) {
572  # No article exists either
573  # Show deletion log to be consistent with normal articles
575  $out,
576  array( 'delete', 'move' ),
577  $this->getTitle()->getPrefixedText(),
578  '',
579  array( 'lim' => 10,
580  'conds' => array( "log_action != 'revision'" ),
581  'showIfEmpty' => false,
582  'msgKey' => array( 'moveddeleted-notice' )
583  )
584  );
585  }
586 
587  if ( $wgEnableUploads && $user->isAllowed( 'upload' ) ) {
588  // Only show an upload link if the user can upload
589  $uploadTitle = SpecialPage::getTitleFor( 'Upload' );
590  $nofile = array(
591  'filepage-nofile-link',
592  $uploadTitle->getFullURL( array( 'wpDestFile' => $this->mPage->getFile()->getName() ) )
593  );
594  } else {
595  $nofile = 'filepage-nofile';
596  }
597  // Note, if there is an image description page, but
598  // no image, then this setRobotPolicy is overridden
599  // by Article::View().
600  $out->setRobotPolicy( 'noindex,nofollow' );
601  $out->wrapWikiMsg( "<div id='mw-imagepage-nofile' class='plainlinks'>\n$1\n</div>", $nofile );
602  if ( !$this->getID() && $wgSend404Code ) {
603  // If there is no image, no shared image, and no description page,
604  // output a 404, to be consistent with articles.
605  $request->response()->header( 'HTTP/1.1 404 Not Found' );
606  }
607  }
608  $out->setFileVersion( $this->displayImg );
609  }
610 
618  private function makeSizeLink( $params, $width, $height ) {
619  $params['width'] = $width;
620  $params['height'] = $height;
621  $thumbnail = $this->displayImg->transform( $params );
622  if ( $thumbnail && !$thumbnail->isError() ) {
623  return Html::rawElement( 'a', array(
624  'href' => $thumbnail->getUrl(),
625  'class' => 'mw-thumbnail-link'
626  ), wfMessage( 'show-big-image-size' )->numParams(
627  $thumbnail->getWidth(), $thumbnail->getHeight()
628  )->parse() );
629  } else {
630  return '';
631  }
632  }
633 
637  protected function printSharedImageText() {
638  $out = $this->getContext()->getOutput();
639  $this->loadFile();
640 
641  $descUrl = $this->mPage->getFile()->getDescriptionUrl();
642  $descText = $this->mPage->getFile()->getDescriptionText( $this->getContext()->getLanguage() );
643 
644  /* Add canonical to head if there is no local page for this shared file */
645  if ( $descUrl && $this->mPage->getID() == 0 ) {
646  $out->setCanonicalUrl( $descUrl );
647  }
648 
649  $wrap = "<div class=\"sharedUploadNotice\">\n$1\n</div>\n";
650  $repo = $this->mPage->getFile()->getRepo()->getDisplayName();
651 
652  if ( $descUrl && $descText && wfMessage( 'sharedupload-desc-here' )->plain() !== '-' ) {
653  $out->wrapWikiMsg( $wrap, array( 'sharedupload-desc-here', $repo, $descUrl ) );
654  } elseif ( $descUrl && wfMessage( 'sharedupload-desc-there' )->plain() !== '-' ) {
655  $out->wrapWikiMsg( $wrap, array( 'sharedupload-desc-there', $repo, $descUrl ) );
656  } else {
657  $out->wrapWikiMsg( $wrap, array( 'sharedupload', $repo ), ''/*BACKCOMPAT*/ );
658  }
659 
660  if ( $descText ) {
661  $this->mExtraDescription = $descText;
662  }
663  }
664 
665  public function getUploadUrl() {
666  $this->loadFile();
667  $uploadTitle = SpecialPage::getTitleFor( 'Upload' );
668  return $uploadTitle->getFullURL( array(
669  'wpDestFile' => $this->mPage->getFile()->getName(),
670  'wpForReUpload' => 1
671  ) );
672  }
673 
678  protected function uploadLinksBox() {
679  global $wgEnableUploads;
680 
681  if ( !$wgEnableUploads ) {
682  return;
683  }
684 
685  $this->loadFile();
686  if ( !$this->mPage->getFile()->isLocal() ) {
687  return;
688  }
689 
690  $out = $this->getContext()->getOutput();
691  $out->addHTML( "<ul>\n" );
692 
693  # "Upload a new version of this file" link
694  $canUpload = $this->getTitle()->userCan( 'upload', $this->getContext()->getUser() );
695  if ( $canUpload && UploadBase::userCanReUpload( $this->getContext()->getUser(), $this->mPage->getFile()->name ) ) {
696  $ulink = Linker::makeExternalLink( $this->getUploadUrl(), wfMessage( 'uploadnewversion-linktext' )->text() );
697  $out->addHTML( "<li id=\"mw-imagepage-reupload-link\"><div class=\"plainlinks\">{$ulink}</div></li>\n" );
698  } else {
699  $out->addHTML( "<li id=\"mw-imagepage-upload-disallowed\">" . $this->getContext()->msg( 'upload-disallowed-here' )->escaped() . "</li>\n" );
700  }
701 
702  $out->addHTML( "</ul>\n" );
703  }
704 
705  protected function closeShowImage() { } # For overloading
706 
711  protected function imageHistory() {
712  $this->loadFile();
713  $out = $this->getContext()->getOutput();
714  $pager = new ImageHistoryPseudoPager( $this );
715  $out->addHTML( $pager->getBody() );
716  $out->preventClickjacking( $pager->getPreventClickjacking() );
717 
718  $this->mPage->getFile()->resetHistory(); // free db resources
719 
720  # Exist check because we don't want to show this on pages where an image
721  # doesn't exist along with the noimage message, that would suck. -ævar
722  if ( $this->mPage->getFile()->exists() ) {
723  $this->uploadLinksBox();
724  }
725  }
726 
732  protected function queryImageLinks( $target, $limit ) {
733  $dbr = wfGetDB( DB_SLAVE );
734 
735  return $dbr->select(
736  array( 'imagelinks', 'page' ),
737  array( 'page_namespace', 'page_title', 'page_is_redirect', 'il_to' ),
738  array( 'il_to' => $target, 'il_from = page_id' ),
739  __METHOD__,
740  array( 'LIMIT' => $limit + 1, 'ORDER BY' => 'il_from', )
741  );
742  }
743 
744  protected function imageLinks() {
745  $limit = 100;
746 
747  $out = $this->getContext()->getOutput();
748  $res = $this->queryImageLinks( $this->getTitle()->getDBkey(), $limit + 1 );
749  $rows = array();
750  $redirects = array();
751  foreach ( $res as $row ) {
752  if ( $row->page_is_redirect ) {
753  $redirects[$row->page_title] = array();
754  }
755  $rows[] = $row;
756  }
757  $count = count( $rows );
758 
759  $hasMore = $count > $limit;
760  if ( !$hasMore && count( $redirects ) ) {
761  $res = $this->queryImageLinks( array_keys( $redirects ),
762  $limit - count( $rows ) + 1 );
763  foreach ( $res as $row ) {
764  $redirects[$row->il_to][] = $row;
765  $count++;
766  }
767  $hasMore = ( $res->numRows() + count( $rows ) ) > $limit;
768  }
769 
770  if ( $count == 0 ) {
771  $out->wrapWikiMsg(
772  Html::rawElement( 'div',
773  array( 'id' => 'mw-imagepage-nolinkstoimage' ), "\n$1\n" ),
774  'nolinkstoimage'
775  );
776  return;
777  }
778 
779  $out->addHTML( "<div id='mw-imagepage-section-linkstoimage'>\n" );
780  if ( !$hasMore ) {
781  $out->addWikiMsg( 'linkstoimage', $count );
782  } else {
783  // More links than the limit. Add a link to [[Special:Whatlinkshere]]
784  $out->addWikiMsg( 'linkstoimage-more',
785  $this->getContext()->getLanguage()->formatNum( $limit ),
786  $this->getTitle()->getPrefixedDBkey()
787  );
788  }
789 
790  $out->addHTML(
791  Html::openElement( 'ul',
792  array( 'class' => 'mw-imagepage-linkstoimage' ) ) . "\n"
793  );
794  $count = 0;
795 
796  // Sort the list by namespace:title
797  usort( $rows, array( $this, 'compare' ) );
798 
799  // Create links for every element
800  $currentCount = 0;
801  foreach ( $rows as $element ) {
802  $currentCount++;
803  if ( $currentCount > $limit ) {
804  break;
805  }
806 
807  $query = array();
808  # Add a redirect=no to make redirect pages reachable
809  if ( isset( $redirects[$element->page_title] ) ) {
810  $query['redirect'] = 'no';
811  }
813  Title::makeTitle( $element->page_namespace, $element->page_title ),
814  null, array(), $query
815  );
816  if ( !isset( $redirects[$element->page_title] ) ) {
817  # No redirects
818  $liContents = $link;
819  } elseif ( count( $redirects[$element->page_title] ) === 0 ) {
820  # Redirect without usages
821  $liContents = wfMessage( 'linkstoimage-redirect' )->rawParams( $link, '' )->parse();
822  } else {
823  # Redirect with usages
824  $li = '';
825  foreach ( $redirects[$element->page_title] as $row ) {
826  $currentCount++;
827  if ( $currentCount > $limit ) {
828  break;
829  }
830 
831  $link2 = Linker::linkKnown( Title::makeTitle( $row->page_namespace, $row->page_title ) );
832  $li .= Html::rawElement(
833  'li',
834  array( 'class' => 'mw-imagepage-linkstoimage-ns' . $element->page_namespace ),
835  $link2
836  ) . "\n";
837  }
838 
840  'ul',
841  array( 'class' => 'mw-imagepage-redirectstofile' ),
842  $li
843  ) . "\n";
844  $liContents = wfMessage( 'linkstoimage-redirect' )->rawParams(
845  $link, $ul )->parse();
846  }
847  $out->addHTML( Html::rawElement(
848  'li',
849  array( 'class' => 'mw-imagepage-linkstoimage-ns' . $element->page_namespace ),
850  $liContents
851  ) . "\n"
852  );
853 
854  };
855  $out->addHTML( Html::closeElement( 'ul' ) . "\n" );
856  $res->free();
857 
858  // Add a links to [[Special:Whatlinkshere]]
859  if ( $count > $limit ) {
860  $out->addWikiMsg( 'morelinkstoimage', $this->getTitle()->getPrefixedDBkey() );
861  }
862  $out->addHTML( Html::closeElement( 'div' ) . "\n" );
863  }
864 
865  protected function imageDupes() {
866  $this->loadFile();
867  $out = $this->getContext()->getOutput();
868 
869  $dupes = $this->mPage->getDuplicates();
870  if ( count( $dupes ) == 0 ) {
871  return;
872  }
873 
874  $out->addHTML( "<div id='mw-imagepage-section-duplicates'>\n" );
875  $out->addWikiMsg( 'duplicatesoffile',
876  $this->getContext()->getLanguage()->formatNum( count( $dupes ) ), $this->getTitle()->getDBkey()
877  );
878  $out->addHTML( "<ul class='mw-imagepage-duplicates'>\n" );
879 
883  foreach ( $dupes as $file ) {
884  $fromSrc = '';
885  if ( $file->isLocal() ) {
886  $link = Linker::linkKnown( $file->getTitle() );
887  } else {
888  $link = Linker::makeExternalLink( $file->getDescriptionUrl(),
889  $file->getTitle()->getPrefixedText() );
890  $fromSrc = wfMessage( 'shared-repo-from', $file->getRepo()->getDisplayName() )->text();
891  }
892  $out->addHTML( "<li>{$link} {$fromSrc}</li>\n" );
893  }
894  $out->addHTML( "</ul></div>\n" );
895  }
896 
900  public function delete() {
901  $file = $this->mPage->getFile();
902  if ( !$file->exists() || !$file->isLocal() || $file->getRedirected() ) {
903  // Standard article deletion
904  parent::delete();
905  return;
906  }
907 
908  $deleter = new FileDeleteForm( $file );
909  $deleter->execute();
910  }
911 
917  function showError( $description ) {
918  $out = $this->getContext()->getOutput();
919  $out->setPageTitle( wfMessage( 'internalerror' ) );
920  $out->setRobotPolicy( 'noindex,nofollow' );
921  $out->setArticleRelated( false );
922  $out->enableClientCache( false );
923  $out->addWikiText( $description );
924  }
925 
934  protected function compare( $a, $b ) {
935  if ( $a->page_namespace == $b->page_namespace ) {
936  return strcmp( $a->page_title, $b->page_title );
937  } else {
938  return $a->page_namespace - $b->page_namespace;
939  }
940  }
941 
950  public function getImageLimitsFromOption( $user, $optionName ) {
951  global $wgImageLimits;
952 
953  $option = $user->getIntOption( $optionName );
954  if ( !isset( $wgImageLimits[$option] ) ) {
955  $option = User::getDefaultOption( $optionName );
956  }
957 
958  // The user offset might still be incorrect, specially if
959  // $wgImageLimits got changed (see bug #8858).
960  if ( !isset( $wgImageLimits[$option] ) ) {
961  // Default to the first offset in $wgImageLimits
962  $option = 0;
963  }
964 
965  return isset( $wgImageLimits[$option] )
966  ? $wgImageLimits[$option]
967  : array( 800, 600 ); // if nothing is set, fallback to a hardcoded default
968  }
969 
978  protected function doRenderLangOpt( array $langChoices, $curLang, $defaultLang ) {
979  global $wgScript;
980  sort( $langChoices );
981  $curLang = wfBCP47( $curLang );
982  $defaultLang = wfBCP47( $defaultLang );
983  $opts = '';
984  $haveCurrentLang = false;
985  $haveDefaultLang = false;
986 
987  // We make a list of all the language choices in the file.
988  // Additionally if the default language to render this file
989  // is not included as being in this file (for example, in svgs
990  // usually the fallback content is the english content) also
991  // include a choice for that. Last of all, if we're viewing
992  // the file in a language not on the list, add it as a choice.
993  foreach ( $langChoices as $lang ) {
994  $code = wfBCP47( $lang );
995  $name = Language::fetchLanguageName( $code, $this->getContext()->getLanguage()->getCode() );
996  if ( $name !== '' ) {
997  $display = wfMessage( 'img-lang-opt', $code, $name )->text();
998  } else {
999  $display = $code;
1000  }
1001  $opts .= "\n" . Xml::option( $display, $code, $curLang === $code );
1002  if ( $curLang === $code ) {
1003  $haveCurrentLang = true;
1004  }
1005  if ( $defaultLang === $code ) {
1006  $haveDefaultLang = true;
1007  }
1008  }
1009  if ( !$haveDefaultLang ) {
1010  // Its hard to know if the content is really in the default language, or
1011  // if its just unmarked content that could be in any language.
1012  $opts = Xml::option( wfMessage( 'img-lang-default' )->text(), $defaultLang, $defaultLang === $curLang ) . $opts;
1013  }
1014  if ( !$haveCurrentLang && $defaultLang !== $curLang ) {
1015  $name = Language::fetchLanguageName( $curLang, $this->getContext()->getLanguage()->getCode() );
1016  if ( $name !== '' ) {
1017  $display = wfMessage( 'img-lang-opt', $curLang, $name )->text();
1018  } else {
1019  $display = $curLang;
1020  }
1021  $opts = Xml::option( $display, $curLang, true ) . $opts;
1022  }
1023 
1024  $select = Html::rawElement( 'select', array( 'id' => 'mw-imglangselector', 'name' => 'lang' ), $opts );
1025  $submit = Xml::submitButton( wfMessage( 'img-lang-go' )->text() );
1026 
1027  $formContents = wfMessage( 'img-lang-info' )->rawParams( $select, $submit )->parse()
1028  . Html::hidden( 'title', $this->getTitle()->getPrefixedDBkey() );
1029 
1030  $langSelectLine = Html::rawElement( 'div', array( 'id' => 'mw-imglangselector-line' ),
1031  Html::rawElement( 'form', array( 'action' => $wgScript ), $formContents )
1032  );
1033  return $langSelectLine;
1034  }
1035 }
1042 class ImageHistoryList extends ContextSource {
1043 
1047  protected $title;
1052  protected $img;
1053 
1057  protected $imagePage;
1058 
1062  protected $current;
1063 
1064  protected $repo, $showThumb;
1065  protected $preventClickjacking = false;
1066 
1070  public function __construct( $imagePage ) {
1071  global $wgShowArchiveThumbnails;
1072  $this->current = $imagePage->getFile();
1073  $this->img = $imagePage->getDisplayedFile();
1074  $this->title = $imagePage->getTitle();
1075  $this->imagePage = $imagePage;
1076  $this->showThumb = $wgShowArchiveThumbnails && $this->img->canRender();
1077  $this->setContext( $imagePage->getContext() );
1078  }
1079 
1083  public function getImagePage() {
1085  }
1086 
1090  public function getFile() {
1091  return $this->img;
1092  }
1093 
1098  public function beginImageHistoryList( $navLinks = '' ) {
1099  return Xml::element( 'h2', array( 'id' => 'filehistory' ), $this->msg( 'filehist' )->text() ) . "\n"
1100  . "<div id=\"mw-imagepage-section-filehistory\">\n"
1101  . $this->msg( 'filehist-help' )->parseAsBlock()
1102  . $navLinks . "\n"
1103  . Xml::openElement( 'table', array( 'class' => 'wikitable filehistory' ) ) . "\n"
1104  . '<tr><td></td>'
1105  . ( $this->current->isLocal() && ( $this->getUser()->isAllowedAny( 'delete', 'deletedhistory' ) ) ? '<td></td>' : '' )
1106  . '<th>' . $this->msg( 'filehist-datetime' )->escaped() . '</th>'
1107  . ( $this->showThumb ? '<th>' . $this->msg( 'filehist-thumb' )->escaped() . '</th>' : '' )
1108  . '<th>' . $this->msg( 'filehist-dimensions' )->escaped() . '</th>'
1109  . '<th>' . $this->msg( 'filehist-user' )->escaped() . '</th>'
1110  . '<th>' . $this->msg( 'filehist-comment' )->escaped() . '</th>'
1111  . "</tr>\n";
1112  }
1118  public function endImageHistoryList( $navLinks = '' ) {
1119  return "</table>\n$navLinks\n</div>\n";
1120  }
1121 
1127  public function imageHistoryLine( $iscur, $file ) {
1129 
1130  $user = $this->getUser();
1131  $lang = $this->getLanguage();
1132  $timestamp = wfTimestamp( TS_MW, $file->getTimestamp() );
1133  $img = $iscur ? $file->getName() : $file->getArchiveName();
1134  $userId = $file->getUser( 'id' );
1135  $userText = $file->getUser( 'text' );
1136  $description = $file->getDescription( File::FOR_THIS_USER, $user );
1137 
1138  $local = $this->current->isLocal();
1139  $row = $selected = '';
1140 
1141  // Deletion link
1142  if ( $local && ( $user->isAllowedAny( 'delete', 'deletedhistory' ) ) ) {
1143  $row .= '<td>';
1144  # Link to remove from history
1145  if ( $user->isAllowed( 'delete' ) ) {
1146  $q = array( 'action' => 'delete' );
1147  if ( !$iscur ) {
1148  $q['oldimage'] = $img;
1149  }
1150  $row .= Linker::linkKnown(
1151  $this->title,
1152  $this->msg( $iscur ? 'filehist-deleteall' : 'filehist-deleteone' )->escaped(),
1153  array(), $q
1154  );
1155  }
1156  # Link to hide content. Don't show useless link to people who cannot hide revisions.
1157  $canHide = $user->isAllowed( 'deleterevision' );
1158  if ( $canHide || ( $user->isAllowed( 'deletedhistory' ) && $file->getVisibility() ) ) {
1159  if ( $user->isAllowed( 'delete' ) ) {
1160  $row .= '<br />';
1161  }
1162  // If file is top revision or locked from this user, don't link
1163  if ( $iscur || !$file->userCan( File::DELETED_RESTRICTED, $user ) ) {
1164  $del = Linker::revDeleteLinkDisabled( $canHide );
1165  } else {
1166  list( $ts, ) = explode( '!', $img, 2 );
1167  $query = array(
1168  'type' => 'oldimage',
1169  'target' => $this->title->getPrefixedText(),
1170  'ids' => $ts,
1171  );
1172  $del = Linker::revDeleteLink( $query,
1173  $file->isDeleted( File::DELETED_RESTRICTED ), $canHide );
1174  }
1175  $row .= $del;
1176  }
1177  $row .= '</td>';
1178  }
1179 
1180  // Reversion link/current indicator
1181  $row .= '<td>';
1182  if ( $iscur ) {
1183  $row .= $this->msg( 'filehist-current' )->escaped();
1184  } elseif ( $local && $this->title->quickUserCan( 'edit', $user )
1185  && $this->title->quickUserCan( 'upload', $user )
1186  ) {
1187  if ( $file->isDeleted( File::DELETED_FILE ) ) {
1188  $row .= $this->msg( 'filehist-revert' )->escaped();
1189  } else {
1190  $row .= Linker::linkKnown(
1191  $this->title,
1192  $this->msg( 'filehist-revert' )->escaped(),
1193  array(),
1194  array(
1195  'action' => 'revert',
1196  'oldimage' => $img,
1197  'wpEditToken' => $user->getEditToken( $img )
1198  )
1199  );
1200  }
1201  }
1202  $row .= '</td>';
1203 
1204  // Date/time and image link
1205  if ( $file->getTimestamp() === $this->img->getTimestamp() ) {
1206  $selected = "class='filehistory-selected'";
1207  }
1208  $row .= "<td $selected style='white-space: nowrap;'>";
1209  if ( !$file->userCan( File::DELETED_FILE, $user ) ) {
1210  # Don't link to unviewable files
1211  $row .= '<span class="history-deleted">' . $lang->userTimeAndDate( $timestamp, $user ) . '</span>';
1212  } elseif ( $file->isDeleted( File::DELETED_FILE ) ) {
1213  if ( $local ) {
1214  $this->preventClickjacking();
1215  $revdel = SpecialPage::getTitleFor( 'Revisiondelete' );
1216  # Make a link to review the image
1217  $url = Linker::linkKnown(
1218  $revdel,
1219  $lang->userTimeAndDate( $timestamp, $user ),
1220  array(),
1221  array(
1222  'target' => $this->title->getPrefixedText(),
1223  'file' => $img,
1224  'token' => $user->getEditToken( $img )
1225  )
1226  );
1227  } else {
1228  $url = $lang->userTimeAndDate( $timestamp, $user );
1229  }
1230  $row .= '<span class="history-deleted">' . $url . '</span>';
1231  } else {
1232  $url = $iscur ? $this->current->getUrl() : $this->current->getArchiveUrl( $img );
1233  $row .= Xml::element( 'a', array( 'href' => $url ), $lang->userTimeAndDate( $timestamp, $user ) );
1234  }
1235  $row .= "</td>";
1236 
1237  // Thumbnail
1238  if ( $this->showThumb ) {
1239  $row .= '<td>' . $this->getThumbForLine( $file ) . '</td>';
1240  }
1241 
1242  // Image dimensions + size
1243  $row .= '<td>';
1244  $row .= htmlspecialchars( $file->getDimensionsString() );
1245  $row .= $this->msg( 'word-separator' )->plain();
1246  $row .= '<span style="white-space: nowrap;">';
1247  $row .= $this->msg( 'parentheses' )->rawParams( Linker::formatSize( $file->getSize() ) )->plain();
1248  $row .= '</span>';
1249  $row .= '</td>';
1250 
1251  // Uploading user
1252  $row .= '<td>';
1253  // Hide deleted usernames
1254  if ( $file->isDeleted( File::DELETED_USER ) ) {
1255  $row .= '<span class="history-deleted">' . $this->msg( 'rev-deleted-user' )->escaped() . '</span>';
1256  } else {
1257  if ( $local ) {
1258  $row .= Linker::userLink( $userId, $userText );
1259  $row .= $this->msg( 'word-separator' )->plain();
1260  $row .= '<span style="white-space: nowrap;">';
1261  $row .= Linker::userToolLinks( $userId, $userText );
1262  $row .= '</span>';
1263  } else {
1264  $row .= htmlspecialchars( $userText );
1265  }
1266  }
1267  $row .= '</td>';
1268 
1269  // Don't show deleted descriptions
1270  if ( $file->isDeleted( File::DELETED_COMMENT ) ) {
1271  $row .= '<td><span class="history-deleted">' . $this->msg( 'rev-deleted-comment' )->escaped() . '</span></td>';
1272  } else {
1273  $row .= '<td dir="' . $wgContLang->getDir() . '">' . Linker::formatComment( $description, $this->title ) . '</td>';
1274  }
1275 
1276  $rowClass = null;
1277  wfRunHooks( 'ImagePageFileHistoryLine', array( $this, $file, &$row, &$rowClass ) );
1278  $classAttr = $rowClass ? " class='$rowClass'" : '';
1279 
1280  return "<tr{$classAttr}>{$row}</tr>\n";
1281  }
1282 
1287  protected function getThumbForLine( $file ) {
1288  $lang = $this->getLanguage();
1289  $user = $this->getUser();
1290  if ( $file->allowInlineDisplay() && $file->userCan( File::DELETED_FILE, $user )
1291  && !$file->isDeleted( File::DELETED_FILE )
1292  ) {
1293  $params = array(
1294  'width' => '120',
1295  'height' => '120',
1296  );
1297  $timestamp = wfTimestamp( TS_MW, $file->getTimestamp() );
1298 
1299  $thumbnail = $file->transform( $params );
1300  $options = array(
1301  'alt' => $this->msg( 'filehist-thumbtext',
1302  $lang->userTimeAndDate( $timestamp, $user ),
1303  $lang->userDate( $timestamp, $user ),
1304  $lang->userTime( $timestamp, $user ) )->text(),
1305  'file-link' => true,
1306  );
1308  if ( !$thumbnail ) {
1309  return $this->msg( 'filehist-nothumb' )->escaped();
1310  }
1311 
1312  return $thumbnail->toHtml( $options );
1313  } else {
1314  return $this->msg( 'filehist-nothumb' )->escaped();
1315  }
1316  }
1317 
1321  protected function preventClickjacking( $enable = true ) {
1322  $this->preventClickjacking = $enable;
1323  }
1328  public function getPreventClickjacking() {
1330  }
1331 }
1332 
1334  protected $preventClickjacking = false;
1335 
1339  protected $mImg;
1340 
1344  protected $mTitle;
1345 
1349  function __construct( $imagePage ) {
1350  parent::__construct( $imagePage->getContext() );
1351  $this->mImagePage = $imagePage;
1352  $this->mTitle = clone ( $imagePage->getTitle() );
1353  $this->mTitle->setFragment( '#filehistory' );
1354  $this->mImg = null;
1355  $this->mHist = array();
1356  $this->mRange = array( 0, 0 ); // display range
1357  }
1358 
1362  function getTitle() {
1363  return $this->mTitle;
1364  }
1366  function getQueryInfo() {
1367  return false;
1368  }
1369 
1373  function getIndexField() {
1374  return '';
1375  }
1376 
1381  function formatRow( $row ) {
1382  return '';
1383  }
1384 
1388  function getBody() {
1389  $s = '';
1390  $this->doQuery();
1391  if ( count( $this->mHist ) ) {
1392  $list = new ImageHistoryList( $this->mImagePage );
1393  # Generate prev/next links
1394  $navLink = $this->getNavigationBar();
1395  $s = $list->beginImageHistoryList( $navLink );
1396  // Skip rows there just for paging links
1397  for ( $i = $this->mRange[0]; $i <= $this->mRange[1]; $i++ ) {
1398  $file = $this->mHist[$i];
1399  $s .= $list->imageHistoryLine( !$file->isOld(), $file );
1400  }
1401  $s .= $list->endImageHistoryList( $navLink );
1402 
1403  if ( $list->getPreventClickjacking() ) {
1404  $this->preventClickjacking();
1405  }
1406  }
1407  return $s;
1408  }
1409 
1410  function doQuery() {
1411  if ( $this->mQueryDone ) {
1412  return;
1413  }
1414  $this->mImg = $this->mImagePage->getFile(); // ensure loading
1415  if ( !$this->mImg->exists() ) {
1416  return;
1417  }
1418  $queryLimit = $this->mLimit + 1; // limit plus extra row
1419  if ( $this->mIsBackwards ) {
1420  // Fetch the file history
1421  $this->mHist = $this->mImg->getHistory( $queryLimit, null, $this->mOffset, false );
1422  // The current rev may not meet the offset/limit
1423  $numRows = count( $this->mHist );
1424  if ( $numRows <= $this->mLimit && $this->mImg->getTimestamp() > $this->mOffset ) {
1425  $this->mHist = array_merge( array( $this->mImg ), $this->mHist );
1426  }
1427  } else {
1428  // The current rev may not meet the offset
1429  if ( !$this->mOffset || $this->mImg->getTimestamp() < $this->mOffset ) {
1430  $this->mHist[] = $this->mImg;
1431  }
1432  // Old image versions (fetch extra row for nav links)
1433  $oiLimit = count( $this->mHist ) ? $this->mLimit : $this->mLimit + 1;
1434  // Fetch the file history
1435  $this->mHist = array_merge( $this->mHist,
1436  $this->mImg->getHistory( $oiLimit, $this->mOffset, null, false ) );
1437  }
1438  $numRows = count( $this->mHist ); // Total number of query results
1439  if ( $numRows ) {
1440  # Index value of top item in the list
1441  $firstIndex = $this->mIsBackwards ?
1442  $this->mHist[$numRows - 1]->getTimestamp() : $this->mHist[0]->getTimestamp();
1443  # Discard the extra result row if there is one
1444  if ( $numRows > $this->mLimit && $numRows > 1 ) {
1445  if ( $this->mIsBackwards ) {
1446  # Index value of item past the index
1447  $this->mPastTheEndIndex = $this->mHist[0]->getTimestamp();
1448  # Index value of bottom item in the list
1449  $lastIndex = $this->mHist[1]->getTimestamp();
1450  # Display range
1451  $this->mRange = array( 1, $numRows - 1 );
1452  } else {
1453  # Index value of item past the index
1454  $this->mPastTheEndIndex = $this->mHist[$numRows - 1]->getTimestamp();
1455  # Index value of bottom item in the list
1456  $lastIndex = $this->mHist[$numRows - 2]->getTimestamp();
1457  # Display range
1458  $this->mRange = array( 0, $numRows - 2 );
1459  }
1460  } else {
1461  # Setting indexes to an empty string means that they will be
1462  # omitted if they would otherwise appear in URLs. It just so
1463  # happens that this is the right thing to do in the standard
1464  # UI, in all the relevant cases.
1465  $this->mPastTheEndIndex = '';
1466  # Index value of bottom item in the list
1467  $lastIndex = $this->mIsBackwards ?
1468  $this->mHist[0]->getTimestamp() : $this->mHist[$numRows - 1]->getTimestamp();
1469  # Display range
1470  $this->mRange = array( 0, $numRows - 1 );
1471  }
1472  } else {
1473  $firstIndex = '';
1474  $lastIndex = '';
1475  $this->mPastTheEndIndex = '';
1476  }
1477  if ( $this->mIsBackwards ) {
1478  $this->mIsFirst = ( $numRows < $queryLimit );
1479  $this->mIsLast = ( $this->mOffset == '' );
1480  $this->mLastShown = $firstIndex;
1481  $this->mFirstShown = $lastIndex;
1482  } else {
1483  $this->mIsFirst = ( $this->mOffset == '' );
1484  $this->mIsLast = ( $numRows < $queryLimit );
1485  $this->mLastShown = $lastIndex;
1486  $this->mFirstShown = $firstIndex;
1487  }
1488  $this->mQueryDone = true;
1489  }
1490 
1494  protected function preventClickjacking( $enable = true ) {
1495  $this->preventClickjacking = $enable;
1496  }
1497 
1501  public function getPreventClickjacking() {
1503  }
1504 
1505 }
User\getDefaultOption
static getDefaultOption( $opt)
Get a given default option value.
Definition: User.php:1383
ImageHistoryPseudoPager\$mImg
File $mImg
Definition: ImagePage.php:1324
ImagePage\makeSizeLink
makeSizeLink( $params, $width, $height)
Creates an thumbnail of specified size and returns an HTML link to it.
Definition: ImagePage.php:616
Title\makeTitle
static & makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:398
wfBCP47
wfBCP47( $code)
Get the normalised IETF language tag See unit test for examples.
Definition: GlobalFunctions.php:3985
$request
do that in ParserLimitReportFormat instead use this to modify the parameters of the image and a DIV can begin in one section and end in another Make sure your code can handle that case gracefully See the EditSectionClearerLink extension for an example zero but section is usually empty its values are the globals values my talk my contributions etc etc otherwise the built in rate limiting checks are if enabled also a ContextSource error or success you ll probably need to make sure the header is varied on WebRequest $request
Definition: hooks.txt:1961
Linker\revDeleteLink
static revDeleteLink( $query=array(), $restricted=false, $delete=true)
Creates a (show/hide) link for deleting revisions/log entries.
Definition: Linker.php:2205
ImagePage\getUploadUrl
getUploadUrl()
Definition: ImagePage.php:663
php
skin txt MediaWiki includes four core it has been set as the default in MediaWiki since the replacing Monobook it had been been the default skin since before being replaced by Vector largely rewritten in while keeping its appearance Several legacy skins were removed in the as the burden of supporting them became too heavy to bear Those in etc for skin dependent CSS etc for skin dependent JavaScript These can also be customised on a per user by etc This feature has led to a wide variety of user styles becoming that gallery is a good place to ending in php
Definition: skin.txt:62
File\DELETED_USER
const DELETED_USER
Definition: File.php:54
$html
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses just before the function returns a value If you return an< a > element with HTML attributes $attribs and contents $html will be returned If you return $ret will be returned and may include noclasses & $html
Definition: hooks.txt:1530
ImageHistoryList\$img
File $img
Definition: ImagePage.php:1040
ContextSource\msg
msg()
Get a Message object with context set Parameters are the same as wfMessage()
Definition: ContextSource.php:175
Xml\tags
static tags( $element, $attribs=null, $contents)
Same as Xml::element(), but does not escape contents.
Definition: Xml.php:131
ImageHistoryList
Builds the image revision log shown on image pages.
Definition: ImagePage.php:1032
Linker\userLink
static userLink( $userId, $userName, $altUserName=false)
Make user link (or user contributions for unregistered users)
Definition: Linker.php:1081
File\DELETED_RESTRICTED
const DELETED_RESTRICTED
Definition: File.php:55
ImageHistoryPseudoPager\__construct
__construct( $imagePage)
Definition: ImagePage.php:1333
ImagePage\closeShowImage
closeShowImage()
Definition: ImagePage.php:703
ImagePage\openShowImage
openShowImage()
Definition: ImagePage.php:291
view
$article view()
ImageHistoryList\$showThumb
$showThumb
Definition: ImagePage.php:1050
wfGetDB
& wfGetDB( $db, $groups=array(), $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:3714
text
design txt This is a brief overview of the new design More thorough and up to date information is available on the documentation wiki at etc Handles the details of getting and saving to the user table of the and dealing with sessions and cookies OutputPage Encapsulates the entire HTML page that will be sent in response to any server request It is used by calling its functions to add text
Definition: design.txt:12
$timestamp
if( $limit) $timestamp
Definition: importImages.php:104
ImageHistoryList\imageHistoryLine
imageHistoryLine( $iscur, $file)
Definition: ImagePage.php:1113
wfTimestamp
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
Definition: GlobalFunctions.php:2530
Xml\option
static option( $text, $value=null, $selected=false, $attribs=array())
Convenience function to build an HTML drop-down list item.
Definition: Xml.php:475
ImagePage\uploadLinksBox
uploadLinksBox()
Print out the various links at the bottom of the image page, e.g.
Definition: ImagePage.php:676
ImagePage\doRenderLangOpt
doRenderLangOpt(array $langChoices, $curLang, $defaultLang)
Output a drop-down box for language options for the file.
Definition: ImagePage.php:976
ImageHistoryList\__construct
__construct( $imagePage)
Definition: ImagePage.php:1056
NS_FILE
const NS_FILE
Definition: Defines.php:85
$params
$params
Definition: styleTest.css.php:40
$limit
if( $sleep) $limit
Definition: importImages.php:99
$s
$s
Definition: mergeMessageFileList.php:156
SpecialPage\getTitleFor
static getTitleFor( $name, $subpage=false, $fragment='')
Get a localised Title object for a specified special page name.
Definition: SpecialPage.php:74
Linker\processResponsiveImages
static processResponsiveImages( $file, $thumb, $hp)
Process responsive images: add 1.5x and 2x subimages to the thumbnail, where applicable.
Definition: Linker.php:879
Html\hidden
static hidden( $name, $value, $attribs=array())
Convenience function to produce an input element with type=hidden.
Definition: Html.php:607
ImagePage
Class for viewing MediaWiki file description pages.
Definition: ImagePage.php:28
$wgContLang
this class mediates it Skin Encapsulates a look and feel for the wiki All of the functions that render HTML and make choices about how to render it are here and are called from various other places when and is meant to be subclassed with other skins that may override some of its functions The User object contains a reference to a and so rather than having a global skin object we just rely on the global User and get the skin with $wgUser and also has some character encoding functions and other locale stuff The current user interface language is instantiated as and the content language as $wgContLang
Definition: design.txt:56
Linker\formatComment
static formatComment( $comment, $title=null, $local=false)
This function is called by all recent changes variants, by the page history, and by the user contribu...
Definition: Linker.php:1263
Linker\makeThumbLinkObj
static makeThumbLinkObj(Title $title, $file, $label='', $alt, $align='right', $params=array(), $framed=false, $manualthumb="")
Make HTML for a thumbnail including image, border and caption.
Definition: Linker.php:726
ImageHistoryPseudoPager\preventClickjacking
preventClickjacking( $enable=true)
Definition: ImagePage.php:1478
ContextSource\getUser
getUser()
Get the User object.
Definition: ContextSource.php:132
$link
set to $title object and return false for a match for latest after cache objects are set use the ContentHandler facility to handle CSS and JavaScript for highlighting & $link
Definition: hooks.txt:2160
ImagePage\render
render()
Handler for action=render Include body text only; none of the image extras.
Definition: ImagePage.php:96
ImagePage\$fileLoaded
$fileLoaded
Definition: ImagePage.php:36
wfMessageFallback
wfMessageFallback()
This function accepts multiple message keys and returns a message instance for the first message whic...
Definition: GlobalFunctions.php:1469
ImagePage\setFile
setFile( $file)
Definition: ImagePage.php:65
ImageHistoryPseudoPager\formatRow
formatRow( $row)
Definition: ImagePage.php:1365
Xml\openElement
static openElement( $element, $attribs=null)
This opens an XML element.
Definition: Xml.php:109
Linker\linkKnown
static linkKnown( $target, $html=null, $customAttribs=array(), $query=array(), $options=array( 'known', 'noclasses'))
Identical to link(), except $options defaults to 'known'.
Definition: Linker.php:264
Linker\makeExternalLink
static makeExternalLink( $url, $text, $escape=true, $linktype='', $attribs=array(), $title=null)
Make an external link.
Definition: Linker.php:1034
ImagePage\$repo
FileRepo $repo
Definition: ImagePage.php:35
$dbr
$dbr
Definition: testCompression.php:48
ContextSource\getLanguage
getLanguage()
Get the Language object.
Definition: ContextSource.php:154
ImagePage\newFromID
static newFromID( $id)
Constructor from a page id.
Definition: ImagePage.php:54
FileRepo
Base class for file repositories.
Definition: FileRepo.php:37
Html\closeElement
static closeElement( $element)
Returns "</$element>".
Definition: Html.php:218
ImageHistoryList\getImagePage
getImagePage()
Definition: ImagePage.php:1069
title
to move a page</td >< td > &*You are moving the page across *A non empty talk page already exists under the new or *You uncheck the box below In those you will have to move or merge the page manually if desired</td >< td > be sure to &You are responsible for making sure that links continue to point where they are supposed to go Note that the page will &a page at the new title
Definition: All_system_messages.txt:2703
Html\openElement
static openElement( $element, $attribs=array())
Identical to rawElement(), but has no third parameter and omits the end tag (and the self-closing '/'...
Definition: Html.php:159
ImageHistoryPseudoPager\getBody
getBody()
Definition: ImagePage.php:1372
ImageHistoryPseudoPager\doQuery
doQuery()
Do the query, using information from the object context.
Definition: ImagePage.php:1394
File
Implements some public methods and some protected utility functions which are required by multiple ch...
Definition: File.php:50
$out
$out
Definition: UtfNormalGenerate.php:167
File\DELETED_COMMENT
const DELETED_COMMENT
Definition: File.php:53
Article\getTitle
getTitle()
Get the title object of the article.
Definition: Article.php:194
ImageHistoryPseudoPager\$preventClickjacking
$preventClickjacking
Definition: ImagePage.php:1320
$css
$css
Definition: styleTest.css.php:50
ImagePage\showTOC
showTOC( $metadata)
Create the TOC.
Definition: ImagePage.php:231
ImagePage\imageLinks
imageLinks()
Definition: ImagePage.php:742
ImageHistoryPseudoPager\getIndexField
getIndexField()
Definition: ImagePage.php:1357
ImageHistoryList\$repo
$repo
Definition: ImagePage.php:1050
ContextSource
The simplest way of implementing IContextSource is to hold a RequestContext as a member variable and ...
Definition: ContextSource.php:30
wfMessage
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses just before the function returns a value If you return an< a > element with HTML attributes $attribs and contents $html will be returned If you return $ret will be returned and may include noclasses after processing after in associative array form externallinks including delete and has completed for all link tables default is conds Array Extra conditions for the No matching items in log is displayed if loglist is empty msgKey Array If you want a nice box with a set this to the key of the message First element is the message additional optional elements are parameters for the key that are processed with wfMessage() -> params() ->parseAsBlock() - offset Set to overwrite offset parameter in $wgRequest set to '' to unset offset - wrap String Wrap the message in html(usually something like "&lt
Xml\element
static element( $element, $attribs=null, $contents='', $allowShortTag=true)
Format an XML element with given attributes and, optionally, text content.
Definition: Xml.php:39
ImagePage\newPage
newPage(Title $title)
Definition: ImagePage.php:44
Article\getContext
getContext()
Gets the context this Article is executed in.
Definition: Article.php:1922
wfRunHooks
wfRunHooks( $event, array $args=array(), $deprecatedVersion=null)
Call hook functions defined in $wgHooks.
Definition: GlobalFunctions.php:4066
FileDeleteForm
File deletion user interface.
Definition: FileDeleteForm.php:30
ImageHistoryPseudoPager\getPreventClickjacking
getPreventClickjacking()
Definition: ImagePage.php:1485
Linker\revDeleteLinkDisabled
static revDeleteLinkDisabled( $delete=true)
Creates a dead (show/hide) link for deleting revisions/log entries.
Definition: Linker.php:2222
array
the array() calling protocol came about after MediaWiki 1.4rc1.
List of Api Query prop modules.
ImageHistoryPseudoPager\getQueryInfo
getQueryInfo()
This function should be overridden to provide all parameters needed for the main paged query.
Definition: ImagePage.php:1350
File\FOR_THIS_USER
const FOR_THIS_USER
Definition: File.php:69
global
when a variable name is used in a it is silently declared as a new masking the global
Definition: design.txt:93
ContextSource\setContext
setContext(IContextSource $context)
Set the IContextSource object.
Definition: ContextSource.php:57
ImageHistoryList\preventClickjacking
preventClickjacking( $enable=true)
Definition: ImagePage.php:1307
list
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a etc PHP does not yet have any way to tell the server to actually return and disconnect while still running these but it might have such a feature in the future We handle these by creating a deferred update object and putting those objects on a global list
Definition: deferred.txt:11
ImagePage\showError
showError( $description)
Display an error with a wikitext description.
Definition: ImagePage.php:915
ImageHistoryList\$imagePage
ImagePage $imagePage
Definition: ImagePage.php:1044
ImageHistoryList\getFile
getFile()
Definition: ImagePage.php:1076
Sanitizer\escapeId
static escapeId( $id, $options=array())
Given a value, escape it so that it can be used in an id attribute and return it.
Definition: Sanitizer.php:1099
ImageHistoryPseudoPager\getTitle
getTitle()
Definition: ImagePage.php:1346
$options
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped & $options
Definition: hooks.txt:1530
ImagePage\view
view()
This is the default action of the index.php entry point: just view the page of the given title.
Definition: ImagePage.php:101
ImagePage\imageDupes
imageDupes()
Definition: ImagePage.php:863
TS_MW
const TS_MW
MediaWiki concatenated string timestamp (YYYYMMDDHHMMSS)
Definition: GlobalFunctions.php:2478
$title
presenting them properly to the user as errors is done by the caller $title
Definition: hooks.txt:1324
$name
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:336
FileRepo\getDisplayName
getDisplayName()
Get the human-readable name of the repo.
Definition: FileRepo.php:1722
ImagePage\makeMetadataTable
makeMetadataTable( $metadata)
Make a table with metadata to be shown in the output page.
Definition: ImagePage.php:254
$size
$size
Definition: RandomTest.php:75
Linker\userToolLinks
static userToolLinks( $userId, $userText, $redContribsWhenNoEdits=false, $flags=0, $edits=null)
Generate standard user tool links (talk, contributions, block link, etc.)
Definition: Linker.php:1109
$ul
$ul
Definition: upgradeLogging.php:173
Linker\formatSize
static formatSize( $size)
Format a size in bytes for output, using an appropriate unit (B, KB, MB or GB) according to the magni...
Definition: Linker.php:2064
IndexPager\$mOffset
$mOffset
Definition: Pager.php:83
ImagePage\$displayImg
File $displayImg
Definition: ImagePage.php:32
wfEscapeWikiText
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
Definition: GlobalFunctions.php:2124
ImagePage\getDisplayedFile
getDisplayedFile()
Definition: ImagePage.php:220
Language\fetchLanguageName
static fetchLanguageName( $code, $inLanguage=null, $include='all')
Definition: Language.php:941
ImageHistoryPseudoPager\$mTitle
Title $mTitle
Definition: ImagePage.php:1328
ImagePage\$mExtraDescription
$mExtraDescription
Definition: ImagePage.php:38
$user
please add to it if you re going to add events to the MediaWiki code where normally authentication against an external auth plugin would be creating a account $user
Definition: hooks.txt:237
$file
if(PHP_SAPI !='cli') $file
Definition: UtfNormalTest2.php:30
$count
$count
Definition: UtfNormalTest2.php:96
DB_SLAVE
const DB_SLAVE
Definition: Defines.php:55
Title
Represents a title within MediaWiki.
Definition: Title.php:35
ImagePage\queryImageLinks
queryImageLinks( $target, $limit)
Definition: ImagePage.php:730
ImageHistoryList\$title
Title $title
Definition: ImagePage.php:1036
Xml\closeElement
static closeElement( $element)
Shortcut to close an XML element.
Definition: Xml.php:118
ImageHistoryList\getThumbForLine
getThumbForLine( $file)
Definition: ImagePage.php:1273
$ext
$ext
Definition: NoLocalSettings.php:34
as
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
Definition: distributors.txt:9
wfFindFile
wfFindFile( $title, $options=array())
Find a file.
Definition: GlobalFunctions.php:3757
ImageHistoryList\beginImageHistoryList
beginImageHistoryList( $navLinks='')
Definition: ImagePage.php:1084
ImagePage\getContentObject
getContentObject()
Overloading Article's getContentObject method.
Definition: ImagePage.php:283
ImagePage\compare
compare( $a, $b)
Callback for usort() to do link sorts by (namespace, title) Function copied from Title::compare()
Definition: ImagePage.php:932
Xml\submitButton
static submitButton( $value, $attribs=array())
Convenience function to build an HTML submit button.
Definition: Xml.php:463
ImagePage\getImageLimitsFromOption
getImageLimitsFromOption( $user, $optionName)
Returns the corresponding $wgImageLimits entry for the selected user option.
Definition: ImagePage.php:948
WikiFilePage
Special handling for file pages.
Definition: WikiFilePage.php:28
ReverseChronologicalPager
IndexPager with a formatted navigation bar.
Definition: Pager.php:829
File\DELETED_FILE
const DELETED_FILE
Definition: File.php:52
ImageHistoryPseudoPager
Definition: ImagePage.php:1319
$t
$t
Definition: testCompression.php:65
Article
Class for viewing MediaWiki article and history.
Definition: Article.php:36
ImageHistoryList\$preventClickjacking
$preventClickjacking
Definition: ImagePage.php:1051
ImagePage\loadFile
loadFile()
Definition: ImagePage.php:71
Html\rawElement
static rawElement( $element, $attribs=array(), $contents='')
Returns an HTML element in a string.
Definition: Html.php:121
$query
return true to allow those checks to and false if checking is done use this to change the tables headers temp or archived zone change it to an object instance and return false override the list derivative used the name of the old file when set the default code will be skipped add a value to it if you want to add a cookie that have to vary cache options can modify $query
Definition: hooks.txt:1105
Article\getOldID
getOldID()
Definition: Article.php:284
ReverseChronologicalPager\getNavigationBar
getNavigationBar()
Definition: Pager.php:834
wfLocalFile
wfLocalFile( $title)
Get an object referring to a locally registered file.
Definition: GlobalFunctions.php:3768
Title\newFromID
static newFromID( $id, $flags=0)
Create a new Title from an article ID.
Definition: Title.php:297
$res
$res
Definition: database.txt:21
ImageHistoryList\$current
File $current
Definition: ImagePage.php:1048
Article\viewRedirect
viewRedirect( $target, $appendSubtitle=true, $forceKnown=false)
View redirect.
Definition: Article.php:1446
ImageHistoryList\getPreventClickjacking
getPreventClickjacking()
Definition: ImagePage.php:1314
ImagePage\printSharedImageText
printSharedImageText()
Show a notice that the file is from a shared repository.
Definition: ImagePage.php:635
ImageHistoryList\endImageHistoryList
endImageHistoryList( $navLinks='')
Definition: ImagePage.php:1104
ImagePage\imageHistory
imageHistory()
If the page we've just displayed is in the "Image" namespace, we follow it with an upload history of ...
Definition: ImagePage.php:709
LogEventsList\showLogExtract
static showLogExtract(&$out, $types=array(), $page='', $user='', $param=array())
Show log extract.
Definition: LogEventsList.php:507
$type
$type
Definition: testCompression.php:46