Index: trunk/phase3/includes/parser/Parser_LinkHooks.php |
— | — | @@ -181,4 +181,201 @@ |
182 | 182 | 'deps' => $deps ); |
183 | 183 | } |
184 | 184 | |
| 185 | + /** |
| 186 | + * Process [[ ]] wikilinks |
| 187 | + * @return LinkHolderArray |
| 188 | + * |
| 189 | + * @private |
| 190 | + */ |
| 191 | + function replaceInternalLinks2( &$s ) { |
| 192 | + global $wgContLang; |
| 193 | + |
| 194 | + wfProfileIn( __METHOD__ ); |
| 195 | + |
| 196 | + wfProfileIn( __METHOD__.'-setup' ); |
| 197 | + static $tc = FALSE, $titleRegex;//$e1, $e1_img; |
| 198 | + if( !$tc ) { |
| 199 | + # the % is needed to support urlencoded titles as well |
| 200 | + $tc = Title::legalChars() . '#%'; |
| 201 | + # Match a link having the form [[namespace:link|alternate]]trail |
| 202 | + //$e1 = "/^([{$tc}]+)(?:\\|(.+?))?]](.*)\$/sD"; |
| 203 | + # Match cases where there is no "]]", which might still be images |
| 204 | + //$e1_img = "/^([{$tc}]+)\\|(.*)\$/sD"; |
| 205 | + # Match a valid plain title |
| 206 | + $titleRegex = "/^([{$tc}]+)$/sD"; |
| 207 | + } |
| 208 | + |
| 209 | + $sk = $this->mOptions->getSkin(); |
| 210 | + $holders = new LinkHolderArray( $this ); |
| 211 | + |
| 212 | + if( is_null( $this->mTitle ) ) { |
| 213 | + wfProfileOut( __METHOD__ ); |
| 214 | + wfProfileOut( __METHOD__.'-setup' ); |
| 215 | + throw new MWException( __METHOD__.": \$this->mTitle is null\n" ); |
| 216 | + } |
| 217 | + $nottalk = !$this->mTitle->isTalkPage(); |
| 218 | + |
| 219 | + if($wgContLang->hasVariants()) { |
| 220 | + $selflink = $wgContLang->convertLinkToAllVariants($this->mTitle->getPrefixedText()); |
| 221 | + } else { |
| 222 | + $selflink = array($this->mTitle->getPrefixedText()); |
| 223 | + } |
| 224 | + wfProfileOut( __METHOD__.'-setup' ); |
| 225 | + |
| 226 | + $offset = 0; |
| 227 | + $offsetStack = array(); |
| 228 | + $markerReplacer = new LinkMarkerReplacer( array( &$this, 'replaceInternalLinksCallback' ) ); |
| 229 | + $markerReplacer->holders( $holders ); |
| 230 | + while( true ) { |
| 231 | + $startBracketOffset = strpos( $s, '[[', $offset ); |
| 232 | + $endBracketOffset = strpos( $s, ']]', $offset ); |
| 233 | + # Finish when there are no more brackets |
| 234 | + if( $startBracketOffset === false && $endBracketOffset === false ) break; |
| 235 | + # Determine if the bracket is a starting or ending bracket |
| 236 | + # When we find both, use the first one |
| 237 | + elseif( $startBracketOffset !== false && $endBracketOffset !== false ) |
| 238 | + $isStart = $startBracketOffset <= $endBracketOffset; |
| 239 | + # When we only found one, check which it is |
| 240 | + else $isStart = $startBracketOffset !== false; |
| 241 | + $bracketOffset = $isStart ? $startBracketOffset : $endBracketOffset; |
| 242 | + if( $isStart ) { |
| 243 | + /** Opening bracket **/ |
| 244 | + # Just push our current offset in the string onto the stack |
| 245 | + $offsetStack[] = $startBracketOffset; |
| 246 | + } else { |
| 247 | + /** Closing bracket **/ |
| 248 | + # Pop the start pos for our current link zone off the stack |
| 249 | + $startBracketOffset = array_pop($offsetStack); |
| 250 | + # Just to clean up the code, lets place offsets on the outer ends |
| 251 | + $endBracketOffset += 2; |
| 252 | + |
| 253 | + # Only do logic if we actually have a opening bracket for this |
| 254 | + if( isset($startBracketOffset) ) { |
| 255 | + # Extract text inside the link |
| 256 | + @list( $titleText, $paramText ) = explode('|', |
| 257 | + substr($s, $startBracketOffset+2, $endBracketOffset-$startBracketOffset-4), 2); |
| 258 | + # Create markers only for valid links |
| 259 | + if( preg_match( $titleRegex, $titleText ) ) { |
| 260 | + # Store the text for the marker |
| 261 | + $marker = $markerReplacer->addMarker($titleText, $paramText); |
| 262 | + # Replace the current link with the marker |
| 263 | + $s = substr($s,0,$startBracketOffset). |
| 264 | + $marker. |
| 265 | + substr($s, $endBracketOffset); |
| 266 | + # We have modified $s, because of this we need to set the |
| 267 | + # offset manually since the end position is different now |
| 268 | + $offset = $startBracketOffset+strlen($marker); |
| 269 | + continue; |
| 270 | + } |
| 271 | + # ToDo: Some LinkHooks may allow recursive links inside of |
| 272 | + # the link text, create a regex that also matches our |
| 273 | + # <!-- LINKMARKER ### --> sequence in titles |
| 274 | + # ToDO: Some LinkHooks use patterns rather than namespaces |
| 275 | + # these need to be tested at this point here |
| 276 | + } |
| 277 | + |
| 278 | + } |
| 279 | + # Bump our offset to after our current bracket |
| 280 | + $offset = $bracketOffset+2; |
| 281 | + } |
| 282 | + |
| 283 | + |
| 284 | + # Now expand our tree |
| 285 | + wfProfileIn( __METHOD__.'-expand' ); |
| 286 | + $s = $markerReplacer->expand( $s ); |
| 287 | + wfProfileOut( __METHOD__.'-expand' ); |
| 288 | + |
| 289 | + wfProfileOut( __METHOD__ ); |
| 290 | + return $holders; |
| 291 | + } |
| 292 | + |
| 293 | + function replaceInternalLinksCallback( $markerReplacer, $titleText, $paramText ) { |
| 294 | + wfProfileIn( __METHOD__ ); |
| 295 | + $wt = isset($paramText) ? "[[$titleText|$paramText]]" : "[[$titleText]]"; |
| 296 | + wfProfileIn( __METHOD__."-misc" ); |
| 297 | + # Don't allow internal links to pages containing |
| 298 | + # PROTO: where PROTO is a valid URL protocol; these |
| 299 | + # should be external links. |
| 300 | + if( preg_match('/^\b(?:' . wfUrlProtocols() . ')/', $titleText) ) { |
| 301 | + wfProfileOut( __METHOD__ ); |
| 302 | + return $wt; |
| 303 | + } |
| 304 | + |
| 305 | + # Make subpage if necessary |
| 306 | + if( $this->areSubpagesAllowed() ) { |
| 307 | + $titleText = $this->maybeDoSubpageLink( $titleText, $paramText ); |
| 308 | + } |
| 309 | + |
| 310 | + # Check for a leading colon and strip it if it is there |
| 311 | + $leadingColon = $titleText[0] == ':'; |
| 312 | + if( $leadingColon ) $titleText = substr( $titleText, 1 ); |
| 313 | + |
| 314 | + wfProfileOut( __METHOD__."-misc" ); |
| 315 | + # Make title object |
| 316 | + wfProfileIn( __METHOD__."-title" ); |
| 317 | + $title = Title::newFromText( $this->mStripState->unstripNoWiki($titleText) ); |
| 318 | + if( !$title ) { |
| 319 | + wfProfileOut( __METHOD__."-title" ); |
| 320 | + wfProfileOut( __METHOD__ ); |
| 321 | + return $wt; |
| 322 | + } |
| 323 | + $ns = $title->getNamespace(); |
| 324 | + wfProfileOut( __METHOD__."-title" ); |
| 325 | + |
| 326 | + $callback = array( 'CoreLinkFunctions', 'defaultLinkHook' ); |
| 327 | + $args = array( $markerReplacer, $title, $titleText, &$paramText, &$leadingColon ); |
| 328 | + $return = call_user_func_array( $callback, $args ); |
| 329 | + if( $return === false ) { |
| 330 | + # False (no link) was returned, output plain wikitext |
| 331 | + # Build it again as the hook is allowed to modify $paramText |
| 332 | + return isset($paramText) ? "[[$titleText|$paramText]]" : "[[$titleText]]"; |
| 333 | + } elseif( $return === true ) { |
| 334 | + # True (treat as plain link) was returned, call the defaultLinkHook |
| 335 | + $args = array( $markerReplacer, $title, $titleText, &$paramText, &$leadingColon ); |
| 336 | + $return = call_user_func_array( array( &$this, 'defaultLinkHook' ), $args ); |
| 337 | + } |
| 338 | + # Content was returned, return it |
| 339 | + return $return; |
| 340 | + } |
| 341 | + |
185 | 342 | } |
| 343 | + |
| 344 | +class LinkMarkerReplacer { |
| 345 | + |
| 346 | + protected $markers, $nextId, $holders; |
| 347 | + |
| 348 | + function __construct( $callback ) { |
| 349 | + $this->nextId = 0; |
| 350 | + $this->markers = array(); |
| 351 | + $this->callback = $callback; |
| 352 | + $this->holders = null; |
| 353 | + } |
| 354 | + |
| 355 | + # Note: This is a bit of an ugly way to do this. It works for now, but before |
| 356 | + # this feature becomes usable we should come up with a better arg list. |
| 357 | + # $parser, $holders, and $linkMarkers appear to be 3 needed ones |
| 358 | + function holders( $holders = null ) { return wfSetVar( $this->holders, $holders ); } |
| 359 | + |
| 360 | + function addMarker($titleText, $paramText) { |
| 361 | + $id = $this->nextId++; |
| 362 | + $this->markers[$id] = array( $titleText, $paramText ); |
| 363 | + return "<!-- LINKMARKER $id -->"; |
| 364 | + } |
| 365 | + |
| 366 | + function findMarker( $string ) { |
| 367 | + return (bool) preg_match('/<!-- LINKMARKER [0-9]+ -->/', $string ); |
| 368 | + } |
| 369 | + |
| 370 | + function expand( $string ) { |
| 371 | + return StringUtils::delimiterReplaceCallback( "<!-- LINKMARKER ", " -->", array( &$this, 'callback' ), $string ); |
| 372 | + } |
| 373 | + |
| 374 | + function callback( $m ) { |
| 375 | + $id = intval($m[1]); |
| 376 | + if( !array_key_exists($id, $this->markers) ) return $m[0]; |
| 377 | + $args = $this->markers[$id]; |
| 378 | + array_unshift( $args, $this ); |
| 379 | + return call_user_func_array( $this->callback, $args ); |
| 380 | + } |
| 381 | + |
| 382 | +} |
Index: trunk/phase3/includes/parser/CoreLinkFunctions.php |
— | — | @@ -0,0 +1,27 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +/** |
| 5 | + * Various core link functions, registered in Parser::firstCallInit() |
| 6 | + * @ingroup Parser |
| 7 | + */ |
| 8 | +class CoreLinkFunctions { |
| 9 | + static static function register( $parser ) { |
| 10 | + |
| 11 | + |
| 12 | + } |
| 13 | + |
| 14 | + static function defaultLinkHook( $markers, Title $title, $titleText, &$displayText = null, &$leadingColon = false ) { |
| 15 | + # Warning: This hook should NEVER return true as it is the fallback |
| 16 | + # default for when other hooks return true |
| 17 | + if( $markers->findMarker( $displayText ) ) { |
| 18 | + # There are links inside of the displayText |
| 19 | + # For backwards compatibility the deepest links are dominant so this |
| 20 | + # link should not be handled |
| 21 | + $displayText = $markers->expand($displayText); |
| 22 | + # Return false so that this link is reverted back to WikiText |
| 23 | + return false; |
| 24 | + } |
| 25 | + return $markers->holders()->makeHolder( $title, isset($displayText) ? $displayText : $titleText, '', '', '' ); |
| 26 | + } |
| 27 | + |
| 28 | +} |
Property changes on: trunk/phase3/includes/parser/CoreLinkFunctions.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 29 | + native |
Index: trunk/phase3/includes/AutoLoader.php |
— | — | @@ -356,9 +356,11 @@ |
357 | 357 | 'UtfNormal' => 'includes/normal/UtfNormal.php', |
358 | 358 | |
359 | 359 | # includes/parser |
| 360 | + 'CoreLinkFunctions' => 'includes/parser/CoreLinkFunctions.php', |
360 | 361 | 'CoreParserFunctions' => 'includes/parser/CoreParserFunctions.php', |
361 | 362 | 'DateFormatter' => 'includes/parser/DateFormatter.php', |
362 | 363 | 'LinkHolderArray' => 'includes/parser/LinkHolderArray.php', |
| 364 | + 'LinkMarkerReplacer' => 'includes/parser/LinkMarkerReplacer.php', |
363 | 365 | 'OnlyIncludeReplacer' => 'includes/parser/Parser.php', |
364 | 366 | 'PPDAccum_Hash' => 'includes/parser/Preprocessor_Hash.php', |
365 | 367 | 'PPDPart' => 'includes/parser/Preprocessor_DOM.php', |