Basic integrated audio/video support, with Ogg implementation.
authorTim Starling <tstarling@users.mediawiki.org>
Wed, 15 Aug 2007 10:50:09 +0000 (10:50 +0000)
committerTim Starling <tstarling@users.mediawiki.org>
Wed, 15 Aug 2007 10:50:09 +0000 (10:50 +0000)
* JavaScript video player based loosely on Greg Maxwell's player
* Image page text snippet customisation
* Abstraction of transform parameters in the parser. Introduced Linker::makeImageLink2().
* Made canRender(), mustRender() depend on file, not just on handler. Moved width=0, height=0 checking to ImageHandler::canRender(), since audio streams have width=height=0 but should be rendered.

Also:
* Automatic upgrade for oldimage rows on image page view, allows media handler selection based on oi_*_mime
* oi_*_mime unconditionally referenced, REQUIRES SCHEMA UPGRADE
* Don't destroy file info for missing files on upgrade
* Simple, centralised extension message file handling
* Made MessageCache::loadAllMessages non-static, optimised for repeated-call case due to abuse in User.php
* Support for lightweight parser output hooks, with callback whitelist for security
* Moved Linker::formatSize() to Language, to join the new formatTimePeriod() and formatBitrate()
* Introduced MagicWordArray, regex capture trick requires that magic word IDs DO NOT CONTAIN HYPHENS.

31 files changed:
RELEASE-NOTES
includes/AutoLoader.php
includes/DefaultSettings.php
includes/GlobalFunctions.php
includes/ImagePage.php
includes/Linker.php
includes/MagicWord.php
includes/MessageCache.php
includes/MimeMagic.php
includes/OutputPage.php
includes/Parser.php
includes/ParserOutput.php
includes/SpecialAllmessages.php
includes/SpecialSpecialpages.php
includes/User.php
includes/filerepo/File.php
includes/filerepo/LocalFile.php
includes/filerepo/OldLocalFile.php
includes/media/DjVu.php
includes/media/Generic.php
includes/media/SVG.php
languages/Language.php
languages/messages/MessagesAr.php
languages/messages/MessagesBg.php
languages/messages/MessagesCs.php
languages/messages/MessagesEn.php
languages/messages/MessagesId.php
languages/messages/MessagesKk_cn.php
languages/messages/MessagesKk_kz.php
languages/messages/MessagesKk_tr.php
languages/messages/MessagesNl.php

index ffc0bc3..e70d5a4 100644 (file)
@@ -173,6 +173,7 @@ it from source control: http://www.mediawiki.org/wiki/Download_from_SVN
 * (bug 10872) Fall back to sane defaults when generating protection selector
   labels for custom restriction levels
 * Show edit count in user preferences
+* Improved support for audio/video extensions
 
 == Bugfixes since 1.10 ==
 
