MediaWiki master
CoreParserFunctions.php
Go to the documentation of this file.
1<?php
38use Wikimedia\RemexHtml\Tokenizer\Attributes;
39use Wikimedia\RemexHtml\Tokenizer\PlainAttributes;
40
47 private const MAX_TTS = 900;
48
52 public const REGISTER_OPTIONS = [
53 // See documentation for the corresponding config options
54 MainConfigNames::AllowDisplayTitle,
55 MainConfigNames::AllowSlowParserFunctions,
56 ];
57
65 public static function register( Parser $parser, ServiceOptions $options ) {
66 $options->assertRequiredOptions( self::REGISTER_OPTIONS );
67 $allowDisplayTitle = $options->get( MainConfigNames::AllowDisplayTitle );
68 $allowSlowParserFunctions = $options->get( MainConfigNames::AllowSlowParserFunctions );
69
70 # Syntax for arguments (see Parser::setFunctionHook):
71 # "name for lookup in localized magic words array",
72 # function callback,
73 # optional Parser::SFH_NO_HASH to omit the hash from calls (e.g. {{int:...}}
74 # instead of {{#int:...}})
75 $noHashFunctions = [
76 'ns', 'nse', 'urlencode', 'lcfirst', 'ucfirst', 'lc', 'uc',
77 'localurl', 'localurle', 'fullurl', 'fullurle', 'canonicalurl',
78 'canonicalurle', 'formatnum', 'grammar', 'gender', 'plural', 'formal',
79 'bidi', 'numberingroup', 'language',
80 'padleft', 'padright', 'anchorencode', 'defaultsort', 'filepath',
81 'pagesincategory', 'pagesize', 'protectionlevel', 'protectionexpiry',
82 # The following are the "parser function" forms of magic
83 # variables defined in CoreMagicVariables. The no-args form will
84 # go through the magic variable code path (and be cached); the
85 # presence of arguments will cause the parser function form to
86 # be invoked. (Note that the actual implementation will pass
87 # a Parser object as first argument, in addition to the
88 # parser function parameters.)
89
90 # For this group, the first parameter to the parser function is
91 # "page title", and the no-args form (and the magic variable)
92 # defaults to "current page title".
93 'pagename', 'pagenamee',
94 'fullpagename', 'fullpagenamee',
95 'subpagename', 'subpagenamee',
96 'rootpagename', 'rootpagenamee',
97 'basepagename', 'basepagenamee',
98 'talkpagename', 'talkpagenamee',
99 'subjectpagename', 'subjectpagenamee',
100 'pageid', 'revisionid', 'revisionday',
101 'revisionday2', 'revisionmonth', 'revisionmonth1', 'revisionyear',
102 'revisiontimestamp',
103 'revisionuser',
104 'cascadingsources',
105 'namespace', 'namespacee', 'namespacenumber', 'talkspace', 'talkspacee',
106 'subjectspace', 'subjectspacee',
107
108 # More parser functions corresponding to CoreMagicVariables.
109 # For this group, the first parameter to the parser function is
110 # "raw" (uses the 'raw' format if present) and the no-args form
111 # (and the magic variable) defaults to 'not raw'.
112 'numberofarticles', 'numberoffiles',
113 'numberofusers',
114 'numberofactiveusers',
115 'numberofpages',
116 'numberofadmins',
117 'numberofedits',
118 ];
119 foreach ( $noHashFunctions as $func ) {
120 $parser->setFunctionHook( $func, [ __CLASS__, $func ], Parser::SFH_NO_HASH );
121 }
122
123 $parser->setFunctionHook( 'int', [ __CLASS__, 'intFunction' ], Parser::SFH_NO_HASH );
124 $parser->setFunctionHook( 'special', [ __CLASS__, 'special' ] );
125 $parser->setFunctionHook( 'speciale', [ __CLASS__, 'speciale' ] );
126 $parser->setFunctionHook( 'tag', [ __CLASS__, 'tagObj' ], Parser::SFH_OBJECT_ARGS );
127 $parser->setFunctionHook( 'formatdate', [ __CLASS__, 'formatDate' ] );
128
129 if ( $allowDisplayTitle ) {
130 $parser->setFunctionHook(
131 'displaytitle',
132 [ __CLASS__, 'displaytitle' ],
133 Parser::SFH_NO_HASH
134 );
135 }
136 if ( $allowSlowParserFunctions ) {
137 $parser->setFunctionHook(
138 'pagesinnamespace',
139 [ __CLASS__, 'pagesinnamespace' ],
140 Parser::SFH_NO_HASH
141 );
142 }
143 }
144
151 public static function intFunction( $parser, $part1 = '', ...$params ) {
152 if ( strval( $part1 ) !== '' ) {
153 $message = wfMessage( $part1, $params )
154 ->inLanguage( $parser->getOptions()->getUserLangObj() );
155 return [ $message->plain(), 'noparse' => false ];
156 } else {
157 return [ 'found' => false ];
158 }
159 }
160
168 public static function formatDate( $parser, $date, $defaultPref = null ) {
169 $lang = $parser->getTargetLanguage();
170 $df = MediaWikiServices::getInstance()->getDateFormatterFactory()->get( $lang );
171
172 $date = trim( $date );
173
174 $pref = $parser->getOptions()->getDateFormat();
175
176 // Specify a different default date format other than the normal default
177 // if the user has 'default' for their setting
178 if ( $pref == 'default' && $defaultPref ) {
179 $pref = $defaultPref;
180 }
181
182 $date = $df->reformat( $pref, $date, [ 'match-whole' ] );
183 return $date;
184 }
185
186 public static function ns( $parser, $part1 = '' ) {
187 if ( intval( $part1 ) || $part1 == "0" ) {
188 $index = intval( $part1 );
189 } else {
190 $index = $parser->getContentLanguage()->getNsIndex( str_replace( ' ', '_', $part1 ) );
191 }
192 if ( $index !== false ) {
193 return $parser->getContentLanguage()->getFormattedNsText( $index );
194 } else {
195 return [ 'found' => false ];
196 }
197 }
198
199 public static function nse( $parser, $part1 = '' ) {
200 $ret = self::ns( $parser, $part1 );
201 if ( is_string( $ret ) ) {
202 $ret = wfUrlencode( str_replace( ' ', '_', $ret ) );
203 }
204 return $ret;
205 }
206
219 public static function urlencode( $parser, $s = '', $arg = null ) {
220 static $magicWords = null;
221 if ( $magicWords === null ) {
223 $parser->getMagicWordFactory()->newArray( [ 'url_path', 'url_query', 'url_wiki' ] );
224 }
225 switch ( $magicWords->matchStartToEnd( $arg ?? '' ) ) {
226 // Encode as though it's a wiki page, '_' for ' '.
227 case 'url_wiki':
228 $func = 'wfUrlencode';
229 $s = str_replace( ' ', '_', $s );
230 break;
231
232 // Encode for an HTTP Path, '%20' for ' '.
233 case 'url_path':
234 $func = 'rawurlencode';
235 break;
236
237 // Encode for HTTP query, '+' for ' '.
238 case 'url_query':
239 default:
240 $func = 'urlencode';
241 }
242 // See T105242, where the choice to kill markers and various
243 // other options were discussed.
244 return $func( $parser->killMarkers( $s ) );
245 }
246
247 public static function lcfirst( $parser, $s = '' ) {
248 return $parser->getContentLanguage()->lcfirst( $s );
249 }
250
251 public static function ucfirst( $parser, $s = '' ) {
252 return $parser->getContentLanguage()->ucfirst( $s );
253 }
254
260 public static function lc( $parser, $s = '' ) {
261 return $parser->markerSkipCallback( $s, [ $parser->getContentLanguage(), 'lc' ] );
262 }
263
269 public static function uc( $parser, $s = '' ) {
270 return $parser->markerSkipCallback( $s, [ $parser->getContentLanguage(), 'uc' ] );
271 }
272
273 public static function localurl( $parser, $s = '', $arg = null ) {
274 return self::urlFunction( 'getLocalURL', $s, $arg );
275 }
276
277 public static function localurle( $parser, $s = '', $arg = null ) {
278 $temp = self::urlFunction( 'getLocalURL', $s, $arg );
279 if ( !is_string( $temp ) ) {
280 return $temp;
281 } else {
282 return htmlspecialchars( $temp, ENT_COMPAT );
283 }
284 }
285
286 public static function fullurl( $parser, $s = '', $arg = null ) {
287 return self::urlFunction( 'getFullURL', $s, $arg );
288 }
289
290 public static function fullurle( $parser, $s = '', $arg = null ) {
291 $temp = self::urlFunction( 'getFullURL', $s, $arg );
292 if ( !is_string( $temp ) ) {
293 return $temp;
294 } else {
295 return htmlspecialchars( $temp, ENT_COMPAT );
296 }
297 }
298
299 public static function canonicalurl( $parser, $s = '', $arg = null ) {
300 return self::urlFunction( 'getCanonicalURL', $s, $arg );
301 }
302
303 public static function canonicalurle( $parser, $s = '', $arg = null ) {
304 $temp = self::urlFunction( 'getCanonicalURL', $s, $arg );
305 if ( !is_string( $temp ) ) {
306 return $temp;
307 } else {
308 return htmlspecialchars( $temp, ENT_COMPAT );
309 }
310 }
311
312 public static function urlFunction( $func, $s = '', $arg = null ) {
313 # Due to order of execution of a lot of bits, the values might be encoded
314 # before arriving here; if that's true, then the title can't be created
315 # and the variable will fail. If we can't get a decent title from the first
316 # attempt, url-decode and try for a second.
317 $title = Title::newFromText( $s ) ?? Title::newFromURL( urldecode( $s ) );
318 if ( $title !== null ) {
319 # Convert NS_MEDIA -> NS_FILE
320 if ( $title->inNamespace( NS_MEDIA ) ) {
321 $title = Title::makeTitle( NS_FILE, $title->getDBkey() );
322 }
323 if ( $arg !== null ) {
324 $text = $title->$func( $arg );
325 } else {
326 $text = $title->$func();
327 }
328 return $text;
329 } else {
330 return [ 'found' => false ];
331 }
332 }
333
340 public static function formatnum( $parser, $num = '', $arg = null ) {
341 if ( self::matchAgainstMagicword( $parser->getMagicWordFactory(), 'rawsuffix', $arg ) ) {
342 $func = [ $parser->getTargetLanguage(), 'parseFormattedNumber' ];
343 } elseif (
344 self::matchAgainstMagicword( $parser->getMagicWordFactory(), 'nocommafysuffix', $arg )
345 ) {
346 $func = [ $parser->getTargetLanguage(), 'formatNumNoSeparators' ];
347 $func = self::getLegacyFormatNum( $parser, $func );
348 } else {
349 $func = [ $parser->getTargetLanguage(), 'formatNum' ];
350 $func = self::getLegacyFormatNum( $parser, $func );
351 }
352 return $parser->markerSkipCallback( $num, $func );
353 }
354
361 private static function getLegacyFormatNum( $parser, $callback ) {
362 // For historic reasons, the formatNum parser function will
363 // take arguments which are not actually formatted numbers,
364 // which then trigger deprecation warnings in Language::formatNum*.
365 // Instead emit a tracking category instead to allow linting.
366 return static function ( $number ) use ( $parser, $callback ) {
367 $validNumberRe = '(-(?=[\d\.]))?(\d+|(?=\.\d))(\.\d*)?([Ee][-+]?\d+)?';
368 if (
369 !is_numeric( $number ) &&
370 $number !== (string)NAN &&
371 $number !== (string)INF &&
372 $number !== (string)-INF
373 ) {
374 $parser->addTrackingCategory( 'nonnumeric-formatnum' );
375 // Don't split on NAN/INF in the legacy case since they are
376 // likely to be found embedded inside non-numeric text.
377 return preg_replace_callback( "/{$validNumberRe}/", static function ( $m ) use ( $callback ) {
378 return call_user_func( $callback, $m[0] );
379 }, $number );
380 }
381 return call_user_func( $callback, $number );
382 };
383 }
384
391 public static function grammar( $parser, $case = '', $word = '' ) {
392 $word = $parser->killMarkers( $word );
393 return $parser->getTargetLanguage()->convertGrammar( $word, $case );
394 }
395
402 public static function gender( $parser, $username, ...$forms ) {
403 // Some shortcuts to avoid loading user data unnecessarily
404 if ( count( $forms ) === 0 ) {
405 return '';
406 } elseif ( count( $forms ) === 1 ) {
407 return $forms[0];
408 }
409
410 $username = trim( $username );
411
412 $userOptionsLookup = MediaWikiServices::getInstance()->getUserOptionsLookup();
413 $gender = $userOptionsLookup->getDefaultOption( 'gender' );
414
415 // allow prefix and normalize (e.g. "&#42;foo" -> "*foo" ).
416 $title = Title::newFromText( $username, NS_USER );
417
418 if ( $title && $title->inNamespace( NS_USER ) ) {
419 $username = $title->getText();
420 }
421
422 // check parameter, or use the ParserOptions if in interface message
423 $user = User::newFromName( $username );
424 $genderCache = MediaWikiServices::getInstance()->getGenderCache();
425 if ( $user ) {
426 $gender = $genderCache->getGenderOf( $user, __METHOD__ );
427 } elseif ( $username === '' && $parser->getOptions()->getInterfaceMessage() ) {
428 $gender = $genderCache->getGenderOf( $parser->getOptions()->getUserIdentity(), __METHOD__ );
429 }
430 $ret = $parser->getTargetLanguage()->gender( $gender, $forms );
431 return $ret;
432 }
433
440 public static function plural( $parser, $text = '', ...$forms ) {
441 $text = $parser->getTargetLanguage()->parseFormattedNumber( $text );
442 settype( $text, ctype_digit( $text ) ? 'int' : 'float' );
443 // @phan-suppress-next-line PhanTypeMismatchArgument Phan does not handle settype
444 return $parser->getTargetLanguage()->convertPlural( $text, $forms );
445 }
446
447 public static function formal( Parser $parser, string ...$forms ): string {
448 $index = $parser->getTargetLanguage()->getFormalityIndex();
449 return $forms[$index] ?? $forms[0];
450 }
451
457 public static function bidi( $parser, $text = '' ) {
458 return $parser->getTargetLanguage()->embedBidi( $text );
459 }
460
470 public static function displaytitle( $parser, $text = '', $uarg = '' ) {
471 $restrictDisplayTitle = MediaWikiServices::getInstance()->getMainConfig()
472 ->get( MainConfigNames::RestrictDisplayTitle );
473
474 static $magicWords = null;
475 if ( $magicWords === null ) {
476 $magicWords = $parser->getMagicWordFactory()->newArray(
477 [ 'displaytitle_noerror', 'displaytitle_noreplace' ] );
478 }
479 $arg = $magicWords->matchStartToEnd( $uarg );
480
481 // parse a limited subset of wiki markup (just the single quote items)
482 $text = $parser->doQuotes( $text );
483
484 // remove stripped text (e.g. the UNIQ-QINU stuff) that was generated by tag extensions/whatever
485 $text = $parser->killMarkers( $text );
486
487 // See T28547 for rationale for this processing.
488 // list of disallowed tags for DISPLAYTITLE
489 // these will be escaped even though they are allowed in normal wiki text
490 $bad = [ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'div', 'blockquote', 'ol', 'ul', 'li', 'hr',
491 'table', 'tr', 'th', 'td', 'dl', 'dd', 'caption', 'p', 'ruby', 'rb', 'rt', 'rtc', 'rp', 'br' ];
492
493 // disallow some styles that could be used to bypass $wgRestrictDisplayTitle
494 if ( $restrictDisplayTitle ) {
495 // This code is tested with the cases marked T28547 in
496 // parserTests.txt
497 $htmlTagsCallback = static function ( Attributes $attr ): Attributes {
498 $decoded = $attr->getValues();
499
500 if ( isset( $decoded['style'] ) ) {
501 // this is called later anyway, but we need it right now for the regexes below to be safe
502 // calling it twice doesn't hurt
503 $decoded['style'] = Sanitizer::checkCss( $decoded['style'] );
504
505 if ( preg_match( '/(display|user-select|visibility)\s*:/i', $decoded['style'] ) ) {
506 $decoded['style'] = '/* attempt to bypass $wgRestrictDisplayTitle */';
507 }
508 }
509
510 return new PlainAttributes( $decoded );
511 };
512 } else {
513 $htmlTagsCallback = null;
514 }
515
516 // only requested titles that normalize to the actual title are allowed through
517 // if $wgRestrictDisplayTitle is true (it is by default)
518 // mimic the escaping process that occurs in OutputPage::setPageTitle
519 $text = Sanitizer::removeSomeTags( $text, [
520 'attrCallback' => $htmlTagsCallback,
521 'removeTags' => $bad,
522 ] );
523 $title = Title::newFromText( Sanitizer::stripAllTags( $text ) );
524 // Decode entities in $text the same way that Title::newFromText does
525 $filteredText = Sanitizer::decodeCharReferencesAndNormalize( $text );
526
527 if ( !$restrictDisplayTitle ||
528 ( $title instanceof Title
529 && !$title->hasFragment()
530 && $title->equals( $parser->getTitle() ) )
531 ) {
532 $old = $parser->getOutput()->getPageProperty( 'displaytitle' );
533 if ( $old === null || $arg !== 'displaytitle_noreplace' ) {
534 $parser->getOutput()->setDisplayTitle( $text );
535 }
536 if ( $old !== null && $old !== $text && !$arg ) {
537
538 $converter = $parser->getTargetLanguageConverter();
539 return '<span class="error">' .
540 $parser->msg( 'duplicate-displaytitle',
541 // Message should be parsed, but these params should only be escaped.
542 $converter->markNoConversion( wfEscapeWikiText( $old ) ),
543 $converter->markNoConversion( wfEscapeWikiText( $filteredText ) )
544 )->text() .
545 '</span>';
546 } else {
547 return '';
548 }
549 } else {
550 $parser->getOutput()->addWarningMsg(
551 'restricted-displaytitle',
552 // Message should be parsed, but this param should only be escaped.
553 Message::plaintextParam( $filteredText )
554 );
555 $parser->addTrackingCategory( 'restricted-displaytitle-ignored' );
556 }
557 }
558
568 private static function matchAgainstMagicword(
569 MagicWordFactory $magicWordFactory, $magicword, $value
570 ) {
571 $value = trim( strval( $value ) );
572 if ( $value === '' ) {
573 return false;
574 }
575 $mwObject = $magicWordFactory->get( $magicword );
576 return $mwObject->matchStartToEnd( $value );
577 }
578
588 public static function formatRaw(
589 $num, $raw, $language, MagicWordFactory $magicWordFactory = null
590 ) {
591 if ( $raw !== null && $raw !== '' ) {
592 if ( !$magicWordFactory ) {
593 $magicWordFactory = MediaWikiServices::getInstance()->getMagicWordFactory();
594 }
595 if ( self::matchAgainstMagicword( $magicWordFactory, 'rawsuffix', $raw ) ) {
596 return (string)$num;
597 }
598 }
599 return $language->formatNum( $num );
600 }
601
602 public static function numberofpages( $parser, $raw = null ) {
603 return self::formatRaw( SiteStats::pages(), $raw, $parser->getTargetLanguage() );
604 }
605
606 public static function numberofusers( $parser, $raw = null ) {
607 return self::formatRaw( SiteStats::users(), $raw, $parser->getTargetLanguage() );
608 }
609
610 public static function numberofactiveusers( $parser, $raw = null ) {
611 return self::formatRaw( SiteStats::activeUsers(), $raw, $parser->getTargetLanguage() );
612 }
613
614 public static function numberofarticles( $parser, $raw = null ) {
615 return self::formatRaw( SiteStats::articles(), $raw, $parser->getTargetLanguage() );
616 }
617
618 public static function numberoffiles( $parser, $raw = null ) {
619 return self::formatRaw( SiteStats::images(), $raw, $parser->getTargetLanguage() );
620 }
621
622 public static function numberofadmins( $parser, $raw = null ) {
623 return self::formatRaw(
624 SiteStats::numberingroup( 'sysop' ),
625 $raw,
626 $parser->getTargetLanguage()
627 );
628 }
629
630 public static function numberofedits( $parser, $raw = null ) {
631 return self::formatRaw( SiteStats::edits(), $raw, $parser->getTargetLanguage() );
632 }
633
634 public static function pagesinnamespace( $parser, $namespace = 0, $raw = null ) {
635 return self::formatRaw(
636 SiteStats::pagesInNs( intval( $namespace ) ),
637 $raw,
638 $parser->getTargetLanguage()
639 );
640 }
641
642 public static function numberingroup( $parser, $name = '', $raw = null ) {
643 return self::formatRaw(
644 SiteStats::numberingroup( strtolower( $name ) ),
645 $raw,
646 $parser->getTargetLanguage()
647 );
648 }
649
658 private static function makeTitle( Parser $parser, ?string $t ) {
659 if ( $t === null ) {
660 // For consistency with magic variable forms
661 $title = $parser->getTitle();
662 } else {
663 $title = Title::newFromText( $t );
664 }
665 return $title;
666 }
667
676 public static function namespace( $parser, $title = null ) {
677 $t = self::makeTitle( $parser, $title );
678 if ( $t === null ) {
679 return '';
680 }
681 return str_replace( '_', ' ', $t->getNsText() );
682 }
683
684 public static function namespacee( $parser, $title = null ) {
685 $t = self::makeTitle( $parser, $title );
686 if ( $t === null ) {
687 return '';
688 }
689 return wfUrlencode( $t->getNsText() );
690 }
691
692 public static function namespacenumber( $parser, $title = null ) {
693 $t = self::makeTitle( $parser, $title );
694 if ( $t === null ) {
695 return '';
696 }
697 return (string)$t->getNamespace();
698 }
699
700 public static function talkspace( $parser, $title = null ) {
701 $t = self::makeTitle( $parser, $title );
702 if ( $t === null || !$t->canHaveTalkPage() ) {
703 return '';
704 }
705 return str_replace( '_', ' ', $t->getTalkNsText() );
706 }
707
708 public static function talkspacee( $parser, $title = null ) {
709 $t = self::makeTitle( $parser, $title );
710 if ( $t === null || !$t->canHaveTalkPage() ) {
711 return '';
712 }
713 return wfUrlencode( $t->getTalkNsText() );
714 }
715
716 public static function subjectspace( $parser, $title = null ) {
717 $t = self::makeTitle( $parser, $title );
718 if ( $t === null ) {
719 return '';
720 }
721 return str_replace( '_', ' ', $t->getSubjectNsText() );
722 }
723
724 public static function subjectspacee( $parser, $title = null ) {
725 $t = self::makeTitle( $parser, $title );
726 if ( $t === null ) {
727 return '';
728 }
729 return wfUrlencode( $t->getSubjectNsText() );
730 }
731
739 public static function pagename( $parser, $title = null ) {
740 $t = self::makeTitle( $parser, $title );
741 if ( $t === null ) {
742 return '';
743 }
744 return wfEscapeWikiText( $t->getText() );
745 }
746
747 public static function pagenamee( $parser, $title = null ) {
748 $t = self::makeTitle( $parser, $title );
749 if ( $t === null ) {
750 return '';
751 }
752 return wfEscapeWikiText( $t->getPartialURL() );
753 }
754
755 public static function fullpagename( $parser, $title = null ) {
756 $t = self::makeTitle( $parser, $title );
757 if ( $t === null ) {
758 return '';
759 }
760 return wfEscapeWikiText( $t->getPrefixedText() );
761 }
762
763 public static function fullpagenamee( $parser, $title = null ) {
764 $t = self::makeTitle( $parser, $title );
765 if ( $t === null ) {
766 return '';
767 }
768 return wfEscapeWikiText( $t->getPrefixedURL() );
769 }
770
771 public static function subpagename( $parser, $title = null ) {
772 $t = self::makeTitle( $parser, $title );
773 if ( $t === null ) {
774 return '';
775 }
776 return wfEscapeWikiText( $t->getSubpageText() );
777 }
778
779 public static function subpagenamee( $parser, $title = null ) {
780 $t = self::makeTitle( $parser, $title );
781 if ( $t === null ) {
782 return '';
783 }
784 return wfEscapeWikiText( $t->getSubpageUrlForm() );
785 }
786
787 public static function rootpagename( $parser, $title = null ) {
788 $t = self::makeTitle( $parser, $title );
789 if ( $t === null ) {
790 return '';
791 }
792 return wfEscapeWikiText( $t->getRootText() );
793 }
794
795 public static function rootpagenamee( $parser, $title = null ) {
796 $t = self::makeTitle( $parser, $title );
797 if ( $t === null ) {
798 return '';
799 }
800 return wfEscapeWikiText( wfUrlencode( str_replace( ' ', '_', $t->getRootText() ) ) );
801 }
802
803 public static function basepagename( $parser, $title = null ) {
804 $t = self::makeTitle( $parser, $title );
805 if ( $t === null ) {
806 return '';
807 }
808 return wfEscapeWikiText( $t->getBaseText() );
809 }
810
811 public static function basepagenamee( $parser, $title = null ) {
812 $t = self::makeTitle( $parser, $title );
813 if ( $t === null ) {
814 return '';
815 }
816 return wfEscapeWikiText( wfUrlencode( str_replace( ' ', '_', $t->getBaseText() ) ) );
817 }
818
819 public static function talkpagename( $parser, $title = null ) {
820 $t = self::makeTitle( $parser, $title );
821 if ( $t === null || !$t->canHaveTalkPage() ) {
822 return '';
823 }
824 return wfEscapeWikiText( $t->getTalkPage()->getPrefixedText() );
825 }
826
827 public static function talkpagenamee( $parser, $title = null ) {
828 $t = self::makeTitle( $parser, $title );
829 if ( $t === null || !$t->canHaveTalkPage() ) {
830 return '';
831 }
832 return wfEscapeWikiText( $t->getTalkPage()->getPrefixedURL() );
833 }
834
835 public static function subjectpagename( $parser, $title = null ) {
836 $t = self::makeTitle( $parser, $title );
837 if ( $t === null ) {
838 return '';
839 }
840 return wfEscapeWikiText( $t->getSubjectPage()->getPrefixedText() );
841 }
842
843 public static function subjectpagenamee( $parser, $title = null ) {
844 $t = self::makeTitle( $parser, $title );
845 if ( $t === null ) {
846 return '';
847 }
848 return wfEscapeWikiText( $t->getSubjectPage()->getPrefixedURL() );
849 }
850
861 public static function pagesincategory( $parser, $name = '', $arg1 = '', $arg2 = '' ) {
862 static $magicWords = null;
863 if ( $magicWords === null ) {
864 $magicWords = $parser->getMagicWordFactory()->newArray( [
865 'pagesincategory_all',
866 'pagesincategory_pages',
867 'pagesincategory_subcats',
868 'pagesincategory_files'
869 ] );
870 }
871 static $cache = [];
872
873 // split the given option to its variable
874 if ( self::matchAgainstMagicword( $parser->getMagicWordFactory(), 'rawsuffix', $arg1 ) ) {
875 // {{pagesincategory:|raw[|type]}}
876 $raw = $arg1;
877 $type = $magicWords->matchStartToEnd( $arg2 );
878 } else {
879 // {{pagesincategory:[|type[|raw]]}}
880 $type = $magicWords->matchStartToEnd( $arg1 );
881 $raw = $arg2;
882 }
883 if ( !$type ) { // backward compatibility
884 $type = 'pagesincategory_all';
885 }
886
887 $title = Title::makeTitleSafe( NS_CATEGORY, $name );
888 if ( !$title ) { # invalid title
889 return self::formatRaw( 0, $raw, $parser->getTargetLanguage() );
890 }
891 $languageConverter = MediaWikiServices::getInstance()
892 ->getLanguageConverterFactory()
893 ->getLanguageConverter( $parser->getContentLanguage() );
894 $languageConverter->findVariantLink( $name, $title, true );
895
896 // Normalize name for cache
897 $name = $title->getDBkey();
898
899 if ( !isset( $cache[$name] ) ) {
900 $category = Category::newFromTitle( $title );
901
902 $allCount = $subcatCount = $fileCount = $pageCount = 0;
903 if ( $parser->incrementExpensiveFunctionCount() ) {
904 $allCount = $category->getMemberCount();
905 $subcatCount = $category->getSubcatCount();
906 $fileCount = $category->getFileCount();
907 $pageCount = $category->getPageCount( Category::COUNT_CONTENT_PAGES );
908 }
909 $cache[$name]['pagesincategory_all'] = $allCount;
910 $cache[$name]['pagesincategory_pages'] = $pageCount;
911 $cache[$name]['pagesincategory_subcats'] = $subcatCount;
912 $cache[$name]['pagesincategory_files'] = $fileCount;
913 }
914
915 $count = $cache[$name][$type];
916 return self::formatRaw( $count, $raw, $parser->getTargetLanguage() );
917 }
918
928 public static function pagesize( $parser, $page = '', $raw = null ) {
929 $title = Title::newFromText( $page );
930
931 if ( !is_object( $title ) || $title->isExternal() ) {
932 return self::formatRaw( 0, $raw, $parser->getTargetLanguage() );
933 }
934
935 // fetch revision from cache/database and return the value
936 $rev = self::getCachedRevisionObject( $parser, $title, ParserOutputFlags::VARY_REVISION_SHA1 );
937 $length = $rev ? $rev->getSize() : 0;
938 if ( $length === null ) {
939 // We've had bugs where rev_len was not being recorded for empty pages, see T135414
940 $length = 0;
941 }
942 return self::formatRaw( $length, $raw, $parser->getTargetLanguage() );
943 }
944
957 public static function protectionlevel( $parser, $type = '', $title = '' ) {
958 $titleObject = Title::newFromText( $title ) ?? $parser->getTitle();
959 $restrictionStore = MediaWikiServices::getInstance()->getRestrictionStore();
960 if ( $restrictionStore->areRestrictionsLoaded( $titleObject ) || $parser->incrementExpensiveFunctionCount() ) {
961 $restrictions = $restrictionStore->getRestrictions( $titleObject, strtolower( $type ) );
962 # RestrictionStore::getRestrictions returns an array, its possible it may have
963 # multiple values in the future
964 return implode( ',', $restrictions );
965 }
966 return '';
967 }
968
981 public static function protectionexpiry( $parser, $type = '', $title = '' ) {
982 $titleObject = Title::newFromText( $title ) ?? $parser->getTitle();
983 $restrictionStore = MediaWikiServices::getInstance()->getRestrictionStore();
984 if ( $restrictionStore->areRestrictionsLoaded( $titleObject ) || $parser->incrementExpensiveFunctionCount() ) {
985 // getRestrictionExpiry() returns null on invalid type; trying to
986 // match protectionlevel() function that returns empty string instead
987 return $restrictionStore->getRestrictionExpiry( $titleObject, strtolower( $type ) ) ?? '';
988 }
989 return '';
990 }
991
999 public static function language( $parser, $code = '', $inLanguage = '' ) {
1000 $code = strtolower( $code );
1001 $inLanguage = strtolower( $inLanguage );
1002 $lang = MediaWikiServices::getInstance()
1003 ->getLanguageNameUtils()
1004 ->getLanguageName( $code, $inLanguage );
1005 return $lang !== '' ? $lang : LanguageCode::bcp47( $code );
1006 }
1007
1017 public static function pad(
1018 $parser, $string, $length, $padding = '0', $direction = STR_PAD_RIGHT
1019 ) {
1020 $padding = $parser->killMarkers( $padding );
1021 $lengthOfPadding = mb_strlen( $padding );
1022 if ( $lengthOfPadding == 0 ) {
1023 return $string;
1024 }
1025
1026 # The remaining length to add counts down to 0 as padding is added
1027 $length = min( (int)$length, 500 ) - mb_strlen( $string );
1028 if ( $length <= 0 ) {
1029 // Nothing to add
1030 return $string;
1031 }
1032
1033 # $finalPadding is just $padding repeated enough times so that
1034 # mb_strlen( $string ) + mb_strlen( $finalPadding ) == $length
1035 $finalPadding = '';
1036 while ( $length > 0 ) {
1037 # If $length < $lengthofPadding, truncate $padding so we get the
1038 # exact length desired.
1039 $finalPadding .= mb_substr( $padding, 0, $length );
1040 $length -= $lengthOfPadding;
1041 }
1042
1043 if ( $direction == STR_PAD_LEFT ) {
1044 return $finalPadding . $string;
1045 } else {
1046 return $string . $finalPadding;
1047 }
1048 }
1049
1050 public static function padleft( $parser, $string = '', $length = 0, $padding = '0' ) {
1051 return self::pad( $parser, $string, $length, $padding, STR_PAD_LEFT );
1052 }
1053
1054 public static function padright( $parser, $string = '', $length = 0, $padding = '0' ) {
1055 return self::pad( $parser, $string, $length, $padding );
1056 }
1057
1063 public static function anchorencode( $parser, $text ) {
1064 $text = $parser->killMarkers( $text );
1065 $section = (string)substr( $parser->guessSectionNameFromWikiText( $text ), 1 );
1066 return Sanitizer::safeEncodeAttribute( $section );
1067 }
1068
1069 public static function special( $parser, $text ) {
1070 [ $page, $subpage ] = MediaWikiServices::getInstance()->getSpecialPageFactory()->
1071 resolveAlias( $text );
1072 if ( $page ) {
1073 $title = SpecialPage::getTitleFor( $page, $subpage );
1074 return $title->getPrefixedText();
1075 } else {
1076 // unknown special page, just use the given text as its title, if at all possible
1077 $title = Title::makeTitleSafe( NS_SPECIAL, $text );
1078 return $title ? $title->getPrefixedText() : self::special( $parser, 'Badtitle' );
1079 }
1080 }
1081
1082 public static function speciale( $parser, $text ) {
1083 return wfUrlencode( str_replace( ' ', '_', self::special( $parser, $text ) ) );
1084 }
1085
1094 public static function defaultsort( $parser, $text, $uarg = '' ) {
1095 static $magicWords = null;
1096 if ( $magicWords === null ) {
1097 $magicWords = $parser->getMagicWordFactory()->newArray(
1098 [ 'defaultsort_noerror', 'defaultsort_noreplace' ] );
1099 }
1100 $arg = $magicWords->matchStartToEnd( $uarg );
1101
1102 $text = trim( $text );
1103 if ( strlen( $text ) == 0 ) {
1104 return '';
1105 }
1106 $old = $parser->getOutput()->getPageProperty( 'defaultsort' );
1107 if ( $old === null || $arg !== 'defaultsort_noreplace' ) {
1108 $parser->getOutput()->setPageProperty( 'defaultsort', $text );
1109 }
1110
1111 if ( $old === null || $old == $text || $arg ) {
1112 return '';
1113 } else {
1114 $converter = $parser->getTargetLanguageConverter();
1115 return '<span class="error">' .
1116 $parser->msg( 'duplicate-defaultsort',
1117 // Message should be parsed, but these params should only be escaped.
1118 $converter->markNoConversion( wfEscapeWikiText( $old ) ),
1119 $converter->markNoConversion( wfEscapeWikiText( $text ) )
1120 )->text() .
1121 '</span>';
1122 }
1123 }
1124
1136 public static function filepath( $parser, $name = '', $argA = '', $argB = '' ) {
1137 $file = MediaWikiServices::getInstance()->getRepoGroup()->findFile( $name );
1138
1139 if ( $argA == 'nowiki' ) {
1140 // {{filepath: | option [| size] }}
1141 $isNowiki = true;
1142 $parsedWidthParam = Parser::parseWidthParam( $argB );
1143 } else {
1144 // {{filepath: [| size [|option]] }}
1145 $parsedWidthParam = Parser::parseWidthParam( $argA );
1146 $isNowiki = ( $argB == 'nowiki' );
1147 }
1148
1149 if ( $file ) {
1150 $url = $file->getFullUrl();
1151
1152 // If a size is requested...
1153 if ( count( $parsedWidthParam ) ) {
1154 $mto = $file->transform( $parsedWidthParam );
1155 // ... and we can
1156 if ( $mto && !$mto->isError() ) {
1157 // ... change the URL to point to a thumbnail.
1158 $url = wfExpandUrl( $mto->getUrl(), PROTO_RELATIVE );
1159 }
1160 }
1161 if ( $isNowiki ) {
1162 return [ $url, 'nowiki' => true ];
1163 }
1164 return $url;
1165 } else {
1166 return '';
1167 }
1168 }
1169
1177 public static function tagObj( $parser, $frame, $args ) {
1178 if ( !count( $args ) ) {
1179 return '';
1180 }
1181 $tagName = strtolower( trim( $frame->expand( array_shift( $args ) ) ) );
1182 $processNowiki = $parser->tagNeedsNowikiStrippedInTagPF( $tagName ) ? PPFrame::PROCESS_NOWIKI : 0;
1183
1184 if ( count( $args ) ) {
1185 $inner = $frame->expand( array_shift( $args ), $processNowiki );
1186 } else {
1187 $inner = null;
1188 }
1189
1190 $attributes = [];
1191 foreach ( $args as $arg ) {
1192 $bits = $arg->splitArg();
1193 if ( strval( $bits['index'] ) === '' ) {
1194 $name = trim( $frame->expand( $bits['name'], PPFrame::STRIP_COMMENTS ) );
1195 $value = trim( $frame->expand( $bits['value'] ) );
1196 if ( preg_match( '/^(?:["\'](.+)["\']|""|\'\')$/s', $value, $m ) ) {
1197 $value = $m[1] ?? '';
1198 }
1199 $attributes[$name] = $value;
1200 }
1201 }
1202
1203 $stripList = $parser->getStripList();
1204 if ( !in_array( $tagName, $stripList ) ) {
1205 // we can't handle this tag (at least not now), so just re-emit it as an ordinary tag
1206 $attrText = '';
1207 foreach ( $attributes as $name => $value ) {
1208 $attrText .= ' ' . htmlspecialchars( $name ) .
1209 '="' . htmlspecialchars( $value, ENT_COMPAT ) . '"';
1210 }
1211 if ( $inner === null ) {
1212 return "<$tagName$attrText/>";
1213 }
1214 return "<$tagName$attrText>$inner</$tagName>";
1215 }
1216
1217 $params = [
1218 'name' => $tagName,
1219 'inner' => $inner,
1220 'attributes' => $attributes,
1221 'close' => "</$tagName>",
1222 ];
1223 return $parser->extensionSubstitution( $params, $frame );
1224 }
1225
1239 private static function getCachedRevisionObject( $parser, $title, $vary ) {
1240 if ( !$title ) {
1241 return null;
1242 }
1243
1244 $revisionRecord = null;
1245
1246 $isSelfReferential = $title->equals( $parser->getTitle() );
1247 if ( $isSelfReferential ) {
1248 // Revision is for the same title that is currently being parsed. Only use the last
1249 // saved revision, regardless of Parser::getRevisionId() or fake revision injection
1250 // callbacks against the current title.
1251
1252 // FIXME (T318278): the above is the intention, but doesn't
1253 // describe the actual current behavior of this code, since
1254 // ->isCurrent() for the last saved revision will return
1255 // false so we're going to fall through and end up calling
1256 // ->getCurrentRevisionRecordOfTitle().
1257 $parserRevisionRecord = $parser->getRevisionRecordObject();
1258 if ( $parserRevisionRecord && $parserRevisionRecord->isCurrent() ) {
1259 $revisionRecord = $parserRevisionRecord;
1260 }
1261 }
1262
1263 $parserOutput = $parser->getOutput();
1264 if ( !$revisionRecord ) {
1265 if (
1266 !$parser->isCurrentRevisionOfTitleCached( $title ) &&
1268 ) {
1269 return null; // not allowed
1270 }
1271 // Get the current revision, ignoring Parser::getRevisionId() being null/old
1272 $revisionRecord = $parser->fetchCurrentRevisionRecordOfTitle( $title );
1273 if ( !$revisionRecord ) {
1274 // Convert `false` error return to `null`
1275 $revisionRecord = null;
1276 }
1277 // Register dependency in templatelinks
1278 $parserOutput->addTemplate(
1279 $title,
1280 $revisionRecord ? $revisionRecord->getPageId() : 0,
1281 $revisionRecord ? $revisionRecord->getId() : 0
1282 );
1283 }
1284
1285 if ( $isSelfReferential ) {
1286 wfDebug( __METHOD__ . ": used current revision, setting $vary" );
1287 // Upon page save, the result of the parser function using this might change
1288 $parserOutput->setOutputFlag( $vary );
1289 if ( $vary === ParserOutputFlags::VARY_REVISION_SHA1 && $revisionRecord ) {
1290 try {
1291 $sha1 = $revisionRecord->getSha1();
1292 } catch ( RevisionAccessException $e ) {
1293 $sha1 = null;
1294 }
1295 $parserOutput->setRevisionUsedSha1Base36( $sha1 );
1296 }
1297 }
1298
1299 return $revisionRecord;
1300 }
1301
1309 public static function pageid( $parser, $title = null ) {
1310 $t = self::makeTitle( $parser, $title );
1311 if ( !$t ) {
1312 return '';
1313 } elseif ( !$t->canExist() || $t->isExternal() ) {
1314 return 0; // e.g. special page or interwiki link
1315 }
1316
1317 $parserOutput = $parser->getOutput();
1318
1319 if ( $t->equals( $parser->getTitle() ) ) {
1320 // Revision is for the same title that is currently being parsed.
1321 // Use the title from Parser in case a new page ID was injected into it.
1322 $parserOutput->setOutputFlag( ParserOutputFlags::VARY_PAGE_ID );
1323 $id = $parser->getTitle()->getArticleID();
1324 if ( $id ) {
1325 $parserOutput->setSpeculativePageIdUsed( $id );
1326 }
1327
1328 return $id;
1329 }
1330
1331 // Check the link cache for the title
1332 $linkCache = MediaWikiServices::getInstance()->getLinkCache();
1333 $pdbk = $t->getPrefixedDBkey();
1334 $id = $linkCache->getGoodLinkID( $pdbk );
1335 if ( $id != 0 || $linkCache->isBadLink( $pdbk ) ) {
1336 $parserOutput->addLink( $t, $id );
1337
1338 return $id;
1339 }
1340
1341 // We need to load it from the DB, so mark expensive
1342 if ( $parser->incrementExpensiveFunctionCount() ) {
1343 $id = $t->getArticleID();
1344 $parserOutput->addLink( $t, $id );
1345
1346 return $id;
1347 }
1348
1349 return null;
1350 }
1351
1359 public static function revisionid( $parser, $title = null ) {
1360 $t = self::makeTitle( $parser, $title );
1361 if ( $t === null || $t->isExternal() ) {
1362 return '';
1363 }
1364
1365 $services = MediaWikiServices::getInstance();
1366 if (
1367 $t->equals( $parser->getTitle() ) &&
1368 $services->getMainConfig()->get( MainConfigNames::MiserMode ) &&
1369 !$parser->getOptions()->getInterfaceMessage() &&
1370 // @TODO: disallow this word on all namespaces (T235957)
1371 $services->getNamespaceInfo()->isSubject( $t->getNamespace() )
1372 ) {
1373 // Use a stub result instead of the actual revision ID in order to avoid
1374 // double parses on page save but still allow preview detection (T137900)
1375 if ( $parser->getRevisionId() || $parser->getOptions()->getSpeculativeRevId() ) {
1376 return '-';
1377 } else {
1378 $parser->getOutput()->setOutputFlag( ParserOutputFlags::VARY_REVISION_EXISTS );
1379 return '';
1380 }
1381 }
1382 // Fetch revision from cache/database and return the value.
1383 // Inform the edit saving system that getting the canonical output
1384 // after revision insertion requires a parse that used that exact
1385 // revision ID.
1386 if ( $t->equals( $parser->getTitle() ) && $title === null ) {
1387 // special handling for no-arg case: use speculative rev id
1388 // for current page.
1389 $parser->getOutput()->setOutputFlag( ParserOutputFlags::VARY_REVISION_ID );
1390 $id = $parser->getRevisionId();
1391 if ( $id === 0 ) {
1392 $rev = $parser->getRevisionRecordObject();
1393 if ( $rev ) {
1394 $id = $rev->getId();
1395 }
1396 }
1397 if ( !$id ) {
1398 $id = $parser->getOptions()->getSpeculativeRevId();
1399 if ( $id ) {
1400 $parser->getOutput()->setSpeculativeRevIdUsed( $id );
1401 }
1402 }
1403 return (string)$id;
1404 }
1405 $rev = self::getCachedRevisionObject( $parser, $t, ParserOutputFlags::VARY_REVISION_ID );
1406 return $rev ? $rev->getId() : '';
1407 }
1408
1409 private static function getRevisionTimestampSubstring(
1410 Parser $parser,
1411 Title $title,
1412 int $start,
1413 int $len,
1414 int $mtts
1415 ): string {
1416 // If fetching the revision timestamp of the current page, substitute the
1417 // speculative timestamp to be used when this revision is saved. This
1418 // avoids having to invalidate the cache immediately by assuming the "last
1419 // saved revision" will in fact be this one.
1420 // Don't do this for interface messages (eg, edit notices) however; in that
1421 // case fall through and use the actual timestamp of the last saved revision.
1422 if ( $title->equals( $parser->getTitle() ) && !$parser->getOptions()->getInterfaceMessage() ) {
1423 // Get the timezone-adjusted timestamp to be used for this revision
1424 $resNow = substr( $parser->getRevisionTimestamp(), $start, $len );
1425 // Possibly set vary-revision if there is not yet an associated revision
1426 if ( !$parser->getRevisionRecordObject() ) {
1427 // Get the timezone-adjusted timestamp $mtts seconds in the future.
1428 // This future is relative to the current time and not that of the
1429 // parser options. The rendered timestamp can be compared to that
1430 // of the timestamp specified by the parser options.
1431 $resThen = substr(
1432 $parser->getContentLanguage()->userAdjust( wfTimestamp( TS_MW, time() + $mtts ), '' ),
1433 $start,
1434 $len
1435 );
1436
1437 if ( $resNow !== $resThen ) {
1438 // Inform the edit saving system that getting the canonical output after
1439 // revision insertion requires a parse that used an actual revision timestamp
1440 $parser->getOutput()->setOutputFlag( ParserOutputFlags::VARY_REVISION_TIMESTAMP );
1441 }
1442 }
1443
1444 return $resNow;
1445 } else {
1446 $rev = self::getCachedRevisionObject( $parser, $title, ParserOutputFlags::VARY_REVISION_TIMESTAMP );
1447 if ( !$rev ) {
1448 return '';
1449 }
1450 $resNow = substr(
1451 $parser->getContentLanguage()->userAdjust( $rev->getTimestamp(), '' ), $start, $len
1452 );
1453 return $resNow;
1454 }
1455 }
1456
1464 public static function revisionday( $parser, $title = null ) {
1465 $t = self::makeTitle( $parser, $title );
1466 if ( $t === null || $t->isExternal() ) {
1467 return '';
1468 }
1469 return strval( (int)self::getRevisionTimestampSubstring(
1470 $parser, $t, 6, 2, self::MAX_TTS
1471 ) );
1472 }
1473
1481 public static function revisionday2( $parser, $title = null ) {
1482 $t = self::makeTitle( $parser, $title );
1483 if ( $t === null || $t->isExternal() ) {
1484 return '';
1485 }
1486 return self::getRevisionTimestampSubstring(
1487 $parser, $t, 6, 2, self::MAX_TTS
1488 );
1489 }
1490
1498 public static function revisionmonth( $parser, $title = null ) {
1499 $t = self::makeTitle( $parser, $title );
1500 if ( $t === null || $t->isExternal() ) {
1501 return '';
1502 }
1503 return self::getRevisionTimestampSubstring(
1504 $parser, $t, 4, 2, self::MAX_TTS
1505 );
1506 }
1507
1515 public static function revisionmonth1( $parser, $title = null ) {
1516 $t = self::makeTitle( $parser, $title );
1517 if ( $t === null || $t->isExternal() ) {
1518 return '';
1519 }
1520 return strval( (int)self::getRevisionTimestampSubstring(
1521 $parser, $t, 4, 2, self::MAX_TTS
1522 ) );
1523 }
1524
1532 public static function revisionyear( $parser, $title = null ) {
1533 $t = self::makeTitle( $parser, $title );
1534 if ( $t === null || $t->isExternal() ) {
1535 return '';
1536 }
1537 return self::getRevisionTimestampSubstring(
1538 $parser, $t, 0, 4, self::MAX_TTS
1539 );
1540 }
1541
1549 public static function revisiontimestamp( $parser, $title = null ) {
1550 $t = self::makeTitle( $parser, $title );
1551 if ( $t === null || $t->isExternal() ) {
1552 return '';
1553 }
1554 return self::getRevisionTimestampSubstring(
1555 $parser, $t, 0, 14, self::MAX_TTS
1556 );
1557 }
1558
1566 public static function revisionuser( $parser, $title = null ) {
1567 $t = self::makeTitle( $parser, $title );
1568 if ( $t === null || $t->isExternal() ) {
1569 return '';
1570 }
1571 // VARY_USER informs the edit saving system that getting the canonical
1572 // output after revision insertion requires a parse that used the
1573 // actual user ID.
1574 if ( $t->equals( $parser->getTitle() ) ) {
1575 // Fall back to Parser's "revision user" for the current title
1576 $parser->getOutput()->setOutputFlag( ParserOutputFlags::VARY_USER );
1577 // Note that getRevisionUser() can return null; we need to
1578 // be sure to cast this to (an empty) string, since returning
1579 // null means "magic variable not handled".
1580 return (string)$parser->getRevisionUser();
1581 }
1582 // Fetch revision from cache/database and return the value.
1583 $rev = self::getCachedRevisionObject( $parser, $t, ParserOutputFlags::VARY_USER );
1584 $user = ( $rev !== null ) ? $rev->getUser() : null;
1585 return $user ? $user->getName() : '';
1586 }
1587
1600 public static function cascadingsources( $parser, $title = '' ) {
1601 $titleObject = Title::newFromText( $title ) ?? $parser->getTitle();
1602 $restrictionStore = MediaWikiServices::getInstance()->getRestrictionStore();
1603 if ( $restrictionStore->areCascadeProtectionSourcesLoaded( $titleObject )
1605 ) {
1606 $names = [];
1607 $sources = $restrictionStore->getCascadeProtectionSources( $titleObject );
1608 $titleFormatter = MediaWikiServices::getInstance()->getTitleFormatter();
1609 foreach ( $sources[0] as $sourcePageIdentity ) {
1610 $names[] = $titleFormatter->getPrefixedText( $sourcePageIdentity );
1611 }
1612 return implode( '|', $names );
1613 }
1614 return '';
1615 }
1616}
const NS_USER
Definition Defines.php:67
const NS_FILE
Definition Defines.php:71
const NS_SPECIAL
Definition Defines.php:54
const NS_MEDIA
Definition Defines.php:53
const PROTO_RELATIVE
Definition Defines.php:205
const NS_CATEGORY
Definition Defines.php:79
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,...
wfExpandUrl( $url, $defaultProto=PROTO_CURRENT)
Expand a potentially local URL to a fully-qualified URL using $wgServer (or one of its alternatives).
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.
$magicWords
@phpcs-require-sorted-array
array $params
The job parameters.
if(!defined('MW_SETUP_CALLBACK'))
Definition WebStart.php:81
Various core parser functions, registered in every Parser.
static language( $parser, $code='', $inLanguage='')
Gives language names.
static numberingroup( $parser, $name='', $raw=null)
static rootpagename( $parser, $title=null)
static localurl( $parser, $s='', $arg=null)
static formatnum( $parser, $num='', $arg=null)
static ns( $parser, $part1='')
static protectionexpiry( $parser, $type='', $title='')
Returns the requested protection expiry for the current page.
static pagenamee( $parser, $title=null)
static padleft( $parser, $string='', $length=0, $padding='0')
static fullurle( $parser, $s='', $arg=null)
static revisionmonth( $parser, $title=null)
Get the month with leading zeros from the last revision of a specified page.
static fullpagename( $parser, $title=null)
static revisionid( $parser, $title=null)
Get the id from the last revision of a specified page.
static pagesinnamespace( $parser, $namespace=0, $raw=null)
static numberofusers( $parser, $raw=null)
static special( $parser, $text)
static formal(Parser $parser, string ... $forms)
static nse( $parser, $part1='')
static pagesize( $parser, $page='', $raw=null)
Return the size of the given page, or 0 if it's nonexistent.
static canonicalurl( $parser, $s='', $arg=null)
static basepagenamee( $parser, $title=null)
static subjectspacee( $parser, $title=null)
static numberofedits( $parser, $raw=null)
static numberoffiles( $parser, $raw=null)
static ucfirst( $parser, $s='')
static basepagename( $parser, $title=null)
static gender( $parser, $username,... $forms)
static numberofpages( $parser, $raw=null)
static lcfirst( $parser, $s='')
static urlFunction( $func, $s='', $arg=null)
static plural( $parser, $text='',... $forms)
static formatRaw( $num, $raw, $language, MagicWordFactory $magicWordFactory=null)
Formats a number according to a language.
static talkspacee( $parser, $title=null)
static bidi( $parser, $text='')
static revisionuser( $parser, $title=null)
Get the user from the last revision of a specified page.
static anchorencode( $parser, $text)
static subjectpagenamee( $parser, $title=null)
static namespacee( $parser, $title=null)
static subjectpagename( $parser, $title=null)
static filepath( $parser, $name='', $argA='', $argB='')
Usage {{filepath|300}}, {{filepath|nowiki}}, {{filepath|nowiki|300}} or {{filepath|300|nowiki}} or {{...
static padright( $parser, $string='', $length=0, $padding='0')
static pad( $parser, $string, $length, $padding='0', $direction=STR_PAD_RIGHT)
Unicode-safe str_pad with the restriction that $length is forced to be <= 500.
static canonicalurle( $parser, $s='', $arg=null)
static cascadingsources( $parser, $title='')
Returns the sources of any cascading protection acting on a specified page.
static numberofactiveusers( $parser, $raw=null)
static displaytitle( $parser, $text='', $uarg='')
Override the title of the page when viewed, provided we've been given a title which will normalise to...
static pageid( $parser, $title=null)
Get the pageid of a specified page.
static formatDate( $parser, $date, $defaultPref=null)
static urlencode( $parser, $s='', $arg=null)
urlencodes a string according to one of three patterns: (T24474)
static grammar( $parser, $case='', $word='')
static subpagenamee( $parser, $title=null)
static tagObj( $parser, $frame, $args)
Parser function to extension tag adaptor.
static revisionmonth1( $parser, $title=null)
Get the month from the last revision of a specified page.
static rootpagenamee( $parser, $title=null)
static protectionlevel( $parser, $type='', $title='')
Returns the requested protection level for the current page.
static namespacenumber( $parser, $title=null)
static revisionday( $parser, $title=null)
Get the day from the last revision of a specified page.
static subjectspace( $parser, $title=null)
static lc( $parser, $s='')
static pagesincategory( $parser, $name='', $arg1='', $arg2='')
Return the number of pages, files or subcats in the given category, or 0 if it's nonexistent.
static talkpagename( $parser, $title=null)
static fullurl( $parser, $s='', $arg=null)
static pagename( $parser, $title=null)
Functions to get and normalize pagenames, corresponding to the magic words of the same names.
static intFunction( $parser, $part1='',... $params)
static numberofadmins( $parser, $raw=null)
static numberofarticles( $parser, $raw=null)
static revisionyear( $parser, $title=null)
Get the year from the last revision of a specified page.
static revisionday2( $parser, $title=null)
Get the day with leading zeros from the last revision of a specified page.
static uc( $parser, $s='')
static talkpagenamee( $parser, $title=null)
static revisiontimestamp( $parser, $title=null)
Get the timestamp from the last revision of a specified page.
static subpagename( $parser, $title=null)
static localurle( $parser, $s='', $arg=null)
static defaultsort( $parser, $text, $uarg='')
static fullpagenamee( $parser, $title=null)
static talkspace( $parser, $title=null)
static speciale( $parser, $text)
Category objects are immutable, strictly speaking.
Definition Category.php:42
A class for passing options to services.
A class containing constants representing the names of configuration variables.
Service locator for MediaWiki core services.
Store information about magic words, and create/cache MagicWord objects.
get( $id)
Get a MagicWord object for a given internal ID.
PHP Parser - Processes wiki markup (which uses a more user-friendly syntax, such as "[[link]]" for ma...
Definition Parser.php:156
getTargetLanguageConverter()
Shorthand for getting a Language Converter for Target language.
Definition Parser.php:1675
markerSkipCallback( $s, callable $callback)
Call a callback function on all regions of the given text that are not inside strip markers,...
Definition Parser.php:6347
tagNeedsNowikiStrippedInTagPF(string $lowerTagName)
Definition Parser.php:3980
getMagicWordFactory()
Get the MagicWordFactory that this Parser is using.
Definition Parser.php:1256
setFunctionHook( $id, callable $callback, $flags=0)
Create a function, e.g.
Definition Parser.php:5056
msg(string $msg,... $args)
Helper function to correctly set the target language and title of a message based on the parser conte...
Definition Parser.php:4198
guessSectionNameFromWikiText( $text)
Try to guess the section anchor name based on a wikitext fragment presumably extracted from a heading...
Definition Parser.php:6239
isCurrentRevisionOfTitleCached(LinkTarget $link)
Definition Parser.php:3585
getRevisionId()
Get the ID of the revision we are parsing.
Definition Parser.php:6056
getContentLanguage()
Get the content language that this Parser is using.
Definition Parser.php:1266
killMarkers( $text)
Remove any strip markers found in the given text.
Definition Parser.php:6378
getRevisionUser()
Get the name of the user that edited the last revision.
Definition Parser.php:6149
getTargetLanguage()
Get the target language for the content being parsed.
Definition Parser.php:1202
getStripList()
Get a list of strippable XML-like elements.
Definition Parser.php:1362
extensionSubstitution(array $params, PPFrame $frame, bool $processNowiki=false)
Return the text to be used for a given extension tag.
Definition Parser.php:4004
getRevisionRecordObject()
Get the revision record object for $this->mRevisionId.
Definition Parser.php:6066
getRevisionTimestamp()
Get the timestamp associated with the current revision, adjusted for the default server-local timesta...
Definition Parser.php:6121
doQuotes( $text)
Helper function for handleAllQuotes()
Definition Parser.php:2011
fetchCurrentRevisionRecordOfTitle(LinkTarget $link)
Fetch the current revision of a given title as a RevisionRecord.
Definition Parser.php:3555
HTML sanitizer for MediaWiki.
Definition Sanitizer.php:46
Exception representing a failure to look up a revision.
Page revision base class.
Static accessor class for site_stats and related things.
Definition SiteStats.php:36
Parent class for all special pages.
Represents a title within MediaWiki.
Definition Title.php:79
inNamespace(int $ns)
Returns true if the title is inside the specified namespace.
Definition Title.php:1303
equals(object $other)
Compares with another Title.
Definition Title.php:3103
getDBkey()
Get the main part with underscores.
Definition Title.php:1036
getText()
Get the text form (spaces not underscores) of the main part.
Definition Title.php:1018
internal since 1.36
Definition User.php:94
hasFragment()
Whether the link target has a fragment.