ported fix for bug 2257 here
[lhc/web/wiklou.git] / includes / Parser.php
index f7d32f5..7736b04 100644 (file)
@@ -99,7 +99,7 @@ class Parser
        var $mTagHooks;
 
        # Cleared with clearState():
-       var $mOutput, $mAutonumber, $mDTopen, $mStripState = array();
+       var $mOutput, $mAutonumber, $mDTopen, $mStripState = array(), $mCurrentParams = array();
        var $mVariables, $mIncludeCount, $mArgStack, $mLastSection, $mInPre;
        var $mInterwikiLinkHolders, $mLinkHolders;
 
@@ -120,7 +120,6 @@ class Parser
         * @access public
         */
        function Parser() {
-               global $wgContLang;
                $this->mTemplates = array();
                $this->mTemplatePath = array();
                $this->mTagHooks = array();
@@ -142,6 +141,7 @@ class Parser
                $this->mStripState = array();
                $this->mArgStack = array();
                $this->mInPre = false;
+               $this->mCurrentParams = array();
                $this->mInterwikiLinkHolders = array(
                        'texts' => array(),
                        'titles' => array()
@@ -221,7 +221,7 @@ class Parser
                wfRunHooks( 'ParserBeforeTidy', array( &$this, &$text ) );
 
                $text = Sanitizer::normalizeCharReferences( $text );
-               global $wgUseTidy;
+               
                if ($wgUseTidy) {
                        $text = Parser::tidy($text);
                }
@@ -377,16 +377,14 @@ class Parser
                }
 
                # math
-               $text = Parser::extractTags('math', $text, $math_content, $uniq_prefix);
-               foreach( $math_content as $marker => $content ){
-                       if( $render ) {
-                               if( $this->mOptions->getUseTeX() ) {
+               if( $this->mOptions->getUseTeX() ) {
+                       $text = Parser::extractTags('math', $text, $math_content, $uniq_prefix);
+                       foreach( $math_content as $marker => $content ){
+                               if( $render ) {
                                        $math_content[$marker] = renderMath( $content );
                                } else {
-                                       $math_content[$marker] = '<math>'.$content.'<math>';
+                                       $math_content[$marker] = '<math>'.$content.'</math>';
                                }
-                       } else {
-                               $math_content[$marker] = '<math>'.$content.'</math>';
                        }
                }
 
@@ -425,10 +423,11 @@ class Parser
                        $text = Parser::extractTagsAndParams( $tag, $text, $ext_content[$tag],
                                $ext_tags[$tag], $ext_params[$tag], $uniq_prefix );
                        foreach( $ext_content[$tag] as $marker => $content ) {
+                               $content = $this->replaceVariables( $content, $this->mCurrentParams );
                                $full_tag = $ext_tags[$tag][$marker];
                                $params = $ext_params[$tag][$marker];
                                if ( $render ) {
-                                       $ext_content[$tag][$marker] = $callback( $content, $params );
+                                       $ext_content[$tag][$marker] = $callback( $content, $params, $this );
                                } else {
                                        $ext_content[$tag][$marker] = "$full_tag$content</$tag>";
                                }
@@ -469,6 +468,10 @@ class Parser
         * @access private
         */
        function unstrip( $text, &$state ) {
+               if ( !is_array( $state ) ) {
+                       return $text;
+               }
+               
                # Must expand in reverse order, otherwise nested tags will be corrupted
                $contentDict = end( $state );
                for ( $contentDict = end( $state ); $contentDict !== false; $contentDict = prev( $state ) ) {
@@ -488,6 +491,10 @@ class Parser
         * @access private
         */
        function unstripNoWiki( $text, &$state ) {
+               if ( !is_array( $state ) ) {
+                       return $text;
+               }
+
                # Must expand in reverse order, otherwise nested tags will be corrupted
                for ( $content = end($state['nowiki']); $content !== false; $content = prev( $state['nowiki'] ) ) {
                        $text = str_replace( key( $state['nowiki'] ), $content, $text );
@@ -650,8 +657,11 @@ class Parser
                        $fc = substr ( $x , 0 , 1 ) ;
                        if ( preg_match( '/^(:*)\{\|(.*)$/', $x, $matches ) ) {
                                $indent_level = strlen( $matches[1] );
+                               
+                               $attributes = $this->unstripForHTML( $matches[2] );
+
                                $t[$k] = str_repeat( '<dl><dd>', $indent_level ) .
-                                       '<table' . Sanitizer::fixTagAttributes ( $matches[2], 'table' ) . '>' ;
+                                       '<table' . Sanitizer::fixTagAttributes ( $attributes, 'table' ) . '>' ;
                                array_push ( $td , false ) ;
                                array_push ( $ltd , '' ) ;
                                array_push ( $tr , false ) ;
@@ -678,7 +688,8 @@ class Parser
                                array_push ( $tr , false ) ;
                                array_push ( $td , false ) ;
                                array_push ( $ltd , '' ) ;
-                               array_push ( $ltr , Sanitizer::fixTagAttributes ( $x, 'tr' ) ) ;
+                               $attributes = $this->unstripForHTML( $x );
+                               array_push ( $ltr , Sanitizer::fixTagAttributes ( $attributes, 'tr' ) ) ;
                        }
                        else if ( '|' == $fc || '!' == $fc || '|+' == substr ( $x , 0 , 2 ) ) { # Caption
                                # $x is a table row
@@ -720,7 +731,10 @@ class Parser
                                        }
                                        if ( count ( $y ) == 1 )
                                                $y = "{$z}<{$l}>{$y[0]}" ;
-                                       else $y = $y = "{$z}<{$l}".Sanitizer::fixTagAttributes($y[0], $l).">{$y[1]}" ;
+                                       else {
+                                               $attributes = $this->unstripForHTML( $y[0] );
+                                               $y = "{$z}<{$l}".Sanitizer::fixTagAttributes($attributes, $l).">{$y[1]}" ;
+                                       }
                                        $t[$k] .= $y ;
                                        array_push ( $td , true ) ;
                                }
@@ -753,7 +767,11 @@ class Parser
                $fname = 'Parser::internalParse';
                wfProfileIn( $fname );
 
-               $text = Sanitizer::removeHTMLtags( $text, array( &$this, 'replaceVariables' ) );
+               # Remove <noinclude> tags and <includeonly> sections
+               $text = strtr( $text, array( '<noinclude>' => '', '</noinclude>' => '') );
+               $text = preg_replace( '/<includeonly>.*?<\/includeonly>/s', '', $text );
+
+               $text = Sanitizer::removeHTMLtags( $text, array( &$this, 'attributeStripCallback' ) );
                $text = $this->replaceVariables( $text, $args );
 
                $text = preg_replace( '/(^|\n)-----*/', '\\1<hr />', $text );
@@ -769,7 +787,7 @@ class Parser
 
                # replaceInternalLinks may sometimes leave behind
                # absolute URLs, which have to be masked to hide them from replaceExternalLinks
-               $text = str_replace("http-noparse://","http://",$text);
+               $text = str_replace(UNIQ_PREFIX."NOPARSE", "", $text);
 
                $text = $this->doMagicLinks( $text );
                $text = $this->doTableStuff( $text );
@@ -808,7 +826,7 @@ class Parser
        function doExponent( $text ) {
                $fname = 'Parser::doExponent';
                wfProfileIn( $fname );
-               $text = preg_replace('/\^\^(.*)\^\^/','<small><sup>\\1</sup></small>', $text);
+               $text = preg_replace('/\^\^(.*?)\^\^/','<small><sup>\\1</sup></small>', $text);
                wfProfileOut( $fname );
                return $text;
        }
@@ -1225,7 +1243,6 @@ class Parser
                if ( $useLinkPrefixExtension ) {
                        if ( preg_match( $e2, $s, $m ) ) {
                                $first_prefix = $m[2];
-                               $s = $m[1];
                        } else {
                                $first_prefix = false;
                        }
@@ -1308,7 +1325,7 @@ class Parser
                                $link = substr($link, 1);
                        }
 
-                       $nt =& Title::newFromText( $this->unstripNoWiki($link, $this->mStripState) );
+                       $nt = Title::newFromText( $this->unstripNoWiki($link, $this->mStripState) );
                        if( !$nt ) {
                                $s .= $prefix . '[[' . $line;
                                continue;
@@ -1329,7 +1346,8 @@ class Parser
                                        $found = false;
                                        while (isset ($a[$k+1]) ) {
                                                #look at the next 'line' to see if we can close it there
-                                               $next_line =  array_shift(array_splice( $a, $k + 1, 1) );
+                                               $spliced = array_splice( $a, $k + 1, 1 );
+                                               $next_line = array_shift( $spliced );
                                                if( preg_match("/^(.*?]].*?)]](.*)$/sD", $next_line, $m) ) {
                                                # the first ]] closes the inner link, the second the image
                                                        $found = true;
@@ -1385,7 +1403,7 @@ class Parser
                                                $text = $this->replaceInternalLinks($text);
 
                                                # cloak any absolute URLs inside the image markup, so replaceExternalLinks() won't touch them
-                                               $s .= $prefix . str_replace('http://', 'http-noparse://', $this->makeImage( $nt, $text ) ) . $trail;
+                                               $s .= $prefix . preg_replace("/\b($wgUrlProtocols)/", UNIQ_PREFIX."NOPARSE$1", $this->makeImage( $nt, $text) ) . $trail;
                                                $wgLinkCache->addImageLinkObj( $nt );
 
                                                wfProfileOut( "$fname-image" );
@@ -1444,22 +1462,7 @@ class Parser
                                $s .= $prefix . $sk->makeKnownLinkObj( $nt, $text, '', $trail );
                                continue;
                        }
-                       if( !$nt->isExternal() && $nt->isAlwaysKnown() ) {
-                               /**
-                                * Skip lookups for special pages and self-links.
-                                * External interwiki links are not included here because
-                                * the HTTP urls would break output in the next parse step;
-                                * they will have placeholders kept.
-                                */
-                               $s .= $sk->makeKnownLinkObj( $nt, $text, '', $trail, $prefix );
-                       } else {
-                               /**
-                                * Add a link placeholder
-                                * Later, this will be replaced by a real link, after the existence or
-                                * non-existence of all the links is known
-                                */
-                               $s .= $this->makeLinkHolder( $nt, $text, '', $trail, $prefix );
-                       }
+                       $s .= $this->makeLinkHolder( $nt, $text, '', $trail, $prefix );
                }
                wfProfileOut( $fname );
                return $s;
@@ -2138,20 +2141,27 @@ class Parser
                        }
                }
 
-               # LOCALURL and LOCALURLE
+               # LOCALURL and FULLURL
                if ( !$found ) {
-                       $mwLocal = MagicWord::get( MAG_LOCALURL );
-                       $mwLocalE = MagicWord::get( MAG_LOCALURLE );
+                       $mwLocal =& MagicWord::get( MAG_LOCALURL );
+                       $mwLocalE =& MagicWord::get( MAG_LOCALURLE );
+                       $mwFull =& MagicWord::get( MAG_FULLURL );
+                       $mwFullE =& MagicWord::get( MAG_FULLURLE );
+                       
 
                        if ( $mwLocal->matchStartAndRemove( $part1 ) ) {
                                $func = 'getLocalURL';
                        } elseif ( $mwLocalE->matchStartAndRemove( $part1 ) ) {
                                $func = 'escapeLocalURL';
+                       } elseif ( $mwFull->matchStartAndRemove( $part1 ) ) {
+                               $func = 'getFullURL';
+                       } elseif ( $mwFullE->matchStartAndRemove( $part1 ) ) {
+                               $func = 'escapeFullURL';
                        } else {
-                               $func = '';
+                               $func = false;
                        }
 
-                       if ( $func !== '' ) {
+                       if ( $func !== false ) {
                                $title = Title::newFromText( $part1 );
                                if ( !is_null( $title ) ) {
                                        if ( $argc > 0 ) {
@@ -2173,6 +2183,16 @@ class Parser
                        }
                }
 
+               # PLURAL
+               if ( !$found && $argc >= 2 ) {
+                       $mwPluralForm =& MagicWord::get( MAG_PLURAL );
+                       if ( $mwPluralForm->matchStartAndRemove( $part1 ) ) {
+                               if ($argc==2) {$args[2]=$args[1];}
+                               $text = $linestart . $wgContLang->convertPlural( $part1, $args[0], $args[1], $args[2]);
+                               $found = true;
+                       }
+               }
+
                # Template table test
 
                # Did we encounter this template already? If yes, it is in the cache
@@ -2206,12 +2226,12 @@ class Parser
                        }
                        $title = Title::newFromText( $part1, $ns );
 
-                        if ($title) {
-                            $interwiki = Title::getInterwikiLink($title->getInterwiki());
-                            if ($interwiki != '' && $title->isTrans()) {
-                                    return $this->scarytransclude($title, $interwiki);
-                            }
-                        }
+                       if ($title) {
+                               $interwiki = Title::getInterwikiLink($title->getInterwiki());
+                               if ($interwiki != '' && $title->isTrans()) {
+                                       return $this->scarytransclude($title, $interwiki);
+                               }
+                       }
 
                        if ( !is_null( $title ) && !$title->isExternal() ) {
                                # Check for excessive inclusion
@@ -2224,11 +2244,11 @@ class Parser
                                                        $found = true;
                                                        $noparse = true;
                                                        $isHTML = true;
-                                                       $this->mOutput->setCacheTime( -1 );
+                                                       $this->disableCache();
                                                }
                                        } else {
                                                $article = new Article( $title );
-                                               $articleContent = $article->getContentWithoutUsingSoManyDamnGlobals();
+                                               $articleContent = $article->fetchContent(0, false);
                                                if ( $articleContent !== false ) {
                                                        $found = true;
                                                        $text = $articleContent;
@@ -2279,6 +2299,11 @@ class Parser
                        $this->mTemplatePath[$part1] = 1;
 
                        if( $this->mOutputType == OT_HTML ) {
+                               # Remove <noinclude> sections and <includeonly> tags
+                               $text = preg_replace( '/<noinclude>.*?<\/noinclude>/s', '', $text );
+                               $text = strtr( $text, array( '<includeonly>' => '' , '</includeonly>' => '' ) );
+                               # Strip <nowiki>, <pre>, etc.
+                               $this->mCurrentParams = $assocArgs;
                                $text = $this->strip( $text, $this->mStripState );
                                $text = Sanitizer::removeHTMLtags( $text, array( &$this, 'replaceVariables' ), $assocArgs );
                        }
@@ -2368,7 +2393,7 @@ class Parser
        }
 
        function fetchScaryTemplateMaybeFromCache($url) {
-               $dbr = wfGetDB(DB_SLAVE);
+               $dbr =& wfGetDB(DB_SLAVE);
                $obj = $dbr->selectRow('transcache', array('tc_time', 'tc_contents'),
                                array('tc_url' => $url));
                if ($obj) {
@@ -2383,7 +2408,7 @@ class Parser
                if (!$text)
                        return wfMsg('scarytranscludefailed', $url);
 
-               $dbw = wfGetDB(DB_MASTER);
+               $dbw =& wfGetDB(DB_MASTER);
                $dbw->replace('transcache', array(), array(
                        'tc_url' => $url,
                        'tc_time' => time(),
@@ -3007,7 +3032,7 @@ class Parser
                                $pdbk = $pdbks[$key] = $title->getPrefixedDBkey();
 
                                # Check if it's in the link cache already
-                               if ( $wgLinkCache->getGoodLinkID( $pdbk ) ) {
+                               if ( $title->isAlwaysKnown() || $wgLinkCache->getGoodLinkID( $pdbk ) ) {
                                        $colours[$pdbk] = 1;
                                } elseif ( $wgLinkCache->isBadLink( $pdbk ) ) {
                                        $colours[$pdbk] = 0;
@@ -3234,6 +3259,7 @@ class Parser
                $part = explode( '|', $options);
 
                $mwThumb  =& MagicWord::get( MAG_IMG_THUMBNAIL );
+               $mwManualThumb =& MagicWord::get( MAG_IMG_MANUALTHUMB );
                $mwLeft   =& MagicWord::get( MAG_IMG_LEFT );
                $mwRight  =& MagicWord::get( MAG_IMG_RIGHT );
                $mwNone   =& MagicWord::get( MAG_IMG_NONE );
@@ -3246,14 +3272,12 @@ class Parser
                $manual_thumb = '' ;
 
                foreach( $part as $key => $val ) {
-                       $val_parts = explode ( '=' , $val , 2 ) ;
-                       $left_part = array_shift ( $val_parts ) ;
                        if ( $wgUseImageResize && ! is_null( $mwThumb->matchVariableStartToEnd($val) ) ) {
                                $thumb=true;
-                       } elseif ( $wgUseImageResize && count ( $val_parts ) == 1 && ! is_null( $mwThumb->matchVariableStartToEnd($left_part) ) ) {
+                       } elseif ( ! is_null( $match = $mwManualThumb->matchVariableStartToEnd($val) ) ) {
                                # use manually specified thumbnail
                                $thumb=true;
-                               $manual_thumb = array_shift ( $val_parts ) ;
+                               $manual_thumb = $match;
                        } elseif ( ! is_null( $mwRight->matchVariableStartToEnd($val) ) ) {
                                # remember to set an alignment, don't render immediately
                                $align = 'right';
@@ -3289,6 +3313,34 @@ class Parser
                $sk =& $this->mOptions->getSkin();
                return $sk->makeImageLinkObj( $nt, $caption, $alt, $align, $width, $height, $framed, $thumb, $manual_thumb );
        }
+
+       /**
+        * Set a flag in the output object indicating that the content is dynamic and 
+        * shouldn't be cached.
+        */
+       function disableCache() {
+               $this->mOutput->mCacheTime = -1;
+       }
+       
+       /**
+        * Callback from the Sanitizer for expanding items found in HTML attribute
+        * values, so they can be safely tested and escaped.
+        * @param string $text
+        * @param array $args
+        * @return string
+        * @access private
+        */
+       function attributeStripCallback( &$text, $args ) {
+               $text = $this->replaceVariables( $text, $args );
+               $text = $this->unstripForHTML( $text );
+               return $text;
+       }
+       
+       function unstripForHTML( $text ) {
+               $text = $this->unstrip( $text, $this->mStripState );
+               $text = $this->unstripNoWiki( $text, $this->mStripState );
+               return $text;
+       }
 }
 
 /**