12use InvalidArgumentException;
18use MediaWiki\Languages\LanguageNameUtils;
29use Wikimedia\Bcp47Code\Bcp47CodeValue;
30use Wikimedia\RemexHtml\Tokenizer\Attributes;
31use Wikimedia\RemexHtml\Tokenizer\PlainAttributes;
32use Wikimedia\Timestamp\TimestampFormat as TS;
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
121 foreach ( $noHashFunctions as $func ) {
131 if ( $allowDisplayTitle ) {
134 self::displaytitle( ... ),
138 if ( $allowSlowParserFunctions ) {
141 self::pagesinnamespace( ... ),
153 public static function intFunction( $parser, $part1 =
'', ...$params ) {
154 if ( strval( $part1 ) !==
'' ) {
156 ->inLanguage( $parser->
getOptions()->getUserLangObj() );
157 return [ $message->plain(),
'noparse' => false ];
159 return [
'found' => false ];
170 public static function formatDate( $parser, $date, $defaultPref =
null ) {
174 $date = trim( $date );
176 $pref = $parser->
getOptions()->getDateFormat();
180 if ( $pref ==
'default' && $defaultPref ) {
181 $pref = $defaultPref;
184 $date = $df->reformat( $pref, $date, [
'match-whole' ] );
193 public static function ns( $parser, $part1 =
'' ) {
194 if ( intval( $part1 ) || $part1 ==
"0" ) {
195 $index = intval( $part1 );
197 $index = $parser->
getContentLanguage()->getNsIndex( str_replace(
' ',
'_', $part1 ) );
199 if ( $index !==
false ) {
202 return [
'found' => false ];
211 public static function nse( $parser, $part1 =
'' ) {
212 $ret = self::ns( $parser, $part1 );
213 if ( is_string( $ret ) ) {
214 $ret =
wfUrlencode( str_replace(
' ',
'_', $ret ) );
231 public static function urlencode( $parser, $s =
'', $arg =
null ) {
237 switch (
$magicWords->matchStartToEnd( $arg ??
'' ) ) {
240 $func =
'wfUrlencode';
241 $s = str_replace(
' ',
'_', $s );
246 $func =
'rawurlencode';
264 public static function lcfirst( $parser, $s =
'' ) {
273 public static function ucfirst( $parser, $s =
'' ) {
282 public static function lc( $parser, $s =
'' ) {
291 public static function uc( $parser, $s =
'' ) {
301 public static function localurl( $parser, $s =
'', $arg =
null ) {
302 return self::urlFunction(
'getLocalURL', $s, $arg );
311 public static function localurle( $parser, $s =
'', $arg =
null ) {
312 $temp = self::urlFunction(
'getLocalURL', $s, $arg );
313 if ( !is_string( $temp ) ) {
316 return htmlspecialchars( $temp, ENT_COMPAT );
326 public static function fullurl( $parser, $s =
'', $arg =
null ) {
327 return self::urlFunction(
'getFullURL', $s, $arg );
336 public static function fullurle( $parser, $s =
'', $arg =
null ) {
337 $temp = self::urlFunction(
'getFullURL', $s, $arg );
338 if ( !is_string( $temp ) ) {
341 return htmlspecialchars( $temp, ENT_COMPAT );
352 return self::urlFunction(
'getCanonicalURL', $s, $arg );
362 $temp = self::urlFunction(
'getCanonicalURL', $s, $arg );
363 if ( !is_string( $temp ) ) {
366 return htmlspecialchars( $temp, ENT_COMPAT );
376 public static function urlFunction( $func, $s =
'', $arg =
null ) {
377 # Due to order of execution of a lot of bits, the values might be encoded
378 # before arriving here; if that's true, then the title can't be created
379 # and the variable will fail. If we can't get a decent title from the first
380 # attempt, url-decode and try for a second.
381 $title = Title::newFromText( $s ) ?? Title::newFromURL( urldecode( $s ) );
382 if ( $title !==
null ) {
383 # Convert NS_MEDIA -> NS_FILE
387 if ( $arg !==
null ) {
388 $text = $title->$func( $arg );
390 $text = $title->$func();
394 return [
'found' => false ];
405 public static function formatnum( $parser, $num =
'', $arg1 =
'', $arg2 =
'' ) {
417 if ( in_array(
'rawsuffix', $modifiers,
true ) ) {
418 $func = [ $targetLanguage,
'parseFormattedNumber' ];
420 if ( in_array(
'nocommafysuffix', $modifiers,
true ) ) {
421 $func = [ $targetLanguage,
'formatNumNoSeparators' ];
423 $func = [ $targetLanguage,
'formatNum' ];
424 $func = self::getLegacyFormatNum( $parser, $func );
426 if ( in_array(
'lossless', $modifiers,
true ) ) {
427 $potentiallyLossyFunc = $func;
428 $func =
static function ( $num ) use ( $targetLanguage, $potentiallyLossyFunc ) {
429 $formatted = $potentiallyLossyFunc( $num );
430 $parsed = $targetLanguage->parseFormattedNumber( $formatted );
431 if ( $num === $parsed ) {
448 private static function getLegacyFormatNum( $parser, $callback ) {
453 return static function ( $number ) use ( $parser, $callback ) {
454 $validNumberRe =
'(-(?=[\d\.]))?(\d+|(?=\.\d))(\.\d*)?([Ee][-+]?\d+)?';
456 !is_numeric( $number ) &&
464 return preg_replace_callback(
"/{$validNumberRe}/",
static function ( $m ) use ( $callback ) {
465 return $callback( $m[0] );
468 return $callback( $number );
478 public static function grammar( $parser, $case =
'', $word =
'' ) {
489 public static function gender( $parser, $username, ...$forms ) {
491 if ( count( $forms ) === 0 ) {
493 } elseif ( count( $forms ) === 1 ) {
497 $username = trim( $username );
503 $title = Title::newFromText( $username,
NS_USER );
510 $user = User::newFromName( $username );
513 $gender = $genderCache->getGenderOf( $user, __METHOD__ );
514 } elseif ( $username ===
'' && $parser->
getOptions()->isMessage() ) {
515 $gender = $genderCache->getGenderOf( $parser->
getOptions()->getUserIdentity(), __METHOD__ );
527 public static function plural( $parser, $text =
'', ...$forms ) {
529 settype( $text, ctype_digit( $text ) ?
'int' :
'float' );
534 public static function formal(
Parser $parser,
string ...$forms ): string {
535 $index = $parser->getTargetLanguage()->getFormalityIndex();
536 return $forms[$index] ?? $forms[0];
544 public static function bidi( $parser, $text =
'' ) {
557 public static function displaytitle( $parser, $text =
'', $uarg =
'' ) {
564 [
'displaytitle_noerror',
'displaytitle_noreplace' ] );
577 $bad = [
'h1',
'h2',
'h3',
'h4',
'h5',
'h6',
'div',
'blockquote',
'ol',
'ul',
'li',
'hr',
578 'table',
'tr',
'th',
'td',
'dl',
'dd',
'caption',
'p',
'ruby',
'rb',
'rt',
'rtc',
'rp',
'br' ];
581 if ( $restrictDisplayTitle ) {
584 $htmlTagsCallback =
static function ( Attributes $attr ): Attributes {
585 $decoded = $attr->getValues();
587 if ( isset( $decoded[
'style'] ) ) {
592 if ( preg_match(
'/(display|user-select|visibility)\s*:/i', $decoded[
'style'] ) ) {
593 $decoded[
'style'] =
'/* attempt to bypass $wgRestrictDisplayTitle */';
597 return new PlainAttributes( $decoded );
600 $htmlTagsCallback =
null;
607 'attrCallback' => $htmlTagsCallback,
608 'removeTags' => $bad,
614 if ( !$restrictDisplayTitle ||
615 ( $title instanceof
Title
619 $old = $parser->
getOutput()->getPageProperty(
'displaytitle' );
620 if ( $old ===
null || $arg !==
'displaytitle_noreplace' ) {
621 $parser->
getOutput()->setDisplayTitle( $text );
623 if ( $old !==
null && $old !== $text && !$arg ) {
626 return '<span class="error">' .
627 $parser->
msg(
'duplicate-displaytitle',
638 'restricted-displaytitle',
655 private static function matchAgainstMagicword(
658 $value = trim( strval( $value ) );
659 if ( $value ===
'' ) {
662 $mwObject = $magicWordFactory->
get( $magicword );
663 return $mwObject->matchStartToEnd( $value );
678 if ( $raw !==
null && $raw !==
'' ) {
679 if ( !$magicWordFactory ) {
682 if ( self::matchAgainstMagicword( $magicWordFactory,
'rawsuffix', $raw ) ) {
686 return $language->formatNum( $num );
695 return self::formatRaw( SiteStats::pages(), $raw, $parser->
getTargetLanguage() );
704 return self::formatRaw( SiteStats::users(), $raw, $parser->
getTargetLanguage() );
713 return self::formatRaw( SiteStats::activeUsers(), $raw, $parser->
getTargetLanguage() );
722 return self::formatRaw( SiteStats::articles(), $raw, $parser->
getTargetLanguage() );
731 return self::formatRaw( SiteStats::images(), $raw, $parser->
getTargetLanguage() );
740 return self::formatRaw(
741 SiteStats::numberingroup(
'sysop' ),
753 return self::formatRaw( SiteStats::edits(), $raw, $parser->
getTargetLanguage() );
763 return self::formatRaw(
764 SiteStats::pagesInNs( intval( $namespace ) ),
777 return self::formatRaw(
778 SiteStats::numberingroup( strtolower( $name ) ),
792 private static function makeTitle(
Parser $parser, ?
string $t ) {
797 $title = Title::newFromText( $t );
810 public static function namespace( $parser, $title = null ) {
811 $t = self::makeTitle( $parser, $title );
815 return str_replace(
'_',
' ', $t->getNsText() );
823 public static function namespacee( $parser, $title =
null ) {
824 $t = self::makeTitle( $parser, $title );
837 $t = self::makeTitle( $parser, $title );
841 return (
string)$t->getNamespace();
849 public static function talkspace( $parser, $title =
null ) {
850 $t = self::makeTitle( $parser, $title );
851 if ( $t ===
null || !$t->canHaveTalkPage() ) {
854 return str_replace(
'_',
' ', $t->getTalkNsText() );
862 public static function talkspacee( $parser, $title =
null ) {
863 $t = self::makeTitle( $parser, $title );
864 if ( $t ===
null || !$t->canHaveTalkPage() ) {
876 $t = self::makeTitle( $parser, $title );
880 return str_replace(
'_',
' ', $t->getSubjectNsText() );
889 $t = self::makeTitle( $parser, $title );
903 public static function pagename( $parser, $title =
null ) {
904 $t = self::makeTitle( $parser, $title );
916 public static function pagenamee( $parser, $title =
null ) {
917 $t = self::makeTitle( $parser, $title );
930 $t = self::makeTitle( $parser, $title );
943 $t = self::makeTitle( $parser, $title );
956 $t = self::makeTitle( $parser, $title );
969 $t = self::makeTitle( $parser, $title );
982 $t = self::makeTitle( $parser, $title );
995 $t = self::makeTitle( $parser, $title );
1008 $t = self::makeTitle( $parser, $title );
1009 if ( $t ===
null ) {
1021 $t = self::makeTitle( $parser, $title );
1022 if ( $t ===
null ) {
1034 $t = self::makeTitle( $parser, $title );
1035 if ( $t ===
null || !$t->canHaveTalkPage() ) {
1047 $t = self::makeTitle( $parser, $title );
1048 if ( $t ===
null || !$t->canHaveTalkPage() ) {
1060 $t = self::makeTitle( $parser, $title );
1061 if ( $t ===
null ) {
1073 $t = self::makeTitle( $parser, $title );
1074 if ( $t ===
null ) {
1094 'pagesincategory_all',
1095 'pagesincategory_pages',
1096 'pagesincategory_subcats',
1097 'pagesincategory_files'
1103 if ( self::matchAgainstMagicword( $parser->
getMagicWordFactory(),
'rawsuffix', $arg1 ) ) {
1113 $type =
'pagesincategory_all';
1116 $title = Title::makeTitleSafe(
NS_CATEGORY, $name );
1117 if ( !$title ) { # invalid title
1121 ->getLanguageConverterFactory()
1123 $languageConverter->findVariantLink( $name, $title,
true );
1126 $name = $title->getDBkey();
1128 if ( !isset( $cache[$name] ) ) {
1129 $category = Category::newFromTitle( $title );
1131 $allCount = $subcatCount = $fileCount = $pageCount = 0;
1133 $allCount = $category->getMemberCount();
1134 $subcatCount = $category->getSubcatCount();
1135 $fileCount = $category->getFileCount();
1136 $pageCount = $category->getPageCount( Category::COUNT_CONTENT_PAGES );
1138 $cache[$name][
'pagesincategory_all'] = $allCount;
1139 $cache[$name][
'pagesincategory_pages'] = $pageCount;
1140 $cache[$name][
'pagesincategory_subcats'] = $subcatCount;
1141 $cache[$name][
'pagesincategory_files'] = $fileCount;
1144 $count = $cache[$name][$type];
1157 public static function pagesize( $parser, $page =
'', $raw =
null ) {
1158 $title = Title::newFromText( $page );
1160 if ( !is_object( $title ) || $title->isExternal() ) {
1165 $rev = self::getCachedRevisionObject( $parser, $title, ParserOutputFlags::VARY_REVISION_SHA1 );
1166 $length = $rev ? $rev->getSize() : 0;
1167 if ( $length ===
null ) {
1187 $titleObject = Title::newFromText( $title ) ?? $parser->
getTitle();
1190 $restrictions = $restrictionStore->getRestrictions( $titleObject, strtolower( $type ) );
1191 # RestrictionStore::getRestrictions returns an array, its possible it may have
1192 # multiple values in the future
1193 return implode(
',', $restrictions );
1211 $titleObject = Title::newFromText( $title ) ?? $parser->
getTitle();
1216 return $restrictionStore->getRestrictionExpiry( $titleObject, strtolower( $type ) ) ??
'';
1229 public static function language( $parser, $code =
'', $inLanguage =
'' ) {
1230 if ( $code ===
'' ) {
1233 if ( $inLanguage ===
'' ) {
1234 $inLanguage = LanguageNameUtils::AUTONYMS;
1237 ->getLanguageNameUtils()
1238 ->getLanguageName( $code, $inLanguage );
1239 return $lang !==
'' ? $lang : LanguageCode::bcp47( $code );
1253 public static function dir(
Parser $parser,
string $code =
'',
string $arg =
'' ): string {
1257 if ( $code ===
'' ) {
1260 if ( $arg !==
'' ) {
1264 if (
$magicWords->matchStartToEnd( $arg ) ===
'language_option_bcp47' ) {
1266 $code =
new Bcp47CodeValue( $code );
1270 $lang = $languageFactory->getLanguage( $code );
1271 }
catch ( InvalidArgumentException ) {
1276 return $lang->getDir();
1287 public static function bcp47(
Parser $parser,
string $code =
'' ): string {
1288 if ( $code ===
'' ) {
1291 return LanguageCode::bcp47( $code );
1305 $parser, $string, $length, $padding =
'0', $direction = STR_PAD_RIGHT
1308 $lengthOfPadding = mb_strlen( $padding );
1309 if ( $lengthOfPadding == 0 ) {
1313 # The remaining length to add counts down to 0 as padding is added
1314 $length = min( (
int)$length, 500 ) - mb_strlen( $string );
1315 if ( $length <= 0 ) {
1320 # $finalPadding is just $padding repeated enough times so that
1321 # mb_strlen( $string ) + mb_strlen( $finalPadding ) == $length
1323 while ( $length > 0 ) {
1324 # If $length < $lengthofPadding, truncate $padding so we get the
1325 # exact length desired.
1326 $finalPadding .= mb_substr( $padding, 0, $length );
1327 $length -= $lengthOfPadding;
1330 if ( $direction == STR_PAD_LEFT ) {
1331 return $finalPadding . $string;
1333 return $string . $finalPadding;
1344 public static function padleft( $parser, $string =
'', $length =
'0', $padding =
'0' ) {
1345 return self::pad( $parser, $string, $length, $padding, STR_PAD_LEFT );
1355 public static function padright( $parser, $string =
'', $length =
'0', $padding =
'0' ) {
1356 return self::pad( $parser, $string, $length, $padding );
1367 $encodedSection = Sanitizer::safeEncodeAttribute( $section );
1369 return str_replace(
'_',
'_', $encodedSection );
1377 public static function special( $parser, $text ) {
1378 [ $page, $subpage ] = MediaWikiServices::getInstance()->getSpecialPageFactory()->
1379 resolveAlias( $text );
1381 $title = SpecialPage::getTitleFor( $page, $subpage );
1382 return $title->getPrefixedText();
1385 $title = Title::makeTitleSafe(
NS_SPECIAL, $text );
1386 return $title ? $title->getPrefixedText() : self::special( $parser,
'Badtitle' );
1396 return wfUrlencode( str_replace(
' ',
'_', self::special( $parser, $text ) ) );
1411 [
'defaultsort_noerror',
'defaultsort_noreplace' ] );
1415 $text = trim( $text );
1416 if ( $text ===
'' ) {
1419 $old = $parser->
getOutput()->getPageProperty(
'defaultsort' );
1420 if ( $old ===
null || $arg !==
'defaultsort_noreplace' ) {
1421 $parser->
getOutput()->setPageProperty(
'defaultsort', $text );
1424 if ( $old ===
null || $old == $text || $arg ) {
1428 return '<span class="error">' .
1429 $parser->
msg(
'duplicate-defaultsort',
1449 public static function filepath( $parser, $name =
'', $argA =
'', $argB =
'' ) {
1450 $file = MediaWikiServices::getInstance()->getRepoGroup()->findFile( $name );
1452 if ( $argA ==
'nowiki' ) {
1459 $isNowiki = ( $argB ==
'nowiki' );
1463 $url = $file->getFullUrl();
1466 if ( count( $parsedWidthParam ) ) {
1467 $mto = $file->transform( $parsedWidthParam );
1469 if ( $mto && !$mto->isError() ) {
1471 $urlUtils = MediaWikiServices::getInstance()->getUrlUtils();
1476 return [
$url,
'nowiki' => true ];
1491 public static function tagObj( $parser, $frame, $args ) {
1492 if ( !count( $args ) ) {
1495 $tagName = strtolower( trim( $parser->
killMarkers(
1496 $frame->expand( array_shift( $args ) )
1500 if ( count( $args ) ) {
1504 $inner = $frame->expand( array_shift( $args ), $processNowiki );
1505 if ( $processNowiki ) {
1518 foreach ( $args as $arg ) {
1519 $bits = $arg->splitArg();
1520 if ( strval( $bits[
'index'] ) ===
'' ) {
1521 $name = trim( $frame->expand( $bits[
'name'], PPFrame::STRIP_COMMENTS ) );
1522 $value = trim( $frame->expand( $bits[
'value'] ) );
1523 if ( preg_match(
'/^(?:["\'](.+)["\']|""|\'\')$/s', $value, $m ) ) {
1524 $value = $m[1] ??
'';
1526 $attributes[$name] = $value;
1531 if ( !in_array( $tagName, $stripList ) ) {
1534 foreach ( $attributes as $name => $value ) {
1535 $attrText .=
' ' . htmlspecialchars( $name ) .
1536 '="' . htmlspecialchars( $parser->
killMarkers( $value ), ENT_COMPAT ) .
'"';
1538 if ( $inner ===
null ) {
1539 return "<$tagName$attrText/>";
1541 return "<$tagName$attrText>$inner</$tagName>";
1547 'attributes' => $attributes,
1548 'close' =>
"</$tagName>",
1566 private static function getCachedRevisionObject( $parser, $title, $vary ) {
1571 $revisionRecord =
null;
1573 $isSelfReferential = $title->equals( $parser->
getTitle() );
1574 if ( $isSelfReferential ) {
1585 if ( $parserRevisionRecord && $parserRevisionRecord->isCurrent() ) {
1586 $revisionRecord = $parserRevisionRecord;
1591 if ( !$revisionRecord ) {
1600 if ( !$revisionRecord ) {
1602 $revisionRecord =
null;
1605 $parserOutput->addTemplate(
1607 $revisionRecord ? $revisionRecord->getPageId() : 0,
1608 $revisionRecord ? $revisionRecord->getId() : 0
1612 if ( $isSelfReferential ) {
1613 wfDebug( __METHOD__ .
": used current revision, setting {$vary->value}" );
1615 $parserOutput->setOutputFlag( $vary );
1616 if ( $vary === ParserOutputFlags::VARY_REVISION_SHA1 && $revisionRecord ) {
1618 $sha1 = $revisionRecord->getSha1();
1619 }
catch ( RevisionAccessException ) {
1622 $parserOutput->setRevisionUsedSha1Base36( $sha1 );
1626 return $revisionRecord;
1636 public static function pageid( $parser, $title =
null ) {
1637 $t = self::makeTitle( $parser, $title );
1640 } elseif ( !$t->canExist() || $t->isExternal() ) {
1646 if ( $t->equals( $parser->
getTitle() ) ) {
1649 $parserOutput->setOutputFlag( ParserOutputFlags::VARY_PAGE_ID );
1650 $id = $parser->
getTitle()->getArticleID();
1652 $parserOutput->setSpeculativePageIdUsed( $id );
1659 $linkCache = MediaWikiServices::getInstance()->getLinkCache();
1660 $pdbk = $t->getPrefixedDBkey();
1661 $id = $linkCache->getGoodLinkID( $pdbk );
1662 if ( $id != 0 || $linkCache->isBadLink( $pdbk ) ) {
1663 $parserOutput->addLink( $t, $id );
1670 $id = $t->getArticleID();
1671 $parserOutput->addLink( $t, $id );
1687 $t = self::makeTitle( $parser, $title );
1688 if ( $t ===
null || $t->isExternal() ) {
1692 $services = MediaWikiServices::getInstance();
1694 $t->equals( $parser->
getTitle() ) &&
1695 $services->getMainConfig()->get( MainConfigNames::MiserMode ) &&
1696 !$parser->
getOptions()->getInterfaceMessage() &&
1698 $services->getNamespaceInfo()->isSubject( $t->getNamespace() )
1705 $parser->
getOutput()->setOutputFlag( ParserOutputFlags::VARY_REVISION_EXISTS );
1713 if ( $t->equals( $parser->
getTitle() ) && $title ===
null ) {
1716 $parser->
getOutput()->setOutputFlag( ParserOutputFlags::VARY_REVISION_ID );
1721 $id = $rev->getId();
1725 $id = $parser->
getOptions()->getSpeculativeRevId();
1727 $parser->
getOutput()->setSpeculativeRevIdUsed( $id );
1732 $rev = self::getCachedRevisionObject( $parser, $t, ParserOutputFlags::VARY_REVISION_ID );
1733 return $rev ? $rev->getId() :
'';
1736 private static function getRevisionTimestampSubstring(
1749 if ( $title->equals( $parser->getTitle() ) && !$parser->getOptions()->getInterfaceMessage() ) {
1764 if ( $resNow !== $resThen ) {
1767 $parser->
getOutput()->setOutputFlag( ParserOutputFlags::VARY_REVISION_TIMESTAMP );
1773 $rev = self::getCachedRevisionObject( $parser, $title, ParserOutputFlags::VARY_REVISION_TIMESTAMP );
1792 $t = self::makeTitle( $parser, $title );
1793 if ( $t ===
null || $t->isExternal() ) {
1796 return strval( (
int)self::getRevisionTimestampSubstring(
1797 $parser, $t, 6, 2, self::MAX_TTS
1809 $t = self::makeTitle( $parser, $title );
1810 if ( $t ===
null || $t->isExternal() ) {
1813 return self::getRevisionTimestampSubstring(
1814 $parser, $t, 6, 2, self::MAX_TTS
1826 $t = self::makeTitle( $parser, $title );
1827 if ( $t ===
null || $t->isExternal() ) {
1830 return self::getRevisionTimestampSubstring(
1831 $parser, $t, 4, 2, self::MAX_TTS
1843 $t = self::makeTitle( $parser, $title );
1844 if ( $t ===
null || $t->isExternal() ) {
1847 return strval( (
int)self::getRevisionTimestampSubstring(
1848 $parser, $t, 4, 2, self::MAX_TTS
1860 $t = self::makeTitle( $parser, $title );
1861 if ( $t ===
null || $t->isExternal() ) {
1864 return self::getRevisionTimestampSubstring(
1865 $parser, $t, 0, 4, self::MAX_TTS
1877 $t = self::makeTitle( $parser, $title );
1878 if ( $t ===
null || $t->isExternal() ) {
1881 return self::getRevisionTimestampSubstring(
1882 $parser, $t, 0, 14, self::MAX_TTS
1894 $t = self::makeTitle( $parser, $title );
1895 if ( $t ===
null || $t->isExternal() ) {
1901 if ( $t->equals( $parser->
getTitle() ) ) {
1903 $parser->
getOutput()->setOutputFlag( ParserOutputFlags::VARY_USER );
1910 $rev = self::getCachedRevisionObject( $parser, $t, ParserOutputFlags::VARY_USER );
1911 $user = ( $rev !== null ) ? $rev->getUser() :
null;
1912 return $user ? $user->getName() :
'';
1928 $titleObject = Title::newFromText( $title ) ?? $parser->
getTitle();
1929 $restrictionStore = MediaWikiServices::getInstance()->getRestrictionStore();
1930 if ( $restrictionStore->areCascadeProtectionSourcesLoaded( $titleObject )
1934 $sources = $restrictionStore->getCascadeProtectionSources( $titleObject );
1935 $titleFormatter = MediaWikiServices::getInstance()->getTitleFormatter();
1936 foreach ( $sources[0] as $sourcePageIdentity ) {
1937 $names[] = $titleFormatter->getPrefixedText( $sourcePageIdentity );
1939 return implode(
'|', $names );
1955 'contentmodel_canonical',
1956 'contentmodel_local',
1960 $formatType = $format ===
null ?
'contentmodel_local' :
1963 $t = self::makeTitle( $parser, $title );
1964 if ( $t ===
null ) {
1972 $contentModel = $t->getContentModel();
1973 if ( $formatType ===
'contentmodel_canonical' ) {
1975 } elseif ( $formatType ===
'contentmodel_local' ) {
1976 $localizedContentModel = ContentHandler::getLocalizedName( $contentModel, $parser->
getTargetLanguage() );
1989 public static function isbn( $parser, $isbn =
'' ) {
1990 $space =
'(?:\t| |&\#0*160;|&\#[Xx]0*[Aa]0;|\p{Zs})';
1991 $spdash =
"(?:-|$space)"; # a dash or a non-newline space
1993 (?: 97[89] $spdash? )? # optional 13-digit ISBN prefix
1994 (?: [0-9] $spdash? ){9} # 9 digits with opt. delimiters
1995 [0-9Xx] # check digit
1997 if ( !preg_match( $regex, $isbn ) ) {
1998 return [
'found' => false ];
2000 $isbn = preg_replace(
"/$space/",
' ', $isbn );
2001 $num = strtr( $isbn, [
2006 $target = SpecialPage::getTitleFor(
'Booksources', $num );
2007 $parser->
getOutput()->addLink( $target );
2013 'class' =>
'internal mw-magiclink-isbn',
2028 public static function interwikilink( $parser, $prefix =
'', $title =
'', $linkText =
null ) {
2029 $services = MediaWikiServices::getInstance();
2032 $services->getInterwikiLookup()->isValidInterwiki( $prefix )
2035 $target = self::getTitleValueSafe( $title, $prefix );
2037 if ( $target !==
null ) {
2038 if ( $linkText !==
null ) {
2039 $linkText = Parser::stripOuterParagraph(
2040 # FIXME T382287: when
using Parsoid
this may leave
2041 # strip markers behind for embedded extension tags.
2045 $parser->
getOutput()->addInterwikiLink( $target );
2047 'text' => Linker::link( $target, $linkText ),
2053 return [
'found' => false ];
2064 $services = MediaWikiServices::getInstance();
2065 $extraInterlanguageLinkPrefixes = $services->getMainConfig()->get(
2066 MainConfigNames::ExtraInterlanguageLinkPrefixes
2070 $services->getInterwikiLookup()->isValidInterwiki( $prefix ) &&
2072 $services->getLanguageNameUtils()->getLanguageName(
2073 $prefix, LanguageNameUtils::AUTONYMS, LanguageNameUtils::DEFINED
2074 ) || in_array( $prefix, $extraInterlanguageLinkPrefixes,
true )
2078 $target = self::getTitleValueSafe( $title, $prefix );
2080 if ( $target !==
null ) {
2081 $parser->
getOutput()->addLanguageLink( $target );
2086 return [
'found' => false ];
2104 private static function getTitleValueSafe( $title, $prefix ): ?
TitleValue {
2105 [ $title, $frag ] = array_pad( explode(
'#', $title, 2 ), 2,
'' );
2109 }
catch ( InvalidArgumentException ) {
2116class_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.