49use Wikimedia\Assert\Assert;
51use Wikimedia\Parsoid\Core\TOCData;
53use Wikimedia\RemexHtml\Serializer\SerializerNode;
114 $target, $html =
null, $customAttribs = [], $query = [], $options = []
117 wfWarn( __METHOD__ .
': Requires $target to be a LinkTarget object.', 2 );
118 return "<!-- ERROR -->$html";
122 $options = (array)$options;
125 $linkRenderer = $services->getLinkRendererFactory()
126 ->createFromLegacyOptions( $options );
128 $linkRenderer = $services->getLinkRenderer();
131 if ( $html !==
null ) {
137 if ( in_array(
'known', $options,
true ) ) {
138 return $linkRenderer->makeKnownLink( $target, $text, $customAttribs, $query );
141 if ( in_array(
'broken', $options,
true ) ) {
142 return $linkRenderer->makeBrokenLink( $target, $text, $customAttribs, $query );
145 if ( in_array(
'noclasses', $options,
true ) ) {
146 return $linkRenderer->makePreloadedLink( $target, $text,
'', $customAttribs, $query );
149 return $linkRenderer->makeLink( $target, $text, $customAttribs, $query );
172 $target, $html =
null, $customAttribs = [],
173 $query = [], $options = [
'known' ]
175 return self::link( $target, $html, $customAttribs, $query, $options );
195 public static function makeSelfLinkObj( $nt, $html =
'', $query =
'', $trail =
'', $prefix =
'', $hash =
'' ) {
196 $nt = Title::newFromLinkTarget( $nt );
199 $attrs[
'class'] =
'mw-selflink-fragment';
200 $attrs[
'href'] =
'#' . $hash;
203 $attrs[
'class'] =
'mw-selflink selflink';
205 $ret = Html::rawElement(
'a', $attrs, $prefix . $html ) . $trail;
207 if ( !$hookRunner->onSelfLinkBegin( $nt, $html, $trail, $prefix, $ret ) ) {
212 $html = htmlspecialchars( $nt->getPrefixedText() );
215 return Html::rawElement(
'a', $attrs, $prefix . $html . $inside ) . $trail;
232 $name = $context->
msg(
'blanknamespace' )->text();
235 getFormattedNsText( $namespace );
237 return $context->
msg(
'invalidtitle-knownnamespace', $namespace, $name, $title )->text();
240 return $context->
msg(
'invalidtitle-unknownnamespace', $namespace, $title )->text();
251 private static function fnamePart( $url ) {
252 $basename = strrchr( $url,
'/' );
253 if ( $basename ===
false ) {
256 $basename = substr( $basename, 1 );
273 $alt = self::fnamePart( $url );
277 ->onLinkerMakeExternalImage( $url, $alt, $img );
279 wfDebug(
"Hook LinkerMakeExternalImage changed the output of external image "
280 .
"with url {$url} and alt text {$alt} to {$img}" );
330 $file, $frameParams = [], $handlerParams = [], $time =
false,
331 $query =
'', $widthOption =
null
333 $title = Title::newFromLinkTarget( $title );
338 if ( !$hookRunner->onImageBeforeProduceHTML( $dummy, $title,
340 $file, $frameParams, $handlerParams, $time, $res,
342 $parser, $query, $widthOption )
347 if ( $file && !$file->allowInlineDisplay() ) {
348 wfDebug( __METHOD__ .
': ' . $title->getPrefixedDBkey() .
' does not allow inline display' );
353 $page = $handlerParams[
'page'] ??
false;
354 if ( !isset( $frameParams[
'align'] ) ) {
355 $frameParams[
'align'] =
'';
357 if ( !isset( $frameParams[
'title'] ) ) {
358 $frameParams[
'title'] =
'';
360 if ( !isset( $frameParams[
'class'] ) ) {
361 $frameParams[
'class'] =
'';
365 $config = $services->getMainConfig();
370 !isset( $handlerParams[
'width'] ) &&
371 !isset( $frameParams[
'manualthumb'] ) &&
372 !isset( $frameParams[
'framed'] )
374 $classes[] =
'mw-default-size';
377 $prefix = $postfix =
'';
379 if ( $enableLegacyMediaDOM ) {
380 if ( $frameParams[
'align'] ==
'center' ) {
381 $prefix =
'<div class="center">';
383 $frameParams[
'align'] =
'none';
387 if ( $file && !isset( $handlerParams[
'width'] ) ) {
388 if ( isset( $handlerParams[
'height'] ) && $file->isVectorized() ) {
392 $handlerParams[
'width'] = $svgMaxSize;
394 $handlerParams[
'width'] = $file->getWidth( $page );
397 if ( isset( $frameParams[
'thumbnail'] )
398 || isset( $frameParams[
'manualthumb'] )
399 || isset( $frameParams[
'framed'] )
400 || isset( $frameParams[
'frameless'] )
401 || !$handlerParams[
'width']
405 if ( $widthOption ===
null || !isset( $thumbLimits[$widthOption] ) ) {
406 $userOptionsLookup = $services->getUserOptionsLookup();
407 $widthOption = $userOptionsLookup->getDefaultOption(
'thumbsize' );
411 if ( isset( $frameParams[
'upright'] ) && $frameParams[
'upright'] == 0 ) {
412 $frameParams[
'upright'] = $thumbUpright;
418 $prefWidth = isset( $frameParams[
'upright'] ) ?
419 round( $thumbLimits[$widthOption] * $frameParams[
'upright'], -1 ) :
420 $thumbLimits[$widthOption];
424 if ( !isset( $handlerParams[
'height'] ) && ( $handlerParams[
'width'] <= 0 ||
425 $prefWidth < $handlerParams[
'width'] || $file->isVectorized() ) ) {
426 $handlerParams[
'width'] = $prefWidth;
432 $hasVisibleCaption = isset( $frameParams[
'thumbnail'] ) ||
433 isset( $frameParams[
'manualthumb'] ) ||
434 isset( $frameParams[
'framed'] );
436 if ( $hasVisibleCaption ) {
437 if ( $enableLegacyMediaDOM ) {
442 # Create a thumbnail. Alignment depends on the writing direction of
443 # the page content language (right-aligned for LTR languages,
444 # left-aligned for RTL languages)
445 # If a thumbnail width has not been provided, it is set
446 # to the default user option as specified in Language*.php
447 if ( $frameParams[
'align'] ==
'' ) {
452 $title, $file, $frameParams, $handlerParams, $time, $query,
457 $rdfaType =
'mw:File';
459 if ( isset( $frameParams[
'frameless'] ) ) {
460 $rdfaType .=
'/Frameless';
462 $srcWidth = $file->getWidth( $page );
463 # For "frameless" option: do not present an image bigger than the
464 # source (for bitmap-style images). This is the same behavior as the
465 # "thumb" option does it already.
466 if ( $srcWidth && !$file->mustRender() && $handlerParams[
'width'] > $srcWidth ) {
467 $handlerParams[
'width'] = $srcWidth;
472 if ( $file && isset( $handlerParams[
'width'] ) ) {
473 # Create a resized image, without the additional thumbnail features
474 $thumb = $file->transform( $handlerParams );
479 $isBadFile = $file && $thumb &&
482 if ( !$thumb || ( !$enableLegacyMediaDOM && $thumb->isError() ) || $isBadFile ) {
483 $rdfaType =
'mw:Error ' . $rdfaType;
484 $currentExists = $file && $file->exists();
485 if ( $enableLegacyMediaDOM ) {
486 $label = $frameParams[
'title'];
488 if ( $currentExists && !$thumb ) {
489 $label =
wfMessage(
'thumbnail_error',
'' )->text();
490 } elseif ( $thumb && $thumb->isError() ) {
493 'Unknown MediaTransformOutput: ' . get_class( $thumb )
495 $label = $thumb->toText();
497 $label = $frameParams[
'alt'] ??
'';
501 $title, $label,
'',
'',
'', (
bool)$time, $handlerParams, $currentExists
509 if ( isset( $frameParams[
'alt'] ) ) {
510 $params[
'alt'] = $frameParams[
'alt'];
512 $params[
'title'] = $frameParams[
'title'];
513 if ( $enableLegacyMediaDOM ) {
515 'valign' => $frameParams[
'valign'] ??
false,
516 'img-class' => $frameParams[
'class'],
518 if ( isset( $frameParams[
'border'] ) ) {
519 $params[
'img-class'] .= (
$params[
'img-class'] !==
'' ?
' ' :
'' ) .
'thumbborder';
523 'img-class' =>
'mw-file-element',
527 $s = $thumb->toHtml(
$params );
530 if ( $enableLegacyMediaDOM ) {
531 if ( $frameParams[
'align'] !=
'' ) {
532 $s = Html::rawElement(
534 [
'class' =>
'float' . $frameParams[
'align'] ],
538 return str_replace(
"\n",
' ', $prefix . $s . $postfix );
544 if ( $frameParams[
'align'] !=
'' ) {
547 $classes[] =
"mw-halign-{$frameParams['align']}";
548 $caption = Html::rawElement(
549 'figcaption', [], $frameParams[
'caption'] ??
''
551 } elseif ( isset( $frameParams[
'valign'] ) ) {
555 $classes[] =
"mw-valign-{$frameParams['valign']}";
558 if ( isset( $frameParams[
'border'] ) ) {
559 $classes[] =
'mw-image-border';
562 if ( isset( $frameParams[
'class'] ) ) {
563 $classes[] = $frameParams[
'class'];
568 'typeof' => $rdfaType,
571 $s = Html::rawElement( $wrapper, $attribs, $s . $caption );
573 return str_replace(
"\n",
' ', $s );
586 if ( isset( $frameParams[
'link-url'] ) && $frameParams[
'link-url'] !==
'' ) {
587 $mtoParams[
'custom-url-link'] = $frameParams[
'link-url'];
588 if ( isset( $frameParams[
'link-target'] ) ) {
589 $mtoParams[
'custom-target-link'] = $frameParams[
'link-target'];
592 $extLinkAttrs = $parser->getExternalLinkAttribs( $frameParams[
'link-url'] );
593 foreach ( $extLinkAttrs as $name => $val ) {
595 $mtoParams[
'parser-extlink-' . $name] = $val;
598 } elseif ( isset( $frameParams[
'link-title'] ) && $frameParams[
'link-title'] !==
'' ) {
600 $mtoParams[
'custom-title-link'] = Title::newFromLinkTarget(
601 $linkRenderer->normalizeTarget( $frameParams[
'link-title'] )
603 if ( isset( $frameParams[
'link-title-query'] ) ) {
604 $mtoParams[
'custom-title-link-query'] = $frameParams[
'link-title-query'];
606 } elseif ( !empty( $frameParams[
'no-link'] ) ) {
609 $mtoParams[
'desc-link'] =
true;
610 $mtoParams[
'desc-query'] = $query;
628 LinkTarget $title, $file, $label =
'', $alt =
'', $align =
null,
629 $params = [], $framed =
false, $manualthumb =
''
637 if ( $manualthumb ) {
638 $frameParams[
'manualthumb'] = $manualthumb;
639 } elseif ( $framed ) {
640 $frameParams[
'framed'] =
true;
641 } elseif ( !isset(
$params[
'width'] ) ) {
642 $classes[] =
'mw-default-size';
645 $title, $file, $frameParams,
$params,
false,
'', $classes
661 LinkTarget $title, $file, $frameParams = [], $handlerParams = [],
662 $time =
false, $query =
'', array $classes = [], ?
Parser $parser =
null
664 $exists = $file && $file->exists();
669 $page = $handlerParams[
'page'] ??
false;
670 $lang = $handlerParams[
'lang'] ??
false;
672 if ( !isset( $frameParams[
'align'] ) ) {
673 $frameParams[
'align'] =
'';
674 if ( $enableLegacyMediaDOM ) {
675 $frameParams[
'align'] =
'right';
678 if ( !isset( $frameParams[
'caption'] ) ) {
679 $frameParams[
'caption'] =
'';
682 if ( empty( $handlerParams[
'width'] ) ) {
684 $handlerParams[
'width'] = isset( $frameParams[
'upright'] ) ? 130 : 180;
689 $manualthumb =
false;
691 $rdfaType =
'mw:File/Thumb';
695 if ( !isset( $frameParams[
'manualthumb'] ) && isset( $frameParams[
'framed'] ) ) {
696 $rdfaType =
'mw:File/Frame';
698 $outerWidth = $handlerParams[
'width'] + 2;
700 if ( isset( $frameParams[
'manualthumb'] ) ) {
701 # Use manually specified thumbnail
702 $manual_title = Title::makeTitleSafe(
NS_FILE, $frameParams[
'manualthumb'] );
703 if ( $manual_title ) {
704 $manual_img = $services->getRepoGroup()
705 ->findFile( $manual_title );
707 $thumb = $manual_img->getUnscaledThumb( $handlerParams );
712 $srcWidth = $file->getWidth( $page );
713 if ( isset( $frameParams[
'framed'] ) ) {
714 $rdfaType =
'mw:File/Frame';
715 if ( !$file->isVectorized() ) {
721 $handlerParams[
'width'] = $srcWidth;
727 if ( $srcWidth && !$file->mustRender() && $handlerParams[
'width'] > $srcWidth ) {
728 $handlerParams[
'width'] = $srcWidth;
732 ? $file->getUnscaledThumb( $handlerParams )
733 : $file->transform( $handlerParams );
737 $outerWidth = $thumb->getWidth() + 2;
739 $outerWidth = $handlerParams[
'width'] + 2;
743 if ( !$enableLegacyMediaDOM && $parser && $rdfaType ===
'mw:File/Thumb' ) {
744 $parser->getOutput()->addModules( [
'mediawiki.page.media' ] );
747 $url = Title::newFromLinkTarget( $title )->getLocalURL( $query );
748 $linkTitleQuery = [];
749 if ( $page || $lang ) {
751 $linkTitleQuery[
'page'] = $page;
754 $linkTitleQuery[
'lang'] = $lang;
756 # ThumbnailImage::toHtml() already adds page= onto the end of DjVu URLs
757 # So we don't need to pass it here in $query. However, the URL for the
758 # zoom icon still needs it, so we make a unique query for it. See T16771
763 && !isset( $frameParams[
'link-title'] )
764 && !isset( $frameParams[
'link-url'] )
765 && !isset( $frameParams[
'no-link'] ) ) {
766 $frameParams[
'link-title'] = $title;
767 $frameParams[
'link-title-query'] = $linkTitleQuery;
770 if ( $frameParams[
'align'] !=
'' ) {
772 $classes[] =
"mw-halign-{$frameParams['align']}";
775 if ( isset( $frameParams[
'class'] ) ) {
776 $classes[] = $frameParams[
'class'];
781 if ( $enableLegacyMediaDOM ) {
782 $s .=
"<div class=\"thumb t{$frameParams['align']}\">"
783 .
"<div class=\"thumbinner\" style=\"width:{$outerWidth}px;\">";
786 $isBadFile = $exists && $thumb && $parser &&
787 $parser->getBadFileLookup()->isBadFile(
788 $manualthumb ? $manual_title : $title->
getDBkey(),
793 $rdfaType =
'mw:Error ' . $rdfaType;
795 if ( !$enableLegacyMediaDOM ) {
796 $label = $frameParams[
'alt'] ??
'';
799 $title, $label,
'',
'',
'', (
bool)$time, $handlerParams,
false
802 } elseif ( !$thumb || ( !$enableLegacyMediaDOM && $thumb->isError() ) || $isBadFile ) {
803 $rdfaType =
'mw:Error ' . $rdfaType;
804 if ( $enableLegacyMediaDOM ) {
806 $s .=
wfMessage(
'thumbnail_error',
'' )->escaped();
809 $title,
'',
'',
'',
'', (
bool)$time, $handlerParams,
true
813 if ( $thumb && $thumb->isError() ) {
816 'Unknown MediaTransformOutput: ' . get_class( $thumb )
818 $label = $thumb->toText();
819 } elseif ( !$thumb ) {
820 $label =
wfMessage(
'thumbnail_error',
'' )->text();
825 $title, $label,
'',
'',
'', (
bool)$time, $handlerParams,
true
830 if ( !$noscale && !$manualthumb ) {
837 if ( isset( $frameParams[
'alt'] ) ) {
838 $params[
'alt'] = $frameParams[
'alt'];
840 if ( $enableLegacyMediaDOM ) {
842 'img-class' => ( isset( $frameParams[
'class'] ) && $frameParams[
'class'] !==
''
843 ? $frameParams[
'class'] .
' '
844 :
'' ) .
'thumbimage'
848 'img-class' =>
'mw-file-element',
851 if ( $rdfaType ===
'mw:File/Thumb' ) {
852 $params[
'magnify-resource'] = $url;
856 $s .= $thumb->toHtml(
$params );
857 if ( isset( $frameParams[
'framed'] ) ) {
860 $zoomIcon = Html::rawElement(
'div', [
'class' =>
'magnify' ],
861 Html::rawElement(
'a', [
863 'class' =>
'internal',
864 'title' =>
wfMessage(
'thumbnail-more' )->text(),
870 if ( $enableLegacyMediaDOM ) {
871 $s .=
' <div class="thumbcaption">' . $zoomIcon . $frameParams[
'caption'] .
'</div></div></div>';
872 return str_replace(
"\n",
' ', $s );
875 $s .= Html::rawElement(
876 'figcaption', [], $frameParams[
'caption'] ??
''
881 'typeof' => $rdfaType,
884 $s = Html::rawElement(
'figure', $attribs, $s );
886 return str_replace(
"\n",
' ', $s );
899 if ( $responsiveImages && $thumb && !$thumb->isError() ) {
901 $hp15[
'width'] = round( $hp[
'width'] * 1.5 );
903 $hp20[
'width'] = $hp[
'width'] * 2;
904 if ( isset( $hp[
'height'] ) ) {
905 $hp15[
'height'] = round( $hp[
'height'] * 1.5 );
906 $hp20[
'height'] = $hp[
'height'] * 2;
909 $thumb15 = $file->transform( $hp15 );
910 $thumb20 = $file->transform( $hp20 );
911 if ( $thumb15 && !$thumb15->isError() && $thumb15->getUrl() !== $thumb->getUrl() ) {
912 $thumb->responsiveUrls[
'1.5'] = $thumb15->getUrl();
914 if ( $thumb20 && !$thumb20->isError() && $thumb20->getUrl() !== $thumb->getUrl() ) {
915 $thumb->responsiveUrls[
'2'] = $thumb20->getUrl();
935 $title, $label =
'', $query =
'', $unused1 =
'', $unused2 =
'',
936 $time =
false, array $handlerParams = [],
bool $currentExists =
false
939 wfWarn( __METHOD__ .
': Requires $title to be a LinkTarget object.' );
940 return "<!-- ERROR -->" . htmlspecialchars( $label );
943 $title = Title::newFromLinkTarget( $title );
945 $mainConfig = $services->getMainConfig();
949 if ( $label ==
'' ) {
950 $label = $title->getPrefixedText();
954 'class' =>
'mw-file-element mw-broken-media',
956 'data-width' => $handlerParams[
'width'] ??
null,
957 'data-height' => $handlerParams[
'height'] ??
null,
961 $html = htmlspecialchars( $label, ENT_COMPAT );
964 $repoGroup = $services->getRepoGroup();
965 $currentExists = $currentExists ||
966 ( $time && $repoGroup->findFile( $title ) !== false );
968 if ( ( $uploadMissingFileUrl || $uploadNavigationUrl || $enableUploads )
972 $title->inNamespace(
NS_FILE ) &&
973 $repoGroup->getLocalRepo()->checkRedirect( $title )
979 [
'class' =>
'mw-redirect' ],
981 [
'known',
'noclasses' ]
984 return Html::rawElement(
'a', [
985 'href' => self::getUploadUrl( $title, $query ),
987 'title' => $title->getPrefixedText()
995 [
'known',
'noclasses' ]
1011 $q =
'wpDestFile=' . Title::newFromLinkTarget( $destFile )->getPartialURL();
1012 if ( $query !=
'' ) {
1016 if ( $uploadMissingFileUrl ) {
1020 if ( $uploadNavigationUrl ) {
1026 return $upload->getLocalURL( $q );
1040 $title, [
'time' => $time ]
1058 if ( $file && $file->exists() ) {
1059 $url = $file->getUrl();
1060 $class =
'internal';
1067 if ( $html ==
'' ) {
1079 Title::newFromLinkTarget( $title ), $file, $html, $attribs, $ret )
1081 wfDebug(
"Hook LinkerMakeMediaLinkFile changed the output of link "
1082 .
"with url {$url} and text {$html} to {$ret}" );
1086 return Html::rawElement(
'a', $attribs, $html );
1100 $queryPos = strpos( $name,
'?' );
1101 if ( $queryPos !==
false ) {
1102 $getParams =
wfCgiToArray( substr( $name, $queryPos + 1 ) );
1103 $name = substr( $name, 0, $queryPos );
1108 $slashPos = strpos( $name,
'/' );
1109 if ( $slashPos !==
false ) {
1110 $subpage = substr( $name, $slashPos + 1 );
1111 $name = substr( $name, 0, $slashPos );
1117 $key = strtolower( $name );
1146 $linktype =
'', $attribs = [], $title =
null
1149 $class =
'external';
1151 $class .=
" $linktype";
1153 if ( isset( $attribs[
'class'] ) && $attribs[
'class'] ) {
1154 $class .=
" {$attribs['class']}";
1156 $attribs[
'class'] = $class;
1159 $text = htmlspecialchars( $text, ENT_COMPAT );
1165 $newRel = Parser::getExternalLinkRel( $url, $title );
1166 if ( !isset( $attribs[
'rel'] ) || $attribs[
'rel'] ===
'' ) {
1167 $attribs[
'rel'] = $newRel;
1168 } elseif ( $newRel !==
null ) {
1170 $newRels = explode(
' ', $newRel );
1171 $oldRels = explode(
' ', $attribs[
'rel'] );
1172 $combined = array_unique( array_merge( $newRels, $oldRels ) );
1173 $attribs[
'rel'] = implode(
' ', $combined );
1177 $url, $text, $link, $attribs, $linktype );
1179 wfDebug(
"Hook LinkerMakeExternalLink changed the output of link "
1180 .
"with url {$url} and text {$text} to {$link}" );
1183 $attribs[
'href'] = $url;
1184 return Html::rawElement(
'a', $attribs, $text );
1202 $altUserName =
false,
1205 if ( $userName ===
'' || $userName ===
false || $userName ===
null ) {
1206 wfDebug( __METHOD__ .
' received an empty username. Are there database errors ' .
1207 'that need to be fixed?' );
1208 return wfMessage(
'empty-username' )->parse();
1211 $classes =
'mw-userlink';
1213 $classes .=
' mw-tempuserlink';
1215 } elseif ( $userId == 0 ) {
1216 $page = ExternalUserNames::getUserLinkTitle( $userName );
1218 if ( ExternalUserNames::isExternal( $userName ) ) {
1219 $classes .=
' mw-extuserlink';
1220 } elseif ( $altUserName ===
false ) {
1221 $altUserName = IPUtils::prettifyIP( $userName );
1223 $classes .=
' mw-anonuserlink';
1225 $page = TitleValue::tryNew(
NS_USER, strtr( $userName,
' ',
'_' ) );
1230 '<bdi>' . htmlspecialchars( $altUserName !==
false ? $altUserName : $userName ) .
'</bdi>';
1232 if ( isset( $attributes[
'class'] ) ) {
1233 $attributes[
'class'] .=
' ' . $classes;
1235 $attributes[
'class'] = $classes;
1239 ?
self::link( $page, $linkText, $attributes )
1240 : Html::rawElement(
'span', $attributes, $linkText );
1262 $userId, $userText, $redContribsWhenNoEdits =
false, $flags = 0, $edits =
null
1266 $talkable = !( $disableAnonTalk && $userId == 0 );
1268 $addEmailLink = $flags & self::TOOL_LINKS_EMAIL && $userId;
1270 if ( $userId == 0 && ExternalUserNames::isExternal( $userText ) ) {
1282 $attribs[
'class'] =
'mw-usertoollinks-contribs';
1283 if ( $redContribsWhenNoEdits ) {
1284 if ( $edits ===
null ) {
1285 $user = UserIdentityValue::newRegistered( $userId, $userText );
1286 $edits = $services->getUserEditTracker()->getUserEditCount( $user );
1288 if ( $edits === 0 ) {
1291 $attribs[
'class'] .=
' mw-usertoollinks-contribs-no-edits';
1298 $userCanBlock = RequestContext::getMain()->getAuthority()->isAllowed(
'block' );
1299 if ( $blockable && $userCanBlock ) {
1303 $user = RequestContext::getMain()->getUser();
1304 if ( $addEmailLink && $user->canSendEmail() ) {
1308 (
new HookRunner( $services->getHookContainer() ) )->onUserToolLinksEdit( $userId, $userText, $items );
1327 if ( $useParentheses ) {
1328 return wfMessage(
'word-separator' )->escaped()
1329 .
'<span class="mw-usertoollinks">'
1330 .
wfMessage(
'parentheses' )->rawParams(
$wgLang->pipeList( $items ) )->escaped()
1335 foreach ( $items as $tool ) {
1336 $tools[] = Html::rawElement(
'span', [], $tool );
1338 return ' <span class="mw-usertoollinks mw-changeslist-links">' .
1339 implode(
' ', $tools ) .
'</span>';
1357 $userId, $userText, $redContribsWhenNoEdits =
false, $flags = 0, $edits =
null,
1358 $useParentheses =
true
1360 if ( $userText ===
'' ) {
1361 wfDebug( __METHOD__ .
' received an empty username. Are there database errors ' .
1362 'that need to be fixed?' );
1363 return ' ' .
wfMessage(
'empty-username' )->parse();
1366 $items = self::userToolLinkArray( $userId, $userText, $redContribsWhenNoEdits, $flags, $edits );
1367 return self::renderUserToolLinksArray( $items, $useParentheses );
1380 $userId, $userText, $edits =
null, $useParentheses =
true
1382 return self::userToolLinks( $userId, $userText,
true, 0, $edits, $useParentheses );
1392 if ( $userText ===
'' ) {
1393 wfDebug( __METHOD__ .
' received an empty username. Are there database errors ' .
1394 'that need to be fixed?' );
1395 return wfMessage(
'empty-username' )->parse();
1398 $userTalkPage = TitleValue::tryNew(
NS_USER_TALK, strtr( $userText,
' ',
'_' ) );
1399 $moreLinkAttribs = [
'class' =>
'mw-usertoollinks-talk' ];
1400 $linkText =
wfMessage(
'talkpagelinktext' )->escaped();
1402 return $userTalkPage
1403 ? self::link( $userTalkPage, $linkText, $moreLinkAttribs )
1404 : Html::rawElement(
'span', $moreLinkAttribs, $linkText );
1414 if ( $userText ===
'' ) {
1415 wfDebug( __METHOD__ .
' received an empty username. Are there database errors ' .
1416 'that need to be fixed?' );
1417 return wfMessage(
'empty-username' )->parse();
1420 $blockPage = SpecialPage::getTitleFor(
'Block', $userText );
1421 $moreLinkAttribs = [
'class' =>
'mw-usertoollinks-block' ];
1423 return self::link( $blockPage,
1435 if ( $userText ===
'' ) {
1436 wfLogWarning( __METHOD__ .
' received an empty username. Are there database errors ' .
1437 'that need to be fixed?' );
1438 return wfMessage(
'empty-username' )->parse();
1441 $emailPage = SpecialPage::getTitleFor(
'Emailuser', $userText );
1442 $moreLinkAttribs = [
'class' =>
'mw-usertoollinks-mail' ];
1443 return self::link( $emailPage,
1462 $authority = RequestContext::getMain()->getAuthority();
1464 $revUser = $revRecord->
getUser(
1465 $isPublic ? RevisionRecord::FOR_PUBLIC : RevisionRecord::FOR_THIS_USER,
1469 $link = self::userLink( $revUser->getId(), $revUser->getName() );
1472 $link =
wfMessage(
'rev-deleted-user' )->escaped();
1475 if ( $revRecord->
isDeleted( RevisionRecord::DELETED_USER ) ) {
1476 $class = self::getRevisionDeletedClass( $revRecord );
1477 return '<span class="' . $class .
'">' . $link .
'</span>';
1489 $class =
'history-deleted';
1490 if ( $revisionRecord->
isDeleted( RevisionRecord::DELETED_RESTRICTED ) ) {
1491 $class .=
' mw-history-suppressed';
1511 $useParentheses =
true
1514 $authority = RequestContext::getMain()->getAuthority();
1516 $revUser = $revRecord->
getUser(
1517 $isPublic ? RevisionRecord::FOR_PUBLIC : RevisionRecord::FOR_THIS_USER,
1521 $link = self::userLink(
1523 $revUser->getName(),
1525 [
'data-mw-revid' => $revRecord->
getId() ]
1526 ) . self::userToolLinks(
1528 $revUser->getName(),
1536 $link =
wfMessage(
'rev-deleted-user' )->escaped();
1539 if ( $revRecord->
isDeleted( RevisionRecord::DELETED_USER ) ) {
1540 $class = self::getRevisionDeletedClass( $revRecord );
1541 return ' <span class="' . $class .
' mw-userlink">' . $link .
'</span>';
1557 return HtmlHelper::modifyElements(
1559 static function ( SerializerNode $node ):
bool {
1560 return $node->name ===
'a' && isset( $node->attrs[
'href'] );
1562 static function ( SerializerNode $node ): SerializerNode {
1563 $node->attrs[
'href'] =
1591 $comment, $title =
null, $local =
false, $wikiId =
null
1594 $formatter = MediaWikiServices::getInstance()->getCommentFormatter();
1595 return $formatter->format( $comment, $title, $local, $wikiId );
1618 $comment, $title =
null, $local =
false, $wikiId =
null
1621 $formatter = MediaWikiServices::getInstance()->getCommentFormatter();
1622 return $formatter->formatLinksUnsafe( $comment, $title, $local, $wikiId );
1634 # :Foobar -- override special treatment of prefix (images, language links)
1635 # /Foobar -- convert to CurrentPage/Foobar
1636 # /Foobar/ -- convert to CurrentPage/Foobar, strip the initial and final / from text
1637 # ../ -- convert to CurrentPage, from CurrentPage/CurrentSubPage
1638 # ../Foobar -- convert to CurrentPage/Foobar,
1639 # (from CurrentPage/CurrentSubPage)
1640 # ../Foobar/ -- convert to CurrentPage/Foobar, use 'Foobar' as text
1641 # (from CurrentPage/CurrentSubPage)
1643 $ret = $target; #
default return value is no change
1645 # Some namespaces don't allow subpages,
1646 # so only perform processing if subpages are allowed
1648 $contextTitle && MediaWikiServices::getInstance()->getNamespaceInfo()->
1649 hasSubpages( $contextTitle->getNamespace() )
1651 $hash = strpos( $target,
'#' );
1652 if ( $hash !==
false ) {
1653 $suffix = substr( $target, $hash );
1654 $target = substr( $target, 0, $hash );
1659 $target = trim( $target );
1660 $contextPrefixedText = MediaWikiServices::getInstance()->getTitleFormatter()->
1661 getPrefixedText( $contextTitle );
1662 # Look at the first character
1663 if ( $target !=
'' && $target[0] ===
'/' ) {
1664 # / at end means we don't want the slash to be shown
1666 $trailingSlashes = preg_match_all(
'%(/+)$%', $target, $m );
1667 if ( $trailingSlashes ) {
1668 $noslash = $target = substr( $target, 1, -strlen( $m[0][0] ) );
1670 $noslash = substr( $target, 1 );
1673 $ret = $contextPrefixedText .
'/' . trim( $noslash ) . $suffix;
1674 if ( $text ===
'' ) {
1675 $text = $target . $suffix;
1676 } #
this might be changed
for ugliness reasons
1678 # check for .. subpage backlinks
1680 $nodotdot = $target;
1681 while ( str_starts_with( $nodotdot,
'../' ) ) {
1683 $nodotdot = substr( $nodotdot, 3 );
1685 if ( $dotdotcount > 0 ) {
1686 $exploded = explode(
'/', $contextPrefixedText );
1687 if ( count( $exploded ) > $dotdotcount ) { # not allowed to go below top level page
1688 $ret = implode(
'/', array_slice( $exploded, 0, -$dotdotcount ) );
1689 # / at the end means don't show full path
1690 if ( substr( $nodotdot, -1, 1 ) ===
'/' ) {
1691 $nodotdot = rtrim( $nodotdot,
'/' );
1692 if ( $text ===
'' ) {
1693 $text = $nodotdot . $suffix;
1696 $nodotdot = trim( $nodotdot );
1697 if ( $nodotdot !=
'' ) {
1698 $ret .=
'/' . $nodotdot;
1729 $comment, $title =
null, $local =
false, $wikiId =
null, $useParentheses =
true
1732 return MediaWikiServices::getInstance()->getCommentFormatter()
1733 ->formatBlock( $comment, $title, $local, $wikiId, $useParentheses );
1755 $useParentheses =
true
1758 $authority = RequestContext::getMain()->getAuthority();
1759 $formatter = MediaWikiServices::getInstance()->getCommentFormatter();
1760 return $formatter->formatRevision( $revRecord, $authority, $local, $isPublic, $useParentheses );
1770 $stxt =
wfMessage(
'historyempty' )->escaped();
1772 $stxt =
wfMessage(
'nbytes' )->numParams( $size )->escaped();
1774 return "<span class=\"history-size mw-diff-bytes\" data-mw-bytes=\"$size\">$stxt</span>";
1799 return "</li>\n" . str_repeat(
"</ul>\n</li>\n", $level > 0 ? $level : 0 );
1814 public static function tocLine( $linkAnchor, $tocline, $tocnumber, $level, $sectionIndex =
false ) {
1816 $classes =
"toclevel-$level";
1821 if ( $sectionIndex !==
false && $sectionIndex !==
'' && !str_starts_with( $sectionIndex,
"T-" ) ) {
1822 $classes .=
" tocsection-$sectionIndex";
1827 return Html::openElement(
'li', [
'class' => $classes ] )
1828 . Html::rawElement(
'a',
1829 [
'href' =>
"#$linkAnchor" ],
1830 Html::element(
'span', [
'class' =>
'tocnumber' ], $tocnumber )
1832 . Html::rawElement(
'span', [
'class' =>
'toctext' ], $tocline )
1860 $lang ??= RequestContext::getMain()->getLanguage();
1862 $title =
wfMessage(
'toc' )->inLanguage( $lang )->escaped();
1864 return '<div id="toc" class="toc" role="navigation" aria-labelledby="mw-toc-heading">'
1865 . Html::element(
'input', [
1866 'type' =>
'checkbox',
1868 'id' =>
'toctogglecheckbox',
1869 'class' =>
'toctogglecheckbox',
1870 'style' =>
'display:none',
1872 . Html::openElement(
'div', [
1873 'class' =>
'toctitle',
1874 'lang' => $lang->getHtmlCode(),
1875 'dir' => $lang->getDir(),
1877 .
'<h2 id="mw-toc-heading">' . $title .
'</h2>'
1878 .
'<span class="toctogglespan">'
1879 . Html::label(
'',
'toctogglecheckbox', [
1880 'class' =>
'toctogglelabel',
1885 .
"</ul>\n</div>\n";
1903 $maxTocLevel = $options[
'maxtoclevel'] ??
null;
1904 if ( $maxTocLevel ===
null ) {
1906 $services = MediaWikiServices::getInstance();
1907 $config = $services->getMainConfig();
1908 $maxTocLevel = $config->get( MainConfigNames::MaxTocLevel );
1910 foreach ( ( $tocData ? $tocData->getSections() : [] ) as $section ) {
1911 $tocLevel = $section->tocLevel;
1912 if ( $tocLevel < $maxTocLevel ) {
1913 if ( $tocLevel > $lastLevel ) {
1914 $toc .= self::tocIndent();
1915 } elseif ( $tocLevel < $lastLevel ) {
1916 if ( $lastLevel < $maxTocLevel ) {
1917 $toc .= self::tocUnindent(
1918 $lastLevel - $tocLevel );
1920 $toc .= self::tocLineEnd();
1923 $toc .= self::tocLineEnd();
1926 $toc .= self::tocLine( $section->linkAnchor,
1927 $section->line, $section->number,
1928 $tocLevel, $section->index );
1929 $lastLevel = $tocLevel;
1932 if ( $lastLevel < $maxTocLevel && $lastLevel > 0 ) {
1933 $toc .= self::tocUnindent( $lastLevel - 1 );
1935 return self::tocList( $toc, $lang );
1956 $link, $fallbackAnchor =
false
1959 $anchorEscaped = htmlspecialchars( $anchor, ENT_COMPAT );
1961 if ( $fallbackAnchor !==
false && $fallbackAnchor !== $anchor ) {
1962 $fallbackAnchor = htmlspecialchars( $fallbackAnchor, ENT_COMPAT );
1963 $fallback =
"<span id=\"$fallbackAnchor\"></span>";
1965 return "<h$level$attribs"
1966 .
"$fallback<span class=\"mw-headline\" id=\"$anchorEscaped\">$html</span>"
1978 $regex = MediaWikiServices::getInstance()->getContentLanguage()->linkTrail();
1980 if ( $trail !==
'' && preg_match( $regex, $trail, $m ) ) {
1981 [ , $inside, $trail ] = $m;
1983 return [ $inside, $trail ];
2021 $context ??= RequestContext::getMain();
2023 $editCount = self::getRollbackEditCount( $revRecord );
2024 if ( $editCount ===
false ) {
2028 $inner = self::buildRollbackLink( $revRecord, $context, $editCount );
2030 $services = MediaWikiServices::getInstance();
2033 if ( !(
new HookRunner( $services->getHookContainer() ) )->onLinkerGenerateRollbackLink(
2034 $revRecord, $context, $options, $inner ) ) {
2038 if ( !in_array(
'noBrackets', $options,
true ) ) {
2039 $inner = $context->msg(
'brackets' )->rawParams( $inner )->escaped();
2042 if ( $services->getUserOptionsLookup()
2043 ->getBoolOption( $context->getUser(),
'showrollbackconfirmation' )
2045 $stats = $services->getStatsdDataFactory();
2046 $stats->increment(
'rollbackconfirmation.event.load' );
2047 $context->getOutput()->addModules(
'mediawiki.misc-authed-curate' );
2050 return '<span class="mw-rollback-link">' . $inner .
'</span>';
2072 if ( func_num_args() > 1 ) {
2073 wfDeprecated( __METHOD__ .
' with $verify parameter',
'1.40' );
2075 $showRollbackEditCount = MediaWikiServices::getInstance()->getMainConfig()
2076 ->get( MainConfigNames::ShowRollbackEditCount );
2078 if ( !is_int( $showRollbackEditCount ) || !$showRollbackEditCount > 0 ) {
2083 $dbr = MediaWikiServices::getInstance()->getConnectionProvider()->getReplicaDatabase();
2086 $queryBuilder = MediaWikiServices::getInstance()->getRevisionStore()->newSelectQueryBuilder( $dbr );
2087 $res = $queryBuilder->where( [
'rev_page' => $revRecord->
getPageId() ] )
2088 ->useIndex( [
'revision' =>
'rev_page_timestamp' ] )
2089 ->orderBy( [
'rev_timestamp',
'rev_id' ], SelectQueryBuilder::SORT_DESC )
2090 ->limit( $showRollbackEditCount + 1 )
2091 ->caller( __METHOD__ )->fetchResultSet();
2093 $revUser = $revRecord->
getUser( RevisionRecord::RAW );
2094 $revUserText = $revUser ? $revUser->getName() :
'';
2098 foreach ( $res as $row ) {
2099 if ( $row->rev_user_text != $revUserText ) {
2100 if ( $row->rev_deleted & RevisionRecord::DELETED_TEXT
2101 || $row->rev_deleted & RevisionRecord::DELETED_USER
2114 if ( $editCount <= $showRollbackEditCount && !$moreRevs ) {
2142 $config = MediaWikiServices::getInstance()->getMainConfig();
2143 $showRollbackEditCount = $config->get( MainConfigNames::ShowRollbackEditCount );
2144 $miserMode = $config->get( MainConfigNames::MiserMode );
2146 $disableRollbackEditCountSpecialPage = [
'Recentchanges',
'Watchlist' ];
2148 $context ??= RequestContext::getMain();
2151 $revUser = $revRecord->
getUser();
2152 $revUserText = $revUser ? $revUser->getName() :
'';
2155 'action' =>
'rollback',
2156 'from' => $revUserText,
2157 'token' => $context->getUser()->getEditToken(
'rollback' ),
2161 'data-mw' =>
'interface',
2162 'title' => $context->msg(
'tooltip-rollback' )->text()
2165 $options = [
'known',
'noclasses' ];
2167 if ( $context->getRequest()->getBool(
'bot' ) ) {
2169 $query[
'hidediff'] =
'1';
2170 $query[
'bot'] =
'1';
2174 foreach ( $disableRollbackEditCountSpecialPage as $specialPage ) {
2175 if ( $context->getTitle()->isSpecial( $specialPage ) ) {
2176 $showRollbackEditCount =
false;
2183 $msg = [
'rollbacklink' ];
2184 if ( is_int( $showRollbackEditCount ) && $showRollbackEditCount > 0 ) {
2185 if ( !is_numeric( $editCount ) ) {
2186 $editCount = self::getRollbackEditCount( $revRecord );
2189 if ( $editCount > $showRollbackEditCount ) {
2190 $msg = [
'rollbacklinkcount-morethan', Message::numParam( $showRollbackEditCount ) ];
2191 } elseif ( $editCount ) {
2192 $msg = [
'rollbacklinkcount', Message::numParam( $editCount ) ];
2196 $html = $context->msg( ...$msg )->parse();
2197 return self::link( $title, $html, $attrs, $query, $options );
2210 if ( count( $hiddencats ) > 0 ) {
2211 # Construct the HTML
2212 $outText =
'<div class="mw-hiddenCategoriesExplanation">';
2213 $outText .=
wfMessage(
'hiddencategories' )->numParams( count( $hiddencats ) )->parseAsBlock();
2214 $outText .=
"</div><ul>\n";
2216 foreach ( $hiddencats as $titleObj ) {
2217 # If it's hidden, it must exist - no need to check with a LinkBatch
2219 . self::link( $titleObj,
null, [], [],
'known' )
2222 $outText .=
'</ul>';
2230 private static function getContextFromMain() {
2231 $context = RequestContext::getMain();
2253 public static function titleAttrib( $name, $options =
null, array $msgParams = [], $localizer =
null ) {
2254 if ( !$localizer ) {
2255 $localizer = self::getContextFromMain();
2257 $message = $localizer->msg(
"tooltip-$name", $msgParams );
2260 if ( !$message->exists() && str_starts_with( $name,
'ca-nstab-' ) ) {
2261 $message = $localizer->msg(
'tooltip-ca-nstab' );
2264 if ( $message->isDisabled() ) {
2267 $tooltip = $message->text();
2268 # Compatibility: formerly some tooltips had [alt-.] hardcoded
2269 $tooltip = preg_replace(
"/ ?\[alt-.\]$/",
'', $tooltip );
2272 $options = (array)$options;
2274 if ( in_array(
'nonexisting', $options ) ) {
2275 $tooltip = $localizer->msg(
'red-link-title', $tooltip ?:
'' )->text();
2277 if ( in_array(
'withaccess', $options ) ) {
2278 $accesskey = self::accesskey( $name, $localizer );
2279 if ( $accesskey !==
false ) {
2281 if ( $tooltip ===
false || $tooltip ===
'' ) {
2282 $tooltip = $localizer->msg(
'brackets', $accesskey )->text();
2284 $tooltip .= $localizer->msg(
'word-separator' )->text();
2285 $tooltip .= $localizer->msg(
'brackets', $accesskey )->text();
2307 public static function accesskey( $name, $localizer =
null ) {
2308 if ( !isset( self::$accesskeycache[$name] ) ) {
2309 if ( !$localizer ) {
2310 $localizer = self::getContextFromMain();
2312 $msg = $localizer->msg(
"accesskey-$name" );
2315 if ( !$msg->exists() && str_starts_with( $name,
'ca-nstab-' ) ) {
2316 $msg = $localizer->msg(
'accesskey-ca-nstab' );
2318 self::$accesskeycache[$name] = $msg->isDisabled() ? false : $msg->plain();
2320 return self::$accesskeycache[$name];
2342 $canHide = $performer->
isAllowed(
'deleterevision' );
2343 $canHideHistory = $performer->
isAllowed(
'deletedhistory' );
2344 if ( !$canHide && !( $revRecord->
getVisibility() && $canHideHistory ) ) {
2348 if ( !$revRecord->
userCan( RevisionRecord::DELETED_RESTRICTED, $performer ) ) {
2349 return self::revDeleteLinkDisabled( $canHide );
2351 $prefixedDbKey = MediaWikiServices::getInstance()->getTitleFormatter()->
2352 getPrefixedDBkey( $title );
2353 if ( $revRecord->
getId() ) {
2357 'type' =>
'revision',
2358 'target' => $prefixedDbKey,
2359 'ids' => $revRecord->
getId()
2365 'type' =>
'archive',
2366 'target' => $prefixedDbKey,
2370 return self::revDeleteLink(
2372 $revRecord->
isDeleted( RevisionRecord::DELETED_RESTRICTED ),
2389 public static function revDeleteLink( $query = [], $restricted =
false, $delete =
true ) {
2390 $sp = SpecialPage::getTitleFor(
'Revisiondelete' );
2391 $msgKey = $delete ?
'rev-delundel' :
'rev-showdeleted';
2392 $html =
wfMessage( $msgKey )->escaped();
2393 $tag = $restricted ?
'strong' :
'span';
2394 $link = self::link( $sp, $html, [], $query, [
'known',
'noclasses' ] );
2397 [
'class' =>
'mw-revdelundel-link' ],
2398 wfMessage(
'parentheses' )->rawParams( $link )->escaped()
2414 $msgKey = $delete ?
'rev-delundel' :
'rev-showdeleted';
2415 $html =
wfMessage( $msgKey )->escaped();
2416 $htmlParentheses =
wfMessage(
'parentheses' )->rawParams( $html )->escaped();
2417 return Xml::tags(
'span', [
'class' =>
'mw-revdelundel-link' ], $htmlParentheses );
2435 array $msgParams = [],
2439 $options = (array)$options;
2440 $options[] =
'withaccess';
2443 if ( !$localizer ) {
2444 $localizer = self::getContextFromMain();
2448 'title' => self::titleAttrib( $name, $options, $msgParams, $localizer ),
2449 'accesskey' => self::accesskey( $name, $localizer )
2451 if ( $attribs[
'title'] ===
false ) {
2452 unset( $attribs[
'title'] );
2454 if ( $attribs[
'accesskey'] ===
false ) {
2455 unset( $attribs[
'accesskey'] );
2467 public static function tooltip( $name, $options =
null ) {
2468 $tooltip = self::titleAttrib( $name, $options );
2469 if ( $tooltip ===
false ) {
2472 return Xml::expandAttributes( [
2482class_alias( Linker::class,
'Linker' );
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
wfWarn( $msg, $callerOffset=1, $level=E_USER_NOTICE)
Send a warning either to the debug log or in a PHP error depending on $wgDevelopmentWarnings.
wfExpandUrl( $url, $defaultProto=PROTO_CURRENT)
Expand a potentially local URL to a fully-qualified URL using $wgServer (or one of its alternatives).
wfLogWarning( $msg, $callerOffset=1, $level=E_USER_WARNING)
Send a warning as a PHP error and the debug log.
wfAppendQuery( $url, $query)
Append a query string to an existing URL, which may or may not already have query string parameters a...
wfCgiToArray( $query)
This is the logical opposite of wfArrayToCgi(): it accepts a query string as its argument and returns...
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that a deprecated feature was used.
if(!defined( 'MW_NO_SESSION') &&MW_ENTRY_POINT !=='cli') $wgLang
if(!defined( 'MW_NO_SESSION') &&MW_ENTRY_POINT !=='cli') $wgTitle
array $params
The job parameters.
Implements some public methods and some protected utility functions which are required by multiple ch...
Marks HTML that shouldn't be escaped.
Base class for language-specific code.
The simplest way of implementing IContextSource is to hold a RequestContext as a member variable and ...
An IContextSource implementation which will inherit context from another source but allow individual ...
Group all the pieces relevant to the context of a request into one instance.
A class containing constants representing the names of configuration variables.
const UploadNavigationUrl
Name constant for the UploadNavigationUrl setting, for use with Config::get()
const ThumbUpright
Name constant for the ThumbUpright setting, for use with Config::get()
const EnableUploads
Name constant for the EnableUploads setting, for use with Config::get()
const SVGMaxSize
Name constant for the SVGMaxSize setting, for use with Config::get()
const ResponsiveImages
Name constant for the ResponsiveImages setting, for use with Config::get()
const DisableAnonTalk
Name constant for the DisableAnonTalk setting, for use with Config::get()
const ParserEnableLegacyMediaDOM
Name constant for the ParserEnableLegacyMediaDOM setting, for use with Config::get()
const ThumbLimits
Name constant for the ThumbLimits setting, for use with Config::get()
const UploadMissingFileUrl
Name constant for the UploadMissingFileUrl setting, for use with Config::get()
Parent class for all special pages.
static getTitleFor( $name, $subpage=false, $fragment='')
Get a localised Title object for a specified special page name If you don't need a full Title object,...
static getTitleValueFor( $name, $subpage=false, $fragment='')
Get a localised TitleValue object for a specified special page name.
Module of static functions for generating XML.
Interface for objects which can provide a MediaWiki context on request.
Interface for localizing messages in MediaWiki.
msg( $key,... $params)
This is the method for getting translated interface messages.