MediaWiki  master
Linker.php
Go to the documentation of this file.
1 <?php
26 use Wikimedia\IPUtils;
27 
37 class Linker {
41  public const TOOL_LINKS_NOBLOCK = 1;
42  public const TOOL_LINKS_EMAIL = 2;
43 
87  public static function link(
88  $target, $html = null, $customAttribs = [], $query = [], $options = []
89  ) {
90  if ( !$target instanceof LinkTarget ) {
91  wfWarn( __METHOD__ . ': Requires $target to be a LinkTarget object.', 2 );
92  return "<!-- ERROR -->$html";
93  }
94 
95  $services = MediaWikiServices::getInstance();
96  $options = (array)$options;
97  if ( $options ) {
98  // Custom options, create new LinkRenderer
99  if ( !isset( $options['stubThreshold'] ) ) {
100  $defaultLinkRenderer = $services->getLinkRenderer();
101  $options['stubThreshold'] = $defaultLinkRenderer->getStubThreshold();
102  }
103  $linkRenderer = $services->getLinkRendererFactory()
104  ->createFromLegacyOptions( $options );
105  } else {
106  $linkRenderer = $services->getLinkRenderer();
107  }
108 
109  if ( $html !== null ) {
110  $text = new HtmlArmor( $html );
111  } else {
112  $text = null;
113  }
114 
115  if ( in_array( 'known', $options, true ) ) {
116  return $linkRenderer->makeKnownLink( $target, $text, $customAttribs, $query );
117  }
118 
119  if ( in_array( 'broken', $options, true ) ) {
120  return $linkRenderer->makeBrokenLink( $target, $text, $customAttribs, $query );
121  }
122 
123  if ( in_array( 'noclasses', $options, true ) ) {
124  return $linkRenderer->makePreloadedLink( $target, $text, '', $customAttribs, $query );
125  }
126 
127  return $linkRenderer->makeLink( $target, $text, $customAttribs, $query );
128  }
129 
143  public static function linkKnown(
144  $target, $html = null, $customAttribs = [],
145  $query = [], $options = [ 'known' ]
146  ) {
147  return self::link( $target, $html, $customAttribs, $query, $options );
148  }
149 
165  public static function makeSelfLinkObj( $nt, $html = '', $query = '', $trail = '', $prefix = '' ) {
166  $nt = Title::newFromLinkTarget( $nt );
167  $ret = "<a class=\"mw-selflink selflink\">{$prefix}{$html}</a>{$trail}";
168  if ( !Hooks::runner()->onSelfLinkBegin( $nt, $html, $trail, $prefix, $ret ) ) {
169  return $ret;
170  }
171 
172  if ( $html == '' ) {
173  $html = htmlspecialchars( $nt->getPrefixedText() );
174  }
175  list( $inside, $trail ) = self::splitTrail( $trail );
176  return "<a class=\"mw-selflink selflink\">{$prefix}{$html}{$inside}</a>{$trail}";
177  }
178 
189  public static function getInvalidTitleDescription( IContextSource $context, $namespace, $title ) {
190  // First we check whether the namespace exists or not.
191  if ( MediaWikiServices::getInstance()->getNamespaceInfo()->exists( $namespace ) ) {
192  if ( $namespace == NS_MAIN ) {
193  $name = $context->msg( 'blanknamespace' )->text();
194  } else {
195  $name = MediaWikiServices::getInstance()->getContentLanguage()->
196  getFormattedNsText( $namespace );
197  }
198  return $context->msg( 'invalidtitle-knownnamespace', $namespace, $name, $title )->text();
199  }
200 
201  return $context->msg( 'invalidtitle-unknownnamespace', $namespace, $title )->text();
202  }
203 
210  public static function normaliseSpecialPage( LinkTarget $target ) {
211  wfDeprecated( __METHOD__, '1.35' );
212  $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
213  return $linkRenderer->normalizeTarget( $target );
214  }
215 
224  private static function fnamePart( $url ) {
225  $basename = strrchr( $url, '/' );
226  if ( $basename === false ) {
227  $basename = $url;
228  } else {
229  $basename = substr( $basename, 1 );
230  }
231  return $basename;
232  }
233 
244  public static function makeExternalImage( $url, $alt = '' ) {
245  if ( $alt == '' ) {
246  $alt = self::fnamePart( $url );
247  }
248  $img = '';
249  $success = Hooks::runner()->onLinkerMakeExternalImage( $url, $alt, $img );
250  if ( !$success ) {
251  wfDebug( "Hook LinkerMakeExternalImage changed the output of external image "
252  . "with url {$url} and alt text {$alt} to {$img}" );
253  return $img;
254  }
255  return Html::element( 'img',
256  [
257  'src' => $url,
258  'alt' => $alt
259  ]
260  );
261  }
262 
300  public static function makeImageLink( Parser $parser, LinkTarget $title,
301  $file, $frameParams = [], $handlerParams = [], $time = false,
302  $query = "", $widthOption = null
303  ) {
305  $res = null;
306  $dummy = new DummyLinker;
307  if ( !Hooks::runner()->onImageBeforeProduceHTML( $dummy, $title,
308  $file, $frameParams, $handlerParams, $time, $res,
309  $parser, $query, $widthOption )
310  ) {
311  return $res;
312  }
313 
314  if ( $file && !$file->allowInlineDisplay() ) {
315  wfDebug( __METHOD__ . ': ' . $title->getPrefixedDBkey() . " does not allow inline display" );
316  return self::link( $title );
317  }
318 
319  // Clean up parameters
320  $page = $handlerParams['page'] ?? false;
321  if ( !isset( $frameParams['align'] ) ) {
322  $frameParams['align'] = '';
323  }
324  if ( !isset( $frameParams['alt'] ) ) {
325  $frameParams['alt'] = '';
326  }
327  if ( !isset( $frameParams['title'] ) ) {
328  $frameParams['title'] = '';
329  }
330  if ( !isset( $frameParams['class'] ) ) {
331  $frameParams['class'] = '';
332  }
333 
334  $prefix = $postfix = '';
335 
336  if ( $frameParams['align'] == 'center' ) {
337  $prefix = '<div class="center">';
338  $postfix = '</div>';
339  $frameParams['align'] = 'none';
340  }
341  if ( $file && !isset( $handlerParams['width'] ) ) {
342  if ( isset( $handlerParams['height'] ) && $file->isVectorized() ) {
343  // If its a vector image, and user only specifies height
344  // we don't want it to be limited by its "normal" width.
345  global $wgSVGMaxSize;
346  $handlerParams['width'] = $wgSVGMaxSize;
347  } else {
348  $handlerParams['width'] = $file->getWidth( $page );
349  }
350 
351  if ( isset( $frameParams['thumbnail'] )
352  || isset( $frameParams['manualthumb'] )
353  || isset( $frameParams['framed'] )
354  || isset( $frameParams['frameless'] )
355  || !$handlerParams['width']
356  ) {
358 
359  if ( $widthOption === null || !isset( $wgThumbLimits[$widthOption] ) ) {
360  $userOptionsLookup = MediaWikiServices::getInstance()->getUserOptionsLookup();
361  $widthOption = $userOptionsLookup->getDefaultOption( 'thumbsize' );
362  }
363 
364  // Reduce width for upright images when parameter 'upright' is used
365  if ( isset( $frameParams['upright'] ) && $frameParams['upright'] == 0 ) {
366  $frameParams['upright'] = $wgThumbUpright;
367  }
368 
369  // For caching health: If width scaled down due to upright
370  // parameter, round to full __0 pixel to avoid the creation of a
371  // lot of odd thumbs.
372  $prefWidth = isset( $frameParams['upright'] ) ?
373  round( $wgThumbLimits[$widthOption] * $frameParams['upright'], -1 ) :
374  $wgThumbLimits[$widthOption];
375 
376  // Use width which is smaller: real image width or user preference width
377  // Unless image is scalable vector.
378  if ( !isset( $handlerParams['height'] ) && ( $handlerParams['width'] <= 0 ||
379  $prefWidth < $handlerParams['width'] || $file->isVectorized() ) ) {
380  $handlerParams['width'] = $prefWidth;
381  }
382  }
383  }
384 
385  if ( isset( $frameParams['thumbnail'] ) || isset( $frameParams['manualthumb'] )
386  || isset( $frameParams['framed'] )
387  ) {
388  # Create a thumbnail. Alignment depends on the writing direction of
389  # the page content language (right-aligned for LTR languages,
390  # left-aligned for RTL languages)
391  # If a thumbnail width has not been provided, it is set
392  # to the default user option as specified in Language*.php
393  if ( $frameParams['align'] == '' ) {
394  $frameParams['align'] = $parser->getTargetLanguage()->alignEnd();
395  }
396  return $prefix .
397  self::makeThumbLink2( $title, $file, $frameParams, $handlerParams, $time, $query ) .
398  $postfix;
399  }
400 
401  if ( $file && isset( $frameParams['frameless'] ) ) {
402  $srcWidth = $file->getWidth( $page );
403  # For "frameless" option: do not present an image bigger than the
404  # source (for bitmap-style images). This is the same behavior as the
405  # "thumb" option does it already.
406  if ( $srcWidth && !$file->mustRender() && $handlerParams['width'] > $srcWidth ) {
407  $handlerParams['width'] = $srcWidth;
408  }
409  }
410 
411  if ( $file && isset( $handlerParams['width'] ) ) {
412  # Create a resized image, without the additional thumbnail features
413  $thumb = $file->transform( $handlerParams );
414  } else {
415  $thumb = false;
416  }
417 
418  if ( !$thumb ) {
419  $s = self::makeBrokenImageLinkObj( $title, $frameParams['title'], '', '', '', $time == true );
420  } else {
421  self::processResponsiveImages( $file, $thumb, $handlerParams );
422  $params = [
423  'alt' => $frameParams['alt'],
424  'title' => $frameParams['title'],
425  'valign' => $frameParams['valign'] ?? false,
426  'img-class' => $frameParams['class'] ];
427  if ( isset( $frameParams['border'] ) ) {
428  $params['img-class'] .= ( $params['img-class'] !== '' ? ' ' : '' ) . 'thumbborder';
429  }
430  $params = self::getImageLinkMTOParams( $frameParams, $query, $parser ) + $params;
431 
432  $s = $thumb->toHtml( $params );
433  }
434  if ( $frameParams['align'] != '' ) {
436  'div',
437  [ 'class' => 'float' . $frameParams['align'] ],
438  $s
439  );
440  }
441  return str_replace( "\n", ' ', $prefix . $s . $postfix );
442  }
443 
452  private static function getImageLinkMTOParams( $frameParams, $query = '', $parser = null ) {
453  $mtoParams = [];
454  if ( isset( $frameParams['link-url'] ) && $frameParams['link-url'] !== '' ) {
455  $mtoParams['custom-url-link'] = $frameParams['link-url'];
456  if ( isset( $frameParams['link-target'] ) ) {
457  $mtoParams['custom-target-link'] = $frameParams['link-target'];
458  }
459  if ( $parser ) {
460  $extLinkAttrs = $parser->getExternalLinkAttribs( $frameParams['link-url'] );
461  foreach ( $extLinkAttrs as $name => $val ) {
462  // Currently could include 'rel' and 'target'
463  $mtoParams['parser-extlink-' . $name] = $val;
464  }
465  }
466  } elseif ( isset( $frameParams['link-title'] ) && $frameParams['link-title'] !== '' ) {
467  $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
468  $mtoParams['custom-title-link'] = Title::newFromLinkTarget(
469  $linkRenderer->normalizeTarget( $frameParams['link-title'] )
470  );
471  } elseif ( !empty( $frameParams['no-link'] ) ) {
472  // No link
473  } else {
474  $mtoParams['desc-link'] = true;
475  $mtoParams['desc-query'] = $query;
476  }
477  return $mtoParams;
478  }
479 
492  public static function makeThumbLinkObj( LinkTarget $title, $file, $label = '', $alt = '',
493  $align = 'right', $params = [], $framed = false, $manualthumb = ""
494  ) {
495  $frameParams = [
496  'alt' => $alt,
497  'caption' => $label,
498  'align' => $align
499  ];
500  if ( $framed ) {
501  $frameParams['framed'] = true;
502  }
503  if ( $manualthumb ) {
504  $frameParams['manualthumb'] = $manualthumb;
505  }
506  return self::makeThumbLink2( $title, $file, $frameParams, $params );
507  }
508 
518  public static function makeThumbLink2( LinkTarget $title, $file, $frameParams = [],
519  $handlerParams = [], $time = false, $query = ""
520  ) {
521  $exists = $file && $file->exists();
522 
523  $page = $handlerParams['page'] ?? false;
524  if ( !isset( $frameParams['align'] ) ) {
525  $frameParams['align'] = 'right';
526  }
527  if ( !isset( $frameParams['alt'] ) ) {
528  $frameParams['alt'] = '';
529  }
530  if ( !isset( $frameParams['title'] ) ) {
531  $frameParams['title'] = '';
532  }
533  if ( !isset( $frameParams['caption'] ) ) {
534  $frameParams['caption'] = '';
535  }
536 
537  if ( empty( $handlerParams['width'] ) ) {
538  // Reduce width for upright images when parameter 'upright' is used
539  $handlerParams['width'] = isset( $frameParams['upright'] ) ? 130 : 180;
540  }
541  $thumb = false;
542  $noscale = false;
543  $manualthumb = false;
544 
545  if ( !$exists ) {
546  $outerWidth = $handlerParams['width'] + 2;
547  } else {
548  if ( isset( $frameParams['manualthumb'] ) ) {
549  # Use manually specified thumbnail
550  $manual_title = Title::makeTitleSafe( NS_FILE, $frameParams['manualthumb'] );
551  if ( $manual_title ) {
552  $manual_img = MediaWikiServices::getInstance()->getRepoGroup()
553  ->findFile( $manual_title );
554  if ( $manual_img ) {
555  $thumb = $manual_img->getUnscaledThumb( $handlerParams );
556  $manualthumb = true;
557  } else {
558  $exists = false;
559  }
560  }
561  } elseif ( isset( $frameParams['framed'] ) ) {
562  // Use image dimensions, don't scale
563  $thumb = $file->getUnscaledThumb( $handlerParams );
564  $noscale = true;
565  } else {
566  # Do not present an image bigger than the source, for bitmap-style images
567  # This is a hack to maintain compatibility with arbitrary pre-1.10 behavior
568  $srcWidth = $file->getWidth( $page );
569  if ( $srcWidth && !$file->mustRender() && $handlerParams['width'] > $srcWidth ) {
570  $handlerParams['width'] = $srcWidth;
571  }
572  $thumb = $file->transform( $handlerParams );
573  }
574 
575  if ( $thumb ) {
576  $outerWidth = $thumb->getWidth() + 2;
577  } else {
578  $outerWidth = $handlerParams['width'] + 2;
579  }
580  }
581 
582  # ThumbnailImage::toHtml() already adds page= onto the end of DjVu URLs
583  # So we don't need to pass it here in $query. However, the URL for the
584  # zoom icon still needs it, so we make a unique query for it. See T16771
585  $url = Title::newFromLinkTarget( $title )->getLocalURL( $query );
586  if ( $page ) {
587  $url = wfAppendQuery( $url, [ 'page' => $page ] );
588  }
589  if ( $manualthumb
590  && !isset( $frameParams['link-title'] )
591  && !isset( $frameParams['link-url'] )
592  && !isset( $frameParams['no-link'] ) ) {
593  $frameParams['link-url'] = $url;
594  }
595 
596  $s = "<div class=\"thumb t{$frameParams['align']}\">"
597  . "<div class=\"thumbinner\" style=\"width:{$outerWidth}px;\">";
598 
599  if ( !$exists ) {
600  $s .= self::makeBrokenImageLinkObj( $title, $frameParams['title'], '', '', '', $time == true );
601  $zoomIcon = '';
602  } elseif ( !$thumb ) {
603  $s .= wfMessage( 'thumbnail_error', '' )->escaped();
604  $zoomIcon = '';
605  } else {
606  if ( !$noscale && !$manualthumb ) {
607  self::processResponsiveImages( $file, $thumb, $handlerParams );
608  }
609  $params = [
610  'alt' => $frameParams['alt'],
611  'title' => $frameParams['title'],
612  'img-class' => ( isset( $frameParams['class'] ) && $frameParams['class'] !== ''
613  ? $frameParams['class'] . ' '
614  : '' ) . 'thumbimage'
615  ];
616  $params = self::getImageLinkMTOParams( $frameParams, $query ) + $params;
617  $s .= $thumb->toHtml( $params );
618  if ( isset( $frameParams['framed'] ) ) {
619  $zoomIcon = "";
620  } else {
621  $zoomIcon = Html::rawElement( 'div', [ 'class' => 'magnify' ],
622  Html::rawElement( 'a', [
623  'href' => $url,
624  'class' => 'internal',
625  'title' => wfMessage( 'thumbnail-more' )->text() ],
626  "" ) );
627  }
628  }
629  $s .= ' <div class="thumbcaption">' . $zoomIcon . $frameParams['caption'] . "</div></div></div>";
630  return str_replace( "\n", ' ', $s );
631  }
632 
641  public static function processResponsiveImages( $file, $thumb, $hp ) {
642  global $wgResponsiveImages;
643  if ( $wgResponsiveImages && $thumb && !$thumb->isError() ) {
644  $hp15 = $hp;
645  $hp15['width'] = round( $hp['width'] * 1.5 );
646  $hp20 = $hp;
647  $hp20['width'] = $hp['width'] * 2;
648  if ( isset( $hp['height'] ) ) {
649  $hp15['height'] = round( $hp['height'] * 1.5 );
650  $hp20['height'] = $hp['height'] * 2;
651  }
652 
653  $thumb15 = $file->transform( $hp15 );
654  $thumb20 = $file->transform( $hp20 );
655  if ( $thumb15 && !$thumb15->isError() && $thumb15->getUrl() !== $thumb->getUrl() ) {
656  $thumb->responsiveUrls['1.5'] = $thumb15->getUrl();
657  }
658  if ( $thumb20 && !$thumb20->isError() && $thumb20->getUrl() !== $thumb->getUrl() ) {
659  $thumb->responsiveUrls['2'] = $thumb20->getUrl();
660  }
661  }
662  }
663 
676  public static function makeBrokenImageLinkObj( $title, $label = '',
677  $query = '', $unused1 = '', $unused2 = '', $time = false
678  ) {
679  if ( !$title instanceof LinkTarget ) {
680  wfWarn( __METHOD__ . ': Requires $title to be a LinkTarget object.' );
681  return "<!-- ERROR -->" . htmlspecialchars( $label );
682  }
683 
685 
687  if ( $label == '' ) {
688  $label = $title->getPrefixedText();
689  }
690  $html = htmlspecialchars( $label );
691  $repoGroup = MediaWikiServices::getInstance()->getRepoGroup();
692  $currentExists = $time
693  && $repoGroup->findFile( $title ) !== false;
694 
696  && !$currentExists
697  ) {
698  if ( $repoGroup->getLocalRepo()->checkRedirect( $title ) ) {
699  // We already know it's a redirect, so mark it accordingly
700  return self::link(
701  $title,
702  $html,
703  [ 'class' => 'mw-redirect' ],
704  wfCgiToArray( $query ),
705  [ 'known', 'noclasses' ]
706  );
707  }
708  return Html::rawElement( 'a', [
709  'href' => self::getUploadUrl( $title, $query ),
710  'class' => 'new',
711  'title' => $title->getPrefixedText()
712  ], $html );
713  }
714  return self::link(
715  $title,
716  $html,
717  [],
718  wfCgiToArray( $query ),
719  [ 'known', 'noclasses' ]
720  );
721  }
722 
731  protected static function getUploadUrl( $destFile, $query = '' ) {
733  $q = 'wpDestFile=' . Title::castFromLinkTarget( $destFile )->getPartialURL();
734  if ( $query != '' ) {
735  $q .= '&' . $query;
736  }
737 
738  if ( $wgUploadMissingFileUrl ) {
740  }
741 
742  if ( $wgUploadNavigationUrl ) {
744  }
745 
746  $upload = SpecialPage::getTitleFor( 'Upload' );
747 
748  return $upload->getLocalURL( $q );
749  }
750 
760  public static function makeMediaLinkObj( $title, $html = '', $time = false ) {
761  $img = MediaWikiServices::getInstance()->getRepoGroup()->findFile(
762  $title, [ 'time' => $time ]
763  );
764  return self::makeMediaLinkFile( $title, $img, $html );
765  }
766 
779  public static function makeMediaLinkFile( LinkTarget $title, $file, $html = '' ) {
780  if ( $file && $file->exists() ) {
781  $url = $file->getUrl();
782  $class = 'internal';
783  } else {
784  $url = self::getUploadUrl( $title );
785  $class = 'new';
786  }
787 
788  $alt = $title->getText();
789  if ( $html == '' ) {
790  $html = $alt;
791  }
792 
793  $ret = '';
794  $attribs = [
795  'href' => $url,
796  'class' => $class,
797  'title' => $alt
798  ];
799 
800  if ( !Hooks::runner()->onLinkerMakeMediaLinkFile(
801  Title::castFromLinkTarget( $title ), $file, $html, $attribs, $ret )
802  ) {
803  wfDebug( "Hook LinkerMakeMediaLinkFile changed the output of link "
804  . "with url {$url} and text {$html} to {$ret}" );
805  return $ret;
806  }
807 
808  return Html::rawElement( 'a', $attribs, $html );
809  }
810 
821  public static function specialLink( $name, $key = '' ) {
822  if ( $key == '' ) {
823  $key = strtolower( $name );
824  }
825 
826  return self::linkKnown( SpecialPage::getTitleFor( $name ), wfMessage( $key )->escaped() );
827  }
828 
847  public static function makeExternalLink( $url, $text, $escape = true,
848  $linktype = '', $attribs = [], $title = null
849  ) {
850  global $wgTitle;
851  $class = "external";
852  if ( $linktype ) {
853  $class .= " $linktype";
854  }
855  if ( isset( $attribs['class'] ) && $attribs['class'] ) {
856  $class .= " {$attribs['class']}";
857  }
858  $attribs['class'] = $class;
859 
860  if ( $escape ) {
861  $text = htmlspecialchars( $text );
862  }
863 
864  if ( !$title ) {
865  $title = $wgTitle;
866  }
867  $newRel = Parser::getExternalLinkRel( $url, $title );
868  if ( !isset( $attribs['rel'] ) || $attribs['rel'] === '' ) {
869  $attribs['rel'] = $newRel;
870  } elseif ( $newRel !== '' ) {
871  // Merge the rel attributes.
872  $newRels = explode( ' ', $newRel );
873  $oldRels = explode( ' ', $attribs['rel'] );
874  $combined = array_unique( array_merge( $newRels, $oldRels ) );
875  $attribs['rel'] = implode( ' ', $combined );
876  }
877  $link = '';
878  $success = Hooks::runner()->onLinkerMakeExternalLink(
879  $url, $text, $link, $attribs, $linktype );
880  if ( !$success ) {
881  wfDebug( "Hook LinkerMakeExternalLink changed the output of link "
882  . "with url {$url} and text {$text} to {$link}" );
883  return $link;
884  }
885  $attribs['href'] = $url;
886  return Html::rawElement( 'a', $attribs, $text );
887  }
888 
897  public static function userLink( $userId, $userName, $altUserName = false ) {
898  if ( $userName === '' || $userName === false || $userName === null ) {
899  wfDebug( __METHOD__ . ' received an empty username. Are there database errors ' .
900  'that need to be fixed?' );
901  return wfMessage( 'empty-username' )->parse();
902  }
903 
904  $classes = 'mw-userlink';
905  $page = null;
906  if ( $userId == 0 ) {
907  $page = ExternalUserNames::getUserLinkTitle( $userName );
908 
909  if ( ExternalUserNames::isExternal( $userName ) ) {
910  $classes .= ' mw-extuserlink';
911  } elseif ( $altUserName === false ) {
912  $altUserName = IPUtils::prettifyIP( $userName );
913  }
914  $classes .= ' mw-anonuserlink'; // Separate link class for anons (T45179)
915  } else {
916  $page = TitleValue::tryNew( NS_USER, strtr( $userName, ' ', '_' ) );
917  }
918 
919  // Wrap the output with <bdi> tags for directionality isolation
920  $linkText =
921  '<bdi>' . htmlspecialchars( $altUserName !== false ? $altUserName : $userName ) . '</bdi>';
922 
923  return $page
924  ? self::link( $page, $linkText, [ 'class' => $classes ] )
925  : Html::rawElement( 'span', [ 'class' => $classes ], $linkText );
926  }
927 
942  public static function userToolLinks(
943  $userId, $userText, $redContribsWhenNoEdits = false, $flags = 0, $edits = null,
944  $useParentheses = true
945  ) {
946  if ( $userText === '' ) {
947  wfDebug( __METHOD__ . ' received an empty username. Are there database errors ' .
948  'that need to be fixed?' );
949  return ' ' . wfMessage( 'empty-username' )->parse();
950  }
951 
952  global $wgDisableAnonTalk, $wgLang;
953  $talkable = !( $wgDisableAnonTalk && $userId == 0 );
954  $blockable = !( $flags & self::TOOL_LINKS_NOBLOCK );
955  $addEmailLink = $flags & self::TOOL_LINKS_EMAIL && $userId;
956 
957  if ( $userId == 0 && ExternalUserNames::isExternal( $userText ) ) {
958  // No tools for an external user
959  return '';
960  }
961 
962  $items = [];
963  if ( $talkable ) {
964  $items[] = self::userTalkLink( $userId, $userText );
965  }
966  if ( $userId ) {
967  // check if the user has an edit
968  $attribs = [];
969  $attribs['class'] = 'mw-usertoollinks-contribs';
970  if ( $redContribsWhenNoEdits ) {
971  if ( intval( $edits ) === 0 && $edits !== 0 ) {
972  $user = User::newFromId( $userId );
973  $edits = $user->getEditCount();
974  }
975  if ( $edits === 0 ) {
976  $attribs['class'] .= ' new';
977  }
978  }
979  $contribsPage = SpecialPage::getTitleFor( 'Contributions', $userText );
980 
981  $items[] = self::link( $contribsPage, wfMessage( 'contribslink' )->escaped(), $attribs );
982  }
983  $userCanBlock = RequestContext::getMain()->getAuthority()->isAllowed( 'block' );
984  if ( $blockable && $userCanBlock ) {
985  $items[] = self::blockLink( $userId, $userText );
986  }
987 
988  $user = RequestContext::getMain()->getUser();
989  if ( $addEmailLink && $user->canSendEmail() ) {
990  $items[] = self::emailLink( $userId, $userText );
991  }
992 
993  Hooks::runner()->onUserToolLinksEdit( $userId, $userText, $items );
994 
995  if ( !$items ) {
996  return '';
997  }
998 
999  if ( $useParentheses ) {
1000  return wfMessage( 'word-separator' )->escaped()
1001  . '<span class="mw-usertoollinks">'
1002  . wfMessage( 'parentheses' )->rawParams( $wgLang->pipeList( $items ) )->escaped()
1003  . '</span>';
1004  }
1005 
1006  $tools = [];
1007  foreach ( $items as $tool ) {
1008  $tools[] = Html::rawElement( 'span', [], $tool );
1009  }
1010  return ' <span class="mw-usertoollinks mw-changeslist-links">' .
1011  implode( ' ', $tools ) . '</span>';
1012  }
1013 
1023  public static function userToolLinksRedContribs(
1024  $userId, $userText, $edits = null, $useParentheses = true
1025  ) {
1026  return self::userToolLinks( $userId, $userText, true, 0, $edits, $useParentheses );
1027  }
1028 
1035  public static function userTalkLink( $userId, $userText ) {
1036  if ( $userText === '' ) {
1037  wfDebug( __METHOD__ . ' received an empty username. Are there database errors ' .
1038  'that need to be fixed?' );
1039  return wfMessage( 'empty-username' )->parse();
1040  }
1041 
1042  $userTalkPage = TitleValue::tryNew( NS_USER_TALK, strtr( $userText, ' ', '_' ) );
1043  $moreLinkAttribs = [ 'class' => 'mw-usertoollinks-talk' ];
1044  $linkText = wfMessage( 'talkpagelinktext' )->escaped();
1045 
1046  return $userTalkPage
1047  ? self::link( $userTalkPage, $linkText, $moreLinkAttribs )
1048  : Html::rawElement( 'span', $moreLinkAttribs, $linkText );
1049  }
1050 
1057  public static function blockLink( $userId, $userText ) {
1058  if ( $userText === '' ) {
1059  wfDebug( __METHOD__ . ' received an empty username. Are there database errors ' .
1060  'that need to be fixed?' );
1061  return wfMessage( 'empty-username' )->parse();
1062  }
1063 
1064  $blockPage = SpecialPage::getTitleFor( 'Block', $userText );
1065  $moreLinkAttribs = [ 'class' => 'mw-usertoollinks-block' ];
1066 
1067  return self::link( $blockPage,
1068  wfMessage( 'blocklink' )->escaped(),
1069  $moreLinkAttribs
1070  );
1071  }
1072 
1078  public static function emailLink( $userId, $userText ) {
1079  if ( $userText === '' ) {
1080  wfLogWarning( __METHOD__ . ' received an empty username. Are there database errors ' .
1081  'that need to be fixed?' );
1082  return wfMessage( 'empty-username' )->parse();
1083  }
1084 
1085  $emailPage = SpecialPage::getTitleFor( 'Emailuser', $userText );
1086  $moreLinkAttribs = [ 'class' => 'mw-usertoollinks-mail' ];
1087  return self::link( $emailPage,
1088  wfMessage( 'emaillink' )->escaped(),
1089  $moreLinkAttribs
1090  );
1091  }
1092 
1101  public static function revUserLink( $rev, $isPublic = false ) {
1102  // TODO inject authority
1103  $authority = RequestContext::getMain()->getAuthority();
1104 
1105  if ( $rev instanceof Revision ) {
1106  wfDeprecated( __METHOD__ . ' with a Revision object', '1.35' );
1107  $revRecord = $rev->getRevisionRecord();
1108  } else {
1109  $revRecord = $rev;
1110  }
1111 
1112  if ( $revRecord->isDeleted( RevisionRecord::DELETED_USER ) && $isPublic ) {
1113  $link = wfMessage( 'rev-deleted-user' )->escaped();
1114  } elseif ( $revRecord->userCan( RevisionRecord::DELETED_USER, $authority ) ) {
1115  $revUser = $revRecord->getUser( RevisionRecord::FOR_THIS_USER, $authority );
1116  $link = self::userLink(
1117  $revUser ? $revUser->getId() : 0,
1118  $revUser ? $revUser->getName() : ''
1119  );
1120  } else {
1121  $link = wfMessage( 'rev-deleted-user' )->escaped();
1122  }
1123  if ( $revRecord->isDeleted( RevisionRecord::DELETED_USER ) ) {
1124  return '<span class="history-deleted">' . $link . '</span>';
1125  }
1126  return $link;
1127  }
1128 
1138  public static function revUserTools( $rev, $isPublic = false, $useParentheses = true ) {
1139  // TODO inject authority
1140  $authority = RequestContext::getMain()->getAuthority();
1141 
1142  if ( $rev instanceof Revision ) {
1143  wfDeprecated( __METHOD__ . ' with a Revision object', '1.35' );
1144  $revRecord = $rev->getRevisionRecord();
1145  } else {
1146  $revRecord = $rev;
1147  }
1148 
1149  if ( $revRecord->userCan( RevisionRecord::DELETED_USER, $authority ) &&
1150  ( !$revRecord->isDeleted( RevisionRecord::DELETED_USER ) || !$isPublic )
1151  ) {
1152  $revUser = $revRecord->getUser( RevisionRecord::FOR_THIS_USER, $authority );
1153  $userId = $revUser ? $revUser->getId() : 0;
1154  $userText = $revUser ? $revUser->getName() : '';
1155 
1156  if ( $userId || $userText !== '' ) {
1157  $link = self::userLink( $userId, $userText )
1158  . self::userToolLinks( $userId, $userText, false, 0, null,
1159  $useParentheses );
1160  }
1161  }
1162 
1163  if ( !isset( $link ) ) {
1164  $link = wfMessage( 'rev-deleted-user' )->escaped();
1165  }
1166 
1167  if ( $revRecord->isDeleted( RevisionRecord::DELETED_USER ) ) {
1168  return ' <span class="history-deleted mw-userlink">' . $link . '</span>';
1169  }
1170  return $link;
1171  }
1172 
1191  public static function formatComment(
1192  $comment, $title = null, $local = false, $wikiId = null
1193  ) {
1194  # Sanitize text a bit:
1195  $comment = str_replace( "\n", " ", $comment );
1196  # Allow HTML entities (for T15815)
1197  $comment = Sanitizer::escapeHtmlAllowEntities( $comment );
1198 
1199  # Render autocomments and make links:
1200  $comment = self::formatAutocomments( $comment, $title, $local, $wikiId );
1201  return self::formatLinksInComment( $comment, $title, $local, $wikiId );
1202  }
1203 
1221  private static function formatAutocomments(
1222  $comment, $title = null, $local = false, $wikiId = null
1223  ) {
1224  // @todo $append here is something of a hack to preserve the status
1225  // quo. Someone who knows more about bidi and such should decide
1226  // (1) what sane rendering even *is* for an LTR edit summary on an RTL
1227  // wiki, both when autocomments exist and when they don't, and
1228  // (2) what markup will make that actually happen.
1229  $append = '';
1230  $comment = preg_replace_callback(
1231  // To detect the presence of content before or after the
1232  // auto-comment, we use capturing groups inside optional zero-width
1233  // assertions. But older versions of PCRE can't directly make
1234  // zero-width assertions optional, so wrap them in a non-capturing
1235  // group.
1236  '!(?:(?<=(.)))?/\*\s*(.*?)\s*\*/(?:(?=(.)))?!',
1237  static function ( $match ) use ( $title, $local, $wikiId, &$append ) {
1238  global $wgLang;
1239 
1240  // Ensure all match positions are defined
1241  $match += [ '', '', '', '' ];
1242 
1243  $pre = $match[1] !== '';
1244  $auto = $match[2];
1245  $post = $match[3] !== '';
1246  $comment = null;
1247 
1248  Hooks::runner()->onFormatAutocomments(
1249  $comment, $pre, $auto, $post, Title::castFromLinkTarget( $title ), $local,
1250  $wikiId );
1251 
1252  if ( $comment === null ) {
1253  if ( $title ) {
1254  $section = $auto;
1255  # Remove links that a user may have manually put in the autosummary
1256  # This could be improved by copying as much of Parser::stripSectionName as desired.
1257  $section = str_replace( [
1258  '[[:',
1259  '[[',
1260  ']]'
1261  ], '', $section );
1262 
1263  // We don't want any links in the auto text to be linked, but we still
1264  // want to show any [[ ]]
1265  $sectionText = str_replace( '[[', '&#91;[', $auto );
1266 
1267  $section = substr( Parser::guessSectionNameFromStrippedText( $section ), 1 );
1268  if ( $section !== '' ) {
1269  if ( $local ) {
1270  $sectionTitle = new TitleValue( NS_MAIN, '', $section );
1271  } else {
1272  $sectionTitle = $title->createFragmentTarget( $section );
1273  }
1274  $auto = Linker::makeCommentLink(
1275  $sectionTitle,
1276  $wgLang->getArrow() . $wgLang->getDirMark() . $sectionText,
1277  $wikiId,
1278  'noclasses'
1279  );
1280  }
1281  }
1282  if ( $pre ) {
1283  # written summary $presep autocomment (summary /* section */)
1284  $pre = wfMessage( 'autocomment-prefix' )->inContentLanguage()->escaped();
1285  }
1286  if ( $post ) {
1287  # autocomment $postsep written summary (/* section */ summary)
1288  $auto .= wfMessage( 'colon-separator' )->inContentLanguage()->escaped();
1289  }
1290  if ( $auto ) {
1291  $auto = '<span dir="auto"><span class="autocomment">' . $auto . '</span>';
1292  $append .= '</span>';
1293  }
1294  $comment = $pre . $auto;
1295  }
1296  return $comment;
1297  },
1298  $comment
1299  );
1300  return $comment . $append;
1301  }
1302 
1322  public static function formatLinksInComment(
1323  $comment, $title = null, $local = false, $wikiId = null
1324  ) {
1325  return preg_replace_callback(
1326  '/
1327  \[\[
1328  \s*+ # ignore leading whitespace, the *+ quantifier disallows backtracking
1329  :? # ignore optional leading colon
1330  ([^[\]|]+) # 1. link target; page names cannot include [, ] or |
1331  (?:\|
1332  # 2. link text
1333  # Stop matching at ]] without relying on backtracking.
1334  ((?:]?[^\]])*+)
1335  )?
1336  \]\]
1337  ([^[]*) # 3. link trail (the text up until the next link)
1338  /x',
1339  static function ( $match ) use ( $title, $local, $wikiId ) {
1340  $services = MediaWikiServices::getInstance();
1341 
1342  $medians = '(?:';
1343  $medians .= preg_quote(
1344  $services->getNamespaceInfo()->getCanonicalName( NS_MEDIA ), '/' );
1345  $medians .= '|';
1346  $medians .= preg_quote(
1347  $services->getContentLanguage()->getNsText( NS_MEDIA ),
1348  '/'
1349  ) . '):';
1350 
1351  $comment = $match[0];
1352 
1353  # fix up urlencoded title texts (copied from Parser::replaceInternalLinks)
1354  if ( strpos( $match[1], '%' ) !== false ) {
1355  $match[1] = strtr(
1356  rawurldecode( $match[1] ),
1357  [ '<' => '&lt;', '>' => '&gt;' ]
1358  );
1359  }
1360 
1361  # Handle link renaming [[foo|text]] will show link as "text"
1362  if ( $match[2] != "" ) {
1363  $text = $match[2];
1364  } else {
1365  $text = $match[1];
1366  }
1367  $submatch = [];
1368  $thelink = null;
1369  if ( preg_match( '/^' . $medians . '(.*)$/i', $match[1], $submatch ) ) {
1370  # Media link; trail not supported.
1371  $linkRegexp = '/\[\[(.*?)\]\]/';
1372  $title = Title::makeTitleSafe( NS_FILE, $submatch[1] );
1373  if ( $title ) {
1374  $thelink = Linker::makeMediaLinkObj( $title, $text );
1375  }
1376  } else {
1377  # Other kind of link
1378  # Make sure its target is non-empty
1379  if ( isset( $match[1][0] ) && $match[1][0] == ':' ) {
1380  $match[1] = substr( $match[1], 1 );
1381  }
1382  if ( $match[1] !== false && $match[1] !== '' ) {
1383  if ( preg_match(
1384  $services->getContentLanguage()->linkTrail(),
1385  $match[3],
1386  $submatch
1387  ) ) {
1388  $trail = $submatch[1];
1389  } else {
1390  $trail = "";
1391  }
1392  $linkRegexp = '/\[\[(.*?)\]\]' . preg_quote( $trail, '/' ) . '/';
1393  list( $inside, $trail ) = Linker::splitTrail( $trail );
1394 
1395  $linkText = $text;
1396  $linkTarget = Linker::normalizeSubpageLink( $title, $match[1], $linkText );
1397 
1398  try {
1399  $target = $services->getTitleParser()->
1400  parseTitle( $linkTarget );
1401 
1402  if ( $target->getText() == '' && !$target->isExternal()
1403  && !$local && $title
1404  ) {
1405  $target = $title->createFragmentTarget( $target->getFragment() );
1406  }
1407 
1408  $thelink = Linker::makeCommentLink( $target, $linkText . $inside, $wikiId ) . $trail;
1409  } catch ( MalformedTitleException $e ) {
1410  // Fall through
1411  }
1412  }
1413  }
1414  if ( $thelink ) {
1415  // If the link is still valid, go ahead and replace it in!
1416  $comment = preg_replace(
1417  $linkRegexp,
1419  $comment,
1420  1
1421  );
1422  }
1423 
1424  return $comment;
1425  },
1426  $comment
1427  );
1428  }
1429 
1443  public static function makeCommentLink(
1444  LinkTarget $linkTarget, $text, $wikiId = null, $options = []
1445  ) {
1446  if ( $wikiId !== null && !$linkTarget->isExternal() ) {
1447  $link = self::makeExternalLink(
1449  $wikiId,
1450  $linkTarget->getNamespace() === 0
1451  ? $linkTarget->getDBkey()
1452  : MediaWikiServices::getInstance()->getNamespaceInfo()->
1453  getCanonicalName( $linkTarget->getNamespace() ) .
1454  ':' . $linkTarget->getDBkey(),
1455  $linkTarget->getFragment()
1456  ),
1457  $text,
1458  /* escape = */ false // Already escaped
1459  );
1460  } else {
1461  $link = self::link( $linkTarget, $text, [], [], $options );
1462  }
1463 
1464  return $link;
1465  }
1466 
1473  public static function normalizeSubpageLink( $contextTitle, $target, &$text ) {
1474  # Valid link forms:
1475  # Foobar -- normal
1476  # :Foobar -- override special treatment of prefix (images, language links)
1477  # /Foobar -- convert to CurrentPage/Foobar
1478  # /Foobar/ -- convert to CurrentPage/Foobar, strip the initial and final / from text
1479  # ../ -- convert to CurrentPage, from CurrentPage/CurrentSubPage
1480  # ../Foobar -- convert to CurrentPage/Foobar,
1481  # (from CurrentPage/CurrentSubPage)
1482  # ../Foobar/ -- convert to CurrentPage/Foobar, use 'Foobar' as text
1483  # (from CurrentPage/CurrentSubPage)
1484 
1485  $ret = $target; # default return value is no change
1486 
1487  # Some namespaces don't allow subpages,
1488  # so only perform processing if subpages are allowed
1489  if (
1490  $contextTitle && MediaWikiServices::getInstance()->getNamespaceInfo()->
1491  hasSubpages( $contextTitle->getNamespace() )
1492  ) {
1493  $hash = strpos( $target, '#' );
1494  if ( $hash !== false ) {
1495  $suffix = substr( $target, $hash );
1496  $target = substr( $target, 0, $hash );
1497  } else {
1498  $suffix = '';
1499  }
1500  # T9425
1501  $target = trim( $target );
1502  $contextPrefixedText = MediaWikiServices::getInstance()->getTitleFormatter()->
1503  getPrefixedText( $contextTitle );
1504  # Look at the first character
1505  if ( $target != '' && $target[0] === '/' ) {
1506  # / at end means we don't want the slash to be shown
1507  $m = [];
1508  $trailingSlashes = preg_match_all( '%(/+)$%', $target, $m );
1509  if ( $trailingSlashes ) {
1510  $noslash = $target = substr( $target, 1, -strlen( $m[0][0] ) );
1511  } else {
1512  $noslash = substr( $target, 1 );
1513  }
1514 
1515  $ret = $contextPrefixedText . '/' . trim( $noslash ) . $suffix;
1516  if ( $text === '' ) {
1517  $text = $target . $suffix;
1518  } # this might be changed for ugliness reasons
1519  } else {
1520  # check for .. subpage backlinks
1521  $dotdotcount = 0;
1522  $nodotdot = $target;
1523  while ( strncmp( $nodotdot, "../", 3 ) == 0 ) {
1524  ++$dotdotcount;
1525  $nodotdot = substr( $nodotdot, 3 );
1526  }
1527  if ( $dotdotcount > 0 ) {
1528  $exploded = explode( '/', $contextPrefixedText );
1529  if ( count( $exploded ) > $dotdotcount ) { # not allowed to go below top level page
1530  $ret = implode( '/', array_slice( $exploded, 0, -$dotdotcount ) );
1531  # / at the end means don't show full path
1532  if ( substr( $nodotdot, -1, 1 ) === '/' ) {
1533  $nodotdot = rtrim( $nodotdot, '/' );
1534  if ( $text === '' ) {
1535  $text = $nodotdot . $suffix;
1536  }
1537  }
1538  $nodotdot = trim( $nodotdot );
1539  if ( $nodotdot != '' ) {
1540  $ret .= '/' . $nodotdot;
1541  }
1542  $ret .= $suffix;
1543  }
1544  }
1545  }
1546  }
1547 
1548  return $ret;
1549  }
1550 
1566  public static function commentBlock(
1567  $comment, $title = null, $local = false, $wikiId = null, $useParentheses = true
1568  ) {
1569  // '*' used to be the comment inserted by the software way back
1570  // in antiquity in case none was provided, here for backwards
1571  // compatibility, acc. to brion -√¶var
1572  if ( $comment == '' || $comment == '*' ) {
1573  return '';
1574  }
1575  $formatted = self::formatComment( $comment, $title, $local, $wikiId );
1576  if ( $useParentheses ) {
1577  $formatted = wfMessage( 'parentheses' )->rawParams( $formatted )->escaped();
1578  $classNames = 'comment';
1579  } else {
1580  $classNames = 'comment comment--without-parentheses';
1581  }
1582  return " <span class=\"$classNames\">$formatted</span>";
1583  }
1584 
1597  public static function revComment( $rev, $local = false, $isPublic = false,
1598  $useParentheses = true
1599  ) {
1600  // TODO inject authority
1601  $authority = RequestContext::getMain()->getAuthority();
1602 
1603  if ( $rev instanceof Revision ) {
1604  wfDeprecated( __METHOD__ . ' with a Revision object', '1.35' );
1605  $revRecord = $rev->getRevisionRecord();
1606  } else {
1607  $revRecord = $rev;
1608  }
1609 
1610  if ( $revRecord->getComment( RevisionRecord::RAW ) === null ) {
1611  return "";
1612  }
1613  if ( $revRecord->isDeleted( RevisionRecord::DELETED_COMMENT ) && $isPublic ) {
1614  $block = " <span class=\"comment\">" . wfMessage( 'rev-deleted-comment' )->escaped() . "</span>";
1615  } elseif ( $revRecord->userCan( RevisionRecord::DELETED_COMMENT, $authority ) ) {
1616  $comment = $revRecord->getComment( RevisionRecord::FOR_THIS_USER, $authority );
1617  $block = self::commentBlock(
1618  $comment ? $comment->text : null,
1619  $revRecord->getPageAsLinkTarget(),
1620  $local,
1621  null,
1622  $useParentheses
1623  );
1624  } else {
1625  $block = " <span class=\"comment\">" . wfMessage( 'rev-deleted-comment' )->escaped() . "</span>";
1626  }
1627  if ( $revRecord->isDeleted( RevisionRecord::DELETED_COMMENT ) ) {
1628  return " <span class=\"history-deleted comment\">$block</span>";
1629  }
1630  return $block;
1631  }
1632 
1638  public static function formatRevisionSize( $size ) {
1639  if ( $size == 0 ) {
1640  $stxt = wfMessage( 'historyempty' )->escaped();
1641  } else {
1642  $stxt = wfMessage( 'nbytes' )->numParams( $size )->escaped();
1643  }
1644  return "<span class=\"history-size mw-diff-bytes\" data-mw-bytes=\"$size\">$stxt</span>";
1645  }
1646 
1653  public static function tocIndent() {
1654  return "\n<ul>\n";
1655  }
1656 
1664  public static function tocUnindent( $level ) {
1665  return "</li>\n" . str_repeat( "</ul>\n</li>\n", $level > 0 ? $level : 0 );
1666  }
1667 
1679  public static function tocLine( $anchor, $tocline, $tocnumber, $level, $sectionIndex = false ) {
1680  $classes = "toclevel-$level";
1681  if ( $sectionIndex !== false ) {
1682  $classes .= " tocsection-$sectionIndex";
1683  }
1684 
1685  // <li class="$classes"><a href="#$anchor"><span class="tocnumber">
1686  // $tocnumber</span> <span class="toctext">$tocline</span></a>
1687  return Html::openElement( 'li', [ 'class' => $classes ] )
1688  . Html::rawElement( 'a',
1689  [ 'href' => "#$anchor" ],
1690  Html::element( 'span', [ 'class' => 'tocnumber' ], $tocnumber )
1691  . ' '
1692  . Html::rawElement( 'span', [ 'class' => 'toctext' ], $tocline )
1693  );
1694  }
1695 
1703  public static function tocLineEnd() {
1704  return "</li>\n";
1705  }
1706 
1715  public static function tocList( $toc, Language $lang = null ) {
1716  $lang = $lang ?? RequestContext::getMain()->getLanguage();
1717 
1718  $title = wfMessage( 'toc' )->inLanguage( $lang )->escaped();
1719 
1720  return '<div id="toc" class="toc" role="navigation" aria-labelledby="mw-toc-heading">'
1721  . Html::element( 'input', [
1722  'type' => 'checkbox',
1723  'role' => 'button',
1724  'id' => 'toctogglecheckbox',
1725  'class' => 'toctogglecheckbox',
1726  'style' => 'display:none',
1727  ] )
1728  . Html::openElement( 'div', [
1729  'class' => 'toctitle',
1730  'lang' => $lang->getHtmlCode(),
1731  'dir' => $lang->getDir(),
1732  ] )
1733  . '<h2 id="mw-toc-heading">' . $title . '</h2>'
1734  . '<span class="toctogglespan">'
1735  . Html::label( '', 'toctogglecheckbox', [
1736  'class' => 'toctogglelabel',
1737  ] )
1738  . '</span>'
1739  . "</div>\n"
1740  . $toc
1741  . "</ul>\n</div>\n";
1742  }
1743 
1752  public static function generateTOC( $tree, Language $lang = null ) {
1753  $toc = '';
1754  $lastLevel = 0;
1755  foreach ( $tree as $section ) {
1756  if ( $section['toclevel'] > $lastLevel ) {
1757  $toc .= self::tocIndent();
1758  } elseif ( $section['toclevel'] < $lastLevel ) {
1759  $toc .= self::tocUnindent(
1760  $lastLevel - $section['toclevel'] );
1761  } else {
1762  $toc .= self::tocLineEnd();
1763  }
1764 
1765  $toc .= self::tocLine( $section['anchor'],
1766  $section['line'], $section['number'],
1767  $section['toclevel'], $section['index'] );
1768  $lastLevel = $section['toclevel'];
1769  }
1770  $toc .= self::tocLineEnd();
1771  return self::tocList( $toc, $lang );
1772  }
1773 
1790  public static function makeHeadline( $level, $attribs, $anchor, $html,
1791  $link, $fallbackAnchor = false
1792  ) {
1793  $anchorEscaped = htmlspecialchars( $anchor );
1794  $fallback = '';
1795  if ( $fallbackAnchor !== false && $fallbackAnchor !== $anchor ) {
1796  $fallbackAnchor = htmlspecialchars( $fallbackAnchor );
1797  $fallback = "<span id=\"$fallbackAnchor\"></span>";
1798  }
1799  return "<h$level$attribs"
1800  . "$fallback<span class=\"mw-headline\" id=\"$anchorEscaped\">$html</span>"
1801  . $link
1802  . "</h$level>";
1803  }
1804 
1811  public static function splitTrail( $trail ) {
1812  $regex = MediaWikiServices::getInstance()->getContentLanguage()->linkTrail();
1813  $inside = '';
1814  if ( $trail !== '' && preg_match( $regex, $trail, $m ) ) {
1815  list( , $inside, $trail ) = $m;
1816  }
1817  return [ $inside, $trail ];
1818  }
1819 
1849  public static function generateRollback( $rev, IContextSource $context = null,
1850  $options = [ 'verify' ]
1851  ) {
1852  if ( $rev instanceof Revision ) {
1853  wfDeprecated( __METHOD__ . ' with a Revision object', '1.35' );
1854  $revRecord = $rev->getRevisionRecord();
1855  } else {
1856  $revRecord = $rev;
1857  }
1858 
1859  if ( $context === null ) {
1860  $context = RequestContext::getMain();
1861  }
1862 
1863  $editCount = false;
1864  if ( in_array( 'verify', $options, true ) ) {
1865  $editCount = self::getRollbackEditCount( $revRecord, true );
1866  if ( $editCount === false ) {
1867  return '';
1868  }
1869  }
1870 
1871  $inner = self::buildRollbackLink( $revRecord, $context, $editCount );
1872 
1873  // Allow extensions to modify the rollback link.
1874  // Abort further execution if the extension wants full control over the link.
1875  if ( !Hooks::runner()->onLinkerGenerateRollbackLink(
1876  $revRecord, $context, $options, $inner ) ) {
1877  return $inner;
1878  }
1879 
1880  if ( !in_array( 'noBrackets', $options, true ) ) {
1881  $inner = $context->msg( 'brackets' )->rawParams( $inner )->escaped();
1882  }
1883 
1884  if ( $context->getUser()->getBoolOption( 'showrollbackconfirmation' ) ) {
1885  $stats = MediaWikiServices::getInstance()->getStatsdDataFactory();
1886  $stats->increment( 'rollbackconfirmation.event.load' );
1887  $context->getOutput()->addModules( 'mediawiki.misc-authed-curate' );
1888  }
1889 
1890  return '<span class="mw-rollback-link">' . $inner . '</span>';
1891  }
1892 
1911  public static function getRollbackEditCount( $rev, $verify ) {
1912  global $wgShowRollbackEditCount;
1913 
1914  if ( $rev instanceof Revision ) {
1915  wfDeprecated( __METHOD__ . ' with a Revision object', '1.35' );
1916  $revRecord = $rev->getRevisionRecord();
1917  } else {
1918  $revRecord = $rev;
1919  }
1920 
1921  if ( !is_int( $wgShowRollbackEditCount ) || !$wgShowRollbackEditCount > 0 ) {
1922  // Nothing has happened, indicate this by returning 'null'
1923  return null;
1924  }
1925 
1926  $dbr = wfGetDB( DB_REPLICA );
1927 
1928  // Up to the value of $wgShowRollbackEditCount revisions are counted
1929  $revQuery = MediaWikiServices::getInstance()->getRevisionStore()->getQueryInfo();
1930  $res = $dbr->select(
1931  $revQuery['tables'],
1932  [ 'rev_user_text' => $revQuery['fields']['rev_user_text'], 'rev_deleted' ],
1933  [ 'rev_page' => $revRecord->getPageId() ],
1934  __METHOD__,
1935  [
1936  'USE INDEX' => [ 'revision' => 'page_timestamp' ],
1937  'ORDER BY' => 'rev_timestamp DESC',
1938  'LIMIT' => $wgShowRollbackEditCount + 1
1939  ],
1940  $revQuery['joins']
1941  );
1942 
1943  $revUser = $revRecord->getUser( RevisionRecord::RAW );
1944  $revUserText = $revUser ? $revUser->getName() : '';
1945 
1946  $editCount = 0;
1947  $moreRevs = false;
1948  foreach ( $res as $row ) {
1949  if ( $row->rev_user_text != $revUserText ) {
1950  if ( $verify &&
1951  ( $row->rev_deleted & RevisionRecord::DELETED_TEXT
1952  || $row->rev_deleted & RevisionRecord::DELETED_USER
1953  ) ) {
1954  // If the user or the text of the revision we might rollback
1955  // to is deleted in some way we can't rollback. Similar to
1956  // the sanity checks in WikiPage::commitRollback.
1957  return false;
1958  }
1959  $moreRevs = true;
1960  break;
1961  }
1962  $editCount++;
1963  }
1964 
1965  if ( $verify && $editCount <= $wgShowRollbackEditCount && !$moreRevs ) {
1966  // We didn't find at least $wgShowRollbackEditCount revisions made by the current user
1967  // and there weren't any other revisions. That means that the current user is the only
1968  // editor, so we can't rollback
1969  return false;
1970  }
1971  return $editCount;
1972  }
1973 
1988  public static function buildRollbackLink( $rev, IContextSource $context = null,
1989  $editCount = false
1990  ) {
1992 
1993  if ( $rev instanceof Revision ) {
1994  wfDeprecated( __METHOD__ . ' with a Revision object', '1.35' );
1995  $revRecord = $rev->getRevisionRecord();
1996  } else {
1997  $revRecord = $rev;
1998  }
1999 
2000  // To config which pages are affected by miser mode
2001  $disableRollbackEditCountSpecialPage = [ 'Recentchanges', 'Watchlist' ];
2002 
2003  if ( $context === null ) {
2004  $context = RequestContext::getMain();
2005  }
2006 
2007  $title = $revRecord->getPageAsLinkTarget();
2008  $revUser = $revRecord->getUser();
2009  $revUserText = $revUser ? $revUser->getName() : '';
2010 
2011  $query = [
2012  'action' => 'rollback',
2013  'from' => $revUserText,
2014  'token' => $context->getUser()->getEditToken( 'rollback' ),
2015  ];
2016 
2017  $attrs = [
2018  'data-mw' => 'interface',
2019  'title' => $context->msg( 'tooltip-rollback' )->text()
2020  ];
2021 
2022  $options = [ 'known', 'noclasses' ];
2023 
2024  if ( $context->getRequest()->getBool( 'bot' ) ) {
2025  // T17999
2026  $query['hidediff'] = '1';
2027  $query['bot'] = '1';
2028  }
2029 
2030  $disableRollbackEditCount = false;
2031  if ( $wgMiserMode ) {
2032  foreach ( $disableRollbackEditCountSpecialPage as $specialPage ) {
2033  if ( $context->getTitle()->isSpecial( $specialPage ) ) {
2034  $disableRollbackEditCount = true;
2035  break;
2036  }
2037  }
2038  }
2039 
2040  if ( !$disableRollbackEditCount
2041  && is_int( $wgShowRollbackEditCount )
2043  ) {
2044  if ( !is_numeric( $editCount ) ) {
2045  $editCount = self::getRollbackEditCount( $revRecord, false );
2046  }
2047 
2048  if ( $editCount > $wgShowRollbackEditCount ) {
2049  $html = $context->msg( 'rollbacklinkcount-morethan' )
2050  ->numParams( $wgShowRollbackEditCount )->parse();
2051  } else {
2052  $html = $context->msg( 'rollbacklinkcount' )->numParams( $editCount )->parse();
2053  }
2054 
2055  return self::link( $title, $html, $attrs, $query, $options );
2056  }
2057 
2058  $html = $context->msg( 'rollbacklink' )->escaped();
2059  return self::link( $title, $html, $attrs, $query, $options );
2060  }
2061 
2070  public static function formatHiddenCategories( $hiddencats ) {
2071  $outText = '';
2072  if ( count( $hiddencats ) > 0 ) {
2073  # Construct the HTML
2074  $outText = '<div class="mw-hiddenCategoriesExplanation">';
2075  $outText .= wfMessage( 'hiddencategories' )->numParams( count( $hiddencats ) )->parseAsBlock();
2076  $outText .= "</div><ul>\n";
2077 
2078  foreach ( $hiddencats as $titleObj ) {
2079  # If it's hidden, it must exist - no need to check with a LinkBatch
2080  $outText .= '<li>'
2081  . self::link( $titleObj, null, [], [], 'known' )
2082  . "</li>\n";
2083  }
2084  $outText .= '</ul>';
2085  }
2086  return $outText;
2087  }
2088 
2105  public static function titleAttrib( $name, $options = null, array $msgParams = [] ) {
2106  $message = wfMessage( "tooltip-$name", $msgParams );
2107  if ( !$message->exists() ) {
2108  $tooltip = false;
2109  } else {
2110  $tooltip = $message->text();
2111  # Compatibility: formerly some tooltips had [alt-.] hardcoded
2112  $tooltip = preg_replace( "/ ?\[alt-.\]$/", '', $tooltip );
2113  # Message equal to '-' means suppress it.
2114  if ( $tooltip == '-' ) {
2115  $tooltip = false;
2116  }
2117  }
2118 
2119  $options = (array)$options;
2120 
2121  if ( in_array( 'nonexisting', $options ) ) {
2122  $tooltip = wfMessage( 'red-link-title', $tooltip ?: '' )->text();
2123  }
2124  if ( in_array( 'withaccess', $options ) ) {
2125  $accesskey = self::accesskey( $name );
2126  if ( $accesskey !== false ) {
2127  // Should be build the same as in jquery.accessKeyLabel.js
2128  if ( $tooltip === false || $tooltip === '' ) {
2129  $tooltip = wfMessage( 'brackets', $accesskey )->text();
2130  } else {
2131  $tooltip .= wfMessage( 'word-separator' )->text();
2132  $tooltip .= wfMessage( 'brackets', $accesskey )->text();
2133  }
2134  }
2135  }
2136 
2137  return $tooltip;
2138  }
2139 
2140  public static $accesskeycache;
2141 
2153  public static function accesskey( $name ) {
2154  if ( isset( self::$accesskeycache[$name] ) ) {
2155  return self::$accesskeycache[$name];
2156  }
2157 
2158  $message = wfMessage( "accesskey-$name" );
2159 
2160  if ( !$message->exists() ) {
2161  $accesskey = false;
2162  } else {
2163  $accesskey = $message->plain();
2164  if ( $accesskey === '' || $accesskey === '-' ) {
2165  # @todo FIXME: Per standard MW behavior, a value of '-' means to suppress the
2166  # attribute, but this is broken for accesskey: that might be a useful
2167  # value.
2168  $accesskey = false;
2169  }
2170  }
2171 
2172  self::$accesskeycache[$name] = $accesskey;
2173  return self::$accesskeycache[$name];
2174  }
2175 
2190  public static function getRevDeleteLink( Authority $performer, $rev, LinkTarget $title ) {
2191  if ( $rev instanceof Revision ) {
2192  wfDeprecated( __METHOD__ . ' with a Revision object', '1.35' );
2193  $revRecord = $rev->getRevisionRecord();
2194  } else {
2195  $revRecord = $rev;
2196  }
2197 
2198  $canHide = $performer->isAllowed( 'deleterevision' );
2199  $canHideHistory = $performer->isAllowed( 'deletedhistory' );
2200  if ( !$canHide && !( $revRecord->getVisibility() && $canHideHistory ) ) {
2201  return '';
2202  }
2203 
2204  if ( !$revRecord->userCan( RevisionRecord::DELETED_RESTRICTED, $performer ) ) {
2205  return self::revDeleteLinkDisabled( $canHide ); // revision was hidden from sysops
2206  }
2207  $prefixedDbKey = MediaWikiServices::getInstance()->getTitleFormatter()->
2208  getPrefixedDBkey( $title );
2209  if ( $revRecord->getId() ) {
2210  // RevDelete links using revision ID are stable across
2211  // page deletion and undeletion; use when possible.
2212  $query = [
2213  'type' => 'revision',
2214  'target' => $prefixedDbKey,
2215  'ids' => $revRecord->getId()
2216  ];
2217  } else {
2218  // Older deleted entries didn't save a revision ID.
2219  // We have to refer to these by timestamp, ick!
2220  $query = [
2221  'type' => 'archive',
2222  'target' => $prefixedDbKey,
2223  'ids' => $revRecord->getTimestamp()
2224  ];
2225  }
2226  return self::revDeleteLink(
2227  $query,
2228  $revRecord->isDeleted( RevisionRecord::DELETED_RESTRICTED ),
2229  $canHide
2230  );
2231  }
2232 
2243  public static function revDeleteLink( $query = [], $restricted = false, $delete = true ) {
2244  $sp = SpecialPage::getTitleFor( 'Revisiondelete' );
2245  $msgKey = $delete ? 'rev-delundel' : 'rev-showdeleted';
2246  $html = wfMessage( $msgKey )->escaped();
2247  $tag = $restricted ? 'strong' : 'span';
2248  $link = self::link( $sp, $html, [], $query, [ 'known', 'noclasses' ] );
2249  return Xml::tags(
2250  $tag,
2251  [ 'class' => 'mw-revdelundel-link' ],
2252  wfMessage( 'parentheses' )->rawParams( $link )->escaped()
2253  );
2254  }
2255 
2265  public static function revDeleteLinkDisabled( $delete = true ) {
2266  $msgKey = $delete ? 'rev-delundel' : 'rev-showdeleted';
2267  $html = wfMessage( $msgKey )->escaped();
2268  $htmlParentheses = wfMessage( 'parentheses' )->rawParams( $html )->escaped();
2269  return Xml::tags( 'span', [ 'class' => 'mw-revdelundel-link' ], $htmlParentheses );
2270  }
2271 
2284  public static function tooltipAndAccesskeyAttribs(
2285  $name,
2286  array $msgParams = [],
2287  $options = null
2288  ) {
2289  $options = (array)$options;
2290  $options[] = 'withaccess';
2291  $tooltipTitle = $name;
2292 
2293  // @since 1.35 - add a WatchlistExpiry feature flag to show new watchstar tooltip message
2294  $skin = RequestContext::getMain()->getSkin();
2295  $isWatchlistExpiryEnabled = $skin->getConfig()->get( 'WatchlistExpiry' );
2296  if ( $name === 'ca-unwatch' && $isWatchlistExpiryEnabled ) {
2297  $watchStore = MediaWikiServices::getInstance()->getWatchedItemStore();
2298  $watchedItem = $watchStore->getWatchedItem( $skin->getUser(),
2299  $skin->getRelevantTitle() );
2300  if ( $watchedItem instanceof WatchedItem && $watchedItem->getExpiry() !== null ) {
2301  $diffInDays = $watchedItem->getExpiryInDays();
2302 
2303  if ( $diffInDays ) {
2304  $msgParams = [ $diffInDays ];
2305  // Resolves to tooltip-ca-unwatch-expiring message
2306  $tooltipTitle = 'ca-unwatch-expiring';
2307  } else { // Resolves to tooltip-ca-unwatch-expiring-hours message
2308  $tooltipTitle = 'ca-unwatch-expiring-hours';
2309  }
2310 
2311  }
2312  }
2313 
2314  $attribs = [
2315  'title' => self::titleAttrib( $tooltipTitle, $options, $msgParams ),
2316  'accesskey' => self::accesskey( $name )
2317  ];
2318  if ( $attribs['title'] === false ) {
2319  unset( $attribs['title'] );
2320  }
2321  if ( $attribs['accesskey'] === false ) {
2322  unset( $attribs['accesskey'] );
2323  }
2324  return $attribs;
2325  }
2326 
2334  public static function tooltip( $name, $options = null ) {
2335  $tooltip = self::titleAttrib( $name, $options );
2336  if ( $tooltip === false ) {
2337  return '';
2338  }
2339  return Xml::expandAttributes( [
2340  'title' => $tooltip
2341  ] );
2342  }
2343 
2344 }
Linker\getImageLinkMTOParams
static getImageLinkMTOParams( $frameParams, $query='', $parser=null)
Get the link parameters for MediaTransformOutput::toHtml() from given frame parameters supplied by th...
Definition: Linker.php:452
User\newFromId
static newFromId( $id)
Static factory method for creation from a given user ID.
Definition: User.php:627
HtmlArmor
Marks HTML that shouldn't be escaped.
Definition: HtmlArmor.php:30
$wgThumbLimits
$wgThumbLimits
Adjust thumbnails on image pages according to a user setting.
Definition: DefaultSettings.php:1612
Linker\formatLinksInComment
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:1322
Linker
Some internal bits split of from Skin.php.
Definition: Linker.php:37
Revision\RevisionRecord
Page revision base class.
Definition: RevisionRecord.php:47
Linker\TOOL_LINKS_NOBLOCK
const TOOL_LINKS_NOBLOCK
Flags for userToolLinks()
Definition: Linker.php:41
Linker\userTalkLink
static userTalkLink( $userId, $userText)
Definition: Linker.php:1035
Xml\expandAttributes
static expandAttributes( $attribs)
Given an array of ('attributename' => 'value'), it generates the code to set the XML attributes : att...
Definition: Xml.php:67
Linker\emailLink
static emailLink( $userId, $userText)
Definition: Linker.php:1078
Linker\generateTOC
static generateTOC( $tree, Language $lang=null)
Generate a table of contents from a section tree.
Definition: Linker.php:1752
$wgResponsiveImages
$wgResponsiveImages
Generate and use thumbnails suitable for screens with 1.5 and 2.0 pixel densities.
Definition: DefaultSettings.php:1730
Linker\revUserLink
static revUserLink( $rev, $isPublic=false)
Generate a user link if the current user is allowed to view it.
Definition: Linker.php:1101
Linker\userLink
static userLink( $userId, $userName, $altUserName=false)
Make user link (or user contributions for unregistered users)
Definition: Linker.php:897
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:173
$lang
if(!isset( $args[0])) $lang
Definition: testCompression.php:37
Linker\makeSelfLinkObj
static makeSelfLinkObj( $nt, $html='', $query='', $trail='', $prefix='')
Make appropriate markup for a link to the current article.
Definition: Linker.php:165
Linker\getUploadUrl
static getUploadUrl( $destFile, $query='')
Get the URL to upload a certain file.
Definition: Linker.php:731
Linker\tocIndent
static tocIndent()
Add another level to the Table of Contents.
Definition: Linker.php:1653
$fallback
$fallback
Definition: MessagesAb.php:11
$wgSVGMaxSize
$wgSVGMaxSize
Don't scale a SVG larger than this.
Definition: DefaultSettings.php:1347
Linker\userToolLinks
static userToolLinks( $userId, $userText, $redContribsWhenNoEdits=false, $flags=0, $edits=null, $useParentheses=true)
Generate standard user tool links (talk, contributions, block link, etc.)
Definition: Linker.php:942
WikiMap\getForeignURL
static getForeignURL( $wikiID, $page, $fragmentId=null)
Convenience to get a url to a page on a foreign wiki.
Definition: WikiMap.php:171
Linker\fnamePart
static fnamePart( $url)
Returns the filename part of an url.
Definition: Linker.php:224
Linker\specialLink
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:821
Linker\revComment
static revComment( $rev, $local=false, $isPublic=false, $useParentheses=true)
Wrap and format the given revision's comment block, if the current user is allowed to view it.
Definition: Linker.php:1597
StringUtils\escapeRegexReplacement
static escapeRegexReplacement( $string)
Escape a string to make it suitable for inclusion in a preg_replace() replacement parameter.
Definition: StringUtils.php:314
$file
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.
Definition: router.php:42
Parser\getTargetLanguage
getTargetLanguage()
Get the target language for the content being parsed.
Definition: Parser.php:1112
Linker\linkKnown
static linkKnown( $target, $html=null, $customAttribs=[], $query=[], $options=[ 'known'])
Identical to link(), except $options defaults to 'known'.
Definition: Linker.php:143
wfMessage
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
Definition: GlobalFunctions.php:1231
SpecialPage\getTitleFor
static getTitleFor( $name, $subpage=false, $fragment='')
Get a localised Title object for a specified special page name If you don't need a full Title object,...
Definition: SpecialPage.php:107
Linker\processResponsiveImages
static processResponsiveImages( $file, $thumb, $hp)
Process responsive images: add 1.5x and 2x subimages to the thumbnail, where applicable.
Definition: Linker.php:641
wfLogWarning
wfLogWarning( $msg, $callerOffset=1, $level=E_USER_WARNING)
Send a warning as a PHP error and the debug log.
Definition: GlobalFunctions.php:1094
$success
$success
Definition: NoLocalSettings.php:42
Linker\getInvalidTitleDescription
static getInvalidTitleDescription(IContextSource $context, $namespace, $title)
Get a message saying that an invalid title was encountered.
Definition: Linker.php:189
Parser\getExternalLinkRel
static getExternalLinkRel( $url=false, LinkTarget $title=null)
Get the rel attribute for a particular external link.
Definition: Parser.php:2185
MediaWiki\Linker\LinkTarget\isExternal
isExternal()
Whether this LinkTarget has an interwiki component.
$res
$res
Definition: testCompression.php:57
Linker\makeThumbLinkObj
static makeThumbLinkObj(LinkTarget $title, $file, $label='', $alt='', $align='right', $params=[], $framed=false, $manualthumb="")
Make HTML for a thumbnail including image, border and caption.
Definition: Linker.php:492
$revQuery
$revQuery
Definition: testCompression.php:56
Linker\tocLine
static tocLine( $anchor, $tocline, $tocnumber, $level, $sectionIndex=false)
parameter level defines if we are on an indentation level
Definition: Linker.php:1679
$wgLang
$wgLang
Definition: Setup.php:782
Linker\tocList
static tocList( $toc, Language $lang=null)
Wraps the TOC in a div with ARIA navigation role and provides the hide/collapse JavaScript.
Definition: Linker.php:1715
Linker\formatHiddenCategories
static formatHiddenCategories( $hiddencats)
Returns HTML for the "hidden categories on this page" list.
Definition: Linker.php:2070
Linker\makeThumbLink2
static makeThumbLink2(LinkTarget $title, $file, $frameParams=[], $handlerParams=[], $time=false, $query="")
Definition: Linker.php:518
Linker\tooltipAndAccesskeyAttribs
static tooltipAndAccesskeyAttribs( $name, array $msgParams=[], $options=null)
Returns the attributes for the tooltip and access key.
Definition: Linker.php:2284
NS_MAIN
const NS_MAIN
Definition: Defines.php:64
$dbr
$dbr
Definition: testCompression.php:54
wfAppendQuery
wfAppendQuery( $url, $query)
Append a query string to an existing URL, which may or may not already have query string parameters a...
Definition: GlobalFunctions.php:443
Revision
Definition: Revision.php:40
WatchedItem\getExpiry
getExpiry(?int $style=TS_MW)
When the watched item will expire.
Definition: WatchedItem.php:147
Linker\$accesskeycache
static $accesskeycache
Definition: Linker.php:2140
MediaWiki\Linker\LinkTarget\getNamespace
getNamespace()
Get the namespace index.
Linker\makeMediaLinkObj
static makeMediaLinkObj( $title, $html='', $time=false)
Create a direct link to a given uploaded file.
Definition: Linker.php:760
wfDeprecated
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that $function is deprecated.
Definition: GlobalFunctions.php:1034
MessageLocalizer\msg
msg( $key,... $params)
This is the method for getting translated interface messages.
Linker\generateRollback
static generateRollback( $rev, IContextSource $context=null, $options=[ 'verify'])
Generate a rollback link for a given revision.
Definition: Linker.php:1849
TitleValue\tryNew
static tryNew( $namespace, $title, $fragment='', $interwiki='')
Constructs a TitleValue, or returns null if the parameters are not valid.
Definition: TitleValue.php:94
Linker\formatAutocomments
static formatAutocomments( $comment, $title=null, $local=false, $wikiId=null)
Converts autogenerated comments in edit summaries into section links.
Definition: Linker.php:1221
wfGetDB
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:2467
Linker\getRollbackEditCount
static getRollbackEditCount( $rev, $verify)
This function will return the number of revisions which a rollback would revert and,...
Definition: Linker.php:1911
Linker\makeHeadline
static makeHeadline( $level, $attribs, $anchor, $html, $link, $fallbackAnchor=false)
Create a headline for content.
Definition: Linker.php:1790
Linker\tocLineEnd
static tocLineEnd()
End a Table Of Contents line.
Definition: Linker.php:1703
wfCgiToArray
wfCgiToArray( $query)
This is the logical opposite of wfArrayToCgi(): it accepts a query string as its argument and returns...
Definition: GlobalFunctions.php:396
Linker\revDeleteLinkDisabled
static revDeleteLinkDisabled( $delete=true)
Creates a dead (show/hide) link for deleting revisions/log entries.
Definition: Linker.php:2265
$title
$title
Definition: testCompression.php:38
Linker\makeExternalLink
static makeExternalLink( $url, $text, $escape=true, $linktype='', $attribs=[], $title=null)
Make an external link.
Definition: Linker.php:847
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
Linker\getRevDeleteLink
static getRevDeleteLink(Authority $performer, $rev, LinkTarget $title)
Get a revision-deletion link, or disabled link, or nothing, depending on user permissions & the setti...
Definition: Linker.php:2190
Linker\revUserTools
static revUserTools( $rev, $isPublic=false, $useParentheses=true)
Generate a user tool link cluster if the current user is allowed to view it.
Definition: Linker.php:1138
Linker\makeCommentLink
static makeCommentLink(LinkTarget $linkTarget, $text, $wikiId=null, $options=[])
Generates a link to the given LinkTarget.
Definition: Linker.php:1443
Html\label
static label( $label, $id, array $attribs=[])
Convenience function for generating a label for inputs.
Definition: Html.php:789
wfDebug
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
Definition: GlobalFunctions.php:915
$wgTitle
$wgTitle
Definition: Setup.php:800
Linker\splitTrail
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:1811
MediaWiki\Permissions\Authority
Definition: Authority.php:30
$wgShowRollbackEditCount
$wgShowRollbackEditCount
The $wgShowRollbackEditCount variable is used to show how many edits can be rolled back.
Definition: DefaultSettings.php:3764
Title\makeTitleSafe
static makeTitleSafe( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:652
Linker\blockLink
static blockLink( $userId, $userText)
Definition: Linker.php:1057
$s
foreach( $mmfl['setupFiles'] as $fileName) if( $queue) if(empty( $mmfl['quiet'])) $s
Definition: mergeMessageFileList.php:206
Linker\normaliseSpecialPage
static normaliseSpecialPage(LinkTarget $target)
Definition: Linker.php:210
ExternalUserNames\getUserLinkTitle
static getUserLinkTitle( $userName)
Get a target Title to link a username.
Definition: ExternalUserNames.php:62
NS_MEDIA
const NS_MEDIA
Definition: Defines.php:52
Linker\TOOL_LINKS_EMAIL
const TOOL_LINKS_EMAIL
Definition: Linker.php:42
Linker\commentBlock
static commentBlock( $comment, $title=null, $local=false, $wikiId=null, $useParentheses=true)
Wrap a comment in standard punctuation and formatting if it's non-empty, otherwise return empty strin...
Definition: Linker.php:1566
Linker\userToolLinksRedContribs
static userToolLinksRedContribs( $userId, $userText, $edits=null, $useParentheses=true)
Alias for userToolLinks( $userId, $userText, true );.
Definition: Linker.php:1023
Xml\tags
static tags( $element, $attribs, $contents)
Same as Xml::element(), but does not escape contents.
Definition: Xml.php:132
Hooks\runner
static runner()
Get a HookRunner instance for calling hooks using the new interfaces.
Definition: Hooks.php:172
Linker\link
static link( $target, $html=null, $customAttribs=[], $query=[], $options=[])
This function returns an HTML link to the given target.
Definition: Linker.php:87
WatchedItem
Representation of a pair of user and title for watchlist entries.
Definition: WatchedItem.php:35
MediaWiki\Linker\LinkTarget\getDBkey
getDBkey()
Get the main part with underscores.
Linker\formatRevisionSize
static formatRevisionSize( $size)
Definition: Linker.php:1638
MediaWiki\Linker\LinkTarget\getFragment
getFragment()
Get the link fragment (i.e.
NS_USER
const NS_USER
Definition: Defines.php:66
Parser
PHP Parser - Processes wiki markup (which uses a more user-friendly syntax, such as "[[link]]" for ma...
Definition: Parser.php:87
RequestContext\getMain
static getMain()
Get the RequestContext object associated with the main request.
Definition: RequestContext.php:476
DummyLinker
Definition: DummyLinker.php:8
Title\newFromLinkTarget
static newFromLinkTarget(LinkTarget $linkTarget, $forceClone='')
Returns a Title given a LinkTarget.
Definition: Title.php:291
Linker\makeMediaLinkFile
static makeMediaLinkFile(LinkTarget $title, $file, $html='')
Create a direct link to a given uploaded file.
Definition: Linker.php:779
$wgUploadNavigationUrl
$wgUploadNavigationUrl
Point the upload navigation link to an external URL Useful if you want to use a shared repository by ...
Definition: DefaultSettings.php:1021
Linker\titleAttrib
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:2105
IContextSource
Interface for objects which can provide a MediaWiki context on request.
Definition: IContextSource.php:57
Linker\formatComment
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:1191
Linker\tooltip
static tooltip( $name, $options=null)
Returns raw bits of HTML, use titleAttrib()
Definition: Linker.php:2334
MediaWiki\Permissions\Authority\isAllowed
isAllowed(string $permission)
Checks whether this authority has the given permission in general.
$wgMiserMode
$wgMiserMode
Disable database-intensive features.
Definition: DefaultSettings.php:2458
MalformedTitleException
MalformedTitleException is thrown when a TitleParser is unable to parse a title string.
Definition: MalformedTitleException.php:26
$wgThumbUpright
$wgThumbUpright
Adjust width of upright images when parameter 'upright' is used This allows a nicer look for upright ...
Definition: DefaultSettings.php:1717
Linker\tocUnindent
static tocUnindent( $level)
Finish one or more sublevels on the Table of Contents.
Definition: Linker.php:1664
Linker\makeImageLink
static makeImageLink(Parser $parser, LinkTarget $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:300
Html\openElement
static openElement( $element, $attribs=[])
Identical to rawElement(), but has no third parameter and omits the end tag (and the self-closing '/'...
Definition: Html.php:254
Html\rawElement
static rawElement( $element, $attribs=[], $contents='')
Returns an HTML element in a string.
Definition: Html.php:212
Linker\revDeleteLink
static revDeleteLink( $query=[], $restricted=false, $delete=true)
Creates a (show/hide) link for deleting revisions/log entries.
Definition: Linker.php:2243
NS_USER_TALK
const NS_USER_TALK
Definition: Defines.php:67
$wgEnableUploads
$wgEnableUploads
Allow users to upload files.
Definition: DefaultSettings.php:505
Linker\normalizeSubpageLink
static normalizeSubpageLink( $contextTitle, $target, &$text)
Definition: Linker.php:1473
wfWarn
wfWarn( $msg, $callerOffset=1, $level=E_USER_NOTICE)
Send a warning either to the debug log or in a PHP error depending on $wgDevelopmentWarnings.
Definition: GlobalFunctions.php:1081
Parser\guessSectionNameFromStrippedText
static guessSectionNameFromStrippedText( $text)
Like guessSectionNameFromWikiText(), but takes already-stripped text as input.
Definition: Parser.php:6086
Title\castFromLinkTarget
static castFromLinkTarget( $linkTarget)
Same as newFromLinkTarget, but if passed null, returns null.
Definition: Title.php:315
Html\element
static element( $element, $attribs=[], $contents='')
Identical to rawElement(), but HTML-escapes $contents (like Xml::element()).
Definition: Html.php:234
$wgUploadMissingFileUrl
$wgUploadMissingFileUrl
Point the upload link for missing files to an external URL, as with $wgUploadNavigationUrl.
Definition: DefaultSettings.php:1028
NS_FILE
const NS_FILE
Definition: Defines.php:70
MediaWiki\Linker\LinkTarget
Definition: LinkTarget.php:26
Linker\accesskey
static accesskey( $name)
Given the id of an interface element, constructs the appropriate accesskey attribute from the system ...
Definition: Linker.php:2153
Linker\buildRollbackLink
static buildRollbackLink( $rev, IContextSource $context=null, $editCount=false)
Build a raw rollback link, useful for collections of "tool" links.
Definition: Linker.php:1988
ExternalUserNames\isExternal
static isExternal( $username)
Tells whether the username is external or not.
Definition: ExternalUserNames.php:147
Linker\makeExternalImage
static makeExternalImage( $url, $alt='')
Return the code for images which were added via external links, via Parser::maybeMakeExternalImage().
Definition: Linker.php:244
Language
Internationalisation code See https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation for more...
Definition: Language.php:43
Linker\makeBrokenImageLinkObj
static makeBrokenImageLinkObj( $title, $label='', $query='', $unused1='', $unused2='', $time=false)
Make a "broken" link to an image.
Definition: Linker.php:676
Sanitizer\escapeHtmlAllowEntities
static escapeHtmlAllowEntities( $html)
Given HTML input, escape with htmlspecialchars but un-escape entities.
Definition: Sanitizer.php:987
$wgDisableAnonTalk
$wgDisableAnonTalk
Disable links to talk pages of anonymous users (IPs) in listings on special pages like page history,...
Definition: DefaultSettings.php:7540
TitleValue
Represents a page (or page fragment) title within MediaWiki.
Definition: TitleValue.php:40