* (bug 10508) Allow HTML attributes on <gallery>
authorBrion Vibber <brion@users.mediawiki.org>
Fri, 13 Jul 2007 17:25:06 +0000 (17:25 +0000)
committerBrion Vibber <brion@users.mediawiki.org>
Fri, 13 Jul 2007 17:25:06 +0000 (17:25 +0000)
* (bug 1962) Allow HTML attributes on <math>

RELEASE-NOTES
includes/ImageGallery.php
includes/Math.php
includes/Parser.php
includes/Sanitizer.php

index ceb49b2..1c3aef7 100644 (file)
@@ -132,6 +132,9 @@ it from source control: http://www.mediawiki.org/wiki/Download_from_SVN
 * Allow showing a one-off preview on first edit with "preview=yes"
 * (bug 9151) Remove timed redirects on "Return to X" pages for accessibility.
 * Link to user logs in toolbox when viewing a user page
+* (bug 10508) Allow HTML attributes on <gallery>
+* (bug 1962) Allow HTML attributes on <math>
+
 
 == Bugfixes since 1.10 ==
 
index aa8c323..8d7c36d 100644 (file)
@@ -32,6 +32,8 @@ class ImageGallery
 
        private $mPerRow = 4; // How many images wide should the gallery be?
        private $mWidths = 120, $mHeights = 120; // How wide/tall each thumbnail should be
+       
+       private $mAttribs = array();
 
        /**
         * Create a new image gallery object.
@@ -181,6 +183,19 @@ class ImageGallery
        function setShowFilename( $f ) {
                $this->mShowFilename = ( $f == true);
        }
+       
+       /**
+        * Set arbitrary attributes to go on the HTML gallery output element.
+        * Should be suitable for a &lt;table&gt; element.
+        * 
+        * Note -- if taking from user input, you should probably run through
+        * Sanitizer::validateAttributes() first.
+        *
+        * @param array of HTML attribute pairs
+        */
+       function setAttributes( $attribs ) {
+               $this->mAttribs = $attribs;
+       }
 
        /**
         * Return a HTML representation of the image gallery
@@ -197,7 +212,13 @@ class ImageGallery
 
                $sk = $this->getSkin();
 
-               $s = '<table class="gallery" cellspacing="0" cellpadding="0">';
+               $attribs = Sanitizer::mergeAttributes(
+                       array(
+                               'class' => 'gallery',
+                               'cellspacing' => '0',
+                               'cellpadding' => '0' ),
+                       $this->mAttribs );
+               $s = Xml::openElement( 'table', $attribs );
                if( $this->mCaption )
                        $s .= "\n\t<caption>{$this->mCaption}</caption>";
 
index c2dd041..2771d04 100644 (file)
@@ -20,8 +20,9 @@ class MathRenderer {
        var $mathml = '';
        var $conservativeness = 0;
 
-       function __construct( $tex ) {
+       function __construct( $tex, $params=array() ) {
                $this->tex = $tex;
+               $this->params = $params;
        }
 
        function setOutputMode( $mode ) {
@@ -233,24 +234,44 @@ class MathRenderer {
         */
        function _doRender() {
                if( $this->mode == MW_MATH_MATHML && $this->mathml != '' ) {
-                       return "<math xmlns='http://www.w3.org/1998/Math/MathML'>{$this->mathml}</math>";
+                       return Xml::tags( 'math',
+                               $this->_attribs( 'math',
+                                       array( 'xmlns' => 'http://www.w3.org/1998/Math/MathML' ) ),
+                               $this->mathml );
                }
                if (($this->mode == MW_MATH_PNG) || ($this->html == '') ||
                   (($this->mode == MW_MATH_SIMPLE) && ($this->conservativeness != 2)) ||
                   (($this->mode == MW_MATH_MODERN || $this->mode == MW_MATH_MATHML) && ($this->conservativeness == 0))) {
                        return $this->_linkToMathImage();
                } else {
-                       return '<span class="texhtml">'.$this->html.'</span>';
+                       return Xml::tags( 'span',
+                               $this->_attribs( 'span',
+                                       array( 'class' => 'texhtml' ) ),
+                               $this->html );
                }
        }
