diff --git a/UsingDataHooks.php b/UsingDataHooks.php
deleted file mode 100644
index 1606e1f..0000000
--- a/UsingDataHooks.php
+++ /dev/null
@@ -1,289 +0,0 @@
-setFunctionHook( 'using', [ self::$instance, 'usingParserFunction' ], SFH_OBJECT_ARGS );
- $parser->setFunctionHook( 'usingarg', [ self::$instance, 'usingArgParserFunction' ], SFH_OBJECT_ARGS );
- $parser->setFunctionHook( 'data', [ self::$instance, 'dataParserFunction' ], SFH_OBJECT_ARGS );
- $parser->setFunctionHook( 'ancestorname', [ __CLASS__, 'ancestorNameFunction' ], SFH_OBJECT_ARGS | SFH_NO_HASH );
- $parser->setHook( 'using', [ self::$instance, 'usingTag' ] );
-
- return true;
- }
-
- /**
- * Tells MediaWiki that one or more magic word IDs should be treated as variables.
- *
- * @return void
- */
- public static function onGetMagicVariableIDs( &$magicWords ) {
- $magicWords[] = 'parentname';
- $magicWords[] = 'selfname';
- }
-
- /* Returns a UsingData frame for a given page
- */
- private function getDataFrame( $sourcePage, $title, &$parser, $frame ) {
- global $wgHooks;
- if ( !isset( $this->dataFrames[$sourcePage] ) ) {
- $this->dataFrames[$sourcePage] = new UsingDataPPFrameDOM( $frame, $sourcePage );
- if ( $sourcePage != ''
- && ( $sourcePage != $parser->getTitle()->getPrefixedText() )
- || $parser->getOptions()->getIsSectionPreview() ) {
- [ $text, $fTitle ] = $parser->fetchTemplateAndTitle( $title );
- if ( is_object( $fTitle ) && $fTitle->getPrefixedText() != $sourcePage ) {
- $this->dataFrames[$fTitle->getPrefixedText()] = $this->dataFrames[$sourcePage];
- }
- if ( is_string( $text ) && $text != '' ) {
- $this->searchingForData = true;
- $clearStateHooks = $wgHooks['ParserClearState'];
- // Other extensions tend to assume the hook is only called by wgParser and reset internal state
- $wgHooks['ParserClearState'] = [];
- $subParser = clone $parser;
- $subParser->preprocess( $text, $fTitle, clone $parser->getOptions() );
- // We might've blocked access to templates while preprocessing; should not be cached
- $subParser->clearState();
- $subParser->getOutput()->setText( $parser->getOutput()->getText() );
- $wgHooks['ParserClearState'] = empty( $wgHooks['ParserClearState'] )
- ? $clearStateHooks
- : array_merge( $clearStateHooks, $wgHooks['ParserClearState'] );
- $parser->mPPNodeCount += $subParser->mPPNodeCount;
- $this->searchingForData = false;
- }
- }
- }
- return $this->dataFrames[$sourcePage];
- }
-
- /* Returns the page title of the $depth ancestor of $frame; empty string if invalid */
- private static function ancestorNameHandler( $frame, $depth ) {
- while ( $depth-- && $frame != null ) {
- $frame = $frame->parent ?? null;
- }
- return is_object( $frame ) && isset( $frame->title ) && is_object( $frame->title )
- ? wfEscapeWikiText( $frame->title->getPrefixedText() ) : '';
- }
-
- /* Handles {{ANCESTORNAME:depth}} */
- public static function ancestorNameFunction( &$parser, $frame, $args ) {
- $arg = $frame->expand( $args[0] );
- return [ self::ancestorNameHandler( $frame, max( 0, is_numeric( $arg ) ? intval( $arg ) : 1 ) ), 'noparse' => true ];
- }
-
- /* Handles {{PARENTNAME}}, {{SELFNAME}}, {{ANCESTORNAME}} */
- public static function ancestorNameVar( &$parser, &$varCache, &$index, &$ret, &$frame ) {
- if ( $index == 'parentname' ) {
- $ret = self::ancestorNameHandler( $frame, 1 );
- }
- if ( $index == 'selfname' ) {
- $ret = self::ancestorNameHandler( $frame, 0 );
- }
- return true;
- }
-
- /* Parses common elements of #using syntax.
- */
- private function usingParse( &$parser, $frame, $args ) {
- if ( $this->searchingForData ) {
- return '';
- }
-
- $titleArg = trim( $frame->expand( $args[0] ) );
- if ( strpos( $titleArg, '%' ) !== false ) {
- $titleArg = str_replace( [ '<', '>' ], [ '<', '>' ], urldecode( $titleArg ) );
- }
- $title = \Title::newFromText( $titleArg, NS_MAIN );
- $sourcePage = is_object( $title ) ? $title->getPrefixedText() : '';
- $sourceHash = is_object( $title ) ? $title->getFragment() : '';
- $namedArgs = [];
-
- $one = null;
- $two = null;
- foreach ( $args as $key => $val ) {
- if ( $key === 0 ) {
- continue;
- }
- $bits = $val->splitArg();
- // It looks like indexes are now integers, it is a change from legacy implementation
- if ( $bits['index'] === 1 ) {
- $one = $frame->expand( $bits['value'] );
- } elseif ( $bits['index'] === 2 ) {
- $two = $bits['value'];
- } elseif ( $bits['index'] === '' ) {
- $namedArgs[trim( $frame->expand( $bits['name'] ) )] = $bits['value'];
- }
- }
- return [ $this->getDataFrame( $sourcePage, $title, $parser, $frame ), $sourceHash, $namedArgs, $one, $two ];
- }
-
- /* {{#using:Page#Hash|Template|Default|...}} parses Template using #data from Page's Hash fragment; or Default
- * if no data from Page can be found. Named arguments override those in the #data tag.
- */
- public function usingParserFunction( &$parser, $frame, $args ) {
- $parse = $this->usingParse( $parser, $frame, $args );
- if ( !is_array( $parse ) ) {
- return '';
- }
-
- [ $dframe, $fragment, $namedArgs, $templateTitle, $defaultValue ] = $parse;
- if ( !$dframe->hasFragment( $fragment ) && $defaultValue !== null ) {
- return $frame->expand( $defaultValue );
- }
- [ $dom, $title ] = $this->fetchTemplate( $parser, $templateTitle );
- return $dframe->expandUsing( $frame, $title, $dom, $namedArgs, $fragment );
- }
-
- /* {{#usingarg:Page#Hash|Arg|Default}} returns the value of Arg data field on Page's Hash fragment, Default if undefined.
- */
- public function usingArgParserFunction( &$parser, $frame, $args ) {
- $parse = $this->usingParse( $parser, $frame, $args );
- if ( !is_array( $parse ) ) {
- return '';
- }
-
- [ $dframe, $fragment, $namedArgs, $argName, $defaultValue ] = $parse;
- $ret = $dframe->getArgumentForParser(
- $parser,
- UsingDataPPFrameDOM::normalizeFragment( $fragment ),
- $argName,
- $defaultValue === null ? '' : false
- );
- return $ret !== false ? $ret : $frame->expand( $defaultValue );
- }
-
- /* ...
- * expands ... using the data from Page's Hash fragment; Default if undefined.
- * This tag relies on $parser->replaceVariables($text, $frame), which may prove fragile across MW versions.
- * Should it break, $parser->recursiveTagParse($text, $frame), in combination with either modifying the markerType, or using
- * insertStripItem directly, is a viable short-term alternative -- but one that call certain hooks prematurely,
- * potentially causing other extensions to misbehave slightly.
- */
- public function usingTag( $text, array $args, Parser $parser, PPFrame $frame ) {
- if ( $this->searchingForData ) {
- return ['', 'markerType' => 'none'];
- }
-
- $source = isset( $args['page'] ) ? $parser->replaceVariables( $args['page'], $frame ) : '';
- unset( $args['page'] );
- if ( strpos( $source, '%' ) !== false ) {
- $source = str_replace( [ '<', '>' ], [ '<', '>' ], urldecode( $source ) );
- }
- $title = \Title::newFromText( $source, NS_MAIN );
- if ( is_object( $title ) ) {
- $dframe = $this->getDataFrame( $title->getPrefixedText(), $title, $parser, $frame );
- if ( is_object( $dframe ) && $dframe->hasFragment( $title->getFragment() ) ) {
- $ovr = [];
- unset( $args['default'] );
- foreach ( $args as $key => $val ) {
- $ovr[$key] = $parser->replaceVariables( $val, $frame );
- }
- return [
- $dframe->expandUsing( $frame, $frame->title, $text, $ovr, $title->getFragment(), true ),
- 'markerType' => 'none'
- ];
- }
- }
-
- return [
- isset( $args['default'] ) ? $parser->replaceVariables( $args['default'], $frame ) : '',
- 'markerType' => 'none'
- ];
- }
-
- /* {{#data:Template#Hash|...}} specifies data-transcludable arguments for the page; may not be transcluded. */
- public function dataParserFunction( Parser &$parser, PPFrame $frame, $args ) {
- $templateTitle = trim( $frame->expand( $args[0] ) );
- $hostPage = $frame->title->getPrefixedText();
- unset( $args[0] );
- $fragment = '';
-
- if ( strpos( $templateTitle, '%' ) !== false ) {
- $templateTitle = str_replace( [ '<', '>' ], [ '<', '>' ], urldecode( $templateTitle ) );
- }
-
- $templateTitleObj = \Title::newFromText( $templateTitle, NS_TEMPLATE );
- if ( is_object( $templateTitleObj ) ) {
- $fragment = $templateTitleObj->getFragment();
- } elseif ( $templateTitle != '' && $templateTitle[0] == '#' ) {
- $fragment = substr( $templateTitle, 1 );
- }
-
- if ( $frame->depth == 0 || $this->searchingForData ) {
- if ( !isset( $this->dataFrames[$hostPage] ) ) {
- $this->dataFrames[$hostPage] = new UsingDataPPFrameDOM( $frame, $hostPage );
- }
- $df =& $this->dataFrames[$hostPage];
- $df->addArgs( $frame, $args, $fragment );
- if ( $this->searchingForData ) {
- return '';
- }
- }
- if ( !is_object( $templateTitleObj ) ) {
- return '';
- }
-
- [ $dom, $tTitle ] = $this->fetchTemplate( $parser, $templateTitleObj );
- foreach ( $args as $k => $v ) {
- // Line below breaks #data processing, but it exists in old implementation
- // $args[$k] = $v->node;
- }
- $cframe = $frame->newChild( $args, $tTitle );
- $nargs =& $cframe->namedArgs;
- $nargs['data-found'] = $frame->depth == 0 ? '3' : '2';
- $nargs['data-source'] = $hostPage;
- $nargs['data-sourcee'] = wfEscapeWikiText( $hostPage );
- $nargs['data-fragment'] = $fragment;
- $nargs['data-source-fragment'] = $hostPage . ( empty( $fragment ) ? '' : ( '#' . $fragment ) );
- return $cframe->expand( $dom );
- }
-
- /* Returns template text for transclusion.
- */
- private function fetchTemplate( $parser, $template ) {
- global $wgNonincludableNamespaces;
-
- if ( $template == '' || ( !is_string( $template ) && !is_object( $template ) ) ) {
- return '';
- }
-
- $title = is_object( $template ) ? $template : \Title::newFromText( $template, NS_TEMPLATE );
- if ( !is_object( $title )
- || $title->getNamespace() == NS_SPECIAL
- || ( $wgNonincludableNamespaces && in_array( $title->getNamespace(), $wgNonincludableNamespaces ) ) ) {
- return is_object( $title )
- ? [ '[[:' . $title->getPrefixedText() . ']]', $title ]
- : [ '[[:' . $template . ']]', null ];
- }
- [ $dom, $title ] = $parser->getTemplateDom( $title );
- return [ $dom ? $dom : ( '[[:' . $title->getPrefixedText() . ']]' ), $title ];
- }
-
- public static function onBeforeParserFetchTemplateRevisionRecord( ?LinkTarget $contextTitle, LinkTarget $title,
- bool &$skip, ?RevisionRecord &$revRecord ): bool {
- if ( !self::$instance->searchingForData ) {
- return true;
- }
- if ( self::$phTitle === null ) {
- self::$phTitle = \Title::newFromText( 'UsingDataPlaceholderTitle', NS_MEDIAWIKI );
- }
-
- $title = self::$phTitle;
- $skip = true;
-
- return false;
- }
-}
diff --git a/extension.json b/extension.json
index f694182..64a74aa 100644
--- a/extension.json
+++ b/extension.json
@@ -1,12 +1,12 @@
{
"name": "UsingData",
- "version": "2.0.0-hydra",
+ "version": "2.1.0",
"author": "foxlit",
"descriptionmsg": "usingdata-description",
"license-name": "GPL-2.0-or-later",
"type": "parserhook",
"requires": {
- "MediaWiki": ">= 1.29.0"
+ "MediaWiki": ">= 1.39.0"
},
"MessagesDirs": {
"UsingData": [
@@ -16,15 +16,20 @@
"ExtensionMessagesFiles": {
"UsingDataMagic": "UsingData.i18n.magic.php"
},
- "AutoloadClasses": {
- "UsingDataHooks": "UsingDataHooks.php",
- "UsingDataPPFrameDOM": "src/UsingDataPPFrameDOM.php"
+ "AutoloadNamespaces": {
+ "UsingData\\": "src/"
+ },
+ "HookHandlers": {
+ "UsingDataHookHandler": {
+ "class": "UsingData\\UsingDataHooks",
+ "services": [ "MainConfig" ]
+ }
},
"Hooks": {
- "BeforeParserFetchTemplateRevisionRecord": "UsingDataHooks::onBeforeParserFetchTemplateRevisionRecord",
- "GetMagicVariableIDs": "UsingDataHooks::onGetMagicVariableIDs",
- "ParserFirstCallInit": "UsingDataHooks::onParserFirstCallInit",
- "ParserGetVariableValueSwitch": "UsingDataHooks::ancestorNameVar"
+ "BeforeParserFetchTemplateRevisionRecord": "UsingDataHookHandler",
+ "GetMagicVariableIDs": "UsingDataHookHandler",
+ "ParserFirstCallInit": "UsingDataHookHandler",
+ "ParserGetVariableValueSwitch": "UsingDataHookHandler"
},
"manifest_version": 1
}
diff --git a/src/UsingDataHooks.php b/src/UsingDataHooks.php
new file mode 100644
index 0000000..e5eefff
--- /dev/null
+++ b/src/UsingDataHooks.php
@@ -0,0 +1,298 @@
+setFunctionHook( 'using', [ $this, 'usingParserFunction' ], SFH_OBJECT_ARGS );
+ $parser->setFunctionHook( 'usingarg', [ $this, 'usingArgParserFunction' ], SFH_OBJECT_ARGS );
+ $parser->setFunctionHook( 'data', [ $this, 'dataParserFunction' ], SFH_OBJECT_ARGS );
+ $parser->setFunctionHook( 'ancestorname', [ $this, 'ancestorNameFunction' ], SFH_OBJECT_ARGS | SFH_NO_HASH );
+ $parser->setHook( 'using', [ $this, 'usingTag' ] );
+ }
+
+ /** @inheritDoc */
+ public function onGetMagicVariableIDs( &$variableIDs ) {
+ $variableIDs[] = 'parentname';
+ $variableIDs[] = 'selfname';
+ }
+
+ /** Returns a UsingData frame for a given page */
+ private function getDataFrame( $sourcePage, ?Title $title, Parser &$parser, PPFrame $frame ): UsingDataPPFrameDOM {
+ if ( isset( $this->dataFrames[$sourcePage] ) ) {
+ return $this->dataFrames[$sourcePage];
+ }
+
+ $this->dataFrames[$sourcePage] = new UsingDataPPFrameDOM( $frame, $sourcePage );
+ if (
+ (
+ $sourcePage === '' ||
+ Title::castFromPageReference( $parser->getPage() )->getPrefixedText()
+ ) && !$parser->getOptions()->getIsSectionPreview() ) {
+ return $this->dataFrames[$sourcePage];
+ }
+
+ [ $text, $fTitle ] = $parser->fetchTemplateAndTitle( $title );
+ if ( is_object( $fTitle ) && $fTitle->getPrefixedText() != $sourcePage ) {
+ $this->dataFrames[$fTitle->getPrefixedText()] = $this->dataFrames[$sourcePage];
+ }
+
+ global $wgHooks;
+ if ( is_string( $text ) && $text != '' ) {
+ $this->searchingForData = true;
+ $clearStateHooks = $wgHooks['ParserClearState'];
+ // Other extensions tend to assume the hook is only called by wgParser and reset internal state
+ $wgHooks['ParserClearState'] = [];
+ $subParser = clone $parser;
+ $subParser->preprocess( $text, $fTitle, clone $parser->getOptions() );
+ // We might've blocked access to templates while preprocessing; should not be cached
+ $subParser->clearState();
+ $subParser->getOutput()->setText( $parser->getOutput()->getText() );
+ $wgHooks['ParserClearState'] = empty( $wgHooks['ParserClearState'] )
+ ? $clearStateHooks
+ : array_merge( $clearStateHooks, $wgHooks['ParserClearState'] );
+ $parser->mPPNodeCount += $subParser->mPPNodeCount;
+ $this->searchingForData = false;
+ }
+ return $this->dataFrames[$sourcePage];
+ }
+
+ /** Returns the page title of the $depth ancestor of $frame; empty string if invalid */
+ private function ancestorNameHandler( PPFrame $frame, int $depth ): string {
+ while ( $depth-- && $frame !== null ) {
+ $frame = $frame->parent ?? null;
+ }
+ return is_object( $frame?->title ) ? wfEscapeWikiText( $frame->title->getPrefixedText() ) : '';
+ }
+
+ /** Handles {{ANCESTORNAME:depth}} */
+ public function ancestorNameFunction( Parser $parser, PPFrame $frame, array $args ) {
+ $arg = $frame->expand( $args[0] );
+ $depth = max( 0, is_numeric( $arg ) ? (int)$arg : 1 );
+ return [ $this->ancestorNameHandler( $frame, $depth ), 'noparse' => true ];
+ }
+
+ /**
+ * @inheritDoc
+ * Handles {{PARENTNAME}}, {{SELFNAME}}, {{ANCESTORNAME}}
+ */
+ public function onParserGetVariableValueSwitch( $parser, &$variableCache, $magicWordId, &$ret, $frame ) {
+ if ( $magicWordId === 'parentname' ) {
+ $ret = $this->ancestorNameHandler( $frame, 1 );
+ }
+ if ( $magicWordId === 'selfname' ) {
+ $ret = $this->ancestorNameHandler( $frame, 0 );
+ }
+ }
+
+ /** Parses common elements of #using syntax. */
+ private function usingParse( Parser &$parser, PPFrame $frame, array $args ) {
+ $title = Title::newFromText( $this->sanitizeTitleName( $frame->expand( $args[0] ) ) );
+ $sourcePage = $title?->getPrefixedText() ?? '';
+ $sourceHash = $title?->getFragment() ?? '';
+ $namedArgs = [];
+
+ $one = null;
+ $two = null;
+ foreach ( $args as $key => $val ) {
+ if ( $key === 0 ) {
+ continue;
+ }
+ $bits = $val->splitArg();
+ // It looks like indexes are now integers, it is a change from legacy implementation
+ if ( $bits['index'] === 1 ) {
+ $one = $frame->expand( $bits['value'] );
+ } elseif ( $bits['index'] === 2 ) {
+ $two = $bits['value'];
+ } elseif ( $bits['index'] === '' ) {
+ $namedArgs[trim( $frame->expand( $bits['name'] ) )] = $bits['value'];
+ }
+ }
+ return [ $this->getDataFrame( $sourcePage, $title, $parser, $frame ), $sourceHash, $namedArgs, $one, $two ];
+ }
+
+ /**
+ * {{#using:Page#Hash|Template|Default|...}} parses Template using #data from Page's Hash fragment; or Default
+ * if no data from Page can be found. Named arguments override those in the #data tag.
+ */
+ public function usingParserFunction( Parser $parser, PPFrame $frame, array $args ) {
+ if ( $this->searchingForData ) {
+ return '';
+ }
+
+ /** @var UsingDataPPFrameDOM $dframe */
+ [ $dframe, $fragment, $namedArgs, $templateTitle, $defaultValue ] = $this->usingParse( $parser, $frame, $args );
+
+ if ( !$dframe->hasFragment( $fragment ) && $defaultValue !== null ) {
+ return $frame->expand( $defaultValue );
+ }
+ [ $dom, $title ] = $this->fetchTemplate( $parser, $templateTitle );
+ return $dframe->expandUsing( $frame, $title, $dom, $namedArgs, $fragment );
+ }
+
+ /**
+ * {{#usingarg:Page#Hash|Arg|Default}}
+ * returns the value of Arg data field on Page's Hash fragment, Default if undefined.
+ */
+ public function usingArgParserFunction( Parser &$parser, PPFrame $frame, array $args ) {
+ if ( $this->searchingForData ) {
+ return '';
+ }
+ /** @var UsingDataPPFrameDOM $dframe */
+ [ $dframe, $fragment, $namedArgs, $argName, $defaultValue ] = $this->usingParse( $parser, $frame, $args );
+
+ $ret = $dframe->getArgumentForParser(
+ $parser,
+ UsingDataPPFrameDOM::normalizeFragment( $fragment ),
+ $argName,
+ $defaultValue === null ? '' : false
+ );
+ return $ret !== false ? $ret : $frame->expand( $defaultValue );
+ }
+
+ /**
+ * ...
+ * expands ... using the data from Page's Hash fragment; Default if undefined.
+ * This tag relies on $parser->replaceVariables($text, $frame), which may prove fragile across MW versions.
+ * Should it break, $parser->recursiveTagParse($text, $frame),
+ * in combination with either modifying the markerType, or using
+ * insertStripItem directly, is a viable short-term alternative -- but one that call certain hooks prematurely,
+ * potentially causing other extensions to misbehave slightly.
+ */
+ public function usingTag( $text, array $args, Parser $parser, PPFrame $frame ): array {
+ if ( $this->searchingForData ) {
+ return [ '', 'markerType' => 'none' ];
+ }
+
+ $source = isset( $args['page'] ) ? $parser->replaceVariables( $args['page'], $frame ) : '';
+ unset( $args['page'] );
+ $title = Title::newFromText( $this->sanitizeTitleName( $source ) );
+ if ( $title ) {
+ $dframe = $this->getDataFrame( $title->getPrefixedText(), $title, $parser, $frame );
+ if ( $dframe->hasFragment( $title->getFragment() ) ) {
+ $ovr = [];
+ unset( $args['default'] );
+ foreach ( $args as $key => $val ) {
+ $ovr[$key] = $parser->replaceVariables( $val, $frame );
+ }
+ return [
+ $dframe->expandUsing( $frame, $frame->title, $text, $ovr, $title->getFragment(), true ),
+ 'markerType' => 'none'
+ ];
+ }
+ }
+
+ return [
+ isset( $args['default'] ) ? $parser->replaceVariables( $args['default'], $frame ) : '',
+ 'markerType' => 'none'
+ ];
+ }
+
+ /** {{#data:Template#Hash|...}} specifies data-transcludable arguments for the page; may not be transcluded. */
+ public function dataParserFunction( Parser &$parser, PPFrame $frame, $args ): string {
+ $templateTitle = $this->sanitizeTitleName( $frame->expand( $args[0] ) );
+ $hostPage = $frame->title->getPrefixedText();
+ unset( $args[0] );
+ $fragment = '';
+
+ $templateTitleObj = Title::newFromText( $templateTitle, NS_TEMPLATE );
+ if ( $templateTitleObj ) {
+ $fragment = $templateTitleObj->getFragment();
+ } elseif ( str_starts_with( $templateTitle, '#' ) ) {
+ $fragment = substr( $templateTitle, 1 );
+ }
+
+ if ( $frame->depth == 0 || $this->searchingForData ) {
+ if ( !isset( $this->dataFrames[$hostPage] ) ) {
+ $this->dataFrames[$hostPage] = new UsingDataPPFrameDOM( $frame, $hostPage );
+ }
+ $this->dataFrames[$hostPage]->addArgs( $frame, $args, $fragment );
+ if ( $this->searchingForData ) {
+ return '';
+ }
+ }
+
+ if ( !$templateTitleObj ) {
+ return '';
+ }
+
+ [ $dom, $tTitle ] = $this->fetchTemplate( $parser, $templateTitleObj );
+ $cframe = $frame->newChild( $args, $tTitle );
+ $cframe->namedArgs['data-found'] = $frame->depth == 0 ? '3' : '2';
+ $cframe->namedArgs['data-source'] = $hostPage;
+ $cframe->namedArgs['data-sourcee'] = wfEscapeWikiText( $hostPage );
+ $cframe->namedArgs['data-fragment'] = $fragment;
+ $cframe->namedArgs['data-source-fragment'] = $hostPage . ( empty( $fragment ) ? '' : ( '#' . $fragment ) );
+ return $cframe->expand( $dom );
+ }
+
+ /** Returns template text for transclusion. */
+ private function fetchTemplate( Parser $parser, $template ): array {
+ if ( $template == '' || ( !is_string( $template ) && !is_object( $template ) ) ) {
+ return [ '', null ];
+ }
+
+ $title = is_object( $template ) ? $template : Title::newFromText( $template, NS_TEMPLATE );
+ if ( !$title ||
+ $title->getNamespace() === NS_SPECIAL ||
+ (
+ $this->config->has( 'NonincludableNamespaces' ) &&
+ in_array( $title->getNamespace(), $this->config->get( 'NonincludableNamespaces' ) )
+ )
+ ) {
+ return $title ? [ '[[:' . $title->getPrefixedText() . ']]', $title ] : [ '[[:' . $template . ']]', null ];
+ }
+
+ [ $dom, $title ] = $parser->getTemplateDom( $title );
+ return [ $dom ?: ( '[[:' . $title->getPrefixedText() . ']]' ), $title ];
+ }
+
+ /** @inheritDoc */
+ public function onBeforeParserFetchTemplateRevisionRecord(
+ ?LinkTarget $contextTitle,
+ LinkTarget $title,
+ bool &$skip,
+ ?RevisionRecord &$revRecord
+ ) {
+ if ( !$this->searchingForData ) {
+ return true;
+ }
+
+ $title = Title::newFromText( 'UsingDataPlaceholderTitle', NS_MEDIAWIKI );
+ $skip = true;
+
+ return false;
+ }
+
+ private function sanitizeTitleName( string $title ): string {
+ $trimmedTitle = trim( $title );
+ if ( str_contains( $trimmedTitle, '%' ) ) {
+ return str_replace( [ '<', '>' ], [ '<', '>' ], urldecode( $trimmedTitle ) );
+ }
+ return $trimmedTitle;
+ }
+}
diff --git a/src/UsingDataPPFrameDOM.php b/src/UsingDataPPFrameDOM.php
index 0b1bdd3..f5626c5 100644
--- a/src/UsingDataPPFrameDOM.php
+++ b/src/UsingDataPPFrameDOM.php
@@ -1,5 +1,11 @@
expandedArgs = [];
}
- if ( is_string( $text ) && $useRTP ) {
- $ret = $this->expansionForParser->replaceVariables( $text, $this );
- } else {
- $ret = $this->expand( $text === null ? '' : $text );
- }
+ $ret = is_string( $text ) && $useRTP ?
+ $this->expansionForParser->replaceVariables( $text, $this ) :
+ $this->expand( $text === null ? '' : $text );
$this->overrideArgs = $oldArgs;
$this->expansionFragment = $oldFragment;
@@ -106,7 +110,7 @@ public function getArgumentForParser( $parser, $normalizedFragment, $arg, $defau
$text = $aar[1][$arg];
unset( $aar[1][$arg] );
$text = $aar[0]->expand( $text );
- if ( strpos( $text, Parser::MARKER_PREFIX ) !== false ) {
+ if ( str_contains( $text, Parser::MARKER_PREFIX ) ) {
$text = $aar[0]->parser->serialiseHalfParsedText( ' ' . $text ); // MW bug 26731
}
$this->serializedArgs[$arg] = $text;
@@ -138,7 +142,7 @@ public function getArgument( $index ) {
case 'data-fragment':
return $this->expansionFragment;
case 'data-source-fragment':
- return $this->sourcePage . ( empty( $this->expansionFragment ) ? '' : ( '#' . $this->expansionFragment ) );
+ return $this->sourcePage . ( empty( $this->expansionFragment ) ? '' : "#$this->expansionFragment" );
default:
if ( is_array( $this->overrideArgs ) && isset( $this->overrideArgs[$index] ) ) {
if ( is_object( $this->overrideArgs[$index] ) ) {
@@ -146,7 +150,7 @@ public function getArgument( $index ) {
}
return $this->overrideArgs[$index];
}
- $p = $this->expansionForParser === null ? $this->parent->parser : $this->expansionForParser;
+ $p = $this->expansionForParser ?? $this->parent->parser;
return $this->getArgumentForParser( $p, $this->expansionFragmentN, $index, false );
}
}