From d6961552b39f3d67c13b81f91afda94355188b33 Mon Sep 17 00:00:00 2001 From: umherirrender Date: Sat, 22 Aug 2015 20:29:00 +0200 Subject: [PATCH] Migrate protect log to new log system This localize the protect type, level and expiry on Special:Log/protect. To allow i18n there are some details stored in the log params of new log items, these details also shown on API output. The details cannot get from the old existing data, because there are containing L10n strings therefore i18n works only for new items. In the api and for IRC the old description text is still stored in the log params for backward compatibility. This allows use of gender on Special:Log. Old messages are kept for use in IRC. Tests already exists to ensure an unchanged IRC message. Bug: T47988 Change-Id: I3bb85c61b857972e66c99c499d7d785c88cafb25 --- includes/DefaultSettings.php | 9 +- includes/logging/LogFormatter.php | 30 +- includes/logging/LogPage.php | 22 +- includes/logging/ProtectLogFormatter.php | 155 +++++++- includes/page/WikiPage.php | 32 +- languages/i18n/en.json | 6 + languages/i18n/qqq.json | 11 +- .../includes/logging/LogFormatterTest.php | 6 +- .../includes/logging/LogFormatterTestCase.php | 1 + .../logging/ProtectLogFormatterTest.php | 359 ++++++++++++++++++ 10 files changed, 560 insertions(+), 71 deletions(-) diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php index aa21c448c5..8b220f33de 100644 --- a/includes/DefaultSettings.php +++ b/includes/DefaultSettings.php @@ -6934,11 +6934,7 @@ $wgLogHeaders = array( * * Extensions with custom log types may add to this array. */ -$wgLogActions = array( - 'protect/modify' => 'modifiedarticleprotection', - 'protect/protect' => 'protectedarticle', - 'protect/unprotect' => 'unprotectedarticle', -); +$wgLogActions = array(); /** * The same as above, but here values are names of classes, @@ -6965,7 +6961,10 @@ $wgLogActionsHandlers = array( 'move/move' => 'MoveLogFormatter', 'move/move_redir' => 'MoveLogFormatter', 'patrol/patrol' => 'PatrolLogFormatter', + 'protect/modify' => 'ProtectLogFormatter', 'protect/move_prot' => 'ProtectLogFormatter', + 'protect/protect' => 'ProtectLogFormatter', + 'protect/unprotect' => 'ProtectLogFormatter', 'rights/autopromote' => 'RightsLogFormatter', 'rights/rights' => 'RightsLogFormatter', 'suppress/block' => 'BlockLogFormatter', diff --git a/includes/logging/LogFormatter.php b/includes/logging/LogFormatter.php index f31a42aadf..1d31088604 100644 --- a/includes/logging/LogFormatter.php +++ b/includes/logging/LogFormatter.php @@ -263,7 +263,7 @@ class LogFormatter { switch ( $entry->getSubtype() ) { case 'protect': $text = wfMessage( 'protectedarticle' ) - ->rawParams( $target . ' ' . $parameters[0] )->inContentLanguage()->escaped(); + ->rawParams( $target . ' ' . $parameters['4::description'] )->inContentLanguage()->escaped(); break; case 'unprotect': $text = wfMessage( 'unprotectedarticle' ) @@ -271,7 +271,7 @@ class LogFormatter { break; case 'modify': $text = wfMessage( 'modifiedarticleprotection' ) - ->rawParams( $target . ' ' . $parameters[0] )->inContentLanguage()->escaped(); + ->rawParams( $target . ' ' . $parameters['4::description'] )->inContentLanguage()->escaped(); break; case 'move_prot': $text = wfMessage( 'movedarticleprotection' ) @@ -932,32 +932,6 @@ class LegacyLogFormatter extends LogFormatter { $type = $this->entry->getType(); $subtype = $this->entry->getSubtype(); - if ( $type == 'protect' - && ( $subtype == 'protect' || $subtype == 'modify' || $subtype == 'unprotect' ) - ) { - $links = array( - Linker::link( $title, - $this->msg( 'hist' )->escaped(), - array(), - array( - 'action' => 'history', - 'offset' => $this->entry->getTimestamp() - ) - ) - ); - if ( $this->context->getUser()->isAllowed( 'protect' ) ) { - $links[] = Linker::linkKnown( - $title, - $this->msg( 'protect_change' )->escaped(), - array(), - array( 'action' => 'protect' ) - ); - } - - return $this->msg( 'parentheses' )->rawParams( - $this->context->getLanguage()->pipeList( $links ) )->escaped(); - } - // Do nothing. The implementation is handled by the hook modifiying the // passed-by-ref parameters. This also changes the default value so that // getComment() and getActionLinks() do not call them indefinitely. diff --git a/includes/logging/LogPage.php b/includes/logging/LogPage.php index 82e5808af6..2e28917007 100644 --- a/includes/logging/LogPage.php +++ b/includes/logging/LogPage.php @@ -285,30 +285,10 @@ class LogPage { $rv = wfMessage( $wgLogActions[$key] )->rawParams( $titleLink ) ->inLanguage( $langObj )->escaped(); } else { - $details = ''; array_unshift( $params, $titleLink ); - // Page protections - if ( $type == 'protect' && count( $params ) == 3 ) { - // Restrictions and expiries - if ( $skin ) { - $details .= $wgLang->getDirMark() . htmlspecialchars( " {$params[1]}" ); - } else { - $details .= " {$params[1]}"; - } - - // Cascading flag... - if ( $params[2] ) { - $text = wfMessage( 'protect-summary-cascade' ) - ->inLanguage( $langObj )->text(); - $details .= ' '; - $details .= wfMessage( 'brackets', $text )->inLanguage( $langObj )->text(); - - } - } - $rv = wfMessage( $wgLogActions[$key] )->rawParams( $params ) - ->inLanguage( $langObj )->escaped() . $details; + ->inLanguage( $langObj )->escaped(); } } } else { diff --git a/includes/logging/ProtectLogFormatter.php b/includes/logging/ProtectLogFormatter.php index 5327e07243..bd04f15e60 100644 --- a/includes/logging/ProtectLogFormatter.php +++ b/includes/logging/ProtectLogFormatter.php @@ -37,11 +37,38 @@ class ProtectLogFormatter extends LogFormatter { return array(); } + protected function getMessageKey() { + $key = parent::getMessageKey(); + $params = $this->extractParameters(); + if ( isset( $params[4] ) && $params[4] ) { + // Messages: logentry-protect-protect-cascade, logentry-protect-modify-cascade + $key .= '-cascade'; + } + + return $key; + } + protected function getMessageParameters() { $params = parent::getMessageParameters(); $subtype = $this->entry->getSubtype(); - if ( $subtype === 'move_prot' ) { + if ( $subtype === 'protect' || $subtype === 'modify' ) { + $rawParams = $this->entry->getParameters(); + if ( isset( $rawParams['details'] ) ) { + $params[3] = $this->createProtectDescription( $rawParams['details'] ); + } elseif ( isset( $params[3] ) ) { + // Old way of Restrictions and expiries + $params[3] = $this->context->getLanguage()->getDirMark() . $params[3]; + } else { + // Very old way (nothing set) + $params[3] = ''; + } + // Cascading flag + if ( isset( $params[4] ) ) { + // handled in getMessageKey + unset( $params[4] ); + } + } elseif ( $subtype === 'move_prot' ) { $oldname = $this->makePageLink( Title::newFromText( $params[3] ), array( 'redirect' => 'no' ) ); $params[3] = Message::rawParam( $oldname ); } @@ -49,15 +76,59 @@ class ProtectLogFormatter extends LogFormatter { return $params; } + public function getActionLinks() { + $subtype = $this->entry->getSubtype(); + if ( $this->entry->isDeleted( LogPage::DELETED_ACTION ) // Action is hidden + || $subtype === 'move_prot' // the move log entry has the right action link + ) { + return ''; + } + + // Show history link for all changes after the protection + $title = $this->entry->getTarget(); + $links = array( + Linker::link( $title, + $this->msg( 'hist' )->escaped(), + array(), + array( + 'action' => 'history', + 'offset' => $this->entry->getTimestamp(), + ) + ) + ); + + // Show change protection link + if ( $this->context->getUser()->isAllowed( 'protect' ) ) { + $links[] = Linker::linkKnown( + $title, + $this->msg( 'protect_change' )->escaped(), + array(), + array( 'action' => 'protect' ) + ); + } + + return $this->msg( 'parentheses' )->rawParams( + $this->context->getLanguage()->pipeList( $links ) )->escaped(); + } + protected function getParametersForApi() { $entry = $this->entry; + $subtype = $this->entry->getSubtype(); $params = $entry->getParameters(); - static $map = array( - // param keys for move_prot sub type - '4:title:oldtitle', - '4::oldtitle' => '4:title:oldtitle', - ); + $map = array(); + if ( $subtype === 'protect' || $subtype === 'modify' ) { + $map = array( + '4::description', + '5:bool:cascade', + 'details' => ':array:details', + ); + } elseif ( $subtype === 'move_prot' ) { + $map = array( + '4:title:oldtitle', + '4::oldtitle' => '4:title:oldtitle', + ); + } foreach ( $map as $index => $key ) { if ( isset( $params[$index] ) ) { $params[$key] = $params[$index]; @@ -65,6 +136,78 @@ class ProtectLogFormatter extends LogFormatter { } } + // Change string to explicit boolean + if ( isset( $params['5:bool:cascade'] ) && is_string( $params['5:bool:cascade'] ) ) { + $params['5:bool:cascade'] = $params['5:bool:cascade'] === 'cascade'; + } + return $params; } + + public function formatParametersForApi() { + global $wgContLang; + + $ret = parent::formatParametersForApi(); + if ( isset( $ret['details'] ) && is_array( $ret['details'] ) ) { + foreach ( $ret['details'] as &$detail ) { + if ( isset( $detail['expiry'] ) ) { + $detail['expiry'] = $wgContLang->formatExpiry( $detail['expiry'], TS_ISO_8601, 'infinite' ); + } + } + } + + return $ret; + } + + /** + * Create the protect description to show in the log formatter + * + * @param array $details + * @return string + */ + public function createProtectDescription( array $details ) { + $protectDescription = ''; + + foreach ( $details as $param ) { + $expiryText = $this->formatExpiry( $param['expiry'] ); + + // Messages: restriction-edit, restriction-move, restriction-create, + // restriction-upload + $action = $this->context->msg( 'restriction-' . $param['type'] )->escaped(); + + $protectionLevel = $param['level']; + // Messages: protect-level-autoconfirmed, protect-level-sysop + $message = $this->context->msg( 'protect-level-' . $protectionLevel ); + if ( $message->isDisabled() ) { + // Require "$1" permission + $restrictions = $this->context->msg( "protect-fallback", $protectionLevel )->parse(); + } else { + $restrictions = $message->escaped(); + } + + if ( $protectDescription !== '' ) { + $protectDescription .= $this->context->msg( 'word-separator' )->escaped(); + } + + $protectDescription .= $this->context->msg( 'protect-summary-desc' ) + ->params( $action, $restrictions, $expiryText )->escaped(); + } + + return $protectDescription; + } + + private function formatExpiry( $expiry ) { + if ( wfIsInfinity( $expiry ) ) { + return $this->context->msg( 'protect-expiry-indefinite' )->text(); + } + $lang = $this->context->getLanguage(); + $user = $this->context->getUser(); + return $this->context->msg( + 'protect-expiring-local', + $lang->userTimeAndDate( $expiry, $user ), + $lang->userDate( $expiry, $user ), + $lang->userTime( $expiry, $user ) + )->text(); + } + } diff --git a/includes/page/WikiPage.php b/includes/page/WikiPage.php index c99928a1c7..8b889867af 100644 --- a/includes/page/WikiPage.php +++ b/includes/page/WikiPage.php @@ -2409,6 +2409,7 @@ class WikiPage implements Page, IDBAccessObject { $logRelationsValues = array(); $logRelationsField = null; + $logParamsDetails = array(); if ( $id ) { // Protection of existing page if ( !Hooks::run( 'ArticleProtect', array( &$this, &$user, $limit, $reason ) ) ) { @@ -2467,6 +2468,7 @@ class WikiPage implements Page, IDBAccessObject { __METHOD__ ); if ( $restrictions != '' ) { + $cascadeValue = ( $cascade && $action == 'edit' ) ? 1 : 0; $dbw->insert( 'page_restrictions', array( @@ -2474,12 +2476,18 @@ class WikiPage implements Page, IDBAccessObject { 'pr_page' => $id, 'pr_type' => $action, 'pr_level' => $restrictions, - 'pr_cascade' => ( $cascade && $action == 'edit' ) ? 1 : 0, + 'pr_cascade' => $cascadeValue, 'pr_expiry' => $dbw->encodeExpiry( $expiry[$action] ) ), __METHOD__ ); $logRelationsValues[] = $dbw->insertId(); + $logParamsDetails[] = array( + 'type' => $action, + 'level' => $restrictions, + 'expiry' => $expiry[$action], + 'cascade' => (bool)$cascadeValue, + ); } } @@ -2510,6 +2518,11 @@ class WikiPage implements Page, IDBAccessObject { 'pt_reason' => $reason, ), __METHOD__ ); + $logParamsDetails[] = array( + 'type' => 'create', + 'level' => $limit['create'], + 'expiry' => $expiry['create'], + ); } else { $dbw->delete( 'protected_titles', array( @@ -2527,15 +2540,24 @@ class WikiPage implements Page, IDBAccessObject { $params = array(); } else { $protectDescriptionLog = $this->protectDescriptionLog( $limit, $expiry ); - $params = array( $protectDescriptionLog, $cascade ? 'cascade' : '' ); + $params = array( + '4::description' => $protectDescriptionLog, // parameter for IRC + '5:bool:cascade' => $cascade, + 'details' => $logParamsDetails, // parameter for localize and api + ); } // Update the protection log - $log = new LogPage( 'protect' ); - $logId = $log->addEntry( $logAction, $this->mTitle, $reason, $params, $user ); + $logEntry = new ManualLogEntry( 'protect', $logAction ); + $logEntry->setTarget( $this->mTitle ); + $logEntry->setComment( $reason ); + $logEntry->setPerformer( $user ); + $logEntry->setParameters( $params ); if ( $logRelationsField !== null && count( $logRelationsValues ) ) { - $log->addRelations( $logRelationsField, $logRelationsValues, $logId ); + $logEntry->setRelations( array( $logRelationsField => $logRelationsValues ) ); } + $logId = $logEntry->insert(); + $logEntry->publish( $logId ); return Status::newGood(); } diff --git a/languages/i18n/en.json b/languages/i18n/en.json index 3fc297cb96..bbdd679ae1 100644 --- a/languages/i18n/en.json +++ b/languages/i18n/en.json @@ -3598,6 +3598,12 @@ "logentry-newusers-byemail": "User account $3 was {{GENDER:$2|created}} by $1 and password was sent by email", "logentry-newusers-autocreate": "User account $1 was {{GENDER:$2|created}} automatically", "logentry-protect-move_prot": "$1 {{GENDER:$2|moved}} protection settings from $4 to $3", + "logentry-protect-unprotect": "$1 {{GENDER:$2|removed}} protection from $3", + "logentry-protect-protect": "$1 {{GENDER:$2|protected}} $3 $4", + "logentry-protect-protect-cascade": "$1 {{GENDER:$2|protected}} $3 $4 [cascading]", + "logentry-protect-modify": "$1 {{GENDER:$2|changed}} protection level for $3 $4", + "logentry-protect-modify-cascade": "$1 {{GENDER:$2|changed}} protection level for $3 $4 [cascading]", + "logentry-protect-unprotect": "$1 {{GENDER:$2|removed}} protection from $3", "logentry-rights-rights": "$1 {{GENDER:$2|changed}} group membership for $3 from $4 to $5", "logentry-rights-rights-legacy": "$1 {{GENDER:$2|changed}} group membership for $3", "logentry-rights-autopromote": "$1 was automatically {{GENDER:$2|promoted}} from $4 to $5", diff --git a/languages/i18n/qqq.json b/languages/i18n/qqq.json index 79d8a69400..9bb321bbc7 100644 --- a/languages/i18n/qqq.json +++ b/languages/i18n/qqq.json @@ -2149,9 +2149,9 @@ "logentry-contentmodel-change-revert": "Prefilled edit summary when reverting a content model change. {{identical|revertmove}}", "protectlogpage": "{{doc-logpage}}\n\nTitle of [[Special:Log/protect]].", "protectlogtext": "Text in [[Special:Log/protect]].", - "protectedarticle": "Text describing an action on [[Special:Log]]. $1 is a page title.", - "modifiedarticleprotection": "Text describing an action on [[Special:Log]]. $1 is a page title.", - "unprotectedarticle": "Used as action in the log. Parameters:\n* $1 - target page title", + "protectedarticle": "This is a ''logentry'' message only used on IRC.\nText describing an action. $1 is a page title.", + "modifiedarticleprotection": "This is a ''logentry'' message only used on IRC.\nText describing an action. $1 is a page title.", + "unprotectedarticle": "This is a ''logentry'' message only used on IRC.\nUsed as action. Parameters:\n* $1 - target page title", "movedarticleprotection": "This is a ''logentry'' message only used on IRC. It appears in the log if a protected page is renamed.\n\nExample:\n00:51, 16 September 2010 Siebrand +(Talk • contribs • block) moved protection settings from \"User:Siebrand/prot-move\" to \"User:Siebrand/prot-moved\" ‎ (User:Siebrand/prot-move moved to User:Siebrand/prot-moved: prot_move test.)\n\nParameters:\n* $1 - target page title\n* $2 - source page title", "protect-title": "Title for the protection form. $1 is the title of the page to be (un)protected.", "protect-title-notallowed": "Same as {{msg-mw|Protect-title}}, but when the user does not have the right to change protection levels.\n\nParameters:\n* $1 - page title", @@ -3771,6 +3771,11 @@ "logentry-newusers-byemail": "{{Logentry|[[Special:Log/newusers]]}}\n\n$4 is the name of the user that was created.", "logentry-newusers-autocreate": "{{Logentry|[[Special:Log/newusers]]}}\n\n$4 is the gender of the target user.", "logentry-protect-move_prot": "{{Logentry|[[Special:Log/protect]]}}\n* $4 - the old title", + "logentry-protect-protect": "{{Logentry|[[Special:Log/protect]]}}\n\n* $4 - protect expiry (formatted with {{msg-mw|protect-summary-desc}}, multiple possible)", + "logentry-protect-protect-cascade": "{{Logentry|[[Special:Log/protect]]}}\n\n* $4 - protect expiry (formatted with {{msg-mw|protect-summary-desc}}, multiple possible)\nFor word \"cascading\" see {{msg-mw|protect-summary-cascade}}", + "logentry-protect-modify": "{{Logentry|[[Special:Log/protect]]}}\n\n* $4 - protect expiry (formatted with {{msg-mw|protect-summary-desc}}, multiple possible)", + "logentry-protect-modify-cascade": "{{Logentry|[[Special:Log/protect]]}}\n\n* $4 - protect expiry (formatted with {{msg-mw|protect-summary-desc}}, multiple possible)\nFor word \"cascading\" see {{msg-mw|protect-summary-cascade}}", + "logentry-protect-unprotect": "{{Logentry|[[Special:Log/protect]]}}", "logentry-rights-rights": "* $1 - username\n* $2 - (see below)\n* $3 - username\n* $4 - list of user groups or {{msg-mw|Rightsnone}}\n* $5 - list of user groups or {{msg-mw|Rightsnone}}\n----\n{{Logentry|[[Special:Log/rights]]}}", "logentry-rights-rights-legacy": "* $1 - username\n* $2 - (see below)\n* $3 - username\n----\n{{Logentry|[[Special:Log/rights]]}}", "logentry-rights-autopromote": "* $1 - username\n* $2 - (see below)\n* $3 - (see below)\n* $4 - comma separated list of old user groups or {{msg-mw|Rightsnone}}\n* $5 - comma separated list of new user groups\n----\n{{Logentry|[[Special:Log/rights]]}}", diff --git a/tests/phpunit/includes/logging/LogFormatterTest.php b/tests/phpunit/includes/logging/LogFormatterTest.php index 844c9afbc0..5d8a258345 100644 --- a/tests/phpunit/includes/logging/LogFormatterTest.php +++ b/tests/phpunit/includes/logging/LogFormatterTest.php @@ -462,13 +462,13 @@ class LogFormatterTest extends MediaWikiLangTestCase { */ public function testIrcMsgForLogTypeProtect() { $protectParams = array( - '[edit=sysop] (indefinite) ‎[move=sysop] (indefinite)' + '4::description' => '[edit=sysop] (indefinite) ‎[move=sysop] (indefinite)' ); $sep = $this->context->msg( 'colon-separator' )->text(); # protect/protect $this->assertIRCComment( - $this->context->msg( 'protectedarticle', 'SomeTitle ' . $protectParams[0] ) + $this->context->msg( 'protectedarticle', 'SomeTitle ' . $protectParams['4::description'] ) ->plain() . $sep . $this->user_comment, 'protect', 'protect', $protectParams, @@ -485,7 +485,7 @@ class LogFormatterTest extends MediaWikiLangTestCase { # protect/modify $this->assertIRCComment( - $this->context->msg( 'modifiedarticleprotection', 'SomeTitle ' . $protectParams[0] ) + $this->context->msg( 'modifiedarticleprotection', 'SomeTitle ' . $protectParams['4::description'] ) ->plain() . $sep . $this->user_comment, 'protect', 'modify', $protectParams, diff --git a/tests/phpunit/includes/logging/LogFormatterTestCase.php b/tests/phpunit/includes/logging/LogFormatterTestCase.php index e88452b758..f17548296e 100644 --- a/tests/phpunit/includes/logging/LogFormatterTestCase.php +++ b/tests/phpunit/includes/logging/LogFormatterTestCase.php @@ -49,6 +49,7 @@ abstract class LogFormatterTestCase extends MediaWikiLangTestCase { private static function removeSomeHtml( $html ) { $html = str_replace( '"', '"', $html ); + $html = preg_replace( '/\xE2\x80[\x8E\x8F]/', '', $html ); // Strip lrm/rlm return trim( preg_replace( '/<(a|span)[^>]*>([^<]*)<\/\1>/', '$2', $html ) ); } diff --git a/tests/phpunit/includes/logging/ProtectLogFormatterTest.php b/tests/phpunit/includes/logging/ProtectLogFormatterTest.php index 611b2dfc9e..718c08c43a 100644 --- a/tests/phpunit/includes/logging/ProtectLogFormatterTest.php +++ b/tests/phpunit/includes/logging/ProtectLogFormatterTest.php @@ -2,6 +2,365 @@ class ProtectLogFormatterTest extends LogFormatterTestCase { + /** + * Provide different rows from the logging table to test + * for backward compatibility. + * Do not change the existing data, just add a new database row + */ + public static function provideProtectLogDatabaseRows() { + return array( + // Current format + array( + array( + 'type' => 'protect', + 'action' => 'protect', + 'comment' => 'protect comment', + 'namespace' => NS_MAIN, + 'title' => 'ProtectPage', + 'params' => array( + '4::description' => '[edit=sysop] (indefinite)[move=sysop] (indefinite)', + '5:bool:cascade' => false, + 'details' => array( + array( + 'type' => 'edit', + 'level' => 'sysop', + 'expiry' => 'infinity', + 'cascade' => false, + ), + array( + 'type' => 'move', + 'level' => 'sysop', + 'expiry' => 'infinity', + 'cascade' => false, + ), + ), + ), + ), + array( + 'text' => 'User protected ProtectPage [Edit=Allow only administrators] (indefinite) [Move=Allow only administrators] (indefinite)', + 'api' => array( + 'description' => '[edit=sysop] (indefinite)[move=sysop] (indefinite)', + 'cascade' => false, + 'details' => array( + array( + 'type' => 'edit', + 'level' => 'sysop', + 'expiry' => 'infinite', + 'cascade' => false, + ), + array( + 'type' => 'move', + 'level' => 'sysop', + 'expiry' => 'infinite', + 'cascade' => false, + ), + ), + ), + ), + ), + + // Current format with cascade + array( + array( + 'type' => 'protect', + 'action' => 'protect', + 'comment' => 'protect comment', + 'namespace' => NS_MAIN, + 'title' => 'ProtectPage', + 'params' => array( + '4::description' => '[edit=sysop] (indefinite)[move=sysop] (indefinite)', + '5:bool:cascade' => true, + 'details' => array( + array( + 'type' => 'edit', + 'level' => 'sysop', + 'expiry' => 'infinity', + 'cascade' => true, + ), + array( + 'type' => 'move', + 'level' => 'sysop', + 'expiry' => 'infinity', + 'cascade' => false, + ), + ), + ), + ), + array( + 'text' => 'User protected ProtectPage [Edit=Allow only administrators] (indefinite) [Move=Allow only administrators] (indefinite) [cascading]', + 'api' => array( + 'description' => '[edit=sysop] (indefinite)[move=sysop] (indefinite)', + 'cascade' => true, + 'details' => array( + array( + 'type' => 'edit', + 'level' => 'sysop', + 'expiry' => 'infinite', + 'cascade' => true, + ), + array( + 'type' => 'move', + 'level' => 'sysop', + 'expiry' => 'infinite', + 'cascade' => false, + ), + ), + ), + ), + ), + + // Legacy format + array( + array( + 'type' => 'protect', + 'action' => 'protect', + 'comment' => 'protect comment', + 'namespace' => NS_MAIN, + 'title' => 'ProtectPage', + 'params' => array( + '[edit=sysop] (indefinite)[move=sysop] (indefinite)', + '', + ), + ), + array( + 'legacy' => true, + 'text' => 'User protected ProtectPage [edit=sysop] (indefinite)[move=sysop] (indefinite)', + 'api' => array( + 'description' => '[edit=sysop] (indefinite)[move=sysop] (indefinite)', + 'cascade' => false, + ), + ), + ), + + // Legacy format with cascade + array( + array( + 'type' => 'protect', + 'action' => 'protect', + 'comment' => 'protect comment', + 'namespace' => NS_MAIN, + 'title' => 'ProtectPage', + 'params' => array( + '[edit=sysop] (indefinite)[move=sysop] (indefinite)', + 'cascade', + ), + ), + array( + 'legacy' => true, + 'text' => 'User protected ProtectPage [edit=sysop] (indefinite)[move=sysop] (indefinite) [cascading]', + 'api' => array( + 'description' => '[edit=sysop] (indefinite)[move=sysop] (indefinite)', + 'cascade' => true, + ), + ), + ), + ); + } + + + /** + * @dataProvider provideProtectLogDatabaseRows + */ + public function testProtectLogDatabaseRows( $row, $extra ) { + $this->doTestLogFormatter( $row, $extra ); + } + + /** + * Provide different rows from the logging table to test + * for backward compatibility. + * Do not change the existing data, just add a new database row + */ + public static function provideModifyLogDatabaseRows() { + return array( + // Current format + array( + array( + 'type' => 'protect', + 'action' => 'modify', + 'comment' => 'protect comment', + 'namespace' => NS_MAIN, + 'title' => 'ProtectPage', + 'params' => array( + '4::description' => '[edit=sysop] (indefinite)[move=sysop] (indefinite)', + '5:bool:cascade' => false, + 'details' => array( + array( + 'type' => 'edit', + 'level' => 'sysop', + 'expiry' => 'infinity', + 'cascade' => false, + ), + array( + 'type' => 'move', + 'level' => 'sysop', + 'expiry' => 'infinity', + 'cascade' => false, + ), + ), + ), + ), + array( + 'text' => 'User changed protection level for ProtectPage [Edit=Allow only administrators] (indefinite) [Move=Allow only administrators] (indefinite)', + 'api' => array( + 'description' => '[edit=sysop] (indefinite)[move=sysop] (indefinite)', + 'cascade' => false, + 'details' => array( + array( + 'type' => 'edit', + 'level' => 'sysop', + 'expiry' => 'infinite', + 'cascade' => false, + ), + array( + 'type' => 'move', + 'level' => 'sysop', + 'expiry' => 'infinite', + 'cascade' => false, + ), + ), + ), + ), + ), + + // Current format with cascade + array( + array( + 'type' => 'protect', + 'action' => 'modify', + 'comment' => 'protect comment', + 'namespace' => NS_MAIN, + 'title' => 'ProtectPage', + 'params' => array( + '4::description' => '[edit=sysop] (indefinite)[move=sysop] (indefinite)', + '5:bool:cascade' => true, + 'details' => array( + array( + 'type' => 'edit', + 'level' => 'sysop', + 'expiry' => 'infinity', + 'cascade' => true, + ), + array( + 'type' => 'move', + 'level' => 'sysop', + 'expiry' => 'infinity', + 'cascade' => false, + ), + ), + ), + ), + array( + 'text' => 'User changed protection level for ProtectPage [Edit=Allow only administrators] (indefinite) [Move=Allow only administrators] (indefinite) [cascading]', + 'api' => array( + 'description' => '[edit=sysop] (indefinite)[move=sysop] (indefinite)', + 'cascade' => true, + 'details' => array( + array( + 'type' => 'edit', + 'level' => 'sysop', + 'expiry' => 'infinite', + 'cascade' => true, + ), + array( + 'type' => 'move', + 'level' => 'sysop', + 'expiry' => 'infinite', + 'cascade' => false, + ), + ), + ), + ), + ), + + // Legacy format + array( + array( + 'type' => 'protect', + 'action' => 'modify', + 'comment' => 'protect comment', + 'namespace' => NS_MAIN, + 'title' => 'ProtectPage', + 'params' => array( + '[edit=sysop] (indefinite)[move=sysop] (indefinite)', + '', + ), + ), + array( + 'legacy' => true, + 'text' => 'User changed protection level for ProtectPage [edit=sysop] (indefinite)[move=sysop] (indefinite)', + 'api' => array( + 'description' => '[edit=sysop] (indefinite)[move=sysop] (indefinite)', + 'cascade' => false, + ), + ), + ), + + // Legacy format with cascade + array( + array( + 'type' => 'protect', + 'action' => 'modify', + 'comment' => 'protect comment', + 'namespace' => NS_MAIN, + 'title' => 'ProtectPage', + 'params' => array( + '[edit=sysop] (indefinite)[move=sysop] (indefinite)', + 'cascade', + ), + ), + array( + 'legacy' => true, + 'text' => 'User changed protection level for ProtectPage [edit=sysop] (indefinite)[move=sysop] (indefinite) [cascading]', + 'api' => array( + 'description' => '[edit=sysop] (indefinite)[move=sysop] (indefinite)', + 'cascade' => true, + ), + ), + ), + ); + } + + + /** + * @dataProvider provideModifyLogDatabaseRows + */ + public function testModifyLogDatabaseRows( $row, $extra ) { + $this->doTestLogFormatter( $row, $extra ); + } + + /** + * Provide different rows from the logging table to test + * for backward compatibility. + * Do not change the existing data, just add a new database row + */ + public static function provideUnprotectLogDatabaseRows() { + return array( + // Current format + array( + array( + 'type' => 'protect', + 'action' => 'unprotect', + 'comment' => 'unprotect comment', + 'namespace' => NS_MAIN, + 'title' => 'ProtectPage', + 'params' => array(), + ), + array( + 'text' => 'User removed protection from ProtectPage', + 'api' => array(), + ), + ), + ); + } + + + /** + * @dataProvider provideUnprotectLogDatabaseRows + */ + public function testUnprotectLogDatabaseRows( $row, $extra ) { + $this->doTestLogFormatter( $row, $extra ); + } + /** * Provide different rows from the logging table to test * for backward compatibility. -- 2.20.1