From 164bb322f28b28d226a36d15794959dd7568a770 Mon Sep 17 00:00:00 2001 From: Tim Starling Date: Wed, 15 Aug 2007 10:50:09 +0000 Subject: [PATCH] Basic integrated audio/video support, with Ogg implementation. * 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. --- RELEASE-NOTES | 1 + includes/AutoLoader.php | 2 + includes/DefaultSettings.php | 22 +++ includes/GlobalFunctions.php | 14 ++ includes/ImagePage.php | 56 +++--- includes/Linker.php | 249 ++++++++++++++++----------- includes/MagicWord.php | 175 +++++++++++++++++++ includes/MessageCache.php | 24 ++- includes/MimeMagic.php | 2 + includes/OutputPage.php | 15 +- includes/Parser.php | 165 ++++++++++-------- includes/ParserOutput.php | 9 +- includes/SpecialAllmessages.php | 3 +- includes/SpecialSpecialpages.php | 4 +- includes/User.php | 9 +- includes/filerepo/File.php | 69 ++++++-- includes/filerepo/LocalFile.php | 35 ++-- includes/filerepo/OldLocalFile.php | 28 ++- includes/media/DjVu.php | 7 + includes/media/Generic.php | 81 ++++++++- includes/media/SVG.php | 9 +- languages/Language.php | 67 +++++++ languages/messages/MessagesAr.php | 4 +- languages/messages/MessagesBg.php | 4 +- languages/messages/MessagesCs.php | 4 +- languages/messages/MessagesEn.php | 17 +- languages/messages/MessagesId.php | 4 +- languages/messages/MessagesKk_cn.php | 4 +- languages/messages/MessagesKk_kz.php | 4 +- languages/messages/MessagesKk_tr.php | 4 +- languages/messages/MessagesNl.php | 4 +- 31 files changed, 820 insertions(+), 275 deletions(-) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index ffc0bc3991..e70d5a4a76 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -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 == diff --git a/includes/AutoLoader.php b/includes/AutoLoader.php index 628e102e60..632903fc90 100644 --- a/includes/AutoLoader.php +++ b/includes/AutoLoader.php @@ -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() { } + diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php index ce1aaf9f07..80f0eb16c3 100644 --- a/includes/DefaultSettings.php +++ b/includes/DefaultSettings.php @@ -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. diff --git a/includes/GlobalFunctions.php b/includes/GlobalFunctions.php index 02a30a471c..ea2304853a 100644 --- a/includes/GlobalFunctions.php +++ b/includes/GlobalFunctions.php @@ -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; + } +} + + diff --git a/includes/ImagePage.php b/includes/ImagePage.php index a6b33cd6cf..d64a41835c 100644 --- a/includes/ImagePage.php +++ b/includes/ImagePage.php @@ -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 . - '
' . Xml::tags( 'a', $linkAttribs, $msgbig ) . ' ' . $msgsize; + '
' . 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 ) . '
'; - } global $wgContLang; $dirmark = $wgContLang->getDirMark(); if (!$this->img->isSafeFile()) { $warning = wfMsg( 'mediawarning' ); $wgOut->addWikiText( <<$infores +
[[Media:$filename|$filename]]$dirmark - $info + $longDesc
$warning
@@ -328,8 +325,8 @@ EOT ); } else { $wgOut->addWikiText( <<$infores -[[Media:$filename|$filename]]$dirmark $info +
+[[Media:$filename|$filename]]$dirmark $longDesc
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 "\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 .= ''; // 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 .= '' . wfMsgHtml( 'widthheight', $width, $height ) . ''; + $row .= '' . htmlspecialchars( $dims ) . ''; // File size $row .= '' . $this->skin->formatSize( $size ) . ''; @@ -754,4 +752,4 @@ class ImageHistoryList { return "{$row}\n"; } -} \ No newline at end of file +} diff --git a/includes/Linker.php b/includes/Linker.php index e48bedfb07..de81233a49 100644 --- a/includes/Linker.php +++ b/includes/Linker.php @@ -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 = '
'; $postfix = '
'; - $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 = "
{$s}
"; + if ( '' != $fp['align'] ) { + $s = "
{$s}
"; } 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 = "
"; + $s = "
"; 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 = ''; } } - $s .= '
'.$zoomicon.$label."
"; + $s .= '
'.$zoomicon.$fp['caption']."
"; 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 { + diff --git a/includes/MagicWord.php b/includes/MagicWord.php index 2ad02e488b..9e8d025c22 100644 --- a/includes/MagicWord.php +++ b/includes/MagicWord.php @@ -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; + } +} diff --git a/includes/MessageCache.php b/includes/MessageCache.php index 277d155557..f06f44da6c 100644 --- a/includes/MessageCache.php +++ b/includes/MessageCache.php @@ -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 ); + } } diff --git a/includes/MimeMagic.php b/includes/MimeMagic.php index c4c6cf1762..264a3595c7 100644 --- a/includes/MimeMagic.php +++ b/includes/MimeMagic.php @@ -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 ); diff --git a/includes/OutputPage.php b/includes/OutputPage.php index 20ca4402bd..30cfd7d9fc 100644 --- a/includes/OutputPage.php +++ b/includes/OutputPage.php @@ -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 ) ); } diff --git a/includes/Parser.php b/includes/Parser.php index d08a369ec8..d1eb156725 100644 --- a/includes/Parser.php +++ b/includes/Parser.php @@ -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; } /** diff --git a/includes/ParserOutput.php b/includes/ParserOutput.php index 13071693a4..8b9f659fb8 100644 --- a/includes/ParserOutput.php +++ b/includes/ParserOutput.php @@ -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 section + $mHeadItems, # Items to put in the 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; } diff --git a/includes/SpecialAllmessages.php b/includes/SpecialAllmessages.php index 017bbda66e..4ba01e2980 100644 --- a/includes/SpecialAllmessages.php +++ b/includes/SpecialAllmessages.php @@ -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 ); diff --git a/includes/SpecialSpecialpages.php b/includes/SpecialSpecialpages.php index d9d5e85f3b..a893966cdc 100644 --- a/includes/SpecialSpecialpages.php +++ b/includes/SpecialSpecialpages.php @@ -8,9 +8,9 @@ * */ function wfSpecialSpecialpages() { - global $wgOut, $wgUser; + global $wgOut, $wgUser, $wgMessageCache; - MessageCache::loadAllMessages(); + $wgMessageCache->loadAllMessages(); $wgOut->setRobotpolicy( 'index,nofollow' ); $sk = $wgUser->getSkin(); diff --git a/includes/User.php b/includes/User.php index 2ecac0873d..055a81326d 100644 --- a/includes/User.php +++ b/includes/User.php @@ -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 ); diff --git a/includes/filerepo/File.php b/includes/filerepo/File.php index a8bd9fbb49..84b64fbbdd 100644 --- a/includes/filerepo/File.php +++ b/includes/filerepo/File.php @@ -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 ); + diff --git a/includes/filerepo/LocalFile.php b/includes/filerepo/LocalFile.php index 8a6b3de165..44000e4900 100644 --- a/includes/filerepo/LocalFile.php +++ b/includes/filerepo/LocalFile.php @@ -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' ) diff --git a/includes/filerepo/OldLocalFile.php b/includes/filerepo/OldLocalFile.php index 4d9dffbdb7..0e35df68d5 100644 --- a/includes/filerepo/OldLocalFile.php +++ b/includes/filerepo/OldLocalFile.php @@ -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(), diff --git a/includes/media/DjVu.php b/includes/media/DjVu.php index 97ea88f546..45664dbe3d 100644 --- a/includes/media/DjVu.php +++ b/includes/media/DjVu.php @@ -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 ) { diff --git a/includes/media/Generic.php b/includes/media/Generic.php index 95e856817e..b97fa047b6 100644 --- a/includes/media/Generic.php +++ b/includes/media/Generic.php @@ -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() ); + } + } } + diff --git a/includes/media/SVG.php b/includes/media/SVG.php index 0fee6cb1da..d279a1ae0d 100644 --- a/includes/media/SVG.php +++ b/includes/media/SVG.php @@ -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() ) ); + } } + diff --git a/languages/Language.php b/languages/Language.php index 6862fd4931..5f1435d9f0 100644 --- a/languages/Language.php +++ b/languages/Language.php @@ -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 ); + } } + diff --git a/languages/messages/MessagesAr.php b/languages/messages/MessagesAr.php index d3e0cd4653..c0e0126916 100644 --- a/languages/messages/MessagesAr.php +++ b/languages/messages/MessagesAr.php @@ -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:" ), diff --git a/languages/messages/MessagesBg.php b/languages/messages/MessagesBg.php index bd389231f6..d127620c73 100644 --- a/languages/messages/MessagesBg.php +++ b/languages/messages/MessagesBg.php @@ -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:', 'ИП:' ), diff --git a/languages/messages/MessagesCs.php b/languages/messages/MessagesCs.php index d9e375ada6..7eb7ad3c99 100644 --- a/languages/messages/MessagesCs.php +++ b/languages/messages/MessagesCs.php @@ -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:' ), diff --git a/languages/messages/MessagesEn.php b/languages/messages/MessagesEn.php index b87daae5de..495715816e 100644 --- a/languages/messages/MessagesEn.php +++ b/languages/messages/MessagesEn.php @@ -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' => 'No higher resolution available.', -'file-svg' => 'This is a lossless scalable vector image. Base size: $1 × $2 pixels.', +'svg-long-desc' => '(SVG file, nominally $1 × $2 pixels, file size: $3)', 'show-big-image' => 'Full resolution', 'show-big-image-thumb' => 'Size of this preview: $1 × $2 pixels', @@ -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: diff --git a/languages/messages/MessagesId.php b/languages/messages/MessagesId.php index 1980022b88..1d694602a9 100644 --- a/languages/messages/MessagesId.php +++ b/languages/messages/MessagesId.php @@ -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:' ), diff --git a/languages/messages/MessagesKk_cn.php b/languages/messages/MessagesKk_cn.php index e807403a7a..faaddf7220 100644 --- a/languages/messages/MessagesKk_cn.php +++ b/languages/messages/MessagesKk_cn.php @@ -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:' ), diff --git a/languages/messages/MessagesKk_kz.php b/languages/messages/MessagesKk_kz.php index f99bf6e8a3..4721105273 100644 --- a/languages/messages/MessagesKk_kz.php +++ b/languages/messages/MessagesKk_kz.php @@ -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:' ), diff --git a/languages/messages/MessagesKk_tr.php b/languages/messages/MessagesKk_tr.php index b7ae471d40..33d1de4aa9 100644 --- a/languages/messages/MessagesKk_tr.php +++ b/languages/messages/MessagesKk_tr.php @@ -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:' ), diff --git a/languages/messages/MessagesNl.php b/languages/messages/MessagesNl.php index 5a7de23804..b531c06e16 100644 --- a/languages/messages/MessagesNl.php +++ b/languages/messages/MessagesNl.php @@ -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:' ), -- 2.20.1