* Wont work
[lhc/web/wiklou.git] / includes / Parser.php
index d08a369..3815dcb 100644 (file)
@@ -97,7 +97,8 @@ class Parser
         * @private
         */
        # Persistent:
-       var $mTagHooks, $mTransparentTagHooks, $mFunctionHooks, $mFunctionSynonyms, $mVariables;
+       var $mTagHooks, $mTransparentTagHooks, $mFunctionHooks, $mFunctionSynonyms, $mVariables,
+               $mImageParams, $mImageParamsMagicArray;
        
        # Cleared with clearState():
        var $mOutput, $mAutonumber, $mDTopen, $mStripState;
@@ -173,6 +174,7 @@ class Parser
                $this->setFunctionHook( 'anchorencode', array( 'CoreParserFunctions', 'anchorencode' ), SFH_NO_HASH );
                $this->setFunctionHook( 'special', array( 'CoreParserFunctions', 'special' ) );
                $this->setFunctionHook( 'defaultsort', array( 'CoreParserFunctions', 'defaultsort' ), SFH_NO_HASH );
+               $this->setFunctionHook( 'filepath', array( 'CoreParserFunctions', 'filepath' ), SFH_NO_HASH );
 
                if ( $wgAllowDisplayTitle ) {
                        $this->setFunctionHook( 'displaytitle', array( 'CoreParserFunctions', 'displaytitle' ), SFH_NO_HASH );
@@ -752,7 +754,7 @@ class Parser
                $descriptorspec = array(
                        0 => array('pipe', 'r'),
                        1 => array('pipe', 'w'),
-                       2 => array('file', '/dev/null', 'a')  // FIXME: this line in UNIX-specific, it generates a warning on Windows, because /dev/null is not a valid Windows file.
+                       2 => array('file', wfGetNull(), 'a')
                );
                $pipes = array();
                $process = proc_open("$wgTidyBin -config $wgTidyConf $wgTidyOpts$opts", $descriptorspec, $pipes);
@@ -1159,9 +1161,8 @@ class Parser
 
        /**
         * Helper function for doAllQuotes()
-        * @private
         */
-       function doQuotes( $text ) {
+       public function doQuotes( $text ) {
                $arr = preg_split( "/(''+)/", $text, -1, PREG_SPLIT_DELIM_CAPTURE );
                if ( count( $arr ) == 1 )
                        return $text;
@@ -3470,7 +3471,7 @@ class Parser
        /**
         * This function accomplishes several tasks:
         * 1) Auto-number headings if that option is enabled
-        * 2) Add an [edit] link to sections for logged in users who have enabled the option
+        * 2) Add an [edit] link to sections for users who have enabled the option and can edit the page
         * 3) Add a Table of contents on the top for users who have enabled the option
         * 4) Auto-anchor headings
         *
@@ -3640,12 +3641,21 @@ class Parser
                                                            "\$this->mInterwikiLinkHolders['texts'][\$1]",
                                                            $canonized_headline );
 
-                       # strip out HTML
-                       $canonized_headline = preg_replace( '/<.*?' . '>/','',$canonized_headline );
-                       $tocline = trim( $canonized_headline );
+                       # Strip out HTML (other than plain <sup> and <sub>: bug 8393)
+                       $tocline = preg_replace(
+                               array( '#<(?!/?(sup|sub)).*?'.'>#', '#<(/?(sup|sub)).*?'.'>#' ),
+                               array( '',                          '<$1>'),
+                               $canonized_headline
+                       );
+                       $tocline = trim( $tocline );
+
+                       # For the anchor, strip out HTML-y stuff period
+                       $canonized_headline = preg_replace( '/<.*?'.'>/', '', $canonized_headline );
+                       $canonized_headline = trim( $canonized_headline );
+
                        # Save headline for section edit hint before it's escaped
-                       $headline_hint = trim( $canonized_headline );
-                       $canonized_headline = Sanitizer::escapeId( $tocline );
+                       $headline_hint = $canonized_headline;
+                       $canonized_headline = Sanitizer::escapeId( $canonized_headline );
                        $refers[$headlineCount] = $canonized_headline;
 
                        # count how many in assoc. array so we can track dupes in anchors
@@ -4410,7 +4420,8 @@ class Parser
                $ig->setContextTitle( $this->mTitle );
                $ig->setShowBytes( false );
                $ig->setShowFilename( false );
-               $ig->setParsing();
+               $ig->setParser( $this );
+               $ig->setHideBadImages();
                $ig->setAttributes( Sanitizer::validateTagAttributes( $params, 'table' ) );
                $ig->useSkin( $this->mOptions->getSkin() );
                $ig->mRevisionId = $this->mRevisionId;
@@ -4473,10 +4484,50 @@ class Parser
                return $ig->toHTML();
        }
 
+       function getImageParams( $handler ) {
+               if ( $handler ) {
+                       $handlerClass = get_class( $handler );
+               } else {
+                       $handlerClass = '';
+               }
+               if ( !isset( $this->mImageParams[$handlerClass]  ) ) {
+                       // Initialise static lists
+                       static $internalParamNames = array(
+                               'horizAlign' => array( 'left', 'right', 'center', 'none' ),
+                               'vertAlign' => array( 'baseline', 'sub', 'super', 'top', 'text-top', 'middle', 
+                                       'bottom', 'text-bottom' ),
+                               'frame' => array( 'thumbnail', 'manualthumb', 'framed', 'frameless', 
+                                       'upright', 'border' ),
+                       );
+                       static $internalParamMap;
+                       if ( !$internalParamMap ) {
+                               $internalParamMap = array();
+                               foreach ( $internalParamNames as $type => $names ) {
+                                       foreach ( $names as $name ) {
+                                               $magicName = str_replace( '-', '_', "img_$name" );
+                                               $internalParamMap[$magicName] = array( $type, $name );
+                                       }
+                               }
+                       }
+
+                       // Add handler params
+                       $paramMap = $internalParamMap;
+                       if ( $handler ) {
+                               $handlerParamMap = $handler->getParamMap();
+                               foreach ( $handlerParamMap as $magic => $paramName ) {
+                                       $paramMap[$magic] = array( 'handler', $paramName );
+                               }
+                       }
+                       $this->mImageParams[$handlerClass] = $paramMap;
+                       $this->mImageParamsMagicArray[$handlerClass] = new MagicWordArray( array_keys( $paramMap ) );
+               }
+               return array( $this->mImageParams[$handlerClass], $this->mImageParamsMagicArray[$handlerClass] );
+       }
+
        /**
         * Parse image options text and use it to make an image
         */
-       function makeImage( $nt, $options ) {
+       function makeImage( $title, $options ) {
                # @TODO: let the MediaHandler specify its transform parameters
                #
                # Check if the options text is of the form "options|alt text"
@@ -4500,77 +4551,66 @@ class Parser
                #  * middle
                #  * bottom
                #  * text-bottom
+               
+               $parts = array_map( 'trim', explode( '|', $options) );
+               $sk = $this->mOptions->getSkin();
 
+               # Give extensions a chance to select the file revision for us
+               $skip = $time = false;
+               wfRunHooks( 'BeforeParserMakeImageLinkObj', array( &$this, &$title, &$skip, &$time ) );
 
-               $part = array_map( 'trim', explode( '|', $options) );
-
-               $mwAlign = array();
-               $alignments = array( 'left', 'right', 'center', 'none', 'baseline', 'sub', 'super', 'top', 'text-top', 'middle', 'bottom', 'text-bottom' );
-               foreach ( $alignments as $alignment ) {
-                       $mwAlign[$alignment] =& MagicWord::get( 'img_'.$alignment );
+               if ( $skip ) {
+                       return $sk->makeLinkObj( $title );
                }
-               $mwThumb  =& MagicWord::get( 'img_thumbnail' );
-               $mwManualThumb =& MagicWord::get( 'img_manualthumb' );
-               $mwWidth  =& MagicWord::get( 'img_width' );
-               $mwFramed =& MagicWord::get( 'img_framed' );
-               $mwFrameless =& MagicWord::get( 'img_frameless' );
-               $mwUpright =& MagicWord::get( 'img_upright' );
-               $mwBorder =& MagicWord::get( 'img_border' );
-               $mwPage   =& MagicWord::get( 'img_page' );
-               $caption = '';
 
-               $params = array();
-               $framed = $thumb = false;
-               $manual_thumb = '' ;
-               $align = $valign = '';
-               $sk = $this->mOptions->getSkin();
+               # Get parameter map
+               $file = wfFindFile( $title, $time );
+               $handler = $file ? $file->getHandler() : false;
 
-               foreach( $part as $val ) {
-                       if ( !is_null( $mwThumb->matchVariableStartToEnd($val) ) ) {
-                               $thumb=true;
-                       } elseif ( !is_null( $match = $mwUpright->matchVariableStartToEnd( $val ) ) ) {
-                               $params['upright'] = true;
-                               $params['upright_factor'] = floatval( $match );
-                       } elseif ( !is_null( $match = $mwFrameless->matchVariableStartToEnd( $val ) ) ) {
-                               $params['frameless'] = true;
-                       } elseif ( !is_null( $mwBorder->matchVariableStartToEnd( $val ) ) ) {
-                               $params['border'] = true;
-                       } elseif ( ! is_null( $match = $mwManualThumb->matchVariableStartToEnd($val) ) ) {
-                               # use manually specified thumbnail
-                               $thumb=true;
-                               $manual_thumb = $match;
-                       } else {
-                               foreach( $alignments as $alignment ) {
-                                       if ( ! is_null( $mwAlign[$alignment]->matchVariableStartToEnd($val) ) ) {
-                                               switch ( $alignment ) {
-                                                       case 'left': case 'right': case 'center': case 'none':
-                                                               $align = $alignment; break;
-                                                       default:
-                                                               $valign = $alignment;
-                                               }
-                                               continue 2;
-                                       }
-                               }
-                               if ( ! is_null( $match = $mwPage->matchVariableStartToEnd($val) ) ) {
-                                       # Select a page in a multipage document
-                                       $params['page'] = $match;
-                               } elseif ( !isset( $params['width'] ) && ! is_null( $match = $mwWidth->matchVariableStartToEnd($val) ) ) {
-                                       wfDebug( "img_width match: $match\n" );
-                                       # $match is the image width in pixels
+               list( $paramMap, $mwArray ) = $this->getImageParams( $handler );
+
+               # Process the input parameters
+               $caption = '';
+               $params = array( 'frame' => array(), 'handler' => array(), 
+                       'horizAlign' => array(), 'vertAlign' => array() );
+               foreach( $parts as $part ) {
+                       list( $magicName, $value ) = $mwArray->matchVariableStartToEnd( $part );
+                       if ( isset( $paramMap[$magicName] ) ) {
+                               list( $type, $paramName ) = $paramMap[$magicName];
+                               $params[$type][$paramName] = $value;
+                               
+                               // Special case; width and height come in one variable together
+                               if( $type == 'handler' && $paramName == 'width' ) {
                                        $m = array();
-                                       if ( preg_match( '/^([0-9]*)x([0-9]*)$/', $match, $m ) ) {
-                                                $params['width'] = intval( $m[1] );
-                                                $params['height'] = intval( $m[2] );
+                                       if ( preg_match( '/^([0-9]*)x([0-9]*)$/', $value, $m ) ) {
+                                               $params[$type]['width'] = intval( $m[1] );
+                                               $params[$type]['height'] = intval( $m[2] );
                                        } else {
-                                               $params['width'] = intval($match);
+                                               $params[$type]['width'] = intval( $value );
                                        }
-                               } elseif ( ! is_null( $mwFramed->matchVariableStartToEnd($val) ) ) {
-                                       $framed=true;
-                               } else {
-                                       $caption = $val;
                                }
+                       } else {
+                               $caption = $part;
                        }
                }
+
+               # Process alignment parameters
+               if ( $params['horizAlign'] ) {
+                       $params['frame']['align'] = key( $params['horizAlign'] );
+               }
+               if ( $params['vertAlign'] ) {
+                       $params['frame']['valign'] = key( $params['vertAlign'] );
+               }
+
+               # Validate the handler parameters
+               if ( $handler ) {
+                       foreach ( $params['handler'] as $name => $value ) {
+                               if ( !$handler->validateParam( $name, $value ) ) {
+                                       unset( $params['handler'][$name] );
+                               }
+                       }
+               }
+
                # Strip bad stuff out of the alt text
                $alt = $this->replaceLinkHoldersText( $caption );
 
@@ -4580,18 +4620,18 @@ class Parser
                $alt = $this->mStripState->unstripBoth( $alt );
                $alt = Sanitizer::stripAllTags( $alt );
 
-               # Give extensions a chance to select the file revision for us
-               $skip = $time = false;
-               wfRunHooks( 'BeforeParserMakeImageLinkObj', array( &$this, &$nt, &$skip, &$time ) );
+               $params['frame']['alt'] = $alt;
+               $params['frame']['caption'] = $caption;
 
                # Linker does the rest
-               if( $skip ) {
-                       $link = $sk->makeLinkObj( $nt );
-               } else {
-                       $link = $sk->makeImageLinkObj( $nt, $caption, $alt, $align, $params, $framed, $thumb, $manual_thumb, $valign, $time );
+               $ret = $sk->makeImageLink2( $title, $file, $params['frame'], $params['handler'] );
+
+               # Give the handler a chance to modify the parser object
+               if ( $handler ) {
+                       $handler->parserTransformHook( $this, $file );
                }
-               
-               return $link;
+
+               return $ret;
        }
 
        /**
@@ -4834,6 +4874,61 @@ class Parser
                                        : $this->mTitle->getPrefixedText();
                }
        }
+
+       /**
+        * Try to guess the section anchor name based on a wikitext fragment 
+        * presumably extracted from a heading, for example "Header" from 
+        * "== Header ==".
+        */
+       public function guessSectionNameFromWikiText( $text ) {
+               # Strip out wikitext links(they break the anchor)
+               $text = $this->stripSectionName( $text );
+               $headline = Sanitizer::decodeCharReferences( $text );
+               # strip out HTML
+               $headline = StringUtils::delimiterReplace( '<', '>', '', $headline );
+               $headline = trim( $headline );
+               $sectionanchor = '#' . urlencode( str_replace( ' ', '_', $headline ) );
+               $replacearray = array(
+                       '%3A' => ':',
+                       '%' => '.'
+               );
+               return str_replace(
+                       array_keys( $replacearray ),
+                       array_values( $replacearray ),
+                       $sectionanchor );
+       }
+
+       /**
+        * Strips a text string of wikitext for use in a section anchor
+        * 
+        * Accepts a text string and then removes all wikitext from the
+        * string and leaves only the resultant text (i.e. the result of
+        * [[User:WikiSysop|Sysop]] would be "Sysop" and the result of
+        * [[User:WikiSysop]] would be "User:WikiSysop") - this is intended
+        * to create valid section anchors by mimicing the output of the
+        * parser when headings are parsed.
+        * 
+        * @param $text string Text string to be stripped of wikitext
+        * for use in a Section anchor
+        * @return Filtered text string
+        */
+       public function stripSectionName( $text ) {
+               # Strip internal link markup
+               $text = preg_replace('/\[\[:?([^[|]+)\|([^[]+)\]\]/','$2',$text);
+               $text = preg_replace('/\[\[:?([^[]+)\|?\]\]/','$1',$text);
+               
+               # Strip external link markup (FIXME: Not Tolerant to blank link text
+               # I.E. [http://www.mediawiki.org] will render as [1] or something depending
+               # on how many empty links there are on the page - need to figure that out.
+               $text = preg_replace('/\[(?:' . wfUrlProtocols() . ')([^ ]+?) ([^[]+)\]/','$2',$text);
+               
+               # Parse wikitext quotes (italics & bold)
+               $text = $this->doQuotes($text);
+               
+               # Strip HTML tags
+               $text = StringUtils::delimiterReplace( '<', '>', '', $text );
+               return $text;
+       }
 }
 
 /**