12use InvalidArgumentException;
28use 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 ??
'' ) ) {
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() ) {
1038 return wfEscapeWikiText( $t->getTalkPageIfDefined()->getPrefixedText() ??
'' );
1047 $t = self::makeTitle( $parser, $title );
1048 if ( $t ===
null || !$t->canHaveTalkPage() ) {
1061 $t = self::makeTitle( $parser, $title );
1062 if ( $t ===
null ) {
1065 return wfEscapeWikiText( Title::newFromLinkTarget( $namespaceInfo->getSubjectPage( $t ) )->getPrefixedText() );
1075 $t = self::makeTitle( $parser, $title );
1076 if ( $t ===
null ) {
1079 return wfEscapeWikiText( Title::newFromLinkTarget( $namespaceInfo->getSubjectPage( $t ) )->getPrefixedURL() );
1096 'pagesincategory_all',
1097 'pagesincategory_pages',
1098 'pagesincategory_subcats',
1099 'pagesincategory_files'
1105 if ( self::matchAgainstMagicword( $parser->
getMagicWordFactory(),
'rawsuffix', $arg1 ) ) {
1115 $type =
'pagesincategory_all';
1118 $title = Title::makeTitleSafe(
NS_CATEGORY, $name );
1119 if ( !$title ) { # invalid title
1123 ->getLanguageConverterFactory()
1125 $languageConverter->findVariantLink( $name, $title,
true );
1128 $name = $title->getDBkey();
1130 if ( !isset( $cache[$name] ) ) {
1131 $category = Category::newFromTitle( $title );
1133 $allCount = $subcatCount = $fileCount = $pageCount = 0;
1135 $allCount = $category->getMemberCount();
1136 $subcatCount = $category->getSubcatCount();
1137 $fileCount = $category->getFileCount();
1138 $pageCount = $category->getPageCount( Category::COUNT_CONTENT_PAGES );
1140 $cache[$name][
'pagesincategory_all'] = $allCount;
1141 $cache[$name][
'pagesincategory_pages'] = $pageCount;
1142 $cache[$name][
'pagesincategory_subcats'] = $subcatCount;
1143 $cache[$name][
'pagesincategory_files'] = $fileCount;
1146 $count = $cache[$name][$type];
1159 public static function pagesize( $parser, $page =
'', $raw =
null ) {
1160 $title = Title::newFromText( $page );
1162 if ( !is_object( $title ) || $title->isExternal() ) {
1167 $rev = self::getCachedRevisionObject( $parser, $title, ParserOutputFlags::VARY_REVISION_SHA1 );
1168 $length = $rev ? $rev->getSize() : 0;
1169 if ( $length ===
null ) {
1189 $titleObject = Title::newFromText( $title ) ?? $parser->
getTitle();
1192 $restrictions = $restrictionStore->getRestrictions( $titleObject, strtolower( $type ) );
1193 # RestrictionStore::getRestrictions returns an array, its possible it may have
1194 # multiple values in the future
1195 return implode(
',', $restrictions );
1213 $titleObject = Title::newFromText( $title ) ?? $parser->
getTitle();
1218 return $restrictionStore->getRestrictionExpiry( $titleObject, strtolower( $type ) ) ??
'';
1231 public static function language( $parser, $code =
'', $inLanguage =
'' ) {
1232 if ( $code ===
'' ) {
1235 if ( $inLanguage ===
'' ) {
1236 $inLanguage = LanguageNameUtils::AUTONYMS;
1239 ->getLanguageNameUtils()
1240 ->getLanguageName( $code, $inLanguage );
1241 return $lang !==
'' ? $lang : LanguageCode::bcp47( $code );
1255 public static function dir(
Parser $parser,
string $code =
'',
string $arg =
'' ): string {
1259 if ( $code ===
'' ) {
1262 if ( $arg !==
'' ) {
1266 if (
$magicWords->matchStartToEnd( $arg ) ===
'language_option_bcp47' ) {
1268 $code =
new Bcp47CodeValue( $code );
1272 $lang = $languageFactory->getLanguage( $code );
1273 }
catch ( InvalidArgumentException ) {
1278 return $lang->getDir();
1289 public static function bcp47(
Parser $parser,
string $code =
'' ): string {
1290 if ( $code ===
'' ) {
1293 return LanguageCode::bcp47( $code );
1307 $parser, $string, $length, $padding =
'0', $direction = STR_PAD_RIGHT
1310 $lengthOfPadding = mb_strlen( $padding );
1311 if ( $lengthOfPadding == 0 ) {
1315 # The remaining length to add counts down to 0 as padding is added
1316 $length = min( (
int)$length, 500 ) - mb_strlen( $string );
1317 if ( $length <= 0 ) {
1322 # $finalPadding is just $padding repeated enough times so that
1323 # mb_strlen( $string ) + mb_strlen( $finalPadding ) == $length
1325 while ( $length > 0 ) {
1326 # If $length < $lengthofPadding, truncate $padding so we get the
1327 # exact length desired.
1328 $finalPadding .= mb_substr( $padding, 0, $length );
1329 $length -= $lengthOfPadding;
1332 if ( $direction == STR_PAD_LEFT ) {
1333 return $finalPadding . $string;
1335 return $string . $finalPadding;
1346 public static function padleft( $parser, $string =
'', $length =
'0', $padding =
'0' ) {
1347 return self::pad( $parser, $string, $length, $padding, STR_PAD_LEFT );
1357 public static function padright( $parser, $string =
'', $length =
'0', $padding =
'0' ) {
1358 return self::pad( $parser, $string, $length, $padding );
1369 $encodedSection = Sanitizer::safeEncodeAttribute( $section );
1371 return str_replace(
'_',
'_', $encodedSection );
1379 public static function special( $parser, $text ) {
1380 [ $page, $subpage ] = MediaWikiServices::getInstance()->getSpecialPageFactory()->
1381 resolveAlias( $text );
1383 $title = SpecialPage::getTitleFor( $page, $subpage );
1384 return $title->getPrefixedText();
1387 $title = Title::makeTitleSafe(
NS_SPECIAL, $text );
1388 return $title ? $title->getPrefixedText() : self::special( $parser,
'Badtitle' );
1398 return wfUrlencode( str_replace(
' ',
'_', self::special( $parser, $text ) ) );
1413 [
'defaultsort_noerror',
'defaultsort_noreplace' ] );
1417 $text = trim( $text );
1418 if ( $text ===
'' ) {
1421 $old = $parser->
getOutput()->getPageProperty(
'defaultsort' );
1422 if ( $old ===
null || $arg !==
'defaultsort_noreplace' ) {
1423 $parser->
getOutput()->setPageProperty(
'defaultsort', $text );
1426 if ( $old ===
null || $old == $text || $arg ) {
1430 return '<span class="error">' .
1431 $parser->
msg(
'duplicate-defaultsort',
1451 public static function filepath( $parser, $name =
'', $argA =
'', $argB =
'' ) {
1452 $file = MediaWikiServices::getInstance()->getRepoGroup()->findFile( $name );
1454 if ( $argA ==
'nowiki' ) {
1461 $isNowiki = ( $argB ==
'nowiki' );
1465 $url = $file->getFullUrl();
1468 if ( count( $parsedWidthParam ) ) {
1469 $mto = $file->transform( $parsedWidthParam );
1471 if ( $mto && !$mto->isError() ) {
1473 $urlUtils = MediaWikiServices::getInstance()->getUrlUtils();
1478 return [
$url,
'nowiki' => true ];
1493 public static function tagObj( $parser, $frame, $args ) {
1494 if ( !count( $args ) ) {
1497 $tagName = strtolower( trim( $parser->
killMarkers(
1498 $frame->expand( array_shift( $args ) )
1502 if ( count( $args ) ) {
1506 $inner = $frame->expand( array_shift( $args ), $processNowiki );
1507 if ( $processNowiki ) {
1520 foreach ( $args as $arg ) {
1521 $bits = $arg->splitArg();
1522 if ( strval( $bits[
'index'] ) ===
'' ) {
1523 $name = trim( $frame->expand( $bits[
'name'], PPFrame::STRIP_COMMENTS ) );
1524 $value = trim( $frame->expand( $bits[
'value'] ) );
1525 if ( preg_match(
'/^(?:["\'](.+)["\']|""|\'\')$/s', $value, $m ) ) {
1526 $value = $m[1] ??
'';
1528 $attributes[$name] = $value;
1533 if ( !in_array( $tagName, $stripList ) ) {
1536 foreach ( $attributes as $name => $value ) {
1537 $attrText .=
' ' . htmlspecialchars( $name ) .
1538 '="' . htmlspecialchars( $parser->
killMarkers( $value ), ENT_COMPAT ) .
'"';
1540 if ( $inner ===
null ) {
1541 return "<$tagName$attrText/>";
1543 return "<$tagName$attrText>$inner</$tagName>";
1549 'attributes' => $attributes,
1550 'close' =>
"</$tagName>",
1568 private static function getCachedRevisionObject( $parser, $title, $vary ) {
1573 $revisionRecord =
null;
1575 $isSelfReferential = $title->equals( $parser->
getTitle() );
1576 if ( $isSelfReferential ) {
1587 if ( $parserRevisionRecord && $parserRevisionRecord->isCurrent() ) {
1588 $revisionRecord = $parserRevisionRecord;
1593 if ( !$revisionRecord ) {
1602 if ( !$revisionRecord ) {
1604 $revisionRecord =
null;
1607 $parserOutput->addTemplate(
1609 $revisionRecord ? $revisionRecord->getPageId() : 0,
1610 $revisionRecord ? $revisionRecord->getId() : 0
1614 if ( $isSelfReferential ) {
1615 wfDebug( __METHOD__ .
": used current revision, setting {$vary->value}" );
1617 $parserOutput->setOutputFlag( $vary );
1618 if ( $vary === ParserOutputFlags::VARY_REVISION_SHA1 && $revisionRecord ) {
1620 $sha1 = $revisionRecord->getSha1();
1621 }
catch ( RevisionAccessException ) {
1624 $parserOutput->setRevisionUsedSha1Base36( $sha1 );
1628 return $revisionRecord;
1638 public static function pageid( $parser, $title =
null ) {
1639 $t = self::makeTitle( $parser, $title );
1642 } elseif ( !$t->canExist() || $t->isExternal() ) {
1648 if ( $t->equals( $parser->
getTitle() ) ) {
1651 $parserOutput->setOutputFlag( ParserOutputFlags::VARY_PAGE_ID );
1652 $id = $parser->
getTitle()->getArticleID();
1654 $parserOutput->setSpeculativePageIdUsed( $id );
1661 $linkCache = MediaWikiServices::getInstance()->getLinkCache();
1662 $pdbk = $t->getPrefixedDBkey();
1663 $id = $linkCache->getGoodLinkID( $pdbk );
1664 if ( $id != 0 || $linkCache->isBadLink( $pdbk ) ) {
1665 $parserOutput->addLink( $t, $id );
1672 $id = $t->getArticleID();
1673 $parserOutput->addLink( $t, $id );
1689 $t = self::makeTitle( $parser, $title );
1690 if ( $t ===
null || $t->isExternal() ) {
1695 $t->equals( $parser->
getTitle() ) &&
1697 !$parser->
getOptions()->getInterfaceMessage() &&
1706 $parser->
getOutput()->setOutputFlag( ParserOutputFlags::VARY_REVISION_EXISTS );
1714 if ( $t->equals( $parser->
getTitle() ) && $title ===
null ) {
1717 $parser->
getOutput()->setOutputFlag( ParserOutputFlags::VARY_REVISION_ID );
1722 $id = $rev->getId();
1726 $id = $parser->
getOptions()->getSpeculativeRevId();
1728 $parser->
getOutput()->setSpeculativeRevIdUsed( $id );
1733 $rev = self::getCachedRevisionObject( $parser, $t, ParserOutputFlags::VARY_REVISION_ID );
1734 return $rev ? $rev->getId() :
'';
1737 private static function getRevisionTimestampSubstring(
1750 if ( $title->equals( $parser->getTitle() ) && !$parser->getOptions()->getInterfaceMessage() ) {
1765 if ( $resNow !== $resThen ) {
1768 $parser->
getOutput()->setOutputFlag( ParserOutputFlags::VARY_REVISION_TIMESTAMP );
1774 $rev = self::getCachedRevisionObject( $parser, $title, ParserOutputFlags::VARY_REVISION_TIMESTAMP );
1793 $t = self::makeTitle( $parser, $title );
1794 if ( $t ===
null || $t->isExternal() ) {
1797 return strval( (
int)self::getRevisionTimestampSubstring(
1798 $parser, $t, 6, 2, self::MAX_TTS
1810 $t = self::makeTitle( $parser, $title );
1811 if ( $t ===
null || $t->isExternal() ) {
1814 return self::getRevisionTimestampSubstring(
1815 $parser, $t, 6, 2, self::MAX_TTS
1827 $t = self::makeTitle( $parser, $title );
1828 if ( $t ===
null || $t->isExternal() ) {
1831 return self::getRevisionTimestampSubstring(
1832 $parser, $t, 4, 2, self::MAX_TTS
1844 $t = self::makeTitle( $parser, $title );
1845 if ( $t ===
null || $t->isExternal() ) {
1848 return strval( (
int)self::getRevisionTimestampSubstring(
1849 $parser, $t, 4, 2, self::MAX_TTS
1861 $t = self::makeTitle( $parser, $title );
1862 if ( $t ===
null || $t->isExternal() ) {
1865 return self::getRevisionTimestampSubstring(
1866 $parser, $t, 0, 4, self::MAX_TTS
1878 $t = self::makeTitle( $parser, $title );
1879 if ( $t ===
null || $t->isExternal() ) {
1882 return self::getRevisionTimestampSubstring(
1883 $parser, $t, 0, 14, self::MAX_TTS
1895 $t = self::makeTitle( $parser, $title );
1896 if ( $t ===
null || $t->isExternal() ) {
1902 if ( $t->equals( $parser->
getTitle() ) ) {
1904 $parser->
getOutput()->setOutputFlag( ParserOutputFlags::VARY_USER );
1911 $rev = self::getCachedRevisionObject( $parser, $t, ParserOutputFlags::VARY_USER );
1912 $user = ( $rev !== null ) ? $rev->getUser() :
null;
1913 return $user ? $user->getName() :
'';
1929 $titleObject = Title::newFromText( $title ) ?? $parser->
getTitle();
1930 $restrictionStore = MediaWikiServices::getInstance()->getRestrictionStore();
1931 if ( $restrictionStore->areCascadeProtectionSourcesLoaded( $titleObject )
1935 $sources = $restrictionStore->getCascadeProtectionSources( $titleObject );
1936 $titleFormatter = MediaWikiServices::getInstance()->getTitleFormatter();
1937 foreach ( $sources[0] as $sourcePageIdentity ) {
1938 $names[] = $titleFormatter->getPrefixedText( $sourcePageIdentity );
1940 return implode(
'|', $names );
1956 'contentmodel_canonical',
1957 'contentmodel_local',
1961 $formatType = $format ===
null ?
'contentmodel_local' :
1964 $t = self::makeTitle( $parser, $title );
1965 if ( $t ===
null ) {
1973 $contentModel = $t->getContentModel();
1974 if ( $formatType ===
'contentmodel_canonical' ) {
1976 } elseif ( $formatType ===
'contentmodel_local' ) {
1977 $localizedContentModel = ContentHandler::getLocalizedName( $contentModel, $parser->
getTargetLanguage() );
1990 public static function isbn( $parser, $isbn =
'' ) {
1991 $space =
'(?:\t| |&\#0*160;|&\#[Xx]0*[Aa]0;|\p{Zs})';
1992 $spdash =
"(?:-|$space)"; # a dash or a non-newline space
1994 (?: 97[89] $spdash? )? # optional 13-digit ISBN prefix
1995 (?: [0-9] $spdash? ){9} # 9 digits with opt. delimiters
1996 [0-9Xx] # check digit
1998 if ( !preg_match( $regex, $isbn ) ) {
1999 return [
'found' => false ];
2001 $isbn = preg_replace(
"/$space/",
' ', $isbn );
2002 $num = strtr( $isbn, [
2007 $target = SpecialPage::getTitleFor(
'Booksources', $num );
2008 $parser->
getOutput()->addLink( $target );
2014 'class' =>
'internal mw-magiclink-isbn',
2029 public static function interwikilink( $parser, $prefix =
'', $title =
'', $linkText =
null ) {
2030 $services = MediaWikiServices::getInstance();
2033 $services->getInterwikiLookup()->isValidInterwiki( $prefix )
2036 $target = self::getTitleValueSafe( $title, $prefix );
2038 if ( $target !==
null ) {
2039 $displayText =
null;
2040 if ( $linkText !==
null ) {
2041 $linkText = Parser::stripOuterParagraph(
2042 # FIXME T382287: when
using Parsoid
this may leave
2043 # strip markers behind for embedded extension tags.
2046 $displayText =
new HTMLArmor( $linkText );
2048 $parser->
getOutput()->addInterwikiLink( $target );
2050 'text' => $services->getLinkRenderer()->makeLink(
2059 return [
'found' => false ];
2070 $services = MediaWikiServices::getInstance();
2071 $extraInterlanguageLinkPrefixes = $services->getMainConfig()->get(
2072 MainConfigNames::ExtraInterlanguageLinkPrefixes
2076 $services->getInterwikiLookup()->isValidInterwiki( $prefix ) &&
2078 $services->getLanguageNameUtils()->getLanguageName(
2079 $prefix, LanguageNameUtils::AUTONYMS, LanguageNameUtils::DEFINED
2080 ) || in_array( $prefix, $extraInterlanguageLinkPrefixes,
true )
2084 $target = self::getTitleValueSafe( $title, $prefix );
2086 if ( $target !==
null ) {
2087 $parser->
getOutput()->addLanguageLink( $target );
2092 return [
'found' => false ];
2110 private static function getTitleValueSafe( $title, $prefix ): ?
TitleValue {
2111 [ $title, $frag ] = array_pad( explode(
'#', $title, 2 ), 2,
'' );
2115 }
catch ( InvalidArgumentException ) {
2122class_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.