| Index: trunk/phase3/includes/Parser.php |
| — | — | @@ -33,11 +33,9 @@ |
| 34 | 34 | # page would be proportional to the square of the input size. Hence, we limit the number |
| 35 | 35 | # of inclusions of any given page, thus bringing any attack back to O(N). |
| 36 | 36 | # |
| | 37 | + |
| 37 | 38 | define( "MAX_INCLUDE_REPEAT", 5 ); |
| 38 | 39 | |
| 39 | | -# Recursion depth of variable/inclusion evaluation |
| 40 | | -define( "MAX_INCLUDE_PASSES", 3 ); |
| 41 | | - |
| 42 | 40 | # Allowed values for $mOutputType |
| 43 | 41 | define( "OT_HTML", 1 ); |
| 44 | 42 | define( "OT_WIKI", 2 ); |
| — | — | @@ -50,7 +48,7 @@ |
| 51 | 49 | { |
| 52 | 50 | # Cleared with clearState(): |
| 53 | 51 | var $mOutput, $mAutonumber, $mLastSection, $mDTopen, $mStripState = array(); |
| 54 | | - var $mVariables, $mIncludeCount; |
| | 52 | + var $mVariables, $mIncludeCount, $mArgStack; |
| 55 | 53 | |
| 56 | 54 | # Temporary: |
| 57 | 55 | var $mOptions, $mTitle, $mOutputType; |
| — | — | @@ -69,10 +67,11 @@ |
| 70 | 68 | $this->mVariables = false; |
| 71 | 69 | $this->mIncludeCount = array(); |
| 72 | 70 | $this->mStripState = array(); |
| | 71 | + $this->mArgStack = array(); |
| 73 | 72 | } |
| 74 | 73 | |
| 75 | 74 | # First pass--just handle <nowiki> sections, pass the rest off |
| 76 | | - # to doWikiPass2() which does all the real work. |
| | 75 | + # to internalParse() which does all the real work. |
| 77 | 76 | # |
| 78 | 77 | # Returns a ParserOutput |
| 79 | 78 | # |
| — | — | @@ -91,21 +90,8 @@ |
| 92 | 91 | |
| 93 | 92 | $stripState = NULL; |
| 94 | 93 | $text = $this->strip( $text, $this->mStripState ); |
| 95 | | - $text = $this->doWikiPass2( $text, $linestart ); |
| 96 | | - # needs to be called last |
| 97 | | - $text = $this->doBlockLevels( $text, $linestart ); |
| | 94 | + $text = $this->internalParse( $text, $linestart ); |
| 98 | 95 | $text = $this->unstrip( $text, $this->mStripState ); |
| 99 | | - # Clean up special characters |
| 100 | | - $fixtags = array( |
| 101 | | - "/<hr *>/i" => '<hr/>', |
| 102 | | - "/<br *>/i" => '<br/>', |
| 103 | | - "/<center *>/i"=>'<span style="text-align:center;">', |
| 104 | | - "/<\\/center *>/i" => '</span>', |
| 105 | | - # Clean up spare ampersands; note that we probably ought to be |
| 106 | | - # more careful about named entities. |
| 107 | | - '/&(?!:amp;|#[Xx][0-9A-fa-f]+;|#[0-9]+;|[a-zA-Z0-9]+;)/' => '&' |
| 108 | | - ); |
| 109 | | - $text = preg_replace( array_keys($fixtags), array_values($fixtags), $text ); |
| 110 | 96 | |
| 111 | 97 | $this->mOutput->setText( $text ); |
| 112 | 98 | wfProfileOut( $fname ); |
| — | — | @@ -155,23 +141,15 @@ |
| 156 | 142 | function strip( $text, &$state ) |
| 157 | 143 | { |
| 158 | 144 | $render = ($this->mOutputType == OT_HTML); |
| 159 | | - if ( $state ) { |
| 160 | | - $nowiki_content = $state['nowiki']; |
| 161 | | - $hiero_content = $state['hiero']; |
| 162 | | - $math_content = $state['math']; |
| 163 | | - $pre_content = $state['pre']; |
| 164 | | - $item_content = $state['item']; |
| 165 | | - } else { |
| 166 | | - $nowiki_content = array(); |
| 167 | | - $hiero_content = array(); |
| 168 | | - $math_content = array(); |
| 169 | | - $pre_content = array(); |
| 170 | | - $item_content = array(); |
| 171 | | - } |
| | 145 | + $nowiki_content = array(); |
| | 146 | + $hiero_content = array(); |
| | 147 | + $math_content = array(); |
| | 148 | + $pre_content = array(); |
| | 149 | + $item_content = array(); |
| 172 | 150 | |
| 173 | 151 | # Replace any instances of the placeholders |
| 174 | 152 | $uniq_prefix = UNIQ_PREFIX; |
| 175 | | - $text = str_replace( $uniq_prefix, wfHtmlEscapeFirst( $uniq_prefix ), $text ); |
| | 153 | + #$text = str_replace( $uniq_prefix, wfHtmlEscapeFirst( $uniq_prefix ), $text ); |
| 176 | 154 | |
| 177 | 155 | $text = Parser::extractTags("nowiki", $text, $nowiki_content, $uniq_prefix); |
| 178 | 156 | foreach( $nowiki_content as $marker => $content ){ |
| — | — | @@ -213,35 +191,34 @@ |
| 214 | 192 | } |
| 215 | 193 | } |
| 216 | 194 | |
| 217 | | - $state = array( |
| 218 | | - 'nowiki' => $nowiki_content, |
| 219 | | - 'hiero' => $hiero_content, |
| 220 | | - 'math' => $math_content, |
| 221 | | - 'pre' => $pre_content, |
| 222 | | - 'item' => $item_content |
| 223 | | - ); |
| | 195 | + # Merge state with the pre-existing state, if there is one |
| | 196 | + if ( $state ) { |
| | 197 | + $state['nowiki'] = $state['nowiki'] + $nowiki_content; |
| | 198 | + $state['hiero'] = $state['hiero'] + $hiero_content; |
| | 199 | + $state['math'] = $state['math'] + $math_content; |
| | 200 | + $state['pre'] = $state['pre'] + $pre_content; |
| | 201 | + } else { |
| | 202 | + $state = array( |
| | 203 | + 'nowiki' => $nowiki_content, |
| | 204 | + 'hiero' => $hiero_content, |
| | 205 | + 'math' => $math_content, |
| | 206 | + 'pre' => $pre_content, |
| | 207 | + 'item' => $item_content |
| | 208 | + ); |
| | 209 | + } |
| 224 | 210 | return $text; |
| 225 | 211 | } |
| 226 | 212 | |
| 227 | 213 | function unstrip( $text, &$state ) |
| 228 | 214 | { |
| 229 | 215 | # Must expand in reverse order, otherwise nested tags will be corrupted |
| 230 | | - /* |
| 231 | | - $dicts = array( 'item', 'pre', 'math', 'hiero', 'nowiki' ); |
| 232 | | - foreach ( $dicts as $dictName ) { |
| 233 | | - $content_dict = $state[$dictName]; |
| 234 | | - foreach( $content_dict as $marker => $content ){ |
| 235 | | - $text = str_replace( $marker, $content, $text ); |
| 236 | | - } |
| 237 | | - }*/ |
| 238 | | - |
| 239 | 216 | $contentDict = end( $state ); |
| 240 | 217 | for ( $contentDict = end( $state ); $contentDict !== false; $contentDict = prev( $state ) ) { |
| 241 | 218 | for ( $content = end( $contentDict ); $content !== false; $content = prev( $contentDict ) ) { |
| 242 | 219 | $text = str_replace( key( $contentDict ), $content, $text ); |
| 243 | 220 | } |
| 244 | 221 | } |
| 245 | | - |
| | 222 | + |
| 246 | 223 | return $text; |
| 247 | 224 | } |
| 248 | 225 | |
| — | — | @@ -471,39 +448,47 @@ |
| 472 | 449 | return $t ; |
| 473 | 450 | } |
| 474 | 451 | |
| 475 | | - # Well, OK, it's actually about 14 passes. But since all the |
| 476 | | - # hard lifting is done inside PHP's regex code, it probably |
| 477 | | - # wouldn't speed things up much to add a real parser. |
| 478 | | - # |
| 479 | | - function doWikiPass2( $text, $linestart ) |
| | 452 | + function internalParse( $text, $linestart, $args = array() ) |
| 480 | 453 | { |
| 481 | | - $fname = "Parser::doWikiPass2"; |
| | 454 | + $fname = "Parser::internalParse"; |
| 482 | 455 | wfProfileIn( $fname ); |
| 483 | | - |
| | 456 | + |
| 484 | 457 | $text = $this->removeHTMLtags( $text ); |
| 485 | | - $text = $this->replaceVariables( $text ); |
| | 458 | + $text = $this->replaceVariables( $text, $args ); |
| 486 | 459 | |
| 487 | 460 | # $text = preg_replace( "/(^|\n)-----*/", "\\1<hr>", $text ); |
| 488 | 461 | |
| 489 | 462 | $text = $this->doHeadings( $text ); |
| 490 | | - |
| 491 | 463 | if($this->mOptions->getUseDynamicDates()) { |
| 492 | 464 | global $wgDateFormatter; |
| 493 | 465 | $text = $wgDateFormatter->reformat( $this->mOptions->getDateFormat(), $text ); |
| 494 | 466 | } |
| 495 | | - |
| 496 | 467 | $text = $this->replaceExternalLinks( $text ); |
| 497 | 468 | $text = $this->doTokenizedParser ( $text ); |
| 498 | | - |
| 499 | 469 | $text = $this->doTableStuff ( $text ) ; |
| 500 | | - |
| 501 | 470 | $text = $this->formatHeadings( $text ); |
| 502 | | - |
| 503 | 471 | $sk =& $this->mOptions->getSkin(); |
| 504 | 472 | $text = $sk->transformContent( $text ); |
| 505 | | - |
| | 473 | + |
| | 474 | + $fixtags = array( |
| | 475 | + "/<hr *>/i" => '<hr/>', |
| | 476 | + "/<br *>/i" => '<br/>', |
| | 477 | + "/<center *>/i"=>'<span style="text-align:center;">', |
| | 478 | + "/<\\/center *>/i" => '</span>' |
| | 479 | + ); |
| | 480 | + $text = preg_replace( array_keys($fixtags), array_values($fixtags), $text ); |
| | 481 | + // another round, but without regex |
| | 482 | + $fixtags = array( |
| | 483 | + '& ' => '&', |
| | 484 | + '&<' => '&<', |
| | 485 | + ); |
| | 486 | + $text = str_replace( array_keys($fixtags), array_values($fixtags), $text ); |
| | 487 | + |
| 506 | 488 | $text .= $this->categoryMagic () ; |
| 507 | 489 | |
| | 490 | + # needs to be called last |
| | 491 | + $text = $this->doBlockLevels( $text, $linestart ); |
| | 492 | + |
| 508 | 493 | wfProfileOut( $fname ); |
| 509 | 494 | return $text; |
| 510 | 495 | } |
| — | — | @@ -736,8 +721,7 @@ |
| 737 | 722 | $nextToken = $tokenizer->nextToken(); |
| 738 | 723 | $txt .= $nextToken["text"]; |
| 739 | 724 | } |
| 740 | | - $fakestate = $this->mStripState; |
| 741 | | - $txt = $this->handleInternalLink( $this->unstrip($txt,$fakestate), $prefix ); |
| | 725 | + $txt = $this->handleInternalLink( $this->unstrip($txt,$this->mStripState), $prefix ); |
| 742 | 726 | |
| 743 | 727 | # did the tag start with 3 [ ? |
| 744 | 728 | if($threeopen) { |
| — | — | @@ -1035,12 +1019,14 @@ |
| 1036 | 1020 | # and making lists from lines starting with * # : etc. |
| 1037 | 1021 | # |
| 1038 | 1022 | $a = explode( "\n", $text ); |
| 1039 | | - $lastPref = $text = ''; |
| 1040 | | - $this->mDTopen = $inBlockElem = $pstack = false; |
| 1041 | 1023 | |
| | 1024 | + $lastPref = $text = $lastLine = ''; |
| | 1025 | + $this->mDTopen = $inBlockElem = false; |
| | 1026 | + $npl = 0; |
| | 1027 | + $pstack = false; |
| | 1028 | + |
| 1042 | 1029 | if ( ! $linestart ) { $text .= array_shift( $a ); } |
| 1043 | 1030 | foreach ( $a as $t ) { |
| 1044 | | - |
| 1045 | 1031 | $oLine = $t; |
| 1046 | 1032 | $opl = strlen( $lastPref ); |
| 1047 | 1033 | $npl = strspn( $t, "*#:;" ); |
| — | — | @@ -1054,7 +1040,7 @@ |
| 1055 | 1041 | |
| 1056 | 1042 | if ( ";" == substr( $pref, -1 ) ) { |
| 1057 | 1043 | $cpos = strpos( $t, ":" ); |
| 1058 | | - if ( ! ( false === $cpos ) ) { |
| | 1044 | + if ( false !== $cpos ) { |
| 1059 | 1045 | $term = substr( $t, 0, $cpos ); |
| 1060 | 1046 | $text .= $term . $this->nextItem( ":" ); |
| 1061 | 1047 | $t = substr( $t, $cpos + 1 ); |
| — | — | @@ -1151,6 +1137,7 @@ |
| 1152 | 1138 | $text .= "</" . $this->mLastSection . ">"; |
| 1153 | 1139 | $this->mLastSection = ""; |
| 1154 | 1140 | } |
| | 1141 | + |
| 1155 | 1142 | wfProfileOut( $fname ); |
| 1156 | 1143 | return $text; |
| 1157 | 1144 | } |
| — | — | @@ -1194,10 +1181,9 @@ |
| 1195 | 1182 | } |
| 1196 | 1183 | } |
| 1197 | 1184 | |
| 1198 | | - /* private */ function replaceVariables( $text ) |
| | 1185 | + /* private */ function replaceVariables( $text, $args = array() ) |
| 1199 | 1186 | { |
| 1200 | | - global $wgLang, $wgCurParser; |
| 1201 | | - global $wgScript, $wgArticlePath; |
| | 1187 | + global $wgLang, $wgScript, $wgArticlePath; |
| 1202 | 1188 | |
| 1203 | 1189 | $fname = "Parser::replaceVariables"; |
| 1204 | 1190 | wfProfileIn( $fname ); |
| — | — | @@ -1207,68 +1193,45 @@ |
| 1208 | 1194 | $this->initialiseVariables(); |
| 1209 | 1195 | } |
| 1210 | 1196 | $titleChars = Title::legalChars(); |
| 1211 | | - $regex = "/{{([$titleChars\\|]*?)}}/s"; |
| | 1197 | + $regex = "/(\\n?){{([$titleChars]*?)(\\|.*?|)}}/s"; |
| | 1198 | + |
| | 1199 | + # This function is called recursively. To keep track of arguments we need a stack: |
| | 1200 | + array_push( $this->mArgStack, $args ); |
| 1212 | 1201 | |
| 1213 | | - # "Recursive" variable expansion: run it through a couple of passes |
| 1214 | | - for ( $i=0; $i<MAX_INCLUDE_REPEAT && !$bail; $i++ ) { |
| 1215 | | - $oldText = $text; |
| 1216 | | - |
| 1217 | | - # It's impossible to rebind a global in PHP |
| 1218 | | - # Instead, we run the substitution on a copy, then merge the changed fields back in |
| 1219 | | - $wgCurParser = $this->fork(); |
| 1220 | | - |
| 1221 | | - $text = preg_replace_callback( $regex, "wfBraceSubstitution", $text ); |
| 1222 | | - if ( $oldText == $text ) { |
| 1223 | | - $bail = true; |
| 1224 | | - } |
| 1225 | | - $this->merge( $wgCurParser ); |
| 1226 | | - } |
| 1227 | | - |
| | 1202 | + # PHP global rebinding syntax is a bit weird, need to use the GLOBALS array |
| | 1203 | + $GLOBALS['wgCurParser'] =& $this; |
| | 1204 | + $text = preg_replace_callback( $regex, "wfBraceSubstitution", $text ); |
| | 1205 | + |
| | 1206 | + array_pop( $this->mArgStack ); |
| | 1207 | + |
| 1228 | 1208 | return $text; |
| 1229 | 1209 | } |
| 1230 | 1210 | |
| 1231 | | - # Returns a copy of this object except with various variables cleared |
| 1232 | | - # This copy can be re-merged with the parent after operations on the copy |
| 1233 | | - function fork() |
| 1234 | | - { |
| 1235 | | - $copy = $this; |
| 1236 | | - $copy->mOutput = new ParserOutput; |
| 1237 | | - return $copy; |
| 1238 | | - } |
| 1239 | | - |
| 1240 | | - # Merges a copy split off with fork() |
| 1241 | | - function merge( &$copy ) |
| 1242 | | - { |
| 1243 | | - # Output objects |
| 1244 | | - $this->mOutput->merge( $copy->mOutput ); |
| 1245 | | - |
| 1246 | | - # Include throttling arrays |
| 1247 | | - foreach( $copy->mIncludeCount as $dbk => $count ) { |
| 1248 | | - if ( array_key_exists( $dbk, $this->mIncludeCount ) ) { |
| 1249 | | - $this->mIncludeCount[$dbk] += $count; |
| 1250 | | - } else { |
| 1251 | | - $this->mIncludeCount[$dbk] = $count; |
| 1252 | | - } |
| 1253 | | - } |
| 1254 | | - |
| 1255 | | - # Strip states |
| 1256 | | - foreach( $copy->mStripState as $dictName => $contentDict ) { |
| 1257 | | - $this->mStripState[$dictName] += $contentDict; |
| 1258 | | - } |
| 1259 | | - } |
| 1260 | | - |
| 1261 | 1211 | function braceSubstitution( $matches ) |
| 1262 | 1212 | { |
| 1263 | 1213 | global $wgLinkCache, $wgLang; |
| 1264 | 1214 | $fname = "Parser::braceSubstitution"; |
| 1265 | 1215 | $found = false; |
| 1266 | 1216 | $nowiki = false; |
| 1267 | | - |
| 1268 | | - $text = $matches[1]; |
| | 1217 | + $title = NULL; |
| | 1218 | + |
| | 1219 | + # $newline is an optional newline character before the braces |
| | 1220 | + # $part1 is the bit before the first |, and must contain only title characters |
| | 1221 | + # $args is a list of arguments, starting from index 0, not including $part1 |
| | 1222 | + |
| | 1223 | + $newline = $matches[1]; |
| | 1224 | + $part1 = $matches[2]; |
| | 1225 | + # If the third subpattern matched anything, it will start with | |
| | 1226 | + if ( $matches[3] !== "" ) { |
| | 1227 | + $args = explode( "|", substr( $matches[3], 1 ) ); |
| | 1228 | + } else { |
| | 1229 | + $args = array(); |
| | 1230 | + } |
| | 1231 | + $argc = count( $args ); |
| 1269 | 1232 | |
| 1270 | 1233 | # SUBST |
| 1271 | 1234 | $mwSubst =& MagicWord::get( MAG_SUBST ); |
| 1272 | | - if ( $mwSubst->matchStartAndRemove( $text ) ) { |
| | 1235 | + if ( $mwSubst->matchStartAndRemove( $part1 ) ) { |
| 1273 | 1236 | if ( $this->mOutputType != OT_WIKI ) { |
| 1274 | 1237 | # Invalid SUBST not replaced at PST time |
| 1275 | 1238 | # Return without further processing |
| — | — | @@ -1285,19 +1248,21 @@ |
| 1286 | 1249 | if ( !$found ) { |
| 1287 | 1250 | # Check for MSGNW: |
| 1288 | 1251 | $mwMsgnw =& MagicWord::get( MAG_MSGNW ); |
| 1289 | | - if ( $mwMsgnw->matchStartAndRemove( $text ) ) { |
| | 1252 | + if ( $mwMsgnw->matchStartAndRemove( $part1 ) ) { |
| 1290 | 1253 | $nowiki = true; |
| 1291 | 1254 | } else { |
| 1292 | 1255 | # Remove obsolete MSG: |
| 1293 | 1256 | $mwMsg =& MagicWord::get( MAG_MSG ); |
| 1294 | | - $mwMsg->matchStartAndRemove( $text ); |
| | 1257 | + $mwMsg->matchStartAndRemove( $part1 ); |
| 1295 | 1258 | } |
| 1296 | 1259 | |
| 1297 | 1260 | # Check if it is an internal message |
| 1298 | 1261 | $mwInt =& MagicWord::get( MAG_INT ); |
| 1299 | | - if ( $mwInt->matchStartAndRemove( $text ) ) { |
| 1300 | | - $text = wfMsg( $text ); |
| 1301 | | - $found = true; |
| | 1262 | + if ( $mwInt->matchStartAndRemove( $part1 ) ) { |
| | 1263 | + if ( $this->incrementIncludeCount( "int:$part1" ) ) { |
| | 1264 | + $text = wfMsgReal( $part1, $args, true ); |
| | 1265 | + $found = true; |
| | 1266 | + } |
| 1302 | 1267 | } |
| 1303 | 1268 | } |
| 1304 | 1269 | |
| — | — | @@ -1305,12 +1270,12 @@ |
| 1306 | 1271 | if ( !$found ) { |
| 1307 | 1272 | # Check for NS: (namespace expansion) |
| 1308 | 1273 | $mwNs = MagicWord::get( MAG_NS ); |
| 1309 | | - if ( $mwNs->matchStartAndRemove( $text ) ) { |
| 1310 | | - if ( intval( $text ) ) { |
| 1311 | | - $text = $wgLang->getNsText( intval( $text ) ); |
| | 1274 | + if ( $mwNs->matchStartAndRemove( $part1 ) ) { |
| | 1275 | + if ( intval( $part1 ) ) { |
| | 1276 | + $text = $wgLang->getNsText( intval( $part1 ) ); |
| 1312 | 1277 | $found = true; |
| 1313 | 1278 | } else { |
| 1314 | | - $index = Namespace::getCanonicalIndex( strtolower( $text ) ); |
| | 1279 | + $index = Namespace::getCanonicalIndex( strtolower( $part1 ) ); |
| 1315 | 1280 | if ( !is_null( $index ) ) { |
| 1316 | 1281 | $text = $wgLang->getNsText( $index ); |
| 1317 | 1282 | $found = true; |
| — | — | @@ -1324,78 +1289,54 @@ |
| 1325 | 1290 | $mwLocal = MagicWord::get( MAG_LOCALURL ); |
| 1326 | 1291 | $mwLocalE = MagicWord::get( MAG_LOCALURLE ); |
| 1327 | 1292 | |
| 1328 | | - if ( $mwLocal->matchStartAndRemove( $text ) ) { |
| | 1293 | + if ( $mwLocal->matchStartAndRemove( $part1 ) ) { |
| 1329 | 1294 | $func = 'getLocalURL'; |
| 1330 | | - } elseif ( $mwLocalE->matchStartAndRemove( $text ) ) { |
| | 1295 | + } elseif ( $mwLocalE->matchStartAndRemove( $part1 ) ) { |
| 1331 | 1296 | $func = 'escapeLocalURL'; |
| 1332 | 1297 | } else { |
| 1333 | 1298 | $func = ''; |
| 1334 | 1299 | } |
| 1335 | 1300 | |
| 1336 | 1301 | if ( $func !== '' ) { |
| 1337 | | - $args = explode( "|", $text ); |
| 1338 | | - $n = count( $args ); |
| 1339 | | - if ( $n > 0 ) { |
| 1340 | | - $title = Title::newFromText( $args[0] ); |
| 1341 | | - if ( !is_null( $title ) ) { |
| 1342 | | - if ( $n > 1 ) { |
| 1343 | | - $text = $title->$func( $args[1] ); |
| 1344 | | - } else { |
| 1345 | | - $text = $title->$func(); |
| 1346 | | - } |
| 1347 | | - $found = true; |
| | 1302 | + $title = Title::newFromText( $part1 ); |
| | 1303 | + if ( !is_null( $title ) ) { |
| | 1304 | + if ( $argc > 0 ) { |
| | 1305 | + $text = $title->$func( $args[0] ); |
| | 1306 | + } else { |
| | 1307 | + $text = $title->$func(); |
| 1348 | 1308 | } |
| | 1309 | + $found = true; |
| 1349 | 1310 | } |
| 1350 | | - } |
| | 1311 | + } |
| 1351 | 1312 | } |
| 1352 | 1313 | |
| 1353 | | - # Check for a match against internal variables |
| 1354 | | - if ( !$found && array_key_exists( $text, $this->mVariables ) ) { |
| 1355 | | - $text = $this->mVariables[$text]; |
| | 1314 | + # Internal variables |
| | 1315 | + if ( !$found && array_key_exists( $part1, $this->mVariables ) ) { |
| | 1316 | + $text = $this->mVariables[$part1]; |
| 1356 | 1317 | $found = true; |
| 1357 | 1318 | $this->mOutput->mContainsOldMagic = true; |
| 1358 | 1319 | } |
| 1359 | 1320 | |
| | 1321 | + # Arguments input from the caller |
| | 1322 | + $inputArgs = end( $this->mArgStack ); |
| | 1323 | + if ( !$found && array_key_exists( $part1, $inputArgs ) ) { |
| | 1324 | + $text = $inputArgs[$part1]; |
| | 1325 | + $found = true; |
| | 1326 | + } |
| | 1327 | + |
| 1360 | 1328 | # Load from database |
| 1361 | 1329 | if ( !$found ) { |
| 1362 | | - $title = Title::newFromText( $text, NS_TEMPLATE ); |
| 1363 | | - if ( is_object( $title ) && !$title->isExternal() ) { |
| | 1330 | + $title = Title::newFromText( $part1, NS_TEMPLATE ); |
| | 1331 | + if ( !is_null( $title ) && !$title->isExternal() ) { |
| 1364 | 1332 | # Check for excessive inclusion |
| 1365 | 1333 | $dbk = $title->getPrefixedDBkey(); |
| 1366 | | - if ( !array_key_exists( $dbk, $this->mIncludeCount ) ) { |
| 1367 | | - $this->mIncludeCount[$dbk] = 0; |
| 1368 | | - } |
| 1369 | | - if ( ++$this->mIncludeCount[$dbk] <= MAX_INCLUDE_REPEAT ) { |
| | 1334 | + if ( $this->incrementIncludeCount( $dbk ) ) { |
| 1370 | 1335 | $article = new Article( $title ); |
| 1371 | 1336 | $articleContent = $article->getContentWithoutUsingSoManyDamnGlobals(); |
| 1372 | 1337 | if ( $articleContent !== false ) { |
| 1373 | 1338 | $found = true; |
| 1374 | 1339 | $text = $articleContent; |
| 1375 | 1340 | |
| 1376 | | - # Escaping and link table handling |
| 1377 | | - # Not required for preSaveTransform() |
| 1378 | | - if ( $this->mOutputType == OT_HTML ) { |
| 1379 | | - if ( $nowiki ) { |
| 1380 | | - $text = wfEscapeWikiText( $text ); |
| 1381 | | - } else { |
| 1382 | | - $text = $this->removeHTMLtags( $text ); |
| 1383 | | - } |
| 1384 | | - # Do not enter included links in link table |
| 1385 | | - $wgLinkCache->suspend(); |
| 1386 | | - |
| 1387 | | - # Run full parser on the included text |
| 1388 | | - $text = $this->strip( $text, $this->mStripState ); |
| 1389 | | - $text = $this->doWikiPass2( $text, true ); |
| 1390 | | - |
| 1391 | | - # Add the result to the strip state for re-inclusion after |
| 1392 | | - # the rest of the processing |
| 1393 | | - $text = $this->insertStripItem( $text, $this->mStripState ); |
| 1394 | | - |
| 1395 | | - # Resume the link cache and register the inclusion as a link |
| 1396 | | - $wgLinkCache->resume(); |
| 1397 | | - $wgLinkCache->addLinkObj( $title ); |
| 1398 | | - |
| 1399 | | - } |
| 1400 | 1341 | } |
| 1401 | 1342 | } |
| 1402 | 1343 | |
| — | — | @@ -1406,14 +1347,72 @@ |
| 1407 | 1348 | } |
| 1408 | 1349 | } |
| 1409 | 1350 | } |
| | 1351 | + |
| | 1352 | + # Recursive parsing, escaping and link table handling |
| | 1353 | + # Only for HTML output |
| | 1354 | + if ( $nowiki && $found && $this->mOutputType == OT_HTML ) { |
| | 1355 | + $text = wfEscapeWikiText( $text ); |
| | 1356 | + } elseif ( $this->mOutputType == OT_HTML && $found ) { |
| | 1357 | + # Clean up argument array |
| | 1358 | + $assocArgs = array(); |
| | 1359 | + $index = 1; |
| | 1360 | + foreach( $args as $arg ) { |
| | 1361 | + $eqpos = strpos( $arg, "=" ); |
| | 1362 | + if ( $eqpos === false ) { |
| | 1363 | + $assocArgs[$index++] = $arg; |
| | 1364 | + } else { |
| | 1365 | + $name = trim( substr( $arg, 0, $eqpos ) ); |
| | 1366 | + $value = trim( substr( $arg, $eqpos+1 ) ); |
| | 1367 | + if ( $value === false ) { |
| | 1368 | + $value = ""; |
| | 1369 | + } |
| | 1370 | + if ( $name !== false ) { |
| | 1371 | + $assocArgs[$name] = $value; |
| | 1372 | + } |
| | 1373 | + } |
| | 1374 | + } |
| 1410 | 1375 | |
| | 1376 | + # Do not enter included links in link table |
| | 1377 | + if ( !is_null( $title ) ) { |
| | 1378 | + $wgLinkCache->suspend(); |
| | 1379 | + } |
| | 1380 | + |
| | 1381 | + # Run full parser on the included text |
| | 1382 | + $text = $this->strip( $text, $this->mStripState ); |
| | 1383 | + $text = $this->internalParse( $text, (bool)$newline, $assocArgs ); |
| | 1384 | + |
| | 1385 | + # Add the result to the strip state for re-inclusion after |
| | 1386 | + # the rest of the processing |
| | 1387 | + $text = $this->insertStripItem( $text, $this->mStripState ); |
| | 1388 | + |
| | 1389 | + # Resume the link cache and register the inclusion as a link |
| | 1390 | + if ( !is_null( $title ) ) { |
| | 1391 | + $wgLinkCache->resume(); |
| | 1392 | + $wgLinkCache->addLinkObj( $title ); |
| | 1393 | + } |
| | 1394 | + } |
| | 1395 | + |
| 1411 | 1396 | if ( !$found ) { |
| 1412 | 1397 | return $matches[0]; |
| 1413 | 1398 | } else { |
| 1414 | | - return $text; |
| | 1399 | + return $newline . $text; |
| 1415 | 1400 | } |
| 1416 | 1401 | } |
| 1417 | 1402 | |
| | 1403 | + # Returns true if the function is allowed to include this entity |
| | 1404 | + function incrementIncludeCount( $dbk ) |
| | 1405 | + { |
| | 1406 | + if ( !array_key_exists( $dbk, $this->mIncludeCount ) ) { |
| | 1407 | + $this->mIncludeCount[$dbk] = 0; |
| | 1408 | + } |
| | 1409 | + if ( ++$this->mIncludeCount[$dbk] <= MAX_INCLUDE_REPEAT ) { |
| | 1410 | + return true; |
| | 1411 | + } else { |
| | 1412 | + return false; |
| | 1413 | + } |
| | 1414 | + } |
| | 1415 | + |
| | 1416 | + |
| 1418 | 1417 | # Cleans up HTML, removes dangerous tags and attributes |
| 1419 | 1418 | /* private */ function removeHTMLtags( $text ) |
| 1420 | 1419 | { |
| — | — | @@ -1617,7 +1616,7 @@ |
| 1618 | 1617 | |
| 1619 | 1618 | # The canonized header is a version of the header text safe to use for links |
| 1620 | 1619 | # Avoid insertion of weird stuff like <math> by expanding the relevant sections |
| 1621 | | - $canonized_headline = Parser::unstrip( $headline, $this->mStripState ); |
| | 1620 | + $canonized_headline = $this->unstrip( $headline, $this->mStripState ); |
| 1622 | 1621 | |
| 1623 | 1622 | # strip out HTML |
| 1624 | 1623 | $canonized_headline = preg_replace( "/<.*?" . ">/","",$canonized_headline ); |
| — | — | @@ -1682,7 +1681,10 @@ |
| 1683 | 1682 | if( $showEditLink && $headlineCount > 0 && $i == 0 && $block != "\n" ) { |
| 1684 | 1683 | # This is the [edit] link that appears for the top block of text when |
| 1685 | 1684 | # section editing is enabled |
| 1686 | | - $full .= $sk->editSectionLink(0); |
| | 1685 | + |
| | 1686 | + # Disabled because it broke block formatting |
| | 1687 | + # For example, a bullet point in the top line |
| | 1688 | + # $full .= $sk->editSectionLink(0); |
| 1687 | 1689 | } |
| 1688 | 1690 | $full .= $block; |
| 1689 | 1691 | if( $doShowToc && !$i) { |