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 ( MWNamespace::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 = wfFindFile( $manual_title );
559  if ( $manual_img ) {
560  $thumb = $manual_img->getUnscaledThumb( $handlerParams );
561  $manualthumb = true;
562  } else {
563  $exists = false;
564  }
565  }
566  } elseif ( isset( $frameParams['framed'] ) ) {
567  // Use image dimensions, don't scale
568  $thumb = $file->getUnscaledThumb( $handlerParams );
569  $noscale = true;
570  } else {
571  # Do not present an image bigger than the source, for bitmap-style images
572  # This is a hack to maintain compatibility with arbitrary pre-1.10 behavior
573  $srcWidth = $file->getWidth( $page );
574  if ( $srcWidth && !$file->mustRender() && $handlerParams['width'] > $srcWidth ) {
575  $handlerParams['width'] = $srcWidth;
576  }
577  $thumb = $file->transform( $handlerParams );
578  }
579 
580  if ( $thumb ) {
581  $outerWidth = $thumb->getWidth() + 2;
582  } else {
583  $outerWidth = $handlerParams['width'] + 2;
584  }
585  }
586 
587  # ThumbnailImage::toHtml() already adds page= onto the end of DjVu URLs
588  # So we don't need to pass it here in $query. However, the URL for the
589  # zoom icon still needs it, so we make a unique query for it. See T16771
590  $url = Title::newFromLinkTarget( $title )->getLocalURL( $query );
591  if ( $page ) {
592  $url = wfAppendQuery( $url, [ 'page' => $page ] );
593  }
594  if ( $manualthumb
595  && !isset( $frameParams['link-title'] )
596  && !isset( $frameParams['link-url'] )
597  && !isset( $frameParams['no-link'] ) ) {
598  $frameParams['link-url'] = $url;
599  }
600 
601  $s = "<div class=\"thumb t{$frameParams['align']}\">"
602  . "<div class=\"thumbinner\" style=\"width:{$outerWidth}px;\">";
603 
604  if ( !$exists ) {
605  $s .= self::makeBrokenImageLinkObj( $title, $frameParams['title'], '', '', '', $time == true );
606  $zoomIcon = '';
607  } elseif ( !$thumb ) {
608  $s .= wfMessage( 'thumbnail_error', '' )->escaped();
609  $zoomIcon = '';
610  } else {
611  if ( !$noscale && !$manualthumb ) {
612  self::processResponsiveImages( $file, $thumb, $handlerParams );
613  }
614  $params = [
615  'alt' => $frameParams['alt'],
616  'title' => $frameParams['title'],
617  'img-class' => ( isset( $frameParams['class'] ) && $frameParams['class'] !== ''
618  ? $frameParams['class'] . ' '
619  : '' ) . 'thumbimage'
620  ];
621  $params = self::getImageLinkMTOParams( $frameParams, $query ) + $params;
622  $s .= $thumb->toHtml( $params );
623  if ( isset( $frameParams['framed'] ) ) {
624  $zoomIcon = "";
625  } else {
626  $zoomIcon = Html::rawElement( 'div', [ 'class' => 'magnify' ],
627  Html::rawElement( 'a', [
628  'href' => $url,
629  'class' => 'internal',
630  'title' => wfMessage( 'thumbnail-more' )->text() ],
631  "" ) );
632  }
633  }
634  $s .= ' <div class="thumbcaption">' . $zoomIcon . $frameParams['caption'] . "</div></div></div>";
635  return str_replace( "\n", ' ', $s );
636  }
637 
646  public static function processResponsiveImages( $file, $thumb, $hp ) {
647  global $wgResponsiveImages;
648  if ( $wgResponsiveImages && $thumb && !$thumb->isError() ) {
649  $hp15 = $hp;
650  $hp15['width'] = round( $hp['width'] * 1.5 );
651  $hp20 = $hp;
652  $hp20['width'] = $hp['width'] * 2;
653  if ( isset( $hp['height'] ) ) {
654  $hp15['height'] = round( $hp['height'] * 1.5 );
655  $hp20['height'] = $hp['height'] * 2;
656  }
657 
658  $thumb15 = $file->transform( $hp15 );
659  $thumb20 = $file->transform( $hp20 );
660  if ( $thumb15 && !$thumb15->isError() && $thumb15->getUrl() !== $thumb->getUrl() ) {
661  $thumb->responsiveUrls['1.5'] = $thumb15->getUrl();
662  }
663  if ( $thumb20 && !$thumb20->isError() && $thumb20->getUrl() !== $thumb->getUrl() ) {
664  $thumb->responsiveUrls['2'] = $thumb20->getUrl();
665  }
666  }
667  }
668 
681  public static function makeBrokenImageLinkObj( $title, $label = '',
682  $query = '', $unused1 = '', $unused2 = '', $time = false
683  ) {
684  if ( !$title instanceof LinkTarget ) {
685  wfWarn( __METHOD__ . ': Requires $title to be a LinkTarget object.' );
686  return "<!-- ERROR -->" . htmlspecialchars( $label );
687  }
688 
690 
692  if ( $label == '' ) {
693  $label = $title->getPrefixedText();
694  }
695  $encLabel = htmlspecialchars( $label );
696  $currentExists = $time ? ( wfFindFile( $title ) != false ) : false;
697 
698  if ( ( $wgUploadMissingFileUrl || $wgUploadNavigationUrl || $wgEnableUploads )
699  && !$currentExists
700  ) {
701  $redir = RepoGroup::singleton()->getLocalRepo()->checkRedirect( $title );
702 
703  if ( $redir ) {
704  // We already know it's a redirect, so mark it
705  // accordingly
706  return self::link(
707  $title,
708  $encLabel,
709  [ 'class' => 'mw-redirect' ],
710  wfCgiToArray( $query ),
711  [ 'known', 'noclasses' ]
712  );
713  }
714 
715  $href = self::getUploadUrl( $title, $query );
716 
717  return '<a href="' . htmlspecialchars( $href ) . '" class="new" title="' .
718  htmlspecialchars( $title->getPrefixedText(), ENT_QUOTES ) . '">' .
719  $encLabel . '</a>';
720  }
721 
722  return self::link( $title, $encLabel, [], wfCgiToArray( $query ), [ 'known', 'noclasses' ] );
723  }
724 
733  protected static function getUploadUrl( $destFile, $query = '' ) {
735  $q = 'wpDestFile=' . Title::castFromLinkTarget( $destFile )->getPartialURL();
736  if ( $query != '' ) {
737  $q .= '&' . $query;
738  }
739 
740  if ( $wgUploadMissingFileUrl ) {
741  return wfAppendQuery( $wgUploadMissingFileUrl, $q );
742  }
743 
744  if ( $wgUploadNavigationUrl ) {
745  return wfAppendQuery( $wgUploadNavigationUrl, $q );
746  }
747 
748  $upload = SpecialPage::getTitleFor( 'Upload' );
749 
750  return $upload->getLocalURL( $q );
751  }
752 
762  public static function makeMediaLinkObj( $title, $html = '', $time = false ) {
763  $img = wfFindFile( $title, [ 'time' => $time ] );
764  return self::makeMediaLinkFile( $title, $img, $html );
765  }
766 
779  public static function makeMediaLinkFile( LinkTarget $title, $file, $html = '' ) {
780  if ( $file && $file->exists() ) {
781  $url = $file->getUrl();
782  $class = 'internal';
783  } else {
784  $url = self::getUploadUrl( $title );
785  $class = 'new';
786  }
787 
788  $alt = $title->getText();
789  if ( $html == '' ) {
790  $html = $alt;
791  }
792 
793  $ret = '';
794  $attribs = [
795  'href' => $url,
796  'class' => $class,
797  'title' => $alt
798  ];
799 
800  if ( !Hooks::run( 'LinkerMakeMediaLinkFile',
801  [ Title::castFromLinkTarget( $title ), $file, &$html, &$attribs, &$ret ] ) ) {
802  wfDebug( "Hook LinkerMakeMediaLinkFile changed the output of link "
803  . "with url {$url} and text {$html} to {$ret}\n", true );
804  return $ret;
805  }
806 
807  return Html::rawElement( 'a', $attribs, $html );
808  }
809 
820  public static function specialLink( $name, $key = '' ) {
821  if ( $key == '' ) {
822  $key = strtolower( $name );
823  }
824 
825  return self::linkKnown( SpecialPage::getTitleFor( $name ), wfMessage( $key )->escaped() );
826  }
827 
846  public static function makeExternalLink( $url, $text, $escape = true,
847  $linktype = '', $attribs = [], $title = null
848  ) {
849  global $wgTitle;
850  $class = "external";
851  if ( $linktype ) {
852  $class .= " $linktype";
853  }
854  if ( isset( $attribs['class'] ) && $attribs['class'] ) {
855  $class .= " {$attribs['class']}";
856  }
857  $attribs['class'] = $class;
858 
859  if ( $escape ) {
860  $text = htmlspecialchars( $text );
861  }
862 
863  if ( !$title ) {
864  $title = $wgTitle;
865  }
866  $newRel = Parser::getExternalLinkRel( $url, $title );
867  if ( !isset( $attribs['rel'] ) || $attribs['rel'] === '' ) {
868  $attribs['rel'] = $newRel;
869  } elseif ( $newRel !== '' ) {
870  // Merge the rel attributes.
871  $newRels = explode( ' ', $newRel );
872  $oldRels = explode( ' ', $attribs['rel'] );
873  $combined = array_unique( array_merge( $newRels, $oldRels ) );
874  $attribs['rel'] = implode( ' ', $combined );
875  }
876  $link = '';
877  $success = Hooks::run( 'LinkerMakeExternalLink',
878  [ &$url, &$text, &$link, &$attribs, $linktype ] );
879  if ( !$success ) {
880  wfDebug( "Hook LinkerMakeExternalLink changed the output of link "
881  . "with url {$url} and text {$text} to {$link}\n", true );
882  return $link;
883  }
884  $attribs['href'] = $url;
885  return Html::rawElement( 'a', $attribs, $text );
886  }
887 
896  public static function userLink( $userId, $userName, $altUserName = false ) {
897  $classes = 'mw-userlink';
898  $page = null;
899  if ( $userId == 0 ) {
900  $page = ExternalUserNames::getUserLinkTitle( $userName );
901 
902  if ( ExternalUserNames::isExternal( $userName ) ) {
903  $classes .= ' mw-extuserlink';
904  } elseif ( $altUserName === false ) {
905  $altUserName = IP::prettifyIP( $userName );
906  }
907  $classes .= ' mw-anonuserlink'; // Separate link class for anons (T45179)
908  } else {
909  $page = new TitleValue( NS_USER, strtr( $userName, ' ', '_' ) );
910  }
911 
912  // Wrap the output with <bdi> tags for directionality isolation
913  $linkText =
914  '<bdi>' . htmlspecialchars( $altUserName !== false ? $altUserName : $userName ) . '</bdi>';
915 
916  return $page
917  ? self::link( $page, $linkText, [ 'class' => $classes ] )
918  : Html::rawElement( 'span', [ 'class' => $classes ], $linkText );
919  }
920 
935  public static function userToolLinks(
936  $userId, $userText, $redContribsWhenNoEdits = false, $flags = 0, $edits = null,
937  $useParentheses = true
938  ) {
939  global $wgUser, $wgDisableAnonTalk, $wgLang;
940  $talkable = !( $wgDisableAnonTalk && $userId == 0 );
941  $blockable = !( $flags & self::TOOL_LINKS_NOBLOCK );
942  $addEmailLink = $flags & self::TOOL_LINKS_EMAIL && $userId;
943 
944  if ( $userId == 0 && ExternalUserNames::isExternal( $userText ) ) {
945  // No tools for an external user
946  return '';
947  }
948 
949  $items = [];
950  if ( $talkable ) {
951  $items[] = self::userTalkLink( $userId, $userText );
952  }
953  if ( $userId ) {
954  // check if the user has an edit
955  $attribs = [];
956  $attribs['class'] = 'mw-usertoollinks-contribs';
957  if ( $redContribsWhenNoEdits ) {
958  if ( intval( $edits ) === 0 && $edits !== 0 ) {
959  $user = User::newFromId( $userId );
960  $edits = $user->getEditCount();
961  }
962  if ( $edits === 0 ) {
963  $attribs['class'] .= ' new';
964  }
965  }
966  $contribsPage = SpecialPage::getTitleFor( 'Contributions', $userText );
967 
968  $items[] = self::link( $contribsPage, wfMessage( 'contribslink' )->escaped(), $attribs );
969  }
970  if ( $blockable && $wgUser->isAllowed( 'block' ) ) {
971  $items[] = self::blockLink( $userId, $userText );
972  }
973 
974  if ( $addEmailLink && $wgUser->canSendEmail() ) {
975  $items[] = self::emailLink( $userId, $userText );
976  }
977 
978  Hooks::run( 'UserToolLinksEdit', [ $userId, $userText, &$items ] );
979 
980  if ( !$items ) {
981  return '';
982  }
983 
984  if ( $useParentheses ) {
985  return wfMessage( 'word-separator' )->escaped()
986  . '<span class="mw-usertoollinks">'
987  . wfMessage( 'parentheses' )->rawParams( $wgLang->pipeList( $items ) )->escaped()
988  . '</span>';
989  }
990 
991  $tools = [];
992  foreach ( $items as $tool ) {
993  $tools[] = Html::rawElement( 'span', [], $tool );
994  }
995  return ' <span class="mw-usertoollinks mw-changeslist-links">' .
996  implode( ' ', $tools ) . '</span>';
997  }
998 
1008  public static function userToolLinksRedContribs(
1009  $userId, $userText, $edits = null, $useParentheses = true
1010  ) {
1011  return self::userToolLinks( $userId, $userText, true, 0, $edits, $useParentheses );
1012  }
1013 
1020  public static function userTalkLink( $userId, $userText ) {
1021  $userTalkPage = new TitleValue( NS_USER_TALK, strtr( $userText, ' ', '_' ) );
1022  $moreLinkAttribs['class'] = 'mw-usertoollinks-talk';
1023 
1024  return self::link( $userTalkPage,
1025  wfMessage( 'talkpagelinktext' )->escaped(),
1026  $moreLinkAttribs
1027  );
1028  }
1029 
1036  public static function blockLink( $userId, $userText ) {
1037  $blockPage = SpecialPage::getTitleFor( 'Block', $userText );
1038  $moreLinkAttribs['class'] = 'mw-usertoollinks-block';
1039 
1040  return self::link( $blockPage,
1041  wfMessage( 'blocklink' )->escaped(),
1042  $moreLinkAttribs
1043  );
1044  }
1045 
1051  public static function emailLink( $userId, $userText ) {
1052  $emailPage = SpecialPage::getTitleFor( 'Emailuser', $userText );
1053  $moreLinkAttribs['class'] = 'mw-usertoollinks-mail';
1054  return self::link( $emailPage,
1055  wfMessage( 'emaillink' )->escaped(),
1056  $moreLinkAttribs
1057  );
1058  }
1059 
1067  public static function revUserLink( $rev, $isPublic = false ) {
1068  if ( $rev->isDeleted( Revision::DELETED_USER ) && $isPublic ) {
1069  $link = wfMessage( 'rev-deleted-user' )->escaped();
1070  } elseif ( $rev->userCan( Revision::DELETED_USER ) ) {
1071  $link = self::userLink( $rev->getUser( Revision::FOR_THIS_USER ),
1072  $rev->getUserText( Revision::FOR_THIS_USER ) );
1073  } else {
1074  $link = wfMessage( 'rev-deleted-user' )->escaped();
1075  }
1076  if ( $rev->isDeleted( Revision::DELETED_USER ) ) {
1077  return '<span class="history-deleted">' . $link . '</span>';
1078  }
1079  return $link;
1080  }
1081 
1090  public static function revUserTools( $rev, $isPublic = false, $useParentheses = true ) {
1091  if ( $rev->isDeleted( Revision::DELETED_USER ) && $isPublic ) {
1092  $link = wfMessage( 'rev-deleted-user' )->escaped();
1093  } elseif ( $rev->userCan( Revision::DELETED_USER ) ) {
1094  $userId = $rev->getUser( Revision::FOR_THIS_USER );
1095  $userText = $rev->getUserText( Revision::FOR_THIS_USER );
1096  $link = self::userLink( $userId, $userText )
1097  . self::userToolLinks( $userId, $userText, false, 0, null,
1098  $useParentheses );
1099  } else {
1100  $link = wfMessage( 'rev-deleted-user' )->escaped();
1101  }
1102  if ( $rev->isDeleted( Revision::DELETED_USER ) ) {
1103  return ' <span class="history-deleted mw-userlink">' . $link . '</span>';
1104  }
1105  return $link;
1106  }
1107 
1126  public static function formatComment(
1127  $comment, $title = null, $local = false, $wikiId = null
1128  ) {
1129  # Sanitize text a bit:
1130  $comment = str_replace( "\n", " ", $comment );
1131  # Allow HTML entities (for T15815)
1132  $comment = Sanitizer::escapeHtmlAllowEntities( $comment );
1133 
1134  # Render autocomments and make links:
1135  $comment = self::formatAutocomments( $comment, $title, $local, $wikiId );
1136  return self::formatLinksInComment( $comment, $title, $local, $wikiId );
1137  }
1138 
1156  private static function formatAutocomments(
1157  $comment, $title = null, $local = false, $wikiId = null
1158  ) {
1159  // @todo $append here is something of a hack to preserve the status
1160  // quo. Someone who knows more about bidi and such should decide
1161  // (1) what sane rendering even *is* for an LTR edit summary on an RTL
1162  // wiki, both when autocomments exist and when they don't, and
1163  // (2) what markup will make that actually happen.
1164  $append = '';
1165  $comment = preg_replace_callback(
1166  // To detect the presence of content before or after the
1167  // auto-comment, we use capturing groups inside optional zero-width
1168  // assertions. But older versions of PCRE can't directly make
1169  // zero-width assertions optional, so wrap them in a non-capturing
1170  // group.
1171  '!(?:(?<=(.)))?/\*\s*(.*?)\s*\*/(?:(?=(.)))?!',
1172  function ( $match ) use ( $title, $local, $wikiId, &$append ) {
1173  global $wgLang;
1174 
1175  // Ensure all match positions are defined
1176  $match += [ '', '', '', '' ];
1177 
1178  $pre = $match[1] !== '';
1179  $auto = $match[2];
1180  $post = $match[3] !== '';
1181  $comment = null;
1182 
1183  Hooks::run(
1184  'FormatAutocomments',
1185  [ &$comment, $pre, $auto, $post, Title::castFromLinkTarget( $title ), $local,
1186  $wikiId ]
1187  );
1188 
1189  if ( $comment === null ) {
1190  if ( $title ) {
1191  $section = $auto;
1192  # Remove links that a user may have manually put in the autosummary
1193  # This could be improved by copying as much of Parser::stripSectionName as desired.
1194  $section = str_replace( [
1195  '[[:',
1196  '[[',
1197  ']]'
1198  ], '', $section );
1199 
1200  // We don't want any links in the auto text to be linked, but we still
1201  // want to show any [[ ]]
1202  $sectionText = str_replace( '[[', '&#91;[', $auto );
1203 
1205  if ( $local ) {
1206  $sectionTitle = new TitleValue( NS_MAIN, '', $section );
1207  } else {
1208  $sectionTitle = $title->createFragmentTarget( $section );
1209  }
1210  if ( $sectionTitle ) {
1212  $sectionTitle, $wgLang->getArrow() . $wgLang->getDirMark() . $sectionText,
1213  $wikiId, 'noclasses'
1214  );
1215  }
1216  }
1217  if ( $pre ) {
1218  # written summary $presep autocomment (summary /* section */)
1219  $pre = wfMessage( 'autocomment-prefix' )->inContentLanguage()->escaped();
1220  }
1221  if ( $post ) {
1222  # autocomment $postsep written summary (/* section */ summary)
1223  $auto .= wfMessage( 'colon-separator' )->inContentLanguage()->escaped();
1224  }
1225  if ( $auto ) {
1226  $auto = '<span dir="auto"><span class="autocomment">' . $auto . '</span>';
1227  $append .= '</span>';
1228  }
1229  $comment = $pre . $auto;
1230  }
1231  return $comment;
1232  },
1233  $comment
1234  );
1235  return $comment . $append;
1236  }
1237 
1257  public static function formatLinksInComment(
1258  $comment, $title = null, $local = false, $wikiId = null
1259  ) {
1260  return preg_replace_callback(
1261  '/
1262  \[\[
1263  \s*+ # ignore leading whitespace, the *+ quantifier disallows backtracking
1264  :? # ignore optional leading colon
1265  ([^\]|]+) # 1. link target; page names cannot include ] or |
1266  (?:\|
1267  # 2. link text
1268  # Stop matching at ]] without relying on backtracking.
1269  ((?:]?[^\]])*+)
1270  )?
1271  \]\]
1272  ([^[]*) # 3. link trail (the text up until the next link)
1273  /x',
1274  function ( $match ) use ( $title, $local, $wikiId ) {
1275  $medians = '(?:' . preg_quote( MWNamespace::getCanonicalName( NS_MEDIA ), '/' ) . '|';
1276  $medians .= preg_quote(
1277  MediaWikiServices::getInstance()->getContentLanguage()->getNsText( NS_MEDIA ),
1278  '/'
1279  ) . '):';
1280 
1281  $comment = $match[0];
1282 
1283  # fix up urlencoded title texts (copied from Parser::replaceInternalLinks)
1284  if ( strpos( $match[1], '%' ) !== false ) {
1285  $match[1] = strtr(
1286  rawurldecode( $match[1] ),
1287  [ '<' => '&lt;', '>' => '&gt;' ]
1288  );
1289  }
1290 
1291  # Handle link renaming [[foo|text]] will show link as "text"
1292  if ( $match[2] != "" ) {
1293  $text = $match[2];
1294  } else {
1295  $text = $match[1];
1296  }
1297  $submatch = [];
1298  $thelink = null;
1299  if ( preg_match( '/^' . $medians . '(.*)$/i', $match[1], $submatch ) ) {
1300  # Media link; trail not supported.
1301  $linkRegexp = '/\[\[(.*?)\]\]/';
1302  $title = Title::makeTitleSafe( NS_FILE, $submatch[1] );
1303  if ( $title ) {
1304  $thelink = Linker::makeMediaLinkObj( $title, $text );
1305  }
1306  } else {
1307  # Other kind of link
1308  # Make sure its target is non-empty
1309  if ( isset( $match[1][0] ) && $match[1][0] == ':' ) {
1310  $match[1] = substr( $match[1], 1 );
1311  }
1312  if ( $match[1] !== false && $match[1] !== '' ) {
1313  if ( preg_match(
1314  MediaWikiServices::getInstance()->getContentLanguage()->linkTrail(),
1315  $match[3],
1316  $submatch
1317  ) ) {
1318  $trail = $submatch[1];
1319  } else {
1320  $trail = "";
1321  }
1322  $linkRegexp = '/\[\[(.*?)\]\]' . preg_quote( $trail, '/' ) . '/';
1323  list( $inside, $trail ) = Linker::splitTrail( $trail );
1324 
1325  $linkText = $text;
1326  $linkTarget = Linker::normalizeSubpageLink( $title, $match[1], $linkText );
1327 
1328  Title::newFromText( $linkTarget );
1329  try {
1330  $target = MediaWikiServices::getInstance()->getTitleParser()->
1331  parseTitle( $linkTarget );
1332 
1333  if ( $target->getText() == '' && !$target->isExternal()
1334  && !$local && $title
1335  ) {
1336  $target = $title->createFragmentTarget( $target->getFragment() );
1337  }
1338 
1339  $thelink = Linker::makeCommentLink( $target, $linkText . $inside, $wikiId ) . $trail;
1340  } catch ( MalformedTitleException $e ) {
1341  // Fall through
1342  }
1343  }
1344  }
1345  if ( $thelink ) {
1346  // If the link is still valid, go ahead and replace it in!
1347  $comment = preg_replace(
1348  $linkRegexp,
1350  $comment,
1351  1
1352  );
1353  }
1354 
1355  return $comment;
1356  },
1357  $comment
1358  );
1359  }
1360 
1374  public static function makeCommentLink(
1375  LinkTarget $linkTarget, $text, $wikiId = null, $options = []
1376  ) {
1377  if ( $wikiId !== null && !$linkTarget->isExternal() ) {
1378  $link = self::makeExternalLink(
1380  $wikiId,
1381  $linkTarget->getNamespace() === 0
1382  ? $linkTarget->getDBkey()
1383  : MWNamespace::getCanonicalName( $linkTarget->getNamespace() ) . ':'
1384  . $linkTarget->getDBkey(),
1385  $linkTarget->getFragment()
1386  ),
1387  $text,
1388  /* escape = */ false // Already escaped
1389  );
1390  } else {
1391  $link = self::link( $linkTarget, $text, [], [], $options );
1392  }
1393 
1394  return $link;
1395  }
1396 
1403  public static function normalizeSubpageLink( $contextTitle, $target, &$text ) {
1404  # Valid link forms:
1405  # Foobar -- normal
1406  # :Foobar -- override special treatment of prefix (images, language links)
1407  # /Foobar -- convert to CurrentPage/Foobar
1408  # /Foobar/ -- convert to CurrentPage/Foobar, strip the initial and final / from text
1409  # ../ -- convert to CurrentPage, from CurrentPage/CurrentSubPage
1410  # ../Foobar -- convert to CurrentPage/Foobar,
1411  # (from CurrentPage/CurrentSubPage)
1412  # ../Foobar/ -- convert to CurrentPage/Foobar, use 'Foobar' as text
1413  # (from CurrentPage/CurrentSubPage)
1414 
1415  $ret = $target; # default return value is no change
1416 
1417  # Some namespaces don't allow subpages,
1418  # so only perform processing if subpages are allowed
1419  if ( $contextTitle && MWNamespace::hasSubpages( $contextTitle->getNamespace() ) ) {
1420  $hash = strpos( $target, '#' );
1421  if ( $hash !== false ) {
1422  $suffix = substr( $target, $hash );
1423  $target = substr( $target, 0, $hash );
1424  } else {
1425  $suffix = '';
1426  }
1427  # T9425
1428  $target = trim( $target );
1429  $contextPrefixedText = MediaWikiServices::getInstance()->getTitleFormatter()->
1430  getPrefixedText( $contextTitle );
1431  # Look at the first character
1432  if ( $target != '' && $target[0] === '/' ) {
1433  # / at end means we don't want the slash to be shown
1434  $m = [];
1435  $trailingSlashes = preg_match_all( '%(/+)$%', $target, $m );
1436  if ( $trailingSlashes ) {
1437  $noslash = $target = substr( $target, 1, -strlen( $m[0][0] ) );
1438  } else {
1439  $noslash = substr( $target, 1 );
1440  }
1441 
1442  $ret = $contextPrefixedText . '/' . trim( $noslash ) . $suffix;
1443  if ( $text === '' ) {
1444  $text = $target . $suffix;
1445  } # this might be changed for ugliness reasons
1446  } else {
1447  # check for .. subpage backlinks
1448  $dotdotcount = 0;
1449  $nodotdot = $target;
1450  while ( strncmp( $nodotdot, "../", 3 ) == 0 ) {
1451  ++$dotdotcount;
1452  $nodotdot = substr( $nodotdot, 3 );
1453  }
1454  if ( $dotdotcount > 0 ) {
1455  $exploded = explode( '/', $contextPrefixedText );
1456  if ( count( $exploded ) > $dotdotcount ) { # not allowed to go below top level page
1457  $ret = implode( '/', array_slice( $exploded, 0, -$dotdotcount ) );
1458  # / at the end means don't show full path
1459  if ( substr( $nodotdot, -1, 1 ) === '/' ) {
1460  $nodotdot = rtrim( $nodotdot, '/' );
1461  if ( $text === '' ) {
1462  $text = $nodotdot . $suffix;
1463  }
1464  }
1465  $nodotdot = trim( $nodotdot );
1466  if ( $nodotdot != '' ) {
1467  $ret .= '/' . $nodotdot;
1468  }
1469  $ret .= $suffix;
1470  }
1471  }
1472  }
1473  }
1474 
1475  return $ret;
1476  }
1477 
1492  public static function commentBlock(
1493  $comment, $title = null, $local = false, $wikiId = null, $useParentheses = true
1494  ) {
1495  // '*' used to be the comment inserted by the software way back
1496  // in antiquity in case none was provided, here for backwards
1497  // compatibility, acc. to brion -√¶var
1498  if ( $comment == '' || $comment == '*' ) {
1499  return '';
1500  }
1501  $formatted = self::formatComment( $comment, $title, $local, $wikiId );
1502  if ( $useParentheses ) {
1503  $formatted = wfMessage( 'parentheses' )->rawParams( $formatted )->escaped();
1504  $classNames = 'comment';
1505  } else {
1506  $classNames = 'comment comment--without-parentheses';
1507  }
1508  return " <span class=\"$classNames\">$formatted</span>";
1509  }
1510 
1522  public static function revComment( Revision $rev, $local = false, $isPublic = false,
1523  $useParentheses = true
1524  ) {
1525  if ( $rev->getComment( Revision::RAW ) == "" ) {
1526  return "";
1527  }
1528  if ( $rev->isDeleted( Revision::DELETED_COMMENT ) && $isPublic ) {
1529  $block = " <span class=\"comment\">" . wfMessage( 'rev-deleted-comment' )->escaped() . "</span>";
1530  } elseif ( $rev->userCan( Revision::DELETED_COMMENT ) ) {
1531  $block = self::commentBlock( $rev->getComment( Revision::FOR_THIS_USER ),
1532  $rev->getTitle(), $local, null, $useParentheses );
1533  } else {
1534  $block = " <span class=\"comment\">" . wfMessage( 'rev-deleted-comment' )->escaped() . "</span>";
1535  }
1536  if ( $rev->isDeleted( Revision::DELETED_COMMENT ) ) {
1537  return " <span class=\"history-deleted comment\">$block</span>";
1538  }
1539  return $block;
1540  }
1541 
1547  public static function formatRevisionSize( $size ) {
1548  if ( $size == 0 ) {
1549  $stxt = wfMessage( 'historyempty' )->escaped();
1550  } else {
1551  $stxt = wfMessage( 'nbytes' )->numParams( $size )->escaped();
1552  }
1553  return "<span class=\"history-size mw-diff-bytes\">$stxt</span>";
1554  }
1555 
1562  public static function tocIndent() {
1563  return "\n<ul>\n";
1564  }
1565 
1573  public static function tocUnindent( $level ) {
1574  return "</li>\n" . str_repeat( "</ul>\n</li>\n", $level > 0 ? $level : 0 );
1575  }
1576 
1588  public static function tocLine( $anchor, $tocline, $tocnumber, $level, $sectionIndex = false ) {
1589  $classes = "toclevel-$level";
1590  if ( $sectionIndex !== false ) {
1591  $classes .= " tocsection-$sectionIndex";
1592  }
1593 
1594  // <li class="$classes"><a href="#$anchor"><span class="tocnumber">
1595  // $tocnumber</span> <span class="toctext">$tocline</span></a>
1596  return Html::openElement( 'li', [ 'class' => $classes ] )
1597  . Html::rawElement( 'a',
1598  [ 'href' => "#$anchor" ],
1599  Html::element( 'span', [ 'class' => 'tocnumber' ], $tocnumber )
1600  . ' '
1601  . Html::rawElement( 'span', [ 'class' => 'toctext' ], $tocline )
1602  );
1603  }
1604 
1612  public static function tocLineEnd() {
1613  return "</li>\n";
1614  }
1615 
1625  public static function tocList( $toc, $lang = null ) {
1626  $lang = $lang ?? RequestContext::getMain()->getLanguage();
1627  if ( !$lang instanceof Language ) {
1628  wfDeprecated( __METHOD__ . ' with type other than Language for $lang', '1.33' );
1629  $lang = wfGetLangObj( $lang );
1630  }
1631 
1632  $title = wfMessage( 'toc' )->inLanguage( $lang )->escaped();
1633 
1634  return '<div id="toc" class="toc">'
1635  . Html::element( 'input', [
1636  'type' => 'checkbox',
1637  'role' => 'button',
1638  'id' => 'toctogglecheckbox',
1639  'class' => 'toctogglecheckbox',
1640  'style' => 'display:none',
1641  ] )
1642  . Html::openElement( 'div', [
1643  'class' => 'toctitle',
1644  'lang' => $lang->getHtmlCode(),
1645  'dir' => $lang->getDir(),
1646  ] )
1647  . "<h2>$title</h2>"
1648  . '<span class="toctogglespan">'
1649  . Html::label( '', 'toctogglecheckbox', [
1650  'class' => 'toctogglelabel',
1651  ] )
1652  . '</span>'
1653  . "</div>\n"
1654  . $toc
1655  . "</ul>\n</div>\n";
1656  }
1657 
1667  public static function generateTOC( $tree, $lang = null ) {
1668  $toc = '';
1669  $lastLevel = 0;
1670  foreach ( $tree as $section ) {
1671  if ( $section['toclevel'] > $lastLevel ) {
1672  $toc .= self::tocIndent();
1673  } elseif ( $section['toclevel'] < $lastLevel ) {
1674  $toc .= self::tocUnindent(
1675  $lastLevel - $section['toclevel'] );
1676  } else {
1677  $toc .= self::tocLineEnd();
1678  }
1679 
1680  $toc .= self::tocLine( $section['anchor'],
1681  $section['line'], $section['number'],
1682  $section['toclevel'], $section['index'] );
1683  $lastLevel = $section['toclevel'];
1684  }
1685  $toc .= self::tocLineEnd();
1686  return self::tocList( $toc, $lang );
1687  }
1688 
1705  public static function makeHeadline( $level, $attribs, $anchor, $html,
1706  $link, $fallbackAnchor = false
1707  ) {
1708  $anchorEscaped = htmlspecialchars( $anchor );
1709  $fallback = '';
1710  if ( $fallbackAnchor !== false && $fallbackAnchor !== $anchor ) {
1711  $fallbackAnchor = htmlspecialchars( $fallbackAnchor );
1712  $fallback = "<span id=\"$fallbackAnchor\"></span>";
1713  }
1714  return "<h$level$attribs"
1715  . "$fallback<span class=\"mw-headline\" id=\"$anchorEscaped\">$html</span>"
1716  . $link
1717  . "</h$level>";
1718  }
1719 
1726  static function splitTrail( $trail ) {
1727  $regex = MediaWikiServices::getInstance()->getContentLanguage()->linkTrail();
1728  $inside = '';
1729  if ( $trail !== '' && preg_match( $regex, $trail, $m ) ) {
1730  list( , $inside, $trail ) = $m;
1731  }
1732  return [ $inside, $trail ];
1733  }
1734 
1762  public static function generateRollback( $rev, IContextSource $context = null,
1763  $options = [ 'verify' ]
1764  ) {
1765  if ( $context === null ) {
1767  }
1768 
1769  $editCount = false;
1770  if ( in_array( 'verify', $options, true ) ) {
1771  $editCount = self::getRollbackEditCount( $rev, true );
1772  if ( $editCount === false ) {
1773  return '';
1774  }
1775  }
1776 
1777  $inner = self::buildRollbackLink( $rev, $context, $editCount );
1778 
1779  if ( !in_array( 'noBrackets', $options, true ) ) {
1780  $inner = $context->msg( 'brackets' )->rawParams( $inner )->escaped();
1781  }
1782 
1783  if ( $context->getUser()->getBoolOption( 'showrollbackconfirmation' ) ) {
1784  $stats = MediaWikiServices::getInstance()->getStatsdDataFactory();
1785  $stats->increment( 'rollbackconfirmation.event.load' );
1786  $context->getOutput()->addModules( 'mediawiki.page.rollback.confirmation' );
1787  }
1788 
1789  return '<span class="mw-rollback-link">' . $inner . '</span>';
1790  }
1791 
1807  public static function getRollbackEditCount( $rev, $verify ) {
1808  global $wgShowRollbackEditCount;
1809  if ( !is_int( $wgShowRollbackEditCount ) || !$wgShowRollbackEditCount > 0 ) {
1810  // Nothing has happened, indicate this by returning 'null'
1811  return null;
1812  }
1813 
1814  $dbr = wfGetDB( DB_REPLICA );
1815 
1816  // Up to the value of $wgShowRollbackEditCount revisions are counted
1818  $res = $dbr->select(
1819  $revQuery['tables'],
1820  [ 'rev_user_text' => $revQuery['fields']['rev_user_text'], 'rev_deleted' ],
1821  // $rev->getPage() returns null sometimes
1822  [ 'rev_page' => $rev->getTitle()->getArticleID() ],
1823  __METHOD__,
1824  [
1825  'USE INDEX' => [ 'revision' => 'page_timestamp' ],
1826  'ORDER BY' => 'rev_timestamp DESC',
1827  'LIMIT' => $wgShowRollbackEditCount + 1
1828  ],
1829  $revQuery['joins']
1830  );
1831 
1832  $editCount = 0;
1833  $moreRevs = false;
1834  foreach ( $res as $row ) {
1835  if ( $rev->getUserText( Revision::RAW ) != $row->rev_user_text ) {
1836  if ( $verify &&
1837  ( $row->rev_deleted & Revision::DELETED_TEXT
1838  || $row->rev_deleted & Revision::DELETED_USER
1839  ) ) {
1840  // If the user or the text of the revision we might rollback
1841  // to is deleted in some way we can't rollback. Similar to
1842  // the sanity checks in WikiPage::commitRollback.
1843  return false;
1844  }
1845  $moreRevs = true;
1846  break;
1847  }
1848  $editCount++;
1849  }
1850 
1851  if ( $verify && $editCount <= $wgShowRollbackEditCount && !$moreRevs ) {
1852  // We didn't find at least $wgShowRollbackEditCount revisions made by the current user
1853  // and there weren't any other revisions. That means that the current user is the only
1854  // editor, so we can't rollback
1855  return false;
1856  }
1857  return $editCount;
1858  }
1859 
1869  public static function buildRollbackLink( $rev, IContextSource $context = null,
1870  $editCount = false
1871  ) {
1873 
1874  // To config which pages are affected by miser mode
1875  $disableRollbackEditCountSpecialPage = [ 'Recentchanges', 'Watchlist' ];
1876 
1877  if ( $context === null ) {
1879  }
1880 
1881  $title = $rev->getTitle();
1882 
1883  $query = [
1884  'action' => 'rollback',
1885  'from' => $rev->getUserText(),
1886  'token' => $context->getUser()->getEditToken( 'rollback' ),
1887  ];
1888 
1889  $attrs = [
1890  'data-mw' => 'interface',
1891  'title' => $context->msg( 'tooltip-rollback' )->text()
1892  ];
1893 
1894  $options = [ 'known', 'noclasses' ];
1895 
1896  if ( $context->getRequest()->getBool( 'bot' ) ) {
1897  //T17999
1898  $query['hidediff'] = '1';
1899  $query['bot'] = '1';
1900  }
1901 
1902  $disableRollbackEditCount = false;
1903  if ( $wgMiserMode ) {
1904  foreach ( $disableRollbackEditCountSpecialPage as $specialPage ) {
1905  if ( $context->getTitle()->isSpecial( $specialPage ) ) {
1906  $disableRollbackEditCount = true;
1907  break;
1908  }
1909  }
1910  }
1911 
1912  if ( !$disableRollbackEditCount
1913  && is_int( $wgShowRollbackEditCount )
1914  && $wgShowRollbackEditCount > 0
1915  ) {
1916  if ( !is_numeric( $editCount ) ) {
1917  $editCount = self::getRollbackEditCount( $rev, false );
1918  }
1919 
1920  if ( $editCount > $wgShowRollbackEditCount ) {
1921  $html = $context->msg( 'rollbacklinkcount-morethan' )
1922  ->numParams( $wgShowRollbackEditCount )->parse();
1923  } else {
1924  $html = $context->msg( 'rollbacklinkcount' )->numParams( $editCount )->parse();
1925  }
1926 
1927  return self::link( $title, $html, $attrs, $query, $options );
1928  }
1929 
1930  $html = $context->msg( 'rollbacklink' )->escaped();
1931  return self::link( $title, $html, $attrs, $query, $options );
1932  }
1933 
1942  public static function formatHiddenCategories( $hiddencats ) {
1943  $outText = '';
1944  if ( count( $hiddencats ) > 0 ) {
1945  # Construct the HTML
1946  $outText = '<div class="mw-hiddenCategoriesExplanation">';
1947  $outText .= wfMessage( 'hiddencategories' )->numParams( count( $hiddencats ) )->parseAsBlock();
1948  $outText .= "</div><ul>\n";
1949 
1950  foreach ( $hiddencats as $titleObj ) {
1951  # If it's hidden, it must exist - no need to check with a LinkBatch
1952  $outText .= '<li>'
1953  . self::link( $titleObj, null, [], [], 'known' )
1954  . "</li>\n";
1955  }
1956  $outText .= '</ul>';
1957  }
1958  return $outText;
1959  }
1960 
1977  public static function titleAttrib( $name, $options = null, array $msgParams = [] ) {
1978  $message = wfMessage( "tooltip-$name", $msgParams );
1979  if ( !$message->exists() ) {
1980  $tooltip = false;
1981  } else {
1982  $tooltip = $message->text();
1983  # Compatibility: formerly some tooltips had [alt-.] hardcoded
1984  $tooltip = preg_replace( "/ ?\[alt-.\]$/", '', $tooltip );
1985  # Message equal to '-' means suppress it.
1986  if ( $tooltip == '-' ) {
1987  $tooltip = false;
1988  }
1989  }
1990 
1991  $options = (array)$options;
1992 
1993  if ( in_array( 'nonexisting', $options ) ) {
1994  $tooltip = wfMessage( 'red-link-title', $tooltip ?: '' )->text();
1995  }
1996  if ( in_array( 'withaccess', $options ) ) {
1997  $accesskey = self::accesskey( $name );
1998  if ( $accesskey !== false ) {
1999  // Should be build the same as in jquery.accessKeyLabel.js
2000  if ( $tooltip === false || $tooltip === '' ) {
2001  $tooltip = wfMessage( 'brackets', $accesskey )->text();
2002  } else {
2003  $tooltip .= wfMessage( 'word-separator' )->text();
2004  $tooltip .= wfMessage( 'brackets', $accesskey )->text();
2005  }
2006  }
2007  }
2008 
2009  return $tooltip;
2010  }
2011 
2012  public static $accesskeycache;
2013 
2025  public static function accesskey( $name ) {
2026  if ( isset( self::$accesskeycache[$name] ) ) {
2027  return self::$accesskeycache[$name];
2028  }
2029 
2030  $message = wfMessage( "accesskey-$name" );
2031 
2032  if ( !$message->exists() ) {
2033  $accesskey = false;
2034  } else {
2035  $accesskey = $message->plain();
2036  if ( $accesskey === '' || $accesskey === '-' ) {
2037  # @todo FIXME: Per standard MW behavior, a value of '-' means to suppress the
2038  # attribute, but this is broken for accesskey: that might be a useful
2039  # value.
2040  $accesskey = false;
2041  }
2042  }
2043 
2044  self::$accesskeycache[$name] = $accesskey;
2045  return self::$accesskeycache[$name];
2046  }
2047 
2061  public static function getRevDeleteLink( User $user, Revision $rev, LinkTarget $title ) {
2062  $canHide = $user->isAllowed( 'deleterevision' );
2063  if ( !$canHide && !( $rev->getVisibility() && $user->isAllowed( 'deletedhistory' ) ) ) {
2064  return '';
2065  }
2066 
2067  if ( !$rev->userCan( Revision::DELETED_RESTRICTED, $user ) ) {
2068  return self::revDeleteLinkDisabled( $canHide ); // revision was hidden from sysops
2069  }
2070  $prefixedDbKey = MediaWikiServices::getInstance()->getTitleFormatter()->
2071  getPrefixedDBkey( $title );
2072  if ( $rev->getId() ) {
2073  // RevDelete links using revision ID are stable across
2074  // page deletion and undeletion; use when possible.
2075  $query = [
2076  'type' => 'revision',
2077  'target' => $prefixedDbKey,
2078  'ids' => $rev->getId()
2079  ];
2080  } else {
2081  // Older deleted entries didn't save a revision ID.
2082  // We have to refer to these by timestamp, ick!
2083  $query = [
2084  'type' => 'archive',
2085  'target' => $prefixedDbKey,
2086  'ids' => $rev->getTimestamp()
2087  ];
2088  }
2089  return self::revDeleteLink( $query,
2090  $rev->isDeleted( Revision::DELETED_RESTRICTED ), $canHide );
2091  }
2092 
2103  public static function revDeleteLink( $query = [], $restricted = false, $delete = true ) {
2104  $sp = SpecialPage::getTitleFor( 'Revisiondelete' );
2105  $msgKey = $delete ? 'rev-delundel' : 'rev-showdeleted';
2106  $html = wfMessage( $msgKey )->escaped();
2107  $tag = $restricted ? 'strong' : 'span';
2108  $link = self::link( $sp, $html, [], $query, [ 'known', 'noclasses' ] );
2109  return Xml::tags(
2110  $tag,
2111  [ 'class' => 'mw-revdelundel-link' ],
2112  wfMessage( 'parentheses' )->rawParams( $link )->escaped()
2113  );
2114  }
2115 
2125  public static function revDeleteLinkDisabled( $delete = true ) {
2126  $msgKey = $delete ? 'rev-delundel' : 'rev-showdeleted';
2127  $html = wfMessage( $msgKey )->escaped();
2128  $htmlParentheses = wfMessage( 'parentheses' )->rawParams( $html )->escaped();
2129  return Xml::tags( 'span', [ 'class' => 'mw-revdelundel-link' ], $htmlParentheses );
2130  }
2131 
2144  public static function tooltipAndAccesskeyAttribs(
2145  $name,
2146  array $msgParams = [],
2147  $options = null
2148  ) {
2149  $options = (array)$options;
2150  $options[] = 'withaccess';
2151 
2152  $attribs = [
2153  'title' => self::titleAttrib( $name, $options, $msgParams ),
2154  'accesskey' => self::accesskey( $name )
2155  ];
2156  if ( $attribs['title'] === false ) {
2157  unset( $attribs['title'] );
2158  }
2159  if ( $attribs['accesskey'] === false ) {
2160  unset( $attribs['accesskey'] );
2161  }
2162  return $attribs;
2163  }
2164 
2172  public static function tooltip( $name, $options = null ) {
2173  $tooltip = self::titleAttrib( $name, $options );
2174  if ( $tooltip === false ) {
2175  return '';
2176  }
2177  return Xml::expandAttributes( [
2178  'title' => $tooltip
2179  ] );
2180  }
2181 
2182 }
static getTitleValueFor( $name, $subpage=false, $fragment='')
Get a localised TitleValue object for a specified special page name.
Definition: SpecialPage.php:97
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:1226
static makeMediaLinkObj( $title, $html='', $time=false)
Create a direct link to a given uploaded file.
Definition: Linker.php:762
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:2172
static tocLineEnd()
End a Table Of Contents line.
Definition: Linker.php:1612
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:1807
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:1981
static formatRevisionSize( $size)
Definition: Linker.php:1547
const NS_MAIN
Definition: Defines.php:64
$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:1403
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:681
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:1726
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:2025
static userToolLinksRedContribs( $userId, $userText, $edits=null, $useParentheses=true)
Alias for userToolLinks( $userId, $userText, true );.
Definition: Linker.php:1008
const NS_SPECIAL
Definition: Defines.php:53
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
static makeMediaLinkFile(LinkTarget $title, $file, $html='')
Create a direct link to a given uploaded file.
Definition: Linker.php:779
null means default & $customAttribs
Definition: hooks.txt:1982
target page
static emailLink( $userId, $userText)
Definition: Linker.php:1051
static buildRollbackLink( $rev, IContextSource $context=null, $editCount=false)
Build a raw rollback link, useful for collections of "tool" links.
Definition: Linker.php:1869
static generateRollback( $rev, IContextSource $context=null, $options=[ 'verify'])
Generate a rollback link for a given revision.
Definition: Linker.php:1762
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:1942
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:48
$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:1257
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:876
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:1126
$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:1977
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 ...
wfFindFile( $title, $options=[])
Find a file.
const NS_MEDIA
Definition: Defines.php:52
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:935
$res
Definition: database.txt:21
static singleton()
Get a RepoGroup instance.
Definition: RepoGroup.php:61
$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:1156
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:1588
$params
isAllowed( $action='')
Internal mechanics of testing a permission.
Definition: User.php:3847
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:846
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
static exists( $index)
Returns whether the specified namespace exists.
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:929
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:70
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:82
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:1374
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:1809
static userLink( $userId, $userName, $altUserName=false)
Make user link (or user contributions for unregistered users)
Definition: Linker.php:896
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:1067
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:611
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:2061
static getCanonicalName( $index)
Returns the canonical (English) name for a given index.
$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:1705
static userTalkLink( $userId, $userText)
Definition: Linker.php:1020
static prettifyIP( $ip)
Prettify an IP for display to end users.
Definition: IP.php:213
static hasSubpages( $index)
Does the namespace allow subpages?
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:2103
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:1562
static processResponsiveImages( $file, $thumb, $hp)
Process responsive images: add 1.5x and 2x subimages to the thumbnail, where applicable.
Definition: Linker.php:646
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:1522
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:1625
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:820
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:1425
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:2012
static tooltipAndAccesskeyAttribs( $name, array $msgParams=[], $options=null)
Returns the attributes for the tooltip and access key.
Definition: Linker.php:2144
static guessSectionNameFromStrippedText( $text)
Like guessSectionNameFromWikiText(), but takes already-stripped text as input.
Definition: Parser.php:6055
$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:67
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:1573
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:1492
static generateTOC( $tree, $lang=null)
Generate a table of contents from a section tree.
Definition: Linker.php:1667
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:1036
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:2125
static getUploadUrl( $destFile, $query='')
Get the URL to upload a certain file.
Definition: Linker.php:733
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:1090
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