Allow handler specific parameters in <gallery> (page number, etc)
authorBrian Wolff <bawolff+wn@gmail.com>
Thu, 25 Apr 2013 00:28:03 +0000 (21:28 -0300)
committerBrian Wolff <bawolff+wn@gmail.com>
Fri, 5 Jul 2013 17:29:09 +0000 (14:29 -0300)
For multipage media, people really want to be able to specify
if the image gallery should display page 1 or page 10. This
also allows other handler specific parameters like thumbtime
for videos, "lossy" for tiff files, etc.

Note, this only allows the handler specific options
(typically things that would change an image). Other options in
the thumb syntax like class, border, upright, left, etc are
still not supported (and mostly probably should not be)

Bug: 8480

Change-Id: Ib831d89ed8676deb2f44238ff9a23ce58ad4d2df

RELEASE-NOTES-1.22
includes/ImageGallery.php
includes/parser/Parser.php

index 45a6f01..24081db 100644 (file)
@@ -137,6 +137,7 @@ production.
   also granting the ability to protect and unprotect.
 * (bug 48256) Make brackets in section edit links accessible to CSS.
   They are now wrapped in <span class="mw-editsection-bracket" />.
+* (bug 8480) Allow handler specific parameters in galleries (like page number)
 
 === Bug fixes in 1.22 ===
 * Disable Special:PasswordReset when $wgEnableEmail is false. Previously one
