Add language handling to imageinfo/extmetadata API
authortgr <tgr.huwiki@gmail.com>
Mon, 28 Oct 2013 18:18:05 +0000 (18:18 +0000)
committertgr <tgr.huwiki@gmail.com>
Tue, 29 Oct 2013 17:49:19 +0000 (17:49 +0000)
Adds options to return metadata in all available languages, or in
a single selected language.

Change-Id: I78f096318904a08abd317a5ed3f74ee33d3289cb

docs/hooks.txt
includes/api/ApiQueryImageInfo.php
includes/filerepo/file/ForeignAPIFile.php
includes/media/FormatMetadata.php

index d18679b..3f88bf5 100644 (file)
@@ -1162,6 +1162,9 @@ underscore) magic words. Called by MagicWord.
 'value' => prop value, 'source' => 'name of hook' ).
 $file: File object of file in question
 $context: RequestContext (including language to use)
+$single: Only extract the current language; if false, the prop value should
+be in the metadata multi-language array format:
+mediawiki.org/wiki/Manual:File_metadata_handling#Multi-language_array_format
 &$maxCacheTime: how long the results can be cached
 
 'GetFullURL': Modify fully-qualified URLs used in redirects/export/offsite data.
index 1e80f57..81c9faf 100755 (executable)
@@ -51,6 +51,8 @@ class ApiQueryImageInfo extends ApiQueryBase {
 
                $metadataOpts = array(
                        'version' => $params['metadataversion'],
+                       'language' => $params['extmetadatalanguage'],
+                       'multilang' => $params['extmetadatamultilang'],
                );
 
                $pageIds = $this->getPageSet()->getAllTitlesByNamespace();
@@ -305,12 +307,18 @@ class ApiQueryImageInfo extends ApiQueryBase {
         * @param string|array $metadataOpts Options for metadata fetching.
         *   This is an array consisting of the keys:
         *    'version': The metadata version for the metadata option
+        *    'language': The language for extmetadata property
+        *    'multilang': Return all translations in extmetadata property
         * @return Array: result array
         */
        static function getInfo( $file, $prop, $result, $thumbParams = null, $metadataOpts = false ) {
+               global $wgContLang;
+
                if ( !$metadataOpts || is_string( $metadataOpts ) ) {
                        $metadataOpts = array(
                                'version' => $metadataOpts ?: 'latest',
+                               'language' => $wgContLang,
+                               'multilang' => false,
                        );
                }
                $version = $metadataOpts['version'];
@@ -437,6 +445,8 @@ class ApiQueryImageInfo extends ApiQueryBase {
                        // start with a letter, and all the values are strings.
                        // Thus there should be no issue with format=xml.
                        $format = new FormatMetadata;
+                       $format->setSingleLanguage( !$metadataOpts['multilang'] );
+                       $format->getContext()->setLanguage( $metadataOpts['language'] );
                        $extmetaArray = $format->fetchExtendedMetadata( $file );
                        $vals['extmetadata'] = $extmetaArray;
                }
@@ -515,6 +525,7 @@ class ApiQueryImageInfo extends ApiQueryBase {
        }
 
        public function getAllowedParams() {
+               global $wgContLang;
                return array(
                        'prop' => array(
                                ApiBase::PARAM_ISMULTI => true,
@@ -546,6 +557,14 @@ class ApiQueryImageInfo extends ApiQueryBase {
                                ApiBase::PARAM_TYPE => 'string',
                                ApiBase::PARAM_DFLT => '1',
                        ),
+                       'extmetadatalanguage' => array(
+                               ApiBase::PARAM_TYPE => 'string',
+                               ApiBase::PARAM_DFLT => $wgContLang->getCode(),
+                       ),
+                       'extmetadatamultilang' => array(
+                               ApiBase::PARAM_TYPE => 'boolean',
+                               ApiBase::PARAM_DFLT => false,
+                       ),
                        'urlparam' => array(
                                ApiBase::PARAM_DFLT => '',
                                ApiBase::PARAM_TYPE => 'string',
@@ -628,6 +647,10 @@ class ApiQueryImageInfo extends ApiQueryBase {
                        'end' => 'Timestamp to stop listing at',
                        'metadataversion' => array( "Version of metadata to use. if 'latest' is specified, use latest version.",
                                                "Defaults to '1' for backwards compatibility" ),
+                       'extmetadatalanguage' => array( 'What language to fetch extmetadata in. This affects both which',
+                                               'translation to fetch, if multiple are available, as well as how things',
+                                               'like numbers and various values are formatted.' ),
+                       'extmetadatamultilang' => 'If translations for extmetadata property are available, fetch all of them.',
                        'continue' => 'If the query response includes a continue value, use it here to get another page of results',
                        'localonly' => 'Look only for files in the local repository',
                );
index fc9e176..0b3d5df 100755 (executable)
@@ -57,7 +57,10 @@ class ForeignAPIFile extends File {
                        'titles' => 'File:' . $title->getDBkey(),
                        'iiprop' => self::getProps(),
                        'prop' => 'imageinfo',
-                       'iimetadataversion' => MediaHandler::getMetadataVersion()
+                       'iimetadataversion' => MediaHandler::getMetadataVersion(),
+                       // extmetadata is language-dependant, accessing the current language here
+                       // would be problematic, so we just get them all
+                       'iiextmetadatamultilang' => 1,
                ) );
 
                $info = $repo->getImageInfo( $data );
index 17de74a..b34ad65 100755 (executable)
  */
 class FormatMetadata extends ContextSource {
 
+       /**
+        * Only output a single language for multi-language fields
+        * @var boolean
+        * @since 1.23
+        */
+       protected $singleLang = false;
+
+       /**
+        * Trigger only outputting single language for multilanguage fields
+        *
+        * @param Boolean $val
+        * @since 1.23
+        */
+       public function setSingleLanguage( $val ) {
+               $this->singleLang = $val;
+       }
+
        /**
         * Numbers given by Exif user agents are often magical, that is they
         * should be replaced by a detailed explanation depending on their
@@ -935,7 +952,6 @@ class FormatMetadata extends ContextSource {
                 * languages, we don't want to show them all by default
                 */
                else {
-                       global $wgContLang;
                        switch ( $type ) {
                        case 'lang':
                                // Display default, followed by ContLang,
@@ -946,7 +962,7 @@ class FormatMetadata extends ContextSource {
 
                                $content = '';
 
-                               $cLang = $wgContLang->getCode();
+                               $priorityLanguages = $this->getPriorityLanguages();
                                $defaultItem = false;
                                $defaultLang = false;
 
@@ -961,18 +977,24 @@ class FormatMetadata extends ContextSource {
                                        $defaultItem = $vals['x-default'];
                                        unset( $vals['x-default'] );
                                }
-                               // Do contentLanguage.
-                               if ( isset( $vals[$cLang] ) ) {
-                                       $isDefault = false;
-                                       if ( $vals[$cLang] === $defaultItem ) {
-                                               $defaultItem = false;
-                                               $isDefault = true;
-                                       }
-                                       $content .= $this->langItem(
-                                               $vals[$cLang], $cLang,
-                                               $isDefault, $noHtml );
+                               foreach( $priorityLanguages as $pLang ) {
+                                       if ( isset( $vals[$pLang] ) ) {
+                                               $isDefault = false;
+                                               if ( $vals[$pLang] === $defaultItem ) {
+                                                       $defaultItem = false;
+                                                       $isDefault = true;
+                                               }
+                                               $content .= $this->langItem(
+                                                       $vals[$pLang], $pLang,
+                                                       $isDefault, $noHtml );
+
+                                               unset( $vals[$pLang] );
 
-                                       unset( $vals[$cLang] );
+                                               if ( $this->singleLang ) {
+                                                       return Html::rawElement( 'span',
+                                                               array( 'lang' => $pLang ), $vals[$pLang] );
+                                               }
+                                       }
                                }
 
                                // Now do the rest.
@@ -983,11 +1005,18 @@ class FormatMetadata extends ContextSource {
                                        }
                                        $content .= $this->langItem( $item,
                                                $lang, false, $noHtml );
+                                       if ( $this->singleLang ) {
+                                               return Html::rawElement( 'span',
+                                                       array( 'lang' => $lang ), $item );
+                                       }
                                }
                                if ( $defaultItem !== false ) {
                                        $content = $this->langItem( $defaultItem,
                                                $defaultLang, true, $noHtml ) .
                                                $content;
+                                       if ( $this->singleLang ) {
+                                               return $defaultItem;
+                                       }
                                }
                                if ( $noHtml ) {
                                        return $content;
@@ -1456,6 +1485,7 @@ class FormatMetadata extends ContextSource {
                $cacheKey = wfMemcKey(
                        'getExtendedMetadata',
                        $this->getLanguage()->getCode(),
+                       (int) $this->singleLang,
                        $file->getSha1()
                );
 
@@ -1469,6 +1499,9 @@ class FormatMetadata extends ContextSource {
                        $maxCacheTime = ( $file instanceof ForeignAPIFile ) ? 60 * 60 * 12 : 60 * 60 * 24 * 30;
                        $fileMetadata = $this->getExtendedMetadataFromFile( $file );
                        $extendedMetadata = $this->getExtendedMetadataFromHook( $file, $fileMetadata, $maxCacheTime );
+                       if ( $this->singleLang ) {
+                               $this->resolveMultilangMetadata( $extendedMetadata );
+                       }
                        // Make sure the metadata won't break the API when an XML format is used.
                        // This is an API-specific function so it would be cleaner to call it from
                        // outside fetchExtendedMetadata, but this way we don't need to redo the
@@ -1558,6 +1591,7 @@ class FormatMetadata extends ContextSource {
                        &$extendedMetadata,
                        $file,
                        $this->getContext(),
+                       $this->singleLang,
                        &$maxCacheTime
                ) );
 
@@ -1572,6 +1606,64 @@ class FormatMetadata extends ContextSource {
                return $extendedMetadata;
        }
 
+       /**
+        * Turns an XMP-style multilang array into a single value.
+        * If the value is not a multilang array, it is returned unchanged.
+        * See mediawiki.org/wiki/Manual:File_metadata_handling#Multi-language_array_format
+        * @param mixed $value
+        * @return mixed value in best language, null if there were no languages at all
+        * @since 1.23
+        */
+       protected function resolveMultilangValue( $value )
+       {
+               if (
+                       !is_array( $value )
+                       || !isset( $value['_type'] )
+                       || $value['_type'] != 'lang'
+               ) {
+                       return $value; // do nothing if not a multilang array
+               }
+
+               // choose the language best matching user or site settings
+               $priorityLanguages = $this->getPriorityLanguages();
+               foreach( $priorityLanguages as $lang ) {
+                       if ( isset( $value[$lang] ) ) {
+                               return $value[$lang];
+                       }
+               }
+
+               // otherwise go with the default language, if set
+               if ( isset( $value['x-default'] ) ) {
+                       return $value['x-default'];
+               }
+
+               // otherwise just return any one language
+               unset($value['_type']);
+               if (!empty($value)) {
+                       return reset($value);
+               }
+
+               // this should not happen; signal error
+               return null;
+       }
+
+       /**
+        * Takes an array returned by the getExtendedMetadata* functions,
+        * and resolves multi-language values in it.
+        * @param array $metadata
+        * @since 1.23
+        */
+       protected function resolveMultilangMetadata( &$metadata ) {
+               if ( !is_array( $metadata ) ) {
+                       return;
+               }
+               foreach ( $metadata as &$field ) {
+                       if ( isset( $field['value'] ) ) {
+                               $field['value'] = $this->resolveMultilangValue( $field['value'] );
+                       }
+               }
+       }
+
        /**
         * Makes sure the given array is a valid API response fragment
         * (can be transformed into XML)