MediaWiki  master
Linker.php
Go to the documentation of this file.
1 <?php
25 
35 class Linker {
39  const TOOL_LINKS_NOBLOCK = 1;
40  const TOOL_LINKS_EMAIL = 2;
41 
85  public static function link(
86  $target, $html = null, $customAttribs = [], $query = [], $options = []
87  ) {
88  if ( !$target instanceof LinkTarget ) {
89  wfWarn( __METHOD__ . ': Requires $target to be a LinkTarget object.', 2 );
90  return "<!-- ERROR -->$html";
91  }
92 
93  $services = MediaWikiServices::getInstance();
94  $options = (array)$options;
95  if ( $options ) {
96  // Custom options, create new LinkRenderer
97  if ( !isset( $options['stubThreshold'] ) ) {
98  $defaultLinkRenderer = $services->getLinkRenderer();
99  $options['stubThreshold'] = $defaultLinkRenderer->getStubThreshold();
100  }
101  $linkRenderer = $services->getLinkRendererFactory()
102  ->createFromLegacyOptions( $options );
103  } else {
104  $linkRenderer = $services->getLinkRenderer();
105  }
106 
107  if ( $html !== null ) {
108  $text = new HtmlArmor( $html );
109  } else {
110  $text = null;
111  }
112 
113  if ( in_array( 'known', $options, true ) ) {
114  return $linkRenderer->makeKnownLink( $target, $text, $customAttribs, $query );
115  }
116 
117  if ( in_array( 'broken', $options, true ) ) {
118  return $linkRenderer->makeBrokenLink( $target, $text, $customAttribs, $query );
119  }
120 
121  if ( in_array( 'noclasses', $options, true ) ) {
122  return $linkRenderer->makePreloadedLink( $target, $text, '', $customAttribs, $query );
123  }
124 
125  return $linkRenderer->makeLink( $target, $text, $customAttribs, $query );
126  }
127 
141  public static function linkKnown(
142  $target, $html = null, $customAttribs = [],
143  $query = [], $options = [ 'known' ]
144  ) {
145  return self::link( $target, $html, $customAttribs, $query, $options );
146  }
147 
163  public static function makeSelfLinkObj( $nt, $html = '', $query = '', $trail = '', $prefix = '' ) {
164  $nt = Title::newFromLinkTarget( $nt );
165  $ret = "<a class=\"mw-selflink selflink\">{$prefix}{$html}</a>{$trail}";
166  if ( !Hooks::run( 'SelfLinkBegin', [ $nt, &$html, &$trail, &$prefix, &$ret ] ) ) {
167  return $ret;
168  }
169 
170  if ( $html == '' ) {
171  $html = htmlspecialchars( $nt->getPrefixedText() );
172  }
173  list( $inside, $trail ) = self::splitTrail( $trail );
174  return "<a class=\"mw-selflink selflink\">{$prefix}{$html}{$inside}</a>{$trail}";
175  }
176 
187  public static function getInvalidTitleDescription( IContextSource $context, $namespace, $title ) {
188  // First we check whether the namespace exists or not.
189  if ( MediaWikiServices::getInstance()->getNamespaceInfo()->exists( $namespace ) ) {
190  if ( $namespace == NS_MAIN ) {
191  $name = $context->msg( 'blanknamespace' )->text();
192  } else {
193  $name = MediaWikiServices::getInstance()->getContentLanguage()->
194  getFormattedNsText( $namespace );
195  }
196  return $context->msg( 'invalidtitle-knownnamespace', $namespace, $name, $title )->text();
197  }
198 
199  return $context->msg( 'invalidtitle-unknownnamespace', $namespace, $title )->text();
200  }
201 
207  public static function normaliseSpecialPage( LinkTarget $target ) {
208  if ( $target->getNamespace() == NS_SPECIAL && !$target->isExternal() ) {
209  list( $name, $subpage ) = MediaWikiServices::getInstance()->getSpecialPageFactory()->
210  resolveAlias( $target->getDBkey() );
211  if ( $name ) {
212  return SpecialPage::getTitleValueFor( $name, $subpage, $target->getFragment() );
213  }
214  }
215 
216  return $target;
217  }
218 
227  private static function fnamePart( $url ) {
228  $basename = strrchr( $url, '/' );
229  if ( $basename === false ) {
230  $basename = $url;
231  } else {
232  $basename = substr( $basename, 1 );
233  }
234  return $basename;
235  }
236 
247  public static function makeExternalImage( $url, $alt = '' ) {
248  if ( $alt == '' ) {
249  $alt = self::fnamePart( $url );
250  }
251  $img = '';
252  $success = Hooks::run( 'LinkerMakeExternalImage', [ &$url, &$alt, &$img ] );
253  if ( !$success ) {
254  wfDebug( "Hook LinkerMakeExternalImage changed the output of external image "
255  . "with url {$url} and alt text {$alt} to {$img}\n", true );
256  return $img;
257  }
258  return Html::element( 'img',
259  [
260  'src' => $url,
261  'alt' => $alt
262  ]
263  );
264  }
265 
303  public static function makeImageLink( Parser $parser, LinkTarget $title,
304  $file, $frameParams = [], $handlerParams = [], $time = false,
305  $query = "", $widthOption = null
306  ) {
307  $title = Title::newFromLinkTarget( $title );
308  $res = null;
309  $dummy = new DummyLinker;
310  if ( !Hooks::run( 'ImageBeforeProduceHTML', [ &$dummy, &$title,
311  &$file, &$frameParams, &$handlerParams, &$time, &$res,
312  $parser, &$query, &$widthOption
313  ] ) ) {
314  return $res;
315  }
316 
317  if ( $file && !$file->allowInlineDisplay() ) {
318  wfDebug( __METHOD__ . ': ' . $title->getPrefixedDBkey() . " does not allow inline display\n" );
319  return self::link( $title );
320  }
321 
322  // Clean up parameters
323  $page = $handlerParams['page'] ?? false;
324  if ( !isset( $frameParams['align'] ) ) {
325  $frameParams['align'] = '';
326  }
327  if ( !isset( $frameParams['alt'] ) ) {
328  $frameParams['alt'] = '';
329  }
330  if ( !isset( $frameParams['title'] ) ) {
331  $frameParams['title'] = '';
332  }
333  if ( !isset( $frameParams['class'] ) ) {
334  $frameParams['class'] = '';
335  }
336 
337  $prefix = $postfix = '';
338 
339  if ( $frameParams['align'] == 'center' ) {
340  $prefix = '<div class="center">';
341  $postfix = '</div>';
342  $frameParams['align'] = 'none';
343  }
344  if ( $file && !isset( $handlerParams['width'] ) ) {
345  if ( isset( $handlerParams['height'] ) && $file->isVectorized() ) {
346  // If its a vector image, and user only specifies height
347  // we don't want it to be limited by its "normal" width.
348  global $wgSVGMaxSize;
349  $handlerParams['width'] = $wgSVGMaxSize;
350  } else {
351  $handlerParams['width'] = $file->getWidth( $page );
352  }
353 
354  if ( isset( $frameParams['thumbnail'] )
355  || isset( $frameParams['manualthumb'] )
356  || isset( $frameParams['framed'] )
357  || isset( $frameParams['frameless'] )
358  || !$handlerParams['width']
359  ) {
361 
362  if ( $widthOption === null || !isset( $wgThumbLimits[$widthOption] ) ) {
363  $widthOption = User::getDefaultOption( 'thumbsize' );
364  }
365 
366  // Reduce width for upright images when parameter 'upright' is used
367  if ( isset( $frameParams['upright'] ) && $frameParams['upright'] == 0 ) {
368  $frameParams['upright'] = $wgThumbUpright;
369  }
370 
371  // For caching health: If width scaled down due to upright
372  // parameter, round to full __0 pixel to avoid the creation of a
373  // lot of odd thumbs.
374  $prefWidth = isset( $frameParams['upright'] ) ?
375  round( $wgThumbLimits[$widthOption] * $frameParams['upright'], -1 ) :
376  $wgThumbLimits[$widthOption];
377 
378  // Use width which is smaller: real image width or user preference width
379  // Unless image is scalable vector.
380  if ( !isset( $handlerParams['height'] ) && ( $handlerParams['width'] <= 0 ||
381  $prefWidth < $handlerParams['width'] || $file->isVectorized() ) ) {
382  $handlerParams['width'] = $prefWidth;
383  }
384  }
385  }
386 
387  if ( isset( $frameParams['thumbnail'] ) || isset( $frameParams['manualthumb'] )
388  || isset( $frameParams['framed'] )
389  ) {
390  # Create a thumbnail. Alignment depends on the writing direction of
391  # the page content language (right-aligned for LTR languages,
392  # left-aligned for RTL languages)
393  # If a thumbnail width has not been provided, it is set
394  # to the default user option as specified in Language*.php
395  if ( $frameParams['align'] == '' ) {
396  $frameParams['align'] = $parser->getTargetLanguage()->alignEnd();
397  }
398  return $prefix .
399  self::makeThumbLink2( $title, $file, $frameParams, $handlerParams, $time, $query ) .
400  $postfix;
401  }
402 
403  if ( $file && isset( $frameParams['frameless'] ) ) {
404  $srcWidth = $file->getWidth( $page );
405  # For "frameless" option: do not present an image bigger than the
406  # source (for bitmap-style images). This is the same behavior as the
407  # "thumb" option does it already.
408  if ( $srcWidth && !$file->mustRender() && $handlerParams['width'] > $srcWidth ) {
409  $handlerParams['width'] = $srcWidth;
410  }
411  }
412 
413  if ( $file && isset( $handlerParams['width'] ) ) {
414  # Create a resized image, without the additional thumbnail features
415  $thumb = $file->transform( $handlerParams );
416  } else {
417  $thumb = false;
418  }
419 
420  if ( !$thumb ) {
421  $s = self::makeBrokenImageLinkObj( $title, $frameParams['title'], '', '', '', $time == true );
422  } else {
423  self::processResponsiveImages( $file, $thumb, $handlerParams );
424  $params = [
425  'alt' => $frameParams['alt'],
426  'title' => $frameParams['title'],
427  'valign' => $frameParams['valign'] ?? false,
428  'img-class' => $frameParams['class'] ];
429  if ( isset( $frameParams['border'] ) ) {
430  $params['img-class'] .= ( $params['img-class'] !== '' ? ' ' : '' ) . 'thumbborder';
431  }
432  $params = self::getImageLinkMTOParams( $frameParams, $query, $parser ) + $params;
433 
434  $s = $thumb->toHtml( $params );
435  }
436  if ( $frameParams['align'] != '' ) {
438  'div',
439  [ 'class' => 'float' . $frameParams['align'] ],
440  $s
441  );
442  }
443  return str_replace( "\n", ' ', $prefix . $s . $postfix );
444  }
445 
454  private static function getImageLinkMTOParams( $frameParams, $query = '', $parser = null ) {
455  $mtoParams = [];
456  if ( isset( $frameParams['link-url'] ) && $frameParams['link-url'] !== '' ) {
457  $mtoParams['custom-url-link'] = $frameParams['link-url'];
458  if ( isset( $frameParams['link-target'] ) ) {
459  $mtoParams['custom-target-link'] = $frameParams['link-target'];
460  }
461  if ( $parser ) {
462  $extLinkAttrs = $parser->getExternalLinkAttribs( $frameParams['link-url'] );
463  foreach ( $extLinkAttrs as $name => $val ) {
464  // Currently could include 'rel' and 'target'
465  $mtoParams['parser-extlink-' . $name] = $val;
466  }
467  }
468  } elseif ( isset( $frameParams['link-title'] ) && $frameParams['link-title'] !== '' ) {
469  $mtoParams['custom-title-link'] = Title::newFromLinkTarget(
470  self::normaliseSpecialPage( $frameParams['link-title'] )
471  );
472  } elseif ( !empty( $frameParams['no-link'] ) ) {
473  // No link
474  } else {
475  $mtoParams['desc-link'] = true;
476  $mtoParams['desc-query'] = $query;
477  }
478  return $mtoParams;
479  }
480 
493  public static function makeThumbLinkObj( LinkTarget $title, $file, $label = '', $alt = '',
494  $align = 'right', $params = [], $framed = false, $manualthumb = ""
495  ) {
496  $frameParams = [
497  'alt' => $alt,
498  'caption' => $label,
499  'align' => $align
500  ];
501  if ( $framed ) {
502  $frameParams['framed'] = true;
503  }
504  if ( $manualthumb ) {
505  $frameParams['manualthumb'] = $manualthumb;
506  }
507  return self::makeThumbLink2( $title, $file, $frameParams, $params );
508  }
509 
519  public static function makeThumbLink2( LinkTarget $title, $file, $frameParams = [],
520  $handlerParams = [], $time = false, $query = ""
521  ) {
522  $exists = $file && $file->exists();
523 
524  $page = $handlerParams['page'] ?? false;
525  if ( !isset( $frameParams['align'] ) ) {
526  $frameParams['align'] = 'right';
527  }
528  if ( !isset( $frameParams['alt'] ) ) {
529  $frameParams['alt'] = '';
530  }
531  if ( !isset( $frameParams['title'] ) ) {
532  $frameParams['title'] = '';
533  }
534  if ( !isset( $frameParams['caption'] ) ) {
535  $frameParams['caption'] = '';
536  }
537 
538  if ( empty( $handlerParams['width'] ) ) {
539  // Reduce width for upright images when parameter 'upright' is used
540  $handlerParams['width'] = isset( $frameParams['upright'] ) ? 130 : 180;
541  }
542  $thumb = false;
543  $noscale = false;
544  $manualthumb = false;
545 
546  if ( !$exists ) {
547  $outerWidth = $handlerParams['width'] + 2;
548  } else {
549  if ( isset( $frameParams['manualthumb'] ) ) {
550  # Use manually specified thumbnail
551  $manual_title = Title::makeTitleSafe( NS_FILE, $frameParams['manualthumb'] );
552  if ( $manual_title ) {
553  $manual_img = MediaWikiServices::getInstance()->getRepoGroup()
554  ->findFile( $manual_title );
555  if ( $manual_img ) {
556  $thumb = $manual_img->getUnscaledThumb( $handlerParams );
557  $manualthumb = true;
558  } else {
559  $exists = false;
560  }
561  }
562  } elseif ( isset( $frameParams['framed'] ) ) {
563  // Use image dimensions, don't scale
564  $thumb = $file->getUnscaledThumb( $handlerParams );
565  $noscale = true;
566  } else {
567  # Do not present an image bigger than the source, for bitmap-style images
568  # This is a hack to maintain compatibility with arbitrary pre-1.10 behavior
569  $srcWidth = $file->getWidth( $page );
570  if ( $srcWidth && !$file->mustRender() && $handlerParams['width'] > $srcWidth ) {
571  $handlerParams['width'] = $srcWidth;
572  }
573  $thumb = $file->transform( $handlerParams );
574  }
575 
576  if ( $thumb ) {
577  $outerWidth = $thumb->getWidth() + 2;
578  } else {
579  $outerWidth = $handlerParams['width'] + 2;
580  }
581  }
582 
583  # ThumbnailImage::toHtml() already adds page= onto the end of DjVu URLs
584  # So we don't need to pass it here in $query. However, the URL for the
585  # zoom icon still needs it, so we make a unique query for it. See T16771
586  $url = Title::newFromLinkTarget( $title )->getLocalURL( $query );
587  if ( $page ) {
588  $url = wfAppendQuery( $url, [ 'page' => $page ] );
589  }
590  if ( $manualthumb
591  && !isset( $frameParams['link-title'] )
592  && !isset( $frameParams['link-url'] )
593  && !isset( $frameParams['no-link'] ) ) {
594  $frameParams['link-url'] = $url;
595  }
596 
597  $s = "<div class=\"thumb t{$frameParams['align']}\">"
598  . "<div class=\"thumbinner\" style=\"width:{$outerWidth}px;\">";
599 
600  if ( !$exists ) {
601  $s .= self::makeBrokenImageLinkObj( $title, $frameParams['title'], '', '', '', $time == true );
602  $zoomIcon = '';
603  } elseif ( !$thumb ) {
604  $s .= wfMessage( 'thumbnail_error', '' )->escaped();
605  $zoomIcon = '';
606  } else {
607  if ( !$noscale && !$manualthumb ) {
608  self::processResponsiveImages( $file, $thumb, $handlerParams );
609  }
610  $params = [
611  'alt' => $frameParams['alt'],
612  'title' => $frameParams['title'],
613  'img-class' => ( isset( $frameParams['class'] ) && $frameParams['class'] !== ''
614  ? $frameParams['class'] . ' '
615  : '' ) . 'thumbimage'
616  ];
617  $params = self::getImageLinkMTOParams( $frameParams, $query ) + $params;
618  $s .= $thumb->toHtml( $params );
619  if ( isset( $frameParams['framed'] ) ) {
620  $zoomIcon = "";
621  } else {
622  $zoomIcon = Html::rawElement( 'div', [ 'class' => 'magnify' ],
623  Html::rawElement( 'a', [
624  'href' => $url,
625  'class' => 'internal',
626  'title' => wfMessage( 'thumbnail-more' )->text() ],
627  "" ) );
628  }
629  }
630  $s .= ' <div class="thumbcaption">' . $zoomIcon . $frameParams['caption'] . "</div></div></div>";
631  return str_replace( "\n", ' ', $s );
632  }
633 
642  public static function processResponsiveImages( $file, $thumb, $hp ) {
643  global $wgResponsiveImages;
644  if ( $wgResponsiveImages && $thumb && !$thumb->isError() ) {
645  $hp15 = $hp;
646  $hp15['width'] = round( $hp['width'] * 1.5 );
647  $hp20 = $hp;
648  $hp20['width'] = $hp['width'] * 2;
649  if ( isset( $hp['height'] ) ) {
650  $hp15['height'] = round( $hp['height'] * 1.5 );
651  $hp20['height'] = $hp['height'] * 2;
652  }
653 
654  $thumb15 = $file->transform( $hp15 );
655  $thumb20 = $file->transform( $hp20 );
656  if ( $thumb15 && !$thumb15->isError() && $thumb15->getUrl() !== $thumb->getUrl() ) {
657  $thumb->responsiveUrls['1.5'] = $thumb15->getUrl();
658  }
659  if ( $thumb20 && !$thumb20->isError() && $thumb20->getUrl() !== $thumb->getUrl() ) {
660  $thumb->responsiveUrls['2'] = $thumb20->getUrl();
661  }
662  }
663  }
664 
677  public static function makeBrokenImageLinkObj( $title, $label = '',
678  $query = '', $unused1 = '', $unused2 = '', $time = false
679  ) {
680  if ( !$title instanceof LinkTarget ) {
681  wfWarn( __METHOD__ . ': Requires $title to be a LinkTarget object.' );
682  return "<!-- ERROR -->" . htmlspecialchars( $label );
683  }
684 
686 
688  if ( $label == '' ) {
689  $label = $title->getPrefixedText();
690  }
691  $encLabel = htmlspecialchars( $label );
692  $currentExists = $time
693  && MediaWikiServices::getInstance()->getRepoGroup()->findFile( $title ) !== false;
694 
695  if ( ( $wgUploadMissingFileUrl || $wgUploadNavigationUrl || $wgEnableUploads )
696  && !$currentExists
697  ) {
698  $redir = RepoGroup::singleton()->getLocalRepo()->checkRedirect( $title );
699 
700  if ( $redir ) {
701  // We already know it's a redirect, so mark it
702  // accordingly
703  return self::link(
704  $title,
705  $encLabel,
706  [ 'class' => 'mw-redirect' ],
707  wfCgiToArray( $query ),
708  [ 'known', 'noclasses' ]
709  );
710  }
711 
712  $href = self::getUploadUrl( $title, $query );
713 
714  return '<a href="' . htmlspecialchars( $href ) . '" class="new" title="' .
715  htmlspecialchars( $title->getPrefixedText(), ENT_QUOTES ) . '">' .
716  $encLabel . '</a>';
717  }
718 
719  return self::link( $title, $encLabel, [], wfCgiToArray( $query ), [ 'known', 'noclasses' ] );
720  }
721 
730  protected static function getUploadUrl( $destFile, $query = '' ) {
732  $q = 'wpDestFile=' . Title::castFromLinkTarget( $destFile )->getPartialURL();
733  if ( $query != '' ) {
734  $q .= '&' . $query;
735  }
736 
737  if ( $wgUploadMissingFileUrl ) {
738  return wfAppendQuery( $wgUploadMissingFileUrl, $q );
739  }
740 
741  if ( $wgUploadNavigationUrl ) {
742  return wfAppendQuery( $wgUploadNavigationUrl, $q );
743  }
744 
745  $upload = SpecialPage::getTitleFor( 'Upload' );
746 
747  return $upload->getLocalURL( $q );
748  }
749 
759  public static function makeMediaLinkObj( $title, $html = '', $time = false ) {
760  $img = MediaWikiServices::getInstance()->getRepoGroup()->findFile(
761  $title, [ 'time' => $time ]
762  );
763  return self::makeMediaLinkFile( $title, $img, $html );
764  }
765 
778  public static function makeMediaLinkFile( LinkTarget $title, $file, $html = '' ) {
779  if ( $file && $file->exists() ) {
780  $url = $file->getUrl();
781  $class = 'internal';
782  } else {
783  $url = self::getUploadUrl( $title );
784  $class = 'new';
785  }
786 
787  $alt = $title->getText();
788  if ( $html == '' ) {
789  $html = $alt;
790  }
791 
792  $ret = '';
793  $attribs = [
794  'href' => $url,
795  'class' => $class,
796  'title' => $alt
797  ];
798 
799  if ( !Hooks::run( 'LinkerMakeMediaLinkFile',
800  [ Title::castFromLinkTarget( $title ), $file, &$html, &$attribs, &$ret ] ) ) {
801  wfDebug( "Hook LinkerMakeMediaLinkFile changed the output of link "
802  . "with url {$url} and text {$html} to {$ret}\n", true );
803  return $ret;
804  }
805 
806  return Html::rawElement( 'a', $attribs, $html );
807  }
808 
819  public static function specialLink( $name, $key = '' ) {
820  if ( $key == '' ) {
821  $key = strtolower( $name );
822  }
823 
824  return self::linkKnown( SpecialPage::getTitleFor( $name ), wfMessage( $key )->escaped() );
825  }
826 
845  public static function makeExternalLink( $url, $text, $escape = true,
846  $linktype = '', $attribs = [], $title = null
847  ) {
848  global $wgTitle;
849  $class = "external";
850  if ( $linktype ) {
851  $class .= " $linktype";
852  }
853  if ( isset( $attribs['class'] ) && $attribs['class'] ) {
854  $class .= " {$attribs['class']}";
855  }
856  $attribs['class'] = $class;
857 
858  if ( $escape ) {
859  $text = htmlspecialchars( $text );
860  }
861 
862  if ( !$title ) {
863  $title = $wgTitle;
864  }
865  $newRel = Parser::getExternalLinkRel( $url, $title );
866  if ( !isset( $attribs['rel'] ) || $attribs['rel'] === '' ) {
867  $attribs['rel'] = $newRel;
868  } elseif ( $newRel !== '' ) {
869  // Merge the rel attributes.
870  $newRels = explode( ' ', $newRel );
871  $oldRels = explode( ' ', $attribs['rel'] );
872  $combined = array_unique( array_merge( $newRels, $oldRels ) );
873  $attribs['rel'] = implode( ' ', $combined );
874  }
875  $link = '';
876  $success = Hooks::run( 'LinkerMakeExternalLink',
877  [ &$url, &$text, &$link, &$attribs, $linktype ] );
878  if ( !$success ) {
879  wfDebug( "Hook LinkerMakeExternalLink changed the output of link "
880  . "with url {$url} and text {$text} to {$link}\n", true );
881  return $link;
882  }
883  $attribs['href'] = $url;
884  return Html::rawElement( 'a', $attribs, $text );
885  }
886 
895  public static function userLink( $userId, $userName, $altUserName = false ) {
896  if ( $userName === '' || $userName === false || $userName === null ) {
897  wfDebug( __METHOD__ . ' received an empty username. Are there database errors ' .
898  'that need to be fixed?' );
899  return wfMessage( 'empty-username' )->parse();
900  }
901 
902  $classes = 'mw-userlink';
903  $page = null;
904  if ( $userId == 0 ) {
905  $page = ExternalUserNames::getUserLinkTitle( $userName );
906 
907  if ( ExternalUserNames::isExternal( $userName ) ) {
908  $classes .= ' mw-extuserlink';
909  } elseif ( $altUserName === false ) {
910  $altUserName = IP::prettifyIP( $userName );
911  }
912  $classes .= ' mw-anonuserlink'; // Separate link class for anons (T45179)
913  } else {
914  $page = new TitleValue( NS_USER, strtr( $userName, ' ', '_' ) );
915  }
916 
917  // Wrap the output with <bdi> tags for directionality isolation
918  $linkText =
919  '<bdi>' . htmlspecialchars( $altUserName !== false ? $altUserName : $userName ) . '</bdi>';
920 
921  return $page
922  ? self::link( $page, $linkText, [ 'class' => $classes ] )
923  : Html::rawElement( 'span', [ 'class' => $classes ], $linkText );
924  }
925 
940  public static function userToolLinks(
941  $userId, $userText, $redContribsWhenNoEdits = false, $flags = 0, $edits = null,
942  $useParentheses = true
943  ) {
944  if ( $userText === '' ) {
945  wfDebug( __METHOD__ . ' received an empty username. Are there database errors ' .
946  'that need to be fixed?' );
947  return ' ' . wfMessage( 'empty-username' )->parse();
948  }
949 
950  global $wgUser, $wgDisableAnonTalk, $wgLang;
951  $talkable = !( $wgDisableAnonTalk && $userId == 0 );
952  $blockable = !( $flags & self::TOOL_LINKS_NOBLOCK );
953  $addEmailLink = $flags & self::TOOL_LINKS_EMAIL && $userId;
954 
955  if ( $userId == 0 && ExternalUserNames::isExternal( $userText ) ) {
956  // No tools for an external user
957  return '';
958  }
959 
960  $items = [];
961  if ( $talkable ) {
962  $items[] = self::userTalkLink( $userId, $userText );
963  }
964  if ( $userId ) {
965  // check if the user has an edit
966  $attribs = [];
967  $attribs['class'] = 'mw-usertoollinks-contribs';
968  if ( $redContribsWhenNoEdits ) {
969  if ( intval( $edits ) === 0 && $edits !== 0 ) {
970  $user = User::newFromId( $userId );
971  $edits = $user->getEditCount();
972  }
973  if ( $edits === 0 ) {
974  $attribs['class'] .= ' new';
975  }
976  }
977  $contribsPage = SpecialPage::getTitleFor( 'Contributions', $userText );
978 
979  $items[] = self::link( $contribsPage, wfMessage( 'contribslink' )->escaped(), $attribs );
980  }
981  if ( $blockable && $wgUser->isAllowed( 'block' ) ) {
982  $items[] = self::blockLink( $userId, $userText );
983  }
984 
985  if ( $addEmailLink && $wgUser->canSendEmail() ) {
986  $items[] = self::emailLink( $userId, $userText );
987  }
988 
989  Hooks::run( 'UserToolLinksEdit', [ $userId, $userText, &$items ] );
990 
991  if ( !$items ) {
992  return '';
993  }
994 
995  if ( $useParentheses ) {
996  return wfMessage( 'word-separator' )->escaped()
997  . '<span class="mw-usertoollinks">'
998  . wfMessage( 'parentheses' )->rawParams( $wgLang->pipeList( $items ) )->escaped()
999  . '</span>';
1000  }
1001 
1002  $tools = [];
1003  foreach ( $items as $tool ) {
1004  $tools[] = Html::rawElement( 'span', [], $tool );
1005  }
1006  return ' <span class="mw-usertoollinks mw-changeslist-links">' .
1007  implode( ' ', $tools ) . '</span>';
1008  }
1009 
1019  public static function userToolLinksRedContribs(
1020  $userId, $userText, $edits = null, $useParentheses = true
1021  ) {
1022  return self::userToolLinks( $userId, $userText, true, 0, $edits, $useParentheses );
1023  }
1024 
1031  public static function userTalkLink( $userId, $userText ) {
1032  if ( $userText === '' ) {
1033  wfDebug( __METHOD__ . ' received an empty username. Are there database errors ' .
1034  'that need to be fixed?' );
1035  return wfMessage( 'empty-username' )->parse();
1036  }
1037 
1038  $userTalkPage = new TitleValue( NS_USER_TALK, strtr( $userText, ' ', '_' ) );
1039  $moreLinkAttribs['class'] = 'mw-usertoollinks-talk';
1040 
1041  return self::link( $userTalkPage,
1042  wfMessage( 'talkpagelinktext' )->escaped(),
1043  $moreLinkAttribs
1044  );
1045  }
1046 
1053  public static function blockLink( $userId, $userText ) {
1054  if ( $userText === '' ) {
1055  wfDebug( __METHOD__ . ' received an empty username. Are there database errors ' .
1056  'that need to be fixed?' );
1057  return wfMessage( 'empty-username' )->parse();
1058  }
1059 
1060  $blockPage = SpecialPage::getTitleFor( 'Block', $userText );
1061  $moreLinkAttribs['class'] = 'mw-usertoollinks-block';
1062 
1063  return self::link( $blockPage,
1064  wfMessage( 'blocklink' )->escaped(),
1065  $moreLinkAttribs
1066  );
1067  }
1068 
1074  public static function emailLink( $userId, $userText ) {
1075  if ( $userText === '' ) {
1076  wfLogWarning( __METHOD__ . ' received an empty username. Are there database errors ' .
1077  'that need to be fixed?' );
1078  return wfMessage( 'empty-username' )->parse();
1079  }
1080 
1081  $emailPage = SpecialPage::getTitleFor( 'Emailuser', $userText );
1082  $moreLinkAttribs['class'] = 'mw-usertoollinks-mail';
1083  return self::link( $emailPage,
1084  wfMessage( 'emaillink' )->escaped(),
1085  $moreLinkAttribs
1086  );
1087  }
1088 
1096  public static function revUserLink( $rev, $isPublic = false ) {
1097  if ( $rev->isDeleted( RevisionRecord::DELETED_USER ) && $isPublic ) {
1098  $link = wfMessage( 'rev-deleted-user' )->escaped();
1099  } elseif ( $rev->userCan( RevisionRecord::DELETED_USER ) ) {
1100  $link = self::userLink( $rev->getUser( RevisionRecord::FOR_THIS_USER ),
1101  $rev->getUserText( RevisionRecord::FOR_THIS_USER ) );
1102  } else {
1103  $link = wfMessage( 'rev-deleted-user' )->escaped();
1104  }
1105  if ( $rev->isDeleted( RevisionRecord::DELETED_USER ) ) {
1106  return '<span class="history-deleted">' . $link . '</span>';
1107  }
1108  return $link;
1109  }
1110 
1119  public static function revUserTools( $rev, $isPublic = false, $useParentheses = true ) {
1120  if ( $rev->userCan( RevisionRecord::DELETED_USER ) &&
1121  ( !$rev->isDeleted( RevisionRecord::DELETED_USER ) || !$isPublic )
1122  ) {
1123  $userId = $rev->getUser( RevisionRecord::FOR_THIS_USER );
1124  $userText = $rev->getUserText( RevisionRecord::FOR_THIS_USER );
1125  if ( $userId || (string)$userText !== '' ) {
1126  $link = self::userLink( $userId, $userText )
1127  . self::userToolLinks( $userId, $userText, false, 0, null,
1128  $useParentheses );
1129  }
1130  }
1131 
1132  if ( !isset( $link ) ) {
1133  $link = wfMessage( 'rev-deleted-user' )->escaped();
1134  }
1135 
1136  if ( $rev->isDeleted( RevisionRecord::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  }
1250  $auto = Linker::makeCommentLink(
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( RevisionRecord::RAW ) == "" ) {
1576  return "";
1577  }
1578  if ( $rev->isDeleted( RevisionRecord::DELETED_COMMENT ) && $isPublic ) {
1579  $block = " <span class=\"comment\">" . wfMessage( 'rev-deleted-comment' )->escaped() . "</span>";
1580  } elseif ( $rev->userCan( RevisionRecord::DELETED_COMMENT ) ) {
1581  $block = self::commentBlock( $rev->getComment( RevisionRecord::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( RevisionRecord::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 
1674  public static function tocList( $toc, Language $lang = null ) {
1675  $lang = $lang ?? RequestContext::getMain()->getLanguage();
1676 
1677  $title = wfMessage( 'toc' )->inLanguage( $lang )->escaped();
1678 
1679  return '<div id="toc" class="toc">'
1680  . Html::element( 'input', [
1681  'type' => 'checkbox',
1682  'role' => 'button',
1683  'id' => 'toctogglecheckbox',
1684  'class' => 'toctogglecheckbox',
1685  'style' => 'display:none',
1686  ] )
1687  . Html::openElement( 'div', [
1688  'class' => 'toctitle',
1689  'lang' => $lang->getHtmlCode(),
1690  'dir' => $lang->getDir(),
1691  ] )
1692  . "<h2>$title</h2>"
1693  . '<span class="toctogglespan">'
1694  . Html::label( '', 'toctogglecheckbox', [
1695  'class' => 'toctogglelabel',
1696  ] )
1697  . '</span>'
1698  . "</div>\n"
1699  . $toc
1700  . "</ul>\n</div>\n";
1701  }
1702 
1711  public static function generateTOC( $tree, Language $lang = null ) {
1712  $toc = '';
1713  $lastLevel = 0;
1714  foreach ( $tree as $section ) {
1715  if ( $section['toclevel'] > $lastLevel ) {
1716  $toc .= self::tocIndent();
1717  } elseif ( $section['toclevel'] < $lastLevel ) {
1718  $toc .= self::tocUnindent(
1719  $lastLevel - $section['toclevel'] );
1720  } else {
1721  $toc .= self::tocLineEnd();
1722  }
1723 
1724  $toc .= self::tocLine( $section['anchor'],
1725  $section['line'], $section['number'],
1726  $section['toclevel'], $section['index'] );
1727  $lastLevel = $section['toclevel'];
1728  }
1729  $toc .= self::tocLineEnd();
1730  return self::tocList( $toc, $lang );
1731  }
1732 
1749  public static function makeHeadline( $level, $attribs, $anchor, $html,
1750  $link, $fallbackAnchor = false
1751  ) {
1752  $anchorEscaped = htmlspecialchars( $anchor );
1753  $fallback = '';
1754  if ( $fallbackAnchor !== false && $fallbackAnchor !== $anchor ) {
1755  $fallbackAnchor = htmlspecialchars( $fallbackAnchor );
1756  $fallback = "<span id=\"$fallbackAnchor\"></span>";
1757  }
1758  return "<h$level$attribs"
1759  . "$fallback<span class=\"mw-headline\" id=\"$anchorEscaped\">$html</span>"
1760  . $link
1761  . "</h$level>";
1762  }
1763 
1770  static function splitTrail( $trail ) {
1771  $regex = MediaWikiServices::getInstance()->getContentLanguage()->linkTrail();
1772  $inside = '';
1773  if ( $trail !== '' && preg_match( $regex, $trail, $m ) ) {
1774  list( , $inside, $trail ) = $m;
1775  }
1776  return [ $inside, $trail ];
1777  }
1778 
1806  public static function generateRollback( $rev, IContextSource $context = null,
1807  $options = [ 'verify' ]
1808  ) {
1809  if ( $context === null ) {
1811  }
1812 
1813  $editCount = false;
1814  if ( in_array( 'verify', $options, true ) ) {
1815  $editCount = self::getRollbackEditCount( $rev, true );
1816  if ( $editCount === false ) {
1817  return '';
1818  }
1819  }
1820 
1821  $inner = self::buildRollbackLink( $rev, $context, $editCount );
1822 
1823  if ( !in_array( 'noBrackets', $options, true ) ) {
1824  $inner = $context->msg( 'brackets' )->rawParams( $inner )->escaped();
1825  }
1826 
1827  if ( $context->getUser()->getBoolOption( 'showrollbackconfirmation' ) ) {
1828  $stats = MediaWikiServices::getInstance()->getStatsdDataFactory();
1829  $stats->increment( 'rollbackconfirmation.event.load' );
1830  $context->getOutput()->addModules( 'mediawiki.page.rollback.confirmation' );
1831  }
1832 
1833  return '<span class="mw-rollback-link">' . $inner . '</span>';
1834  }
1835 
1851  public static function getRollbackEditCount( $rev, $verify ) {
1852  global $wgShowRollbackEditCount;
1853  if ( !is_int( $wgShowRollbackEditCount ) || !$wgShowRollbackEditCount > 0 ) {
1854  // Nothing has happened, indicate this by returning 'null'
1855  return null;
1856  }
1857 
1858  $dbr = wfGetDB( DB_REPLICA );
1859 
1860  // Up to the value of $wgShowRollbackEditCount revisions are counted
1862  $res = $dbr->select(
1863  $revQuery['tables'],
1864  [ 'rev_user_text' => $revQuery['fields']['rev_user_text'], 'rev_deleted' ],
1865  // $rev->getPage() returns null sometimes
1866  [ 'rev_page' => $rev->getTitle()->getArticleID() ],
1867  __METHOD__,
1868  [
1869  'USE INDEX' => [ 'revision' => 'page_timestamp' ],
1870  'ORDER BY' => 'rev_timestamp DESC',
1871  'LIMIT' => $wgShowRollbackEditCount + 1
1872  ],
1873  $revQuery['joins']
1874  );
1875 
1876  $editCount = 0;
1877  $moreRevs = false;
1878  foreach ( $res as $row ) {
1879  if ( $rev->getUserText( RevisionRecord::RAW ) != $row->rev_user_text ) {
1880  if ( $verify &&
1881  ( $row->rev_deleted & RevisionRecord::DELETED_TEXT
1882  || $row->rev_deleted & RevisionRecord::DELETED_USER
1883  ) ) {
1884  // If the user or the text of the revision we might rollback
1885  // to is deleted in some way we can't rollback. Similar to
1886  // the sanity checks in WikiPage::commitRollback.
1887  return false;
1888  }
1889  $moreRevs = true;
1890  break;
1891  }
1892  $editCount++;
1893  }
1894 
1895  if ( $verify && $editCount <= $wgShowRollbackEditCount && !$moreRevs ) {
1896  // We didn't find at least $wgShowRollbackEditCount revisions made by the current user
1897  // and there weren't any other revisions. That means that the current user is the only
1898  // editor, so we can't rollback
1899  return false;
1900  }
1901  return $editCount;
1902  }
1903 
1913  public static function buildRollbackLink( $rev, IContextSource $context = null,
1914  $editCount = false
1915  ) {
1917 
1918  // To config which pages are affected by miser mode
1919  $disableRollbackEditCountSpecialPage = [ 'Recentchanges', 'Watchlist' ];
1920 
1921  if ( $context === null ) {
1923  }
1924 
1925  $title = $rev->getTitle();
1926 
1927  $query = [
1928  'action' => 'rollback',
1929  'from' => $rev->getUserText(),
1930  'token' => $context->getUser()->getEditToken( 'rollback' ),
1931  ];
1932 
1933  $attrs = [
1934  'data-mw' => 'interface',
1935  'title' => $context->msg( 'tooltip-rollback' )->text()
1936  ];
1937 
1938  $options = [ 'known', 'noclasses' ];
1939 
1940  if ( $context->getRequest()->getBool( 'bot' ) ) {
1941  //T17999
1942  $query['hidediff'] = '1';
1943  $query['bot'] = '1';
1944  }
1945 
1946  $disableRollbackEditCount = false;
1947  if ( $wgMiserMode ) {
1948  foreach ( $disableRollbackEditCountSpecialPage as $specialPage ) {
1949  if ( $context->getTitle()->isSpecial( $specialPage ) ) {
1950  $disableRollbackEditCount = true;
1951  break;
1952  }
1953  }
1954  }
1955 
1956  if ( !$disableRollbackEditCount
1957  && is_int( $wgShowRollbackEditCount )
1958  && $wgShowRollbackEditCount > 0
1959  ) {
1960  if ( !is_numeric( $editCount ) ) {
1961  $editCount = self::getRollbackEditCount( $rev, false );
1962  }
1963 
1964  if ( $editCount > $wgShowRollbackEditCount ) {
1965  $html = $context->msg( 'rollbacklinkcount-morethan' )
1966  ->numParams( $wgShowRollbackEditCount )->parse();
1967  } else {
1968  $html = $context->msg( 'rollbacklinkcount' )->numParams( $editCount )->parse();
1969  }
1970 
1971  return self::link( $title, $html, $attrs, $query, $options );
1972  }
1973 
1974  $html = $context->msg( 'rollbacklink' )->escaped();
1975  return self::link( $title, $html, $attrs, $query, $options );
1976  }
1977 
1986  public static function formatHiddenCategories( $hiddencats ) {
1987  $outText = '';
1988  if ( count( $hiddencats ) > 0 ) {
1989  # Construct the HTML
1990  $outText = '<div class="mw-hiddenCategoriesExplanation">';
1991  $outText .= wfMessage( 'hiddencategories' )->numParams( count( $hiddencats ) )->parseAsBlock();
1992  $outText .= "</div><ul>\n";
1993 
1994  foreach ( $hiddencats as $titleObj ) {
1995  # If it's hidden, it must exist - no need to check with a LinkBatch
1996  $outText .= '<li>'
1997  . self::link( $titleObj, null, [], [], 'known' )
1998  . "</li>\n";
1999  }
2000  $outText .= '</ul>';
2001  }
2002  return $outText;
2003  }
2004 
2021  public static function titleAttrib( $name, $options = null, array $msgParams = [] ) {
2022  $message = wfMessage( "tooltip-$name", $msgParams );
2023  if ( !$message->exists() ) {
2024  $tooltip = false;
2025  } else {
2026  $tooltip = $message->text();
2027  # Compatibility: formerly some tooltips had [alt-.] hardcoded
2028  $tooltip = preg_replace( "/ ?\[alt-.\]$/", '', $tooltip );
2029  # Message equal to '-' means suppress it.
2030  if ( $tooltip == '-' ) {
2031  $tooltip = false;
2032  }
2033  }
2034 
2035  $options = (array)$options;
2036 
2037  if ( in_array( 'nonexisting', $options ) ) {
2038  $tooltip = wfMessage( 'red-link-title', $tooltip ?: '' )->text();
2039  }
2040  if ( in_array( 'withaccess', $options ) ) {
2041  $accesskey = self::accesskey( $name );
2042  if ( $accesskey !== false ) {
2043  // Should be build the same as in jquery.accessKeyLabel.js
2044  if ( $tooltip === false || $tooltip === '' ) {
2045  $tooltip = wfMessage( 'brackets', $accesskey )->text();
2046  } else {
2047  $tooltip .= wfMessage( 'word-separator' )->text();
2048  $tooltip .= wfMessage( 'brackets', $accesskey )->text();
2049  }
2050  }
2051  }
2052 
2053  return $tooltip;
2054  }
2055 
2056  public static $accesskeycache;
2057 
2069  public static function accesskey( $name ) {
2070  if ( isset( self::$accesskeycache[$name] ) ) {
2071  return self::$accesskeycache[$name];
2072  }
2073 
2074  $message = wfMessage( "accesskey-$name" );
2075 
2076  if ( !$message->exists() ) {
2077  $accesskey = false;
2078  } else {
2079  $accesskey = $message->plain();
2080  if ( $accesskey === '' || $accesskey === '-' ) {
2081  # @todo FIXME: Per standard MW behavior, a value of '-' means to suppress the
2082  # attribute, but this is broken for accesskey: that might be a useful
2083  # value.
2084  $accesskey = false;
2085  }
2086  }
2087 
2088  self::$accesskeycache[$name] = $accesskey;
2089  return self::$accesskeycache[$name];
2090  }
2091 
2105  public static function getRevDeleteLink( User $user, Revision $rev, LinkTarget $title ) {
2106  $canHide = $user->isAllowed( 'deleterevision' );
2107  if ( !$canHide && !( $rev->getVisibility() && $user->isAllowed( 'deletedhistory' ) ) ) {
2108  return '';
2109  }
2110 
2111  if ( !$rev->userCan( RevisionRecord::DELETED_RESTRICTED, $user ) ) {
2112  return self::revDeleteLinkDisabled( $canHide ); // revision was hidden from sysops
2113  }
2114  $prefixedDbKey = MediaWikiServices::getInstance()->getTitleFormatter()->
2115  getPrefixedDBkey( $title );
2116  if ( $rev->getId() ) {
2117  // RevDelete links using revision ID are stable across
2118  // page deletion and undeletion; use when possible.
2119  $query = [
2120  'type' => 'revision',
2121  'target' => $prefixedDbKey,
2122  'ids' => $rev->getId()
2123  ];
2124  } else {
2125  // Older deleted entries didn't save a revision ID.
2126  // We have to refer to these by timestamp, ick!
2127  $query = [
2128  'type' => 'archive',
2129  'target' => $prefixedDbKey,
2130  'ids' => $rev->getTimestamp()
2131  ];
2132  }
2133  return self::revDeleteLink( $query,
2134  $rev->isDeleted( RevisionRecord::DELETED_RESTRICTED ), $canHide );
2135  }
2136 
2147  public static function revDeleteLink( $query = [], $restricted = false, $delete = true ) {
2148  $sp = SpecialPage::getTitleFor( 'Revisiondelete' );
2149  $msgKey = $delete ? 'rev-delundel' : 'rev-showdeleted';
2150  $html = wfMessage( $msgKey )->escaped();
2151  $tag = $restricted ? 'strong' : 'span';
2152  $link = self::link( $sp, $html, [], $query, [ 'known', 'noclasses' ] );
2153  return Xml::tags(
2154  $tag,
2155  [ 'class' => 'mw-revdelundel-link' ],
2156  wfMessage( 'parentheses' )->rawParams( $link )->escaped()
2157  );
2158  }
2159 
2169  public static function revDeleteLinkDisabled( $delete = true ) {
2170  $msgKey = $delete ? 'rev-delundel' : 'rev-showdeleted';
2171  $html = wfMessage( $msgKey )->escaped();
2172  $htmlParentheses = wfMessage( 'parentheses' )->rawParams( $html )->escaped();
2173  return Xml::tags( 'span', [ 'class' => 'mw-revdelundel-link' ], $htmlParentheses );
2174  }
2175 
2188  public static function tooltipAndAccesskeyAttribs(
2189  $name,
2190  array $msgParams = [],
2191  $options = null
2192  ) {
2193  $options = (array)$options;
2194  $options[] = 'withaccess';
2195 
2196  $attribs = [
2197  'title' => self::titleAttrib( $name, $options, $msgParams ),
2198  'accesskey' => self::accesskey( $name )
2199  ];
2200  if ( $attribs['title'] === false ) {
2201  unset( $attribs['title'] );
2202  }
2203  if ( $attribs['accesskey'] === false ) {
2204  unset( $attribs['accesskey'] );
2205  }
2206  return $attribs;
2207  }
2208 
2216  public static function tooltip( $name, $options = null ) {
2217  $tooltip = self::titleAttrib( $name, $options );
2218  if ( $tooltip === false ) {
2219  return '';
2220  }
2221  return Xml::expandAttributes( [
2222  'title' => $tooltip
2223  ] );
2224  }
2225 
2226 }
static getTitleValueFor( $name, $subpage=false, $fragment='')
Get a localised TitleValue object for a specified special page name.
Definition: SpecialPage.php:98
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:759
static castFromLinkTarget( $linkTarget)
Same as newFromLinkTarget, but if passed null, returns null.
Definition: Title.php:298
static tooltip( $name, $options=null)
Returns raw bits of HTML, use titleAttrib()
Definition: Linker.php:2216
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:1972
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:1851
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 $groups Array of ChangesListFilterGroup objects(added in 1.34) 'FileDeleteComplete' 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:1529
static element( $element, $attribs=[], $contents='')
Identical to rawElement(), but HTML-escapes $contents (like Xml::element()).
Definition: Html.php:231
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:2075
static formatRevisionSize( $size)
Definition: Linker.php:1597
const NS_MAIN
Definition: Defines.php:60
$success
static generateTOC( $tree, Language $lang=null)
Generate a table of contents from a section tree.
Definition: Linker.php:1711
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:1972
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:1972
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:677
div flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException' returning false will NOT prevent logging $e
Definition: hooks.txt:2147
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:1770
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:251
static rawElement( $element, $attribs=[], $contents='')
Returns an HTML element in a string.
Definition: Html.php:209
static normaliseSpecialPage(LinkTarget $target)
Definition: Linker.php:207
static fnamePart( $url)
Returns the filename part of an url.
Definition: Linker.php:227
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:2069
static userToolLinksRedContribs( $userId, $userText, $edits=null, $useParentheses=true)
Alias for userToolLinks( $userId, $userText, true );.
Definition: Linker.php:1019
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:778
null means default & $customAttribs
Definition: hooks.txt:1972
target page
static emailLink( $userId, $userText)
Definition: Linker.php:1074
static buildRollbackLink( $rev, IContextSource $context=null, $editCount=false)
Build a raw rollback link, useful for collections of "tool" links.
Definition: Linker.php:1913
static generateRollback( $rev, IContextSource $context=null, $options=[ 'verify'])
Generate a rollback link for a given revision.
Definition: Linker.php:1806
see documentation in includes Linker php for Linker::makeImageLink & $time
Definition: hooks.txt:1781
see documentation in includes Linker php for Linker::makeImageLink or false for current used if you return false $parser
Definition: hooks.txt:1781
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.
static formatHiddenCategories( $hiddencats)
Returns HTML for the "hidden categories on this page" list.
Definition: Linker.php:1986
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:3039
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:247
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:922
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:163
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:2205
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:2021
Some internal bits split of from Skin.php.
Definition: Linker.php:35
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:940
$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:39
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:3642
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:1972
$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:845
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:1972
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:767
getTargetLanguage()
Get the target language for the content being parsed.
Definition: Parser.php:1014
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:912
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:1748
static makeCommentLink(LinkTarget $linkTarget, $text, $wikiId=null, $options=[])
Generates a link to the given LinkTarget.
Definition: Linker.php:1420
$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:3039
$wgSVGMaxSize
Don&#39;t scale a SVG larger than this.
static getDefaultOption( $opt)
Get a given default option value.
Definition: User.php:1733
static userLink( $userId, $userName, $altUserName=false)
Make user link (or user contributions for unregistered users)
Definition: Linker.php:895
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:620
static newFromLinkTarget(LinkTarget $linkTarget, $forceClone='')
Returns a Title given a LinkTarget.
Definition: Title.php:274
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:1096
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:559
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:303
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:2105
$fallback
Definition: MessagesAb.php:11
static makeHeadline( $level, $attribs, $anchor, $html, $link, $fallbackAnchor=false)
Create a headline for content.
Definition: Linker.php:1749
static userTalkLink( $userId, $userText)
Definition: Linker.php:1031
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:141
static revDeleteLink( $query=[], $restricted=false, $delete=true)
Creates a (show/hide) link for deleting revisions/log entries.
Definition: Linker.php:2147
static tocList( $toc, Language $lang=null)
Wraps the TOC in a table and provides the hide/collapse javascript.
Definition: Linker.php:1674
$revQuery
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:3039
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:2621
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:1779
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:642
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 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:819
static getImageLinkMTOParams( $frameParams, $query='', $parser=null)
Get the link parameters for MediaTransformOutput::toHtml() from given frame parameters supplied by th...
Definition: Linker.php:454
static escapeHtmlAllowEntities( $html)
Given HTML input, escape with htmlspecialchars but un-escape entities.
Definition: Sanitizer.php:1433
static getInvalidTitleDescription(IContextSource $context, $namespace, $title)
Get a message saying that an invalid title was encountered.
Definition: Linker.php:187
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
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:493
static link( $target, $html=null, $customAttribs=[], $query=[], $options=[])
This function returns an HTML link to the given target.
Definition: Linker.php:85
static $accesskeycache
Definition: Linker.php:2056
static tooltipAndAccesskeyAttribs( $name, array $msgParams=[], $options=null)
Returns the attributes for the tooltip and access key.
Definition: Linker.php:2188
static guessSectionNameFromStrippedText( $text)
Like guessSectionNameFromWikiText(), but takes already-stripped text as input.
Definition: Parser.php:6165
$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:519
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
return true to allow those checks to and false if checking is done & $user
Definition: hooks.txt:1454
static blockLink( $userId, $userText)
Definition: Linker.php:1053
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:2169
static getUploadUrl( $destFile, $query='')
Get the URL to upload a certain file.
Definition: Linker.php:730
const TOOL_LINKS_EMAIL
Definition: Linker.php:40
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:780
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:1119
static newFromText( $text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
Definition: Title.php:322