12use InvalidArgumentException;
18use MediaWiki\Languages\LanguageNameUtils;
30use Wikimedia\Bcp47Code\Bcp47CodeValue;
31use Wikimedia\RemexHtml\Tokenizer\Attributes;
32use Wikimedia\RemexHtml\Tokenizer\PlainAttributes;
40 private const MAX_TTS = 900;
59 $options->assertRequiredOptions( self::REGISTER_OPTIONS );
63 # Syntax for arguments (see Parser::setFunctionHook):
64 # "name for lookup in localized magic words array",
66 # optional Parser::SFH_NO_HASH to omit the hash from calls (e.g. {{int:...}}
67 # instead of {{#int:...}})
69 'ns',
'nse',
'urlencode',
'lcfirst',
'ucfirst',
'lc',
'uc',
70 'localurl',
'localurle',
'fullurl',
'fullurle',
'canonicalurl',
71 'canonicalurle',
'formatnum',
'grammar',
'gender',
'plural',
'formal',
72 'bidi',
'numberingroup',
'language',
73 'padleft',
'padright',
'anchorencode',
'defaultsort',
'filepath',
74 'pagesincategory',
'pagesize',
'protectionlevel',
'protectionexpiry',
75 # The following are the "parser function" forms of magic
76 # variables defined in CoreMagicVariables. The no-args form will
77 # go through the magic variable code path (and be cached); the
78 # presence of arguments will cause the parser function form to
79 # be invoked. (Note that the actual implementation will pass
80 # a Parser object as first argument, in addition to the
81 # parser function parameters.)
83 # For this group, the first parameter to the parser function is
84 # "page title", and the no-args form (and the magic variable)
85 # defaults to "current page title".
86 'pagename',
'pagenamee',
87 'fullpagename',
'fullpagenamee',
88 'subpagename',
'subpagenamee',
89 'rootpagename',
'rootpagenamee',
90 'basepagename',
'basepagenamee',
91 'talkpagename',
'talkpagenamee',
92 'subjectpagename',
'subjectpagenamee',
93 'pageid',
'revisionid',
'revisionday',
94 'revisionday2',
'revisionmonth',
'revisionmonth1',
'revisionyear',
98 'namespace',
'namespacee',
'namespacenumber',
'talkspace',
'talkspacee',
99 'subjectspace',
'subjectspacee',
101 # More parser functions corresponding to CoreMagicVariables.
102 # For this group, the first parameter to the parser function is
103 # "raw" (uses the 'raw' format if present) and the no-args form
104 # (and the magic variable) defaults to 'not raw'.
105 'numberofarticles',
'numberoffiles',
107 'numberofactiveusers',
112 # These magic words already contain the hash, and the no-args form
113 # is the same as passing an empty first argument
120 foreach ( $noHashFunctions as $func ) {
128 $parser->
setFunctionHook(
'formatdate', [ self::class,
'formatDate' ] );
130 if ( $allowDisplayTitle ) {
133 [ self::class,
'displaytitle' ],
137 if ( $allowSlowParserFunctions ) {
140 [ self::class,
'pagesinnamespace' ],
152 public static function intFunction( $parser, $part1 =
'', ...$params ) {
153 if ( strval( $part1 ) !==
'' ) {
155 ->inLanguage( $parser->
getOptions()->getUserLangObj() );
156 return [ $message->plain(),
'noparse' => false ];
158 return [
'found' => false ];
169 public static function formatDate( $parser, $date, $defaultPref =
null ) {
173 $date = trim( $date );
175 $pref = $parser->
getOptions()->getDateFormat();
179 if ( $pref ==
'default' && $defaultPref ) {
180 $pref = $defaultPref;
183 $date = $df->reformat( $pref, $date, [
'match-whole' ] );
192 public static function ns( $parser, $part1 =
'' ) {
193 if ( intval( $part1 ) || $part1 ==
"0" ) {
194 $index = intval( $part1 );
196 $index = $parser->
getContentLanguage()->getNsIndex( str_replace(
' ',
'_', $part1 ) );
198 if ( $index !==
false ) {
201 return [
'found' => false ];
210 public static function nse( $parser, $part1 =
'' ) {
211 $ret = self::ns( $parser, $part1 );
212 if ( is_string( $ret ) ) {
213 $ret =
wfUrlencode( str_replace(
' ',
'_', $ret ) );
230 public static function urlencode( $parser, $s =
'', $arg =
null ) {
236 switch (
$magicWords->matchStartToEnd( $arg ??
'' ) ) {
239 $func =
'wfUrlencode';
240 $s = str_replace(
' ',
'_', $s );
245 $func =
'rawurlencode';
263 public static function lcfirst( $parser, $s =
'' ) {
272 public static function ucfirst( $parser, $s =
'' ) {
281 public static function lc( $parser, $s =
'' ) {
290 public static function uc( $parser, $s =
'' ) {
300 public static function localurl( $parser, $s =
'', $arg =
null ) {
301 return self::urlFunction(
'getLocalURL', $s, $arg );
310 public static function localurle( $parser, $s =
'', $arg =
null ) {
311 $temp = self::urlFunction(
'getLocalURL', $s, $arg );
312 if ( !is_string( $temp ) ) {
315 return htmlspecialchars( $temp, ENT_COMPAT );
325 public static function fullurl( $parser, $s =
'', $arg =
null ) {
326 return self::urlFunction(
'getFullURL', $s, $arg );
335 public static function fullurle( $parser, $s =
'', $arg =
null ) {
336 $temp = self::urlFunction(
'getFullURL', $s, $arg );
337 if ( !is_string( $temp ) ) {
340 return htmlspecialchars( $temp, ENT_COMPAT );
351 return self::urlFunction(
'getCanonicalURL', $s, $arg );
361 $temp = self::urlFunction(
'getCanonicalURL', $s, $arg );
362 if ( !is_string( $temp ) ) {
365 return htmlspecialchars( $temp, ENT_COMPAT );
375 public static function urlFunction( $func, $s =
'', $arg =
null ) {
376 # Due to order of execution of a lot of bits, the values might be encoded
377 # before arriving here; if that's true, then the title can't be created
378 # and the variable will fail. If we can't get a decent title from the first
379 # attempt, url-decode and try for a second.
380 $title = Title::newFromText( $s ) ?? Title::newFromURL( urldecode( $s ) );
381 if ( $title !==
null ) {
382 # Convert NS_MEDIA -> NS_FILE
386 if ( $arg !==
null ) {
387 $text = $title->$func( $arg );
389 $text = $title->$func();
393 return [
'found' => false ];
404 public static function formatnum( $parser, $num =
'', $arg1 =
'', $arg2 =
'' ) {
416 if ( in_array(
'rawsuffix', $modifiers,
true ) ) {
417 $func = [ $targetLanguage,
'parseFormattedNumber' ];
419 if ( in_array(
'nocommafysuffix', $modifiers,
true ) ) {
420 $func = [ $targetLanguage,
'formatNumNoSeparators' ];
422 $func = [ $targetLanguage,
'formatNum' ];
423 $func = self::getLegacyFormatNum( $parser, $func );
425 if ( in_array(
'lossless', $modifiers,
true ) ) {
426 $potentiallyLossyFunc = $func;
427 $func =
static function ( $num ) use ( $targetLanguage, $potentiallyLossyFunc ) {
428 $formatted = $potentiallyLossyFunc( $num );
429 $parsed = $targetLanguage->parseFormattedNumber( $formatted );
430 if ( $num === $parsed ) {
447 private static function getLegacyFormatNum( $parser, $callback ) {
452 return static function ( $number ) use ( $parser, $callback ) {
453 $validNumberRe =
'(-(?=[\d\.]))?(\d+|(?=\.\d))(\.\d*)?([Ee][-+]?\d+)?';
455 !is_numeric( $number ) &&
456 $number !== (string)NAN &&
457 $number !== (
string)INF &&
458 $number !== (string)-INF
463 return preg_replace_callback(
"/{$validNumberRe}/",
static function ( $m ) use ( $callback ) {
464 return $callback( $m[0] );
467 return $callback( $number );
477 public static function grammar( $parser, $case =
'', $word =
'' ) {
488 public static function gender( $parser, $username, ...$forms ) {
490 if ( count( $forms ) === 0 ) {
492 } elseif ( count( $forms ) === 1 ) {
496 $username = trim( $username );
502 $title = Title::newFromText( $username,
NS_USER );
509 $user = User::newFromName( $username );
512 $gender = $genderCache->getGenderOf( $user, __METHOD__ );
513 } elseif ( $username ===
'' && $parser->
getOptions()->isMessage() ) {
514 $gender = $genderCache->getGenderOf( $parser->
getOptions()->getUserIdentity(), __METHOD__ );
526 public static function plural( $parser, $text =
'', ...$forms ) {
528 settype( $text, ctype_digit( $text ) ?
'int' :
'float' );
533 public static function formal(
Parser $parser,
string ...$forms ): string {
534 $index = $parser->getTargetLanguage()->getFormalityIndex();
535 return $forms[$index] ?? $forms[0];
543 public static function bidi( $parser, $text =
'' ) {
556 public static function displaytitle( $parser, $text =
'', $uarg =
'' ) {
563 [
'displaytitle_noerror',
'displaytitle_noreplace' ] );
576 $bad = [
'h1',
'h2',
'h3',
'h4',
'h5',
'h6',
'div',
'blockquote',
'ol',
'ul',
'li',
'hr',
577 'table',
'tr',
'th',
'td',
'dl',
'dd',
'caption',
'p',
'ruby',
'rb',
'rt',
'rtc',
'rp',
'br' ];
580 if ( $restrictDisplayTitle ) {
583 $htmlTagsCallback =
static function ( Attributes $attr ): Attributes {
584 $decoded = $attr->getValues();
586 if ( isset( $decoded[
'style'] ) ) {
591 if ( preg_match(
'/(display|user-select|visibility)\s*:/i', $decoded[
'style'] ) ) {
592 $decoded[
'style'] =
'/* attempt to bypass $wgRestrictDisplayTitle */';
596 return new PlainAttributes( $decoded );
599 $htmlTagsCallback =
null;
606 'attrCallback' => $htmlTagsCallback,
607 'removeTags' => $bad,
613 if ( !$restrictDisplayTitle ||
614 ( $title instanceof
Title
618 $old = $parser->
getOutput()->getPageProperty(
'displaytitle' );
619 if ( $old ===
null || $arg !==
'displaytitle_noreplace' ) {
620 $parser->
getOutput()->setDisplayTitle( $text );
622 if ( $old !==
null && $old !== $text && !$arg ) {
625 return '<span class="error">' .
626 $parser->
msg(
'duplicate-displaytitle',
637 'restricted-displaytitle',
654 private static function matchAgainstMagicword(
657 $value = trim( strval( $value ) );
658 if ( $value ===
'' ) {
661 $mwObject = $magicWordFactory->
get( $magicword );
662 return $mwObject->matchStartToEnd( $value );
677 if ( $raw !==
null && $raw !==
'' ) {
678 if ( !$magicWordFactory ) {
681 if ( self::matchAgainstMagicword( $magicWordFactory,
'rawsuffix', $raw ) ) {
685 return $language->formatNum( $num );
694 return self::formatRaw( SiteStats::pages(), $raw, $parser->
getTargetLanguage() );
703 return self::formatRaw( SiteStats::users(), $raw, $parser->
getTargetLanguage() );
712 return self::formatRaw( SiteStats::activeUsers(), $raw, $parser->
getTargetLanguage() );
721 return self::formatRaw( SiteStats::articles(), $raw, $parser->
getTargetLanguage() );
730 return self::formatRaw( SiteStats::images(), $raw, $parser->
getTargetLanguage() );
739 return self::formatRaw(
740 SiteStats::numberingroup(
'sysop' ),
752 return self::formatRaw( SiteStats::edits(), $raw, $parser->
getTargetLanguage() );
762 return self::formatRaw(
763 SiteStats::pagesInNs( intval( $namespace ) ),
776 return self::formatRaw(
777 SiteStats::numberingroup( strtolower( $name ) ),
791 private static function makeTitle(
Parser $parser, ?
string $t ) {
796 $title = Title::newFromText( $t );
809 public static function namespace( $parser, $title = null ) {
810 $t = self::makeTitle( $parser, $title );
814 return str_replace(
'_',
' ', $t->getNsText() );
822 public static function namespacee( $parser, $title =
null ) {
823 $t = self::makeTitle( $parser, $title );
836 $t = self::makeTitle( $parser, $title );
840 return (
string)$t->getNamespace();
848 public static function talkspace( $parser, $title =
null ) {
849 $t = self::makeTitle( $parser, $title );
850 if ( $t ===
null || !$t->canHaveTalkPage() ) {
853 return str_replace(
'_',
' ', $t->getTalkNsText() );
861 public static function talkspacee( $parser, $title =
null ) {
862 $t = self::makeTitle( $parser, $title );
863 if ( $t ===
null || !$t->canHaveTalkPage() ) {
875 $t = self::makeTitle( $parser, $title );
879 return str_replace(
'_',
' ', $t->getSubjectNsText() );
888 $t = self::makeTitle( $parser, $title );
902 public static function pagename( $parser, $title =
null ) {
903 $t = self::makeTitle( $parser, $title );
915 public static function pagenamee( $parser, $title =
null ) {
916 $t = self::makeTitle( $parser, $title );
929 $t = self::makeTitle( $parser, $title );
942 $t = self::makeTitle( $parser, $title );
955 $t = self::makeTitle( $parser, $title );
968 $t = self::makeTitle( $parser, $title );
981 $t = self::makeTitle( $parser, $title );
994 $t = self::makeTitle( $parser, $title );
1007 $t = self::makeTitle( $parser, $title );
1008 if ( $t ===
null ) {
1020 $t = self::makeTitle( $parser, $title );
1021 if ( $t ===
null ) {
1033 $t = self::makeTitle( $parser, $title );
1034 if ( $t ===
null || !$t->canHaveTalkPage() ) {
1046 $t = self::makeTitle( $parser, $title );
1047 if ( $t ===
null || !$t->canHaveTalkPage() ) {
1059 $t = self::makeTitle( $parser, $title );
1060 if ( $t ===
null ) {
1072 $t = self::makeTitle( $parser, $title );
1073 if ( $t ===
null ) {
1093 'pagesincategory_all',
1094 'pagesincategory_pages',
1095 'pagesincategory_subcats',
1096 'pagesincategory_files'
1102 if ( self::matchAgainstMagicword( $parser->
getMagicWordFactory(),
'rawsuffix', $arg1 ) ) {
1112 $type =
'pagesincategory_all';
1115 $title = Title::makeTitleSafe(
NS_CATEGORY, $name );
1116 if ( !$title ) { # invalid title
1120 ->getLanguageConverterFactory()
1122 $languageConverter->findVariantLink( $name, $title,
true );
1125 $name = $title->getDBkey();
1127 if ( !isset( $cache[$name] ) ) {
1128 $category = Category::newFromTitle( $title );
1130 $allCount = $subcatCount = $fileCount = $pageCount = 0;
1132 $allCount = $category->getMemberCount();
1133 $subcatCount = $category->getSubcatCount();
1134 $fileCount = $category->getFileCount();
1135 $pageCount = $category->getPageCount( Category::COUNT_CONTENT_PAGES );
1137 $cache[$name][
'pagesincategory_all'] = $allCount;
1138 $cache[$name][
'pagesincategory_pages'] = $pageCount;
1139 $cache[$name][
'pagesincategory_subcats'] = $subcatCount;
1140 $cache[$name][
'pagesincategory_files'] = $fileCount;
1143 $count = $cache[$name][$type];
1156 public static function pagesize( $parser, $page =
'', $raw =
null ) {
1157 $title = Title::newFromText( $page );
1159 if ( !is_object( $title ) || $title->isExternal() ) {
1164 $rev = self::getCachedRevisionObject( $parser, $title, ParserOutputFlags::VARY_REVISION_SHA1 );
1165 $length = $rev ? $rev->getSize() : 0;
1166 if ( $length ===
null ) {
1186 $titleObject = Title::newFromText( $title ) ?? $parser->
getTitle();
1189 $restrictions = $restrictionStore->getRestrictions( $titleObject, strtolower( $type ) );
1190 # RestrictionStore::getRestrictions returns an array, its possible it may have
1191 # multiple values in the future
1192 return implode(
',', $restrictions );
1210 $titleObject = Title::newFromText( $title ) ?? $parser->
getTitle();
1215 return $restrictionStore->getRestrictionExpiry( $titleObject, strtolower( $type ) ) ??
'';
1228 public static function language( $parser, $code =
'', $inLanguage =
'' ) {
1229 if ( $code ===
'' ) {
1232 if ( $inLanguage ===
'' ) {
1233 $inLanguage = LanguageNameUtils::AUTONYMS;
1236 ->getLanguageNameUtils()
1237 ->getLanguageName( $code, $inLanguage );
1238 return $lang !==
'' ? $lang : LanguageCode::bcp47( $code );
1252 public static function dir(
Parser $parser,
string $code =
'',
string $arg =
'' ): string {
1256 if ( $code ===
'' ) {
1259 if ( $arg !==
'' ) {
1263 if (
$magicWords->matchStartToEnd( $arg ) ===
'language_option_bcp47' ) {
1265 $code =
new Bcp47CodeValue( $code );
1269 $lang = $languageFactory->getLanguage( $code );
1270 }
catch ( InvalidArgumentException ) {
1275 return $lang->getDir();
1286 public static function bcp47(
Parser $parser,
string $code =
'' ): string {
1287 if ( $code ===
'' ) {
1290 return LanguageCode::bcp47( $code );
1304 $parser, $string, $length, $padding =
'0', $direction = STR_PAD_RIGHT
1307 $lengthOfPadding = mb_strlen( $padding );
1308 if ( $lengthOfPadding == 0 ) {
1312 # The remaining length to add counts down to 0 as padding is added
1313 $length = min( (
int)$length, 500 ) - mb_strlen( $string );
1314 if ( $length <= 0 ) {
1319 # $finalPadding is just $padding repeated enough times so that
1320 # mb_strlen( $string ) + mb_strlen( $finalPadding ) == $length
1322 while ( $length > 0 ) {
1323 # If $length < $lengthofPadding, truncate $padding so we get the
1324 # exact length desired.
1325 $finalPadding .= mb_substr( $padding, 0, $length );
1326 $length -= $lengthOfPadding;
1329 if ( $direction == STR_PAD_LEFT ) {
1330 return $finalPadding . $string;
1332 return $string . $finalPadding;
1343 public static function padleft( $parser, $string =
'', $length =
'0', $padding =
'0' ) {
1344 return self::pad( $parser, $string, $length, $padding, STR_PAD_LEFT );
1354 public static function padright( $parser, $string =
'', $length =
'0', $padding =
'0' ) {
1355 return self::pad( $parser, $string, $length, $padding );
1366 return Sanitizer::safeEncodeAttribute( $section );
1374 public static function special( $parser, $text ) {
1375 [ $page, $subpage ] = MediaWikiServices::getInstance()->getSpecialPageFactory()->
1376 resolveAlias( $text );
1378 $title = SpecialPage::getTitleFor( $page, $subpage );
1379 return $title->getPrefixedText();
1382 $title = Title::makeTitleSafe(
NS_SPECIAL, $text );
1383 return $title ? $title->getPrefixedText() : self::special( $parser,
'Badtitle' );
1393 return wfUrlencode( str_replace(
' ',
'_', self::special( $parser, $text ) ) );
1408 [
'defaultsort_noerror',
'defaultsort_noreplace' ] );
1412 $text = trim( $text );
1413 if ( $text ===
'' ) {
1416 $old = $parser->
getOutput()->getPageProperty(
'defaultsort' );
1417 if ( $old ===
null || $arg !==
'defaultsort_noreplace' ) {
1418 $parser->
getOutput()->setPageProperty(
'defaultsort', $text );
1421 if ( $old ===
null || $old == $text || $arg ) {
1425 return '<span class="error">' .
1426 $parser->
msg(
'duplicate-defaultsort',
1446 public static function filepath( $parser, $name =
'', $argA =
'', $argB =
'' ) {
1447 $file = MediaWikiServices::getInstance()->getRepoGroup()->findFile( $name );
1449 if ( $argA ==
'nowiki' ) {
1456 $isNowiki = ( $argB ==
'nowiki' );
1460 $url = $file->getFullUrl();
1463 if ( count( $parsedWidthParam ) ) {
1464 $mto = $file->transform( $parsedWidthParam );
1466 if ( $mto && !$mto->isError() ) {
1468 $urlUtils = MediaWikiServices::getInstance()->getUrlUtils();
1473 return [
$url,
'nowiki' => true ];
1488 public static function tagObj( $parser, $frame, $args ) {
1489 if ( !count( $args ) ) {
1492 $tagName = strtolower( trim( $parser->
killMarkers(
1493 $frame->expand( array_shift( $args ) )
1497 if ( count( $args ) ) {
1501 $inner = $frame->expand( array_shift( $args ), $processNowiki );
1502 if ( $processNowiki ) {
1515 foreach ( $args as $arg ) {
1516 $bits = $arg->splitArg();
1517 if ( strval( $bits[
'index'] ) ===
'' ) {
1518 $name = trim( $frame->expand( $bits[
'name'], PPFrame::STRIP_COMMENTS ) );
1519 $value = trim( $frame->expand( $bits[
'value'] ) );
1520 if ( preg_match(
'/^(?:["\'](.+)["\']|""|\'\')$/s', $value, $m ) ) {
1521 $value = $m[1] ??
'';
1523 $attributes[$name] = $value;
1528 if ( !in_array( $tagName, $stripList ) ) {
1531 foreach ( $attributes as $name => $value ) {
1532 $attrText .=
' ' . htmlspecialchars( $name ) .
1533 '="' . htmlspecialchars( $parser->
killMarkers( $value ), ENT_COMPAT ) .
'"';
1535 if ( $inner ===
null ) {
1536 return "<$tagName$attrText/>";
1538 return "<$tagName$attrText>$inner</$tagName>";
1544 'attributes' => $attributes,
1545 'close' =>
"</$tagName>",
1563 private static function getCachedRevisionObject( $parser, $title, $vary ) {
1568 $revisionRecord =
null;
1570 $isSelfReferential = $title->equals( $parser->
getTitle() );
1571 if ( $isSelfReferential ) {
1582 if ( $parserRevisionRecord && $parserRevisionRecord->isCurrent() ) {
1583 $revisionRecord = $parserRevisionRecord;
1588 if ( !$revisionRecord ) {
1597 if ( !$revisionRecord ) {
1599 $revisionRecord =
null;
1602 $parserOutput->addTemplate(
1604 $revisionRecord ? $revisionRecord->getPageId() : 0,
1605 $revisionRecord ? $revisionRecord->getId() : 0
1609 if ( $isSelfReferential ) {
1610 wfDebug( __METHOD__ .
": used current revision, setting {$vary->value}" );
1612 $parserOutput->setOutputFlag( $vary );
1613 if ( $vary === ParserOutputFlags::VARY_REVISION_SHA1 && $revisionRecord ) {
1615 $sha1 = $revisionRecord->getSha1();
1616 }
catch ( RevisionAccessException ) {
1619 $parserOutput->setRevisionUsedSha1Base36( $sha1 );
1623 return $revisionRecord;
1633 public static function pageid( $parser, $title =
null ) {
1634 $t = self::makeTitle( $parser, $title );
1637 } elseif ( !$t->canExist() || $t->isExternal() ) {
1643 if ( $t->equals( $parser->
getTitle() ) ) {
1646 $parserOutput->setOutputFlag( ParserOutputFlags::VARY_PAGE_ID );
1647 $id = $parser->
getTitle()->getArticleID();
1649 $parserOutput->setSpeculativePageIdUsed( $id );
1656 $linkCache = MediaWikiServices::getInstance()->getLinkCache();
1657 $pdbk = $t->getPrefixedDBkey();
1658 $id = $linkCache->getGoodLinkID( $pdbk );
1659 if ( $id != 0 || $linkCache->isBadLink( $pdbk ) ) {
1660 $parserOutput->addLink( $t, $id );
1667 $id = $t->getArticleID();
1668 $parserOutput->addLink( $t, $id );
1684 $t = self::makeTitle( $parser, $title );
1685 if ( $t ===
null || $t->isExternal() ) {
1689 $services = MediaWikiServices::getInstance();
1691 $t->equals( $parser->
getTitle() ) &&
1692 $services->getMainConfig()->get( MainConfigNames::MiserMode ) &&
1693 !$parser->
getOptions()->getInterfaceMessage() &&
1695 $services->getNamespaceInfo()->isSubject( $t->getNamespace() )
1702 $parser->
getOutput()->setOutputFlag( ParserOutputFlags::VARY_REVISION_EXISTS );
1710 if ( $t->equals( $parser->
getTitle() ) && $title ===
null ) {
1713 $parser->
getOutput()->setOutputFlag( ParserOutputFlags::VARY_REVISION_ID );
1718 $id = $rev->getId();
1722 $id = $parser->
getOptions()->getSpeculativeRevId();
1724 $parser->
getOutput()->setSpeculativeRevIdUsed( $id );
1729 $rev = self::getCachedRevisionObject( $parser, $t, ParserOutputFlags::VARY_REVISION_ID );
1730 return $rev ? $rev->getId() :
'';
1733 private static function getRevisionTimestampSubstring(
1746 if ( $title->equals( $parser->getTitle() ) && !$parser->getOptions()->getInterfaceMessage() ) {
1761 if ( $resNow !== $resThen ) {
1764 $parser->
getOutput()->setOutputFlag( ParserOutputFlags::VARY_REVISION_TIMESTAMP );
1770 $rev = self::getCachedRevisionObject( $parser, $title, ParserOutputFlags::VARY_REVISION_TIMESTAMP );
1789 $t = self::makeTitle( $parser, $title );
1790 if ( $t ===
null || $t->isExternal() ) {
1793 return strval( (
int)self::getRevisionTimestampSubstring(
1794 $parser, $t, 6, 2, self::MAX_TTS
1806 $t = self::makeTitle( $parser, $title );
1807 if ( $t ===
null || $t->isExternal() ) {
1810 return self::getRevisionTimestampSubstring(
1811 $parser, $t, 6, 2, self::MAX_TTS
1823 $t = self::makeTitle( $parser, $title );
1824 if ( $t ===
null || $t->isExternal() ) {
1827 return self::getRevisionTimestampSubstring(
1828 $parser, $t, 4, 2, self::MAX_TTS
1840 $t = self::makeTitle( $parser, $title );
1841 if ( $t ===
null || $t->isExternal() ) {
1844 return strval( (
int)self::getRevisionTimestampSubstring(
1845 $parser, $t, 4, 2, self::MAX_TTS
1857 $t = self::makeTitle( $parser, $title );
1858 if ( $t ===
null || $t->isExternal() ) {
1861 return self::getRevisionTimestampSubstring(
1862 $parser, $t, 0, 4, self::MAX_TTS
1874 $t = self::makeTitle( $parser, $title );
1875 if ( $t ===
null || $t->isExternal() ) {
1878 return self::getRevisionTimestampSubstring(
1879 $parser, $t, 0, 14, self::MAX_TTS
1891 $t = self::makeTitle( $parser, $title );
1892 if ( $t ===
null || $t->isExternal() ) {
1898 if ( $t->equals( $parser->
getTitle() ) ) {
1900 $parser->
getOutput()->setOutputFlag( ParserOutputFlags::VARY_USER );
1907 $rev = self::getCachedRevisionObject( $parser, $t, ParserOutputFlags::VARY_USER );
1908 $user = ( $rev !== null ) ? $rev->getUser() :
null;
1909 return $user ? $user->getName() :
'';
1925 $titleObject = Title::newFromText( $title ) ?? $parser->
getTitle();
1926 $restrictionStore = MediaWikiServices::getInstance()->getRestrictionStore();
1927 if ( $restrictionStore->areCascadeProtectionSourcesLoaded( $titleObject )
1931 $sources = $restrictionStore->getCascadeProtectionSources( $titleObject );
1932 $titleFormatter = MediaWikiServices::getInstance()->getTitleFormatter();
1933 foreach ( $sources[0] as $sourcePageIdentity ) {
1934 $names[] = $titleFormatter->getPrefixedText( $sourcePageIdentity );
1936 return implode(
'|', $names );
1952 'contentmodel_canonical',
1953 'contentmodel_local',
1957 $formatType = $format ===
null ?
'contentmodel_local' :
1960 $t = self::makeTitle( $parser, $title );
1961 if ( $t ===
null ) {
1969 $contentModel = $t->getContentModel();
1970 if ( $formatType ===
'contentmodel_canonical' ) {
1972 } elseif ( $formatType ===
'contentmodel_local' ) {
1973 $localizedContentModel = ContentHandler::getLocalizedName( $contentModel, $parser->
getTargetLanguage() );
1988 public static function interwikilink( $parser, $prefix =
'', $title =
'', $linkText =
null ) {
1989 $services = MediaWikiServices::getInstance();
1992 $services->getInterwikiLookup()->isValidInterwiki( $prefix )
1995 $target = self::getTitleValueSafe( $title, $prefix );
1997 if ( $target !==
null ) {
1998 if ( $linkText !==
null ) {
1999 $linkText = Parser::stripOuterParagraph(
2000 # FIXME T382287: when
using Parsoid
this may leave
2001 # strip markers behind for embedded extension tags.
2005 $parser->
getOutput()->addInterwikiLink( $target );
2007 'text' => Linker::link( $target, $linkText ),
2013 return [
'found' => false ];
2024 $services = MediaWikiServices::getInstance();
2025 $extraInterlanguageLinkPrefixes = $services->getMainConfig()->get(
2026 MainConfigNames::ExtraInterlanguageLinkPrefixes
2030 $services->getInterwikiLookup()->isValidInterwiki( $prefix ) &&
2032 $services->getLanguageNameUtils()->getLanguageName(
2033 $prefix, LanguageNameUtils::AUTONYMS, LanguageNameUtils::DEFINED
2034 ) || in_array( $prefix, $extraInterlanguageLinkPrefixes,
true )
2038 $target = self::getTitleValueSafe( $title, $prefix );
2040 if ( $target !==
null ) {
2041 $parser->
getOutput()->addLanguageLink( $target );
2046 return [
'found' => false ];
2064 private static function getTitleValueSafe( $title, $prefix ): ?
TitleValue {
2065 [ $title, $frag ] = array_pad( explode(
'#', $title, 2 ), 2,
'' );
2069 }
catch ( InvalidArgumentException ) {
2076class_alias( CoreParserFunctions::class,
'CoreParserFunctions' );
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
wfUrlencode( $s)
We want some things to be included as literal characters in our title URLs for prettiness,...
wfEscapeWikiText( $input)
Escapes the given text so that it may be output using addWikiText() without any linking,...
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
if(!defined('MW_SETUP_CALLBACK'))
Base class for content handling.
A class containing constants representing the names of configuration variables.
const AllowSlowParserFunctions
Name constant for the AllowSlowParserFunctions setting, for use with Config::get()
const AllowDisplayTitle
Name constant for the AllowDisplayTitle setting, for use with Config::get()
const RestrictDisplayTitle
Name constant for the RestrictDisplayTitle setting, for use with Config::get()
Parent class for all special pages.