From b8d2ca802004d2ba2ec592d62f2c76c953abc360 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Gerg=C5=91=20Tisza?= Date: Wed, 12 Nov 2014 19:10:05 +0000 Subject: [PATCH] FormatMetadata::fetchExtendedMetadata: Ignore multiple EXIF/XMP values Ignore all but the first value of multivalued metadata fields. Before this, the function returned an array of values for such fields, causing various errors. Bug: 72652 Change-Id: I0fd22b78e4938eecc506d870e352b3b196ee3ace Mingle: https://wikimedia.mingle.thoughtworks.com/projects/multimedia/cards/973 --- includes/media/FormatMetadata.php | 50 +++++++++++++++++++ .../includes/media/FormatMetadataTest.php | 32 ++++++++++++ 2 files changed, 82 insertions(+) diff --git a/includes/media/FormatMetadata.php b/includes/media/FormatMetadata.php index 4356953997..07d7618c60 100644 --- a/includes/media/FormatMetadata.php +++ b/includes/media/FormatMetadata.php @@ -1624,6 +1624,7 @@ class FormatMetadata extends ContextSource { if ( $this->singleLang ) { $this->resolveMultilangMetadata( $extendedMetadata ); } + $this->discardMultipleValues( $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 @@ -1776,6 +1777,32 @@ class FormatMetadata extends ContextSource { return null; } + /** + * Turns an XMP-style multivalue array into a single value by dropping all but the first value. + * If the value is not a multivalue array (or a multivalue array inside a multilang array), it is returned unchanged. + * See mediawiki.org/wiki/Manual:File_metadata_handling#Multi-language_array_format + * @param mixed $value + * @return mixed The value, or the first value if there were multiple ones + * @since 1.25 + */ + protected function resolveMultivalueValue( $value ) { + if ( !is_array( $value ) ) { + return $value; + } elseif ( isset( $value['_type'] ) && $value['_type'] === 'lang' ) { // if this is a multilang array, process fields separately + $newValue = array(); + foreach ( $value as $k => $v ) { + $newValue[$k] = $this->resolveMultivalueValue( $v ); + } + return $newValue; + } else { // _type is 'ul' or 'ol' or missing in which case it defaults to 'ul' + list( $k, $v ) = each( $value ); + if ( $k === '_type' ) { + $v = current( $value ); + } + return $v; + } + } + /** * Takes an array returned by the getExtendedMetadata* functions, * and resolves multi-language values in it. @@ -1793,6 +1820,29 @@ class FormatMetadata extends ContextSource { } } + /** + * Takes an array returned by the getExtendedMetadata* functions, + * and turns all fields into single-valued ones by dropping extra values. + * @param array $metadata + * @since 1.25 + */ + protected function discardMultipleValues( &$metadata ) { + if ( !is_array( $metadata ) ) { + return; + } + foreach ( $metadata as $key => &$field ) { + if ( $key === 'Software' || $key === 'Contact' ) { + // we skip some fields which have composite values. They are not particularly interesting + // and you can get them via the metadata / commonmetadata APIs anyway. + continue; + } + if ( isset( $field['value'] ) ) { + $field['value'] = $this->resolveMultivalueValue( $field['value'] ); + } + } + + } + /** * Makes sure the given array is a valid API response fragment * (can be transformed into XML) diff --git a/tests/phpunit/includes/media/FormatMetadataTest.php b/tests/phpunit/includes/media/FormatMetadataTest.php index 002e2cb987..97aa0a3dce 100644 --- a/tests/phpunit/includes/media/FormatMetadataTest.php +++ b/tests/phpunit/includes/media/FormatMetadataTest.php @@ -68,4 +68,36 @@ class FormatMetadataTest extends MediaWikiMediaTestCase { // TODO: more test cases ); } + + /** + * @param mixed $input + * @param mixed $output + * @dataProvider provideResolveMultivalueValue + * @covers FormatMetadata::resolveMultivalueValue + */ + public function testResolveMultivalueValue( $input, $output ) { + $formatMetadata = new FormatMetadata(); + $class = new ReflectionClass( 'FormatMetadata' ); + $method = $class->getMethod( 'resolveMultivalueValue' ); + $method->setAccessible( true ); + $actualInput = $method->invoke( $formatMetadata, $input ); + $this->assertEquals( $output, $actualInput ); + } + + public function provideResolveMultivalueValue() { + return array( + 'nonArray' => array( 'foo', 'foo' ), + 'multiValue' => array( array( 'first', 'second', 'third', '_type' => 'ol' ), 'first' ), + 'noType' => array( array( 'first', 'second', 'third' ), 'first' ), + 'typeFirst' => array( array( '_type' => 'ol', 'first', 'second', 'third' ), 'first' ), + 'multilang' => array( + array( 'en' => 'first', 'de' => 'Erste', '_type' => 'lang' ), + array( 'en' => 'first', 'de' => 'Erste', '_type' => 'lang' ), + ), + 'multilang-multivalue' => array( + array( 'en' => array( 'first', 'second' ), 'de' => array( 'Erste', 'Zweite' ), '_type' => 'lang' ), + array( 'en' => 'first', 'de' => 'Erste', '_type' => 'lang' ), + ), + ); + } } -- 2.20.1