Merge "Fix/update inline documentation of LoadBalancer"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Thu, 20 Nov 2014 18:46:41 +0000 (18:46 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Thu, 20 Nov 2014 18:46:41 +0000 (18:46 +0000)
RELEASE-NOTES-1.25
includes/DefaultSettings.php
includes/api/ApiFormatJson.php
includes/api/ApiFormatPhp.php
includes/media/FormatMetadata.php
tests/phpunit/includes/media/FormatMetadataTest.php

index e6d8e7d..5aba823 100644 (file)
@@ -98,6 +98,9 @@ production.
 * If the user has the 'deletedhistory' right, action=query's revids parameter
   will now recognize deleted revids.
 * prop=revisions may be used as a generator, generating revids.
+* (bug 66776) format=json results will no longer be corrupted when
+  $wgMangleFlashPolicy is in effect. format=php results will cleanly return an
+  error instead of returning invalid serialized data.
 
 === Action API internal changes in 1.25 ===
 * ApiHelp has been rewritten to support i18n and paginated HTML output.
index f93737c..f83c402 100644 (file)
@@ -3199,6 +3199,8 @@ $wgEnableCanonicalServerLink = false;
  * <cross-domain-policy>. Without this, an attacker can send their own
  * cross-domain policy unless it is prevented by the crossdomain.xml file at
  * the domain root.
+ *
+ * @since 1.25
  */
 $wgMangleFlashPolicy = true;
 
index ce8656e..966e82d 100644 (file)
@@ -67,6 +67,16 @@ class ApiFormatJson extends ApiFormatBase {
                        $this->getIsHtml(),
                        $params['utf8'] ? FormatJson::ALL_OK : FormatJson::XMLMETA_OK
                );
+
+               // Bug 66776: wfMangleFlashPolicy() is needed to avoid a nasty bug in
+               // Flash, but what it does isn't friendly for the API, so we need to
+               // work around it.
+               if ( preg_match( '/\<\s*cross-domain-policy\s*\>/i', $json ) ) {
+                       $json = preg_replace(
+                               '/\<(\s*cross-domain-policy\s*)\>/i', '\\u003C$1\\u003E', $json
+                       );
+               }
+
                $callback = $params['callback'];
                if ( $callback !== null ) {
                        $callback = preg_replace( "/[^][.\\'\\\"_A-Za-z0-9]/", '', $callback );
index ae93812..a4b4a11 100644 (file)
@@ -35,6 +35,22 @@ class ApiFormatPhp extends ApiFormatBase {
        }
 
        public function execute() {
-               $this->printText( serialize( $this->getResultData() ) );
+               $text = serialize( $this->getResultData() );
+
+               // Bug 66776: wfMangleFlashPolicy() is needed to avoid a nasty bug in
+               // Flash, but what it does isn't friendly for the API. There's nothing
+               // we can do here that isn't actively broken in some manner, so let's
+               // just be broken in a useful manner.
+               if ( $this->getConfig()->get( 'MangleFlashPolicy' ) &&
+                       in_array( 'wfOutputHandler', ob_list_handlers(), true ) &&
+                       preg_match( '/\<\s*cross-domain-policy\s*\>/i', $text )
+               ) {
+                       $this->dieUsage(
+                               'This response cannot be represented using format=php. See https://bugzilla.wikimedia.org/show_bug.cgi?id=66776',
+                               'internalerror'
+                       );
+               }
+
+               $this->printText( $text );
        }
 }
index 4356953..07d7618 100644 (file)
@@ -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)
index 002e2cb..97aa0a3 100644 (file)
@@ -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' ),
+                   ),
+               );
+       }
 }