Index: Preprocessor_Hash.php =================================================================== --- Preprocessor_Hash.php (revision 64504) +++ Preprocessor_Hash.php (working copy) @@ -849,7 +849,7 @@ return new PPTemplateFrame_Hash( $this->preprocessor, $this, $numberedArgs, $namedArgs, $title ); } - function expand( $root, $flags = 0 ) { + function expand( $root, $flags = 0, $parseState = null ) { static $expansionDepth = 0; if ( is_string( $root ) ) { return $root; @@ -918,7 +918,7 @@ if ( $flags & self::NO_TEMPLATES ) { $newIterator = $this->virtualBracketedImplode( '{{', '|', '}}', $bits['title'], $bits['parts'] ); } else { - $ret = $this->parser->braceSubstitution( $bits, $this ); + $ret = $this->parser->braceSubstitution( $bits, $this, $parseState ); if ( isset( $ret['object'] ) ) { $newIterator = $ret['object']; } else { @@ -931,7 +931,7 @@ if ( $flags & self::NO_ARGS ) { $newIterator = $this->virtualBracketedImplode( '{{{', '|', '}}}', $bits['title'], $bits['parts'] ); } else { - $ret = $this->parser->argSubstitution( $bits, $this ); + $ret = $this->parser->argSubstitution( $bits, $this, $parseState ); if ( isset( $ret['object'] ) ) { $newIterator = $ret['object']; } else { @@ -969,7 +969,7 @@ } elseif ( $contextNode->name == 'ext' ) { # Extension tag $bits = $contextNode->splitExt() + array( 'attr' => null, 'inner' => null, 'close' => null ); - $out .= $this->parser->extensionSubstitution( $bits, $this ); + $out .= $this->parser->extensionSubstitution( $bits, $this, $parseState ); } elseif ( $contextNode->name == 'h' ) { # Heading if ( $this->parser->ot['html'] ) { Index: Parser.php =================================================================== --- Parser.php (revision 64504) +++ Parser.php (working copy) @@ -2797,10 +2797,11 @@ * $piece['parts']: the parameter array * $piece['lineStart']: whether the brace was at the start of a line * @param PPFrame The current frame, contains template arguments + * @param ParserState The current parse-state. * @return string the text of the template * @private */ - function braceSubstitution( $piece, $frame ) { + function braceSubstitution( $piece, $frame, $parseState ) { global $wgContLang, $wgNonincludableNamespaces; wfProfileIn( __METHOD__ ); wfProfileIn( __METHOD__.'-setup' ); @@ -2829,28 +2830,37 @@ $args = ( null == $piece['parts'] ) ? array() : $piece['parts']; wfProfileOut( __METHOD__.'-setup' ); + print_r( $parseState ); # SUBST wfProfileIn( __METHOD__.'-modifiers' ); if ( !$found ) { $substMatch = $this->mSubstWords->matchStartAndRemove( $part1 ); - # Possibilities for substMatch: "subst", "safesubst" or FALSE # Decide whether to expand template or keep wikitext as-is. - if ( $this->ot['wiki'] ) { - if ( $substMatch === false ) { - $literal = true; # literal when in PST with no prefix - } else { - $literal = false; # expand when in PST with subst: or safesubst: - } - } else { - if ( $substMatch == 'subst' ) { - $literal = true; # literal when not in PST with plain subst: - } else { - $literal = false; # expand when not in PST with safesubst: or no prefix - } - } - if ( $literal ) { + switch ( $substMatch ) { + case 'subst': + # expand only during the PST + $literal = !$this->ot['wiki']; + break; + + case 'safesubst': + # always expand + $literal = false; + break; + + case 'substall': + # always expand + $literal = false; + $parseState = array_merge( (array)$parseState, array( 'forceExpand' => true ) ); + break; + + default: + # expand only after the PST + $literal = $this->ot['wiki']; + break; + } + if ( $literal && !( $parseState && $parseState['forceExpand'] ) ) { $text = $frame->virtualBracketedImplode( '{{', '|', '}}', $titleWithSpaces, $args ); $isLocalObj = true; $found = true; @@ -3056,18 +3066,18 @@ $newFrame = $frame->newChild( $args, $title ); if ( $nowiki ) { - $text = $newFrame->expand( $text, PPFrame::RECOVER_ORIG ); - } elseif ( $titleText !== false && $newFrame->isEmpty() ) { + $text = $newFrame->expand( $text, PPFrame::RECOVER_ORIG, $parseState ); + } elseif ( $titleText !== false && $newFrame->isEmpty() && !$parseState ) { # Expansion is eligible for the empty-frame cache if ( isset( $this->mTplExpandCache[$titleText] ) ) { $text = $this->mTplExpandCache[$titleText]; } else { - $text = $newFrame->expand( $text ); + $text = $newFrame->expand( $text, 0, $parseState ); $this->mTplExpandCache[$titleText] = $text; } } else { # Uncached expansion - $text = $newFrame->expand( $text ); + $text = $newFrame->expand( $text, 0, $parseState ); } } if ( $isLocalObj && $nowiki ) { @@ -3276,12 +3286,12 @@ * Triple brace replacement -- used for template arguments * @private */ - function argSubstitution( $piece, $frame ) { + function argSubstitution( $piece, $frame, $parseState ) { wfProfileIn( __METHOD__ ); $error = false; $parts = $piece['parts']; - $nameWithSpaces = $frame->expand( $piece['title'] ); + $nameWithSpaces = $frame->expand( $piece['title'], 0, $parseState ); $argName = trim( $nameWithSpaces ); $object = false; $text = $frame->getArgument( $argName ); @@ -3329,12 +3339,12 @@ * noClose Original text did not have a close tag * @param PPFrame $frame */ - function extensionSubstitution( $params, $frame ) { + function extensionSubstitution( $params, $frame, $parseState ) { global $wgRawHtml, $wgContLang; - $name = $frame->expand( $params['name'] ); - $attrText = !isset( $params['attr'] ) ? null : $frame->expand( $params['attr'] ); - $content = !isset( $params['inner'] ) ? null : $frame->expand( $params['inner'] ); + $name = $frame->expand( $params['name'], 0, $parseState ); + $attrText = !isset( $params['attr'] ) ? null : $frame->expand( $params['attr'], 0, $parseState ); + $content = !isset( $params['inner'] ) ? null : $frame->expand( $params['inner'], 0, $parseState ); $marker = "{$this->mUniqPrefix}-$name-" . sprintf( '%08X', $this->mMarkerIndex++ ) . self::MARKER_SUFFIX; $isFunctionTag = isset( $this->mFunctionTagHooks[strtolower($name)] ) && @@ -3390,7 +3400,7 @@ if ( $content === null ) { $output = "<$name$attrText/>"; } else { - $close = is_null( $params['close'] ) ? '' : $frame->expand( $params['close'] ); + $close = is_null( $params['close'] ) ? '' : $frame->expand( $params['close'], 0, $parseState ); $output = "<$name$attrText>$content$close"; } } Index: Preprocessor_DOM.php =================================================================== --- Preprocessor_DOM.php (revision 64504) +++ Preprocessor_DOM.php (working copy) @@ -871,7 +871,7 @@ return new PPTemplateFrame_DOM( $this->preprocessor, $this, $numberedArgs, $namedArgs, $title ); } - function expand( $root, $flags = 0 ) { + function expand( $root, $flags = 0, $parseState = null ) { static $expansionDepth = 0; if ( is_string( $root ) ) { return $root; @@ -958,8 +958,9 @@ $params = array( 'title' => new PPNode_DOM( $title ), 'parts' => new PPNode_DOM( $parts ), - 'lineStart' => $lineStart ); - $ret = $this->parser->braceSubstitution( $params, $this ); + 'lineStart' => $lineStart, + ); + $ret = $this->parser->braceSubstitution( $params, $this, $parseState ); if ( isset( $ret['object'] ) ) { $newIterator = $ret['object']; } else { @@ -978,7 +979,7 @@ $params = array( 'title' => new PPNode_DOM( $title ), 'parts' => new PPNode_DOM( $parts ) ); - $ret = $this->parser->argSubstitution( $params, $this ); + $ret = $this->parser->argSubstitution( $params, $this, $parseState ); if ( isset( $ret['object'] ) ) { $newIterator = $ret['object']; } else { @@ -1026,7 +1027,7 @@ 'inner' => $inners->length > 0 ? new PPNode_DOM( $inners->item( 0 ) ) : null, 'close' => $closes->length > 0 ? new PPNode_DOM( $closes->item( 0 ) ) : null, ); - $out .= $this->parser->extensionSubstitution( $params, $this ); + $out .= $this->parser->extensionSubstitution( $params, $this, $parseState ); } elseif ( $contextNode->nodeName == 'h' ) { # Heading $s = $this->expand( $contextNode->childNodes, $flags );