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 
84  public static function link(
85  $target, $html = null, $customAttribs = [], $query = [], $options = []
86  ) {
87  if ( !$target instanceof LinkTarget ) {
88  wfWarn( __METHOD__ . ': Requires $target to be a LinkTarget object.', 2 );
89  return "<!-- ERROR -->$html";
90  }
91 
92  if ( is_string( $query ) ) {
93  // some functions withing core using this still hand over query strings
94  wfDeprecated( __METHOD__ . ' with parameter $query as string (should be array)', '1.20' );
96  }
97 
98  $services = MediaWikiServices::getInstance();
100  if ( $options ) {
101  // Custom options, create new LinkRenderer
102  if ( !isset( $options['stubThreshold'] ) ) {
103  $defaultLinkRenderer = $services->getLinkRenderer();
104  $options['stubThreshold'] = $defaultLinkRenderer->getStubThreshold();
105  }
106  $linkRenderer = $services->getLinkRendererFactory()
107  ->createFromLegacyOptions( $options );
108  } else {
109  $linkRenderer = $services->getLinkRenderer();
110  }
111 
112  if ( $html !== null ) {
113  $text = new HtmlArmor( $html );
114  } else {
115  $text = null;
116  }
117 
118  if ( in_array( 'known', $options, true ) ) {
119  return $linkRenderer->makeKnownLink( $target, $text, $customAttribs, $query );
120  }
121 
122  if ( in_array( 'broken', $options, true ) ) {
123  return $linkRenderer->makeBrokenLink( $target, $text, $customAttribs, $query );
124  }
125 
126  if ( in_array( 'noclasses', $options, true ) ) {
127  return $linkRenderer->makePreloadedLink( $target, $text, '', $customAttribs, $query );
128  }
129 
130  return $linkRenderer->makeLink( $target, $text, $customAttribs, $query );
131  }
132 
146  public static function linkKnown(
147  $target, $html = null, $customAttribs = [],
148  $query = [], $options = [ 'known' ]
149  ) {
150  return self::link( $target, $html, $customAttribs, $query, $options );
151  }
152 
168  public static function makeSelfLinkObj( $nt, $html = '', $query = '', $trail = '', $prefix = '' ) {
169  $nt = Title::newFromLinkTarget( $nt );
170  $ret = "<a class=\"mw-selflink selflink\">{$prefix}{$html}</a>{$trail}";
171  if ( !Hooks::run( 'SelfLinkBegin', [ $nt, &$html, &$trail, &$prefix, &$ret ] ) ) {
172  return $ret;
173  }
174 
175  if ( $html == '' ) {
176  $html = htmlspecialchars( $nt->getPrefixedText() );
177  }
178  list( $inside, $trail ) = self::splitTrail( $trail );
179  return "<a class=\"mw-selflink selflink\">{$prefix}{$html}{$inside}</a>{$trail}";
180  }
181 
192  public static function getInvalidTitleDescription( IContextSource $context, $namespace, $title ) {
193  // First we check whether the namespace exists or not.
194  if ( MediaWikiServices::getInstance()->getNamespaceInfo()->exists( $namespace ) ) {
195  if ( $namespace == NS_MAIN ) {
196  $name = $context->msg( 'blanknamespace' )->text();
197  } else {
198  $name = MediaWikiServices::getInstance()->getContentLanguage()->
199  getFormattedNsText( $namespace );
200  }
201  return $context->msg( 'invalidtitle-knownnamespace', $namespace, $name, $title )->text();
202  }
203 
204  return $context->msg( 'invalidtitle-unknownnamespace', $namespace, $title )->text();
205  }
206 
212  public static function normaliseSpecialPage( LinkTarget $target ) {
213  if ( $target->getNamespace() == NS_SPECIAL && !$target->isExternal() ) {
214  list( $name, $subpage ) = MediaWikiServices::getInstance()->getSpecialPageFactory()->
215  resolveAlias( $target->getDBkey() );
216  if ( $name ) {
217  return SpecialPage::getTitleValueFor( $name, $subpage, $target->getFragment() );
218  }
219  }
220 
221  return $target;
222  }
223 
232  private static function fnamePart( $url ) {
233  $basename = strrchr( $url, '/' );
234  if ( $basename === false ) {
235  $basename = $url;
236  } else {
237  $basename = substr( $basename, 1 );
238  }
239  return $basename;
240  }
241 
252  public static function makeExternalImage( $url, $alt = '' ) {
253  if ( $alt == '' ) {
254  $alt = self::fnamePart( $url );
255  }
256  $img = '';
257  $success = Hooks::run( 'LinkerMakeExternalImage', [ &$url, &$alt, &$img ] );
258  if ( !$success ) {
259  wfDebug( "Hook LinkerMakeExternalImage changed the output of external image "
260  . "with url {$url} and alt text {$alt} to {$img}\n", true );
261  return $img;
262  }
263  return Html::element( 'img',
264  [
265  'src' => $url,
266  'alt' => $alt
267  ]
268  );
269  }
270 
308  public static function makeImageLink( Parser $parser, LinkTarget $title,
309  $file, $frameParams = [], $handlerParams = [], $time = false,
310  $query = "", $widthOption = null
311  ) {
312  $title = Title::newFromLinkTarget( $title );
313  $res = null;
314  $dummy = new DummyLinker;
315  if ( !Hooks::run( 'ImageBeforeProduceHTML', [ &$dummy, &$title,
316  &$file, &$frameParams, &$handlerParams, &$time, &$res,
317  $parser, &$query, &$widthOption
318  ] ) ) {
319  return $res;
320  }
321 
322  if ( $file && !$file->allowInlineDisplay() ) {
323  wfDebug( __METHOD__ . ': ' . $title->getPrefixedDBkey() . " does not allow inline display\n" );
324  return self::link( $title );
325  }
326 
327  // Clean up parameters
328  $page = $handlerParams['page'] ?? false;
329  if ( !isset( $frameParams['align'] ) ) {
330  $frameParams['align'] = '';
331  }
332  if ( !isset( $frameParams['alt'] ) ) {
333  $frameParams['alt'] = '';
334  }
335  if ( !isset( $frameParams['title'] ) ) {
336  $frameParams['title'] = '';
337  }
338  if ( !isset( $frameParams['class'] ) ) {
339  $frameParams['class'] = '';
340  }
341 
342  $prefix = $postfix = '';
343 
344  if ( $frameParams['align'] == 'center' ) {
345  $prefix = '<div class="center">';
346  $postfix = '</div>';
347  $frameParams['align'] = 'none';
348  }
349  if ( $file && !isset( $handlerParams['width'] ) ) {
350  if ( isset( $handlerParams['height'] ) && $file->isVectorized() ) {
351  // If its a vector image, and user only specifies height
352  // we don't want it to be limited by its "normal" width.
353  global $wgSVGMaxSize;
354  $handlerParams['width'] = $wgSVGMaxSize;
355  } else {
356  $handlerParams['width'] = $file->getWidth( $page );
357  }
358 
359  if ( isset( $frameParams['thumbnail'] )
360  || isset( $frameParams['manualthumb'] )
361  || isset( $frameParams['framed'] )
362  || isset( $frameParams['frameless'] )
363  || !$handlerParams['width']
364  ) {
366 
367  if ( $widthOption === null || !isset( $wgThumbLimits[$widthOption] ) ) {
368  $widthOption = User::getDefaultOption( 'thumbsize' );
369  }
370 
371  // Reduce width for upright images when parameter 'upright' is used
372  if ( isset( $frameParams['upright'] ) && $frameParams['upright'] == 0 ) {
373  $frameParams['upright'] = $wgThumbUpright;
374  }
375 
376  // For caching health: If width scaled down due to upright
377  // parameter, round to full __0 pixel to avoid the creation of a
378  // lot of odd thumbs.
379  $prefWidth = isset( $frameParams['upright'] ) ?
380  round( $wgThumbLimits[$widthOption] * $frameParams['upright'], -1 ) :
381  $wgThumbLimits[$widthOption];
382 
383  // Use width which is smaller: real image width or user preference width
384  // Unless image is scalable vector.
385  if ( !isset( $handlerParams['height'] ) && ( $handlerParams['width'] <= 0 ||
386  $prefWidth < $handlerParams['width'] || $file->isVectorized() ) ) {
387  $handlerParams['width'] = $prefWidth;
388  }
389  }
390  }
391 
392  if ( isset( $frameParams['thumbnail'] ) || isset( $frameParams['manualthumb'] )
393  || isset( $frameParams['framed'] )
394  ) {
395  # Create a thumbnail. Alignment depends on the writing direction of
396  # the page content language (right-aligned for LTR languages,
397  # left-aligned for RTL languages)
398  # If a thumbnail width has not been provided, it is set
399  # to the default user option as specified in Language*.php
400  if ( $frameParams['align'] == '' ) {
401  $frameParams['align'] = $parser->getTargetLanguage()->alignEnd();
402  }
403  return $prefix .
404  self::makeThumbLink2( $title, $file, $frameParams, $handlerParams, $time, $query ) .
405  $postfix;
406  }
407 
408  if ( $file && isset( $frameParams['frameless'] ) ) {
409  $srcWidth = $file->getWidth( $page );
410  # For "frameless" option: do not present an image bigger than the
411  # source (for bitmap-style images). This is the same behavior as the
412  # "thumb" option does it already.
413  if ( $srcWidth && !$file->mustRender() && $handlerParams['width'] > $srcWidth ) {
414  $handlerParams['width'] = $srcWidth;
415  }
416  }
417 
418  if ( $file && isset( $handlerParams['width'] ) ) {
419  # Create a resized image, without the additional thumbnail features
420  $thumb = $file->transform( $handlerParams );
421  } else {
422  $thumb = false;
423  }
424 
425  if ( !$thumb ) {
426  $s = self::makeBrokenImageLinkObj( $title, $frameParams['title'], '', '', '', $time == true );
427  } else {
428  self::processResponsiveImages( $file, $thumb, $handlerParams );
429  $params = [
430  'alt' => $frameParams['alt'],
431  'title' => $frameParams['title'],
432  'valign' => $frameParams['valign'] ?? false,
433  'img-class' => $frameParams['class'] ];
434  if ( isset( $frameParams['border'] ) ) {
435  $params['img-class'] .= ( $params['img-class'] !== '' ? ' ' : '' ) . 'thumbborder';
436  }
437  $params = self::getImageLinkMTOParams( $frameParams, $query, $parser ) + $params;
438 
439  $s = $thumb->toHtml( $params );
440  }
441  if ( $frameParams['align'] != '' ) {
443  'div',
444  [ 'class' => 'float' . $frameParams['align'] ],
445  $s
446  );
447  }
448  return str_replace( "\n", ' ', $prefix . $s . $postfix );
449  }
450 
459  private static function getImageLinkMTOParams( $frameParams, $query = '', $parser = null ) {
460  $mtoParams = [];
461  if ( isset( $frameParams['link-url'] ) && $frameParams['link-url'] !== '' ) {
462  $mtoParams['custom-url-link'] = $frameParams['link-url'];
463  if ( isset( $frameParams['link-target'] ) ) {
464  $mtoParams['custom-target-link'] = $frameParams['link-target'];
465  }
466  if ( $parser ) {
467  $extLinkAttrs = $parser->getExternalLinkAttribs( $frameParams['link-url'] );
468  foreach ( $extLinkAttrs as $name => $val ) {
469  // Currently could include 'rel' and 'target'
470  $mtoParams['parser-extlink-' . $name] = $val;
471  }
472  }
473  } elseif ( isset( $frameParams['link-title'] ) && $frameParams['link-title'] !== '' ) {
474  $mtoParams['custom-title-link'] = Title::newFromLinkTarget(
475  self::normaliseSpecialPage( $frameParams['link-title'] )
476  );
477  } elseif ( !empty( $frameParams['no-link'] ) ) {
478  // No link
479  } else {
480  $mtoParams['desc-link'] = true;
481  $mtoParams['desc-query'] = $query;
482  }
483  return $mtoParams;
484  }
485 
498  public static function makeThumbLinkObj( LinkTarget $title, $file, $label = '', $alt = '',
499  $align = 'right', $params = [], $framed = false, $manualthumb = ""
500  ) {
501  $frameParams = [
502  'alt' => $alt,
503  'caption' => $label,
504  'align' => $align
505  ];
506  if ( $framed ) {
507  $frameParams['framed'] = true;
508  }
509  if ( $manualthumb ) {
510  $frameParams['manualthumb'] = $manualthumb;
511  }
512  return self::makeThumbLink2( $title, $file, $frameParams, $params );
513  }
514 
524  public static function makeThumbLink2( LinkTarget $title, $file, $frameParams = [],
525  $handlerParams = [], $time = false, $query = ""
526  ) {
527  $exists = $file && $file->exists();
528 
529  $page = $handlerParams['page'] ?? false;
530  if ( !isset( $frameParams['align'] ) ) {
531  $frameParams['align'] = 'right';
532  }
533  if ( !isset( $frameParams['alt'] ) ) {
534  $frameParams['alt'] = '';
535  }
536  if ( !isset( $frameParams['title'] ) ) {
537  $frameParams['title'] = '';
538  }
539  if ( !isset( $frameParams['caption'] ) ) {
540  $frameParams['caption'] = '';
541  }
542 
543  if ( empty( $handlerParams['width'] ) ) {
544  // Reduce width for upright images when parameter 'upright' is used
545  $handlerParams['width'] = isset( $frameParams['upright'] ) ? 130 : 180;
546  }
547  $thumb = false;
548  $noscale = false;
549  $manualthumb = false;
550 
551  if ( !$exists ) {
552  $outerWidth = $handlerParams['width'] + 2;
553  } else {
554  if ( isset( $frameParams['manualthumb'] ) ) {
555  # Use manually specified thumbnail
556  $manual_title = Title::makeTitleSafe( NS_FILE, $frameParams['manualthumb'] );
557  if ( $manual_title ) {
558  $manual_img = MediaWikiServices::getInstance()->getRepoGroup()
559  ->findFile( $manual_title );
560  if ( $manual_img ) {
561  $thumb = $manual_img->getUnscaledThumb( $handlerParams );
562  $manualthumb = true;
563  } else {
564  $exists = false;
565  }
566  }
567  } elseif ( isset( $frameParams['framed'] ) ) {
568  // Use image dimensions, don't scale
569  $thumb = $file->getUnscaledThumb( $handlerParams );
570  $noscale = true;
571  } else {
572  # Do not present an image bigger than the source, for bitmap-style images
573  # This is a hack to maintain compatibility with arbitrary pre-1.10 behavior
574  $srcWidth = $file->getWidth( $page );
575  if ( $srcWidth && !$file->mustRender() && $handlerParams['width'] > $srcWidth ) {
576  $handlerParams['width'] = $srcWidth;
577  }
578  $thumb = $file->transform( $handlerParams );
579  }
580 
581  if ( $thumb ) {
582  $outerWidth = $thumb->getWidth() + 2;
583  } else {
584  $outerWidth = $handlerParams['width'] + 2;
585  }
586  }
587 
588  # ThumbnailImage::toHtml() already adds page= onto the end of DjVu URLs
589  # So we don't need to pass it here in $query. However, the URL for the
590  # zoom icon still needs it, so we make a unique query for it. See T16771
591  $url = Title::newFromLinkTarget( $title )->getLocalURL( $query );
592  if ( $page ) {
593  $url = wfAppendQuery( $url, [ 'page' => $page ] );
594  }
595  if ( $manualthumb
596  && !isset( $frameParams['link-title'] )
597  && !isset( $frameParams['link-url'] )
598  && !isset( $frameParams['no-link'] ) ) {
599  $frameParams['link-url'] = $url;
600  }
601 
602  $s = "<div class=\"thumb t{$frameParams['align']}\">"
603  . "<div class=\"thumbinner\" style=\"width:{$outerWidth}px;\">";
604 
605  if ( !$exists ) {
606  $s .= self::makeBrokenImageLinkObj( $title, $frameParams['title'], '', '', '', $time == true );
607  $zoomIcon = '';
608  } elseif ( !$thumb ) {
609  $s .= wfMessage( 'thumbnail_error', '' )->escaped();
610  $zoomIcon = '';
611  } else {
612  if ( !$noscale && !$manualthumb ) {
613  self::processResponsiveImages( $file, $thumb, $handlerParams );
614  }
615  $params = [
616  'alt' => $frameParams['alt'],
617  'title' => $frameParams['title'],
618  'img-class' => ( isset( $frameParams['class'] ) && $frameParams['class'] !== ''
619  ? $frameParams['class'] . ' '
620  : '' ) . 'thumbimage'
621  ];
622  $params = self::getImageLinkMTOParams( $frameParams, $query ) + $params;
623  $s .= $thumb->toHtml( $params );
624  if ( isset( $frameParams['framed'] ) ) {
625  $zoomIcon = "";
626  } else {
627  $zoomIcon = Html::rawElement( 'div', [ 'class' => 'magnify' ],
628  Html::rawElement( 'a', [
629  'href' => $url,
630  'class' => 'internal',
631  'title' => wfMessage( 'thumbnail-more' )->text() ],
632  "" ) );
633  }
634  }
635  $s .= ' <div class="thumbcaption">' . $zoomIcon . $frameParams['caption'] . "</div></div></div>";
636  return str_replace( "\n", ' ', $s );
637  }
638 
647  public static function processResponsiveImages( $file, $thumb, $hp ) {
648  global $wgResponsiveImages;
649  if ( $wgResponsiveImages && $thumb && !$thumb->isError() ) {
650  $hp15 = $hp;
651  $hp15['width'] = round( $hp['width'] * 1.5 );
652  $hp20 = $hp;
653  $hp20['width'] = $hp['width'] * 2;
654  if ( isset( $hp['height'] ) ) {
655  $hp15['height'] = round( $hp['height'] * 1.5 );
656  $hp20['height'] = $hp['height'] * 2;
657  }
658 
659  $thumb15 = $file->transform( $hp15 );
660  $thumb20 = $file->transform( $hp20 );
661  if ( $thumb15 && !$thumb15->isError() && $thumb15->getUrl() !== $thumb->getUrl() ) {
662  $thumb->responsiveUrls['1.5'] = $thumb15->getUrl();
663  }
664  if ( $thumb20 && !$thumb20->isError() && $thumb20->getUrl() !== $thumb->getUrl() ) {
665  $thumb->responsiveUrls['2'] = $thumb20->getUrl();
666  }
667  }
668  }
669 
682  public static function makeBrokenImageLinkObj( $title, $label = '',
683  $query = '', $unused1 = '', $unused2 = '', $time = false
684  ) {
685  if ( !$title instanceof LinkTarget ) {
686  wfWarn( __METHOD__ . ': Requires $title to be a LinkTarget object.' );
687  return "<!-- ERROR -->" . htmlspecialchars( $label );
688  }
689 
691 
693  if ( $label == '' ) {
694  $label = $title->getPrefixedText();
695  }
696  $encLabel = htmlspecialchars( $label );
697  $file = MediaWikiServices::getInstance()->getRepoGroup()->findFile( $title );
698  $currentExists = $time ? ( $file != false ) : false;
699 
700  if ( ( $wgUploadMissingFileUrl || $wgUploadNavigationUrl || $wgEnableUploads )
701  && !$currentExists
702  ) {
703  $redir = RepoGroup::singleton()->getLocalRepo()->checkRedirect( $title );
704 
705  if ( $redir ) {
706  // We already know it's a redirect, so mark it
707  // accordingly
708  return self::link(
709  $title,
710  $encLabel,
711  [ 'class' => 'mw-redirect' ],
712  wfCgiToArray( $query ),
713  [ 'known', 'noclasses' ]
714  );
715  }
716 
717  $href = self::getUploadUrl( $title, $query );
718 
719  return '<a href="' . htmlspecialchars( $href ) . '" class="new" title="' .
720  htmlspecialchars( $title->getPrefixedText(), ENT_QUOTES ) . '">' .
721  $encLabel . '</a>';
722  }
723 
724  return self::link( $title, $encLabel, [], wfCgiToArray( $query ), [ 'known', 'noclasses' ] );
725  }
726 
735  protected static function getUploadUrl( $destFile, $query = '' ) {
737  $q = 'wpDestFile=' . Title::castFromLinkTarget( $destFile )->getPartialURL();
738  if ( $query != '' ) {
739  $q .= '&' . $query;
740  }
741 
742  if ( $wgUploadMissingFileUrl ) {
743  return wfAppendQuery( $wgUploadMissingFileUrl, $q );
744  }
745 
746  if ( $wgUploadNavigationUrl ) {
747  return wfAppendQuery( $wgUploadNavigationUrl, $q );
748  }
749 
750  $upload = SpecialPage::getTitleFor( 'Upload' );
751 
752  return $upload->getLocalURL( $q );
753  }
754 
764  public static function makeMediaLinkObj( $title, $html = '', $time = false ) {
765  $img = MediaWikiServices::getInstance()->getRepoGroup()->findFile(
766  $title, [ 'time' => $time ]
767  );
768  return self::makeMediaLinkFile( $title, $img, $html );
769  }
770 
783  public static function makeMediaLinkFile( LinkTarget $title, $file, $html = '' ) {
784  if ( $file && $file->exists() ) {
785  $url = $file->getUrl();
786  $class = 'internal';
787  } else {
788  $url = self::getUploadUrl( $title );
789  $class = 'new';
790  }
791 
792  $alt = $title->getText();
793  if ( $html == '' ) {
794  $html = $alt;
795  }
796 
797  $ret = '';
798  $attribs = [
799  'href' => $url,
800  'class' => $class,
801  'title' => $alt
802  ];
803 
804  if ( !Hooks::run( 'LinkerMakeMediaLinkFile',
805  [ Title::castFromLinkTarget( $title ), $file, &$html, &$attribs, &$ret ] ) ) {
806  wfDebug( "Hook LinkerMakeMediaLinkFile changed the output of link "
807  . "with url {$url} and text {$html} to {$ret}\n", true );
808  return $ret;
809  }
810 
811  return Html::rawElement( 'a', $attribs, $html );
812  }
813 
824  public static function specialLink( $name, $key = '' ) {
825  if ( $key == '' ) {
826  $key = strtolower( $name );
827  }
828 
829  return self::linkKnown( SpecialPage::getTitleFor( $name ), wfMessage( $key )->escaped() );
830  }
831 
850  public static function makeExternalLink( $url, $text, $escape = true,
851  $linktype = '', $attribs = [], $title = null
852  ) {
853  global $wgTitle;
854  $class = "external";
855  if ( $linktype ) {
856  $class .= " $linktype";
857  }
858  if ( isset( $attribs['class'] ) && $attribs['class'] ) {
859  $class .= " {$attribs['class']}";
860  }
861  $attribs['class'] = $class;
862 
863  if ( $escape ) {
864  $text = htmlspecialchars( $text );
865  }
866 
867  if ( !$title ) {
868  $title = $wgTitle;
869  }
870  $newRel = Parser::getExternalLinkRel( $url, $title );
871  if ( !isset( $attribs['rel'] ) || $attribs['rel'] === '' ) {
872  $attribs['rel'] = $newRel;
873  } elseif ( $newRel !== '' ) {
874  // Merge the rel attributes.
875  $newRels = explode( ' ', $newRel );
876  $oldRels = explode( ' ', $attribs['rel'] );
877  $combined = array_unique( array_merge( $newRels, $oldRels ) );
878  $attribs['rel'] = implode( ' ', $combined );
879  }
880  $link = '';
881  $success = Hooks::run( 'LinkerMakeExternalLink',
882  [ &$url, &$text, &$link, &$attribs, $linktype ] );
883  if ( !$success ) {
884  wfDebug( "Hook LinkerMakeExternalLink changed the output of link "
885  . "with url {$url} and text {$text} to {$link}\n", true );
886  return $link;
887  }
888  $attribs['href'] = $url;
889  return Html::rawElement( 'a', $attribs, $text );
890  }
891 
900  public static function userLink( $userId, $userName, $altUserName = false ) {
901  if ( $userName === '' || $userName === false || $userName === null ) {
902  wfDebug( __METHOD__ . ' received an empty username. Are there database errors ' .
903  'that need to be fixed?' );
904  return wfMessage( 'empty-username' )->parse();
905  }
906 
907  $classes = 'mw-userlink';
908  $page = null;
909  if ( $userId == 0 ) {
910  $page = ExternalUserNames::getUserLinkTitle( $userName );
911 
912  if ( ExternalUserNames::isExternal( $userName ) ) {
913  $classes .= ' mw-extuserlink';
914  } elseif ( $altUserName === false ) {
915  $altUserName = IP::prettifyIP( $userName );
916  }
917  $classes .= ' mw-anonuserlink'; // Separate link class for anons (T45179)
918  } else {
919  $page = new TitleValue( NS_USER, strtr( $userName, ' ', '_' ) );
920  }
921 
922  // Wrap the output with <bdi> tags for directionality isolation
923  $linkText =
924  '<bdi>' . htmlspecialchars( $altUserName !== false ? $altUserName : $userName ) . '</bdi>';
925 
926  return $page
927  ? self::link( $page, $linkText, [ 'class' => $classes ] )
928  : Html::rawElement( 'span', [ 'class' => $classes ], $linkText );
929  }
930 
945  public static function userToolLinks(
946  $userId, $userText, $redContribsWhenNoEdits = false, $flags = 0, $edits = null,
947  $useParentheses = true
948  ) {
949  if ( $userText === '' ) {
950  wfDebug( __METHOD__ . ' received an empty username. Are there database errors ' .
951  'that need to be fixed?' );
952  return ' ' . wfMessage( 'empty-username' )->parse();
953  }
954 
955  global $wgUser, $wgDisableAnonTalk, $wgLang;
956  $talkable = !( $wgDisableAnonTalk && $userId == 0 );
957  $blockable = !( $flags & self::TOOL_LINKS_NOBLOCK );
958  $addEmailLink = $flags & self::TOOL_LINKS_EMAIL && $userId;
959 
960  if ( $userId == 0 && ExternalUserNames::isExternal( $userText ) ) {
961  // No tools for an external user
962  return '';
963  }
964 
965  $items = [];
966  if ( $talkable ) {
967  $items[] = self::userTalkLink( $userId, $userText );
968  }
969  if ( $userId ) {
970  // check if the user has an edit
971  $attribs = [];
972  $attribs['class'] = 'mw-usertoollinks-contribs';
973  if ( $redContribsWhenNoEdits ) {
974  if ( intval( $edits ) === 0 && $edits !== 0 ) {
975  $user = User::newFromId( $userId );
976  $edits = $user->getEditCount();
977  }
978  if ( $edits === 0 ) {
979  $attribs['class'] .= ' new';
980  }
981  }
982  $contribsPage = SpecialPage::getTitleFor( 'Contributions', $userText );
983 
984  $items[] = self::link( $contribsPage, wfMessage( 'contribslink' )->escaped(), $attribs );
985  }
986  if ( $blockable && $wgUser->isAllowed( 'block' ) ) {
987  $items[] = self::blockLink( $userId, $userText );
988  }
989 
990  if ( $addEmailLink && $wgUser->canSendEmail() ) {
991  $items[] = self::emailLink( $userId, $userText );
992  }
993 
994  Hooks::run( 'UserToolLinksEdit', [ $userId, $userText, &$items ] );
995 
996  if ( !$items ) {
997  return '';
998  }
999 
1000  if ( $useParentheses ) {
1001  return wfMessage( 'word-separator' )->escaped()
1002  . '<span class="mw-usertoollinks">'
1003  . wfMessage( 'parentheses' )->rawParams( $wgLang->pipeList( $items ) )->escaped()
1004  . '</span>';
1005  }
1006 
1007  $tools = [];
1008  foreach ( $items as $tool ) {
1009  $tools[] = Html::rawElement( 'span', [], $tool );
1010  }
1011  return ' <span class="mw-usertoollinks mw-changeslist-links">' .
1012  implode( ' ', $tools ) . '</span>';
1013  }
1014 
1024  public static function userToolLinksRedContribs(
1025  $userId, $userText, $edits = null, $useParentheses = true
1026  ) {
1027  return self::userToolLinks( $userId, $userText, true, 0, $edits, $useParentheses );
1028  }
1029 
1036  public static function userTalkLink( $userId, $userText ) {
1037  if ( $userText === '' ) {
1038  wfDebug( __METHOD__ . ' received an empty username. Are there database errors ' .
1039  'that need to be fixed?' );
1040  return wfMessage( 'empty-username' )->parse();
1041  }
1042 
1043  $userTalkPage = new TitleValue( NS_USER_TALK, strtr( $userText, ' ', '_' ) );
1044  $moreLinkAttribs['class'] = 'mw-usertoollinks-talk';
1045 
1046  return self::link( $userTalkPage,
1047  wfMessage( 'talkpagelinktext' )->escaped(),
1048  $moreLinkAttribs
1049  );
1050  }
1051 
1058  public static function blockLink( $userId, $userText ) {
1059  if ( $userText === '' ) {
1060  wfDebug( __METHOD__ . ' received an empty username. Are there database errors ' .
1061  'that need to be fixed?' );
1062  return wfMessage( 'empty-username' )->parse();
1063  }
1064 
1065  $blockPage = SpecialPage::getTitleFor( 'Block', $userText );
1066  $moreLinkAttribs['class'] = 'mw-usertoollinks-block';
1067 
1068  return self::link( $blockPage,
1069  wfMessage( 'blocklink' )->escaped(),
1070  $moreLinkAttribs
1071  );
1072  }
1073 
1079  public static function emailLink( $userId, $userText ) {
1080  if ( $userText === '' ) {
1081  wfLogWarning( __METHOD__ . ' received an empty username. Are there database errors ' .
1082  'that need to be fixed?' );
1083  return wfMessage( 'empty-username' )->parse();
1084  }
1085 
1086  $emailPage = SpecialPage::getTitleFor( 'Emailuser', $userText );
1087  $moreLinkAttribs['class'] = 'mw-usertoollinks-mail';
1088  return self::link( $emailPage,
1089  wfMessage( 'emaillink' )->escaped(),
1090  $moreLinkAttribs
1091  );
1092  }
1093 
1101  public static function revUserLink( $rev, $isPublic = false ) {
1102  if ( $rev->isDeleted( Revision::DELETED_USER ) && $isPublic ) {
1103  $link = wfMessage( 'rev-deleted-user' )->escaped();
1104  } elseif ( $rev->userCan( Revision::DELETED_USER ) ) {
1105  $link = self::userLink( $rev->getUser( Revision::FOR_THIS_USER ),
1106  $rev->getUserText( Revision::FOR_THIS_USER ) );
1107  } else {
1108  $link = wfMessage( 'rev-deleted-user' )->escaped();
1109  }
1110  if ( $rev->isDeleted( Revision::DELETED_USER ) ) {
1111  return '<span class="history-deleted">' . $link . '</span>';
1112  }
1113  return $link;
1114  }
1115 
1124  public static function revUserTools( $rev, $isPublic = false, $useParentheses = true ) {
1125  if ( $rev->isDeleted( Revision::DELETED_USER ) && $isPublic ) {
1126  $link = wfMessage( 'rev-deleted-user' )->escaped();
1127  } elseif ( $rev->userCan( Revision::DELETED_USER ) ) {
1128  $userId = $rev->getUser( Revision::FOR_THIS_USER );
1129  $userText = $rev->getUserText( Revision::FOR_THIS_USER );
1130  $link = self::userLink( $userId, $userText )
1131  . self::userToolLinks( $userId, $userText, false, 0, null,
1132  $useParentheses );
1133  } else {
1134  $link = wfMessage( 'rev-deleted-user' )->escaped();
1135  }
1136  if ( $rev->isDeleted( Revision::DELETED_USER ) ) {
1137  return ' <span class="history-deleted mw-userlink">' . $link . '</span>';
1138  }
1139  return $link;
1140  }
1141 
1160  public static function formatComment(
1161  $comment, $title = null, $local = false, $wikiId = null
1162  ) {
1163  # Sanitize text a bit:
1164  $comment = str_replace( "\n", " ", $comment );
1165  # Allow HTML entities (for T15815)
1166  $comment = Sanitizer::escapeHtmlAllowEntities( $comment );
1167 
1168  # Render autocomments and make links:
1169  $comment = self::formatAutocomments( $comment, $title, $local, $wikiId );
1170  return self::formatLinksInComment( $comment, $title, $local, $wikiId );
1171  }
1172 
1190  private static function formatAutocomments(
1191  $comment, $title = null, $local = false, $wikiId = null
1192  ) {
1193  // @todo $append here is something of a hack to preserve the status
1194  // quo. Someone who knows more about bidi and such should decide
1195  // (1) what sane rendering even *is* for an LTR edit summary on an RTL
1196  // wiki, both when autocomments exist and when they don't, and
1197  // (2) what markup will make that actually happen.
1198  $append = '';
1199  $comment = preg_replace_callback(
1200  // To detect the presence of content before or after the
1201  // auto-comment, we use capturing groups inside optional zero-width
1202  // assertions. But older versions of PCRE can't directly make
1203  // zero-width assertions optional, so wrap them in a non-capturing
1204  // group.
1205  '!(?:(?<=(.)))?/\*\s*(.*?)\s*\*/(?:(?=(.)))?!',
1206  function ( $match ) use ( $title, $local, $wikiId, &$append ) {
1207  global $wgLang;
1208 
1209  // Ensure all match positions are defined
1210  $match += [ '', '', '', '' ];
1211 
1212  $pre = $match[1] !== '';
1213  $auto = $match[2];
1214  $post = $match[3] !== '';
1215  $comment = null;
1216 
1217  Hooks::run(
1218  'FormatAutocomments',
1219  [ &$comment, $pre, $auto, $post, Title::castFromLinkTarget( $title ), $local,
1220  $wikiId ]
1221  );
1222 
1223  if ( $comment === null ) {
1224  if ( $title ) {
1225  $section = $auto;
1226  # Remove links that a user may have manually put in the autosummary
1227  # This could be improved by copying as much of Parser::stripSectionName as desired.
1228  $section = str_replace( [
1229  '[[:',
1230  '[[',
1231  ']]'
1232  ], '', $section );
1233 
1234  // We don't want any links in the auto text to be linked, but we still
1235  // want to show any [[ ]]
1236  $sectionText = str_replace( '[[', '&#91;[', $auto );
1237 
1239  // Support: HHVM (T222857)
1240  // The guessSectionNameFromStrippedText method returns a non-empty string
1241  // that starts with "#". Before PHP 7 (and still on HHVM) substr() would
1242  // return false if the start offset is the end of the string.
1243  // On PHP 7+, it gracefully returns empty string instead.
1244  if ( $section !== '' && $section !== false ) {
1245  if ( $local ) {
1246  $sectionTitle = new TitleValue( NS_MAIN, '', $section );
1247  } else {
1248  $sectionTitle = $title->createFragmentTarget( $section );
1249  }
1251  $sectionTitle,
1252  $wgLang->getArrow() . $wgLang->getDirMark() . $sectionText,
1253  $wikiId,
1254  'noclasses'
1255  );
1256  }
1257  }
1258  if ( $pre ) {
1259  # written summary $presep autocomment (summary /* section */)
1260  $pre = wfMessage( 'autocomment-prefix' )->inContentLanguage()->escaped();
1261  }
1262  if ( $post ) {
1263  # autocomment $postsep written summary (/* section */ summary)
1264  $auto .= wfMessage( 'colon-separator' )->inContentLanguage()->escaped();
1265  }
1266  if ( $auto ) {
1267  $auto = '<span dir="auto"><span class="autocomment">' . $auto . '</span>';
1268  $append .= '</span>';
1269  }
1270  $comment = $pre . $auto;
1271  }
1272  return $comment;
1273  },
1274  $comment
1275  );
1276  return $comment . $append;
1277  }
1278 
1298  public static function formatLinksInComment(
1299  $comment, $title = null, $local = false, $wikiId = null
1300  ) {
1301  return preg_replace_callback(
1302  '/
1303  \[\[
1304  \s*+ # ignore leading whitespace, the *+ quantifier disallows backtracking
1305  :? # ignore optional leading colon
1306  ([^\]|]+) # 1. link target; page names cannot include ] or |
1307  (?:\|
1308  # 2. link text
1309  # Stop matching at ]] without relying on backtracking.
1310  ((?:]?[^\]])*+)
1311  )?
1312  \]\]
1313  ([^[]*) # 3. link trail (the text up until the next link)
1314  /x',
1315  function ( $match ) use ( $title, $local, $wikiId ) {
1316  $services = MediaWikiServices::getInstance();
1317 
1318  $medians = '(?:';
1319  $medians .= preg_quote(
1320  $services->getNamespaceInfo()->getCanonicalName( NS_MEDIA ), '/' );
1321  $medians .= '|';
1322  $medians .= preg_quote(
1323  MediaWikiServices::getInstance()->getContentLanguage()->getNsText( NS_MEDIA ),
1324  '/'
1325  ) . '):';
1326 
1327  $comment = $match[0];
1328 
1329  # fix up urlencoded title texts (copied from Parser::replaceInternalLinks)
1330  if ( strpos( $match[1], '%' ) !== false ) {
1331  $match[1] = strtr(
1332  rawurldecode( $match[1] ),
1333  [ '<' => '&lt;', '>' => '&gt;' ]
1334  );
1335  }
1336 
1337  # Handle link renaming [[foo|text]] will show link as "text"
1338  if ( $match[2] != "" ) {
1339  $text = $match[2];
1340  } else {
1341  $text = $match[1];
1342  }
1343  $submatch = [];
1344  $thelink = null;
1345  if ( preg_match( '/^' . $medians . '(.*)$/i', $match[1], $submatch ) ) {
1346  # Media link; trail not supported.
1347  $linkRegexp = '/\[\[(.*?)\]\]/';
1348  $title = Title::makeTitleSafe( NS_FILE, $submatch[1] );
1349  if ( $title ) {
1350  $thelink = Linker::makeMediaLinkObj( $title, $text );
1351  }
1352  } else {
1353  # Other kind of link
1354  # Make sure its target is non-empty
1355  if ( isset( $match[1][0] ) && $match[1][0] == ':' ) {
1356  $match[1] = substr( $match[1], 1 );
1357  }
1358  if ( $match[1] !== false && $match[1] !== '' ) {
1359  if ( preg_match(
1360  MediaWikiServices::getInstance()->getContentLanguage()->linkTrail(),
1361  $match[3],
1362  $submatch
1363  ) ) {
1364  $trail = $submatch[1];
1365  } else {
1366  $trail = "";
1367  }
1368  $linkRegexp = '/\[\[(.*?)\]\]' . preg_quote( $trail, '/' ) . '/';
1369  list( $inside, $trail ) = Linker::splitTrail( $trail );
1370 
1371  $linkText = $text;
1372  $linkTarget = Linker::normalizeSubpageLink( $title, $match[1], $linkText );
1373 
1374  Title::newFromText( $linkTarget );
1375  try {
1376  $target = MediaWikiServices::getInstance()->getTitleParser()->
1377  parseTitle( $linkTarget );
1378 
1379  if ( $target->getText() == '' && !$target->isExternal()
1380  && !$local && $title
1381  ) {
1382  $target = $title->createFragmentTarget( $target->getFragment() );
1383  }
1384 
1385  $thelink = Linker::makeCommentLink( $target, $linkText . $inside, $wikiId ) . $trail;
1386  } catch ( MalformedTitleException $e ) {
1387  // Fall through
1388  }
1389  }
1390  }
1391  if ( $thelink ) {
1392  // If the link is still valid, go ahead and replace it in!
1393  $comment = preg_replace(
1394  $linkRegexp,
1396  $comment,
1397  1
1398  );
1399  }
1400 
1401  return $comment;
1402  },
1403  $comment
1404  );
1405  }
1406 
1420  public static function makeCommentLink(
1421  LinkTarget $linkTarget, $text, $wikiId = null, $options = []
1422  ) {
1423  if ( $wikiId !== null && !$linkTarget->isExternal() ) {
1424  $link = self::makeExternalLink(
1426  $wikiId,
1427  $linkTarget->getNamespace() === 0
1428  ? $linkTarget->getDBkey()
1429  : MediaWikiServices::getInstance()->getNamespaceInfo()->
1430  getCanonicalName( $linkTarget->getNamespace() ) .
1431  ':' . $linkTarget->getDBkey(),
1432  $linkTarget->getFragment()
1433  ),
1434  $text,
1435  /* escape = */ false // Already escaped
1436  );
1437  } else {
1438  $link = self::link( $linkTarget, $text, [], [], $options );
1439  }
1440 
1441  return $link;
1442  }
1443 
1450  public static function normalizeSubpageLink( $contextTitle, $target, &$text ) {
1451  # Valid link forms:
1452  # Foobar -- normal
1453  # :Foobar -- override special treatment of prefix (images, language links)
1454  # /Foobar -- convert to CurrentPage/Foobar
1455  # /Foobar/ -- convert to CurrentPage/Foobar, strip the initial and final / from text
1456  # ../ -- convert to CurrentPage, from CurrentPage/CurrentSubPage
1457  # ../Foobar -- convert to CurrentPage/Foobar,
1458  # (from CurrentPage/CurrentSubPage)
1459  # ../Foobar/ -- convert to CurrentPage/Foobar, use 'Foobar' as text
1460  # (from CurrentPage/CurrentSubPage)
1461 
1462  $ret = $target; # default return value is no change
1463 
1464  # Some namespaces don't allow subpages,
1465  # so only perform processing if subpages are allowed
1466  if (
1467  $contextTitle && MediaWikiServices::getInstance()->getNamespaceInfo()->
1468  hasSubpages( $contextTitle->getNamespace() )
1469  ) {
1470  $hash = strpos( $target, '#' );
1471  if ( $hash !== false ) {
1472  $suffix = substr( $target, $hash );
1473  $target = substr( $target, 0, $hash );
1474  } else {
1475  $suffix = '';
1476  }
1477  # T9425
1478  $target = trim( $target );
1479  $contextPrefixedText = MediaWikiServices::getInstance()->getTitleFormatter()->
1480  getPrefixedText( $contextTitle );
1481  # Look at the first character
1482  if ( $target != '' && $target[0] === '/' ) {
1483  # / at end means we don't want the slash to be shown
1484  $m = [];
1485  $trailingSlashes = preg_match_all( '%(/+)$%', $target, $m );
1486  if ( $trailingSlashes ) {
1487  $noslash = $target = substr( $target, 1, -strlen( $m[0][0] ) );
1488  } else {
1489  $noslash = substr( $target, 1 );
1490  }
1491 
1492  $ret = $contextPrefixedText . '/' . trim( $noslash ) . $suffix;
1493  if ( $text === '' ) {
1494  $text = $target . $suffix;
1495  } # this might be changed for ugliness reasons
1496  } else {
1497  # check for .. subpage backlinks
1498  $dotdotcount = 0;
1499  $nodotdot = $target;
1500  while ( strncmp( $nodotdot, "../", 3 ) == 0 ) {
1501  ++$dotdotcount;
1502  $nodotdot = substr( $nodotdot, 3 );
1503  }
1504  if ( $dotdotcount > 0 ) {
1505  $exploded = explode( '/', $contextPrefixedText );
1506  if ( count( $exploded ) > $dotdotcount ) { # not allowed to go below top level page
1507  $ret = implode( '/', array_slice( $exploded, 0, -$dotdotcount ) );
1508  # / at the end means don't show full path
1509  if ( substr( $nodotdot, -1, 1 ) === '/' ) {
1510  $nodotdot = rtrim( $nodotdot, '/' );
1511  if ( $text === '' ) {
1512  $text = $nodotdot . $suffix;
1513  }
1514  }
1515  $nodotdot = trim( $nodotdot );
1516  if ( $nodotdot != '' ) {
1517  $ret .= '/' . $nodotdot;
1518  }
1519  $ret .= $suffix;
1520  }
1521  }
1522  }
1523  }
1524 
1525  return $ret;
1526  }
1527 
1542  public static function commentBlock(
1543  $comment, $title = null, $local = false, $wikiId = null, $useParentheses = true
1544  ) {
1545  // '*' used to be the comment inserted by the software way back
1546  // in antiquity in case none was provided, here for backwards
1547  // compatibility, acc. to brion -√¶var
1548  if ( $comment == '' || $comment == '*' ) {
1549  return '';
1550  }
1551  $formatted = self::formatComment( $comment, $title, $local, $wikiId );
1552  if ( $useParentheses ) {
1553  $formatted = wfMessage( 'parentheses' )->rawParams( $formatted )->escaped();
1554  $classNames = 'comment';
1555  } else {
1556  $classNames = 'comment comment--without-parentheses';
1557  }
1558  return " <span class=\"$classNames\">$formatted</span>";
1559  }
1560 
1572  public static function revComment( Revision $rev, $local = false, $isPublic = false,
1573  $useParentheses = true
1574  ) {
1575  if ( $rev->getComment( Revision::RAW ) == "" ) {
1576  return "";
1577  }
1578  if ( $rev->isDeleted( Revision::DELETED_COMMENT ) && $isPublic ) {
1579  $block = " <span class=\"comment\">" . wfMessage( 'rev-deleted-comment' )->escaped() . "</span>";
1580  } elseif ( $rev->userCan( Revision::DELETED_COMMENT ) ) {
1581  $block = self::commentBlock( $rev->getComment( Revision::FOR_THIS_USER ),
1582  $rev->getTitle(), $local, null, $useParentheses );
1583  } else {
1584  $block = " <span class=\"comment\">" . wfMessage( 'rev-deleted-comment' )->escaped() . "</span>";
1585  }
1586  if ( $rev->isDeleted( Revision::DELETED_COMMENT ) ) {
1587  return " <span class=\"history-deleted comment\">$block</span>";
1588  }
1589  return $block;
1590  }
1591 
1597  public static function formatRevisionSize( $size ) {
1598  if ( $size == 0 ) {
1599  $stxt = wfMessage( 'historyempty' )->escaped();
1600  } else {
1601  $stxt = wfMessage( 'nbytes' )->numParams( $size )->escaped();
1602  }
1603  return "<span class=\"history-size mw-diff-bytes\">$stxt</span>";
1604  }
1605 
1612  public static function tocIndent() {
1613  return "\n<ul>\n";
1614  }
1615 
1623  public static function tocUnindent( $level ) {
1624  return "</li>\n" . str_repeat( "</ul>\n</li>\n", $level > 0 ? $level : 0 );
1625  }
1626 
1638  public static function tocLine( $anchor, $tocline, $tocnumber, $level, $sectionIndex = false ) {
1639  $classes = "toclevel-$level";
1640  if ( $sectionIndex !== false ) {
1641  $classes .= " tocsection-$sectionIndex";
1642  }
1643 
1644  // <li class="$classes"><a href="#$anchor"><span class="tocnumber">
1645  // $tocnumber</span> <span class="toctext">$tocline</span></a>
1646  return Html::openElement( 'li', [ 'class' => $classes ] )
1647  . Html::rawElement( 'a',
1648  [ 'href' => "#$anchor" ],
1649  Html::element( 'span', [ 'class' => 'tocnumber' ], $tocnumber )
1650  . ' '
1651  . Html::rawElement( 'span', [ 'class' => 'toctext' ], $tocline )
1652  );
1653  }
1654 
1662  public static function tocLineEnd() {
1663  return "</li>\n";
1664  }
1665 
1675  public static function tocList( $toc, $lang = null ) {
1676  $lang = $lang ?? RequestContext::getMain()->getLanguage();
1677  if ( !$lang instanceof Language ) {
1678  wfDeprecated( __METHOD__ . ' with type other than Language for $lang', '1.33' );
1679  $lang = wfGetLangObj( $lang );
1680  }
1681 
1682  $title = wfMessage( 'toc' )->inLanguage( $lang )->escaped();
1683 
1684  return '<div id="toc" class="toc">'
1685  . Html::element( 'input', [
1686  'type' => 'checkbox',
1687  'role' => 'button',
1688  'id' => 'toctogglecheckbox',
1689  'class' => 'toctogglecheckbox',
1690  'style' => 'display:none',
1691  ] )
1692  . Html::openElement( 'div', [
1693  'class' => 'toctitle',
1694  'lang' => $lang->getHtmlCode(),
1695  'dir' => $lang->getDir(),
1696  ] )
1697  . "<h2>$title</h2>"
1698  . '<span class="toctogglespan">'
1699  . Html::label( '', 'toctogglecheckbox', [
1700  'class' => 'toctogglelabel',
1701  ] )
1702  . '</span>'
1703  . "</div>\n"
1704  . $toc
1705  . "</ul>\n</div>\n";
1706  }
1707 
1717  public static function generateTOC( $tree, $lang = null ) {
1718  $toc = '';
1719  $lastLevel = 0;
1720  foreach ( $tree as $section ) {
1721  if ( $section['toclevel'] > $lastLevel ) {
1722  $toc .= self::tocIndent();
1723  } elseif ( $section['toclevel'] < $lastLevel ) {
1724  $toc .= self::tocUnindent(
1725  $lastLevel - $section['toclevel'] );
1726  } else {
1727  $toc .= self::tocLineEnd();
1728  }
1729 
1730  $toc .= self::tocLine( $section['anchor'],
1731  $section['line'], $section['number'],
1732  $section['toclevel'], $section['index'] );
1733  $lastLevel = $section['toclevel'];
1734  }
1735  $toc .= self::tocLineEnd();
1736  return self::tocList( $toc, $lang );
1737  }
1738 
1755  public static function makeHeadline( $level, $attribs, $anchor, $html,
1756  $link, $fallbackAnchor = false
1757  ) {
1758  $anchorEscaped = htmlspecialchars( $anchor );
1759  $fallback = '';
1760  if ( $fallbackAnchor !== false && $fallbackAnchor !== $anchor ) {
1761  $fallbackAnchor = htmlspecialchars( $fallbackAnchor );
1762  $fallback = "<span id=\"$fallbackAnchor\"></span>";
1763  }
1764  return "<h$level$attribs"
1765  . "$fallback<span class=\"mw-headline\" id=\"$anchorEscaped\">$html</span>"
1766  . $link
1767  . "</h$level>";
1768  }
1769 
1776  static function splitTrail( $trail ) {
1777  $regex = MediaWikiServices::getInstance()->getContentLanguage()->linkTrail();
1778  $inside = '';
1779  if ( $trail !== '' && preg_match( $regex, $trail, $m ) ) {
1780  list( , $inside, $trail ) = $m;
1781  }
1782  return [ $inside, $trail ];
1783  }
1784 
1812  public static function generateRollback( $rev, IContextSource $context = null,
1813  $options = [ 'verify' ]
1814  ) {
1815  if ( $context === null ) {
1817  }
1818 
1819  $editCount = false;
1820  if ( in_array( 'verify', $options, true ) ) {
1821  $editCount = self::getRollbackEditCount( $rev, true );
1822  if ( $editCount === false ) {
1823  return '';
1824  }
1825  }
1826 
1827  $inner = self::buildRollbackLink( $rev, $context, $editCount );
1828 
1829  if ( !in_array( 'noBrackets', $options, true ) ) {
1830  $inner = $context->msg( 'brackets' )->rawParams( $inner )->escaped();
1831  }
1832 
1833  if ( $context->getUser()->getBoolOption( 'showrollbackconfirmation' ) ) {
1834  $stats = MediaWikiServices::getInstance()->getStatsdDataFactory();
1835  $stats->increment( 'rollbackconfirmation.event.load' );
1836  $context->getOutput()->addModules( 'mediawiki.page.rollback.confirmation' );
1837  }
1838 
1839  return '<span class="mw-rollback-link">' . $inner . '</span>';
1840  }
1841 
1857  public static function getRollbackEditCount( $rev, $verify ) {
1858  global $wgShowRollbackEditCount;
1859  if ( !is_int( $wgShowRollbackEditCount ) || !$wgShowRollbackEditCount > 0 ) {
1860  // Nothing has happened, indicate this by returning 'null'
1861  return null;
1862  }
1863 
1864  $dbr = wfGetDB( DB_REPLICA );
1865 
1866  // Up to the value of $wgShowRollbackEditCount revisions are counted
1868  $res = $dbr->select(
1869  $revQuery['tables'],
1870  [ 'rev_user_text' => $revQuery['fields']['rev_user_text'], 'rev_deleted' ],
1871  // $rev->getPage() returns null sometimes
1872  [ 'rev_page' => $rev->getTitle()->getArticleID() ],
1873  __METHOD__,
1874  [
1875  'USE INDEX' => [ 'revision' => 'page_timestamp' ],
1876  'ORDER BY' => 'rev_timestamp DESC',
1877  'LIMIT' => $wgShowRollbackEditCount + 1
1878  ],
1879  $revQuery['joins']
1880  );
1881 
1882  $editCount = 0;
1883  $moreRevs = false;
1884  foreach ( $res as $row ) {
1885  if ( $rev->getUserText( Revision::RAW ) != $row->rev_user_text ) {
1886  if ( $verify &&
1887  ( $row->rev_deleted & Revision::DELETED_TEXT
1888  || $row->rev_deleted & Revision::DELETED_USER
1889  ) ) {
1890  // If the user or the text of the revision we might rollback
1891  // to is deleted in some way we can't rollback. Similar to
1892  // the sanity checks in WikiPage::commitRollback.
1893  return false;
1894  }
1895  $moreRevs = true;
1896  break;
1897  }
1898  $editCount++;
1899  }
1900 
1901  if ( $verify && $editCount <= $wgShowRollbackEditCount && !$moreRevs ) {
1902  // We didn't find at least $wgShowRollbackEditCount revisions made by the current user
1903  // and there weren't any other revisions. That means that the current user is the only
1904  // editor, so we can't rollback
1905  return false;
1906  }
1907  return $editCount;
1908  }
1909 
1919  public static function buildRollbackLink( $rev, IContextSource $context = null,
1920  $editCount = false
1921  ) {
1923 
1924  // To config which pages are affected by miser mode
1925  $disableRollbackEditCountSpecialPage = [ 'Recentchanges', 'Watchlist' ];
1926 
1927  if ( $context === null ) {
1929  }
1930 
1931  $title = $rev->getTitle();
1932 
1933  $query = [
1934  'action' => 'rollback',
1935  'from' => $rev->getUserText(),
1936  'token' => $context->getUser()->getEditToken( 'rollback' ),
1937  ];
1938 
1939  $attrs = [
1940  'data-mw' => 'interface',
1941  'title' => $context->msg( 'tooltip-rollback' )->text()
1942  ];
1943 
1944  $options = [ 'known', 'noclasses' ];
1945 
1946  if ( $context->getRequest()->getBool( 'bot' ) ) {
1947  //T17999
1948  $query['hidediff'] = '1';
1949  $query['bot'] = '1';
1950  }
1951 
1952  $disableRollbackEditCount = false;
1953  if ( $wgMiserMode ) {
1954  foreach ( $disableRollbackEditCountSpecialPage as $specialPage ) {
1955  if ( $context->getTitle()->isSpecial( $specialPage ) ) {
1956  $disableRollbackEditCount = true;
1957  break;
1958  }
1959  }
1960  }
1961 
1962  if ( !$disableRollbackEditCount
1963  && is_int( $wgShowRollbackEditCount )
1964  && $wgShowRollbackEditCount > 0
1965  ) {
1966  if ( !is_numeric( $editCount ) ) {
1967  $editCount = self::getRollbackEditCount( $rev, false );
1968  }
1969 
1970  if ( $editCount > $wgShowRollbackEditCount ) {
1971  $html = $context->msg( 'rollbacklinkcount-morethan' )
1972  ->numParams( $wgShowRollbackEditCount )->parse();
1973  } else {
1974  $html = $context->msg( 'rollbacklinkcount' )->numParams( $editCount )->parse();
1975  }
1976 
1977  return self::link( $title, $html, $attrs, $query, $options );
1978  }
1979 
1980  $html = $context->msg( 'rollbacklink' )->escaped();
1981  return self::link( $title, $html, $attrs, $query, $options );
1982  }
1983 
1992  public static function formatHiddenCategories( $hiddencats ) {
1993  $outText = '';
1994  if ( count( $hiddencats ) > 0 ) {
1995  # Construct the HTML
1996  $outText = '<div class="mw-hiddenCategoriesExplanation">';
1997  $outText .= wfMessage( 'hiddencategories' )->numParams( count( $hiddencats ) )->parseAsBlock();
1998  $outText .= "</div><ul>\n";
1999 
2000  foreach ( $hiddencats as $titleObj ) {
2001  # If it's hidden, it must exist - no need to check with a LinkBatch
2002  $outText .= '<li>'
2003  . self::link( $titleObj, null, [], [], 'known' )
2004  . "</li>\n";
2005  }
2006  $outText .= '</ul>';
2007  }
2008  return $outText;
2009  }
2010 
2027  public static function titleAttrib( $name, $options = null, array $msgParams = [] ) {
2028  $message = wfMessage( "tooltip-$name", $msgParams );
2029  if ( !$message->exists() ) {
2030  $tooltip = false;
2031  } else {
2032  $tooltip = $message->text();
2033  # Compatibility: formerly some tooltips had [alt-.] hardcoded
2034  $tooltip = preg_replace( "/ ?\[alt-.\]$/", '', $tooltip );
2035  # Message equal to '-' means suppress it.
2036  if ( $tooltip == '-' ) {
2037  $tooltip = false;
2038  }
2039  }
2040 
2041  $options = (array)$options;
2042 
2043  if ( in_array( 'nonexisting', $options ) ) {
2044  $tooltip = wfMessage( 'red-link-title', $tooltip ?: '' )->text();
2045  }
2046  if ( in_array( 'withaccess', $options ) ) {
2047  $accesskey = self::accesskey( $name );
2048  if ( $accesskey !== false ) {
2049  // Should be build the same as in jquery.accessKeyLabel.js
2050  if ( $tooltip === false || $tooltip === '' ) {
2051  $tooltip = wfMessage( 'brackets', $accesskey )->text();
2052  } else {
2053  $tooltip .= wfMessage( 'word-separator' )->text();
2054  $tooltip .= wfMessage( 'brackets', $accesskey )->text();
2055  }
2056  }
2057  }
2058 
2059  return $tooltip;
2060  }
2061 
2062  public static $accesskeycache;
2063 
2075  public static function accesskey( $name ) {
2076  if ( isset( self::$accesskeycache[$name] ) ) {
2077  return self::$accesskeycache[$name];
2078  }
2079 
2080  $message = wfMessage( "accesskey-$name" );
2081 
2082  if ( !$message->exists() ) {
2083  $accesskey = false;
2084  } else {
2085  $accesskey = $message->plain();
2086  if ( $accesskey === '' || $accesskey === '-' ) {
2087  # @todo FIXME: Per standard MW behavior, a value of '-' means to suppress the
2088  # attribute, but this is broken for accesskey: that might be a useful
2089  # value.
2090  $accesskey = false;
2091  }
2092  }
2093 
2094  self::$accesskeycache[$name] = $accesskey;
2095  return self::$accesskeycache[$name];
2096  }
2097 
2111  public static function getRevDeleteLink( User $user, Revision $rev, LinkTarget $title ) {
2112  $canHide = $user->isAllowed( 'deleterevision' );
2113  if ( !$canHide && !( $rev->getVisibility() && $user->isAllowed( 'deletedhistory' ) ) ) {
2114  return '';
2115  }
2116 
2117  if ( !$rev->userCan( Revision::DELETED_RESTRICTED, $user ) ) {
2118  return self::revDeleteLinkDisabled( $canHide ); // revision was hidden from sysops
2119  }
2120  $prefixedDbKey = MediaWikiServices::getInstance()->getTitleFormatter()->
2121  getPrefixedDBkey( $title );
2122  if ( $rev->getId() ) {
2123  // RevDelete links using revision ID are stable across
2124  // page deletion and undeletion; use when possible.
2125  $query = [
2126  'type' => 'revision',
2127  'target' => $prefixedDbKey,
2128  'ids' => $rev->getId()
2129  ];
2130  } else {
2131  // Older deleted entries didn't save a revision ID.
2132  // We have to refer to these by timestamp, ick!
2133  $query = [
2134  'type' => 'archive',
2135  'target' => $prefixedDbKey,
2136  'ids' => $rev->getTimestamp()
2137  ];
2138  }
2139  return self::revDeleteLink( $query,
2140  $rev->isDeleted( Revision::DELETED_RESTRICTED ), $canHide );
2141  }
2142 
2153  public static function revDeleteLink( $query = [], $restricted = false, $delete = true ) {
2154  $sp = SpecialPage::getTitleFor( 'Revisiondelete' );
2155  $msgKey = $delete ? 'rev-delundel' : 'rev-showdeleted';
2156  $html = wfMessage( $msgKey )->escaped();
2157  $tag = $restricted ? 'strong' : 'span';
2158  $link = self::link( $sp, $html, [], $query, [ 'known', 'noclasses' ] );
2159  return Xml::tags(
2160  $tag,
2161  [ 'class' => 'mw-revdelundel-link' ],
2162  wfMessage( 'parentheses' )->rawParams( $link )->escaped()
2163  );
2164  }
2165 
2175  public static function revDeleteLinkDisabled( $delete = true ) {
2176  $msgKey = $delete ? 'rev-delundel' : 'rev-showdeleted';
2177  $html = wfMessage( $msgKey )->escaped();
2178  $htmlParentheses = wfMessage( 'parentheses' )->rawParams( $html )->escaped();
2179  return Xml::tags( 'span', [ 'class' => 'mw-revdelundel-link' ], $htmlParentheses );
2180  }
2181 
2194  public static function tooltipAndAccesskeyAttribs(
2195  $name,
2196  array $msgParams = [],
2197  $options = null
2198  ) {
2199  $options = (array)$options;
2200  $options[] = 'withaccess';
2201 
2202  $attribs = [
2203  'title' => self::titleAttrib( $name, $options, $msgParams ),
2204  'accesskey' => self::accesskey( $name )
2205  ];
2206  if ( $attribs['title'] === false ) {
2207  unset( $attribs['title'] );
2208  }
2209  if ( $attribs['accesskey'] === false ) {
2210  unset( $attribs['accesskey'] );
2211  }
2212  return $attribs;
2213  }
2214 
2222  public static function tooltip( $name, $options = null ) {
2223  $tooltip = self::titleAttrib( $name, $options );
2224  if ( $tooltip === false ) {
2225  return '';
2226  }
2227  return Xml::expandAttributes( [
2228  'title' => $tooltip
2229  ] );
2230  }
2231 
2232 }
static getTitleValueFor( $name, $subpage=false, $fragment='')
Get a localised TitleValue object for a specified special page name.
Definition: SpecialPage.php:98
The wiki should then use memcached to cache various data To use multiple just add more items to the array To increase the weight of a make its entry a array("192.168.0.1:11211", 2))
userCan( $field, User $user=null)
Determine if the current user is allowed to view a particular field of this revision, if it&#39;s marked as deleted.
Definition: Revision.php:1224
static makeMediaLinkObj( $title, $html='', $time=false)
Create a direct link to a given uploaded file.
Definition: Linker.php:764
const FOR_THIS_USER
Definition: Revision.php:55
static castFromLinkTarget( $linkTarget)
Same as newFromLinkTarget, but if passed null, returns null.
Definition: Title.php:295
static tooltip( $name, $options=null)
Returns raw bits of HTML, use titleAttrib()
Definition: Linker.php:2222
static tocLineEnd()
End a Table Of Contents line.
Definition: Linker.php:1662
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:1982
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
wfWarn( $msg, $callerOffset=1, $level=E_USER_NOTICE)
Send a warning either to the debug log or in a PHP error depending on $wgDevelopmentWarnings.
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&#39;t the only contributor and the revision we might rollback to isn&#39;t deleted).
Definition: Linker.php:1857
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:1585
static element( $element, $attribs=[], $contents='')
Identical to rawElement(), but HTML-escapes $contents (like Xml::element()).
Definition: Html.php:232
MalformedTitleException is thrown when a TitleParser is unable to parse a title string.
static getExternalLinkRel( $url=false, $title=null)
Get the rel attribute for a particular external link.
Definition: Parser.php:2044
static formatRevisionSize( $size)
Definition: Linker.php:1597
const NS_MAIN
Definition: Defines.php:60
$success
processing should stop and the error should be shown to the user * false
Definition: hooks.txt:187
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:1982
static normalizeSubpageLink( $contextTitle, $target, &$text)
Definition: Linker.php:1450
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
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:1982
getTimestamp()
Definition: Revision.php:994
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Definition: router.php:42
static makeBrokenImageLinkObj( $title, $label='', $query='', $unused1='', $unused2='', $time=false)
Make a "broken" link to an image.
Definition: Linker.php:682
div flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException' returning false will NOT prevent logging $e
Definition: hooks.txt:2159
static expandAttributes( $attribs)
Given an array of (&#39;attributename&#39; => &#39;value&#39;), it generates the code to set the XML attributes : att...
Definition: Xml.php:67
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:1776
if(!isset( $args[0])) $lang
static escapeRegexReplacement( $string)
Escape a string to make it suitable for inclusion in a preg_replace() replacement parameter...
static openElement( $element, $attribs=[])
Identical to rawElement(), but has no third parameter and omits the end tag (and the self-closing &#39;/&#39;...
Definition: Html.php:252
static rawElement( $element, $attribs=[], $contents='')
Returns an HTML element in a string.
Definition: Html.php:210
static normaliseSpecialPage(LinkTarget $target)
Definition: Linker.php:212
static fnamePart( $url)
Returns the filename part of an url.
Definition: Linker.php:232
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
static accesskey( $name)
Given the id of an interface element, constructs the appropriate accesskey attribute from the system ...
Definition: Linker.php:2075
static userToolLinksRedContribs( $userId, $userText, $edits=null, $useParentheses=true)
Alias for userToolLinks( $userId, $userText, true );.
Definition: Linker.php:1024
const NS_SPECIAL
Definition: Defines.php:49
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
wfLogWarning( $msg, $callerOffset=1, $level=E_USER_WARNING)
Send a warning as a PHP error and the debug log.
static makeMediaLinkFile(LinkTarget $title, $file, $html='')
Create a direct link to a given uploaded file.
Definition: Linker.php:783
null means default & $customAttribs
Definition: hooks.txt:1982
target page
static emailLink( $userId, $userText)
Definition: Linker.php:1079
static buildRollbackLink( $rev, IContextSource $context=null, $editCount=false)
Build a raw rollback link, useful for collections of "tool" links.
Definition: Linker.php:1919
static generateRollback( $rev, IContextSource $context=null, $options=[ 'verify'])
Generate a rollback link for a given revision.
Definition: Linker.php:1812
see documentation in includes Linker php for Linker::makeImageLink & $time
Definition: hooks.txt:1799
see documentation in includes Linker php for Linker::makeImageLink or false for current used if you return false $parser
Definition: hooks.txt:1799
This list may contain false positives That usually means there is additional text with links below the first Each row contains links to the first and second as well as the first line of the second redirect text
getNamespace()
Get the namespace index.
wfGetLangObj( $langcode=false)
Return a Language object from $langcode.
static formatHiddenCategories( $hiddencats)
Returns HTML for the "hidden categories on this page" list.
Definition: Linker.php:1992
This document provides an overview of the usage of PageUpdater and that is
Definition: pageupdater.txt:3
getFragment()
Get the link fragment (i.e.
isExternal()
Whether this LinkTarget has an interwiki component.
usually copyright or history_copyright This message must be in HTML not wikitext & $link
Definition: hooks.txt:3050
The User object encapsulates all of the user-specific settings (user_id, name, rights, email address, options, last login time).
Definition: User.php:51
$wgEnableUploads
Allow users to upload files.
static makeExternalImage( $url, $alt='')
Return the code for images which were added via external links, via Parser::maybeMakeExternalImage()...
Definition: Linker.php:252
wfAppendQuery( $url, $query)
Append a query string to an existing URL, which may or may not already have query string parameters a...
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:1298
static getForeignURL( $wikiID, $page, $fragmentId=null)
Convenience to get a url to a page on a foreign wiki.
Definition: WikiMap.php:171
getTitle()
Returns the title of the page associated with this entry.
Definition: Revision.php:755
$wgLang
Definition: Setup.php:931
either a unescaped string or a HtmlArmor object after in associative array form externallinks including delete and has completed for all link tables whether this was an auto creation use $formDescriptor instead default is conds Array Extra conditions for the No matching items in log is displayed if loglist is empty msgKey Array If you want a nice box with a set this to the key of the message First element is the message additional optional elements are parameters for the key that are processed with wfMessage() -> params() ->parseAsBlock() - offset Set to overwrite offset parameter in $wgRequest set to '' to unset offset - wrap String Wrap the message in html(usually something like "&lt
static getMain()
Get the RequestContext object associated with the main request.
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:1160
$wgUploadMissingFileUrl
Point the upload link for missing files to an external URL, as with $wgUploadNavigationUrl.
See &</td >< td > &Fill in a specific reason below(for example, citing particular pages that were vandalized).</td >< td >
wfCgiToArray( $query)
This is the logical opposite of wfArrayToCgi(): it accepts a query string as its argument and returns...
static makeSelfLinkObj( $nt, $html='', $query='', $trail='', $prefix='')
Make appropriate markup for a link to the current article.
Definition: Linker.php:168
static configuration should be added through ResourceLoaderGetConfigVars instead can be used to get the real title e g db for database replication lag or jobqueue for job queue size converted to pseudo seconds It is possible to add more fields and they will be returned to the user in the API response 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:2217
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:2027
Some internal bits split of from Skin.php.
Definition: Linker.php:34
isDeleted( $field)
Definition: Revision.php:888
getId()
Get revision ID.
Definition: Revision.php:638
$wgThumbUpright
Adjust width of upright images when parameter &#39;upright&#39; is used This allows a nicer look for upright ...
const NS_MEDIA
Definition: Defines.php:48
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:945
$res
Definition: database.txt:21
static singleton()
Definition: RepoGroup.php:60
$wgMiserMode
Disable database-intensive features.
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
const TOOL_LINKS_NOBLOCK
Flags for userToolLinks()
Definition: Linker.php:38
static formatAutocomments( $comment, $title=null, $local=false, $wikiId=null)
Converts autogenerated comments in edit summaries into section links.
Definition: Linker.php:1190
getDBkey()
Get the main part with underscores.
static tocLine( $anchor, $tocline, $tocnumber, $level, $sectionIndex=false)
parameter level defines if we are on an indentation level
Definition: Linker.php:1638
$params
isAllowed( $action='')
Internal mechanics of testing a permission.
Definition: User.php:3730
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped & $options
Definition: hooks.txt:1982
$wgThumbLimits
Adjust thumbnails on image pages according to a user setting.
static tags( $element, $attribs, $contents)
Same as Xml::element(), but does not escape contents.
Definition: Xml.php:130
static makeExternalLink( $url, $text, $escape=true, $linktype='', $attribs=[], $title=null)
Make an external link.
Definition: Linker.php:850
getComment( $audience=self::FOR_PUBLIC, User $user=null)
Definition: Revision.php:845
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:1982
const DELETED_RESTRICTED
Definition: Revision.php:49
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that When $user is not null
Definition: hooks.txt:780
getTargetLanguage()
Get the target language for the content being parsed.
Definition: Parser.php:992
static getUserLinkTitle( $userName)
Get a target Title to link a username.
namespace and then decline to actually register it file or subcat img or subcat $title
Definition: hooks.txt:925
static getQueryInfo( $options=[])
Return the tables, fields, and join conditions to be selected to create a new revision object...
Definition: Revision.php:511
const NS_FILE
Definition: Defines.php:66
static getTitleFor( $name, $subpage=false, $fragment='')
Get a localised Title object for a specified special page name If you don&#39;t need a full Title object...
Definition: SpecialPage.php:83
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:1766
static makeCommentLink(LinkTarget $linkTarget, $text, $wikiId=null, $options=[])
Generates a link to the given LinkTarget.
Definition: Linker.php:1420
const RAW
Definition: Revision.php:56
$wgDisableAnonTalk
Disable links to talk pages of anonymous users (IPs) in listings on special pages like page history...
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
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:3050
$wgSVGMaxSize
Don&#39;t scale a SVG larger than this.
const DELETED_TEXT
Definition: Revision.php:46
static getDefaultOption( $opt)
Get a given default option value.
Definition: User.php:1792
static userLink( $userId, $userName, $altUserName=false)
Make user link (or user contributions for unregistered users)
Definition: Linker.php:900
getTitle()
Get the Title object that we&#39;ll be acting on, as specified in the WebRequest.
Definition: MediaWiki.php:137
static makeTitleSafe( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:617
static newFromLinkTarget(LinkTarget $linkTarget, $forceClone='')
Returns a Title given a LinkTarget.
Definition: Title.php:271
getVisibility()
Get the deletion bitfield of the revision.
Definition: Revision.php:897
msg( $key)
This is the method for getting translated interface messages.
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
static revUserLink( $rev, $isPublic=false)
Generate a user link if the current user is allowed to view it.
Definition: Linker.php:1101
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 newFromId( $id)
Static factory method for creation from a given user ID.
Definition: User.php:618
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:308
static getRevDeleteLink(User $user, Revision $rev, LinkTarget $title)
Get a revision-deletion link, or disabled link, or nothing, depending on user permissions & the setti...
Definition: Linker.php:2111
$fallback
Definition: MessagesAb.php:11
const DELETED_USER
Definition: Revision.php:48
static makeHeadline( $level, $attribs, $anchor, $html, $link, $fallbackAnchor=false)
Create a headline for content.
Definition: Linker.php:1755
static userTalkLink( $userId, $userText)
Definition: Linker.php:1036
static prettifyIP( $ip)
Prettify an IP for display to end users.
Definition: IP.php:200
static linkKnown( $target, $html=null, $customAttribs=[], $query=[], $options=[ 'known'])
Identical to link(), except $options defaults to &#39;known&#39;.
Definition: Linker.php:146
static revDeleteLink( $query=[], $restricted=false, $delete=true)
Creates a (show/hide) link for deleting revisions/log entries.
Definition: Linker.php:2153
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Throws a warning that $function is deprecated.
$revQuery
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 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:1473
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:3050
do that in ParserLimitReportFormat instead use this to modify the parameters of the image all existing parser cache entries will be invalid To avoid you ll need to handle that somehow(e.g. with the RejectParserCacheValue hook) because MediaWiki won 't do it for you. & $defaults also a ContextSource after deleting those rows but within the same transaction you ll probably need to make sure the header is varied on and they can depend only on the ResourceLoaderContext $context
Definition: hooks.txt:2633
getText()
Returns the link in text form, without namespace prefix or fragment.
see documentation in includes Linker php for Linker::makeImageLink & $handlerParams
Definition: hooks.txt:1797
static tocIndent()
Add another level to the Table of Contents.
Definition: Linker.php:1612
static processResponsiveImages( $file, $thumb, $hp)
Process responsive images: add 1.5x and 2x subimages to the thumbnail, where applicable.
Definition: Linker.php:647
static revComment(Revision $rev, $local=false, $isPublic=false, $useParentheses=true)
Wrap and format the given revision&#39;s comment block, if the current user is allowed to view it...
Definition: Linker.php:1572
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:271
static tocList( $toc, $lang=null)
Wraps the TOC in a table and provides the hide/collapse javascript.
Definition: Linker.php:1675
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:824
static getImageLinkMTOParams( $frameParams, $query='', $parser=null)
Get the link parameters for MediaTransformOutput::toHtml() from given frame parameters supplied by th...
Definition: Linker.php:459
static escapeHtmlAllowEntities( $html)
Given HTML input, escape with htmlspecialchars but un-escape entities.
Definition: Sanitizer.php:1426
static getInvalidTitleDescription(IContextSource $context, $namespace, $title)
Get a message saying that an invalid title was encountered.
Definition: Linker.php:192
const DB_REPLICA
Definition: defines.php:25
$wgShowRollbackEditCount
The $wgShowRollbackEditCount variable is used to show how many edits can be rolled back...
if(! $wgRequest->checkUrlExtension()) if(isset( $_SERVER['PATH_INFO']) && $_SERVER['PATH_INFO'] !='') $wgTitle
Definition: api.php:57
const DELETED_COMMENT
Definition: Revision.php:47
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:498
static link( $target, $html=null, $customAttribs=[], $query=[], $options=[])
This function returns an HTML link to the given target.
Definition: Linker.php:84
static $accesskeycache
Definition: Linker.php:2062
static tooltipAndAccesskeyAttribs( $name, array $msgParams=[], $options=null)
Returns the attributes for the tooltip and access key.
Definition: Linker.php:2194
static guessSectionNameFromStrippedText( $text)
Like guessSectionNameFromWikiText(), but takes already-stripped text as input.
Definition: Parser.php:6118
$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:63
static makeThumbLink2(LinkTarget $title, $file, $frameParams=[], $handlerParams=[], $time=false, $query="")
Definition: Linker.php:524
static tocUnindent( $level)
Finish one or more sublevels on the Table of Contents.
Definition: Linker.php:1623
static commentBlock( $comment, $title=null, $local=false, $wikiId=null, $useParentheses=true)
Wrap a comment in standard punctuation and formatting if it&#39;s non-empty, otherwise return empty strin...
Definition: Linker.php:1542
static generateTOC( $tree, $lang=null)
Generate a table of contents from a section tree.
Definition: Linker.php:1717
return true to allow those checks to and false if checking is done & $user
Definition: hooks.txt:1473
static blockLink( $userId, $userText)
Definition: Linker.php:1058
static run( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:200
static revDeleteLinkDisabled( $delete=true)
Creates a dead (show/hide) link for deleting revisions/log entries.
Definition: Linker.php:2175
static getUploadUrl( $destFile, $query='')
Get the URL to upload a certain file.
Definition: Linker.php:735
const TOOL_LINKS_EMAIL
Definition: Linker.php:39
static isExternal( $username)
Tells whether the username is external or not.
static label( $label, $id, array $attribs=[])
Convenience function for generating a label for inputs.
Definition: Html.php:781
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:1124
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 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:1473
static newFromText( $text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
Definition: Title.php:319