index 628e102..632903f 100644 (file)
@@ -118,6 +118,7 @@ function __autoload($className) {
                'LogPage' => 'includes/LogPage.php',
                'MacBinary' => 'includes/MacBinary.php',
                'MagicWord' => 'includes/MagicWord.php',
+               'MagicWordArray' => 'includes/MagicWord.php',
                'MathRenderer' => 'includes/Math.php',
                'MediaTransformOutput' => 'includes/MediaTransformOutput.php',
                'ThumbnailImage' => 'includes/MediaTransformOutput.php',
@@ -385,3 +386,4 @@ function wfLoadAllExtensions() {
 }
 
 
+
index ce1aaf9..80f0eb1 100644 (file)
@@ -1916,6 +1916,28 @@ $wgExtensionFunctions = array();
  */
 $wgSkinExtensionFunctions = array();
 
+/**
+ * Extension messages files
+ * Associative array mapping extension name to the filename where messages can be found.
+ * The file must create a variable called $messages.
+ * When the messages are needed, the extension should call wfLoadMessagesFile()
+ */
+$wgExtensionMessagesFiles = array();
+
+/**
+ * Parser output hooks.
+ * This is an associative array where the key is an extension-defined tag
+ * (typically the extension name), and the value is a PHP callback.
+ * These will be called as an OutputPageParserOutput hook, if the relevant
+ * tag has been registered with the parser output object.
+ *
+ * Registration is done with $pout->addOutputHook( $tag, $data ). 
+ *
+ * The callback has the form:
+ *    function outputHook( $outputPage, $parserOutput, $data ) { ... }
+ */
+$wgParserOutputHooks = array();
+
 /**
  * List of valid skin names.
  * The key should be the name in all lower case, the value should be a display name.
index 02a30a4..ea23048 100644 (file)
@@ -2297,3 +2297,17 @@ function wfScript( $script = 'index' ) {
 function wfBoolToStr( $value ) {
        return $value ? 'true' : 'false';
 }
+
+/**
+ * Load an extension messages file
+ */
+function wfLoadExtensionMessages( $extensionName ) {
+       global $wgExtensionMessagesFiles, $wgMessageCache;
+       if ( !empty( $wgExtensionMessagesFiles[$extensionName] ) ) {
+               $wgMessageCache->loadMessagesFile( $wgExtensionMessagesFiles[$extensionName] );
+               // Prevent double-loading
+               $wgExtensionMessagesFiles[$extensionName] = false;
+       }
+}
+
+
index a6b33cd..d64a418 100644 (file)
@@ -16,6 +16,7 @@ if( !defined( 'MEDIAWIKI' ) )
 class ImagePage extends Article {
 
        /* private */ var $img;  // Image object this page is shown for
+       /* private */ var $repo;
        var $mExtraDescription = false;
 
        function __construct( $title ) {
@@ -24,6 +25,7 @@ class ImagePage extends Article {
                if ( !$this->img ) {
                        $this->img = wfLocalFile( $this->mTitle );
                }
+               $this->repo = $this->img->repo;
        }
 
        /**
@@ -46,6 +48,7 @@ class ImagePage extends Article {
                        return Article::view();
 
                if ($wgShowEXIF && $this->img->exists()) {
+                       // FIXME: bad interface, see note on MediaHandler::formatMetadata(). 
                        $formattedMetadata = $this->img->formatMetadata();
                        $showmeta = $formattedMetadata !== false;
                } else {
@@ -115,6 +118,8 @@ class ImagePage extends Article {
        /**
         * Make a table with metadata to be shown in the output page.
         *
+        * FIXME: bad interface, see note on MediaHandler::formatMetadata(). 
+        *
         * @access private
         *
         * @param array $exif The array containing the EXIF data
@@ -188,14 +193,15 @@ class ImagePage extends Article {
                        $mime = $this->img->getMimeType();
                        $showLink = false;
                        $linkAttribs = array( 'href' => $full_url );
+                       $longDesc = $this->img->getLongDesc();
 
-      wfRunHooks( 'ImageOpenShowImageInlineBefore', array( &$this , &$wgOut ) )        ;
+                       wfRunHooks( 'ImageOpenShowImageInlineBefore', array( &$this , &$wgOut ) )       ;
 
-                       if ( $this->img->allowInlineDisplay() and $width and $height) {
+                       if ( $this->img->allowInlineDisplay() ) {
                                # image
 
                                # "Download high res version" link below the image
-                               $msgsize = wfMsgHtml('file-info-size', $width_orig, $height_orig, $sk->formatSize( $this->img->getSize() ), $mime );
+                               #$msgsize = wfMsgHtml('file-info-size', $width_orig, $height_orig, $sk->formatSize( $this->img->getSize() ), $mime );
                                # We'll show a thumbnail of this image
                                if ( $width > $maxWidth || $height > $maxHeight ) {
                                        # Calculate the thumbnail size.
@@ -229,7 +235,7 @@ class ImagePage extends Article {
                                } else {
                                        $anchorclose .= 
                                                $msgsmall .
-                                               '<br />' . Xml::tags( 'a', $linkAttribs,  $msgbig ) . ' ' . $msgsize;
+                                               '<br />' . Xml::tags( 'a', $linkAttribs,  $msgbig ) . ' ' . $longDesc;
                                }
 
                                if ( $this->img->isMultipage() ) {
@@ -301,26 +307,17 @@ class ImagePage extends Article {
 
 
                        if ($showLink) {
-                               // Workaround for incorrect MIME type on SVGs uploaded in previous versions
-                               if ($mime == 'image/svg') $mime = 'image/svg+xml';
-
                                $filename = wfEscapeWikiText( $this->img->getName() );
                                $info = wfMsg( 'file-info', $sk->formatSize( $this->img->getSize() ), $mime );
-                               $infores = '';
-
-                               // Check for MIME type. Other types may have more information in the future.
-                               if (substr($mime,0,9) == 'image/svg' ) {
-                                       $infores = wfMsg('file-svg', $width_orig, $height_orig ) . '<br />';
-                               }
 
                                global $wgContLang;
                                $dirmark = $wgContLang->getDirMark();
                                if (!$this->img->isSafeFile()) {
                                        $warning = wfMsg( 'mediawarning' );
                                        $wgOut->addWikiText( <<<EOT
-<div class="fullMedia">$infores
+<div class="fullMedia">
 <span class="dangerousLink">[[Media:$filename|$filename]]</span>$dirmark
-<span class="fileInfo"> $info</span>
+<span class="fileInfo"> $longDesc</span>
 </div>
 
 <div class="mediaWarning">$warning</div>
@@ -328,8 +325,8 @@ EOT
                                                );
                                } else {
                                        $wgOut->addWikiText( <<<EOT
-<div class="fullMedia">$infores
-[[Media:$filename|$filename]]$dirmark <span class="fileInfo"> $info</span>
+<div class="fullMedia">
+[[Media:$filename|$filename]]$dirmark <span class="fileInfo"> $longDesc</span>
 </div>
 EOT
                                                );
@@ -421,18 +418,22 @@ EOT
 
                if ( $line ) {
                        $list = new ImageHistoryList( $sk, $this->img );
+                       $file = $this->repo->newFileFromRow( $line );
+                       $dims = $file->getDimensionsString();
                        $s = $list->beginImageHistoryList() .
                                $list->imageHistoryLine( true, wfTimestamp(TS_MW, $line->img_timestamp),
                                        $this->mTitle->getDBkey(),  $line->img_user,
                                        $line->img_user_text, $line->img_size, $line->img_description,
-                                       $line->img_width, $line->img_height
+                                       $dims
                                );
 
                        while ( $line = $this->img->nextHistoryLine() ) {
-                               $s .= $list->imageHistoryLine( false, $line->img_timestamp,
-                                       $line->oi_archive_name, $line->img_user,
-                                       $line->img_user_text, $line->img_size, $line->img_description,
-                                       $line->img_width, $line->img_height
+                               $file = $this->repo->newFileFromRow( $line );
+                               $dims = $file->getDimensionsString();
+                               $s .= $list->imageHistoryLine( false, $line->oi_timestamp,
+                                       $line->oi_archive_name, $line->oi_user,
+                                       $line->oi_user_text, $line->oi_size, $line->oi_description,
+                                       $dims
                                );
                        }
                        $s .= $list->endImageHistoryList();
@@ -655,7 +656,7 @@ EOT
  */
 class ImageHistoryList {
 
-       protected $img, $skin, $title;
+       protected $img, $skin, $title, $repo;
 
        public function __construct( $skin, $img ) {
                $this->skin = $skin;
@@ -682,7 +683,7 @@ class ImageHistoryList {
                return "</table>\n";
        }
 
-       public function imageHistoryLine( $iscur, $timestamp, $img, $user, $usertext, $size, $description, $width, $height ) {
+       public function imageHistoryLine( $iscur, $timestamp, $img, $user, $usertext, $size, $description, $dims ) {
                global $wgUser, $wgLang, $wgTitle, $wgContLang;
                $local = $this->img->isLocal();
                $row = '';
@@ -740,10 +741,7 @@ class ImageHistoryList {
                $row .= '</td>';
 
                // Image dimensions
-               // FIXME: It would be nice to show the duration (sound files) or
-               // width/height/duration (video files) here, but this needs some
-               // additional media handler work
-               $row .= '<td>' . wfMsgHtml( 'widthheight', $width, $height ) . '</td>';
+               $row .= '<td>' . htmlspecialchars( $dims ) . '</td>';
 
                // File size
                $row .= '<td class="mw-imagepage-filesize">' . $this->skin->formatSize( $size ) . '</td>';
@@ -754,4 +752,4 @@ class ImageHistoryList {
                return "<tr>{$row}</tr>\n";
        }
 
-}
\ No newline at end of file
+}
index e48bedf..de81233 100644 (file)
@@ -433,42 +433,96 @@ class Linker {
                return $s;
        }
 
-       /** Creates the HTML source for images
-       * @param object $nt
-       * @param string $label label text
-       * @param string $alt alt text
-       * @param string $align horizontal alignment: none, left, center, right)
-       * @param array $params some format keywords: width, height, page, upright, upright_factor, frameless, border
-       * @param boolean $framed shows image in original size in a frame
-       * @param boolean $thumb shows image as thumbnail in a frame
-       * @param string $manual_thumb image name for the manual thumbnail
-       * @param string $valign vertical alignment: baseline, sub, super, top, text-top, middle, bottom, text-bottom
-       * @return string
-       */
-       function makeImageLinkObj( $nt, $label, $alt, $align = '', $params = array(), $framed = false,
-         $thumb = false, $manual_thumb = '', $valign = '', $time = false )
+       /** 
+        * Creates the HTML source for images
+        * @deprecated use makeImageLink2
+        *
+        * @param object $title
+        * @param string $label label text
+        * @param string $alt alt text
+        * @param string $align horizontal alignment: none, left, center, right)
+        * @param array $handlerParams Parameters to be passed to the media handler
+        * @param boolean $framed shows image in original size in a frame
+        * @param boolean $thumb shows image as thumbnail in a frame
+        * @param string $manualthumb image name for the manual thumbnail
+        * @param string $valign vertical alignment: baseline, sub, super, top, text-top, middle, bottom, text-bottom
+        * @return string
+        */
+       function makeImageLinkObj( $title, $label, $alt, $align = '', $handlerParams = array(), $framed = false,
+         $thumb = false, $manualthumb = '', $valign = '', $time = false )
        {
+               $frameParams = array( 'alt' => $alt, 'caption' => $label );
+               if ( $align ) {
+                       $frameParams['align'] = $align;
+               }
+               if ( $framed ) {
+                       $frameParams['framed'] = true;
+               }
+               if ( $thumb ) {
+                       $frameParams['thumb'] = true;
+               }
+               if ( $manualthumb ) {
+                       $frameParams['manualthumb'] = $manualthumb;
+               }
+               if ( $valign ) {
+                       $frameParams['valign'] = $valign;
+               }
+               $file = wfFindFile( $title, $time );
+               return $this->makeImageLink2( $title, $file, $label, $alt, $frameParams, $handlerParams );
+       }
+
+       /**
+        * Make an image link
+        * @param Title $title Title object
+        * @param File $file File object, or false if it doesn't exist
+        *
+     * @param array $frameParams Associative array of parameters external to the media handler.
+     *     Boolean parameters are indicated by presence or absence, the value is arbitrary and 
+     *     will often be false.
+     *          thumbnail       If present, downscale and frame
+     *          manualthumb     Image name to use as a thumbnail, instead of automatic scaling
+     *          framed          Shows image in original size in a frame
+     *          frameless       Downscale but don't frame
+     *          upright         If present, tweak default sizes for portrait orientation
+     *          upright_factor  Fudge factor for "upright" tweak (default 0.75)
+     *          border          If present, show a border around the image
+     *          align           Horizontal alignment (left, right, center, none)
+     *          valign          Vertical alignment (baseline, sub, super, top, text-top, middle, 
+     *                          bottom, text-bottom)
+     *          alt             Alternate text for image (i.e. alt attribute). Plain text.
+     *          caption         HTML for image caption.
+        *
+     * @param array $handlerParams Associative array of media handler parameters, to be passed 
+     *       to transform(). Typical keys are "width" and "page". 
+        */
+       function makeImageLink2( Title $title, $file, $frameParams = array(), $handlerParams = array() ) {
                global $wgContLang, $wgUser, $wgThumbLimits, $wgThumbUpright;
+               if ( $file && !$file->allowInlineDisplay() ) {
+                       wfDebug( __METHOD__.': '.$title->getPrefixedDBkey()." does not allow inline display\n" );
+                       return $this->makeKnownLinkObj( $title );
+               }
 
-               $img = wfFindFile( $nt, $time );
+               // Shortcuts
+               $fp =& $frameParams;
+               $hp =& $handlerParams;
 
-               if ( $img && !$img->allowInlineDisplay() ) {
-                       wfDebug( __METHOD__.': '.$nt->getPrefixedDBkey()." does not allow inline display\n" );
-                       return $this->makeKnownLinkObj( $nt );
-               }
+               // Clean up parameters
+               $page = isset( $hp['page'] ) ? $hp['page'] : false;
+               if ( !isset( $fp['align'] ) ) $fp['align'] = '';
+               if ( !isset( $fp['alt'] ) ) $fp['alt'] = '';
 
-               $error = $prefix = $postfix = '';
-               $page = isset( $params['page'] ) ? $params['page'] : false;
+               $prefix = $postfix = '';
 
-               if ( 'center' == $align )
+               if ( 'center' == $fp['align'] )
                {
                        $prefix  = '<div class="center">';
                        $postfix = '</div>';
-                       $align   = 'none';
+                       $fp['align']   = 'none';
                }
-               if ( $img && !isset( $params['width'] ) ) {
-                       $params['width'] = $img->getWidth( $page );
-                       if( $thumb || $framed || isset( $params['frameless'] ) ) {
+               if ( $file && !isset( $hp['width'] ) ) {
+                       $hp['width'] = $file->getWidth( $page );
+
+                       if( isset( $fp['thumbnail'] ) || isset( $fp['framed'] ) || isset( $fp['frameless'] ) || !$hp['width'] ) {
                                $wopt = $wgUser->getOption( 'thumbsize' );
 
                                if( !isset( $wgThumbLimits[$wopt] ) ) {
@@ -476,16 +530,21 @@ class Linker {
                                }
 
                                // Reduce width for upright images when parameter 'upright' is used
-                               if ( !isset( $params['upright_factor'] ) || $params['upright_factor'] == 0 ) {
-                                       $params['upright_factor'] = $wgThumbUpright;
+                               if ( !isset( $fp['upright_factor'] ) || $fp['upright_factor'] == 0 ) {
+                                       $fp['upright_factor'] = $wgThumbUpright;
                                }
                                // Use width which is smaller: real image width or user preference width
                                // For caching health: If width scaled down due to upright parameter, round to full __0 pixel to avoid the creation of a lot of odd thumbs
-                               $params['width'] = min( $params['width'], isset( $params['upright'] ) ? round( $wgThumbLimits[$wopt] * $params['upright_factor'], -1 ) : $wgThumbLimits[$wopt] );
+                               $prefWidth = isset( $fp['upright'] ) ? 
+                                       round( $wgThumbLimits[$wopt] * $fp['upright_factor'], -1 ) : 
+                                       $wgThumbLimits[$wopt];
+                               if ( $hp['width'] <= 0 || $prefWidth < $hp['width'] ) {
+                                       $hp['width'] = $prefWidth;
+                               }
                        }
                }
 
-               if ( $thumb || $framed ) {
+               if ( isset( $fp['thumbnail'] ) || isset( $fp['framed'] ) ) {
 
                        # Create a thumbnail. Alignment depends on language
                        # writing direction, # right aligned for left-to-right-
@@ -494,15 +553,15 @@ class Linker {
                        #
                        # If  thumbnail width has not been provided, it is set
                        # to the default user option as specified in Language*.php
-                       if ( $align == '' ) {
-                               $align = $wgContLang->isRTL() ? 'left' : 'right';
+                       if ( $fp['align'] == '' ) {
+                               $fp['align'] = $wgContLang->isRTL() ? 'left' : 'right';
                        }
-                       return $prefix.$this->makeThumbLinkObj( $nt, $img, $label, $alt, $align, $params, $framed, $manual_thumb ).$postfix;
+                       return $prefix.$this->makeThumbLink2( $title, $file, $fp, $hp ).$postfix;
                }
 
-               if ( $img && $params['width'] ) {
+               if ( $file && $hp['width'] ) {
                        # Create a resized image, without the additional thumbnail features
-                       $thumb = $img->transform( $params );
+                       $thumb = $file->transform( $hp );
                } else {
                        $thumb = false;
                }
@@ -512,58 +571,76 @@ class Linker {
                } else {
                        $query = '';
                }
-               $u = $nt->getLocalURL( $query );
+               $url = $title->getLocalURL( $query );
                $imgAttribs = array(
-                       'alt' => $alt,
-                       'longdesc' => $u
+                       'alt' => $fp['alt'],
+                       'longdesc' => $url
                );
 
-               if ( $valign ) {
-                       $imgAttribs['style'] = "vertical-align: $valign";
+               if ( isset( $fp['valign'] ) ) {
+                       $imgAttribs['style'] = "vertical-align: {$fp['valign']}";
                }
-               if ( isset( $params['border'] ) ) {
+               if ( isset( $fp['border'] ) ) {
                        $imgAttribs['class'] = "thumbborder";
                }
                $linkAttribs = array(
-                       'href' => $u,
+                       'href' => $url,
                        'class' => 'image',
-                       'title' => $alt
+                       'title' => $fp['alt']
                );
 
                if ( !$thumb ) {
-                       $s = $this->makeBrokenImageLinkObj( $nt );
+                       $s = $this->makeBrokenImageLinkObj( $title );
                } else {
                        $s = $thumb->toHtml( $imgAttribs, $linkAttribs );
                }
-               if ( '' != $align ) {
-                       $s = "<div class=\"float{$align}\"><span>{$s}</span></div>";
+               if ( '' != $fp['align'] ) {
+                       $s = "<div class=\"float{$fp['align']}\"><span>{$s}</span></div>";
                }
                return str_replace("\n", ' ',$prefix.$s.$postfix);
        }
 
        /**
         * Make HTML for a thumbnail including image, border and caption
-        * @param Title $nt 
-        * @param Image $img Image object or false if it doesn't exist
+        * @param Title $title 
+        * @param File $file File object or false if it doesn't exist
         */
-       function makeThumbLinkObj( Title $nt, $img, $label = '', $alt, $align = 'right', $params = array(), $framed=false , $manual_thumb = "" ) {
+       function makeThumbLinkObj( Title $title, $file, $label = '', $alt, $align = 'right', $params = array(), $framed=false , $manualthumb = "" ) {
+               $frameParams = array( 
+                       'alt' => $alt,
+                       'caption' => $label,
+                       'align' => $align
+               );
+               if ( $framed ) $frameParams['framed'] = true;
+               if ( $manualthumb ) $frameParams['manualthumb'] = $manualthumb;
+               return $this->makeThumbLink2( $title, $file, $frameParams, $handlerParams );
+       }
+
+       function makeThumbLink2( Title $title, $file, $frameParams = array(), $handlerParams = array() ) {
                global $wgStylePath, $wgContLang;
-               $exists = $img && $img->exists();
+               $exists = $file && $file->exists();
 
-               $page = isset( $params['page'] ) ? $params['page'] : false;
+               # Shortcuts
+               $fp =& $frameParams;
+               $hp =& $handlerParams;
 
-               if ( empty( $params['width'] ) ) {
+               $page = isset( $hp['page'] ) ? $hp['page'] : false;
+               if ( !isset( $fp['align'] ) ) $fp['align'] = 'right';
+               if ( !isset( $fp['alt'] ) ) $fp['alt'] = '';
+               if ( !isset( $fp['caption'] ) ) $fp['caption'] = '';
+
+               if ( empty( $hp['width'] ) ) {
                        // Reduce width for upright images when parameter 'upright' is used 
-                       $params['width'] = isset( $params['upright'] ) ? 130 : 180;
+                       $hp['width'] = isset( $fp['upright'] ) ? 130 : 180;
                }
                $thumb = false;
 
                if ( !$exists ) {
-                       $outerWidth = $params['width'] + 2;
+                       $outerWidth = $hp['width'] + 2;
                } else {
-                       if ( $manual_thumb != '' ) {
+                       if ( isset( $fp['manualthumb'] ) ) {
                                # Use manually specified thumbnail
-                               $manual_title = Title::makeTitleSafe( NS_IMAGE, $manual_thumb );
+                               $manual_title = Title::makeTitleSafe( NS_IMAGE, $fp['manualthumb'] );
                                if( $manual_title ) {
                                        $manual_img = wfFindFile( $manual_title );
                                        if ( $manual_img ) {
@@ -572,63 +649,63 @@ class Linker {
                                                $exists = false;
                                        }
                                }
-                       } elseif ( $framed ) {
+                       } elseif ( isset( $fp['framed'] ) ) {
                                // Use image dimensions, don't scale
-                               $thumb = $img->getUnscaledThumb( $page );
+                               $thumb = $file->getUnscaledThumb( $page );
                        } else {
                                # Do not present an image bigger than the source, for bitmap-style images
                                # This is a hack to maintain compatibility with arbitrary pre-1.10 behaviour
-                               $srcWidth = $img->getWidth( $page );
-                               if ( $srcWidth && !$img->mustRender() && $params['width'] > $srcWidth ) {
-                                       $params['width'] = $srcWidth;
+                               $srcWidth = $file->getWidth( $page );
+                               if ( $srcWidth && !$file->mustRender() && $hp['width'] > $srcWidth ) {
+                                       $hp['width'] = $srcWidth;
                                }
-                               $thumb = $img->transform( $params );
+                               $thumb = $file->transform( $hp );
                        }
 
                        if ( $thumb ) {
                                $outerWidth = $thumb->getWidth() + 2;
                        } else {
-                               $outerWidth = $params['width'] + 2;
+                               $outerWidth = $hp['width'] + 2;
                        }
                }
 
                $query = $page ? 'page=' . urlencode( $page ) : '';
-               $u = $nt->getLocalURL( $query );
+               $url = $title->getLocalURL( $query );
 
                $more = htmlspecialchars( wfMsg( 'thumbnail-more' ) );
                $magnifyalign = $wgContLang->isRTL() ? 'left' : 'right';
                $textalign = $wgContLang->isRTL() ? ' style="text-align:right"' : '';
 
-               $s = "<div class=\"thumb t{$align}\"><div class=\"thumbinner\" style=\"width:{$outerWidth}px;\">";
+               $s = "<div class=\"thumb t{$fp['align']}\"><div class=\"thumbinner\" style=\"width:{$outerWidth}px;\">";
                if( !$exists ) {
-                       $s .= $this->makeBrokenImageLinkObj( $nt );
+                       $s .= $this->makeBrokenImageLinkObj( $title );
                        $zoomicon = '';
                } elseif ( !$thumb ) {
                        $s .= htmlspecialchars( wfMsg( 'thumbnail_error', '' ) );
                        $zoomicon = '';
                } else {
                        $imgAttribs = array(
-                               'alt' => $alt,
-                               'longdesc' => $u,
+                               'alt' => $fp['alt'],
+                               'longdesc' => $url,
                                'class' => 'thumbimage'
                        );
                        $linkAttribs = array(
-                               'href' => $u,
+                               'href' => $url,
                                'class' => 'internal',
-                               'title' => $alt
+                               'title' => $fp['alt']
                        );
-                               
+                       
                        $s .= $thumb->toHtml( $imgAttribs, $linkAttribs );
-                       if ( $framed ) {
+                       if ( isset( $fp['framed'] ) ) {
                                $zoomicon="";
                        } else {
                                $zoomicon =  '<div class="magnify" style="float:'.$magnifyalign.'">'.
-                                       '<a href="'.$u.'" class="internal" title="'.$more.'">'.
+                                       '<a href="'.$url.'" class="internal" title="'.$more.'">'.
                                        '<img src="'.$wgStylePath.'/common/images/magnify-clip.png" ' .
                                        'width="15" height="11" alt="" /></a></div>';
                        }
                }
-               $s .= '  <div class="thumbcaption"'.$textalign.'>'.$zoomicon.$label."</div></div></div>";
+               $s .= '  <div class="thumbcaption"'.$textalign.'>'.$zoomicon.$fp['caption']."</div></div></div>";
                return str_replace("\n", ' ', $s);
        }
 
@@ -1269,28 +1346,7 @@ class Linker {
         */
        public function formatSize( $size ) {
                global $wgLang;
-               // For small sizes no decimal places necessary
-               $round = 0;
-               if( $size > 1024 ) {
-                       $size = $size / 1024;
-                       if( $size > 1024 ) {
-                               $size = $size / 1024;
-                               // For MB and bigger two decimal places are smarter
-                               $round = 2;
-                               if( $size > 1024 ) {
-                                       $size = $size / 1024;
-                                       $msg = 'size-gigabytes';
-                               } else {
-                                       $msg = 'size-megabytes';
-                               }
-                       } else {
-                               $msg = 'size-kilobytes';
-                       }
-               } else {
-                       $msg = 'size-bytes';
-               }
-               $size = round( $size, $round );
-               return wfMsgHtml( $msg, $wgLang->formatNum( $size ) );
+               return htmlspecialchars( $wgLang->formatSize( $size ) );
        }
 
        /**
@@ -1346,3 +1402,4 @@ class Linker {
 
 
 
+
index 2ad02e4..9e8d025 100644 (file)
@@ -386,6 +386,181 @@ class MagicWord {
        function isCaseSensitive() {
                return $this->mCaseSensitive;
        }
+
+       function getId() {
+               return $this->mId;
+       }
 }
 
+/**
+ * Class for handling an array of magic words
+ */
+class MagicWordArray {
+       var $names = array();
+       var $hash;
+       var $baseRegex, $regex;
+
+       function __construct( $names = array() ) {
+               $this->names = $names;
+       }
+
+       /**
+        * Add a magic word by name
+        */
+       public function add( $name ) {
+               global $wgContLang;
+               $this->names[] = $name;
+               $this->hash = $this->baseRegex = $this->regex = null;
+       }
+
+       /**
+        * Add a number of magic words by name
+        */
+       public function addArray( $names ) {
+               $this->names = array_merge( $this->names, array_values( $names ) );
+               $this->hash = $this->baseRegex = $this->regex = null;
+       }
+
+       /**
+        * Get a 2-d hashtable for this array
+        */
+       function getHash() {
+               if ( is_null( $this->hash ) ) {
+                       global $wgContLang;
+                       $this->hash = array( 0 => array(), 1 => array() );
+                       foreach ( $this->names as $name ) {
+                               $magic = MagicWord::get( $name );
+                               $case = intval( $magic->isCaseSensitive() );
+                               foreach ( $magic->getSynonyms() as $syn ) {
+                                       if ( !$case ) {
+                                               $syn = $wgContLang->lc( $syn );
+                                       }
+                                       $this->hash[$case][$syn] = $name;
+                               }
+                       }
+               }
+               return $this->hash;
+       }
+
+       /**
+        * Get the base regex
+        */
+       function getBaseRegex() {
+               if ( is_null( $this->baseRegex ) ) {
+                       $this->baseRegex = array( 0 => '', 1 => '' );
+                       foreach ( $this->names as $name ) {
+                               $magic = MagicWord::get( $name );
+                               $case = intval( $magic->isCaseSensitive() );
+                               foreach ( $magic->getSynonyms() as $i => $syn ) {
+                                       $group = "(?P<{$i}_{$name}>" . preg_quote( $syn, '/' ) . ')';
+                                       if ( $this->baseRegex[$case] === '' ) {
+                                               $this->baseRegex[$case] = $group;
+                                       } else {
+                                               $this->baseRegex[$case] .= '|' . $group;
+                                       }
+                               }
+                       }
+               }
+               return $this->baseRegex;
+       }
+
+       /**
+        * Get an unanchored regex
+        */
+       function getRegex() {
+               if ( is_null( $this->regex ) ) {
+                       $base = $this->getBaseRegex();
+                       $this->regex = array( '', '' );
+                       if ( $this->baseRegex[0] !== '' ) {
+                               $this->regex[0] = "/{$base[0]}/iuS";
+                       }
+                       if ( $this->baseRegex[1] !== '' ) {
+                               $this->regex[1] = "/{$base[1]}/S";
+                       }
+               }
+               return $this->regex;
+       }
+
+       /**
+        * Get a regex for matching variables
+        */
+       function getVariableRegex() {
+               return str_replace( "\\$1", "(.*?)", $this->getRegex() );
+       }
+
+       /**
+        * Get an anchored regex for matching variables
+        */
+       function getVariableStartToEndRegex() {
+               $base = $this->getBaseRegex();
+               $newRegex = array( '', '' );
+               if ( $base[0] !== '' ) {
+                       $newRegex[0] = str_replace( "\\$1", "(.*?)", "/^(?:{$base[0]})$/iuS" );
+               }
+               if ( $base[1] !== '' ) {
+                       $newRegex[1] = str_replace( "\\$1", "(.*?)", "/^(?:{$base[1]})$/S" );
+               }
+               return $newRegex;
+       }
+
+       /**
+        * Parse a match array from preg_match
+        */
+       function parseMatch( $m ) {
+               reset( $m );
+               while ( list( $key, $value ) = each( $m ) ) {
+                       if ( $key === 0 || $value === '' ) {
+                               continue;
+                       }
+                       $parts = explode( '_', $key, 2 );
+                       if ( count( $parts ) != 2 ) {
+                               // This shouldn't happen
+                               // continue;
+                               throw new MWException( __METHOD__ . ': bad parameter name' );
+                       }
+                       list( $synIndex, $magicName ) = $parts;
+                       $paramValue = next( $m );
+                       return array( $magicName, $paramValue );
+               }
+               // This shouldn't happen either
+               throw new MWException( __METHOD__.': parameter not found' );
+               return array( false, false );
+       }
+
+       /**
+        * Match some text, with parameter capture
+        * Returns an array with the magic word name in the first element and the 
+        * parameter in the second element.
+        * Both elements are false if there was no match.
+        */
+       public function matchVariableStartToEnd( $text ) {
+               global $wgContLang;
+               $regexes = $this->getVariableStartToEndRegex();
+               foreach ( $regexes as $case => $regex ) {
+                       if ( $regex !== '' ) {
+                               $m = false;
+                               if ( preg_match( $regex, $text, $m ) ) {
+                                       return $this->parseMatch( $m );
+                               }
+                       }
+               }
+               return array( false, false );
+       }
 
+       /**
+        * Match some text, without parameter capture
+        * Returns the magic word name, or false if there was no capture
+        */
+       public function matchStartToEnd( $text ) {
+               $hash = $this->getHash();
+               if ( isset( $hash[1][$text] ) ) {
+                       return $hash[1][$text];
+               }
+               global $wgContLang;
+               $lc = $wgContLang->lc( $text );
+               if ( isset( $hash[0][$lc] ) ) {
+                       return $hash[0][$lc];
+               }
+               return false;
+       }
+}
index 277d155..f06f44d 100644 (file)
@@ -23,6 +23,7 @@ class MessageCache {
        var $mExtensionMessages = array();
        var $mInitialised = false;
        var $mDeferred = true;
+       var $mAllMessagesLoaded;
 
        function __construct( &$memCached, $useDB, $expiry, $memcPrefix) {
                wfProfileIn( __METHOD__ );
@@ -669,12 +670,33 @@ class MessageCache {
                }
        }
 
-       static function loadAllMessages() {
+       function loadAllMessages() {
+               global $wgExtensionMessagesFiles;
+               if ( $this->mAllMessagesLoaded ) {
+                       return;
+               }
+               $this->mAllMessagesLoaded = true;
+
                # Some extensions will load their messages when you load their class file
                wfLoadAllExtensions();
                # Others will respond to this hook
                wfRunHooks( 'LoadAllMessages' );
+               # Some register their messages in $wgExtensionMessagesFiles
+               foreach ( $wgExtensionMessagesFiles as $name => $file ) {
+                       if ( $file ) {
+                               $this->loadMessagesFile( $file );
+                               $wgExtensionMessagesFiles[$name] = false;
+                       }
+               }
                # Still others will respond to neither, they are EVIL. We sometimes need to know!
        }
+
+       /**
+        * Load messages from a given file
+        */
+       function loadMessagesFile( $filename ) {
+               require( $filename );
+               $this->addMessagesByLang( $messages );
+       }
 }
 
index c4c6cf1..264a359 100644 (file)
@@ -374,7 +374,9 @@ class MimeMagic {
                $mime = $this->detectMimeType( $file, $ext );
 
                // Read a chunk of the file
+               wfSuppressWarnings();
                $f = fopen( $file, "rt" );
+               wfRestoreWarnings();
                if( !$f ) return "unknown/unknown";
                $head = fread( $f, 1024 );
                fclose( $f );
index 20ca440..30cfd7d 100644 (file)
@@ -109,6 +109,10 @@ class OutputPage {
                $this->mHeadItems[$name] = $value;
        }
 
+       function hasHeadItem( $name ) {
+               return isset( $this->mHeadItems[$name] );
+       }
+
        function setETag($tag) { $this->mETag = $tag; }
        function setArticleBodyOnly($only) { $this->mArticleBodyOnly = $only; }
        function getArticleBodyOnly($only) { return $this->mArticleBodyOnly; }
@@ -377,7 +381,16 @@ class OutputPage {
                # Display title
                if( ( $dt = $parserOutput->getDisplayTitle() ) !== false )
                        $this->setPageTitle( $dt );
-               
+
+               # Hooks registered in the object
+               global $wgParserOutputHooks;
+               foreach ( $parserOutput->getOutputHooks() as $hookInfo ) {
+                       list( $hookName, $data ) = $hookInfo;
+                       if ( isset( $wgParserOutputHooks[$hookName] ) ) {
+                               call_user_func( $wgParserOutputHooks[$hookName], $this, $parserOutput, $data );
+                       }
+               }
+
                wfRunHooks( 'OutputPageParserOutput', array( &$this, $parserOutput ) );
        }
 
index d08a369..d1eb156 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;
@@ -4473,10 +4474,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 +4541,55 @@ 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;
+               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;
                        } 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
-                                       $m = array();
-                                       if ( preg_match( '/^([0-9]*)x([0-9]*)$/', $match, $m ) ) {
-                                                $params['width'] = intval( $m[1] );
-                                                $params['height'] = intval( $m[2] );
-                                       } else {
-                                               $params['width'] = intval($match);
-                                       }
-                               } elseif ( ! is_null( $mwFramed->matchVariableStartToEnd($val) ) ) {
-                                       $framed=true;
-                               } else {
-                                       $caption = $val;
+                               $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 +4599,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;
        }
 
        /**
index 1307169..8b9f659 100644 (file)
@@ -19,7 +19,8 @@ class ParserOutput
                $mExternalLinks,    # External link URLs, in the key only
                $mNewSection,       # Show a new section link?
                $mNoGallery,        # No gallery on category page? (__NOGALLERY__)
-               $mHeadItems;        # Items to put in the <head> section
+               $mHeadItems,        # Items to put in the <head> section
+               $mOutputHooks;      # Hook tags as per $wgParserOutputHooks
        
        /**
         * Overridden title for display
@@ -44,6 +45,7 @@ class ParserOutput
                $this->mNoGallery = false;
                $this->mHeadItems = array();
                $this->mTemplateIds = array();
+               $this->mOutputHooks = array();
        }
 
        function getText()                   { return $this->mText; }
@@ -58,6 +60,7 @@ class ParserOutput
        function &getExternalLinks()         { return $this->mExternalLinks; }
        function getNoGallery()              { return $this->mNoGallery; }
        function getSubtitle()               { return $this->mSubtitle; }
+       function getOutputHooks()            { return $this->mOutputHooks; }
 
        function containsOldMagic()          { return $this->mContainsOldMagic; }
        function setText( $text )            { return wfSetVar( $this->mText, $text ); }
@@ -71,6 +74,10 @@ class ParserOutput
        function addLanguageLink( $t )       { $this->mLanguageLinks[] = $t; }
        function addExternalLink( $url )     { $this->mExternalLinks[$url] = 1; }
 
+       function addOutputHook( $hook, $data = false ) { 
+               $this->mOutputHooks[] = array( $hook, $data );
+       }
+
        function setNewSection( $value ) {
                $this->mNewSection = (bool)$value;
        }
index 017bbda..4ba01e2 100644 (file)
@@ -25,7 +25,8 @@ function wfSpecialAllmessages() {
        $navText = wfMsg( 'allmessagestext' );
 
        # Make sure all extension messages are available
-       MessageCache::loadAllMessages();
+       
+       $wgMessageCache->loadAllMessages();
 
        $sortedArray = array_merge( Language::getMessagesFor( 'en' ), $wgMessageCache->getExtensionMessagesFor( 'en' ) );
        ksort( $sortedArray );
index d9d5e85..a893966 100644 (file)
@@ -8,9 +8,9 @@
  *
  */
 function wfSpecialSpecialpages() {
-       global $wgOut, $wgUser;
+       global $wgOut, $wgUser, $wgMessageCache;
 
-       MessageCache::loadAllMessages();
+       $wgMessageCache->loadAllMessages();
 
        $wgOut->setRobotpolicy( 'index,nofollow' );
        $sk = $wgUser->getSkin();
index 2ecac08..055a813 100644 (file)
@@ -2527,7 +2527,8 @@ class User {
         * @static
         */
        static function getGroupName( $group ) {
-               MessageCache::loadAllMessages();
+               global $wgMessageCache;
+               $wgMessageCache->loadAllMessages();
                $key = "group-$group";
                $name = wfMsg( $key );
                return $name == '' || wfEmptyMsg( $key, $name )
@@ -2541,7 +2542,8 @@ class User {
         * @static
         */
        static function getGroupMember( $group ) {
-               MessageCache::loadAllMessages();
+               global $wgMessageCache;
+               $wgMessageCache->loadAllMessages();
                $key = "group-$group-member";
                $name = wfMsg( $key );
                return $name == '' || wfEmptyMsg( $key, $name )
@@ -2586,7 +2588,8 @@ class User {
         * @return mixed
         */
        static function getGroupPage( $group ) {
-               MessageCache::loadAllMessages();
+               global $wgMessageCache;
+               $wgMessageCache->loadAllMessages();
                $page = wfMsgForContent( 'grouppage-' . $group );
                if( !wfEmptyMsg( 'grouppage-' . $group, $page ) ) {
                        $title = Title::newFromText( $page );
index a8bd9fb..84b64fb 100644 (file)
@@ -209,6 +209,18 @@ abstract class File {
         */
        public function getHeight( $page = 1 ) { return false; }
 
+       /**
+        * Get the duration of a media file in seconds
+        */
+       public function getLength() {
+               $handler = $this->getHandler();
+               if ( $handler ) {
+                       return $handler->getLength( $this );
+               } else {
+                       return 0;
+               }
+       }
+
        /**
         * Get handler-specific metadata
         * Overridden by LocalFile, UnregisteredLocalFile
@@ -239,7 +251,9 @@ abstract class File {
        function getMediaType() { return MEDIATYPE_UNKNOWN; }
 
        /**
-        * Checks if the file can be presented to the browser as a bitmap.
+        * Checks if the output of transform() for this file is likely 
+        * to be valid. If this is false, various user elements will 
+        * display a placeholder instead.
         *
         * Currently, this checks if the file is an image format
         * that can be converted to a format
@@ -248,7 +262,7 @@ abstract class File {
         */
        function canRender() {
                if ( !isset( $this->canRender ) ) {
-                       $this->canRender = $this->getHandler() && $this->handler->canRender();
+                       $this->canRender = $this->getHandler() && $this->handler->canRender( $this );
                }
                return $this->canRender;
        }
@@ -271,16 +285,11 @@ abstract class File {
         * @return bool
         */
        function mustRender() {
-               return $this->getHandler() && $this->handler->mustRender();
+               return $this->getHandler() && $this->handler->mustRender( $this );
        }
 
        /**
-        * Determines if this media file may be shown inline on a page.
-        *
-        * This is currently synonymous to canRender(), but this could be
-        * extended to also allow inline display of other media,
-        * like flash animations or videos. If you do so, please keep in mind that
-        * that could be a security risk.
+        * Alias for canRender()
         */
        function allowInlineDisplay() {
                return $this->canRender();
@@ -470,7 +479,7 @@ abstract class File {
 
                wfProfileIn( __METHOD__ );
                do {
-                       if ( !$this->getHandler() || !$this->handler->canRender() ) {
+                       if ( !$this->canRender() ) {
                                // not a bitmap or renderable image, don't try.
                                $thumb = $this->iconThumb();
                                break;
@@ -906,7 +915,7 @@ abstract class File {
         * @return Bool
         */
        function isMultipage() {
-               return $this->getHandler() && $this->handler->isMultiPage();
+               return $this->getHandler() && $this->handler->isMultiPage( $this );
        }
 
        /**
@@ -915,7 +924,7 @@ abstract class File {
         */
        function pageCount() {
                if ( !isset( $this->pageCount ) ) {
-                       if ( $this->getHandler() && $this->handler->isMultiPage() ) {
+                       if ( $this->getHandler() && $this->handler->isMultiPage( $this ) ) {
                                $this->pageCount = $this->handler->pageCount( $this );
                        } else {
                                $this->pageCount = false;
@@ -1014,7 +1023,9 @@ abstract class File {
        static function getPropsFromPath( $path, $ext = true ) {
                wfProfileIn( __METHOD__ );
                wfDebug( __METHOD__.": Getting file info for $path\n" );
-               $info = array( 'fileExists' => file_exists( $path ) );
+               $info = array( 
+                       'fileExists' => file_exists( $path ) && !is_dir( $path )
+               );
                $gis = false;
                if ( $info['fileExists'] ) {
                        $magic = MimeMagic::singleton();
@@ -1030,8 +1041,8 @@ abstract class File {
                        $handler = MediaHandler::getHandler( $info['mime'] );
                        if ( $handler ) {
                                $tempImage = (object)array();
-                               $gis = $handler->getImageSize( $tempImage, $path );
                                $info['metadata'] = $handler->getMetadata( $tempImage, $path );
+                               $gis = $handler->getImageSize( $tempImage, $path, $info['metadata'] );
                        } else {
                                $gis = false;
                                $info['metadata'] = '';
@@ -1074,13 +1085,42 @@ abstract class File {
         * Returns false on failure
         */
        static function sha1Base36( $path ) {
+               wfSuppressWarnings();
                $hash = sha1_file( $path );
+               wfRestoreWarnings();
                if ( $hash === false ) {
                        return false;
                } else {
                        return wfBaseConvert( $hash, 16, 36, 31 );
                }
        }
+
+       function getLongDesc() {
+               $handler = $this->getHandler();
+               if ( $handler ) {
+                       return $handler->getLongDesc( $this );
+               } else {
+                       return MediaHandler::getLongDesc( $this );
+               }
+       }
+
+       function getShortDesc() {
+               $handler = $this->getHandler();
+               if ( $handler ) {
+                       return $handler->getShortDesc( $this );
+               } else {
+                       return MediaHandler::getShortDesc( $this );
+               }
+       }
+
+       function getDimensionsString() {
+               $handler = $this->getHandler();
+               if ( $handler ) {
+                       return $handler->getDimensionsString( $this );
+               } else {
+                       return '';
+               }
+       }
 }
 /**
  * Aliases for backwards compatibility with 1.6
@@ -1090,3 +1130,4 @@ define( 'MW_IMG_DELETED_COMMENT', File::DELETED_COMMENT );
 define( 'MW_IMG_DELETED_USER', File::DELETED_USER );
 define( 'MW_IMG_DELETED_RESTRICTED', File::DELETED_RESTRICTED );
 
+
index 8a6b3de..44000e4 100644 (file)
@@ -39,7 +39,7 @@ class LocalFile extends File
                $media_type,    # MEDIATYPE_xxx (bitmap, drawing, audio...)
                $mime,          # MIME type, determined by MimeMagic::guessMimeType
                $major_mime,    # Major mime type
-               $minor_mine,    # Minor mime type
+               $minor_mime,    # Minor mime type
                $size,          # Size in bytes (loadFromXxx)
                $metadata,      # Handler-specific metadata
                $timestamp,     # Upload timestamp
@@ -231,6 +231,7 @@ class LocalFile extends File
         * Load file metadata from a DB result row
         */
        function loadFromRow( $row, $prefix = 'img_' ) {
+               $this->dataLoaded = true;
                $array = $this->decodeRow( $row, $prefix );
                foreach ( $array as $name => $value ) {
                        $this->$name = $value;
@@ -287,6 +288,11 @@ class LocalFile extends File
 
                $this->loadFromFile();
 
+               # Don't destroy file info of missing files
+               if ( !$this->fileExists ) {
+                       wfDebug( __METHOD__.": file does not exist, aborting\n" );
+                       return;
+               }
                $dbw = $this->repo->getMasterDB();
                list( $major, $minor ) = self::splitMime( $this->mime );
 
@@ -318,6 +324,12 @@ class LocalFile extends File
                                $this->$field = $info[$field];
                        }
                }
+               // Fix up mime fields
+               if ( isset( $info['major_mime'] ) ) {
+                       $this->mime = "{$info['major_mime']}/{$info['minor_mime']}";
+               } elseif ( isset( $info['mime'] ) ) {
+                       list( $this->major_mime, $this->minor_mime ) = self::splitMime( $this->mime );
+               }
        }
 
        /** splitMime inherited */
@@ -560,14 +572,9 @@ class LocalFile extends File
                $dbr = $this->repo->getSlaveDB();
 
                if ( $this->historyLine == 0 ) {// called for the first time, return line from cur
-                       $this->historyRes = $dbr->select( 'image',
+                       $this->historyRes = $dbr->select( 'image', 
                                array(
-                                       'img_size',
-                                       'img_description',
-                                       'img_user','img_user_text',
-                                       'img_timestamp',
-                                       'img_width',
-                                       'img_height',
+                                       '*',
                                        "'' AS oi_archive_name"
                                ),
                                array( 'img_name' => $this->title->getDBkey() ),
@@ -580,17 +587,7 @@ class LocalFile extends File
                        }
                } else if ( $this->historyLine == 1 ) {
                        $dbr->freeResult($this->historyRes);
-                       $this->historyRes = $dbr->select( 'oldimage',
-                               array(
-                                       'oi_size AS img_size',
-                                       'oi_description AS img_description',
-                                       'oi_user AS img_user',
-                                       'oi_user_text AS img_user_text',
-                                       'oi_timestamp AS img_timestamp',
-                                       'oi_width as img_width',
-                                       'oi_height as img_height',
-                                       'oi_archive_name'
-                               ),
+                       $this->historyRes = $dbr->select( 'oldimage', '*', 
                                array( 'oi_name' => $this->title->getDBkey() ),
                                __METHOD__,
                                array( 'ORDER BY' => 'oi_timestamp DESC' )
index 4d9dffb..0e35df6 100644 (file)
@@ -118,7 +118,10 @@ class OldLocalFile extends LocalFile {
        }
 
        function saveToCache() {
-               // Cache the entire history of the image (up to MAX_CACHE_ROWS).
+               // If a timestamp was specified, cache the entire history of the image (up to MAX_CACHE_ROWS).
+               if ( is_null( $this->requestedTime ) ) {
+                       return;
+               }
                // This is expensive, so we only do it if $wgMemc is real
                global $wgMemc;
                if ( $wgMemc instanceof FakeMemcachedClient ) {
@@ -155,6 +158,7 @@ class OldLocalFile extends LocalFile {
 
        function loadFromDB() {
                wfProfileIn( __METHOD__ );
+               $this->dataLoaded = true;
                $dbr = $this->repo->getSlaveDB();
                $conds = array( 'oi_name' => $this->getName() );
                if ( is_null( $this->requestedTime ) ) {
@@ -169,7 +173,6 @@ class OldLocalFile extends LocalFile {
                } else {
                        $this->fileExists = false;
                }
-               $this->dataLoaded = true;
                wfProfileOut( __METHOD__ );
        }
 
@@ -178,8 +181,8 @@ class OldLocalFile extends LocalFile {
                $fields[] = $prefix . 'archive_name';
 
                // XXX: Temporary hack before schema update
-               $fields = array_diff( $fields, array( 
-                       'oi_media_type', 'oi_major_mime', 'oi_minor_mime', 'oi_metadata' ) );
+               //$fields = array_diff( $fields, array( 
+               //      'oi_media_type', 'oi_major_mime', 'oi_minor_mime', 'oi_metadata' ) );
                return $fields;
        }
 
@@ -193,11 +196,18 @@ class OldLocalFile extends LocalFile {
        
        function upgradeRow() {
                wfProfileIn( __METHOD__ );
-
                $this->loadFromFile();
+               
+               # Don't destroy file info of missing files
+               if ( !$this->fileExists ) {
+                       wfDebug( __METHOD__.": file does not exist, aborting\n" );
+                       wfProfileOut( __METHOD__ );
+                       return;
+               }
 
                $dbw = $this->repo->getMasterDB();
                list( $major, $minor ) = self::splitMime( $this->mime );
+               $mime = $this->mime;
 
                wfDebug(__METHOD__.': upgrading '.$this->archive_name." to the current schema\n");
                $dbw->update( 'oldimage',
@@ -205,10 +215,10 @@ class OldLocalFile extends LocalFile {
                                'oi_width' => $this->width,
                                'oi_height' => $this->height,
                                'oi_bits' => $this->bits,
-                               #'oi_media_type' => $this->media_type,
-                               #'oi_major_mime' => $major,
-                               #'oi_minor_mime' => $minor,
-                               #'oi_metadata' => $this->metadata,
+                               'oi_media_type' => $this->media_type,
+                               'oi_major_mime' => $major,
+                               'oi_minor_mime' => $minor,
+                               'oi_metadata' => $this->metadata,
                                'oi_sha1' => $this->sha1,
                        ), array( 
                                'oi_name' => $this->getName(), 
index 97ea88f..45664db 100644 (file)
@@ -17,6 +17,13 @@ class DjVuHandler extends ImageHandler {
        function mustRender() { return true; }
        function isMultiPage() { return true; }
 
+       function getParamMap() {
+               return array(
+                       'img_width' => 'width',
+                       'img_page' => 'page',
+               );
+       }
+
        function validateParam( $name, $value ) {
                if ( in_array( $name, array( 'width', 'height', 'page' ) ) ) {
                        if ( $value <= 0 ) {
index 95e8568..b97fa04 100644 (file)
@@ -36,6 +36,12 @@ abstract class MediaHandler {
                return self::$handlers[$class];
        }
 
+       /**
+        * Get an associative array mapping magic word IDs to parameter names.
+        * Will be used by the parser to identify parameters.
+        */
+       abstract function getParamMap();
+
        /*
         * Validate a thumbnail parameter at parse time. 
         * Return true to accept the parameter, and false to reject it.
@@ -126,20 +132,20 @@ abstract class MediaHandler {
        /**
         * True if the handled types can be transformed
         */
-       function canRender() { return true; }
+       function canRender( $file ) { return true; }
        /**
         * True if handled types cannot be displayed directly in a browser 
         * but can be rendered
         */
-       function mustRender() { return false; }
+       function mustRender( $file ) { return false; }
        /**
         * True if the type has multi-page capabilities
         */
-       function isMultiPage() { return false; }
+       function isMultiPage( $file ) { return false; }
        /**
         * Page count for a multi-page document, false if unsupported or unknown
         */
-       function pageCount() { return false; }
+       function pageCount( $file ) { return false; }
        /**
         * False if the handler is disabled for all files
         */
@@ -177,10 +183,18 @@ abstract class MediaHandler {
         *
         * The function should return false if there is no metadata to display.
         */
+
+       /**
+        * FIXME: I don't really like this interface, it's not very flexible
+        * I think the media handler should generate HTML instead. It can do 
+        * all the formatting according to some standard. That makes it possible
+        * to do things like visual indication of grouped and chained streams
+        * in ogg container files.
+        */
        function formatMetadata( $image, $metadata ) {
                return false;
        }
-       
+
        protected static function addMeta( &$array, $visibility, $type, $id, $value, $param = false ) {
                $array[$visibility][] = array(
                        'id' => "$type-$id",
@@ -189,6 +203,26 @@ abstract class MediaHandler {
                );
        }
 
+       function getShortDesc( $file ) {
+               global $wgLang;
+               $nbytes = '(' . wfMsgExt( 'nbytes', array( 'parsemag', 'escape' ),
+                       $wgLang->formatNum( $file->getSize() ) ) . ')';
+       }
+
+       function getLongDesc( $file ) {
+               global $wgUser;
+               $sk = $wgUser->getSkin();
+               return wfMsg( 'file-info', $sk->formatSize( $file->getSize() ), $file->getMimeType() );
+       }
+
+       function getDimensionsString() {
+               return '';
+       }
+
+       /**
+        * Modify the parser object post-transform
+        */
+       function parserTransformHook( $parser, $file ) {}
 }
 
 /**
@@ -197,6 +231,18 @@ abstract class MediaHandler {
  * @addtogroup Media
  */
 abstract class ImageHandler extends MediaHandler {
+       function canRender( $file ) {
+               if ( $file->getWidth() && $file->getHeight() ) {
+                       return true;
+               } else {
+                       return false;
+               }
+       }
+
+       function getParamMap() {
+               return array( 'img_width' => 'width' );
+       }
+
        function validateParam( $name, $value ) {
                if ( in_array( $name, array( 'width', 'height' ) ) ) {
                        if ( $value <= 0 ) {
@@ -325,6 +371,31 @@ abstract class ImageHandler extends MediaHandler {
                wfRestoreWarnings();
                return $gis;
        }
+
+       function getShortDesc( $file ) {
+               global $wgLang;
+               $nbytes = '(' . wfMsgExt( 'nbytes', array( 'parsemag', 'escape' ),
+                       $wgLang->formatNum( $file->getSize() ) ) . ')';
+               $widthheight = wfMsgHtml( 'widthheight', $file->getWidth(), $file->getHeight() );
+               
+               return "$widthheight ($nbytes)";
+       }
+
+       function getLongDesc( $file ) {
+               global $wgLang;
+               return wfMsgHtml('file-info-size', $file->getWidth(), $file->getHeight(), 
+                       $wgLang->formatSize( $file->getSize() ), $file->getMimeType() );
+       }
+
+       function getDimensionsString( $file ) {
+               $pages = $file->pageCount();
+               if ( $pages > 1 ) {
+                       return wfMsg( 'widthheightpage', $file->getWidth(), $file->getHeight(), $pages );
+               } else {
+                       return wfMsg( 'widthheight', $file->getWidth(), $file->getHeight() );
+               }
+       }
 }
 
 
+
index 0fee6cb..d279a1a 100644 (file)
@@ -14,7 +14,7 @@ class SvgHandler extends ImageHandler {
                }
        }
 
-       function mustRender() {
+       function mustRender( $file ) {
                return true;
        }
 
@@ -91,5 +91,12 @@ class SvgHandler extends ImageHandler {
        function getThumbType( $ext, $mime ) {
                return array( 'png', 'image/png' );
        }
+
+       function getLongDesc( $file ) {
+               global $wgLang;
+               return wfMsg( 'svg-long-desc', $file->getWidth(), $file->getHeight(), 
+                       $wgLang->formatSize( $file->getSize() ) );
+       }
 }
 
+
index 6862fd4..5f1435d 100644 (file)
@@ -1824,6 +1824,73 @@ class Language {
                wfProfileOut( __METHOD__ );
                return array( $wikiUpperChars, $wikiLowerChars );
        }
+
+       function formatTimePeriod( $seconds ) {
+               if ( $seconds < 10 ) {
+                       return $this->formatNum( sprintf( "%.1f", $seconds ) ) . wfMsg( 'seconds-abbrev' );
+               } elseif ( $seconds < 60 ) {
+                       return $this->formatNum( round( $seconds ) ) . wfMsg( 'seconds-abbrev' );
+               } elseif ( $seconds < 3600 ) {
+                       return $this->formatNum( floor( $seconds / 60 ) ) . wfMsg( 'minutes-abbrev' ) . 
+                               $this->formatNum( round( fmod( $seconds, 60 ) ) ) . wfMsg( 'seconds-abbrev' );
+               } else {
+                       $hours = floor( $seconds / 3600 );
+                       $minutes = floor( ( $seconds - $hours * 3600 ) / 60 );
+                       $secondsPart = round( $seconds - $hours * 3600 - $minutes * 60 );
+                       return $this->formatNum( $hours ) . wfMsg( 'hours-abbrev' ) . 
+                               $this->formatNum( $minutes ) . wfMsg( 'minutes-abbrev' ) .
+                               $this->formatNum( $secondsPart ) . wfMsg( 'seconds-abbrev' );
+               }
+       }
+
+       function formatBitrate( $bps ) {
+               $units = array( 'bps', 'kbps', 'Mbps', 'Gbps' );
+               if ( $bps <= 0 ) {
+                       return $this->formatNum( $bps ) . $units[0];
+               }
+               $unitIndex = floor( log10( $bps ) / 3 );
+               $mantissa = $bps / pow( 1000, $unitIndex );
+               if ( $mantissa < 10 ) {
+                       $mantissa = round( $mantissa, 1 );
+               } else {
+                       $mantissa = round( $mantissa );
+               }
+               return $this->formatNum( $mantissa ) . $units[$unitIndex];
+       }
+
+       /**
+        * Format a size in bytes for output, using an appropriate
+        * unit (B, KB, MB or GB) according to the magnitude in question
+        *
+        * @param $size Size to format
+        * @return string Plain text (not HTML)
+        */
+       function formatSize( $size ) {
+               // For small sizes no decimal places necessary
+               $round = 0;
+               if( $size > 1024 ) {
+                       $size = $size / 1024;
+                       if( $size > 1024 ) {
+                               $size = $size / 1024;
+                               // For MB and bigger two decimal places are smarter
+                               $round = 2;
+                               if( $size > 1024 ) {
+                                       $size = $size / 1024;
+                                       $msg = 'size-gigabytes';
+                               } else {
+                                       $msg = 'size-megabytes';
+                               }
+                       } else {
+                               $msg = 'size-kilobytes';
+                       }
+               } else {
+                       $msg = 'size-bytes';
+               }
+               $size = round( $size, $round );
+               $text = $this->getMessageFromDB( $msg );
+               return str_replace( '$1', $this->formatNum( $size ), $text );
+       }
 }
 
 
+
index d3e0cd4..c0e0126 100644 (file)
@@ -156,10 +156,10 @@ $magicWords = array(
        'img_page'            => array( 1, "صفحة=$1", "صفحة $1", "page=$1", "page $1" ),
        'img_border'          => array( 1, "حد", "حدود", "border" ),
        'img_top'             => array( 1, "أعلى", "top" ),
-       'img_text-top'        => array( 1, "نص_أعلى", "text-top" ),
+       'img_text_top'        => array( 1, "نص_أعلى", "text-top" ),
        'img_middle'          => array( 1, "وسط", "middle" ),
        'img_bottom'          => array( 1, "أسفل", "bottom" ),
-       'img_text-bottom'     => array( 1, "نص_أسفل", "text-bottom" ),
+       'img_text_bottom'     => array( 1, "نص_أسفل", "text-bottom" ),
        'int'                 => array( 0, "محتوى:", "INT:" ),
        'sitename'            => array( 1, "اسم_الموقع", "اسم_موقع", "SITENAME" ),
        'ns'                  => array( 0, "نط:", "NS:" ),
index bd38923..d127620 100644 (file)
@@ -114,10 +114,10 @@ $magicWords = array(
        'img_sub'                => array( 1, 'sub'                    ),
        'img_super'              => array( 1, 'super', 'sup'           ),
        'img_top'                => array( 1, 'top'                    ),
-       'img_text-top'           => array( 1, 'text-top'               ),
+       'img_text_top'           => array( 1, 'text-top'               ),
        'img_middle'             => array( 1, 'middle'                 ),
        'img_bottom'             => array( 1, 'bottom'                 ),
-       'img_text-bottom'        => array( 1, 'text-bottom'            ),
+       'img_text_bottom'        => array( 1, 'text-bottom'            ),
        'int'                    => array( 0, 'INT:', 'ВЪТР:'),
        'sitename'               => array( 1, 'SITENAME', 'ИМЕНАСАЙТА'),
        'ns'                     => array( 0, 'NS:', 'ИП:'                    ),
index d9e375a..7eb7ad3 100644 (file)
@@ -133,10 +133,10 @@ $bookstoreList = array(
        'img_sub'                => array( 1,    'sub'                    ),
        'img_super'              => array( 1,    'super', 'sup'           ),
        'img_top'                => array( 1,    'top'                    ),
-       'img_text-top'           => array( 1,    'text-top'               ),
+       'img_text_top'           => array( 1,    'text-top'               ),
        'img_middle'             => array( 1,    'middle'                 ),
        'img_bottom'             => array( 1,    'bottom'                 ),
-       'img_text-bottom'        => array( 1,    'text-bottom'            ),
+       'img_text_bottom'        => array( 1,    'text-bottom'            ),
        'int'                    => array( 0,    'INT:'                   ),
        'sitename'               => array( 1,    'SITENAME',         'NÁZEVSERVERU'       ),
        'ns'                     => array( 0,    'NS:'                    ),
index b87daae..4957158 100644 (file)
@@ -209,7 +209,9 @@ $bookstoreList = array(
 
 /**
  * Magic words
- * Customisable syntax for wikitext and elsewhere
+ * Customisable syntax for wikitext and elsewhere.
+ *
+ * IDs must be valid identifiers, they can't contain hyphens. 
  *
  * Note to translators:
  *   Please include the English words as synonyms.  This allows people
@@ -287,10 +289,10 @@ $magicWords = array(
        'img_sub'                => array( 1,    'sub'                    ),
        'img_super'              => array( 1,    'super', 'sup'           ),
        'img_top'                => array( 1,    'top'                    ),
-       'img_text-top'           => array( 1,    'text-top'               ),
+       'img_text_top'           => array( 1,    'text-top'               ),
        'img_middle'             => array( 1,    'middle'                 ),
        'img_bottom'             => array( 1,    'bottom'                 ),
-       'img_text-bottom'        => array( 1,    'text-bottom'            ),
+       'img_text_bottom'        => array( 1,    'text-bottom'            ),
        'int'                    => array( 0,    'INT:'                   ),
        'sitename'               => array( 1,    'SITENAME'               ),
        'ns'                     => array( 0,    'NS:'                    ),
@@ -2422,10 +2424,11 @@ All transwiki import actions are logged at the [[Special:Log/import|import log]]
 'imagemaxsize'         => 'Limit images on image description pages to:',
 'thumbsize'            => 'Thumbnail size:',
 'widthheight'          => '$1×$2', # only translate this message to other languages if you have to change it
+'widthheightpage'      => '$1×$2, $3 pages',
 'file-info'            => '(file size: $1, MIME type: $2)',
 'file-info-size'       => '($1 × $2 pixel, file size: $3, MIME type: $4)',
 'file-nohires'         => '<small>No higher resolution available.</small>',
-'file-svg'             => '<small>This is a lossless scalable vector image. Base size: $1 × $2 pixels.</small>',
+'svg-long-desc'        => '(SVG file, nominally $1 × $2 pixels, file size: $3)',
 'show-big-image'       => 'Full resolution',
 'show-big-image-thumb' => '<small>Size of this preview: $1 × $2 pixels</small>',
 
@@ -2434,6 +2437,12 @@ All transwiki import actions are logged at the [[Special:Log/import|import log]]
 'showhidebots'      => '($1 bots)',
 'noimages'          => 'Nothing to see.',
 
+'video-dims'           => '$1, $2×$3',
+# Used by Language::formatTimePeriod() to format lengths in the above messages
+'seconds-abbrev'       => 's',
+'minutes-abbrev'       => 'm',
+'hours-abbrev'         => 'h',
+
 # Bad image list
 'bad_image_list' => 'The format is as follows:
 
index 1980022..1d69460 100644 (file)
@@ -147,10 +147,10 @@ $magicWords = array(
        'img_sub'             => array( 1,                             'sub'                    ),
        'img_super'           => array( 1,                             'super', 'sup'           ),
        'img_top'             => array( 1, 'atas',                     'top'                    ),
-       'img_text-top'        => array( 1, 'atas-teks',                'text-top'               ),
+       'img_text_top'        => array( 1, 'atas-teks',                'text-top'               ),
        'img_middle'          => array( 1, 'tengah',                   'middle'                 ),
        'img_bottom'          => array( 1, 'bawah',                    'bottom'                 ),
-       'img_text-bottom'     => array( 1, 'bawah-teks',               'text-bottom'            ),
+       'img_text_bottom'     => array( 1, 'bawah-teks',               'text-bottom'            ),
        'int'                 => array( 0,                             'INT:'                   ),
        'sitename'            => array( 1, 'NAMASITUS',                'SITENAME'               ),
        'ns'                  => array( 0, 'RN:',                      'NS:'                    ),
index e807403..faaddf7 100644 (file)
@@ -223,10 +223,10 @@ $magicWords = array(
        'img_sub'                => array( 1,    'استىلىعى', 'است', 'sub'),
        'img_super'              => array( 1,    'ٷستٸلٸگٸ', 'ٷست', 'sup', 'super', 'sup' ),
        'img_top'                => array( 1,    'ٷستٸنە', 'top' ),
-       'img_text-top'           => array( 1,    'مٵتٸن-ٷستٸندە', 'text-top' ),
+       'img_text_top'           => array( 1,    'مٵتٸن-ٷستٸندە', 'text-top' ),
        'img_middle'             => array( 1,    'ارالىعىنا', 'middle' ),
        'img_bottom'             => array( 1,    'استىنا', 'bottom' ),
-       'img_text-bottom'        => array( 1,    'مٵتٸن-استىندا', 'text-bottom' ),
+       'img_text_bottom'        => array( 1,    'مٵتٸن-استىندا', 'text-bottom' ),
        'int'                    => array( 0,    'ٸشكٸ:', 'INT:' ),
        'sitename'               => array( 1,    'توراپاتاۋى', 'SITENAME' ),
        'ns'                     => array( 0,    'ەا:', 'ەسٸمايا:', 'NS:' ),
index f99bf6e..4721105 100644 (file)
@@ -215,10 +215,10 @@ $magicWords = array(
        'img_sub'                => array( 1,    'астылығы', 'аст', 'sub'),
        'img_super'              => array( 1,    'үстілігі', 'үст', 'sup', 'super', 'sup' ),
        'img_top'                => array( 1,    'үстіне', 'top' ),
-       'img_text-top'           => array( 1,    'мәтін-үстінде', 'text-top' ),
+       'img_text_top'           => array( 1,    'мәтін-үстінде', 'text-top' ),
        'img_middle'             => array( 1,    'аралығына', 'middle' ),
        'img_bottom'             => array( 1,    'астына', 'bottom' ),
-       'img_text-bottom'        => array( 1,    'мәтін-астында', 'text-bottom' ),
+       'img_text_bottom'        => array( 1,    'мәтін-астында', 'text-bottom' ),
        'int'                    => array( 0,    'ІШКІ:', 'INT:' ),
        'sitename'               => array( 1,    'ТОРАПАТАУЫ', 'SITENAME' ),
        'ns'                     => array( 0,    'ЕА:', 'ЕСІМАЯ:', 'NS:' ),
index b7ae471..33d1de4 100644 (file)
@@ -216,10 +216,10 @@ $magicWords = array(
        'img_sub'                => array( 1,    'astılığı', 'ast', 'sub'),
        'img_super'              => array( 1,    'üstiligi', 'üst', 'sup', 'super', 'sup' ),
        'img_top'                => array( 1,    'üstine', 'top' ),
-       'img_text-top'           => array( 1,    'mätin-üstinde', 'text-top' ),
+       'img_text_top'           => array( 1,    'mätin-üstinde', 'text-top' ),
        'img_middle'             => array( 1,    'aralığına', 'middle' ),
        'img_bottom'             => array( 1,    'astına', 'bottom' ),
-       'img_text-bottom'        => array( 1,    'mätin-astında', 'text-bottom' ),
+       'img_text_bottom'        => array( 1,    'mätin-astında', 'text-bottom' ),
        'int'                    => array( 0,    'İŞKİ:', 'INT:' ),
        'sitename'               => array( 1,    'TORAPATAWI', 'SITENAME' ),
        'ns'                     => array( 0,    'EA:', 'ESİMAYA:', 'NS:' ),
index 5a7de23..b531c06 100644 (file)
@@ -165,10 +165,10 @@ $magicWords = array(
        'img_sub'                => array( 1,    'sub'                    ),
        'img_super'              => array( 1,    'super', 'sup'           ),
        'img_top'                => array( 1,    'top', 'boven'           ),
-       'img_text-top'           => array( 1,    'text-top', 'tekst-boven' ),
+       'img_text_top'           => array( 1,    'text-top', 'tekst-boven' ),
        'img_middle'             => array( 1,    'middle', 'midden'       ),
        'img_bottom'             => array( 1,    'bottom', 'beneden'      ),
-       'img_text-bottom'        => array( 1,    'text-bottom', 'tekst-beneden' ),
+       'img_text_bottom'        => array( 1,    'text-bottom', 'tekst-beneden' ),
        'int'                    => array( 0,    'INT:'                   ),
        'sitename'               => array( 1,    'SITENAME', 'SITENAAM'   ),
        'ns'                     => array( 0,    'NS:', 'NR:'             ),