MediaWiki  master
Linker.php
Go to the documentation of this file.
1 <?php
24 
34 class Linker {
38  const TOOL_LINKS_NOBLOCK = 1;
39  const TOOL_LINKS_EMAIL = 2;
40 
54  static function getInterwikiLinkAttributes( $title, $unused = null, $class = 'external' ) {
56 
57  wfDeprecated( __METHOD__, '1.25' );
58 
59  # @todo FIXME: We have a whole bunch of handling here that doesn't happen in
60  # getExternalLinkAttributes, why?
61  $title = urldecode( $title );
62  $title = $wgContLang->checkTitleEncoding( $title );
63  $title = preg_replace( '/[\\x00-\\x1f]/', ' ', $title );
64 
65  return self::getLinkAttributesInternal( $title, $class );
66  }
67 
80  static function getInternalLinkAttributes( $title, $unused = null, $class = '' ) {
81  wfDeprecated( __METHOD__, '1.25' );
82 
83  $title = urldecode( $title );
84  $title = strtr( $title, '_', ' ' );
85  return self::getLinkAttributesInternal( $title, $class );
86  }
87 
102  static function getInternalLinkAttributesObj( $nt, $unused = null, $class = '', $title = false ) {
103  wfDeprecated( __METHOD__, '1.25' );
104 
105  if ( $title === false ) {
106  $title = $nt->getPrefixedText();
107  }
108  return self::getLinkAttributesInternal( $title, $class );
109  }
110 
122  private static function getLinkAttributesInternal( $title, $class ) {
123  wfDeprecated( __METHOD__, '1.25' );
124 
125  $title = htmlspecialchars( $title );
126  $class = htmlspecialchars( $class );
127  $r = '';
128  if ( $class != '' ) {
129  $r .= " class=\"$class\"";
130  }
131  if ( $title != '' ) {
132  $r .= " title=\"$title\"";
133  }
134  return $r;
135  }
136 
147  public static function getLinkColour( LinkTarget $t, $threshold ) {
148  wfDeprecated( __METHOD__, '1.28' );
149  $services = MediaWikiServices::getInstance();
150  $linkRenderer = $services->getLinkRenderer();
151  if ( $threshold !== $linkRenderer->getStubThreshold() ) {
152  // Need to create a new instance with the right stub threshold...
153  $linkRenderer = $services->getLinkRendererFactory()->create();
154  $linkRenderer->setStubThreshold( $threshold );
155  }
156 
157  return $linkRenderer->getLinkClasses( $t );
158  }
159 
203  public static function link(
204  $target, $html = null, $customAttribs = [], $query = [], $options = []
205  ) {
206  if ( !$target instanceof Title ) {
207  wfWarn( __METHOD__ . ': Requires $target to be a Title object.', 2 );
208  return "<!-- ERROR -->$html";
209  }
210 
211  if ( is_string( $query ) ) {
212  // some functions withing core using this still hand over query strings
213  wfDeprecated( __METHOD__ . ' with parameter $query as string (should be array)', '1.20' );
215  }
216 
217  $services = MediaWikiServices::getInstance();
219  if ( $options ) {
220  // Custom options, create new LinkRenderer
221  if ( !isset( $options['stubThreshold'] ) ) {
222  $defaultLinkRenderer = $services->getLinkRenderer();
223  $options['stubThreshold'] = $defaultLinkRenderer->getStubThreshold();
224  }
225  $linkRenderer = $services->getLinkRendererFactory()
226  ->createFromLegacyOptions( $options );
227  } else {
228  $linkRenderer = $services->getLinkRenderer();
229  }
230 
231  if ( $html !== null ) {
232  $text = new HtmlArmor( $html );
233  } else {
234  $text = $html; // null
235  }
236  if ( in_array( 'known', $options, true ) ) {
237  return $linkRenderer->makeKnownLink( $target, $text, $customAttribs, $query );
238  } elseif ( in_array( 'broken', $options, true ) ) {
239  return $linkRenderer->makeBrokenLink( $target, $text, $customAttribs, $query );
240  } elseif ( in_array( 'noclasses', $options, true ) ) {
241  return $linkRenderer->makePreloadedLink( $target, $text, '', $customAttribs, $query );
242  } else {
243  return $linkRenderer->makeLink( $target, $text, $customAttribs, $query );
244  }
245  }
246 
255  public static function linkKnown(
256  $target, $html = null, $customAttribs = [],
257  $query = [], $options = [ 'known' ]
258  ) {
259  return self::link( $target, $html, $customAttribs, $query, $options );
260  }
261 
277  public static function makeSelfLinkObj( $nt, $html = '', $query = '', $trail = '', $prefix = '' ) {
278  $ret = "<strong class=\"selflink\">{$prefix}{$html}</strong>{$trail}";
279  if ( !Hooks::run( 'SelfLinkBegin', [ $nt, &$html, &$trail, &$prefix, &$ret ] ) ) {
280  return $ret;
281  }
282 
283  if ( $html == '' ) {
284  $html = htmlspecialchars( $nt->getPrefixedText() );
285  }
286  list( $inside, $trail ) = self::splitTrail( $trail );
287  return "<strong class=\"selflink\">{$prefix}{$html}{$inside}</strong>{$trail}";
288  }
289 
300  public static function getInvalidTitleDescription( IContextSource $context, $namespace, $title ) {
302 
303  // First we check whether the namespace exists or not.
304  if ( MWNamespace::exists( $namespace ) ) {
305  if ( $namespace == NS_MAIN ) {
306  $name = $context->msg( 'blanknamespace' )->text();
307  } else {
308  $name = $wgContLang->getFormattedNsText( $namespace );
309  }
310  return $context->msg( 'invalidtitle-knownnamespace', $namespace, $name, $title )->text();
311  } else {
312  return $context->msg( 'invalidtitle-unknownnamespace', $namespace, $title )->text();
313  }
314  }
315 
321  public static function normaliseSpecialPage( LinkTarget $target ) {
322  if ( $target->getNamespace() == NS_SPECIAL ) {
323  list( $name, $subpage ) = SpecialPageFactory::resolveAlias( $target->getDBkey() );
324  if ( !$name ) {
325  return $target;
326  }
327  $ret = SpecialPage::getTitleFor( $name, $subpage, $target->getFragment() );
328  return $ret;
329  } else {
330  return $target;
331  }
332  }
333 
342  private static function fnamePart( $url ) {
343  $basename = strrchr( $url, '/' );
344  if ( false === $basename ) {
345  $basename = $url;
346  } else {
347  $basename = substr( $basename, 1 );
348  }
349  return $basename;
350  }
351 
362  public static function makeExternalImage( $url, $alt = '' ) {
363  if ( $alt == '' ) {
364  $alt = self::fnamePart( $url );
365  }
366  $img = '';
367  $success = Hooks::run( 'LinkerMakeExternalImage', [ &$url, &$alt, &$img ] );
368  if ( !$success ) {
369  wfDebug( "Hook LinkerMakeExternalImage changed the output of external image "
370  . "with url {$url} and alt text {$alt} to {$img}\n", true );
371  return $img;
372  }
373  return Html::element( 'img',
374  [
375  'src' => $url,
376  'alt' => $alt ] );
377  }
378 
415  public static function makeImageLink( Parser $parser, Title $title,
416  $file, $frameParams = [], $handlerParams = [], $time = false,
417  $query = "", $widthOption = null
418  ) {
419  $res = null;
420  $dummy = new DummyLinker;
421  if ( !Hooks::run( 'ImageBeforeProduceHTML', [ &$dummy, &$title,
422  &$file, &$frameParams, &$handlerParams, &$time, &$res ] ) ) {
423  return $res;
424  }
425 
426  if ( $file && !$file->allowInlineDisplay() ) {
427  wfDebug( __METHOD__ . ': ' . $title->getPrefixedDBkey() . " does not allow inline display\n" );
428  return self::link( $title );
429  }
430 
431  // Shortcuts
432  $fp =& $frameParams;
433  $hp =& $handlerParams;
434 
435  // Clean up parameters
436  $page = isset( $hp['page'] ) ? $hp['page'] : false;
437  if ( !isset( $fp['align'] ) ) {
438  $fp['align'] = '';
439  }
440  if ( !isset( $fp['alt'] ) ) {
441  $fp['alt'] = '';
442  }
443  if ( !isset( $fp['title'] ) ) {
444  $fp['title'] = '';
445  }
446  if ( !isset( $fp['class'] ) ) {
447  $fp['class'] = '';
448  }
449 
450  $prefix = $postfix = '';
451 
452  if ( 'center' == $fp['align'] ) {
453  $prefix = '<div class="center">';
454  $postfix = '</div>';
455  $fp['align'] = 'none';
456  }
457  if ( $file && !isset( $hp['width'] ) ) {
458  if ( isset( $hp['height'] ) && $file->isVectorized() ) {
459  // If its a vector image, and user only specifies height
460  // we don't want it to be limited by its "normal" width.
462  $hp['width'] = $wgSVGMaxSize;
463  } else {
464  $hp['width'] = $file->getWidth( $page );
465  }
466 
467  if ( isset( $fp['thumbnail'] )
468  || isset( $fp['manualthumb'] )
469  || isset( $fp['framed'] )
470  || isset( $fp['frameless'] )
471  || !$hp['width']
472  ) {
474 
475  if ( $widthOption === null || !isset( $wgThumbLimits[$widthOption] ) ) {
476  $widthOption = User::getDefaultOption( 'thumbsize' );
477  }
478 
479  // Reduce width for upright images when parameter 'upright' is used
480  if ( isset( $fp['upright'] ) && $fp['upright'] == 0 ) {
481  $fp['upright'] = $wgThumbUpright;
482  }
483 
484  // For caching health: If width scaled down due to upright
485  // parameter, round to full __0 pixel to avoid the creation of a
486  // lot of odd thumbs.
487  $prefWidth = isset( $fp['upright'] ) ?
488  round( $wgThumbLimits[$widthOption] * $fp['upright'], -1 ) :
489  $wgThumbLimits[$widthOption];
490 
491  // Use width which is smaller: real image width or user preference width
492  // Unless image is scalable vector.
493  if ( !isset( $hp['height'] ) && ( $hp['width'] <= 0 ||
494  $prefWidth < $hp['width'] || $file->isVectorized() ) ) {
495  $hp['width'] = $prefWidth;
496  }
497  }
498  }
499 
500  if ( isset( $fp['thumbnail'] ) || isset( $fp['manualthumb'] ) || isset( $fp['framed'] ) ) {
501  # Create a thumbnail. Alignment depends on the writing direction of
502  # the page content language (right-aligned for LTR languages,
503  # left-aligned for RTL languages)
504  # If a thumbnail width has not been provided, it is set
505  # to the default user option as specified in Language*.php
506  if ( $fp['align'] == '' ) {
507  $fp['align'] = $parser->getTargetLanguage()->alignEnd();
508  }
509  return $prefix . self::makeThumbLink2( $title, $file, $fp, $hp, $time, $query ) . $postfix;
510  }
511 
512  if ( $file && isset( $fp['frameless'] ) ) {
513  $srcWidth = $file->getWidth( $page );
514  # For "frameless" option: do not present an image bigger than the
515  # source (for bitmap-style images). This is the same behavior as the
516  # "thumb" option does it already.
517  if ( $srcWidth && !$file->mustRender() && $hp['width'] > $srcWidth ) {
518  $hp['width'] = $srcWidth;
519  }
520  }
521 
522  if ( $file && isset( $hp['width'] ) ) {
523  # Create a resized image, without the additional thumbnail features
524  $thumb = $file->transform( $hp );
525  } else {
526  $thumb = false;
527  }
528 
529  if ( !$thumb ) {
530  $s = self::makeBrokenImageLinkObj( $title, $fp['title'], '', '', '', $time == true );
531  } else {
532  self::processResponsiveImages( $file, $thumb, $hp );
533  $params = [
534  'alt' => $fp['alt'],
535  'title' => $fp['title'],
536  'valign' => isset( $fp['valign'] ) ? $fp['valign'] : false,
537  'img-class' => $fp['class'] ];
538  if ( isset( $fp['border'] ) ) {
539  $params['img-class'] .= ( $params['img-class'] !== '' ? ' ' : '' ) . 'thumbborder';
540  }
541  $params = self::getImageLinkMTOParams( $fp, $query, $parser ) + $params;
542 
543  $s = $thumb->toHtml( $params );
544  }
545  if ( $fp['align'] != '' ) {
546  $s = "<div class=\"float{$fp['align']}\">{$s}</div>";
547  }
548  return str_replace( "\n", ' ', $prefix . $s . $postfix );
549  }
550 
559  private static function getImageLinkMTOParams( $frameParams, $query = '', $parser = null ) {
560  $mtoParams = [];
561  if ( isset( $frameParams['link-url'] ) && $frameParams['link-url'] !== '' ) {
562  $mtoParams['custom-url-link'] = $frameParams['link-url'];
563  if ( isset( $frameParams['link-target'] ) ) {
564  $mtoParams['custom-target-link'] = $frameParams['link-target'];
565  }
566  if ( $parser ) {
567  $extLinkAttrs = $parser->getExternalLinkAttribs( $frameParams['link-url'] );
568  foreach ( $extLinkAttrs as $name => $val ) {
569  // Currently could include 'rel' and 'target'
570  $mtoParams['parser-extlink-' . $name] = $val;
571  }
572  }
573  } elseif ( isset( $frameParams['link-title'] ) && $frameParams['link-title'] !== '' ) {
574  $mtoParams['custom-title-link'] = self::normaliseSpecialPage( $frameParams['link-title'] );
575  } elseif ( !empty( $frameParams['no-link'] ) ) {
576  // No link
577  } else {
578  $mtoParams['desc-link'] = true;
579  $mtoParams['desc-query'] = $query;
580  }
581  return $mtoParams;
582  }
583 
596  public static function makeThumbLinkObj( Title $title, $file, $label = '', $alt,
597  $align = 'right', $params = [], $framed = false, $manualthumb = ""
598  ) {
599  $frameParams = [
600  'alt' => $alt,
601  'caption' => $label,
602  'align' => $align
603  ];
604  if ( $framed ) {
605  $frameParams['framed'] = true;
606  }
607  if ( $manualthumb ) {
608  $frameParams['manualthumb'] = $manualthumb;
609  }
610  return self::makeThumbLink2( $title, $file, $frameParams, $params );
611  }
612 
622  public static function makeThumbLink2( Title $title, $file, $frameParams = [],
623  $handlerParams = [], $time = false, $query = ""
624  ) {
625  $exists = $file && $file->exists();
626 
627  # Shortcuts
628  $fp =& $frameParams;
629  $hp =& $handlerParams;
630 
631  $page = isset( $hp['page'] ) ? $hp['page'] : false;
632  if ( !isset( $fp['align'] ) ) {
633  $fp['align'] = 'right';
634  }
635  if ( !isset( $fp['alt'] ) ) {
636  $fp['alt'] = '';
637  }
638  if ( !isset( $fp['title'] ) ) {
639  $fp['title'] = '';
640  }
641  if ( !isset( $fp['caption'] ) ) {
642  $fp['caption'] = '';
643  }
644 
645  if ( empty( $hp['width'] ) ) {
646  // Reduce width for upright images when parameter 'upright' is used
647  $hp['width'] = isset( $fp['upright'] ) ? 130 : 180;
648  }
649  $thumb = false;
650  $noscale = false;
651  $manualthumb = false;
652 
653  if ( !$exists ) {
654  $outerWidth = $hp['width'] + 2;
655  } else {
656  if ( isset( $fp['manualthumb'] ) ) {
657  # Use manually specified thumbnail
658  $manual_title = Title::makeTitleSafe( NS_FILE, $fp['manualthumb'] );
659  if ( $manual_title ) {
660  $manual_img = wfFindFile( $manual_title );
661  if ( $manual_img ) {
662  $thumb = $manual_img->getUnscaledThumb( $hp );
663  $manualthumb = true;
664  } else {
665  $exists = false;
666  }
667  }
668  } elseif ( isset( $fp['framed'] ) ) {
669  // Use image dimensions, don't scale
670  $thumb = $file->getUnscaledThumb( $hp );
671  $noscale = true;
672  } else {
673  # Do not present an image bigger than the source, for bitmap-style images
674  # This is a hack to maintain compatibility with arbitrary pre-1.10 behavior
675  $srcWidth = $file->getWidth( $page );
676  if ( $srcWidth && !$file->mustRender() && $hp['width'] > $srcWidth ) {
677  $hp['width'] = $srcWidth;
678  }
679  $thumb = $file->transform( $hp );
680  }
681 
682  if ( $thumb ) {
683  $outerWidth = $thumb->getWidth() + 2;
684  } else {
685  $outerWidth = $hp['width'] + 2;
686  }
687  }
688 
689  # ThumbnailImage::toHtml() already adds page= onto the end of DjVu URLs
690  # So we don't need to pass it here in $query. However, the URL for the
691  # zoom icon still needs it, so we make a unique query for it. See bug 14771
692  $url = $title->getLocalURL( $query );
693  if ( $page ) {
694  $url = wfAppendQuery( $url, [ 'page' => $page ] );
695  }
696  if ( $manualthumb
697  && !isset( $fp['link-title'] )
698  && !isset( $fp['link-url'] )
699  && !isset( $fp['no-link'] ) ) {
700  $fp['link-url'] = $url;
701  }
702 
703  $s = "<div class=\"thumb t{$fp['align']}\">"
704  . "<div class=\"thumbinner\" style=\"width:{$outerWidth}px;\">";
705 
706  if ( !$exists ) {
707  $s .= self::makeBrokenImageLinkObj( $title, $fp['title'], '', '', '', $time == true );
708  $zoomIcon = '';
709  } elseif ( !$thumb ) {
710  $s .= wfMessage( 'thumbnail_error', '' )->escaped();
711  $zoomIcon = '';
712  } else {
713  if ( !$noscale && !$manualthumb ) {
714  self::processResponsiveImages( $file, $thumb, $hp );
715  }
716  $params = [
717  'alt' => $fp['alt'],
718  'title' => $fp['title'],
719  'img-class' => ( isset( $fp['class'] ) && $fp['class'] !== ''
720  ? $fp['class'] . ' '
721  : '' ) . 'thumbimage'
722  ];
723  $params = self::getImageLinkMTOParams( $fp, $query ) + $params;
724  $s .= $thumb->toHtml( $params );
725  if ( isset( $fp['framed'] ) ) {
726  $zoomIcon = "";
727  } else {
728  $zoomIcon = Html::rawElement( 'div', [ 'class' => 'magnify' ],
729  Html::rawElement( 'a', [
730  'href' => $url,
731  'class' => 'internal',
732  'title' => wfMessage( 'thumbnail-more' )->text() ],
733  "" ) );
734  }
735  }
736  $s .= ' <div class="thumbcaption">' . $zoomIcon . $fp['caption'] . "</div></div></div>";
737  return str_replace( "\n", ' ', $s );
738  }
739 
748  public static function processResponsiveImages( $file, $thumb, $hp ) {
750  if ( $wgResponsiveImages && $thumb && !$thumb->isError() ) {
751  $hp15 = $hp;
752  $hp15['width'] = round( $hp['width'] * 1.5 );
753  $hp20 = $hp;
754  $hp20['width'] = $hp['width'] * 2;
755  if ( isset( $hp['height'] ) ) {
756  $hp15['height'] = round( $hp['height'] * 1.5 );
757  $hp20['height'] = $hp['height'] * 2;
758  }
759 
760  $thumb15 = $file->transform( $hp15 );
761  $thumb20 = $file->transform( $hp20 );
762  if ( $thumb15 && !$thumb15->isError() && $thumb15->getUrl() !== $thumb->getUrl() ) {
763  $thumb->responsiveUrls['1.5'] = $thumb15->getUrl();
764  }
765  if ( $thumb20 && !$thumb20->isError() && $thumb20->getUrl() !== $thumb->getUrl() ) {
766  $thumb->responsiveUrls['2'] = $thumb20->getUrl();
767  }
768  }
769  }
770 
783  public static function makeBrokenImageLinkObj( $title, $label = '',
784  $query = '', $unused1 = '', $unused2 = '', $time = false
785  ) {
786  if ( !$title instanceof Title ) {
787  wfWarn( __METHOD__ . ': Requires $title to be a Title object.' );
788  return "<!-- ERROR -->" . htmlspecialchars( $label );
789  }
790 
792  if ( $label == '' ) {
793  $label = $title->getPrefixedText();
794  }
795  $encLabel = htmlspecialchars( $label );
796  $currentExists = $time ? ( wfFindFile( $title ) != false ) : false;
797 
798  if ( ( $wgUploadMissingFileUrl || $wgUploadNavigationUrl || $wgEnableUploads )
799  && !$currentExists
800  ) {
801  $redir = RepoGroup::singleton()->getLocalRepo()->checkRedirect( $title );
802 
803  if ( $redir ) {
804  // We already know it's a redirect, so mark it
805  // accordingly
806  return self::link(
807  $title,
808  $encLabel,
809  [ 'class' => 'mw-redirect' ],
810  wfCgiToArray( $query ),
811  [ 'known', 'noclasses' ]
812  );
813  }
814 
815  $href = self::getUploadUrl( $title, $query );
816 
817  return '<a href="' . htmlspecialchars( $href ) . '" class="new" title="' .
818  htmlspecialchars( $title->getPrefixedText(), ENT_QUOTES ) . '">' .
819  $encLabel . '</a>';
820  }
821 
822  return self::link( $title, $encLabel, [], wfCgiToArray( $query ), [ 'known', 'noclasses' ] );
823  }
824 
833  protected static function getUploadUrl( $destFile, $query = '' ) {
835  $q = 'wpDestFile=' . $destFile->getPartialURL();
836  if ( $query != '' ) {
837  $q .= '&' . $query;
838  }
839 
840  if ( $wgUploadMissingFileUrl ) {
841  return wfAppendQuery( $wgUploadMissingFileUrl, $q );
842  } elseif ( $wgUploadNavigationUrl ) {
843  return wfAppendQuery( $wgUploadNavigationUrl, $q );
844  } else {
845  $upload = SpecialPage::getTitleFor( 'Upload' );
846  return $upload->getLocalURL( $q );
847  }
848  }
849 
859  public static function makeMediaLinkObj( $title, $html = '', $time = false ) {
860  $img = wfFindFile( $title, [ 'time' => $time ] );
861  return self::makeMediaLinkFile( $title, $img, $html );
862  }
863 
876  public static function makeMediaLinkFile( Title $title, $file, $html = '' ) {
877  if ( $file && $file->exists() ) {
878  $url = $file->getUrl();
879  $class = 'internal';
880  } else {
881  $url = self::getUploadUrl( $title );
882  $class = 'new';
883  }
884 
885  $alt = $title->getText();
886  if ( $html == '' ) {
887  $html = $alt;
888  }
889 
890  $ret = '';
891  $attribs = [
892  'href' => $url,
893  'class' => $class,
894  'title' => $alt
895  ];
896 
897  if ( !Hooks::run( 'LinkerMakeMediaLinkFile',
898  [ $title, $file, &$html, &$attribs, &$ret ] ) ) {
899  wfDebug( "Hook LinkerMakeMediaLinkFile changed the output of link "
900  . "with url {$url} and text {$html} to {$ret}\n", true );
901  return $ret;
902  }
903 
904  return Html::rawElement( 'a', $attribs, $html );
905  }
906 
917  public static function specialLink( $name, $key = '' ) {
918  if ( $key == '' ) {
919  $key = strtolower( $name );
920  }
921 
922  return self::linkKnown( SpecialPage::getTitleFor( $name ), wfMessage( $key )->text() );
923  }
924 
936  public static function makeExternalLink( $url, $text, $escape = true,
937  $linktype = '', $attribs = [], $title = null
938  ) {
940  $class = "external";
941  if ( $linktype ) {
942  $class .= " $linktype";
943  }
944  if ( isset( $attribs['class'] ) && $attribs['class'] ) {
945  $class .= " {$attribs['class']}";
946  }
947  $attribs['class'] = $class;
948 
949  if ( $escape ) {
950  $text = htmlspecialchars( $text );
951  }
952 
953  if ( !$title ) {
954  $title = $wgTitle;
955  }
956  $newRel = Parser::getExternalLinkRel( $url, $title );
957  if ( !isset( $attribs['rel'] ) || $attribs['rel'] === '' ) {
958  $attribs['rel'] = $newRel;
959  } elseif ( $newRel !== '' ) {
960  // Merge the rel attributes.
961  $newRels = explode( ' ', $newRel );
962  $oldRels = explode( ' ', $attribs['rel'] );
963  $combined = array_unique( array_merge( $newRels, $oldRels ) );
964  $attribs['rel'] = implode( ' ', $combined );
965  }
966  $link = '';
967  $success = Hooks::run( 'LinkerMakeExternalLink',
968  [ &$url, &$text, &$link, &$attribs, $linktype ] );
969  if ( !$success ) {
970  wfDebug( "Hook LinkerMakeExternalLink changed the output of link "
971  . "with url {$url} and text {$text} to {$link}\n", true );
972  return $link;
973  }
974  $attribs['href'] = $url;
975  return Html::rawElement( 'a', $attribs, $text );
976  }
977 
986  public static function userLink( $userId, $userName, $altUserName = false ) {
987  $classes = 'mw-userlink';
988  if ( $userId == 0 ) {
989  $page = SpecialPage::getTitleFor( 'Contributions', $userName );
990  if ( $altUserName === false ) {
991  $altUserName = IP::prettifyIP( $userName );
992  }
993  $classes .= ' mw-anonuserlink'; // Separate link class for anons (bug 43179)
994  } else {
995  $page = Title::makeTitle( NS_USER, $userName );
996  }
997 
998  return self::link(
999  $page,
1000  htmlspecialchars( $altUserName !== false ? $altUserName : $userName ),
1001  [ 'class' => $classes ]
1002  );
1003  }
1004 
1018  public static function userToolLinks(
1019  $userId, $userText, $redContribsWhenNoEdits = false, $flags = 0, $edits = null
1020  ) {
1022  $talkable = !( $wgDisableAnonTalk && 0 == $userId );
1023  $blockable = !( $flags & self::TOOL_LINKS_NOBLOCK );
1024  $addEmailLink = $flags & self::TOOL_LINKS_EMAIL && $userId;
1025 
1026  $items = [];
1027  if ( $talkable ) {
1028  $items[] = self::userTalkLink( $userId, $userText );
1029  }
1030  if ( $userId ) {
1031  // check if the user has an edit
1032  $attribs = [];
1033  if ( $redContribsWhenNoEdits ) {
1034  if ( intval( $edits ) === 0 && $edits !== 0 ) {
1035  $user = User::newFromId( $userId );
1036  $edits = $user->getEditCount();
1037  }
1038  if ( $edits === 0 ) {
1039  $attribs['class'] = 'new';
1040  }
1041  }
1042  $contribsPage = SpecialPage::getTitleFor( 'Contributions', $userText );
1043 
1044  $items[] = self::link( $contribsPage, wfMessage( 'contribslink' )->escaped(), $attribs );
1045  }
1046  if ( $blockable && $wgUser->isAllowed( 'block' ) ) {
1047  $items[] = self::blockLink( $userId, $userText );
1048  }
1049 
1050  if ( $addEmailLink && $wgUser->canSendEmail() ) {
1051  $items[] = self::emailLink( $userId, $userText );
1052  }
1053 
1054  Hooks::run( 'UserToolLinksEdit', [ $userId, $userText, &$items ] );
1055 
1056  if ( $items ) {
1057  return wfMessage( 'word-separator' )->escaped()
1058  . '<span class="mw-usertoollinks">'
1059  . wfMessage( 'parentheses' )->rawParams( $wgLang->pipeList( $items ) )->escaped()
1060  . '</span>';
1061  } else {
1062  return '';
1063  }
1064  }
1065 
1074  public static function userToolLinksRedContribs( $userId, $userText, $edits = null ) {
1075  return self::userToolLinks( $userId, $userText, true, 0, $edits );
1076  }
1077 
1084  public static function userTalkLink( $userId, $userText ) {
1085  $userTalkPage = Title::makeTitle( NS_USER_TALK, $userText );
1086  $userTalkLink = self::link( $userTalkPage, wfMessage( 'talkpagelinktext' )->escaped() );
1087  return $userTalkLink;
1088  }
1089 
1096  public static function blockLink( $userId, $userText ) {
1097  $blockPage = SpecialPage::getTitleFor( 'Block', $userText );
1098  $blockLink = self::link( $blockPage, wfMessage( 'blocklink' )->escaped() );
1099  return $blockLink;
1100  }
1101 
1107  public static function emailLink( $userId, $userText ) {
1108  $emailPage = SpecialPage::getTitleFor( 'Emailuser', $userText );
1109  $emailLink = self::link( $emailPage, wfMessage( 'emaillink' )->escaped() );
1110  return $emailLink;
1111  }
1112 
1120  public static function revUserLink( $rev, $isPublic = false ) {
1121  if ( $rev->isDeleted( Revision::DELETED_USER ) && $isPublic ) {
1122  $link = wfMessage( 'rev-deleted-user' )->escaped();
1123  } elseif ( $rev->userCan( Revision::DELETED_USER ) ) {
1124  $link = self::userLink( $rev->getUser( Revision::FOR_THIS_USER ),
1125  $rev->getUserText( Revision::FOR_THIS_USER ) );
1126  } else {
1127  $link = wfMessage( 'rev-deleted-user' )->escaped();
1128  }
1129  if ( $rev->isDeleted( Revision::DELETED_USER ) ) {
1130  return '<span class="history-deleted">' . $link . '</span>';
1131  }
1132  return $link;
1133  }
1134 
1142  public static function revUserTools( $rev, $isPublic = false ) {
1143  if ( $rev->isDeleted( Revision::DELETED_USER ) && $isPublic ) {
1144  $link = wfMessage( 'rev-deleted-user' )->escaped();
1145  } elseif ( $rev->userCan( Revision::DELETED_USER ) ) {
1146  $userId = $rev->getUser( Revision::FOR_THIS_USER );
1147  $userText = $rev->getUserText( Revision::FOR_THIS_USER );
1148  $link = self::userLink( $userId, $userText )
1149  . self::userToolLinks( $userId, $userText );
1150  } else {
1151  $link = wfMessage( 'rev-deleted-user' )->escaped();
1152  }
1153  if ( $rev->isDeleted( Revision::DELETED_USER ) ) {
1154  return ' <span class="history-deleted">' . $link . '</span>';
1155  }
1156  return $link;
1157  }
1158 
1181  public static function formatComment(
1182  $comment, $title = null, $local = false, $wikiId = null
1183  ) {
1184  # Sanitize text a bit:
1185  $comment = str_replace( "\n", " ", $comment );
1186  # Allow HTML entities (for bug 13815)
1188 
1189  # Render autocomments and make links:
1190  $comment = self::formatAutocomments( $comment, $title, $local, $wikiId );
1191  $comment = self::formatLinksInComment( $comment, $title, $local, $wikiId );
1192 
1193  return $comment;
1194  }
1195 
1213  private static function formatAutocomments(
1214  $comment, $title = null, $local = false, $wikiId = null
1215  ) {
1216  // @todo $append here is something of a hack to preserve the status
1217  // quo. Someone who knows more about bidi and such should decide
1218  // (1) what sane rendering even *is* for an LTR edit summary on an RTL
1219  // wiki, both when autocomments exist and when they don't, and
1220  // (2) what markup will make that actually happen.
1221  $append = '';
1222  $comment = preg_replace_callback(
1223  // To detect the presence of content before or after the
1224  // auto-comment, we use capturing groups inside optional zero-width
1225  // assertions. But older versions of PCRE can't directly make
1226  // zero-width assertions optional, so wrap them in a non-capturing
1227  // group.
1228  '!(?:(?<=(.)))?/\*\s*(.*?)\s*\*/(?:(?=(.)))?!',
1229  function ( $match ) use ( $title, $local, $wikiId, &$append ) {
1230  global $wgLang;
1231 
1232  // Ensure all match positions are defined
1233  $match += [ '', '', '', '' ];
1234 
1235  $pre = $match[1] !== '';
1236  $auto = $match[2];
1237  $post = $match[3] !== '';
1238  $comment = null;
1239 
1240  Hooks::run(
1241  'FormatAutocomments',
1242  [ &$comment, $pre, $auto, $post, $title, $local, $wikiId ]
1243  );
1244 
1245  if ( $comment === null ) {
1246  $link = '';
1247  if ( $title ) {
1248  $section = $auto;
1249  # Remove links that a user may have manually put in the autosummary
1250  # This could be improved by copying as much of Parser::stripSectionName as desired.
1251  $section = str_replace( '[[:', '', $section );
1252  $section = str_replace( '[[', '', $section );
1253  $section = str_replace( ']]', '', $section );
1254 
1256  if ( $local ) {
1257  $sectionTitle = Title::newFromText( '#' . $section );
1258  } else {
1259  $sectionTitle = Title::makeTitleSafe( $title->getNamespace(),
1260  $title->getDBkey(), $section );
1261  }
1262  if ( $sectionTitle ) {
1263  $link = Linker::makeCommentLink( $sectionTitle, $wgLang->getArrow(), $wikiId, 'noclasses' );
1264  } else {
1265  $link = '';
1266  }
1267  }
1268  if ( $pre ) {
1269  # written summary $presep autocomment (summary /* section */)
1270  $pre = wfMessage( 'autocomment-prefix' )->inContentLanguage()->escaped();
1271  }
1272  if ( $post ) {
1273  # autocomment $postsep written summary (/* section */ summary)
1274  $auto .= wfMessage( 'colon-separator' )->inContentLanguage()->escaped();
1275  }
1276  $auto = '<span class="autocomment">' . $auto . '</span>';
1277  $comment = $pre . $link . $wgLang->getDirMark()
1278  . '<span dir="auto">' . $auto;
1279  $append .= '</span>';
1280  }
1281  return $comment;
1282  },
1283  $comment
1284  );
1285  return $comment . $append;
1286  }
1287 
1306  public static function formatLinksInComment(
1307  $comment, $title = null, $local = false, $wikiId = null
1308  ) {
1309  return preg_replace_callback(
1310  '/
1311  \[\[
1312  :? # ignore optional leading colon
1313  ([^\]|]+) # 1. link target; page names cannot include ] or |
1314  (?:\|
1315  # 2. a pipe-separated substring; only the last is captured
1316  # Stop matching at | and ]] without relying on backtracking.
1317  ((?:]?[^\]|])*+)
1318  )*
1319  \]\]
1320  ([^[]*) # 3. link trail (the text up until the next link)
1321  /x',
1322  function ( $match ) use ( $title, $local, $wikiId ) {
1324 
1325  $medians = '(?:' . preg_quote( MWNamespace::getCanonicalName( NS_MEDIA ), '/' ) . '|';
1326  $medians .= preg_quote( $wgContLang->getNsText( NS_MEDIA ), '/' ) . '):';
1327 
1328  $comment = $match[0];
1329 
1330  # fix up urlencoded title texts (copied from Parser::replaceInternalLinks)
1331  if ( strpos( $match[1], '%' ) !== false ) {
1332  $match[1] = strtr(
1333  rawurldecode( $match[1] ),
1334  [ '<' => '&lt;', '>' => '&gt;' ]
1335  );
1336  }
1337 
1338  # Handle link renaming [[foo|text]] will show link as "text"
1339  if ( $match[2] != "" ) {
1340  $text = $match[2];
1341  } else {
1342  $text = $match[1];
1343  }
1344  $submatch = [];
1345  $thelink = null;
1346  if ( preg_match( '/^' . $medians . '(.*)$/i', $match[1], $submatch ) ) {
1347  # Media link; trail not supported.
1348  $linkRegexp = '/\[\[(.*?)\]\]/';
1349  $title = Title::makeTitleSafe( NS_FILE, $submatch[1] );
1350  if ( $title ) {
1351  $thelink = Linker::makeMediaLinkObj( $title, $text );
1352  }
1353  } else {
1354  # Other kind of link
1355  # Make sure its target is non-empty
1356  if ( isset( $match[1][0] ) && $match[1][0] == ':' ) {
1357  $match[1] = substr( $match[1], 1 );
1358  }
1359  if ( $match[1] !== false && $match[1] !== '' ) {
1360  if ( preg_match( $wgContLang->linkTrail(), $match[3], $submatch ) ) {
1361  $trail = $submatch[1];
1362  } else {
1363  $trail = "";
1364  }
1365  $linkRegexp = '/\[\[(.*?)\]\]' . preg_quote( $trail, '/' ) . '/';
1366  list( $inside, $trail ) = Linker::splitTrail( $trail );
1367 
1368  $linkText = $text;
1369  $linkTarget = Linker::normalizeSubpageLink( $title, $match[1], $linkText );
1370 
1371  $target = Title::newFromText( $linkTarget );
1372  if ( $target ) {
1373  if ( $target->getText() == '' && !$target->isExternal()
1374  && !$local && $title
1375  ) {
1376  $newTarget = clone $title;
1377  $newTarget->setFragment( '#' . $target->getFragment() );
1378  $target = $newTarget;
1379  }
1380 
1381  $thelink = Linker::makeCommentLink( $target, $linkText . $inside, $wikiId ) . $trail;
1382  }
1383  }
1384  }
1385  if ( $thelink ) {
1386  // If the link is still valid, go ahead and replace it in!
1387  $comment = preg_replace(
1388  $linkRegexp,
1389  StringUtils::escapeRegexReplacement( $thelink ),
1390  $comment,
1391  1
1392  );
1393  }
1394 
1395  return $comment;
1396  },
1397  $comment
1398  );
1399  }
1400 
1414  public static function makeCommentLink(
1415  Title $title, $text, $wikiId = null, $options = []
1416  ) {
1417  if ( $wikiId !== null && !$title->isExternal() ) {
1420  $wikiId,
1421  $title->getPrefixedText(),
1422  $title->getFragment()
1423  ),
1424  $text,
1425  /* escape = */ false // Already escaped
1426  );
1427  } else {
1428  $link = Linker::link( $title, $text, [], [], $options );
1429  }
1430 
1431  return $link;
1432  }
1433 
1440  public static function normalizeSubpageLink( $contextTitle, $target, &$text ) {
1441  # Valid link forms:
1442  # Foobar -- normal
1443  # :Foobar -- override special treatment of prefix (images, language links)
1444  # /Foobar -- convert to CurrentPage/Foobar
1445  # /Foobar/ -- convert to CurrentPage/Foobar, strip the initial and final / from text
1446  # ../ -- convert to CurrentPage, from CurrentPage/CurrentSubPage
1447  # ../Foobar -- convert to CurrentPage/Foobar,
1448  # (from CurrentPage/CurrentSubPage)
1449  # ../Foobar/ -- convert to CurrentPage/Foobar, use 'Foobar' as text
1450  # (from CurrentPage/CurrentSubPage)
1451 
1452  $ret = $target; # default return value is no change
1453 
1454  # Some namespaces don't allow subpages,
1455  # so only perform processing if subpages are allowed
1456  if ( $contextTitle && MWNamespace::hasSubpages( $contextTitle->getNamespace() ) ) {
1457  $hash = strpos( $target, '#' );
1458  if ( $hash !== false ) {
1459  $suffix = substr( $target, $hash );
1460  $target = substr( $target, 0, $hash );
1461  } else {
1462  $suffix = '';
1463  }
1464  # bug 7425
1465  $target = trim( $target );
1466  # Look at the first character
1467  if ( $target != '' && $target[0] === '/' ) {
1468  # / at end means we don't want the slash to be shown
1469  $m = [];
1470  $trailingSlashes = preg_match_all( '%(/+)$%', $target, $m );
1471  if ( $trailingSlashes ) {
1472  $noslash = $target = substr( $target, 1, -strlen( $m[0][0] ) );
1473  } else {
1474  $noslash = substr( $target, 1 );
1475  }
1476 
1477  $ret = $contextTitle->getPrefixedText() . '/' . trim( $noslash ) . $suffix;
1478  if ( $text === '' ) {
1479  $text = $target . $suffix;
1480  } # this might be changed for ugliness reasons
1481  } else {
1482  # check for .. subpage backlinks
1483  $dotdotcount = 0;
1484  $nodotdot = $target;
1485  while ( strncmp( $nodotdot, "../", 3 ) == 0 ) {
1486  ++$dotdotcount;
1487  $nodotdot = substr( $nodotdot, 3 );
1488  }
1489  if ( $dotdotcount > 0 ) {
1490  $exploded = explode( '/', $contextTitle->getPrefixedText() );
1491  if ( count( $exploded ) > $dotdotcount ) { # not allowed to go below top level page
1492  $ret = implode( '/', array_slice( $exploded, 0, -$dotdotcount ) );
1493  # / at the end means don't show full path
1494  if ( substr( $nodotdot, -1, 1 ) === '/' ) {
1495  $nodotdot = rtrim( $nodotdot, '/' );
1496  if ( $text === '' ) {
1497  $text = $nodotdot . $suffix;
1498  }
1499  }
1500  $nodotdot = trim( $nodotdot );
1501  if ( $nodotdot != '' ) {
1502  $ret .= '/' . $nodotdot;
1503  }
1504  $ret .= $suffix;
1505  }
1506  }
1507  }
1508  }
1509 
1510  return $ret;
1511  }
1512 
1526  public static function commentBlock(
1527  $comment, $title = null, $local = false, $wikiId = null
1528  ) {
1529  // '*' used to be the comment inserted by the software way back
1530  // in antiquity in case none was provided, here for backwards
1531  // compatibility, acc. to brion -ævar
1532  if ( $comment == '' || $comment == '*' ) {
1533  return '';
1534  } else {
1535  $formatted = self::formatComment( $comment, $title, $local, $wikiId );
1536  $formatted = wfMessage( 'parentheses' )->rawParams( $formatted )->escaped();
1537  return " <span class=\"comment\">$formatted</span>";
1538  }
1539  }
1540 
1551  public static function revComment( Revision $rev, $local = false, $isPublic = false ) {
1552  if ( $rev->getComment( Revision::RAW ) == "" ) {
1553  return "";
1554  }
1555  if ( $rev->isDeleted( Revision::DELETED_COMMENT ) && $isPublic ) {
1556  $block = " <span class=\"comment\">" . wfMessage( 'rev-deleted-comment' )->escaped() . "</span>";
1557  } elseif ( $rev->userCan( Revision::DELETED_COMMENT ) ) {
1558  $block = self::commentBlock( $rev->getComment( Revision::FOR_THIS_USER ),
1559  $rev->getTitle(), $local );
1560  } else {
1561  $block = " <span class=\"comment\">" . wfMessage( 'rev-deleted-comment' )->escaped() . "</span>";
1562  }
1563  if ( $rev->isDeleted( Revision::DELETED_COMMENT ) ) {
1564  return " <span class=\"history-deleted\">$block</span>";
1565  }
1566  return $block;
1567  }
1568 
1574  public static function formatRevisionSize( $size ) {
1575  if ( $size == 0 ) {
1576  $stxt = wfMessage( 'historyempty' )->escaped();
1577  } else {
1578  $stxt = wfMessage( 'nbytes' )->numParams( $size )->escaped();
1579  $stxt = wfMessage( 'parentheses' )->rawParams( $stxt )->escaped();
1580  }
1581  return "<span class=\"history-size\">$stxt</span>";
1582  }
1583 
1590  public static function tocIndent() {
1591  return "\n<ul>";
1592  }
1593 
1601  public static function tocUnindent( $level ) {
1602  return "</li>\n" . str_repeat( "</ul>\n</li>\n", $level > 0 ? $level : 0 );
1603  }
1604 
1616  public static function tocLine( $anchor, $tocline, $tocnumber, $level, $sectionIndex = false ) {
1617  $classes = "toclevel-$level";
1618  if ( $sectionIndex !== false ) {
1619  $classes .= " tocsection-$sectionIndex";
1620  }
1621  return "\n<li class=\"$classes\"><a href=\"#" .
1622  $anchor . '"><span class="tocnumber">' .
1623  $tocnumber . '</span> <span class="toctext">' .
1624  $tocline . '</span></a>';
1625  }
1626 
1634  public static function tocLineEnd() {
1635  return "</li>\n";
1636  }
1637 
1646  public static function tocList( $toc, $lang = false ) {
1647  $lang = wfGetLangObj( $lang );
1648  $title = wfMessage( 'toc' )->inLanguage( $lang )->escaped();
1649 
1650  return '<div id="toc" class="toc">'
1651  . '<div id="toctitle"><h2>' . $title . "</h2></div>\n"
1652  . $toc
1653  . "</ul>\n</div>\n";
1654  }
1655 
1664  public static function generateTOC( $tree, $lang = false ) {
1665  $toc = '';
1666  $lastLevel = 0;
1667  foreach ( $tree as $section ) {
1668  if ( $section['toclevel'] > $lastLevel ) {
1669  $toc .= self::tocIndent();
1670  } elseif ( $section['toclevel'] < $lastLevel ) {
1671  $toc .= self::tocUnindent(
1672  $lastLevel - $section['toclevel'] );
1673  } else {
1674  $toc .= self::tocLineEnd();
1675  }
1676 
1677  $toc .= self::tocLine( $section['anchor'],
1678  $section['line'], $section['number'],
1679  $section['toclevel'], $section['index'] );
1680  $lastLevel = $section['toclevel'];
1681  }
1682  $toc .= self::tocLineEnd();
1683  return self::tocList( $toc, $lang );
1684  }
1685 
1702  public static function makeHeadline( $level, $attribs, $anchor, $html,
1703  $link, $legacyAnchor = false
1704  ) {
1705  $ret = "<h$level$attribs"
1706  . "<span class=\"mw-headline\" id=\"$anchor\">$html</span>"
1707  . $link
1708  . "</h$level>";
1709  if ( $legacyAnchor !== false ) {
1710  $ret = "<div id=\"$legacyAnchor\"></div>$ret";
1711  }
1712  return $ret;
1713  }
1714 
1721  static function splitTrail( $trail ) {
1723  $regex = $wgContLang->linkTrail();
1724  $inside = '';
1725  if ( $trail !== '' ) {
1726  $m = [];
1727  if ( preg_match( $regex, $trail, $m ) ) {
1728  $inside = $m[1];
1729  $trail = $m[2];
1730  }
1731  }
1732  return [ $inside, $trail ];
1733  }
1734 
1762  public static function generateRollback( $rev, IContextSource $context = null,
1763  $options = [ 'verify' ]
1764  ) {
1765  if ( $context === null ) {
1767  }
1768 
1769  $editCount = false;
1770  if ( in_array( 'verify', $options, true ) ) {
1771  $editCount = self::getRollbackEditCount( $rev, true );
1772  if ( $editCount === false ) {
1773  return '';
1774  }
1775  }
1776 
1777  $inner = self::buildRollbackLink( $rev, $context, $editCount );
1778 
1779  if ( !in_array( 'noBrackets', $options, true ) ) {
1780  $inner = $context->msg( 'brackets' )->rawParams( $inner )->escaped();
1781  }
1782 
1783  return '<span class="mw-rollback-link">' . $inner . '</span>';
1784  }
1785 
1801  public static function getRollbackEditCount( $rev, $verify ) {
1803  if ( !is_int( $wgShowRollbackEditCount ) || !$wgShowRollbackEditCount > 0 ) {
1804  // Nothing has happened, indicate this by returning 'null'
1805  return null;
1806  }
1807 
1808  $dbr = wfGetDB( DB_SLAVE );
1809 
1810  // Up to the value of $wgShowRollbackEditCount revisions are counted
1811  $res = $dbr->select(
1812  'revision',
1813  [ 'rev_user_text', 'rev_deleted' ],
1814  // $rev->getPage() returns null sometimes
1815  [ 'rev_page' => $rev->getTitle()->getArticleID() ],
1816  __METHOD__,
1817  [
1818  'USE INDEX' => [ 'revision' => 'page_timestamp' ],
1819  'ORDER BY' => 'rev_timestamp DESC',
1820  'LIMIT' => $wgShowRollbackEditCount + 1
1821  ]
1822  );
1823 
1824  $editCount = 0;
1825  $moreRevs = false;
1826  foreach ( $res as $row ) {
1827  if ( $rev->getUserText( Revision::RAW ) != $row->rev_user_text ) {
1828  if ( $verify &&
1829  ( $row->rev_deleted & Revision::DELETED_TEXT
1830  || $row->rev_deleted & Revision::DELETED_USER
1831  ) ) {
1832  // If the user or the text of the revision we might rollback
1833  // to is deleted in some way we can't rollback. Similar to
1834  // the sanity checks in WikiPage::commitRollback.
1835  return false;
1836  }
1837  $moreRevs = true;
1838  break;
1839  }
1840  $editCount++;
1841  }
1842 
1843  if ( $verify && $editCount <= $wgShowRollbackEditCount && !$moreRevs ) {
1844  // We didn't find at least $wgShowRollbackEditCount revisions made by the current user
1845  // and there weren't any other revisions. That means that the current user is the only
1846  // editor, so we can't rollback
1847  return false;
1848  }
1849  return $editCount;
1850  }
1851 
1861  public static function buildRollbackLink( $rev, IContextSource $context = null,
1862  $editCount = false
1863  ) {
1865 
1866  // To config which pages are affected by miser mode
1867  $disableRollbackEditCountSpecialPage = [ 'Recentchanges', 'Watchlist' ];
1868 
1869  if ( $context === null ) {
1871  }
1872 
1873  $title = $rev->getTitle();
1874  $query = [
1875  'action' => 'rollback',
1876  'from' => $rev->getUserText(),
1877  'token' => $context->getUser()->getEditToken( 'rollback' ),
1878  ];
1879  $attrs = [
1880  'data-mw' => 'interface',
1881  'title' => $context->msg( 'tooltip-rollback' )->text(),
1882  ];
1883  $options = [ 'known', 'noclasses' ];
1884 
1885  if ( $context->getRequest()->getBool( 'bot' ) ) {
1886  $query['bot'] = '1';
1887  $query['hidediff'] = '1'; // bug 15999
1888  }
1889 
1890  $disableRollbackEditCount = false;
1891  if ( $wgMiserMode ) {
1892  foreach ( $disableRollbackEditCountSpecialPage as $specialPage ) {
1893  if ( $context->getTitle()->isSpecial( $specialPage ) ) {
1894  $disableRollbackEditCount = true;
1895  break;
1896  }
1897  }
1898  }
1899 
1900  if ( !$disableRollbackEditCount
1901  && is_int( $wgShowRollbackEditCount )
1902  && $wgShowRollbackEditCount > 0
1903  ) {
1904  if ( !is_numeric( $editCount ) ) {
1905  $editCount = self::getRollbackEditCount( $rev, false );
1906  }
1907 
1908  if ( $editCount > $wgShowRollbackEditCount ) {
1909  $html = $context->msg( 'rollbacklinkcount-morethan' )
1910  ->numParams( $wgShowRollbackEditCount )->parse();
1911  } else {
1912  $html = $context->msg( 'rollbacklinkcount' )->numParams( $editCount )->parse();
1913  }
1914 
1915  return self::link( $title, $html, $attrs, $query, $options );
1916  } else {
1917  $html = $context->msg( 'rollbacklink' )->escaped();
1918  return self::link( $title, $html, $attrs, $query, $options );
1919  }
1920  }
1921 
1938  public static function formatTemplates( $templates, $preview = false,
1939  $section = false, $more = null
1940  ) {
1941  global $wgLang;
1942 
1943  $outText = '';
1944  if ( count( $templates ) > 0 ) {
1945  # Do a batch existence check
1946  $batch = new LinkBatch;
1947  foreach ( $templates as $title ) {
1948  $batch->addObj( $title );
1949  }
1950  $batch->execute();
1951 
1952  # Construct the HTML
1953  $outText = '<div class="mw-templatesUsedExplanation">';
1954  if ( $preview ) {
1955  $outText .= wfMessage( 'templatesusedpreview' )->numParams( count( $templates ) )
1956  ->parseAsBlock();
1957  } elseif ( $section ) {
1958  $outText .= wfMessage( 'templatesusedsection' )->numParams( count( $templates ) )
1959  ->parseAsBlock();
1960  } else {
1961  $outText .= wfMessage( 'templatesused' )->numParams( count( $templates ) )
1962  ->parseAsBlock();
1963  }
1964  $outText .= "</div><ul>\n";
1965 
1966  usort( $templates, 'Title::compare' );
1967  foreach ( $templates as $titleObj ) {
1968  $protected = '';
1969  $restrictions = $titleObj->getRestrictions( 'edit' );
1970  if ( $restrictions ) {
1971  // Check backwards-compatible messages
1972  $msg = null;
1973  if ( $restrictions === [ 'sysop' ] ) {
1974  $msg = wfMessage( 'template-protected' );
1975  } elseif ( $restrictions === [ 'autoconfirmed' ] ) {
1976  $msg = wfMessage( 'template-semiprotected' );
1977  }
1978  if ( $msg && !$msg->isDisabled() ) {
1979  $protected = $msg->parse();
1980  } else {
1981  // Construct the message from restriction-level-*
1982  // e.g. restriction-level-sysop, restriction-level-autoconfirmed
1983  $msgs = [];
1984  foreach ( $restrictions as $r ) {
1985  $msgs[] = wfMessage( "restriction-level-$r" )->parse();
1986  }
1987  $protected = wfMessage( 'parentheses' )
1988  ->rawParams( $wgLang->commaList( $msgs ) )->escaped();
1989  }
1990  }
1991  if ( $titleObj->quickUserCan( 'edit' ) ) {
1992  $editLink = self::link(
1993  $titleObj,
1994  wfMessage( 'editlink' )->escaped(),
1995  [],
1996  [ 'action' => 'edit' ]
1997  );
1998  } else {
1999  $editLink = self::link(
2000  $titleObj,
2001  wfMessage( 'viewsourcelink' )->escaped(),
2002  [],
2003  [ 'action' => 'edit' ]
2004  );
2005  }
2006  $outText .= '<li>' . self::link( $titleObj )
2007  . wfMessage( 'word-separator' )->escaped()
2008  . wfMessage( 'parentheses' )->rawParams( $editLink )->escaped()
2009  . wfMessage( 'word-separator' )->escaped()
2010  . $protected . '</li>';
2011  }
2012 
2013  if ( $more instanceof Title ) {
2014  $outText .= '<li>' . self::link( $more, wfMessage( 'moredotdotdot' ) ) . '</li>';
2015  } elseif ( $more ) {
2016  $outText .= "<li>$more</li>";
2017  }
2018 
2019  $outText .= '</ul>';
2020  }
2021  return $outText;
2022  }
2023 
2032  public static function formatHiddenCategories( $hiddencats ) {
2033 
2034  $outText = '';
2035  if ( count( $hiddencats ) > 0 ) {
2036  # Construct the HTML
2037  $outText = '<div class="mw-hiddenCategoriesExplanation">';
2038  $outText .= wfMessage( 'hiddencategories' )->numParams( count( $hiddencats ) )->parseAsBlock();
2039  $outText .= "</div><ul>\n";
2040 
2041  foreach ( $hiddencats as $titleObj ) {
2042  # If it's hidden, it must exist - no need to check with a LinkBatch
2043  $outText .= '<li>'
2044  . self::link( $titleObj, null, [], [], 'known' )
2045  . "</li>\n";
2046  }
2047  $outText .= '</ul>';
2048  }
2049  return $outText;
2050  }
2051 
2060  public static function formatSize( $size ) {
2061  global $wgLang;
2062  return htmlspecialchars( $wgLang->formatSize( $size ) );
2063  }
2064 
2080  public static function titleAttrib( $name, $options = null, array $msgParams = [] ) {
2081  $message = wfMessage( "tooltip-$name", $msgParams );
2082  if ( !$message->exists() ) {
2083  $tooltip = false;
2084  } else {
2085  $tooltip = $message->text();
2086  # Compatibility: formerly some tooltips had [alt-.] hardcoded
2087  $tooltip = preg_replace( "/ ?\[alt-.\]$/", '', $tooltip );
2088  # Message equal to '-' means suppress it.
2089  if ( $tooltip == '-' ) {
2090  $tooltip = false;
2091  }
2092  }
2093 
2094  if ( $options == 'withaccess' ) {
2095  $accesskey = self::accesskey( $name );
2096  if ( $accesskey !== false ) {
2097  // Should be build the same as in jquery.accessKeyLabel.js
2098  if ( $tooltip === false || $tooltip === '' ) {
2099  $tooltip = wfMessage( 'brackets', $accesskey )->text();
2100  } else {
2101  $tooltip .= wfMessage( 'word-separator' )->text();
2102  $tooltip .= wfMessage( 'brackets', $accesskey )->text();
2103  }
2104  }
2105  }
2106 
2107  return $tooltip;
2108  }
2109 
2110  public static $accesskeycache;
2111 
2123  public static function accesskey( $name ) {
2124  if ( isset( self::$accesskeycache[$name] ) ) {
2125  return self::$accesskeycache[$name];
2126  }
2127 
2128  $message = wfMessage( "accesskey-$name" );
2129 
2130  if ( !$message->exists() ) {
2131  $accesskey = false;
2132  } else {
2133  $accesskey = $message->plain();
2134  if ( $accesskey === '' || $accesskey === '-' ) {
2135  # @todo FIXME: Per standard MW behavior, a value of '-' means to suppress the
2136  # attribute, but this is broken for accesskey: that might be a useful
2137  # value.
2138  $accesskey = false;
2139  }
2140  }
2141 
2142  self::$accesskeycache[$name] = $accesskey;
2143  return self::$accesskeycache[$name];
2144  }
2145 
2159  public static function getRevDeleteLink( User $user, Revision $rev, Title $title ) {
2160  $canHide = $user->isAllowed( 'deleterevision' );
2161  if ( !$canHide && !( $rev->getVisibility() && $user->isAllowed( 'deletedhistory' ) ) ) {
2162  return '';
2163  }
2164 
2165  if ( !$rev->userCan( Revision::DELETED_RESTRICTED, $user ) ) {
2166  return Linker::revDeleteLinkDisabled( $canHide ); // revision was hidden from sysops
2167  } else {
2168  if ( $rev->getId() ) {
2169  // RevDelete links using revision ID are stable across
2170  // page deletion and undeletion; use when possible.
2171  $query = [
2172  'type' => 'revision',
2173  'target' => $title->getPrefixedDBkey(),
2174  'ids' => $rev->getId()
2175  ];
2176  } else {
2177  // Older deleted entries didn't save a revision ID.
2178  // We have to refer to these by timestamp, ick!
2179  $query = [
2180  'type' => 'archive',
2181  'target' => $title->getPrefixedDBkey(),
2182  'ids' => $rev->getTimestamp()
2183  ];
2184  }
2185  return Linker::revDeleteLink( $query,
2186  $rev->isDeleted( Revision::DELETED_RESTRICTED ), $canHide );
2187  }
2188  }
2189 
2200  public static function revDeleteLink( $query = [], $restricted = false, $delete = true ) {
2201  $sp = SpecialPage::getTitleFor( 'Revisiondelete' );
2202  $msgKey = $delete ? 'rev-delundel' : 'rev-showdeleted';
2203  $html = wfMessage( $msgKey )->escaped();
2204  $tag = $restricted ? 'strong' : 'span';
2205  $link = self::link( $sp, $html, [], $query, [ 'known', 'noclasses' ] );
2206  return Xml::tags(
2207  $tag,
2208  [ 'class' => 'mw-revdelundel-link' ],
2209  wfMessage( 'parentheses' )->rawParams( $link )->escaped()
2210  );
2211  }
2212 
2222  public static function revDeleteLinkDisabled( $delete = true ) {
2223  $msgKey = $delete ? 'rev-delundel' : 'rev-showdeleted';
2224  $html = wfMessage( $msgKey )->escaped();
2225  $htmlParentheses = wfMessage( 'parentheses' )->rawParams( $html )->escaped();
2226  return Xml::tags( 'span', [ 'class' => 'mw-revdelundel-link' ], $htmlParentheses );
2227  }
2228 
2229  /* Deprecated methods */
2230 
2240  public static function tooltipAndAccesskeyAttribs( $name, array $msgParams = [] ) {
2241  # @todo FIXME: If Sanitizer::expandAttributes() treated "false" as "output
2242  # no attribute" instead of "output '' as value for attribute", this
2243  # would be three lines.
2244  $attribs = [
2245  'title' => self::titleAttrib( $name, 'withaccess', $msgParams ),
2246  'accesskey' => self::accesskey( $name )
2247  ];
2248  if ( $attribs['title'] === false ) {
2249  unset( $attribs['title'] );
2250  }
2251  if ( $attribs['accesskey'] === false ) {
2252  unset( $attribs['accesskey'] );
2253  }
2254  return $attribs;
2255  }
2256 
2264  public static function tooltip( $name, $options = null ) {
2265  # @todo FIXME: If Sanitizer::expandAttributes() treated "false" as "output
2266  # no attribute" instead of "output '' as value for attribute", this
2267  # would be two lines.
2268  $tooltip = self::titleAttrib( $name, $options );
2269  if ( $tooltip === false ) {
2270  return '';
2271  }
2272  return Xml::expandAttributes( [
2273  'title' => $tooltip
2274  ] );
2275  }
2276 
2277 }
2278 
static generateTOC($tree, $lang=false)
Generate a table of contents from a section tree.
Definition: Linker.php:1664
const FOR_THIS_USER
Definition: Revision.php:84
getFragment()
Get the Title fragment (i.e.
Definition: Title.php:1334
static tocLineEnd()
End a Table Of Contents line.
Definition: Linker.php:1634
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:1816
Interface for objects which can provide a MediaWiki context on request.
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
static revComment(Revision $rev, $local=false, $isPublic=false)
Wrap and format the given revision's comment block, if the current user is allowed to view it...
Definition: Linker.php:1551
wfGetDB($db, $groups=[], $wiki=false)
Get a Database object.
static tocList($toc, $lang=false)
Wraps the TOC in a table and provides the hide/collapse javascript.
Definition: Linker.php:1646
static formatAutocomments($comment, $title=null, $local=false, $wikiId=null)
Converts autogenerated comments in edit summaries into section links.
Definition: Linker.php:1213
static processResponsiveImages($file, $thumb, $hp)
Process responsive images: add 1.5x and 2x subimages to the thumbnail, where applicable.
Definition: Linker.php:748
the array() calling protocol came about after MediaWiki 1.4rc1.
null for the local wiki Added should default to null in handler for backwards compatibility add a value to it if you want to add a cookie that have to vary cache options can modify $query
Definition: hooks.txt:1435
$context
Definition: load.php:43
static getRevDeleteLink(User $user, Revision $rev, Title $title)
Get a revision-deletion link, or disabled link, or nothing, depending on user permissions & the setti...
Definition: Linker.php:2159
const NS_MAIN
Definition: Defines.php:69
$success
getText()
Get the text form (spaces not underscores) of the main part.
Definition: Title.php:872
processing should stop and the error should be shown to the user * false
Definition: hooks.txt:189
static buildRollbackLink($rev, IContextSource $context=null, $editCount=false)
Build a raw rollback link, useful for collections of "tool" links.
Definition: Linker.php:1861
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
static getTitleFor($name, $subpage=false, $fragment= '')
Get a localised Title object for a specified special page name.
Definition: SpecialPage.php:80
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 before processing starts Return false to skip default processing and return $ret $linkRenderer
Definition: hooks.txt:1816
getTimestamp()
Definition: Revision.php:1169
msg()
Get a Message object with context set.
static rawElement($element, $attribs=[], $contents= '')
Returns an HTML element in a string.
Definition: Html.php:210
static expandAttributes($attribs)
Given an array of ('attributename' => 'value'), it generates the code to set the XML attributes : attr...
Definition: Xml.php:67
if(!isset($args[0])) $lang
static formatRevisionSize($size)
Definition: Linker.php:1574
static normaliseSpecialPage(LinkTarget $target)
Definition: Linker.php:321
static fnamePart($url)
Returns the filename part of an url.
Definition: Linker.php:342
$comment
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 in any and then calling but I prefer the flexibility This should also do the output encoding The system allocates a global one in $wgOut Title Represents the title of an and does all the work of translating among various forms such as plain database etc For and for historical reasons
Definition: design.txt:25
static makeSelfLinkObj($nt, $html= '', $query= '', $trail= '', $prefix= '')
Make appropriate markup for a link to the current article.
Definition: Linker.php:277
static newFromId($id)
Static factory method for creation from a given user ID.
Definition: User.php:545
const NS_SPECIAL
Definition: Defines.php:58
static getLinkColour(LinkTarget $t, $threshold)
Return the CSS colour of a known link.
Definition: Linker.php:147
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency MediaWikiServices
Definition: injection.txt:23
it s the revision text itself In either if gzip is the revision text is gzipped $flags
Definition: hooks.txt:2588
getPrefixedText()
Get the prefixed title with spaces.
Definition: Title.php:1430
static exists($index)
Returns whether the specified namespace exists.
static newFromText($text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
Definition: Title.php:256
null means default & $customAttribs
Definition: hooks.txt:1816
Represents a title within MediaWiki.
Definition: Title.php:36
when a variable name is used in a it is silently declared as a new local masking the global
Definition: design.txt:93
magic word & $parser
Definition: hooks.txt:2372
static blockLink($userId, $userText)
Definition: Linker.php:1096
static tooltipAndAccesskeyAttribs($name, array $msgParams=[])
Returns the attributes for the tooltip and access key.
Definition: Linker.php:2240
getNamespace()
Get the namespace index.
see documentation in includes Linker php for Linker::makeImageLink & $time
Definition: hooks.txt:1629
static formatTemplates($templates, $preview=false, $section=false, $more=null)
Returns HTML for the "templates used on this page" list.
Definition: Linker.php:1938
wfDebug($text, $dest= 'all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
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 $wgLang
Definition: design.txt:56
the value to return A Title object or null for latest to be modified or replaced by the hook handler or if authentication is not possible after cache objects are set for highlighting & $link
Definition: hooks.txt:2621
getFragment()
Get the link fragment (i.e.
$batch
Definition: linkcache.txt:23
static getRollbackEditCount($rev, $verify)
This function will return the number of revisions which a rollback would revert and, if $verify is set it will verify that a revision can be reverted (that the user isn't the only contributor and the revision we might rollback to isn't deleted).
Definition: Linker.php:1801
static splitTrail($trail)
Split a link trail, return the "inside" portion and the remainder of the trail as a two-element array...
Definition: Linker.php:1721
The User object encapsulates all of the user-specific settings (user_id, name, rights, email address, options, last login time).
Definition: User.php:47
$wgEnableUploads
Uploads have to be specially set up to be secure.
static makeMediaLinkObj($title, $html= '', $time=false)
Create a direct link to a given uploaded file.
Definition: Linker.php:859
static makeBrokenImageLinkObj($title, $label= '', $query= '', $unused1= '', $unused2= '', $time=false)
Make a "broken" link to an image.
Definition: Linker.php:783
Class representing a list of titles The execute() method checks them all for existence and adds them ...
Definition: LinkBatch.php:32
static getLinkAttributesInternal($title, $class)
Common code for getLinkAttributesX functions.
Definition: Linker.php:122
wfCgiToArray($query)
This is the logical opposite of wfArrayToCgi(): it accepts a query string as its argument and returns...
isDeleted($field)
Definition: Revision.php:996
static normalizeSectionNameWhitespace($section)
Normalizes whitespace in a section name, such as might be returned by Parser::stripSectionName(), for use in the id's that are used for section links.
Definition: Sanitizer.php:1380
static getForeignURL($wikiID, $page, $fragmentId=null)
Convenience to get a url to a page on a foreign wiki.
Definition: WikiMap.php:168
getTitle()
Returns the title of the page associated with this entry or null.
Definition: Revision.php:790
Some quick notes on the file repository architecture Functionality is
Definition: README:3
isExternal()
Is this Title interwiki?
Definition: Title.php:789
static getMain()
Static methods.
$wgUploadMissingFileUrl
Point the upload link for missing files to an external URL, as with $wgUploadNavigationUrl.
static normalizeSubpageLink($contextTitle, $target, &$text)
Definition: Linker.php:1440
static getCanonicalName($index)
Returns the canonical (English) name for a given index.
wfAppendQuery($url, $query)
Append a query string to an existing URL, which may or may not already have query string parameters a...
static titleAttrib($name, $options=null, array $msgParams=[])
Given the id of an interface element, constructs the appropriate title attribute from the system mess...
Definition: Linker.php:2080
isAllowed($action= '')
Internal mechanics of testing a permission.
Definition: User.php:3576
static formatLinksInComment($comment, $title=null, $local=false, $wikiId=null)
Formats wiki links and media links in text; all other wiki formatting is ignored. ...
Definition: Linker.php:1306
Some internal bits split of from Skin.php.
Definition: Linker.php:34
wfWarn($msg, $callerOffset=1, $level=E_USER_NOTICE)
Send a warning either to the debug log or in a PHP error depending on $wgDevelopmentWarnings.
getId()
Get revision ID.
Definition: Revision.php:716
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set and then return false from the hook function Ensure you consume the ChangeTagAfterDelete hook to carry out custom deletion actions as context called by AbstractContent::getParserOutput May be used to override the normal model specific rendering of page content as context as context $options
Definition: hooks.txt:1020
$wgThumbUpright
Adjust width of upright images when parameter 'upright' is used This allows a nicer look for upright ...
static escapeHtmlAllowEntities($html)
Given HTML input, escape with htmlspecialchars but un-escape entities.
Definition: Sanitizer.php:1261
const NS_MEDIA
Definition: Defines.php:57
getLocalURL($query= '', $query2=false)
Get a URL with no fragment or server name (relative URL) from a Title object.
Definition: Title.php:1688
$res
Definition: database.txt:21
static singleton()
Get a RepoGroup instance.
Definition: RepoGroup.php:59
$wgMiserMode
Disable database-intensive features.
static accesskey($name)
Given the id of an interface element, constructs the appropriate accesskey attribute from the system ...
Definition: Linker.php:2123
static emailLink($userId, $userText)
Definition: Linker.php:1107
const TOOL_LINKS_NOBLOCK
Flags for userToolLinks()
Definition: Linker.php:38
getDBkey()
Get the main part with underscores.
static revDeleteLinkDisabled($delete=true)
Creates a dead (show/hide) link for deleting revisions/log entries.
Definition: Linker.php:2222
$params
static makeHeadline($level, $attribs, $anchor, $html, $link, $legacyAnchor=false)
Create a headline for content.
Definition: Linker.php:1702
$wgThumbLimits
Adjust thumbnails on image pages according to a user setting.
wfDeprecated($function, $version=false, $component=false, $callerOffset=2)
Throws a warning that $function is deprecated.
userCan($field, User $user=null)
Determine if the current user is allowed to view a particular field of this revision, if it's marked as deleted.
Definition: Revision.php:1708
getComment($audience=self::FOR_PUBLIC, User $user=null)
Fetch revision comment if it's available to the specified audience.
Definition: Revision.php:922
static makeTitleSafe($ns, $title, $fragment= '', $interwiki= '')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:527
const DELETED_RESTRICTED
Definition: Revision.php:79
const DB_SLAVE
Definition: Defines.php:46
getTargetLanguage()
Get the target language for the content being parsed.
Definition: Parser.php:836
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 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 after processing after in associative array form externallinks including delete and has completed for all link tables whether this was an auto creation 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 unsetoffset-wrap String Wrap the message in html(usually something like"&lt
Allows to change the fields on the form that will be generated are created Can be used to omit specific feeds from being outputted You must not use this hook to add use OutputPage::addFeedLink() instead.&$feedLinks conditions will AND in the final query as a Content object as a Content object $title
Definition: hooks.txt:312
static linkKnown($target, $html=null, $customAttribs=[], $query=[], $options=[ 'known'])
Identical to link(), except $options defaults to 'known'.
Definition: Linker.php:255
static hasSubpages($index)
Does the namespace allow subpages?
static tocUnindent($level)
Finish one or more sublevels on the Table of Contents.
Definition: Linker.php:1601
static run($event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:131
static tocLine($anchor, $tocline, $tocnumber, $level, $sectionIndex=false)
parameter level defines if we are on an indentation level
Definition: Linker.php:1616
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
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books $tag
Definition: hooks.txt:981
const NS_FILE
Definition: Defines.php:75
static makeImageLink(Parser $parser, Title $title, $file, $frameParams=[], $handlerParams=[], $time=false, $query="", $widthOption=null)
Given parameters derived from [[Image:Foo|options...]], generate the HTML that that syntax inserts in...
Definition: Linker.php:415
presenting them properly to the user as errors is done by the caller return true use this to change the list i e etc $rev
Definition: hooks.txt:1601
static makeCommentLink(Title $title, $text, $wikiId=null, $options=[])
Generates a link to the given Title.
Definition: Linker.php:1414
static configuration should be added through ResourceLoaderGetConfigVars instead can be used to get the real title after the basic globals have been set but before ordinary actions take place or wrap services the preferred way to define a new service is the $wgServiceWiringFiles array $services
Definition: hooks.txt:2044
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 & $ret
Definition: hooks.txt:1816
static userToolLinksRedContribs($userId, $userText, $edits=null)
Alias for userToolLinks( $userId, $userText, true );.
Definition: Linker.php:1074
const RAW
Definition: Revision.php:85
$wgDisableAnonTalk
Disable links to talk pages of anonymous users (IPs) in listings on special pages like page history...
static revUserLink($rev, $isPublic=false)
Generate a user link if the current user is allowed to view it.
Definition: Linker.php:1120
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
return true to allow those checks to and false if checking is done remove or add to the links of a group of changes in EnhancedChangesList Hook subscribers can return false to omit this line from recentchanges 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 true if there is text before this autocomment $auto
Definition: hooks.txt:1322
static formatComment($comment, $title=null, $local=false, $wikiId=null)
This function is called by all recent changes variants, by the page history, and by the user contribu...
Definition: Linker.php:1181
usually copyright or history_copyright This message must be in HTML not wikitext if the section is included from a template $section
Definition: hooks.txt:2755
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 local account $user
Definition: hooks.txt:242
static link($target, $html=null, $customAttribs=[], $query=[], $options=[])
This function returns an HTML link to the given target.
Definition: Linker.php:203
$wgSVGMaxSize
Don't scale a SVG larger than this.
const DELETED_TEXT
Definition: Revision.php:76
static getImageLinkMTOParams($frameParams, $query= '', $parser=null)
Get the link parameters for MediaTransformOutput::toHtml() from given frame parameters supplied by th...
Definition: Linker.php:559
static tooltip($name, $options=null)
Returns raw bits of HTML, use titleAttrib()
Definition: Linker.php:2264
static userLink($userId, $userName, $altUserName=false)
Make user link (or user contributions for unregistered users)
Definition: Linker.php:986
static makeExternalLink($url, $text, $escape=true, $linktype= '', $attribs=[], $title=null)
Make an external link.
Definition: Linker.php:936
static getExternalLinkRel($url=false, $title=null)
Get the rel attribute for a particular external link.
Definition: Parser.php:1861
getVisibility()
Get the deletion bitfield of the revision.
Definition: Revision.php:1005
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition: injection.txt:35
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 change
Definition: distributors.txt:9
static tags($element, $attribs=null, $contents)
Same as Xml::element(), but does not escape contents.
Definition: Xml.php:131
const DELETED_USER
Definition: Revision.php:78
controlled by $wgMainCacheType controlled by $wgParserCacheType controlled by $wgMessageCacheType If you set CACHE_NONE to one of the three control default value for MediaWiki still create a but requests to it are no ops and we always fall through to the database If the cache daemon can t be it should also disable itself fairly smoothly By $wgMemc is used but when it is $parserMemc or $messageMemc this is mentioned below
Definition: memcached.txt:96
static getInternalLinkAttributesObj($nt, $unused=null, $class= '', $title=false)
Get the appropriate HTML attributes to add to the "a" element of an internal link, given the Title object for the page we want to link to.
Definition: Linker.php:102
static resolveAlias($alias)
Given a special page name with a possible subpage, return an array where the first element is the spe...
static generateRollback($rev, IContextSource $context=null, $options=[ 'verify'])
Generate a rollback link for a given revision.
Definition: Linker.php:1762
static formatHiddenCategories($hiddencats)
Returns HTML for the "hidden categories on this page" list.
Definition: Linker.php:2032
static makeThumbLink2(Title $title, $file, $frameParams=[], $handlerParams=[], $time=false, $query="")
Definition: Linker.php:622
static userTalkLink($userId, $userText)
Definition: Linker.php:1084
static getUploadUrl($destFile, $query= '')
Get the URL to upload a certain file.
Definition: Linker.php:833
static makeMediaLinkFile(Title $title, $file, $html= '')
Create a direct link to a given uploaded file.
Definition: Linker.php:876
static userToolLinks($userId, $userText, $redContribsWhenNoEdits=false, $flags=0, $edits=null)
Generate standard user tool links (talk, contributions, block link, etc.)
Definition: Linker.php:1018
usually copyright or history_copyright This message must be in HTML not wikitext if the section is included from a template to be included in the link
Definition: hooks.txt:2755
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 local content language as $wgContLang
Definition: design.txt:56
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:2060
see documentation in includes Linker php for Linker::makeImageLink & $handlerParams
Definition: hooks.txt:1627
static tocIndent()
Add another level to the Table of Contents.
Definition: Linker.php:1590
static getInterwikiLinkAttributes($title, $unused=null, $class= 'external')
Get the appropriate HTML attributes to add to the "a" element of an interwiki link.
Definition: Linker.php:54
static getInternalLinkAttributes($title, $unused=null, $class= '')
Get the appropriate HTML attributes to add to the "a" element of an internal link.
Definition: Linker.php:80
static getInvalidTitleDescription(IContextSource $context, $namespace, $title)
Get a message saying that an invalid title was encountered.
Definition: Linker.php:300
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 & $attribs
Definition: hooks.txt:1816
$wgShowRollbackEditCount
The $wgShowRollbackEditCount variable is used to show how many edits can be rolled back...
if(!$wgRequest->checkUrlExtension()) if(!$wgEnableAPI) $wgTitle
Definition: api.php:57
const DELETED_COMMENT
Definition: Revision.php:77
static commentBlock($comment, $title=null, $local=false, $wikiId=null)
Wrap a comment in standard punctuation and formatting if it's non-empty, otherwise return empty strin...
Definition: Linker.php:1526
Marks HTML that shouldn't be escaped.
Definition: HtmlArmor.php:28
static makeThumbLinkObj(Title $title, $file, $label= '', $alt, $align= 'right', $params=[], $framed=false, $manualthumb="")
Make HTML for a thumbnail including image, border and caption.
Definition: Linker.php:596
static getDefaultOption($opt)
Get a given default option value.
Definition: User.php:1598
static $accesskeycache
Definition: Linker.php:2110
$wgUploadNavigationUrl
Point the upload navigation link to an external URL Useful if you want to use a shared repository by ...
$wgResponsiveImages
Generate and use thumbnails suitable for screens with 1.5 and 2.0 pixel densities.
const NS_USER_TALK
Definition: Defines.php:72
static revUserTools($rev, $isPublic=false)
Generate a user tool link cluster if the current user is allowed to view it.
Definition: Linker.php:1142
return true to allow those checks to and false if checking is done remove or add to the links of a group of changes in EnhancedChangesList Hook subscribers can return false to omit this line from recentchanges 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 $pre
Definition: hooks.txt:1322
static makeExternalImage($url, $alt= '')
Return the code for images which were added via external links, via Parser::maybeMakeExternalImage()...
Definition: Linker.php:362
static specialLink($name, $key= '')
Make a link to a special page given its name and, optionally, a message key from the link text...
Definition: Linker.php:917
static element($element, $attribs=[], $contents= '')
Identical to rawElement(), but HTML-escapes $contents (like Xml::element()).
Definition: Html.php:230
wfFindFile($title, $options=[])
Find a file.
static revDeleteLink($query=[], $restricted=false, $delete=true)
Creates a (show/hide) link for deleting revisions/log entries.
Definition: Linker.php:2200
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 before the output is cached one of or reset my talk page
Definition: hooks.txt:2376
static makeTitle($ns, $title, $fragment= '', $interwiki= '')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:503
PHP Parser - Processes wiki markup (which uses a more user-friendly syntax, such as "[[link]]" for ma...
Definition: Parser.php:69
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 before the output is cached $page
Definition: hooks.txt:2376
wfGetLangObj($langcode=false)
Return a Language object from $langcode.
getPrefixedDBkey()
Get the prefixed database key form.
Definition: Title.php:1418
const TOOL_LINKS_EMAIL
Definition: Linker.php:39
static prettifyIP($ip)
Prettify an IP for display to end users.
Definition: IP.php:201
$wgUser
Definition: Setup.php:801
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:310