+       
+       function _attribs( $tag, $defaults=array(), $overrides=array() ) {
+               $attribs = Sanitizer::validateTagAttributes( $this->params, $tag );
+               $attribs = Sanitizer::mergeAttributes( $defaults, $attribs );
+               $attribs = Sanitizer::mergeAttributes( $attribs, $overrides );
+               return $attribs;
+       }
 
        function _linkToMathImage() {
                global $wgMathPath;
-               $url = htmlspecialchars( "$wgMathPath/" . substr($this->hash, 0, 1)
+               $url = "$wgMathPath/" . substr($this->hash, 0, 1)
                                        .'/'. substr($this->hash, 1, 1) .'/'. substr($this->hash, 2, 1)
-                                       . "/{$this->hash}.png" );
-               $alt = trim(str_replace("\n", ' ', htmlspecialchars( $this->tex )));
-               return "<img class='tex' src=\"$url\" alt=\"$alt\" />";
+                                       . "/{$this->hash}.png";
+
+               return Xml::element( 'img',
+                       $this->_attribs(
+                               'img',
+                               array(
+                                       'class' => 'tex',
+                                       'alt' => $this->tex ),
+                               array(
+                                       'src' => $url ) ) );
        }
 
        function _getHashPath() {
@@ -262,9 +283,9 @@ class MathRenderer {
                return $path;
        }
 
-       public static function renderMath( $tex ) {
+       public static function renderMath( $tex, $params=array() ) {
                global $wgUser;
-               $math = new MathRenderer( $tex );
+               $math = new MathRenderer( $tex, $params );
                $math->setOutputMode( $wgUser->getOption('math'));
                return $math->render();
        }
index 3495943..04cd9ed 100644 (file)
@@ -592,7 +592,8 @@ class Parser
                                        $output = Xml::escapeTagsOnly( $content );
                                        break;
                                case 'math':
-                                       $output = $wgContLang->armourMath( MathRenderer::renderMath( $content ) );
+                                       $output = $wgContLang->armourMath(
+                                               MathRenderer::renderMath( $content, $params ) );
                                        break;
                                case 'gallery':
                                        $output = $this->renderImageGallery( $content, $params );
@@ -4381,6 +4382,7 @@ class Parser
                $ig->setShowBytes( false );
                $ig->setShowFilename( false );
                $ig->setParsing();
+               $ig->setAttributes( Sanitizer::validateTagAttributes( $params, 'table' ) );
                $ig->useSkin( $this->mOptions->getSkin() );
                $ig->mRevisionId = $this->mRevisionId;
 
index 7d33bd8..ffe10f9 100644 (file)
@@ -566,6 +566,7 @@ class Sanitizer {
         *
         * - Discards attributes not on a whitelist for the given element
         * - Unsafe style attributes are discarded
+        * - Invalid id attributes are reencoded
         *
         * @param array $attribs
         * @param string $element
@@ -575,7 +576,27 @@ class Sanitizer {
         * @todo Check for unique id attribute :P
         */
        static function validateTagAttributes( $attribs, $element ) {
-               $whitelist = array_flip( Sanitizer::attributeWhitelist( $element ) );
+               return Sanitizer::validateAttributes( $attribs,
+                       Sanitizer::attributeWhitelist( $element ) );
+       }
+       
+       /**
+        * Take an array of attribute names and values and normalize or discard
+        * illegal values for the given whitelist.
+        *
+        * - Discards attributes not the given whitelist
+        * - Unsafe style attributes are discarded
+        * - Invalid id attributes are reencoded
+        *
+        * @param array $attribs
+        * @param array $whitelist list of allowed attribute names
+        * @return array
+        *
+        * @todo Check for legal values where the DTD limits things.
+        * @todo Check for unique id attribute :P
+        */
+       static function validateAttributes( $attribs, $whitelist ) {
+               $whitelist = array_flip( $whitelist );
                $out = array();
                foreach( $attribs as $attribute => $value ) {
                        if( !isset( $whitelist[$attribute] ) ) {
@@ -601,6 +622,33 @@ class Sanitizer {
                return $out;
        }
        
+       /**
+        * Merge two sets of HTML attributes.
+        * Conflicting items in the second set will override those
+        * in the first, except for 'class' attributes which will be
+        * combined.
+        *
+        * @todo implement merging for other attributes such as style
+        * @param array $a
+        * @param array $b
+        * @return array
+        */
+       static function mergeAttributes( $a, $b ) {
+               $out = array_merge( $a, $b );
+               if( isset( $a['class'] )
+                       && isset( $b['class'] )
+                       && $a['class'] !== $b['class'] ) {
+                       
+                       $out['class'] = implode( ' ',
+                               array_unique(
+                                       preg_split( '/\s+/',
+                                               $a['class'] . ' ' . $b['class'],
+                                               -1,
+                                               PREG_SPLIT_NO_EMPTY ) ) );
+               }
+               return $out;
+       }
+       
        /**
         * Pick apart some CSS and check it for forbidden or unsafe structures.
         * Returns a sanitized string, or false if it was just too evil.
@@ -1159,6 +1207,11 @@ class Sanitizer {
                        # 11.2.6
                        'td'         => array_merge( $common, $tablecell, $tablealign ),
                        'th'         => array_merge( $common, $tablecell, $tablealign ),
+                       
+                       # 13.2
+                       # Not usually allowed, but may be used for extension-style hooks
+                       # such as <math> when it is rasterized
+                       'img'        => array_merge( $common, array( 'alt' ) ),
 
                        # 15.2.1
                        'tt'         => $common,
@@ -1185,6 +1238,11 @@ class Sanitizer {
                        'rb'         => $common,
                        'rt'         => $common, #array_merge( $common, array( 'rbspan' ) ),
                        'rp'         => $common,
+                       
+                       # MathML root element, where used for extensions
+                       # 'title' may not be 100% valid here; it's XHTML
+                       # http://www.w3.org/TR/REC-MathML/
+                       'math'       => array( 'class', 'style', 'id', 'title' ),
                        );
                return $whitelist;
        }