diff --git a/RELEASE-NOTES-1.25 b/RELEASE-NOTES-1.25 index 4f648f51950..f0d1c07f8f6 100644 --- a/RELEASE-NOTES-1.25 +++ b/RELEASE-NOTES-1.25 @@ -125,6 +125,9 @@ production. Special:EditTags, generally accessed via the revision-deletion-like interface on history pages and Special:Log is likely to be more useful. * Added 'applychangetags' and 'changetags' user rights. +* (T35235) LogFormatter subclasses are now responsible for formatting the + parameters for API log event output. Extensions should implement the new + getParametersForApi() method in their log formatters. ==== External libraries ==== * MediaWiki now requires certain external libraries to be installed. In the past @@ -253,6 +256,18 @@ production. * Default type param for query list=watchlist and list=recentchanges has been changed from all types (e.g. including 'external') to 'edit|new|log'. * Added formatversion to format=json, still experimental. +* (T73020) Log event details are now always under a 'params' subkey for + list=logevents, and a 'logparams' subkey for list=watchlist and + list=recentchanges. +* Log event details are changing formatting: + * block events now report flags as an array rather than as a comma-separated + list. + * patrol events now report the 'auto' flag as a boolean (absent/empty string + for BC formats) rather than as an integer. + * rights events now report the old and new group lists as arrays rather than + as comma-separated lists. + * merge events use new-style formatting. + * delete/event and delete/revision events use new-style formatting. === Action API internal changes in 1.25 === * ApiHelp has been rewritten to support i18n and paginated HTML output. @@ -299,6 +314,8 @@ production. * ApiResult/ApiFormatBase "raw mode" is deprecated. * ApiFormatXml now assumes defaults and so on instead of throwing errors when metadata isn't set. +* (T35235) LogFormatter subclasses are now responsible for formatting log event + parameters for the API. * The following methods have been deprecated and may be removed in a future release: * ApiBase::getDescription @@ -341,6 +358,7 @@ production. * ApiResult::size * ApiResult::convertStatusToArray * ApiQueryImageInfo::getPropertyDescriptions + * ApiQueryLogEvents::addLogParams * The following classes have been deprecated and may be removed in a future release: * ApiQueryDeletedrevs diff --git a/autoload.php b/autoload.php index 38d1ac08187..ddd82546949 100644 --- a/autoload.php +++ b/autoload.php @@ -1276,6 +1276,7 @@ $wgAutoloadLocalClasses = array( 'UploadFromStash' => __DIR__ . '/includes/upload/UploadFromStash.php', 'UploadFromUrl' => __DIR__ . '/includes/upload/UploadFromUrl.php', 'UploadFromUrlJob' => __DIR__ . '/includes/jobqueue/jobs/UploadFromUrlJob.php', + 'UploadLogFormatter' => __DIR__ . '/includes/logging/UploadLogFormatter.php', 'UploadSourceAdapter' => __DIR__ . '/includes/Import.php', 'UploadSourceField' => __DIR__ . '/includes/specials/SpecialUpload.php', 'UploadStash' => __DIR__ . '/includes/upload/UploadStash.php', diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php index c2d6c9512d7..b08fe4d65a1 100644 --- a/includes/DefaultSettings.php +++ b/includes/DefaultSettings.php @@ -6675,7 +6675,7 @@ $wgLogActions = array( ); /** - * The same as above, but here values are names of functions, + * The same as above, but here values are names of classes, * not messages. * @see LogPage::actionText * @see LogFormatter @@ -6693,9 +6693,9 @@ $wgLogActionsHandlers = array( 'patrol/patrol' => 'PatrolLogFormatter', 'rights/rights' => 'RightsLogFormatter', 'rights/autopromote' => 'RightsLogFormatter', - 'upload/upload' => 'LogFormatter', - 'upload/overwrite' => 'LogFormatter', - 'upload/revert' => 'LogFormatter', + 'upload/upload' => 'UploadLogFormatter', + 'upload/overwrite' => 'UploadLogFormatter', + 'upload/revert' => 'UploadLogFormatter', 'merge/merge' => 'MergeLogFormatter', 'tag/update' => 'TagLogFormatter', 'managetags/create' => 'LogFormatter', diff --git a/includes/api/ApiQueryLogEvents.php b/includes/api/ApiQueryLogEvents.php index a626c8a132e..b2397272b09 100644 --- a/includes/api/ApiQueryLogEvents.php +++ b/includes/api/ApiQueryLogEvents.php @@ -243,6 +243,7 @@ class ApiQueryLogEvents extends ApiQueryBase { } /** + * @deprecated since 1.25 Use LogFormatter::formatParametersForApi instead * @param ApiResult $result * @param array $vals * @param string $params @@ -255,142 +256,14 @@ class ApiQueryLogEvents extends ApiQueryBase { public static function addLogParams( $result, &$vals, $params, $type, $action, $ts, $legacy = false ) { - switch ( $type ) { - case 'move': - if ( $legacy ) { - $targetKey = 0; - $noredirKey = 1; - } else { - $targetKey = '4::target'; - $noredirKey = '5::noredir'; - } + wfDeprecated( __METHOD__, '1.25' ); - if ( isset( $params[$targetKey] ) ) { - $title = Title::newFromText( $params[$targetKey] ); - if ( $title ) { - $vals2 = array(); - ApiQueryBase::addTitleInfo( $vals2, $title, 'new_' ); - $vals[$type] = $vals2; - } - } - if ( isset( $params[$noredirKey] ) && $params[$noredirKey] ) { - $vals[$type]['suppressedredirect'] = ''; - } - $params = null; - break; - case 'patrol': - if ( $legacy ) { - $cur = 0; - $prev = 1; - $auto = 2; - } else { - $cur = '4::curid'; - $prev = '5::previd'; - $auto = '6::auto'; - } - $vals2 = array(); - $vals2['cur'] = $params[$cur]; - $vals2['prev'] = $params[$prev]; - $vals2['auto'] = $params[$auto]; - $vals[$type] = $vals2; - $params = null; - break; - case 'rights': - $vals2 = array(); - if ( $legacy ) { - list( $vals2['old'], $vals2['new'] ) = $params; - } else { - $vals2['new'] = implode( ', ', $params['5::newgroups'] ); - $vals2['old'] = implode( ', ', $params['4::oldgroups'] ); - } - $vals[$type] = $vals2; - $params = null; - break; - case 'block': - if ( $action == 'unblock' ) { - break; - } - if ( $legacy ) { - $durationKey = 0; - $flagsKey = 1; - } else { - $durationKey = '5::duration'; - $flagsKey = '6::flags'; - } - $vals2 = array(); - $vals2['duration'] = $params[$durationKey]; - $vals2['flags'] = isset( $params[$flagsKey] ) ? $params[$flagsKey] : ''; - - // Indefinite blocks have no expiry time - if ( SpecialBlock::parseExpiryInput( $params[$durationKey] ) !== 'infinity' ) { - $vals2['expiry'] = wfTimestamp( TS_ISO_8601, - strtotime( $params[$durationKey], wfTimestamp( TS_UNIX, $ts ) ) ); - } - $vals[$type] = $vals2; - $params = null; - break; - case 'upload': - if ( isset( $params['img_timestamp'] ) ) { - $params['img_timestamp'] = wfTimestamp( TS_ISO_8601, $params['img_timestamp'] ); - } - break; - case 'merge': - // replace the named parameter with numbered for backward compatibility - if ( isset( $params['4::dest'] ) ) { - $params[] = $params['4::dest']; - unset( $params['4::dest'] ); - } - if ( isset( $params['5::mergepoint'] ) ) { - $params[] = $params['5::mergepoint']; - unset( $params['5::mergepoint'] ); - } - break; - case 'delete': - if ( $action === 'event' || $action === 'revision' ) { - // replace the named parameter with numbered for backward compatibility - if ( $action === 'event' ) { - $idsKey = '4::ids'; - $ofieldKey = '5::ofield'; - $nfieldKey = '6::nfield'; - } else { - if ( isset( $params['4::type'] ) ) { - $params[] = $params['4::type']; - unset( $params['4::type'] ); - } - $idsKey = '5::ids'; - $ofieldKey = '6::ofield'; - $nfieldKey = '7::nfield'; - } - if ( isset( $params[$idsKey] ) ) { - $params[] = implode( ',', $params[$idsKey] ); - unset( $params[$idsKey] ); - } - if ( isset( $params[$ofieldKey] ) ) { - $params[] = 'ofield=' . $params[$ofieldKey]; - unset( $params[$ofieldKey] ); - } - if ( isset( $params[$nfieldKey] ) ) { - $params[] = 'nfield=' . $params[$nfieldKey]; - unset( $params[$nfieldKey] ); - } - } - break; - } - if ( !is_null( $params ) ) { - $logParams = array(); - // Keys like "4::paramname" can't be used for output so we change them to "paramname" - foreach ( $params as $key => $value ) { - if ( strpos( $key, ':' ) === false ) { - $logParams[$key] = $value; - continue; - } - $logParam = explode( ':', $key, 3 ); - $logParams[$logParam[2]] = $value; - } - ApiResult::setIndexedTagName( $logParams, 'param' ); - ApiResult::setIndexedTagNameOnSubarrays( $logParams, 'param' ); - $vals = array_merge( $vals, $logParams ); - } + $entry = new ManualLogEntry( $type, $action ); + $entry->setParameters( $params ); + $entry->setTimestamp( $ts ); + $entry->setLegacy( $legacy ); + $formatter = LogFormatter::newFromEntry( $entry ); + $vals['params'] = $formatter->formatParametersForApi(); return $vals; } @@ -423,15 +296,7 @@ class ApiQueryLogEvents extends ApiQueryBase { $vals['logpage'] = intval( $row->log_page ); } if ( $this->fld_details && $row->log_params !== '' ) { - self::addLogParams( - $this->getResult(), - $vals, - $logEntry->getParameters(), - $logEntry->getType(), - $logEntry->getSubtype(), - $logEntry->getTimestamp(), - $logEntry->isLegacy() - ); + $vals['params'] = LogFormatter::newFromRow( $row )->formatParametersForApi(); } } } diff --git a/includes/api/ApiQueryRecentChanges.php b/includes/api/ApiQueryRecentChanges.php index 3dbfdf9f7e4..28f8b7d9af2 100644 --- a/includes/api/ApiQueryRecentChanges.php +++ b/includes/api/ApiQueryRecentChanges.php @@ -535,16 +535,7 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase { $vals['logid'] = intval( $row->rc_logid ); $vals['logtype'] = $row->rc_log_type; $vals['logaction'] = $row->rc_log_action; - $logEntry = DatabaseLogEntry::newFromRow( $row ); - ApiQueryLogEvents::addLogParams( - $this->getResult(), - $vals, - $logEntry->getParameters(), - $logEntry->getType(), - $logEntry->getSubtype(), - $logEntry->getTimestamp(), - $logEntry->isLegacy() - ); + $vals['logparams'] = LogFormatter::newFromRow( $row )->formatParametersForApi(); } } diff --git a/includes/api/ApiQueryWatchlist.php b/includes/api/ApiQueryWatchlist.php index 04eea54009c..bd1ed0a1fc9 100644 --- a/includes/api/ApiQueryWatchlist.php +++ b/includes/api/ApiQueryWatchlist.php @@ -412,16 +412,7 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase { $vals['logid'] = intval( $row->rc_logid ); $vals['logtype'] = $row->rc_log_type; $vals['logaction'] = $row->rc_log_action; - $logEntry = DatabaseLogEntry::newFromRow( $row ); - ApiQueryLogEvents::addLogParams( - $this->getResult(), - $vals, - $logEntry->getParameters(), - $logEntry->getType(), - $logEntry->getSubtype(), - $logEntry->getTimestamp(), - $logEntry->isLegacy() - ); + $vals['logparams'] = LogFormatter::newFromRow( $row )->formatParametersForApi(); } } diff --git a/includes/logging/BlockLogFormatter.php b/includes/logging/BlockLogFormatter.php index 436fed84bb6..38a279f5f5f 100644 --- a/includes/logging/BlockLogFormatter.php +++ b/includes/logging/BlockLogFormatter.php @@ -168,4 +168,47 @@ class BlockLogFormatter extends LogFormatter { return $messages[$flag]; } + + protected function getParametersForApi() { + $entry = $this->entry; + $params = $entry->getParameters(); + + static $map = array( + // While this looks wrong to be starting at 5 rather than 4, it's + // because getMessageParameters uses $4 for its own purposes. + '5::duration', + '6:array:flags', + '6::flags' => '6:array:flags', + ); + foreach ( $map as $index => $key ) { + if ( isset( $params[$index] ) ) { + $params[$key] = $params[$index]; + unset( $params[$index] ); + } + } + + if ( isset( $params['6:array:flags'] ) && !is_array( $params['6:array:flags'] ) ) { + $params['6:array:flags'] = $params['6:array:flags'] === '' + ? array() + : explode( ',', $params['6:array:flags'] ); + } + + if ( isset( $params['5::duration'] ) && + SpecialBlock::parseExpiryInput( $params['5::duration'] ) !== wfGetDB( DB_SLAVE )->getInfinity() + ) { + $ts = wfTimestamp( TS_UNIX, $entry->getTimestamp() ); + $params[':timestamp:expiry'] = strtotime( $params['5::duration'], $ts ); + } + + return $params; + } + + public function formatParametersForApi() { + $ret = parent::formatParametersForApi(); + if ( isset( $ret['flags'] ) ) { + ApiResult::setIndexedTagName( $ret['flags'], 'f' ); + } + return $ret; + } + } diff --git a/includes/logging/DeleteLogFormatter.php b/includes/logging/DeleteLogFormatter.php index 7fe77b4ba30..f0598aa7498 100644 --- a/includes/logging/DeleteLogFormatter.php +++ b/includes/logging/DeleteLogFormatter.php @@ -216,4 +216,67 @@ class DeleteLogFormatter extends LogFormatter { return ''; } } + + protected function getParametersForApi() { + $entry = $this->entry; + $params = array(); + + $subtype = $this->entry->getSubtype(); + if ( in_array( $subtype, array( 'event', 'revision' ) ) ) { + $rawParams = $entry->getParameters(); + if ( $subtype === 'event' ) { + array_unshift( $rawParams, 'logging' ); + } + + static $map = array( + '4::type', + '5::ids', + '6::ofield', + '7::nfield', + '4::ids' => '5::ids', + '5::ofield' => '6::ofield', + '6::nfield' => '7::nfield', + ); + foreach ( $map as $index => $key ) { + if ( isset( $rawParams[$index] ) ) { + $rawParams[$key] = $rawParams[$index]; + unset( $rawParams[$index] ); + } + } + + $old = $this->parseBitField( $rawParams['6::ofield'] ); + $new = $this->parseBitField( $rawParams['7::nfield'] ); + if ( !is_array( $rawParams['5::ids'] ) ) { + $rawParams['5::ids'] = explode( ',', $rawParams['5::ids'] ); + } + + $params = array( + '::type' => $rawParams['4::type'], + ':array:ids' => $rawParams['5::ids'], + ':assoc:old' => array( 'bitmask' => $old ), + ':assoc:new' => array( 'bitmask' => $new ), + ); + + static $fields = array( + Revision::DELETED_TEXT => 'content', + Revision::DELETED_COMMENT => 'comment', + Revision::DELETED_USER => 'user', + Revision::DELETED_RESTRICTED => 'restricted', + ); + foreach ( $fields as $bit => $key ) { + $params[':assoc:old'][$key] = (bool)( $old & $bit ); + $params[':assoc:new'][$key] = (bool)( $new & $bit ); + } + } + + return $params; + } + + public function formatParametersForApi() { + $ret = parent::formatParametersForApi(); + if ( isset( $ret['ids'] ) ) { + ApiResult::setIndexedTagName( $ret['ids'], 'id' ); + } + return $ret; + } } diff --git a/includes/logging/LogFormatter.php b/includes/logging/LogFormatter.php index 6571888cfe3..9c2fdd354a7 100644 --- a/includes/logging/LogFormatter.php +++ b/includes/logging/LogFormatter.php @@ -472,7 +472,9 @@ class LogFormatter { continue; } list( $index, $type, ) = explode( ':', $key, 3 ); - $params[$index - 1] = $this->formatParameterValue( $type, $value ); + if ( ctype_digit( $index ) ) { + $params[$index - 1] = $this->formatParameterValue( $type, $value ); + } } /* Message class doesn't like non consecutive numbering. @@ -731,6 +733,120 @@ class LogFormatter { // problems with extensions return $this->getMessageParameters(); } + + /** + * Get the array of parameters, converted from legacy format if necessary. + * @since 1.25 + * @return array + */ + protected function getParametersForApi() { + return $this->entry->getParameters(); + } + + /** + * Format parameters for API output + * + * The result array should generally map named keys to values. Index and + * type should be omitted, e.g. "4::foo" should be returned as "foo" in the + * output. Values should generally be unformatted. + * + * Renames or removals of keys besides from the legacy numeric format to + * modern named style should be avoided. Any renames should be announced to + * the mediawiki-api-announce mailing list. + * + * @since 1.25 + * @return array + */ + public function formatParametersForApi() { + $logParams = array(); + foreach ( $this->getParametersForApi() as $key => $value ) { + $vals = explode( ':', $key, 3 ); + if ( count( $vals ) !== 3 ) { + $logParams[$key] = $value; + continue; + } + $logParams += $this->formatParameterValueForApi( $vals[2], $vals[1], $value ); + } + ApiResult::setIndexedTagName( $logParams, 'param' ); + ApiResult::setArrayType( $logParams, 'assoc' ); + + return $logParams; + } + + /** + * Format a single parameter value for API output + * + * @since 1.25 + * @param string $name + * @param string $type + * @param string $value + * @return array + */ + protected function formatParameterValueForApi( $name, $type, $value ) { + $type = strtolower( trim( $type ) ); + switch ( $type ) { + case 'bool': + $value = (bool)$value; + break; + + case 'number': + if ( ctype_digit( $value ) ) { + $value = (int)$value; + } else { + $value = (float)$value; + } + break; + + case 'array': + case 'assoc': + case 'kvp': + if ( is_array( $value ) ) { + ApiResult::setArrayType( $value, $type ); + } + break; + + case 'timestamp': + $value = wfTimestamp( TS_ISO_8601, $value ); + break; + + case 'msg': + case 'msg-content': + $msg = $this->msg( $value ); + if ( $type === 'msg-content' ) { + $msg->inContentLanguage(); + } + $value = array(); + $value["{$name}_key"] = $msg->getKey(); + if ( $msg->getParams() ) { + $value["{$name}_params"] = $msg->getParams(); + } + $value["{$name}_text"] = $msg->text(); + return $value; + + case 'title': + case 'title-link': + $title = Title::newFromText( $value ); + if ( $title ) { + $value = array(); + ApiQueryBase::addTitleInfo( $value, $title, "{$name}_" ); + } + return $value; + + case 'user': + case 'user-link': + $user = User::newFromName( $value ); + if ( $user ) { + $value = $user->getName(); + } + break; + + default: + // do nothing + break; + } + + return array( $name => $value ); + } } /** diff --git a/includes/logging/MergeLogFormatter.php b/includes/logging/MergeLogFormatter.php index 6763dbd19f9..36e383b069b 100644 --- a/includes/logging/MergeLogFormatter.php +++ b/includes/logging/MergeLogFormatter.php @@ -68,4 +68,24 @@ class MergeLogFormatter extends LogFormatter { return $this->msg( 'parentheses' )->rawParams( $revert )->escaped(); } + + protected function getParametersForApi() { + $entry = $this->entry; + $params = $entry->getParameters(); + + static $map = array( + '4:title:dest', + '5:timestamp:mergepoint', + '4::dest' => '4:title:dest', + '5::mergepoint' => '5:timestamp:mergepoint', + ); + foreach ( $map as $index => $key ) { + if ( isset( $params[$index] ) ) { + $params[$key] = $params[$index]; + unset( $params[$index] ); + } + } + + return $params; + } } diff --git a/includes/logging/MoveLogFormatter.php b/includes/logging/MoveLogFormatter.php index 35da113ab58..e60708db629 100644 --- a/includes/logging/MoveLogFormatter.php +++ b/includes/logging/MoveLogFormatter.php @@ -85,4 +85,25 @@ class MoveLogFormatter extends LogFormatter { return $this->msg( 'parentheses' )->rawParams( $revert )->escaped(); } + + protected function getParametersForApi() { + $entry = $this->entry; + $params = $entry->getParameters(); + + static $map = array( + '4:title:target', + '5:bool:suppressredirect', + '4::target' => '4:title:target', + '5::noredir' => '5:bool:suppressredirect', + ); + foreach ( $map as $index => $key ) { + if ( isset( $params[$index] ) ) { + $params[$key] = $params[$index]; + unset( $params[$index] ); + } + } + + return $params; + } + } diff --git a/includes/logging/PatrolLogFormatter.php b/includes/logging/PatrolLogFormatter.php index 2abaf173592..00337432cc1 100644 --- a/includes/logging/PatrolLogFormatter.php +++ b/includes/logging/PatrolLogFormatter.php @@ -62,4 +62,24 @@ class PatrolLogFormatter extends LogFormatter { return $params; } + + protected function getParametersForApi() { + $entry = $this->entry; + $params = $entry->getParameters(); + + static $map = array( + '4::curid', + '5::previd', + '6:bool:auto', + '6::auto' => '6:bool:auto', + ); + foreach ( $map as $index => $key ) { + if ( isset( $params[$index] ) ) { + $params[$key] = $params[$index]; + unset( $params[$index] ); + } + } + + return $params; + } } diff --git a/includes/logging/RightsLogFormatter.php b/includes/logging/RightsLogFormatter.php index ac252aebc3f..597bec58e9c 100644 --- a/includes/logging/RightsLogFormatter.php +++ b/includes/logging/RightsLogFormatter.php @@ -110,4 +110,35 @@ class RightsLogFormatter extends LogFormatter { return $params; } + + protected function getParametersForApi() { + $entry = $this->entry; + $params = $entry->getParameters(); + + static $map = array( + '4:array:oldgroups', + '5:array:newgroups', + '4::oldgroups' => '4:array:oldgroups', + '5::newgroups' => '5:array:newgroups', + ); + foreach ( $map as $index => $key ) { + if ( isset( $params[$index] ) ) { + $params[$key] = $params[$index]; + unset( $params[$index] ); + } + } + + return $params; + } + + public function formatParametersForApi() { + $ret = parent::formatParametersForApi(); + if ( isset( $ret['oldgroups'] ) ) { + ApiResult::setIndexedTagName( $ret['oldgroups'], 'g' ); + } + if ( isset( $ret['newgroups'] ) ) { + ApiResult::setIndexedTagName( $ret['newgroups'], 'g' ); + } + return $ret; + } } diff --git a/includes/logging/UploadLogFormatter.php b/includes/logging/UploadLogFormatter.php new file mode 100644 index 00000000000..52961dc4aa1 --- /dev/null +++ b/includes/logging/UploadLogFormatter.php @@ -0,0 +1,49 @@ +entry; + $params = $entry->getParameters(); + + static $map = array( + 'img_timestamp' => ':timestamp:img_timestamp', + ); + foreach ( $map as $index => $key ) { + if ( isset( $params[$index] ) ) { + $params[$key] = $params[$index]; + unset( $params[$index] ); + } + } + + return $params; + } + +} diff --git a/includes/revisiondelete/RevDelLogItem.php b/includes/revisiondelete/RevDelLogItem.php index 5c8b8c9d8f5..49adf204a84 100644 --- a/includes/revisiondelete/RevDelLogItem.php +++ b/includes/revisiondelete/RevDelLogItem.php @@ -124,15 +124,7 @@ class RevDelLogItem extends RevDelItem { : array(); if ( LogEventsList::userCan( $this->row, LogPage::DELETED_ACTION, $user ) ) { - ApiQueryLogEvents::addLogParams( - $result, - $ret, - $logEntry->getParameters(), - $logEntry->getType(), - $logEntry->getSubtype(), - $logEntry->getTimestamp(), - $logEntry->isLegacy() - ); + $ret['params'] = LogFormatter::newFromEntry( $logEntry )->formatParametersForApi(); } if ( LogEventsList::userCan( $this->row, LogPage::DELETED_USER, $user ) ) { $ret += array( diff --git a/tests/phpunit/includes/logging/LogFormatterTest.php b/tests/phpunit/includes/logging/LogFormatterTest.php index 6210d0985c9..515990e64af 100644 --- a/tests/phpunit/includes/logging/LogFormatterTest.php +++ b/tests/phpunit/includes/logging/LogFormatterTest.php @@ -239,4 +239,57 @@ class LogFormatterTest extends MediaWikiLangTestCase { $this->assertEquals( $comment, $formatter->getComment() ); } + + /** + * @dataProvider provideApiParamFormatting + * @covers LogFormatter::formatParametersForApi + * @covers LogFormatter::formatParameterValueForApi + */ + public function testApiParamFormatting( $key, $value, $expected ) { + $entry = $this->newLogEntry( 'param', array( $key => $value ) ); + $formatter = LogFormatter::newFromEntry( $entry ); + $formatter->setContext( $this->context ); + + ApiResult::setIndexedTagName( $expected, 'param' ); + ApiResult::setArrayType( $expected, 'assoc' ); + + $this->assertEquals( $expected, $formatter->formatParametersForApi() ); + } + + public static function provideApiParamFormatting() { + return array( + array( 0, 'value', array( 'value' ) ), + array( 'named', 'value', array( 'named' => 'value' ) ), + array( '::key', 'value', array( 'key' => 'value' ) ), + array( '4::key', 'value', array( 'key' => 'value' ) ), + array( '4:raw:key', 'value', array( 'key' => 'value' ) ), + array( '4:plain:key', 'value', array( 'key' => 'value' ) ), + array( '4:bool:key', '1', array( 'key' => true ) ), + array( '4:bool:key', '0', array( 'key' => false ) ), + array( '4:number:key', '123', array( 'key' => 123 ) ), + array( '4:number:key', '123.5', array( 'key' => 123.5 ) ), + array( '4:array:key', array(), array( 'key' => array( ApiResult::META_TYPE => 'array' ) ) ), + array( '4:assoc:key', array(), array( 'key' => array( ApiResult::META_TYPE => 'assoc' ) ) ), + array( '4:kvp:key', array(), array( 'key' => array( ApiResult::META_TYPE => 'kvp' ) ) ), + array( '4:timestamp:key', '20150102030405', array( 'key' => '2015-01-02T03:04:05Z' ) ), + array( '4:msg:key', 'parentheses', array( + 'key_key' => 'parentheses', + 'key_text' => wfMessage( 'parentheses' )->text(), + ) ), + array( '4:msg-content:key', 'parentheses', array( + 'key_key' => 'parentheses', + 'key_text' => wfMessage( 'parentheses' )->inContentLanguage()->text(), + ) ), + array( '4:title:key', 'project:foo', array( + 'key_ns' => NS_PROJECT, + 'key_title' => Title::newFromText( 'project:foo' )->getFullText(), + ) ), + array( '4:title-link:key', 'project:foo', array( + 'key_ns' => NS_PROJECT, + 'key_title' => Title::newFromText( 'project:foo' )->getFullText(), + ) ), + array( '4:user:key', 'foo', array( 'key' => 'Foo' ) ), + array( '4:user-link:key', 'foo', array( 'key' => 'Foo' ) ), + ); + } }