MediaWiki  1.23.0
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 );
451  $this->getTitle(),
452  $label,
453  array(),
454  array( 'page' => $page - 1 )
455  );
456  $thumb1 = Linker::makeThumbLinkObj( $this->getTitle(), $this->displayImg, $link, $label, 'none',
457  array( 'page' => $page - 1 ) );
458  } else {
459  $thumb1 = '';
460  }
461 
462  if ( $page < $count ) {
463  $label = wfMessage( 'imgmultipagenext' )->text();
465  $this->getTitle(),
466  $label,
467  array(),
468  array( 'page' => $page + 1 )
469  );
470  $thumb2 = Linker::makeThumbLinkObj( $this->getTitle(), $this->displayImg, $link, $label, 'none',
471  array( 'page' => $page + 1 ) );
472  } else {
473  $thumb2 = '';
474  }
475 
476  global $wgScript;
477 
478  $formParams = array(
479  'name' => 'pageselector',
480  'action' => $wgScript,
481  );
482  $options = array();
483  for ( $i = 1; $i <= $count; $i++ ) {
484  $options[] = Xml::option( $lang->formatNum( $i ), $i, $i == $page );
485  }
486  $select = Xml::tags( 'select',
487  array( 'id' => 'pageselector', 'name' => 'page' ),
488  implode( "\n", $options ) );
489 
490  $out->addHTML(
491  '</td><td><div class="multipageimagenavbox">' .
492  Xml::openElement( 'form', $formParams ) .
493  Html::hidden( 'title', $this->getTitle()->getPrefixedDBkey() ) .
494  wfMessage( 'imgmultigoto' )->rawParams( $select )->parse() .
495  Xml::submitButton( wfMessage( 'imgmultigo' )->text() ) .
496  Xml::closeElement( 'form' ) .
497  "<hr />$thumb1\n$thumb2<br style=\"clear: both\" /></div></td></tr></table>"
498  );
499  }
500  } elseif ( $this->displayImg->isSafeFile() ) {
501  # if direct link is allowed but it's not a renderable image, show an icon.
502  $icon = $this->displayImg->iconThumb();
503 
504  $out->addHTML( '<div class="fullImageLink" id="file">' .
505  $icon->toHtml( array( 'file-link' => true ) ) .
506  "</div>\n" );
507  }
508 
509  $longDesc = wfMessage( 'parentheses', $this->displayImg->getLongDesc() )->text();
510 
511  $medialink = "[[Media:$filename|$linktext]]";
512 
513  if ( !$this->displayImg->isSafeFile() ) {
514  $warning = wfMessage( 'mediawarning' )->plain();
515  // dirmark is needed here to separate the file name, which
516  // most likely ends in Latin characters, from the description,
517  // which may begin with the file type. In RTL environment
518  // this will get messy.
519  // The dirmark, however, must not be immediately adjacent
520  // to the filename, because it can get copied with it.
521  // See bug 25277.
522  $out->addWikiText( <<<EOT
523 <div class="fullMedia"><span class="dangerousLink">{$medialink}</span> $dirmark<span class="fileInfo">$longDesc</span></div>
524 <div class="mediaWarning">$warning</div>
525 EOT
526  );
527  } else {
528  $out->addWikiText( <<<EOT
529 <div class="fullMedia">{$medialink} {$dirmark}<span class="fileInfo">$longDesc</span>
530 </div>
531 EOT
532  );
533  }
534 
535  $renderLangOptions = $this->displayImg->getAvailableLanguages();
536  if ( count( $renderLangOptions ) >= 1 ) {
537  $currentLanguage = $renderLang;
538  $defaultLang = $this->displayImg->getDefaultRenderLanguage();
539  if ( is_null( $currentLanguage ) ) {
540  $currentLanguage = $defaultLang;
541  }
542  $out->addHtml( $this->doRenderLangOpt( $renderLangOptions, $currentLanguage, $defaultLang ) );
543  }
544 
545  // Add cannot animate thumbnail warning
546  if ( !$this->displayImg->canAnimateThumbIfAppropriate() ) {
547  // Include the extension so wiki admins can
548  // customize it on a per file-type basis
549  // (aka say things like use format X instead).
550  // additionally have a specific message for
551  // file-no-thumb-animation-gif
552  $ext = $this->displayImg->getExtension();
553  $noAnimMesg = wfMessageFallback(
554  'file-no-thumb-animation-' . $ext,
555  'file-no-thumb-animation'
556  )->plain();
557 
558  $out->addWikiText( <<<EOT
559 <div class="mw-noanimatethumb">{$noAnimMesg}</div>
560 EOT
561  );
562  }
563 
564  if ( !$this->displayImg->isLocal() ) {
565  $this->printSharedImageText();
566  }
567  } else {
568  # Image does not exist
569  if ( !$this->getID() ) {
570  # No article exists either
571  # Show deletion log to be consistent with normal articles
573  $out,
574  array( 'delete', 'move' ),
575  $this->getTitle()->getPrefixedText(),
576  '',
577  array( 'lim' => 10,
578  'conds' => array( "log_action != 'revision'" ),
579  'showIfEmpty' => false,
580  'msgKey' => array( 'moveddeleted-notice' )
581  )
582  );
583  }
584 
585  if ( $wgEnableUploads && $user->isAllowed( 'upload' ) ) {
586  // Only show an upload link if the user can upload
587  $uploadTitle = SpecialPage::getTitleFor( 'Upload' );
588  $nofile = array(
589  'filepage-nofile-link',
590  $uploadTitle->getFullURL( array( 'wpDestFile' => $this->mPage->getFile()->getName() ) )
591  );
592  } else {
593  $nofile = 'filepage-nofile';
594  }
595  // Note, if there is an image description page, but
596  // no image, then this setRobotPolicy is overridden
597  // by Article::View().
598  $out->setRobotPolicy( 'noindex,nofollow' );
599  $out->wrapWikiMsg( "<div id='mw-imagepage-nofile' class='plainlinks'>\n$1\n</div>", $nofile );
600  if ( !$this->getID() && $wgSend404Code ) {
601  // If there is no image, no shared image, and no description page,
602  // output a 404, to be consistent with articles.
603  $request->response()->header( 'HTTP/1.1 404 Not Found' );
604  }
605  }
606  $out->setFileVersion( $this->displayImg );
607  }
608 
616  private function makeSizeLink( $params, $width, $height ) {
617  $params['width'] = $width;
618  $params['height'] = $height;
619  $thumbnail = $this->displayImg->transform( $params );
620  if ( $thumbnail && !$thumbnail->isError() ) {
621  return Html::rawElement( 'a', array(
622  'href' => $thumbnail->getUrl(),
623  'class' => 'mw-thumbnail-link'
624  ), wfMessage( 'show-big-image-size' )->numParams(
625  $thumbnail->getWidth(), $thumbnail->getHeight()
626  )->parse() );
627  } else {
628  return '';
629  }
630  }
631 
635  protected function printSharedImageText() {
636  $out = $this->getContext()->getOutput();
637  $this->loadFile();
638 
639  $descUrl = $this->mPage->getFile()->getDescriptionUrl();
640  $descText = $this->mPage->getFile()->getDescriptionText( $this->getContext()->getLanguage() );
641 
642  /* Add canonical to head if there is no local page for this shared file */
643  if ( $descUrl && $this->mPage->getID() == 0 ) {
644  $out->setCanonicalUrl( $descUrl );
645  }
646 
647  $wrap = "<div class=\"sharedUploadNotice\">\n$1\n</div>\n";
648  $repo = $this->mPage->getFile()->getRepo()->getDisplayName();
649 
650  if ( $descUrl && $descText && wfMessage( 'sharedupload-desc-here' )->plain() !== '-' ) {
651  $out->wrapWikiMsg( $wrap, array( 'sharedupload-desc-here', $repo, $descUrl ) );
652  } elseif ( $descUrl && wfMessage( 'sharedupload-desc-there' )->plain() !== '-' ) {
653  $out->wrapWikiMsg( $wrap, array( 'sharedupload-desc-there', $repo, $descUrl ) );
654  } else {
655  $out->wrapWikiMsg( $wrap, array( 'sharedupload', $repo ), ''/*BACKCOMPAT*/ );
656  }
657 
658  if ( $descText ) {
659  $this->mExtraDescription = $descText;
660  }
661  }
662 
663  public function getUploadUrl() {
664  $this->loadFile();
665  $uploadTitle = SpecialPage::getTitleFor( 'Upload' );
666  return $uploadTitle->getFullURL( array(
667  'wpDestFile' => $this->mPage->getFile()->getName(),
668  'wpForReUpload' => 1
669  ) );
670  }
671 
676  protected function uploadLinksBox() {
677  global $wgEnableUploads;
678 
679  if ( !$wgEnableUploads ) {
680  return;
681  }
682 
683  $this->loadFile();
684  if ( !$this->mPage->getFile()->isLocal() ) {
685  return;
686  }
687 
688  $out = $this->getContext()->getOutput();
689  $out->addHTML( "<ul>\n" );
690 
691  # "Upload a new version of this file" link
692  $canUpload = $this->getTitle()->userCan( 'upload', $this->getContext()->getUser() );
693  if ( $canUpload && UploadBase::userCanReUpload( $this->getContext()->getUser(), $this->mPage->getFile()->name ) ) {
694  $ulink = Linker::makeExternalLink( $this->getUploadUrl(), wfMessage( 'uploadnewversion-linktext' )->text() );
695  $out->addHTML( "<li id=\"mw-imagepage-reupload-link\"><div class=\"plainlinks\">{$ulink}</div></li>\n" );
696  } else {
697  $out->addHTML( "<li id=\"mw-imagepage-upload-disallowed\">" . $this->getContext()->msg( 'upload-disallowed-here' )->escaped() . "</li>\n" );
698  }
699 
700  $out->addHTML( "</ul>\n" );
701  }
702 
703  protected function closeShowImage() { } # For overloading
704 
709  protected function imageHistory() {
710  $this->loadFile();
711  $out = $this->getContext()->getOutput();
712  $pager = new ImageHistoryPseudoPager( $this );
713  $out->addHTML( $pager->getBody() );
714  $out->preventClickjacking( $pager->getPreventClickjacking() );
715 
716  $this->mPage->getFile()->resetHistory(); // free db resources
717 
718  # Exist check because we don't want to show this on pages where an image
719  # doesn't exist along with the noimage message, that would suck. -ævar
720  if ( $this->mPage->getFile()->exists() ) {
721  $this->uploadLinksBox();
722  }
723  }
724 
730  protected function queryImageLinks( $target, $limit ) {
731  $dbr = wfGetDB( DB_SLAVE );
732 
733  return $dbr->select(
734  array( 'imagelinks', 'page' ),
735  array( 'page_namespace', 'page_title', 'page_is_redirect', 'il_to' ),
736  array( 'il_to' => $target, 'il_from = page_id' ),
737  __METHOD__,
738  array( 'LIMIT' => $limit + 1, 'ORDER BY' => 'il_from', )
739  );
740  }
741 
742  protected function imageLinks() {
743  $limit = 100;
744 
745  $out = $this->getContext()->getOutput();
746  $res = $this->queryImageLinks( $this->getTitle()->getDBkey(), $limit + 1 );
747  $rows = array();
748  $redirects = array();
749  foreach ( $res as $row ) {
750  if ( $row->page_is_redirect ) {
751  $redirects[$row->page_title] = array();
752  }
753  $rows[] = $row;
754  }
755  $count = count( $rows );
756 
757  $hasMore = $count > $limit;
758  if ( !$hasMore && count( $redirects ) ) {
759  $res = $this->queryImageLinks( array_keys( $redirects ),
760  $limit - count( $rows ) + 1 );
761  foreach ( $res as $row ) {
762  $redirects[$row->il_to][] = $row;
763  $count++;
764  }
765  $hasMore = ( $res->numRows() + count( $rows ) ) > $limit;
766  }
767 
768  if ( $count == 0 ) {
769  $out->wrapWikiMsg(
770  Html::rawElement( 'div',
771  array( 'id' => 'mw-imagepage-nolinkstoimage' ), "\n$1\n" ),
772  'nolinkstoimage'
773  );
774  return;
775  }
776 
777  $out->addHTML( "<div id='mw-imagepage-section-linkstoimage'>\n" );
778  if ( !$hasMore ) {
779  $out->addWikiMsg( 'linkstoimage', $count );
780  } else {
781  // More links than the limit. Add a link to [[Special:Whatlinkshere]]
782  $out->addWikiMsg( 'linkstoimage-more',
783  $this->getContext()->getLanguage()->formatNum( $limit ),
784  $this->getTitle()->getPrefixedDBkey()
785  );
786  }
787 
788  $out->addHTML(
789  Html::openElement( 'ul',
790  array( 'class' => 'mw-imagepage-linkstoimage' ) ) . "\n"
791  );
792  $count = 0;
793 
794  // Sort the list by namespace:title
795  usort( $rows, array( $this, 'compare' ) );
796 
797  // Create links for every element
798  $currentCount = 0;
799  foreach ( $rows as $element ) {
800  $currentCount++;
801  if ( $currentCount > $limit ) {
802  break;
803  }
804 
805  $query = array();
806  # Add a redirect=no to make redirect pages reachable
807  if ( isset( $redirects[$element->page_title] ) ) {
808  $query['redirect'] = 'no';
809  }
811  Title::makeTitle( $element->page_namespace, $element->page_title ),
812  null, array(), $query
813  );
814  if ( !isset( $redirects[$element->page_title] ) ) {
815  # No redirects
816  $liContents = $link;
817  } elseif ( count( $redirects[$element->page_title] ) === 0 ) {
818  # Redirect without usages
819  $liContents = wfMessage( 'linkstoimage-redirect' )->rawParams( $link, '' )->parse();
820  } else {
821  # Redirect with usages
822  $li = '';
823  foreach ( $redirects[$element->page_title] as $row ) {
824  $currentCount++;
825  if ( $currentCount > $limit ) {
826  break;
827  }
828 
829  $link2 = Linker::linkKnown( Title::makeTitle( $row->page_namespace, $row->page_title ) );
830  $li .= Html::rawElement(
831  'li',
832  array( 'class' => 'mw-imagepage-linkstoimage-ns' . $element->page_namespace ),
833  $link2
834  ) . "\n";
835  }
836 
838  'ul',
839  array( 'class' => 'mw-imagepage-redirectstofile' ),
840  $li
841  ) . "\n";
842  $liContents = wfMessage( 'linkstoimage-redirect' )->rawParams(
843  $link, $ul )->parse();
844  }
845  $out->addHTML( Html::rawElement(
846  'li',
847  array( 'class' => 'mw-imagepage-linkstoimage-ns' . $element->page_namespace ),
848  $liContents
849  ) . "\n"
850  );
851 
852  };
853  $out->addHTML( Html::closeElement( 'ul' ) . "\n" );
854  $res->free();
855 
856  // Add a links to [[Special:Whatlinkshere]]
857  if ( $count > $limit ) {
858  $out->addWikiMsg( 'morelinkstoimage', $this->getTitle()->getPrefixedDBkey() );
859  }
860  $out->addHTML( Html::closeElement( 'div' ) . "\n" );
861  }
862 
863  protected function imageDupes() {
864  $this->loadFile();
865  $out = $this->getContext()->getOutput();
866 
867  $dupes = $this->mPage->getDuplicates();
868  if ( count( $dupes ) == 0 ) {
869  return;
870  }
871 
872  $out->addHTML( "<div id='mw-imagepage-section-duplicates'>\n" );
873  $out->addWikiMsg( 'duplicatesoffile',
874  $this->getContext()->getLanguage()->formatNum( count( $dupes ) ), $this->getTitle()->getDBkey()
875  );
876  $out->addHTML( "<ul class='mw-imagepage-duplicates'>\n" );
877 
881  foreach ( $dupes as $file ) {
882  $fromSrc = '';
883  if ( $file->isLocal() ) {
884  $link = Linker::linkKnown( $file->getTitle() );
885  } else {
886  $link = Linker::makeExternalLink( $file->getDescriptionUrl(),
887  $file->getTitle()->getPrefixedText() );
888  $fromSrc = wfMessage( 'shared-repo-from', $file->getRepo()->getDisplayName() )->text();
889  }
890  $out->addHTML( "<li>{$link} {$fromSrc}</li>\n" );
891  }
892  $out->addHTML( "</ul></div>\n" );
893  }
894 
898  public function delete() {
899  $file = $this->mPage->getFile();
900  if ( !$file->exists() || !$file->isLocal() || $file->getRedirected() ) {
901  // Standard article deletion
902  parent::delete();
903  return;
904  }
905 
906  $deleter = new FileDeleteForm( $file );
907  $deleter->execute();
908  }
909 
915  function showError( $description ) {
916  $out = $this->getContext()->getOutput();
917  $out->setPageTitle( wfMessage( 'internalerror' ) );
918  $out->setRobotPolicy( 'noindex,nofollow' );
919  $out->setArticleRelated( false );
920  $out->enableClientCache( false );
921  $out->addWikiText( $description );
922  }
923 
932  protected function compare( $a, $b ) {
933  if ( $a->page_namespace == $b->page_namespace ) {
934  return strcmp( $a->page_title, $b->page_title );
935  } else {
936  return $a->page_namespace - $b->page_namespace;
937  }
938  }
939 
948  public function getImageLimitsFromOption( $user, $optionName ) {
949  global $wgImageLimits;
950 
951  $option = $user->getIntOption( $optionName );
952  if ( !isset( $wgImageLimits[$option] ) ) {
953  $option = User::getDefaultOption( $optionName );
954  }
955 
956  // The user offset might still be incorrect, specially if
957  // $wgImageLimits got changed (see bug #8858).
958  if ( !isset( $wgImageLimits[$option] ) ) {
959  // Default to the first offset in $wgImageLimits
960  $option = 0;
961  }
962 
963  return isset( $wgImageLimits[$option] )
964  ? $wgImageLimits[$option]
965  : array( 800, 600 ); // if nothing is set, fallback to a hardcoded default
966  }
967 
976  protected function doRenderLangOpt( array $langChoices, $curLang, $defaultLang ) {
977  global $wgScript;
978  sort( $langChoices );
979  $curLang = wfBCP47( $curLang );
980  $defaultLang = wfBCP47( $defaultLang );
981  $opts = '';
982  $haveCurrentLang = false;
983  $haveDefaultLang = false;
984 
985  // We make a list of all the language choices in the file.
986  // Additionally if the default language to render this file
987  // is not included as being in this file (for example, in svgs
988  // usually the fallback content is the english content) also
989  // include a choice for that. Last of all, if we're viewing
990  // the file in a language not on the list, add it as a choice.
991  foreach ( $langChoices as $lang ) {
992  $code = wfBCP47( $lang );
993  $name = Language::fetchLanguageName( $code, $this->getContext()->getLanguage()->getCode() );
994  if ( $name !== '' ) {
995  $display = wfMessage( 'img-lang-opt', $code, $name )->text();
996  } else {
997  $display = $code;
998  }
999  $opts .= "\n" . Xml::option( $display, $code, $curLang === $code );
1000  if ( $curLang === $code ) {
1001  $haveCurrentLang = true;
1002  }
1003  if ( $defaultLang === $code ) {
1004  $haveDefaultLang = true;
1005  }
1006  }
1007  if ( !$haveDefaultLang ) {
1008  // Its hard to know if the content is really in the default language, or
1009  // if its just unmarked content that could be in any language.
1010  $opts = Xml::option( wfMessage( 'img-lang-default' )->text(), $defaultLang, $defaultLang === $curLang ) . $opts;
1011  }
1012  if ( !$haveCurrentLang && $defaultLang !== $curLang ) {
1013  $name = Language::fetchLanguageName( $curLang, $this->getContext()->getLanguage()->getCode() );
1014  if ( $name !== '' ) {
1015  $display = wfMessage( 'img-lang-opt', $curLang, $name )->text();
1016  } else {
1017  $display = $curLang;
1018  }
1019  $opts = Xml::option( $display, $curLang, true ) . $opts;
1020  }
1021 
1022  $select = Html::rawElement( 'select', array( 'id' => 'mw-imglangselector', 'name' => 'lang' ), $opts );
1023  $submit = Xml::submitButton( wfMessage( 'img-lang-go' )->text() );
1024 
1025  $formContents = wfMessage( 'img-lang-info' )->rawParams( $select, $submit )->parse()
1026  . Html::hidden( 'title', $this->getTitle()->getPrefixedDBkey() );
1027 
1028  $langSelectLine = Html::rawElement( 'div', array( 'id' => 'mw-imglangselector-line' ),
1029  Html::rawElement( 'form', array( 'action' => $wgScript ), $formContents )
1030  );
1031  return $langSelectLine;
1032  }
1033 }
1040 class ImageHistoryList extends ContextSource {
1041 
1045  protected $title;
1050  protected $img;
1051 
1055  protected $imagePage;
1056 
1060  protected $current;
1061 
1062  protected $repo, $showThumb;
1063  protected $preventClickjacking = false;
1064 
1068  public function __construct( $imagePage ) {
1069  global $wgShowArchiveThumbnails;
1070  $this->current = $imagePage->getFile();
1071  $this->img = $imagePage->getDisplayedFile();
1072  $this->title = $imagePage->getTitle();
1073  $this->imagePage = $imagePage;
1074  $this->showThumb = $wgShowArchiveThumbnails && $this->img->canRender();
1075  $this->setContext( $imagePage->getContext() );
1076  }
1077 
1081  public function getImagePage() {
1083  }
1084 
1088  public function getFile() {
1089  return $this->img;
1090  }
1091 
1096  public function beginImageHistoryList( $navLinks = '' ) {
1097  return Xml::element( 'h2', array( 'id' => 'filehistory' ), $this->msg( 'filehist' )->text() ) . "\n"
1098  . "<div id=\"mw-imagepage-section-filehistory\">\n"
1099  . $this->msg( 'filehist-help' )->parseAsBlock()
1100  . $navLinks . "\n"
1101  . Xml::openElement( 'table', array( 'class' => 'wikitable filehistory' ) ) . "\n"
1102  . '<tr><td></td>'
1103  . ( $this->current->isLocal() && ( $this->getUser()->isAllowedAny( 'delete', 'deletedhistory' ) ) ? '<td></td>' : '' )
1104  . '<th>' . $this->msg( 'filehist-datetime' )->escaped() . '</th>'
1105  . ( $this->showThumb ? '<th>' . $this->msg( 'filehist-thumb' )->escaped() . '</th>' : '' )
1106  . '<th>' . $this->msg( 'filehist-dimensions' )->escaped() . '</th>'
1107  . '<th>' . $this->msg( 'filehist-user' )->escaped() . '</th>'
1108  . '<th>' . $this->msg( 'filehist-comment' )->escaped() . '</th>'
1109  . "</tr>\n";
1110  }
1116  public function endImageHistoryList( $navLinks = '' ) {
1117  return "</table>\n$navLinks\n</div>\n";
1118  }
1119 
1125  public function imageHistoryLine( $iscur, $file ) {
1127 
1128  $user = $this->getUser();
1129  $lang = $this->getLanguage();
1130  $timestamp = wfTimestamp( TS_MW, $file->getTimestamp() );
1131  $img = $iscur ? $file->getName() : $file->getArchiveName();
1132  $userId = $file->getUser( 'id' );
1133  $userText = $file->getUser( 'text' );
1134  $description = $file->getDescription( File::FOR_THIS_USER, $user );
1135 
1136  $local = $this->current->isLocal();
1137  $row = $selected = '';
1138 
1139  // Deletion link
1140  if ( $local && ( $user->isAllowedAny( 'delete', 'deletedhistory' ) ) ) {
1141  $row .= '<td>';
1142  # Link to remove from history
1143  if ( $user->isAllowed( 'delete' ) ) {
1144  $q = array( 'action' => 'delete' );
1145  if ( !$iscur ) {
1146  $q['oldimage'] = $img;
1147  }
1148  $row .= Linker::linkKnown(
1149  $this->title,
1150  $this->msg( $iscur ? 'filehist-deleteall' : 'filehist-deleteone' )->escaped(),
1151  array(), $q
1152  );
1153  }
1154  # Link to hide content. Don't show useless link to people who cannot hide revisions.
1155  $canHide = $user->isAllowed( 'deleterevision' );
1156  if ( $canHide || ( $user->isAllowed( 'deletedhistory' ) && $file->getVisibility() ) ) {
1157  if ( $user->isAllowed( 'delete' ) ) {
1158  $row .= '<br />';
1159  }
1160  // If file is top revision or locked from this user, don't link
1161  if ( $iscur || !$file->userCan( File::DELETED_RESTRICTED, $user ) ) {
1162  $del = Linker::revDeleteLinkDisabled( $canHide );
1163  } else {
1164  list( $ts, ) = explode( '!', $img, 2 );
1165  $query = array(
1166  'type' => 'oldimage',
1167  'target' => $this->title->getPrefixedText(),
1168  'ids' => $ts,
1169  );
1170  $del = Linker::revDeleteLink( $query,
1171  $file->isDeleted( File::DELETED_RESTRICTED ), $canHide );
1172  }
1173  $row .= $del;
1174  }
1175  $row .= '</td>';
1176  }
1177 
1178  // Reversion link/current indicator
1179  $row .= '<td>';
1180  if ( $iscur ) {
1181  $row .= $this->msg( 'filehist-current' )->escaped();
1182  } elseif ( $local && $this->title->quickUserCan( 'edit', $user )
1183  && $this->title->quickUserCan( 'upload', $user )
1184  ) {
1185  if ( $file->isDeleted( File::DELETED_FILE ) ) {
1186  $row .= $this->msg( 'filehist-revert' )->escaped();
1187  } else {
1188  $row .= Linker::linkKnown(
1189  $this->title,
1190  $this->msg( 'filehist-revert' )->escaped(),
1191  array(),
1192  array(
1193  'action' => 'revert',
1194  'oldimage' => $img,
1195  'wpEditToken' => $user->getEditToken( $img )
1196  )
1197  );
1198  }
1199  }
1200  $row .= '</td>';
1201 
1202  // Date/time and image link
1203  if ( $file->getTimestamp() === $this->img->getTimestamp() ) {
1204  $selected = "class='filehistory-selected'";
1205  }
1206  $row .= "<td $selected style='white-space: nowrap;'>";
1207  if ( !$file->userCan( File::DELETED_FILE, $user ) ) {
1208  # Don't link to unviewable files
1209  $row .= '<span class="history-deleted">' . $lang->userTimeAndDate( $timestamp, $user ) . '</span>';
1210  } elseif ( $file->isDeleted( File::DELETED_FILE ) ) {
1211  if ( $local ) {
1212  $this->preventClickjacking();
1213  $revdel = SpecialPage::getTitleFor( 'Revisiondelete' );
1214  # Make a link to review the image
1215  $url = Linker::linkKnown(
1216  $revdel,
1217  $lang->userTimeAndDate( $timestamp, $user ),
1218  array(),
1219  array(
1220  'target' => $this->title->getPrefixedText(),
1221  'file' => $img,
1222  'token' => $user->getEditToken( $img )
1223  )
1224  );
1225  } else {
1226  $url = $lang->userTimeAndDate( $timestamp, $user );
1227  }
1228  $row .= '<span class="history-deleted">' . $url . '</span>';
1229  } else {
1230  $url = $iscur ? $this->current->getUrl() : $this->current->getArchiveUrl( $img );
1231  $row .= Xml::element( 'a', array( 'href' => $url ), $lang->userTimeAndDate( $timestamp, $user ) );
1232  }
1233  $row .= "</td>";
1234 
1235  // Thumbnail
1236  if ( $this->showThumb ) {
1237  $row .= '<td>' . $this->getThumbForLine( $file ) . '</td>';
1238  }
1239 
1240  // Image dimensions + size
1241  $row .= '<td>';
1242  $row .= htmlspecialchars( $file->getDimensionsString() );
1243  $row .= $this->msg( 'word-separator' )->plain();
1244  $row .= '<span style="white-space: nowrap;">';
1245  $row .= $this->msg( 'parentheses' )->rawParams( Linker::formatSize( $file->getSize() ) )->plain();
1246  $row .= '</span>';
1247  $row .= '</td>';
1248 
1249  // Uploading user
1250  $row .= '<td>';
1251  // Hide deleted usernames
1252  if ( $file->isDeleted( File::DELETED_USER ) ) {
1253  $row .= '<span class="history-deleted">' . $this->msg( 'rev-deleted-user' )->escaped() . '</span>';
1254  } else {
1255  if ( $local ) {
1256  $row .= Linker::userLink( $userId, $userText );
1257  $row .= $this->msg( 'word-separator' )->plain();
1258  $row .= '<span style="white-space: nowrap;">';
1259  $row .= Linker::userToolLinks( $userId, $userText );
1260  $row .= '</span>';
1261  } else {
1262  $row .= htmlspecialchars( $userText );
1263  }
1264  }
1265  $row .= '</td>';
1266 
1267  // Don't show deleted descriptions
1268  if ( $file->isDeleted( File::DELETED_COMMENT ) ) {
1269  $row .= '<td><span class="history-deleted">' . $this->msg( 'rev-deleted-comment' )->escaped() . '</span></td>';
1270  } else {
1271  $row .= '<td dir="' . $wgContLang->getDir() . '">' . Linker::formatComment( $description, $this->title ) . '</td>';
1272  }
1273 
1274  $rowClass = null;
1275  wfRunHooks( 'ImagePageFileHistoryLine', array( $this, $file, &$row, &$rowClass ) );
1276  $classAttr = $rowClass ? " class='$rowClass'" : '';
1277 
1278  return "<tr{$classAttr}>{$row}</tr>\n";
1279  }
1280 
1285  protected function getThumbForLine( $file ) {
1286  $lang = $this->getLanguage();
1287  $user = $this->getUser();
1288  if ( $file->allowInlineDisplay() && $file->userCan( File::DELETED_FILE, $user )
1289  && !$file->isDeleted( File::DELETED_FILE )
1290  ) {
1291  $params = array(
1292  'width' => '120',
1293  'height' => '120',
1294  );
1295  $timestamp = wfTimestamp( TS_MW, $file->getTimestamp() );
1296 
1297  $thumbnail = $file->transform( $params );
1298  $options = array(
1299  'alt' => $this->msg( 'filehist-thumbtext',
1300  $lang->userTimeAndDate( $timestamp, $user ),
1301  $lang->userDate( $timestamp, $user ),
1302  $lang->userTime( $timestamp, $user ) )->text(),
1303  'file-link' => true,
1304  );
1306  if ( !$thumbnail ) {
1307  return $this->msg( 'filehist-nothumb' )->escaped();
1308  }
1309 
1310  return $thumbnail->toHtml( $options );
1311  } else {
1312  return $this->msg( 'filehist-nothumb' )->escaped();
1313  }
1314  }
1315 
1319  protected function preventClickjacking( $enable = true ) {
1320  $this->preventClickjacking = $enable;
1321  }
1326  public function getPreventClickjacking() {
1328  }
1329 }
1330 
1332  protected $preventClickjacking = false;
1333 
1337  protected $mImg;
1338 
1342  protected $mTitle;
1343 
1347  function __construct( $imagePage ) {
1348  parent::__construct( $imagePage->getContext() );
1349  $this->mImagePage = $imagePage;
1350  $this->mTitle = clone ( $imagePage->getTitle() );
1351  $this->mTitle->setFragment( '#filehistory' );
1352  $this->mImg = null;
1353  $this->mHist = array();
1354  $this->mRange = array( 0, 0 ); // display range
1355  }
1356 
1360  function getTitle() {
1361  return $this->mTitle;
1362  }
1364  function getQueryInfo() {
1365  return false;
1366  }
1367 
1371  function getIndexField() {
1372  return '';
1373  }
1374 
1379  function formatRow( $row ) {
1380  return '';
1381  }
1382 
1386  function getBody() {
1387  $s = '';
1388  $this->doQuery();
1389  if ( count( $this->mHist ) ) {
1390  $list = new ImageHistoryList( $this->mImagePage );
1391  # Generate prev/next links
1392  $navLink = $this->getNavigationBar();
1393  $s = $list->beginImageHistoryList( $navLink );
1394  // Skip rows there just for paging links
1395  for ( $i = $this->mRange[0]; $i <= $this->mRange[1]; $i++ ) {
1396  $file = $this->mHist[$i];
1397  $s .= $list->imageHistoryLine( !$file->isOld(), $file );
1398  }
1399  $s .= $list->endImageHistoryList( $navLink );
1400 
1401  if ( $list->getPreventClickjacking() ) {
1402  $this->preventClickjacking();
1403  }
1404  }
1405  return $s;
1406  }
1407 
1408  function doQuery() {
1409  if ( $this->mQueryDone ) {
1410  return;
1411  }
1412  $this->mImg = $this->mImagePage->getFile(); // ensure loading
1413  if ( !$this->mImg->exists() ) {
1414  return;
1415  }
1416  $queryLimit = $this->mLimit + 1; // limit plus extra row
1417  if ( $this->mIsBackwards ) {
1418  // Fetch the file history
1419  $this->mHist = $this->mImg->getHistory( $queryLimit, null, $this->mOffset, false );
1420  // The current rev may not meet the offset/limit
1421  $numRows = count( $this->mHist );
1422  if ( $numRows <= $this->mLimit && $this->mImg->getTimestamp() > $this->mOffset ) {
1423  $this->mHist = array_merge( array( $this->mImg ), $this->mHist );
1424  }
1425  } else {
1426  // The current rev may not meet the offset
1427  if ( !$this->mOffset || $this->mImg->getTimestamp() < $this->mOffset ) {
1428  $this->mHist[] = $this->mImg;
1429  }
1430  // Old image versions (fetch extra row for nav links)
1431  $oiLimit = count( $this->mHist ) ? $this->mLimit : $this->mLimit + 1;
1432  // Fetch the file history
1433  $this->mHist = array_merge( $this->mHist,
1434  $this->mImg->getHistory( $oiLimit, $this->mOffset, null, false ) );
1435  }
1436  $numRows = count( $this->mHist ); // Total number of query results
1437  if ( $numRows ) {
1438  # Index value of top item in the list
1439  $firstIndex = $this->mIsBackwards ?
1440  $this->mHist[$numRows - 1]->getTimestamp() : $this->mHist[0]->getTimestamp();
1441  # Discard the extra result row if there is one
1442  if ( $numRows > $this->mLimit && $numRows > 1 ) {
1443  if ( $this->mIsBackwards ) {
1444  # Index value of item past the index
1445  $this->mPastTheEndIndex = $this->mHist[0]->getTimestamp();
1446  # Index value of bottom item in the list
1447  $lastIndex = $this->mHist[1]->getTimestamp();
1448  # Display range
1449  $this->mRange = array( 1, $numRows - 1 );
1450  } else {
1451  # Index value of item past the index
1452  $this->mPastTheEndIndex = $this->mHist[$numRows - 1]->getTimestamp();
1453  # Index value of bottom item in the list
1454  $lastIndex = $this->mHist[$numRows - 2]->getTimestamp();
1455  # Display range
1456  $this->mRange = array( 0, $numRows - 2 );
1457  }
1458  } else {
1459  # Setting indexes to an empty string means that they will be
1460  # omitted if they would otherwise appear in URLs. It just so
1461  # happens that this is the right thing to do in the standard
1462  # UI, in all the relevant cases.
1463  $this->mPastTheEndIndex = '';
1464  # Index value of bottom item in the list
1465  $lastIndex = $this->mIsBackwards ?
1466  $this->mHist[0]->getTimestamp() : $this->mHist[$numRows - 1]->getTimestamp();
1467  # Display range
1468  $this->mRange = array( 0, $numRows - 1 );
1469  }
1470  } else {
1471  $firstIndex = '';
1472  $lastIndex = '';
1473  $this->mPastTheEndIndex = '';
1474  }
1475  if ( $this->mIsBackwards ) {
1476  $this->mIsFirst = ( $numRows < $queryLimit );
1477  $this->mIsLast = ( $this->mOffset == '' );
1478  $this->mLastShown = $firstIndex;
1479  $this->mFirstShown = $lastIndex;
1480  } else {
1481  $this->mIsFirst = ( $this->mOffset == '' );
1482  $this->mIsLast = ( $numRows < $queryLimit );
1483  $this->mLastShown = $lastIndex;
1484  $this->mFirstShown = $firstIndex;
1485  }
1486  $this->mQueryDone = true;
1487  }
1488 
1492  protected function preventClickjacking( $enable = true ) {
1493  $this->preventClickjacking = $enable;
1494  }
1495 
1499  public function getPreventClickjacking() {
1501  }
1502 
1503 }
User\getDefaultOption
static getDefaultOption( $opt)
Get a given default option value.
Definition: User.php:1383
ImageHistoryPseudoPager\$mImg
File $mImg
Definition: ImagePage.php:1322
ImagePage\makeSizeLink
makeSizeLink( $params, $width, $height)
Creates an thumbnail of specified size and returns an HTML link to it.
Definition: ImagePage.php:614
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:3920
Linker\revDeleteLink
static revDeleteLink( $query=array(), $restricted=false, $delete=true)
Creates a (show/hide) link for deleting revisions/log entries.
Definition: Linker.php:2196
ImagePage\getUploadUrl
getUploadUrl()
Definition: ImagePage.php:661
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:1038
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:1030
Linker\userLink
static userLink( $userId, $userName, $altUserName=false)
Make user link (or user contributions for unregistered users)
Definition: Linker.php:1072
File\DELETED_RESTRICTED
const DELETED_RESTRICTED
Definition: File.php:55
ImageHistoryPseudoPager\__construct
__construct( $imagePage)
Definition: ImagePage.php:1331
ImagePage\closeShowImage
closeShowImage()
Definition: ImagePage.php:701
ImagePage\openShowImage
openShowImage()
Definition: ImagePage.php:291
view
$article view()
ImageHistoryList\$showThumb
$showThumb
Definition: ImagePage.php:1048
wfGetDB
& wfGetDB( $db, $groups=array(), $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:3650
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:1111
wfTimestamp
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
Definition: GlobalFunctions.php:2483
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:674
ImagePage\doRenderLangOpt
doRenderLangOpt(array $langChoices, $curLang, $defaultLang)
Output a drop-down box for language options for the file.
Definition: ImagePage.php:974
ImageHistoryList\__construct
__construct( $imagePage)
Definition: ImagePage.php:1054
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:662
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:1254
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:1476
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:2149
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:1422
ImagePage\setFile
setFile( $file)
Definition: ImagePage.php:65
ImageHistoryPseudoPager\formatRow
formatRow( $row)
Definition: ImagePage.php:1363
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>", except if $wgWellFormedXml is off, in which case it returns the empty string w...
Definition: Html.php:235
ImageHistoryList\getImagePage
getImagePage()
Definition: ImagePage.php:1067
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:166
ImageHistoryPseudoPager\getBody
getBody()
Definition: ImagePage.php:1370
ImageHistoryPseudoPager\doQuery
doQuery()
Do the query, using information from the object context.
Definition: ImagePage.php:1392
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:1318
$css
$css
Definition: styleTest.css.php:50
ImagePage\showTOC
showTOC( $metadata)
Create the TOC.
Definition: ImagePage.php:231
ImagePage\imageLinks
imageLinks()
Definition: ImagePage.php:740
ImageHistoryPseudoPager\getIndexField
getIndexField()
Definition: ImagePage.php:1355
ImageHistoryList\$repo
$repo
Definition: ImagePage.php:1048
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:4001
FileDeleteForm
File deletion user interface.
Definition: FileDeleteForm.php:30
ImageHistoryPseudoPager\getPreventClickjacking
getPreventClickjacking()
Definition: ImagePage.php:1483
Linker\revDeleteLinkDisabled
static revDeleteLinkDisabled( $delete=true)
Creates a dead (show/hide) link for deleting revisions/log entries.
Definition: Linker.php:2213
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:1348
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:1305
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:913
ImageHistoryList\$imagePage
ImagePage $imagePage
Definition: ImagePage.php:1042
ImageHistoryList\getFile
getFile()
Definition: ImagePage.php:1074
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:1082
ImageHistoryPseudoPager\getTitle
getTitle()
Definition: ImagePage.php:1344
$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:861
TS_MW
const TS_MW
MediaWiki concatenated string timestamp (YYYYMMDDHHMMSS)
Definition: GlobalFunctions.php:2431
$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:1100
$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:2055
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:2077
ImagePage\getDisplayedFile
getDisplayedFile()
Definition: ImagePage.php:220
Language\fetchLanguageName
static fetchLanguageName( $code, $inLanguage=null, $include='all')
Definition: Language.php:936
ImageHistoryPseudoPager\$mTitle
Title $mTitle
Definition: ImagePage.php:1326
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:728
ImageHistoryList\$title
Title $title
Definition: ImagePage.php:1034
Xml\closeElement
static closeElement( $element)
Shortcut to close an XML element.
Definition: Xml.php:118
ImageHistoryList\getThumbForLine
getThumbForLine( $file)
Definition: ImagePage.php:1271
$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:3693
ImageHistoryList\beginImageHistoryList
beginImageHistoryList( $navLinks='')
Definition: ImagePage.php:1082
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:930
UploadBase\userCanReUpload
static userCanReUpload(User $user, $img)
Check if a user is the last uploader.
Definition: UploadBase.php:1517
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:946
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:1317
$t
$t
Definition: testCompression.php:65
Article
Class for viewing MediaWiki article and history.
Definition: Article.php:36
ImageHistoryList\$preventClickjacking
$preventClickjacking
Definition: ImagePage.php:1049
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:124
$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:3704
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:1046
Article\viewRedirect
viewRedirect( $target, $appendSubtitle=true, $forceKnown=false)
View redirect.
Definition: Article.php:1446
ImageHistoryList\getPreventClickjacking
getPreventClickjacking()
Definition: ImagePage.php:1312
ImagePage\printSharedImageText
printSharedImageText()
Show a notice that the file is from a shared repository.
Definition: ImagePage.php:633
ImageHistoryList\endImageHistoryList
endImageHistoryList( $navLinks='')
Definition: ImagePage.php:1102
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:707
LogEventsList\showLogExtract
static showLogExtract(&$out, $types=array(), $page='', $user='', $param=array())
Show log extract.
Definition: LogEventsList.php:507
$type
$type
Definition: testCompression.php:46