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