index 5b45404..b4ef904 100644 (file)
@@ -160,13 +160,14 @@ class ImageGallery {
         * @param $html  String: Additional HTML text to be shown. The name and size of the image are always shown.
         * @param $alt   String: Alt text for the image
         * @param $link  String: Override image link (optional)
+        * @param $handlerOpts Array: Array of options for image handler (aka page number)
         */
-       function add( $title, $html = '', $alt = '', $link = '' ) {
+       function add( $title, $html = '', $alt = '', $link = '', $handlerOpts = array() ) {
                if ( $title instanceof File ) {
                        // Old calling convention
                        $title = $title->getTitle();
                }
-               $this->mImages[] = array( $title, $html, $alt, $link );
+               $this->mImages[] = array( $title, $html, $alt, $link, $handlerOpts );
                wfDebug( 'ImageGallery::add ' . $title->getText() . "\n" );
        }
 
@@ -176,13 +177,15 @@ class ImageGallery {
         * @param $title Title object of the image that is added to the gallery
         * @param $html  String: Additional HTML text to be shown. The name and size of the image are always shown.
         * @param $alt   String: Alt text for the image
+        * @param $link  String: Override image link (optional)
+        * @param $handlerOpts Array: Array of options for image handler (aka page number)
         */
-       function insert( $title, $html = '', $alt = '' ) {
+       function insert( $title, $html = '', $alt = '', $link = '', $handlerOpts = array() ) {
                if ( $title instanceof File ) {
                        // Old calling convention
                        $title = $title->getTitle();
                }
-               array_unshift( $this->mImages, array( &$title, $html, $alt ) );
+               array_unshift( $this->mImages, array( &$title, $html, $alt, $link, $handlerOpts ) );
        }
 
        /**
@@ -264,6 +267,8 @@ class ImageGallery {
                        $text = $pair[1]; # "text" means "caption" here
                        $alt = $pair[2];
                        $link = $pair[3];
+                       // $pair[4] is per image handler options
+                       $transformOptions = $params + $pair[4];
 
                        $descQuery = false;
                        if ( $nt->getNamespace() == NS_FILE ) {
@@ -301,7 +306,7 @@ class ImageGallery {
                                                array( 'known', 'noclasses' )
                                        ) .
                                        '</div>';
-                       } elseif ( !( $thumb = $img->transform( $params ) ) ) {
+                       } elseif ( !( $thumb = $img->transform( $transformOptions ) ) ) {
                                # Error generating thumbnail.
                                $thumbhtml = "\n\t\t\t" . '<div style="height: ' . ( self::THUMB_PADDING + $this->mHeights ) . 'px;">'
                                        . htmlspecialchars( $img->getLastError() ) . '</div>';
index c2eeb0a..e1a3271 100644 (file)
@@ -3776,13 +3776,8 @@ class Parser {
         * @return Array ( File or false, Title of file )
         */
        function fetchFileAndTitle( $title, $options = array() ) {
-               if ( isset( $options['broken'] ) ) {
-                       $file = false; // broken thumbnail forced by hook
-               } elseif ( isset( $options['sha1'] ) ) { // get by (sha1,timestamp)
-                       $file = RepoGroup::singleton()->findFileFromKey( $options['sha1'], $options );
-               } else { // get by (name,timestamp)
-                       $file = wfFindFile( $title, $options );
-               }
+               $file = $this->fetchFileNoRegister( $title, $options );
+
                $time = $file ? $file->getTimestamp() : false;
                $sha1 = $file ? $file->getSha1() : false;
                # Register the file as a dependency...
@@ -3800,6 +3795,27 @@ class Parser {
                return array( $file, $title );
        }
 
+       /**
+        * Helper function for fetchFileAndTitle.
+        *
+        * Also useful if you need to fetch a file but not use it yet,
+        * for example to get the file's handler.
+        *
+        * @param Title $title
+        * @param array $options Array of options to RepoGroup::findFile
+        * @return File or false
+        */
+       protected function fetchFileNoRegister( $title, $options = array() ) {
+               if ( isset( $options['broken'] ) ) {
+                       $file = false; // broken thumbnail forced by hook
+               } elseif ( isset( $options['sha1'] ) ) { // get by (sha1,timestamp)
+                       $file = RepoGroup::singleton()->findFileFromKey( $options['sha1'], $options );
+               } else { // get by (name,timestamp)
+                       $file = wfFindFile( $title, $options );
+               }
+               return $file;
+       }
+
        /**
         * Transclude an interwiki link.
         *
@@ -5017,6 +5033,7 @@ class Parser {
         * @return string HTML
         */
        function renderImageGallery( $text, $params ) {
+               wfProfileIn( __METHOD__ );
                $ig = new ImageGallery();
                $ig->setContextTitle( $this->mTitle );
                $ig->setShowBytes( false );
@@ -5068,38 +5085,81 @@ class Parser {
                                continue;
                        }
 
+                       # We need to get what handler the file uses, to figure out parameters.
+                       # Note, a hook can overide the file name, and chose an entirely different
+                       # file (which potentially could be of a different type and have different handler).
+                       $options = array();
+                       $descQuery = false;
+                       wfRunHooks( 'BeforeParserFetchFileAndTitle',
+                               array( $this, $title, &$options, &$descQuery ) );
+                       # Don't register it now, as ImageGallery does that later.
+                       $file = $this->fetchFileNoRegister( $title, $options );
+                       $handler = $file ? $file->getHandler() : false;
+
+                       wfProfileIn( __METHOD__ . '-getMagicWord' );
+                       $paramMap = array(
+                               'img_alt' => 'gallery-internal-alt',
+                               'img_link' => 'gallery-internal-link',
+                       );
+                       if ( $handler ) {
+                               $paramMap = $paramMap + $handler->getParamMap();
+                               // We don't want people to specify per-image widths.
+                               // Additionally the width parameter would need special casing anyhow.
+                               unset( $paramMap['img_width'] );
+                       }
+
+                       $mwArray = new MagicWordArray( array_keys( $paramMap ) );
+                       wfProfileOut( __METHOD__ . '-getMagicWord' );
+
                        $label = '';
                        $alt = '';
                        $link = '';
+                       $handlerOptions = array();
                        if ( isset( $matches[3] ) ) {
                                // look for an |alt= definition while trying not to break existing
                                // captions with multiple pipes (|) in it, until a more sensible grammar
                                // is defined for images in galleries
 
+                               // FIXME: Doing recursiveTagParse at this stage, and the trim before
+                               // splitting on '|' is a bit odd, and different from makeImage.
                                $matches[3] = $this->recursiveTagParse( trim( $matches[3] ) );
                                $parameterMatches = StringUtils::explode( '|', $matches[3] );
-                               $magicWordAlt = MagicWord::get( 'img_alt' );
-                               $magicWordLink = MagicWord::get( 'img_link' );
 
                                foreach ( $parameterMatches as $parameterMatch ) {
-                                       if ( $match = $magicWordAlt->matchVariableStartToEnd( $parameterMatch ) ) {
-                                               $alt = $this->stripAltText( $match, false );
-                                       }
-                                       elseif ( $match = $magicWordLink->matchVariableStartToEnd( $parameterMatch ) ) {
-                                               $linkValue = strip_tags( $this->replaceLinkHoldersText( $match ) );
-                                               $chars = self::EXT_LINK_URL_CLASS;
-                                               $prots = $this->mUrlProtocols;
-                                               //check to see if link matches an absolute url, if not then it must be a wiki link.
-                                               if ( preg_match( "/^($prots)$chars+$/u", $linkValue ) ) {
-                                                       $link = $linkValue;
-                                               } else {
-                                                       $localLinkTitle = Title::newFromText( $linkValue );
-                                                       if ( $localLinkTitle !== null ) {
-                                                               $link = $localLinkTitle->getLocalURL();
+                                       list( $magicName, $match ) = $mwArray->matchVariableStartToEnd( $parameterMatch );
+                                       if ( $magicName ) {
+                                               $paramName = $paramMap[$magicName];
+
+                                               switch( $paramName ) {
+                                               case 'gallery-internal-alt':
+                                                       $alt = $this->stripAltText( $match, false );
+                                                       break;
+                                               case 'gallery-internal-link':
+                                                       $linkValue = strip_tags( $this->replaceLinkHoldersText( $match ) );
+                                                       $chars = self::EXT_LINK_URL_CLASS;
+                                                       $prots = $this->mUrlProtocols;
+                                                       //check to see if link matches an absolute url, if not then it must be a wiki link.
+                                                       if ( preg_match( "/^($prots)$chars+$/u", $linkValue ) ) {
+                                                               $link = $linkValue;
+                                                       } else {
+                                                               $localLinkTitle = Title::newFromText( $linkValue );
+                                                               if ( $localLinkTitle !== null ) {
+                                                                       $link = $localLinkTitle->getLocalURL();
+                                                               }
+                                                       }
+                                                       break;
+                                               default:
+                                                       // Must be a handler specific parameter.
+                                                       if ( $handler->validateParam( $paramName, $match ) ) {
+                                                               $handlerOptions[$paramName] = $match;
+                                                       } else {
+                                                               // Guess not. Append it to the caption.
+                                                               wfDebug( "$parameterMatch failed parameter validation" );
+                                                               $label .= '|' . $parameterMatch;
                                                        }
                                                }
-                                       }
-                                       else {
+
+                                       else {
                                                // concatenate all other pipes
                                                $label .= '|' . $parameterMatch;
                                        }
@@ -5108,9 +5168,11 @@ class Parser {
                                $label = substr( $label, 1 );
                        }
 
-                       $ig->add( $title, $label, $alt, $link );
+                       $ig->add( $title, $label, $alt, $link, $handlerOptions );
                }
-               return $ig->toHTML();
+               $html = $ig->toHTML();
+               wfProfileOut( __METHOD__ );
+               return $html;
        }
 
        /**