Index: trunk/phase3/maintenance/parserTests.txt |
— | — | @@ -6569,7 +6569,56 @@ |
6570 | 6570 | </p> |
6571 | 6571 | !! end |
6572 | 6572 | |
| 6573 | +!!article |
| 6574 | +Template:Bullet |
| 6575 | +!!text |
| 6576 | +* Bar |
| 6577 | +!!endarticle |
6573 | 6578 | |
| 6579 | +!! test |
| 6580 | +Bug 529: Uncovered bullet |
| 6581 | +!! input |
| 6582 | +* Foo {{bullet}} |
| 6583 | +!! result |
| 6584 | +<ul><li> Foo |
| 6585 | +</li><li> Bar |
| 6586 | +</li></ul> |
| 6587 | + |
| 6588 | +!! end |
| 6589 | + |
| 6590 | +!! test |
| 6591 | +Bug 529: Uncovered table already at line-start |
| 6592 | +!! input |
| 6593 | +x |
| 6594 | + |
| 6595 | +{{table}} |
| 6596 | +y |
| 6597 | +!! result |
| 6598 | +<p>x |
| 6599 | +</p> |
| 6600 | +<table> |
| 6601 | +<tr> |
| 6602 | +<td> 1 </td><td> 2 |
| 6603 | +</td></tr> |
| 6604 | +<tr> |
| 6605 | +<td> 3 </td><td> 4 |
| 6606 | +</td></tr></table> |
| 6607 | +<p>y |
| 6608 | +</p> |
| 6609 | +!! end |
| 6610 | + |
| 6611 | +!! test |
| 6612 | +Bug 529: Uncovered bullet in parser function result |
| 6613 | +!! input |
| 6614 | +* Foo {{lc:{{bullet}} }} |
| 6615 | +!! result |
| 6616 | +<ul><li> Foo |
| 6617 | +</li><li> bar |
| 6618 | +</li></ul> |
| 6619 | + |
| 6620 | +!! end |
| 6621 | + |
| 6622 | + |
6574 | 6623 | # |
6575 | 6624 | # |
6576 | 6625 | # |
Index: trunk/phase3/includes/Parser.php |
— | — | @@ -2593,6 +2593,10 @@ |
2594 | 2594 | $rules = $normalRules; |
2595 | 2595 | } |
2596 | 2596 | |
| 2597 | + if ( $this->ot['html'] || ( $this->ot['pre'] && $this->mOptions->getRemoveComments() ) ) { |
| 2598 | + $text = Sanitizer::removeHTMLcomments( $text ); |
| 2599 | + } |
| 2600 | + |
2597 | 2601 | $extElements = implode( '|', $this->getStripList() ); |
2598 | 2602 | // Use "A" modifier (anchored) instead of "^", because ^ doesn't work with an offset |
2599 | 2603 | $extElementsRegex = "/($extElements)(?:\s|\/>|>)|(!--)/iA"; |
— | — | @@ -3018,6 +3022,27 @@ |
3019 | 3023 | } |
3020 | 3024 | |
3021 | 3025 | /** |
| 3026 | + * Convert text to a document tree, like preprocessToDom(), but with some special handling |
| 3027 | + * assuming the source text is from a template -- specifically noinclude/includeonly behaviour. |
| 3028 | + */ |
| 3029 | + function preprocessTplToDom( $text ) { |
| 3030 | + # If there are any <onlyinclude> tags, only include them |
| 3031 | + if ( !$this->ot['msg'] ) { |
| 3032 | + if ( in_string( '<onlyinclude>', $text ) && in_string( '</onlyinclude>', $text ) ) { |
| 3033 | + $replacer = new OnlyIncludeReplacer; |
| 3034 | + StringUtils::delimiterReplaceCallback( '<onlyinclude>', '</onlyinclude>', |
| 3035 | + array( &$replacer, 'replace' ), $text ); |
| 3036 | + $text = $replacer->output; |
| 3037 | + } |
| 3038 | + # Remove <noinclude> sections and <includeonly> tags |
| 3039 | + $text = StringUtils::delimiterReplace( '<noinclude>', '</noinclude>', '', $text ); |
| 3040 | + $text = strtr( $text, array( '<includeonly>' => '' , '</includeonly>' => '' ) ); |
| 3041 | + } |
| 3042 | + |
| 3043 | + return $this->preprocessToDom( $text ); |
| 3044 | + } |
| 3045 | + |
| 3046 | + /** |
3022 | 3047 | * Replace magic variables, templates, and template arguments |
3023 | 3048 | * with the appropriate text. Templates are substituted recursively, |
3024 | 3049 | * taking care to avoid infinite loops. |
— | — | @@ -3047,12 +3072,6 @@ |
3048 | 3073 | throw new MWException( __METHOD__ . ' called using the old argument format' ); |
3049 | 3074 | } |
3050 | 3075 | |
3051 | | - # Remove comments |
3052 | | - # This could theoretically be merged into preprocessToDom() |
3053 | | - if ( $this->ot['html'] || ( $this->ot['pre'] && $this->mOptions->getRemoveComments() ) ) { |
3054 | | - $text = Sanitizer::removeHTMLcomments( $text ); |
3055 | | - } |
3056 | | - |
3057 | 3076 | $dom = $this->preprocessToDom( $text ); |
3058 | 3077 | $flags = $argsOnly ? PPFrame::NO_TEMPLATES : 0; |
3059 | 3078 | $text = $frame->expand( $dom, $flags ); |
— | — | @@ -3105,8 +3124,6 @@ |
3106 | 3125 | # Flags |
3107 | 3126 | $found = false; # $text has been filled |
3108 | 3127 | $nowiki = false; # wiki markup in $text should be escaped |
3109 | | - $noparse = false; # Unsafe HTML tags should not be stripped, etc. |
3110 | | - $noargs = false; # Don't replace triple-brace arguments in $text |
3111 | 3128 | $isHTML = false; # $text is HTML, armour it against wikitext transformation |
3112 | 3129 | $forceRawInterwiki = false; # Force interwiki transclusion to be done in raw mode not rendered |
3113 | 3130 | $isDOM = false; # $text is a DOM node needing expansion |
— | — | @@ -3138,8 +3155,6 @@ |
3139 | 3156 | # In either case, return without further processing |
3140 | 3157 | $text = '{{' . $frame->implode( '|', $titleWithSpaces, $args ) . '}}'; |
3141 | 3158 | $found = true; |
3142 | | - $noparse = true; |
3143 | | - $noargs = true; |
3144 | 3159 | } |
3145 | 3160 | } |
3146 | 3161 | |
— | — | @@ -3150,8 +3165,6 @@ |
3151 | 3166 | $text = $this->getVariableValue( $id ); |
3152 | 3167 | $this->mOutput->mContainsOldMagic = true; |
3153 | 3168 | $found = true; |
3154 | | - $noparse = true; |
3155 | | - $noargs = true; |
3156 | 3169 | } |
3157 | 3170 | } |
3158 | 3171 | |
— | — | @@ -3220,10 +3233,6 @@ |
3221 | 3234 | $result = call_user_func_array( $callback, $allArgs ); |
3222 | 3235 | $found = true; |
3223 | 3236 | |
3224 | | - // The text is usually already parsed, doesn't need triple-brace tags expanded, etc. |
3225 | | - $noargs = true; |
3226 | | - $noparse = true; |
3227 | | - |
3228 | 3237 | if ( is_array( $result ) ) { |
3229 | 3238 | if ( isset( $result[0] ) ) { |
3230 | 3239 | $text = $result[0]; |
— | — | @@ -3231,7 +3240,7 @@ |
3232 | 3241 | } |
3233 | 3242 | |
3234 | 3243 | // Extract flags into the local scope |
3235 | | - // This allows callers to set flags such as nowiki, noparse, found, etc. |
| 3244 | + // This allows callers to set flags such as nowiki, found, etc. |
3236 | 3245 | extract( $result ); |
3237 | 3246 | } else { |
3238 | 3247 | $text = $result; |
— | — | @@ -3260,8 +3269,6 @@ |
3261 | 3270 | } |
3262 | 3271 | # Do infinite loop check |
3263 | 3272 | if ( isset( $this->mTemplatePath[$titleText] ) ) { |
3264 | | - $noparse = true; |
3265 | | - $noargs = true; |
3266 | 3273 | $found = true; |
3267 | 3274 | $text = "[[$part1]]" . $this->insertStripItem( '<!-- WARNING: template loop detected -->' ); |
3268 | 3275 | wfDebug( __METHOD__.": template loop broken at '$titleText'\n" ); |
— | — | @@ -3277,8 +3284,6 @@ |
3278 | 3285 | $text = SpecialPage::capturePath( $title ); |
3279 | 3286 | if ( is_string( $text ) ) { |
3280 | 3287 | $found = true; |
3281 | | - $noparse = true; |
3282 | | - $noargs = true; |
3283 | 3288 | $isHTML = true; |
3284 | 3289 | $this->disableCache(); |
3285 | 3290 | } |
— | — | @@ -3303,108 +3308,75 @@ |
3304 | 3309 | if ( $this->ot['html'] && !$forceRawInterwiki ) { |
3305 | 3310 | $text = $this->interwikiTransclude( $title, 'render' ); |
3306 | 3311 | $isHTML = true; |
3307 | | - $noparse = true; |
3308 | 3312 | } else { |
3309 | 3313 | $text = $this->interwikiTransclude( $title, 'raw' ); |
| 3314 | + // Preprocess it like a template |
| 3315 | + $text = $this->preprocessTplToDom( $text ); |
| 3316 | + $isDOM = true; |
3310 | 3317 | } |
3311 | 3318 | $found = true; |
3312 | 3319 | } |
3313 | 3320 | wfProfileOut( __METHOD__ . '-loadtpl' ); |
3314 | 3321 | } |
3315 | 3322 | |
3316 | | - # Recursive parsing, escaping and link table handling |
3317 | | - # Only for HTML output |
3318 | | - if ( $nowiki && $found && ( $this->ot['html'] || $this->ot['pre'] ) ) { |
3319 | | - if ( $isDOM ) { |
3320 | | - $text = $frame->expand( $text ); |
3321 | | - } |
3322 | | - $text = wfEscapeWikiText( $text ); |
3323 | | - } elseif ( !$this->ot['msg'] && $found ) { |
3324 | | - if ( $noargs ) { |
3325 | | - $newFrame = $frame->newChild(); |
3326 | | - } else { |
3327 | | - # Clean up argument array |
3328 | | - $newFrame = $frame->newChild( $args, $title ); |
3329 | | - # Add a new element to the templace recursion path |
3330 | | - $this->mTemplatePath[$titleText] = 1; |
3331 | | - } |
| 3323 | + # If we haven't found text to substitute by now, we're done |
| 3324 | + # Recover the source wikitext and return it |
| 3325 | + if ( !$found ) { |
| 3326 | + $text = '{{' . $frame->implode( '|', $titleWithSpaces, $args ) . '}}'; |
| 3327 | + # Prune lower levels off the recursion check path |
| 3328 | + $this->mTemplatePath = $lastPathLevel; |
| 3329 | + wfProfileOut( $fname ); |
| 3330 | + return $text; |
| 3331 | + } |
3332 | 3332 | |
3333 | | - if ( !$noparse ) { |
3334 | | - if ( $isDOM ) { |
3335 | | - if ( $titleText !== false && count( $newFrame->args ) == 0 ) { |
3336 | | - # Expansion is eligible for the empty-frame cache |
3337 | | - if ( isset( $this->mTplExpandCache[$titleText] ) ) { |
3338 | | - $text = $this->mTplExpandCache[$titleText]; |
3339 | | - } else { |
3340 | | - $text = $newFrame->expand( $text ); |
3341 | | - $this->mTplExpandCache[$titleText] = $text; |
3342 | | - } |
3343 | | - } else { |
3344 | | - $text = $newFrame->expand( $text ); |
3345 | | - } |
3346 | | - } else { |
3347 | | - $text = $this->replaceVariables( $text, $newFrame ); |
3348 | | - } |
| 3333 | + # Expand DOM-style return values in a child frame |
| 3334 | + if ( $isDOM ) { |
| 3335 | + # Clean up argument array |
| 3336 | + $newFrame = $frame->newChild( $args, $title ); |
| 3337 | + # Add a new element to the templace recursion path |
| 3338 | + $this->mTemplatePath[$titleText] = 1; |
3349 | 3339 | |
3350 | | - # strip woz 'ere 2004-07 |
3351 | | - |
3352 | | - # Bug 529: if the template begins with a table or block-level |
3353 | | - # element, it should be treated as beginning a new line. |
3354 | | - # This behaviour is somewhat controversial. |
3355 | | - if (!$piece['lineStart'] && preg_match('/^(?:{\\||:|;|#|\*)/', $text)) /*}*/{ |
3356 | | - $text = "\n" . $text; |
3357 | | - } |
3358 | | - } elseif ( !$noargs ) { |
3359 | | - # $noparse and !$noargs |
3360 | | - # Just replace the arguments, not any double-brace items |
3361 | | - # This is used for rendered interwiki transclusion |
3362 | | - if ( $isDOM ) { |
3363 | | - $text = $newFrame->expand( $text, PPFrame::NO_TEMPLATES ); |
| 3340 | + if ( $titleText !== false && count( $newFrame->args ) == 0 ) { |
| 3341 | + # Expansion is eligible for the empty-frame cache |
| 3342 | + if ( isset( $this->mTplExpandCache[$titleText] ) ) { |
| 3343 | + $text = $this->mTplExpandCache[$titleText]; |
3364 | 3344 | } else { |
3365 | | - $text = $this->replaceVariables( $text, $newFrame, true ); |
| 3345 | + $text = $newFrame->expand( $text ); |
| 3346 | + $this->mTplExpandCache[$titleText] = $text; |
3366 | 3347 | } |
3367 | | - } elseif ( $isDOM ) { |
3368 | | - $text = $frame->expand( $text ); |
| 3348 | + } else { |
| 3349 | + $text = $newFrame->expand( $text ); |
3369 | 3350 | } |
3370 | | - } elseif ( $isDOM ) { |
3371 | | - $text = $frame->expand( $text, PPFrame::NO_TEMPLATES | PPFrame::NO_ARGS ); |
3372 | 3351 | } |
3373 | 3352 | |
| 3353 | + # Replace raw HTML by a placeholder |
| 3354 | + # Add a blank line preceding, to prevent it from mucking up |
| 3355 | + # immediately preceding headings |
| 3356 | + if ( $isHTML ) { |
| 3357 | + $text = "\n\n" . $this->insertStripItem( $text ); |
| 3358 | + } |
| 3359 | + # Escape nowiki-style return values |
| 3360 | + elseif ( $nowiki && ( $this->ot['html'] || $this->ot['pre'] ) ) { |
| 3361 | + $text = wfEscapeWikiText( $text ); |
| 3362 | + } |
| 3363 | + # Bug 529: if the template begins with a table or block-level |
| 3364 | + # element, it should be treated as beginning a new line. |
| 3365 | + # This behaviour is somewhat controversial. |
| 3366 | + elseif ( !$piece['lineStart'] && preg_match('/^(?:{\\||:|;|#|\*)/', $text)) /*}*/{ |
| 3367 | + $text = "\n" . $text; |
| 3368 | + } |
| 3369 | + |
3374 | 3370 | # Prune lower levels off the recursion check path |
3375 | 3371 | $this->mTemplatePath = $lastPathLevel; |
3376 | 3372 | |
3377 | | - if ( $found && !$this->incrementIncludeSize( 'post-expand', strlen( $text ) ) ) { |
| 3373 | + if ( !$this->incrementIncludeSize( 'post-expand', strlen( $text ) ) ) { |
3378 | 3374 | # Error, oversize inclusion |
3379 | 3375 | $text = "[[$originalTitle]]" . |
3380 | 3376 | $this->insertStripItem( '<!-- WARNING: template omitted, post-expand include size too large -->' ); |
3381 | | - $noparse = true; |
3382 | | - $noargs = true; |
3383 | 3377 | } |
3384 | 3378 | |
3385 | | - if ( !$found ) { |
3386 | | - wfProfileOut( $fname ); |
3387 | | - return '{{' . $frame->implode( '|', $titleWithSpaces, $args ) . '}}'; |
3388 | | - } else { |
3389 | | - wfProfileIn( __METHOD__ . '-placeholders' ); |
3390 | | - if ( $isHTML ) { |
3391 | | - # Replace raw HTML by a placeholder |
3392 | | - # Add a blank line preceding, to prevent it from mucking up |
3393 | | - # immediately preceding headings |
3394 | | - $text = "\n\n" . $this->insertStripItem( $text ); |
3395 | | - } |
3396 | | - wfProfileOut( __METHOD__ . '-placeholders' ); |
3397 | | - } |
3398 | | - |
3399 | | - # Prune lower levels off the recursion check path |
3400 | | - $this->mTemplatePath = $lastPathLevel; |
3401 | | - |
3402 | | - if ( !$found ) { |
3403 | | - wfProfileOut( $fname ); |
3404 | | - return '{{' . $frame->implode( '|', $titleWithSpaces, $args ) . '}}'; |
3405 | | - } else { |
3406 | | - wfProfileOut( $fname ); |
3407 | | - return $text; |
3408 | | - } |
| 3379 | + wfProfileOut( $fname ); |
| 3380 | + return $text; |
3409 | 3381 | } |
3410 | 3382 | |
3411 | 3383 | /** |
— | — | @@ -3432,27 +3404,7 @@ |
3433 | 3405 | return array( false, $title ); |
3434 | 3406 | } |
3435 | 3407 | |
3436 | | - # If there are any <onlyinclude> tags, only include them |
3437 | | - if ( !$this->ot['msg'] ) { |
3438 | | - if ( in_string( '<onlyinclude>', $text ) && in_string( '</onlyinclude>', $text ) ) { |
3439 | | - $replacer = new OnlyIncludeReplacer; |
3440 | | - StringUtils::delimiterReplaceCallback( '<onlyinclude>', '</onlyinclude>', |
3441 | | - array( &$replacer, 'replace' ), $text ); |
3442 | | - $text = $replacer->output; |
3443 | | - } |
3444 | | - # Remove <noinclude> sections and <includeonly> tags |
3445 | | - $text = StringUtils::delimiterReplace( '<noinclude>', '</noinclude>', '', $text ); |
3446 | | - $text = strtr( $text, array( '<includeonly>' => '' , '</includeonly>' => '' ) ); |
3447 | | - } |
3448 | | - |
3449 | | - # Remove comments |
3450 | | - # This could theoretically be merged into preprocessToDom() |
3451 | | - if ( $this->ot['html'] || ( $this->ot['pre'] && $this->mOptions->getRemoveComments() ) ) { |
3452 | | - $text = Sanitizer::removeHTMLcomments( $text ); |
3453 | | - } |
3454 | | - |
3455 | | - $dom = $this->preprocessToDom( $text ); |
3456 | | - |
| 3408 | + $dom = $this->preprocessTplToDom( $text ); |
3457 | 3409 | $this->mTplDomCache[ $titleText ] = $dom; |
3458 | 3410 | |
3459 | 3411 | if (! $title->equals($cacheTitle)) { |